diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000000..e70fe1c3aec --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +.git +.idea +.vscode +.nyc_output + +logs +db +scripts +node_modules + +packages/*/.nyc_output +packages/*/.ultra.cache.json +packages/*/test +packages/*/tests +packages/*/doc +packages/*/docs +packages/*/dist +packages/*/node_modules + +!packages/platform-test-suite/test + +# Yarn +.yarn/build-state.yml +.yarn/install-state.gz +.yarn/unplugged diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..774df6a49de --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf + +[*.{md,markdown}] +trim_trailing_whitespace = false diff --git a/packages/dash-spv/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md similarity index 97% rename from packages/dash-spv/.github/ISSUE_TEMPLATE/bug_report.md rename to .github/ISSUE_TEMPLATE/bug_report.md index edd3e4df4ad..450469efbb4 100644 --- a/packages/dash-spv/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -35,4 +35,4 @@ assignees: '' * Version used: * Environment name and version (e.g. Chrome 39, node.js 5.4): * Operating System and version (desktop, server, or mobile): -* Link to your project: +* Link to your project: \ No newline at end of file diff --git a/packages/dash-spv/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md similarity index 99% rename from packages/dash-spv/.github/ISSUE_TEMPLATE/feature_request.md rename to .github/ISSUE_TEMPLATE/feature_request.md index 5088a47370a..2126d8754cc 100644 --- a/packages/dash-spv/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -24,3 +24,4 @@ assignees: '' ## Additional Context + diff --git a/packages/dash-spv/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from packages/dash-spv/.github/PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/semantic.yml b/.github/semantic.yml new file mode 100644 index 00000000000..8f5305afc7b --- /dev/null +++ b/.github/semantic.yml @@ -0,0 +1,36 @@ +# https://github.com/zeke/semantic-pull-requests#configuration + +titleOnly: true + +scopes: + - bench-suite + - dapi + - dapi-grpc + - dashmate + - dashpay-contract + - dpns-contract + - feature-flags-contract + - dapi-client + - sdk + - dpp + - drive + - grpc-common + - masternode-reward-shares-contract + - test-suite + - wallet-lib + - release + - dash-spv + +types: + - feat + - fix + - docs + - style + - refactor + - perf + - test + - build + - ci + - chore + - revert + diff --git a/.github/workflows/all-packages.yml b/.github/workflows/all-packages.yml new file mode 100644 index 00000000000..5ac2f92c83c --- /dev/null +++ b/.github/workflows/all-packages.yml @@ -0,0 +1,175 @@ +name: All Packages + +on: + workflow_dispatch: + pull_request: + branches: + - master + - v[0-9]+\.[0-9]+-dev + +jobs: + workspaces: + name: Validate project workspaces + runs-on: ubuntu-20.04 + steps: + - name: Check out repo + uses: actions/checkout@v2 + + - name: Setup Node.JS + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Enable corepack + run: corepack enable + + - name: Validate workspaces + run: yarn constraints + + test-suite: + name: Run Platform Test Suite + runs-on: ubuntu-20.04 + timeout-minutes: 60 + steps: + - name: Cancel previous runs + uses: styfle/cancel-workflow-action@0.9.1 + with: + access_token: ${{ github.token }} + + - name: Check out repo + uses: actions/checkout@v2 + + - name: Setup Node.JS + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Enable corepack + run: corepack enable + + - name: Disable NPM audit + run: npm config set audit false + + - name: Enable Yarn unplugged modules cache + uses: actions/cache@v2 + with: + path: '.yarn/unplugged' + key: ${{ runner.os }}-yarn-unplugged-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-unplugged- + + - name: Install dependencies + run: yarn install + + - name: Build packages + run: yarn build + + - name: Set up Docker BuildX + id: buildx + uses: docker/setup-buildx-action@v1 + with: + version: v0.6.3 + install: true + driver-opts: image=moby/buildkit:buildx-stable-1 + + - name: Enable buildkit cache + uses: actions/cache@v2 + with: + path: /tmp/buildkit-cache/buildkit-state.tar + key: ${{ runner.os }}-buildkit-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildkit- + + - name: Load buildkit state from cache + uses: dashevo/gh-action-cache-buildkit-state@v1 + with: + builder: buildx_buildkit_${{ steps.buildx.outputs.name }}0 + cache-path: /tmp/buildkit-cache + cache-max-size: 3g + + - name: Setup local network + run: yarn configure + + - name: Start local network + run: yarn start + + - name: Run test suite + run: yarn test:suite + + - name: Show Docker logs + if: ${{ failure() }} + uses: jwalton/gh-docker-logs@v2 + + test-suite-browsers: + name: Run Platform Test Suite in Browsers + runs-on: ubuntu-20.04 + timeout-minutes: 60 + steps: + - name: Cancel previous runs + uses: styfle/cancel-workflow-action@0.9.1 + with: + access_token: ${{ github.token }} + + - name: Check out repo + uses: actions/checkout@v2 + + - name: Setup Node.JS + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Enable corepack + run: corepack enable + + - name: Disable NPM audit + run: npm config set audit false + + - name: Enable Yarn unplugged modules cache + uses: actions/cache@v2 + with: + path: '.yarn/unplugged' + key: ${{ runner.os }}-yarn-unplugged-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-unplugged- + + - name: Install dependencies + run: yarn install + + - name: Build packages + run: yarn build + + - name: Set up Docker BuildX + id: buildx + uses: docker/setup-buildx-action@v1 + with: + version: v0.6.3 + install: true + driver-opts: image=moby/buildkit:buildx-stable-1 + + - name: Enable buildkit cache + uses: actions/cache@v2 + with: + path: /tmp/buildkit-cache/buildkit-state.tar + key: ${{ runner.os }}-buildkit-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildkit- + + - name: Load buildkit state from cache + uses: dashevo/gh-action-cache-buildkit-state@v1 + with: + builder: buildx_buildkit_${{ steps.buildx.outputs.name }}0 + cache-path: /tmp/buildkit-cache + cache-max-size: 3g + + - name: Setup local network + run: yarn configure + + - name: Start local network + run: yarn start + + - name: Run test suite in browsers + run: yarn test:suite:browsers + + - name: Show Docker logs + if: ${{ failure() }} + uses: jwalton/gh-docker-logs@v2 diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml new file mode 100644 index 00000000000..3e177208354 --- /dev/null +++ b/.github/workflows/cron.yml @@ -0,0 +1,83 @@ +name: Run All Tests Every Day + +on: + schedule: + - cron: '30 4 * * *' + +jobs: + test: + name: Test all packages + runs-on: ubuntu-20.04 + timeout-minutes: 60 + steps: + - name: Cancel previous runs + uses: styfle/cancel-workflow-action@0.9.1 + with: + access_token: ${{ github.token }} + + - name: Check out repo + uses: actions/checkout@v2 + + - name: Setup Node.JS + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Enable corepack + run: corepack enable + + - name: Disable NPM audit + run: npm config set audit false + + - name: Enable Yarn unplugged modules cache + uses: actions/cache@v2 + with: + path: '.yarn/unplugged' + key: ${{ runner.os }}-yarn-unplugged-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-unplugged- + + - name: Install dependencies + run: yarn install + + - name: Run ESLinter + run: yarn lint + + - name: Build packages + run: yarn build + + - name: Set up Docker BuildX + id: buildx + uses: docker/setup-buildx-action@v1 + with: + version: v0.6.3 + install: true + driver-opts: image=moby/buildkit:buildx-stable-1 + + - name: Enable buildkit cache + uses: actions/cache@v2 + with: + path: /tmp/buildkit-cache/buildkit-state.tar + key: ${{ runner.os }}-buildkit-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildkit- + + - name: Load buildkit state from cache + uses: dashevo/gh-action-cache-buildkit-state@v1 + with: + builder: buildx_buildkit_${{ steps.buildx.outputs.name }}0 + cache-path: /tmp/buildkit-cache + cache-max-size: 3g + + - name: Setup local network + run: yarn configure + + - name: Start local network + run: yarn start + + - name: Run tests + run: yarn test + + - name: Show Docker logs + if: ${{ failure() }} + uses: jwalton/gh-docker-logs@v2 diff --git a/.github/workflows/dapi-grpc.yml b/.github/workflows/dapi-grpc.yml new file mode 100644 index 00000000000..b6b8f441d8d --- /dev/null +++ b/.github/workflows/dapi-grpc.yml @@ -0,0 +1,20 @@ +name: DAPI gRPC + +on: + workflow_dispatch: + pull_request: + branches: + - master + - v[0-9]+\.[0-9]+-dev + paths: + - .github/workflows/dapi-grpc.yml + - .github/workflows/test.yml + - packages/dapi-grpc/** + - packages/js-grpc-common/** + +jobs: + dapi-tests: + name: Run DAPI gRPC tests + uses: dashevo/platform/.github/workflows/test.yml@master + with: + package: '@dashevo/dapi-grpc' diff --git a/.github/workflows/dapi.yml b/.github/workflows/dapi.yml new file mode 100644 index 00000000000..4a89254c583 --- /dev/null +++ b/.github/workflows/dapi.yml @@ -0,0 +1,28 @@ +name: DAPI + +on: + workflow_dispatch: + pull_request: + branches: + - master + - v[0-9]+\.[0-9]+-dev + paths: + - .github/workflows/dapi.yml + - .github/workflows/test.yml + - packages/dapi/** + - packages/dapi-grpc/** + - packages/js-dpp/** + - packages/js-grpc-common/** + - packages/js-dapi-client/** + - packages/dashpay-contract/** + - packages/feature-flags-contract/** + - packages/dpns-contract/** + - packages/masternode-reward-shares-contract/** + - packages/dash-spv/** + +jobs: + dapi-tests: + name: Run DAPI tests + uses: dashevo/platform/.github/workflows/test.yml@master + with: + package: '@dashevo/dapi' diff --git a/.github/workflows/dash-spv.yml b/.github/workflows/dash-spv.yml new file mode 100644 index 00000000000..09663ec189d --- /dev/null +++ b/.github/workflows/dash-spv.yml @@ -0,0 +1,20 @@ +name: Dash SPV + +on: + workflow_dispatch: + pull_request: + branches: + - master + - v[0-9]+\.[0-9]+-dev + paths: + - .github/workflows/dash-spv.yml + - .github/workflows/test.yml + - packages/dash-spv/** + +jobs: + dash-spv-tests: + name: Run Dash SPV tests + uses: dashevo/platform/.github/workflows/test.yml@master + with: + package: 'dash-spv' + start-local-network: false diff --git a/.github/workflows/dashpay-contract.yml b/.github/workflows/dashpay-contract.yml new file mode 100644 index 00000000000..778cc75ddd2 --- /dev/null +++ b/.github/workflows/dashpay-contract.yml @@ -0,0 +1,22 @@ +name: DashPay Contract + +on: + workflow_dispatch: + pull_request: + branches: + - master + - v[0-9]+\.[0-9]+-dev + paths: + - .github/workflows/dashpay-contract.yml + - .github/workflows/test.yml + - packages/dashpay-contract/** + - packages/js-dpp/** + - packages/feature-flags-contract/** + - packages/dpns-contract/** + +jobs: + dapi-tests: + name: Run DAPI gRPC tests + uses: dashevo/platform/.github/workflows/test.yml@master + with: + package: '@dashevo/dashpay-contract' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000000..3b97555ae7c --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,25 @@ +name: Publish docs via GitHub Pages +on: + push: + branches: + - master + +jobs: + build: + name: Deploy docs + runs-on: ubuntu-latest + steps: + - name: Checkout main + uses: actions/checkout@v2 + + - name: Prepare docs + run: "${GITHUB_WORKSPACE}/scripts/prepare_docs.sh" + + - name: Deploy docs + uses: mhausenblas/mkdocs-deploy-gh-pages@master + # Or use mhausenblas/mkdocs-deploy-gh-pages@nomaterial to build without the mkdocs-material theme + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CONFIG_FILE: mkdocs.yml + EXTRA_PACKAGES: build-base + # REQUIREMENTS: folder/requirements.txt diff --git a/.github/workflows/js-dapi-client.yml b/.github/workflows/js-dapi-client.yml new file mode 100644 index 00000000000..8fcb4f02a1e --- /dev/null +++ b/.github/workflows/js-dapi-client.yml @@ -0,0 +1,27 @@ +name: DAPI Client + +on: + workflow_dispatch: + pull_request: + branches: + - master + - v[0-9]+\.[0-9]+-dev + paths: + - .github/workflows/js-dapi-client.yml + - .github/workflows/test.yml + - packages/js-dapi-client/** + - packages/dapi-grpc/** + - packages/js-dpp/** + - packages/js-grpc-common/** + - packages/dashpay-contract/** + - packages/feature-flags-contract/** + - packages/dpns-contract/** + - packages/masternode-reward-shares-contract/** + - packages/dash-spv/** + +jobs: + js-dapi-client-tests: + name: Run DAPI Client tests + uses: dashevo/platform/.github/workflows/test.yml@master + with: + package: '@dashevo/dapi-client' diff --git a/.github/workflows/js-dash-sdk.yml b/.github/workflows/js-dash-sdk.yml new file mode 100644 index 00000000000..e82b680129c --- /dev/null +++ b/.github/workflows/js-dash-sdk.yml @@ -0,0 +1,30 @@ +name: Dash SDK + +on: + workflow_dispatch: + pull_request: + branches: + - master + - v[0-9]+\.[0-9]+-dev + paths: + - .github/workflows/js-dash-sdk.yml + - .github/workflows/test.yml + - packages/js-dash-sdk/** + - packages/wallet-lib/** + - packages/js-dapi-client/** + - packages/dapi-grpc/** + - packages/js-dpp/** + - packages/js-grpc-common/** + - packages/dashpay-contract/** + - packages/feature-flags-contract/** + - packages/masternode-reward-shares-contract/** + - packages/dpns-contract/** + - packages/dash-spv/** + +jobs: + js-dash-sdk-tests: + name: Run Dash SDK tests + uses: dashevo/platform/.github/workflows/test.yml@master + with: + package: 'dash' + start-local-network: true diff --git a/.github/workflows/js-dpp.yml b/.github/workflows/js-dpp.yml new file mode 100644 index 00000000000..a5505980e88 --- /dev/null +++ b/.github/workflows/js-dpp.yml @@ -0,0 +1,23 @@ +name: DPP + +on: + workflow_dispatch: + pull_request: + branches: + - master + - v[0-9]+\.[0-9]+-dev + paths: + - .github/workflows/js-dpp.yml + - .github/workflows/test.yml + - packages/js-dpp/** + - packages/feature-flags-contract/** + - packages/masternode-reward-shares-contract/** + - packages/dpns-contract/** + - packages/dashpay-contract/** + +jobs: + js-dpp-tests: + name: Run DPP tests + uses: dashevo/platform/.github/workflows/test.yml@master + with: + package: '@dashevo/dpp' diff --git a/.github/workflows/js-drive.yml b/.github/workflows/js-drive.yml new file mode 100644 index 00000000000..5f9e1385812 --- /dev/null +++ b/.github/workflows/js-drive.yml @@ -0,0 +1,26 @@ +name: Drive + +on: + workflow_dispatch: + pull_request: + branches: + - master + - v[0-9]+\.[0-9]+-dev + paths: + - .github/workflows/js-drive.yml + - .github/workflows/test.yml + - packages/js-drive/** + - packages/feature-flags-contract/** + - packages/dpns-contract/** + - packages/dashpay-contract/** + - packages/js-dpp/** + - packages/masternode-reward-shares-contract/** + - packages/dapi-grpc/** + - packages/js-grpc-common/** + +jobs: + js-dpp-tests: + name: Run Drive tests + uses: dashevo/platform/.github/workflows/test.yml@master + with: + package: '@dashevo/drive' diff --git a/.github/workflows/js-grpc-common.yml b/.github/workflows/js-grpc-common.yml new file mode 100644 index 00000000000..78c8c2503e0 --- /dev/null +++ b/.github/workflows/js-grpc-common.yml @@ -0,0 +1,19 @@ +name: gRPC Common + +on: + workflow_dispatch: + pull_request: + branches: + - master + - v[0-9]+\.[0-9]+-dev + paths: + - .github/workflows/js-grpc-common.yml + - .github/workflows/test.yml + - packages/js-grpc-common/** + +jobs: + js-dpp-tests: + name: Run gRPC Common tests + uses: dashevo/platform/.github/workflows/test.yml@master + with: + package: '@dashevo/grpc-common' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000000..cb670bc533d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,360 @@ +name: Release Packages + +on: + release: + types: + - published + workflow_dispatch: + inputs: + tag: + description: 'Version (i.e. v0.22.3-pre.2)' + required: true + +jobs: + release-npm: + name: Release NPM packages + runs-on: ubuntu-20.04 + if: github.event_name != 'workflow_dispatch' + steps: + - name: Check out repo + uses: actions/checkout@v2 + + - name: Check package version matches tag + uses: geritol/match-tag-to-package-version@0.2.0 + env: + TAG_PREFIX: v + + - name: Setup Node.JS + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Enable corepack + run: corepack enable + + - name: Disable NPM audit + run: npm config set audit false + + - name: Enable Yarn unplugged modules cache + uses: actions/cache@v2 + with: + path: '.yarn/unplugged' + key: ${{ runner.os }}-yarn-unplugged-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-unplugged- + + - name: Install dependencies + run: yarn install + + - name: Build packages + run: yarn build + + - name: Set NPM release tag + uses: actions/github-script@v5 + id: tag + with: + result-encoding: string + script: | + const tag = context.payload.release.tag_name; + const [, major, minor] = tag.match(/^v([0-9]+)\.([0-9]+)/); + return (tag.includes('dev') ? `${major}.${minor}-dev` : 'latest'); + + - name: Configure NPM auth token + run: yarn config set npmAuthToken ${{ secrets.NPM_TOKEN }} + + - name: Publish NPM packages + run: yarn workspaces foreach --all --no-private --parallel npm publish --access public --tag ${{ steps.tag.outputs.result }} + + release-drive-docker-image: + name: Release Drive to Docker Hub + runs-on: ubuntu-20.04 + steps: + - name: Check out repo + uses: actions/checkout@v2 + + - name: Setup Node.JS + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Enable corepack + run: corepack enable + + - name: Disable NPM audit + run: npm config set audit false + + - name: Enable Yarn unplugged modules cache + uses: actions/cache@v2 + with: + path: '.yarn/unplugged' + key: ${{ runner.os }}-yarn-unplugged-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-unplugged- + + - name: Install dependencies + run: yarn install + + - name: Build packages + run: yarn build --filter "+@dashevo/drive" + + - name: Set up QEMU to run multi-arch builds + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker BuildX + id: buildx + uses: docker/setup-buildx-action@v1 + with: + version: v0.7.0 + install: true + driver-opts: image=moby/buildkit:buildx-stable-1 + + - name: Enable buildkit cache + uses: actions/cache@v2 + with: + path: /tmp/buildkit-cache/buildkit-state.tar + key: ${{ runner.os }}-buildkit-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildkit- + + - name: Load buildkit state from cache + uses: dashevo/gh-action-cache-buildkit-state@v1 + with: + builder: buildx_buildkit_${{ steps.buildx.outputs.name }}0 + cache-path: /tmp/buildkit-cache + cache-max-size: 3g + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set suffix to Docker tags + uses: actions/github-script@v5 + id: suffix + if: github.event_name != 'workflow_dispatch' + with: + result-encoding: string + script: "return (context.payload.release.tag_name.includes('-dev') ? '-dev' : '');" + + - name: Set Docker tags and labels + id: docker_meta + uses: docker/metadata-action@v4 + with: + images: dashpay/drive + tags: | + type=match,pattern=v(\d+),group=1,enable=${{github.event_name == 'release'}} + type=match,pattern=v(\d+.\d+),group=1,enable=${{github.event_name == 'release'}} + type=match,pattern=v(\d+.\d+.\d+),group=1,enable=${{github.event_name == 'release'}} + type=match,pattern=v(.*),group=1,suffix=,enable=${{ contains(github.event.release.tag_name, '-dev') }} + type=match,pattern=v(.*),group=1,value=${{ github.event.inputs.tag }},enable=${{github.event_name == 'workflow_dispatch'}} + flavor: | + suffix=${{ steps.suffix.outputs.result }},onlatest=true + latest=${{ github.event_name == 'release' }} + + - name: Build and push Docker image for Drive + uses: docker/build-push-action@v2 + with: + context: . + builder: ${{ steps.buildx.outputs.name }} + file: ./packages/js-drive/Dockerfile + push: true + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + + release-dapi-docker-image: + name: Release DAPI to Docker Hub + runs-on: ubuntu-20.04 + steps: + - name: Check out repo + uses: actions/checkout@v2 + + - name: Setup Node.JS + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Enable corepack + run: corepack enable + + - name: Disable NPM audit + run: npm config set audit false + + - name: Enable Yarn unplugged modules cache + uses: actions/cache@v2 + with: + path: '.yarn/unplugged' + key: ${{ runner.os }}-yarn-unplugged-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-unplugged- + + - name: Install dependencies + run: yarn install + + - name: Build packages + run: yarn build --filter "+@dashevo/dapi" + + - name: Set up QEMU to run multi-arch builds + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker BuildX + id: buildx + uses: docker/setup-buildx-action@v1 + with: + version: v0.7.0 + install: true + driver-opts: image=moby/buildkit:buildx-stable-1 + + - name: Enable buildkit cache + uses: actions/cache@v2 + with: + path: /tmp/buildkit-cache/buildkit-state.tar + key: ${{ runner.os }}-buildkit-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildkit- + + - name: Load buildkit state from cache + uses: dashevo/gh-action-cache-buildkit-state@v1 + with: + builder: buildx_buildkit_${{ steps.buildx.outputs.name }}0 + cache-path: /tmp/buildkit-cache + cache-max-size: 3g + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set suffix to Docker tags + uses: actions/github-script@v5 + id: suffix + if: github.event_name != 'workflow_dispatch' + with: + result-encoding: string + script: "return (context.payload.release.tag_name.includes('-dev') ? '-dev' : '');" + + - name: Set Docker tags and labels + id: docker_meta + uses: docker/metadata-action@v4 + with: + images: dashpay/dapi + tags: | + type=match,pattern=v(\d+),group=1,enable=${{github.event_name == 'release'}} + type=match,pattern=v(\d+.\d+),group=1,enable=${{github.event_name == 'release'}} + type=match,pattern=v(\d+.\d+.\d+),group=1,enable=${{github.event_name == 'release'}} + type=match,pattern=v(.*),group=1,suffix=,enable=${{ contains(github.event.release.tag_name, '-dev') }} + type=match,pattern=v(.*),group=1,value=${{ github.event.inputs.tag }},enable=${{github.event_name == 'workflow_dispatch'}} + flavor: | + suffix=${{ steps.suffix.outputs.result }},onlatest=true + latest=${{ github.event_name == 'release' }} + + - name: Build and push Docker image + uses: docker/build-push-action@v2 + with: + context: . + builder: ${{ steps.buildx.outputs.name }} + file: ./packages/dapi/Dockerfile + push: true + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + + release-test-suite-docker-image: + name: Release Test Suite to Docker Hub + runs-on: ubuntu-20.04 + steps: + - name: Check out repo + uses: actions/checkout@v2 + + - name: Setup Node.JS + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Enable corepack + run: corepack enable + + - name: Disable NPM audit + run: npm config set audit false + + - name: Enable Yarn unplugged modules cache + uses: actions/cache@v2 + with: + path: '.yarn/unplugged' + key: ${{ runner.os }}-yarn-unplugged-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-unplugged- + + - name: Install dependencies + run: yarn install + + - name: Build packages + run: yarn build --filter "+@dashevo/platform-test-suite" + + - name: Set up QEMU to run multi-arch builds + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker BuildX + id: buildx + uses: docker/setup-buildx-action@v1 + with: + version: v0.7.0 + install: true + driver-opts: image=moby/buildkit:buildx-stable-1 + + - name: Enable buildkit cache + uses: actions/cache@v2 + with: + path: /tmp/buildkit-cache/buildkit-state.tar + key: ${{ runner.os }}-buildkit-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildkit- + + - name: Load buildkit state from cache + uses: dashevo/gh-action-cache-buildkit-state@v1 + with: + builder: buildx_buildkit_${{ steps.buildx.outputs.name }}0 + cache-path: /tmp/buildkit-cache + cache-max-size: 3g + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set suffix to Docker tags + uses: actions/github-script@v5 + id: suffix + if: github.event_name != 'workflow_dispatch' + with: + result-encoding: string + script: "return (context.payload.release.tag_name.includes('-dev') ? '-dev' : '');" + + - name: Set Docker tags and labels + id: docker_meta + uses: docker/metadata-action@v4 + with: + images: dashpay/platform-test-suite + tags: | + type=match,pattern=v(\d+),group=1,enable=${{github.event_name == 'release'}} + type=match,pattern=v(\d+.\d+),group=1,enable=${{github.event_name == 'release'}} + type=match,pattern=v(\d+.\d+.\d+),group=1,enable=${{github.event_name == 'release'}} + type=match,pattern=v(.*),group=1,suffix=,enable=${{ contains(github.event.release.tag_name, '-dev') }} + type=match,pattern=v(.*),group=1,value=${{ github.event.inputs.tag }},enable=${{github.event_name == 'workflow_dispatch'}} + flavor: | + suffix=${{ steps.suffix.outputs.result }},onlatest=true + latest=${{ github.event_name == 'release' }} + + - name: Build and push Docker image + uses: docker/build-push-action@v2 + with: + context: . + builder: ${{ steps.buildx.outputs.name }} + file: ./packages/platform-test-suite/Dockerfile + push: true + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000000..64694a007f0 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,107 @@ +name: Package Tests + +on: + workflow_call: + inputs: + package: + description: The package name to run tests for + type: string + required: true + start-local-network: + description: Does the specified package require local network to run tests + type: boolean + default: false + required: false +jobs: + test: + name: Test package + runs-on: ubuntu-20.04 + timeout-minutes: 60 + steps: + - name: Cancel previous runs + uses: styfle/cancel-workflow-action@0.9.1 + with: + access_token: ${{ github.token }} + + - name: Check out repo + uses: actions/checkout@v2 + + - name: Setup Node.JS + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Enable corepack + run: corepack enable + + - name: Disable NPM audit + run: npm config set audit false + + - name: Enable Yarn unplugged modules cache + uses: actions/cache@v2 + with: + path: '.yarn/unplugged' + key: ${{ runner.os }}-yarn-unplugged-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-unplugged- + + - name: Install dependencies + run: yarn install + + - name: Run ESLinter + run: yarn lint --filter "${{ inputs.package }}" + + - name: Build package and dependencies + run: yarn build --filter "+${{ inputs.package }}" + if: ${{ !inputs.start-local-network }} + + - name: Build all packages + run: yarn build + if: ${{ inputs.start-local-network }} + + - name: Create necessary dotenv files + run: | + cp packages/dapi/.env.example packages/dapi/.env + cp packages/js-drive/.env.example packages/js-drive/.env + if: ${{ !inputs.start-local-network }} + + - name: Set up Docker BuildX + id: buildx + uses: docker/setup-buildx-action@v1 + with: + version: v0.6.3 + install: true + driver-opts: image=moby/buildkit:buildx-stable-1 + if: ${{ inputs.start-local-network }} + + - name: Enable buildkit cache + uses: actions/cache@v2 + with: + path: /tmp/buildkit-cache/buildkit-state.tar + key: ${{ runner.os }}-buildkit-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildkit- + if: ${{ inputs.start-local-network }} + + - name: Load buildkit state from cache + uses: dashevo/gh-action-cache-buildkit-state@v1 + with: + builder: buildx_buildkit_${{ steps.buildx.outputs.name }}0 + cache-path: /tmp/buildkit-cache + cache-max-size: 3g + if: ${{ inputs.start-local-network }} + + - name: Setup local network + run: yarn configure + if: ${{ inputs.start-local-network }} + + - name: Start local network + run: yarn start + if: ${{ inputs.start-local-network }} + + - name: Run tests + run: yarn test --filter "${{ inputs.package }}" + + - name: Show Docker logs + if: ${{ failure() }} + uses: jwalton/gh-docker-logs@v2 diff --git a/.github/workflows/wallet-lib.yml b/.github/workflows/wallet-lib.yml new file mode 100644 index 00000000000..926ed4ada02 --- /dev/null +++ b/.github/workflows/wallet-lib.yml @@ -0,0 +1,29 @@ +name: Wallet Lib + +on: + workflow_dispatch: + pull_request: + branches: + - master + - v[0-9]+\.[0-9]+-dev + paths: + - .github/workflows/wallet-lib.yml + - .github/workflows/test.yml + - packages/wallet-lib/** + - packages/js-dapi-client/** + - packages/dapi-grpc/** + - packages/js-dpp/** + - packages/js-grpc-common/** + - packages/dashpay-contract/** + - packages/feature-flags-contract/** + - packages/dpns-contract/** + - packages/masternode-reward-shares-contract/** + - packages/dash-spv/** + +jobs: + js-wallet-lib-tests: + name: Run Wallet lib tests + uses: dashevo/platform/.github/workflows/test.yml@master + with: + package: '@dashevo/wallet-lib' + start-local-network: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..896f07b802b --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# ignore Mac OS metadata +.DS_Store + +# ignore JetBrains IDE project specific files +.idea + +# ignore VSCode project specific files +.vscode + +# Env file +.env + +# NYC test runnner +.nyc_output + +node_modules + +*.log + +# Ultra runner build cache +.ultra.cache.json + +# Yarn +.yarn/* +!.yarn/cache +!.yarn/constraints.pro +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +faucet-wallet-* diff --git a/.pnp.cjs b/.pnp.cjs new file mode 100755 index 00000000000..bed8d1c33be --- /dev/null +++ b/.pnp.cjs @@ -0,0 +1,30756 @@ +#!/usr/bin/env node +/* eslint-disable */ + +try { + Object.freeze({}).detectStrictMode = true; +} catch (error) { + throw new Error(`The whole PnP file got strict-mode-ified, which is known to break (Emscripten libraries aren't strict mode). This usually happens when the file goes through Babel.`); +} + +var __non_webpack_module__ = module; + +function $$SETUP_STATE(hydrateRuntimeState, basePath) { + return hydrateRuntimeState({ + "__info": [ + "This file is automatically generated. Do not touch it, or risk", + "your modifications being lost. We also recommend you not to read", + "it either without using the @yarnpkg/pnp package, as the data layout", + "is entirely unspecified and WILL change from a version to another." + ], + "dependencyTreeRoots": [ + { + "name": "@dashevo/platform", + "reference": "workspace:." + }, + { + "name": "@dashevo/bench-suite", + "reference": "workspace:packages/bench-suite" + }, + { + "name": "@dashevo/dapi", + "reference": "workspace:packages/dapi" + }, + { + "name": "@dashevo/dapi-grpc", + "reference": "workspace:packages/dapi-grpc" + }, + { + "name": "@dashevo/dash-spv", + "reference": "workspace:packages/dash-spv" + }, + { + "name": "dashmate", + "reference": "workspace:packages/dashmate" + }, + { + "name": "@dashevo/dashpay-contract", + "reference": "workspace:packages/dashpay-contract" + }, + { + "name": "@dashevo/dpns-contract", + "reference": "workspace:packages/dpns-contract" + }, + { + "name": "@dashevo/feature-flags-contract", + "reference": "workspace:packages/feature-flags-contract" + }, + { + "name": "@dashevo/dapi-client", + "reference": "workspace:packages/js-dapi-client" + }, + { + "name": "dash", + "reference": "workspace:packages/js-dash-sdk" + }, + { + "name": "@dashevo/dpp", + "reference": "workspace:packages/js-dpp" + }, + { + "name": "@dashevo/drive", + "reference": "workspace:packages/js-drive" + }, + { + "name": "@dashevo/grpc-common", + "reference": "workspace:packages/js-grpc-common" + }, + { + "name": "@dashevo/masternode-reward-shares-contract", + "reference": "workspace:packages/masternode-reward-shares-contract" + }, + { + "name": "@dashevo/platform-test-suite", + "reference": "workspace:packages/platform-test-suite" + }, + { + "name": "@dashevo/wallet-lib", + "reference": "workspace:packages/wallet-lib" + } + ], + "enableTopLevelFallback": true, + "ignorePatternData": "(^(?:\\.yarn\\/sdks(?:\\/(?!\\.{1,2}(?:\\/|$))(?:(?:(?!(?:^|\\/)\\.{1,2}(?:\\/|$)).)*?)|$))$)", + "fallbackExclusionList": [ + ["@dashevo/bench-suite", ["workspace:packages/bench-suite"]], + ["@dashevo/dapi", ["workspace:packages/dapi"]], + ["@dashevo/dapi-client", ["workspace:packages/js-dapi-client"]], + ["@dashevo/dapi-grpc", ["workspace:packages/dapi-grpc"]], + ["@dashevo/dash-spv", ["workspace:packages/dash-spv"]], + ["@dashevo/dashpay-contract", ["workspace:packages/dashpay-contract"]], + ["@dashevo/dpns-contract", ["workspace:packages/dpns-contract"]], + ["@dashevo/dpp", ["workspace:packages/js-dpp"]], + ["@dashevo/drive", ["workspace:packages/js-drive"]], + ["@dashevo/feature-flags-contract", ["workspace:packages/feature-flags-contract"]], + ["@dashevo/grpc-common", ["workspace:packages/js-grpc-common"]], + ["@dashevo/masternode-reward-shares-contract", ["workspace:packages/masternode-reward-shares-contract"]], + ["@dashevo/platform", ["workspace:."]], + ["@dashevo/platform-test-suite", ["workspace:packages/platform-test-suite"]], + ["@dashevo/wallet-lib", ["workspace:packages/wallet-lib"]], + ["dash", ["workspace:packages/js-dash-sdk"]], + ["dashmate", ["workspace:packages/dashmate"]] + ], + "fallbackPool": [ + ], + "packageRegistryData": [ + [null, [ + [null, { + "packageLocation": "./", + "packageDependencies": [ + ["add-stream", "npm:1.0.0"], + ["conventional-changelog", "npm:3.1.24"], + ["conventional-changelog-dash", "https://github.com/dashevo/conventional-changelog-dash.git#commit=3d4d77e2cea876a27b92641c28b15aedf13eb788"], + ["semver", "npm:7.3.5"], + ["tempfile", "npm:3.0.0"], + ["ultra-runner", "npm:3.10.5"] + ], + "linkType": "SOFT", + }] + ]], + ["@apidevtools/json-schema-ref-parser", [ + ["npm:8.0.0", { + "packageLocation": "./.yarn/cache/@apidevtools-json-schema-ref-parser-npm-8.0.0-3f5ddbd534-3875f3c2fc.zip/node_modules/@apidevtools/json-schema-ref-parser/", + "packageDependencies": [ + ["@apidevtools/json-schema-ref-parser", "npm:8.0.0"], + ["@jsdevtools/ono", "npm:7.1.3"], + ["call-me-maybe", "npm:1.0.1"], + ["js-yaml", "npm:3.14.1"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/code-frame", [ + ["npm:7.12.11", { + "packageLocation": "./.yarn/cache/@babel-code-frame-npm-7.12.11-1a9a1b277f-3963eff3eb.zip/node_modules/@babel/code-frame/", + "packageDependencies": [ + ["@babel/code-frame", "npm:7.12.11"], + ["@babel/highlight", "npm:7.16.10"] + ], + "linkType": "HARD", + }], + ["npm:7.16.7", { + "packageLocation": "./.yarn/cache/@babel-code-frame-npm-7.16.7-093eb9e124-db2f7faa31.zip/node_modules/@babel/code-frame/", + "packageDependencies": [ + ["@babel/code-frame", "npm:7.16.7"], + ["@babel/highlight", "npm:7.16.10"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/compat-data", [ + ["npm:7.16.4", { + "packageLocation": "./.yarn/cache/@babel-compat-data-npm-7.16.4-9128f11195-4949ce54ea.zip/node_modules/@babel/compat-data/", + "packageDependencies": [ + ["@babel/compat-data", "npm:7.16.4"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/core", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-core-npm-7.16.0-5612f0ce31-a140f669da.zip/node_modules/@babel/core/", + "packageDependencies": [ + ["@babel/core", "npm:7.16.0"], + ["@babel/code-frame", "npm:7.16.7"], + ["@babel/generator", "npm:7.17.3"], + ["@babel/helper-compilation-targets", "virtual:5612f0ce311a7844500ba5948d5d47b8376a902bfa55b1e3797dd916bf18f7512ba75518521ba3bc2f39f6565fb127bcb8fee5b440624dbebaf9ac5f3566ebd0#npm:7.16.3"], + ["@babel/helper-module-transforms", "npm:7.16.0"], + ["@babel/helpers", "npm:7.16.3"], + ["@babel/parser", "npm:7.17.3"], + ["@babel/template", "npm:7.16.7"], + ["@babel/traverse", "npm:7.17.3"], + ["@babel/types", "npm:7.17.0"], + ["convert-source-map", "npm:1.8.0"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["gensync", "npm:1.0.0-beta.2"], + ["json5", "npm:2.2.0"], + ["semver", "npm:6.3.0"], + ["source-map", "npm:0.5.7"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/generator", [ + ["npm:7.17.3", { + "packageLocation": "./.yarn/cache/@babel-generator-npm-7.17.3-b206625c17-ddf70e3489.zip/node_modules/@babel/generator/", + "packageDependencies": [ + ["@babel/generator", "npm:7.17.3"], + ["@babel/types", "npm:7.17.0"], + ["jsesc", "npm:2.5.2"], + ["source-map", "npm:0.5.7"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-annotate-as-pure", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-helper-annotate-as-pure-npm-7.16.0-7d5d6eb28a-0db7610698.zip/node_modules/@babel/helper-annotate-as-pure/", + "packageDependencies": [ + ["@babel/helper-annotate-as-pure", "npm:7.16.0"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-builder-binary-assignment-operator-visitor", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-helper-builder-binary-assignment-operator-visitor-npm-7.16.0-8218316996-01beb9f3f2.zip/node_modules/@babel/helper-builder-binary-assignment-operator-visitor/", + "packageDependencies": [ + ["@babel/helper-builder-binary-assignment-operator-visitor", "npm:7.16.0"], + ["@babel/helper-explode-assignable-expression", "npm:7.16.0"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-compilation-targets", [ + ["npm:7.16.3", { + "packageLocation": "./.yarn/cache/@babel-helper-compilation-targets-npm-7.16.3-200287cc80-038bcd43ac.zip/node_modules/@babel/helper-compilation-targets/", + "packageDependencies": [ + ["@babel/helper-compilation-targets", "npm:7.16.3"] + ], + "linkType": "SOFT", + }], + ["virtual:5612f0ce311a7844500ba5948d5d47b8376a902bfa55b1e3797dd916bf18f7512ba75518521ba3bc2f39f6565fb127bcb8fee5b440624dbebaf9ac5f3566ebd0#npm:7.16.3", { + "packageLocation": "./.yarn/__virtual__/@babel-helper-compilation-targets-virtual-6c04311cc1/0/cache/@babel-helper-compilation-targets-npm-7.16.3-200287cc80-038bcd43ac.zip/node_modules/@babel/helper-compilation-targets/", + "packageDependencies": [ + ["@babel/helper-compilation-targets", "virtual:5612f0ce311a7844500ba5948d5d47b8376a902bfa55b1e3797dd916bf18f7512ba75518521ba3bc2f39f6565fb127bcb8fee5b440624dbebaf9ac5f3566ebd0#npm:7.16.3"], + ["@babel/compat-data", "npm:7.16.4"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-validator-option", "npm:7.14.5"], + ["@types/babel__core", null], + ["browserslist", "npm:4.18.1"], + ["semver", "npm:6.3.0"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-create-class-features-plugin", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-helper-create-class-features-plugin-npm-7.16.0-99dc71616c-0f7d1b8d41.zip/node_modules/@babel/helper-create-class-features-plugin/", + "packageDependencies": [ + ["@babel/helper-create-class-features-plugin", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:55b2d80e5e15a4ec7d794b3909e34d28bb0c0d5d2df927c2b6280ce198de35eeb764a43aaf6f9cdf47a4e79d1560b818c4add7ca648e433f18c492b6c58754ee#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-helper-create-class-features-plugin-virtual-48097392c9/0/cache/@babel-helper-create-class-features-plugin-npm-7.16.0-99dc71616c-0f7d1b8d41.zip/node_modules/@babel/helper-create-class-features-plugin/", + "packageDependencies": [ + ["@babel/helper-create-class-features-plugin", "virtual:55b2d80e5e15a4ec7d794b3909e34d28bb0c0d5d2df927c2b6280ce198de35eeb764a43aaf6f9cdf47a4e79d1560b818c4add7ca648e433f18c492b6c58754ee#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-annotate-as-pure", "npm:7.16.0"], + ["@babel/helper-function-name", "npm:7.16.7"], + ["@babel/helper-member-expression-to-functions", "npm:7.16.0"], + ["@babel/helper-optimise-call-expression", "npm:7.16.0"], + ["@babel/helper-replace-supers", "npm:7.16.0"], + ["@babel/helper-split-export-declaration", "npm:7.16.7"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-create-regexp-features-plugin", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-helper-create-regexp-features-plugin-npm-7.16.0-9afb84be3e-d6230477e1.zip/node_modules/@babel/helper-create-regexp-features-plugin/", + "packageDependencies": [ + ["@babel/helper-create-regexp-features-plugin", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:9ce5a9282e152f3fba76420378e4252441c87f195af85577209bdfda27fc39e017652eda9bb3af430ee582fe6a109cdb6f73d790b962837af8f39a627b435bc0#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-helper-create-regexp-features-plugin-virtual-6e69a91fe7/0/cache/@babel-helper-create-regexp-features-plugin-npm-7.16.0-9afb84be3e-d6230477e1.zip/node_modules/@babel/helper-create-regexp-features-plugin/", + "packageDependencies": [ + ["@babel/helper-create-regexp-features-plugin", "virtual:9ce5a9282e152f3fba76420378e4252441c87f195af85577209bdfda27fc39e017652eda9bb3af430ee582fe6a109cdb6f73d790b962837af8f39a627b435bc0#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-annotate-as-pure", "npm:7.16.0"], + ["@types/babel__core", null], + ["regexpu-core", "npm:4.8.0"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-define-polyfill-provider", [ + ["npm:0.3.0", { + "packageLocation": "./.yarn/cache/@babel-helper-define-polyfill-provider-npm-0.3.0-c19f133e9a-372378ac42.zip/node_modules/@babel/helper-define-polyfill-provider/", + "packageDependencies": [ + ["@babel/helper-define-polyfill-provider", "npm:0.3.0"] + ], + "linkType": "SOFT", + }], + ["virtual:ef89d8c000df9e14e09ca866bb81153d80fba90e9d2193815b4078b792631c08caaf98362eca8cf9a3c29249b6d2c9e7d7b24629716c4bb0b5ab719ccefcb2b2#npm:0.3.0", { + "packageLocation": "./.yarn/__virtual__/@babel-helper-define-polyfill-provider-virtual-39e41632a2/0/cache/@babel-helper-define-polyfill-provider-npm-0.3.0-c19f133e9a-372378ac42.zip/node_modules/@babel/helper-define-polyfill-provider/", + "packageDependencies": [ + ["@babel/helper-define-polyfill-provider", "virtual:ef89d8c000df9e14e09ca866bb81153d80fba90e9d2193815b4078b792631c08caaf98362eca8cf9a3c29249b6d2c9e7d7b24629716c4bb0b5ab719ccefcb2b2#npm:0.3.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-compilation-targets", "virtual:5612f0ce311a7844500ba5948d5d47b8376a902bfa55b1e3797dd916bf18f7512ba75518521ba3bc2f39f6565fb127bcb8fee5b440624dbebaf9ac5f3566ebd0#npm:7.16.3"], + ["@babel/helper-module-imports", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/traverse", "npm:7.17.3"], + ["@types/babel__core", null], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["lodash.debounce", "npm:4.0.8"], + ["resolve", "patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b"], + ["semver", "npm:6.3.0"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-environment-visitor", [ + ["npm:7.16.7", { + "packageLocation": "./.yarn/cache/@babel-helper-environment-visitor-npm-7.16.7-3ee2ba2019-c03a10105d.zip/node_modules/@babel/helper-environment-visitor/", + "packageDependencies": [ + ["@babel/helper-environment-visitor", "npm:7.16.7"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-explode-assignable-expression", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-helper-explode-assignable-expression-npm-7.16.0-c7497452bc-563352b5e9.zip/node_modules/@babel/helper-explode-assignable-expression/", + "packageDependencies": [ + ["@babel/helper-explode-assignable-expression", "npm:7.16.0"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-function-name", [ + ["npm:7.16.7", { + "packageLocation": "./.yarn/cache/@babel-helper-function-name-npm-7.16.7-aa24c7b296-fc77cbe7b1.zip/node_modules/@babel/helper-function-name/", + "packageDependencies": [ + ["@babel/helper-function-name", "npm:7.16.7"], + ["@babel/helper-get-function-arity", "npm:7.16.7"], + ["@babel/template", "npm:7.16.7"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-get-function-arity", [ + ["npm:7.16.7", { + "packageLocation": "./.yarn/cache/@babel-helper-get-function-arity-npm-7.16.7-987b1b1bed-25d969fb20.zip/node_modules/@babel/helper-get-function-arity/", + "packageDependencies": [ + ["@babel/helper-get-function-arity", "npm:7.16.7"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-hoist-variables", [ + ["npm:7.16.7", { + "packageLocation": "./.yarn/cache/@babel-helper-hoist-variables-npm-7.16.7-25cc3abba4-6ae1641f4a.zip/node_modules/@babel/helper-hoist-variables/", + "packageDependencies": [ + ["@babel/helper-hoist-variables", "npm:7.16.7"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-member-expression-to-functions", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-helper-member-expression-to-functions-npm-7.16.0-714f06863b-58ef8e3a4a.zip/node_modules/@babel/helper-member-expression-to-functions/", + "packageDependencies": [ + ["@babel/helper-member-expression-to-functions", "npm:7.16.0"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-module-imports", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-helper-module-imports-npm-7.16.0-ae62b2ede7-8e1eb9ac39.zip/node_modules/@babel/helper-module-imports/", + "packageDependencies": [ + ["@babel/helper-module-imports", "npm:7.16.0"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-module-transforms", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-helper-module-transforms-npm-7.16.0-928840049e-a3d0e5556f.zip/node_modules/@babel/helper-module-transforms/", + "packageDependencies": [ + ["@babel/helper-module-transforms", "npm:7.16.0"], + ["@babel/helper-module-imports", "npm:7.16.0"], + ["@babel/helper-replace-supers", "npm:7.16.0"], + ["@babel/helper-simple-access", "npm:7.16.0"], + ["@babel/helper-split-export-declaration", "npm:7.16.7"], + ["@babel/helper-validator-identifier", "npm:7.16.7"], + ["@babel/template", "npm:7.16.7"], + ["@babel/traverse", "npm:7.17.3"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-optimise-call-expression", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-helper-optimise-call-expression-npm-7.16.0-fd091f8fdf-121ae6054f.zip/node_modules/@babel/helper-optimise-call-expression/", + "packageDependencies": [ + ["@babel/helper-optimise-call-expression", "npm:7.16.0"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-plugin-utils", [ + ["npm:7.14.5", { + "packageLocation": "./.yarn/cache/@babel-helper-plugin-utils-npm-7.14.5-e35eef11cb-fe20e90a24.zip/node_modules/@babel/helper-plugin-utils/", + "packageDependencies": [ + ["@babel/helper-plugin-utils", "npm:7.14.5"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-remap-async-to-generator", [ + ["npm:7.16.4", { + "packageLocation": "./.yarn/cache/@babel-helper-remap-async-to-generator-npm-7.16.4-691d3036ec-debe997695.zip/node_modules/@babel/helper-remap-async-to-generator/", + "packageDependencies": [ + ["@babel/helper-remap-async-to-generator", "npm:7.16.4"], + ["@babel/helper-annotate-as-pure", "npm:7.16.0"], + ["@babel/helper-wrap-function", "npm:7.16.0"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-replace-supers", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-helper-replace-supers-npm-7.16.0-e04b4caf96-61f04bbe05.zip/node_modules/@babel/helper-replace-supers/", + "packageDependencies": [ + ["@babel/helper-replace-supers", "npm:7.16.0"], + ["@babel/helper-member-expression-to-functions", "npm:7.16.0"], + ["@babel/helper-optimise-call-expression", "npm:7.16.0"], + ["@babel/traverse", "npm:7.17.3"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-simple-access", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-helper-simple-access-npm-7.16.0-d2675c6f1c-2d7155f318.zip/node_modules/@babel/helper-simple-access/", + "packageDependencies": [ + ["@babel/helper-simple-access", "npm:7.16.0"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-skip-transparent-expression-wrappers", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-helper-skip-transparent-expression-wrappers-npm-7.16.0-caad6e8361-b9ed2896eb.zip/node_modules/@babel/helper-skip-transparent-expression-wrappers/", + "packageDependencies": [ + ["@babel/helper-skip-transparent-expression-wrappers", "npm:7.16.0"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-split-export-declaration", [ + ["npm:7.16.7", { + "packageLocation": "./.yarn/cache/@babel-helper-split-export-declaration-npm-7.16.7-5b9ae90171-e10aaf1354.zip/node_modules/@babel/helper-split-export-declaration/", + "packageDependencies": [ + ["@babel/helper-split-export-declaration", "npm:7.16.7"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-validator-identifier", [ + ["npm:7.16.7", { + "packageLocation": "./.yarn/cache/@babel-helper-validator-identifier-npm-7.16.7-8599fb00fc-dbb3db9d18.zip/node_modules/@babel/helper-validator-identifier/", + "packageDependencies": [ + ["@babel/helper-validator-identifier", "npm:7.16.7"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-validator-option", [ + ["npm:7.14.5", { + "packageLocation": "./.yarn/cache/@babel-helper-validator-option-npm-7.14.5-fd38dcf0bc-1b25c34a5c.zip/node_modules/@babel/helper-validator-option/", + "packageDependencies": [ + ["@babel/helper-validator-option", "npm:7.14.5"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-wrap-function", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-helper-wrap-function-npm-7.16.0-58e751a57c-2bb4e05f49.zip/node_modules/@babel/helper-wrap-function/", + "packageDependencies": [ + ["@babel/helper-wrap-function", "npm:7.16.0"], + ["@babel/helper-function-name", "npm:7.16.7"], + ["@babel/template", "npm:7.16.7"], + ["@babel/traverse", "npm:7.17.3"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helpers", [ + ["npm:7.16.3", { + "packageLocation": "./.yarn/cache/@babel-helpers-npm-7.16.3-02251c435f-b725b1aab7.zip/node_modules/@babel/helpers/", + "packageDependencies": [ + ["@babel/helpers", "npm:7.16.3"], + ["@babel/template", "npm:7.16.7"], + ["@babel/traverse", "npm:7.17.3"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/highlight", [ + ["npm:7.16.10", { + "packageLocation": "./.yarn/cache/@babel-highlight-npm-7.16.10-626c03326c-1f1bdd752a.zip/node_modules/@babel/highlight/", + "packageDependencies": [ + ["@babel/highlight", "npm:7.16.10"], + ["@babel/helper-validator-identifier", "npm:7.16.7"], + ["chalk", "npm:2.4.2"], + ["js-tokens", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/parser", [ + ["npm:7.17.3", { + "packageLocation": "./.yarn/cache/@babel-parser-npm-7.17.3-1c3b6747e0-311869baef.zip/node_modules/@babel/parser/", + "packageDependencies": [ + ["@babel/parser", "npm:7.17.3"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression", [ + ["npm:7.16.2", { + "packageLocation": "./.yarn/cache/@babel-plugin-bugfix-safari-id-destructuring-collision-in-function-expression-npm-7.16.2-1aa5b1f875-6ed9dbbf18.zip/node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/", + "packageDependencies": [ + ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression", "npm:7.16.2"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.2", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-bugfix-safari-id-destructuring-collision-in-function-expression-virtual-8c7c09b873/0/cache/@babel-plugin-bugfix-safari-id-destructuring-collision-in-function-expression-npm-7.16.2-1aa5b1f875-6ed9dbbf18.zip/node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/", + "packageDependencies": [ + ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.2"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining-npm-7.16.0-f3fb88813d-bb11547929.zip/node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/", + "packageDependencies": [ + ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining-virtual-17c307c7cd/0/cache/@babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining-npm-7.16.0-f3fb88813d-bb11547929.zip/node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/", + "packageDependencies": [ + ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/helper-skip-transparent-expression-wrappers", "npm:7.16.0"], + ["@babel/plugin-proposal-optional-chaining", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-async-generator-functions", [ + ["npm:7.16.4", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-async-generator-functions-npm-7.16.4-8379d8fc90-dcd5a76ee1.zip/node_modules/@babel/plugin-proposal-async-generator-functions/", + "packageDependencies": [ + ["@babel/plugin-proposal-async-generator-functions", "npm:7.16.4"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.4", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-async-generator-functions-virtual-ca854b71a7/0/cache/@babel-plugin-proposal-async-generator-functions-npm-7.16.4-8379d8fc90-dcd5a76ee1.zip/node_modules/@babel/plugin-proposal-async-generator-functions/", + "packageDependencies": [ + ["@babel/plugin-proposal-async-generator-functions", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.4"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/helper-remap-async-to-generator", "npm:7.16.4"], + ["@babel/plugin-syntax-async-generators", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.4"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-class-properties", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-class-properties-npm-7.16.0-9106ec25a5-b1665ced55.zip/node_modules/@babel/plugin-proposal-class-properties/", + "packageDependencies": [ + ["@babel/plugin-proposal-class-properties", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-class-properties-virtual-55b2d80e5e/0/cache/@babel-plugin-proposal-class-properties-npm-7.16.0-9106ec25a5-b1665ced55.zip/node_modules/@babel/plugin-proposal-class-properties/", + "packageDependencies": [ + ["@babel/plugin-proposal-class-properties", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-create-class-features-plugin", "virtual:55b2d80e5e15a4ec7d794b3909e34d28bb0c0d5d2df927c2b6280ce198de35eeb764a43aaf6f9cdf47a4e79d1560b818c4add7ca648e433f18c492b6c58754ee#npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-class-static-block", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-class-static-block-npm-7.16.0-4ac545628c-59c4bb3d6a.zip/node_modules/@babel/plugin-proposal-class-static-block/", + "packageDependencies": [ + ["@babel/plugin-proposal-class-static-block", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-class-static-block-virtual-bafa506984/0/cache/@babel-plugin-proposal-class-static-block-npm-7.16.0-4ac545628c-59c4bb3d6a.zip/node_modules/@babel/plugin-proposal-class-static-block/", + "packageDependencies": [ + ["@babel/plugin-proposal-class-static-block", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-create-class-features-plugin", "virtual:55b2d80e5e15a4ec7d794b3909e34d28bb0c0d5d2df927c2b6280ce198de35eeb764a43aaf6f9cdf47a4e79d1560b818c4add7ca648e433f18c492b6c58754ee#npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/plugin-syntax-class-static-block", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-dynamic-import", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-dynamic-import-npm-7.16.0-8de1a50b8f-4027da6404.zip/node_modules/@babel/plugin-proposal-dynamic-import/", + "packageDependencies": [ + ["@babel/plugin-proposal-dynamic-import", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-dynamic-import-virtual-1b8e538b0d/0/cache/@babel-plugin-proposal-dynamic-import-npm-7.16.0-8de1a50b8f-4027da6404.zip/node_modules/@babel/plugin-proposal-dynamic-import/", + "packageDependencies": [ + ["@babel/plugin-proposal-dynamic-import", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/plugin-syntax-dynamic-import", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-export-namespace-from", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-export-namespace-from-npm-7.16.0-3523b50929-0bdc166ac4.zip/node_modules/@babel/plugin-proposal-export-namespace-from/", + "packageDependencies": [ + ["@babel/plugin-proposal-export-namespace-from", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-export-namespace-from-virtual-6191cdeed9/0/cache/@babel-plugin-proposal-export-namespace-from-npm-7.16.0-3523b50929-0bdc166ac4.zip/node_modules/@babel/plugin-proposal-export-namespace-from/", + "packageDependencies": [ + ["@babel/plugin-proposal-export-namespace-from", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/plugin-syntax-export-namespace-from", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-json-strings", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-json-strings-npm-7.16.0-1070c01042-fa93be8eff.zip/node_modules/@babel/plugin-proposal-json-strings/", + "packageDependencies": [ + ["@babel/plugin-proposal-json-strings", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-json-strings-virtual-f2a02ece98/0/cache/@babel-plugin-proposal-json-strings-npm-7.16.0-1070c01042-fa93be8eff.zip/node_modules/@babel/plugin-proposal-json-strings/", + "packageDependencies": [ + ["@babel/plugin-proposal-json-strings", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/plugin-syntax-json-strings", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-logical-assignment-operators", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-logical-assignment-operators-npm-7.16.0-8163433ffc-7e6cd10248.zip/node_modules/@babel/plugin-proposal-logical-assignment-operators/", + "packageDependencies": [ + ["@babel/plugin-proposal-logical-assignment-operators", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-logical-assignment-operators-virtual-38f94fdf2c/0/cache/@babel-plugin-proposal-logical-assignment-operators-npm-7.16.0-8163433ffc-7e6cd10248.zip/node_modules/@babel/plugin-proposal-logical-assignment-operators/", + "packageDependencies": [ + ["@babel/plugin-proposal-logical-assignment-operators", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/plugin-syntax-logical-assignment-operators", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.10.4"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-nullish-coalescing-operator", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-nullish-coalescing-operator-npm-7.16.0-bdd28f11cb-e50f949299.zip/node_modules/@babel/plugin-proposal-nullish-coalescing-operator/", + "packageDependencies": [ + ["@babel/plugin-proposal-nullish-coalescing-operator", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-nullish-coalescing-operator-virtual-a80e6fae80/0/cache/@babel-plugin-proposal-nullish-coalescing-operator-npm-7.16.0-bdd28f11cb-e50f949299.zip/node_modules/@babel/plugin-proposal-nullish-coalescing-operator/", + "packageDependencies": [ + ["@babel/plugin-proposal-nullish-coalescing-operator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/plugin-syntax-nullish-coalescing-operator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-numeric-separator", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-numeric-separator-npm-7.16.0-5852a76307-eb7895a4f3.zip/node_modules/@babel/plugin-proposal-numeric-separator/", + "packageDependencies": [ + ["@babel/plugin-proposal-numeric-separator", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-numeric-separator-virtual-426cfab729/0/cache/@babel-plugin-proposal-numeric-separator-npm-7.16.0-5852a76307-eb7895a4f3.zip/node_modules/@babel/plugin-proposal-numeric-separator/", + "packageDependencies": [ + ["@babel/plugin-proposal-numeric-separator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/plugin-syntax-numeric-separator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.10.4"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-object-rest-spread", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-object-rest-spread-npm-7.16.0-f193853f3b-c7716ba50e.zip/node_modules/@babel/plugin-proposal-object-rest-spread/", + "packageDependencies": [ + ["@babel/plugin-proposal-object-rest-spread", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-object-rest-spread-virtual-e55dacf459/0/cache/@babel-plugin-proposal-object-rest-spread-npm-7.16.0-f193853f3b-c7716ba50e.zip/node_modules/@babel/plugin-proposal-object-rest-spread/", + "packageDependencies": [ + ["@babel/plugin-proposal-object-rest-spread", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/compat-data", "npm:7.16.4"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-compilation-targets", "virtual:5612f0ce311a7844500ba5948d5d47b8376a902bfa55b1e3797dd916bf18f7512ba75518521ba3bc2f39f6565fb127bcb8fee5b440624dbebaf9ac5f3566ebd0#npm:7.16.3"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/plugin-syntax-object-rest-spread", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/plugin-transform-parameters", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.3"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-optional-catch-binding", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-optional-catch-binding-npm-7.16.0-122cc09c2e-5003a1d48f.zip/node_modules/@babel/plugin-proposal-optional-catch-binding/", + "packageDependencies": [ + ["@babel/plugin-proposal-optional-catch-binding", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-optional-catch-binding-virtual-93c06ca960/0/cache/@babel-plugin-proposal-optional-catch-binding-npm-7.16.0-122cc09c2e-5003a1d48f.zip/node_modules/@babel/plugin-proposal-optional-catch-binding/", + "packageDependencies": [ + ["@babel/plugin-proposal-optional-catch-binding", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/plugin-syntax-optional-catch-binding", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-optional-chaining", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-optional-chaining-npm-7.16.0-fe3431862e-8301e08292.zip/node_modules/@babel/plugin-proposal-optional-chaining/", + "packageDependencies": [ + ["@babel/plugin-proposal-optional-chaining", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-optional-chaining-virtual-b4fb06a7ac/0/cache/@babel-plugin-proposal-optional-chaining-npm-7.16.0-fe3431862e-8301e08292.zip/node_modules/@babel/plugin-proposal-optional-chaining/", + "packageDependencies": [ + ["@babel/plugin-proposal-optional-chaining", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/helper-skip-transparent-expression-wrappers", "npm:7.16.0"], + ["@babel/plugin-syntax-optional-chaining", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-private-methods", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-private-methods-npm-7.16.0-9036fdd8d2-6f648f54ea.zip/node_modules/@babel/plugin-proposal-private-methods/", + "packageDependencies": [ + ["@babel/plugin-proposal-private-methods", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-private-methods-virtual-456070b6f1/0/cache/@babel-plugin-proposal-private-methods-npm-7.16.0-9036fdd8d2-6f648f54ea.zip/node_modules/@babel/plugin-proposal-private-methods/", + "packageDependencies": [ + ["@babel/plugin-proposal-private-methods", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-create-class-features-plugin", "virtual:55b2d80e5e15a4ec7d794b3909e34d28bb0c0d5d2df927c2b6280ce198de35eeb764a43aaf6f9cdf47a4e79d1560b818c4add7ca648e433f18c492b6c58754ee#npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-private-property-in-object", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-private-property-in-object-npm-7.16.0-84109b160c-9098fb34f4.zip/node_modules/@babel/plugin-proposal-private-property-in-object/", + "packageDependencies": [ + ["@babel/plugin-proposal-private-property-in-object", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-private-property-in-object-virtual-60f55ac87f/0/cache/@babel-plugin-proposal-private-property-in-object-npm-7.16.0-84109b160c-9098fb34f4.zip/node_modules/@babel/plugin-proposal-private-property-in-object/", + "packageDependencies": [ + ["@babel/plugin-proposal-private-property-in-object", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-annotate-as-pure", "npm:7.16.0"], + ["@babel/helper-create-class-features-plugin", "virtual:55b2d80e5e15a4ec7d794b3909e34d28bb0c0d5d2df927c2b6280ce198de35eeb764a43aaf6f9cdf47a4e79d1560b818c4add7ca648e433f18c492b6c58754ee#npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/plugin-syntax-private-property-in-object", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-proposal-unicode-property-regex", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-proposal-unicode-property-regex-npm-7.16.0-13487b534c-f26b76c9aa.zip/node_modules/@babel/plugin-proposal-unicode-property-regex/", + "packageDependencies": [ + ["@babel/plugin-proposal-unicode-property-regex", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-proposal-unicode-property-regex-virtual-9ce5a9282e/0/cache/@babel-plugin-proposal-unicode-property-regex-npm-7.16.0-13487b534c-f26b76c9aa.zip/node_modules/@babel/plugin-proposal-unicode-property-regex/", + "packageDependencies": [ + ["@babel/plugin-proposal-unicode-property-regex", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-create-regexp-features-plugin", "virtual:9ce5a9282e152f3fba76420378e4252441c87f195af85577209bdfda27fc39e017652eda9bb3af430ee582fe6a109cdb6f73d790b962837af8f39a627b435bc0#npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-async-generators", [ + ["npm:7.8.4", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-async-generators-npm-7.8.4-d10cf993c9-7ed1c1d9b9.zip/node_modules/@babel/plugin-syntax-async-generators/", + "packageDependencies": [ + ["@babel/plugin-syntax-async-generators", "npm:7.8.4"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.4", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-async-generators-virtual-1dc64866de/0/cache/@babel-plugin-syntax-async-generators-npm-7.8.4-d10cf993c9-7ed1c1d9b9.zip/node_modules/@babel/plugin-syntax-async-generators/", + "packageDependencies": [ + ["@babel/plugin-syntax-async-generators", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.4"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-class-properties", [ + ["npm:7.12.13", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-class-properties-npm-7.12.13-002ee9d930-24f34b196d.zip/node_modules/@babel/plugin-syntax-class-properties/", + "packageDependencies": [ + ["@babel/plugin-syntax-class-properties", "npm:7.12.13"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.12.13", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-class-properties-virtual-670280f92c/0/cache/@babel-plugin-syntax-class-properties-npm-7.12.13-002ee9d930-24f34b196d.zip/node_modules/@babel/plugin-syntax-class-properties/", + "packageDependencies": [ + ["@babel/plugin-syntax-class-properties", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.12.13"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-class-static-block", [ + ["npm:7.14.5", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-class-static-block-npm-7.14.5-7bdd0ff1b3-3e80814b5b.zip/node_modules/@babel/plugin-syntax-class-static-block/", + "packageDependencies": [ + ["@babel/plugin-syntax-class-static-block", "npm:7.14.5"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.14.5", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-class-static-block-virtual-ac1fa6a423/0/cache/@babel-plugin-syntax-class-static-block-npm-7.14.5-7bdd0ff1b3-3e80814b5b.zip/node_modules/@babel/plugin-syntax-class-static-block/", + "packageDependencies": [ + ["@babel/plugin-syntax-class-static-block", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.14.5"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-dynamic-import", [ + ["npm:7.8.3", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-dynamic-import-npm-7.8.3-fb9ff5634a-ce307af83c.zip/node_modules/@babel/plugin-syntax-dynamic-import/", + "packageDependencies": [ + ["@babel/plugin-syntax-dynamic-import", "npm:7.8.3"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-dynamic-import-virtual-8710e496d5/0/cache/@babel-plugin-syntax-dynamic-import-npm-7.8.3-fb9ff5634a-ce307af83c.zip/node_modules/@babel/plugin-syntax-dynamic-import/", + "packageDependencies": [ + ["@babel/plugin-syntax-dynamic-import", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-export-namespace-from", [ + ["npm:7.8.3", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-export-namespace-from-npm-7.8.3-1747201aa9-85740478be.zip/node_modules/@babel/plugin-syntax-export-namespace-from/", + "packageDependencies": [ + ["@babel/plugin-syntax-export-namespace-from", "npm:7.8.3"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-export-namespace-from-virtual-df89174721/0/cache/@babel-plugin-syntax-export-namespace-from-npm-7.8.3-1747201aa9-85740478be.zip/node_modules/@babel/plugin-syntax-export-namespace-from/", + "packageDependencies": [ + ["@babel/plugin-syntax-export-namespace-from", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-json-strings", [ + ["npm:7.8.3", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-json-strings-npm-7.8.3-6dc7848179-bf5aea1f31.zip/node_modules/@babel/plugin-syntax-json-strings/", + "packageDependencies": [ + ["@babel/plugin-syntax-json-strings", "npm:7.8.3"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-json-strings-virtual-3be474a273/0/cache/@babel-plugin-syntax-json-strings-npm-7.8.3-6dc7848179-bf5aea1f31.zip/node_modules/@babel/plugin-syntax-json-strings/", + "packageDependencies": [ + ["@babel/plugin-syntax-json-strings", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-logical-assignment-operators", [ + ["npm:7.10.4", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-logical-assignment-operators-npm-7.10.4-72ae00fdf6-aff3357703.zip/node_modules/@babel/plugin-syntax-logical-assignment-operators/", + "packageDependencies": [ + ["@babel/plugin-syntax-logical-assignment-operators", "npm:7.10.4"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.10.4", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-logical-assignment-operators-virtual-9aca02ef00/0/cache/@babel-plugin-syntax-logical-assignment-operators-npm-7.10.4-72ae00fdf6-aff3357703.zip/node_modules/@babel/plugin-syntax-logical-assignment-operators/", + "packageDependencies": [ + ["@babel/plugin-syntax-logical-assignment-operators", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.10.4"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-nullish-coalescing-operator", [ + ["npm:7.8.3", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-nullish-coalescing-operator-npm-7.8.3-8a723173b5-87aca49189.zip/node_modules/@babel/plugin-syntax-nullish-coalescing-operator/", + "packageDependencies": [ + ["@babel/plugin-syntax-nullish-coalescing-operator", "npm:7.8.3"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-nullish-coalescing-operator-virtual-4436f3187b/0/cache/@babel-plugin-syntax-nullish-coalescing-operator-npm-7.8.3-8a723173b5-87aca49189.zip/node_modules/@babel/plugin-syntax-nullish-coalescing-operator/", + "packageDependencies": [ + ["@babel/plugin-syntax-nullish-coalescing-operator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-numeric-separator", [ + ["npm:7.10.4", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-numeric-separator-npm-7.10.4-81444be605-01ec5547bd.zip/node_modules/@babel/plugin-syntax-numeric-separator/", + "packageDependencies": [ + ["@babel/plugin-syntax-numeric-separator", "npm:7.10.4"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.10.4", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-numeric-separator-virtual-8324f3c09b/0/cache/@babel-plugin-syntax-numeric-separator-npm-7.10.4-81444be605-01ec5547bd.zip/node_modules/@babel/plugin-syntax-numeric-separator/", + "packageDependencies": [ + ["@babel/plugin-syntax-numeric-separator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.10.4"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-object-rest-spread", [ + ["npm:7.8.3", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-object-rest-spread-npm-7.8.3-60bd05b6ae-fddcf581a5.zip/node_modules/@babel/plugin-syntax-object-rest-spread/", + "packageDependencies": [ + ["@babel/plugin-syntax-object-rest-spread", "npm:7.8.3"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-object-rest-spread-virtual-51cb92f0a9/0/cache/@babel-plugin-syntax-object-rest-spread-npm-7.8.3-60bd05b6ae-fddcf581a5.zip/node_modules/@babel/plugin-syntax-object-rest-spread/", + "packageDependencies": [ + ["@babel/plugin-syntax-object-rest-spread", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-optional-catch-binding", [ + ["npm:7.8.3", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-optional-catch-binding-npm-7.8.3-ce337427d8-910d90e72b.zip/node_modules/@babel/plugin-syntax-optional-catch-binding/", + "packageDependencies": [ + ["@babel/plugin-syntax-optional-catch-binding", "npm:7.8.3"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-optional-catch-binding-virtual-ba665a96e9/0/cache/@babel-plugin-syntax-optional-catch-binding-npm-7.8.3-ce337427d8-910d90e72b.zip/node_modules/@babel/plugin-syntax-optional-catch-binding/", + "packageDependencies": [ + ["@babel/plugin-syntax-optional-catch-binding", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-optional-chaining", [ + ["npm:7.8.3", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-optional-chaining-npm-7.8.3-f3f3c79579-eef94d53a1.zip/node_modules/@babel/plugin-syntax-optional-chaining/", + "packageDependencies": [ + ["@babel/plugin-syntax-optional-chaining", "npm:7.8.3"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-optional-chaining-virtual-bc1fba4439/0/cache/@babel-plugin-syntax-optional-chaining-npm-7.8.3-f3f3c79579-eef94d53a1.zip/node_modules/@babel/plugin-syntax-optional-chaining/", + "packageDependencies": [ + ["@babel/plugin-syntax-optional-chaining", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-private-property-in-object", [ + ["npm:7.14.5", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-private-property-in-object-npm-7.14.5-ee837fdbb2-b317174783.zip/node_modules/@babel/plugin-syntax-private-property-in-object/", + "packageDependencies": [ + ["@babel/plugin-syntax-private-property-in-object", "npm:7.14.5"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.14.5", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-private-property-in-object-virtual-582c436447/0/cache/@babel-plugin-syntax-private-property-in-object-npm-7.14.5-ee837fdbb2-b317174783.zip/node_modules/@babel/plugin-syntax-private-property-in-object/", + "packageDependencies": [ + ["@babel/plugin-syntax-private-property-in-object", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.14.5"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-syntax-top-level-await", [ + ["npm:7.14.5", { + "packageLocation": "./.yarn/cache/@babel-plugin-syntax-top-level-await-npm-7.14.5-60a0a2e83b-bbd1a56b09.zip/node_modules/@babel/plugin-syntax-top-level-await/", + "packageDependencies": [ + ["@babel/plugin-syntax-top-level-await", "npm:7.14.5"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.14.5", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-syntax-top-level-await-virtual-bbcea7f85f/0/cache/@babel-plugin-syntax-top-level-await-npm-7.14.5-60a0a2e83b-bbd1a56b09.zip/node_modules/@babel/plugin-syntax-top-level-await/", + "packageDependencies": [ + ["@babel/plugin-syntax-top-level-await", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.14.5"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-arrow-functions", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-arrow-functions-npm-7.16.0-41f0acc5fd-ff64730042.zip/node_modules/@babel/plugin-transform-arrow-functions/", + "packageDependencies": [ + ["@babel/plugin-transform-arrow-functions", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-arrow-functions-virtual-142ba08367/0/cache/@babel-plugin-transform-arrow-functions-npm-7.16.0-41f0acc5fd-ff64730042.zip/node_modules/@babel/plugin-transform-arrow-functions/", + "packageDependencies": [ + ["@babel/plugin-transform-arrow-functions", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-async-to-generator", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-async-to-generator-npm-7.16.0-86f0f376d0-2ebf505f43.zip/node_modules/@babel/plugin-transform-async-to-generator/", + "packageDependencies": [ + ["@babel/plugin-transform-async-to-generator", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-async-to-generator-virtual-ad2a2995b3/0/cache/@babel-plugin-transform-async-to-generator-npm-7.16.0-86f0f376d0-2ebf505f43.zip/node_modules/@babel/plugin-transform-async-to-generator/", + "packageDependencies": [ + ["@babel/plugin-transform-async-to-generator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-module-imports", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/helper-remap-async-to-generator", "npm:7.16.4"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-block-scoped-functions", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-block-scoped-functions-npm-7.16.0-b0a5ff16fd-f7efc5d8ce.zip/node_modules/@babel/plugin-transform-block-scoped-functions/", + "packageDependencies": [ + ["@babel/plugin-transform-block-scoped-functions", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-block-scoped-functions-virtual-534b67698c/0/cache/@babel-plugin-transform-block-scoped-functions-npm-7.16.0-b0a5ff16fd-f7efc5d8ce.zip/node_modules/@babel/plugin-transform-block-scoped-functions/", + "packageDependencies": [ + ["@babel/plugin-transform-block-scoped-functions", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-block-scoping", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-block-scoping-npm-7.16.0-79b754526e-e5bcb9eeed.zip/node_modules/@babel/plugin-transform-block-scoping/", + "packageDependencies": [ + ["@babel/plugin-transform-block-scoping", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-block-scoping-virtual-134ca6b36a/0/cache/@babel-plugin-transform-block-scoping-npm-7.16.0-79b754526e-e5bcb9eeed.zip/node_modules/@babel/plugin-transform-block-scoping/", + "packageDependencies": [ + ["@babel/plugin-transform-block-scoping", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-classes", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-classes-npm-7.16.0-eb06677280-7db4729604.zip/node_modules/@babel/plugin-transform-classes/", + "packageDependencies": [ + ["@babel/plugin-transform-classes", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-classes-virtual-f68d02bacc/0/cache/@babel-plugin-transform-classes-npm-7.16.0-eb06677280-7db4729604.zip/node_modules/@babel/plugin-transform-classes/", + "packageDependencies": [ + ["@babel/plugin-transform-classes", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-annotate-as-pure", "npm:7.16.0"], + ["@babel/helper-function-name", "npm:7.16.7"], + ["@babel/helper-optimise-call-expression", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/helper-replace-supers", "npm:7.16.0"], + ["@babel/helper-split-export-declaration", "npm:7.16.7"], + ["@types/babel__core", null], + ["globals", "npm:11.12.0"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-computed-properties", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-computed-properties-npm-7.16.0-e7e3f1f458-0f86de419c.zip/node_modules/@babel/plugin-transform-computed-properties/", + "packageDependencies": [ + ["@babel/plugin-transform-computed-properties", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-computed-properties-virtual-bf493a3670/0/cache/@babel-plugin-transform-computed-properties-npm-7.16.0-e7e3f1f458-0f86de419c.zip/node_modules/@babel/plugin-transform-computed-properties/", + "packageDependencies": [ + ["@babel/plugin-transform-computed-properties", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-destructuring", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-destructuring-npm-7.16.0-e06e24c3ed-0a499c9abd.zip/node_modules/@babel/plugin-transform-destructuring/", + "packageDependencies": [ + ["@babel/plugin-transform-destructuring", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-destructuring-virtual-460b5f6428/0/cache/@babel-plugin-transform-destructuring-npm-7.16.0-e06e24c3ed-0a499c9abd.zip/node_modules/@babel/plugin-transform-destructuring/", + "packageDependencies": [ + ["@babel/plugin-transform-destructuring", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-dotall-regex", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-dotall-regex-npm-7.16.0-5369887cb9-c1f381f0d4.zip/node_modules/@babel/plugin-transform-dotall-regex/", + "packageDependencies": [ + ["@babel/plugin-transform-dotall-regex", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-dotall-regex-virtual-fe3aea9477/0/cache/@babel-plugin-transform-dotall-regex-npm-7.16.0-5369887cb9-c1f381f0d4.zip/node_modules/@babel/plugin-transform-dotall-regex/", + "packageDependencies": [ + ["@babel/plugin-transform-dotall-regex", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-create-regexp-features-plugin", "virtual:9ce5a9282e152f3fba76420378e4252441c87f195af85577209bdfda27fc39e017652eda9bb3af430ee582fe6a109cdb6f73d790b962837af8f39a627b435bc0#npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-duplicate-keys", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-duplicate-keys-npm-7.16.0-de841439f8-66f09487fd.zip/node_modules/@babel/plugin-transform-duplicate-keys/", + "packageDependencies": [ + ["@babel/plugin-transform-duplicate-keys", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-duplicate-keys-virtual-5d278c1277/0/cache/@babel-plugin-transform-duplicate-keys-npm-7.16.0-de841439f8-66f09487fd.zip/node_modules/@babel/plugin-transform-duplicate-keys/", + "packageDependencies": [ + ["@babel/plugin-transform-duplicate-keys", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-exponentiation-operator", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-exponentiation-operator-npm-7.16.0-2f4326910a-22e1d4804a.zip/node_modules/@babel/plugin-transform-exponentiation-operator/", + "packageDependencies": [ + ["@babel/plugin-transform-exponentiation-operator", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-exponentiation-operator-virtual-52f053af37/0/cache/@babel-plugin-transform-exponentiation-operator-npm-7.16.0-2f4326910a-22e1d4804a.zip/node_modules/@babel/plugin-transform-exponentiation-operator/", + "packageDependencies": [ + ["@babel/plugin-transform-exponentiation-operator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-builder-binary-assignment-operator-visitor", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-for-of", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-for-of-npm-7.16.0-fa8c8350b0-504d967b30.zip/node_modules/@babel/plugin-transform-for-of/", + "packageDependencies": [ + ["@babel/plugin-transform-for-of", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-for-of-virtual-af486403bb/0/cache/@babel-plugin-transform-for-of-npm-7.16.0-fa8c8350b0-504d967b30.zip/node_modules/@babel/plugin-transform-for-of/", + "packageDependencies": [ + ["@babel/plugin-transform-for-of", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-function-name", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-function-name-npm-7.16.0-51d8c4f9e8-289f4fce26.zip/node_modules/@babel/plugin-transform-function-name/", + "packageDependencies": [ + ["@babel/plugin-transform-function-name", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-function-name-virtual-8bd0de06b7/0/cache/@babel-plugin-transform-function-name-npm-7.16.0-51d8c4f9e8-289f4fce26.zip/node_modules/@babel/plugin-transform-function-name/", + "packageDependencies": [ + ["@babel/plugin-transform-function-name", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-function-name", "npm:7.16.7"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-literals", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-literals-npm-7.16.0-14c027a2e4-7291771c76.zip/node_modules/@babel/plugin-transform-literals/", + "packageDependencies": [ + ["@babel/plugin-transform-literals", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-literals-virtual-cfc3f7ebcc/0/cache/@babel-plugin-transform-literals-npm-7.16.0-14c027a2e4-7291771c76.zip/node_modules/@babel/plugin-transform-literals/", + "packageDependencies": [ + ["@babel/plugin-transform-literals", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-member-expression-literals", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-member-expression-literals-npm-7.16.0-e6f2ca03f6-d5ed6cf840.zip/node_modules/@babel/plugin-transform-member-expression-literals/", + "packageDependencies": [ + ["@babel/plugin-transform-member-expression-literals", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-member-expression-literals-virtual-50f904d886/0/cache/@babel-plugin-transform-member-expression-literals-npm-7.16.0-e6f2ca03f6-d5ed6cf840.zip/node_modules/@babel/plugin-transform-member-expression-literals/", + "packageDependencies": [ + ["@babel/plugin-transform-member-expression-literals", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-modules-amd", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-modules-amd-npm-7.16.0-d080840cd8-c37ccb8cd7.zip/node_modules/@babel/plugin-transform-modules-amd/", + "packageDependencies": [ + ["@babel/plugin-transform-modules-amd", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-modules-amd-virtual-ae294c403a/0/cache/@babel-plugin-transform-modules-amd-npm-7.16.0-d080840cd8-c37ccb8cd7.zip/node_modules/@babel/plugin-transform-modules-amd/", + "packageDependencies": [ + ["@babel/plugin-transform-modules-amd", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-module-transforms", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null], + ["babel-plugin-dynamic-import-node", "npm:2.3.3"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-modules-commonjs", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-modules-commonjs-npm-7.16.0-2af1b97bf1-a7e43670f5.zip/node_modules/@babel/plugin-transform-modules-commonjs/", + "packageDependencies": [ + ["@babel/plugin-transform-modules-commonjs", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-modules-commonjs-virtual-63c6679fbb/0/cache/@babel-plugin-transform-modules-commonjs-npm-7.16.0-2af1b97bf1-a7e43670f5.zip/node_modules/@babel/plugin-transform-modules-commonjs/", + "packageDependencies": [ + ["@babel/plugin-transform-modules-commonjs", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-module-transforms", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/helper-simple-access", "npm:7.16.0"], + ["@types/babel__core", null], + ["babel-plugin-dynamic-import-node", "npm:2.3.3"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-modules-systemjs", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-modules-systemjs-npm-7.16.0-1c528e3b5f-4aa9bd45a4.zip/node_modules/@babel/plugin-transform-modules-systemjs/", + "packageDependencies": [ + ["@babel/plugin-transform-modules-systemjs", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-modules-systemjs-virtual-8923101059/0/cache/@babel-plugin-transform-modules-systemjs-npm-7.16.0-1c528e3b5f-4aa9bd45a4.zip/node_modules/@babel/plugin-transform-modules-systemjs/", + "packageDependencies": [ + ["@babel/plugin-transform-modules-systemjs", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-hoist-variables", "npm:7.16.7"], + ["@babel/helper-module-transforms", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/helper-validator-identifier", "npm:7.16.7"], + ["@types/babel__core", null], + ["babel-plugin-dynamic-import-node", "npm:2.3.3"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-modules-umd", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-modules-umd-npm-7.16.0-d01b0d9cde-b07d41eae3.zip/node_modules/@babel/plugin-transform-modules-umd/", + "packageDependencies": [ + ["@babel/plugin-transform-modules-umd", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-modules-umd-virtual-219a6714c4/0/cache/@babel-plugin-transform-modules-umd-npm-7.16.0-d01b0d9cde-b07d41eae3.zip/node_modules/@babel/plugin-transform-modules-umd/", + "packageDependencies": [ + ["@babel/plugin-transform-modules-umd", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-module-transforms", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-named-capturing-groups-regex", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-named-capturing-groups-regex-npm-7.16.0-6c90f144b8-758a87aca6.zip/node_modules/@babel/plugin-transform-named-capturing-groups-regex/", + "packageDependencies": [ + ["@babel/plugin-transform-named-capturing-groups-regex", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-named-capturing-groups-regex-virtual-c13e3b8bb9/0/cache/@babel-plugin-transform-named-capturing-groups-regex-npm-7.16.0-6c90f144b8-758a87aca6.zip/node_modules/@babel/plugin-transform-named-capturing-groups-regex/", + "packageDependencies": [ + ["@babel/plugin-transform-named-capturing-groups-regex", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-create-regexp-features-plugin", "virtual:9ce5a9282e152f3fba76420378e4252441c87f195af85577209bdfda27fc39e017652eda9bb3af430ee582fe6a109cdb6f73d790b962837af8f39a627b435bc0#npm:7.16.0"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-new-target", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-new-target-npm-7.16.0-5d7ffa3fc5-c741ba3e84.zip/node_modules/@babel/plugin-transform-new-target/", + "packageDependencies": [ + ["@babel/plugin-transform-new-target", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-new-target-virtual-dd7c3de9cc/0/cache/@babel-plugin-transform-new-target-npm-7.16.0-5d7ffa3fc5-c741ba3e84.zip/node_modules/@babel/plugin-transform-new-target/", + "packageDependencies": [ + ["@babel/plugin-transform-new-target", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-object-super", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-object-super-npm-7.16.0-ac4f46c62c-b6ed0a8f5a.zip/node_modules/@babel/plugin-transform-object-super/", + "packageDependencies": [ + ["@babel/plugin-transform-object-super", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-object-super-virtual-f86f554c8b/0/cache/@babel-plugin-transform-object-super-npm-7.16.0-ac4f46c62c-b6ed0a8f5a.zip/node_modules/@babel/plugin-transform-object-super/", + "packageDependencies": [ + ["@babel/plugin-transform-object-super", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/helper-replace-supers", "npm:7.16.0"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-parameters", [ + ["npm:7.16.3", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-parameters-npm-7.16.3-37e31de570-7c0154fa66.zip/node_modules/@babel/plugin-transform-parameters/", + "packageDependencies": [ + ["@babel/plugin-transform-parameters", "npm:7.16.3"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.3", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-parameters-virtual-f1e991033c/0/cache/@babel-plugin-transform-parameters-npm-7.16.3-37e31de570-7c0154fa66.zip/node_modules/@babel/plugin-transform-parameters/", + "packageDependencies": [ + ["@babel/plugin-transform-parameters", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.3"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-property-literals", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-property-literals-npm-7.16.0-ac6d37bdd0-e9eb9355db.zip/node_modules/@babel/plugin-transform-property-literals/", + "packageDependencies": [ + ["@babel/plugin-transform-property-literals", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-property-literals-virtual-4c9f8264b6/0/cache/@babel-plugin-transform-property-literals-npm-7.16.0-ac6d37bdd0-e9eb9355db.zip/node_modules/@babel/plugin-transform-property-literals/", + "packageDependencies": [ + ["@babel/plugin-transform-property-literals", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-regenerator", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-regenerator-npm-7.16.0-498b430132-32b1b43f8d.zip/node_modules/@babel/plugin-transform-regenerator/", + "packageDependencies": [ + ["@babel/plugin-transform-regenerator", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-regenerator-virtual-c6ec237200/0/cache/@babel-plugin-transform-regenerator-npm-7.16.0-498b430132-32b1b43f8d.zip/node_modules/@babel/plugin-transform-regenerator/", + "packageDependencies": [ + ["@babel/plugin-transform-regenerator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@types/babel__core", null], + ["regenerator-transform", "npm:0.14.5"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-reserved-words", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-reserved-words-npm-7.16.0-1acaa9020b-7a8288cfe2.zip/node_modules/@babel/plugin-transform-reserved-words/", + "packageDependencies": [ + ["@babel/plugin-transform-reserved-words", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-reserved-words-virtual-0d7211bebb/0/cache/@babel-plugin-transform-reserved-words-npm-7.16.0-1acaa9020b-7a8288cfe2.zip/node_modules/@babel/plugin-transform-reserved-words/", + "packageDependencies": [ + ["@babel/plugin-transform-reserved-words", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-shorthand-properties", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-shorthand-properties-npm-7.16.0-b33ad8f611-7ae0f218aa.zip/node_modules/@babel/plugin-transform-shorthand-properties/", + "packageDependencies": [ + ["@babel/plugin-transform-shorthand-properties", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-shorthand-properties-virtual-2aaf958012/0/cache/@babel-plugin-transform-shorthand-properties-npm-7.16.0-b33ad8f611-7ae0f218aa.zip/node_modules/@babel/plugin-transform-shorthand-properties/", + "packageDependencies": [ + ["@babel/plugin-transform-shorthand-properties", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-spread", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-spread-npm-7.16.0-5cf66a86c2-c295ef5e32.zip/node_modules/@babel/plugin-transform-spread/", + "packageDependencies": [ + ["@babel/plugin-transform-spread", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-spread-virtual-4deb7b432e/0/cache/@babel-plugin-transform-spread-npm-7.16.0-5cf66a86c2-c295ef5e32.zip/node_modules/@babel/plugin-transform-spread/", + "packageDependencies": [ + ["@babel/plugin-transform-spread", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/helper-skip-transparent-expression-wrappers", "npm:7.16.0"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-sticky-regex", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-sticky-regex-npm-7.16.0-fe13551c5b-80c7ccb797.zip/node_modules/@babel/plugin-transform-sticky-regex/", + "packageDependencies": [ + ["@babel/plugin-transform-sticky-regex", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-sticky-regex-virtual-d97da267ce/0/cache/@babel-plugin-transform-sticky-regex-npm-7.16.0-fe13551c5b-80c7ccb797.zip/node_modules/@babel/plugin-transform-sticky-regex/", + "packageDependencies": [ + ["@babel/plugin-transform-sticky-regex", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-template-literals", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-template-literals-npm-7.16.0-28db7976f7-230638ee56.zip/node_modules/@babel/plugin-transform-template-literals/", + "packageDependencies": [ + ["@babel/plugin-transform-template-literals", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-template-literals-virtual-522f6fd182/0/cache/@babel-plugin-transform-template-literals-npm-7.16.0-28db7976f7-230638ee56.zip/node_modules/@babel/plugin-transform-template-literals/", + "packageDependencies": [ + ["@babel/plugin-transform-template-literals", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-typeof-symbol", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-typeof-symbol-npm-7.16.0-77d5f48897-60e91d57b3.zip/node_modules/@babel/plugin-transform-typeof-symbol/", + "packageDependencies": [ + ["@babel/plugin-transform-typeof-symbol", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-typeof-symbol-virtual-9c21594871/0/cache/@babel-plugin-transform-typeof-symbol-npm-7.16.0-77d5f48897-60e91d57b3.zip/node_modules/@babel/plugin-transform-typeof-symbol/", + "packageDependencies": [ + ["@babel/plugin-transform-typeof-symbol", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-unicode-escapes", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-unicode-escapes-npm-7.16.0-dab90cd689-63ac80d6b7.zip/node_modules/@babel/plugin-transform-unicode-escapes/", + "packageDependencies": [ + ["@babel/plugin-transform-unicode-escapes", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-unicode-escapes-virtual-14819847a6/0/cache/@babel-plugin-transform-unicode-escapes-npm-7.16.0-dab90cd689-63ac80d6b7.zip/node_modules/@babel/plugin-transform-unicode-escapes/", + "packageDependencies": [ + ["@babel/plugin-transform-unicode-escapes", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/plugin-transform-unicode-regex", [ + ["npm:7.16.0", { + "packageLocation": "./.yarn/cache/@babel-plugin-transform-unicode-regex-npm-7.16.0-5f79e96758-61e498425f.zip/node_modules/@babel/plugin-transform-unicode-regex/", + "packageDependencies": [ + ["@babel/plugin-transform-unicode-regex", "npm:7.16.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0", { + "packageLocation": "./.yarn/__virtual__/@babel-plugin-transform-unicode-regex-virtual-ad681eb9f2/0/cache/@babel-plugin-transform-unicode-regex-npm-7.16.0-5f79e96758-61e498425f.zip/node_modules/@babel/plugin-transform-unicode-regex/", + "packageDependencies": [ + ["@babel/plugin-transform-unicode-regex", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-create-regexp-features-plugin", "virtual:9ce5a9282e152f3fba76420378e4252441c87f195af85577209bdfda27fc39e017652eda9bb3af430ee582fe6a109cdb6f73d790b962837af8f39a627b435bc0#npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/preset-env", [ + ["npm:7.16.4", { + "packageLocation": "./.yarn/cache/@babel-preset-env-npm-7.16.4-115941abdf-72a5d7e460.zip/node_modules/@babel/preset-env/", + "packageDependencies": [ + ["@babel/preset-env", "npm:7.16.4"] + ], + "linkType": "SOFT", + }], + ["virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:7.16.4", { + "packageLocation": "./.yarn/__virtual__/@babel-preset-env-virtual-aa7011b53d/0/cache/@babel-preset-env-npm-7.16.4-115941abdf-72a5d7e460.zip/node_modules/@babel/preset-env/", + "packageDependencies": [ + ["@babel/preset-env", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:7.16.4"], + ["@babel/compat-data", "npm:7.16.4"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-compilation-targets", "virtual:5612f0ce311a7844500ba5948d5d47b8376a902bfa55b1e3797dd916bf18f7512ba75518521ba3bc2f39f6565fb127bcb8fee5b440624dbebaf9ac5f3566ebd0#npm:7.16.3"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/helper-validator-option", "npm:7.14.5"], + ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.2"], + ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-async-generator-functions", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.4"], + ["@babel/plugin-proposal-class-properties", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-class-static-block", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-dynamic-import", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-export-namespace-from", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-json-strings", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-logical-assignment-operators", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-nullish-coalescing-operator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-numeric-separator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-object-rest-spread", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-optional-catch-binding", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-optional-chaining", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-private-methods", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-private-property-in-object", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-proposal-unicode-property-regex", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-syntax-async-generators", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.4"], + ["@babel/plugin-syntax-class-properties", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.12.13"], + ["@babel/plugin-syntax-class-static-block", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.14.5"], + ["@babel/plugin-syntax-dynamic-import", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/plugin-syntax-export-namespace-from", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/plugin-syntax-json-strings", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/plugin-syntax-logical-assignment-operators", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.10.4"], + ["@babel/plugin-syntax-nullish-coalescing-operator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/plugin-syntax-numeric-separator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.10.4"], + ["@babel/plugin-syntax-object-rest-spread", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/plugin-syntax-optional-catch-binding", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/plugin-syntax-optional-chaining", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.8.3"], + ["@babel/plugin-syntax-private-property-in-object", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.14.5"], + ["@babel/plugin-syntax-top-level-await", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.14.5"], + ["@babel/plugin-transform-arrow-functions", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-async-to-generator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-block-scoped-functions", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-block-scoping", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-classes", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-computed-properties", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-destructuring", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-dotall-regex", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-duplicate-keys", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-exponentiation-operator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-for-of", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-function-name", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-literals", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-member-expression-literals", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-modules-amd", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-modules-commonjs", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-modules-systemjs", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-modules-umd", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-named-capturing-groups-regex", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-new-target", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-object-super", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-parameters", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.3"], + ["@babel/plugin-transform-property-literals", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-regenerator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-reserved-words", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-shorthand-properties", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-spread", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-sticky-regex", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-template-literals", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-typeof-symbol", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-unicode-escapes", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-unicode-regex", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/preset-modules", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:0.1.5"], + ["@babel/types", "npm:7.17.0"], + ["@types/babel__core", null], + ["babel-plugin-polyfill-corejs2", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:0.3.0"], + ["babel-plugin-polyfill-corejs3", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:0.4.0"], + ["babel-plugin-polyfill-regenerator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:0.3.0"], + ["core-js-compat", "npm:3.19.1"], + ["semver", "npm:6.3.0"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/preset-modules", [ + ["npm:0.1.5", { + "packageLocation": "./.yarn/cache/@babel-preset-modules-npm-0.1.5-15ffcd64c2-8430e0e9e9.zip/node_modules/@babel/preset-modules/", + "packageDependencies": [ + ["@babel/preset-modules", "npm:0.1.5"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:0.1.5", { + "packageLocation": "./.yarn/__virtual__/@babel-preset-modules-virtual-377b8cad9f/0/cache/@babel-preset-modules-npm-0.1.5-15ffcd64c2-8430e0e9e9.zip/node_modules/@babel/preset-modules/", + "packageDependencies": [ + ["@babel/preset-modules", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:0.1.5"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-plugin-utils", "npm:7.14.5"], + ["@babel/plugin-proposal-unicode-property-regex", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/plugin-transform-dotall-regex", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:7.16.0"], + ["@babel/types", "npm:7.17.0"], + ["@types/babel__core", null], + ["esutils", "npm:2.0.3"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/runtime", [ + ["npm:7.17.9", { + "packageLocation": "./.yarn/cache/@babel-runtime-npm-7.17.9-c52a5e9d27-4d56bdb828.zip/node_modules/@babel/runtime/", + "packageDependencies": [ + ["@babel/runtime", "npm:7.17.9"], + ["regenerator-runtime", "npm:0.13.9"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/template", [ + ["npm:7.16.7", { + "packageLocation": "./.yarn/cache/@babel-template-npm-7.16.7-a18e444be8-10cd112e89.zip/node_modules/@babel/template/", + "packageDependencies": [ + ["@babel/template", "npm:7.16.7"], + ["@babel/code-frame", "npm:7.16.7"], + ["@babel/parser", "npm:7.17.3"], + ["@babel/types", "npm:7.17.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/traverse", [ + ["npm:7.17.3", { + "packageLocation": "./.yarn/cache/@babel-traverse-npm-7.17.3-c2bff3e671-780d7ecf71.zip/node_modules/@babel/traverse/", + "packageDependencies": [ + ["@babel/traverse", "npm:7.17.3"], + ["@babel/code-frame", "npm:7.16.7"], + ["@babel/generator", "npm:7.17.3"], + ["@babel/helper-environment-visitor", "npm:7.16.7"], + ["@babel/helper-function-name", "npm:7.16.7"], + ["@babel/helper-hoist-variables", "npm:7.16.7"], + ["@babel/helper-split-export-declaration", "npm:7.16.7"], + ["@babel/parser", "npm:7.17.3"], + ["@babel/types", "npm:7.17.0"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["globals", "npm:11.12.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/types", [ + ["npm:7.17.0", { + "packageLocation": "./.yarn/cache/@babel-types-npm-7.17.0-3c936b54e4-12e5a28798.zip/node_modules/@babel/types/", + "packageDependencies": [ + ["@babel/types", "npm:7.17.0"], + ["@babel/helper-validator-identifier", "npm:7.16.7"], + ["to-fast-properties", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@cspotcode/source-map-consumer", [ + ["npm:0.8.0", { + "packageLocation": "./.yarn/cache/@cspotcode-source-map-consumer-npm-0.8.0-1f37e9e72b-c0c16ca3d2.zip/node_modules/@cspotcode/source-map-consumer/", + "packageDependencies": [ + ["@cspotcode/source-map-consumer", "npm:0.8.0"] + ], + "linkType": "HARD", + }] + ]], + ["@cspotcode/source-map-support", [ + ["npm:0.7.0", { + "packageLocation": "./.yarn/cache/@cspotcode-source-map-support-npm-0.7.0-456c3ea2ce-9faddda775.zip/node_modules/@cspotcode/source-map-support/", + "packageDependencies": [ + ["@cspotcode/source-map-support", "npm:0.7.0"], + ["@cspotcode/source-map-consumer", "npm:0.8.0"] + ], + "linkType": "HARD", + }] + ]], + ["@dabh/diagnostics", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/@dabh-diagnostics-npm-2.0.2-83eb005a83-4d95cc3124.zip/node_modules/@dabh/diagnostics/", + "packageDependencies": [ + ["@dabh/diagnostics", "npm:2.0.2"], + ["colorspace", "npm:1.1.4"], + ["enabled", "npm:2.0.0"], + ["kuler", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@dashevo/abci", [ + ["npm:0.23.0-dev.1", { + "packageLocation": "./.yarn/cache/@dashevo-abci-npm-0.23.0-dev.1-4b76d57180-6a7cf16ed9.zip/node_modules/@dashevo/abci/", + "packageDependencies": [ + ["@dashevo/abci", "npm:0.23.0-dev.1"], + ["@dashevo/protobufjs", "npm:6.10.5"], + ["bl", "npm:1.2.3"], + ["protocol-buffers-encodings", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["@dashevo/bench-suite", [ + ["workspace:packages/bench-suite", { + "packageLocation": "./packages/bench-suite/", + "packageDependencies": [ + ["@dashevo/bench-suite", "workspace:packages/bench-suite"], + ["@dashevo/dashcore-lib", "npm:0.19.39"], + ["@dashevo/dpns-contract", "workspace:packages/dpns-contract"], + ["@dashevo/dpp", "workspace:packages/js-dpp"], + ["@dashevo/wallet-lib", "workspace:packages/wallet-lib"], + ["babel-eslint", "virtual:27dae49067a60fa65fec6e1c3adad1497d0dda3f71eda711624109131ff3b7d1061a20f55e89b5a0a219da1f7a0a1a0a76bc414d36870315bd60acf5bdcb7f55#npm:10.1.0"], + ["console-table-printer", "npm:2.11.0"], + ["dash", "workspace:packages/js-dash-sdk"], + ["dotenv-safe", "npm:8.2.0"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["lodash.clone", "npm:4.5.0"], + ["lodash.matches", "npm:4.6.0"], + ["mathjs", "npm:10.4.3"], + ["mocha", "npm:9.1.3"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/dapi", [ + ["workspace:packages/dapi", { + "packageLocation": "./packages/dapi/", + "packageDependencies": [ + ["@dashevo/dapi", "workspace:packages/dapi"], + ["@dashevo/dapi-client", "workspace:packages/js-dapi-client"], + ["@dashevo/dapi-grpc", "workspace:packages/dapi-grpc"], + ["@dashevo/dashcore-lib", "npm:0.19.39"], + ["@dashevo/dashd-rpc", "npm:2.3.2"], + ["@dashevo/dp-services-ctl", "https://github.com/dashevo/js-dp-services-ctl.git#commit=3976076b0018c5b4632ceda4c752fc597f27a640"], + ["@dashevo/dpp", "workspace:packages/js-dpp"], + ["@dashevo/grpc-common", "workspace:packages/js-grpc-common"], + ["@grpc/grpc-js", "npm:1.4.4"], + ["ajv", "npm:8.8.1"], + ["bs58", "npm:4.0.1"], + ["cbor", "npm:8.1.0"], + ["chai", "npm:4.3.4"], + ["chai-as-promised", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:7.1.1"], + ["dirty-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.0.1"], + ["dotenv", "npm:8.6.0"], + ["dotenv-expand", "npm:5.1.0"], + ["dotenv-safe", "npm:8.2.0"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["jayson", "npm:3.6.5"], + ["lodash", "npm:4.17.21"], + ["lru-cache", "npm:5.1.1"], + ["mocha", "npm:9.1.3"], + ["mocha-sinon", "virtual:595d7482cc8ddf98ee6aef33fc48b46393554ab5f17f851ef62e6e39315e53666c3e66226b978689aa0bc7f1e83a03081511a21db1c381362fe67614887077f9#npm:2.1.2"], + ["nyc", "npm:15.1.0"], + ["request", "npm:2.88.2"], + ["request-promise-native", "virtual:6c6296bde00603e266f7d80babe1e01aa0c19f626934f58fe08f890a291bb1a38fcee25bf30c24857d5cfba290f01209decc48384318fd6815c5a514cb48be25#npm:1.0.9"], + ["semver", "npm:7.3.5"], + ["sinon", "npm:11.1.2"], + ["sinon-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0"], + ["swagger-jsdoc", "npm:3.7.0"], + ["ws", "virtual:2fd01a647a5c8b340dd0adae82833428da22baa88327826cc44910efed2d9ad403a63cf3d639ef91449c9c952ea1afcdd4379371f5022db3053206940836b879#npm:7.5.5"], + ["zeromq", "npm:5.2.8"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/dapi-client", [ + ["workspace:packages/js-dapi-client", { + "packageLocation": "./packages/js-dapi-client/", + "packageDependencies": [ + ["@dashevo/dapi-client", "workspace:packages/js-dapi-client"], + ["@babel/core", "npm:7.16.0"], + ["@dashevo/dapi-grpc", "workspace:packages/dapi-grpc"], + ["@dashevo/dash-spv", "workspace:packages/dash-spv"], + ["@dashevo/dashcore-lib", "npm:0.19.39"], + ["@dashevo/dpp", "workspace:packages/js-dpp"], + ["@dashevo/grpc-common", "workspace:packages/js-grpc-common"], + ["@grpc/grpc-js", "npm:1.4.4"], + ["assert-browserify", "npm:2.0.0"], + ["axios", "npm:0.21.4"], + ["babel-loader", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:8.2.3"], + ["bs58", "npm:4.0.1"], + ["buffer", "npm:6.0.3"], + ["cbor", "npm:8.1.0"], + ["chai", "npm:4.3.4"], + ["chai-as-promised", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:7.1.1"], + ["comment-parser", "npm:0.7.6"], + ["core-js", "npm:3.19.1"], + ["crypto-browserify", "npm:3.12.0"], + ["dirty-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.0.1"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["eslint-plugin-jsdoc", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:27.1.2"], + ["events", "npm:3.3.0"], + ["karma", "npm:6.3.9"], + ["karma-chai", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:0.1.0"], + ["karma-chrome-launcher", "npm:3.1.0"], + ["karma-firefox-launcher", "npm:2.1.2"], + ["karma-mocha", "npm:2.0.1"], + ["karma-mocha-reporter", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.2.5"], + ["karma-webpack", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:5.0.0"], + ["lodash.sample", "npm:4.2.1"], + ["mocha", "npm:9.1.3"], + ["node-inspect-extracted", "npm:1.0.8"], + ["nyc", "npm:15.1.0"], + ["path-browserify", "npm:1.0.1"], + ["process", "npm:0.11.10"], + ["sinon", "npm:11.1.2"], + ["sinon-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0"], + ["stream-browserify", "npm:3.0.0"], + ["string_decoder", "npm:1.3.0"], + ["url", "npm:0.11.0"], + ["util", "npm:0.12.4"], + ["webpack", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:5.64.1"], + ["webpack-cli", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:4.9.1"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/dapi-grpc", [ + ["workspace:packages/dapi-grpc", { + "packageLocation": "./packages/dapi-grpc/", + "packageDependencies": [ + ["@dashevo/dapi-grpc", "workspace:packages/dapi-grpc"], + ["@dashevo/grpc-common", "workspace:packages/js-grpc-common"], + ["@dashevo/protobufjs", "npm:6.10.5"], + ["@grpc/grpc-js", "npm:1.4.4"], + ["chai", "npm:4.3.4"], + ["chai-as-promised", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:7.1.1"], + ["dirty-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.0.1"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["google-protobuf", "npm:3.19.1"], + ["grpc-web", "npm:1.2.1"], + ["long", "npm:5.2.0"], + ["mocha", "npm:9.1.3"], + ["mocha-sinon", "virtual:595d7482cc8ddf98ee6aef33fc48b46393554ab5f17f851ef62e6e39315e53666c3e66226b978689aa0bc7f1e83a03081511a21db1c381362fe67614887077f9#npm:2.1.2"], + ["sinon", "npm:11.1.2"], + ["sinon-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/dark-gravity-wave", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/@dashevo-dark-gravity-wave-npm-1.1.1-aa785de435-4f2f0bddfa.zip/node_modules/@dashevo/dark-gravity-wave/", + "packageDependencies": [ + ["@dashevo/dark-gravity-wave", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["@dashevo/dash-spv", [ + ["workspace:packages/dash-spv", { + "packageLocation": "./packages/dash-spv/", + "packageDependencies": [ + ["@dashevo/dash-spv", "workspace:packages/dash-spv"], + ["@dashevo/dark-gravity-wave", "npm:1.1.1"], + ["@dashevo/dash-util", "npm:2.0.3"], + ["@dashevo/dashcore-lib", "npm:0.19.39"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["levelup", "npm:4.4.0"], + ["memdown", "npm:5.1.0"], + ["mocha", "npm:9.1.3"], + ["should", "npm:13.2.3"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/dash-util", [ + ["npm:2.0.3", { + "packageLocation": "./.yarn/cache/@dashevo-dash-util-npm-2.0.3-a597c1b8b3-ef93e629e9.zip/node_modules/@dashevo/dash-util/", + "packageDependencies": [ + ["@dashevo/dash-util", "npm:2.0.3"], + ["bn.js", "npm:4.12.0"], + ["buffer-reverse", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["@dashevo/dashcore-lib", [ + ["npm:0.19.39", { + "packageLocation": "./.yarn/cache/@dashevo-dashcore-lib-npm-0.19.39-b28e06588f-e441cf46a9.zip/node_modules/@dashevo/dashcore-lib/", + "packageDependencies": [ + ["@dashevo/dashcore-lib", "npm:0.19.39"], + ["@dashevo/x11-hash-js", "npm:1.0.2"], + ["@types/node", "npm:12.20.37"], + ["bloom-filter", "npm:0.2.0"], + ["bls-signatures", "npm:0.2.5"], + ["bn.js", "npm:4.12.0"], + ["bs58", "npm:4.0.1"], + ["elliptic", "npm:6.5.3"], + ["eslint-config-prettier", "virtual:b28e06588f8884ad00999d9ef1772f24cee4941229e01144c8d0ec740c177a99fec30f4d4abc3e4fed61ea09a8dd1831c34fb8ad78b40153cc83d898499b5720#npm:8.3.0"], + ["inherits", "npm:2.0.1"], + ["lodash", "npm:4.17.21"], + ["unorm", "npm:1.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["@dashevo/dashd-rpc", [ + ["npm:2.3.2", { + "packageLocation": "./.yarn/cache/@dashevo-dashd-rpc-npm-2.3.2-119e544222-56ff41d695.zip/node_modules/@dashevo/dashd-rpc/", + "packageDependencies": [ + ["@dashevo/dashd-rpc", "npm:2.3.2"], + ["async", "npm:3.2.2"], + ["bluebird", "npm:3.7.2"] + ], + "linkType": "HARD", + }] + ]], + ["@dashevo/dashpay-contract", [ + ["npm:0.22.1", { + "packageLocation": "./.yarn/cache/@dashevo-dashpay-contract-npm-0.22.1-2aded78119-cff4700aaf.zip/node_modules/@dashevo/dashpay-contract/", + "packageDependencies": [ + ["@dashevo/dashpay-contract", "npm:0.22.1"] + ], + "linkType": "HARD", + }], + ["workspace:packages/dashpay-contract", { + "packageLocation": "./packages/dashpay-contract/", + "packageDependencies": [ + ["@dashevo/dashpay-contract", "workspace:packages/dashpay-contract"], + ["@dashevo/dpp", "workspace:packages/js-dpp"], + ["chai", "npm:4.3.4"], + ["dirty-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.0.1"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["mocha", "npm:9.1.3"], + ["sinon", "npm:11.1.2"], + ["sinon-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/docker-compose", [ + ["npm:0.24.1", { + "packageLocation": "./.yarn/cache/@dashevo-docker-compose-npm-0.24.1-e4dc7f3c5e-7792e09b5d.zip/node_modules/@dashevo/docker-compose/", + "packageDependencies": [ + ["@dashevo/docker-compose", "npm:0.24.1"], + ["yaml", "npm:1.10.2"] + ], + "linkType": "HARD", + }] + ]], + ["@dashevo/dp-services-ctl", [ + ["https://github.com/dashevo/js-dp-services-ctl.git#commit=3976076b0018c5b4632ceda4c752fc597f27a640", { + "packageLocation": "./.yarn/cache/@dashevo-dp-services-ctl-https-a393167701-0325823966.zip/node_modules/@dashevo/dp-services-ctl/", + "packageDependencies": [ + ["@dashevo/dp-services-ctl", "https://github.com/dashevo/js-dp-services-ctl.git#commit=3976076b0018c5b4632ceda4c752fc597f27a640"], + ["@dashevo/dashd-rpc", "npm:2.3.2"], + ["dockerode", "npm:3.3.1"], + ["jayson", "npm:2.1.2"], + ["lodash", "npm:4.17.21"], + ["mongodb", "virtual:a39316770159f0a8e3f370c1c3a56eb433794f8d2beaf0837a3349497b1cf2188cea77a97e39f187d7b9f59864fa6d9d57b4c49a9871c8de6a876e77cba350c7#npm:3.7.3"] + ], + "linkType": "HARD", + }] + ]], + ["@dashevo/dpns-contract", [ + ["npm:0.22.1", { + "packageLocation": "./.yarn/cache/@dashevo-dpns-contract-npm-0.22.1-013d358290-d96f91f8ee.zip/node_modules/@dashevo/dpns-contract/", + "packageDependencies": [ + ["@dashevo/dpns-contract", "npm:0.22.1"] + ], + "linkType": "HARD", + }], + ["workspace:packages/dpns-contract", { + "packageLocation": "./packages/dpns-contract/", + "packageDependencies": [ + ["@dashevo/dpns-contract", "workspace:packages/dpns-contract"], + ["@dashevo/dpp", "workspace:packages/js-dpp"], + ["chai", "npm:4.3.4"], + ["dirty-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.0.1"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["mocha", "npm:9.1.3"], + ["sinon", "npm:11.1.2"], + ["sinon-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/dpp", [ + ["npm:0.22.1", { + "packageLocation": "./.yarn/cache/@dashevo-dpp-npm-0.22.1-65dac58df1-ae3d2e1c9f.zip/node_modules/@dashevo/dpp/", + "packageDependencies": [ + ["@dashevo/dpp", "npm:0.22.1"], + ["@apidevtools/json-schema-ref-parser", "npm:8.0.0"], + ["@dashevo/dashcore-lib", "npm:0.19.39"], + ["@dashevo/dashpay-contract", "npm:0.22.1"], + ["@dashevo/dpns-contract", "npm:0.22.1"], + ["@dashevo/feature-flags-contract", "npm:0.22.1"], + ["@dashevo/masternode-reward-shares-contract", "npm:0.22.1"], + ["@dashevo/wasm-re2", "npm:1.0.2"], + ["ajv", "npm:8.8.1"], + ["ajv-formats", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.1.1"], + ["bignumber.js", "npm:9.0.1"], + ["bls-signatures", "npm:0.2.5"], + ["bs58", "npm:4.0.1"], + ["cbor", "npm:8.1.0"], + ["fast-json-patch", "npm:3.1.0"], + ["json-schema-diff-validator", "npm:0.4.1"], + ["json-schema-traverse", "npm:1.0.0"], + ["lodash.clonedeep", "npm:4.5.0"], + ["lodash.clonedeepwith", "npm:4.5.0"], + ["lodash.get", "npm:4.4.2"], + ["lodash.set", "npm:4.3.2"], + ["long", "npm:5.2.0"] + ], + "linkType": "HARD", + }], + ["workspace:packages/js-dpp", { + "packageLocation": "./packages/js-dpp/", + "packageDependencies": [ + ["@dashevo/dpp", "workspace:packages/js-dpp"], + ["@apidevtools/json-schema-ref-parser", "npm:8.0.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/preset-env", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:7.16.4"], + ["@dashevo/dashcore-lib", "npm:0.19.39"], + ["@dashevo/dashpay-contract", "workspace:packages/dashpay-contract"], + ["@dashevo/dpns-contract", "workspace:packages/dpns-contract"], + ["@dashevo/feature-flags-contract", "workspace:packages/feature-flags-contract"], + ["@dashevo/masternode-reward-shares-contract", "workspace:packages/masternode-reward-shares-contract"], + ["@dashevo/wasm-re2", "npm:1.0.2"], + ["acorn", "npm:8.6.0"], + ["ajv", "npm:8.8.1"], + ["ajv-formats", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.1.1"], + ["assert", "npm:2.0.0"], + ["babel-loader", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:8.2.3"], + ["bignumber.js", "npm:9.0.1"], + ["bls-signatures", "npm:0.2.5"], + ["bs58", "npm:4.0.1"], + ["buffer", "npm:6.0.3"], + ["cbor", "npm:8.1.0"], + ["chai", "npm:4.3.4"], + ["chai-as-promised", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:7.1.1"], + ["chai-exclude", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.1.0"], + ["chai-string", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:1.5.0"], + ["core-js", "npm:3.19.1"], + ["crypto-browserify", "npm:3.12.0"], + ["dirty-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.0.1"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["events", "npm:3.3.0"], + ["fast-json-patch", "npm:3.1.0"], + ["https-browserify", "npm:1.0.0"], + ["json-schema-diff-validator", "npm:0.4.1"], + ["json-schema-traverse", "npm:1.0.0"], + ["karma", "npm:6.3.9"], + ["karma-chai", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:0.1.0"], + ["karma-chrome-launcher", "npm:3.1.0"], + ["karma-firefox-launcher", "npm:2.1.2"], + ["karma-mocha", "npm:2.0.1"], + ["karma-mocha-reporter", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.2.5"], + ["karma-webpack", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:5.0.0"], + ["lodash.clonedeep", "npm:4.5.0"], + ["lodash.clonedeepwith", "npm:4.5.0"], + ["lodash.get", "npm:4.4.2"], + ["lodash.set", "npm:4.3.2"], + ["long", "npm:5.2.0"], + ["mocha", "npm:9.1.3"], + ["node-inspect-extracted", "npm:1.0.8"], + ["nyc", "npm:15.1.0"], + ["path-browserify", "npm:1.0.1"], + ["process", "npm:0.11.10"], + ["sinon", "npm:11.1.2"], + ["sinon-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0"], + ["stream-browserify", "npm:3.0.0"], + ["stream-http", "npm:3.2.0"], + ["string_decoder", "npm:1.3.0"], + ["url", "npm:0.11.0"], + ["util", "npm:0.12.4"], + ["webpack", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:5.64.1"], + ["webpack-cli", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:4.9.1"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/drive", [ + ["workspace:packages/js-drive", { + "packageLocation": "./packages/js-drive/", + "packageDependencies": [ + ["@dashevo/drive", "workspace:packages/js-drive"], + ["@dashevo/abci", "npm:0.23.0-dev.1"], + ["@dashevo/dapi-grpc", "workspace:packages/dapi-grpc"], + ["@dashevo/dashcore-lib", "npm:0.19.39"], + ["@dashevo/dashd-rpc", "npm:2.3.2"], + ["@dashevo/dashpay-contract", "workspace:packages/dashpay-contract"], + ["@dashevo/dp-services-ctl", "https://github.com/dashevo/js-dp-services-ctl.git#commit=3976076b0018c5b4632ceda4c752fc597f27a640"], + ["@dashevo/dpns-contract", "workspace:packages/dpns-contract"], + ["@dashevo/dpp", "workspace:packages/js-dpp"], + ["@dashevo/feature-flags-contract", "workspace:packages/feature-flags-contract"], + ["@dashevo/grpc-common", "workspace:packages/js-grpc-common"], + ["@dashevo/masternode-reward-shares-contract", "workspace:packages/masternode-reward-shares-contract"], + ["@dashevo/rs-drive", "npm:0.23.0-dev.5.pr.114.5"], + ["@types/pino", "npm:6.3.12"], + ["ajv", "npm:8.8.1"], + ["ajv-keywords", "virtual:34fbe5a7dba3086dcbcce8a7faed986b10f7a208f11db70499feb2c1afd76e24089e5b95f9e3b937e89512de1cf4937177cc2000303a1e908baefc73362a7d48#npm:5.0.0"], + ["awilix", "npm:4.3.4"], + ["babel-eslint", "virtual:27dae49067a60fa65fec6e1c3adad1497d0dda3f71eda711624109131ff3b7d1061a20f55e89b5a0a219da1f7a0a1a0a76bc414d36870315bd60acf5bdcb7f55#npm:10.1.0"], + ["blake3", "npm:2.1.7"], + ["browserify", "npm:16.5.2"], + ["bs58", "npm:4.0.1"], + ["cbor", "npm:8.1.0"], + ["chai", "npm:4.3.4"], + ["chai-as-promised", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:7.1.1"], + ["chai-string", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:1.5.0"], + ["chalk", "npm:4.1.2"], + ["dirty-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.0.1"], + ["dotenv-expand", "npm:5.1.0"], + ["dotenv-safe", "npm:8.2.0"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["find-my-way", "npm:2.2.5"], + ["js-merkle", "npm:0.1.5"], + ["levelup", "npm:4.4.0"], + ["lodash.clonedeep", "npm:4.5.0"], + ["lodash.get", "npm:4.4.2"], + ["lodash.set", "npm:4.3.2"], + ["long", "npm:5.2.0"], + ["lru-cache", "npm:5.1.1"], + ["memdown", "npm:5.1.0"], + ["mocha", "npm:9.1.3"], + ["node-graceful", "npm:3.1.0"], + ["nyc", "npm:15.1.0"], + ["pino", "npm:6.13.3"], + ["pino-multi-stream", "npm:5.3.0"], + ["pino-pretty", "npm:4.8.0"], + ["rimraf", "npm:3.0.2"], + ["setimmediate", "npm:1.0.5"], + ["sinon", "npm:11.1.2"], + ["sinon-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0"], + ["through2", "npm:3.0.2"], + ["zeromq", "npm:5.2.8"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/feature-flags-contract", [ + ["npm:0.22.1", { + "packageLocation": "./.yarn/cache/@dashevo-feature-flags-contract-npm-0.22.1-9357800b5c-6590ea68bb.zip/node_modules/@dashevo/feature-flags-contract/", + "packageDependencies": [ + ["@dashevo/feature-flags-contract", "npm:0.22.1"] + ], + "linkType": "HARD", + }], + ["workspace:packages/feature-flags-contract", { + "packageLocation": "./packages/feature-flags-contract/", + "packageDependencies": [ + ["@dashevo/feature-flags-contract", "workspace:packages/feature-flags-contract"], + ["@dashevo/dpp", "workspace:packages/js-dpp"], + ["chai", "npm:4.3.4"], + ["dirty-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.0.1"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["mocha", "npm:9.1.3"], + ["sinon", "npm:11.1.2"], + ["sinon-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/grpc-common", [ + ["workspace:packages/js-grpc-common", { + "packageLocation": "./packages/js-grpc-common/", + "packageDependencies": [ + ["@dashevo/grpc-common", "workspace:packages/js-grpc-common"], + ["@dashevo/protobufjs", "npm:6.10.5"], + ["@grpc/grpc-js", "npm:1.4.4"], + ["@grpc/proto-loader", "npm:0.5.6"], + ["cbor", "npm:8.1.0"], + ["chai", "npm:4.3.4"], + ["chai-as-promised", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:7.1.1"], + ["dirty-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.0.1"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["lodash.get", "npm:4.4.2"], + ["long", "npm:5.2.0"], + ["mocha", "npm:9.1.3"], + ["mocha-sinon", "virtual:595d7482cc8ddf98ee6aef33fc48b46393554ab5f17f851ef62e6e39315e53666c3e66226b978689aa0bc7f1e83a03081511a21db1c381362fe67614887077f9#npm:2.1.2"], + ["nyc", "npm:15.1.0"], + ["semver", "npm:7.3.5"], + ["sinon", "npm:11.1.2"], + ["sinon-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/masternode-reward-shares-contract", [ + ["npm:0.22.1", { + "packageLocation": "./.yarn/cache/@dashevo-masternode-reward-shares-contract-npm-0.22.1-9cfbca722e-577a40876b.zip/node_modules/@dashevo/masternode-reward-shares-contract/", + "packageDependencies": [ + ["@dashevo/masternode-reward-shares-contract", "npm:0.22.1"] + ], + "linkType": "HARD", + }], + ["workspace:packages/masternode-reward-shares-contract", { + "packageLocation": "./packages/masternode-reward-shares-contract/", + "packageDependencies": [ + ["@dashevo/masternode-reward-shares-contract", "workspace:packages/masternode-reward-shares-contract"], + ["@dashevo/dpp", "workspace:packages/js-dpp"], + ["chai", "npm:4.3.4"], + ["dirty-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.0.1"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["mocha", "npm:9.1.3"], + ["sinon", "npm:11.1.2"], + ["sinon-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/merk", [ + ["https://github.com/dashevo/node-merk.git#commit=eb37003300d22c6c04604463bcd7e861dd07000f", { + "packageLocation": "./.yarn/unplugged/@dashevo-merk-https-e3939f6b2b/node_modules/@dashevo/merk/", + "packageDependencies": [ + ["@dashevo/merk", "https://github.com/dashevo/node-merk.git#commit=eb37003300d22c6c04604463bcd7e861dd07000f"], + ["neon-load-or-build", "npm:2.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["@dashevo/platform", [ + ["workspace:.", { + "packageLocation": "./", + "packageDependencies": [ + ["@dashevo/platform", "workspace:."], + ["add-stream", "npm:1.0.0"], + ["conventional-changelog", "npm:3.1.24"], + ["conventional-changelog-dash", "https://github.com/dashevo/conventional-changelog-dash.git#commit=3d4d77e2cea876a27b92641c28b15aedf13eb788"], + ["semver", "npm:7.3.5"], + ["tempfile", "npm:3.0.0"], + ["ultra-runner", "npm:3.10.5"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/platform-test-suite", [ + ["workspace:packages/platform-test-suite", { + "packageLocation": "./packages/platform-test-suite/", + "packageDependencies": [ + ["@dashevo/platform-test-suite", "workspace:packages/platform-test-suite"], + ["@dashevo/dapi-client", "workspace:packages/js-dapi-client"], + ["@dashevo/dashcore-lib", "npm:0.19.39"], + ["@dashevo/dpns-contract", "workspace:packages/dpns-contract"], + ["@dashevo/dpp", "workspace:packages/js-dpp"], + ["@dashevo/feature-flags-contract", "workspace:packages/feature-flags-contract"], + ["@dashevo/grpc-common", "workspace:packages/js-grpc-common"], + ["@dashevo/masternode-reward-shares-contract", "workspace:packages/masternode-reward-shares-contract"], + ["@dashevo/merk", "https://github.com/dashevo/node-merk.git#commit=eb37003300d22c6c04604463bcd7e861dd07000f"], + ["@dashevo/wallet-lib", "workspace:packages/wallet-lib"], + ["assert", "npm:2.0.0"], + ["assert-browserify", "npm:2.0.0"], + ["blake3", "npm:2.1.7"], + ["browserify-zlib", "npm:0.2.0"], + ["buffer", "npm:6.0.3"], + ["bufferutil", "npm:4.0.6"], + ["chai", "npm:4.3.4"], + ["chai-as-promised", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:7.1.1"], + ["crypto-browserify", "npm:3.12.0"], + ["dash", "workspace:packages/js-dash-sdk"], + ["dirty-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.0.1"], + ["dotenv-safe", "npm:8.2.0"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["events", "npm:3.3.0"], + ["github-api", "npm:3.4.0"], + ["https-browserify", "npm:1.0.0"], + ["js-merkle", "npm:0.1.5"], + ["karma", "npm:6.3.9"], + ["karma-chai", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:0.1.0"], + ["karma-chrome-launcher", "npm:3.1.0"], + ["karma-firefox-launcher", "npm:2.1.2"], + ["karma-mocha", "npm:2.0.1"], + ["karma-mocha-reporter", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.2.5"], + ["karma-sourcemap-loader", "npm:0.3.8"], + ["karma-webpack", "virtual:01938c2be4835443e5a304e2b117c575220e96e8b7cedeb0f48d79264590b4c4babc6d1fea6367f522b1ca0149d795b42f2ab89c34a6ffe3c20f0a8cbb8b4453#npm:5.0.0"], + ["localforage", "npm:1.10.0"], + ["mocha", "npm:9.1.3"], + ["net", "npm:1.0.2"], + ["nodeforage", "npm:1.1.2"], + ["os-browserify", "npm:0.3.0"], + ["path-browserify", "npm:1.0.1"], + ["process", "npm:0.11.10"], + ["semver", "npm:7.3.5"], + ["sinon", "npm:11.1.2"], + ["sinon-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0"], + ["stream-browserify", "npm:3.0.0"], + ["stream-http", "npm:3.2.0"], + ["string_decoder", "npm:1.3.0"], + ["tls", "npm:0.0.1"], + ["url", "npm:0.11.0"], + ["utf-8-validate", "npm:5.0.9"], + ["util", "npm:0.12.4"], + ["webpack", "virtual:01938c2be4835443e5a304e2b117c575220e96e8b7cedeb0f48d79264590b4c4babc6d1fea6367f522b1ca0149d795b42f2ab89c34a6ffe3c20f0a8cbb8b4453#npm:5.64.1"], + ["ws", "virtual:01938c2be4835443e5a304e2b117c575220e96e8b7cedeb0f48d79264590b4c4babc6d1fea6367f522b1ca0149d795b42f2ab89c34a6ffe3c20f0a8cbb8b4453#npm:7.5.5"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/protobufjs", [ + ["npm:6.10.5", { + "packageLocation": "./.yarn/unplugged/@dashevo-protobufjs-npm-6.10.5-9ffa190993/node_modules/@dashevo/protobufjs/", + "packageDependencies": [ + ["@dashevo/protobufjs", "npm:6.10.5"], + ["@protobufjs/aspromise", "npm:1.1.2"], + ["@protobufjs/base64", "npm:1.1.2"], + ["@protobufjs/codegen", "npm:2.0.4"], + ["@protobufjs/eventemitter", "npm:1.1.0"], + ["@protobufjs/fetch", "npm:1.1.0"], + ["@protobufjs/float", "npm:1.0.2"], + ["@protobufjs/inquire", "npm:1.1.0"], + ["@protobufjs/path", "npm:1.1.2"], + ["@protobufjs/pool", "npm:1.1.0"], + ["@protobufjs/utf8", "npm:1.1.0"], + ["@types/long", "npm:4.0.1"], + ["@types/node", "npm:13.13.52"], + ["chalk", "npm:3.0.0"], + ["escodegen", "npm:2.0.0"], + ["espree", "npm:9.1.0"], + ["estraverse", "npm:5.3.0"], + ["glob", "npm:7.2.0"], + ["long", "npm:4.0.0"], + ["minimist", "npm:1.2.5"], + ["semver", "npm:7.3.5"], + ["uglify-js", "npm:3.14.4"] + ], + "linkType": "HARD", + }] + ]], + ["@dashevo/rs-drive", [ + ["npm:0.23.0-dev.5.pr.114.5", { + "packageLocation": "./.yarn/unplugged/@dashevo-rs-drive-npm-0.23.0-dev.5.pr.114.5-a6cb7a87e0/node_modules/@dashevo/rs-drive/", + "packageDependencies": [ + ["@dashevo/rs-drive", "npm:0.23.0-dev.5.pr.114.5"], + ["@dashevo/dpp", "npm:0.22.1"], + ["cargo-cp-artifact", "npm:0.1.6"], + ["cbor", "npm:8.1.0"], + ["neon-load-or-build", "npm:2.2.2"], + ["neon-tag-prebuild", "https://github.com/shumkov/neon-tag-prebuild.git#commit=a429834da27432b129eceb737e4d2b3f03fa5496"] + ], + "linkType": "HARD", + }] + ]], + ["@dashevo/wallet-lib", [ + ["workspace:packages/wallet-lib", { + "packageLocation": "./packages/wallet-lib/", + "packageDependencies": [ + ["@dashevo/wallet-lib", "workspace:packages/wallet-lib"], + ["@dashevo/dapi-client", "workspace:packages/js-dapi-client"], + ["@dashevo/dashcore-lib", "npm:0.19.39"], + ["@dashevo/dpp", "workspace:packages/js-dpp"], + ["@dashevo/grpc-common", "workspace:packages/js-grpc-common"], + ["assert", "npm:2.0.0"], + ["browserify-zlib", "npm:0.2.0"], + ["buffer", "npm:6.0.3"], + ["cbor", "npm:8.1.0"], + ["chai", "npm:4.3.4"], + ["chai-as-promised", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:7.1.1"], + ["crypto-browserify", "npm:3.12.0"], + ["crypto-js", "npm:4.1.1"], + ["dotenv-safe", "npm:8.2.0"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["events", "npm:3.3.0"], + ["https-browserify", "npm:1.0.0"], + ["karma", "npm:6.3.9"], + ["karma-chai", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:0.1.0"], + ["karma-chrome-launcher", "npm:3.1.0"], + ["karma-mocha", "npm:2.0.1"], + ["karma-mocha-reporter", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.2.5"], + ["karma-sourcemap-loader", "npm:0.3.8"], + ["karma-webpack", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:5.0.0"], + ["lodash", "npm:4.17.21"], + ["mocha", "npm:9.1.3"], + ["node-inspect-extracted", "npm:1.0.8"], + ["nyc", "npm:15.1.0"], + ["os-browserify", "npm:0.3.0"], + ["path-browserify", "npm:1.0.1"], + ["pbkdf2", "npm:3.1.2"], + ["process", "npm:0.11.10"], + ["setimmediate", "npm:1.0.5"], + ["sinon", "npm:11.1.2"], + ["sinon-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0"], + ["stream-browserify", "npm:3.0.0"], + ["stream-http", "npm:3.2.0"], + ["string_decoder", "npm:1.3.0"], + ["url", "npm:0.11.0"], + ["util", "npm:0.12.4"], + ["webpack", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:5.64.1"], + ["webpack-cli", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:4.9.1"], + ["winston", "npm:3.3.3"] + ], + "linkType": "SOFT", + }] + ]], + ["@dashevo/wasm-re2", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/@dashevo-wasm-re2-npm-1.0.2-50818efe42-3d54788e4e.zip/node_modules/@dashevo/wasm-re2/", + "packageDependencies": [ + ["@dashevo/wasm-re2", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["@dashevo/x11-hash-js", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/@dashevo-x11-hash-js-npm-1.0.2-f84bd94ece-a4856fb50f.zip/node_modules/@dashevo/x11-hash-js/", + "packageDependencies": [ + ["@dashevo/x11-hash-js", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["@discoveryjs/json-ext", [ + ["npm:0.5.5", { + "packageLocation": "./.yarn/cache/@discoveryjs-json-ext-npm-0.5.5-595932ce4b-40844548d8.zip/node_modules/@discoveryjs/json-ext/", + "packageDependencies": [ + ["@discoveryjs/json-ext", "npm:0.5.5"] + ], + "linkType": "HARD", + }] + ]], + ["@eslint/eslintrc", [ + ["npm:0.4.3", { + "packageLocation": "./.yarn/cache/@eslint-eslintrc-npm-0.4.3-ee1bbcab87-03a7704150.zip/node_modules/@eslint/eslintrc/", + "packageDependencies": [ + ["@eslint/eslintrc", "npm:0.4.3"], + ["ajv", "npm:6.12.6"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["espree", "npm:7.3.1"], + ["globals", "npm:13.12.0"], + ["ignore", "npm:4.0.6"], + ["import-fresh", "npm:3.3.0"], + ["js-yaml", "npm:3.14.1"], + ["minimatch", "npm:3.0.4"], + ["strip-json-comments", "npm:3.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["@gar/promisify", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/@gar-promisify-npm-1.1.2-2343f94380-d05081e088.zip/node_modules/@gar/promisify/", + "packageDependencies": [ + ["@gar/promisify", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["@grpc/grpc-js", [ + ["npm:1.4.4", { + "packageLocation": "./.yarn/cache/@grpc-grpc-js-npm-1.4.4-f333f82239-f9be710cef.zip/node_modules/@grpc/grpc-js/", + "packageDependencies": [ + ["@grpc/grpc-js", "npm:1.4.4"], + ["@grpc/proto-loader", "npm:0.6.7"], + ["@types/node", "npm:17.0.21"] + ], + "linkType": "HARD", + }] + ]], + ["@grpc/proto-loader", [ + ["npm:0.5.6", { + "packageLocation": "./.yarn/cache/@grpc-proto-loader-npm-0.5.6-ef97ffeb0b-13fe76d84a.zip/node_modules/@grpc/proto-loader/", + "packageDependencies": [ + ["@grpc/proto-loader", "npm:0.5.6"], + ["lodash.camelcase", "npm:4.3.0"], + ["protobufjs", "npm:6.11.2"] + ], + "linkType": "HARD", + }], + ["npm:0.6.7", { + "packageLocation": "./.yarn/cache/@grpc-proto-loader-npm-0.6.7-283fc039b9-af1909ec36.zip/node_modules/@grpc/proto-loader/", + "packageDependencies": [ + ["@grpc/proto-loader", "npm:0.6.7"], + ["@types/long", "npm:4.0.1"], + ["lodash.camelcase", "npm:4.3.0"], + ["long", "npm:4.0.0"], + ["protobufjs", "npm:6.11.2"], + ["yargs", "npm:16.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["@hapi/bourne", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/@hapi-bourne-npm-2.0.0-8eeda7e0a2-2ea0922101.zip/node_modules/@hapi/bourne/", + "packageDependencies": [ + ["@hapi/bourne", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@humanwhocodes/config-array", [ + ["npm:0.5.0", { + "packageLocation": "./.yarn/cache/@humanwhocodes-config-array-npm-0.5.0-5ded120470-44ee6a9f05.zip/node_modules/@humanwhocodes/config-array/", + "packageDependencies": [ + ["@humanwhocodes/config-array", "npm:0.5.0"], + ["@humanwhocodes/object-schema", "npm:1.2.1"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["minimatch", "npm:3.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["@humanwhocodes/object-schema", [ + ["npm:1.2.1", { + "packageLocation": "./.yarn/cache/@humanwhocodes-object-schema-npm-1.2.1-eb622b5d0e-a824a1ec31.zip/node_modules/@humanwhocodes/object-schema/", + "packageDependencies": [ + ["@humanwhocodes/object-schema", "npm:1.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["@hutson/parse-repository-url", [ + ["npm:3.0.2", { + "packageLocation": "./.yarn/cache/@hutson-parse-repository-url-npm-3.0.2-ae5ef1b671-39992c5f18.zip/node_modules/@hutson/parse-repository-url/", + "packageDependencies": [ + ["@hutson/parse-repository-url", "npm:3.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["@isaacs/string-locale-compare", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@isaacs-string-locale-compare-npm-1.1.0-3911094464-7287da5d11.zip/node_modules/@isaacs/string-locale-compare/", + "packageDependencies": [ + ["@isaacs/string-locale-compare", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["@istanbuljs/load-nyc-config", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@istanbuljs-load-nyc-config-npm-1.1.0-42d17c9cb1-d578da5e2e.zip/node_modules/@istanbuljs/load-nyc-config/", + "packageDependencies": [ + ["@istanbuljs/load-nyc-config", "npm:1.1.0"], + ["camelcase", "npm:5.3.1"], + ["find-up", "npm:4.1.0"], + ["get-package-type", "npm:0.1.0"], + ["js-yaml", "npm:3.14.1"], + ["resolve-from", "npm:5.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@istanbuljs/schema", [ + ["npm:0.1.3", { + "packageLocation": "./.yarn/cache/@istanbuljs-schema-npm-0.1.3-466bd3eaaa-5282759d96.zip/node_modules/@istanbuljs/schema/", + "packageDependencies": [ + ["@istanbuljs/schema", "npm:0.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["@jest/types", [ + ["npm:27.2.5", { + "packageLocation": "./.yarn/cache/@jest-types-npm-27.2.5-620da3d425-322603c243.zip/node_modules/@jest/types/", + "packageDependencies": [ + ["@jest/types", "npm:27.2.5"], + ["@types/istanbul-lib-coverage", "npm:2.0.3"], + ["@types/istanbul-reports", "npm:3.0.1"], + ["@types/node", "npm:17.0.21"], + ["@types/yargs", "npm:16.0.4"], + ["chalk", "npm:4.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["@jridgewell/resolve-uri", [ + ["npm:3.0.8", { + "packageLocation": "./.yarn/cache/@jridgewell-resolve-uri-npm-3.0.8-94779c6a1d-28d739f49b.zip/node_modules/@jridgewell/resolve-uri/", + "packageDependencies": [ + ["@jridgewell/resolve-uri", "npm:3.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["@jridgewell/sourcemap-codec", [ + ["npm:1.4.14", { + "packageLocation": "./.yarn/cache/@jridgewell-sourcemap-codec-npm-1.4.14-f5f0630788-61100637b6.zip/node_modules/@jridgewell/sourcemap-codec/", + "packageDependencies": [ + ["@jridgewell/sourcemap-codec", "npm:1.4.14"] + ], + "linkType": "HARD", + }] + ]], + ["@jridgewell/trace-mapping", [ + ["npm:0.3.14", { + "packageLocation": "./.yarn/cache/@jridgewell-trace-mapping-npm-0.3.14-c78fcccfdf-b9537b9630.zip/node_modules/@jridgewell/trace-mapping/", + "packageDependencies": [ + ["@jridgewell/trace-mapping", "npm:0.3.14"], + ["@jridgewell/resolve-uri", "npm:3.0.8"], + ["@jridgewell/sourcemap-codec", "npm:1.4.14"] + ], + "linkType": "HARD", + }] + ]], + ["@jsdevtools/ono", [ + ["npm:7.1.3", { + "packageLocation": "./.yarn/cache/@jsdevtools-ono-npm-7.1.3-cb2313543b-2297fcd472.zip/node_modules/@jsdevtools/ono/", + "packageDependencies": [ + ["@jsdevtools/ono", "npm:7.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["@leichtgewicht/ip-codec", [ + ["npm:2.0.3", { + "packageLocation": "./.yarn/cache/@leichtgewicht-ip-codec-npm-2.0.3-536ebba640-5b6bee0481.zip/node_modules/@leichtgewicht/ip-codec/", + "packageDependencies": [ + ["@leichtgewicht/ip-codec", "npm:2.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["@nodelib/fs.scandir", [ + ["npm:2.1.5", { + "packageLocation": "./.yarn/cache/@nodelib-fs.scandir-npm-2.1.5-89c67370dd-a970d595bd.zip/node_modules/@nodelib/fs.scandir/", + "packageDependencies": [ + ["@nodelib/fs.scandir", "npm:2.1.5"], + ["@nodelib/fs.stat", "npm:2.0.5"], + ["run-parallel", "npm:1.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["@nodelib/fs.stat", [ + ["npm:2.0.5", { + "packageLocation": "./.yarn/cache/@nodelib-fs.stat-npm-2.0.5-01f4dd3030-012480b5ca.zip/node_modules/@nodelib/fs.stat/", + "packageDependencies": [ + ["@nodelib/fs.stat", "npm:2.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["@nodelib/fs.walk", [ + ["npm:1.2.8", { + "packageLocation": "./.yarn/cache/@nodelib-fs.walk-npm-1.2.8-b4a89da548-190c643f15.zip/node_modules/@nodelib/fs.walk/", + "packageDependencies": [ + ["@nodelib/fs.walk", "npm:1.2.8"], + ["@nodelib/fs.scandir", "npm:2.1.5"], + ["fastq", "npm:1.13.0"] + ], + "linkType": "HARD", + }] + ]], + ["@npmcli/arborist", [ + ["npm:4.3.1", { + "packageLocation": "./.yarn/cache/@npmcli-arborist-npm-4.3.1-68b2741cb0-51470ebb9a.zip/node_modules/@npmcli/arborist/", + "packageDependencies": [ + ["@npmcli/arborist", "npm:4.3.1"], + ["@isaacs/string-locale-compare", "npm:1.1.0"], + ["@npmcli/installed-package-contents", "npm:1.0.7"], + ["@npmcli/map-workspaces", "npm:2.0.1"], + ["@npmcli/metavuln-calculator", "npm:2.0.0"], + ["@npmcli/move-file", "npm:1.1.2"], + ["@npmcli/name-from-folder", "npm:1.0.1"], + ["@npmcli/node-gyp", "npm:1.0.3"], + ["@npmcli/package-json", "npm:1.0.1"], + ["@npmcli/run-script", "npm:2.0.0"], + ["bin-links", "npm:3.0.0"], + ["cacache", "npm:15.3.0"], + ["common-ancestor-path", "npm:1.0.1"], + ["json-parse-even-better-errors", "npm:2.3.1"], + ["json-stringify-nice", "npm:1.1.4"], + ["mkdirp", "npm:1.0.4"], + ["mkdirp-infer-owner", "npm:2.0.0"], + ["npm-install-checks", "npm:4.0.0"], + ["npm-package-arg", "npm:8.1.5"], + ["npm-pick-manifest", "npm:6.1.1"], + ["npm-registry-fetch", "npm:12.0.2"], + ["pacote", "npm:12.0.3"], + ["parse-conflict-json", "npm:2.0.1"], + ["proc-log", "npm:1.0.0"], + ["promise-all-reject-late", "npm:1.0.1"], + ["promise-call-limit", "npm:1.0.1"], + ["read-package-json-fast", "npm:2.0.3"], + ["readdir-scoped-modules", "npm:1.1.0"], + ["rimraf", "npm:3.0.2"], + ["semver", "npm:7.3.5"], + ["ssri", "npm:8.0.1"], + ["treeverse", "npm:1.0.4"], + ["walk-up-path", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@npmcli/fs", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/@npmcli-fs-npm-1.0.0-92194475f3-f2b4990107.zip/node_modules/@npmcli/fs/", + "packageDependencies": [ + ["@npmcli/fs", "npm:1.0.0"], + ["@gar/promisify", "npm:1.1.2"], + ["semver", "npm:7.3.5"] + ], + "linkType": "HARD", + }] + ]], + ["@npmcli/git", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/@npmcli-git-npm-2.1.0-b85bc3f444-1f89752df7.zip/node_modules/@npmcli/git/", + "packageDependencies": [ + ["@npmcli/git", "npm:2.1.0"], + ["@npmcli/promise-spawn", "npm:1.3.2"], + ["lru-cache", "npm:6.0.0"], + ["mkdirp", "npm:1.0.4"], + ["npm-pick-manifest", "npm:6.1.1"], + ["promise-inflight", "virtual:a7e5239c6ae68bf6359adfd3598326db000e94dbb349bc00a3852ed53a31712a0e2e787228c6e859d3e5cf2fbb872aba1ea4abe4995cef8086a77ef619ae1be6#npm:1.0.1"], + ["promise-retry", "npm:2.0.1"], + ["semver", "npm:7.3.5"], + ["which", "npm:2.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["@npmcli/installed-package-contents", [ + ["npm:1.0.7", { + "packageLocation": "./.yarn/cache/@npmcli-installed-package-contents-npm-1.0.7-b15a13ab4f-a4a29b99d4.zip/node_modules/@npmcli/installed-package-contents/", + "packageDependencies": [ + ["@npmcli/installed-package-contents", "npm:1.0.7"], + ["npm-bundled", "npm:1.1.2"], + ["npm-normalize-package-bin", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["@npmcli/map-workspaces", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/@npmcli-map-workspaces-npm-2.0.1-4911719cd1-16c6738e15.zip/node_modules/@npmcli/map-workspaces/", + "packageDependencies": [ + ["@npmcli/map-workspaces", "npm:2.0.1"], + ["@npmcli/name-from-folder", "npm:1.0.1"], + ["glob", "npm:7.2.0"], + ["minimatch", "npm:5.0.0"], + ["read-package-json-fast", "npm:2.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["@npmcli/metavuln-calculator", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/@npmcli-metavuln-calculator-npm-2.0.0-df87832d39-bf88115e7c.zip/node_modules/@npmcli/metavuln-calculator/", + "packageDependencies": [ + ["@npmcli/metavuln-calculator", "npm:2.0.0"], + ["cacache", "npm:15.3.0"], + ["json-parse-even-better-errors", "npm:2.3.1"], + ["pacote", "npm:12.0.3"], + ["semver", "npm:7.3.5"] + ], + "linkType": "HARD", + }] + ]], + ["@npmcli/move-file", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/@npmcli-move-file-npm-1.1.2-4f6c7b3354-c96381d4a3.zip/node_modules/@npmcli/move-file/", + "packageDependencies": [ + ["@npmcli/move-file", "npm:1.1.2"], + ["mkdirp", "npm:1.0.4"], + ["rimraf", "npm:3.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["@npmcli/name-from-folder", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/@npmcli-name-from-folder-npm-1.0.1-b2b2fde7e0-67339f4096.zip/node_modules/@npmcli/name-from-folder/", + "packageDependencies": [ + ["@npmcli/name-from-folder", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["@npmcli/node-gyp", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/@npmcli-node-gyp-npm-1.0.3-678a56ae5b-496d5eef2e.zip/node_modules/@npmcli/node-gyp/", + "packageDependencies": [ + ["@npmcli/node-gyp", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["@npmcli/package-json", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/@npmcli-package-json-npm-1.0.1-4a9d430114-08b66c8ddb.zip/node_modules/@npmcli/package-json/", + "packageDependencies": [ + ["@npmcli/package-json", "npm:1.0.1"], + ["json-parse-even-better-errors", "npm:2.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["@npmcli/promise-spawn", [ + ["npm:1.3.2", { + "packageLocation": "./.yarn/cache/@npmcli-promise-spawn-npm-1.3.2-7762aaada5-543b7c1e26.zip/node_modules/@npmcli/promise-spawn/", + "packageDependencies": [ + ["@npmcli/promise-spawn", "npm:1.3.2"], + ["infer-owner", "npm:1.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["@npmcli/run-script", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/@npmcli-run-script-npm-2.0.0-244659a556-c016ea9411.zip/node_modules/@npmcli/run-script/", + "packageDependencies": [ + ["@npmcli/run-script", "npm:2.0.0"], + ["@npmcli/node-gyp", "npm:1.0.3"], + ["@npmcli/promise-spawn", "npm:1.3.2"], + ["node-gyp", "npm:8.4.1"], + ["read-package-json-fast", "npm:2.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["@oclif/color", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/@oclif-color-npm-1.0.0-a76d65e26d-60521f90eb.zip/node_modules/@oclif/color/", + "packageDependencies": [ + ["@oclif/color", "npm:1.0.0"], + ["ansi-styles", "npm:4.3.0"], + ["chalk", "npm:4.1.2"], + ["strip-ansi", "npm:6.0.1"], + ["supports-color", "npm:8.1.1"], + ["tslib", "npm:2.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["@oclif/core", [ + ["npm:1.3.4", { + "packageLocation": "./.yarn/cache/@oclif-core-npm-1.3.4-e0bcdb30fd-c7f29f71ce.zip/node_modules/@oclif/core/", + "packageDependencies": [ + ["@oclif/core", "npm:1.3.4"], + ["@oclif/linewrap", "npm:1.0.0"], + ["@oclif/screen", "npm:3.0.2"], + ["ansi-escapes", "npm:4.3.2"], + ["ansi-styles", "npm:4.3.0"], + ["cardinal", "npm:2.1.1"], + ["chalk", "npm:4.1.2"], + ["clean-stack", "npm:3.0.1"], + ["cli-progress", "npm:3.10.0"], + ["debug", "virtual:e0bcdb30fd626f99ea6779721a0a71e37a1e0c50c9b9efdc8c529c07facadd7c604bf4988d80d980de298e61ede04b9695580df0baa812da80a4ce3b8a002d33#npm:4.3.3"], + ["ejs", "npm:3.1.6"], + ["fs-extra", "npm:9.1.0"], + ["get-package-type", "npm:0.1.0"], + ["globby", "npm:11.1.0"], + ["hyperlinker", "npm:1.0.0"], + ["indent-string", "npm:4.0.0"], + ["is-wsl", "npm:2.2.0"], + ["js-yaml", "npm:3.14.1"], + ["lodash", "npm:4.17.21"], + ["natural-orderby", "npm:2.0.3"], + ["object-treeify", "npm:1.1.33"], + ["password-prompt", "npm:1.1.2"], + ["semver", "npm:7.3.5"], + ["string-width", "npm:4.2.3"], + ["strip-ansi", "npm:6.0.1"], + ["supports-color", "npm:8.1.1"], + ["supports-hyperlinks", "npm:2.2.0"], + ["tslib", "npm:2.3.1"], + ["widest-line", "npm:3.1.0"], + ["wrap-ansi", "npm:7.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@oclif/linewrap", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/@oclif-linewrap-npm-1.0.0-e738997487-a072016a58.zip/node_modules/@oclif/linewrap/", + "packageDependencies": [ + ["@oclif/linewrap", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@oclif/plugin-help", [ + ["npm:5.1.11", { + "packageLocation": "./.yarn/cache/@oclif-plugin-help-npm-5.1.11-d0cc56652c-ab2d1377cb.zip/node_modules/@oclif/plugin-help/", + "packageDependencies": [ + ["@oclif/plugin-help", "npm:5.1.11"], + ["@oclif/core", "npm:1.3.4"] + ], + "linkType": "HARD", + }] + ]], + ["@oclif/plugin-not-found", [ + ["npm:2.3.1", { + "packageLocation": "./.yarn/cache/@oclif-plugin-not-found-npm-2.3.1-87e43b78d1-b6aeddb733.zip/node_modules/@oclif/plugin-not-found/", + "packageDependencies": [ + ["@oclif/plugin-not-found", "npm:2.3.1"], + ["@oclif/color", "npm:1.0.0"], + ["@oclif/core", "npm:1.3.4"], + ["fast-levenshtein", "npm:3.0.0"], + ["lodash", "npm:4.17.21"] + ], + "linkType": "HARD", + }] + ]], + ["@oclif/plugin-warn-if-update-available", [ + ["npm:2.0.4", { + "packageLocation": "./.yarn/cache/@oclif-plugin-warn-if-update-available-npm-2.0.4-d71b5c1f00-9a127aaaa3.zip/node_modules/@oclif/plugin-warn-if-update-available/", + "packageDependencies": [ + ["@oclif/plugin-warn-if-update-available", "npm:2.0.4"], + ["@oclif/core", "npm:1.3.4"], + ["chalk", "npm:4.1.2"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["fs-extra", "npm:9.1.0"], + ["http-call", "npm:5.3.0"], + ["lodash", "npm:4.17.21"], + ["semver", "npm:7.3.5"] + ], + "linkType": "HARD", + }] + ]], + ["@oclif/screen", [ + ["npm:3.0.2", { + "packageLocation": "./.yarn/cache/@oclif-screen-npm-3.0.2-68fdcc8cd0-962678c65f.zip/node_modules/@oclif/screen/", + "packageDependencies": [ + ["@oclif/screen", "npm:3.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["@octokit/auth-token", [ + ["npm:2.5.0", { + "packageLocation": "./.yarn/cache/@octokit-auth-token-npm-2.5.0-a1c6ffb640-45949296c0.zip/node_modules/@octokit/auth-token/", + "packageDependencies": [ + ["@octokit/auth-token", "npm:2.5.0"], + ["@octokit/types", "npm:6.34.0"] + ], + "linkType": "HARD", + }] + ]], + ["@octokit/core", [ + ["npm:3.5.1", { + "packageLocation": "./.yarn/cache/@octokit-core-npm-3.5.1-a933dedcf7-67179739fc.zip/node_modules/@octokit/core/", + "packageDependencies": [ + ["@octokit/core", "npm:3.5.1"], + ["@octokit/auth-token", "npm:2.5.0"], + ["@octokit/graphql", "npm:4.8.0"], + ["@octokit/request", "npm:5.6.3"], + ["@octokit/request-error", "npm:2.1.0"], + ["@octokit/types", "npm:6.34.0"], + ["before-after-hook", "npm:2.2.2"], + ["universal-user-agent", "npm:6.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@octokit/endpoint", [ + ["npm:6.0.12", { + "packageLocation": "./.yarn/cache/@octokit-endpoint-npm-6.0.12-d467db27fd-b48b29940a.zip/node_modules/@octokit/endpoint/", + "packageDependencies": [ + ["@octokit/endpoint", "npm:6.0.12"], + ["@octokit/types", "npm:6.34.0"], + ["is-plain-object", "npm:5.0.0"], + ["universal-user-agent", "npm:6.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@octokit/graphql", [ + ["npm:4.8.0", { + "packageLocation": "./.yarn/cache/@octokit-graphql-npm-4.8.0-83d118b4da-f68afe53f6.zip/node_modules/@octokit/graphql/", + "packageDependencies": [ + ["@octokit/graphql", "npm:4.8.0"], + ["@octokit/request", "npm:5.6.3"], + ["@octokit/types", "npm:6.34.0"], + ["universal-user-agent", "npm:6.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@octokit/openapi-types", [ + ["npm:11.2.0", { + "packageLocation": "./.yarn/cache/@octokit-openapi-types-npm-11.2.0-10b7a5c509-eb373ea496.zip/node_modules/@octokit/openapi-types/", + "packageDependencies": [ + ["@octokit/openapi-types", "npm:11.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["@octokit/plugin-paginate-rest", [ + ["npm:2.17.0", { + "packageLocation": "./.yarn/cache/@octokit-plugin-paginate-rest-npm-2.17.0-4d48903092-c8753cda6f.zip/node_modules/@octokit/plugin-paginate-rest/", + "packageDependencies": [ + ["@octokit/plugin-paginate-rest", "npm:2.17.0"] + ], + "linkType": "SOFT", + }], + ["virtual:f250ac8e5eb682f2f60768f4330fc728a36405b667dc5acc56c520d0ff4519a3db937536614af90173f6af26d8665c4fe9f532c66765a577f6ea1f6b70d54bc1#npm:2.17.0", { + "packageLocation": "./.yarn/__virtual__/@octokit-plugin-paginate-rest-virtual-f47910934d/0/cache/@octokit-plugin-paginate-rest-npm-2.17.0-4d48903092-c8753cda6f.zip/node_modules/@octokit/plugin-paginate-rest/", + "packageDependencies": [ + ["@octokit/plugin-paginate-rest", "virtual:f250ac8e5eb682f2f60768f4330fc728a36405b667dc5acc56c520d0ff4519a3db937536614af90173f6af26d8665c4fe9f532c66765a577f6ea1f6b70d54bc1#npm:2.17.0"], + ["@octokit/core", "npm:3.5.1"], + ["@octokit/types", "npm:6.34.0"], + ["@types/octokit__core", null] + ], + "packagePeers": [ + "@octokit/core", + "@types/octokit__core" + ], + "linkType": "HARD", + }] + ]], + ["@octokit/plugin-request-log", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/@octokit-plugin-request-log-npm-1.0.4-9ab5a2f888-2086db0005.zip/node_modules/@octokit/plugin-request-log/", + "packageDependencies": [ + ["@octokit/plugin-request-log", "npm:1.0.4"] + ], + "linkType": "SOFT", + }], + ["virtual:f250ac8e5eb682f2f60768f4330fc728a36405b667dc5acc56c520d0ff4519a3db937536614af90173f6af26d8665c4fe9f532c66765a577f6ea1f6b70d54bc1#npm:1.0.4", { + "packageLocation": "./.yarn/__virtual__/@octokit-plugin-request-log-virtual-e50d6a2304/0/cache/@octokit-plugin-request-log-npm-1.0.4-9ab5a2f888-2086db0005.zip/node_modules/@octokit/plugin-request-log/", + "packageDependencies": [ + ["@octokit/plugin-request-log", "virtual:f250ac8e5eb682f2f60768f4330fc728a36405b667dc5acc56c520d0ff4519a3db937536614af90173f6af26d8665c4fe9f532c66765a577f6ea1f6b70d54bc1#npm:1.0.4"], + ["@octokit/core", "npm:3.5.1"], + ["@types/octokit__core", null] + ], + "packagePeers": [ + "@octokit/core", + "@types/octokit__core" + ], + "linkType": "HARD", + }] + ]], + ["@octokit/plugin-rest-endpoint-methods", [ + ["npm:5.13.0", { + "packageLocation": "./.yarn/cache/@octokit-plugin-rest-endpoint-methods-npm-5.13.0-976c113da3-f331457e43.zip/node_modules/@octokit/plugin-rest-endpoint-methods/", + "packageDependencies": [ + ["@octokit/plugin-rest-endpoint-methods", "npm:5.13.0"] + ], + "linkType": "SOFT", + }], + ["virtual:f250ac8e5eb682f2f60768f4330fc728a36405b667dc5acc56c520d0ff4519a3db937536614af90173f6af26d8665c4fe9f532c66765a577f6ea1f6b70d54bc1#npm:5.13.0", { + "packageLocation": "./.yarn/__virtual__/@octokit-plugin-rest-endpoint-methods-virtual-a73b92a65a/0/cache/@octokit-plugin-rest-endpoint-methods-npm-5.13.0-976c113da3-f331457e43.zip/node_modules/@octokit/plugin-rest-endpoint-methods/", + "packageDependencies": [ + ["@octokit/plugin-rest-endpoint-methods", "virtual:f250ac8e5eb682f2f60768f4330fc728a36405b667dc5acc56c520d0ff4519a3db937536614af90173f6af26d8665c4fe9f532c66765a577f6ea1f6b70d54bc1#npm:5.13.0"], + ["@octokit/core", "npm:3.5.1"], + ["@octokit/types", "npm:6.34.0"], + ["@types/octokit__core", null], + ["deprecation", "npm:2.3.1"] + ], + "packagePeers": [ + "@octokit/core", + "@types/octokit__core" + ], + "linkType": "HARD", + }] + ]], + ["@octokit/request", [ + ["npm:5.6.3", { + "packageLocation": "./.yarn/cache/@octokit-request-npm-5.6.3-25a5f5382d-c0b4542eb4.zip/node_modules/@octokit/request/", + "packageDependencies": [ + ["@octokit/request", "npm:5.6.3"], + ["@octokit/endpoint", "npm:6.0.12"], + ["@octokit/request-error", "npm:2.1.0"], + ["@octokit/types", "npm:6.34.0"], + ["is-plain-object", "npm:5.0.0"], + ["node-fetch", "virtual:25a5f5382d53dbf298bf7a1191760bc2e0a523a619eeb0e667b99a8649e8ad183f9e2e0b45f6fb831b92f4078b61622aa567cf79565f6aa5af9597e3c84864f6#npm:2.6.7"], + ["universal-user-agent", "npm:6.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@octokit/request-error", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/@octokit-request-error-npm-2.1.0-51ac624306-baec2b5700.zip/node_modules/@octokit/request-error/", + "packageDependencies": [ + ["@octokit/request-error", "npm:2.1.0"], + ["@octokit/types", "npm:6.34.0"], + ["deprecation", "npm:2.3.1"], + ["once", "npm:1.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["@octokit/rest", [ + ["npm:18.12.0", { + "packageLocation": "./.yarn/cache/@octokit-rest-npm-18.12.0-f250ac8e5e-c18bd6676a.zip/node_modules/@octokit/rest/", + "packageDependencies": [ + ["@octokit/rest", "npm:18.12.0"], + ["@octokit/core", "npm:3.5.1"], + ["@octokit/plugin-paginate-rest", "virtual:f250ac8e5eb682f2f60768f4330fc728a36405b667dc5acc56c520d0ff4519a3db937536614af90173f6af26d8665c4fe9f532c66765a577f6ea1f6b70d54bc1#npm:2.17.0"], + ["@octokit/plugin-request-log", "virtual:f250ac8e5eb682f2f60768f4330fc728a36405b667dc5acc56c520d0ff4519a3db937536614af90173f6af26d8665c4fe9f532c66765a577f6ea1f6b70d54bc1#npm:1.0.4"], + ["@octokit/plugin-rest-endpoint-methods", "virtual:f250ac8e5eb682f2f60768f4330fc728a36405b667dc5acc56c520d0ff4519a3db937536614af90173f6af26d8665c4fe9f532c66765a577f6ea1f6b70d54bc1#npm:5.13.0"] + ], + "linkType": "HARD", + }] + ]], + ["@octokit/types", [ + ["npm:6.34.0", { + "packageLocation": "./.yarn/cache/@octokit-types-npm-6.34.0-1de469b7ee-f122b9aee8.zip/node_modules/@octokit/types/", + "packageDependencies": [ + ["@octokit/types", "npm:6.34.0"], + ["@octokit/openapi-types", "npm:11.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/aspromise", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/@protobufjs-aspromise-npm-1.1.2-71d00b938f-011fe7ef08.zip/node_modules/@protobufjs/aspromise/", + "packageDependencies": [ + ["@protobufjs/aspromise", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/base64", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/@protobufjs-base64-npm-1.1.2-cd8ca6814a-67173ac34d.zip/node_modules/@protobufjs/base64/", + "packageDependencies": [ + ["@protobufjs/base64", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/codegen", [ + ["npm:2.0.4", { + "packageLocation": "./.yarn/cache/@protobufjs-codegen-npm-2.0.4-36e188bbe6-59240c850b.zip/node_modules/@protobufjs/codegen/", + "packageDependencies": [ + ["@protobufjs/codegen", "npm:2.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/eventemitter", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@protobufjs-eventemitter-npm-1.1.0-029cc7d431-0369163a3d.zip/node_modules/@protobufjs/eventemitter/", + "packageDependencies": [ + ["@protobufjs/eventemitter", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/fetch", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@protobufjs-fetch-npm-1.1.0-ca857b7df4-3fce7e09eb.zip/node_modules/@protobufjs/fetch/", + "packageDependencies": [ + ["@protobufjs/fetch", "npm:1.1.0"], + ["@protobufjs/aspromise", "npm:1.1.2"], + ["@protobufjs/inquire", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/float", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/@protobufjs-float-npm-1.0.2-5678f64d08-5781e12412.zip/node_modules/@protobufjs/float/", + "packageDependencies": [ + ["@protobufjs/float", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/inquire", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@protobufjs-inquire-npm-1.1.0-3c7759e9ce-ca06f02eaf.zip/node_modules/@protobufjs/inquire/", + "packageDependencies": [ + ["@protobufjs/inquire", "npm:1.1.0"], + ["long", "npm:5.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/path", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/@protobufjs-path-npm-1.1.2-641d08de76-856eeb532b.zip/node_modules/@protobufjs/path/", + "packageDependencies": [ + ["@protobufjs/path", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/pool", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@protobufjs-pool-npm-1.1.0-47a76f96a1-d6a34fbbd2.zip/node_modules/@protobufjs/pool/", + "packageDependencies": [ + ["@protobufjs/pool", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/utf8", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@protobufjs-utf8-npm-1.1.0-02c590807c-f9bf3163d1.zip/node_modules/@protobufjs/utf8/", + "packageDependencies": [ + ["@protobufjs/utf8", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["@sindresorhus/is", [ + ["npm:0.14.0", { + "packageLocation": "./.yarn/cache/@sindresorhus-is-npm-0.14.0-9f906ea34b-971e0441dd.zip/node_modules/@sindresorhus/is/", + "packageDependencies": [ + ["@sindresorhus/is", "npm:0.14.0"] + ], + "linkType": "HARD", + }] + ]], + ["@sinonjs/commons", [ + ["npm:1.8.3", { + "packageLocation": "./.yarn/cache/@sinonjs-commons-npm-1.8.3-30cf78d93f-6159726db5.zip/node_modules/@sinonjs/commons/", + "packageDependencies": [ + ["@sinonjs/commons", "npm:1.8.3"], + ["type-detect", "npm:4.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["@sinonjs/fake-timers", [ + ["npm:7.1.2", { + "packageLocation": "./.yarn/cache/@sinonjs-fake-timers-npm-7.1.2-2a6b119ac7-c84773d797.zip/node_modules/@sinonjs/fake-timers/", + "packageDependencies": [ + ["@sinonjs/fake-timers", "npm:7.1.2"], + ["@sinonjs/commons", "npm:1.8.3"] + ], + "linkType": "HARD", + }] + ]], + ["@sinonjs/samsam", [ + ["npm:6.0.2", { + "packageLocation": "./.yarn/cache/@sinonjs-samsam-npm-6.0.2-5e8e8897e2-bc1514edf1.zip/node_modules/@sinonjs/samsam/", + "packageDependencies": [ + ["@sinonjs/samsam", "npm:6.0.2"], + ["@sinonjs/commons", "npm:1.8.3"], + ["lodash.get", "npm:4.4.2"], + ["type-detect", "npm:4.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["@sinonjs/text-encoding", [ + ["npm:0.7.1", { + "packageLocation": "./.yarn/cache/@sinonjs-text-encoding-npm-0.7.1-865b0079b5-130de0bb56.zip/node_modules/@sinonjs/text-encoding/", + "packageDependencies": [ + ["@sinonjs/text-encoding", "npm:0.7.1"] + ], + "linkType": "HARD", + }] + ]], + ["@szmarczak/http-timer", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/@szmarczak-http-timer-npm-1.1.2-ea82ca2d55-4d9158061c.zip/node_modules/@szmarczak/http-timer/", + "packageDependencies": [ + ["@szmarczak/http-timer", "npm:1.1.2"], + ["defer-to-connect", "npm:1.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["@tootallnate/once", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/@tootallnate-once-npm-1.1.2-0517220057-e1fb1bbbc1.zip/node_modules/@tootallnate/once/", + "packageDependencies": [ + ["@tootallnate/once", "npm:1.1.2"] + ], + "linkType": "HARD", + }], + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/@tootallnate-once-npm-2.0.0-e36cf4f140-ad87447820.zip/node_modules/@tootallnate/once/", + "packageDependencies": [ + ["@tootallnate/once", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@tsconfig/node10", [ + ["npm:1.0.8", { + "packageLocation": "./.yarn/cache/@tsconfig-node10-npm-1.0.8-90a8cce25d-b8d5fffbc6.zip/node_modules/@tsconfig/node10/", + "packageDependencies": [ + ["@tsconfig/node10", "npm:1.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["@tsconfig/node12", [ + ["npm:1.0.9", { + "packageLocation": "./.yarn/cache/@tsconfig-node12-npm-1.0.9-780563856d-a01b2400ab.zip/node_modules/@tsconfig/node12/", + "packageDependencies": [ + ["@tsconfig/node12", "npm:1.0.9"] + ], + "linkType": "HARD", + }] + ]], + ["@tsconfig/node14", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/@tsconfig-node14-npm-1.0.1-3ecac58e68-976345e896.zip/node_modules/@tsconfig/node14/", + "packageDependencies": [ + ["@tsconfig/node14", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["@tsconfig/node16", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/@tsconfig-node16-npm-1.0.2-1f43ab567a-ca94d36397.zip/node_modules/@tsconfig/node16/", + "packageDependencies": [ + ["@tsconfig/node16", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["@types/chai", [ + ["npm:4.2.22", { + "packageLocation": "./.yarn/cache/@types-chai-npm-4.2.22-557883092e-dca66a263b.zip/node_modules/@types/chai/", + "packageDependencies": [ + ["@types/chai", "npm:4.2.22"] + ], + "linkType": "HARD", + }] + ]], + ["@types/chai-as-promised", [ + ["npm:7.1.4", { + "packageLocation": "./.yarn/cache/@types-chai-as-promised-npm-7.1.4-0ab573c373-bb974e77e0.zip/node_modules/@types/chai-as-promised/", + "packageDependencies": [ + ["@types/chai-as-promised", "npm:7.1.4"], + ["@types/chai", "npm:4.2.22"] + ], + "linkType": "HARD", + }] + ]], + ["@types/component-emitter", [ + ["npm:1.2.11", { + "packageLocation": "./.yarn/cache/@types-component-emitter-npm-1.2.11-581f0366a3-0e081c5f7a.zip/node_modules/@types/component-emitter/", + "packageDependencies": [ + ["@types/component-emitter", "npm:1.2.11"] + ], + "linkType": "HARD", + }] + ]], + ["@types/connect", [ + ["npm:3.4.35", { + "packageLocation": "./.yarn/cache/@types-connect-npm-3.4.35-7337eee0a3-fe81351470.zip/node_modules/@types/connect/", + "packageDependencies": [ + ["@types/connect", "npm:3.4.35"], + ["@types/node", "npm:17.0.21"] + ], + "linkType": "HARD", + }] + ]], + ["@types/cookie", [ + ["npm:0.4.1", { + "packageLocation": "./.yarn/cache/@types-cookie-npm-0.4.1-274a704dc6-3275534ed6.zip/node_modules/@types/cookie/", + "packageDependencies": [ + ["@types/cookie", "npm:0.4.1"] + ], + "linkType": "HARD", + }] + ]], + ["@types/cors", [ + ["npm:2.8.12", { + "packageLocation": "./.yarn/cache/@types-cors-npm-2.8.12-ff52e8e514-8c45f112c7.zip/node_modules/@types/cors/", + "packageDependencies": [ + ["@types/cors", "npm:2.8.12"] + ], + "linkType": "HARD", + }] + ]], + ["@types/dirty-chai", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/@types-dirty-chai-npm-2.0.2-440bf7c05c-6015689ef7.zip/node_modules/@types/dirty-chai/", + "packageDependencies": [ + ["@types/dirty-chai", "npm:2.0.2"], + ["@types/chai", "npm:4.2.22"], + ["@types/chai-as-promised", "npm:7.1.4"] + ], + "linkType": "HARD", + }] + ]], + ["@types/eslint", [ + ["npm:8.2.0", { + "packageLocation": "./.yarn/cache/@types-eslint-npm-8.2.0-971aa21b00-18f37790af.zip/node_modules/@types/eslint/", + "packageDependencies": [ + ["@types/eslint", "npm:8.2.0"], + ["@types/estree", "npm:0.0.50"], + ["@types/json-schema", "npm:7.0.9"] + ], + "linkType": "HARD", + }] + ]], + ["@types/eslint-scope", [ + ["npm:3.7.1", { + "packageLocation": "./.yarn/cache/@types-eslint-scope-npm-3.7.1-8d60f27ad9-4271c9adad.zip/node_modules/@types/eslint-scope/", + "packageDependencies": [ + ["@types/eslint-scope", "npm:3.7.1"], + ["@types/eslint", "npm:8.2.0"], + ["@types/estree", "npm:0.0.50"] + ], + "linkType": "HARD", + }] + ]], + ["@types/estree", [ + ["npm:0.0.50", { + "packageLocation": "./.yarn/cache/@types-estree-npm-0.0.50-b9bc3b8409-9a2b6a4a8c.zip/node_modules/@types/estree/", + "packageDependencies": [ + ["@types/estree", "npm:0.0.50"] + ], + "linkType": "HARD", + }] + ]], + ["@types/expect", [ + ["npm:1.20.4", { + "packageLocation": "./.yarn/cache/@types-expect-npm-1.20.4-9b033f86cb-c09a9abec2.zip/node_modules/@types/expect/", + "packageDependencies": [ + ["@types/expect", "npm:1.20.4"] + ], + "linkType": "HARD", + }], + ["npm:24.3.0", { + "packageLocation": "./.yarn/cache/@types-expect-npm-24.3.0-dc41523666-8d017b49b1.zip/node_modules/@types/expect/", + "packageDependencies": [ + ["@types/expect", "npm:24.3.0"], + ["expect", "npm:27.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["@types/express-serve-static-core", [ + ["npm:4.17.25", { + "packageLocation": "./.yarn/cache/@types-express-serve-static-core-npm-4.17.25-77a729b982-a60d44676d.zip/node_modules/@types/express-serve-static-core/", + "packageDependencies": [ + ["@types/express-serve-static-core", "npm:4.17.25"], + ["@types/node", "npm:17.0.21"], + ["@types/qs", "npm:6.9.7"], + ["@types/range-parser", "npm:1.2.4"] + ], + "linkType": "HARD", + }] + ]], + ["@types/glob", [ + ["npm:7.2.0", { + "packageLocation": "./.yarn/cache/@types-glob-npm-7.2.0-772334bf9a-6ae717fedf.zip/node_modules/@types/glob/", + "packageDependencies": [ + ["@types/glob", "npm:7.2.0"], + ["@types/minimatch", "npm:3.0.5"], + ["@types/node", "npm:17.0.21"] + ], + "linkType": "HARD", + }] + ]], + ["@types/istanbul-lib-coverage", [ + ["npm:2.0.3", { + "packageLocation": "./.yarn/cache/@types-istanbul-lib-coverage-npm-2.0.3-67a37eb00a-0650cba4be.zip/node_modules/@types/istanbul-lib-coverage/", + "packageDependencies": [ + ["@types/istanbul-lib-coverage", "npm:2.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["@types/istanbul-lib-report", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/@types-istanbul-lib-report-npm-3.0.0-50de3e6b3b-656398b62d.zip/node_modules/@types/istanbul-lib-report/", + "packageDependencies": [ + ["@types/istanbul-lib-report", "npm:3.0.0"], + ["@types/istanbul-lib-coverage", "npm:2.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["@types/istanbul-reports", [ + ["npm:3.0.1", { + "packageLocation": "./.yarn/cache/@types-istanbul-reports-npm-3.0.1-770e825002-f1ad54bc68.zip/node_modules/@types/istanbul-reports/", + "packageDependencies": [ + ["@types/istanbul-reports", "npm:3.0.1"], + ["@types/istanbul-lib-report", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@types/json-schema", [ + ["npm:7.0.9", { + "packageLocation": "./.yarn/cache/@types-json-schema-npm-7.0.9-361918cff3-259d0e25f1.zip/node_modules/@types/json-schema/", + "packageDependencies": [ + ["@types/json-schema", "npm:7.0.9"] + ], + "linkType": "HARD", + }] + ]], + ["@types/json5", [ + ["npm:0.0.29", { + "packageLocation": "./.yarn/cache/@types-json5-npm-0.0.29-f63a7916bd-e60b153664.zip/node_modules/@types/json5/", + "packageDependencies": [ + ["@types/json5", "npm:0.0.29"] + ], + "linkType": "HARD", + }] + ]], + ["@types/keyv", [ + ["npm:3.1.3", { + "packageLocation": "./.yarn/cache/@types-keyv-npm-3.1.3-8864e3cbf3-b5f8aa592c.zip/node_modules/@types/keyv/", + "packageDependencies": [ + ["@types/keyv", "npm:3.1.3"], + ["@types/node", "npm:17.0.21"] + ], + "linkType": "HARD", + }] + ]], + ["@types/lodash", [ + ["npm:4.14.177", { + "packageLocation": "./.yarn/cache/@types-lodash-npm-4.14.177-a28410b30a-00f9eb300e.zip/node_modules/@types/lodash/", + "packageDependencies": [ + ["@types/lodash", "npm:4.14.177"] + ], + "linkType": "HARD", + }] + ]], + ["@types/long", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/@types-long-npm-4.0.1-022c8b6e77-ff9653c33f.zip/node_modules/@types/long/", + "packageDependencies": [ + ["@types/long", "npm:4.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["@types/minimatch", [ + ["npm:3.0.5", { + "packageLocation": "./.yarn/cache/@types-minimatch-npm-3.0.5-802bb0797f-c41d136f67.zip/node_modules/@types/minimatch/", + "packageDependencies": [ + ["@types/minimatch", "npm:3.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["@types/minimist", [ + ["npm:1.2.2", { + "packageLocation": "./.yarn/cache/@types-minimist-npm-1.2.2-a445de65da-b8da83c66e.zip/node_modules/@types/minimist/", + "packageDependencies": [ + ["@types/minimist", "npm:1.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["@types/mocha", [ + ["npm:8.2.3", { + "packageLocation": "./.yarn/cache/@types-mocha-npm-8.2.3-7aff51fdb4-b43ed1b642.zip/node_modules/@types/mocha/", + "packageDependencies": [ + ["@types/mocha", "npm:8.2.3"] + ], + "linkType": "HARD", + }] + ]], + ["@types/node", [ + ["npm:10.17.60", { + "packageLocation": "./.yarn/cache/@types-node-npm-10.17.60-63ac1f669f-2cdb3a77d0.zip/node_modules/@types/node/", + "packageDependencies": [ + ["@types/node", "npm:10.17.60"] + ], + "linkType": "HARD", + }], + ["npm:12.20.37", { + "packageLocation": "./.yarn/cache/@types-node-npm-12.20.37-9ce6eac5c0-8c8b12f802.zip/node_modules/@types/node/", + "packageDependencies": [ + ["@types/node", "npm:12.20.37"] + ], + "linkType": "HARD", + }], + ["npm:13.13.52", { + "packageLocation": "./.yarn/cache/@types-node-npm-13.13.52-95159539bb-8f1afff497.zip/node_modules/@types/node/", + "packageDependencies": [ + ["@types/node", "npm:13.13.52"] + ], + "linkType": "HARD", + }], + ["npm:14.17.34", { + "packageLocation": "./.yarn/cache/@types-node-npm-14.17.34-1d7f20f643-803a7532b6.zip/node_modules/@types/node/", + "packageDependencies": [ + ["@types/node", "npm:14.17.34"] + ], + "linkType": "HARD", + }], + ["npm:15.14.9", { + "packageLocation": "./.yarn/cache/@types-node-npm-15.14.9-739a59edff-49f7f0522a.zip/node_modules/@types/node/", + "packageDependencies": [ + ["@types/node", "npm:15.14.9"] + ], + "linkType": "HARD", + }], + ["npm:17.0.21", { + "packageLocation": "./.yarn/cache/@types-node-npm-17.0.21-7d68eb6a13-89dcd2fe82.zip/node_modules/@types/node/", + "packageDependencies": [ + ["@types/node", "npm:17.0.21"] + ], + "linkType": "HARD", + }] + ]], + ["@types/normalize-package-data", [ + ["npm:2.4.1", { + "packageLocation": "./.yarn/cache/@types-normalize-package-data-npm-2.4.1-c31c56ae6a-e87bccbf11.zip/node_modules/@types/normalize-package-data/", + "packageDependencies": [ + ["@types/normalize-package-data", "npm:2.4.1"] + ], + "linkType": "HARD", + }] + ]], + ["@types/pino", [ + ["npm:6.3.12", { + "packageLocation": "./.yarn/cache/@types-pino-npm-6.3.12-19c7982858-8017351466.zip/node_modules/@types/pino/", + "packageDependencies": [ + ["@types/pino", "npm:6.3.12"], + ["@types/node", "npm:17.0.21"], + ["@types/pino-pretty", "npm:4.7.3"], + ["@types/pino-std-serializers", "npm:2.4.1"], + ["sonic-boom", "npm:2.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["@types/pino-pretty", [ + ["npm:4.7.3", { + "packageLocation": "./.yarn/cache/@types-pino-pretty-npm-4.7.3-5ebf57cfd2-40fe67e73d.zip/node_modules/@types/pino-pretty/", + "packageDependencies": [ + ["@types/pino-pretty", "npm:4.7.3"], + ["@types/node", "npm:17.0.21"], + ["@types/pino", "npm:6.3.12"] + ], + "linkType": "HARD", + }] + ]], + ["@types/pino-std-serializers", [ + ["npm:2.4.1", { + "packageLocation": "./.yarn/cache/@types-pino-std-serializers-npm-2.4.1-e7c36178c0-a156e25882.zip/node_modules/@types/pino-std-serializers/", + "packageDependencies": [ + ["@types/pino-std-serializers", "npm:2.4.1"], + ["@types/node", "npm:17.0.21"] + ], + "linkType": "HARD", + }] + ]], + ["@types/qs", [ + ["npm:6.9.7", { + "packageLocation": "./.yarn/cache/@types-qs-npm-6.9.7-4a3e6ca0d0-7fd6f9c250.zip/node_modules/@types/qs/", + "packageDependencies": [ + ["@types/qs", "npm:6.9.7"] + ], + "linkType": "HARD", + }] + ]], + ["@types/range-parser", [ + ["npm:1.2.4", { + "packageLocation": "./.yarn/cache/@types-range-parser-npm-1.2.4-23d797fbde-b7c0dfd508.zip/node_modules/@types/range-parser/", + "packageDependencies": [ + ["@types/range-parser", "npm:1.2.4"] + ], + "linkType": "HARD", + }] + ]], + ["@types/responselike", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/@types-responselike-npm-1.0.0-85dd08af42-e99fc7cc62.zip/node_modules/@types/responselike/", + "packageDependencies": [ + ["@types/responselike", "npm:1.0.0"], + ["@types/node", "npm:17.0.21"] + ], + "linkType": "HARD", + }] + ]], + ["@types/sinon", [ + ["npm:10.0.6", { + "packageLocation": "./.yarn/cache/@types-sinon-npm-10.0.6-3a1b027ac2-1c2ae7daa8.zip/node_modules/@types/sinon/", + "packageDependencies": [ + ["@types/sinon", "npm:10.0.6"], + ["@sinonjs/fake-timers", "npm:7.1.2"] + ], + "linkType": "HARD", + }], + ["npm:9.0.11", { + "packageLocation": "./.yarn/cache/@types-sinon-npm-9.0.11-231734b808-2074490973.zip/node_modules/@types/sinon/", + "packageDependencies": [ + ["@types/sinon", "npm:9.0.11"], + ["@types/sinonjs__fake-timers", "npm:8.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["@types/sinon-chai", [ + ["npm:3.2.5", { + "packageLocation": "./.yarn/cache/@types-sinon-chai-npm-3.2.5-1d6490532a-ac332b8f2c.zip/node_modules/@types/sinon-chai/", + "packageDependencies": [ + ["@types/sinon-chai", "npm:3.2.5"], + ["@types/chai", "npm:4.2.22"], + ["@types/sinon", "npm:10.0.6"] + ], + "linkType": "HARD", + }] + ]], + ["@types/sinonjs__fake-timers", [ + ["npm:8.1.0", { + "packageLocation": "./.yarn/cache/@types-sinonjs__fake-timers-npm-8.1.0-b26c9e7f56-02d8f5a2c8.zip/node_modules/@types/sinonjs__fake-timers/", + "packageDependencies": [ + ["@types/sinonjs__fake-timers", "npm:8.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["@types/stack-utils", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/@types-stack-utils-npm-2.0.1-867718ab70-205fdbe332.zip/node_modules/@types/stack-utils/", + "packageDependencies": [ + ["@types/stack-utils", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["@types/vinyl", [ + ["npm:2.0.6", { + "packageLocation": "./.yarn/cache/@types-vinyl-npm-2.0.6-62fe43810b-5012fb61e3.zip/node_modules/@types/vinyl/", + "packageDependencies": [ + ["@types/vinyl", "npm:2.0.6"], + ["@types/expect", "npm:1.20.4"], + ["@types/node", "npm:17.0.21"] + ], + "linkType": "HARD", + }] + ]], + ["@types/ws", [ + ["npm:7.4.7", { + "packageLocation": "./.yarn/cache/@types-ws-npm-7.4.7-d0c95c0958-b4c9b8ad20.zip/node_modules/@types/ws/", + "packageDependencies": [ + ["@types/ws", "npm:7.4.7"], + ["@types/node", "npm:17.0.21"] + ], + "linkType": "HARD", + }] + ]], + ["@types/yargs", [ + ["npm:16.0.4", { + "packageLocation": "./.yarn/cache/@types-yargs-npm-16.0.4-7aaef7d6c8-caa21d2c95.zip/node_modules/@types/yargs/", + "packageDependencies": [ + ["@types/yargs", "npm:16.0.4"], + ["@types/yargs-parser", "npm:20.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["@types/yargs-parser", [ + ["npm:20.2.1", { + "packageLocation": "./.yarn/cache/@types-yargs-parser-npm-20.2.1-2eed5b5c1c-1d039e6449.zip/node_modules/@types/yargs-parser/", + "packageDependencies": [ + ["@types/yargs-parser", "npm:20.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["@ungap/promise-all-settled", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/@ungap-promise-all-settled-npm-1.1.2-c0f42e147b-08d37fdfa2.zip/node_modules/@ungap/promise-all-settled/", + "packageDependencies": [ + ["@ungap/promise-all-settled", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/ast", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-ast-npm-1.11.1-623d3d973e-1eee1534ad.zip/node_modules/@webassemblyjs/ast/", + "packageDependencies": [ + ["@webassemblyjs/ast", "npm:1.11.1"], + ["@webassemblyjs/helper-numbers", "npm:1.11.1"], + ["@webassemblyjs/helper-wasm-bytecode", "npm:1.11.1"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/floating-point-hex-parser", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-floating-point-hex-parser-npm-1.11.1-f8af5c0037-b8efc6fa08.zip/node_modules/@webassemblyjs/floating-point-hex-parser/", + "packageDependencies": [ + ["@webassemblyjs/floating-point-hex-parser", "npm:1.11.1"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/helper-api-error", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-helper-api-error-npm-1.11.1-b839d59053-0792813f0e.zip/node_modules/@webassemblyjs/helper-api-error/", + "packageDependencies": [ + ["@webassemblyjs/helper-api-error", "npm:1.11.1"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/helper-buffer", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-helper-buffer-npm-1.11.1-6afb1ef4aa-a337ee44b4.zip/node_modules/@webassemblyjs/helper-buffer/", + "packageDependencies": [ + ["@webassemblyjs/helper-buffer", "npm:1.11.1"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/helper-numbers", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-helper-numbers-npm-1.11.1-a41f7439eb-44d2905dac.zip/node_modules/@webassemblyjs/helper-numbers/", + "packageDependencies": [ + ["@webassemblyjs/helper-numbers", "npm:1.11.1"], + ["@webassemblyjs/floating-point-hex-parser", "npm:1.11.1"], + ["@webassemblyjs/helper-api-error", "npm:1.11.1"], + ["@xtuc/long", "npm:4.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/helper-wasm-bytecode", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-helper-wasm-bytecode-npm-1.11.1-84f0ee4c30-eac4001131.zip/node_modules/@webassemblyjs/helper-wasm-bytecode/", + "packageDependencies": [ + ["@webassemblyjs/helper-wasm-bytecode", "npm:1.11.1"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/helper-wasm-section", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-helper-wasm-section-npm-1.11.1-e4e8450b9d-617696cfe8.zip/node_modules/@webassemblyjs/helper-wasm-section/", + "packageDependencies": [ + ["@webassemblyjs/helper-wasm-section", "npm:1.11.1"], + ["@webassemblyjs/ast", "npm:1.11.1"], + ["@webassemblyjs/helper-buffer", "npm:1.11.1"], + ["@webassemblyjs/helper-wasm-bytecode", "npm:1.11.1"], + ["@webassemblyjs/wasm-gen", "npm:1.11.1"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/ieee754", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-ieee754-npm-1.11.1-897eb85879-23a0ac02a5.zip/node_modules/@webassemblyjs/ieee754/", + "packageDependencies": [ + ["@webassemblyjs/ieee754", "npm:1.11.1"], + ["@xtuc/ieee754", "npm:1.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/leb128", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-leb128-npm-1.11.1-fd9f27673d-33ccc4ade2.zip/node_modules/@webassemblyjs/leb128/", + "packageDependencies": [ + ["@webassemblyjs/leb128", "npm:1.11.1"], + ["@xtuc/long", "npm:4.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/utf8", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-utf8-npm-1.11.1-583036e767-972c5cfc76.zip/node_modules/@webassemblyjs/utf8/", + "packageDependencies": [ + ["@webassemblyjs/utf8", "npm:1.11.1"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/wasm-edit", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-wasm-edit-npm-1.11.1-34565c1e92-6d7d9efaec.zip/node_modules/@webassemblyjs/wasm-edit/", + "packageDependencies": [ + ["@webassemblyjs/wasm-edit", "npm:1.11.1"], + ["@webassemblyjs/ast", "npm:1.11.1"], + ["@webassemblyjs/helper-buffer", "npm:1.11.1"], + ["@webassemblyjs/helper-wasm-bytecode", "npm:1.11.1"], + ["@webassemblyjs/helper-wasm-section", "npm:1.11.1"], + ["@webassemblyjs/wasm-gen", "npm:1.11.1"], + ["@webassemblyjs/wasm-opt", "npm:1.11.1"], + ["@webassemblyjs/wasm-parser", "npm:1.11.1"], + ["@webassemblyjs/wast-printer", "npm:1.11.1"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/wasm-gen", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-wasm-gen-npm-1.11.1-a6d0b4d37d-1f6921e640.zip/node_modules/@webassemblyjs/wasm-gen/", + "packageDependencies": [ + ["@webassemblyjs/wasm-gen", "npm:1.11.1"], + ["@webassemblyjs/ast", "npm:1.11.1"], + ["@webassemblyjs/helper-wasm-bytecode", "npm:1.11.1"], + ["@webassemblyjs/ieee754", "npm:1.11.1"], + ["@webassemblyjs/leb128", "npm:1.11.1"], + ["@webassemblyjs/utf8", "npm:1.11.1"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/wasm-opt", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-wasm-opt-npm-1.11.1-0bb73c20b9-21586883a2.zip/node_modules/@webassemblyjs/wasm-opt/", + "packageDependencies": [ + ["@webassemblyjs/wasm-opt", "npm:1.11.1"], + ["@webassemblyjs/ast", "npm:1.11.1"], + ["@webassemblyjs/helper-buffer", "npm:1.11.1"], + ["@webassemblyjs/wasm-gen", "npm:1.11.1"], + ["@webassemblyjs/wasm-parser", "npm:1.11.1"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/wasm-parser", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-wasm-parser-npm-1.11.1-cd49c51fdc-1521644065.zip/node_modules/@webassemblyjs/wasm-parser/", + "packageDependencies": [ + ["@webassemblyjs/wasm-parser", "npm:1.11.1"], + ["@webassemblyjs/ast", "npm:1.11.1"], + ["@webassemblyjs/helper-api-error", "npm:1.11.1"], + ["@webassemblyjs/helper-wasm-bytecode", "npm:1.11.1"], + ["@webassemblyjs/ieee754", "npm:1.11.1"], + ["@webassemblyjs/leb128", "npm:1.11.1"], + ["@webassemblyjs/utf8", "npm:1.11.1"] + ], + "linkType": "HARD", + }] + ]], + ["@webassemblyjs/wast-printer", [ + ["npm:1.11.1", { + "packageLocation": "./.yarn/cache/@webassemblyjs-wast-printer-npm-1.11.1-f1213430d6-f15ae4c244.zip/node_modules/@webassemblyjs/wast-printer/", + "packageDependencies": [ + ["@webassemblyjs/wast-printer", "npm:1.11.1"], + ["@webassemblyjs/ast", "npm:1.11.1"], + ["@xtuc/long", "npm:4.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["@webpack-cli/configtest", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@webpack-cli-configtest-npm-1.1.0-2b6b2ef3d7-69e7816b5b.zip/node_modules/@webpack-cli/configtest/", + "packageDependencies": [ + ["@webpack-cli/configtest", "npm:1.1.0"] + ], + "linkType": "SOFT", + }], + ["virtual:0249f7ceb5542d6b732af2b44f9fcd16c60be8b8440f0f3abc6a5de67aabcff731bc3bc83f3067ab2f9037661176f001f89208fcea9e8962835fd43d0aabe88a#npm:1.1.0", { + "packageLocation": "./.yarn/__virtual__/@webpack-cli-configtest-virtual-7a30f6ad18/0/cache/@webpack-cli-configtest-npm-1.1.0-2b6b2ef3d7-69e7816b5b.zip/node_modules/@webpack-cli/configtest/", + "packageDependencies": [ + ["@webpack-cli/configtest", "virtual:0249f7ceb5542d6b732af2b44f9fcd16c60be8b8440f0f3abc6a5de67aabcff731bc3bc83f3067ab2f9037661176f001f89208fcea9e8962835fd43d0aabe88a#npm:1.1.0"], + ["@types/webpack", null], + ["@types/webpack-cli", null], + ["webpack", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:5.64.1"], + ["webpack-cli", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:4.9.1"] + ], + "packagePeers": [ + "@types/webpack-cli", + "@types/webpack", + "webpack-cli", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:7fc88da9d00679384dc013444a3b1ed8ef8395fcad9d046790a1851d5db985e5ee052061731f87c5475e4bf20a92d69ea1a1a287c0d76d7b1a6bf97010c63532#npm:1.1.0", { + "packageLocation": "./.yarn/__virtual__/@webpack-cli-configtest-virtual-4b48f64ff3/0/cache/@webpack-cli-configtest-npm-1.1.0-2b6b2ef3d7-69e7816b5b.zip/node_modules/@webpack-cli/configtest/", + "packageDependencies": [ + ["@webpack-cli/configtest", "virtual:7fc88da9d00679384dc013444a3b1ed8ef8395fcad9d046790a1851d5db985e5ee052061731f87c5475e4bf20a92d69ea1a1a287c0d76d7b1a6bf97010c63532#npm:1.1.0"], + ["@types/webpack", null], + ["@types/webpack-cli", null], + ["webpack", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.64.1"], + ["webpack-cli", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:4.9.1"] + ], + "packagePeers": [ + "@types/webpack-cli", + "@types/webpack", + "webpack-cli", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:933f3c2d7f6a5dac21dc52727e214cdc1fcf36d71628fb136e96267112c54b183dd48537afe26d9bffb18203e9028c2bf712344b29e251121cd4c374fbb4e51a#npm:1.1.0", { + "packageLocation": "./.yarn/__virtual__/@webpack-cli-configtest-virtual-b11143e5d6/0/cache/@webpack-cli-configtest-npm-1.1.0-2b6b2ef3d7-69e7816b5b.zip/node_modules/@webpack-cli/configtest/", + "packageDependencies": [ + ["@webpack-cli/configtest", "virtual:933f3c2d7f6a5dac21dc52727e214cdc1fcf36d71628fb136e96267112c54b183dd48537afe26d9bffb18203e9028c2bf712344b29e251121cd4c374fbb4e51a#npm:1.1.0"], + ["@types/webpack", null], + ["@types/webpack-cli", null], + ["webpack", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:5.64.1"], + ["webpack-cli", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:4.9.1"] + ], + "packagePeers": [ + "@types/webpack-cli", + "@types/webpack", + "webpack-cli", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:b37ef7cf98ceabe8c7b789a7db3f0a5f3444d083afa5f0e3ab570292e74eff241f890fadbf245a134b2ebfcba326b1782124a4dd4f16ca7cdb6091dd9a987c04#npm:1.1.0", { + "packageLocation": "./.yarn/__virtual__/@webpack-cli-configtest-virtual-34b876bdf7/0/cache/@webpack-cli-configtest-npm-1.1.0-2b6b2ef3d7-69e7816b5b.zip/node_modules/@webpack-cli/configtest/", + "packageDependencies": [ + ["@webpack-cli/configtest", "virtual:b37ef7cf98ceabe8c7b789a7db3f0a5f3444d083afa5f0e3ab570292e74eff241f890fadbf245a134b2ebfcba326b1782124a4dd4f16ca7cdb6091dd9a987c04#npm:1.1.0"], + ["@types/webpack", null], + ["@types/webpack-cli", null], + ["webpack", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:5.64.1"], + ["webpack-cli", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:4.9.1"] + ], + "packagePeers": [ + "@types/webpack-cli", + "@types/webpack", + "webpack-cli", + "webpack" + ], + "linkType": "HARD", + }] + ]], + ["@webpack-cli/info", [ + ["npm:1.4.0", { + "packageLocation": "./.yarn/cache/@webpack-cli-info-npm-1.4.0-4a26ccee64-6385b1e2c5.zip/node_modules/@webpack-cli/info/", + "packageDependencies": [ + ["@webpack-cli/info", "npm:1.4.0"] + ], + "linkType": "SOFT", + }], + ["virtual:0249f7ceb5542d6b732af2b44f9fcd16c60be8b8440f0f3abc6a5de67aabcff731bc3bc83f3067ab2f9037661176f001f89208fcea9e8962835fd43d0aabe88a#npm:1.4.0", { + "packageLocation": "./.yarn/__virtual__/@webpack-cli-info-virtual-b6b54dc15e/0/cache/@webpack-cli-info-npm-1.4.0-4a26ccee64-6385b1e2c5.zip/node_modules/@webpack-cli/info/", + "packageDependencies": [ + ["@webpack-cli/info", "virtual:0249f7ceb5542d6b732af2b44f9fcd16c60be8b8440f0f3abc6a5de67aabcff731bc3bc83f3067ab2f9037661176f001f89208fcea9e8962835fd43d0aabe88a#npm:1.4.0"], + ["@types/webpack-cli", null], + ["envinfo", "npm:7.8.1"], + ["webpack-cli", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:4.9.1"] + ], + "packagePeers": [ + "@types/webpack-cli", + "webpack-cli" + ], + "linkType": "HARD", + }], + ["virtual:7fc88da9d00679384dc013444a3b1ed8ef8395fcad9d046790a1851d5db985e5ee052061731f87c5475e4bf20a92d69ea1a1a287c0d76d7b1a6bf97010c63532#npm:1.4.0", { + "packageLocation": "./.yarn/__virtual__/@webpack-cli-info-virtual-c05860be1d/0/cache/@webpack-cli-info-npm-1.4.0-4a26ccee64-6385b1e2c5.zip/node_modules/@webpack-cli/info/", + "packageDependencies": [ + ["@webpack-cli/info", "virtual:7fc88da9d00679384dc013444a3b1ed8ef8395fcad9d046790a1851d5db985e5ee052061731f87c5475e4bf20a92d69ea1a1a287c0d76d7b1a6bf97010c63532#npm:1.4.0"], + ["@types/webpack-cli", null], + ["envinfo", "npm:7.8.1"], + ["webpack-cli", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:4.9.1"] + ], + "packagePeers": [ + "@types/webpack-cli", + "webpack-cli" + ], + "linkType": "HARD", + }], + ["virtual:933f3c2d7f6a5dac21dc52727e214cdc1fcf36d71628fb136e96267112c54b183dd48537afe26d9bffb18203e9028c2bf712344b29e251121cd4c374fbb4e51a#npm:1.4.0", { + "packageLocation": "./.yarn/__virtual__/@webpack-cli-info-virtual-045782f8e6/0/cache/@webpack-cli-info-npm-1.4.0-4a26ccee64-6385b1e2c5.zip/node_modules/@webpack-cli/info/", + "packageDependencies": [ + ["@webpack-cli/info", "virtual:933f3c2d7f6a5dac21dc52727e214cdc1fcf36d71628fb136e96267112c54b183dd48537afe26d9bffb18203e9028c2bf712344b29e251121cd4c374fbb4e51a#npm:1.4.0"], + ["@types/webpack-cli", null], + ["envinfo", "npm:7.8.1"], + ["webpack-cli", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:4.9.1"] + ], + "packagePeers": [ + "@types/webpack-cli", + "webpack-cli" + ], + "linkType": "HARD", + }], + ["virtual:b37ef7cf98ceabe8c7b789a7db3f0a5f3444d083afa5f0e3ab570292e74eff241f890fadbf245a134b2ebfcba326b1782124a4dd4f16ca7cdb6091dd9a987c04#npm:1.4.0", { + "packageLocation": "./.yarn/__virtual__/@webpack-cli-info-virtual-5b3c564e68/0/cache/@webpack-cli-info-npm-1.4.0-4a26ccee64-6385b1e2c5.zip/node_modules/@webpack-cli/info/", + "packageDependencies": [ + ["@webpack-cli/info", "virtual:b37ef7cf98ceabe8c7b789a7db3f0a5f3444d083afa5f0e3ab570292e74eff241f890fadbf245a134b2ebfcba326b1782124a4dd4f16ca7cdb6091dd9a987c04#npm:1.4.0"], + ["@types/webpack-cli", null], + ["envinfo", "npm:7.8.1"], + ["webpack-cli", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:4.9.1"] + ], + "packagePeers": [ + "@types/webpack-cli", + "webpack-cli" + ], + "linkType": "HARD", + }] + ]], + ["@webpack-cli/serve", [ + ["npm:1.6.0", { + "packageLocation": "./.yarn/cache/@webpack-cli-serve-npm-1.6.0-c7b35aa4ef-050a930b63.zip/node_modules/@webpack-cli/serve/", + "packageDependencies": [ + ["@webpack-cli/serve", "npm:1.6.0"] + ], + "linkType": "SOFT", + }], + ["virtual:0249f7ceb5542d6b732af2b44f9fcd16c60be8b8440f0f3abc6a5de67aabcff731bc3bc83f3067ab2f9037661176f001f89208fcea9e8962835fd43d0aabe88a#npm:1.6.0", { + "packageLocation": "./.yarn/__virtual__/@webpack-cli-serve-virtual-5bd62e0e2f/0/cache/@webpack-cli-serve-npm-1.6.0-c7b35aa4ef-050a930b63.zip/node_modules/@webpack-cli/serve/", + "packageDependencies": [ + ["@webpack-cli/serve", "virtual:0249f7ceb5542d6b732af2b44f9fcd16c60be8b8440f0f3abc6a5de67aabcff731bc3bc83f3067ab2f9037661176f001f89208fcea9e8962835fd43d0aabe88a#npm:1.6.0"], + ["@types/webpack-cli", null], + ["@types/webpack-dev-server", null], + ["webpack-cli", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:4.9.1"], + ["webpack-dev-server", null] + ], + "packagePeers": [ + "@types/webpack-cli", + "@types/webpack-dev-server", + "webpack-cli", + "webpack-dev-server" + ], + "linkType": "HARD", + }], + ["virtual:7fc88da9d00679384dc013444a3b1ed8ef8395fcad9d046790a1851d5db985e5ee052061731f87c5475e4bf20a92d69ea1a1a287c0d76d7b1a6bf97010c63532#npm:1.6.0", { + "packageLocation": "./.yarn/__virtual__/@webpack-cli-serve-virtual-02793a9ef0/0/cache/@webpack-cli-serve-npm-1.6.0-c7b35aa4ef-050a930b63.zip/node_modules/@webpack-cli/serve/", + "packageDependencies": [ + ["@webpack-cli/serve", "virtual:7fc88da9d00679384dc013444a3b1ed8ef8395fcad9d046790a1851d5db985e5ee052061731f87c5475e4bf20a92d69ea1a1a287c0d76d7b1a6bf97010c63532#npm:1.6.0"], + ["@types/webpack-cli", null], + ["@types/webpack-dev-server", null], + ["webpack-cli", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:4.9.1"], + ["webpack-dev-server", null] + ], + "packagePeers": [ + "@types/webpack-cli", + "@types/webpack-dev-server", + "webpack-cli", + "webpack-dev-server" + ], + "linkType": "HARD", + }], + ["virtual:933f3c2d7f6a5dac21dc52727e214cdc1fcf36d71628fb136e96267112c54b183dd48537afe26d9bffb18203e9028c2bf712344b29e251121cd4c374fbb4e51a#npm:1.6.0", { + "packageLocation": "./.yarn/__virtual__/@webpack-cli-serve-virtual-cd4b24dba0/0/cache/@webpack-cli-serve-npm-1.6.0-c7b35aa4ef-050a930b63.zip/node_modules/@webpack-cli/serve/", + "packageDependencies": [ + ["@webpack-cli/serve", "virtual:933f3c2d7f6a5dac21dc52727e214cdc1fcf36d71628fb136e96267112c54b183dd48537afe26d9bffb18203e9028c2bf712344b29e251121cd4c374fbb4e51a#npm:1.6.0"], + ["@types/webpack-cli", null], + ["@types/webpack-dev-server", null], + ["webpack-cli", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:4.9.1"], + ["webpack-dev-server", null] + ], + "packagePeers": [ + "@types/webpack-cli", + "@types/webpack-dev-server", + "webpack-cli", + "webpack-dev-server" + ], + "linkType": "HARD", + }], + ["virtual:b37ef7cf98ceabe8c7b789a7db3f0a5f3444d083afa5f0e3ab570292e74eff241f890fadbf245a134b2ebfcba326b1782124a4dd4f16ca7cdb6091dd9a987c04#npm:1.6.0", { + "packageLocation": "./.yarn/__virtual__/@webpack-cli-serve-virtual-bcf913d932/0/cache/@webpack-cli-serve-npm-1.6.0-c7b35aa4ef-050a930b63.zip/node_modules/@webpack-cli/serve/", + "packageDependencies": [ + ["@webpack-cli/serve", "virtual:b37ef7cf98ceabe8c7b789a7db3f0a5f3444d083afa5f0e3ab570292e74eff241f890fadbf245a134b2ebfcba326b1782124a4dd4f16ca7cdb6091dd9a987c04#npm:1.6.0"], + ["@types/webpack-cli", null], + ["@types/webpack-dev-server", null], + ["webpack-cli", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:4.9.1"], + ["webpack-dev-server", null] + ], + "packagePeers": [ + "@types/webpack-cli", + "@types/webpack-dev-server", + "webpack-cli", + "webpack-dev-server" + ], + "linkType": "HARD", + }] + ]], + ["@xtuc/ieee754", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/@xtuc-ieee754-npm-1.2.0-ec0ce4e025-ac56d4ca6e.zip/node_modules/@xtuc/ieee754/", + "packageDependencies": [ + ["@xtuc/ieee754", "npm:1.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["@xtuc/long", [ + ["npm:4.2.2", { + "packageLocation": "./.yarn/cache/@xtuc-long-npm-4.2.2-37236e6d72-8ed0d477ce.zip/node_modules/@xtuc/long/", + "packageDependencies": [ + ["@xtuc/long", "npm:4.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["JSONStream", [ + ["npm:1.3.5", { + "packageLocation": "./.yarn/cache/JSONStream-npm-1.3.5-1987f2e6dd-2605fa1242.zip/node_modules/JSONStream/", + "packageDependencies": [ + ["JSONStream", "npm:1.3.5"], + ["jsonparse", "npm:1.3.1"], + ["through", "npm:2.3.8"] + ], + "linkType": "HARD", + }] + ]], + ["abbrev", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/abbrev-npm-1.1.1-3659247eab-a4a97ec07d.zip/node_modules/abbrev/", + "packageDependencies": [ + ["abbrev", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["abstract-leveldown", [ + ["npm:6.2.3", { + "packageLocation": "./.yarn/cache/abstract-leveldown-npm-6.2.3-73e4ffefa5-00202b2eb7.zip/node_modules/abstract-leveldown/", + "packageDependencies": [ + ["abstract-leveldown", "npm:6.2.3"], + ["buffer", "npm:5.7.1"], + ["immediate", "npm:3.3.0"], + ["level-concat-iterator", "npm:2.0.1"], + ["level-supports", "npm:1.0.1"], + ["xtend", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["accepts", [ + ["npm:1.3.7", { + "packageLocation": "./.yarn/cache/accepts-npm-1.3.7-0dc9de65aa-27fc8060ff.zip/node_modules/accepts/", + "packageDependencies": [ + ["accepts", "npm:1.3.7"], + ["mime-types", "npm:2.1.34"], + ["negotiator", "npm:0.6.2"] + ], + "linkType": "HARD", + }] + ]], + ["acorn", [ + ["npm:7.4.1", { + "packageLocation": "./.yarn/cache/acorn-npm-7.4.1-f450b4646c-1860f23c21.zip/node_modules/acorn/", + "packageDependencies": [ + ["acorn", "npm:7.4.1"] + ], + "linkType": "HARD", + }], + ["npm:8.6.0", { + "packageLocation": "./.yarn/cache/acorn-npm-8.6.0-9de50afc7d-9d0de73b73.zip/node_modules/acorn/", + "packageDependencies": [ + ["acorn", "npm:8.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["acorn-import-assertions", [ + ["npm:1.8.0", { + "packageLocation": "./.yarn/cache/acorn-import-assertions-npm-1.8.0-e9a9d57e27-5c4cf7c850.zip/node_modules/acorn-import-assertions/", + "packageDependencies": [ + ["acorn-import-assertions", "npm:1.8.0"] + ], + "linkType": "SOFT", + }], + ["virtual:74c6cfbee4804d578abc9f87850914089f1afe762b6ef9dde76ecf912446a2b7772b58b2a3d070d6a0e21b0f126bfcdfa013123d743c15f32eee4a9cf9f4d551#npm:1.8.0", { + "packageLocation": "./.yarn/__virtual__/acorn-import-assertions-virtual-f9765fa54f/0/cache/acorn-import-assertions-npm-1.8.0-e9a9d57e27-5c4cf7c850.zip/node_modules/acorn-import-assertions/", + "packageDependencies": [ + ["acorn-import-assertions", "virtual:74c6cfbee4804d578abc9f87850914089f1afe762b6ef9dde76ecf912446a2b7772b58b2a3d070d6a0e21b0f126bfcdfa013123d743c15f32eee4a9cf9f4d551#npm:1.8.0"], + ["@types/acorn", null], + ["acorn", "npm:8.6.0"] + ], + "packagePeers": [ + "@types/acorn", + "acorn" + ], + "linkType": "HARD", + }] + ]], + ["acorn-jsx", [ + ["npm:5.3.2", { + "packageLocation": "./.yarn/cache/acorn-jsx-npm-5.3.2-d7594599ea-c3d3b2a89c.zip/node_modules/acorn-jsx/", + "packageDependencies": [ + ["acorn-jsx", "npm:5.3.2"] + ], + "linkType": "SOFT", + }], + ["virtual:8d8ea5d1e3376905d0290522290f47c29213c64d936d96293d758a315829a3cf4c6a5b8ffc1cfee36c3db08f700ad3aaf0711cc5d406a7218c275de6d74effa9#npm:5.3.2", { + "packageLocation": "./.yarn/__virtual__/acorn-jsx-virtual-6934646a20/0/cache/acorn-jsx-npm-5.3.2-d7594599ea-c3d3b2a89c.zip/node_modules/acorn-jsx/", + "packageDependencies": [ + ["acorn-jsx", "virtual:8d8ea5d1e3376905d0290522290f47c29213c64d936d96293d758a315829a3cf4c6a5b8ffc1cfee36c3db08f700ad3aaf0711cc5d406a7218c275de6d74effa9#npm:5.3.2"], + ["@types/acorn", null], + ["acorn", "npm:7.4.1"] + ], + "packagePeers": [ + "@types/acorn", + "acorn" + ], + "linkType": "HARD", + }], + ["virtual:fd2253859039a15030fecf2d1545fcad47d7bd43468b9166c71fdd4e35b538414e653775f5401c948ed8db3eb1925f84c66c161d39a27b19ee73fef5e721329e#npm:5.3.2", { + "packageLocation": "./.yarn/__virtual__/acorn-jsx-virtual-0c3ff7dbc9/0/cache/acorn-jsx-npm-5.3.2-d7594599ea-c3d3b2a89c.zip/node_modules/acorn-jsx/", + "packageDependencies": [ + ["acorn-jsx", "virtual:fd2253859039a15030fecf2d1545fcad47d7bd43468b9166c71fdd4e35b538414e653775f5401c948ed8db3eb1925f84c66c161d39a27b19ee73fef5e721329e#npm:5.3.2"], + ["@types/acorn", null], + ["acorn", "npm:8.6.0"] + ], + "packagePeers": [ + "@types/acorn", + "acorn" + ], + "linkType": "HARD", + }] + ]], + ["acorn-node", [ + ["npm:1.8.2", { + "packageLocation": "./.yarn/cache/acorn-node-npm-1.8.2-b30b72c499-02e1564a1c.zip/node_modules/acorn-node/", + "packageDependencies": [ + ["acorn-node", "npm:1.8.2"], + ["acorn", "npm:7.4.1"], + ["acorn-walk", "npm:7.2.0"], + ["xtend", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["acorn-walk", [ + ["npm:7.2.0", { + "packageLocation": "./.yarn/cache/acorn-walk-npm-7.2.0-5f8b515308-9252158a79.zip/node_modules/acorn-walk/", + "packageDependencies": [ + ["acorn-walk", "npm:7.2.0"] + ], + "linkType": "HARD", + }], + ["npm:8.2.0", { + "packageLocation": "./.yarn/cache/acorn-walk-npm-8.2.0-2f2cac3177-1715e76c01.zip/node_modules/acorn-walk/", + "packageDependencies": [ + ["acorn-walk", "npm:8.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["add-stream", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/add-stream-npm-1.0.0-a5a0c0498c-3e9e8b0b8f.zip/node_modules/add-stream/", + "packageDependencies": [ + ["add-stream", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["agent-base", [ + ["npm:6.0.2", { + "packageLocation": "./.yarn/cache/agent-base-npm-6.0.2-428f325a93-f52b6872cc.zip/node_modules/agent-base/", + "packageDependencies": [ + ["agent-base", "npm:6.0.2"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"] + ], + "linkType": "HARD", + }] + ]], + ["agentkeepalive", [ + ["npm:4.2.0", { + "packageLocation": "./.yarn/cache/agentkeepalive-npm-4.2.0-e5e72b8ce4-89806f83ce.zip/node_modules/agentkeepalive/", + "packageDependencies": [ + ["agentkeepalive", "npm:4.2.0"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["depd", "npm:1.1.2"], + ["humanize-ms", "npm:1.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["aggregate-error", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/aggregate-error-npm-3.1.0-415a406f4e-1101a33f21.zip/node_modules/aggregate-error/", + "packageDependencies": [ + ["aggregate-error", "npm:3.1.0"], + ["clean-stack", "npm:2.2.0"], + ["indent-string", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["ajv", [ + ["npm:6.12.6", { + "packageLocation": "./.yarn/cache/ajv-npm-6.12.6-4b5105e2b2-874972efe5.zip/node_modules/ajv/", + "packageDependencies": [ + ["ajv", "npm:6.12.6"], + ["fast-deep-equal", "npm:3.1.3"], + ["fast-json-stable-stringify", "npm:2.1.0"], + ["json-schema-traverse", "npm:0.4.1"], + ["uri-js", "npm:4.4.1"] + ], + "linkType": "HARD", + }], + ["npm:8.8.1", { + "packageLocation": "./.yarn/cache/ajv-npm-8.8.1-3d331224e3-1d586cea81.zip/node_modules/ajv/", + "packageDependencies": [ + ["ajv", "npm:8.8.1"], + ["fast-deep-equal", "npm:3.1.3"], + ["json-schema-traverse", "npm:1.0.0"], + ["require-from-string", "npm:2.0.2"], + ["uri-js", "npm:4.4.1"] + ], + "linkType": "HARD", + }] + ]], + ["ajv-formats", [ + ["npm:2.1.1", { + "packageLocation": "./.yarn/cache/ajv-formats-npm-2.1.1-3cec02eae9-4a287d937f.zip/node_modules/ajv-formats/", + "packageDependencies": [ + ["ajv-formats", "npm:2.1.1"] + ], + "linkType": "SOFT", + }], + ["virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.1.1", { + "packageLocation": "./.yarn/__virtual__/ajv-formats-virtual-dc22a27c7f/0/cache/ajv-formats-npm-2.1.1-3cec02eae9-4a287d937f.zip/node_modules/ajv-formats/", + "packageDependencies": [ + ["ajv-formats", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.1.1"], + ["@types/ajv", null], + ["ajv", "npm:8.8.1"] + ], + "packagePeers": [ + "@types/ajv", + "ajv" + ], + "linkType": "HARD", + }] + ]], + ["ajv-keywords", [ + ["npm:3.5.2", { + "packageLocation": "./.yarn/cache/ajv-keywords-npm-3.5.2-0e391b70e2-7dc5e59316.zip/node_modules/ajv-keywords/", + "packageDependencies": [ + ["ajv-keywords", "npm:3.5.2"] + ], + "linkType": "SOFT", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/ajv-keywords-npm-5.0.0-50b946aaa2-239dd46383.zip/node_modules/ajv-keywords/", + "packageDependencies": [ + ["ajv-keywords", "npm:5.0.0"] + ], + "linkType": "SOFT", + }], + ["virtual:34fbe5a7dba3086dcbcce8a7faed986b10f7a208f11db70499feb2c1afd76e24089e5b95f9e3b937e89512de1cf4937177cc2000303a1e908baefc73362a7d48#npm:5.0.0", { + "packageLocation": "./.yarn/__virtual__/ajv-keywords-virtual-75ea4b6cf0/0/cache/ajv-keywords-npm-5.0.0-50b946aaa2-239dd46383.zip/node_modules/ajv-keywords/", + "packageDependencies": [ + ["ajv-keywords", "virtual:34fbe5a7dba3086dcbcce8a7faed986b10f7a208f11db70499feb2c1afd76e24089e5b95f9e3b937e89512de1cf4937177cc2000303a1e908baefc73362a7d48#npm:5.0.0"], + ["@types/ajv", null], + ["ajv", "npm:8.8.1"], + ["fast-deep-equal", "npm:3.1.3"] + ], + "packagePeers": [ + "@types/ajv", + "ajv" + ], + "linkType": "HARD", + }], + ["virtual:f84d18c473fad3c01e1cf352f81ad13de804ca40da5bf6e752464a2e78dcb097ad579b06da5ff33a55ba9957fb9c74909b99fc5e215420a3f9b5dc87ad71363b#npm:3.5.2", { + "packageLocation": "./.yarn/__virtual__/ajv-keywords-virtual-11d24a6cf1/0/cache/ajv-keywords-npm-3.5.2-0e391b70e2-7dc5e59316.zip/node_modules/ajv-keywords/", + "packageDependencies": [ + ["ajv-keywords", "virtual:f84d18c473fad3c01e1cf352f81ad13de804ca40da5bf6e752464a2e78dcb097ad579b06da5ff33a55ba9957fb9c74909b99fc5e215420a3f9b5dc87ad71363b#npm:3.5.2"], + ["@types/ajv", null], + ["ajv", "npm:6.12.6"] + ], + "packagePeers": [ + "@types/ajv", + "ajv" + ], + "linkType": "HARD", + }] + ]], + ["ansi-align", [ + ["npm:3.0.1", { + "packageLocation": "./.yarn/cache/ansi-align-npm-3.0.1-8e6288d20a-6abfa08f21.zip/node_modules/ansi-align/", + "packageDependencies": [ + ["ansi-align", "npm:3.0.1"], + ["string-width", "npm:4.2.3"] + ], + "linkType": "HARD", + }] + ]], + ["ansi-colors", [ + ["npm:4.1.1", { + "packageLocation": "./.yarn/cache/ansi-colors-npm-4.1.1-97ad42f223-138d04a510.zip/node_modules/ansi-colors/", + "packageDependencies": [ + ["ansi-colors", "npm:4.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["ansi-escapes", [ + ["npm:3.2.0", { + "packageLocation": "./.yarn/cache/ansi-escapes-npm-3.2.0-a9d573100e-0f94695b67.zip/node_modules/ansi-escapes/", + "packageDependencies": [ + ["ansi-escapes", "npm:3.2.0"] + ], + "linkType": "HARD", + }], + ["npm:4.3.2", { + "packageLocation": "./.yarn/cache/ansi-escapes-npm-4.3.2-3ad173702f-93111c4218.zip/node_modules/ansi-escapes/", + "packageDependencies": [ + ["ansi-escapes", "npm:4.3.2"], + ["type-fest", "npm:0.21.3"] + ], + "linkType": "HARD", + }] + ]], + ["ansi-regex", [ + ["npm:2.1.1", { + "packageLocation": "./.yarn/cache/ansi-regex-npm-2.1.1-ddd24d102b-190abd03e4.zip/node_modules/ansi-regex/", + "packageDependencies": [ + ["ansi-regex", "npm:2.1.1"] + ], + "linkType": "HARD", + }], + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/ansi-regex-npm-3.0.0-be0b845911-2ad11c416f.zip/node_modules/ansi-regex/", + "packageDependencies": [ + ["ansi-regex", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/ansi-regex-npm-4.1.0-4a7d8413fe-97aa465953.zip/node_modules/ansi-regex/", + "packageDependencies": [ + ["ansi-regex", "npm:4.1.0"] + ], + "linkType": "HARD", + }], + ["npm:5.0.1", { + "packageLocation": "./.yarn/cache/ansi-regex-npm-5.0.1-c963a48615-2aa4bb54ca.zip/node_modules/ansi-regex/", + "packageDependencies": [ + ["ansi-regex", "npm:5.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["ansi-split", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/ansi-split-npm-1.0.1-586a5367da-301b98e935.zip/node_modules/ansi-split/", + "packageDependencies": [ + ["ansi-split", "npm:1.0.1"], + ["ansi-regex", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["ansi-styles", [ + ["npm:2.2.1", { + "packageLocation": "./.yarn/cache/ansi-styles-npm-2.2.1-f3297e782c-ebc0e00381.zip/node_modules/ansi-styles/", + "packageDependencies": [ + ["ansi-styles", "npm:2.2.1"] + ], + "linkType": "HARD", + }], + ["npm:3.2.1", { + "packageLocation": "./.yarn/cache/ansi-styles-npm-3.2.1-8cb8107983-d85ade01c1.zip/node_modules/ansi-styles/", + "packageDependencies": [ + ["ansi-styles", "npm:3.2.1"], + ["color-convert", "npm:1.9.3"] + ], + "linkType": "HARD", + }], + ["npm:4.3.0", { + "packageLocation": "./.yarn/cache/ansi-styles-npm-4.3.0-245c7d42c7-513b44c3b2.zip/node_modules/ansi-styles/", + "packageDependencies": [ + ["ansi-styles", "npm:4.3.0"], + ["color-convert", "npm:2.0.1"] + ], + "linkType": "HARD", + }], + ["npm:5.2.0", { + "packageLocation": "./.yarn/cache/ansi-styles-npm-5.2.0-72fc7003e3-d7f4e97ce0.zip/node_modules/ansi-styles/", + "packageDependencies": [ + ["ansi-styles", "npm:5.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["ansicolors", [ + ["npm:0.3.2", { + "packageLocation": "./.yarn/cache/ansicolors-npm-0.3.2-cc35882814-e84fae7ebc.zip/node_modules/ansicolors/", + "packageDependencies": [ + ["ansicolors", "npm:0.3.2"] + ], + "linkType": "HARD", + }] + ]], + ["anymatch", [ + ["npm:3.1.2", { + "packageLocation": "./.yarn/cache/anymatch-npm-3.1.2-1d5471acfa-985163db22.zip/node_modules/anymatch/", + "packageDependencies": [ + ["anymatch", "npm:3.1.2"], + ["normalize-path", "npm:3.0.0"], + ["picomatch", "npm:2.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["append-transform", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/append-transform-npm-2.0.0-99bd7d69ed-f26f393bf7.zip/node_modules/append-transform/", + "packageDependencies": [ + ["append-transform", "npm:2.0.0"], + ["default-require-extensions", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["aproba", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/aproba-npm-1.2.0-34129f0778-0fca141966.zip/node_modules/aproba/", + "packageDependencies": [ + ["aproba", "npm:1.2.0"] + ], + "linkType": "HARD", + }], + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/aproba-npm-2.0.0-8716bcfde6-5615cadcfb.zip/node_modules/aproba/", + "packageDependencies": [ + ["aproba", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["archy", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/archy-npm-1.0.0-7db8bfdc3b-504ae7af65.zip/node_modules/archy/", + "packageDependencies": [ + ["archy", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["are-we-there-yet", [ + ["npm:1.1.7", { + "packageLocation": "./.yarn/cache/are-we-there-yet-npm-1.1.7-db9f39924e-70d251719c.zip/node_modules/are-we-there-yet/", + "packageDependencies": [ + ["are-we-there-yet", "npm:1.1.7"], + ["delegates", "npm:1.0.0"], + ["readable-stream", "npm:2.3.7"] + ], + "linkType": "HARD", + }], + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/are-we-there-yet-npm-2.0.0-7d2f5201ce-6c80b4fd04.zip/node_modules/are-we-there-yet/", + "packageDependencies": [ + ["are-we-there-yet", "npm:2.0.0"], + ["delegates", "npm:1.0.0"], + ["readable-stream", "npm:3.6.0"] + ], + "linkType": "HARD", + }], + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/are-we-there-yet-npm-3.0.0-1391430190-348edfdd93.zip/node_modules/are-we-there-yet/", + "packageDependencies": [ + ["are-we-there-yet", "npm:3.0.0"], + ["delegates", "npm:1.0.0"], + ["readable-stream", "npm:3.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["arg", [ + ["npm:4.1.3", { + "packageLocation": "./.yarn/cache/arg-npm-4.1.3-1748b966a8-544af8dd3f.zip/node_modules/arg/", + "packageDependencies": [ + ["arg", "npm:4.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["argparse", [ + ["npm:1.0.10", { + "packageLocation": "./.yarn/cache/argparse-npm-1.0.10-528934e59d-7ca6e45583.zip/node_modules/argparse/", + "packageDependencies": [ + ["argparse", "npm:1.0.10"], + ["sprintf-js", "npm:1.0.3"] + ], + "linkType": "HARD", + }], + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/argparse-npm-2.0.1-faff7999e6-83644b5649.zip/node_modules/argparse/", + "packageDependencies": [ + ["argparse", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["args", [ + ["npm:5.0.1", { + "packageLocation": "./.yarn/cache/args-npm-5.0.1-cd7b0f9dcc-51e2a05f32.zip/node_modules/args/", + "packageDependencies": [ + ["args", "npm:5.0.1"], + ["camelcase", "npm:5.0.0"], + ["chalk", "npm:2.4.2"], + ["leven", "npm:2.1.0"], + ["mri", "npm:1.1.4"] + ], + "linkType": "HARD", + }] + ]], + ["array-differ", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/array-differ-npm-3.0.0-ddc0d89007-117edd9df5.zip/node_modules/array-differ/", + "packageDependencies": [ + ["array-differ", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["array-ify", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/array-ify-npm-1.0.0-e09a371977-c0502015b3.zip/node_modules/array-ify/", + "packageDependencies": [ + ["array-ify", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["array-includes", [ + ["npm:3.1.4", { + "packageLocation": "./.yarn/cache/array-includes-npm-3.1.4-79bb883109-69967c38c5.zip/node_modules/array-includes/", + "packageDependencies": [ + ["array-includes", "npm:3.1.4"], + ["call-bind", "npm:1.0.2"], + ["define-properties", "npm:1.1.3"], + ["es-abstract", "npm:1.19.1"], + ["get-intrinsic", "npm:1.1.1"], + ["is-string", "npm:1.0.7"] + ], + "linkType": "HARD", + }] + ]], + ["array-union", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/array-union-npm-2.1.0-4e4852b221-5bee12395c.zip/node_modules/array-union/", + "packageDependencies": [ + ["array-union", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["array.prototype.flat", [ + ["npm:1.2.5", { + "packageLocation": "./.yarn/cache/array.prototype.flat-npm-1.2.5-6ee21996a1-9cc6414b11.zip/node_modules/array.prototype.flat/", + "packageDependencies": [ + ["array.prototype.flat", "npm:1.2.5"], + ["call-bind", "npm:1.0.2"], + ["define-properties", "npm:1.1.3"], + ["es-abstract", "npm:1.19.1"] + ], + "linkType": "HARD", + }] + ]], + ["arrify", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/arrify-npm-1.0.1-affafba9fe-745075dd4a.zip/node_modules/arrify/", + "packageDependencies": [ + ["arrify", "npm:1.0.1"] + ], + "linkType": "HARD", + }], + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/arrify-npm-2.0.1-38c408f77c-067c4c1afd.zip/node_modules/arrify/", + "packageDependencies": [ + ["arrify", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["asap", [ + ["npm:2.0.6", { + "packageLocation": "./.yarn/cache/asap-npm-2.0.6-36714d439d-b296c92c4b.zip/node_modules/asap/", + "packageDependencies": [ + ["asap", "npm:2.0.6"] + ], + "linkType": "HARD", + }] + ]], + ["asn1", [ + ["npm:0.2.6", { + "packageLocation": "./.yarn/cache/asn1-npm-0.2.6-bdd07356c4-39f2ae343b.zip/node_modules/asn1/", + "packageDependencies": [ + ["asn1", "npm:0.2.6"], + ["safer-buffer", "npm:2.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["asn1.js", [ + ["npm:5.4.1", { + "packageLocation": "./.yarn/cache/asn1.js-npm-5.4.1-37c7edbcb0-3786a101ac.zip/node_modules/asn1.js/", + "packageDependencies": [ + ["asn1.js", "npm:5.4.1"], + ["bn.js", "npm:4.12.0"], + ["inherits", "npm:2.0.4"], + ["minimalistic-assert", "npm:1.0.1"], + ["safer-buffer", "npm:2.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["assert", [ + ["npm:1.5.0", { + "packageLocation": "./.yarn/cache/assert-npm-1.5.0-3303b97e04-9be48435f7.zip/node_modules/assert/", + "packageDependencies": [ + ["assert", "npm:1.5.0"], + ["object-assign", "npm:4.1.1"], + ["util", "npm:0.10.3"] + ], + "linkType": "HARD", + }], + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/assert-npm-2.0.0-ef73bc19f5-bb91f181a8.zip/node_modules/assert/", + "packageDependencies": [ + ["assert", "npm:2.0.0"], + ["es6-object-assign", "npm:1.1.0"], + ["is-nan", "npm:1.3.2"], + ["object-is", "npm:1.1.5"], + ["util", "npm:0.12.4"] + ], + "linkType": "HARD", + }] + ]], + ["assert-browserify", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/assert-browserify-npm-2.0.0-8125e483ea-93c167293e.zip/node_modules/assert-browserify/", + "packageDependencies": [ + ["assert-browserify", "npm:2.0.0"], + ["es6-object-assign", "npm:1.1.0"], + ["is-nan", "npm:1.3.2"], + ["object-is", "npm:1.1.5"], + ["util", "npm:0.12.4"] + ], + "linkType": "HARD", + }] + ]], + ["assert-plus", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/assert-plus-npm-1.0.0-cac95ef098-19b4340cb8.zip/node_modules/assert-plus/", + "packageDependencies": [ + ["assert-plus", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["assertion-error", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/assertion-error-npm-1.1.0-66b893015e-fd9429d3a3.zip/node_modules/assertion-error/", + "packageDependencies": [ + ["assertion-error", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["astral-regex", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/astral-regex-npm-1.0.0-2df7c41332-93417fc087.zip/node_modules/astral-regex/", + "packageDependencies": [ + ["astral-regex", "npm:1.0.0"] + ], + "linkType": "HARD", + }], + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/astral-regex-npm-2.0.0-f30d866aab-876231688c.zip/node_modules/astral-regex/", + "packageDependencies": [ + ["astral-regex", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["async", [ + ["npm:0.9.2", { + "packageLocation": "./.yarn/cache/async-npm-0.9.2-d8cafe6cc3-87dbf12929.zip/node_modules/async/", + "packageDependencies": [ + ["async", "npm:0.9.2"] + ], + "linkType": "HARD", + }], + ["npm:1.5.2", { + "packageLocation": "./.yarn/cache/async-npm-1.5.2-e971969e27-fe5d6214d8.zip/node_modules/async/", + "packageDependencies": [ + ["async", "npm:1.5.2"] + ], + "linkType": "HARD", + }], + ["npm:3.2.2", { + "packageLocation": "./.yarn/cache/async-npm-3.2.2-0245d236b6-90712c98df.zip/node_modules/async/", + "packageDependencies": [ + ["async", "npm:3.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["asynckit", [ + ["npm:0.4.0", { + "packageLocation": "./.yarn/cache/asynckit-npm-0.4.0-c718858525-7b78c451df.zip/node_modules/asynckit/", + "packageDependencies": [ + ["asynckit", "npm:0.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["at-least-node", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/at-least-node-npm-1.0.0-2b36e661fa-463e2f8e43.zip/node_modules/at-least-node/", + "packageDependencies": [ + ["at-least-node", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["atomic-sleep", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/atomic-sleep-npm-1.0.0-17d8a762a3-b95275afb2.zip/node_modules/atomic-sleep/", + "packageDependencies": [ + ["atomic-sleep", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["available-typed-arrays", [ + ["npm:1.0.5", { + "packageLocation": "./.yarn/cache/available-typed-arrays-npm-1.0.5-88f321e4d3-20eb47b3ce.zip/node_modules/available-typed-arrays/", + "packageDependencies": [ + ["available-typed-arrays", "npm:1.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["awilix", [ + ["npm:4.3.4", { + "packageLocation": "./.yarn/cache/awilix-npm-4.3.4-0b277b4254-d8cd0afd03.zip/node_modules/awilix/", + "packageDependencies": [ + ["awilix", "npm:4.3.4"], + ["camel-case", "npm:4.1.2"], + ["glob", "npm:7.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["aws-sdk", [ + ["npm:2.1076.0", { + "packageLocation": "./.yarn/cache/aws-sdk-npm-2.1076.0-5e60905fe8-b618ff8168.zip/node_modules/aws-sdk/", + "packageDependencies": [ + ["aws-sdk", "npm:2.1076.0"], + ["buffer", "npm:4.9.2"], + ["events", "npm:1.1.1"], + ["ieee754", "npm:1.1.13"], + ["jmespath", "npm:0.16.0"], + ["querystring", "npm:0.2.0"], + ["sax", "npm:1.2.1"], + ["url", "npm:0.10.3"], + ["uuid", "npm:3.3.2"], + ["xml2js", "npm:0.4.19"] + ], + "linkType": "HARD", + }] + ]], + ["aws-sign2", [ + ["npm:0.7.0", { + "packageLocation": "./.yarn/cache/aws-sign2-npm-0.7.0-656c6cb84d-b148b0bb07.zip/node_modules/aws-sign2/", + "packageDependencies": [ + ["aws-sign2", "npm:0.7.0"] + ], + "linkType": "HARD", + }] + ]], + ["aws4", [ + ["npm:1.11.0", { + "packageLocation": "./.yarn/cache/aws4-npm-1.11.0-283476ad94-5a00d045fd.zip/node_modules/aws4/", + "packageDependencies": [ + ["aws4", "npm:1.11.0"] + ], + "linkType": "HARD", + }] + ]], + ["axios", [ + ["npm:0.21.4", { + "packageLocation": "./.yarn/cache/axios-npm-0.21.4-e278873748-44245f24ac.zip/node_modules/axios/", + "packageDependencies": [ + ["axios", "npm:0.21.4"], + ["follow-redirects", "virtual:a313c479c5c7e54d9ec8fbeeea69ff640f56b8989ea2dff42351a3fa5c4061fb80a52d8ede0f0826a181a216820c2d2c3f15da881e7fdf31cef1c446e42f0c45#npm:1.14.5"] + ], + "linkType": "HARD", + }] + ]], + ["babel-eslint", [ + ["npm:10.1.0", { + "packageLocation": "./.yarn/cache/babel-eslint-npm-10.1.0-6a6d2b1533-bdc1f62b6b.zip/node_modules/babel-eslint/", + "packageDependencies": [ + ["babel-eslint", "npm:10.1.0"] + ], + "linkType": "SOFT", + }], + ["virtual:27dae49067a60fa65fec6e1c3adad1497d0dda3f71eda711624109131ff3b7d1061a20f55e89b5a0a219da1f7a0a1a0a76bc414d36870315bd60acf5bdcb7f55#npm:10.1.0", { + "packageLocation": "./.yarn/__virtual__/babel-eslint-virtual-ff1372ed3f/0/cache/babel-eslint-npm-10.1.0-6a6d2b1533-bdc1f62b6b.zip/node_modules/babel-eslint/", + "packageDependencies": [ + ["babel-eslint", "virtual:27dae49067a60fa65fec6e1c3adad1497d0dda3f71eda711624109131ff3b7d1061a20f55e89b5a0a219da1f7a0a1a0a76bc414d36870315bd60acf5bdcb7f55#npm:10.1.0"], + ["@babel/code-frame", "npm:7.16.7"], + ["@babel/parser", "npm:7.17.3"], + ["@babel/traverse", "npm:7.17.3"], + ["@babel/types", "npm:7.17.0"], + ["@types/eslint", null], + ["eslint", "npm:7.32.0"], + ["eslint-visitor-keys", "npm:1.3.0"], + ["resolve", "patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b"] + ], + "packagePeers": [ + "@types/eslint", + "eslint" + ], + "linkType": "HARD", + }] + ]], + ["babel-loader", [ + ["npm:8.2.3", { + "packageLocation": "./.yarn/cache/babel-loader-npm-8.2.3-855681b984-78e1e1a919.zip/node_modules/babel-loader/", + "packageDependencies": [ + ["babel-loader", "npm:8.2.3"] + ], + "linkType": "SOFT", + }], + ["virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:8.2.3", { + "packageLocation": "./.yarn/__virtual__/babel-loader-virtual-8e2065e3b7/0/cache/babel-loader-npm-8.2.3-855681b984-78e1e1a919.zip/node_modules/babel-loader/", + "packageDependencies": [ + ["babel-loader", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:8.2.3"], + ["@babel/core", "npm:7.16.0"], + ["@types/babel__core", null], + ["@types/webpack", null], + ["find-cache-dir", "npm:3.3.2"], + ["loader-utils", "npm:1.4.0"], + ["make-dir", "npm:3.1.0"], + ["schema-utils", "npm:2.7.1"], + ["webpack", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:5.64.1"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core", + "@types/webpack", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:8.2.3", { + "packageLocation": "./.yarn/__virtual__/babel-loader-virtual-87a7b434b2/0/cache/babel-loader-npm-8.2.3-855681b984-78e1e1a919.zip/node_modules/babel-loader/", + "packageDependencies": [ + ["babel-loader", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:8.2.3"], + ["@babel/core", "npm:7.16.0"], + ["@types/babel__core", null], + ["@types/webpack", null], + ["find-cache-dir", "npm:3.3.2"], + ["loader-utils", "npm:1.4.0"], + ["make-dir", "npm:3.1.0"], + ["schema-utils", "npm:2.7.1"], + ["webpack", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:5.64.1"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core", + "@types/webpack", + "webpack" + ], + "linkType": "HARD", + }] + ]], + ["babel-plugin-dynamic-import-node", [ + ["npm:2.3.3", { + "packageLocation": "./.yarn/cache/babel-plugin-dynamic-import-node-npm-2.3.3-be081936a9-c9d24415bc.zip/node_modules/babel-plugin-dynamic-import-node/", + "packageDependencies": [ + ["babel-plugin-dynamic-import-node", "npm:2.3.3"], + ["object.assign", "npm:4.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["babel-plugin-polyfill-corejs2", [ + ["npm:0.3.0", { + "packageLocation": "./.yarn/cache/babel-plugin-polyfill-corejs2-npm-0.3.0-4e58d302d2-ffede59798.zip/node_modules/babel-plugin-polyfill-corejs2/", + "packageDependencies": [ + ["babel-plugin-polyfill-corejs2", "npm:0.3.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:0.3.0", { + "packageLocation": "./.yarn/__virtual__/babel-plugin-polyfill-corejs2-virtual-ef89d8c000/0/cache/babel-plugin-polyfill-corejs2-npm-0.3.0-4e58d302d2-ffede59798.zip/node_modules/babel-plugin-polyfill-corejs2/", + "packageDependencies": [ + ["babel-plugin-polyfill-corejs2", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:0.3.0"], + ["@babel/compat-data", "npm:7.16.4"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-define-polyfill-provider", "virtual:ef89d8c000df9e14e09ca866bb81153d80fba90e9d2193815b4078b792631c08caaf98362eca8cf9a3c29249b6d2c9e7d7b24629716c4bb0b5ab719ccefcb2b2#npm:0.3.0"], + ["@types/babel__core", null], + ["semver", "npm:6.3.0"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["babel-plugin-polyfill-corejs3", [ + ["npm:0.4.0", { + "packageLocation": "./.yarn/cache/babel-plugin-polyfill-corejs3-npm-0.4.0-0b821f8a09-18dce9a09a.zip/node_modules/babel-plugin-polyfill-corejs3/", + "packageDependencies": [ + ["babel-plugin-polyfill-corejs3", "npm:0.4.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:0.4.0", { + "packageLocation": "./.yarn/__virtual__/babel-plugin-polyfill-corejs3-virtual-d38673db6f/0/cache/babel-plugin-polyfill-corejs3-npm-0.4.0-0b821f8a09-18dce9a09a.zip/node_modules/babel-plugin-polyfill-corejs3/", + "packageDependencies": [ + ["babel-plugin-polyfill-corejs3", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:0.4.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-define-polyfill-provider", "virtual:ef89d8c000df9e14e09ca866bb81153d80fba90e9d2193815b4078b792631c08caaf98362eca8cf9a3c29249b6d2c9e7d7b24629716c4bb0b5ab719ccefcb2b2#npm:0.3.0"], + ["@types/babel__core", null], + ["core-js-compat", "npm:3.19.1"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["babel-plugin-polyfill-regenerator", [ + ["npm:0.3.0", { + "packageLocation": "./.yarn/cache/babel-plugin-polyfill-regenerator-npm-0.3.0-3228238f85-ecca4389fd.zip/node_modules/babel-plugin-polyfill-regenerator/", + "packageDependencies": [ + ["babel-plugin-polyfill-regenerator", "npm:0.3.0"] + ], + "linkType": "SOFT", + }], + ["virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:0.3.0", { + "packageLocation": "./.yarn/__virtual__/babel-plugin-polyfill-regenerator-virtual-f99f2a349a/0/cache/babel-plugin-polyfill-regenerator-npm-0.3.0-3228238f85-ecca4389fd.zip/node_modules/babel-plugin-polyfill-regenerator/", + "packageDependencies": [ + ["babel-plugin-polyfill-regenerator", "virtual:aa7011b53d1c3eceb38fb992363d77c734b2c7f63c2d81a38e3a7b4bd4a3780a5014de57825893df2df03b784ea10967abd6c822373be63240233d5575188ca3#npm:0.3.0"], + ["@babel/core", "npm:7.16.0"], + ["@babel/helper-define-polyfill-provider", "virtual:ef89d8c000df9e14e09ca866bb81153d80fba90e9d2193815b4078b792631c08caaf98362eca8cf9a3c29249b6d2c9e7d7b24629716c4bb0b5ab719ccefcb2b2#npm:0.3.0"], + ["@types/babel__core", null] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["balanced-match", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/balanced-match-npm-1.0.2-a53c126459-9706c088a2.zip/node_modules/balanced-match/", + "packageDependencies": [ + ["balanced-match", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["base-x", [ + ["npm:3.0.9", { + "packageLocation": "./.yarn/cache/base-x-npm-3.0.9-7b2588e106-957101d6fd.zip/node_modules/base-x/", + "packageDependencies": [ + ["base-x", "npm:3.0.9"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["base64-arraybuffer", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/base64-arraybuffer-npm-1.0.1-e1053d5403-04b6fe6818.zip/node_modules/base64-arraybuffer/", + "packageDependencies": [ + ["base64-arraybuffer", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["base64-js", [ + ["npm:1.5.1", { + "packageLocation": "./.yarn/cache/base64-js-npm-1.5.1-b2f7275641-669632eb37.zip/node_modules/base64-js/", + "packageDependencies": [ + ["base64-js", "npm:1.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["base64id", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/base64id-npm-2.0.0-ef4afeee0a-581b1d37e6.zip/node_modules/base64id/", + "packageDependencies": [ + ["base64id", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["bcrypt-pbkdf", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/bcrypt-pbkdf-npm-1.0.2-80db8b16ed-4edfc9fe7d.zip/node_modules/bcrypt-pbkdf/", + "packageDependencies": [ + ["bcrypt-pbkdf", "npm:1.0.2"], + ["tweetnacl", "npm:0.14.5"] + ], + "linkType": "HARD", + }] + ]], + ["before-after-hook", [ + ["npm:2.2.2", { + "packageLocation": "./.yarn/cache/before-after-hook-npm-2.2.2-b463f0552f-dc2e1ffe38.zip/node_modules/before-after-hook/", + "packageDependencies": [ + ["before-after-hook", "npm:2.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["big.js", [ + ["npm:5.2.2", { + "packageLocation": "./.yarn/cache/big.js-npm-5.2.2-e147c30820-b89b6e8419.zip/node_modules/big.js/", + "packageDependencies": [ + ["big.js", "npm:5.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["bignumber.js", [ + ["npm:9.0.1", { + "packageLocation": "./.yarn/cache/bignumber.js-npm-9.0.1-270d0c8a55-6e72f6069d.zip/node_modules/bignumber.js/", + "packageDependencies": [ + ["bignumber.js", "npm:9.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["bin-links", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/bin-links-npm-3.0.0-6e5e94c609-61cec54a91.zip/node_modules/bin-links/", + "packageDependencies": [ + ["bin-links", "npm:3.0.0"], + ["cmd-shim", "npm:4.1.0"], + ["mkdirp-infer-owner", "npm:2.0.0"], + ["npm-normalize-package-bin", "npm:1.0.1"], + ["read-cmd-shim", "npm:2.0.0"], + ["rimraf", "npm:3.0.2"], + ["write-file-atomic", "npm:4.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["binary-extensions", [ + ["npm:2.2.0", { + "packageLocation": "./.yarn/cache/binary-extensions-npm-2.2.0-180c33fec7-ccd267956c.zip/node_modules/binary-extensions/", + "packageDependencies": [ + ["binary-extensions", "npm:2.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["binaryextensions", [ + ["npm:4.18.0", { + "packageLocation": "./.yarn/cache/binaryextensions-npm-4.18.0-af6f83841f-6fe92a9004.zip/node_modules/binaryextensions/", + "packageDependencies": [ + ["binaryextensions", "npm:4.18.0"] + ], + "linkType": "HARD", + }] + ]], + ["bl", [ + ["npm:1.2.3", { + "packageLocation": "./.yarn/cache/bl-npm-1.2.3-49c4213ca5-123f097989.zip/node_modules/bl/", + "packageDependencies": [ + ["bl", "npm:1.2.3"], + ["readable-stream", "npm:2.3.7"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }], + ["npm:2.2.1", { + "packageLocation": "./.yarn/cache/bl-npm-2.2.1-f294e1ea12-4f5d9b2589.zip/node_modules/bl/", + "packageDependencies": [ + ["bl", "npm:2.2.1"], + ["readable-stream", "npm:2.3.7"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }], + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/bl-npm-4.1.0-7f94cdcf3f-9e8521fa7e.zip/node_modules/bl/", + "packageDependencies": [ + ["bl", "npm:4.1.0"], + ["buffer", "npm:5.7.1"], + ["inherits", "npm:2.0.4"], + ["readable-stream", "npm:3.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["blake3", [ + ["npm:2.1.7", { + "packageLocation": "./.yarn/unplugged/blake3-npm-2.1.7-7bf40c44b4/node_modules/blake3/", + "packageDependencies": [ + ["blake3", "npm:2.1.7"] + ], + "linkType": "HARD", + }] + ]], + ["bloom-filter", [ + ["npm:0.2.0", { + "packageLocation": "./.yarn/cache/bloom-filter-npm-0.2.0-36415efc43-0a19b85cbd.zip/node_modules/bloom-filter/", + "packageDependencies": [ + ["bloom-filter", "npm:0.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["bls-signatures", [ + ["npm:0.2.5", { + "packageLocation": "./.yarn/cache/bls-signatures-npm-0.2.5-2b4387e166-472d697f09.zip/node_modules/bls-signatures/", + "packageDependencies": [ + ["bls-signatures", "npm:0.2.5"] + ], + "linkType": "HARD", + }] + ]], + ["bluebird", [ + ["npm:3.7.2", { + "packageLocation": "./.yarn/cache/bluebird-npm-3.7.2-6a54136ee3-869417503c.zip/node_modules/bluebird/", + "packageDependencies": [ + ["bluebird", "npm:3.7.2"] + ], + "linkType": "HARD", + }] + ]], + ["bn.js", [ + ["npm:4.12.0", { + "packageLocation": "./.yarn/cache/bn.js-npm-4.12.0-3ec6c884f6-39afb4f15f.zip/node_modules/bn.js/", + "packageDependencies": [ + ["bn.js", "npm:4.12.0"] + ], + "linkType": "HARD", + }] + ]], + ["body-parser", [ + ["npm:1.19.0", { + "packageLocation": "./.yarn/cache/body-parser-npm-1.19.0-6e177cabfa-490231b4c8.zip/node_modules/body-parser/", + "packageDependencies": [ + ["body-parser", "npm:1.19.0"], + ["bytes", "npm:3.1.0"], + ["content-type", "npm:1.0.4"], + ["debug", "virtual:0684f4c444aee59cc66233fcce3e4e9a25ed7e35886aed11393a5d4d03c3e7ec6f43fe70e91d783fa0717aa30724ce3a9a32ae0cd75006d103d8f81d5b9758c1#npm:2.6.9"], + ["depd", "npm:1.1.2"], + ["http-errors", "npm:1.7.2"], + ["iconv-lite", "npm:0.4.24"], + ["on-finished", "npm:2.3.0"], + ["qs", "npm:6.7.0"], + ["raw-body", "npm:2.4.0"], + ["type-is", "npm:1.6.18"] + ], + "linkType": "HARD", + }] + ]], + ["boxen", [ + ["npm:5.1.2", { + "packageLocation": "./.yarn/cache/boxen-npm-5.1.2-364ee34f2f-82d03e42a7.zip/node_modules/boxen/", + "packageDependencies": [ + ["boxen", "npm:5.1.2"], + ["ansi-align", "npm:3.0.1"], + ["camelcase", "npm:6.2.1"], + ["chalk", "npm:4.1.2"], + ["cli-boxes", "npm:2.2.1"], + ["string-width", "npm:4.2.3"], + ["type-fest", "npm:0.20.2"], + ["widest-line", "npm:3.1.0"], + ["wrap-ansi", "npm:7.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["brace-expansion", [ + ["npm:1.1.11", { + "packageLocation": "./.yarn/cache/brace-expansion-npm-1.1.11-fb95eb05ad-faf34a7bb0.zip/node_modules/brace-expansion/", + "packageDependencies": [ + ["brace-expansion", "npm:1.1.11"], + ["balanced-match", "npm:1.0.2"], + ["concat-map", "npm:0.0.1"] + ], + "linkType": "HARD", + }], + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/brace-expansion-npm-2.0.1-17aa2616f9-a61e7cd2e8.zip/node_modules/brace-expansion/", + "packageDependencies": [ + ["brace-expansion", "npm:2.0.1"], + ["balanced-match", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["braces", [ + ["npm:3.0.2", { + "packageLocation": "./.yarn/cache/braces-npm-3.0.2-782240b28a-e2a8e769a8.zip/node_modules/braces/", + "packageDependencies": [ + ["braces", "npm:3.0.2"], + ["fill-range", "npm:7.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["brorand", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/brorand-npm-1.1.0-ea86634c4b-8a05c9f3c4.zip/node_modules/brorand/", + "packageDependencies": [ + ["brorand", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["browser-pack", [ + ["npm:6.1.0", { + "packageLocation": "./.yarn/cache/browser-pack-npm-6.1.0-67557e011b-9e5993d3ee.zip/node_modules/browser-pack/", + "packageDependencies": [ + ["browser-pack", "npm:6.1.0"], + ["JSONStream", "npm:1.3.5"], + ["combine-source-map", "npm:0.8.0"], + ["defined", "npm:1.0.0"], + ["safe-buffer", "npm:5.2.1"], + ["through2", "npm:2.0.5"], + ["umd", "npm:3.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["browser-resolve", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/browser-resolve-npm-2.0.0-b837a8fc14-69225e73b5.zip/node_modules/browser-resolve/", + "packageDependencies": [ + ["browser-resolve", "npm:2.0.0"], + ["resolve", "patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b"] + ], + "linkType": "HARD", + }] + ]], + ["browser-stdout", [ + ["npm:1.3.1", { + "packageLocation": "./.yarn/cache/browser-stdout-npm-1.3.1-6b2376bf3f-b717b19b25.zip/node_modules/browser-stdout/", + "packageDependencies": [ + ["browser-stdout", "npm:1.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["browserify", [ + ["npm:16.5.2", { + "packageLocation": "./.yarn/cache/browserify-npm-16.5.2-cfbe4f6efb-75dacf5c82.zip/node_modules/browserify/", + "packageDependencies": [ + ["browserify", "npm:16.5.2"], + ["JSONStream", "npm:1.3.5"], + ["assert", "npm:1.5.0"], + ["browser-pack", "npm:6.1.0"], + ["browser-resolve", "npm:2.0.0"], + ["browserify-zlib", "npm:0.2.0"], + ["buffer", "npm:5.2.1"], + ["cached-path-relative", "npm:1.0.2"], + ["concat-stream", "npm:1.6.2"], + ["console-browserify", "npm:1.2.0"], + ["constants-browserify", "npm:1.0.0"], + ["crypto-browserify", "npm:3.12.0"], + ["defined", "npm:1.0.0"], + ["deps-sort", "npm:2.0.1"], + ["domain-browser", "npm:1.2.0"], + ["duplexer2", "npm:0.1.4"], + ["events", "npm:2.1.0"], + ["glob", "npm:7.2.0"], + ["has", "npm:1.0.3"], + ["htmlescape", "npm:1.1.1"], + ["https-browserify", "npm:1.0.0"], + ["inherits", "npm:2.0.4"], + ["insert-module-globals", "npm:7.2.1"], + ["labeled-stream-splicer", "npm:2.0.2"], + ["mkdirp-classic", "npm:0.5.3"], + ["module-deps", "npm:6.2.3"], + ["os-browserify", "npm:0.3.0"], + ["parents", "npm:1.0.1"], + ["path-browserify", "npm:0.0.1"], + ["process", "npm:0.11.10"], + ["punycode", "npm:1.4.1"], + ["querystring-es3", "npm:0.2.1"], + ["read-only-stream", "npm:2.0.0"], + ["readable-stream", "npm:2.3.7"], + ["resolve", "patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b"], + ["shasum", "npm:1.0.2"], + ["shell-quote", "npm:1.7.3"], + ["stream-browserify", "npm:2.0.2"], + ["stream-http", "npm:3.2.0"], + ["string_decoder", "npm:1.3.0"], + ["subarg", "npm:1.0.0"], + ["syntax-error", "npm:1.4.0"], + ["through2", "npm:2.0.5"], + ["timers-browserify", "npm:1.4.2"], + ["tty-browserify", "npm:0.0.1"], + ["url", "npm:0.11.0"], + ["util", "npm:0.10.4"], + ["vm-browserify", "npm:1.1.2"], + ["xtend", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["browserify-aes", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/browserify-aes-npm-1.2.0-2ad4aeefbe-4a17c3eb55.zip/node_modules/browserify-aes/", + "packageDependencies": [ + ["browserify-aes", "npm:1.2.0"], + ["buffer-xor", "npm:1.0.3"], + ["cipher-base", "npm:1.0.4"], + ["create-hash", "npm:1.2.0"], + ["evp_bytestokey", "npm:1.0.3"], + ["inherits", "npm:2.0.4"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["browserify-cipher", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/browserify-cipher-npm-1.0.1-e00d75c093-2d8500acf1.zip/node_modules/browserify-cipher/", + "packageDependencies": [ + ["browserify-cipher", "npm:1.0.1"], + ["browserify-aes", "npm:1.2.0"], + ["browserify-des", "npm:1.0.2"], + ["evp_bytestokey", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["browserify-des", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/browserify-des-npm-1.0.2-5d04e0cde2-b15a3e358a.zip/node_modules/browserify-des/", + "packageDependencies": [ + ["browserify-des", "npm:1.0.2"], + ["cipher-base", "npm:1.0.4"], + ["des.js", "npm:1.0.1"], + ["inherits", "npm:2.0.4"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["browserify-rsa", [ + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/browserify-rsa-npm-4.1.0-2a224a51bc-155f0c1358.zip/node_modules/browserify-rsa/", + "packageDependencies": [ + ["browserify-rsa", "npm:4.1.0"], + ["bn.js", "npm:4.12.0"], + ["randombytes", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["browserify-sign", [ + ["npm:4.2.1", { + "packageLocation": "./.yarn/cache/browserify-sign-npm-4.2.1-9a8530ca87-0221f190e3.zip/node_modules/browserify-sign/", + "packageDependencies": [ + ["browserify-sign", "npm:4.2.1"], + ["bn.js", "npm:4.12.0"], + ["browserify-rsa", "npm:4.1.0"], + ["create-hash", "npm:1.2.0"], + ["create-hmac", "npm:1.1.7"], + ["elliptic", "npm:6.5.3"], + ["inherits", "npm:2.0.4"], + ["parse-asn1", "npm:5.1.6"], + ["readable-stream", "npm:3.6.0"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["browserify-zlib", [ + ["npm:0.2.0", { + "packageLocation": "./.yarn/cache/browserify-zlib-npm-0.2.0-eab4087284-5cd9d6a665.zip/node_modules/browserify-zlib/", + "packageDependencies": [ + ["browserify-zlib", "npm:0.2.0"], + ["pako", "npm:1.0.11"] + ], + "linkType": "HARD", + }] + ]], + ["browserslist", [ + ["npm:4.18.1", { + "packageLocation": "./.yarn/cache/browserslist-npm-4.18.1-38eb8a64b9-ae58322dee.zip/node_modules/browserslist/", + "packageDependencies": [ + ["browserslist", "npm:4.18.1"], + ["caniuse-lite", "npm:1.0.30001282"], + ["electron-to-chromium", "npm:1.3.903"], + ["escalade", "npm:3.1.1"], + ["node-releases", "npm:2.0.1"], + ["picocolors", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["bs58", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/bs58-npm-4.0.1-8d2a7822b1-b3c5365bb9.zip/node_modules/bs58/", + "packageDependencies": [ + ["bs58", "npm:4.0.1"], + ["base-x", "npm:3.0.9"] + ], + "linkType": "HARD", + }] + ]], + ["bson", [ + ["npm:1.1.6", { + "packageLocation": "./.yarn/cache/bson-npm-1.1.6-071be5c52e-75762c9b7e.zip/node_modules/bson/", + "packageDependencies": [ + ["bson", "npm:1.1.6"] + ], + "linkType": "HARD", + }] + ]], + ["buffer", [ + ["npm:4.9.2", { + "packageLocation": "./.yarn/cache/buffer-npm-4.9.2-9e40b5e87a-8801bc1ba0.zip/node_modules/buffer/", + "packageDependencies": [ + ["buffer", "npm:4.9.2"], + ["base64-js", "npm:1.5.1"], + ["ieee754", "npm:1.2.1"], + ["isarray", "npm:1.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.2.1", { + "packageLocation": "./.yarn/cache/buffer-npm-5.2.1-9f7652b857-aa3f25bb88.zip/node_modules/buffer/", + "packageDependencies": [ + ["buffer", "npm:5.2.1"], + ["base64-js", "npm:1.5.1"], + ["ieee754", "npm:1.2.1"] + ], + "linkType": "HARD", + }], + ["npm:5.7.1", { + "packageLocation": "./.yarn/cache/buffer-npm-5.7.1-513ef8259e-e2cf8429e1.zip/node_modules/buffer/", + "packageDependencies": [ + ["buffer", "npm:5.7.1"], + ["base64-js", "npm:1.5.1"], + ["ieee754", "npm:1.2.1"] + ], + "linkType": "HARD", + }], + ["npm:6.0.3", { + "packageLocation": "./.yarn/cache/buffer-npm-6.0.3-cd90dfedfe-5ad23293d9.zip/node_modules/buffer/", + "packageDependencies": [ + ["buffer", "npm:6.0.3"], + ["base64-js", "npm:1.5.1"], + ["ieee754", "npm:1.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["buffer-from", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/buffer-from-npm-1.1.2-03d2f20d7e-0448524a56.zip/node_modules/buffer-from/", + "packageDependencies": [ + ["buffer-from", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["buffer-reverse", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/buffer-reverse-npm-1.0.1-2224e35393-e350872a89.zip/node_modules/buffer-reverse/", + "packageDependencies": [ + ["buffer-reverse", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["buffer-xor", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/buffer-xor-npm-1.0.3-56bb81b0dd-10c520df29.zip/node_modules/buffer-xor/", + "packageDependencies": [ + ["buffer-xor", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["bufferutil", [ + ["npm:4.0.6", { + "packageLocation": "./.yarn/unplugged/bufferutil-npm-4.0.6-b93c8a5e05/node_modules/bufferutil/", + "packageDependencies": [ + ["bufferutil", "npm:4.0.6"], + ["node-gyp", "npm:8.4.0"], + ["node-gyp-build", "npm:4.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["builtin-status-codes", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/builtin-status-codes-npm-3.0.0-e376b0580b-1119429cf4.zip/node_modules/builtin-status-codes/", + "packageDependencies": [ + ["builtin-status-codes", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["builtins", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/builtins-npm-1.0.3-f09d2d57f2-47ce94f7ee.zip/node_modules/builtins/", + "packageDependencies": [ + ["builtins", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["bytes", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/bytes-npm-3.1.0-19c5b15405-7c3b21c5d9.zip/node_modules/bytes/", + "packageDependencies": [ + ["bytes", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["cacache", [ + ["npm:15.3.0", { + "packageLocation": "./.yarn/cache/cacache-npm-15.3.0-a7e5239c6a-a07327c27a.zip/node_modules/cacache/", + "packageDependencies": [ + ["cacache", "npm:15.3.0"], + ["@npmcli/fs", "npm:1.0.0"], + ["@npmcli/move-file", "npm:1.1.2"], + ["chownr", "npm:2.0.0"], + ["fs-minipass", "npm:2.1.0"], + ["glob", "npm:7.2.0"], + ["infer-owner", "npm:1.0.4"], + ["lru-cache", "npm:6.0.0"], + ["minipass", "npm:3.1.6"], + ["minipass-collect", "npm:1.0.2"], + ["minipass-flush", "npm:1.0.5"], + ["minipass-pipeline", "npm:1.2.4"], + ["mkdirp", "npm:1.0.4"], + ["p-map", "npm:4.0.0"], + ["promise-inflight", "virtual:a7e5239c6ae68bf6359adfd3598326db000e94dbb349bc00a3852ed53a31712a0e2e787228c6e859d3e5cf2fbb872aba1ea4abe4995cef8086a77ef619ae1be6#npm:1.0.1"], + ["rimraf", "npm:3.0.2"], + ["ssri", "npm:8.0.1"], + ["tar", "npm:6.1.11"], + ["unique-filename", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["cacheable-request", [ + ["npm:6.1.0", { + "packageLocation": "./.yarn/cache/cacheable-request-npm-6.1.0-684b834873-b510b237b1.zip/node_modules/cacheable-request/", + "packageDependencies": [ + ["cacheable-request", "npm:6.1.0"], + ["clone-response", "npm:1.0.2"], + ["get-stream", "npm:5.2.0"], + ["http-cache-semantics", "npm:4.1.0"], + ["keyv", "npm:3.1.0"], + ["lowercase-keys", "npm:2.0.0"], + ["normalize-url", "npm:4.5.1"], + ["responselike", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["cached-path-relative", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/cached-path-relative-npm-1.0.2-375da1d4a2-643fa65a65.zip/node_modules/cached-path-relative/", + "packageDependencies": [ + ["cached-path-relative", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["caching-transform", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/caching-transform-npm-4.0.0-d619d562ea-c4db693953.zip/node_modules/caching-transform/", + "packageDependencies": [ + ["caching-transform", "npm:4.0.0"], + ["hasha", "npm:5.2.2"], + ["make-dir", "npm:3.1.0"], + ["package-hash", "npm:4.0.0"], + ["write-file-atomic", "npm:3.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["call-bind", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/call-bind-npm-1.0.2-c957124861-f8e31de9d1.zip/node_modules/call-bind/", + "packageDependencies": [ + ["call-bind", "npm:1.0.2"], + ["function-bind", "npm:1.1.1"], + ["get-intrinsic", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["call-me-maybe", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/call-me-maybe-npm-1.0.1-d07e74bc9c-d19e9d6ac2.zip/node_modules/call-me-maybe/", + "packageDependencies": [ + ["call-me-maybe", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["callsites", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/callsites-npm-3.1.0-268f989910-072d17b6ab.zip/node_modules/callsites/", + "packageDependencies": [ + ["callsites", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["camel-case", [ + ["npm:4.1.2", { + "packageLocation": "./.yarn/cache/camel-case-npm-4.1.2-082bf67a9a-bcbd25cd25.zip/node_modules/camel-case/", + "packageDependencies": [ + ["camel-case", "npm:4.1.2"], + ["pascal-case", "npm:3.1.2"], + ["tslib", "npm:2.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["camelcase", [ + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/camelcase-npm-5.0.0-c808398846-8bfe920e04.zip/node_modules/camelcase/", + "packageDependencies": [ + ["camelcase", "npm:5.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.3.1", { + "packageLocation": "./.yarn/cache/camelcase-npm-5.3.1-5db8af62c5-e6effce26b.zip/node_modules/camelcase/", + "packageDependencies": [ + ["camelcase", "npm:5.3.1"] + ], + "linkType": "HARD", + }], + ["npm:6.2.1", { + "packageLocation": "./.yarn/cache/camelcase-npm-6.2.1-5a9a60f6d3-d876272ef7.zip/node_modules/camelcase/", + "packageDependencies": [ + ["camelcase", "npm:6.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["camelcase-keys", [ + ["npm:6.2.2", { + "packageLocation": "./.yarn/cache/camelcase-keys-npm-6.2.2-d13777ec12-43c9af1adf.zip/node_modules/camelcase-keys/", + "packageDependencies": [ + ["camelcase-keys", "npm:6.2.2"], + ["camelcase", "npm:5.3.1"], + ["map-obj", "npm:4.3.0"], + ["quick-lru", "npm:4.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["caniuse-lite", [ + ["npm:1.0.30001282", { + "packageLocation": "./.yarn/cache/caniuse-lite-npm-1.0.30001282-49173a42dd-62797fd756.zip/node_modules/caniuse-lite/", + "packageDependencies": [ + ["caniuse-lite", "npm:1.0.30001282"] + ], + "linkType": "HARD", + }] + ]], + ["cardinal", [ + ["npm:2.1.1", { + "packageLocation": "./.yarn/cache/cardinal-npm-2.1.1-b77e7b28a7-e8d4ae4643.zip/node_modules/cardinal/", + "packageDependencies": [ + ["cardinal", "npm:2.1.1"], + ["ansicolors", "npm:0.3.2"], + ["redeyed", "npm:2.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["cargo-cp-artifact", [ + ["npm:0.1.6", { + "packageLocation": "./.yarn/cache/cargo-cp-artifact-npm-0.1.6-fe2dd40a8f-2f5d2f3e73.zip/node_modules/cargo-cp-artifact/", + "packageDependencies": [ + ["cargo-cp-artifact", "npm:0.1.6"] + ], + "linkType": "HARD", + }] + ]], + ["caseless", [ + ["npm:0.12.0", { + "packageLocation": "./.yarn/cache/caseless-npm-0.12.0-e83bc5df83-b43bd4c440.zip/node_modules/caseless/", + "packageDependencies": [ + ["caseless", "npm:0.12.0"] + ], + "linkType": "HARD", + }] + ]], + ["cbor", [ + ["npm:8.1.0", { + "packageLocation": "./.yarn/cache/cbor-npm-8.1.0-c1a4d6266a-a90338435d.zip/node_modules/cbor/", + "packageDependencies": [ + ["cbor", "npm:8.1.0"], + ["nofilter", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["chai", [ + ["npm:4.3.4", { + "packageLocation": "./.yarn/cache/chai-npm-4.3.4-808f3b5355-772c522b3b.zip/node_modules/chai/", + "packageDependencies": [ + ["chai", "npm:4.3.4"], + ["assertion-error", "npm:1.1.0"], + ["check-error", "npm:1.0.2"], + ["deep-eql", "npm:3.0.1"], + ["get-func-name", "npm:2.0.0"], + ["pathval", "npm:1.1.1"], + ["type-detect", "npm:4.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["chai-as-promised", [ + ["npm:7.1.1", { + "packageLocation": "./.yarn/cache/chai-as-promised-npm-7.1.1-cdc17e4612-7262868a5b.zip/node_modules/chai-as-promised/", + "packageDependencies": [ + ["chai-as-promised", "npm:7.1.1"] + ], + "linkType": "SOFT", + }], + ["virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:7.1.1", { + "packageLocation": "./.yarn/__virtual__/chai-as-promised-virtual-c69e977e8a/0/cache/chai-as-promised-npm-7.1.1-cdc17e4612-7262868a5b.zip/node_modules/chai-as-promised/", + "packageDependencies": [ + ["chai-as-promised", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:7.1.1"], + ["@types/chai", null], + ["chai", "npm:4.3.4"], + ["check-error", "npm:1.0.2"] + ], + "packagePeers": [ + "@types/chai", + "chai" + ], + "linkType": "HARD", + }] + ]], + ["chai-exclude", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/chai-exclude-npm-2.1.0-47ff9dee55-29d964d9f6.zip/node_modules/chai-exclude/", + "packageDependencies": [ + ["chai-exclude", "npm:2.1.0"] + ], + "linkType": "SOFT", + }], + ["virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.1.0", { + "packageLocation": "./.yarn/__virtual__/chai-exclude-virtual-437d0daf57/0/cache/chai-exclude-npm-2.1.0-47ff9dee55-29d964d9f6.zip/node_modules/chai-exclude/", + "packageDependencies": [ + ["chai-exclude", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.1.0"], + ["@types/chai", null], + ["chai", "npm:4.3.4"], + ["fclone", "npm:1.0.11"] + ], + "packagePeers": [ + "@types/chai", + "chai" + ], + "linkType": "HARD", + }] + ]], + ["chai-string", [ + ["npm:1.5.0", { + "packageLocation": "./.yarn/cache/chai-string-npm-1.5.0-b46dce1494-d443bb416f.zip/node_modules/chai-string/", + "packageDependencies": [ + ["chai-string", "npm:1.5.0"] + ], + "linkType": "SOFT", + }], + ["virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:1.5.0", { + "packageLocation": "./.yarn/__virtual__/chai-string-virtual-06778e00c2/0/cache/chai-string-npm-1.5.0-b46dce1494-d443bb416f.zip/node_modules/chai-string/", + "packageDependencies": [ + ["chai-string", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:1.5.0"], + ["@types/chai", null], + ["chai", "npm:4.3.4"] + ], + "packagePeers": [ + "@types/chai", + "chai" + ], + "linkType": "HARD", + }] + ]], + ["chalk", [ + ["npm:1.1.3", { + "packageLocation": "./.yarn/cache/chalk-npm-1.1.3-59144c3a87-9d2ea6b98f.zip/node_modules/chalk/", + "packageDependencies": [ + ["chalk", "npm:1.1.3"], + ["ansi-styles", "npm:2.2.1"], + ["escape-string-regexp", "npm:1.0.5"], + ["has-ansi", "npm:2.0.0"], + ["strip-ansi", "npm:3.0.1"], + ["supports-color", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["npm:2.4.2", { + "packageLocation": "./.yarn/cache/chalk-npm-2.4.2-3ea16dd91e-ec3661d38f.zip/node_modules/chalk/", + "packageDependencies": [ + ["chalk", "npm:2.4.2"], + ["ansi-styles", "npm:3.2.1"], + ["escape-string-regexp", "npm:1.0.5"], + ["supports-color", "npm:5.5.0"] + ], + "linkType": "HARD", + }], + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/chalk-npm-3.0.0-e813208025-8e3ddf3981.zip/node_modules/chalk/", + "packageDependencies": [ + ["chalk", "npm:3.0.0"], + ["ansi-styles", "npm:4.3.0"], + ["supports-color", "npm:7.2.0"] + ], + "linkType": "HARD", + }], + ["npm:4.1.2", { + "packageLocation": "./.yarn/cache/chalk-npm-4.1.2-ba8b67ab80-fe75c9d5c7.zip/node_modules/chalk/", + "packageDependencies": [ + ["chalk", "npm:4.1.2"], + ["ansi-styles", "npm:4.3.0"], + ["supports-color", "npm:7.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["chance", [ + ["npm:1.1.8", { + "packageLocation": "./.yarn/cache/chance-npm-1.1.8-47e2e1db1e-e733f51e10.zip/node_modules/chance/", + "packageDependencies": [ + ["chance", "npm:1.1.8"] + ], + "linkType": "HARD", + }] + ]], + ["chardet", [ + ["npm:0.7.0", { + "packageLocation": "./.yarn/cache/chardet-npm-0.7.0-27933dd6c7-6fd5da1f5d.zip/node_modules/chardet/", + "packageDependencies": [ + ["chardet", "npm:0.7.0"] + ], + "linkType": "HARD", + }] + ]], + ["check-error", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/check-error-npm-1.0.2-00c540c6e9-d9d1065044.zip/node_modules/check-error/", + "packageDependencies": [ + ["check-error", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["chokidar", [ + ["npm:3.5.2", { + "packageLocation": "./.yarn/cache/chokidar-npm-3.5.2-6752340fec-d1fda32fcd.zip/node_modules/chokidar/", + "packageDependencies": [ + ["chokidar", "npm:3.5.2"], + ["anymatch", "npm:3.1.2"], + ["braces", "npm:3.0.2"], + ["fsevents", "patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=18f3a7"], + ["glob-parent", "npm:5.1.2"], + ["is-binary-path", "npm:2.1.0"], + ["is-glob", "npm:4.0.3"], + ["normalize-path", "npm:3.0.0"], + ["readdirp", "npm:3.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["chownr", [ + ["npm:1.1.4", { + "packageLocation": "./.yarn/cache/chownr-npm-1.1.4-5bd400ab08-115648f8eb.zip/node_modules/chownr/", + "packageDependencies": [ + ["chownr", "npm:1.1.4"] + ], + "linkType": "HARD", + }], + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/chownr-npm-2.0.0-638f1c9c61-c57cf9dd07.zip/node_modules/chownr/", + "packageDependencies": [ + ["chownr", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["chrome-trace-event", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/chrome-trace-event-npm-1.0.3-e0ae3dcd60-cb8b1fc7e8.zip/node_modules/chrome-trace-event/", + "packageDependencies": [ + ["chrome-trace-event", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["ci-info", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/ci-info-npm-2.0.0-78012236a1-3b374666a8.zip/node_modules/ci-info/", + "packageDependencies": [ + ["ci-info", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["cipher-base", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/cipher-base-npm-1.0.4-2e98b97140-47d3568dbc.zip/node_modules/cipher-base/", + "packageDependencies": [ + ["cipher-base", "npm:1.0.4"], + ["inherits", "npm:2.0.4"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["clean-stack", [ + ["npm:2.2.0", { + "packageLocation": "./.yarn/cache/clean-stack-npm-2.2.0-a8ce435a5c-2ac8cd2b2f.zip/node_modules/clean-stack/", + "packageDependencies": [ + ["clean-stack", "npm:2.2.0"] + ], + "linkType": "HARD", + }], + ["npm:3.0.1", { + "packageLocation": "./.yarn/cache/clean-stack-npm-3.0.1-85c3878b76-dc18c842d7.zip/node_modules/clean-stack/", + "packageDependencies": [ + ["clean-stack", "npm:3.0.1"], + ["escape-string-regexp", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["cli-boxes", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/cli-boxes-npm-1.0.0-fdd89bc01b-101cfd6464.zip/node_modules/cli-boxes/", + "packageDependencies": [ + ["cli-boxes", "npm:1.0.0"] + ], + "linkType": "HARD", + }], + ["npm:2.2.1", { + "packageLocation": "./.yarn/cache/cli-boxes-npm-2.2.1-7125a5ba44-be79f8ec23.zip/node_modules/cli-boxes/", + "packageDependencies": [ + ["cli-boxes", "npm:2.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["cli-cursor", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/cli-cursor-npm-3.1.0-fee1e46b5e-2692784c6c.zip/node_modules/cli-cursor/", + "packageDependencies": [ + ["cli-cursor", "npm:3.1.0"], + ["restore-cursor", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["cli-progress", [ + ["npm:3.10.0", { + "packageLocation": "./.yarn/cache/cli-progress-npm-3.10.0-a1609d715c-8e22c6265f.zip/node_modules/cli-progress/", + "packageDependencies": [ + ["cli-progress", "npm:3.10.0"], + ["string-width", "npm:4.2.3"] + ], + "linkType": "HARD", + }] + ]], + ["cli-spinners", [ + ["npm:2.6.1", { + "packageLocation": "./.yarn/cache/cli-spinners-npm-2.6.1-33ce2bad0f-423409baaa.zip/node_modules/cli-spinners/", + "packageDependencies": [ + ["cli-spinners", "npm:2.6.1"] + ], + "linkType": "HARD", + }] + ]], + ["cli-table", [ + ["npm:0.3.11", { + "packageLocation": "./.yarn/cache/cli-table-npm-0.3.11-f912789cff-59fb61f992.zip/node_modules/cli-table/", + "packageDependencies": [ + ["cli-table", "npm:0.3.11"], + ["colors", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["cli-truncate", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/cli-truncate-npm-2.1.0-72184d3467-bf1e4e6195.zip/node_modules/cli-truncate/", + "packageDependencies": [ + ["cli-truncate", "npm:2.1.0"], + ["slice-ansi", "npm:3.0.0"], + ["string-width", "npm:4.2.3"] + ], + "linkType": "HARD", + }] + ]], + ["cli-width", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/cli-width-npm-3.0.0-387b3f68f9-4c94af3769.zip/node_modules/cli-width/", + "packageDependencies": [ + ["cli-width", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["cliui", [ + ["npm:6.0.0", { + "packageLocation": "./.yarn/cache/cliui-npm-6.0.0-488b2414c6-4fcfd26d29.zip/node_modules/cliui/", + "packageDependencies": [ + ["cliui", "npm:6.0.0"], + ["string-width", "npm:4.2.3"], + ["strip-ansi", "npm:6.0.1"], + ["wrap-ansi", "npm:6.2.0"] + ], + "linkType": "HARD", + }], + ["npm:7.0.4", { + "packageLocation": "./.yarn/cache/cliui-npm-7.0.4-d6b8a9edb6-ce2e8f578a.zip/node_modules/cliui/", + "packageDependencies": [ + ["cliui", "npm:7.0.4"], + ["string-width", "npm:4.2.3"], + ["strip-ansi", "npm:6.0.1"], + ["wrap-ansi", "npm:7.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["clone", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/clone-npm-1.0.4-a610fcbcf9-d06418b733.zip/node_modules/clone/", + "packageDependencies": [ + ["clone", "npm:1.0.4"] + ], + "linkType": "HARD", + }], + ["npm:2.1.2", { + "packageLocation": "./.yarn/cache/clone-npm-2.1.2-1d491c6629-aaf106e9bc.zip/node_modules/clone/", + "packageDependencies": [ + ["clone", "npm:2.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["clone-buffer", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/clone-buffer-npm-1.0.0-7a16490ce4-a39a35e7fd.zip/node_modules/clone-buffer/", + "packageDependencies": [ + ["clone-buffer", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["clone-deep", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/clone-deep-npm-4.0.1-70adab92c8-770f912fe4.zip/node_modules/clone-deep/", + "packageDependencies": [ + ["clone-deep", "npm:4.0.1"], + ["is-plain-object", "npm:2.0.4"], + ["kind-of", "npm:6.0.3"], + ["shallow-clone", "npm:3.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["clone-response", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/clone-response-npm-1.0.2-135ae8239d-2d0e61547f.zip/node_modules/clone-response/", + "packageDependencies": [ + ["clone-response", "npm:1.0.2"], + ["mimic-response", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["clone-stats", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/clone-stats-npm-1.0.0-cca25a0a42-654c0425af.zip/node_modules/clone-stats/", + "packageDependencies": [ + ["clone-stats", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["cloneable-readable", [ + ["npm:1.1.3", { + "packageLocation": "./.yarn/cache/cloneable-readable-npm-1.1.3-a5888ff6e9-23b3741225.zip/node_modules/cloneable-readable/", + "packageDependencies": [ + ["cloneable-readable", "npm:1.1.3"], + ["inherits", "npm:2.0.4"], + ["process-nextick-args", "npm:2.0.1"], + ["readable-stream", "npm:2.3.7"] + ], + "linkType": "HARD", + }] + ]], + ["cmd-shim", [ + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/cmd-shim-npm-4.1.0-018e70f153-d25bb57a8a.zip/node_modules/cmd-shim/", + "packageDependencies": [ + ["cmd-shim", "npm:4.1.0"], + ["mkdirp-infer-owner", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["code-point-at", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/code-point-at-npm-1.1.0-37de5fe566-17d5666611.zip/node_modules/code-point-at/", + "packageDependencies": [ + ["code-point-at", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["color", [ + ["npm:3.2.1", { + "packageLocation": "./.yarn/cache/color-npm-3.2.1-568cf1014f-f81220e8b7.zip/node_modules/color/", + "packageDependencies": [ + ["color", "npm:3.2.1"], + ["color-convert", "npm:1.9.3"], + ["color-string", "npm:1.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["color-convert", [ + ["npm:1.9.3", { + "packageLocation": "./.yarn/cache/color-convert-npm-1.9.3-1fe690075e-fd7a64a17c.zip/node_modules/color-convert/", + "packageDependencies": [ + ["color-convert", "npm:1.9.3"], + ["color-name", "npm:1.1.3"] + ], + "linkType": "HARD", + }], + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/color-convert-npm-2.0.1-79730e935b-79e6bdb9fd.zip/node_modules/color-convert/", + "packageDependencies": [ + ["color-convert", "npm:2.0.1"], + ["color-name", "npm:1.1.4"] + ], + "linkType": "HARD", + }] + ]], + ["color-name", [ + ["npm:1.1.3", { + "packageLocation": "./.yarn/cache/color-name-npm-1.1.3-728b7b5d39-09c5d3e33d.zip/node_modules/color-name/", + "packageDependencies": [ + ["color-name", "npm:1.1.3"] + ], + "linkType": "HARD", + }], + ["npm:1.1.4", { + "packageLocation": "./.yarn/cache/color-name-npm-1.1.4-025792b0ea-b044585952.zip/node_modules/color-name/", + "packageDependencies": [ + ["color-name", "npm:1.1.4"] + ], + "linkType": "HARD", + }] + ]], + ["color-string", [ + ["npm:1.6.0", { + "packageLocation": "./.yarn/cache/color-string-npm-1.6.0-94ed25c258-33466a6527.zip/node_modules/color-string/", + "packageDependencies": [ + ["color-string", "npm:1.6.0"], + ["color-name", "npm:1.1.4"], + ["simple-swizzle", "npm:0.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["color-support", [ + ["npm:1.1.3", { + "packageLocation": "./.yarn/cache/color-support-npm-1.1.3-3be5c53455-9b73568176.zip/node_modules/color-support/", + "packageDependencies": [ + ["color-support", "npm:1.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["colorette", [ + ["npm:2.0.16", { + "packageLocation": "./.yarn/cache/colorette-npm-2.0.16-7b996485d7-cd55596a3a.zip/node_modules/colorette/", + "packageDependencies": [ + ["colorette", "npm:2.0.16"] + ], + "linkType": "HARD", + }] + ]], + ["colors", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/colors-npm-1.0.3-6c5d583ab3-234e8d3ab7.zip/node_modules/colors/", + "packageDependencies": [ + ["colors", "npm:1.0.3"] + ], + "linkType": "HARD", + }], + ["npm:1.4.0", { + "packageLocation": "./.yarn/cache/colors-npm-1.4.0-7e2cf12234-98aa2c2418.zip/node_modules/colors/", + "packageDependencies": [ + ["colors", "npm:1.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["colorspace", [ + ["npm:1.1.4", { + "packageLocation": "./.yarn/cache/colorspace-npm-1.1.4-f01655548a-bb3934ef3c.zip/node_modules/colorspace/", + "packageDependencies": [ + ["colorspace", "npm:1.1.4"], + ["color", "npm:3.2.1"], + ["text-hex", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["combine-source-map", [ + ["npm:0.8.0", { + "packageLocation": "./.yarn/cache/combine-source-map-npm-0.8.0-3715049f57-26b3064a4e.zip/node_modules/combine-source-map/", + "packageDependencies": [ + ["combine-source-map", "npm:0.8.0"], + ["convert-source-map", "npm:1.1.3"], + ["inline-source-map", "npm:0.6.2"], + ["lodash.memoize", "npm:3.0.4"], + ["source-map", "npm:0.5.7"] + ], + "linkType": "HARD", + }] + ]], + ["combined-stream", [ + ["npm:1.0.8", { + "packageLocation": "./.yarn/cache/combined-stream-npm-1.0.8-dc14d4a63a-49fa4aeb49.zip/node_modules/combined-stream/", + "packageDependencies": [ + ["combined-stream", "npm:1.0.8"], + ["delayed-stream", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["commander", [ + ["npm:2.20.3", { + "packageLocation": "./.yarn/cache/commander-npm-2.20.3-d8dcbaa39b-ab8c07884e.zip/node_modules/commander/", + "packageDependencies": [ + ["commander", "npm:2.20.3"] + ], + "linkType": "HARD", + }], + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/commander-npm-4.0.1-7d2e712c26-a8df9873c6.zip/node_modules/commander/", + "packageDependencies": [ + ["commander", "npm:4.0.1"] + ], + "linkType": "HARD", + }], + ["npm:7.1.0", { + "packageLocation": "./.yarn/cache/commander-npm-7.1.0-632d393e57-99c120b939.zip/node_modules/commander/", + "packageDependencies": [ + ["commander", "npm:7.1.0"] + ], + "linkType": "HARD", + }], + ["npm:7.2.0", { + "packageLocation": "./.yarn/cache/commander-npm-7.2.0-19178180f8-53501cbeee.zip/node_modules/commander/", + "packageDependencies": [ + ["commander", "npm:7.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["comment-parser", [ + ["npm:0.7.6", { + "packageLocation": "./.yarn/cache/comment-parser-npm-0.7.6-927ea8eaf8-880e4d58c0.zip/node_modules/comment-parser/", + "packageDependencies": [ + ["comment-parser", "npm:0.7.6"] + ], + "linkType": "HARD", + }] + ]], + ["common-ancestor-path", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/common-ancestor-path-npm-1.0.1-27534e68da-1d2e418606.zip/node_modules/common-ancestor-path/", + "packageDependencies": [ + ["common-ancestor-path", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["commondir", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/commondir-npm-1.0.1-291b790340-59715f2fc4.zip/node_modules/commondir/", + "packageDependencies": [ + ["commondir", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["compare-func", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/compare-func-npm-2.0.0-9cd7852f23-fb71d70632.zip/node_modules/compare-func/", + "packageDependencies": [ + ["compare-func", "npm:2.0.0"], + ["array-ify", "npm:1.0.0"], + ["dot-prop", "npm:5.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["complex.js", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/complex.js-npm-2.1.0-6d9742b352-8a31a0d819.zip/node_modules/complex.js/", + "packageDependencies": [ + ["complex.js", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["component-emitter", [ + ["npm:1.3.0", { + "packageLocation": "./.yarn/cache/component-emitter-npm-1.3.0-4b848565b9-b3c46de38f.zip/node_modules/component-emitter/", + "packageDependencies": [ + ["component-emitter", "npm:1.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["concat-map", [ + ["npm:0.0.1", { + "packageLocation": "./.yarn/cache/concat-map-npm-0.0.1-85a921b7ee-902a9f5d89.zip/node_modules/concat-map/", + "packageDependencies": [ + ["concat-map", "npm:0.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["concat-stream", [ + ["npm:1.6.2", { + "packageLocation": "./.yarn/cache/concat-stream-npm-1.6.2-2bee337060-1ef77032cb.zip/node_modules/concat-stream/", + "packageDependencies": [ + ["concat-stream", "npm:1.6.2"], + ["buffer-from", "npm:1.1.2"], + ["inherits", "npm:2.0.4"], + ["readable-stream", "npm:2.3.7"], + ["typedarray", "npm:0.0.6"] + ], + "linkType": "HARD", + }] + ]], + ["concurrently", [ + ["npm:7.0.0", { + "packageLocation": "./.yarn/cache/concurrently-npm-7.0.0-c402b003bc-1be78f24bf.zip/node_modules/concurrently/", + "packageDependencies": [ + ["concurrently", "npm:7.0.0"], + ["chalk", "npm:4.1.2"], + ["date-fns", "npm:2.28.0"], + ["lodash", "npm:4.17.21"], + ["rxjs", "npm:6.6.7"], + ["spawn-command", "npm:0.0.2"], + ["supports-color", "npm:8.1.1"], + ["tree-kill", "npm:1.2.2"], + ["yargs", "npm:16.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["configstore", [ + ["npm:5.0.1", { + "packageLocation": "./.yarn/cache/configstore-npm-5.0.1-739433cdc5-60ef65d493.zip/node_modules/configstore/", + "packageDependencies": [ + ["configstore", "npm:5.0.1"], + ["dot-prop", "npm:5.3.0"], + ["graceful-fs", "npm:4.2.10"], + ["make-dir", "npm:3.1.0"], + ["unique-string", "npm:2.0.0"], + ["write-file-atomic", "npm:3.0.3"], + ["xdg-basedir", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["confusing-browser-globals", [ + ["npm:1.0.10", { + "packageLocation": "./.yarn/cache/confusing-browser-globals-npm-1.0.10-ecb768852b-7ccdc44c2c.zip/node_modules/confusing-browser-globals/", + "packageDependencies": [ + ["confusing-browser-globals", "npm:1.0.10"] + ], + "linkType": "HARD", + }] + ]], + ["connect", [ + ["npm:3.7.0", { + "packageLocation": "./.yarn/cache/connect-npm-3.7.0-25ccb085cc-96e1c4effc.zip/node_modules/connect/", + "packageDependencies": [ + ["connect", "npm:3.7.0"], + ["debug", "virtual:0684f4c444aee59cc66233fcce3e4e9a25ed7e35886aed11393a5d4d03c3e7ec6f43fe70e91d783fa0717aa30724ce3a9a32ae0cd75006d103d8f81d5b9758c1#npm:2.6.9"], + ["finalhandler", "npm:1.1.2"], + ["parseurl", "npm:1.3.3"], + ["utils-merge", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["console-browserify", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/console-browserify-npm-1.2.0-5619eeb6ff-226591eeff.zip/node_modules/console-browserify/", + "packageDependencies": [ + ["console-browserify", "npm:1.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["console-control-strings", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/console-control-strings-npm-1.1.0-e3160e5275-8755d76787.zip/node_modules/console-control-strings/", + "packageDependencies": [ + ["console-control-strings", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["console-table-printer", [ + ["npm:2.11.0", { + "packageLocation": "./.yarn/cache/console-table-printer-npm-2.11.0-5c300077b5-125797e3b9.zip/node_modules/console-table-printer/", + "packageDependencies": [ + ["console-table-printer", "npm:2.11.0"], + ["simple-wcswidth", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["constants-browserify", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/constants-browserify-npm-1.0.0-b9a9bcfe4b-f7ac8c6d0b.zip/node_modules/constants-browserify/", + "packageDependencies": [ + ["constants-browserify", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["content-type", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/content-type-npm-1.0.4-3b1a5ca16b-3d93585fda.zip/node_modules/content-type/", + "packageDependencies": [ + ["content-type", "npm:1.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog", [ + ["npm:3.1.24", { + "packageLocation": "./.yarn/cache/conventional-changelog-npm-3.1.24-11de891016-54253a3e37.zip/node_modules/conventional-changelog/", + "packageDependencies": [ + ["conventional-changelog", "npm:3.1.24"], + ["conventional-changelog-angular", "npm:5.0.13"], + ["conventional-changelog-atom", "npm:2.0.8"], + ["conventional-changelog-codemirror", "npm:2.0.8"], + ["conventional-changelog-conventionalcommits", "npm:4.6.1"], + ["conventional-changelog-core", "npm:4.2.4"], + ["conventional-changelog-ember", "npm:2.0.9"], + ["conventional-changelog-eslint", "npm:3.0.9"], + ["conventional-changelog-express", "npm:2.0.6"], + ["conventional-changelog-jquery", "npm:3.0.11"], + ["conventional-changelog-jshint", "npm:2.0.9"], + ["conventional-changelog-preset-loader", "npm:2.3.4"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog-angular", [ + ["npm:5.0.13", { + "packageLocation": "./.yarn/cache/conventional-changelog-angular-npm-5.0.13-50e4a302c4-6ed4972fce.zip/node_modules/conventional-changelog-angular/", + "packageDependencies": [ + ["conventional-changelog-angular", "npm:5.0.13"], + ["compare-func", "npm:2.0.0"], + ["q", "npm:1.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog-atom", [ + ["npm:2.0.8", { + "packageLocation": "./.yarn/cache/conventional-changelog-atom-npm-2.0.8-ab61571c15-12ecbd928f.zip/node_modules/conventional-changelog-atom/", + "packageDependencies": [ + ["conventional-changelog-atom", "npm:2.0.8"], + ["q", "npm:1.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog-codemirror", [ + ["npm:2.0.8", { + "packageLocation": "./.yarn/cache/conventional-changelog-codemirror-npm-2.0.8-342d72f6a3-cf331db40c.zip/node_modules/conventional-changelog-codemirror/", + "packageDependencies": [ + ["conventional-changelog-codemirror", "npm:2.0.8"], + ["q", "npm:1.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog-conventionalcommits", [ + ["npm:4.6.1", { + "packageLocation": "./.yarn/cache/conventional-changelog-conventionalcommits-npm-4.6.1-030ed159a8-f866616c8f.zip/node_modules/conventional-changelog-conventionalcommits/", + "packageDependencies": [ + ["conventional-changelog-conventionalcommits", "npm:4.6.1"], + ["compare-func", "npm:2.0.0"], + ["lodash", "npm:4.17.21"], + ["q", "npm:1.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog-core", [ + ["npm:4.2.4", { + "packageLocation": "./.yarn/cache/conventional-changelog-core-npm-4.2.4-3507358941-56d5194040.zip/node_modules/conventional-changelog-core/", + "packageDependencies": [ + ["conventional-changelog-core", "npm:4.2.4"], + ["add-stream", "npm:1.0.0"], + ["conventional-changelog-writer", "npm:5.0.0"], + ["conventional-commits-parser", "npm:3.2.3"], + ["dateformat", "npm:3.0.3"], + ["get-pkg-repo", "npm:4.2.1"], + ["git-raw-commits", "npm:2.0.10"], + ["git-remote-origin-url", "npm:2.0.0"], + ["git-semver-tags", "npm:4.1.1"], + ["lodash", "npm:4.17.21"], + ["normalize-package-data", "npm:3.0.3"], + ["q", "npm:1.5.1"], + ["read-pkg", "npm:3.0.0"], + ["read-pkg-up", "npm:3.0.0"], + ["through2", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog-dash", [ + ["https://github.com/dashevo/conventional-changelog-dash.git#commit=3d4d77e2cea876a27b92641c28b15aedf13eb788", { + "packageLocation": "./.yarn/cache/conventional-changelog-dash-https-4455a1a41e-98199fb767.zip/node_modules/conventional-changelog-dash/", + "packageDependencies": [ + ["conventional-changelog-dash", "https://github.com/dashevo/conventional-changelog-dash.git#commit=3d4d77e2cea876a27b92641c28b15aedf13eb788"], + ["compare-func", "npm:2.0.0"], + ["lodash", "npm:4.17.21"], + ["q", "npm:1.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog-ember", [ + ["npm:2.0.9", { + "packageLocation": "./.yarn/cache/conventional-changelog-ember-npm-2.0.9-2276834930-30c7bd48ce.zip/node_modules/conventional-changelog-ember/", + "packageDependencies": [ + ["conventional-changelog-ember", "npm:2.0.9"], + ["q", "npm:1.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog-eslint", [ + ["npm:3.0.9", { + "packageLocation": "./.yarn/cache/conventional-changelog-eslint-npm-3.0.9-62c523a901-402ae73a8c.zip/node_modules/conventional-changelog-eslint/", + "packageDependencies": [ + ["conventional-changelog-eslint", "npm:3.0.9"], + ["q", "npm:1.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog-express", [ + ["npm:2.0.6", { + "packageLocation": "./.yarn/cache/conventional-changelog-express-npm-2.0.6-8a37ff0369-c139fa9878.zip/node_modules/conventional-changelog-express/", + "packageDependencies": [ + ["conventional-changelog-express", "npm:2.0.6"], + ["q", "npm:1.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog-jquery", [ + ["npm:3.0.11", { + "packageLocation": "./.yarn/cache/conventional-changelog-jquery-npm-3.0.11-d4ff10c6e2-df1145467c.zip/node_modules/conventional-changelog-jquery/", + "packageDependencies": [ + ["conventional-changelog-jquery", "npm:3.0.11"], + ["q", "npm:1.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog-jshint", [ + ["npm:2.0.9", { + "packageLocation": "./.yarn/cache/conventional-changelog-jshint-npm-2.0.9-ef6b791bee-ec96144b75.zip/node_modules/conventional-changelog-jshint/", + "packageDependencies": [ + ["conventional-changelog-jshint", "npm:2.0.9"], + ["compare-func", "npm:2.0.0"], + ["q", "npm:1.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog-preset-loader", [ + ["npm:2.3.4", { + "packageLocation": "./.yarn/cache/conventional-changelog-preset-loader-npm-2.3.4-a907f2e49a-23a889b7fc.zip/node_modules/conventional-changelog-preset-loader/", + "packageDependencies": [ + ["conventional-changelog-preset-loader", "npm:2.3.4"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-changelog-writer", [ + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/conventional-changelog-writer-npm-5.0.0-acebc38f2a-c310b949d3.zip/node_modules/conventional-changelog-writer/", + "packageDependencies": [ + ["conventional-changelog-writer", "npm:5.0.0"], + ["conventional-commits-filter", "npm:2.0.7"], + ["dateformat", "npm:3.0.3"], + ["handlebars", "npm:4.7.7"], + ["json-stringify-safe", "npm:5.0.1"], + ["lodash", "npm:4.17.21"], + ["meow", "npm:8.1.2"], + ["semver", "npm:6.3.0"], + ["split", "npm:1.0.1"], + ["through2", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-commits-filter", [ + ["npm:2.0.7", { + "packageLocation": "./.yarn/cache/conventional-commits-filter-npm-2.0.7-8762ee3bfa-feb567f680.zip/node_modules/conventional-commits-filter/", + "packageDependencies": [ + ["conventional-commits-filter", "npm:2.0.7"], + ["lodash.ismatch", "npm:4.4.0"], + ["modify-values", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["conventional-commits-parser", [ + ["npm:3.2.3", { + "packageLocation": "./.yarn/cache/conventional-commits-parser-npm-3.2.3-f108fda552-0f57b5cb7c.zip/node_modules/conventional-commits-parser/", + "packageDependencies": [ + ["conventional-commits-parser", "npm:3.2.3"], + ["JSONStream", "npm:1.3.5"], + ["is-text-path", "npm:1.0.1"], + ["lodash", "npm:4.17.21"], + ["meow", "npm:8.1.2"], + ["split2", "npm:3.2.2"], + ["through2", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["convert-source-map", [ + ["npm:1.1.3", { + "packageLocation": "./.yarn/cache/convert-source-map-npm-1.1.3-7f1bfeabd4-0ed6bdecd3.zip/node_modules/convert-source-map/", + "packageDependencies": [ + ["convert-source-map", "npm:1.1.3"] + ], + "linkType": "HARD", + }], + ["npm:1.8.0", { + "packageLocation": "./.yarn/cache/convert-source-map-npm-1.8.0-037f671dde-985d974a2d.zip/node_modules/convert-source-map/", + "packageDependencies": [ + ["convert-source-map", "npm:1.8.0"], + ["safe-buffer", "npm:5.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["cookie", [ + ["npm:0.4.1", { + "packageLocation": "./.yarn/cache/cookie-npm-0.4.1-cc5e2ebb42-bd7c47f5d9.zip/node_modules/cookie/", + "packageDependencies": [ + ["cookie", "npm:0.4.1"] + ], + "linkType": "HARD", + }] + ]], + ["core-js", [ + ["npm:3.19.1", { + "packageLocation": "./.yarn/unplugged/core-js-npm-3.19.1-772a85cbf5/node_modules/core-js/", + "packageDependencies": [ + ["core-js", "npm:3.19.1"] + ], + "linkType": "HARD", + }] + ]], + ["core-js-compat", [ + ["npm:3.19.1", { + "packageLocation": "./.yarn/cache/core-js-compat-npm-3.19.1-fbc4223527-ed302c9981.zip/node_modules/core-js-compat/", + "packageDependencies": [ + ["core-js-compat", "npm:3.19.1"], + ["browserslist", "npm:4.18.1"], + ["semver", "npm:7.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["core-util-is", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/core-util-is-npm-1.0.2-9fc2b94dc3-7a4c925b49.zip/node_modules/core-util-is/", + "packageDependencies": [ + ["core-util-is", "npm:1.0.2"] + ], + "linkType": "HARD", + }], + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/core-util-is-npm-1.0.3-ca74b76c90-9de8597363.zip/node_modules/core-util-is/", + "packageDependencies": [ + ["core-util-is", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["cors", [ + ["npm:2.8.5", { + "packageLocation": "./.yarn/cache/cors-npm-2.8.5-c9935a2d12-ced838404c.zip/node_modules/cors/", + "packageDependencies": [ + ["cors", "npm:2.8.5"], + ["object-assign", "npm:4.1.1"], + ["vary", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["cpu-features", [ + ["npm:0.0.2", { + "packageLocation": "./.yarn/unplugged/cpu-features-npm-0.0.2-b27e7998ec/node_modules/cpu-features/", + "packageDependencies": [ + ["cpu-features", "npm:0.0.2"], + ["nan", "npm:2.15.0"], + ["node-gyp", "npm:8.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["create-ecdh", [ + ["npm:4.0.4", { + "packageLocation": "./.yarn/cache/create-ecdh-npm-4.0.4-1048ce2035-0dd7fca971.zip/node_modules/create-ecdh/", + "packageDependencies": [ + ["create-ecdh", "npm:4.0.4"], + ["bn.js", "npm:4.12.0"], + ["elliptic", "npm:6.5.3"] + ], + "linkType": "HARD", + }] + ]], + ["create-hash", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/create-hash-npm-1.2.0-afd048e1ce-02a6ae3bb9.zip/node_modules/create-hash/", + "packageDependencies": [ + ["create-hash", "npm:1.2.0"], + ["cipher-base", "npm:1.0.4"], + ["inherits", "npm:2.0.4"], + ["md5.js", "npm:1.3.5"], + ["ripemd160", "npm:2.0.2"], + ["sha.js", "npm:2.4.11"] + ], + "linkType": "HARD", + }] + ]], + ["create-hmac", [ + ["npm:1.1.7", { + "packageLocation": "./.yarn/cache/create-hmac-npm-1.1.7-b4ef32668a-ba12bb2257.zip/node_modules/create-hmac/", + "packageDependencies": [ + ["create-hmac", "npm:1.1.7"], + ["cipher-base", "npm:1.0.4"], + ["create-hash", "npm:1.2.0"], + ["inherits", "npm:2.0.4"], + ["ripemd160", "npm:2.0.2"], + ["safe-buffer", "npm:5.2.1"], + ["sha.js", "npm:2.4.11"] + ], + "linkType": "HARD", + }] + ]], + ["create-require", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/create-require-npm-1.1.1-839884ca2e-a9a1503d43.zip/node_modules/create-require/", + "packageDependencies": [ + ["create-require", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["cross-spawn", [ + ["npm:6.0.5", { + "packageLocation": "./.yarn/cache/cross-spawn-npm-6.0.5-2deab6c280-f893bb0d96.zip/node_modules/cross-spawn/", + "packageDependencies": [ + ["cross-spawn", "npm:6.0.5"], + ["nice-try", "npm:1.0.5"], + ["path-key", "npm:2.0.1"], + ["semver", "npm:5.7.1"], + ["shebang-command", "npm:1.2.0"], + ["which", "npm:1.3.1"] + ], + "linkType": "HARD", + }], + ["npm:7.0.3", { + "packageLocation": "./.yarn/cache/cross-spawn-npm-7.0.3-e4ff3e65b3-671cc7c728.zip/node_modules/cross-spawn/", + "packageDependencies": [ + ["cross-spawn", "npm:7.0.3"], + ["path-key", "npm:3.1.1"], + ["shebang-command", "npm:2.0.0"], + ["which", "npm:2.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["crypto-browserify", [ + ["npm:3.12.0", { + "packageLocation": "./.yarn/cache/crypto-browserify-npm-3.12.0-bed454fef0-c1609af826.zip/node_modules/crypto-browserify/", + "packageDependencies": [ + ["crypto-browserify", "npm:3.12.0"], + ["browserify-cipher", "npm:1.0.1"], + ["browserify-sign", "npm:4.2.1"], + ["create-ecdh", "npm:4.0.4"], + ["create-hash", "npm:1.2.0"], + ["create-hmac", "npm:1.1.7"], + ["diffie-hellman", "npm:5.0.3"], + ["inherits", "npm:2.0.4"], + ["pbkdf2", "npm:3.1.2"], + ["public-encrypt", "npm:4.0.3"], + ["randombytes", "npm:2.1.0"], + ["randomfill", "npm:1.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["crypto-js", [ + ["npm:4.1.1", { + "packageLocation": "./.yarn/cache/crypto-js-npm-4.1.1-38a3b8c19d-b3747c12ee.zip/node_modules/crypto-js/", + "packageDependencies": [ + ["crypto-js", "npm:4.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["crypto-random-string", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/crypto-random-string-npm-2.0.0-8ab47992ef-0283879f55.zip/node_modules/crypto-random-string/", + "packageDependencies": [ + ["crypto-random-string", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["custom-event", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/custom-event-npm-1.0.1-6693c8e298-334f48a6d5.zip/node_modules/custom-event/", + "packageDependencies": [ + ["custom-event", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["dargs", [ + ["npm:7.0.0", { + "packageLocation": "./.yarn/cache/dargs-npm-7.0.0-62701e0c7a-b8f1e3cba5.zip/node_modules/dargs/", + "packageDependencies": [ + ["dargs", "npm:7.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["dash", [ + ["workspace:packages/js-dash-sdk", { + "packageLocation": "./packages/js-dash-sdk/", + "packageDependencies": [ + ["dash", "workspace:packages/js-dash-sdk"], + ["@dashevo/dapi-client", "workspace:packages/js-dapi-client"], + ["@dashevo/dashcore-lib", "npm:0.19.39"], + ["@dashevo/dashpay-contract", "workspace:packages/dashpay-contract"], + ["@dashevo/dpns-contract", "workspace:packages/dpns-contract"], + ["@dashevo/dpp", "workspace:packages/js-dpp"], + ["@dashevo/grpc-common", "workspace:packages/js-grpc-common"], + ["@dashevo/masternode-reward-shares-contract", "workspace:packages/masternode-reward-shares-contract"], + ["@dashevo/wallet-lib", "workspace:packages/wallet-lib"], + ["@types/chai", "npm:4.2.22"], + ["@types/dirty-chai", "npm:2.0.2"], + ["@types/expect", "npm:24.3.0"], + ["@types/mocha", "npm:8.2.3"], + ["@types/node", "npm:14.17.34"], + ["@types/sinon", "npm:9.0.11"], + ["@types/sinon-chai", "npm:3.2.5"], + ["assert", "npm:2.0.0"], + ["browserify-zlib", "npm:0.2.0"], + ["bs58", "npm:4.0.1"], + ["buffer", "npm:6.0.3"], + ["chai", "npm:4.3.4"], + ["chance", "npm:1.1.8"], + ["crypto-browserify", "npm:3.12.0"], + ["dirty-chai", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:2.0.1"], + ["dotenv-safe", "npm:8.2.0"], + ["events", "npm:3.3.0"], + ["https-browserify", "npm:1.0.0"], + ["karma", "npm:6.3.9"], + ["karma-chai", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:0.1.0"], + ["karma-chrome-launcher", "npm:3.1.0"], + ["karma-firefox-launcher", "npm:2.1.2"], + ["karma-mocha", "npm:2.0.1"], + ["karma-mocha-reporter", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.2.5"], + ["karma-webpack", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.0.0"], + ["mocha", "npm:9.1.3"], + ["net", "npm:1.0.2"], + ["node-inspect-extracted", "npm:1.0.8"], + ["nodemon", "npm:2.0.15"], + ["os-browserify", "npm:0.3.0"], + ["path-browserify", "npm:1.0.1"], + ["process", "npm:0.11.10"], + ["rimraf", "npm:3.0.2"], + ["sinon", "npm:11.1.2"], + ["sinon-chai", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:3.7.0"], + ["stream-browserify", "npm:3.0.0"], + ["stream-http", "npm:3.2.0"], + ["string_decoder", "npm:1.3.0"], + ["terser-webpack-plugin", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.3.3"], + ["tls", "npm:0.0.1"], + ["ts-loader", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:8.3.0"], + ["ts-mocha", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:8.0.0"], + ["ts-mock-imports", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:1.3.8"], + ["ts-node", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:10.4.0"], + ["typescript", "patch:typescript@npm%3A3.9.10#~builtin::version=3.9.10&hash=ddd1e8"], + ["url", "npm:0.11.0"], + ["util", "npm:0.12.4"], + ["webpack", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.64.1"], + ["webpack-cli", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:4.9.1"] + ], + "linkType": "SOFT", + }] + ]], + ["dash-ast", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/dash-ast-npm-1.0.0-2481bb8f5a-db59e5e275.zip/node_modules/dash-ast/", + "packageDependencies": [ + ["dash-ast", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["dashdash", [ + ["npm:1.14.1", { + "packageLocation": "./.yarn/cache/dashdash-npm-1.14.1-be8f10a286-3634c24957.zip/node_modules/dashdash/", + "packageDependencies": [ + ["dashdash", "npm:1.14.1"], + ["assert-plus", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["dashmate", [ + ["workspace:packages/dashmate", { + "packageLocation": "./packages/dashmate/", + "packageDependencies": [ + ["dashmate", "workspace:packages/dashmate"], + ["@dashevo/dashcore-lib", "npm:0.19.39"], + ["@dashevo/dashd-rpc", "npm:2.3.2"], + ["@dashevo/dashpay-contract", "workspace:packages/dashpay-contract"], + ["@dashevo/docker-compose", "npm:0.24.1"], + ["@dashevo/dpns-contract", "workspace:packages/dpns-contract"], + ["@dashevo/dpp", "workspace:packages/js-dpp"], + ["@dashevo/feature-flags-contract", "workspace:packages/feature-flags-contract"], + ["@dashevo/masternode-reward-shares-contract", "workspace:packages/masternode-reward-shares-contract"], + ["@dashevo/wallet-lib", "workspace:packages/wallet-lib"], + ["@oclif/core", "npm:1.3.4"], + ["@oclif/plugin-help", "npm:5.1.11"], + ["ajv", "npm:8.8.1"], + ["ajv-formats", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.1.1"], + ["awilix", "npm:4.3.4"], + ["bls-signatures", "npm:0.2.5"], + ["chalk", "npm:4.1.2"], + ["dash", "workspace:packages/js-dash-sdk"], + ["dockerode", "npm:3.3.1"], + ["dot", "npm:1.1.3"], + ["dotenv", "npm:8.6.0"], + ["enquirer", "npm:2.3.6"], + ["eslint", "npm:7.32.0"], + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["glob", "npm:7.2.0"], + ["globby", "npm:11.1.0"], + ["hasbin", "npm:1.2.3"], + ["jayson", "npm:3.6.5"], + ["listr2", "virtual:880cda903c2a2be387819a3f857d21494004437a03c92969b9853f7bdeebdfed08d417e68364ee9e158338603a6d78d690c457a55ab11e56398bc10f0ad232fc#npm:3.5.0"], + ["lodash.clonedeep", "npm:4.5.0"], + ["lodash.get", "npm:4.4.2"], + ["lodash.isequal", "npm:4.5.0"], + ["lodash.merge", "npm:4.6.2"], + ["lodash.set", "npm:4.3.2"], + ["memory-streams", "npm:0.1.3"], + ["node-fetch", "virtual:25a5f5382d53dbf298bf7a1191760bc2e0a523a619eeb0e667b99a8649e8ad183f9e2e0b45f6fb831b92f4078b61622aa567cf79565f6aa5af9597e3c84864f6#npm:2.6.7"], + ["node-graceful", "npm:3.1.0"], + ["oclif", "npm:2.4.5"], + ["pretty-bytes", "npm:5.6.0"], + ["pretty-ms", "npm:7.0.1"], + ["public-ip", "npm:4.0.4"], + ["rxjs", "npm:6.6.7"], + ["semver", "npm:7.3.5"], + ["strip-ansi", "npm:6.0.1"], + ["table", "npm:5.4.6"] + ], + "linkType": "SOFT", + }] + ]], + ["date-fns", [ + ["npm:2.28.0", { + "packageLocation": "./.yarn/cache/date-fns-npm-2.28.0-c19c5add1b-a0516b2e4f.zip/node_modules/date-fns/", + "packageDependencies": [ + ["date-fns", "npm:2.28.0"] + ], + "linkType": "HARD", + }] + ]], + ["date-format", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/date-format-npm-2.1.0-6d206457ad-ff2c80c760.zip/node_modules/date-format/", + "packageDependencies": [ + ["date-format", "npm:2.1.0"] + ], + "linkType": "HARD", + }], + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/date-format-npm-3.0.0-da04d77f82-9e1d224460.zip/node_modules/date-format/", + "packageDependencies": [ + ["date-format", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["dateformat", [ + ["npm:3.0.3", { + "packageLocation": "./.yarn/cache/dateformat-npm-3.0.3-ed02e5ddbd-ca4911148a.zip/node_modules/dateformat/", + "packageDependencies": [ + ["dateformat", "npm:3.0.3"] + ], + "linkType": "HARD", + }], + ["npm:4.6.3", { + "packageLocation": "./.yarn/cache/dateformat-npm-4.6.3-aa1a4cb7f9-c3aa0617c0.zip/node_modules/dateformat/", + "packageDependencies": [ + ["dateformat", "npm:4.6.3"] + ], + "linkType": "HARD", + }] + ]], + ["debug", [ + ["npm:2.6.9", { + "packageLocation": "./.yarn/cache/debug-npm-2.6.9-7d4cb597dc-d2f51589ca.zip/node_modules/debug/", + "packageDependencies": [ + ["debug", "npm:2.6.9"] + ], + "linkType": "SOFT", + }], + ["npm:3.2.7", { + "packageLocation": "./.yarn/cache/debug-npm-3.2.7-754e818c7a-b3d8c59407.zip/node_modules/debug/", + "packageDependencies": [ + ["debug", "npm:3.2.7"] + ], + "linkType": "SOFT", + }], + ["npm:4.3.2", { + "packageLocation": "./.yarn/cache/debug-npm-4.3.2-f0148b6afe-820ea160e2.zip/node_modules/debug/", + "packageDependencies": [ + ["debug", "npm:4.3.2"] + ], + "linkType": "SOFT", + }], + ["npm:4.3.3", { + "packageLocation": "./.yarn/cache/debug-npm-4.3.3-710fd4cc7f-14472d56fe.zip/node_modules/debug/", + "packageDependencies": [ + ["debug", "npm:4.3.3"] + ], + "linkType": "SOFT", + }], + ["virtual:0684f4c444aee59cc66233fcce3e4e9a25ed7e35886aed11393a5d4d03c3e7ec6f43fe70e91d783fa0717aa30724ce3a9a32ae0cd75006d103d8f81d5b9758c1#npm:2.6.9", { + "packageLocation": "./.yarn/__virtual__/debug-virtual-b88361380f/0/cache/debug-npm-2.6.9-7d4cb597dc-d2f51589ca.zip/node_modules/debug/", + "packageDependencies": [ + ["debug", "virtual:0684f4c444aee59cc66233fcce3e4e9a25ed7e35886aed11393a5d4d03c3e7ec6f43fe70e91d783fa0717aa30724ce3a9a32ae0cd75006d103d8f81d5b9758c1#npm:2.6.9"], + ["@types/supports-color", null], + ["ms", "npm:2.0.0"], + ["supports-color", null] + ], + "packagePeers": [ + "@types/supports-color", + "supports-color" + ], + "linkType": "HARD", + }], + ["virtual:5e88e7aef540459ed10e9f06791cff75f35e8e44e625132fefff7246f500bd5b319d818390ce1930a4b11fbbce9102b75ea690c8a75bc6e0969fe6dd59e3c283#npm:3.2.7", { + "packageLocation": "./.yarn/__virtual__/debug-virtual-cf9851a345/0/cache/debug-npm-3.2.7-754e818c7a-b3d8c59407.zip/node_modules/debug/", + "packageDependencies": [ + ["debug", "virtual:5e88e7aef540459ed10e9f06791cff75f35e8e44e625132fefff7246f500bd5b319d818390ce1930a4b11fbbce9102b75ea690c8a75bc6e0969fe6dd59e3c283#npm:3.2.7"], + ["@types/supports-color", null], + ["ms", "npm:2.1.3"], + ["supports-color", "npm:5.5.0"] + ], + "packagePeers": [ + "@types/supports-color", + "supports-color" + ], + "linkType": "HARD", + }], + ["virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3", { + "packageLocation": "./.yarn/__virtual__/debug-virtual-abb0bc5aef/0/cache/debug-npm-4.3.3-710fd4cc7f-14472d56fe.zip/node_modules/debug/", + "packageDependencies": [ + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["@types/supports-color", null], + ["ms", "npm:2.1.2"], + ["supports-color", null] + ], + "packagePeers": [ + "@types/supports-color", + "supports-color" + ], + "linkType": "HARD", + }], + ["virtual:cf8df742ce8e4e935902993bcfceab61a23301352e0174959d2524c3ce25388a4d3477170dec0ebaf85f7f409c4c58568061d13cf886536628fdbe79510fc4de#npm:4.3.2", { + "packageLocation": "./.yarn/__virtual__/debug-virtual-81e1236598/0/cache/debug-npm-4.3.2-f0148b6afe-820ea160e2.zip/node_modules/debug/", + "packageDependencies": [ + ["debug", "virtual:cf8df742ce8e4e935902993bcfceab61a23301352e0174959d2524c3ce25388a4d3477170dec0ebaf85f7f409c4c58568061d13cf886536628fdbe79510fc4de#npm:4.3.2"], + ["@types/supports-color", null], + ["ms", "npm:2.1.2"], + ["supports-color", "npm:8.1.1"] + ], + "packagePeers": [ + "@types/supports-color", + "supports-color" + ], + "linkType": "HARD", + }], + ["virtual:d9426786c635bc4b52511d6cc4b56156f50d780a698c0e20fc6caf10d3be51cbf176e79cff882f4d42a23ff4d0f89fe94222849578214e7fbae0f2754c82af02#npm:3.2.7", { + "packageLocation": "./.yarn/__virtual__/debug-virtual-b810fb6338/0/cache/debug-npm-3.2.7-754e818c7a-b3d8c59407.zip/node_modules/debug/", + "packageDependencies": [ + ["debug", "virtual:d9426786c635bc4b52511d6cc4b56156f50d780a698c0e20fc6caf10d3be51cbf176e79cff882f4d42a23ff4d0f89fe94222849578214e7fbae0f2754c82af02#npm:3.2.7"], + ["@types/supports-color", null], + ["ms", "npm:2.1.3"], + ["supports-color", null] + ], + "packagePeers": [ + "@types/supports-color", + "supports-color" + ], + "linkType": "HARD", + }], + ["virtual:e0bcdb30fd626f99ea6779721a0a71e37a1e0c50c9b9efdc8c529c07facadd7c604bf4988d80d980de298e61ede04b9695580df0baa812da80a4ce3b8a002d33#npm:4.3.3", { + "packageLocation": "./.yarn/__virtual__/debug-virtual-da65a63157/0/cache/debug-npm-4.3.3-710fd4cc7f-14472d56fe.zip/node_modules/debug/", + "packageDependencies": [ + ["debug", "virtual:e0bcdb30fd626f99ea6779721a0a71e37a1e0c50c9b9efdc8c529c07facadd7c604bf4988d80d980de298e61ede04b9695580df0baa812da80a4ce3b8a002d33#npm:4.3.3"], + ["@types/supports-color", null], + ["ms", "npm:2.1.2"], + ["supports-color", "npm:8.1.1"] + ], + "packagePeers": [ + "@types/supports-color", + "supports-color" + ], + "linkType": "HARD", + }] + ]], + ["debuglog", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/debuglog-npm-1.0.1-c553c84ea5-970679f2eb.zip/node_modules/debuglog/", + "packageDependencies": [ + ["debuglog", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["decamelize", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/decamelize-npm-1.2.0-c5a2fdc622-ad8c51a7e7.zip/node_modules/decamelize/", + "packageDependencies": [ + ["decamelize", "npm:1.2.0"] + ], + "linkType": "HARD", + }], + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/decamelize-npm-4.0.0-12410e3409-b7d09b8265.zip/node_modules/decamelize/", + "packageDependencies": [ + ["decamelize", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["decamelize-keys", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/decamelize-keys-npm-1.1.0-75168ffadd-8bc5d32e03.zip/node_modules/decamelize-keys/", + "packageDependencies": [ + ["decamelize-keys", "npm:1.1.0"], + ["decamelize", "npm:1.2.0"], + ["map-obj", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["decimal.js", [ + ["npm:10.3.1", { + "packageLocation": "./.yarn/cache/decimal.js-npm-10.3.1-797c736b6c-0351ac9f05.zip/node_modules/decimal.js/", + "packageDependencies": [ + ["decimal.js", "npm:10.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["decompress-response", [ + ["npm:3.3.0", { + "packageLocation": "./.yarn/cache/decompress-response-npm-3.3.0-6e7b6375c3-952552ac3b.zip/node_modules/decompress-response/", + "packageDependencies": [ + ["decompress-response", "npm:3.3.0"], + ["mimic-response", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["deep-eql", [ + ["npm:3.0.1", { + "packageLocation": "./.yarn/cache/deep-eql-npm-3.0.1-9a66c09c65-4f4c9fb79e.zip/node_modules/deep-eql/", + "packageDependencies": [ + ["deep-eql", "npm:3.0.1"], + ["type-detect", "npm:4.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["deep-extend", [ + ["npm:0.6.0", { + "packageLocation": "./.yarn/cache/deep-extend-npm-0.6.0-e182924219-7be7e5a8d4.zip/node_modules/deep-extend/", + "packageDependencies": [ + ["deep-extend", "npm:0.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["deep-is", [ + ["npm:0.1.4", { + "packageLocation": "./.yarn/cache/deep-is-npm-0.1.4-88938b5a67-edb65dd0d7.zip/node_modules/deep-is/", + "packageDependencies": [ + ["deep-is", "npm:0.1.4"] + ], + "linkType": "HARD", + }] + ]], + ["default-require-extensions", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/default-require-extensions-npm-3.0.0-40586718d6-0b5bdb6786.zip/node_modules/default-require-extensions/", + "packageDependencies": [ + ["default-require-extensions", "npm:3.0.0"], + ["strip-bom", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["defaults", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/defaults-npm-1.0.3-e829107b9e-96e2112da6.zip/node_modules/defaults/", + "packageDependencies": [ + ["defaults", "npm:1.0.3"], + ["clone", "npm:1.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["defer-to-connect", [ + ["npm:1.1.3", { + "packageLocation": "./.yarn/cache/defer-to-connect-npm-1.1.3-5887885147-9491b301dc.zip/node_modules/defer-to-connect/", + "packageDependencies": [ + ["defer-to-connect", "npm:1.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["deferred-leveldown", [ + ["npm:5.3.0", { + "packageLocation": "./.yarn/cache/deferred-leveldown-npm-5.3.0-01247ab5af-5631e15352.zip/node_modules/deferred-leveldown/", + "packageDependencies": [ + ["deferred-leveldown", "npm:5.3.0"], + ["abstract-leveldown", "npm:6.2.3"], + ["inherits", "npm:2.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["define-properties", [ + ["npm:1.1.3", { + "packageLocation": "./.yarn/cache/define-properties-npm-1.1.3-0f3115e2b9-da80dba55d.zip/node_modules/define-properties/", + "packageDependencies": [ + ["define-properties", "npm:1.1.3"], + ["object-keys", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["defined", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/defined-npm-1.0.0-891782ba77-77672997c5.zip/node_modules/defined/", + "packageDependencies": [ + ["defined", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["delay", [ + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/delay-npm-5.0.0-1d1c758b46-62f151151e.zip/node_modules/delay/", + "packageDependencies": [ + ["delay", "npm:5.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["delayed-stream", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/delayed-stream-npm-1.0.0-c5a4c4cc02-46fe6e83e2.zip/node_modules/delayed-stream/", + "packageDependencies": [ + ["delayed-stream", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["delegates", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/delegates-npm-1.0.0-9b1942d75f-a51744d9b5.zip/node_modules/delegates/", + "packageDependencies": [ + ["delegates", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["denque", [ + ["npm:1.5.1", { + "packageLocation": "./.yarn/cache/denque-npm-1.5.1-2dd42d2dcb-4375ad19d5.zip/node_modules/denque/", + "packageDependencies": [ + ["denque", "npm:1.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["depd", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/depd-npm-1.1.2-b0c8414da7-6b406620d2.zip/node_modules/depd/", + "packageDependencies": [ + ["depd", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["deprecation", [ + ["npm:2.3.1", { + "packageLocation": "./.yarn/cache/deprecation-npm-2.3.1-e19c92d6e7-f56a05e182.zip/node_modules/deprecation/", + "packageDependencies": [ + ["deprecation", "npm:2.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["deps-sort", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/deps-sort-npm-2.0.1-d962bf2c4d-1cbaad500a.zip/node_modules/deps-sort/", + "packageDependencies": [ + ["deps-sort", "npm:2.0.1"], + ["JSONStream", "npm:1.3.5"], + ["shasum-object", "npm:1.0.0"], + ["subarg", "npm:1.0.0"], + ["through2", "npm:2.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["des.js", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/des.js-npm-1.0.1-9f155eddb6-1ec2eedd7e.zip/node_modules/des.js/", + "packageDependencies": [ + ["des.js", "npm:1.0.1"], + ["inherits", "npm:2.0.4"], + ["minimalistic-assert", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["detect-indent", [ + ["npm:6.1.0", { + "packageLocation": "./.yarn/cache/detect-indent-npm-6.1.0-d8c441ff7a-ab953a73c7.zip/node_modules/detect-indent/", + "packageDependencies": [ + ["detect-indent", "npm:6.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["detective", [ + ["npm:5.2.0", { + "packageLocation": "./.yarn/cache/detective-npm-5.2.0-c623eb79e6-2ab266aecb.zip/node_modules/detective/", + "packageDependencies": [ + ["detective", "npm:5.2.0"], + ["acorn-node", "npm:1.8.2"], + ["defined", "npm:1.0.0"], + ["minimist", "npm:1.2.5"] + ], + "linkType": "HARD", + }] + ]], + ["dezalgo", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/dezalgo-npm-1.0.3-e2bc978ebd-8b26238db9.zip/node_modules/dezalgo/", + "packageDependencies": [ + ["dezalgo", "npm:1.0.3"], + ["asap", "npm:2.0.6"], + ["wrappy", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["di", [ + ["npm:0.0.1", { + "packageLocation": "./.yarn/cache/di-npm-0.0.1-bff5be391f-3f09a99534.zip/node_modules/di/", + "packageDependencies": [ + ["di", "npm:0.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["diff", [ + ["npm:3.5.0", { + "packageLocation": "./.yarn/cache/diff-npm-3.5.0-a321a0df19-00842950a6.zip/node_modules/diff/", + "packageDependencies": [ + ["diff", "npm:3.5.0"] + ], + "linkType": "HARD", + }], + ["npm:4.0.2", { + "packageLocation": "./.yarn/cache/diff-npm-4.0.2-73133c7102-f2c09b0ce4.zip/node_modules/diff/", + "packageDependencies": [ + ["diff", "npm:4.0.2"] + ], + "linkType": "HARD", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/diff-npm-5.0.0-ad6900db18-f19fe29284.zip/node_modules/diff/", + "packageDependencies": [ + ["diff", "npm:5.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["diff-sequences", [ + ["npm:27.0.6", { + "packageLocation": "./.yarn/cache/diff-sequences-npm-27.0.6-1eed05107b-f35ad024d4.zip/node_modules/diff-sequences/", + "packageDependencies": [ + ["diff-sequences", "npm:27.0.6"] + ], + "linkType": "HARD", + }] + ]], + ["diffie-hellman", [ + ["npm:5.0.3", { + "packageLocation": "./.yarn/cache/diffie-hellman-npm-5.0.3-cbef8f3171-0e620f3221.zip/node_modules/diffie-hellman/", + "packageDependencies": [ + ["diffie-hellman", "npm:5.0.3"], + ["bn.js", "npm:4.12.0"], + ["miller-rabin", "npm:4.0.1"], + ["randombytes", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["dir-glob", [ + ["npm:3.0.1", { + "packageLocation": "./.yarn/cache/dir-glob-npm-3.0.1-1aea628b1b-fa05e18324.zip/node_modules/dir-glob/", + "packageDependencies": [ + ["dir-glob", "npm:3.0.1"], + ["path-type", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["dirty-chai", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/dirty-chai-npm-2.0.1-acaf82c8df-1e8602e78a.zip/node_modules/dirty-chai/", + "packageDependencies": [ + ["dirty-chai", "npm:2.0.1"] + ], + "linkType": "SOFT", + }], + ["virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.0.1", { + "packageLocation": "./.yarn/__virtual__/dirty-chai-virtual-c9fb872014/0/cache/dirty-chai-npm-2.0.1-acaf82c8df-1e8602e78a.zip/node_modules/dirty-chai/", + "packageDependencies": [ + ["dirty-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.0.1"], + ["@types/chai", null], + ["chai", "npm:4.3.4"] + ], + "packagePeers": [ + "@types/chai", + "chai" + ], + "linkType": "HARD", + }], + ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:2.0.1", { + "packageLocation": "./.yarn/__virtual__/dirty-chai-virtual-776ab6e5e1/0/cache/dirty-chai-npm-2.0.1-acaf82c8df-1e8602e78a.zip/node_modules/dirty-chai/", + "packageDependencies": [ + ["dirty-chai", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:2.0.1"], + ["@types/chai", "npm:4.2.22"], + ["chai", "npm:4.3.4"] + ], + "packagePeers": [ + "@types/chai", + "chai" + ], + "linkType": "HARD", + }] + ]], + ["dns-packet", [ + ["npm:5.3.0", { + "packageLocation": "./.yarn/cache/dns-packet-npm-5.3.0-a1e660206b-ac93e0f6d4.zip/node_modules/dns-packet/", + "packageDependencies": [ + ["dns-packet", "npm:5.3.0"], + ["@leichtgewicht/ip-codec", "npm:2.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["dns-socket", [ + ["npm:4.2.2", { + "packageLocation": "./.yarn/cache/dns-socket-npm-4.2.2-2d13a1bfa6-d02b83ecc9.zip/node_modules/dns-socket/", + "packageDependencies": [ + ["dns-socket", "npm:4.2.2"], + ["dns-packet", "npm:5.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["docker-modem", [ + ["npm:3.0.3", { + "packageLocation": "./.yarn/cache/docker-modem-npm-3.0.3-5736be136e-4ad495d17a.zip/node_modules/docker-modem/", + "packageDependencies": [ + ["docker-modem", "npm:3.0.3"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["readable-stream", "npm:3.6.0"], + ["split-ca", "npm:1.0.1"], + ["ssh2", "npm:1.5.0"] + ], + "linkType": "HARD", + }] + ]], + ["dockerode", [ + ["npm:3.3.1", { + "packageLocation": "./.yarn/cache/dockerode-npm-3.3.1-77efbe3384-930162ae2d.zip/node_modules/dockerode/", + "packageDependencies": [ + ["dockerode", "npm:3.3.1"], + ["docker-modem", "npm:3.0.3"], + ["tar-fs", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["doctrine", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/doctrine-npm-2.1.0-ac15d049b7-a45e277f7f.zip/node_modules/doctrine/", + "packageDependencies": [ + ["doctrine", "npm:2.1.0"], + ["esutils", "npm:2.0.3"] + ], + "linkType": "HARD", + }], + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/doctrine-npm-3.0.0-c6f1615f04-fd7673ca77.zip/node_modules/doctrine/", + "packageDependencies": [ + ["doctrine", "npm:3.0.0"], + ["esutils", "npm:2.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["dom-serialize", [ + ["npm:2.2.1", { + "packageLocation": "./.yarn/cache/dom-serialize-npm-2.2.1-01ec16503e-48262e299a.zip/node_modules/dom-serialize/", + "packageDependencies": [ + ["dom-serialize", "npm:2.2.1"], + ["custom-event", "npm:1.0.1"], + ["ent", "npm:2.2.0"], + ["extend", "npm:3.0.2"], + ["void-elements", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["domain-browser", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/domain-browser-npm-1.2.0-d99f0de5ec-8f1235c7f4.zip/node_modules/domain-browser/", + "packageDependencies": [ + ["domain-browser", "npm:1.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["dot", [ + ["npm:1.1.3", { + "packageLocation": "./.yarn/cache/dot-npm-1.1.3-a570dedf33-9a2ecf7b5f.zip/node_modules/dot/", + "packageDependencies": [ + ["dot", "npm:1.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["dot-prop", [ + ["npm:5.3.0", { + "packageLocation": "./.yarn/cache/dot-prop-npm-5.3.0-7bf6ee1eb8-d577579009.zip/node_modules/dot-prop/", + "packageDependencies": [ + ["dot-prop", "npm:5.3.0"], + ["is-obj", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["dotenv", [ + ["npm:8.6.0", { + "packageLocation": "./.yarn/cache/dotenv-npm-8.6.0-2ce3e9f7bb-38e902c80b.zip/node_modules/dotenv/", + "packageDependencies": [ + ["dotenv", "npm:8.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["dotenv-expand", [ + ["npm:5.1.0", { + "packageLocation": "./.yarn/cache/dotenv-expand-npm-5.1.0-c3fff50eb5-8017675b7f.zip/node_modules/dotenv-expand/", + "packageDependencies": [ + ["dotenv-expand", "npm:5.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["dotenv-safe", [ + ["npm:8.2.0", { + "packageLocation": "./.yarn/cache/dotenv-safe-npm-8.2.0-f1bcdebce9-8b73770330.zip/node_modules/dotenv-safe/", + "packageDependencies": [ + ["dotenv-safe", "npm:8.2.0"], + ["dotenv", "npm:8.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["duplexer2", [ + ["npm:0.1.4", { + "packageLocation": "./.yarn/cache/duplexer2-npm-0.1.4-6bca6bef12-744961f03c.zip/node_modules/duplexer2/", + "packageDependencies": [ + ["duplexer2", "npm:0.1.4"], + ["readable-stream", "npm:2.3.7"] + ], + "linkType": "HARD", + }] + ]], + ["duplexer3", [ + ["npm:0.1.4", { + "packageLocation": "./.yarn/cache/duplexer3-npm-0.1.4-361a33d994-c2fd696931.zip/node_modules/duplexer3/", + "packageDependencies": [ + ["duplexer3", "npm:0.1.4"] + ], + "linkType": "HARD", + }] + ]], + ["ecc-jsbn", [ + ["npm:0.1.2", { + "packageLocation": "./.yarn/cache/ecc-jsbn-npm-0.1.2-85b7a7be89-22fef4b620.zip/node_modules/ecc-jsbn/", + "packageDependencies": [ + ["ecc-jsbn", "npm:0.1.2"], + ["jsbn", "npm:0.1.1"], + ["safer-buffer", "npm:2.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["ee-first", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/ee-first-npm-1.1.1-33f8535b39-1b4cac778d.zip/node_modules/ee-first/", + "packageDependencies": [ + ["ee-first", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["ejs", [ + ["npm:3.1.6", { + "packageLocation": "./.yarn/cache/ejs-npm-3.1.6-03db39fd15-81a9cdea0b.zip/node_modules/ejs/", + "packageDependencies": [ + ["ejs", "npm:3.1.6"], + ["jake", "npm:10.8.2"] + ], + "linkType": "HARD", + }] + ]], + ["electron-to-chromium", [ + ["npm:1.3.903", { + "packageLocation": "./.yarn/cache/electron-to-chromium-npm-1.3.903-3e6dfabc20-0f96af03ef.zip/node_modules/electron-to-chromium/", + "packageDependencies": [ + ["electron-to-chromium", "npm:1.3.903"] + ], + "linkType": "HARD", + }] + ]], + ["elliptic", [ + ["npm:6.5.3", { + "packageLocation": "./.yarn/cache/elliptic-npm-6.5.3-783c509c01-fe1e546ed3.zip/node_modules/elliptic/", + "packageDependencies": [ + ["elliptic", "npm:6.5.3"], + ["bn.js", "npm:4.12.0"], + ["brorand", "npm:1.1.0"], + ["hash.js", "npm:1.1.7"], + ["hmac-drbg", "npm:1.0.1"], + ["inherits", "npm:2.0.4"], + ["minimalistic-assert", "npm:1.0.1"], + ["minimalistic-crypto-utils", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["emoji-regex", [ + ["npm:7.0.3", { + "packageLocation": "./.yarn/cache/emoji-regex-npm-7.0.3-cfe9479bb3-9159b2228b.zip/node_modules/emoji-regex/", + "packageDependencies": [ + ["emoji-regex", "npm:7.0.3"] + ], + "linkType": "HARD", + }], + ["npm:8.0.0", { + "packageLocation": "./.yarn/cache/emoji-regex-npm-8.0.0-213764015c-d4c5c39d5a.zip/node_modules/emoji-regex/", + "packageDependencies": [ + ["emoji-regex", "npm:8.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["emojis-list", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/emojis-list-npm-3.0.0-7faa48e6fd-ddaaa02542.zip/node_modules/emojis-list/", + "packageDependencies": [ + ["emojis-list", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["enabled", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/enabled-npm-2.0.0-bf5d96c9d8-9d256d89f4.zip/node_modules/enabled/", + "packageDependencies": [ + ["enabled", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["encodeurl", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/encodeurl-npm-1.0.2-f8c8454c41-e50e3d508c.zip/node_modules/encodeurl/", + "packageDependencies": [ + ["encodeurl", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["encoding", [ + ["npm:0.1.13", { + "packageLocation": "./.yarn/cache/encoding-npm-0.1.13-82a1837d30-bb98632f8f.zip/node_modules/encoding/", + "packageDependencies": [ + ["encoding", "npm:0.1.13"], + ["iconv-lite", "npm:0.6.3"] + ], + "linkType": "HARD", + }] + ]], + ["end-of-stream", [ + ["npm:1.4.4", { + "packageLocation": "./.yarn/cache/end-of-stream-npm-1.4.4-497fc6dee1-530a5a5a1e.zip/node_modules/end-of-stream/", + "packageDependencies": [ + ["end-of-stream", "npm:1.4.4"], + ["once", "npm:1.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["engine.io", [ + ["npm:6.1.0", { + "packageLocation": "./.yarn/cache/engine.io-npm-6.1.0-cdba019cb1-37ff47e24c.zip/node_modules/engine.io/", + "packageDependencies": [ + ["engine.io", "npm:6.1.0"], + ["@types/cookie", "npm:0.4.1"], + ["@types/cors", "npm:2.8.12"], + ["@types/node", "npm:17.0.21"], + ["accepts", "npm:1.3.7"], + ["base64id", "npm:2.0.0"], + ["cookie", "npm:0.4.1"], + ["cors", "npm:2.8.5"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["engine.io-parser", "npm:5.0.2"], + ["ws", "virtual:cdba019cb110be40f42092768543381bb2825f22f13486586cfed065b9cbe9680eec0a5effcf0de8a08dad7b560b087b2a511d35315801730378caca1b7f0ff4#npm:8.2.3"] + ], + "linkType": "HARD", + }] + ]], + ["engine.io-parser", [ + ["npm:5.0.2", { + "packageLocation": "./.yarn/cache/engine.io-parser-npm-5.0.2-884d92291e-bd65c3cdce.zip/node_modules/engine.io-parser/", + "packageDependencies": [ + ["engine.io-parser", "npm:5.0.2"], + ["base64-arraybuffer", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["enhanced-resolve", [ + ["npm:4.5.0", { + "packageLocation": "./.yarn/cache/enhanced-resolve-npm-4.5.0-1bcc7900d2-4d87488584.zip/node_modules/enhanced-resolve/", + "packageDependencies": [ + ["enhanced-resolve", "npm:4.5.0"], + ["graceful-fs", "npm:4.2.10"], + ["memory-fs", "npm:0.5.0"], + ["tapable", "npm:1.1.3"] + ], + "linkType": "HARD", + }], + ["npm:5.8.3", { + "packageLocation": "./.yarn/cache/enhanced-resolve-npm-5.8.3-24a728966e-d79fbe5311.zip/node_modules/enhanced-resolve/", + "packageDependencies": [ + ["enhanced-resolve", "npm:5.8.3"], + ["graceful-fs", "npm:4.2.10"], + ["tapable", "npm:2.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["enquirer", [ + ["npm:2.3.6", { + "packageLocation": "./.yarn/cache/enquirer-npm-2.3.6-7899175762-1c0911e14a.zip/node_modules/enquirer/", + "packageDependencies": [ + ["enquirer", "npm:2.3.6"], + ["ansi-colors", "npm:4.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["ent", [ + ["npm:2.2.0", { + "packageLocation": "./.yarn/cache/ent-npm-2.2.0-97a5f0ffb8-f588b5707d.zip/node_modules/ent/", + "packageDependencies": [ + ["ent", "npm:2.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["env-paths", [ + ["npm:2.2.1", { + "packageLocation": "./.yarn/cache/env-paths-npm-2.2.1-7c7577428c-65b5df55a8.zip/node_modules/env-paths/", + "packageDependencies": [ + ["env-paths", "npm:2.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["envinfo", [ + ["npm:7.8.1", { + "packageLocation": "./.yarn/cache/envinfo-npm-7.8.1-f320033691-de736c98d6.zip/node_modules/envinfo/", + "packageDependencies": [ + ["envinfo", "npm:7.8.1"] + ], + "linkType": "HARD", + }] + ]], + ["err-code", [ + ["npm:2.0.3", { + "packageLocation": "./.yarn/cache/err-code-npm-2.0.3-082e0ff9a7-8b7b1be20d.zip/node_modules/err-code/", + "packageDependencies": [ + ["err-code", "npm:2.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["errno", [ + ["npm:0.1.8", { + "packageLocation": "./.yarn/cache/errno-npm-0.1.8-10ebc185bf-1271f7b9fb.zip/node_modules/errno/", + "packageDependencies": [ + ["errno", "npm:0.1.8"], + ["prr", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["error", [ + ["npm:10.4.0", { + "packageLocation": "./.yarn/cache/error-npm-10.4.0-cb27050f2f-26c9ecb7af.zip/node_modules/error/", + "packageDependencies": [ + ["error", "npm:10.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["error-ex", [ + ["npm:1.3.2", { + "packageLocation": "./.yarn/cache/error-ex-npm-1.3.2-5654f80c0f-c1c2b8b65f.zip/node_modules/error-ex/", + "packageDependencies": [ + ["error-ex", "npm:1.3.2"], + ["is-arrayish", "npm:0.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["es-abstract", [ + ["npm:1.19.1", { + "packageLocation": "./.yarn/cache/es-abstract-npm-1.19.1-885c72759a-b6be841067.zip/node_modules/es-abstract/", + "packageDependencies": [ + ["es-abstract", "npm:1.19.1"], + ["call-bind", "npm:1.0.2"], + ["es-to-primitive", "npm:1.2.1"], + ["function-bind", "npm:1.1.1"], + ["get-intrinsic", "npm:1.1.1"], + ["get-symbol-description", "npm:1.0.0"], + ["has", "npm:1.0.3"], + ["has-symbols", "npm:1.0.2"], + ["internal-slot", "npm:1.0.3"], + ["is-callable", "npm:1.2.4"], + ["is-negative-zero", "npm:2.0.1"], + ["is-regex", "npm:1.1.4"], + ["is-shared-array-buffer", "npm:1.0.1"], + ["is-string", "npm:1.0.7"], + ["is-weakref", "npm:1.0.1"], + ["object-inspect", "npm:1.11.0"], + ["object-keys", "npm:1.1.1"], + ["object.assign", "npm:4.1.2"], + ["string.prototype.trimend", "npm:1.0.4"], + ["string.prototype.trimstart", "npm:1.0.4"], + ["unbox-primitive", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["es-module-lexer", [ + ["npm:0.9.3", { + "packageLocation": "./.yarn/cache/es-module-lexer-npm-0.9.3-ff6236dadb-84bbab23c3.zip/node_modules/es-module-lexer/", + "packageDependencies": [ + ["es-module-lexer", "npm:0.9.3"] + ], + "linkType": "HARD", + }] + ]], + ["es-to-primitive", [ + ["npm:1.2.1", { + "packageLocation": "./.yarn/cache/es-to-primitive-npm-1.2.1-b7a7eac6c5-4ead6671a2.zip/node_modules/es-to-primitive/", + "packageDependencies": [ + ["es-to-primitive", "npm:1.2.1"], + ["is-callable", "npm:1.2.4"], + ["is-date-object", "npm:1.0.5"], + ["is-symbol", "npm:1.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["es6-error", [ + ["npm:4.1.1", { + "packageLocation": "./.yarn/cache/es6-error-npm-4.1.1-5e8c22b20f-ae41332a51.zip/node_modules/es6-error/", + "packageDependencies": [ + ["es6-error", "npm:4.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["es6-object-assign", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/es6-object-assign-npm-1.1.0-0565318480-8d4fdf6348.zip/node_modules/es6-object-assign/", + "packageDependencies": [ + ["es6-object-assign", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["es6-promise", [ + ["npm:4.2.8", { + "packageLocation": "./.yarn/cache/es6-promise-npm-4.2.8-c9f5b11f66-95614a8887.zip/node_modules/es6-promise/", + "packageDependencies": [ + ["es6-promise", "npm:4.2.8"] + ], + "linkType": "HARD", + }] + ]], + ["es6-promisify", [ + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/es6-promisify-npm-5.0.0-3726550934-fbed9d7915.zip/node_modules/es6-promisify/", + "packageDependencies": [ + ["es6-promisify", "npm:5.0.0"], + ["es6-promise", "npm:4.2.8"] + ], + "linkType": "HARD", + }] + ]], + ["escalade", [ + ["npm:3.1.1", { + "packageLocation": "./.yarn/cache/escalade-npm-3.1.1-e02da076aa-a3e2a99f07.zip/node_modules/escalade/", + "packageDependencies": [ + ["escalade", "npm:3.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["escape-goat", [ + ["npm:2.1.1", { + "packageLocation": "./.yarn/cache/escape-goat-npm-2.1.1-2e437cf3fe-ce05c70c20.zip/node_modules/escape-goat/", + "packageDependencies": [ + ["escape-goat", "npm:2.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["escape-html", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/escape-html-npm-1.0.3-376c22ee74-6213ca9ae0.zip/node_modules/escape-html/", + "packageDependencies": [ + ["escape-html", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["escape-latex", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/escape-latex-npm-1.2.0-1481ca81a7-73a787319f.zip/node_modules/escape-latex/", + "packageDependencies": [ + ["escape-latex", "npm:1.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["escape-string-regexp", [ + ["npm:1.0.5", { + "packageLocation": "./.yarn/cache/escape-string-regexp-npm-1.0.5-3284de402f-6092fda75c.zip/node_modules/escape-string-regexp/", + "packageDependencies": [ + ["escape-string-regexp", "npm:1.0.5"] + ], + "linkType": "HARD", + }], + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/escape-string-regexp-npm-2.0.0-aef69d2a25-9f8a2d5743.zip/node_modules/escape-string-regexp/", + "packageDependencies": [ + ["escape-string-regexp", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/escape-string-regexp-npm-4.0.0-4b531d8d59-98b48897d9.zip/node_modules/escape-string-regexp/", + "packageDependencies": [ + ["escape-string-regexp", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["escodegen", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/escodegen-npm-2.0.0-6450b02925-5aa6b2966f.zip/node_modules/escodegen/", + "packageDependencies": [ + ["escodegen", "npm:2.0.0"], + ["esprima", "npm:4.0.1"], + ["estraverse", "npm:5.3.0"], + ["esutils", "npm:2.0.3"], + ["optionator", "npm:0.8.3"], + ["source-map", "npm:0.6.1"] + ], + "linkType": "HARD", + }] + ]], + ["eslint", [ + ["npm:7.32.0", { + "packageLocation": "./.yarn/cache/eslint-npm-7.32.0-e15cc6682f-cc85af9985.zip/node_modules/eslint/", + "packageDependencies": [ + ["eslint", "npm:7.32.0"], + ["@babel/code-frame", "npm:7.12.11"], + ["@eslint/eslintrc", "npm:0.4.3"], + ["@humanwhocodes/config-array", "npm:0.5.0"], + ["ajv", "npm:6.12.6"], + ["chalk", "npm:4.1.2"], + ["cross-spawn", "npm:7.0.3"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["doctrine", "npm:3.0.0"], + ["enquirer", "npm:2.3.6"], + ["escape-string-regexp", "npm:4.0.0"], + ["eslint-scope", "npm:5.1.1"], + ["eslint-utils", "npm:2.1.0"], + ["eslint-visitor-keys", "npm:2.1.0"], + ["espree", "npm:7.3.1"], + ["esquery", "npm:1.4.0"], + ["esutils", "npm:2.0.3"], + ["fast-deep-equal", "npm:3.1.3"], + ["file-entry-cache", "npm:6.0.1"], + ["functional-red-black-tree", "npm:1.0.1"], + ["glob-parent", "npm:5.1.2"], + ["globals", "npm:13.12.0"], + ["ignore", "npm:4.0.6"], + ["import-fresh", "npm:3.3.0"], + ["imurmurhash", "npm:0.1.4"], + ["is-glob", "npm:4.0.3"], + ["js-yaml", "npm:3.14.1"], + ["json-stable-stringify-without-jsonify", "npm:1.0.1"], + ["levn", "npm:0.4.1"], + ["lodash.merge", "npm:4.6.2"], + ["minimatch", "npm:3.0.4"], + ["natural-compare", "npm:1.4.0"], + ["optionator", "npm:0.9.1"], + ["progress", "npm:2.0.3"], + ["regexpp", "npm:3.2.0"], + ["semver", "npm:7.3.5"], + ["strip-ansi", "npm:6.0.1"], + ["strip-json-comments", "npm:3.1.1"], + ["table", "npm:6.7.3"], + ["text-table", "npm:0.2.0"], + ["v8-compile-cache", "npm:2.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["eslint-config-airbnb-base", [ + ["npm:14.2.1", { + "packageLocation": "./.yarn/cache/eslint-config-airbnb-base-npm-14.2.1-50131c00fb-858bea748a.zip/node_modules/eslint-config-airbnb-base/", + "packageDependencies": [ + ["eslint-config-airbnb-base", "npm:14.2.1"] + ], + "linkType": "SOFT", + }], + ["virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1", { + "packageLocation": "./.yarn/__virtual__/eslint-config-airbnb-base-virtual-13e91749d9/0/cache/eslint-config-airbnb-base-npm-14.2.1-50131c00fb-858bea748a.zip/node_modules/eslint-config-airbnb-base/", + "packageDependencies": [ + ["eslint-config-airbnb-base", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:14.2.1"], + ["@types/eslint", null], + ["@types/eslint-plugin-import", null], + ["confusing-browser-globals", "npm:1.0.10"], + ["eslint", "npm:7.32.0"], + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["object.assign", "npm:4.1.2"], + ["object.entries", "npm:1.1.5"] + ], + "packagePeers": [ + "@types/eslint-plugin-import", + "@types/eslint", + "eslint-plugin-import", + "eslint" + ], + "linkType": "HARD", + }] + ]], + ["eslint-config-prettier", [ + ["npm:8.3.0", { + "packageLocation": "./.yarn/cache/eslint-config-prettier-npm-8.3.0-f540cd1f53-df4cea3032.zip/node_modules/eslint-config-prettier/", + "packageDependencies": [ + ["eslint-config-prettier", "npm:8.3.0"] + ], + "linkType": "SOFT", + }], + ["virtual:b28e06588f8884ad00999d9ef1772f24cee4941229e01144c8d0ec740c177a99fec30f4d4abc3e4fed61ea09a8dd1831c34fb8ad78b40153cc83d898499b5720#npm:8.3.0", { + "packageLocation": "./.yarn/__virtual__/eslint-config-prettier-virtual-ee5d0e0f8d/0/cache/eslint-config-prettier-npm-8.3.0-f540cd1f53-df4cea3032.zip/node_modules/eslint-config-prettier/", + "packageDependencies": [ + ["eslint-config-prettier", "virtual:b28e06588f8884ad00999d9ef1772f24cee4941229e01144c8d0ec740c177a99fec30f4d4abc3e4fed61ea09a8dd1831c34fb8ad78b40153cc83d898499b5720#npm:8.3.0"], + ["@types/eslint", null], + ["eslint", null] + ], + "packagePeers": [ + "@types/eslint", + "eslint" + ], + "linkType": "HARD", + }] + ]], + ["eslint-import-resolver-node", [ + ["npm:0.3.6", { + "packageLocation": "./.yarn/cache/eslint-import-resolver-node-npm-0.3.6-d9426786c6-6266733af1.zip/node_modules/eslint-import-resolver-node/", + "packageDependencies": [ + ["eslint-import-resolver-node", "npm:0.3.6"], + ["debug", "virtual:d9426786c635bc4b52511d6cc4b56156f50d780a698c0e20fc6caf10d3be51cbf176e79cff882f4d42a23ff4d0f89fe94222849578214e7fbae0f2754c82af02#npm:3.2.7"], + ["resolve", "patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b"] + ], + "linkType": "HARD", + }] + ]], + ["eslint-module-utils", [ + ["npm:2.7.1", { + "packageLocation": "./.yarn/cache/eslint-module-utils-npm-2.7.1-2b7798b493-c30dfa125a.zip/node_modules/eslint-module-utils/", + "packageDependencies": [ + ["eslint-module-utils", "npm:2.7.1"], + ["debug", "virtual:d9426786c635bc4b52511d6cc4b56156f50d780a698c0e20fc6caf10d3be51cbf176e79cff882f4d42a23ff4d0f89fe94222849578214e7fbae0f2754c82af02#npm:3.2.7"], + ["eslint-import-resolver-node", "npm:0.3.6"], + ["find-up", "npm:2.1.0"], + ["pkg-dir", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["eslint-plugin-import", [ + ["npm:2.25.3", { + "packageLocation": "./.yarn/cache/eslint-plugin-import-npm-2.25.3-f5faefaae3-8bdf4b1faf.zip/node_modules/eslint-plugin-import/", + "packageDependencies": [ + ["eslint-plugin-import", "npm:2.25.3"] + ], + "linkType": "SOFT", + }], + ["virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3", { + "packageLocation": "./.yarn/__virtual__/eslint-plugin-import-virtual-0684f4c444/0/cache/eslint-plugin-import-npm-2.25.3-f5faefaae3-8bdf4b1faf.zip/node_modules/eslint-plugin-import/", + "packageDependencies": [ + ["eslint-plugin-import", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:2.25.3"], + ["@types/eslint", null], + ["array-includes", "npm:3.1.4"], + ["array.prototype.flat", "npm:1.2.5"], + ["debug", "virtual:0684f4c444aee59cc66233fcce3e4e9a25ed7e35886aed11393a5d4d03c3e7ec6f43fe70e91d783fa0717aa30724ce3a9a32ae0cd75006d103d8f81d5b9758c1#npm:2.6.9"], + ["doctrine", "npm:2.1.0"], + ["eslint", "npm:7.32.0"], + ["eslint-import-resolver-node", "npm:0.3.6"], + ["eslint-module-utils", "npm:2.7.1"], + ["has", "npm:1.0.3"], + ["is-core-module", "npm:2.8.1"], + ["is-glob", "npm:4.0.3"], + ["minimatch", "npm:3.0.4"], + ["object.values", "npm:1.1.5"], + ["resolve", "patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b"], + ["tsconfig-paths", "npm:3.12.0"] + ], + "packagePeers": [ + "@types/eslint", + "eslint" + ], + "linkType": "HARD", + }] + ]], + ["eslint-plugin-jsdoc", [ + ["npm:27.1.2", { + "packageLocation": "./.yarn/cache/eslint-plugin-jsdoc-npm-27.1.2-30c15b9d07-df6550e057.zip/node_modules/eslint-plugin-jsdoc/", + "packageDependencies": [ + ["eslint-plugin-jsdoc", "npm:27.1.2"] + ], + "linkType": "SOFT", + }], + ["virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:27.1.2", { + "packageLocation": "./.yarn/__virtual__/eslint-plugin-jsdoc-virtual-cbf6bbf962/0/cache/eslint-plugin-jsdoc-npm-27.1.2-30c15b9d07-df6550e057.zip/node_modules/eslint-plugin-jsdoc/", + "packageDependencies": [ + ["eslint-plugin-jsdoc", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:27.1.2"], + ["@types/eslint", null], + ["comment-parser", "npm:0.7.6"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["eslint", "npm:7.32.0"], + ["jsdoctypeparser", "npm:6.1.0"], + ["lodash", "npm:4.17.21"], + ["regextras", "npm:0.7.1"], + ["semver", "npm:6.3.0"], + ["spdx-expression-parse", "npm:3.0.1"] + ], + "packagePeers": [ + "@types/eslint", + "eslint" + ], + "linkType": "HARD", + }] + ]], + ["eslint-scope", [ + ["npm:5.1.1", { + "packageLocation": "./.yarn/cache/eslint-scope-npm-5.1.1-71fe59b18a-47e4b6a3f0.zip/node_modules/eslint-scope/", + "packageDependencies": [ + ["eslint-scope", "npm:5.1.1"], + ["esrecurse", "npm:4.3.0"], + ["estraverse", "npm:4.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["eslint-utils", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/eslint-utils-npm-2.1.0-a3a7ebf4fa-27500938f3.zip/node_modules/eslint-utils/", + "packageDependencies": [ + ["eslint-utils", "npm:2.1.0"], + ["eslint-visitor-keys", "npm:1.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["eslint-visitor-keys", [ + ["npm:1.3.0", { + "packageLocation": "./.yarn/cache/eslint-visitor-keys-npm-1.3.0-c07780a0fb-37a19b712f.zip/node_modules/eslint-visitor-keys/", + "packageDependencies": [ + ["eslint-visitor-keys", "npm:1.3.0"] + ], + "linkType": "HARD", + }], + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/eslint-visitor-keys-npm-2.1.0-c31806b6b9-e3081d7dd2.zip/node_modules/eslint-visitor-keys/", + "packageDependencies": [ + ["eslint-visitor-keys", "npm:2.1.0"] + ], + "linkType": "HARD", + }], + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/eslint-visitor-keys-npm-3.1.0-9a6ffc9175-fd2d613bb3.zip/node_modules/eslint-visitor-keys/", + "packageDependencies": [ + ["eslint-visitor-keys", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["espree", [ + ["npm:7.3.1", { + "packageLocation": "./.yarn/cache/espree-npm-7.3.1-8d8ea5d1e3-aa9b50dcce.zip/node_modules/espree/", + "packageDependencies": [ + ["espree", "npm:7.3.1"], + ["acorn", "npm:7.4.1"], + ["acorn-jsx", "virtual:8d8ea5d1e3376905d0290522290f47c29213c64d936d96293d758a315829a3cf4c6a5b8ffc1cfee36c3db08f700ad3aaf0711cc5d406a7218c275de6d74effa9#npm:5.3.2"], + ["eslint-visitor-keys", "npm:1.3.0"] + ], + "linkType": "HARD", + }], + ["npm:9.1.0", { + "packageLocation": "./.yarn/cache/espree-npm-9.1.0-fd22538590-ba9b0f759c.zip/node_modules/espree/", + "packageDependencies": [ + ["espree", "npm:9.1.0"], + ["acorn", "npm:8.6.0"], + ["acorn-jsx", "virtual:fd2253859039a15030fecf2d1545fcad47d7bd43468b9166c71fdd4e35b538414e653775f5401c948ed8db3eb1925f84c66c161d39a27b19ee73fef5e721329e#npm:5.3.2"], + ["eslint-visitor-keys", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["esprima", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/esprima-npm-4.0.1-1084e98778-b45bc805a6.zip/node_modules/esprima/", + "packageDependencies": [ + ["esprima", "npm:4.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["esquery", [ + ["npm:1.4.0", { + "packageLocation": "./.yarn/cache/esquery-npm-1.4.0-f39408b1a7-a0807e17ab.zip/node_modules/esquery/", + "packageDependencies": [ + ["esquery", "npm:1.4.0"], + ["estraverse", "npm:5.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["esrecurse", [ + ["npm:4.3.0", { + "packageLocation": "./.yarn/cache/esrecurse-npm-4.3.0-10b86a887a-ebc17b1a33.zip/node_modules/esrecurse/", + "packageDependencies": [ + ["esrecurse", "npm:4.3.0"], + ["estraverse", "npm:5.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["estraverse", [ + ["npm:4.3.0", { + "packageLocation": "./.yarn/cache/estraverse-npm-4.3.0-920a32f3c6-a6299491f9.zip/node_modules/estraverse/", + "packageDependencies": [ + ["estraverse", "npm:4.3.0"] + ], + "linkType": "HARD", + }], + ["npm:5.3.0", { + "packageLocation": "./.yarn/cache/estraverse-npm-5.3.0-03284f8f63-072780882d.zip/node_modules/estraverse/", + "packageDependencies": [ + ["estraverse", "npm:5.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["esutils", [ + ["npm:2.0.3", { + "packageLocation": "./.yarn/cache/esutils-npm-2.0.3-f865beafd5-22b5b08f74.zip/node_modules/esutils/", + "packageDependencies": [ + ["esutils", "npm:2.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["eventemitter3", [ + ["npm:4.0.7", { + "packageLocation": "./.yarn/cache/eventemitter3-npm-4.0.7-7afcdd74ae-1875311c42.zip/node_modules/eventemitter3/", + "packageDependencies": [ + ["eventemitter3", "npm:4.0.7"] + ], + "linkType": "HARD", + }] + ]], + ["events", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/events-npm-1.1.1-ca9e5d580e-40431eb005.zip/node_modules/events/", + "packageDependencies": [ + ["events", "npm:1.1.1"] + ], + "linkType": "HARD", + }], + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/events-npm-2.1.0-883256cbfc-8756c4f40a.zip/node_modules/events/", + "packageDependencies": [ + ["events", "npm:2.1.0"] + ], + "linkType": "HARD", + }], + ["npm:3.3.0", { + "packageLocation": "./.yarn/cache/events-npm-3.3.0-c280bc7e48-f6f487ad21.zip/node_modules/events/", + "packageDependencies": [ + ["events", "npm:3.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["evp_bytestokey", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/evp_bytestokey-npm-1.0.3-4a2644aaea-ad4e1577f1.zip/node_modules/evp_bytestokey/", + "packageDependencies": [ + ["evp_bytestokey", "npm:1.0.3"], + ["md5.js", "npm:1.3.5"], + ["node-gyp", "npm:8.4.0"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["execa", [ + ["npm:0.10.0", { + "packageLocation": "./.yarn/cache/execa-npm-0.10.0-d18cb8f7af-da132af2b2.zip/node_modules/execa/", + "packageDependencies": [ + ["execa", "npm:0.10.0"], + ["cross-spawn", "npm:6.0.5"], + ["get-stream", "npm:3.0.0"], + ["is-stream", "npm:1.1.0"], + ["npm-run-path", "npm:2.0.2"], + ["p-finally", "npm:1.0.0"], + ["signal-exit", "npm:3.0.7"], + ["strip-eof", "npm:1.0.0"] + ], + "linkType": "HARD", + }], + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/execa-npm-4.1.0-cc675b4189-e30d298934.zip/node_modules/execa/", + "packageDependencies": [ + ["execa", "npm:4.1.0"], + ["cross-spawn", "npm:7.0.3"], + ["get-stream", "npm:5.2.0"], + ["human-signals", "npm:1.1.1"], + ["is-stream", "npm:2.0.1"], + ["merge-stream", "npm:2.0.0"], + ["npm-run-path", "npm:4.0.1"], + ["onetime", "npm:5.1.2"], + ["signal-exit", "npm:3.0.7"], + ["strip-final-newline", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.1.1", { + "packageLocation": "./.yarn/cache/execa-npm-5.1.1-191347acf5-fba9022c8c.zip/node_modules/execa/", + "packageDependencies": [ + ["execa", "npm:5.1.1"], + ["cross-spawn", "npm:7.0.3"], + ["get-stream", "npm:6.0.1"], + ["human-signals", "npm:2.1.0"], + ["is-stream", "npm:2.0.1"], + ["merge-stream", "npm:2.0.0"], + ["npm-run-path", "npm:4.0.1"], + ["onetime", "npm:5.1.2"], + ["signal-exit", "npm:3.0.7"], + ["strip-final-newline", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["expect", [ + ["npm:27.3.1", { + "packageLocation": "./.yarn/cache/expect-npm-27.3.1-c00331f3de-e7681ecc7a.zip/node_modules/expect/", + "packageDependencies": [ + ["expect", "npm:27.3.1"], + ["@jest/types", "npm:27.2.5"], + ["ansi-styles", "npm:5.2.0"], + ["jest-get-type", "npm:27.3.1"], + ["jest-matcher-utils", "npm:27.3.1"], + ["jest-message-util", "npm:27.3.1"], + ["jest-regex-util", "npm:27.0.6"] + ], + "linkType": "HARD", + }] + ]], + ["extend", [ + ["npm:3.0.2", { + "packageLocation": "./.yarn/cache/extend-npm-3.0.2-e1ca07ac54-a50a8309ca.zip/node_modules/extend/", + "packageDependencies": [ + ["extend", "npm:3.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["external-editor", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/external-editor-npm-3.1.0-878e7807af-1c2a616a73.zip/node_modules/external-editor/", + "packageDependencies": [ + ["external-editor", "npm:3.1.0"], + ["chardet", "npm:0.7.0"], + ["iconv-lite", "npm:0.4.24"], + ["tmp", "npm:0.0.33"] + ], + "linkType": "HARD", + }] + ]], + ["extsprintf", [ + ["npm:1.3.0", { + "packageLocation": "./.yarn/cache/extsprintf-npm-1.3.0-61a92b324c-cee7a4a1e3.zip/node_modules/extsprintf/", + "packageDependencies": [ + ["extsprintf", "npm:1.3.0"] + ], + "linkType": "HARD", + }], + ["npm:1.4.1", { + "packageLocation": "./.yarn/cache/extsprintf-npm-1.4.1-140b2f27ab-a2f29b2419.zip/node_modules/extsprintf/", + "packageDependencies": [ + ["extsprintf", "npm:1.4.1"] + ], + "linkType": "HARD", + }] + ]], + ["eyes", [ + ["npm:0.1.8", { + "packageLocation": "./.yarn/cache/eyes-npm-0.1.8-4f28ed333f-c31703a92b.zip/node_modules/eyes/", + "packageDependencies": [ + ["eyes", "npm:0.1.8"] + ], + "linkType": "HARD", + }] + ]], + ["fast-decode-uri-component", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/fast-decode-uri-component-npm-1.0.1-578ba9fecf-427a48fe09.zip/node_modules/fast-decode-uri-component/", + "packageDependencies": [ + ["fast-decode-uri-component", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["fast-deep-equal", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/fast-deep-equal-npm-2.0.1-9c01e08a62-b701835a87.zip/node_modules/fast-deep-equal/", + "packageDependencies": [ + ["fast-deep-equal", "npm:2.0.1"] + ], + "linkType": "HARD", + }], + ["npm:3.1.3", { + "packageLocation": "./.yarn/cache/fast-deep-equal-npm-3.1.3-790edcfcf5-e21a9d8d84.zip/node_modules/fast-deep-equal/", + "packageDependencies": [ + ["fast-deep-equal", "npm:3.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["fast-glob", [ + ["npm:3.2.11", { + "packageLocation": "./.yarn/cache/fast-glob-npm-3.2.11-bc01135fef-f473105324.zip/node_modules/fast-glob/", + "packageDependencies": [ + ["fast-glob", "npm:3.2.11"], + ["@nodelib/fs.stat", "npm:2.0.5"], + ["@nodelib/fs.walk", "npm:1.2.8"], + ["glob-parent", "npm:5.1.2"], + ["merge2", "npm:1.4.1"], + ["micromatch", "npm:4.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["fast-json-patch", [ + ["npm:2.2.1", { + "packageLocation": "./.yarn/cache/fast-json-patch-npm-2.2.1-63b021bb37-955aebb3f8.zip/node_modules/fast-json-patch/", + "packageDependencies": [ + ["fast-json-patch", "npm:2.2.1"], + ["fast-deep-equal", "npm:2.0.1"] + ], + "linkType": "HARD", + }], + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/fast-json-patch-npm-3.1.0-f4bd467b5f-bad25a6121.zip/node_modules/fast-json-patch/", + "packageDependencies": [ + ["fast-json-patch", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["fast-json-stable-stringify", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/fast-json-stable-stringify-npm-2.1.0-02e8905fda-b191531e36.zip/node_modules/fast-json-stable-stringify/", + "packageDependencies": [ + ["fast-json-stable-stringify", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["fast-levenshtein", [ + ["npm:2.0.6", { + "packageLocation": "./.yarn/cache/fast-levenshtein-npm-2.0.6-fcd74b8df5-92cfec0a8d.zip/node_modules/fast-levenshtein/", + "packageDependencies": [ + ["fast-levenshtein", "npm:2.0.6"] + ], + "linkType": "HARD", + }], + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/fast-levenshtein-npm-3.0.0-8fbb1bef2f-02732ba6c6.zip/node_modules/fast-levenshtein/", + "packageDependencies": [ + ["fast-levenshtein", "npm:3.0.0"], + ["fastest-levenshtein", "npm:1.0.12"] + ], + "linkType": "HARD", + }] + ]], + ["fast-redact", [ + ["npm:3.0.2", { + "packageLocation": "./.yarn/cache/fast-redact-npm-3.0.2-98d6f1d433-f4ffdf48f1.zip/node_modules/fast-redact/", + "packageDependencies": [ + ["fast-redact", "npm:3.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["fast-safe-stringify", [ + ["npm:2.1.1", { + "packageLocation": "./.yarn/cache/fast-safe-stringify-npm-2.1.1-7ce89033ca-a851cbddc4.zip/node_modules/fast-safe-stringify/", + "packageDependencies": [ + ["fast-safe-stringify", "npm:2.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["fastest-levenshtein", [ + ["npm:1.0.12", { + "packageLocation": "./.yarn/cache/fastest-levenshtein-npm-1.0.12-a32b4ef51e-e1a013698d.zip/node_modules/fastest-levenshtein/", + "packageDependencies": [ + ["fastest-levenshtein", "npm:1.0.12"] + ], + "linkType": "HARD", + }] + ]], + ["fastify-warning", [ + ["npm:0.2.0", { + "packageLocation": "./.yarn/cache/fastify-warning-npm-0.2.0-f9c53563fc-c19ebccf54.zip/node_modules/fastify-warning/", + "packageDependencies": [ + ["fastify-warning", "npm:0.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["fastq", [ + ["npm:1.13.0", { + "packageLocation": "./.yarn/cache/fastq-npm-1.13.0-a45963881c-32cf15c29a.zip/node_modules/fastq/", + "packageDependencies": [ + ["fastq", "npm:1.13.0"], + ["reusify", "npm:1.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["fclone", [ + ["npm:1.0.11", { + "packageLocation": "./.yarn/cache/fclone-npm-1.0.11-7e6cfa9908-016eb1eac4.zip/node_modules/fclone/", + "packageDependencies": [ + ["fclone", "npm:1.0.11"] + ], + "linkType": "HARD", + }] + ]], + ["fecha", [ + ["npm:4.2.1", { + "packageLocation": "./.yarn/cache/fecha-npm-4.2.1-40d84f7733-2699347494.zip/node_modules/fecha/", + "packageDependencies": [ + ["fecha", "npm:4.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["figures", [ + ["npm:3.2.0", { + "packageLocation": "./.yarn/cache/figures-npm-3.2.0-85d357e955-85a6ad29e9.zip/node_modules/figures/", + "packageDependencies": [ + ["figures", "npm:3.2.0"], + ["escape-string-regexp", "npm:1.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["file-entry-cache", [ + ["npm:6.0.1", { + "packageLocation": "./.yarn/cache/file-entry-cache-npm-6.0.1-31965cf0af-f49701feaa.zip/node_modules/file-entry-cache/", + "packageDependencies": [ + ["file-entry-cache", "npm:6.0.1"], + ["flat-cache", "npm:3.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["filelist", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/filelist-npm-1.0.2-d98495ab20-4d6953cb6f.zip/node_modules/filelist/", + "packageDependencies": [ + ["filelist", "npm:1.0.2"], + ["minimatch", "npm:3.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["fill-range", [ + ["npm:7.0.1", { + "packageLocation": "./.yarn/cache/fill-range-npm-7.0.1-b8b1817caa-cc283f4e65.zip/node_modules/fill-range/", + "packageDependencies": [ + ["fill-range", "npm:7.0.1"], + ["to-regex-range", "npm:5.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["finalhandler", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/finalhandler-npm-1.1.2-55a75d6b53-617880460c.zip/node_modules/finalhandler/", + "packageDependencies": [ + ["finalhandler", "npm:1.1.2"], + ["debug", "virtual:0684f4c444aee59cc66233fcce3e4e9a25ed7e35886aed11393a5d4d03c3e7ec6f43fe70e91d783fa0717aa30724ce3a9a32ae0cd75006d103d8f81d5b9758c1#npm:2.6.9"], + ["encodeurl", "npm:1.0.2"], + ["escape-html", "npm:1.0.3"], + ["on-finished", "npm:2.3.0"], + ["parseurl", "npm:1.3.3"], + ["statuses", "npm:1.5.0"], + ["unpipe", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["find-cache-dir", [ + ["npm:3.3.2", { + "packageLocation": "./.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-1e61c2e64f.zip/node_modules/find-cache-dir/", + "packageDependencies": [ + ["find-cache-dir", "npm:3.3.2"], + ["commondir", "npm:1.0.1"], + ["make-dir", "npm:3.1.0"], + ["pkg-dir", "npm:4.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["find-my-way", [ + ["npm:2.2.5", { + "packageLocation": "./.yarn/cache/find-my-way-npm-2.2.5-3bef2f72f0-9330349565.zip/node_modules/find-my-way/", + "packageDependencies": [ + ["find-my-way", "npm:2.2.5"], + ["fast-decode-uri-component", "npm:1.0.1"], + ["safe-regex2", "npm:2.0.0"], + ["semver-store", "npm:0.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["find-up", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/find-up-npm-2.1.0-9f6cb1765c-43284fe4da.zip/node_modules/find-up/", + "packageDependencies": [ + ["find-up", "npm:2.1.0"], + ["locate-path", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/find-up-npm-4.1.0-c3ccf8d855-4c172680e8.zip/node_modules/find-up/", + "packageDependencies": [ + ["find-up", "npm:4.1.0"], + ["locate-path", "npm:5.0.0"], + ["path-exists", "npm:4.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/find-up-npm-5.0.0-e03e9b796d-07955e3573.zip/node_modules/find-up/", + "packageDependencies": [ + ["find-up", "npm:5.0.0"], + ["locate-path", "npm:6.0.0"], + ["path-exists", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["find-yarn-workspace-root", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/find-yarn-workspace-root-npm-2.0.0-e58a501607-fa5ca8f9d0.zip/node_modules/find-yarn-workspace-root/", + "packageDependencies": [ + ["find-yarn-workspace-root", "npm:2.0.0"], + ["micromatch", "npm:4.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["find-yarn-workspace-root2", [ + ["npm:1.2.16", { + "packageLocation": "./.yarn/cache/find-yarn-workspace-root2-npm-1.2.16-0d4f3213bd-b4abdd37ab.zip/node_modules/find-yarn-workspace-root2/", + "packageDependencies": [ + ["find-yarn-workspace-root2", "npm:1.2.16"], + ["micromatch", "npm:4.0.4"], + ["pkg-dir", "npm:4.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["first-chunk-stream", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/first-chunk-stream-npm-2.0.0-08ecb1b0f2-2fa86f93a4.zip/node_modules/first-chunk-stream/", + "packageDependencies": [ + ["first-chunk-stream", "npm:2.0.0"], + ["readable-stream", "npm:2.3.7"] + ], + "linkType": "HARD", + }] + ]], + ["flat", [ + ["npm:5.0.2", { + "packageLocation": "./.yarn/cache/flat-npm-5.0.2-12748102a5-12a1536ac7.zip/node_modules/flat/", + "packageDependencies": [ + ["flat", "npm:5.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["flat-cache", [ + ["npm:3.0.4", { + "packageLocation": "./.yarn/cache/flat-cache-npm-3.0.4-ee77e5911e-4fdd10ecbc.zip/node_modules/flat-cache/", + "packageDependencies": [ + ["flat-cache", "npm:3.0.4"], + ["flatted", "npm:3.2.4"], + ["rimraf", "npm:3.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["flatstr", [ + ["npm:1.0.12", { + "packageLocation": "./.yarn/cache/flatstr-npm-1.0.12-4311d37d16-e1bb562c94.zip/node_modules/flatstr/", + "packageDependencies": [ + ["flatstr", "npm:1.0.12"] + ], + "linkType": "HARD", + }] + ]], + ["flatted", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/flatted-npm-2.0.2-ccb06e14ff-473c754db7.zip/node_modules/flatted/", + "packageDependencies": [ + ["flatted", "npm:2.0.2"] + ], + "linkType": "HARD", + }], + ["npm:3.2.4", { + "packageLocation": "./.yarn/cache/flatted-npm-3.2.4-b14c5985c7-7d33846428.zip/node_modules/flatted/", + "packageDependencies": [ + ["flatted", "npm:3.2.4"] + ], + "linkType": "HARD", + }] + ]], + ["fn.name", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/fn.name-npm-1.1.0-b472333184-e357144f48.zip/node_modules/fn.name/", + "packageDependencies": [ + ["fn.name", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["follow-redirects", [ + ["npm:1.14.5", { + "packageLocation": "./.yarn/cache/follow-redirects-npm-1.14.5-7c681222a0-f004a76b2e.zip/node_modules/follow-redirects/", + "packageDependencies": [ + ["follow-redirects", "npm:1.14.5"] + ], + "linkType": "SOFT", + }], + ["virtual:a313c479c5c7e54d9ec8fbeeea69ff640f56b8989ea2dff42351a3fa5c4061fb80a52d8ede0f0826a181a216820c2d2c3f15da881e7fdf31cef1c446e42f0c45#npm:1.14.5", { + "packageLocation": "./.yarn/__virtual__/follow-redirects-virtual-af3566fb1d/0/cache/follow-redirects-npm-1.14.5-7c681222a0-f004a76b2e.zip/node_modules/follow-redirects/", + "packageDependencies": [ + ["follow-redirects", "virtual:a313c479c5c7e54d9ec8fbeeea69ff640f56b8989ea2dff42351a3fa5c4061fb80a52d8ede0f0826a181a216820c2d2c3f15da881e7fdf31cef1c446e42f0c45#npm:1.14.5"], + ["@types/debug", null], + ["debug", null] + ], + "packagePeers": [ + "@types/debug", + "debug" + ], + "linkType": "HARD", + }] + ]], + ["foreach", [ + ["npm:2.0.5", { + "packageLocation": "./.yarn/cache/foreach-npm-2.0.5-9fbfc73114-dab4fbfef0.zip/node_modules/foreach/", + "packageDependencies": [ + ["foreach", "npm:2.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["foreground-child", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/foreground-child-npm-2.0.0-80c976b61e-f77ec9aff6.zip/node_modules/foreground-child/", + "packageDependencies": [ + ["foreground-child", "npm:2.0.0"], + ["cross-spawn", "npm:7.0.3"], + ["signal-exit", "npm:3.0.7"] + ], + "linkType": "HARD", + }] + ]], + ["forever-agent", [ + ["npm:0.6.1", { + "packageLocation": "./.yarn/cache/forever-agent-npm-0.6.1-01dae53bf9-766ae6e220.zip/node_modules/forever-agent/", + "packageDependencies": [ + ["forever-agent", "npm:0.6.1"] + ], + "linkType": "HARD", + }] + ]], + ["form-data", [ + ["npm:2.3.3", { + "packageLocation": "./.yarn/cache/form-data-npm-2.3.3-c016cc11c0-10c1780fa1.zip/node_modules/form-data/", + "packageDependencies": [ + ["form-data", "npm:2.3.3"], + ["asynckit", "npm:0.4.0"], + ["combined-stream", "npm:1.0.8"], + ["mime-types", "npm:2.1.34"] + ], + "linkType": "HARD", + }] + ]], + ["fraction.js", [ + ["npm:4.2.0", { + "packageLocation": "./.yarn/cache/fraction.js-npm-4.2.0-28efe4afc7-8c76a6e21d.zip/node_modules/fraction.js/", + "packageDependencies": [ + ["fraction.js", "npm:4.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["fromentries", [ + ["npm:1.3.2", { + "packageLocation": "./.yarn/cache/fromentries-npm-1.3.2-f5392090b8-33729c529c.zip/node_modules/fromentries/", + "packageDependencies": [ + ["fromentries", "npm:1.3.2"] + ], + "linkType": "HARD", + }] + ]], + ["fs-constants", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/fs-constants-npm-1.0.0-59576b2177-18f5b71837.zip/node_modules/fs-constants/", + "packageDependencies": [ + ["fs-constants", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["fs-extra", [ + ["npm:6.0.1", { + "packageLocation": "./.yarn/cache/fs-extra-npm-6.0.1-fe74e3ae93-133dbd765e.zip/node_modules/fs-extra/", + "packageDependencies": [ + ["fs-extra", "npm:6.0.1"], + ["graceful-fs", "npm:4.2.10"], + ["jsonfile", "npm:4.0.0"], + ["universalify", "npm:0.1.2"] + ], + "linkType": "HARD", + }], + ["npm:8.1.0", { + "packageLocation": "./.yarn/cache/fs-extra-npm-8.1.0-197473387f-bf44f0e6ce.zip/node_modules/fs-extra/", + "packageDependencies": [ + ["fs-extra", "npm:8.1.0"], + ["graceful-fs", "npm:4.2.10"], + ["jsonfile", "npm:4.0.0"], + ["universalify", "npm:0.1.2"] + ], + "linkType": "HARD", + }], + ["npm:9.1.0", { + "packageLocation": "./.yarn/cache/fs-extra-npm-9.1.0-983c2ddb4c-ba71ba32e0.zip/node_modules/fs-extra/", + "packageDependencies": [ + ["fs-extra", "npm:9.1.0"], + ["at-least-node", "npm:1.0.0"], + ["graceful-fs", "npm:4.2.10"], + ["jsonfile", "npm:6.1.0"], + ["universalify", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["fs-minipass", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/fs-minipass-npm-2.1.0-501ef87306-1b8d128dae.zip/node_modules/fs-minipass/", + "packageDependencies": [ + ["fs-minipass", "npm:2.1.0"], + ["minipass", "npm:3.1.6"] + ], + "linkType": "HARD", + }] + ]], + ["fs.realpath", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/fs.realpath-npm-1.0.0-c8f05d8126-99ddea01a7.zip/node_modules/fs.realpath/", + "packageDependencies": [ + ["fs.realpath", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["fsevents", [ + ["patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=18f3a7", { + "packageLocation": "./.yarn/unplugged/fsevents-patch-3340e2eb10/node_modules/fsevents/", + "packageDependencies": [ + ["fsevents", "patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=18f3a7"], + ["node-gyp", "npm:8.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["function-bind", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/function-bind-npm-1.1.1-b56b322ae9-b32fbaebb3.zip/node_modules/function-bind/", + "packageDependencies": [ + ["function-bind", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["functional-red-black-tree", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/functional-red-black-tree-npm-1.0.1-ccfe924dcd-ca6c170f37.zip/node_modules/functional-red-black-tree/", + "packageDependencies": [ + ["functional-red-black-tree", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["gauge", [ + ["npm:2.7.4", { + "packageLocation": "./.yarn/cache/gauge-npm-2.7.4-2189a73529-a89b53cee6.zip/node_modules/gauge/", + "packageDependencies": [ + ["gauge", "npm:2.7.4"], + ["aproba", "npm:1.2.0"], + ["console-control-strings", "npm:1.1.0"], + ["has-unicode", "npm:2.0.1"], + ["object-assign", "npm:4.1.1"], + ["signal-exit", "npm:3.0.7"], + ["string-width", "npm:1.0.2"], + ["strip-ansi", "npm:3.0.1"], + ["wide-align", "npm:1.1.5"] + ], + "linkType": "HARD", + }], + ["npm:3.0.2", { + "packageLocation": "./.yarn/cache/gauge-npm-3.0.2-9e22f7af9e-81296c00c7.zip/node_modules/gauge/", + "packageDependencies": [ + ["gauge", "npm:3.0.2"], + ["aproba", "npm:2.0.0"], + ["color-support", "npm:1.1.3"], + ["console-control-strings", "npm:1.1.0"], + ["has-unicode", "npm:2.0.1"], + ["object-assign", "npm:4.1.1"], + ["signal-exit", "npm:3.0.7"], + ["string-width", "npm:4.2.3"], + ["strip-ansi", "npm:6.0.1"], + ["wide-align", "npm:1.1.5"] + ], + "linkType": "HARD", + }], + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/gauge-npm-4.0.1-c54e7ba970-398540c761.zip/node_modules/gauge/", + "packageDependencies": [ + ["gauge", "npm:4.0.1"], + ["ansi-regex", "npm:5.0.1"], + ["aproba", "npm:2.0.0"], + ["color-support", "npm:1.1.3"], + ["console-control-strings", "npm:1.1.0"], + ["has-unicode", "npm:2.0.1"], + ["signal-exit", "npm:3.0.7"], + ["string-width", "npm:4.2.3"], + ["strip-ansi", "npm:6.0.1"], + ["wide-align", "npm:1.1.5"] + ], + "linkType": "HARD", + }] + ]], + ["gensync", [ + ["npm:1.0.0-beta.2", { + "packageLocation": "./.yarn/cache/gensync-npm-1.0.0-beta.2-224666d72f-a7437e58c6.zip/node_modules/gensync/", + "packageDependencies": [ + ["gensync", "npm:1.0.0-beta.2"] + ], + "linkType": "HARD", + }] + ]], + ["get-assigned-identifiers", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/get-assigned-identifiers-npm-1.2.0-559db40691-5ea831c744.zip/node_modules/get-assigned-identifiers/", + "packageDependencies": [ + ["get-assigned-identifiers", "npm:1.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["get-caller-file", [ + ["npm:2.0.5", { + "packageLocation": "./.yarn/cache/get-caller-file-npm-2.0.5-80e8a86305-b9769a836d.zip/node_modules/get-caller-file/", + "packageDependencies": [ + ["get-caller-file", "npm:2.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["get-func-name", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/get-func-name-npm-2.0.0-afbf363765-8d82e69f3e.zip/node_modules/get-func-name/", + "packageDependencies": [ + ["get-func-name", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["get-intrinsic", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/get-intrinsic-npm-1.1.1-7e868745da-a9fe2ca8fa.zip/node_modules/get-intrinsic/", + "packageDependencies": [ + ["get-intrinsic", "npm:1.1.1"], + ["function-bind", "npm:1.1.1"], + ["has", "npm:1.0.3"], + ["has-symbols", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["get-package-type", [ + ["npm:0.1.0", { + "packageLocation": "./.yarn/cache/get-package-type-npm-0.1.0-6c70cdc8ab-bba0811116.zip/node_modules/get-package-type/", + "packageDependencies": [ + ["get-package-type", "npm:0.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["get-pkg-repo", [ + ["npm:4.2.1", { + "packageLocation": "./.yarn/cache/get-pkg-repo-npm-4.2.1-b1cd052cb4-5abf169137.zip/node_modules/get-pkg-repo/", + "packageDependencies": [ + ["get-pkg-repo", "npm:4.2.1"], + ["@hutson/parse-repository-url", "npm:3.0.2"], + ["hosted-git-info", "npm:4.0.2"], + ["through2", "npm:2.0.5"], + ["yargs", "npm:16.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["get-stdin", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/get-stdin-npm-4.0.1-10c6ac0b43-4f73d3fe05.zip/node_modules/get-stdin/", + "packageDependencies": [ + ["get-stdin", "npm:4.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["get-stream", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/get-stream-npm-3.0.0-ca0b13ddbe-36142f4600.zip/node_modules/get-stream/", + "packageDependencies": [ + ["get-stream", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/get-stream-npm-4.1.0-314d430a5d-443e191417.zip/node_modules/get-stream/", + "packageDependencies": [ + ["get-stream", "npm:4.1.0"], + ["pump", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.2.0", { + "packageLocation": "./.yarn/cache/get-stream-npm-5.2.0-2cfd3b452b-8bc1a23174.zip/node_modules/get-stream/", + "packageDependencies": [ + ["get-stream", "npm:5.2.0"], + ["pump", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:6.0.1", { + "packageLocation": "./.yarn/cache/get-stream-npm-6.0.1-83e51a4642-e04ecece32.zip/node_modules/get-stream/", + "packageDependencies": [ + ["get-stream", "npm:6.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["get-symbol-description", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/get-symbol-description-npm-1.0.0-9c95a4bc1f-9ceff8fe96.zip/node_modules/get-symbol-description/", + "packageDependencies": [ + ["get-symbol-description", "npm:1.0.0"], + ["call-bind", "npm:1.0.2"], + ["get-intrinsic", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["getpass", [ + ["npm:0.1.7", { + "packageLocation": "./.yarn/cache/getpass-npm-0.1.7-519164a3be-ab18d55661.zip/node_modules/getpass/", + "packageDependencies": [ + ["getpass", "npm:0.1.7"], + ["assert-plus", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["git-raw-commits", [ + ["npm:2.0.10", { + "packageLocation": "./.yarn/cache/git-raw-commits-npm-2.0.10-66e3a843dd-66e2d7b4cd.zip/node_modules/git-raw-commits/", + "packageDependencies": [ + ["git-raw-commits", "npm:2.0.10"], + ["dargs", "npm:7.0.0"], + ["lodash", "npm:4.17.21"], + ["meow", "npm:8.1.2"], + ["split2", "npm:3.2.2"], + ["through2", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["git-remote-origin-url", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/git-remote-origin-url-npm-2.0.0-319debe0d1-85263a09c0.zip/node_modules/git-remote-origin-url/", + "packageDependencies": [ + ["git-remote-origin-url", "npm:2.0.0"], + ["gitconfiglocal", "npm:1.0.0"], + ["pify", "npm:2.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["git-semver-tags", [ + ["npm:4.1.1", { + "packageLocation": "./.yarn/cache/git-semver-tags-npm-4.1.1-93b9747811-e16d02a515.zip/node_modules/git-semver-tags/", + "packageDependencies": [ + ["git-semver-tags", "npm:4.1.1"], + ["meow", "npm:8.1.2"], + ["semver", "npm:6.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["gitconfiglocal", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/gitconfiglocal-npm-1.0.0-905970379d-e6d2764c15.zip/node_modules/gitconfiglocal/", + "packageDependencies": [ + ["gitconfiglocal", "npm:1.0.0"], + ["ini", "npm:1.3.8"] + ], + "linkType": "HARD", + }] + ]], + ["github-api", [ + ["npm:3.4.0", { + "packageLocation": "./.yarn/cache/github-api-npm-3.4.0-da2c85f5b5-d6f2def92b.zip/node_modules/github-api/", + "packageDependencies": [ + ["github-api", "npm:3.4.0"], + ["axios", "npm:0.21.4"], + ["debug", "virtual:0684f4c444aee59cc66233fcce3e4e9a25ed7e35886aed11393a5d4d03c3e7ec6f43fe70e91d783fa0717aa30724ce3a9a32ae0cd75006d103d8f81d5b9758c1#npm:2.6.9"], + ["js-base64", "npm:2.6.4"], + ["utf8", "npm:2.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["github-slugger", [ + ["npm:1.4.0", { + "packageLocation": "./.yarn/cache/github-slugger-npm-1.4.0-29ff958597-4f52e7a21f.zip/node_modules/github-slugger/", + "packageDependencies": [ + ["github-slugger", "npm:1.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["github-username", [ + ["npm:6.0.0", { + "packageLocation": "./.yarn/cache/github-username-npm-6.0.0-6b7380ded2-c40a6151dc.zip/node_modules/github-username/", + "packageDependencies": [ + ["github-username", "npm:6.0.0"], + ["@octokit/rest", "npm:18.12.0"] + ], + "linkType": "HARD", + }] + ]], + ["glob", [ + ["npm:7.1.6", { + "packageLocation": "./.yarn/cache/glob-npm-7.1.6-1ce3a5189a-351d549dd9.zip/node_modules/glob/", + "packageDependencies": [ + ["glob", "npm:7.1.6"], + ["fs.realpath", "npm:1.0.0"], + ["inflight", "npm:1.0.6"], + ["inherits", "npm:2.0.4"], + ["minimatch", "npm:3.0.4"], + ["once", "npm:1.4.0"], + ["path-is-absolute", "npm:1.0.1"] + ], + "linkType": "HARD", + }], + ["npm:7.1.7", { + "packageLocation": "./.yarn/cache/glob-npm-7.1.7-5698ad9c48-b61f48973b.zip/node_modules/glob/", + "packageDependencies": [ + ["glob", "npm:7.1.7"], + ["fs.realpath", "npm:1.0.0"], + ["inflight", "npm:1.0.6"], + ["inherits", "npm:2.0.4"], + ["minimatch", "npm:3.0.4"], + ["once", "npm:1.4.0"], + ["path-is-absolute", "npm:1.0.1"] + ], + "linkType": "HARD", + }], + ["npm:7.2.0", { + "packageLocation": "./.yarn/cache/glob-npm-7.2.0-bb4644d239-78a8ea9423.zip/node_modules/glob/", + "packageDependencies": [ + ["glob", "npm:7.2.0"], + ["fs.realpath", "npm:1.0.0"], + ["inflight", "npm:1.0.6"], + ["inherits", "npm:2.0.4"], + ["minimatch", "npm:3.0.4"], + ["once", "npm:1.4.0"], + ["path-is-absolute", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["glob-parent", [ + ["npm:5.1.2", { + "packageLocation": "./.yarn/cache/glob-parent-npm-5.1.2-021ab32634-f4f2bfe242.zip/node_modules/glob-parent/", + "packageDependencies": [ + ["glob-parent", "npm:5.1.2"], + ["is-glob", "npm:4.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["glob-to-regexp", [ + ["npm:0.4.1", { + "packageLocation": "./.yarn/cache/glob-to-regexp-npm-0.4.1-cd697e0fc7-e795f4e8f0.zip/node_modules/glob-to-regexp/", + "packageDependencies": [ + ["glob-to-regexp", "npm:0.4.1"] + ], + "linkType": "HARD", + }] + ]], + ["global-dirs", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/global-dirs-npm-3.0.0-45faebeb68-953c17cf14.zip/node_modules/global-dirs/", + "packageDependencies": [ + ["global-dirs", "npm:3.0.0"], + ["ini", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["globals", [ + ["npm:11.12.0", { + "packageLocation": "./.yarn/cache/globals-npm-11.12.0-1fa7f41a6c-67051a45ec.zip/node_modules/globals/", + "packageDependencies": [ + ["globals", "npm:11.12.0"] + ], + "linkType": "HARD", + }], + ["npm:13.12.0", { + "packageLocation": "./.yarn/cache/globals-npm-13.12.0-df8e0eef2a-1f959abb11.zip/node_modules/globals/", + "packageDependencies": [ + ["globals", "npm:13.12.0"], + ["type-fest", "npm:0.20.2"] + ], + "linkType": "HARD", + }] + ]], + ["globby", [ + ["npm:10.0.2", { + "packageLocation": "./.yarn/cache/globby-npm-10.0.2-9b274c88d3-167cd067f2.zip/node_modules/globby/", + "packageDependencies": [ + ["globby", "npm:10.0.2"], + ["@types/glob", "npm:7.2.0"], + ["array-union", "npm:2.1.0"], + ["dir-glob", "npm:3.0.1"], + ["fast-glob", "npm:3.2.11"], + ["glob", "npm:7.2.0"], + ["ignore", "npm:5.2.0"], + ["merge2", "npm:1.4.1"], + ["slash", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:11.1.0", { + "packageLocation": "./.yarn/cache/globby-npm-11.1.0-bdcdf20c71-b4be8885e0.zip/node_modules/globby/", + "packageDependencies": [ + ["globby", "npm:11.1.0"], + ["array-union", "npm:2.1.0"], + ["dir-glob", "npm:3.0.1"], + ["fast-glob", "npm:3.2.11"], + ["ignore", "npm:5.2.0"], + ["merge2", "npm:1.4.1"], + ["slash", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["globrex", [ + ["npm:0.1.2", { + "packageLocation": "./.yarn/cache/globrex-npm-0.1.2-ddda94f2d0-adca162494.zip/node_modules/globrex/", + "packageDependencies": [ + ["globrex", "npm:0.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["google-protobuf", [ + ["npm:3.19.1", { + "packageLocation": "./.yarn/cache/google-protobuf-npm-3.19.1-f2bb0b2cd2-9ec57e1bdf.zip/node_modules/google-protobuf/", + "packageDependencies": [ + ["google-protobuf", "npm:3.19.1"] + ], + "linkType": "HARD", + }] + ]], + ["got", [ + ["npm:9.6.0", { + "packageLocation": "./.yarn/cache/got-npm-9.6.0-80edc15fd0-941807bd97.zip/node_modules/got/", + "packageDependencies": [ + ["got", "npm:9.6.0"], + ["@sindresorhus/is", "npm:0.14.0"], + ["@szmarczak/http-timer", "npm:1.1.2"], + ["@types/keyv", "npm:3.1.3"], + ["@types/responselike", "npm:1.0.0"], + ["cacheable-request", "npm:6.1.0"], + ["decompress-response", "npm:3.3.0"], + ["duplexer3", "npm:0.1.4"], + ["get-stream", "npm:4.1.0"], + ["lowercase-keys", "npm:1.0.1"], + ["mimic-response", "npm:1.0.1"], + ["p-cancelable", "npm:1.1.0"], + ["to-readable-stream", "npm:1.0.0"], + ["url-parse-lax", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["graceful-fs", [ + ["npm:4.2.10", { + "packageLocation": "./.yarn/cache/graceful-fs-npm-4.2.10-79c70989ca-3f109d70ae.zip/node_modules/graceful-fs/", + "packageDependencies": [ + ["graceful-fs", "npm:4.2.10"] + ], + "linkType": "HARD", + }] + ]], + ["grouped-queue", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/grouped-queue-npm-2.0.0-81fdc84ef7-be5c6cfac0.zip/node_modules/grouped-queue/", + "packageDependencies": [ + ["grouped-queue", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["growl", [ + ["npm:1.10.5", { + "packageLocation": "./.yarn/cache/growl-npm-1.10.5-2d1da54198-4b86685de6.zip/node_modules/growl/", + "packageDependencies": [ + ["growl", "npm:1.10.5"] + ], + "linkType": "HARD", + }] + ]], + ["grpc-web", [ + ["npm:1.2.1", { + "packageLocation": "./.yarn/cache/grpc-web-npm-1.2.1-3d331ff494-3860a37617.zip/node_modules/grpc-web/", + "packageDependencies": [ + ["grpc-web", "npm:1.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["handlebars", [ + ["npm:4.7.7", { + "packageLocation": "./.yarn/cache/handlebars-npm-4.7.7-a9ccfabf80-1e79a43f5e.zip/node_modules/handlebars/", + "packageDependencies": [ + ["handlebars", "npm:4.7.7"], + ["minimist", "npm:1.2.5"], + ["neo-async", "npm:2.6.2"], + ["source-map", "npm:0.6.1"], + ["uglify-js", "npm:3.14.4"], + ["wordwrap", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["har-schema", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/har-schema-npm-2.0.0-3a318c0ca5-d8946348f3.zip/node_modules/har-schema/", + "packageDependencies": [ + ["har-schema", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["har-validator", [ + ["npm:5.1.5", { + "packageLocation": "./.yarn/cache/har-validator-npm-5.1.5-bd9ac162f5-b998a7269c.zip/node_modules/har-validator/", + "packageDependencies": [ + ["har-validator", "npm:5.1.5"], + ["ajv", "npm:6.12.6"], + ["har-schema", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["hard-rejection", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/hard-rejection-npm-2.1.0-a80f2a977d-7baaf80a0c.zip/node_modules/hard-rejection/", + "packageDependencies": [ + ["hard-rejection", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["has", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/has-npm-1.0.3-b7f00631c1-b9ad53d53b.zip/node_modules/has/", + "packageDependencies": [ + ["has", "npm:1.0.3"], + ["function-bind", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["has-ansi", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/has-ansi-npm-2.0.0-9bf0cff2af-1b51daa021.zip/node_modules/has-ansi/", + "packageDependencies": [ + ["has-ansi", "npm:2.0.0"], + ["ansi-regex", "npm:2.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["has-bigints", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/has-bigints-npm-1.0.1-1b93717a74-44ab558681.zip/node_modules/has-bigints/", + "packageDependencies": [ + ["has-bigints", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["has-flag", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/has-flag-npm-3.0.0-16ac11fe05-4a15638b45.zip/node_modules/has-flag/", + "packageDependencies": [ + ["has-flag", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/has-flag-npm-4.0.0-32af9f0536-261a135703.zip/node_modules/has-flag/", + "packageDependencies": [ + ["has-flag", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["has-symbols", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/has-symbols-npm-1.0.2-50e53af115-2309c42607.zip/node_modules/has-symbols/", + "packageDependencies": [ + ["has-symbols", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["has-tostringtag", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/has-tostringtag-npm-1.0.0-b1fcf3ab55-cc12eb28cb.zip/node_modules/has-tostringtag/", + "packageDependencies": [ + ["has-tostringtag", "npm:1.0.0"], + ["has-symbols", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["has-unicode", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/has-unicode-npm-2.0.1-893adb4747-1eab07a743.zip/node_modules/has-unicode/", + "packageDependencies": [ + ["has-unicode", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["has-yarn", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/has-yarn-npm-2.1.0-b73f6750d9-5eb1d0bb85.zip/node_modules/has-yarn/", + "packageDependencies": [ + ["has-yarn", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["hasbin", [ + ["npm:1.2.3", { + "packageLocation": "./.yarn/cache/hasbin-npm-1.2.3-c030bf47c8-b30ae3dc4b.zip/node_modules/hasbin/", + "packageDependencies": [ + ["hasbin", "npm:1.2.3"], + ["async", "npm:1.5.2"] + ], + "linkType": "HARD", + }] + ]], + ["hash-base", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/hash-base-npm-3.1.0-26fc5711dd-26b7e97ac3.zip/node_modules/hash-base/", + "packageDependencies": [ + ["hash-base", "npm:3.1.0"], + ["inherits", "npm:2.0.4"], + ["readable-stream", "npm:3.6.0"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["hash.js", [ + ["npm:1.1.7", { + "packageLocation": "./.yarn/cache/hash.js-npm-1.1.7-f1ad187358-e350096e65.zip/node_modules/hash.js/", + "packageDependencies": [ + ["hash.js", "npm:1.1.7"], + ["inherits", "npm:2.0.4"], + ["minimalistic-assert", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["hasha", [ + ["npm:5.2.2", { + "packageLocation": "./.yarn/cache/hasha-npm-5.2.2-d171116d12-06cc474bed.zip/node_modules/hasha/", + "packageDependencies": [ + ["hasha", "npm:5.2.2"], + ["is-stream", "npm:2.0.1"], + ["type-fest", "npm:0.8.1"] + ], + "linkType": "HARD", + }] + ]], + ["he", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/he-npm-1.2.0-3b73a2ff07-3d4d6babcc.zip/node_modules/he/", + "packageDependencies": [ + ["he", "npm:1.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["hmac-drbg", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/hmac-drbg-npm-1.0.1-3499ad31cd-bd30b6a68d.zip/node_modules/hmac-drbg/", + "packageDependencies": [ + ["hmac-drbg", "npm:1.0.1"], + ["hash.js", "npm:1.1.7"], + ["minimalistic-assert", "npm:1.0.1"], + ["minimalistic-crypto-utils", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["hosted-git-info", [ + ["npm:2.8.9", { + "packageLocation": "./.yarn/cache/hosted-git-info-npm-2.8.9-62c44fa93f-c955394bda.zip/node_modules/hosted-git-info/", + "packageDependencies": [ + ["hosted-git-info", "npm:2.8.9"] + ], + "linkType": "HARD", + }], + ["npm:4.0.2", { + "packageLocation": "./.yarn/cache/hosted-git-info-npm-4.0.2-7330924e0c-d1b2d77203.zip/node_modules/hosted-git-info/", + "packageDependencies": [ + ["hosted-git-info", "npm:4.0.2"], + ["lru-cache", "npm:6.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["html-escaper", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/html-escaper-npm-2.0.2-38e51ef294-d2df2da3ad.zip/node_modules/html-escaper/", + "packageDependencies": [ + ["html-escaper", "npm:2.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["htmlescape", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/htmlescape-npm-1.1.1-21441b0193-c59a915ae6.zip/node_modules/htmlescape/", + "packageDependencies": [ + ["htmlescape", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["http-cache-semantics", [ + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/http-cache-semantics-npm-4.1.0-860520a31f-974de94a81.zip/node_modules/http-cache-semantics/", + "packageDependencies": [ + ["http-cache-semantics", "npm:4.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["http-call", [ + ["npm:5.3.0", { + "packageLocation": "./.yarn/cache/http-call-npm-5.3.0-f2c0703f3b-06e9342e1f.zip/node_modules/http-call/", + "packageDependencies": [ + ["http-call", "npm:5.3.0"], + ["content-type", "npm:1.0.4"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["is-retry-allowed", "npm:1.2.0"], + ["is-stream", "npm:2.0.1"], + ["parse-json", "npm:4.0.0"], + ["tunnel-agent", "npm:0.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["http-errors", [ + ["npm:1.7.2", { + "packageLocation": "./.yarn/cache/http-errors-npm-1.7.2-67163ae1df-5534b0ae08.zip/node_modules/http-errors/", + "packageDependencies": [ + ["http-errors", "npm:1.7.2"], + ["depd", "npm:1.1.2"], + ["inherits", "npm:2.0.3"], + ["setprototypeof", "npm:1.1.1"], + ["statuses", "npm:1.5.0"], + ["toidentifier", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["http-proxy", [ + ["npm:1.18.1", { + "packageLocation": "./.yarn/cache/http-proxy-npm-1.18.1-a313c479c5-f5bd96bf83.zip/node_modules/http-proxy/", + "packageDependencies": [ + ["http-proxy", "npm:1.18.1"], + ["eventemitter3", "npm:4.0.7"], + ["follow-redirects", "virtual:a313c479c5c7e54d9ec8fbeeea69ff640f56b8989ea2dff42351a3fa5c4061fb80a52d8ede0f0826a181a216820c2d2c3f15da881e7fdf31cef1c446e42f0c45#npm:1.14.5"], + ["requires-port", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["http-proxy-agent", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/http-proxy-agent-npm-4.0.1-ce9ef61788-c6a5da5a19.zip/node_modules/http-proxy-agent/", + "packageDependencies": [ + ["http-proxy-agent", "npm:4.0.1"], + ["@tootallnate/once", "npm:1.1.2"], + ["agent-base", "npm:6.0.2"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"] + ], + "linkType": "HARD", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/http-proxy-agent-npm-5.0.0-7f1f121b83-e2ee1ff165.zip/node_modules/http-proxy-agent/", + "packageDependencies": [ + ["http-proxy-agent", "npm:5.0.0"], + ["@tootallnate/once", "npm:2.0.0"], + ["agent-base", "npm:6.0.2"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"] + ], + "linkType": "HARD", + }] + ]], + ["http-signature", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/http-signature-npm-1.2.0-ee92426f34-3324598712.zip/node_modules/http-signature/", + "packageDependencies": [ + ["http-signature", "npm:1.2.0"], + ["assert-plus", "npm:1.0.0"], + ["jsprim", "npm:1.4.1"], + ["sshpk", "npm:1.16.1"] + ], + "linkType": "HARD", + }] + ]], + ["https-browserify", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/https-browserify-npm-1.0.0-7d6b10abbc-09b35353e4.zip/node_modules/https-browserify/", + "packageDependencies": [ + ["https-browserify", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["https-proxy-agent", [ + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/https-proxy-agent-npm-5.0.0-bb777903c3-165bfb090b.zip/node_modules/https-proxy-agent/", + "packageDependencies": [ + ["https-proxy-agent", "npm:5.0.0"], + ["agent-base", "npm:6.0.2"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"] + ], + "linkType": "HARD", + }] + ]], + ["human-signals", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/human-signals-npm-1.1.1-616b2586c2-d587647c9e.zip/node_modules/human-signals/", + "packageDependencies": [ + ["human-signals", "npm:1.1.1"] + ], + "linkType": "HARD", + }], + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/human-signals-npm-2.1.0-f75815481d-b87fd89fce.zip/node_modules/human-signals/", + "packageDependencies": [ + ["human-signals", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["humanize-ms", [ + ["npm:1.2.1", { + "packageLocation": "./.yarn/cache/humanize-ms-npm-1.2.1-e942bd7329-9c7a74a282.zip/node_modules/humanize-ms/", + "packageDependencies": [ + ["humanize-ms", "npm:1.2.1"], + ["ms", "npm:2.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["hyperlinker", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/hyperlinker-npm-1.0.0-c2e60c3b2a-f6d020ac55.zip/node_modules/hyperlinker/", + "packageDependencies": [ + ["hyperlinker", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["iconv-lite", [ + ["npm:0.4.24", { + "packageLocation": "./.yarn/cache/iconv-lite-npm-0.4.24-c5c4ac6695-bd9f120f5a.zip/node_modules/iconv-lite/", + "packageDependencies": [ + ["iconv-lite", "npm:0.4.24"], + ["safer-buffer", "npm:2.1.2"] + ], + "linkType": "HARD", + }], + ["npm:0.6.3", { + "packageLocation": "./.yarn/cache/iconv-lite-npm-0.6.3-24b8aae27e-3f60d47a5c.zip/node_modules/iconv-lite/", + "packageDependencies": [ + ["iconv-lite", "npm:0.6.3"], + ["safer-buffer", "npm:2.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["ieee754", [ + ["npm:1.1.13", { + "packageLocation": "./.yarn/cache/ieee754-npm-1.1.13-a57522ba12-102df1ba66.zip/node_modules/ieee754/", + "packageDependencies": [ + ["ieee754", "npm:1.1.13"] + ], + "linkType": "HARD", + }], + ["npm:1.2.1", { + "packageLocation": "./.yarn/cache/ieee754-npm-1.2.1-fb63b3caeb-5144c0c981.zip/node_modules/ieee754/", + "packageDependencies": [ + ["ieee754", "npm:1.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["ignore", [ + ["npm:4.0.6", { + "packageLocation": "./.yarn/cache/ignore-npm-4.0.6-66c0d6543e-248f82e50a.zip/node_modules/ignore/", + "packageDependencies": [ + ["ignore", "npm:4.0.6"] + ], + "linkType": "HARD", + }], + ["npm:5.2.0", { + "packageLocation": "./.yarn/cache/ignore-npm-5.2.0-fc4b58a4f3-6b1f926792.zip/node_modules/ignore/", + "packageDependencies": [ + ["ignore", "npm:5.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["ignore-by-default", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/ignore-by-default-npm-1.0.1-78ea10bc54-441509147b.zip/node_modules/ignore-by-default/", + "packageDependencies": [ + ["ignore-by-default", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["ignore-walk", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/ignore-walk-npm-4.0.1-e301e7e75f-903cd5cb68.zip/node_modules/ignore-walk/", + "packageDependencies": [ + ["ignore-walk", "npm:4.0.1"], + ["minimatch", "npm:3.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["immediate", [ + ["npm:3.0.6", { + "packageLocation": "./.yarn/cache/immediate-npm-3.0.6-c27588a2d3-f9b3486477.zip/node_modules/immediate/", + "packageDependencies": [ + ["immediate", "npm:3.0.6"] + ], + "linkType": "HARD", + }], + ["npm:3.2.3", { + "packageLocation": "./.yarn/cache/immediate-npm-3.2.3-c87ede9b47-9867dc7079.zip/node_modules/immediate/", + "packageDependencies": [ + ["immediate", "npm:3.2.3"] + ], + "linkType": "HARD", + }], + ["npm:3.3.0", { + "packageLocation": "./.yarn/cache/immediate-npm-3.3.0-d00fd9df7d-634b430510.zip/node_modules/immediate/", + "packageDependencies": [ + ["immediate", "npm:3.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["import-fresh", [ + ["npm:3.3.0", { + "packageLocation": "./.yarn/cache/import-fresh-npm-3.3.0-3e34265ca9-2cacfad06e.zip/node_modules/import-fresh/", + "packageDependencies": [ + ["import-fresh", "npm:3.3.0"], + ["parent-module", "npm:1.0.1"], + ["resolve-from", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["import-lazy", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/import-lazy-npm-2.1.0-b128ce6959-05294f3b9d.zip/node_modules/import-lazy/", + "packageDependencies": [ + ["import-lazy", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["import-local", [ + ["npm:3.0.3", { + "packageLocation": "./.yarn/cache/import-local-npm-3.0.3-fd16a368c1-38ae57d35e.zip/node_modules/import-local/", + "packageDependencies": [ + ["import-local", "npm:3.0.3"], + ["pkg-dir", "npm:4.2.0"], + ["resolve-cwd", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["imurmurhash", [ + ["npm:0.1.4", { + "packageLocation": "./.yarn/cache/imurmurhash-npm-0.1.4-610c5068a0-7cae75c8cd.zip/node_modules/imurmurhash/", + "packageDependencies": [ + ["imurmurhash", "npm:0.1.4"] + ], + "linkType": "HARD", + }] + ]], + ["indent-string", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/indent-string-npm-4.0.0-7b717435b2-824cfb9929.zip/node_modules/indent-string/", + "packageDependencies": [ + ["indent-string", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["infer-owner", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/infer-owner-npm-1.0.4-685ac3d2af-181e732764.zip/node_modules/infer-owner/", + "packageDependencies": [ + ["infer-owner", "npm:1.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["inflight", [ + ["npm:1.0.6", { + "packageLocation": "./.yarn/cache/inflight-npm-1.0.6-ccedb4b908-f4f76aa072.zip/node_modules/inflight/", + "packageDependencies": [ + ["inflight", "npm:1.0.6"], + ["once", "npm:1.4.0"], + ["wrappy", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["inherits", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/inherits-npm-2.0.1-0011554c03-6536b93772.zip/node_modules/inherits/", + "packageDependencies": [ + ["inherits", "npm:2.0.1"] + ], + "linkType": "HARD", + }], + ["npm:2.0.3", { + "packageLocation": "./.yarn/cache/inherits-npm-2.0.3-401e64b080-78cb8d7d85.zip/node_modules/inherits/", + "packageDependencies": [ + ["inherits", "npm:2.0.3"] + ], + "linkType": "HARD", + }], + ["npm:2.0.4", { + "packageLocation": "./.yarn/cache/inherits-npm-2.0.4-c66b3957a0-4a48a73384.zip/node_modules/inherits/", + "packageDependencies": [ + ["inherits", "npm:2.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["ini", [ + ["npm:1.3.8", { + "packageLocation": "./.yarn/cache/ini-npm-1.3.8-fb5040b4c0-dfd98b0ca3.zip/node_modules/ini/", + "packageDependencies": [ + ["ini", "npm:1.3.8"] + ], + "linkType": "HARD", + }], + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/ini-npm-2.0.0-28f7426761-e7aadc5fb2.zip/node_modules/ini/", + "packageDependencies": [ + ["ini", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["inline-source-map", [ + ["npm:0.6.2", { + "packageLocation": "./.yarn/cache/inline-source-map-npm-0.6.2-96902459a0-1f7fa2ad17.zip/node_modules/inline-source-map/", + "packageDependencies": [ + ["inline-source-map", "npm:0.6.2"], + ["source-map", "npm:0.5.7"] + ], + "linkType": "HARD", + }] + ]], + ["inquirer", [ + ["npm:8.2.0", { + "packageLocation": "./.yarn/cache/inquirer-npm-8.2.0-2bfa19a3d0-861d1a9324.zip/node_modules/inquirer/", + "packageDependencies": [ + ["inquirer", "npm:8.2.0"], + ["ansi-escapes", "npm:4.3.2"], + ["chalk", "npm:4.1.2"], + ["cli-cursor", "npm:3.1.0"], + ["cli-width", "npm:3.0.0"], + ["external-editor", "npm:3.1.0"], + ["figures", "npm:3.2.0"], + ["lodash", "npm:4.17.21"], + ["mute-stream", "npm:0.0.8"], + ["ora", "npm:5.4.1"], + ["run-async", "npm:2.4.1"], + ["rxjs", "npm:7.5.4"], + ["string-width", "npm:4.2.3"], + ["strip-ansi", "npm:6.0.1"], + ["through", "npm:2.3.8"] + ], + "linkType": "HARD", + }] + ]], + ["insert-module-globals", [ + ["npm:7.2.1", { + "packageLocation": "./.yarn/cache/insert-module-globals-npm-7.2.1-28e63ed201-c44de7e802.zip/node_modules/insert-module-globals/", + "packageDependencies": [ + ["insert-module-globals", "npm:7.2.1"], + ["JSONStream", "npm:1.3.5"], + ["acorn-node", "npm:1.8.2"], + ["combine-source-map", "npm:0.8.0"], + ["concat-stream", "npm:1.6.2"], + ["is-buffer", "npm:1.1.6"], + ["path-is-absolute", "npm:1.0.1"], + ["process", "npm:0.11.10"], + ["through2", "npm:2.0.5"], + ["undeclared-identifiers", "npm:1.1.3"], + ["xtend", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["internal-slot", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/internal-slot-npm-1.0.3-9e05eea002-1944f92e98.zip/node_modules/internal-slot/", + "packageDependencies": [ + ["internal-slot", "npm:1.0.3"], + ["get-intrinsic", "npm:1.1.1"], + ["has", "npm:1.0.3"], + ["side-channel", "npm:1.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["interpret", [ + ["npm:1.4.0", { + "packageLocation": "./.yarn/cache/interpret-npm-1.4.0-17b4b5b0a4-2e5f51268b.zip/node_modules/interpret/", + "packageDependencies": [ + ["interpret", "npm:1.4.0"] + ], + "linkType": "HARD", + }], + ["npm:2.2.0", { + "packageLocation": "./.yarn/cache/interpret-npm-2.2.0-3603a544e1-f51efef7cb.zip/node_modules/interpret/", + "packageDependencies": [ + ["interpret", "npm:2.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["ip", [ + ["npm:1.1.5", { + "packageLocation": "./.yarn/cache/ip-npm-1.1.5-af36318aa6-30133981f0.zip/node_modules/ip/", + "packageDependencies": [ + ["ip", "npm:1.1.5"] + ], + "linkType": "HARD", + }] + ]], + ["ip-regex", [ + ["npm:4.3.0", { + "packageLocation": "./.yarn/cache/ip-regex-npm-4.3.0-4ac12c6be9-7ff904b891.zip/node_modules/ip-regex/", + "packageDependencies": [ + ["ip-regex", "npm:4.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-arguments", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/is-arguments-npm-1.1.1-eff4f6d4d7-7f02700ec2.zip/node_modules/is-arguments/", + "packageDependencies": [ + ["is-arguments", "npm:1.1.1"], + ["call-bind", "npm:1.0.2"], + ["has-tostringtag", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-arrayish", [ + ["npm:0.2.1", { + "packageLocation": "./.yarn/cache/is-arrayish-npm-0.2.1-23927dfb15-eef4417e3c.zip/node_modules/is-arrayish/", + "packageDependencies": [ + ["is-arrayish", "npm:0.2.1"] + ], + "linkType": "HARD", + }], + ["npm:0.3.2", { + "packageLocation": "./.yarn/cache/is-arrayish-npm-0.3.2-f856180f79-977e64f54d.zip/node_modules/is-arrayish/", + "packageDependencies": [ + ["is-arrayish", "npm:0.3.2"] + ], + "linkType": "HARD", + }] + ]], + ["is-bigint", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/is-bigint-npm-1.0.4-31c2eecbc9-c56edfe09b.zip/node_modules/is-bigint/", + "packageDependencies": [ + ["is-bigint", "npm:1.0.4"], + ["has-bigints", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["is-binary-path", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/is-binary-path-npm-2.1.0-e61d46f557-84192eb88c.zip/node_modules/is-binary-path/", + "packageDependencies": [ + ["is-binary-path", "npm:2.1.0"], + ["binary-extensions", "npm:2.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-boolean-object", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/is-boolean-object-npm-1.1.2-ecbd575e6a-c03b23dbaa.zip/node_modules/is-boolean-object/", + "packageDependencies": [ + ["is-boolean-object", "npm:1.1.2"], + ["call-bind", "npm:1.0.2"], + ["has-tostringtag", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-buffer", [ + ["npm:1.1.6", { + "packageLocation": "./.yarn/cache/is-buffer-npm-1.1.6-08199d9ccc-4a186d995d.zip/node_modules/is-buffer/", + "packageDependencies": [ + ["is-buffer", "npm:1.1.6"] + ], + "linkType": "HARD", + }] + ]], + ["is-callable", [ + ["npm:1.2.4", { + "packageLocation": "./.yarn/cache/is-callable-npm-1.2.4-03fc17459c-1a28d57dc4.zip/node_modules/is-callable/", + "packageDependencies": [ + ["is-callable", "npm:1.2.4"] + ], + "linkType": "HARD", + }] + ]], + ["is-ci", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/is-ci-npm-2.0.0-8662a0f445-77b8690575.zip/node_modules/is-ci/", + "packageDependencies": [ + ["is-ci", "npm:2.0.0"], + ["ci-info", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-core-module", [ + ["npm:2.8.1", { + "packageLocation": "./.yarn/cache/is-core-module-npm-2.8.1-ce21740d1b-418b7bc107.zip/node_modules/is-core-module/", + "packageDependencies": [ + ["is-core-module", "npm:2.8.1"], + ["has", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["is-date-object", [ + ["npm:1.0.5", { + "packageLocation": "./.yarn/cache/is-date-object-npm-1.0.5-88f3d08b5e-baa9077cdf.zip/node_modules/is-date-object/", + "packageDependencies": [ + ["is-date-object", "npm:1.0.5"], + ["has-tostringtag", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-docker", [ + ["npm:2.2.1", { + "packageLocation": "./.yarn/cache/is-docker-npm-2.2.1-3f18a53aff-3fef7ddbf0.zip/node_modules/is-docker/", + "packageDependencies": [ + ["is-docker", "npm:2.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["is-extglob", [ + ["npm:2.1.1", { + "packageLocation": "./.yarn/cache/is-extglob-npm-2.1.1-0870ea68b5-df033653d0.zip/node_modules/is-extglob/", + "packageDependencies": [ + ["is-extglob", "npm:2.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["is-fullwidth-code-point", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/is-fullwidth-code-point-npm-1.0.0-0e436ba1ef-4d46a7465a.zip/node_modules/is-fullwidth-code-point/", + "packageDependencies": [ + ["is-fullwidth-code-point", "npm:1.0.0"], + ["number-is-nan", "npm:1.0.1"] + ], + "linkType": "HARD", + }], + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/is-fullwidth-code-point-npm-2.0.0-507f56ec71-eef9c6e15f.zip/node_modules/is-fullwidth-code-point/", + "packageDependencies": [ + ["is-fullwidth-code-point", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/is-fullwidth-code-point-npm-3.0.0-1ecf4ebee5-44a30c2945.zip/node_modules/is-fullwidth-code-point/", + "packageDependencies": [ + ["is-fullwidth-code-point", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-generator-function", [ + ["npm:1.0.10", { + "packageLocation": "./.yarn/cache/is-generator-function-npm-1.0.10-1d0f3809ef-d54644e7db.zip/node_modules/is-generator-function/", + "packageDependencies": [ + ["is-generator-function", "npm:1.0.10"], + ["has-tostringtag", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-glob", [ + ["npm:4.0.3", { + "packageLocation": "./.yarn/cache/is-glob-npm-4.0.3-cb87bf1bdb-d381c1319f.zip/node_modules/is-glob/", + "packageDependencies": [ + ["is-glob", "npm:4.0.3"], + ["is-extglob", "npm:2.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["is-installed-globally", [ + ["npm:0.4.0", { + "packageLocation": "./.yarn/cache/is-installed-globally-npm-0.4.0-a30dd056c7-3359840d59.zip/node_modules/is-installed-globally/", + "packageDependencies": [ + ["is-installed-globally", "npm:0.4.0"], + ["global-dirs", "npm:3.0.0"], + ["is-path-inside", "npm:3.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["is-interactive", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/is-interactive-npm-1.0.0-7ff7c6e04a-824808776e.zip/node_modules/is-interactive/", + "packageDependencies": [ + ["is-interactive", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-ip", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/is-ip-npm-3.1.0-7b8bc9330c-da2c2b2824.zip/node_modules/is-ip/", + "packageDependencies": [ + ["is-ip", "npm:3.1.0"], + ["ip-regex", "npm:4.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-lambda", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/is-lambda-npm-1.0.1-7ab55bc8a8-93a32f0194.zip/node_modules/is-lambda/", + "packageDependencies": [ + ["is-lambda", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["is-nan", [ + ["npm:1.3.2", { + "packageLocation": "./.yarn/cache/is-nan-npm-1.3.2-a087d31a28-5dfadcef6a.zip/node_modules/is-nan/", + "packageDependencies": [ + ["is-nan", "npm:1.3.2"], + ["call-bind", "npm:1.0.2"], + ["define-properties", "npm:1.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["is-negative-zero", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/is-negative-zero-npm-2.0.1-d8f3dbcfe1-a46f2e0cb5.zip/node_modules/is-negative-zero/", + "packageDependencies": [ + ["is-negative-zero", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["is-npm", [ + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/is-npm-npm-5.0.0-2758bcd54b-9baff02b0c.zip/node_modules/is-npm/", + "packageDependencies": [ + ["is-npm", "npm:5.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-number", [ + ["npm:7.0.0", { + "packageLocation": "./.yarn/cache/is-number-npm-7.0.0-060086935c-456ac6f8e0.zip/node_modules/is-number/", + "packageDependencies": [ + ["is-number", "npm:7.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-number-object", [ + ["npm:1.0.6", { + "packageLocation": "./.yarn/cache/is-number-object-npm-1.0.6-88e8d0e936-c697704e8f.zip/node_modules/is-number-object/", + "packageDependencies": [ + ["is-number-object", "npm:1.0.6"], + ["has-tostringtag", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-obj", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/is-obj-npm-2.0.0-3d95e053f4-c9916ac8f4.zip/node_modules/is-obj/", + "packageDependencies": [ + ["is-obj", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-path-inside", [ + ["npm:3.0.3", { + "packageLocation": "./.yarn/cache/is-path-inside-npm-3.0.3-2ea0ef44fd-abd50f0618.zip/node_modules/is-path-inside/", + "packageDependencies": [ + ["is-path-inside", "npm:3.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["is-plain-obj", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/is-plain-obj-npm-1.1.0-1046f64c0b-0ee0480779.zip/node_modules/is-plain-obj/", + "packageDependencies": [ + ["is-plain-obj", "npm:1.1.0"] + ], + "linkType": "HARD", + }], + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/is-plain-obj-npm-2.1.0-8dffd7ae9c-cec9100678.zip/node_modules/is-plain-obj/", + "packageDependencies": [ + ["is-plain-obj", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-plain-object", [ + ["npm:2.0.4", { + "packageLocation": "./.yarn/cache/is-plain-object-npm-2.0.4-da3265d804-2a401140cf.zip/node_modules/is-plain-object/", + "packageDependencies": [ + ["is-plain-object", "npm:2.0.4"], + ["isobject", "npm:3.0.1"] + ], + "linkType": "HARD", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/is-plain-object-npm-5.0.0-285b70faa3-e32d27061e.zip/node_modules/is-plain-object/", + "packageDependencies": [ + ["is-plain-object", "npm:5.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-regex", [ + ["npm:1.1.4", { + "packageLocation": "./.yarn/cache/is-regex-npm-1.1.4-cca193ef11-362399b335.zip/node_modules/is-regex/", + "packageDependencies": [ + ["is-regex", "npm:1.1.4"], + ["call-bind", "npm:1.0.2"], + ["has-tostringtag", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-retry-allowed", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/is-retry-allowed-npm-1.2.0-730be11f6c-50d700a89a.zip/node_modules/is-retry-allowed/", + "packageDependencies": [ + ["is-retry-allowed", "npm:1.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-scoped", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/is-scoped-npm-2.1.0-7710eece3d-bc4726ec6c.zip/node_modules/is-scoped/", + "packageDependencies": [ + ["is-scoped", "npm:2.1.0"], + ["scoped-regex", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-shared-array-buffer", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/is-shared-array-buffer-npm-1.0.1-84bc270861-2ffb92533e.zip/node_modules/is-shared-array-buffer/", + "packageDependencies": [ + ["is-shared-array-buffer", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["is-stream", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/is-stream-npm-1.1.0-818ecbf6bb-063c6bec9d.zip/node_modules/is-stream/", + "packageDependencies": [ + ["is-stream", "npm:1.1.0"] + ], + "linkType": "HARD", + }], + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/is-stream-npm-2.0.1-c802db55e7-b8e05ccdf9.zip/node_modules/is-stream/", + "packageDependencies": [ + ["is-stream", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["is-string", [ + ["npm:1.0.7", { + "packageLocation": "./.yarn/cache/is-string-npm-1.0.7-9f7066daed-323b3d0462.zip/node_modules/is-string/", + "packageDependencies": [ + ["is-string", "npm:1.0.7"], + ["has-tostringtag", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-symbol", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/is-symbol-npm-1.0.4-eb9baac703-92805812ef.zip/node_modules/is-symbol/", + "packageDependencies": [ + ["is-symbol", "npm:1.0.4"], + ["has-symbols", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["is-text-path", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/is-text-path-npm-1.0.1-92c78fe58d-fb5d78752c.zip/node_modules/is-text-path/", + "packageDependencies": [ + ["is-text-path", "npm:1.0.1"], + ["text-extensions", "npm:1.9.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-typed-array", [ + ["npm:1.1.8", { + "packageLocation": "./.yarn/cache/is-typed-array-npm-1.1.8-147f090d0d-aa0f9f0716.zip/node_modules/is-typed-array/", + "packageDependencies": [ + ["is-typed-array", "npm:1.1.8"], + ["available-typed-arrays", "npm:1.0.5"], + ["call-bind", "npm:1.0.2"], + ["es-abstract", "npm:1.19.1"], + ["foreach", "npm:2.0.5"], + ["has-tostringtag", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-typedarray", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/is-typedarray-npm-1.0.0-bbd99de5b6-3508c6cd0a.zip/node_modules/is-typedarray/", + "packageDependencies": [ + ["is-typedarray", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-unicode-supported", [ + ["npm:0.1.0", { + "packageLocation": "./.yarn/cache/is-unicode-supported-npm-0.1.0-0833e1bbfb-a2aab86ee7.zip/node_modules/is-unicode-supported/", + "packageDependencies": [ + ["is-unicode-supported", "npm:0.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["is-utf8", [ + ["npm:0.2.1", { + "packageLocation": "./.yarn/cache/is-utf8-npm-0.2.1-46ab364e2f-167ccd2be8.zip/node_modules/is-utf8/", + "packageDependencies": [ + ["is-utf8", "npm:0.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["is-weakref", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/is-weakref-npm-1.0.1-152a166933-fdafb7b955.zip/node_modules/is-weakref/", + "packageDependencies": [ + ["is-weakref", "npm:1.0.1"], + ["call-bind", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["is-windows", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/is-windows-npm-1.0.2-898cd6f3d7-438b7e5265.zip/node_modules/is-windows/", + "packageDependencies": [ + ["is-windows", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["is-wsl", [ + ["npm:2.2.0", { + "packageLocation": "./.yarn/cache/is-wsl-npm-2.2.0-2ba10d6393-20849846ae.zip/node_modules/is-wsl/", + "packageDependencies": [ + ["is-wsl", "npm:2.2.0"], + ["is-docker", "npm:2.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["is-yarn-global", [ + ["npm:0.3.0", { + "packageLocation": "./.yarn/cache/is-yarn-global-npm-0.3.0-18cad00879-bca013d65f.zip/node_modules/is-yarn-global/", + "packageDependencies": [ + ["is-yarn-global", "npm:0.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["isarray", [ + ["npm:0.0.1", { + "packageLocation": "./.yarn/cache/isarray-npm-0.0.1-92e37e0a70-49191f1425.zip/node_modules/isarray/", + "packageDependencies": [ + ["isarray", "npm:0.0.1"] + ], + "linkType": "HARD", + }], + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/isarray-npm-1.0.0-db4f547720-f032df8e02.zip/node_modules/isarray/", + "packageDependencies": [ + ["isarray", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["isbinaryfile", [ + ["npm:4.0.8", { + "packageLocation": "./.yarn/cache/isbinaryfile-npm-4.0.8-62c71dd57b-606e3bb648.zip/node_modules/isbinaryfile/", + "packageDependencies": [ + ["isbinaryfile", "npm:4.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["isexe", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/isexe-npm-2.0.0-b58870bd2e-26bf6c5480.zip/node_modules/isexe/", + "packageDependencies": [ + ["isexe", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["isobject", [ + ["npm:3.0.1", { + "packageLocation": "./.yarn/cache/isobject-npm-3.0.1-8145901fd2-db85c4c970.zip/node_modules/isobject/", + "packageDependencies": [ + ["isobject", "npm:3.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["isomorphic-ws", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/isomorphic-ws-npm-4.0.1-aa39192848-d7190eadef.zip/node_modules/isomorphic-ws/", + "packageDependencies": [ + ["isomorphic-ws", "npm:4.0.1"] + ], + "linkType": "SOFT", + }], + ["virtual:2fd01a647a5c8b340dd0adae82833428da22baa88327826cc44910efed2d9ad403a63cf3d639ef91449c9c952ea1afcdd4379371f5022db3053206940836b879#npm:4.0.1", { + "packageLocation": "./.yarn/__virtual__/isomorphic-ws-virtual-43a65c6c81/0/cache/isomorphic-ws-npm-4.0.1-aa39192848-d7190eadef.zip/node_modules/isomorphic-ws/", + "packageDependencies": [ + ["isomorphic-ws", "virtual:2fd01a647a5c8b340dd0adae82833428da22baa88327826cc44910efed2d9ad403a63cf3d639ef91449c9c952ea1afcdd4379371f5022db3053206940836b879#npm:4.0.1"], + ["@types/ws", "npm:7.4.7"], + ["ws", "virtual:2fd01a647a5c8b340dd0adae82833428da22baa88327826cc44910efed2d9ad403a63cf3d639ef91449c9c952ea1afcdd4379371f5022db3053206940836b879#npm:7.5.5"] + ], + "packagePeers": [ + "@types/ws", + "ws" + ], + "linkType": "HARD", + }] + ]], + ["isstream", [ + ["npm:0.1.2", { + "packageLocation": "./.yarn/cache/isstream-npm-0.1.2-8581c75385-1eb2fe63a7.zip/node_modules/isstream/", + "packageDependencies": [ + ["isstream", "npm:0.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["istanbul-lib-coverage", [ + ["npm:3.2.0", { + "packageLocation": "./.yarn/cache/istanbul-lib-coverage-npm-3.2.0-93f84b2c8c-a2a545033b.zip/node_modules/istanbul-lib-coverage/", + "packageDependencies": [ + ["istanbul-lib-coverage", "npm:3.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["istanbul-lib-hook", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/istanbul-lib-hook-npm-3.0.0-be73f95173-ac4d0a0751.zip/node_modules/istanbul-lib-hook/", + "packageDependencies": [ + ["istanbul-lib-hook", "npm:3.0.0"], + ["append-transform", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["istanbul-lib-instrument", [ + ["npm:4.0.3", { + "packageLocation": "./.yarn/cache/istanbul-lib-instrument-npm-4.0.3-4d4c2263f8-fa1171d302.zip/node_modules/istanbul-lib-instrument/", + "packageDependencies": [ + ["istanbul-lib-instrument", "npm:4.0.3"], + ["@babel/core", "npm:7.16.0"], + ["@istanbuljs/schema", "npm:0.1.3"], + ["istanbul-lib-coverage", "npm:3.2.0"], + ["semver", "npm:6.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["istanbul-lib-processinfo", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/istanbul-lib-processinfo-npm-2.0.2-74916fa6cb-400bd0b25b.zip/node_modules/istanbul-lib-processinfo/", + "packageDependencies": [ + ["istanbul-lib-processinfo", "npm:2.0.2"], + ["archy", "npm:1.0.0"], + ["cross-spawn", "npm:7.0.3"], + ["istanbul-lib-coverage", "npm:3.2.0"], + ["make-dir", "npm:3.1.0"], + ["p-map", "npm:3.0.0"], + ["rimraf", "npm:3.0.2"], + ["uuid", "npm:3.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["istanbul-lib-report", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/istanbul-lib-report-npm-3.0.0-660f97340a-3f29eb3f53.zip/node_modules/istanbul-lib-report/", + "packageDependencies": [ + ["istanbul-lib-report", "npm:3.0.0"], + ["istanbul-lib-coverage", "npm:3.2.0"], + ["make-dir", "npm:3.1.0"], + ["supports-color", "npm:7.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["istanbul-lib-source-maps", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/istanbul-lib-source-maps-npm-4.0.1-af0f859df7-21ad3df45d.zip/node_modules/istanbul-lib-source-maps/", + "packageDependencies": [ + ["istanbul-lib-source-maps", "npm:4.0.1"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["istanbul-lib-coverage", "npm:3.2.0"], + ["source-map", "npm:0.6.1"] + ], + "linkType": "HARD", + }] + ]], + ["istanbul-reports", [ + ["npm:3.0.5", { + "packageLocation": "./.yarn/cache/istanbul-reports-npm-3.0.5-2a13f8a7b1-b167411c4c.zip/node_modules/istanbul-reports/", + "packageDependencies": [ + ["istanbul-reports", "npm:3.0.5"], + ["html-escaper", "npm:2.0.2"], + ["istanbul-lib-report", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["jake", [ + ["npm:10.8.2", { + "packageLocation": "./.yarn/cache/jake-npm-10.8.2-e211473cb9-b604c51863.zip/node_modules/jake/", + "packageDependencies": [ + ["jake", "npm:10.8.2"], + ["async", "npm:0.9.2"], + ["chalk", "npm:2.4.2"], + ["filelist", "npm:1.0.2"], + ["minimatch", "npm:3.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["javascript-natural-sort", [ + ["npm:0.7.1", { + "packageLocation": "./.yarn/cache/javascript-natural-sort-npm-0.7.1-9018625996-161e2c512c.zip/node_modules/javascript-natural-sort/", + "packageDependencies": [ + ["javascript-natural-sort", "npm:0.7.1"] + ], + "linkType": "HARD", + }] + ]], + ["jayson", [ + ["npm:2.1.2", { + "packageLocation": "./.yarn/cache/jayson-npm-2.1.2-f9240aab4b-7d66d37ea0.zip/node_modules/jayson/", + "packageDependencies": [ + ["jayson", "npm:2.1.2"], + ["@types/node", "npm:10.17.60"], + ["JSONStream", "npm:1.3.5"], + ["commander", "npm:2.20.3"], + ["es6-promisify", "npm:5.0.0"], + ["eyes", "npm:0.1.8"], + ["json-stringify-safe", "npm:5.0.1"], + ["lodash", "npm:4.17.21"], + ["uuid", "npm:3.4.0"] + ], + "linkType": "HARD", + }], + ["npm:3.6.5", { + "packageLocation": "./.yarn/cache/jayson-npm-3.6.5-2fd01a647a-dde536e720.zip/node_modules/jayson/", + "packageDependencies": [ + ["jayson", "npm:3.6.5"], + ["@types/connect", "npm:3.4.35"], + ["@types/express-serve-static-core", "npm:4.17.25"], + ["@types/lodash", "npm:4.14.177"], + ["@types/node", "npm:12.20.37"], + ["@types/ws", "npm:7.4.7"], + ["JSONStream", "npm:1.3.5"], + ["commander", "npm:2.20.3"], + ["delay", "npm:5.0.0"], + ["es6-promisify", "npm:5.0.0"], + ["eyes", "npm:0.1.8"], + ["isomorphic-ws", "virtual:2fd01a647a5c8b340dd0adae82833428da22baa88327826cc44910efed2d9ad403a63cf3d639ef91449c9c952ea1afcdd4379371f5022db3053206940836b879#npm:4.0.1"], + ["json-stringify-safe", "npm:5.0.1"], + ["lodash", "npm:4.17.21"], + ["uuid", "npm:3.4.0"], + ["ws", "virtual:2fd01a647a5c8b340dd0adae82833428da22baa88327826cc44910efed2d9ad403a63cf3d639ef91449c9c952ea1afcdd4379371f5022db3053206940836b879#npm:7.5.5"] + ], + "linkType": "HARD", + }] + ]], + ["jest-diff", [ + ["npm:27.3.1", { + "packageLocation": "./.yarn/cache/jest-diff-npm-27.3.1-c347dd1e5a-49231a4ac4.zip/node_modules/jest-diff/", + "packageDependencies": [ + ["jest-diff", "npm:27.3.1"], + ["chalk", "npm:4.1.2"], + ["diff-sequences", "npm:27.0.6"], + ["jest-get-type", "npm:27.3.1"], + ["pretty-format", "npm:27.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["jest-get-type", [ + ["npm:27.3.1", { + "packageLocation": "./.yarn/cache/jest-get-type-npm-27.3.1-fdb27a0157-b0b8db1d77.zip/node_modules/jest-get-type/", + "packageDependencies": [ + ["jest-get-type", "npm:27.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["jest-matcher-utils", [ + ["npm:27.3.1", { + "packageLocation": "./.yarn/cache/jest-matcher-utils-npm-27.3.1-8eb9f8e92d-118c428b55.zip/node_modules/jest-matcher-utils/", + "packageDependencies": [ + ["jest-matcher-utils", "npm:27.3.1"], + ["chalk", "npm:4.1.2"], + ["jest-diff", "npm:27.3.1"], + ["jest-get-type", "npm:27.3.1"], + ["pretty-format", "npm:27.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["jest-message-util", [ + ["npm:27.3.1", { + "packageLocation": "./.yarn/cache/jest-message-util-npm-27.3.1-0d163b84de-2d10734765.zip/node_modules/jest-message-util/", + "packageDependencies": [ + ["jest-message-util", "npm:27.3.1"], + ["@babel/code-frame", "npm:7.16.7"], + ["@jest/types", "npm:27.2.5"], + ["@types/stack-utils", "npm:2.0.1"], + ["chalk", "npm:4.1.2"], + ["graceful-fs", "npm:4.2.10"], + ["micromatch", "npm:4.0.4"], + ["pretty-format", "npm:27.3.1"], + ["slash", "npm:3.0.0"], + ["stack-utils", "npm:2.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["jest-regex-util", [ + ["npm:27.0.6", { + "packageLocation": "./.yarn/cache/jest-regex-util-npm-27.0.6-02fca95995-4d613b00f2.zip/node_modules/jest-regex-util/", + "packageDependencies": [ + ["jest-regex-util", "npm:27.0.6"] + ], + "linkType": "HARD", + }] + ]], + ["jest-worker", [ + ["npm:27.5.1", { + "packageLocation": "./.yarn/cache/jest-worker-npm-27.5.1-1c110b5894-98cd68b696.zip/node_modules/jest-worker/", + "packageDependencies": [ + ["jest-worker", "npm:27.5.1"], + ["@types/node", "npm:17.0.21"], + ["merge-stream", "npm:2.0.0"], + ["supports-color", "npm:8.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["jmespath", [ + ["npm:0.15.0", { + "packageLocation": "./.yarn/cache/jmespath-npm-0.15.0-df80ed6dd1-353bb9e69c.zip/node_modules/jmespath/", + "packageDependencies": [ + ["jmespath", "npm:0.15.0"] + ], + "linkType": "HARD", + }], + ["npm:0.16.0", { + "packageLocation": "./.yarn/cache/jmespath-npm-0.16.0-d47535c65a-2d602493a1.zip/node_modules/jmespath/", + "packageDependencies": [ + ["jmespath", "npm:0.16.0"] + ], + "linkType": "HARD", + }] + ]], + ["joycon", [ + ["npm:2.2.5", { + "packageLocation": "./.yarn/cache/joycon-npm-2.2.5-fff23ab519-930bb748c0.zip/node_modules/joycon/", + "packageDependencies": [ + ["joycon", "npm:2.2.5"] + ], + "linkType": "HARD", + }] + ]], + ["js-base64", [ + ["npm:2.6.4", { + "packageLocation": "./.yarn/cache/js-base64-npm-2.6.4-569350f803-5f4084078d.zip/node_modules/js-base64/", + "packageDependencies": [ + ["js-base64", "npm:2.6.4"] + ], + "linkType": "HARD", + }] + ]], + ["js-merkle", [ + ["npm:0.1.5", { + "packageLocation": "./.yarn/cache/js-merkle-npm-0.1.5-d759dbfead-1b19f50c06.zip/node_modules/js-merkle/", + "packageDependencies": [ + ["js-merkle", "npm:0.1.5"] + ], + "linkType": "HARD", + }] + ]], + ["js-tokens", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/js-tokens-npm-4.0.0-0ac852e9e2-8a95213a5a.zip/node_modules/js-tokens/", + "packageDependencies": [ + ["js-tokens", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["js-yaml", [ + ["npm:3.13.1", { + "packageLocation": "./.yarn/cache/js-yaml-npm-3.13.1-3a28ff3b75-7511b764ab.zip/node_modules/js-yaml/", + "packageDependencies": [ + ["js-yaml", "npm:3.13.1"], + ["argparse", "npm:1.0.10"], + ["esprima", "npm:4.0.1"] + ], + "linkType": "HARD", + }], + ["npm:3.14.1", { + "packageLocation": "./.yarn/cache/js-yaml-npm-3.14.1-b968c6095e-bef146085f.zip/node_modules/js-yaml/", + "packageDependencies": [ + ["js-yaml", "npm:3.14.1"], + ["argparse", "npm:1.0.10"], + ["esprima", "npm:4.0.1"] + ], + "linkType": "HARD", + }], + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/js-yaml-npm-4.1.0-3606f32312-c7830dfd45.zip/node_modules/js-yaml/", + "packageDependencies": [ + ["js-yaml", "npm:4.1.0"], + ["argparse", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["jsbn", [ + ["npm:0.1.1", { + "packageLocation": "./.yarn/cache/jsbn-npm-0.1.1-0eb7132404-e5ff29c1b8.zip/node_modules/jsbn/", + "packageDependencies": [ + ["jsbn", "npm:0.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["jsdoctypeparser", [ + ["npm:6.1.0", { + "packageLocation": "./.yarn/cache/jsdoctypeparser-npm-6.1.0-069387bc3e-14a0ef3671.zip/node_modules/jsdoctypeparser/", + "packageDependencies": [ + ["jsdoctypeparser", "npm:6.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["jsesc", [ + ["npm:0.5.0", { + "packageLocation": "./.yarn/cache/jsesc-npm-0.5.0-6827074492-b8b44cbfc9.zip/node_modules/jsesc/", + "packageDependencies": [ + ["jsesc", "npm:0.5.0"] + ], + "linkType": "HARD", + }], + ["npm:2.5.2", { + "packageLocation": "./.yarn/cache/jsesc-npm-2.5.2-c5acb78804-4dc1907711.zip/node_modules/jsesc/", + "packageDependencies": [ + ["jsesc", "npm:2.5.2"] + ], + "linkType": "HARD", + }] + ]], + ["json-buffer", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/json-buffer-npm-3.0.0-21c267a314-0cecacb802.zip/node_modules/json-buffer/", + "packageDependencies": [ + ["json-buffer", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["json-parse-better-errors", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/json-parse-better-errors-npm-1.0.2-7f37637d19-ff2b5ba2a7.zip/node_modules/json-parse-better-errors/", + "packageDependencies": [ + ["json-parse-better-errors", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["json-parse-even-better-errors", [ + ["npm:2.3.1", { + "packageLocation": "./.yarn/cache/json-parse-even-better-errors-npm-2.3.1-144d62256e-798ed4cf33.zip/node_modules/json-parse-even-better-errors/", + "packageDependencies": [ + ["json-parse-even-better-errors", "npm:2.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["json-pointer", [ + ["npm:0.6.1", { + "packageLocation": "./.yarn/cache/json-pointer-npm-0.6.1-d72965882f-882b4b24b5.zip/node_modules/json-pointer/", + "packageDependencies": [ + ["json-pointer", "npm:0.6.1"], + ["foreach", "npm:2.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["json-schema", [ + ["npm:0.2.3", { + "packageLocation": "./.yarn/cache/json-schema-npm-0.2.3-018ee3dfc9-bbc2070988.zip/node_modules/json-schema/", + "packageDependencies": [ + ["json-schema", "npm:0.2.3"] + ], + "linkType": "HARD", + }] + ]], + ["json-schema-diff-validator", [ + ["npm:0.4.1", { + "packageLocation": "./.yarn/cache/json-schema-diff-validator-npm-0.4.1-a98b53360c-d75b7f5540.zip/node_modules/json-schema-diff-validator/", + "packageDependencies": [ + ["json-schema-diff-validator", "npm:0.4.1"], + ["fast-json-patch", "npm:2.2.1"], + ["json-pointer", "npm:0.6.1"] + ], + "linkType": "HARD", + }] + ]], + ["json-schema-ref-parser", [ + ["npm:7.1.4", { + "packageLocation": "./.yarn/cache/json-schema-ref-parser-npm-7.1.4-948f9e347a-690252bb1e.zip/node_modules/json-schema-ref-parser/", + "packageDependencies": [ + ["json-schema-ref-parser", "npm:7.1.4"], + ["call-me-maybe", "npm:1.0.1"], + ["js-yaml", "npm:3.14.1"], + ["ono", "npm:6.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["json-schema-traverse", [ + ["npm:0.4.1", { + "packageLocation": "./.yarn/cache/json-schema-traverse-npm-0.4.1-4759091693-7486074d3b.zip/node_modules/json-schema-traverse/", + "packageDependencies": [ + ["json-schema-traverse", "npm:0.4.1"] + ], + "linkType": "HARD", + }], + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/json-schema-traverse-npm-1.0.0-fb3684f4f0-02f2f466cd.zip/node_modules/json-schema-traverse/", + "packageDependencies": [ + ["json-schema-traverse", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["json-stable-stringify", [ + ["npm:0.0.1", { + "packageLocation": "./.yarn/cache/json-stable-stringify-npm-0.0.1-9428a6f044-3a148d4c32.zip/node_modules/json-stable-stringify/", + "packageDependencies": [ + ["json-stable-stringify", "npm:0.0.1"], + ["jsonify", "npm:0.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["json-stable-stringify-without-jsonify", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/json-stable-stringify-without-jsonify-npm-1.0.1-b65772b28b-cff44156dd.zip/node_modules/json-stable-stringify-without-jsonify/", + "packageDependencies": [ + ["json-stable-stringify-without-jsonify", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["json-stringify-nice", [ + ["npm:1.1.4", { + "packageLocation": "./.yarn/cache/json-stringify-nice-npm-1.1.4-0b0ddb188b-6ddf781148.zip/node_modules/json-stringify-nice/", + "packageDependencies": [ + ["json-stringify-nice", "npm:1.1.4"] + ], + "linkType": "HARD", + }] + ]], + ["json-stringify-safe", [ + ["npm:5.0.1", { + "packageLocation": "./.yarn/cache/json-stringify-safe-npm-5.0.1-064ddd6ab4-48ec0adad5.zip/node_modules/json-stringify-safe/", + "packageDependencies": [ + ["json-stringify-safe", "npm:5.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["json5", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/json5-npm-1.0.1-647fc8794b-e76ea23dbb.zip/node_modules/json5/", + "packageDependencies": [ + ["json5", "npm:1.0.1"], + ["minimist", "npm:1.2.5"] + ], + "linkType": "HARD", + }], + ["npm:2.2.0", { + "packageLocation": "./.yarn/cache/json5-npm-2.2.0-da49dc7cb5-e88fc5274b.zip/node_modules/json5/", + "packageDependencies": [ + ["json5", "npm:2.2.0"], + ["minimist", "npm:1.2.5"] + ], + "linkType": "HARD", + }] + ]], + ["jsonfile", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/jsonfile-npm-4.0.0-10ce3aea15-6447d6224f.zip/node_modules/jsonfile/", + "packageDependencies": [ + ["jsonfile", "npm:4.0.0"], + ["graceful-fs", "npm:4.2.10"] + ], + "linkType": "HARD", + }], + ["npm:6.1.0", { + "packageLocation": "./.yarn/cache/jsonfile-npm-6.1.0-20a4796cee-7af3b8e1ac.zip/node_modules/jsonfile/", + "packageDependencies": [ + ["jsonfile", "npm:6.1.0"], + ["graceful-fs", "npm:4.2.10"], + ["universalify", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["jsonify", [ + ["npm:0.0.0", { + "packageLocation": "./.yarn/cache/jsonify-npm-0.0.0-80da2da40c-d8d4ed476c.zip/node_modules/jsonify/", + "packageDependencies": [ + ["jsonify", "npm:0.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["jsonparse", [ + ["npm:1.3.1", { + "packageLocation": "./.yarn/cache/jsonparse-npm-1.3.1-b6fde74828-6514a7be46.zip/node_modules/jsonparse/", + "packageDependencies": [ + ["jsonparse", "npm:1.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["jsprim", [ + ["npm:1.4.1", { + "packageLocation": "./.yarn/cache/jsprim-npm-1.4.1-948d2c9ec3-6bcb20ec26.zip/node_modules/jsprim/", + "packageDependencies": [ + ["jsprim", "npm:1.4.1"], + ["assert-plus", "npm:1.0.0"], + ["extsprintf", "npm:1.3.0"], + ["json-schema", "npm:0.2.3"], + ["verror", "npm:1.10.0"] + ], + "linkType": "HARD", + }] + ]], + ["just-diff", [ + ["npm:5.0.1", { + "packageLocation": "./.yarn/cache/just-diff-npm-5.0.1-6477d7b637-efbdb65298.zip/node_modules/just-diff/", + "packageDependencies": [ + ["just-diff", "npm:5.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["just-diff-apply", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/just-diff-apply-npm-4.0.1-dfc12fe759-fdb58c0c8d.zip/node_modules/just-diff-apply/", + "packageDependencies": [ + ["just-diff-apply", "npm:4.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["just-extend", [ + ["npm:4.2.1", { + "packageLocation": "./.yarn/cache/just-extend-npm-4.2.1-ccc4201277-ff9fdede24.zip/node_modules/just-extend/", + "packageDependencies": [ + ["just-extend", "npm:4.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["karma", [ + ["npm:6.3.9", { + "packageLocation": "./.yarn/cache/karma-npm-6.3.9-f5f936b668-2e652c8f4d.zip/node_modules/karma/", + "packageDependencies": [ + ["karma", "npm:6.3.9"], + ["body-parser", "npm:1.19.0"], + ["braces", "npm:3.0.2"], + ["chokidar", "npm:3.5.2"], + ["colors", "npm:1.4.0"], + ["connect", "npm:3.7.0"], + ["di", "npm:0.0.1"], + ["dom-serialize", "npm:2.2.1"], + ["glob", "npm:7.2.0"], + ["graceful-fs", "npm:4.2.10"], + ["http-proxy", "npm:1.18.1"], + ["isbinaryfile", "npm:4.0.8"], + ["lodash", "npm:4.17.21"], + ["log4js", "npm:6.3.0"], + ["mime", "npm:2.6.0"], + ["minimatch", "npm:3.0.4"], + ["qjobs", "npm:1.2.0"], + ["range-parser", "npm:1.2.1"], + ["rimraf", "npm:3.0.2"], + ["socket.io", "npm:4.4.0"], + ["source-map", "npm:0.6.1"], + ["tmp", "npm:0.2.1"], + ["ua-parser-js", "npm:0.7.31"], + ["yargs", "npm:16.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["karma-chai", [ + ["npm:0.1.0", { + "packageLocation": "./.yarn/cache/karma-chai-npm-0.1.0-d1d807f507-7fae0b4ace.zip/node_modules/karma-chai/", + "packageDependencies": [ + ["karma-chai", "npm:0.1.0"] + ], + "linkType": "SOFT", + }], + ["virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:0.1.0", { + "packageLocation": "./.yarn/__virtual__/karma-chai-virtual-8476fab83b/0/cache/karma-chai-npm-0.1.0-d1d807f507-7fae0b4ace.zip/node_modules/karma-chai/", + "packageDependencies": [ + ["karma-chai", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:0.1.0"], + ["@types/chai", null], + ["@types/karma", null], + ["chai", "npm:4.3.4"], + ["karma", "npm:6.3.9"] + ], + "packagePeers": [ + "@types/chai", + "@types/karma", + "chai", + "karma" + ], + "linkType": "HARD", + }], + ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:0.1.0", { + "packageLocation": "./.yarn/__virtual__/karma-chai-virtual-e2e0d5ff8a/0/cache/karma-chai-npm-0.1.0-d1d807f507-7fae0b4ace.zip/node_modules/karma-chai/", + "packageDependencies": [ + ["karma-chai", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:0.1.0"], + ["@types/chai", "npm:4.2.22"], + ["@types/karma", null], + ["chai", "npm:4.3.4"], + ["karma", "npm:6.3.9"] + ], + "packagePeers": [ + "@types/chai", + "@types/karma", + "chai", + "karma" + ], + "linkType": "HARD", + }] + ]], + ["karma-chrome-launcher", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/karma-chrome-launcher-npm-3.1.0-999405afd7-63431ddec9.zip/node_modules/karma-chrome-launcher/", + "packageDependencies": [ + ["karma-chrome-launcher", "npm:3.1.0"], + ["which", "npm:1.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["karma-firefox-launcher", [ + ["npm:2.1.2", { + "packageLocation": "./.yarn/cache/karma-firefox-launcher-npm-2.1.2-63bf50abac-bfd5b35b35.zip/node_modules/karma-firefox-launcher/", + "packageDependencies": [ + ["karma-firefox-launcher", "npm:2.1.2"], + ["is-wsl", "npm:2.2.0"], + ["which", "npm:2.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["karma-mocha", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/karma-mocha-npm-2.0.1-b8979157d3-a09f475875.zip/node_modules/karma-mocha/", + "packageDependencies": [ + ["karma-mocha", "npm:2.0.1"], + ["minimist", "npm:1.2.5"], + ["mocha", "npm:9.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["karma-mocha-reporter", [ + ["npm:2.2.5", { + "packageLocation": "./.yarn/cache/karma-mocha-reporter-npm-2.2.5-4329166101-8b9e43c64b.zip/node_modules/karma-mocha-reporter/", + "packageDependencies": [ + ["karma-mocha-reporter", "npm:2.2.5"] + ], + "linkType": "SOFT", + }], + ["virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.2.5", { + "packageLocation": "./.yarn/__virtual__/karma-mocha-reporter-virtual-18298602e1/0/cache/karma-mocha-reporter-npm-2.2.5-4329166101-8b9e43c64b.zip/node_modules/karma-mocha-reporter/", + "packageDependencies": [ + ["karma-mocha-reporter", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:2.2.5"], + ["@types/karma", null], + ["chalk", "npm:2.4.2"], + ["karma", "npm:6.3.9"], + ["log-symbols", "npm:2.2.0"], + ["strip-ansi", "npm:4.0.0"] + ], + "packagePeers": [ + "@types/karma", + "karma" + ], + "linkType": "HARD", + }] + ]], + ["karma-sourcemap-loader", [ + ["npm:0.3.8", { + "packageLocation": "./.yarn/cache/karma-sourcemap-loader-npm-0.3.8-a7560c795e-12e21849af.zip/node_modules/karma-sourcemap-loader/", + "packageDependencies": [ + ["karma-sourcemap-loader", "npm:0.3.8"], + ["graceful-fs", "npm:4.2.10"] + ], + "linkType": "HARD", + }] + ]], + ["karma-webpack", [ + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/karma-webpack-npm-5.0.0-d7c66b2a8a-869b835f91.zip/node_modules/karma-webpack/", + "packageDependencies": [ + ["karma-webpack", "npm:5.0.0"] + ], + "linkType": "SOFT", + }], + ["virtual:01938c2be4835443e5a304e2b117c575220e96e8b7cedeb0f48d79264590b4c4babc6d1fea6367f522b1ca0149d795b42f2ab89c34a6ffe3c20f0a8cbb8b4453#npm:5.0.0", { + "packageLocation": "./.yarn/__virtual__/karma-webpack-virtual-df47bbf310/0/cache/karma-webpack-npm-5.0.0-d7c66b2a8a-869b835f91.zip/node_modules/karma-webpack/", + "packageDependencies": [ + ["karma-webpack", "virtual:01938c2be4835443e5a304e2b117c575220e96e8b7cedeb0f48d79264590b4c4babc6d1fea6367f522b1ca0149d795b42f2ab89c34a6ffe3c20f0a8cbb8b4453#npm:5.0.0"], + ["@types/webpack", null], + ["glob", "npm:7.2.0"], + ["minimatch", "npm:3.0.4"], + ["webpack", "virtual:01938c2be4835443e5a304e2b117c575220e96e8b7cedeb0f48d79264590b4c4babc6d1fea6367f522b1ca0149d795b42f2ab89c34a6ffe3c20f0a8cbb8b4453#npm:5.64.1"], + ["webpack-merge", "npm:4.2.2"] + ], + "packagePeers": [ + "@types/webpack", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:5.0.0", { + "packageLocation": "./.yarn/__virtual__/karma-webpack-virtual-7645831669/0/cache/karma-webpack-npm-5.0.0-d7c66b2a8a-869b835f91.zip/node_modules/karma-webpack/", + "packageDependencies": [ + ["karma-webpack", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:5.0.0"], + ["@types/webpack", null], + ["glob", "npm:7.2.0"], + ["minimatch", "npm:3.0.4"], + ["webpack", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:5.64.1"], + ["webpack-merge", "npm:4.2.2"] + ], + "packagePeers": [ + "@types/webpack", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:5.0.0", { + "packageLocation": "./.yarn/__virtual__/karma-webpack-virtual-f0f12d5c65/0/cache/karma-webpack-npm-5.0.0-d7c66b2a8a-869b835f91.zip/node_modules/karma-webpack/", + "packageDependencies": [ + ["karma-webpack", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:5.0.0"], + ["@types/webpack", null], + ["glob", "npm:7.2.0"], + ["minimatch", "npm:3.0.4"], + ["webpack", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:5.64.1"], + ["webpack-merge", "npm:4.2.2"] + ], + "packagePeers": [ + "@types/webpack", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:5.0.0", { + "packageLocation": "./.yarn/__virtual__/karma-webpack-virtual-bbb0fdb943/0/cache/karma-webpack-npm-5.0.0-d7c66b2a8a-869b835f91.zip/node_modules/karma-webpack/", + "packageDependencies": [ + ["karma-webpack", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:5.0.0"], + ["@types/webpack", null], + ["glob", "npm:7.2.0"], + ["minimatch", "npm:3.0.4"], + ["webpack", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:5.64.1"], + ["webpack-merge", "npm:4.2.2"] + ], + "packagePeers": [ + "@types/webpack", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.0.0", { + "packageLocation": "./.yarn/__virtual__/karma-webpack-virtual-94955efe11/0/cache/karma-webpack-npm-5.0.0-d7c66b2a8a-869b835f91.zip/node_modules/karma-webpack/", + "packageDependencies": [ + ["karma-webpack", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.0.0"], + ["@types/webpack", null], + ["glob", "npm:7.2.0"], + ["minimatch", "npm:3.0.4"], + ["webpack", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.64.1"], + ["webpack-merge", "npm:4.2.2"] + ], + "packagePeers": [ + "@types/webpack", + "webpack" + ], + "linkType": "HARD", + }] + ]], + ["keyv", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/keyv-npm-3.1.0-81c9ff4454-bb7e8f3acf.zip/node_modules/keyv/", + "packageDependencies": [ + ["keyv", "npm:3.1.0"], + ["json-buffer", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["kind-of", [ + ["npm:6.0.3", { + "packageLocation": "./.yarn/cache/kind-of-npm-6.0.3-ab15f36220-3ab01e7b1d.zip/node_modules/kind-of/", + "packageDependencies": [ + ["kind-of", "npm:6.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["kuler", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/kuler-npm-2.0.0-19e74c9695-9e10b5a165.zip/node_modules/kuler/", + "packageDependencies": [ + ["kuler", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["labeled-stream-splicer", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/labeled-stream-splicer-npm-2.0.2-ac01fae08b-4f7097b766.zip/node_modules/labeled-stream-splicer/", + "packageDependencies": [ + ["labeled-stream-splicer", "npm:2.0.2"], + ["inherits", "npm:2.0.4"], + ["stream-splicer", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["latest-version", [ + ["npm:5.1.0", { + "packageLocation": "./.yarn/cache/latest-version-npm-5.1.0-ddb9b0eb39-fbc72b071e.zip/node_modules/latest-version/", + "packageDependencies": [ + ["latest-version", "npm:5.1.0"], + ["package-json", "npm:6.5.0"] + ], + "linkType": "HARD", + }] + ]], + ["level-concat-iterator", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/level-concat-iterator-npm-2.0.1-5179af5bd2-562583ef12.zip/node_modules/level-concat-iterator/", + "packageDependencies": [ + ["level-concat-iterator", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["level-errors", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/level-errors-npm-2.0.1-981e46a3dc-aca5d7670e.zip/node_modules/level-errors/", + "packageDependencies": [ + ["level-errors", "npm:2.0.1"], + ["errno", "npm:0.1.8"] + ], + "linkType": "HARD", + }] + ]], + ["level-iterator-stream", [ + ["npm:4.0.2", { + "packageLocation": "./.yarn/cache/level-iterator-stream-npm-4.0.2-27e0549122-239e2c7e62.zip/node_modules/level-iterator-stream/", + "packageDependencies": [ + ["level-iterator-stream", "npm:4.0.2"], + ["inherits", "npm:2.0.4"], + ["readable-stream", "npm:3.6.0"], + ["xtend", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["level-supports", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/level-supports-npm-1.0.1-e9d5ae27f4-5d6bdb88cf.zip/node_modules/level-supports/", + "packageDependencies": [ + ["level-supports", "npm:1.0.1"], + ["xtend", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["levelup", [ + ["npm:4.4.0", { + "packageLocation": "./.yarn/cache/levelup-npm-4.4.0-3053c0e5bc-5a09e34c78.zip/node_modules/levelup/", + "packageDependencies": [ + ["levelup", "npm:4.4.0"], + ["deferred-leveldown", "npm:5.3.0"], + ["level-errors", "npm:2.0.1"], + ["level-iterator-stream", "npm:4.0.2"], + ["level-supports", "npm:1.0.1"], + ["xtend", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["leven", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/leven-npm-2.1.0-19f0a16606-f7b4a01b15.zip/node_modules/leven/", + "packageDependencies": [ + ["leven", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["levn", [ + ["npm:0.3.0", { + "packageLocation": "./.yarn/cache/levn-npm-0.3.0-48d774b1c2-0d084a5242.zip/node_modules/levn/", + "packageDependencies": [ + ["levn", "npm:0.3.0"], + ["prelude-ls", "npm:1.1.2"], + ["type-check", "npm:0.3.2"] + ], + "linkType": "HARD", + }], + ["npm:0.4.1", { + "packageLocation": "./.yarn/cache/levn-npm-0.4.1-d183b2d7bb-12c5021c85.zip/node_modules/levn/", + "packageDependencies": [ + ["levn", "npm:0.4.1"], + ["prelude-ls", "npm:1.2.1"], + ["type-check", "npm:0.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["lie", [ + ["npm:3.1.1", { + "packageLocation": "./.yarn/cache/lie-npm-3.1.1-91350720d9-6da9f2121d.zip/node_modules/lie/", + "packageDependencies": [ + ["lie", "npm:3.1.1"], + ["immediate", "npm:3.0.6"] + ], + "linkType": "HARD", + }] + ]], + ["lines-and-columns", [ + ["npm:1.1.6", { + "packageLocation": "./.yarn/cache/lines-and-columns-npm-1.1.6-23e74fab67-198a5436b1.zip/node_modules/lines-and-columns/", + "packageDependencies": [ + ["lines-and-columns", "npm:1.1.6"] + ], + "linkType": "HARD", + }] + ]], + ["listr2", [ + ["npm:3.5.0", { + "packageLocation": "./.yarn/cache/listr2-npm-3.5.0-6aad1da502-cf30837462.zip/node_modules/listr2/", + "packageDependencies": [ + ["listr2", "npm:3.5.0"] + ], + "linkType": "SOFT", + }], + ["virtual:880cda903c2a2be387819a3f857d21494004437a03c92969b9853f7bdeebdfed08d417e68364ee9e158338603a6d78d690c457a55ab11e56398bc10f0ad232fc#npm:3.5.0", { + "packageLocation": "./.yarn/__virtual__/listr2-virtual-30886726f2/0/cache/listr2-npm-3.5.0-6aad1da502-cf30837462.zip/node_modules/listr2/", + "packageDependencies": [ + ["listr2", "virtual:880cda903c2a2be387819a3f857d21494004437a03c92969b9853f7bdeebdfed08d417e68364ee9e158338603a6d78d690c457a55ab11e56398bc10f0ad232fc#npm:3.5.0"], + ["@types/enquirer", null], + ["chalk", "npm:4.1.2"], + ["cli-truncate", "npm:2.1.0"], + ["enquirer", "npm:2.3.6"], + ["figures", "npm:3.2.0"], + ["indent-string", "npm:4.0.0"], + ["log-update", "npm:4.0.0"], + ["p-map", "npm:4.0.0"], + ["rxjs", "npm:6.6.7"], + ["through", "npm:2.3.8"], + ["wrap-ansi", "npm:7.0.0"] + ], + "packagePeers": [ + "@types/enquirer", + "enquirer" + ], + "linkType": "HARD", + }] + ]], + ["load-json-file", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/load-json-file-npm-4.0.0-c9f09d85eb-8f5d6d93ba.zip/node_modules/load-json-file/", + "packageDependencies": [ + ["load-json-file", "npm:4.0.0"], + ["graceful-fs", "npm:4.2.10"], + ["parse-json", "npm:4.0.0"], + ["pify", "npm:3.0.0"], + ["strip-bom", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:6.2.0", { + "packageLocation": "./.yarn/cache/load-json-file-npm-6.2.0-516f143724-4429e430eb.zip/node_modules/load-json-file/", + "packageDependencies": [ + ["load-json-file", "npm:6.2.0"], + ["graceful-fs", "npm:4.2.10"], + ["parse-json", "npm:5.2.0"], + ["strip-bom", "npm:4.0.0"], + ["type-fest", "npm:0.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["load-yaml-file", [ + ["npm:0.2.0", { + "packageLocation": "./.yarn/cache/load-yaml-file-npm-0.2.0-0369385ceb-d86d7ec7b1.zip/node_modules/load-yaml-file/", + "packageDependencies": [ + ["load-yaml-file", "npm:0.2.0"], + ["graceful-fs", "npm:4.2.10"], + ["js-yaml", "npm:3.14.1"], + ["pify", "npm:4.0.1"], + ["strip-bom", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["loader-runner", [ + ["npm:4.2.0", { + "packageLocation": "./.yarn/cache/loader-runner-npm-4.2.0-427f0e7134-e61aea8b69.zip/node_modules/loader-runner/", + "packageDependencies": [ + ["loader-runner", "npm:4.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["loader-utils", [ + ["npm:1.4.0", { + "packageLocation": "./.yarn/cache/loader-utils-npm-1.4.0-a56254a277-d150b15e7a.zip/node_modules/loader-utils/", + "packageDependencies": [ + ["loader-utils", "npm:1.4.0"], + ["big.js", "npm:5.2.2"], + ["emojis-list", "npm:3.0.0"], + ["json5", "npm:1.0.1"] + ], + "linkType": "HARD", + }], + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/loader-utils-npm-2.0.2-c693411911-9078d1ed47.zip/node_modules/loader-utils/", + "packageDependencies": [ + ["loader-utils", "npm:2.0.2"], + ["big.js", "npm:5.2.2"], + ["emojis-list", "npm:3.0.0"], + ["json5", "npm:2.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["localforage", [ + ["npm:1.10.0", { + "packageLocation": "./.yarn/cache/localforage-npm-1.10.0-cf9ea9a436-f2978b434d.zip/node_modules/localforage/", + "packageDependencies": [ + ["localforage", "npm:1.10.0"], + ["lie", "npm:3.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["locate-path", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/locate-path-npm-2.0.0-673d28b0ea-02d581edbb.zip/node_modules/locate-path/", + "packageDependencies": [ + ["locate-path", "npm:2.0.0"], + ["p-locate", "npm:2.0.0"], + ["path-exists", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/locate-path-npm-5.0.0-46580c43e4-83e51725e6.zip/node_modules/locate-path/", + "packageDependencies": [ + ["locate-path", "npm:5.0.0"], + ["p-locate", "npm:4.1.0"] + ], + "linkType": "HARD", + }], + ["npm:6.0.0", { + "packageLocation": "./.yarn/cache/locate-path-npm-6.0.0-06a1e4c528-72eb661788.zip/node_modules/locate-path/", + "packageDependencies": [ + ["locate-path", "npm:6.0.0"], + ["p-locate", "npm:5.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["lodash", [ + ["npm:4.17.21", { + "packageLocation": "./.yarn/cache/lodash-npm-4.17.21-6382451519-eb835a2e51.zip/node_modules/lodash/", + "packageDependencies": [ + ["lodash", "npm:4.17.21"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.camelcase", [ + ["npm:4.3.0", { + "packageLocation": "./.yarn/cache/lodash.camelcase-npm-4.3.0-bf268e3bf0-cb9227612f.zip/node_modules/lodash.camelcase/", + "packageDependencies": [ + ["lodash.camelcase", "npm:4.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.clone", [ + ["npm:4.5.0", { + "packageLocation": "./.yarn/cache/lodash.clone-npm-4.5.0-d9f712430b-5839f22acf.zip/node_modules/lodash.clone/", + "packageDependencies": [ + ["lodash.clone", "npm:4.5.0"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.clonedeep", [ + ["npm:4.5.0", { + "packageLocation": "./.yarn/cache/lodash.clonedeep-npm-4.5.0-fbc3cda4e5-92c46f094b.zip/node_modules/lodash.clonedeep/", + "packageDependencies": [ + ["lodash.clonedeep", "npm:4.5.0"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.clonedeepwith", [ + ["npm:4.5.0", { + "packageLocation": "./.yarn/cache/lodash.clonedeepwith-npm-4.5.0-67373e487a-9fbf4ebfa0.zip/node_modules/lodash.clonedeepwith/", + "packageDependencies": [ + ["lodash.clonedeepwith", "npm:4.5.0"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.debounce", [ + ["npm:4.0.8", { + "packageLocation": "./.yarn/cache/lodash.debounce-npm-4.0.8-f1d6e09799-a3f527d22c.zip/node_modules/lodash.debounce/", + "packageDependencies": [ + ["lodash.debounce", "npm:4.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.find", [ + ["npm:4.6.0", { + "packageLocation": "./.yarn/cache/lodash.find-npm-4.6.0-dd2db8c53f-b737f849a4.zip/node_modules/lodash.find/", + "packageDependencies": [ + ["lodash.find", "npm:4.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.flattendeep", [ + ["npm:4.4.0", { + "packageLocation": "./.yarn/cache/lodash.flattendeep-npm-4.4.0-26b2b4cbd7-8521c919ac.zip/node_modules/lodash.flattendeep/", + "packageDependencies": [ + ["lodash.flattendeep", "npm:4.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.get", [ + ["npm:4.4.2", { + "packageLocation": "./.yarn/cache/lodash.get-npm-4.4.2-7bda64ed87-e403047ddb.zip/node_modules/lodash.get/", + "packageDependencies": [ + ["lodash.get", "npm:4.4.2"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.isequal", [ + ["npm:4.5.0", { + "packageLocation": "./.yarn/cache/lodash.isequal-npm-4.5.0-f8b0f64d63-da27515dc5.zip/node_modules/lodash.isequal/", + "packageDependencies": [ + ["lodash.isequal", "npm:4.5.0"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.ismatch", [ + ["npm:4.4.0", { + "packageLocation": "./.yarn/cache/lodash.ismatch-npm-4.4.0-e538fd6c3d-a393917578.zip/node_modules/lodash.ismatch/", + "packageDependencies": [ + ["lodash.ismatch", "npm:4.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.matches", [ + ["npm:4.6.0", { + "packageLocation": "./.yarn/cache/lodash.matches-npm-4.6.0-4ac5f4f696-002617abb6.zip/node_modules/lodash.matches/", + "packageDependencies": [ + ["lodash.matches", "npm:4.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.memoize", [ + ["npm:3.0.4", { + "packageLocation": "./.yarn/cache/lodash.memoize-npm-3.0.4-40c36c3de4-fc52e0916b.zip/node_modules/lodash.memoize/", + "packageDependencies": [ + ["lodash.memoize", "npm:3.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.merge", [ + ["npm:4.6.2", { + "packageLocation": "./.yarn/cache/lodash.merge-npm-4.6.2-77cb4416bf-ad580b4bdb.zip/node_modules/lodash.merge/", + "packageDependencies": [ + ["lodash.merge", "npm:4.6.2"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.sample", [ + ["npm:4.2.1", { + "packageLocation": "./.yarn/cache/lodash.sample-npm-4.2.1-ec9e9fdf4d-8d93c1db13.zip/node_modules/lodash.sample/", + "packageDependencies": [ + ["lodash.sample", "npm:4.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.set", [ + ["npm:4.3.2", { + "packageLocation": "./.yarn/cache/lodash.set-npm-4.3.2-7586c942c2-a9122f49ee.zip/node_modules/lodash.set/", + "packageDependencies": [ + ["lodash.set", "npm:4.3.2"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.truncate", [ + ["npm:4.4.2", { + "packageLocation": "./.yarn/cache/lodash.truncate-npm-4.4.2-bc50fe1663-b463d8a382.zip/node_modules/lodash.truncate/", + "packageDependencies": [ + ["lodash.truncate", "npm:4.4.2"] + ], + "linkType": "HARD", + }] + ]], + ["log-symbols", [ + ["npm:2.2.0", { + "packageLocation": "./.yarn/cache/log-symbols-npm-2.2.0-9541ad4da6-4c95e3b65f.zip/node_modules/log-symbols/", + "packageDependencies": [ + ["log-symbols", "npm:2.2.0"], + ["chalk", "npm:2.4.2"] + ], + "linkType": "HARD", + }], + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/log-symbols-npm-4.1.0-0a13492d8b-fce1497b31.zip/node_modules/log-symbols/", + "packageDependencies": [ + ["log-symbols", "npm:4.1.0"], + ["chalk", "npm:4.1.2"], + ["is-unicode-supported", "npm:0.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["log-update", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/log-update-npm-4.0.0-9d0554261c-ae2f85bbab.zip/node_modules/log-update/", + "packageDependencies": [ + ["log-update", "npm:4.0.0"], + ["ansi-escapes", "npm:4.3.2"], + ["cli-cursor", "npm:3.1.0"], + ["slice-ansi", "npm:4.0.0"], + ["wrap-ansi", "npm:6.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["log4js", [ + ["npm:6.3.0", { + "packageLocation": "./.yarn/cache/log4js-npm-6.3.0-7d1ea76e6b-da2812bbe4.zip/node_modules/log4js/", + "packageDependencies": [ + ["log4js", "npm:6.3.0"], + ["date-format", "npm:3.0.0"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["flatted", "npm:2.0.2"], + ["rfdc", "npm:1.3.0"], + ["streamroller", "npm:2.2.4"] + ], + "linkType": "HARD", + }] + ]], + ["logform", [ + ["npm:2.3.0", { + "packageLocation": "./.yarn/cache/logform-npm-2.3.0-13155f7f21-a82d36823d.zip/node_modules/logform/", + "packageDependencies": [ + ["logform", "npm:2.3.0"], + ["colors", "npm:1.4.0"], + ["fecha", "npm:4.2.1"], + ["ms", "npm:2.1.3"], + ["safe-stable-stringify", "npm:1.1.1"], + ["triple-beam", "npm:1.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["long", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/long-npm-4.0.0-ecd96a31ed-16afbe8f74.zip/node_modules/long/", + "packageDependencies": [ + ["long", "npm:4.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.2.0", { + "packageLocation": "./.yarn/cache/long-npm-5.2.0-bbbd23a9e6-37aa4e67b9.zip/node_modules/long/", + "packageDependencies": [ + ["long", "npm:5.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["lower-case", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/lower-case-npm-2.0.2-151055f1c2-83a0a5f159.zip/node_modules/lower-case/", + "packageDependencies": [ + ["lower-case", "npm:2.0.2"], + ["tslib", "npm:2.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["lowercase-keys", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/lowercase-keys-npm-1.0.1-0979e653b8-4d04502659.zip/node_modules/lowercase-keys/", + "packageDependencies": [ + ["lowercase-keys", "npm:1.0.1"] + ], + "linkType": "HARD", + }], + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/lowercase-keys-npm-2.0.0-1876065a32-24d7ebd56c.zip/node_modules/lowercase-keys/", + "packageDependencies": [ + ["lowercase-keys", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["lru-cache", [ + ["npm:5.1.1", { + "packageLocation": "./.yarn/cache/lru-cache-npm-5.1.1-f475882a51-c154ae1cbb.zip/node_modules/lru-cache/", + "packageDependencies": [ + ["lru-cache", "npm:5.1.1"], + ["yallist", "npm:3.1.1"] + ], + "linkType": "HARD", + }], + ["npm:6.0.0", { + "packageLocation": "./.yarn/cache/lru-cache-npm-6.0.0-b4c8668fe1-f97f499f89.zip/node_modules/lru-cache/", + "packageDependencies": [ + ["lru-cache", "npm:6.0.0"], + ["yallist", "npm:4.0.0"] + ], + "linkType": "HARD", + }], + ["npm:7.3.1", { + "packageLocation": "./.yarn/cache/lru-cache-npm-7.3.1-b157dca680-34bb50c015.zip/node_modules/lru-cache/", + "packageDependencies": [ + ["lru-cache", "npm:7.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["ltgt", [ + ["npm:2.2.1", { + "packageLocation": "./.yarn/cache/ltgt-npm-2.2.1-443b5da86d-7e3874296f.zip/node_modules/ltgt/", + "packageDependencies": [ + ["ltgt", "npm:2.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["make-dir", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/make-dir-npm-3.1.0-d1d7505142-484200020a.zip/node_modules/make-dir/", + "packageDependencies": [ + ["make-dir", "npm:3.1.0"], + ["semver", "npm:6.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["make-error", [ + ["npm:1.3.6", { + "packageLocation": "./.yarn/cache/make-error-npm-1.3.6-ccb85d9458-b86e5e0e25.zip/node_modules/make-error/", + "packageDependencies": [ + ["make-error", "npm:1.3.6"] + ], + "linkType": "HARD", + }] + ]], + ["make-fetch-happen", [ + ["npm:10.0.3", { + "packageLocation": "./.yarn/cache/make-fetch-happen-npm-10.0.3-e552879254-edf3ba5119.zip/node_modules/make-fetch-happen/", + "packageDependencies": [ + ["make-fetch-happen", "npm:10.0.3"], + ["agentkeepalive", "npm:4.2.0"], + ["cacache", "npm:15.3.0"], + ["http-cache-semantics", "npm:4.1.0"], + ["http-proxy-agent", "npm:5.0.0"], + ["https-proxy-agent", "npm:5.0.0"], + ["is-lambda", "npm:1.0.1"], + ["lru-cache", "npm:7.3.1"], + ["minipass", "npm:3.1.6"], + ["minipass-collect", "npm:1.0.2"], + ["minipass-fetch", "npm:1.4.1"], + ["minipass-flush", "npm:1.0.5"], + ["minipass-pipeline", "npm:1.2.4"], + ["negotiator", "npm:0.6.3"], + ["promise-retry", "npm:2.0.1"], + ["socks-proxy-agent", "npm:6.1.1"], + ["ssri", "npm:8.0.1"] + ], + "linkType": "HARD", + }], + ["npm:9.1.0", { + "packageLocation": "./.yarn/cache/make-fetch-happen-npm-9.1.0-23184ad7f6-0eb371c85f.zip/node_modules/make-fetch-happen/", + "packageDependencies": [ + ["make-fetch-happen", "npm:9.1.0"], + ["agentkeepalive", "npm:4.2.0"], + ["cacache", "npm:15.3.0"], + ["http-cache-semantics", "npm:4.1.0"], + ["http-proxy-agent", "npm:4.0.1"], + ["https-proxy-agent", "npm:5.0.0"], + ["is-lambda", "npm:1.0.1"], + ["lru-cache", "npm:6.0.0"], + ["minipass", "npm:3.1.6"], + ["minipass-collect", "npm:1.0.2"], + ["minipass-fetch", "npm:1.4.1"], + ["minipass-flush", "npm:1.0.5"], + ["minipass-pipeline", "npm:1.2.4"], + ["negotiator", "npm:0.6.3"], + ["promise-retry", "npm:2.0.1"], + ["socks-proxy-agent", "npm:6.1.1"], + ["ssri", "npm:8.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["map-obj", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/map-obj-npm-1.0.1-fa55100fac-9949e7baec.zip/node_modules/map-obj/", + "packageDependencies": [ + ["map-obj", "npm:1.0.1"] + ], + "linkType": "HARD", + }], + ["npm:4.3.0", { + "packageLocation": "./.yarn/cache/map-obj-npm-4.3.0-d53e32935d-fbc554934d.zip/node_modules/map-obj/", + "packageDependencies": [ + ["map-obj", "npm:4.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["mathjs", [ + ["npm:10.4.3", { + "packageLocation": "./.yarn/cache/mathjs-npm-10.4.3-9f80458c54-ed2343b2ab.zip/node_modules/mathjs/", + "packageDependencies": [ + ["mathjs", "npm:10.4.3"], + ["@babel/runtime", "npm:7.17.9"], + ["complex.js", "npm:2.1.0"], + ["decimal.js", "npm:10.3.1"], + ["escape-latex", "npm:1.2.0"], + ["fraction.js", "npm:4.2.0"], + ["javascript-natural-sort", "npm:0.7.1"], + ["seedrandom", "npm:3.0.5"], + ["tiny-emitter", "npm:2.1.0"], + ["typed-function", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["md5.js", [ + ["npm:1.3.5", { + "packageLocation": "./.yarn/cache/md5.js-npm-1.3.5-130901125a-098494d885.zip/node_modules/md5.js/", + "packageDependencies": [ + ["md5.js", "npm:1.3.5"], + ["hash-base", "npm:3.1.0"], + ["inherits", "npm:2.0.4"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["media-typer", [ + ["npm:0.3.0", { + "packageLocation": "./.yarn/cache/media-typer-npm-0.3.0-8674f8f0f5-af1b38516c.zip/node_modules/media-typer/", + "packageDependencies": [ + ["media-typer", "npm:0.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["mem-fs", [ + ["npm:2.2.1", { + "packageLocation": "./.yarn/cache/mem-fs-npm-2.2.1-5a394345d4-e44fb4acf8.zip/node_modules/mem-fs/", + "packageDependencies": [ + ["mem-fs", "npm:2.2.1"], + ["@types/node", "npm:15.14.9"], + ["@types/vinyl", "npm:2.0.6"], + ["vinyl", "npm:2.2.1"], + ["vinyl-file", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["mem-fs-editor", [ + ["npm:9.4.0", { + "packageLocation": "./.yarn/cache/mem-fs-editor-npm-9.4.0-97c608fb01-427b71d59a.zip/node_modules/mem-fs-editor/", + "packageDependencies": [ + ["mem-fs-editor", "npm:9.4.0"] + ], + "linkType": "SOFT", + }], + ["virtual:afa2bac4a722b39c23915b10124591b74c4663d71c6a3f7b65097011d176d90774bf555115519397a9a70486856c408af659df08946fcfcdbe1a8b32c472e211#npm:9.4.0", { + "packageLocation": "./.yarn/__virtual__/mem-fs-editor-virtual-08b5184a99/0/cache/mem-fs-editor-npm-9.4.0-97c608fb01-427b71d59a.zip/node_modules/mem-fs-editor/", + "packageDependencies": [ + ["mem-fs-editor", "virtual:afa2bac4a722b39c23915b10124591b74c4663d71c6a3f7b65097011d176d90774bf555115519397a9a70486856c408af659df08946fcfcdbe1a8b32c472e211#npm:9.4.0"], + ["@types/mem-fs", null], + ["binaryextensions", "npm:4.18.0"], + ["commondir", "npm:1.0.1"], + ["deep-extend", "npm:0.6.0"], + ["ejs", "npm:3.1.6"], + ["globby", "npm:11.1.0"], + ["isbinaryfile", "npm:4.0.8"], + ["mem-fs", "npm:2.2.1"], + ["minimatch", "npm:3.0.4"], + ["multimatch", "npm:5.0.0"], + ["normalize-path", "npm:3.0.0"], + ["textextensions", "npm:5.14.0"] + ], + "packagePeers": [ + "@types/mem-fs", + "mem-fs" + ], + "linkType": "HARD", + }] + ]], + ["memdown", [ + ["npm:5.1.0", { + "packageLocation": "./.yarn/cache/memdown-npm-5.1.0-e769608fe2-23e4414034.zip/node_modules/memdown/", + "packageDependencies": [ + ["memdown", "npm:5.1.0"], + ["abstract-leveldown", "npm:6.2.3"], + ["functional-red-black-tree", "npm:1.0.1"], + ["immediate", "npm:3.2.3"], + ["inherits", "npm:2.0.4"], + ["ltgt", "npm:2.2.1"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["memory-fs", [ + ["npm:0.5.0", { + "packageLocation": "./.yarn/cache/memory-fs-npm-0.5.0-8be5938449-a9f25b0a8e.zip/node_modules/memory-fs/", + "packageDependencies": [ + ["memory-fs", "npm:0.5.0"], + ["errno", "npm:0.1.8"], + ["readable-stream", "npm:2.3.7"] + ], + "linkType": "HARD", + }] + ]], + ["memory-pager", [ + ["npm:1.5.0", { + "packageLocation": "./.yarn/cache/memory-pager-npm-1.5.0-46e20e6c81-d1a2e68458.zip/node_modules/memory-pager/", + "packageDependencies": [ + ["memory-pager", "npm:1.5.0"] + ], + "linkType": "HARD", + }] + ]], + ["memory-streams", [ + ["npm:0.1.3", { + "packageLocation": "./.yarn/cache/memory-streams-npm-0.1.3-8b67a57781-aebb6dc54c.zip/node_modules/memory-streams/", + "packageDependencies": [ + ["memory-streams", "npm:0.1.3"], + ["readable-stream", "npm:1.0.34"] + ], + "linkType": "HARD", + }] + ]], + ["meow", [ + ["npm:8.1.2", { + "packageLocation": "./.yarn/cache/meow-npm-8.1.2-bcfe48d4f3-bc23bf1b44.zip/node_modules/meow/", + "packageDependencies": [ + ["meow", "npm:8.1.2"], + ["@types/minimist", "npm:1.2.2"], + ["camelcase-keys", "npm:6.2.2"], + ["decamelize-keys", "npm:1.1.0"], + ["hard-rejection", "npm:2.1.0"], + ["minimist-options", "npm:4.1.0"], + ["normalize-package-data", "npm:3.0.3"], + ["read-pkg-up", "npm:7.0.1"], + ["redent", "npm:3.0.0"], + ["trim-newlines", "npm:3.0.1"], + ["type-fest", "npm:0.18.1"], + ["yargs-parser", "npm:20.2.9"] + ], + "linkType": "HARD", + }] + ]], + ["merge-stream", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/merge-stream-npm-2.0.0-2ac83efea5-6fa4dcc8d8.zip/node_modules/merge-stream/", + "packageDependencies": [ + ["merge-stream", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["merge2", [ + ["npm:1.4.1", { + "packageLocation": "./.yarn/cache/merge2-npm-1.4.1-a2507bd06c-7268db63ed.zip/node_modules/merge2/", + "packageDependencies": [ + ["merge2", "npm:1.4.1"] + ], + "linkType": "HARD", + }] + ]], + ["micro-memoize", [ + ["npm:4.0.9", { + "packageLocation": "./.yarn/cache/micro-memoize-npm-4.0.9-ebbd2df842-c755539864.zip/node_modules/micro-memoize/", + "packageDependencies": [ + ["micro-memoize", "npm:4.0.9"] + ], + "linkType": "HARD", + }] + ]], + ["micromatch", [ + ["npm:4.0.4", { + "packageLocation": "./.yarn/cache/micromatch-npm-4.0.4-9fdcbb7a0e-ef3d1c88e7.zip/node_modules/micromatch/", + "packageDependencies": [ + ["micromatch", "npm:4.0.4"], + ["braces", "npm:3.0.2"], + ["picomatch", "npm:2.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["miller-rabin", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/miller-rabin-npm-4.0.1-3426ac0bf7-00cd1ab838.zip/node_modules/miller-rabin/", + "packageDependencies": [ + ["miller-rabin", "npm:4.0.1"], + ["bn.js", "npm:4.12.0"], + ["brorand", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["mime", [ + ["npm:2.6.0", { + "packageLocation": "./.yarn/cache/mime-npm-2.6.0-88b89d8de0-1497ba7b9f.zip/node_modules/mime/", + "packageDependencies": [ + ["mime", "npm:2.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["mime-db", [ + ["npm:1.51.0", { + "packageLocation": "./.yarn/cache/mime-db-npm-1.51.0-d5e42b45ad-613b1ac9d6.zip/node_modules/mime-db/", + "packageDependencies": [ + ["mime-db", "npm:1.51.0"] + ], + "linkType": "HARD", + }] + ]], + ["mime-types", [ + ["npm:2.1.34", { + "packageLocation": "./.yarn/cache/mime-types-npm-2.1.34-3cd0bb907c-67013de9e9.zip/node_modules/mime-types/", + "packageDependencies": [ + ["mime-types", "npm:2.1.34"], + ["mime-db", "npm:1.51.0"] + ], + "linkType": "HARD", + }] + ]], + ["mimic-fn", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/mimic-fn-npm-2.1.0-4fbeb3abb4-d2421a3444.zip/node_modules/mimic-fn/", + "packageDependencies": [ + ["mimic-fn", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["mimic-response", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/mimic-response-npm-1.0.1-f6f85dde84-034c78753b.zip/node_modules/mimic-response/", + "packageDependencies": [ + ["mimic-response", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["min-indent", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/min-indent-npm-1.0.1-77031f50e1-bfc6dd03c5.zip/node_modules/min-indent/", + "packageDependencies": [ + ["min-indent", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["minimalistic-assert", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/minimalistic-assert-npm-1.0.1-dc8bb23d29-cc7974a926.zip/node_modules/minimalistic-assert/", + "packageDependencies": [ + ["minimalistic-assert", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["minimalistic-crypto-utils", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/minimalistic-crypto-utils-npm-1.0.1-e66b10822e-6e8a0422b3.zip/node_modules/minimalistic-crypto-utils/", + "packageDependencies": [ + ["minimalistic-crypto-utils", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["minimatch", [ + ["npm:3.0.4", { + "packageLocation": "./.yarn/cache/minimatch-npm-3.0.4-6e76f51c23-66ac295f8a.zip/node_modules/minimatch/", + "packageDependencies": [ + ["minimatch", "npm:3.0.4"], + ["brace-expansion", "npm:1.1.11"] + ], + "linkType": "HARD", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/minimatch-npm-5.0.0-969101c1d1-810d4165fa.zip/node_modules/minimatch/", + "packageDependencies": [ + ["minimatch", "npm:5.0.0"], + ["brace-expansion", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["minimist", [ + ["npm:1.2.5", { + "packageLocation": "./.yarn/cache/minimist-npm-1.2.5-ced0e1f617-86706ce5b3.zip/node_modules/minimist/", + "packageDependencies": [ + ["minimist", "npm:1.2.5"] + ], + "linkType": "HARD", + }] + ]], + ["minimist-options", [ + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/minimist-options-npm-4.1.0-64ca250fc1-8c040b3068.zip/node_modules/minimist-options/", + "packageDependencies": [ + ["minimist-options", "npm:4.1.0"], + ["arrify", "npm:1.0.1"], + ["is-plain-obj", "npm:1.1.0"], + ["kind-of", "npm:6.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["minipass", [ + ["npm:3.1.6", { + "packageLocation": "./.yarn/cache/minipass-npm-3.1.6-f032df1661-57a0404141.zip/node_modules/minipass/", + "packageDependencies": [ + ["minipass", "npm:3.1.6"], + ["yallist", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["minipass-collect", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/minipass-collect-npm-1.0.2-3b4676eab5-14df761028.zip/node_modules/minipass-collect/", + "packageDependencies": [ + ["minipass-collect", "npm:1.0.2"], + ["minipass", "npm:3.1.6"] + ], + "linkType": "HARD", + }] + ]], + ["minipass-fetch", [ + ["npm:1.4.1", { + "packageLocation": "./.yarn/cache/minipass-fetch-npm-1.4.1-2d67357feb-ec93697bdb.zip/node_modules/minipass-fetch/", + "packageDependencies": [ + ["minipass-fetch", "npm:1.4.1"], + ["encoding", "npm:0.1.13"], + ["minipass", "npm:3.1.6"], + ["minipass-sized", "npm:1.0.3"], + ["minizlib", "npm:2.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["minipass-flush", [ + ["npm:1.0.5", { + "packageLocation": "./.yarn/cache/minipass-flush-npm-1.0.5-efe79d9826-56269a0b22.zip/node_modules/minipass-flush/", + "packageDependencies": [ + ["minipass-flush", "npm:1.0.5"], + ["minipass", "npm:3.1.6"] + ], + "linkType": "HARD", + }] + ]], + ["minipass-json-stream", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/minipass-json-stream-npm-1.0.1-96490706d6-791b696a27.zip/node_modules/minipass-json-stream/", + "packageDependencies": [ + ["minipass-json-stream", "npm:1.0.1"], + ["jsonparse", "npm:1.3.1"], + ["minipass", "npm:3.1.6"] + ], + "linkType": "HARD", + }] + ]], + ["minipass-pipeline", [ + ["npm:1.2.4", { + "packageLocation": "./.yarn/cache/minipass-pipeline-npm-1.2.4-5924cb077f-b14240dac0.zip/node_modules/minipass-pipeline/", + "packageDependencies": [ + ["minipass-pipeline", "npm:1.2.4"], + ["minipass", "npm:3.1.6"] + ], + "linkType": "HARD", + }] + ]], + ["minipass-sized", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/minipass-sized-npm-1.0.3-306d86f432-79076749fc.zip/node_modules/minipass-sized/", + "packageDependencies": [ + ["minipass-sized", "npm:1.0.3"], + ["minipass", "npm:3.1.6"] + ], + "linkType": "HARD", + }] + ]], + ["minizlib", [ + ["npm:2.1.2", { + "packageLocation": "./.yarn/cache/minizlib-npm-2.1.2-ea89cd0cfb-f1fdeac0b0.zip/node_modules/minizlib/", + "packageDependencies": [ + ["minizlib", "npm:2.1.2"], + ["minipass", "npm:3.1.6"], + ["yallist", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["mkdirp", [ + ["npm:0.5.5", { + "packageLocation": "./.yarn/cache/mkdirp-npm-0.5.5-6bc76534fc-3bce20ea52.zip/node_modules/mkdirp/", + "packageDependencies": [ + ["mkdirp", "npm:0.5.5"], + ["minimist", "npm:1.2.5"] + ], + "linkType": "HARD", + }], + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/mkdirp-npm-1.0.4-37f6ef56b9-a96865108c.zip/node_modules/mkdirp/", + "packageDependencies": [ + ["mkdirp", "npm:1.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["mkdirp-classic", [ + ["npm:0.5.3", { + "packageLocation": "./.yarn/cache/mkdirp-classic-npm-0.5.3-3b5c991910-3f4e088208.zip/node_modules/mkdirp-classic/", + "packageDependencies": [ + ["mkdirp-classic", "npm:0.5.3"] + ], + "linkType": "HARD", + }] + ]], + ["mkdirp-infer-owner", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/mkdirp-infer-owner-npm-2.0.0-de1fb05d31-d8f4ecd32f.zip/node_modules/mkdirp-infer-owner/", + "packageDependencies": [ + ["mkdirp-infer-owner", "npm:2.0.0"], + ["chownr", "npm:2.0.0"], + ["infer-owner", "npm:1.0.4"], + ["mkdirp", "npm:1.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["mocha", [ + ["npm:9.1.3", { + "packageLocation": "./.yarn/cache/mocha-npm-9.1.3-cf8df742ce-4185038f1d.zip/node_modules/mocha/", + "packageDependencies": [ + ["mocha", "npm:9.1.3"], + ["@ungap/promise-all-settled", "npm:1.1.2"], + ["ansi-colors", "npm:4.1.1"], + ["browser-stdout", "npm:1.3.1"], + ["chokidar", "npm:3.5.2"], + ["debug", "virtual:cf8df742ce8e4e935902993bcfceab61a23301352e0174959d2524c3ce25388a4d3477170dec0ebaf85f7f409c4c58568061d13cf886536628fdbe79510fc4de#npm:4.3.2"], + ["diff", "npm:5.0.0"], + ["escape-string-regexp", "npm:4.0.0"], + ["find-up", "npm:5.0.0"], + ["glob", "npm:7.1.7"], + ["growl", "npm:1.10.5"], + ["he", "npm:1.2.0"], + ["js-yaml", "npm:4.1.0"], + ["log-symbols", "npm:4.1.0"], + ["minimatch", "npm:3.0.4"], + ["ms", "npm:2.1.3"], + ["nanoid", "npm:3.1.25"], + ["serialize-javascript", "npm:6.0.0"], + ["strip-json-comments", "npm:3.1.1"], + ["supports-color", "npm:8.1.1"], + ["which", "npm:2.0.2"], + ["workerpool", "npm:6.1.5"], + ["yargs", "npm:16.2.0"], + ["yargs-parser", "npm:20.2.4"], + ["yargs-unparser", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["mocha-sinon", [ + ["npm:2.1.2", { + "packageLocation": "./.yarn/cache/mocha-sinon-npm-2.1.2-8583aedf2f-605cfdd9af.zip/node_modules/mocha-sinon/", + "packageDependencies": [ + ["mocha-sinon", "npm:2.1.2"] + ], + "linkType": "SOFT", + }], + ["virtual:595d7482cc8ddf98ee6aef33fc48b46393554ab5f17f851ef62e6e39315e53666c3e66226b978689aa0bc7f1e83a03081511a21db1c381362fe67614887077f9#npm:2.1.2", { + "packageLocation": "./.yarn/__virtual__/mocha-sinon-virtual-89c2a40275/0/cache/mocha-sinon-npm-2.1.2-8583aedf2f-605cfdd9af.zip/node_modules/mocha-sinon/", + "packageDependencies": [ + ["mocha-sinon", "virtual:595d7482cc8ddf98ee6aef33fc48b46393554ab5f17f851ef62e6e39315e53666c3e66226b978689aa0bc7f1e83a03081511a21db1c381362fe67614887077f9#npm:2.1.2"], + ["@types/mocha", null], + ["@types/sinon", null], + ["mocha", "npm:9.1.3"], + ["sinon", "npm:11.1.2"] + ], + "packagePeers": [ + "@types/mocha", + "@types/sinon", + "mocha", + "sinon" + ], + "linkType": "HARD", + }] + ]], + ["modify-values", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/modify-values-npm-1.0.1-9b2377e166-8296610c60.zip/node_modules/modify-values/", + "packageDependencies": [ + ["modify-values", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["module-deps", [ + ["npm:6.2.3", { + "packageLocation": "./.yarn/cache/module-deps-npm-6.2.3-948059fe9d-cccead8f81.zip/node_modules/module-deps/", + "packageDependencies": [ + ["module-deps", "npm:6.2.3"], + ["JSONStream", "npm:1.3.5"], + ["browser-resolve", "npm:2.0.0"], + ["cached-path-relative", "npm:1.0.2"], + ["concat-stream", "npm:1.6.2"], + ["defined", "npm:1.0.0"], + ["detective", "npm:5.2.0"], + ["duplexer2", "npm:0.1.4"], + ["inherits", "npm:2.0.4"], + ["parents", "npm:1.0.1"], + ["readable-stream", "npm:2.3.7"], + ["resolve", "patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b"], + ["stream-combiner2", "npm:1.1.1"], + ["subarg", "npm:1.0.0"], + ["through2", "npm:2.0.5"], + ["xtend", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["mongodb", [ + ["npm:3.7.3", { + "packageLocation": "./.yarn/cache/mongodb-npm-3.7.3-c479129d1e-ef7690fe6e.zip/node_modules/mongodb/", + "packageDependencies": [ + ["mongodb", "npm:3.7.3"] + ], + "linkType": "SOFT", + }], + ["virtual:a39316770159f0a8e3f370c1c3a56eb433794f8d2beaf0837a3349497b1cf2188cea77a97e39f187d7b9f59864fa6d9d57b4c49a9871c8de6a876e77cba350c7#npm:3.7.3", { + "packageLocation": "./.yarn/__virtual__/mongodb-virtual-79851ff444/0/cache/mongodb-npm-3.7.3-c479129d1e-ef7690fe6e.zip/node_modules/mongodb/", + "packageDependencies": [ + ["mongodb", "virtual:a39316770159f0a8e3f370c1c3a56eb433794f8d2beaf0837a3349497b1cf2188cea77a97e39f187d7b9f59864fa6d9d57b4c49a9871c8de6a876e77cba350c7#npm:3.7.3"], + ["@types/aws4", null], + ["@types/bson-ext", null], + ["@types/kerberos", null], + ["@types/mongodb-client-encryption", null], + ["@types/mongodb-extjson", null], + ["@types/snappy", null], + ["aws4", null], + ["bl", "npm:2.2.1"], + ["bson", "npm:1.1.6"], + ["bson-ext", null], + ["denque", "npm:1.5.1"], + ["kerberos", null], + ["mongodb-client-encryption", null], + ["mongodb-extjson", null], + ["optional-require", "npm:1.1.8"], + ["safe-buffer", "npm:5.2.1"], + ["saslprep", "npm:1.0.3"], + ["snappy", null] + ], + "packagePeers": [ + "@types/aws4", + "@types/bson-ext", + "@types/kerberos", + "@types/mongodb-client-encryption", + "@types/mongodb-extjson", + "@types/snappy", + "aws4", + "bson-ext", + "kerberos", + "mongodb-client-encryption", + "mongodb-extjson", + "snappy" + ], + "linkType": "HARD", + }] + ]], + ["mri", [ + ["npm:1.1.4", { + "packageLocation": "./.yarn/cache/mri-npm-1.1.4-d22a399f26-e65b9aed3b.zip/node_modules/mri/", + "packageDependencies": [ + ["mri", "npm:1.1.4"] + ], + "linkType": "HARD", + }] + ]], + ["ms", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/ms-npm-2.0.0-9e1101a471-0e6a22b8b7.zip/node_modules/ms/", + "packageDependencies": [ + ["ms", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["npm:2.1.2", { + "packageLocation": "./.yarn/cache/ms-npm-2.1.2-ec0c1512ff-673cdb2c31.zip/node_modules/ms/", + "packageDependencies": [ + ["ms", "npm:2.1.2"] + ], + "linkType": "HARD", + }], + ["npm:2.1.3", { + "packageLocation": "./.yarn/cache/ms-npm-2.1.3-81ff3cfac1-aa92de6080.zip/node_modules/ms/", + "packageDependencies": [ + ["ms", "npm:2.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["multimatch", [ + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/multimatch-npm-5.0.0-9938abf6fa-82c8030a53.zip/node_modules/multimatch/", + "packageDependencies": [ + ["multimatch", "npm:5.0.0"], + ["@types/minimatch", "npm:3.0.5"], + ["array-differ", "npm:3.0.0"], + ["array-union", "npm:2.1.0"], + ["arrify", "npm:2.0.1"], + ["minimatch", "npm:3.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["mute-stream", [ + ["npm:0.0.8", { + "packageLocation": "./.yarn/cache/mute-stream-npm-0.0.8-489a7d6c2b-ff48d251fc.zip/node_modules/mute-stream/", + "packageDependencies": [ + ["mute-stream", "npm:0.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["nan", [ + ["npm:2.14.2", { + "packageLocation": "./.yarn/unplugged/nan-npm-2.14.2-e3ede8ce5d/node_modules/nan/", + "packageDependencies": [ + ["nan", "npm:2.14.2"], + ["node-gyp", "npm:8.4.0"] + ], + "linkType": "HARD", + }], + ["npm:2.15.0", { + "packageLocation": "./.yarn/unplugged/nan-npm-2.15.0-505c98ef4d/node_modules/nan/", + "packageDependencies": [ + ["nan", "npm:2.15.0"], + ["node-gyp", "npm:8.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["nanoid", [ + ["npm:3.1.25", { + "packageLocation": "./.yarn/cache/nanoid-npm-3.1.25-c8f62ce160-e2353828c7.zip/node_modules/nanoid/", + "packageDependencies": [ + ["nanoid", "npm:3.1.25"] + ], + "linkType": "HARD", + }] + ]], + ["natural-compare", [ + ["npm:1.4.0", { + "packageLocation": "./.yarn/cache/natural-compare-npm-1.4.0-97b75b362d-23ad088b08.zip/node_modules/natural-compare/", + "packageDependencies": [ + ["natural-compare", "npm:1.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["natural-orderby", [ + ["npm:2.0.3", { + "packageLocation": "./.yarn/cache/natural-orderby-npm-2.0.3-e519eaa77c-039be7f0b6.zip/node_modules/natural-orderby/", + "packageDependencies": [ + ["natural-orderby", "npm:2.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["negotiator", [ + ["npm:0.6.2", { + "packageLocation": "./.yarn/cache/negotiator-npm-0.6.2-ba538e167a-dfddaff6c0.zip/node_modules/negotiator/", + "packageDependencies": [ + ["negotiator", "npm:0.6.2"] + ], + "linkType": "HARD", + }], + ["npm:0.6.3", { + "packageLocation": "./.yarn/cache/negotiator-npm-0.6.3-9d50e36171-b8ffeb1e26.zip/node_modules/negotiator/", + "packageDependencies": [ + ["negotiator", "npm:0.6.3"] + ], + "linkType": "HARD", + }] + ]], + ["neo-async", [ + ["npm:2.6.2", { + "packageLocation": "./.yarn/cache/neo-async-npm-2.6.2-75d6902586-deac9f8d00.zip/node_modules/neo-async/", + "packageDependencies": [ + ["neo-async", "npm:2.6.2"] + ], + "linkType": "HARD", + }] + ]], + ["neon-load-or-build", [ + ["npm:2.2.2", { + "packageLocation": "./.yarn/cache/neon-load-or-build-npm-2.2.2-548a286943-3cafba0e26.zip/node_modules/neon-load-or-build/", + "packageDependencies": [ + ["neon-load-or-build", "npm:2.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["neon-tag-prebuild", [ + ["https://github.com/shumkov/neon-tag-prebuild.git#commit=a429834da27432b129eceb737e4d2b3f03fa5496", { + "packageLocation": "./.yarn/cache/neon-tag-prebuild-https-d665d28b1f-00a16bf27c.zip/node_modules/neon-tag-prebuild/", + "packageDependencies": [ + ["neon-tag-prebuild", "https://github.com/shumkov/neon-tag-prebuild.git#commit=a429834da27432b129eceb737e4d2b3f03fa5496"], + ["mkdirp", "npm:1.0.4"], + ["node-abi", "npm:2.30.1"] + ], + "linkType": "HARD", + }] + ]], + ["net", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/net-npm-1.0.2-1d5514df5b-d97e215d92.zip/node_modules/net/", + "packageDependencies": [ + ["net", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["nice-try", [ + ["npm:1.0.5", { + "packageLocation": "./.yarn/cache/nice-try-npm-1.0.5-963856b16f-0b4af3b5bb.zip/node_modules/nice-try/", + "packageDependencies": [ + ["nice-try", "npm:1.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["nise", [ + ["npm:5.1.0", { + "packageLocation": "./.yarn/cache/nise-npm-5.1.0-8fc543b66e-e3843cc125.zip/node_modules/nise/", + "packageDependencies": [ + ["nise", "npm:5.1.0"], + ["@sinonjs/commons", "npm:1.8.3"], + ["@sinonjs/fake-timers", "npm:7.1.2"], + ["@sinonjs/text-encoding", "npm:0.7.1"], + ["just-extend", "npm:4.2.1"], + ["path-to-regexp", "npm:1.8.0"] + ], + "linkType": "HARD", + }] + ]], + ["no-case", [ + ["npm:3.0.4", { + "packageLocation": "./.yarn/cache/no-case-npm-3.0.4-12884c3d98-0b2ebc113d.zip/node_modules/no-case/", + "packageDependencies": [ + ["no-case", "npm:3.0.4"], + ["lower-case", "npm:2.0.2"], + ["tslib", "npm:2.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["node-abi", [ + ["npm:2.30.1", { + "packageLocation": "./.yarn/cache/node-abi-npm-2.30.1-36a2c4e28a-3f4b0c912c.zip/node_modules/node-abi/", + "packageDependencies": [ + ["node-abi", "npm:2.30.1"], + ["semver", "npm:5.7.1"] + ], + "linkType": "HARD", + }] + ]], + ["node-fetch", [ + ["npm:2.6.7", { + "packageLocation": "./.yarn/cache/node-fetch-npm-2.6.7-777aa2a6df-8d816ffd1e.zip/node_modules/node-fetch/", + "packageDependencies": [ + ["node-fetch", "npm:2.6.7"] + ], + "linkType": "SOFT", + }], + ["virtual:25a5f5382d53dbf298bf7a1191760bc2e0a523a619eeb0e667b99a8649e8ad183f9e2e0b45f6fb831b92f4078b61622aa567cf79565f6aa5af9597e3c84864f6#npm:2.6.7", { + "packageLocation": "./.yarn/__virtual__/node-fetch-virtual-d3846f8e12/0/cache/node-fetch-npm-2.6.7-777aa2a6df-8d816ffd1e.zip/node_modules/node-fetch/", + "packageDependencies": [ + ["node-fetch", "virtual:25a5f5382d53dbf298bf7a1191760bc2e0a523a619eeb0e667b99a8649e8ad183f9e2e0b45f6fb831b92f4078b61622aa567cf79565f6aa5af9597e3c84864f6#npm:2.6.7"], + ["@types/encoding", null], + ["encoding", null], + ["whatwg-url", "npm:5.0.0"] + ], + "packagePeers": [ + "@types/encoding", + "encoding" + ], + "linkType": "HARD", + }] + ]], + ["node-graceful", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/node-graceful-npm-3.1.0-645605305d-159d06ca29.zip/node_modules/node-graceful/", + "packageDependencies": [ + ["node-graceful", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["node-gyp", [ + ["npm:8.4.0", { + "packageLocation": "./.yarn/unplugged/node-gyp-npm-8.4.0-ee07b38f64/node_modules/node-gyp/", + "packageDependencies": [ + ["node-gyp", "npm:8.4.0"], + ["env-paths", "npm:2.2.1"], + ["glob", "npm:7.2.0"], + ["graceful-fs", "npm:4.2.10"], + ["make-fetch-happen", "npm:9.1.0"], + ["nopt", "npm:5.0.0"], + ["npmlog", "npm:4.1.2"], + ["rimraf", "npm:3.0.2"], + ["semver", "npm:7.3.5"], + ["tar", "npm:6.1.11"], + ["which", "npm:2.0.2"] + ], + "linkType": "HARD", + }], + ["npm:8.4.1", { + "packageLocation": "./.yarn/unplugged/node-gyp-npm-8.4.1-13c90a9c9b/node_modules/node-gyp/", + "packageDependencies": [ + ["node-gyp", "npm:8.4.1"], + ["env-paths", "npm:2.2.1"], + ["glob", "npm:7.2.0"], + ["graceful-fs", "npm:4.2.10"], + ["make-fetch-happen", "npm:9.1.0"], + ["nopt", "npm:5.0.0"], + ["npmlog", "npm:6.0.1"], + ["rimraf", "npm:3.0.2"], + ["semver", "npm:7.3.5"], + ["tar", "npm:6.1.11"], + ["which", "npm:2.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["node-gyp-build", [ + ["npm:4.3.0", { + "packageLocation": "./.yarn/cache/node-gyp-build-npm-4.3.0-87bdf5216f-1ecab16d9f.zip/node_modules/node-gyp-build/", + "packageDependencies": [ + ["node-gyp-build", "npm:4.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["node-inspect-extracted", [ + ["npm:1.0.8", { + "packageLocation": "./.yarn/cache/node-inspect-extracted-npm-1.0.8-53baa7fd4f-41ecca97d3.zip/node_modules/node-inspect-extracted/", + "packageDependencies": [ + ["node-inspect-extracted", "npm:1.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["node-preload", [ + ["npm:0.2.1", { + "packageLocation": "./.yarn/cache/node-preload-npm-0.2.1-5b6aef1c8e-4586f91ac7.zip/node_modules/node-preload/", + "packageDependencies": [ + ["node-preload", "npm:0.2.1"], + ["process-on-spawn", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["node-releases", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/node-releases-npm-2.0.1-77b8e327f7-b20dd8d4bc.zip/node_modules/node-releases/", + "packageDependencies": [ + ["node-releases", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["nodeforage", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/nodeforage-npm-1.1.2-38c6ac6257-a670ece8b5.zip/node_modules/nodeforage/", + "packageDependencies": [ + ["nodeforage", "npm:1.1.2"], + ["lodash.find", "npm:4.6.0"], + ["lodash.ismatch", "npm:4.4.0"], + ["lodash.merge", "npm:4.6.2"], + ["proper-lockfile", "npm:3.2.0"], + ["slocket", "npm:1.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["nodemon", [ + ["npm:2.0.15", { + "packageLocation": "./.yarn/unplugged/nodemon-npm-2.0.15-5e88e7aef5/node_modules/nodemon/", + "packageDependencies": [ + ["nodemon", "npm:2.0.15"], + ["chokidar", "npm:3.5.2"], + ["debug", "virtual:5e88e7aef540459ed10e9f06791cff75f35e8e44e625132fefff7246f500bd5b319d818390ce1930a4b11fbbce9102b75ea690c8a75bc6e0969fe6dd59e3c283#npm:3.2.7"], + ["ignore-by-default", "npm:1.0.1"], + ["minimatch", "npm:3.0.4"], + ["pstree.remy", "npm:1.1.8"], + ["semver", "npm:5.7.1"], + ["supports-color", "npm:5.5.0"], + ["touch", "npm:3.1.0"], + ["undefsafe", "npm:2.0.5"], + ["update-notifier", "npm:5.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["nofilter", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/nofilter-npm-3.1.0-3c5ba47d92-58aa85a5b4.zip/node_modules/nofilter/", + "packageDependencies": [ + ["nofilter", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["nopt", [ + ["npm:1.0.10", { + "packageLocation": "./.yarn/cache/nopt-npm-1.0.10-f3db192976-f62575acea.zip/node_modules/nopt/", + "packageDependencies": [ + ["nopt", "npm:1.0.10"], + ["abbrev", "npm:1.1.1"] + ], + "linkType": "HARD", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/nopt-npm-5.0.0-304b40fbfe-d35fdec187.zip/node_modules/nopt/", + "packageDependencies": [ + ["nopt", "npm:5.0.0"], + ["abbrev", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["normalize-package-data", [ + ["npm:2.5.0", { + "packageLocation": "./.yarn/cache/normalize-package-data-npm-2.5.0-af0345deed-7999112efc.zip/node_modules/normalize-package-data/", + "packageDependencies": [ + ["normalize-package-data", "npm:2.5.0"], + ["hosted-git-info", "npm:2.8.9"], + ["resolve", "patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b"], + ["semver", "npm:5.7.1"], + ["validate-npm-package-license", "npm:3.0.4"] + ], + "linkType": "HARD", + }], + ["npm:3.0.3", { + "packageLocation": "./.yarn/cache/normalize-package-data-npm-3.0.3-1a49056685-bbcee00339.zip/node_modules/normalize-package-data/", + "packageDependencies": [ + ["normalize-package-data", "npm:3.0.3"], + ["hosted-git-info", "npm:4.0.2"], + ["is-core-module", "npm:2.8.1"], + ["semver", "npm:7.3.5"], + ["validate-npm-package-license", "npm:3.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["normalize-path", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/normalize-path-npm-3.0.0-658ba7d77f-88eeb4da89.zip/node_modules/normalize-path/", + "packageDependencies": [ + ["normalize-path", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["normalize-url", [ + ["npm:4.5.1", { + "packageLocation": "./.yarn/cache/normalize-url-npm-4.5.1-603d40bc18-9a9dee01df.zip/node_modules/normalize-url/", + "packageDependencies": [ + ["normalize-url", "npm:4.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["npm-bundled", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/npm-bundled-npm-1.1.2-e299e533ef-6e599155ef.zip/node_modules/npm-bundled/", + "packageDependencies": [ + ["npm-bundled", "npm:1.1.2"], + ["npm-normalize-package-bin", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["npm-install-checks", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/npm-install-checks-npm-4.0.0-4dabe69bc2-8308ff48e6.zip/node_modules/npm-install-checks/", + "packageDependencies": [ + ["npm-install-checks", "npm:4.0.0"], + ["semver", "npm:7.3.5"] + ], + "linkType": "HARD", + }] + ]], + ["npm-normalize-package-bin", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/npm-normalize-package-bin-npm-1.0.1-2cf38a5d95-ae7f15155a.zip/node_modules/npm-normalize-package-bin/", + "packageDependencies": [ + ["npm-normalize-package-bin", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["npm-package-arg", [ + ["npm:8.1.5", { + "packageLocation": "./.yarn/cache/npm-package-arg-npm-8.1.5-02a51cea62-ae76afbceb.zip/node_modules/npm-package-arg/", + "packageDependencies": [ + ["npm-package-arg", "npm:8.1.5"], + ["hosted-git-info", "npm:4.0.2"], + ["semver", "npm:7.3.5"], + ["validate-npm-package-name", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["npm-packlist", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/npm-packlist-npm-3.0.0-9671ff7386-8550ecdec5.zip/node_modules/npm-packlist/", + "packageDependencies": [ + ["npm-packlist", "npm:3.0.0"], + ["glob", "npm:7.2.0"], + ["ignore-walk", "npm:4.0.1"], + ["npm-bundled", "npm:1.1.2"], + ["npm-normalize-package-bin", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["npm-pick-manifest", [ + ["npm:6.1.1", { + "packageLocation": "./.yarn/cache/npm-pick-manifest-npm-6.1.1-880ed92d15-7a7b9475ae.zip/node_modules/npm-pick-manifest/", + "packageDependencies": [ + ["npm-pick-manifest", "npm:6.1.1"], + ["npm-install-checks", "npm:4.0.0"], + ["npm-normalize-package-bin", "npm:1.0.1"], + ["npm-package-arg", "npm:8.1.5"], + ["semver", "npm:7.3.5"] + ], + "linkType": "HARD", + }] + ]], + ["npm-registry-fetch", [ + ["npm:12.0.2", { + "packageLocation": "./.yarn/cache/npm-registry-fetch-npm-12.0.2-4e28b8c5f6-88ef49b6fa.zip/node_modules/npm-registry-fetch/", + "packageDependencies": [ + ["npm-registry-fetch", "npm:12.0.2"], + ["make-fetch-happen", "npm:10.0.3"], + ["minipass", "npm:3.1.6"], + ["minipass-fetch", "npm:1.4.1"], + ["minipass-json-stream", "npm:1.0.1"], + ["minizlib", "npm:2.1.2"], + ["npm-package-arg", "npm:8.1.5"] + ], + "linkType": "HARD", + }] + ]], + ["npm-run-path", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/npm-run-path-npm-2.0.2-96c8b48857-acd5ad8164.zip/node_modules/npm-run-path/", + "packageDependencies": [ + ["npm-run-path", "npm:2.0.2"], + ["path-key", "npm:2.0.1"] + ], + "linkType": "HARD", + }], + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/npm-run-path-npm-4.0.1-7aebd8bab3-5374c0cea4.zip/node_modules/npm-run-path/", + "packageDependencies": [ + ["npm-run-path", "npm:4.0.1"], + ["path-key", "npm:3.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["npmlog", [ + ["npm:4.1.2", { + "packageLocation": "./.yarn/cache/npmlog-npm-4.1.2-cfb32957b5-edbda9f95e.zip/node_modules/npmlog/", + "packageDependencies": [ + ["npmlog", "npm:4.1.2"], + ["are-we-there-yet", "npm:1.1.7"], + ["console-control-strings", "npm:1.1.0"], + ["gauge", "npm:2.7.4"], + ["set-blocking", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.0.1", { + "packageLocation": "./.yarn/cache/npmlog-npm-5.0.1-366cab64a2-516b266302.zip/node_modules/npmlog/", + "packageDependencies": [ + ["npmlog", "npm:5.0.1"], + ["are-we-there-yet", "npm:2.0.0"], + ["console-control-strings", "npm:1.1.0"], + ["gauge", "npm:3.0.2"], + ["set-blocking", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["npm:6.0.1", { + "packageLocation": "./.yarn/cache/npmlog-npm-6.0.1-f597f2e057-f1a4078a73.zip/node_modules/npmlog/", + "packageDependencies": [ + ["npmlog", "npm:6.0.1"], + ["are-we-there-yet", "npm:3.0.0"], + ["console-control-strings", "npm:1.1.0"], + ["gauge", "npm:4.0.1"], + ["set-blocking", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["number-is-nan", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/number-is-nan-npm-1.0.1-845325a0fe-13656bc9aa.zip/node_modules/number-is-nan/", + "packageDependencies": [ + ["number-is-nan", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["nyc", [ + ["npm:15.1.0", { + "packageLocation": "./.yarn/cache/nyc-npm-15.1.0-f134b19668-82a7031982.zip/node_modules/nyc/", + "packageDependencies": [ + ["nyc", "npm:15.1.0"], + ["@istanbuljs/load-nyc-config", "npm:1.1.0"], + ["@istanbuljs/schema", "npm:0.1.3"], + ["caching-transform", "npm:4.0.0"], + ["convert-source-map", "npm:1.8.0"], + ["decamelize", "npm:1.2.0"], + ["find-cache-dir", "npm:3.3.2"], + ["find-up", "npm:4.1.0"], + ["foreground-child", "npm:2.0.0"], + ["get-package-type", "npm:0.1.0"], + ["glob", "npm:7.2.0"], + ["istanbul-lib-coverage", "npm:3.2.0"], + ["istanbul-lib-hook", "npm:3.0.0"], + ["istanbul-lib-instrument", "npm:4.0.3"], + ["istanbul-lib-processinfo", "npm:2.0.2"], + ["istanbul-lib-report", "npm:3.0.0"], + ["istanbul-lib-source-maps", "npm:4.0.1"], + ["istanbul-reports", "npm:3.0.5"], + ["make-dir", "npm:3.1.0"], + ["node-preload", "npm:0.2.1"], + ["p-map", "npm:3.0.0"], + ["process-on-spawn", "npm:1.0.0"], + ["resolve-from", "npm:5.0.0"], + ["rimraf", "npm:3.0.2"], + ["signal-exit", "npm:3.0.7"], + ["spawn-wrap", "npm:2.0.0"], + ["test-exclude", "npm:6.0.0"], + ["yargs", "npm:15.4.1"] + ], + "linkType": "HARD", + }] + ]], + ["oauth-sign", [ + ["npm:0.9.0", { + "packageLocation": "./.yarn/cache/oauth-sign-npm-0.9.0-7aa9422221-8f5497a127.zip/node_modules/oauth-sign/", + "packageDependencies": [ + ["oauth-sign", "npm:0.9.0"] + ], + "linkType": "HARD", + }] + ]], + ["object-assign", [ + ["npm:4.1.1", { + "packageLocation": "./.yarn/cache/object-assign-npm-4.1.1-1004ad6dec-fcc6e4ea8c.zip/node_modules/object-assign/", + "packageDependencies": [ + ["object-assign", "npm:4.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["object-inspect", [ + ["npm:1.11.0", { + "packageLocation": "./.yarn/cache/object-inspect-npm-1.11.0-c9d4bd1487-8c64f89ce3.zip/node_modules/object-inspect/", + "packageDependencies": [ + ["object-inspect", "npm:1.11.0"] + ], + "linkType": "HARD", + }] + ]], + ["object-is", [ + ["npm:1.1.5", { + "packageLocation": "./.yarn/cache/object-is-npm-1.1.5-48a862602b-989b18c4cb.zip/node_modules/object-is/", + "packageDependencies": [ + ["object-is", "npm:1.1.5"], + ["call-bind", "npm:1.0.2"], + ["define-properties", "npm:1.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["object-keys", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/object-keys-npm-1.1.1-1bf2f1be93-b363c5e764.zip/node_modules/object-keys/", + "packageDependencies": [ + ["object-keys", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["object-treeify", [ + ["npm:1.1.33", { + "packageLocation": "./.yarn/cache/object-treeify-npm-1.1.33-2273de9233-3af7f88934.zip/node_modules/object-treeify/", + "packageDependencies": [ + ["object-treeify", "npm:1.1.33"] + ], + "linkType": "HARD", + }] + ]], + ["object.assign", [ + ["npm:4.1.2", { + "packageLocation": "./.yarn/cache/object.assign-npm-4.1.2-d52edada1c-d621d832ed.zip/node_modules/object.assign/", + "packageDependencies": [ + ["object.assign", "npm:4.1.2"], + ["call-bind", "npm:1.0.2"], + ["define-properties", "npm:1.1.3"], + ["has-symbols", "npm:1.0.2"], + ["object-keys", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["object.entries", [ + ["npm:1.1.5", { + "packageLocation": "./.yarn/cache/object.entries-npm-1.1.5-7a8fcbc43e-d658696f74.zip/node_modules/object.entries/", + "packageDependencies": [ + ["object.entries", "npm:1.1.5"], + ["call-bind", "npm:1.0.2"], + ["define-properties", "npm:1.1.3"], + ["es-abstract", "npm:1.19.1"] + ], + "linkType": "HARD", + }] + ]], + ["object.values", [ + ["npm:1.1.5", { + "packageLocation": "./.yarn/cache/object.values-npm-1.1.5-f1de7f3742-0f17e99741.zip/node_modules/object.values/", + "packageDependencies": [ + ["object.values", "npm:1.1.5"], + ["call-bind", "npm:1.0.2"], + ["define-properties", "npm:1.1.3"], + ["es-abstract", "npm:1.19.1"] + ], + "linkType": "HARD", + }] + ]], + ["oclif", [ + ["npm:2.4.5", { + "packageLocation": "./.yarn/cache/oclif-npm-2.4.5-2547df0920-8c8901c5f3.zip/node_modules/oclif/", + "packageDependencies": [ + ["oclif", "npm:2.4.5"], + ["@oclif/core", "npm:1.3.4"], + ["@oclif/plugin-help", "npm:5.1.11"], + ["@oclif/plugin-not-found", "npm:2.3.1"], + ["@oclif/plugin-warn-if-update-available", "npm:2.0.4"], + ["aws-sdk", "npm:2.1076.0"], + ["concurrently", "npm:7.0.0"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["find-yarn-workspace-root", "npm:2.0.0"], + ["fs-extra", "npm:8.1.0"], + ["github-slugger", "npm:1.4.0"], + ["lodash", "npm:4.17.21"], + ["normalize-package-data", "npm:3.0.3"], + ["qqjs", "npm:0.3.11"], + ["semver", "npm:7.3.5"], + ["tslib", "npm:2.3.1"], + ["yeoman-environment", "virtual:2547df092054b19ae87906f86214dec0d3c475408d8bf5ca754c4734b3e71161d8ea0542bb5e9d4d900aaec1844ee46ab4983cd497a68035b6ae07fb64e1ee26#npm:3.9.1"], + ["yeoman-generator", "virtual:2547df092054b19ae87906f86214dec0d3c475408d8bf5ca754c4734b3e71161d8ea0542bb5e9d4d900aaec1844ee46ab4983cd497a68035b6ae07fb64e1ee26#npm:5.6.1"], + ["yosay", "npm:2.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["on-finished", [ + ["npm:2.3.0", { + "packageLocation": "./.yarn/cache/on-finished-npm-2.3.0-4ce92f72c6-1db595bd96.zip/node_modules/on-finished/", + "packageDependencies": [ + ["on-finished", "npm:2.3.0"], + ["ee-first", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["once", [ + ["npm:1.4.0", { + "packageLocation": "./.yarn/cache/once-npm-1.4.0-ccf03ef07a-cd0a885013.zip/node_modules/once/", + "packageDependencies": [ + ["once", "npm:1.4.0"], + ["wrappy", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["one-time", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/one-time-npm-1.0.0-aeaad5e524-fd008d7e99.zip/node_modules/one-time/", + "packageDependencies": [ + ["one-time", "npm:1.0.0"], + ["fn.name", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["onetime", [ + ["npm:5.1.2", { + "packageLocation": "./.yarn/cache/onetime-npm-5.1.2-3ed148fa42-2478859ef8.zip/node_modules/onetime/", + "packageDependencies": [ + ["onetime", "npm:5.1.2"], + ["mimic-fn", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["ono", [ + ["npm:6.0.1", { + "packageLocation": "./.yarn/cache/ono-npm-6.0.1-088f000ca0-182db954b7.zip/node_modules/ono/", + "packageDependencies": [ + ["ono", "npm:6.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["openapi-schemas", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/openapi-schemas-npm-1.0.3-d820c175a8-170dbf4d10.zip/node_modules/openapi-schemas/", + "packageDependencies": [ + ["openapi-schemas", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["openapi-types", [ + ["npm:1.3.5", { + "packageLocation": "./.yarn/cache/openapi-types-npm-1.3.5-f765461ce7-c2d20ea228.zip/node_modules/openapi-types/", + "packageDependencies": [ + ["openapi-types", "npm:1.3.5"] + ], + "linkType": "HARD", + }] + ]], + ["optional-require", [ + ["npm:1.1.8", { + "packageLocation": "./.yarn/cache/optional-require-npm-1.1.8-b94e3971c9-437db76f71.zip/node_modules/optional-require/", + "packageDependencies": [ + ["optional-require", "npm:1.1.8"], + ["require-at", "npm:1.0.6"] + ], + "linkType": "HARD", + }] + ]], + ["optionator", [ + ["npm:0.8.3", { + "packageLocation": "./.yarn/cache/optionator-npm-0.8.3-bc555bc5b7-b8695ddf3d.zip/node_modules/optionator/", + "packageDependencies": [ + ["optionator", "npm:0.8.3"], + ["deep-is", "npm:0.1.4"], + ["fast-levenshtein", "npm:2.0.6"], + ["levn", "npm:0.3.0"], + ["prelude-ls", "npm:1.1.2"], + ["type-check", "npm:0.3.2"], + ["word-wrap", "npm:1.2.3"] + ], + "linkType": "HARD", + }], + ["npm:0.9.1", { + "packageLocation": "./.yarn/cache/optionator-npm-0.9.1-577e397aae-dbc6fa0656.zip/node_modules/optionator/", + "packageDependencies": [ + ["optionator", "npm:0.9.1"], + ["deep-is", "npm:0.1.4"], + ["fast-levenshtein", "npm:2.0.6"], + ["levn", "npm:0.4.1"], + ["prelude-ls", "npm:1.2.1"], + ["type-check", "npm:0.4.0"], + ["word-wrap", "npm:1.2.3"] + ], + "linkType": "HARD", + }] + ]], + ["ora", [ + ["npm:5.4.1", { + "packageLocation": "./.yarn/cache/ora-npm-5.4.1-4f0343adb7-28d476ee6c.zip/node_modules/ora/", + "packageDependencies": [ + ["ora", "npm:5.4.1"], + ["bl", "npm:4.1.0"], + ["chalk", "npm:4.1.2"], + ["cli-cursor", "npm:3.1.0"], + ["cli-spinners", "npm:2.6.1"], + ["is-interactive", "npm:1.0.0"], + ["is-unicode-supported", "npm:0.1.0"], + ["log-symbols", "npm:4.1.0"], + ["strip-ansi", "npm:6.0.1"], + ["wcwidth", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["os-browserify", [ + ["npm:0.3.0", { + "packageLocation": "./.yarn/cache/os-browserify-npm-0.3.0-cbc91c79a5-16e37ba3c0.zip/node_modules/os-browserify/", + "packageDependencies": [ + ["os-browserify", "npm:0.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["os-tmpdir", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/os-tmpdir-npm-1.0.2-e305b0689b-5666560f7b.zip/node_modules/os-tmpdir/", + "packageDependencies": [ + ["os-tmpdir", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["p-cancelable", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/p-cancelable-npm-1.1.0-d147d5996f-2db3814fef.zip/node_modules/p-cancelable/", + "packageDependencies": [ + ["p-cancelable", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["p-finally", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/p-finally-npm-1.0.0-35fbaa57c6-93a654c53d.zip/node_modules/p-finally/", + "packageDependencies": [ + ["p-finally", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["p-limit", [ + ["npm:1.3.0", { + "packageLocation": "./.yarn/cache/p-limit-npm-1.3.0-fdb471d864-281c1c0b8c.zip/node_modules/p-limit/", + "packageDependencies": [ + ["p-limit", "npm:1.3.0"], + ["p-try", "npm:1.0.0"] + ], + "linkType": "HARD", + }], + ["npm:2.3.0", { + "packageLocation": "./.yarn/cache/p-limit-npm-2.3.0-94a0310039-84ff17f1a3.zip/node_modules/p-limit/", + "packageDependencies": [ + ["p-limit", "npm:2.3.0"], + ["p-try", "npm:2.2.0"] + ], + "linkType": "HARD", + }], + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/p-limit-npm-3.1.0-05d2ede37f-7c3690c4db.zip/node_modules/p-limit/", + "packageDependencies": [ + ["p-limit", "npm:3.1.0"], + ["yocto-queue", "npm:0.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["p-locate", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/p-locate-npm-2.0.0-3a2ee263dd-e2dceb9b49.zip/node_modules/p-locate/", + "packageDependencies": [ + ["p-locate", "npm:2.0.0"], + ["p-limit", "npm:1.3.0"] + ], + "linkType": "HARD", + }], + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/p-locate-npm-4.1.0-eec6872537-513bd14a45.zip/node_modules/p-locate/", + "packageDependencies": [ + ["p-locate", "npm:4.1.0"], + ["p-limit", "npm:2.3.0"] + ], + "linkType": "HARD", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/p-locate-npm-5.0.0-92cc7c7a3e-1623088f36.zip/node_modules/p-locate/", + "packageDependencies": [ + ["p-locate", "npm:5.0.0"], + ["p-limit", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["p-map", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/p-map-npm-3.0.0-e4f17c4167-49b0fcbc66.zip/node_modules/p-map/", + "packageDependencies": [ + ["p-map", "npm:3.0.0"], + ["aggregate-error", "npm:3.1.0"] + ], + "linkType": "HARD", + }], + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/p-map-npm-4.0.0-4677ae07c7-cb0ab21ec0.zip/node_modules/p-map/", + "packageDependencies": [ + ["p-map", "npm:4.0.0"], + ["aggregate-error", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["p-queue", [ + ["npm:6.6.2", { + "packageLocation": "./.yarn/cache/p-queue-npm-6.6.2-b173c5bfa8-832642fcc4.zip/node_modules/p-queue/", + "packageDependencies": [ + ["p-queue", "npm:6.6.2"], + ["eventemitter3", "npm:4.0.7"], + ["p-timeout", "npm:3.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["p-timeout", [ + ["npm:3.2.0", { + "packageLocation": "./.yarn/cache/p-timeout-npm-3.2.0-7fdb33f733-3dd0eaa048.zip/node_modules/p-timeout/", + "packageDependencies": [ + ["p-timeout", "npm:3.2.0"], + ["p-finally", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["p-transform", [ + ["npm:1.3.0", { + "packageLocation": "./.yarn/cache/p-transform-npm-1.3.0-99cf79f22a-d1e2d6ad75.zip/node_modules/p-transform/", + "packageDependencies": [ + ["p-transform", "npm:1.3.0"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["p-queue", "npm:6.6.2"] + ], + "linkType": "HARD", + }] + ]], + ["p-try", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/p-try-npm-1.0.0-7373139e40-3b5303f77e.zip/node_modules/p-try/", + "packageDependencies": [ + ["p-try", "npm:1.0.0"] + ], + "linkType": "HARD", + }], + ["npm:2.2.0", { + "packageLocation": "./.yarn/cache/p-try-npm-2.2.0-e0390dbaf8-f8a8e9a769.zip/node_modules/p-try/", + "packageDependencies": [ + ["p-try", "npm:2.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["package-hash", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/package-hash-npm-4.0.0-1e83d2429d-32c49e3a0e.zip/node_modules/package-hash/", + "packageDependencies": [ + ["package-hash", "npm:4.0.0"], + ["graceful-fs", "npm:4.2.10"], + ["hasha", "npm:5.2.2"], + ["lodash.flattendeep", "npm:4.4.0"], + ["release-zalgo", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["package-json", [ + ["npm:6.5.0", { + "packageLocation": "./.yarn/cache/package-json-npm-6.5.0-30e58237bb-cc9f890d36.zip/node_modules/package-json/", + "packageDependencies": [ + ["package-json", "npm:6.5.0"], + ["got", "npm:9.6.0"], + ["registry-auth-token", "npm:4.2.1"], + ["registry-url", "npm:5.1.0"], + ["semver", "npm:6.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["pacote", [ + ["npm:12.0.3", { + "packageLocation": "./.yarn/cache/pacote-npm-12.0.3-99a2ca9e19-730e2b3446.zip/node_modules/pacote/", + "packageDependencies": [ + ["pacote", "npm:12.0.3"], + ["@npmcli/git", "npm:2.1.0"], + ["@npmcli/installed-package-contents", "npm:1.0.7"], + ["@npmcli/promise-spawn", "npm:1.3.2"], + ["@npmcli/run-script", "npm:2.0.0"], + ["cacache", "npm:15.3.0"], + ["chownr", "npm:2.0.0"], + ["fs-minipass", "npm:2.1.0"], + ["infer-owner", "npm:1.0.4"], + ["minipass", "npm:3.1.6"], + ["mkdirp", "npm:1.0.4"], + ["npm-package-arg", "npm:8.1.5"], + ["npm-packlist", "npm:3.0.0"], + ["npm-pick-manifest", "npm:6.1.1"], + ["npm-registry-fetch", "npm:12.0.2"], + ["promise-retry", "npm:2.0.1"], + ["read-package-json-fast", "npm:2.0.3"], + ["rimraf", "npm:3.0.2"], + ["ssri", "npm:8.0.1"], + ["tar", "npm:6.1.11"] + ], + "linkType": "HARD", + }] + ]], + ["pad-component", [ + ["npm:0.0.1", { + "packageLocation": "./.yarn/cache/pad-component-npm-0.0.1-96c929da6f-2d92ad68b6.zip/node_modules/pad-component/", + "packageDependencies": [ + ["pad-component", "npm:0.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["pako", [ + ["npm:1.0.11", { + "packageLocation": "./.yarn/cache/pako-npm-1.0.11-b8f1b69d3e-1be2bfa1f8.zip/node_modules/pako/", + "packageDependencies": [ + ["pako", "npm:1.0.11"] + ], + "linkType": "HARD", + }] + ]], + ["parent-module", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/parent-module-npm-1.0.1-1fae11b095-6ba8b25514.zip/node_modules/parent-module/", + "packageDependencies": [ + ["parent-module", "npm:1.0.1"], + ["callsites", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["parents", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/parents-npm-1.0.1-2009842484-094fc817d5.zip/node_modules/parents/", + "packageDependencies": [ + ["parents", "npm:1.0.1"], + ["path-platform", "npm:0.11.15"] + ], + "linkType": "HARD", + }] + ]], + ["parse-asn1", [ + ["npm:5.1.6", { + "packageLocation": "./.yarn/cache/parse-asn1-npm-5.1.6-6cc3a6eeae-9243311d1f.zip/node_modules/parse-asn1/", + "packageDependencies": [ + ["parse-asn1", "npm:5.1.6"], + ["asn1.js", "npm:5.4.1"], + ["browserify-aes", "npm:1.2.0"], + ["evp_bytestokey", "npm:1.0.3"], + ["pbkdf2", "npm:3.1.2"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["parse-conflict-json", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/parse-conflict-json-npm-2.0.1-7cdcd9a753-398728731f.zip/node_modules/parse-conflict-json/", + "packageDependencies": [ + ["parse-conflict-json", "npm:2.0.1"], + ["json-parse-even-better-errors", "npm:2.3.1"], + ["just-diff", "npm:5.0.1"], + ["just-diff-apply", "npm:4.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["parse-json", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/parse-json-npm-4.0.0-a6f7771010-0fe227d410.zip/node_modules/parse-json/", + "packageDependencies": [ + ["parse-json", "npm:4.0.0"], + ["error-ex", "npm:1.3.2"], + ["json-parse-better-errors", "npm:1.0.2"] + ], + "linkType": "HARD", + }], + ["npm:5.2.0", { + "packageLocation": "./.yarn/cache/parse-json-npm-5.2.0-00a63b1199-62085b17d6.zip/node_modules/parse-json/", + "packageDependencies": [ + ["parse-json", "npm:5.2.0"], + ["@babel/code-frame", "npm:7.16.7"], + ["error-ex", "npm:1.3.2"], + ["json-parse-even-better-errors", "npm:2.3.1"], + ["lines-and-columns", "npm:1.1.6"] + ], + "linkType": "HARD", + }] + ]], + ["parse-ms", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/parse-ms-npm-2.1.0-de852c39bb-d5c66c76cc.zip/node_modules/parse-ms/", + "packageDependencies": [ + ["parse-ms", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["parseurl", [ + ["npm:1.3.3", { + "packageLocation": "./.yarn/cache/parseurl-npm-1.3.3-1542397e00-407cee8e0a.zip/node_modules/parseurl/", + "packageDependencies": [ + ["parseurl", "npm:1.3.3"] + ], + "linkType": "HARD", + }] + ]], + ["pascal-case", [ + ["npm:3.1.2", { + "packageLocation": "./.yarn/cache/pascal-case-npm-3.1.2-35f5b9bff6-ba98bfd595.zip/node_modules/pascal-case/", + "packageDependencies": [ + ["pascal-case", "npm:3.1.2"], + ["no-case", "npm:3.0.4"], + ["tslib", "npm:2.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["password-prompt", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/password-prompt-npm-1.1.2-086b60f9fe-4763ec1b48.zip/node_modules/password-prompt/", + "packageDependencies": [ + ["password-prompt", "npm:1.1.2"], + ["ansi-escapes", "npm:3.2.0"], + ["cross-spawn", "npm:6.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["path-browserify", [ + ["npm:0.0.1", { + "packageLocation": "./.yarn/cache/path-browserify-npm-0.0.1-bb8b2a97b1-ae8dcd45d0.zip/node_modules/path-browserify/", + "packageDependencies": [ + ["path-browserify", "npm:0.0.1"] + ], + "linkType": "HARD", + }], + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/path-browserify-npm-1.0.1-f975d99a99-c6d7fa3764.zip/node_modules/path-browserify/", + "packageDependencies": [ + ["path-browserify", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["path-exists", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/path-exists-npm-3.0.0-e80371aa68-96e92643aa.zip/node_modules/path-exists/", + "packageDependencies": [ + ["path-exists", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/path-exists-npm-4.0.0-e9e4f63eb0-505807199d.zip/node_modules/path-exists/", + "packageDependencies": [ + ["path-exists", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["path-is-absolute", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/path-is-absolute-npm-1.0.1-31bc695ffd-060840f92c.zip/node_modules/path-is-absolute/", + "packageDependencies": [ + ["path-is-absolute", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["path-key", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/path-key-npm-2.0.1-b1a971833d-f7ab0ad42f.zip/node_modules/path-key/", + "packageDependencies": [ + ["path-key", "npm:2.0.1"] + ], + "linkType": "HARD", + }], + ["npm:3.1.1", { + "packageLocation": "./.yarn/cache/path-key-npm-3.1.1-0e66ea8321-55cd7a9dd4.zip/node_modules/path-key/", + "packageDependencies": [ + ["path-key", "npm:3.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["path-parse", [ + ["npm:1.0.7", { + "packageLocation": "./.yarn/cache/path-parse-npm-1.0.7-09564527b7-49abf3d811.zip/node_modules/path-parse/", + "packageDependencies": [ + ["path-parse", "npm:1.0.7"] + ], + "linkType": "HARD", + }] + ]], + ["path-platform", [ + ["npm:0.11.15", { + "packageLocation": "./.yarn/cache/path-platform-npm-0.11.15-8cf3865ad1-239f2eae72.zip/node_modules/path-platform/", + "packageDependencies": [ + ["path-platform", "npm:0.11.15"] + ], + "linkType": "HARD", + }] + ]], + ["path-to-regexp", [ + ["npm:1.8.0", { + "packageLocation": "./.yarn/cache/path-to-regexp-npm-1.8.0-a1904f5c44-709f6f083c.zip/node_modules/path-to-regexp/", + "packageDependencies": [ + ["path-to-regexp", "npm:1.8.0"], + ["isarray", "npm:0.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["path-type", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/path-type-npm-3.0.0-252361a0eb-735b35e256.zip/node_modules/path-type/", + "packageDependencies": [ + ["path-type", "npm:3.0.0"], + ["pify", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/path-type-npm-4.0.0-10d47fc86a-5b1e2daa24.zip/node_modules/path-type/", + "packageDependencies": [ + ["path-type", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["pathval", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/pathval-npm-1.1.1-ce0311d7e0-090e314771.zip/node_modules/pathval/", + "packageDependencies": [ + ["pathval", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["pbkdf2", [ + ["npm:3.1.2", { + "packageLocation": "./.yarn/cache/pbkdf2-npm-3.1.2-d67bbb584f-2c950a100b.zip/node_modules/pbkdf2/", + "packageDependencies": [ + ["pbkdf2", "npm:3.1.2"], + ["create-hash", "npm:1.2.0"], + ["create-hmac", "npm:1.1.7"], + ["ripemd160", "npm:2.0.2"], + ["safe-buffer", "npm:5.2.1"], + ["sha.js", "npm:2.4.11"] + ], + "linkType": "HARD", + }] + ]], + ["performance-now", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/performance-now-npm-2.1.0-45e3ce7e49-534e641aa8.zip/node_modules/performance-now/", + "packageDependencies": [ + ["performance-now", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["picocolors", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/picocolors-npm-1.0.0-d81e0b1927-a2e8092dd8.zip/node_modules/picocolors/", + "packageDependencies": [ + ["picocolors", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["picomatch", [ + ["npm:2.3.0", { + "packageLocation": "./.yarn/cache/picomatch-npm-2.3.0-5e60e6c82d-16818720ea.zip/node_modules/picomatch/", + "packageDependencies": [ + ["picomatch", "npm:2.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["pid-cwd", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/unplugged/pid-cwd-npm-1.2.0-c7bf6feeb4/node_modules/pid-cwd/", + "packageDependencies": [ + ["pid-cwd", "npm:1.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["pify", [ + ["npm:2.3.0", { + "packageLocation": "./.yarn/cache/pify-npm-2.3.0-8b63310934-9503aaeaf4.zip/node_modules/pify/", + "packageDependencies": [ + ["pify", "npm:2.3.0"] + ], + "linkType": "HARD", + }], + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/pify-npm-3.0.0-679ee405c8-6cdcbc3567.zip/node_modules/pify/", + "packageDependencies": [ + ["pify", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/pify-npm-4.0.1-062756097b-9c4e34278c.zip/node_modules/pify/", + "packageDependencies": [ + ["pify", "npm:4.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["pino", [ + ["npm:6.13.3", { + "packageLocation": "./.yarn/cache/pino-npm-6.13.3-50e2aceb53-a580decd47.zip/node_modules/pino/", + "packageDependencies": [ + ["pino", "npm:6.13.3"], + ["fast-redact", "npm:3.0.2"], + ["fast-safe-stringify", "npm:2.1.1"], + ["fastify-warning", "npm:0.2.0"], + ["flatstr", "npm:1.0.12"], + ["pino-pretty", "npm:4.8.0"], + ["pino-std-serializers", "npm:3.2.0"], + ["quick-format-unescaped", "npm:4.0.4"], + ["sonic-boom", "npm:1.4.1"] + ], + "linkType": "HARD", + }] + ]], + ["pino-multi-stream", [ + ["npm:5.3.0", { + "packageLocation": "./.yarn/cache/pino-multi-stream-npm-5.3.0-ecb9b754cb-10ddb85983.zip/node_modules/pino-multi-stream/", + "packageDependencies": [ + ["pino-multi-stream", "npm:5.3.0"], + ["pino", "npm:6.13.3"] + ], + "linkType": "HARD", + }] + ]], + ["pino-pretty", [ + ["npm:4.8.0", { + "packageLocation": "./.yarn/cache/pino-pretty-npm-4.8.0-0c822e28cb-8e2e4cdb80.zip/node_modules/pino-pretty/", + "packageDependencies": [ + ["pino-pretty", "npm:4.8.0"], + ["@hapi/bourne", "npm:2.0.0"], + ["args", "npm:5.0.1"], + ["chalk", "npm:4.1.2"], + ["dateformat", "npm:4.6.3"], + ["fast-safe-stringify", "npm:2.1.1"], + ["jmespath", "npm:0.15.0"], + ["joycon", "npm:2.2.5"], + ["pump", "npm:3.0.0"], + ["readable-stream", "npm:3.6.0"], + ["rfdc", "npm:1.3.0"], + ["split2", "npm:3.2.2"], + ["strip-json-comments", "npm:3.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["pino-std-serializers", [ + ["npm:3.2.0", { + "packageLocation": "./.yarn/cache/pino-std-serializers-npm-3.2.0-9fd67503a4-77e29675b1.zip/node_modules/pino-std-serializers/", + "packageDependencies": [ + ["pino-std-serializers", "npm:3.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["pkg-dir", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/pkg-dir-npm-2.0.0-2b4bf4abd1-8c72b71230.zip/node_modules/pkg-dir/", + "packageDependencies": [ + ["pkg-dir", "npm:2.0.0"], + ["find-up", "npm:2.1.0"] + ], + "linkType": "HARD", + }], + ["npm:4.2.0", { + "packageLocation": "./.yarn/cache/pkg-dir-npm-4.2.0-2b5d0a8d32-9863e3f351.zip/node_modules/pkg-dir/", + "packageDependencies": [ + ["pkg-dir", "npm:4.2.0"], + ["find-up", "npm:4.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["preferred-pm", [ + ["npm:3.0.3", { + "packageLocation": "./.yarn/cache/preferred-pm-npm-3.0.3-68a4791e4b-0de0948cb6.zip/node_modules/preferred-pm/", + "packageDependencies": [ + ["preferred-pm", "npm:3.0.3"], + ["find-up", "npm:5.0.0"], + ["find-yarn-workspace-root2", "npm:1.2.16"], + ["path-exists", "npm:4.0.0"], + ["which-pm", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["prelude-ls", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/prelude-ls-npm-1.1.2-a0daac0886-c4867c8748.zip/node_modules/prelude-ls/", + "packageDependencies": [ + ["prelude-ls", "npm:1.1.2"] + ], + "linkType": "HARD", + }], + ["npm:1.2.1", { + "packageLocation": "./.yarn/cache/prelude-ls-npm-1.2.1-3e4d272a55-cd192ec0d0.zip/node_modules/prelude-ls/", + "packageDependencies": [ + ["prelude-ls", "npm:1.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["prepend-http", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/prepend-http-npm-2.0.0-e1fc4332f2-7694a95254.zip/node_modules/prepend-http/", + "packageDependencies": [ + ["prepend-http", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["pretty-bytes", [ + ["npm:5.6.0", { + "packageLocation": "./.yarn/cache/pretty-bytes-npm-5.6.0-0061079c9f-9c082500d1.zip/node_modules/pretty-bytes/", + "packageDependencies": [ + ["pretty-bytes", "npm:5.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["pretty-format", [ + ["npm:27.3.1", { + "packageLocation": "./.yarn/cache/pretty-format-npm-27.3.1-872f4f2791-2979eae85a.zip/node_modules/pretty-format/", + "packageDependencies": [ + ["pretty-format", "npm:27.3.1"], + ["@jest/types", "npm:27.2.5"], + ["ansi-regex", "npm:5.0.1"], + ["ansi-styles", "npm:5.2.0"], + ["react-is", "npm:17.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["pretty-ms", [ + ["npm:7.0.1", { + "packageLocation": "./.yarn/cache/pretty-ms-npm-7.0.1-d748cac064-d76c492028.zip/node_modules/pretty-ms/", + "packageDependencies": [ + ["pretty-ms", "npm:7.0.1"], + ["parse-ms", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["proc-log", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/proc-log-npm-1.0.0-cf9ff93bba-249605d5b2.zip/node_modules/proc-log/", + "packageDependencies": [ + ["proc-log", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["process", [ + ["npm:0.11.10", { + "packageLocation": "./.yarn/cache/process-npm-0.11.10-aeb3b641ae-bfcce49814.zip/node_modules/process/", + "packageDependencies": [ + ["process", "npm:0.11.10"] + ], + "linkType": "HARD", + }] + ]], + ["process-nextick-args", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/process-nextick-args-npm-2.0.1-b8d7971609-1d38588e52.zip/node_modules/process-nextick-args/", + "packageDependencies": [ + ["process-nextick-args", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["process-on-spawn", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/process-on-spawn-npm-1.0.0-676960b4dd-597769e3db.zip/node_modules/process-on-spawn/", + "packageDependencies": [ + ["process-on-spawn", "npm:1.0.0"], + ["fromentries", "npm:1.3.2"] + ], + "linkType": "HARD", + }] + ]], + ["progress", [ + ["npm:2.0.3", { + "packageLocation": "./.yarn/cache/progress-npm-2.0.3-d1f87e2ac6-f67403fe7b.zip/node_modules/progress/", + "packageDependencies": [ + ["progress", "npm:2.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["promise-all-reject-late", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/promise-all-reject-late-npm-1.0.1-19ba0dce9c-d7d61ac412.zip/node_modules/promise-all-reject-late/", + "packageDependencies": [ + ["promise-all-reject-late", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["promise-call-limit", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/promise-call-limit-npm-1.0.1-18d83007c3-e69aed17f5.zip/node_modules/promise-call-limit/", + "packageDependencies": [ + ["promise-call-limit", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["promise-inflight", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/promise-inflight-npm-1.0.1-5bb925afac-2274948309.zip/node_modules/promise-inflight/", + "packageDependencies": [ + ["promise-inflight", "npm:1.0.1"] + ], + "linkType": "SOFT", + }], + ["virtual:a7e5239c6ae68bf6359adfd3598326db000e94dbb349bc00a3852ed53a31712a0e2e787228c6e859d3e5cf2fbb872aba1ea4abe4995cef8086a77ef619ae1be6#npm:1.0.1", { + "packageLocation": "./.yarn/__virtual__/promise-inflight-virtual-b427a57c8f/0/cache/promise-inflight-npm-1.0.1-5bb925afac-2274948309.zip/node_modules/promise-inflight/", + "packageDependencies": [ + ["promise-inflight", "virtual:a7e5239c6ae68bf6359adfd3598326db000e94dbb349bc00a3852ed53a31712a0e2e787228c6e859d3e5cf2fbb872aba1ea4abe4995cef8086a77ef619ae1be6#npm:1.0.1"], + ["@types/bluebird", null], + ["bluebird", null] + ], + "packagePeers": [ + "@types/bluebird", + "bluebird" + ], + "linkType": "HARD", + }] + ]], + ["promise-retry", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/promise-retry-npm-2.0.1-871f0b01b7-f96a3f6d90.zip/node_modules/promise-retry/", + "packageDependencies": [ + ["promise-retry", "npm:2.0.1"], + ["err-code", "npm:2.0.3"], + ["retry", "npm:0.12.0"] + ], + "linkType": "HARD", + }] + ]], + ["proper-lockfile", [ + ["npm:3.2.0", { + "packageLocation": "./.yarn/cache/proper-lockfile-npm-3.2.0-4c500143f0-1be1bb702b.zip/node_modules/proper-lockfile/", + "packageDependencies": [ + ["proper-lockfile", "npm:3.2.0"], + ["graceful-fs", "npm:4.2.10"], + ["retry", "npm:0.12.0"], + ["signal-exit", "npm:3.0.7"] + ], + "linkType": "HARD", + }] + ]], + ["protobufjs", [ + ["npm:6.11.2", { + "packageLocation": "./.yarn/unplugged/protobufjs-npm-6.11.2-9b422ce98e/node_modules/protobufjs/", + "packageDependencies": [ + ["protobufjs", "npm:6.11.2"], + ["@protobufjs/aspromise", "npm:1.1.2"], + ["@protobufjs/base64", "npm:1.1.2"], + ["@protobufjs/codegen", "npm:2.0.4"], + ["@protobufjs/eventemitter", "npm:1.1.0"], + ["@protobufjs/fetch", "npm:1.1.0"], + ["@protobufjs/float", "npm:1.0.2"], + ["@protobufjs/inquire", "npm:1.1.0"], + ["@protobufjs/path", "npm:1.1.2"], + ["@protobufjs/pool", "npm:1.1.0"], + ["@protobufjs/utf8", "npm:1.1.0"], + ["@types/long", "npm:4.0.1"], + ["@types/node", "npm:17.0.21"], + ["long", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["protocol-buffers-encodings", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/protocol-buffers-encodings-npm-1.1.1-07111209e8-1b22d6d05b.zip/node_modules/protocol-buffers-encodings/", + "packageDependencies": [ + ["protocol-buffers-encodings", "npm:1.1.1"], + ["signed-varint", "npm:2.0.1"], + ["varint", "npm:5.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["prr", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/prr-npm-1.0.1-608d442761-3bca2db047.zip/node_modules/prr/", + "packageDependencies": [ + ["prr", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["ps-list", [ + ["npm:7.2.0", { + "packageLocation": "./.yarn/unplugged/ps-list-npm-7.2.0-7b32c6b513/node_modules/ps-list/", + "packageDependencies": [ + ["ps-list", "npm:7.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["psl", [ + ["npm:1.8.0", { + "packageLocation": "./.yarn/cache/psl-npm-1.8.0-226099d70e-6150048ed2.zip/node_modules/psl/", + "packageDependencies": [ + ["psl", "npm:1.8.0"] + ], + "linkType": "HARD", + }] + ]], + ["pstree.remy", [ + ["npm:1.1.8", { + "packageLocation": "./.yarn/cache/pstree.remy-npm-1.1.8-2dd5d55de2-5cb53698d6.zip/node_modules/pstree.remy/", + "packageDependencies": [ + ["pstree.remy", "npm:1.1.8"] + ], + "linkType": "HARD", + }] + ]], + ["public-encrypt", [ + ["npm:4.0.3", { + "packageLocation": "./.yarn/cache/public-encrypt-npm-4.0.3-b25e19fada-215d446e43.zip/node_modules/public-encrypt/", + "packageDependencies": [ + ["public-encrypt", "npm:4.0.3"], + ["bn.js", "npm:4.12.0"], + ["browserify-rsa", "npm:4.1.0"], + ["create-hash", "npm:1.2.0"], + ["parse-asn1", "npm:5.1.6"], + ["randombytes", "npm:2.1.0"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["public-ip", [ + ["npm:4.0.4", { + "packageLocation": "./.yarn/cache/public-ip-npm-4.0.4-8624c1184b-9a0c3194b2.zip/node_modules/public-ip/", + "packageDependencies": [ + ["public-ip", "npm:4.0.4"], + ["dns-socket", "npm:4.2.2"], + ["got", "npm:9.6.0"], + ["is-ip", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["pump", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/pump-npm-3.0.0-0080bf6a7a-e42e9229fb.zip/node_modules/pump/", + "packageDependencies": [ + ["pump", "npm:3.0.0"], + ["end-of-stream", "npm:1.4.4"], + ["once", "npm:1.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["punycode", [ + ["npm:1.3.2", { + "packageLocation": "./.yarn/cache/punycode-npm-1.3.2-3727a84cea-b8807fd594.zip/node_modules/punycode/", + "packageDependencies": [ + ["punycode", "npm:1.3.2"] + ], + "linkType": "HARD", + }], + ["npm:1.4.1", { + "packageLocation": "./.yarn/cache/punycode-npm-1.4.1-be4c23e6d2-fa6e698cb5.zip/node_modules/punycode/", + "packageDependencies": [ + ["punycode", "npm:1.4.1"] + ], + "linkType": "HARD", + }], + ["npm:2.1.1", { + "packageLocation": "./.yarn/cache/punycode-npm-2.1.1-26eb3e15cf-823bf443c6.zip/node_modules/punycode/", + "packageDependencies": [ + ["punycode", "npm:2.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["pupa", [ + ["npm:2.1.1", { + "packageLocation": "./.yarn/cache/pupa-npm-2.1.1-fb256825ba-49529e5037.zip/node_modules/pupa/", + "packageDependencies": [ + ["pupa", "npm:2.1.1"], + ["escape-goat", "npm:2.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["q", [ + ["npm:1.5.1", { + "packageLocation": "./.yarn/cache/q-npm-1.5.1-a28b3cfeaf-147baa93c8.zip/node_modules/q/", + "packageDependencies": [ + ["q", "npm:1.5.1"] + ], + "linkType": "HARD", + }] + ]], + ["qjobs", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/qjobs-npm-1.2.0-e3396bd5d4-eb64c00724.zip/node_modules/qjobs/", + "packageDependencies": [ + ["qjobs", "npm:1.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["qqjs", [ + ["npm:0.3.11", { + "packageLocation": "./.yarn/cache/qqjs-npm-0.3.11-a7e926aa2c-7962df855b.zip/node_modules/qqjs/", + "packageDependencies": [ + ["qqjs", "npm:0.3.11"], + ["chalk", "npm:2.4.2"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["execa", "npm:0.10.0"], + ["fs-extra", "npm:6.0.1"], + ["get-stream", "npm:5.2.0"], + ["glob", "npm:7.2.0"], + ["globby", "npm:10.0.2"], + ["http-call", "npm:5.3.0"], + ["load-json-file", "npm:6.2.0"], + ["pkg-dir", "npm:4.2.0"], + ["tar-fs", "npm:2.1.1"], + ["tmp", "npm:0.1.0"], + ["write-json-file", "npm:4.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["qs", [ + ["npm:6.5.2", { + "packageLocation": "./.yarn/cache/qs-npm-6.5.2-dbf9d8386b-24af7b9928.zip/node_modules/qs/", + "packageDependencies": [ + ["qs", "npm:6.5.2"] + ], + "linkType": "HARD", + }], + ["npm:6.7.0", { + "packageLocation": "./.yarn/cache/qs-npm-6.7.0-15161a344c-dfd5f6adef.zip/node_modules/qs/", + "packageDependencies": [ + ["qs", "npm:6.7.0"] + ], + "linkType": "HARD", + }] + ]], + ["querystring", [ + ["npm:0.2.0", { + "packageLocation": "./.yarn/cache/querystring-npm-0.2.0-421b870c92-8258d6734f.zip/node_modules/querystring/", + "packageDependencies": [ + ["querystring", "npm:0.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["querystring-es3", [ + ["npm:0.2.1", { + "packageLocation": "./.yarn/cache/querystring-es3-npm-0.2.1-f4632f2760-691e8d6b8b.zip/node_modules/querystring-es3/", + "packageDependencies": [ + ["querystring-es3", "npm:0.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["queue-microtask", [ + ["npm:1.2.3", { + "packageLocation": "./.yarn/cache/queue-microtask-npm-1.2.3-fcc98e4e2d-b676f8c040.zip/node_modules/queue-microtask/", + "packageDependencies": [ + ["queue-microtask", "npm:1.2.3"] + ], + "linkType": "HARD", + }] + ]], + ["quick-format-unescaped", [ + ["npm:4.0.4", { + "packageLocation": "./.yarn/cache/quick-format-unescaped-npm-4.0.4-7e22c9b7dc-7bc32b9935.zip/node_modules/quick-format-unescaped/", + "packageDependencies": [ + ["quick-format-unescaped", "npm:4.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["quick-lru", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/quick-lru-npm-4.0.1-ef8aa17c9c-bea46e1abf.zip/node_modules/quick-lru/", + "packageDependencies": [ + ["quick-lru", "npm:4.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["randombytes", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/randombytes-npm-2.1.0-e3da76bccf-d779499376.zip/node_modules/randombytes/", + "packageDependencies": [ + ["randombytes", "npm:2.1.0"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["randomfill", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/randomfill-npm-1.0.4-a08651a679-33734bb578.zip/node_modules/randomfill/", + "packageDependencies": [ + ["randomfill", "npm:1.0.4"], + ["randombytes", "npm:2.1.0"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["range-parser", [ + ["npm:1.2.1", { + "packageLocation": "./.yarn/cache/range-parser-npm-1.2.1-1a470fa390-0a268d4fea.zip/node_modules/range-parser/", + "packageDependencies": [ + ["range-parser", "npm:1.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["raw-body", [ + ["npm:2.4.0", { + "packageLocation": "./.yarn/cache/raw-body-npm-2.4.0-14d9d633af-6343906939.zip/node_modules/raw-body/", + "packageDependencies": [ + ["raw-body", "npm:2.4.0"], + ["bytes", "npm:3.1.0"], + ["http-errors", "npm:1.7.2"], + ["iconv-lite", "npm:0.4.24"], + ["unpipe", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["rc", [ + ["npm:1.2.8", { + "packageLocation": "./.yarn/cache/rc-npm-1.2.8-d6768ac936-2e26e052f8.zip/node_modules/rc/", + "packageDependencies": [ + ["rc", "npm:1.2.8"], + ["deep-extend", "npm:0.6.0"], + ["ini", "npm:1.3.8"], + ["minimist", "npm:1.2.5"], + ["strip-json-comments", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["react-is", [ + ["npm:17.0.2", { + "packageLocation": "./.yarn/cache/react-is-npm-17.0.2-091bbb8db6-9d6d111d89.zip/node_modules/react-is/", + "packageDependencies": [ + ["react-is", "npm:17.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["read-cmd-shim", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/read-cmd-shim-npm-2.0.0-bf49908226-024f0a092d.zip/node_modules/read-cmd-shim/", + "packageDependencies": [ + ["read-cmd-shim", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["read-only-stream", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/read-only-stream-npm-2.0.0-020991ee6f-aa48979d1f.zip/node_modules/read-only-stream/", + "packageDependencies": [ + ["read-only-stream", "npm:2.0.0"], + ["readable-stream", "npm:2.3.7"] + ], + "linkType": "HARD", + }] + ]], + ["read-package-json-fast", [ + ["npm:2.0.3", { + "packageLocation": "./.yarn/cache/read-package-json-fast-npm-2.0.3-f163572d18-fca37b3b21.zip/node_modules/read-package-json-fast/", + "packageDependencies": [ + ["read-package-json-fast", "npm:2.0.3"], + ["json-parse-even-better-errors", "npm:2.3.1"], + ["npm-normalize-package-bin", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["read-pkg", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/read-pkg-npm-3.0.0-41471436cb-398903ebae.zip/node_modules/read-pkg/", + "packageDependencies": [ + ["read-pkg", "npm:3.0.0"], + ["load-json-file", "npm:4.0.0"], + ["normalize-package-data", "npm:2.5.0"], + ["path-type", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.2.0", { + "packageLocation": "./.yarn/cache/read-pkg-npm-5.2.0-50426bd8dc-eb696e6052.zip/node_modules/read-pkg/", + "packageDependencies": [ + ["read-pkg", "npm:5.2.0"], + ["@types/normalize-package-data", "npm:2.4.1"], + ["normalize-package-data", "npm:2.5.0"], + ["parse-json", "npm:5.2.0"], + ["type-fest", "npm:0.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["read-pkg-up", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/read-pkg-up-npm-3.0.0-3d7faf047f-16175573f2.zip/node_modules/read-pkg-up/", + "packageDependencies": [ + ["read-pkg-up", "npm:3.0.0"], + ["find-up", "npm:2.1.0"], + ["read-pkg", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:7.0.1", { + "packageLocation": "./.yarn/cache/read-pkg-up-npm-7.0.1-11895bed9a-e4e93ce70e.zip/node_modules/read-pkg-up/", + "packageDependencies": [ + ["read-pkg-up", "npm:7.0.1"], + ["find-up", "npm:4.1.0"], + ["read-pkg", "npm:5.2.0"], + ["type-fest", "npm:0.8.1"] + ], + "linkType": "HARD", + }] + ]], + ["readable-stream", [ + ["npm:1.0.34", { + "packageLocation": "./.yarn/cache/readable-stream-npm-1.0.34-db63158f3f-85042c537e.zip/node_modules/readable-stream/", + "packageDependencies": [ + ["readable-stream", "npm:1.0.34"], + ["core-util-is", "npm:1.0.3"], + ["inherits", "npm:2.0.4"], + ["isarray", "npm:0.0.1"], + ["string_decoder", "npm:0.10.31"] + ], + "linkType": "HARD", + }], + ["npm:2.3.7", { + "packageLocation": "./.yarn/cache/readable-stream-npm-2.3.7-77b22a9818-e4920cf754.zip/node_modules/readable-stream/", + "packageDependencies": [ + ["readable-stream", "npm:2.3.7"], + ["core-util-is", "npm:1.0.3"], + ["inherits", "npm:2.0.4"], + ["isarray", "npm:1.0.0"], + ["process-nextick-args", "npm:2.0.1"], + ["safe-buffer", "npm:5.1.2"], + ["string_decoder", "npm:1.1.1"], + ["util-deprecate", "npm:1.0.2"] + ], + "linkType": "HARD", + }], + ["npm:3.6.0", { + "packageLocation": "./.yarn/cache/readable-stream-npm-3.6.0-23a4a5eb56-d4ea81502d.zip/node_modules/readable-stream/", + "packageDependencies": [ + ["readable-stream", "npm:3.6.0"], + ["inherits", "npm:2.0.4"], + ["string_decoder", "npm:1.3.0"], + ["util-deprecate", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["readdir-scoped-modules", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/readdir-scoped-modules-npm-1.1.0-651d6882ac-6d9f334e40.zip/node_modules/readdir-scoped-modules/", + "packageDependencies": [ + ["readdir-scoped-modules", "npm:1.1.0"], + ["debuglog", "npm:1.0.1"], + ["dezalgo", "npm:1.0.3"], + ["graceful-fs", "npm:4.2.10"], + ["once", "npm:1.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["readdirp", [ + ["npm:3.6.0", { + "packageLocation": "./.yarn/cache/readdirp-npm-3.6.0-f950cc74ab-1ced032e6e.zip/node_modules/readdirp/", + "packageDependencies": [ + ["readdirp", "npm:3.6.0"], + ["picomatch", "npm:2.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["rechoir", [ + ["npm:0.6.2", { + "packageLocation": "./.yarn/cache/rechoir-npm-0.6.2-0df5f171ec-fe76bf9c21.zip/node_modules/rechoir/", + "packageDependencies": [ + ["rechoir", "npm:0.6.2"], + ["resolve", "patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b"] + ], + "linkType": "HARD", + }], + ["npm:0.7.1", { + "packageLocation": "./.yarn/cache/rechoir-npm-0.7.1-0c7e5c1201-2a04aab4e2.zip/node_modules/rechoir/", + "packageDependencies": [ + ["rechoir", "npm:0.7.1"], + ["resolve", "patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b"] + ], + "linkType": "HARD", + }] + ]], + ["redent", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/redent-npm-3.0.0-31892f4906-fa1ef20404.zip/node_modules/redent/", + "packageDependencies": [ + ["redent", "npm:3.0.0"], + ["indent-string", "npm:4.0.0"], + ["strip-indent", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["redeyed", [ + ["npm:2.1.1", { + "packageLocation": "./.yarn/cache/redeyed-npm-2.1.1-7cbceb60bb-39a1426e37.zip/node_modules/redeyed/", + "packageDependencies": [ + ["redeyed", "npm:2.1.1"], + ["esprima", "npm:4.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["regenerate", [ + ["npm:1.4.2", { + "packageLocation": "./.yarn/cache/regenerate-npm-1.4.2-b296c5b63a-3317a09b2f.zip/node_modules/regenerate/", + "packageDependencies": [ + ["regenerate", "npm:1.4.2"] + ], + "linkType": "HARD", + }] + ]], + ["regenerate-unicode-properties", [ + ["npm:9.0.0", { + "packageLocation": "./.yarn/cache/regenerate-unicode-properties-npm-9.0.0-73b46c97bd-62df21c274.zip/node_modules/regenerate-unicode-properties/", + "packageDependencies": [ + ["regenerate-unicode-properties", "npm:9.0.0"], + ["regenerate", "npm:1.4.2"] + ], + "linkType": "HARD", + }] + ]], + ["regenerator-runtime", [ + ["npm:0.13.9", { + "packageLocation": "./.yarn/cache/regenerator-runtime-npm-0.13.9-6d02340eec-65ed455fe5.zip/node_modules/regenerator-runtime/", + "packageDependencies": [ + ["regenerator-runtime", "npm:0.13.9"] + ], + "linkType": "HARD", + }] + ]], + ["regenerator-transform", [ + ["npm:0.14.5", { + "packageLocation": "./.yarn/cache/regenerator-transform-npm-0.14.5-40045884e9-a467a3b652.zip/node_modules/regenerator-transform/", + "packageDependencies": [ + ["regenerator-transform", "npm:0.14.5"], + ["@babel/runtime", "npm:7.17.9"] + ], + "linkType": "HARD", + }] + ]], + ["regexpp", [ + ["npm:3.2.0", { + "packageLocation": "./.yarn/cache/regexpp-npm-3.2.0-2513f32cfc-a78dc5c715.zip/node_modules/regexpp/", + "packageDependencies": [ + ["regexpp", "npm:3.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["regexpu-core", [ + ["npm:4.8.0", { + "packageLocation": "./.yarn/cache/regexpu-core-npm-4.8.0-b5aa95540a-df92e3e648.zip/node_modules/regexpu-core/", + "packageDependencies": [ + ["regexpu-core", "npm:4.8.0"], + ["regenerate", "npm:1.4.2"], + ["regenerate-unicode-properties", "npm:9.0.0"], + ["regjsgen", "npm:0.5.2"], + ["regjsparser", "npm:0.7.0"], + ["unicode-match-property-ecmascript", "npm:2.0.0"], + ["unicode-match-property-value-ecmascript", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["regextras", [ + ["npm:0.7.1", { + "packageLocation": "./.yarn/cache/regextras-npm-0.7.1-f017685aa7-ffcd5bfd58.zip/node_modules/regextras/", + "packageDependencies": [ + ["regextras", "npm:0.7.1"] + ], + "linkType": "HARD", + }] + ]], + ["registry-auth-token", [ + ["npm:4.2.1", { + "packageLocation": "./.yarn/cache/registry-auth-token-npm-4.2.1-200e2be697-aa72060b57.zip/node_modules/registry-auth-token/", + "packageDependencies": [ + ["registry-auth-token", "npm:4.2.1"], + ["rc", "npm:1.2.8"] + ], + "linkType": "HARD", + }] + ]], + ["registry-url", [ + ["npm:5.1.0", { + "packageLocation": "./.yarn/cache/registry-url-npm-5.1.0-f58d0ca7ff-bcea86c84a.zip/node_modules/registry-url/", + "packageDependencies": [ + ["registry-url", "npm:5.1.0"], + ["rc", "npm:1.2.8"] + ], + "linkType": "HARD", + }] + ]], + ["regjsgen", [ + ["npm:0.5.2", { + "packageLocation": "./.yarn/cache/regjsgen-npm-0.5.2-4c9c408ab2-87c83d8488.zip/node_modules/regjsgen/", + "packageDependencies": [ + ["regjsgen", "npm:0.5.2"] + ], + "linkType": "HARD", + }] + ]], + ["regjsparser", [ + ["npm:0.7.0", { + "packageLocation": "./.yarn/cache/regjsparser-npm-0.7.0-a4d515e434-fefff9adca.zip/node_modules/regjsparser/", + "packageDependencies": [ + ["regjsparser", "npm:0.7.0"], + ["jsesc", "npm:0.5.0"] + ], + "linkType": "HARD", + }] + ]], + ["release-zalgo", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/release-zalgo-npm-1.0.0-aa3e59962f-b59849dc31.zip/node_modules/release-zalgo/", + "packageDependencies": [ + ["release-zalgo", "npm:1.0.0"], + ["es6-error", "npm:4.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["remove-trailing-separator", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/remove-trailing-separator-npm-1.1.0-16d7231316-d3c20b5a2d.zip/node_modules/remove-trailing-separator/", + "packageDependencies": [ + ["remove-trailing-separator", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["replace-ext", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/replace-ext-npm-1.0.1-ab0bac6614-4994ea1aaa.zip/node_modules/replace-ext/", + "packageDependencies": [ + ["replace-ext", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["request", [ + ["npm:2.88.2", { + "packageLocation": "./.yarn/cache/request-npm-2.88.2-f4a57c72c4-4e112c087f.zip/node_modules/request/", + "packageDependencies": [ + ["request", "npm:2.88.2"], + ["aws-sign2", "npm:0.7.0"], + ["aws4", "npm:1.11.0"], + ["caseless", "npm:0.12.0"], + ["combined-stream", "npm:1.0.8"], + ["extend", "npm:3.0.2"], + ["forever-agent", "npm:0.6.1"], + ["form-data", "npm:2.3.3"], + ["har-validator", "npm:5.1.5"], + ["http-signature", "npm:1.2.0"], + ["is-typedarray", "npm:1.0.0"], + ["isstream", "npm:0.1.2"], + ["json-stringify-safe", "npm:5.0.1"], + ["mime-types", "npm:2.1.34"], + ["oauth-sign", "npm:0.9.0"], + ["performance-now", "npm:2.1.0"], + ["qs", "npm:6.5.2"], + ["safe-buffer", "npm:5.2.1"], + ["tough-cookie", "npm:2.5.0"], + ["tunnel-agent", "npm:0.6.0"], + ["uuid", "npm:3.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["request-promise-core", [ + ["npm:1.1.4", { + "packageLocation": "./.yarn/cache/request-promise-core-npm-1.1.4-cb9fff6c90-c798bafd55.zip/node_modules/request-promise-core/", + "packageDependencies": [ + ["request-promise-core", "npm:1.1.4"] + ], + "linkType": "SOFT", + }], + ["virtual:0c85e267af17b2d01ad90466325c2ccc27f5214b08f889a03c9744573473515a6be391b5a64c9d6121b3ed0fe3eb434b28a617f2b0bf86aba1a08e6bf15973a9#npm:1.1.4", { + "packageLocation": "./.yarn/__virtual__/request-promise-core-virtual-7249cdb671/0/cache/request-promise-core-npm-1.1.4-cb9fff6c90-c798bafd55.zip/node_modules/request-promise-core/", + "packageDependencies": [ + ["request-promise-core", "virtual:0c85e267af17b2d01ad90466325c2ccc27f5214b08f889a03c9744573473515a6be391b5a64c9d6121b3ed0fe3eb434b28a617f2b0bf86aba1a08e6bf15973a9#npm:1.1.4"], + ["@types/request", null], + ["lodash", "npm:4.17.21"], + ["request", "npm:2.88.2"] + ], + "packagePeers": [ + "@types/request", + "request" + ], + "linkType": "HARD", + }] + ]], + ["request-promise-native", [ + ["npm:1.0.9", { + "packageLocation": "./.yarn/cache/request-promise-native-npm-1.0.9-6ae8e592e8-3e2c694eef.zip/node_modules/request-promise-native/", + "packageDependencies": [ + ["request-promise-native", "npm:1.0.9"] + ], + "linkType": "SOFT", + }], + ["virtual:6c6296bde00603e266f7d80babe1e01aa0c19f626934f58fe08f890a291bb1a38fcee25bf30c24857d5cfba290f01209decc48384318fd6815c5a514cb48be25#npm:1.0.9", { + "packageLocation": "./.yarn/__virtual__/request-promise-native-virtual-0c85e267af/0/cache/request-promise-native-npm-1.0.9-6ae8e592e8-3e2c694eef.zip/node_modules/request-promise-native/", + "packageDependencies": [ + ["request-promise-native", "virtual:6c6296bde00603e266f7d80babe1e01aa0c19f626934f58fe08f890a291bb1a38fcee25bf30c24857d5cfba290f01209decc48384318fd6815c5a514cb48be25#npm:1.0.9"], + ["@types/request", null], + ["request", "npm:2.88.2"], + ["request-promise-core", "virtual:0c85e267af17b2d01ad90466325c2ccc27f5214b08f889a03c9744573473515a6be391b5a64c9d6121b3ed0fe3eb434b28a617f2b0bf86aba1a08e6bf15973a9#npm:1.1.4"], + ["stealthy-require", "npm:1.1.1"], + ["tough-cookie", "npm:2.5.0"] + ], + "packagePeers": [ + "@types/request", + "request" + ], + "linkType": "HARD", + }] + ]], + ["require-at", [ + ["npm:1.0.6", { + "packageLocation": "./.yarn/cache/require-at-npm-1.0.6-eee905f868-7753a6ebad.zip/node_modules/require-at/", + "packageDependencies": [ + ["require-at", "npm:1.0.6"] + ], + "linkType": "HARD", + }] + ]], + ["require-directory", [ + ["npm:2.1.1", { + "packageLocation": "./.yarn/cache/require-directory-npm-2.1.1-8608aee50b-fb47e70bf0.zip/node_modules/require-directory/", + "packageDependencies": [ + ["require-directory", "npm:2.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["require-from-string", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/require-from-string-npm-2.0.2-8557e0db12-a03ef68954.zip/node_modules/require-from-string/", + "packageDependencies": [ + ["require-from-string", "npm:2.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["require-main-filename", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/require-main-filename-npm-2.0.0-03eef65c84-e9e294695f.zip/node_modules/require-main-filename/", + "packageDependencies": [ + ["require-main-filename", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["requires-port", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/requires-port-npm-1.0.0-fd036b488a-eee0e303ad.zip/node_modules/requires-port/", + "packageDependencies": [ + ["requires-port", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["resolve", [ + ["patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b", { + "packageLocation": "./.yarn/cache/resolve-patch-bad885c6ea-c79ecaea36.zip/node_modules/resolve/", + "packageDependencies": [ + ["resolve", "patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b"], + ["is-core-module", "npm:2.8.1"], + ["path-parse", "npm:1.0.7"], + ["supports-preserve-symlinks-flag", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["resolve-cwd", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/resolve-cwd-npm-3.0.0-e6f4e296bf-546e081601.zip/node_modules/resolve-cwd/", + "packageDependencies": [ + ["resolve-cwd", "npm:3.0.0"], + ["resolve-from", "npm:5.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["resolve-from", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/resolve-from-npm-4.0.0-f758ec21bf-f4ba0b8494.zip/node_modules/resolve-from/", + "packageDependencies": [ + ["resolve-from", "npm:4.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/resolve-from-npm-5.0.0-15c9db4d33-4ceeb9113e.zip/node_modules/resolve-from/", + "packageDependencies": [ + ["resolve-from", "npm:5.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["responselike", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/responselike-npm-1.0.2-d0bf50cde4-2e9e70f1dc.zip/node_modules/responselike/", + "packageDependencies": [ + ["responselike", "npm:1.0.2"], + ["lowercase-keys", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["restore-cursor", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/restore-cursor-npm-3.1.0-52c5a4c98f-f877dd8741.zip/node_modules/restore-cursor/", + "packageDependencies": [ + ["restore-cursor", "npm:3.1.0"], + ["onetime", "npm:5.1.2"], + ["signal-exit", "npm:3.0.7"] + ], + "linkType": "HARD", + }] + ]], + ["ret", [ + ["npm:0.2.2", { + "packageLocation": "./.yarn/cache/ret-npm-0.2.2-f5d3022812-774964bb41.zip/node_modules/ret/", + "packageDependencies": [ + ["ret", "npm:0.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["retry", [ + ["npm:0.12.0", { + "packageLocation": "./.yarn/cache/retry-npm-0.12.0-72ac7fb4cc-623bd7d2e5.zip/node_modules/retry/", + "packageDependencies": [ + ["retry", "npm:0.12.0"] + ], + "linkType": "HARD", + }] + ]], + ["reusify", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/reusify-npm-1.0.4-95ac4aec11-c3076ebcc2.zip/node_modules/reusify/", + "packageDependencies": [ + ["reusify", "npm:1.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["rfdc", [ + ["npm:1.3.0", { + "packageLocation": "./.yarn/cache/rfdc-npm-1.3.0-272f288ad8-fb2ba8512e.zip/node_modules/rfdc/", + "packageDependencies": [ + ["rfdc", "npm:1.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["rimraf", [ + ["npm:2.7.1", { + "packageLocation": "./.yarn/cache/rimraf-npm-2.7.1-9a71f3cc37-cdc7f6eacb.zip/node_modules/rimraf/", + "packageDependencies": [ + ["rimraf", "npm:2.7.1"], + ["glob", "npm:7.2.0"] + ], + "linkType": "HARD", + }], + ["npm:3.0.2", { + "packageLocation": "./.yarn/cache/rimraf-npm-3.0.2-2cb7dac69a-87f4164e39.zip/node_modules/rimraf/", + "packageDependencies": [ + ["rimraf", "npm:3.0.2"], + ["glob", "npm:7.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["ripemd160", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/ripemd160-npm-2.0.2-7b1fb8dc76-006accc405.zip/node_modules/ripemd160/", + "packageDependencies": [ + ["ripemd160", "npm:2.0.2"], + ["hash-base", "npm:3.1.0"], + ["inherits", "npm:2.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["run-async", [ + ["npm:2.4.1", { + "packageLocation": "./.yarn/cache/run-async-npm-2.4.1-a94bb90861-a2c88aa15d.zip/node_modules/run-async/", + "packageDependencies": [ + ["run-async", "npm:2.4.1"] + ], + "linkType": "HARD", + }] + ]], + ["run-parallel", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/run-parallel-npm-1.2.0-3f47ff2034-cb4f97ad25.zip/node_modules/run-parallel/", + "packageDependencies": [ + ["run-parallel", "npm:1.2.0"], + ["queue-microtask", "npm:1.2.3"] + ], + "linkType": "HARD", + }] + ]], + ["rxjs", [ + ["npm:6.6.7", { + "packageLocation": "./.yarn/cache/rxjs-npm-6.6.7-055046ea3c-bc334edef1.zip/node_modules/rxjs/", + "packageDependencies": [ + ["rxjs", "npm:6.6.7"], + ["tslib", "npm:1.14.1"] + ], + "linkType": "HARD", + }], + ["npm:7.5.4", { + "packageLocation": "./.yarn/cache/rxjs-npm-7.5.4-1527612cf9-6f55f835f2.zip/node_modules/rxjs/", + "packageDependencies": [ + ["rxjs", "npm:7.5.4"], + ["tslib", "npm:2.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["safe-buffer", [ + ["npm:5.1.2", { + "packageLocation": "./.yarn/cache/safe-buffer-npm-5.1.2-c27fedf6c4-f2f1f7943c.zip/node_modules/safe-buffer/", + "packageDependencies": [ + ["safe-buffer", "npm:5.1.2"] + ], + "linkType": "HARD", + }], + ["npm:5.2.1", { + "packageLocation": "./.yarn/cache/safe-buffer-npm-5.2.1-3481c8aa9b-b99c4b41fd.zip/node_modules/safe-buffer/", + "packageDependencies": [ + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["safe-regex2", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/safe-regex2-npm-2.0.0-eadecc9909-f5e182fca0.zip/node_modules/safe-regex2/", + "packageDependencies": [ + ["safe-regex2", "npm:2.0.0"], + ["ret", "npm:0.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["safe-stable-stringify", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/safe-stable-stringify-npm-1.1.1-1c282e1c55-e32a30720e.zip/node_modules/safe-stable-stringify/", + "packageDependencies": [ + ["safe-stable-stringify", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["safer-buffer", [ + ["npm:2.1.2", { + "packageLocation": "./.yarn/cache/safer-buffer-npm-2.1.2-8d5c0b705e-cab8f25ae6.zip/node_modules/safer-buffer/", + "packageDependencies": [ + ["safer-buffer", "npm:2.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["saslprep", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/saslprep-npm-1.0.3-8db649c346-4fdc0b70fb.zip/node_modules/saslprep/", + "packageDependencies": [ + ["saslprep", "npm:1.0.3"], + ["sparse-bitfield", "npm:3.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["sax", [ + ["npm:1.2.1", { + "packageLocation": "./.yarn/cache/sax-npm-1.2.1-fd2ad7b223-8dca7d5e1c.zip/node_modules/sax/", + "packageDependencies": [ + ["sax", "npm:1.2.1"] + ], + "linkType": "HARD", + }], + ["npm:1.2.4", { + "packageLocation": "./.yarn/cache/sax-npm-1.2.4-178f05f12f-d3df7d32b8.zip/node_modules/sax/", + "packageDependencies": [ + ["sax", "npm:1.2.4"] + ], + "linkType": "HARD", + }] + ]], + ["schema-utils", [ + ["npm:2.7.1", { + "packageLocation": "./.yarn/cache/schema-utils-npm-2.7.1-f84d18c473-32c62fc9e2.zip/node_modules/schema-utils/", + "packageDependencies": [ + ["schema-utils", "npm:2.7.1"], + ["@types/json-schema", "npm:7.0.9"], + ["ajv", "npm:6.12.6"], + ["ajv-keywords", "virtual:f84d18c473fad3c01e1cf352f81ad13de804ca40da5bf6e752464a2e78dcb097ad579b06da5ff33a55ba9957fb9c74909b99fc5e215420a3f9b5dc87ad71363b#npm:3.5.2"] + ], + "linkType": "HARD", + }], + ["npm:3.1.1", { + "packageLocation": "./.yarn/cache/schema-utils-npm-3.1.1-8704647575-fb73f3d759.zip/node_modules/schema-utils/", + "packageDependencies": [ + ["schema-utils", "npm:3.1.1"], + ["@types/json-schema", "npm:7.0.9"], + ["ajv", "npm:6.12.6"], + ["ajv-keywords", "virtual:f84d18c473fad3c01e1cf352f81ad13de804ca40da5bf6e752464a2e78dcb097ad579b06da5ff33a55ba9957fb9c74909b99fc5e215420a3f9b5dc87ad71363b#npm:3.5.2"] + ], + "linkType": "HARD", + }] + ]], + ["scoped-regex", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/scoped-regex-npm-2.1.0-6fbe8a6c4c-4e820444cb.zip/node_modules/scoped-regex/", + "packageDependencies": [ + ["scoped-regex", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["seedrandom", [ + ["npm:3.0.5", { + "packageLocation": "./.yarn/cache/seedrandom-npm-3.0.5-6946e8f8db-728b56bc3b.zip/node_modules/seedrandom/", + "packageDependencies": [ + ["seedrandom", "npm:3.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["semver", [ + ["npm:5.7.1", { + "packageLocation": "./.yarn/cache/semver-npm-5.7.1-40bcea106b-57fd0acfd0.zip/node_modules/semver/", + "packageDependencies": [ + ["semver", "npm:5.7.1"] + ], + "linkType": "HARD", + }], + ["npm:6.3.0", { + "packageLocation": "./.yarn/cache/semver-npm-6.3.0-b3eace8bfd-1b26ecf6db.zip/node_modules/semver/", + "packageDependencies": [ + ["semver", "npm:6.3.0"] + ], + "linkType": "HARD", + }], + ["npm:7.0.0", { + "packageLocation": "./.yarn/cache/semver-npm-7.0.0-218e8c00ca-272c11bf8d.zip/node_modules/semver/", + "packageDependencies": [ + ["semver", "npm:7.0.0"] + ], + "linkType": "HARD", + }], + ["npm:7.3.5", { + "packageLocation": "./.yarn/cache/semver-npm-7.3.5-618cf5db6a-5eafe6102b.zip/node_modules/semver/", + "packageDependencies": [ + ["semver", "npm:7.3.5"], + ["lru-cache", "npm:6.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["semver-diff", [ + ["npm:3.1.1", { + "packageLocation": "./.yarn/cache/semver-diff-npm-3.1.1-1207a795e9-8bbe5a5d7a.zip/node_modules/semver-diff/", + "packageDependencies": [ + ["semver-diff", "npm:3.1.1"], + ["semver", "npm:6.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["semver-store", [ + ["npm:0.3.0", { + "packageLocation": "./.yarn/cache/semver-store-npm-0.3.0-0fc88fd5b9-b38f747123.zip/node_modules/semver-store/", + "packageDependencies": [ + ["semver-store", "npm:0.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["serialize-javascript", [ + ["npm:6.0.0", { + "packageLocation": "./.yarn/cache/serialize-javascript-npm-6.0.0-0bb8a3c88d-56f90b562a.zip/node_modules/serialize-javascript/", + "packageDependencies": [ + ["serialize-javascript", "npm:6.0.0"], + ["randombytes", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["set-blocking", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/set-blocking-npm-2.0.0-49e2cffa24-6e65a05f7c.zip/node_modules/set-blocking/", + "packageDependencies": [ + ["set-blocking", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["setimmediate", [ + ["npm:1.0.5", { + "packageLocation": "./.yarn/cache/setimmediate-npm-1.0.5-54587459b6-c9a6f2c5b5.zip/node_modules/setimmediate/", + "packageDependencies": [ + ["setimmediate", "npm:1.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["setprototypeof", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/setprototypeof-npm-1.1.1-706b6318ec-a8bee29c1c.zip/node_modules/setprototypeof/", + "packageDependencies": [ + ["setprototypeof", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["sha.js", [ + ["npm:2.4.11", { + "packageLocation": "./.yarn/cache/sha.js-npm-2.4.11-14868df4ca-ebd3f59d4b.zip/node_modules/sha.js/", + "packageDependencies": [ + ["sha.js", "npm:2.4.11"], + ["inherits", "npm:2.0.4"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["shallow-clone", [ + ["npm:3.0.1", { + "packageLocation": "./.yarn/cache/shallow-clone-npm-3.0.1-dab5873d0d-39b3dd9630.zip/node_modules/shallow-clone/", + "packageDependencies": [ + ["shallow-clone", "npm:3.0.1"], + ["kind-of", "npm:6.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["shasum", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/shasum-npm-1.0.2-bcace62f08-61d908825c.zip/node_modules/shasum/", + "packageDependencies": [ + ["shasum", "npm:1.0.2"], + ["json-stable-stringify", "npm:0.0.1"], + ["sha.js", "npm:2.4.11"] + ], + "linkType": "HARD", + }] + ]], + ["shasum-object", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/shasum-object-npm-1.0.0-5c621ed8ed-fc3531b7ae.zip/node_modules/shasum-object/", + "packageDependencies": [ + ["shasum-object", "npm:1.0.0"], + ["fast-safe-stringify", "npm:2.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["shebang-command", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/shebang-command-npm-1.2.0-8990ba5d1d-9eed175030.zip/node_modules/shebang-command/", + "packageDependencies": [ + ["shebang-command", "npm:1.2.0"], + ["shebang-regex", "npm:1.0.0"] + ], + "linkType": "HARD", + }], + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/shebang-command-npm-2.0.0-eb2b01921d-6b52fe8727.zip/node_modules/shebang-command/", + "packageDependencies": [ + ["shebang-command", "npm:2.0.0"], + ["shebang-regex", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["shebang-regex", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/shebang-regex-npm-1.0.0-c3612b74e9-404c5a752c.zip/node_modules/shebang-regex/", + "packageDependencies": [ + ["shebang-regex", "npm:1.0.0"] + ], + "linkType": "HARD", + }], + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/shebang-regex-npm-3.0.0-899a0cd65e-1a2bcae50d.zip/node_modules/shebang-regex/", + "packageDependencies": [ + ["shebang-regex", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["shell-quote", [ + ["npm:1.7.3", { + "packageLocation": "./.yarn/cache/shell-quote-npm-1.7.3-76a78a6d77-aca58e73a3.zip/node_modules/shell-quote/", + "packageDependencies": [ + ["shell-quote", "npm:1.7.3"] + ], + "linkType": "HARD", + }] + ]], + ["shelljs", [ + ["npm:0.8.5", { + "packageLocation": "./.yarn/cache/shelljs-npm-0.8.5-44be43f84a-7babc46f73.zip/node_modules/shelljs/", + "packageDependencies": [ + ["shelljs", "npm:0.8.5"], + ["glob", "npm:7.2.0"], + ["interpret", "npm:1.4.0"], + ["rechoir", "npm:0.6.2"] + ], + "linkType": "HARD", + }] + ]], + ["shellwords-ts", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/shellwords-ts-npm-3.0.0-f21ef3e36f-32faa081b1.zip/node_modules/shellwords-ts/", + "packageDependencies": [ + ["shellwords-ts", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["should", [ + ["npm:13.2.3", { + "packageLocation": "./.yarn/cache/should-npm-13.2.3-fbb7954a33-74bcc0eb85.zip/node_modules/should/", + "packageDependencies": [ + ["should", "npm:13.2.3"], + ["should-equal", "npm:2.0.0"], + ["should-format", "npm:3.0.3"], + ["should-type", "npm:1.4.0"], + ["should-type-adaptors", "npm:1.1.0"], + ["should-util", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["should-equal", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/should-equal-npm-2.0.0-ae8768ed44-3f3580a223.zip/node_modules/should-equal/", + "packageDependencies": [ + ["should-equal", "npm:2.0.0"], + ["should-type", "npm:1.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["should-format", [ + ["npm:3.0.3", { + "packageLocation": "./.yarn/cache/should-format-npm-3.0.3-74f60dd776-5304e89b4d.zip/node_modules/should-format/", + "packageDependencies": [ + ["should-format", "npm:3.0.3"], + ["should-type", "npm:1.4.0"], + ["should-type-adaptors", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["should-type", [ + ["npm:1.4.0", { + "packageLocation": "./.yarn/cache/should-type-npm-1.4.0-6590b6ee32-88d9324c6c.zip/node_modules/should-type/", + "packageDependencies": [ + ["should-type", "npm:1.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["should-type-adaptors", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/should-type-adaptors-npm-1.1.0-730d8324e4-94dd1d225c.zip/node_modules/should-type-adaptors/", + "packageDependencies": [ + ["should-type-adaptors", "npm:1.1.0"], + ["should-type", "npm:1.4.0"], + ["should-util", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["should-util", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/should-util-npm-1.0.1-f3701a5e03-c3be15e0fd.zip/node_modules/should-util/", + "packageDependencies": [ + ["should-util", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["side-channel", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/side-channel-npm-1.0.4-e1f38b9e06-351e41b947.zip/node_modules/side-channel/", + "packageDependencies": [ + ["side-channel", "npm:1.0.4"], + ["call-bind", "npm:1.0.2"], + ["get-intrinsic", "npm:1.1.1"], + ["object-inspect", "npm:1.11.0"] + ], + "linkType": "HARD", + }] + ]], + ["signal-exit", [ + ["npm:3.0.7", { + "packageLocation": "./.yarn/cache/signal-exit-npm-3.0.7-bd270458a3-a2f098f247.zip/node_modules/signal-exit/", + "packageDependencies": [ + ["signal-exit", "npm:3.0.7"] + ], + "linkType": "HARD", + }] + ]], + ["signed-varint", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/signed-varint-npm-2.0.1-18301876e5-a9fd2d954d.zip/node_modules/signed-varint/", + "packageDependencies": [ + ["signed-varint", "npm:2.0.1"], + ["varint", "npm:5.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["simple-concat", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/simple-concat-npm-1.0.1-48df70de29-4d211042cc.zip/node_modules/simple-concat/", + "packageDependencies": [ + ["simple-concat", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["simple-swizzle", [ + ["npm:0.2.2", { + "packageLocation": "./.yarn/cache/simple-swizzle-npm-0.2.2-8dee37fad1-a7f3f2ab5c.zip/node_modules/simple-swizzle/", + "packageDependencies": [ + ["simple-swizzle", "npm:0.2.2"], + ["is-arrayish", "npm:0.3.2"] + ], + "linkType": "HARD", + }] + ]], + ["simple-wcswidth", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/simple-wcswidth-npm-1.0.1-ac1dd0a592-dc5bf4cb13.zip/node_modules/simple-wcswidth/", + "packageDependencies": [ + ["simple-wcswidth", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["sinon", [ + ["npm:11.1.2", { + "packageLocation": "./.yarn/cache/sinon-npm-11.1.2-5325724cb2-1d01377e23.zip/node_modules/sinon/", + "packageDependencies": [ + ["sinon", "npm:11.1.2"], + ["@sinonjs/commons", "npm:1.8.3"], + ["@sinonjs/fake-timers", "npm:7.1.2"], + ["@sinonjs/samsam", "npm:6.0.2"], + ["diff", "npm:5.0.0"], + ["nise", "npm:5.1.0"], + ["supports-color", "npm:7.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["sinon-chai", [ + ["npm:3.7.0", { + "packageLocation": "./.yarn/cache/sinon-chai-npm-3.7.0-8e6588805e-49a353d8eb.zip/node_modules/sinon-chai/", + "packageDependencies": [ + ["sinon-chai", "npm:3.7.0"] + ], + "linkType": "SOFT", + }], + ["virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0", { + "packageLocation": "./.yarn/__virtual__/sinon-chai-virtual-942ebe16a9/0/cache/sinon-chai-npm-3.7.0-8e6588805e-49a353d8eb.zip/node_modules/sinon-chai/", + "packageDependencies": [ + ["sinon-chai", "virtual:12a596dc9572e25ce715d3736dc74b317c5ca5cfb3b4f67864b1e76b3a401006f84d381aaf975bb5b4da9cafac5125e6869fc78d5202f4c95780c81479112f32#npm:3.7.0"], + ["@types/chai", null], + ["@types/sinon", null], + ["chai", "npm:4.3.4"], + ["sinon", "npm:11.1.2"] + ], + "packagePeers": [ + "@types/chai", + "@types/sinon", + "chai", + "sinon" + ], + "linkType": "HARD", + }], + ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:3.7.0", { + "packageLocation": "./.yarn/__virtual__/sinon-chai-virtual-02851cc178/0/cache/sinon-chai-npm-3.7.0-8e6588805e-49a353d8eb.zip/node_modules/sinon-chai/", + "packageDependencies": [ + ["sinon-chai", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:3.7.0"], + ["@types/chai", "npm:4.2.22"], + ["@types/sinon", "npm:9.0.11"], + ["chai", "npm:4.3.4"], + ["sinon", "npm:11.1.2"] + ], + "packagePeers": [ + "@types/chai", + "@types/sinon", + "chai", + "sinon" + ], + "linkType": "HARD", + }] + ]], + ["slash", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/slash-npm-3.0.0-b87de2279a-94a93fff61.zip/node_modules/slash/", + "packageDependencies": [ + ["slash", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["slice-ansi", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/slice-ansi-npm-2.1.0-02505ccc06-4e82995aa5.zip/node_modules/slice-ansi/", + "packageDependencies": [ + ["slice-ansi", "npm:2.1.0"], + ["ansi-styles", "npm:3.2.1"], + ["astral-regex", "npm:1.0.0"], + ["is-fullwidth-code-point", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/slice-ansi-npm-3.0.0-d9999864af-5ec6d022d1.zip/node_modules/slice-ansi/", + "packageDependencies": [ + ["slice-ansi", "npm:3.0.0"], + ["ansi-styles", "npm:4.3.0"], + ["astral-regex", "npm:2.0.0"], + ["is-fullwidth-code-point", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/slice-ansi-npm-4.0.0-6eeca1d10e-4a82d7f085.zip/node_modules/slice-ansi/", + "packageDependencies": [ + ["slice-ansi", "npm:4.0.0"], + ["ansi-styles", "npm:4.3.0"], + ["astral-regex", "npm:2.0.0"], + ["is-fullwidth-code-point", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["slocket", [ + ["npm:1.0.5", { + "packageLocation": "./.yarn/cache/slocket-npm-1.0.5-6a604ece59-4ea3cba56c.zip/node_modules/slocket/", + "packageDependencies": [ + ["slocket", "npm:1.0.5"], + ["bluebird", "npm:3.7.2"], + ["rimraf", "npm:2.7.1"], + ["signal-exit", "npm:3.0.7"] + ], + "linkType": "HARD", + }] + ]], + ["smart-buffer", [ + ["npm:4.2.0", { + "packageLocation": "./.yarn/cache/smart-buffer-npm-4.2.0-5ac3f668bb-b5167a7142.zip/node_modules/smart-buffer/", + "packageDependencies": [ + ["smart-buffer", "npm:4.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["socket.io", [ + ["npm:4.4.0", { + "packageLocation": "./.yarn/cache/socket.io-npm-4.4.0-dc1419e09a-3e680f6969.zip/node_modules/socket.io/", + "packageDependencies": [ + ["socket.io", "npm:4.4.0"], + ["accepts", "npm:1.3.7"], + ["base64id", "npm:2.0.0"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["engine.io", "npm:6.1.0"], + ["socket.io-adapter", "npm:2.3.3"], + ["socket.io-parser", "npm:4.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["socket.io-adapter", [ + ["npm:2.3.3", { + "packageLocation": "./.yarn/cache/socket.io-adapter-npm-2.3.3-4fd6b5d0bd-73890e0a33.zip/node_modules/socket.io-adapter/", + "packageDependencies": [ + ["socket.io-adapter", "npm:2.3.3"] + ], + "linkType": "HARD", + }] + ]], + ["socket.io-parser", [ + ["npm:4.0.4", { + "packageLocation": "./.yarn/cache/socket.io-parser-npm-4.0.4-1dfc284556-c173b4f374.zip/node_modules/socket.io-parser/", + "packageDependencies": [ + ["socket.io-parser", "npm:4.0.4"], + ["@types/component-emitter", "npm:1.2.11"], + ["component-emitter", "npm:1.3.0"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"] + ], + "linkType": "HARD", + }] + ]], + ["socks", [ + ["npm:2.6.1", { + "packageLocation": "./.yarn/cache/socks-npm-2.6.1-09133d0d22-2ca9d616e4.zip/node_modules/socks/", + "packageDependencies": [ + ["socks", "npm:2.6.1"], + ["ip", "npm:1.1.5"], + ["smart-buffer", "npm:4.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["socks-proxy-agent", [ + ["npm:6.1.1", { + "packageLocation": "./.yarn/cache/socks-proxy-agent-npm-6.1.1-a3843946ba-9a8a4f791b.zip/node_modules/socks-proxy-agent/", + "packageDependencies": [ + ["socks-proxy-agent", "npm:6.1.1"], + ["agent-base", "npm:6.0.2"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["socks", "npm:2.6.1"] + ], + "linkType": "HARD", + }] + ]], + ["sonic-boom", [ + ["npm:1.4.1", { + "packageLocation": "./.yarn/cache/sonic-boom-npm-1.4.1-e42b921f99-189fa8fe5c.zip/node_modules/sonic-boom/", + "packageDependencies": [ + ["sonic-boom", "npm:1.4.1"], + ["atomic-sleep", "npm:1.0.0"], + ["flatstr", "npm:1.0.12"] + ], + "linkType": "HARD", + }], + ["npm:2.3.1", { + "packageLocation": "./.yarn/cache/sonic-boom-npm-2.3.1-0ba04b648c-4f5022de97.zip/node_modules/sonic-boom/", + "packageDependencies": [ + ["sonic-boom", "npm:2.3.1"], + ["atomic-sleep", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["sort-keys", [ + ["npm:4.2.0", { + "packageLocation": "./.yarn/cache/sort-keys-npm-4.2.0-bf52ceef80-1535ffd5a7.zip/node_modules/sort-keys/", + "packageDependencies": [ + ["sort-keys", "npm:4.2.0"], + ["is-plain-obj", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["source-map", [ + ["npm:0.5.7", { + "packageLocation": "./.yarn/cache/source-map-npm-0.5.7-7c3f035429-5dc2043b93.zip/node_modules/source-map/", + "packageDependencies": [ + ["source-map", "npm:0.5.7"] + ], + "linkType": "HARD", + }], + ["npm:0.6.1", { + "packageLocation": "./.yarn/cache/source-map-npm-0.6.1-1a3621db16-59ce8640cf.zip/node_modules/source-map/", + "packageDependencies": [ + ["source-map", "npm:0.6.1"] + ], + "linkType": "HARD", + }], + ["npm:0.7.3", { + "packageLocation": "./.yarn/cache/source-map-npm-0.7.3-e3b4f7982a-cd24efb3b8.zip/node_modules/source-map/", + "packageDependencies": [ + ["source-map", "npm:0.7.3"] + ], + "linkType": "HARD", + }] + ]], + ["source-map-support", [ + ["npm:0.5.21", { + "packageLocation": "./.yarn/cache/source-map-support-npm-0.5.21-09ca99e250-43e98d700d.zip/node_modules/source-map-support/", + "packageDependencies": [ + ["source-map-support", "npm:0.5.21"], + ["buffer-from", "npm:1.1.2"], + ["source-map", "npm:0.6.1"] + ], + "linkType": "HARD", + }] + ]], + ["sparse-bitfield", [ + ["npm:3.0.3", { + "packageLocation": "./.yarn/cache/sparse-bitfield-npm-3.0.3-cb80d0c89f-174da88dbb.zip/node_modules/sparse-bitfield/", + "packageDependencies": [ + ["sparse-bitfield", "npm:3.0.3"], + ["memory-pager", "npm:1.5.0"] + ], + "linkType": "HARD", + }] + ]], + ["spawn-command", [ + ["npm:0.0.2", { + "packageLocation": "./.yarn/cache/spawn-command-npm-0.0.2-014d4d5d9f-e35c5d2817.zip/node_modules/spawn-command/", + "packageDependencies": [ + ["spawn-command", "npm:0.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["spawn-wrap", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/spawn-wrap-npm-2.0.0-368c0a5bad-5a518e3762.zip/node_modules/spawn-wrap/", + "packageDependencies": [ + ["spawn-wrap", "npm:2.0.0"], + ["foreground-child", "npm:2.0.0"], + ["is-windows", "npm:1.0.2"], + ["make-dir", "npm:3.1.0"], + ["rimraf", "npm:3.0.2"], + ["signal-exit", "npm:3.0.7"], + ["which", "npm:2.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["spdx-correct", [ + ["npm:3.1.1", { + "packageLocation": "./.yarn/cache/spdx-correct-npm-3.1.1-47f574c27a-77ce438344.zip/node_modules/spdx-correct/", + "packageDependencies": [ + ["spdx-correct", "npm:3.1.1"], + ["spdx-expression-parse", "npm:3.0.1"], + ["spdx-license-ids", "npm:3.0.11"] + ], + "linkType": "HARD", + }] + ]], + ["spdx-exceptions", [ + ["npm:2.3.0", { + "packageLocation": "./.yarn/cache/spdx-exceptions-npm-2.3.0-2b68dad75a-cb69a26fa3.zip/node_modules/spdx-exceptions/", + "packageDependencies": [ + ["spdx-exceptions", "npm:2.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["spdx-expression-parse", [ + ["npm:3.0.1", { + "packageLocation": "./.yarn/cache/spdx-expression-parse-npm-3.0.1-b718cbb35a-a1c6e104a2.zip/node_modules/spdx-expression-parse/", + "packageDependencies": [ + ["spdx-expression-parse", "npm:3.0.1"], + ["spdx-exceptions", "npm:2.3.0"], + ["spdx-license-ids", "npm:3.0.11"] + ], + "linkType": "HARD", + }] + ]], + ["spdx-license-ids", [ + ["npm:3.0.11", { + "packageLocation": "./.yarn/cache/spdx-license-ids-npm-3.0.11-a8d9a5ff74-1da1acb090.zip/node_modules/spdx-license-ids/", + "packageDependencies": [ + ["spdx-license-ids", "npm:3.0.11"] + ], + "linkType": "HARD", + }] + ]], + ["split", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/split-npm-1.0.1-88871d88a2-12f4554a57.zip/node_modules/split/", + "packageDependencies": [ + ["split", "npm:1.0.1"], + ["through", "npm:2.3.8"] + ], + "linkType": "HARD", + }] + ]], + ["split-ca", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/split-ca-npm-1.0.1-8e5f2e1d22-1e7409938a.zip/node_modules/split-ca/", + "packageDependencies": [ + ["split-ca", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["split2", [ + ["npm:3.2.2", { + "packageLocation": "./.yarn/cache/split2-npm-3.2.2-4ccd21b4f7-8127ddbedd.zip/node_modules/split2/", + "packageDependencies": [ + ["split2", "npm:3.2.2"], + ["readable-stream", "npm:3.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["sprintf-js", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/sprintf-js-npm-1.0.3-73f0a322fa-19d79aec21.zip/node_modules/sprintf-js/", + "packageDependencies": [ + ["sprintf-js", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["ssh2", [ + ["npm:1.5.0", { + "packageLocation": "./.yarn/unplugged/ssh2-npm-1.5.0-8a0e3032ea/node_modules/ssh2/", + "packageDependencies": [ + ["ssh2", "npm:1.5.0"], + ["asn1", "npm:0.2.6"], + ["bcrypt-pbkdf", "npm:1.0.2"], + ["cpu-features", "npm:0.0.2"], + ["nan", "npm:2.15.0"] + ], + "linkType": "HARD", + }] + ]], + ["sshpk", [ + ["npm:1.16.1", { + "packageLocation": "./.yarn/cache/sshpk-npm-1.16.1-feb759e7e0-5e76afd1ce.zip/node_modules/sshpk/", + "packageDependencies": [ + ["sshpk", "npm:1.16.1"], + ["asn1", "npm:0.2.6"], + ["assert-plus", "npm:1.0.0"], + ["bcrypt-pbkdf", "npm:1.0.2"], + ["dashdash", "npm:1.14.1"], + ["ecc-jsbn", "npm:0.1.2"], + ["getpass", "npm:0.1.7"], + ["jsbn", "npm:0.1.1"], + ["safer-buffer", "npm:2.1.2"], + ["tweetnacl", "npm:0.14.5"] + ], + "linkType": "HARD", + }] + ]], + ["ssri", [ + ["npm:8.0.1", { + "packageLocation": "./.yarn/cache/ssri-npm-8.0.1-a369e72ce2-bc447f5af8.zip/node_modules/ssri/", + "packageDependencies": [ + ["ssri", "npm:8.0.1"], + ["minipass", "npm:3.1.6"] + ], + "linkType": "HARD", + }] + ]], + ["stack-trace", [ + ["npm:0.0.10", { + "packageLocation": "./.yarn/cache/stack-trace-npm-0.0.10-9460b173e1-473036ad32.zip/node_modules/stack-trace/", + "packageDependencies": [ + ["stack-trace", "npm:0.0.10"] + ], + "linkType": "HARD", + }] + ]], + ["stack-utils", [ + ["npm:2.0.5", { + "packageLocation": "./.yarn/cache/stack-utils-npm-2.0.5-e0438f409a-76b69da0f5.zip/node_modules/stack-utils/", + "packageDependencies": [ + ["stack-utils", "npm:2.0.5"], + ["escape-string-regexp", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["statuses", [ + ["npm:1.5.0", { + "packageLocation": "./.yarn/cache/statuses-npm-1.5.0-f88f91b2e9-c469b9519d.zip/node_modules/statuses/", + "packageDependencies": [ + ["statuses", "npm:1.5.0"] + ], + "linkType": "HARD", + }] + ]], + ["stealthy-require", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/stealthy-require-npm-1.1.1-0105ec8207-6805b857a9.zip/node_modules/stealthy-require/", + "packageDependencies": [ + ["stealthy-require", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["stream-browserify", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/stream-browserify-npm-2.0.2-145ceec889-8de7bcab55.zip/node_modules/stream-browserify/", + "packageDependencies": [ + ["stream-browserify", "npm:2.0.2"], + ["inherits", "npm:2.0.4"], + ["readable-stream", "npm:2.3.7"] + ], + "linkType": "HARD", + }], + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/stream-browserify-npm-3.0.0-4c0bd97245-4c47ef64d6.zip/node_modules/stream-browserify/", + "packageDependencies": [ + ["stream-browserify", "npm:3.0.0"], + ["inherits", "npm:2.0.4"], + ["readable-stream", "npm:3.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["stream-combiner2", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/stream-combiner2-npm-1.1.1-72d11c75e4-dd32d179fa.zip/node_modules/stream-combiner2/", + "packageDependencies": [ + ["stream-combiner2", "npm:1.1.1"], + ["duplexer2", "npm:0.1.4"], + ["readable-stream", "npm:2.3.7"] + ], + "linkType": "HARD", + }] + ]], + ["stream-http", [ + ["npm:3.2.0", { + "packageLocation": "./.yarn/cache/stream-http-npm-3.2.0-c6d720ac4f-c9b78453ae.zip/node_modules/stream-http/", + "packageDependencies": [ + ["stream-http", "npm:3.2.0"], + ["builtin-status-codes", "npm:3.0.0"], + ["inherits", "npm:2.0.4"], + ["readable-stream", "npm:3.6.0"], + ["xtend", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["stream-splicer", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/stream-splicer-npm-2.0.1-add41315d2-7bb3563961.zip/node_modules/stream-splicer/", + "packageDependencies": [ + ["stream-splicer", "npm:2.0.1"], + ["inherits", "npm:2.0.4"], + ["readable-stream", "npm:2.3.7"] + ], + "linkType": "HARD", + }] + ]], + ["streamroller", [ + ["npm:2.2.4", { + "packageLocation": "./.yarn/cache/streamroller-npm-2.2.4-84aaab4674-83060ded80.zip/node_modules/streamroller/", + "packageDependencies": [ + ["streamroller", "npm:2.2.4"], + ["date-format", "npm:2.1.0"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["fs-extra", "npm:8.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["string-width", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/string-width-npm-1.0.2-01031f9add-5c79439e95.zip/node_modules/string-width/", + "packageDependencies": [ + ["string-width", "npm:1.0.2"], + ["code-point-at", "npm:1.1.0"], + ["is-fullwidth-code-point", "npm:1.0.0"], + ["strip-ansi", "npm:3.0.1"] + ], + "linkType": "HARD", + }], + ["npm:2.1.1", { + "packageLocation": "./.yarn/cache/string-width-npm-2.1.1-0c2c6ae53f-d6173abe08.zip/node_modules/string-width/", + "packageDependencies": [ + ["string-width", "npm:2.1.1"], + ["is-fullwidth-code-point", "npm:2.0.0"], + ["strip-ansi", "npm:4.0.0"] + ], + "linkType": "HARD", + }], + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/string-width-npm-3.1.0-e031bfa4e0-57f7ca73d2.zip/node_modules/string-width/", + "packageDependencies": [ + ["string-width", "npm:3.1.0"], + ["emoji-regex", "npm:7.0.3"], + ["is-fullwidth-code-point", "npm:2.0.0"], + ["strip-ansi", "npm:5.2.0"] + ], + "linkType": "HARD", + }], + ["npm:4.2.3", { + "packageLocation": "./.yarn/cache/string-width-npm-4.2.3-2c27177bae-e52c10dc3f.zip/node_modules/string-width/", + "packageDependencies": [ + ["string-width", "npm:4.2.3"], + ["emoji-regex", "npm:8.0.0"], + ["is-fullwidth-code-point", "npm:3.0.0"], + ["strip-ansi", "npm:6.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["string.prototype.trimend", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/string.prototype.trimend-npm-1.0.4-a656b8fe24-17e5aa45c3.zip/node_modules/string.prototype.trimend/", + "packageDependencies": [ + ["string.prototype.trimend", "npm:1.0.4"], + ["call-bind", "npm:1.0.2"], + ["define-properties", "npm:1.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["string.prototype.trimstart", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/string.prototype.trimstart-npm-1.0.4-b31f5e7c85-3fb06818d3.zip/node_modules/string.prototype.trimstart/", + "packageDependencies": [ + ["string.prototype.trimstart", "npm:1.0.4"], + ["call-bind", "npm:1.0.2"], + ["define-properties", "npm:1.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["string_decoder", [ + ["npm:0.10.31", { + "packageLocation": "./.yarn/cache/string_decoder-npm-0.10.31-851f3f7302-fe00f8e303.zip/node_modules/string_decoder/", + "packageDependencies": [ + ["string_decoder", "npm:0.10.31"] + ], + "linkType": "HARD", + }], + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/string_decoder-npm-1.1.1-e46a6c1353-9ab7e56f9d.zip/node_modules/string_decoder/", + "packageDependencies": [ + ["string_decoder", "npm:1.1.1"], + ["safe-buffer", "npm:5.1.2"] + ], + "linkType": "HARD", + }], + ["npm:1.3.0", { + "packageLocation": "./.yarn/cache/string_decoder-npm-1.3.0-2422117fd0-8417646695.zip/node_modules/string_decoder/", + "packageDependencies": [ + ["string_decoder", "npm:1.3.0"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["strip-ansi", [ + ["npm:3.0.1", { + "packageLocation": "./.yarn/cache/strip-ansi-npm-3.0.1-6aec1365b9-9b974de611.zip/node_modules/strip-ansi/", + "packageDependencies": [ + ["strip-ansi", "npm:3.0.1"], + ["ansi-regex", "npm:2.1.1"] + ], + "linkType": "HARD", + }], + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/strip-ansi-npm-4.0.0-d4de985014-d9186e6c0c.zip/node_modules/strip-ansi/", + "packageDependencies": [ + ["strip-ansi", "npm:4.0.0"], + ["ansi-regex", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.2.0", { + "packageLocation": "./.yarn/cache/strip-ansi-npm-5.2.0-275214c316-bdb5f76ade.zip/node_modules/strip-ansi/", + "packageDependencies": [ + ["strip-ansi", "npm:5.2.0"], + ["ansi-regex", "npm:4.1.0"] + ], + "linkType": "HARD", + }], + ["npm:6.0.1", { + "packageLocation": "./.yarn/cache/strip-ansi-npm-6.0.1-caddc7cb40-f3cd25890a.zip/node_modules/strip-ansi/", + "packageDependencies": [ + ["strip-ansi", "npm:6.0.1"], + ["ansi-regex", "npm:5.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["strip-bom", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/strip-bom-npm-2.0.0-5c4b64ed5a-08efb746bc.zip/node_modules/strip-bom/", + "packageDependencies": [ + ["strip-bom", "npm:2.0.0"], + ["is-utf8", "npm:0.2.1"] + ], + "linkType": "HARD", + }], + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/strip-bom-npm-3.0.0-71e8f81ff9-8d50ff27b7.zip/node_modules/strip-bom/", + "packageDependencies": [ + ["strip-bom", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/strip-bom-npm-4.0.0-97d367a64d-9dbcfbaf50.zip/node_modules/strip-bom/", + "packageDependencies": [ + ["strip-bom", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["strip-bom-buf", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/strip-bom-buf-npm-1.0.0-056a57a073-246665fa1c.zip/node_modules/strip-bom-buf/", + "packageDependencies": [ + ["strip-bom-buf", "npm:1.0.0"], + ["is-utf8", "npm:0.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["strip-bom-stream", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/strip-bom-stream-npm-2.0.0-e1d65f77cc-3e2ff494d9.zip/node_modules/strip-bom-stream/", + "packageDependencies": [ + ["strip-bom-stream", "npm:2.0.0"], + ["first-chunk-stream", "npm:2.0.0"], + ["strip-bom", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["strip-eof", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/strip-eof-npm-1.0.0-d82eaf947c-40bc8ddd7e.zip/node_modules/strip-eof/", + "packageDependencies": [ + ["strip-eof", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["strip-final-newline", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/strip-final-newline-npm-2.0.0-340c4f7c66-69412b5e25.zip/node_modules/strip-final-newline/", + "packageDependencies": [ + ["strip-final-newline", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["strip-indent", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/strip-indent-npm-3.0.0-519e75a28d-18f045d57d.zip/node_modules/strip-indent/", + "packageDependencies": [ + ["strip-indent", "npm:3.0.0"], + ["min-indent", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["strip-json-comments", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/strip-json-comments-npm-2.0.1-e7883b2d04-1074ccb632.zip/node_modules/strip-json-comments/", + "packageDependencies": [ + ["strip-json-comments", "npm:2.0.1"] + ], + "linkType": "HARD", + }], + ["npm:3.1.1", { + "packageLocation": "./.yarn/cache/strip-json-comments-npm-3.1.1-dcb2324823-492f73e272.zip/node_modules/strip-json-comments/", + "packageDependencies": [ + ["strip-json-comments", "npm:3.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["subarg", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/subarg-npm-1.0.0-05f4a18d07-8359df72e9.zip/node_modules/subarg/", + "packageDependencies": [ + ["subarg", "npm:1.0.0"], + ["minimist", "npm:1.2.5"] + ], + "linkType": "HARD", + }] + ]], + ["supports-color", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/supports-color-npm-2.0.0-22c0f0adbc-602538c581.zip/node_modules/supports-color/", + "packageDependencies": [ + ["supports-color", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.5.0", { + "packageLocation": "./.yarn/cache/supports-color-npm-5.5.0-183ac537bc-95f6f4ba5a.zip/node_modules/supports-color/", + "packageDependencies": [ + ["supports-color", "npm:5.5.0"], + ["has-flag", "npm:3.0.0"] + ], + "linkType": "HARD", + }], + ["npm:7.2.0", { + "packageLocation": "./.yarn/cache/supports-color-npm-7.2.0-606bfcf7da-3dda818de0.zip/node_modules/supports-color/", + "packageDependencies": [ + ["supports-color", "npm:7.2.0"], + ["has-flag", "npm:4.0.0"] + ], + "linkType": "HARD", + }], + ["npm:8.1.1", { + "packageLocation": "./.yarn/cache/supports-color-npm-8.1.1-289e937149-c052193a7e.zip/node_modules/supports-color/", + "packageDependencies": [ + ["supports-color", "npm:8.1.1"], + ["has-flag", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["supports-hyperlinks", [ + ["npm:2.2.0", { + "packageLocation": "./.yarn/cache/supports-hyperlinks-npm-2.2.0-9b22a6271b-aef04fb41f.zip/node_modules/supports-hyperlinks/", + "packageDependencies": [ + ["supports-hyperlinks", "npm:2.2.0"], + ["has-flag", "npm:4.0.0"], + ["supports-color", "npm:7.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["supports-preserve-symlinks-flag", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/supports-preserve-symlinks-flag-npm-1.0.0-f17c4d0028-53b1e247e6.zip/node_modules/supports-preserve-symlinks-flag/", + "packageDependencies": [ + ["supports-preserve-symlinks-flag", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["swagger-jsdoc", [ + ["npm:3.7.0", { + "packageLocation": "./.yarn/cache/swagger-jsdoc-npm-3.7.0-483149b581-436e6321f3.zip/node_modules/swagger-jsdoc/", + "packageDependencies": [ + ["swagger-jsdoc", "npm:3.7.0"], + ["commander", "npm:4.0.1"], + ["doctrine", "npm:3.0.0"], + ["glob", "npm:7.1.6"], + ["js-yaml", "npm:3.13.1"], + ["swagger-parser", "npm:8.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["swagger-methods", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/swagger-methods-npm-2.0.2-81541b17dc-1321362be7.zip/node_modules/swagger-methods/", + "packageDependencies": [ + ["swagger-methods", "npm:2.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["swagger-parser", [ + ["npm:8.0.4", { + "packageLocation": "./.yarn/cache/swagger-parser-npm-8.0.4-0123559a75-cd07ac3dbe.zip/node_modules/swagger-parser/", + "packageDependencies": [ + ["swagger-parser", "npm:8.0.4"], + ["call-me-maybe", "npm:1.0.1"], + ["json-schema-ref-parser", "npm:7.1.4"], + ["ono", "npm:6.0.1"], + ["openapi-schemas", "npm:1.0.3"], + ["openapi-types", "npm:1.3.5"], + ["swagger-methods", "npm:2.0.2"], + ["z-schema", "npm:4.2.4"] + ], + "linkType": "HARD", + }] + ]], + ["syntax-error", [ + ["npm:1.4.0", { + "packageLocation": "./.yarn/cache/syntax-error-npm-1.4.0-8721590265-c1c3f048fe.zip/node_modules/syntax-error/", + "packageDependencies": [ + ["syntax-error", "npm:1.4.0"], + ["acorn-node", "npm:1.8.2"] + ], + "linkType": "HARD", + }] + ]], + ["table", [ + ["npm:5.4.6", { + "packageLocation": "./.yarn/cache/table-npm-5.4.6-190b118384-9e35d3efa7.zip/node_modules/table/", + "packageDependencies": [ + ["table", "npm:5.4.6"], + ["ajv", "npm:6.12.6"], + ["lodash", "npm:4.17.21"], + ["slice-ansi", "npm:2.1.0"], + ["string-width", "npm:3.1.0"] + ], + "linkType": "HARD", + }], + ["npm:6.7.3", { + "packageLocation": "./.yarn/cache/table-npm-6.7.3-a96402c315-61d732f511.zip/node_modules/table/", + "packageDependencies": [ + ["table", "npm:6.7.3"], + ["ajv", "npm:8.8.1"], + ["lodash.truncate", "npm:4.4.2"], + ["slice-ansi", "npm:4.0.0"], + ["string-width", "npm:4.2.3"], + ["strip-ansi", "npm:6.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["taketalk", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/taketalk-npm-1.0.0-2fc66802cb-b9a6ae2d6e.zip/node_modules/taketalk/", + "packageDependencies": [ + ["taketalk", "npm:1.0.0"], + ["get-stdin", "npm:4.0.1"], + ["minimist", "npm:1.2.5"] + ], + "linkType": "HARD", + }] + ]], + ["tapable", [ + ["npm:1.1.3", { + "packageLocation": "./.yarn/cache/tapable-npm-1.1.3-f1c2843426-53ff4e7c39.zip/node_modules/tapable/", + "packageDependencies": [ + ["tapable", "npm:1.1.3"] + ], + "linkType": "HARD", + }], + ["npm:2.2.1", { + "packageLocation": "./.yarn/cache/tapable-npm-2.2.1-8cf5ff3039-3b7a1b4d86.zip/node_modules/tapable/", + "packageDependencies": [ + ["tapable", "npm:2.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["tar", [ + ["npm:6.1.11", { + "packageLocation": "./.yarn/cache/tar-npm-6.1.11-e6ac3cba9c-a04c07bb9e.zip/node_modules/tar/", + "packageDependencies": [ + ["tar", "npm:6.1.11"], + ["chownr", "npm:2.0.0"], + ["fs-minipass", "npm:2.1.0"], + ["minipass", "npm:3.1.6"], + ["minizlib", "npm:2.1.2"], + ["mkdirp", "npm:1.0.4"], + ["yallist", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["tar-fs", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/tar-fs-npm-2.0.1-0734c93785-26cd297ed2.zip/node_modules/tar-fs/", + "packageDependencies": [ + ["tar-fs", "npm:2.0.1"], + ["chownr", "npm:1.1.4"], + ["mkdirp-classic", "npm:0.5.3"], + ["pump", "npm:3.0.0"], + ["tar-stream", "npm:2.2.0"] + ], + "linkType": "HARD", + }], + ["npm:2.1.1", { + "packageLocation": "./.yarn/cache/tar-fs-npm-2.1.1-e374d3b7a2-f5b9a70059.zip/node_modules/tar-fs/", + "packageDependencies": [ + ["tar-fs", "npm:2.1.1"], + ["chownr", "npm:1.1.4"], + ["mkdirp-classic", "npm:0.5.3"], + ["pump", "npm:3.0.0"], + ["tar-stream", "npm:2.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["tar-stream", [ + ["npm:2.2.0", { + "packageLocation": "./.yarn/cache/tar-stream-npm-2.2.0-884c79b510-699831a8b9.zip/node_modules/tar-stream/", + "packageDependencies": [ + ["tar-stream", "npm:2.2.0"], + ["bl", "npm:4.1.0"], + ["end-of-stream", "npm:1.4.4"], + ["fs-constants", "npm:1.0.0"], + ["inherits", "npm:2.0.4"], + ["readable-stream", "npm:3.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["temp-dir", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/temp-dir-npm-2.0.0-e8af180805-cc4f0404bf.zip/node_modules/temp-dir/", + "packageDependencies": [ + ["temp-dir", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["tempfile", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/tempfile-npm-3.0.0-fcac8b1ecd-ebf07b7e58.zip/node_modules/tempfile/", + "packageDependencies": [ + ["tempfile", "npm:3.0.0"], + ["temp-dir", "npm:2.0.0"], + ["uuid", "npm:3.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["terser", [ + ["npm:5.10.0", { + "packageLocation": "./.yarn/cache/terser-npm-5.10.0-1690d2acb8-1080faeb6d.zip/node_modules/terser/", + "packageDependencies": [ + ["terser", "npm:5.10.0"] + ], + "linkType": "SOFT", + }], + ["virtual:f3cc17b892cb058884ff16eb3f1371753a8294308b1d178fc6569cbc5c9b1374e19bfec938007a17ee2385a0876bcb15676e4dca7149c249b9777c70e29d6324#npm:5.10.0", { + "packageLocation": "./.yarn/__virtual__/terser-virtual-b8983f505c/0/cache/terser-npm-5.10.0-1690d2acb8-1080faeb6d.zip/node_modules/terser/", + "packageDependencies": [ + ["terser", "virtual:f3cc17b892cb058884ff16eb3f1371753a8294308b1d178fc6569cbc5c9b1374e19bfec938007a17ee2385a0876bcb15676e4dca7149c249b9777c70e29d6324#npm:5.10.0"], + ["@types/acorn", null], + ["acorn", null], + ["commander", "npm:2.20.3"], + ["source-map", "npm:0.7.3"], + ["source-map-support", "npm:0.5.21"] + ], + "packagePeers": [ + "@types/acorn", + "acorn" + ], + "linkType": "HARD", + }] + ]], + ["terser-webpack-plugin", [ + ["npm:5.3.3", { + "packageLocation": "./.yarn/cache/terser-webpack-plugin-npm-5.3.3-659a8e4514-4b8d508d8a.zip/node_modules/terser-webpack-plugin/", + "packageDependencies": [ + ["terser-webpack-plugin", "npm:5.3.3"] + ], + "linkType": "SOFT", + }], + ["virtual:1d78bb01f21914b3c919ffb707b6b169127e3e506be79a82799d0632e5da000051686a18675813b8a2e4b9d97f1d372c927db824a65bfcada7f79e5b0273240f#npm:5.3.3", { + "packageLocation": "./.yarn/__virtual__/terser-webpack-plugin-virtual-8250027957/0/cache/terser-webpack-plugin-npm-5.3.3-659a8e4514-4b8d508d8a.zip/node_modules/terser-webpack-plugin/", + "packageDependencies": [ + ["terser-webpack-plugin", "virtual:1d78bb01f21914b3c919ffb707b6b169127e3e506be79a82799d0632e5da000051686a18675813b8a2e4b9d97f1d372c927db824a65bfcada7f79e5b0273240f#npm:5.3.3"], + ["@jridgewell/trace-mapping", "npm:0.3.14"], + ["@swc/core", null], + ["@types/esbuild", null], + ["@types/swc__core", null], + ["@types/uglify-js", null], + ["@types/webpack", null], + ["esbuild", null], + ["jest-worker", "npm:27.5.1"], + ["schema-utils", "npm:3.1.1"], + ["serialize-javascript", "npm:6.0.0"], + ["terser", "virtual:f3cc17b892cb058884ff16eb3f1371753a8294308b1d178fc6569cbc5c9b1374e19bfec938007a17ee2385a0876bcb15676e4dca7149c249b9777c70e29d6324#npm:5.10.0"], + ["uglify-js", null], + ["webpack", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:5.64.1"] + ], + "packagePeers": [ + "@swc/core", + "@types/esbuild", + "@types/swc__core", + "@types/uglify-js", + "@types/webpack", + "esbuild", + "uglify-js", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:35ef488be3738fb73696e8919272b5c35cfafebd6b82f716b7cf402514ee937bd8da0f25ae67baa1d8aeef0e69c557930350fe0dec6c03e135d60acb24c93c14#npm:5.3.3", { + "packageLocation": "./.yarn/__virtual__/terser-webpack-plugin-virtual-48f02661da/0/cache/terser-webpack-plugin-npm-5.3.3-659a8e4514-4b8d508d8a.zip/node_modules/terser-webpack-plugin/", + "packageDependencies": [ + ["terser-webpack-plugin", "virtual:35ef488be3738fb73696e8919272b5c35cfafebd6b82f716b7cf402514ee937bd8da0f25ae67baa1d8aeef0e69c557930350fe0dec6c03e135d60acb24c93c14#npm:5.3.3"], + ["@jridgewell/trace-mapping", "npm:0.3.14"], + ["@swc/core", null], + ["@types/esbuild", null], + ["@types/swc__core", null], + ["@types/uglify-js", null], + ["@types/webpack", null], + ["esbuild", null], + ["jest-worker", "npm:27.5.1"], + ["schema-utils", "npm:3.1.1"], + ["serialize-javascript", "npm:6.0.0"], + ["terser", "virtual:f3cc17b892cb058884ff16eb3f1371753a8294308b1d178fc6569cbc5c9b1374e19bfec938007a17ee2385a0876bcb15676e4dca7149c249b9777c70e29d6324#npm:5.10.0"], + ["uglify-js", null], + ["webpack", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:5.64.1"] + ], + "packagePeers": [ + "@swc/core", + "@types/esbuild", + "@types/swc__core", + "@types/uglify-js", + "@types/webpack", + "esbuild", + "uglify-js", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:4953b975b7bd5a519c3d3acbf14e67153262cfb500bb1b8ce3d6f78e6b4fc3e422ef0018b19f059be78f5a124f310f4b9d100d3e3076ea1db98ff3d39975a669#npm:5.3.3", { + "packageLocation": "./.yarn/__virtual__/terser-webpack-plugin-virtual-6184b6aeb4/0/cache/terser-webpack-plugin-npm-5.3.3-659a8e4514-4b8d508d8a.zip/node_modules/terser-webpack-plugin/", + "packageDependencies": [ + ["terser-webpack-plugin", "virtual:4953b975b7bd5a519c3d3acbf14e67153262cfb500bb1b8ce3d6f78e6b4fc3e422ef0018b19f059be78f5a124f310f4b9d100d3e3076ea1db98ff3d39975a669#npm:5.3.3"], + ["@jridgewell/trace-mapping", "npm:0.3.14"], + ["@swc/core", null], + ["@types/esbuild", null], + ["@types/swc__core", null], + ["@types/uglify-js", null], + ["@types/webpack", null], + ["esbuild", null], + ["jest-worker", "npm:27.5.1"], + ["schema-utils", "npm:3.1.1"], + ["serialize-javascript", "npm:6.0.0"], + ["terser", "virtual:f3cc17b892cb058884ff16eb3f1371753a8294308b1d178fc6569cbc5c9b1374e19bfec938007a17ee2385a0876bcb15676e4dca7149c249b9777c70e29d6324#npm:5.10.0"], + ["uglify-js", null], + ["webpack", "virtual:01938c2be4835443e5a304e2b117c575220e96e8b7cedeb0f48d79264590b4c4babc6d1fea6367f522b1ca0149d795b42f2ab89c34a6ffe3c20f0a8cbb8b4453#npm:5.64.1"] + ], + "packagePeers": [ + "@swc/core", + "@types/esbuild", + "@types/swc__core", + "@types/uglify-js", + "@types/webpack", + "esbuild", + "uglify-js", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:74c6cfbee4804d578abc9f87850914089f1afe762b6ef9dde76ecf912446a2b7772b58b2a3d070d6a0e21b0f126bfcdfa013123d743c15f32eee4a9cf9f4d551#npm:5.3.3", { + "packageLocation": "./.yarn/__virtual__/terser-webpack-plugin-virtual-f3cc17b892/0/cache/terser-webpack-plugin-npm-5.3.3-659a8e4514-4b8d508d8a.zip/node_modules/terser-webpack-plugin/", + "packageDependencies": [ + ["terser-webpack-plugin", "virtual:74c6cfbee4804d578abc9f87850914089f1afe762b6ef9dde76ecf912446a2b7772b58b2a3d070d6a0e21b0f126bfcdfa013123d743c15f32eee4a9cf9f4d551#npm:5.3.3"], + ["@jridgewell/trace-mapping", "npm:0.3.14"], + ["@swc/core", null], + ["@types/esbuild", null], + ["@types/swc__core", null], + ["@types/uglify-js", null], + ["@types/webpack", null], + ["esbuild", null], + ["jest-worker", "npm:27.5.1"], + ["schema-utils", "npm:3.1.1"], + ["serialize-javascript", "npm:6.0.0"], + ["terser", "virtual:f3cc17b892cb058884ff16eb3f1371753a8294308b1d178fc6569cbc5c9b1374e19bfec938007a17ee2385a0876bcb15676e4dca7149c249b9777c70e29d6324#npm:5.10.0"], + ["uglify-js", null], + ["webpack", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:5.64.1"] + ], + "packagePeers": [ + "@swc/core", + "@types/esbuild", + "@types/swc__core", + "@types/uglify-js", + "@types/webpack", + "esbuild", + "uglify-js", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.3.3", { + "packageLocation": "./.yarn/__virtual__/terser-webpack-plugin-virtual-65e4fce360/0/cache/terser-webpack-plugin-npm-5.3.3-659a8e4514-4b8d508d8a.zip/node_modules/terser-webpack-plugin/", + "packageDependencies": [ + ["terser-webpack-plugin", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.3.3"], + ["@jridgewell/trace-mapping", "npm:0.3.14"], + ["@swc/core", null], + ["@types/esbuild", null], + ["@types/swc__core", null], + ["@types/uglify-js", null], + ["@types/webpack", null], + ["esbuild", null], + ["jest-worker", "npm:27.5.1"], + ["schema-utils", "npm:3.1.1"], + ["serialize-javascript", "npm:6.0.0"], + ["terser", "virtual:f3cc17b892cb058884ff16eb3f1371753a8294308b1d178fc6569cbc5c9b1374e19bfec938007a17ee2385a0876bcb15676e4dca7149c249b9777c70e29d6324#npm:5.10.0"], + ["uglify-js", null], + ["webpack", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.64.1"] + ], + "packagePeers": [ + "@swc/core", + "@types/esbuild", + "@types/swc__core", + "@types/uglify-js", + "@types/webpack", + "esbuild", + "uglify-js", + "webpack" + ], + "linkType": "HARD", + }] + ]], + ["test-exclude", [ + ["npm:6.0.0", { + "packageLocation": "./.yarn/cache/test-exclude-npm-6.0.0-3fb03d69df-3b34a3d771.zip/node_modules/test-exclude/", + "packageDependencies": [ + ["test-exclude", "npm:6.0.0"], + ["@istanbuljs/schema", "npm:0.1.3"], + ["glob", "npm:7.2.0"], + ["minimatch", "npm:3.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["text-extensions", [ + ["npm:1.9.0", { + "packageLocation": "./.yarn/cache/text-extensions-npm-1.9.0-87655d768f-56a9962c1b.zip/node_modules/text-extensions/", + "packageDependencies": [ + ["text-extensions", "npm:1.9.0"] + ], + "linkType": "HARD", + }] + ]], + ["text-hex", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/text-hex-npm-1.0.0-22389e4d56-1138f68adc.zip/node_modules/text-hex/", + "packageDependencies": [ + ["text-hex", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["text-table", [ + ["npm:0.2.0", { + "packageLocation": "./.yarn/cache/text-table-npm-0.2.0-d92a778b59-b6937a38c8.zip/node_modules/text-table/", + "packageDependencies": [ + ["text-table", "npm:0.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["textextensions", [ + ["npm:5.14.0", { + "packageLocation": "./.yarn/cache/textextensions-npm-5.14.0-5251a1bdcc-1f610ccf2a.zip/node_modules/textextensions/", + "packageDependencies": [ + ["textextensions", "npm:5.14.0"] + ], + "linkType": "HARD", + }] + ]], + ["through", [ + ["npm:2.3.8", { + "packageLocation": "./.yarn/cache/through-npm-2.3.8-df5f72a16e-a38c3e0598.zip/node_modules/through/", + "packageDependencies": [ + ["through", "npm:2.3.8"] + ], + "linkType": "HARD", + }] + ]], + ["through2", [ + ["npm:2.0.5", { + "packageLocation": "./.yarn/cache/through2-npm-2.0.5-77d90f13cd-beb0f338aa.zip/node_modules/through2/", + "packageDependencies": [ + ["through2", "npm:2.0.5"], + ["readable-stream", "npm:2.3.7"], + ["xtend", "npm:4.0.2"] + ], + "linkType": "HARD", + }], + ["npm:3.0.2", { + "packageLocation": "./.yarn/cache/through2-npm-3.0.2-403f837012-47c9586c73.zip/node_modules/through2/", + "packageDependencies": [ + ["through2", "npm:3.0.2"], + ["inherits", "npm:2.0.4"], + ["readable-stream", "npm:3.6.0"] + ], + "linkType": "HARD", + }], + ["npm:4.0.2", { + "packageLocation": "./.yarn/cache/through2-npm-4.0.2-da7b2da443-ac7430bd54.zip/node_modules/through2/", + "packageDependencies": [ + ["through2", "npm:4.0.2"], + ["readable-stream", "npm:3.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["timers-browserify", [ + ["npm:1.4.2", { + "packageLocation": "./.yarn/cache/timers-browserify-npm-1.4.2-40215963ae-b7437e2286.zip/node_modules/timers-browserify/", + "packageDependencies": [ + ["timers-browserify", "npm:1.4.2"], + ["process", "npm:0.11.10"] + ], + "linkType": "HARD", + }] + ]], + ["tiny-emitter", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/tiny-emitter-npm-2.1.0-2a4d94f487-fbcfb51457.zip/node_modules/tiny-emitter/", + "packageDependencies": [ + ["tiny-emitter", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["tls", [ + ["npm:0.0.1", { + "packageLocation": "./.yarn/cache/tls-npm-0.0.1-d44eeeb72e-b0205b0efb.zip/node_modules/tls/", + "packageDependencies": [ + ["tls", "npm:0.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["tmp", [ + ["npm:0.0.33", { + "packageLocation": "./.yarn/cache/tmp-npm-0.0.33-bcbf65df2a-902d7aceb7.zip/node_modules/tmp/", + "packageDependencies": [ + ["tmp", "npm:0.0.33"], + ["os-tmpdir", "npm:1.0.2"] + ], + "linkType": "HARD", + }], + ["npm:0.1.0", { + "packageLocation": "./.yarn/cache/tmp-npm-0.1.0-fa18ef19c4-6bab8431de.zip/node_modules/tmp/", + "packageDependencies": [ + ["tmp", "npm:0.1.0"], + ["rimraf", "npm:2.7.1"] + ], + "linkType": "HARD", + }], + ["npm:0.2.1", { + "packageLocation": "./.yarn/cache/tmp-npm-0.2.1-a9c8d9c0ca-8b12146541.zip/node_modules/tmp/", + "packageDependencies": [ + ["tmp", "npm:0.2.1"], + ["rimraf", "npm:3.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["to-fast-properties", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/to-fast-properties-npm-2.0.0-0dc60cc481-be2de62fe5.zip/node_modules/to-fast-properties/", + "packageDependencies": [ + ["to-fast-properties", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["to-readable-stream", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/to-readable-stream-npm-1.0.0-4fa4da8130-2bd7778490.zip/node_modules/to-readable-stream/", + "packageDependencies": [ + ["to-readable-stream", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["to-regex-range", [ + ["npm:5.0.1", { + "packageLocation": "./.yarn/cache/to-regex-range-npm-5.0.1-f1e8263b00-f76fa01b3d.zip/node_modules/to-regex-range/", + "packageDependencies": [ + ["to-regex-range", "npm:5.0.1"], + ["is-number", "npm:7.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["toidentifier", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/toidentifier-npm-1.0.0-5dad252f90-199e6bfca1.zip/node_modules/toidentifier/", + "packageDependencies": [ + ["toidentifier", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["touch", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/touch-npm-3.1.0-e2eacebbda-e0be589cb5.zip/node_modules/touch/", + "packageDependencies": [ + ["touch", "npm:3.1.0"], + ["nopt", "npm:1.0.10"] + ], + "linkType": "HARD", + }] + ]], + ["tough-cookie", [ + ["npm:2.5.0", { + "packageLocation": "./.yarn/cache/tough-cookie-npm-2.5.0-79a2fe43fe-16a8cd0902.zip/node_modules/tough-cookie/", + "packageDependencies": [ + ["tough-cookie", "npm:2.5.0"], + ["psl", "npm:1.8.0"], + ["punycode", "npm:2.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["tr46", [ + ["npm:0.0.3", { + "packageLocation": "./.yarn/cache/tr46-npm-0.0.3-de53018915-726321c5ea.zip/node_modules/tr46/", + "packageDependencies": [ + ["tr46", "npm:0.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["tree-kill", [ + ["npm:1.2.2", { + "packageLocation": "./.yarn/cache/tree-kill-npm-1.2.2-3da0e5a759-49117f5f41.zip/node_modules/tree-kill/", + "packageDependencies": [ + ["tree-kill", "npm:1.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["treeverse", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/treeverse-npm-1.0.4-dc3cd6f6c7-712640acd8.zip/node_modules/treeverse/", + "packageDependencies": [ + ["treeverse", "npm:1.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["trim-newlines", [ + ["npm:3.0.1", { + "packageLocation": "./.yarn/cache/trim-newlines-npm-3.0.1-22f1f216de-b530f3fadf.zip/node_modules/trim-newlines/", + "packageDependencies": [ + ["trim-newlines", "npm:3.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["triple-beam", [ + ["npm:1.3.0", { + "packageLocation": "./.yarn/cache/triple-beam-npm-1.3.0-eda4e2a46c-7d7b77d862.zip/node_modules/triple-beam/", + "packageDependencies": [ + ["triple-beam", "npm:1.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["ts-loader", [ + ["npm:8.3.0", { + "packageLocation": "./.yarn/cache/ts-loader-npm-8.3.0-2a35793883-93dd15b553.zip/node_modules/ts-loader/", + "packageDependencies": [ + ["ts-loader", "npm:8.3.0"] + ], + "linkType": "SOFT", + }], + ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:8.3.0", { + "packageLocation": "./.yarn/__virtual__/ts-loader-virtual-73c9377ec3/0/cache/ts-loader-npm-8.3.0-2a35793883-93dd15b553.zip/node_modules/ts-loader/", + "packageDependencies": [ + ["ts-loader", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:8.3.0"], + ["@types/typescript", null], + ["@types/webpack", null], + ["chalk", "npm:4.1.2"], + ["enhanced-resolve", "npm:4.5.0"], + ["loader-utils", "npm:2.0.2"], + ["micromatch", "npm:4.0.4"], + ["semver", "npm:7.3.5"], + ["typescript", "patch:typescript@npm%3A3.9.10#~builtin::version=3.9.10&hash=ddd1e8"], + ["webpack", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.64.1"] + ], + "packagePeers": [ + "@types/typescript", + "@types/webpack", + "typescript", + "webpack" + ], + "linkType": "HARD", + }] + ]], + ["ts-mocha", [ + ["npm:8.0.0", { + "packageLocation": "./.yarn/cache/ts-mocha-npm-8.0.0-958ec73bec-66062e82f9.zip/node_modules/ts-mocha/", + "packageDependencies": [ + ["ts-mocha", "npm:8.0.0"] + ], + "linkType": "SOFT", + }], + ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:8.0.0", { + "packageLocation": "./.yarn/__virtual__/ts-mocha-virtual-d58836497b/0/cache/ts-mocha-npm-8.0.0-958ec73bec-66062e82f9.zip/node_modules/ts-mocha/", + "packageDependencies": [ + ["ts-mocha", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:8.0.0"], + ["@types/mocha", "npm:8.2.3"], + ["mocha", "npm:9.1.3"], + ["ts-node", "npm:7.0.1"], + ["tsconfig-paths", "npm:3.12.0"] + ], + "packagePeers": [ + "@types/mocha", + "mocha" + ], + "linkType": "HARD", + }] + ]], + ["ts-mock-imports", [ + ["npm:1.3.8", { + "packageLocation": "./.yarn/cache/ts-mock-imports-npm-1.3.8-ce172e5189-1600946484.zip/node_modules/ts-mock-imports/", + "packageDependencies": [ + ["ts-mock-imports", "npm:1.3.8"] + ], + "linkType": "SOFT", + }], + ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:1.3.8", { + "packageLocation": "./.yarn/__virtual__/ts-mock-imports-virtual-ed0dfa7cb7/0/cache/ts-mock-imports-npm-1.3.8-ce172e5189-1600946484.zip/node_modules/ts-mock-imports/", + "packageDependencies": [ + ["ts-mock-imports", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:1.3.8"], + ["@types/sinon", "npm:9.0.11"], + ["@types/typescript", null], + ["sinon", "npm:11.1.2"], + ["typescript", "patch:typescript@npm%3A3.9.10#~builtin::version=3.9.10&hash=ddd1e8"] + ], + "packagePeers": [ + "@types/sinon", + "@types/typescript", + "sinon", + "typescript" + ], + "linkType": "HARD", + }] + ]], + ["ts-node", [ + ["npm:10.4.0", { + "packageLocation": "./.yarn/cache/ts-node-npm-10.4.0-04cb6e2279-3933ac0a93.zip/node_modules/ts-node/", + "packageDependencies": [ + ["ts-node", "npm:10.4.0"] + ], + "linkType": "SOFT", + }], + ["npm:7.0.1", { + "packageLocation": "./.yarn/cache/ts-node-npm-7.0.1-dfa4b9e69b-07ed6ea180.zip/node_modules/ts-node/", + "packageDependencies": [ + ["ts-node", "npm:7.0.1"], + ["arrify", "npm:1.0.1"], + ["buffer-from", "npm:1.1.2"], + ["diff", "npm:3.5.0"], + ["make-error", "npm:1.3.6"], + ["minimist", "npm:1.2.5"], + ["mkdirp", "npm:0.5.5"], + ["source-map-support", "npm:0.5.21"], + ["typescript", "patch:typescript@npm%3A3.9.10#~builtin::version=3.9.10&hash=ddd1e8"], + ["yn", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:10.4.0", { + "packageLocation": "./.yarn/__virtual__/ts-node-virtual-065e96423f/0/cache/ts-node-npm-10.4.0-04cb6e2279-3933ac0a93.zip/node_modules/ts-node/", + "packageDependencies": [ + ["ts-node", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:10.4.0"], + ["@cspotcode/source-map-support", "npm:0.7.0"], + ["@swc/core", null], + ["@swc/wasm", null], + ["@tsconfig/node10", "npm:1.0.8"], + ["@tsconfig/node12", "npm:1.0.9"], + ["@tsconfig/node14", "npm:1.0.1"], + ["@tsconfig/node16", "npm:1.0.2"], + ["@types/node", "npm:14.17.34"], + ["@types/swc__core", null], + ["@types/swc__wasm", null], + ["@types/typescript", null], + ["acorn", "npm:8.6.0"], + ["acorn-walk", "npm:8.2.0"], + ["arg", "npm:4.1.3"], + ["create-require", "npm:1.1.1"], + ["diff", "npm:4.0.2"], + ["make-error", "npm:1.3.6"], + ["typescript", "patch:typescript@npm%3A3.9.10#~builtin::version=3.9.10&hash=ddd1e8"], + ["yn", "npm:3.1.1"] + ], + "packagePeers": [ + "@swc/core", + "@swc/wasm", + "@types/node", + "@types/swc__core", + "@types/swc__wasm", + "@types/typescript", + "typescript" + ], + "linkType": "HARD", + }] + ]], + ["tsconfig-paths", [ + ["npm:3.12.0", { + "packageLocation": "./.yarn/cache/tsconfig-paths-npm-3.12.0-b78aadfb3f-4999ec6cd1.zip/node_modules/tsconfig-paths/", + "packageDependencies": [ + ["tsconfig-paths", "npm:3.12.0"], + ["@types/json5", "npm:0.0.29"], + ["json5", "npm:1.0.1"], + ["minimist", "npm:1.2.5"], + ["strip-bom", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["tslib", [ + ["npm:1.14.1", { + "packageLocation": "./.yarn/cache/tslib-npm-1.14.1-102499115e-dbe628ef87.zip/node_modules/tslib/", + "packageDependencies": [ + ["tslib", "npm:1.14.1"] + ], + "linkType": "HARD", + }], + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/tslib-npm-2.1.0-81c9ac9b82-aa189c8179.zip/node_modules/tslib/", + "packageDependencies": [ + ["tslib", "npm:2.1.0"] + ], + "linkType": "HARD", + }], + ["npm:2.3.1", { + "packageLocation": "./.yarn/cache/tslib-npm-2.3.1-0e21e18015-de17a98d46.zip/node_modules/tslib/", + "packageDependencies": [ + ["tslib", "npm:2.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["tty-browserify", [ + ["npm:0.0.1", { + "packageLocation": "./.yarn/cache/tty-browserify-npm-0.0.1-d2494d5a73-93b745d43f.zip/node_modules/tty-browserify/", + "packageDependencies": [ + ["tty-browserify", "npm:0.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["tunnel-agent", [ + ["npm:0.6.0", { + "packageLocation": "./.yarn/cache/tunnel-agent-npm-0.6.0-64345ab7eb-05f6510358.zip/node_modules/tunnel-agent/", + "packageDependencies": [ + ["tunnel-agent", "npm:0.6.0"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["tweetnacl", [ + ["npm:0.14.5", { + "packageLocation": "./.yarn/cache/tweetnacl-npm-0.14.5-a3f766c0d1-6061daba17.zip/node_modules/tweetnacl/", + "packageDependencies": [ + ["tweetnacl", "npm:0.14.5"] + ], + "linkType": "HARD", + }] + ]], + ["type-check", [ + ["npm:0.3.2", { + "packageLocation": "./.yarn/cache/type-check-npm-0.3.2-a4a38bb0b6-dd3b149564.zip/node_modules/type-check/", + "packageDependencies": [ + ["type-check", "npm:0.3.2"], + ["prelude-ls", "npm:1.1.2"] + ], + "linkType": "HARD", + }], + ["npm:0.4.0", { + "packageLocation": "./.yarn/cache/type-check-npm-0.4.0-60565800ce-ec688ebfc9.zip/node_modules/type-check/", + "packageDependencies": [ + ["type-check", "npm:0.4.0"], + ["prelude-ls", "npm:1.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["type-detect", [ + ["npm:4.0.8", { + "packageLocation": "./.yarn/cache/type-detect-npm-4.0.8-8d8127b901-62b5628bff.zip/node_modules/type-detect/", + "packageDependencies": [ + ["type-detect", "npm:4.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["type-fest", [ + ["npm:0.18.1", { + "packageLocation": "./.yarn/cache/type-fest-npm-0.18.1-47b079775d-e96dcee18a.zip/node_modules/type-fest/", + "packageDependencies": [ + ["type-fest", "npm:0.18.1"] + ], + "linkType": "HARD", + }], + ["npm:0.20.2", { + "packageLocation": "./.yarn/cache/type-fest-npm-0.20.2-b36432617f-4fb3272df2.zip/node_modules/type-fest/", + "packageDependencies": [ + ["type-fest", "npm:0.20.2"] + ], + "linkType": "HARD", + }], + ["npm:0.21.3", { + "packageLocation": "./.yarn/cache/type-fest-npm-0.21.3-5ff2a9c6fd-e6b32a3b38.zip/node_modules/type-fest/", + "packageDependencies": [ + ["type-fest", "npm:0.21.3"] + ], + "linkType": "HARD", + }], + ["npm:0.6.0", { + "packageLocation": "./.yarn/cache/type-fest-npm-0.6.0-76b229965b-b2188e6e4b.zip/node_modules/type-fest/", + "packageDependencies": [ + ["type-fest", "npm:0.6.0"] + ], + "linkType": "HARD", + }], + ["npm:0.8.1", { + "packageLocation": "./.yarn/cache/type-fest-npm-0.8.1-351ad028fe-d61c4b2eba.zip/node_modules/type-fest/", + "packageDependencies": [ + ["type-fest", "npm:0.8.1"] + ], + "linkType": "HARD", + }] + ]], + ["type-is", [ + ["npm:1.6.18", { + "packageLocation": "./.yarn/cache/type-is-npm-1.6.18-6dee4d4961-2c8e47675d.zip/node_modules/type-is/", + "packageDependencies": [ + ["type-is", "npm:1.6.18"], + ["media-typer", "npm:0.3.0"], + ["mime-types", "npm:2.1.34"] + ], + "linkType": "HARD", + }] + ]], + ["typed-function", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/typed-function-npm-2.1.0-de442ec721-168c2c8f76.zip/node_modules/typed-function/", + "packageDependencies": [ + ["typed-function", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["typedarray", [ + ["npm:0.0.6", { + "packageLocation": "./.yarn/cache/typedarray-npm-0.0.6-37638b2241-33b39f3d0e.zip/node_modules/typedarray/", + "packageDependencies": [ + ["typedarray", "npm:0.0.6"] + ], + "linkType": "HARD", + }] + ]], + ["typedarray-to-buffer", [ + ["npm:3.1.5", { + "packageLocation": "./.yarn/cache/typedarray-to-buffer-npm-3.1.5-aadc11995e-99c11aaa8f.zip/node_modules/typedarray-to-buffer/", + "packageDependencies": [ + ["typedarray-to-buffer", "npm:3.1.5"], + ["is-typedarray", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["typescript", [ + ["patch:typescript@npm%3A3.9.10#~builtin::version=3.9.10&hash=ddd1e8", { + "packageLocation": "./.yarn/cache/typescript-patch-e9c475da82-dc7141ab55.zip/node_modules/typescript/", + "packageDependencies": [ + ["typescript", "patch:typescript@npm%3A3.9.10#~builtin::version=3.9.10&hash=ddd1e8"] + ], + "linkType": "HARD", + }] + ]], + ["ua-parser-js", [ + ["npm:0.7.31", { + "packageLocation": "./.yarn/cache/ua-parser-js-npm-0.7.31-aeb4c9aae9-e2f8324a83.zip/node_modules/ua-parser-js/", + "packageDependencies": [ + ["ua-parser-js", "npm:0.7.31"] + ], + "linkType": "HARD", + }] + ]], + ["uglify-js", [ + ["npm:3.14.4", { + "packageLocation": "./.yarn/cache/uglify-js-npm-3.14.4-690963fdb4-13217db521.zip/node_modules/uglify-js/", + "packageDependencies": [ + ["uglify-js", "npm:3.14.4"] + ], + "linkType": "HARD", + }] + ]], + ["ultra-runner", [ + ["npm:3.10.5", { + "packageLocation": "./.yarn/cache/ultra-runner-npm-3.10.5-9f810878b0-4aed834863.zip/node_modules/ultra-runner/", + "packageDependencies": [ + ["ultra-runner", "npm:3.10.5"], + ["ansi-split", "npm:1.0.1"], + ["chalk", "npm:4.1.2"], + ["cross-spawn", "npm:7.0.3"], + ["fast-glob", "npm:3.2.11"], + ["globrex", "npm:0.1.2"], + ["ignore", "npm:5.2.0"], + ["json5", "npm:2.2.0"], + ["micro-memoize", "npm:4.0.9"], + ["npm-run-path", "npm:4.0.1"], + ["pid-cwd", "npm:1.2.0"], + ["ps-list", "npm:7.2.0"], + ["shellwords-ts", "npm:3.0.0"], + ["string-width", "npm:4.2.3"], + ["tslib", "npm:2.1.0"], + ["type-fest", "npm:0.21.3"], + ["wrap-ansi", "npm:7.0.0"], + ["yamljs", "npm:0.3.0"], + ["yargs", "npm:16.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["umd", [ + ["npm:3.0.3", { + "packageLocation": "./.yarn/cache/umd-npm-3.0.3-637d100527-264302acab.zip/node_modules/umd/", + "packageDependencies": [ + ["umd", "npm:3.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["unbox-primitive", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/unbox-primitive-npm-1.0.1-50b9fde246-89d950e18f.zip/node_modules/unbox-primitive/", + "packageDependencies": [ + ["unbox-primitive", "npm:1.0.1"], + ["function-bind", "npm:1.1.1"], + ["has-bigints", "npm:1.0.1"], + ["has-symbols", "npm:1.0.2"], + ["which-boxed-primitive", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["undeclared-identifiers", [ + ["npm:1.1.3", { + "packageLocation": "./.yarn/cache/undeclared-identifiers-npm-1.1.3-f4b85bcf76-e1f2a18d7b.zip/node_modules/undeclared-identifiers/", + "packageDependencies": [ + ["undeclared-identifiers", "npm:1.1.3"], + ["acorn-node", "npm:1.8.2"], + ["dash-ast", "npm:1.0.0"], + ["get-assigned-identifiers", "npm:1.2.0"], + ["simple-concat", "npm:1.0.1"], + ["xtend", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["undefsafe", [ + ["npm:2.0.5", { + "packageLocation": "./.yarn/cache/undefsafe-npm-2.0.5-8c3bbf9354-f42ab3b577.zip/node_modules/undefsafe/", + "packageDependencies": [ + ["undefsafe", "npm:2.0.5"] + ], + "linkType": "HARD", + }] + ]], + ["unicode-canonical-property-names-ecmascript", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/unicode-canonical-property-names-ecmascript-npm-2.0.0-d2d8554a14-39be078afd.zip/node_modules/unicode-canonical-property-names-ecmascript/", + "packageDependencies": [ + ["unicode-canonical-property-names-ecmascript", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["unicode-match-property-ecmascript", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/unicode-match-property-ecmascript-npm-2.0.0-97a00fd52c-1f34a7434a.zip/node_modules/unicode-match-property-ecmascript/", + "packageDependencies": [ + ["unicode-match-property-ecmascript", "npm:2.0.0"], + ["unicode-canonical-property-names-ecmascript", "npm:2.0.0"], + ["unicode-property-aliases-ecmascript", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["unicode-match-property-value-ecmascript", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/unicode-match-property-value-ecmascript-npm-2.0.0-b52f4f7ca4-8fe6a09d90.zip/node_modules/unicode-match-property-value-ecmascript/", + "packageDependencies": [ + ["unicode-match-property-value-ecmascript", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["unicode-property-aliases-ecmascript", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/unicode-property-aliases-ecmascript-npm-2.0.0-1636cb7768-dda4d39128.zip/node_modules/unicode-property-aliases-ecmascript/", + "packageDependencies": [ + ["unicode-property-aliases-ecmascript", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["unique-filename", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/unique-filename-npm-1.1.1-c885c5095b-cf4998c922.zip/node_modules/unique-filename/", + "packageDependencies": [ + ["unique-filename", "npm:1.1.1"], + ["unique-slug", "npm:2.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["unique-slug", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/unique-slug-npm-2.0.2-f6ba1ddeb7-5b6876a645.zip/node_modules/unique-slug/", + "packageDependencies": [ + ["unique-slug", "npm:2.0.2"], + ["imurmurhash", "npm:0.1.4"] + ], + "linkType": "HARD", + }] + ]], + ["unique-string", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/unique-string-npm-2.0.0-3153c97e47-ef68f63913.zip/node_modules/unique-string/", + "packageDependencies": [ + ["unique-string", "npm:2.0.0"], + ["crypto-random-string", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["universal-user-agent", [ + ["npm:6.0.0", { + "packageLocation": "./.yarn/cache/universal-user-agent-npm-6.0.0-b148fb997a-5092bbc80d.zip/node_modules/universal-user-agent/", + "packageDependencies": [ + ["universal-user-agent", "npm:6.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["universalify", [ + ["npm:0.1.2", { + "packageLocation": "./.yarn/cache/universalify-npm-0.1.2-9b22d31d2d-40cdc60f6e.zip/node_modules/universalify/", + "packageDependencies": [ + ["universalify", "npm:0.1.2"] + ], + "linkType": "HARD", + }], + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/universalify-npm-2.0.0-03b8b418a8-2406a4edf4.zip/node_modules/universalify/", + "packageDependencies": [ + ["universalify", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["unorm", [ + ["npm:1.6.0", { + "packageLocation": "./.yarn/cache/unorm-npm-1.6.0-43467eccf1-9a86546256.zip/node_modules/unorm/", + "packageDependencies": [ + ["unorm", "npm:1.6.0"] + ], + "linkType": "HARD", + }] + ]], + ["unpipe", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/unpipe-npm-1.0.0-2ed2a3c2bf-4fa18d8d8d.zip/node_modules/unpipe/", + "packageDependencies": [ + ["unpipe", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["untildify", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/untildify-npm-4.0.0-4a8b569825-39ced9c418.zip/node_modules/untildify/", + "packageDependencies": [ + ["untildify", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["update-notifier", [ + ["npm:5.1.0", { + "packageLocation": "./.yarn/cache/update-notifier-npm-5.1.0-6bf595ecee-461e5e5b00.zip/node_modules/update-notifier/", + "packageDependencies": [ + ["update-notifier", "npm:5.1.0"], + ["boxen", "npm:5.1.2"], + ["chalk", "npm:4.1.2"], + ["configstore", "npm:5.0.1"], + ["has-yarn", "npm:2.1.0"], + ["import-lazy", "npm:2.1.0"], + ["is-ci", "npm:2.0.0"], + ["is-installed-globally", "npm:0.4.0"], + ["is-npm", "npm:5.0.0"], + ["is-yarn-global", "npm:0.3.0"], + ["latest-version", "npm:5.1.0"], + ["pupa", "npm:2.1.1"], + ["semver", "npm:7.3.5"], + ["semver-diff", "npm:3.1.1"], + ["xdg-basedir", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["uri-js", [ + ["npm:4.4.1", { + "packageLocation": "./.yarn/cache/uri-js-npm-4.4.1-66d11cbcaf-7167432de6.zip/node_modules/uri-js/", + "packageDependencies": [ + ["uri-js", "npm:4.4.1"], + ["punycode", "npm:2.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["url", [ + ["npm:0.10.3", { + "packageLocation": "./.yarn/cache/url-npm-0.10.3-37c0b27c3c-7b83ddb106.zip/node_modules/url/", + "packageDependencies": [ + ["url", "npm:0.10.3"], + ["punycode", "npm:1.3.2"], + ["querystring", "npm:0.2.0"] + ], + "linkType": "HARD", + }], + ["npm:0.11.0", { + "packageLocation": "./.yarn/cache/url-npm-0.11.0-32ce15acfb-50d100d3dd.zip/node_modules/url/", + "packageDependencies": [ + ["url", "npm:0.11.0"], + ["punycode", "npm:1.3.2"], + ["querystring", "npm:0.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["url-parse-lax", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/url-parse-lax-npm-3.0.0-92aa8effa0-1040e35775.zip/node_modules/url-parse-lax/", + "packageDependencies": [ + ["url-parse-lax", "npm:3.0.0"], + ["prepend-http", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["utf-8-validate", [ + ["npm:5.0.9", { + "packageLocation": "./.yarn/unplugged/utf-8-validate-npm-5.0.9-ed88df348e/node_modules/utf-8-validate/", + "packageDependencies": [ + ["utf-8-validate", "npm:5.0.9"], + ["node-gyp", "npm:8.4.0"], + ["node-gyp-build", "npm:4.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["utf8", [ + ["npm:2.1.2", { + "packageLocation": "./.yarn/cache/utf8-npm-2.1.2-17bfd49a94-de5d18adb2.zip/node_modules/utf8/", + "packageDependencies": [ + ["utf8", "npm:2.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["util", [ + ["npm:0.10.3", { + "packageLocation": "./.yarn/cache/util-npm-0.10.3-f43de5ccbb-bd800f5d23.zip/node_modules/util/", + "packageDependencies": [ + ["util", "npm:0.10.3"], + ["inherits", "npm:2.0.1"] + ], + "linkType": "HARD", + }], + ["npm:0.10.4", { + "packageLocation": "./.yarn/cache/util-npm-0.10.4-7c577db41a-913f9a90d0.zip/node_modules/util/", + "packageDependencies": [ + ["util", "npm:0.10.4"], + ["inherits", "npm:2.0.3"] + ], + "linkType": "HARD", + }], + ["npm:0.12.4", { + "packageLocation": "./.yarn/cache/util-npm-0.12.4-a022701e3b-8eac7a6e6b.zip/node_modules/util/", + "packageDependencies": [ + ["util", "npm:0.12.4"], + ["inherits", "npm:2.0.4"], + ["is-arguments", "npm:1.1.1"], + ["is-generator-function", "npm:1.0.10"], + ["is-typed-array", "npm:1.1.8"], + ["safe-buffer", "npm:5.2.1"], + ["which-typed-array", "npm:1.1.7"] + ], + "linkType": "HARD", + }] + ]], + ["util-deprecate", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/util-deprecate-npm-1.0.2-e3fe1a219c-474acf1146.zip/node_modules/util-deprecate/", + "packageDependencies": [ + ["util-deprecate", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["utils-merge", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/utils-merge-npm-1.0.1-363bbdfbca-c810954932.zip/node_modules/utils-merge/", + "packageDependencies": [ + ["utils-merge", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["uuid", [ + ["npm:3.3.2", { + "packageLocation": "./.yarn/cache/uuid-npm-3.3.2-62715051ac-8793629d27.zip/node_modules/uuid/", + "packageDependencies": [ + ["uuid", "npm:3.3.2"] + ], + "linkType": "HARD", + }], + ["npm:3.4.0", { + "packageLocation": "./.yarn/cache/uuid-npm-3.4.0-4fd8ef88ad-58de2feed6.zip/node_modules/uuid/", + "packageDependencies": [ + ["uuid", "npm:3.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["v8-compile-cache", [ + ["npm:2.3.0", { + "packageLocation": "./.yarn/cache/v8-compile-cache-npm-2.3.0-961375f150-adb0a271ea.zip/node_modules/v8-compile-cache/", + "packageDependencies": [ + ["v8-compile-cache", "npm:2.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["validate-npm-package-license", [ + ["npm:3.0.4", { + "packageLocation": "./.yarn/cache/validate-npm-package-license-npm-3.0.4-7af8adc7a8-35703ac889.zip/node_modules/validate-npm-package-license/", + "packageDependencies": [ + ["validate-npm-package-license", "npm:3.0.4"], + ["spdx-correct", "npm:3.1.1"], + ["spdx-expression-parse", "npm:3.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["validate-npm-package-name", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/validate-npm-package-name-npm-3.0.0-e44c263962-ce4c68207a.zip/node_modules/validate-npm-package-name/", + "packageDependencies": [ + ["validate-npm-package-name", "npm:3.0.0"], + ["builtins", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["validator", [ + ["npm:13.7.0", { + "packageLocation": "./.yarn/cache/validator-npm-13.7.0-624277e841-2b83283de1.zip/node_modules/validator/", + "packageDependencies": [ + ["validator", "npm:13.7.0"] + ], + "linkType": "HARD", + }] + ]], + ["varint", [ + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/varint-npm-5.0.0-c2491b868a-527c65ad87.zip/node_modules/varint/", + "packageDependencies": [ + ["varint", "npm:5.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.0.2", { + "packageLocation": "./.yarn/cache/varint-npm-5.0.2-fcb43e79c5-e1a66bf9a6.zip/node_modules/varint/", + "packageDependencies": [ + ["varint", "npm:5.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["vary", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/vary-npm-1.1.2-b49f70ae63-ae0123222c.zip/node_modules/vary/", + "packageDependencies": [ + ["vary", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["verror", [ + ["npm:1.10.0", { + "packageLocation": "./.yarn/cache/verror-npm-1.10.0-c3f839c579-c431df0bed.zip/node_modules/verror/", + "packageDependencies": [ + ["verror", "npm:1.10.0"], + ["assert-plus", "npm:1.0.0"], + ["core-util-is", "npm:1.0.2"], + ["extsprintf", "npm:1.4.1"] + ], + "linkType": "HARD", + }] + ]], + ["vinyl", [ + ["npm:2.2.1", { + "packageLocation": "./.yarn/cache/vinyl-npm-2.2.1-6b14799ad3-1f663973f1.zip/node_modules/vinyl/", + "packageDependencies": [ + ["vinyl", "npm:2.2.1"], + ["clone", "npm:2.1.2"], + ["clone-buffer", "npm:1.0.0"], + ["clone-stats", "npm:1.0.0"], + ["cloneable-readable", "npm:1.1.3"], + ["remove-trailing-separator", "npm:1.1.0"], + ["replace-ext", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["vinyl-file", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/vinyl-file-npm-3.0.0-4d55e6cd5d-e187a74d41.zip/node_modules/vinyl-file/", + "packageDependencies": [ + ["vinyl-file", "npm:3.0.0"], + ["graceful-fs", "npm:4.2.10"], + ["pify", "npm:2.3.0"], + ["strip-bom-buf", "npm:1.0.0"], + ["strip-bom-stream", "npm:2.0.0"], + ["vinyl", "npm:2.2.1"] + ], + "linkType": "HARD", + }] + ]], + ["vm-browserify", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/vm-browserify-npm-1.1.2-f96404b36f-10a1c50aab.zip/node_modules/vm-browserify/", + "packageDependencies": [ + ["vm-browserify", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["void-elements", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/void-elements-npm-2.0.1-85e6962130-700c07ba9c.zip/node_modules/void-elements/", + "packageDependencies": [ + ["void-elements", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["walk-up-path", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/walk-up-path-npm-1.0.0-54fda77042-b8019ac4fb.zip/node_modules/walk-up-path/", + "packageDependencies": [ + ["walk-up-path", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["watchpack", [ + ["npm:2.2.0", { + "packageLocation": "./.yarn/cache/watchpack-npm-2.2.0-fca5986ad5-e275f48fae.zip/node_modules/watchpack/", + "packageDependencies": [ + ["watchpack", "npm:2.2.0"], + ["glob-to-regexp", "npm:0.4.1"], + ["graceful-fs", "npm:4.2.10"] + ], + "linkType": "HARD", + }] + ]], + ["wcwidth", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/wcwidth-npm-1.0.1-05fa596453-814e9d1ddc.zip/node_modules/wcwidth/", + "packageDependencies": [ + ["wcwidth", "npm:1.0.1"], + ["defaults", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["webidl-conversions", [ + ["npm:3.0.1", { + "packageLocation": "./.yarn/cache/webidl-conversions-npm-3.0.1-60310f6a2b-c92a0a6ab9.zip/node_modules/webidl-conversions/", + "packageDependencies": [ + ["webidl-conversions", "npm:3.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["webpack", [ + ["npm:5.64.1", { + "packageLocation": "./.yarn/cache/webpack-npm-5.64.1-77fbd9ac18-d2a1baddae.zip/node_modules/webpack/", + "packageDependencies": [ + ["webpack", "npm:5.64.1"] + ], + "linkType": "SOFT", + }], + ["virtual:01938c2be4835443e5a304e2b117c575220e96e8b7cedeb0f48d79264590b4c4babc6d1fea6367f522b1ca0149d795b42f2ab89c34a6ffe3c20f0a8cbb8b4453#npm:5.64.1", { + "packageLocation": "./.yarn/__virtual__/webpack-virtual-4953b975b7/0/cache/webpack-npm-5.64.1-77fbd9ac18-d2a1baddae.zip/node_modules/webpack/", + "packageDependencies": [ + ["webpack", "virtual:01938c2be4835443e5a304e2b117c575220e96e8b7cedeb0f48d79264590b4c4babc6d1fea6367f522b1ca0149d795b42f2ab89c34a6ffe3c20f0a8cbb8b4453#npm:5.64.1"], + ["@types/eslint-scope", "npm:3.7.1"], + ["@types/estree", "npm:0.0.50"], + ["@types/webpack-cli", null], + ["@webassemblyjs/ast", "npm:1.11.1"], + ["@webassemblyjs/wasm-edit", "npm:1.11.1"], + ["@webassemblyjs/wasm-parser", "npm:1.11.1"], + ["acorn", "npm:8.6.0"], + ["acorn-import-assertions", "virtual:74c6cfbee4804d578abc9f87850914089f1afe762b6ef9dde76ecf912446a2b7772b58b2a3d070d6a0e21b0f126bfcdfa013123d743c15f32eee4a9cf9f4d551#npm:1.8.0"], + ["browserslist", "npm:4.18.1"], + ["chrome-trace-event", "npm:1.0.3"], + ["enhanced-resolve", "npm:5.8.3"], + ["es-module-lexer", "npm:0.9.3"], + ["eslint-scope", "npm:5.1.1"], + ["events", "npm:3.3.0"], + ["glob-to-regexp", "npm:0.4.1"], + ["graceful-fs", "npm:4.2.10"], + ["json-parse-better-errors", "npm:1.0.2"], + ["loader-runner", "npm:4.2.0"], + ["mime-types", "npm:2.1.34"], + ["neo-async", "npm:2.6.2"], + ["schema-utils", "npm:3.1.1"], + ["tapable", "npm:2.2.1"], + ["terser-webpack-plugin", "virtual:4953b975b7bd5a519c3d3acbf14e67153262cfb500bb1b8ce3d6f78e6b4fc3e422ef0018b19f059be78f5a124f310f4b9d100d3e3076ea1db98ff3d39975a669#npm:5.3.3"], + ["watchpack", "npm:2.2.0"], + ["webpack-cli", null], + ["webpack-sources", "npm:3.2.2"] + ], + "packagePeers": [ + "@types/webpack-cli", + "webpack-cli" + ], + "linkType": "HARD", + }], + ["virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:5.64.1", { + "packageLocation": "./.yarn/__virtual__/webpack-virtual-1d78bb01f2/0/cache/webpack-npm-5.64.1-77fbd9ac18-d2a1baddae.zip/node_modules/webpack/", + "packageDependencies": [ + ["webpack", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:5.64.1"], + ["@types/eslint-scope", "npm:3.7.1"], + ["@types/estree", "npm:0.0.50"], + ["@types/webpack-cli", null], + ["@webassemblyjs/ast", "npm:1.11.1"], + ["@webassemblyjs/wasm-edit", "npm:1.11.1"], + ["@webassemblyjs/wasm-parser", "npm:1.11.1"], + ["acorn", "npm:8.6.0"], + ["acorn-import-assertions", "virtual:74c6cfbee4804d578abc9f87850914089f1afe762b6ef9dde76ecf912446a2b7772b58b2a3d070d6a0e21b0f126bfcdfa013123d743c15f32eee4a9cf9f4d551#npm:1.8.0"], + ["browserslist", "npm:4.18.1"], + ["chrome-trace-event", "npm:1.0.3"], + ["enhanced-resolve", "npm:5.8.3"], + ["es-module-lexer", "npm:0.9.3"], + ["eslint-scope", "npm:5.1.1"], + ["events", "npm:3.3.0"], + ["glob-to-regexp", "npm:0.4.1"], + ["graceful-fs", "npm:4.2.10"], + ["json-parse-better-errors", "npm:1.0.2"], + ["loader-runner", "npm:4.2.0"], + ["mime-types", "npm:2.1.34"], + ["neo-async", "npm:2.6.2"], + ["schema-utils", "npm:3.1.1"], + ["tapable", "npm:2.2.1"], + ["terser-webpack-plugin", "virtual:1d78bb01f21914b3c919ffb707b6b169127e3e506be79a82799d0632e5da000051686a18675813b8a2e4b9d97f1d372c927db824a65bfcada7f79e5b0273240f#npm:5.3.3"], + ["watchpack", "npm:2.2.0"], + ["webpack-cli", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:4.9.1"], + ["webpack-sources", "npm:3.2.2"] + ], + "packagePeers": [ + "@types/webpack-cli", + "webpack-cli" + ], + "linkType": "HARD", + }], + ["virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:5.64.1", { + "packageLocation": "./.yarn/__virtual__/webpack-virtual-74c6cfbee4/0/cache/webpack-npm-5.64.1-77fbd9ac18-d2a1baddae.zip/node_modules/webpack/", + "packageDependencies": [ + ["webpack", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:5.64.1"], + ["@types/eslint-scope", "npm:3.7.1"], + ["@types/estree", "npm:0.0.50"], + ["@types/webpack-cli", null], + ["@webassemblyjs/ast", "npm:1.11.1"], + ["@webassemblyjs/wasm-edit", "npm:1.11.1"], + ["@webassemblyjs/wasm-parser", "npm:1.11.1"], + ["acorn", "npm:8.6.0"], + ["acorn-import-assertions", "virtual:74c6cfbee4804d578abc9f87850914089f1afe762b6ef9dde76ecf912446a2b7772b58b2a3d070d6a0e21b0f126bfcdfa013123d743c15f32eee4a9cf9f4d551#npm:1.8.0"], + ["browserslist", "npm:4.18.1"], + ["chrome-trace-event", "npm:1.0.3"], + ["enhanced-resolve", "npm:5.8.3"], + ["es-module-lexer", "npm:0.9.3"], + ["eslint-scope", "npm:5.1.1"], + ["events", "npm:3.3.0"], + ["glob-to-regexp", "npm:0.4.1"], + ["graceful-fs", "npm:4.2.10"], + ["json-parse-better-errors", "npm:1.0.2"], + ["loader-runner", "npm:4.2.0"], + ["mime-types", "npm:2.1.34"], + ["neo-async", "npm:2.6.2"], + ["schema-utils", "npm:3.1.1"], + ["tapable", "npm:2.2.1"], + ["terser-webpack-plugin", "virtual:74c6cfbee4804d578abc9f87850914089f1afe762b6ef9dde76ecf912446a2b7772b58b2a3d070d6a0e21b0f126bfcdfa013123d743c15f32eee4a9cf9f4d551#npm:5.3.3"], + ["watchpack", "npm:2.2.0"], + ["webpack-cli", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:4.9.1"], + ["webpack-sources", "npm:3.2.2"] + ], + "packagePeers": [ + "@types/webpack-cli", + "webpack-cli" + ], + "linkType": "HARD", + }], + ["virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:5.64.1", { + "packageLocation": "./.yarn/__virtual__/webpack-virtual-35ef488be3/0/cache/webpack-npm-5.64.1-77fbd9ac18-d2a1baddae.zip/node_modules/webpack/", + "packageDependencies": [ + ["webpack", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:5.64.1"], + ["@types/eslint-scope", "npm:3.7.1"], + ["@types/estree", "npm:0.0.50"], + ["@types/webpack-cli", null], + ["@webassemblyjs/ast", "npm:1.11.1"], + ["@webassemblyjs/wasm-edit", "npm:1.11.1"], + ["@webassemblyjs/wasm-parser", "npm:1.11.1"], + ["acorn", "npm:8.6.0"], + ["acorn-import-assertions", "virtual:74c6cfbee4804d578abc9f87850914089f1afe762b6ef9dde76ecf912446a2b7772b58b2a3d070d6a0e21b0f126bfcdfa013123d743c15f32eee4a9cf9f4d551#npm:1.8.0"], + ["browserslist", "npm:4.18.1"], + ["chrome-trace-event", "npm:1.0.3"], + ["enhanced-resolve", "npm:5.8.3"], + ["es-module-lexer", "npm:0.9.3"], + ["eslint-scope", "npm:5.1.1"], + ["events", "npm:3.3.0"], + ["glob-to-regexp", "npm:0.4.1"], + ["graceful-fs", "npm:4.2.10"], + ["json-parse-better-errors", "npm:1.0.2"], + ["loader-runner", "npm:4.2.0"], + ["mime-types", "npm:2.1.34"], + ["neo-async", "npm:2.6.2"], + ["schema-utils", "npm:3.1.1"], + ["tapable", "npm:2.2.1"], + ["terser-webpack-plugin", "virtual:35ef488be3738fb73696e8919272b5c35cfafebd6b82f716b7cf402514ee937bd8da0f25ae67baa1d8aeef0e69c557930350fe0dec6c03e135d60acb24c93c14#npm:5.3.3"], + ["watchpack", "npm:2.2.0"], + ["webpack-cli", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:4.9.1"], + ["webpack-sources", "npm:3.2.2"] + ], + "packagePeers": [ + "@types/webpack-cli", + "webpack-cli" + ], + "linkType": "HARD", + }], + ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.64.1", { + "packageLocation": "./.yarn/__virtual__/webpack-virtual-afdb643c18/0/cache/webpack-npm-5.64.1-77fbd9ac18-d2a1baddae.zip/node_modules/webpack/", + "packageDependencies": [ + ["webpack", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.64.1"], + ["@types/eslint-scope", "npm:3.7.1"], + ["@types/estree", "npm:0.0.50"], + ["@types/webpack-cli", null], + ["@webassemblyjs/ast", "npm:1.11.1"], + ["@webassemblyjs/wasm-edit", "npm:1.11.1"], + ["@webassemblyjs/wasm-parser", "npm:1.11.1"], + ["acorn", "npm:8.6.0"], + ["acorn-import-assertions", "virtual:74c6cfbee4804d578abc9f87850914089f1afe762b6ef9dde76ecf912446a2b7772b58b2a3d070d6a0e21b0f126bfcdfa013123d743c15f32eee4a9cf9f4d551#npm:1.8.0"], + ["browserslist", "npm:4.18.1"], + ["chrome-trace-event", "npm:1.0.3"], + ["enhanced-resolve", "npm:5.8.3"], + ["es-module-lexer", "npm:0.9.3"], + ["eslint-scope", "npm:5.1.1"], + ["events", "npm:3.3.0"], + ["glob-to-regexp", "npm:0.4.1"], + ["graceful-fs", "npm:4.2.10"], + ["json-parse-better-errors", "npm:1.0.2"], + ["loader-runner", "npm:4.2.0"], + ["mime-types", "npm:2.1.34"], + ["neo-async", "npm:2.6.2"], + ["schema-utils", "npm:3.1.1"], + ["tapable", "npm:2.2.1"], + ["terser-webpack-plugin", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.3.3"], + ["watchpack", "npm:2.2.0"], + ["webpack-cli", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:4.9.1"], + ["webpack-sources", "npm:3.2.2"] + ], + "packagePeers": [ + "@types/webpack-cli", + "webpack-cli" + ], + "linkType": "HARD", + }] + ]], + ["webpack-cli", [ + ["npm:4.9.1", { + "packageLocation": "./.yarn/cache/webpack-cli-npm-4.9.1-1b8a5f360f-2aff0349c1.zip/node_modules/webpack-cli/", + "packageDependencies": [ + ["webpack-cli", "npm:4.9.1"] + ], + "linkType": "SOFT", + }], + ["virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:4.9.1", { + "packageLocation": "./.yarn/__virtual__/webpack-cli-virtual-0249f7ceb5/0/cache/webpack-cli-npm-4.9.1-1b8a5f360f-2aff0349c1.zip/node_modules/webpack-cli/", + "packageDependencies": [ + ["webpack-cli", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:4.9.1"], + ["@discoveryjs/json-ext", "npm:0.5.5"], + ["@types/webpack", null], + ["@types/webpack-bundle-analyzer", null], + ["@types/webpack-cli__generators", null], + ["@types/webpack-cli__migrate", null], + ["@types/webpack-dev-server", null], + ["@webpack-cli/configtest", "virtual:0249f7ceb5542d6b732af2b44f9fcd16c60be8b8440f0f3abc6a5de67aabcff731bc3bc83f3067ab2f9037661176f001f89208fcea9e8962835fd43d0aabe88a#npm:1.1.0"], + ["@webpack-cli/generators", null], + ["@webpack-cli/info", "virtual:0249f7ceb5542d6b732af2b44f9fcd16c60be8b8440f0f3abc6a5de67aabcff731bc3bc83f3067ab2f9037661176f001f89208fcea9e8962835fd43d0aabe88a#npm:1.4.0"], + ["@webpack-cli/migrate", null], + ["@webpack-cli/serve", "virtual:0249f7ceb5542d6b732af2b44f9fcd16c60be8b8440f0f3abc6a5de67aabcff731bc3bc83f3067ab2f9037661176f001f89208fcea9e8962835fd43d0aabe88a#npm:1.6.0"], + ["colorette", "npm:2.0.16"], + ["commander", "npm:7.2.0"], + ["execa", "npm:5.1.1"], + ["fastest-levenshtein", "npm:1.0.12"], + ["import-local", "npm:3.0.3"], + ["interpret", "npm:2.2.0"], + ["rechoir", "npm:0.7.1"], + ["webpack", "virtual:45f214395bc38640da4dc5e940482d5df0572c5384e0262802601d1973e71077ec8bbd76b77eafa4c0550b706b664abd84d63fd67a5897139f0b2675530fc84f#npm:5.64.1"], + ["webpack-bundle-analyzer", null], + ["webpack-dev-server", null], + ["webpack-merge", "npm:5.8.0"] + ], + "packagePeers": [ + "@types/webpack-bundle-analyzer", + "@types/webpack-cli__generators", + "@types/webpack-cli__migrate", + "@types/webpack-dev-server", + "@types/webpack", + "@webpack-cli/generators", + "@webpack-cli/migrate", + "webpack-bundle-analyzer", + "webpack-dev-server", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:4.9.1", { + "packageLocation": "./.yarn/__virtual__/webpack-cli-virtual-933f3c2d7f/0/cache/webpack-cli-npm-4.9.1-1b8a5f360f-2aff0349c1.zip/node_modules/webpack-cli/", + "packageDependencies": [ + ["webpack-cli", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:4.9.1"], + ["@discoveryjs/json-ext", "npm:0.5.5"], + ["@types/webpack", null], + ["@types/webpack-bundle-analyzer", null], + ["@types/webpack-cli__generators", null], + ["@types/webpack-cli__migrate", null], + ["@types/webpack-dev-server", null], + ["@webpack-cli/configtest", "virtual:933f3c2d7f6a5dac21dc52727e214cdc1fcf36d71628fb136e96267112c54b183dd48537afe26d9bffb18203e9028c2bf712344b29e251121cd4c374fbb4e51a#npm:1.1.0"], + ["@webpack-cli/generators", null], + ["@webpack-cli/info", "virtual:933f3c2d7f6a5dac21dc52727e214cdc1fcf36d71628fb136e96267112c54b183dd48537afe26d9bffb18203e9028c2bf712344b29e251121cd4c374fbb4e51a#npm:1.4.0"], + ["@webpack-cli/migrate", null], + ["@webpack-cli/serve", "virtual:933f3c2d7f6a5dac21dc52727e214cdc1fcf36d71628fb136e96267112c54b183dd48537afe26d9bffb18203e9028c2bf712344b29e251121cd4c374fbb4e51a#npm:1.6.0"], + ["colorette", "npm:2.0.16"], + ["commander", "npm:7.2.0"], + ["execa", "npm:5.1.1"], + ["fastest-levenshtein", "npm:1.0.12"], + ["import-local", "npm:3.0.3"], + ["interpret", "npm:2.2.0"], + ["rechoir", "npm:0.7.1"], + ["webpack", "virtual:58fb68f2aed20e5e0f2e48520ab903ae9bb3440369bfd5e912034003cf27c5aae368649fc5620dd2acbed578131f3a0975e75b838d77d12335fb0412e24026c6#npm:5.64.1"], + ["webpack-bundle-analyzer", null], + ["webpack-dev-server", null], + ["webpack-merge", "npm:5.8.0"] + ], + "packagePeers": [ + "@types/webpack-bundle-analyzer", + "@types/webpack-cli__generators", + "@types/webpack-cli__migrate", + "@types/webpack-dev-server", + "@types/webpack", + "@webpack-cli/generators", + "@webpack-cli/migrate", + "webpack-bundle-analyzer", + "webpack-dev-server", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:4.9.1", { + "packageLocation": "./.yarn/__virtual__/webpack-cli-virtual-b37ef7cf98/0/cache/webpack-cli-npm-4.9.1-1b8a5f360f-2aff0349c1.zip/node_modules/webpack-cli/", + "packageDependencies": [ + ["webpack-cli", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:4.9.1"], + ["@discoveryjs/json-ext", "npm:0.5.5"], + ["@types/webpack", null], + ["@types/webpack-bundle-analyzer", null], + ["@types/webpack-cli__generators", null], + ["@types/webpack-cli__migrate", null], + ["@types/webpack-dev-server", null], + ["@webpack-cli/configtest", "virtual:b37ef7cf98ceabe8c7b789a7db3f0a5f3444d083afa5f0e3ab570292e74eff241f890fadbf245a134b2ebfcba326b1782124a4dd4f16ca7cdb6091dd9a987c04#npm:1.1.0"], + ["@webpack-cli/generators", null], + ["@webpack-cli/info", "virtual:b37ef7cf98ceabe8c7b789a7db3f0a5f3444d083afa5f0e3ab570292e74eff241f890fadbf245a134b2ebfcba326b1782124a4dd4f16ca7cdb6091dd9a987c04#npm:1.4.0"], + ["@webpack-cli/migrate", null], + ["@webpack-cli/serve", "virtual:b37ef7cf98ceabe8c7b789a7db3f0a5f3444d083afa5f0e3ab570292e74eff241f890fadbf245a134b2ebfcba326b1782124a4dd4f16ca7cdb6091dd9a987c04#npm:1.6.0"], + ["colorette", "npm:2.0.16"], + ["commander", "npm:7.2.0"], + ["execa", "npm:5.1.1"], + ["fastest-levenshtein", "npm:1.0.12"], + ["import-local", "npm:3.0.3"], + ["interpret", "npm:2.2.0"], + ["rechoir", "npm:0.7.1"], + ["webpack", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:5.64.1"], + ["webpack-bundle-analyzer", null], + ["webpack-dev-server", null], + ["webpack-merge", "npm:5.8.0"] + ], + "packagePeers": [ + "@types/webpack-bundle-analyzer", + "@types/webpack-cli__generators", + "@types/webpack-cli__migrate", + "@types/webpack-dev-server", + "@types/webpack", + "@webpack-cli/generators", + "@webpack-cli/migrate", + "webpack-bundle-analyzer", + "webpack-dev-server", + "webpack" + ], + "linkType": "HARD", + }], + ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:4.9.1", { + "packageLocation": "./.yarn/__virtual__/webpack-cli-virtual-7fc88da9d0/0/cache/webpack-cli-npm-4.9.1-1b8a5f360f-2aff0349c1.zip/node_modules/webpack-cli/", + "packageDependencies": [ + ["webpack-cli", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:4.9.1"], + ["@discoveryjs/json-ext", "npm:0.5.5"], + ["@types/webpack", null], + ["@types/webpack-bundle-analyzer", null], + ["@types/webpack-cli__generators", null], + ["@types/webpack-cli__migrate", null], + ["@types/webpack-dev-server", null], + ["@webpack-cli/configtest", "virtual:7fc88da9d00679384dc013444a3b1ed8ef8395fcad9d046790a1851d5db985e5ee052061731f87c5475e4bf20a92d69ea1a1a287c0d76d7b1a6bf97010c63532#npm:1.1.0"], + ["@webpack-cli/generators", null], + ["@webpack-cli/info", "virtual:7fc88da9d00679384dc013444a3b1ed8ef8395fcad9d046790a1851d5db985e5ee052061731f87c5475e4bf20a92d69ea1a1a287c0d76d7b1a6bf97010c63532#npm:1.4.0"], + ["@webpack-cli/migrate", null], + ["@webpack-cli/serve", "virtual:7fc88da9d00679384dc013444a3b1ed8ef8395fcad9d046790a1851d5db985e5ee052061731f87c5475e4bf20a92d69ea1a1a287c0d76d7b1a6bf97010c63532#npm:1.6.0"], + ["colorette", "npm:2.0.16"], + ["commander", "npm:7.2.0"], + ["execa", "npm:5.1.1"], + ["fastest-levenshtein", "npm:1.0.12"], + ["import-local", "npm:3.0.3"], + ["interpret", "npm:2.2.0"], + ["rechoir", "npm:0.7.1"], + ["webpack", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.64.1"], + ["webpack-bundle-analyzer", null], + ["webpack-dev-server", null], + ["webpack-merge", "npm:5.8.0"] + ], + "packagePeers": [ + "@types/webpack-bundle-analyzer", + "@types/webpack-cli__generators", + "@types/webpack-cli__migrate", + "@types/webpack-dev-server", + "@types/webpack", + "@webpack-cli/generators", + "@webpack-cli/migrate", + "webpack-bundle-analyzer", + "webpack-dev-server", + "webpack" + ], + "linkType": "HARD", + }] + ]], + ["webpack-merge", [ + ["npm:4.2.2", { + "packageLocation": "./.yarn/cache/webpack-merge-npm-4.2.2-f98139a8eb-ce58bc8ab5.zip/node_modules/webpack-merge/", + "packageDependencies": [ + ["webpack-merge", "npm:4.2.2"], + ["lodash", "npm:4.17.21"] + ], + "linkType": "HARD", + }], + ["npm:5.8.0", { + "packageLocation": "./.yarn/cache/webpack-merge-npm-5.8.0-e3c95fdc3c-88786ab910.zip/node_modules/webpack-merge/", + "packageDependencies": [ + ["webpack-merge", "npm:5.8.0"], + ["clone-deep", "npm:4.0.1"], + ["wildcard", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["webpack-sources", [ + ["npm:3.2.2", { + "packageLocation": "./.yarn/cache/webpack-sources-npm-3.2.2-9b97404a4e-cc81f1f1bf.zip/node_modules/webpack-sources/", + "packageDependencies": [ + ["webpack-sources", "npm:3.2.2"] + ], + "linkType": "HARD", + }] + ]], + ["whatwg-url", [ + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/whatwg-url-npm-5.0.0-374fb45e60-b8daed4ad3.zip/node_modules/whatwg-url/", + "packageDependencies": [ + ["whatwg-url", "npm:5.0.0"], + ["tr46", "npm:0.0.3"], + ["webidl-conversions", "npm:3.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["which", [ + ["npm:1.3.1", { + "packageLocation": "./.yarn/cache/which-npm-1.3.1-f0ebb8bdd8-f2e185c624.zip/node_modules/which/", + "packageDependencies": [ + ["which", "npm:1.3.1"], + ["isexe", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/which-npm-2.0.2-320ddf72f7-1a5c563d3c.zip/node_modules/which/", + "packageDependencies": [ + ["which", "npm:2.0.2"], + ["isexe", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["which-boxed-primitive", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/which-boxed-primitive-npm-1.0.2-e214f9ae5a-53ce774c73.zip/node_modules/which-boxed-primitive/", + "packageDependencies": [ + ["which-boxed-primitive", "npm:1.0.2"], + ["is-bigint", "npm:1.0.4"], + ["is-boolean-object", "npm:1.1.2"], + ["is-number-object", "npm:1.0.6"], + ["is-string", "npm:1.0.7"], + ["is-symbol", "npm:1.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["which-module", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/which-module-npm-2.0.0-daf3daa08d-809f7fd3df.zip/node_modules/which-module/", + "packageDependencies": [ + ["which-module", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["which-pm", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/which-pm-npm-2.0.0-b9f68562bc-e556635eaf.zip/node_modules/which-pm/", + "packageDependencies": [ + ["which-pm", "npm:2.0.0"], + ["load-yaml-file", "npm:0.2.0"], + ["path-exists", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["which-typed-array", [ + ["npm:1.1.7", { + "packageLocation": "./.yarn/cache/which-typed-array-npm-1.1.7-7cf2d674e6-147837cf58.zip/node_modules/which-typed-array/", + "packageDependencies": [ + ["which-typed-array", "npm:1.1.7"], + ["available-typed-arrays", "npm:1.0.5"], + ["call-bind", "npm:1.0.2"], + ["es-abstract", "npm:1.19.1"], + ["foreach", "npm:2.0.5"], + ["has-tostringtag", "npm:1.0.0"], + ["is-typed-array", "npm:1.1.8"] + ], + "linkType": "HARD", + }] + ]], + ["wide-align", [ + ["npm:1.1.5", { + "packageLocation": "./.yarn/cache/wide-align-npm-1.1.5-889d77e592-d5fc37cd56.zip/node_modules/wide-align/", + "packageDependencies": [ + ["wide-align", "npm:1.1.5"], + ["string-width", "npm:4.2.3"] + ], + "linkType": "HARD", + }] + ]], + ["widest-line", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/widest-line-npm-3.1.0-717bf2680b-03db6c9d0a.zip/node_modules/widest-line/", + "packageDependencies": [ + ["widest-line", "npm:3.1.0"], + ["string-width", "npm:4.2.3"] + ], + "linkType": "HARD", + }] + ]], + ["wildcard", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/wildcard-npm-2.0.0-baedca033a-1f4fe4c03d.zip/node_modules/wildcard/", + "packageDependencies": [ + ["wildcard", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["winston", [ + ["npm:3.3.3", { + "packageLocation": "./.yarn/cache/winston-npm-3.3.3-3fa4527b42-89a0a8db4e.zip/node_modules/winston/", + "packageDependencies": [ + ["winston", "npm:3.3.3"], + ["@dabh/diagnostics", "npm:2.0.2"], + ["async", "npm:3.2.2"], + ["is-stream", "npm:2.0.1"], + ["logform", "npm:2.3.0"], + ["one-time", "npm:1.0.0"], + ["readable-stream", "npm:3.6.0"], + ["stack-trace", "npm:0.0.10"], + ["triple-beam", "npm:1.3.0"], + ["winston-transport", "npm:4.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["winston-transport", [ + ["npm:4.4.0", { + "packageLocation": "./.yarn/cache/winston-transport-npm-4.4.0-e1b3134c1e-953d78d152.zip/node_modules/winston-transport/", + "packageDependencies": [ + ["winston-transport", "npm:4.4.0"], + ["logform", "npm:2.3.0"], + ["readable-stream", "npm:2.3.7"], + ["triple-beam", "npm:1.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["word-wrap", [ + ["npm:1.2.3", { + "packageLocation": "./.yarn/cache/word-wrap-npm-1.2.3-7fb15ab002-30b48f91fc.zip/node_modules/word-wrap/", + "packageDependencies": [ + ["word-wrap", "npm:1.2.3"] + ], + "linkType": "HARD", + }] + ]], + ["wordwrap", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/wordwrap-npm-1.0.0-ae57a645e8-2a44b27881.zip/node_modules/wordwrap/", + "packageDependencies": [ + ["wordwrap", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["workerpool", [ + ["npm:6.1.5", { + "packageLocation": "./.yarn/cache/workerpool-npm-6.1.5-61adb98c59-5defea1fd3.zip/node_modules/workerpool/", + "packageDependencies": [ + ["workerpool", "npm:6.1.5"] + ], + "linkType": "HARD", + }] + ]], + ["wrap-ansi", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/wrap-ansi-npm-2.1.0-1fd9d50973-2dacd4b363.zip/node_modules/wrap-ansi/", + "packageDependencies": [ + ["wrap-ansi", "npm:2.1.0"], + ["string-width", "npm:1.0.2"], + ["strip-ansi", "npm:3.0.1"] + ], + "linkType": "HARD", + }], + ["npm:6.2.0", { + "packageLocation": "./.yarn/cache/wrap-ansi-npm-6.2.0-439a7246d8-6cd96a4101.zip/node_modules/wrap-ansi/", + "packageDependencies": [ + ["wrap-ansi", "npm:6.2.0"], + ["ansi-styles", "npm:4.3.0"], + ["string-width", "npm:4.2.3"], + ["strip-ansi", "npm:6.0.1"] + ], + "linkType": "HARD", + }], + ["npm:7.0.0", { + "packageLocation": "./.yarn/cache/wrap-ansi-npm-7.0.0-ad6e1a0554-a790b846fd.zip/node_modules/wrap-ansi/", + "packageDependencies": [ + ["wrap-ansi", "npm:7.0.0"], + ["ansi-styles", "npm:4.3.0"], + ["string-width", "npm:4.2.3"], + ["strip-ansi", "npm:6.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["wrappy", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/wrappy-npm-1.0.2-916de4d4b3-159da4805f.zip/node_modules/wrappy/", + "packageDependencies": [ + ["wrappy", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["write-file-atomic", [ + ["npm:3.0.3", { + "packageLocation": "./.yarn/cache/write-file-atomic-npm-3.0.3-d948a237da-c55b24617c.zip/node_modules/write-file-atomic/", + "packageDependencies": [ + ["write-file-atomic", "npm:3.0.3"], + ["imurmurhash", "npm:0.1.4"], + ["is-typedarray", "npm:1.0.0"], + ["signal-exit", "npm:3.0.7"], + ["typedarray-to-buffer", "npm:3.1.5"] + ], + "linkType": "HARD", + }], + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/write-file-atomic-npm-4.0.1-96ec744721-8f78023253.zip/node_modules/write-file-atomic/", + "packageDependencies": [ + ["write-file-atomic", "npm:4.0.1"], + ["imurmurhash", "npm:0.1.4"], + ["signal-exit", "npm:3.0.7"] + ], + "linkType": "HARD", + }] + ]], + ["write-json-file", [ + ["npm:4.3.0", { + "packageLocation": "./.yarn/cache/write-json-file-npm-4.3.0-89a21c4468-33908c5919.zip/node_modules/write-json-file/", + "packageDependencies": [ + ["write-json-file", "npm:4.3.0"], + ["detect-indent", "npm:6.1.0"], + ["graceful-fs", "npm:4.2.10"], + ["is-plain-obj", "npm:2.1.0"], + ["make-dir", "npm:3.1.0"], + ["sort-keys", "npm:4.2.0"], + ["write-file-atomic", "npm:3.0.3"] + ], + "linkType": "HARD", + }] + ]], + ["ws", [ + ["npm:7.5.5", { + "packageLocation": "./.yarn/cache/ws-npm-7.5.5-8f4a2a84a8-bd2b437256.zip/node_modules/ws/", + "packageDependencies": [ + ["ws", "npm:7.5.5"] + ], + "linkType": "SOFT", + }], + ["npm:8.2.3", { + "packageLocation": "./.yarn/cache/ws-npm-8.2.3-03a35b8ad7-c869296ccb.zip/node_modules/ws/", + "packageDependencies": [ + ["ws", "npm:8.2.3"] + ], + "linkType": "SOFT", + }], + ["virtual:01938c2be4835443e5a304e2b117c575220e96e8b7cedeb0f48d79264590b4c4babc6d1fea6367f522b1ca0149d795b42f2ab89c34a6ffe3c20f0a8cbb8b4453#npm:7.5.5", { + "packageLocation": "./.yarn/__virtual__/ws-virtual-49f0813c39/0/cache/ws-npm-7.5.5-8f4a2a84a8-bd2b437256.zip/node_modules/ws/", + "packageDependencies": [ + ["ws", "virtual:01938c2be4835443e5a304e2b117c575220e96e8b7cedeb0f48d79264590b4c4babc6d1fea6367f522b1ca0149d795b42f2ab89c34a6ffe3c20f0a8cbb8b4453#npm:7.5.5"], + ["@types/bufferutil", null], + ["@types/utf-8-validate", null], + ["bufferutil", "npm:4.0.6"], + ["utf-8-validate", "npm:5.0.9"] + ], + "packagePeers": [ + "@types/bufferutil", + "@types/utf-8-validate", + "bufferutil", + "utf-8-validate" + ], + "linkType": "HARD", + }], + ["virtual:2fd01a647a5c8b340dd0adae82833428da22baa88327826cc44910efed2d9ad403a63cf3d639ef91449c9c952ea1afcdd4379371f5022db3053206940836b879#npm:7.5.5", { + "packageLocation": "./.yarn/__virtual__/ws-virtual-fca14c3640/0/cache/ws-npm-7.5.5-8f4a2a84a8-bd2b437256.zip/node_modules/ws/", + "packageDependencies": [ + ["ws", "virtual:2fd01a647a5c8b340dd0adae82833428da22baa88327826cc44910efed2d9ad403a63cf3d639ef91449c9c952ea1afcdd4379371f5022db3053206940836b879#npm:7.5.5"], + ["@types/bufferutil", null], + ["@types/utf-8-validate", null], + ["bufferutil", null], + ["utf-8-validate", null] + ], + "packagePeers": [ + "@types/bufferutil", + "@types/utf-8-validate", + "bufferutil", + "utf-8-validate" + ], + "linkType": "HARD", + }], + ["virtual:cdba019cb110be40f42092768543381bb2825f22f13486586cfed065b9cbe9680eec0a5effcf0de8a08dad7b560b087b2a511d35315801730378caca1b7f0ff4#npm:8.2.3", { + "packageLocation": "./.yarn/__virtual__/ws-virtual-7be2b670f3/0/cache/ws-npm-8.2.3-03a35b8ad7-c869296ccb.zip/node_modules/ws/", + "packageDependencies": [ + ["ws", "virtual:cdba019cb110be40f42092768543381bb2825f22f13486586cfed065b9cbe9680eec0a5effcf0de8a08dad7b560b087b2a511d35315801730378caca1b7f0ff4#npm:8.2.3"], + ["@types/bufferutil", null], + ["@types/utf-8-validate", null], + ["bufferutil", null], + ["utf-8-validate", null] + ], + "packagePeers": [ + "@types/bufferutil", + "@types/utf-8-validate", + "bufferutil", + "utf-8-validate" + ], + "linkType": "HARD", + }] + ]], + ["xdg-basedir", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/xdg-basedir-npm-4.0.0-ed08d380e2-0073d5b59a.zip/node_modules/xdg-basedir/", + "packageDependencies": [ + ["xdg-basedir", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["xml2js", [ + ["npm:0.4.19", { + "packageLocation": "./.yarn/cache/xml2js-npm-0.4.19-104b7b16eb-ca8b2fee43.zip/node_modules/xml2js/", + "packageDependencies": [ + ["xml2js", "npm:0.4.19"], + ["sax", "npm:1.2.4"], + ["xmlbuilder", "npm:9.0.7"] + ], + "linkType": "HARD", + }] + ]], + ["xmlbuilder", [ + ["npm:9.0.7", { + "packageLocation": "./.yarn/cache/xmlbuilder-npm-9.0.7-44519dbccb-8193bb3238.zip/node_modules/xmlbuilder/", + "packageDependencies": [ + ["xmlbuilder", "npm:9.0.7"] + ], + "linkType": "HARD", + }] + ]], + ["xtend", [ + ["npm:4.0.2", { + "packageLocation": "./.yarn/cache/xtend-npm-4.0.2-7f2375736e-ac5dfa738b.zip/node_modules/xtend/", + "packageDependencies": [ + ["xtend", "npm:4.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["y18n", [ + ["npm:4.0.3", { + "packageLocation": "./.yarn/cache/y18n-npm-4.0.3-ced95acdbc-014dfcd9b5.zip/node_modules/y18n/", + "packageDependencies": [ + ["y18n", "npm:4.0.3"] + ], + "linkType": "HARD", + }], + ["npm:5.0.8", { + "packageLocation": "./.yarn/cache/y18n-npm-5.0.8-5f3a0a7e62-54f0fb9562.zip/node_modules/y18n/", + "packageDependencies": [ + ["y18n", "npm:5.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["yallist", [ + ["npm:3.1.1", { + "packageLocation": "./.yarn/cache/yallist-npm-3.1.1-a568a556b4-48f7bb00dc.zip/node_modules/yallist/", + "packageDependencies": [ + ["yallist", "npm:3.1.1"] + ], + "linkType": "HARD", + }], + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/yallist-npm-4.0.0-b493d9e907-343617202a.zip/node_modules/yallist/", + "packageDependencies": [ + ["yallist", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["yaml", [ + ["npm:1.10.2", { + "packageLocation": "./.yarn/cache/yaml-npm-1.10.2-0e780aebdf-ce4ada136e.zip/node_modules/yaml/", + "packageDependencies": [ + ["yaml", "npm:1.10.2"] + ], + "linkType": "HARD", + }] + ]], + ["yamljs", [ + ["npm:0.3.0", { + "packageLocation": "./.yarn/cache/yamljs-npm-0.3.0-b0b262e524-76b770d34c.zip/node_modules/yamljs/", + "packageDependencies": [ + ["yamljs", "npm:0.3.0"], + ["argparse", "npm:1.0.10"], + ["glob", "npm:7.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["yargs", [ + ["npm:15.4.1", { + "packageLocation": "./.yarn/cache/yargs-npm-15.4.1-ca1c444de1-40b974f508.zip/node_modules/yargs/", + "packageDependencies": [ + ["yargs", "npm:15.4.1"], + ["cliui", "npm:6.0.0"], + ["decamelize", "npm:1.2.0"], + ["find-up", "npm:4.1.0"], + ["get-caller-file", "npm:2.0.5"], + ["require-directory", "npm:2.1.1"], + ["require-main-filename", "npm:2.0.0"], + ["set-blocking", "npm:2.0.0"], + ["string-width", "npm:4.2.3"], + ["which-module", "npm:2.0.0"], + ["y18n", "npm:4.0.3"], + ["yargs-parser", "npm:18.1.3"] + ], + "linkType": "HARD", + }], + ["npm:16.2.0", { + "packageLocation": "./.yarn/cache/yargs-npm-16.2.0-547873d425-b14afbb51e.zip/node_modules/yargs/", + "packageDependencies": [ + ["yargs", "npm:16.2.0"], + ["cliui", "npm:7.0.4"], + ["escalade", "npm:3.1.1"], + ["get-caller-file", "npm:2.0.5"], + ["require-directory", "npm:2.1.1"], + ["string-width", "npm:4.2.3"], + ["y18n", "npm:5.0.8"], + ["yargs-parser", "npm:20.2.9"] + ], + "linkType": "HARD", + }] + ]], + ["yargs-parser", [ + ["npm:18.1.3", { + "packageLocation": "./.yarn/cache/yargs-parser-npm-18.1.3-0ba9c4f088-60e8c7d1b8.zip/node_modules/yargs-parser/", + "packageDependencies": [ + ["yargs-parser", "npm:18.1.3"], + ["camelcase", "npm:5.3.1"], + ["decamelize", "npm:1.2.0"] + ], + "linkType": "HARD", + }], + ["npm:20.2.4", { + "packageLocation": "./.yarn/cache/yargs-parser-npm-20.2.4-1de20916a6-d251998a37.zip/node_modules/yargs-parser/", + "packageDependencies": [ + ["yargs-parser", "npm:20.2.4"] + ], + "linkType": "HARD", + }], + ["npm:20.2.9", { + "packageLocation": "./.yarn/cache/yargs-parser-npm-20.2.9-a1d19e598d-8bb69015f2.zip/node_modules/yargs-parser/", + "packageDependencies": [ + ["yargs-parser", "npm:20.2.9"] + ], + "linkType": "HARD", + }] + ]], + ["yargs-unparser", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/yargs-unparser-npm-2.0.0-930f3ff3f6-68f9a542c6.zip/node_modules/yargs-unparser/", + "packageDependencies": [ + ["yargs-unparser", "npm:2.0.0"], + ["camelcase", "npm:6.2.1"], + ["decamelize", "npm:4.0.0"], + ["flat", "npm:5.0.2"], + ["is-plain-obj", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["yeoman-environment", [ + ["npm:3.9.1", { + "packageLocation": "./.yarn/cache/yeoman-environment-npm-3.9.1-6ff00ff453-60a19b9962.zip/node_modules/yeoman-environment/", + "packageDependencies": [ + ["yeoman-environment", "npm:3.9.1"] + ], + "linkType": "SOFT", + }], + ["virtual:2547df092054b19ae87906f86214dec0d3c475408d8bf5ca754c4734b3e71161d8ea0542bb5e9d4d900aaec1844ee46ab4983cd497a68035b6ae07fb64e1ee26#npm:3.9.1", { + "packageLocation": "./.yarn/__virtual__/yeoman-environment-virtual-afa2bac4a7/0/cache/yeoman-environment-npm-3.9.1-6ff00ff453-60a19b9962.zip/node_modules/yeoman-environment/", + "packageDependencies": [ + ["yeoman-environment", "virtual:2547df092054b19ae87906f86214dec0d3c475408d8bf5ca754c4734b3e71161d8ea0542bb5e9d4d900aaec1844ee46ab4983cd497a68035b6ae07fb64e1ee26#npm:3.9.1"], + ["@npmcli/arborist", "npm:4.3.1"], + ["@types/mem-fs", null], + ["@types/mem-fs-editor", null], + ["are-we-there-yet", "npm:2.0.0"], + ["arrify", "npm:2.0.1"], + ["binaryextensions", "npm:4.18.0"], + ["chalk", "npm:4.1.2"], + ["cli-table", "npm:0.3.11"], + ["commander", "npm:7.1.0"], + ["dateformat", "npm:4.6.3"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["diff", "npm:5.0.0"], + ["error", "npm:10.4.0"], + ["escape-string-regexp", "npm:4.0.0"], + ["execa", "npm:5.1.1"], + ["find-up", "npm:5.0.0"], + ["globby", "npm:11.1.0"], + ["grouped-queue", "npm:2.0.0"], + ["inquirer", "npm:8.2.0"], + ["is-scoped", "npm:2.1.0"], + ["lodash", "npm:4.17.21"], + ["log-symbols", "npm:4.1.0"], + ["mem-fs", "npm:2.2.1"], + ["mem-fs-editor", "virtual:afa2bac4a722b39c23915b10124591b74c4663d71c6a3f7b65097011d176d90774bf555115519397a9a70486856c408af659df08946fcfcdbe1a8b32c472e211#npm:9.4.0"], + ["minimatch", "npm:3.0.4"], + ["npmlog", "npm:5.0.1"], + ["p-queue", "npm:6.6.2"], + ["p-transform", "npm:1.3.0"], + ["pacote", "npm:12.0.3"], + ["preferred-pm", "npm:3.0.3"], + ["pretty-bytes", "npm:5.6.0"], + ["semver", "npm:7.3.5"], + ["slash", "npm:3.0.0"], + ["strip-ansi", "npm:6.0.1"], + ["text-table", "npm:0.2.0"], + ["textextensions", "npm:5.14.0"], + ["untildify", "npm:4.0.0"] + ], + "packagePeers": [ + "@types/mem-fs-editor", + "@types/mem-fs" + ], + "linkType": "HARD", + }] + ]], + ["yeoman-generator", [ + ["npm:5.6.1", { + "packageLocation": "./.yarn/cache/yeoman-generator-npm-5.6.1-a49b7654c4-ef036210b6.zip/node_modules/yeoman-generator/", + "packageDependencies": [ + ["yeoman-generator", "npm:5.6.1"] + ], + "linkType": "SOFT", + }], + ["virtual:2547df092054b19ae87906f86214dec0d3c475408d8bf5ca754c4734b3e71161d8ea0542bb5e9d4d900aaec1844ee46ab4983cd497a68035b6ae07fb64e1ee26#npm:5.6.1", { + "packageLocation": "./.yarn/__virtual__/yeoman-generator-virtual-66fb1b3800/0/cache/yeoman-generator-npm-5.6.1-a49b7654c4-ef036210b6.zip/node_modules/yeoman-generator/", + "packageDependencies": [ + ["yeoman-generator", "virtual:2547df092054b19ae87906f86214dec0d3c475408d8bf5ca754c4734b3e71161d8ea0542bb5e9d4d900aaec1844ee46ab4983cd497a68035b6ae07fb64e1ee26#npm:5.6.1"], + ["@types/yeoman-environment", null], + ["chalk", "npm:4.1.2"], + ["dargs", "npm:7.0.0"], + ["debug", "virtual:c2bff3e67180802999655a22d390062982690e911b9d9225c258f3b25e7409f3867b2682c16232b77415f560a09d05a95042dc512a5b8c566c42bbbed88b0bbc#npm:4.3.3"], + ["execa", "npm:4.1.0"], + ["github-username", "npm:6.0.0"], + ["lodash", "npm:4.17.21"], + ["minimist", "npm:1.2.5"], + ["read-pkg-up", "npm:7.0.1"], + ["run-async", "npm:2.4.1"], + ["semver", "npm:7.3.5"], + ["shelljs", "npm:0.8.5"], + ["sort-keys", "npm:4.2.0"], + ["text-table", "npm:0.2.0"], + ["yeoman-environment", "virtual:2547df092054b19ae87906f86214dec0d3c475408d8bf5ca754c4734b3e71161d8ea0542bb5e9d4d900aaec1844ee46ab4983cd497a68035b6ae07fb64e1ee26#npm:3.9.1"] + ], + "packagePeers": [ + "@types/yeoman-environment", + "yeoman-environment" + ], + "linkType": "HARD", + }] + ]], + ["yn", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/yn-npm-2.0.0-3ad11617c1-9d49527cb3.zip/node_modules/yn/", + "packageDependencies": [ + ["yn", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["npm:3.1.1", { + "packageLocation": "./.yarn/cache/yn-npm-3.1.1-8ad4259784-2c487b0e14.zip/node_modules/yn/", + "packageDependencies": [ + ["yn", "npm:3.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["yocto-queue", [ + ["npm:0.1.0", { + "packageLocation": "./.yarn/cache/yocto-queue-npm-0.1.0-c6c9a7db29-f77b3d8d00.zip/node_modules/yocto-queue/", + "packageDependencies": [ + ["yocto-queue", "npm:0.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["yosay", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/yosay-npm-2.0.2-50f629c5fa-7e0220ef13.zip/node_modules/yosay/", + "packageDependencies": [ + ["yosay", "npm:2.0.2"], + ["ansi-regex", "npm:2.1.1"], + ["ansi-styles", "npm:3.2.1"], + ["chalk", "npm:1.1.3"], + ["cli-boxes", "npm:1.0.0"], + ["pad-component", "npm:0.0.1"], + ["string-width", "npm:2.1.1"], + ["strip-ansi", "npm:3.0.1"], + ["taketalk", "npm:1.0.0"], + ["wrap-ansi", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["z-schema", [ + ["npm:4.2.4", { + "packageLocation": "./.yarn/cache/z-schema-npm-4.2.4-450fc6608e-9afc0b8d4f.zip/node_modules/z-schema/", + "packageDependencies": [ + ["z-schema", "npm:4.2.4"], + ["commander", "npm:2.20.3"], + ["lodash.get", "npm:4.4.2"], + ["lodash.isequal", "npm:4.5.0"], + ["validator", "npm:13.7.0"] + ], + "linkType": "HARD", + }] + ]], + ["zeromq", [ + ["npm:5.2.8", { + "packageLocation": "./.yarn/unplugged/zeromq-npm-5.2.8-213a0f74bc/node_modules/zeromq/", + "packageDependencies": [ + ["zeromq", "npm:5.2.8"], + ["nan", "npm:2.14.2"], + ["node-gyp", "npm:8.4.0"], + ["node-gyp-build", "npm:4.3.0"] + ], + "linkType": "HARD", + }] + ]] + ] + }, {basePath: basePath || __dirname}); + } + +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["pnpHook"] = factory(); + else + root["pnpHook"] = factory(); +})(global, function() { +return /******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ 368: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +var frozenFs = Object.assign({}, __webpack_require__(747)); +var Module = typeof Module !== "undefined" ? Module : {}; +var moduleOverrides = {}; +var key; +for (key in Module) { + if (Module.hasOwnProperty(key)) { + moduleOverrides[key] = Module[key]; + } +} +var arguments_ = []; +var thisProgram = "./this.program"; +var quit_ = function(status, toThrow) { + throw toThrow; +}; +var ENVIRONMENT_IS_WORKER = false; +var ENVIRONMENT_IS_NODE = true; +var scriptDirectory = ""; +function locateFile(path) { + if (Module["locateFile"]) { + return Module["locateFile"](path, scriptDirectory); + } + return scriptDirectory + path; +} +var read_, readBinary; +var nodeFS; +var nodePath; +if (ENVIRONMENT_IS_NODE) { + if (ENVIRONMENT_IS_WORKER) { + scriptDirectory = __webpack_require__(622).dirname(scriptDirectory) + "/"; + } else { + scriptDirectory = __dirname + "/"; + } + read_ = function shell_read(filename, binary) { + var ret = tryParseAsDataURI(filename); + if (ret) { + return binary ? ret : ret.toString(); + } + if (!nodeFS) nodeFS = frozenFs; + if (!nodePath) nodePath = __webpack_require__(622); + filename = nodePath["normalize"](filename); + return nodeFS["readFileSync"](filename, binary ? null : "utf8"); + }; + readBinary = function readBinary(filename) { + var ret = read_(filename, true); + if (!ret.buffer) { + ret = new Uint8Array(ret); + } + assert(ret.buffer); + return ret; + }; + if (process["argv"].length > 1) { + thisProgram = process["argv"][1].replace(/\\/g, "/"); + } + arguments_ = process["argv"].slice(2); + if (true) { + module["exports"] = Module; + } + quit_ = function(status) { + process["exit"](status); + }; + Module["inspect"] = function() { + return "[Emscripten Module object]"; + }; +} else { +} +var out = Module["print"] || console.log.bind(console); +var err = Module["printErr"] || console.warn.bind(console); +for (key in moduleOverrides) { + if (moduleOverrides.hasOwnProperty(key)) { + Module[key] = moduleOverrides[key]; + } +} +moduleOverrides = null; +if (Module["arguments"]) arguments_ = Module["arguments"]; +if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; +if (Module["quit"]) quit_ = Module["quit"]; +var STACK_ALIGN = 16; +function alignMemory(size, factor) { + if (!factor) factor = STACK_ALIGN; + return Math.ceil(size / factor) * factor; +} +var tempRet0 = 0; +var setTempRet0 = function(value) { + tempRet0 = value; +}; +var wasmBinary; +if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"]; +var noExitRuntime = Module["noExitRuntime"] || true; +if (typeof WebAssembly !== "object") { + abort("no native wasm support detected"); +} +function getValue(ptr, type, noSafe) { + type = type || "i8"; + if (type.charAt(type.length - 1) === "*") type = "i32"; + switch (type) { + case "i1": + return HEAP8[ptr >> 0]; + case "i8": + return HEAP8[ptr >> 0]; + case "i16": + return HEAP16[ptr >> 1]; + case "i32": + return HEAP32[ptr >> 2]; + case "i64": + return HEAP32[ptr >> 2]; + case "float": + return HEAPF32[ptr >> 2]; + case "double": + return HEAPF64[ptr >> 3]; + default: + abort("invalid type for getValue: " + type); + } + return null; +} +var wasmMemory; +var ABORT = false; +var EXITSTATUS; +function assert(condition, text) { + if (!condition) { + abort("Assertion failed: " + text); + } +} +function getCFunc(ident) { + var func = Module["_" + ident]; + assert( + func, + "Cannot call unknown function " + ident + ", make sure it is exported" + ); + return func; +} +function ccall(ident, returnType, argTypes, args, opts) { + var toC = { + string: function(str) { + var ret = 0; + if (str !== null && str !== undefined && str !== 0) { + var len = (str.length << 2) + 1; + ret = stackAlloc(len); + stringToUTF8(str, ret, len); + } + return ret; + }, + array: function(arr) { + var ret = stackAlloc(arr.length); + writeArrayToMemory(arr, ret); + return ret; + } + }; + function convertReturnValue(ret) { + if (returnType === "string") return UTF8ToString(ret); + if (returnType === "boolean") return Boolean(ret); + return ret; + } + var func = getCFunc(ident); + var cArgs = []; + var stack = 0; + if (args) { + for (var i = 0; i < args.length; i++) { + var converter = toC[argTypes[i]]; + if (converter) { + if (stack === 0) stack = stackSave(); + cArgs[i] = converter(args[i]); + } else { + cArgs[i] = args[i]; + } + } + } + var ret = func.apply(null, cArgs); + ret = convertReturnValue(ret); + if (stack !== 0) stackRestore(stack); + return ret; +} +function cwrap(ident, returnType, argTypes, opts) { + argTypes = argTypes || []; + var numericArgs = argTypes.every(function(type) { + return type === "number"; + }); + var numericRet = returnType !== "string"; + if (numericRet && numericArgs && !opts) { + return getCFunc(ident); + } + return function() { + return ccall(ident, returnType, argTypes, arguments, opts); + }; +} +var UTF8Decoder = + typeof TextDecoder !== "undefined" ? new TextDecoder("utf8") : undefined; +function UTF8ArrayToString(heap, idx, maxBytesToRead) { + var endIdx = idx + maxBytesToRead; + var endPtr = idx; + while (heap[endPtr] && !(endPtr >= endIdx)) ++endPtr; + if (endPtr - idx > 16 && heap.subarray && UTF8Decoder) { + return UTF8Decoder.decode(heap.subarray(idx, endPtr)); + } else { + var str = ""; + while (idx < endPtr) { + var u0 = heap[idx++]; + if (!(u0 & 128)) { + str += String.fromCharCode(u0); + continue; + } + var u1 = heap[idx++] & 63; + if ((u0 & 224) == 192) { + str += String.fromCharCode(((u0 & 31) << 6) | u1); + continue; + } + var u2 = heap[idx++] & 63; + if ((u0 & 240) == 224) { + u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; + } else { + u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heap[idx++] & 63); + } + if (u0 < 65536) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 65536; + str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)); + } + } + } + return str; +} +function UTF8ToString(ptr, maxBytesToRead) { + return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ""; +} +function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { + if (!(maxBytesToWrite > 0)) return 0; + var startIdx = outIdx; + var endIdx = outIdx + maxBytesToWrite - 1; + for (var i = 0; i < str.length; ++i) { + var u = str.charCodeAt(i); + if (u >= 55296 && u <= 57343) { + var u1 = str.charCodeAt(++i); + u = (65536 + ((u & 1023) << 10)) | (u1 & 1023); + } + if (u <= 127) { + if (outIdx >= endIdx) break; + heap[outIdx++] = u; + } else if (u <= 2047) { + if (outIdx + 1 >= endIdx) break; + heap[outIdx++] = 192 | (u >> 6); + heap[outIdx++] = 128 | (u & 63); + } else if (u <= 65535) { + if (outIdx + 2 >= endIdx) break; + heap[outIdx++] = 224 | (u >> 12); + heap[outIdx++] = 128 | ((u >> 6) & 63); + heap[outIdx++] = 128 | (u & 63); + } else { + if (outIdx + 3 >= endIdx) break; + heap[outIdx++] = 240 | (u >> 18); + heap[outIdx++] = 128 | ((u >> 12) & 63); + heap[outIdx++] = 128 | ((u >> 6) & 63); + heap[outIdx++] = 128 | (u & 63); + } + } + heap[outIdx] = 0; + return outIdx - startIdx; +} +function stringToUTF8(str, outPtr, maxBytesToWrite) { + return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); +} +function lengthBytesUTF8(str) { + var len = 0; + for (var i = 0; i < str.length; ++i) { + var u = str.charCodeAt(i); + if (u >= 55296 && u <= 57343) + u = (65536 + ((u & 1023) << 10)) | (str.charCodeAt(++i) & 1023); + if (u <= 127) ++len; + else if (u <= 2047) len += 2; + else if (u <= 65535) len += 3; + else len += 4; + } + return len; +} +function allocateUTF8(str) { + var size = lengthBytesUTF8(str) + 1; + var ret = _malloc(size); + if (ret) stringToUTF8Array(str, HEAP8, ret, size); + return ret; +} +function writeArrayToMemory(array, buffer) { + HEAP8.set(array, buffer); +} +function alignUp(x, multiple) { + if (x % multiple > 0) { + x += multiple - (x % multiple); + } + return x; +} +var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64; +function updateGlobalBufferAndViews(buf) { + buffer = buf; + Module["HEAP8"] = HEAP8 = new Int8Array(buf); + Module["HEAP16"] = HEAP16 = new Int16Array(buf); + Module["HEAP32"] = HEAP32 = new Int32Array(buf); + Module["HEAPU8"] = HEAPU8 = new Uint8Array(buf); + Module["HEAPU16"] = HEAPU16 = new Uint16Array(buf); + Module["HEAPU32"] = HEAPU32 = new Uint32Array(buf); + Module["HEAPF32"] = HEAPF32 = new Float32Array(buf); + Module["HEAPF64"] = HEAPF64 = new Float64Array(buf); +} +var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 16777216; +var wasmTable; +var __ATPRERUN__ = []; +var __ATINIT__ = []; +var __ATPOSTRUN__ = []; +var runtimeInitialized = false; +function preRun() { + if (Module["preRun"]) { + if (typeof Module["preRun"] == "function") + Module["preRun"] = [Module["preRun"]]; + while (Module["preRun"].length) { + addOnPreRun(Module["preRun"].shift()); + } + } + callRuntimeCallbacks(__ATPRERUN__); +} +function initRuntime() { + runtimeInitialized = true; + if (!Module["noFSInit"] && !FS.init.initialized) FS.init(); + TTY.init(); + callRuntimeCallbacks(__ATINIT__); +} +function postRun() { + if (Module["postRun"]) { + if (typeof Module["postRun"] == "function") + Module["postRun"] = [Module["postRun"]]; + while (Module["postRun"].length) { + addOnPostRun(Module["postRun"].shift()); + } + } + callRuntimeCallbacks(__ATPOSTRUN__); +} +function addOnPreRun(cb) { + __ATPRERUN__.unshift(cb); +} +function addOnInit(cb) { + __ATINIT__.unshift(cb); +} +function addOnPostRun(cb) { + __ATPOSTRUN__.unshift(cb); +} +var runDependencies = 0; +var runDependencyWatcher = null; +var dependenciesFulfilled = null; +function getUniqueRunDependency(id) { + return id; +} +function addRunDependency(id) { + runDependencies++; + if (Module["monitorRunDependencies"]) { + Module["monitorRunDependencies"](runDependencies); + } +} +function removeRunDependency(id) { + runDependencies--; + if (Module["monitorRunDependencies"]) { + Module["monitorRunDependencies"](runDependencies); + } + if (runDependencies == 0) { + if (runDependencyWatcher !== null) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + } + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); + } + } +} +Module["preloadedImages"] = {}; +Module["preloadedAudios"] = {}; +function abort(what) { + if (Module["onAbort"]) { + Module["onAbort"](what); + } + what += ""; + err(what); + ABORT = true; + EXITSTATUS = 1; + what = "abort(" + what + "). Build with -s ASSERTIONS=1 for more info."; + var e = new WebAssembly.RuntimeError(what); + throw e; +} +var dataURIPrefix = "data:application/octet-stream;base64,"; +function isDataURI(filename) { + return filename.startsWith(dataURIPrefix); +} +var wasmBinaryFile = + "data:application/octet-stream;base64,AGFzbQEAAAABlAInYAF/AX9gA39/fwF/YAF/AGACf38Bf2ACf38AYAV/f39/fwF/YAR/f39/AX9gA39/fwBgBH9+f38Bf2AAAX9gBX9/f35/AX5gA39+fwF/YAF/AX5gAn9+AX9gBH9/fn8BfmADf35/AX5gA39/fgF/YAR/f35/AX9gBn9/f39/fwF/YAR/f39/AGADf39+AX5gAn5/AX9gA398fwBgBH9/f38BfmADf39/AX5gBn98f39/fwF/YAV/f35/fwF/YAV/fn9/fwF/YAV/f39/fwBgAn9+AGACf38BfmACf3wAYAh/fn5/f39+fwF/YAV/f39+fwBgAABgBX5+f35/AX5gAnx/AXxgAn9+AX5gBX9/f39/AX4CeRQBYQFhAAIBYQFiAAABYQFjAAMBYQFkAAYBYQFlAAEBYQFmAAABYQFnAAYBYQFoAAABYQFpAAMBYQFqAAMBYQFrAAMBYQFsAAMBYQFtAAABYQFuAAUBYQFvAAEBYQFwAAMBYQFxAAEBYQFyAAABYQFzAAEBYQF0AAADggKAAgcCAgQAAQECAgANBAQOBwICAhwLEw0AAA0dFAwMAAcCDBAeAgMCAwIAAgEABwgUBBUIBgADAAwABAgIAgEGBgABAB8XAQEDAhMCAwUFEQICIA8GAgMYAQgCAQAABwUBGAAaAxIBAAcEAyERCCIHAQsVAQMABQMDAwAFBAACIwYAAQEAGw0bFw0BBAALCwMDDAwAAwAHJAMBBAgaAQECBQMBAwMABwcHAgICAiURCwgICwEmCQkAAAAKAAIABQAGBgUFBQEDBgYGBRISBgQBAQEAAAIJBgABAA4AAQEPCQABBBkJCQkAAAADCgoBAQIQAAAAAgEDAwkEAQoABQ4AAAkEBQFwAR8fBQcBAYACgIACBgkBfwFB0KDBAgsHvgI8AXUCAAF2AIABAXcAkwIBeADxAQF5AM8BAXoAzQEBQQDLAQFCAMoBAUMAyQEBRADIAQFFAMcBAUYAkgIBRwCRAgFIAI4CAUkA6QEBSgDiAQFLAOEBAUwAPQFNAOABAU4A+gEBTwD5AQFQAPIBAVEA+wEBUgDfAQFTAN4BAVQA3QEBVQDcAQFWAOMBAVcA2wEBWADaAQFZANkBAVoA2AEBXwDXAQEkAOoBAmFhAJwBAmJhANYBAmNhANUBAmRhANQBAmVhADECZmEA6wECZ2EAGwJoYQDOAQJpYQBJAmphANMBAmthANIBAmxhAGgCbWEA0QECbmEA6AECb2EA0AECcGEA5AECcWEAigICcmEA+AECc2EA9wECdGEA9gECdWEA5wECdmEA5gECd2EA5QECeGEAGAJ5YQAVAnphAQAJQQEAQQELHswBkAKNAo8CjAKLArYBiQKIAocChgKFAoQCgwKCAoECgAL/Af4B/QH8AVr1AfQB8wHwAe8B7gHtAewBCq2RCYACQAEBfyMAQRBrIgMgADYCDCADIAE2AgggAyACNgIEIAMoAgwEQCADKAIMIAMoAgg2AgAgAygCDCADKAIENgIECwvMDAEHfwJAIABFDQAgAEEIayIDIABBBGsoAgAiAUF4cSIAaiEFAkAgAUEBcQ0AIAFBA3FFDQEgAyADKAIAIgFrIgNByJsBKAIASQ0BIAAgAWohACADQcybASgCAEcEQCABQf8BTQRAIAMoAggiAiABQQN2IgRBA3RB4JsBakYaIAIgAygCDCIBRgRAQbibAUG4mwEoAgBBfiAEd3E2AgAMAwsgAiABNgIMIAEgAjYCCAwCCyADKAIYIQYCQCADIAMoAgwiAUcEQCADKAIIIgIgATYCDCABIAI2AggMAQsCQCADQRRqIgIoAgAiBA0AIANBEGoiAigCACIEDQBBACEBDAELA0AgAiEHIAQiAUEUaiICKAIAIgQNACABQRBqIQIgASgCECIEDQALIAdBADYCAAsgBkUNAQJAIAMgAygCHCICQQJ0QeidAWoiBCgCAEYEQCAEIAE2AgAgAQ0BQbybAUG8mwEoAgBBfiACd3E2AgAMAwsgBkEQQRQgBigCECADRhtqIAE2AgAgAUUNAgsgASAGNgIYIAMoAhAiAgRAIAEgAjYCECACIAE2AhgLIAMoAhQiAkUNASABIAI2AhQgAiABNgIYDAELIAUoAgQiAUEDcUEDRw0AQcCbASAANgIAIAUgAUF+cTYCBCADIABBAXI2AgQgACADaiAANgIADwsgAyAFTw0AIAUoAgQiAUEBcUUNAAJAIAFBAnFFBEAgBUHQmwEoAgBGBEBB0JsBIAM2AgBBxJsBQcSbASgCACAAaiIANgIAIAMgAEEBcjYCBCADQcybASgCAEcNA0HAmwFBADYCAEHMmwFBADYCAA8LIAVBzJsBKAIARgRAQcybASADNgIAQcCbAUHAmwEoAgAgAGoiADYCACADIABBAXI2AgQgACADaiAANgIADwsgAUF4cSAAaiEAAkAgAUH/AU0EQCAFKAIIIgIgAUEDdiIEQQN0QeCbAWpGGiACIAUoAgwiAUYEQEG4mwFBuJsBKAIAQX4gBHdxNgIADAILIAIgATYCDCABIAI2AggMAQsgBSgCGCEGAkAgBSAFKAIMIgFHBEAgBSgCCCICQcibASgCAEkaIAIgATYCDCABIAI2AggMAQsCQCAFQRRqIgIoAgAiBA0AIAVBEGoiAigCACIEDQBBACEBDAELA0AgAiEHIAQiAUEUaiICKAIAIgQNACABQRBqIQIgASgCECIEDQALIAdBADYCAAsgBkUNAAJAIAUgBSgCHCICQQJ0QeidAWoiBCgCAEYEQCAEIAE2AgAgAQ0BQbybAUG8mwEoAgBBfiACd3E2AgAMAgsgBkEQQRQgBigCECAFRhtqIAE2AgAgAUUNAQsgASAGNgIYIAUoAhAiAgRAIAEgAjYCECACIAE2AhgLIAUoAhQiAkUNACABIAI2AhQgAiABNgIYCyADIABBAXI2AgQgACADaiAANgIAIANBzJsBKAIARw0BQcCbASAANgIADwsgBSABQX5xNgIEIAMgAEEBcjYCBCAAIANqIAA2AgALIABB/wFNBEAgAEEDdiIBQQN0QeCbAWohAAJ/QbibASgCACICQQEgAXQiAXFFBEBBuJsBIAEgAnI2AgAgAAwBCyAAKAIICyECIAAgAzYCCCACIAM2AgwgAyAANgIMIAMgAjYCCA8LQR8hAiADQgA3AhAgAEH///8HTQRAIABBCHYiASABQYD+P2pBEHZBCHEiAXQiAiACQYDgH2pBEHZBBHEiAnQiBCAEQYCAD2pBEHZBAnEiBHRBD3YgASACciAEcmsiAUEBdCAAIAFBFWp2QQFxckEcaiECCyADIAI2AhwgAkECdEHonQFqIQECQAJAAkBBvJsBKAIAIgRBASACdCIHcUUEQEG8mwEgBCAHcjYCACABIAM2AgAgAyABNgIYDAELIABBAEEZIAJBAXZrIAJBH0YbdCECIAEoAgAhAQNAIAEiBCgCBEF4cSAARg0CIAJBHXYhASACQQF0IQIgBCABQQRxaiIHQRBqKAIAIgENAAsgByADNgIQIAMgBDYCGAsgAyADNgIMIAMgAzYCCAwBCyAEKAIIIgAgAzYCDCAEIAM2AgggA0EANgIYIAMgBDYCDCADIAA2AggLQdibAUHYmwEoAgBBAWsiAEF/IAAbNgIACwtCAQF/IwBBEGsiASQAIAEgADYCDCABKAIMBEAgASgCDC0AAUEBcQRAIAEoAgwoAgQQFQsgASgCDBAVCyABQRBqJAALQwEBfyMAQRBrIgIkACACIAA2AgwgAiABNgIIIAIoAgwCfyMAQRBrIgAgAigCCDYCDCAAKAIMQQxqCxBDIAJBEGokAAuiLgEMfyMAQRBrIgwkAAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAAQfQBTQRAQbibASgCACIFQRAgAEELakF4cSAAQQtJGyIIQQN2IgJ2IgFBA3EEQCABQX9zQQFxIAJqIgNBA3QiAUHomwFqKAIAIgRBCGohAAJAIAQoAggiAiABQeCbAWoiAUYEQEG4mwEgBUF+IAN3cTYCAAwBCyACIAE2AgwgASACNgIICyAEIANBA3QiAUEDcjYCBCABIARqIgEgASgCBEEBcjYCBAwNCyAIQcCbASgCACIKTQ0BIAEEQAJAQQIgAnQiAEEAIABrciABIAJ0cSIAQQAgAGtxQQFrIgAgAEEMdkEQcSICdiIBQQV2QQhxIgAgAnIgASAAdiIBQQJ2QQRxIgByIAEgAHYiAUEBdkECcSIAciABIAB2IgFBAXZBAXEiAHIgASAAdmoiA0EDdCIAQeibAWooAgAiBCgCCCIBIABB4JsBaiIARgRAQbibASAFQX4gA3dxIgU2AgAMAQsgASAANgIMIAAgATYCCAsgBEEIaiEAIAQgCEEDcjYCBCAEIAhqIgIgA0EDdCIBIAhrIgNBAXI2AgQgASAEaiADNgIAIAoEQCAKQQN2IgFBA3RB4JsBaiEHQcybASgCACEEAn8gBUEBIAF0IgFxRQRAQbibASABIAVyNgIAIAcMAQsgBygCCAshASAHIAQ2AgggASAENgIMIAQgBzYCDCAEIAE2AggLQcybASACNgIAQcCbASADNgIADA0LQbybASgCACIGRQ0BIAZBACAGa3FBAWsiACAAQQx2QRBxIgJ2IgFBBXZBCHEiACACciABIAB2IgFBAnZBBHEiAHIgASAAdiIBQQF2QQJxIgByIAEgAHYiAUEBdkEBcSIAciABIAB2akECdEHonQFqKAIAIgEoAgRBeHEgCGshAyABIQIDQAJAIAIoAhAiAEUEQCACKAIUIgBFDQELIAAoAgRBeHEgCGsiAiADIAIgA0kiAhshAyAAIAEgAhshASAAIQIMAQsLIAEgCGoiCSABTQ0CIAEoAhghCyABIAEoAgwiBEcEQCABKAIIIgBByJsBKAIASRogACAENgIMIAQgADYCCAwMCyABQRRqIgIoAgAiAEUEQCABKAIQIgBFDQQgAUEQaiECCwNAIAIhByAAIgRBFGoiAigCACIADQAgBEEQaiECIAQoAhAiAA0ACyAHQQA2AgAMCwtBfyEIIABBv39LDQAgAEELaiIAQXhxIQhBvJsBKAIAIglFDQBBACAIayEDAkACQAJAAn9BACAIQYACSQ0AGkEfIAhB////B0sNABogAEEIdiIAIABBgP4/akEQdkEIcSICdCIAIABBgOAfakEQdkEEcSIBdCIAIABBgIAPakEQdkECcSIAdEEPdiABIAJyIAByayIAQQF0IAggAEEVanZBAXFyQRxqCyIFQQJ0QeidAWooAgAiAkUEQEEAIQAMAQtBACEAIAhBAEEZIAVBAXZrIAVBH0YbdCEBA0ACQCACKAIEQXhxIAhrIgcgA08NACACIQQgByIDDQBBACEDIAIhAAwDCyAAIAIoAhQiByAHIAIgAUEddkEEcWooAhAiAkYbIAAgBxshACABQQF0IQEgAg0ACwsgACAEckUEQEECIAV0IgBBACAAa3IgCXEiAEUNAyAAQQAgAGtxQQFrIgAgAEEMdkEQcSICdiIBQQV2QQhxIgAgAnIgASAAdiIBQQJ2QQRxIgByIAEgAHYiAUEBdkECcSIAciABIAB2IgFBAXZBAXEiAHIgASAAdmpBAnRB6J0BaigCACEACyAARQ0BCwNAIAAoAgRBeHEgCGsiASADSSECIAEgAyACGyEDIAAgBCACGyEEIAAoAhAiAQR/IAEFIAAoAhQLIgANAAsLIARFDQAgA0HAmwEoAgAgCGtPDQAgBCAIaiIGIARNDQEgBCgCGCEFIAQgBCgCDCIBRwRAIAQoAggiAEHImwEoAgBJGiAAIAE2AgwgASAANgIIDAoLIARBFGoiAigCACIARQRAIAQoAhAiAEUNBCAEQRBqIQILA0AgAiEHIAAiAUEUaiICKAIAIgANACABQRBqIQIgASgCECIADQALIAdBADYCAAwJCyAIQcCbASgCACICTQRAQcybASgCACEDAkAgAiAIayIBQRBPBEBBwJsBIAE2AgBBzJsBIAMgCGoiADYCACAAIAFBAXI2AgQgAiADaiABNgIAIAMgCEEDcjYCBAwBC0HMmwFBADYCAEHAmwFBADYCACADIAJBA3I2AgQgAiADaiIAIAAoAgRBAXI2AgQLIANBCGohAAwLCyAIQcSbASgCACIGSQRAQcSbASAGIAhrIgE2AgBB0JsBQdCbASgCACICIAhqIgA2AgAgACABQQFyNgIEIAIgCEEDcjYCBCACQQhqIQAMCwtBACEAIAhBL2oiCQJ/QZCfASgCAARAQZifASgCAAwBC0GcnwFCfzcCAEGUnwFCgKCAgICABDcCAEGQnwEgDEEMakFwcUHYqtWqBXM2AgBBpJ8BQQA2AgBB9J4BQQA2AgBBgCALIgFqIgVBACABayIHcSICIAhNDQpB8J4BKAIAIgQEQEHongEoAgAiAyACaiIBIANNDQsgASAESw0LC0H0ngEtAABBBHENBQJAAkBB0JsBKAIAIgMEQEH4ngEhAANAIAMgACgCACIBTwRAIAEgACgCBGogA0sNAwsgACgCCCIADQALC0EAEDwiAUF/Rg0GIAIhBUGUnwEoAgAiA0EBayIAIAFxBEAgAiABayAAIAFqQQAgA2txaiEFCyAFIAhNDQYgBUH+////B0sNBkHwngEoAgAiBARAQeieASgCACIDIAVqIgAgA00NByAAIARLDQcLIAUQPCIAIAFHDQEMCAsgBSAGayAHcSIFQf7///8HSw0FIAUQPCIBIAAoAgAgACgCBGpGDQQgASEACwJAIABBf0YNACAIQTBqIAVNDQBBmJ8BKAIAIgEgCSAFa2pBACABa3EiAUH+////B0sEQCAAIQEMCAsgARA8QX9HBEAgASAFaiEFIAAhAQwIC0EAIAVrEDwaDAULIAAiAUF/Rw0GDAQLAAtBACEEDAcLQQAhAQwFCyABQX9HDQILQfSeAUH0ngEoAgBBBHI2AgALIAJB/v///wdLDQEgAhA8IQFBABA8IQAgAUF/Rg0BIABBf0YNASAAIAFNDQEgACABayIFIAhBKGpNDQELQeieAUHongEoAgAgBWoiADYCAEHsngEoAgAgAEkEQEHsngEgADYCAAsCQAJAAkBB0JsBKAIAIgcEQEH4ngEhAANAIAEgACgCACIDIAAoAgQiAmpGDQIgACgCCCIADQALDAILQcibASgCACIAQQAgACABTRtFBEBByJsBIAE2AgALQQAhAEH8ngEgBTYCAEH4ngEgATYCAEHYmwFBfzYCAEHcmwFBkJ8BKAIANgIAQYSfAUEANgIAA0AgAEEDdCIDQeibAWogA0HgmwFqIgI2AgAgA0HsmwFqIAI2AgAgAEEBaiIAQSBHDQALQcSbASAFQShrIgNBeCABa0EHcUEAIAFBCGpBB3EbIgBrIgI2AgBB0JsBIAAgAWoiADYCACAAIAJBAXI2AgQgASADakEoNgIEQdSbAUGgnwEoAgA2AgAMAgsgAC0ADEEIcQ0AIAMgB0sNACABIAdNDQAgACACIAVqNgIEQdCbASAHQXggB2tBB3FBACAHQQhqQQdxGyIAaiICNgIAQcSbAUHEmwEoAgAgBWoiASAAayIANgIAIAIgAEEBcjYCBCABIAdqQSg2AgRB1JsBQaCfASgCADYCAAwBC0HImwEoAgAgAUsEQEHImwEgATYCAAsgASAFaiECQfieASEAAkACQAJAAkACQAJAA0AgAiAAKAIARwRAIAAoAggiAA0BDAILCyAALQAMQQhxRQ0BC0H4ngEhAANAIAcgACgCACICTwRAIAIgACgCBGoiBCAHSw0DCyAAKAIIIQAMAAsACyAAIAE2AgAgACAAKAIEIAVqNgIEIAFBeCABa0EHcUEAIAFBCGpBB3EbaiIJIAhBA3I2AgQgAkF4IAJrQQdxQQAgAkEIakEHcRtqIgUgCCAJaiIGayECIAUgB0YEQEHQmwEgBjYCAEHEmwFBxJsBKAIAIAJqIgA2AgAgBiAAQQFyNgIEDAMLIAVBzJsBKAIARgRAQcybASAGNgIAQcCbAUHAmwEoAgAgAmoiADYCACAGIABBAXI2AgQgACAGaiAANgIADAMLIAUoAgQiAEEDcUEBRgRAIABBeHEhBwJAIABB/wFNBEAgBSgCCCIDIABBA3YiAEEDdEHgmwFqRhogAyAFKAIMIgFGBEBBuJsBQbibASgCAEF+IAB3cTYCAAwCCyADIAE2AgwgASADNgIIDAELIAUoAhghCAJAIAUgBSgCDCIBRwRAIAUoAggiACABNgIMIAEgADYCCAwBCwJAIAVBFGoiACgCACIDDQAgBUEQaiIAKAIAIgMNAEEAIQEMAQsDQCAAIQQgAyIBQRRqIgAoAgAiAw0AIAFBEGohACABKAIQIgMNAAsgBEEANgIACyAIRQ0AAkAgBSAFKAIcIgNBAnRB6J0BaiIAKAIARgRAIAAgATYCACABDQFBvJsBQbybASgCAEF+IAN3cTYCAAwCCyAIQRBBFCAIKAIQIAVGG2ogATYCACABRQ0BCyABIAg2AhggBSgCECIABEAgASAANgIQIAAgATYCGAsgBSgCFCIARQ0AIAEgADYCFCAAIAE2AhgLIAUgB2ohBSACIAdqIQILIAUgBSgCBEF+cTYCBCAGIAJBAXI2AgQgAiAGaiACNgIAIAJB/wFNBEAgAkEDdiIAQQN0QeCbAWohAgJ/QbibASgCACIBQQEgAHQiAHFFBEBBuJsBIAAgAXI2AgAgAgwBCyACKAIICyEAIAIgBjYCCCAAIAY2AgwgBiACNgIMIAYgADYCCAwDC0EfIQAgAkH///8HTQRAIAJBCHYiACAAQYD+P2pBEHZBCHEiA3QiACAAQYDgH2pBEHZBBHEiAXQiACAAQYCAD2pBEHZBAnEiAHRBD3YgASADciAAcmsiAEEBdCACIABBFWp2QQFxckEcaiEACyAGIAA2AhwgBkIANwIQIABBAnRB6J0BaiEEAkBBvJsBKAIAIgNBASAAdCIBcUUEQEG8mwEgASADcjYCACAEIAY2AgAgBiAENgIYDAELIAJBAEEZIABBAXZrIABBH0YbdCEAIAQoAgAhAQNAIAEiAygCBEF4cSACRg0DIABBHXYhASAAQQF0IQAgAyABQQRxaiIEKAIQIgENAAsgBCAGNgIQIAYgAzYCGAsgBiAGNgIMIAYgBjYCCAwCC0HEmwEgBUEoayIDQXggAWtBB3FBACABQQhqQQdxGyIAayICNgIAQdCbASAAIAFqIgA2AgAgACACQQFyNgIEIAEgA2pBKDYCBEHUmwFBoJ8BKAIANgIAIAcgBEEnIARrQQdxQQAgBEEna0EHcRtqQS9rIgAgACAHQRBqSRsiAkEbNgIEIAJBgJ8BKQIANwIQIAJB+J4BKQIANwIIQYCfASACQQhqNgIAQfyeASAFNgIAQfieASABNgIAQYSfAUEANgIAIAJBGGohAANAIABBBzYCBCAAQQhqIQEgAEEEaiEAIAEgBEkNAAsgAiAHRg0DIAIgAigCBEF+cTYCBCAHIAIgB2siBEEBcjYCBCACIAQ2AgAgBEH/AU0EQCAEQQN2IgBBA3RB4JsBaiECAn9BuJsBKAIAIgFBASAAdCIAcUUEQEG4mwEgACABcjYCACACDAELIAIoAggLIQAgAiAHNgIIIAAgBzYCDCAHIAI2AgwgByAANgIIDAQLQR8hACAHQgA3AhAgBEH///8HTQRAIARBCHYiACAAQYD+P2pBEHZBCHEiAnQiACAAQYDgH2pBEHZBBHEiAXQiACAAQYCAD2pBEHZBAnEiAHRBD3YgASACciAAcmsiAEEBdCAEIABBFWp2QQFxckEcaiEACyAHIAA2AhwgAEECdEHonQFqIQMCQEG8mwEoAgAiAkEBIAB0IgFxRQRAQbybASABIAJyNgIAIAMgBzYCACAHIAM2AhgMAQsgBEEAQRkgAEEBdmsgAEEfRht0IQAgAygCACEBA0AgASICKAIEQXhxIARGDQQgAEEddiEBIABBAXQhACACIAFBBHFqIgMoAhAiAQ0ACyADIAc2AhAgByACNgIYCyAHIAc2AgwgByAHNgIIDAMLIAMoAggiACAGNgIMIAMgBjYCCCAGQQA2AhggBiADNgIMIAYgADYCCAsgCUEIaiEADAULIAIoAggiACAHNgIMIAIgBzYCCCAHQQA2AhggByACNgIMIAcgADYCCAtBxJsBKAIAIgAgCE0NAEHEmwEgACAIayIBNgIAQdCbAUHQmwEoAgAiAiAIaiIANgIAIAAgAUEBcjYCBCACIAhBA3I2AgQgAkEIaiEADAMLQbSbAUEwNgIAQQAhAAwCCwJAIAVFDQACQCAEKAIcIgJBAnRB6J0BaiIAKAIAIARGBEAgACABNgIAIAENAUG8mwEgCUF+IAJ3cSIJNgIADAILIAVBEEEUIAUoAhAgBEYbaiABNgIAIAFFDQELIAEgBTYCGCAEKAIQIgAEQCABIAA2AhAgACABNgIYCyAEKAIUIgBFDQAgASAANgIUIAAgATYCGAsCQCADQQ9NBEAgBCADIAhqIgBBA3I2AgQgACAEaiIAIAAoAgRBAXI2AgQMAQsgBCAIQQNyNgIEIAYgA0EBcjYCBCADIAZqIAM2AgAgA0H/AU0EQCADQQN2IgBBA3RB4JsBaiECAn9BuJsBKAIAIgFBASAAdCIAcUUEQEG4mwEgACABcjYCACACDAELIAIoAggLIQAgAiAGNgIIIAAgBjYCDCAGIAI2AgwgBiAANgIIDAELQR8hACADQf///wdNBEAgA0EIdiIAIABBgP4/akEQdkEIcSICdCIAIABBgOAfakEQdkEEcSIBdCIAIABBgIAPakEQdkECcSIAdEEPdiABIAJyIAByayIAQQF0IAMgAEEVanZBAXFyQRxqIQALIAYgADYCHCAGQgA3AhAgAEECdEHonQFqIQICQAJAIAlBASAAdCIBcUUEQEG8mwEgASAJcjYCACACIAY2AgAgBiACNgIYDAELIANBAEEZIABBAXZrIABBH0YbdCEAIAIoAgAhCANAIAgiASgCBEF4cSADRg0CIABBHXYhAiAAQQF0IQAgASACQQRxaiICKAIQIggNAAsgAiAGNgIQIAYgATYCGAsgBiAGNgIMIAYgBjYCCAwBCyABKAIIIgAgBjYCDCABIAY2AgggBkEANgIYIAYgATYCDCAGIAA2AggLIARBCGohAAwBCwJAIAtFDQACQCABKAIcIgJBAnRB6J0BaiIAKAIAIAFGBEAgACAENgIAIAQNAUG8mwEgBkF+IAJ3cTYCAAwCCyALQRBBFCALKAIQIAFGG2ogBDYCACAERQ0BCyAEIAs2AhggASgCECIABEAgBCAANgIQIAAgBDYCGAsgASgCFCIARQ0AIAQgADYCFCAAIAQ2AhgLAkAgA0EPTQRAIAEgAyAIaiIAQQNyNgIEIAAgAWoiACAAKAIEQQFyNgIEDAELIAEgCEEDcjYCBCAJIANBAXI2AgQgAyAJaiADNgIAIAoEQCAKQQN2IgBBA3RB4JsBaiEEQcybASgCACECAn9BASAAdCIAIAVxRQRAQbibASAAIAVyNgIAIAQMAQsgBCgCCAshACAEIAI2AgggACACNgIMIAIgBDYCDCACIAA2AggLQcybASAJNgIAQcCbASADNgIACyABQQhqIQALIAxBEGokACAAC4MEAQN/IAJBgARPBEAgACABIAIQEhogAA8LIAAgAmohAwJAIAAgAXNBA3FFBEACQCAAQQNxRQRAIAAhAgwBCyACQQFIBEAgACECDAELIAAhAgNAIAIgAS0AADoAACABQQFqIQEgAkEBaiICQQNxRQ0BIAIgA0kNAAsLAkAgA0F8cSIEQcAASQ0AIAIgBEFAaiIFSw0AA0AgAiABKAIANgIAIAIgASgCBDYCBCACIAEoAgg2AgggAiABKAIMNgIMIAIgASgCEDYCECACIAEoAhQ2AhQgAiABKAIYNgIYIAIgASgCHDYCHCACIAEoAiA2AiAgAiABKAIkNgIkIAIgASgCKDYCKCACIAEoAiw2AiwgAiABKAIwNgIwIAIgASgCNDYCNCACIAEoAjg2AjggAiABKAI8NgI8IAFBQGshASACQUBrIgIgBU0NAAsLIAIgBE8NAQNAIAIgASgCADYCACABQQRqIQEgAkEEaiICIARJDQALDAELIANBBEkEQCAAIQIMAQsgACADQQRrIgRLBEAgACECDAELIAAhAgNAIAIgAS0AADoAACACIAEtAAE6AAEgAiABLQACOgACIAIgAS0AAzoAAyABQQRqIQEgAkEEaiICIARNDQALCyACIANJBEADQCACIAEtAAA6AAAgAUEBaiEBIAJBAWoiAiADRw0ACwsgAAu4GAECfyMAQRBrIgQkACAEIAA2AgwgBCABNgIIIAQgAjYCBCAEKAIMIQAgBCgCCCECIAQoAgQhAyMAQSBrIgEkACABIAA2AhggASACNgIUIAEgAzYCEAJAIAEoAhRFBEAgAUEANgIcDAELIAFBATYCDCABLQAMBEAgASgCFCECIAEoAhAhAyMAQSBrIgAgASgCGDYCHCAAIAI2AhggACADNgIUIAAgACgCHDYCECAAIAAoAhBBf3M2AhADQCAAKAIUBH8gACgCGEEDcUEARwVBAAtBAXEEQCAAKAIQIQIgACAAKAIYIgNBAWo2AhggACADLQAAIAJzQf8BcUECdEGQFWooAgAgACgCEEEIdnM2AhAgACAAKAIUQQFrNgIUDAELCyAAIAAoAhg2AgwDQCAAKAIUQSBPBEAgACAAKAIMIgJBBGo2AgwgACACKAIAIAAoAhBzNgIQIAAgACgCEEEYdkECdEGQFWooAgAgACgCEEEQdkH/AXFBAnRBkB1qKAIAIAAoAhBB/wFxQQJ0QZAtaigCACAAKAIQQQh2Qf8BcUECdEGQJWooAgBzc3M2AhAgACAAKAIMIgJBBGo2AgwgACACKAIAIAAoAhBzNgIQIAAgACgCEEEYdkECdEGQFWooAgAgACgCEEEQdkH/AXFBAnRBkB1qKAIAIAAoAhBB/wFxQQJ0QZAtaigCACAAKAIQQQh2Qf8BcUECdEGQJWooAgBzc3M2AhAgACAAKAIMIgJBBGo2AgwgACACKAIAIAAoAhBzNgIQIAAgACgCEEEYdkECdEGQFWooAgAgACgCEEEQdkH/AXFBAnRBkB1qKAIAIAAoAhBB/wFxQQJ0QZAtaigCACAAKAIQQQh2Qf8BcUECdEGQJWooAgBzc3M2AhAgACAAKAIMIgJBBGo2AgwgACACKAIAIAAoAhBzNgIQIAAgACgCEEEYdkECdEGQFWooAgAgACgCEEEQdkH/AXFBAnRBkB1qKAIAIAAoAhBB/wFxQQJ0QZAtaigCACAAKAIQQQh2Qf8BcUECdEGQJWooAgBzc3M2AhAgACAAKAIMIgJBBGo2AgwgACACKAIAIAAoAhBzNgIQIAAgACgCEEEYdkECdEGQFWooAgAgACgCEEEQdkH/AXFBAnRBkB1qKAIAIAAoAhBB/wFxQQJ0QZAtaigCACAAKAIQQQh2Qf8BcUECdEGQJWooAgBzc3M2AhAgACAAKAIMIgJBBGo2AgwgACACKAIAIAAoAhBzNgIQIAAgACgCEEEYdkECdEGQFWooAgAgACgCEEEQdkH/AXFBAnRBkB1qKAIAIAAoAhBB/wFxQQJ0QZAtaigCACAAKAIQQQh2Qf8BcUECdEGQJWooAgBzc3M2AhAgACAAKAIMIgJBBGo2AgwgACACKAIAIAAoAhBzNgIQIAAgACgCEEEYdkECdEGQFWooAgAgACgCEEEQdkH/AXFBAnRBkB1qKAIAIAAoAhBB/wFxQQJ0QZAtaigCACAAKAIQQQh2Qf8BcUECdEGQJWooAgBzc3M2AhAgACAAKAIMIgJBBGo2AgwgACACKAIAIAAoAhBzNgIQIAAgACgCEEEYdkECdEGQFWooAgAgACgCEEEQdkH/AXFBAnRBkB1qKAIAIAAoAhBB/wFxQQJ0QZAtaigCACAAKAIQQQh2Qf8BcUECdEGQJWooAgBzc3M2AhAgACAAKAIUQSBrNgIUDAELCwNAIAAoAhRBBE8EQCAAIAAoAgwiAkEEajYCDCAAIAIoAgAgACgCEHM2AhAgACAAKAIQQRh2QQJ0QZAVaigCACAAKAIQQRB2Qf8BcUECdEGQHWooAgAgACgCEEH/AXFBAnRBkC1qKAIAIAAoAhBBCHZB/wFxQQJ0QZAlaigCAHNzczYCECAAIAAoAhRBBGs2AhQMAQsLIAAgACgCDDYCGCAAKAIUBEADQCAAKAIQIQIgACAAKAIYIgNBAWo2AhggACADLQAAIAJzQf8BcUECdEGQFWooAgAgACgCEEEIdnM2AhAgACAAKAIUQQFrIgI2AhQgAg0ACwsgACAAKAIQQX9zNgIQIAEgACgCEDYCHAwBCyABKAIUIQIgASgCECEDIwBBIGsiACABKAIYNgIcIAAgAjYCGCAAIAM2AhQgACAAKAIcQQh2QYD+A3EgACgCHEEYdmogACgCHEGA/gNxQQh0aiAAKAIcQf8BcUEYdGo2AhAgACAAKAIQQX9zNgIQA0AgACgCFAR/IAAoAhhBA3FBAEcFQQALQQFxBEAgACgCEEEYdiECIAAgACgCGCIDQQFqNgIYIAAgAy0AACACc0ECdEGQNWooAgAgACgCEEEIdHM2AhAgACAAKAIUQQFrNgIUDAELCyAAIAAoAhg2AgwDQCAAKAIUQSBPBEAgACAAKAIMIgJBBGo2AgwgACACKAIAIAAoAhBzNgIQIAAgACgCEEEYdkECdEGQzQBqKAIAIAAoAhBBEHZB/wFxQQJ0QZDFAGooAgAgACgCEEH/AXFBAnRBkDVqKAIAIAAoAhBBCHZB/wFxQQJ0QZA9aigCAHNzczYCECAAIAAoAgwiAkEEajYCDCAAIAIoAgAgACgCEHM2AhAgACAAKAIQQRh2QQJ0QZDNAGooAgAgACgCEEEQdkH/AXFBAnRBkMUAaigCACAAKAIQQf8BcUECdEGQNWooAgAgACgCEEEIdkH/AXFBAnRBkD1qKAIAc3NzNgIQIAAgACgCDCICQQRqNgIMIAAgAigCACAAKAIQczYCECAAIAAoAhBBGHZBAnRBkM0AaigCACAAKAIQQRB2Qf8BcUECdEGQxQBqKAIAIAAoAhBB/wFxQQJ0QZA1aigCACAAKAIQQQh2Qf8BcUECdEGQPWooAgBzc3M2AhAgACAAKAIMIgJBBGo2AgwgACACKAIAIAAoAhBzNgIQIAAgACgCEEEYdkECdEGQzQBqKAIAIAAoAhBBEHZB/wFxQQJ0QZDFAGooAgAgACgCEEH/AXFBAnRBkDVqKAIAIAAoAhBBCHZB/wFxQQJ0QZA9aigCAHNzczYCECAAIAAoAgwiAkEEajYCDCAAIAIoAgAgACgCEHM2AhAgACAAKAIQQRh2QQJ0QZDNAGooAgAgACgCEEEQdkH/AXFBAnRBkMUAaigCACAAKAIQQf8BcUECdEGQNWooAgAgACgCEEEIdkH/AXFBAnRBkD1qKAIAc3NzNgIQIAAgACgCDCICQQRqNgIMIAAgAigCACAAKAIQczYCECAAIAAoAhBBGHZBAnRBkM0AaigCACAAKAIQQRB2Qf8BcUECdEGQxQBqKAIAIAAoAhBB/wFxQQJ0QZA1aigCACAAKAIQQQh2Qf8BcUECdEGQPWooAgBzc3M2AhAgACAAKAIMIgJBBGo2AgwgACACKAIAIAAoAhBzNgIQIAAgACgCEEEYdkECdEGQzQBqKAIAIAAoAhBBEHZB/wFxQQJ0QZDFAGooAgAgACgCEEH/AXFBAnRBkDVqKAIAIAAoAhBBCHZB/wFxQQJ0QZA9aigCAHNzczYCECAAIAAoAgwiAkEEajYCDCAAIAIoAgAgACgCEHM2AhAgACAAKAIQQRh2QQJ0QZDNAGooAgAgACgCEEEQdkH/AXFBAnRBkMUAaigCACAAKAIQQf8BcUECdEGQNWooAgAgACgCEEEIdkH/AXFBAnRBkD1qKAIAc3NzNgIQIAAgACgCFEEgazYCFAwBCwsDQCAAKAIUQQRPBEAgACAAKAIMIgJBBGo2AgwgACACKAIAIAAoAhBzNgIQIAAgACgCEEEYdkECdEGQzQBqKAIAIAAoAhBBEHZB/wFxQQJ0QZDFAGooAgAgACgCEEH/AXFBAnRBkDVqKAIAIAAoAhBBCHZB/wFxQQJ0QZA9aigCAHNzczYCECAAIAAoAhRBBGs2AhQMAQsLIAAgACgCDDYCGCAAKAIUBEADQCAAKAIQQRh2IQIgACAAKAIYIgNBAWo2AhggACADLQAAIAJzQQJ0QZA1aigCACAAKAIQQQh0czYCECAAIAAoAhRBAWsiAjYCFCACDQALCyAAIAAoAhBBf3M2AhAgASAAKAIQQQh2QYD+A3EgACgCEEEYdmogACgCEEGA/gNxQQh0aiAAKAIQQf8BcUEYdGo2AhwLIAEoAhwhACABQSBqJAAgBEEQaiQAIAAL7AIBAn8jAEEQayIBJAAgASAANgIMAkAgASgCDEUNACABKAIMKAIwBEAgASgCDCIAIAAoAjBBAWs2AjALIAEoAgwoAjANACABKAIMKAIgBEAgASgCDEEBNgIgIAEoAgwQMRoLIAEoAgwoAiRBAUYEQCABKAIMEGcLAkAgASgCDCgCLEUNACABKAIMLQAoQQFxDQAgASgCDCECIwBBEGsiACABKAIMKAIsNgIMIAAgAjYCCCAAQQA2AgQDQCAAKAIEIAAoAgwoAkRJBEAgACgCDCgCTCAAKAIEQQJ0aigCACAAKAIIRgRAIAAoAgwoAkwgACgCBEECdGogACgCDCgCTCAAKAIMKAJEQQFrQQJ0aigCADYCACAAKAIMIgAgACgCREEBazYCRAUgACAAKAIEQQFqNgIEDAILCwsLIAEoAgxBAEIAQQUQIRogASgCDCgCAARAIAEoAgwoAgAQGwsgASgCDBAVCyABQRBqJAALnwIBAn8jAEEQayIBJAAgASAANgIMIAEgASgCDCgCHDYCBCABKAIEIQIjAEEQayIAJAAgACACNgIMIAAoAgwQuwEgAEEQaiQAIAEgASgCBCgCFDYCCCABKAIIIAEoAgwoAhBLBEAgASABKAIMKAIQNgIICwJAIAEoAghFDQAgASgCDCgCDCABKAIEKAIQIAEoAggQGRogASgCDCIAIAEoAgggACgCDGo2AgwgASgCBCIAIAEoAgggACgCEGo2AhAgASgCDCIAIAEoAgggACgCFGo2AhQgASgCDCIAIAAoAhAgASgCCGs2AhAgASgCBCIAIAAoAhQgASgCCGs2AhQgASgCBCgCFA0AIAEoAgQgASgCBCgCCDYCEAsgAUEQaiQAC2ABAX8jAEEQayIBJAAgASAANgIIIAEgASgCCEICEB42AgQCQCABKAIERQRAIAFBADsBDgwBCyABIAEoAgQtAAAgASgCBC0AAUEIdGo7AQ4LIAEvAQ4hACABQRBqJAAgAAvpAQEBfyMAQSBrIgIkACACIAA2AhwgAiABNwMQIAIpAxAhASMAQSBrIgAgAigCHDYCGCAAIAE3AxACQAJAAkAgACgCGC0AAEEBcUUNACAAKQMQIAAoAhgpAxAgACkDEHxWDQAgACgCGCkDCCAAKAIYKQMQIAApAxB8Wg0BCyAAKAIYQQA6AAAgAEEANgIcDAELIAAgACgCGCgCBCAAKAIYKQMQp2o2AgwgACAAKAIMNgIcCyACIAAoAhw2AgwgAigCDARAIAIoAhwiACACKQMQIAApAxB8NwMQCyACKAIMIQAgAkEgaiQAIAALbwEBfyMAQRBrIgIkACACIAA2AgggAiABOwEGIAIgAigCCEICEB42AgACQCACKAIARQRAIAJBfzYCDAwBCyACKAIAIAIvAQY6AAAgAigCACACLwEGQQh2OgABIAJBADYCDAsgAigCDBogAkEQaiQAC48BAQF/IwBBEGsiAiQAIAIgADYCCCACIAE2AgQgAiACKAIIQgQQHjYCAAJAIAIoAgBFBEAgAkF/NgIMDAELIAIoAgAgAigCBDoAACACKAIAIAIoAgRBCHY6AAEgAigCACACKAIEQRB2OgACIAIoAgAgAigCBEEYdjoAAyACQQA2AgwLIAIoAgwaIAJBEGokAAu2AgEBfyMAQTBrIgQkACAEIAA2AiQgBCABNgIgIAQgAjcDGCAEIAM2AhQCQCAEKAIkKQMYQgEgBCgCFK2Gg1AEQCAEKAIkQQxqQRxBABAUIARCfzcDKAwBCwJAIAQoAiQoAgBFBEAgBCAEKAIkKAIIIAQoAiAgBCkDGCAEKAIUIAQoAiQoAgQRDgA3AwgMAQsgBCAEKAIkKAIAIAQoAiQoAgggBCgCICAEKQMYIAQoAhQgBCgCJCgCBBEKADcDCAsgBCkDCEIAUwRAAkAgBCgCFEEERg0AIAQoAhRBDkYNAAJAIAQoAiQgBEIIQQQQIUIAUwRAIAQoAiRBDGpBFEEAEBQMAQsgBCgCJEEMaiAEKAIAIAQoAgQQFAsLCyAEIAQpAwg3AygLIAQpAyghAiAEQTBqJAAgAgsXACAALQAAQSBxRQRAIAEgAiAAEHIaCwtQAQF/IwBBEGsiASQAIAEgADYCDANAIAEoAgwEQCABIAEoAgwoAgA2AgggASgCDCgCDBAVIAEoAgwQFSABIAEoAgg2AgwMAQsLIAFBEGokAAt9AQF/IwBBEGsiASQAIAEgADYCDCABKAIMBEAgAUIANwMAA0AgASkDACABKAIMKQMIWkUEQCABKAIMKAIAIAEpAwCnQQR0ahBiIAEgASkDAEIBfDcDAAwBCwsgASgCDCgCABAVIAEoAgwoAigQJSABKAIMEBULIAFBEGokAAs+AQF/IwBBEGsiASQAIAEgADYCDCABKAIMBEAgASgCDCgCABAVIAEoAgwoAgwQFSABKAIMEBULIAFBEGokAAtuAQF/IwBBgAJrIgUkAAJAIARBgMAEcQ0AIAIgA0wNACAFIAFB/wFxIAIgA2siAkGAAiACQYACSSIBGxAyIAFFBEADQCAAIAVBgAIQIiACQYACayICQf8BSw0ACwsgACAFIAIQIgsgBUGAAmokAAvRAQEBfyMAQTBrIgMkACADIAA2AiggAyABNwMgIAMgAjYCHAJAIAMoAigtAChBAXEEQCADQX82AiwMAQsCQCADKAIoKAIgBEAgAygCHEUNASADKAIcQQFGDQEgAygCHEECRg0BCyADKAIoQQxqQRJBABAUIANBfzYCLAwBCyADIAMpAyA3AwggAyADKAIcNgIQIAMoAiggA0EIakIQQQYQIUIAUwRAIANBfzYCLAwBCyADKAIoQQA6ADQgA0EANgIsCyADKAIsIQAgA0EwaiQAIAALmBcBAn8jAEEwayIEJAAgBCAANgIsIAQgATYCKCAEIAI2AiQgBCADNgIgIARBADYCFAJAIAQoAiwoAoQBQQBKBEAgBCgCLCgCACgCLEECRgRAIwBBEGsiACAEKAIsNgIIIABB/4D/n382AgQgAEEANgIAAkADQCAAKAIAQR9MBEACQCAAKAIEQQFxRQ0AIAAoAghBlAFqIAAoAgBBAnRqLwEARQ0AIABBADYCDAwDCyAAIAAoAgBBAWo2AgAgACAAKAIEQQF2NgIEDAELCwJAAkAgACgCCC8BuAENACAAKAIILwG8AQ0AIAAoAggvAcgBRQ0BCyAAQQE2AgwMAQsgAEEgNgIAA0AgACgCAEGAAkgEQCAAKAIIQZQBaiAAKAIAQQJ0ai8BAARAIABBATYCDAwDBSAAIAAoAgBBAWo2AgAMAgsACwsgAEEANgIMCyAAKAIMIQAgBCgCLCgCACAANgIsCyAEKAIsIAQoAixBmBZqEHsgBCgCLCAEKAIsQaQWahB7IAQoAiwhASMAQRBrIgAkACAAIAE2AgwgACgCDCAAKAIMQZQBaiAAKAIMKAKcFhC5ASAAKAIMIAAoAgxBiBNqIAAoAgwoAqgWELkBIAAoAgwgACgCDEGwFmoQeyAAQRI2AggDQAJAIAAoAghBA0gNACAAKAIMQfwUaiAAKAIILQDgbEECdGovAQINACAAIAAoAghBAWs2AggMAQsLIAAoAgwiASABKAKoLSAAKAIIQQNsQRFqajYCqC0gACgCCCEBIABBEGokACAEIAE2AhQgBCAEKAIsKAKoLUEKakEDdjYCHCAEIAQoAiwoAqwtQQpqQQN2NgIYIAQoAhggBCgCHE0EQCAEIAQoAhg2AhwLDAELIAQgBCgCJEEFaiIANgIYIAQgADYCHAsCQAJAIAQoAhwgBCgCJEEEakkNACAEKAIoRQ0AIAQoAiwgBCgCKCAEKAIkIAQoAiAQXAwBCwJAAkAgBCgCLCgCiAFBBEcEQCAEKAIYIAQoAhxHDQELIARBAzYCEAJAIAQoAiwoArwtQRAgBCgCEGtKBEAgBCAEKAIgQQJqNgIMIAQoAiwiACAALwG4LSAEKAIMQf//A3EgBCgCLCgCvC10cjsBuC0gBCgCLC8BuC1B/wFxIQEgBCgCLCgCCCECIAQoAiwiAygCFCEAIAMgAEEBajYCFCAAIAJqIAE6AAAgBCgCLC8BuC1BCHYhASAEKAIsKAIIIQIgBCgCLCIDKAIUIQAgAyAAQQFqNgIUIAAgAmogAToAACAEKAIsIAQoAgxB//8DcUEQIAQoAiwoArwta3U7AbgtIAQoAiwiACAAKAK8LSAEKAIQQRBrajYCvC0MAQsgBCgCLCIAIAAvAbgtIAQoAiBBAmpB//8DcSAEKAIsKAK8LXRyOwG4LSAEKAIsIgAgBCgCECAAKAK8LWo2ArwtCyAEKAIsQZDgAEGQ6QAQugEMAQsgBEEDNgIIAkAgBCgCLCgCvC1BECAEKAIIa0oEQCAEIAQoAiBBBGo2AgQgBCgCLCIAIAAvAbgtIAQoAgRB//8DcSAEKAIsKAK8LXRyOwG4LSAEKAIsLwG4LUH/AXEhASAEKAIsKAIIIQIgBCgCLCIDKAIUIQAgAyAAQQFqNgIUIAAgAmogAToAACAEKAIsLwG4LUEIdiEBIAQoAiwoAgghAiAEKAIsIgMoAhQhACADIABBAWo2AhQgACACaiABOgAAIAQoAiwgBCgCBEH//wNxQRAgBCgCLCgCvC1rdTsBuC0gBCgCLCIAIAAoArwtIAQoAghBEGtqNgK8LQwBCyAEKAIsIgAgAC8BuC0gBCgCIEEEakH//wNxIAQoAiwoArwtdHI7AbgtIAQoAiwiACAEKAIIIAAoArwtajYCvC0LIAQoAiwhASAEKAIsKAKcFkEBaiECIAQoAiwoAqgWQQFqIQMgBCgCFEEBaiEFIwBBQGoiACQAIAAgATYCPCAAIAI2AjggACADNgI0IAAgBTYCMCAAQQU2AigCQCAAKAI8KAK8LUEQIAAoAihrSgRAIAAgACgCOEGBAms2AiQgACgCPCIBIAEvAbgtIAAoAiRB//8DcSAAKAI8KAK8LXRyOwG4LSAAKAI8LwG4LUH/AXEhAiAAKAI8KAIIIQMgACgCPCIFKAIUIQEgBSABQQFqNgIUIAEgA2ogAjoAACAAKAI8LwG4LUEIdiECIAAoAjwoAgghAyAAKAI8IgUoAhQhASAFIAFBAWo2AhQgASADaiACOgAAIAAoAjwgACgCJEH//wNxQRAgACgCPCgCvC1rdTsBuC0gACgCPCIBIAEoArwtIAAoAihBEGtqNgK8LQwBCyAAKAI8IgEgAS8BuC0gACgCOEGBAmtB//8DcSAAKAI8KAK8LXRyOwG4LSAAKAI8IgEgACgCKCABKAK8LWo2ArwtCyAAQQU2AiACQCAAKAI8KAK8LUEQIAAoAiBrSgRAIAAgACgCNEEBazYCHCAAKAI8IgEgAS8BuC0gACgCHEH//wNxIAAoAjwoArwtdHI7AbgtIAAoAjwvAbgtQf8BcSECIAAoAjwoAgghAyAAKAI8IgUoAhQhASAFIAFBAWo2AhQgASADaiACOgAAIAAoAjwvAbgtQQh2IQIgACgCPCgCCCEDIAAoAjwiBSgCFCEBIAUgAUEBajYCFCABIANqIAI6AAAgACgCPCAAKAIcQf//A3FBECAAKAI8KAK8LWt1OwG4LSAAKAI8IgEgASgCvC0gACgCIEEQa2o2ArwtDAELIAAoAjwiASABLwG4LSAAKAI0QQFrQf//A3EgACgCPCgCvC10cjsBuC0gACgCPCIBIAAoAiAgASgCvC1qNgK8LQsgAEEENgIYAkAgACgCPCgCvC1BECAAKAIYa0oEQCAAIAAoAjBBBGs2AhQgACgCPCIBIAEvAbgtIAAoAhRB//8DcSAAKAI8KAK8LXRyOwG4LSAAKAI8LwG4LUH/AXEhAiAAKAI8KAIIIQMgACgCPCIFKAIUIQEgBSABQQFqNgIUIAEgA2ogAjoAACAAKAI8LwG4LUEIdiECIAAoAjwoAgghAyAAKAI8IgUoAhQhASAFIAFBAWo2AhQgASADaiACOgAAIAAoAjwgACgCFEH//wNxQRAgACgCPCgCvC1rdTsBuC0gACgCPCIBIAEoArwtIAAoAhhBEGtqNgK8LQwBCyAAKAI8IgEgAS8BuC0gACgCMEEEa0H//wNxIAAoAjwoArwtdHI7AbgtIAAoAjwiASAAKAIYIAEoArwtajYCvC0LIABBADYCLANAIAAoAiwgACgCMEgEQCAAQQM2AhACQCAAKAI8KAK8LUEQIAAoAhBrSgRAIAAgACgCPEH8FGogACgCLC0A4GxBAnRqLwECNgIMIAAoAjwiASABLwG4LSAAKAIMQf//A3EgACgCPCgCvC10cjsBuC0gACgCPC8BuC1B/wFxIQIgACgCPCgCCCEDIAAoAjwiBSgCFCEBIAUgAUEBajYCFCABIANqIAI6AAAgACgCPC8BuC1BCHYhAiAAKAI8KAIIIQMgACgCPCIFKAIUIQEgBSABQQFqNgIUIAEgA2ogAjoAACAAKAI8IAAoAgxB//8DcUEQIAAoAjwoArwta3U7AbgtIAAoAjwiASABKAK8LSAAKAIQQRBrajYCvC0MAQsgACgCPCIBIAEvAbgtIAAoAjxB/BRqIAAoAiwtAOBsQQJ0ai8BAiAAKAI8KAK8LXRyOwG4LSAAKAI8IgEgACgCECABKAK8LWo2ArwtCyAAIAAoAixBAWo2AiwMAQsLIAAoAjwgACgCPEGUAWogACgCOEEBaxC4ASAAKAI8IAAoAjxBiBNqIAAoAjRBAWsQuAEgAEFAayQAIAQoAiwgBCgCLEGUAWogBCgCLEGIE2oQugELCyAEKAIsEL0BIAQoAiAEQCAEKAIsELwBCyAEQTBqJAAL1AEBAX8jAEEgayICJAAgAiAANgIYIAIgATcDECACIAIoAhhFOgAPAkAgAigCGEUEQCACIAIpAxCnEBgiADYCGCAARQRAIAJBADYCHAwCCwsgAkEYEBgiADYCCCAARQRAIAItAA9BAXEEQCACKAIYEBULIAJBADYCHAwBCyACKAIIQQE6AAAgAigCCCACKAIYNgIEIAIoAgggAikDEDcDCCACKAIIQgA3AxAgAigCCCACLQAPQQFxOgABIAIgAigCCDYCHAsgAigCHCEAIAJBIGokACAAC3gBAX8jAEEQayIBJAAgASAANgIIIAEgASgCCEIEEB42AgQCQCABKAIERQRAIAFBADYCDAwBCyABIAEoAgQtAAAgASgCBC0AASABKAIELQACIAEoAgQtAANBCHRqQQh0akEIdGo2AgwLIAEoAgwhACABQRBqJAAgAAt/AQN/IAAhAQJAIABBA3EEQANAIAEtAABFDQIgAUEBaiIBQQNxDQALCwNAIAEiAkEEaiEBIAIoAgAiA0F/cyADQYGChAhrcUGAgYKEeHFFDQALIANB/wFxRQRAIAIgAGsPCwNAIAItAAEhAyACQQFqIgEhAiADDQALCyABIABrC2EBAX8jAEEQayICIAA2AgggAiABNwMAAkAgAikDACACKAIIKQMIVgRAIAIoAghBADoAACACQX82AgwMAQsgAigCCEEBOgAAIAIoAgggAikDADcDECACQQA2AgwLIAIoAgwL7wEBAX8jAEEgayICJAAgAiAANgIYIAIgATcDECACIAIoAhhCCBAeNgIMAkAgAigCDEUEQCACQX82AhwMAQsgAigCDCACKQMQQv8BgzwAACACKAIMIAIpAxBCCIhC/wGDPAABIAIoAgwgAikDEEIQiEL/AYM8AAIgAigCDCACKQMQQhiIQv8BgzwAAyACKAIMIAIpAxBCIIhC/wGDPAAEIAIoAgwgAikDEEIoiEL/AYM8AAUgAigCDCACKQMQQjCIQv8BgzwABiACKAIMIAIpAxBCOIhC/wGDPAAHIAJBADYCHAsgAigCHBogAkEgaiQAC4cDAQF/IwBBMGsiAyQAIAMgADYCJCADIAE2AiAgAyACNwMYAkAgAygCJC0AKEEBcQRAIANCfzcDKAwBCwJAAkAgAygCJCgCIEUNACADKQMYQv///////////wBWDQAgAykDGFANASADKAIgDQELIAMoAiRBDGpBEkEAEBQgA0J/NwMoDAELIAMoAiQtADVBAXEEQCADQn83AygMAQsCfyMAQRBrIgAgAygCJDYCDCAAKAIMLQA0QQFxCwRAIANCADcDKAwBCyADKQMYUARAIANCADcDKAwBCyADQgA3AxADQCADKQMQIAMpAxhUBEAgAyADKAIkIAMoAiAgAykDEKdqIAMpAxggAykDEH1BARAhIgI3AwggAkIAUwRAIAMoAiRBAToANSADKQMQUARAIANCfzcDKAwECyADIAMpAxA3AygMAwsgAykDCFAEQCADKAIkQQE6ADQFIAMgAykDCCADKQMQfDcDEAwCCwsLIAMgAykDEDcDKAsgAykDKCECIANBMGokACACCzYBAX8jAEEQayIBIAA2AgwCfiABKAIMLQAAQQFxBEAgASgCDCkDCCABKAIMKQMQfQwBC0IACwuyAQIBfwF+IwBBEGsiASQAIAEgADYCBCABIAEoAgRCCBAeNgIAAkAgASgCAEUEQCABQgA3AwgMAQsgASABKAIALQAArSABKAIALQAHrUI4hiABKAIALQAGrUIwhnwgASgCAC0ABa1CKIZ8IAEoAgAtAAStQiCGfCABKAIALQADrUIYhnwgASgCAC0AAq1CEIZ8IAEoAgAtAAGtQgiGfHw3AwgLIAEpAwghAiABQRBqJAAgAgumAQEBfyMAQRBrIgEkACABIAA2AggCQCABKAIIKAIgRQRAIAEoAghBDGpBEkEAEBQgAUF/NgIMDAELIAEoAggiACAAKAIgQQFrNgIgIAEoAggoAiBFBEAgASgCCEEAQgBBAhAhGiABKAIIKAIABEAgASgCCCgCABAxQQBIBEAgASgCCEEMakEUQQAQFAsLCyABQQA2AgwLIAEoAgwhACABQRBqJAAgAAvwAgICfwF+AkAgAkUNACAAIAJqIgNBAWsgAToAACAAIAE6AAAgAkEDSQ0AIANBAmsgAToAACAAIAE6AAEgA0EDayABOgAAIAAgAToAAiACQQdJDQAgA0EEayABOgAAIAAgAToAAyACQQlJDQAgAEEAIABrQQNxIgRqIgMgAUH/AXFBgYKECGwiADYCACADIAIgBGtBfHEiAmoiAUEEayAANgIAIAJBCUkNACADIAA2AgggAyAANgIEIAFBCGsgADYCACABQQxrIAA2AgAgAkEZSQ0AIAMgADYCGCADIAA2AhQgAyAANgIQIAMgADYCDCABQRBrIAA2AgAgAUEUayAANgIAIAFBGGsgADYCACABQRxrIAA2AgAgAiADQQRxQRhyIgFrIgJBIEkNACAArUKBgICAEH4hBSABIANqIQEDQCABIAU3AxggASAFNwMQIAEgBTcDCCABIAU3AwAgAUEgaiEBIAJBIGsiAkEfSw0ACwsL3AEBAX8jAEEQayIBJAAgASAANgIMIAEoAgwEQCABKAIMKAIoBEAgASgCDCgCKEEANgIoIAEoAgwoAihCADcDICABKAIMAn4gASgCDCkDGCABKAIMKQMgVgRAIAEoAgwpAxgMAQsgASgCDCkDIAs3AxgLIAEgASgCDCkDGDcDAANAIAEpAwAgASgCDCkDCFpFBEAgASgCDCgCACABKQMAp0EEdGooAgAQFSABIAEpAwBCAXw3AwAMAQsLIAEoAgwoAgAQFSABKAIMKAIEEBUgASgCDBAVCyABQRBqJAALYAIBfwF+IwBBEGsiASQAIAEgADYCBAJAIAEoAgQoAiRBAUcEQCABKAIEQQxqQRJBABAUIAFCfzcDCAwBCyABIAEoAgRBAEIAQQ0QITcDCAsgASkDCCECIAFBEGokACACC6UCAQJ/IwBBIGsiAyQAIAMgADYCGCADIAE2AhQgAyACNwMIIAMoAhgoAgAhASADKAIUIQQgAykDCCECIwBBIGsiACQAIAAgATYCFCAAIAQ2AhAgACACNwMIAkACQCAAKAIUKAIkQQFGBEAgACkDCEL///////////8AWA0BCyAAKAIUQQxqQRJBABAUIABCfzcDGAwBCyAAIAAoAhQgACgCECAAKQMIQQsQITcDGAsgACkDGCECIABBIGokACADIAI3AwACQCACQgBTBEAgAygCGEEIaiADKAIYKAIAEBcgA0F/NgIcDAELIAMpAwAgAykDCFIEQCADKAIYQQhqQQZBGxAUIANBfzYCHAwBCyADQQA2AhwLIAMoAhwhACADQSBqJAAgAAtrAQF/IwBBIGsiAiAANgIcIAJCASACKAIcrYY3AxAgAkEMaiABNgIAA0AgAiACKAIMIgBBBGo2AgwgAiAAKAIANgIIIAIoAghBAEhFBEAgAiACKQMQQgEgAigCCK2GhDcDEAwBCwsgAikDEAsvAQF/IwBBEGsiASQAIAEgADYCDCABKAIMKAIIEBUgASgCDEEANgIIIAFBEGokAAvNAQEBfyMAQRBrIgIkACACIAA2AgggAiABNgIEAkAgAigCCC0AKEEBcQRAIAJBfzYCDAwBCyACKAIERQRAIAIoAghBDGpBEkEAEBQgAkF/NgIMDAELIAIoAgQQOyACKAIIKAIABEAgAigCCCgCACACKAIEEDhBAEgEQCACKAIIQQxqIAIoAggoAgAQFyACQX82AgwMAgsLIAIoAgggAigCBEI4QQMQIUIAUwRAIAJBfzYCDAwBCyACQQA2AgwLIAIoAgwhACACQRBqJAAgAAsxAQF/IwBBEGsiASQAIAEgADYCDCABKAIMBEAgASgCDBBdIAEoAgwQFQsgAUEQaiQAC98EAQF/IwBBIGsiAiAANgIYIAIgATYCFAJAIAIoAhhFBEAgAkEBNgIcDAELIAIgAigCGCgCADYCDAJAIAIoAhgoAggEQCACIAIoAhgoAgg2AhAMAQsgAkEBNgIQIAJBADYCCANAAkAgAigCCCACKAIYLwEETw0AAkAgAigCDCACKAIIai0AAEEfSwRAIAIoAgwgAigCCGotAABBgAFJDQELIAIoAgwgAigCCGotAABBDUYNACACKAIMIAIoAghqLQAAQQpGDQAgAigCDCACKAIIai0AAEEJRgRADAELIAJBAzYCEAJAIAIoAgwgAigCCGotAABB4AFxQcABRgRAIAJBATYCAAwBCwJAIAIoAgwgAigCCGotAABB8AFxQeABRgRAIAJBAjYCAAwBCwJAIAIoAgwgAigCCGotAABB+AFxQfABRgRAIAJBAzYCAAwBCyACQQQ2AhAMBAsLCyACKAIYLwEEIAIoAgggAigCAGpNBEAgAkEENgIQDAILIAJBATYCBANAIAIoAgQgAigCAE0EQCACKAIMIAIoAgggAigCBGpqLQAAQcABcUGAAUcEQCACQQQ2AhAMBgUgAiACKAIEQQFqNgIEDAILAAsLIAIgAigCACACKAIIajYCCAsgAiACKAIIQQFqNgIIDAELCwsgAigCGCACKAIQNgIIIAIoAhQEQAJAIAIoAhRBAkcNACACKAIQQQNHDQAgAkECNgIQIAIoAhhBAjYCCAsCQCACKAIUIAIoAhBGDQAgAigCEEEBRg0AIAJBBTYCHAwCCwsgAiACKAIQNgIcCyACKAIcC2oBAX8jAEEQayIBIAA2AgwgASgCDEIANwMAIAEoAgxBADYCCCABKAIMQn83AxAgASgCDEEANgIsIAEoAgxBfzYCKCABKAIMQgA3AxggASgCDEIANwMgIAEoAgxBADsBMCABKAIMQQA7ATILUgECf0GQlwEoAgAiASAAQQNqQXxxIgJqIQACQCACQQAgACABTRsNACAAPwBBEHRLBEAgABATRQ0BC0GQlwEgADYCACABDwtBtJsBQTA2AgBBfwuNBQEDfyMAQRBrIgEkACABIAA2AgwgASgCDARAIAEoAgwoAgAEQCABKAIMKAIAEDEaIAEoAgwoAgAQGwsgASgCDCgCHBAVIAEoAgwoAiAQJSABKAIMKAIkECUgASgCDCgCUCECIwBBEGsiACQAIAAgAjYCDCAAKAIMBEAgACgCDCgCEARAIABBADYCCANAIAAoAgggACgCDCgCAEkEQCAAKAIMKAIQIAAoAghBAnRqKAIABEAgACgCDCgCECAAKAIIQQJ0aigCACEDIwBBEGsiAiQAIAIgAzYCDANAIAIoAgwEQCACIAIoAgwoAhg2AgggAigCDBAVIAIgAigCCDYCDAwBCwsgAkEQaiQACyAAIAAoAghBAWo2AggMAQsLIAAoAgwoAhAQFQsgACgCDBAVCyAAQRBqJAAgASgCDCgCQARAIAFCADcDAANAIAEpAwAgASgCDCkDMFQEQCABKAIMKAJAIAEpAwCnQQR0ahBiIAEgASkDAEIBfDcDAAwBCwsgASgCDCgCQBAVCyABQgA3AwADQCABKQMAIAEoAgwoAkStVARAIAEoAgwoAkwgASkDAKdBAnRqKAIAIQIjAEEQayIAJAAgACACNgIMIAAoAgxBAToAKAJ/IwBBEGsiAiAAKAIMQQxqNgIMIAIoAgwoAgBFCwRAIAAoAgxBDGpBCEEAEBQLIABBEGokACABIAEpAwBCAXw3AwAMAQsLIAEoAgwoAkwQFSABKAIMKAJUIQIjAEEQayIAJAAgACACNgIMIAAoAgwEQCAAKAIMKAIIBEAgACgCDCgCDCAAKAIMKAIIEQIACyAAKAIMEBULIABBEGokACABKAIMQQhqEDcgASgCDBAVCyABQRBqJAALjw4BAX8jAEEQayIDJAAgAyAANgIMIAMgATYCCCADIAI2AgQgAygCCCEBIAMoAgQhAiMAQSBrIgAgAygCDDYCGCAAIAE2AhQgACACNgIQIAAgACgCGEEQdjYCDCAAIAAoAhhB//8DcTYCGAJAIAAoAhBBAUYEQCAAIAAoAhQtAAAgACgCGGo2AhggACgCGEHx/wNPBEAgACAAKAIYQfH/A2s2AhgLIAAgACgCGCAAKAIMajYCDCAAKAIMQfH/A08EQCAAIAAoAgxB8f8DazYCDAsgACAAKAIYIAAoAgxBEHRyNgIcDAELIAAoAhRFBEAgAEEBNgIcDAELIAAoAhBBEEkEQANAIAAgACgCECIBQQFrNgIQIAEEQCAAIAAoAhQiAUEBajYCFCAAIAEtAAAgACgCGGo2AhggACAAKAIYIAAoAgxqNgIMDAELCyAAKAIYQfH/A08EQCAAIAAoAhhB8f8DazYCGAsgACAAKAIMQfH/A3A2AgwgACAAKAIYIAAoAgxBEHRyNgIcDAELA0AgACgCEEGwK08EQCAAIAAoAhBBsCtrNgIQIABB2wI2AggDQCAAIAAoAhQtAAAgACgCGGo2AhggACAAKAIYIAAoAgxqNgIMIAAgACgCFC0AASAAKAIYajYCGCAAIAAoAhggACgCDGo2AgwgACAAKAIULQACIAAoAhhqNgIYIAAgACgCGCAAKAIMajYCDCAAIAAoAhQtAAMgACgCGGo2AhggACAAKAIYIAAoAgxqNgIMIAAgACgCFC0ABCAAKAIYajYCGCAAIAAoAhggACgCDGo2AgwgACAAKAIULQAFIAAoAhhqNgIYIAAgACgCGCAAKAIMajYCDCAAIAAoAhQtAAYgACgCGGo2AhggACAAKAIYIAAoAgxqNgIMIAAgACgCFC0AByAAKAIYajYCGCAAIAAoAhggACgCDGo2AgwgACAAKAIULQAIIAAoAhhqNgIYIAAgACgCGCAAKAIMajYCDCAAIAAoAhQtAAkgACgCGGo2AhggACAAKAIYIAAoAgxqNgIMIAAgACgCFC0ACiAAKAIYajYCGCAAIAAoAhggACgCDGo2AgwgACAAKAIULQALIAAoAhhqNgIYIAAgACgCGCAAKAIMajYCDCAAIAAoAhQtAAwgACgCGGo2AhggACAAKAIYIAAoAgxqNgIMIAAgACgCFC0ADSAAKAIYajYCGCAAIAAoAhggACgCDGo2AgwgACAAKAIULQAOIAAoAhhqNgIYIAAgACgCGCAAKAIMajYCDCAAIAAoAhQtAA8gACgCGGo2AhggACAAKAIYIAAoAgxqNgIMIAAgACgCFEEQajYCFCAAIAAoAghBAWsiATYCCCABDQALIAAgACgCGEHx/wNwNgIYIAAgACgCDEHx/wNwNgIMDAELCyAAKAIQBEADQCAAKAIQQRBPBEAgACAAKAIQQRBrNgIQIAAgACgCFC0AACAAKAIYajYCGCAAIAAoAhggACgCDGo2AgwgACAAKAIULQABIAAoAhhqNgIYIAAgACgCGCAAKAIMajYCDCAAIAAoAhQtAAIgACgCGGo2AhggACAAKAIYIAAoAgxqNgIMIAAgACgCFC0AAyAAKAIYajYCGCAAIAAoAhggACgCDGo2AgwgACAAKAIULQAEIAAoAhhqNgIYIAAgACgCGCAAKAIMajYCDCAAIAAoAhQtAAUgACgCGGo2AhggACAAKAIYIAAoAgxqNgIMIAAgACgCFC0ABiAAKAIYajYCGCAAIAAoAhggACgCDGo2AgwgACAAKAIULQAHIAAoAhhqNgIYIAAgACgCGCAAKAIMajYCDCAAIAAoAhQtAAggACgCGGo2AhggACAAKAIYIAAoAgxqNgIMIAAgACgCFC0ACSAAKAIYajYCGCAAIAAoAhggACgCDGo2AgwgACAAKAIULQAKIAAoAhhqNgIYIAAgACgCGCAAKAIMajYCDCAAIAAoAhQtAAsgACgCGGo2AhggACAAKAIYIAAoAgxqNgIMIAAgACgCFC0ADCAAKAIYajYCGCAAIAAoAhggACgCDGo2AgwgACAAKAIULQANIAAoAhhqNgIYIAAgACgCGCAAKAIMajYCDCAAIAAoAhQtAA4gACgCGGo2AhggACAAKAIYIAAoAgxqNgIMIAAgACgCFC0ADyAAKAIYajYCGCAAIAAoAhggACgCDGo2AgwgACAAKAIUQRBqNgIUDAELCwNAIAAgACgCECIBQQFrNgIQIAEEQCAAIAAoAhQiAUEBajYCFCAAIAEtAAAgACgCGGo2AhggACAAKAIYIAAoAgxqNgIMDAELCyAAIAAoAhhB8f8DcDYCGCAAIAAoAgxB8f8DcDYCDAsgACAAKAIYIAAoAgxBEHRyNgIcCyAAKAIcIQAgA0EQaiQAIAALhAEBAX8jAEEQayIBJAAgASAANgIIIAFB2AAQGCIANgIEAkAgAEUEQCABQQA2AgwMAQsCQCABKAIIBEAgASgCBCABKAIIQdgAEBkaDAELIAEoAgQQTwsgASgCBEEANgIAIAEoAgRBAToABSABIAEoAgQ2AgwLIAEoAgwhACABQRBqJAAgAAtvAQF/IwBBIGsiAyQAIAMgADYCGCADIAE2AhQgAyACNgIQIAMgAygCGCADKAIQrRAeNgIMAkAgAygCDEUEQCADQX82AhwMAQsgAygCDCADKAIUIAMoAhAQGRogA0EANgIcCyADKAIcGiADQSBqJAALogEBAX8jAEEgayIEJAAgBCAANgIYIAQgATcDECAEIAI2AgwgBCADNgIIIAQgBCgCDCAEKQMQECkiADYCBAJAIABFBEAgBCgCCEEOQQAQFCAEQQA2AhwMAQsgBCgCGCAEKAIEKAIEIAQpAxAgBCgCCBBhQQBIBEAgBCgCBBAWIARBADYCHAwBCyAEIAQoAgQ2AhwLIAQoAhwhACAEQSBqJAAgAAugAQEBfyMAQSBrIgMkACADIAA2AhQgAyABNgIQIAMgAjcDCCADIAMoAhA2AgQCQCADKQMIQghUBEAgA0J/NwMYDAELIwBBEGsiACADKAIUNgIMIAAoAgwoAgAhACADKAIEIAA2AgAjAEEQayIAIAMoAhQ2AgwgACgCDCgCBCEAIAMoAgQgADYCBCADQgg3AxgLIAMpAxghAiADQSBqJAAgAgs/AQF/IwBBEGsiAiAANgIMIAIgATYCCCACKAIMBEAgAigCDCACKAIIKAIANgIAIAIoAgwgAigCCCgCBDYCBAsLgwECA38BfgJAIABCgICAgBBUBEAgACEFDAELA0AgAUEBayIBIAAgAEIKgCIFQgp+fadBMHI6AAAgAEL/////nwFWIQIgBSEAIAINAAsLIAWnIgIEQANAIAFBAWsiASACIAJBCm4iA0EKbGtBMHI6AAAgAkEJSyEEIAMhAiAEDQALCyABC7wCAQF/IwBBIGsiBCQAIAQgADYCGCAEIAE3AxAgBCACNgIMIAQgAzYCCCAEKAIIRQRAIAQgBCgCGEEIajYCCAsCQCAEKQMQIAQoAhgpAzBaBEAgBCgCCEESQQAQFCAEQQA2AhwMAQsCQCAEKAIMQQhxRQRAIAQoAhgoAkAgBCkDEKdBBHRqKAIEDQELIAQoAhgoAkAgBCkDEKdBBHRqKAIARQRAIAQoAghBEkEAEBQgBEEANgIcDAILAkAgBCgCGCgCQCAEKQMQp0EEdGotAAxBAXFFDQAgBCgCDEEIcQ0AIAQoAghBF0EAEBQgBEEANgIcDAILIAQgBCgCGCgCQCAEKQMQp0EEdGooAgA2AhwMAQsgBCAEKAIYKAJAIAQpAxCnQQR0aigCBDYCHAsgBCgCHCEAIARBIGokACAAC9kIAQJ/IwBBIGsiBCQAIAQgADYCGCAEIAE2AhQgBCACNgIQIAQgAzYCDAJAIAQoAhhFBEAgBCgCFARAIAQoAhRBADYCAAsgBEGQ2QA2AhwMAQsgBCgCEEHAAHFFBEAgBCgCGCgCCEUEQCAEKAIYQQAQOhoLAkACQAJAIAQoAhBBgAFxRQ0AIAQoAhgoAghBAUYNACAEKAIYKAIIQQJHDQELIAQoAhgoAghBBEcNAQsgBCgCGCgCDEUEQCAEKAIYKAIAIQEgBCgCGC8BBCECIAQoAhhBEGohAyAEKAIMIQUjAEEwayIAJAAgACABNgIoIAAgAjYCJCAAIAM2AiAgACAFNgIcIAAgACgCKDYCGAJAIAAoAiRFBEAgACgCIARAIAAoAiBBADYCAAsgAEEANgIsDAELIABBATYCECAAQQA2AgwDQCAAKAIMIAAoAiRJBEAjAEEQayIBIAAoAhggACgCDGotAABBAXRBkNUAai8BADYCCAJAIAEoAghBgAFJBEAgAUEBNgIMDAELIAEoAghBgBBJBEAgAUECNgIMDAELIAEoAghBgIAESQRAIAFBAzYCDAwBCyABQQQ2AgwLIAAgASgCDCAAKAIQajYCECAAIAAoAgxBAWo2AgwMAQsLIAAgACgCEBAYIgE2AhQgAUUEQCAAKAIcQQ5BABAUIABBADYCLAwBCyAAQQA2AgggAEEANgIMA0AgACgCDCAAKAIkSQRAIAAoAhQgACgCCGohAiMAQRBrIgEgACgCGCAAKAIMai0AAEEBdEGQ1QBqLwEANgIIIAEgAjYCBAJAIAEoAghBgAFJBEAgASgCBCABKAIIOgAAIAFBATYCDAwBCyABKAIIQYAQSQRAIAEoAgQgASgCCEEGdkEfcUHAAXI6AAAgASgCBCABKAIIQT9xQYABcjoAASABQQI2AgwMAQsgASgCCEGAgARJBEAgASgCBCABKAIIQQx2QQ9xQeABcjoAACABKAIEIAEoAghBBnZBP3FBgAFyOgABIAEoAgQgASgCCEE/cUGAAXI6AAIgAUEDNgIMDAELIAEoAgQgASgCCEESdkEHcUHwAXI6AAAgASgCBCABKAIIQQx2QT9xQYABcjoAASABKAIEIAEoAghBBnZBP3FBgAFyOgACIAEoAgQgASgCCEE/cUGAAXI6AAMgAUEENgIMCyAAIAEoAgwgACgCCGo2AgggACAAKAIMQQFqNgIMDAELCyAAKAIUIAAoAhBBAWtqQQA6AAAgACgCIARAIAAoAiAgACgCEEEBazYCAAsgACAAKAIUNgIsCyAAKAIsIQEgAEEwaiQAIAEhACAEKAIYIAA2AgwgAEUEQCAEQQA2AhwMBAsLIAQoAhQEQCAEKAIUIAQoAhgoAhA2AgALIAQgBCgCGCgCDDYCHAwCCwsgBCgCFARAIAQoAhQgBCgCGC8BBDYCAAsgBCAEKAIYKAIANgIcCyAEKAIcIQAgBEEgaiQAIAALOQEBfyMAQRBrIgEgADYCDEEAIQAgASgCDC0AAEEBcQR/IAEoAgwpAxAgASgCDCkDCFEFQQALQQFxC5wIAQt/IABFBEAgARAYDwsgAUFATwRAQbSbAUEwNgIAQQAPCwJ/QRAgAUELakF4cSABQQtJGyEGIABBCGsiBSgCBCIJQXhxIQQCQCAJQQNxRQRAQQAgBkGAAkkNAhogBkEEaiAETQRAIAUhAiAEIAZrQZifASgCAEEBdE0NAgtBAAwCCyAEIAVqIQcCQCAEIAZPBEAgBCAGayIDQRBJDQEgBSAJQQFxIAZyQQJyNgIEIAUgBmoiAiADQQNyNgIEIAcgBygCBEEBcjYCBCACIAMQrAEMAQsgB0HQmwEoAgBGBEBBxJsBKAIAIARqIgQgBk0NAiAFIAlBAXEgBnJBAnI2AgQgBSAGaiIDIAQgBmsiAkEBcjYCBEHEmwEgAjYCAEHQmwEgAzYCAAwBCyAHQcybASgCAEYEQEHAmwEoAgAgBGoiAyAGSQ0CAkAgAyAGayICQRBPBEAgBSAJQQFxIAZyQQJyNgIEIAUgBmoiBCACQQFyNgIEIAMgBWoiAyACNgIAIAMgAygCBEF+cTYCBAwBCyAFIAlBAXEgA3JBAnI2AgQgAyAFaiICIAIoAgRBAXI2AgRBACECQQAhBAtBzJsBIAQ2AgBBwJsBIAI2AgAMAQsgBygCBCIDQQJxDQEgA0F4cSAEaiIKIAZJDQEgCiAGayEMAkAgA0H/AU0EQCAHKAIIIgQgA0EDdiICQQN0QeCbAWpGGiAEIAcoAgwiA0YEQEG4mwFBuJsBKAIAQX4gAndxNgIADAILIAQgAzYCDCADIAQ2AggMAQsgBygCGCELAkAgByAHKAIMIghHBEAgBygCCCICQcibASgCAEkaIAIgCDYCDCAIIAI2AggMAQsCQCAHQRRqIgQoAgAiAg0AIAdBEGoiBCgCACICDQBBACEIDAELA0AgBCEDIAIiCEEUaiIEKAIAIgINACAIQRBqIQQgCCgCECICDQALIANBADYCAAsgC0UNAAJAIAcgBygCHCIDQQJ0QeidAWoiAigCAEYEQCACIAg2AgAgCA0BQbybAUG8mwEoAgBBfiADd3E2AgAMAgsgC0EQQRQgCygCECAHRhtqIAg2AgAgCEUNAQsgCCALNgIYIAcoAhAiAgRAIAggAjYCECACIAg2AhgLIAcoAhQiAkUNACAIIAI2AhQgAiAINgIYCyAMQQ9NBEAgBSAJQQFxIApyQQJyNgIEIAUgCmoiAiACKAIEQQFyNgIEDAELIAUgCUEBcSAGckECcjYCBCAFIAZqIgMgDEEDcjYCBCAFIApqIgIgAigCBEEBcjYCBCADIAwQrAELIAUhAgsgAgsiAgRAIAJBCGoPCyABEBgiBUUEQEEADwsgBSAAQXxBeCAAQQRrKAIAIgJBA3EbIAJBeHFqIgIgASABIAJLGxAZGiAAEBUgBQvvAgEBfyMAQRBrIgEkACABIAA2AggCQCABKAIILQAoQQFxBEAgAUF/NgIMDAELIAEoAggoAiRBA0YEQCABKAIIQQxqQRdBABAUIAFBfzYCDAwBCwJAIAEoAggoAiAEQAJ/IwBBEGsiACABKAIINgIMIAAoAgwpAxhCwACDUAsEQCABKAIIQQxqQR1BABAUIAFBfzYCDAwDCwwBCyABKAIIKAIABEAgASgCCCgCABBJQQBIBEAgASgCCEEMaiABKAIIKAIAEBcgAUF/NgIMDAMLCyABKAIIQQBCAEEAECFCAFMEQCABKAIIKAIABEAgASgCCCgCABAxGgsgAUF/NgIMDAILCyABKAIIQQA6ADQgASgCCEEAOgA1IwBBEGsiACABKAIIQQxqNgIMIAAoAgwEQCAAKAIMQQA2AgAgACgCDEEANgIECyABKAIIIgAgACgCIEEBajYCICABQQA2AgwLIAEoAgwhACABQRBqJAAgAAt1AgF/AX4jAEEQayIBJAAgASAANgIEAkAgASgCBC0AKEEBcQRAIAFCfzcDCAwBCyABKAIEKAIgRQRAIAEoAgRBDGpBEkEAEBQgAUJ/NwMIDAELIAEgASgCBEEAQgBBBxAhNwMICyABKQMIIQIgAUEQaiQAIAILnQEBAX8jAEEQayIBIAA2AggCQAJAAkAgASgCCEUNACABKAIIKAIgRQ0AIAEoAggoAiQNAQsgAUEBNgIMDAELIAEgASgCCCgCHDYCBAJAAkAgASgCBEUNACABKAIEKAIAIAEoAghHDQAgASgCBCgCBEG0/gBJDQAgASgCBCgCBEHT/gBNDQELIAFBATYCDAwBCyABQQA2AgwLIAEoAgwLgAEBA38jAEEQayICIAA2AgwgAiABNgIIIAIoAghBCHYhASACKAIMKAIIIQMgAigCDCIEKAIUIQAgBCAAQQFqNgIUIAAgA2ogAToAACACKAIIQf8BcSEBIAIoAgwoAgghAyACKAIMIgIoAhQhACACIABBAWo2AhQgACADaiABOgAAC5kFAQF/IwBBQGoiBCQAIAQgADYCOCAEIAE3AzAgBCACNgIsIAQgAzYCKCAEQcgAEBgiADYCJAJAIABFBEAgBEEANgI8DAELIAQoAiRCADcDOCAEKAIkQgA3AxggBCgCJEIANwMwIAQoAiRBADYCACAEKAIkQQA2AgQgBCgCJEIANwMIIAQoAiRCADcDECAEKAIkQQA2AiggBCgCJEIANwMgAkAgBCkDMFAEQEEIEBghACAEKAIkIAA2AgQgAEUEQCAEKAIkEBUgBCgCKEEOQQAQFCAEQQA2AjwMAwsgBCgCJCgCBEIANwMADAELIAQoAiQgBCkDMEEAEMEBQQFxRQRAIAQoAihBDkEAEBQgBCgCJBAzIARBADYCPAwCCyAEQgA3AwggBEIANwMYIARCADcDEANAIAQpAxggBCkDMFQEQCAEKAI4IAQpAxinQQR0aikDCFBFBEAgBCgCOCAEKQMYp0EEdGooAgBFBEAgBCgCKEESQQAQFCAEKAIkEDMgBEEANgI8DAULIAQoAiQoAgAgBCkDEKdBBHRqIAQoAjggBCkDGKdBBHRqKAIANgIAIAQoAiQoAgAgBCkDEKdBBHRqIAQoAjggBCkDGKdBBHRqKQMINwMIIAQoAiQoAgQgBCkDGKdBA3RqIAQpAwg3AwAgBCAEKAI4IAQpAxinQQR0aikDCCAEKQMIfDcDCCAEIAQpAxBCAXw3AxALIAQgBCkDGEIBfDcDGAwBCwsgBCgCJCAEKQMQNwMIIAQoAiQgBCgCLAR+QgAFIAQoAiQpAwgLNwMYIAQoAiQoAgQgBCgCJCkDCKdBA3RqIAQpAwg3AwAgBCgCJCAEKQMINwMwCyAEIAQoAiQ2AjwLIAQoAjwhACAEQUBrJAAgAAueAQEBfyMAQSBrIgQkACAEIAA2AhggBCABNwMQIAQgAjYCDCAEIAM2AgggBCAEKAIYIAQpAxAgBCgCDCAEKAIIEEUiADYCBAJAIABFBEAgBEEANgIcDAELIAQgBCgCBCgCMEEAIAQoAgwgBCgCCBBGIgA2AgAgAEUEQCAEQQA2AhwMAQsgBCAEKAIANgIcCyAEKAIcIQAgBEEgaiQAIAAL8QEBAX8jAEEQayIBIAA2AgwgASgCDEEANgIAIAEoAgxBADoABCABKAIMQQA6AAUgASgCDEEBOgAGIAEoAgxBvwY7AQggASgCDEEKOwEKIAEoAgxBADsBDCABKAIMQX82AhAgASgCDEEANgIUIAEoAgxBADYCGCABKAIMQgA3AyAgASgCDEIANwMoIAEoAgxBADYCMCABKAIMQQA2AjQgASgCDEEANgI4IAEoAgxBADYCPCABKAIMQQA7AUAgASgCDEGAgNiNeDYCRCABKAIMQgA3A0ggASgCDEEAOwFQIAEoAgxBADsBUiABKAIMQQA2AlQL0hMBAX8jAEGwAWsiAyQAIAMgADYCqAEgAyABNgKkASADIAI2AqABIANBADYCkAEgAyADKAKkASgCMEEAEDo2ApQBIAMgAygCpAEoAjhBABA6NgKYAQJAAkACQAJAIAMoApQBQQJGBEAgAygCmAFBAUYNAQsgAygClAFBAUYEQCADKAKYAUECRg0BCyADKAKUAUECRw0BIAMoApgBQQJHDQELIAMoAqQBIgAgAC8BDEGAEHI7AQwMAQsgAygCpAEiACAALwEMQf/vA3E7AQwgAygClAFBAkYEQCADQfXgASADKAKkASgCMCADKAKoAUEIahCCATYCkAEgAygCkAFFBEAgA0F/NgKsAQwDCwsCQCADKAKgAUGAAnENACADKAKYAUECRw0AIANB9cYBIAMoAqQBKAI4IAMoAqgBQQhqEIIBNgJIIAMoAkhFBEAgAygCkAEQIyADQX82AqwBDAMLIAMoAkggAygCkAE2AgAgAyADKAJINgKQAQsLAkAgAygCpAEvAVJFBEAgAygCpAEiACAALwEMQf7/A3E7AQwMAQsgAygCpAEiACAALwEMQQFyOwEMCyADIAMoAqQBIAMoAqABEF5BAXE6AIYBIAMgAygCoAFBgApxQYAKRwR/IAMtAIYBBUEBC0EBcToAhwEgAwJ/QQEgAygCpAEvAVJBgQJGDQAaQQEgAygCpAEvAVJBggJGDQAaIAMoAqQBLwFSQYMCRgtBAXE6AIUBIAMtAIcBQQFxBEAgAyADQSBqQhwQKTYCHCADKAIcRQRAIAMoAqgBQQhqQQ5BABAUIAMoApABECMgA0F/NgKsAQwCCwJAIAMoAqABQYACcQRAAkAgAygCoAFBgAhxDQAgAygCpAEpAyBC/////w9WDQAgAygCpAEpAyhC/////w9YDQILIAMoAhwgAygCpAEpAygQLSADKAIcIAMoAqQBKQMgEC0MAQsCQAJAIAMoAqABQYAIcQ0AIAMoAqQBKQMgQv////8PVg0AIAMoAqQBKQMoQv////8PVg0AIAMoAqQBKQNIQv////8PWA0BCyADKAKkASkDKEL/////D1oEQCADKAIcIAMoAqQBKQMoEC0LIAMoAqQBKQMgQv////8PWgRAIAMoAhwgAygCpAEpAyAQLQsgAygCpAEpA0hC/////w9aBEAgAygCHCADKAKkASkDSBAtCwsLAn8jAEEQayIAIAMoAhw2AgwgACgCDC0AAEEBcUULBEAgAygCqAFBCGpBFEEAEBQgAygCHBAWIAMoApABECMgA0F/NgKsAQwCCyADQQECfyMAQRBrIgAgAygCHDYCDAJ+IAAoAgwtAABBAXEEQCAAKAIMKQMQDAELQgALp0H//wNxCyADQSBqQYAGEFE2AowBIAMoAhwQFiADKAKMASADKAKQATYCACADIAMoAowBNgKQAQsgAy0AhQFBAXEEQCADIANBFWpCBxApNgIQIAMoAhBFBEAgAygCqAFBCGpBDkEAEBQgAygCkAEQIyADQX82AqwBDAILIAMoAhBBAhAfIAMoAhBBvRJBAhBAIAMoAhAgAygCpAEvAVJB/wFxEI4BIAMoAhAgAygCpAEoAhBB//8DcRAfAn8jAEEQayIAIAMoAhA2AgwgACgCDC0AAEEBcUULBEAgAygCqAFBCGpBFEEAEBQgAygCEBAWIAMoApABECMgA0F/NgKsAQwCCyADQYGyAkEHIANBFWpBgAYQUTYCDCADKAIQEBYgAygCDCADKAKQATYCACADIAMoAgw2ApABCyADIANB0ABqQi4QKSIANgJMIABFBEAgAygCqAFBCGpBDkEAEBQgAygCkAEQIyADQX82AqwBDAELIAMoAkxB8RJB9hIgAygCoAFBgAJxG0EEEEAgAygCoAFBgAJxRQRAIAMoAkwgAy0AhgFBAXEEf0EtBSADKAKkAS8BCAtB//8DcRAfCyADKAJMIAMtAIYBQQFxBH9BLQUgAygCpAEvAQoLQf//A3EQHyADKAJMIAMoAqQBLwEMEB8CQCADLQCFAUEBcQRAIAMoAkxB4wAQHwwBCyADKAJMIAMoAqQBKAIQQf//A3EQHwsgAygCpAEoAhQgA0GeAWogA0GcAWoQgQEgAygCTCADLwGeARAfIAMoAkwgAy8BnAEQHwJAAkAgAy0AhQFBAXFFDQAgAygCpAEpAyhCFFoNACADKAJMQQAQIAwBCyADKAJMIAMoAqQBKAIYECALAkACQCADKAKgAUGAAnFBgAJHDQAgAygCpAEpAyBC/////w9UBEAgAygCpAEpAyhC/////w9UDQELIAMoAkxBfxAgIAMoAkxBfxAgDAELAkAgAygCpAEpAyBC/////w9UBEAgAygCTCADKAKkASkDIKcQIAwBCyADKAJMQX8QIAsCQCADKAKkASkDKEL/////D1QEQCADKAJMIAMoAqQBKQMopxAgDAELIAMoAkxBfxAgCwsgAygCTCADKAKkASgCMBBTQf//A3EQHyADIAMoAqQBKAI0IAMoAqABEIYBQf//A3EgAygCkAFBgAYQhgFB//8DcWo2AogBIAMoAkwgAygCiAFB//8DcRAfIAMoAqABQYACcUUEQCADKAJMIAMoAqQBKAI4EFNB//8DcRAfIAMoAkwgAygCpAEoAjxB//8DcRAfIAMoAkwgAygCpAEvAUAQHyADKAJMIAMoAqQBKAJEECACQCADKAKkASkDSEL/////D1QEQCADKAJMIAMoAqQBKQNIpxAgDAELIAMoAkxBfxAgCwsCfyMAQRBrIgAgAygCTDYCDCAAKAIMLQAAQQFxRQsEQCADKAKoAUEIakEUQQAQFCADKAJMEBYgAygCkAEQIyADQX82AqwBDAELIAMoAqgBIANB0ABqAn4jAEEQayIAIAMoAkw2AgwCfiAAKAIMLQAAQQFxBEAgACgCDCkDEAwBC0IACwsQNUEASARAIAMoAkwQFiADKAKQARAjIANBfzYCrAEMAQsgAygCTBAWIAMoAqQBKAIwBEAgAygCqAEgAygCpAEoAjAQigFBAEgEQCADKAKQARAjIANBfzYCrAEMAgsLIAMoApABBEAgAygCqAEgAygCkAFBgAYQhQFBAEgEQCADKAKQARAjIANBfzYCrAEMAgsLIAMoApABECMgAygCpAEoAjQEQCADKAKoASADKAKkASgCNCADKAKgARCFAUEASARAIANBfzYCrAEMAgsLIAMoAqABQYACcUUEQCADKAKkASgCOARAIAMoAqgBIAMoAqQBKAI4EIoBQQBIBEAgA0F/NgKsAQwDCwsLIAMgAy0AhwFBAXE2AqwBCyADKAKsASEAIANBsAFqJAAgAAvgAgEBfyMAQSBrIgQkACAEIAA7ARogBCABOwEYIAQgAjYCFCAEIAM2AhAgBEEQEBgiADYCDAJAIABFBEAgBEEANgIcDAELIAQoAgxBADYCACAEKAIMIAQoAhA2AgQgBCgCDCAELwEaOwEIIAQoAgwgBC8BGDsBCgJAIAQvARgEQCAEKAIUIQEgBC8BGCECIwBBIGsiACQAIAAgATYCGCAAIAI2AhQgAEEANgIQAkAgACgCFEUEQCAAQQA2AhwMAQsgACAAKAIUEBg2AgwgACgCDEUEQCAAKAIQQQ5BABAUIABBADYCHAwBCyAAKAIMIAAoAhggACgCFBAZGiAAIAAoAgw2AhwLIAAoAhwhASAAQSBqJAAgASEAIAQoAgwgADYCDCAARQRAIAQoAgwQFSAEQQA2AhwMAwsMAQsgBCgCDEEANgIMCyAEIAQoAgw2AhwLIAQoAhwhACAEQSBqJAAgAAuMAwEBfyMAQSBrIgQkACAEIAA2AhggBCABOwEWIAQgAjYCECAEIAM2AgwCQCAELwEWRQRAIARBADYCHAwBCwJAAkACQAJAIAQoAhBBgDBxIgAEQCAAQYAQRg0BIABBgCBGDQIMAwsgBEEANgIEDAMLIARBAjYCBAwCCyAEQQQ2AgQMAQsgBCgCDEESQQAQFCAEQQA2AhwMAQsgBEEUEBgiADYCCCAARQRAIAQoAgxBDkEAEBQgBEEANgIcDAELIAQvARZBAWoQGCEAIAQoAgggADYCACAARQRAIAQoAggQFSAEQQA2AhwMAQsgBCgCCCgCACAEKAIYIAQvARYQGRogBCgCCCgCACAELwEWakEAOgAAIAQoAgggBC8BFjsBBCAEKAIIQQA2AgggBCgCCEEANgIMIAQoAghBADYCECAEKAIEBEAgBCgCCCAEKAIEEDpBBUYEQCAEKAIIECUgBCgCDEESQQAQFCAEQQA2AhwMAgsLIAQgBCgCCDYCHAsgBCgCHCEAIARBIGokACAACzcBAX8jAEEQayIBIAA2AggCQCABKAIIRQRAIAFBADsBDgwBCyABIAEoAggvAQQ7AQ4LIAEvAQ4LQwEDfwJAIAJFDQADQCAALQAAIgQgAS0AACIFRgRAIAFBAWohASAAQQFqIQAgAkEBayICDQEMAgsLIAQgBWshAwsgAwuRAQEFfyAAKAJMQQBOIQMgACgCAEEBcSIERQRAIAAoAjQiAQRAIAEgACgCODYCOAsgACgCOCICBEAgAiABNgI0CyAAQaygASgCAEYEQEGsoAEgAjYCAAsLIAAQpQEhASAAIAAoAgwRAAAhAiAAKAJgIgUEQCAFEBULAkAgBEUEQCAAEBUMAQsgA0UNAAsgASACcgv5AQEBfyMAQSBrIgIkACACIAA2AhwgAiABOQMQAkAgAigCHEUNACACAnwCfCACKwMQRAAAAAAAAAAAZARAIAIrAxAMAQtEAAAAAAAAAAALRAAAAAAAAPA/YwRAAnwgAisDEEQAAAAAAAAAAGQEQCACKwMQDAELRAAAAAAAAAAACwwBC0QAAAAAAADwPwsgAigCHCsDKCACKAIcKwMgoaIgAigCHCsDIKA5AwggAigCHCsDECACKwMIIAIoAhwrAxihY0UNACACKAIcKAIAIAIrAwggAigCHCgCDCACKAIcKAIEERYAIAIoAhwgAisDCDkDGAsgAkEgaiQAC+EFAgJ/AX4jAEEwayIEJAAgBCAANgIkIAQgATYCICAEIAI2AhwgBCADNgIYAkAgBCgCJEUEQCAEQn83AygMAQsgBCgCIEUEQCAEKAIYQRJBABAUIARCfzcDKAwBCyAEKAIcQYMgcQRAIARBFUEWIAQoAhxBAXEbNgIUIARCADcDAANAIAQpAwAgBCgCJCkDMFQEQCAEIAQoAiQgBCkDACAEKAIcIAQoAhgQTjYCECAEKAIQBEAgBCgCHEECcQRAIAQCfyAEKAIQIgEQK0EBaiEAA0BBACAARQ0BGiABIABBAWsiAGoiAi0AAEEvRw0ACyACCzYCDCAEKAIMBEAgBCAEKAIMQQFqNgIQCwsgBCgCICAEKAIQIAQoAhQRAwBFBEAjAEEQayIAIAQoAhg2AgwgACgCDARAIAAoAgxBADYCACAAKAIMQQA2AgQLIAQgBCkDADcDKAwFCwsgBCAEKQMAQgF8NwMADAELCyAEKAIYQQlBABAUIARCfzcDKAwBCyAEKAIkKAJQIQEgBCgCICECIAQoAhwhAyAEKAIYIQUjAEEwayIAJAAgACABNgIkIAAgAjYCICAAIAM2AhwgACAFNgIYAkACQCAAKAIkBEAgACgCIA0BCyAAKAIYQRJBABAUIABCfzcDKAwBCyAAKAIkKQMIQgBSBEAgACAAKAIgEHQ2AhQgACAAKAIUIAAoAiQoAgBwNgIQIAAgACgCJCgCECAAKAIQQQJ0aigCADYCDANAAkAgACgCDEUNACAAKAIgIAAoAgwoAgAQWgRAIAAgACgCDCgCGDYCDAwCBSAAKAIcQQhxBEAgACgCDCkDCEJ/UgRAIAAgACgCDCkDCDcDKAwGCwwCCyAAKAIMKQMQQn9SBEAgACAAKAIMKQMQNwMoDAULCwsLCyAAKAIYQQlBABAUIABCfzcDKAsgACkDKCEGIABBMGokACAEIAY3AygLIAQpAyghBiAEQTBqJAAgBgvUAwEBfyMAQSBrIgMkACADIAA2AhggAyABNgIUIAMgAjYCEAJAAkAgAygCGARAIAMoAhQNAQsgAygCEEESQQAQFCADQQA6AB8MAQsgAygCGCkDCEIAUgRAIAMgAygCFBB0NgIMIAMgAygCDCADKAIYKAIAcDYCCCADQQA2AgAgAyADKAIYKAIQIAMoAghBAnRqKAIANgIEA0AgAygCBARAAkAgAygCBCgCHCADKAIMRw0AIAMoAhQgAygCBCgCABBaDQACQCADKAIEKQMIQn9RBEACQCADKAIABEAgAygCACADKAIEKAIYNgIYDAELIAMoAhgoAhAgAygCCEECdGogAygCBCgCGDYCAAsgAygCBBAVIAMoAhgiACAAKQMIQgF9NwMIAkAgAygCGCIAKQMIuiAAKAIAuER7FK5H4XqEP6JjRQ0AIAMoAhgoAgBBgAJNDQAgAygCGCADKAIYKAIAQQF2IAMoAhAQWUEBcUUEQCADQQA6AB8MCAsLDAELIAMoAgRCfzcDEAsgA0EBOgAfDAQLIAMgAygCBDYCACADIAMoAgQoAhg2AgQMAQsLCyADKAIQQQlBABAUIANBADoAHwsgAy0AH0EBcSEAIANBIGokACAAC98CAQF/IwBBMGsiAyQAIAMgADYCKCADIAE2AiQgAyACNgIgAkAgAygCJCADKAIoKAIARgRAIANBAToALwwBCyADIAMoAiRBBBB2IgA2AhwgAEUEQCADKAIgQQ5BABAUIANBADoALwwBCyADKAIoKQMIQgBSBEAgA0EANgIYA0AgAygCGCADKAIoKAIAT0UEQCADIAMoAigoAhAgAygCGEECdGooAgA2AhQDQCADKAIUBEAgAyADKAIUKAIYNgIQIAMgAygCFCgCHCADKAIkcDYCDCADKAIUIAMoAhwgAygCDEECdGooAgA2AhggAygCHCADKAIMQQJ0aiADKAIUNgIAIAMgAygCEDYCFAwBCwsgAyADKAIYQQFqNgIYDAELCwsgAygCKCgCEBAVIAMoAiggAygCHDYCECADKAIoIAMoAiQ2AgAgA0EBOgAvCyADLQAvQQFxIQAgA0EwaiQAIAALTQECfyABLQAAIQICQCAALQAAIgNFDQAgAiADRw0AA0AgAS0AASECIAAtAAEiA0UNASABQQFqIQEgAEEBaiEAIAIgA0YNAAsLIAMgAmsL0QkBAn8jAEEgayIBJAAgASAANgIcIAEgASgCHCgCLDYCEANAIAEgASgCHCgCPCABKAIcKAJ0ayABKAIcKAJsazYCFCABKAIcKAJsIAEoAhAgASgCHCgCLEGGAmtqTwRAIAEoAhwoAjggASgCHCgCOCABKAIQaiABKAIQIAEoAhRrEBkaIAEoAhwiACAAKAJwIAEoAhBrNgJwIAEoAhwiACAAKAJsIAEoAhBrNgJsIAEoAhwiACAAKAJcIAEoAhBrNgJcIwBBIGsiACABKAIcNgIcIAAgACgCHCgCLDYCDCAAIAAoAhwoAkw2AhggACAAKAIcKAJEIAAoAhhBAXRqNgIQA0AgACAAKAIQQQJrIgI2AhAgACACLwEANgIUIAAoAhACfyAAKAIUIAAoAgxPBEAgACgCFCAAKAIMawwBC0EACzsBACAAIAAoAhhBAWsiAjYCGCACDQALIAAgACgCDDYCGCAAIAAoAhwoAkAgACgCGEEBdGo2AhADQCAAIAAoAhBBAmsiAjYCECAAIAIvAQA2AhQgACgCEAJ/IAAoAhQgACgCDE8EQCAAKAIUIAAoAgxrDAELQQALOwEAIAAgACgCGEEBayICNgIYIAINAAsgASABKAIQIAEoAhRqNgIUCyABKAIcKAIAKAIEBEAgASABKAIcKAIAIAEoAhwoAnQgASgCHCgCOCABKAIcKAJsamogASgCFBB4NgIYIAEoAhwiACABKAIYIAAoAnRqNgJ0IAEoAhwoAnQgASgCHCgCtC1qQQNPBEAgASABKAIcKAJsIAEoAhwoArQtazYCDCABKAIcIAEoAhwoAjggASgCDGotAAA2AkggASgCHCABKAIcKAJUIAEoAhwoAjggASgCDEEBamotAAAgASgCHCgCSCABKAIcKAJYdHNxNgJIA0AgASgCHCgCtC0EQCABKAIcIAEoAhwoAlQgASgCHCgCOCABKAIMQQJqai0AACABKAIcKAJIIAEoAhwoAlh0c3E2AkggASgCHCgCQCABKAIMIAEoAhwoAjRxQQF0aiABKAIcKAJEIAEoAhwoAkhBAXRqLwEAOwEAIAEoAhwoAkQgASgCHCgCSEEBdGogASgCDDsBACABIAEoAgxBAWo2AgwgASgCHCIAIAAoArQtQQFrNgK0LSABKAIcKAJ0IAEoAhwoArQtakEDTw0BCwsLIAEoAhwoAnRBhgJJBH8gASgCHCgCACgCBEEARwVBAAtBAXENAQsLIAEoAhwoAsAtIAEoAhwoAjxJBEAgASABKAIcKAJsIAEoAhwoAnRqNgIIAkAgASgCHCgCwC0gASgCCEkEQCABIAEoAhwoAjwgASgCCGs2AgQgASgCBEGCAksEQCABQYICNgIECyABKAIcKAI4IAEoAghqQQAgASgCBBAyIAEoAhwgASgCCCABKAIEajYCwC0MAQsgASgCHCgCwC0gASgCCEGCAmpJBEAgASABKAIIQYICaiABKAIcKALALWs2AgQgASgCBCABKAIcKAI8IAEoAhwoAsAta0sEQCABIAEoAhwoAjwgASgCHCgCwC1rNgIECyABKAIcKAI4IAEoAhwoAsAtakEAIAEoAgQQMiABKAIcIgAgASgCBCAAKALALWo2AsAtCwsLIAFBIGokAAuGBQEBfyMAQSBrIgQkACAEIAA2AhwgBCABNgIYIAQgAjYCFCAEIAM2AhAgBEEDNgIMAkAgBCgCHCgCvC1BECAEKAIMa0oEQCAEIAQoAhA2AgggBCgCHCIAIAAvAbgtIAQoAghB//8DcSAEKAIcKAK8LXRyOwG4LSAEKAIcLwG4LUH/AXEhASAEKAIcKAIIIQIgBCgCHCIDKAIUIQAgAyAAQQFqNgIUIAAgAmogAToAACAEKAIcLwG4LUEIdiEBIAQoAhwoAgghAiAEKAIcIgMoAhQhACADIABBAWo2AhQgACACaiABOgAAIAQoAhwgBCgCCEH//wNxQRAgBCgCHCgCvC1rdTsBuC0gBCgCHCIAIAAoArwtIAQoAgxBEGtqNgK8LQwBCyAEKAIcIgAgAC8BuC0gBCgCEEH//wNxIAQoAhwoArwtdHI7AbgtIAQoAhwiACAEKAIMIAAoArwtajYCvC0LIAQoAhwQvAEgBCgCFEH/AXEhASAEKAIcKAIIIQIgBCgCHCIDKAIUIQAgAyAAQQFqNgIUIAAgAmogAToAACAEKAIUQf//A3FBCHYhASAEKAIcKAIIIQIgBCgCHCIDKAIUIQAgAyAAQQFqNgIUIAAgAmogAToAACAEKAIUQX9zQf8BcSEBIAQoAhwoAgghAiAEKAIcIgMoAhQhACADIABBAWo2AhQgACACaiABOgAAIAQoAhRBf3NB//8DcUEIdiEBIAQoAhwoAgghAiAEKAIcIgMoAhQhACADIABBAWo2AhQgACACaiABOgAAIAQoAhwoAgggBCgCHCgCFGogBCgCGCAEKAIUEBkaIAQoAhwiACAEKAIUIAAoAhRqNgIUIARBIGokAAuJAgEBfyMAQRBrIgEkACABIAA2AgwCQCABKAIMLQAFQQFxBEAgASgCDCgCAEECcUUNAQsgASgCDCgCMBAlIAEoAgxBADYCMAsCQCABKAIMLQAFQQFxBEAgASgCDCgCAEEIcUUNAQsgASgCDCgCNBAjIAEoAgxBADYCNAsCQCABKAIMLQAFQQFxBEAgASgCDCgCAEEEcUUNAQsgASgCDCgCOBAlIAEoAgxBADYCOAsCQCABKAIMLQAFQQFxBEAgASgCDCgCAEGAAXFFDQELIAEoAgwoAlQEQCABKAIMKAJUQQAgASgCDCgCVBArEDILIAEoAgwoAlQQFSABKAIMQQA2AlQLIAFBEGokAAt3AQF/IwBBEGsiAiAANgIIIAIgATYCBAJAAkACQCACKAIIKQMoQv////8PWg0AIAIoAggpAyBC/////w9aDQAgAigCBEGABHFFDQEgAigCCCkDSEL/////D1QNAQsgAkEBOgAPDAELIAJBADoADwsgAi0AD0EBcQv/AQEBfyMAQSBrIgUkACAFIAA2AhggBSABNgIUIAUgAjsBEiAFQQA7ARAgBSADNgIMIAUgBDYCCCAFQQA2AgQCQANAIAUoAhgEQAJAIAUoAhgvAQggBS8BEkcNACAFKAIYKAIEIAUoAgxxQYAGcUUNACAFKAIEIAUvARBIBEAgBSAFKAIEQQFqNgIEDAELIAUoAhQEQCAFKAIUIAUoAhgvAQo7AQALIAUoAhgvAQoEQCAFIAUoAhgoAgw2AhwMBAsgBUGR2QA2AhwMAwsgBSAFKAIYKAIANgIYDAELCyAFKAIIQQlBABAUIAVBADYCHAsgBSgCHCEAIAVBIGokACAAC/8CAQF/IwBBMGsiBSQAIAUgADYCKCAFIAE2AiQgBSACNgIgIAUgAzoAHyAFIAQ2AhgCQAJAIAUoAiANACAFLQAfQQFxDQAgBUEANgIsDAELIAUgBSgCICAFLQAfQQFxahAYNgIUIAUoAhRFBEAgBSgCGEEOQQAQFCAFQQA2AiwMAQsCQCAFKAIoBEAgBSAFKAIoIAUoAiCtEB42AhAgBSgCEEUEQCAFKAIYQQ5BABAUIAUoAhQQFSAFQQA2AiwMAwsgBSgCFCAFKAIQIAUoAiAQGRoMAQsgBSgCJCAFKAIUIAUoAiCtIAUoAhgQYUEASARAIAUoAhQQFSAFQQA2AiwMAgsLIAUtAB9BAXEEQCAFKAIUIAUoAiBqQQA6AAAgBSAFKAIUNgIMA0AgBSgCDCAFKAIUIAUoAiBqSQRAIAUoAgwtAABFBEAgBSgCDEEgOgAACyAFIAUoAgxBAWo2AgwMAQsLCyAFIAUoAhQ2AiwLIAUoAiwhACAFQTBqJAAgAAvCAQEBfyMAQTBrIgQkACAEIAA2AiggBCABNgIkIAQgAjcDGCAEIAM2AhQCQCAEKQMYQv///////////wBWBEAgBCgCFEEUQQAQFCAEQX82AiwMAQsgBCAEKAIoIAQoAiQgBCkDGBAuIgI3AwggAkIAUwRAIAQoAhQgBCgCKBAXIARBfzYCLAwBCyAEKQMIIAQpAxhTBEAgBCgCFEERQQAQFCAEQX82AiwMAQsgBEEANgIsCyAEKAIsIQAgBEEwaiQAIAALNgEBfyMAQRBrIgEkACABIAA2AgwgASgCDBBjIAEoAgwoAgAQOSABKAIMKAIEEDkgAUEQaiQAC6sBAQF/IwBBEGsiASQAIAEgADYCDCABKAIMKAIIBEAgASgCDCgCCBAbIAEoAgxBADYCCAsCQCABKAIMKAIERQ0AIAEoAgwoAgQoAgBBAXFFDQAgASgCDCgCBCgCEEF+Rw0AIAEoAgwoAgQiACAAKAIAQX5xNgIAIAEoAgwoAgQoAgBFBEAgASgCDCgCBBA5IAEoAgxBADYCBAsLIAEoAgxBADoADCABQRBqJAAL8QMBAX8jAEHQAGsiCCQAIAggADYCSCAIIAE3A0AgCCACNwM4IAggAzYCNCAIIAQ6ADMgCCAFNgIsIAggBjcDICAIIAc2AhwCQAJAAkAgCCgCSEUNACAIKQNAIAgpA0AgCCkDOHxWDQAgCCgCLA0BIAgpAyBQDQELIAgoAhxBEkEAEBQgCEEANgJMDAELIAhBgAEQGCIANgIYIABFBEAgCCgCHEEOQQAQFCAIQQA2AkwMAQsgCCgCGCAIKQNANwMAIAgoAhggCCkDQCAIKQM4fDcDCCAIKAIYQShqEDsgCCgCGCAILQAzOgBgIAgoAhggCCgCLDYCECAIKAIYIAgpAyA3AxgjAEEQayIAIAgoAhhB5ABqNgIMIAAoAgxBADYCACAAKAIMQQA2AgQgACgCDEEANgIIIwBBEGsiACAIKAJINgIMIAAoAgwpAxhC/4EBgyEBIAhBfzYCCCAIQQc2AgQgCEEONgIAQRAgCBA2IAGEIQEgCCgCGCABNwNwIAgoAhggCCgCGCkDcELAAINCAFI6AHggCCgCNARAIAgoAhhBKGogCCgCNCAIKAIcEJUBQQBIBEAgCCgCGBAVIAhBADYCTAwCCwsgCCAIKAJIQQEgCCgCGCAIKAIcEJIBNgJMCyAIKAJMIQAgCEHQAGokACAAC9MEAQJ/IwBBMGsiAyQAIAMgADYCJCADIAE3AxggAyACNgIUAkAgAygCJCgCQCADKQMYp0EEdGooAgBFBEAgAygCFEEUQQAQFCADQgA3AygMAQsgAyADKAIkKAJAIAMpAxinQQR0aigCACkDSDcDCCADKAIkKAIAIAMpAwhBABAnQQBIBEAgAygCFCADKAIkKAIAEBcgA0IANwMoDAELIAMoAiQoAgAhAiADKAIUIQQjAEEwayIAJAAgACACNgIoIABBgAI7ASYgACAENgIgIAAgAC8BJkGAAnFBAEc6ABsgAEEeQS4gAC0AG0EBcRs2AhwCQCAAKAIoQRpBHCAALQAbQQFxG6xBARAnQQBIBEAgACgCICAAKAIoEBcgAEF/NgIsDAELIAAgACgCKEEEQQYgAC0AG0EBcRusIABBDmogACgCIBBBIgI2AgggAkUEQCAAQX82AiwMAQsgAEEANgIUA0AgACgCFEECQQMgAC0AG0EBcRtIBEAgACAAKAIIEB1B//8DcSAAKAIcajYCHCAAIAAoAhRBAWo2AhQMAQsLIAAoAggQR0EBcUUEQCAAKAIgQRRBABAUIAAoAggQFiAAQX82AiwMAQsgACgCCBAWIAAgACgCHDYCLAsgACgCLCECIABBMGokACADIAIiADYCBCAAQQBIBEAgA0IANwMoDAELIAMpAwggAygCBK18Qv///////////wBWBEAgAygCFEEEQRYQFCADQgA3AygMAQsgAyADKQMIIAMoAgStfDcDKAsgAykDKCEBIANBMGokACABC20BAX8jAEEgayIEJAAgBCAANgIYIAQgATYCFCAEIAI2AhAgBCADNgIMAkAgBCgCGEUEQCAEQQA2AhwMAQsgBCAEKAIUIAQoAhAgBCgCDCAEKAIYQQhqEJIBNgIcCyAEKAIcIQAgBEEgaiQAIAALVQEBfyMAQRBrIgEkACABIAA2AgwCQAJAIAEoAgwoAiRBAUYNACABKAIMKAIkQQJGDQAMAQsgASgCDEEAQgBBChAhGiABKAIMQQA2AiQLIAFBEGokAAumAQEBfyMAQRBrIgIkACACIAA2AgggAiABNgIEAkAgAigCCC0AKEEBcQRAIAJBfzYCDAwBCyACKAIIKAIABEAgAigCCCgCACACKAIEEGhBAEgEQCACKAIIQQxqIAIoAggoAgAQFyACQX82AgwMAgsLIAIoAgggAkEEakIEQRMQIUIAUwRAIAJBfzYCDAwBCyACQQA2AgwLIAIoAgwhACACQRBqJAAgAAuNCAIBfwF+IwBBkAFrIgMkACADIAA2AoQBIAMgATYCgAEgAyACNgJ8IAMQTwJAIAMoAoABKQMIQgBSBEAgAyADKAKAASgCACgCACkDSDcDYCADIAMoAoABKAIAKAIAKQNINwNoDAELIANCADcDYCADQgA3A2gLIANCADcDcAJAA0AgAykDcCADKAKAASkDCFQEQCADKAKAASgCACADKQNwp0EEdGooAgApA0ggAykDaFQEQCADIAMoAoABKAIAIAMpA3CnQQR0aigCACkDSDcDaAsgAykDaCADKAKAASkDIFYEQCADKAJ8QRNBABAUIANCfzcDiAEMAwsgAyADKAKAASgCACADKQNwp0EEdGooAgApA0ggAygCgAEoAgAgAykDcKdBBHRqKAIAKQMgfCADKAKAASgCACADKQNwp0EEdGooAgAoAjAQU0H//wNxrXxCHnw3A1ggAykDWCADKQNgVgRAIAMgAykDWDcDYAsgAykDYCADKAKAASkDIFYEQCADKAJ8QRNBABAUIANCfzcDiAEMAwsgAygChAEoAgAgAygCgAEoAgAgAykDcKdBBHRqKAIAKQNIQQAQJ0EASARAIAMoAnwgAygChAEoAgAQFyADQn83A4gBDAMLIAMgAygChAEoAgBBAEEBIAMoAnwQxgFCf1EEQCADEF0gA0J/NwOIAQwDCwJ/IAMoAoABKAIAIAMpA3CnQQR0aigCACEBIwBBEGsiACQAIAAgATYCCCAAIAM2AgQCQAJAAkAgACgCCC8BCiAAKAIELwEKSA0AIAAoAggoAhAgACgCBCgCEEcNACAAKAIIKAIUIAAoAgQoAhRHDQAgACgCCCgCMCAAKAIEKAIwEIsBDQELIABBfzYCDAwBCwJAAkAgACgCCCgCGCAAKAIEKAIYRw0AIAAoAggpAyAgACgCBCkDIFINACAAKAIIKQMoIAAoAgQpAyhRDQELAkACQCAAKAIELwEMQQhxRQ0AIAAoAgQoAhgNACAAKAIEKQMgQgBSDQAgACgCBCkDKFANAQsgAEF/NgIMDAILCyAAQQA2AgwLIAAoAgwhASAAQRBqJAAgAQsEQCADKAJ8QRVBABAUIAMQXSADQn83A4gBDAMFIAMoAoABKAIAIAMpA3CnQQR0aigCACgCNCADKAI0EIkBIQAgAygCgAEoAgAgAykDcKdBBHRqKAIAIAA2AjQgAygCgAEoAgAgAykDcKdBBHRqKAIAQQE6AAQgA0EANgI0IAMQXSADIAMpA3BCAXw3A3AMAgsACwsgAwJ+IAMpA2AgAykDaH1C////////////AFQEQCADKQNgIAMpA2h9DAELQv///////////wALNwOIAQsgAykDiAEhBCADQZABaiQAIAQL1AQBAX8jAEEgayIDJAAgAyAANgIYIAMgATYCFCADIAI2AhAgAygCECEBIwBBEGsiACQAIAAgATYCCCAAQdgAEBg2AgQCQCAAKAIERQRAIAAoAghBDkEAEBQgAEEANgIMDAELIAAoAgghAiMAQRBrIgEkACABIAI2AgggAUEYEBgiAjYCBAJAIAJFBEAgASgCCEEOQQAQFCABQQA2AgwMAQsgASgCBEEANgIAIAEoAgRCADcDCCABKAIEQQA2AhAgASABKAIENgIMCyABKAIMIQIgAUEQaiQAIAAoAgQgAjYCUCACRQRAIAAoAgQQFSAAQQA2AgwMAQsgACgCBEEANgIAIAAoAgRBADYCBCMAQRBrIgEgACgCBEEIajYCDCABKAIMQQA2AgAgASgCDEEANgIEIAEoAgxBADYCCCAAKAIEQQA2AhggACgCBEEANgIUIAAoAgRBADYCHCAAKAIEQQA2AiQgACgCBEEANgIgIAAoAgRBADoAKCAAKAIEQgA3AzggACgCBEIANwMwIAAoAgRBADYCQCAAKAIEQQA2AkggACgCBEEANgJEIAAoAgRBADYCTCAAKAIEQQA2AlQgACAAKAIENgIMCyAAKAIMIQEgAEEQaiQAIAMgASIANgIMAkAgAEUEQCADQQA2AhwMAQsgAygCDCADKAIYNgIAIAMoAgwgAygCFDYCBCADKAIUQRBxBEAgAygCDCIAIAAoAhRBAnI2AhQgAygCDCIAIAAoAhhBAnI2AhgLIAMgAygCDDYCHAsgAygCHCEAIANBIGokACAAC9UBAQF/IwBBIGsiBCQAIAQgADYCGCAEIAE3AxAgBCACNgIMIAQgAzYCCAJAAkAgBCkDEEL///////////8AVwRAIAQpAxBCgICAgICAgICAf1kNAQsgBCgCCEEEQT0QFCAEQX82AhwMAQsCfyAEKQMQIQEgBCgCDCEAIAQoAhgiAigCTEF/TARAIAIgASAAEKABDAELIAIgASAAEKABC0EASARAIAQoAghBBEG0mwEoAgAQFCAEQX82AhwMAQsgBEEANgIcCyAEKAIcIQAgBEEgaiQAIAALJABBACAAEAUiACAAQRtGGyIABH9BtJsBIAA2AgBBAAVBAAsaC3ABAX8jAEEQayIDJAAgAwJ/IAFBwABxRQRAQQAgAUGAgIQCcUGAgIQCRw0BGgsgAyACQQRqNgIMIAIoAgALNgIAIAAgAUGAgAJyIAMQECIAQYFgTwRAQbSbAUEAIABrNgIAQX8hAAsgA0EQaiQAIAALMwEBfwJ/IAAQByIBQWFGBEAgABARIQELIAFBgWBPCwR/QbSbAUEAIAFrNgIAQX8FIAELC2kBAn8CQCAAKAIUIAAoAhxNDQAgAEEAQQAgACgCJBEBABogACgCFA0AQX8PCyAAKAIEIgEgACgCCCICSQRAIAAgASACa6xBASAAKAIoEQ8AGgsgAEEANgIcIABCADcDECAAQgA3AgRBAAvaAwEGfyMAQRBrIgUkACAFIAI2AgwjAEGgAWsiBCQAIARBCGpBkIcBQZABEBkaIAQgADYCNCAEIAA2AhwgBEF+IABrIgNB/////wcgA0H/////B0kbIgY2AjggBCAAIAZqIgA2AiQgBCAANgIYIARBCGohACMAQdABayIDJAAgAyACNgLMASADQaABakEAQSgQMiADIAMoAswBNgLIAQJAQQAgASADQcgBaiADQdAAaiADQaABahBxQQBIDQAgACgCTEEATiEHIAAoAgAhAiAALABKQQBMBEAgACACQV9xNgIACyACQSBxIQgCfyAAKAIwBEAgACABIANByAFqIANB0ABqIANBoAFqEHEMAQsgAEHQADYCMCAAIANB0ABqNgIQIAAgAzYCHCAAIAM2AhQgACgCLCECIAAgAzYCLCAAIAEgA0HIAWogA0HQAGogA0GgAWoQcSACRQ0AGiAAQQBBACAAKAIkEQEAGiAAQQA2AjAgACACNgIsIABBADYCHCAAQQA2AhAgACgCFBogAEEANgIUQQALGiAAIAAoAgAgCHI2AgAgB0UNAAsgA0HQAWokACAGBEAgBCgCHCIAIAAgBCgCGEZrQQA6AAALIARBoAFqJAAgBUEQaiQAC4wSAg9/AX4jAEHQAGsiBSQAIAUgATYCTCAFQTdqIRMgBUE4aiEQQQAhAQNAAkAgDUEASA0AQf////8HIA1rIAFIBEBBtJsBQT02AgBBfyENDAELIAEgDWohDQsgBSgCTCIHIQECQAJAAkACQAJAAkACQAJAIAUCfwJAIActAAAiBgRAA0ACQAJAIAZB/wFxIgZFBEAgASEGDAELIAZBJUcNASABIQYDQCABLQABQSVHDQEgBSABQQJqIgg2AkwgBkEBaiEGIAEtAAIhDiAIIQEgDkElRg0ACwsgBiAHayEBIAAEQCAAIAcgARAiCyABDQ0gBSgCTCEBIAUoAkwsAAFBMGtBCk8NAyABLQACQSRHDQMgASwAAUEwayEPQQEhESABQQNqDAQLIAUgAUEBaiIINgJMIAEtAAEhBiAIIQEMAAsACyANIQsgAA0IIBFFDQJBASEBA0AgBCABQQJ0aigCACIABEAgAyABQQN0aiAAIAIQqAFBASELIAFBAWoiAUEKRw0BDAoLC0EBIQsgAUEKTw0IA0AgBCABQQJ0aigCAA0IIAFBAWoiAUEKRw0ACwwIC0F/IQ8gAUEBagsiATYCTEEAIQgCQCABLAAAIgxBIGsiBkEfSw0AQQEgBnQiBkGJ0QRxRQ0AA0ACQCAFIAFBAWoiCDYCTCABLAABIgxBIGsiAUEgTw0AQQEgAXQiAUGJ0QRxRQ0AIAEgBnIhBiAIIQEMAQsLIAghASAGIQgLAkAgDEEqRgRAIAUCfwJAIAEsAAFBMGtBCk8NACAFKAJMIgEtAAJBJEcNACABLAABQQJ0IARqQcABa0EKNgIAIAEsAAFBA3QgA2pBgANrKAIAIQpBASERIAFBA2oMAQsgEQ0IQQAhEUEAIQogAARAIAIgAigCACIBQQRqNgIAIAEoAgAhCgsgBSgCTEEBagsiATYCTCAKQX9KDQFBACAKayEKIAhBgMAAciEIDAELIAVBzABqEKcBIgpBAEgNBiAFKAJMIQELQX8hCQJAIAEtAABBLkcNACABLQABQSpGBEACQCABLAACQTBrQQpPDQAgBSgCTCIBLQADQSRHDQAgASwAAkECdCAEakHAAWtBCjYCACABLAACQQN0IANqQYADaygCACEJIAUgAUEEaiIBNgJMDAILIBENByAABH8gAiACKAIAIgFBBGo2AgAgASgCAAVBAAshCSAFIAUoAkxBAmoiATYCTAwBCyAFIAFBAWo2AkwgBUHMAGoQpwEhCSAFKAJMIQELQQAhBgNAIAYhEkF/IQsgASwAAEHBAGtBOUsNByAFIAFBAWoiDDYCTCABLAAAIQYgDCEBIAYgEkE6bGpB74IBai0AACIGQQFrQQhJDQALIAZBE0YNAiAGRQ0GIA9BAE4EQCAEIA9BAnRqIAY2AgAgBSADIA9BA3RqKQMANwNADAQLIAANAQtBACELDAULIAVBQGsgBiACEKgBIAUoAkwhDAwCCyAPQX9KDQMLQQAhASAARQ0ECyAIQf//e3EiDiAIIAhBgMAAcRshBkEAIQtBpAghDyAQIQgCQAJAAkACfwJAAkACQAJAAn8CQAJAAkACQAJAAkACQCAMQQFrLAAAIgFBX3EgASABQQ9xQQNGGyABIBIbIgFB2ABrDiEEEhISEhISEhIOEg8GDg4OEgYSEhISAgUDEhIJEgESEgQACwJAIAFBwQBrDgcOEgsSDg4OAAsgAUHTAEYNCQwRCyAFKQNAIRRBpAgMBQtBACEBAkACQAJAAkACQAJAAkAgEkH/AXEOCAABAgMEFwUGFwsgBSgCQCANNgIADBYLIAUoAkAgDTYCAAwVCyAFKAJAIA2sNwMADBQLIAUoAkAgDTsBAAwTCyAFKAJAIA06AAAMEgsgBSgCQCANNgIADBELIAUoAkAgDaw3AwAMEAsgCUEIIAlBCEsbIQkgBkEIciEGQfgAIQELIBAhByABQSBxIQ4gBSkDQCIUUEUEQANAIAdBAWsiByAUp0EPcUGAhwFqLQAAIA5yOgAAIBRCD1YhDCAUQgSIIRQgDA0ACwsgBSkDQFANAyAGQQhxRQ0DIAFBBHZBpAhqIQ9BAiELDAMLIBAhASAFKQNAIhRQRQRAA0AgAUEBayIBIBSnQQdxQTByOgAAIBRCB1YhByAUQgOIIRQgBw0ACwsgASEHIAZBCHFFDQIgCSAQIAdrIgFBAWogASAJSBshCQwCCyAFKQNAIhRCf1cEQCAFQgAgFH0iFDcDQEEBIQtBpAgMAQsgBkGAEHEEQEEBIQtBpQgMAQtBpghBpAggBkEBcSILGwshDyAUIBAQRCEHCyAGQf//e3EgBiAJQX9KGyEGAkAgBSkDQCIUQgBSDQAgCQ0AQQAhCSAQIQcMCgsgCSAUUCAQIAdraiIBIAEgCUgbIQkMCQsgBSgCQCIBQdgSIAEbIgdBACAJEKsBIgEgByAJaiABGyEIIA4hBiABIAdrIAkgARshCQwICyAJBEAgBSgCQAwCC0EAIQEgAEEgIApBACAGECYMAgsgBUEANgIMIAUgBSkDQD4CCCAFIAVBCGo2AkBBfyEJIAVBCGoLIQhBACEBAkADQCAIKAIAIgdFDQECQCAFQQRqIAcQqgEiB0EASCIODQAgByAJIAFrSw0AIAhBBGohCCAJIAEgB2oiAUsNAQwCCwtBfyELIA4NBQsgAEEgIAogASAGECYgAUUEQEEAIQEMAQtBACEIIAUoAkAhDANAIAwoAgAiB0UNASAFQQRqIAcQqgEiByAIaiIIIAFKDQEgACAFQQRqIAcQIiAMQQRqIQwgASAISw0ACwsgAEEgIAogASAGQYDAAHMQJiAKIAEgASAKSBshAQwFCyAAIAUrA0AgCiAJIAYgAUEXERkAIQEMBAsgBSAFKQNAPAA3QQEhCSATIQcgDiEGDAILQX8hCwsgBUHQAGokACALDwsgAEEgIAsgCCAHayIOIAkgCSAOSBsiDGoiCCAKIAggCkobIgEgCCAGECYgACAPIAsQIiAAQTAgASAIIAZBgIAEcxAmIABBMCAMIA5BABAmIAAgByAOECIgAEEgIAEgCCAGQYDAAHMQJgwACwALkAIBA38CQCABIAIoAhAiBAR/IAQFQQAhBAJ/IAIgAi0ASiIDQQFrIANyOgBKIAIoAgAiA0EIcQRAIAIgA0EgcjYCAEF/DAELIAJCADcCBCACIAIoAiwiAzYCHCACIAM2AhQgAiADIAIoAjBqNgIQQQALDQEgAigCEAsgAigCFCIFa0sEQCACIAAgASACKAIkEQEADwsCfyACLABLQX9KBEAgASEEA0AgASAEIgNFDQIaIAAgA0EBayIEai0AAEEKRw0ACyACIAAgAyACKAIkEQEAIgQgA0kNAiAAIANqIQAgAigCFCEFIAEgA2sMAQsgAQshBCAFIAAgBBAZGiACIAIoAhQgBGo2AhQgASEECyAEC0gCAX8BfiMAQRBrIgMkACADIAA2AgwgAyABNgIIIAMgAjYCBCADKAIMIAMoAgggAygCBCADKAIMQQhqEFchBCADQRBqJAAgBAt3AQF/IwBBEGsiASAANgIIIAFChSo3AwACQCABKAIIRQRAIAFBADYCDAwBCwNAIAEoAggtAAAEQCABIAEoAggtAACtIAEpAwBCIX58Qv////8PgzcDACABIAEoAghBAWo2AggMAQsLIAEgASkDAD4CDAsgASgCDAuHBQEBfyMAQTBrIgUkACAFIAA2AiggBSABNgIkIAUgAjcDGCAFIAM2AhQgBSAENgIQAkACQAJAIAUoAihFDQAgBSgCJEUNACAFKQMYQv///////////wBYDQELIAUoAhBBEkEAEBQgBUEAOgAvDAELIAUoAigoAgBFBEAgBSgCKEGAAiAFKAIQEFlBAXFFBEAgBUEAOgAvDAILCyAFIAUoAiQQdDYCDCAFIAUoAgwgBSgCKCgCAHA2AgggBSAFKAIoKAIQIAUoAghBAnRqKAIANgIEA0ACQCAFKAIERQ0AAkAgBSgCBCgCHCAFKAIMRw0AIAUoAiQgBSgCBCgCABBaDQACQAJAIAUoAhRBCHEEQCAFKAIEKQMIQn9SDQELIAUoAgQpAxBCf1ENAQsgBSgCEEEKQQAQFCAFQQA6AC8MBAsMAQsgBSAFKAIEKAIYNgIEDAELCyAFKAIERQRAIAVBIBAYIgA2AgQgAEUEQCAFKAIQQQ5BABAUIAVBADoALwwCCyAFKAIEIAUoAiQ2AgAgBSgCBCAFKAIoKAIQIAUoAghBAnRqKAIANgIYIAUoAigoAhAgBSgCCEECdGogBSgCBDYCACAFKAIEIAUoAgw2AhwgBSgCBEJ/NwMIIAUoAigiACAAKQMIQgF8NwMIAkAgBSgCKCIAKQMIuiAAKAIAuEQAAAAAAADoP6JkRQ0AIAUoAigoAgBBgICAgHhPDQAgBSgCKCAFKAIoKAIAQQF0IAUoAhAQWUEBcUUEQCAFQQA6AC8MAwsLCyAFKAIUQQhxBEAgBSgCBCAFKQMYNwMICyAFKAIEIAUpAxg3AxAgBUEBOgAvCyAFLQAvQQFxIQAgBUEwaiQAIAALWQIBfwF+AkACf0EAIABFDQAaIACtIAGtfiIDpyICIAAgAXJBgIAESQ0AGkF/IAIgA0IgiKcbCyICEBgiAEUNACAAQQRrLQAAQQNxRQ0AIABBACACEDILIAAL1BEBAX8jAEGwAWsiBiQAIAYgADYCqAEgBiABNgKkASAGIAI2AqABIAYgAzYCnAEgBiAENgKYASAGIAU2ApQBIAZBADYCkAEDQCAGKAKQAUEPS0UEQCAGQSBqIAYoApABQQF0akEAOwEAIAYgBigCkAFBAWo2ApABDAELCyAGQQA2AowBA0AgBigCjAEgBigCoAFPRQRAIAZBIGogBigCpAEgBigCjAFBAXRqLwEAQQF0aiIAIAAvAQBBAWo7AQAgBiAGKAKMAUEBajYCjAEMAQsLIAYgBigCmAEoAgA2AoABIAZBDzYChAEDQAJAIAYoAoQBQQFJDQAgBkEgaiAGKAKEAUEBdGovAQANACAGIAYoAoQBQQFrNgKEAQwBCwsgBigCgAEgBigChAFLBEAgBiAGKAKEATYCgAELAkAgBigChAFFBEAgBkHAADoAWCAGQQE6AFkgBkEAOwFaIAYoApwBIgEoAgAhACABIABBBGo2AgAgACAGQdgAaigBADYBACAGKAKcASIBKAIAIQAgASAAQQRqNgIAIAAgBkHYAGooAQA2AQAgBigCmAFBATYCACAGQQA2AqwBDAELIAZBATYCiAEDQAJAIAYoAogBIAYoAoQBTw0AIAZBIGogBigCiAFBAXRqLwEADQAgBiAGKAKIAUEBajYCiAEMAQsLIAYoAoABIAYoAogBSQRAIAYgBigCiAE2AoABCyAGQQE2AnQgBkEBNgKQAQNAIAYoApABQQ9NBEAgBiAGKAJ0QQF0NgJ0IAYgBigCdCAGQSBqIAYoApABQQF0ai8BAGs2AnQgBigCdEEASARAIAZBfzYCrAEMAwUgBiAGKAKQAUEBajYCkAEMAgsACwsCQCAGKAJ0QQBMDQAgBigCqAEEQCAGKAKEAUEBRg0BCyAGQX82AqwBDAELIAZBADsBAiAGQQE2ApABA0AgBigCkAFBD09FBEAgBigCkAFBAWpBAXQgBmogBigCkAFBAXQgBmovAQAgBkEgaiAGKAKQAUEBdGovAQBqOwEAIAYgBigCkAFBAWo2ApABDAELCyAGQQA2AowBA0AgBigCjAEgBigCoAFJBEAgBigCpAEgBigCjAFBAXRqLwEABEAgBigClAEhASAGKAKkASAGKAKMASICQQF0ai8BAEEBdCAGaiIDLwEAIQAgAyAAQQFqOwEAIABB//8DcUEBdCABaiACOwEACyAGIAYoAowBQQFqNgKMAQwBCwsCQAJAAkACQCAGKAKoAQ4CAAECCyAGIAYoApQBIgA2AkwgBiAANgJQIAZBFDYCSAwCCyAGQYDwADYCUCAGQcDwADYCTCAGQYECNgJIDAELIAZBgPEANgJQIAZBwPEANgJMIAZBADYCSAsgBkEANgJsIAZBADYCjAEgBiAGKAKIATYCkAEgBiAGKAKcASgCADYCVCAGIAYoAoABNgJ8IAZBADYCeCAGQX82AmAgBkEBIAYoAoABdDYCcCAGIAYoAnBBAWs2AlwCQAJAIAYoAqgBQQFGBEAgBigCcEHUBksNAQsgBigCqAFBAkcNASAGKAJwQdAETQ0BCyAGQQE2AqwBDAELA0AgBiAGKAKQASAGKAJ4azoAWQJAIAYoAkggBigClAEgBigCjAFBAXRqLwEAQQFqSwRAIAZBADoAWCAGIAYoApQBIAYoAowBQQF0ai8BADsBWgwBCwJAIAYoApQBIAYoAowBQQF0ai8BACAGKAJITwRAIAYgBigCTCAGKAKUASAGKAKMAUEBdGovAQAgBigCSGtBAXRqLwEAOgBYIAYgBigCUCAGKAKUASAGKAKMAUEBdGovAQAgBigCSGtBAXRqLwEAOwFaDAELIAZB4AA6AFggBkEAOwFaCwsgBkEBIAYoApABIAYoAnhrdDYCaCAGQQEgBigCfHQ2AmQgBiAGKAJkNgKIAQNAIAYgBigCZCAGKAJoazYCZCAGKAJUIAYoAmQgBigCbCAGKAJ4dmpBAnRqIAZB2ABqKAEANgEAIAYoAmQNAAsgBkEBIAYoApABQQFrdDYCaANAIAYoAmwgBigCaHEEQCAGIAYoAmhBAXY2AmgMAQsLAkAgBigCaARAIAYgBigCbCAGKAJoQQFrcTYCbCAGIAYoAmggBigCbGo2AmwMAQsgBkEANgJsCyAGIAYoAowBQQFqNgKMASAGQSBqIAYoApABQQF0aiIBLwEAQQFrIQAgASAAOwEAAkAgAEH//wNxRQRAIAYoApABIAYoAoQBRg0BIAYgBigCpAEgBigClAEgBigCjAFBAXRqLwEAQQF0ai8BADYCkAELAkAgBigCkAEgBigCgAFNDQAgBigCYCAGKAJsIAYoAlxxRg0AIAYoAnhFBEAgBiAGKAKAATYCeAsgBiAGKAJUIAYoAogBQQJ0ajYCVCAGIAYoApABIAYoAnhrNgJ8IAZBASAGKAJ8dDYCdANAAkAgBigChAEgBigCfCAGKAJ4ak0NACAGIAYoAnQgBkEgaiAGKAJ8IAYoAnhqQQF0ai8BAGs2AnQgBigCdEEATA0AIAYgBigCfEEBajYCfCAGIAYoAnRBAXQ2AnQMAQsLIAYgBigCcEEBIAYoAnx0ajYCcAJAAkAgBigCqAFBAUYEQCAGKAJwQdQGSw0BCyAGKAKoAUECRw0BIAYoAnBB0ARNDQELIAZBATYCrAEMBAsgBiAGKAJsIAYoAlxxNgJgIAYoApwBKAIAIAYoAmBBAnRqIAYoAnw6AAAgBigCnAEoAgAgBigCYEECdGogBigCgAE6AAEgBigCnAEoAgAgBigCYEECdGogBigCVCAGKAKcASgCAGtBAnU7AQILDAELCyAGKAJsBEAgBkHAADoAWCAGIAYoApABIAYoAnhrOgBZIAZBADsBWiAGKAJUIAYoAmxBAnRqIAZB2ABqKAEANgEACyAGKAKcASIAIAAoAgAgBigCcEECdGo2AgAgBigCmAEgBigCgAE2AgAgBkEANgKsAQsgBigCrAEhACAGQbABaiQAIAALsQIBAX8jAEEgayIDJAAgAyAANgIYIAMgATYCFCADIAI2AhAgAyADKAIYKAIENgIMIAMoAgwgAygCEEsEQCADIAMoAhA2AgwLAkAgAygCDEUEQCADQQA2AhwMAQsgAygCGCIAIAAoAgQgAygCDGs2AgQgAygCFCADKAIYKAIAIAMoAgwQGRoCQCADKAIYKAIcKAIYQQFGBEAgAygCGCgCMCADKAIUIAMoAgwQPiEAIAMoAhggADYCMAwBCyADKAIYKAIcKAIYQQJGBEAgAygCGCgCMCADKAIUIAMoAgwQGiEAIAMoAhggADYCMAsLIAMoAhgiACADKAIMIAAoAgBqNgIAIAMoAhgiACADKAIMIAAoAghqNgIIIAMgAygCDDYCHAsgAygCHCEAIANBIGokACAAC+0BAQF/IwBBEGsiASAANgIIAkACQAJAIAEoAghFDQAgASgCCCgCIEUNACABKAIIKAIkDQELIAFBATYCDAwBCyABIAEoAggoAhw2AgQCQAJAIAEoAgRFDQAgASgCBCgCACABKAIIRw0AIAEoAgQoAgRBKkYNASABKAIEKAIEQTlGDQEgASgCBCgCBEHFAEYNASABKAIEKAIEQckARg0BIAEoAgQoAgRB2wBGDQEgASgCBCgCBEHnAEYNASABKAIEKAIEQfEARg0BIAEoAgQoAgRBmgVGDQELIAFBATYCDAwBCyABQQA2AgwLIAEoAgwL0gQBAX8jAEEgayIDIAA2AhwgAyABNgIYIAMgAjYCFCADIAMoAhxB3BZqIAMoAhRBAnRqKAIANgIQIAMgAygCFEEBdDYCDANAAkAgAygCDCADKAIcKALQKEoNAAJAIAMoAgwgAygCHCgC0ChODQAgAygCGCADKAIcIAMoAgxBAnRqQeAWaigCAEECdGovAQAgAygCGCADKAIcQdwWaiADKAIMQQJ0aigCAEECdGovAQBOBEAgAygCGCADKAIcIAMoAgxBAnRqQeAWaigCAEECdGovAQAgAygCGCADKAIcQdwWaiADKAIMQQJ0aigCAEECdGovAQBHDQEgAygCHCADKAIMQQJ0akHgFmooAgAgAygCHEHYKGpqLQAAIAMoAhxB3BZqIAMoAgxBAnRqKAIAIAMoAhxB2Chqai0AAEoNAQsgAyADKAIMQQFqNgIMCyADKAIYIAMoAhBBAnRqLwEAIAMoAhggAygCHEHcFmogAygCDEECdGooAgBBAnRqLwEASA0AAkAgAygCGCADKAIQQQJ0ai8BACADKAIYIAMoAhxB3BZqIAMoAgxBAnRqKAIAQQJ0ai8BAEcNACADKAIQIAMoAhxB2Chqai0AACADKAIcQdwWaiADKAIMQQJ0aigCACADKAIcQdgoamotAABKDQAMAQsgAygCHEHcFmogAygCFEECdGogAygCHEHcFmogAygCDEECdGooAgA2AgAgAyADKAIMNgIUIAMgAygCDEEBdDYCDAwBCwsgAygCHEHcFmogAygCFEECdGogAygCEDYCAAvXEwEDfyMAQTBrIgIkACACIAA2AiwgAiABNgIoIAIgAigCKCgCADYCJCACIAIoAigoAggoAgA2AiAgAiACKAIoKAIIKAIMNgIcIAJBfzYCECACKAIsQQA2AtAoIAIoAixBvQQ2AtQoIAJBADYCGANAIAIoAhggAigCHEgEQAJAIAIoAiQgAigCGEECdGovAQAEQCACIAIoAhgiATYCECACKAIsQdwWaiEDIAIoAiwiBCgC0ChBAWohACAEIAA2AtAoIABBAnQgA2ogATYCACACKAIYIAIoAixB2ChqakEAOgAADAELIAIoAiQgAigCGEECdGpBADsBAgsgAiACKAIYQQFqNgIYDAELCwNAIAIoAiwoAtAoQQJIBEACQCACKAIQQQJIBEAgAiACKAIQQQFqIgA2AhAMAQtBACEACyACKAIsQdwWaiEDIAIoAiwiBCgC0ChBAWohASAEIAE2AtAoIAFBAnQgA2ogADYCACACIAA2AgwgAigCJCACKAIMQQJ0akEBOwEAIAIoAgwgAigCLEHYKGpqQQA6AAAgAigCLCIAIAAoAqgtQQFrNgKoLSACKAIgBEAgAigCLCIAIAAoAqwtIAIoAiAgAigCDEECdGovAQJrNgKsLQsMAQsLIAIoAiggAigCEDYCBCACIAIoAiwoAtAoQQJtNgIYA0AgAigCGEEBTgRAIAIoAiwgAigCJCACKAIYEHogAiACKAIYQQFrNgIYDAELCyACIAIoAhw2AgwDQCACIAIoAiwoAuAWNgIYIAIoAixB3BZqIQEgAigCLCIDKALQKCEAIAMgAEEBazYC0CggAigCLCAAQQJ0IAFqKAIANgLgFiACKAIsIAIoAiRBARB6IAIgAigCLCgC4BY2AhQgAigCGCEBIAIoAixB3BZqIQMgAigCLCIEKALUKEEBayEAIAQgADYC1CggAEECdCADaiABNgIAIAIoAhQhASACKAIsQdwWaiEDIAIoAiwiBCgC1ChBAWshACAEIAA2AtQoIABBAnQgA2ogATYCACACKAIkIAIoAgxBAnRqIAIoAiQgAigCGEECdGovAQAgAigCJCACKAIUQQJ0ai8BAGo7AQAgAigCDCACKAIsQdgoamoCfyACKAIYIAIoAixB2Chqai0AACACKAIUIAIoAixB2Chqai0AAE4EQCACKAIYIAIoAixB2Chqai0AAAwBCyACKAIUIAIoAixB2Chqai0AAAtBAWo6AAAgAigCJCACKAIUQQJ0aiACKAIMIgA7AQIgAigCJCACKAIYQQJ0aiAAOwECIAIgAigCDCIAQQFqNgIMIAIoAiwgADYC4BYgAigCLCACKAIkQQEQeiACKAIsKALQKEECTg0ACyACKAIsKALgFiEBIAIoAixB3BZqIQMgAigCLCIEKALUKEEBayEAIAQgADYC1CggAEECdCADaiABNgIAIAIoAighASMAQUBqIgAgAigCLDYCPCAAIAE2AjggACAAKAI4KAIANgI0IAAgACgCOCgCBDYCMCAAIAAoAjgoAggoAgA2AiwgACAAKAI4KAIIKAIENgIoIAAgACgCOCgCCCgCCDYCJCAAIAAoAjgoAggoAhA2AiAgAEEANgIEIABBADYCEANAIAAoAhBBD0wEQCAAKAI8QbwWaiAAKAIQQQF0akEAOwEAIAAgACgCEEEBajYCEAwBCwsgACgCNCAAKAI8QdwWaiAAKAI8KALUKEECdGooAgBBAnRqQQA7AQIgACAAKAI8KALUKEEBajYCHANAIAAoAhxBvQRIBEAgACAAKAI8QdwWaiAAKAIcQQJ0aigCADYCGCAAIAAoAjQgACgCNCAAKAIYQQJ0ai8BAkECdGovAQJBAWo2AhAgACgCECAAKAIgSgRAIAAgACgCIDYCECAAIAAoAgRBAWo2AgQLIAAoAjQgACgCGEECdGogACgCEDsBAiAAKAIYIAAoAjBMBEAgACgCPCAAKAIQQQF0akG8FmoiASABLwEAQQFqOwEAIABBADYCDCAAKAIYIAAoAiROBEAgACAAKAIoIAAoAhggACgCJGtBAnRqKAIANgIMCyAAIAAoAjQgACgCGEECdGovAQA7AQogACgCPCIBIAEoAqgtIAAvAQogACgCECAAKAIMamxqNgKoLSAAKAIsBEAgACgCPCIBIAEoAqwtIAAvAQogACgCLCAAKAIYQQJ0ai8BAiAAKAIMamxqNgKsLQsLIAAgACgCHEEBajYCHAwBCwsCQCAAKAIERQ0AA0AgACAAKAIgQQFrNgIQA0AgACgCPEG8FmogACgCEEEBdGovAQBFBEAgACAAKAIQQQFrNgIQDAELCyAAKAI8IAAoAhBBAXRqQbwWaiIBIAEvAQBBAWs7AQAgACgCPCAAKAIQQQF0akG+FmoiASABLwEAQQJqOwEAIAAoAjwgACgCIEEBdGpBvBZqIgEgAS8BAEEBazsBACAAIAAoAgRBAms2AgQgACgCBEEASg0ACyAAIAAoAiA2AhADQCAAKAIQRQ0BIAAgACgCPEG8FmogACgCEEEBdGovAQA2AhgDQCAAKAIYBEAgACgCPEHcFmohASAAIAAoAhxBAWsiAzYCHCAAIANBAnQgAWooAgA2AhQgACgCFCAAKAIwSg0BIAAoAjQgACgCFEECdGovAQIgACgCEEcEQCAAKAI8IgEgASgCqC0gACgCNCAAKAIUQQJ0ai8BACAAKAIQIAAoAjQgACgCFEECdGovAQJrbGo2AqgtIAAoAjQgACgCFEECdGogACgCEDsBAgsgACAAKAIYQQFrNgIYDAELCyAAIAAoAhBBAWs2AhAMAAsACyACKAIkIQEgAigCECEDIAIoAixBvBZqIQQjAEFAaiIAJAAgACABNgI8IAAgAzYCOCAAIAQ2AjQgAEEANgIMIABBATYCCANAIAAoAghBD0wEQCAAIAAoAgwgACgCNCAAKAIIQQFrQQF0ai8BAGpBAXQ2AgwgAEEQaiAAKAIIQQF0aiAAKAIMOwEAIAAgACgCCEEBajYCCAwBCwsgAEEANgIEA0AgACgCBCAAKAI4TARAIAAgACgCPCAAKAIEQQJ0ai8BAjYCACAAKAIABEAgAEEQaiAAKAIAQQF0aiIBLwEAIQMgASADQQFqOwEAIAAoAgAhBCMAQRBrIgEgAzYCDCABIAQ2AgggAUEANgIEA0AgASABKAIEIAEoAgxBAXFyNgIEIAEgASgCDEEBdjYCDCABIAEoAgRBAXQ2AgQgASABKAIIQQFrIgM2AgggA0EASg0ACyABKAIEQQF2IQEgACgCPCAAKAIEQQJ0aiABOwEACyAAIAAoAgRBAWo2AgQMAQsLIABBQGskACACQTBqJAALTgEBfyMAQRBrIgIgADsBCiACIAE2AgQCQCACLwEKQQFGBEAgAigCBEEBRgRAIAJBADYCDAwCCyACQQQ2AgwMAQsgAkEANgIMCyACKAIMC84CAQF/IwBBMGsiBSQAIAUgADYCLCAFIAE2AiggBSACNgIkIAUgAzcDGCAFIAQ2AhQgBUIANwMIA0AgBSkDCCAFKQMYVARAIAUgBSgCJCAFKQMIp2otAAA6AAcgBSgCFEUEQCAFIAUoAiwoAhRBAnI7ARIgBSAFLwESIAUvARJBAXNsQQh2OwESIAUgBS0AByAFLwESQf8BcXM6AAcLIAUoAigEQCAFKAIoIAUpAwinaiAFLQAHOgAACyAFKAIsKAIMQX9zIAVBB2pBARAaQX9zIQAgBSgCLCAANgIMIAUoAiwgBSgCLCgCECAFKAIsKAIMQf8BcWpBhYiiwABsQQFqNgIQIAUgBSgCLCgCEEEYdjoAByAFKAIsKAIUQX9zIAVBB2pBARAaQX9zIQAgBSgCLCAANgIUIAUgBSkDCEIBfDcDCAwBCwsgBUEwaiQAC20BAX8jAEEgayIEJAAgBCAANgIYIAQgATYCFCAEIAI3AwggBCADNgIEAkAgBCgCGEUEQCAEQQA2AhwMAQsgBCAEKAIUIAQpAwggBCgCBCAEKAIYQQhqEMMBNgIcCyAEKAIcIQAgBEEgaiQAIAALpwMBAX8jAEEgayIEJAAgBCAANgIYIAQgATcDECAEIAI2AgwgBCADNgIIIAQgBCgCGCAEKQMQIAQoAgxBABBFIgA2AgACQCAARQRAIARBfzYCHAwBCyAEIAQoAhggBCkDECAEKAIMEMQBIgA2AgQgAEUEQCAEQX82AhwMAQsCQAJAIAQoAgxBCHENACAEKAIYKAJAIAQpAxCnQQR0aigCCEUNACAEKAIYKAJAIAQpAxCnQQR0aigCCCAEKAIIEDhBAEgEQCAEKAIYQQhqQQ9BABAUIARBfzYCHAwDCwwBCyAEKAIIEDsgBCgCCCAEKAIAKAIYNgIsIAQoAgggBCgCACkDKDcDGCAEKAIIIAQoAgAoAhQ2AiggBCgCCCAEKAIAKQMgNwMgIAQoAgggBCgCACgCEDsBMCAEKAIIIAQoAgAvAVI7ATIgBCgCCEEgQQAgBCgCAC0ABkEBcRtB3AFyrTcDAAsgBCgCCCAEKQMQNwMQIAQoAgggBCgCBDYCCCAEKAIIIgAgACkDAEIDhDcDACAEQQA2AhwLIAQoAhwhACAEQSBqJAAgAAsDAAELzQEBAX8jAEEQayIDJAAgAyAANgIMIAMgATYCCCADIAI2AgQgAyADQQxqQaifARALNgIAAkAgAygCAEUEQCADKAIEQSE7AQAgAygCCEEAOwEADAELIAMoAgAoAhRB0ABIBEAgAygCAEHQADYCFAsgAygCBCADKAIAKAIMIAMoAgAoAhRBCXQgAygCACgCEEEFdGpB4L8Ca2o7AQAgAygCCCADKAIAKAIIQQt0IAMoAgAoAgRBBXRqIAMoAgAoAgBBAXVqOwEACyADQRBqJAALgwMBAX8jAEEgayIDJAAgAyAAOwEaIAMgATYCFCADIAI2AhAgAyADKAIUIANBCGpBwABBABBGIgA2AgwCQCAARQRAIANBADYCHAwBCyADKAIIQQVqQf//A0sEQCADKAIQQRJBABAUIANBADYCHAwBCyADQQAgAygCCEEFaq0QKSIANgIEIABFBEAgAygCEEEOQQAQFCADQQA2AhwMAQsgAygCBEEBEI4BIAMoAgQgAygCFBCMARAgIAMoAgQgAygCDCADKAIIEEACfyMAQRBrIgAgAygCBDYCDCAAKAIMLQAAQQFxRQsEQCADKAIQQRRBABAUIAMoAgQQFiADQQA2AhwMAQsgAyADLwEaAn8jAEEQayIAIAMoAgQ2AgwCfiAAKAIMLQAAQQFxBEAgACgCDCkDEAwBC0IAC6dB//8DcQsCfyMAQRBrIgAgAygCBDYCDCAAKAIMKAIEC0GABhBRNgIAIAMoAgQQFiADIAMoAgA2AhwLIAMoAhwhACADQSBqJAAgAAu0AgEBfyMAQTBrIgMkACADIAA2AiggAyABNwMgIAMgAjYCHAJAIAMpAyBQBEAgA0EBOgAvDAELIAMgAygCKCkDECADKQMgfDcDCAJAIAMpAwggAykDIFoEQCADKQMIQv////8AWA0BCyADKAIcQQ5BABAUIANBADoALwwBCyADIAMoAigoAgAgAykDCKdBBHQQSCIANgIEIABFBEAgAygCHEEOQQAQFCADQQA6AC8MAQsgAygCKCADKAIENgIAIAMgAygCKCkDCDcDEANAIAMpAxAgAykDCFpFBEAgAygCKCgCACADKQMQp0EEdGoQkAEgAyADKQMQQgF8NwMQDAELCyADKAIoIAMpAwgiATcDECADKAIoIAE3AwggA0EBOgAvCyADLQAvQQFxIQAgA0EwaiQAIAALzAEBAX8jAEEgayICJAAgAiAANwMQIAIgATYCDCACQTAQGCIBNgIIAkAgAUUEQCACKAIMQQ5BABAUIAJBADYCHAwBCyACKAIIQQA2AgAgAigCCEIANwMQIAIoAghCADcDCCACKAIIQgA3AyAgAigCCEIANwMYIAIoAghBADYCKCACKAIIQQA6ACwgAigCCCACKQMQIAIoAgwQgwFBAXFFBEAgAigCCBAkIAJBADYCHAwBCyACIAIoAgg2AhwLIAIoAhwhASACQSBqJAAgAQvWAgEBfyMAQSBrIgMkACADIAA2AhggAyABNgIUIAMgAjYCECADIANBDGpCBBApNgIIAkAgAygCCEUEQCADQX82AhwMAQsDQCADKAIUBEAgAygCFCgCBCADKAIQcUGABnEEQCADKAIIQgAQLBogAygCCCADKAIULwEIEB8gAygCCCADKAIULwEKEB8CfyMAQRBrIgAgAygCCDYCDCAAKAIMLQAAQQFxRQsEQCADKAIYQQhqQRRBABAUIAMoAggQFiADQX82AhwMBAsgAygCGCADQQxqQgQQNUEASARAIAMoAggQFiADQX82AhwMBAsgAygCFC8BCgRAIAMoAhggAygCFCgCDCADKAIULwEKrRA1QQBIBEAgAygCCBAWIANBfzYCHAwFCwsLIAMgAygCFCgCADYCFAwBCwsgAygCCBAWIANBADYCHAsgAygCHCEAIANBIGokACAAC2gBAX8jAEEQayICIAA2AgwgAiABNgIIIAJBADsBBgNAIAIoAgwEQCACKAIMKAIEIAIoAghxQYAGcQRAIAIgAigCDC8BCiACLwEGQQRqajsBBgsgAiACKAIMKAIANgIMDAELCyACLwEGC/ABAQF/IwBBEGsiASQAIAEgADYCDCABIAEoAgw2AgggAUEANgIEA0AgASgCDARAAkACQCABKAIMLwEIQfXGAUYNACABKAIMLwEIQfXgAUYNACABKAIMLwEIQYGyAkYNACABKAIMLwEIQQFHDQELIAEgASgCDCgCADYCACABKAIIIAEoAgxGBEAgASABKAIANgIICyABKAIMQQA2AgAgASgCDBAjIAEoAgQEQCABKAIEIAEoAgA2AgALIAEgASgCADYCDAwCCyABIAEoAgw2AgQgASABKAIMKAIANgIMDAELCyABKAIIIQAgAUEQaiQAIAALswQBAX8jAEFAaiIFJAAgBSAANgI4IAUgATsBNiAFIAI2AjAgBSADNgIsIAUgBDYCKCAFIAUoAjggBS8BNq0QKSIANgIkAkAgAEUEQCAFKAIoQQ5BABAUIAVBADoAPwwBCyAFQQA2AiAgBUEANgIYA0ACfyMAQRBrIgAgBSgCJDYCDCAAKAIMLQAAQQFxCwR/IAUoAiQQL0IEWgVBAAtBAXEEQCAFIAUoAiQQHTsBFiAFIAUoAiQQHTsBFCAFIAUoAiQgBS8BFK0QHjYCECAFKAIQRQRAIAUoAihBFUEAEBQgBSgCJBAWIAUoAhgQIyAFQQA6AD8MAwsgBSAFLwEWIAUvARQgBSgCECAFKAIwEFEiADYCHCAARQRAIAUoAihBDkEAEBQgBSgCJBAWIAUoAhgQIyAFQQA6AD8MAwsCQCAFKAIYBEAgBSgCICAFKAIcNgIAIAUgBSgCHDYCIAwBCyAFIAUoAhwiADYCICAFIAA2AhgLDAELCyAFKAIkEEdBAXFFBEAgBSAFKAIkEC8+AgwgBSAFKAIkIAUoAgytEB42AggCQAJAIAUoAgxBBE8NACAFKAIIRQ0AIAUoAghBktkAIAUoAgwQVEUNAQsgBSgCKEEVQQAQFCAFKAIkEBYgBSgCGBAjIAVBADoAPwwCCwsgBSgCJBAWAkAgBSgCLARAIAUoAiwgBSgCGDYCAAwBCyAFKAIYECMLIAVBAToAPwsgBS0AP0EBcSEAIAVBQGskACAAC+8CAQF/IwBBIGsiAiQAIAIgADYCGCACIAE2AhQCQCACKAIYRQRAIAIgAigCFDYCHAwBCyACIAIoAhg2AggDQCACKAIIKAIABEAgAiACKAIIKAIANgIIDAELCwNAIAIoAhQEQCACIAIoAhQoAgA2AhAgAkEANgIEIAIgAigCGDYCDANAAkAgAigCDEUNAAJAIAIoAgwvAQggAigCFC8BCEcNACACKAIMLwEKIAIoAhQvAQpHDQAgAigCDC8BCgRAIAIoAgwoAgwgAigCFCgCDCACKAIMLwEKEFQNAQsgAigCDCIAIAAoAgQgAigCFCgCBEGABnFyNgIEIAJBATYCBAwBCyACIAIoAgwoAgA2AgwMAQsLIAIoAhRBADYCAAJAIAIoAgQEQCACKAIUECMMAQsgAigCCCACKAIUIgA2AgAgAiAANgIICyACIAIoAhA2AhQMAQsLIAIgAigCGDYCHAsgAigCHCEAIAJBIGokACAAC10BAX8jAEEQayICJAAgAiAANgIIIAIgATYCBAJAIAIoAgRFBEAgAkEANgIMDAELIAIgAigCCCACKAIEKAIAIAIoAgQvAQStEDU2AgwLIAIoAgwhACACQRBqJAAgAAuPAQEBfyMAQRBrIgIkACACIAA2AgggAiABNgIEAkACQCACKAIIBEAgAigCBA0BCyACIAIoAgggAigCBEY2AgwMAQsgAigCCC8BBCACKAIELwEERwRAIAJBADYCDAwBCyACIAIoAggoAgAgAigCBCgCACACKAIILwEEEFRFNgIMCyACKAIMIQAgAkEQaiQAIAALVQEBfyMAQRBrIgEkACABIAA2AgwgAUEAQQBBABAaNgIIIAEoAgwEQCABIAEoAgggASgCDCgCACABKAIMLwEEEBo2AggLIAEoAgghACABQRBqJAAgAAugAQEBfyMAQSBrIgUkACAFIAA2AhggBSABNgIUIAUgAjsBEiAFIAM6ABEgBSAENgIMIAUgBSgCGCAFKAIUIAUvARIgBS0AEUEBcSAFKAIMEGAiADYCCAJAIABFBEAgBUEANgIcDAELIAUgBSgCCCAFLwESQQAgBSgCDBBSNgIEIAUoAggQFSAFIAUoAgQ2AhwLIAUoAhwhACAFQSBqJAAgAAtfAQF/IwBBEGsiAiQAIAIgADYCCCACIAE6AAcgAiACKAIIQgEQHjYCAAJAIAIoAgBFBEAgAkF/NgIMDAELIAIoAgAgAi0ABzoAACACQQA2AgwLIAIoAgwaIAJBEGokAAtUAQF/IwBBEGsiASQAIAEgADYCCCABIAEoAghCARAeNgIEAkAgASgCBEUEQCABQQA6AA8MAQsgASABKAIELQAAOgAPCyABLQAPIQAgAUEQaiQAIAALOAEBfyMAQRBrIgEgADYCDCABKAIMQQA2AgAgASgCDEEANgIEIAEoAgxBADYCCCABKAIMQQA6AAwLnwIBAX8jAEFAaiIFJAAgBSAANwMwIAUgATcDKCAFIAI2AiQgBSADNwMYIAUgBDYCFCAFAn8gBSkDGEIQVARAIAUoAhRBEkEAEBRBAAwBCyAFKAIkCzYCBAJAIAUoAgRFBEAgBUJ/NwM4DAELAkACQAJAAkACQCAFKAIEKAIIDgMCAAEDCyAFIAUpAzAgBSgCBCkDAHw3AwgMAwsgBSAFKQMoIAUoAgQpAwB8NwMIDAILIAUgBSgCBCkDADcDCAwBCyAFKAIUQRJBABAUIAVCfzcDOAwBCwJAIAUpAwhCAFkEQCAFKQMIIAUpAyhYDQELIAUoAhRBEkEAEBQgBUJ/NwM4DAELIAUgBSkDCDcDOAsgBSkDOCEAIAVBQGskACAAC+oBAgF/AX4jAEEgayIEJAAgBCAANgIYIAQgATYCFCAEIAI2AhAgBCADNgIMIAQgBCgCDBCTASIANgIIAkAgAEUEQCAEQQA2AhwMAQsjAEEQayIAIAQoAhg2AgwgACgCDCIAIAAoAjBBAWo2AjAgBCgCCCAEKAIYNgIAIAQoAgggBCgCFDYCBCAEKAIIIAQoAhA2AgggBCgCGCAEKAIQQQBCAEEOIAQoAhQRCgAhBSAEKAIIIAU3AxggBCgCCCkDGEIAUwRAIAQoAghCPzcDGAsgBCAEKAIINgIcCyAEKAIcIQAgBEEgaiQAIAAL6gEBAX8jAEEQayIBJAAgASAANgIIIAFBOBAYIgA2AgQCQCAARQRAIAEoAghBDkEAEBQgAUEANgIMDAELIAEoAgRBADYCACABKAIEQQA2AgQgASgCBEEANgIIIAEoAgRBADYCICABKAIEQQA2AiQgASgCBEEAOgAoIAEoAgRBADYCLCABKAIEQQE2AjAjAEEQayIAIAEoAgRBDGo2AgwgACgCDEEANgIAIAAoAgxBADYCBCAAKAIMQQA2AgggASgCBEEAOgA0IAEoAgRBADoANSABIAEoAgQ2AgwLIAEoAgwhACABQRBqJAAgAAuwAQIBfwF+IwBBIGsiAyQAIAMgADYCGCADIAE2AhQgAyACNgIQIAMgAygCEBCTASIANgIMAkAgAEUEQCADQQA2AhwMAQsgAygCDCADKAIYNgIEIAMoAgwgAygCFDYCCCADKAIUQQBCAEEOIAMoAhgRDgAhBCADKAIMIAQ3AxggAygCDCkDGEIAUwRAIAMoAgxCPzcDGAsgAyADKAIMNgIcCyADKAIcIQAgA0EgaiQAIAALwwIBAX8jAEEQayIDIAA2AgwgAyABNgIIIAMgAjYCBCADKAIIKQMAQgKDQgBSBEAgAygCDCADKAIIKQMQNwMQCyADKAIIKQMAQgSDQgBSBEAgAygCDCADKAIIKQMYNwMYCyADKAIIKQMAQgiDQgBSBEAgAygCDCADKAIIKQMgNwMgCyADKAIIKQMAQhCDQgBSBEAgAygCDCADKAIIKAIoNgIoCyADKAIIKQMAQiCDQgBSBEAgAygCDCADKAIIKAIsNgIsCyADKAIIKQMAQsAAg0IAUgRAIAMoAgwgAygCCC8BMDsBMAsgAygCCCkDAEKAAYNCAFIEQCADKAIMIAMoAggvATI7ATILIAMoAggpAwBCgAKDQgBSBEAgAygCDCADKAIIKAI0NgI0CyADKAIMIgAgAygCCCkDACAAKQMAhDcDAEEAC1oBAX8jAEEQayIBIAA2AggCQAJAIAEoAggoAgBBAE4EQCABKAIIKAIAQYAUKAIASA0BCyABQQA2AgwMAQsgASABKAIIKAIAQQJ0QZAUaigCADYCDAsgASgCDAumAQEBfyMAQSBrIgUkACAFIAA2AhggBSABNwMQIAUgAjYCDCAFIAM2AgggBSAENgIEIAUgBSgCGCAFKQMQIAUoAgxBABBFIgA2AgACQCAARQRAIAVBfzYCHAwBCyAFKAIIBEAgBSgCCCAFKAIALwEIQQh2OgAACyAFKAIEBEAgBSgCBCAFKAIAKAJENgIACyAFQQA2AhwLIAUoAhwhACAFQSBqJAAgAAucBgECfyMAQSBrIgIkACACIAA2AhggAiABNwMQAkAgAikDECACKAIYKQMwWgRAIAIoAhhBCGpBEkEAEBQgAkF/NgIcDAELIAIoAhgoAhhBAnEEQCACKAIYQQhqQRlBABAUIAJBfzYCHAwBCyACIAIoAhggAikDEEEAIAIoAhhBCGoQTiIANgIMIABFBEAgAkF/NgIcDAELIAIoAhgoAlAgAigCDCACKAIYQQhqEFhBAXFFBEAgAkF/NgIcDAELAn8gAigCGCEDIAIpAxAhASMAQTBrIgAkACAAIAM2AiggACABNwMgIABBATYCHAJAIAApAyAgACgCKCkDMFoEQCAAKAIoQQhqQRJBABAUIABBfzYCLAwBCwJAIAAoAhwNACAAKAIoKAJAIAApAyCnQQR0aigCBEUNACAAKAIoKAJAIAApAyCnQQR0aigCBCgCAEECcUUNAAJAIAAoAigoAkAgACkDIKdBBHRqKAIABEAgACAAKAIoIAApAyBBCCAAKAIoQQhqEE4iAzYCDCADRQRAIABBfzYCLAwECyAAIAAoAiggACgCDEEAQQAQVzcDEAJAIAApAxBCAFMNACAAKQMQIAApAyBRDQAgACgCKEEIakEKQQAQFCAAQX82AiwMBAsMAQsgAEEANgIMCyAAIAAoAiggACkDIEEAIAAoAihBCGoQTiIDNgIIIANFBEAgAEF/NgIsDAILIAAoAgwEQCAAKAIoKAJQIAAoAgwgACkDIEEAIAAoAihBCGoQdUEBcUUEQCAAQX82AiwMAwsLIAAoAigoAlAgACgCCCAAKAIoQQhqEFhBAXFFBEAgACgCKCgCUCAAKAIMQQAQWBogAEF/NgIsDAILCyAAKAIoKAJAIAApAyCnQQR0aigCBBA5IAAoAigoAkAgACkDIKdBBHRqQQA2AgQgACgCKCgCQCAAKQMgp0EEdGoQYyAAQQA2AiwLIAAoAiwhAyAAQTBqJAAgAwsEQCACQX82AhwMAQsgAigCGCgCQCACKQMQp0EEdGpBAToADCACQQA2AhwLIAIoAhwhACACQSBqJAAgAAulBAEBfyMAQTBrIgUkACAFIAA2AiggBSABNwMgIAUgAjYCHCAFIAM6ABsgBSAENgIUAkAgBSgCKCAFKQMgQQBBABBFRQRAIAVBfzYCLAwBCyAFKAIoKAIYQQJxBEAgBSgCKEEIakEZQQAQFCAFQX82AiwMAQsgBSAFKAIoKAJAIAUpAyCnQQR0ajYCECAFAn8gBSgCECgCAARAIAUoAhAoAgAvAQhBCHYMAQtBAws6AAsgBQJ/IAUoAhAoAgAEQCAFKAIQKAIAKAJEDAELQYCA2I14CzYCBEEBIQAgBSAFLQAbIAUtAAtGBH8gBSgCFCAFKAIERwVBAQtBAXE2AgwCQCAFKAIMBEAgBSgCECgCBEUEQCAFKAIQKAIAED8hACAFKAIQIAA2AgQgAEUEQCAFKAIoQQhqQQ5BABAUIAVBfzYCLAwECwsgBSgCECgCBCAFKAIQKAIELwEIQf8BcSAFLQAbQQh0cjsBCCAFKAIQKAIEIAUoAhQ2AkQgBSgCECgCBCIAIAAoAgBBEHI2AgAMAQsgBSgCECgCBARAIAUoAhAoAgQiACAAKAIAQW9xNgIAAkAgBSgCECgCBCgCAEUEQCAFKAIQKAIEEDkgBSgCEEEANgIEDAELIAUoAhAoAgQgBSgCECgCBC8BCEH/AXEgBS0AC0EIdHI7AQggBSgCECgCBCAFKAIENgJECwsLIAVBADYCLAsgBSgCLCEAIAVBMGokACAAC90PAgF/AX4jAEFAaiIEJAAgBCAANgI0IARCfzcDKCAEIAE2AiQgBCACNgIgIAQgAzYCHAJAIAQoAjQoAhhBAnEEQCAEKAI0QQhqQRlBABAUIARCfzcDOAwBCyAEIAQoAjQpAzA3AxAgBCkDKEJ/UQRAIARCfzcDCCAEKAIcQYDAAHEEQCAEIAQoAjQgBCgCJCAEKAIcQQAQVzcDCAsgBCkDCEJ/UQRAIAQoAjQhASMAQUBqIgAkACAAIAE2AjQCQCAAKAI0KQM4IAAoAjQpAzBCAXxYBEAgACAAKAI0KQM4NwMYIAAgACkDGEIBhjcDEAJAIAApAxBCEFQEQCAAQhA3AxAMAQsgACkDEEKACFYEQCAAQoAINwMQCwsgACAAKQMQIAApAxh8NwMYIAAgACkDGKdBBHStNwMIIAApAwggACgCNCkDOKdBBHStVARAIAAoAjRBCGpBDkEAEBQgAEJ/NwM4DAILIAAgACgCNCgCQCAAKQMYp0EEdBBINgIkIAAoAiRFBEAgACgCNEEIakEOQQAQFCAAQn83AzgMAgsgACgCNCAAKAIkNgJAIAAoAjQgACkDGDcDOAsgACgCNCIBKQMwIQUgASAFQgF8NwMwIAAgBTcDKCAAKAI0KAJAIAApAyinQQR0ahCQASAAIAApAyg3AzgLIAApAzghBSAAQUBrJAAgBCAFNwMIIAVCAFMEQCAEQn83AzgMAwsLIAQgBCkDCDcDKAsCQCAEKAIkRQ0AIAQoAjQhASAEKQMoIQUgBCgCJCECIAQoAhwhAyMAQUBqIgAkACAAIAE2AjggACAFNwMwIAAgAjYCLCAAIAM2AigCQCAAKQMwIAAoAjgpAzBaBEAgACgCOEEIakESQQAQFCAAQX82AjwMAQsgACgCOCgCGEECcQRAIAAoAjhBCGpBGUEAEBQgAEF/NgI8DAELAkACQCAAKAIsRQ0AIAAoAiwsAABFDQAgACAAKAIsIAAoAiwQK0H//wNxIAAoAiggACgCOEEIahBSIgE2AiAgAUUEQCAAQX82AjwMAwsCQCAAKAIoQYAwcQ0AIAAoAiBBABA6QQNHDQAgACgCIEECNgIICwwBCyAAQQA2AiALIAAgACgCOCAAKAIsQQBBABBXIgU3AxACQCAFQgBTDQAgACkDECAAKQMwUQ0AIAAoAiAQJSAAKAI4QQhqQQpBABAUIABBfzYCPAwBCwJAIAApAxBCAFMNACAAKQMQIAApAzBSDQAgACgCIBAlIABBADYCPAwBCyAAIAAoAjgoAkAgACkDMKdBBHRqNgIkAkAgACgCJCgCAARAIAAgACgCJCgCACgCMCAAKAIgEIsBQQBHOgAfDAELIABBADoAHwsCQCAALQAfQQFxDQAgACgCJCgCBA0AIAAoAiQoAgAQPyEBIAAoAiQgATYCBCABRQRAIAAoAjhBCGpBDkEAEBQgACgCIBAlIABBfzYCPAwCCwsgAAJ/IAAtAB9BAXEEQCAAKAIkKAIAKAIwDAELIAAoAiALQQBBACAAKAI4QQhqEEYiATYCCCABRQRAIAAoAiAQJSAAQX82AjwMAQsCQCAAKAIkKAIEBEAgACAAKAIkKAIEKAIwNgIEDAELAkAgACgCJCgCAARAIAAgACgCJCgCACgCMDYCBAwBCyAAQQA2AgQLCwJAIAAoAgQEQCAAIAAoAgRBAEEAIAAoAjhBCGoQRiIBNgIMIAFFBEAgACgCIBAlIABBfzYCPAwDCwwBCyAAQQA2AgwLIAAoAjgoAlAgACgCCCAAKQMwQQAgACgCOEEIahB1QQFxRQRAIAAoAiAQJSAAQX82AjwMAQsgACgCDARAIAAoAjgoAlAgACgCDEEAEFgaCwJAIAAtAB9BAXEEQCAAKAIkKAIEBEAgACgCJCgCBCgCAEECcQRAIAAoAiQoAgQoAjAQJSAAKAIkKAIEIgEgASgCAEF9cTYCAAJAIAAoAiQoAgQoAgBFBEAgACgCJCgCBBA5IAAoAiRBADYCBAwBCyAAKAIkKAIEIAAoAiQoAgAoAjA2AjALCwsgACgCIBAlDAELIAAoAiQoAgQoAgBBAnEEQCAAKAIkKAIEKAIwECULIAAoAiQoAgQiASABKAIAQQJyNgIAIAAoAiQoAgQgACgCIDYCMAsgAEEANgI8CyAAKAI8IQEgAEFAayQAIAFFDQAgBCgCNCkDMCAEKQMQUgRAIAQoAjQoAkAgBCkDKKdBBHRqEGIgBCgCNCAEKQMQNwMwCyAEQn83AzgMAQsgBCgCNCgCQCAEKQMop0EEdGoQYwJAIAQoAjQoAkAgBCkDKKdBBHRqKAIARQ0AIAQoAjQoAkAgBCkDKKdBBHRqKAIEBEAgBCgCNCgCQCAEKQMop0EEdGooAgQoAgBBAXENAQsgBCgCNCgCQCAEKQMop0EEdGooAgRFBEAgBCgCNCgCQCAEKQMop0EEdGooAgAQPyEAIAQoAjQoAkAgBCkDKKdBBHRqIAA2AgQgAEUEQCAEKAI0QQhqQQ5BABAUIARCfzcDOAwDCwsgBCgCNCgCQCAEKQMop0EEdGooAgRBfjYCECAEKAI0KAJAIAQpAyinQQR0aigCBCIAIAAoAgBBAXI2AgALIAQoAjQoAkAgBCkDKKdBBHRqIAQoAiA2AgggBCAEKQMoNwM4CyAEKQM4IQUgBEFAayQAIAULqgEBAX8jAEEwayICJAAgAiAANgIoIAIgATcDICACQQA2AhwCQAJAIAIoAigoAiRBAUYEQCACKAIcRQ0BIAIoAhxBAUYNASACKAIcQQJGDQELIAIoAihBDGpBEkEAEBQgAkF/NgIsDAELIAIgAikDIDcDCCACIAIoAhw2AhAgAkF/QQAgAigCKCACQQhqQhBBDBAhQgBTGzYCLAsgAigCLCEAIAJBMGokACAAC6UyAwZ/AX4BfCMAQeAAayIEJAAgBCAANgJYIAQgATYCVCAEIAI2AlACQAJAIAQoAlRBAE4EQCAEKAJYDQELIAQoAlBBEkEAEBQgBEEANgJcDAELIAQgBCgCVDYCTCMAQRBrIgAgBCgCWDYCDCAEIAAoAgwpAxg3A0BB4JoBKQMAQn9RBEAgBEF/NgIUIARBAzYCECAEQQc2AgwgBEEGNgIIIARBAjYCBCAEQQE2AgBB4JoBQQAgBBA2NwMAIARBfzYCNCAEQQ82AjAgBEENNgIsIARBDDYCKCAEQQo2AiQgBEEJNgIgQeiaAUEIIARBIGoQNjcDAAtB4JoBKQMAIAQpA0BB4JoBKQMAg1IEQCAEKAJQQRxBABAUIARBADYCXAwBC0HomgEpAwAgBCkDQEHomgEpAwCDUgRAIAQgBCgCTEEQcjYCTAsgBCgCTEEYcUEYRgRAIAQoAlBBGUEAEBQgBEEANgJcDAELIAQoAlghASAEKAJQIQIjAEHQAGsiACQAIAAgATYCSCAAIAI2AkQgAEEIahA7AkAgACgCSCAAQQhqEDgEQCMAQRBrIgEgACgCSDYCDCAAIAEoAgxBDGo2AgQjAEEQayIBIAAoAgQ2AgwCQCABKAIMKAIAQQVHDQAjAEEQayIBIAAoAgQ2AgwgASgCDCgCBEEsRw0AIABBADYCTAwCCyAAKAJEIAAoAgQQQyAAQX82AkwMAQsgAEEBNgJMCyAAKAJMIQEgAEHQAGokACAEIAE2AjwCQAJAAkAgBCgCPEEBag4CAAECCyAEQQA2AlwMAgsgBCgCTEEBcUUEQCAEKAJQQQlBABAUIARBADYCXAwCCyAEIAQoAlggBCgCTCAEKAJQEGo2AlwMAQsgBCgCTEECcQRAIAQoAlBBCkEAEBQgBEEANgJcDAELIAQoAlgQSUEASARAIAQoAlAgBCgCWBAXIARBADYCXAwBCwJAIAQoAkxBCHEEQCAEIAQoAlggBCgCTCAEKAJQEGo2AjgMAQsgBCgCWCEAIAQoAkwhASAEKAJQIQIjAEHwAGsiAyQAIAMgADYCaCADIAE2AmQgAyACNgJgIANBIGoQOwJAIAMoAmggA0EgahA4QQBIBEAgAygCYCADKAJoEBcgA0EANgJsDAELIAMpAyBCBINQBEAgAygCYEEEQYoBEBQgA0EANgJsDAELIAMgAykDODcDGCADIAMoAmggAygCZCADKAJgEGoiADYCXCAARQRAIANBADYCbAwBCwJAIAMpAxhQRQ0AIAMoAmgQngFBAXFFDQAgAyADKAJcNgJsDAELIAMoAlwhACADKQMYIQkjAEHgAGsiAiQAIAIgADYCWCACIAk3A1ACQCACKQNQQhZUBEAgAigCWEEIakETQQAQFCACQQA2AlwMAQsgAgJ+IAIpA1BCqoAEVARAIAIpA1AMAQtCqoAECzcDMCACKAJYKAIAQgAgAikDMH1BAhAnQQBIBEAjAEEQayIAIAIoAlgoAgA2AgwgAiAAKAIMQQxqNgIIAkACfyMAQRBrIgAgAigCCDYCDCAAKAIMKAIAQQRGCwRAIwBBEGsiACACKAIINgIMIAAoAgwoAgRBFkYNAQsgAigCWEEIaiACKAIIEEMgAkEANgJcDAILCyACIAIoAlgoAgAQSiIJNwM4IAlCAFMEQCACKAJYQQhqIAIoAlgoAgAQFyACQQA2AlwMAQsgAiACKAJYKAIAIAIpAzBBACACKAJYQQhqEEEiADYCDCAARQRAIAJBADYCXAwBCyACQn83AyAgAkEANgJMIAIpAzBCqoAEWgRAIAIoAgxCFBAsGgsgAkEQakETQQAQFCACIAIoAgxCABAeNgJEA0ACQCACKAJEIQEgAigCDBAvQhJ9pyEFIwBBIGsiACQAIAAgATYCGCAAIAU2AhQgAEHsEjYCECAAQQQ2AgwCQAJAIAAoAhQgACgCDE8EQCAAKAIMDQELIABBADYCHAwBCyAAIAAoAhhBAWs2AggDQAJAIAAgACgCCEEBaiAAKAIQLQAAIAAoAhggACgCCGsgACgCFCAAKAIMa2oQqwEiATYCCCABRQ0AIAAoAghBAWogACgCEEEBaiAAKAIMQQFrEFQNASAAIAAoAgg2AhwMAgsLIABBADYCHAsgACgCHCEBIABBIGokACACIAE2AkQgAUUNACACKAIMIAIoAkQCfyMAQRBrIgAgAigCDDYCDCAAKAIMKAIEC2usECwaIAIoAlghASACKAIMIQUgAikDOCEJIwBB8ABrIgAkACAAIAE2AmggACAFNgJkIAAgCTcDWCAAIAJBEGo2AlQjAEEQayIBIAAoAmQ2AgwgAAJ+IAEoAgwtAABBAXEEQCABKAIMKQMQDAELQgALNwMwAkAgACgCZBAvQhZUBEAgACgCVEETQQAQFCAAQQA2AmwMAQsgACgCZEIEEB4oAABB0JaVMEcEQCAAKAJUQRNBABAUIABBADYCbAwBCwJAAkAgACkDMEIUVA0AIwBBEGsiASAAKAJkNgIMIAEoAgwoAgQgACkDMKdqQRRrKAAAQdCWmThHDQAgACgCZCAAKQMwQhR9ECwaIAAoAmgoAgAhBSAAKAJkIQYgACkDWCEJIAAoAmgoAhQhByAAKAJUIQgjAEGwAWsiASQAIAEgBTYCqAEgASAGNgKkASABIAk3A5gBIAEgBzYClAEgASAINgKQASMAQRBrIgUgASgCpAE2AgwgAQJ+IAUoAgwtAABBAXEEQCAFKAIMKQMQDAELQgALNwMYIAEoAqQBQgQQHhogASABKAKkARAdQf//A3E2AhAgASABKAKkARAdQf//A3E2AgggASABKAKkARAwNwM4AkAgASkDOEL///////////8AVgRAIAEoApABQQRBFhAUIAFBADYCrAEMAQsgASkDOEI4fCABKQMYIAEpA5gBfFYEQCABKAKQAUEVQQAQFCABQQA2AqwBDAELAkACQCABKQM4IAEpA5gBVA0AIAEpAzhCOHwgASkDmAECfiMAQRBrIgUgASgCpAE2AgwgBSgCDCkDCAt8Vg0AIAEoAqQBIAEpAzggASkDmAF9ECwaIAFBADoAFwwBCyABKAKoASABKQM4QQAQJ0EASARAIAEoApABIAEoAqgBEBcgAUEANgKsAQwCCyABIAEoAqgBQjggAUFAayABKAKQARBBIgU2AqQBIAVFBEAgAUEANgKsAQwCCyABQQE6ABcLIAEoAqQBQgQQHigAAEHQlpkwRwRAIAEoApABQRVBABAUIAEtABdBAXEEQCABKAKkARAWCyABQQA2AqwBDAELIAEgASgCpAEQMDcDMAJAIAEoApQBQQRxRQ0AIAEpAzAgASkDOHxCDHwgASkDmAEgASkDGHxRDQAgASgCkAFBFUEAEBQgAS0AF0EBcQRAIAEoAqQBEBYLIAFBADYCrAEMAQsgASgCpAFCBBAeGiABIAEoAqQBECo2AgwgASABKAKkARAqNgIEIAEoAhBB//8DRgRAIAEgASgCDDYCEAsgASgCCEH//wNGBEAgASABKAIENgIICwJAIAEoApQBQQRxRQ0AIAEoAgggASgCBEYEQCABKAIQIAEoAgxGDQELIAEoApABQRVBABAUIAEtABdBAXEEQCABKAKkARAWCyABQQA2AqwBDAELAkAgASgCEEUEQCABKAIIRQ0BCyABKAKQAUEBQQAQFCABLQAXQQFxBEAgASgCpAEQFgsgAUEANgKsAQwBCyABIAEoAqQBEDA3AyggASABKAKkARAwNwMgIAEpAyggASkDIFIEQCABKAKQAUEBQQAQFCABLQAXQQFxBEAgASgCpAEQFgsgAUEANgKsAQwBCyABIAEoAqQBEDA3AzAgASABKAKkARAwNwOAAQJ/IwBBEGsiBSABKAKkATYCDCAFKAIMLQAAQQFxRQsEQCABKAKQAUEUQQAQFCABLQAXQQFxBEAgASgCpAEQFgsgAUEANgKsAQwBCyABLQAXQQFxBEAgASgCpAEQFgsCQCABKQOAAUL///////////8AWARAIAEpA4ABIAEpA4ABIAEpAzB8WA0BCyABKAKQAUEEQRYQFCABQQA2AqwBDAELIAEpA4ABIAEpAzB8IAEpA5gBIAEpAzh8VgRAIAEoApABQRVBABAUIAFBADYCrAEMAQsCQCABKAKUAUEEcUUNACABKQOAASABKQMwfCABKQOYASABKQM4fFENACABKAKQAUEVQQAQFCABQQA2AqwBDAELIAEpAyggASkDMEIugFYEQCABKAKQAUEVQQAQFCABQQA2AqwBDAELIAEgASkDKCABKAKQARCEASIFNgKMASAFRQRAIAFBADYCrAEMAQsgASgCjAFBAToALCABKAKMASABKQMwNwMYIAEoAowBIAEpA4ABNwMgIAEgASgCjAE2AqwBCyABKAKsASEFIAFBsAFqJAAgACAFNgJQDAELIAAoAmQgACkDMBAsGiAAKAJkIQUgACkDWCEJIAAoAmgoAhQhBiAAKAJUIQcjAEHQAGsiASQAIAEgBTYCSCABIAk3A0AgASAGNgI8IAEgBzYCOAJAIAEoAkgQL0IWVARAIAEoAjhBFUEAEBQgAUEANgJMDAELIwBBEGsiBSABKAJINgIMIAECfiAFKAIMLQAAQQFxBEAgBSgCDCkDEAwBC0IACzcDCCABKAJIQgQQHhogASgCSBAqBEAgASgCOEEBQQAQFCABQQA2AkwMAQsgASABKAJIEB1B//8Dca03AyggASABKAJIEB1B//8Dca03AyAgASkDICABKQMoUgRAIAEoAjhBE0EAEBQgAUEANgJMDAELIAEgASgCSBAqrTcDGCABIAEoAkgQKq03AxAgASkDECABKQMQIAEpAxh8VgRAIAEoAjhBBEEWEBQgAUEANgJMDAELIAEpAxAgASkDGHwgASkDQCABKQMIfFYEQCABKAI4QRVBABAUIAFBADYCTAwBCwJAIAEoAjxBBHFFDQAgASkDECABKQMYfCABKQNAIAEpAwh8UQ0AIAEoAjhBFUEAEBQgAUEANgJMDAELIAEgASkDICABKAI4EIQBIgU2AjQgBUUEQCABQQA2AkwMAQsgASgCNEEAOgAsIAEoAjQgASkDGDcDGCABKAI0IAEpAxA3AyAgASABKAI0NgJMCyABKAJMIQUgAUHQAGokACAAIAU2AlALIAAoAlBFBEAgAEEANgJsDAELIAAoAmQgACkDMEIUfBAsGiAAIAAoAmQQHTsBTiAAKAJQKQMgIAAoAlApAxh8IAApA1ggACkDMHxWBEAgACgCVEEVQQAQFCAAKAJQECQgAEEANgJsDAELAkAgAC8BTkUEQCAAKAJoKAIEQQRxRQ0BCyAAKAJkIAApAzBCFnwQLBogACAAKAJkEC83AyACQCAAKQMgIAAvAU6tWgRAIAAoAmgoAgRBBHFFDQEgACkDICAALwFOrVENAQsgACgCVEEVQQAQFCAAKAJQECQgAEEANgJsDAILIAAvAU4EQCAAKAJkIAAvAU6tEB4gAC8BTkEAIAAoAlQQUiEBIAAoAlAgATYCKCABRQRAIAAoAlAQJCAAQQA2AmwMAwsLCwJAIAAoAlApAyAgACkDWFoEQCAAKAJkIAAoAlApAyAgACkDWH0QLBogACAAKAJkIAAoAlApAxgQHiIBNgIcIAFFBEAgACgCVEEVQQAQFCAAKAJQECQgAEEANgJsDAMLIAAgACgCHCAAKAJQKQMYECkiATYCLCABRQRAIAAoAlRBDkEAEBQgACgCUBAkIABBADYCbAwDCwwBCyAAQQA2AiwgACgCaCgCACAAKAJQKQMgQQAQJ0EASARAIAAoAlQgACgCaCgCABAXIAAoAlAQJCAAQQA2AmwMAgsgACgCaCgCABBKIAAoAlApAyBSBEAgACgCVEETQQAQFCAAKAJQECQgAEEANgJsDAILCyAAIAAoAlApAxg3AzggAEIANwNAA0ACQCAAKQM4UA0AIABBADoAGyAAKQNAIAAoAlApAwhRBEAgACgCUC0ALEEBcQ0BIAApAzhCLlQNASAAKAJQQoCABCAAKAJUEIMBQQFxRQRAIAAoAlAQJCAAKAIsEBYgAEEANgJsDAQLIABBAToAGwsjAEEQayIBJAAgAUHYABAYIgU2AggCQCAFRQRAIAFBADYCDAwBCyABKAIIEE8gASABKAIINgIMCyABKAIMIQUgAUEQaiQAIAUhASAAKAJQKAIAIAApA0CnQQR0aiABNgIAAkAgAQRAIAAgACgCUCgCACAAKQNAp0EEdGooAgAgACgCaCgCACAAKAIsQQAgACgCVBDGASIJNwMQIAlCAFkNAQsCQCAALQAbQQFxRQ0AIwBBEGsiASAAKAJUNgIMIAEoAgwoAgBBE0cNACAAKAJUQRVBABAUCyAAKAJQECQgACgCLBAWIABBADYCbAwDCyAAIAApA0BCAXw3A0AgACAAKQM4IAApAxB9NwM4DAELCwJAIAApA0AgACgCUCkDCFEEQCAAKQM4UA0BCyAAKAJUQRVBABAUIAAoAiwQFiAAKAJQECQgAEEANgJsDAELIAAoAmgoAgRBBHEEQAJAIAAoAiwEQCAAIAAoAiwQR0EBcToADwwBCyAAIAAoAmgoAgAQSjcDACAAKQMAQgBTBEAgACgCVCAAKAJoKAIAEBcgACgCUBAkIABBADYCbAwDCyAAIAApAwAgACgCUCkDICAAKAJQKQMYfFE6AA8LIAAtAA9BAXFFBEAgACgCVEEVQQAQFCAAKAIsEBYgACgCUBAkIABBADYCbAwCCwsgACgCLBAWIAAgACgCUDYCbAsgACgCbCEBIABB8ABqJAAgAiABNgJIIAEEQAJAIAIoAkwEQCACKQMgQgBXBEAgAiACKAJYIAIoAkwgAkEQahBpNwMgCyACIAIoAlggAigCSCACQRBqEGk3AygCQCACKQMgIAIpAyhTBEAgAigCTBAkIAIgAigCSDYCTCACIAIpAyg3AyAMAQsgAigCSBAkCwwBCyACIAIoAkg2AkwCQCACKAJYKAIEQQRxBEAgAiACKAJYIAIoAkwgAkEQahBpNwMgDAELIAJCADcDIAsLIAJBADYCSAsgAiACKAJEQQFqNgJEIAIoAgwgAigCRAJ/IwBBEGsiACACKAIMNgIMIAAoAgwoAgQLa6wQLBoMAQsLIAIoAgwQFiACKQMgQgBTBEAgAigCWEEIaiACQRBqEEMgAigCTBAkIAJBADYCXAwBCyACIAIoAkw2AlwLIAIoAlwhACACQeAAaiQAIAMgADYCWCAARQRAIAMoAmAgAygCXEEIahBDIwBBEGsiACADKAJoNgIMIAAoAgwiACAAKAIwQQFqNgIwIAMoAlwQPSADQQA2AmwMAQsgAygCXCADKAJYKAIANgJAIAMoAlwgAygCWCkDCDcDMCADKAJcIAMoAlgpAxA3AzggAygCXCADKAJYKAIoNgIgIAMoAlgQFSADKAJcKAJQIQAgAygCXCkDMCEJIAMoAlxBCGohAiMAQSBrIgEkACABIAA2AhggASAJNwMQIAEgAjYCDAJAIAEpAxBQBEAgAUEBOgAfDAELIwBBIGsiACABKQMQNwMQIAAgACkDELpEAAAAAAAA6D+jOQMIAkAgACsDCEQAAOD////vQWQEQCAAQX82AgQMAQsgAAJ/IAArAwgiCkQAAAAAAADwQWMgCkQAAAAAAAAAAGZxBEAgCqsMAQtBAAs2AgQLAkAgACgCBEGAgICAeEsEQCAAQYCAgIB4NgIcDAELIAAgACgCBEEBazYCBCAAIAAoAgQgACgCBEEBdnI2AgQgACAAKAIEIAAoAgRBAnZyNgIEIAAgACgCBCAAKAIEQQR2cjYCBCAAIAAoAgQgACgCBEEIdnI2AgQgACAAKAIEIAAoAgRBEHZyNgIEIAAgACgCBEEBajYCBCAAIAAoAgQ2AhwLIAEgACgCHDYCCCABKAIIIAEoAhgoAgBNBEAgAUEBOgAfDAELIAEoAhggASgCCCABKAIMEFlBAXFFBEAgAUEAOgAfDAELIAFBAToAHwsgAS0AHxogAUEgaiQAIANCADcDEANAIAMpAxAgAygCXCkDMFQEQCADIAMoAlwoAkAgAykDEKdBBHRqKAIAKAIwQQBBACADKAJgEEY2AgwgAygCDEUEQCMAQRBrIgAgAygCaDYCDCAAKAIMIgAgACgCMEEBajYCMCADKAJcED0gA0EANgJsDAMLIAMoAlwoAlAgAygCDCADKQMQQQggAygCXEEIahB1QQFxRQRAAkAgAygCXCgCCEEKRgRAIAMoAmRBBHFFDQELIAMoAmAgAygCXEEIahBDIwBBEGsiACADKAJoNgIMIAAoAgwiACAAKAIwQQFqNgIwIAMoAlwQPSADQQA2AmwMBAsLIAMgAykDEEIBfDcDEAwBCwsgAygCXCADKAJcKAIUNgIYIAMgAygCXDYCbAsgAygCbCEAIANB8ABqJAAgBCAANgI4CyAEKAI4RQRAIAQoAlgQMRogBEEANgJcDAELIAQgBCgCODYCXAsgBCgCXCEAIARB4ABqJAAgAAuOAQEBfyMAQRBrIgIkACACIAA2AgwgAiABNgIIIAJBADYCBCACKAIIBEAjAEEQayIAIAIoAgg2AgwgAiAAKAIMKAIANgIEIAIoAggQlgFBAUYEQCMAQRBrIgAgAigCCDYCDEG0mwEgACgCDCgCBDYCAAsLIAIoAgwEQCACKAIMIAIoAgQ2AgALIAJBEGokAAuVAQEBfyMAQRBrIgEkACABIAA2AggCQAJ/IwBBEGsiACABKAIINgIMIAAoAgwpAxhCgIAQg1ALBEAgASgCCCgCAARAIAEgASgCCCgCABCeAUEBcToADwwCCyABQQE6AA8MAQsgASABKAIIQQBCAEESECE+AgQgASABKAIEQQBHOgAPCyABLQAPQQFxIQAgAUEQaiQAIAALfwEBfyMAQSBrIgMkACADIAA2AhggAyABNwMQIANBADYCDCADIAI2AggCQCADKQMQQv///////////wBWBEAgAygCCEEEQT0QFCADQX82AhwMAQsgAyADKAIYIAMpAxAgAygCDCADKAIIEGs2AhwLIAMoAhwhACADQSBqJAAgAAt9ACACQQFGBEAgASAAKAIIIAAoAgRrrH0hAQsCQCAAKAIUIAAoAhxLBEAgAEEAQQAgACgCJBEBABogACgCFEUNAQsgAEEANgIcIABCADcDECAAIAEgAiAAKAIoEQ8AQgBTDQAgAEIANwIEIAAgACgCAEFvcTYCAEEADwtBfwvhAgECfyMAQSBrIgMkAAJ/AkACQEGnEiABLAAAEKIBRQRAQbSbAUEcNgIADAELQZgJEBgiAg0BC0EADAELIAJBAEGQARAyIAFBKxCiAUUEQCACQQhBBCABLQAAQfIARhs2AgALAkAgAS0AAEHhAEcEQCACKAIAIQEMAQsgAEEDQQAQBCIBQYAIcUUEQCADIAFBgAhyNgIQIABBBCADQRBqEAQaCyACIAIoAgBBgAFyIgE2AgALIAJB/wE6AEsgAkGACDYCMCACIAA2AjwgAiACQZgBajYCLAJAIAFBCHENACADIANBGGo2AgAgAEGTqAEgAxAODQAgAkEKOgBLCyACQRo2AiggAkEbNgIkIAJBHDYCICACQR02AgxB6J8BKAIARQRAIAJBfzYCTAsgAkGsoAEoAgA2AjhBrKABKAIAIgAEQCAAIAI2AjQLQaygASACNgIAIAILIQAgA0EgaiQAIAAL8AEBAn8CfwJAIAFB/wFxIgMEQCAAQQNxBEADQCAALQAAIgJFDQMgAiABQf8BcUYNAyAAQQFqIgBBA3ENAAsLAkAgACgCACICQX9zIAJBgYKECGtxQYCBgoR4cQ0AIANBgYKECGwhAwNAIAIgA3MiAkF/cyACQYGChAhrcUGAgYKEeHENASAAKAIEIQIgAEEEaiEAIAJBgYKECGsgAkF/c3FBgIGChHhxRQ0ACwsDQCAAIgItAAAiAwRAIAJBAWohACADIAFB/wFxRw0BCwsgAgwCCyAAECsgAGoMAQsgAAsiAEEAIAAtAAAgAUH/AXFGGwsYACAAKAJMQX9MBEAgABCkAQ8LIAAQpAELYAIBfgJ/IAAoAighAkEBIQMgAEIAIAAtAABBgAFxBH9BAkEBIAAoAhQgACgCHEsbBUEBCyACEQ8AIgFCAFkEfiAAKAIUIAAoAhxrrCABIAAoAgggACgCBGusfXwFIAELC2sBAX8gAARAIAAoAkxBf0wEQCAAEG8PCyAAEG8PC0GwoAEoAgAEQEGwoAEoAgAQpQEhAQtBrKABKAIAIgAEQANAIAAoAkwaIAAoAhQgACgCHEsEQCAAEG8gAXIhAQsgACgCOCIADQALCyABCyIAIAAgARACIgBBgWBPBH9BtJsBQQAgAGs2AgBBfwUgAAsLUwEDfwJAIAAoAgAsAABBMGtBCk8NAANAIAAoAgAiAiwAACEDIAAgAkEBajYCACABIANqQTBrIQEgAiwAAUEwa0EKTw0BIAFBCmwhAQwACwALIAELuwIAAkAgAUEUSw0AAkACQAJAAkACQAJAAkACQAJAAkAgAUEJaw4KAAECAwQFBgcICQoLIAIgAigCACIBQQRqNgIAIAAgASgCADYCAA8LIAIgAigCACIBQQRqNgIAIAAgATQCADcDAA8LIAIgAigCACIBQQRqNgIAIAAgATUCADcDAA8LIAIgAigCAEEHakF4cSIBQQhqNgIAIAAgASkDADcDAA8LIAIgAigCACIBQQRqNgIAIAAgATIBADcDAA8LIAIgAigCACIBQQRqNgIAIAAgATMBADcDAA8LIAIgAigCACIBQQRqNgIAIAAgATAAADcDAA8LIAIgAigCACIBQQRqNgIAIAAgATEAADcDAA8LIAIgAigCAEEHakF4cSIBQQhqNgIAIAAgASsDADkDAA8LIAAgAkEYEQQACwt/AgF/AX4gAL0iA0I0iKdB/w9xIgJB/w9HBHwgAkUEQCABIABEAAAAAAAAAABhBH9BAAUgAEQAAAAAAADwQ6IgARCpASEAIAEoAgBBQGoLNgIAIAAPCyABIAJB/gdrNgIAIANC/////////4eAf4NCgICAgICAgPA/hL8FIAALC5sCACAARQRAQQAPCwJ/AkAgAAR/IAFB/wBNDQECQEGQmQEoAgAoAgBFBEAgAUGAf3FBgL8DRg0DDAELIAFB/w9NBEAgACABQT9xQYABcjoAASAAIAFBBnZBwAFyOgAAQQIMBAsgAUGAsANPQQAgAUGAQHFBgMADRxtFBEAgACABQT9xQYABcjoAAiAAIAFBDHZB4AFyOgAAIAAgAUEGdkE/cUGAAXI6AAFBAwwECyABQYCABGtB//8/TQRAIAAgAUE/cUGAAXI6AAMgACABQRJ2QfABcjoAACAAIAFBBnZBP3FBgAFyOgACIAAgAUEMdkE/cUGAAXI6AAFBBAwECwtBtJsBQRk2AgBBfwVBAQsMAQsgACABOgAAQQELC+MBAQJ/IAJBAEchAwJAAkACQCAAQQNxRQ0AIAJFDQAgAUH/AXEhBANAIAAtAAAgBEYNAiACQQFrIgJBAEchAyAAQQFqIgBBA3FFDQEgAg0ACwsgA0UNAQsCQCAALQAAIAFB/wFxRg0AIAJBBEkNACABQf8BcUGBgoQIbCEDA0AgACgCACADcyIEQX9zIARBgYKECGtxQYCBgoR4cQ0BIABBBGohACACQQRrIgJBA0sNAAsLIAJFDQAgAUH/AXEhAQNAIAEgAC0AAEYEQCAADwsgAEEBaiEAIAJBAWsiAg0ACwtBAAuLDAEGfyAAIAFqIQUCQAJAIAAoAgQiAkEBcQ0AIAJBA3FFDQEgACgCACICIAFqIQECQCAAIAJrIgBBzJsBKAIARwRAIAJB/wFNBEAgACgCCCIEIAJBA3YiAkEDdEHgmwFqRhogACgCDCIDIARHDQJBuJsBQbibASgCAEF+IAJ3cTYCAAwDCyAAKAIYIQYCQCAAIAAoAgwiA0cEQCAAKAIIIgJByJsBKAIASRogAiADNgIMIAMgAjYCCAwBCwJAIABBFGoiAigCACIEDQAgAEEQaiICKAIAIgQNAEEAIQMMAQsDQCACIQcgBCIDQRRqIgIoAgAiBA0AIANBEGohAiADKAIQIgQNAAsgB0EANgIACyAGRQ0CAkAgACAAKAIcIgRBAnRB6J0BaiICKAIARgRAIAIgAzYCACADDQFBvJsBQbybASgCAEF+IAR3cTYCAAwECyAGQRBBFCAGKAIQIABGG2ogAzYCACADRQ0DCyADIAY2AhggACgCECICBEAgAyACNgIQIAIgAzYCGAsgACgCFCICRQ0CIAMgAjYCFCACIAM2AhgMAgsgBSgCBCICQQNxQQNHDQFBwJsBIAE2AgAgBSACQX5xNgIEIAAgAUEBcjYCBCAFIAE2AgAPCyAEIAM2AgwgAyAENgIICwJAIAUoAgQiAkECcUUEQCAFQdCbASgCAEYEQEHQmwEgADYCAEHEmwFBxJsBKAIAIAFqIgE2AgAgACABQQFyNgIEIABBzJsBKAIARw0DQcCbAUEANgIAQcybAUEANgIADwsgBUHMmwEoAgBGBEBBzJsBIAA2AgBBwJsBQcCbASgCACABaiIBNgIAIAAgAUEBcjYCBCAAIAFqIAE2AgAPCyACQXhxIAFqIQECQCACQf8BTQRAIAUoAggiBCACQQN2IgJBA3RB4JsBakYaIAQgBSgCDCIDRgRAQbibAUG4mwEoAgBBfiACd3E2AgAMAgsgBCADNgIMIAMgBDYCCAwBCyAFKAIYIQYCQCAFIAUoAgwiA0cEQCAFKAIIIgJByJsBKAIASRogAiADNgIMIAMgAjYCCAwBCwJAIAVBFGoiBCgCACICDQAgBUEQaiIEKAIAIgINAEEAIQMMAQsDQCAEIQcgAiIDQRRqIgQoAgAiAg0AIANBEGohBCADKAIQIgINAAsgB0EANgIACyAGRQ0AAkAgBSAFKAIcIgRBAnRB6J0BaiICKAIARgRAIAIgAzYCACADDQFBvJsBQbybASgCAEF+IAR3cTYCAAwCCyAGQRBBFCAGKAIQIAVGG2ogAzYCACADRQ0BCyADIAY2AhggBSgCECICBEAgAyACNgIQIAIgAzYCGAsgBSgCFCICRQ0AIAMgAjYCFCACIAM2AhgLIAAgAUEBcjYCBCAAIAFqIAE2AgAgAEHMmwEoAgBHDQFBwJsBIAE2AgAPCyAFIAJBfnE2AgQgACABQQFyNgIEIAAgAWogATYCAAsgAUH/AU0EQCABQQN2IgJBA3RB4JsBaiEBAn9BuJsBKAIAIgNBASACdCICcUUEQEG4mwEgAiADcjYCACABDAELIAEoAggLIQIgASAANgIIIAIgADYCDCAAIAE2AgwgACACNgIIDwtBHyECIABCADcCECABQf///wdNBEAgAUEIdiICIAJBgP4/akEQdkEIcSIEdCICIAJBgOAfakEQdkEEcSIDdCICIAJBgIAPakEQdkECcSICdEEPdiADIARyIAJyayICQQF0IAEgAkEVanZBAXFyQRxqIQILIAAgAjYCHCACQQJ0QeidAWohBwJAAkBBvJsBKAIAIgRBASACdCIDcUUEQEG8mwEgAyAEcjYCACAHIAA2AgAgACAHNgIYDAELIAFBAEEZIAJBAXZrIAJBH0YbdCECIAcoAgAhAwNAIAMiBCgCBEF4cSABRg0CIAJBHXYhAyACQQF0IQIgBCADQQRxaiIHQRBqKAIAIgMNAAsgByAANgIQIAAgBDYCGAsgACAANgIMIAAgADYCCA8LIAQoAggiASAANgIMIAQgADYCCCAAQQA2AhggACAENgIMIAAgATYCCAsL+QIBAX8jAEEgayIEJAAgBCAANgIYIAQgATcDECAEIAI2AgwgBCADNgIIIAQgBCgCGCAEKAIYIAQpAxAgBCgCDCAEKAIIEK4BIgA2AgACQCAARQRAIARBADYCHAwBCyAEKAIAEElBAEgEQCAEKAIYQQhqIAQoAgAQFyAEKAIAEBsgBEEANgIcDAELIAQoAhghAiMAQRBrIgAkACAAIAI2AgggAEEYEBgiAjYCBAJAIAJFBEAgACgCCEEIakEOQQAQFCAAQQA2AgwMAQsgACgCBCAAKAIINgIAIwBBEGsiAiAAKAIEQQRqNgIMIAIoAgxBADYCACACKAIMQQA2AgQgAigCDEEANgIIIAAoAgRBADoAECAAKAIEQQA2AhQgACAAKAIENgIMCyAAKAIMIQIgAEEQaiQAIAQgAjYCBCACRQRAIAQoAgAQGyAEQQA2AhwMAQsgBCgCBCAEKAIANgIUIAQgBCgCBDYCHAsgBCgCHCEAIARBIGokACAAC7cOAgN/AX4jAEHAAWsiBSQAIAUgADYCuAEgBSABNgK0ASAFIAI3A6gBIAUgAzYCpAEgBUIANwOYASAFQgA3A5ABIAUgBDYCjAECQCAFKAK4AUUEQCAFQQA2ArwBDAELAkAgBSgCtAEEQCAFKQOoASAFKAK0ASkDMFQNAQsgBSgCuAFBCGpBEkEAEBQgBUEANgK8AQwBCwJAIAUoAqQBQQhxDQAgBSgCtAEoAkAgBSkDqAGnQQR0aigCCEUEQCAFKAK0ASgCQCAFKQOoAadBBHRqLQAMQQFxRQ0BCyAFKAK4AUEIakEPQQAQFCAFQQA2ArwBDAELIAUoArQBIAUpA6gBIAUoAqQBQQhyIAVByABqEH9BAEgEQCAFKAK4AUEIakEUQQAQFCAFQQA2ArwBDAELIAUoAqQBQSBxBEAgBSAFKAKkAUEEcjYCpAELAkAgBSkDmAFQBEAgBSkDkAFQDQELIAUoAqQBQQRxRQ0AIAUoArgBQQhqQRJBABAUIAVBADYCvAEMAQsCQCAFKQOYAVAEQCAFKQOQAVANAQsgBSkDmAEgBSkDmAEgBSkDkAF8WARAIAUpA2AgBSkDmAEgBSkDkAF8Wg0BCyAFKAK4AUEIakESQQAQFCAFQQA2ArwBDAELIAUpA5ABUARAIAUgBSkDYCAFKQOYAX03A5ABCyAFIAUpA5ABIAUpA2BUOgBHIAUgBSgCpAFBIHEEf0EABSAFLwF6QQBHC0EBcToARSAFIAUoAqQBQQRxBH9BAAUgBS8BeEEARwtBAXE6AEQgBQJ/IAUoAqQBQQRxBEBBACAFLwF4DQEaCyAFLQBHQX9zC0EBcToARiAFLQBFQQFxBEAgBSgCjAFFBEAgBSAFKAK4ASgCHDYCjAELIAUoAowBRQRAIAUoArgBQQhqQRpBABAUIAVBADYCvAEMAgsLIAUpA2hQBEAgBSAFKAK4AUEAQgBBABB+NgK8AQwBCwJAAkAgBS0AR0EBcUUNACAFLQBFQQFxDQAgBS0AREEBcQ0AIAUgBSkDkAE3AyAgBSAFKQOQATcDKCAFQQA7ATggBSAFKAJwNgIwIAVC3AA3AwggBSAFKAK0ASgCACAFKQOYASAFKQOQASAFQQhqQQAgBSgCtAEgBSkDqAEgBSgCuAFBCGoQZCIANgKIAQwBCyAFIAUoArQBIAUpA6gBIAUoAqQBIAUoArgBQQhqEEUiADYCBCAARQRAIAVBADYCvAEMAgsgBSAFKAK0ASgCAEIAIAUpA2ggBUHIAGogBSgCBC8BDEEBdkEDcSAFKAK0ASAFKQOoASAFKAK4AUEIahBkIgA2AogBCyAARQRAIAVBADYCvAEMAQsCfyAFKAKIASEAIAUoArQBIQMjAEEQayIBJAAgASAANgIMIAEgAzYCCCABKAIMIAEoAgg2AiwgASgCCCEDIAEoAgwhBCMAQSBrIgAkACAAIAM2AhggACAENgIUAkAgACgCGCgCSCAAKAIYKAJEQQFqTQRAIAAgACgCGCgCSEEKajYCDCAAIAAoAhgoAkwgACgCDEECdBBINgIQIAAoAhBFBEAgACgCGEEIakEOQQAQFCAAQX82AhwMAgsgACgCGCAAKAIMNgJIIAAoAhggACgCEDYCTAsgACgCFCEEIAAoAhgoAkwhBiAAKAIYIgcoAkQhAyAHIANBAWo2AkQgA0ECdCAGaiAENgIAIABBADYCHAsgACgCHCEDIABBIGokACABQRBqJAAgA0EASAsEQCAFKAKIARAbIAVBADYCvAEMAQsgBS0ARUEBcQRAIAUgBS8BekEAEHwiADYCACAARQRAIAUoArgBQQhqQRhBABAUIAVBADYCvAEMAgsgBSAFKAK4ASAFKAKIASAFLwF6QQAgBSgCjAEgBSgCABEFADYChAEgBSgCiAEQGyAFKAKEAUUEQCAFQQA2ArwBDAILIAUgBSgChAE2AogBCyAFLQBEQQFxBEAgBSAFKAK4ASAFKAKIASAFLwF4ELABNgKEASAFKAKIARAbIAUoAoQBRQRAIAVBADYCvAEMAgsgBSAFKAKEATYCiAELIAUtAEZBAXEEQCAFIAUoArgBIAUoAogBQQEQrwE2AoQBIAUoAogBEBsgBSgChAFFBEAgBUEANgK8AQwCCyAFIAUoAoQBNgKIAQsCQCAFLQBHQQFxRQ0AIAUtAEVBAXFFBEAgBS0AREEBcUUNAQsgBSgCuAEhASAFKAKIASEDIAUpA5gBIQIgBSkDkAEhCCMAQSBrIgAkACAAIAE2AhwgACADNgIYIAAgAjcDECAAIAg3AwggACgCGCAAKQMQIAApAwhBAEEAQQBCACAAKAIcQQhqEGQhASAAQSBqJAAgBSABNgKEASAFKAKIARAbIAUoAoQBRQRAIAVBADYCvAEMAgsgBSAFKAKEATYCiAELIAUgBSgCiAE2ArwBCyAFKAK8ASEAIAVBwAFqJAAgAAuEAgEBfyMAQSBrIgMkACADIAA2AhggAyABNgIUIAMgAjYCEAJAIAMoAhRFBEAgAygCGEEIakESQQAQFCADQQA2AhwMAQsgA0E4EBgiADYCDCAARQRAIAMoAhhBCGpBDkEAEBQgA0EANgIcDAELIwBBEGsiACADKAIMQQhqNgIMIAAoAgxBADYCACAAKAIMQQA2AgQgACgCDEEANgIIIAMoAgwgAygCEDYCACADKAIMQQA2AgQgAygCDEIANwMoQQBBAEEAEBohACADKAIMIAA2AjAgAygCDEIANwMYIAMgAygCGCADKAIUQRQgAygCDBBmNgIcCyADKAIcIQAgA0EgaiQAIAALQwEBfyMAQRBrIgMkACADIAA2AgwgAyABNgIIIAMgAjYCBCADKAIMIAMoAgggAygCBEEAQQAQsgEhACADQRBqJAAgAAtJAQF/IwBBEGsiASQAIAEgADYCDCABKAIMBEAgASgCDCgCrEAgASgCDCgCqEAoAgQRAgAgASgCDBA3IAEoAgwQFQsgAUEQaiQAC5QFAQF/IwBBMGsiBSQAIAUgADYCKCAFIAE2AiQgBSACNgIgIAUgAzoAHyAFIAQ2AhggBUEANgIMAkAgBSgCJEUEQCAFKAIoQQhqQRJBABAUIAVBADYCLAwBCyAFIAUoAiAgBS0AH0EBcRCzASIANgIMIABFBEAgBSgCKEEIakEQQQAQFCAFQQA2AiwMAQsgBSgCICEBIAUtAB9BAXEhAiAFKAIYIQMgBSgCDCEEIwBBIGsiACQAIAAgATYCGCAAIAI6ABcgACADNgIQIAAgBDYCDCAAQbDAABAYIgE2AggCQCABRQRAIABBADYCHAwBCyMAQRBrIgEgACgCCDYCDCABKAIMQQA2AgAgASgCDEEANgIEIAEoAgxBADYCCCAAKAIIAn8gAC0AF0EBcQRAIAAoAhhBf0cEfyAAKAIYQX5GBUEBC0EBcQwBC0EAC0EARzoADiAAKAIIIAAoAgw2AqhAIAAoAgggACgCGDYCFCAAKAIIIAAtABdBAXE6ABAgACgCCEEAOgAMIAAoAghBADoADSAAKAIIQQA6AA8gACgCCCgCqEAoAgAhAQJ/AkAgACgCGEF/RwRAIAAoAhhBfkcNAQtBCAwBCyAAKAIYC0H//wNxIAAoAhAgACgCCCABEQEAIQEgACgCCCABNgKsQCABRQRAIAAoAggQNyAAKAIIEBUgAEEANgIcDAELIAAgACgCCDYCHAsgACgCHCEBIABBIGokACAFIAE2AhQgAUUEQCAFKAIoQQhqQQ5BABAUIAVBADYCLAwBCyAFIAUoAiggBSgCJEETIAUoAhQQZiIANgIQIABFBEAgBSgCFBCxASAFQQA2AiwMAQsgBSAFKAIQNgIsCyAFKAIsIQAgBUEwaiQAIAALzAEBAX8jAEEgayICIAA2AhggAiABOgAXIAICfwJAIAIoAhhBf0cEQCACKAIYQX5HDQELQQgMAQsgAigCGAs7AQ4gAkEANgIQAkADQCACKAIQQdSXASgCAEkEQCACKAIQQQxsQdiXAWovAQAgAi8BDkYEQCACLQAXQQFxBEAgAiACKAIQQQxsQdiXAWooAgQ2AhwMBAsgAiACKAIQQQxsQdiXAWooAgg2AhwMAwUgAiACKAIQQQFqNgIQDAILAAsLIAJBADYCHAsgAigCHAvkAQEBfyMAQSBrIgMkACADIAA6ABsgAyABNgIUIAMgAjYCECADQcgAEBgiADYCDAJAIABFBEAgAygCEEEBQbSbASgCABAUIANBADYCHAwBCyADKAIMIAMoAhA2AgAgAygCDCADLQAbQQFxOgAEIAMoAgwgAygCFDYCCAJAIAMoAgwoAghBAU4EQCADKAIMKAIIQQlMDQELIAMoAgxBCTYCCAsgAygCDEEAOgAMIAMoAgxBADYCMCADKAIMQQA2AjQgAygCDEEANgI4IAMgAygCDDYCHAsgAygCHCEAIANBIGokACAAC+MIAQF/IwBBQGoiAiAANgI4IAIgATYCNCACIAIoAjgoAnw2AjAgAiACKAI4KAI4IAIoAjgoAmxqNgIsIAIgAigCOCgCeDYCICACIAIoAjgoApABNgIcIAICfyACKAI4KAJsIAIoAjgoAixBhgJrSwRAIAIoAjgoAmwgAigCOCgCLEGGAmtrDAELQQALNgIYIAIgAigCOCgCQDYCFCACIAIoAjgoAjQ2AhAgAiACKAI4KAI4IAIoAjgoAmxqQYICajYCDCACIAIoAiwgAigCIEEBa2otAAA6AAsgAiACKAIsIAIoAiBqLQAAOgAKIAIoAjgoAnggAigCOCgCjAFPBEAgAiACKAIwQQJ2NgIwCyACKAIcIAIoAjgoAnRLBEAgAiACKAI4KAJ0NgIcCwNAAkAgAiACKAI4KAI4IAIoAjRqNgIoAkAgAigCKCACKAIgai0AACACLQAKRw0AIAIoAiggAigCIEEBa2otAAAgAi0AC0cNACACKAIoLQAAIAIoAiwtAABHDQAgAiACKAIoIgBBAWo2AiggAC0AASACKAIsLQABRwRADAELIAIgAigCLEECajYCLCACIAIoAihBAWo2AigDQCACIAIoAiwiAEEBajYCLCAALQABIQEgAiACKAIoIgBBAWo2AigCf0EAIAAtAAEgAUcNABogAiACKAIsIgBBAWo2AiwgAC0AASEBIAIgAigCKCIAQQFqNgIoQQAgAC0AASABRw0AGiACIAIoAiwiAEEBajYCLCAALQABIQEgAiACKAIoIgBBAWo2AihBACAALQABIAFHDQAaIAIgAigCLCIAQQFqNgIsIAAtAAEhASACIAIoAigiAEEBajYCKEEAIAAtAAEgAUcNABogAiACKAIsIgBBAWo2AiwgAC0AASEBIAIgAigCKCIAQQFqNgIoQQAgAC0AASABRw0AGiACIAIoAiwiAEEBajYCLCAALQABIQEgAiACKAIoIgBBAWo2AihBACAALQABIAFHDQAaIAIgAigCLCIAQQFqNgIsIAAtAAEhASACIAIoAigiAEEBajYCKEEAIAAtAAEgAUcNABogAiACKAIsIgBBAWo2AiwgAC0AASEBIAIgAigCKCIAQQFqNgIoQQAgAC0AASABRw0AGiACKAIsIAIoAgxJC0EBcQ0ACyACQYICIAIoAgwgAigCLGtrNgIkIAIgAigCDEGCAms2AiwgAigCJCACKAIgSgRAIAIoAjggAigCNDYCcCACIAIoAiQ2AiAgAigCJCACKAIcTg0CIAIgAigCLCACKAIgQQFrai0AADoACyACIAIoAiwgAigCIGotAAA6AAoLCyACIAIoAhQgAigCNCACKAIQcUEBdGovAQAiATYCNEEAIQAgASACKAIYSwR/IAIgAigCMEEBayIANgIwIABBAEcFQQALQQFxDQELCwJAIAIoAiAgAigCOCgCdE0EQCACIAIoAiA2AjwMAQsgAiACKAI4KAJ0NgI8CyACKAI8C5IQAQF/IwBBMGsiAiQAIAIgADYCKCACIAE2AiQgAgJ/IAIoAigoAiwgAigCKCgCDEEFa0kEQCACKAIoKAIsDAELIAIoAigoAgxBBWsLNgIgIAJBADYCECACIAIoAigoAgAoAgQ2AgwDQAJAIAJB//8DNgIcIAIgAigCKCgCvC1BKmpBA3U2AhQgAigCKCgCACgCECACKAIUSQ0AIAIgAigCKCgCACgCECACKAIUazYCFCACIAIoAigoAmwgAigCKCgCXGs2AhggAigCHCACKAIYIAIoAigoAgAoAgRqSwRAIAIgAigCGCACKAIoKAIAKAIEajYCHAsgAigCHCACKAIUSwRAIAIgAigCFDYCHAsCQCACKAIcIAIoAiBPDQACQCACKAIcRQRAIAIoAiRBBEcNAQsgAigCJEUNACACKAIcIAIoAhggAigCKCgCACgCBGpGDQELDAELQQAhACACIAIoAiRBBEYEfyACKAIcIAIoAhggAigCKCgCACgCBGpGBUEAC0EBcTYCECACKAIoQQBBACACKAIQEFwgAigCKCgCCCACKAIoKAIUQQRraiACKAIcOgAAIAIoAigoAgggAigCKCgCFEEDa2ogAigCHEEIdjoAACACKAIoKAIIIAIoAigoAhRBAmtqIAIoAhxBf3M6AAAgAigCKCgCCCACKAIoKAIUQQFraiACKAIcQX9zQQh2OgAAIAIoAigoAgAQHCACKAIYBEAgAigCGCACKAIcSwRAIAIgAigCHDYCGAsgAigCKCgCACgCDCACKAIoKAI4IAIoAigoAlxqIAIoAhgQGRogAigCKCgCACIAIAIoAhggACgCDGo2AgwgAigCKCgCACIAIAAoAhAgAigCGGs2AhAgAigCKCgCACIAIAIoAhggACgCFGo2AhQgAigCKCIAIAIoAhggACgCXGo2AlwgAiACKAIcIAIoAhhrNgIcCyACKAIcBEAgAigCKCgCACACKAIoKAIAKAIMIAIoAhwQeBogAigCKCgCACIAIAIoAhwgACgCDGo2AgwgAigCKCgCACIAIAAoAhAgAigCHGs2AhAgAigCKCgCACIAIAIoAhwgACgCFGo2AhQLIAIoAhBFDQELCyACIAIoAgwgAigCKCgCACgCBGs2AgwgAigCDARAAkAgAigCDCACKAIoKAIsTwRAIAIoAihBAjYCsC0gAigCKCgCOCACKAIoKAIAKAIAIAIoAigoAixrIAIoAigoAiwQGRogAigCKCACKAIoKAIsNgJsDAELIAIoAgwgAigCKCgCPCACKAIoKAJsa08EQCACKAIoIgAgACgCbCACKAIoKAIsazYCbCACKAIoKAI4IAIoAigoAjggAigCKCgCLGogAigCKCgCbBAZGiACKAIoKAKwLUECSQRAIAIoAigiACAAKAKwLUEBajYCsC0LCyACKAIoKAI4IAIoAigoAmxqIAIoAigoAgAoAgAgAigCDGsgAigCDBAZGiACKAIoIgAgAigCDCAAKAJsajYCbAsgAigCKCACKAIoKAJsNgJcIAIoAigiAQJ/IAIoAgwgAigCKCgCLCACKAIoKAK0LWtLBEAgAigCKCgCLCACKAIoKAK0LWsMAQsgAigCDAsgASgCtC1qNgK0LQsgAigCKCgCwC0gAigCKCgCbEkEQCACKAIoIAIoAigoAmw2AsAtCwJAIAIoAhAEQCACQQM2AiwMAQsCQCACKAIkRQ0AIAIoAiRBBEYNACACKAIoKAIAKAIEDQAgAigCKCgCbCACKAIoKAJcRw0AIAJBATYCLAwBCyACIAIoAigoAjwgAigCKCgCbGtBAWs2AhQCQCACKAIoKAIAKAIEIAIoAhRNDQAgAigCKCgCXCACKAIoKAIsSA0AIAIoAigiACAAKAJcIAIoAigoAixrNgJcIAIoAigiACAAKAJsIAIoAigoAixrNgJsIAIoAigoAjggAigCKCgCOCACKAIoKAIsaiACKAIoKAJsEBkaIAIoAigoArAtQQJJBEAgAigCKCIAIAAoArAtQQFqNgKwLQsgAiACKAIoKAIsIAIoAhRqNgIUCyACKAIUIAIoAigoAgAoAgRLBEAgAiACKAIoKAIAKAIENgIUCyACKAIUBEAgAigCKCgCACACKAIoKAI4IAIoAigoAmxqIAIoAhQQeBogAigCKCIAIAIoAhQgACgCbGo2AmwLIAIoAigoAsAtIAIoAigoAmxJBEAgAigCKCACKAIoKAJsNgLALQsgAiACKAIoKAK8LUEqakEDdTYCFCACIAIoAigoAgwgAigCFGtB//8DSwR/Qf//AwUgAigCKCgCDCACKAIUaws2AhQgAgJ/IAIoAhQgAigCKCgCLEsEQCACKAIoKAIsDAELIAIoAhQLNgIgIAIgAigCKCgCbCACKAIoKAJcazYCGAJAIAIoAhggAigCIEkEQCACKAIYRQRAIAIoAiRBBEcNAgsgAigCJEUNASACKAIoKAIAKAIEDQEgAigCGCACKAIUSw0BCyACAn8gAigCGCACKAIUSwRAIAIoAhQMAQsgAigCGAs2AhwgAgJ/QQAgAigCJEEERw0AGkEAIAIoAigoAgAoAgQNABogAigCHCACKAIYRgtBAXE2AhAgAigCKCACKAIoKAI4IAIoAigoAlxqIAIoAhwgAigCEBBcIAIoAigiACACKAIcIAAoAlxqNgJcIAIoAigoAgAQHAsgAkECQQAgAigCEBs2AiwLIAIoAiwhACACQTBqJAAgAAuyAgEBfyMAQRBrIgEkACABIAA2AggCQCABKAIIEHkEQCABQX42AgwMAQsgASABKAIIKAIcKAIENgIEIAEoAggoAhwoAggEQCABKAIIKAIoIAEoAggoAhwoAgggASgCCCgCJBEEAAsgASgCCCgCHCgCRARAIAEoAggoAiggASgCCCgCHCgCRCABKAIIKAIkEQQACyABKAIIKAIcKAJABEAgASgCCCgCKCABKAIIKAIcKAJAIAEoAggoAiQRBAALIAEoAggoAhwoAjgEQCABKAIIKAIoIAEoAggoAhwoAjggASgCCCgCJBEEAAsgASgCCCgCKCABKAIIKAIcIAEoAggoAiQRBAAgASgCCEEANgIcIAFBfUEAIAEoAgRB8QBGGzYCDAsgASgCDCEAIAFBEGokACAAC+sXAQJ/IwBB8ABrIgMgADYCbCADIAE2AmggAyACNgJkIANBfzYCXCADIAMoAmgvAQI2AlQgA0EANgJQIANBBzYCTCADQQQ2AkggAygCVEUEQCADQYoBNgJMIANBAzYCSAsgA0EANgJgA0AgAygCYCADKAJkSkUEQCADIAMoAlQ2AlggAyADKAJoIAMoAmBBAWpBAnRqLwECNgJUIAMgAygCUEEBaiIANgJQAkACQCADKAJMIABMDQAgAygCWCADKAJURw0ADAELAkAgAygCUCADKAJISARAA0AgAyADKAJsQfwUaiADKAJYQQJ0ai8BAjYCRAJAIAMoAmwoArwtQRAgAygCRGtKBEAgAyADKAJsQfwUaiADKAJYQQJ0ai8BADYCQCADKAJsIgAgAC8BuC0gAygCQEH//wNxIAMoAmwoArwtdHI7AbgtIAMoAmwvAbgtQf8BcSEBIAMoAmwoAgghAiADKAJsIgQoAhQhACAEIABBAWo2AhQgACACaiABOgAAIAMoAmwvAbgtQQh2IQEgAygCbCgCCCECIAMoAmwiBCgCFCEAIAQgAEEBajYCFCAAIAJqIAE6AAAgAygCbCADKAJAQf//A3FBECADKAJsKAK8LWt1OwG4LSADKAJsIgAgACgCvC0gAygCREEQa2o2ArwtDAELIAMoAmwiACAALwG4LSADKAJsQfwUaiADKAJYQQJ0ai8BACADKAJsKAK8LXRyOwG4LSADKAJsIgAgAygCRCAAKAK8LWo2ArwtCyADIAMoAlBBAWsiADYCUCAADQALDAELAkAgAygCWARAIAMoAlggAygCXEcEQCADIAMoAmxB/BRqIAMoAlhBAnRqLwECNgI8AkAgAygCbCgCvC1BECADKAI8a0oEQCADIAMoAmxB/BRqIAMoAlhBAnRqLwEANgI4IAMoAmwiACAALwG4LSADKAI4Qf//A3EgAygCbCgCvC10cjsBuC0gAygCbC8BuC1B/wFxIQEgAygCbCgCCCECIAMoAmwiBCgCFCEAIAQgAEEBajYCFCAAIAJqIAE6AAAgAygCbC8BuC1BCHYhASADKAJsKAIIIQIgAygCbCIEKAIUIQAgBCAAQQFqNgIUIAAgAmogAToAACADKAJsIAMoAjhB//8DcUEQIAMoAmwoArwta3U7AbgtIAMoAmwiACAAKAK8LSADKAI8QRBrajYCvC0MAQsgAygCbCIAIAAvAbgtIAMoAmxB/BRqIAMoAlhBAnRqLwEAIAMoAmwoArwtdHI7AbgtIAMoAmwiACADKAI8IAAoArwtajYCvC0LIAMgAygCUEEBazYCUAsgAyADKAJsLwG+FTYCNAJAIAMoAmwoArwtQRAgAygCNGtKBEAgAyADKAJsLwG8FTYCMCADKAJsIgAgAC8BuC0gAygCMEH//wNxIAMoAmwoArwtdHI7AbgtIAMoAmwvAbgtQf8BcSEBIAMoAmwoAgghAiADKAJsIgQoAhQhACAEIABBAWo2AhQgACACaiABOgAAIAMoAmwvAbgtQQh2IQEgAygCbCgCCCECIAMoAmwiBCgCFCEAIAQgAEEBajYCFCAAIAJqIAE6AAAgAygCbCADKAIwQf//A3FBECADKAJsKAK8LWt1OwG4LSADKAJsIgAgACgCvC0gAygCNEEQa2o2ArwtDAELIAMoAmwiACAALwG4LSADKAJsLwG8FSADKAJsKAK8LXRyOwG4LSADKAJsIgAgAygCNCAAKAK8LWo2ArwtCyADQQI2AiwCQCADKAJsKAK8LUEQIAMoAixrSgRAIAMgAygCUEEDazYCKCADKAJsIgAgAC8BuC0gAygCKEH//wNxIAMoAmwoArwtdHI7AbgtIAMoAmwvAbgtQf8BcSEBIAMoAmwoAgghAiADKAJsIgQoAhQhACAEIABBAWo2AhQgACACaiABOgAAIAMoAmwvAbgtQQh2IQEgAygCbCgCCCECIAMoAmwiBCgCFCEAIAQgAEEBajYCFCAAIAJqIAE6AAAgAygCbCADKAIoQf//A3FBECADKAJsKAK8LWt1OwG4LSADKAJsIgAgACgCvC0gAygCLEEQa2o2ArwtDAELIAMoAmwiACAALwG4LSADKAJQQQNrQf//A3EgAygCbCgCvC10cjsBuC0gAygCbCIAIAMoAiwgACgCvC1qNgK8LQsMAQsCQCADKAJQQQpMBEAgAyADKAJsLwHCFTYCJAJAIAMoAmwoArwtQRAgAygCJGtKBEAgAyADKAJsLwHAFTYCICADKAJsIgAgAC8BuC0gAygCIEH//wNxIAMoAmwoArwtdHI7AbgtIAMoAmwvAbgtQf8BcSEBIAMoAmwoAgghAiADKAJsIgQoAhQhACAEIABBAWo2AhQgACACaiABOgAAIAMoAmwvAbgtQQh2IQEgAygCbCgCCCECIAMoAmwiBCgCFCEAIAQgAEEBajYCFCAAIAJqIAE6AAAgAygCbCADKAIgQf//A3FBECADKAJsKAK8LWt1OwG4LSADKAJsIgAgACgCvC0gAygCJEEQa2o2ArwtDAELIAMoAmwiACAALwG4LSADKAJsLwHAFSADKAJsKAK8LXRyOwG4LSADKAJsIgAgAygCJCAAKAK8LWo2ArwtCyADQQM2AhwCQCADKAJsKAK8LUEQIAMoAhxrSgRAIAMgAygCUEEDazYCGCADKAJsIgAgAC8BuC0gAygCGEH//wNxIAMoAmwoArwtdHI7AbgtIAMoAmwvAbgtQf8BcSEBIAMoAmwoAgghAiADKAJsIgQoAhQhACAEIABBAWo2AhQgACACaiABOgAAIAMoAmwvAbgtQQh2IQEgAygCbCgCCCECIAMoAmwiBCgCFCEAIAQgAEEBajYCFCAAIAJqIAE6AAAgAygCbCADKAIYQf//A3FBECADKAJsKAK8LWt1OwG4LSADKAJsIgAgACgCvC0gAygCHEEQa2o2ArwtDAELIAMoAmwiACAALwG4LSADKAJQQQNrQf//A3EgAygCbCgCvC10cjsBuC0gAygCbCIAIAMoAhwgACgCvC1qNgK8LQsMAQsgAyADKAJsLwHGFTYCFAJAIAMoAmwoArwtQRAgAygCFGtKBEAgAyADKAJsLwHEFTYCECADKAJsIgAgAC8BuC0gAygCEEH//wNxIAMoAmwoArwtdHI7AbgtIAMoAmwvAbgtQf8BcSEBIAMoAmwoAgghAiADKAJsIgQoAhQhACAEIABBAWo2AhQgACACaiABOgAAIAMoAmwvAbgtQQh2IQEgAygCbCgCCCECIAMoAmwiBCgCFCEAIAQgAEEBajYCFCAAIAJqIAE6AAAgAygCbCADKAIQQf//A3FBECADKAJsKAK8LWt1OwG4LSADKAJsIgAgACgCvC0gAygCFEEQa2o2ArwtDAELIAMoAmwiACAALwG4LSADKAJsLwHEFSADKAJsKAK8LXRyOwG4LSADKAJsIgAgAygCFCAAKAK8LWo2ArwtCyADQQc2AgwCQCADKAJsKAK8LUEQIAMoAgxrSgRAIAMgAygCUEELazYCCCADKAJsIgAgAC8BuC0gAygCCEH//wNxIAMoAmwoArwtdHI7AbgtIAMoAmwvAbgtQf8BcSEBIAMoAmwoAgghAiADKAJsIgQoAhQhACAEIABBAWo2AhQgACACaiABOgAAIAMoAmwvAbgtQQh2IQEgAygCbCgCCCECIAMoAmwiBCgCFCEAIAQgAEEBajYCFCAAIAJqIAE6AAAgAygCbCADKAIIQf//A3FBECADKAJsKAK8LWt1OwG4LSADKAJsIgAgACgCvC0gAygCDEEQa2o2ArwtDAELIAMoAmwiACAALwG4LSADKAJQQQtrQf//A3EgAygCbCgCvC10cjsBuC0gAygCbCIAIAMoAgwgACgCvC1qNgK8LQsLCwsgA0EANgJQIAMgAygCWDYCXAJAIAMoAlRFBEAgA0GKATYCTCADQQM2AkgMAQsCQCADKAJYIAMoAlRGBEAgA0EGNgJMIANBAzYCSAwBCyADQQc2AkwgA0EENgJICwsLIAMgAygCYEEBajYCYAwBCwsLkQQBAX8jAEEwayIDIAA2AiwgAyABNgIoIAMgAjYCJCADQX82AhwgAyADKAIoLwECNgIUIANBADYCECADQQc2AgwgA0EENgIIIAMoAhRFBEAgA0GKATYCDCADQQM2AggLIAMoAiggAygCJEEBakECdGpB//8DOwECIANBADYCIANAIAMoAiAgAygCJEpFBEAgAyADKAIUNgIYIAMgAygCKCADKAIgQQFqQQJ0ai8BAjYCFCADIAMoAhBBAWoiADYCEAJAAkAgAygCDCAATA0AIAMoAhggAygCFEcNAAwBCwJAIAMoAhAgAygCCEgEQCADKAIsQfwUaiADKAIYQQJ0aiIAIAMoAhAgAC8BAGo7AQAMAQsCQCADKAIYBEAgAygCGCADKAIcRwRAIAMoAiwgAygCGEECdGpB/BRqIgAgAC8BAEEBajsBAAsgAygCLCIAIABBvBVqLwEAQQFqOwG8FQwBCwJAIAMoAhBBCkwEQCADKAIsIgAgAEHAFWovAQBBAWo7AcAVDAELIAMoAiwiACAAQcQVai8BAEEBajsBxBULCwsgA0EANgIQIAMgAygCGDYCHAJAIAMoAhRFBEAgA0GKATYCDCADQQM2AggMAQsCQCADKAIYIAMoAhRGBEAgA0EGNgIMIANBAzYCCAwBCyADQQc2AgwgA0EENgIICwsLIAMgAygCIEEBajYCIAwBCwsLpxIBAn8jAEHQAGsiAyAANgJMIAMgATYCSCADIAI2AkQgA0EANgI4IAMoAkwoAqAtBEADQCADIAMoAkwoAqQtIAMoAjhBAXRqLwEANgJAIAMoAkwoApgtIQAgAyADKAI4IgFBAWo2AjggAyAAIAFqLQAANgI8AkAgAygCQEUEQCADIAMoAkggAygCPEECdGovAQI2AiwCQCADKAJMKAK8LUEQIAMoAixrSgRAIAMgAygCSCADKAI8QQJ0ai8BADYCKCADKAJMIgAgAC8BuC0gAygCKEH//wNxIAMoAkwoArwtdHI7AbgtIAMoAkwvAbgtQf8BcSEBIAMoAkwoAgghAiADKAJMIgQoAhQhACAEIABBAWo2AhQgACACaiABOgAAIAMoAkwvAbgtQQh2IQEgAygCTCgCCCECIAMoAkwiBCgCFCEAIAQgAEEBajYCFCAAIAJqIAE6AAAgAygCTCADKAIoQf//A3FBECADKAJMKAK8LWt1OwG4LSADKAJMIgAgACgCvC0gAygCLEEQa2o2ArwtDAELIAMoAkwiACAALwG4LSADKAJIIAMoAjxBAnRqLwEAIAMoAkwoArwtdHI7AbgtIAMoAkwiACADKAIsIAAoArwtajYCvC0LDAELIAMgAygCPC0A0F02AjQgAyADKAJIIAMoAjRBgQJqQQJ0ai8BAjYCJAJAIAMoAkwoArwtQRAgAygCJGtKBEAgAyADKAJIIAMoAjRBgQJqQQJ0ai8BADYCICADKAJMIgAgAC8BuC0gAygCIEH//wNxIAMoAkwoArwtdHI7AbgtIAMoAkwvAbgtQf8BcSEBIAMoAkwoAgghAiADKAJMIgQoAhQhACAEIABBAWo2AhQgACACaiABOgAAIAMoAkwvAbgtQQh2IQEgAygCTCgCCCECIAMoAkwiBCgCFCEAIAQgAEEBajYCFCAAIAJqIAE6AAAgAygCTCADKAIgQf//A3FBECADKAJMKAK8LWt1OwG4LSADKAJMIgAgACgCvC0gAygCJEEQa2o2ArwtDAELIAMoAkwiACAALwG4LSADKAJIIAMoAjRBgQJqQQJ0ai8BACADKAJMKAK8LXRyOwG4LSADKAJMIgAgAygCJCAAKAK8LWo2ArwtCyADIAMoAjRBAnRBkOoAaigCADYCMCADKAIwBEAgAyADKAI8IAMoAjRBAnRBgO0AaigCAGs2AjwgAyADKAIwNgIcAkAgAygCTCgCvC1BECADKAIca0oEQCADIAMoAjw2AhggAygCTCIAIAAvAbgtIAMoAhhB//8DcSADKAJMKAK8LXRyOwG4LSADKAJMLwG4LUH/AXEhASADKAJMKAIIIQIgAygCTCIEKAIUIQAgBCAAQQFqNgIUIAAgAmogAToAACADKAJMLwG4LUEIdiEBIAMoAkwoAgghAiADKAJMIgQoAhQhACAEIABBAWo2AhQgACACaiABOgAAIAMoAkwgAygCGEH//wNxQRAgAygCTCgCvC1rdTsBuC0gAygCTCIAIAAoArwtIAMoAhxBEGtqNgK8LQwBCyADKAJMIgAgAC8BuC0gAygCPEH//wNxIAMoAkwoArwtdHI7AbgtIAMoAkwiACADKAIcIAAoArwtajYCvC0LCyADIAMoAkBBAWs2AkAgAwJ/IAMoAkBBgAJJBEAgAygCQC0A0FkMAQsgAygCQEEHdkGAAmotANBZCzYCNCADIAMoAkQgAygCNEECdGovAQI2AhQCQCADKAJMKAK8LUEQIAMoAhRrSgRAIAMgAygCRCADKAI0QQJ0ai8BADYCECADKAJMIgAgAC8BuC0gAygCEEH//wNxIAMoAkwoArwtdHI7AbgtIAMoAkwvAbgtQf8BcSEBIAMoAkwoAgghAiADKAJMIgQoAhQhACAEIABBAWo2AhQgACACaiABOgAAIAMoAkwvAbgtQQh2IQEgAygCTCgCCCECIAMoAkwiBCgCFCEAIAQgAEEBajYCFCAAIAJqIAE6AAAgAygCTCADKAIQQf//A3FBECADKAJMKAK8LWt1OwG4LSADKAJMIgAgACgCvC0gAygCFEEQa2o2ArwtDAELIAMoAkwiACAALwG4LSADKAJEIAMoAjRBAnRqLwEAIAMoAkwoArwtdHI7AbgtIAMoAkwiACADKAIUIAAoArwtajYCvC0LIAMgAygCNEECdEGQ6wBqKAIANgIwIAMoAjAEQCADIAMoAkAgAygCNEECdEGA7gBqKAIAazYCQCADIAMoAjA2AgwCQCADKAJMKAK8LUEQIAMoAgxrSgRAIAMgAygCQDYCCCADKAJMIgAgAC8BuC0gAygCCEH//wNxIAMoAkwoArwtdHI7AbgtIAMoAkwvAbgtQf8BcSEBIAMoAkwoAgghAiADKAJMIgQoAhQhACAEIABBAWo2AhQgACACaiABOgAAIAMoAkwvAbgtQQh2IQEgAygCTCgCCCECIAMoAkwiBCgCFCEAIAQgAEEBajYCFCAAIAJqIAE6AAAgAygCTCADKAIIQf//A3FBECADKAJMKAK8LWt1OwG4LSADKAJMIgAgACgCvC0gAygCDEEQa2o2ArwtDAELIAMoAkwiACAALwG4LSADKAJAQf//A3EgAygCTCgCvC10cjsBuC0gAygCTCIAIAMoAgwgACgCvC1qNgK8LQsLCyADKAI4IAMoAkwoAqAtSQ0ACwsgAyADKAJILwGCCDYCBAJAIAMoAkwoArwtQRAgAygCBGtKBEAgAyADKAJILwGACDYCACADKAJMIgAgAC8BuC0gAygCAEH//wNxIAMoAkwoArwtdHI7AbgtIAMoAkwvAbgtQf8BcSEBIAMoAkwoAgghAiADKAJMIgQoAhQhACAEIABBAWo2AhQgACACaiABOgAAIAMoAkwvAbgtQQh2IQEgAygCTCgCCCECIAMoAkwiBCgCFCEAIAQgAEEBajYCFCAAIAJqIAE6AAAgAygCTCADKAIAQf//A3FBECADKAJMKAK8LWt1OwG4LSADKAJMIgAgACgCvC0gAygCBEEQa2o2ArwtDAELIAMoAkwiACAALwG4LSADKAJILwGACCADKAJMKAK8LXRyOwG4LSADKAJMIgAgAygCBCAAKAK8LWo2ArwtCwuXAgEEfyMAQRBrIgEgADYCDAJAIAEoAgwoArwtQRBGBEAgASgCDC8BuC1B/wFxIQIgASgCDCgCCCEDIAEoAgwiBCgCFCEAIAQgAEEBajYCFCAAIANqIAI6AAAgASgCDC8BuC1BCHYhAiABKAIMKAIIIQMgASgCDCIEKAIUIQAgBCAAQQFqNgIUIAAgA2ogAjoAACABKAIMQQA7AbgtIAEoAgxBADYCvC0MAQsgASgCDCgCvC1BCE4EQCABKAIMLwG4LSECIAEoAgwoAgghAyABKAIMIgQoAhQhACAEIABBAWo2AhQgACADaiACOgAAIAEoAgwiACAALwG4LUEIdjsBuC0gASgCDCIAIAAoArwtQQhrNgK8LQsLC+8BAQR/IwBBEGsiASAANgIMAkAgASgCDCgCvC1BCEoEQCABKAIMLwG4LUH/AXEhAiABKAIMKAIIIQMgASgCDCIEKAIUIQAgBCAAQQFqNgIUIAAgA2ogAjoAACABKAIMLwG4LUEIdiECIAEoAgwoAgghAyABKAIMIgQoAhQhACAEIABBAWo2AhQgACADaiACOgAADAELIAEoAgwoArwtQQBKBEAgASgCDC8BuC0hAiABKAIMKAIIIQMgASgCDCIEKAIUIQAgBCAAQQFqNgIUIAAgA2ogAjoAAAsLIAEoAgxBADsBuC0gASgCDEEANgK8LQv8AQEBfyMAQRBrIgEgADYCDCABQQA2AggDQCABKAIIQZ4CTkUEQCABKAIMQZQBaiABKAIIQQJ0akEAOwEAIAEgASgCCEEBajYCCAwBCwsgAUEANgIIA0AgASgCCEEeTkUEQCABKAIMQYgTaiABKAIIQQJ0akEAOwEAIAEgASgCCEEBajYCCAwBCwsgAUEANgIIA0AgASgCCEETTkUEQCABKAIMQfwUaiABKAIIQQJ0akEAOwEAIAEgASgCCEEBajYCCAwBCwsgASgCDEEBOwGUCSABKAIMQQA2AqwtIAEoAgxBADYCqC0gASgCDEEANgKwLSABKAIMQQA2AqAtCyIBAX8jAEEQayIBJAAgASAANgIMIAEoAgwQFSABQRBqJAAL6QEBAX8jAEEwayICIAA2AiQgAiABNwMYIAJCADcDECACIAIoAiQpAwhCAX03AwgCQANAIAIpAxAgAikDCFQEQCACIAIpAxAgAikDCCACKQMQfUIBiHw3AwACQCACKAIkKAIEIAIpAwCnQQN0aikDACACKQMYVgRAIAIgAikDAEIBfTcDCAwBCwJAIAIpAwAgAigCJCkDCFIEQCACKAIkKAIEIAIpAwBCAXynQQN0aikDACACKQMYWA0BCyACIAIpAwA3AygMBAsgAiACKQMAQgF8NwMQCwwBCwsgAiACKQMQNwMoCyACKQMoC6cBAQF/IwBBMGsiBCQAIAQgADYCKCAEIAE2AiQgBCACNwMYIAQgAzYCFCAEIAQoAigpAzggBCgCKCkDMCAEKAIkIAQpAxggBCgCFBCRATcDCAJAIAQpAwhCAFMEQCAEQX82AiwMAQsgBCgCKCAEKQMINwM4IAQoAiggBCgCKCkDOBC/ASECIAQoAiggAjcDQCAEQQA2AiwLIAQoAiwhACAEQTBqJAAgAAvrAQEBfyMAQSBrIgMkACADIAA2AhggAyABNwMQIAMgAjYCDAJAIAMpAxAgAygCGCkDEFQEQCADQQE6AB8MAQsgAyADKAIYKAIAIAMpAxBCBIanEEgiADYCCCAARQRAIAMoAgxBDkEAEBQgA0EAOgAfDAELIAMoAhggAygCCDYCACADIAMoAhgoAgQgAykDEEIBfEIDhqcQSCIANgIEIABFBEAgAygCDEEOQQAQFCADQQA6AB8MAQsgAygCGCADKAIENgIEIAMoAhggAykDEDcDECADQQE6AB8LIAMtAB9BAXEhACADQSBqJAAgAAvOAgEBfyMAQTBrIgQkACAEIAA2AiggBCABNwMgIAQgAjYCHCAEIAM2AhgCQAJAIAQoAigNACAEKQMgUA0AIAQoAhhBEkEAEBQgBEEANgIsDAELIAQgBCgCKCAEKQMgIAQoAhwgBCgCGBBNIgA2AgwgAEUEQCAEQQA2AiwMAQsgBEEYEBgiADYCFCAARQRAIAQoAhhBDkEAEBQgBCgCDBAzIARBADYCLAwBCyAEKAIUIAQoAgw2AhAgBCgCFEEANgIUQQAQASEAIAQoAhQgADYCDCMAQRBrIgAgBCgCFDYCDCAAKAIMQQA2AgAgACgCDEEANgIEIAAoAgxBADYCCCAEQQIgBCgCFCAEKAIYEJQBIgA2AhAgAEUEQCAEKAIUKAIQEDMgBCgCFBAVIARBADYCLAwBCyAEIAQoAhA2AiwLIAQoAiwhACAEQTBqJAAgAAupAQEBfyMAQTBrIgQkACAEIAA2AiggBCABNwMgIAQgAjYCHCAEIAM2AhgCQCAEKAIoRQRAIAQpAyBCAFIEQCAEKAIYQRJBABAUIARBADYCLAwCCyAEQQBCACAEKAIcIAQoAhgQwgE2AiwMAQsgBCAEKAIoNgIIIAQgBCkDIDcDECAEIARBCGpCASAEKAIcIAQoAhgQwgE2AiwLIAQoAiwhACAEQTBqJAAgAAtGAQF/IwBBIGsiAyQAIAMgADYCHCADIAE3AxAgAyACNgIMIAMoAhwgAykDECADKAIMIAMoAhxBCGoQTiEAIANBIGokACAAC40CAQF/IwBBMGsiAyQAIAMgADYCKCADIAE7ASYgAyACNgIgIAMgAygCKCgCNCADQR5qIAMvASZBgAZBABBfNgIQAkAgAygCEEUNACADLwEeQQVJDQACQCADKAIQLQAAQQFGDQAMAQsgAyADKAIQIAMvAR6tECkiADYCFCAARQRADAELIAMoAhQQjwEaIAMgAygCFBAqNgIYIAMoAiAQjAEgAygCGEYEQCADIAMoAhQQLz0BDiADIAMoAhQgAy8BDq0QHiADLwEOQYAQQQAQUjYCCCADKAIIBEAgAygCIBAlIAMgAygCCDYCIAsLIAMoAhQQFgsgAyADKAIgNgIsIAMoAiwhACADQTBqJAAgAAvaFwIBfwF+IwBBgAFrIgUkACAFIAA2AnQgBSABNgJwIAUgAjYCbCAFIAM6AGsgBSAENgJkIAUgBSgCbEEARzoAHSAFQR5BLiAFLQBrQQFxGzYCKAJAAkAgBSgCbARAIAUoAmwQLyAFKAIorVQEQCAFKAJkQRNBABAUIAVCfzcDeAwDCwwBCyAFIAUoAnAgBSgCKK0gBUEwaiAFKAJkEEEiADYCbCAARQRAIAVCfzcDeAwCCwsgBSgCbEIEEB4hAEHxEkH2EiAFLQBrQQFxGygAACAAKAAARwRAIAUoAmRBE0EAEBQgBS0AHUEBcUUEQCAFKAJsEBYLIAVCfzcDeAwBCyAFKAJ0EE8CQCAFLQBrQQFxRQRAIAUoAmwQHSEAIAUoAnQgADsBCAwBCyAFKAJ0QQA7AQgLIAUoAmwQHSEAIAUoAnQgADsBCiAFKAJsEB0hACAFKAJ0IAA7AQwgBSgCbBAdQf//A3EhACAFKAJ0IAA2AhAgBSAFKAJsEB07AS4gBSAFKAJsEB07ASwgBS8BLiEBIAUvASwhAiMAQTBrIgAkACAAIAE7AS4gACACOwEsIABCADcCACAAQQA2AiggAEIANwIgIABCADcCGCAAQgA3AhAgAEIANwIIIABBADYCICAAIAAvASxBCXZB0ABqNgIUIAAgAC8BLEEFdkEPcUEBazYCECAAIAAvASxBH3E2AgwgACAALwEuQQt2NgIIIAAgAC8BLkEFdkE/cTYCBCAAIAAvAS5BAXRBPnE2AgAgABAMIQEgAEEwaiQAIAEhACAFKAJ0IAA2AhQgBSgCbBAqIQAgBSgCdCAANgIYIAUoAmwQKq0hBiAFKAJ0IAY3AyAgBSgCbBAqrSEGIAUoAnQgBjcDKCAFIAUoAmwQHTsBIiAFIAUoAmwQHTsBHgJAIAUtAGtBAXEEQCAFQQA7ASAgBSgCdEEANgI8IAUoAnRBADsBQCAFKAJ0QQA2AkQgBSgCdEIANwNIDAELIAUgBSgCbBAdOwEgIAUoAmwQHUH//wNxIQAgBSgCdCAANgI8IAUoAmwQHSEAIAUoAnQgADsBQCAFKAJsECohACAFKAJ0IAA2AkQgBSgCbBAqrSEGIAUoAnQgBjcDSAsCfyMAQRBrIgAgBSgCbDYCDCAAKAIMLQAAQQFxRQsEQCAFKAJkQRRBABAUIAUtAB1BAXFFBEAgBSgCbBAWCyAFQn83A3gMAQsCQCAFKAJ0LwEMQQFxBEAgBSgCdC8BDEHAAHEEQCAFKAJ0Qf//AzsBUgwCCyAFKAJ0QQE7AVIMAQsgBSgCdEEAOwFSCyAFKAJ0QQA2AjAgBSgCdEEANgI0IAUoAnRBADYCOCAFIAUvASAgBS8BIiAFLwEeamo2AiQCQCAFLQAdQQFxBEAgBSgCbBAvIAUoAiStVARAIAUoAmRBFUEAEBQgBUJ/NwN4DAMLDAELIAUoAmwQFiAFIAUoAnAgBSgCJK1BACAFKAJkEEEiADYCbCAARQRAIAVCfzcDeAwCCwsgBS8BIgRAIAUoAmwgBSgCcCAFLwEiQQEgBSgCZBCNASEAIAUoAnQgADYCMCAFKAJ0KAIwRQRAAn8jAEEQayIAIAUoAmQ2AgwgACgCDCgCAEERRgsEQCAFKAJkQRVBABAUCyAFLQAdQQFxRQRAIAUoAmwQFgsgBUJ/NwN4DAILIAUoAnQvAQxBgBBxBEAgBSgCdCgCMEECEDpBBUYEQCAFKAJkQRVBABAUIAUtAB1BAXFFBEAgBSgCbBAWCyAFQn83A3gMAwsLCyAFLwEeBEAgBSAFKAJsIAUoAnAgBS8BHkEAIAUoAmQQYDYCGCAFKAIYRQRAIAUtAB1BAXFFBEAgBSgCbBAWCyAFQn83A3gMAgsgBSgCGCAFLwEeQYACQYAEIAUtAGtBAXEbIAUoAnRBNGogBSgCZBCIAUEBcUUEQCAFKAIYEBUgBS0AHUEBcUUEQCAFKAJsEBYLIAVCfzcDeAwCCyAFKAIYEBUgBS0Aa0EBcQRAIAUoAnRBAToABAsLIAUvASAEQCAFKAJsIAUoAnAgBS8BIEEAIAUoAmQQjQEhACAFKAJ0IAA2AjggBSgCdCgCOEUEQCAFLQAdQQFxRQRAIAUoAmwQFgsgBUJ/NwN4DAILIAUoAnQvAQxBgBBxBEAgBSgCdCgCOEECEDpBBUYEQCAFKAJkQRVBABAUIAUtAB1BAXFFBEAgBSgCbBAWCyAFQn83A3gMAwsLCyAFKAJ0QfXgASAFKAJ0KAIwEMUBIQAgBSgCdCAANgIwIAUoAnRB9cYBIAUoAnQoAjgQxQEhACAFKAJ0IAA2AjgCQAJAIAUoAnQpAyhC/////w9RDQAgBSgCdCkDIEL/////D1ENACAFKAJ0KQNIQv////8PUg0BCyAFIAUoAnQoAjQgBUEWakEBQYACQYAEIAUtAGtBAXEbIAUoAmQQXzYCDCAFKAIMRQRAIAUtAB1BAXFFBEAgBSgCbBAWCyAFQn83A3gMAgsgBSAFKAIMIAUvARatECkiADYCECAARQRAIAUoAmRBDkEAEBQgBS0AHUEBcUUEQCAFKAJsEBYLIAVCfzcDeAwCCwJAIAUoAnQpAyhC/////w9RBEAgBSgCEBAwIQYgBSgCdCAGNwMoDAELIAUtAGtBAXEEQCAFKAIQIQEjAEEgayIAJAAgACABNgIYIABCCDcDECAAIAAoAhgpAxAgACkDEHw3AwgCQCAAKQMIIAAoAhgpAxBUBEAgACgCGEEAOgAAIABBfzYCHAwBCyAAIAAoAhggACkDCBAsNgIcCyAAKAIcGiAAQSBqJAALCyAFKAJ0KQMgQv////8PUQRAIAUoAhAQMCEGIAUoAnQgBjcDIAsgBS0Aa0EBcUUEQCAFKAJ0KQNIQv////8PUQRAIAUoAhAQMCEGIAUoAnQgBjcDSAsgBSgCdCgCPEH//wNGBEAgBSgCEBAqIQAgBSgCdCAANgI8CwsgBSgCEBBHQQFxRQRAIAUoAmRBFUEAEBQgBSgCEBAWIAUtAB1BAXFFBEAgBSgCbBAWCyAFQn83A3gMAgsgBSgCEBAWCwJ/IwBBEGsiACAFKAJsNgIMIAAoAgwtAABBAXFFCwRAIAUoAmRBFEEAEBQgBS0AHUEBcUUEQCAFKAJsEBYLIAVCfzcDeAwBCyAFLQAdQQFxRQRAIAUoAmwQFgsgBSgCdCkDSEL///////////8AVgRAIAUoAmRBBEEWEBQgBUJ/NwN4DAELAn8gBSgCdCEBIAUoAmQhAiMAQSBrIgAkACAAIAE2AhggACACNgIUAkAgACgCGCgCEEHjAEcEQCAAQQE6AB8MAQsgACAAKAIYKAI0IABBEmpBgbICQYAGQQAQXzYCCAJAIAAoAggEQCAALwESQQdPDQELIAAoAhRBFUEAEBQgAEEAOgAfDAELIAAgACgCCCAALwESrRApIgE2AgwgAUUEQCAAKAIUQRRBABAUIABBADoAHwwBCyAAQQE6AAcCQAJAAkAgACgCDBAdQQFrDgICAAELIAAoAhgpAyhCFFQEQCAAQQA6AAcLDAELIAAoAhRBGEEAEBQgACgCDBAWIABBADoAHwwBCyAAKAIMQgIQHi8AAEHBigFHBEAgACgCFEEYQQAQFCAAKAIMEBYgAEEAOgAfDAELAkACQAJAAkACQCAAKAIMEI8BQQFrDgMAAQIDCyAAQYECOwEEDAMLIABBggI7AQQMAgsgAEGDAjsBBAwBCyAAKAIUQRhBABAUIAAoAgwQFiAAQQA6AB8MAQsgAC8BEkEHRwRAIAAoAhRBFUEAEBQgACgCDBAWIABBADoAHwwBCyAAKAIYIAAtAAdBAXE6AAYgACgCGCAALwEEOwFSIAAoAgwQHUH//wNxIQEgACgCGCABNgIQIAAoAgwQFiAAQQE6AB8LIAAtAB9BAXEhASAAQSBqJAAgAUEBcUULBEAgBUJ/NwN4DAELIAUoAnQoAjQQhwEhACAFKAJ0IAA2AjQgBSAFKAIoIAUoAiRqrTcDeAsgBSkDeCEGIAVBgAFqJAAgBgsYAEGomwFCADcCAEGwmwFBADYCAEGomwELCABBAUEMEHYLBwAgACgCLAsHACAAKAIoCwcAIAAoAhgLtQkBAX8jAEHgwABrIgUkACAFIAA2AtRAIAUgATYC0EAgBSACNgLMQCAFIAM3A8BAIAUgBDYCvEAgBSAFKALQQDYCuEACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgBSgCvEAOEQMEAAYBAgUJCgoKCgoKCAoHCgsgBUIANwPYQAwKCyAFIAUoArhAQeQAaiAFKALMQCAFKQPAQBBCNwPYQAwJCyAFKAK4QBAVIAVCADcD2EAMCAsgBSgCuEAoAhAEQCAFIAUoArhAKAIQIAUoArhAKQMYIAUoArhAQeQAahBlIgM3A5hAIANQBEAgBUJ/NwPYQAwJCyAFKAK4QCkDCCAFKAK4QCkDCCAFKQOYQHxWBEAgBSgCuEBB5ABqQRVBABAUIAVCfzcD2EAMCQsgBSgCuEAiACAFKQOYQCAAKQMAfDcDACAFKAK4QCIAIAUpA5hAIAApAwh8NwMIIAUoArhAQQA2AhALIAUoArhALQB4QQFxRQRAIAVCADcDqEADQCAFKQOoQCAFKAK4QCkDAFQEQCAFIAUoArhAKQMAIAUpA6hAfUKAwABWBH5CgMAABSAFKAK4QCkDACAFKQOoQH0LNwOgQCAFIAUoAtRAIAVBEGogBSkDoEAQLiIDNwOwQCADQgBTBEAgBSgCuEBB5ABqIAUoAtRAEBcgBUJ/NwPYQAwLCyAFKQOwQFAEQCAFKAK4QEHkAGpBEUEAEBQgBUJ/NwPYQAwLBSAFIAUpA7BAIAUpA6hAfDcDqEAMAgsACwsLIAUoArhAIAUoArhAKQMANwMgIAVCADcD2EAMBwsgBSkDwEAgBSgCuEApAwggBSgCuEApAyB9VgRAIAUgBSgCuEApAwggBSgCuEApAyB9NwPAQAsgBSkDwEBQBEAgBUIANwPYQAwHCyAFKAK4QC0AeEEBcQRAIAUoAtRAIAUoArhAKQMgQQAQJ0EASARAIAUoArhAQeQAaiAFKALUQBAXIAVCfzcD2EAMCAsLIAUgBSgC1EAgBSgCzEAgBSkDwEAQLiIDNwOwQCADQgBTBEAgBSgCuEBB5ABqQRFBABAUIAVCfzcD2EAMBwsgBSgCuEAiACAFKQOwQCAAKQMgfDcDICAFKQOwQFAEQCAFKAK4QCkDICAFKAK4QCkDCFQEQCAFKAK4QEHkAGpBEUEAEBQgBUJ/NwPYQAwICwsgBSAFKQOwQDcD2EAMBgsgBSAFKAK4QCkDICAFKAK4QCkDAH0gBSgCuEApAwggBSgCuEApAwB9IAUoAsxAIAUpA8BAIAUoArhAQeQAahCRATcDCCAFKQMIQgBTBEAgBUJ/NwPYQAwGCyAFKAK4QCAFKQMIIAUoArhAKQMAfDcDICAFQgA3A9hADAULIAUgBSgCzEA2AgQgBSgCBCAFKAK4QEEoaiAFKAK4QEHkAGoQlQFBAEgEQCAFQn83A9hADAULIAVCADcD2EAMBAsgBSAFKAK4QCwAYKw3A9hADAMLIAUgBSgCuEApA3A3A9hADAILIAUgBSgCuEApAyAgBSgCuEApAwB9NwPYQAwBCyAFKAK4QEHkAGpBHEEAEBQgBUJ/NwPYQAsgBSkD2EAhAyAFQeDAAGokACADCwcAIAAoAhALIgEBfyMAQRBrIgEgADYCDCABKAIMIgAgACgCMEEBajYCMAsHACAAKAIICxQAIAAgAa0gAq1CIIaEIAMgBBB/CxMBAX4gABBKIgFCIIinEAAgAacLEgAgACABrSACrUIghoQgAxAnCx8BAX4gACABIAKtIAOtQiCGhBAuIgRCIIinEAAgBKcLFQAgACABrSACrUIghoQgAyAEEMMBCxQAIAAgASACrSADrUIghoQgBBB+C60EAQF/IwBBIGsiBSQAIAUgADYCGCAFIAGtIAKtQiCGhDcDECAFIAM2AgwgBSAENgIIAkACQCAFKQMQIAUoAhgpAzBUBEAgBSgCCEEJTQ0BCyAFKAIYQQhqQRJBABAUIAVBfzYCHAwBCyAFKAIYKAIYQQJxBEAgBSgCGEEIakEZQQAQFCAFQX82AhwMAQsCfyAFKAIMIQEjAEEQayIAJAAgACABNgIIIABBAToABwJAIAAoAghFBEAgAEEBOgAPDAELIAAgACgCCCAALQAHQQFxELMBQQBHOgAPCyAALQAPQQFxIQEgAEEQaiQAIAFFCwRAIAUoAhhBCGpBEEEAEBQgBUF/NgIcDAELIAUgBSgCGCgCQCAFKQMQp0EEdGo2AgQgBSAFKAIEKAIABH8gBSgCBCgCACgCEAVBfws2AgACQCAFKAIMIAUoAgBGBEAgBSgCBCgCBARAIAUoAgQoAgQiACAAKAIAQX5xNgIAIAUoAgQoAgRBADsBUCAFKAIEKAIEKAIARQRAIAUoAgQoAgQQOSAFKAIEQQA2AgQLCwwBCyAFKAIEKAIERQRAIAUoAgQoAgAQPyEAIAUoAgQgADYCBCAARQRAIAUoAhhBCGpBDkEAEBQgBUF/NgIcDAMLCyAFKAIEKAIEIAUoAgw2AhAgBSgCBCgCBCAFKAIIOwFQIAUoAgQoAgQiACAAKAIAQQFyNgIACyAFQQA2AhwLIAUoAhwhACAFQSBqJAAgAAsXAQF+IAAgASACEHMiA0IgiKcQACADpwuuAQIBfwF+An8jAEEgayICIAA2AhQgAiABNgIQAkAgAigCFEUEQCACQn83AxgMAQsgAigCEEEIcQRAIAIgAigCFCkDMDcDCANAIAIpAwhCAFIEfyACKAIUKAJAIAIpAwhCAX2nQQR0aigCAAVBAQtFBEAgAiACKQMIQgF9NwMIDAELCyACIAIpAwg3AxgMAQsgAiACKAIUKQMwNwMYCyACKQMYIgNCIIinCxAAIAOnCxMAIAAgAa0gAq1CIIaEIAMQxAELiAICAX8BfgJ/IwBBIGsiBCQAIAQgADYCFCAEIAE2AhAgBCACrSADrUIghoQ3AwgCQCAEKAIURQRAIARCfzcDGAwBCyAEKAIUKAIEBEAgBEJ/NwMYDAELIAQpAwhC////////////AFYEQCAEKAIUQQRqQRJBABAUIARCfzcDGAwBCwJAIAQoAhQtABBBAXFFBEAgBCkDCFBFDQELIARCADcDGAwBCyAEIAQoAhQoAhQgBCgCECAEKQMIEC4iBTcDACAFQgBTBEAgBCgCFEEEaiAEKAIUKAIUEBcgBEJ/NwMYDAELIAQgBCkDADcDGAsgBCkDGCEFIARBIGokACAFQiCIpwsQACAFpwtPAQF/IwBBIGsiBCQAIAQgADYCHCAEIAGtIAKtQiCGhDcDECAEIAM2AgwgBCgCHCAEKQMQIAQoAgwgBCgCHCgCHBCtASEAIARBIGokACAAC9kDAQF/IwBBIGsiBSQAIAUgADYCGCAFIAGtIAKtQiCGhDcDECAFIAM2AgwgBSAENgIIAkAgBSgCGCAFKQMQQQBBABBFRQRAIAVBfzYCHAwBCyAFKAIYKAIYQQJxBEAgBSgCGEEIakEZQQAQFCAFQX82AhwMAQsgBSgCGCgCQCAFKQMQp0EEdGooAggEQCAFKAIYKAJAIAUpAxCnQQR0aigCCCAFKAIMEGhBAEgEQCAFKAIYQQhqQQ9BABAUIAVBfzYCHAwCCyAFQQA2AhwMAQsgBSAFKAIYKAJAIAUpAxCnQQR0ajYCBCAFIAUoAgQoAgAEfyAFKAIMIAUoAgQoAgAoAhRHBUEBC0EBcTYCAAJAIAUoAgAEQCAFKAIEKAIERQRAIAUoAgQoAgAQPyEAIAUoAgQgADYCBCAARQRAIAUoAhhBCGpBDkEAEBQgBUF/NgIcDAQLCyAFKAIEKAIEIAUoAgw2AhQgBSgCBCgCBCIAIAAoAgBBIHI2AgAMAQsgBSgCBCgCBARAIAUoAgQoAgQiACAAKAIAQV9xNgIAIAUoAgQoAgQoAgBFBEAgBSgCBCgCBBA5IAUoAgRBADYCBAsLCyAFQQA2AhwLIAUoAhwhACAFQSBqJAAgAAsXACAAIAGtIAKtQiCGhCADIAQgBRCZAQsXACAAIAGtIAKtQiCGhCADIAQgBRCXAQuPAQIBfwF+An8jAEEgayIEJAAgBCAANgIUIAQgATYCECAEIAI2AgwgBCADNgIIAkACQCAEKAIQBEAgBCgCDA0BCyAEKAIUQQhqQRJBABAUIARCfzcDGAwBCyAEIAQoAhQgBCgCECAEKAIMIAQoAggQmgE3AxgLIAQpAxghBSAEQSBqJAAgBUIgiKcLEAAgBacLiAEBAX8jAEEQayICJAAgAiAANgIMIAIgATYCCCMAQRBrIgAgAigCDDYCDCAAKAIMQQA2AgAgACgCDEEANgIEIAAoAgxBADYCCCACKAIMIAIoAgg2AgACQCACKAIMEJYBQQFGBEAgAigCDEG0mwEoAgA2AgQMAQsgAigCDEEANgIECyACQRBqJAALhQUCAX8BfgJ/IwBBMGsiAyQAIAMgADYCJCADIAE2AiAgAyACNgIcAkAgAygCJCgCGEECcQRAIAMoAiRBCGpBGUEAEBQgA0J/NwMoDAELIAMoAiBFBEAgAygCJEEIakESQQAQFCADQn83AygMAQsgA0EANgIMIAMgAygCIBArNgIYIAMoAiAgAygCGEEBa2osAABBL0cEQCADIAMoAhhBAmoQGCIANgIMIABFBEAgAygCJEEIakEOQQAQFCADQn83AygMAgsCQAJAIAMoAgwiASADKAIgIgBzQQNxDQAgAEEDcQRAA0AgASAALQAAIgI6AAAgAkUNAyABQQFqIQEgAEEBaiIAQQNxDQALCyAAKAIAIgJBf3MgAkGBgoQIa3FBgIGChHhxDQADQCABIAI2AgAgACgCBCECIAFBBGohASAAQQRqIQAgAkGBgoQIayACQX9zcUGAgYKEeHFFDQALCyABIAAtAAAiAjoAACACRQ0AA0AgASAALQABIgI6AAEgAUEBaiEBIABBAWohACACDQALCyADKAIMIAMoAhhqQS86AAAgAygCDCADKAIYQQFqakEAOgAACyADIAMoAiRBAEIAQQAQfiIANgIIIABFBEAgAygCDBAVIANCfzcDKAwBCyADIAMoAiQCfyADKAIMBEAgAygCDAwBCyADKAIgCyADKAIIIAMoAhwQmgE3AxAgAygCDBAVAkAgAykDEEIAUwRAIAMoAggQGwwBCyADKAIkIAMpAxBBAEEDQYCA/I8EEJkBQQBIBEAgAygCJCADKQMQEJgBGiADQn83AygMAgsLIAMgAykDEDcDKAsgAykDKCEEIANBMGokACAEQiCIpwsQACAEpwsRACAAIAGtIAKtQiCGhBCYAQt/AgF/AX4jAEEgayIDJAAgAyAANgIYIAMgATYCFCADIAI2AhAgAyADKAIYIAMoAhQgAygCEBBzIgQ3AwgCQCAEQgBTBEAgA0EANgIcDAELIAMgAygCGCADKQMIIAMoAhAgAygCGCgCHBCtATYCHAsgAygCHCEAIANBIGokACAAC8QBAQF/IwBBMGsiASQAIAEgADYCKCABQQA2AiQgAUIANwMYAkADQCABKQMYIAEoAigpAzBUBEAgASABKAIoIAEpAxhBACABQRdqIAFBEGoQlwE2AgwgASgCDEF/RgRAIAFBfzYCLAwDBQJAIAEtABdBA0cNACABKAIQQRB2QYDgA3FBgMACRw0AIAEgASgCJEEBajYCJAsgASABKQMYQgF8NwMYDAILAAsLIAEgASgCJDYCLAsgASgCLCEAIAFBMGokACAACxAAIwAgAGtBcHEiACQAIAALBgAgACQACwQAIwALggECAX8BfiMAQSBrIgQkACAEIAA2AhggBCABNgIUIAQgAjYCECAEIAM2AgwgBCAEKAIYIAQoAhQgBCgCEBBzIgU3AwACQCAFQgBTBEAgBEF/NgIcDAELIAQgBCgCGCAEKQMAIAQoAhAgBCgCDBB/NgIcCyAEKAIcIQAgBEEgaiQAIAAL0EUDBn8BfgJ8IwBB4ABrIgEkACABIAA2AlgCQCABKAJYRQRAIAFBfzYCXAwBCyMAQSBrIgAgASgCWDYCHCAAIAFBQGs2AhggAEEANgIUIABCADcDAAJAIAAoAhwtAChBAXFFBEAgACgCHCgCGCAAKAIcKAIURg0BCyAAQQE2AhQLIABCADcDCANAIAApAwggACgCHCkDMFQEQAJAAkAgACgCHCgCQCAAKQMIp0EEdGooAggNACAAKAIcKAJAIAApAwinQQR0ai0ADEEBcQ0AIAAoAhwoAkAgACkDCKdBBHRqKAIERQ0BIAAoAhwoAkAgACkDCKdBBHRqKAIEKAIARQ0BCyAAQQE2AhQLIAAoAhwoAkAgACkDCKdBBHRqLQAMQQFxRQRAIAAgACkDAEIBfDcDAAsgACAAKQMIQgF8NwMIDAELCyAAKAIYBEAgACgCGCAAKQMANwMACyABIAAoAhQ2AiQgASkDQFAEQAJAIAEoAlgoAgRBCHFFBEAgASgCJEUNAQsCfyABKAJYKAIAIQIjAEEQayIAJAAgACACNgIIAkAgACgCCCgCJEEDRgRAIABBADYCDAwBCyAAKAIIKAIgBEAgACgCCBAxQQBIBEAgAEF/NgIMDAILCyAAKAIIKAIkBEAgACgCCBBnCyAAKAIIQQBCAEEPECFCAFMEQCAAQX82AgwMAQsgACgCCEEDNgIkIABBADYCDAsgACgCDCECIABBEGokACACQQBICwRAAkACfyMAQRBrIgAgASgCWCgCADYCDCMAQRBrIgIgACgCDEEMajYCDCACKAIMKAIAQRZGCwRAIwBBEGsiACABKAJYKAIANgIMIwBBEGsiAiAAKAIMQQxqNgIMIAIoAgwoAgRBLEYNAQsgASgCWEEIaiABKAJYKAIAEBcgAUF/NgJcDAQLCwsgASgCWBA9IAFBADYCXAwBCyABKAIkRQRAIAEoAlgQPSABQQA2AlwMAQsgASkDQCABKAJYKQMwVgRAIAEoAlhBCGpBFEEAEBQgAUF/NgJcDAELIAEgASkDQKdBA3QQGCIANgIoIABFBEAgAUF/NgJcDAELIAFCfzcDOCABQgA3A0ggAUIANwNQA0AgASkDUCABKAJYKQMwVARAAkAgASgCWCgCQCABKQNQp0EEdGooAgBFDQACQCABKAJYKAJAIAEpA1CnQQR0aigCCA0AIAEoAlgoAkAgASkDUKdBBHRqLQAMQQFxDQAgASgCWCgCQCABKQNQp0EEdGooAgRFDQEgASgCWCgCQCABKQNQp0EEdGooAgQoAgBFDQELIAECfiABKQM4IAEoAlgoAkAgASkDUKdBBHRqKAIAKQNIVARAIAEpAzgMAQsgASgCWCgCQCABKQNQp0EEdGooAgApA0gLNwM4CyABKAJYKAJAIAEpA1CnQQR0ai0ADEEBcUUEQCABKQNIIAEpA0BaBEAgASgCKBAVIAEoAlhBCGpBFEEAEBQgAUF/NgJcDAQLIAEoAiggASkDSKdBA3RqIAEpA1A3AwAgASABKQNIQgF8NwNICyABIAEpA1BCAXw3A1AMAQsLIAEpA0ggASkDQFQEQCABKAIoEBUgASgCWEEIakEUQQAQFCABQX82AlwMAQsCQAJ/IwBBEGsiACABKAJYKAIANgIMIAAoAgwpAxhCgIAIg1ALBEAgAUIANwM4DAELIAEpAzhCf1EEQCABQn83AxggAUIANwM4IAFCADcDUANAIAEpA1AgASgCWCkDMFQEQCABKAJYKAJAIAEpA1CnQQR0aigCAARAIAEoAlgoAkAgASkDUKdBBHRqKAIAKQNIIAEpAzhaBEAgASABKAJYKAJAIAEpA1CnQQR0aigCACkDSDcDOCABIAEpA1A3AxgLCyABIAEpA1BCAXw3A1AMAQsLIAEpAxhCf1IEQCABKAJYIQIgASkDGCEHIAEoAlhBCGohAyMAQTBrIgAkACAAIAI2AiQgACAHNwMYIAAgAzYCFCAAIAAoAiQgACkDGCAAKAIUEGUiBzcDCAJAIAdQBEAgAEIANwMoDAELIAAgACgCJCgCQCAAKQMYp0EEdGooAgA2AgQCQCAAKQMIIAApAwggACgCBCkDIHxYBEAgACkDCCAAKAIEKQMgfEL///////////8AWA0BCyAAKAIUQQRBFhAUIABCADcDKAwBCyAAIAAoAgQpAyAgACkDCHw3AwggACgCBC8BDEEIcQRAIAAoAiQoAgAgACkDCEEAECdBAEgEQCAAKAIUIAAoAiQoAgAQFyAAQgA3AygMAgsgACgCJCgCACAAQgQQLkIEUgRAIAAoAhQgACgCJCgCABAXIABCADcDKAwCCyAAKAAAQdCWncAARgRAIAAgACkDCEIEfDcDCAsgACAAKQMIQgx8NwMIIAAoAgRBABBeQQFxBEAgACAAKQMIQgh8NwMICyAAKQMIQv///////////wBWBEAgACgCFEEEQRYQFCAAQgA3AygMAgsLIAAgACkDCDcDKAsgACkDKCEHIABBMGokACABIAc3AzggB1AEQCABKAIoEBUgAUF/NgJcDAQLCwsgASkDOEIAUgRAAn8gASgCWCgCACECIAEpAzghByMAQRBrIgAkACAAIAI2AgggACAHNwMAAkAgACgCCCgCJEEBRgRAIAAoAghBDGpBEkEAEBQgAEF/NgIMDAELIAAoAghBACAAKQMAQREQIUIAUwRAIABBfzYCDAwBCyAAKAIIQQE2AiQgAEEANgIMCyAAKAIMIQIgAEEQaiQAIAJBAEgLBEAgAUIANwM4CwsLIAEpAzhQBEACfyABKAJYKAIAIQIjAEEQayIAJAAgACACNgIIAkAgACgCCCgCJEEBRgRAIAAoAghBDGpBEkEAEBQgAEF/NgIMDAELIAAoAghBAEIAQQgQIUIAUwRAIABBfzYCDAwBCyAAKAIIQQE2AiQgAEEANgIMCyAAKAIMIQIgAEEQaiQAIAJBAEgLBEAgASgCWEEIaiABKAJYKAIAEBcgASgCKBAVIAFBfzYCXAwCCwsgASgCWCgCVCECIwBBEGsiACQAIAAgAjYCDCAAKAIMBEAgACgCDEQAAAAAAAAAADkDGCAAKAIMKAIARAAAAAAAAAAAIAAoAgwoAgwgACgCDCgCBBEWAAsgAEEQaiQAIAFBADYCLCABQgA3A0gDQAJAIAEpA0ggASkDQFoNACABKAJYKAJUIQIgASkDSCIHuiABKQNAuiIIoyEJIwBBIGsiACQAIAAgAjYCHCAAIAk5AxAgACAHQgF8uiAIozkDCCAAKAIcBEAgACgCHCAAKwMQOQMgIAAoAhwgACsDCDkDKCAAKAIcRAAAAAAAAAAAEFYLIABBIGokACABIAEoAiggASkDSKdBA3RqKQMANwNQIAEgASgCWCgCQCABKQNQp0EEdGo2AhACQAJAIAEoAhAoAgBFDQAgASgCECgCACkDSCABKQM4Wg0ADAELIAECf0EBIAEoAhAoAggNABogASgCECgCBARAQQEgASgCECgCBCgCAEEBcQ0BGgsgASgCECgCBAR/IAEoAhAoAgQoAgBBwABxQQBHBUEACwtBAXE2AhQgASgCECgCBEUEQCABKAIQKAIAED8hACABKAIQIAA2AgQgAEUEQCABKAJYQQhqQQ5BABAUIAFBATYCLAwDCwsgASABKAIQKAIENgIMAn8gASgCWCECIAEpA1AhByMAQTBrIgAkACAAIAI2AiggACAHNwMgAkAgACkDICAAKAIoKQMwWgRAIAAoAihBCGpBEkEAEBQgAEF/NgIsDAELIAAgACgCKCgCQCAAKQMgp0EEdGo2AhwCQCAAKAIcKAIABEAgACgCHCgCAC0ABEEBcUUNAQsgAEEANgIsDAELIAAoAhwoAgApA0hCGnxC////////////AFYEQCAAKAIoQQhqQQRBFhAUIABBfzYCLAwBCyAAKAIoKAIAIAAoAhwoAgApA0hCGnxBABAnQQBIBEAgACgCKEEIaiAAKAIoKAIAEBcgAEF/NgIsDAELIAAgACgCKCgCAEIEIABBGGogACgCKEEIahBBIgI2AhQgAkUEQCAAQX82AiwMAQsgACAAKAIUEB07ARIgACAAKAIUEB07ARAgACgCFBBHQQFxRQRAIAAoAhQQFiAAKAIoQQhqQRRBABAUIABBfzYCLAwBCyAAKAIUEBYgAC8BEARAIAAoAigoAgAgAC8BEq1BARAnQQBIBEAgACgCKEEIakEEQbSbASgCABAUIABBfzYCLAwCCyAAQQAgACgCKCgCACAALwEQQQAgACgCKEEIahBgNgIIIAAoAghFBEAgAEF/NgIsDAILIAAoAgggAC8BEEGAAiAAQQxqIAAoAihBCGoQiAFBAXFFBEAgACgCCBAVIABBfzYCLAwCCyAAKAIIEBUgACgCDARAIAAgACgCDBCHATYCDCAAKAIcKAIAKAI0IAAoAgwQiQEhAiAAKAIcKAIAIAI2AjQLCyAAKAIcKAIAQQE6AAQCQCAAKAIcKAIERQ0AIAAoAhwoAgQtAARBAXENACAAKAIcKAIEIAAoAhwoAgAoAjQ2AjQgACgCHCgCBEEBOgAECyAAQQA2AiwLIAAoAiwhAiAAQTBqJAAgAkEASAsEQCABQQE2AiwMAgsgASABKAJYKAIAEDQiBzcDMCAHQgBTBEAgAUEBNgIsDAILIAEoAgwgASkDMDcDSAJAIAEoAhQEQCABQQA2AgggASgCECgCCEUEQCABIAEoAlggASgCWCABKQNQQQhBABCuASIANgIIIABFBEAgAUEBNgIsDAULCwJ/IAEoAlghAgJ/IAEoAggEQCABKAIIDAELIAEoAhAoAggLIQMgASgCDCEEIwBBoAFrIgAkACAAIAI2ApgBIAAgAzYClAEgACAENgKQAQJAIAAoApQBIABBOGoQOEEASARAIAAoApgBQQhqIAAoApQBEBcgAEF/NgKcAQwBCyAAKQM4QsAAg1AEQCAAIAApAzhCwACENwM4IABBADsBaAsCQAJAIAAoApABKAIQQX9HBEAgACgCkAEoAhBBfkcNAQsgAC8BaEUNACAAKAKQASAALwFoNgIQDAELAkACQCAAKAKQASgCEA0AIAApAzhCBINQDQAgACAAKQM4QgiENwM4IAAgACkDUDcDWAwBCyAAIAApAzhC9////w+DNwM4CwsgACkDOEKAAYNQBEAgACAAKQM4QoABhDcDOCAAQQA7AWoLIABBgAI2AiQCQCAAKQM4QgSDUARAIAAgACgCJEGACHI2AiQgAEJ/NwNwDAELIAAoApABIAApA1A3AyggACAAKQNQNwNwAkAgACkDOEIIg1AEQAJAAkACQAJAAkACfwJAIAAoApABKAIQQX9HBEAgACgCkAEoAhBBfkcNAQtBCAwBCyAAKAKQASgCEAtB//8DcQ4NAgMDAwMDAwMBAwMDAAMLIABClMLk8w83AxAMAwsgAEKDg7D/DzcDEAwCCyAAQv////8PNwMQDAELIABCADcDEAsgACkDUCAAKQMQVgRAIAAgACgCJEGACHI2AiQLDAELIAAoApABIAApA1g3AyALCyAAIAAoApgBKAIAEDQiBzcDiAEgB0IAUwRAIAAoApgBQQhqIAAoApgBKAIAEBcgAEF/NgKcAQwBCyAAKAKQASICIAIvAQxB9/8DcTsBDCAAIAAoApgBIAAoApABIAAoAiQQUCICNgIoIAJBAEgEQCAAQX82ApwBDAELIAAgAC8BaAJ/AkAgACgCkAEoAhBBf0cEQCAAKAKQASgCEEF+Rw0BC0EIDAELIAAoApABKAIQC0H//wNxRzoAIiAAIAAtACJBAXEEfyAALwFoQQBHBUEAC0EBcToAISAAIAAvAWgEfyAALQAhBUEBC0EBcToAICAAIAAtACJBAXEEfyAAKAKQASgCEEEARwVBAAtBAXE6AB8gAAJ/QQEgAC0AIkEBcQ0AGkEBIAAoApABKAIAQYABcQ0AGiAAKAKQAS8BUiAALwFqRwtBAXE6AB4gACAALQAeQQFxBH8gAC8BakEARwVBAAtBAXE6AB0gACAALQAeQQFxBH8gACgCkAEvAVJBAEcFQQALQQFxOgAcIAAgACgClAE2AjQjAEEQayICIAAoAjQ2AgwgAigCDCICIAIoAjBBAWo2AjAgAC0AHUEBcQRAIAAgAC8BakEAEHwiAjYCDCACRQRAIAAoApgBQQhqQRhBABAUIAAoAjQQGyAAQX82ApwBDAILIAAgACgCmAEgACgCNCAALwFqQQAgACgCmAEoAhwgACgCDBEFACICNgIwIAJFBEAgACgCNBAbIABBfzYCnAEMAgsgACgCNBAbIAAgACgCMDYCNAsgAC0AIUEBcQRAIAAgACgCmAEgACgCNCAALwFoELABIgI2AjAgAkUEQCAAKAI0EBsgAEF/NgKcAQwCCyAAKAI0EBsgACAAKAIwNgI0CyAALQAgQQFxBEAgACAAKAKYASAAKAI0QQAQrwEiAjYCMCACRQRAIAAoAjQQGyAAQX82ApwBDAILIAAoAjQQGyAAIAAoAjA2AjQLIAAtAB9BAXEEQCAAKAKYASEDIAAoAjQhBCAAKAKQASgCECEFIAAoApABLwFQIQYjAEEQayICJAAgAiADNgIMIAIgBDYCCCACIAU2AgQgAiAGNgIAIAIoAgwgAigCCCACKAIEQQEgAigCABCyASEDIAJBEGokACAAIAMiAjYCMCACRQRAIAAoAjQQGyAAQX82ApwBDAILIAAoAjQQGyAAIAAoAjA2AjQLIAAtABxBAXEEQCAAQQA2AgQCQCAAKAKQASgCVARAIAAgACgCkAEoAlQ2AgQMAQsgACgCmAEoAhwEQCAAIAAoApgBKAIcNgIECwsgACAAKAKQAS8BUkEBEHwiAjYCCCACRQRAIAAoApgBQQhqQRhBABAUIAAoAjQQGyAAQX82ApwBDAILIAAgACgCmAEgACgCNCAAKAKQAS8BUkEBIAAoAgQgACgCCBEFACICNgIwIAJFBEAgACgCNBAbIABBfzYCnAEMAgsgACgCNBAbIAAgACgCMDYCNAsgACAAKAKYASgCABA0Igc3A4ABIAdCAFMEQCAAKAKYAUEIaiAAKAKYASgCABAXIABBfzYCnAEMAQsgACgCmAEhAyAAKAI0IQQgACkDcCEHIwBBwMAAayICJAAgAiADNgK4QCACIAQ2ArRAIAIgBzcDqEACQCACKAK0QBBJQQBIBEAgAigCuEBBCGogAigCtEAQFyACQX82ArxADAELIAJBADYCDCACQgA3AxADQAJAIAIgAigCtEAgAkEgakKAwAAQLiIHNwMYIAdCAFcNACACKAK4QCACQSBqIAIpAxgQNUEASARAIAJBfzYCDAUgAikDGEKAwABSDQIgAigCuEAoAlRFDQIgAikDqEBCAFcNAiACIAIpAxggAikDEHw3AxAgAigCuEAoAlQgAikDELkgAikDqEC5oxBWDAILCwsgAikDGEIAUwRAIAIoArhAQQhqIAIoArRAEBcgAkF/NgIMCyACKAK0QBAxGiACIAIoAgw2ArxACyACKAK8QCEDIAJBwMAAaiQAIAAgAzYCLCAAKAI0IABBOGoQOEEASARAIAAoApgBQQhqIAAoAjQQFyAAQX82AiwLIAAoAjQhAyMAQRBrIgIkACACIAM2AggCQANAIAIoAggEQCACKAIIKQMYQoCABINCAFIEQCACIAIoAghBAEIAQRAQITcDACACKQMAQgBTBEAgAkH/AToADwwECyACKQMAQgNVBEAgAigCCEEMakEUQQAQFCACQf8BOgAPDAQLIAIgAikDADwADwwDBSACIAIoAggoAgA2AggMAgsACwsgAkEAOgAPCyACLAAPIQMgAkEQaiQAIAAgAyICOgAjIAJBGHRBGHVBAEgEQCAAKAKYAUEIaiAAKAI0EBcgAEF/NgIsCyAAKAI0EBsgACgCLEEASARAIABBfzYCnAEMAQsgACAAKAKYASgCABA0Igc3A3ggB0IAUwRAIAAoApgBQQhqIAAoApgBKAIAEBcgAEF/NgKcAQwBCyAAKAKYASgCACAAKQOIARCbAUEASARAIAAoApgBQQhqIAAoApgBKAIAEBcgAEF/NgKcAQwBCyAAKQM4QuQAg0LkAFIEQCAAKAKYAUEIakEUQQAQFCAAQX82ApwBDAELIAAoApABKAIAQSBxRQRAAkAgACkDOEIQg0IAUgRAIAAoApABIAAoAmA2AhQMAQsgACgCkAFBFGoQARoLCyAAKAKQASAALwFoNgIQIAAoApABIAAoAmQ2AhggACgCkAEgACkDUDcDKCAAKAKQASAAKQN4IAApA4ABfTcDICAAKAKQASAAKAKQAS8BDEH5/wNxIAAtACNBAXRyOwEMIAAoApABIQMgACgCJEGACHFBAEchBCMAQRBrIgIkACACIAM2AgwgAiAEOgALAkAgAigCDCgCEEEORgRAIAIoAgxBPzsBCgwBCyACKAIMKAIQQQxGBEAgAigCDEEuOwEKDAELAkAgAi0AC0EBcUUEQCACKAIMQQAQXkEBcUUNAQsgAigCDEEtOwEKDAELAkAgAigCDCgCEEEIRwRAIAIoAgwvAVJBAUcNAQsgAigCDEEUOwEKDAELIAIgAigCDCgCMBBTIgM7AQggA0H//wNxBEAgAigCDCgCMCgCACACLwEIQQFrai0AAEEvRgRAIAIoAgxBFDsBCgwCCwsgAigCDEEKOwEKCyACQRBqJAAgACAAKAKYASAAKAKQASAAKAIkEFAiAjYCLCACQQBIBEAgAEF/NgKcAQwBCyAAKAIoIAAoAixHBEAgACgCmAFBCGpBFEEAEBQgAEF/NgKcAQwBCyAAKAKYASgCACAAKQN4EJsBQQBIBEAgACgCmAFBCGogACgCmAEoAgAQFyAAQX82ApwBDAELIABBADYCnAELIAAoApwBIQIgAEGgAWokACACQQBICwRAIAFBATYCLCABKAIIBEAgASgCCBAbCwwECyABKAIIBEAgASgCCBAbCwwBCyABKAIMIgAgAC8BDEH3/wNxOwEMIAEoAlggASgCDEGAAhBQQQBIBEAgAUEBNgIsDAMLIAEgASgCWCABKQNQIAEoAlhBCGoQZSIHNwMAIAdQBEAgAUEBNgIsDAMLIAEoAlgoAgAgASkDAEEAECdBAEgEQCABKAJYQQhqIAEoAlgoAgAQFyABQQE2AiwMAwsCfyABKAJYIQIgASgCDCkDICEHIwBBoMAAayIAJAAgACACNgKYQCAAIAc3A5BAIAAgACkDkEC6OQMAAkADQCAAKQOQQFBFBEAgACAAKQOQQEKAwABWBH5CgMAABSAAKQOQQAs+AgwgACgCmEAoAgAgAEEQaiAAKAIMrSAAKAKYQEEIahBhQQBIBEAgAEF/NgKcQAwDCyAAKAKYQCAAQRBqIAAoAgytEDVBAEgEQCAAQX82ApxADAMFIAAgACkDkEAgADUCDH03A5BAIAAoAphAKAJUIAArAwAgACkDkEC6oSAAKwMAoxBWDAILAAsLIABBADYCnEALIAAoApxAIQIgAEGgwABqJAAgAkEASAsEQCABQQE2AiwMAwsLCyABIAEpA0hCAXw3A0gMAQsLIAEoAixFBEACfyABKAJYIQAgASgCKCEDIAEpA0AhByMAQTBrIgIkACACIAA2AiggAiADNgIkIAIgBzcDGCACIAIoAigoAgAQNCIHNwMQAkAgB0IAUwRAIAJBfzYCLAwBCyACKAIoIQMgAigCJCEEIAIpAxghByMAQcABayIAJAAgACADNgK0ASAAIAQ2ArABIAAgBzcDqAEgACAAKAK0ASgCABA0Igc3AyACQCAHQgBTBEAgACgCtAFBCGogACgCtAEoAgAQFyAAQn83A7gBDAELIAAgACkDIDcDoAEgAEEAOgAXIABCADcDGANAIAApAxggACkDqAFUBEAgACAAKAK0ASgCQCAAKAKwASAAKQMYp0EDdGopAwCnQQR0ajYCDCAAIAAoArQBAn8gACgCDCgCBARAIAAoAgwoAgQMAQsgACgCDCgCAAtBgAQQUCIDNgIQIANBAEgEQCAAQn83A7gBDAMLIAAoAhAEQCAAQQE6ABcLIAAgACkDGEIBfDcDGAwBCwsgACAAKAK0ASgCABA0Igc3AyAgB0IAUwRAIAAoArQBQQhqIAAoArQBKAIAEBcgAEJ/NwO4AQwBCyAAIAApAyAgACkDoAF9NwOYAQJAIAApA6ABQv////8PWARAIAApA6gBQv//A1gNAQsgAEEBOgAXCyAAIABBMGpC4gAQKSIDNgIsIANFBEAgACgCtAFBCGpBDkEAEBQgAEJ/NwO4AQwBCyAALQAXQQFxBEAgACgCLEHnEkEEEEAgACgCLEIsEC0gACgCLEEtEB8gACgCLEEtEB8gACgCLEEAECAgACgCLEEAECAgACgCLCAAKQOoARAtIAAoAiwgACkDqAEQLSAAKAIsIAApA5gBEC0gACgCLCAAKQOgARAtIAAoAixB4hJBBBBAIAAoAixBABAgIAAoAiwgACkDoAEgACkDmAF8EC0gACgCLEEBECALIAAoAixB7BJBBBBAIAAoAixBABAgIAAoAiwgACkDqAFC//8DWgR+Qv//AwUgACkDqAELp0H//wNxEB8gACgCLCAAKQOoAUL//wNaBH5C//8DBSAAKQOoAQunQf//A3EQHyAAKAIsIAApA5gBQv////8PWgR/QX8FIAApA5gBpwsQICAAKAIsIAApA6ABQv////8PWgR/QX8FIAApA6ABpwsQICAAAn8gACgCtAEtAChBAXEEQCAAKAK0ASgCJAwBCyAAKAK0ASgCIAs2ApQBIAAoAiwCfyAAKAKUAQRAIAAoApQBLwEEDAELQQALQf//A3EQHwJ/IwBBEGsiAyAAKAIsNgIMIAMoAgwtAABBAXFFCwRAIAAoArQBQQhqQRRBABAUIAAoAiwQFiAAQn83A7gBDAELIAAoArQBAn8jAEEQayIDIAAoAiw2AgwgAygCDCgCBAsCfiMAQRBrIgMgACgCLDYCDAJ+IAMoAgwtAABBAXEEQCADKAIMKQMQDAELQgALCxA1QQBIBEAgACgCLBAWIABCfzcDuAEMAQsgACgCLBAWIAAoApQBBEAgACgCtAEgACgClAEoAgAgACgClAEvAQStEDVBAEgEQCAAQn83A7gBDAILCyAAIAApA5gBNwO4AQsgACkDuAEhByAAQcABaiQAIAIgBzcDACAHQgBTBEAgAkF/NgIsDAELIAIgAigCKCgCABA0Igc3AwggB0IAUwRAIAJBfzYCLAwBCyACQQA2AiwLIAIoAiwhACACQTBqJAAgAEEASAsEQCABQQE2AiwLCyABKAIoEBUgASgCLEUEQAJ/IAEoAlgoAgAhAiMAQRBrIgAkACAAIAI2AggCQCAAKAIIKAIkQQFHBEAgACgCCEEMakESQQAQFCAAQX82AgwMAQsgACgCCCgCIEEBSwRAIAAoAghBDGpBHUEAEBQgAEF/NgIMDAELIAAoAggoAiAEQCAAKAIIEDFBAEgEQCAAQX82AgwMAgsLIAAoAghBAEIAQQkQIUIAUwRAIAAoAghBAjYCJCAAQX82AgwMAQsgACgCCEEANgIkIABBADYCDAsgACgCDCECIABBEGokACACCwRAIAEoAlhBCGogASgCWCgCABAXIAFBATYCLAsLIAEoAlgoAlQhAiMAQRBrIgAkACAAIAI2AgwgACgCDEQAAAAAAADwPxBWIABBEGokACABKAIsBEAgASgCWCgCABBnIAFBfzYCXAwBCyABKAJYED0gAUEANgJcCyABKAJcIQAgAUHgAGokACAAC9IOAgd/An4jAEEwayIDJAAgAyAANgIoIAMgATYCJCADIAI2AiAjAEEQayIAIANBCGo2AgwgACgCDEEANgIAIAAoAgxBADYCBCAAKAIMQQA2AgggAygCKCEAIwBBIGsiBCQAIAQgADYCGCAEQgA3AxAgBEJ/NwMIIAQgA0EIajYCBAJAAkAgBCgCGARAIAQpAwhCf1kNAQsgBCgCBEESQQAQFCAEQQA2AhwMAQsgBCgCGCEAIAQpAxAhCiAEKQMIIQsgBCgCBCEBIwBBoAFrIgIkACACIAA2ApgBIAJBADYClAEgAiAKNwOIASACIAs3A4ABIAJBADYCfCACIAE2AngCQAJAIAIoApQBDQAgAigCmAENACACKAJ4QRJBABAUIAJBADYCnAEMAQsgAikDgAFCAFMEQCACQgA3A4ABCwJAIAIpA4gBQv///////////wBYBEAgAikDiAEgAikDiAEgAikDgAF8WA0BCyACKAJ4QRJBABAUIAJBADYCnAEMAQsgAkGIARAYIgA2AnQgAEUEQCACKAJ4QQ5BABAUIAJBADYCnAEMAQsgAigCdEEANgIYIAIoApgBBEAgAigCmAEiABArQQFqIgEQGCIFBH8gBSAAIAEQGQVBAAshACACKAJ0IAA2AhggAEUEQCACKAJ4QQ5BABAUIAIoAnQQFSACQQA2ApwBDAILCyACKAJ0IAIoApQBNgIcIAIoAnQgAikDiAE3A2ggAigCdCACKQOAATcDcAJAIAIoAnwEQCACKAJ0IgAgAigCfCIBKQMANwMgIAAgASkDMDcDUCAAIAEpAyg3A0ggACABKQMgNwNAIAAgASkDGDcDOCAAIAEpAxA3AzAgACABKQMINwMoIAIoAnRBADYCKCACKAJ0IgAgACkDIEL+////D4M3AyAMAQsgAigCdEEgahA7CyACKAJ0KQNwQgBSBEAgAigCdCACKAJ0KQNwNwM4IAIoAnQiACAAKQMgQgSENwMgCyMAQRBrIgAgAigCdEHYAGo2AgwgACgCDEEANgIAIAAoAgxBADYCBCAAKAIMQQA2AgggAigCdEEANgKAASACKAJ0QQA2AoQBIwBBEGsiACACKAJ0NgIMIAAoAgxBADYCACAAKAIMQQA2AgQgACgCDEEANgIIIAJBfzYCBCACQQc2AgBBDiACEDZCP4QhCiACKAJ0IAo3AxACQCACKAJ0KAIYBEAgAiACKAJ0KAIYIAJBGGoQpgFBAE46ABcgAi0AF0EBcUUEQAJAIAIoAnQpA2hQRQ0AIAIoAnQpA3BQRQ0AIAIoAnRC//8DNwMQCwsMAQsCQCACKAJ0KAIcIgAoAkxBAEgNAAsgACgCPCEAQQAhBSMAQSBrIgYkAAJ/AkAgACACQRhqIgkQCiIBQXhGBEAjAEEgayIHJAAgACAHQQhqEAkiCAR/QbSbASAINgIAQQAFQQELIQggB0EgaiQAIAgNAQsgAUGBYE8Ef0G0mwFBACABazYCAEF/BSABCwwBCwNAIAUgBmoiASAFQccSai0AADoAACAFQQ5HIQcgBUEBaiEFIAcNAAsCQCAABEBBDyEFIAAhAQNAIAFBCk8EQCAFQQFqIQUgAUEKbiEBDAELCyAFIAZqQQA6AAADQCAGIAVBAWsiBWogACAAQQpuIgFBCmxrQTByOgAAIABBCUshByABIQAgBw0ACwwBCyABQTA6AAAgBkEAOgAPCyAGIAkQAiIAQYFgTwR/QbSbAUEAIABrNgIAQX8FIAALCyEAIAZBIGokACACIABBAE46ABcLAkAgAi0AF0EBcUUEQCACKAJ0QdgAakEFQbSbASgCABAUDAELIAIoAnQpAyBCEINQBEAgAigCdCACKAJYNgJIIAIoAnQiACAAKQMgQhCENwMgCyACKAIkQYDgA3FBgIACRgRAIAIoAnRC/4EBNwMQIAIpA0AgAigCdCkDaCACKAJ0KQNwfFQEQCACKAJ4QRJBABAUIAIoAnQoAhgQFSACKAJ0EBUgAkEANgKcAQwDCyACKAJ0KQNwUARAIAIoAnQgAikDQCACKAJ0KQNofTcDOCACKAJ0IgAgACkDIEIEhDcDIAJAIAIoAnQoAhhFDQAgAikDiAFQRQ0AIAIoAnRC//8DNwMQCwsLCyACKAJ0IgAgACkDEEKAgBCENwMQIAJBHiACKAJ0IAIoAngQlAEiADYCcCAARQRAIAIoAnQoAhgQFSACKAJ0EBUgAkEANgKcAQwBCyACIAIoAnA2ApwBCyACKAKcASEAIAJBoAFqJAAgBCAANgIcCyAEKAIcIQAgBEEgaiQAIAMgADYCGAJAIABFBEAgAygCICADQQhqEJ0BIANBCGoQNyADQQA2AiwMAQsgAyADKAIYIAMoAiQgA0EIahCcASIANgIcIABFBEAgAygCGBAbIAMoAiAgA0EIahCdASADQQhqEDcgA0EANgIsDAELIANBCGoQNyADIAMoAhw2AiwLIAMoAiwhACADQTBqJAAgAAsYAQF/IwBBEGsiASAANgIMIAEoAgxBDGoLkh8BBn8jAEHgAGsiBCQAIAQgADYCVCAEIAE2AlAgBCACNwNIIAQgAzYCRCAEIAQoAlQ2AkAgBCAEKAJQNgI8AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgBCgCRA4TBgcCDAQFCg4BAwkQCw8NCBERABELIARCADcDWAwRCyAEKAJAKAIYRQRAIAQoAkBBHEEAEBQgBEJ/NwNYDBELIAQoAkAhACMAQYABayIBJAAgASAANgJ4IAEgASgCeCgCGBArQQhqEBgiADYCdAJAIABFBEAgASgCeEEOQQAQFCABQX82AnwMAQsCQCABKAJ4KAIYIAFBEGoQpgFFBEAgASABKAIcNgJsDAELIAFBfzYCbAsgASgCdCEAIAEgASgCeCgCGDYCACAAQasSIAEQcCABKAJ0IQMgASgCbCEHIwBBMGsiACQAIAAgAzYCKCAAIAc2AiQgAEEANgIQIAAgACgCKCAAKAIoECtqNgIYIAAgACgCGEEBazYCHANAIAAoAhwgACgCKE8EfyAAKAIcLAAAQdgARgVBAAtBAXEEQCAAIAAoAhBBAWo2AhAgACAAKAIcQQFrNgIcDAELCwJAIAAoAhBFBEBBtJsBQRw2AgAgAEF/NgIsDAELIAAgACgCHEEBajYCHANAIwBBEGsiByQAAkACfyMAQRBrIgMkACADIAdBCGo2AgggA0EEOwEGIANB6AtBAEEAEG0iBTYCAAJAIAVBAEgEQCADQQA6AA8MAQsCfyADKAIAIQYgAygCCCEIIAMvAQYhCSMAQRBrIgUkACAFIAk2AgwgBSAINgIIIAYgBUEIakEBIAVBBGoQBiIGBH9BtJsBIAY2AgBBfwVBAAshBiAFKAIEIQggBUEQaiQAIAMvAQZBfyAIIAYbRwsEQCADKAIAEGwgA0EAOgAPDAELIAMoAgAQbCADQQE6AA8LIAMtAA9BAXEhBSADQRBqJAAgBQsEQCAHIAcoAgg2AgwMAQtBwKABLQAAQQFxRQRAQQAQASEGAkBByJkBKAIAIgNFBEBBzJkBKAIAIAY2AgAMAQtB0JkBQQNBA0EBIANBB0YbIANBH0YbNgIAQbygAUEANgIAQcyZASgCACEFIANBAU4EQCAGrSECQQAhBgNAIAUgBkECdGogAkKt/tXk1IX9qNgAfkIBfCICQiCIPgIAIAZBAWoiBiADRw0ACwsgBSAFKAIAQQFyNgIACwtBzJkBKAIAIQMCQEHImQEoAgAiBUUEQCADIAMoAgBB7ZyZjgRsQbngAGpB/////wdxIgM2AgAMAQsgA0HQmQEoAgAiBkECdGoiCCAIKAIAIANBvKABKAIAIghBAnRqKAIAaiIDNgIAQbygAUEAIAhBAWoiCCAFIAhGGzYCAEHQmQFBACAGQQFqIgYgBSAGRhs2AgAgA0EBdiEDCyAHIAM2AgwLIAcoAgwhAyAHQRBqJAAgACADNgIMIAAgACgCHDYCFANAIAAoAhQgACgCGEkEQCAAIAAoAgxBJHA6AAsCfyAALAALQQpIBEAgACwAC0EwagwBCyAALAALQdcAagshAyAAIAAoAhQiB0EBajYCFCAHIAM6AAAgACAAKAIMQSRuNgIMDAELCyAAKAIoIQMgACAAKAIkQX9GBH9BtgMFIAAoAiQLNgIAIAAgA0HCgSAgABBtIgM2AiAgA0EATgRAIAAoAiRBf0cEQCAAKAIoIAAoAiQQDyIDQYFgTwR/QbSbAUEAIANrNgIAQQAFIAMLGgsgACAAKAIgNgIsDAILQbSbASgCAEEURg0ACyAAQX82AiwLIAAoAiwhAyAAQTBqJAAgASADIgA2AnAgAEF/RgRAIAEoAnhBDEG0mwEoAgAQFCABKAJ0EBUgAUF/NgJ8DAELIAEgASgCcEGjEhChASIANgJoIABFBEAgASgCeEEMQbSbASgCABAUIAEoAnAQbCABKAJ0EG4aIAEoAnQQFSABQX82AnwMAQsgASgCeCABKAJoNgKEASABKAJ4IAEoAnQ2AoABIAFBADYCfAsgASgCfCEAIAFBgAFqJAAgBCAArDcDWAwQCyAEKAJAKAIYBEAgBCgCQCgCHBBVGiAEKAJAQQA2AhwLIARCADcDWAwPCyAEKAJAKAKEARBVQQBIBEAgBCgCQEEANgKEASAEKAJAQQZBtJsBKAIAEBQLIAQoAkBBADYChAEgBCgCQCgCgAEgBCgCQCgCGBAIIgBBgWBPBH9BtJsBQQAgAGs2AgBBfwUgAAtBAEgEQCAEKAJAQQJBtJsBKAIAEBQgBEJ/NwNYDA8LIAQoAkAoAoABEBUgBCgCQEEANgKAASAEQgA3A1gMDgsgBCAEKAJAIAQoAlAgBCkDSBBCNwNYDA0LIAQoAkAoAhgQFSAEKAJAKAKAARAVIAQoAkAoAhwEQCAEKAJAKAIcEFUaCyAEKAJAEBUgBEIANwNYDAwLIAQoAkAoAhgEQCAEKAJAKAIYIQEjAEEgayIAJAAgACABNgIYIABBADoAFyAAQYCAIDYCDAJAIAAtABdBAXEEQCAAIAAoAgxBAnI2AgwMAQsgACAAKAIMNgIMCyAAKAIYIQEgACgCDCEDIABBtgM2AgAgACABIAMgABBtIgE2AhACQCABQQBIBEAgAEEANgIcDAELIAAgACgCEEGjEkGgEiAALQAXQQFxGxChASIBNgIIIAFFBEAgAEEANgIcDAELIAAgACgCCDYCHAsgACgCHCEBIABBIGokACAEKAJAIAE2AhwgAUUEQCAEKAJAQQtBtJsBKAIAEBQgBEJ/NwNYDA0LCyAEKAJAKQNoQgBSBEAgBCgCQCgCHCAEKAJAKQNoIAQoAkAQnwFBAEgEQCAEQn83A1gMDQsLIAQoAkBCADcDeCAEQgA3A1gMCwsCQCAEKAJAKQNwQgBSBEAgBCAEKAJAKQNwIAQoAkApA3h9NwMwIAQpAzAgBCkDSFYEQCAEIAQpA0g3AzALDAELIAQgBCkDSDcDMAsgBCkDMEL/////D1YEQCAEQv////8PNwMwCyAEAn8gBCgCPCEHIAQpAzCnIQAgBCgCQCgCHCIDKAJMGiADIAMtAEoiAUEBayABcjoASiADKAIIIAMoAgQiBWsiAUEBSAR/IAAFIAcgBSABIAAgACABSxsiARAZGiADIAMoAgQgAWo2AgQgASAHaiEHIAAgAWsLIgEEQANAAkACfyADIAMtAEoiBUEBayAFcjoASiADKAIUIAMoAhxLBEAgA0EAQQAgAygCJBEBABoLIANBADYCHCADQgA3AxAgAygCACIFQQRxBEAgAyAFQSByNgIAQX8MAQsgAyADKAIsIAMoAjBqIgY2AgggAyAGNgIEIAVBG3RBH3ULRQRAIAMgByABIAMoAiARAQAiBUEBakEBSw0BCyAAIAFrDAMLIAUgB2ohByABIAVrIgENAAsLIAALIgA2AiwgAEUEQAJ/IAQoAkAoAhwiACgCTEF/TARAIAAoAgAMAQsgACgCAAtBBXZBAXEEQCAEKAJAQQVBtJsBKAIAEBQgBEJ/NwNYDAwLCyAEKAJAIgAgACkDeCAEKAIsrXw3A3ggBCAEKAIsrTcDWAwKCyAEKAJAKAIYEG5BAEgEQCAEKAJAQRZBtJsBKAIAEBQgBEJ/NwNYDAoLIARCADcDWAwJCyAEKAJAKAKEAQRAIAQoAkAoAoQBEFUaIAQoAkBBADYChAELIAQoAkAoAoABEG4aIAQoAkAoAoABEBUgBCgCQEEANgKAASAEQgA3A1gMCAsgBAJ/IAQpA0hCEFQEQCAEKAJAQRJBABAUQQAMAQsgBCgCUAs2AhggBCgCGEUEQCAEQn83A1gMCAsgBEEBNgIcAkACQAJAAkACQCAEKAIYKAIIDgMAAgEDCyAEIAQoAhgpAwA3AyAMAwsCQCAEKAJAKQNwUARAIAQoAkAoAhwgBCgCGCkDAEECIAQoAkAQa0EASARAIARCfzcDWAwNCyAEIAQoAkAoAhwQowEiAjcDICACQgBTBEAgBCgCQEEEQbSbASgCABAUIARCfzcDWAwNCyAEIAQpAyAgBCgCQCkDaH03AyAgBEEANgIcDAELIAQgBCgCQCkDcCAEKAIYKQMAfDcDIAsMAgsgBCAEKAJAKQN4IAQoAhgpAwB8NwMgDAELIAQoAkBBEkEAEBQgBEJ/NwNYDAgLAkACQCAEKQMgQgBTDQAgBCgCQCkDcEIAUgRAIAQpAyAgBCgCQCkDcFYNAQsgBCgCQCkDaCAEKQMgIAQoAkApA2h8WA0BCyAEKAJAQRJBABAUIARCfzcDWAwICyAEKAJAIAQpAyA3A3ggBCgCHARAIAQoAkAoAhwgBCgCQCkDeCAEKAJAKQNofCAEKAJAEJ8BQQBIBEAgBEJ/NwNYDAkLCyAEQgA3A1gMBwsgBAJ/IAQpA0hCEFQEQCAEKAJAQRJBABAUQQAMAQsgBCgCUAs2AhQgBCgCFEUEQCAEQn83A1gMBwsgBCgCQCgChAEgBCgCFCkDACAEKAIUKAIIIAQoAkAQa0EASARAIARCfzcDWAwHCyAEQgA3A1gMBgsgBCkDSEI4VARAIARCfzcDWAwGCwJ/IwBBEGsiACAEKAJAQdgAajYCDCAAKAIMKAIACwRAIAQoAkACfyMAQRBrIgAgBCgCQEHYAGo2AgwgACgCDCgCAAsCfyMAQRBrIgAgBCgCQEHYAGo2AgwgACgCDCgCBAsQFCAEQn83A1gMBgsgBCgCUCIAIAQoAkAiASkAIDcAACAAIAEpAFA3ADAgACABKQBINwAoIAAgASkAQDcAICAAIAEpADg3ABggACABKQAwNwAQIAAgASkAKDcACCAEQjg3A1gMBQsgBCAEKAJAKQMQNwNYDAQLIAQgBCgCQCkDeDcDWAwDCyAEIAQoAkAoAoQBEKMBNwMIIAQpAwhCAFMEQCAEKAJAQR5BtJsBKAIAEBQgBEJ/NwNYDAMLIAQgBCkDCDcDWAwCCyAEKAJAKAKEASIAKAJMQQBOGiAAIAAoAgBBT3E2AgAgBAJ/IAQoAlAhASAEKQNIpyIAIAACfyAEKAJAKAKEASIDKAJMQX9MBEAgASAAIAMQcgwBCyABIAAgAxByCyIBRg0AGiABCzYCBAJAIAQpA0ggBCgCBK1RBEACfyAEKAJAKAKEASIAKAJMQX9MBEAgACgCAAwBCyAAKAIAC0EFdkEBcUUNAQsgBCgCQEEGQbSbASgCABAUIARCfzcDWAwCCyAEIAQoAgStNwNYDAELIAQoAkBBHEEAEBQgBEJ/NwNYCyAEKQNYIQIgBEHgAGokACACCwkAIAAoAjwQBQvkAQEEfyMAQSBrIgMkACADIAE2AhAgAyACIAAoAjAiBEEAR2s2AhQgACgCLCEFIAMgBDYCHCADIAU2AhhBfyEEAkACQCAAKAI8IANBEGpBAiADQQxqEAYiBQR/QbSbASAFNgIAQX8FQQALRQRAIAMoAgwiBEEASg0BCyAAIAAoAgAgBEEwcUEQc3I2AgAMAQsgBCADKAIUIgZNDQAgACAAKAIsIgU2AgQgACAFIAQgBmtqNgIIIAAoAjAEQCAAIAVBAWo2AgQgASACakEBayAFLQAAOgAACyACIQQLIANBIGokACAEC/QCAQd/IwBBIGsiAyQAIAMgACgCHCIFNgIQIAAoAhQhBCADIAI2AhwgAyABNgIYIAMgBCAFayIBNgIUIAEgAmohBUECIQcgA0EQaiEBAn8CQAJAIAAoAjwgA0EQakECIANBDGoQAyIEBH9BtJsBIAQ2AgBBfwVBAAtFBEADQCAFIAMoAgwiBEYNAiAEQX9MDQMgASAEIAEoAgQiCEsiBkEDdGoiCSAEIAhBACAGG2siCCAJKAIAajYCACABQQxBBCAGG2oiCSAJKAIAIAhrNgIAIAUgBGshBSAAKAI8IAFBCGogASAGGyIBIAcgBmsiByADQQxqEAMiBAR/QbSbASAENgIAQX8FQQALRQ0ACwsgBUF/Rw0BCyAAIAAoAiwiATYCHCAAIAE2AhQgACABIAAoAjBqNgIQIAIMAQsgAEEANgIcIABCADcDECAAIAAoAgBBIHI2AgBBACAHQQJGDQAaIAIgASgCBGsLIQAgA0EgaiQAIAALUgEBfyMAQRBrIgMkACAAKAI8IAGnIAFCIIinIAJB/wFxIANBCGoQDSIABH9BtJsBIAA2AgBBfwVBAAshACADKQMIIQEgA0EQaiQAQn8gASAAGwtFAEGgmwFCADcDAEGYmwFCADcDAEGQmwFCADcDAEGImwFCADcDAEGAmwFCADcDAEH4mgFCADcDAEHwmgFCADcDAEHwmgEL1QQBBX8jAEGwAWsiASQAIAEgADYCqAEgASgCqAEQNwJAAkAgASgCqAEoAgBBAE4EQCABKAKoASgCAEGAFCgCAEgNAQsgASABKAKoASgCADYCECABQSBqQY8SIAFBEGoQcCABQQA2AqQBIAEgAUEgajYCoAEMAQsgASABKAKoASgCAEECdEGAE2ooAgA2AqQBAkACQAJAAkAgASgCqAEoAgBBAnRBkBRqKAIAQQFrDgIAAQILIAEoAqgBKAIEIQJBkJkBKAIAIQRBACEAAkACQANAIAIgAEGgiAFqLQAARwRAQdcAIQMgAEEBaiIAQdcARw0BDAILCyAAIgMNAEGAiQEhAgwBC0GAiQEhAANAIAAtAAAhBSAAQQFqIgIhACAFDQAgAiEAIANBAWsiAw0ACwsgBCgCFBogASACNgKgAQwCCyMAQRBrIgAgASgCqAEoAgQ2AgwgAUEAIAAoAgxrQQJ0QajZAGooAgA2AqABDAELIAFBADYCoAELCwJAIAEoAqABRQRAIAEgASgCpAE2AqwBDAELIAEgASgCoAEQKwJ/IAEoAqQBBEAgASgCpAEQK0ECagwBC0EAC2pBAWoQGCIANgIcIABFBEAgAUG4EygCADYCrAEMAQsgASgCHCEAAn8gASgCpAEEQCABKAKkAQwBC0H6EgshA0HfEkH6EiABKAKkARshAiABIAEoAqABNgIIIAEgAjYCBCABIAM2AgAgAEG+CiABEHAgASgCqAEgASgCHDYCCCABIAEoAhw2AqwBCyABKAKsASEAIAFBsAFqJAAgAAszAQF/IAAoAhQiAyABIAIgACgCECADayIBIAEgAksbIgEQGRogACAAKAIUIAFqNgIUIAILjwUCBn4BfyABIAEoAgBBD2pBcHEiAUEQajYCACAAAnwgASkDACEDIAEpAwghBiMAQSBrIggkAAJAIAZC////////////AIMiBEKAgICAgIDAgDx9IARCgICAgICAwP/DAH1UBEAgBkIEhiADQjyIhCEEIANC//////////8PgyIDQoGAgICAgICACFoEQCAEQoGAgICAgICAwAB8IQIMAgsgBEKAgICAgICAgEB9IQIgA0KAgICAgICAgAiFQgBSDQEgAiAEQgGDfCECDAELIANQIARCgICAgICAwP//AFQgBEKAgICAgIDA//8AURtFBEAgBkIEhiADQjyIhEL/////////A4NCgICAgICAgPz/AIQhAgwBC0KAgICAgICA+P8AIQIgBEL///////+//8MAVg0AQgAhAiAEQjCIpyIAQZH3AEkNACADIQIgBkL///////8/g0KAgICAgIDAAIQiBSEHAkAgAEGB9wBrIgFBwABxBEAgAiABQUBqrYYhB0IAIQIMAQsgAUUNACAHIAGtIgSGIAJBwAAgAWutiIQhByACIASGIQILIAggAjcDECAIIAc3AxgCQEGB+AAgAGsiAEHAAHEEQCAFIABBQGqtiCEDQgAhBQwBCyAARQ0AIAVBwAAgAGuthiADIACtIgKIhCEDIAUgAoghBQsgCCADNwMAIAggBTcDCCAIKQMIQgSGIAgpAwAiA0I8iIQhAiAIKQMQIAgpAxiEQgBSrSADQv//////////D4OEIgNCgYCAgICAgIAIWgRAIAJCAXwhAgwBCyADQoCAgICAgICACIVCAFINACACQgGDIAJ8IQILIAhBIGokACACIAZCgICAgICAgICAf4OEvws5AwALrRcDEn8CfgF8IwBBsARrIgkkACAJQQA2AiwCQCABvSIYQn9XBEBBASESQa4IIRMgAZoiAb0hGAwBCyAEQYAQcQRAQQEhEkGxCCETDAELQbQIQa8IIARBAXEiEhshEyASRSEXCwJAIBhCgICAgICAgPj/AINCgICAgICAgPj/AFEEQCAAQSAgAiASQQNqIg0gBEH//3txECYgACATIBIQIiAAQeQLQbUSIAVBIHEiAxtBjw1BuRIgAxsgASABYhtBAxAiDAELIAlBEGohEAJAAn8CQCABIAlBLGoQqQEiASABoCIBRAAAAAAAAAAAYgRAIAkgCSgCLCIGQQFrNgIsIAVBIHIiFEHhAEcNAQwDCyAFQSByIhRB4QBGDQIgCSgCLCELQQYgAyADQQBIGwwBCyAJIAZBHWsiCzYCLCABRAAAAAAAALBBoiEBQQYgAyADQQBIGwshCiAJQTBqIAlB0AJqIAtBAEgbIg4hBwNAIAcCfyABRAAAAAAAAPBBYyABRAAAAAAAAAAAZnEEQCABqwwBC0EACyIDNgIAIAdBBGohByABIAO4oUQAAAAAZc3NQaIiAUQAAAAAAAAAAGINAAsCQCALQQFIBEAgCyEDIAchBiAOIQgMAQsgDiEIIAshAwNAIANBHSADQR1IGyEMAkAgB0EEayIGIAhJDQAgDK0hGUIAIRgDQCAGIAY1AgAgGYYgGHwiGCAYQoCU69wDgCIYQoCU69wDfn0+AgAgCCAGQQRrIgZNBEAgGEL/////D4MhGAwBCwsgGKciA0UNACAIQQRrIgggAzYCAAsDQCAIIAciBkkEQCAGQQRrIgcoAgBFDQELCyAJIAkoAiwgDGsiAzYCLCAGIQcgA0EASg0ACwsgCkEZakEJbSEHIANBf0wEQCAHQQFqIQ0gFEHmAEYhFQNAQQlBACADayADQXdIGyEWAkAgBiAISwRAQYCU69wDIBZ2IQ9BfyAWdEF/cyERQQAhAyAIIQcDQCAHIAMgBygCACIMIBZ2ajYCACAMIBFxIA9sIQMgB0EEaiIHIAZJDQALIAggCEEEaiAIKAIAGyEIIANFDQEgBiADNgIAIAZBBGohBgwBCyAIIAhBBGogCCgCABshCAsgCSAJKAIsIBZqIgM2AiwgDiAIIBUbIgcgDUECdGogBiAGIAdrQQJ1IA1KGyEGIANBAEgNAAsLQQAhBwJAIAYgCE0NACAOIAhrQQJ1QQlsIQcgCCgCACIMQQpJDQBB5AAhAwNAIAdBAWohByADIAxLDQEgA0EKbCEDDAALAAsgCkEAIAcgFEHmAEYbayAUQecARiAKQQBHcWsiAyAGIA5rQQJ1QQlsQQlrSARAIANBgMgAaiIRQQltIgxBAnQgCUEwakEEciAJQdQCaiALQQBIG2pBgCBrIQ1BCiEDAkAgESAMQQlsayIMQQdKDQBB5AAhAwNAIAxBAWoiDEEIRg0BIANBCmwhAwwACwALAkAgDSgCACIRIBEgA24iDCADbGsiD0EBIA1BBGoiCyAGRhtFDQBEAAAAAAAA4D9EAAAAAAAA8D9EAAAAAAAA+D8gBiALRhtEAAAAAAAA+D8gDyADQQF2IgtGGyALIA9LGyEaRAEAAAAAAEBDRAAAAAAAAEBDIAxBAXEbIQECQCAXDQAgEy0AAEEtRw0AIBqaIRogAZohAQsgDSARIA9rIgs2AgAgASAaoCABYQ0AIA0gAyALaiIDNgIAIANBgJTr3ANPBEADQCANQQA2AgAgCCANQQRrIg1LBEAgCEEEayIIQQA2AgALIA0gDSgCAEEBaiIDNgIAIANB/5Pr3ANLDQALCyAOIAhrQQJ1QQlsIQcgCCgCACILQQpJDQBB5AAhAwNAIAdBAWohByADIAtLDQEgA0EKbCEDDAALAAsgDUEEaiIDIAYgAyAGSRshBgsDQCAGIgsgCE0iDEUEQCALQQRrIgYoAgBFDQELCwJAIBRB5wBHBEAgBEEIcSEPDAELIAdBf3NBfyAKQQEgChsiBiAHSiAHQXtKcSIDGyAGaiEKQX9BfiADGyAFaiEFIARBCHEiDw0AQXchBgJAIAwNACALQQRrKAIAIgNFDQBBACEGIANBCnANAEEAIQxB5AAhBgNAIAMgBnBFBEAgDEEBaiEMIAZBCmwhBgwBCwsgDEF/cyEGCyALIA5rQQJ1QQlsIQMgBUFfcUHGAEYEQEEAIQ8gCiADIAZqQQlrIgNBACADQQBKGyIDIAMgCkobIQoMAQtBACEPIAogAyAHaiAGakEJayIDQQAgA0EAShsiAyADIApKGyEKCyAKIA9yQQBHIREgAEEgIAIgBUFfcSIMQcYARgR/IAdBACAHQQBKGwUgECAHIAdBH3UiA2ogA3OtIBAQRCIGa0EBTARAA0AgBkEBayIGQTA6AAAgECAGa0ECSA0ACwsgBkECayIVIAU6AAAgBkEBa0EtQSsgB0EASBs6AAAgECAVawsgCiASaiARampBAWoiDSAEECYgACATIBIQIiAAQTAgAiANIARBgIAEcxAmAkACQAJAIAxBxgBGBEAgCUEQakEIciEDIAlBEGpBCXIhByAOIAggCCAOSxsiBSEIA0AgCDUCACAHEEQhBgJAIAUgCEcEQCAGIAlBEGpNDQEDQCAGQQFrIgZBMDoAACAGIAlBEGpLDQALDAELIAYgB0cNACAJQTA6ABggAyEGCyAAIAYgByAGaxAiIAhBBGoiCCAOTQ0AC0EAIQYgEUUNAiAAQdYSQQEQIiAIIAtPDQEgCkEBSA0BA0AgCDUCACAHEEQiBiAJQRBqSwRAA0AgBkEBayIGQTA6AAAgBiAJQRBqSw0ACwsgACAGIApBCSAKQQlIGxAiIApBCWshBiAIQQRqIgggC08NAyAKQQlKIQMgBiEKIAMNAAsMAgsCQCAKQQBIDQAgCyAIQQRqIAggC0kbIQUgCUEQakEJciELIAlBEGpBCHIhAyAIIQcDQCALIAc1AgAgCxBEIgZGBEAgCUEwOgAYIAMhBgsCQCAHIAhHBEAgBiAJQRBqTQ0BA0AgBkEBayIGQTA6AAAgBiAJQRBqSw0ACwwBCyAAIAZBARAiIAZBAWohBkEAIApBAEwgDxsNACAAQdYSQQEQIgsgACAGIAsgBmsiBiAKIAYgCkgbECIgCiAGayEKIAdBBGoiByAFTw0BIApBf0oNAAsLIABBMCAKQRJqQRJBABAmIAAgFSAQIBVrECIMAgsgCiEGCyAAQTAgBkEJakEJQQAQJgsMAQsgE0EJaiATIAVBIHEiCxshCgJAIANBC0sNAEEMIANrIgZFDQBEAAAAAAAAIEAhGgNAIBpEAAAAAAAAMECiIRogBkEBayIGDQALIAotAABBLUYEQCAaIAGaIBqhoJohAQwBCyABIBqgIBqhIQELIBAgCSgCLCIGIAZBH3UiBmogBnOtIBAQRCIGRgRAIAlBMDoADyAJQQ9qIQYLIBJBAnIhDiAJKAIsIQcgBkECayIMIAVBD2o6AAAgBkEBa0EtQSsgB0EASBs6AAAgBEEIcSEHIAlBEGohCANAIAgiBQJ/IAGZRAAAAAAAAOBBYwRAIAGqDAELQYCAgIB4CyIGQYCHAWotAAAgC3I6AAAgASAGt6FEAAAAAAAAMECiIQECQCAFQQFqIgggCUEQamtBAUcNAAJAIAFEAAAAAAAAAABiDQAgA0EASg0AIAdFDQELIAVBLjoAASAFQQJqIQgLIAFEAAAAAAAAAABiDQALIABBICACIA4CfwJAIANFDQAgCCAJa0ESayADTg0AIAMgEGogDGtBAmoMAQsgECAJQRBqIAxqayAIagsiA2oiDSAEECYgACAKIA4QIiAAQTAgAiANIARBgIAEcxAmIAAgCUEQaiAIIAlBEGprIgUQIiAAQTAgAyAFIBAgDGsiA2prQQBBABAmIAAgDCADECILIABBICACIA0gBEGAwABzECYgCUGwBGokACACIA0gAiANShsLBgBB4J8BCwYAQdyfAQsGAEHUnwELGAEBfyMAQRBrIgEgADYCDCABKAIMQQRqCxgBAX8jAEEQayIBIAA2AgwgASgCDEEIagtpAQF/IwBBEGsiASQAIAEgADYCDCABKAIMKAIUBEAgASgCDCgCFBAbCyABQQA2AgggASgCDCgCBARAIAEgASgCDCgCBDYCCAsgASgCDEEEahA3IAEoAgwQFSABKAIIIQAgAUEQaiQAIAALqQEBA38CQCAALQAAIgJFDQADQCABLQAAIgRFBEAgAiEDDAILAkAgAiAERg0AIAJBIHIgAiACQcEAa0EaSRsgAS0AACICQSByIAIgAkHBAGtBGkkbRg0AIAAtAAAhAwwCCyABQQFqIQEgAC0AASECIABBAWohACACDQALCyADQf8BcSIAQSByIAAgAEHBAGtBGkkbIAEtAAAiAEEgciAAIABBwQBrQRpJG2sL2AkBAX8jAEGwAWsiBSQAIAUgADYCpAEgBSABNgKgASAFIAI2ApwBIAUgAzcDkAEgBSAENgKMASAFIAUoAqABNgKIAQJAAkACQAJAAkACQAJAAkACQAJAAkAgBSgCjAEODwABAgMEBQcICQkJCQkJBgkLIAUoAogBQgA3AyAgBUIANwOoAQwJCyAFIAUoAqQBIAUoApwBIAUpA5ABEC4iAzcDgAEgA0IAUwRAIAUoAogBQQhqIAUoAqQBEBcgBUJ/NwOoAQwJCwJAIAUpA4ABUARAIAUoAogBKQMoIAUoAogBKQMgUQRAIAUoAogBQQE2AgQgBSgCiAEgBSgCiAEpAyA3AxggBSgCiAEoAgAEQCAFKAKkASAFQcgAahA4QQBIBEAgBSgCiAFBCGogBSgCpAEQFyAFQn83A6gBDA0LAkAgBSkDSEIgg1ANACAFKAJ0IAUoAogBKAIwRg0AIAUoAogBQQhqQQdBABAUIAVCfzcDqAEMDQsCQCAFKQNIQgSDUA0AIAUpA2AgBSgCiAEpAxhRDQAgBSgCiAFBCGpBFUEAEBQgBUJ/NwOoAQwNCwsLDAELAkAgBSgCiAEoAgQNACAFKAKIASkDICAFKAKIASkDKFYNACAFIAUoAogBKQMoIAUoAogBKQMgfTcDQANAIAUpA0AgBSkDgAFUBEAgBSAFKQOAASAFKQNAfUL/////D1YEfkL/////DwUgBSkDgAEgBSkDQH0LNwM4IAUoAogBKAIwIAUoApwBIAUpA0CnaiAFKQM4pxAaIQAgBSgCiAEgADYCMCAFKAKIASIAIAUpAzggACkDKHw3AyggBSAFKQM4IAUpA0B8NwNADAELCwsLIAUoAogBIgAgBSkDgAEgACkDIHw3AyAgBSAFKQOAATcDqAEMCAsgBUIANwOoAQwHCyAFIAUoApwBNgI0IAUoAogBKAIEBEAgBSgCNCAFKAKIASkDGDcDGCAFKAI0IAUoAogBKAIwNgIsIAUoAjQgBSgCiAEpAxg3AyAgBSgCNEEAOwEwIAUoAjRBADsBMiAFKAI0IgAgACkDAELsAYQ3AwALIAVCADcDqAEMBgsgBSAFKAKIAUEIaiAFKAKcASAFKQOQARBCNwOoAQwFCyAFKAKIARAVIAVCADcDqAEMBAsjAEEQayIAIAUoAqQBNgIMIAUgACgCDCkDGDcDKCAFKQMoQgBTBEAgBSgCiAFBCGogBSgCpAEQFyAFQn83A6gBDAQLIAUpAyghAyAFQX82AhggBUEQNgIUIAVBDzYCECAFQQ02AgwgBUEMNgIIIAVBCjYCBCAFQQk2AgAgBUEIIAUQNkJ/hSADgzcDqAEMAwsgBQJ/IAUpA5ABQhBUBEAgBSgCiAFBCGpBEkEAEBRBAAwBCyAFKAKcAQs2AhwgBSgCHEUEQCAFQn83A6gBDAMLAkAgBSgCpAEgBSgCHCkDACAFKAIcKAIIECdBAE4EQCAFIAUoAqQBEEoiAzcDICADQgBZDQELIAUoAogBQQhqIAUoAqQBEBcgBUJ/NwOoAQwDCyAFKAKIASAFKQMgNwMgIAVCADcDqAEMAgsgBSAFKAKIASkDIDcDqAEMAQsgBSgCiAFBCGpBHEEAEBQgBUJ/NwOoAQsgBSkDqAEhAyAFQbABaiQAIAMLnAwBAX8jAEEwayIFJAAgBSAANgIkIAUgATYCICAFIAI2AhwgBSADNwMQIAUgBDYCDCAFIAUoAiA2AggCQAJAAkACQAJAAkACQAJAAkACQCAFKAIMDhEAAQIDBQYICAgICAgICAcIBAgLIAUoAghCADcDGCAFKAIIQQA6AAwgBSgCCEEAOgANIAUoAghBADoADyAFKAIIQn83AyAgBSgCCCgCrEAgBSgCCCgCqEAoAgwRAABBAXFFBEAgBUJ/NwMoDAkLIAVCADcDKAwICyAFKAIkIQEgBSgCCCECIAUoAhwhBCAFKQMQIQMjAEFAaiIAJAAgACABNgI0IAAgAjYCMCAAIAQ2AiwgACADNwMgAkACfyMAQRBrIgEgACgCMDYCDCABKAIMKAIACwRAIABCfzcDOAwBCwJAIAApAyBQRQRAIAAoAjAtAA1BAXFFDQELIABCADcDOAwBCyAAQgA3AwggAEEAOgAbA0AgAC0AG0EBcQR/QQAFIAApAwggACkDIFQLQQFxBEAgACAAKQMgIAApAwh9NwMAIAAgACgCMCgCrEAgACgCLCAAKQMIp2ogACAAKAIwKAKoQCgCHBEBADYCHCAAKAIcQQJHBEAgACAAKQMAIAApAwh8NwMICwJAAkACQAJAIAAoAhxBAWsOAwACAQMLIAAoAjBBAToADQJAIAAoAjAtAAxBAXENAAsgACgCMCkDIEIAUwRAIAAoAjBBFEEAEBQgAEEBOgAbDAMLAkAgACgCMC0ADkEBcUUNACAAKAIwKQMgIAApAwhWDQAgACgCMEEBOgAPIAAoAjAgACgCMCkDIDcDGCAAKAIsIAAoAjBBKGogACgCMCkDGKcQGRogACAAKAIwKQMYNwM4DAYLIABBAToAGwwCCyAAKAIwLQAMQQFxBEAgAEEBOgAbDAILIAAgACgCNCAAKAIwQShqQoDAABAuIgM3AxAgA0IAUwRAIAAoAjAgACgCNBAXIABBAToAGwwCCwJAIAApAxBQBEAgACgCMEEBOgAMIAAoAjAoAqxAIAAoAjAoAqhAKAIYEQIAIAAoAjApAyBCAFMEQCAAKAIwQgA3AyALDAELAkAgACgCMCkDIEIAWQRAIAAoAjBBADoADgwBCyAAKAIwIAApAxA3AyALIAAoAjAoAqxAIAAoAjBBKGogACkDECAAKAIwKAKoQCgCFBEQABoLDAELAn8jAEEQayIBIAAoAjA2AgwgASgCDCgCAEULBEAgACgCMEEUQQAQFAsgAEEBOgAbCwwBCwsgACkDCEIAUgRAIAAoAjBBADoADiAAKAIwIgEgACkDCCABKQMYfDcDGCAAIAApAwg3AzgMAQsgAEF/QQACfyMAQRBrIgEgACgCMDYCDCABKAIMKAIACxusNwM4CyAAKQM4IQMgAEFAayQAIAUgAzcDKAwHCyAFKAIIKAKsQCAFKAIIKAKoQCgCEBEAAEEBcUUEQCAFQn83AygMBwsgBUIANwMoDAYLIAUgBSgCHDYCBAJAIAUoAggtABBBAXEEQCAFKAIILQANQQFxBEAgBSgCBCAFKAIILQAPQQFxBH9BAAUCfwJAIAUoAggoAhRBf0cEQCAFKAIIKAIUQX5HDQELQQgMAQsgBSgCCCgCFAtB//8DcQs7ATAgBSgCBCAFKAIIKQMYNwMgIAUoAgQiACAAKQMAQsgAhDcDAAwCCyAFKAIEIgAgACkDAEK3////D4M3AwAMAQsgBSgCBEEAOwEwIAUoAgQiACAAKQMAQsAAhDcDAAJAIAUoAggtAA1BAXEEQCAFKAIEIAUoAggpAxg3AxggBSgCBCIAIAApAwBCBIQ3AwAMAQsgBSgCBCIAIAApAwBC+////w+DNwMACwsgBUIANwMoDAULIAUgBSgCCC0AD0EBcQR/QQAFIAUoAggoAqxAIAUoAggoAqhAKAIIEQAAC6w3AygMBAsgBSAFKAIIIAUoAhwgBSkDEBBCNwMoDAMLIAUoAggQsQEgBUIANwMoDAILIAVBfzYCACAFQRAgBRA2Qj+ENwMoDAELIAUoAghBFEEAEBQgBUJ/NwMoCyAFKQMoIQMgBUEwaiQAIAMLPAEBfyMAQRBrIgMkACADIAA7AQ4gAyABNgIIIAMgAjYCBEEAIAMoAgggAygCBBC0ASEAIANBEGokACAAC46nAQEEfyMAQSBrIgUkACAFIAA2AhggBSABNgIUIAUgAjYCECAFIAUoAhg2AgwgBSgCDCAFKAIQKQMAQv////8PVgR+Qv////8PBSAFKAIQKQMACz4CICAFKAIMIAUoAhQ2AhwCQCAFKAIMLQAEQQFxBEAgBSgCDEEQaiEBQQRBACAFKAIMLQAMQQFxGyECIwBBQGoiACQAIAAgATYCOCAAIAI2AjQCQAJAAkAgACgCOBB5DQAgACgCNEEFSg0AIAAoAjRBAE4NAQsgAEF+NgI8DAELIAAgACgCOCgCHDYCLAJAAkAgACgCOCgCDEUNACAAKAI4KAIEBEAgACgCOCgCAEUNAQsgACgCLCgCBEGaBUcNASAAKAI0QQRGDQELIAAoAjhBsNkAKAIANgIYIABBfjYCPAwBCyAAKAI4KAIQRQRAIAAoAjhBvNkAKAIANgIYIABBezYCPAwBCyAAIAAoAiwoAig2AjAgACgCLCAAKAI0NgIoAkAgACgCLCgCFARAIAAoAjgQHCAAKAI4KAIQRQRAIAAoAixBfzYCKCAAQQA2AjwMAwsMAQsCQCAAKAI4KAIEDQAgACgCNEEBdEEJQQAgACgCNEEEShtrIAAoAjBBAXRBCUEAIAAoAjBBBEoba0oNACAAKAI0QQRGDQAgACgCOEG82QAoAgA2AhggAEF7NgI8DAILCwJAIAAoAiwoAgRBmgVHDQAgACgCOCgCBEUNACAAKAI4QbzZACgCADYCGCAAQXs2AjwMAQsgACgCLCgCBEEqRgRAIAAgACgCLCgCMEEEdEH4AGtBCHQ2AigCQAJAIAAoAiwoAogBQQJIBEAgACgCLCgChAFBAk4NAQsgAEEANgIkDAELAkAgACgCLCgChAFBBkgEQCAAQQE2AiQMAQsCQCAAKAIsKAKEAUEGRgRAIABBAjYCJAwBCyAAQQM2AiQLCwsgACAAKAIoIAAoAiRBBnRyNgIoIAAoAiwoAmwEQCAAIAAoAihBIHI2AigLIAAgACgCKEEfIAAoAihBH3BrajYCKCAAKAIsIAAoAigQTCAAKAIsKAJsBEAgACgCLCAAKAI4KAIwQRB2EEwgACgCLCAAKAI4KAIwQf//A3EQTAtBAEEAQQAQPiEBIAAoAjggATYCMCAAKAIsQfEANgIEIAAoAjgQHCAAKAIsKAIUBEAgACgCLEF/NgIoIABBADYCPAwCCwsgACgCLCgCBEE5RgRAQQBBAEEAEBohASAAKAI4IAE2AjAgACgCLCgCCCECIAAoAiwiAygCFCEBIAMgAUEBajYCFCABIAJqQR86AAAgACgCLCgCCCECIAAoAiwiAygCFCEBIAMgAUEBajYCFCABIAJqQYsBOgAAIAAoAiwoAgghAiAAKAIsIgMoAhQhASADIAFBAWo2AhQgASACakEIOgAAAkAgACgCLCgCHEUEQCAAKAIsKAIIIQIgACgCLCIDKAIUIQEgAyABQQFqNgIUIAEgAmpBADoAACAAKAIsKAIIIQIgACgCLCIDKAIUIQEgAyABQQFqNgIUIAEgAmpBADoAACAAKAIsKAIIIQIgACgCLCIDKAIUIQEgAyABQQFqNgIUIAEgAmpBADoAACAAKAIsKAIIIQIgACgCLCIDKAIUIQEgAyABQQFqNgIUIAEgAmpBADoAACAAKAIsKAIIIQIgACgCLCIDKAIUIQEgAyABQQFqNgIUIAEgAmpBADoAACAAKAIsKAKEAUEJRgR/QQIFQQRBACAAKAIsKAKIAUECSAR/IAAoAiwoAoQBQQJIBUEBC0EBcRsLIQIgACgCLCgCCCEDIAAoAiwiBCgCFCEBIAQgAUEBajYCFCABIANqIAI6AAAgACgCLCgCCCECIAAoAiwiAygCFCEBIAMgAUEBajYCFCABIAJqQQM6AAAgACgCLEHxADYCBCAAKAI4EBwgACgCLCgCFARAIAAoAixBfzYCKCAAQQA2AjwMBAsMAQsgACgCLCgCHCgCAEVFQQJBACAAKAIsKAIcKAIsG2pBBEEAIAAoAiwoAhwoAhAbakEIQQAgACgCLCgCHCgCHBtqQRBBACAAKAIsKAIcKAIkG2ohAiAAKAIsKAIIIQMgACgCLCIEKAIUIQEgBCABQQFqNgIUIAEgA2ogAjoAACAAKAIsKAIcKAIEQf8BcSECIAAoAiwoAgghAyAAKAIsIgQoAhQhASAEIAFBAWo2AhQgASADaiACOgAAIAAoAiwoAhwoAgRBCHZB/wFxIQIgACgCLCgCCCEDIAAoAiwiBCgCFCEBIAQgAUEBajYCFCABIANqIAI6AAAgACgCLCgCHCgCBEEQdkH/AXEhAiAAKAIsKAIIIQMgACgCLCIEKAIUIQEgBCABQQFqNgIUIAEgA2ogAjoAACAAKAIsKAIcKAIEQRh2IQIgACgCLCgCCCEDIAAoAiwiBCgCFCEBIAQgAUEBajYCFCABIANqIAI6AAAgACgCLCgChAFBCUYEf0ECBUEEQQAgACgCLCgCiAFBAkgEfyAAKAIsKAKEAUECSAVBAQtBAXEbCyECIAAoAiwoAgghAyAAKAIsIgQoAhQhASAEIAFBAWo2AhQgASADaiACOgAAIAAoAiwoAhwoAgxB/wFxIQIgACgCLCgCCCEDIAAoAiwiBCgCFCEBIAQgAUEBajYCFCABIANqIAI6AAAgACgCLCgCHCgCEARAIAAoAiwoAhwoAhRB/wFxIQIgACgCLCgCCCEDIAAoAiwiBCgCFCEBIAQgAUEBajYCFCABIANqIAI6AAAgACgCLCgCHCgCFEEIdkH/AXEhAiAAKAIsKAIIIQMgACgCLCIEKAIUIQEgBCABQQFqNgIUIAEgA2ogAjoAAAsgACgCLCgCHCgCLARAIAAoAjgoAjAgACgCLCgCCCAAKAIsKAIUEBohASAAKAI4IAE2AjALIAAoAixBADYCICAAKAIsQcUANgIECwsgACgCLCgCBEHFAEYEQCAAKAIsKAIcKAIQBEAgACAAKAIsKAIUNgIgIAAgACgCLCgCHCgCFEH//wNxIAAoAiwoAiBrNgIcA0AgACgCLCgCDCAAKAIsKAIUIAAoAhxqSQRAIAAgACgCLCgCDCAAKAIsKAIUazYCGCAAKAIsKAIIIAAoAiwoAhRqIAAoAiwoAhwoAhAgACgCLCgCIGogACgCGBAZGiAAKAIsIAAoAiwoAgw2AhQCQCAAKAIsKAIcKAIsRQ0AIAAoAiwoAhQgACgCIE0NACAAKAI4KAIwIAAoAiwoAgggACgCIGogACgCLCgCFCAAKAIgaxAaIQEgACgCOCABNgIwCyAAKAIsIgEgACgCGCABKAIgajYCICAAKAI4EBwgACgCLCgCFARAIAAoAixBfzYCKCAAQQA2AjwMBQUgAEEANgIgIAAgACgCHCAAKAIYazYCHAwCCwALCyAAKAIsKAIIIAAoAiwoAhRqIAAoAiwoAhwoAhAgACgCLCgCIGogACgCHBAZGiAAKAIsIgEgACgCHCABKAIUajYCFAJAIAAoAiwoAhwoAixFDQAgACgCLCgCFCAAKAIgTQ0AIAAoAjgoAjAgACgCLCgCCCAAKAIgaiAAKAIsKAIUIAAoAiBrEBohASAAKAI4IAE2AjALIAAoAixBADYCIAsgACgCLEHJADYCBAsgACgCLCgCBEHJAEYEQCAAKAIsKAIcKAIcBEAgACAAKAIsKAIUNgIUA0AgACgCLCgCFCAAKAIsKAIMRgRAAkAgACgCLCgCHCgCLEUNACAAKAIsKAIUIAAoAhRNDQAgACgCOCgCMCAAKAIsKAIIIAAoAhRqIAAoAiwoAhQgACgCFGsQGiEBIAAoAjggATYCMAsgACgCOBAcIAAoAiwoAhQEQCAAKAIsQX82AiggAEEANgI8DAULIABBADYCFAsgACgCLCgCHCgCHCECIAAoAiwiAygCICEBIAMgAUEBajYCICAAIAEgAmotAAA2AhAgACgCECECIAAoAiwoAgghAyAAKAIsIgQoAhQhASAEIAFBAWo2AhQgASADaiACOgAAIAAoAhANAAsCQCAAKAIsKAIcKAIsRQ0AIAAoAiwoAhQgACgCFE0NACAAKAI4KAIwIAAoAiwoAgggACgCFGogACgCLCgCFCAAKAIUaxAaIQEgACgCOCABNgIwCyAAKAIsQQA2AiALIAAoAixB2wA2AgQLIAAoAiwoAgRB2wBGBEAgACgCLCgCHCgCJARAIAAgACgCLCgCFDYCDANAIAAoAiwoAhQgACgCLCgCDEYEQAJAIAAoAiwoAhwoAixFDQAgACgCLCgCFCAAKAIMTQ0AIAAoAjgoAjAgACgCLCgCCCAAKAIMaiAAKAIsKAIUIAAoAgxrEBohASAAKAI4IAE2AjALIAAoAjgQHCAAKAIsKAIUBEAgACgCLEF/NgIoIABBADYCPAwFCyAAQQA2AgwLIAAoAiwoAhwoAiQhAiAAKAIsIgMoAiAhASADIAFBAWo2AiAgACABIAJqLQAANgIIIAAoAgghAiAAKAIsKAIIIQMgACgCLCIEKAIUIQEgBCABQQFqNgIUIAEgA2ogAjoAACAAKAIIDQALAkAgACgCLCgCHCgCLEUNACAAKAIsKAIUIAAoAgxNDQAgACgCOCgCMCAAKAIsKAIIIAAoAgxqIAAoAiwoAhQgACgCDGsQGiEBIAAoAjggATYCMAsLIAAoAixB5wA2AgQLIAAoAiwoAgRB5wBGBEAgACgCLCgCHCgCLARAIAAoAiwoAgwgACgCLCgCFEECakkEQCAAKAI4EBwgACgCLCgCFARAIAAoAixBfzYCKCAAQQA2AjwMBAsLIAAoAjgoAjBB/wFxIQIgACgCLCgCCCEDIAAoAiwiBCgCFCEBIAQgAUEBajYCFCABIANqIAI6AAAgACgCOCgCMEEIdkH/AXEhAiAAKAIsKAIIIQMgACgCLCIEKAIUIQEgBCABQQFqNgIUIAEgA2ogAjoAAEEAQQBBABAaIQEgACgCOCABNgIwCyAAKAIsQfEANgIEIAAoAjgQHCAAKAIsKAIUBEAgACgCLEF/NgIoIABBADYCPAwCCwsCQAJAIAAoAjgoAgQNACAAKAIsKAJ0DQAgACgCNEUNASAAKAIsKAIEQZoFRg0BCyAAAn8gACgCLCgChAFFBEAgACgCLCAAKAI0ELYBDAELAn8gACgCLCgCiAFBAkYEQCAAKAIsIQIgACgCNCEDIwBBIGsiASQAIAEgAjYCGCABIAM2AhQCQANAAkAgASgCGCgCdEUEQCABKAIYEFsgASgCGCgCdEUEQCABKAIURQRAIAFBADYCHAwFCwwCCwsgASgCGEEANgJgIAEgASgCGCICKAI4IAIoAmxqLQAAOgAPIAEoAhgiAigCpC0gAigCoC1BAXRqQQA7AQAgAS0ADyEDIAEoAhgiAigCmC0hBCACIAIoAqAtIgJBAWo2AqAtIAIgBGogAzoAACABKAIYIAEtAA9BAnRqIgIgAi8BlAFBAWo7AZQBIAEgASgCGCgCoC0gASgCGCgCnC1BAWtGNgIQIAEoAhgiAiACKAJ0QQFrNgJ0IAEoAhgiAiACKAJsQQFqNgJsIAEoAhAEQCABKAIYAn8gASgCGCgCXEEATgRAIAEoAhgoAjggASgCGCgCXGoMAQtBAAsgASgCGCgCbCABKAIYKAJca0EAECggASgCGCABKAIYKAJsNgJcIAEoAhgoAgAQHCABKAIYKAIAKAIQRQRAIAFBADYCHAwECwsMAQsLIAEoAhhBADYCtC0gASgCFEEERgRAIAEoAhgCfyABKAIYKAJcQQBOBEAgASgCGCgCOCABKAIYKAJcagwBC0EACyABKAIYKAJsIAEoAhgoAlxrQQEQKCABKAIYIAEoAhgoAmw2AlwgASgCGCgCABAcIAEoAhgoAgAoAhBFBEAgAUECNgIcDAILIAFBAzYCHAwBCyABKAIYKAKgLQRAIAEoAhgCfyABKAIYKAJcQQBOBEAgASgCGCgCOCABKAIYKAJcagwBC0EACyABKAIYKAJsIAEoAhgoAlxrQQAQKCABKAIYIAEoAhgoAmw2AlwgASgCGCgCABAcIAEoAhgoAgAoAhBFBEAgAUEANgIcDAILCyABQQE2AhwLIAEoAhwhAiABQSBqJAAgAgwBCwJ/IAAoAiwoAogBQQNGBEAgACgCLCECIAAoAjQhAyMAQTBrIgEkACABIAI2AiggASADNgIkAkADQAJAIAEoAigoAnRBggJNBEAgASgCKBBbAkAgASgCKCgCdEGCAksNACABKAIkDQAgAUEANgIsDAQLIAEoAigoAnRFDQELIAEoAihBADYCYAJAIAEoAigoAnRBA0kNACABKAIoKAJsRQ0AIAEgASgCKCgCOCABKAIoKAJsakEBazYCGCABIAEoAhgtAAA2AhwgASgCHCECIAEgASgCGCIDQQFqNgIYAkAgAy0AASACRw0AIAEoAhwhAiABIAEoAhgiA0EBajYCGCADLQABIAJHDQAgASgCHCECIAEgASgCGCIDQQFqNgIYIAMtAAEgAkcNACABIAEoAigoAjggASgCKCgCbGpBggJqNgIUA0AgASgCHCECIAEgASgCGCIDQQFqNgIYAn9BACADLQABIAJHDQAaIAEoAhwhAiABIAEoAhgiA0EBajYCGEEAIAMtAAEgAkcNABogASgCHCECIAEgASgCGCIDQQFqNgIYQQAgAy0AASACRw0AGiABKAIcIQIgASABKAIYIgNBAWo2AhhBACADLQABIAJHDQAaIAEoAhwhAiABIAEoAhgiA0EBajYCGEEAIAMtAAEgAkcNABogASgCHCECIAEgASgCGCIDQQFqNgIYQQAgAy0AASACRw0AGiABKAIcIQIgASABKAIYIgNBAWo2AhhBACADLQABIAJHDQAaIAEoAhwhAiABIAEoAhgiA0EBajYCGEEAIAMtAAEgAkcNABogASgCGCABKAIUSQtBAXENAAsgASgCKEGCAiABKAIUIAEoAhhrazYCYCABKAIoKAJgIAEoAigoAnRLBEAgASgCKCABKAIoKAJ0NgJgCwsLAkAgASgCKCgCYEEDTwRAIAEgASgCKCgCYEEDazoAEyABQQE7ARAgASgCKCICKAKkLSACKAKgLUEBdGogAS8BEDsBACABLQATIQMgASgCKCICKAKYLSEEIAIgAigCoC0iAkEBajYCoC0gAiAEaiADOgAAIAEgAS8BEEEBazsBECABKAIoIAEtABNB0N0Aai0AAEECdGpBmAlqIgIgAi8BAEEBajsBACABKAIoQYgTagJ/IAEvARBBgAJJBEAgAS8BEC0A0FkMAQsgAS8BEEEHdkGAAmotANBZC0ECdGoiAiACLwEAQQFqOwEAIAEgASgCKCgCoC0gASgCKCgCnC1BAWtGNgIgIAEoAigiAiACKAJ0IAEoAigoAmBrNgJ0IAEoAigiAiABKAIoKAJgIAIoAmxqNgJsIAEoAihBADYCYAwBCyABIAEoAigiAigCOCACKAJsai0AADoADyABKAIoIgIoAqQtIAIoAqAtQQF0akEAOwEAIAEtAA8hAyABKAIoIgIoApgtIQQgAiACKAKgLSICQQFqNgKgLSACIARqIAM6AAAgASgCKCABLQAPQQJ0aiICIAIvAZQBQQFqOwGUASABIAEoAigoAqAtIAEoAigoApwtQQFrRjYCICABKAIoIgIgAigCdEEBazYCdCABKAIoIgIgAigCbEEBajYCbAsgASgCIARAIAEoAigCfyABKAIoKAJcQQBOBEAgASgCKCgCOCABKAIoKAJcagwBC0EACyABKAIoKAJsIAEoAigoAlxrQQAQKCABKAIoIAEoAigoAmw2AlwgASgCKCgCABAcIAEoAigoAgAoAhBFBEAgAUEANgIsDAQLCwwBCwsgASgCKEEANgK0LSABKAIkQQRGBEAgASgCKAJ/IAEoAigoAlxBAE4EQCABKAIoKAI4IAEoAigoAlxqDAELQQALIAEoAigoAmwgASgCKCgCXGtBARAoIAEoAiggASgCKCgCbDYCXCABKAIoKAIAEBwgASgCKCgCACgCEEUEQCABQQI2AiwMAgsgAUEDNgIsDAELIAEoAigoAqAtBEAgASgCKAJ/IAEoAigoAlxBAE4EQCABKAIoKAI4IAEoAigoAlxqDAELQQALIAEoAigoAmwgASgCKCgCXGtBABAoIAEoAiggASgCKCgCbDYCXCABKAIoKAIAEBwgASgCKCgCACgCEEUEQCABQQA2AiwMAgsLIAFBATYCLAsgASgCLCECIAFBMGokACACDAELIAAoAiwgACgCNCAAKAIsKAKEAUEMbEGA7wBqKAIIEQMACwsLNgIEAkAgACgCBEECRwRAIAAoAgRBA0cNAQsgACgCLEGaBTYCBAsCQCAAKAIEBEAgACgCBEECRw0BCyAAKAI4KAIQRQRAIAAoAixBfzYCKAsgAEEANgI8DAILIAAoAgRBAUYEQAJAIAAoAjRBAUYEQCAAKAIsIQIjAEEgayIBJAAgASACNgIcIAFBAzYCGAJAIAEoAhwoArwtQRAgASgCGGtKBEAgAUECNgIUIAEoAhwiAiACLwG4LSABKAIUQf//A3EgASgCHCgCvC10cjsBuC0gASgCHC8BuC1B/wFxIQMgASgCHCgCCCEEIAEoAhwiBigCFCECIAYgAkEBajYCFCACIARqIAM6AAAgASgCHC8BuC1BCHYhAyABKAIcKAIIIQQgASgCHCIGKAIUIQIgBiACQQFqNgIUIAIgBGogAzoAACABKAIcIAEoAhRB//8DcUEQIAEoAhwoArwta3U7AbgtIAEoAhwiAiACKAK8LSABKAIYQRBrajYCvC0MAQsgASgCHCICIAIvAbgtQQIgASgCHCgCvC10cjsBuC0gASgCHCICIAEoAhggAigCvC1qNgK8LQsgAUGS6AAvAQA2AhACQCABKAIcKAK8LUEQIAEoAhBrSgRAIAFBkOgALwEANgIMIAEoAhwiAiACLwG4LSABKAIMQf//A3EgASgCHCgCvC10cjsBuC0gASgCHC8BuC1B/wFxIQMgASgCHCgCCCEEIAEoAhwiBigCFCECIAYgAkEBajYCFCACIARqIAM6AAAgASgCHC8BuC1BCHYhAyABKAIcKAIIIQQgASgCHCIGKAIUIQIgBiACQQFqNgIUIAIgBGogAzoAACABKAIcIAEoAgxB//8DcUEQIAEoAhwoArwta3U7AbgtIAEoAhwiAiACKAK8LSABKAIQQRBrajYCvC0MAQsgASgCHCICIAIvAbgtQZDoAC8BACABKAIcKAK8LXRyOwG4LSABKAIcIgIgASgCECACKAK8LWo2ArwtCyABKAIcELsBIAFBIGokAAwBCyAAKAI0QQVHBEAgACgCLEEAQQBBABBcIAAoAjRBA0YEQCAAKAIsKAJEIAAoAiwoAkxBAWtBAXRqQQA7AQAgACgCLCgCREEAIAAoAiwoAkxBAWtBAXQQMiAAKAIsKAJ0RQRAIAAoAixBADYCbCAAKAIsQQA2AlwgACgCLEEANgK0LQsLCwsgACgCOBAcIAAoAjgoAhBFBEAgACgCLEF/NgIoIABBADYCPAwDCwsLIAAoAjRBBEcEQCAAQQA2AjwMAQsgACgCLCgCGEEATARAIABBATYCPAwBCwJAIAAoAiwoAhhBAkYEQCAAKAI4KAIwQf8BcSECIAAoAiwoAgghAyAAKAIsIgQoAhQhASAEIAFBAWo2AhQgASADaiACOgAAIAAoAjgoAjBBCHZB/wFxIQIgACgCLCgCCCEDIAAoAiwiBCgCFCEBIAQgAUEBajYCFCABIANqIAI6AAAgACgCOCgCMEEQdkH/AXEhAiAAKAIsKAIIIQMgACgCLCIEKAIUIQEgBCABQQFqNgIUIAEgA2ogAjoAACAAKAI4KAIwQRh2IQIgACgCLCgCCCEDIAAoAiwiBCgCFCEBIAQgAUEBajYCFCABIANqIAI6AAAgACgCOCgCCEH/AXEhAiAAKAIsKAIIIQMgACgCLCIEKAIUIQEgBCABQQFqNgIUIAEgA2ogAjoAACAAKAI4KAIIQQh2Qf8BcSECIAAoAiwoAgghAyAAKAIsIgQoAhQhASAEIAFBAWo2AhQgASADaiACOgAAIAAoAjgoAghBEHZB/wFxIQIgACgCLCgCCCEDIAAoAiwiBCgCFCEBIAQgAUEBajYCFCABIANqIAI6AAAgACgCOCgCCEEYdiECIAAoAiwoAgghAyAAKAIsIgQoAhQhASAEIAFBAWo2AhQgASADaiACOgAADAELIAAoAiwgACgCOCgCMEEQdhBMIAAoAiwgACgCOCgCMEH//wNxEEwLIAAoAjgQHCAAKAIsKAIYQQBKBEAgACgCLEEAIAAoAiwoAhhrNgIYCyAAIAAoAiwoAhRFNgI8CyAAKAI8IQEgAEFAayQAIAUgATYCCAwBCyAFKAIMQRBqIQEjAEHgAGsiACQAIAAgATYCWCAAQQI2AlQCQAJAAkAgACgCWBBLDQAgACgCWCgCDEUNACAAKAJYKAIADQEgACgCWCgCBEUNAQsgAEF+NgJcDAELIAAgACgCWCgCHDYCUCAAKAJQKAIEQb/+AEYEQCAAKAJQQcD+ADYCBAsgACAAKAJYKAIMNgJIIAAgACgCWCgCEDYCQCAAIAAoAlgoAgA2AkwgACAAKAJYKAIENgJEIAAgACgCUCgCPDYCPCAAIAAoAlAoAkA2AjggACAAKAJENgI0IAAgACgCQDYCMCAAQQA2AhADQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAAKAJQKAIEQbT+AGsOHwABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fCyAAKAJQKAIMRQRAIAAoAlBBwP4ANgIEDCELA0AgACgCOEEQSQRAIAAoAkRFDSEgACAAKAJEQQFrNgJEIAAgACgCTCIBQQFqNgJMIAAgACgCPCABLQAAIAAoAjh0ajYCPCAAIAAoAjhBCGo2AjgMAQsLAkAgACgCUCgCDEECcUUNACAAKAI8QZ+WAkcNACAAKAJQKAIoRQRAIAAoAlBBDzYCKAtBAEEAQQAQGiEBIAAoAlAgATYCHCAAIAAoAjw6AAwgACAAKAI8QQh2OgANIAAoAlAoAhwgAEEMakECEBohASAAKAJQIAE2AhwgAEEANgI8IABBADYCOCAAKAJQQbX+ADYCBAwhCyAAKAJQQQA2AhQgACgCUCgCJARAIAAoAlAoAiRBfzYCMAsCQCAAKAJQKAIMQQFxBEAgACgCPEH/AXFBCHQgACgCPEEIdmpBH3BFDQELIAAoAlhBmgw2AhggACgCUEHR/gA2AgQMIQsgACgCPEEPcUEIRwRAIAAoAlhBmw82AhggACgCUEHR/gA2AgQMIQsgACAAKAI8QQR2NgI8IAAgACgCOEEEazYCOCAAIAAoAjxBD3FBCGo2AhQgACgCUCgCKEUEQCAAKAJQIAAoAhQ2AigLAkAgACgCFEEPTQRAIAAoAhQgACgCUCgCKE0NAQsgACgCWEGTDTYCGCAAKAJQQdH+ADYCBAwhCyAAKAJQQQEgACgCFHQ2AhhBAEEAQQAQPiEBIAAoAlAgATYCHCAAKAJYIAE2AjAgACgCUEG9/gBBv/4AIAAoAjxBgARxGzYCBCAAQQA2AjwgAEEANgI4DCALA0AgACgCOEEQSQRAIAAoAkRFDSAgACAAKAJEQQFrNgJEIAAgACgCTCIBQQFqNgJMIAAgACgCPCABLQAAIAAoAjh0ajYCPCAAIAAoAjhBCGo2AjgMAQsLIAAoAlAgACgCPDYCFCAAKAJQKAIUQf8BcUEIRwRAIAAoAlhBmw82AhggACgCUEHR/gA2AgQMIAsgACgCUCgCFEGAwANxBEAgACgCWEGgCTYCGCAAKAJQQdH+ADYCBAwgCyAAKAJQKAIkBEAgACgCUCgCJCAAKAI8QQh2QQFxNgIACwJAIAAoAlAoAhRBgARxRQ0AIAAoAlAoAgxBBHFFDQAgACAAKAI8OgAMIAAgACgCPEEIdjoADSAAKAJQKAIcIABBDGpBAhAaIQEgACgCUCABNgIcCyAAQQA2AjwgAEEANgI4IAAoAlBBtv4ANgIECwNAIAAoAjhBIEkEQCAAKAJERQ0fIAAgACgCREEBazYCRCAAIAAoAkwiAUEBajYCTCAAIAAoAjwgAS0AACAAKAI4dGo2AjwgACAAKAI4QQhqNgI4DAELCyAAKAJQKAIkBEAgACgCUCgCJCAAKAI8NgIECwJAIAAoAlAoAhRBgARxRQ0AIAAoAlAoAgxBBHFFDQAgACAAKAI8OgAMIAAgACgCPEEIdjoADSAAIAAoAjxBEHY6AA4gACAAKAI8QRh2OgAPIAAoAlAoAhwgAEEMakEEEBohASAAKAJQIAE2AhwLIABBADYCPCAAQQA2AjggACgCUEG3/gA2AgQLA0AgACgCOEEQSQRAIAAoAkRFDR4gACAAKAJEQQFrNgJEIAAgACgCTCIBQQFqNgJMIAAgACgCPCABLQAAIAAoAjh0ajYCPCAAIAAoAjhBCGo2AjgMAQsLIAAoAlAoAiQEQCAAKAJQKAIkIAAoAjxB/wFxNgIIIAAoAlAoAiQgACgCPEEIdjYCDAsCQCAAKAJQKAIUQYAEcUUNACAAKAJQKAIMQQRxRQ0AIAAgACgCPDoADCAAIAAoAjxBCHY6AA0gACgCUCgCHCAAQQxqQQIQGiEBIAAoAlAgATYCHAsgAEEANgI8IABBADYCOCAAKAJQQbj+ADYCBAsCQCAAKAJQKAIUQYAIcQRAA0AgACgCOEEQSQRAIAAoAkRFDR8gACAAKAJEQQFrNgJEIAAgACgCTCIBQQFqNgJMIAAgACgCPCABLQAAIAAoAjh0ajYCPCAAIAAoAjhBCGo2AjgMAQsLIAAoAlAgACgCPDYCRCAAKAJQKAIkBEAgACgCUCgCJCAAKAI8NgIUCwJAIAAoAlAoAhRBgARxRQ0AIAAoAlAoAgxBBHFFDQAgACAAKAI8OgAMIAAgACgCPEEIdjoADSAAKAJQKAIcIABBDGpBAhAaIQEgACgCUCABNgIcCyAAQQA2AjwgAEEANgI4DAELIAAoAlAoAiQEQCAAKAJQKAIkQQA2AhALCyAAKAJQQbn+ADYCBAsgACgCUCgCFEGACHEEQCAAIAAoAlAoAkQ2AiwgACgCLCAAKAJESwRAIAAgACgCRDYCLAsgACgCLARAAkAgACgCUCgCJEUNACAAKAJQKAIkKAIQRQ0AIAAgACgCUCgCJCgCFCAAKAJQKAJEazYCFCAAKAJQKAIkKAIQIAAoAhRqIAAoAkwCfyAAKAJQKAIkKAIYIAAoAhQgACgCLGpJBEAgACgCUCgCJCgCGCAAKAIUawwBCyAAKAIsCxAZGgsCQCAAKAJQKAIUQYAEcUUNACAAKAJQKAIMQQRxRQ0AIAAoAlAoAhwgACgCTCAAKAIsEBohASAAKAJQIAE2AhwLIAAgACgCRCAAKAIsazYCRCAAIAAoAiwgACgCTGo2AkwgACgCUCIBIAEoAkQgACgCLGs2AkQLIAAoAlAoAkQNGwsgACgCUEEANgJEIAAoAlBBuv4ANgIECwJAIAAoAlAoAhRBgBBxBEAgACgCREUNGyAAQQA2AiwDQCAAKAJMIQEgACAAKAIsIgJBAWo2AiwgACABIAJqLQAANgIUAkAgACgCUCgCJEUNACAAKAJQKAIkKAIcRQ0AIAAoAlAoAkQgACgCUCgCJCgCIE8NACAAKAIUIQIgACgCUCgCJCgCHCEDIAAoAlAiBCgCRCEBIAQgAUEBajYCRCABIANqIAI6AAALIAAoAhQEfyAAKAIsIAAoAkRJBUEAC0EBcQ0ACwJAIAAoAlAoAhRBgARxRQ0AIAAoAlAoAgxBBHFFDQAgACgCUCgCHCAAKAJMIAAoAiwQGiEBIAAoAlAgATYCHAsgACAAKAJEIAAoAixrNgJEIAAgACgCLCAAKAJMajYCTCAAKAIUDRsMAQsgACgCUCgCJARAIAAoAlAoAiRBADYCHAsLIAAoAlBBADYCRCAAKAJQQbv+ADYCBAsCQCAAKAJQKAIUQYAgcQRAIAAoAkRFDRogAEEANgIsA0AgACgCTCEBIAAgACgCLCICQQFqNgIsIAAgASACai0AADYCFAJAIAAoAlAoAiRFDQAgACgCUCgCJCgCJEUNACAAKAJQKAJEIAAoAlAoAiQoAihPDQAgACgCFCECIAAoAlAoAiQoAiQhAyAAKAJQIgQoAkQhASAEIAFBAWo2AkQgASADaiACOgAACyAAKAIUBH8gACgCLCAAKAJESQVBAAtBAXENAAsCQCAAKAJQKAIUQYAEcUUNACAAKAJQKAIMQQRxRQ0AIAAoAlAoAhwgACgCTCAAKAIsEBohASAAKAJQIAE2AhwLIAAgACgCRCAAKAIsazYCRCAAIAAoAiwgACgCTGo2AkwgACgCFA0aDAELIAAoAlAoAiQEQCAAKAJQKAIkQQA2AiQLCyAAKAJQQbz+ADYCBAsgACgCUCgCFEGABHEEQANAIAAoAjhBEEkEQCAAKAJERQ0aIAAgACgCREEBazYCRCAAIAAoAkwiAUEBajYCTCAAIAAoAjwgAS0AACAAKAI4dGo2AjwgACAAKAI4QQhqNgI4DAELCwJAIAAoAlAoAgxBBHFFDQAgACgCPCAAKAJQKAIcQf//A3FGDQAgACgCWEH7DDYCGCAAKAJQQdH+ADYCBAwaCyAAQQA2AjwgAEEANgI4CyAAKAJQKAIkBEAgACgCUCgCJCAAKAJQKAIUQQl1QQFxNgIsIAAoAlAoAiRBATYCMAtBAEEAQQAQGiEBIAAoAlAgATYCHCAAKAJYIAE2AjAgACgCUEG//gA2AgQMGAsDQCAAKAI4QSBJBEAgACgCREUNGCAAIAAoAkRBAWs2AkQgACAAKAJMIgFBAWo2AkwgACAAKAI8IAEtAAAgACgCOHRqNgI8IAAgACgCOEEIajYCOAwBCwsgACgCUCAAKAI8QQh2QYD+A3EgACgCPEEYdmogACgCPEGA/gNxQQh0aiAAKAI8Qf8BcUEYdGoiATYCHCAAKAJYIAE2AjAgAEEANgI8IABBADYCOCAAKAJQQb7+ADYCBAsgACgCUCgCEEUEQCAAKAJYIAAoAkg2AgwgACgCWCAAKAJANgIQIAAoAlggACgCTDYCACAAKAJYIAAoAkQ2AgQgACgCUCAAKAI8NgI8IAAoAlAgACgCODYCQCAAQQI2AlwMGAtBAEEAQQAQPiEBIAAoAlAgATYCHCAAKAJYIAE2AjAgACgCUEG//gA2AgQLIAAoAlRBBUYNFCAAKAJUQQZGDRQLIAAoAlAoAggEQCAAIAAoAjwgACgCOEEHcXY2AjwgACAAKAI4IAAoAjhBB3FrNgI4IAAoAlBBzv4ANgIEDBULA0AgACgCOEEDSQRAIAAoAkRFDRUgACAAKAJEQQFrNgJEIAAgACgCTCIBQQFqNgJMIAAgACgCPCABLQAAIAAoAjh0ajYCPCAAIAAoAjhBCGo2AjgMAQsLIAAoAlAgACgCPEEBcTYCCCAAIAAoAjxBAXY2AjwgACAAKAI4QQFrNgI4AkACQAJAAkACQCAAKAI8QQNxDgQAAQIDBAsgACgCUEHB/gA2AgQMAwsjAEEQayIBIAAoAlA2AgwgASgCDEGw8gA2AlAgASgCDEEJNgJYIAEoAgxBsIIBNgJUIAEoAgxBBTYCXCAAKAJQQcf+ADYCBCAAKAJUQQZGBEAgACAAKAI8QQJ2NgI8IAAgACgCOEECazYCOAwXCwwCCyAAKAJQQcT+ADYCBAwBCyAAKAJYQfANNgIYIAAoAlBB0f4ANgIECyAAIAAoAjxBAnY2AjwgACAAKAI4QQJrNgI4DBQLIAAgACgCPCAAKAI4QQdxdjYCPCAAIAAoAjggACgCOEEHcWs2AjgDQCAAKAI4QSBJBEAgACgCREUNFCAAIAAoAkRBAWs2AkQgACAAKAJMIgFBAWo2AkwgACAAKAI8IAEtAAAgACgCOHRqNgI8IAAgACgCOEEIajYCOAwBCwsgACgCPEH//wNxIAAoAjxBEHZB//8Dc0cEQCAAKAJYQaEKNgIYIAAoAlBB0f4ANgIEDBQLIAAoAlAgACgCPEH//wNxNgJEIABBADYCPCAAQQA2AjggACgCUEHC/gA2AgQgACgCVEEGRg0SCyAAKAJQQcP+ADYCBAsgACAAKAJQKAJENgIsIAAoAiwEQCAAKAIsIAAoAkRLBEAgACAAKAJENgIsCyAAKAIsIAAoAkBLBEAgACAAKAJANgIsCyAAKAIsRQ0RIAAoAkggACgCTCAAKAIsEBkaIAAgACgCRCAAKAIsazYCRCAAIAAoAiwgACgCTGo2AkwgACAAKAJAIAAoAixrNgJAIAAgACgCLCAAKAJIajYCSCAAKAJQIgEgASgCRCAAKAIsazYCRAwSCyAAKAJQQb/+ADYCBAwRCwNAIAAoAjhBDkkEQCAAKAJERQ0RIAAgACgCREEBazYCRCAAIAAoAkwiAUEBajYCTCAAIAAoAjwgAS0AACAAKAI4dGo2AjwgACAAKAI4QQhqNgI4DAELCyAAKAJQIAAoAjxBH3FBgQJqNgJkIAAgACgCPEEFdjYCPCAAIAAoAjhBBWs2AjggACgCUCAAKAI8QR9xQQFqNgJoIAAgACgCPEEFdjYCPCAAIAAoAjhBBWs2AjggACgCUCAAKAI8QQ9xQQRqNgJgIAAgACgCPEEEdjYCPCAAIAAoAjhBBGs2AjgCQCAAKAJQKAJkQZ4CTQRAIAAoAlAoAmhBHk0NAQsgACgCWEH9CTYCGCAAKAJQQdH+ADYCBAwRCyAAKAJQQQA2AmwgACgCUEHF/gA2AgQLA0AgACgCUCgCbCAAKAJQKAJgSQRAA0AgACgCOEEDSQRAIAAoAkRFDRIgACAAKAJEQQFrNgJEIAAgACgCTCIBQQFqNgJMIAAgACgCPCABLQAAIAAoAjh0ajYCPCAAIAAoAjhBCGo2AjgMAQsLIAAoAjxBB3EhAiAAKAJQQfQAaiEDIAAoAlAiBCgCbCEBIAQgAUEBajYCbCABQQF0QYDyAGovAQBBAXQgA2ogAjsBACAAIAAoAjxBA3Y2AjwgACAAKAI4QQNrNgI4DAELCwNAIAAoAlAoAmxBE0kEQCAAKAJQQfQAaiECIAAoAlAiAygCbCEBIAMgAUEBajYCbCABQQF0QYDyAGovAQBBAXQgAmpBADsBAAwBCwsgACgCUCAAKAJQQbQKajYCcCAAKAJQIAAoAlAoAnA2AlAgACgCUEEHNgJYIABBACAAKAJQQfQAakETIAAoAlBB8ABqIAAoAlBB2ABqIAAoAlBB9AVqEHc2AhAgACgCEARAIAAoAlhBhwk2AhggACgCUEHR/gA2AgQMEAsgACgCUEEANgJsIAAoAlBBxv4ANgIECwNAAkAgACgCUCgCbCAAKAJQKAJkIAAoAlAoAmhqTw0AA0ACQCAAIAAoAlAoAlAgACgCPEEBIAAoAlAoAlh0QQFrcUECdGooAQA2ASAgAC0AISAAKAI4TQ0AIAAoAkRFDREgACAAKAJEQQFrNgJEIAAgACgCTCIBQQFqNgJMIAAgACgCPCABLQAAIAAoAjh0ajYCPCAAIAAoAjhBCGo2AjgMAQsLAkAgAC8BIkEQSQRAIAAgACgCPCAALQAhdjYCPCAAIAAoAjggAC0AIWs2AjggAC8BIiECIAAoAlBB9ABqIQMgACgCUCIEKAJsIQEgBCABQQFqNgJsIAFBAXQgA2ogAjsBAAwBCwJAIAAvASJBEEYEQANAIAAoAjggAC0AIUECakkEQCAAKAJERQ0UIAAgACgCREEBazYCRCAAIAAoAkwiAUEBajYCTCAAIAAoAjwgAS0AACAAKAI4dGo2AjwgACAAKAI4QQhqNgI4DAELCyAAIAAoAjwgAC0AIXY2AjwgACAAKAI4IAAtACFrNgI4IAAoAlAoAmxFBEAgACgCWEHPCTYCGCAAKAJQQdH+ADYCBAwECyAAIAAoAlAgACgCUCgCbEEBdGovAXI2AhQgACAAKAI8QQNxQQNqNgIsIAAgACgCPEECdjYCPCAAIAAoAjhBAms2AjgMAQsCQCAALwEiQRFGBEADQCAAKAI4IAAtACFBA2pJBEAgACgCREUNFSAAIAAoAkRBAWs2AkQgACAAKAJMIgFBAWo2AkwgACAAKAI8IAEtAAAgACgCOHRqNgI8IAAgACgCOEEIajYCOAwBCwsgACAAKAI8IAAtACF2NgI8IAAgACgCOCAALQAhazYCOCAAQQA2AhQgACAAKAI8QQdxQQNqNgIsIAAgACgCPEEDdjYCPCAAIAAoAjhBA2s2AjgMAQsDQCAAKAI4IAAtACFBB2pJBEAgACgCREUNFCAAIAAoAkRBAWs2AkQgACAAKAJMIgFBAWo2AkwgACAAKAI8IAEtAAAgACgCOHRqNgI8IAAgACgCOEEIajYCOAwBCwsgACAAKAI8IAAtACF2NgI8IAAgACgCOCAALQAhazYCOCAAQQA2AhQgACAAKAI8Qf8AcUELajYCLCAAIAAoAjxBB3Y2AjwgACAAKAI4QQdrNgI4CwsgACgCUCgCbCAAKAIsaiAAKAJQKAJkIAAoAlAoAmhqSwRAIAAoAlhBzwk2AhggACgCUEHR/gA2AgQMAgsDQCAAIAAoAiwiAUEBazYCLCABBEAgACgCFCECIAAoAlBB9ABqIQMgACgCUCIEKAJsIQEgBCABQQFqNgJsIAFBAXQgA2ogAjsBAAwBCwsLDAELCyAAKAJQKAIEQdH+AEYNDiAAKAJQLwH0BEUEQCAAKAJYQfULNgIYIAAoAlBB0f4ANgIEDA8LIAAoAlAgACgCUEG0Cmo2AnAgACgCUCAAKAJQKAJwNgJQIAAoAlBBCTYCWCAAQQEgACgCUEH0AGogACgCUCgCZCAAKAJQQfAAaiAAKAJQQdgAaiAAKAJQQfQFahB3NgIQIAAoAhAEQCAAKAJYQesINgIYIAAoAlBB0f4ANgIEDA8LIAAoAlAgACgCUCgCcDYCVCAAKAJQQQY2AlwgAEECIAAoAlBB9ABqIAAoAlAoAmRBAXRqIAAoAlAoAmggACgCUEHwAGogACgCUEHcAGogACgCUEH0BWoQdzYCECAAKAIQBEAgACgCWEG5CTYCGCAAKAJQQdH+ADYCBAwPCyAAKAJQQcf+ADYCBCAAKAJUQQZGDQ0LIAAoAlBByP4ANgIECwJAIAAoAkRBBkkNACAAKAJAQYICSQ0AIAAoAlggACgCSDYCDCAAKAJYIAAoAkA2AhAgACgCWCAAKAJMNgIAIAAoAlggACgCRDYCBCAAKAJQIAAoAjw2AjwgACgCUCAAKAI4NgJAIAAoAjAhAiMAQeAAayIBIAAoAlg2AlwgASACNgJYIAEgASgCXCgCHDYCVCABIAEoAlwoAgA2AlAgASABKAJQIAEoAlwoAgRBBWtqNgJMIAEgASgCXCgCDDYCSCABIAEoAkggASgCWCABKAJcKAIQa2s2AkQgASABKAJIIAEoAlwoAhBBgQJrajYCQCABIAEoAlQoAiw2AjwgASABKAJUKAIwNgI4IAEgASgCVCgCNDYCNCABIAEoAlQoAjg2AjAgASABKAJUKAI8NgIsIAEgASgCVCgCQDYCKCABIAEoAlQoAlA2AiQgASABKAJUKAJUNgIgIAFBASABKAJUKAJYdEEBazYCHCABQQEgASgCVCgCXHRBAWs2AhgDQCABKAIoQQ9JBEAgASABKAJQIgJBAWo2AlAgASABKAIsIAItAAAgASgCKHRqNgIsIAEgASgCKEEIajYCKCABIAEoAlAiAkEBajYCUCABIAEoAiwgAi0AACABKAIodGo2AiwgASABKAIoQQhqNgIoCyABIAEoAiQgASgCLCABKAIccUECdGooAQA2ARACQAJAA0AgASABLQARNgIMIAEgASgCLCABKAIMdjYCLCABIAEoAiggASgCDGs2AiggASABLQAQNgIMIAEoAgxFBEAgAS8BEiECIAEgASgCSCIDQQFqNgJIIAMgAjoAAAwCCyABKAIMQRBxBEAgASABLwESNgIIIAEgASgCDEEPcTYCDCABKAIMBEAgASgCKCABKAIMSQRAIAEgASgCUCICQQFqNgJQIAEgASgCLCACLQAAIAEoAih0ajYCLCABIAEoAihBCGo2AigLIAEgASgCCCABKAIsQQEgASgCDHRBAWtxajYCCCABIAEoAiwgASgCDHY2AiwgASABKAIoIAEoAgxrNgIoCyABKAIoQQ9JBEAgASABKAJQIgJBAWo2AlAgASABKAIsIAItAAAgASgCKHRqNgIsIAEgASgCKEEIajYCKCABIAEoAlAiAkEBajYCUCABIAEoAiwgAi0AACABKAIodGo2AiwgASABKAIoQQhqNgIoCyABIAEoAiAgASgCLCABKAIYcUECdGooAQA2ARACQANAIAEgAS0AETYCDCABIAEoAiwgASgCDHY2AiwgASABKAIoIAEoAgxrNgIoIAEgAS0AEDYCDCABKAIMQRBxBEAgASABLwESNgIEIAEgASgCDEEPcTYCDCABKAIoIAEoAgxJBEAgASABKAJQIgJBAWo2AlAgASABKAIsIAItAAAgASgCKHRqNgIsIAEgASgCKEEIajYCKCABKAIoIAEoAgxJBEAgASABKAJQIgJBAWo2AlAgASABKAIsIAItAAAgASgCKHRqNgIsIAEgASgCKEEIajYCKAsLIAEgASgCBCABKAIsQQEgASgCDHRBAWtxajYCBCABIAEoAiwgASgCDHY2AiwgASABKAIoIAEoAgxrNgIoIAEgASgCSCABKAJEazYCDAJAIAEoAgQgASgCDEsEQCABIAEoAgQgASgCDGs2AgwgASgCDCABKAI4SwRAIAEoAlQoAsQ3BEAgASgCXEHdDDYCGCABKAJUQdH+ADYCBAwKCwsgASABKAIwNgIAAkAgASgCNEUEQCABIAEoAgAgASgCPCABKAIMa2o2AgAgASgCDCABKAIISQRAIAEgASgCCCABKAIMazYCCANAIAEgASgCACICQQFqNgIAIAItAAAhAiABIAEoAkgiA0EBajYCSCADIAI6AAAgASABKAIMQQFrIgI2AgwgAg0ACyABIAEoAkggASgCBGs2AgALDAELAkAgASgCNCABKAIMSQRAIAEgASgCACABKAI8IAEoAjRqIAEoAgxrajYCACABIAEoAgwgASgCNGs2AgwgASgCDCABKAIISQRAIAEgASgCCCABKAIMazYCCANAIAEgASgCACICQQFqNgIAIAItAAAhAiABIAEoAkgiA0EBajYCSCADIAI6AAAgASABKAIMQQFrIgI2AgwgAg0ACyABIAEoAjA2AgAgASgCNCABKAIISQRAIAEgASgCNDYCDCABIAEoAgggASgCDGs2AggDQCABIAEoAgAiAkEBajYCACACLQAAIQIgASABKAJIIgNBAWo2AkggAyACOgAAIAEgASgCDEEBayICNgIMIAINAAsgASABKAJIIAEoAgRrNgIACwsMAQsgASABKAIAIAEoAjQgASgCDGtqNgIAIAEoAgwgASgCCEkEQCABIAEoAgggASgCDGs2AggDQCABIAEoAgAiAkEBajYCACACLQAAIQIgASABKAJIIgNBAWo2AkggAyACOgAAIAEgASgCDEEBayICNgIMIAINAAsgASABKAJIIAEoAgRrNgIACwsLA0AgASgCCEECSwRAIAEgASgCACICQQFqNgIAIAItAAAhAiABIAEoAkgiA0EBajYCSCADIAI6AAAgASABKAIAIgJBAWo2AgAgAi0AACECIAEgASgCSCIDQQFqNgJIIAMgAjoAACABIAEoAgAiAkEBajYCACACLQAAIQIgASABKAJIIgNBAWo2AkggAyACOgAAIAEgASgCCEEDazYCCAwBCwsMAQsgASABKAJIIAEoAgRrNgIAA0AgASABKAIAIgJBAWo2AgAgAi0AACECIAEgASgCSCIDQQFqNgJIIAMgAjoAACABIAEoAgAiAkEBajYCACACLQAAIQIgASABKAJIIgNBAWo2AkggAyACOgAAIAEgASgCACICQQFqNgIAIAItAAAhAiABIAEoAkgiA0EBajYCSCADIAI6AAAgASABKAIIQQNrNgIIIAEoAghBAksNAAsLIAEoAggEQCABIAEoAgAiAkEBajYCACACLQAAIQIgASABKAJIIgNBAWo2AkggAyACOgAAIAEoAghBAUsEQCABIAEoAgAiAkEBajYCACACLQAAIQIgASABKAJIIgNBAWo2AkggAyACOgAACwsMAgsgASgCDEHAAHFFBEAgASABKAIgIAEvARIgASgCLEEBIAEoAgx0QQFrcWpBAnRqKAEANgEQDAELCyABKAJcQYUPNgIYIAEoAlRB0f4ANgIEDAQLDAILIAEoAgxBwABxRQRAIAEgASgCJCABLwESIAEoAixBASABKAIMdEEBa3FqQQJ0aigBADYBEAwBCwsgASgCDEEgcQRAIAEoAlRBv/4ANgIEDAILIAEoAlxB6Q42AhggASgCVEHR/gA2AgQMAQsgASgCUCABKAJMSQR/IAEoAkggASgCQEkFQQALQQFxDQELCyABIAEoAihBA3Y2AgggASABKAJQIAEoAghrNgJQIAEgASgCKCABKAIIQQN0azYCKCABIAEoAixBASABKAIodEEBa3E2AiwgASgCXCABKAJQNgIAIAEoAlwgASgCSDYCDCABKAJcAn8gASgCUCABKAJMSQRAIAEoAkwgASgCUGtBBWoMAQtBBSABKAJQIAEoAkxraws2AgQgASgCXAJ/IAEoAkggASgCQEkEQCABKAJAIAEoAkhrQYECagwBC0GBAiABKAJIIAEoAkBraws2AhAgASgCVCABKAIsNgI8IAEoAlQgASgCKDYCQCAAIAAoAlgoAgw2AkggACAAKAJYKAIQNgJAIAAgACgCWCgCADYCTCAAIAAoAlgoAgQ2AkQgACAAKAJQKAI8NgI8IAAgACgCUCgCQDYCOCAAKAJQKAIEQb/+AEYEQCAAKAJQQX82Asg3CwwNCyAAKAJQQQA2Asg3A0ACQCAAIAAoAlAoAlAgACgCPEEBIAAoAlAoAlh0QQFrcUECdGooAQA2ASAgAC0AISAAKAI4TQ0AIAAoAkRFDQ0gACAAKAJEQQFrNgJEIAAgACgCTCIBQQFqNgJMIAAgACgCPCABLQAAIAAoAjh0ajYCPCAAIAAoAjhBCGo2AjgMAQsLAkAgAC0AIEUNACAALQAgQfABcQ0AIAAgACgBIDYBGANAAkAgACAAKAJQKAJQIAAvARogACgCPEEBIAAtABkgAC0AGGp0QQFrcSAALQAZdmpBAnRqKAEANgEgIAAoAjggAC0AGSAALQAhak8NACAAKAJERQ0OIAAgACgCREEBazYCRCAAIAAoAkwiAUEBajYCTCAAIAAoAjwgAS0AACAAKAI4dGo2AjwgACAAKAI4QQhqNgI4DAELCyAAIAAoAjwgAC0AGXY2AjwgACAAKAI4IAAtABlrNgI4IAAoAlAiASAALQAZIAEoAsg3ajYCyDcLIAAgACgCPCAALQAhdjYCPCAAIAAoAjggAC0AIWs2AjggACgCUCIBIAAtACEgASgCyDdqNgLINyAAKAJQIAAvASI2AkQgAC0AIEUEQCAAKAJQQc3+ADYCBAwNCyAALQAgQSBxBEAgACgCUEF/NgLINyAAKAJQQb/+ADYCBAwNCyAALQAgQcAAcQRAIAAoAlhB6Q42AhggACgCUEHR/gA2AgQMDQsgACgCUCAALQAgQQ9xNgJMIAAoAlBByf4ANgIECyAAKAJQKAJMBEADQCAAKAI4IAAoAlAoAkxJBEAgACgCREUNDSAAIAAoAkRBAWs2AkQgACAAKAJMIgFBAWo2AkwgACAAKAI8IAEtAAAgACgCOHRqNgI8IAAgACgCOEEIajYCOAwBCwsgACgCUCIBIAEoAkQgACgCPEEBIAAoAlAoAkx0QQFrcWo2AkQgACAAKAI8IAAoAlAoAkx2NgI8IAAgACgCOCAAKAJQKAJMazYCOCAAKAJQIgEgACgCUCgCTCABKALIN2o2Asg3CyAAKAJQIAAoAlAoAkQ2Asw3IAAoAlBByv4ANgIECwNAAkAgACAAKAJQKAJUIAAoAjxBASAAKAJQKAJcdEEBa3FBAnRqKAEANgEgIAAtACEgACgCOE0NACAAKAJERQ0LIAAgACgCREEBazYCRCAAIAAoAkwiAUEBajYCTCAAIAAoAjwgAS0AACAAKAI4dGo2AjwgACAAKAI4QQhqNgI4DAELCyAALQAgQfABcUUEQCAAIAAoASA2ARgDQAJAIAAgACgCUCgCVCAALwEaIAAoAjxBASAALQAZIAAtABhqdEEBa3EgAC0AGXZqQQJ0aigBADYBICAAKAI4IAAtABkgAC0AIWpPDQAgACgCREUNDCAAIAAoAkRBAWs2AkQgACAAKAJMIgFBAWo2AkwgACAAKAI8IAEtAAAgACgCOHRqNgI8IAAgACgCOEEIajYCOAwBCwsgACAAKAI8IAAtABl2NgI8IAAgACgCOCAALQAZazYCOCAAKAJQIgEgAC0AGSABKALIN2o2Asg3CyAAIAAoAjwgAC0AIXY2AjwgACAAKAI4IAAtACFrNgI4IAAoAlAiASAALQAhIAEoAsg3ajYCyDcgAC0AIEHAAHEEQCAAKAJYQYUPNgIYIAAoAlBB0f4ANgIEDAsLIAAoAlAgAC8BIjYCSCAAKAJQIAAtACBBD3E2AkwgACgCUEHL/gA2AgQLIAAoAlAoAkwEQANAIAAoAjggACgCUCgCTEkEQCAAKAJERQ0LIAAgACgCREEBazYCRCAAIAAoAkwiAUEBajYCTCAAIAAoAjwgAS0AACAAKAI4dGo2AjwgACAAKAI4QQhqNgI4DAELCyAAKAJQIgEgASgCSCAAKAI8QQEgACgCUCgCTHRBAWtxajYCSCAAIAAoAjwgACgCUCgCTHY2AjwgACAAKAI4IAAoAlAoAkxrNgI4IAAoAlAiASAAKAJQKAJMIAEoAsg3ajYCyDcLIAAoAlBBzP4ANgIECyAAKAJARQ0HIAAgACgCMCAAKAJAazYCLAJAIAAoAlAoAkggACgCLEsEQCAAIAAoAlAoAkggACgCLGs2AiwgACgCLCAAKAJQKAIwSwRAIAAoAlAoAsQ3BEAgACgCWEHdDDYCGCAAKAJQQdH+ADYCBAwMCwsCQCAAKAIsIAAoAlAoAjRLBEAgACAAKAIsIAAoAlAoAjRrNgIsIAAgACgCUCgCOCAAKAJQKAIsIAAoAixrajYCKAwBCyAAIAAoAlAoAjggACgCUCgCNCAAKAIsa2o2AigLIAAoAiwgACgCUCgCREsEQCAAIAAoAlAoAkQ2AiwLDAELIAAgACgCSCAAKAJQKAJIazYCKCAAIAAoAlAoAkQ2AiwLIAAoAiwgACgCQEsEQCAAIAAoAkA2AiwLIAAgACgCQCAAKAIsazYCQCAAKAJQIgEgASgCRCAAKAIsazYCRANAIAAgACgCKCIBQQFqNgIoIAEtAAAhASAAIAAoAkgiAkEBajYCSCACIAE6AAAgACAAKAIsQQFrIgE2AiwgAQ0ACyAAKAJQKAJERQRAIAAoAlBByP4ANgIECwwICyAAKAJARQ0GIAAoAlAoAkQhASAAIAAoAkgiAkEBajYCSCACIAE6AAAgACAAKAJAQQFrNgJAIAAoAlBByP4ANgIEDAcLIAAoAlAoAgwEQANAIAAoAjhBIEkEQCAAKAJERQ0IIAAgACgCREEBazYCRCAAIAAoAkwiAUEBajYCTCAAIAAoAjwgAS0AACAAKAI4dGo2AjwgACAAKAI4QQhqNgI4DAELCyAAIAAoAjAgACgCQGs2AjAgACgCWCIBIAAoAjAgASgCFGo2AhQgACgCUCIBIAAoAjAgASgCIGo2AiACQCAAKAJQKAIMQQRxRQ0AIAAoAjBFDQACfyAAKAJQKAIUBEAgACgCUCgCHCAAKAJIIAAoAjBrIAAoAjAQGgwBCyAAKAJQKAIcIAAoAkggACgCMGsgACgCMBA+CyEBIAAoAlAgATYCHCAAKAJYIAE2AjALIAAgACgCQDYCMAJAIAAoAlAoAgxBBHFFDQACfyAAKAJQKAIUBEAgACgCPAwBCyAAKAI8QQh2QYD+A3EgACgCPEEYdmogACgCPEGA/gNxQQh0aiAAKAI8Qf8BcUEYdGoLIAAoAlAoAhxGDQAgACgCWEHIDDYCGCAAKAJQQdH+ADYCBAwICyAAQQA2AjwgAEEANgI4CyAAKAJQQc/+ADYCBAsCQCAAKAJQKAIMRQ0AIAAoAlAoAhRFDQADQCAAKAI4QSBJBEAgACgCREUNByAAIAAoAkRBAWs2AkQgACAAKAJMIgFBAWo2AkwgACAAKAI8IAEtAAAgACgCOHRqNgI8IAAgACgCOEEIajYCOAwBCwsgACgCPCAAKAJQKAIgRwRAIAAoAlhBsQw2AhggACgCUEHR/gA2AgQMBwsgAEEANgI8IABBADYCOAsgACgCUEHQ/gA2AgQLIABBATYCEAwDCyAAQX02AhAMAgsgAEF8NgJcDAMLIABBfjYCXAwCCwsgACgCWCAAKAJINgIMIAAoAlggACgCQDYCECAAKAJYIAAoAkw2AgAgACgCWCAAKAJENgIEIAAoAlAgACgCPDYCPCAAKAJQIAAoAjg2AkACQAJAIAAoAlAoAiwNACAAKAIwIAAoAlgoAhBGDQEgACgCUCgCBEHR/gBPDQEgACgCUCgCBEHO/gBJDQAgACgCVEEERg0BCwJ/IAAoAlghAiAAKAJYKAIMIQMgACgCMCAAKAJYKAIQayEEIwBBIGsiASQAIAEgAjYCGCABIAM2AhQgASAENgIQIAEgASgCGCgCHDYCDAJAIAEoAgwoAjhFBEAgASgCGCgCKEEBIAEoAgwoAih0QQEgASgCGCgCIBEBACECIAEoAgwgAjYCOCABKAIMKAI4RQRAIAFBATYCHAwCCwsgASgCDCgCLEUEQCABKAIMQQEgASgCDCgCKHQ2AiwgASgCDEEANgI0IAEoAgxBADYCMAsCQCABKAIQIAEoAgwoAixPBEAgASgCDCgCOCABKAIUIAEoAgwoAixrIAEoAgwoAiwQGRogASgCDEEANgI0IAEoAgwgASgCDCgCLDYCMAwBCyABIAEoAgwoAiwgASgCDCgCNGs2AgggASgCCCABKAIQSwRAIAEgASgCEDYCCAsgASgCDCgCOCABKAIMKAI0aiABKAIUIAEoAhBrIAEoAggQGRogASABKAIQIAEoAghrNgIQAkAgASgCEARAIAEoAgwoAjggASgCFCABKAIQayABKAIQEBkaIAEoAgwgASgCEDYCNCABKAIMIAEoAgwoAiw2AjAMAQsgASgCDCICIAEoAgggAigCNGo2AjQgASgCDCgCNCABKAIMKAIsRgRAIAEoAgxBADYCNAsgASgCDCgCMCABKAIMKAIsSQRAIAEoAgwiAiABKAIIIAIoAjBqNgIwCwsLIAFBADYCHAsgASgCHCECIAFBIGokACACCwRAIAAoAlBB0v4ANgIEIABBfDYCXAwCCwsgACAAKAI0IAAoAlgoAgRrNgI0IAAgACgCMCAAKAJYKAIQazYCMCAAKAJYIgEgACgCNCABKAIIajYCCCAAKAJYIgEgACgCMCABKAIUajYCFCAAKAJQIgEgACgCMCABKAIgajYCIAJAIAAoAlAoAgxBBHFFDQAgACgCMEUNAAJ/IAAoAlAoAhQEQCAAKAJQKAIcIAAoAlgoAgwgACgCMGsgACgCMBAaDAELIAAoAlAoAhwgACgCWCgCDCAAKAIwayAAKAIwED4LIQEgACgCUCABNgIcIAAoAlggATYCMAsgACgCWCAAKAJQKAJAQcAAQQAgACgCUCgCCBtqQYABQQAgACgCUCgCBEG//gBGG2pBgAJBACAAKAJQKAIEQcf+AEcEfyAAKAJQKAIEQcL+AEYFQQELQQFxG2o2AiwCQAJAIAAoAjRFBEAgACgCMEUNAQsgACgCVEEERw0BCyAAKAIQDQAgAEF7NgIQCyAAIAAoAhA2AlwLIAAoAlwhASAAQeAAaiQAIAUgATYCCAsgBSgCECIAIAApAwAgBSgCDDUCIH03AwACQAJAAkACQAJAIAUoAghBBWoOBwIDAwMDAAEDCyAFQQA2AhwMAwsgBUEBNgIcDAILIAUoAgwoAhRFBEAgBUEDNgIcDAILCyAFKAIMKAIAQQ0gBSgCCBAUIAVBAjYCHAsgBSgCHCEAIAVBIGokACAACyQBAX8jAEEQayIBIAA2AgwgASABKAIMNgIIIAEoAghBAToADAuXAQEBfyMAQSBrIgMkACADIAA2AhggAyABNgIUIAMgAjcDCCADIAMoAhg2AgQCQAJAIAMpAwhC/////w9YBEAgAygCBCgCFEUNAQsgAygCBCgCAEESQQAQFCADQQA6AB8MAQsgAygCBCADKQMIPgIUIAMoAgQgAygCFDYCECADQQE6AB8LIAMtAB9BAXEhACADQSBqJAAgAAukAgECfyMAQRBrIgEkACABIAA2AgggASABKAIINgIEAkAgASgCBC0ABEEBcQRAIAEgASgCBEEQahC3ATYCAAwBCyABKAIEQRBqIQIjAEEQayIAJAAgACACNgIIAkAgACgCCBBLBEAgAEF+NgIMDAELIAAgACgCCCgCHDYCBCAAKAIEKAI4BEAgACgCCCgCKCAAKAIEKAI4IAAoAggoAiQRBAALIAAoAggoAiggACgCCCgCHCAAKAIIKAIkEQQAIAAoAghBADYCHCAAQQA2AgwLIAAoAgwhAiAAQRBqJAAgASACNgIACwJAIAEoAgAEQCABKAIEKAIAQQ0gASgCABAUIAFBADoADwwBCyABQQE6AA8LIAEtAA9BAXEhACABQRBqJAAgAAuyGAEFfyMAQRBrIgQkACAEIAA2AgggBCAEKAIINgIEIAQoAgRBADYCFCAEKAIEQQA2AhAgBCgCBEEANgIgIAQoAgRBADYCHAJAIAQoAgQtAARBAXEEQCAEKAIEQRBqIQEgBCgCBCgCCCECIwBBMGsiACQAIAAgATYCKCAAIAI2AiQgAEEINgIgIABBcTYCHCAAQQk2AhggAEEANgIUIABBwBI2AhAgAEE4NgIMIABBATYCBAJAAkACQCAAKAIQRQ0AIAAoAhAsAABB+O4ALAAARw0AIAAoAgxBOEYNAQsgAEF6NgIsDAELIAAoAihFBEAgAEF+NgIsDAELIAAoAihBADYCGCAAKAIoKAIgRQRAIAAoAihBBTYCICAAKAIoQQA2AigLIAAoAigoAiRFBEAgACgCKEEGNgIkCyAAKAIkQX9GBEAgAEEGNgIkCwJAIAAoAhxBAEgEQCAAQQA2AgQgAEEAIAAoAhxrNgIcDAELIAAoAhxBD0oEQCAAQQI2AgQgACAAKAIcQRBrNgIcCwsCQAJAIAAoAhhBAUgNACAAKAIYQQlKDQAgACgCIEEIRw0AIAAoAhxBCEgNACAAKAIcQQ9KDQAgACgCJEEASA0AIAAoAiRBCUoNACAAKAIUQQBIDQAgACgCFEEESg0AIAAoAhxBCEcNASAAKAIEQQFGDQELIABBfjYCLAwBCyAAKAIcQQhGBEAgAEEJNgIcCyAAIAAoAigoAihBAUHELSAAKAIoKAIgEQEANgIIIAAoAghFBEAgAEF8NgIsDAELIAAoAiggACgCCDYCHCAAKAIIIAAoAig2AgAgACgCCEEqNgIEIAAoAgggACgCBDYCGCAAKAIIQQA2AhwgACgCCCAAKAIcNgIwIAAoAghBASAAKAIIKAIwdDYCLCAAKAIIIAAoAggoAixBAWs2AjQgACgCCCAAKAIYQQdqNgJQIAAoAghBASAAKAIIKAJQdDYCTCAAKAIIIAAoAggoAkxBAWs2AlQgACgCCCAAKAIIKAJQQQJqQQNuNgJYIAAoAigoAiggACgCCCgCLEECIAAoAigoAiARAQAhASAAKAIIIAE2AjggACgCKCgCKCAAKAIIKAIsQQIgACgCKCgCIBEBACEBIAAoAgggATYCQCAAKAIoKAIoIAAoAggoAkxBAiAAKAIoKAIgEQEAIQEgACgCCCABNgJEIAAoAghBADYCwC0gACgCCEEBIAAoAhhBBmp0NgKcLSAAIAAoAigoAiggACgCCCgCnC1BBCAAKAIoKAIgEQEANgIAIAAoAgggACgCADYCCCAAKAIIIAAoAggoApwtQQJ0NgIMAkACQCAAKAIIKAI4RQ0AIAAoAggoAkBFDQAgACgCCCgCREUNACAAKAIIKAIIDQELIAAoAghBmgU2AgQgACgCKEG42QAoAgA2AhggACgCKBC3ARogAEF8NgIsDAELIAAoAgggACgCACAAKAIIKAKcLUEBdkEBdGo2AqQtIAAoAgggACgCCCgCCCAAKAIIKAKcLUEDbGo2ApgtIAAoAgggACgCJDYChAEgACgCCCAAKAIUNgKIASAAKAIIIAAoAiA6ACQgACgCKCEBIwBBEGsiAyQAIAMgATYCDCADKAIMIQIjAEEQayIBJAAgASACNgIIAkAgASgCCBB5BEAgAUF+NgIMDAELIAEoAghBADYCFCABKAIIQQA2AgggASgCCEEANgIYIAEoAghBAjYCLCABIAEoAggoAhw2AgQgASgCBEEANgIUIAEoAgQgASgCBCgCCDYCECABKAIEKAIYQQBIBEAgASgCBEEAIAEoAgQoAhhrNgIYCyABKAIEIAEoAgQoAhhBAkYEf0E5BUEqQfEAIAEoAgQoAhgbCzYCBAJ/IAEoAgQoAhhBAkYEQEEAQQBBABAaDAELQQBBAEEAED4LIQIgASgCCCACNgIwIAEoAgRBADYCKCABKAIEIQUjAEEQayICJAAgAiAFNgIMIAIoAgwgAigCDEGUAWo2ApgWIAIoAgxB0N8ANgKgFiACKAIMIAIoAgxBiBNqNgKkFiACKAIMQeTfADYCrBYgAigCDCACKAIMQfwUajYCsBYgAigCDEH43wA2ArgWIAIoAgxBADsBuC0gAigCDEEANgK8LSACKAIMEL0BIAJBEGokACABQQA2AgwLIAEoAgwhAiABQRBqJAAgAyACNgIIIAMoAghFBEAgAygCDCgCHCECIwBBEGsiASQAIAEgAjYCDCABKAIMIAEoAgwoAixBAXQ2AjwgASgCDCgCRCABKAIMKAJMQQFrQQF0akEAOwEAIAEoAgwoAkRBACABKAIMKAJMQQFrQQF0EDIgASgCDCABKAIMKAKEAUEMbEGA7wBqLwECNgKAASABKAIMIAEoAgwoAoQBQQxsQYDvAGovAQA2AowBIAEoAgwgASgCDCgChAFBDGxBgO8Aai8BBDYCkAEgASgCDCABKAIMKAKEAUEMbEGA7wBqLwEGNgJ8IAEoAgxBADYCbCABKAIMQQA2AlwgASgCDEEANgJ0IAEoAgxBADYCtC0gASgCDEECNgJ4IAEoAgxBAjYCYCABKAIMQQA2AmggASgCDEEANgJIIAFBEGokAAsgAygCCCEBIANBEGokACAAIAE2AiwLIAAoAiwhASAAQTBqJAAgBCABNgIADAELIAQoAgRBEGohASMAQSBrIgAkACAAIAE2AhggAEFxNgIUIABBwBI2AhAgAEE4NgIMAkACQAJAIAAoAhBFDQAgACgCECwAAEHAEiwAAEcNACAAKAIMQThGDQELIABBejYCHAwBCyAAKAIYRQRAIABBfjYCHAwBCyAAKAIYQQA2AhggACgCGCgCIEUEQCAAKAIYQQU2AiAgACgCGEEANgIoCyAAKAIYKAIkRQRAIAAoAhhBBjYCJAsgACAAKAIYKAIoQQFB0DcgACgCGCgCIBEBADYCBCAAKAIERQRAIABBfDYCHAwBCyAAKAIYIAAoAgQ2AhwgACgCBCAAKAIYNgIAIAAoAgRBADYCOCAAKAIEQbT+ADYCBCAAKAIYIQIgACgCFCEDIwBBIGsiASQAIAEgAjYCGCABIAM2AhQCQCABKAIYEEsEQCABQX42AhwMAQsgASABKAIYKAIcNgIMAkAgASgCFEEASARAIAFBADYCECABQQAgASgCFGs2AhQMAQsgASABKAIUQQR1QQVqNgIQIAEoAhRBMEgEQCABIAEoAhRBD3E2AhQLCwJAIAEoAhRFDQAgASgCFEEITgRAIAEoAhRBD0wNAQsgAUF+NgIcDAELAkAgASgCDCgCOEUNACABKAIMKAIoIAEoAhRGDQAgASgCGCgCKCABKAIMKAI4IAEoAhgoAiQRBAAgASgCDEEANgI4CyABKAIMIAEoAhA2AgwgASgCDCABKAIUNgIoIAEoAhghAiMAQRBrIgMkACADIAI2AggCQCADKAIIEEsEQCADQX42AgwMAQsgAyADKAIIKAIcNgIEIAMoAgRBADYCLCADKAIEQQA2AjAgAygCBEEANgI0IAMoAgghBSMAQRBrIgIkACACIAU2AggCQCACKAIIEEsEQCACQX42AgwMAQsgAiACKAIIKAIcNgIEIAIoAgRBADYCICACKAIIQQA2AhQgAigCCEEANgIIIAIoAghBADYCGCACKAIEKAIMBEAgAigCCCACKAIEKAIMQQFxNgIwCyACKAIEQbT+ADYCBCACKAIEQQA2AgggAigCBEEANgIQIAIoAgRBgIACNgIYIAIoAgRBADYCJCACKAIEQQA2AjwgAigCBEEANgJAIAIoAgQgAigCBEG0CmoiBTYCcCACKAIEIAU2AlQgAigCBCAFNgJQIAIoAgRBATYCxDcgAigCBEF/NgLINyACQQA2AgwLIAIoAgwhBSACQRBqJAAgAyAFNgIMCyADKAIMIQIgA0EQaiQAIAEgAjYCHAsgASgCHCECIAFBIGokACAAIAI2AgggACgCCARAIAAoAhgoAiggACgCBCAAKAIYKAIkEQQAIAAoAhhBADYCHAsgACAAKAIINgIcCyAAKAIcIQEgAEEgaiQAIAQgATYCAAsCQCAEKAIABEAgBCgCBCgCAEENIAQoAgAQFCAEQQA6AA8MAQsgBEEBOgAPCyAELQAPQQFxIQAgBEEQaiQAIAALbwEBfyMAQRBrIgEgADYCCCABIAEoAgg2AgQCQCABKAIELQAEQQFxRQRAIAFBADYCDAwBCyABKAIEKAIIQQNIBEAgAUECNgIMDAELIAEoAgQoAghBB0oEQCABQQE2AgwMAQsgAUEANgIMCyABKAIMCywBAX8jAEEQayIBJAAgASAANgIMIAEgASgCDDYCCCABKAIIEBUgAUEQaiQACzwBAX8jAEEQayIDJAAgAyAAOwEOIAMgATYCCCADIAI2AgRBASADKAIIIAMoAgQQtAEhACADQRBqJAAgAAvBEAECfyMAQSBrIgIkACACIAA2AhggAiABNgIUAkADQAJAIAIoAhgoAnRBhgJJBEAgAigCGBBbAkAgAigCGCgCdEGGAk8NACACKAIUDQAgAkEANgIcDAQLIAIoAhgoAnRFDQELIAJBADYCECACKAIYKAJ0QQNPBEAgAigCGCACKAIYKAJUIAIoAhgoAjggAigCGCgCbEECamotAAAgAigCGCgCSCACKAIYKAJYdHNxNgJIIAIoAhgoAkAgAigCGCgCbCACKAIYKAI0cUEBdGogAigCGCgCRCACKAIYKAJIQQF0ai8BACIAOwEAIAIgAEH//wNxNgIQIAIoAhgoAkQgAigCGCgCSEEBdGogAigCGCgCbDsBAAsgAigCGCACKAIYKAJgNgJ4IAIoAhggAigCGCgCcDYCZCACKAIYQQI2AmACQCACKAIQRQ0AIAIoAhgoAnggAigCGCgCgAFPDQAgAigCGCgCLEGGAmsgAigCGCgCbCACKAIQa0kNACACKAIYIAIoAhAQtQEhACACKAIYIAA2AmACQCACKAIYKAJgQQVLDQAgAigCGCgCiAFBAUcEQCACKAIYKAJgQQNHDQEgAigCGCgCbCACKAIYKAJwa0GAIE0NAQsgAigCGEECNgJgCwsCQAJAIAIoAhgoAnhBA0kNACACKAIYKAJgIAIoAhgoAnhLDQAgAiACKAIYIgAoAmwgACgCdGpBA2s2AgggAiACKAIYKAJ4QQNrOgAHIAIgAigCGCIAKAJsIAAoAmRBf3NqOwEEIAIoAhgiACgCpC0gACgCoC1BAXRqIAIvAQQ7AQAgAi0AByEBIAIoAhgiACgCmC0hAyAAIAAoAqAtIgBBAWo2AqAtIAAgA2ogAToAACACIAIvAQRBAWs7AQQgAigCGCACLQAHQdDdAGotAABBAnRqQZgJaiIAIAAvAQBBAWo7AQAgAigCGEGIE2oCfyACLwEEQYACSQRAIAIvAQQtANBZDAELIAIvAQRBB3ZBgAJqLQDQWQtBAnRqIgAgAC8BAEEBajsBACACIAIoAhgoAqAtIAIoAhgoApwtQQFrRjYCDCACKAIYIgAgACgCdCACKAIYKAJ4QQFrazYCdCACKAIYIgAgACgCeEECazYCeANAIAIoAhgiASgCbEEBaiEAIAEgADYCbCAAIAIoAghNBEAgAigCGCACKAIYKAJUIAIoAhgoAjggAigCGCgCbEECamotAAAgAigCGCgCSCACKAIYKAJYdHNxNgJIIAIoAhgoAkAgAigCGCgCbCACKAIYKAI0cUEBdGogAigCGCgCRCACKAIYKAJIQQF0ai8BACIAOwEAIAIgAEH//wNxNgIQIAIoAhgoAkQgAigCGCgCSEEBdGogAigCGCgCbDsBAAsgAigCGCIBKAJ4QQFrIQAgASAANgJ4IAANAAsgAigCGEEANgJoIAIoAhhBAjYCYCACKAIYIgAgACgCbEEBajYCbCACKAIMBEAgAigCGAJ/IAIoAhgoAlxBAE4EQCACKAIYKAI4IAIoAhgoAlxqDAELQQALIAIoAhgoAmwgAigCGCgCXGtBABAoIAIoAhggAigCGCgCbDYCXCACKAIYKAIAEBwgAigCGCgCACgCEEUEQCACQQA2AhwMBgsLDAELAkAgAigCGCgCaARAIAIgAigCGCIAKAI4IAAoAmxqQQFrLQAAOgADIAIoAhgiACgCpC0gACgCoC1BAXRqQQA7AQAgAi0AAyEBIAIoAhgiACgCmC0hAyAAIAAoAqAtIgBBAWo2AqAtIAAgA2ogAToAACACKAIYIAItAANBAnRqIgAgAC8BlAFBAWo7AZQBIAIgAigCGCgCoC0gAigCGCgCnC1BAWtGNgIMIAIoAgwEQCACKAIYAn8gAigCGCgCXEEATgRAIAIoAhgoAjggAigCGCgCXGoMAQtBAAsgAigCGCgCbCACKAIYKAJca0EAECggAigCGCACKAIYKAJsNgJcIAIoAhgoAgAQHAsgAigCGCIAIAAoAmxBAWo2AmwgAigCGCIAIAAoAnRBAWs2AnQgAigCGCgCACgCEEUEQCACQQA2AhwMBgsMAQsgAigCGEEBNgJoIAIoAhgiACAAKAJsQQFqNgJsIAIoAhgiACAAKAJ0QQFrNgJ0CwsMAQsLIAIoAhgoAmgEQCACIAIoAhgiACgCOCAAKAJsakEBay0AADoAAiACKAIYIgAoAqQtIAAoAqAtQQF0akEAOwEAIAItAAIhASACKAIYIgAoApgtIQMgACAAKAKgLSIAQQFqNgKgLSAAIANqIAE6AAAgAigCGCACLQACQQJ0aiIAIAAvAZQBQQFqOwGUASACIAIoAhgoAqAtIAIoAhgoApwtQQFrRjYCDCACKAIYQQA2AmgLIAIoAhgCfyACKAIYKAJsQQJJBEAgAigCGCgCbAwBC0ECCzYCtC0gAigCFEEERgRAIAIoAhgCfyACKAIYKAJcQQBOBEAgAigCGCgCOCACKAIYKAJcagwBC0EACyACKAIYKAJsIAIoAhgoAlxrQQEQKCACKAIYIAIoAhgoAmw2AlwgAigCGCgCABAcIAIoAhgoAgAoAhBFBEAgAkECNgIcDAILIAJBAzYCHAwBCyACKAIYKAKgLQRAIAIoAhgCfyACKAIYKAJcQQBOBEAgAigCGCgCOCACKAIYKAJcagwBC0EACyACKAIYKAJsIAIoAhgoAlxrQQAQKCACKAIYIAIoAhgoAmw2AlwgAigCGCgCABAcIAIoAhgoAgAoAhBFBEAgAkEANgIcDAILCyACQQE2AhwLIAIoAhwhACACQSBqJAAgAAuVDQECfyMAQSBrIgIkACACIAA2AhggAiABNgIUAkADQAJAIAIoAhgoAnRBhgJJBEAgAigCGBBbAkAgAigCGCgCdEGGAk8NACACKAIUDQAgAkEANgIcDAQLIAIoAhgoAnRFDQELIAJBADYCECACKAIYKAJ0QQNPBEAgAigCGCACKAIYKAJUIAIoAhgoAjggAigCGCgCbEECamotAAAgAigCGCgCSCACKAIYKAJYdHNxNgJIIAIoAhgoAkAgAigCGCgCbCACKAIYKAI0cUEBdGogAigCGCgCRCACKAIYKAJIQQF0ai8BACIAOwEAIAIgAEH//wNxNgIQIAIoAhgoAkQgAigCGCgCSEEBdGogAigCGCgCbDsBAAsCQCACKAIQRQ0AIAIoAhgoAixBhgJrIAIoAhgoAmwgAigCEGtJDQAgAigCGCACKAIQELUBIQAgAigCGCAANgJgCwJAIAIoAhgoAmBBA08EQCACIAIoAhgoAmBBA2s6AAsgAiACKAIYIgAoAmwgACgCcGs7AQggAigCGCIAKAKkLSAAKAKgLUEBdGogAi8BCDsBACACLQALIQEgAigCGCIAKAKYLSEDIAAgACgCoC0iAEEBajYCoC0gACADaiABOgAAIAIgAi8BCEEBazsBCCACKAIYIAItAAtB0N0Aai0AAEECdGpBmAlqIgAgAC8BAEEBajsBACACKAIYQYgTagJ/IAIvAQhBgAJJBEAgAi8BCC0A0FkMAQsgAi8BCEEHdkGAAmotANBZC0ECdGoiACAALwEAQQFqOwEAIAIgAigCGCgCoC0gAigCGCgCnC1BAWtGNgIMIAIoAhgiACAAKAJ0IAIoAhgoAmBrNgJ0AkACQCACKAIYKAJgIAIoAhgoAoABSw0AIAIoAhgoAnRBA0kNACACKAIYIgAgACgCYEEBazYCYANAIAIoAhgiACAAKAJsQQFqNgJsIAIoAhggAigCGCgCVCACKAIYKAI4IAIoAhgoAmxBAmpqLQAAIAIoAhgoAkggAigCGCgCWHRzcTYCSCACKAIYKAJAIAIoAhgoAmwgAigCGCgCNHFBAXRqIAIoAhgoAkQgAigCGCgCSEEBdGovAQAiADsBACACIABB//8DcTYCECACKAIYKAJEIAIoAhgoAkhBAXRqIAIoAhgoAmw7AQAgAigCGCIBKAJgQQFrIQAgASAANgJgIAANAAsgAigCGCIAIAAoAmxBAWo2AmwMAQsgAigCGCIAIAIoAhgoAmAgACgCbGo2AmwgAigCGEEANgJgIAIoAhggAigCGCgCOCACKAIYKAJsai0AADYCSCACKAIYIAIoAhgoAlQgAigCGCgCOCACKAIYKAJsQQFqai0AACACKAIYKAJIIAIoAhgoAlh0c3E2AkgLDAELIAIgAigCGCIAKAI4IAAoAmxqLQAAOgAHIAIoAhgiACgCpC0gACgCoC1BAXRqQQA7AQAgAi0AByEBIAIoAhgiACgCmC0hAyAAIAAoAqAtIgBBAWo2AqAtIAAgA2ogAToAACACKAIYIAItAAdBAnRqIgAgAC8BlAFBAWo7AZQBIAIgAigCGCgCoC0gAigCGCgCnC1BAWtGNgIMIAIoAhgiACAAKAJ0QQFrNgJ0IAIoAhgiACAAKAJsQQFqNgJsCyACKAIMBEAgAigCGAJ/IAIoAhgoAlxBAE4EQCACKAIYKAI4IAIoAhgoAlxqDAELQQALIAIoAhgoAmwgAigCGCgCXGtBABAoIAIoAhggAigCGCgCbDYCXCACKAIYKAIAEBwgAigCGCgCACgCEEUEQCACQQA2AhwMBAsLDAELCyACKAIYAn8gAigCGCgCbEECSQRAIAIoAhgoAmwMAQtBAgs2ArQtIAIoAhRBBEYEQCACKAIYAn8gAigCGCgCXEEATgRAIAIoAhgoAjggAigCGCgCXGoMAQtBAAsgAigCGCgCbCACKAIYKAJca0EBECggAigCGCACKAIYKAJsNgJcIAIoAhgoAgAQHCACKAIYKAIAKAIQRQRAIAJBAjYCHAwCCyACQQM2AhwMAQsgAigCGCgCoC0EQCACKAIYAn8gAigCGCgCXEEATgRAIAIoAhgoAjggAigCGCgCXGoMAQtBAAsgAigCGCgCbCACKAIYKAJca0EAECggAigCGCACKAIYKAJsNgJcIAIoAhgoAgAQHCACKAIYKAIAKAIQRQRAIAJBADYCHAwCCwsgAkEBNgIcCyACKAIcIQAgAkEgaiQAIAALBgBBtJsBCykBAX8jAEEQayICJAAgAiAANgIMIAIgATYCCCACKAIIEBUgAkEQaiQACzoBAX8jAEEQayIDJAAgAyAANgIMIAMgATYCCCADIAI2AgQgAygCCCADKAIEbBAYIQAgA0EQaiQAIAALzgUBAX8jAEHQAGsiBSQAIAUgADYCRCAFIAE2AkAgBSACNgI8IAUgAzcDMCAFIAQ2AiwgBSAFKAJANgIoAkACQAJAAkACQAJAAkACQAJAIAUoAiwODwABAgMFBgcHBwcHBwcHBAcLAn8gBSgCRCEBIAUoAighAiMAQeAAayIAJAAgACABNgJYIAAgAjYCVCAAIAAoAlggAEHIAGpCDBAuIgM3AwgCQCADQgBTBEAgACgCVCAAKAJYEBcgAEF/NgJcDAELIAApAwhCDFIEQCAAKAJUQRFBABAUIABBfzYCXAwBCyAAKAJUIABByABqIABByABqQgxBABB9IAAoAlggAEEQahA4QQBIBEAgAEEANgJcDAELIAAoAjggAEEGaiAAQQRqEIEBAkAgAC0AUyAAKAI8QRh2Rg0AIAAtAFMgAC8BBkEIdkYNACAAKAJUQRtBABAUIABBfzYCXAwBCyAAQQA2AlwLIAAoAlwhASAAQeAAaiQAIAFBAEgLBEAgBUJ/NwNIDAgLIAVCADcDSAwHCyAFIAUoAkQgBSgCPCAFKQMwEC4iAzcDICADQgBTBEAgBSgCKCAFKAJEEBcgBUJ/NwNIDAcLIAUoAkAgBSgCPCAFKAI8IAUpAyBBABB9IAUgBSkDIDcDSAwGCyAFQgA3A0gMBQsgBSAFKAI8NgIcIAUoAhxBADsBMiAFKAIcIgAgACkDAEKAAYQ3AwAgBSgCHCkDAEIIg0IAUgRAIAUoAhwiACAAKQMgQgx9NwMgCyAFQgA3A0gMBAsgBUF/NgIUIAVBBTYCECAFQQQ2AgwgBUEDNgIIIAVBAjYCBCAFQQE2AgAgBUEAIAUQNjcDSAwDCyAFIAUoAiggBSgCPCAFKQMwEEI3A0gMAgsgBSgCKBC+ASAFQgA3A0gMAQsgBSgCKEESQQAQFCAFQn83A0gLIAUpA0ghAyAFQdAAaiQAIAMLBwAgAC8BMAvuAgEBfyMAQSBrIgUkACAFIAA2AhggBSABNgIUIAUgAjsBEiAFIAM2AgwgBSAENgIIAkACQAJAIAUoAghFDQAgBSgCFEUNACAFLwESQQFGDQELIAUoAhhBCGpBEkEAEBQgBUEANgIcDAELIAUoAgxBAXEEQCAFKAIYQQhqQRhBABAUIAVBADYCHAwBCyAFQRgQGCIANgIEIABFBEAgBSgCGEEIakEOQQAQFCAFQQA2AhwMAQsjAEEQayIAIAUoAgQ2AgwgACgCDEEANgIAIAAoAgxBADYCBCAAKAIMQQA2AgggBSgCBEH4rNGRATYCDCAFKAIEQYnPlZoCNgIQIAUoAgRBkPHZogM2AhQgBSgCBEEAIAUoAgggBSgCCBArrUEBEH0gBSAFKAIYIAUoAhRBAyAFKAIEEGYiADYCACAARQRAIAUoAgQQvgEgBUEANgIcDAELIAUgBSgCADYCHAsgBSgCHCEAIAVBIGokACAAC70YAQJ/IwBB8ABrIgQkACAEIAA2AmQgBCABNgJgIAQgAjcDWCAEIAM2AlQgBCAEKAJkNgJQAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAEKAJUDhQGBwIMBAUKDwADCRELEA4IEgESDRILQQBCAEEAIAQoAlAQTSEAIAQoAlAgADYCFCAARQRAIARCfzcDaAwTCyAEKAJQKAIUQgA3AzggBCgCUCgCFEIANwNAIARCADcDaAwSCyAEKAJQKAIQIQEgBCkDWCECIAQoAlAhAyMAQUBqIgAkACAAIAE2AjggACACNwMwIAAgAzYCLAJAIAApAzBQBEAgAEEAQgBBASAAKAIsEE02AjwMAQsgACkDMCAAKAI4KQMwVgRAIAAoAixBEkEAEBQgAEEANgI8DAELIAAoAjgoAigEQCAAKAIsQR1BABAUIABBADYCPAwBCyAAIAAoAjggACkDMBC/ATcDICAAIAApAzAgACgCOCgCBCAAKQMgp0EDdGopAwB9NwMYIAApAxhQBEAgACAAKQMgQgF9NwMgIAAgACgCOCgCACAAKQMgp0EEdGopAwg3AxgLIAAgACgCOCgCACAAKQMgp0EEdGopAwggACkDGH03AxAgACkDECAAKQMwVgRAIAAoAixBHEEAEBQgAEEANgI8DAELIAAgACgCOCgCACAAKQMgQgF8QQAgACgCLBBNIgE2AgwgAUUEQCAAQQA2AjwMAQsgACgCDCgCACAAKAIMKQMIQgF9p0EEdGogACkDGDcDCCAAKAIMKAIEIAAoAgwpAwinQQN0aiAAKQMwNwMAIAAoAgwgACkDMDcDMCAAKAIMAn4gACgCOCkDGCAAKAIMKQMIQgF9VARAIAAoAjgpAxgMAQsgACgCDCkDCEIBfQs3AxggACgCOCAAKAIMNgIoIAAoAgwgACgCODYCKCAAKAI4IAAoAgwpAwg3AyAgACgCDCAAKQMgQgF8NwMgIAAgACgCDDYCPAsgACgCPCEBIABBQGskACABIQAgBCgCUCAANgIUIABFBEAgBEJ/NwNoDBILIAQoAlAoAhQgBCkDWDcDOCAEKAJQKAIUIAQoAlAoAhQpAwg3A0AgBEIANwNoDBELIARCADcDaAwQCyAEKAJQKAIQEDMgBCgCUCAEKAJQKAIUNgIQIAQoAlBBADYCFCAEQgA3A2gMDwsgBCAEKAJQIAQoAmAgBCkDWBBCNwNoDA4LIAQoAlAoAhAQMyAEKAJQKAIUEDMgBCgCUBAVIARCADcDaAwNCyAEKAJQKAIQQgA3AzggBCgCUCgCEEIANwNAIARCADcDaAwMCyAEKQNYQv///////////wBWBEAgBCgCUEESQQAQFCAEQn83A2gMDAsgBCgCUCgCECEBIAQoAmAhAyAEKQNYIQIjAEFAaiIAJAAgACABNgI0IAAgAzYCMCAAIAI3AyggAAJ+IAApAyggACgCNCkDMCAAKAI0KQM4fVQEQCAAKQMoDAELIAAoAjQpAzAgACgCNCkDOH0LNwMoAkAgACkDKFAEQCAAQgA3AzgMAQsgACkDKEL///////////8AVgRAIABCfzcDOAwBCyAAIAAoAjQpA0A3AxggACAAKAI0KQM4IAAoAjQoAgQgACkDGKdBA3RqKQMAfTcDECAAQgA3AyADQCAAKQMgIAApAyhUBEAgAAJ+IAApAyggACkDIH0gACgCNCgCACAAKQMYp0EEdGopAwggACkDEH1UBEAgACkDKCAAKQMgfQwBCyAAKAI0KAIAIAApAxinQQR0aikDCCAAKQMQfQs3AwggACgCMCAAKQMgp2ogACgCNCgCACAAKQMYp0EEdGooAgAgACkDEKdqIAApAwinEBkaIAApAwggACgCNCgCACAAKQMYp0EEdGopAwggACkDEH1RBEAgACAAKQMYQgF8NwMYCyAAIAApAwggACkDIHw3AyAgAEIANwMQDAELCyAAKAI0IgEgACkDICABKQM4fDcDOCAAKAI0IAApAxg3A0AgACAAKQMgNwM4CyAAKQM4IQIgAEFAayQAIAQgAjcDaAwLCyAEQQBCAEEAIAQoAlAQTTYCTCAEKAJMRQRAIARCfzcDaAwLCyAEKAJQKAIQEDMgBCgCUCAEKAJMNgIQIARCADcDaAwKCyAEKAJQKAIUEDMgBCgCUEEANgIUIARCADcDaAwJCyAEIAQoAlAoAhAgBCgCYCAEKQNYIAQoAlAQwAGsNwNoDAgLIAQgBCgCUCgCFCAEKAJgIAQpA1ggBCgCUBDAAaw3A2gMBwsgBCkDWEI4VARAIAQoAlBBEkEAEBQgBEJ/NwNoDAcLIAQgBCgCYDYCSCAEKAJIEDsgBCgCSCAEKAJQKAIMNgIoIAQoAkggBCgCUCgCECkDMDcDGCAEKAJIIAQoAkgpAxg3AyAgBCgCSEEAOwEwIAQoAkhBADsBMiAEKAJIQtwBNwMAIARCODcDaAwGCyAEKAJQIAQoAmAoAgA2AgwgBEIANwNoDAULIARBfzYCQCAEQRM2AjwgBEELNgI4IARBDTYCNCAEQQw2AjAgBEEKNgIsIARBDzYCKCAEQQk2AiQgBEERNgIgIARBCDYCHCAEQQc2AhggBEEGNgIUIARBBTYCECAEQQQ2AgwgBEEDNgIIIARBAjYCBCAEQQE2AgAgBEEAIAQQNjcDaAwECyAEKAJQKAIQKQM4Qv///////////wBWBEAgBCgCUEEeQT0QFCAEQn83A2gMBAsgBCAEKAJQKAIQKQM4NwNoDAMLIAQoAlAoAhQpAzhC////////////AFYEQCAEKAJQQR5BPRAUIARCfzcDaAwDCyAEIAQoAlAoAhQpAzg3A2gMAgsgBCkDWEL///////////8AVgRAIAQoAlBBEkEAEBQgBEJ/NwNoDAILIAQoAlAoAhQhASAEKAJgIQMgBCkDWCECIAQoAlAhBSMAQeAAayIAJAAgACABNgJUIAAgAzYCUCAAIAI3A0ggACAFNgJEAkAgACkDSCAAKAJUKQM4IAApA0h8Qv//A3xWBEAgACgCREESQQAQFCAAQn83A1gMAQsgACAAKAJUKAIEIAAoAlQpAwinQQN0aikDADcDICAAKQMgIAAoAlQpAzggACkDSHxUBEAgACAAKAJUKQMIIAApA0ggACkDICAAKAJUKQM4fX1C//8DfEIQiHw3AxggACkDGCAAKAJUKQMQVgRAIAAgACgCVCkDEDcDECAAKQMQUARAIABCEDcDEAsDQCAAKQMQIAApAxhUBEAgACAAKQMQQgGGNwMQDAELCyAAKAJUIAApAxAgACgCRBDBAUEBcUUEQCAAKAJEQQ5BABAUIABCfzcDWAwDCwsDQCAAKAJUKQMIIAApAxhUBEBBgIAEEBghASAAKAJUKAIAIAAoAlQpAwinQQR0aiABNgIAIAEEQCAAKAJUKAIAIAAoAlQpAwinQQR0akKAgAQ3AwggACgCVCIBIAEpAwhCAXw3AwggACAAKQMgQoCABHw3AyAgACgCVCgCBCAAKAJUKQMIp0EDdGogACkDIDcDAAwCBSAAKAJEQQ5BABAUIABCfzcDWAwECwALCwsgACAAKAJUKQNANwMwIAAgACgCVCkDOCAAKAJUKAIEIAApAzCnQQN0aikDAH03AyggAEIANwM4A0AgACkDOCAAKQNIVARAIAACfiAAKQNIIAApAzh9IAAoAlQoAgAgACkDMKdBBHRqKQMIIAApAyh9VARAIAApA0ggACkDOH0MAQsgACgCVCgCACAAKQMwp0EEdGopAwggACkDKH0LNwMIIAAoAlQoAgAgACkDMKdBBHRqKAIAIAApAyinaiAAKAJQIAApAzinaiAAKQMIpxAZGiAAKQMIIAAoAlQoAgAgACkDMKdBBHRqKQMIIAApAyh9UQRAIAAgACkDMEIBfDcDMAsgACAAKQMIIAApAzh8NwM4IABCADcDKAwBCwsgACgCVCIBIAApAzggASkDOHw3AzggACgCVCAAKQMwNwNAIAAoAlQpAzggACgCVCkDMFYEQCAAKAJUIAAoAlQpAzg3AzALIAAgACkDODcDWAsgACkDWCECIABB4ABqJAAgBCACNwNoDAELIAQoAlBBHEEAEBQgBEJ/NwNoCyAEKQNoIQIgBEHwAGokACACCwcAIAAoAiALBwAgACgCAAsIAEEBQTgQdgsLhY0BJABBgAgLgQxpbnN1ZmZpY2llbnQgbWVtb3J5AG5lZWQgZGljdGlvbmFyeQAtKyAgIDBYMHgALTBYKzBYIDBYLTB4KzB4IDB4AFppcCBhcmNoaXZlIGluY29uc2lzdGVudABJbnZhbGlkIGFyZ3VtZW50AGludmFsaWQgbGl0ZXJhbC9sZW5ndGhzIHNldABpbnZhbGlkIGNvZGUgbGVuZ3RocyBzZXQAdW5rbm93biBoZWFkZXIgZmxhZ3Mgc2V0AGludmFsaWQgZGlzdGFuY2VzIHNldABpbnZhbGlkIGJpdCBsZW5ndGggcmVwZWF0AEZpbGUgYWxyZWFkeSBleGlzdHMAdG9vIG1hbnkgbGVuZ3RoIG9yIGRpc3RhbmNlIHN5bWJvbHMAaW52YWxpZCBzdG9yZWQgYmxvY2sgbGVuZ3RocwAlcyVzJXMAYnVmZmVyIGVycm9yAE5vIGVycm9yAHN0cmVhbSBlcnJvcgBUZWxsIGVycm9yAEludGVybmFsIGVycm9yAFNlZWsgZXJyb3IAV3JpdGUgZXJyb3IAZmlsZSBlcnJvcgBSZWFkIGVycm9yAFpsaWIgZXJyb3IAZGF0YSBlcnJvcgBDUkMgZXJyb3IAaW5jb21wYXRpYmxlIHZlcnNpb24AbmFuAC9kZXYvdXJhbmRvbQBpbnZhbGlkIGNvZGUgLS0gbWlzc2luZyBlbmQtb2YtYmxvY2sAaW5jb3JyZWN0IGhlYWRlciBjaGVjawBpbmNvcnJlY3QgbGVuZ3RoIGNoZWNrAGluY29ycmVjdCBkYXRhIGNoZWNrAGludmFsaWQgZGlzdGFuY2UgdG9vIGZhciBiYWNrAGhlYWRlciBjcmMgbWlzbWF0Y2gAaW5mAGludmFsaWQgd2luZG93IHNpemUAUmVhZC1vbmx5IGFyY2hpdmUATm90IGEgemlwIGFyY2hpdmUAUmVzb3VyY2Ugc3RpbGwgaW4gdXNlAE1hbGxvYyBmYWlsdXJlAGludmFsaWQgYmxvY2sgdHlwZQBGYWlsdXJlIHRvIGNyZWF0ZSB0ZW1wb3JhcnkgZmlsZQBDYW4ndCBvcGVuIGZpbGUATm8gc3VjaCBmaWxlAFByZW1hdHVyZSBlbmQgb2YgZmlsZQBDYW4ndCByZW1vdmUgZmlsZQBpbnZhbGlkIGxpdGVyYWwvbGVuZ3RoIGNvZGUAaW52YWxpZCBkaXN0YW5jZSBjb2RlAHVua25vd24gY29tcHJlc3Npb24gbWV0aG9kAHN0cmVhbSBlbmQAQ29tcHJlc3NlZCBkYXRhIGludmFsaWQATXVsdGktZGlzayB6aXAgYXJjaGl2ZXMgbm90IHN1cHBvcnRlZABPcGVyYXRpb24gbm90IHN1cHBvcnRlZABFbmNyeXB0aW9uIG1ldGhvZCBub3Qgc3VwcG9ydGVkAENvbXByZXNzaW9uIG1ldGhvZCBub3Qgc3VwcG9ydGVkAEVudHJ5IGhhcyBiZWVuIGRlbGV0ZWQAQ29udGFpbmluZyB6aXAgYXJjaGl2ZSB3YXMgY2xvc2VkAENsb3NpbmcgemlwIGFyY2hpdmUgZmFpbGVkAFJlbmFtaW5nIHRlbXBvcmFyeSBmaWxlIGZhaWxlZABFbnRyeSBoYXMgYmVlbiBjaGFuZ2VkAE5vIHBhc3N3b3JkIHByb3ZpZGVkAFdyb25nIHBhc3N3b3JkIHByb3ZpZGVkAFVua25vd24gZXJyb3IgJWQAcmIAcitiAHJ3YQAlcy5YWFhYWFgATkFOAElORgBBRQAxLjIuMTEAL3Byb2Mvc2VsZi9mZC8ALgAobnVsbCkAOiAAUEsGBwBQSwYGAFBLBQYAUEsDBABQSwECAAAAAAAAUgUAANkHAACsCAAAkQgAAIIFAACkBQAAjQUAAMUFAABvCAAANAcAAOkEAAAkBwAAAwcAAK8FAADhBgAAywgAADcIAABBBwAAWgQAALkGAABzBQAAQQQAAFcHAABYCAAAFwgAAKcGAADiCAAA9wgAAP8HAADLBgAAaAUAAMEHAAAgAEGYFAsRAQAAAAEAAAABAAAAAQAAAAEAQbwUCwkBAAAAAQAAAAIAQegUCwEBAEGIFQsBAQBBlBUL+0OWMAd3LGEO7rpRCZkZxG0Hj/RqcDWlY+mjlWSeMojbDqS43Hke6dXgiNnSlytMtgm9fLF+By2455Edv5BkELcd8iCwakhxufPeQb6EfdTaGuvk3W1RtdT0x4XTg1aYbBPAqGtkevli/ezJZYpPXAEU2WwGY2M9D/r1DQiNyCBuO14QaUzkQWDVcnFnotHkAzxH1ARL/YUN0mu1CqX6qLU1bJiyQtbJu9tA+bys42zYMnVc30XPDdbcWT3Rq6ww2SY6AN5RgFHXyBZh0L+19LQhI8SzVpmVus8Ppb24nrgCKAiIBV+y2QzGJOkLsYd8by8RTGhYqx1hwT0tZraQQdx2BnHbAbwg0pgqENXviYWxcR+1tgal5L+fM9S46KLJB3g0+QAPjqgJlhiYDuG7DWp/LT1tCJdsZJEBXGPm9FFra2JhbBzYMGWFTgBi8u2VBmx7pQEbwfQIglfED/XG2bBlUOm3Euq4vot8iLn83x3dYkkt2hXzfNOMZUzU+1hhsk3OUbU6dAC8o+Iwu9RBpd9K15XYPW3E0aT79NbTaulpQ/zZbjRGiGet0Lhg2nMtBETlHQMzX0wKqsl8Dd08cQVQqkECJxAQC76GIAzJJbVoV7OFbyAJ1Ga5n+Rhzg753l6YydkpIpjQsLSo18cXPbNZgQ20LjtcvbetbLrAIIO47bazv5oM4rYDmtKxdDlH1eqvd9KdFSbbBIMW3HMSC2PjhDtklD5qbQ2oWmp6C88O5J3/CZMnrgAKsZ4HfUSTD/DSowiHaPIBHv7CBmldV2L3y2dlgHE2bBnnBmtudhvU/uAr04laetoQzErdZ2/fufn5776OQ763F9WOsGDoo9bWfpPRocTC2DhS8t9P8We70WdXvKbdBrU/SzaySNorDdhMGwqv9koDNmB6BEHD72DfVd9nqO+ObjF5vmlGjLNhyxqDZryg0m8lNuJoUpV3DMwDRwu7uRYCIi8mBVW+O7rFKAu9spJatCsEarNcp//XwjHP0LWLntksHa7eW7DCZJsm8mPsnKNqdQqTbQKpBgmcPzYO64VnB3ITVwAFgkq/lRR6uOKuK7F7OBu2DJuO0pINvtXlt+/cfCHf2wvU0tOGQuLU8fiz3Whug9ofzRa+gVsmufbhd7Bvd0e3GOZaCIhwag//yjsGZlwLARH/nmWPaa5i+NP/a2FFz2wWeOIKoO7SDddUgwROwrMDOWEmZ6f3FmDQTUdpSdt3bj5KatGu3FrW2WYL30DwO9g3U668qcWeu95/z7JH6f+1MBzyvb2KwrrKMJOzU6ajtCQFNtC6kwbXzSlX3lS/Z9kjLnpms7hKYcQCG2hdlCtvKje+C7ShjgzDG98FWo3vAi0AAAAAQTEbGYJiNjLDUy0rBMVsZEX0d32Gp1pWx5ZBTwiK2chJu8LRiujv+svZ9OMMT7WsTX6utY4tg57PHJiHURLCShAj2VPTcPR4kkHvYVXXri4U5rU317WYHJaEgwVZmBuCGKkAm9v6LbCayzapXV135hxsbP/fP0HUng5azaIkhJXjFZ+MIEayp2F3qb6m4ejx59Dz6CSD3sNlssXaqq5dXeufRkQozGtvaf1wdq5rMTnvWiogLAkHC204HBLzNkbfsgddxnFUcO0wZWv09/Mqu7bCMaJ1kRyJNKAHkPu8nxe6jYQOed6pJTjvsjz/efNzvkjoan0bxUE8Kt5YBU958ER+YumHLU/CxhxU2wGKFZRAuw6Ng+gjpsLZOL8NxaA4TPS7IY+nlgrOlo0TCQDMXEgx10WLYvpuylPhd1Rdu7oVbKCj1j+NiJcOlpFQmNfeEanMx9L64eyTy/r1XNdich3meWvetVRAn4RPWVgSDhYZIxUP2nA4JJtBIz2na/1l5lrmfCUJy1dkONBOo66RAeKfihghzKczYP28Kq/hJK3u0D+0LYMSn2yyCYarJEjJ6hVT0ClGfvtod2Xi9nk/L7dIJDZ0GwkdNSoSBPK8U0uzjUhScN5leTHvfmD+8+bnv8L9/nyR0NU9oMvM+jaKg7sHkZp4VLyxOWWnqEuYgzsKqZgiyfq1CYjLrhBPXe9fDmz0Rs0/2W2MDsJ0QxJa8wIjQerBcGzBgEF32EfXNpcG5i2OxbUApYSEG7waikFxW7taaJjod0PZ2WxaHk8tFV9+NgycLRsn3RwAPhIAmLlTMYOgkGKui9FTtZIWxfTdV/TvxJSnwu/Vltn26bwHrqiNHLdr3jGcKu8qhe15a8qsSHDTbxtd+C4qRuHhNt5moAfFf2NU6FQiZfNN5fOyAqTCqRtnkYQwJqCfKbiuxeT5n979Oszz1nv96M+8a6mA/VqymT4Jn7J/OISrsCQcLPEVBzUyRioec3cxB7ThcEj10GtRNoNGeneyXWNO1/rLD+bh0sy1zPmNhNfgShKWrwsjjbbIcKCdiUG7hEZdIwMHbDgaxD8VMYUODihCmE9nA6lUfsD6eVWBy2JMH8U4gV70I5idpw6z3JYVqhsAVOVaMU/8mWJi19hTec4XT+FJVn76UJUt13vUHMxiE4qNLVK7ljSR6Lsf0NmgBuzzfl6twmVHbpFIbC+gU3XoNhI6qQcJI2pUJAgrZT8R5HmnlqVIvI9mG5GkJyqKveC8y/KhjdDrYt79wCPv5tm94bwU/NCnDT+DiiZ+spE/uSTQcPgVy2k7RuZCenf9W7VrZdz0Wn7FNwlT7nY4SPexrgm48J8SoTPMP4py/SSTAAAAADdqwgFu1IQDWb5GAtyoCQfrwssGsnyNBIUWTwW4URMOjzvRD9aFlw3h71UMZPkaCVOT2AgKLZ4KPUdcC3CjJhxHyeQdHneiHykdYB6sCy8bm2HtGsLfqxj1tWkZyPI1Ev+Y9xOmJrERkUxzEBRaPBUjMP4Ueo64Fk3kehfgRk041yyPOY6SyTu5+As6PO5EPwuEhj5SOsA8ZVACPVgXXjZvfZw3NsPaNQGpGDSEv1cxs9WVMOpr0zLdAREzkOVrJKePqSX+Me8nyVstJkxNYiN7J6AiIpnmIBXzJCEotHgqH966K0Zg/ClxCj4o9BxxLcN2syyayPUuraI3L8CNmnD351hxrlkec5kz3HIcJZN3K09RdnLxF3RFm9V1eNyJfk+2S38WCA19IWLPfKR0gHmTHkJ4yqAEev3KxnuwLrxsh0R+bd76OG/pkPpubIa1a1vsd2oCUjFoNTjzaQh/r2I/FW1jZqsrYVHB6WDU16Zl471kZLoDImaNaeBnIMvXSBehFUlOH1NLeXWRSvxj3k/LCRxOkrdaTKXdmE2YmsRGr/AGR/ZOQEXBJIJERDLNQXNYD0Aq5klCHYyLQ1Bo8VRnAjNVPrx1VwnWt1aMwPhTu6o6UuIUfFDVfr5R6DniWt9TIFuG7WZZsYekWDSR610D+ylcWkVvXm0vrV+AGzXht3H34O7PseLZpXPjXLM85mvZ/ucyZ7jlBQ165DhKJu8PIOTuVp6i7GH0YO3k4i/o04jt6Yo2q+u9XGnq8LgT/cfS0fyebJf+qQZV/ywQGvobetj7QsSe+XWuXPhI6QDzf4PC8iY9hPARV0bxlEEJ9KMry/X6lY33zf9P9mBdeNlXN7rYDon82jnjPtu89XHei5+z39Ih9d3lSzfc2Axr1+9mqda22O/UgbIt1QSkYtAzzqDRanDm010aJNIQ/l7FJ5ScxH4q2sZJQBjHzFZXwvs8lcOigtPBlegRwKivTcufxY/KxnvJyPERC8l0B0TMQ22GzRrTwM8tuQLOQJavkXf8bZAuQiuSGSjpk5w+pparVGSX8uoilcWA4JT4x7yfz61+npYTOJyhefqdJG+1mBMFd5lKuzGbfdHzmjA1iY0HX0uMXuENjmmLz4/snYCK2/dCi4JJBIm1I8aIiGSag78OWILmsB6A0drcgVTMk4RjplGFOhgXhw1y1Yag0OKpl7ogqM4EZqr5bqSrfHjrrksSKa8SrG+tJcatrBiB8acv6zOmdlV1pEE/t6XEKfig80M6oar9fKOdl76i0HPEtecZBrS+p0C2ic2CtwzbzbI7sQ+zYg9JsVVli7BoIte7X0gVugb2U7gxnJG5tIrevIPgHL3aXlq/7TSYvgAAAABlZ7y4i8gJqu6vtRJXl2KPMvDeN9xfayW5ONed7yi0xYpPCH1k4L1vAYcB17i/1krd2GryM3ff4FYQY1ifVxlQ+jCl6BSfEPpx+KxCyMB7362nx2dDCHJ1Jm/OzXB/rZUVGBEt+7ekP57QGIcn6M8aQo9zoqwgxrDJR3oIPq8yoFvIjhi1ZzsK0ACHsmk4UC8MX+yX4vBZhYeX5T3Rh4ZltOA63VpPj88/KDN3hhDk6uN3WFIN2O1AaL9R+KH4K/DEn5dIKjAiWk9XnuL2b0l/kwj1x32nQNUYwPxtTtCfNSu3I43FGJafoH8qJxlH/bp8IEECko/0EPfoSKg9WBSbWD+oI7aQHTHT96GJas92FA+oyqzhB3++hGDDBtJwoF63FxzmWbip9DzfFUyF58LR4IB+aQ4vy3trSHfDog8Ny8dosXMpxwRhTKC42fWYb0SQ/9P8flBm7hs32lZNJ7kOKEAFtsbvsKSjiAwcGrDbgX/XZzmReNIr9B9ukwP3JjtmkJqDiD8vke1YkylUYES0MQf4DN+oTR66z/Gm7N+S/om4LkZnF5tUAnAn7LtI8HHeL0zJMID521XnRWOcoD9r+ceD0xdoNsFyD4p5yzdd5K5Q4VxA/1ROJZjo9nOIi64W7zcW+ECCBJ0nPrwkH+khQXhVma/X4IvKsFwzO7ZZ7V7R5VWwflBH1Rns/2whO2IJRofa5+kyyIKOjnDUnu0osflRkF9W5II6MVg6gwmPp+ZuMx8IwYYNbaY6taThQL3BhvwFLylJF0pO9a/zdiIylhGeini+K5gd2ZcgS8n0eC6uSMDAAf3SpWZBahxelvd5OSpPl5afXfLxI+UFGWtNYH7X9Y7RYufrtt5fUo4JwjfptXrZRgBovCG80Oox34iPVmMwYfnWIgSeapq9pr0H2MEBvzZutK1TCQgVmk5yHf8pzqURhnu3dOHHD83ZEJKovqwqRhEZOCN2pYB1ZsbYEAF6YP6uz3KbyXPKIvGkV0eWGO+pOa39zF4RRQbuTXZjifHOjSZE3OhB+GRReS/5NB6TQdqxJlO/1prr6cb5s4yhRQtiDvAZB2lMob5RmzzbNieENZmSllD+Li6ZuVQm/N7onhJxXYx3FuE0zi42qatJihFF5j8DIIGDu3aR4OMT9lxb/VnpSZg+VfEhBoJsRGE+1KrOi8bPqTd+OEF/1l0mw26ziXZ81u7KxG/WHVkKsaHh5B4U84F5qEvXacsTsg53q1yhwrk5xn4BgP6pnOWZFSQLNqA2blEcjqcWZobCcdo+LN5vLEm505TwgQQJlea4sXtJDaMeLrEbSD7SQy1ZbvvD9tvpppFnUR+psMx6zgx0lGG5ZvEGBd4AAAAAdwcwlu4OYSyZCVG6B23EGXBq9I/pY6U1nmSVow7biDJ53Lik4NXpHpfS2YgJtkwrfrF8vee4LQeQvx2RHbcQZGqwIPLzuXFIhL5B3hra1H1t3eTr9NS1UYPThccTbJhWZGuowP1i+XqKZcnsFAFcT2MGbNn6Dz1jjQgN9TtuIMhMaRBe1WBB5KJncXI8A+TRSwTUR9INhf2lCrVrNbWo+kKymGzbu8nWrLz5QDLYbONF31x13NYNz6vRPVkm2TCsUd4AOsjXUYC/0GEWIbT0tVazxCPPupWZuL2lDygCuJ5fBYgIxgzZsrEL6SQvb3yHWGhMEcFhHau2Zi09dtxBkAHbcQaY0iC879UQKnGxhYkGtrUfn7/kpei41DN4B8miDwD5NJYJqI7hDpgYf2oNuwhtPS2RZGyX5mNcAWtrUfQcbGFihWUw2PJiAE5sBpXtGwGle4II9MH1D8RXZbDZxhK36VCLvrjq/LmIfGLdHd8V2i1JjNN88/vUTGVNsmFYOrVRzqO8AHTUuzDiSt+lQT3Yldek0cRt09b0+0Np6Wo0btn8rWeIRtpguNBEBC1zMwMd5aoKTF/dDXzJUAVxPCcCQaq+CxAQyQwghldotSUgb4WzuWbUCc5h5J9e3vkOKdnJmLDQmCLH16i0WbM9Fy60DYG3vVw7wLpsre24gyCav7O2A7biDHSx0prq1Uc5ndJ3rwTbJhVz3BaD42MLEpRkO4QNbWo+empaqOQOzwuTCf+dCgCuJ30HnrHwD5NEhwij0h4B8mhpBsL+92JXXYBlZ8sZbDZxbmsG5/7UG3aJ0yvgENp6WmfdSsz5ud9vjr7v+Re3vkNgsI7V1taj6KHRk3442MLET9/yUtG7Z/GmvFdnP7UG3UiyNkvYDSvarwobTDYDSvZBBHpg32Dvw6hn31Uxbo7vRmm+ecths4y8ZoMaJW/SoFJo4jbMDHeVuwtHAyICFrlVBSYvxbo7vrK9CygrtFqSXLNqBMLX/6e10M8xLNmei1verh2bZMKw7GPyJnVqo5wCbZMKnAkGqesONj9yB2eFBQBXE5W/SoLiuHoUe7Errgy2GziS0o6b5dW+DXzc77cL298hhtPS1PHU4kJo3bP4H9qDboG+Fs32uSZbb7B34Ri3R3eICFrm/w9qcGYGO8oRAQtcj2We//hirmlha//TFmzPRaAK4njXDdLuTgSDVDkDs8KnZyZh0GAW90lpR00+bnfbrtFqStnWWtxA3wtmN9g78Km8rlPeu57FR7LPfzC1/+m9vfIcyrrCilOzkzAktKOmutA2Bc3XBpNU3lcpI9lnv7Nmei7EYUq4XWgbAipvK5S0C743wwyOoVoF3xstAu+NAAAAABkbMUEyNmKCKy1Tw2RsxQR9d/RFVlqnhk9BlsfI2YoI0cK7Sfrv6Irj9NnLrLVPDLWufk2egy2Oh5gcz0rCElFT2SMQePRw02HvQZIurtdVN7XmFByYtdcFg4SWghuYWZsAqRiwLfrbqTbLmuZ3XV3/bGwc1EE/381aDp6VhCSijJ8V46eyRiC+qXdh8ejhpujz0OfD3oMk2sWyZV1drqpERp/rb2vMKHZw/Wk5MWuuICpa7wsHCSwSHDht30Y288ZdB7LtcFRx9GtlMLsq8/eiMcK2iRyRdZAHoDQXn7z7DoSNuiWp3nk8su84c/N5/2roSL5BxRt9WN4qPPB5TwXpYn5Ewk8th9tUHMaUFYoBjQ67QKYj6IO/ONnCOKDFDSG79EwKlqePE42WzlzMAAlF1zFIbvpii3fhU8q6u11Uo6BsFYiNP9aRlg6X3teYUMfMqRHs4frS9frLk3Ji11xreeYdQFS13llPhJ8WDhJYDxUjGSQ4cNo9I0GbZf1rp3zmWuZXywklTtA4ZAGRrqMYip/iM6fMISq8/WCtJOGvtD/Q7p8Sgy2GCbJsyUgkq9BTFer7fkYp4mV3aC8/efY2JEi3HQkbdAQSKjVLU7zyUkiNs3ll3nBgfu8x5+bz/v79wr/V0JF8zMugPYOKNvqakQe7sbxUeKinZTk7g5hLIpipCgm1+skQrsuIX+9dT0b0bA5t2T/NdMIOjPNaEkPqQSMCwWxwwdh3QYCXNtdHji3mBqUAtcW8G4SEcUGKGmhau1tDd+iYWmzZ2RUtTx4MNn5fJxstnD4AHN25mAASoIMxU4uuYpCStVPR3fTFFsTv9FfvwqeU9tmW1a4HvOm3HI2onDHea4Uq7yrKa3nt03BIrPhdG2/hRiouZt424X/FB6BU6FRjTfNlIgKy8+UbqcKkMISRZymfoCbkxa64/d6f+dbzzDrP6P17gKlrvJmyWv2ynwk+q4Q4fywcJLA1BxXxHipGMgcxd3NIcOG0UWvQ9XpGgzZjXbJ3y/rXTtLh5g/5zLXM4NeEja+WEkq2jSMLnaBwyIS7QYkDI11GGjhsBzEVP8QoDg6FZ0+YQn5UqQNVefrATGLLgYE4xR+YI/Resw6nnaoVltzlVAAb/E8xWtdiYpnOeVPYSeFPF1D6flZ71y2VYswc1C2NihM0lrtSH7vokQag2dBefvPsR2XCrWxIkW51U6AvOhI26CMJB6kIJFRqET9lK5aneeSPvEilpJEbZr2KKifyy7zg69CNocD93mLZ5u8jFLzhvQ2n0PwmioM/P5GyfnDQJLlpyxX4QuZGO1v9d3rcZWu1xX5a9O5TCTf3SDh2uAmusaESn/CKP8wzkyT9cgAAAAABwmo3A4TUbgJGvlkHCajcBsvC6wSNfLIFTxaFDhNRuA/RO48Nl4XWDFXv4Qka+WQI2JNTCp4tCgtcRz0cJqNwHeTJRx+idx4eYB0pGy8LrBrtYZsYq9/CGWm19RI18sgT95j/EbEmphBzTJEVPFoUFP4wIxa4jnoXeuRNOE1G4DmPLNc7yZKOOgv4uT9E7jw+hoQLPMA6Uj0CUGU2XhdYN5x9bzXawzY0GKkBMVe/hDCV1bMy02vqMxEB3SRr5ZAlqY+nJ+8x/iYtW8kjYk1MIqAneyDmmSIhJPMVKni0KCu63h8p/GBGKD4KcS1xHPQss3bDLvXImi83oq1wmo3AcVjn93MeWa5y3DOZd5MlHHZRTyt0F/FyddWbRX6J3Hh/S7ZPfQ0IFnzPYiF5gHSkeEIek3oEoMp7xsr9bLwusG1+RIdvOPrebvqQ6Wu1hmxqd+xbaDFSAmnzODVir38IY20VP2Erq2Zg6cFRZabX1GRkveNmIgO6Z+BpjUjXyyBJFaEXS1MfTkqRdXlP3mP8ThwJy0xat5JNmN2lRsSamEcG8K9FQE72RIIkwUHNMkRAD1hzQknmKkOLjB1U8WhQVTMCZ1d1vD5Wt9YJU/jAjFI6qrtQfBTiUb5+1VriOehbIFPfWWbthlikh7Fd65E0XCn7A15vRVpfrS9t4TUbgOD3cbfisc/u43Ol2eY8s1zn/tlr5bhnMuR6DQXvJko47uQgD+yinlbtYPRh6C/i5OntiNPrqzaK6mlcvf0TuPD80dLH/pdsnv9VBqn6GhAs+9h6G/mexEL4XK518wDpSPLCg3/whD0m8UZXEfQJQZT1yyuj942V+vZP/83ZeF1g2Lo3V9r8iQ7bPuM53nH1vN+zn4vd9SHS3DdL5ddrDNjWqWbv1O/YttUtsoHQYqQE0aDOM9PmcGrSJBpdxV7+EMSclCfG2ip+xxhAScJXVszDlTz7wdOCosAR6JXLTa+oyo/Fn8jJe8bJCxHxzEQHdM2GbUPPwNMazgK5LZGvlkCQbfx3kitCLpPpKBmWpj6cl2RUq5Ui6vKU4IDFn7zH+J5+rc+cOBOWnfp5oZi1bySZdwUTmzG7Sprz0X2NiTUwjEtfB44N4V6Pz4tpioCd7ItC99uJBEmCiMYjtYOaZIiCWA6/gB6w5oHc2tGEk8xUhVGmY4cXGDqG1XINqeLQoKggupeqZgTOq6Ru+a7reHyvKRJLrW+sEqytxiWn8YEYpjPrL6R1VXaltz9BoPgpxKE6Q/OjfP2qor6XnbXEc9C0BhnntkCnvreCzYmyzdsMsw+xO7FJD2Kwi2VVu9ciaLoVSF+4U/YGuZGcMbzeirS9HOCDv1pe2r6YNO0AAAAAuLxnZaoJyIsSta/uj2KXVzfe8DIla1/cndc4ucW0KO99CE+Kb73gZNcBhwFK1r+48mrY3eDfdzNYYxBWUBlXn+ilMPr6EJ8UQqz4cd97wMhnx6etdXIIQ83ObyaVrX9wLREYFT+kt/uHGNCeGs/oJ6Jzj0KwxiCsCHpHyaAyrz4YjshbCjtntbKHANAvUDhpl+xfDIVZ8OI95ZeHZYaH0d064LTPj09adzMoP+rkEIZSWHfjQO3YDfhRv2jwK/ihSJefxFoiMCrinldPf0lv9sf1CJPVQKd9bfzAGDWf0E6NI7crn5YYxScqf6C6/UcZAkEgfBD0j5KoSOj3mxRYPSOoP1gxHZC2iaH30xR2z2qsyqgPvn8H4QbDYIReoHDS5hwXt/SpuFlMFd880cLnhWl+gOB7yy8Ow3dIa8sND6JzsWjHYQTHKdm4oExEb5j1/NP/kO5mUH5W2jcbDrknTbYFQCiksO/GHAyIo4HbsBo5Z9d/K9J4kZNuH/Q7JvcDg5qQZpEvP4gpk1jttERgVAz4BzEeTajfpvHPuv6S3+xGLriJVJsXZ+wncAJx8Ei7yUwv3tv5gDBjRedVaz+gnNODx/nBNmgXeYoPcuRdN8tc4VCuTlT/QPbomCWui4hzFjfvFgSCQPi8PiedIekfJJlVeEGL4NevM1ywyu1ZtjtV5dFeR1B+sP/sGdViOyFs2odGCcgy6edwjo6CKO2e1JBR+bGC5FZfOlgxOqePCYMfM27mDYbBCLU6pm29QOGkBfyGwRdJKS+v9U5KMiJ284qeEZaYK754IJfZHXj0yUvASK4u0v0BwGpBZqX3ll4cTyo5eV2flpflI/HyTWsZBfXXfmDnYtGOX96268IJjlJ6tek3aABG2dC8IbyI3zHqMGNWjyLW+WGaap4EB72mvb8BwdittG42FQgJUx1yTpqlzin/t3uGEQ/H4XSSENnNKqy+qDgZEUaApXYj2MZmdWB6ARByz67+ynPJm1ek8SLvGJZH/a05qUURXsx2Te4GzvGJY9xEJo1k+EHo+S95UUGTHjRTJrHa65rWv7P5xukLRaGMGfAOYqFMaQc8m1G+hCc225aSmTUuLv5QJlS5mZ7o3vyMXXESNOEWd6k2Ls4RikmrAz/mRbuDgSDj4JF2W1z2E0npWf3xVT6YbIIGIdQ+YUTGi86qfjepz9Z/QThuwyZdfHaJs8TK7tZZHdZv4aGxCvMUHuRLqHmBE8tp16t3DrK5wqFcAX7GOZyp/oAkFZnlNqA2C44cUW6GZhanPtpxwixv3iyU07lJCQSB8LG45pWjDUl7G7EuHkPSPkj7blkt6dv2w1FnkabMsKkfdAzOema5YZTeBQbxAAA6JjsmZSZmJmMmYCYiINglyyXZJUImQCZqJmsmPCa6JcQllSE8ILYApwCsJaghkSGTIZIhkCEfIpQhsiW8JSAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIAMwA0ADUANgA3ADgAOQA6ADsAPAA9AD4APwBAAEEAQgBDAEQARQBGAEcASABJAEoASwBMAE0ATgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAF8AYABhAGIAYwBkAGUAZgBnAGgAaQBqAGsAbABtAG4AbwBwAHEAcgBzAHQAdQB2AHcAeAB5AHoAewB8AH0AfgACI8cA/ADpAOIA5ADgAOUA5wDqAOsA6ADvAO4A7ADEAMUAyQDmAMYA9AD2APIA+wD5AP8A1gDcAKIAowClAKcgkgHhAO0A8wD6APEA0QCqALoAvwAQI6wAvQC8AKEAqwC7AJElkiWTJQIlJCVhJWIlViVVJWMlUSVXJV0lXCVbJRAlFCU0JSwlHCUAJTwlXiVfJVolVCVpJWYlYCVQJWwlZyVoJWQlZSVZJVglUiVTJWslaiUYJQwliCWEJYwlkCWAJbED3wCTA8ADowPDA7UAxAOmA5gDqQO0Ax4ixgO1AykiYSKxAGUiZCIgIyEj9wBIIrAAGSK3ABoifyCyAKAloABBoNkACyYUBAAAtgcAAHoJAACZBQAAWwUAALoFAAAABAAARQUAAM8FAAB6CQBB0dkAC7YQAQIDBAQFBQYGBgYHBwcHCAgICAgICAgJCQkJCQkJCQoKCgoKCgoKCgoKCgoKCgoLCwsLCwsLCwsLCwsLCwsLDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PAAAQERISExMUFBQUFRUVFRYWFhYWFhYWFxcXFxcXFxcYGBgYGBgYGBgYGBgYGBgYGRkZGRkZGRkZGRkZGRkZGRoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxscHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHQABAgMEBQYHCAgJCQoKCwsMDAwMDQ0NDQ4ODg4PDw8PEBAQEBAQEBARERERERERERISEhISEhISExMTExMTExMUFBQUFBQUFBQUFBQUFBQUFRUVFRUVFRUVFRUVFRUVFRYWFhYWFhYWFhYWFhYWFhYXFxcXFxcXFxcXFxcXFxcXGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxwQMAAAEDUAAAEBAAAeAQAADwAAAJA0AACQNQAAAAAAAB4AAAAPAAAAAAAAABA2AAAAAAAAEwAAAAcAAAAAAAAADAAIAIwACABMAAgAzAAIACwACACsAAgAbAAIAOwACAAcAAgAnAAIAFwACADcAAgAPAAIALwACAB8AAgA/AAIAAIACACCAAgAQgAIAMIACAAiAAgAogAIAGIACADiAAgAEgAIAJIACABSAAgA0gAIADIACACyAAgAcgAIAPIACAAKAAgAigAIAEoACADKAAgAKgAIAKoACABqAAgA6gAIABoACACaAAgAWgAIANoACAA6AAgAugAIAHoACAD6AAgABgAIAIYACABGAAgAxgAIACYACACmAAgAZgAIAOYACAAWAAgAlgAIAFYACADWAAgANgAIALYACAB2AAgA9gAIAA4ACACOAAgATgAIAM4ACAAuAAgArgAIAG4ACADuAAgAHgAIAJ4ACABeAAgA3gAIAD4ACAC+AAgAfgAIAP4ACAABAAgAgQAIAEEACADBAAgAIQAIAKEACABhAAgA4QAIABEACACRAAgAUQAIANEACAAxAAgAsQAIAHEACADxAAgACQAIAIkACABJAAgAyQAIACkACACpAAgAaQAIAOkACAAZAAgAmQAIAFkACADZAAgAOQAIALkACAB5AAgA+QAIAAUACACFAAgARQAIAMUACAAlAAgApQAIAGUACADlAAgAFQAIAJUACABVAAgA1QAIADUACAC1AAgAdQAIAPUACAANAAgAjQAIAE0ACADNAAgALQAIAK0ACABtAAgA7QAIAB0ACACdAAgAXQAIAN0ACAA9AAgAvQAIAH0ACAD9AAgAEwAJABMBCQCTAAkAkwEJAFMACQBTAQkA0wAJANMBCQAzAAkAMwEJALMACQCzAQkAcwAJAHMBCQDzAAkA8wEJAAsACQALAQkAiwAJAIsBCQBLAAkASwEJAMsACQDLAQkAKwAJACsBCQCrAAkAqwEJAGsACQBrAQkA6wAJAOsBCQAbAAkAGwEJAJsACQCbAQkAWwAJAFsBCQDbAAkA2wEJADsACQA7AQkAuwAJALsBCQB7AAkAewEJAPsACQD7AQkABwAJAAcBCQCHAAkAhwEJAEcACQBHAQkAxwAJAMcBCQAnAAkAJwEJAKcACQCnAQkAZwAJAGcBCQDnAAkA5wEJABcACQAXAQkAlwAJAJcBCQBXAAkAVwEJANcACQDXAQkANwAJADcBCQC3AAkAtwEJAHcACQB3AQkA9wAJAPcBCQAPAAkADwEJAI8ACQCPAQkATwAJAE8BCQDPAAkAzwEJAC8ACQAvAQkArwAJAK8BCQBvAAkAbwEJAO8ACQDvAQkAHwAJAB8BCQCfAAkAnwEJAF8ACQBfAQkA3wAJAN8BCQA/AAkAPwEJAL8ACQC/AQkAfwAJAH8BCQD/AAkA/wEJAAAABwBAAAcAIAAHAGAABwAQAAcAUAAHADAABwBwAAcACAAHAEgABwAoAAcAaAAHABgABwBYAAcAOAAHAHgABwAEAAcARAAHACQABwBkAAcAFAAHAFQABwA0AAcAdAAHAAMACACDAAgAQwAIAMMACAAjAAgAowAIAGMACADjAAgAAAAFABAABQAIAAUAGAAFAAQABQAUAAUADAAFABwABQACAAUAEgAFAAoABQAaAAUABgAFABYABQAOAAUAHgAFAAEABQARAAUACQAFABkABQAFAAUAFQAFAA0ABQAdAAUAAwAFABMABQALAAUAGwAFAAcABQAXAAUAQbDqAAtNAQAAAAEAAAABAAAAAQAAAAIAAAACAAAAAgAAAAIAAAADAAAAAwAAAAMAAAADAAAABAAAAAQAAAAEAAAABAAAAAUAAAAFAAAABQAAAAUAQaDrAAtlAQAAAAEAAAACAAAAAgAAAAMAAAADAAAABAAAAAQAAAAFAAAABQAAAAYAAAAGAAAABwAAAAcAAAAIAAAACAAAAAkAAAAJAAAACgAAAAoAAAALAAAACwAAAAwAAAAMAAAADQAAAA0AQdDsAAsjAgAAAAMAAAAHAAAAAAAAABAREgAIBwkGCgULBAwDDQIOAQ8AQYTtAAtpAQAAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAKAAAADAAAAA4AAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAwAAAAOAAAAEAAAABQAAAAYAAAAHAAAACAAAAAoAAAAMAAAADgAEGE7gALegEAAAACAAAAAwAAAAQAAAAGAAAACAAAAAwAAAAQAAAAGAAAACAAAAAwAAAAQAAAAGAAAACAAAAAwAAAAAABAACAAQAAAAIAAAADAAAABAAAAAYAAAAIAAAADAAAABAAAAAYAAAAIAAAADAAAABAAAAAYAAAMS4yLjExAEGI7wALbQcAAAAEAAQACAAEAAgAAAAEAAUAEAAIAAgAAAAEAAYAIAAgAAgAAAAEAAQAEAAQAAkAAAAIABAAIAAgAAkAAAAIABAAgACAAAkAAAAIACAAgAAAAQkAAAAgAIAAAgEABAkAAAAgAAIBAgEAEAkAQYDwAAulAgMABAAFAAYABwAIAAkACgALAA0ADwARABMAFwAbAB8AIwArADMAOwBDAFMAYwBzAIMAowDDAOMAAgEAAAAAAAAQABAAEAAQABAAEAAQABAAEQARABEAEQASABIAEgASABMAEwATABMAFAAUABQAFAAVABUAFQAVABAATQDKAAAAAQACAAMABAAFAAcACQANABEAGQAhADEAQQBhAIEAwQABAYEBAQIBAwEEAQYBCAEMARABGAEgATABQAFgAAAAABAAEAAQABAAEQARABIAEgATABMAFAAUABUAFQAWABYAFwAXABgAGAAZABkAGgAaABsAGwAcABwAHQAdAEAAQAAQABEAEgAAAAgABwAJAAYACgAFAAsABAAMAAMADQACAA4AAQAPAEGw8gALwRFgBwAAAAhQAAAIEAAUCHMAEgcfAAAIcAAACDAAAAnAABAHCgAACGAAAAggAAAJoAAACAAAAAiAAAAIQAAACeAAEAcGAAAIWAAACBgAAAmQABMHOwAACHgAAAg4AAAJ0AARBxEAAAhoAAAIKAAACbAAAAgIAAAIiAAACEgAAAnwABAHBAAACFQAAAgUABUI4wATBysAAAh0AAAINAAACcgAEQcNAAAIZAAACCQAAAmoAAAIBAAACIQAAAhEAAAJ6AAQBwgAAAhcAAAIHAAACZgAFAdTAAAIfAAACDwAAAnYABIHFwAACGwAAAgsAAAJuAAACAwAAAiMAAAITAAACfgAEAcDAAAIUgAACBIAFQijABMHIwAACHIAAAgyAAAJxAARBwsAAAhiAAAIIgAACaQAAAgCAAAIggAACEIAAAnkABAHBwAACFoAAAgaAAAJlAAUB0MAAAh6AAAIOgAACdQAEgcTAAAIagAACCoAAAm0AAAICgAACIoAAAhKAAAJ9AAQBwUAAAhWAAAIFgBACAAAEwczAAAIdgAACDYAAAnMABEHDwAACGYAAAgmAAAJrAAACAYAAAiGAAAIRgAACewAEAcJAAAIXgAACB4AAAmcABQHYwAACH4AAAg+AAAJ3AASBxsAAAhuAAAILgAACbwAAAgOAAAIjgAACE4AAAn8AGAHAAAACFEAAAgRABUIgwASBx8AAAhxAAAIMQAACcIAEAcKAAAIYQAACCEAAAmiAAAIAQAACIEAAAhBAAAJ4gAQBwYAAAhZAAAIGQAACZIAEwc7AAAIeQAACDkAAAnSABEHEQAACGkAAAgpAAAJsgAACAkAAAiJAAAISQAACfIAEAcEAAAIVQAACBUAEAgCARMHKwAACHUAAAg1AAAJygARBw0AAAhlAAAIJQAACaoAAAgFAAAIhQAACEUAAAnqABAHCAAACF0AAAgdAAAJmgAUB1MAAAh9AAAIPQAACdoAEgcXAAAIbQAACC0AAAm6AAAIDQAACI0AAAhNAAAJ+gAQBwMAAAhTAAAIEwAVCMMAEwcjAAAIcwAACDMAAAnGABEHCwAACGMAAAgjAAAJpgAACAMAAAiDAAAIQwAACeYAEAcHAAAIWwAACBsAAAmWABQHQwAACHsAAAg7AAAJ1gASBxMAAAhrAAAIKwAACbYAAAgLAAAIiwAACEsAAAn2ABAHBQAACFcAAAgXAEAIAAATBzMAAAh3AAAINwAACc4AEQcPAAAIZwAACCcAAAmuAAAIBwAACIcAAAhHAAAJ7gAQBwkAAAhfAAAIHwAACZ4AFAdjAAAIfwAACD8AAAneABIHGwAACG8AAAgvAAAJvgAACA8AAAiPAAAITwAACf4AYAcAAAAIUAAACBAAFAhzABIHHwAACHAAAAgwAAAJwQAQBwoAAAhgAAAIIAAACaEAAAgAAAAIgAAACEAAAAnhABAHBgAACFgAAAgYAAAJkQATBzsAAAh4AAAIOAAACdEAEQcRAAAIaAAACCgAAAmxAAAICAAACIgAAAhIAAAJ8QAQBwQAAAhUAAAIFAAVCOMAEwcrAAAIdAAACDQAAAnJABEHDQAACGQAAAgkAAAJqQAACAQAAAiEAAAIRAAACekAEAcIAAAIXAAACBwAAAmZABQHUwAACHwAAAg8AAAJ2QASBxcAAAhsAAAILAAACbkAAAgMAAAIjAAACEwAAAn5ABAHAwAACFIAAAgSABUIowATByMAAAhyAAAIMgAACcUAEQcLAAAIYgAACCIAAAmlAAAIAgAACIIAAAhCAAAJ5QAQBwcAAAhaAAAIGgAACZUAFAdDAAAIegAACDoAAAnVABIHEwAACGoAAAgqAAAJtQAACAoAAAiKAAAISgAACfUAEAcFAAAIVgAACBYAQAgAABMHMwAACHYAAAg2AAAJzQARBw8AAAhmAAAIJgAACa0AAAgGAAAIhgAACEYAAAntABAHCQAACF4AAAgeAAAJnQAUB2MAAAh+AAAIPgAACd0AEgcbAAAIbgAACC4AAAm9AAAIDgAACI4AAAhOAAAJ/QBgBwAAAAhRAAAIEQAVCIMAEgcfAAAIcQAACDEAAAnDABAHCgAACGEAAAghAAAJowAACAEAAAiBAAAIQQAACeMAEAcGAAAIWQAACBkAAAmTABMHOwAACHkAAAg5AAAJ0wARBxEAAAhpAAAIKQAACbMAAAgJAAAIiQAACEkAAAnzABAHBAAACFUAAAgVABAIAgETBysAAAh1AAAINQAACcsAEQcNAAAIZQAACCUAAAmrAAAIBQAACIUAAAhFAAAJ6wAQBwgAAAhdAAAIHQAACZsAFAdTAAAIfQAACD0AAAnbABIHFwAACG0AAAgtAAAJuwAACA0AAAiNAAAITQAACfsAEAcDAAAIUwAACBMAFQjDABMHIwAACHMAAAgzAAAJxwARBwsAAAhjAAAIIwAACacAAAgDAAAIgwAACEMAAAnnABAHBwAACFsAAAgbAAAJlwAUB0MAAAh7AAAIOwAACdcAEgcTAAAIawAACCsAAAm3AAAICwAACIsAAAhLAAAJ9wAQBwUAAAhXAAAIFwBACAAAEwczAAAIdwAACDcAAAnPABEHDwAACGcAAAgnAAAJrwAACAcAAAiHAAAIRwAACe8AEAcJAAAIXwAACB8AAAmfABQHYwAACH8AAAg/AAAJ3wASBxsAAAhvAAAILwAACb8AAAgPAAAIjwAACE8AAAn/ABAFAQAXBQEBEwURABsFARARBQUAGQUBBBUFQQAdBQFAEAUDABgFAQIUBSEAHAUBIBIFCQAaBQEIFgWBAEAFAAAQBQIAFwWBARMFGQAbBQEYEQUHABkFAQYVBWEAHQUBYBAFBAAYBQEDFAUxABwFATASBQ0AGgUBDBYFwQBABQAAEQAKABEREQAAAAAFAAAAAAAACQAAAAALAAAAAAAAAAARAA8KERERAwoHAAEACQsLAAAJBgsAAAsABhEAAAAREREAQYGEAQshCwAAAAAAAAAAEQAKChEREQAKAAACAAkLAAAACQALAAALAEG7hAELAQwAQceEAQsVDAAAAAAMAAAAAAkMAAAAAAAMAAAMAEH1hAELAQ4AQYGFAQsVDQAAAAQNAAAAAAkOAAAAAAAOAAAOAEGvhQELARAAQbuFAQseDwAAAAAPAAAAAAkQAAAAAAAQAAAQAAASAAAAEhISAEHyhQELDhIAAAASEhIAAAAAAAAJAEGjhgELAQsAQa+GAQsVCgAAAAAKAAAAAAkLAAAAAAALAAALAEHdhgELAQwAQemGAQsnDAAAAAAMAAAAAAkMAAAAAAAMAAAMAAAwMTIzNDU2Nzg5QUJDREVGAEG0hwELARkAQduHAQsF//////8AQaCIAQtXGRJEOwI/LEcUPTMwChsGRktFNw9JDo4XA0AdPGkrNh9KLRwBICUpIQgMFRYiLhA4Pgs0MRhkdHV2L0EJfzkRI0MyQomKiwUEJignDSoeNYwHGkiTE5SVAEGAiQELig5JbGxlZ2FsIGJ5dGUgc2VxdWVuY2UARG9tYWluIGVycm9yAFJlc3VsdCBub3QgcmVwcmVzZW50YWJsZQBOb3QgYSB0dHkAUGVybWlzc2lvbiBkZW5pZWQAT3BlcmF0aW9uIG5vdCBwZXJtaXR0ZWQATm8gc3VjaCBmaWxlIG9yIGRpcmVjdG9yeQBObyBzdWNoIHByb2Nlc3MARmlsZSBleGlzdHMAVmFsdWUgdG9vIGxhcmdlIGZvciBkYXRhIHR5cGUATm8gc3BhY2UgbGVmdCBvbiBkZXZpY2UAT3V0IG9mIG1lbW9yeQBSZXNvdXJjZSBidXN5AEludGVycnVwdGVkIHN5c3RlbSBjYWxsAFJlc291cmNlIHRlbXBvcmFyaWx5IHVuYXZhaWxhYmxlAEludmFsaWQgc2VlawBDcm9zcy1kZXZpY2UgbGluawBSZWFkLW9ubHkgZmlsZSBzeXN0ZW0ARGlyZWN0b3J5IG5vdCBlbXB0eQBDb25uZWN0aW9uIHJlc2V0IGJ5IHBlZXIAT3BlcmF0aW9uIHRpbWVkIG91dABDb25uZWN0aW9uIHJlZnVzZWQASG9zdCBpcyBkb3duAEhvc3QgaXMgdW5yZWFjaGFibGUAQWRkcmVzcyBpbiB1c2UAQnJva2VuIHBpcGUASS9PIGVycm9yAE5vIHN1Y2ggZGV2aWNlIG9yIGFkZHJlc3MAQmxvY2sgZGV2aWNlIHJlcXVpcmVkAE5vIHN1Y2ggZGV2aWNlAE5vdCBhIGRpcmVjdG9yeQBJcyBhIGRpcmVjdG9yeQBUZXh0IGZpbGUgYnVzeQBFeGVjIGZvcm1hdCBlcnJvcgBJbnZhbGlkIGFyZ3VtZW50AEFyZ3VtZW50IGxpc3QgdG9vIGxvbmcAU3ltYm9saWMgbGluayBsb29wAEZpbGVuYW1lIHRvbyBsb25nAFRvbyBtYW55IG9wZW4gZmlsZXMgaW4gc3lzdGVtAE5vIGZpbGUgZGVzY3JpcHRvcnMgYXZhaWxhYmxlAEJhZCBmaWxlIGRlc2NyaXB0b3IATm8gY2hpbGQgcHJvY2VzcwBCYWQgYWRkcmVzcwBGaWxlIHRvbyBsYXJnZQBUb28gbWFueSBsaW5rcwBObyBsb2NrcyBhdmFpbGFibGUAUmVzb3VyY2UgZGVhZGxvY2sgd291bGQgb2NjdXIAU3RhdGUgbm90IHJlY292ZXJhYmxlAFByZXZpb3VzIG93bmVyIGRpZWQAT3BlcmF0aW9uIGNhbmNlbGVkAEZ1bmN0aW9uIG5vdCBpbXBsZW1lbnRlZABObyBtZXNzYWdlIG9mIGRlc2lyZWQgdHlwZQBJZGVudGlmaWVyIHJlbW92ZWQARGV2aWNlIG5vdCBhIHN0cmVhbQBObyBkYXRhIGF2YWlsYWJsZQBEZXZpY2UgdGltZW91dABPdXQgb2Ygc3RyZWFtcyByZXNvdXJjZXMATGluayBoYXMgYmVlbiBzZXZlcmVkAFByb3RvY29sIGVycm9yAEJhZCBtZXNzYWdlAEZpbGUgZGVzY3JpcHRvciBpbiBiYWQgc3RhdGUATm90IGEgc29ja2V0AERlc3RpbmF0aW9uIGFkZHJlc3MgcmVxdWlyZWQATWVzc2FnZSB0b28gbGFyZ2UAUHJvdG9jb2wgd3JvbmcgdHlwZSBmb3Igc29ja2V0AFByb3RvY29sIG5vdCBhdmFpbGFibGUAUHJvdG9jb2wgbm90IHN1cHBvcnRlZABTb2NrZXQgdHlwZSBub3Qgc3VwcG9ydGVkAE5vdCBzdXBwb3J0ZWQAUHJvdG9jb2wgZmFtaWx5IG5vdCBzdXBwb3J0ZWQAQWRkcmVzcyBmYW1pbHkgbm90IHN1cHBvcnRlZCBieSBwcm90b2NvbABBZGRyZXNzIG5vdCBhdmFpbGFibGUATmV0d29yayBpcyBkb3duAE5ldHdvcmsgdW5yZWFjaGFibGUAQ29ubmVjdGlvbiByZXNldCBieSBuZXR3b3JrAENvbm5lY3Rpb24gYWJvcnRlZABObyBidWZmZXIgc3BhY2UgYXZhaWxhYmxlAFNvY2tldCBpcyBjb25uZWN0ZWQAU29ja2V0IG5vdCBjb25uZWN0ZWQAQ2Fubm90IHNlbmQgYWZ0ZXIgc29ja2V0IHNodXRkb3duAE9wZXJhdGlvbiBhbHJlYWR5IGluIHByb2dyZXNzAE9wZXJhdGlvbiBpbiBwcm9ncmVzcwBTdGFsZSBmaWxlIGhhbmRsZQBSZW1vdGUgSS9PIGVycm9yAFF1b3RhIGV4Y2VlZGVkAE5vIG1lZGl1bSBmb3VuZABXcm9uZyBtZWRpdW0gdHlwZQBObyBlcnJvciBpbmZvcm1hdGlvbgBBkJcBC1JQUFAACgAAAAsAAAAMAAAADQAAAA4AAAAPAAAAEAAAABEAAAASAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEQAAAAEAAAAIAAAAlEsAALRLAEGQmQELAgxQAEHImQELCR8AAADkTAAAAwBB5JkBC4wBLfRRWM+MscBG9rXLKTEDxwRbcDC0Xf0geH+LmthZKVBoSImrp1YDbP+3zYg/1He0K6WjcPG65Kj8QYP92W/hinovLXSWBx8NCV4Ddixw90ClLKdvV0GoqnTfoFhkA0rHxDxTrq9fGAQVseNtKIarDKS/Q/DpUIE5VxZSN/////////////////////8="; +if (!isDataURI(wasmBinaryFile)) { + wasmBinaryFile = locateFile(wasmBinaryFile); +} +function getBinary(file) { + try { + if (file == wasmBinaryFile && wasmBinary) { + return new Uint8Array(wasmBinary); + } + var binary = tryParseAsDataURI(file); + if (binary) { + return binary; + } + if (readBinary) { + return readBinary(file); + } else { + throw "sync fetching of the wasm failed: you can preload it to Module['wasmBinary'] manually, or emcc.py will do that for you when generating HTML (but not JS)"; + } + } catch (err) { + abort(err); + } +} +function instantiateSync(file, info) { + var instance; + var module; + var binary; + try { + binary = getBinary(file); + module = new WebAssembly.Module(binary); + instance = new WebAssembly.Instance(module, info); + } catch (e) { + var str = e.toString(); + err("failed to compile wasm module: " + str); + if (str.includes("imported Memory") || str.includes("memory import")) { + err( + "Memory size incompatibility issues may be due to changing INITIAL_MEMORY at runtime to something too large. Use ALLOW_MEMORY_GROWTH to allow any size memory (and also make sure not to set INITIAL_MEMORY at runtime to something smaller than it was at compile time)." + ); + } + throw e; + } + return [instance, module]; +} +function createWasm() { + var info = { a: asmLibraryArg }; + function receiveInstance(instance, module) { + var exports = instance.exports; + Module["asm"] = exports; + wasmMemory = Module["asm"]["u"]; + updateGlobalBufferAndViews(wasmMemory.buffer); + wasmTable = Module["asm"]["za"]; + addOnInit(Module["asm"]["v"]); + removeRunDependency("wasm-instantiate"); + } + addRunDependency("wasm-instantiate"); + if (Module["instantiateWasm"]) { + try { + var exports = Module["instantiateWasm"](info, receiveInstance); + return exports; + } catch (e) { + err("Module.instantiateWasm callback failed with error: " + e); + return false; + } + } + var result = instantiateSync(wasmBinaryFile, info); + receiveInstance(result[0]); + return Module["asm"]; +} +var tempDouble; +var tempI64; +function callRuntimeCallbacks(callbacks) { + while (callbacks.length > 0) { + var callback = callbacks.shift(); + if (typeof callback == "function") { + callback(Module); + continue; + } + var func = callback.func; + if (typeof func === "number") { + if (callback.arg === undefined) { + wasmTable.get(func)(); + } else { + wasmTable.get(func)(callback.arg); + } + } else { + func(callback.arg === undefined ? null : callback.arg); + } + } +} +function _gmtime_r(time, tmPtr) { + var date = new Date(HEAP32[time >> 2] * 1e3); + HEAP32[tmPtr >> 2] = date.getUTCSeconds(); + HEAP32[(tmPtr + 4) >> 2] = date.getUTCMinutes(); + HEAP32[(tmPtr + 8) >> 2] = date.getUTCHours(); + HEAP32[(tmPtr + 12) >> 2] = date.getUTCDate(); + HEAP32[(tmPtr + 16) >> 2] = date.getUTCMonth(); + HEAP32[(tmPtr + 20) >> 2] = date.getUTCFullYear() - 1900; + HEAP32[(tmPtr + 24) >> 2] = date.getUTCDay(); + HEAP32[(tmPtr + 36) >> 2] = 0; + HEAP32[(tmPtr + 32) >> 2] = 0; + var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); + var yday = ((date.getTime() - start) / (1e3 * 60 * 60 * 24)) | 0; + HEAP32[(tmPtr + 28) >> 2] = yday; + if (!_gmtime_r.GMTString) _gmtime_r.GMTString = allocateUTF8("GMT"); + HEAP32[(tmPtr + 40) >> 2] = _gmtime_r.GMTString; + return tmPtr; +} +function ___gmtime_r(a0, a1) { + return _gmtime_r(a0, a1); +} +var PATH = { + splitPath: function(filename) { + var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; + return splitPathRe.exec(filename).slice(1); + }, + normalizeArray: function(parts, allowAboveRoot) { + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === ".") { + parts.splice(i, 1); + } else if (last === "..") { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + if (allowAboveRoot) { + for (; up; up--) { + parts.unshift(".."); + } + } + return parts; + }, + normalize: function(path) { + var isAbsolute = path.charAt(0) === "/", + trailingSlash = path.substr(-1) === "/"; + path = PATH.normalizeArray( + path.split("/").filter(function(p) { + return !!p; + }), + !isAbsolute + ).join("/"); + if (!path && !isAbsolute) { + path = "."; + } + if (path && trailingSlash) { + path += "/"; + } + return (isAbsolute ? "/" : "") + path; + }, + dirname: function(path) { + var result = PATH.splitPath(path), + root = result[0], + dir = result[1]; + if (!root && !dir) { + return "."; + } + if (dir) { + dir = dir.substr(0, dir.length - 1); + } + return root + dir; + }, + basename: function(path) { + if (path === "/") return "/"; + path = PATH.normalize(path); + path = path.replace(/\/$/, ""); + var lastSlash = path.lastIndexOf("/"); + if (lastSlash === -1) return path; + return path.substr(lastSlash + 1); + }, + extname: function(path) { + return PATH.splitPath(path)[3]; + }, + join: function() { + var paths = Array.prototype.slice.call(arguments, 0); + return PATH.normalize(paths.join("/")); + }, + join2: function(l, r) { + return PATH.normalize(l + "/" + r); + } +}; +function getRandomDevice() { + if ( + typeof crypto === "object" && + typeof crypto["getRandomValues"] === "function" + ) { + var randomBuffer = new Uint8Array(1); + return function() { + crypto.getRandomValues(randomBuffer); + return randomBuffer[0]; + }; + } else if (ENVIRONMENT_IS_NODE) { + try { + var crypto_module = __webpack_require__(417); + return function() { + return crypto_module["randomBytes"](1)[0]; + }; + } catch (e) {} + } + return function() { + abort("randomDevice"); + }; +} +var PATH_FS = { + resolve: function() { + var resolvedPath = "", + resolvedAbsolute = false; + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = i >= 0 ? arguments[i] : FS.cwd(); + if (typeof path !== "string") { + throw new TypeError("Arguments to path.resolve must be strings"); + } else if (!path) { + return ""; + } + resolvedPath = path + "/" + resolvedPath; + resolvedAbsolute = path.charAt(0) === "/"; + } + resolvedPath = PATH.normalizeArray( + resolvedPath.split("/").filter(function(p) { + return !!p; + }), + !resolvedAbsolute + ).join("/"); + return (resolvedAbsolute ? "/" : "") + resolvedPath || "."; + }, + relative: function(from, to) { + from = PATH_FS.resolve(from).substr(1); + to = PATH_FS.resolve(to).substr(1); + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== "") break; + } + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== "") break; + } + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + var fromParts = trim(from.split("/")); + var toParts = trim(to.split("/")); + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push(".."); + } + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + return outputParts.join("/"); + } +}; +var TTY = { + ttys: [], + init: function() {}, + shutdown: function() {}, + register: function(dev, ops) { + TTY.ttys[dev] = { input: [], output: [], ops: ops }; + FS.registerDevice(dev, TTY.stream_ops); + }, + stream_ops: { + open: function(stream) { + var tty = TTY.ttys[stream.node.rdev]; + if (!tty) { + throw new FS.ErrnoError(43); + } + stream.tty = tty; + stream.seekable = false; + }, + close: function(stream) { + stream.tty.ops.flush(stream.tty); + }, + flush: function(stream) { + stream.tty.ops.flush(stream.tty); + }, + read: function(stream, buffer, offset, length, pos) { + if (!stream.tty || !stream.tty.ops.get_char) { + throw new FS.ErrnoError(60); + } + var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = stream.tty.ops.get_char(stream.tty); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (result === undefined && bytesRead === 0) { + throw new FS.ErrnoError(6); + } + if (result === null || result === undefined) break; + bytesRead++; + buffer[offset + i] = result; + } + if (bytesRead) { + stream.node.timestamp = Date.now(); + } + return bytesRead; + }, + write: function(stream, buffer, offset, length, pos) { + if (!stream.tty || !stream.tty.ops.put_char) { + throw new FS.ErrnoError(60); + } + try { + for (var i = 0; i < length; i++) { + stream.tty.ops.put_char(stream.tty, buffer[offset + i]); + } + } catch (e) { + throw new FS.ErrnoError(29); + } + if (length) { + stream.node.timestamp = Date.now(); + } + return i; + } + }, + default_tty_ops: { + get_char: function(tty) { + if (!tty.input.length) { + var result = null; + if (ENVIRONMENT_IS_NODE) { + var BUFSIZE = 256; + var buf = Buffer.alloc ? Buffer.alloc(BUFSIZE) : new Buffer(BUFSIZE); + var bytesRead = 0; + try { + bytesRead = nodeFS.readSync( + process.stdin.fd, + buf, + 0, + BUFSIZE, + null + ); + } catch (e) { + if (e.toString().includes("EOF")) bytesRead = 0; + else throw e; + } + if (bytesRead > 0) { + result = buf.slice(0, bytesRead).toString("utf-8"); + } else { + result = null; + } + } else if ( + typeof window != "undefined" && + typeof window.prompt == "function" + ) { + result = window.prompt("Input: "); + if (result !== null) { + result += "\n"; + } + } else if (typeof readline == "function") { + result = readline(); + if (result !== null) { + result += "\n"; + } + } + if (!result) { + return null; + } + tty.input = intArrayFromString(result, true); + } + return tty.input.shift(); + }, + put_char: function(tty, val) { + if (val === null || val === 10) { + out(UTF8ArrayToString(tty.output, 0)); + tty.output = []; + } else { + if (val != 0) tty.output.push(val); + } + }, + flush: function(tty) { + if (tty.output && tty.output.length > 0) { + out(UTF8ArrayToString(tty.output, 0)); + tty.output = []; + } + } + }, + default_tty1_ops: { + put_char: function(tty, val) { + if (val === null || val === 10) { + err(UTF8ArrayToString(tty.output, 0)); + tty.output = []; + } else { + if (val != 0) tty.output.push(val); + } + }, + flush: function(tty) { + if (tty.output && tty.output.length > 0) { + err(UTF8ArrayToString(tty.output, 0)); + tty.output = []; + } + } + } +}; +function mmapAlloc(size) { + var alignedSize = alignMemory(size, 65536); + var ptr = _malloc(alignedSize); + while (size < alignedSize) HEAP8[ptr + size++] = 0; + return ptr; +} +var MEMFS = { + ops_table: null, + mount: function(mount) { + return MEMFS.createNode(null, "/", 16384 | 511, 0); + }, + createNode: function(parent, name, mode, dev) { + if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { + throw new FS.ErrnoError(63); + } + if (!MEMFS.ops_table) { + MEMFS.ops_table = { + dir: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr, + lookup: MEMFS.node_ops.lookup, + mknod: MEMFS.node_ops.mknod, + rename: MEMFS.node_ops.rename, + unlink: MEMFS.node_ops.unlink, + rmdir: MEMFS.node_ops.rmdir, + readdir: MEMFS.node_ops.readdir, + symlink: MEMFS.node_ops.symlink + }, + stream: { llseek: MEMFS.stream_ops.llseek } + }, + file: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr + }, + stream: { + llseek: MEMFS.stream_ops.llseek, + read: MEMFS.stream_ops.read, + write: MEMFS.stream_ops.write, + allocate: MEMFS.stream_ops.allocate, + mmap: MEMFS.stream_ops.mmap, + msync: MEMFS.stream_ops.msync + } + }, + link: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr, + readlink: MEMFS.node_ops.readlink + }, + stream: {} + }, + chrdev: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr + }, + stream: FS.chrdev_stream_ops + } + }; + } + var node = FS.createNode(parent, name, mode, dev); + if (FS.isDir(node.mode)) { + node.node_ops = MEMFS.ops_table.dir.node; + node.stream_ops = MEMFS.ops_table.dir.stream; + node.contents = {}; + } else if (FS.isFile(node.mode)) { + node.node_ops = MEMFS.ops_table.file.node; + node.stream_ops = MEMFS.ops_table.file.stream; + node.usedBytes = 0; + node.contents = null; + } else if (FS.isLink(node.mode)) { + node.node_ops = MEMFS.ops_table.link.node; + node.stream_ops = MEMFS.ops_table.link.stream; + } else if (FS.isChrdev(node.mode)) { + node.node_ops = MEMFS.ops_table.chrdev.node; + node.stream_ops = MEMFS.ops_table.chrdev.stream; + } + node.timestamp = Date.now(); + if (parent) { + parent.contents[name] = node; + parent.timestamp = node.timestamp; + } + return node; + }, + getFileDataAsTypedArray: function(node) { + if (!node.contents) return new Uint8Array(0); + if (node.contents.subarray) + return node.contents.subarray(0, node.usedBytes); + return new Uint8Array(node.contents); + }, + expandFileStorage: function(node, newCapacity) { + var prevCapacity = node.contents ? node.contents.length : 0; + if (prevCapacity >= newCapacity) return; + var CAPACITY_DOUBLING_MAX = 1024 * 1024; + newCapacity = Math.max( + newCapacity, + (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2 : 1.125)) >>> 0 + ); + if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); + var oldContents = node.contents; + node.contents = new Uint8Array(newCapacity); + if (node.usedBytes > 0) + node.contents.set(oldContents.subarray(0, node.usedBytes), 0); + }, + resizeFileStorage: function(node, newSize) { + if (node.usedBytes == newSize) return; + if (newSize == 0) { + node.contents = null; + node.usedBytes = 0; + } else { + var oldContents = node.contents; + node.contents = new Uint8Array(newSize); + if (oldContents) { + node.contents.set( + oldContents.subarray(0, Math.min(newSize, node.usedBytes)) + ); + } + node.usedBytes = newSize; + } + }, + node_ops: { + getattr: function(node) { + var attr = {}; + attr.dev = FS.isChrdev(node.mode) ? node.id : 1; + attr.ino = node.id; + attr.mode = node.mode; + attr.nlink = 1; + attr.uid = 0; + attr.gid = 0; + attr.rdev = node.rdev; + if (FS.isDir(node.mode)) { + attr.size = 4096; + } else if (FS.isFile(node.mode)) { + attr.size = node.usedBytes; + } else if (FS.isLink(node.mode)) { + attr.size = node.link.length; + } else { + attr.size = 0; + } + attr.atime = new Date(node.timestamp); + attr.mtime = new Date(node.timestamp); + attr.ctime = new Date(node.timestamp); + attr.blksize = 4096; + attr.blocks = Math.ceil(attr.size / attr.blksize); + return attr; + }, + setattr: function(node, attr) { + if (attr.mode !== undefined) { + node.mode = attr.mode; + } + if (attr.timestamp !== undefined) { + node.timestamp = attr.timestamp; + } + if (attr.size !== undefined) { + MEMFS.resizeFileStorage(node, attr.size); + } + }, + lookup: function(parent, name) { + throw FS.genericErrors[44]; + }, + mknod: function(parent, name, mode, dev) { + return MEMFS.createNode(parent, name, mode, dev); + }, + rename: function(old_node, new_dir, new_name) { + if (FS.isDir(old_node.mode)) { + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) {} + if (new_node) { + for (var i in new_node.contents) { + throw new FS.ErrnoError(55); + } + } + } + delete old_node.parent.contents[old_node.name]; + old_node.parent.timestamp = Date.now(); + old_node.name = new_name; + new_dir.contents[new_name] = old_node; + new_dir.timestamp = old_node.parent.timestamp; + old_node.parent = new_dir; + }, + unlink: function(parent, name) { + delete parent.contents[name]; + parent.timestamp = Date.now(); + }, + rmdir: function(parent, name) { + var node = FS.lookupNode(parent, name); + for (var i in node.contents) { + throw new FS.ErrnoError(55); + } + delete parent.contents[name]; + parent.timestamp = Date.now(); + }, + readdir: function(node) { + var entries = [".", ".."]; + for (var key in node.contents) { + if (!node.contents.hasOwnProperty(key)) { + continue; + } + entries.push(key); + } + return entries; + }, + symlink: function(parent, newname, oldpath) { + var node = MEMFS.createNode(parent, newname, 511 | 40960, 0); + node.link = oldpath; + return node; + }, + readlink: function(node) { + if (!FS.isLink(node.mode)) { + throw new FS.ErrnoError(28); + } + return node.link; + } + }, + stream_ops: { + read: function(stream, buffer, offset, length, position) { + var contents = stream.node.contents; + if (position >= stream.node.usedBytes) return 0; + var size = Math.min(stream.node.usedBytes - position, length); + if (size > 8 && contents.subarray) { + buffer.set(contents.subarray(position, position + size), offset); + } else { + for (var i = 0; i < size; i++) + buffer[offset + i] = contents[position + i]; + } + return size; + }, + write: function(stream, buffer, offset, length, position, canOwn) { + if (buffer.buffer === HEAP8.buffer) { + canOwn = false; + } + if (!length) return 0; + var node = stream.node; + node.timestamp = Date.now(); + if (buffer.subarray && (!node.contents || node.contents.subarray)) { + if (canOwn) { + node.contents = buffer.subarray(offset, offset + length); + node.usedBytes = length; + return length; + } else if (node.usedBytes === 0 && position === 0) { + node.contents = buffer.slice(offset, offset + length); + node.usedBytes = length; + return length; + } else if (position + length <= node.usedBytes) { + node.contents.set(buffer.subarray(offset, offset + length), position); + return length; + } + } + MEMFS.expandFileStorage(node, position + length); + if (node.contents.subarray && buffer.subarray) { + node.contents.set(buffer.subarray(offset, offset + length), position); + } else { + for (var i = 0; i < length; i++) { + node.contents[position + i] = buffer[offset + i]; + } + } + node.usedBytes = Math.max(node.usedBytes, position + length); + return length; + }, + llseek: function(stream, offset, whence) { + var position = offset; + if (whence === 1) { + position += stream.position; + } else if (whence === 2) { + if (FS.isFile(stream.node.mode)) { + position += stream.node.usedBytes; + } + } + if (position < 0) { + throw new FS.ErrnoError(28); + } + return position; + }, + allocate: function(stream, offset, length) { + MEMFS.expandFileStorage(stream.node, offset + length); + stream.node.usedBytes = Math.max(stream.node.usedBytes, offset + length); + }, + mmap: function(stream, address, length, position, prot, flags) { + if (address !== 0) { + throw new FS.ErrnoError(28); + } + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError(43); + } + var ptr; + var allocated; + var contents = stream.node.contents; + if (!(flags & 2) && contents.buffer === buffer) { + allocated = false; + ptr = contents.byteOffset; + } else { + if (position > 0 || position + length < contents.length) { + if (contents.subarray) { + contents = contents.subarray(position, position + length); + } else { + contents = Array.prototype.slice.call( + contents, + position, + position + length + ); + } + } + allocated = true; + ptr = mmapAlloc(length); + if (!ptr) { + throw new FS.ErrnoError(48); + } + HEAP8.set(contents, ptr); + } + return { ptr: ptr, allocated: allocated }; + }, + msync: function(stream, buffer, offset, length, mmapFlags) { + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError(43); + } + if (mmapFlags & 2) { + return 0; + } + var bytesWritten = MEMFS.stream_ops.write( + stream, + buffer, + 0, + length, + offset, + false + ); + return 0; + } + } +}; +var ERRNO_CODES = { + EPERM: 63, + ENOENT: 44, + ESRCH: 71, + EINTR: 27, + EIO: 29, + ENXIO: 60, + E2BIG: 1, + ENOEXEC: 45, + EBADF: 8, + ECHILD: 12, + EAGAIN: 6, + EWOULDBLOCK: 6, + ENOMEM: 48, + EACCES: 2, + EFAULT: 21, + ENOTBLK: 105, + EBUSY: 10, + EEXIST: 20, + EXDEV: 75, + ENODEV: 43, + ENOTDIR: 54, + EISDIR: 31, + EINVAL: 28, + ENFILE: 41, + EMFILE: 33, + ENOTTY: 59, + ETXTBSY: 74, + EFBIG: 22, + ENOSPC: 51, + ESPIPE: 70, + EROFS: 69, + EMLINK: 34, + EPIPE: 64, + EDOM: 18, + ERANGE: 68, + ENOMSG: 49, + EIDRM: 24, + ECHRNG: 106, + EL2NSYNC: 156, + EL3HLT: 107, + EL3RST: 108, + ELNRNG: 109, + EUNATCH: 110, + ENOCSI: 111, + EL2HLT: 112, + EDEADLK: 16, + ENOLCK: 46, + EBADE: 113, + EBADR: 114, + EXFULL: 115, + ENOANO: 104, + EBADRQC: 103, + EBADSLT: 102, + EDEADLOCK: 16, + EBFONT: 101, + ENOSTR: 100, + ENODATA: 116, + ETIME: 117, + ENOSR: 118, + ENONET: 119, + ENOPKG: 120, + EREMOTE: 121, + ENOLINK: 47, + EADV: 122, + ESRMNT: 123, + ECOMM: 124, + EPROTO: 65, + EMULTIHOP: 36, + EDOTDOT: 125, + EBADMSG: 9, + ENOTUNIQ: 126, + EBADFD: 127, + EREMCHG: 128, + ELIBACC: 129, + ELIBBAD: 130, + ELIBSCN: 131, + ELIBMAX: 132, + ELIBEXEC: 133, + ENOSYS: 52, + ENOTEMPTY: 55, + ENAMETOOLONG: 37, + ELOOP: 32, + EOPNOTSUPP: 138, + EPFNOSUPPORT: 139, + ECONNRESET: 15, + ENOBUFS: 42, + EAFNOSUPPORT: 5, + EPROTOTYPE: 67, + ENOTSOCK: 57, + ENOPROTOOPT: 50, + ESHUTDOWN: 140, + ECONNREFUSED: 14, + EADDRINUSE: 3, + ECONNABORTED: 13, + ENETUNREACH: 40, + ENETDOWN: 38, + ETIMEDOUT: 73, + EHOSTDOWN: 142, + EHOSTUNREACH: 23, + EINPROGRESS: 26, + EALREADY: 7, + EDESTADDRREQ: 17, + EMSGSIZE: 35, + EPROTONOSUPPORT: 66, + ESOCKTNOSUPPORT: 137, + EADDRNOTAVAIL: 4, + ENETRESET: 39, + EISCONN: 30, + ENOTCONN: 53, + ETOOMANYREFS: 141, + EUSERS: 136, + EDQUOT: 19, + ESTALE: 72, + ENOTSUP: 138, + ENOMEDIUM: 148, + EILSEQ: 25, + EOVERFLOW: 61, + ECANCELED: 11, + ENOTRECOVERABLE: 56, + EOWNERDEAD: 62, + ESTRPIPE: 135 +}; +var NODEFS = { + isWindows: false, + staticInit: function() { + NODEFS.isWindows = !!process.platform.match(/^win/); + var flags = { fs: fs.constants }; + if (flags["fs"]) { + flags = flags["fs"]; + } + NODEFS.flagsForNodeMap = { + 1024: flags["O_APPEND"], + 64: flags["O_CREAT"], + 128: flags["O_EXCL"], + 256: flags["O_NOCTTY"], + 0: flags["O_RDONLY"], + 2: flags["O_RDWR"], + 4096: flags["O_SYNC"], + 512: flags["O_TRUNC"], + 1: flags["O_WRONLY"] + }; + }, + bufferFrom: function(arrayBuffer) { + return Buffer["alloc"] ? Buffer.from(arrayBuffer) : new Buffer(arrayBuffer); + }, + convertNodeCode: function(e) { + var code = e.code; + return ERRNO_CODES[code]; + }, + mount: function(mount) { + return NODEFS.createNode(null, "/", NODEFS.getMode(mount.opts.root), 0); + }, + createNode: function(parent, name, mode, dev) { + if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { + throw new FS.ErrnoError(28); + } + var node = FS.createNode(parent, name, mode); + node.node_ops = NODEFS.node_ops; + node.stream_ops = NODEFS.stream_ops; + return node; + }, + getMode: function(path) { + var stat; + try { + stat = fs.lstatSync(path); + if (NODEFS.isWindows) { + stat.mode = stat.mode | ((stat.mode & 292) >> 2); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + return stat.mode; + }, + realPath: function(node) { + var parts = []; + while (node.parent !== node) { + parts.push(node.name); + node = node.parent; + } + parts.push(node.mount.opts.root); + parts.reverse(); + return PATH.join.apply(null, parts); + }, + flagsForNode: function(flags) { + flags &= ~2097152; + flags &= ~2048; + flags &= ~32768; + flags &= ~524288; + var newFlags = 0; + for (var k in NODEFS.flagsForNodeMap) { + if (flags & k) { + newFlags |= NODEFS.flagsForNodeMap[k]; + flags ^= k; + } + } + if (!flags) { + return newFlags; + } else { + throw new FS.ErrnoError(28); + } + }, + node_ops: { + getattr: function(node) { + var path = NODEFS.realPath(node); + var stat; + try { + stat = fs.lstatSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + if (NODEFS.isWindows && !stat.blksize) { + stat.blksize = 4096; + } + if (NODEFS.isWindows && !stat.blocks) { + stat.blocks = ((stat.size + stat.blksize - 1) / stat.blksize) | 0; + } + return { + dev: stat.dev, + ino: stat.ino, + mode: stat.mode, + nlink: stat.nlink, + uid: stat.uid, + gid: stat.gid, + rdev: stat.rdev, + size: stat.size, + atime: stat.atime, + mtime: stat.mtime, + ctime: stat.ctime, + blksize: stat.blksize, + blocks: stat.blocks + }; + }, + setattr: function(node, attr) { + var path = NODEFS.realPath(node); + try { + if (attr.mode !== undefined) { + fs.chmodSync(path, attr.mode); + node.mode = attr.mode; + } + if (attr.timestamp !== undefined) { + var date = new Date(attr.timestamp); + fs.utimesSync(path, date, date); + } + if (attr.size !== undefined) { + fs.truncateSync(path, attr.size); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + }, + lookup: function(parent, name) { + var path = PATH.join2(NODEFS.realPath(parent), name); + var mode = NODEFS.getMode(path); + return NODEFS.createNode(parent, name, mode); + }, + mknod: function(parent, name, mode, dev) { + var node = NODEFS.createNode(parent, name, mode, dev); + var path = NODEFS.realPath(node); + try { + if (FS.isDir(node.mode)) { + fs.mkdirSync(path, node.mode); + } else { + fs.writeFileSync(path, "", { mode: node.mode }); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + return node; + }, + rename: function(oldNode, newDir, newName) { + var oldPath = NODEFS.realPath(oldNode); + var newPath = PATH.join2(NODEFS.realPath(newDir), newName); + try { + fs.renameSync(oldPath, newPath); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + oldNode.name = newName; + }, + unlink: function(parent, name) { + var path = PATH.join2(NODEFS.realPath(parent), name); + try { + fs.unlinkSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + }, + rmdir: function(parent, name) { + var path = PATH.join2(NODEFS.realPath(parent), name); + try { + fs.rmdirSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + }, + readdir: function(node) { + var path = NODEFS.realPath(node); + try { + return fs.readdirSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + }, + symlink: function(parent, newName, oldPath) { + var newPath = PATH.join2(NODEFS.realPath(parent), newName); + try { + fs.symlinkSync(oldPath, newPath); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + }, + readlink: function(node) { + var path = NODEFS.realPath(node); + try { + path = fs.readlinkSync(path); + path = NODEJS_PATH.relative( + NODEJS_PATH.resolve(node.mount.opts.root), + path + ); + return path; + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + } + }, + stream_ops: { + open: function(stream) { + var path = NODEFS.realPath(stream.node); + try { + if (FS.isFile(stream.node.mode)) { + stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags)); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + }, + close: function(stream) { + try { + if (FS.isFile(stream.node.mode) && stream.nfd) { + fs.closeSync(stream.nfd); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + }, + read: function(stream, buffer, offset, length, position) { + if (length === 0) return 0; + try { + return fs.readSync( + stream.nfd, + NODEFS.bufferFrom(buffer.buffer), + offset, + length, + position + ); + } catch (e) { + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + }, + write: function(stream, buffer, offset, length, position) { + try { + return fs.writeSync( + stream.nfd, + NODEFS.bufferFrom(buffer.buffer), + offset, + length, + position + ); + } catch (e) { + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + }, + llseek: function(stream, offset, whence) { + var position = offset; + if (whence === 1) { + position += stream.position; + } else if (whence === 2) { + if (FS.isFile(stream.node.mode)) { + try { + var stat = fs.fstatSync(stream.nfd); + position += stat.size; + } catch (e) { + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + } + } + if (position < 0) { + throw new FS.ErrnoError(28); + } + return position; + }, + mmap: function(stream, address, length, position, prot, flags) { + if (address !== 0) { + throw new FS.ErrnoError(28); + } + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError(43); + } + var ptr = mmapAlloc(length); + NODEFS.stream_ops.read(stream, HEAP8, ptr, length, position); + return { ptr: ptr, allocated: true }; + }, + msync: function(stream, buffer, offset, length, mmapFlags) { + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError(43); + } + if (mmapFlags & 2) { + return 0; + } + var bytesWritten = NODEFS.stream_ops.write( + stream, + buffer, + 0, + length, + offset, + false + ); + return 0; + } + } +}; +var NODERAWFS = { + lookupPath: function(path) { + return { path: path, node: { mode: NODEFS.getMode(path) } }; + }, + createStandardStreams: function() { + FS.streams[0] = { + fd: 0, + nfd: 0, + position: 0, + path: "", + flags: 0, + tty: true, + seekable: false + }; + for (var i = 1; i < 3; i++) { + FS.streams[i] = { + fd: i, + nfd: i, + position: 0, + path: "", + flags: 577, + tty: true, + seekable: false + }; + } + }, + cwd: function() { + return process.cwd(); + }, + chdir: function() { + process.chdir.apply(void 0, arguments); + }, + mknod: function(path, mode) { + if (FS.isDir(path)) { + fs.mkdirSync(path, mode); + } else { + fs.writeFileSync(path, "", { mode: mode }); + } + }, + mkdir: function() { + fs.mkdirSync.apply(void 0, arguments); + }, + symlink: function() { + fs.symlinkSync.apply(void 0, arguments); + }, + rename: function() { + fs.renameSync.apply(void 0, arguments); + }, + rmdir: function() { + fs.rmdirSync.apply(void 0, arguments); + }, + readdir: function() { + fs.readdirSync.apply(void 0, arguments); + }, + unlink: function() { + fs.unlinkSync.apply(void 0, arguments); + }, + readlink: function() { + return fs.readlinkSync.apply(void 0, arguments); + }, + stat: function() { + return fs.statSync.apply(void 0, arguments); + }, + lstat: function() { + return fs.lstatSync.apply(void 0, arguments); + }, + chmod: function() { + fs.chmodSync.apply(void 0, arguments); + }, + fchmod: function() { + fs.fchmodSync.apply(void 0, arguments); + }, + chown: function() { + fs.chownSync.apply(void 0, arguments); + }, + fchown: function() { + fs.fchownSync.apply(void 0, arguments); + }, + truncate: function() { + fs.truncateSync.apply(void 0, arguments); + }, + ftruncate: function(fd, len) { + if (len < 0) { + throw new FS.ErrnoError(28); + } + fs.ftruncateSync.apply(void 0, arguments); + }, + utime: function() { + fs.utimesSync.apply(void 0, arguments); + }, + open: function(path, flags, mode, suggestFD) { + if (typeof flags === "string") { + flags = VFS.modeStringToFlags(flags); + } + var nfd = fs.openSync(path, NODEFS.flagsForNode(flags), mode); + var fd = suggestFD != null ? suggestFD : FS.nextfd(nfd); + var stream = { + fd: fd, + nfd: nfd, + position: 0, + path: path, + flags: flags, + seekable: true + }; + FS.streams[fd] = stream; + return stream; + }, + close: function(stream) { + if (!stream.stream_ops) { + fs.closeSync(stream.nfd); + } + FS.closeStream(stream.fd); + }, + llseek: function(stream, offset, whence) { + if (stream.stream_ops) { + return VFS.llseek(stream, offset, whence); + } + var position = offset; + if (whence === 1) { + position += stream.position; + } else if (whence === 2) { + position += fs.fstatSync(stream.nfd).size; + } else if (whence !== 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + if (position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + stream.position = position; + return position; + }, + read: function(stream, buffer, offset, length, position) { + if (stream.stream_ops) { + return VFS.read(stream, buffer, offset, length, position); + } + var seeking = typeof position !== "undefined"; + if (!seeking && stream.seekable) position = stream.position; + var bytesRead = fs.readSync( + stream.nfd, + NODEFS.bufferFrom(buffer.buffer), + offset, + length, + position + ); + if (!seeking) stream.position += bytesRead; + return bytesRead; + }, + write: function(stream, buffer, offset, length, position) { + if (stream.stream_ops) { + return VFS.write(stream, buffer, offset, length, position); + } + if (stream.flags & +"1024") { + FS.llseek(stream, 0, +"2"); + } + var seeking = typeof position !== "undefined"; + if (!seeking && stream.seekable) position = stream.position; + var bytesWritten = fs.writeSync( + stream.nfd, + NODEFS.bufferFrom(buffer.buffer), + offset, + length, + position + ); + if (!seeking) stream.position += bytesWritten; + return bytesWritten; + }, + allocate: function() { + throw new FS.ErrnoError(ERRNO_CODES.EOPNOTSUPP); + }, + mmap: function(stream, address, length, position, prot, flags) { + if (stream.stream_ops) { + return VFS.mmap(stream, address, length, position, prot, flags); + } + if (address !== 0) { + throw new FS.ErrnoError(28); + } + var ptr = mmapAlloc(length); + FS.read(stream, HEAP8, ptr, length, position); + return { ptr: ptr, allocated: true }; + }, + msync: function(stream, buffer, offset, length, mmapFlags) { + if (stream.stream_ops) { + return VFS.msync(stream, buffer, offset, length, mmapFlags); + } + if (mmapFlags & 2) { + return 0; + } + FS.write(stream, buffer, 0, length, offset); + return 0; + }, + munmap: function() { + return 0; + }, + ioctl: function() { + throw new FS.ErrnoError(ERRNO_CODES.ENOTTY); + } +}; +var FS = { + root: null, + mounts: [], + devices: {}, + streams: [], + nextInode: 1, + nameTable: null, + currentPath: "/", + initialized: false, + ignorePermissions: true, + trackingDelegate: {}, + tracking: { openFlags: { READ: 1, WRITE: 2 } }, + ErrnoError: null, + genericErrors: {}, + filesystems: null, + syncFSRequests: 0, + lookupPath: function(path, opts) { + path = PATH_FS.resolve(FS.cwd(), path); + opts = opts || {}; + if (!path) return { path: "", node: null }; + var defaults = { follow_mount: true, recurse_count: 0 }; + for (var key in defaults) { + if (opts[key] === undefined) { + opts[key] = defaults[key]; + } + } + if (opts.recurse_count > 8) { + throw new FS.ErrnoError(32); + } + var parts = PATH.normalizeArray( + path.split("/").filter(function(p) { + return !!p; + }), + false + ); + var current = FS.root; + var current_path = "/"; + for (var i = 0; i < parts.length; i++) { + var islast = i === parts.length - 1; + if (islast && opts.parent) { + break; + } + current = FS.lookupNode(current, parts[i]); + current_path = PATH.join2(current_path, parts[i]); + if (FS.isMountpoint(current)) { + if (!islast || (islast && opts.follow_mount)) { + current = current.mounted.root; + } + } + if (!islast || opts.follow) { + var count = 0; + while (FS.isLink(current.mode)) { + var link = FS.readlink(current_path); + current_path = PATH_FS.resolve(PATH.dirname(current_path), link); + var lookup = FS.lookupPath(current_path, { + recurse_count: opts.recurse_count + }); + current = lookup.node; + if (count++ > 40) { + throw new FS.ErrnoError(32); + } + } + } + } + return { path: current_path, node: current }; + }, + getPath: function(node) { + var path; + while (true) { + if (FS.isRoot(node)) { + var mount = node.mount.mountpoint; + if (!path) return mount; + return mount[mount.length - 1] !== "/" + ? mount + "/" + path + : mount + path; + } + path = path ? node.name + "/" + path : node.name; + node = node.parent; + } + }, + hashName: function(parentid, name) { + var hash = 0; + for (var i = 0; i < name.length; i++) { + hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0; + } + return ((parentid + hash) >>> 0) % FS.nameTable.length; + }, + hashAddNode: function(node) { + var hash = FS.hashName(node.parent.id, node.name); + node.name_next = FS.nameTable[hash]; + FS.nameTable[hash] = node; + }, + hashRemoveNode: function(node) { + var hash = FS.hashName(node.parent.id, node.name); + if (FS.nameTable[hash] === node) { + FS.nameTable[hash] = node.name_next; + } else { + var current = FS.nameTable[hash]; + while (current) { + if (current.name_next === node) { + current.name_next = node.name_next; + break; + } + current = current.name_next; + } + } + }, + lookupNode: function(parent, name) { + var errCode = FS.mayLookup(parent); + if (errCode) { + throw new FS.ErrnoError(errCode, parent); + } + var hash = FS.hashName(parent.id, name); + for (var node = FS.nameTable[hash]; node; node = node.name_next) { + var nodeName = node.name; + if (node.parent.id === parent.id && nodeName === name) { + return node; + } + } + return FS.lookup(parent, name); + }, + createNode: function(parent, name, mode, rdev) { + var node = new FS.FSNode(parent, name, mode, rdev); + FS.hashAddNode(node); + return node; + }, + destroyNode: function(node) { + FS.hashRemoveNode(node); + }, + isRoot: function(node) { + return node === node.parent; + }, + isMountpoint: function(node) { + return !!node.mounted; + }, + isFile: function(mode) { + return (mode & 61440) === 32768; + }, + isDir: function(mode) { + return (mode & 61440) === 16384; + }, + isLink: function(mode) { + return (mode & 61440) === 40960; + }, + isChrdev: function(mode) { + return (mode & 61440) === 8192; + }, + isBlkdev: function(mode) { + return (mode & 61440) === 24576; + }, + isFIFO: function(mode) { + return (mode & 61440) === 4096; + }, + isSocket: function(mode) { + return (mode & 49152) === 49152; + }, + flagModes: { r: 0, "r+": 2, w: 577, "w+": 578, a: 1089, "a+": 1090 }, + modeStringToFlags: function(str) { + var flags = FS.flagModes[str]; + if (typeof flags === "undefined") { + throw new Error("Unknown file open mode: " + str); + } + return flags; + }, + flagsToPermissionString: function(flag) { + var perms = ["r", "w", "rw"][flag & 3]; + if (flag & 512) { + perms += "w"; + } + return perms; + }, + nodePermissions: function(node, perms) { + if (FS.ignorePermissions) { + return 0; + } + if (perms.includes("r") && !(node.mode & 292)) { + return 2; + } else if (perms.includes("w") && !(node.mode & 146)) { + return 2; + } else if (perms.includes("x") && !(node.mode & 73)) { + return 2; + } + return 0; + }, + mayLookup: function(dir) { + var errCode = FS.nodePermissions(dir, "x"); + if (errCode) return errCode; + if (!dir.node_ops.lookup) return 2; + return 0; + }, + mayCreate: function(dir, name) { + try { + var node = FS.lookupNode(dir, name); + return 20; + } catch (e) {} + return FS.nodePermissions(dir, "wx"); + }, + mayDelete: function(dir, name, isdir) { + var node; + try { + node = FS.lookupNode(dir, name); + } catch (e) { + return e.errno; + } + var errCode = FS.nodePermissions(dir, "wx"); + if (errCode) { + return errCode; + } + if (isdir) { + if (!FS.isDir(node.mode)) { + return 54; + } + if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { + return 10; + } + } else { + if (FS.isDir(node.mode)) { + return 31; + } + } + return 0; + }, + mayOpen: function(node, flags) { + if (!node) { + return 44; + } + if (FS.isLink(node.mode)) { + return 32; + } else if (FS.isDir(node.mode)) { + if (FS.flagsToPermissionString(flags) !== "r" || flags & 512) { + return 31; + } + } + return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); + }, + MAX_OPEN_FDS: 4096, + nextfd: function(fd_start, fd_end) { + fd_start = fd_start || 0; + fd_end = fd_end || FS.MAX_OPEN_FDS; + for (var fd = fd_start; fd <= fd_end; fd++) { + if (!FS.streams[fd]) { + return fd; + } + } + throw new FS.ErrnoError(33); + }, + getStream: function(fd) { + return FS.streams[fd]; + }, + createStream: function(stream, fd_start, fd_end) { + if (!FS.FSStream) { + FS.FSStream = function() {}; + FS.FSStream.prototype = { + object: { + get: function() { + return this.node; + }, + set: function(val) { + this.node = val; + } + }, + isRead: { + get: function() { + return (this.flags & 2097155) !== 1; + } + }, + isWrite: { + get: function() { + return (this.flags & 2097155) !== 0; + } + }, + isAppend: { + get: function() { + return this.flags & 1024; + } + } + }; + } + var newStream = new FS.FSStream(); + for (var p in stream) { + newStream[p] = stream[p]; + } + stream = newStream; + var fd = FS.nextfd(fd_start, fd_end); + stream.fd = fd; + FS.streams[fd] = stream; + return stream; + }, + closeStream: function(fd) { + FS.streams[fd] = null; + }, + chrdev_stream_ops: { + open: function(stream) { + var device = FS.getDevice(stream.node.rdev); + stream.stream_ops = device.stream_ops; + if (stream.stream_ops.open) { + stream.stream_ops.open(stream); + } + }, + llseek: function() { + throw new FS.ErrnoError(70); + } + }, + major: function(dev) { + return dev >> 8; + }, + minor: function(dev) { + return dev & 255; + }, + makedev: function(ma, mi) { + return (ma << 8) | mi; + }, + registerDevice: function(dev, ops) { + FS.devices[dev] = { stream_ops: ops }; + }, + getDevice: function(dev) { + return FS.devices[dev]; + }, + getMounts: function(mount) { + var mounts = []; + var check = [mount]; + while (check.length) { + var m = check.pop(); + mounts.push(m); + check.push.apply(check, m.mounts); + } + return mounts; + }, + syncfs: function(populate, callback) { + if (typeof populate === "function") { + callback = populate; + populate = false; + } + FS.syncFSRequests++; + if (FS.syncFSRequests > 1) { + err( + "warning: " + + FS.syncFSRequests + + " FS.syncfs operations in flight at once, probably just doing extra work" + ); + } + var mounts = FS.getMounts(FS.root.mount); + var completed = 0; + function doCallback(errCode) { + FS.syncFSRequests--; + return callback(errCode); + } + function done(errCode) { + if (errCode) { + if (!done.errored) { + done.errored = true; + return doCallback(errCode); + } + return; + } + if (++completed >= mounts.length) { + doCallback(null); + } + } + mounts.forEach(function(mount) { + if (!mount.type.syncfs) { + return done(null); + } + mount.type.syncfs(mount, populate, done); + }); + }, + mount: function(type, opts, mountpoint) { + var root = mountpoint === "/"; + var pseudo = !mountpoint; + var node; + if (root && FS.root) { + throw new FS.ErrnoError(10); + } else if (!root && !pseudo) { + var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); + mountpoint = lookup.path; + node = lookup.node; + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(10); + } + if (!FS.isDir(node.mode)) { + throw new FS.ErrnoError(54); + } + } + var mount = { type: type, opts: opts, mountpoint: mountpoint, mounts: [] }; + var mountRoot = type.mount(mount); + mountRoot.mount = mount; + mount.root = mountRoot; + if (root) { + FS.root = mountRoot; + } else if (node) { + node.mounted = mount; + if (node.mount) { + node.mount.mounts.push(mount); + } + } + return mountRoot; + }, + unmount: function(mountpoint) { + var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); + if (!FS.isMountpoint(lookup.node)) { + throw new FS.ErrnoError(28); + } + var node = lookup.node; + var mount = node.mounted; + var mounts = FS.getMounts(mount); + Object.keys(FS.nameTable).forEach(function(hash) { + var current = FS.nameTable[hash]; + while (current) { + var next = current.name_next; + if (mounts.includes(current.mount)) { + FS.destroyNode(current); + } + current = next; + } + }); + node.mounted = null; + var idx = node.mount.mounts.indexOf(mount); + node.mount.mounts.splice(idx, 1); + }, + lookup: function(parent, name) { + return parent.node_ops.lookup(parent, name); + }, + mknod: function(path, mode, dev) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + if (!name || name === "." || name === "..") { + throw new FS.ErrnoError(28); + } + var errCode = FS.mayCreate(parent, name); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!parent.node_ops.mknod) { + throw new FS.ErrnoError(63); + } + return parent.node_ops.mknod(parent, name, mode, dev); + }, + create: function(path, mode) { + mode = mode !== undefined ? mode : 438; + mode &= 4095; + mode |= 32768; + return FS.mknod(path, mode, 0); + }, + mkdir: function(path, mode) { + mode = mode !== undefined ? mode : 511; + mode &= 511 | 512; + mode |= 16384; + return FS.mknod(path, mode, 0); + }, + mkdirTree: function(path, mode) { + var dirs = path.split("/"); + var d = ""; + for (var i = 0; i < dirs.length; ++i) { + if (!dirs[i]) continue; + d += "/" + dirs[i]; + try { + FS.mkdir(d, mode); + } catch (e) { + if (e.errno != 20) throw e; + } + } + }, + mkdev: function(path, mode, dev) { + if (typeof dev === "undefined") { + dev = mode; + mode = 438; + } + mode |= 8192; + return FS.mknod(path, mode, dev); + }, + symlink: function(oldpath, newpath) { + if (!PATH_FS.resolve(oldpath)) { + throw new FS.ErrnoError(44); + } + var lookup = FS.lookupPath(newpath, { parent: true }); + var parent = lookup.node; + if (!parent) { + throw new FS.ErrnoError(44); + } + var newname = PATH.basename(newpath); + var errCode = FS.mayCreate(parent, newname); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!parent.node_ops.symlink) { + throw new FS.ErrnoError(63); + } + return parent.node_ops.symlink(parent, newname, oldpath); + }, + rename: function(old_path, new_path) { + var old_dirname = PATH.dirname(old_path); + var new_dirname = PATH.dirname(new_path); + var old_name = PATH.basename(old_path); + var new_name = PATH.basename(new_path); + var lookup, old_dir, new_dir; + lookup = FS.lookupPath(old_path, { parent: true }); + old_dir = lookup.node; + lookup = FS.lookupPath(new_path, { parent: true }); + new_dir = lookup.node; + if (!old_dir || !new_dir) throw new FS.ErrnoError(44); + if (old_dir.mount !== new_dir.mount) { + throw new FS.ErrnoError(75); + } + var old_node = FS.lookupNode(old_dir, old_name); + var relative = PATH_FS.relative(old_path, new_dirname); + if (relative.charAt(0) !== ".") { + throw new FS.ErrnoError(28); + } + relative = PATH_FS.relative(new_path, old_dirname); + if (relative.charAt(0) !== ".") { + throw new FS.ErrnoError(55); + } + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) {} + if (old_node === new_node) { + return; + } + var isdir = FS.isDir(old_node.mode); + var errCode = FS.mayDelete(old_dir, old_name, isdir); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + errCode = new_node + ? FS.mayDelete(new_dir, new_name, isdir) + : FS.mayCreate(new_dir, new_name); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!old_dir.node_ops.rename) { + throw new FS.ErrnoError(63); + } + if (FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node))) { + throw new FS.ErrnoError(10); + } + if (new_dir !== old_dir) { + errCode = FS.nodePermissions(old_dir, "w"); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + } + try { + if (FS.trackingDelegate["willMovePath"]) { + FS.trackingDelegate["willMovePath"](old_path, new_path); + } + } catch (e) { + err( + "FS.trackingDelegate['willMovePath']('" + + old_path + + "', '" + + new_path + + "') threw an exception: " + + e.message + ); + } + FS.hashRemoveNode(old_node); + try { + old_dir.node_ops.rename(old_node, new_dir, new_name); + } catch (e) { + throw e; + } finally { + FS.hashAddNode(old_node); + } + try { + if (FS.trackingDelegate["onMovePath"]) + FS.trackingDelegate["onMovePath"](old_path, new_path); + } catch (e) { + err( + "FS.trackingDelegate['onMovePath']('" + + old_path + + "', '" + + new_path + + "') threw an exception: " + + e.message + ); + } + }, + rmdir: function(path) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var errCode = FS.mayDelete(parent, name, true); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!parent.node_ops.rmdir) { + throw new FS.ErrnoError(63); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(10); + } + try { + if (FS.trackingDelegate["willDeletePath"]) { + FS.trackingDelegate["willDeletePath"](path); + } + } catch (e) { + err( + "FS.trackingDelegate['willDeletePath']('" + + path + + "') threw an exception: " + + e.message + ); + } + parent.node_ops.rmdir(parent, name); + FS.destroyNode(node); + try { + if (FS.trackingDelegate["onDeletePath"]) + FS.trackingDelegate["onDeletePath"](path); + } catch (e) { + err( + "FS.trackingDelegate['onDeletePath']('" + + path + + "') threw an exception: " + + e.message + ); + } + }, + readdir: function(path) { + var lookup = FS.lookupPath(path, { follow: true }); + var node = lookup.node; + if (!node.node_ops.readdir) { + throw new FS.ErrnoError(54); + } + return node.node_ops.readdir(node); + }, + unlink: function(path) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var errCode = FS.mayDelete(parent, name, false); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!parent.node_ops.unlink) { + throw new FS.ErrnoError(63); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(10); + } + try { + if (FS.trackingDelegate["willDeletePath"]) { + FS.trackingDelegate["willDeletePath"](path); + } + } catch (e) { + err( + "FS.trackingDelegate['willDeletePath']('" + + path + + "') threw an exception: " + + e.message + ); + } + parent.node_ops.unlink(parent, name); + FS.destroyNode(node); + try { + if (FS.trackingDelegate["onDeletePath"]) + FS.trackingDelegate["onDeletePath"](path); + } catch (e) { + err( + "FS.trackingDelegate['onDeletePath']('" + + path + + "') threw an exception: " + + e.message + ); + } + }, + readlink: function(path) { + var lookup = FS.lookupPath(path); + var link = lookup.node; + if (!link) { + throw new FS.ErrnoError(44); + } + if (!link.node_ops.readlink) { + throw new FS.ErrnoError(28); + } + return PATH_FS.resolve( + FS.getPath(link.parent), + link.node_ops.readlink(link) + ); + }, + stat: function(path, dontFollow) { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + var node = lookup.node; + if (!node) { + throw new FS.ErrnoError(44); + } + if (!node.node_ops.getattr) { + throw new FS.ErrnoError(63); + } + return node.node_ops.getattr(node); + }, + lstat: function(path) { + return FS.stat(path, true); + }, + chmod: function(path, mode, dontFollow) { + var node; + if (typeof path === "string") { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(63); + } + node.node_ops.setattr(node, { + mode: (mode & 4095) | (node.mode & ~4095), + timestamp: Date.now() + }); + }, + lchmod: function(path, mode) { + FS.chmod(path, mode, true); + }, + fchmod: function(fd, mode) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(8); + } + FS.chmod(stream.node, mode); + }, + chown: function(path, uid, gid, dontFollow) { + var node; + if (typeof path === "string") { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(63); + } + node.node_ops.setattr(node, { timestamp: Date.now() }); + }, + lchown: function(path, uid, gid) { + FS.chown(path, uid, gid, true); + }, + fchown: function(fd, uid, gid) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(8); + } + FS.chown(stream.node, uid, gid); + }, + truncate: function(path, len) { + if (len < 0) { + throw new FS.ErrnoError(28); + } + var node; + if (typeof path === "string") { + var lookup = FS.lookupPath(path, { follow: true }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(63); + } + if (FS.isDir(node.mode)) { + throw new FS.ErrnoError(31); + } + if (!FS.isFile(node.mode)) { + throw new FS.ErrnoError(28); + } + var errCode = FS.nodePermissions(node, "w"); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + node.node_ops.setattr(node, { size: len, timestamp: Date.now() }); + }, + ftruncate: function(fd, len) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(8); + } + if ((stream.flags & 2097155) === 0) { + throw new FS.ErrnoError(28); + } + FS.truncate(stream.node, len); + }, + utime: function(path, atime, mtime) { + var lookup = FS.lookupPath(path, { follow: true }); + var node = lookup.node; + node.node_ops.setattr(node, { timestamp: Math.max(atime, mtime) }); + }, + open: function(path, flags, mode, fd_start, fd_end) { + if (path === "") { + throw new FS.ErrnoError(44); + } + flags = typeof flags === "string" ? FS.modeStringToFlags(flags) : flags; + mode = typeof mode === "undefined" ? 438 : mode; + if (flags & 64) { + mode = (mode & 4095) | 32768; + } else { + mode = 0; + } + var node; + if (typeof path === "object") { + node = path; + } else { + path = PATH.normalize(path); + try { + var lookup = FS.lookupPath(path, { follow: !(flags & 131072) }); + node = lookup.node; + } catch (e) {} + } + var created = false; + if (flags & 64) { + if (node) { + if (flags & 128) { + throw new FS.ErrnoError(20); + } + } else { + node = FS.mknod(path, mode, 0); + created = true; + } + } + if (!node) { + throw new FS.ErrnoError(44); + } + if (FS.isChrdev(node.mode)) { + flags &= ~512; + } + if (flags & 65536 && !FS.isDir(node.mode)) { + throw new FS.ErrnoError(54); + } + if (!created) { + var errCode = FS.mayOpen(node, flags); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + } + if (flags & 512) { + FS.truncate(node, 0); + } + flags &= ~(128 | 512 | 131072); + var stream = FS.createStream( + { + node: node, + path: FS.getPath(node), + flags: flags, + seekable: true, + position: 0, + stream_ops: node.stream_ops, + ungotten: [], + error: false + }, + fd_start, + fd_end + ); + if (stream.stream_ops.open) { + stream.stream_ops.open(stream); + } + if (Module["logReadFiles"] && !(flags & 1)) { + if (!FS.readFiles) FS.readFiles = {}; + if (!(path in FS.readFiles)) { + FS.readFiles[path] = 1; + err("FS.trackingDelegate error on read file: " + path); + } + } + try { + if (FS.trackingDelegate["onOpenFile"]) { + var trackingFlags = 0; + if ((flags & 2097155) !== 1) { + trackingFlags |= FS.tracking.openFlags.READ; + } + if ((flags & 2097155) !== 0) { + trackingFlags |= FS.tracking.openFlags.WRITE; + } + FS.trackingDelegate["onOpenFile"](path, trackingFlags); + } + } catch (e) { + err( + "FS.trackingDelegate['onOpenFile']('" + + path + + "', flags) threw an exception: " + + e.message + ); + } + return stream; + }, + close: function(stream) { + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if (stream.getdents) stream.getdents = null; + try { + if (stream.stream_ops.close) { + stream.stream_ops.close(stream); + } + } catch (e) { + throw e; + } finally { + FS.closeStream(stream.fd); + } + stream.fd = null; + }, + isClosed: function(stream) { + return stream.fd === null; + }, + llseek: function(stream, offset, whence) { + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if (!stream.seekable || !stream.stream_ops.llseek) { + throw new FS.ErrnoError(70); + } + if (whence != 0 && whence != 1 && whence != 2) { + throw new FS.ErrnoError(28); + } + stream.position = stream.stream_ops.llseek(stream, offset, whence); + stream.ungotten = []; + return stream.position; + }, + read: function(stream, buffer, offset, length, position) { + if (length < 0 || position < 0) { + throw new FS.ErrnoError(28); + } + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if ((stream.flags & 2097155) === 1) { + throw new FS.ErrnoError(8); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(31); + } + if (!stream.stream_ops.read) { + throw new FS.ErrnoError(28); + } + var seeking = typeof position !== "undefined"; + if (!seeking) { + position = stream.position; + } else if (!stream.seekable) { + throw new FS.ErrnoError(70); + } + var bytesRead = stream.stream_ops.read( + stream, + buffer, + offset, + length, + position + ); + if (!seeking) stream.position += bytesRead; + return bytesRead; + }, + write: function(stream, buffer, offset, length, position, canOwn) { + if (length < 0 || position < 0) { + throw new FS.ErrnoError(28); + } + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if ((stream.flags & 2097155) === 0) { + throw new FS.ErrnoError(8); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(31); + } + if (!stream.stream_ops.write) { + throw new FS.ErrnoError(28); + } + if (stream.seekable && stream.flags & 1024) { + FS.llseek(stream, 0, 2); + } + var seeking = typeof position !== "undefined"; + if (!seeking) { + position = stream.position; + } else if (!stream.seekable) { + throw new FS.ErrnoError(70); + } + var bytesWritten = stream.stream_ops.write( + stream, + buffer, + offset, + length, + position, + canOwn + ); + if (!seeking) stream.position += bytesWritten; + try { + if (stream.path && FS.trackingDelegate["onWriteToFile"]) + FS.trackingDelegate["onWriteToFile"](stream.path); + } catch (e) { + err( + "FS.trackingDelegate['onWriteToFile']('" + + stream.path + + "') threw an exception: " + + e.message + ); + } + return bytesWritten; + }, + allocate: function(stream, offset, length) { + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if (offset < 0 || length <= 0) { + throw new FS.ErrnoError(28); + } + if ((stream.flags & 2097155) === 0) { + throw new FS.ErrnoError(8); + } + if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(43); + } + if (!stream.stream_ops.allocate) { + throw new FS.ErrnoError(138); + } + stream.stream_ops.allocate(stream, offset, length); + }, + mmap: function(stream, address, length, position, prot, flags) { + if ( + (prot & 2) !== 0 && + (flags & 2) === 0 && + (stream.flags & 2097155) !== 2 + ) { + throw new FS.ErrnoError(2); + } + if ((stream.flags & 2097155) === 1) { + throw new FS.ErrnoError(2); + } + if (!stream.stream_ops.mmap) { + throw new FS.ErrnoError(43); + } + return stream.stream_ops.mmap( + stream, + address, + length, + position, + prot, + flags + ); + }, + msync: function(stream, buffer, offset, length, mmapFlags) { + if (!stream || !stream.stream_ops.msync) { + return 0; + } + return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags); + }, + munmap: function(stream) { + return 0; + }, + ioctl: function(stream, cmd, arg) { + if (!stream.stream_ops.ioctl) { + throw new FS.ErrnoError(59); + } + return stream.stream_ops.ioctl(stream, cmd, arg); + }, + readFile: function(path, opts) { + opts = opts || {}; + opts.flags = opts.flags || 0; + opts.encoding = opts.encoding || "binary"; + if (opts.encoding !== "utf8" && opts.encoding !== "binary") { + throw new Error('Invalid encoding type "' + opts.encoding + '"'); + } + var ret; + var stream = FS.open(path, opts.flags); + var stat = FS.stat(path); + var length = stat.size; + var buf = new Uint8Array(length); + FS.read(stream, buf, 0, length, 0); + if (opts.encoding === "utf8") { + ret = UTF8ArrayToString(buf, 0); + } else if (opts.encoding === "binary") { + ret = buf; + } + FS.close(stream); + return ret; + }, + writeFile: function(path, data, opts) { + opts = opts || {}; + opts.flags = opts.flags || 577; + var stream = FS.open(path, opts.flags, opts.mode); + if (typeof data === "string") { + var buf = new Uint8Array(lengthBytesUTF8(data) + 1); + var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length); + FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn); + } else if (ArrayBuffer.isView(data)) { + FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); + } else { + throw new Error("Unsupported data type"); + } + FS.close(stream); + }, + cwd: function() { + return FS.currentPath; + }, + chdir: function(path) { + var lookup = FS.lookupPath(path, { follow: true }); + if (lookup.node === null) { + throw new FS.ErrnoError(44); + } + if (!FS.isDir(lookup.node.mode)) { + throw new FS.ErrnoError(54); + } + var errCode = FS.nodePermissions(lookup.node, "x"); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + FS.currentPath = lookup.path; + }, + createDefaultDirectories: function() { + FS.mkdir("/tmp"); + FS.mkdir("/home"); + FS.mkdir("/home/web_user"); + }, + createDefaultDevices: function() { + FS.mkdir("/dev"); + FS.registerDevice(FS.makedev(1, 3), { + read: function() { + return 0; + }, + write: function(stream, buffer, offset, length, pos) { + return length; + } + }); + FS.mkdev("/dev/null", FS.makedev(1, 3)); + TTY.register(FS.makedev(5, 0), TTY.default_tty_ops); + TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops); + FS.mkdev("/dev/tty", FS.makedev(5, 0)); + FS.mkdev("/dev/tty1", FS.makedev(6, 0)); + var random_device = getRandomDevice(); + FS.createDevice("/dev", "random", random_device); + FS.createDevice("/dev", "urandom", random_device); + FS.mkdir("/dev/shm"); + FS.mkdir("/dev/shm/tmp"); + }, + createSpecialDirectories: function() { + FS.mkdir("/proc"); + var proc_self = FS.mkdir("/proc/self"); + FS.mkdir("/proc/self/fd"); + FS.mount( + { + mount: function() { + var node = FS.createNode(proc_self, "fd", 16384 | 511, 73); + node.node_ops = { + lookup: function(parent, name) { + var fd = +name; + var stream = FS.getStream(fd); + if (!stream) throw new FS.ErrnoError(8); + var ret = { + parent: null, + mount: { mountpoint: "fake" }, + node_ops: { + readlink: function() { + return stream.path; + } + } + }; + ret.parent = ret; + return ret; + } + }; + return node; + } + }, + {}, + "/proc/self/fd" + ); + }, + createStandardStreams: function() { + if (Module["stdin"]) { + FS.createDevice("/dev", "stdin", Module["stdin"]); + } else { + FS.symlink("/dev/tty", "/dev/stdin"); + } + if (Module["stdout"]) { + FS.createDevice("/dev", "stdout", null, Module["stdout"]); + } else { + FS.symlink("/dev/tty", "/dev/stdout"); + } + if (Module["stderr"]) { + FS.createDevice("/dev", "stderr", null, Module["stderr"]); + } else { + FS.symlink("/dev/tty1", "/dev/stderr"); + } + var stdin = FS.open("/dev/stdin", 0); + var stdout = FS.open("/dev/stdout", 1); + var stderr = FS.open("/dev/stderr", 1); + }, + ensureErrnoError: function() { + if (FS.ErrnoError) return; + FS.ErrnoError = function ErrnoError(errno, node) { + this.node = node; + this.setErrno = function(errno) { + this.errno = errno; + }; + this.setErrno(errno); + this.message = "FS error"; + }; + FS.ErrnoError.prototype = new Error(); + FS.ErrnoError.prototype.constructor = FS.ErrnoError; + [44].forEach(function(code) { + FS.genericErrors[code] = new FS.ErrnoError(code); + FS.genericErrors[code].stack = ""; + }); + }, + staticInit: function() { + FS.ensureErrnoError(); + FS.nameTable = new Array(4096); + FS.mount(MEMFS, {}, "/"); + FS.createDefaultDirectories(); + FS.createDefaultDevices(); + FS.createSpecialDirectories(); + FS.filesystems = { MEMFS: MEMFS, NODEFS: NODEFS }; + }, + init: function(input, output, error) { + FS.init.initialized = true; + FS.ensureErrnoError(); + Module["stdin"] = input || Module["stdin"]; + Module["stdout"] = output || Module["stdout"]; + Module["stderr"] = error || Module["stderr"]; + FS.createStandardStreams(); + }, + quit: function() { + FS.init.initialized = false; + var fflush = Module["_fflush"]; + if (fflush) fflush(0); + for (var i = 0; i < FS.streams.length; i++) { + var stream = FS.streams[i]; + if (!stream) { + continue; + } + FS.close(stream); + } + }, + getMode: function(canRead, canWrite) { + var mode = 0; + if (canRead) mode |= 292 | 73; + if (canWrite) mode |= 146; + return mode; + }, + findObject: function(path, dontResolveLastLink) { + var ret = FS.analyzePath(path, dontResolveLastLink); + if (ret.exists) { + return ret.object; + } else { + return null; + } + }, + analyzePath: function(path, dontResolveLastLink) { + try { + var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + path = lookup.path; + } catch (e) {} + var ret = { + isRoot: false, + exists: false, + error: 0, + name: null, + path: null, + object: null, + parentExists: false, + parentPath: null, + parentObject: null + }; + try { + var lookup = FS.lookupPath(path, { parent: true }); + ret.parentExists = true; + ret.parentPath = lookup.path; + ret.parentObject = lookup.node; + ret.name = PATH.basename(path); + lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + ret.exists = true; + ret.path = lookup.path; + ret.object = lookup.node; + ret.name = lookup.node.name; + ret.isRoot = lookup.path === "/"; + } catch (e) { + ret.error = e.errno; + } + return ret; + }, + createPath: function(parent, path, canRead, canWrite) { + parent = typeof parent === "string" ? parent : FS.getPath(parent); + var parts = path.split("/").reverse(); + while (parts.length) { + var part = parts.pop(); + if (!part) continue; + var current = PATH.join2(parent, part); + try { + FS.mkdir(current); + } catch (e) {} + parent = current; + } + return current; + }, + createFile: function(parent, name, properties, canRead, canWrite) { + var path = PATH.join2( + typeof parent === "string" ? parent : FS.getPath(parent), + name + ); + var mode = FS.getMode(canRead, canWrite); + return FS.create(path, mode); + }, + createDataFile: function(parent, name, data, canRead, canWrite, canOwn) { + var path = name + ? PATH.join2( + typeof parent === "string" ? parent : FS.getPath(parent), + name + ) + : parent; + var mode = FS.getMode(canRead, canWrite); + var node = FS.create(path, mode); + if (data) { + if (typeof data === "string") { + var arr = new Array(data.length); + for (var i = 0, len = data.length; i < len; ++i) + arr[i] = data.charCodeAt(i); + data = arr; + } + FS.chmod(node, mode | 146); + var stream = FS.open(node, 577); + FS.write(stream, data, 0, data.length, 0, canOwn); + FS.close(stream); + FS.chmod(node, mode); + } + return node; + }, + createDevice: function(parent, name, input, output) { + var path = PATH.join2( + typeof parent === "string" ? parent : FS.getPath(parent), + name + ); + var mode = FS.getMode(!!input, !!output); + if (!FS.createDevice.major) FS.createDevice.major = 64; + var dev = FS.makedev(FS.createDevice.major++, 0); + FS.registerDevice(dev, { + open: function(stream) { + stream.seekable = false; + }, + close: function(stream) { + if (output && output.buffer && output.buffer.length) { + output(10); + } + }, + read: function(stream, buffer, offset, length, pos) { + var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = input(); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (result === undefined && bytesRead === 0) { + throw new FS.ErrnoError(6); + } + if (result === null || result === undefined) break; + bytesRead++; + buffer[offset + i] = result; + } + if (bytesRead) { + stream.node.timestamp = Date.now(); + } + return bytesRead; + }, + write: function(stream, buffer, offset, length, pos) { + for (var i = 0; i < length; i++) { + try { + output(buffer[offset + i]); + } catch (e) { + throw new FS.ErrnoError(29); + } + } + if (length) { + stream.node.timestamp = Date.now(); + } + return i; + } + }); + return FS.mkdev(path, mode, dev); + }, + forceLoadFile: function(obj) { + if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; + if (typeof XMLHttpRequest !== "undefined") { + throw new Error( + "Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread." + ); + } else if (read_) { + try { + obj.contents = intArrayFromString(read_(obj.url), true); + obj.usedBytes = obj.contents.length; + } catch (e) { + throw new FS.ErrnoError(29); + } + } else { + throw new Error("Cannot load without read() or XMLHttpRequest."); + } + }, + createLazyFile: function(parent, name, url, canRead, canWrite) { + function LazyUint8Array() { + this.lengthKnown = false; + this.chunks = []; + } + LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) { + if (idx > this.length - 1 || idx < 0) { + return undefined; + } + var chunkOffset = idx % this.chunkSize; + var chunkNum = (idx / this.chunkSize) | 0; + return this.getter(chunkNum)[chunkOffset]; + }; + LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter( + getter + ) { + this.getter = getter; + }; + LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { + var xhr = new XMLHttpRequest(); + xhr.open("HEAD", url, false); + xhr.send(null); + if (!((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) + throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + var datalength = Number(xhr.getResponseHeader("Content-length")); + var header; + var hasByteServing = + (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; + var usesGzip = + (header = xhr.getResponseHeader("Content-Encoding")) && + header === "gzip"; + var chunkSize = 1024 * 1024; + if (!hasByteServing) chunkSize = datalength; + var doXHR = function(from, to) { + if (from > to) + throw new Error( + "invalid range (" + from + ", " + to + ") or no bytes requested!" + ); + if (to > datalength - 1) + throw new Error( + "only " + datalength + " bytes available! programmer error!" + ); + var xhr = new XMLHttpRequest(); + xhr.open("GET", url, false); + if (datalength !== chunkSize) + xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); + if (typeof Uint8Array != "undefined") xhr.responseType = "arraybuffer"; + if (xhr.overrideMimeType) { + xhr.overrideMimeType("text/plain; charset=x-user-defined"); + } + xhr.send(null); + if (!((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) + throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + if (xhr.response !== undefined) { + return new Uint8Array(xhr.response || []); + } else { + return intArrayFromString(xhr.responseText || "", true); + } + }; + var lazyArray = this; + lazyArray.setDataGetter(function(chunkNum) { + var start = chunkNum * chunkSize; + var end = (chunkNum + 1) * chunkSize - 1; + end = Math.min(end, datalength - 1); + if (typeof lazyArray.chunks[chunkNum] === "undefined") { + lazyArray.chunks[chunkNum] = doXHR(start, end); + } + if (typeof lazyArray.chunks[chunkNum] === "undefined") + throw new Error("doXHR failed!"); + return lazyArray.chunks[chunkNum]; + }); + if (usesGzip || !datalength) { + chunkSize = datalength = 1; + datalength = this.getter(0).length; + chunkSize = datalength; + out( + "LazyFiles on gzip forces download of the whole file when length is accessed" + ); + } + this._length = datalength; + this._chunkSize = chunkSize; + this.lengthKnown = true; + }; + if (typeof XMLHttpRequest !== "undefined") { + if (!ENVIRONMENT_IS_WORKER) + throw "Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc"; + var lazyArray = new LazyUint8Array(); + Object.defineProperties(lazyArray, { + length: { + get: function() { + if (!this.lengthKnown) { + this.cacheLength(); + } + return this._length; + } + }, + chunkSize: { + get: function() { + if (!this.lengthKnown) { + this.cacheLength(); + } + return this._chunkSize; + } + } + }); + var properties = { isDevice: false, contents: lazyArray }; + } else { + var properties = { isDevice: false, url: url }; + } + var node = FS.createFile(parent, name, properties, canRead, canWrite); + if (properties.contents) { + node.contents = properties.contents; + } else if (properties.url) { + node.contents = null; + node.url = properties.url; + } + Object.defineProperties(node, { + usedBytes: { + get: function() { + return this.contents.length; + } + } + }); + var stream_ops = {}; + var keys = Object.keys(node.stream_ops); + keys.forEach(function(key) { + var fn = node.stream_ops[key]; + stream_ops[key] = function forceLoadLazyFile() { + FS.forceLoadFile(node); + return fn.apply(null, arguments); + }; + }); + stream_ops.read = function stream_ops_read( + stream, + buffer, + offset, + length, + position + ) { + FS.forceLoadFile(node); + var contents = stream.node.contents; + if (position >= contents.length) return 0; + var size = Math.min(contents.length - position, length); + if (contents.slice) { + for (var i = 0; i < size; i++) { + buffer[offset + i] = contents[position + i]; + } + } else { + for (var i = 0; i < size; i++) { + buffer[offset + i] = contents.get(position + i); + } + } + return size; + }; + node.stream_ops = stream_ops; + return node; + }, + createPreloadedFile: function( + parent, + name, + url, + canRead, + canWrite, + onload, + onerror, + dontCreateFile, + canOwn, + preFinish + ) { + Browser.init(); + var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent; + var dep = getUniqueRunDependency("cp " + fullname); + function processData(byteArray) { + function finish(byteArray) { + if (preFinish) preFinish(); + if (!dontCreateFile) { + FS.createDataFile(parent, name, byteArray, canRead, canWrite, canOwn); + } + if (onload) onload(); + removeRunDependency(dep); + } + var handled = false; + Module["preloadPlugins"].forEach(function(plugin) { + if (handled) return; + if (plugin["canHandle"](fullname)) { + plugin["handle"](byteArray, fullname, finish, function() { + if (onerror) onerror(); + removeRunDependency(dep); + }); + handled = true; + } + }); + if (!handled) finish(byteArray); + } + addRunDependency(dep); + if (typeof url == "string") { + Browser.asyncLoad( + url, + function(byteArray) { + processData(byteArray); + }, + onerror + ); + } else { + processData(url); + } + }, + indexedDB: function() { + return ( + window.indexedDB || + window.mozIndexedDB || + window.webkitIndexedDB || + window.msIndexedDB + ); + }, + DB_NAME: function() { + return "EM_FS_" + window.location.pathname; + }, + DB_VERSION: 20, + DB_STORE_NAME: "FILE_DATA", + saveFilesToDB: function(paths, onload, onerror) { + onload = onload || function() {}; + onerror = onerror || function() {}; + var indexedDB = FS.indexedDB(); + try { + var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION); + } catch (e) { + return onerror(e); + } + openRequest.onupgradeneeded = function openRequest_onupgradeneeded() { + out("creating db"); + var db = openRequest.result; + db.createObjectStore(FS.DB_STORE_NAME); + }; + openRequest.onsuccess = function openRequest_onsuccess() { + var db = openRequest.result; + var transaction = db.transaction([FS.DB_STORE_NAME], "readwrite"); + var files = transaction.objectStore(FS.DB_STORE_NAME); + var ok = 0, + fail = 0, + total = paths.length; + function finish() { + if (fail == 0) onload(); + else onerror(); + } + paths.forEach(function(path) { + var putRequest = files.put(FS.analyzePath(path).object.contents, path); + putRequest.onsuccess = function putRequest_onsuccess() { + ok++; + if (ok + fail == total) finish(); + }; + putRequest.onerror = function putRequest_onerror() { + fail++; + if (ok + fail == total) finish(); + }; + }); + transaction.onerror = onerror; + }; + openRequest.onerror = onerror; + }, + loadFilesFromDB: function(paths, onload, onerror) { + onload = onload || function() {}; + onerror = onerror || function() {}; + var indexedDB = FS.indexedDB(); + try { + var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION); + } catch (e) { + return onerror(e); + } + openRequest.onupgradeneeded = onerror; + openRequest.onsuccess = function openRequest_onsuccess() { + var db = openRequest.result; + try { + var transaction = db.transaction([FS.DB_STORE_NAME], "readonly"); + } catch (e) { + onerror(e); + return; + } + var files = transaction.objectStore(FS.DB_STORE_NAME); + var ok = 0, + fail = 0, + total = paths.length; + function finish() { + if (fail == 0) onload(); + else onerror(); + } + paths.forEach(function(path) { + var getRequest = files.get(path); + getRequest.onsuccess = function getRequest_onsuccess() { + if (FS.analyzePath(path).exists) { + FS.unlink(path); + } + FS.createDataFile( + PATH.dirname(path), + PATH.basename(path), + getRequest.result, + true, + true, + true + ); + ok++; + if (ok + fail == total) finish(); + }; + getRequest.onerror = function getRequest_onerror() { + fail++; + if (ok + fail == total) finish(); + }; + }); + transaction.onerror = onerror; + }; + openRequest.onerror = onerror; + } +}; +var SYSCALLS = { + mappings: {}, + DEFAULT_POLLMASK: 5, + umask: 511, + calculateAt: function(dirfd, path, allowEmpty) { + if (path[0] === "/") { + return path; + } + var dir; + if (dirfd === -100) { + dir = FS.cwd(); + } else { + var dirstream = FS.getStream(dirfd); + if (!dirstream) throw new FS.ErrnoError(8); + dir = dirstream.path; + } + if (path.length == 0) { + if (!allowEmpty) { + throw new FS.ErrnoError(44); + } + return dir; + } + return PATH.join2(dir, path); + }, + doStat: function(func, path, buf) { + try { + var stat = func(path); + } catch (e) { + if ( + e && + e.node && + PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node)) + ) { + return -54; + } + throw e; + } + HEAP32[buf >> 2] = stat.dev; + HEAP32[(buf + 4) >> 2] = 0; + HEAP32[(buf + 8) >> 2] = stat.ino; + HEAP32[(buf + 12) >> 2] = stat.mode; + HEAP32[(buf + 16) >> 2] = stat.nlink; + HEAP32[(buf + 20) >> 2] = stat.uid; + HEAP32[(buf + 24) >> 2] = stat.gid; + HEAP32[(buf + 28) >> 2] = stat.rdev; + HEAP32[(buf + 32) >> 2] = 0; + (tempI64 = [ + stat.size >>> 0, + ((tempDouble = stat.size), + +Math.abs(tempDouble) >= 1 + ? tempDouble > 0 + ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> + 0 + : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> + 0 + : 0) + ]), + (HEAP32[(buf + 40) >> 2] = tempI64[0]), + (HEAP32[(buf + 44) >> 2] = tempI64[1]); + HEAP32[(buf + 48) >> 2] = 4096; + HEAP32[(buf + 52) >> 2] = stat.blocks; + HEAP32[(buf + 56) >> 2] = (stat.atime.getTime() / 1e3) | 0; + HEAP32[(buf + 60) >> 2] = 0; + HEAP32[(buf + 64) >> 2] = (stat.mtime.getTime() / 1e3) | 0; + HEAP32[(buf + 68) >> 2] = 0; + HEAP32[(buf + 72) >> 2] = (stat.ctime.getTime() / 1e3) | 0; + HEAP32[(buf + 76) >> 2] = 0; + (tempI64 = [ + stat.ino >>> 0, + ((tempDouble = stat.ino), + +Math.abs(tempDouble) >= 1 + ? tempDouble > 0 + ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> + 0 + : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> + 0 + : 0) + ]), + (HEAP32[(buf + 80) >> 2] = tempI64[0]), + (HEAP32[(buf + 84) >> 2] = tempI64[1]); + return 0; + }, + doMsync: function(addr, stream, len, flags, offset) { + var buffer = HEAPU8.slice(addr, addr + len); + FS.msync(stream, buffer, offset, len, flags); + }, + doMkdir: function(path, mode) { + path = PATH.normalize(path); + if (path[path.length - 1] === "/") path = path.substr(0, path.length - 1); + FS.mkdir(path, mode, 0); + return 0; + }, + doMknod: function(path, mode, dev) { + switch (mode & 61440) { + case 32768: + case 8192: + case 24576: + case 4096: + case 49152: + break; + default: + return -28; + } + FS.mknod(path, mode, dev); + return 0; + }, + doReadlink: function(path, buf, bufsize) { + if (bufsize <= 0) return -28; + var ret = FS.readlink(path); + var len = Math.min(bufsize, lengthBytesUTF8(ret)); + var endChar = HEAP8[buf + len]; + stringToUTF8(ret, buf, bufsize + 1); + HEAP8[buf + len] = endChar; + return len; + }, + doAccess: function(path, amode) { + if (amode & ~7) { + return -28; + } + var node; + var lookup = FS.lookupPath(path, { follow: true }); + node = lookup.node; + if (!node) { + return -44; + } + var perms = ""; + if (amode & 4) perms += "r"; + if (amode & 2) perms += "w"; + if (amode & 1) perms += "x"; + if (perms && FS.nodePermissions(node, perms)) { + return -2; + } + return 0; + }, + doDup: function(path, flags, suggestFD) { + var suggest = FS.getStream(suggestFD); + if (suggest) FS.close(suggest); + return FS.open(path, flags, 0, suggestFD, suggestFD).fd; + }, + doReadv: function(stream, iov, iovcnt, offset) { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAP32[(iov + i * 8) >> 2]; + var len = HEAP32[(iov + (i * 8 + 4)) >> 2]; + var curr = FS.read(stream, HEAP8, ptr, len, offset); + if (curr < 0) return -1; + ret += curr; + if (curr < len) break; + } + return ret; + }, + doWritev: function(stream, iov, iovcnt, offset) { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAP32[(iov + i * 8) >> 2]; + var len = HEAP32[(iov + (i * 8 + 4)) >> 2]; + var curr = FS.write(stream, HEAP8, ptr, len, offset); + if (curr < 0) return -1; + ret += curr; + } + return ret; + }, + varargs: undefined, + get: function() { + SYSCALLS.varargs += 4; + var ret = HEAP32[(SYSCALLS.varargs - 4) >> 2]; + return ret; + }, + getStr: function(ptr) { + var ret = UTF8ToString(ptr); + return ret; + }, + getStreamFromFD: function(fd) { + var stream = FS.getStream(fd); + if (!stream) throw new FS.ErrnoError(8); + return stream; + }, + get64: function(low, high) { + return low; + } +}; +function ___sys_chmod(path, mode) { + try { + path = SYSCALLS.getStr(path); + FS.chmod(path, mode); + return 0; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return -e.errno; + } +} +function setErrNo(value) { + HEAP32[___errno_location() >> 2] = value; + return value; +} +function ___sys_fcntl64(fd, cmd, varargs) { + SYSCALLS.varargs = varargs; + try { + var stream = SYSCALLS.getStreamFromFD(fd); + switch (cmd) { + case 0: { + var arg = SYSCALLS.get(); + if (arg < 0) { + return -28; + } + var newStream; + newStream = FS.open(stream.path, stream.flags, 0, arg); + return newStream.fd; + } + case 1: + case 2: + return 0; + case 3: + return stream.flags; + case 4: { + var arg = SYSCALLS.get(); + stream.flags |= arg; + return 0; + } + case 12: { + var arg = SYSCALLS.get(); + var offset = 0; + HEAP16[(arg + offset) >> 1] = 2; + return 0; + } + case 13: + case 14: + return 0; + case 16: + case 8: + return -28; + case 9: + setErrNo(28); + return -1; + default: { + return -28; + } + } + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return -e.errno; + } +} +function ___sys_fstat64(fd, buf) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + return SYSCALLS.doStat(FS.stat, stream.path, buf); + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return -e.errno; + } +} +function ___sys_ioctl(fd, op, varargs) { + SYSCALLS.varargs = varargs; + try { + var stream = SYSCALLS.getStreamFromFD(fd); + switch (op) { + case 21509: + case 21505: { + if (!stream.tty) return -59; + return 0; + } + case 21510: + case 21511: + case 21512: + case 21506: + case 21507: + case 21508: { + if (!stream.tty) return -59; + return 0; + } + case 21519: { + if (!stream.tty) return -59; + var argp = SYSCALLS.get(); + HEAP32[argp >> 2] = 0; + return 0; + } + case 21520: { + if (!stream.tty) return -59; + return -28; + } + case 21531: { + var argp = SYSCALLS.get(); + return FS.ioctl(stream, op, argp); + } + case 21523: { + if (!stream.tty) return -59; + return 0; + } + case 21524: { + if (!stream.tty) return -59; + return 0; + } + default: + abort("bad ioctl syscall " + op); + } + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return -e.errno; + } +} +function ___sys_open(path, flags, varargs) { + SYSCALLS.varargs = varargs; + try { + var pathname = SYSCALLS.getStr(path); + var mode = varargs ? SYSCALLS.get() : 0; + var stream = FS.open(pathname, flags, mode); + return stream.fd; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return -e.errno; + } +} +function ___sys_rename(old_path, new_path) { + try { + old_path = SYSCALLS.getStr(old_path); + new_path = SYSCALLS.getStr(new_path); + FS.rename(old_path, new_path); + return 0; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return -e.errno; + } +} +function ___sys_rmdir(path) { + try { + path = SYSCALLS.getStr(path); + FS.rmdir(path); + return 0; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return -e.errno; + } +} +function ___sys_stat64(path, buf) { + try { + path = SYSCALLS.getStr(path); + return SYSCALLS.doStat(FS.stat, path, buf); + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return -e.errno; + } +} +function ___sys_unlink(path) { + try { + path = SYSCALLS.getStr(path); + FS.unlink(path); + return 0; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return -e.errno; + } +} +function _emscripten_memcpy_big(dest, src, num) { + HEAPU8.copyWithin(dest, src, src + num); +} +function emscripten_realloc_buffer(size) { + try { + wasmMemory.grow((size - buffer.byteLength + 65535) >>> 16); + updateGlobalBufferAndViews(wasmMemory.buffer); + return 1; + } catch (e) {} +} +function _emscripten_resize_heap(requestedSize) { + var oldSize = HEAPU8.length; + requestedSize = requestedSize >>> 0; + var maxHeapSize = 2147483648; + if (requestedSize > maxHeapSize) { + return false; + } + for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { + var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown); + overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296); + var newSize = Math.min( + maxHeapSize, + alignUp(Math.max(requestedSize, overGrownHeapSize), 65536) + ); + var replacement = emscripten_realloc_buffer(newSize); + if (replacement) { + return true; + } + } + return false; +} +function _fd_close(fd) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + FS.close(stream); + return 0; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return e.errno; + } +} +function _fd_fdstat_get(fd, pbuf) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + var type = stream.tty + ? 2 + : FS.isDir(stream.mode) + ? 3 + : FS.isLink(stream.mode) + ? 7 + : 4; + HEAP8[pbuf >> 0] = type; + return 0; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return e.errno; + } +} +function _fd_read(fd, iov, iovcnt, pnum) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + var num = SYSCALLS.doReadv(stream, iov, iovcnt); + HEAP32[pnum >> 2] = num; + return 0; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return e.errno; + } +} +function _fd_seek(fd, offset_low, offset_high, whence, newOffset) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + var HIGH_OFFSET = 4294967296; + var offset = offset_high * HIGH_OFFSET + (offset_low >>> 0); + var DOUBLE_LIMIT = 9007199254740992; + if (offset <= -DOUBLE_LIMIT || offset >= DOUBLE_LIMIT) { + return -61; + } + FS.llseek(stream, offset, whence); + (tempI64 = [ + stream.position >>> 0, + ((tempDouble = stream.position), + +Math.abs(tempDouble) >= 1 + ? tempDouble > 0 + ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> + 0 + : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> + 0 + : 0) + ]), + (HEAP32[newOffset >> 2] = tempI64[0]), + (HEAP32[(newOffset + 4) >> 2] = tempI64[1]); + if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null; + return 0; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return e.errno; + } +} +function _fd_write(fd, iov, iovcnt, pnum) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + var num = SYSCALLS.doWritev(stream, iov, iovcnt); + HEAP32[pnum >> 2] = num; + return 0; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e); + return e.errno; + } +} +function _setTempRet0(val) { + setTempRet0(val); +} +function _time(ptr) { + var ret = (Date.now() / 1e3) | 0; + if (ptr) { + HEAP32[ptr >> 2] = ret; + } + return ret; +} +function _tzset() { + if (_tzset.called) return; + _tzset.called = true; + var currentYear = new Date().getFullYear(); + var winter = new Date(currentYear, 0, 1); + var summer = new Date(currentYear, 6, 1); + var winterOffset = winter.getTimezoneOffset(); + var summerOffset = summer.getTimezoneOffset(); + var stdTimezoneOffset = Math.max(winterOffset, summerOffset); + HEAP32[__get_timezone() >> 2] = stdTimezoneOffset * 60; + HEAP32[__get_daylight() >> 2] = Number(winterOffset != summerOffset); + function extractZone(date) { + var match = date.toTimeString().match(/\(([A-Za-z ]+)\)$/); + return match ? match[1] : "GMT"; + } + var winterName = extractZone(winter); + var summerName = extractZone(summer); + var winterNamePtr = allocateUTF8(winterName); + var summerNamePtr = allocateUTF8(summerName); + if (summerOffset < winterOffset) { + HEAP32[__get_tzname() >> 2] = winterNamePtr; + HEAP32[(__get_tzname() + 4) >> 2] = summerNamePtr; + } else { + HEAP32[__get_tzname() >> 2] = summerNamePtr; + HEAP32[(__get_tzname() + 4) >> 2] = winterNamePtr; + } +} +function _timegm(tmPtr) { + _tzset(); + var time = Date.UTC( + HEAP32[(tmPtr + 20) >> 2] + 1900, + HEAP32[(tmPtr + 16) >> 2], + HEAP32[(tmPtr + 12) >> 2], + HEAP32[(tmPtr + 8) >> 2], + HEAP32[(tmPtr + 4) >> 2], + HEAP32[tmPtr >> 2], + 0 + ); + var date = new Date(time); + HEAP32[(tmPtr + 24) >> 2] = date.getUTCDay(); + var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); + var yday = ((date.getTime() - start) / (1e3 * 60 * 60 * 24)) | 0; + HEAP32[(tmPtr + 28) >> 2] = yday; + return (date.getTime() / 1e3) | 0; +} +var FSNode = function(parent, name, mode, rdev) { + if (!parent) { + parent = this; + } + this.parent = parent; + this.mount = parent.mount; + this.mounted = null; + this.id = FS.nextInode++; + this.name = name; + this.mode = mode; + this.node_ops = {}; + this.stream_ops = {}; + this.rdev = rdev; +}; +var readMode = 292 | 73; +var writeMode = 146; +Object.defineProperties(FSNode.prototype, { + read: { + get: function() { + return (this.mode & readMode) === readMode; + }, + set: function(val) { + val ? (this.mode |= readMode) : (this.mode &= ~readMode); + } + }, + write: { + get: function() { + return (this.mode & writeMode) === writeMode; + }, + set: function(val) { + val ? (this.mode |= writeMode) : (this.mode &= ~writeMode); + } + }, + isFolder: { + get: function() { + return FS.isDir(this.mode); + } + }, + isDevice: { + get: function() { + return FS.isChrdev(this.mode); + } + } +}); +FS.FSNode = FSNode; +FS.staticInit(); +if (ENVIRONMENT_IS_NODE) { + var fs = frozenFs; + var NODEJS_PATH = __webpack_require__(622); + NODEFS.staticInit(); +} +if (ENVIRONMENT_IS_NODE) { + var _wrapNodeError = function(func) { + return function() { + try { + return func.apply(this, arguments); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }; + }; + var VFS = Object.assign({}, FS); + for (var _key in NODERAWFS) FS[_key] = _wrapNodeError(NODERAWFS[_key]); +} else { + throw new Error( + "NODERAWFS is currently only supported on Node.js environment." + ); +} +function intArrayFromString(stringy, dontAddNull, length) { + var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1; + var u8array = new Array(len); + var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length); + if (dontAddNull) u8array.length = numBytesWritten; + return u8array; +} +var decodeBase64 = + typeof atob === "function" + ? atob + : function(input) { + var keyStr = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + do { + enc1 = keyStr.indexOf(input.charAt(i++)); + enc2 = keyStr.indexOf(input.charAt(i++)); + enc3 = keyStr.indexOf(input.charAt(i++)); + enc4 = keyStr.indexOf(input.charAt(i++)); + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + output = output + String.fromCharCode(chr1); + if (enc3 !== 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 !== 64) { + output = output + String.fromCharCode(chr3); + } + } while (i < input.length); + return output; + }; +function intArrayFromBase64(s) { + if (typeof ENVIRONMENT_IS_NODE === "boolean" && ENVIRONMENT_IS_NODE) { + var buf; + try { + buf = Buffer.from(s, "base64"); + } catch (_) { + buf = new Buffer(s, "base64"); + } + return new Uint8Array(buf["buffer"], buf["byteOffset"], buf["byteLength"]); + } + try { + var decoded = decodeBase64(s); + var bytes = new Uint8Array(decoded.length); + for (var i = 0; i < decoded.length; ++i) { + bytes[i] = decoded.charCodeAt(i); + } + return bytes; + } catch (_) { + throw new Error("Converting base64 string to bytes failed."); + } +} +function tryParseAsDataURI(filename) { + if (!isDataURI(filename)) { + return; + } + return intArrayFromBase64(filename.slice(dataURIPrefix.length)); +} +var asmLibraryArg = { + l: ___gmtime_r, + p: ___sys_chmod, + e: ___sys_fcntl64, + k: ___sys_fstat64, + o: ___sys_ioctl, + q: ___sys_open, + i: ___sys_rename, + r: ___sys_rmdir, + c: ___sys_stat64, + h: ___sys_unlink, + s: _emscripten_memcpy_big, + t: _emscripten_resize_heap, + f: _fd_close, + j: _fd_fdstat_get, + g: _fd_read, + n: _fd_seek, + d: _fd_write, + a: _setTempRet0, + b: _time, + m: _timegm +}; +var asm = createWasm(); +var ___wasm_call_ctors = (Module["___wasm_call_ctors"] = asm["v"]); +var _zipstruct_stat = (Module["_zipstruct_stat"] = asm["w"]); +var _zipstruct_statS = (Module["_zipstruct_statS"] = asm["x"]); +var _zipstruct_stat_name = (Module["_zipstruct_stat_name"] = asm["y"]); +var _zipstruct_stat_index = (Module["_zipstruct_stat_index"] = asm["z"]); +var _zipstruct_stat_size = (Module["_zipstruct_stat_size"] = asm["A"]); +var _zipstruct_stat_mtime = (Module["_zipstruct_stat_mtime"] = asm["B"]); +var _zipstruct_stat_crc = (Module["_zipstruct_stat_crc"] = asm["C"]); +var _zipstruct_error = (Module["_zipstruct_error"] = asm["D"]); +var _zipstruct_errorS = (Module["_zipstruct_errorS"] = asm["E"]); +var _zipstruct_error_code_zip = (Module["_zipstruct_error_code_zip"] = + asm["F"]); +var _zipstruct_stat_comp_size = (Module["_zipstruct_stat_comp_size"] = + asm["G"]); +var _zipstruct_stat_comp_method = (Module["_zipstruct_stat_comp_method"] = + asm["H"]); +var _zip_close = (Module["_zip_close"] = asm["I"]); +var _zip_delete = (Module["_zip_delete"] = asm["J"]); +var _zip_dir_add = (Module["_zip_dir_add"] = asm["K"]); +var _zip_discard = (Module["_zip_discard"] = asm["L"]); +var _zip_error_init_with_code = (Module["_zip_error_init_with_code"] = + asm["M"]); +var _zip_get_error = (Module["_zip_get_error"] = asm["N"]); +var _zip_file_get_error = (Module["_zip_file_get_error"] = asm["O"]); +var _zip_error_strerror = (Module["_zip_error_strerror"] = asm["P"]); +var _zip_fclose = (Module["_zip_fclose"] = asm["Q"]); +var _zip_file_add = (Module["_zip_file_add"] = asm["R"]); +var _zip_file_get_external_attributes = (Module[ + "_zip_file_get_external_attributes" +] = asm["S"]); +var _zip_file_set_external_attributes = (Module[ + "_zip_file_set_external_attributes" +] = asm["T"]); +var _zip_file_set_mtime = (Module["_zip_file_set_mtime"] = asm["U"]); +var _zip_fopen = (Module["_zip_fopen"] = asm["V"]); +var _zip_fopen_index = (Module["_zip_fopen_index"] = asm["W"]); +var _zip_fread = (Module["_zip_fread"] = asm["X"]); +var _zip_get_name = (Module["_zip_get_name"] = asm["Y"]); +var _zip_get_num_entries = (Module["_zip_get_num_entries"] = asm["Z"]); +var _zip_name_locate = (Module["_zip_name_locate"] = asm["_"]); +var _zip_open = (Module["_zip_open"] = asm["$"]); +var _zip_open_from_source = (Module["_zip_open_from_source"] = asm["aa"]); +var _zip_set_file_compression = (Module["_zip_set_file_compression"] = + asm["ba"]); +var _zip_source_buffer = (Module["_zip_source_buffer"] = asm["ca"]); +var _zip_source_buffer_create = (Module["_zip_source_buffer_create"] = + asm["da"]); +var _zip_source_close = (Module["_zip_source_close"] = asm["ea"]); +var _zip_source_error = (Module["_zip_source_error"] = asm["fa"]); +var _zip_source_free = (Module["_zip_source_free"] = asm["ga"]); +var _zip_source_keep = (Module["_zip_source_keep"] = asm["ha"]); +var _zip_source_open = (Module["_zip_source_open"] = asm["ia"]); +var _zip_source_read = (Module["_zip_source_read"] = asm["ja"]); +var _zip_source_seek = (Module["_zip_source_seek"] = asm["ka"]); +var _zip_source_set_mtime = (Module["_zip_source_set_mtime"] = asm["la"]); +var _zip_source_tell = (Module["_zip_source_tell"] = asm["ma"]); +var _zip_stat = (Module["_zip_stat"] = asm["na"]); +var _zip_stat_index = (Module["_zip_stat_index"] = asm["oa"]); +var _zip_ext_count_symlinks = (Module["_zip_ext_count_symlinks"] = asm["pa"]); +var ___errno_location = (Module["___errno_location"] = asm["qa"]); +var __get_tzname = (Module["__get_tzname"] = asm["ra"]); +var __get_daylight = (Module["__get_daylight"] = asm["sa"]); +var __get_timezone = (Module["__get_timezone"] = asm["ta"]); +var stackSave = (Module["stackSave"] = asm["ua"]); +var stackRestore = (Module["stackRestore"] = asm["va"]); +var stackAlloc = (Module["stackAlloc"] = asm["wa"]); +var _malloc = (Module["_malloc"] = asm["xa"]); +var _free = (Module["_free"] = asm["ya"]); +Module["cwrap"] = cwrap; +Module["getValue"] = getValue; +var calledRun; +dependenciesFulfilled = function runCaller() { + if (!calledRun) run(); + if (!calledRun) dependenciesFulfilled = runCaller; +}; +function run(args) { + args = args || arguments_; + if (runDependencies > 0) { + return; + } + preRun(); + if (runDependencies > 0) { + return; + } + function doRun() { + if (calledRun) return; + calledRun = true; + Module["calledRun"] = true; + if (ABORT) return; + initRuntime(); + if (Module["onRuntimeInitialized"]) Module["onRuntimeInitialized"](); + postRun(); + } + if (Module["setStatus"]) { + Module["setStatus"]("Running..."); + setTimeout(function() { + setTimeout(function() { + Module["setStatus"](""); + }, 1); + doRun(); + }, 1); + } else { + doRun(); + } +} +Module["run"] = run; +if (Module["preInit"]) { + if (typeof Module["preInit"] == "function") + Module["preInit"] = [Module["preInit"]]; + while (Module["preInit"].length > 0) { + Module["preInit"].pop()(); + } +} +run(); + + +/***/ }), + +/***/ 417: +/***/ ((module) => { + +"use strict"; +module.exports = require("crypto");; + +/***/ }), + +/***/ 747: +/***/ ((module) => { + +"use strict"; +module.exports = require("fs");; + +/***/ }), + +/***/ 282: +/***/ ((module) => { + +"use strict"; +module.exports = require("module");; + +/***/ }), + +/***/ 622: +/***/ ((module) => { + +"use strict"; +module.exports = require("path");; + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be in strict mode. +(() => { +"use strict"; + +// EXPORTS +__webpack_require__.d(__webpack_exports__, { + "default": () => (/* binding */ _entryPoint) +}); + +// EXTERNAL MODULE: external "fs" +var external_fs_ = __webpack_require__(747); +var external_fs_default = /*#__PURE__*/__webpack_require__.n(external_fs_); +;// CONCATENATED MODULE: external "os" +const external_os_namespaceObject = require("os");; +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/constants.ts +const constants_S_IFMT = 0o170000; +const constants_S_IFDIR = 0o040000; +const constants_S_IFREG = 0o100000; +const constants_S_IFLNK = 0o120000; +/** + * Unix timestamp for `1984-06-22T21:50:00.000Z` + * + * It needs to be after 1980-01-01 because that's what Zip supports, and it + * needs to have a slight offset to account for different timezones (because + * zip assumes that all times are local to whoever writes the file, which is + * really silly). + */ + +const SAFE_TIME = 456789000; +// EXTERNAL MODULE: external "path" +var external_path_ = __webpack_require__(622); +var external_path_default = /*#__PURE__*/__webpack_require__.n(external_path_); +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/path.ts + +var PathType; + +(function (PathType) { + PathType[PathType["File"] = 0] = "File"; + PathType[PathType["Portable"] = 1] = "Portable"; + PathType[PathType["Native"] = 2] = "Native"; +})(PathType || (PathType = {})); + +const PortablePath = { + root: `/`, + dot: `.` +}; +const Filename = { + nodeModules: `node_modules`, + manifest: `package.json`, + lockfile: `yarn.lock`, + virtual: `__virtual__`, + + /** + * @deprecated + */ + pnpJs: `.pnp.js`, + pnpCjs: `.pnp.cjs`, + rc: `.yarnrc.yml` +}; +const npath = Object.create((external_path_default())); +const ppath = Object.create((external_path_default()).posix); + +npath.cwd = () => process.cwd(); + +ppath.cwd = () => toPortablePath(process.cwd()); + +ppath.resolve = (...segments) => { + if (segments.length > 0 && ppath.isAbsolute(segments[0])) { + return external_path_default().posix.resolve(...segments); + } else { + return external_path_default().posix.resolve(ppath.cwd(), ...segments); + } +}; + +const contains = function (pathUtils, from, to) { + from = pathUtils.normalize(from); + to = pathUtils.normalize(to); + if (from === to) return `.`; + if (!from.endsWith(pathUtils.sep)) from = from + pathUtils.sep; + + if (to.startsWith(from)) { + return to.slice(from.length); + } else { + return null; + } +}; + +npath.fromPortablePath = fromPortablePath; +npath.toPortablePath = toPortablePath; + +npath.contains = (from, to) => contains(npath, from, to); + +ppath.contains = (from, to) => contains(ppath, from, to); + +const WINDOWS_PATH_REGEXP = /^([a-zA-Z]:.*)$/; +const UNC_WINDOWS_PATH_REGEXP = /^\\\\(\.\\)?(.*)$/; +const PORTABLE_PATH_REGEXP = /^\/([a-zA-Z]:.*)$/; +const UNC_PORTABLE_PATH_REGEXP = /^\/unc\/(\.dot\/)?(.*)$/; // Path should look like "/N:/berry/scripts/plugin-pack.js" +// And transform to "N:\berry\scripts\plugin-pack.js" + +function fromPortablePath(p) { + if (process.platform !== `win32`) return p; + let portablePathMatch, uncPortablePathMatch; + if (portablePathMatch = p.match(PORTABLE_PATH_REGEXP)) p = portablePathMatch[1];else if (uncPortablePathMatch = p.match(UNC_PORTABLE_PATH_REGEXP)) p = `\\\\${uncPortablePathMatch[1] ? `.\\` : ``}${uncPortablePathMatch[2]}`;else return p; + return p.replace(/\//g, `\\`); +} // Path should look like "N:/berry/scripts/plugin-pack.js" +// And transform to "/N:/berry/scripts/plugin-pack.js" + + +function toPortablePath(p) { + if (process.platform !== `win32`) return p; + let windowsPathMatch, uncWindowsPathMatch; + if (windowsPathMatch = p.match(WINDOWS_PATH_REGEXP)) p = `/${windowsPathMatch[1]}`;else if (uncWindowsPathMatch = p.match(UNC_WINDOWS_PATH_REGEXP)) p = `/unc/${uncWindowsPathMatch[1] ? `.dot/` : ``}${uncWindowsPathMatch[2]}`; + return p.replace(/\\/g, `/`); +} + +function convertPath(targetPathUtils, sourcePath) { + return targetPathUtils === npath ? fromPortablePath(sourcePath) : toPortablePath(sourcePath); +} +function toFilename(filename) { + if (npath.parse(filename).dir !== `` || ppath.parse(filename).dir !== ``) throw new Error(`Invalid filename: "${filename}"`); + return filename; +} +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/algorithms/copyPromise.ts + + + +const defaultTime = new Date(SAFE_TIME * 1000); +var LinkStrategy; + +(function (LinkStrategy) { + LinkStrategy["Allow"] = "allow"; + LinkStrategy["ReadOnly"] = "readOnly"; +})(LinkStrategy || (LinkStrategy = {})); + +async function copyPromise(destinationFs, destination, sourceFs, source, opts) { + const normalizedDestination = destinationFs.pathUtils.normalize(destination); + const normalizedSource = sourceFs.pathUtils.normalize(source); + const prelayout = []; + const postlayout = []; + const referenceTime = opts.stableTime ? { + mtime: defaultTime, + atime: defaultTime + } : await sourceFs.lstatPromise(normalizedSource); + await destinationFs.mkdirpPromise(destinationFs.pathUtils.dirname(destination), { + utimes: [referenceTime.atime, referenceTime.mtime] + }); + const updateTime = typeof destinationFs.lutimesPromise === `function` ? destinationFs.lutimesPromise.bind(destinationFs) : destinationFs.utimesPromise.bind(destinationFs); + await copyImpl(prelayout, postlayout, updateTime, destinationFs, normalizedDestination, sourceFs, normalizedSource, opts); + + for (const operation of prelayout) await operation(); + + await Promise.all(postlayout.map(operation => { + return operation(); + })); +} + +async function copyImpl(prelayout, postlayout, updateTime, destinationFs, destination, sourceFs, source, opts) { + var _a, _b; + + const destinationStat = await maybeLStat(destinationFs, destination); + const sourceStat = await sourceFs.lstatPromise(source); + const referenceTime = opts.stableTime ? { + mtime: defaultTime, + atime: defaultTime + } : sourceStat; + let updated; + + switch (true) { + case sourceStat.isDirectory(): + { + updated = await copyFolder(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); + } + break; + + case sourceStat.isFile(): + { + updated = await copyFile(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); + } + break; + + case sourceStat.isSymbolicLink(): + { + updated = await copySymlink(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); + } + break; + + default: + { + throw new Error(`Unsupported file type (${sourceStat.mode})`); + } + break; + } + + if (updated || ((_a = destinationStat === null || destinationStat === void 0 ? void 0 : destinationStat.mtime) === null || _a === void 0 ? void 0 : _a.getTime()) !== referenceTime.mtime.getTime() || ((_b = destinationStat === null || destinationStat === void 0 ? void 0 : destinationStat.atime) === null || _b === void 0 ? void 0 : _b.getTime()) !== referenceTime.atime.getTime()) { + postlayout.push(() => updateTime(destination, referenceTime.atime, referenceTime.mtime)); + updated = true; + } + + if (destinationStat === null || (destinationStat.mode & 0o777) !== (sourceStat.mode & 0o777)) { + postlayout.push(() => destinationFs.chmodPromise(destination, sourceStat.mode & 0o777)); + updated = true; + } + + return updated; +} + +async function maybeLStat(baseFs, p) { + try { + return await baseFs.lstatPromise(p); + } catch (e) { + return null; + } +} + +async function copyFolder(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { + if (destinationStat !== null && !destinationStat.isDirectory()) { + if (opts.overwrite) { + prelayout.push(async () => destinationFs.removePromise(destination)); + destinationStat = null; + } else { + return false; + } + } + + let updated = false; + + if (destinationStat === null) { + prelayout.push(async () => { + try { + await destinationFs.mkdirPromise(destination, { + mode: sourceStat.mode + }); + } catch (err) { + if (err.code !== `EEXIST`) { + throw err; + } + } + }); + updated = true; + } + + const entries = await sourceFs.readdirPromise(source); + + if (opts.stableSort) { + for (const entry of entries.sort()) { + if (await copyImpl(prelayout, postlayout, updateTime, destinationFs, destinationFs.pathUtils.join(destination, entry), sourceFs, sourceFs.pathUtils.join(source, entry), opts)) { + updated = true; + } + } + } else { + const entriesUpdateStatus = await Promise.all(entries.map(async entry => { + await copyImpl(prelayout, postlayout, updateTime, destinationFs, destinationFs.pathUtils.join(destination, entry), sourceFs, sourceFs.pathUtils.join(source, entry), opts); + })); + + if (entriesUpdateStatus.some(status => status)) { + updated = true; + } + } + + return updated; +} + +const isCloneSupportedCache = new WeakMap(); + +function makeLinkOperation(opFs, destination, source, sourceStat, linkStrategy) { + return async () => { + await opFs.linkPromise(source, destination); + + if (linkStrategy === LinkStrategy.ReadOnly) { + // We mutate the stat, otherwise it'll be reset by copyImpl + sourceStat.mode &= ~0o222; + await opFs.chmodPromise(destination, sourceStat.mode); + } + }; +} + +function makeCloneLinkOperation(opFs, destination, source, sourceStat, linkStrategy) { + const isCloneSupported = isCloneSupportedCache.get(opFs); + + if (typeof isCloneSupported === `undefined`) { + return async () => { + try { + await opFs.copyFilePromise(source, destination, (external_fs_default()).constants.COPYFILE_FICLONE_FORCE); + isCloneSupportedCache.set(opFs, true); + } catch (err) { + if (err.code === `ENOSYS` || err.code === `ENOTSUP`) { + isCloneSupportedCache.set(opFs, false); + await makeLinkOperation(opFs, destination, source, sourceStat, linkStrategy)(); + } else { + throw err; + } + } + }; + } else { + if (isCloneSupported) { + return async () => opFs.copyFilePromise(source, destination, (external_fs_default()).constants.COPYFILE_FICLONE_FORCE); + } else { + return makeLinkOperation(opFs, destination, source, sourceStat, linkStrategy); + } + } +} + +async function copyFile(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { + var _a; + + if (destinationStat !== null) { + if (opts.overwrite) { + prelayout.push(async () => destinationFs.removePromise(destination)); + destinationStat = null; + } else { + return false; + } + } + + const linkStrategy = (_a = opts.linkStrategy) !== null && _a !== void 0 ? _a : null; + const op = destinationFs === sourceFs ? linkStrategy !== null ? makeCloneLinkOperation(destinationFs, destination, source, sourceStat, linkStrategy) : async () => destinationFs.copyFilePromise(source, destination, (external_fs_default()).constants.COPYFILE_FICLONE) : linkStrategy !== null ? makeLinkOperation(destinationFs, destination, source, sourceStat, linkStrategy) : async () => destinationFs.writeFilePromise(destination, await sourceFs.readFilePromise(source)); + prelayout.push(async () => op()); + return true; +} + +async function copySymlink(prelayout, postlayout, updateTime, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { + if (destinationStat !== null) { + if (opts.overwrite) { + prelayout.push(async () => destinationFs.removePromise(destination)); + destinationStat = null; + } else { + return false; + } + } + + prelayout.push(async () => { + await destinationFs.symlinkPromise(convertPath(destinationFs.pathUtils, await sourceFs.readlinkPromise(source)), destination); + }); + return true; +} +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/FakeFS.ts + + + +class FakeFS { + constructor(pathUtils) { + this.pathUtils = pathUtils; + } + + async *genTraversePromise(init, { + stableSort = false + } = {}) { + const stack = [init]; + + while (stack.length > 0) { + const p = stack.shift(); + const entry = await this.lstatPromise(p); + + if (entry.isDirectory()) { + const entries = await this.readdirPromise(p); + + if (stableSort) { + for (const entry of entries.sort()) { + stack.push(this.pathUtils.join(p, entry)); + } + } else { + throw new Error(`Not supported`); + } + } else { + yield p; + } + } + } + + async removePromise(p, { + recursive = true, + maxRetries = 5 + } = {}) { + let stat; + + try { + stat = await this.lstatPromise(p); + } catch (error) { + if (error.code === `ENOENT`) { + return; + } else { + throw error; + } + } + + if (stat.isDirectory()) { + if (recursive) { + const entries = await this.readdirPromise(p); + await Promise.all(entries.map(entry => { + return this.removePromise(this.pathUtils.resolve(p, entry)); + })); + } // 5 gives 1s worth of retries at worst + + + let t = 0; + + do { + try { + await this.rmdirPromise(p); + break; + } catch (error) { + if (error.code === `EBUSY` || error.code === `ENOTEMPTY`) { + if (maxRetries === 0) { + break; + } else { + await new Promise(resolve => setTimeout(resolve, t * 100)); + continue; + } + } else { + throw error; + } + } + } while (t++ < maxRetries); + } else { + await this.unlinkPromise(p); + } + } + + removeSync(p, { + recursive = true + } = {}) { + let stat; + + try { + stat = this.lstatSync(p); + } catch (error) { + if (error.code === `ENOENT`) { + return; + } else { + throw error; + } + } + + if (stat.isDirectory()) { + if (recursive) for (const entry of this.readdirSync(p)) this.removeSync(this.pathUtils.resolve(p, entry)); + this.rmdirSync(p); + } else { + this.unlinkSync(p); + } + } + + async mkdirpPromise(p, { + chmod, + utimes + } = {}) { + p = this.resolve(p); + if (p === this.pathUtils.dirname(p)) return; + const parts = p.split(this.pathUtils.sep); + + for (let u = 2; u <= parts.length; ++u) { + const subPath = parts.slice(0, u).join(this.pathUtils.sep); + + if (!this.existsSync(subPath)) { + try { + await this.mkdirPromise(subPath); + } catch (error) { + if (error.code === `EEXIST`) { + continue; + } else { + throw error; + } + } + + if (chmod != null) await this.chmodPromise(subPath, chmod); + + if (utimes != null) { + await this.utimesPromise(subPath, utimes[0], utimes[1]); + } else { + const parentStat = await this.statPromise(this.pathUtils.dirname(subPath)); + await this.utimesPromise(subPath, parentStat.atime, parentStat.mtime); + } + } + } + } + + mkdirpSync(p, { + chmod, + utimes + } = {}) { + p = this.resolve(p); + if (p === this.pathUtils.dirname(p)) return; + const parts = p.split(this.pathUtils.sep); + + for (let u = 2; u <= parts.length; ++u) { + const subPath = parts.slice(0, u).join(this.pathUtils.sep); + + if (!this.existsSync(subPath)) { + try { + this.mkdirSync(subPath); + } catch (error) { + if (error.code === `EEXIST`) { + continue; + } else { + throw error; + } + } + + if (chmod != null) this.chmodSync(subPath, chmod); + + if (utimes != null) { + this.utimesSync(subPath, utimes[0], utimes[1]); + } else { + const parentStat = this.statSync(this.pathUtils.dirname(subPath)); + this.utimesSync(subPath, parentStat.atime, parentStat.mtime); + } + } + } + } + + async copyPromise(destination, source, { + baseFs = this, + overwrite = true, + stableSort = false, + stableTime = false, + linkStrategy = null + } = {}) { + return await copyPromise(this, destination, baseFs, source, { + overwrite, + stableSort, + stableTime, + linkStrategy + }); + } + + copySync(destination, source, { + baseFs = this, + overwrite = true + } = {}) { + const stat = baseFs.lstatSync(source); + const exists = this.existsSync(destination); + + if (stat.isDirectory()) { + this.mkdirpSync(destination); + const directoryListing = baseFs.readdirSync(source); + + for (const entry of directoryListing) { + this.copySync(this.pathUtils.join(destination, entry), baseFs.pathUtils.join(source, entry), { + baseFs, + overwrite + }); + } + } else if (stat.isFile()) { + if (!exists || overwrite) { + if (exists) this.removeSync(destination); + const content = baseFs.readFileSync(source); + this.writeFileSync(destination, content); + } + } else if (stat.isSymbolicLink()) { + if (!exists || overwrite) { + if (exists) this.removeSync(destination); + const target = baseFs.readlinkSync(source); + this.symlinkSync(convertPath(this.pathUtils, target), destination); + } + } else { + throw new Error(`Unsupported file type (file: ${source}, mode: 0o${stat.mode.toString(8).padStart(6, `0`)})`); + } + + const mode = stat.mode & 0o777; + this.chmodSync(destination, mode); + } + + async changeFilePromise(p, content, opts = {}) { + if (Buffer.isBuffer(content)) { + return this.changeFileBufferPromise(p, content, opts); + } else { + return this.changeFileTextPromise(p, content, opts); + } + } + + async changeFileBufferPromise(p, content, { + mode + } = {}) { + let current = Buffer.alloc(0); + + try { + current = await this.readFilePromise(p); + } catch (error) {// ignore errors, no big deal + } + + if (Buffer.compare(current, content) === 0) return; + await this.writeFilePromise(p, content, { + mode + }); + } + + async changeFileTextPromise(p, content, { + automaticNewlines, + mode + } = {}) { + let current = ``; + + try { + current = await this.readFilePromise(p, `utf8`); + } catch (error) {// ignore errors, no big deal + } + + const normalizedContent = automaticNewlines ? normalizeLineEndings(current, content) : content; + if (current === normalizedContent) return; + await this.writeFilePromise(p, normalizedContent, { + mode + }); + } + + changeFileSync(p, content, opts = {}) { + if (Buffer.isBuffer(content)) { + return this.changeFileBufferSync(p, content, opts); + } else { + return this.changeFileTextSync(p, content, opts); + } + } + + changeFileBufferSync(p, content, { + mode + } = {}) { + let current = Buffer.alloc(0); + + try { + current = this.readFileSync(p); + } catch (error) {// ignore errors, no big deal + } + + if (Buffer.compare(current, content) === 0) return; + this.writeFileSync(p, content, { + mode + }); + } + + changeFileTextSync(p, content, { + automaticNewlines = false, + mode + } = {}) { + let current = ``; + + try { + current = this.readFileSync(p, `utf8`); + } catch (error) {// ignore errors, no big deal + } + + const normalizedContent = automaticNewlines ? normalizeLineEndings(current, content) : content; + if (current === normalizedContent) return; + this.writeFileSync(p, normalizedContent, { + mode + }); + } + + async movePromise(fromP, toP) { + try { + await this.renamePromise(fromP, toP); + } catch (error) { + if (error.code === `EXDEV`) { + await this.copyPromise(toP, fromP); + await this.removePromise(fromP); + } else { + throw error; + } + } + } + + moveSync(fromP, toP) { + try { + this.renameSync(fromP, toP); + } catch (error) { + if (error.code === `EXDEV`) { + this.copySync(toP, fromP); + this.removeSync(fromP); + } else { + throw error; + } + } + } + + async lockPromise(affectedPath, callback) { + const lockPath = `${affectedPath}.flock`; + const interval = 1000 / 60; + const startTime = Date.now(); + let fd = null; // Even when we detect that a lock file exists, we still look inside to see + // whether the pid that created it is still alive. It's not foolproof + // (there are false positive), but there are no false negative and that's + // all that matters in 99% of the cases. + + const isAlive = async () => { + let pid; + + try { + [pid] = await this.readJsonPromise(lockPath); + } catch (error) { + // If we can't read the file repeatedly, we assume the process was + // aborted before even writing finishing writing the payload. + return Date.now() - startTime < 500; + } + + try { + // "As a special case, a signal of 0 can be used to test for the + // existence of a process" - so we check whether it's alive. + process.kill(pid, 0); + return true; + } catch (error) { + return false; + } + }; + + while (fd === null) { + try { + fd = await this.openPromise(lockPath, `wx`); + } catch (error) { + if (error.code === `EEXIST`) { + if (!(await isAlive())) { + try { + await this.unlinkPromise(lockPath); + continue; + } catch (error) {// No big deal if we can't remove it. Just fallback to wait for + // it to be eventually released by its owner. + } + } + + if (Date.now() - startTime < 60 * 1000) { + await new Promise(resolve => setTimeout(resolve, interval)); + } else { + throw new Error(`Couldn't acquire a lock in a reasonable time (via ${lockPath})`); + } + } else { + throw error; + } + } + } + + await this.writePromise(fd, JSON.stringify([process.pid])); + + try { + return await callback(); + } finally { + try { + // closePromise needs to come before unlinkPromise otherwise another process can attempt + // to get the file handle after the unlink but before close resuling in + // EPERM: operation not permitted, open + await this.closePromise(fd); + await this.unlinkPromise(lockPath); + } catch (error) {// noop + } + } + } + + async readJsonPromise(p) { + const content = await this.readFilePromise(p, `utf8`); + + try { + return JSON.parse(content); + } catch (error) { + error.message += ` (in ${p})`; + throw error; + } + } + + readJsonSync(p) { + const content = this.readFileSync(p, `utf8`); + + try { + return JSON.parse(content); + } catch (error) { + error.message += ` (in ${p})`; + throw error; + } + } + + async writeJsonPromise(p, data) { + return await this.writeFilePromise(p, `${JSON.stringify(data, null, 2)}\n`); + } + + writeJsonSync(p, data) { + return this.writeFileSync(p, `${JSON.stringify(data, null, 2)}\n`); + } + + async preserveTimePromise(p, cb) { + const stat = await this.lstatPromise(p); + const result = await cb(); + if (typeof result !== `undefined`) p = result; + + if (this.lutimesPromise) { + await this.lutimesPromise(p, stat.atime, stat.mtime); + } else if (!stat.isSymbolicLink()) { + await this.utimesPromise(p, stat.atime, stat.mtime); + } + } + + async preserveTimeSync(p, cb) { + const stat = this.lstatSync(p); + const result = cb(); + if (typeof result !== `undefined`) p = result; + + if (this.lutimesSync) { + this.lutimesSync(p, stat.atime, stat.mtime); + } else if (!stat.isSymbolicLink()) { + this.utimesSync(p, stat.atime, stat.mtime); + } + } + +} +class BasePortableFakeFS extends FakeFS { + constructor() { + super(ppath); + } + +} + +function getEndOfLine(content) { + const matches = content.match(/\r?\n/g); + if (matches === null) return external_os_namespaceObject.EOL; + const crlf = matches.filter(nl => nl === `\r\n`).length; + const lf = matches.length - crlf; + return crlf > lf ? `\r\n` : `\n`; +} + +function normalizeLineEndings(originalContent, newContent) { + return newContent.replace(/\r?\n/g, getEndOfLine(originalContent)); +} +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/errors.ts +function makeError(code, message) { + return Object.assign(new Error(`${code}: ${message}`), { + code + }); +} + +function EBUSY(message) { + return makeError(`EBUSY`, message); +} +function ENOSYS(message, reason) { + return makeError(`ENOSYS`, `${message}, ${reason}`); +} +function EINVAL(reason) { + return makeError(`EINVAL`, `invalid argument, ${reason}`); +} +function EBADF(reason) { + return makeError(`EBADF`, `bad file descriptor, ${reason}`); +} +function ENOENT(reason) { + return makeError(`ENOENT`, `no such file or directory, ${reason}`); +} +function ENOTDIR(reason) { + return makeError(`ENOTDIR`, `not a directory, ${reason}`); +} +function EISDIR(reason) { + return makeError(`EISDIR`, `illegal operation on a directory, ${reason}`); +} +function EEXIST(reason) { + return makeError(`EEXIST`, `file already exists, ${reason}`); +} +function EROFS(reason) { + return makeError(`EROFS`, `read-only filesystem, ${reason}`); +} +function ENOTEMPTY(reason) { + return makeError(`ENOTEMPTY`, `directory not empty, ${reason}`); +} +function EOPNOTSUPP(reason) { + return makeError(`EOPNOTSUPP`, `operation not supported, ${reason}`); +} // ------------------------------------------------------------------------ + +function ERR_DIR_CLOSED() { + return makeError(`ERR_DIR_CLOSED`, `Directory handle was closed`); +} // ------------------------------------------------------------------------ + +class LibzipError extends Error { + constructor(message, code) { + super(message); + this.name = `Libzip Error`; + this.code = code; + } + +} +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/NodeFS.ts + + + + +class NodeFS extends BasePortableFakeFS { + constructor(realFs = (external_fs_default())) { + super(); + this.realFs = realFs; // @ts-expect-error + + if (typeof this.realFs.lutimes !== `undefined`) { + this.lutimesPromise = this.lutimesPromiseImpl; + this.lutimesSync = this.lutimesSyncImpl; + } + } + + getExtractHint() { + return false; + } + + getRealPath() { + return PortablePath.root; + } + + resolve(p) { + return ppath.resolve(p); + } + + async openPromise(p, flags, mode) { + return await new Promise((resolve, reject) => { + this.realFs.open(npath.fromPortablePath(p), flags, mode, this.makeCallback(resolve, reject)); + }); + } + + openSync(p, flags, mode) { + return this.realFs.openSync(npath.fromPortablePath(p), flags, mode); + } + + async opendirPromise(p, opts) { + return await new Promise((resolve, reject) => { + if (typeof opts !== `undefined`) { + this.realFs.opendir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); + } else { + this.realFs.opendir(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); + } + }).then(dir => { + return Object.defineProperty(dir, `path`, { + value: p, + configurable: true, + writable: true + }); + }); + } + + opendirSync(p, opts) { + const dir = typeof opts !== `undefined` ? this.realFs.opendirSync(npath.fromPortablePath(p), opts) : this.realFs.opendirSync(npath.fromPortablePath(p)); + return Object.defineProperty(dir, `path`, { + value: p, + configurable: true, + writable: true + }); + } + + async readPromise(fd, buffer, offset = 0, length = 0, position = -1) { + return await new Promise((resolve, reject) => { + this.realFs.read(fd, buffer, offset, length, position, (error, bytesRead) => { + if (error) { + reject(error); + } else { + resolve(bytesRead); + } + }); + }); + } + + readSync(fd, buffer, offset, length, position) { + return this.realFs.readSync(fd, buffer, offset, length, position); + } + + async writePromise(fd, buffer, offset, length, position) { + return await new Promise((resolve, reject) => { + if (typeof buffer === `string`) { + return this.realFs.write(fd, buffer, offset, this.makeCallback(resolve, reject)); + } else { + return this.realFs.write(fd, buffer, offset, length, position, this.makeCallback(resolve, reject)); + } + }); + } + + writeSync(fd, buffer, offset, length, position) { + if (typeof buffer === `string`) { + return this.realFs.writeSync(fd, buffer, offset); + } else { + return this.realFs.writeSync(fd, buffer, offset, length, position); + } + } + + async closePromise(fd) { + await new Promise((resolve, reject) => { + this.realFs.close(fd, this.makeCallback(resolve, reject)); + }); + } + + closeSync(fd) { + this.realFs.closeSync(fd); + } + + createReadStream(p, opts) { + const realPath = p !== null ? npath.fromPortablePath(p) : p; + return this.realFs.createReadStream(realPath, opts); + } + + createWriteStream(p, opts) { + const realPath = p !== null ? npath.fromPortablePath(p) : p; + return this.realFs.createWriteStream(realPath, opts); + } + + async realpathPromise(p) { + return await new Promise((resolve, reject) => { + this.realFs.realpath(npath.fromPortablePath(p), {}, this.makeCallback(resolve, reject)); + }).then(path => { + return npath.toPortablePath(path); + }); + } + + realpathSync(p) { + return npath.toPortablePath(this.realFs.realpathSync(npath.fromPortablePath(p), {})); + } + + async existsPromise(p) { + return await new Promise(resolve => { + this.realFs.exists(npath.fromPortablePath(p), resolve); + }); + } + + accessSync(p, mode) { + return this.realFs.accessSync(npath.fromPortablePath(p), mode); + } + + async accessPromise(p, mode) { + return await new Promise((resolve, reject) => { + this.realFs.access(npath.fromPortablePath(p), mode, this.makeCallback(resolve, reject)); + }); + } + + existsSync(p) { + return this.realFs.existsSync(npath.fromPortablePath(p)); + } + + async statPromise(p, opts) { + return await new Promise((resolve, reject) => { + if (opts) { + this.realFs.stat(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); + } else { + this.realFs.stat(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); + } + }); + } + + statSync(p, opts) { + if (opts) { + return this.realFs.statSync(npath.fromPortablePath(p), opts); + } else { + return this.realFs.statSync(npath.fromPortablePath(p)); + } + } + + async fstatPromise(fd, opts) { + return await new Promise((resolve, reject) => { + if (opts) { + // @ts-expect-error - The node typings doesn't know about the options + this.realFs.fstat(fd, opts, this.makeCallback(resolve, reject)); + } else { + this.realFs.fstat(fd, this.makeCallback(resolve, reject)); + } + }); + } + + fstatSync(fd, opts) { + if (opts) { + // @ts-expect-error - The node typings doesn't know about the options + return this.realFs.fstatSync(fd, opts); + } else { + return this.realFs.fstatSync(fd); + } + } + + async lstatPromise(p, opts) { + return await new Promise((resolve, reject) => { + if (opts) { + // @ts-expect-error - TS does not know this takes options + this.realFs.lstat(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); + } else { + this.realFs.lstat(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); + } + }); + } + + lstatSync(p, opts) { + if (opts) { + // @ts-expect-error - TS does not know this takes options + return this.realFs.lstatSync(npath.fromPortablePath(p), opts); + } else { + return this.realFs.lstatSync(npath.fromPortablePath(p)); + } + } + + async chmodPromise(p, mask) { + return await new Promise((resolve, reject) => { + this.realFs.chmod(npath.fromPortablePath(p), mask, this.makeCallback(resolve, reject)); + }); + } + + chmodSync(p, mask) { + return this.realFs.chmodSync(npath.fromPortablePath(p), mask); + } + + async chownPromise(p, uid, gid) { + return await new Promise((resolve, reject) => { + this.realFs.chown(npath.fromPortablePath(p), uid, gid, this.makeCallback(resolve, reject)); + }); + } + + chownSync(p, uid, gid) { + return this.realFs.chownSync(npath.fromPortablePath(p), uid, gid); + } + + async renamePromise(oldP, newP) { + return await new Promise((resolve, reject) => { + this.realFs.rename(npath.fromPortablePath(oldP), npath.fromPortablePath(newP), this.makeCallback(resolve, reject)); + }); + } + + renameSync(oldP, newP) { + return this.realFs.renameSync(npath.fromPortablePath(oldP), npath.fromPortablePath(newP)); + } + + async copyFilePromise(sourceP, destP, flags = 0) { + return await new Promise((resolve, reject) => { + this.realFs.copyFile(npath.fromPortablePath(sourceP), npath.fromPortablePath(destP), flags, this.makeCallback(resolve, reject)); + }); + } + + copyFileSync(sourceP, destP, flags = 0) { + return this.realFs.copyFileSync(npath.fromPortablePath(sourceP), npath.fromPortablePath(destP), flags); + } + + async appendFilePromise(p, content, opts) { + return await new Promise((resolve, reject) => { + const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; + + if (opts) { + this.realFs.appendFile(fsNativePath, content, opts, this.makeCallback(resolve, reject)); + } else { + this.realFs.appendFile(fsNativePath, content, this.makeCallback(resolve, reject)); + } + }); + } + + appendFileSync(p, content, opts) { + const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; + + if (opts) { + this.realFs.appendFileSync(fsNativePath, content, opts); + } else { + this.realFs.appendFileSync(fsNativePath, content); + } + } + + async writeFilePromise(p, content, opts) { + return await new Promise((resolve, reject) => { + const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; + + if (opts) { + this.realFs.writeFile(fsNativePath, content, opts, this.makeCallback(resolve, reject)); + } else { + this.realFs.writeFile(fsNativePath, content, this.makeCallback(resolve, reject)); + } + }); + } + + writeFileSync(p, content, opts) { + const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; + + if (opts) { + this.realFs.writeFileSync(fsNativePath, content, opts); + } else { + this.realFs.writeFileSync(fsNativePath, content); + } + } + + async unlinkPromise(p) { + return await new Promise((resolve, reject) => { + this.realFs.unlink(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); + }); + } + + unlinkSync(p) { + return this.realFs.unlinkSync(npath.fromPortablePath(p)); + } + + async utimesPromise(p, atime, mtime) { + return await new Promise((resolve, reject) => { + this.realFs.utimes(npath.fromPortablePath(p), atime, mtime, this.makeCallback(resolve, reject)); + }); + } + + utimesSync(p, atime, mtime) { + this.realFs.utimesSync(npath.fromPortablePath(p), atime, mtime); + } + + async lutimesPromiseImpl(p, atime, mtime) { + // @ts-expect-error: Not yet in DefinitelyTyped + const lutimes = this.realFs.lutimes; + if (typeof lutimes === `undefined`) throw ENOSYS(`unavailable Node binding`, `lutimes '${p}'`); + return await new Promise((resolve, reject) => { + lutimes.call(this.realFs, npath.fromPortablePath(p), atime, mtime, this.makeCallback(resolve, reject)); + }); + } + + lutimesSyncImpl(p, atime, mtime) { + // @ts-expect-error: Not yet in DefinitelyTyped + const lutimesSync = this.realFs.lutimesSync; + if (typeof lutimesSync === `undefined`) throw ENOSYS(`unavailable Node binding`, `lutimes '${p}'`); + lutimesSync.call(this.realFs, npath.fromPortablePath(p), atime, mtime); + } + + async mkdirPromise(p, opts) { + return await new Promise((resolve, reject) => { + this.realFs.mkdir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); + }); + } + + mkdirSync(p, opts) { + return this.realFs.mkdirSync(npath.fromPortablePath(p), opts); + } + + async rmdirPromise(p, opts) { + return await new Promise((resolve, reject) => { + // TODO: always pass opts when min node version is 12.10+ + if (opts) { + this.realFs.rmdir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); + } else { + this.realFs.rmdir(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); + } + }); + } + + rmdirSync(p, opts) { + return this.realFs.rmdirSync(npath.fromPortablePath(p), opts); + } + + async linkPromise(existingP, newP) { + return await new Promise((resolve, reject) => { + this.realFs.link(npath.fromPortablePath(existingP), npath.fromPortablePath(newP), this.makeCallback(resolve, reject)); + }); + } + + linkSync(existingP, newP) { + return this.realFs.linkSync(npath.fromPortablePath(existingP), npath.fromPortablePath(newP)); + } + + async symlinkPromise(target, p, type) { + return await new Promise((resolve, reject) => { + this.realFs.symlink(npath.fromPortablePath(target.replace(/\/+$/, ``)), npath.fromPortablePath(p), type, this.makeCallback(resolve, reject)); + }); + } + + symlinkSync(target, p, type) { + return this.realFs.symlinkSync(npath.fromPortablePath(target.replace(/\/+$/, ``)), npath.fromPortablePath(p), type); + } + + async readFilePromise(p, encoding) { + return await new Promise((resolve, reject) => { + const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; + this.realFs.readFile(fsNativePath, encoding, this.makeCallback(resolve, reject)); + }); + } + + readFileSync(p, encoding) { + const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; + return this.realFs.readFileSync(fsNativePath, encoding); + } + + async readdirPromise(p, opts) { + return await new Promise((resolve, reject) => { + if (opts === null || opts === void 0 ? void 0 : opts.withFileTypes) { + this.realFs.readdir(npath.fromPortablePath(p), { + withFileTypes: true + }, this.makeCallback(resolve, reject)); + } else { + this.realFs.readdir(npath.fromPortablePath(p), this.makeCallback(value => resolve(value), reject)); + } + }); + } + + readdirSync(p, opts) { + if (opts === null || opts === void 0 ? void 0 : opts.withFileTypes) { + return this.realFs.readdirSync(npath.fromPortablePath(p), { + withFileTypes: true + }); + } else { + return this.realFs.readdirSync(npath.fromPortablePath(p)); + } + } + + async readlinkPromise(p) { + return await new Promise((resolve, reject) => { + this.realFs.readlink(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); + }).then(path => { + return npath.toPortablePath(path); + }); + } + + readlinkSync(p) { + return npath.toPortablePath(this.realFs.readlinkSync(npath.fromPortablePath(p))); + } + + async truncatePromise(p, len) { + return await new Promise((resolve, reject) => { + this.realFs.truncate(npath.fromPortablePath(p), len, this.makeCallback(resolve, reject)); + }); + } + + truncateSync(p, len) { + return this.realFs.truncateSync(npath.fromPortablePath(p), len); + } + + watch(p, a, b) { + return this.realFs.watch(npath.fromPortablePath(p), // @ts-expect-error + a, b); + } + + watchFile(p, a, b) { + return this.realFs.watchFile(npath.fromPortablePath(p), // @ts-expect-error + a, b); + } + + unwatchFile(p, cb) { + return this.realFs.unwatchFile(npath.fromPortablePath(p), cb); + } + + makeCallback(resolve, reject) { + return (err, result) => { + if (err) { + reject(err); + } else { + resolve(result); + } + }; + } + +} +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/ProxiedFS.ts + +class ProxiedFS extends FakeFS { + getExtractHint(hints) { + return this.baseFs.getExtractHint(hints); + } + + resolve(path) { + return this.mapFromBase(this.baseFs.resolve(this.mapToBase(path))); + } + + getRealPath() { + return this.mapFromBase(this.baseFs.getRealPath()); + } + + async openPromise(p, flags, mode) { + return this.baseFs.openPromise(this.mapToBase(p), flags, mode); + } + + openSync(p, flags, mode) { + return this.baseFs.openSync(this.mapToBase(p), flags, mode); + } + + async opendirPromise(p, opts) { + return Object.assign(await this.baseFs.opendirPromise(this.mapToBase(p), opts), { + path: p + }); + } + + opendirSync(p, opts) { + return Object.assign(this.baseFs.opendirSync(this.mapToBase(p), opts), { + path: p + }); + } + + async readPromise(fd, buffer, offset, length, position) { + return await this.baseFs.readPromise(fd, buffer, offset, length, position); + } + + readSync(fd, buffer, offset, length, position) { + return this.baseFs.readSync(fd, buffer, offset, length, position); + } + + async writePromise(fd, buffer, offset, length, position) { + if (typeof buffer === `string`) { + return await this.baseFs.writePromise(fd, buffer, offset); + } else { + return await this.baseFs.writePromise(fd, buffer, offset, length, position); + } + } + + writeSync(fd, buffer, offset, length, position) { + if (typeof buffer === `string`) { + return this.baseFs.writeSync(fd, buffer, offset); + } else { + return this.baseFs.writeSync(fd, buffer, offset, length, position); + } + } + + async closePromise(fd) { + return this.baseFs.closePromise(fd); + } + + closeSync(fd) { + this.baseFs.closeSync(fd); + } + + createReadStream(p, opts) { + return this.baseFs.createReadStream(p !== null ? this.mapToBase(p) : p, opts); + } + + createWriteStream(p, opts) { + return this.baseFs.createWriteStream(p !== null ? this.mapToBase(p) : p, opts); + } + + async realpathPromise(p) { + return this.mapFromBase(await this.baseFs.realpathPromise(this.mapToBase(p))); + } + + realpathSync(p) { + return this.mapFromBase(this.baseFs.realpathSync(this.mapToBase(p))); + } + + async existsPromise(p) { + return this.baseFs.existsPromise(this.mapToBase(p)); + } + + existsSync(p) { + return this.baseFs.existsSync(this.mapToBase(p)); + } + + accessSync(p, mode) { + return this.baseFs.accessSync(this.mapToBase(p), mode); + } + + async accessPromise(p, mode) { + return this.baseFs.accessPromise(this.mapToBase(p), mode); + } + + async statPromise(p, opts) { + return this.baseFs.statPromise(this.mapToBase(p), opts); + } + + statSync(p, opts) { + return this.baseFs.statSync(this.mapToBase(p), opts); + } + + async fstatPromise(fd, opts) { + return this.baseFs.fstatPromise(fd, opts); + } + + fstatSync(fd, opts) { + return this.baseFs.fstatSync(fd, opts); + } + + async lstatPromise(p, opts) { + return this.baseFs.lstatPromise(this.mapToBase(p), opts); + } + + lstatSync(p, opts) { + return this.baseFs.lstatSync(this.mapToBase(p), opts); + } + + async chmodPromise(p, mask) { + return this.baseFs.chmodPromise(this.mapToBase(p), mask); + } + + chmodSync(p, mask) { + return this.baseFs.chmodSync(this.mapToBase(p), mask); + } + + async chownPromise(p, uid, gid) { + return this.baseFs.chownPromise(this.mapToBase(p), uid, gid); + } + + chownSync(p, uid, gid) { + return this.baseFs.chownSync(this.mapToBase(p), uid, gid); + } + + async renamePromise(oldP, newP) { + return this.baseFs.renamePromise(this.mapToBase(oldP), this.mapToBase(newP)); + } + + renameSync(oldP, newP) { + return this.baseFs.renameSync(this.mapToBase(oldP), this.mapToBase(newP)); + } + + async copyFilePromise(sourceP, destP, flags = 0) { + return this.baseFs.copyFilePromise(this.mapToBase(sourceP), this.mapToBase(destP), flags); + } + + copyFileSync(sourceP, destP, flags = 0) { + return this.baseFs.copyFileSync(this.mapToBase(sourceP), this.mapToBase(destP), flags); + } + + async appendFilePromise(p, content, opts) { + return this.baseFs.appendFilePromise(this.fsMapToBase(p), content, opts); + } + + appendFileSync(p, content, opts) { + return this.baseFs.appendFileSync(this.fsMapToBase(p), content, opts); + } + + async writeFilePromise(p, content, opts) { + return this.baseFs.writeFilePromise(this.fsMapToBase(p), content, opts); + } + + writeFileSync(p, content, opts) { + return this.baseFs.writeFileSync(this.fsMapToBase(p), content, opts); + } + + async unlinkPromise(p) { + return this.baseFs.unlinkPromise(this.mapToBase(p)); + } + + unlinkSync(p) { + return this.baseFs.unlinkSync(this.mapToBase(p)); + } + + async utimesPromise(p, atime, mtime) { + return this.baseFs.utimesPromise(this.mapToBase(p), atime, mtime); + } + + utimesSync(p, atime, mtime) { + return this.baseFs.utimesSync(this.mapToBase(p), atime, mtime); + } + + async mkdirPromise(p, opts) { + return this.baseFs.mkdirPromise(this.mapToBase(p), opts); + } + + mkdirSync(p, opts) { + return this.baseFs.mkdirSync(this.mapToBase(p), opts); + } + + async rmdirPromise(p, opts) { + return this.baseFs.rmdirPromise(this.mapToBase(p), opts); + } + + rmdirSync(p, opts) { + return this.baseFs.rmdirSync(this.mapToBase(p), opts); + } + + async linkPromise(existingP, newP) { + return this.baseFs.linkPromise(this.mapToBase(existingP), this.mapToBase(newP)); + } + + linkSync(existingP, newP) { + return this.baseFs.linkSync(this.mapToBase(existingP), this.mapToBase(newP)); + } + + async symlinkPromise(target, p, type) { + const mappedP = this.mapToBase(p); + if (this.pathUtils.isAbsolute(target)) return this.baseFs.symlinkPromise(this.mapToBase(target), mappedP, type); + const mappedAbsoluteTarget = this.mapToBase(this.pathUtils.join(this.pathUtils.dirname(p), target)); + const mappedTarget = this.baseFs.pathUtils.relative(this.baseFs.pathUtils.dirname(mappedP), mappedAbsoluteTarget); + return this.baseFs.symlinkPromise(mappedTarget, mappedP, type); + } + + symlinkSync(target, p, type) { + const mappedP = this.mapToBase(p); + if (this.pathUtils.isAbsolute(target)) return this.baseFs.symlinkSync(this.mapToBase(target), mappedP, type); + const mappedAbsoluteTarget = this.mapToBase(this.pathUtils.join(this.pathUtils.dirname(p), target)); + const mappedTarget = this.baseFs.pathUtils.relative(this.baseFs.pathUtils.dirname(mappedP), mappedAbsoluteTarget); + return this.baseFs.symlinkSync(mappedTarget, mappedP, type); + } + + async readFilePromise(p, encoding) { + // This weird condition is required to tell TypeScript that the signatures are proper (otherwise it thinks that only the generic one is covered) + if (encoding === `utf8`) { + return this.baseFs.readFilePromise(this.fsMapToBase(p), encoding); + } else { + return this.baseFs.readFilePromise(this.fsMapToBase(p), encoding); + } + } + + readFileSync(p, encoding) { + // This weird condition is required to tell TypeScript that the signatures are proper (otherwise it thinks that only the generic one is covered) + if (encoding === `utf8`) { + return this.baseFs.readFileSync(this.fsMapToBase(p), encoding); + } else { + return this.baseFs.readFileSync(this.fsMapToBase(p), encoding); + } + } + + async readdirPromise(p, opts) { + return this.baseFs.readdirPromise(this.mapToBase(p), opts); + } + + readdirSync(p, opts) { + return this.baseFs.readdirSync(this.mapToBase(p), opts); + } + + async readlinkPromise(p) { + return this.mapFromBase(await this.baseFs.readlinkPromise(this.mapToBase(p))); + } + + readlinkSync(p) { + return this.mapFromBase(this.baseFs.readlinkSync(this.mapToBase(p))); + } + + async truncatePromise(p, len) { + return this.baseFs.truncatePromise(this.mapToBase(p), len); + } + + truncateSync(p, len) { + return this.baseFs.truncateSync(this.mapToBase(p), len); + } + + watch(p, a, b) { + return this.baseFs.watch(this.mapToBase(p), // @ts-expect-error + a, b); + } + + watchFile(p, a, b) { + return this.baseFs.watchFile(this.mapToBase(p), // @ts-expect-error + a, b); + } + + unwatchFile(p, cb) { + return this.baseFs.unwatchFile(this.mapToBase(p), cb); + } + + fsMapToBase(p) { + if (typeof p === `number`) { + return p; + } else { + return this.mapToBase(p); + } + } + +} +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/VirtualFS.ts + + + +const NUMBER_REGEXP = /^[0-9]+$/; // $0: full path +// $1: virtual folder +// $2: virtual segment +// $3: hash +// $4: depth +// $5: subpath + +const VIRTUAL_REGEXP = /^(\/(?:[^/]+\/)*?(?:\$\$virtual|__virtual__))((?:\/((?:[^/]+-)?[a-f0-9]+)(?:\/([^/]+))?)?((?:\/.*)?))$/; +const VALID_COMPONENT = /^([^/]+-)?[a-f0-9]+$/; +class VirtualFS extends ProxiedFS { + constructor({ + baseFs = new NodeFS() + } = {}) { + super(ppath); + this.baseFs = baseFs; + } + + static makeVirtualPath(base, component, to) { + if (ppath.basename(base) !== `__virtual__`) throw new Error(`Assertion failed: Virtual folders must be named "__virtual__"`); + if (!ppath.basename(component).match(VALID_COMPONENT)) throw new Error(`Assertion failed: Virtual components must be ended by an hexadecimal hash`); // Obtains the relative distance between the virtual path and its actual target + + const target = ppath.relative(ppath.dirname(base), to); + const segments = target.split(`/`); // Counts how many levels we need to go back to start applying the rest of the path + + let depth = 0; + + while (depth < segments.length && segments[depth] === `..`) depth += 1; + + const finalSegments = segments.slice(depth); + const fullVirtualPath = ppath.join(base, component, String(depth), ...finalSegments); + return fullVirtualPath; + } + + static resolveVirtual(p) { + const match = p.match(VIRTUAL_REGEXP); + if (!match || !match[3] && match[5]) return p; + const target = ppath.dirname(match[1]); + if (!match[3] || !match[4]) return target; + const isnum = NUMBER_REGEXP.test(match[4]); + if (!isnum) return p; + const depth = Number(match[4]); + const backstep = `../`.repeat(depth); + const subpath = match[5] || `.`; + return VirtualFS.resolveVirtual(ppath.join(target, backstep, subpath)); + } + + getExtractHint(hints) { + return this.baseFs.getExtractHint(hints); + } + + getRealPath() { + return this.baseFs.getRealPath(); + } + + realpathSync(p) { + const match = p.match(VIRTUAL_REGEXP); + if (!match) return this.baseFs.realpathSync(p); + if (!match[5]) return p; + const realpath = this.baseFs.realpathSync(this.mapToBase(p)); + return VirtualFS.makeVirtualPath(match[1], match[3], realpath); + } + + async realpathPromise(p) { + const match = p.match(VIRTUAL_REGEXP); + if (!match) return await this.baseFs.realpathPromise(p); + if (!match[5]) return p; + const realpath = await this.baseFs.realpathPromise(this.mapToBase(p)); + return VirtualFS.makeVirtualPath(match[1], match[3], realpath); + } + + mapToBase(p) { + if (p === ``) return p; + if (this.pathUtils.isAbsolute(p)) return VirtualFS.resolveVirtual(p); + const resolvedRoot = VirtualFS.resolveVirtual(this.baseFs.resolve(PortablePath.dot)); + const resolvedP = VirtualFS.resolveVirtual(this.baseFs.resolve(p)); + return ppath.relative(resolvedRoot, resolvedP) || PortablePath.dot; + } + + mapFromBase(p) { + return p; + } + +} +;// CONCATENATED MODULE: external "stream" +const external_stream_namespaceObject = require("stream");; +;// CONCATENATED MODULE: external "util" +const external_util_namespaceObject = require("util");; +;// CONCATENATED MODULE: external "zlib" +const external_zlib_namespaceObject = require("zlib");; +var external_zlib_default = /*#__PURE__*/__webpack_require__.n(external_zlib_namespaceObject); +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/algorithms/opendir.ts + +class CustomDir { + constructor(path, nextDirent, opts = {}) { + this.path = path; + this.nextDirent = nextDirent; + this.opts = opts; + this.closed = false; + } + + throwIfClosed() { + if (this.closed) { + throw ERR_DIR_CLOSED(); + } + } + + async *[Symbol.asyncIterator]() { + try { + let dirent; // eslint-disable-next-line no-cond-assign + + while ((dirent = await this.read()) !== null) { + yield dirent; + } + } finally { + await this.close(); + } + } + + read(cb) { + const dirent = this.readSync(); + if (typeof cb !== `undefined`) return cb(null, dirent); + return Promise.resolve(dirent); + } + + readSync() { + this.throwIfClosed(); + return this.nextDirent(); + } + + close(cb) { + this.closeSync(); + if (typeof cb !== `undefined`) return cb(null); + return Promise.resolve(); + } + + closeSync() { + var _a, _b; + + this.throwIfClosed(); + (_b = (_a = this.opts).onClose) === null || _b === void 0 ? void 0 : _b.call(_a); + this.closed = true; + } + +} +function opendir(fakeFs, path, entries, opts) { + const nextDirent = () => { + const filename = entries.shift(); + if (typeof filename === `undefined`) return null; + return Object.assign(fakeFs.statSync(fakeFs.pathUtils.join(path, filename)), { + name: filename + }); + }; + + return new CustomDir(path, nextDirent, opts); +} +;// CONCATENATED MODULE: external "events" +const external_events_namespaceObject = require("events");; +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/statUtils.ts + + +const DEFAULT_MODE = constants_S_IFREG | 0o644; +class DirEntry { + constructor() { + this.name = ``; + this.mode = 0; + } + + isBlockDevice() { + return false; + } + + isCharacterDevice() { + return false; + } + + isDirectory() { + return (this.mode & S_IFMT) === S_IFDIR; + } + + isFIFO() { + return false; + } + + isFile() { + return (this.mode & S_IFMT) === S_IFREG; + } + + isSocket() { + return false; + } + + isSymbolicLink() { + return (this.mode & S_IFMT) === S_IFLNK; + } + +} +class StatEntry { + constructor() { + this.uid = 0; + this.gid = 0; + this.size = 0; + this.blksize = 0; + this.atimeMs = 0; + this.mtimeMs = 0; + this.ctimeMs = 0; + this.birthtimeMs = 0; + this.atime = new Date(0); + this.mtime = new Date(0); + this.ctime = new Date(0); + this.birthtime = new Date(0); + this.dev = 0; + this.ino = 0; + this.mode = DEFAULT_MODE; + this.nlink = 1; + this.rdev = 0; + this.blocks = 1; + } + + isBlockDevice() { + return false; + } + + isCharacterDevice() { + return false; + } + + isDirectory() { + return (this.mode & constants_S_IFMT) === constants_S_IFDIR; + } + + isFIFO() { + return false; + } + + isFile() { + return (this.mode & constants_S_IFMT) === constants_S_IFREG; + } + + isSocket() { + return false; + } + + isSymbolicLink() { + return (this.mode & constants_S_IFMT) === constants_S_IFLNK; + } + +} +class BigIntStatsEntry { + constructor() { + this.uid = BigInt(0); + this.gid = BigInt(0); + this.size = BigInt(0); + this.blksize = BigInt(0); + this.atimeMs = BigInt(0); + this.mtimeMs = BigInt(0); + this.ctimeMs = BigInt(0); + this.birthtimeMs = BigInt(0); + this.atimeNs = BigInt(0); + this.mtimeNs = BigInt(0); + this.ctimeNs = BigInt(0); + this.birthtimeNs = BigInt(0); + this.atime = new Date(0); + this.mtime = new Date(0); + this.ctime = new Date(0); + this.birthtime = new Date(0); + this.dev = BigInt(0); + this.ino = BigInt(0); + this.mode = BigInt(DEFAULT_MODE); + this.nlink = BigInt(1); + this.rdev = BigInt(0); + this.blocks = BigInt(1); + } + + isBlockDevice() { + return false; + } + + isCharacterDevice() { + return false; + } + + isDirectory() { + return (this.mode & BigInt(constants_S_IFMT)) === BigInt(constants_S_IFDIR); + } + + isFIFO() { + return false; + } + + isFile() { + return (this.mode & BigInt(constants_S_IFMT)) === BigInt(constants_S_IFREG); + } + + isSocket() { + return false; + } + + isSymbolicLink() { + return (this.mode & BigInt(constants_S_IFMT)) === BigInt(constants_S_IFLNK); + } + +} +function makeDefaultStats() { + return new StatEntry(); +} +function makeEmptyStats() { + return clearStats(makeDefaultStats()); +} +/** + * Mutates the provided stats object to zero it out then returns it for convenience + */ + +function clearStats(stats) { + for (const key in stats) { + if (Object.prototype.hasOwnProperty.call(stats, key)) { + const element = stats[key]; + + if (typeof element === `number`) { + // @ts-expect-error Typescript can't tell that stats[key] is a number + stats[key] = 0; + } else if (typeof element === `bigint`) { + // @ts-expect-error Typescript can't tell that stats[key] is a bigint + stats[key] = BigInt(0); + } else if (external_util_namespaceObject.types.isDate(element)) { + // @ts-expect-error Typescript can't tell that stats[key] is a bigint + stats[key] = new Date(0); + } + } + } + + return stats; +} +function convertToBigIntStats(stats) { + const bigintStats = new BigIntStatsEntry(); + + for (const key in stats) { + if (Object.prototype.hasOwnProperty.call(stats, key)) { + const element = stats[key]; + + if (typeof element === `number`) { + // @ts-expect-error Typescript isn't able to tell this is valid + bigintStats[key] = BigInt(element); + } else if (external_util_namespaceObject.types.isDate(element)) { + // @ts-expect-error Typescript isn't able to tell this is valid + bigintStats[key] = new Date(element); + } + } + } + + bigintStats.atimeNs = bigintStats.atimeMs * BigInt(1e6); + bigintStats.mtimeNs = bigintStats.mtimeMs * BigInt(1e6); + bigintStats.ctimeNs = bigintStats.ctimeMs * BigInt(1e6); + bigintStats.birthtimeNs = bigintStats.birthtimeMs * BigInt(1e6); + return bigintStats; +} +function areStatsEqual(a, b) { + if (a.atimeMs !== b.atimeMs) return false; + if (a.birthtimeMs !== b.birthtimeMs) return false; + if (a.blksize !== b.blksize) return false; + if (a.blocks !== b.blocks) return false; + if (a.ctimeMs !== b.ctimeMs) return false; + if (a.dev !== b.dev) return false; + if (a.gid !== b.gid) return false; + if (a.ino !== b.ino) return false; + if (a.isBlockDevice() !== b.isBlockDevice()) return false; + if (a.isCharacterDevice() !== b.isCharacterDevice()) return false; + if (a.isDirectory() !== b.isDirectory()) return false; + if (a.isFIFO() !== b.isFIFO()) return false; + if (a.isFile() !== b.isFile()) return false; + if (a.isSocket() !== b.isSocket()) return false; + if (a.isSymbolicLink() !== b.isSymbolicLink()) return false; + if (a.mode !== b.mode) return false; + if (a.mtimeMs !== b.mtimeMs) return false; + if (a.nlink !== b.nlink) return false; + if (a.rdev !== b.rdev) return false; + if (a.size !== b.size) return false; + if (a.uid !== b.uid) return false; + const aN = a; + const bN = b; + if (aN.atimeNs !== bN.atimeNs) return false; + if (aN.mtimeNs !== bN.mtimeNs) return false; + if (aN.ctimeNs !== bN.ctimeNs) return false; + if (aN.birthtimeNs !== bN.birthtimeNs) return false; + return true; +} +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/algorithms/watchFile/CustomStatWatcher.ts + + +var Event; + +(function (Event) { + Event["Change"] = "change"; + Event["Stop"] = "stop"; +})(Event || (Event = {})); + +var Status; + +(function (Status) { + Status["Ready"] = "ready"; + Status["Running"] = "running"; + Status["Stopped"] = "stopped"; +})(Status || (Status = {})); + +function assertStatus(current, expected) { + if (current !== expected) { + throw new Error(`Invalid StatWatcher status: expected '${expected}', got '${current}'`); + } +} +class CustomStatWatcher extends external_events_namespaceObject.EventEmitter { + constructor(fakeFs, path, { + bigint = false + } = {}) { + super(); + this.status = Status.Ready; + this.changeListeners = new Map(); + this.startTimeout = null; + this.fakeFs = fakeFs; + this.path = path; + this.bigint = bigint; + this.lastStats = this.stat(); + } + + static create(fakeFs, path, opts) { + const statWatcher = new CustomStatWatcher(fakeFs, path, opts); + statWatcher.start(); + return statWatcher; + } + + start() { + assertStatus(this.status, Status.Ready); + this.status = Status.Running; // Node allows other listeners to be registered up to 3 milliseconds + // after the watcher has been started, so that's what we're doing too + + this.startTimeout = setTimeout(() => { + this.startTimeout = null; // Per the Node FS docs: + // "When an fs.watchFile operation results in an ENOENT error, + // it will invoke the listener once, with all the fields zeroed + // (or, for dates, the Unix Epoch)." + + if (!this.fakeFs.existsSync(this.path)) { + this.emit(Event.Change, this.lastStats, this.lastStats); + } + }, 3); + } + + stop() { + assertStatus(this.status, Status.Running); + this.status = Status.Stopped; + + if (this.startTimeout !== null) { + clearTimeout(this.startTimeout); + this.startTimeout = null; + } + + this.emit(Event.Stop); + } + + stat() { + try { + return this.fakeFs.statSync(this.path, { + bigint: this.bigint + }); + } catch (error) { + // From observation, all errors seem to be mostly ignored by Node. + // Checked with ENOENT, ENOTDIR, EPERM + const statInstance = this.bigint ? new BigIntStatsEntry() : new StatEntry(); + return clearStats(statInstance); + } + } + /** + * Creates an interval whose callback compares the current stats with the previous stats and notifies all listeners in case of changes. + * + * @param opts.persistent Decides whether the interval should be immediately unref-ed. + */ + + + makeInterval(opts) { + const interval = setInterval(() => { + const currentStats = this.stat(); + const previousStats = this.lastStats; + if (areStatsEqual(currentStats, previousStats)) return; + this.lastStats = currentStats; + this.emit(Event.Change, currentStats, previousStats); + }, opts.interval); + return opts.persistent ? interval : interval.unref(); + } + /** + * Registers a listener and assigns it an interval. + */ + + + registerChangeListener(listener, opts) { + this.addListener(Event.Change, listener); + this.changeListeners.set(listener, this.makeInterval(opts)); + } + /** + * Unregisters the listener and clears the assigned interval. + */ + + + unregisterChangeListener(listener) { + this.removeListener(Event.Change, listener); + const interval = this.changeListeners.get(listener); + if (typeof interval !== `undefined`) clearInterval(interval); + this.changeListeners.delete(listener); + } + /** + * Unregisters all listeners and clears all assigned intervals. + */ + + + unregisterAllChangeListeners() { + for (const listener of this.changeListeners.keys()) { + this.unregisterChangeListener(listener); + } + } + + hasChangeListeners() { + return this.changeListeners.size > 0; + } + /** + * Refs all stored intervals. + */ + + + ref() { + for (const interval of this.changeListeners.values()) interval.ref(); + + return this; + } + /** + * Unrefs all stored intervals. + */ + + + unref() { + for (const interval of this.changeListeners.values()) interval.unref(); + + return this; + } + +} +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/algorithms/watchFile.ts + +const statWatchersByFakeFS = new WeakMap(); +function watchFile(fakeFs, path, a, b) { + let bigint; + let persistent; + let interval; + let listener; + + switch (typeof a) { + case `function`: + { + bigint = false; + persistent = true; + interval = 5007; + listener = a; + } + break; + + default: + { + ({ + bigint = false, + persistent = true, + interval = 5007 + } = a); + listener = b; + } + break; + } + + let statWatchers = statWatchersByFakeFS.get(fakeFs); + if (typeof statWatchers === `undefined`) statWatchersByFakeFS.set(fakeFs, statWatchers = new Map()); + let statWatcher = statWatchers.get(path); + + if (typeof statWatcher === `undefined`) { + statWatcher = CustomStatWatcher.create(fakeFs, path, { + bigint + }); + statWatchers.set(path, statWatcher); + } + + statWatcher.registerChangeListener(listener, { + persistent, + interval + }); + return statWatcher; +} +function unwatchFile(fakeFs, path, cb) { + const statWatchers = statWatchersByFakeFS.get(fakeFs); + if (typeof statWatchers === `undefined`) return; + const statWatcher = statWatchers.get(path); + if (typeof statWatcher === `undefined`) return; + if (typeof cb === `undefined`) statWatcher.unregisterAllChangeListeners();else statWatcher.unregisterChangeListener(cb); + + if (!statWatcher.hasChangeListeners()) { + statWatcher.stop(); + statWatchers.delete(path); + } +} +function unwatchAllFiles(fakeFs) { + const statWatchers = statWatchersByFakeFS.get(fakeFs); + if (typeof statWatchers === `undefined`) return; + + for (const path of statWatchers.keys()) { + unwatchFile(fakeFs, path); + } +} +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/ZipFS.ts + + + + + + + + + + + + +const DEFAULT_COMPRESSION_LEVEL = `mixed`; + +function toUnixTimestamp(time) { + if (typeof time === `string` && String(+time) === time) return +time; + + if (Number.isFinite(time)) { + if (time < 0) { + return Date.now() / 1000; + } else { + return time; + } + } // convert to 123.456 UNIX timestamp + + + if ((0,external_util_namespaceObject.isDate)(time)) return time.getTime() / 1000; + throw new Error(`Invalid time`); +} + +function makeEmptyArchive() { + return Buffer.from([0x50, 0x4B, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); +} +class ZipFS extends BasePortableFakeFS { + constructor(source, opts) { + super(); + this.lzSource = null; + this.listings = new Map(); + this.entries = new Map(); + /** + * A cache of indices mapped to file sources. + * Populated by `setFileSource` calls. + * Required for supporting read after write. + */ + + this.fileSources = new Map(); + this.fds = new Map(); + this.nextFd = 0; + this.ready = false; + this.readOnly = false; + this.libzip = opts.libzip; + const pathOptions = opts; + this.level = typeof pathOptions.level !== `undefined` ? pathOptions.level : DEFAULT_COMPRESSION_LEVEL; + source !== null && source !== void 0 ? source : source = makeEmptyArchive(); + + if (typeof source === `string`) { + const { + baseFs = new NodeFS() + } = pathOptions; + this.baseFs = baseFs; + this.path = source; + } else { + this.path = null; + this.baseFs = null; + } + + if (opts.stats) { + this.stats = opts.stats; + } else { + if (typeof source === `string`) { + try { + this.stats = this.baseFs.statSync(source); + } catch (error) { + if (error.code === `ENOENT` && pathOptions.create) { + this.stats = makeDefaultStats(); + } else { + throw error; + } + } + } else { + this.stats = makeDefaultStats(); + } + } + + const errPtr = this.libzip.malloc(4); + + try { + let flags = 0; + if (typeof source === `string` && pathOptions.create) flags |= this.libzip.ZIP_CREATE | this.libzip.ZIP_TRUNCATE; + + if (opts.readOnly) { + flags |= this.libzip.ZIP_RDONLY; + this.readOnly = true; + } + + if (typeof source === `string`) { + this.zip = this.libzip.open(npath.fromPortablePath(source), flags, errPtr); + } else { + const lzSource = this.allocateUnattachedSource(source); + + try { + this.zip = this.libzip.openFromSource(lzSource, flags, errPtr); + this.lzSource = lzSource; + } catch (error) { + this.libzip.source.free(lzSource); + throw error; + } + } + + if (this.zip === 0) { + const error = this.libzip.struct.errorS(); + this.libzip.error.initWithCode(error, this.libzip.getValue(errPtr, `i32`)); + throw this.makeLibzipError(error); + } + } finally { + this.libzip.free(errPtr); + } + + this.listings.set(PortablePath.root, new Set()); + const entryCount = this.libzip.getNumEntries(this.zip, 0); + + for (let t = 0; t < entryCount; ++t) { + const raw = this.libzip.getName(this.zip, t, 0); + if (ppath.isAbsolute(raw)) continue; + const p = ppath.resolve(PortablePath.root, raw); + this.registerEntry(p, t); // If the raw path is a directory, register it + // to prevent empty folder being skipped + + if (raw.endsWith(`/`)) { + this.registerListing(p); + } + } + + this.symlinkCount = this.libzip.ext.countSymlinks(this.zip); + if (this.symlinkCount === -1) throw this.makeLibzipError(this.libzip.getError(this.zip)); + this.ready = true; + } + + makeLibzipError(error) { + const errorCode = this.libzip.struct.errorCodeZip(error); + const strerror = this.libzip.error.strerror(error); + const libzipError = new LibzipError(strerror, this.libzip.errors[errorCode]); // This error should never come up because of the file source cache + + if (errorCode === this.libzip.errors.ZIP_ER_CHANGED) throw new Error(`Assertion failed: Unexpected libzip error: ${libzipError.message}`); + return libzipError; + } + + getExtractHint(hints) { + for (const fileName of this.entries.keys()) { + const ext = this.pathUtils.extname(fileName); + + if (hints.relevantExtensions.has(ext)) { + return true; + } + } + + return false; + } + + getAllFiles() { + return Array.from(this.entries.keys()); + } + + getRealPath() { + if (!this.path) throw new Error(`ZipFS don't have real paths when loaded from a buffer`); + return this.path; + } + + getBufferAndClose() { + this.prepareClose(); + if (!this.lzSource) throw new Error(`ZipFS was not created from a Buffer`); + + try { + // Prevent close from cleaning up the source + this.libzip.source.keep(this.lzSource); // Close the zip archive + + if (this.libzip.close(this.zip) === -1) throw this.makeLibzipError(this.libzip.getError(this.zip)); // Open the source for reading + + if (this.libzip.source.open(this.lzSource) === -1) throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); // Move to the end of source + + if (this.libzip.source.seek(this.lzSource, 0, 0, this.libzip.SEEK_END) === -1) throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); // Get the size of source + + const size = this.libzip.source.tell(this.lzSource); + if (size === -1) throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); // Move to the start of source + + if (this.libzip.source.seek(this.lzSource, 0, 0, this.libzip.SEEK_SET) === -1) throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); + const buffer = this.libzip.malloc(size); + if (!buffer) throw new Error(`Couldn't allocate enough memory`); + + try { + const rc = this.libzip.source.read(this.lzSource, buffer, size); + if (rc === -1) throw this.makeLibzipError(this.libzip.source.error(this.lzSource));else if (rc < size) throw new Error(`Incomplete read`);else if (rc > size) throw new Error(`Overread`); + const memory = this.libzip.HEAPU8.subarray(buffer, buffer + size); + return Buffer.from(memory); + } finally { + this.libzip.free(buffer); + } + } finally { + this.libzip.source.close(this.lzSource); + this.libzip.source.free(this.lzSource); + this.ready = false; + } + } + + prepareClose() { + if (!this.ready) throw EBUSY(`archive closed, close`); + unwatchAllFiles(this); + } + + saveAndClose() { + if (!this.path || !this.baseFs) throw new Error(`ZipFS cannot be saved and must be discarded when loaded from a buffer`); + this.prepareClose(); + + if (this.readOnly) { + this.discardAndClose(); + return; + } + + const newMode = this.baseFs.existsSync(this.path) || this.stats.mode === DEFAULT_MODE ? undefined : this.stats.mode; // zip_close doesn't persist empty archives + + if (this.entries.size === 0) { + this.discardAndClose(); + this.baseFs.writeFileSync(this.path, makeEmptyArchive(), { + mode: newMode + }); + } else { + const rc = this.libzip.close(this.zip); + if (rc === -1) throw this.makeLibzipError(this.libzip.getError(this.zip)); + + if (typeof newMode !== `undefined`) { + this.baseFs.chmodSync(this.path, newMode); + } + } + + this.ready = false; + } + + discardAndClose() { + this.prepareClose(); + this.libzip.discard(this.zip); + this.ready = false; + } + + resolve(p) { + return ppath.resolve(PortablePath.root, p); + } + + async openPromise(p, flags, mode) { + return this.openSync(p, flags, mode); + } + + openSync(p, flags, mode) { + const fd = this.nextFd++; + this.fds.set(fd, { + cursor: 0, + p + }); + return fd; + } + + hasOpenFileHandles() { + return !!this.fds.size; + } + + async opendirPromise(p, opts) { + return this.opendirSync(p, opts); + } + + opendirSync(p, opts = {}) { + const resolvedP = this.resolveFilename(`opendir '${p}'`, p); + if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) throw ENOENT(`opendir '${p}'`); + const directoryListing = this.listings.get(resolvedP); + if (!directoryListing) throw ENOTDIR(`opendir '${p}'`); + const entries = [...directoryListing]; + const fd = this.openSync(resolvedP, `r`); + + const onClose = () => { + this.closeSync(fd); + }; + + return opendir(this, resolvedP, entries, { + onClose + }); + } + + async readPromise(fd, buffer, offset, length, position) { + return this.readSync(fd, buffer, offset, length, position); + } + + readSync(fd, buffer, offset = 0, length = buffer.byteLength, position = -1) { + const entry = this.fds.get(fd); + if (typeof entry === `undefined`) throw EBADF(`read`); + let realPosition; + if (position === -1 || position === null) realPosition = entry.cursor;else realPosition = position; + const source = this.readFileSync(entry.p); + source.copy(buffer, offset, realPosition, realPosition + length); + const bytesRead = Math.max(0, Math.min(source.length - realPosition, length)); + if (position === -1 || position === null) entry.cursor += bytesRead; + return bytesRead; + } + + async writePromise(fd, buffer, offset, length, position) { + if (typeof buffer === `string`) { + return this.writeSync(fd, buffer, position); + } else { + return this.writeSync(fd, buffer, offset, length, position); + } + } + + writeSync(fd, buffer, offset, length, position) { + const entry = this.fds.get(fd); + if (typeof entry === `undefined`) throw EBADF(`read`); + throw new Error(`Unimplemented`); + } + + async closePromise(fd) { + return this.closeSync(fd); + } + + closeSync(fd) { + const entry = this.fds.get(fd); + if (typeof entry === `undefined`) throw EBADF(`read`); + this.fds.delete(fd); + } + + createReadStream(p, { + encoding + } = {}) { + if (p === null) throw new Error(`Unimplemented`); + const fd = this.openSync(p, `r`); + const stream = Object.assign(new external_stream_namespaceObject.PassThrough({ + emitClose: true, + autoDestroy: true, + destroy: (error, callback) => { + clearImmediate(immediate); + this.closeSync(fd); + callback(error); + } + }), { + close() { + stream.destroy(); + }, + + bytesRead: 0, + path: p + }); + const immediate = setImmediate(async () => { + try { + const data = await this.readFilePromise(p, encoding); + stream.bytesRead = data.length; + stream.end(data); + } catch (error) { + stream.destroy(error); + } + }); + return stream; + } + + createWriteStream(p, { + encoding + } = {}) { + if (this.readOnly) throw EROFS(`open '${p}'`); + if (p === null) throw new Error(`Unimplemented`); + const chunks = []; + const fd = this.openSync(p, `w`); + const stream = Object.assign(new external_stream_namespaceObject.PassThrough({ + autoDestroy: true, + emitClose: true, + destroy: (error, callback) => { + try { + if (error) { + callback(error); + } else { + this.writeFileSync(p, Buffer.concat(chunks), encoding); + callback(null); + } + } catch (err) { + callback(err); + } finally { + this.closeSync(fd); + } + } + }), { + bytesWritten: 0, + path: p, + + close() { + stream.destroy(); + } + + }); + stream.on(`data`, chunk => { + const chunkBuffer = Buffer.from(chunk); + stream.bytesWritten += chunkBuffer.length; + chunks.push(chunkBuffer); + }); + return stream; + } + + async realpathPromise(p) { + return this.realpathSync(p); + } + + realpathSync(p) { + const resolvedP = this.resolveFilename(`lstat '${p}'`, p); + if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) throw ENOENT(`lstat '${p}'`); + return resolvedP; + } + + async existsPromise(p) { + return this.existsSync(p); + } + + existsSync(p) { + if (!this.ready) throw EBUSY(`archive closed, existsSync '${p}'`); + + if (this.symlinkCount === 0) { + const resolvedP = ppath.resolve(PortablePath.root, p); + return this.entries.has(resolvedP) || this.listings.has(resolvedP); + } + + let resolvedP; + + try { + resolvedP = this.resolveFilename(`stat '${p}'`, p); + } catch (error) { + return false; + } + + return this.entries.has(resolvedP) || this.listings.has(resolvedP); + } + + async accessPromise(p, mode) { + return this.accessSync(p, mode); + } + + accessSync(p, mode = external_fs_.constants.F_OK) { + const resolvedP = this.resolveFilename(`access '${p}'`, p); + if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) throw ENOENT(`access '${p}'`); + + if (this.readOnly && mode & external_fs_.constants.W_OK) { + throw EROFS(`access '${p}'`); + } + } + + async statPromise(p, opts) { + return this.statSync(p, opts); + } + + statSync(p, opts) { + const resolvedP = this.resolveFilename(`stat '${p}'`, p); + if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) throw ENOENT(`stat '${p}'`); + if (p[p.length - 1] === `/` && !this.listings.has(resolvedP)) throw ENOTDIR(`stat '${p}'`); + return this.statImpl(`stat '${p}'`, resolvedP, opts); + } + + async fstatPromise(fd, opts) { + return this.fstatSync(fd, opts); + } + + fstatSync(fd, opts) { + const entry = this.fds.get(fd); + if (typeof entry === `undefined`) throw EBADF(`fstatSync`); + const { + p + } = entry; + const resolvedP = this.resolveFilename(`stat '${p}'`, p); + if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) throw ENOENT(`stat '${p}'`); + if (p[p.length - 1] === `/` && !this.listings.has(resolvedP)) throw ENOTDIR(`stat '${p}'`); + return this.statImpl(`fstat '${p}'`, resolvedP, opts); + } + + async lstatPromise(p, opts) { + return this.lstatSync(p, opts); + } + + lstatSync(p, opts) { + const resolvedP = this.resolveFilename(`lstat '${p}'`, p, false); + if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) throw ENOENT(`lstat '${p}'`); + if (p[p.length - 1] === `/` && !this.listings.has(resolvedP)) throw ENOTDIR(`lstat '${p}'`); + return this.statImpl(`lstat '${p}'`, resolvedP, opts); + } + + statImpl(reason, p, opts = {}) { + const entry = this.entries.get(p); // File, or explicit directory + + if (typeof entry !== `undefined`) { + const stat = this.libzip.struct.statS(); + const rc = this.libzip.statIndex(this.zip, entry, 0, 0, stat); + if (rc === -1) throw this.makeLibzipError(this.libzip.getError(this.zip)); + const uid = this.stats.uid; + const gid = this.stats.gid; + const size = this.libzip.struct.statSize(stat) >>> 0; + const blksize = 512; + const blocks = Math.ceil(size / blksize); + const mtimeMs = (this.libzip.struct.statMtime(stat) >>> 0) * 1000; + const atimeMs = mtimeMs; + const birthtimeMs = mtimeMs; + const ctimeMs = mtimeMs; + const atime = new Date(atimeMs); + const birthtime = new Date(birthtimeMs); + const ctime = new Date(ctimeMs); + const mtime = new Date(mtimeMs); + const type = this.listings.has(p) ? constants_S_IFDIR : this.isSymbolicLink(entry) ? constants_S_IFLNK : constants_S_IFREG; + const defaultMode = type === constants_S_IFDIR ? 0o755 : 0o644; + const mode = type | this.getUnixMode(entry, defaultMode) & 0o777; + const crc = this.libzip.struct.statCrc(stat); + const statInstance = Object.assign(new StatEntry(), { + uid, + gid, + size, + blksize, + blocks, + atime, + birthtime, + ctime, + mtime, + atimeMs, + birthtimeMs, + ctimeMs, + mtimeMs, + mode, + crc + }); + return opts.bigint === true ? convertToBigIntStats(statInstance) : statInstance; + } // Implicit directory + + + if (this.listings.has(p)) { + const uid = this.stats.uid; + const gid = this.stats.gid; + const size = 0; + const blksize = 512; + const blocks = 0; + const atimeMs = this.stats.mtimeMs; + const birthtimeMs = this.stats.mtimeMs; + const ctimeMs = this.stats.mtimeMs; + const mtimeMs = this.stats.mtimeMs; + const atime = new Date(atimeMs); + const birthtime = new Date(birthtimeMs); + const ctime = new Date(ctimeMs); + const mtime = new Date(mtimeMs); + const mode = constants_S_IFDIR | 0o755; + const crc = 0; + const statInstance = Object.assign(new StatEntry(), { + uid, + gid, + size, + blksize, + blocks, + atime, + birthtime, + ctime, + mtime, + atimeMs, + birthtimeMs, + ctimeMs, + mtimeMs, + mode, + crc + }); + return opts.bigint === true ? convertToBigIntStats(statInstance) : statInstance; + } + + throw new Error(`Unreachable`); + } + + getUnixMode(index, defaultMode) { + const rc = this.libzip.file.getExternalAttributes(this.zip, index, 0, 0, this.libzip.uint08S, this.libzip.uint32S); + if (rc === -1) throw this.makeLibzipError(this.libzip.getError(this.zip)); + const opsys = this.libzip.getValue(this.libzip.uint08S, `i8`) >>> 0; + if (opsys !== this.libzip.ZIP_OPSYS_UNIX) return defaultMode; + return this.libzip.getValue(this.libzip.uint32S, `i32`) >>> 16; + } + + registerListing(p) { + let listing = this.listings.get(p); + if (listing) return listing; + const parentListing = this.registerListing(ppath.dirname(p)); + listing = new Set(); + parentListing.add(ppath.basename(p)); + this.listings.set(p, listing); + return listing; + } + + registerEntry(p, index) { + const parentListing = this.registerListing(ppath.dirname(p)); + parentListing.add(ppath.basename(p)); + this.entries.set(p, index); + } + + unregisterListing(p) { + this.listings.delete(p); + const parentListing = this.listings.get(ppath.dirname(p)); + parentListing === null || parentListing === void 0 ? void 0 : parentListing.delete(ppath.basename(p)); + } + + unregisterEntry(p) { + this.unregisterListing(p); + const entry = this.entries.get(p); + this.entries.delete(p); + if (typeof entry === `undefined`) return; + this.fileSources.delete(entry); + + if (this.isSymbolicLink(entry)) { + this.symlinkCount--; + } + } + + deleteEntry(p, index) { + this.unregisterEntry(p); + const rc = this.libzip.delete(this.zip, index); + + if (rc === -1) { + throw this.makeLibzipError(this.libzip.getError(this.zip)); + } + } + + resolveFilename(reason, p, resolveLastComponent = true) { + if (!this.ready) throw EBUSY(`archive closed, ${reason}`); + let resolvedP = ppath.resolve(PortablePath.root, p); + if (resolvedP === `/`) return PortablePath.root; + const fileIndex = this.entries.get(resolvedP); + + if (resolveLastComponent && fileIndex !== undefined) { + if (this.symlinkCount !== 0 && this.isSymbolicLink(fileIndex)) { + const target = this.getFileSource(fileIndex).toString(); + return this.resolveFilename(reason, ppath.resolve(ppath.dirname(resolvedP), target), true); + } else { + return resolvedP; + } + } + + while (true) { + const parentP = this.resolveFilename(reason, ppath.dirname(resolvedP), true); + const isDir = this.listings.has(parentP); + const doesExist = this.entries.has(parentP); + if (!isDir && !doesExist) throw ENOENT(reason); + if (!isDir) throw ENOTDIR(reason); + resolvedP = ppath.resolve(parentP, ppath.basename(resolvedP)); + if (!resolveLastComponent || this.symlinkCount === 0) break; + const index = this.libzip.name.locate(this.zip, resolvedP.slice(1)); + if (index === -1) break; + + if (this.isSymbolicLink(index)) { + const target = this.getFileSource(index).toString(); + resolvedP = ppath.resolve(ppath.dirname(resolvedP), target); + } else { + break; + } + } + + return resolvedP; + } + + allocateBuffer(content) { + if (!Buffer.isBuffer(content)) content = Buffer.from(content); + const buffer = this.libzip.malloc(content.byteLength); + if (!buffer) throw new Error(`Couldn't allocate enough memory`); // Copy the file into the Emscripten heap + + const heap = new Uint8Array(this.libzip.HEAPU8.buffer, buffer, content.byteLength); + heap.set(content); + return { + buffer, + byteLength: content.byteLength + }; + } + + allocateUnattachedSource(content) { + const error = this.libzip.struct.errorS(); + const { + buffer, + byteLength + } = this.allocateBuffer(content); + const source = this.libzip.source.fromUnattachedBuffer(buffer, byteLength, 0, true, error); + + if (source === 0) { + this.libzip.free(error); + throw this.makeLibzipError(error); + } + + return source; + } + + allocateSource(content) { + const { + buffer, + byteLength + } = this.allocateBuffer(content); + const source = this.libzip.source.fromBuffer(this.zip, buffer, byteLength, 0, true); + + if (source === 0) { + this.libzip.free(buffer); + throw this.makeLibzipError(this.libzip.getError(this.zip)); + } + + return source; + } + + setFileSource(p, content) { + const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content); + const target = ppath.relative(PortablePath.root, p); + const lzSource = this.allocateSource(content); + + try { + const newIndex = this.libzip.file.add(this.zip, target, lzSource, this.libzip.ZIP_FL_OVERWRITE); + if (newIndex === -1) throw this.makeLibzipError(this.libzip.getError(this.zip)); + + if (this.level !== `mixed`) { + // Use store for level 0, and deflate for 1..9 + let method; + if (this.level === 0) method = this.libzip.ZIP_CM_STORE;else method = this.libzip.ZIP_CM_DEFLATE; + const rc = this.libzip.file.setCompression(this.zip, newIndex, 0, method, this.level); + + if (rc === -1) { + throw this.makeLibzipError(this.libzip.getError(this.zip)); + } + } + + this.fileSources.set(newIndex, buffer); + return newIndex; + } catch (error) { + this.libzip.source.free(lzSource); + throw error; + } + } + + isSymbolicLink(index) { + if (this.symlinkCount === 0) return false; + const attrs = this.libzip.file.getExternalAttributes(this.zip, index, 0, 0, this.libzip.uint08S, this.libzip.uint32S); + if (attrs === -1) throw this.makeLibzipError(this.libzip.getError(this.zip)); + const opsys = this.libzip.getValue(this.libzip.uint08S, `i8`) >>> 0; + if (opsys !== this.libzip.ZIP_OPSYS_UNIX) return false; + const attributes = this.libzip.getValue(this.libzip.uint32S, `i32`) >>> 16; + return (attributes & constants_S_IFMT) === constants_S_IFLNK; + } + + getFileSource(index, opts = { + asyncDecompress: false + }) { + const cachedFileSource = this.fileSources.get(index); + if (typeof cachedFileSource !== `undefined`) return cachedFileSource; + const stat = this.libzip.struct.statS(); + const rc = this.libzip.statIndex(this.zip, index, 0, 0, stat); + if (rc === -1) throw this.makeLibzipError(this.libzip.getError(this.zip)); + const size = this.libzip.struct.statCompSize(stat); + const compressionMethod = this.libzip.struct.statCompMethod(stat); + const buffer = this.libzip.malloc(size); + + try { + const file = this.libzip.fopenIndex(this.zip, index, 0, this.libzip.ZIP_FL_COMPRESSED); + if (file === 0) throw this.makeLibzipError(this.libzip.getError(this.zip)); + + try { + const rc = this.libzip.fread(file, buffer, size, 0); + if (rc === -1) throw this.makeLibzipError(this.libzip.file.getError(file));else if (rc < size) throw new Error(`Incomplete read`);else if (rc > size) throw new Error(`Overread`); + const memory = this.libzip.HEAPU8.subarray(buffer, buffer + size); + const data = Buffer.from(memory); + + if (compressionMethod === 0) { + this.fileSources.set(index, data); + return data; + } else if (opts.asyncDecompress) { + return new Promise((resolve, reject) => { + external_zlib_default().inflateRaw(data, (error, result) => { + if (error) { + reject(error); + } else { + this.fileSources.set(index, result); + resolve(result); + } + }); + }); + } else { + const decompressedData = external_zlib_default().inflateRawSync(data); + this.fileSources.set(index, decompressedData); + return decompressedData; + } + } finally { + this.libzip.fclose(file); + } + } finally { + this.libzip.free(buffer); + } + } + + async chmodPromise(p, mask) { + return this.chmodSync(p, mask); + } + + chmodSync(p, mask) { + if (this.readOnly) throw EROFS(`chmod '${p}'`); // We don't allow to make the extracted entries group-writable + + mask &= 0o755; + const resolvedP = this.resolveFilename(`chmod '${p}'`, p, false); + const entry = this.entries.get(resolvedP); + if (typeof entry === `undefined`) throw new Error(`Assertion failed: The entry should have been registered (${resolvedP})`); + const oldMod = this.getUnixMode(entry, constants_S_IFREG | 0o000); + const newMod = oldMod & ~0o777 | mask; + const rc = this.libzip.file.setExternalAttributes(this.zip, entry, 0, 0, this.libzip.ZIP_OPSYS_UNIX, newMod << 16); + + if (rc === -1) { + throw this.makeLibzipError(this.libzip.getError(this.zip)); + } + } + + async chownPromise(p, uid, gid) { + return this.chownSync(p, uid, gid); + } + + chownSync(p, uid, gid) { + throw new Error(`Unimplemented`); + } + + async renamePromise(oldP, newP) { + return this.renameSync(oldP, newP); + } + + renameSync(oldP, newP) { + throw new Error(`Unimplemented`); + } + + async copyFilePromise(sourceP, destP, flags) { + const { + indexSource, + indexDest, + resolvedDestP + } = this.prepareCopyFile(sourceP, destP, flags); + const source = await this.getFileSource(indexSource, { + asyncDecompress: true + }); + const newIndex = this.setFileSource(resolvedDestP, source); + + if (newIndex !== indexDest) { + this.registerEntry(resolvedDestP, newIndex); + } + } + + copyFileSync(sourceP, destP, flags = 0) { + const { + indexSource, + indexDest, + resolvedDestP + } = this.prepareCopyFile(sourceP, destP, flags); + const source = this.getFileSource(indexSource); + const newIndex = this.setFileSource(resolvedDestP, source); + + if (newIndex !== indexDest) { + this.registerEntry(resolvedDestP, newIndex); + } + } + + prepareCopyFile(sourceP, destP, flags = 0) { + if (this.readOnly) throw EROFS(`copyfile '${sourceP} -> '${destP}'`); + if ((flags & external_fs_.constants.COPYFILE_FICLONE_FORCE) !== 0) throw ENOSYS(`unsupported clone operation`, `copyfile '${sourceP}' -> ${destP}'`); + const resolvedSourceP = this.resolveFilename(`copyfile '${sourceP} -> ${destP}'`, sourceP); + const indexSource = this.entries.get(resolvedSourceP); + if (typeof indexSource === `undefined`) throw EINVAL(`copyfile '${sourceP}' -> '${destP}'`); + const resolvedDestP = this.resolveFilename(`copyfile '${sourceP}' -> ${destP}'`, destP); + const indexDest = this.entries.get(resolvedDestP); + if ((flags & (external_fs_.constants.COPYFILE_EXCL | external_fs_.constants.COPYFILE_FICLONE_FORCE)) !== 0 && typeof indexDest !== `undefined`) throw EEXIST(`copyfile '${sourceP}' -> '${destP}'`); + return { + indexSource, + resolvedDestP, + indexDest + }; + } + + async appendFilePromise(p, content, opts) { + if (this.readOnly) throw EROFS(`open '${p}'`); + if (typeof opts === `undefined`) opts = { + flag: `a` + };else if (typeof opts === `string`) opts = { + flag: `a`, + encoding: opts + };else if (typeof opts.flag === `undefined`) opts = { + flag: `a`, + ...opts + }; + return this.writeFilePromise(p, content, opts); + } + + appendFileSync(p, content, opts = {}) { + if (this.readOnly) throw EROFS(`open '${p}'`); + if (typeof opts === `undefined`) opts = { + flag: `a` + };else if (typeof opts === `string`) opts = { + flag: `a`, + encoding: opts + };else if (typeof opts.flag === `undefined`) opts = { + flag: `a`, + ...opts + }; + return this.writeFileSync(p, content, opts); + } + + fdToPath(fd, reason) { + var _a; + + const path = (_a = this.fds.get(fd)) === null || _a === void 0 ? void 0 : _a.p; + if (typeof path === `undefined`) throw EBADF(reason); + return path; + } + + async writeFilePromise(p, content, opts) { + const { + encoding, + mode, + index, + resolvedP + } = this.prepareWriteFile(p, opts); + if (index !== undefined && typeof opts === `object` && opts.flag && opts.flag.includes(`a`)) content = Buffer.concat([await this.getFileSource(index, { + asyncDecompress: true + }), Buffer.from(content)]); + if (encoding !== null) content = content.toString(encoding); + const newIndex = this.setFileSource(resolvedP, content); + if (newIndex !== index) this.registerEntry(resolvedP, newIndex); + + if (mode !== null) { + await this.chmodPromise(resolvedP, mode); + } + } + + writeFileSync(p, content, opts) { + const { + encoding, + mode, + index, + resolvedP + } = this.prepareWriteFile(p, opts); + if (index !== undefined && typeof opts === `object` && opts.flag && opts.flag.includes(`a`)) content = Buffer.concat([this.getFileSource(index), Buffer.from(content)]); + if (encoding !== null) content = content.toString(encoding); + const newIndex = this.setFileSource(resolvedP, content); + if (newIndex !== index) this.registerEntry(resolvedP, newIndex); + + if (mode !== null) { + this.chmodSync(resolvedP, mode); + } + } + + prepareWriteFile(p, opts) { + if (typeof p === `number`) p = this.fdToPath(p, `read`); + if (this.readOnly) throw EROFS(`open '${p}'`); + const resolvedP = this.resolveFilename(`open '${p}'`, p); + if (this.listings.has(resolvedP)) throw EISDIR(`open '${p}'`); + let encoding = null, + mode = null; + + if (typeof opts === `string`) { + encoding = opts; + } else if (typeof opts === `object`) { + ({ + encoding = null, + mode = null + } = opts); + } + + const index = this.entries.get(resolvedP); + return { + encoding, + mode, + resolvedP, + index + }; + } + + async unlinkPromise(p) { + return this.unlinkSync(p); + } + + unlinkSync(p) { + if (this.readOnly) throw EROFS(`unlink '${p}'`); + const resolvedP = this.resolveFilename(`unlink '${p}'`, p); + if (this.listings.has(resolvedP)) throw EISDIR(`unlink '${p}'`); + const index = this.entries.get(resolvedP); + if (typeof index === `undefined`) throw EINVAL(`unlink '${p}'`); + this.deleteEntry(resolvedP, index); + } + + async utimesPromise(p, atime, mtime) { + return this.utimesSync(p, atime, mtime); + } + + utimesSync(p, atime, mtime) { + if (this.readOnly) throw EROFS(`utimes '${p}'`); + const resolvedP = this.resolveFilename(`utimes '${p}'`, p); + this.utimesImpl(resolvedP, mtime); + } + + async lutimesPromise(p, atime, mtime) { + return this.lutimesSync(p, atime, mtime); + } + + lutimesSync(p, atime, mtime) { + if (this.readOnly) throw EROFS(`lutimes '${p}'`); + const resolvedP = this.resolveFilename(`utimes '${p}'`, p, false); + this.utimesImpl(resolvedP, mtime); + } + + utimesImpl(resolvedP, mtime) { + if (this.listings.has(resolvedP)) if (!this.entries.has(resolvedP)) this.hydrateDirectory(resolvedP); + const entry = this.entries.get(resolvedP); + if (entry === undefined) throw new Error(`Unreachable`); + const rc = this.libzip.file.setMtime(this.zip, entry, 0, toUnixTimestamp(mtime), 0); + + if (rc === -1) { + throw this.makeLibzipError(this.libzip.getError(this.zip)); + } + } + + async mkdirPromise(p, opts) { + return this.mkdirSync(p, opts); + } + + mkdirSync(p, { + mode = 0o755, + recursive = false + } = {}) { + if (recursive) { + this.mkdirpSync(p, { + chmod: mode + }); + return; + } + + if (this.readOnly) throw EROFS(`mkdir '${p}'`); + const resolvedP = this.resolveFilename(`mkdir '${p}'`, p); + if (this.entries.has(resolvedP) || this.listings.has(resolvedP)) throw EEXIST(`mkdir '${p}'`); + this.hydrateDirectory(resolvedP); + this.chmodSync(resolvedP, mode); + } + + async rmdirPromise(p, opts) { + return this.rmdirSync(p, opts); + } + + rmdirSync(p, { + recursive = false + } = {}) { + if (this.readOnly) throw EROFS(`rmdir '${p}'`); + + if (recursive) { + this.removeSync(p); + return; + } + + const resolvedP = this.resolveFilename(`rmdir '${p}'`, p); + const directoryListing = this.listings.get(resolvedP); + if (!directoryListing) throw ENOTDIR(`rmdir '${p}'`); + if (directoryListing.size > 0) throw ENOTEMPTY(`rmdir '${p}'`); + const index = this.entries.get(resolvedP); + if (typeof index === `undefined`) throw EINVAL(`rmdir '${p}'`); + this.deleteEntry(p, index); + } + + hydrateDirectory(resolvedP) { + const index = this.libzip.dir.add(this.zip, ppath.relative(PortablePath.root, resolvedP)); + if (index === -1) throw this.makeLibzipError(this.libzip.getError(this.zip)); + this.registerListing(resolvedP); + this.registerEntry(resolvedP, index); + return index; + } + + async linkPromise(existingP, newP) { + return this.linkSync(existingP, newP); + } + + linkSync(existingP, newP) { + // Zip archives don't support hard links: + // https://stackoverflow.com/questions/8859616/are-hard-links-possible-within-a-zip-archive + throw EOPNOTSUPP(`link '${existingP}' -> '${newP}'`); + } + + async symlinkPromise(target, p) { + return this.symlinkSync(target, p); + } + + symlinkSync(target, p) { + if (this.readOnly) throw EROFS(`symlink '${target}' -> '${p}'`); + const resolvedP = this.resolveFilename(`symlink '${target}' -> '${p}'`, p); + if (this.listings.has(resolvedP)) throw EISDIR(`symlink '${target}' -> '${p}'`); + if (this.entries.has(resolvedP)) throw EEXIST(`symlink '${target}' -> '${p}'`); + const index = this.setFileSource(resolvedP, target); + this.registerEntry(resolvedP, index); + const rc = this.libzip.file.setExternalAttributes(this.zip, index, 0, 0, this.libzip.ZIP_OPSYS_UNIX, (constants_S_IFLNK | 0o777) << 16); + if (rc === -1) throw this.makeLibzipError(this.libzip.getError(this.zip)); + this.symlinkCount += 1; + } + + async readFilePromise(p, encoding) { + // This is messed up regarding the TS signatures + if (typeof encoding === `object`) // @ts-expect-error + encoding = encoding ? encoding.encoding : undefined; + const data = await this.readFileBuffer(p, { + asyncDecompress: true + }); + return encoding ? data.toString(encoding) : data; + } + + readFileSync(p, encoding) { + // This is messed up regarding the TS signatures + if (typeof encoding === `object`) // @ts-expect-error + encoding = encoding ? encoding.encoding : undefined; + const data = this.readFileBuffer(p); + return encoding ? data.toString(encoding) : data; + } + + readFileBuffer(p, opts = { + asyncDecompress: false + }) { + if (typeof p === `number`) p = this.fdToPath(p, `read`); + const resolvedP = this.resolveFilename(`open '${p}'`, p); + if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) throw ENOENT(`open '${p}'`); // Ensures that the last component is a directory, if the user said so (even if it is we'll throw right after with EISDIR anyway) + + if (p[p.length - 1] === `/` && !this.listings.has(resolvedP)) throw ENOTDIR(`open '${p}'`); + if (this.listings.has(resolvedP)) throw EISDIR(`read`); + const entry = this.entries.get(resolvedP); + if (entry === undefined) throw new Error(`Unreachable`); + return this.getFileSource(entry, opts); + } + + async readdirPromise(p, opts) { + return this.readdirSync(p, opts); + } + + readdirSync(p, opts) { + const resolvedP = this.resolveFilename(`scandir '${p}'`, p); + if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) throw ENOENT(`scandir '${p}'`); + const directoryListing = this.listings.get(resolvedP); + if (!directoryListing) throw ENOTDIR(`scandir '${p}'`); + const entries = [...directoryListing]; + if (!(opts === null || opts === void 0 ? void 0 : opts.withFileTypes)) return entries; + return entries.map(name => { + return Object.assign(this.statImpl(`lstat`, ppath.join(p, name)), { + name + }); + }); + } + + async readlinkPromise(p) { + const entry = this.prepareReadlink(p); + return (await this.getFileSource(entry, { + asyncDecompress: true + })).toString(); + } + + readlinkSync(p) { + const entry = this.prepareReadlink(p); + return this.getFileSource(entry).toString(); + } + + prepareReadlink(p) { + const resolvedP = this.resolveFilename(`readlink '${p}'`, p, false); + if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) throw ENOENT(`readlink '${p}'`); // Ensure that the last component is a directory (if it is we'll throw right after with EISDIR anyway) + + if (p[p.length - 1] === `/` && !this.listings.has(resolvedP)) throw ENOTDIR(`open '${p}'`); + if (this.listings.has(resolvedP)) throw EINVAL(`readlink '${p}'`); + const entry = this.entries.get(resolvedP); + if (entry === undefined) throw new Error(`Unreachable`); + if (!this.isSymbolicLink(entry)) throw EINVAL(`readlink '${p}'`); + return entry; + } + + async truncatePromise(p, len = 0) { + const resolvedP = this.resolveFilename(`open '${p}'`, p); + const index = this.entries.get(resolvedP); + if (typeof index === `undefined`) throw EINVAL(`open '${p}'`); + const source = await this.getFileSource(index, { + asyncDecompress: true + }); + const truncated = Buffer.alloc(len, 0x00); + source.copy(truncated); + return await this.writeFilePromise(p, truncated); + } + + truncateSync(p, len = 0) { + const resolvedP = this.resolveFilename(`open '${p}'`, p); + const index = this.entries.get(resolvedP); + if (typeof index === `undefined`) throw EINVAL(`open '${p}'`); + const source = this.getFileSource(index); + const truncated = Buffer.alloc(len, 0x00); + source.copy(truncated); + return this.writeFileSync(p, truncated); + } + + watch(p, a, b) { + let persistent; + + switch (typeof a) { + case `function`: + case `string`: + case `undefined`: + { + persistent = true; + } + break; + + default: + { + ({ + persistent = true + } = a); + } + break; + } + + if (!persistent) return { + on: () => {}, + close: () => {} + }; + const interval = setInterval(() => {}, 24 * 60 * 60 * 1000); + return { + on: () => {}, + close: () => { + clearInterval(interval); + } + }; + } + + watchFile(p, a, b) { + const resolvedP = ppath.resolve(PortablePath.root, p); + return watchFile(this, resolvedP, a, b); + } + + unwatchFile(p, cb) { + const resolvedP = ppath.resolve(PortablePath.root, p); + return unwatchFile(this, resolvedP, cb); + } + +} +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/ZipOpenFS.ts + + + + + + + +const ZIP_FD = 0x80000000; +/** + * Extracts the archive part (ending in the first instance of `extension`) from a path. + * + * The indexOf-based implementation is ~3.7x faster than a RegExp-based implementation. + */ + +const getArchivePart = (path, extension) => { + let idx = path.indexOf(extension); + if (idx <= 0) return null; + let nextCharIdx = idx; + + while (idx >= 0) { + nextCharIdx = idx + extension.length; + if (path[nextCharIdx] === ppath.sep) break; // Disallow files named ".zip" + + if (path[idx - 1] === ppath.sep) return null; + idx = path.indexOf(extension, nextCharIdx); + } // The path either has to end in ".zip" or contain an archive subpath (".zip/...") + + + if (path.length > nextCharIdx && path[nextCharIdx] !== ppath.sep) return null; + return path.slice(0, nextCharIdx); +}; +class ZipOpenFS extends BasePortableFakeFS { + constructor({ + libzip, + baseFs = new NodeFS(), + filter = null, + maxOpenFiles = Infinity, + readOnlyArchives = false, + useCache = true, + maxAge = 5000, + fileExtensions = null + }) { + super(); + this.fdMap = new Map(); + this.nextFd = 3; + this.isZip = new Set(); + this.notZip = new Set(); + this.realPaths = new Map(); + this.limitOpenFilesTimeout = null; + this.libzipFactory = typeof libzip !== `function` ? () => libzip : libzip; + this.baseFs = baseFs; + this.zipInstances = useCache ? new Map() : null; + this.filter = filter; + this.maxOpenFiles = maxOpenFiles; + this.readOnlyArchives = readOnlyArchives; + this.maxAge = maxAge; + this.fileExtensions = fileExtensions; + } + + static async openPromise(fn, opts) { + const zipOpenFs = new ZipOpenFS(opts); + + try { + return await fn(zipOpenFs); + } finally { + zipOpenFs.saveAndClose(); + } + } + + get libzip() { + if (typeof this.libzipInstance === `undefined`) this.libzipInstance = this.libzipFactory(); + return this.libzipInstance; + } + + getExtractHint(hints) { + return this.baseFs.getExtractHint(hints); + } + + getRealPath() { + return this.baseFs.getRealPath(); + } + + saveAndClose() { + unwatchAllFiles(this); + + if (this.zipInstances) { + for (const [path, { + zipFs + }] of this.zipInstances.entries()) { + zipFs.saveAndClose(); + this.zipInstances.delete(path); + } + } + } + + discardAndClose() { + unwatchAllFiles(this); + + if (this.zipInstances) { + for (const [path, { + zipFs + }] of this.zipInstances.entries()) { + zipFs.discardAndClose(); + this.zipInstances.delete(path); + } + } + } + + resolve(p) { + return this.baseFs.resolve(p); + } + + remapFd(zipFs, fd) { + const remappedFd = this.nextFd++ | ZIP_FD; + this.fdMap.set(remappedFd, [zipFs, fd]); + return remappedFd; + } + + async openPromise(p, flags, mode) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.openPromise(p, flags, mode); + }, async (zipFs, { + subPath + }) => { + return this.remapFd(zipFs, await zipFs.openPromise(subPath, flags, mode)); + }); + } + + openSync(p, flags, mode) { + return this.makeCallSync(p, () => { + return this.baseFs.openSync(p, flags, mode); + }, (zipFs, { + subPath + }) => { + return this.remapFd(zipFs, zipFs.openSync(subPath, flags, mode)); + }); + } + + async opendirPromise(p, opts) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.opendirPromise(p, opts); + }, async (zipFs, { + subPath + }) => { + return await zipFs.opendirPromise(subPath, opts); + }, { + requireSubpath: false + }); + } + + opendirSync(p, opts) { + return this.makeCallSync(p, () => { + return this.baseFs.opendirSync(p, opts); + }, (zipFs, { + subPath + }) => { + return zipFs.opendirSync(subPath, opts); + }, { + requireSubpath: false + }); + } + + async readPromise(fd, buffer, offset, length, position) { + if ((fd & ZIP_FD) === 0) return await this.baseFs.readPromise(fd, buffer, offset, length, position); + const entry = this.fdMap.get(fd); + if (typeof entry === `undefined`) throw EBADF(`read`); + const [zipFs, realFd] = entry; + return await zipFs.readPromise(realFd, buffer, offset, length, position); + } + + readSync(fd, buffer, offset, length, position) { + if ((fd & ZIP_FD) === 0) return this.baseFs.readSync(fd, buffer, offset, length, position); + const entry = this.fdMap.get(fd); + if (typeof entry === `undefined`) throw EBADF(`readSync`); + const [zipFs, realFd] = entry; + return zipFs.readSync(realFd, buffer, offset, length, position); + } + + async writePromise(fd, buffer, offset, length, position) { + if ((fd & ZIP_FD) === 0) { + if (typeof buffer === `string`) { + return await this.baseFs.writePromise(fd, buffer, offset); + } else { + return await this.baseFs.writePromise(fd, buffer, offset, length, position); + } + } + + const entry = this.fdMap.get(fd); + if (typeof entry === `undefined`) throw EBADF(`write`); + const [zipFs, realFd] = entry; + + if (typeof buffer === `string`) { + return await zipFs.writePromise(realFd, buffer, offset); + } else { + return await zipFs.writePromise(realFd, buffer, offset, length, position); + } + } + + writeSync(fd, buffer, offset, length, position) { + if ((fd & ZIP_FD) === 0) { + if (typeof buffer === `string`) { + return this.baseFs.writeSync(fd, buffer, offset); + } else { + return this.baseFs.writeSync(fd, buffer, offset, length, position); + } + } + + const entry = this.fdMap.get(fd); + if (typeof entry === `undefined`) throw EBADF(`writeSync`); + const [zipFs, realFd] = entry; + + if (typeof buffer === `string`) { + return zipFs.writeSync(realFd, buffer, offset); + } else { + return zipFs.writeSync(realFd, buffer, offset, length, position); + } + } + + async closePromise(fd) { + if ((fd & ZIP_FD) === 0) return await this.baseFs.closePromise(fd); + const entry = this.fdMap.get(fd); + if (typeof entry === `undefined`) throw EBADF(`close`); + this.fdMap.delete(fd); + const [zipFs, realFd] = entry; + return await zipFs.closePromise(realFd); + } + + closeSync(fd) { + if ((fd & ZIP_FD) === 0) return this.baseFs.closeSync(fd); + const entry = this.fdMap.get(fd); + if (typeof entry === `undefined`) throw EBADF(`closeSync`); + this.fdMap.delete(fd); + const [zipFs, realFd] = entry; + return zipFs.closeSync(realFd); + } + + createReadStream(p, opts) { + if (p === null) return this.baseFs.createReadStream(p, opts); + return this.makeCallSync(p, () => { + return this.baseFs.createReadStream(p, opts); + }, (zipFs, { + subPath + }) => { + return zipFs.createReadStream(subPath, opts); + }); + } + + createWriteStream(p, opts) { + if (p === null) return this.baseFs.createWriteStream(p, opts); + return this.makeCallSync(p, () => { + return this.baseFs.createWriteStream(p, opts); + }, (zipFs, { + subPath + }) => { + return zipFs.createWriteStream(subPath, opts); + }); + } + + async realpathPromise(p) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.realpathPromise(p); + }, async (zipFs, { + archivePath, + subPath + }) => { + let realArchivePath = this.realPaths.get(archivePath); + + if (typeof realArchivePath === `undefined`) { + realArchivePath = await this.baseFs.realpathPromise(archivePath); + this.realPaths.set(archivePath, realArchivePath); + } + + return this.pathUtils.join(realArchivePath, this.pathUtils.relative(PortablePath.root, await zipFs.realpathPromise(subPath))); + }); + } + + realpathSync(p) { + return this.makeCallSync(p, () => { + return this.baseFs.realpathSync(p); + }, (zipFs, { + archivePath, + subPath + }) => { + let realArchivePath = this.realPaths.get(archivePath); + + if (typeof realArchivePath === `undefined`) { + realArchivePath = this.baseFs.realpathSync(archivePath); + this.realPaths.set(archivePath, realArchivePath); + } + + return this.pathUtils.join(realArchivePath, this.pathUtils.relative(PortablePath.root, zipFs.realpathSync(subPath))); + }); + } + + async existsPromise(p) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.existsPromise(p); + }, async (zipFs, { + subPath + }) => { + return await zipFs.existsPromise(subPath); + }); + } + + existsSync(p) { + return this.makeCallSync(p, () => { + return this.baseFs.existsSync(p); + }, (zipFs, { + subPath + }) => { + return zipFs.existsSync(subPath); + }); + } + + async accessPromise(p, mode) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.accessPromise(p, mode); + }, async (zipFs, { + subPath + }) => { + return await zipFs.accessPromise(subPath, mode); + }); + } + + accessSync(p, mode) { + return this.makeCallSync(p, () => { + return this.baseFs.accessSync(p, mode); + }, (zipFs, { + subPath + }) => { + return zipFs.accessSync(subPath, mode); + }); + } + + async statPromise(p, opts) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.statPromise(p, opts); + }, async (zipFs, { + subPath + }) => { + return await zipFs.statPromise(subPath, opts); + }); + } + + statSync(p, opts) { + return this.makeCallSync(p, () => { + return this.baseFs.statSync(p, opts); + }, (zipFs, { + subPath + }) => { + return zipFs.statSync(subPath, opts); + }); + } + + async fstatPromise(fd, opts) { + if ((fd & ZIP_FD) === 0) return this.baseFs.fstatPromise(fd, opts); + const entry = this.fdMap.get(fd); + if (typeof entry === `undefined`) throw EBADF(`fstat`); + const [zipFs, realFd] = entry; + return zipFs.fstatPromise(realFd, opts); + } + + fstatSync(fd, opts) { + if ((fd & ZIP_FD) === 0) return this.baseFs.fstatSync(fd, opts); + const entry = this.fdMap.get(fd); + if (typeof entry === `undefined`) throw EBADF(`fstatSync`); + const [zipFs, realFd] = entry; + return zipFs.fstatSync(realFd, opts); + } + + async lstatPromise(p, opts) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.lstatPromise(p, opts); + }, async (zipFs, { + subPath + }) => { + return await zipFs.lstatPromise(subPath, opts); + }); + } + + lstatSync(p, opts) { + return this.makeCallSync(p, () => { + return this.baseFs.lstatSync(p, opts); + }, (zipFs, { + subPath + }) => { + return zipFs.lstatSync(subPath, opts); + }); + } + + async chmodPromise(p, mask) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.chmodPromise(p, mask); + }, async (zipFs, { + subPath + }) => { + return await zipFs.chmodPromise(subPath, mask); + }); + } + + chmodSync(p, mask) { + return this.makeCallSync(p, () => { + return this.baseFs.chmodSync(p, mask); + }, (zipFs, { + subPath + }) => { + return zipFs.chmodSync(subPath, mask); + }); + } + + async chownPromise(p, uid, gid) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.chownPromise(p, uid, gid); + }, async (zipFs, { + subPath + }) => { + return await zipFs.chownPromise(subPath, uid, gid); + }); + } + + chownSync(p, uid, gid) { + return this.makeCallSync(p, () => { + return this.baseFs.chownSync(p, uid, gid); + }, (zipFs, { + subPath + }) => { + return zipFs.chownSync(subPath, uid, gid); + }); + } + + async renamePromise(oldP, newP) { + return await this.makeCallPromise(oldP, async () => { + return await this.makeCallPromise(newP, async () => { + return await this.baseFs.renamePromise(oldP, newP); + }, async () => { + throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { + code: `EEXDEV` + }); + }); + }, async (zipFsO, { + subPath: subPathO + }) => { + return await this.makeCallPromise(newP, async () => { + throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { + code: `EEXDEV` + }); + }, async (zipFsN, { + subPath: subPathN + }) => { + if (zipFsO !== zipFsN) { + throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { + code: `EEXDEV` + }); + } else { + return await zipFsO.renamePromise(subPathO, subPathN); + } + }); + }); + } + + renameSync(oldP, newP) { + return this.makeCallSync(oldP, () => { + return this.makeCallSync(newP, () => { + return this.baseFs.renameSync(oldP, newP); + }, () => { + throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { + code: `EEXDEV` + }); + }); + }, (zipFsO, { + subPath: subPathO + }) => { + return this.makeCallSync(newP, () => { + throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { + code: `EEXDEV` + }); + }, (zipFsN, { + subPath: subPathN + }) => { + if (zipFsO !== zipFsN) { + throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { + code: `EEXDEV` + }); + } else { + return zipFsO.renameSync(subPathO, subPathN); + } + }); + }); + } + + async copyFilePromise(sourceP, destP, flags = 0) { + const fallback = async (sourceFs, sourceP, destFs, destP) => { + if ((flags & external_fs_.constants.COPYFILE_FICLONE_FORCE) !== 0) throw Object.assign(new Error(`EXDEV: cross-device clone not permitted, copyfile '${sourceP}' -> ${destP}'`), { + code: `EXDEV` + }); + if (flags & external_fs_.constants.COPYFILE_EXCL && (await this.existsPromise(sourceP))) throw Object.assign(new Error(`EEXIST: file already exists, copyfile '${sourceP}' -> '${destP}'`), { + code: `EEXIST` + }); + let content; + + try { + content = await sourceFs.readFilePromise(sourceP); + } catch (error) { + throw Object.assign(new Error(`EINVAL: invalid argument, copyfile '${sourceP}' -> '${destP}'`), { + code: `EINVAL` + }); + } + + await destFs.writeFilePromise(destP, content); + }; + + return await this.makeCallPromise(sourceP, async () => { + return await this.makeCallPromise(destP, async () => { + return await this.baseFs.copyFilePromise(sourceP, destP, flags); + }, async (zipFsD, { + subPath: subPathD + }) => { + return await fallback(this.baseFs, sourceP, zipFsD, subPathD); + }); + }, async (zipFsS, { + subPath: subPathS + }) => { + return await this.makeCallPromise(destP, async () => { + return await fallback(zipFsS, subPathS, this.baseFs, destP); + }, async (zipFsD, { + subPath: subPathD + }) => { + if (zipFsS !== zipFsD) { + return await fallback(zipFsS, subPathS, zipFsD, subPathD); + } else { + return await zipFsS.copyFilePromise(subPathS, subPathD, flags); + } + }); + }); + } + + copyFileSync(sourceP, destP, flags = 0) { + const fallback = (sourceFs, sourceP, destFs, destP) => { + if ((flags & external_fs_.constants.COPYFILE_FICLONE_FORCE) !== 0) throw Object.assign(new Error(`EXDEV: cross-device clone not permitted, copyfile '${sourceP}' -> ${destP}'`), { + code: `EXDEV` + }); + if (flags & external_fs_.constants.COPYFILE_EXCL && this.existsSync(sourceP)) throw Object.assign(new Error(`EEXIST: file already exists, copyfile '${sourceP}' -> '${destP}'`), { + code: `EEXIST` + }); + let content; + + try { + content = sourceFs.readFileSync(sourceP); + } catch (error) { + throw Object.assign(new Error(`EINVAL: invalid argument, copyfile '${sourceP}' -> '${destP}'`), { + code: `EINVAL` + }); + } + + destFs.writeFileSync(destP, content); + }; + + return this.makeCallSync(sourceP, () => { + return this.makeCallSync(destP, () => { + return this.baseFs.copyFileSync(sourceP, destP, flags); + }, (zipFsD, { + subPath: subPathD + }) => { + return fallback(this.baseFs, sourceP, zipFsD, subPathD); + }); + }, (zipFsS, { + subPath: subPathS + }) => { + return this.makeCallSync(destP, () => { + return fallback(zipFsS, subPathS, this.baseFs, destP); + }, (zipFsD, { + subPath: subPathD + }) => { + if (zipFsS !== zipFsD) { + return fallback(zipFsS, subPathS, zipFsD, subPathD); + } else { + return zipFsS.copyFileSync(subPathS, subPathD, flags); + } + }); + }); + } + + async appendFilePromise(p, content, opts) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.appendFilePromise(p, content, opts); + }, async (zipFs, { + subPath + }) => { + return await zipFs.appendFilePromise(subPath, content, opts); + }); + } + + appendFileSync(p, content, opts) { + return this.makeCallSync(p, () => { + return this.baseFs.appendFileSync(p, content, opts); + }, (zipFs, { + subPath + }) => { + return zipFs.appendFileSync(subPath, content, opts); + }); + } + + async writeFilePromise(p, content, opts) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.writeFilePromise(p, content, opts); + }, async (zipFs, { + subPath + }) => { + return await zipFs.writeFilePromise(subPath, content, opts); + }); + } + + writeFileSync(p, content, opts) { + return this.makeCallSync(p, () => { + return this.baseFs.writeFileSync(p, content, opts); + }, (zipFs, { + subPath + }) => { + return zipFs.writeFileSync(subPath, content, opts); + }); + } + + async unlinkPromise(p) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.unlinkPromise(p); + }, async (zipFs, { + subPath + }) => { + return await zipFs.unlinkPromise(subPath); + }); + } + + unlinkSync(p) { + return this.makeCallSync(p, () => { + return this.baseFs.unlinkSync(p); + }, (zipFs, { + subPath + }) => { + return zipFs.unlinkSync(subPath); + }); + } + + async utimesPromise(p, atime, mtime) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.utimesPromise(p, atime, mtime); + }, async (zipFs, { + subPath + }) => { + return await zipFs.utimesPromise(subPath, atime, mtime); + }); + } + + utimesSync(p, atime, mtime) { + return this.makeCallSync(p, () => { + return this.baseFs.utimesSync(p, atime, mtime); + }, (zipFs, { + subPath + }) => { + return zipFs.utimesSync(subPath, atime, mtime); + }); + } + + async mkdirPromise(p, opts) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.mkdirPromise(p, opts); + }, async (zipFs, { + subPath + }) => { + return await zipFs.mkdirPromise(subPath, opts); + }); + } + + mkdirSync(p, opts) { + return this.makeCallSync(p, () => { + return this.baseFs.mkdirSync(p, opts); + }, (zipFs, { + subPath + }) => { + return zipFs.mkdirSync(subPath, opts); + }); + } + + async rmdirPromise(p, opts) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.rmdirPromise(p, opts); + }, async (zipFs, { + subPath + }) => { + return await zipFs.rmdirPromise(subPath, opts); + }); + } + + rmdirSync(p, opts) { + return this.makeCallSync(p, () => { + return this.baseFs.rmdirSync(p, opts); + }, (zipFs, { + subPath + }) => { + return zipFs.rmdirSync(subPath, opts); + }); + } + + async linkPromise(existingP, newP) { + return await this.makeCallPromise(newP, async () => { + return await this.baseFs.linkPromise(existingP, newP); + }, async (zipFs, { + subPath + }) => { + return await zipFs.linkPromise(existingP, subPath); + }); + } + + linkSync(existingP, newP) { + return this.makeCallSync(newP, () => { + return this.baseFs.linkSync(existingP, newP); + }, (zipFs, { + subPath + }) => { + return zipFs.linkSync(existingP, subPath); + }); + } + + async symlinkPromise(target, p, type) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.symlinkPromise(target, p, type); + }, async (zipFs, { + subPath + }) => { + return await zipFs.symlinkPromise(target, subPath); + }); + } + + symlinkSync(target, p, type) { + return this.makeCallSync(p, () => { + return this.baseFs.symlinkSync(target, p, type); + }, (zipFs, { + subPath + }) => { + return zipFs.symlinkSync(target, subPath); + }); + } + + async readFilePromise(p, encoding) { + return this.makeCallPromise(p, async () => { + // This weird switch is required to tell TypeScript that the signatures are proper (otherwise it thinks that only the generic one is covered) + switch (encoding) { + case `utf8`: + return await this.baseFs.readFilePromise(p, encoding); + + default: + return await this.baseFs.readFilePromise(p, encoding); + } + }, async (zipFs, { + subPath + }) => { + return await zipFs.readFilePromise(subPath, encoding); + }); + } + + readFileSync(p, encoding) { + return this.makeCallSync(p, () => { + // This weird switch is required to tell TypeScript that the signatures are proper (otherwise it thinks that only the generic one is covered) + switch (encoding) { + case `utf8`: + return this.baseFs.readFileSync(p, encoding); + + default: + return this.baseFs.readFileSync(p, encoding); + } + }, (zipFs, { + subPath + }) => { + return zipFs.readFileSync(subPath, encoding); + }); + } + + async readdirPromise(p, opts) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.readdirPromise(p, opts); + }, async (zipFs, { + subPath + }) => { + return await zipFs.readdirPromise(subPath, opts); + }, { + requireSubpath: false + }); + } + + readdirSync(p, opts) { + return this.makeCallSync(p, () => { + return this.baseFs.readdirSync(p, opts); + }, (zipFs, { + subPath + }) => { + return zipFs.readdirSync(subPath, opts); + }, { + requireSubpath: false + }); + } + + async readlinkPromise(p) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.readlinkPromise(p); + }, async (zipFs, { + subPath + }) => { + return await zipFs.readlinkPromise(subPath); + }); + } + + readlinkSync(p) { + return this.makeCallSync(p, () => { + return this.baseFs.readlinkSync(p); + }, (zipFs, { + subPath + }) => { + return zipFs.readlinkSync(subPath); + }); + } + + async truncatePromise(p, len) { + return await this.makeCallPromise(p, async () => { + return await this.baseFs.truncatePromise(p, len); + }, async (zipFs, { + subPath + }) => { + return await zipFs.truncatePromise(subPath, len); + }); + } + + truncateSync(p, len) { + return this.makeCallSync(p, () => { + return this.baseFs.truncateSync(p, len); + }, (zipFs, { + subPath + }) => { + return zipFs.truncateSync(subPath, len); + }); + } + + watch(p, a, b) { + return this.makeCallSync(p, () => { + return this.baseFs.watch(p, // @ts-expect-error + a, b); + }, (zipFs, { + subPath + }) => { + return zipFs.watch(subPath, // @ts-expect-error + a, b); + }); + } + + watchFile(p, a, b) { + return this.makeCallSync(p, () => { + return this.baseFs.watchFile(p, // @ts-expect-error + a, b); + }, () => { + return watchFile(this, p, a, b); + }); + } + + unwatchFile(p, cb) { + return this.makeCallSync(p, () => { + return this.baseFs.unwatchFile(p, cb); + }, () => { + return unwatchFile(this, p, cb); + }); + } + + async makeCallPromise(p, discard, accept, { + requireSubpath = true + } = {}) { + if (typeof p !== `string`) return await discard(); + const normalizedP = this.resolve(p); + const zipInfo = this.findZip(normalizedP); + if (!zipInfo) return await discard(); + if (requireSubpath && zipInfo.subPath === `/`) return await discard(); + return await this.getZipPromise(zipInfo.archivePath, async zipFs => await accept(zipFs, zipInfo)); + } + + makeCallSync(p, discard, accept, { + requireSubpath = true + } = {}) { + if (typeof p !== `string`) return discard(); + const normalizedP = this.resolve(p); + const zipInfo = this.findZip(normalizedP); + if (!zipInfo) return discard(); + if (requireSubpath && zipInfo.subPath === `/`) return discard(); + return this.getZipSync(zipInfo.archivePath, zipFs => accept(zipFs, zipInfo)); + } + + findZip(p) { + if (this.filter && !this.filter.test(p)) return null; + let filePath = ``; + + while (true) { + const pathPartWithArchive = p.substr(filePath.length); + let archivePart; + + if (!this.fileExtensions) { + archivePart = getArchivePart(pathPartWithArchive, `.zip`); + } else { + for (const ext of this.fileExtensions) { + archivePart = getArchivePart(pathPartWithArchive, ext); + + if (archivePart) { + break; + } + } + } + + if (!archivePart) return null; + filePath = this.pathUtils.join(filePath, archivePart); + + if (this.isZip.has(filePath) === false) { + if (this.notZip.has(filePath)) continue; + + try { + if (!this.baseFs.lstatSync(filePath).isFile()) { + this.notZip.add(filePath); + continue; + } + } catch { + return null; + } + + this.isZip.add(filePath); + } + + return { + archivePath: filePath, + subPath: this.pathUtils.join(PortablePath.root, p.substr(filePath.length)) + }; + } + } + + limitOpenFiles(max) { + if (this.zipInstances === null) return; + const now = Date.now(); + let nextExpiresAt = now + this.maxAge; + let closeCount = max === null ? 0 : this.zipInstances.size - max; + + for (const [path, { + zipFs, + expiresAt, + refCount + }] of this.zipInstances.entries()) { + if (refCount !== 0 || zipFs.hasOpenFileHandles()) { + continue; + } else if (now >= expiresAt) { + zipFs.saveAndClose(); + this.zipInstances.delete(path); + closeCount -= 1; + continue; + } else if (max === null || closeCount <= 0) { + nextExpiresAt = expiresAt; + break; + } + + zipFs.saveAndClose(); + this.zipInstances.delete(path); + closeCount -= 1; + } + + if (this.limitOpenFilesTimeout === null && (max === null && this.zipInstances.size > 0 || max !== null)) { + this.limitOpenFilesTimeout = setTimeout(() => { + this.limitOpenFilesTimeout = null; + this.limitOpenFiles(null); + }, nextExpiresAt - now).unref(); + } + } + + async getZipPromise(p, accept) { + const getZipOptions = async () => ({ + baseFs: this.baseFs, + libzip: this.libzip, + readOnly: this.readOnlyArchives, + stats: await this.baseFs.statPromise(p) + }); + + if (this.zipInstances) { + let cachedZipFs = this.zipInstances.get(p); + + if (!cachedZipFs) { + const zipOptions = await getZipOptions(); // We need to recheck because concurrent getZipPromise calls may + // have instantiated the zip archive while we were waiting + + cachedZipFs = this.zipInstances.get(p); + + if (!cachedZipFs) { + cachedZipFs = { + zipFs: new ZipFS(p, zipOptions), + expiresAt: 0, + refCount: 0 + }; + } + } // Removing then re-adding the field allows us to easily implement + // a basic LRU garbage collection strategy + + + this.zipInstances.delete(p); + this.limitOpenFiles(this.maxOpenFiles - 1); + this.zipInstances.set(p, cachedZipFs); + cachedZipFs.expiresAt = Date.now() + this.maxAge; + cachedZipFs.refCount += 1; + + try { + return await accept(cachedZipFs.zipFs); + } finally { + cachedZipFs.refCount -= 1; + } + } else { + const zipFs = new ZipFS(p, await getZipOptions()); + + try { + return await accept(zipFs); + } finally { + zipFs.saveAndClose(); + } + } + } + + getZipSync(p, accept) { + const getZipOptions = () => ({ + baseFs: this.baseFs, + libzip: this.libzip, + readOnly: this.readOnlyArchives, + stats: this.baseFs.statSync(p) + }); + + if (this.zipInstances) { + let cachedZipFs = this.zipInstances.get(p); + + if (!cachedZipFs) { + cachedZipFs = { + zipFs: new ZipFS(p, getZipOptions()), + expiresAt: 0, + refCount: 0 + }; + } // Removing then re-adding the field allows us to easily implement + // a basic LRU garbage collection strategy + + + this.zipInstances.delete(p); + this.limitOpenFiles(this.maxOpenFiles - 1); + this.zipInstances.set(p, cachedZipFs); + cachedZipFs.expiresAt = Date.now() + this.maxAge; + return accept(cachedZipFs.zipFs); + } else { + const zipFs = new ZipFS(p, getZipOptions()); + + try { + return accept(zipFs); + } finally { + zipFs.saveAndClose(); + } + } + } + +} +;// CONCATENATED MODULE: ../yarnpkg-libzip/sources/makeInterface.ts +const number64 = [`number`, `number` // high +]; +var Errors; + +(function (Errors) { + Errors[Errors["ZIP_ER_OK"] = 0] = "ZIP_ER_OK"; + Errors[Errors["ZIP_ER_MULTIDISK"] = 1] = "ZIP_ER_MULTIDISK"; + Errors[Errors["ZIP_ER_RENAME"] = 2] = "ZIP_ER_RENAME"; + Errors[Errors["ZIP_ER_CLOSE"] = 3] = "ZIP_ER_CLOSE"; + Errors[Errors["ZIP_ER_SEEK"] = 4] = "ZIP_ER_SEEK"; + Errors[Errors["ZIP_ER_READ"] = 5] = "ZIP_ER_READ"; + Errors[Errors["ZIP_ER_WRITE"] = 6] = "ZIP_ER_WRITE"; + Errors[Errors["ZIP_ER_CRC"] = 7] = "ZIP_ER_CRC"; + Errors[Errors["ZIP_ER_ZIPCLOSED"] = 8] = "ZIP_ER_ZIPCLOSED"; + Errors[Errors["ZIP_ER_NOENT"] = 9] = "ZIP_ER_NOENT"; + Errors[Errors["ZIP_ER_EXISTS"] = 10] = "ZIP_ER_EXISTS"; + Errors[Errors["ZIP_ER_OPEN"] = 11] = "ZIP_ER_OPEN"; + Errors[Errors["ZIP_ER_TMPOPEN"] = 12] = "ZIP_ER_TMPOPEN"; + Errors[Errors["ZIP_ER_ZLIB"] = 13] = "ZIP_ER_ZLIB"; + Errors[Errors["ZIP_ER_MEMORY"] = 14] = "ZIP_ER_MEMORY"; + Errors[Errors["ZIP_ER_CHANGED"] = 15] = "ZIP_ER_CHANGED"; + Errors[Errors["ZIP_ER_COMPNOTSUPP"] = 16] = "ZIP_ER_COMPNOTSUPP"; + Errors[Errors["ZIP_ER_EOF"] = 17] = "ZIP_ER_EOF"; + Errors[Errors["ZIP_ER_INVAL"] = 18] = "ZIP_ER_INVAL"; + Errors[Errors["ZIP_ER_NOZIP"] = 19] = "ZIP_ER_NOZIP"; + Errors[Errors["ZIP_ER_INTERNAL"] = 20] = "ZIP_ER_INTERNAL"; + Errors[Errors["ZIP_ER_INCONS"] = 21] = "ZIP_ER_INCONS"; + Errors[Errors["ZIP_ER_REMOVE"] = 22] = "ZIP_ER_REMOVE"; + Errors[Errors["ZIP_ER_DELETED"] = 23] = "ZIP_ER_DELETED"; + Errors[Errors["ZIP_ER_ENCRNOTSUPP"] = 24] = "ZIP_ER_ENCRNOTSUPP"; + Errors[Errors["ZIP_ER_RDONLY"] = 25] = "ZIP_ER_RDONLY"; + Errors[Errors["ZIP_ER_NOPASSWD"] = 26] = "ZIP_ER_NOPASSWD"; + Errors[Errors["ZIP_ER_WRONGPASSWD"] = 27] = "ZIP_ER_WRONGPASSWD"; + Errors[Errors["ZIP_ER_OPNOTSUPP"] = 28] = "ZIP_ER_OPNOTSUPP"; + Errors[Errors["ZIP_ER_INUSE"] = 29] = "ZIP_ER_INUSE"; + Errors[Errors["ZIP_ER_TELL"] = 30] = "ZIP_ER_TELL"; + Errors[Errors["ZIP_ER_COMPRESSED_DATA"] = 31] = "ZIP_ER_COMPRESSED_DATA"; +})(Errors || (Errors = {})); + +const makeInterface = libzip => ({ + // Those are getters because they can change after memory growth + get HEAP8() { + return libzip.HEAP8; + }, + + get HEAPU8() { + return libzip.HEAPU8; + }, + + errors: Errors, + SEEK_SET: 0, + SEEK_CUR: 1, + SEEK_END: 2, + ZIP_CHECKCONS: 4, + ZIP_CREATE: 1, + ZIP_EXCL: 2, + ZIP_TRUNCATE: 8, + ZIP_RDONLY: 16, + ZIP_FL_OVERWRITE: 8192, + ZIP_FL_COMPRESSED: 4, + ZIP_OPSYS_DOS: 0x00, + ZIP_OPSYS_AMIGA: 0x01, + ZIP_OPSYS_OPENVMS: 0x02, + ZIP_OPSYS_UNIX: 0x03, + ZIP_OPSYS_VM_CMS: 0x04, + ZIP_OPSYS_ATARI_ST: 0x05, + ZIP_OPSYS_OS_2: 0x06, + ZIP_OPSYS_MACINTOSH: 0x07, + ZIP_OPSYS_Z_SYSTEM: 0x08, + ZIP_OPSYS_CPM: 0x09, + ZIP_OPSYS_WINDOWS_NTFS: 0x0a, + ZIP_OPSYS_MVS: 0x0b, + ZIP_OPSYS_VSE: 0x0c, + ZIP_OPSYS_ACORN_RISC: 0x0d, + ZIP_OPSYS_VFAT: 0x0e, + ZIP_OPSYS_ALTERNATE_MVS: 0x0f, + ZIP_OPSYS_BEOS: 0x10, + ZIP_OPSYS_TANDEM: 0x11, + ZIP_OPSYS_OS_400: 0x12, + ZIP_OPSYS_OS_X: 0x13, + ZIP_CM_DEFAULT: -1, + ZIP_CM_STORE: 0, + ZIP_CM_DEFLATE: 8, + uint08S: libzip._malloc(1), + uint16S: libzip._malloc(2), + uint32S: libzip._malloc(4), + uint64S: libzip._malloc(8), + malloc: libzip._malloc, + free: libzip._free, + getValue: libzip.getValue, + open: libzip.cwrap(`zip_open`, `number`, [`string`, `number`, `number`]), + openFromSource: libzip.cwrap(`zip_open_from_source`, `number`, [`number`, `number`, `number`]), + close: libzip.cwrap(`zip_close`, `number`, [`number`]), + discard: libzip.cwrap(`zip_discard`, null, [`number`]), + getError: libzip.cwrap(`zip_get_error`, `number`, [`number`]), + getName: libzip.cwrap(`zip_get_name`, `string`, [`number`, `number`, `number`]), + getNumEntries: libzip.cwrap(`zip_get_num_entries`, `number`, [`number`, `number`]), + delete: libzip.cwrap(`zip_delete`, `number`, [`number`, `number`]), + stat: libzip.cwrap(`zip_stat`, `number`, [`number`, `string`, `number`, `number`]), + statIndex: libzip.cwrap(`zip_stat_index`, `number`, [`number`, ...number64, `number`, `number`]), + fopen: libzip.cwrap(`zip_fopen`, `number`, [`number`, `string`, `number`]), + fopenIndex: libzip.cwrap(`zip_fopen_index`, `number`, [`number`, ...number64, `number`]), + fread: libzip.cwrap(`zip_fread`, `number`, [`number`, `number`, `number`, `number`]), + fclose: libzip.cwrap(`zip_fclose`, `number`, [`number`]), + dir: { + add: libzip.cwrap(`zip_dir_add`, `number`, [`number`, `string`]) + }, + file: { + add: libzip.cwrap(`zip_file_add`, `number`, [`number`, `string`, `number`, `number`]), + getError: libzip.cwrap(`zip_file_get_error`, `number`, [`number`]), + getExternalAttributes: libzip.cwrap(`zip_file_get_external_attributes`, `number`, [`number`, ...number64, `number`, `number`, `number`]), + setExternalAttributes: libzip.cwrap(`zip_file_set_external_attributes`, `number`, [`number`, ...number64, `number`, `number`, `number`]), + setMtime: libzip.cwrap(`zip_file_set_mtime`, `number`, [`number`, ...number64, `number`, `number`]), + setCompression: libzip.cwrap(`zip_set_file_compression`, `number`, [`number`, ...number64, `number`, `number`]) + }, + ext: { + countSymlinks: libzip.cwrap(`zip_ext_count_symlinks`, `number`, [`number`]) + }, + error: { + initWithCode: libzip.cwrap(`zip_error_init_with_code`, null, [`number`, `number`]), + strerror: libzip.cwrap(`zip_error_strerror`, `string`, [`number`]) + }, + name: { + locate: libzip.cwrap(`zip_name_locate`, `number`, [`number`, `string`, `number`]) + }, + source: { + fromUnattachedBuffer: libzip.cwrap(`zip_source_buffer_create`, `number`, [`number`, `number`, `number`, `number`]), + fromBuffer: libzip.cwrap(`zip_source_buffer`, `number`, [`number`, `number`, ...number64, `number`]), + free: libzip.cwrap(`zip_source_free`, null, [`number`]), + keep: libzip.cwrap(`zip_source_keep`, null, [`number`]), + open: libzip.cwrap(`zip_source_open`, `number`, [`number`]), + close: libzip.cwrap(`zip_source_close`, `number`, [`number`]), + seek: libzip.cwrap(`zip_source_seek`, `number`, [`number`, ...number64, `number`]), + tell: libzip.cwrap(`zip_source_tell`, `number`, [`number`]), + read: libzip.cwrap(`zip_source_read`, `number`, [`number`, `number`, `number`]), + error: libzip.cwrap(`zip_source_error`, `number`, [`number`]), + setMtime: libzip.cwrap(`zip_source_set_mtime`, `number`, [`number`, `number`]) + }, + struct: { + stat: libzip.cwrap(`zipstruct_stat`, `number`, []), + statS: libzip.cwrap(`zipstruct_statS`, `number`, []), + statName: libzip.cwrap(`zipstruct_stat_name`, `string`, [`number`]), + statIndex: libzip.cwrap(`zipstruct_stat_index`, `number`, [`number`]), + statSize: libzip.cwrap(`zipstruct_stat_size`, `number`, [`number`]), + statCompSize: libzip.cwrap(`zipstruct_stat_comp_size`, `number`, [`number`]), + statCompMethod: libzip.cwrap(`zipstruct_stat_comp_method`, `number`, [`number`]), + statMtime: libzip.cwrap(`zipstruct_stat_mtime`, `number`, [`number`]), + statCrc: libzip.cwrap(`zipstruct_stat_crc`, `number`, [`number`]), + error: libzip.cwrap(`zipstruct_error`, `number`, []), + errorS: libzip.cwrap(`zipstruct_errorS`, `number`, []), + errorCodeZip: libzip.cwrap(`zipstruct_error_code_zip`, `number`, [`number`]) + } +}); +;// CONCATENATED MODULE: ../yarnpkg-libzip/sources/sync.ts + +let mod = null; +function getLibzipSync() { + if (mod === null) mod = makeInterface(__webpack_require__(368)); + return mod; +} +async function getLibzipPromise() { + return getLibzipSync(); +} +// EXTERNAL MODULE: external "module" +var external_module_ = __webpack_require__(282); +var external_module_default = /*#__PURE__*/__webpack_require__.n(external_module_); +;// CONCATENATED MODULE: external "string_decoder" +const external_string_decoder_namespaceObject = require("string_decoder");; +var external_string_decoder_default = /*#__PURE__*/__webpack_require__.n(external_string_decoder_namespaceObject); +;// CONCATENATED MODULE: external "url" +const external_url_namespaceObject = require("url");; +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/URLFS.ts + + + +/** + * Adds support for file URLs to the wrapped `baseFs`, but *not* inside the typings. + * + * Only exists for compatibility with Node's behavior. + * + * Automatically wraps all FS instances passed to `patchFs` & `extendFs`. + * + * Don't use it! + */ + +class URLFS extends ProxiedFS { + constructor(baseFs) { + super(npath); + this.baseFs = baseFs; + } + + mapFromBase(path) { + return path; + } + + mapToBase(path) { + if (path instanceof external_url_namespaceObject.URL) return (0,external_url_namespaceObject.fileURLToPath)(path); + return path; + } + +} +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/patchFs.ts + + +const SYNC_IMPLEMENTATIONS = new Set([`accessSync`, `appendFileSync`, `createReadStream`, `createWriteStream`, `chmodSync`, `chownSync`, `closeSync`, `copyFileSync`, `linkSync`, `lstatSync`, `fstatSync`, `lutimesSync`, `mkdirSync`, `openSync`, `opendirSync`, `readSync`, `readlinkSync`, `readFileSync`, `readdirSync`, `readlinkSync`, `realpathSync`, `renameSync`, `rmdirSync`, `statSync`, `symlinkSync`, `truncateSync`, `unlinkSync`, `unwatchFile`, `utimesSync`, `watch`, `watchFile`, `writeFileSync`, `writeSync`]); +const ASYNC_IMPLEMENTATIONS = new Set([`accessPromise`, `appendFilePromise`, `chmodPromise`, `chownPromise`, `closePromise`, `copyFilePromise`, `linkPromise`, `fstatPromise`, `lstatPromise`, `lutimesPromise`, `mkdirPromise`, `openPromise`, `opendirPromise`, `readdirPromise`, `realpathPromise`, `readFilePromise`, `readdirPromise`, `readlinkPromise`, `renamePromise`, `rmdirPromise`, `statPromise`, `symlinkPromise`, `truncatePromise`, `unlinkPromise`, `utimesPromise`, `writeFilePromise`, `writeSync`]); +const FILEHANDLE_IMPLEMENTATIONS = new Set([`appendFilePromise`, `chmodPromise`, `chownPromise`, `closePromise`, `readPromise`, `readFilePromise`, `statPromise`, `truncatePromise`, `utimesPromise`, `writePromise`, `writeFilePromise`]); +function patchFs(patchedFs, fakeFs) { + // We wrap the `fakeFs` with a `URLFS` to add support for URL instances + fakeFs = new URLFS(fakeFs); + + const setupFn = (target, name, replacement) => { + const orig = target[name]; + target[name] = replacement; // Preserve any util.promisify implementations + + if (typeof (orig === null || orig === void 0 ? void 0 : orig[external_util_namespaceObject.promisify.custom]) !== `undefined`) { + replacement[external_util_namespaceObject.promisify.custom] = orig[external_util_namespaceObject.promisify.custom]; + } + }; + /** Callback implementations */ + + + { + setupFn(patchedFs, `exists`, (p, ...args) => { + const hasCallback = typeof args[args.length - 1] === `function`; + const callback = hasCallback ? args.pop() : () => {}; + process.nextTick(() => { + fakeFs.existsPromise(p).then(exists => { + callback(exists); + }, () => { + callback(false); + }); + }); + }); + setupFn(patchedFs, `read`, (p, buffer, ...args) => { + const hasCallback = typeof args[args.length - 1] === `function`; + const callback = hasCallback ? args.pop() : () => {}; + process.nextTick(() => { + fakeFs.readPromise(p, buffer, ...args).then(bytesRead => { + callback(null, bytesRead, buffer); + }, error => { + // https://github.com/nodejs/node/blob/1317252dfe8824fd9cfee125d2aaa94004db2f3b/lib/fs.js#L655-L658 + // Known issue: bytesRead could theoretically be > than 0, but we currently always return 0 + callback(error, 0, buffer); + }); + }); + }); + + for (const fnName of ASYNC_IMPLEMENTATIONS) { + const origName = fnName.replace(/Promise$/, ``); + if (typeof patchedFs[origName] === `undefined`) continue; + const fakeImpl = fakeFs[fnName]; + if (typeof fakeImpl === `undefined`) continue; + + const wrapper = (...args) => { + const hasCallback = typeof args[args.length - 1] === `function`; + const callback = hasCallback ? args.pop() : () => {}; + process.nextTick(() => { + fakeImpl.apply(fakeFs, args).then(result => { + callback(null, result); + }, error => { + callback(error); + }); + }); + }; + + setupFn(patchedFs, origName, wrapper); + } + + patchedFs.realpath.native = patchedFs.realpath; + } + /** Sync implementations */ + + { + setupFn(patchedFs, `existsSync`, p => { + try { + return fakeFs.existsSync(p); + } catch (error) { + return false; + } + }); + + for (const fnName of SYNC_IMPLEMENTATIONS) { + const origName = fnName; + if (typeof patchedFs[origName] === `undefined`) continue; + const fakeImpl = fakeFs[fnName]; + if (typeof fakeImpl === `undefined`) continue; + setupFn(patchedFs, origName, fakeImpl.bind(fakeFs)); + } + + patchedFs.realpathSync.native = patchedFs.realpathSync; + } + /** Promise implementations */ + + { + // `fs.promises` is a getter that returns a reference to require(`fs/promises`), + // so we can just patch `fs.promises` and both will be updated + const origEmitWarning = process.emitWarning; + + process.emitWarning = () => {}; + + let patchedFsPromises; + + try { + patchedFsPromises = patchedFs.promises; + } finally { + process.emitWarning = origEmitWarning; + } + + if (typeof patchedFsPromises !== `undefined`) { + // `fs.promises.exists` doesn't exist + for (const fnName of ASYNC_IMPLEMENTATIONS) { + const origName = fnName.replace(/Promise$/, ``); + if (typeof patchedFsPromises[origName] === `undefined`) continue; + const fakeImpl = fakeFs[fnName]; + if (typeof fakeImpl === `undefined`) continue; // Open is a bit particular with fs.promises: it returns a file handle + // instance instead of the traditional file descriptor number + + if (fnName === `open`) continue; + setupFn(patchedFsPromises, origName, fakeImpl.bind(fakeFs)); + } + + class FileHandle { + constructor(fd) { + this.fd = fd; + } + + } + + for (const fnName of FILEHANDLE_IMPLEMENTATIONS) { + const origName = fnName.replace(/Promise$/, ``); + const fakeImpl = fakeFs[fnName]; + if (typeof fakeImpl === `undefined`) continue; + setupFn(FileHandle.prototype, origName, function (...args) { + return fakeImpl.call(fakeFs, this.fd, ...args); + }); + } + + setupFn(patchedFsPromises, `open`, async (...args) => { + // @ts-expect-error + const fd = await fakeFs.openPromise(...args); + return new FileHandle(fd); + }); // `fs.promises.realpath` doesn't have a `native` property + } + } + /** util.promisify implementations */ + + { + // Override the promisified version of `fs.read` to return an object as per + // https://github.com/nodejs/node/blob/dc79f3f37caf6f25b8efee4623bec31e2c20f595/lib/fs.js#L559-L560 + // and + // https://github.com/nodejs/node/blob/ba684805b6c0eded76e5cd89ee00328ac7a59365/lib/internal/util.js#L293 + // @ts-expect-error + patchedFs.read[external_util_namespaceObject.promisify.custom] = async (p, buffer, ...args) => { + const res = fakeFs.readPromise(p, buffer, ...args); + return { + bytesRead: await res, + buffer + }; + }; + } +} +function extendFs(realFs, fakeFs) { + const patchedFs = Object.create(realFs); + patchFs(patchedFs, fakeFs); + return patchedFs; +} +;// CONCATENATED MODULE: ../yarnpkg-fslib/sources/PosixFS.ts + + +class PosixFS extends ProxiedFS { + constructor(baseFs) { + super(npath); + this.baseFs = baseFs; + } + + mapFromBase(path) { + return npath.fromPortablePath(path); + } + + mapToBase(path) { + return npath.toPortablePath(path); + } + +} +;// CONCATENATED MODULE: ./sources/loader/internalTools.ts + +var ErrorCode; + +(function (ErrorCode) { + ErrorCode["API_ERROR"] = "API_ERROR"; + ErrorCode["BUILTIN_NODE_RESOLUTION_FAILED"] = "BUILTIN_NODE_RESOLUTION_FAILED"; + ErrorCode["MISSING_DEPENDENCY"] = "MISSING_DEPENDENCY"; + ErrorCode["MISSING_PEER_DEPENDENCY"] = "MISSING_PEER_DEPENDENCY"; + ErrorCode["QUALIFIED_PATH_RESOLUTION_FAILED"] = "QUALIFIED_PATH_RESOLUTION_FAILED"; + ErrorCode["INTERNAL"] = "INTERNAL"; + ErrorCode["UNDECLARED_DEPENDENCY"] = "UNDECLARED_DEPENDENCY"; + ErrorCode["UNSUPPORTED"] = "UNSUPPORTED"; +})(ErrorCode || (ErrorCode = {})); // Some errors are exposed as MODULE_NOT_FOUND for compatibility with packages +// that expect this umbrella error when the resolution fails + + +const MODULE_NOT_FOUND_ERRORS = new Set([ErrorCode.BUILTIN_NODE_RESOLUTION_FAILED, ErrorCode.MISSING_DEPENDENCY, ErrorCode.MISSING_PEER_DEPENDENCY, ErrorCode.QUALIFIED_PATH_RESOLUTION_FAILED, ErrorCode.UNDECLARED_DEPENDENCY]); +/** + * Simple helper function that assign an error code to an error, so that it can more easily be caught and used + * by third-parties. + */ + +function internalTools_makeError(pnpCode, message, data = {}) { + const code = MODULE_NOT_FOUND_ERRORS.has(pnpCode) ? `MODULE_NOT_FOUND` : pnpCode; + const propertySpec = { + configurable: true, + writable: true, + enumerable: false + }; + return Object.defineProperties(new Error(message), { + code: { ...propertySpec, + value: code + }, + pnpCode: { ...propertySpec, + value: pnpCode + }, + data: { ...propertySpec, + value: data + } + }); +} +/** + * Returns the module that should be used to resolve require calls. It's usually the direct parent, except if we're + * inside an eval expression. + */ + +function getIssuerModule(parent) { + let issuer = parent; + + while (issuer && (issuer.id === `[eval]` || issuer.id === `` || !issuer.filename)) issuer = issuer.parent; + + return issuer || null; +} +function getPathForDisplay(p) { + return npath.normalize(npath.fromPortablePath(p)); +} +;// CONCATENATED MODULE: ./sources/loader/nodeUtils.ts + + + // @ts-expect-error + +const builtinModules = new Set(external_module_.Module.builtinModules || Object.keys(process.binding(`natives`))); +const isBuiltinModule = request => request.startsWith(`node:`) || builtinModules.has(request); // https://github.com/nodejs/node/blob/e817ba70f56c4bfd5d4a68dce8b165142312e7b6/lib/internal/modules/run_main.js#L11-L24 + +function resolveMainPath(main) { + let mainPath = external_module_.Module._findPath(npath.resolve(main), null, true); + + if (!mainPath) return false; // const preserveSymlinksMain = getOptionValue(`--preserve-symlinks-main`); + // if (!preserveSymlinksMain) + + mainPath = external_fs_default().realpathSync(mainPath); + return mainPath; +} // https://github.com/nodejs/node/blob/e817ba70f56c4bfd5d4a68dce8b165142312e7b6/lib/internal/modules/run_main.js#L26-L41 + +function shouldUseESMLoader(mainPath) { + // const userLoader = getOptionValue(`--experimental-loader`); + // if (userLoader) + // return true; + // const esModuleSpecifierResolution = + // getOptionValue(`--experimental-specifier-resolution`); + // if (esModuleSpecifierResolution === `node`) + // return true; + // Determine the module format of the main + if (mainPath && mainPath.endsWith(`.mjs`)) return true; + if (!mainPath || mainPath.endsWith(`.cjs`)) return false; + const pkg = readPackageScope(mainPath); + return pkg && pkg.data.type === `module`; +} // https://github.com/nodejs/node/blob/e817ba70f56c4bfd5d4a68dce8b165142312e7b6/lib/internal/modules/cjs/loader.js#L315-L330 + +function readPackageScope(checkPath) { + const rootSeparatorIndex = checkPath.indexOf(npath.sep); + let separatorIndex; + + do { + separatorIndex = checkPath.lastIndexOf(npath.sep); + checkPath = checkPath.slice(0, separatorIndex); + if (checkPath.endsWith(`${npath.sep}node_modules`)) return false; + const pjson = readPackage(checkPath + npath.sep); + + if (pjson) { + return { + data: pjson, + path: checkPath + }; + } + } while (separatorIndex > rootSeparatorIndex); + + return false; +} // https://github.com/nodejs/node/blob/e817ba70f56c4bfd5d4a68dce8b165142312e7b6/lib/internal/modules/cjs/loader.js#L284-L313 + +function readPackage(requestPath) { + const jsonPath = npath.resolve(requestPath, `package.json`); + if (!external_fs_default().existsSync(jsonPath)) return null; + return JSON.parse(external_fs_default().readFileSync(jsonPath, `utf8`)); +} +;// CONCATENATED MODULE: ./sources/loader/applyPatch.ts + + + + + + +function applyPatch(pnpapi, opts) { + /** + * The cache that will be used for all accesses occurring outside of a PnP context. + */ + const defaultCache = {}; + /** + * Used to disable the resolution hooks (for when we want to fallback to the previous resolution - we then need + * a way to "reset" the environment temporarily) + */ + + let enableNativeHooks = true; // @ts-expect-error + + process.versions.pnp = String(pnpapi.VERSIONS.std); + + const moduleExports = __webpack_require__(282); + + moduleExports.findPnpApi = lookupSource => { + const lookupPath = lookupSource instanceof external_url_namespaceObject.URL ? (0,external_url_namespaceObject.fileURLToPath)(lookupSource) : lookupSource; + const apiPath = opts.manager.findApiPathFor(lookupPath); + if (apiPath === null) return null; + const apiEntry = opts.manager.getApiEntry(apiPath, true); // Check if the path is ignored + + return apiEntry.instance.findPackageLocator(lookupPath) ? apiEntry.instance : null; + }; + + function getRequireStack(parent) { + const requireStack = []; + + for (let cursor = parent; cursor; cursor = cursor.parent) requireStack.push(cursor.filename || cursor.id); + + return requireStack; + } // A small note: we don't replace the cache here (and instead use the native one). This is an effort to not + // break code similar to "delete require.cache[require.resolve(FOO)]", where FOO is a package located outside + // of the Yarn dependency tree. In this case, we defer the load to the native loader. If we were to replace the + // cache by our own, the native loader would populate its own cache, which wouldn't be exposed anymore, so the + // delete call would be broken. + + + const originalModuleLoad = external_module_.Module._load; + + external_module_.Module._load = function (request, parent, isMain) { + if (!enableNativeHooks) return originalModuleLoad.call(external_module_.Module, request, parent, isMain); // Builtins are managed by the regular Node loader + + if (isBuiltinModule(request)) { + try { + enableNativeHooks = false; + return originalModuleLoad.call(external_module_.Module, request, parent, isMain); + } finally { + enableNativeHooks = true; + } + } + + const parentApiPath = opts.manager.getApiPathFromParent(parent); + const parentApi = parentApiPath !== null ? opts.manager.getApiEntry(parentApiPath, true).instance : null; // Requests that aren't covered by the PnP runtime goes through the + // parent `_load` implementation. This is required for VSCode, for example, + // which override `_load` to provide additional builtins to its extensions. + + if (parentApi === null) return originalModuleLoad(request, parent, isMain); // The 'pnpapi' name is reserved to return the PnP api currently in use + // by the program + + if (request === `pnpapi`) return parentApi; // Request `Module._resolveFilename` (ie. `resolveRequest`) to tell us + // which file we should load + + const modulePath = external_module_.Module._resolveFilename(request, parent, isMain); // We check whether the module is owned by the dependency tree of the + // module that required it. If it isn't, then we need to create a new + // store and possibly load its sandboxed PnP runtime. + + + const isOwnedByRuntime = parentApi !== null ? parentApi.findPackageLocator(modulePath) !== null : false; + const moduleApiPath = isOwnedByRuntime ? parentApiPath : opts.manager.findApiPathFor(npath.dirname(modulePath)); + const entry = moduleApiPath !== null ? opts.manager.getApiEntry(moduleApiPath) : { + instance: null, + cache: defaultCache + }; // Check if the module has already been created for the given file + + const cacheEntry = entry.cache[modulePath]; + + if (cacheEntry) { + // When a dynamic import is used in CJS files Node adds the module + // to the cache but doesn't load it so we do it here. + // + // Keep track of and check if the module is already loading to + // handle circular requires. + // + // The explicit checks are required since `@babel/register` et al. + // create modules without the `loaded` and `load` properties + if (cacheEntry.loaded === false && cacheEntry.isLoading !== true) { + try { + cacheEntry.isLoading = true; + cacheEntry.load(modulePath); + } finally { + cacheEntry.isLoading = false; + } + } + + return cacheEntry.exports; + } // Create a new module and store it into the cache + + + const module = new external_module_.Module(modulePath, parent !== null && parent !== void 0 ? parent : undefined); + module.pnpApiPath = moduleApiPath; + entry.cache[modulePath] = module; // The main module is exposed as global variable + + if (isMain) { + process.mainModule = module; + module.id = `.`; + } // Try to load the module, and remove it from the cache if it fails + + + let hasThrown = true; + + try { + module.isLoading = true; + module.load(modulePath); + hasThrown = false; + } finally { + module.isLoading = false; + + if (hasThrown) { + delete external_module_.Module._cache[modulePath]; + } + } + + return module.exports; + }; + + function getIssuerSpecsFromPaths(paths) { + return paths.map(path => ({ + apiPath: opts.manager.findApiPathFor(path), + path, + module: null + })); + } + + function getIssuerSpecsFromModule(module) { + var _a; + + if (module && module.id !== `` && module.id !== `internal/preload` && !module.parent && !module.filename && module.paths.length > 0) { + return [{ + apiPath: opts.manager.findApiPathFor(module.paths[0]), + path: module.paths[0], + module + }]; + } + + const issuer = getIssuerModule(module); + + if (issuer !== null) { + const path = npath.dirname(issuer.filename); + const apiPath = opts.manager.getApiPathFromParent(issuer); + return [{ + apiPath, + path, + module + }]; + } else { + const path = process.cwd(); + const apiPath = (_a = opts.manager.findApiPathFor(npath.join(path, `[file]`))) !== null && _a !== void 0 ? _a : opts.manager.getApiPathFromParent(null); + return [{ + apiPath, + path, + module + }]; + } + } + + function makeFakeParent(path) { + const fakeParent = new external_module_.Module(``); + const fakeFilePath = npath.join(path, `[file]`); + fakeParent.paths = external_module_.Module._nodeModulePaths(fakeFilePath); + return fakeParent; + } // Splits a require request into its components, or return null if the request is a file path + + + const pathRegExp = /^(?![a-zA-Z]:[\\/]|\\\\|\.{0,2}(?:\/|$))((?:@[^/]+\/)?[^/]+)\/*(.*|)$/; + const originalModuleResolveFilename = external_module_.Module._resolveFilename; + + external_module_.Module._resolveFilename = function (request, parent, isMain, options) { + if (isBuiltinModule(request)) return request; + if (!enableNativeHooks) return originalModuleResolveFilename.call(external_module_.Module, request, parent, isMain, options); + + if (options && options.plugnplay === false) { + const { + plugnplay, + ...rest + } = options; // Workaround a bug present in some version of Node (now fixed) + // https://github.com/nodejs/node/pull/28078 + + const forwardedOptions = Object.keys(rest).length > 0 ? rest : undefined; + + try { + enableNativeHooks = false; + return originalModuleResolveFilename.call(external_module_.Module, request, parent, isMain, forwardedOptions); + } finally { + enableNativeHooks = true; + } + } // We check that all the options present here are supported; better + // to fail fast than to introduce subtle bugs in the runtime. + + + if (options) { + const optionNames = new Set(Object.keys(options)); + optionNames.delete(`paths`); + optionNames.delete(`plugnplay`); + + if (optionNames.size > 0) { + throw internalTools_makeError(ErrorCode.UNSUPPORTED, `Some options passed to require() aren't supported by PnP yet (${Array.from(optionNames).join(`, `)})`); + } + } + + const issuerSpecs = options && options.paths ? getIssuerSpecsFromPaths(options.paths) : getIssuerSpecsFromModule(parent); + + if (request.match(pathRegExp) === null) { + const parentDirectory = (parent === null || parent === void 0 ? void 0 : parent.filename) != null ? npath.dirname(parent.filename) : null; + const absoluteRequest = npath.isAbsolute(request) ? request : parentDirectory !== null ? npath.resolve(parentDirectory, request) : null; + + if (absoluteRequest !== null) { + const apiPath = parentDirectory === npath.dirname(absoluteRequest) && (parent === null || parent === void 0 ? void 0 : parent.pnpApiPath) ? parent.pnpApiPath : opts.manager.findApiPathFor(absoluteRequest); + + if (apiPath !== null) { + issuerSpecs.unshift({ + apiPath, + path: parentDirectory, + module: null + }); + } + } + } + + let firstError; + + for (const { + apiPath, + path, + module + } of issuerSpecs) { + let resolution; + const issuerApi = apiPath !== null ? opts.manager.getApiEntry(apiPath, true).instance : null; + + try { + if (issuerApi !== null) { + resolution = issuerApi.resolveRequest(request, path !== null ? `${path}/` : null); + } else { + if (path === null) throw new Error(`Assertion failed: Expected the path to be set`); + resolution = originalModuleResolveFilename.call(external_module_.Module, request, module || makeFakeParent(path), isMain); + } + } catch (error) { + firstError = firstError || error; + continue; + } + + if (resolution !== null) { + return resolution; + } + } + + const requireStack = getRequireStack(parent); + Object.defineProperty(firstError, `requireStack`, { + configurable: true, + writable: true, + enumerable: false, + value: requireStack + }); + if (requireStack.length > 0) firstError.message += `\nRequire stack:\n- ${requireStack.join(`\n- `)}`; + if (typeof firstError.pnpCode === `string`) Error.captureStackTrace(firstError); + throw firstError; + }; + + const originalFindPath = external_module_.Module._findPath; + + external_module_.Module._findPath = function (request, paths, isMain) { + if (request === `pnpapi`) return false; + if (!enableNativeHooks) return originalFindPath.call(external_module_.Module, request, paths, isMain); // https://github.com/nodejs/node/blob/e817ba70f56c4bfd5d4a68dce8b165142312e7b6/lib/internal/modules/cjs/loader.js#L490-L494 + + const isAbsolute = npath.isAbsolute(request); + if (isAbsolute) paths = [``];else if (!paths || paths.length === 0) return false; + + for (const path of paths) { + let resolution; + + try { + const pnpApiPath = opts.manager.findApiPathFor(isAbsolute ? request : path); + + if (pnpApiPath !== null) { + const api = opts.manager.getApiEntry(pnpApiPath, true).instance; + resolution = api.resolveRequest(request, path) || false; + } else { + resolution = originalFindPath.call(external_module_.Module, request, [path], isMain); + } + } catch (error) { + continue; + } + + if (resolution) { + return resolution; + } + } + + return false; + }; // Specifying the `--experimental-loader` flag makes Node enter ESM mode so we change it to not do that + // https://github.com/nodejs/node/blob/e817ba70f56c4bfd5d4a68dce8b165142312e7b6/lib/internal/modules/run_main.js#L72-L81 + // Tested by https://github.com/yarnpkg/berry/blob/d80ee2dc5298d31eb864288d77671a2264713371/packages/acceptance-tests/pkg-tests-specs/sources/pnp-esm.test.ts#L226-L244 + // Upstream issue https://github.com/nodejs/node/issues/33226 + + + const originalRunMain = moduleExports.runMain; + + moduleExports.runMain = function (main = process.argv[1]) { + const resolvedMain = resolveMainPath(main); + const useESMLoader = resolvedMain ? shouldUseESMLoader(resolvedMain) : false; + + if (useESMLoader) { + originalRunMain(main); + } else { + external_module_.Module._load(main, null, true); + } + }; + + patchFs((external_fs_default()), new PosixFS(opts.fakeFs)); +} +;// CONCATENATED MODULE: ./sources/loader/hydrateRuntimeState.ts + +function hydrateRuntimeState(data, { + basePath +}) { + const portablePath = npath.toPortablePath(basePath); + const absolutePortablePath = ppath.resolve(portablePath); + const ignorePattern = data.ignorePatternData !== null ? new RegExp(data.ignorePatternData) : null; + const packageLocatorsByLocations = new Map(); + const packageRegistry = new Map(data.packageRegistryData.map(([packageName, packageStoreData]) => { + return [packageName, new Map(packageStoreData.map(([packageReference, packageInformationData]) => { + var _a; + + if (packageName === null !== (packageReference === null)) throw new Error(`Assertion failed: The name and reference should be null, or neither should`); + const discardFromLookup = (_a = packageInformationData.discardFromLookup) !== null && _a !== void 0 ? _a : false; // @ts-expect-error: TypeScript isn't smart enough to understand the type assertion + + const packageLocator = { + name: packageName, + reference: packageReference + }; + const entry = packageLocatorsByLocations.get(packageInformationData.packageLocation); + + if (!entry) { + packageLocatorsByLocations.set(packageInformationData.packageLocation, { + locator: packageLocator, + discardFromLookup + }); + } else { + entry.discardFromLookup = entry.discardFromLookup && discardFromLookup; + + if (!discardFromLookup) { + entry.locator = packageLocator; + } + } + + let resolvedPackageLocation = null; + return [packageReference, { + packageDependencies: new Map(packageInformationData.packageDependencies), + packagePeers: new Set(packageInformationData.packagePeers), + linkType: packageInformationData.linkType, + discardFromLookup, + + // we only need this for packages that are used by the currently running script + // this is a lazy getter because `ppath.join` has some overhead + get packageLocation() { + // We use ppath.join instead of ppath.resolve because: + // 1) packageInformationData.packageLocation is a relative path when part of the SerializedState + // 2) ppath.join preserves trailing slashes + return resolvedPackageLocation || (resolvedPackageLocation = ppath.join(absolutePortablePath, packageInformationData.packageLocation)); + } + + }]; + }))]; + })); + const fallbackExclusionList = new Map(data.fallbackExclusionList.map(([packageName, packageReferences]) => { + return [packageName, new Set(packageReferences)]; + })); + const fallbackPool = new Map(data.fallbackPool); + const dependencyTreeRoots = data.dependencyTreeRoots; + const enableTopLevelFallback = data.enableTopLevelFallback; + return { + basePath: portablePath, + dependencyTreeRoots, + enableTopLevelFallback, + fallbackExclusionList, + fallbackPool, + ignorePattern, + packageLocatorsByLocations, + packageRegistry + }; +} +;// CONCATENATED MODULE: ../../.yarn/cache/resolve.exports-npm-1.1.0-81756e03ba-52865af8ed.zip/node_modules/resolve.exports/dist/index.mjs +/** + * @param {object} exports + * @param {Set} keys + */ +function loop(exports, keys) { + if (typeof exports === 'string') { + return exports; + } + + if (exports) { + let idx, tmp; + if (Array.isArray(exports)) { + for (idx=0; idx < exports.length; idx++) { + if (tmp = loop(exports[idx], keys)) return tmp; + } + } else { + for (idx in exports) { + if (keys.has(idx)) { + return loop(exports[idx], keys); + } + } + } + } +} + +/** + * @param {string} name The package name + * @param {string} entry The target entry, eg "." + * @param {number} [condition] Unmatched condition? + */ +function bail(name, entry, condition) { + throw new Error( + condition + ? `No known conditions for "${entry}" entry in "${name}" package` + : `Missing "${entry}" export in "${name}" package` + ); +} + +/** + * @param {string} name the package name + * @param {string} entry the target path/import + */ +function toName(name, entry) { + return entry === name ? '.' + : entry[0] === '.' ? entry + : entry.replace(new RegExp('^' + name + '\/'), './'); +} + +/** + * @param {object} pkg package.json contents + * @param {string} [entry] entry name or import path + * @param {object} [options] + * @param {boolean} [options.browser] + * @param {boolean} [options.require] + * @param {string[]} [options.conditions] + * @param {boolean} [options.unsafe] + */ +function resolve(pkg, entry='.', options={}) { + let { name, exports } = pkg; + + if (exports) { + let { browser, require, unsafe, conditions=[] } = options; + + let target = toName(name, entry); + if (target[0] !== '.') target = './' + target; + + if (typeof exports === 'string') { + return target === '.' ? exports : bail(name, target); + } + + let allows = new Set(['default', ...conditions]); + unsafe || allows.add(require ? 'require' : 'import'); + unsafe || allows.add(browser ? 'browser' : 'node'); + + let key, tmp, isSingle=false; + + for (key in exports) { + isSingle = key[0] !== '.'; + break; + } + + if (isSingle) { + return target === '.' + ? loop(exports, allows) || bail(name, target, 1) + : bail(name, target); + } + + if (tmp = exports[target]) { + return loop(tmp, allows) || bail(name, target, 1); + } + + for (key in exports) { + tmp = key[key.length - 1]; + if (tmp === '/' && target.startsWith(key)) { + return (tmp = loop(exports[key], allows)) + ? (tmp + target.substring(key.length)) + : bail(name, target, 1); + } + if (tmp === '*' && target.startsWith(key.slice(0, -1))) { + // do not trigger if no *content* to inject + if (target.substring(key.length - 1).length > 0) { + return (tmp = loop(exports[key], allows)) + ? tmp.replace('*', target.substring(key.length - 1)) + : bail(name, target, 1); + } + } + } + + return bail(name, target); + } +} + +/** + * @param {object} pkg + * @param {object} [options] + * @param {string|boolean} [options.browser] + * @param {string[]} [options.fields] + */ +function legacy(pkg, options={}) { + let i=0, value, + browser = options.browser, + fields = options.fields || ['module', 'main']; + + if (browser && !fields.includes('browser')) { + fields.unshift('browser'); + } + + for (; i < fields.length; i++) { + if (value = pkg[fields[i]]) { + if (typeof value == 'string') { + // + } else if (typeof value == 'object' && fields[i] == 'browser') { + if (typeof browser == 'string') { + value = value[browser=toName(pkg.name, browser)]; + if (value == null) return browser; + } + } else { + continue; + } + + return typeof value == 'string' + ? ('./' + value.replace(/^\.?\//, '')) + : value; + } + } +} + +;// CONCATENATED MODULE: ./sources/loader/makeApi.ts + + + + + + +function makeApi(runtimeState, opts) { + const alwaysWarnOnFallback = Number(process.env.PNP_ALWAYS_WARN_ON_FALLBACK) > 0; + const debugLevel = Number(process.env.PNP_DEBUG_LEVEL); // @ts-expect-error + + const builtinModules = new Set(external_module_.Module.builtinModules || Object.keys(process.binding(`natives`))); + + const isBuiltinModule = request => builtinModules.has(request) || request.startsWith(`node:`); // Splits a require request into its components, or return null if the request is a file path + + + const pathRegExp = /^(?![a-zA-Z]:[\\/]|\\\\|\.{0,2}(?:\/|$))((?:node:)?(?:@[^/]+\/)?[^/]+)\/*(.*|)$/; // Matches if the path starts with a valid path qualifier (./, ../, /) + // eslint-disable-next-line no-unused-vars + + const isStrictRegExp = /^(\/|\.{1,2}(\/|$))/; // Matches if the path must point to a directory (ie ends with /) + + const isDirRegExp = /\/$/; // Matches if the path starts with a relative path qualifier (./, ../) + + const isRelativeRegexp = /^\.{0,2}\//; // We only instantiate one of those so that we can use strict-equal comparisons + + const topLevelLocator = { + name: null, + reference: null + }; // Used for compatibility purposes - cf setupCompatibilityLayer + + const fallbackLocators = []; // To avoid emitting the same warning multiple times + + const emittedWarnings = new Set(); + if (runtimeState.enableTopLevelFallback === true) fallbackLocators.push(topLevelLocator); + + if (opts.compatibilityMode !== false) { + // ESLint currently doesn't have any portable way for shared configs to + // specify their own plugins that should be used (cf issue #10125). This + // will likely get fixed at some point but it'll take time, so in the + // meantime we'll just add additional fallback entries for common shared + // configs. + // Similarly, Gatsby generates files within the `public` folder located + // within the project, but doesn't pre-resolve the `require` calls to use + // its own dependencies. Meaning that when PnP see a file from the `public` + // folder making a require, it thinks that your project forgot to list one + // of your dependencies. + for (const name of [`react-scripts`, `gatsby`]) { + const packageStore = runtimeState.packageRegistry.get(name); + + if (packageStore) { + for (const reference of packageStore.keys()) { + if (reference === null) { + throw new Error(`Assertion failed: This reference shouldn't be null`); + } else { + fallbackLocators.push({ + name, + reference + }); + } + } + } + } + } + /** + * The setup code will be injected here. The tables listed below are guaranteed to be filled after the call to + * the $$DYNAMICALLY_GENERATED_CODE function. + */ + + + const { + ignorePattern, + packageRegistry, + packageLocatorsByLocations + } = runtimeState; + /** + * Allows to print useful logs just be setting a value in the environment + */ + + function makeLogEntry(name, args) { + return { + fn: name, + args, + error: null, + result: null + }; + } + + function trace(entry) { + var _a, _b, _c, _d, _e, _f; + + const colors = (_c = (_b = (_a = process.stderr) === null || _a === void 0 ? void 0 : _a.hasColors) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : process.stdout.isTTY; + + const c = (n, str) => `\u001b[${n}m${str}\u001b[0m`; + + const error = entry.error; + if (error) console.error(c(`31;1`, `✖ ${(_d = entry.error) === null || _d === void 0 ? void 0 : _d.message.replace(/\n.*/s, ``)}`));else console.error(c(`33;1`, `‼ Resolution`)); + if (entry.args.length > 0) console.error(); + + for (const arg of entry.args) console.error(` ${c(`37;1`, `In ←`)} ${(0,external_util_namespaceObject.inspect)(arg, { + colors, + compact: true + })}`); + + if (entry.result) { + console.error(); + console.error(` ${c(`37;1`, `Out →`)} ${(0,external_util_namespaceObject.inspect)(entry.result, { + colors, + compact: true + })}`); + } + + const stack = (_f = (_e = new Error().stack.match(/(?<=^ +)at.*/gm)) === null || _e === void 0 ? void 0 : _e.slice(2)) !== null && _f !== void 0 ? _f : []; + + if (stack.length > 0) { + console.error(); + + for (const line of stack) { + console.error(` ${c(`38;5;244`, line)}`); + } + } + + console.error(); + } + + function maybeLog(name, fn) { + if (opts.allowDebug === false) return fn; + + if (Number.isFinite(debugLevel)) { + if (debugLevel >= 2) { + return (...args) => { + const logEntry = makeLogEntry(name, args); + + try { + return logEntry.result = fn(...args); + } catch (error) { + throw logEntry.error = error; + } finally { + trace(logEntry); + } + }; + } else if (debugLevel >= 1) { + return (...args) => { + try { + return fn(...args); + } catch (error) { + const logEntry = makeLogEntry(name, args); + logEntry.error = error; + trace(logEntry); + throw error; + } + }; + } + } + + return fn; + } + /** + * Returns information about a package in a safe way (will throw if they cannot be retrieved) + */ + + + function getPackageInformationSafe(packageLocator) { + const packageInformation = getPackageInformation(packageLocator); + + if (!packageInformation) { + throw internalTools_makeError(ErrorCode.INTERNAL, `Couldn't find a matching entry in the dependency tree for the specified parent (this is probably an internal error)`); + } + + return packageInformation; + } + /** + * Returns whether the specified locator is a dependency tree root (in which case it's part of the project) or not + */ + + + function isDependencyTreeRoot(packageLocator) { + if (packageLocator.name === null) return true; + + for (const dependencyTreeRoot of runtimeState.dependencyTreeRoots) if (dependencyTreeRoot.name === packageLocator.name && dependencyTreeRoot.reference === packageLocator.reference) return true; + + return false; + } + + const defaultExportsConditions = new Set([`default`, `node`, `require`]); + /** + * Implements the node resolution for the "exports" field + * + * @returns The remapped path or `null` if the package doesn't have a package.json or an "exports" field + */ + + function applyNodeExportsResolution(unqualifiedPath, conditions = defaultExportsConditions) { + const locator = findPackageLocator(ppath.join(unqualifiedPath, `internal.js`), { + resolveIgnored: true, + includeDiscardFromLookup: true + }); + + if (locator === null) { + throw internalTools_makeError(ErrorCode.INTERNAL, `The locator that owns the "${unqualifiedPath}" path can't be found inside the dependency tree (this is probably an internal error)`); + } + + const { + packageLocation + } = getPackageInformationSafe(locator); + const manifestPath = ppath.join(packageLocation, Filename.manifest); + if (!opts.fakeFs.existsSync(manifestPath)) return null; + const pkgJson = JSON.parse(opts.fakeFs.readFileSync(manifestPath, `utf8`)); + let subpath = ppath.contains(packageLocation, unqualifiedPath); + + if (subpath === null) { + throw internalTools_makeError(ErrorCode.INTERNAL, `unqualifiedPath doesn't contain the packageLocation (this is probably an internal error)`); + } + + if (!isRelativeRegexp.test(subpath)) subpath = `./${subpath}`; + const resolvedExport = resolve(pkgJson, ppath.normalize(subpath), { + // TODO: implement support for the --conditions flag + // Waiting on https://github.com/nodejs/node/issues/36935 + // @ts-expect-error - Type should be Iterable + conditions, + unsafe: true + }); + if (typeof resolvedExport === `string`) return ppath.join(packageLocation, resolvedExport); + return null; + } + /** + * Implements the node resolution for folder access and extension selection + */ + + + function applyNodeExtensionResolution(unqualifiedPath, candidates, { + extensions + }) { + let stat; + + try { + candidates.push(unqualifiedPath); + stat = opts.fakeFs.statSync(unqualifiedPath); + } catch (error) {} // If the file exists and is a file, we can stop right there + + + if (stat && !stat.isDirectory()) return opts.fakeFs.realpathSync(unqualifiedPath); // If the file is a directory, we must check if it contains a package.json with a "main" entry + + if (stat && stat.isDirectory()) { + let pkgJson; + + try { + pkgJson = JSON.parse(opts.fakeFs.readFileSync(ppath.join(unqualifiedPath, Filename.manifest), `utf8`)); + } catch (error) {} + + let nextUnqualifiedPath; + if (pkgJson && pkgJson.main) nextUnqualifiedPath = ppath.resolve(unqualifiedPath, pkgJson.main); // If the "main" field changed the path, we start again from this new location + + if (nextUnqualifiedPath && nextUnqualifiedPath !== unqualifiedPath) { + const resolution = applyNodeExtensionResolution(nextUnqualifiedPath, candidates, { + extensions + }); + + if (resolution !== null) { + return resolution; + } + } + } // Otherwise we check if we find a file that match one of the supported extensions + + + for (let i = 0, length = extensions.length; i < length; i++) { + const candidateFile = `${unqualifiedPath}${extensions[i]}`; + candidates.push(candidateFile); + + if (opts.fakeFs.existsSync(candidateFile)) { + return candidateFile; + } + } // Otherwise, we check if the path is a folder - in such a case, we try to use its index + + + if (stat && stat.isDirectory()) { + for (let i = 0, length = extensions.length; i < length; i++) { + const candidateFile = ppath.format({ + dir: unqualifiedPath, + name: `index`, + ext: extensions[i] + }); + candidates.push(candidateFile); + + if (opts.fakeFs.existsSync(candidateFile)) { + return candidateFile; + } + } + } // Otherwise there's nothing else we can do :( + + + return null; + } + /** + * This function creates fake modules that can be used with the _resolveFilename function. + * Ideally it would be nice to be able to avoid this, since it causes useless allocations + * and cannot be cached efficiently (we recompute the nodeModulePaths every time). + * + * Fortunately, this should only affect the fallback, and there hopefully shouldn't have a + * lot of them. + */ + + + function makeFakeModule(path) { + // @ts-expect-error + const fakeModule = new external_module_.Module(path, null); + fakeModule.filename = path; + fakeModule.paths = external_module_.Module._nodeModulePaths(path); + return fakeModule; + } + /** + * Forward the resolution to the next resolver (usually the native one) + */ + + + function callNativeResolution(request, issuer) { + if (issuer.endsWith(`/`)) issuer = ppath.join(issuer, `internal.js`); // Since we would need to create a fake module anyway (to call _resolveLookupPath that + // would give us the paths to give to _resolveFilename), we can as well not use + // the {paths} option at all, since it internally makes _resolveFilename create another + // fake module anyway. + + return external_module_.Module._resolveFilename(npath.fromPortablePath(request), makeFakeModule(npath.fromPortablePath(issuer)), false, { + plugnplay: false + }); + } + /** + * + */ + + + function isPathIgnored(path) { + if (ignorePattern === null) return false; + const subPath = ppath.contains(runtimeState.basePath, path); + if (subPath === null) return false; + + if (ignorePattern.test(subPath.replace(/\/$/, ``))) { + return true; + } else { + return false; + } + } + /** + * This key indicates which version of the standard is implemented by this resolver. The `std` key is the + * Plug'n'Play standard, and any other key are third-party extensions. Third-party extensions are not allowed + * to override the standard, and can only offer new methods. + * + * If a new version of the Plug'n'Play standard is released and some extensions conflict with newly added + * functions, they'll just have to fix the conflicts and bump their own version number. + */ + + + const VERSIONS = { + std: 3, + resolveVirtual: 1, + getAllLocators: 1 + }; + /** + * We export a special symbol for easy access to the top level locator. + */ + + const topLevel = topLevelLocator; + /** + * Gets the package information for a given locator. Returns null if they cannot be retrieved. + */ + + function getPackageInformation({ + name, + reference + }) { + const packageInformationStore = packageRegistry.get(name); + if (!packageInformationStore) return null; + const packageInformation = packageInformationStore.get(reference); + if (!packageInformation) return null; + return packageInformation; + } + /** + * Find all packages that depend on the specified one. + * + * Note: This is a private function; we expect consumers to implement it + * themselves. We keep it that way because this implementation isn't + * optimized at all, since we only need it when printing errors. + */ + + + function findPackageDependents({ + name, + reference + }) { + const dependents = []; + + for (const [dependentName, packageInformationStore] of packageRegistry) { + if (dependentName === null) continue; + + for (const [dependentReference, packageInformation] of packageInformationStore) { + if (dependentReference === null) continue; + const dependencyReference = packageInformation.packageDependencies.get(name); + if (dependencyReference !== reference) continue; // Don't forget that all packages depend on themselves + + if (dependentName === name && dependentReference === reference) continue; + dependents.push({ + name: dependentName, + reference: dependentReference + }); + } + } + + return dependents; + } + /** + * Find all packages that broke the peer dependency on X, starting from Y. + * + * Note: This is a private function; we expect consumers to implement it + * themselves. We keep it that way because this implementation isn't + * optimized at all, since we only need it when printing errors. + */ + + + function findBrokenPeerDependencies(dependency, initialPackage) { + const brokenPackages = new Map(); + const alreadyVisited = new Set(); + + const traversal = currentPackage => { + const identifier = JSON.stringify(currentPackage.name); + if (alreadyVisited.has(identifier)) return; + alreadyVisited.add(identifier); + const dependents = findPackageDependents(currentPackage); + + for (const dependent of dependents) { + const dependentInformation = getPackageInformationSafe(dependent); + + if (dependentInformation.packagePeers.has(dependency)) { + traversal(dependent); + } else { + let brokenSet = brokenPackages.get(dependent.name); + if (typeof brokenSet === `undefined`) brokenPackages.set(dependent.name, brokenSet = new Set()); + brokenSet.add(dependent.reference); + } + } + }; + + traversal(initialPackage); + const brokenList = []; + + for (const name of [...brokenPackages.keys()].sort()) for (const reference of [...brokenPackages.get(name)].sort()) brokenList.push({ + name, + reference + }); + + return brokenList; + } + /** + * Finds the package locator that owns the specified path. If none is found, returns null instead. + */ + + + function findPackageLocator(location, { + resolveIgnored = false, + includeDiscardFromLookup = false + } = {}) { + if (isPathIgnored(location) && !resolveIgnored) return null; + let relativeLocation = ppath.relative(runtimeState.basePath, location); + if (!relativeLocation.match(isStrictRegExp)) relativeLocation = `./${relativeLocation}`; + if (!relativeLocation.endsWith(`/`)) relativeLocation = `${relativeLocation}/`; + + do { + const entry = packageLocatorsByLocations.get(relativeLocation); + + if (typeof entry === `undefined` || entry.discardFromLookup && !includeDiscardFromLookup) { + relativeLocation = relativeLocation.substring(0, relativeLocation.lastIndexOf(`/`, relativeLocation.length - 2) + 1); + continue; + } + + return entry.locator; + } while (relativeLocation !== ``); + + return null; + } + /** + * Transforms a request (what's typically passed as argument to the require function) into an unqualified path. + * This path is called "unqualified" because it only changes the package name to the package location on the disk, + * which means that the end result still cannot be directly accessed (for example, it doesn't try to resolve the + * file extension, or to resolve directories to their "index.js" content). Use the "resolveUnqualified" function + * to convert them to fully-qualified paths, or just use "resolveRequest" that do both operations in one go. + * + * Note that it is extremely important that the `issuer` path ends with a forward slash if the issuer is to be + * treated as a folder (ie. "/tmp/foo/" rather than "/tmp/foo" if "foo" is a directory). Otherwise relative + * imports won't be computed correctly (they'll get resolved relative to "/tmp/" instead of "/tmp/foo/"). + */ + + + function resolveToUnqualified(request, issuer, { + considerBuiltins = true + } = {}) { + // The 'pnpapi' request is reserved and will always return the path to the PnP file, from everywhere + if (request === `pnpapi`) return npath.toPortablePath(opts.pnpapiResolution); // Bailout if the request is a native module + + if (considerBuiltins && isBuiltinModule(request)) return null; + const requestForDisplay = getPathForDisplay(request); + const issuerForDisplay = issuer && getPathForDisplay(issuer); // We allow disabling the pnp resolution for some subpaths. + // This is because some projects, often legacy, contain multiple + // levels of dependencies (ie. a yarn.lock inside a subfolder of + // a yarn.lock). This is typically solved using workspaces, but + // not all of them have been converted already. + + if (issuer && isPathIgnored(issuer)) { + // Absolute paths that seem to belong to a PnP tree are still + // handled by our runtime even if the issuer isn't. This is + // because the native Node resolution uses a special version + // of the `stat` syscall which would otherwise bypass the + // filesystem layer we require to access the files. + if (!ppath.isAbsolute(request) || findPackageLocator(request) === null) { + const result = callNativeResolution(request, issuer); + + if (result === false) { + throw internalTools_makeError(ErrorCode.BUILTIN_NODE_RESOLUTION_FAILED, `The builtin node resolution algorithm was unable to resolve the requested module (it didn't go through the pnp resolver because the issuer was explicitely ignored by the regexp)\n\nRequire request: "${requestForDisplay}"\nRequired by: ${issuerForDisplay}\n`, { + request: requestForDisplay, + issuer: issuerForDisplay + }); + } + + return npath.toPortablePath(result); + } + } + + let unqualifiedPath; // If the request is a relative or absolute path, we just return it normalized + + const dependencyNameMatch = request.match(pathRegExp); + + if (!dependencyNameMatch) { + if (ppath.isAbsolute(request)) { + unqualifiedPath = ppath.normalize(request); + } else { + if (!issuer) { + throw internalTools_makeError(ErrorCode.API_ERROR, `The resolveToUnqualified function must be called with a valid issuer when the path isn't a builtin nor absolute`, { + request: requestForDisplay, + issuer: issuerForDisplay + }); + } // We use ppath.join instead of ppath.resolve because: + // 1) The request is a relative path in this branch + // 2) ppath.join preserves trailing slashes + + + const absoluteIssuer = ppath.resolve(issuer); + + if (issuer.match(isDirRegExp)) { + unqualifiedPath = ppath.normalize(ppath.join(absoluteIssuer, request)); + } else { + unqualifiedPath = ppath.normalize(ppath.join(ppath.dirname(absoluteIssuer), request)); + } + } + } else { + // Things are more hairy if it's a package require - we then need to figure out which package is needed, and in + // particular the exact version for the given location on the dependency tree + if (!issuer) { + throw internalTools_makeError(ErrorCode.API_ERROR, `The resolveToUnqualified function must be called with a valid issuer when the path isn't a builtin nor absolute`, { + request: requestForDisplay, + issuer: issuerForDisplay + }); + } + + const [, dependencyName, subPath] = dependencyNameMatch; + const issuerLocator = findPackageLocator(issuer); // If the issuer file doesn't seem to be owned by a package managed through pnp, then we resort to using the next + // resolution algorithm in the chain, usually the native Node resolution one + + if (!issuerLocator) { + const result = callNativeResolution(request, issuer); + + if (result === false) { + throw internalTools_makeError(ErrorCode.BUILTIN_NODE_RESOLUTION_FAILED, `The builtin node resolution algorithm was unable to resolve the requested module (it didn't go through the pnp resolver because the issuer doesn't seem to be part of the Yarn-managed dependency tree).\n\nRequire path: "${requestForDisplay}"\nRequired by: ${issuerForDisplay}\n`, { + request: requestForDisplay, + issuer: issuerForDisplay + }); + } + + return npath.toPortablePath(result); + } + + const issuerInformation = getPackageInformationSafe(issuerLocator); // We obtain the dependency reference in regard to the package that request it + + let dependencyReference = issuerInformation.packageDependencies.get(dependencyName); + let fallbackReference = null; // If we can't find it, we check if we can potentially load it from the packages that have been defined as potential fallbacks. + // It's a bit of a hack, but it improves compatibility with the existing Node ecosystem. Hopefully we should eventually be able + // to kill this logic and become stricter once pnp gets enough traction and the affected packages fix themselves. + + if (dependencyReference == null) { + if (issuerLocator.name !== null) { + // To allow programs to become gradually stricter, starting from the v2 we enforce that workspaces cannot depend on fallbacks. + // This works by having a list containing all their locators, and checking when a fallback is required whether it's one of them. + const exclusionEntry = runtimeState.fallbackExclusionList.get(issuerLocator.name); + const canUseFallbacks = !exclusionEntry || !exclusionEntry.has(issuerLocator.reference); + + if (canUseFallbacks) { + for (let t = 0, T = fallbackLocators.length; t < T; ++t) { + const fallbackInformation = getPackageInformationSafe(fallbackLocators[t]); + const reference = fallbackInformation.packageDependencies.get(dependencyName); + if (reference == null) continue; + if (alwaysWarnOnFallback) fallbackReference = reference;else dependencyReference = reference; + break; + } + + if (runtimeState.enableTopLevelFallback) { + if (dependencyReference == null && fallbackReference === null) { + const reference = runtimeState.fallbackPool.get(dependencyName); + + if (reference != null) { + fallbackReference = reference; + } + } + } + } + } + } // If we can't find the path, and if the package making the request is the top-level, we can offer nicer error messages + + + let error = null; + + if (dependencyReference === null) { + if (isDependencyTreeRoot(issuerLocator)) { + error = internalTools_makeError(ErrorCode.MISSING_PEER_DEPENDENCY, `Your application tried to access ${dependencyName} (a peer dependency); this isn't allowed as there is no ancestor to satisfy the requirement. Use a devDependency if needed.\n\nRequired package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``}\nRequired by: ${issuerForDisplay}\n`, { + request: requestForDisplay, + issuer: issuerForDisplay, + dependencyName + }); + } else { + const brokenAncestors = findBrokenPeerDependencies(dependencyName, issuerLocator); + + if (brokenAncestors.every(ancestor => isDependencyTreeRoot(ancestor))) { + error = internalTools_makeError(ErrorCode.MISSING_PEER_DEPENDENCY, `${issuerLocator.name} tried to access ${dependencyName} (a peer dependency) but it isn't provided by your application; this makes the require call ambiguous and unsound.\n\nRequired package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``}\nRequired by: ${issuerLocator.name}@${issuerLocator.reference} (via ${issuerForDisplay})\n${brokenAncestors.map(ancestorLocator => `Ancestor breaking the chain: ${ancestorLocator.name}@${ancestorLocator.reference}\n`).join(``)}\n`, { + request: requestForDisplay, + issuer: issuerForDisplay, + issuerLocator: Object.assign({}, issuerLocator), + dependencyName, + brokenAncestors + }); + } else { + error = internalTools_makeError(ErrorCode.MISSING_PEER_DEPENDENCY, `${issuerLocator.name} tried to access ${dependencyName} (a peer dependency) but it isn't provided by its ancestors; this makes the require call ambiguous and unsound.\n\nRequired package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``}\nRequired by: ${issuerLocator.name}@${issuerLocator.reference} (via ${issuerForDisplay})\n\n${brokenAncestors.map(ancestorLocator => `Ancestor breaking the chain: ${ancestorLocator.name}@${ancestorLocator.reference}\n`).join(``)}\n`, { + request: requestForDisplay, + issuer: issuerForDisplay, + issuerLocator: Object.assign({}, issuerLocator), + dependencyName, + brokenAncestors + }); + } + } + } else if (dependencyReference === undefined) { + if (!considerBuiltins && isBuiltinModule(request)) { + if (isDependencyTreeRoot(issuerLocator)) { + error = internalTools_makeError(ErrorCode.UNDECLARED_DEPENDENCY, `Your application tried to access ${dependencyName}. While this module is usually interpreted as a Node builtin, your resolver is running inside a non-Node resolution context where such builtins are ignored. Since ${dependencyName} isn't otherwise declared in your dependencies, this makes the require call ambiguous and unsound.\n\nRequired package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``}\nRequired by: ${issuerForDisplay}\n`, { + request: requestForDisplay, + issuer: issuerForDisplay, + dependencyName + }); + } else { + error = internalTools_makeError(ErrorCode.UNDECLARED_DEPENDENCY, `${issuerLocator.name} tried to access ${dependencyName}. While this module is usually interpreted as a Node builtin, your resolver is running inside a non-Node resolution context where such builtins are ignored. Since ${dependencyName} isn't otherwise declared in ${issuerLocator.name}'s dependencies, this makes the require call ambiguous and unsound.\n\nRequired package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``}\nRequired by: ${issuerForDisplay}\n`, { + request: requestForDisplay, + issuer: issuerForDisplay, + issuerLocator: Object.assign({}, issuerLocator), + dependencyName + }); + } + } else { + if (isDependencyTreeRoot(issuerLocator)) { + error = internalTools_makeError(ErrorCode.UNDECLARED_DEPENDENCY, `Your application tried to access ${dependencyName}, but it isn't declared in your dependencies; this makes the require call ambiguous and unsound.\n\nRequired package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``}\nRequired by: ${issuerForDisplay}\n`, { + request: requestForDisplay, + issuer: issuerForDisplay, + dependencyName + }); + } else { + error = internalTools_makeError(ErrorCode.UNDECLARED_DEPENDENCY, `${issuerLocator.name} tried to access ${dependencyName}, but it isn't declared in its dependencies; this makes the require call ambiguous and unsound.\n\nRequired package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``}\nRequired by: ${issuerLocator.name}@${issuerLocator.reference} (via ${issuerForDisplay})\n`, { + request: requestForDisplay, + issuer: issuerForDisplay, + issuerLocator: Object.assign({}, issuerLocator), + dependencyName + }); + } + } + } + + if (dependencyReference == null) { + if (fallbackReference === null || error === null) throw error || new Error(`Assertion failed: Expected an error to have been set`); + dependencyReference = fallbackReference; + const message = error.message.replace(/\n.*/g, ``); + error.message = message; + + if (!emittedWarnings.has(message) && debugLevel !== 0) { + emittedWarnings.add(message); + process.emitWarning(error); + } + } // We need to check that the package exists on the filesystem, because it might not have been installed + + + const dependencyLocator = Array.isArray(dependencyReference) ? { + name: dependencyReference[0], + reference: dependencyReference[1] + } : { + name: dependencyName, + reference: dependencyReference + }; + const dependencyInformation = getPackageInformationSafe(dependencyLocator); + + if (!dependencyInformation.packageLocation) { + throw internalTools_makeError(ErrorCode.MISSING_DEPENDENCY, `A dependency seems valid but didn't get installed for some reason. This might be caused by a partial install, such as dev vs prod.\n\nRequired package: ${dependencyLocator.name}@${dependencyLocator.reference}${dependencyLocator.name !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``}\nRequired by: ${issuerLocator.name}@${issuerLocator.reference} (via ${issuerForDisplay})\n`, { + request: requestForDisplay, + issuer: issuerForDisplay, + dependencyLocator: Object.assign({}, dependencyLocator) + }); + } // Now that we know which package we should resolve to, we only have to find out the file location + // packageLocation is always absolute as it's returned by getPackageInformationSafe + + + const dependencyLocation = dependencyInformation.packageLocation; + + if (subPath) { + // We use ppath.join instead of ppath.resolve because: + // 1) subPath is always a relative path + // 2) ppath.join preserves trailing slashes + unqualifiedPath = ppath.join(dependencyLocation, subPath); + } else { + unqualifiedPath = dependencyLocation; + } + } + + return ppath.normalize(unqualifiedPath); + } + + function resolveUnqualifiedExport(request, unqualifiedPath, conditions = defaultExportsConditions) { + // "exports" only apply when requiring a package, not when requiring via an absolute / relative path + if (isStrictRegExp.test(request)) return unqualifiedPath; + const unqualifiedExportPath = applyNodeExportsResolution(unqualifiedPath, conditions); + + if (unqualifiedExportPath) { + return ppath.normalize(unqualifiedExportPath); + } else { + return unqualifiedPath; + } + } + /** + * Transforms an unqualified path into a qualified path by using the Node resolution algorithm (which automatically + * appends ".js" / ".json", and transforms directory accesses into "index.js"). + */ + + + function resolveUnqualified(unqualifiedPath, { + extensions = Object.keys(external_module_.Module._extensions) + } = {}) { + const candidates = []; + const qualifiedPath = applyNodeExtensionResolution(unqualifiedPath, candidates, { + extensions + }); + + if (qualifiedPath) { + return ppath.normalize(qualifiedPath); + } else { + const unqualifiedPathForDisplay = getPathForDisplay(unqualifiedPath); + const containingPackage = findPackageLocator(unqualifiedPath); + + if (containingPackage) { + const { + packageLocation + } = getPackageInformationSafe(containingPackage); + + if (!opts.fakeFs.existsSync(packageLocation)) { + const errorMessage = packageLocation.includes(`/unplugged/`) ? `Required unplugged package missing from disk. This may happen when switching branches without running installs (unplugged packages must be fully materialized on disk to work).` : `Required package missing from disk. If you keep your packages inside your repository then restarting the Node process may be enough. Otherwise, try to run an install first.`; + throw internalTools_makeError(ErrorCode.QUALIFIED_PATH_RESOLUTION_FAILED, `${errorMessage}\n\nMissing package: ${containingPackage.name}@${containingPackage.reference}\nExpected package location: ${getPathForDisplay(packageLocation)}\n`, { + unqualifiedPath: unqualifiedPathForDisplay + }); + } + } + + throw internalTools_makeError(ErrorCode.QUALIFIED_PATH_RESOLUTION_FAILED, `Qualified path resolution failed - none of those files can be found on the disk.\n\nSource path: ${unqualifiedPathForDisplay}\n${candidates.map(candidate => `Not found: ${getPathForDisplay(candidate)}\n`).join(``)}`, { + unqualifiedPath: unqualifiedPathForDisplay + }); + } + } + /** + * Transforms a request into a fully qualified path. + * + * Note that it is extremely important that the `issuer` path ends with a forward slash if the issuer is to be + * treated as a folder (ie. "/tmp/foo/" rather than "/tmp/foo" if "foo" is a directory). Otherwise relative + * imports won't be computed correctly (they'll get resolved relative to "/tmp/" instead of "/tmp/foo/"). + */ + + + function resolveRequest(request, issuer, { + considerBuiltins, + extensions, + conditions + } = {}) { + const unqualifiedPath = resolveToUnqualified(request, issuer, { + considerBuiltins + }); // If the request is the pnpapi, we can just return the unqualifiedPath + // without having to apply the exports resolution or the extension resolution + // (opts.pnpapiResolution is always a full path - makeManager enforces this by stat-ing it) + + if (request === `pnpapi`) return unqualifiedPath; + if (unqualifiedPath === null) return null; + + const isIssuerIgnored = () => issuer !== null ? isPathIgnored(issuer) : false; + + const remappedPath = (!considerBuiltins || !isBuiltinModule(request)) && !isIssuerIgnored() ? resolveUnqualifiedExport(request, unqualifiedPath, conditions) : unqualifiedPath; + + try { + return resolveUnqualified(remappedPath, { + extensions + }); + } catch (resolutionError) { + if (resolutionError.pnpCode === `QUALIFIED_PATH_RESOLUTION_FAILED`) Object.assign(resolutionError.data, { + request: getPathForDisplay(request), + issuer: issuer && getPathForDisplay(issuer) + }); + throw resolutionError; + } + } + + function resolveVirtual(request) { + const normalized = ppath.normalize(request); + const resolved = VirtualFS.resolveVirtual(normalized); + return resolved !== normalized ? resolved : null; + } + + return { + VERSIONS, + topLevel, + getLocator: (name, referencish) => { + if (Array.isArray(referencish)) { + return { + name: referencish[0], + reference: referencish[1] + }; + } else { + return { + name, + reference: referencish + }; + } + }, + getDependencyTreeRoots: () => { + return [...runtimeState.dependencyTreeRoots]; + }, + + getAllLocators() { + const locators = []; + + for (const [name, entry] of packageRegistry) for (const reference of entry.keys()) if (name !== null && reference !== null) locators.push({ + name, + reference + }); + + return locators; + }, + + getPackageInformation: locator => { + const info = getPackageInformation(locator); + if (info === null) return null; + const packageLocation = npath.fromPortablePath(info.packageLocation); + const nativeInfo = { ...info, + packageLocation + }; + return nativeInfo; + }, + findPackageLocator: path => { + return findPackageLocator(npath.toPortablePath(path)); + }, + resolveToUnqualified: maybeLog(`resolveToUnqualified`, (request, issuer, opts) => { + const portableIssuer = issuer !== null ? npath.toPortablePath(issuer) : null; + const resolution = resolveToUnqualified(npath.toPortablePath(request), portableIssuer, opts); + if (resolution === null) return null; + return npath.fromPortablePath(resolution); + }), + resolveUnqualified: maybeLog(`resolveUnqualified`, (unqualifiedPath, opts) => { + return npath.fromPortablePath(resolveUnqualified(npath.toPortablePath(unqualifiedPath), opts)); + }), + resolveRequest: maybeLog(`resolveRequest`, (request, issuer, opts) => { + const portableIssuer = issuer !== null ? npath.toPortablePath(issuer) : null; + const resolution = resolveRequest(npath.toPortablePath(request), portableIssuer, opts); + if (resolution === null) return null; + return npath.fromPortablePath(resolution); + }), + resolveVirtual: maybeLog(`resolveVirtual`, path => { + const result = resolveVirtual(npath.toPortablePath(path)); + + if (result !== null) { + return npath.fromPortablePath(result); + } else { + return null; + } + }) + }; +} +;// CONCATENATED MODULE: ./sources/loader/makeManager.ts + + +function makeManager(pnpapi, opts) { + const initialApiPath = npath.toPortablePath(pnpapi.resolveToUnqualified(`pnpapi`, null)); + const initialApiStats = opts.fakeFs.statSync(npath.toPortablePath(initialApiPath)); + const apiMetadata = new Map([[initialApiPath, { + cache: external_module_.Module._cache, + instance: pnpapi, + stats: initialApiStats, + lastRefreshCheck: Date.now() + }]]); + + function loadApiInstance(pnpApiPath) { + const nativePath = npath.fromPortablePath(pnpApiPath); // @ts-expect-error + + const module = new external_module_.Module(nativePath, null); // @ts-expect-error + + module.load(nativePath); + return module.exports; + } + + function refreshApiEntry(pnpApiPath, apiEntry) { + const timeNow = Date.now(); + if (timeNow - apiEntry.lastRefreshCheck < 500) return; + apiEntry.lastRefreshCheck = timeNow; + const stats = opts.fakeFs.statSync(pnpApiPath); + + if (stats.mtime > apiEntry.stats.mtime) { + process.emitWarning(`[Warning] The runtime detected new informations in a PnP file; reloading the API instance (${npath.fromPortablePath(pnpApiPath)})`); + apiEntry.stats = stats; + apiEntry.instance = loadApiInstance(pnpApiPath); + } + } + + function getApiEntry(pnpApiPath, refresh = false) { + let apiEntry = apiMetadata.get(pnpApiPath); + + if (typeof apiEntry !== `undefined`) { + if (refresh) { + refreshApiEntry(pnpApiPath, apiEntry); + } + } else { + apiMetadata.set(pnpApiPath, apiEntry = { + cache: {}, + instance: loadApiInstance(pnpApiPath), + stats: opts.fakeFs.statSync(pnpApiPath), + lastRefreshCheck: Date.now() + }); + } + + return apiEntry; + } + + const findApiPathCache = new Map(); + + function addToCacheAndReturn(start, end, target) { + if (target !== null) target = VirtualFS.resolveVirtual(target); + let curr; + let next = start; + + do { + curr = next; + findApiPathCache.set(curr, target); + next = ppath.dirname(curr); + } while (curr !== end); + + return target; + } + + function findApiPathFor(modulePath) { + let bestCandidate = null; + + for (const [apiPath, apiEntry] of apiMetadata) { + const locator = apiEntry.instance.findPackageLocator(modulePath); + if (!locator) continue; // No need to go the slow way when there's a single API + + if (apiMetadata.size === 1) return apiPath; + const packageInformation = apiEntry.instance.getPackageInformation(locator); + if (!packageInformation) throw new Error(`Assertion failed: Couldn't get package information for '${modulePath}'`); + if (!bestCandidate) bestCandidate = { + packageLocation: packageInformation.packageLocation, + apiPaths: [] + }; + + if (packageInformation.packageLocation === bestCandidate.packageLocation) { + bestCandidate.apiPaths.push(apiPath); + } else if (packageInformation.packageLocation.length > bestCandidate.packageLocation.length) { + bestCandidate = { + packageLocation: packageInformation.packageLocation, + apiPaths: [apiPath] + }; + } + } + + if (bestCandidate) { + if (bestCandidate.apiPaths.length === 1) return bestCandidate.apiPaths[0]; + const controlSegment = bestCandidate.apiPaths.map(apiPath => ` ${npath.fromPortablePath(apiPath)}`).join(`\n`); + throw new Error(`Unable to locate pnpapi, the module '${modulePath}' is controlled by multiple pnpapi instances.\nThis is usually caused by using the global cache (enableGlobalCache: true)\n\nControlled by:\n${controlSegment}\n`); + } + + const start = ppath.resolve(npath.toPortablePath(modulePath)); + let curr; + let next = start; + + do { + curr = next; + const cached = findApiPathCache.get(curr); + if (cached !== undefined) return addToCacheAndReturn(start, curr, cached); + const cjsCandidate = ppath.join(curr, Filename.pnpCjs); + if (opts.fakeFs.existsSync(cjsCandidate) && opts.fakeFs.statSync(cjsCandidate).isFile()) return addToCacheAndReturn(start, curr, cjsCandidate); // We still support .pnp.js files to improve multi-project compatibility. + // TODO: Remove support for .pnp.js files after they stop being used. + + const legacyCjsCandidate = ppath.join(curr, Filename.pnpJs); + if (opts.fakeFs.existsSync(legacyCjsCandidate) && opts.fakeFs.statSync(legacyCjsCandidate).isFile()) return addToCacheAndReturn(start, curr, legacyCjsCandidate); + next = ppath.dirname(curr); + } while (curr !== PortablePath.root); + + return addToCacheAndReturn(start, curr, null); + } + + function getApiPathFromParent(parent) { + if (parent == null) return initialApiPath; + + if (typeof parent.pnpApiPath === `undefined`) { + if (parent.filename !== null) { + return parent.pnpApiPath = findApiPathFor(parent.filename); + } else { + return initialApiPath; + } + } + + if (parent.pnpApiPath !== null) return parent.pnpApiPath; + return null; + } + + return { + getApiPathFromParent, + findApiPathFor, + getApiEntry + }; +} +;// CONCATENATED MODULE: ./sources/loader/_entryPoint.ts + + + + + + + + + // We must copy the fs into a local, because otherwise +// 1. we would make the NodeFS instance use the function that we patched (infinite loop) +// 2. Object.create(fs) isn't enough, since it won't prevent the proto from being modified + +const localFs = { ...(external_fs_default()) +}; +const nodeFs = new NodeFS(localFs); +const defaultRuntimeState = $$SETUP_STATE(hydrateRuntimeState); +const defaultPnpapiResolution = __filename; // We create a virtual filesystem that will do three things: +// 1. all requests inside a folder named "__virtual___" will be remapped according the virtual folder rules +// 2. all requests going inside a Zip archive will be handled by the Zip fs implementation +// 3. any remaining request will be forwarded to Node as-is + +const defaultFsLayer = new VirtualFS({ + baseFs: new ZipOpenFS({ + baseFs: nodeFs, + libzip: () => getLibzipSync(), + maxOpenFiles: 80, + readOnlyArchives: true + }) +}); + +class DynamicFS extends ProxiedFS { + constructor() { + super(ppath); + this.baseFs = defaultFsLayer; + } + + mapToBase(p) { + return p; + } + + mapFromBase(p) { + return p; + } + +} + +const dynamicFsLayer = new DynamicFS(); +let manager; +const defaultApi = Object.assign(makeApi(defaultRuntimeState, { + fakeFs: dynamicFsLayer, + pnpapiResolution: defaultPnpapiResolution +}), { + /** + * Can be used to generate a different API than the default one (for example + * to map it on `/` rather than the local directory path, or to use a + * different FS layer than the default one). + */ + makeApi: ({ + basePath = undefined, + fakeFs = dynamicFsLayer, + pnpapiResolution = defaultPnpapiResolution, + ...rest + }) => { + const apiRuntimeState = typeof basePath !== `undefined` ? $$SETUP_STATE(hydrateRuntimeState, basePath) : defaultRuntimeState; + return makeApi(apiRuntimeState, { + fakeFs, + pnpapiResolution, + ...rest + }); + }, + + /** + * Will inject the specified API into the environment, monkey-patching FS. Is + * automatically called when the hook is loaded through `--require`. + */ + setup: api => { + applyPatch(api || defaultApi, { + fakeFs: defaultFsLayer, + manager + }); // Now that the `fs` module is patched we can swap the `baseFs` to + // a NodeFS with a live `fs` binding to pick up changes to the `fs` + // module allowing users to patch it + + dynamicFsLayer.baseFs = new NodeFS((external_fs_default())); + } +}); +manager = makeManager(defaultApi, { + fakeFs: dynamicFsLayer +}); // eslint-disable-next-line arca/no-default-export + +/* harmony default export */ const _entryPoint = (defaultApi); + +if (__non_webpack_module__.parent && __non_webpack_module__.parent.id === `internal/preload`) { + defaultApi.setup(); + + if (__non_webpack_module__.filename) { + // We delete it from the cache in order to support the case where the CLI resolver is invoked from "yarn run" + // It's annoying because it might cause some issues when the file is multiple times in NODE_OPTIONS, but it shouldn't happen anyway. + delete (external_module_default())._cache[__non_webpack_module__.filename]; + } +} + +if (process.mainModule === __non_webpack_module__) { + const reportError = (code, message, data) => { + process.stdout.write(`${JSON.stringify([{ + code, + message, + data + }, null])}\n`); + }; + + const reportSuccess = resolution => { + process.stdout.write(`${JSON.stringify([null, resolution])}\n`); + }; + + const processResolution = (request, issuer) => { + try { + reportSuccess(defaultApi.resolveRequest(request, issuer)); + } catch (error) { + reportError(error.code, error.message, error.data); + } + }; + + const processRequest = data => { + try { + const [request, issuer] = JSON.parse(data); + processResolution(request, issuer); + } catch (error) { + reportError(`INVALID_JSON`, error.message, error.data); + } + }; + + if (process.argv.length > 2) { + if (process.argv.length !== 4) { + process.stderr.write(`Usage: ${process.argv[0]} ${process.argv[1]} \n`); + process.exitCode = 64; + /* EX_USAGE */ + } else { + processResolution(process.argv[2], process.argv[3]); + } + } else { + let buffer = ``; + const decoder = new (external_string_decoder_default()).StringDecoder(); + process.stdin.on(`data`, chunk => { + buffer += decoder.write(chunk); + + do { + const index = buffer.indexOf(`\n`); + if (index === -1) break; + const line = buffer.slice(0, index); + buffer = buffer.slice(index + 1); + processRequest(line); + } while (true); + }); + } +} +})(); + +__webpack_exports__ = __webpack_exports__.default; +/******/ return __webpack_exports__; +/******/ })() +; +}); \ No newline at end of file diff --git a/.pnp.loader.mjs b/.pnp.loader.mjs new file mode 100644 index 00000000000..9cb52bf76c3 --- /dev/null +++ b/.pnp.loader.mjs @@ -0,0 +1,249 @@ +import { URL, fileURLToPath, pathToFileURL } from 'url'; +import fs from 'fs'; +import path from 'path'; +import moduleExports, { Module } from 'module'; + +var PathType; +(function(PathType2) { + PathType2[PathType2["File"] = 0] = "File"; + PathType2[PathType2["Portable"] = 1] = "Portable"; + PathType2[PathType2["Native"] = 2] = "Native"; +})(PathType || (PathType = {})); +const npath = Object.create(path); +const ppath = Object.create(path.posix); +npath.cwd = () => process.cwd(); +ppath.cwd = () => toPortablePath(process.cwd()); +ppath.resolve = (...segments) => { + if (segments.length > 0 && ppath.isAbsolute(segments[0])) { + return path.posix.resolve(...segments); + } else { + return path.posix.resolve(ppath.cwd(), ...segments); + } +}; +const contains = function(pathUtils, from, to) { + from = pathUtils.normalize(from); + to = pathUtils.normalize(to); + if (from === to) + return `.`; + if (!from.endsWith(pathUtils.sep)) + from = from + pathUtils.sep; + if (to.startsWith(from)) { + return to.slice(from.length); + } else { + return null; + } +}; +npath.fromPortablePath = fromPortablePath; +npath.toPortablePath = toPortablePath; +npath.contains = (from, to) => contains(npath, from, to); +ppath.contains = (from, to) => contains(ppath, from, to); +const WINDOWS_PATH_REGEXP = /^([a-zA-Z]:.*)$/; +const UNC_WINDOWS_PATH_REGEXP = /^\\\\(\.\\)?(.*)$/; +const PORTABLE_PATH_REGEXP = /^\/([a-zA-Z]:.*)$/; +const UNC_PORTABLE_PATH_REGEXP = /^\/unc\/(\.dot\/)?(.*)$/; +function fromPortablePath(p) { + if (process.platform !== `win32`) + return p; + let portablePathMatch, uncPortablePathMatch; + if (portablePathMatch = p.match(PORTABLE_PATH_REGEXP)) + p = portablePathMatch[1]; + else if (uncPortablePathMatch = p.match(UNC_PORTABLE_PATH_REGEXP)) + p = `\\\\${uncPortablePathMatch[1] ? `.\\` : ``}${uncPortablePathMatch[2]}`; + else + return p; + return p.replace(/\//g, `\\`); +} +function toPortablePath(p) { + if (process.platform !== `win32`) + return p; + let windowsPathMatch, uncWindowsPathMatch; + if (windowsPathMatch = p.match(WINDOWS_PATH_REGEXP)) + p = `/${windowsPathMatch[1]}`; + else if (uncWindowsPathMatch = p.match(UNC_WINDOWS_PATH_REGEXP)) + p = `/unc/${uncWindowsPathMatch[1] ? `.dot/` : ``}${uncWindowsPathMatch[2]}`; + return p.replace(/\\/g, `/`); +} + +const builtinModules = new Set(Module.builtinModules || Object.keys(process.binding(`natives`))); +const isBuiltinModule = (request) => request.startsWith(`node:`) || builtinModules.has(request); +function readPackageScope(checkPath) { + const rootSeparatorIndex = checkPath.indexOf(npath.sep); + let separatorIndex; + do { + separatorIndex = checkPath.lastIndexOf(npath.sep); + checkPath = checkPath.slice(0, separatorIndex); + if (checkPath.endsWith(`${npath.sep}node_modules`)) + return false; + const pjson = readPackage(checkPath + npath.sep); + if (pjson) { + return { + data: pjson, + path: checkPath + }; + } + } while (separatorIndex > rootSeparatorIndex); + return false; +} +function readPackage(requestPath) { + const jsonPath = npath.resolve(requestPath, `package.json`); + if (!fs.existsSync(jsonPath)) + return null; + return JSON.parse(fs.readFileSync(jsonPath, `utf8`)); +} + +async function tryReadFile(path2) { + try { + return await fs.promises.readFile(path2, `utf8`); + } catch (error) { + if (error.code === `ENOENT`) + return null; + throw error; + } +} +function tryParseURL(str) { + try { + return new URL(str); + } catch { + return null; + } +} +function getFileFormat(filepath) { + var _a; + const ext = path.extname(filepath); + switch (ext) { + case `.mjs`: { + return `module`; + } + case `.cjs`: { + return `commonjs`; + } + case `.wasm`: { + throw new Error(`Unknown file extension ".wasm" for ${filepath}`); + } + case `.json`: { + throw new Error(`Unknown file extension ".json" for ${filepath}`); + } + case `.js`: { + const pkg = readPackageScope(filepath); + if (pkg) { + return (_a = pkg.data.type) != null ? _a : `commonjs`; + } + } + } + return null; +} + +async function getFormat$1(resolved, context, defaultGetFormat) { + const url = tryParseURL(resolved); + if ((url == null ? void 0 : url.protocol) !== `file:`) + return defaultGetFormat(resolved, context, defaultGetFormat); + const format = getFileFormat(fileURLToPath(url)); + if (format) { + return { + format + }; + } + return defaultGetFormat(resolved, context, defaultGetFormat); +} + +async function getSource$1(urlString, context, defaultGetSource) { + const url = tryParseURL(urlString); + if ((url == null ? void 0 : url.protocol) !== `file:`) + return defaultGetSource(urlString, context, defaultGetSource); + return { + source: await fs.promises.readFile(fileURLToPath(url), `utf8`) + }; +} + +async function load$1(urlString, context, defaultLoad) { + const url = tryParseURL(urlString); + if ((url == null ? void 0 : url.protocol) !== `file:`) + return defaultLoad(urlString, context, defaultLoad); + const filePath = fileURLToPath(url); + const format = getFileFormat(filePath); + if (!format) + return defaultLoad(urlString, context, defaultLoad); + return { + format, + source: await fs.promises.readFile(filePath, `utf8`) + }; +} + +const pathRegExp = /^(?![a-zA-Z]:[\\/]|\\\\|\.{0,2}(?:\/|$))((?:node:)?(?:@[^/]+\/)?[^/]+)\/*(.*|)$/; +async function resolve$1(originalSpecifier, context, defaultResolver) { + var _a; + const {findPnpApi} = moduleExports; + if (!findPnpApi || isBuiltinModule(originalSpecifier)) + return defaultResolver(originalSpecifier, context, defaultResolver); + let specifier = originalSpecifier; + const url = tryParseURL(specifier); + if (url) { + if (url.protocol !== `file:`) + return defaultResolver(originalSpecifier, context, defaultResolver); + specifier = fileURLToPath(specifier); + } + const {parentURL, conditions = []} = context; + const issuer = parentURL ? fileURLToPath(parentURL) : process.cwd(); + const pnpapi = (_a = findPnpApi(issuer)) != null ? _a : url ? findPnpApi(specifier) : null; + if (!pnpapi) + return defaultResolver(originalSpecifier, context, defaultResolver); + const dependencyNameMatch = specifier.match(pathRegExp); + let allowLegacyResolve = false; + if (dependencyNameMatch) { + const [, dependencyName, subPath] = dependencyNameMatch; + if (subPath === ``) { + const resolved = pnpapi.resolveToUnqualified(`${dependencyName}/package.json`, issuer); + if (resolved) { + const content = await tryReadFile(resolved); + if (content) { + const pkg = JSON.parse(content); + allowLegacyResolve = pkg.exports == null; + } + } + } + } + const result = pnpapi.resolveRequest(specifier, issuer, { + conditions: new Set(conditions), + extensions: allowLegacyResolve ? void 0 : [] + }); + if (!result) + throw new Error(`Resolving '${specifier}' from '${issuer}' failed`); + return { + url: pathToFileURL(result).href + }; +} + +const binding = process.binding(`fs`); +const originalfstat = binding.fstat; +const ZIP_FD = 2147483648; +binding.fstat = function(...args) { + const [fd, useBigint, req] = args; + if ((fd & ZIP_FD) !== 0 && useBigint === false && req === void 0) { + try { + const stats = fs.fstatSync(fd); + return new Float64Array([ + stats.dev, + stats.mode, + stats.nlink, + stats.uid, + stats.gid, + stats.rdev, + stats.blksize, + stats.ino, + stats.size, + stats.blocks + ]); + } catch { + } + } + return originalfstat.apply(this, args); +}; + +const [major, minor] = process.versions.node.split(`.`).map((value) => parseInt(value, 10)); +const hasConsolidatedHooks = major > 16 || major === 16 && minor >= 12; +const resolve = resolve$1; +const getFormat = hasConsolidatedHooks ? void 0 : getFormat$1; +const getSource = hasConsolidatedHooks ? void 0 : getSource$1; +const load = hasConsolidatedHooks ? load$1 : void 0; + +export { getFormat, getSource, load, resolve }; diff --git a/.yarn/cache/@apidevtools-json-schema-ref-parser-npm-8.0.0-3f5ddbd534-3875f3c2fc.zip b/.yarn/cache/@apidevtools-json-schema-ref-parser-npm-8.0.0-3f5ddbd534-3875f3c2fc.zip new file mode 100644 index 00000000000..546722a2db2 Binary files /dev/null and b/.yarn/cache/@apidevtools-json-schema-ref-parser-npm-8.0.0-3f5ddbd534-3875f3c2fc.zip differ diff --git a/.yarn/cache/@babel-code-frame-npm-7.12.11-1a9a1b277f-3963eff3eb.zip b/.yarn/cache/@babel-code-frame-npm-7.12.11-1a9a1b277f-3963eff3eb.zip new file mode 100644 index 00000000000..404e74ab0b3 Binary files /dev/null and b/.yarn/cache/@babel-code-frame-npm-7.12.11-1a9a1b277f-3963eff3eb.zip differ diff --git a/.yarn/cache/@babel-code-frame-npm-7.16.7-093eb9e124-db2f7faa31.zip b/.yarn/cache/@babel-code-frame-npm-7.16.7-093eb9e124-db2f7faa31.zip new file mode 100644 index 00000000000..1c98cc9480c Binary files /dev/null and b/.yarn/cache/@babel-code-frame-npm-7.16.7-093eb9e124-db2f7faa31.zip differ diff --git a/.yarn/cache/@babel-compat-data-npm-7.16.4-9128f11195-4949ce54ea.zip b/.yarn/cache/@babel-compat-data-npm-7.16.4-9128f11195-4949ce54ea.zip new file mode 100644 index 00000000000..cac0550612b Binary files /dev/null and b/.yarn/cache/@babel-compat-data-npm-7.16.4-9128f11195-4949ce54ea.zip differ diff --git a/.yarn/cache/@babel-core-npm-7.16.0-5612f0ce31-a140f669da.zip b/.yarn/cache/@babel-core-npm-7.16.0-5612f0ce31-a140f669da.zip new file mode 100644 index 00000000000..3c18189a96d Binary files /dev/null and b/.yarn/cache/@babel-core-npm-7.16.0-5612f0ce31-a140f669da.zip differ diff --git a/.yarn/cache/@babel-generator-npm-7.17.3-b206625c17-ddf70e3489.zip b/.yarn/cache/@babel-generator-npm-7.17.3-b206625c17-ddf70e3489.zip new file mode 100644 index 00000000000..a6edcec2aed Binary files /dev/null and b/.yarn/cache/@babel-generator-npm-7.17.3-b206625c17-ddf70e3489.zip differ diff --git a/.yarn/cache/@babel-helper-annotate-as-pure-npm-7.16.0-7d5d6eb28a-0db7610698.zip b/.yarn/cache/@babel-helper-annotate-as-pure-npm-7.16.0-7d5d6eb28a-0db7610698.zip new file mode 100644 index 00000000000..a8c78d0ac52 Binary files /dev/null and b/.yarn/cache/@babel-helper-annotate-as-pure-npm-7.16.0-7d5d6eb28a-0db7610698.zip differ diff --git a/.yarn/cache/@babel-helper-builder-binary-assignment-operator-visitor-npm-7.16.0-8218316996-01beb9f3f2.zip b/.yarn/cache/@babel-helper-builder-binary-assignment-operator-visitor-npm-7.16.0-8218316996-01beb9f3f2.zip new file mode 100644 index 00000000000..fd17eb752f4 Binary files /dev/null and b/.yarn/cache/@babel-helper-builder-binary-assignment-operator-visitor-npm-7.16.0-8218316996-01beb9f3f2.zip differ diff --git a/.yarn/cache/@babel-helper-compilation-targets-npm-7.16.3-200287cc80-038bcd43ac.zip b/.yarn/cache/@babel-helper-compilation-targets-npm-7.16.3-200287cc80-038bcd43ac.zip new file mode 100644 index 00000000000..3d663e883cd Binary files /dev/null and b/.yarn/cache/@babel-helper-compilation-targets-npm-7.16.3-200287cc80-038bcd43ac.zip differ diff --git a/.yarn/cache/@babel-helper-create-class-features-plugin-npm-7.16.0-99dc71616c-0f7d1b8d41.zip b/.yarn/cache/@babel-helper-create-class-features-plugin-npm-7.16.0-99dc71616c-0f7d1b8d41.zip new file mode 100644 index 00000000000..ff9f0a134d6 Binary files /dev/null and b/.yarn/cache/@babel-helper-create-class-features-plugin-npm-7.16.0-99dc71616c-0f7d1b8d41.zip differ diff --git a/.yarn/cache/@babel-helper-create-regexp-features-plugin-npm-7.16.0-9afb84be3e-d6230477e1.zip b/.yarn/cache/@babel-helper-create-regexp-features-plugin-npm-7.16.0-9afb84be3e-d6230477e1.zip new file mode 100644 index 00000000000..7d0a94fa8ad Binary files /dev/null and b/.yarn/cache/@babel-helper-create-regexp-features-plugin-npm-7.16.0-9afb84be3e-d6230477e1.zip differ diff --git a/.yarn/cache/@babel-helper-define-polyfill-provider-npm-0.3.0-c19f133e9a-372378ac42.zip b/.yarn/cache/@babel-helper-define-polyfill-provider-npm-0.3.0-c19f133e9a-372378ac42.zip new file mode 100644 index 00000000000..ea40b1a97bb Binary files /dev/null and b/.yarn/cache/@babel-helper-define-polyfill-provider-npm-0.3.0-c19f133e9a-372378ac42.zip differ diff --git a/.yarn/cache/@babel-helper-environment-visitor-npm-7.16.7-3ee2ba2019-c03a10105d.zip b/.yarn/cache/@babel-helper-environment-visitor-npm-7.16.7-3ee2ba2019-c03a10105d.zip new file mode 100644 index 00000000000..525f2b2fcf8 Binary files /dev/null and b/.yarn/cache/@babel-helper-environment-visitor-npm-7.16.7-3ee2ba2019-c03a10105d.zip differ diff --git a/.yarn/cache/@babel-helper-explode-assignable-expression-npm-7.16.0-c7497452bc-563352b5e9.zip b/.yarn/cache/@babel-helper-explode-assignable-expression-npm-7.16.0-c7497452bc-563352b5e9.zip new file mode 100644 index 00000000000..f481f8d2c90 Binary files /dev/null and b/.yarn/cache/@babel-helper-explode-assignable-expression-npm-7.16.0-c7497452bc-563352b5e9.zip differ diff --git a/.yarn/cache/@babel-helper-function-name-npm-7.16.7-aa24c7b296-fc77cbe7b1.zip b/.yarn/cache/@babel-helper-function-name-npm-7.16.7-aa24c7b296-fc77cbe7b1.zip new file mode 100644 index 00000000000..3a05350f02c Binary files /dev/null and b/.yarn/cache/@babel-helper-function-name-npm-7.16.7-aa24c7b296-fc77cbe7b1.zip differ diff --git a/.yarn/cache/@babel-helper-get-function-arity-npm-7.16.7-987b1b1bed-25d969fb20.zip b/.yarn/cache/@babel-helper-get-function-arity-npm-7.16.7-987b1b1bed-25d969fb20.zip new file mode 100644 index 00000000000..cf5b13d58cf Binary files /dev/null and b/.yarn/cache/@babel-helper-get-function-arity-npm-7.16.7-987b1b1bed-25d969fb20.zip differ diff --git a/.yarn/cache/@babel-helper-hoist-variables-npm-7.16.7-25cc3abba4-6ae1641f4a.zip b/.yarn/cache/@babel-helper-hoist-variables-npm-7.16.7-25cc3abba4-6ae1641f4a.zip new file mode 100644 index 00000000000..81cfcbbfad0 Binary files /dev/null and b/.yarn/cache/@babel-helper-hoist-variables-npm-7.16.7-25cc3abba4-6ae1641f4a.zip differ diff --git a/.yarn/cache/@babel-helper-member-expression-to-functions-npm-7.16.0-714f06863b-58ef8e3a4a.zip b/.yarn/cache/@babel-helper-member-expression-to-functions-npm-7.16.0-714f06863b-58ef8e3a4a.zip new file mode 100644 index 00000000000..58296c7260a Binary files /dev/null and b/.yarn/cache/@babel-helper-member-expression-to-functions-npm-7.16.0-714f06863b-58ef8e3a4a.zip differ diff --git a/.yarn/cache/@babel-helper-module-imports-npm-7.16.0-ae62b2ede7-8e1eb9ac39.zip b/.yarn/cache/@babel-helper-module-imports-npm-7.16.0-ae62b2ede7-8e1eb9ac39.zip new file mode 100644 index 00000000000..005d4722e69 Binary files /dev/null and b/.yarn/cache/@babel-helper-module-imports-npm-7.16.0-ae62b2ede7-8e1eb9ac39.zip differ diff --git a/.yarn/cache/@babel-helper-module-transforms-npm-7.16.0-928840049e-a3d0e5556f.zip b/.yarn/cache/@babel-helper-module-transforms-npm-7.16.0-928840049e-a3d0e5556f.zip new file mode 100644 index 00000000000..b7736b6304e Binary files /dev/null and b/.yarn/cache/@babel-helper-module-transforms-npm-7.16.0-928840049e-a3d0e5556f.zip differ diff --git a/.yarn/cache/@babel-helper-optimise-call-expression-npm-7.16.0-fd091f8fdf-121ae6054f.zip b/.yarn/cache/@babel-helper-optimise-call-expression-npm-7.16.0-fd091f8fdf-121ae6054f.zip new file mode 100644 index 00000000000..2d6f5656a22 Binary files /dev/null and b/.yarn/cache/@babel-helper-optimise-call-expression-npm-7.16.0-fd091f8fdf-121ae6054f.zip differ diff --git a/.yarn/cache/@babel-helper-plugin-utils-npm-7.14.5-e35eef11cb-fe20e90a24.zip b/.yarn/cache/@babel-helper-plugin-utils-npm-7.14.5-e35eef11cb-fe20e90a24.zip new file mode 100644 index 00000000000..47da224221a Binary files /dev/null and b/.yarn/cache/@babel-helper-plugin-utils-npm-7.14.5-e35eef11cb-fe20e90a24.zip differ diff --git a/.yarn/cache/@babel-helper-remap-async-to-generator-npm-7.16.4-691d3036ec-debe997695.zip b/.yarn/cache/@babel-helper-remap-async-to-generator-npm-7.16.4-691d3036ec-debe997695.zip new file mode 100644 index 00000000000..569a2364aad Binary files /dev/null and b/.yarn/cache/@babel-helper-remap-async-to-generator-npm-7.16.4-691d3036ec-debe997695.zip differ diff --git a/.yarn/cache/@babel-helper-replace-supers-npm-7.16.0-e04b4caf96-61f04bbe05.zip b/.yarn/cache/@babel-helper-replace-supers-npm-7.16.0-e04b4caf96-61f04bbe05.zip new file mode 100644 index 00000000000..223db7676c2 Binary files /dev/null and b/.yarn/cache/@babel-helper-replace-supers-npm-7.16.0-e04b4caf96-61f04bbe05.zip differ diff --git a/.yarn/cache/@babel-helper-simple-access-npm-7.16.0-d2675c6f1c-2d7155f318.zip b/.yarn/cache/@babel-helper-simple-access-npm-7.16.0-d2675c6f1c-2d7155f318.zip new file mode 100644 index 00000000000..f5555922b26 Binary files /dev/null and b/.yarn/cache/@babel-helper-simple-access-npm-7.16.0-d2675c6f1c-2d7155f318.zip differ diff --git a/.yarn/cache/@babel-helper-skip-transparent-expression-wrappers-npm-7.16.0-caad6e8361-b9ed2896eb.zip b/.yarn/cache/@babel-helper-skip-transparent-expression-wrappers-npm-7.16.0-caad6e8361-b9ed2896eb.zip new file mode 100644 index 00000000000..3b12e0f1b0a Binary files /dev/null and b/.yarn/cache/@babel-helper-skip-transparent-expression-wrappers-npm-7.16.0-caad6e8361-b9ed2896eb.zip differ diff --git a/.yarn/cache/@babel-helper-split-export-declaration-npm-7.16.7-5b9ae90171-e10aaf1354.zip b/.yarn/cache/@babel-helper-split-export-declaration-npm-7.16.7-5b9ae90171-e10aaf1354.zip new file mode 100644 index 00000000000..5249cf09fe3 Binary files /dev/null and b/.yarn/cache/@babel-helper-split-export-declaration-npm-7.16.7-5b9ae90171-e10aaf1354.zip differ diff --git a/.yarn/cache/@babel-helper-validator-identifier-npm-7.16.7-8599fb00fc-dbb3db9d18.zip b/.yarn/cache/@babel-helper-validator-identifier-npm-7.16.7-8599fb00fc-dbb3db9d18.zip new file mode 100644 index 00000000000..0cde98edb13 Binary files /dev/null and b/.yarn/cache/@babel-helper-validator-identifier-npm-7.16.7-8599fb00fc-dbb3db9d18.zip differ diff --git a/.yarn/cache/@babel-helper-validator-option-npm-7.14.5-fd38dcf0bc-1b25c34a5c.zip b/.yarn/cache/@babel-helper-validator-option-npm-7.14.5-fd38dcf0bc-1b25c34a5c.zip new file mode 100644 index 00000000000..2587e741a40 Binary files /dev/null and b/.yarn/cache/@babel-helper-validator-option-npm-7.14.5-fd38dcf0bc-1b25c34a5c.zip differ diff --git a/.yarn/cache/@babel-helper-wrap-function-npm-7.16.0-58e751a57c-2bb4e05f49.zip b/.yarn/cache/@babel-helper-wrap-function-npm-7.16.0-58e751a57c-2bb4e05f49.zip new file mode 100644 index 00000000000..36a77be012c Binary files /dev/null and b/.yarn/cache/@babel-helper-wrap-function-npm-7.16.0-58e751a57c-2bb4e05f49.zip differ diff --git a/.yarn/cache/@babel-helpers-npm-7.16.3-02251c435f-b725b1aab7.zip b/.yarn/cache/@babel-helpers-npm-7.16.3-02251c435f-b725b1aab7.zip new file mode 100644 index 00000000000..57d5ec5dc1d Binary files /dev/null and b/.yarn/cache/@babel-helpers-npm-7.16.3-02251c435f-b725b1aab7.zip differ diff --git a/.yarn/cache/@babel-highlight-npm-7.16.10-626c03326c-1f1bdd752a.zip b/.yarn/cache/@babel-highlight-npm-7.16.10-626c03326c-1f1bdd752a.zip new file mode 100644 index 00000000000..18595b7127d Binary files /dev/null and b/.yarn/cache/@babel-highlight-npm-7.16.10-626c03326c-1f1bdd752a.zip differ diff --git a/.yarn/cache/@babel-parser-npm-7.17.3-1c3b6747e0-311869baef.zip b/.yarn/cache/@babel-parser-npm-7.17.3-1c3b6747e0-311869baef.zip new file mode 100644 index 00000000000..0fbe1bc5948 Binary files /dev/null and b/.yarn/cache/@babel-parser-npm-7.17.3-1c3b6747e0-311869baef.zip differ diff --git a/.yarn/cache/@babel-plugin-bugfix-safari-id-destructuring-collision-in-function-expression-npm-7.16.2-1aa5b1f875-6ed9dbbf18.zip b/.yarn/cache/@babel-plugin-bugfix-safari-id-destructuring-collision-in-function-expression-npm-7.16.2-1aa5b1f875-6ed9dbbf18.zip new file mode 100644 index 00000000000..76ad2afa84c Binary files /dev/null and b/.yarn/cache/@babel-plugin-bugfix-safari-id-destructuring-collision-in-function-expression-npm-7.16.2-1aa5b1f875-6ed9dbbf18.zip differ diff --git a/.yarn/cache/@babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining-npm-7.16.0-f3fb88813d-bb11547929.zip b/.yarn/cache/@babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining-npm-7.16.0-f3fb88813d-bb11547929.zip new file mode 100644 index 00000000000..83b0f15191a Binary files /dev/null and b/.yarn/cache/@babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining-npm-7.16.0-f3fb88813d-bb11547929.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-async-generator-functions-npm-7.16.4-8379d8fc90-dcd5a76ee1.zip b/.yarn/cache/@babel-plugin-proposal-async-generator-functions-npm-7.16.4-8379d8fc90-dcd5a76ee1.zip new file mode 100644 index 00000000000..856a8c083d4 Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-async-generator-functions-npm-7.16.4-8379d8fc90-dcd5a76ee1.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-class-properties-npm-7.16.0-9106ec25a5-b1665ced55.zip b/.yarn/cache/@babel-plugin-proposal-class-properties-npm-7.16.0-9106ec25a5-b1665ced55.zip new file mode 100644 index 00000000000..77f7756343d Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-class-properties-npm-7.16.0-9106ec25a5-b1665ced55.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-class-static-block-npm-7.16.0-4ac545628c-59c4bb3d6a.zip b/.yarn/cache/@babel-plugin-proposal-class-static-block-npm-7.16.0-4ac545628c-59c4bb3d6a.zip new file mode 100644 index 00000000000..c65d5c626ee Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-class-static-block-npm-7.16.0-4ac545628c-59c4bb3d6a.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-dynamic-import-npm-7.16.0-8de1a50b8f-4027da6404.zip b/.yarn/cache/@babel-plugin-proposal-dynamic-import-npm-7.16.0-8de1a50b8f-4027da6404.zip new file mode 100644 index 00000000000..80cea5ba82e Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-dynamic-import-npm-7.16.0-8de1a50b8f-4027da6404.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-export-namespace-from-npm-7.16.0-3523b50929-0bdc166ac4.zip b/.yarn/cache/@babel-plugin-proposal-export-namespace-from-npm-7.16.0-3523b50929-0bdc166ac4.zip new file mode 100644 index 00000000000..763bb06aaad Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-export-namespace-from-npm-7.16.0-3523b50929-0bdc166ac4.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-json-strings-npm-7.16.0-1070c01042-fa93be8eff.zip b/.yarn/cache/@babel-plugin-proposal-json-strings-npm-7.16.0-1070c01042-fa93be8eff.zip new file mode 100644 index 00000000000..cf75cb88fe3 Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-json-strings-npm-7.16.0-1070c01042-fa93be8eff.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-logical-assignment-operators-npm-7.16.0-8163433ffc-7e6cd10248.zip b/.yarn/cache/@babel-plugin-proposal-logical-assignment-operators-npm-7.16.0-8163433ffc-7e6cd10248.zip new file mode 100644 index 00000000000..6d5ffbdad3f Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-logical-assignment-operators-npm-7.16.0-8163433ffc-7e6cd10248.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-nullish-coalescing-operator-npm-7.16.0-bdd28f11cb-e50f949299.zip b/.yarn/cache/@babel-plugin-proposal-nullish-coalescing-operator-npm-7.16.0-bdd28f11cb-e50f949299.zip new file mode 100644 index 00000000000..f440cfe51c5 Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-nullish-coalescing-operator-npm-7.16.0-bdd28f11cb-e50f949299.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-numeric-separator-npm-7.16.0-5852a76307-eb7895a4f3.zip b/.yarn/cache/@babel-plugin-proposal-numeric-separator-npm-7.16.0-5852a76307-eb7895a4f3.zip new file mode 100644 index 00000000000..f5fa9278e7b Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-numeric-separator-npm-7.16.0-5852a76307-eb7895a4f3.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-object-rest-spread-npm-7.16.0-f193853f3b-c7716ba50e.zip b/.yarn/cache/@babel-plugin-proposal-object-rest-spread-npm-7.16.0-f193853f3b-c7716ba50e.zip new file mode 100644 index 00000000000..7d071cdc0d8 Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-object-rest-spread-npm-7.16.0-f193853f3b-c7716ba50e.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-optional-catch-binding-npm-7.16.0-122cc09c2e-5003a1d48f.zip b/.yarn/cache/@babel-plugin-proposal-optional-catch-binding-npm-7.16.0-122cc09c2e-5003a1d48f.zip new file mode 100644 index 00000000000..b76368e8090 Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-optional-catch-binding-npm-7.16.0-122cc09c2e-5003a1d48f.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-optional-chaining-npm-7.16.0-fe3431862e-8301e08292.zip b/.yarn/cache/@babel-plugin-proposal-optional-chaining-npm-7.16.0-fe3431862e-8301e08292.zip new file mode 100644 index 00000000000..dbbde4e9ff3 Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-optional-chaining-npm-7.16.0-fe3431862e-8301e08292.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-private-methods-npm-7.16.0-9036fdd8d2-6f648f54ea.zip b/.yarn/cache/@babel-plugin-proposal-private-methods-npm-7.16.0-9036fdd8d2-6f648f54ea.zip new file mode 100644 index 00000000000..8ca622bdb7d Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-private-methods-npm-7.16.0-9036fdd8d2-6f648f54ea.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-private-property-in-object-npm-7.16.0-84109b160c-9098fb34f4.zip b/.yarn/cache/@babel-plugin-proposal-private-property-in-object-npm-7.16.0-84109b160c-9098fb34f4.zip new file mode 100644 index 00000000000..e6537aa35c0 Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-private-property-in-object-npm-7.16.0-84109b160c-9098fb34f4.zip differ diff --git a/.yarn/cache/@babel-plugin-proposal-unicode-property-regex-npm-7.16.0-13487b534c-f26b76c9aa.zip b/.yarn/cache/@babel-plugin-proposal-unicode-property-regex-npm-7.16.0-13487b534c-f26b76c9aa.zip new file mode 100644 index 00000000000..6dacb2f9751 Binary files /dev/null and b/.yarn/cache/@babel-plugin-proposal-unicode-property-regex-npm-7.16.0-13487b534c-f26b76c9aa.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-async-generators-npm-7.8.4-d10cf993c9-7ed1c1d9b9.zip b/.yarn/cache/@babel-plugin-syntax-async-generators-npm-7.8.4-d10cf993c9-7ed1c1d9b9.zip new file mode 100644 index 00000000000..bc3c60f08b3 Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-async-generators-npm-7.8.4-d10cf993c9-7ed1c1d9b9.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-class-properties-npm-7.12.13-002ee9d930-24f34b196d.zip b/.yarn/cache/@babel-plugin-syntax-class-properties-npm-7.12.13-002ee9d930-24f34b196d.zip new file mode 100644 index 00000000000..7bddd9a6f60 Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-class-properties-npm-7.12.13-002ee9d930-24f34b196d.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-class-static-block-npm-7.14.5-7bdd0ff1b3-3e80814b5b.zip b/.yarn/cache/@babel-plugin-syntax-class-static-block-npm-7.14.5-7bdd0ff1b3-3e80814b5b.zip new file mode 100644 index 00000000000..025890a465f Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-class-static-block-npm-7.14.5-7bdd0ff1b3-3e80814b5b.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-dynamic-import-npm-7.8.3-fb9ff5634a-ce307af83c.zip b/.yarn/cache/@babel-plugin-syntax-dynamic-import-npm-7.8.3-fb9ff5634a-ce307af83c.zip new file mode 100644 index 00000000000..a41ecb49c10 Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-dynamic-import-npm-7.8.3-fb9ff5634a-ce307af83c.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-export-namespace-from-npm-7.8.3-1747201aa9-85740478be.zip b/.yarn/cache/@babel-plugin-syntax-export-namespace-from-npm-7.8.3-1747201aa9-85740478be.zip new file mode 100644 index 00000000000..f7f1bab987c Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-export-namespace-from-npm-7.8.3-1747201aa9-85740478be.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-json-strings-npm-7.8.3-6dc7848179-bf5aea1f31.zip b/.yarn/cache/@babel-plugin-syntax-json-strings-npm-7.8.3-6dc7848179-bf5aea1f31.zip new file mode 100644 index 00000000000..027e0bdcc1f Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-json-strings-npm-7.8.3-6dc7848179-bf5aea1f31.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-logical-assignment-operators-npm-7.10.4-72ae00fdf6-aff3357703.zip b/.yarn/cache/@babel-plugin-syntax-logical-assignment-operators-npm-7.10.4-72ae00fdf6-aff3357703.zip new file mode 100644 index 00000000000..ddbc188c520 Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-logical-assignment-operators-npm-7.10.4-72ae00fdf6-aff3357703.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-nullish-coalescing-operator-npm-7.8.3-8a723173b5-87aca49189.zip b/.yarn/cache/@babel-plugin-syntax-nullish-coalescing-operator-npm-7.8.3-8a723173b5-87aca49189.zip new file mode 100644 index 00000000000..91115bda03b Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-nullish-coalescing-operator-npm-7.8.3-8a723173b5-87aca49189.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-numeric-separator-npm-7.10.4-81444be605-01ec5547bd.zip b/.yarn/cache/@babel-plugin-syntax-numeric-separator-npm-7.10.4-81444be605-01ec5547bd.zip new file mode 100644 index 00000000000..f541ce07bf2 Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-numeric-separator-npm-7.10.4-81444be605-01ec5547bd.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-object-rest-spread-npm-7.8.3-60bd05b6ae-fddcf581a5.zip b/.yarn/cache/@babel-plugin-syntax-object-rest-spread-npm-7.8.3-60bd05b6ae-fddcf581a5.zip new file mode 100644 index 00000000000..9ad98a0b2d5 Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-object-rest-spread-npm-7.8.3-60bd05b6ae-fddcf581a5.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-optional-catch-binding-npm-7.8.3-ce337427d8-910d90e72b.zip b/.yarn/cache/@babel-plugin-syntax-optional-catch-binding-npm-7.8.3-ce337427d8-910d90e72b.zip new file mode 100644 index 00000000000..dbc1482ba38 Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-optional-catch-binding-npm-7.8.3-ce337427d8-910d90e72b.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-optional-chaining-npm-7.8.3-f3f3c79579-eef94d53a1.zip b/.yarn/cache/@babel-plugin-syntax-optional-chaining-npm-7.8.3-f3f3c79579-eef94d53a1.zip new file mode 100644 index 00000000000..1a12bdbd7a5 Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-optional-chaining-npm-7.8.3-f3f3c79579-eef94d53a1.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-private-property-in-object-npm-7.14.5-ee837fdbb2-b317174783.zip b/.yarn/cache/@babel-plugin-syntax-private-property-in-object-npm-7.14.5-ee837fdbb2-b317174783.zip new file mode 100644 index 00000000000..f4e1801301d Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-private-property-in-object-npm-7.14.5-ee837fdbb2-b317174783.zip differ diff --git a/.yarn/cache/@babel-plugin-syntax-top-level-await-npm-7.14.5-60a0a2e83b-bbd1a56b09.zip b/.yarn/cache/@babel-plugin-syntax-top-level-await-npm-7.14.5-60a0a2e83b-bbd1a56b09.zip new file mode 100644 index 00000000000..041d0452f44 Binary files /dev/null and b/.yarn/cache/@babel-plugin-syntax-top-level-await-npm-7.14.5-60a0a2e83b-bbd1a56b09.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-arrow-functions-npm-7.16.0-41f0acc5fd-ff64730042.zip b/.yarn/cache/@babel-plugin-transform-arrow-functions-npm-7.16.0-41f0acc5fd-ff64730042.zip new file mode 100644 index 00000000000..a66228c05b3 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-arrow-functions-npm-7.16.0-41f0acc5fd-ff64730042.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-async-to-generator-npm-7.16.0-86f0f376d0-2ebf505f43.zip b/.yarn/cache/@babel-plugin-transform-async-to-generator-npm-7.16.0-86f0f376d0-2ebf505f43.zip new file mode 100644 index 00000000000..bd70b129a3f Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-async-to-generator-npm-7.16.0-86f0f376d0-2ebf505f43.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-block-scoped-functions-npm-7.16.0-b0a5ff16fd-f7efc5d8ce.zip b/.yarn/cache/@babel-plugin-transform-block-scoped-functions-npm-7.16.0-b0a5ff16fd-f7efc5d8ce.zip new file mode 100644 index 00000000000..9c004a688f8 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-block-scoped-functions-npm-7.16.0-b0a5ff16fd-f7efc5d8ce.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-block-scoping-npm-7.16.0-79b754526e-e5bcb9eeed.zip b/.yarn/cache/@babel-plugin-transform-block-scoping-npm-7.16.0-79b754526e-e5bcb9eeed.zip new file mode 100644 index 00000000000..304b5f7ccb9 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-block-scoping-npm-7.16.0-79b754526e-e5bcb9eeed.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-classes-npm-7.16.0-eb06677280-7db4729604.zip b/.yarn/cache/@babel-plugin-transform-classes-npm-7.16.0-eb06677280-7db4729604.zip new file mode 100644 index 00000000000..a8e556dac41 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-classes-npm-7.16.0-eb06677280-7db4729604.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-computed-properties-npm-7.16.0-e7e3f1f458-0f86de419c.zip b/.yarn/cache/@babel-plugin-transform-computed-properties-npm-7.16.0-e7e3f1f458-0f86de419c.zip new file mode 100644 index 00000000000..a4dda26079f Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-computed-properties-npm-7.16.0-e7e3f1f458-0f86de419c.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-destructuring-npm-7.16.0-e06e24c3ed-0a499c9abd.zip b/.yarn/cache/@babel-plugin-transform-destructuring-npm-7.16.0-e06e24c3ed-0a499c9abd.zip new file mode 100644 index 00000000000..4845288605a Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-destructuring-npm-7.16.0-e06e24c3ed-0a499c9abd.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-dotall-regex-npm-7.16.0-5369887cb9-c1f381f0d4.zip b/.yarn/cache/@babel-plugin-transform-dotall-regex-npm-7.16.0-5369887cb9-c1f381f0d4.zip new file mode 100644 index 00000000000..85ce4ca39dd Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-dotall-regex-npm-7.16.0-5369887cb9-c1f381f0d4.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-duplicate-keys-npm-7.16.0-de841439f8-66f09487fd.zip b/.yarn/cache/@babel-plugin-transform-duplicate-keys-npm-7.16.0-de841439f8-66f09487fd.zip new file mode 100644 index 00000000000..dc12a56bff9 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-duplicate-keys-npm-7.16.0-de841439f8-66f09487fd.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-exponentiation-operator-npm-7.16.0-2f4326910a-22e1d4804a.zip b/.yarn/cache/@babel-plugin-transform-exponentiation-operator-npm-7.16.0-2f4326910a-22e1d4804a.zip new file mode 100644 index 00000000000..9e6e4d6737e Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-exponentiation-operator-npm-7.16.0-2f4326910a-22e1d4804a.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-for-of-npm-7.16.0-fa8c8350b0-504d967b30.zip b/.yarn/cache/@babel-plugin-transform-for-of-npm-7.16.0-fa8c8350b0-504d967b30.zip new file mode 100644 index 00000000000..61467a22dd1 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-for-of-npm-7.16.0-fa8c8350b0-504d967b30.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-function-name-npm-7.16.0-51d8c4f9e8-289f4fce26.zip b/.yarn/cache/@babel-plugin-transform-function-name-npm-7.16.0-51d8c4f9e8-289f4fce26.zip new file mode 100644 index 00000000000..52bbfe978c0 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-function-name-npm-7.16.0-51d8c4f9e8-289f4fce26.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-literals-npm-7.16.0-14c027a2e4-7291771c76.zip b/.yarn/cache/@babel-plugin-transform-literals-npm-7.16.0-14c027a2e4-7291771c76.zip new file mode 100644 index 00000000000..70f39d8343d Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-literals-npm-7.16.0-14c027a2e4-7291771c76.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-member-expression-literals-npm-7.16.0-e6f2ca03f6-d5ed6cf840.zip b/.yarn/cache/@babel-plugin-transform-member-expression-literals-npm-7.16.0-e6f2ca03f6-d5ed6cf840.zip new file mode 100644 index 00000000000..77826aea5a3 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-member-expression-literals-npm-7.16.0-e6f2ca03f6-d5ed6cf840.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-modules-amd-npm-7.16.0-d080840cd8-c37ccb8cd7.zip b/.yarn/cache/@babel-plugin-transform-modules-amd-npm-7.16.0-d080840cd8-c37ccb8cd7.zip new file mode 100644 index 00000000000..e7716e99fbd Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-modules-amd-npm-7.16.0-d080840cd8-c37ccb8cd7.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-modules-commonjs-npm-7.16.0-2af1b97bf1-a7e43670f5.zip b/.yarn/cache/@babel-plugin-transform-modules-commonjs-npm-7.16.0-2af1b97bf1-a7e43670f5.zip new file mode 100644 index 00000000000..beb2f38a5d2 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-modules-commonjs-npm-7.16.0-2af1b97bf1-a7e43670f5.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-modules-systemjs-npm-7.16.0-1c528e3b5f-4aa9bd45a4.zip b/.yarn/cache/@babel-plugin-transform-modules-systemjs-npm-7.16.0-1c528e3b5f-4aa9bd45a4.zip new file mode 100644 index 00000000000..7f7b6e8d5df Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-modules-systemjs-npm-7.16.0-1c528e3b5f-4aa9bd45a4.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-modules-umd-npm-7.16.0-d01b0d9cde-b07d41eae3.zip b/.yarn/cache/@babel-plugin-transform-modules-umd-npm-7.16.0-d01b0d9cde-b07d41eae3.zip new file mode 100644 index 00000000000..692cd71ef44 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-modules-umd-npm-7.16.0-d01b0d9cde-b07d41eae3.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-named-capturing-groups-regex-npm-7.16.0-6c90f144b8-758a87aca6.zip b/.yarn/cache/@babel-plugin-transform-named-capturing-groups-regex-npm-7.16.0-6c90f144b8-758a87aca6.zip new file mode 100644 index 00000000000..c69864d9d1a Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-named-capturing-groups-regex-npm-7.16.0-6c90f144b8-758a87aca6.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-new-target-npm-7.16.0-5d7ffa3fc5-c741ba3e84.zip b/.yarn/cache/@babel-plugin-transform-new-target-npm-7.16.0-5d7ffa3fc5-c741ba3e84.zip new file mode 100644 index 00000000000..5874433ebba Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-new-target-npm-7.16.0-5d7ffa3fc5-c741ba3e84.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-object-super-npm-7.16.0-ac4f46c62c-b6ed0a8f5a.zip b/.yarn/cache/@babel-plugin-transform-object-super-npm-7.16.0-ac4f46c62c-b6ed0a8f5a.zip new file mode 100644 index 00000000000..19f5d2a0d0d Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-object-super-npm-7.16.0-ac4f46c62c-b6ed0a8f5a.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-parameters-npm-7.16.3-37e31de570-7c0154fa66.zip b/.yarn/cache/@babel-plugin-transform-parameters-npm-7.16.3-37e31de570-7c0154fa66.zip new file mode 100644 index 00000000000..23a2824ad3b Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-parameters-npm-7.16.3-37e31de570-7c0154fa66.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-property-literals-npm-7.16.0-ac6d37bdd0-e9eb9355db.zip b/.yarn/cache/@babel-plugin-transform-property-literals-npm-7.16.0-ac6d37bdd0-e9eb9355db.zip new file mode 100644 index 00000000000..ebacee93664 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-property-literals-npm-7.16.0-ac6d37bdd0-e9eb9355db.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-regenerator-npm-7.16.0-498b430132-32b1b43f8d.zip b/.yarn/cache/@babel-plugin-transform-regenerator-npm-7.16.0-498b430132-32b1b43f8d.zip new file mode 100644 index 00000000000..5270878bfe4 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-regenerator-npm-7.16.0-498b430132-32b1b43f8d.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-reserved-words-npm-7.16.0-1acaa9020b-7a8288cfe2.zip b/.yarn/cache/@babel-plugin-transform-reserved-words-npm-7.16.0-1acaa9020b-7a8288cfe2.zip new file mode 100644 index 00000000000..68123f9429c Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-reserved-words-npm-7.16.0-1acaa9020b-7a8288cfe2.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-shorthand-properties-npm-7.16.0-b33ad8f611-7ae0f218aa.zip b/.yarn/cache/@babel-plugin-transform-shorthand-properties-npm-7.16.0-b33ad8f611-7ae0f218aa.zip new file mode 100644 index 00000000000..2c82dcc5e64 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-shorthand-properties-npm-7.16.0-b33ad8f611-7ae0f218aa.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-spread-npm-7.16.0-5cf66a86c2-c295ef5e32.zip b/.yarn/cache/@babel-plugin-transform-spread-npm-7.16.0-5cf66a86c2-c295ef5e32.zip new file mode 100644 index 00000000000..17c703bd38c Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-spread-npm-7.16.0-5cf66a86c2-c295ef5e32.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-sticky-regex-npm-7.16.0-fe13551c5b-80c7ccb797.zip b/.yarn/cache/@babel-plugin-transform-sticky-regex-npm-7.16.0-fe13551c5b-80c7ccb797.zip new file mode 100644 index 00000000000..7d51ee1a3a5 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-sticky-regex-npm-7.16.0-fe13551c5b-80c7ccb797.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-template-literals-npm-7.16.0-28db7976f7-230638ee56.zip b/.yarn/cache/@babel-plugin-transform-template-literals-npm-7.16.0-28db7976f7-230638ee56.zip new file mode 100644 index 00000000000..f7a94c57055 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-template-literals-npm-7.16.0-28db7976f7-230638ee56.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-typeof-symbol-npm-7.16.0-77d5f48897-60e91d57b3.zip b/.yarn/cache/@babel-plugin-transform-typeof-symbol-npm-7.16.0-77d5f48897-60e91d57b3.zip new file mode 100644 index 00000000000..1358f0a9dbe Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-typeof-symbol-npm-7.16.0-77d5f48897-60e91d57b3.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-unicode-escapes-npm-7.16.0-dab90cd689-63ac80d6b7.zip b/.yarn/cache/@babel-plugin-transform-unicode-escapes-npm-7.16.0-dab90cd689-63ac80d6b7.zip new file mode 100644 index 00000000000..cd26473a13a Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-unicode-escapes-npm-7.16.0-dab90cd689-63ac80d6b7.zip differ diff --git a/.yarn/cache/@babel-plugin-transform-unicode-regex-npm-7.16.0-5f79e96758-61e498425f.zip b/.yarn/cache/@babel-plugin-transform-unicode-regex-npm-7.16.0-5f79e96758-61e498425f.zip new file mode 100644 index 00000000000..4af9042c070 Binary files /dev/null and b/.yarn/cache/@babel-plugin-transform-unicode-regex-npm-7.16.0-5f79e96758-61e498425f.zip differ diff --git a/.yarn/cache/@babel-preset-env-npm-7.16.4-115941abdf-72a5d7e460.zip b/.yarn/cache/@babel-preset-env-npm-7.16.4-115941abdf-72a5d7e460.zip new file mode 100644 index 00000000000..80e734693e6 Binary files /dev/null and b/.yarn/cache/@babel-preset-env-npm-7.16.4-115941abdf-72a5d7e460.zip differ diff --git a/.yarn/cache/@babel-preset-modules-npm-0.1.5-15ffcd64c2-8430e0e9e9.zip b/.yarn/cache/@babel-preset-modules-npm-0.1.5-15ffcd64c2-8430e0e9e9.zip new file mode 100644 index 00000000000..874f013a15d Binary files /dev/null and b/.yarn/cache/@babel-preset-modules-npm-0.1.5-15ffcd64c2-8430e0e9e9.zip differ diff --git a/.yarn/cache/@babel-runtime-npm-7.17.9-c52a5e9d27-4d56bdb828.zip b/.yarn/cache/@babel-runtime-npm-7.17.9-c52a5e9d27-4d56bdb828.zip new file mode 100644 index 00000000000..db1a6eda61c Binary files /dev/null and b/.yarn/cache/@babel-runtime-npm-7.17.9-c52a5e9d27-4d56bdb828.zip differ diff --git a/.yarn/cache/@babel-template-npm-7.16.7-a18e444be8-10cd112e89.zip b/.yarn/cache/@babel-template-npm-7.16.7-a18e444be8-10cd112e89.zip new file mode 100644 index 00000000000..6bda3b8fdab Binary files /dev/null and b/.yarn/cache/@babel-template-npm-7.16.7-a18e444be8-10cd112e89.zip differ diff --git a/.yarn/cache/@babel-traverse-npm-7.17.3-c2bff3e671-780d7ecf71.zip b/.yarn/cache/@babel-traverse-npm-7.17.3-c2bff3e671-780d7ecf71.zip new file mode 100644 index 00000000000..0994a896c22 Binary files /dev/null and b/.yarn/cache/@babel-traverse-npm-7.17.3-c2bff3e671-780d7ecf71.zip differ diff --git a/.yarn/cache/@babel-types-npm-7.17.0-3c936b54e4-12e5a28798.zip b/.yarn/cache/@babel-types-npm-7.17.0-3c936b54e4-12e5a28798.zip new file mode 100644 index 00000000000..c3b7b0c7b99 Binary files /dev/null and b/.yarn/cache/@babel-types-npm-7.17.0-3c936b54e4-12e5a28798.zip differ diff --git a/.yarn/cache/@cspotcode-source-map-consumer-npm-0.8.0-1f37e9e72b-c0c16ca3d2.zip b/.yarn/cache/@cspotcode-source-map-consumer-npm-0.8.0-1f37e9e72b-c0c16ca3d2.zip new file mode 100644 index 00000000000..273fc20201e Binary files /dev/null and b/.yarn/cache/@cspotcode-source-map-consumer-npm-0.8.0-1f37e9e72b-c0c16ca3d2.zip differ diff --git a/.yarn/cache/@cspotcode-source-map-support-npm-0.7.0-456c3ea2ce-9faddda775.zip b/.yarn/cache/@cspotcode-source-map-support-npm-0.7.0-456c3ea2ce-9faddda775.zip new file mode 100644 index 00000000000..4f0f68a4859 Binary files /dev/null and b/.yarn/cache/@cspotcode-source-map-support-npm-0.7.0-456c3ea2ce-9faddda775.zip differ diff --git a/.yarn/cache/@dabh-diagnostics-npm-2.0.2-83eb005a83-4d95cc3124.zip b/.yarn/cache/@dabh-diagnostics-npm-2.0.2-83eb005a83-4d95cc3124.zip new file mode 100644 index 00000000000..fb36a5ed4b9 Binary files /dev/null and b/.yarn/cache/@dabh-diagnostics-npm-2.0.2-83eb005a83-4d95cc3124.zip differ diff --git a/.yarn/cache/@dashevo-abci-npm-0.23.0-dev.1-4b76d57180-6a7cf16ed9.zip b/.yarn/cache/@dashevo-abci-npm-0.23.0-dev.1-4b76d57180-6a7cf16ed9.zip new file mode 100644 index 00000000000..18c4c92d080 Binary files /dev/null and b/.yarn/cache/@dashevo-abci-npm-0.23.0-dev.1-4b76d57180-6a7cf16ed9.zip differ diff --git a/.yarn/cache/@dashevo-dark-gravity-wave-npm-1.1.1-aa785de435-4f2f0bddfa.zip b/.yarn/cache/@dashevo-dark-gravity-wave-npm-1.1.1-aa785de435-4f2f0bddfa.zip new file mode 100644 index 00000000000..5a9792407a4 Binary files /dev/null and b/.yarn/cache/@dashevo-dark-gravity-wave-npm-1.1.1-aa785de435-4f2f0bddfa.zip differ diff --git a/.yarn/cache/@dashevo-dash-util-npm-2.0.3-a597c1b8b3-ef93e629e9.zip b/.yarn/cache/@dashevo-dash-util-npm-2.0.3-a597c1b8b3-ef93e629e9.zip new file mode 100644 index 00000000000..8ccbb137fb8 Binary files /dev/null and b/.yarn/cache/@dashevo-dash-util-npm-2.0.3-a597c1b8b3-ef93e629e9.zip differ diff --git a/.yarn/cache/@dashevo-dashcore-lib-npm-0.19.39-b28e06588f-e441cf46a9.zip b/.yarn/cache/@dashevo-dashcore-lib-npm-0.19.39-b28e06588f-e441cf46a9.zip new file mode 100644 index 00000000000..792369b1d11 Binary files /dev/null and b/.yarn/cache/@dashevo-dashcore-lib-npm-0.19.39-b28e06588f-e441cf46a9.zip differ diff --git a/.yarn/cache/@dashevo-dashd-rpc-npm-2.3.2-119e544222-56ff41d695.zip b/.yarn/cache/@dashevo-dashd-rpc-npm-2.3.2-119e544222-56ff41d695.zip new file mode 100644 index 00000000000..7390d2d758b Binary files /dev/null and b/.yarn/cache/@dashevo-dashd-rpc-npm-2.3.2-119e544222-56ff41d695.zip differ diff --git a/.yarn/cache/@dashevo-dashpay-contract-npm-0.22.1-2aded78119-cff4700aaf.zip b/.yarn/cache/@dashevo-dashpay-contract-npm-0.22.1-2aded78119-cff4700aaf.zip new file mode 100644 index 00000000000..e1b76213b44 Binary files /dev/null and b/.yarn/cache/@dashevo-dashpay-contract-npm-0.22.1-2aded78119-cff4700aaf.zip differ diff --git a/.yarn/cache/@dashevo-docker-compose-npm-0.24.1-e4dc7f3c5e-7792e09b5d.zip b/.yarn/cache/@dashevo-docker-compose-npm-0.24.1-e4dc7f3c5e-7792e09b5d.zip new file mode 100644 index 00000000000..2637e740d02 Binary files /dev/null and b/.yarn/cache/@dashevo-docker-compose-npm-0.24.1-e4dc7f3c5e-7792e09b5d.zip differ diff --git a/.yarn/cache/@dashevo-dp-services-ctl-https-a393167701-0325823966.zip b/.yarn/cache/@dashevo-dp-services-ctl-https-a393167701-0325823966.zip new file mode 100644 index 00000000000..9402c2560c0 Binary files /dev/null and b/.yarn/cache/@dashevo-dp-services-ctl-https-a393167701-0325823966.zip differ diff --git a/.yarn/cache/@dashevo-dpns-contract-npm-0.22.1-013d358290-d96f91f8ee.zip b/.yarn/cache/@dashevo-dpns-contract-npm-0.22.1-013d358290-d96f91f8ee.zip new file mode 100644 index 00000000000..6490a851324 Binary files /dev/null and b/.yarn/cache/@dashevo-dpns-contract-npm-0.22.1-013d358290-d96f91f8ee.zip differ diff --git a/.yarn/cache/@dashevo-dpp-npm-0.22.1-65dac58df1-ae3d2e1c9f.zip b/.yarn/cache/@dashevo-dpp-npm-0.22.1-65dac58df1-ae3d2e1c9f.zip new file mode 100644 index 00000000000..c0bfbdeb850 Binary files /dev/null and b/.yarn/cache/@dashevo-dpp-npm-0.22.1-65dac58df1-ae3d2e1c9f.zip differ diff --git a/.yarn/cache/@dashevo-feature-flags-contract-npm-0.22.1-9357800b5c-6590ea68bb.zip b/.yarn/cache/@dashevo-feature-flags-contract-npm-0.22.1-9357800b5c-6590ea68bb.zip new file mode 100644 index 00000000000..44a99b72b38 Binary files /dev/null and b/.yarn/cache/@dashevo-feature-flags-contract-npm-0.22.1-9357800b5c-6590ea68bb.zip differ diff --git a/.yarn/cache/@dashevo-masternode-reward-shares-contract-npm-0.22.1-9cfbca722e-577a40876b.zip b/.yarn/cache/@dashevo-masternode-reward-shares-contract-npm-0.22.1-9cfbca722e-577a40876b.zip new file mode 100644 index 00000000000..a3c07117733 Binary files /dev/null and b/.yarn/cache/@dashevo-masternode-reward-shares-contract-npm-0.22.1-9cfbca722e-577a40876b.zip differ diff --git a/.yarn/cache/@dashevo-merk-https-e3939f6b2b-1e056bbd02.zip b/.yarn/cache/@dashevo-merk-https-e3939f6b2b-1e056bbd02.zip new file mode 100644 index 00000000000..1f642b94ffb Binary files /dev/null and b/.yarn/cache/@dashevo-merk-https-e3939f6b2b-1e056bbd02.zip differ diff --git a/.yarn/cache/@dashevo-protobufjs-npm-6.10.5-9ffa190993-2128385663.zip b/.yarn/cache/@dashevo-protobufjs-npm-6.10.5-9ffa190993-2128385663.zip new file mode 100644 index 00000000000..4730992f538 Binary files /dev/null and b/.yarn/cache/@dashevo-protobufjs-npm-6.10.5-9ffa190993-2128385663.zip differ diff --git a/.yarn/cache/@dashevo-rs-drive-npm-0.23.0-dev.5.pr.114.5-a6cb7a87e0-000ba38e33.zip b/.yarn/cache/@dashevo-rs-drive-npm-0.23.0-dev.5.pr.114.5-a6cb7a87e0-000ba38e33.zip new file mode 100644 index 00000000000..84c4a78430a Binary files /dev/null and b/.yarn/cache/@dashevo-rs-drive-npm-0.23.0-dev.5.pr.114.5-a6cb7a87e0-000ba38e33.zip differ diff --git a/.yarn/cache/@dashevo-wasm-re2-npm-1.0.2-50818efe42-3d54788e4e.zip b/.yarn/cache/@dashevo-wasm-re2-npm-1.0.2-50818efe42-3d54788e4e.zip new file mode 100644 index 00000000000..e791add968c Binary files /dev/null and b/.yarn/cache/@dashevo-wasm-re2-npm-1.0.2-50818efe42-3d54788e4e.zip differ diff --git a/.yarn/cache/@dashevo-x11-hash-js-npm-1.0.2-f84bd94ece-a4856fb50f.zip b/.yarn/cache/@dashevo-x11-hash-js-npm-1.0.2-f84bd94ece-a4856fb50f.zip new file mode 100644 index 00000000000..6ff9a47a943 Binary files /dev/null and b/.yarn/cache/@dashevo-x11-hash-js-npm-1.0.2-f84bd94ece-a4856fb50f.zip differ diff --git a/.yarn/cache/@discoveryjs-json-ext-npm-0.5.5-595932ce4b-40844548d8.zip b/.yarn/cache/@discoveryjs-json-ext-npm-0.5.5-595932ce4b-40844548d8.zip new file mode 100644 index 00000000000..bb9fb3e420a Binary files /dev/null and b/.yarn/cache/@discoveryjs-json-ext-npm-0.5.5-595932ce4b-40844548d8.zip differ diff --git a/.yarn/cache/@eslint-eslintrc-npm-0.4.3-ee1bbcab87-03a7704150.zip b/.yarn/cache/@eslint-eslintrc-npm-0.4.3-ee1bbcab87-03a7704150.zip new file mode 100644 index 00000000000..da531c65765 Binary files /dev/null and b/.yarn/cache/@eslint-eslintrc-npm-0.4.3-ee1bbcab87-03a7704150.zip differ diff --git a/.yarn/cache/@gar-promisify-npm-1.1.2-2343f94380-d05081e088.zip b/.yarn/cache/@gar-promisify-npm-1.1.2-2343f94380-d05081e088.zip new file mode 100644 index 00000000000..ce03a21f836 Binary files /dev/null and b/.yarn/cache/@gar-promisify-npm-1.1.2-2343f94380-d05081e088.zip differ diff --git a/.yarn/cache/@grpc-grpc-js-npm-1.4.4-f333f82239-f9be710cef.zip b/.yarn/cache/@grpc-grpc-js-npm-1.4.4-f333f82239-f9be710cef.zip new file mode 100644 index 00000000000..b20586a73f8 Binary files /dev/null and b/.yarn/cache/@grpc-grpc-js-npm-1.4.4-f333f82239-f9be710cef.zip differ diff --git a/.yarn/cache/@grpc-proto-loader-npm-0.5.6-ef97ffeb0b-13fe76d84a.zip b/.yarn/cache/@grpc-proto-loader-npm-0.5.6-ef97ffeb0b-13fe76d84a.zip new file mode 100644 index 00000000000..38e527d0a45 Binary files /dev/null and b/.yarn/cache/@grpc-proto-loader-npm-0.5.6-ef97ffeb0b-13fe76d84a.zip differ diff --git a/.yarn/cache/@grpc-proto-loader-npm-0.6.7-283fc039b9-af1909ec36.zip b/.yarn/cache/@grpc-proto-loader-npm-0.6.7-283fc039b9-af1909ec36.zip new file mode 100644 index 00000000000..8bf486e6acc Binary files /dev/null and b/.yarn/cache/@grpc-proto-loader-npm-0.6.7-283fc039b9-af1909ec36.zip differ diff --git a/.yarn/cache/@hapi-bourne-npm-2.0.0-8eeda7e0a2-2ea0922101.zip b/.yarn/cache/@hapi-bourne-npm-2.0.0-8eeda7e0a2-2ea0922101.zip new file mode 100644 index 00000000000..7b0f02dc258 Binary files /dev/null and b/.yarn/cache/@hapi-bourne-npm-2.0.0-8eeda7e0a2-2ea0922101.zip differ diff --git a/.yarn/cache/@humanwhocodes-config-array-npm-0.5.0-5ded120470-44ee6a9f05.zip b/.yarn/cache/@humanwhocodes-config-array-npm-0.5.0-5ded120470-44ee6a9f05.zip new file mode 100644 index 00000000000..b8cc2d99be1 Binary files /dev/null and b/.yarn/cache/@humanwhocodes-config-array-npm-0.5.0-5ded120470-44ee6a9f05.zip differ diff --git a/.yarn/cache/@humanwhocodes-object-schema-npm-1.2.1-eb622b5d0e-a824a1ec31.zip b/.yarn/cache/@humanwhocodes-object-schema-npm-1.2.1-eb622b5d0e-a824a1ec31.zip new file mode 100644 index 00000000000..2b79104af59 Binary files /dev/null and b/.yarn/cache/@humanwhocodes-object-schema-npm-1.2.1-eb622b5d0e-a824a1ec31.zip differ diff --git a/.yarn/cache/@hutson-parse-repository-url-npm-3.0.2-ae5ef1b671-39992c5f18.zip b/.yarn/cache/@hutson-parse-repository-url-npm-3.0.2-ae5ef1b671-39992c5f18.zip new file mode 100644 index 00000000000..62c9ed24d85 Binary files /dev/null and b/.yarn/cache/@hutson-parse-repository-url-npm-3.0.2-ae5ef1b671-39992c5f18.zip differ diff --git a/.yarn/cache/@isaacs-string-locale-compare-npm-1.1.0-3911094464-7287da5d11.zip b/.yarn/cache/@isaacs-string-locale-compare-npm-1.1.0-3911094464-7287da5d11.zip new file mode 100644 index 00000000000..23f49f77ada Binary files /dev/null and b/.yarn/cache/@isaacs-string-locale-compare-npm-1.1.0-3911094464-7287da5d11.zip differ diff --git a/.yarn/cache/@istanbuljs-load-nyc-config-npm-1.1.0-42d17c9cb1-d578da5e2e.zip b/.yarn/cache/@istanbuljs-load-nyc-config-npm-1.1.0-42d17c9cb1-d578da5e2e.zip new file mode 100644 index 00000000000..3e663a24f29 Binary files /dev/null and b/.yarn/cache/@istanbuljs-load-nyc-config-npm-1.1.0-42d17c9cb1-d578da5e2e.zip differ diff --git a/.yarn/cache/@istanbuljs-schema-npm-0.1.3-466bd3eaaa-5282759d96.zip b/.yarn/cache/@istanbuljs-schema-npm-0.1.3-466bd3eaaa-5282759d96.zip new file mode 100644 index 00000000000..5796f760161 Binary files /dev/null and b/.yarn/cache/@istanbuljs-schema-npm-0.1.3-466bd3eaaa-5282759d96.zip differ diff --git a/.yarn/cache/@jest-types-npm-27.2.5-620da3d425-322603c243.zip b/.yarn/cache/@jest-types-npm-27.2.5-620da3d425-322603c243.zip new file mode 100644 index 00000000000..d522bf87e0d Binary files /dev/null and b/.yarn/cache/@jest-types-npm-27.2.5-620da3d425-322603c243.zip differ diff --git a/.yarn/cache/@jridgewell-resolve-uri-npm-3.0.8-94779c6a1d-28d739f49b.zip b/.yarn/cache/@jridgewell-resolve-uri-npm-3.0.8-94779c6a1d-28d739f49b.zip new file mode 100644 index 00000000000..8fc1c6feccc Binary files /dev/null and b/.yarn/cache/@jridgewell-resolve-uri-npm-3.0.8-94779c6a1d-28d739f49b.zip differ diff --git a/.yarn/cache/@jridgewell-sourcemap-codec-npm-1.4.14-f5f0630788-61100637b6.zip b/.yarn/cache/@jridgewell-sourcemap-codec-npm-1.4.14-f5f0630788-61100637b6.zip new file mode 100644 index 00000000000..d8703c89675 Binary files /dev/null and b/.yarn/cache/@jridgewell-sourcemap-codec-npm-1.4.14-f5f0630788-61100637b6.zip differ diff --git a/.yarn/cache/@jridgewell-trace-mapping-npm-0.3.14-c78fcccfdf-b9537b9630.zip b/.yarn/cache/@jridgewell-trace-mapping-npm-0.3.14-c78fcccfdf-b9537b9630.zip new file mode 100644 index 00000000000..95455c62eb2 Binary files /dev/null and b/.yarn/cache/@jridgewell-trace-mapping-npm-0.3.14-c78fcccfdf-b9537b9630.zip differ diff --git a/.yarn/cache/@jsdevtools-ono-npm-7.1.3-cb2313543b-2297fcd472.zip b/.yarn/cache/@jsdevtools-ono-npm-7.1.3-cb2313543b-2297fcd472.zip new file mode 100644 index 00000000000..1d2852c1435 Binary files /dev/null and b/.yarn/cache/@jsdevtools-ono-npm-7.1.3-cb2313543b-2297fcd472.zip differ diff --git a/.yarn/cache/@leichtgewicht-ip-codec-npm-2.0.3-536ebba640-5b6bee0481.zip b/.yarn/cache/@leichtgewicht-ip-codec-npm-2.0.3-536ebba640-5b6bee0481.zip new file mode 100644 index 00000000000..6087e048dd2 Binary files /dev/null and b/.yarn/cache/@leichtgewicht-ip-codec-npm-2.0.3-536ebba640-5b6bee0481.zip differ diff --git a/.yarn/cache/@nodelib-fs.scandir-npm-2.1.5-89c67370dd-a970d595bd.zip b/.yarn/cache/@nodelib-fs.scandir-npm-2.1.5-89c67370dd-a970d595bd.zip new file mode 100644 index 00000000000..99f6bc1e236 Binary files /dev/null and b/.yarn/cache/@nodelib-fs.scandir-npm-2.1.5-89c67370dd-a970d595bd.zip differ diff --git a/.yarn/cache/@nodelib-fs.stat-npm-2.0.5-01f4dd3030-012480b5ca.zip b/.yarn/cache/@nodelib-fs.stat-npm-2.0.5-01f4dd3030-012480b5ca.zip new file mode 100644 index 00000000000..e86d01e26b3 Binary files /dev/null and b/.yarn/cache/@nodelib-fs.stat-npm-2.0.5-01f4dd3030-012480b5ca.zip differ diff --git a/.yarn/cache/@nodelib-fs.walk-npm-1.2.8-b4a89da548-190c643f15.zip b/.yarn/cache/@nodelib-fs.walk-npm-1.2.8-b4a89da548-190c643f15.zip new file mode 100644 index 00000000000..1750003a76f Binary files /dev/null and b/.yarn/cache/@nodelib-fs.walk-npm-1.2.8-b4a89da548-190c643f15.zip differ diff --git a/.yarn/cache/@npmcli-arborist-npm-4.3.1-68b2741cb0-51470ebb9a.zip b/.yarn/cache/@npmcli-arborist-npm-4.3.1-68b2741cb0-51470ebb9a.zip new file mode 100644 index 00000000000..222511f58de Binary files /dev/null and b/.yarn/cache/@npmcli-arborist-npm-4.3.1-68b2741cb0-51470ebb9a.zip differ diff --git a/.yarn/cache/@npmcli-fs-npm-1.0.0-92194475f3-f2b4990107.zip b/.yarn/cache/@npmcli-fs-npm-1.0.0-92194475f3-f2b4990107.zip new file mode 100644 index 00000000000..a6429ab2963 Binary files /dev/null and b/.yarn/cache/@npmcli-fs-npm-1.0.0-92194475f3-f2b4990107.zip differ diff --git a/.yarn/cache/@npmcli-git-npm-2.1.0-b85bc3f444-1f89752df7.zip b/.yarn/cache/@npmcli-git-npm-2.1.0-b85bc3f444-1f89752df7.zip new file mode 100644 index 00000000000..1a912f9bc08 Binary files /dev/null and b/.yarn/cache/@npmcli-git-npm-2.1.0-b85bc3f444-1f89752df7.zip differ diff --git a/.yarn/cache/@npmcli-installed-package-contents-npm-1.0.7-b15a13ab4f-a4a29b99d4.zip b/.yarn/cache/@npmcli-installed-package-contents-npm-1.0.7-b15a13ab4f-a4a29b99d4.zip new file mode 100644 index 00000000000..7855582b00e Binary files /dev/null and b/.yarn/cache/@npmcli-installed-package-contents-npm-1.0.7-b15a13ab4f-a4a29b99d4.zip differ diff --git a/.yarn/cache/@npmcli-map-workspaces-npm-2.0.1-4911719cd1-16c6738e15.zip b/.yarn/cache/@npmcli-map-workspaces-npm-2.0.1-4911719cd1-16c6738e15.zip new file mode 100644 index 00000000000..c3374440889 Binary files /dev/null and b/.yarn/cache/@npmcli-map-workspaces-npm-2.0.1-4911719cd1-16c6738e15.zip differ diff --git a/.yarn/cache/@npmcli-metavuln-calculator-npm-2.0.0-df87832d39-bf88115e7c.zip b/.yarn/cache/@npmcli-metavuln-calculator-npm-2.0.0-df87832d39-bf88115e7c.zip new file mode 100644 index 00000000000..df75f20f7e1 Binary files /dev/null and b/.yarn/cache/@npmcli-metavuln-calculator-npm-2.0.0-df87832d39-bf88115e7c.zip differ diff --git a/.yarn/cache/@npmcli-move-file-npm-1.1.2-4f6c7b3354-c96381d4a3.zip b/.yarn/cache/@npmcli-move-file-npm-1.1.2-4f6c7b3354-c96381d4a3.zip new file mode 100644 index 00000000000..279b2de42bc Binary files /dev/null and b/.yarn/cache/@npmcli-move-file-npm-1.1.2-4f6c7b3354-c96381d4a3.zip differ diff --git a/.yarn/cache/@npmcli-name-from-folder-npm-1.0.1-b2b2fde7e0-67339f4096.zip b/.yarn/cache/@npmcli-name-from-folder-npm-1.0.1-b2b2fde7e0-67339f4096.zip new file mode 100644 index 00000000000..472b75fc3fe Binary files /dev/null and b/.yarn/cache/@npmcli-name-from-folder-npm-1.0.1-b2b2fde7e0-67339f4096.zip differ diff --git a/.yarn/cache/@npmcli-node-gyp-npm-1.0.3-678a56ae5b-496d5eef2e.zip b/.yarn/cache/@npmcli-node-gyp-npm-1.0.3-678a56ae5b-496d5eef2e.zip new file mode 100644 index 00000000000..11b6fe3a2fe Binary files /dev/null and b/.yarn/cache/@npmcli-node-gyp-npm-1.0.3-678a56ae5b-496d5eef2e.zip differ diff --git a/.yarn/cache/@npmcli-package-json-npm-1.0.1-4a9d430114-08b66c8ddb.zip b/.yarn/cache/@npmcli-package-json-npm-1.0.1-4a9d430114-08b66c8ddb.zip new file mode 100644 index 00000000000..acb862a2c0d Binary files /dev/null and b/.yarn/cache/@npmcli-package-json-npm-1.0.1-4a9d430114-08b66c8ddb.zip differ diff --git a/.yarn/cache/@npmcli-promise-spawn-npm-1.3.2-7762aaada5-543b7c1e26.zip b/.yarn/cache/@npmcli-promise-spawn-npm-1.3.2-7762aaada5-543b7c1e26.zip new file mode 100644 index 00000000000..57ae893f966 Binary files /dev/null and b/.yarn/cache/@npmcli-promise-spawn-npm-1.3.2-7762aaada5-543b7c1e26.zip differ diff --git a/.yarn/cache/@npmcli-run-script-npm-2.0.0-244659a556-c016ea9411.zip b/.yarn/cache/@npmcli-run-script-npm-2.0.0-244659a556-c016ea9411.zip new file mode 100644 index 00000000000..271c6a8c34b Binary files /dev/null and b/.yarn/cache/@npmcli-run-script-npm-2.0.0-244659a556-c016ea9411.zip differ diff --git a/.yarn/cache/@oclif-color-npm-1.0.0-a76d65e26d-60521f90eb.zip b/.yarn/cache/@oclif-color-npm-1.0.0-a76d65e26d-60521f90eb.zip new file mode 100644 index 00000000000..5dfe8acba1b Binary files /dev/null and b/.yarn/cache/@oclif-color-npm-1.0.0-a76d65e26d-60521f90eb.zip differ diff --git a/.yarn/cache/@oclif-core-npm-1.3.4-e0bcdb30fd-c7f29f71ce.zip b/.yarn/cache/@oclif-core-npm-1.3.4-e0bcdb30fd-c7f29f71ce.zip new file mode 100644 index 00000000000..2d697dc5e28 Binary files /dev/null and b/.yarn/cache/@oclif-core-npm-1.3.4-e0bcdb30fd-c7f29f71ce.zip differ diff --git a/.yarn/cache/@oclif-linewrap-npm-1.0.0-e738997487-a072016a58.zip b/.yarn/cache/@oclif-linewrap-npm-1.0.0-e738997487-a072016a58.zip new file mode 100644 index 00000000000..c09d1fcda09 Binary files /dev/null and b/.yarn/cache/@oclif-linewrap-npm-1.0.0-e738997487-a072016a58.zip differ diff --git a/.yarn/cache/@oclif-plugin-help-npm-5.1.11-d0cc56652c-ab2d1377cb.zip b/.yarn/cache/@oclif-plugin-help-npm-5.1.11-d0cc56652c-ab2d1377cb.zip new file mode 100644 index 00000000000..920839d21bf Binary files /dev/null and b/.yarn/cache/@oclif-plugin-help-npm-5.1.11-d0cc56652c-ab2d1377cb.zip differ diff --git a/.yarn/cache/@oclif-plugin-not-found-npm-2.3.1-87e43b78d1-b6aeddb733.zip b/.yarn/cache/@oclif-plugin-not-found-npm-2.3.1-87e43b78d1-b6aeddb733.zip new file mode 100644 index 00000000000..1156603ef05 Binary files /dev/null and b/.yarn/cache/@oclif-plugin-not-found-npm-2.3.1-87e43b78d1-b6aeddb733.zip differ diff --git a/.yarn/cache/@oclif-plugin-warn-if-update-available-npm-2.0.4-d71b5c1f00-9a127aaaa3.zip b/.yarn/cache/@oclif-plugin-warn-if-update-available-npm-2.0.4-d71b5c1f00-9a127aaaa3.zip new file mode 100644 index 00000000000..e23d97523c6 Binary files /dev/null and b/.yarn/cache/@oclif-plugin-warn-if-update-available-npm-2.0.4-d71b5c1f00-9a127aaaa3.zip differ diff --git a/.yarn/cache/@oclif-screen-npm-3.0.2-68fdcc8cd0-962678c65f.zip b/.yarn/cache/@oclif-screen-npm-3.0.2-68fdcc8cd0-962678c65f.zip new file mode 100644 index 00000000000..6fd795ef6dd Binary files /dev/null and b/.yarn/cache/@oclif-screen-npm-3.0.2-68fdcc8cd0-962678c65f.zip differ diff --git a/.yarn/cache/@octokit-auth-token-npm-2.5.0-a1c6ffb640-45949296c0.zip b/.yarn/cache/@octokit-auth-token-npm-2.5.0-a1c6ffb640-45949296c0.zip new file mode 100644 index 00000000000..b737fadf4dd Binary files /dev/null and b/.yarn/cache/@octokit-auth-token-npm-2.5.0-a1c6ffb640-45949296c0.zip differ diff --git a/.yarn/cache/@octokit-core-npm-3.5.1-a933dedcf7-67179739fc.zip b/.yarn/cache/@octokit-core-npm-3.5.1-a933dedcf7-67179739fc.zip new file mode 100644 index 00000000000..4ef44697712 Binary files /dev/null and b/.yarn/cache/@octokit-core-npm-3.5.1-a933dedcf7-67179739fc.zip differ diff --git a/.yarn/cache/@octokit-endpoint-npm-6.0.12-d467db27fd-b48b29940a.zip b/.yarn/cache/@octokit-endpoint-npm-6.0.12-d467db27fd-b48b29940a.zip new file mode 100644 index 00000000000..b844a8513e4 Binary files /dev/null and b/.yarn/cache/@octokit-endpoint-npm-6.0.12-d467db27fd-b48b29940a.zip differ diff --git a/.yarn/cache/@octokit-graphql-npm-4.8.0-83d118b4da-f68afe53f6.zip b/.yarn/cache/@octokit-graphql-npm-4.8.0-83d118b4da-f68afe53f6.zip new file mode 100644 index 00000000000..1b16a606642 Binary files /dev/null and b/.yarn/cache/@octokit-graphql-npm-4.8.0-83d118b4da-f68afe53f6.zip differ diff --git a/.yarn/cache/@octokit-openapi-types-npm-11.2.0-10b7a5c509-eb373ea496.zip b/.yarn/cache/@octokit-openapi-types-npm-11.2.0-10b7a5c509-eb373ea496.zip new file mode 100644 index 00000000000..8f882910f31 Binary files /dev/null and b/.yarn/cache/@octokit-openapi-types-npm-11.2.0-10b7a5c509-eb373ea496.zip differ diff --git a/.yarn/cache/@octokit-plugin-paginate-rest-npm-2.17.0-4d48903092-c8753cda6f.zip b/.yarn/cache/@octokit-plugin-paginate-rest-npm-2.17.0-4d48903092-c8753cda6f.zip new file mode 100644 index 00000000000..713ee03a57c Binary files /dev/null and b/.yarn/cache/@octokit-plugin-paginate-rest-npm-2.17.0-4d48903092-c8753cda6f.zip differ diff --git a/.yarn/cache/@octokit-plugin-request-log-npm-1.0.4-9ab5a2f888-2086db0005.zip b/.yarn/cache/@octokit-plugin-request-log-npm-1.0.4-9ab5a2f888-2086db0005.zip new file mode 100644 index 00000000000..93a5036447a Binary files /dev/null and b/.yarn/cache/@octokit-plugin-request-log-npm-1.0.4-9ab5a2f888-2086db0005.zip differ diff --git a/.yarn/cache/@octokit-plugin-rest-endpoint-methods-npm-5.13.0-976c113da3-f331457e43.zip b/.yarn/cache/@octokit-plugin-rest-endpoint-methods-npm-5.13.0-976c113da3-f331457e43.zip new file mode 100644 index 00000000000..4ac318f57c4 Binary files /dev/null and b/.yarn/cache/@octokit-plugin-rest-endpoint-methods-npm-5.13.0-976c113da3-f331457e43.zip differ diff --git a/.yarn/cache/@octokit-request-error-npm-2.1.0-51ac624306-baec2b5700.zip b/.yarn/cache/@octokit-request-error-npm-2.1.0-51ac624306-baec2b5700.zip new file mode 100644 index 00000000000..fadd972f220 Binary files /dev/null and b/.yarn/cache/@octokit-request-error-npm-2.1.0-51ac624306-baec2b5700.zip differ diff --git a/.yarn/cache/@octokit-request-npm-5.6.3-25a5f5382d-c0b4542eb4.zip b/.yarn/cache/@octokit-request-npm-5.6.3-25a5f5382d-c0b4542eb4.zip new file mode 100644 index 00000000000..19a09612e9e Binary files /dev/null and b/.yarn/cache/@octokit-request-npm-5.6.3-25a5f5382d-c0b4542eb4.zip differ diff --git a/.yarn/cache/@octokit-rest-npm-18.12.0-f250ac8e5e-c18bd6676a.zip b/.yarn/cache/@octokit-rest-npm-18.12.0-f250ac8e5e-c18bd6676a.zip new file mode 100644 index 00000000000..3819d961180 Binary files /dev/null and b/.yarn/cache/@octokit-rest-npm-18.12.0-f250ac8e5e-c18bd6676a.zip differ diff --git a/.yarn/cache/@octokit-types-npm-6.34.0-1de469b7ee-f122b9aee8.zip b/.yarn/cache/@octokit-types-npm-6.34.0-1de469b7ee-f122b9aee8.zip new file mode 100644 index 00000000000..92ee0a5c7a1 Binary files /dev/null and b/.yarn/cache/@octokit-types-npm-6.34.0-1de469b7ee-f122b9aee8.zip differ diff --git a/.yarn/cache/@protobufjs-aspromise-npm-1.1.2-71d00b938f-011fe7ef08.zip b/.yarn/cache/@protobufjs-aspromise-npm-1.1.2-71d00b938f-011fe7ef08.zip new file mode 100644 index 00000000000..fc9081b9d47 Binary files /dev/null and b/.yarn/cache/@protobufjs-aspromise-npm-1.1.2-71d00b938f-011fe7ef08.zip differ diff --git a/.yarn/cache/@protobufjs-base64-npm-1.1.2-cd8ca6814a-67173ac34d.zip b/.yarn/cache/@protobufjs-base64-npm-1.1.2-cd8ca6814a-67173ac34d.zip new file mode 100644 index 00000000000..cdc42f13f86 Binary files /dev/null and b/.yarn/cache/@protobufjs-base64-npm-1.1.2-cd8ca6814a-67173ac34d.zip differ diff --git a/.yarn/cache/@protobufjs-codegen-npm-2.0.4-36e188bbe6-59240c850b.zip b/.yarn/cache/@protobufjs-codegen-npm-2.0.4-36e188bbe6-59240c850b.zip new file mode 100644 index 00000000000..2217a817e12 Binary files /dev/null and b/.yarn/cache/@protobufjs-codegen-npm-2.0.4-36e188bbe6-59240c850b.zip differ diff --git a/.yarn/cache/@protobufjs-eventemitter-npm-1.1.0-029cc7d431-0369163a3d.zip b/.yarn/cache/@protobufjs-eventemitter-npm-1.1.0-029cc7d431-0369163a3d.zip new file mode 100644 index 00000000000..91729815333 Binary files /dev/null and b/.yarn/cache/@protobufjs-eventemitter-npm-1.1.0-029cc7d431-0369163a3d.zip differ diff --git a/.yarn/cache/@protobufjs-fetch-npm-1.1.0-ca857b7df4-3fce7e09eb.zip b/.yarn/cache/@protobufjs-fetch-npm-1.1.0-ca857b7df4-3fce7e09eb.zip new file mode 100644 index 00000000000..3f687b0bfc2 Binary files /dev/null and b/.yarn/cache/@protobufjs-fetch-npm-1.1.0-ca857b7df4-3fce7e09eb.zip differ diff --git a/.yarn/cache/@protobufjs-float-npm-1.0.2-5678f64d08-5781e12412.zip b/.yarn/cache/@protobufjs-float-npm-1.0.2-5678f64d08-5781e12412.zip new file mode 100644 index 00000000000..d7027a9cf26 Binary files /dev/null and b/.yarn/cache/@protobufjs-float-npm-1.0.2-5678f64d08-5781e12412.zip differ diff --git a/.yarn/cache/@protobufjs-inquire-npm-1.1.0-3c7759e9ce-ca06f02eaf.zip b/.yarn/cache/@protobufjs-inquire-npm-1.1.0-3c7759e9ce-ca06f02eaf.zip new file mode 100644 index 00000000000..c7a6b3dcd25 Binary files /dev/null and b/.yarn/cache/@protobufjs-inquire-npm-1.1.0-3c7759e9ce-ca06f02eaf.zip differ diff --git a/.yarn/cache/@protobufjs-path-npm-1.1.2-641d08de76-856eeb532b.zip b/.yarn/cache/@protobufjs-path-npm-1.1.2-641d08de76-856eeb532b.zip new file mode 100644 index 00000000000..27b166d2286 Binary files /dev/null and b/.yarn/cache/@protobufjs-path-npm-1.1.2-641d08de76-856eeb532b.zip differ diff --git a/.yarn/cache/@protobufjs-pool-npm-1.1.0-47a76f96a1-d6a34fbbd2.zip b/.yarn/cache/@protobufjs-pool-npm-1.1.0-47a76f96a1-d6a34fbbd2.zip new file mode 100644 index 00000000000..14babc22bbb Binary files /dev/null and b/.yarn/cache/@protobufjs-pool-npm-1.1.0-47a76f96a1-d6a34fbbd2.zip differ diff --git a/.yarn/cache/@protobufjs-utf8-npm-1.1.0-02c590807c-f9bf3163d1.zip b/.yarn/cache/@protobufjs-utf8-npm-1.1.0-02c590807c-f9bf3163d1.zip new file mode 100644 index 00000000000..6e9fdd4c725 Binary files /dev/null and b/.yarn/cache/@protobufjs-utf8-npm-1.1.0-02c590807c-f9bf3163d1.zip differ diff --git a/.yarn/cache/@sindresorhus-is-npm-0.14.0-9f906ea34b-971e0441dd.zip b/.yarn/cache/@sindresorhus-is-npm-0.14.0-9f906ea34b-971e0441dd.zip new file mode 100644 index 00000000000..db20dee9589 Binary files /dev/null and b/.yarn/cache/@sindresorhus-is-npm-0.14.0-9f906ea34b-971e0441dd.zip differ diff --git a/.yarn/cache/@sinonjs-commons-npm-1.8.3-30cf78d93f-6159726db5.zip b/.yarn/cache/@sinonjs-commons-npm-1.8.3-30cf78d93f-6159726db5.zip new file mode 100644 index 00000000000..ad5699eb82d Binary files /dev/null and b/.yarn/cache/@sinonjs-commons-npm-1.8.3-30cf78d93f-6159726db5.zip differ diff --git a/.yarn/cache/@sinonjs-fake-timers-npm-7.1.2-2a6b119ac7-c84773d797.zip b/.yarn/cache/@sinonjs-fake-timers-npm-7.1.2-2a6b119ac7-c84773d797.zip new file mode 100644 index 00000000000..eca02ec7312 Binary files /dev/null and b/.yarn/cache/@sinonjs-fake-timers-npm-7.1.2-2a6b119ac7-c84773d797.zip differ diff --git a/.yarn/cache/@sinonjs-samsam-npm-6.0.2-5e8e8897e2-bc1514edf1.zip b/.yarn/cache/@sinonjs-samsam-npm-6.0.2-5e8e8897e2-bc1514edf1.zip new file mode 100644 index 00000000000..e668806ee04 Binary files /dev/null and b/.yarn/cache/@sinonjs-samsam-npm-6.0.2-5e8e8897e2-bc1514edf1.zip differ diff --git a/.yarn/cache/@sinonjs-text-encoding-npm-0.7.1-865b0079b5-130de0bb56.zip b/.yarn/cache/@sinonjs-text-encoding-npm-0.7.1-865b0079b5-130de0bb56.zip new file mode 100644 index 00000000000..b9b97e01ecc Binary files /dev/null and b/.yarn/cache/@sinonjs-text-encoding-npm-0.7.1-865b0079b5-130de0bb56.zip differ diff --git a/.yarn/cache/@szmarczak-http-timer-npm-1.1.2-ea82ca2d55-4d9158061c.zip b/.yarn/cache/@szmarczak-http-timer-npm-1.1.2-ea82ca2d55-4d9158061c.zip new file mode 100644 index 00000000000..01358f2780d Binary files /dev/null and b/.yarn/cache/@szmarczak-http-timer-npm-1.1.2-ea82ca2d55-4d9158061c.zip differ diff --git a/.yarn/cache/@tootallnate-once-npm-1.1.2-0517220057-e1fb1bbbc1.zip b/.yarn/cache/@tootallnate-once-npm-1.1.2-0517220057-e1fb1bbbc1.zip new file mode 100644 index 00000000000..05ad66ab220 Binary files /dev/null and b/.yarn/cache/@tootallnate-once-npm-1.1.2-0517220057-e1fb1bbbc1.zip differ diff --git a/.yarn/cache/@tootallnate-once-npm-2.0.0-e36cf4f140-ad87447820.zip b/.yarn/cache/@tootallnate-once-npm-2.0.0-e36cf4f140-ad87447820.zip new file mode 100644 index 00000000000..d240a82ae2f Binary files /dev/null and b/.yarn/cache/@tootallnate-once-npm-2.0.0-e36cf4f140-ad87447820.zip differ diff --git a/.yarn/cache/@tsconfig-node10-npm-1.0.8-90a8cce25d-b8d5fffbc6.zip b/.yarn/cache/@tsconfig-node10-npm-1.0.8-90a8cce25d-b8d5fffbc6.zip new file mode 100644 index 00000000000..484f8ab845a Binary files /dev/null and b/.yarn/cache/@tsconfig-node10-npm-1.0.8-90a8cce25d-b8d5fffbc6.zip differ diff --git a/.yarn/cache/@tsconfig-node12-npm-1.0.9-780563856d-a01b2400ab.zip b/.yarn/cache/@tsconfig-node12-npm-1.0.9-780563856d-a01b2400ab.zip new file mode 100644 index 00000000000..0d3ceef97ef Binary files /dev/null and b/.yarn/cache/@tsconfig-node12-npm-1.0.9-780563856d-a01b2400ab.zip differ diff --git a/.yarn/cache/@tsconfig-node14-npm-1.0.1-3ecac58e68-976345e896.zip b/.yarn/cache/@tsconfig-node14-npm-1.0.1-3ecac58e68-976345e896.zip new file mode 100644 index 00000000000..85d7b4cd1eb Binary files /dev/null and b/.yarn/cache/@tsconfig-node14-npm-1.0.1-3ecac58e68-976345e896.zip differ diff --git a/.yarn/cache/@tsconfig-node16-npm-1.0.2-1f43ab567a-ca94d36397.zip b/.yarn/cache/@tsconfig-node16-npm-1.0.2-1f43ab567a-ca94d36397.zip new file mode 100644 index 00000000000..e39b74d1063 Binary files /dev/null and b/.yarn/cache/@tsconfig-node16-npm-1.0.2-1f43ab567a-ca94d36397.zip differ diff --git a/.yarn/cache/@types-chai-as-promised-npm-7.1.4-0ab573c373-bb974e77e0.zip b/.yarn/cache/@types-chai-as-promised-npm-7.1.4-0ab573c373-bb974e77e0.zip new file mode 100644 index 00000000000..83ba1c454f5 Binary files /dev/null and b/.yarn/cache/@types-chai-as-promised-npm-7.1.4-0ab573c373-bb974e77e0.zip differ diff --git a/.yarn/cache/@types-chai-npm-4.2.22-557883092e-dca66a263b.zip b/.yarn/cache/@types-chai-npm-4.2.22-557883092e-dca66a263b.zip new file mode 100644 index 00000000000..629fd645bac Binary files /dev/null and b/.yarn/cache/@types-chai-npm-4.2.22-557883092e-dca66a263b.zip differ diff --git a/.yarn/cache/@types-component-emitter-npm-1.2.11-581f0366a3-0e081c5f7a.zip b/.yarn/cache/@types-component-emitter-npm-1.2.11-581f0366a3-0e081c5f7a.zip new file mode 100644 index 00000000000..c9112cb118b Binary files /dev/null and b/.yarn/cache/@types-component-emitter-npm-1.2.11-581f0366a3-0e081c5f7a.zip differ diff --git a/.yarn/cache/@types-connect-npm-3.4.35-7337eee0a3-fe81351470.zip b/.yarn/cache/@types-connect-npm-3.4.35-7337eee0a3-fe81351470.zip new file mode 100644 index 00000000000..ae5f3a0f182 Binary files /dev/null and b/.yarn/cache/@types-connect-npm-3.4.35-7337eee0a3-fe81351470.zip differ diff --git a/.yarn/cache/@types-cookie-npm-0.4.1-274a704dc6-3275534ed6.zip b/.yarn/cache/@types-cookie-npm-0.4.1-274a704dc6-3275534ed6.zip new file mode 100644 index 00000000000..1c1769dcd05 Binary files /dev/null and b/.yarn/cache/@types-cookie-npm-0.4.1-274a704dc6-3275534ed6.zip differ diff --git a/.yarn/cache/@types-cors-npm-2.8.12-ff52e8e514-8c45f112c7.zip b/.yarn/cache/@types-cors-npm-2.8.12-ff52e8e514-8c45f112c7.zip new file mode 100644 index 00000000000..3a10db9f6d6 Binary files /dev/null and b/.yarn/cache/@types-cors-npm-2.8.12-ff52e8e514-8c45f112c7.zip differ diff --git a/.yarn/cache/@types-dirty-chai-npm-2.0.2-440bf7c05c-6015689ef7.zip b/.yarn/cache/@types-dirty-chai-npm-2.0.2-440bf7c05c-6015689ef7.zip new file mode 100644 index 00000000000..4cca061f6b9 Binary files /dev/null and b/.yarn/cache/@types-dirty-chai-npm-2.0.2-440bf7c05c-6015689ef7.zip differ diff --git a/.yarn/cache/@types-eslint-npm-8.2.0-971aa21b00-18f37790af.zip b/.yarn/cache/@types-eslint-npm-8.2.0-971aa21b00-18f37790af.zip new file mode 100644 index 00000000000..149f59ca1d8 Binary files /dev/null and b/.yarn/cache/@types-eslint-npm-8.2.0-971aa21b00-18f37790af.zip differ diff --git a/.yarn/cache/@types-eslint-scope-npm-3.7.1-8d60f27ad9-4271c9adad.zip b/.yarn/cache/@types-eslint-scope-npm-3.7.1-8d60f27ad9-4271c9adad.zip new file mode 100644 index 00000000000..da444c24425 Binary files /dev/null and b/.yarn/cache/@types-eslint-scope-npm-3.7.1-8d60f27ad9-4271c9adad.zip differ diff --git a/.yarn/cache/@types-estree-npm-0.0.50-b9bc3b8409-9a2b6a4a8c.zip b/.yarn/cache/@types-estree-npm-0.0.50-b9bc3b8409-9a2b6a4a8c.zip new file mode 100644 index 00000000000..dfe0eca7ace Binary files /dev/null and b/.yarn/cache/@types-estree-npm-0.0.50-b9bc3b8409-9a2b6a4a8c.zip differ diff --git a/.yarn/cache/@types-expect-npm-1.20.4-9b033f86cb-c09a9abec2.zip b/.yarn/cache/@types-expect-npm-1.20.4-9b033f86cb-c09a9abec2.zip new file mode 100644 index 00000000000..48593124ee3 Binary files /dev/null and b/.yarn/cache/@types-expect-npm-1.20.4-9b033f86cb-c09a9abec2.zip differ diff --git a/.yarn/cache/@types-expect-npm-24.3.0-dc41523666-8d017b49b1.zip b/.yarn/cache/@types-expect-npm-24.3.0-dc41523666-8d017b49b1.zip new file mode 100644 index 00000000000..36748dd748e Binary files /dev/null and b/.yarn/cache/@types-expect-npm-24.3.0-dc41523666-8d017b49b1.zip differ diff --git a/.yarn/cache/@types-express-serve-static-core-npm-4.17.25-77a729b982-a60d44676d.zip b/.yarn/cache/@types-express-serve-static-core-npm-4.17.25-77a729b982-a60d44676d.zip new file mode 100644 index 00000000000..d29e8fc3028 Binary files /dev/null and b/.yarn/cache/@types-express-serve-static-core-npm-4.17.25-77a729b982-a60d44676d.zip differ diff --git a/.yarn/cache/@types-glob-npm-7.2.0-772334bf9a-6ae717fedf.zip b/.yarn/cache/@types-glob-npm-7.2.0-772334bf9a-6ae717fedf.zip new file mode 100644 index 00000000000..f3ad9aedc27 Binary files /dev/null and b/.yarn/cache/@types-glob-npm-7.2.0-772334bf9a-6ae717fedf.zip differ diff --git a/.yarn/cache/@types-istanbul-lib-coverage-npm-2.0.3-67a37eb00a-0650cba4be.zip b/.yarn/cache/@types-istanbul-lib-coverage-npm-2.0.3-67a37eb00a-0650cba4be.zip new file mode 100644 index 00000000000..fb8fe6af052 Binary files /dev/null and b/.yarn/cache/@types-istanbul-lib-coverage-npm-2.0.3-67a37eb00a-0650cba4be.zip differ diff --git a/.yarn/cache/@types-istanbul-lib-report-npm-3.0.0-50de3e6b3b-656398b62d.zip b/.yarn/cache/@types-istanbul-lib-report-npm-3.0.0-50de3e6b3b-656398b62d.zip new file mode 100644 index 00000000000..30b798782f4 Binary files /dev/null and b/.yarn/cache/@types-istanbul-lib-report-npm-3.0.0-50de3e6b3b-656398b62d.zip differ diff --git a/.yarn/cache/@types-istanbul-reports-npm-3.0.1-770e825002-f1ad54bc68.zip b/.yarn/cache/@types-istanbul-reports-npm-3.0.1-770e825002-f1ad54bc68.zip new file mode 100644 index 00000000000..2b6b8f206ad Binary files /dev/null and b/.yarn/cache/@types-istanbul-reports-npm-3.0.1-770e825002-f1ad54bc68.zip differ diff --git a/.yarn/cache/@types-json-schema-npm-7.0.9-361918cff3-259d0e25f1.zip b/.yarn/cache/@types-json-schema-npm-7.0.9-361918cff3-259d0e25f1.zip new file mode 100644 index 00000000000..db94395975e Binary files /dev/null and b/.yarn/cache/@types-json-schema-npm-7.0.9-361918cff3-259d0e25f1.zip differ diff --git a/.yarn/cache/@types-json5-npm-0.0.29-f63a7916bd-e60b153664.zip b/.yarn/cache/@types-json5-npm-0.0.29-f63a7916bd-e60b153664.zip new file mode 100644 index 00000000000..82bfbc82ae9 Binary files /dev/null and b/.yarn/cache/@types-json5-npm-0.0.29-f63a7916bd-e60b153664.zip differ diff --git a/.yarn/cache/@types-keyv-npm-3.1.3-8864e3cbf3-b5f8aa592c.zip b/.yarn/cache/@types-keyv-npm-3.1.3-8864e3cbf3-b5f8aa592c.zip new file mode 100644 index 00000000000..a40eb814d77 Binary files /dev/null and b/.yarn/cache/@types-keyv-npm-3.1.3-8864e3cbf3-b5f8aa592c.zip differ diff --git a/.yarn/cache/@types-lodash-npm-4.14.177-a28410b30a-00f9eb300e.zip b/.yarn/cache/@types-lodash-npm-4.14.177-a28410b30a-00f9eb300e.zip new file mode 100644 index 00000000000..52bf07ddb4a Binary files /dev/null and b/.yarn/cache/@types-lodash-npm-4.14.177-a28410b30a-00f9eb300e.zip differ diff --git a/.yarn/cache/@types-long-npm-4.0.1-022c8b6e77-ff9653c33f.zip b/.yarn/cache/@types-long-npm-4.0.1-022c8b6e77-ff9653c33f.zip new file mode 100644 index 00000000000..c88a2cb34f0 Binary files /dev/null and b/.yarn/cache/@types-long-npm-4.0.1-022c8b6e77-ff9653c33f.zip differ diff --git a/.yarn/cache/@types-minimatch-npm-3.0.5-802bb0797f-c41d136f67.zip b/.yarn/cache/@types-minimatch-npm-3.0.5-802bb0797f-c41d136f67.zip new file mode 100644 index 00000000000..11730d3c38d Binary files /dev/null and b/.yarn/cache/@types-minimatch-npm-3.0.5-802bb0797f-c41d136f67.zip differ diff --git a/.yarn/cache/@types-minimist-npm-1.2.2-a445de65da-b8da83c66e.zip b/.yarn/cache/@types-minimist-npm-1.2.2-a445de65da-b8da83c66e.zip new file mode 100644 index 00000000000..42814291074 Binary files /dev/null and b/.yarn/cache/@types-minimist-npm-1.2.2-a445de65da-b8da83c66e.zip differ diff --git a/.yarn/cache/@types-mocha-npm-8.2.3-7aff51fdb4-b43ed1b642.zip b/.yarn/cache/@types-mocha-npm-8.2.3-7aff51fdb4-b43ed1b642.zip new file mode 100644 index 00000000000..bad62337120 Binary files /dev/null and b/.yarn/cache/@types-mocha-npm-8.2.3-7aff51fdb4-b43ed1b642.zip differ diff --git a/.yarn/cache/@types-node-npm-10.17.60-63ac1f669f-2cdb3a77d0.zip b/.yarn/cache/@types-node-npm-10.17.60-63ac1f669f-2cdb3a77d0.zip new file mode 100644 index 00000000000..3f120443e13 Binary files /dev/null and b/.yarn/cache/@types-node-npm-10.17.60-63ac1f669f-2cdb3a77d0.zip differ diff --git a/.yarn/cache/@types-node-npm-12.20.37-9ce6eac5c0-8c8b12f802.zip b/.yarn/cache/@types-node-npm-12.20.37-9ce6eac5c0-8c8b12f802.zip new file mode 100644 index 00000000000..7b26a029dcd Binary files /dev/null and b/.yarn/cache/@types-node-npm-12.20.37-9ce6eac5c0-8c8b12f802.zip differ diff --git a/.yarn/cache/@types-node-npm-13.13.52-95159539bb-8f1afff497.zip b/.yarn/cache/@types-node-npm-13.13.52-95159539bb-8f1afff497.zip new file mode 100644 index 00000000000..954faa27dfb Binary files /dev/null and b/.yarn/cache/@types-node-npm-13.13.52-95159539bb-8f1afff497.zip differ diff --git a/.yarn/cache/@types-node-npm-14.17.34-1d7f20f643-803a7532b6.zip b/.yarn/cache/@types-node-npm-14.17.34-1d7f20f643-803a7532b6.zip new file mode 100644 index 00000000000..56574d42dac Binary files /dev/null and b/.yarn/cache/@types-node-npm-14.17.34-1d7f20f643-803a7532b6.zip differ diff --git a/.yarn/cache/@types-node-npm-15.14.9-739a59edff-49f7f0522a.zip b/.yarn/cache/@types-node-npm-15.14.9-739a59edff-49f7f0522a.zip new file mode 100644 index 00000000000..18bd5d9249a Binary files /dev/null and b/.yarn/cache/@types-node-npm-15.14.9-739a59edff-49f7f0522a.zip differ diff --git a/.yarn/cache/@types-node-npm-17.0.21-7d68eb6a13-89dcd2fe82.zip b/.yarn/cache/@types-node-npm-17.0.21-7d68eb6a13-89dcd2fe82.zip new file mode 100644 index 00000000000..ccdd01e5a42 Binary files /dev/null and b/.yarn/cache/@types-node-npm-17.0.21-7d68eb6a13-89dcd2fe82.zip differ diff --git a/.yarn/cache/@types-normalize-package-data-npm-2.4.1-c31c56ae6a-e87bccbf11.zip b/.yarn/cache/@types-normalize-package-data-npm-2.4.1-c31c56ae6a-e87bccbf11.zip new file mode 100644 index 00000000000..a17de3f091a Binary files /dev/null and b/.yarn/cache/@types-normalize-package-data-npm-2.4.1-c31c56ae6a-e87bccbf11.zip differ diff --git a/.yarn/cache/@types-pino-npm-6.3.12-19c7982858-8017351466.zip b/.yarn/cache/@types-pino-npm-6.3.12-19c7982858-8017351466.zip new file mode 100644 index 00000000000..dfed8ac6e72 Binary files /dev/null and b/.yarn/cache/@types-pino-npm-6.3.12-19c7982858-8017351466.zip differ diff --git a/.yarn/cache/@types-pino-pretty-npm-4.7.3-5ebf57cfd2-40fe67e73d.zip b/.yarn/cache/@types-pino-pretty-npm-4.7.3-5ebf57cfd2-40fe67e73d.zip new file mode 100644 index 00000000000..98c7a35e627 Binary files /dev/null and b/.yarn/cache/@types-pino-pretty-npm-4.7.3-5ebf57cfd2-40fe67e73d.zip differ diff --git a/.yarn/cache/@types-pino-std-serializers-npm-2.4.1-e7c36178c0-a156e25882.zip b/.yarn/cache/@types-pino-std-serializers-npm-2.4.1-e7c36178c0-a156e25882.zip new file mode 100644 index 00000000000..31a47a212a2 Binary files /dev/null and b/.yarn/cache/@types-pino-std-serializers-npm-2.4.1-e7c36178c0-a156e25882.zip differ diff --git a/.yarn/cache/@types-qs-npm-6.9.7-4a3e6ca0d0-7fd6f9c250.zip b/.yarn/cache/@types-qs-npm-6.9.7-4a3e6ca0d0-7fd6f9c250.zip new file mode 100644 index 00000000000..9137540a999 Binary files /dev/null and b/.yarn/cache/@types-qs-npm-6.9.7-4a3e6ca0d0-7fd6f9c250.zip differ diff --git a/.yarn/cache/@types-range-parser-npm-1.2.4-23d797fbde-b7c0dfd508.zip b/.yarn/cache/@types-range-parser-npm-1.2.4-23d797fbde-b7c0dfd508.zip new file mode 100644 index 00000000000..951f3f1062d Binary files /dev/null and b/.yarn/cache/@types-range-parser-npm-1.2.4-23d797fbde-b7c0dfd508.zip differ diff --git a/.yarn/cache/@types-responselike-npm-1.0.0-85dd08af42-e99fc7cc62.zip b/.yarn/cache/@types-responselike-npm-1.0.0-85dd08af42-e99fc7cc62.zip new file mode 100644 index 00000000000..45d042f89a0 Binary files /dev/null and b/.yarn/cache/@types-responselike-npm-1.0.0-85dd08af42-e99fc7cc62.zip differ diff --git a/.yarn/cache/@types-sinon-chai-npm-3.2.5-1d6490532a-ac332b8f2c.zip b/.yarn/cache/@types-sinon-chai-npm-3.2.5-1d6490532a-ac332b8f2c.zip new file mode 100644 index 00000000000..b7de5483720 Binary files /dev/null and b/.yarn/cache/@types-sinon-chai-npm-3.2.5-1d6490532a-ac332b8f2c.zip differ diff --git a/.yarn/cache/@types-sinon-npm-10.0.6-3a1b027ac2-1c2ae7daa8.zip b/.yarn/cache/@types-sinon-npm-10.0.6-3a1b027ac2-1c2ae7daa8.zip new file mode 100644 index 00000000000..c870bf07326 Binary files /dev/null and b/.yarn/cache/@types-sinon-npm-10.0.6-3a1b027ac2-1c2ae7daa8.zip differ diff --git a/.yarn/cache/@types-sinon-npm-9.0.11-231734b808-2074490973.zip b/.yarn/cache/@types-sinon-npm-9.0.11-231734b808-2074490973.zip new file mode 100644 index 00000000000..982629fdf27 Binary files /dev/null and b/.yarn/cache/@types-sinon-npm-9.0.11-231734b808-2074490973.zip differ diff --git a/.yarn/cache/@types-sinonjs__fake-timers-npm-8.1.0-b26c9e7f56-02d8f5a2c8.zip b/.yarn/cache/@types-sinonjs__fake-timers-npm-8.1.0-b26c9e7f56-02d8f5a2c8.zip new file mode 100644 index 00000000000..502fc38a490 Binary files /dev/null and b/.yarn/cache/@types-sinonjs__fake-timers-npm-8.1.0-b26c9e7f56-02d8f5a2c8.zip differ diff --git a/.yarn/cache/@types-stack-utils-npm-2.0.1-867718ab70-205fdbe332.zip b/.yarn/cache/@types-stack-utils-npm-2.0.1-867718ab70-205fdbe332.zip new file mode 100644 index 00000000000..b381b831fc8 Binary files /dev/null and b/.yarn/cache/@types-stack-utils-npm-2.0.1-867718ab70-205fdbe332.zip differ diff --git a/.yarn/cache/@types-vinyl-npm-2.0.6-62fe43810b-5012fb61e3.zip b/.yarn/cache/@types-vinyl-npm-2.0.6-62fe43810b-5012fb61e3.zip new file mode 100644 index 00000000000..f3b41a945d6 Binary files /dev/null and b/.yarn/cache/@types-vinyl-npm-2.0.6-62fe43810b-5012fb61e3.zip differ diff --git a/.yarn/cache/@types-ws-npm-7.4.7-d0c95c0958-b4c9b8ad20.zip b/.yarn/cache/@types-ws-npm-7.4.7-d0c95c0958-b4c9b8ad20.zip new file mode 100644 index 00000000000..d397de84f0a Binary files /dev/null and b/.yarn/cache/@types-ws-npm-7.4.7-d0c95c0958-b4c9b8ad20.zip differ diff --git a/.yarn/cache/@types-yargs-npm-16.0.4-7aaef7d6c8-caa21d2c95.zip b/.yarn/cache/@types-yargs-npm-16.0.4-7aaef7d6c8-caa21d2c95.zip new file mode 100644 index 00000000000..3f670a569a5 Binary files /dev/null and b/.yarn/cache/@types-yargs-npm-16.0.4-7aaef7d6c8-caa21d2c95.zip differ diff --git a/.yarn/cache/@types-yargs-parser-npm-20.2.1-2eed5b5c1c-1d039e6449.zip b/.yarn/cache/@types-yargs-parser-npm-20.2.1-2eed5b5c1c-1d039e6449.zip new file mode 100644 index 00000000000..730031fc070 Binary files /dev/null and b/.yarn/cache/@types-yargs-parser-npm-20.2.1-2eed5b5c1c-1d039e6449.zip differ diff --git a/.yarn/cache/@ungap-promise-all-settled-npm-1.1.2-c0f42e147b-08d37fdfa2.zip b/.yarn/cache/@ungap-promise-all-settled-npm-1.1.2-c0f42e147b-08d37fdfa2.zip new file mode 100644 index 00000000000..074ceb3e039 Binary files /dev/null and b/.yarn/cache/@ungap-promise-all-settled-npm-1.1.2-c0f42e147b-08d37fdfa2.zip differ diff --git a/.yarn/cache/@webassemblyjs-ast-npm-1.11.1-623d3d973e-1eee1534ad.zip b/.yarn/cache/@webassemblyjs-ast-npm-1.11.1-623d3d973e-1eee1534ad.zip new file mode 100644 index 00000000000..42dd17df83e Binary files /dev/null and b/.yarn/cache/@webassemblyjs-ast-npm-1.11.1-623d3d973e-1eee1534ad.zip differ diff --git a/.yarn/cache/@webassemblyjs-floating-point-hex-parser-npm-1.11.1-f8af5c0037-b8efc6fa08.zip b/.yarn/cache/@webassemblyjs-floating-point-hex-parser-npm-1.11.1-f8af5c0037-b8efc6fa08.zip new file mode 100644 index 00000000000..9b03be94399 Binary files /dev/null and b/.yarn/cache/@webassemblyjs-floating-point-hex-parser-npm-1.11.1-f8af5c0037-b8efc6fa08.zip differ diff --git a/.yarn/cache/@webassemblyjs-helper-api-error-npm-1.11.1-b839d59053-0792813f0e.zip b/.yarn/cache/@webassemblyjs-helper-api-error-npm-1.11.1-b839d59053-0792813f0e.zip new file mode 100644 index 00000000000..28665e752bb Binary files /dev/null and b/.yarn/cache/@webassemblyjs-helper-api-error-npm-1.11.1-b839d59053-0792813f0e.zip differ diff --git a/.yarn/cache/@webassemblyjs-helper-buffer-npm-1.11.1-6afb1ef4aa-a337ee44b4.zip b/.yarn/cache/@webassemblyjs-helper-buffer-npm-1.11.1-6afb1ef4aa-a337ee44b4.zip new file mode 100644 index 00000000000..c4c06dd687a Binary files /dev/null and b/.yarn/cache/@webassemblyjs-helper-buffer-npm-1.11.1-6afb1ef4aa-a337ee44b4.zip differ diff --git a/.yarn/cache/@webassemblyjs-helper-numbers-npm-1.11.1-a41f7439eb-44d2905dac.zip b/.yarn/cache/@webassemblyjs-helper-numbers-npm-1.11.1-a41f7439eb-44d2905dac.zip new file mode 100644 index 00000000000..e62e0cc99a3 Binary files /dev/null and b/.yarn/cache/@webassemblyjs-helper-numbers-npm-1.11.1-a41f7439eb-44d2905dac.zip differ diff --git a/.yarn/cache/@webassemblyjs-helper-wasm-bytecode-npm-1.11.1-84f0ee4c30-eac4001131.zip b/.yarn/cache/@webassemblyjs-helper-wasm-bytecode-npm-1.11.1-84f0ee4c30-eac4001131.zip new file mode 100644 index 00000000000..eae9fa0c580 Binary files /dev/null and b/.yarn/cache/@webassemblyjs-helper-wasm-bytecode-npm-1.11.1-84f0ee4c30-eac4001131.zip differ diff --git a/.yarn/cache/@webassemblyjs-helper-wasm-section-npm-1.11.1-e4e8450b9d-617696cfe8.zip b/.yarn/cache/@webassemblyjs-helper-wasm-section-npm-1.11.1-e4e8450b9d-617696cfe8.zip new file mode 100644 index 00000000000..77694dc9801 Binary files /dev/null and b/.yarn/cache/@webassemblyjs-helper-wasm-section-npm-1.11.1-e4e8450b9d-617696cfe8.zip differ diff --git a/.yarn/cache/@webassemblyjs-ieee754-npm-1.11.1-897eb85879-23a0ac02a5.zip b/.yarn/cache/@webassemblyjs-ieee754-npm-1.11.1-897eb85879-23a0ac02a5.zip new file mode 100644 index 00000000000..fd9e4c5e99c Binary files /dev/null and b/.yarn/cache/@webassemblyjs-ieee754-npm-1.11.1-897eb85879-23a0ac02a5.zip differ diff --git a/.yarn/cache/@webassemblyjs-leb128-npm-1.11.1-fd9f27673d-33ccc4ade2.zip b/.yarn/cache/@webassemblyjs-leb128-npm-1.11.1-fd9f27673d-33ccc4ade2.zip new file mode 100644 index 00000000000..e696bafa1e1 Binary files /dev/null and b/.yarn/cache/@webassemblyjs-leb128-npm-1.11.1-fd9f27673d-33ccc4ade2.zip differ diff --git a/.yarn/cache/@webassemblyjs-utf8-npm-1.11.1-583036e767-972c5cfc76.zip b/.yarn/cache/@webassemblyjs-utf8-npm-1.11.1-583036e767-972c5cfc76.zip new file mode 100644 index 00000000000..0559d788184 Binary files /dev/null and b/.yarn/cache/@webassemblyjs-utf8-npm-1.11.1-583036e767-972c5cfc76.zip differ diff --git a/.yarn/cache/@webassemblyjs-wasm-edit-npm-1.11.1-34565c1e92-6d7d9efaec.zip b/.yarn/cache/@webassemblyjs-wasm-edit-npm-1.11.1-34565c1e92-6d7d9efaec.zip new file mode 100644 index 00000000000..14dae414a78 Binary files /dev/null and b/.yarn/cache/@webassemblyjs-wasm-edit-npm-1.11.1-34565c1e92-6d7d9efaec.zip differ diff --git a/.yarn/cache/@webassemblyjs-wasm-gen-npm-1.11.1-a6d0b4d37d-1f6921e640.zip b/.yarn/cache/@webassemblyjs-wasm-gen-npm-1.11.1-a6d0b4d37d-1f6921e640.zip new file mode 100644 index 00000000000..419b6a36504 Binary files /dev/null and b/.yarn/cache/@webassemblyjs-wasm-gen-npm-1.11.1-a6d0b4d37d-1f6921e640.zip differ diff --git a/.yarn/cache/@webassemblyjs-wasm-opt-npm-1.11.1-0bb73c20b9-21586883a2.zip b/.yarn/cache/@webassemblyjs-wasm-opt-npm-1.11.1-0bb73c20b9-21586883a2.zip new file mode 100644 index 00000000000..96100b1b5cf Binary files /dev/null and b/.yarn/cache/@webassemblyjs-wasm-opt-npm-1.11.1-0bb73c20b9-21586883a2.zip differ diff --git a/.yarn/cache/@webassemblyjs-wasm-parser-npm-1.11.1-cd49c51fdc-1521644065.zip b/.yarn/cache/@webassemblyjs-wasm-parser-npm-1.11.1-cd49c51fdc-1521644065.zip new file mode 100644 index 00000000000..7003b8acaec Binary files /dev/null and b/.yarn/cache/@webassemblyjs-wasm-parser-npm-1.11.1-cd49c51fdc-1521644065.zip differ diff --git a/.yarn/cache/@webassemblyjs-wast-printer-npm-1.11.1-f1213430d6-f15ae4c244.zip b/.yarn/cache/@webassemblyjs-wast-printer-npm-1.11.1-f1213430d6-f15ae4c244.zip new file mode 100644 index 00000000000..366b7cb0d97 Binary files /dev/null and b/.yarn/cache/@webassemblyjs-wast-printer-npm-1.11.1-f1213430d6-f15ae4c244.zip differ diff --git a/.yarn/cache/@webpack-cli-configtest-npm-1.1.0-2b6b2ef3d7-69e7816b5b.zip b/.yarn/cache/@webpack-cli-configtest-npm-1.1.0-2b6b2ef3d7-69e7816b5b.zip new file mode 100644 index 00000000000..fb746da1a84 Binary files /dev/null and b/.yarn/cache/@webpack-cli-configtest-npm-1.1.0-2b6b2ef3d7-69e7816b5b.zip differ diff --git a/.yarn/cache/@webpack-cli-info-npm-1.4.0-4a26ccee64-6385b1e2c5.zip b/.yarn/cache/@webpack-cli-info-npm-1.4.0-4a26ccee64-6385b1e2c5.zip new file mode 100644 index 00000000000..af2cdcff6ed Binary files /dev/null and b/.yarn/cache/@webpack-cli-info-npm-1.4.0-4a26ccee64-6385b1e2c5.zip differ diff --git a/.yarn/cache/@webpack-cli-serve-npm-1.6.0-c7b35aa4ef-050a930b63.zip b/.yarn/cache/@webpack-cli-serve-npm-1.6.0-c7b35aa4ef-050a930b63.zip new file mode 100644 index 00000000000..9e8a099c225 Binary files /dev/null and b/.yarn/cache/@webpack-cli-serve-npm-1.6.0-c7b35aa4ef-050a930b63.zip differ diff --git a/.yarn/cache/@xtuc-ieee754-npm-1.2.0-ec0ce4e025-ac56d4ca6e.zip b/.yarn/cache/@xtuc-ieee754-npm-1.2.0-ec0ce4e025-ac56d4ca6e.zip new file mode 100644 index 00000000000..be075971a9c Binary files /dev/null and b/.yarn/cache/@xtuc-ieee754-npm-1.2.0-ec0ce4e025-ac56d4ca6e.zip differ diff --git a/.yarn/cache/@xtuc-long-npm-4.2.2-37236e6d72-8ed0d477ce.zip b/.yarn/cache/@xtuc-long-npm-4.2.2-37236e6d72-8ed0d477ce.zip new file mode 100644 index 00000000000..392ac465ad2 Binary files /dev/null and b/.yarn/cache/@xtuc-long-npm-4.2.2-37236e6d72-8ed0d477ce.zip differ diff --git a/.yarn/cache/JSONStream-npm-1.3.5-1987f2e6dd-2605fa1242.zip b/.yarn/cache/JSONStream-npm-1.3.5-1987f2e6dd-2605fa1242.zip new file mode 100644 index 00000000000..bd4533e7c37 Binary files /dev/null and b/.yarn/cache/JSONStream-npm-1.3.5-1987f2e6dd-2605fa1242.zip differ diff --git a/.yarn/cache/abbrev-npm-1.1.1-3659247eab-a4a97ec07d.zip b/.yarn/cache/abbrev-npm-1.1.1-3659247eab-a4a97ec07d.zip new file mode 100644 index 00000000000..a8b40a5f9dc Binary files /dev/null and b/.yarn/cache/abbrev-npm-1.1.1-3659247eab-a4a97ec07d.zip differ diff --git a/.yarn/cache/abstract-leveldown-npm-6.2.3-73e4ffefa5-00202b2eb7.zip b/.yarn/cache/abstract-leveldown-npm-6.2.3-73e4ffefa5-00202b2eb7.zip new file mode 100644 index 00000000000..aa83ed4a587 Binary files /dev/null and b/.yarn/cache/abstract-leveldown-npm-6.2.3-73e4ffefa5-00202b2eb7.zip differ diff --git a/.yarn/cache/accepts-npm-1.3.7-0dc9de65aa-27fc8060ff.zip b/.yarn/cache/accepts-npm-1.3.7-0dc9de65aa-27fc8060ff.zip new file mode 100644 index 00000000000..34a9001a26c Binary files /dev/null and b/.yarn/cache/accepts-npm-1.3.7-0dc9de65aa-27fc8060ff.zip differ diff --git a/.yarn/cache/acorn-import-assertions-npm-1.8.0-e9a9d57e27-5c4cf7c850.zip b/.yarn/cache/acorn-import-assertions-npm-1.8.0-e9a9d57e27-5c4cf7c850.zip new file mode 100644 index 00000000000..3322c652bf6 Binary files /dev/null and b/.yarn/cache/acorn-import-assertions-npm-1.8.0-e9a9d57e27-5c4cf7c850.zip differ diff --git a/.yarn/cache/acorn-jsx-npm-5.3.2-d7594599ea-c3d3b2a89c.zip b/.yarn/cache/acorn-jsx-npm-5.3.2-d7594599ea-c3d3b2a89c.zip new file mode 100644 index 00000000000..786b9ec4f1b Binary files /dev/null and b/.yarn/cache/acorn-jsx-npm-5.3.2-d7594599ea-c3d3b2a89c.zip differ diff --git a/.yarn/cache/acorn-node-npm-1.8.2-b30b72c499-02e1564a1c.zip b/.yarn/cache/acorn-node-npm-1.8.2-b30b72c499-02e1564a1c.zip new file mode 100644 index 00000000000..16b51107c18 Binary files /dev/null and b/.yarn/cache/acorn-node-npm-1.8.2-b30b72c499-02e1564a1c.zip differ diff --git a/.yarn/cache/acorn-npm-7.4.1-f450b4646c-1860f23c21.zip b/.yarn/cache/acorn-npm-7.4.1-f450b4646c-1860f23c21.zip new file mode 100644 index 00000000000..9fdd0487b42 Binary files /dev/null and b/.yarn/cache/acorn-npm-7.4.1-f450b4646c-1860f23c21.zip differ diff --git a/.yarn/cache/acorn-npm-8.6.0-9de50afc7d-9d0de73b73.zip b/.yarn/cache/acorn-npm-8.6.0-9de50afc7d-9d0de73b73.zip new file mode 100644 index 00000000000..83abbdb6442 Binary files /dev/null and b/.yarn/cache/acorn-npm-8.6.0-9de50afc7d-9d0de73b73.zip differ diff --git a/.yarn/cache/acorn-walk-npm-7.2.0-5f8b515308-9252158a79.zip b/.yarn/cache/acorn-walk-npm-7.2.0-5f8b515308-9252158a79.zip new file mode 100644 index 00000000000..db97eed38c3 Binary files /dev/null and b/.yarn/cache/acorn-walk-npm-7.2.0-5f8b515308-9252158a79.zip differ diff --git a/.yarn/cache/acorn-walk-npm-8.2.0-2f2cac3177-1715e76c01.zip b/.yarn/cache/acorn-walk-npm-8.2.0-2f2cac3177-1715e76c01.zip new file mode 100644 index 00000000000..f140c4ab5c2 Binary files /dev/null and b/.yarn/cache/acorn-walk-npm-8.2.0-2f2cac3177-1715e76c01.zip differ diff --git a/.yarn/cache/add-stream-npm-1.0.0-a5a0c0498c-3e9e8b0b8f.zip b/.yarn/cache/add-stream-npm-1.0.0-a5a0c0498c-3e9e8b0b8f.zip new file mode 100644 index 00000000000..a013e55da95 Binary files /dev/null and b/.yarn/cache/add-stream-npm-1.0.0-a5a0c0498c-3e9e8b0b8f.zip differ diff --git a/.yarn/cache/agent-base-npm-6.0.2-428f325a93-f52b6872cc.zip b/.yarn/cache/agent-base-npm-6.0.2-428f325a93-f52b6872cc.zip new file mode 100644 index 00000000000..c7d271af28b Binary files /dev/null and b/.yarn/cache/agent-base-npm-6.0.2-428f325a93-f52b6872cc.zip differ diff --git a/.yarn/cache/agentkeepalive-npm-4.2.0-e5e72b8ce4-89806f83ce.zip b/.yarn/cache/agentkeepalive-npm-4.2.0-e5e72b8ce4-89806f83ce.zip new file mode 100644 index 00000000000..d4bc2c20a8b Binary files /dev/null and b/.yarn/cache/agentkeepalive-npm-4.2.0-e5e72b8ce4-89806f83ce.zip differ diff --git a/.yarn/cache/aggregate-error-npm-3.1.0-415a406f4e-1101a33f21.zip b/.yarn/cache/aggregate-error-npm-3.1.0-415a406f4e-1101a33f21.zip new file mode 100644 index 00000000000..7db0127bfdb Binary files /dev/null and b/.yarn/cache/aggregate-error-npm-3.1.0-415a406f4e-1101a33f21.zip differ diff --git a/.yarn/cache/ajv-formats-npm-2.1.1-3cec02eae9-4a287d937f.zip b/.yarn/cache/ajv-formats-npm-2.1.1-3cec02eae9-4a287d937f.zip new file mode 100644 index 00000000000..04111da95e7 Binary files /dev/null and b/.yarn/cache/ajv-formats-npm-2.1.1-3cec02eae9-4a287d937f.zip differ diff --git a/.yarn/cache/ajv-keywords-npm-3.5.2-0e391b70e2-7dc5e59316.zip b/.yarn/cache/ajv-keywords-npm-3.5.2-0e391b70e2-7dc5e59316.zip new file mode 100644 index 00000000000..cb1e9955eaa Binary files /dev/null and b/.yarn/cache/ajv-keywords-npm-3.5.2-0e391b70e2-7dc5e59316.zip differ diff --git a/.yarn/cache/ajv-keywords-npm-5.0.0-50b946aaa2-239dd46383.zip b/.yarn/cache/ajv-keywords-npm-5.0.0-50b946aaa2-239dd46383.zip new file mode 100644 index 00000000000..7ebc0a276f6 Binary files /dev/null and b/.yarn/cache/ajv-keywords-npm-5.0.0-50b946aaa2-239dd46383.zip differ diff --git a/.yarn/cache/ajv-npm-6.12.6-4b5105e2b2-874972efe5.zip b/.yarn/cache/ajv-npm-6.12.6-4b5105e2b2-874972efe5.zip new file mode 100644 index 00000000000..16973dd8c54 Binary files /dev/null and b/.yarn/cache/ajv-npm-6.12.6-4b5105e2b2-874972efe5.zip differ diff --git a/.yarn/cache/ajv-npm-8.8.1-3d331224e3-1d586cea81.zip b/.yarn/cache/ajv-npm-8.8.1-3d331224e3-1d586cea81.zip new file mode 100644 index 00000000000..3e00dfd7625 Binary files /dev/null and b/.yarn/cache/ajv-npm-8.8.1-3d331224e3-1d586cea81.zip differ diff --git a/.yarn/cache/ansi-align-npm-3.0.1-8e6288d20a-6abfa08f21.zip b/.yarn/cache/ansi-align-npm-3.0.1-8e6288d20a-6abfa08f21.zip new file mode 100644 index 00000000000..faf9ad44561 Binary files /dev/null and b/.yarn/cache/ansi-align-npm-3.0.1-8e6288d20a-6abfa08f21.zip differ diff --git a/.yarn/cache/ansi-colors-npm-4.1.1-97ad42f223-138d04a510.zip b/.yarn/cache/ansi-colors-npm-4.1.1-97ad42f223-138d04a510.zip new file mode 100644 index 00000000000..19c6d99a78e Binary files /dev/null and b/.yarn/cache/ansi-colors-npm-4.1.1-97ad42f223-138d04a510.zip differ diff --git a/.yarn/cache/ansi-escapes-npm-3.2.0-a9d573100e-0f94695b67.zip b/.yarn/cache/ansi-escapes-npm-3.2.0-a9d573100e-0f94695b67.zip new file mode 100644 index 00000000000..6faf69c10b3 Binary files /dev/null and b/.yarn/cache/ansi-escapes-npm-3.2.0-a9d573100e-0f94695b67.zip differ diff --git a/.yarn/cache/ansi-escapes-npm-4.3.2-3ad173702f-93111c4218.zip b/.yarn/cache/ansi-escapes-npm-4.3.2-3ad173702f-93111c4218.zip new file mode 100644 index 00000000000..6b90effb518 Binary files /dev/null and b/.yarn/cache/ansi-escapes-npm-4.3.2-3ad173702f-93111c4218.zip differ diff --git a/.yarn/cache/ansi-regex-npm-2.1.1-ddd24d102b-190abd03e4.zip b/.yarn/cache/ansi-regex-npm-2.1.1-ddd24d102b-190abd03e4.zip new file mode 100644 index 00000000000..39b46403778 Binary files /dev/null and b/.yarn/cache/ansi-regex-npm-2.1.1-ddd24d102b-190abd03e4.zip differ diff --git a/.yarn/cache/ansi-regex-npm-3.0.0-be0b845911-2ad11c416f.zip b/.yarn/cache/ansi-regex-npm-3.0.0-be0b845911-2ad11c416f.zip new file mode 100644 index 00000000000..d0c2902890f Binary files /dev/null and b/.yarn/cache/ansi-regex-npm-3.0.0-be0b845911-2ad11c416f.zip differ diff --git a/.yarn/cache/ansi-regex-npm-4.1.0-4a7d8413fe-97aa465953.zip b/.yarn/cache/ansi-regex-npm-4.1.0-4a7d8413fe-97aa465953.zip new file mode 100644 index 00000000000..7ff4a9216a6 Binary files /dev/null and b/.yarn/cache/ansi-regex-npm-4.1.0-4a7d8413fe-97aa465953.zip differ diff --git a/.yarn/cache/ansi-regex-npm-5.0.1-c963a48615-2aa4bb54ca.zip b/.yarn/cache/ansi-regex-npm-5.0.1-c963a48615-2aa4bb54ca.zip new file mode 100644 index 00000000000..fffc17acac2 Binary files /dev/null and b/.yarn/cache/ansi-regex-npm-5.0.1-c963a48615-2aa4bb54ca.zip differ diff --git a/.yarn/cache/ansi-split-npm-1.0.1-586a5367da-301b98e935.zip b/.yarn/cache/ansi-split-npm-1.0.1-586a5367da-301b98e935.zip new file mode 100644 index 00000000000..8b47190c3fc Binary files /dev/null and b/.yarn/cache/ansi-split-npm-1.0.1-586a5367da-301b98e935.zip differ diff --git a/.yarn/cache/ansi-styles-npm-2.2.1-f3297e782c-ebc0e00381.zip b/.yarn/cache/ansi-styles-npm-2.2.1-f3297e782c-ebc0e00381.zip new file mode 100644 index 00000000000..5581240ca2b Binary files /dev/null and b/.yarn/cache/ansi-styles-npm-2.2.1-f3297e782c-ebc0e00381.zip differ diff --git a/.yarn/cache/ansi-styles-npm-3.2.1-8cb8107983-d85ade01c1.zip b/.yarn/cache/ansi-styles-npm-3.2.1-8cb8107983-d85ade01c1.zip new file mode 100644 index 00000000000..4ffdcc49463 Binary files /dev/null and b/.yarn/cache/ansi-styles-npm-3.2.1-8cb8107983-d85ade01c1.zip differ diff --git a/.yarn/cache/ansi-styles-npm-4.3.0-245c7d42c7-513b44c3b2.zip b/.yarn/cache/ansi-styles-npm-4.3.0-245c7d42c7-513b44c3b2.zip new file mode 100644 index 00000000000..a18e3e6439c Binary files /dev/null and b/.yarn/cache/ansi-styles-npm-4.3.0-245c7d42c7-513b44c3b2.zip differ diff --git a/.yarn/cache/ansi-styles-npm-5.2.0-72fc7003e3-d7f4e97ce0.zip b/.yarn/cache/ansi-styles-npm-5.2.0-72fc7003e3-d7f4e97ce0.zip new file mode 100644 index 00000000000..62c09039bd1 Binary files /dev/null and b/.yarn/cache/ansi-styles-npm-5.2.0-72fc7003e3-d7f4e97ce0.zip differ diff --git a/.yarn/cache/ansicolors-npm-0.3.2-cc35882814-e84fae7ebc.zip b/.yarn/cache/ansicolors-npm-0.3.2-cc35882814-e84fae7ebc.zip new file mode 100644 index 00000000000..ca253e36f17 Binary files /dev/null and b/.yarn/cache/ansicolors-npm-0.3.2-cc35882814-e84fae7ebc.zip differ diff --git a/.yarn/cache/anymatch-npm-3.1.2-1d5471acfa-985163db22.zip b/.yarn/cache/anymatch-npm-3.1.2-1d5471acfa-985163db22.zip new file mode 100644 index 00000000000..b71280dc2a7 Binary files /dev/null and b/.yarn/cache/anymatch-npm-3.1.2-1d5471acfa-985163db22.zip differ diff --git a/.yarn/cache/append-transform-npm-2.0.0-99bd7d69ed-f26f393bf7.zip b/.yarn/cache/append-transform-npm-2.0.0-99bd7d69ed-f26f393bf7.zip new file mode 100644 index 00000000000..005d3fdd9a3 Binary files /dev/null and b/.yarn/cache/append-transform-npm-2.0.0-99bd7d69ed-f26f393bf7.zip differ diff --git a/.yarn/cache/aproba-npm-1.2.0-34129f0778-0fca141966.zip b/.yarn/cache/aproba-npm-1.2.0-34129f0778-0fca141966.zip new file mode 100644 index 00000000000..87d8517ee4c Binary files /dev/null and b/.yarn/cache/aproba-npm-1.2.0-34129f0778-0fca141966.zip differ diff --git a/.yarn/cache/aproba-npm-2.0.0-8716bcfde6-5615cadcfb.zip b/.yarn/cache/aproba-npm-2.0.0-8716bcfde6-5615cadcfb.zip new file mode 100644 index 00000000000..6b148888c0e Binary files /dev/null and b/.yarn/cache/aproba-npm-2.0.0-8716bcfde6-5615cadcfb.zip differ diff --git a/.yarn/cache/archy-npm-1.0.0-7db8bfdc3b-504ae7af65.zip b/.yarn/cache/archy-npm-1.0.0-7db8bfdc3b-504ae7af65.zip new file mode 100644 index 00000000000..2ab9f669483 Binary files /dev/null and b/.yarn/cache/archy-npm-1.0.0-7db8bfdc3b-504ae7af65.zip differ diff --git a/.yarn/cache/are-we-there-yet-npm-1.1.7-db9f39924e-70d251719c.zip b/.yarn/cache/are-we-there-yet-npm-1.1.7-db9f39924e-70d251719c.zip new file mode 100644 index 00000000000..464871d0a5a Binary files /dev/null and b/.yarn/cache/are-we-there-yet-npm-1.1.7-db9f39924e-70d251719c.zip differ diff --git a/.yarn/cache/are-we-there-yet-npm-2.0.0-7d2f5201ce-6c80b4fd04.zip b/.yarn/cache/are-we-there-yet-npm-2.0.0-7d2f5201ce-6c80b4fd04.zip new file mode 100644 index 00000000000..41d8c663d3c Binary files /dev/null and b/.yarn/cache/are-we-there-yet-npm-2.0.0-7d2f5201ce-6c80b4fd04.zip differ diff --git a/.yarn/cache/are-we-there-yet-npm-3.0.0-1391430190-348edfdd93.zip b/.yarn/cache/are-we-there-yet-npm-3.0.0-1391430190-348edfdd93.zip new file mode 100644 index 00000000000..b4d0a71b828 Binary files /dev/null and b/.yarn/cache/are-we-there-yet-npm-3.0.0-1391430190-348edfdd93.zip differ diff --git a/.yarn/cache/arg-npm-4.1.3-1748b966a8-544af8dd3f.zip b/.yarn/cache/arg-npm-4.1.3-1748b966a8-544af8dd3f.zip new file mode 100644 index 00000000000..21128e2b875 Binary files /dev/null and b/.yarn/cache/arg-npm-4.1.3-1748b966a8-544af8dd3f.zip differ diff --git a/.yarn/cache/argparse-npm-1.0.10-528934e59d-7ca6e45583.zip b/.yarn/cache/argparse-npm-1.0.10-528934e59d-7ca6e45583.zip new file mode 100644 index 00000000000..5cd3176e95c Binary files /dev/null and b/.yarn/cache/argparse-npm-1.0.10-528934e59d-7ca6e45583.zip differ diff --git a/.yarn/cache/argparse-npm-2.0.1-faff7999e6-83644b5649.zip b/.yarn/cache/argparse-npm-2.0.1-faff7999e6-83644b5649.zip new file mode 100644 index 00000000000..26a9ce4aca9 Binary files /dev/null and b/.yarn/cache/argparse-npm-2.0.1-faff7999e6-83644b5649.zip differ diff --git a/.yarn/cache/args-npm-5.0.1-cd7b0f9dcc-51e2a05f32.zip b/.yarn/cache/args-npm-5.0.1-cd7b0f9dcc-51e2a05f32.zip new file mode 100644 index 00000000000..5a4d861d199 Binary files /dev/null and b/.yarn/cache/args-npm-5.0.1-cd7b0f9dcc-51e2a05f32.zip differ diff --git a/.yarn/cache/array-differ-npm-3.0.0-ddc0d89007-117edd9df5.zip b/.yarn/cache/array-differ-npm-3.0.0-ddc0d89007-117edd9df5.zip new file mode 100644 index 00000000000..292ae640ef4 Binary files /dev/null and b/.yarn/cache/array-differ-npm-3.0.0-ddc0d89007-117edd9df5.zip differ diff --git a/.yarn/cache/array-ify-npm-1.0.0-e09a371977-c0502015b3.zip b/.yarn/cache/array-ify-npm-1.0.0-e09a371977-c0502015b3.zip new file mode 100644 index 00000000000..7b98d69afec Binary files /dev/null and b/.yarn/cache/array-ify-npm-1.0.0-e09a371977-c0502015b3.zip differ diff --git a/.yarn/cache/array-includes-npm-3.1.4-79bb883109-69967c38c5.zip b/.yarn/cache/array-includes-npm-3.1.4-79bb883109-69967c38c5.zip new file mode 100644 index 00000000000..c88aec7c23b Binary files /dev/null and b/.yarn/cache/array-includes-npm-3.1.4-79bb883109-69967c38c5.zip differ diff --git a/.yarn/cache/array-union-npm-2.1.0-4e4852b221-5bee12395c.zip b/.yarn/cache/array-union-npm-2.1.0-4e4852b221-5bee12395c.zip new file mode 100644 index 00000000000..b51da2ed384 Binary files /dev/null and b/.yarn/cache/array-union-npm-2.1.0-4e4852b221-5bee12395c.zip differ diff --git a/.yarn/cache/array.prototype.flat-npm-1.2.5-6ee21996a1-9cc6414b11.zip b/.yarn/cache/array.prototype.flat-npm-1.2.5-6ee21996a1-9cc6414b11.zip new file mode 100644 index 00000000000..abe536a1141 Binary files /dev/null and b/.yarn/cache/array.prototype.flat-npm-1.2.5-6ee21996a1-9cc6414b11.zip differ diff --git a/.yarn/cache/arrify-npm-1.0.1-affafba9fe-745075dd4a.zip b/.yarn/cache/arrify-npm-1.0.1-affafba9fe-745075dd4a.zip new file mode 100644 index 00000000000..a8cbb301663 Binary files /dev/null and b/.yarn/cache/arrify-npm-1.0.1-affafba9fe-745075dd4a.zip differ diff --git a/.yarn/cache/arrify-npm-2.0.1-38c408f77c-067c4c1afd.zip b/.yarn/cache/arrify-npm-2.0.1-38c408f77c-067c4c1afd.zip new file mode 100644 index 00000000000..5dbd5402d57 Binary files /dev/null and b/.yarn/cache/arrify-npm-2.0.1-38c408f77c-067c4c1afd.zip differ diff --git a/.yarn/cache/asap-npm-2.0.6-36714d439d-b296c92c4b.zip b/.yarn/cache/asap-npm-2.0.6-36714d439d-b296c92c4b.zip new file mode 100644 index 00000000000..ca3c4366bdd Binary files /dev/null and b/.yarn/cache/asap-npm-2.0.6-36714d439d-b296c92c4b.zip differ diff --git a/.yarn/cache/asn1-npm-0.2.6-bdd07356c4-39f2ae343b.zip b/.yarn/cache/asn1-npm-0.2.6-bdd07356c4-39f2ae343b.zip new file mode 100644 index 00000000000..a6463962d7b Binary files /dev/null and b/.yarn/cache/asn1-npm-0.2.6-bdd07356c4-39f2ae343b.zip differ diff --git a/.yarn/cache/asn1.js-npm-5.4.1-37c7edbcb0-3786a101ac.zip b/.yarn/cache/asn1.js-npm-5.4.1-37c7edbcb0-3786a101ac.zip new file mode 100644 index 00000000000..39e96a163d4 Binary files /dev/null and b/.yarn/cache/asn1.js-npm-5.4.1-37c7edbcb0-3786a101ac.zip differ diff --git a/.yarn/cache/assert-browserify-npm-2.0.0-8125e483ea-93c167293e.zip b/.yarn/cache/assert-browserify-npm-2.0.0-8125e483ea-93c167293e.zip new file mode 100644 index 00000000000..1ce61b90772 Binary files /dev/null and b/.yarn/cache/assert-browserify-npm-2.0.0-8125e483ea-93c167293e.zip differ diff --git a/.yarn/cache/assert-npm-1.5.0-3303b97e04-9be48435f7.zip b/.yarn/cache/assert-npm-1.5.0-3303b97e04-9be48435f7.zip new file mode 100644 index 00000000000..6d2fc193f8c Binary files /dev/null and b/.yarn/cache/assert-npm-1.5.0-3303b97e04-9be48435f7.zip differ diff --git a/.yarn/cache/assert-npm-2.0.0-ef73bc19f5-bb91f181a8.zip b/.yarn/cache/assert-npm-2.0.0-ef73bc19f5-bb91f181a8.zip new file mode 100644 index 00000000000..80543ed0e0d Binary files /dev/null and b/.yarn/cache/assert-npm-2.0.0-ef73bc19f5-bb91f181a8.zip differ diff --git a/.yarn/cache/assert-plus-npm-1.0.0-cac95ef098-19b4340cb8.zip b/.yarn/cache/assert-plus-npm-1.0.0-cac95ef098-19b4340cb8.zip new file mode 100644 index 00000000000..30c557d6878 Binary files /dev/null and b/.yarn/cache/assert-plus-npm-1.0.0-cac95ef098-19b4340cb8.zip differ diff --git a/.yarn/cache/assertion-error-npm-1.1.0-66b893015e-fd9429d3a3.zip b/.yarn/cache/assertion-error-npm-1.1.0-66b893015e-fd9429d3a3.zip new file mode 100644 index 00000000000..e7b45eee3a5 Binary files /dev/null and b/.yarn/cache/assertion-error-npm-1.1.0-66b893015e-fd9429d3a3.zip differ diff --git a/.yarn/cache/astral-regex-npm-1.0.0-2df7c41332-93417fc087.zip b/.yarn/cache/astral-regex-npm-1.0.0-2df7c41332-93417fc087.zip new file mode 100644 index 00000000000..d8a1b724e6b Binary files /dev/null and b/.yarn/cache/astral-regex-npm-1.0.0-2df7c41332-93417fc087.zip differ diff --git a/.yarn/cache/astral-regex-npm-2.0.0-f30d866aab-876231688c.zip b/.yarn/cache/astral-regex-npm-2.0.0-f30d866aab-876231688c.zip new file mode 100644 index 00000000000..1af622c047d Binary files /dev/null and b/.yarn/cache/astral-regex-npm-2.0.0-f30d866aab-876231688c.zip differ diff --git a/.yarn/cache/async-npm-0.9.2-d8cafe6cc3-87dbf12929.zip b/.yarn/cache/async-npm-0.9.2-d8cafe6cc3-87dbf12929.zip new file mode 100644 index 00000000000..142effc28a9 Binary files /dev/null and b/.yarn/cache/async-npm-0.9.2-d8cafe6cc3-87dbf12929.zip differ diff --git a/.yarn/cache/async-npm-1.5.2-e971969e27-fe5d6214d8.zip b/.yarn/cache/async-npm-1.5.2-e971969e27-fe5d6214d8.zip new file mode 100644 index 00000000000..374b6228316 Binary files /dev/null and b/.yarn/cache/async-npm-1.5.2-e971969e27-fe5d6214d8.zip differ diff --git a/.yarn/cache/async-npm-3.2.2-0245d236b6-90712c98df.zip b/.yarn/cache/async-npm-3.2.2-0245d236b6-90712c98df.zip new file mode 100644 index 00000000000..7594e36367c Binary files /dev/null and b/.yarn/cache/async-npm-3.2.2-0245d236b6-90712c98df.zip differ diff --git a/.yarn/cache/asynckit-npm-0.4.0-c718858525-7b78c451df.zip b/.yarn/cache/asynckit-npm-0.4.0-c718858525-7b78c451df.zip new file mode 100644 index 00000000000..bb08c24f1bf Binary files /dev/null and b/.yarn/cache/asynckit-npm-0.4.0-c718858525-7b78c451df.zip differ diff --git a/.yarn/cache/at-least-node-npm-1.0.0-2b36e661fa-463e2f8e43.zip b/.yarn/cache/at-least-node-npm-1.0.0-2b36e661fa-463e2f8e43.zip new file mode 100644 index 00000000000..bc549750e64 Binary files /dev/null and b/.yarn/cache/at-least-node-npm-1.0.0-2b36e661fa-463e2f8e43.zip differ diff --git a/.yarn/cache/atomic-sleep-npm-1.0.0-17d8a762a3-b95275afb2.zip b/.yarn/cache/atomic-sleep-npm-1.0.0-17d8a762a3-b95275afb2.zip new file mode 100644 index 00000000000..d172f9448b5 Binary files /dev/null and b/.yarn/cache/atomic-sleep-npm-1.0.0-17d8a762a3-b95275afb2.zip differ diff --git a/.yarn/cache/available-typed-arrays-npm-1.0.5-88f321e4d3-20eb47b3ce.zip b/.yarn/cache/available-typed-arrays-npm-1.0.5-88f321e4d3-20eb47b3ce.zip new file mode 100644 index 00000000000..62f8601d5bb Binary files /dev/null and b/.yarn/cache/available-typed-arrays-npm-1.0.5-88f321e4d3-20eb47b3ce.zip differ diff --git a/.yarn/cache/awilix-npm-4.3.4-0b277b4254-d8cd0afd03.zip b/.yarn/cache/awilix-npm-4.3.4-0b277b4254-d8cd0afd03.zip new file mode 100644 index 00000000000..b617f2bc4e4 Binary files /dev/null and b/.yarn/cache/awilix-npm-4.3.4-0b277b4254-d8cd0afd03.zip differ diff --git a/.yarn/cache/aws-sdk-npm-2.1076.0-5e60905fe8-b618ff8168.zip b/.yarn/cache/aws-sdk-npm-2.1076.0-5e60905fe8-b618ff8168.zip new file mode 100644 index 00000000000..ee522c6998b Binary files /dev/null and b/.yarn/cache/aws-sdk-npm-2.1076.0-5e60905fe8-b618ff8168.zip differ diff --git a/.yarn/cache/aws-sign2-npm-0.7.0-656c6cb84d-b148b0bb07.zip b/.yarn/cache/aws-sign2-npm-0.7.0-656c6cb84d-b148b0bb07.zip new file mode 100644 index 00000000000..6d41947851d Binary files /dev/null and b/.yarn/cache/aws-sign2-npm-0.7.0-656c6cb84d-b148b0bb07.zip differ diff --git a/.yarn/cache/aws4-npm-1.11.0-283476ad94-5a00d045fd.zip b/.yarn/cache/aws4-npm-1.11.0-283476ad94-5a00d045fd.zip new file mode 100644 index 00000000000..41cb9dfbb63 Binary files /dev/null and b/.yarn/cache/aws4-npm-1.11.0-283476ad94-5a00d045fd.zip differ diff --git a/.yarn/cache/axios-npm-0.21.4-e278873748-44245f24ac.zip b/.yarn/cache/axios-npm-0.21.4-e278873748-44245f24ac.zip new file mode 100644 index 00000000000..756d87a545f Binary files /dev/null and b/.yarn/cache/axios-npm-0.21.4-e278873748-44245f24ac.zip differ diff --git a/.yarn/cache/babel-eslint-npm-10.1.0-6a6d2b1533-bdc1f62b6b.zip b/.yarn/cache/babel-eslint-npm-10.1.0-6a6d2b1533-bdc1f62b6b.zip new file mode 100644 index 00000000000..b7799f8434b Binary files /dev/null and b/.yarn/cache/babel-eslint-npm-10.1.0-6a6d2b1533-bdc1f62b6b.zip differ diff --git a/.yarn/cache/babel-loader-npm-8.2.3-855681b984-78e1e1a919.zip b/.yarn/cache/babel-loader-npm-8.2.3-855681b984-78e1e1a919.zip new file mode 100644 index 00000000000..3332a4f5b86 Binary files /dev/null and b/.yarn/cache/babel-loader-npm-8.2.3-855681b984-78e1e1a919.zip differ diff --git a/.yarn/cache/babel-plugin-dynamic-import-node-npm-2.3.3-be081936a9-c9d24415bc.zip b/.yarn/cache/babel-plugin-dynamic-import-node-npm-2.3.3-be081936a9-c9d24415bc.zip new file mode 100644 index 00000000000..8b45a45e5b1 Binary files /dev/null and b/.yarn/cache/babel-plugin-dynamic-import-node-npm-2.3.3-be081936a9-c9d24415bc.zip differ diff --git a/.yarn/cache/babel-plugin-polyfill-corejs2-npm-0.3.0-4e58d302d2-ffede59798.zip b/.yarn/cache/babel-plugin-polyfill-corejs2-npm-0.3.0-4e58d302d2-ffede59798.zip new file mode 100644 index 00000000000..2977c38c675 Binary files /dev/null and b/.yarn/cache/babel-plugin-polyfill-corejs2-npm-0.3.0-4e58d302d2-ffede59798.zip differ diff --git a/.yarn/cache/babel-plugin-polyfill-corejs3-npm-0.4.0-0b821f8a09-18dce9a09a.zip b/.yarn/cache/babel-plugin-polyfill-corejs3-npm-0.4.0-0b821f8a09-18dce9a09a.zip new file mode 100644 index 00000000000..e5d14c52efe Binary files /dev/null and b/.yarn/cache/babel-plugin-polyfill-corejs3-npm-0.4.0-0b821f8a09-18dce9a09a.zip differ diff --git a/.yarn/cache/babel-plugin-polyfill-regenerator-npm-0.3.0-3228238f85-ecca4389fd.zip b/.yarn/cache/babel-plugin-polyfill-regenerator-npm-0.3.0-3228238f85-ecca4389fd.zip new file mode 100644 index 00000000000..347524d7f8b Binary files /dev/null and b/.yarn/cache/babel-plugin-polyfill-regenerator-npm-0.3.0-3228238f85-ecca4389fd.zip differ diff --git a/.yarn/cache/balanced-match-npm-1.0.2-a53c126459-9706c088a2.zip b/.yarn/cache/balanced-match-npm-1.0.2-a53c126459-9706c088a2.zip new file mode 100644 index 00000000000..0693b6d7be2 Binary files /dev/null and b/.yarn/cache/balanced-match-npm-1.0.2-a53c126459-9706c088a2.zip differ diff --git a/.yarn/cache/base-x-npm-3.0.9-7b2588e106-957101d6fd.zip b/.yarn/cache/base-x-npm-3.0.9-7b2588e106-957101d6fd.zip new file mode 100644 index 00000000000..79bdd2c7052 Binary files /dev/null and b/.yarn/cache/base-x-npm-3.0.9-7b2588e106-957101d6fd.zip differ diff --git a/.yarn/cache/base64-arraybuffer-npm-1.0.1-e1053d5403-04b6fe6818.zip b/.yarn/cache/base64-arraybuffer-npm-1.0.1-e1053d5403-04b6fe6818.zip new file mode 100644 index 00000000000..10c5264efb5 Binary files /dev/null and b/.yarn/cache/base64-arraybuffer-npm-1.0.1-e1053d5403-04b6fe6818.zip differ diff --git a/.yarn/cache/base64-js-npm-1.5.1-b2f7275641-669632eb37.zip b/.yarn/cache/base64-js-npm-1.5.1-b2f7275641-669632eb37.zip new file mode 100644 index 00000000000..a49ec87ac2c Binary files /dev/null and b/.yarn/cache/base64-js-npm-1.5.1-b2f7275641-669632eb37.zip differ diff --git a/.yarn/cache/base64id-npm-2.0.0-ef4afeee0a-581b1d37e6.zip b/.yarn/cache/base64id-npm-2.0.0-ef4afeee0a-581b1d37e6.zip new file mode 100644 index 00000000000..e0bb981132e Binary files /dev/null and b/.yarn/cache/base64id-npm-2.0.0-ef4afeee0a-581b1d37e6.zip differ diff --git a/.yarn/cache/bcrypt-pbkdf-npm-1.0.2-80db8b16ed-4edfc9fe7d.zip b/.yarn/cache/bcrypt-pbkdf-npm-1.0.2-80db8b16ed-4edfc9fe7d.zip new file mode 100644 index 00000000000..75152520d61 Binary files /dev/null and b/.yarn/cache/bcrypt-pbkdf-npm-1.0.2-80db8b16ed-4edfc9fe7d.zip differ diff --git a/.yarn/cache/before-after-hook-npm-2.2.2-b463f0552f-dc2e1ffe38.zip b/.yarn/cache/before-after-hook-npm-2.2.2-b463f0552f-dc2e1ffe38.zip new file mode 100644 index 00000000000..ca7bd8a7278 Binary files /dev/null and b/.yarn/cache/before-after-hook-npm-2.2.2-b463f0552f-dc2e1ffe38.zip differ diff --git a/.yarn/cache/big.js-npm-5.2.2-e147c30820-b89b6e8419.zip b/.yarn/cache/big.js-npm-5.2.2-e147c30820-b89b6e8419.zip new file mode 100644 index 00000000000..7e587ac0bd4 Binary files /dev/null and b/.yarn/cache/big.js-npm-5.2.2-e147c30820-b89b6e8419.zip differ diff --git a/.yarn/cache/bignumber.js-npm-9.0.1-270d0c8a55-6e72f6069d.zip b/.yarn/cache/bignumber.js-npm-9.0.1-270d0c8a55-6e72f6069d.zip new file mode 100644 index 00000000000..c35ccab77c0 Binary files /dev/null and b/.yarn/cache/bignumber.js-npm-9.0.1-270d0c8a55-6e72f6069d.zip differ diff --git a/.yarn/cache/bin-links-npm-3.0.0-6e5e94c609-61cec54a91.zip b/.yarn/cache/bin-links-npm-3.0.0-6e5e94c609-61cec54a91.zip new file mode 100644 index 00000000000..7f9815c11fe Binary files /dev/null and b/.yarn/cache/bin-links-npm-3.0.0-6e5e94c609-61cec54a91.zip differ diff --git a/.yarn/cache/binary-extensions-npm-2.2.0-180c33fec7-ccd267956c.zip b/.yarn/cache/binary-extensions-npm-2.2.0-180c33fec7-ccd267956c.zip new file mode 100644 index 00000000000..2ac750c15e1 Binary files /dev/null and b/.yarn/cache/binary-extensions-npm-2.2.0-180c33fec7-ccd267956c.zip differ diff --git a/.yarn/cache/binaryextensions-npm-4.18.0-af6f83841f-6fe92a9004.zip b/.yarn/cache/binaryextensions-npm-4.18.0-af6f83841f-6fe92a9004.zip new file mode 100644 index 00000000000..063b6c28ac3 Binary files /dev/null and b/.yarn/cache/binaryextensions-npm-4.18.0-af6f83841f-6fe92a9004.zip differ diff --git a/.yarn/cache/bl-npm-1.2.3-49c4213ca5-123f097989.zip b/.yarn/cache/bl-npm-1.2.3-49c4213ca5-123f097989.zip new file mode 100644 index 00000000000..b7c6cb8640e Binary files /dev/null and b/.yarn/cache/bl-npm-1.2.3-49c4213ca5-123f097989.zip differ diff --git a/.yarn/cache/bl-npm-2.2.1-f294e1ea12-4f5d9b2589.zip b/.yarn/cache/bl-npm-2.2.1-f294e1ea12-4f5d9b2589.zip new file mode 100644 index 00000000000..388f2149471 Binary files /dev/null and b/.yarn/cache/bl-npm-2.2.1-f294e1ea12-4f5d9b2589.zip differ diff --git a/.yarn/cache/bl-npm-4.1.0-7f94cdcf3f-9e8521fa7e.zip b/.yarn/cache/bl-npm-4.1.0-7f94cdcf3f-9e8521fa7e.zip new file mode 100644 index 00000000000..0b0454bb891 Binary files /dev/null and b/.yarn/cache/bl-npm-4.1.0-7f94cdcf3f-9e8521fa7e.zip differ diff --git a/.yarn/cache/blake3-npm-2.1.7-7bf40c44b4-5960e1cb36.zip b/.yarn/cache/blake3-npm-2.1.7-7bf40c44b4-5960e1cb36.zip new file mode 100644 index 00000000000..03433e07fd6 Binary files /dev/null and b/.yarn/cache/blake3-npm-2.1.7-7bf40c44b4-5960e1cb36.zip differ diff --git a/.yarn/cache/bloom-filter-npm-0.2.0-36415efc43-0a19b85cbd.zip b/.yarn/cache/bloom-filter-npm-0.2.0-36415efc43-0a19b85cbd.zip new file mode 100644 index 00000000000..79ae19f2899 Binary files /dev/null and b/.yarn/cache/bloom-filter-npm-0.2.0-36415efc43-0a19b85cbd.zip differ diff --git a/.yarn/cache/bls-signatures-npm-0.2.5-2b4387e166-472d697f09.zip b/.yarn/cache/bls-signatures-npm-0.2.5-2b4387e166-472d697f09.zip new file mode 100644 index 00000000000..2315e7e3d77 Binary files /dev/null and b/.yarn/cache/bls-signatures-npm-0.2.5-2b4387e166-472d697f09.zip differ diff --git a/.yarn/cache/bluebird-npm-3.7.2-6a54136ee3-869417503c.zip b/.yarn/cache/bluebird-npm-3.7.2-6a54136ee3-869417503c.zip new file mode 100644 index 00000000000..f49f62c71b2 Binary files /dev/null and b/.yarn/cache/bluebird-npm-3.7.2-6a54136ee3-869417503c.zip differ diff --git a/.yarn/cache/bn.js-npm-4.12.0-3ec6c884f6-39afb4f15f.zip b/.yarn/cache/bn.js-npm-4.12.0-3ec6c884f6-39afb4f15f.zip new file mode 100644 index 00000000000..b8e780696bf Binary files /dev/null and b/.yarn/cache/bn.js-npm-4.12.0-3ec6c884f6-39afb4f15f.zip differ diff --git a/.yarn/cache/body-parser-npm-1.19.0-6e177cabfa-490231b4c8.zip b/.yarn/cache/body-parser-npm-1.19.0-6e177cabfa-490231b4c8.zip new file mode 100644 index 00000000000..be82c3b590b Binary files /dev/null and b/.yarn/cache/body-parser-npm-1.19.0-6e177cabfa-490231b4c8.zip differ diff --git a/.yarn/cache/boxen-npm-5.1.2-364ee34f2f-82d03e42a7.zip b/.yarn/cache/boxen-npm-5.1.2-364ee34f2f-82d03e42a7.zip new file mode 100644 index 00000000000..2bfc3764318 Binary files /dev/null and b/.yarn/cache/boxen-npm-5.1.2-364ee34f2f-82d03e42a7.zip differ diff --git a/.yarn/cache/brace-expansion-npm-1.1.11-fb95eb05ad-faf34a7bb0.zip b/.yarn/cache/brace-expansion-npm-1.1.11-fb95eb05ad-faf34a7bb0.zip new file mode 100644 index 00000000000..9deab64addc Binary files /dev/null and b/.yarn/cache/brace-expansion-npm-1.1.11-fb95eb05ad-faf34a7bb0.zip differ diff --git a/.yarn/cache/brace-expansion-npm-2.0.1-17aa2616f9-a61e7cd2e8.zip b/.yarn/cache/brace-expansion-npm-2.0.1-17aa2616f9-a61e7cd2e8.zip new file mode 100644 index 00000000000..11d5bd0dbce Binary files /dev/null and b/.yarn/cache/brace-expansion-npm-2.0.1-17aa2616f9-a61e7cd2e8.zip differ diff --git a/.yarn/cache/braces-npm-3.0.2-782240b28a-e2a8e769a8.zip b/.yarn/cache/braces-npm-3.0.2-782240b28a-e2a8e769a8.zip new file mode 100644 index 00000000000..92998e3cc76 Binary files /dev/null and b/.yarn/cache/braces-npm-3.0.2-782240b28a-e2a8e769a8.zip differ diff --git a/.yarn/cache/brorand-npm-1.1.0-ea86634c4b-8a05c9f3c4.zip b/.yarn/cache/brorand-npm-1.1.0-ea86634c4b-8a05c9f3c4.zip new file mode 100644 index 00000000000..d238411688c Binary files /dev/null and b/.yarn/cache/brorand-npm-1.1.0-ea86634c4b-8a05c9f3c4.zip differ diff --git a/.yarn/cache/browser-pack-npm-6.1.0-67557e011b-9e5993d3ee.zip b/.yarn/cache/browser-pack-npm-6.1.0-67557e011b-9e5993d3ee.zip new file mode 100644 index 00000000000..06f74645094 Binary files /dev/null and b/.yarn/cache/browser-pack-npm-6.1.0-67557e011b-9e5993d3ee.zip differ diff --git a/.yarn/cache/browser-resolve-npm-2.0.0-b837a8fc14-69225e73b5.zip b/.yarn/cache/browser-resolve-npm-2.0.0-b837a8fc14-69225e73b5.zip new file mode 100644 index 00000000000..212bebc051d Binary files /dev/null and b/.yarn/cache/browser-resolve-npm-2.0.0-b837a8fc14-69225e73b5.zip differ diff --git a/.yarn/cache/browser-stdout-npm-1.3.1-6b2376bf3f-b717b19b25.zip b/.yarn/cache/browser-stdout-npm-1.3.1-6b2376bf3f-b717b19b25.zip new file mode 100644 index 00000000000..bf43caa5823 Binary files /dev/null and b/.yarn/cache/browser-stdout-npm-1.3.1-6b2376bf3f-b717b19b25.zip differ diff --git a/.yarn/cache/browserify-aes-npm-1.2.0-2ad4aeefbe-4a17c3eb55.zip b/.yarn/cache/browserify-aes-npm-1.2.0-2ad4aeefbe-4a17c3eb55.zip new file mode 100644 index 00000000000..66bfb898db2 Binary files /dev/null and b/.yarn/cache/browserify-aes-npm-1.2.0-2ad4aeefbe-4a17c3eb55.zip differ diff --git a/.yarn/cache/browserify-cipher-npm-1.0.1-e00d75c093-2d8500acf1.zip b/.yarn/cache/browserify-cipher-npm-1.0.1-e00d75c093-2d8500acf1.zip new file mode 100644 index 00000000000..26bf43d83bc Binary files /dev/null and b/.yarn/cache/browserify-cipher-npm-1.0.1-e00d75c093-2d8500acf1.zip differ diff --git a/.yarn/cache/browserify-des-npm-1.0.2-5d04e0cde2-b15a3e358a.zip b/.yarn/cache/browserify-des-npm-1.0.2-5d04e0cde2-b15a3e358a.zip new file mode 100644 index 00000000000..bed53ad0d8f Binary files /dev/null and b/.yarn/cache/browserify-des-npm-1.0.2-5d04e0cde2-b15a3e358a.zip differ diff --git a/.yarn/cache/browserify-npm-16.5.2-cfbe4f6efb-75dacf5c82.zip b/.yarn/cache/browserify-npm-16.5.2-cfbe4f6efb-75dacf5c82.zip new file mode 100644 index 00000000000..05f938e031b Binary files /dev/null and b/.yarn/cache/browserify-npm-16.5.2-cfbe4f6efb-75dacf5c82.zip differ diff --git a/.yarn/cache/browserify-rsa-npm-4.1.0-2a224a51bc-155f0c1358.zip b/.yarn/cache/browserify-rsa-npm-4.1.0-2a224a51bc-155f0c1358.zip new file mode 100644 index 00000000000..9fb3d71d614 Binary files /dev/null and b/.yarn/cache/browserify-rsa-npm-4.1.0-2a224a51bc-155f0c1358.zip differ diff --git a/.yarn/cache/browserify-sign-npm-4.2.1-9a8530ca87-0221f190e3.zip b/.yarn/cache/browserify-sign-npm-4.2.1-9a8530ca87-0221f190e3.zip new file mode 100644 index 00000000000..9d443277ef5 Binary files /dev/null and b/.yarn/cache/browserify-sign-npm-4.2.1-9a8530ca87-0221f190e3.zip differ diff --git a/.yarn/cache/browserify-zlib-npm-0.2.0-eab4087284-5cd9d6a665.zip b/.yarn/cache/browserify-zlib-npm-0.2.0-eab4087284-5cd9d6a665.zip new file mode 100644 index 00000000000..74928a1f2f8 Binary files /dev/null and b/.yarn/cache/browserify-zlib-npm-0.2.0-eab4087284-5cd9d6a665.zip differ diff --git a/.yarn/cache/browserslist-npm-4.18.1-38eb8a64b9-ae58322dee.zip b/.yarn/cache/browserslist-npm-4.18.1-38eb8a64b9-ae58322dee.zip new file mode 100644 index 00000000000..b6a20f78c84 Binary files /dev/null and b/.yarn/cache/browserslist-npm-4.18.1-38eb8a64b9-ae58322dee.zip differ diff --git a/.yarn/cache/bs58-npm-4.0.1-8d2a7822b1-b3c5365bb9.zip b/.yarn/cache/bs58-npm-4.0.1-8d2a7822b1-b3c5365bb9.zip new file mode 100644 index 00000000000..c297f8c41f9 Binary files /dev/null and b/.yarn/cache/bs58-npm-4.0.1-8d2a7822b1-b3c5365bb9.zip differ diff --git a/.yarn/cache/bson-npm-1.1.6-071be5c52e-75762c9b7e.zip b/.yarn/cache/bson-npm-1.1.6-071be5c52e-75762c9b7e.zip new file mode 100644 index 00000000000..5f5d61a73f8 Binary files /dev/null and b/.yarn/cache/bson-npm-1.1.6-071be5c52e-75762c9b7e.zip differ diff --git a/.yarn/cache/buffer-from-npm-1.1.2-03d2f20d7e-0448524a56.zip b/.yarn/cache/buffer-from-npm-1.1.2-03d2f20d7e-0448524a56.zip new file mode 100644 index 00000000000..efe1b763807 Binary files /dev/null and b/.yarn/cache/buffer-from-npm-1.1.2-03d2f20d7e-0448524a56.zip differ diff --git a/.yarn/cache/buffer-npm-4.9.2-9e40b5e87a-8801bc1ba0.zip b/.yarn/cache/buffer-npm-4.9.2-9e40b5e87a-8801bc1ba0.zip new file mode 100644 index 00000000000..d2ab6cbe398 Binary files /dev/null and b/.yarn/cache/buffer-npm-4.9.2-9e40b5e87a-8801bc1ba0.zip differ diff --git a/.yarn/cache/buffer-npm-5.2.1-9f7652b857-aa3f25bb88.zip b/.yarn/cache/buffer-npm-5.2.1-9f7652b857-aa3f25bb88.zip new file mode 100644 index 00000000000..1cf78a0b4ae Binary files /dev/null and b/.yarn/cache/buffer-npm-5.2.1-9f7652b857-aa3f25bb88.zip differ diff --git a/.yarn/cache/buffer-npm-5.7.1-513ef8259e-e2cf8429e1.zip b/.yarn/cache/buffer-npm-5.7.1-513ef8259e-e2cf8429e1.zip new file mode 100644 index 00000000000..15c7810bc9a Binary files /dev/null and b/.yarn/cache/buffer-npm-5.7.1-513ef8259e-e2cf8429e1.zip differ diff --git a/.yarn/cache/buffer-npm-6.0.3-cd90dfedfe-5ad23293d9.zip b/.yarn/cache/buffer-npm-6.0.3-cd90dfedfe-5ad23293d9.zip new file mode 100644 index 00000000000..dbf2748bbb8 Binary files /dev/null and b/.yarn/cache/buffer-npm-6.0.3-cd90dfedfe-5ad23293d9.zip differ diff --git a/.yarn/cache/buffer-reverse-npm-1.0.1-2224e35393-e350872a89.zip b/.yarn/cache/buffer-reverse-npm-1.0.1-2224e35393-e350872a89.zip new file mode 100644 index 00000000000..6942b0d28e2 Binary files /dev/null and b/.yarn/cache/buffer-reverse-npm-1.0.1-2224e35393-e350872a89.zip differ diff --git a/.yarn/cache/buffer-xor-npm-1.0.3-56bb81b0dd-10c520df29.zip b/.yarn/cache/buffer-xor-npm-1.0.3-56bb81b0dd-10c520df29.zip new file mode 100644 index 00000000000..7a036a16369 Binary files /dev/null and b/.yarn/cache/buffer-xor-npm-1.0.3-56bb81b0dd-10c520df29.zip differ diff --git a/.yarn/cache/bufferutil-npm-4.0.6-b93c8a5e05-dd10756094.zip b/.yarn/cache/bufferutil-npm-4.0.6-b93c8a5e05-dd10756094.zip new file mode 100644 index 00000000000..5fbe3a9e82e Binary files /dev/null and b/.yarn/cache/bufferutil-npm-4.0.6-b93c8a5e05-dd10756094.zip differ diff --git a/.yarn/cache/builtin-status-codes-npm-3.0.0-e376b0580b-1119429cf4.zip b/.yarn/cache/builtin-status-codes-npm-3.0.0-e376b0580b-1119429cf4.zip new file mode 100644 index 00000000000..13e1b559951 Binary files /dev/null and b/.yarn/cache/builtin-status-codes-npm-3.0.0-e376b0580b-1119429cf4.zip differ diff --git a/.yarn/cache/builtins-npm-1.0.3-f09d2d57f2-47ce94f7ee.zip b/.yarn/cache/builtins-npm-1.0.3-f09d2d57f2-47ce94f7ee.zip new file mode 100644 index 00000000000..6d3afeb525e Binary files /dev/null and b/.yarn/cache/builtins-npm-1.0.3-f09d2d57f2-47ce94f7ee.zip differ diff --git a/.yarn/cache/bytes-npm-3.1.0-19c5b15405-7c3b21c5d9.zip b/.yarn/cache/bytes-npm-3.1.0-19c5b15405-7c3b21c5d9.zip new file mode 100644 index 00000000000..a459fadbe56 Binary files /dev/null and b/.yarn/cache/bytes-npm-3.1.0-19c5b15405-7c3b21c5d9.zip differ diff --git a/.yarn/cache/cacache-npm-15.3.0-a7e5239c6a-a07327c27a.zip b/.yarn/cache/cacache-npm-15.3.0-a7e5239c6a-a07327c27a.zip new file mode 100644 index 00000000000..15dac2d6d49 Binary files /dev/null and b/.yarn/cache/cacache-npm-15.3.0-a7e5239c6a-a07327c27a.zip differ diff --git a/.yarn/cache/cacheable-request-npm-6.1.0-684b834873-b510b237b1.zip b/.yarn/cache/cacheable-request-npm-6.1.0-684b834873-b510b237b1.zip new file mode 100644 index 00000000000..9e62d128189 Binary files /dev/null and b/.yarn/cache/cacheable-request-npm-6.1.0-684b834873-b510b237b1.zip differ diff --git a/.yarn/cache/cached-path-relative-npm-1.0.2-375da1d4a2-643fa65a65.zip b/.yarn/cache/cached-path-relative-npm-1.0.2-375da1d4a2-643fa65a65.zip new file mode 100644 index 00000000000..e60b448c838 Binary files /dev/null and b/.yarn/cache/cached-path-relative-npm-1.0.2-375da1d4a2-643fa65a65.zip differ diff --git a/.yarn/cache/caching-transform-npm-4.0.0-d619d562ea-c4db693953.zip b/.yarn/cache/caching-transform-npm-4.0.0-d619d562ea-c4db693953.zip new file mode 100644 index 00000000000..2f6cf1028ba Binary files /dev/null and b/.yarn/cache/caching-transform-npm-4.0.0-d619d562ea-c4db693953.zip differ diff --git a/.yarn/cache/call-bind-npm-1.0.2-c957124861-f8e31de9d1.zip b/.yarn/cache/call-bind-npm-1.0.2-c957124861-f8e31de9d1.zip new file mode 100644 index 00000000000..bff7528d404 Binary files /dev/null and b/.yarn/cache/call-bind-npm-1.0.2-c957124861-f8e31de9d1.zip differ diff --git a/.yarn/cache/call-me-maybe-npm-1.0.1-d07e74bc9c-d19e9d6ac2.zip b/.yarn/cache/call-me-maybe-npm-1.0.1-d07e74bc9c-d19e9d6ac2.zip new file mode 100644 index 00000000000..c7828354649 Binary files /dev/null and b/.yarn/cache/call-me-maybe-npm-1.0.1-d07e74bc9c-d19e9d6ac2.zip differ diff --git a/.yarn/cache/callsites-npm-3.1.0-268f989910-072d17b6ab.zip b/.yarn/cache/callsites-npm-3.1.0-268f989910-072d17b6ab.zip new file mode 100644 index 00000000000..be6414c5472 Binary files /dev/null and b/.yarn/cache/callsites-npm-3.1.0-268f989910-072d17b6ab.zip differ diff --git a/.yarn/cache/camel-case-npm-4.1.2-082bf67a9a-bcbd25cd25.zip b/.yarn/cache/camel-case-npm-4.1.2-082bf67a9a-bcbd25cd25.zip new file mode 100644 index 00000000000..6bb1dd0b012 Binary files /dev/null and b/.yarn/cache/camel-case-npm-4.1.2-082bf67a9a-bcbd25cd25.zip differ diff --git a/.yarn/cache/camelcase-keys-npm-6.2.2-d13777ec12-43c9af1adf.zip b/.yarn/cache/camelcase-keys-npm-6.2.2-d13777ec12-43c9af1adf.zip new file mode 100644 index 00000000000..efdc4e474d9 Binary files /dev/null and b/.yarn/cache/camelcase-keys-npm-6.2.2-d13777ec12-43c9af1adf.zip differ diff --git a/.yarn/cache/camelcase-npm-5.0.0-c808398846-8bfe920e04.zip b/.yarn/cache/camelcase-npm-5.0.0-c808398846-8bfe920e04.zip new file mode 100644 index 00000000000..cdc64a3c0b3 Binary files /dev/null and b/.yarn/cache/camelcase-npm-5.0.0-c808398846-8bfe920e04.zip differ diff --git a/.yarn/cache/camelcase-npm-5.3.1-5db8af62c5-e6effce26b.zip b/.yarn/cache/camelcase-npm-5.3.1-5db8af62c5-e6effce26b.zip new file mode 100644 index 00000000000..9cc2f6ddf19 Binary files /dev/null and b/.yarn/cache/camelcase-npm-5.3.1-5db8af62c5-e6effce26b.zip differ diff --git a/.yarn/cache/camelcase-npm-6.2.1-5a9a60f6d3-d876272ef7.zip b/.yarn/cache/camelcase-npm-6.2.1-5a9a60f6d3-d876272ef7.zip new file mode 100644 index 00000000000..b180c012278 Binary files /dev/null and b/.yarn/cache/camelcase-npm-6.2.1-5a9a60f6d3-d876272ef7.zip differ diff --git a/.yarn/cache/caniuse-lite-npm-1.0.30001282-49173a42dd-62797fd756.zip b/.yarn/cache/caniuse-lite-npm-1.0.30001282-49173a42dd-62797fd756.zip new file mode 100644 index 00000000000..0e299b488ca Binary files /dev/null and b/.yarn/cache/caniuse-lite-npm-1.0.30001282-49173a42dd-62797fd756.zip differ diff --git a/.yarn/cache/cardinal-npm-2.1.1-b77e7b28a7-e8d4ae4643.zip b/.yarn/cache/cardinal-npm-2.1.1-b77e7b28a7-e8d4ae4643.zip new file mode 100644 index 00000000000..7809eee0e1f Binary files /dev/null and b/.yarn/cache/cardinal-npm-2.1.1-b77e7b28a7-e8d4ae4643.zip differ diff --git a/.yarn/cache/cargo-cp-artifact-npm-0.1.6-fe2dd40a8f-2f5d2f3e73.zip b/.yarn/cache/cargo-cp-artifact-npm-0.1.6-fe2dd40a8f-2f5d2f3e73.zip new file mode 100644 index 00000000000..2da3bc5d400 Binary files /dev/null and b/.yarn/cache/cargo-cp-artifact-npm-0.1.6-fe2dd40a8f-2f5d2f3e73.zip differ diff --git a/.yarn/cache/caseless-npm-0.12.0-e83bc5df83-b43bd4c440.zip b/.yarn/cache/caseless-npm-0.12.0-e83bc5df83-b43bd4c440.zip new file mode 100644 index 00000000000..a12be75cdb5 Binary files /dev/null and b/.yarn/cache/caseless-npm-0.12.0-e83bc5df83-b43bd4c440.zip differ diff --git a/.yarn/cache/cbor-npm-8.1.0-c1a4d6266a-a90338435d.zip b/.yarn/cache/cbor-npm-8.1.0-c1a4d6266a-a90338435d.zip new file mode 100644 index 00000000000..8184280b3ad Binary files /dev/null and b/.yarn/cache/cbor-npm-8.1.0-c1a4d6266a-a90338435d.zip differ diff --git a/.yarn/cache/chai-as-promised-npm-7.1.1-cdc17e4612-7262868a5b.zip b/.yarn/cache/chai-as-promised-npm-7.1.1-cdc17e4612-7262868a5b.zip new file mode 100644 index 00000000000..947fe7c3cd6 Binary files /dev/null and b/.yarn/cache/chai-as-promised-npm-7.1.1-cdc17e4612-7262868a5b.zip differ diff --git a/.yarn/cache/chai-exclude-npm-2.1.0-47ff9dee55-29d964d9f6.zip b/.yarn/cache/chai-exclude-npm-2.1.0-47ff9dee55-29d964d9f6.zip new file mode 100644 index 00000000000..9266d4825d0 Binary files /dev/null and b/.yarn/cache/chai-exclude-npm-2.1.0-47ff9dee55-29d964d9f6.zip differ diff --git a/.yarn/cache/chai-npm-4.3.4-808f3b5355-772c522b3b.zip b/.yarn/cache/chai-npm-4.3.4-808f3b5355-772c522b3b.zip new file mode 100644 index 00000000000..54fd041d63d Binary files /dev/null and b/.yarn/cache/chai-npm-4.3.4-808f3b5355-772c522b3b.zip differ diff --git a/.yarn/cache/chai-string-npm-1.5.0-b46dce1494-d443bb416f.zip b/.yarn/cache/chai-string-npm-1.5.0-b46dce1494-d443bb416f.zip new file mode 100644 index 00000000000..cf1e9bb7fd5 Binary files /dev/null and b/.yarn/cache/chai-string-npm-1.5.0-b46dce1494-d443bb416f.zip differ diff --git a/.yarn/cache/chalk-npm-1.1.3-59144c3a87-9d2ea6b98f.zip b/.yarn/cache/chalk-npm-1.1.3-59144c3a87-9d2ea6b98f.zip new file mode 100644 index 00000000000..e7d3003b97d Binary files /dev/null and b/.yarn/cache/chalk-npm-1.1.3-59144c3a87-9d2ea6b98f.zip differ diff --git a/.yarn/cache/chalk-npm-2.4.2-3ea16dd91e-ec3661d38f.zip b/.yarn/cache/chalk-npm-2.4.2-3ea16dd91e-ec3661d38f.zip new file mode 100644 index 00000000000..3f58a7b2333 Binary files /dev/null and b/.yarn/cache/chalk-npm-2.4.2-3ea16dd91e-ec3661d38f.zip differ diff --git a/.yarn/cache/chalk-npm-3.0.0-e813208025-8e3ddf3981.zip b/.yarn/cache/chalk-npm-3.0.0-e813208025-8e3ddf3981.zip new file mode 100644 index 00000000000..47b36c70187 Binary files /dev/null and b/.yarn/cache/chalk-npm-3.0.0-e813208025-8e3ddf3981.zip differ diff --git a/.yarn/cache/chalk-npm-4.1.2-ba8b67ab80-fe75c9d5c7.zip b/.yarn/cache/chalk-npm-4.1.2-ba8b67ab80-fe75c9d5c7.zip new file mode 100644 index 00000000000..03d46b8646f Binary files /dev/null and b/.yarn/cache/chalk-npm-4.1.2-ba8b67ab80-fe75c9d5c7.zip differ diff --git a/.yarn/cache/chance-npm-1.1.8-47e2e1db1e-e733f51e10.zip b/.yarn/cache/chance-npm-1.1.8-47e2e1db1e-e733f51e10.zip new file mode 100644 index 00000000000..ace146fd9de Binary files /dev/null and b/.yarn/cache/chance-npm-1.1.8-47e2e1db1e-e733f51e10.zip differ diff --git a/.yarn/cache/chardet-npm-0.7.0-27933dd6c7-6fd5da1f5d.zip b/.yarn/cache/chardet-npm-0.7.0-27933dd6c7-6fd5da1f5d.zip new file mode 100644 index 00000000000..0316560c62e Binary files /dev/null and b/.yarn/cache/chardet-npm-0.7.0-27933dd6c7-6fd5da1f5d.zip differ diff --git a/.yarn/cache/check-error-npm-1.0.2-00c540c6e9-d9d1065044.zip b/.yarn/cache/check-error-npm-1.0.2-00c540c6e9-d9d1065044.zip new file mode 100644 index 00000000000..23753533b3f Binary files /dev/null and b/.yarn/cache/check-error-npm-1.0.2-00c540c6e9-d9d1065044.zip differ diff --git a/.yarn/cache/chokidar-npm-3.5.2-6752340fec-d1fda32fcd.zip b/.yarn/cache/chokidar-npm-3.5.2-6752340fec-d1fda32fcd.zip new file mode 100644 index 00000000000..594bbeb37d8 Binary files /dev/null and b/.yarn/cache/chokidar-npm-3.5.2-6752340fec-d1fda32fcd.zip differ diff --git a/.yarn/cache/chownr-npm-1.1.4-5bd400ab08-115648f8eb.zip b/.yarn/cache/chownr-npm-1.1.4-5bd400ab08-115648f8eb.zip new file mode 100644 index 00000000000..b4f504340ce Binary files /dev/null and b/.yarn/cache/chownr-npm-1.1.4-5bd400ab08-115648f8eb.zip differ diff --git a/.yarn/cache/chownr-npm-2.0.0-638f1c9c61-c57cf9dd07.zip b/.yarn/cache/chownr-npm-2.0.0-638f1c9c61-c57cf9dd07.zip new file mode 100644 index 00000000000..e074b2f4c78 Binary files /dev/null and b/.yarn/cache/chownr-npm-2.0.0-638f1c9c61-c57cf9dd07.zip differ diff --git a/.yarn/cache/chrome-trace-event-npm-1.0.3-e0ae3dcd60-cb8b1fc7e8.zip b/.yarn/cache/chrome-trace-event-npm-1.0.3-e0ae3dcd60-cb8b1fc7e8.zip new file mode 100644 index 00000000000..b1b2134d5f0 Binary files /dev/null and b/.yarn/cache/chrome-trace-event-npm-1.0.3-e0ae3dcd60-cb8b1fc7e8.zip differ diff --git a/.yarn/cache/ci-info-npm-2.0.0-78012236a1-3b374666a8.zip b/.yarn/cache/ci-info-npm-2.0.0-78012236a1-3b374666a8.zip new file mode 100644 index 00000000000..be3be89f49b Binary files /dev/null and b/.yarn/cache/ci-info-npm-2.0.0-78012236a1-3b374666a8.zip differ diff --git a/.yarn/cache/cipher-base-npm-1.0.4-2e98b97140-47d3568dbc.zip b/.yarn/cache/cipher-base-npm-1.0.4-2e98b97140-47d3568dbc.zip new file mode 100644 index 00000000000..02eeb2cc02e Binary files /dev/null and b/.yarn/cache/cipher-base-npm-1.0.4-2e98b97140-47d3568dbc.zip differ diff --git a/.yarn/cache/clean-stack-npm-2.2.0-a8ce435a5c-2ac8cd2b2f.zip b/.yarn/cache/clean-stack-npm-2.2.0-a8ce435a5c-2ac8cd2b2f.zip new file mode 100644 index 00000000000..c510995715e Binary files /dev/null and b/.yarn/cache/clean-stack-npm-2.2.0-a8ce435a5c-2ac8cd2b2f.zip differ diff --git a/.yarn/cache/clean-stack-npm-3.0.1-85c3878b76-dc18c842d7.zip b/.yarn/cache/clean-stack-npm-3.0.1-85c3878b76-dc18c842d7.zip new file mode 100644 index 00000000000..c01117fb632 Binary files /dev/null and b/.yarn/cache/clean-stack-npm-3.0.1-85c3878b76-dc18c842d7.zip differ diff --git a/.yarn/cache/cli-boxes-npm-1.0.0-fdd89bc01b-101cfd6464.zip b/.yarn/cache/cli-boxes-npm-1.0.0-fdd89bc01b-101cfd6464.zip new file mode 100644 index 00000000000..6eee65e2edb Binary files /dev/null and b/.yarn/cache/cli-boxes-npm-1.0.0-fdd89bc01b-101cfd6464.zip differ diff --git a/.yarn/cache/cli-boxes-npm-2.2.1-7125a5ba44-be79f8ec23.zip b/.yarn/cache/cli-boxes-npm-2.2.1-7125a5ba44-be79f8ec23.zip new file mode 100644 index 00000000000..9f0f731386a Binary files /dev/null and b/.yarn/cache/cli-boxes-npm-2.2.1-7125a5ba44-be79f8ec23.zip differ diff --git a/.yarn/cache/cli-cursor-npm-3.1.0-fee1e46b5e-2692784c6c.zip b/.yarn/cache/cli-cursor-npm-3.1.0-fee1e46b5e-2692784c6c.zip new file mode 100644 index 00000000000..2a8723c64ee Binary files /dev/null and b/.yarn/cache/cli-cursor-npm-3.1.0-fee1e46b5e-2692784c6c.zip differ diff --git a/.yarn/cache/cli-progress-npm-3.10.0-a1609d715c-8e22c6265f.zip b/.yarn/cache/cli-progress-npm-3.10.0-a1609d715c-8e22c6265f.zip new file mode 100644 index 00000000000..a84dd36b6f2 Binary files /dev/null and b/.yarn/cache/cli-progress-npm-3.10.0-a1609d715c-8e22c6265f.zip differ diff --git a/.yarn/cache/cli-spinners-npm-2.6.1-33ce2bad0f-423409baaa.zip b/.yarn/cache/cli-spinners-npm-2.6.1-33ce2bad0f-423409baaa.zip new file mode 100644 index 00000000000..485c09ed3df Binary files /dev/null and b/.yarn/cache/cli-spinners-npm-2.6.1-33ce2bad0f-423409baaa.zip differ diff --git a/.yarn/cache/cli-table-npm-0.3.11-f912789cff-59fb61f992.zip b/.yarn/cache/cli-table-npm-0.3.11-f912789cff-59fb61f992.zip new file mode 100644 index 00000000000..6c17b986c25 Binary files /dev/null and b/.yarn/cache/cli-table-npm-0.3.11-f912789cff-59fb61f992.zip differ diff --git a/.yarn/cache/cli-truncate-npm-2.1.0-72184d3467-bf1e4e6195.zip b/.yarn/cache/cli-truncate-npm-2.1.0-72184d3467-bf1e4e6195.zip new file mode 100644 index 00000000000..f8c20f36514 Binary files /dev/null and b/.yarn/cache/cli-truncate-npm-2.1.0-72184d3467-bf1e4e6195.zip differ diff --git a/.yarn/cache/cli-width-npm-3.0.0-387b3f68f9-4c94af3769.zip b/.yarn/cache/cli-width-npm-3.0.0-387b3f68f9-4c94af3769.zip new file mode 100644 index 00000000000..b652c4f73c5 Binary files /dev/null and b/.yarn/cache/cli-width-npm-3.0.0-387b3f68f9-4c94af3769.zip differ diff --git a/.yarn/cache/cliui-npm-6.0.0-488b2414c6-4fcfd26d29.zip b/.yarn/cache/cliui-npm-6.0.0-488b2414c6-4fcfd26d29.zip new file mode 100644 index 00000000000..d3c2fa41dc5 Binary files /dev/null and b/.yarn/cache/cliui-npm-6.0.0-488b2414c6-4fcfd26d29.zip differ diff --git a/.yarn/cache/cliui-npm-7.0.4-d6b8a9edb6-ce2e8f578a.zip b/.yarn/cache/cliui-npm-7.0.4-d6b8a9edb6-ce2e8f578a.zip new file mode 100644 index 00000000000..24f58564e46 Binary files /dev/null and b/.yarn/cache/cliui-npm-7.0.4-d6b8a9edb6-ce2e8f578a.zip differ diff --git a/.yarn/cache/clone-buffer-npm-1.0.0-7a16490ce4-a39a35e7fd.zip b/.yarn/cache/clone-buffer-npm-1.0.0-7a16490ce4-a39a35e7fd.zip new file mode 100644 index 00000000000..4dab88f6f8f Binary files /dev/null and b/.yarn/cache/clone-buffer-npm-1.0.0-7a16490ce4-a39a35e7fd.zip differ diff --git a/.yarn/cache/clone-deep-npm-4.0.1-70adab92c8-770f912fe4.zip b/.yarn/cache/clone-deep-npm-4.0.1-70adab92c8-770f912fe4.zip new file mode 100644 index 00000000000..1017703e129 Binary files /dev/null and b/.yarn/cache/clone-deep-npm-4.0.1-70adab92c8-770f912fe4.zip differ diff --git a/.yarn/cache/clone-npm-1.0.4-a610fcbcf9-d06418b733.zip b/.yarn/cache/clone-npm-1.0.4-a610fcbcf9-d06418b733.zip new file mode 100644 index 00000000000..e06cc8632e2 Binary files /dev/null and b/.yarn/cache/clone-npm-1.0.4-a610fcbcf9-d06418b733.zip differ diff --git a/.yarn/cache/clone-npm-2.1.2-1d491c6629-aaf106e9bc.zip b/.yarn/cache/clone-npm-2.1.2-1d491c6629-aaf106e9bc.zip new file mode 100644 index 00000000000..6ae29b32e57 Binary files /dev/null and b/.yarn/cache/clone-npm-2.1.2-1d491c6629-aaf106e9bc.zip differ diff --git a/.yarn/cache/clone-response-npm-1.0.2-135ae8239d-2d0e61547f.zip b/.yarn/cache/clone-response-npm-1.0.2-135ae8239d-2d0e61547f.zip new file mode 100644 index 00000000000..5b5af5351ed Binary files /dev/null and b/.yarn/cache/clone-response-npm-1.0.2-135ae8239d-2d0e61547f.zip differ diff --git a/.yarn/cache/clone-stats-npm-1.0.0-cca25a0a42-654c0425af.zip b/.yarn/cache/clone-stats-npm-1.0.0-cca25a0a42-654c0425af.zip new file mode 100644 index 00000000000..13cf0ab90e4 Binary files /dev/null and b/.yarn/cache/clone-stats-npm-1.0.0-cca25a0a42-654c0425af.zip differ diff --git a/.yarn/cache/cloneable-readable-npm-1.1.3-a5888ff6e9-23b3741225.zip b/.yarn/cache/cloneable-readable-npm-1.1.3-a5888ff6e9-23b3741225.zip new file mode 100644 index 00000000000..631f7b91a8c Binary files /dev/null and b/.yarn/cache/cloneable-readable-npm-1.1.3-a5888ff6e9-23b3741225.zip differ diff --git a/.yarn/cache/cmd-shim-npm-4.1.0-018e70f153-d25bb57a8a.zip b/.yarn/cache/cmd-shim-npm-4.1.0-018e70f153-d25bb57a8a.zip new file mode 100644 index 00000000000..bed795f3ddf Binary files /dev/null and b/.yarn/cache/cmd-shim-npm-4.1.0-018e70f153-d25bb57a8a.zip differ diff --git a/.yarn/cache/code-point-at-npm-1.1.0-37de5fe566-17d5666611.zip b/.yarn/cache/code-point-at-npm-1.1.0-37de5fe566-17d5666611.zip new file mode 100644 index 00000000000..5e910b2e53d Binary files /dev/null and b/.yarn/cache/code-point-at-npm-1.1.0-37de5fe566-17d5666611.zip differ diff --git a/.yarn/cache/color-convert-npm-1.9.3-1fe690075e-fd7a64a17c.zip b/.yarn/cache/color-convert-npm-1.9.3-1fe690075e-fd7a64a17c.zip new file mode 100644 index 00000000000..1b4c9391ea3 Binary files /dev/null and b/.yarn/cache/color-convert-npm-1.9.3-1fe690075e-fd7a64a17c.zip differ diff --git a/.yarn/cache/color-convert-npm-2.0.1-79730e935b-79e6bdb9fd.zip b/.yarn/cache/color-convert-npm-2.0.1-79730e935b-79e6bdb9fd.zip new file mode 100644 index 00000000000..b3499adbb84 Binary files /dev/null and b/.yarn/cache/color-convert-npm-2.0.1-79730e935b-79e6bdb9fd.zip differ diff --git a/.yarn/cache/color-name-npm-1.1.3-728b7b5d39-09c5d3e33d.zip b/.yarn/cache/color-name-npm-1.1.3-728b7b5d39-09c5d3e33d.zip new file mode 100644 index 00000000000..f158de9e2eb Binary files /dev/null and b/.yarn/cache/color-name-npm-1.1.3-728b7b5d39-09c5d3e33d.zip differ diff --git a/.yarn/cache/color-name-npm-1.1.4-025792b0ea-b044585952.zip b/.yarn/cache/color-name-npm-1.1.4-025792b0ea-b044585952.zip new file mode 100644 index 00000000000..ce1ffc4bf31 Binary files /dev/null and b/.yarn/cache/color-name-npm-1.1.4-025792b0ea-b044585952.zip differ diff --git a/.yarn/cache/color-npm-3.2.1-568cf1014f-f81220e8b7.zip b/.yarn/cache/color-npm-3.2.1-568cf1014f-f81220e8b7.zip new file mode 100644 index 00000000000..6021f3d1136 Binary files /dev/null and b/.yarn/cache/color-npm-3.2.1-568cf1014f-f81220e8b7.zip differ diff --git a/.yarn/cache/color-string-npm-1.6.0-94ed25c258-33466a6527.zip b/.yarn/cache/color-string-npm-1.6.0-94ed25c258-33466a6527.zip new file mode 100644 index 00000000000..3bcbd4db536 Binary files /dev/null and b/.yarn/cache/color-string-npm-1.6.0-94ed25c258-33466a6527.zip differ diff --git a/.yarn/cache/color-support-npm-1.1.3-3be5c53455-9b73568176.zip b/.yarn/cache/color-support-npm-1.1.3-3be5c53455-9b73568176.zip new file mode 100644 index 00000000000..625a79f1779 Binary files /dev/null and b/.yarn/cache/color-support-npm-1.1.3-3be5c53455-9b73568176.zip differ diff --git a/.yarn/cache/colorette-npm-2.0.16-7b996485d7-cd55596a3a.zip b/.yarn/cache/colorette-npm-2.0.16-7b996485d7-cd55596a3a.zip new file mode 100644 index 00000000000..0d086dd3784 Binary files /dev/null and b/.yarn/cache/colorette-npm-2.0.16-7b996485d7-cd55596a3a.zip differ diff --git a/.yarn/cache/colors-npm-1.0.3-6c5d583ab3-234e8d3ab7.zip b/.yarn/cache/colors-npm-1.0.3-6c5d583ab3-234e8d3ab7.zip new file mode 100644 index 00000000000..ff9794571d4 Binary files /dev/null and b/.yarn/cache/colors-npm-1.0.3-6c5d583ab3-234e8d3ab7.zip differ diff --git a/.yarn/cache/colors-npm-1.4.0-7e2cf12234-98aa2c2418.zip b/.yarn/cache/colors-npm-1.4.0-7e2cf12234-98aa2c2418.zip new file mode 100644 index 00000000000..74451b04ab5 Binary files /dev/null and b/.yarn/cache/colors-npm-1.4.0-7e2cf12234-98aa2c2418.zip differ diff --git a/.yarn/cache/colorspace-npm-1.1.4-f01655548a-bb3934ef3c.zip b/.yarn/cache/colorspace-npm-1.1.4-f01655548a-bb3934ef3c.zip new file mode 100644 index 00000000000..61c649a0c59 Binary files /dev/null and b/.yarn/cache/colorspace-npm-1.1.4-f01655548a-bb3934ef3c.zip differ diff --git a/.yarn/cache/combine-source-map-npm-0.8.0-3715049f57-26b3064a4e.zip b/.yarn/cache/combine-source-map-npm-0.8.0-3715049f57-26b3064a4e.zip new file mode 100644 index 00000000000..ef2b915feba Binary files /dev/null and b/.yarn/cache/combine-source-map-npm-0.8.0-3715049f57-26b3064a4e.zip differ diff --git a/.yarn/cache/combined-stream-npm-1.0.8-dc14d4a63a-49fa4aeb49.zip b/.yarn/cache/combined-stream-npm-1.0.8-dc14d4a63a-49fa4aeb49.zip new file mode 100644 index 00000000000..89c8caa0fdc Binary files /dev/null and b/.yarn/cache/combined-stream-npm-1.0.8-dc14d4a63a-49fa4aeb49.zip differ diff --git a/.yarn/cache/commander-npm-2.20.3-d8dcbaa39b-ab8c07884e.zip b/.yarn/cache/commander-npm-2.20.3-d8dcbaa39b-ab8c07884e.zip new file mode 100644 index 00000000000..6a14adf507d Binary files /dev/null and b/.yarn/cache/commander-npm-2.20.3-d8dcbaa39b-ab8c07884e.zip differ diff --git a/.yarn/cache/commander-npm-4.0.1-7d2e712c26-a8df9873c6.zip b/.yarn/cache/commander-npm-4.0.1-7d2e712c26-a8df9873c6.zip new file mode 100644 index 00000000000..b5deacca66c Binary files /dev/null and b/.yarn/cache/commander-npm-4.0.1-7d2e712c26-a8df9873c6.zip differ diff --git a/.yarn/cache/commander-npm-7.1.0-632d393e57-99c120b939.zip b/.yarn/cache/commander-npm-7.1.0-632d393e57-99c120b939.zip new file mode 100644 index 00000000000..b02a0130caa Binary files /dev/null and b/.yarn/cache/commander-npm-7.1.0-632d393e57-99c120b939.zip differ diff --git a/.yarn/cache/commander-npm-7.2.0-19178180f8-53501cbeee.zip b/.yarn/cache/commander-npm-7.2.0-19178180f8-53501cbeee.zip new file mode 100644 index 00000000000..1c86bf71813 Binary files /dev/null and b/.yarn/cache/commander-npm-7.2.0-19178180f8-53501cbeee.zip differ diff --git a/.yarn/cache/comment-parser-npm-0.7.6-927ea8eaf8-880e4d58c0.zip b/.yarn/cache/comment-parser-npm-0.7.6-927ea8eaf8-880e4d58c0.zip new file mode 100644 index 00000000000..ead0b149a86 Binary files /dev/null and b/.yarn/cache/comment-parser-npm-0.7.6-927ea8eaf8-880e4d58c0.zip differ diff --git a/.yarn/cache/common-ancestor-path-npm-1.0.1-27534e68da-1d2e418606.zip b/.yarn/cache/common-ancestor-path-npm-1.0.1-27534e68da-1d2e418606.zip new file mode 100644 index 00000000000..431dda39a35 Binary files /dev/null and b/.yarn/cache/common-ancestor-path-npm-1.0.1-27534e68da-1d2e418606.zip differ diff --git a/.yarn/cache/commondir-npm-1.0.1-291b790340-59715f2fc4.zip b/.yarn/cache/commondir-npm-1.0.1-291b790340-59715f2fc4.zip new file mode 100644 index 00000000000..b2b0817487e Binary files /dev/null and b/.yarn/cache/commondir-npm-1.0.1-291b790340-59715f2fc4.zip differ diff --git a/.yarn/cache/compare-func-npm-2.0.0-9cd7852f23-fb71d70632.zip b/.yarn/cache/compare-func-npm-2.0.0-9cd7852f23-fb71d70632.zip new file mode 100644 index 00000000000..5919970e234 Binary files /dev/null and b/.yarn/cache/compare-func-npm-2.0.0-9cd7852f23-fb71d70632.zip differ diff --git a/.yarn/cache/complex.js-npm-2.1.0-6d9742b352-8a31a0d819.zip b/.yarn/cache/complex.js-npm-2.1.0-6d9742b352-8a31a0d819.zip new file mode 100644 index 00000000000..d68000842e3 Binary files /dev/null and b/.yarn/cache/complex.js-npm-2.1.0-6d9742b352-8a31a0d819.zip differ diff --git a/.yarn/cache/component-emitter-npm-1.3.0-4b848565b9-b3c46de38f.zip b/.yarn/cache/component-emitter-npm-1.3.0-4b848565b9-b3c46de38f.zip new file mode 100644 index 00000000000..7ab5c74c0e8 Binary files /dev/null and b/.yarn/cache/component-emitter-npm-1.3.0-4b848565b9-b3c46de38f.zip differ diff --git a/.yarn/cache/concat-map-npm-0.0.1-85a921b7ee-902a9f5d89.zip b/.yarn/cache/concat-map-npm-0.0.1-85a921b7ee-902a9f5d89.zip new file mode 100644 index 00000000000..66b4c329f86 Binary files /dev/null and b/.yarn/cache/concat-map-npm-0.0.1-85a921b7ee-902a9f5d89.zip differ diff --git a/.yarn/cache/concat-stream-npm-1.6.2-2bee337060-1ef77032cb.zip b/.yarn/cache/concat-stream-npm-1.6.2-2bee337060-1ef77032cb.zip new file mode 100644 index 00000000000..2adcea76123 Binary files /dev/null and b/.yarn/cache/concat-stream-npm-1.6.2-2bee337060-1ef77032cb.zip differ diff --git a/.yarn/cache/concurrently-npm-7.0.0-c402b003bc-1be78f24bf.zip b/.yarn/cache/concurrently-npm-7.0.0-c402b003bc-1be78f24bf.zip new file mode 100644 index 00000000000..eff22b10da9 Binary files /dev/null and b/.yarn/cache/concurrently-npm-7.0.0-c402b003bc-1be78f24bf.zip differ diff --git a/.yarn/cache/configstore-npm-5.0.1-739433cdc5-60ef65d493.zip b/.yarn/cache/configstore-npm-5.0.1-739433cdc5-60ef65d493.zip new file mode 100644 index 00000000000..f14393361af Binary files /dev/null and b/.yarn/cache/configstore-npm-5.0.1-739433cdc5-60ef65d493.zip differ diff --git a/.yarn/cache/confusing-browser-globals-npm-1.0.10-ecb768852b-7ccdc44c2c.zip b/.yarn/cache/confusing-browser-globals-npm-1.0.10-ecb768852b-7ccdc44c2c.zip new file mode 100644 index 00000000000..c453b25b9bf Binary files /dev/null and b/.yarn/cache/confusing-browser-globals-npm-1.0.10-ecb768852b-7ccdc44c2c.zip differ diff --git a/.yarn/cache/connect-npm-3.7.0-25ccb085cc-96e1c4effc.zip b/.yarn/cache/connect-npm-3.7.0-25ccb085cc-96e1c4effc.zip new file mode 100644 index 00000000000..584ac00ab0f Binary files /dev/null and b/.yarn/cache/connect-npm-3.7.0-25ccb085cc-96e1c4effc.zip differ diff --git a/.yarn/cache/console-browserify-npm-1.2.0-5619eeb6ff-226591eeff.zip b/.yarn/cache/console-browserify-npm-1.2.0-5619eeb6ff-226591eeff.zip new file mode 100644 index 00000000000..997476451c7 Binary files /dev/null and b/.yarn/cache/console-browserify-npm-1.2.0-5619eeb6ff-226591eeff.zip differ diff --git a/.yarn/cache/console-control-strings-npm-1.1.0-e3160e5275-8755d76787.zip b/.yarn/cache/console-control-strings-npm-1.1.0-e3160e5275-8755d76787.zip new file mode 100644 index 00000000000..a1f2fe661be Binary files /dev/null and b/.yarn/cache/console-control-strings-npm-1.1.0-e3160e5275-8755d76787.zip differ diff --git a/.yarn/cache/console-table-printer-npm-2.11.0-5c300077b5-125797e3b9.zip b/.yarn/cache/console-table-printer-npm-2.11.0-5c300077b5-125797e3b9.zip new file mode 100644 index 00000000000..a258faaf1dd Binary files /dev/null and b/.yarn/cache/console-table-printer-npm-2.11.0-5c300077b5-125797e3b9.zip differ diff --git a/.yarn/cache/constants-browserify-npm-1.0.0-b9a9bcfe4b-f7ac8c6d0b.zip b/.yarn/cache/constants-browserify-npm-1.0.0-b9a9bcfe4b-f7ac8c6d0b.zip new file mode 100644 index 00000000000..d1618d9c5aa Binary files /dev/null and b/.yarn/cache/constants-browserify-npm-1.0.0-b9a9bcfe4b-f7ac8c6d0b.zip differ diff --git a/.yarn/cache/content-type-npm-1.0.4-3b1a5ca16b-3d93585fda.zip b/.yarn/cache/content-type-npm-1.0.4-3b1a5ca16b-3d93585fda.zip new file mode 100644 index 00000000000..9e1b5d89073 Binary files /dev/null and b/.yarn/cache/content-type-npm-1.0.4-3b1a5ca16b-3d93585fda.zip differ diff --git a/.yarn/cache/conventional-changelog-angular-npm-5.0.13-50e4a302c4-6ed4972fce.zip b/.yarn/cache/conventional-changelog-angular-npm-5.0.13-50e4a302c4-6ed4972fce.zip new file mode 100644 index 00000000000..a00a91e2a06 Binary files /dev/null and b/.yarn/cache/conventional-changelog-angular-npm-5.0.13-50e4a302c4-6ed4972fce.zip differ diff --git a/.yarn/cache/conventional-changelog-atom-npm-2.0.8-ab61571c15-12ecbd928f.zip b/.yarn/cache/conventional-changelog-atom-npm-2.0.8-ab61571c15-12ecbd928f.zip new file mode 100644 index 00000000000..a71b898e38a Binary files /dev/null and b/.yarn/cache/conventional-changelog-atom-npm-2.0.8-ab61571c15-12ecbd928f.zip differ diff --git a/.yarn/cache/conventional-changelog-codemirror-npm-2.0.8-342d72f6a3-cf331db40c.zip b/.yarn/cache/conventional-changelog-codemirror-npm-2.0.8-342d72f6a3-cf331db40c.zip new file mode 100644 index 00000000000..cc1ff892aa4 Binary files /dev/null and b/.yarn/cache/conventional-changelog-codemirror-npm-2.0.8-342d72f6a3-cf331db40c.zip differ diff --git a/.yarn/cache/conventional-changelog-conventionalcommits-npm-4.6.1-030ed159a8-f866616c8f.zip b/.yarn/cache/conventional-changelog-conventionalcommits-npm-4.6.1-030ed159a8-f866616c8f.zip new file mode 100644 index 00000000000..ee40e562cf4 Binary files /dev/null and b/.yarn/cache/conventional-changelog-conventionalcommits-npm-4.6.1-030ed159a8-f866616c8f.zip differ diff --git a/.yarn/cache/conventional-changelog-core-npm-4.2.4-3507358941-56d5194040.zip b/.yarn/cache/conventional-changelog-core-npm-4.2.4-3507358941-56d5194040.zip new file mode 100644 index 00000000000..ef22121cbba Binary files /dev/null and b/.yarn/cache/conventional-changelog-core-npm-4.2.4-3507358941-56d5194040.zip differ diff --git a/.yarn/cache/conventional-changelog-dash-https-4455a1a41e-98199fb767.zip b/.yarn/cache/conventional-changelog-dash-https-4455a1a41e-98199fb767.zip new file mode 100644 index 00000000000..10b9e79a8e8 Binary files /dev/null and b/.yarn/cache/conventional-changelog-dash-https-4455a1a41e-98199fb767.zip differ diff --git a/.yarn/cache/conventional-changelog-ember-npm-2.0.9-2276834930-30c7bd48ce.zip b/.yarn/cache/conventional-changelog-ember-npm-2.0.9-2276834930-30c7bd48ce.zip new file mode 100644 index 00000000000..e3e7b8fecde Binary files /dev/null and b/.yarn/cache/conventional-changelog-ember-npm-2.0.9-2276834930-30c7bd48ce.zip differ diff --git a/.yarn/cache/conventional-changelog-eslint-npm-3.0.9-62c523a901-402ae73a8c.zip b/.yarn/cache/conventional-changelog-eslint-npm-3.0.9-62c523a901-402ae73a8c.zip new file mode 100644 index 00000000000..dab4bec7113 Binary files /dev/null and b/.yarn/cache/conventional-changelog-eslint-npm-3.0.9-62c523a901-402ae73a8c.zip differ diff --git a/.yarn/cache/conventional-changelog-express-npm-2.0.6-8a37ff0369-c139fa9878.zip b/.yarn/cache/conventional-changelog-express-npm-2.0.6-8a37ff0369-c139fa9878.zip new file mode 100644 index 00000000000..5cbb3f0bcfd Binary files /dev/null and b/.yarn/cache/conventional-changelog-express-npm-2.0.6-8a37ff0369-c139fa9878.zip differ diff --git a/.yarn/cache/conventional-changelog-jquery-npm-3.0.11-d4ff10c6e2-df1145467c.zip b/.yarn/cache/conventional-changelog-jquery-npm-3.0.11-d4ff10c6e2-df1145467c.zip new file mode 100644 index 00000000000..7e7d068c748 Binary files /dev/null and b/.yarn/cache/conventional-changelog-jquery-npm-3.0.11-d4ff10c6e2-df1145467c.zip differ diff --git a/.yarn/cache/conventional-changelog-jshint-npm-2.0.9-ef6b791bee-ec96144b75.zip b/.yarn/cache/conventional-changelog-jshint-npm-2.0.9-ef6b791bee-ec96144b75.zip new file mode 100644 index 00000000000..47404f8d211 Binary files /dev/null and b/.yarn/cache/conventional-changelog-jshint-npm-2.0.9-ef6b791bee-ec96144b75.zip differ diff --git a/.yarn/cache/conventional-changelog-npm-3.1.24-11de891016-54253a3e37.zip b/.yarn/cache/conventional-changelog-npm-3.1.24-11de891016-54253a3e37.zip new file mode 100644 index 00000000000..43545d00a03 Binary files /dev/null and b/.yarn/cache/conventional-changelog-npm-3.1.24-11de891016-54253a3e37.zip differ diff --git a/.yarn/cache/conventional-changelog-preset-loader-npm-2.3.4-a907f2e49a-23a889b7fc.zip b/.yarn/cache/conventional-changelog-preset-loader-npm-2.3.4-a907f2e49a-23a889b7fc.zip new file mode 100644 index 00000000000..52e52b15fa4 Binary files /dev/null and b/.yarn/cache/conventional-changelog-preset-loader-npm-2.3.4-a907f2e49a-23a889b7fc.zip differ diff --git a/.yarn/cache/conventional-changelog-writer-npm-5.0.0-acebc38f2a-c310b949d3.zip b/.yarn/cache/conventional-changelog-writer-npm-5.0.0-acebc38f2a-c310b949d3.zip new file mode 100644 index 00000000000..07d12d4ced3 Binary files /dev/null and b/.yarn/cache/conventional-changelog-writer-npm-5.0.0-acebc38f2a-c310b949d3.zip differ diff --git a/.yarn/cache/conventional-commits-filter-npm-2.0.7-8762ee3bfa-feb567f680.zip b/.yarn/cache/conventional-commits-filter-npm-2.0.7-8762ee3bfa-feb567f680.zip new file mode 100644 index 00000000000..4d90d61f5e6 Binary files /dev/null and b/.yarn/cache/conventional-commits-filter-npm-2.0.7-8762ee3bfa-feb567f680.zip differ diff --git a/.yarn/cache/conventional-commits-parser-npm-3.2.3-f108fda552-0f57b5cb7c.zip b/.yarn/cache/conventional-commits-parser-npm-3.2.3-f108fda552-0f57b5cb7c.zip new file mode 100644 index 00000000000..0e3e5bf6bc3 Binary files /dev/null and b/.yarn/cache/conventional-commits-parser-npm-3.2.3-f108fda552-0f57b5cb7c.zip differ diff --git a/.yarn/cache/convert-source-map-npm-1.1.3-7f1bfeabd4-0ed6bdecd3.zip b/.yarn/cache/convert-source-map-npm-1.1.3-7f1bfeabd4-0ed6bdecd3.zip new file mode 100644 index 00000000000..2fea896d09a Binary files /dev/null and b/.yarn/cache/convert-source-map-npm-1.1.3-7f1bfeabd4-0ed6bdecd3.zip differ diff --git a/.yarn/cache/convert-source-map-npm-1.8.0-037f671dde-985d974a2d.zip b/.yarn/cache/convert-source-map-npm-1.8.0-037f671dde-985d974a2d.zip new file mode 100644 index 00000000000..00cffe534e4 Binary files /dev/null and b/.yarn/cache/convert-source-map-npm-1.8.0-037f671dde-985d974a2d.zip differ diff --git a/.yarn/cache/cookie-npm-0.4.1-cc5e2ebb42-bd7c47f5d9.zip b/.yarn/cache/cookie-npm-0.4.1-cc5e2ebb42-bd7c47f5d9.zip new file mode 100644 index 00000000000..67c675ed56e Binary files /dev/null and b/.yarn/cache/cookie-npm-0.4.1-cc5e2ebb42-bd7c47f5d9.zip differ diff --git a/.yarn/cache/core-js-compat-npm-3.19.1-fbc4223527-ed302c9981.zip b/.yarn/cache/core-js-compat-npm-3.19.1-fbc4223527-ed302c9981.zip new file mode 100644 index 00000000000..b557f843a50 Binary files /dev/null and b/.yarn/cache/core-js-compat-npm-3.19.1-fbc4223527-ed302c9981.zip differ diff --git a/.yarn/cache/core-js-npm-3.19.1-772a85cbf5-2f66906178.zip b/.yarn/cache/core-js-npm-3.19.1-772a85cbf5-2f66906178.zip new file mode 100644 index 00000000000..3e51139e265 Binary files /dev/null and b/.yarn/cache/core-js-npm-3.19.1-772a85cbf5-2f66906178.zip differ diff --git a/.yarn/cache/core-util-is-npm-1.0.2-9fc2b94dc3-7a4c925b49.zip b/.yarn/cache/core-util-is-npm-1.0.2-9fc2b94dc3-7a4c925b49.zip new file mode 100644 index 00000000000..00b0792a3b9 Binary files /dev/null and b/.yarn/cache/core-util-is-npm-1.0.2-9fc2b94dc3-7a4c925b49.zip differ diff --git a/.yarn/cache/core-util-is-npm-1.0.3-ca74b76c90-9de8597363.zip b/.yarn/cache/core-util-is-npm-1.0.3-ca74b76c90-9de8597363.zip new file mode 100644 index 00000000000..2c844fee1b3 Binary files /dev/null and b/.yarn/cache/core-util-is-npm-1.0.3-ca74b76c90-9de8597363.zip differ diff --git a/.yarn/cache/cors-npm-2.8.5-c9935a2d12-ced838404c.zip b/.yarn/cache/cors-npm-2.8.5-c9935a2d12-ced838404c.zip new file mode 100644 index 00000000000..b7ab2c53f9e Binary files /dev/null and b/.yarn/cache/cors-npm-2.8.5-c9935a2d12-ced838404c.zip differ diff --git a/.yarn/cache/cpu-features-npm-0.0.2-b27e7998ec-15177f9a2d.zip b/.yarn/cache/cpu-features-npm-0.0.2-b27e7998ec-15177f9a2d.zip new file mode 100644 index 00000000000..3c7c9bab7a2 Binary files /dev/null and b/.yarn/cache/cpu-features-npm-0.0.2-b27e7998ec-15177f9a2d.zip differ diff --git a/.yarn/cache/create-ecdh-npm-4.0.4-1048ce2035-0dd7fca971.zip b/.yarn/cache/create-ecdh-npm-4.0.4-1048ce2035-0dd7fca971.zip new file mode 100644 index 00000000000..64ebbe44be4 Binary files /dev/null and b/.yarn/cache/create-ecdh-npm-4.0.4-1048ce2035-0dd7fca971.zip differ diff --git a/.yarn/cache/create-hash-npm-1.2.0-afd048e1ce-02a6ae3bb9.zip b/.yarn/cache/create-hash-npm-1.2.0-afd048e1ce-02a6ae3bb9.zip new file mode 100644 index 00000000000..cb81aa9aa5a Binary files /dev/null and b/.yarn/cache/create-hash-npm-1.2.0-afd048e1ce-02a6ae3bb9.zip differ diff --git a/.yarn/cache/create-hmac-npm-1.1.7-b4ef32668a-ba12bb2257.zip b/.yarn/cache/create-hmac-npm-1.1.7-b4ef32668a-ba12bb2257.zip new file mode 100644 index 00000000000..07a58d193e7 Binary files /dev/null and b/.yarn/cache/create-hmac-npm-1.1.7-b4ef32668a-ba12bb2257.zip differ diff --git a/.yarn/cache/create-require-npm-1.1.1-839884ca2e-a9a1503d43.zip b/.yarn/cache/create-require-npm-1.1.1-839884ca2e-a9a1503d43.zip new file mode 100644 index 00000000000..afbfac2109d Binary files /dev/null and b/.yarn/cache/create-require-npm-1.1.1-839884ca2e-a9a1503d43.zip differ diff --git a/.yarn/cache/cross-spawn-npm-6.0.5-2deab6c280-f893bb0d96.zip b/.yarn/cache/cross-spawn-npm-6.0.5-2deab6c280-f893bb0d96.zip new file mode 100644 index 00000000000..dfa0f5171a3 Binary files /dev/null and b/.yarn/cache/cross-spawn-npm-6.0.5-2deab6c280-f893bb0d96.zip differ diff --git a/.yarn/cache/cross-spawn-npm-7.0.3-e4ff3e65b3-671cc7c728.zip b/.yarn/cache/cross-spawn-npm-7.0.3-e4ff3e65b3-671cc7c728.zip new file mode 100644 index 00000000000..9613e383d10 Binary files /dev/null and b/.yarn/cache/cross-spawn-npm-7.0.3-e4ff3e65b3-671cc7c728.zip differ diff --git a/.yarn/cache/crypto-browserify-npm-3.12.0-bed454fef0-c1609af826.zip b/.yarn/cache/crypto-browserify-npm-3.12.0-bed454fef0-c1609af826.zip new file mode 100644 index 00000000000..b4b8d3dfea0 Binary files /dev/null and b/.yarn/cache/crypto-browserify-npm-3.12.0-bed454fef0-c1609af826.zip differ diff --git a/.yarn/cache/crypto-js-npm-4.1.1-38a3b8c19d-b3747c12ee.zip b/.yarn/cache/crypto-js-npm-4.1.1-38a3b8c19d-b3747c12ee.zip new file mode 100644 index 00000000000..e392e0d53c1 Binary files /dev/null and b/.yarn/cache/crypto-js-npm-4.1.1-38a3b8c19d-b3747c12ee.zip differ diff --git a/.yarn/cache/crypto-random-string-npm-2.0.0-8ab47992ef-0283879f55.zip b/.yarn/cache/crypto-random-string-npm-2.0.0-8ab47992ef-0283879f55.zip new file mode 100644 index 00000000000..90bce332286 Binary files /dev/null and b/.yarn/cache/crypto-random-string-npm-2.0.0-8ab47992ef-0283879f55.zip differ diff --git a/.yarn/cache/custom-event-npm-1.0.1-6693c8e298-334f48a6d5.zip b/.yarn/cache/custom-event-npm-1.0.1-6693c8e298-334f48a6d5.zip new file mode 100644 index 00000000000..022c5a05935 Binary files /dev/null and b/.yarn/cache/custom-event-npm-1.0.1-6693c8e298-334f48a6d5.zip differ diff --git a/.yarn/cache/dargs-npm-7.0.0-62701e0c7a-b8f1e3cba5.zip b/.yarn/cache/dargs-npm-7.0.0-62701e0c7a-b8f1e3cba5.zip new file mode 100644 index 00000000000..004e5a487a7 Binary files /dev/null and b/.yarn/cache/dargs-npm-7.0.0-62701e0c7a-b8f1e3cba5.zip differ diff --git a/.yarn/cache/dash-ast-npm-1.0.0-2481bb8f5a-db59e5e275.zip b/.yarn/cache/dash-ast-npm-1.0.0-2481bb8f5a-db59e5e275.zip new file mode 100644 index 00000000000..39c1047fcef Binary files /dev/null and b/.yarn/cache/dash-ast-npm-1.0.0-2481bb8f5a-db59e5e275.zip differ diff --git a/.yarn/cache/dashdash-npm-1.14.1-be8f10a286-3634c24957.zip b/.yarn/cache/dashdash-npm-1.14.1-be8f10a286-3634c24957.zip new file mode 100644 index 00000000000..108f90531ad Binary files /dev/null and b/.yarn/cache/dashdash-npm-1.14.1-be8f10a286-3634c24957.zip differ diff --git a/.yarn/cache/date-fns-npm-2.28.0-c19c5add1b-a0516b2e4f.zip b/.yarn/cache/date-fns-npm-2.28.0-c19c5add1b-a0516b2e4f.zip new file mode 100644 index 00000000000..1e88493b725 Binary files /dev/null and b/.yarn/cache/date-fns-npm-2.28.0-c19c5add1b-a0516b2e4f.zip differ diff --git a/.yarn/cache/date-format-npm-2.1.0-6d206457ad-ff2c80c760.zip b/.yarn/cache/date-format-npm-2.1.0-6d206457ad-ff2c80c760.zip new file mode 100644 index 00000000000..d54e1984aea Binary files /dev/null and b/.yarn/cache/date-format-npm-2.1.0-6d206457ad-ff2c80c760.zip differ diff --git a/.yarn/cache/date-format-npm-3.0.0-da04d77f82-9e1d224460.zip b/.yarn/cache/date-format-npm-3.0.0-da04d77f82-9e1d224460.zip new file mode 100644 index 00000000000..d5dcb839e3a Binary files /dev/null and b/.yarn/cache/date-format-npm-3.0.0-da04d77f82-9e1d224460.zip differ diff --git a/.yarn/cache/dateformat-npm-3.0.3-ed02e5ddbd-ca4911148a.zip b/.yarn/cache/dateformat-npm-3.0.3-ed02e5ddbd-ca4911148a.zip new file mode 100644 index 00000000000..5747aa6945d Binary files /dev/null and b/.yarn/cache/dateformat-npm-3.0.3-ed02e5ddbd-ca4911148a.zip differ diff --git a/.yarn/cache/dateformat-npm-4.6.3-aa1a4cb7f9-c3aa0617c0.zip b/.yarn/cache/dateformat-npm-4.6.3-aa1a4cb7f9-c3aa0617c0.zip new file mode 100644 index 00000000000..6af1e8a8f84 Binary files /dev/null and b/.yarn/cache/dateformat-npm-4.6.3-aa1a4cb7f9-c3aa0617c0.zip differ diff --git a/.yarn/cache/debug-npm-2.6.9-7d4cb597dc-d2f51589ca.zip b/.yarn/cache/debug-npm-2.6.9-7d4cb597dc-d2f51589ca.zip new file mode 100644 index 00000000000..5a112760762 Binary files /dev/null and b/.yarn/cache/debug-npm-2.6.9-7d4cb597dc-d2f51589ca.zip differ diff --git a/.yarn/cache/debug-npm-3.2.7-754e818c7a-b3d8c59407.zip b/.yarn/cache/debug-npm-3.2.7-754e818c7a-b3d8c59407.zip new file mode 100644 index 00000000000..b9eb5a9e889 Binary files /dev/null and b/.yarn/cache/debug-npm-3.2.7-754e818c7a-b3d8c59407.zip differ diff --git a/.yarn/cache/debug-npm-4.3.2-f0148b6afe-820ea160e2.zip b/.yarn/cache/debug-npm-4.3.2-f0148b6afe-820ea160e2.zip new file mode 100644 index 00000000000..57db42a8498 Binary files /dev/null and b/.yarn/cache/debug-npm-4.3.2-f0148b6afe-820ea160e2.zip differ diff --git a/.yarn/cache/debug-npm-4.3.3-710fd4cc7f-14472d56fe.zip b/.yarn/cache/debug-npm-4.3.3-710fd4cc7f-14472d56fe.zip new file mode 100644 index 00000000000..f2809aaad51 Binary files /dev/null and b/.yarn/cache/debug-npm-4.3.3-710fd4cc7f-14472d56fe.zip differ diff --git a/.yarn/cache/debuglog-npm-1.0.1-c553c84ea5-970679f2eb.zip b/.yarn/cache/debuglog-npm-1.0.1-c553c84ea5-970679f2eb.zip new file mode 100644 index 00000000000..b27761581d7 Binary files /dev/null and b/.yarn/cache/debuglog-npm-1.0.1-c553c84ea5-970679f2eb.zip differ diff --git a/.yarn/cache/decamelize-keys-npm-1.1.0-75168ffadd-8bc5d32e03.zip b/.yarn/cache/decamelize-keys-npm-1.1.0-75168ffadd-8bc5d32e03.zip new file mode 100644 index 00000000000..4892844ad7d Binary files /dev/null and b/.yarn/cache/decamelize-keys-npm-1.1.0-75168ffadd-8bc5d32e03.zip differ diff --git a/.yarn/cache/decamelize-npm-1.2.0-c5a2fdc622-ad8c51a7e7.zip b/.yarn/cache/decamelize-npm-1.2.0-c5a2fdc622-ad8c51a7e7.zip new file mode 100644 index 00000000000..db4ac470f71 Binary files /dev/null and b/.yarn/cache/decamelize-npm-1.2.0-c5a2fdc622-ad8c51a7e7.zip differ diff --git a/.yarn/cache/decamelize-npm-4.0.0-12410e3409-b7d09b8265.zip b/.yarn/cache/decamelize-npm-4.0.0-12410e3409-b7d09b8265.zip new file mode 100644 index 00000000000..ab2e9464344 Binary files /dev/null and b/.yarn/cache/decamelize-npm-4.0.0-12410e3409-b7d09b8265.zip differ diff --git a/.yarn/cache/decimal.js-npm-10.3.1-797c736b6c-0351ac9f05.zip b/.yarn/cache/decimal.js-npm-10.3.1-797c736b6c-0351ac9f05.zip new file mode 100644 index 00000000000..585ed75c001 Binary files /dev/null and b/.yarn/cache/decimal.js-npm-10.3.1-797c736b6c-0351ac9f05.zip differ diff --git a/.yarn/cache/decompress-response-npm-3.3.0-6e7b6375c3-952552ac3b.zip b/.yarn/cache/decompress-response-npm-3.3.0-6e7b6375c3-952552ac3b.zip new file mode 100644 index 00000000000..52b2ac76bd0 Binary files /dev/null and b/.yarn/cache/decompress-response-npm-3.3.0-6e7b6375c3-952552ac3b.zip differ diff --git a/.yarn/cache/deep-eql-npm-3.0.1-9a66c09c65-4f4c9fb79e.zip b/.yarn/cache/deep-eql-npm-3.0.1-9a66c09c65-4f4c9fb79e.zip new file mode 100644 index 00000000000..0c632a27ef0 Binary files /dev/null and b/.yarn/cache/deep-eql-npm-3.0.1-9a66c09c65-4f4c9fb79e.zip differ diff --git a/.yarn/cache/deep-extend-npm-0.6.0-e182924219-7be7e5a8d4.zip b/.yarn/cache/deep-extend-npm-0.6.0-e182924219-7be7e5a8d4.zip new file mode 100644 index 00000000000..87f0270ec53 Binary files /dev/null and b/.yarn/cache/deep-extend-npm-0.6.0-e182924219-7be7e5a8d4.zip differ diff --git a/.yarn/cache/deep-is-npm-0.1.4-88938b5a67-edb65dd0d7.zip b/.yarn/cache/deep-is-npm-0.1.4-88938b5a67-edb65dd0d7.zip new file mode 100644 index 00000000000..2078a471f04 Binary files /dev/null and b/.yarn/cache/deep-is-npm-0.1.4-88938b5a67-edb65dd0d7.zip differ diff --git a/.yarn/cache/default-require-extensions-npm-3.0.0-40586718d6-0b5bdb6786.zip b/.yarn/cache/default-require-extensions-npm-3.0.0-40586718d6-0b5bdb6786.zip new file mode 100644 index 00000000000..3a6d1367a7a Binary files /dev/null and b/.yarn/cache/default-require-extensions-npm-3.0.0-40586718d6-0b5bdb6786.zip differ diff --git a/.yarn/cache/defaults-npm-1.0.3-e829107b9e-96e2112da6.zip b/.yarn/cache/defaults-npm-1.0.3-e829107b9e-96e2112da6.zip new file mode 100644 index 00000000000..0b0bc1beb0f Binary files /dev/null and b/.yarn/cache/defaults-npm-1.0.3-e829107b9e-96e2112da6.zip differ diff --git a/.yarn/cache/defer-to-connect-npm-1.1.3-5887885147-9491b301dc.zip b/.yarn/cache/defer-to-connect-npm-1.1.3-5887885147-9491b301dc.zip new file mode 100644 index 00000000000..75ad626c849 Binary files /dev/null and b/.yarn/cache/defer-to-connect-npm-1.1.3-5887885147-9491b301dc.zip differ diff --git a/.yarn/cache/deferred-leveldown-npm-5.3.0-01247ab5af-5631e15352.zip b/.yarn/cache/deferred-leveldown-npm-5.3.0-01247ab5af-5631e15352.zip new file mode 100644 index 00000000000..5fedbf4184a Binary files /dev/null and b/.yarn/cache/deferred-leveldown-npm-5.3.0-01247ab5af-5631e15352.zip differ diff --git a/.yarn/cache/define-properties-npm-1.1.3-0f3115e2b9-da80dba55d.zip b/.yarn/cache/define-properties-npm-1.1.3-0f3115e2b9-da80dba55d.zip new file mode 100644 index 00000000000..c44631293b1 Binary files /dev/null and b/.yarn/cache/define-properties-npm-1.1.3-0f3115e2b9-da80dba55d.zip differ diff --git a/.yarn/cache/defined-npm-1.0.0-891782ba77-77672997c5.zip b/.yarn/cache/defined-npm-1.0.0-891782ba77-77672997c5.zip new file mode 100644 index 00000000000..c2fc980e659 Binary files /dev/null and b/.yarn/cache/defined-npm-1.0.0-891782ba77-77672997c5.zip differ diff --git a/.yarn/cache/delay-npm-5.0.0-1d1c758b46-62f151151e.zip b/.yarn/cache/delay-npm-5.0.0-1d1c758b46-62f151151e.zip new file mode 100644 index 00000000000..80151c63cda Binary files /dev/null and b/.yarn/cache/delay-npm-5.0.0-1d1c758b46-62f151151e.zip differ diff --git a/.yarn/cache/delayed-stream-npm-1.0.0-c5a4c4cc02-46fe6e83e2.zip b/.yarn/cache/delayed-stream-npm-1.0.0-c5a4c4cc02-46fe6e83e2.zip new file mode 100644 index 00000000000..71514340e4e Binary files /dev/null and b/.yarn/cache/delayed-stream-npm-1.0.0-c5a4c4cc02-46fe6e83e2.zip differ diff --git a/.yarn/cache/delegates-npm-1.0.0-9b1942d75f-a51744d9b5.zip b/.yarn/cache/delegates-npm-1.0.0-9b1942d75f-a51744d9b5.zip new file mode 100644 index 00000000000..9921e5ec103 Binary files /dev/null and b/.yarn/cache/delegates-npm-1.0.0-9b1942d75f-a51744d9b5.zip differ diff --git a/.yarn/cache/denque-npm-1.5.1-2dd42d2dcb-4375ad19d5.zip b/.yarn/cache/denque-npm-1.5.1-2dd42d2dcb-4375ad19d5.zip new file mode 100644 index 00000000000..60f22e0bed0 Binary files /dev/null and b/.yarn/cache/denque-npm-1.5.1-2dd42d2dcb-4375ad19d5.zip differ diff --git a/.yarn/cache/depd-npm-1.1.2-b0c8414da7-6b406620d2.zip b/.yarn/cache/depd-npm-1.1.2-b0c8414da7-6b406620d2.zip new file mode 100644 index 00000000000..082e9254835 Binary files /dev/null and b/.yarn/cache/depd-npm-1.1.2-b0c8414da7-6b406620d2.zip differ diff --git a/.yarn/cache/deprecation-npm-2.3.1-e19c92d6e7-f56a05e182.zip b/.yarn/cache/deprecation-npm-2.3.1-e19c92d6e7-f56a05e182.zip new file mode 100644 index 00000000000..943bc441bf4 Binary files /dev/null and b/.yarn/cache/deprecation-npm-2.3.1-e19c92d6e7-f56a05e182.zip differ diff --git a/.yarn/cache/deps-sort-npm-2.0.1-d962bf2c4d-1cbaad500a.zip b/.yarn/cache/deps-sort-npm-2.0.1-d962bf2c4d-1cbaad500a.zip new file mode 100644 index 00000000000..8885221af7b Binary files /dev/null and b/.yarn/cache/deps-sort-npm-2.0.1-d962bf2c4d-1cbaad500a.zip differ diff --git a/.yarn/cache/des.js-npm-1.0.1-9f155eddb6-1ec2eedd7e.zip b/.yarn/cache/des.js-npm-1.0.1-9f155eddb6-1ec2eedd7e.zip new file mode 100644 index 00000000000..cf75727ed7c Binary files /dev/null and b/.yarn/cache/des.js-npm-1.0.1-9f155eddb6-1ec2eedd7e.zip differ diff --git a/.yarn/cache/detect-indent-npm-6.1.0-d8c441ff7a-ab953a73c7.zip b/.yarn/cache/detect-indent-npm-6.1.0-d8c441ff7a-ab953a73c7.zip new file mode 100644 index 00000000000..2cdbdeaed84 Binary files /dev/null and b/.yarn/cache/detect-indent-npm-6.1.0-d8c441ff7a-ab953a73c7.zip differ diff --git a/.yarn/cache/detective-npm-5.2.0-c623eb79e6-2ab266aecb.zip b/.yarn/cache/detective-npm-5.2.0-c623eb79e6-2ab266aecb.zip new file mode 100644 index 00000000000..8fb6a41a924 Binary files /dev/null and b/.yarn/cache/detective-npm-5.2.0-c623eb79e6-2ab266aecb.zip differ diff --git a/.yarn/cache/dezalgo-npm-1.0.3-e2bc978ebd-8b26238db9.zip b/.yarn/cache/dezalgo-npm-1.0.3-e2bc978ebd-8b26238db9.zip new file mode 100644 index 00000000000..70a87e9468c Binary files /dev/null and b/.yarn/cache/dezalgo-npm-1.0.3-e2bc978ebd-8b26238db9.zip differ diff --git a/.yarn/cache/di-npm-0.0.1-bff5be391f-3f09a99534.zip b/.yarn/cache/di-npm-0.0.1-bff5be391f-3f09a99534.zip new file mode 100644 index 00000000000..a2306916e47 Binary files /dev/null and b/.yarn/cache/di-npm-0.0.1-bff5be391f-3f09a99534.zip differ diff --git a/.yarn/cache/diff-npm-3.5.0-a321a0df19-00842950a6.zip b/.yarn/cache/diff-npm-3.5.0-a321a0df19-00842950a6.zip new file mode 100644 index 00000000000..06b673c4f99 Binary files /dev/null and b/.yarn/cache/diff-npm-3.5.0-a321a0df19-00842950a6.zip differ diff --git a/.yarn/cache/diff-npm-4.0.2-73133c7102-f2c09b0ce4.zip b/.yarn/cache/diff-npm-4.0.2-73133c7102-f2c09b0ce4.zip new file mode 100644 index 00000000000..e532815fd0c Binary files /dev/null and b/.yarn/cache/diff-npm-4.0.2-73133c7102-f2c09b0ce4.zip differ diff --git a/.yarn/cache/diff-npm-5.0.0-ad6900db18-f19fe29284.zip b/.yarn/cache/diff-npm-5.0.0-ad6900db18-f19fe29284.zip new file mode 100644 index 00000000000..301b142879c Binary files /dev/null and b/.yarn/cache/diff-npm-5.0.0-ad6900db18-f19fe29284.zip differ diff --git a/.yarn/cache/diff-sequences-npm-27.0.6-1eed05107b-f35ad024d4.zip b/.yarn/cache/diff-sequences-npm-27.0.6-1eed05107b-f35ad024d4.zip new file mode 100644 index 00000000000..a481c1777b8 Binary files /dev/null and b/.yarn/cache/diff-sequences-npm-27.0.6-1eed05107b-f35ad024d4.zip differ diff --git a/.yarn/cache/diffie-hellman-npm-5.0.3-cbef8f3171-0e620f3221.zip b/.yarn/cache/diffie-hellman-npm-5.0.3-cbef8f3171-0e620f3221.zip new file mode 100644 index 00000000000..823a8a83933 Binary files /dev/null and b/.yarn/cache/diffie-hellman-npm-5.0.3-cbef8f3171-0e620f3221.zip differ diff --git a/.yarn/cache/dir-glob-npm-3.0.1-1aea628b1b-fa05e18324.zip b/.yarn/cache/dir-glob-npm-3.0.1-1aea628b1b-fa05e18324.zip new file mode 100644 index 00000000000..e292fec5bde Binary files /dev/null and b/.yarn/cache/dir-glob-npm-3.0.1-1aea628b1b-fa05e18324.zip differ diff --git a/.yarn/cache/dirty-chai-npm-2.0.1-acaf82c8df-1e8602e78a.zip b/.yarn/cache/dirty-chai-npm-2.0.1-acaf82c8df-1e8602e78a.zip new file mode 100644 index 00000000000..aff80158983 Binary files /dev/null and b/.yarn/cache/dirty-chai-npm-2.0.1-acaf82c8df-1e8602e78a.zip differ diff --git a/.yarn/cache/dns-packet-npm-5.3.0-a1e660206b-ac93e0f6d4.zip b/.yarn/cache/dns-packet-npm-5.3.0-a1e660206b-ac93e0f6d4.zip new file mode 100644 index 00000000000..20b1ea2d1f6 Binary files /dev/null and b/.yarn/cache/dns-packet-npm-5.3.0-a1e660206b-ac93e0f6d4.zip differ diff --git a/.yarn/cache/dns-socket-npm-4.2.2-2d13a1bfa6-d02b83ecc9.zip b/.yarn/cache/dns-socket-npm-4.2.2-2d13a1bfa6-d02b83ecc9.zip new file mode 100644 index 00000000000..0dcf6b4a4e0 Binary files /dev/null and b/.yarn/cache/dns-socket-npm-4.2.2-2d13a1bfa6-d02b83ecc9.zip differ diff --git a/.yarn/cache/docker-modem-npm-3.0.3-5736be136e-4ad495d17a.zip b/.yarn/cache/docker-modem-npm-3.0.3-5736be136e-4ad495d17a.zip new file mode 100644 index 00000000000..3f2b3b898a0 Binary files /dev/null and b/.yarn/cache/docker-modem-npm-3.0.3-5736be136e-4ad495d17a.zip differ diff --git a/.yarn/cache/dockerode-npm-3.3.1-77efbe3384-930162ae2d.zip b/.yarn/cache/dockerode-npm-3.3.1-77efbe3384-930162ae2d.zip new file mode 100644 index 00000000000..4459b1b6be1 Binary files /dev/null and b/.yarn/cache/dockerode-npm-3.3.1-77efbe3384-930162ae2d.zip differ diff --git a/.yarn/cache/doctrine-npm-2.1.0-ac15d049b7-a45e277f7f.zip b/.yarn/cache/doctrine-npm-2.1.0-ac15d049b7-a45e277f7f.zip new file mode 100644 index 00000000000..b85eaafa1d7 Binary files /dev/null and b/.yarn/cache/doctrine-npm-2.1.0-ac15d049b7-a45e277f7f.zip differ diff --git a/.yarn/cache/doctrine-npm-3.0.0-c6f1615f04-fd7673ca77.zip b/.yarn/cache/doctrine-npm-3.0.0-c6f1615f04-fd7673ca77.zip new file mode 100644 index 00000000000..25e0903144c Binary files /dev/null and b/.yarn/cache/doctrine-npm-3.0.0-c6f1615f04-fd7673ca77.zip differ diff --git a/.yarn/cache/dom-serialize-npm-2.2.1-01ec16503e-48262e299a.zip b/.yarn/cache/dom-serialize-npm-2.2.1-01ec16503e-48262e299a.zip new file mode 100644 index 00000000000..73b90067729 Binary files /dev/null and b/.yarn/cache/dom-serialize-npm-2.2.1-01ec16503e-48262e299a.zip differ diff --git a/.yarn/cache/domain-browser-npm-1.2.0-d99f0de5ec-8f1235c7f4.zip b/.yarn/cache/domain-browser-npm-1.2.0-d99f0de5ec-8f1235c7f4.zip new file mode 100644 index 00000000000..892dfb4c652 Binary files /dev/null and b/.yarn/cache/domain-browser-npm-1.2.0-d99f0de5ec-8f1235c7f4.zip differ diff --git a/.yarn/cache/dot-npm-1.1.3-a570dedf33-9a2ecf7b5f.zip b/.yarn/cache/dot-npm-1.1.3-a570dedf33-9a2ecf7b5f.zip new file mode 100644 index 00000000000..310387a71a8 Binary files /dev/null and b/.yarn/cache/dot-npm-1.1.3-a570dedf33-9a2ecf7b5f.zip differ diff --git a/.yarn/cache/dot-prop-npm-5.3.0-7bf6ee1eb8-d577579009.zip b/.yarn/cache/dot-prop-npm-5.3.0-7bf6ee1eb8-d577579009.zip new file mode 100644 index 00000000000..4b2860f7501 Binary files /dev/null and b/.yarn/cache/dot-prop-npm-5.3.0-7bf6ee1eb8-d577579009.zip differ diff --git a/.yarn/cache/dotenv-expand-npm-5.1.0-c3fff50eb5-8017675b7f.zip b/.yarn/cache/dotenv-expand-npm-5.1.0-c3fff50eb5-8017675b7f.zip new file mode 100644 index 00000000000..6eba7a7e7a9 Binary files /dev/null and b/.yarn/cache/dotenv-expand-npm-5.1.0-c3fff50eb5-8017675b7f.zip differ diff --git a/.yarn/cache/dotenv-npm-8.6.0-2ce3e9f7bb-38e902c80b.zip b/.yarn/cache/dotenv-npm-8.6.0-2ce3e9f7bb-38e902c80b.zip new file mode 100644 index 00000000000..21f3698c0f4 Binary files /dev/null and b/.yarn/cache/dotenv-npm-8.6.0-2ce3e9f7bb-38e902c80b.zip differ diff --git a/.yarn/cache/dotenv-safe-npm-8.2.0-f1bcdebce9-8b73770330.zip b/.yarn/cache/dotenv-safe-npm-8.2.0-f1bcdebce9-8b73770330.zip new file mode 100644 index 00000000000..ffd25273467 Binary files /dev/null and b/.yarn/cache/dotenv-safe-npm-8.2.0-f1bcdebce9-8b73770330.zip differ diff --git a/.yarn/cache/duplexer2-npm-0.1.4-6bca6bef12-744961f03c.zip b/.yarn/cache/duplexer2-npm-0.1.4-6bca6bef12-744961f03c.zip new file mode 100644 index 00000000000..cb6fed9bef8 Binary files /dev/null and b/.yarn/cache/duplexer2-npm-0.1.4-6bca6bef12-744961f03c.zip differ diff --git a/.yarn/cache/duplexer3-npm-0.1.4-361a33d994-c2fd696931.zip b/.yarn/cache/duplexer3-npm-0.1.4-361a33d994-c2fd696931.zip new file mode 100644 index 00000000000..858d0a852a7 Binary files /dev/null and b/.yarn/cache/duplexer3-npm-0.1.4-361a33d994-c2fd696931.zip differ diff --git a/.yarn/cache/ecc-jsbn-npm-0.1.2-85b7a7be89-22fef4b620.zip b/.yarn/cache/ecc-jsbn-npm-0.1.2-85b7a7be89-22fef4b620.zip new file mode 100644 index 00000000000..80e362ad73d Binary files /dev/null and b/.yarn/cache/ecc-jsbn-npm-0.1.2-85b7a7be89-22fef4b620.zip differ diff --git a/.yarn/cache/ee-first-npm-1.1.1-33f8535b39-1b4cac778d.zip b/.yarn/cache/ee-first-npm-1.1.1-33f8535b39-1b4cac778d.zip new file mode 100644 index 00000000000..458439cbabc Binary files /dev/null and b/.yarn/cache/ee-first-npm-1.1.1-33f8535b39-1b4cac778d.zip differ diff --git a/.yarn/cache/ejs-npm-3.1.6-03db39fd15-81a9cdea0b.zip b/.yarn/cache/ejs-npm-3.1.6-03db39fd15-81a9cdea0b.zip new file mode 100644 index 00000000000..d062b8c600e Binary files /dev/null and b/.yarn/cache/ejs-npm-3.1.6-03db39fd15-81a9cdea0b.zip differ diff --git a/.yarn/cache/electron-to-chromium-npm-1.3.903-3e6dfabc20-0f96af03ef.zip b/.yarn/cache/electron-to-chromium-npm-1.3.903-3e6dfabc20-0f96af03ef.zip new file mode 100644 index 00000000000..9cd9ac7bd2d Binary files /dev/null and b/.yarn/cache/electron-to-chromium-npm-1.3.903-3e6dfabc20-0f96af03ef.zip differ diff --git a/.yarn/cache/elliptic-npm-6.5.3-783c509c01-fe1e546ed3.zip b/.yarn/cache/elliptic-npm-6.5.3-783c509c01-fe1e546ed3.zip new file mode 100644 index 00000000000..a583def1ab6 Binary files /dev/null and b/.yarn/cache/elliptic-npm-6.5.3-783c509c01-fe1e546ed3.zip differ diff --git a/.yarn/cache/emoji-regex-npm-7.0.3-cfe9479bb3-9159b2228b.zip b/.yarn/cache/emoji-regex-npm-7.0.3-cfe9479bb3-9159b2228b.zip new file mode 100644 index 00000000000..22e27d234b1 Binary files /dev/null and b/.yarn/cache/emoji-regex-npm-7.0.3-cfe9479bb3-9159b2228b.zip differ diff --git a/.yarn/cache/emoji-regex-npm-8.0.0-213764015c-d4c5c39d5a.zip b/.yarn/cache/emoji-regex-npm-8.0.0-213764015c-d4c5c39d5a.zip new file mode 100644 index 00000000000..d02d8879711 Binary files /dev/null and b/.yarn/cache/emoji-regex-npm-8.0.0-213764015c-d4c5c39d5a.zip differ diff --git a/.yarn/cache/emojis-list-npm-3.0.0-7faa48e6fd-ddaaa02542.zip b/.yarn/cache/emojis-list-npm-3.0.0-7faa48e6fd-ddaaa02542.zip new file mode 100644 index 00000000000..977d62dad74 Binary files /dev/null and b/.yarn/cache/emojis-list-npm-3.0.0-7faa48e6fd-ddaaa02542.zip differ diff --git a/.yarn/cache/enabled-npm-2.0.0-bf5d96c9d8-9d256d89f4.zip b/.yarn/cache/enabled-npm-2.0.0-bf5d96c9d8-9d256d89f4.zip new file mode 100644 index 00000000000..def4625384c Binary files /dev/null and b/.yarn/cache/enabled-npm-2.0.0-bf5d96c9d8-9d256d89f4.zip differ diff --git a/.yarn/cache/encodeurl-npm-1.0.2-f8c8454c41-e50e3d508c.zip b/.yarn/cache/encodeurl-npm-1.0.2-f8c8454c41-e50e3d508c.zip new file mode 100644 index 00000000000..e9badb76527 Binary files /dev/null and b/.yarn/cache/encodeurl-npm-1.0.2-f8c8454c41-e50e3d508c.zip differ diff --git a/.yarn/cache/encoding-npm-0.1.13-82a1837d30-bb98632f8f.zip b/.yarn/cache/encoding-npm-0.1.13-82a1837d30-bb98632f8f.zip new file mode 100644 index 00000000000..202e93181a5 Binary files /dev/null and b/.yarn/cache/encoding-npm-0.1.13-82a1837d30-bb98632f8f.zip differ diff --git a/.yarn/cache/end-of-stream-npm-1.4.4-497fc6dee1-530a5a5a1e.zip b/.yarn/cache/end-of-stream-npm-1.4.4-497fc6dee1-530a5a5a1e.zip new file mode 100644 index 00000000000..fecd2286f20 Binary files /dev/null and b/.yarn/cache/end-of-stream-npm-1.4.4-497fc6dee1-530a5a5a1e.zip differ diff --git a/.yarn/cache/engine.io-npm-6.1.0-cdba019cb1-37ff47e24c.zip b/.yarn/cache/engine.io-npm-6.1.0-cdba019cb1-37ff47e24c.zip new file mode 100644 index 00000000000..be676d90021 Binary files /dev/null and b/.yarn/cache/engine.io-npm-6.1.0-cdba019cb1-37ff47e24c.zip differ diff --git a/.yarn/cache/engine.io-parser-npm-5.0.2-884d92291e-bd65c3cdce.zip b/.yarn/cache/engine.io-parser-npm-5.0.2-884d92291e-bd65c3cdce.zip new file mode 100644 index 00000000000..99abe6dcd36 Binary files /dev/null and b/.yarn/cache/engine.io-parser-npm-5.0.2-884d92291e-bd65c3cdce.zip differ diff --git a/.yarn/cache/enhanced-resolve-npm-4.5.0-1bcc7900d2-4d87488584.zip b/.yarn/cache/enhanced-resolve-npm-4.5.0-1bcc7900d2-4d87488584.zip new file mode 100644 index 00000000000..7fe27b8caab Binary files /dev/null and b/.yarn/cache/enhanced-resolve-npm-4.5.0-1bcc7900d2-4d87488584.zip differ diff --git a/.yarn/cache/enhanced-resolve-npm-5.8.3-24a728966e-d79fbe5311.zip b/.yarn/cache/enhanced-resolve-npm-5.8.3-24a728966e-d79fbe5311.zip new file mode 100644 index 00000000000..3985c147351 Binary files /dev/null and b/.yarn/cache/enhanced-resolve-npm-5.8.3-24a728966e-d79fbe5311.zip differ diff --git a/.yarn/cache/enquirer-npm-2.3.6-7899175762-1c0911e14a.zip b/.yarn/cache/enquirer-npm-2.3.6-7899175762-1c0911e14a.zip new file mode 100644 index 00000000000..22c981f2bc0 Binary files /dev/null and b/.yarn/cache/enquirer-npm-2.3.6-7899175762-1c0911e14a.zip differ diff --git a/.yarn/cache/ent-npm-2.2.0-97a5f0ffb8-f588b5707d.zip b/.yarn/cache/ent-npm-2.2.0-97a5f0ffb8-f588b5707d.zip new file mode 100644 index 00000000000..a5577c5c46d Binary files /dev/null and b/.yarn/cache/ent-npm-2.2.0-97a5f0ffb8-f588b5707d.zip differ diff --git a/.yarn/cache/env-paths-npm-2.2.1-7c7577428c-65b5df55a8.zip b/.yarn/cache/env-paths-npm-2.2.1-7c7577428c-65b5df55a8.zip new file mode 100644 index 00000000000..5fecf17a478 Binary files /dev/null and b/.yarn/cache/env-paths-npm-2.2.1-7c7577428c-65b5df55a8.zip differ diff --git a/.yarn/cache/envinfo-npm-7.8.1-f320033691-de736c98d6.zip b/.yarn/cache/envinfo-npm-7.8.1-f320033691-de736c98d6.zip new file mode 100644 index 00000000000..006bb8e749b Binary files /dev/null and b/.yarn/cache/envinfo-npm-7.8.1-f320033691-de736c98d6.zip differ diff --git a/.yarn/cache/err-code-npm-2.0.3-082e0ff9a7-8b7b1be20d.zip b/.yarn/cache/err-code-npm-2.0.3-082e0ff9a7-8b7b1be20d.zip new file mode 100644 index 00000000000..30585845686 Binary files /dev/null and b/.yarn/cache/err-code-npm-2.0.3-082e0ff9a7-8b7b1be20d.zip differ diff --git a/.yarn/cache/errno-npm-0.1.8-10ebc185bf-1271f7b9fb.zip b/.yarn/cache/errno-npm-0.1.8-10ebc185bf-1271f7b9fb.zip new file mode 100644 index 00000000000..b88d29849cc Binary files /dev/null and b/.yarn/cache/errno-npm-0.1.8-10ebc185bf-1271f7b9fb.zip differ diff --git a/.yarn/cache/error-ex-npm-1.3.2-5654f80c0f-c1c2b8b65f.zip b/.yarn/cache/error-ex-npm-1.3.2-5654f80c0f-c1c2b8b65f.zip new file mode 100644 index 00000000000..9577ccee8de Binary files /dev/null and b/.yarn/cache/error-ex-npm-1.3.2-5654f80c0f-c1c2b8b65f.zip differ diff --git a/.yarn/cache/error-npm-10.4.0-cb27050f2f-26c9ecb7af.zip b/.yarn/cache/error-npm-10.4.0-cb27050f2f-26c9ecb7af.zip new file mode 100644 index 00000000000..3ae5ead8f14 Binary files /dev/null and b/.yarn/cache/error-npm-10.4.0-cb27050f2f-26c9ecb7af.zip differ diff --git a/.yarn/cache/es-abstract-npm-1.19.1-885c72759a-b6be841067.zip b/.yarn/cache/es-abstract-npm-1.19.1-885c72759a-b6be841067.zip new file mode 100644 index 00000000000..755b623f0b5 Binary files /dev/null and b/.yarn/cache/es-abstract-npm-1.19.1-885c72759a-b6be841067.zip differ diff --git a/.yarn/cache/es-module-lexer-npm-0.9.3-ff6236dadb-84bbab23c3.zip b/.yarn/cache/es-module-lexer-npm-0.9.3-ff6236dadb-84bbab23c3.zip new file mode 100644 index 00000000000..d7a4a2e00ce Binary files /dev/null and b/.yarn/cache/es-module-lexer-npm-0.9.3-ff6236dadb-84bbab23c3.zip differ diff --git a/.yarn/cache/es-to-primitive-npm-1.2.1-b7a7eac6c5-4ead6671a2.zip b/.yarn/cache/es-to-primitive-npm-1.2.1-b7a7eac6c5-4ead6671a2.zip new file mode 100644 index 00000000000..c0bb5b0c69a Binary files /dev/null and b/.yarn/cache/es-to-primitive-npm-1.2.1-b7a7eac6c5-4ead6671a2.zip differ diff --git a/.yarn/cache/es6-error-npm-4.1.1-5e8c22b20f-ae41332a51.zip b/.yarn/cache/es6-error-npm-4.1.1-5e8c22b20f-ae41332a51.zip new file mode 100644 index 00000000000..4d8e3ef896c Binary files /dev/null and b/.yarn/cache/es6-error-npm-4.1.1-5e8c22b20f-ae41332a51.zip differ diff --git a/.yarn/cache/es6-object-assign-npm-1.1.0-0565318480-8d4fdf6348.zip b/.yarn/cache/es6-object-assign-npm-1.1.0-0565318480-8d4fdf6348.zip new file mode 100644 index 00000000000..8fca3cd4f26 Binary files /dev/null and b/.yarn/cache/es6-object-assign-npm-1.1.0-0565318480-8d4fdf6348.zip differ diff --git a/.yarn/cache/es6-promise-npm-4.2.8-c9f5b11f66-95614a8887.zip b/.yarn/cache/es6-promise-npm-4.2.8-c9f5b11f66-95614a8887.zip new file mode 100644 index 00000000000..91f4494f946 Binary files /dev/null and b/.yarn/cache/es6-promise-npm-4.2.8-c9f5b11f66-95614a8887.zip differ diff --git a/.yarn/cache/es6-promisify-npm-5.0.0-3726550934-fbed9d7915.zip b/.yarn/cache/es6-promisify-npm-5.0.0-3726550934-fbed9d7915.zip new file mode 100644 index 00000000000..8a448346ac4 Binary files /dev/null and b/.yarn/cache/es6-promisify-npm-5.0.0-3726550934-fbed9d7915.zip differ diff --git a/.yarn/cache/escalade-npm-3.1.1-e02da076aa-a3e2a99f07.zip b/.yarn/cache/escalade-npm-3.1.1-e02da076aa-a3e2a99f07.zip new file mode 100644 index 00000000000..88c57af4b86 Binary files /dev/null and b/.yarn/cache/escalade-npm-3.1.1-e02da076aa-a3e2a99f07.zip differ diff --git a/.yarn/cache/escape-goat-npm-2.1.1-2e437cf3fe-ce05c70c20.zip b/.yarn/cache/escape-goat-npm-2.1.1-2e437cf3fe-ce05c70c20.zip new file mode 100644 index 00000000000..bcf798a59dd Binary files /dev/null and b/.yarn/cache/escape-goat-npm-2.1.1-2e437cf3fe-ce05c70c20.zip differ diff --git a/.yarn/cache/escape-html-npm-1.0.3-376c22ee74-6213ca9ae0.zip b/.yarn/cache/escape-html-npm-1.0.3-376c22ee74-6213ca9ae0.zip new file mode 100644 index 00000000000..d12a72b12e6 Binary files /dev/null and b/.yarn/cache/escape-html-npm-1.0.3-376c22ee74-6213ca9ae0.zip differ diff --git a/.yarn/cache/escape-latex-npm-1.2.0-1481ca81a7-73a787319f.zip b/.yarn/cache/escape-latex-npm-1.2.0-1481ca81a7-73a787319f.zip new file mode 100644 index 00000000000..ae599c2ca22 Binary files /dev/null and b/.yarn/cache/escape-latex-npm-1.2.0-1481ca81a7-73a787319f.zip differ diff --git a/.yarn/cache/escape-string-regexp-npm-1.0.5-3284de402f-6092fda75c.zip b/.yarn/cache/escape-string-regexp-npm-1.0.5-3284de402f-6092fda75c.zip new file mode 100644 index 00000000000..b7ea3be1478 Binary files /dev/null and b/.yarn/cache/escape-string-regexp-npm-1.0.5-3284de402f-6092fda75c.zip differ diff --git a/.yarn/cache/escape-string-regexp-npm-2.0.0-aef69d2a25-9f8a2d5743.zip b/.yarn/cache/escape-string-regexp-npm-2.0.0-aef69d2a25-9f8a2d5743.zip new file mode 100644 index 00000000000..5150d4e552f Binary files /dev/null and b/.yarn/cache/escape-string-regexp-npm-2.0.0-aef69d2a25-9f8a2d5743.zip differ diff --git a/.yarn/cache/escape-string-regexp-npm-4.0.0-4b531d8d59-98b48897d9.zip b/.yarn/cache/escape-string-regexp-npm-4.0.0-4b531d8d59-98b48897d9.zip new file mode 100644 index 00000000000..c23e416b5a7 Binary files /dev/null and b/.yarn/cache/escape-string-regexp-npm-4.0.0-4b531d8d59-98b48897d9.zip differ diff --git a/.yarn/cache/escodegen-npm-2.0.0-6450b02925-5aa6b2966f.zip b/.yarn/cache/escodegen-npm-2.0.0-6450b02925-5aa6b2966f.zip new file mode 100644 index 00000000000..847a157509a Binary files /dev/null and b/.yarn/cache/escodegen-npm-2.0.0-6450b02925-5aa6b2966f.zip differ diff --git a/.yarn/cache/eslint-config-airbnb-base-npm-14.2.1-50131c00fb-858bea748a.zip b/.yarn/cache/eslint-config-airbnb-base-npm-14.2.1-50131c00fb-858bea748a.zip new file mode 100644 index 00000000000..5f84b75fab2 Binary files /dev/null and b/.yarn/cache/eslint-config-airbnb-base-npm-14.2.1-50131c00fb-858bea748a.zip differ diff --git a/.yarn/cache/eslint-config-prettier-npm-8.3.0-f540cd1f53-df4cea3032.zip b/.yarn/cache/eslint-config-prettier-npm-8.3.0-f540cd1f53-df4cea3032.zip new file mode 100644 index 00000000000..90bb397fe0c Binary files /dev/null and b/.yarn/cache/eslint-config-prettier-npm-8.3.0-f540cd1f53-df4cea3032.zip differ diff --git a/.yarn/cache/eslint-import-resolver-node-npm-0.3.6-d9426786c6-6266733af1.zip b/.yarn/cache/eslint-import-resolver-node-npm-0.3.6-d9426786c6-6266733af1.zip new file mode 100644 index 00000000000..a4588dad43f Binary files /dev/null and b/.yarn/cache/eslint-import-resolver-node-npm-0.3.6-d9426786c6-6266733af1.zip differ diff --git a/.yarn/cache/eslint-module-utils-npm-2.7.1-2b7798b493-c30dfa125a.zip b/.yarn/cache/eslint-module-utils-npm-2.7.1-2b7798b493-c30dfa125a.zip new file mode 100644 index 00000000000..43f1faa1927 Binary files /dev/null and b/.yarn/cache/eslint-module-utils-npm-2.7.1-2b7798b493-c30dfa125a.zip differ diff --git a/.yarn/cache/eslint-npm-7.32.0-e15cc6682f-cc85af9985.zip b/.yarn/cache/eslint-npm-7.32.0-e15cc6682f-cc85af9985.zip new file mode 100644 index 00000000000..74115cf00d5 Binary files /dev/null and b/.yarn/cache/eslint-npm-7.32.0-e15cc6682f-cc85af9985.zip differ diff --git a/.yarn/cache/eslint-plugin-import-npm-2.25.3-f5faefaae3-8bdf4b1faf.zip b/.yarn/cache/eslint-plugin-import-npm-2.25.3-f5faefaae3-8bdf4b1faf.zip new file mode 100644 index 00000000000..e1c8af81475 Binary files /dev/null and b/.yarn/cache/eslint-plugin-import-npm-2.25.3-f5faefaae3-8bdf4b1faf.zip differ diff --git a/.yarn/cache/eslint-plugin-jsdoc-npm-27.1.2-30c15b9d07-df6550e057.zip b/.yarn/cache/eslint-plugin-jsdoc-npm-27.1.2-30c15b9d07-df6550e057.zip new file mode 100644 index 00000000000..57da694d6ef Binary files /dev/null and b/.yarn/cache/eslint-plugin-jsdoc-npm-27.1.2-30c15b9d07-df6550e057.zip differ diff --git a/.yarn/cache/eslint-scope-npm-5.1.1-71fe59b18a-47e4b6a3f0.zip b/.yarn/cache/eslint-scope-npm-5.1.1-71fe59b18a-47e4b6a3f0.zip new file mode 100644 index 00000000000..cf013ed64f8 Binary files /dev/null and b/.yarn/cache/eslint-scope-npm-5.1.1-71fe59b18a-47e4b6a3f0.zip differ diff --git a/.yarn/cache/eslint-utils-npm-2.1.0-a3a7ebf4fa-27500938f3.zip b/.yarn/cache/eslint-utils-npm-2.1.0-a3a7ebf4fa-27500938f3.zip new file mode 100644 index 00000000000..1dadeb5d09b Binary files /dev/null and b/.yarn/cache/eslint-utils-npm-2.1.0-a3a7ebf4fa-27500938f3.zip differ diff --git a/.yarn/cache/eslint-visitor-keys-npm-1.3.0-c07780a0fb-37a19b712f.zip b/.yarn/cache/eslint-visitor-keys-npm-1.3.0-c07780a0fb-37a19b712f.zip new file mode 100644 index 00000000000..070b3cb7884 Binary files /dev/null and b/.yarn/cache/eslint-visitor-keys-npm-1.3.0-c07780a0fb-37a19b712f.zip differ diff --git a/.yarn/cache/eslint-visitor-keys-npm-2.1.0-c31806b6b9-e3081d7dd2.zip b/.yarn/cache/eslint-visitor-keys-npm-2.1.0-c31806b6b9-e3081d7dd2.zip new file mode 100644 index 00000000000..a99eddbc6a3 Binary files /dev/null and b/.yarn/cache/eslint-visitor-keys-npm-2.1.0-c31806b6b9-e3081d7dd2.zip differ diff --git a/.yarn/cache/eslint-visitor-keys-npm-3.1.0-9a6ffc9175-fd2d613bb3.zip b/.yarn/cache/eslint-visitor-keys-npm-3.1.0-9a6ffc9175-fd2d613bb3.zip new file mode 100644 index 00000000000..4fcb36af65f Binary files /dev/null and b/.yarn/cache/eslint-visitor-keys-npm-3.1.0-9a6ffc9175-fd2d613bb3.zip differ diff --git a/.yarn/cache/espree-npm-7.3.1-8d8ea5d1e3-aa9b50dcce.zip b/.yarn/cache/espree-npm-7.3.1-8d8ea5d1e3-aa9b50dcce.zip new file mode 100644 index 00000000000..be256f02595 Binary files /dev/null and b/.yarn/cache/espree-npm-7.3.1-8d8ea5d1e3-aa9b50dcce.zip differ diff --git a/.yarn/cache/espree-npm-9.1.0-fd22538590-ba9b0f759c.zip b/.yarn/cache/espree-npm-9.1.0-fd22538590-ba9b0f759c.zip new file mode 100644 index 00000000000..73fc6eb3b31 Binary files /dev/null and b/.yarn/cache/espree-npm-9.1.0-fd22538590-ba9b0f759c.zip differ diff --git a/.yarn/cache/esprima-npm-4.0.1-1084e98778-b45bc805a6.zip b/.yarn/cache/esprima-npm-4.0.1-1084e98778-b45bc805a6.zip new file mode 100644 index 00000000000..501ceb373bf Binary files /dev/null and b/.yarn/cache/esprima-npm-4.0.1-1084e98778-b45bc805a6.zip differ diff --git a/.yarn/cache/esquery-npm-1.4.0-f39408b1a7-a0807e17ab.zip b/.yarn/cache/esquery-npm-1.4.0-f39408b1a7-a0807e17ab.zip new file mode 100644 index 00000000000..abf91d4c4fa Binary files /dev/null and b/.yarn/cache/esquery-npm-1.4.0-f39408b1a7-a0807e17ab.zip differ diff --git a/.yarn/cache/esrecurse-npm-4.3.0-10b86a887a-ebc17b1a33.zip b/.yarn/cache/esrecurse-npm-4.3.0-10b86a887a-ebc17b1a33.zip new file mode 100644 index 00000000000..97e67b46e5d Binary files /dev/null and b/.yarn/cache/esrecurse-npm-4.3.0-10b86a887a-ebc17b1a33.zip differ diff --git a/.yarn/cache/estraverse-npm-4.3.0-920a32f3c6-a6299491f9.zip b/.yarn/cache/estraverse-npm-4.3.0-920a32f3c6-a6299491f9.zip new file mode 100644 index 00000000000..f907761a9f5 Binary files /dev/null and b/.yarn/cache/estraverse-npm-4.3.0-920a32f3c6-a6299491f9.zip differ diff --git a/.yarn/cache/estraverse-npm-5.3.0-03284f8f63-072780882d.zip b/.yarn/cache/estraverse-npm-5.3.0-03284f8f63-072780882d.zip new file mode 100644 index 00000000000..eb7c3ccbcd9 Binary files /dev/null and b/.yarn/cache/estraverse-npm-5.3.0-03284f8f63-072780882d.zip differ diff --git a/.yarn/cache/esutils-npm-2.0.3-f865beafd5-22b5b08f74.zip b/.yarn/cache/esutils-npm-2.0.3-f865beafd5-22b5b08f74.zip new file mode 100644 index 00000000000..c163c32a0e8 Binary files /dev/null and b/.yarn/cache/esutils-npm-2.0.3-f865beafd5-22b5b08f74.zip differ diff --git a/.yarn/cache/eventemitter3-npm-4.0.7-7afcdd74ae-1875311c42.zip b/.yarn/cache/eventemitter3-npm-4.0.7-7afcdd74ae-1875311c42.zip new file mode 100644 index 00000000000..0cfd591e807 Binary files /dev/null and b/.yarn/cache/eventemitter3-npm-4.0.7-7afcdd74ae-1875311c42.zip differ diff --git a/.yarn/cache/events-npm-1.1.1-ca9e5d580e-40431eb005.zip b/.yarn/cache/events-npm-1.1.1-ca9e5d580e-40431eb005.zip new file mode 100644 index 00000000000..826f7d087d2 Binary files /dev/null and b/.yarn/cache/events-npm-1.1.1-ca9e5d580e-40431eb005.zip differ diff --git a/.yarn/cache/events-npm-2.1.0-883256cbfc-8756c4f40a.zip b/.yarn/cache/events-npm-2.1.0-883256cbfc-8756c4f40a.zip new file mode 100644 index 00000000000..6daf297d911 Binary files /dev/null and b/.yarn/cache/events-npm-2.1.0-883256cbfc-8756c4f40a.zip differ diff --git a/.yarn/cache/events-npm-3.3.0-c280bc7e48-f6f487ad21.zip b/.yarn/cache/events-npm-3.3.0-c280bc7e48-f6f487ad21.zip new file mode 100644 index 00000000000..6f643482ad8 Binary files /dev/null and b/.yarn/cache/events-npm-3.3.0-c280bc7e48-f6f487ad21.zip differ diff --git a/.yarn/cache/evp_bytestokey-npm-1.0.3-4a2644aaea-ad4e1577f1.zip b/.yarn/cache/evp_bytestokey-npm-1.0.3-4a2644aaea-ad4e1577f1.zip new file mode 100644 index 00000000000..7688cebcd97 Binary files /dev/null and b/.yarn/cache/evp_bytestokey-npm-1.0.3-4a2644aaea-ad4e1577f1.zip differ diff --git a/.yarn/cache/execa-npm-0.10.0-d18cb8f7af-da132af2b2.zip b/.yarn/cache/execa-npm-0.10.0-d18cb8f7af-da132af2b2.zip new file mode 100644 index 00000000000..7437b145216 Binary files /dev/null and b/.yarn/cache/execa-npm-0.10.0-d18cb8f7af-da132af2b2.zip differ diff --git a/.yarn/cache/execa-npm-4.1.0-cc675b4189-e30d298934.zip b/.yarn/cache/execa-npm-4.1.0-cc675b4189-e30d298934.zip new file mode 100644 index 00000000000..bffd898183d Binary files /dev/null and b/.yarn/cache/execa-npm-4.1.0-cc675b4189-e30d298934.zip differ diff --git a/.yarn/cache/execa-npm-5.1.1-191347acf5-fba9022c8c.zip b/.yarn/cache/execa-npm-5.1.1-191347acf5-fba9022c8c.zip new file mode 100644 index 00000000000..2150a7b151f Binary files /dev/null and b/.yarn/cache/execa-npm-5.1.1-191347acf5-fba9022c8c.zip differ diff --git a/.yarn/cache/expect-npm-27.3.1-c00331f3de-e7681ecc7a.zip b/.yarn/cache/expect-npm-27.3.1-c00331f3de-e7681ecc7a.zip new file mode 100644 index 00000000000..a3a1493991c Binary files /dev/null and b/.yarn/cache/expect-npm-27.3.1-c00331f3de-e7681ecc7a.zip differ diff --git a/.yarn/cache/extend-npm-3.0.2-e1ca07ac54-a50a8309ca.zip b/.yarn/cache/extend-npm-3.0.2-e1ca07ac54-a50a8309ca.zip new file mode 100644 index 00000000000..a33fb285f42 Binary files /dev/null and b/.yarn/cache/extend-npm-3.0.2-e1ca07ac54-a50a8309ca.zip differ diff --git a/.yarn/cache/external-editor-npm-3.1.0-878e7807af-1c2a616a73.zip b/.yarn/cache/external-editor-npm-3.1.0-878e7807af-1c2a616a73.zip new file mode 100644 index 00000000000..6375d6aad07 Binary files /dev/null and b/.yarn/cache/external-editor-npm-3.1.0-878e7807af-1c2a616a73.zip differ diff --git a/.yarn/cache/extsprintf-npm-1.3.0-61a92b324c-cee7a4a1e3.zip b/.yarn/cache/extsprintf-npm-1.3.0-61a92b324c-cee7a4a1e3.zip new file mode 100644 index 00000000000..e72ea1cf447 Binary files /dev/null and b/.yarn/cache/extsprintf-npm-1.3.0-61a92b324c-cee7a4a1e3.zip differ diff --git a/.yarn/cache/extsprintf-npm-1.4.1-140b2f27ab-a2f29b2419.zip b/.yarn/cache/extsprintf-npm-1.4.1-140b2f27ab-a2f29b2419.zip new file mode 100644 index 00000000000..21fa7b436bc Binary files /dev/null and b/.yarn/cache/extsprintf-npm-1.4.1-140b2f27ab-a2f29b2419.zip differ diff --git a/.yarn/cache/eyes-npm-0.1.8-4f28ed333f-c31703a92b.zip b/.yarn/cache/eyes-npm-0.1.8-4f28ed333f-c31703a92b.zip new file mode 100644 index 00000000000..c3034510e77 Binary files /dev/null and b/.yarn/cache/eyes-npm-0.1.8-4f28ed333f-c31703a92b.zip differ diff --git a/.yarn/cache/fast-decode-uri-component-npm-1.0.1-578ba9fecf-427a48fe09.zip b/.yarn/cache/fast-decode-uri-component-npm-1.0.1-578ba9fecf-427a48fe09.zip new file mode 100644 index 00000000000..a2ebdfe0b21 Binary files /dev/null and b/.yarn/cache/fast-decode-uri-component-npm-1.0.1-578ba9fecf-427a48fe09.zip differ diff --git a/.yarn/cache/fast-deep-equal-npm-2.0.1-9c01e08a62-b701835a87.zip b/.yarn/cache/fast-deep-equal-npm-2.0.1-9c01e08a62-b701835a87.zip new file mode 100644 index 00000000000..7da7ed6cb43 Binary files /dev/null and b/.yarn/cache/fast-deep-equal-npm-2.0.1-9c01e08a62-b701835a87.zip differ diff --git a/.yarn/cache/fast-deep-equal-npm-3.1.3-790edcfcf5-e21a9d8d84.zip b/.yarn/cache/fast-deep-equal-npm-3.1.3-790edcfcf5-e21a9d8d84.zip new file mode 100644 index 00000000000..c06008992c9 Binary files /dev/null and b/.yarn/cache/fast-deep-equal-npm-3.1.3-790edcfcf5-e21a9d8d84.zip differ diff --git a/.yarn/cache/fast-glob-npm-3.2.11-bc01135fef-f473105324.zip b/.yarn/cache/fast-glob-npm-3.2.11-bc01135fef-f473105324.zip new file mode 100644 index 00000000000..2bd4bfc03a2 Binary files /dev/null and b/.yarn/cache/fast-glob-npm-3.2.11-bc01135fef-f473105324.zip differ diff --git a/.yarn/cache/fast-json-patch-npm-2.2.1-63b021bb37-955aebb3f8.zip b/.yarn/cache/fast-json-patch-npm-2.2.1-63b021bb37-955aebb3f8.zip new file mode 100644 index 00000000000..27384491048 Binary files /dev/null and b/.yarn/cache/fast-json-patch-npm-2.2.1-63b021bb37-955aebb3f8.zip differ diff --git a/.yarn/cache/fast-json-patch-npm-3.1.0-f4bd467b5f-bad25a6121.zip b/.yarn/cache/fast-json-patch-npm-3.1.0-f4bd467b5f-bad25a6121.zip new file mode 100644 index 00000000000..78f052a5677 Binary files /dev/null and b/.yarn/cache/fast-json-patch-npm-3.1.0-f4bd467b5f-bad25a6121.zip differ diff --git a/.yarn/cache/fast-json-stable-stringify-npm-2.1.0-02e8905fda-b191531e36.zip b/.yarn/cache/fast-json-stable-stringify-npm-2.1.0-02e8905fda-b191531e36.zip new file mode 100644 index 00000000000..737d4761f35 Binary files /dev/null and b/.yarn/cache/fast-json-stable-stringify-npm-2.1.0-02e8905fda-b191531e36.zip differ diff --git a/.yarn/cache/fast-levenshtein-npm-2.0.6-fcd74b8df5-92cfec0a8d.zip b/.yarn/cache/fast-levenshtein-npm-2.0.6-fcd74b8df5-92cfec0a8d.zip new file mode 100644 index 00000000000..ffb76eb13cd Binary files /dev/null and b/.yarn/cache/fast-levenshtein-npm-2.0.6-fcd74b8df5-92cfec0a8d.zip differ diff --git a/.yarn/cache/fast-levenshtein-npm-3.0.0-8fbb1bef2f-02732ba6c6.zip b/.yarn/cache/fast-levenshtein-npm-3.0.0-8fbb1bef2f-02732ba6c6.zip new file mode 100644 index 00000000000..ce80edbffac Binary files /dev/null and b/.yarn/cache/fast-levenshtein-npm-3.0.0-8fbb1bef2f-02732ba6c6.zip differ diff --git a/.yarn/cache/fast-redact-npm-3.0.2-98d6f1d433-f4ffdf48f1.zip b/.yarn/cache/fast-redact-npm-3.0.2-98d6f1d433-f4ffdf48f1.zip new file mode 100644 index 00000000000..e74e97b048a Binary files /dev/null and b/.yarn/cache/fast-redact-npm-3.0.2-98d6f1d433-f4ffdf48f1.zip differ diff --git a/.yarn/cache/fast-safe-stringify-npm-2.1.1-7ce89033ca-a851cbddc4.zip b/.yarn/cache/fast-safe-stringify-npm-2.1.1-7ce89033ca-a851cbddc4.zip new file mode 100644 index 00000000000..0de375bb1a1 Binary files /dev/null and b/.yarn/cache/fast-safe-stringify-npm-2.1.1-7ce89033ca-a851cbddc4.zip differ diff --git a/.yarn/cache/fastest-levenshtein-npm-1.0.12-a32b4ef51e-e1a013698d.zip b/.yarn/cache/fastest-levenshtein-npm-1.0.12-a32b4ef51e-e1a013698d.zip new file mode 100644 index 00000000000..2e122db17b7 Binary files /dev/null and b/.yarn/cache/fastest-levenshtein-npm-1.0.12-a32b4ef51e-e1a013698d.zip differ diff --git a/.yarn/cache/fastify-warning-npm-0.2.0-f9c53563fc-c19ebccf54.zip b/.yarn/cache/fastify-warning-npm-0.2.0-f9c53563fc-c19ebccf54.zip new file mode 100644 index 00000000000..c8595ca6e75 Binary files /dev/null and b/.yarn/cache/fastify-warning-npm-0.2.0-f9c53563fc-c19ebccf54.zip differ diff --git a/.yarn/cache/fastq-npm-1.13.0-a45963881c-32cf15c29a.zip b/.yarn/cache/fastq-npm-1.13.0-a45963881c-32cf15c29a.zip new file mode 100644 index 00000000000..45cfbb09941 Binary files /dev/null and b/.yarn/cache/fastq-npm-1.13.0-a45963881c-32cf15c29a.zip differ diff --git a/.yarn/cache/fclone-npm-1.0.11-7e6cfa9908-016eb1eac4.zip b/.yarn/cache/fclone-npm-1.0.11-7e6cfa9908-016eb1eac4.zip new file mode 100644 index 00000000000..0a1285224a1 Binary files /dev/null and b/.yarn/cache/fclone-npm-1.0.11-7e6cfa9908-016eb1eac4.zip differ diff --git a/.yarn/cache/fecha-npm-4.2.1-40d84f7733-2699347494.zip b/.yarn/cache/fecha-npm-4.2.1-40d84f7733-2699347494.zip new file mode 100644 index 00000000000..138f738ec9f Binary files /dev/null and b/.yarn/cache/fecha-npm-4.2.1-40d84f7733-2699347494.zip differ diff --git a/.yarn/cache/figures-npm-3.2.0-85d357e955-85a6ad29e9.zip b/.yarn/cache/figures-npm-3.2.0-85d357e955-85a6ad29e9.zip new file mode 100644 index 00000000000..eac0ef72262 Binary files /dev/null and b/.yarn/cache/figures-npm-3.2.0-85d357e955-85a6ad29e9.zip differ diff --git a/.yarn/cache/file-entry-cache-npm-6.0.1-31965cf0af-f49701feaa.zip b/.yarn/cache/file-entry-cache-npm-6.0.1-31965cf0af-f49701feaa.zip new file mode 100644 index 00000000000..3748d0b2d21 Binary files /dev/null and b/.yarn/cache/file-entry-cache-npm-6.0.1-31965cf0af-f49701feaa.zip differ diff --git a/.yarn/cache/filelist-npm-1.0.2-d98495ab20-4d6953cb6f.zip b/.yarn/cache/filelist-npm-1.0.2-d98495ab20-4d6953cb6f.zip new file mode 100644 index 00000000000..b45167bd4da Binary files /dev/null and b/.yarn/cache/filelist-npm-1.0.2-d98495ab20-4d6953cb6f.zip differ diff --git a/.yarn/cache/fill-range-npm-7.0.1-b8b1817caa-cc283f4e65.zip b/.yarn/cache/fill-range-npm-7.0.1-b8b1817caa-cc283f4e65.zip new file mode 100644 index 00000000000..1da4a361d87 Binary files /dev/null and b/.yarn/cache/fill-range-npm-7.0.1-b8b1817caa-cc283f4e65.zip differ diff --git a/.yarn/cache/finalhandler-npm-1.1.2-55a75d6b53-617880460c.zip b/.yarn/cache/finalhandler-npm-1.1.2-55a75d6b53-617880460c.zip new file mode 100644 index 00000000000..3d0f6f375b9 Binary files /dev/null and b/.yarn/cache/finalhandler-npm-1.1.2-55a75d6b53-617880460c.zip differ diff --git a/.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-1e61c2e64f.zip b/.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-1e61c2e64f.zip new file mode 100644 index 00000000000..bb911f56131 Binary files /dev/null and b/.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-1e61c2e64f.zip differ diff --git a/.yarn/cache/find-my-way-npm-2.2.5-3bef2f72f0-9330349565.zip b/.yarn/cache/find-my-way-npm-2.2.5-3bef2f72f0-9330349565.zip new file mode 100644 index 00000000000..0b65f54fe7a Binary files /dev/null and b/.yarn/cache/find-my-way-npm-2.2.5-3bef2f72f0-9330349565.zip differ diff --git a/.yarn/cache/find-up-npm-2.1.0-9f6cb1765c-43284fe4da.zip b/.yarn/cache/find-up-npm-2.1.0-9f6cb1765c-43284fe4da.zip new file mode 100644 index 00000000000..6b2c2d9da4e Binary files /dev/null and b/.yarn/cache/find-up-npm-2.1.0-9f6cb1765c-43284fe4da.zip differ diff --git a/.yarn/cache/find-up-npm-4.1.0-c3ccf8d855-4c172680e8.zip b/.yarn/cache/find-up-npm-4.1.0-c3ccf8d855-4c172680e8.zip new file mode 100644 index 00000000000..6c1c05a3e30 Binary files /dev/null and b/.yarn/cache/find-up-npm-4.1.0-c3ccf8d855-4c172680e8.zip differ diff --git a/.yarn/cache/find-up-npm-5.0.0-e03e9b796d-07955e3573.zip b/.yarn/cache/find-up-npm-5.0.0-e03e9b796d-07955e3573.zip new file mode 100644 index 00000000000..034f3a07ef6 Binary files /dev/null and b/.yarn/cache/find-up-npm-5.0.0-e03e9b796d-07955e3573.zip differ diff --git a/.yarn/cache/find-yarn-workspace-root-npm-2.0.0-e58a501607-fa5ca8f9d0.zip b/.yarn/cache/find-yarn-workspace-root-npm-2.0.0-e58a501607-fa5ca8f9d0.zip new file mode 100644 index 00000000000..6eb6b14ab1c Binary files /dev/null and b/.yarn/cache/find-yarn-workspace-root-npm-2.0.0-e58a501607-fa5ca8f9d0.zip differ diff --git a/.yarn/cache/find-yarn-workspace-root2-npm-1.2.16-0d4f3213bd-b4abdd37ab.zip b/.yarn/cache/find-yarn-workspace-root2-npm-1.2.16-0d4f3213bd-b4abdd37ab.zip new file mode 100644 index 00000000000..8a9fbffc2e0 Binary files /dev/null and b/.yarn/cache/find-yarn-workspace-root2-npm-1.2.16-0d4f3213bd-b4abdd37ab.zip differ diff --git a/.yarn/cache/first-chunk-stream-npm-2.0.0-08ecb1b0f2-2fa86f93a4.zip b/.yarn/cache/first-chunk-stream-npm-2.0.0-08ecb1b0f2-2fa86f93a4.zip new file mode 100644 index 00000000000..4ddbc4790b5 Binary files /dev/null and b/.yarn/cache/first-chunk-stream-npm-2.0.0-08ecb1b0f2-2fa86f93a4.zip differ diff --git a/.yarn/cache/flat-cache-npm-3.0.4-ee77e5911e-4fdd10ecbc.zip b/.yarn/cache/flat-cache-npm-3.0.4-ee77e5911e-4fdd10ecbc.zip new file mode 100644 index 00000000000..adabb73b05d Binary files /dev/null and b/.yarn/cache/flat-cache-npm-3.0.4-ee77e5911e-4fdd10ecbc.zip differ diff --git a/.yarn/cache/flat-npm-5.0.2-12748102a5-12a1536ac7.zip b/.yarn/cache/flat-npm-5.0.2-12748102a5-12a1536ac7.zip new file mode 100644 index 00000000000..e3295fae7be Binary files /dev/null and b/.yarn/cache/flat-npm-5.0.2-12748102a5-12a1536ac7.zip differ diff --git a/.yarn/cache/flatstr-npm-1.0.12-4311d37d16-e1bb562c94.zip b/.yarn/cache/flatstr-npm-1.0.12-4311d37d16-e1bb562c94.zip new file mode 100644 index 00000000000..0ead0ea76a6 Binary files /dev/null and b/.yarn/cache/flatstr-npm-1.0.12-4311d37d16-e1bb562c94.zip differ diff --git a/.yarn/cache/flatted-npm-2.0.2-ccb06e14ff-473c754db7.zip b/.yarn/cache/flatted-npm-2.0.2-ccb06e14ff-473c754db7.zip new file mode 100644 index 00000000000..ee141710fd6 Binary files /dev/null and b/.yarn/cache/flatted-npm-2.0.2-ccb06e14ff-473c754db7.zip differ diff --git a/.yarn/cache/flatted-npm-3.2.4-b14c5985c7-7d33846428.zip b/.yarn/cache/flatted-npm-3.2.4-b14c5985c7-7d33846428.zip new file mode 100644 index 00000000000..c0483d993e9 Binary files /dev/null and b/.yarn/cache/flatted-npm-3.2.4-b14c5985c7-7d33846428.zip differ diff --git a/.yarn/cache/fn.name-npm-1.1.0-b472333184-e357144f48.zip b/.yarn/cache/fn.name-npm-1.1.0-b472333184-e357144f48.zip new file mode 100644 index 00000000000..416b895bfce Binary files /dev/null and b/.yarn/cache/fn.name-npm-1.1.0-b472333184-e357144f48.zip differ diff --git a/.yarn/cache/follow-redirects-npm-1.14.5-7c681222a0-f004a76b2e.zip b/.yarn/cache/follow-redirects-npm-1.14.5-7c681222a0-f004a76b2e.zip new file mode 100644 index 00000000000..a8d020683a1 Binary files /dev/null and b/.yarn/cache/follow-redirects-npm-1.14.5-7c681222a0-f004a76b2e.zip differ diff --git a/.yarn/cache/foreach-npm-2.0.5-9fbfc73114-dab4fbfef0.zip b/.yarn/cache/foreach-npm-2.0.5-9fbfc73114-dab4fbfef0.zip new file mode 100644 index 00000000000..c80ba2ee23e Binary files /dev/null and b/.yarn/cache/foreach-npm-2.0.5-9fbfc73114-dab4fbfef0.zip differ diff --git a/.yarn/cache/foreground-child-npm-2.0.0-80c976b61e-f77ec9aff6.zip b/.yarn/cache/foreground-child-npm-2.0.0-80c976b61e-f77ec9aff6.zip new file mode 100644 index 00000000000..d947311d1e9 Binary files /dev/null and b/.yarn/cache/foreground-child-npm-2.0.0-80c976b61e-f77ec9aff6.zip differ diff --git a/.yarn/cache/forever-agent-npm-0.6.1-01dae53bf9-766ae6e220.zip b/.yarn/cache/forever-agent-npm-0.6.1-01dae53bf9-766ae6e220.zip new file mode 100644 index 00000000000..8250de6b49d Binary files /dev/null and b/.yarn/cache/forever-agent-npm-0.6.1-01dae53bf9-766ae6e220.zip differ diff --git a/.yarn/cache/form-data-npm-2.3.3-c016cc11c0-10c1780fa1.zip b/.yarn/cache/form-data-npm-2.3.3-c016cc11c0-10c1780fa1.zip new file mode 100644 index 00000000000..9e2c84d8441 Binary files /dev/null and b/.yarn/cache/form-data-npm-2.3.3-c016cc11c0-10c1780fa1.zip differ diff --git a/.yarn/cache/fraction.js-npm-4.2.0-28efe4afc7-8c76a6e21d.zip b/.yarn/cache/fraction.js-npm-4.2.0-28efe4afc7-8c76a6e21d.zip new file mode 100644 index 00000000000..ac7ea788602 Binary files /dev/null and b/.yarn/cache/fraction.js-npm-4.2.0-28efe4afc7-8c76a6e21d.zip differ diff --git a/.yarn/cache/fromentries-npm-1.3.2-f5392090b8-33729c529c.zip b/.yarn/cache/fromentries-npm-1.3.2-f5392090b8-33729c529c.zip new file mode 100644 index 00000000000..060711fe32d Binary files /dev/null and b/.yarn/cache/fromentries-npm-1.3.2-f5392090b8-33729c529c.zip differ diff --git a/.yarn/cache/fs-constants-npm-1.0.0-59576b2177-18f5b71837.zip b/.yarn/cache/fs-constants-npm-1.0.0-59576b2177-18f5b71837.zip new file mode 100644 index 00000000000..91f5b6f1f20 Binary files /dev/null and b/.yarn/cache/fs-constants-npm-1.0.0-59576b2177-18f5b71837.zip differ diff --git a/.yarn/cache/fs-extra-npm-6.0.1-fe74e3ae93-133dbd765e.zip b/.yarn/cache/fs-extra-npm-6.0.1-fe74e3ae93-133dbd765e.zip new file mode 100644 index 00000000000..2ad76523bfb Binary files /dev/null and b/.yarn/cache/fs-extra-npm-6.0.1-fe74e3ae93-133dbd765e.zip differ diff --git a/.yarn/cache/fs-extra-npm-8.1.0-197473387f-bf44f0e6ce.zip b/.yarn/cache/fs-extra-npm-8.1.0-197473387f-bf44f0e6ce.zip new file mode 100644 index 00000000000..feb64dafe93 Binary files /dev/null and b/.yarn/cache/fs-extra-npm-8.1.0-197473387f-bf44f0e6ce.zip differ diff --git a/.yarn/cache/fs-extra-npm-9.1.0-983c2ddb4c-ba71ba32e0.zip b/.yarn/cache/fs-extra-npm-9.1.0-983c2ddb4c-ba71ba32e0.zip new file mode 100644 index 00000000000..4a760ba0f6d Binary files /dev/null and b/.yarn/cache/fs-extra-npm-9.1.0-983c2ddb4c-ba71ba32e0.zip differ diff --git a/.yarn/cache/fs-minipass-npm-2.1.0-501ef87306-1b8d128dae.zip b/.yarn/cache/fs-minipass-npm-2.1.0-501ef87306-1b8d128dae.zip new file mode 100644 index 00000000000..21a91aac75a Binary files /dev/null and b/.yarn/cache/fs-minipass-npm-2.1.0-501ef87306-1b8d128dae.zip differ diff --git a/.yarn/cache/fs.realpath-npm-1.0.0-c8f05d8126-99ddea01a7.zip b/.yarn/cache/fs.realpath-npm-1.0.0-c8f05d8126-99ddea01a7.zip new file mode 100644 index 00000000000..920c4caedca Binary files /dev/null and b/.yarn/cache/fs.realpath-npm-1.0.0-c8f05d8126-99ddea01a7.zip differ diff --git a/.yarn/cache/fsevents-npm-2.3.2-a881d6ac9f-97ade64e75.zip b/.yarn/cache/fsevents-npm-2.3.2-a881d6ac9f-97ade64e75.zip new file mode 100644 index 00000000000..204c8e4846b Binary files /dev/null and b/.yarn/cache/fsevents-npm-2.3.2-a881d6ac9f-97ade64e75.zip differ diff --git a/.yarn/cache/fsevents-patch-3340e2eb10-8.zip b/.yarn/cache/fsevents-patch-3340e2eb10-8.zip new file mode 100644 index 00000000000..c4511f19bda Binary files /dev/null and b/.yarn/cache/fsevents-patch-3340e2eb10-8.zip differ diff --git a/.yarn/cache/function-bind-npm-1.1.1-b56b322ae9-b32fbaebb3.zip b/.yarn/cache/function-bind-npm-1.1.1-b56b322ae9-b32fbaebb3.zip new file mode 100644 index 00000000000..c22a184eba2 Binary files /dev/null and b/.yarn/cache/function-bind-npm-1.1.1-b56b322ae9-b32fbaebb3.zip differ diff --git a/.yarn/cache/functional-red-black-tree-npm-1.0.1-ccfe924dcd-ca6c170f37.zip b/.yarn/cache/functional-red-black-tree-npm-1.0.1-ccfe924dcd-ca6c170f37.zip new file mode 100644 index 00000000000..3478d021dae Binary files /dev/null and b/.yarn/cache/functional-red-black-tree-npm-1.0.1-ccfe924dcd-ca6c170f37.zip differ diff --git a/.yarn/cache/gauge-npm-2.7.4-2189a73529-a89b53cee6.zip b/.yarn/cache/gauge-npm-2.7.4-2189a73529-a89b53cee6.zip new file mode 100644 index 00000000000..6d86f11cc87 Binary files /dev/null and b/.yarn/cache/gauge-npm-2.7.4-2189a73529-a89b53cee6.zip differ diff --git a/.yarn/cache/gauge-npm-3.0.2-9e22f7af9e-81296c00c7.zip b/.yarn/cache/gauge-npm-3.0.2-9e22f7af9e-81296c00c7.zip new file mode 100644 index 00000000000..92db2513981 Binary files /dev/null and b/.yarn/cache/gauge-npm-3.0.2-9e22f7af9e-81296c00c7.zip differ diff --git a/.yarn/cache/gauge-npm-4.0.1-c54e7ba970-398540c761.zip b/.yarn/cache/gauge-npm-4.0.1-c54e7ba970-398540c761.zip new file mode 100644 index 00000000000..a73d87eee10 Binary files /dev/null and b/.yarn/cache/gauge-npm-4.0.1-c54e7ba970-398540c761.zip differ diff --git a/.yarn/cache/gensync-npm-1.0.0-beta.2-224666d72f-a7437e58c6.zip b/.yarn/cache/gensync-npm-1.0.0-beta.2-224666d72f-a7437e58c6.zip new file mode 100644 index 00000000000..75a7ba5cdec Binary files /dev/null and b/.yarn/cache/gensync-npm-1.0.0-beta.2-224666d72f-a7437e58c6.zip differ diff --git a/.yarn/cache/get-assigned-identifiers-npm-1.2.0-559db40691-5ea831c744.zip b/.yarn/cache/get-assigned-identifiers-npm-1.2.0-559db40691-5ea831c744.zip new file mode 100644 index 00000000000..6a31fb77b7a Binary files /dev/null and b/.yarn/cache/get-assigned-identifiers-npm-1.2.0-559db40691-5ea831c744.zip differ diff --git a/.yarn/cache/get-caller-file-npm-2.0.5-80e8a86305-b9769a836d.zip b/.yarn/cache/get-caller-file-npm-2.0.5-80e8a86305-b9769a836d.zip new file mode 100644 index 00000000000..0aa2c9cd03d Binary files /dev/null and b/.yarn/cache/get-caller-file-npm-2.0.5-80e8a86305-b9769a836d.zip differ diff --git a/.yarn/cache/get-func-name-npm-2.0.0-afbf363765-8d82e69f3e.zip b/.yarn/cache/get-func-name-npm-2.0.0-afbf363765-8d82e69f3e.zip new file mode 100644 index 00000000000..7374eae53be Binary files /dev/null and b/.yarn/cache/get-func-name-npm-2.0.0-afbf363765-8d82e69f3e.zip differ diff --git a/.yarn/cache/get-intrinsic-npm-1.1.1-7e868745da-a9fe2ca8fa.zip b/.yarn/cache/get-intrinsic-npm-1.1.1-7e868745da-a9fe2ca8fa.zip new file mode 100644 index 00000000000..a33f35fae33 Binary files /dev/null and b/.yarn/cache/get-intrinsic-npm-1.1.1-7e868745da-a9fe2ca8fa.zip differ diff --git a/.yarn/cache/get-package-type-npm-0.1.0-6c70cdc8ab-bba0811116.zip b/.yarn/cache/get-package-type-npm-0.1.0-6c70cdc8ab-bba0811116.zip new file mode 100644 index 00000000000..3ea9023ca27 Binary files /dev/null and b/.yarn/cache/get-package-type-npm-0.1.0-6c70cdc8ab-bba0811116.zip differ diff --git a/.yarn/cache/get-pkg-repo-npm-4.2.1-b1cd052cb4-5abf169137.zip b/.yarn/cache/get-pkg-repo-npm-4.2.1-b1cd052cb4-5abf169137.zip new file mode 100644 index 00000000000..bb0c9ba6eb9 Binary files /dev/null and b/.yarn/cache/get-pkg-repo-npm-4.2.1-b1cd052cb4-5abf169137.zip differ diff --git a/.yarn/cache/get-stdin-npm-4.0.1-10c6ac0b43-4f73d3fe05.zip b/.yarn/cache/get-stdin-npm-4.0.1-10c6ac0b43-4f73d3fe05.zip new file mode 100644 index 00000000000..eea9cd6d7c2 Binary files /dev/null and b/.yarn/cache/get-stdin-npm-4.0.1-10c6ac0b43-4f73d3fe05.zip differ diff --git a/.yarn/cache/get-stream-npm-3.0.0-ca0b13ddbe-36142f4600.zip b/.yarn/cache/get-stream-npm-3.0.0-ca0b13ddbe-36142f4600.zip new file mode 100644 index 00000000000..c8e25da7b5a Binary files /dev/null and b/.yarn/cache/get-stream-npm-3.0.0-ca0b13ddbe-36142f4600.zip differ diff --git a/.yarn/cache/get-stream-npm-4.1.0-314d430a5d-443e191417.zip b/.yarn/cache/get-stream-npm-4.1.0-314d430a5d-443e191417.zip new file mode 100644 index 00000000000..96506105cb1 Binary files /dev/null and b/.yarn/cache/get-stream-npm-4.1.0-314d430a5d-443e191417.zip differ diff --git a/.yarn/cache/get-stream-npm-5.2.0-2cfd3b452b-8bc1a23174.zip b/.yarn/cache/get-stream-npm-5.2.0-2cfd3b452b-8bc1a23174.zip new file mode 100644 index 00000000000..f5e0b29aa20 Binary files /dev/null and b/.yarn/cache/get-stream-npm-5.2.0-2cfd3b452b-8bc1a23174.zip differ diff --git a/.yarn/cache/get-stream-npm-6.0.1-83e51a4642-e04ecece32.zip b/.yarn/cache/get-stream-npm-6.0.1-83e51a4642-e04ecece32.zip new file mode 100644 index 00000000000..ca09fa2648e Binary files /dev/null and b/.yarn/cache/get-stream-npm-6.0.1-83e51a4642-e04ecece32.zip differ diff --git a/.yarn/cache/get-symbol-description-npm-1.0.0-9c95a4bc1f-9ceff8fe96.zip b/.yarn/cache/get-symbol-description-npm-1.0.0-9c95a4bc1f-9ceff8fe96.zip new file mode 100644 index 00000000000..5cdbc26f258 Binary files /dev/null and b/.yarn/cache/get-symbol-description-npm-1.0.0-9c95a4bc1f-9ceff8fe96.zip differ diff --git a/.yarn/cache/getpass-npm-0.1.7-519164a3be-ab18d55661.zip b/.yarn/cache/getpass-npm-0.1.7-519164a3be-ab18d55661.zip new file mode 100644 index 00000000000..c0a0abf62c4 Binary files /dev/null and b/.yarn/cache/getpass-npm-0.1.7-519164a3be-ab18d55661.zip differ diff --git a/.yarn/cache/git-raw-commits-npm-2.0.10-66e3a843dd-66e2d7b4cd.zip b/.yarn/cache/git-raw-commits-npm-2.0.10-66e3a843dd-66e2d7b4cd.zip new file mode 100644 index 00000000000..bd47a15dc17 Binary files /dev/null and b/.yarn/cache/git-raw-commits-npm-2.0.10-66e3a843dd-66e2d7b4cd.zip differ diff --git a/.yarn/cache/git-remote-origin-url-npm-2.0.0-319debe0d1-85263a09c0.zip b/.yarn/cache/git-remote-origin-url-npm-2.0.0-319debe0d1-85263a09c0.zip new file mode 100644 index 00000000000..dff9fcc5ac4 Binary files /dev/null and b/.yarn/cache/git-remote-origin-url-npm-2.0.0-319debe0d1-85263a09c0.zip differ diff --git a/.yarn/cache/git-semver-tags-npm-4.1.1-93b9747811-e16d02a515.zip b/.yarn/cache/git-semver-tags-npm-4.1.1-93b9747811-e16d02a515.zip new file mode 100644 index 00000000000..db19674f106 Binary files /dev/null and b/.yarn/cache/git-semver-tags-npm-4.1.1-93b9747811-e16d02a515.zip differ diff --git a/.yarn/cache/gitconfiglocal-npm-1.0.0-905970379d-e6d2764c15.zip b/.yarn/cache/gitconfiglocal-npm-1.0.0-905970379d-e6d2764c15.zip new file mode 100644 index 00000000000..f7f1269038d Binary files /dev/null and b/.yarn/cache/gitconfiglocal-npm-1.0.0-905970379d-e6d2764c15.zip differ diff --git a/.yarn/cache/github-api-npm-3.4.0-da2c85f5b5-d6f2def92b.zip b/.yarn/cache/github-api-npm-3.4.0-da2c85f5b5-d6f2def92b.zip new file mode 100644 index 00000000000..52bc2ba3671 Binary files /dev/null and b/.yarn/cache/github-api-npm-3.4.0-da2c85f5b5-d6f2def92b.zip differ diff --git a/.yarn/cache/github-slugger-npm-1.4.0-29ff958597-4f52e7a21f.zip b/.yarn/cache/github-slugger-npm-1.4.0-29ff958597-4f52e7a21f.zip new file mode 100644 index 00000000000..0e6748a3354 Binary files /dev/null and b/.yarn/cache/github-slugger-npm-1.4.0-29ff958597-4f52e7a21f.zip differ diff --git a/.yarn/cache/github-username-npm-6.0.0-6b7380ded2-c40a6151dc.zip b/.yarn/cache/github-username-npm-6.0.0-6b7380ded2-c40a6151dc.zip new file mode 100644 index 00000000000..929fbe69279 Binary files /dev/null and b/.yarn/cache/github-username-npm-6.0.0-6b7380ded2-c40a6151dc.zip differ diff --git a/.yarn/cache/glob-npm-7.1.6-1ce3a5189a-351d549dd9.zip b/.yarn/cache/glob-npm-7.1.6-1ce3a5189a-351d549dd9.zip new file mode 100644 index 00000000000..a696eebd16d Binary files /dev/null and b/.yarn/cache/glob-npm-7.1.6-1ce3a5189a-351d549dd9.zip differ diff --git a/.yarn/cache/glob-npm-7.1.7-5698ad9c48-b61f48973b.zip b/.yarn/cache/glob-npm-7.1.7-5698ad9c48-b61f48973b.zip new file mode 100644 index 00000000000..e1b168a7c7c Binary files /dev/null and b/.yarn/cache/glob-npm-7.1.7-5698ad9c48-b61f48973b.zip differ diff --git a/.yarn/cache/glob-npm-7.2.0-bb4644d239-78a8ea9423.zip b/.yarn/cache/glob-npm-7.2.0-bb4644d239-78a8ea9423.zip new file mode 100644 index 00000000000..0ef1638a62a Binary files /dev/null and b/.yarn/cache/glob-npm-7.2.0-bb4644d239-78a8ea9423.zip differ diff --git a/.yarn/cache/glob-parent-npm-5.1.2-021ab32634-f4f2bfe242.zip b/.yarn/cache/glob-parent-npm-5.1.2-021ab32634-f4f2bfe242.zip new file mode 100644 index 00000000000..8a94317ecb6 Binary files /dev/null and b/.yarn/cache/glob-parent-npm-5.1.2-021ab32634-f4f2bfe242.zip differ diff --git a/.yarn/cache/glob-to-regexp-npm-0.4.1-cd697e0fc7-e795f4e8f0.zip b/.yarn/cache/glob-to-regexp-npm-0.4.1-cd697e0fc7-e795f4e8f0.zip new file mode 100644 index 00000000000..2276b3f4a5f Binary files /dev/null and b/.yarn/cache/glob-to-regexp-npm-0.4.1-cd697e0fc7-e795f4e8f0.zip differ diff --git a/.yarn/cache/global-dirs-npm-3.0.0-45faebeb68-953c17cf14.zip b/.yarn/cache/global-dirs-npm-3.0.0-45faebeb68-953c17cf14.zip new file mode 100644 index 00000000000..3f2995afe67 Binary files /dev/null and b/.yarn/cache/global-dirs-npm-3.0.0-45faebeb68-953c17cf14.zip differ diff --git a/.yarn/cache/globals-npm-11.12.0-1fa7f41a6c-67051a45ec.zip b/.yarn/cache/globals-npm-11.12.0-1fa7f41a6c-67051a45ec.zip new file mode 100644 index 00000000000..306b5aacad3 Binary files /dev/null and b/.yarn/cache/globals-npm-11.12.0-1fa7f41a6c-67051a45ec.zip differ diff --git a/.yarn/cache/globals-npm-13.12.0-df8e0eef2a-1f959abb11.zip b/.yarn/cache/globals-npm-13.12.0-df8e0eef2a-1f959abb11.zip new file mode 100644 index 00000000000..fac5faf4688 Binary files /dev/null and b/.yarn/cache/globals-npm-13.12.0-df8e0eef2a-1f959abb11.zip differ diff --git a/.yarn/cache/globby-npm-10.0.2-9b274c88d3-167cd067f2.zip b/.yarn/cache/globby-npm-10.0.2-9b274c88d3-167cd067f2.zip new file mode 100644 index 00000000000..94455f13441 Binary files /dev/null and b/.yarn/cache/globby-npm-10.0.2-9b274c88d3-167cd067f2.zip differ diff --git a/.yarn/cache/globby-npm-11.1.0-bdcdf20c71-b4be8885e0.zip b/.yarn/cache/globby-npm-11.1.0-bdcdf20c71-b4be8885e0.zip new file mode 100644 index 00000000000..8cd2b285830 Binary files /dev/null and b/.yarn/cache/globby-npm-11.1.0-bdcdf20c71-b4be8885e0.zip differ diff --git a/.yarn/cache/globrex-npm-0.1.2-ddda94f2d0-adca162494.zip b/.yarn/cache/globrex-npm-0.1.2-ddda94f2d0-adca162494.zip new file mode 100644 index 00000000000..5cd539b88a2 Binary files /dev/null and b/.yarn/cache/globrex-npm-0.1.2-ddda94f2d0-adca162494.zip differ diff --git a/.yarn/cache/google-protobuf-npm-3.19.1-f2bb0b2cd2-9ec57e1bdf.zip b/.yarn/cache/google-protobuf-npm-3.19.1-f2bb0b2cd2-9ec57e1bdf.zip new file mode 100644 index 00000000000..b35716c0ba4 Binary files /dev/null and b/.yarn/cache/google-protobuf-npm-3.19.1-f2bb0b2cd2-9ec57e1bdf.zip differ diff --git a/.yarn/cache/got-npm-9.6.0-80edc15fd0-941807bd97.zip b/.yarn/cache/got-npm-9.6.0-80edc15fd0-941807bd97.zip new file mode 100644 index 00000000000..95d74887b9e Binary files /dev/null and b/.yarn/cache/got-npm-9.6.0-80edc15fd0-941807bd97.zip differ diff --git a/.yarn/cache/graceful-fs-npm-4.2.10-79c70989ca-3f109d70ae.zip b/.yarn/cache/graceful-fs-npm-4.2.10-79c70989ca-3f109d70ae.zip new file mode 100644 index 00000000000..2d04255c12e Binary files /dev/null and b/.yarn/cache/graceful-fs-npm-4.2.10-79c70989ca-3f109d70ae.zip differ diff --git a/.yarn/cache/grouped-queue-npm-2.0.0-81fdc84ef7-be5c6cfac0.zip b/.yarn/cache/grouped-queue-npm-2.0.0-81fdc84ef7-be5c6cfac0.zip new file mode 100644 index 00000000000..f3e6f124fd6 Binary files /dev/null and b/.yarn/cache/grouped-queue-npm-2.0.0-81fdc84ef7-be5c6cfac0.zip differ diff --git a/.yarn/cache/growl-npm-1.10.5-2d1da54198-4b86685de6.zip b/.yarn/cache/growl-npm-1.10.5-2d1da54198-4b86685de6.zip new file mode 100644 index 00000000000..b05a103da8e Binary files /dev/null and b/.yarn/cache/growl-npm-1.10.5-2d1da54198-4b86685de6.zip differ diff --git a/.yarn/cache/grpc-web-npm-1.2.1-3d331ff494-3860a37617.zip b/.yarn/cache/grpc-web-npm-1.2.1-3d331ff494-3860a37617.zip new file mode 100644 index 00000000000..30ea196add1 Binary files /dev/null and b/.yarn/cache/grpc-web-npm-1.2.1-3d331ff494-3860a37617.zip differ diff --git a/.yarn/cache/handlebars-npm-4.7.7-a9ccfabf80-1e79a43f5e.zip b/.yarn/cache/handlebars-npm-4.7.7-a9ccfabf80-1e79a43f5e.zip new file mode 100644 index 00000000000..3b4e0992683 Binary files /dev/null and b/.yarn/cache/handlebars-npm-4.7.7-a9ccfabf80-1e79a43f5e.zip differ diff --git a/.yarn/cache/har-schema-npm-2.0.0-3a318c0ca5-d8946348f3.zip b/.yarn/cache/har-schema-npm-2.0.0-3a318c0ca5-d8946348f3.zip new file mode 100644 index 00000000000..e9ea175739e Binary files /dev/null and b/.yarn/cache/har-schema-npm-2.0.0-3a318c0ca5-d8946348f3.zip differ diff --git a/.yarn/cache/har-validator-npm-5.1.5-bd9ac162f5-b998a7269c.zip b/.yarn/cache/har-validator-npm-5.1.5-bd9ac162f5-b998a7269c.zip new file mode 100644 index 00000000000..5ae256de39d Binary files /dev/null and b/.yarn/cache/har-validator-npm-5.1.5-bd9ac162f5-b998a7269c.zip differ diff --git a/.yarn/cache/hard-rejection-npm-2.1.0-a80f2a977d-7baaf80a0c.zip b/.yarn/cache/hard-rejection-npm-2.1.0-a80f2a977d-7baaf80a0c.zip new file mode 100644 index 00000000000..95f1143f094 Binary files /dev/null and b/.yarn/cache/hard-rejection-npm-2.1.0-a80f2a977d-7baaf80a0c.zip differ diff --git a/.yarn/cache/has-ansi-npm-2.0.0-9bf0cff2af-1b51daa021.zip b/.yarn/cache/has-ansi-npm-2.0.0-9bf0cff2af-1b51daa021.zip new file mode 100644 index 00000000000..61a5a3439fb Binary files /dev/null and b/.yarn/cache/has-ansi-npm-2.0.0-9bf0cff2af-1b51daa021.zip differ diff --git a/.yarn/cache/has-bigints-npm-1.0.1-1b93717a74-44ab558681.zip b/.yarn/cache/has-bigints-npm-1.0.1-1b93717a74-44ab558681.zip new file mode 100644 index 00000000000..1f82d8f3f64 Binary files /dev/null and b/.yarn/cache/has-bigints-npm-1.0.1-1b93717a74-44ab558681.zip differ diff --git a/.yarn/cache/has-flag-npm-3.0.0-16ac11fe05-4a15638b45.zip b/.yarn/cache/has-flag-npm-3.0.0-16ac11fe05-4a15638b45.zip new file mode 100644 index 00000000000..60eafa65f5d Binary files /dev/null and b/.yarn/cache/has-flag-npm-3.0.0-16ac11fe05-4a15638b45.zip differ diff --git a/.yarn/cache/has-flag-npm-4.0.0-32af9f0536-261a135703.zip b/.yarn/cache/has-flag-npm-4.0.0-32af9f0536-261a135703.zip new file mode 100644 index 00000000000..6f5845da2f6 Binary files /dev/null and b/.yarn/cache/has-flag-npm-4.0.0-32af9f0536-261a135703.zip differ diff --git a/.yarn/cache/has-npm-1.0.3-b7f00631c1-b9ad53d53b.zip b/.yarn/cache/has-npm-1.0.3-b7f00631c1-b9ad53d53b.zip new file mode 100644 index 00000000000..f0731c951d2 Binary files /dev/null and b/.yarn/cache/has-npm-1.0.3-b7f00631c1-b9ad53d53b.zip differ diff --git a/.yarn/cache/has-symbols-npm-1.0.2-50e53af115-2309c42607.zip b/.yarn/cache/has-symbols-npm-1.0.2-50e53af115-2309c42607.zip new file mode 100644 index 00000000000..ece6cfd19c5 Binary files /dev/null and b/.yarn/cache/has-symbols-npm-1.0.2-50e53af115-2309c42607.zip differ diff --git a/.yarn/cache/has-tostringtag-npm-1.0.0-b1fcf3ab55-cc12eb28cb.zip b/.yarn/cache/has-tostringtag-npm-1.0.0-b1fcf3ab55-cc12eb28cb.zip new file mode 100644 index 00000000000..7718fc28f64 Binary files /dev/null and b/.yarn/cache/has-tostringtag-npm-1.0.0-b1fcf3ab55-cc12eb28cb.zip differ diff --git a/.yarn/cache/has-unicode-npm-2.0.1-893adb4747-1eab07a743.zip b/.yarn/cache/has-unicode-npm-2.0.1-893adb4747-1eab07a743.zip new file mode 100644 index 00000000000..5988a7e8aa9 Binary files /dev/null and b/.yarn/cache/has-unicode-npm-2.0.1-893adb4747-1eab07a743.zip differ diff --git a/.yarn/cache/has-yarn-npm-2.1.0-b73f6750d9-5eb1d0bb85.zip b/.yarn/cache/has-yarn-npm-2.1.0-b73f6750d9-5eb1d0bb85.zip new file mode 100644 index 00000000000..cca0d8a3a99 Binary files /dev/null and b/.yarn/cache/has-yarn-npm-2.1.0-b73f6750d9-5eb1d0bb85.zip differ diff --git a/.yarn/cache/hasbin-npm-1.2.3-c030bf47c8-b30ae3dc4b.zip b/.yarn/cache/hasbin-npm-1.2.3-c030bf47c8-b30ae3dc4b.zip new file mode 100644 index 00000000000..ab0c4074f59 Binary files /dev/null and b/.yarn/cache/hasbin-npm-1.2.3-c030bf47c8-b30ae3dc4b.zip differ diff --git a/.yarn/cache/hash-base-npm-3.1.0-26fc5711dd-26b7e97ac3.zip b/.yarn/cache/hash-base-npm-3.1.0-26fc5711dd-26b7e97ac3.zip new file mode 100644 index 00000000000..c43529d3a86 Binary files /dev/null and b/.yarn/cache/hash-base-npm-3.1.0-26fc5711dd-26b7e97ac3.zip differ diff --git a/.yarn/cache/hash.js-npm-1.1.7-f1ad187358-e350096e65.zip b/.yarn/cache/hash.js-npm-1.1.7-f1ad187358-e350096e65.zip new file mode 100644 index 00000000000..8ec9b47f8a1 Binary files /dev/null and b/.yarn/cache/hash.js-npm-1.1.7-f1ad187358-e350096e65.zip differ diff --git a/.yarn/cache/hasha-npm-5.2.2-d171116d12-06cc474bed.zip b/.yarn/cache/hasha-npm-5.2.2-d171116d12-06cc474bed.zip new file mode 100644 index 00000000000..ff144b928f2 Binary files /dev/null and b/.yarn/cache/hasha-npm-5.2.2-d171116d12-06cc474bed.zip differ diff --git a/.yarn/cache/he-npm-1.2.0-3b73a2ff07-3d4d6babcc.zip b/.yarn/cache/he-npm-1.2.0-3b73a2ff07-3d4d6babcc.zip new file mode 100644 index 00000000000..fe1d45f7634 Binary files /dev/null and b/.yarn/cache/he-npm-1.2.0-3b73a2ff07-3d4d6babcc.zip differ diff --git a/.yarn/cache/hmac-drbg-npm-1.0.1-3499ad31cd-bd30b6a68d.zip b/.yarn/cache/hmac-drbg-npm-1.0.1-3499ad31cd-bd30b6a68d.zip new file mode 100644 index 00000000000..e53988bb290 Binary files /dev/null and b/.yarn/cache/hmac-drbg-npm-1.0.1-3499ad31cd-bd30b6a68d.zip differ diff --git a/.yarn/cache/hosted-git-info-npm-2.8.9-62c44fa93f-c955394bda.zip b/.yarn/cache/hosted-git-info-npm-2.8.9-62c44fa93f-c955394bda.zip new file mode 100644 index 00000000000..ed4da950075 Binary files /dev/null and b/.yarn/cache/hosted-git-info-npm-2.8.9-62c44fa93f-c955394bda.zip differ diff --git a/.yarn/cache/hosted-git-info-npm-4.0.2-7330924e0c-d1b2d77203.zip b/.yarn/cache/hosted-git-info-npm-4.0.2-7330924e0c-d1b2d77203.zip new file mode 100644 index 00000000000..8670af9ee6d Binary files /dev/null and b/.yarn/cache/hosted-git-info-npm-4.0.2-7330924e0c-d1b2d77203.zip differ diff --git a/.yarn/cache/html-escaper-npm-2.0.2-38e51ef294-d2df2da3ad.zip b/.yarn/cache/html-escaper-npm-2.0.2-38e51ef294-d2df2da3ad.zip new file mode 100644 index 00000000000..cf5e7a07742 Binary files /dev/null and b/.yarn/cache/html-escaper-npm-2.0.2-38e51ef294-d2df2da3ad.zip differ diff --git a/.yarn/cache/htmlescape-npm-1.1.1-21441b0193-c59a915ae6.zip b/.yarn/cache/htmlescape-npm-1.1.1-21441b0193-c59a915ae6.zip new file mode 100644 index 00000000000..0deff122d3f Binary files /dev/null and b/.yarn/cache/htmlescape-npm-1.1.1-21441b0193-c59a915ae6.zip differ diff --git a/.yarn/cache/http-cache-semantics-npm-4.1.0-860520a31f-974de94a81.zip b/.yarn/cache/http-cache-semantics-npm-4.1.0-860520a31f-974de94a81.zip new file mode 100644 index 00000000000..ed85c1c4c78 Binary files /dev/null and b/.yarn/cache/http-cache-semantics-npm-4.1.0-860520a31f-974de94a81.zip differ diff --git a/.yarn/cache/http-call-npm-5.3.0-f2c0703f3b-06e9342e1f.zip b/.yarn/cache/http-call-npm-5.3.0-f2c0703f3b-06e9342e1f.zip new file mode 100644 index 00000000000..acb9d435d73 Binary files /dev/null and b/.yarn/cache/http-call-npm-5.3.0-f2c0703f3b-06e9342e1f.zip differ diff --git a/.yarn/cache/http-errors-npm-1.7.2-67163ae1df-5534b0ae08.zip b/.yarn/cache/http-errors-npm-1.7.2-67163ae1df-5534b0ae08.zip new file mode 100644 index 00000000000..a298ea7ebaf Binary files /dev/null and b/.yarn/cache/http-errors-npm-1.7.2-67163ae1df-5534b0ae08.zip differ diff --git a/.yarn/cache/http-proxy-agent-npm-4.0.1-ce9ef61788-c6a5da5a19.zip b/.yarn/cache/http-proxy-agent-npm-4.0.1-ce9ef61788-c6a5da5a19.zip new file mode 100644 index 00000000000..c3f1cf3169d Binary files /dev/null and b/.yarn/cache/http-proxy-agent-npm-4.0.1-ce9ef61788-c6a5da5a19.zip differ diff --git a/.yarn/cache/http-proxy-agent-npm-5.0.0-7f1f121b83-e2ee1ff165.zip b/.yarn/cache/http-proxy-agent-npm-5.0.0-7f1f121b83-e2ee1ff165.zip new file mode 100644 index 00000000000..a999ab7d5a9 Binary files /dev/null and b/.yarn/cache/http-proxy-agent-npm-5.0.0-7f1f121b83-e2ee1ff165.zip differ diff --git a/.yarn/cache/http-proxy-npm-1.18.1-a313c479c5-f5bd96bf83.zip b/.yarn/cache/http-proxy-npm-1.18.1-a313c479c5-f5bd96bf83.zip new file mode 100644 index 00000000000..0f0116f3cad Binary files /dev/null and b/.yarn/cache/http-proxy-npm-1.18.1-a313c479c5-f5bd96bf83.zip differ diff --git a/.yarn/cache/http-signature-npm-1.2.0-ee92426f34-3324598712.zip b/.yarn/cache/http-signature-npm-1.2.0-ee92426f34-3324598712.zip new file mode 100644 index 00000000000..06ea3b244c8 Binary files /dev/null and b/.yarn/cache/http-signature-npm-1.2.0-ee92426f34-3324598712.zip differ diff --git a/.yarn/cache/https-browserify-npm-1.0.0-7d6b10abbc-09b35353e4.zip b/.yarn/cache/https-browserify-npm-1.0.0-7d6b10abbc-09b35353e4.zip new file mode 100644 index 00000000000..9be5bb0dd10 Binary files /dev/null and b/.yarn/cache/https-browserify-npm-1.0.0-7d6b10abbc-09b35353e4.zip differ diff --git a/.yarn/cache/https-proxy-agent-npm-5.0.0-bb777903c3-165bfb090b.zip b/.yarn/cache/https-proxy-agent-npm-5.0.0-bb777903c3-165bfb090b.zip new file mode 100644 index 00000000000..c0b93354748 Binary files /dev/null and b/.yarn/cache/https-proxy-agent-npm-5.0.0-bb777903c3-165bfb090b.zip differ diff --git a/.yarn/cache/human-signals-npm-1.1.1-616b2586c2-d587647c9e.zip b/.yarn/cache/human-signals-npm-1.1.1-616b2586c2-d587647c9e.zip new file mode 100644 index 00000000000..1dcc5877fe1 Binary files /dev/null and b/.yarn/cache/human-signals-npm-1.1.1-616b2586c2-d587647c9e.zip differ diff --git a/.yarn/cache/human-signals-npm-2.1.0-f75815481d-b87fd89fce.zip b/.yarn/cache/human-signals-npm-2.1.0-f75815481d-b87fd89fce.zip new file mode 100644 index 00000000000..6346a18da28 Binary files /dev/null and b/.yarn/cache/human-signals-npm-2.1.0-f75815481d-b87fd89fce.zip differ diff --git a/.yarn/cache/humanize-ms-npm-1.2.1-e942bd7329-9c7a74a282.zip b/.yarn/cache/humanize-ms-npm-1.2.1-e942bd7329-9c7a74a282.zip new file mode 100644 index 00000000000..c09856b3348 Binary files /dev/null and b/.yarn/cache/humanize-ms-npm-1.2.1-e942bd7329-9c7a74a282.zip differ diff --git a/.yarn/cache/hyperlinker-npm-1.0.0-c2e60c3b2a-f6d020ac55.zip b/.yarn/cache/hyperlinker-npm-1.0.0-c2e60c3b2a-f6d020ac55.zip new file mode 100644 index 00000000000..2f03c84743c Binary files /dev/null and b/.yarn/cache/hyperlinker-npm-1.0.0-c2e60c3b2a-f6d020ac55.zip differ diff --git a/.yarn/cache/iconv-lite-npm-0.4.24-c5c4ac6695-bd9f120f5a.zip b/.yarn/cache/iconv-lite-npm-0.4.24-c5c4ac6695-bd9f120f5a.zip new file mode 100644 index 00000000000..9cae309cf91 Binary files /dev/null and b/.yarn/cache/iconv-lite-npm-0.4.24-c5c4ac6695-bd9f120f5a.zip differ diff --git a/.yarn/cache/iconv-lite-npm-0.6.3-24b8aae27e-3f60d47a5c.zip b/.yarn/cache/iconv-lite-npm-0.6.3-24b8aae27e-3f60d47a5c.zip new file mode 100644 index 00000000000..f3f767a22d7 Binary files /dev/null and b/.yarn/cache/iconv-lite-npm-0.6.3-24b8aae27e-3f60d47a5c.zip differ diff --git a/.yarn/cache/ieee754-npm-1.1.13-a57522ba12-102df1ba66.zip b/.yarn/cache/ieee754-npm-1.1.13-a57522ba12-102df1ba66.zip new file mode 100644 index 00000000000..c98e87ba347 Binary files /dev/null and b/.yarn/cache/ieee754-npm-1.1.13-a57522ba12-102df1ba66.zip differ diff --git a/.yarn/cache/ieee754-npm-1.2.1-fb63b3caeb-5144c0c981.zip b/.yarn/cache/ieee754-npm-1.2.1-fb63b3caeb-5144c0c981.zip new file mode 100644 index 00000000000..74128ad8f26 Binary files /dev/null and b/.yarn/cache/ieee754-npm-1.2.1-fb63b3caeb-5144c0c981.zip differ diff --git a/.yarn/cache/ignore-by-default-npm-1.0.1-78ea10bc54-441509147b.zip b/.yarn/cache/ignore-by-default-npm-1.0.1-78ea10bc54-441509147b.zip new file mode 100644 index 00000000000..fecc35c21f5 Binary files /dev/null and b/.yarn/cache/ignore-by-default-npm-1.0.1-78ea10bc54-441509147b.zip differ diff --git a/.yarn/cache/ignore-npm-4.0.6-66c0d6543e-248f82e50a.zip b/.yarn/cache/ignore-npm-4.0.6-66c0d6543e-248f82e50a.zip new file mode 100644 index 00000000000..f5bcbcf28ee Binary files /dev/null and b/.yarn/cache/ignore-npm-4.0.6-66c0d6543e-248f82e50a.zip differ diff --git a/.yarn/cache/ignore-npm-5.2.0-fc4b58a4f3-6b1f926792.zip b/.yarn/cache/ignore-npm-5.2.0-fc4b58a4f3-6b1f926792.zip new file mode 100644 index 00000000000..68895e2edcb Binary files /dev/null and b/.yarn/cache/ignore-npm-5.2.0-fc4b58a4f3-6b1f926792.zip differ diff --git a/.yarn/cache/ignore-walk-npm-4.0.1-e301e7e75f-903cd5cb68.zip b/.yarn/cache/ignore-walk-npm-4.0.1-e301e7e75f-903cd5cb68.zip new file mode 100644 index 00000000000..a6178242429 Binary files /dev/null and b/.yarn/cache/ignore-walk-npm-4.0.1-e301e7e75f-903cd5cb68.zip differ diff --git a/.yarn/cache/immediate-npm-3.0.6-c27588a2d3-f9b3486477.zip b/.yarn/cache/immediate-npm-3.0.6-c27588a2d3-f9b3486477.zip new file mode 100644 index 00000000000..d3f74981e57 Binary files /dev/null and b/.yarn/cache/immediate-npm-3.0.6-c27588a2d3-f9b3486477.zip differ diff --git a/.yarn/cache/immediate-npm-3.2.3-c87ede9b47-9867dc7079.zip b/.yarn/cache/immediate-npm-3.2.3-c87ede9b47-9867dc7079.zip new file mode 100644 index 00000000000..d6c4b538c79 Binary files /dev/null and b/.yarn/cache/immediate-npm-3.2.3-c87ede9b47-9867dc7079.zip differ diff --git a/.yarn/cache/immediate-npm-3.3.0-d00fd9df7d-634b430510.zip b/.yarn/cache/immediate-npm-3.3.0-d00fd9df7d-634b430510.zip new file mode 100644 index 00000000000..102fe7ed51e Binary files /dev/null and b/.yarn/cache/immediate-npm-3.3.0-d00fd9df7d-634b430510.zip differ diff --git a/.yarn/cache/import-fresh-npm-3.3.0-3e34265ca9-2cacfad06e.zip b/.yarn/cache/import-fresh-npm-3.3.0-3e34265ca9-2cacfad06e.zip new file mode 100644 index 00000000000..318d7b8460d Binary files /dev/null and b/.yarn/cache/import-fresh-npm-3.3.0-3e34265ca9-2cacfad06e.zip differ diff --git a/.yarn/cache/import-lazy-npm-2.1.0-b128ce6959-05294f3b9d.zip b/.yarn/cache/import-lazy-npm-2.1.0-b128ce6959-05294f3b9d.zip new file mode 100644 index 00000000000..9eabede0ed1 Binary files /dev/null and b/.yarn/cache/import-lazy-npm-2.1.0-b128ce6959-05294f3b9d.zip differ diff --git a/.yarn/cache/import-local-npm-3.0.3-fd16a368c1-38ae57d35e.zip b/.yarn/cache/import-local-npm-3.0.3-fd16a368c1-38ae57d35e.zip new file mode 100644 index 00000000000..3c52d597aea Binary files /dev/null and b/.yarn/cache/import-local-npm-3.0.3-fd16a368c1-38ae57d35e.zip differ diff --git a/.yarn/cache/imurmurhash-npm-0.1.4-610c5068a0-7cae75c8cd.zip b/.yarn/cache/imurmurhash-npm-0.1.4-610c5068a0-7cae75c8cd.zip new file mode 100644 index 00000000000..9ddf4f880fb Binary files /dev/null and b/.yarn/cache/imurmurhash-npm-0.1.4-610c5068a0-7cae75c8cd.zip differ diff --git a/.yarn/cache/indent-string-npm-4.0.0-7b717435b2-824cfb9929.zip b/.yarn/cache/indent-string-npm-4.0.0-7b717435b2-824cfb9929.zip new file mode 100644 index 00000000000..eedfdb0f37a Binary files /dev/null and b/.yarn/cache/indent-string-npm-4.0.0-7b717435b2-824cfb9929.zip differ diff --git a/.yarn/cache/infer-owner-npm-1.0.4-685ac3d2af-181e732764.zip b/.yarn/cache/infer-owner-npm-1.0.4-685ac3d2af-181e732764.zip new file mode 100644 index 00000000000..bdc705082a7 Binary files /dev/null and b/.yarn/cache/infer-owner-npm-1.0.4-685ac3d2af-181e732764.zip differ diff --git a/.yarn/cache/inflight-npm-1.0.6-ccedb4b908-f4f76aa072.zip b/.yarn/cache/inflight-npm-1.0.6-ccedb4b908-f4f76aa072.zip new file mode 100644 index 00000000000..c5a4bb07259 Binary files /dev/null and b/.yarn/cache/inflight-npm-1.0.6-ccedb4b908-f4f76aa072.zip differ diff --git a/.yarn/cache/inherits-npm-2.0.1-0011554c03-6536b93772.zip b/.yarn/cache/inherits-npm-2.0.1-0011554c03-6536b93772.zip new file mode 100644 index 00000000000..eccdc67c535 Binary files /dev/null and b/.yarn/cache/inherits-npm-2.0.1-0011554c03-6536b93772.zip differ diff --git a/.yarn/cache/inherits-npm-2.0.3-401e64b080-78cb8d7d85.zip b/.yarn/cache/inherits-npm-2.0.3-401e64b080-78cb8d7d85.zip new file mode 100644 index 00000000000..6afa4073697 Binary files /dev/null and b/.yarn/cache/inherits-npm-2.0.3-401e64b080-78cb8d7d85.zip differ diff --git a/.yarn/cache/inherits-npm-2.0.4-c66b3957a0-4a48a73384.zip b/.yarn/cache/inherits-npm-2.0.4-c66b3957a0-4a48a73384.zip new file mode 100644 index 00000000000..62c31cb78bb Binary files /dev/null and b/.yarn/cache/inherits-npm-2.0.4-c66b3957a0-4a48a73384.zip differ diff --git a/.yarn/cache/ini-npm-1.3.8-fb5040b4c0-dfd98b0ca3.zip b/.yarn/cache/ini-npm-1.3.8-fb5040b4c0-dfd98b0ca3.zip new file mode 100644 index 00000000000..ee9245b9cd2 Binary files /dev/null and b/.yarn/cache/ini-npm-1.3.8-fb5040b4c0-dfd98b0ca3.zip differ diff --git a/.yarn/cache/ini-npm-2.0.0-28f7426761-e7aadc5fb2.zip b/.yarn/cache/ini-npm-2.0.0-28f7426761-e7aadc5fb2.zip new file mode 100644 index 00000000000..377051d248b Binary files /dev/null and b/.yarn/cache/ini-npm-2.0.0-28f7426761-e7aadc5fb2.zip differ diff --git a/.yarn/cache/inline-source-map-npm-0.6.2-96902459a0-1f7fa2ad17.zip b/.yarn/cache/inline-source-map-npm-0.6.2-96902459a0-1f7fa2ad17.zip new file mode 100644 index 00000000000..43a541d2e85 Binary files /dev/null and b/.yarn/cache/inline-source-map-npm-0.6.2-96902459a0-1f7fa2ad17.zip differ diff --git a/.yarn/cache/inquirer-npm-8.2.0-2bfa19a3d0-861d1a9324.zip b/.yarn/cache/inquirer-npm-8.2.0-2bfa19a3d0-861d1a9324.zip new file mode 100644 index 00000000000..fe72a3386b0 Binary files /dev/null and b/.yarn/cache/inquirer-npm-8.2.0-2bfa19a3d0-861d1a9324.zip differ diff --git a/.yarn/cache/insert-module-globals-npm-7.2.1-28e63ed201-c44de7e802.zip b/.yarn/cache/insert-module-globals-npm-7.2.1-28e63ed201-c44de7e802.zip new file mode 100644 index 00000000000..85c5c67a7a0 Binary files /dev/null and b/.yarn/cache/insert-module-globals-npm-7.2.1-28e63ed201-c44de7e802.zip differ diff --git a/.yarn/cache/internal-slot-npm-1.0.3-9e05eea002-1944f92e98.zip b/.yarn/cache/internal-slot-npm-1.0.3-9e05eea002-1944f92e98.zip new file mode 100644 index 00000000000..18c6edaa912 Binary files /dev/null and b/.yarn/cache/internal-slot-npm-1.0.3-9e05eea002-1944f92e98.zip differ diff --git a/.yarn/cache/interpret-npm-1.4.0-17b4b5b0a4-2e5f51268b.zip b/.yarn/cache/interpret-npm-1.4.0-17b4b5b0a4-2e5f51268b.zip new file mode 100644 index 00000000000..1b6c6b147a5 Binary files /dev/null and b/.yarn/cache/interpret-npm-1.4.0-17b4b5b0a4-2e5f51268b.zip differ diff --git a/.yarn/cache/interpret-npm-2.2.0-3603a544e1-f51efef7cb.zip b/.yarn/cache/interpret-npm-2.2.0-3603a544e1-f51efef7cb.zip new file mode 100644 index 00000000000..20392aec99e Binary files /dev/null and b/.yarn/cache/interpret-npm-2.2.0-3603a544e1-f51efef7cb.zip differ diff --git a/.yarn/cache/ip-npm-1.1.5-af36318aa6-30133981f0.zip b/.yarn/cache/ip-npm-1.1.5-af36318aa6-30133981f0.zip new file mode 100644 index 00000000000..b0bbc7922fe Binary files /dev/null and b/.yarn/cache/ip-npm-1.1.5-af36318aa6-30133981f0.zip differ diff --git a/.yarn/cache/ip-regex-npm-4.3.0-4ac12c6be9-7ff904b891.zip b/.yarn/cache/ip-regex-npm-4.3.0-4ac12c6be9-7ff904b891.zip new file mode 100644 index 00000000000..57f27084cdd Binary files /dev/null and b/.yarn/cache/ip-regex-npm-4.3.0-4ac12c6be9-7ff904b891.zip differ diff --git a/.yarn/cache/is-arguments-npm-1.1.1-eff4f6d4d7-7f02700ec2.zip b/.yarn/cache/is-arguments-npm-1.1.1-eff4f6d4d7-7f02700ec2.zip new file mode 100644 index 00000000000..9b956d8699f Binary files /dev/null and b/.yarn/cache/is-arguments-npm-1.1.1-eff4f6d4d7-7f02700ec2.zip differ diff --git a/.yarn/cache/is-arrayish-npm-0.2.1-23927dfb15-eef4417e3c.zip b/.yarn/cache/is-arrayish-npm-0.2.1-23927dfb15-eef4417e3c.zip new file mode 100644 index 00000000000..8d3275c2113 Binary files /dev/null and b/.yarn/cache/is-arrayish-npm-0.2.1-23927dfb15-eef4417e3c.zip differ diff --git a/.yarn/cache/is-arrayish-npm-0.3.2-f856180f79-977e64f54d.zip b/.yarn/cache/is-arrayish-npm-0.3.2-f856180f79-977e64f54d.zip new file mode 100644 index 00000000000..593895a1629 Binary files /dev/null and b/.yarn/cache/is-arrayish-npm-0.3.2-f856180f79-977e64f54d.zip differ diff --git a/.yarn/cache/is-bigint-npm-1.0.4-31c2eecbc9-c56edfe09b.zip b/.yarn/cache/is-bigint-npm-1.0.4-31c2eecbc9-c56edfe09b.zip new file mode 100644 index 00000000000..5282dfa9189 Binary files /dev/null and b/.yarn/cache/is-bigint-npm-1.0.4-31c2eecbc9-c56edfe09b.zip differ diff --git a/.yarn/cache/is-binary-path-npm-2.1.0-e61d46f557-84192eb88c.zip b/.yarn/cache/is-binary-path-npm-2.1.0-e61d46f557-84192eb88c.zip new file mode 100644 index 00000000000..b509d00f589 Binary files /dev/null and b/.yarn/cache/is-binary-path-npm-2.1.0-e61d46f557-84192eb88c.zip differ diff --git a/.yarn/cache/is-boolean-object-npm-1.1.2-ecbd575e6a-c03b23dbaa.zip b/.yarn/cache/is-boolean-object-npm-1.1.2-ecbd575e6a-c03b23dbaa.zip new file mode 100644 index 00000000000..7a1ae53d09d Binary files /dev/null and b/.yarn/cache/is-boolean-object-npm-1.1.2-ecbd575e6a-c03b23dbaa.zip differ diff --git a/.yarn/cache/is-buffer-npm-1.1.6-08199d9ccc-4a186d995d.zip b/.yarn/cache/is-buffer-npm-1.1.6-08199d9ccc-4a186d995d.zip new file mode 100644 index 00000000000..fbd49828982 Binary files /dev/null and b/.yarn/cache/is-buffer-npm-1.1.6-08199d9ccc-4a186d995d.zip differ diff --git a/.yarn/cache/is-callable-npm-1.2.4-03fc17459c-1a28d57dc4.zip b/.yarn/cache/is-callable-npm-1.2.4-03fc17459c-1a28d57dc4.zip new file mode 100644 index 00000000000..a5b40696917 Binary files /dev/null and b/.yarn/cache/is-callable-npm-1.2.4-03fc17459c-1a28d57dc4.zip differ diff --git a/.yarn/cache/is-ci-npm-2.0.0-8662a0f445-77b8690575.zip b/.yarn/cache/is-ci-npm-2.0.0-8662a0f445-77b8690575.zip new file mode 100644 index 00000000000..c45432484cd Binary files /dev/null and b/.yarn/cache/is-ci-npm-2.0.0-8662a0f445-77b8690575.zip differ diff --git a/.yarn/cache/is-core-module-npm-2.8.1-ce21740d1b-418b7bc107.zip b/.yarn/cache/is-core-module-npm-2.8.1-ce21740d1b-418b7bc107.zip new file mode 100644 index 00000000000..578d1513265 Binary files /dev/null and b/.yarn/cache/is-core-module-npm-2.8.1-ce21740d1b-418b7bc107.zip differ diff --git a/.yarn/cache/is-date-object-npm-1.0.5-88f3d08b5e-baa9077cdf.zip b/.yarn/cache/is-date-object-npm-1.0.5-88f3d08b5e-baa9077cdf.zip new file mode 100644 index 00000000000..3dbce36af99 Binary files /dev/null and b/.yarn/cache/is-date-object-npm-1.0.5-88f3d08b5e-baa9077cdf.zip differ diff --git a/.yarn/cache/is-docker-npm-2.2.1-3f18a53aff-3fef7ddbf0.zip b/.yarn/cache/is-docker-npm-2.2.1-3f18a53aff-3fef7ddbf0.zip new file mode 100644 index 00000000000..70c44640db2 Binary files /dev/null and b/.yarn/cache/is-docker-npm-2.2.1-3f18a53aff-3fef7ddbf0.zip differ diff --git a/.yarn/cache/is-extglob-npm-2.1.1-0870ea68b5-df033653d0.zip b/.yarn/cache/is-extglob-npm-2.1.1-0870ea68b5-df033653d0.zip new file mode 100644 index 00000000000..0acbc56e225 Binary files /dev/null and b/.yarn/cache/is-extglob-npm-2.1.1-0870ea68b5-df033653d0.zip differ diff --git a/.yarn/cache/is-fullwidth-code-point-npm-1.0.0-0e436ba1ef-4d46a7465a.zip b/.yarn/cache/is-fullwidth-code-point-npm-1.0.0-0e436ba1ef-4d46a7465a.zip new file mode 100644 index 00000000000..6d63e1f5ebf Binary files /dev/null and b/.yarn/cache/is-fullwidth-code-point-npm-1.0.0-0e436ba1ef-4d46a7465a.zip differ diff --git a/.yarn/cache/is-fullwidth-code-point-npm-2.0.0-507f56ec71-eef9c6e15f.zip b/.yarn/cache/is-fullwidth-code-point-npm-2.0.0-507f56ec71-eef9c6e15f.zip new file mode 100644 index 00000000000..56f17d3988f Binary files /dev/null and b/.yarn/cache/is-fullwidth-code-point-npm-2.0.0-507f56ec71-eef9c6e15f.zip differ diff --git a/.yarn/cache/is-fullwidth-code-point-npm-3.0.0-1ecf4ebee5-44a30c2945.zip b/.yarn/cache/is-fullwidth-code-point-npm-3.0.0-1ecf4ebee5-44a30c2945.zip new file mode 100644 index 00000000000..dccc80a970b Binary files /dev/null and b/.yarn/cache/is-fullwidth-code-point-npm-3.0.0-1ecf4ebee5-44a30c2945.zip differ diff --git a/.yarn/cache/is-generator-function-npm-1.0.10-1d0f3809ef-d54644e7db.zip b/.yarn/cache/is-generator-function-npm-1.0.10-1d0f3809ef-d54644e7db.zip new file mode 100644 index 00000000000..6045379e638 Binary files /dev/null and b/.yarn/cache/is-generator-function-npm-1.0.10-1d0f3809ef-d54644e7db.zip differ diff --git a/.yarn/cache/is-glob-npm-4.0.3-cb87bf1bdb-d381c1319f.zip b/.yarn/cache/is-glob-npm-4.0.3-cb87bf1bdb-d381c1319f.zip new file mode 100644 index 00000000000..52274ed2541 Binary files /dev/null and b/.yarn/cache/is-glob-npm-4.0.3-cb87bf1bdb-d381c1319f.zip differ diff --git a/.yarn/cache/is-installed-globally-npm-0.4.0-a30dd056c7-3359840d59.zip b/.yarn/cache/is-installed-globally-npm-0.4.0-a30dd056c7-3359840d59.zip new file mode 100644 index 00000000000..f94dbc064b9 Binary files /dev/null and b/.yarn/cache/is-installed-globally-npm-0.4.0-a30dd056c7-3359840d59.zip differ diff --git a/.yarn/cache/is-interactive-npm-1.0.0-7ff7c6e04a-824808776e.zip b/.yarn/cache/is-interactive-npm-1.0.0-7ff7c6e04a-824808776e.zip new file mode 100644 index 00000000000..0c1f90e041f Binary files /dev/null and b/.yarn/cache/is-interactive-npm-1.0.0-7ff7c6e04a-824808776e.zip differ diff --git a/.yarn/cache/is-ip-npm-3.1.0-7b8bc9330c-da2c2b2824.zip b/.yarn/cache/is-ip-npm-3.1.0-7b8bc9330c-da2c2b2824.zip new file mode 100644 index 00000000000..c8c5b4a91aa Binary files /dev/null and b/.yarn/cache/is-ip-npm-3.1.0-7b8bc9330c-da2c2b2824.zip differ diff --git a/.yarn/cache/is-lambda-npm-1.0.1-7ab55bc8a8-93a32f0194.zip b/.yarn/cache/is-lambda-npm-1.0.1-7ab55bc8a8-93a32f0194.zip new file mode 100644 index 00000000000..f981b1bea6c Binary files /dev/null and b/.yarn/cache/is-lambda-npm-1.0.1-7ab55bc8a8-93a32f0194.zip differ diff --git a/.yarn/cache/is-nan-npm-1.3.2-a087d31a28-5dfadcef6a.zip b/.yarn/cache/is-nan-npm-1.3.2-a087d31a28-5dfadcef6a.zip new file mode 100644 index 00000000000..7b75a28659b Binary files /dev/null and b/.yarn/cache/is-nan-npm-1.3.2-a087d31a28-5dfadcef6a.zip differ diff --git a/.yarn/cache/is-negative-zero-npm-2.0.1-d8f3dbcfe1-a46f2e0cb5.zip b/.yarn/cache/is-negative-zero-npm-2.0.1-d8f3dbcfe1-a46f2e0cb5.zip new file mode 100644 index 00000000000..f7c9fb7ab5a Binary files /dev/null and b/.yarn/cache/is-negative-zero-npm-2.0.1-d8f3dbcfe1-a46f2e0cb5.zip differ diff --git a/.yarn/cache/is-npm-npm-5.0.0-2758bcd54b-9baff02b0c.zip b/.yarn/cache/is-npm-npm-5.0.0-2758bcd54b-9baff02b0c.zip new file mode 100644 index 00000000000..e09ab33f371 Binary files /dev/null and b/.yarn/cache/is-npm-npm-5.0.0-2758bcd54b-9baff02b0c.zip differ diff --git a/.yarn/cache/is-number-npm-7.0.0-060086935c-456ac6f8e0.zip b/.yarn/cache/is-number-npm-7.0.0-060086935c-456ac6f8e0.zip new file mode 100644 index 00000000000..e4ae0485760 Binary files /dev/null and b/.yarn/cache/is-number-npm-7.0.0-060086935c-456ac6f8e0.zip differ diff --git a/.yarn/cache/is-number-object-npm-1.0.6-88e8d0e936-c697704e8f.zip b/.yarn/cache/is-number-object-npm-1.0.6-88e8d0e936-c697704e8f.zip new file mode 100644 index 00000000000..6e41d74766c Binary files /dev/null and b/.yarn/cache/is-number-object-npm-1.0.6-88e8d0e936-c697704e8f.zip differ diff --git a/.yarn/cache/is-obj-npm-2.0.0-3d95e053f4-c9916ac8f4.zip b/.yarn/cache/is-obj-npm-2.0.0-3d95e053f4-c9916ac8f4.zip new file mode 100644 index 00000000000..a7f0e896299 Binary files /dev/null and b/.yarn/cache/is-obj-npm-2.0.0-3d95e053f4-c9916ac8f4.zip differ diff --git a/.yarn/cache/is-path-inside-npm-3.0.3-2ea0ef44fd-abd50f0618.zip b/.yarn/cache/is-path-inside-npm-3.0.3-2ea0ef44fd-abd50f0618.zip new file mode 100644 index 00000000000..27f29d70bee Binary files /dev/null and b/.yarn/cache/is-path-inside-npm-3.0.3-2ea0ef44fd-abd50f0618.zip differ diff --git a/.yarn/cache/is-plain-obj-npm-1.1.0-1046f64c0b-0ee0480779.zip b/.yarn/cache/is-plain-obj-npm-1.1.0-1046f64c0b-0ee0480779.zip new file mode 100644 index 00000000000..8b9e598333c Binary files /dev/null and b/.yarn/cache/is-plain-obj-npm-1.1.0-1046f64c0b-0ee0480779.zip differ diff --git a/.yarn/cache/is-plain-obj-npm-2.1.0-8dffd7ae9c-cec9100678.zip b/.yarn/cache/is-plain-obj-npm-2.1.0-8dffd7ae9c-cec9100678.zip new file mode 100644 index 00000000000..49504a5bbc5 Binary files /dev/null and b/.yarn/cache/is-plain-obj-npm-2.1.0-8dffd7ae9c-cec9100678.zip differ diff --git a/.yarn/cache/is-plain-object-npm-2.0.4-da3265d804-2a401140cf.zip b/.yarn/cache/is-plain-object-npm-2.0.4-da3265d804-2a401140cf.zip new file mode 100644 index 00000000000..8b68965a0f0 Binary files /dev/null and b/.yarn/cache/is-plain-object-npm-2.0.4-da3265d804-2a401140cf.zip differ diff --git a/.yarn/cache/is-plain-object-npm-5.0.0-285b70faa3-e32d27061e.zip b/.yarn/cache/is-plain-object-npm-5.0.0-285b70faa3-e32d27061e.zip new file mode 100644 index 00000000000..fd9d03a7540 Binary files /dev/null and b/.yarn/cache/is-plain-object-npm-5.0.0-285b70faa3-e32d27061e.zip differ diff --git a/.yarn/cache/is-regex-npm-1.1.4-cca193ef11-362399b335.zip b/.yarn/cache/is-regex-npm-1.1.4-cca193ef11-362399b335.zip new file mode 100644 index 00000000000..41d26b8c736 Binary files /dev/null and b/.yarn/cache/is-regex-npm-1.1.4-cca193ef11-362399b335.zip differ diff --git a/.yarn/cache/is-retry-allowed-npm-1.2.0-730be11f6c-50d700a89a.zip b/.yarn/cache/is-retry-allowed-npm-1.2.0-730be11f6c-50d700a89a.zip new file mode 100644 index 00000000000..ee51d6e352a Binary files /dev/null and b/.yarn/cache/is-retry-allowed-npm-1.2.0-730be11f6c-50d700a89a.zip differ diff --git a/.yarn/cache/is-scoped-npm-2.1.0-7710eece3d-bc4726ec6c.zip b/.yarn/cache/is-scoped-npm-2.1.0-7710eece3d-bc4726ec6c.zip new file mode 100644 index 00000000000..2710015bd07 Binary files /dev/null and b/.yarn/cache/is-scoped-npm-2.1.0-7710eece3d-bc4726ec6c.zip differ diff --git a/.yarn/cache/is-shared-array-buffer-npm-1.0.1-84bc270861-2ffb92533e.zip b/.yarn/cache/is-shared-array-buffer-npm-1.0.1-84bc270861-2ffb92533e.zip new file mode 100644 index 00000000000..0f589025b38 Binary files /dev/null and b/.yarn/cache/is-shared-array-buffer-npm-1.0.1-84bc270861-2ffb92533e.zip differ diff --git a/.yarn/cache/is-stream-npm-1.1.0-818ecbf6bb-063c6bec9d.zip b/.yarn/cache/is-stream-npm-1.1.0-818ecbf6bb-063c6bec9d.zip new file mode 100644 index 00000000000..6695e77d4a2 Binary files /dev/null and b/.yarn/cache/is-stream-npm-1.1.0-818ecbf6bb-063c6bec9d.zip differ diff --git a/.yarn/cache/is-stream-npm-2.0.1-c802db55e7-b8e05ccdf9.zip b/.yarn/cache/is-stream-npm-2.0.1-c802db55e7-b8e05ccdf9.zip new file mode 100644 index 00000000000..c5699a4eeb7 Binary files /dev/null and b/.yarn/cache/is-stream-npm-2.0.1-c802db55e7-b8e05ccdf9.zip differ diff --git a/.yarn/cache/is-string-npm-1.0.7-9f7066daed-323b3d0462.zip b/.yarn/cache/is-string-npm-1.0.7-9f7066daed-323b3d0462.zip new file mode 100644 index 00000000000..21039f90178 Binary files /dev/null and b/.yarn/cache/is-string-npm-1.0.7-9f7066daed-323b3d0462.zip differ diff --git a/.yarn/cache/is-symbol-npm-1.0.4-eb9baac703-92805812ef.zip b/.yarn/cache/is-symbol-npm-1.0.4-eb9baac703-92805812ef.zip new file mode 100644 index 00000000000..aa6f763ead9 Binary files /dev/null and b/.yarn/cache/is-symbol-npm-1.0.4-eb9baac703-92805812ef.zip differ diff --git a/.yarn/cache/is-text-path-npm-1.0.1-92c78fe58d-fb5d78752c.zip b/.yarn/cache/is-text-path-npm-1.0.1-92c78fe58d-fb5d78752c.zip new file mode 100644 index 00000000000..03514391a12 Binary files /dev/null and b/.yarn/cache/is-text-path-npm-1.0.1-92c78fe58d-fb5d78752c.zip differ diff --git a/.yarn/cache/is-typed-array-npm-1.1.8-147f090d0d-aa0f9f0716.zip b/.yarn/cache/is-typed-array-npm-1.1.8-147f090d0d-aa0f9f0716.zip new file mode 100644 index 00000000000..275bf5b2ee6 Binary files /dev/null and b/.yarn/cache/is-typed-array-npm-1.1.8-147f090d0d-aa0f9f0716.zip differ diff --git a/.yarn/cache/is-typedarray-npm-1.0.0-bbd99de5b6-3508c6cd0a.zip b/.yarn/cache/is-typedarray-npm-1.0.0-bbd99de5b6-3508c6cd0a.zip new file mode 100644 index 00000000000..09d0014a470 Binary files /dev/null and b/.yarn/cache/is-typedarray-npm-1.0.0-bbd99de5b6-3508c6cd0a.zip differ diff --git a/.yarn/cache/is-unicode-supported-npm-0.1.0-0833e1bbfb-a2aab86ee7.zip b/.yarn/cache/is-unicode-supported-npm-0.1.0-0833e1bbfb-a2aab86ee7.zip new file mode 100644 index 00000000000..7425daa3669 Binary files /dev/null and b/.yarn/cache/is-unicode-supported-npm-0.1.0-0833e1bbfb-a2aab86ee7.zip differ diff --git a/.yarn/cache/is-utf8-npm-0.2.1-46ab364e2f-167ccd2be8.zip b/.yarn/cache/is-utf8-npm-0.2.1-46ab364e2f-167ccd2be8.zip new file mode 100644 index 00000000000..952563a49a9 Binary files /dev/null and b/.yarn/cache/is-utf8-npm-0.2.1-46ab364e2f-167ccd2be8.zip differ diff --git a/.yarn/cache/is-weakref-npm-1.0.1-152a166933-fdafb7b955.zip b/.yarn/cache/is-weakref-npm-1.0.1-152a166933-fdafb7b955.zip new file mode 100644 index 00000000000..eab2ea13a8b Binary files /dev/null and b/.yarn/cache/is-weakref-npm-1.0.1-152a166933-fdafb7b955.zip differ diff --git a/.yarn/cache/is-windows-npm-1.0.2-898cd6f3d7-438b7e5265.zip b/.yarn/cache/is-windows-npm-1.0.2-898cd6f3d7-438b7e5265.zip new file mode 100644 index 00000000000..927b3c5cead Binary files /dev/null and b/.yarn/cache/is-windows-npm-1.0.2-898cd6f3d7-438b7e5265.zip differ diff --git a/.yarn/cache/is-wsl-npm-2.2.0-2ba10d6393-20849846ae.zip b/.yarn/cache/is-wsl-npm-2.2.0-2ba10d6393-20849846ae.zip new file mode 100644 index 00000000000..eaddb88d411 Binary files /dev/null and b/.yarn/cache/is-wsl-npm-2.2.0-2ba10d6393-20849846ae.zip differ diff --git a/.yarn/cache/is-yarn-global-npm-0.3.0-18cad00879-bca013d65f.zip b/.yarn/cache/is-yarn-global-npm-0.3.0-18cad00879-bca013d65f.zip new file mode 100644 index 00000000000..2eadd438a21 Binary files /dev/null and b/.yarn/cache/is-yarn-global-npm-0.3.0-18cad00879-bca013d65f.zip differ diff --git a/.yarn/cache/isarray-npm-0.0.1-92e37e0a70-49191f1425.zip b/.yarn/cache/isarray-npm-0.0.1-92e37e0a70-49191f1425.zip new file mode 100644 index 00000000000..4c3f427b3eb Binary files /dev/null and b/.yarn/cache/isarray-npm-0.0.1-92e37e0a70-49191f1425.zip differ diff --git a/.yarn/cache/isarray-npm-1.0.0-db4f547720-f032df8e02.zip b/.yarn/cache/isarray-npm-1.0.0-db4f547720-f032df8e02.zip new file mode 100644 index 00000000000..67c393dc1a3 Binary files /dev/null and b/.yarn/cache/isarray-npm-1.0.0-db4f547720-f032df8e02.zip differ diff --git a/.yarn/cache/isbinaryfile-npm-4.0.8-62c71dd57b-606e3bb648.zip b/.yarn/cache/isbinaryfile-npm-4.0.8-62c71dd57b-606e3bb648.zip new file mode 100644 index 00000000000..1108e943918 Binary files /dev/null and b/.yarn/cache/isbinaryfile-npm-4.0.8-62c71dd57b-606e3bb648.zip differ diff --git a/.yarn/cache/isexe-npm-2.0.0-b58870bd2e-26bf6c5480.zip b/.yarn/cache/isexe-npm-2.0.0-b58870bd2e-26bf6c5480.zip new file mode 100644 index 00000000000..077597d686e Binary files /dev/null and b/.yarn/cache/isexe-npm-2.0.0-b58870bd2e-26bf6c5480.zip differ diff --git a/.yarn/cache/isobject-npm-3.0.1-8145901fd2-db85c4c970.zip b/.yarn/cache/isobject-npm-3.0.1-8145901fd2-db85c4c970.zip new file mode 100644 index 00000000000..214104c89e6 Binary files /dev/null and b/.yarn/cache/isobject-npm-3.0.1-8145901fd2-db85c4c970.zip differ diff --git a/.yarn/cache/isomorphic-ws-npm-4.0.1-aa39192848-d7190eadef.zip b/.yarn/cache/isomorphic-ws-npm-4.0.1-aa39192848-d7190eadef.zip new file mode 100644 index 00000000000..a082a905883 Binary files /dev/null and b/.yarn/cache/isomorphic-ws-npm-4.0.1-aa39192848-d7190eadef.zip differ diff --git a/.yarn/cache/isstream-npm-0.1.2-8581c75385-1eb2fe63a7.zip b/.yarn/cache/isstream-npm-0.1.2-8581c75385-1eb2fe63a7.zip new file mode 100644 index 00000000000..7c1a1e17180 Binary files /dev/null and b/.yarn/cache/isstream-npm-0.1.2-8581c75385-1eb2fe63a7.zip differ diff --git a/.yarn/cache/istanbul-lib-coverage-npm-3.2.0-93f84b2c8c-a2a545033b.zip b/.yarn/cache/istanbul-lib-coverage-npm-3.2.0-93f84b2c8c-a2a545033b.zip new file mode 100644 index 00000000000..89e143d50e2 Binary files /dev/null and b/.yarn/cache/istanbul-lib-coverage-npm-3.2.0-93f84b2c8c-a2a545033b.zip differ diff --git a/.yarn/cache/istanbul-lib-hook-npm-3.0.0-be73f95173-ac4d0a0751.zip b/.yarn/cache/istanbul-lib-hook-npm-3.0.0-be73f95173-ac4d0a0751.zip new file mode 100644 index 00000000000..e116b337c65 Binary files /dev/null and b/.yarn/cache/istanbul-lib-hook-npm-3.0.0-be73f95173-ac4d0a0751.zip differ diff --git a/.yarn/cache/istanbul-lib-instrument-npm-4.0.3-4d4c2263f8-fa1171d302.zip b/.yarn/cache/istanbul-lib-instrument-npm-4.0.3-4d4c2263f8-fa1171d302.zip new file mode 100644 index 00000000000..5bc5a757878 Binary files /dev/null and b/.yarn/cache/istanbul-lib-instrument-npm-4.0.3-4d4c2263f8-fa1171d302.zip differ diff --git a/.yarn/cache/istanbul-lib-processinfo-npm-2.0.2-74916fa6cb-400bd0b25b.zip b/.yarn/cache/istanbul-lib-processinfo-npm-2.0.2-74916fa6cb-400bd0b25b.zip new file mode 100644 index 00000000000..816ab650bfe Binary files /dev/null and b/.yarn/cache/istanbul-lib-processinfo-npm-2.0.2-74916fa6cb-400bd0b25b.zip differ diff --git a/.yarn/cache/istanbul-lib-report-npm-3.0.0-660f97340a-3f29eb3f53.zip b/.yarn/cache/istanbul-lib-report-npm-3.0.0-660f97340a-3f29eb3f53.zip new file mode 100644 index 00000000000..90bcd0adf54 Binary files /dev/null and b/.yarn/cache/istanbul-lib-report-npm-3.0.0-660f97340a-3f29eb3f53.zip differ diff --git a/.yarn/cache/istanbul-lib-source-maps-npm-4.0.1-af0f859df7-21ad3df45d.zip b/.yarn/cache/istanbul-lib-source-maps-npm-4.0.1-af0f859df7-21ad3df45d.zip new file mode 100644 index 00000000000..344cd7cdbb1 Binary files /dev/null and b/.yarn/cache/istanbul-lib-source-maps-npm-4.0.1-af0f859df7-21ad3df45d.zip differ diff --git a/.yarn/cache/istanbul-reports-npm-3.0.5-2a13f8a7b1-b167411c4c.zip b/.yarn/cache/istanbul-reports-npm-3.0.5-2a13f8a7b1-b167411c4c.zip new file mode 100644 index 00000000000..451649f755c Binary files /dev/null and b/.yarn/cache/istanbul-reports-npm-3.0.5-2a13f8a7b1-b167411c4c.zip differ diff --git a/.yarn/cache/jake-npm-10.8.2-e211473cb9-b604c51863.zip b/.yarn/cache/jake-npm-10.8.2-e211473cb9-b604c51863.zip new file mode 100644 index 00000000000..bbce54668c6 Binary files /dev/null and b/.yarn/cache/jake-npm-10.8.2-e211473cb9-b604c51863.zip differ diff --git a/.yarn/cache/javascript-natural-sort-npm-0.7.1-9018625996-161e2c512c.zip b/.yarn/cache/javascript-natural-sort-npm-0.7.1-9018625996-161e2c512c.zip new file mode 100644 index 00000000000..533471e9dd3 Binary files /dev/null and b/.yarn/cache/javascript-natural-sort-npm-0.7.1-9018625996-161e2c512c.zip differ diff --git a/.yarn/cache/jayson-npm-2.1.2-f9240aab4b-7d66d37ea0.zip b/.yarn/cache/jayson-npm-2.1.2-f9240aab4b-7d66d37ea0.zip new file mode 100644 index 00000000000..3db38df3a47 Binary files /dev/null and b/.yarn/cache/jayson-npm-2.1.2-f9240aab4b-7d66d37ea0.zip differ diff --git a/.yarn/cache/jayson-npm-3.6.5-2fd01a647a-dde536e720.zip b/.yarn/cache/jayson-npm-3.6.5-2fd01a647a-dde536e720.zip new file mode 100644 index 00000000000..10d31441679 Binary files /dev/null and b/.yarn/cache/jayson-npm-3.6.5-2fd01a647a-dde536e720.zip differ diff --git a/.yarn/cache/jest-diff-npm-27.3.1-c347dd1e5a-49231a4ac4.zip b/.yarn/cache/jest-diff-npm-27.3.1-c347dd1e5a-49231a4ac4.zip new file mode 100644 index 00000000000..e2f6bf88b92 Binary files /dev/null and b/.yarn/cache/jest-diff-npm-27.3.1-c347dd1e5a-49231a4ac4.zip differ diff --git a/.yarn/cache/jest-get-type-npm-27.3.1-fdb27a0157-b0b8db1d77.zip b/.yarn/cache/jest-get-type-npm-27.3.1-fdb27a0157-b0b8db1d77.zip new file mode 100644 index 00000000000..40b53ed4bb4 Binary files /dev/null and b/.yarn/cache/jest-get-type-npm-27.3.1-fdb27a0157-b0b8db1d77.zip differ diff --git a/.yarn/cache/jest-matcher-utils-npm-27.3.1-8eb9f8e92d-118c428b55.zip b/.yarn/cache/jest-matcher-utils-npm-27.3.1-8eb9f8e92d-118c428b55.zip new file mode 100644 index 00000000000..cf49f3d4f3f Binary files /dev/null and b/.yarn/cache/jest-matcher-utils-npm-27.3.1-8eb9f8e92d-118c428b55.zip differ diff --git a/.yarn/cache/jest-message-util-npm-27.3.1-0d163b84de-2d10734765.zip b/.yarn/cache/jest-message-util-npm-27.3.1-0d163b84de-2d10734765.zip new file mode 100644 index 00000000000..5afee6366ca Binary files /dev/null and b/.yarn/cache/jest-message-util-npm-27.3.1-0d163b84de-2d10734765.zip differ diff --git a/.yarn/cache/jest-regex-util-npm-27.0.6-02fca95995-4d613b00f2.zip b/.yarn/cache/jest-regex-util-npm-27.0.6-02fca95995-4d613b00f2.zip new file mode 100644 index 00000000000..f29bdd76c7b Binary files /dev/null and b/.yarn/cache/jest-regex-util-npm-27.0.6-02fca95995-4d613b00f2.zip differ diff --git a/.yarn/cache/jest-worker-npm-27.5.1-1c110b5894-98cd68b696.zip b/.yarn/cache/jest-worker-npm-27.5.1-1c110b5894-98cd68b696.zip new file mode 100644 index 00000000000..10e0b5b9643 Binary files /dev/null and b/.yarn/cache/jest-worker-npm-27.5.1-1c110b5894-98cd68b696.zip differ diff --git a/.yarn/cache/jmespath-npm-0.15.0-df80ed6dd1-353bb9e69c.zip b/.yarn/cache/jmespath-npm-0.15.0-df80ed6dd1-353bb9e69c.zip new file mode 100644 index 00000000000..3f85bba8b31 Binary files /dev/null and b/.yarn/cache/jmespath-npm-0.15.0-df80ed6dd1-353bb9e69c.zip differ diff --git a/.yarn/cache/jmespath-npm-0.16.0-d47535c65a-2d602493a1.zip b/.yarn/cache/jmespath-npm-0.16.0-d47535c65a-2d602493a1.zip new file mode 100644 index 00000000000..b78e5e14e00 Binary files /dev/null and b/.yarn/cache/jmespath-npm-0.16.0-d47535c65a-2d602493a1.zip differ diff --git a/.yarn/cache/joycon-npm-2.2.5-fff23ab519-930bb748c0.zip b/.yarn/cache/joycon-npm-2.2.5-fff23ab519-930bb748c0.zip new file mode 100644 index 00000000000..a73836079da Binary files /dev/null and b/.yarn/cache/joycon-npm-2.2.5-fff23ab519-930bb748c0.zip differ diff --git a/.yarn/cache/js-base64-npm-2.6.4-569350f803-5f4084078d.zip b/.yarn/cache/js-base64-npm-2.6.4-569350f803-5f4084078d.zip new file mode 100644 index 00000000000..074d3a2dfde Binary files /dev/null and b/.yarn/cache/js-base64-npm-2.6.4-569350f803-5f4084078d.zip differ diff --git a/.yarn/cache/js-merkle-npm-0.1.5-d759dbfead-1b19f50c06.zip b/.yarn/cache/js-merkle-npm-0.1.5-d759dbfead-1b19f50c06.zip new file mode 100644 index 00000000000..f9a3023a27e Binary files /dev/null and b/.yarn/cache/js-merkle-npm-0.1.5-d759dbfead-1b19f50c06.zip differ diff --git a/.yarn/cache/js-tokens-npm-4.0.0-0ac852e9e2-8a95213a5a.zip b/.yarn/cache/js-tokens-npm-4.0.0-0ac852e9e2-8a95213a5a.zip new file mode 100644 index 00000000000..8ffd9d48a5c Binary files /dev/null and b/.yarn/cache/js-tokens-npm-4.0.0-0ac852e9e2-8a95213a5a.zip differ diff --git a/.yarn/cache/js-yaml-npm-3.13.1-3a28ff3b75-7511b764ab.zip b/.yarn/cache/js-yaml-npm-3.13.1-3a28ff3b75-7511b764ab.zip new file mode 100644 index 00000000000..b5e64466046 Binary files /dev/null and b/.yarn/cache/js-yaml-npm-3.13.1-3a28ff3b75-7511b764ab.zip differ diff --git a/.yarn/cache/js-yaml-npm-3.14.1-b968c6095e-bef146085f.zip b/.yarn/cache/js-yaml-npm-3.14.1-b968c6095e-bef146085f.zip new file mode 100644 index 00000000000..31ddcc7f72e Binary files /dev/null and b/.yarn/cache/js-yaml-npm-3.14.1-b968c6095e-bef146085f.zip differ diff --git a/.yarn/cache/js-yaml-npm-4.1.0-3606f32312-c7830dfd45.zip b/.yarn/cache/js-yaml-npm-4.1.0-3606f32312-c7830dfd45.zip new file mode 100644 index 00000000000..659c85d097f Binary files /dev/null and b/.yarn/cache/js-yaml-npm-4.1.0-3606f32312-c7830dfd45.zip differ diff --git a/.yarn/cache/jsbn-npm-0.1.1-0eb7132404-e5ff29c1b8.zip b/.yarn/cache/jsbn-npm-0.1.1-0eb7132404-e5ff29c1b8.zip new file mode 100644 index 00000000000..8ec54a26c59 Binary files /dev/null and b/.yarn/cache/jsbn-npm-0.1.1-0eb7132404-e5ff29c1b8.zip differ diff --git a/.yarn/cache/jsdoctypeparser-npm-6.1.0-069387bc3e-14a0ef3671.zip b/.yarn/cache/jsdoctypeparser-npm-6.1.0-069387bc3e-14a0ef3671.zip new file mode 100644 index 00000000000..086a5f6fda1 Binary files /dev/null and b/.yarn/cache/jsdoctypeparser-npm-6.1.0-069387bc3e-14a0ef3671.zip differ diff --git a/.yarn/cache/jsesc-npm-0.5.0-6827074492-b8b44cbfc9.zip b/.yarn/cache/jsesc-npm-0.5.0-6827074492-b8b44cbfc9.zip new file mode 100644 index 00000000000..00aca139dd6 Binary files /dev/null and b/.yarn/cache/jsesc-npm-0.5.0-6827074492-b8b44cbfc9.zip differ diff --git a/.yarn/cache/jsesc-npm-2.5.2-c5acb78804-4dc1907711.zip b/.yarn/cache/jsesc-npm-2.5.2-c5acb78804-4dc1907711.zip new file mode 100644 index 00000000000..08cc200f93a Binary files /dev/null and b/.yarn/cache/jsesc-npm-2.5.2-c5acb78804-4dc1907711.zip differ diff --git a/.yarn/cache/json-buffer-npm-3.0.0-21c267a314-0cecacb802.zip b/.yarn/cache/json-buffer-npm-3.0.0-21c267a314-0cecacb802.zip new file mode 100644 index 00000000000..e4303c62930 Binary files /dev/null and b/.yarn/cache/json-buffer-npm-3.0.0-21c267a314-0cecacb802.zip differ diff --git a/.yarn/cache/json-parse-better-errors-npm-1.0.2-7f37637d19-ff2b5ba2a7.zip b/.yarn/cache/json-parse-better-errors-npm-1.0.2-7f37637d19-ff2b5ba2a7.zip new file mode 100644 index 00000000000..3892f16875b Binary files /dev/null and b/.yarn/cache/json-parse-better-errors-npm-1.0.2-7f37637d19-ff2b5ba2a7.zip differ diff --git a/.yarn/cache/json-parse-even-better-errors-npm-2.3.1-144d62256e-798ed4cf33.zip b/.yarn/cache/json-parse-even-better-errors-npm-2.3.1-144d62256e-798ed4cf33.zip new file mode 100644 index 00000000000..96a83fe3c51 Binary files /dev/null and b/.yarn/cache/json-parse-even-better-errors-npm-2.3.1-144d62256e-798ed4cf33.zip differ diff --git a/.yarn/cache/json-pointer-npm-0.6.1-d72965882f-882b4b24b5.zip b/.yarn/cache/json-pointer-npm-0.6.1-d72965882f-882b4b24b5.zip new file mode 100644 index 00000000000..43836675838 Binary files /dev/null and b/.yarn/cache/json-pointer-npm-0.6.1-d72965882f-882b4b24b5.zip differ diff --git a/.yarn/cache/json-schema-diff-validator-npm-0.4.1-a98b53360c-d75b7f5540.zip b/.yarn/cache/json-schema-diff-validator-npm-0.4.1-a98b53360c-d75b7f5540.zip new file mode 100644 index 00000000000..9c36912021d Binary files /dev/null and b/.yarn/cache/json-schema-diff-validator-npm-0.4.1-a98b53360c-d75b7f5540.zip differ diff --git a/.yarn/cache/json-schema-npm-0.2.3-018ee3dfc9-bbc2070988.zip b/.yarn/cache/json-schema-npm-0.2.3-018ee3dfc9-bbc2070988.zip new file mode 100644 index 00000000000..df27d8b4691 Binary files /dev/null and b/.yarn/cache/json-schema-npm-0.2.3-018ee3dfc9-bbc2070988.zip differ diff --git a/.yarn/cache/json-schema-ref-parser-npm-7.1.4-948f9e347a-690252bb1e.zip b/.yarn/cache/json-schema-ref-parser-npm-7.1.4-948f9e347a-690252bb1e.zip new file mode 100644 index 00000000000..43ba84fefb4 Binary files /dev/null and b/.yarn/cache/json-schema-ref-parser-npm-7.1.4-948f9e347a-690252bb1e.zip differ diff --git a/.yarn/cache/json-schema-traverse-npm-0.4.1-4759091693-7486074d3b.zip b/.yarn/cache/json-schema-traverse-npm-0.4.1-4759091693-7486074d3b.zip new file mode 100644 index 00000000000..54f0a7acb67 Binary files /dev/null and b/.yarn/cache/json-schema-traverse-npm-0.4.1-4759091693-7486074d3b.zip differ diff --git a/.yarn/cache/json-schema-traverse-npm-1.0.0-fb3684f4f0-02f2f466cd.zip b/.yarn/cache/json-schema-traverse-npm-1.0.0-fb3684f4f0-02f2f466cd.zip new file mode 100644 index 00000000000..bfd6fdcd8ae Binary files /dev/null and b/.yarn/cache/json-schema-traverse-npm-1.0.0-fb3684f4f0-02f2f466cd.zip differ diff --git a/.yarn/cache/json-stable-stringify-npm-0.0.1-9428a6f044-3a148d4c32.zip b/.yarn/cache/json-stable-stringify-npm-0.0.1-9428a6f044-3a148d4c32.zip new file mode 100644 index 00000000000..03be45323fb Binary files /dev/null and b/.yarn/cache/json-stable-stringify-npm-0.0.1-9428a6f044-3a148d4c32.zip differ diff --git a/.yarn/cache/json-stable-stringify-without-jsonify-npm-1.0.1-b65772b28b-cff44156dd.zip b/.yarn/cache/json-stable-stringify-without-jsonify-npm-1.0.1-b65772b28b-cff44156dd.zip new file mode 100644 index 00000000000..47d58522043 Binary files /dev/null and b/.yarn/cache/json-stable-stringify-without-jsonify-npm-1.0.1-b65772b28b-cff44156dd.zip differ diff --git a/.yarn/cache/json-stringify-nice-npm-1.1.4-0b0ddb188b-6ddf781148.zip b/.yarn/cache/json-stringify-nice-npm-1.1.4-0b0ddb188b-6ddf781148.zip new file mode 100644 index 00000000000..f9a27120414 Binary files /dev/null and b/.yarn/cache/json-stringify-nice-npm-1.1.4-0b0ddb188b-6ddf781148.zip differ diff --git a/.yarn/cache/json-stringify-safe-npm-5.0.1-064ddd6ab4-48ec0adad5.zip b/.yarn/cache/json-stringify-safe-npm-5.0.1-064ddd6ab4-48ec0adad5.zip new file mode 100644 index 00000000000..bda01edf7c4 Binary files /dev/null and b/.yarn/cache/json-stringify-safe-npm-5.0.1-064ddd6ab4-48ec0adad5.zip differ diff --git a/.yarn/cache/json5-npm-1.0.1-647fc8794b-e76ea23dbb.zip b/.yarn/cache/json5-npm-1.0.1-647fc8794b-e76ea23dbb.zip new file mode 100644 index 00000000000..cc70df52204 Binary files /dev/null and b/.yarn/cache/json5-npm-1.0.1-647fc8794b-e76ea23dbb.zip differ diff --git a/.yarn/cache/json5-npm-2.2.0-da49dc7cb5-e88fc5274b.zip b/.yarn/cache/json5-npm-2.2.0-da49dc7cb5-e88fc5274b.zip new file mode 100644 index 00000000000..322b81d9095 Binary files /dev/null and b/.yarn/cache/json5-npm-2.2.0-da49dc7cb5-e88fc5274b.zip differ diff --git a/.yarn/cache/jsonfile-npm-4.0.0-10ce3aea15-6447d6224f.zip b/.yarn/cache/jsonfile-npm-4.0.0-10ce3aea15-6447d6224f.zip new file mode 100644 index 00000000000..a8f0e975adb Binary files /dev/null and b/.yarn/cache/jsonfile-npm-4.0.0-10ce3aea15-6447d6224f.zip differ diff --git a/.yarn/cache/jsonfile-npm-6.1.0-20a4796cee-7af3b8e1ac.zip b/.yarn/cache/jsonfile-npm-6.1.0-20a4796cee-7af3b8e1ac.zip new file mode 100644 index 00000000000..eaf6e09e673 Binary files /dev/null and b/.yarn/cache/jsonfile-npm-6.1.0-20a4796cee-7af3b8e1ac.zip differ diff --git a/.yarn/cache/jsonify-npm-0.0.0-80da2da40c-d8d4ed476c.zip b/.yarn/cache/jsonify-npm-0.0.0-80da2da40c-d8d4ed476c.zip new file mode 100644 index 00000000000..78b9d9a1898 Binary files /dev/null and b/.yarn/cache/jsonify-npm-0.0.0-80da2da40c-d8d4ed476c.zip differ diff --git a/.yarn/cache/jsonparse-npm-1.3.1-b6fde74828-6514a7be46.zip b/.yarn/cache/jsonparse-npm-1.3.1-b6fde74828-6514a7be46.zip new file mode 100644 index 00000000000..fb66b389093 Binary files /dev/null and b/.yarn/cache/jsonparse-npm-1.3.1-b6fde74828-6514a7be46.zip differ diff --git a/.yarn/cache/jsprim-npm-1.4.1-948d2c9ec3-6bcb20ec26.zip b/.yarn/cache/jsprim-npm-1.4.1-948d2c9ec3-6bcb20ec26.zip new file mode 100644 index 00000000000..a7a228d73d0 Binary files /dev/null and b/.yarn/cache/jsprim-npm-1.4.1-948d2c9ec3-6bcb20ec26.zip differ diff --git a/.yarn/cache/just-diff-apply-npm-4.0.1-dfc12fe759-fdb58c0c8d.zip b/.yarn/cache/just-diff-apply-npm-4.0.1-dfc12fe759-fdb58c0c8d.zip new file mode 100644 index 00000000000..d9747215ba3 Binary files /dev/null and b/.yarn/cache/just-diff-apply-npm-4.0.1-dfc12fe759-fdb58c0c8d.zip differ diff --git a/.yarn/cache/just-diff-npm-5.0.1-6477d7b637-efbdb65298.zip b/.yarn/cache/just-diff-npm-5.0.1-6477d7b637-efbdb65298.zip new file mode 100644 index 00000000000..f0bddd56f8a Binary files /dev/null and b/.yarn/cache/just-diff-npm-5.0.1-6477d7b637-efbdb65298.zip differ diff --git a/.yarn/cache/just-extend-npm-4.2.1-ccc4201277-ff9fdede24.zip b/.yarn/cache/just-extend-npm-4.2.1-ccc4201277-ff9fdede24.zip new file mode 100644 index 00000000000..8e6593fadb9 Binary files /dev/null and b/.yarn/cache/just-extend-npm-4.2.1-ccc4201277-ff9fdede24.zip differ diff --git a/.yarn/cache/karma-chai-npm-0.1.0-d1d807f507-7fae0b4ace.zip b/.yarn/cache/karma-chai-npm-0.1.0-d1d807f507-7fae0b4ace.zip new file mode 100644 index 00000000000..f2ff67e22a3 Binary files /dev/null and b/.yarn/cache/karma-chai-npm-0.1.0-d1d807f507-7fae0b4ace.zip differ diff --git a/.yarn/cache/karma-chrome-launcher-npm-3.1.0-999405afd7-63431ddec9.zip b/.yarn/cache/karma-chrome-launcher-npm-3.1.0-999405afd7-63431ddec9.zip new file mode 100644 index 00000000000..e752cbdad97 Binary files /dev/null and b/.yarn/cache/karma-chrome-launcher-npm-3.1.0-999405afd7-63431ddec9.zip differ diff --git a/.yarn/cache/karma-firefox-launcher-npm-2.1.2-63bf50abac-bfd5b35b35.zip b/.yarn/cache/karma-firefox-launcher-npm-2.1.2-63bf50abac-bfd5b35b35.zip new file mode 100644 index 00000000000..b0551b5f70f Binary files /dev/null and b/.yarn/cache/karma-firefox-launcher-npm-2.1.2-63bf50abac-bfd5b35b35.zip differ diff --git a/.yarn/cache/karma-mocha-npm-2.0.1-b8979157d3-a09f475875.zip b/.yarn/cache/karma-mocha-npm-2.0.1-b8979157d3-a09f475875.zip new file mode 100644 index 00000000000..cbe9391aca0 Binary files /dev/null and b/.yarn/cache/karma-mocha-npm-2.0.1-b8979157d3-a09f475875.zip differ diff --git a/.yarn/cache/karma-mocha-reporter-npm-2.2.5-4329166101-8b9e43c64b.zip b/.yarn/cache/karma-mocha-reporter-npm-2.2.5-4329166101-8b9e43c64b.zip new file mode 100644 index 00000000000..d67bcfb9386 Binary files /dev/null and b/.yarn/cache/karma-mocha-reporter-npm-2.2.5-4329166101-8b9e43c64b.zip differ diff --git a/.yarn/cache/karma-npm-6.3.9-f5f936b668-2e652c8f4d.zip b/.yarn/cache/karma-npm-6.3.9-f5f936b668-2e652c8f4d.zip new file mode 100644 index 00000000000..04c53a83d83 Binary files /dev/null and b/.yarn/cache/karma-npm-6.3.9-f5f936b668-2e652c8f4d.zip differ diff --git a/.yarn/cache/karma-sourcemap-loader-npm-0.3.8-a7560c795e-12e21849af.zip b/.yarn/cache/karma-sourcemap-loader-npm-0.3.8-a7560c795e-12e21849af.zip new file mode 100644 index 00000000000..1dd86185309 Binary files /dev/null and b/.yarn/cache/karma-sourcemap-loader-npm-0.3.8-a7560c795e-12e21849af.zip differ diff --git a/.yarn/cache/karma-webpack-npm-5.0.0-d7c66b2a8a-869b835f91.zip b/.yarn/cache/karma-webpack-npm-5.0.0-d7c66b2a8a-869b835f91.zip new file mode 100644 index 00000000000..325cd62a770 Binary files /dev/null and b/.yarn/cache/karma-webpack-npm-5.0.0-d7c66b2a8a-869b835f91.zip differ diff --git a/.yarn/cache/keyv-npm-3.1.0-81c9ff4454-bb7e8f3acf.zip b/.yarn/cache/keyv-npm-3.1.0-81c9ff4454-bb7e8f3acf.zip new file mode 100644 index 00000000000..b5940b4b893 Binary files /dev/null and b/.yarn/cache/keyv-npm-3.1.0-81c9ff4454-bb7e8f3acf.zip differ diff --git a/.yarn/cache/kind-of-npm-6.0.3-ab15f36220-3ab01e7b1d.zip b/.yarn/cache/kind-of-npm-6.0.3-ab15f36220-3ab01e7b1d.zip new file mode 100644 index 00000000000..90b2647fece Binary files /dev/null and b/.yarn/cache/kind-of-npm-6.0.3-ab15f36220-3ab01e7b1d.zip differ diff --git a/.yarn/cache/kuler-npm-2.0.0-19e74c9695-9e10b5a165.zip b/.yarn/cache/kuler-npm-2.0.0-19e74c9695-9e10b5a165.zip new file mode 100644 index 00000000000..1c905daa0dc Binary files /dev/null and b/.yarn/cache/kuler-npm-2.0.0-19e74c9695-9e10b5a165.zip differ diff --git a/.yarn/cache/labeled-stream-splicer-npm-2.0.2-ac01fae08b-4f7097b766.zip b/.yarn/cache/labeled-stream-splicer-npm-2.0.2-ac01fae08b-4f7097b766.zip new file mode 100644 index 00000000000..82e7a0471f4 Binary files /dev/null and b/.yarn/cache/labeled-stream-splicer-npm-2.0.2-ac01fae08b-4f7097b766.zip differ diff --git a/.yarn/cache/latest-version-npm-5.1.0-ddb9b0eb39-fbc72b071e.zip b/.yarn/cache/latest-version-npm-5.1.0-ddb9b0eb39-fbc72b071e.zip new file mode 100644 index 00000000000..d4ad359859c Binary files /dev/null and b/.yarn/cache/latest-version-npm-5.1.0-ddb9b0eb39-fbc72b071e.zip differ diff --git a/.yarn/cache/level-concat-iterator-npm-2.0.1-5179af5bd2-562583ef12.zip b/.yarn/cache/level-concat-iterator-npm-2.0.1-5179af5bd2-562583ef12.zip new file mode 100644 index 00000000000..74f371902c2 Binary files /dev/null and b/.yarn/cache/level-concat-iterator-npm-2.0.1-5179af5bd2-562583ef12.zip differ diff --git a/.yarn/cache/level-errors-npm-2.0.1-981e46a3dc-aca5d7670e.zip b/.yarn/cache/level-errors-npm-2.0.1-981e46a3dc-aca5d7670e.zip new file mode 100644 index 00000000000..cb82152f5d4 Binary files /dev/null and b/.yarn/cache/level-errors-npm-2.0.1-981e46a3dc-aca5d7670e.zip differ diff --git a/.yarn/cache/level-iterator-stream-npm-4.0.2-27e0549122-239e2c7e62.zip b/.yarn/cache/level-iterator-stream-npm-4.0.2-27e0549122-239e2c7e62.zip new file mode 100644 index 00000000000..8607aba4b3d Binary files /dev/null and b/.yarn/cache/level-iterator-stream-npm-4.0.2-27e0549122-239e2c7e62.zip differ diff --git a/.yarn/cache/level-supports-npm-1.0.1-e9d5ae27f4-5d6bdb88cf.zip b/.yarn/cache/level-supports-npm-1.0.1-e9d5ae27f4-5d6bdb88cf.zip new file mode 100644 index 00000000000..10501178d82 Binary files /dev/null and b/.yarn/cache/level-supports-npm-1.0.1-e9d5ae27f4-5d6bdb88cf.zip differ diff --git a/.yarn/cache/levelup-npm-4.4.0-3053c0e5bc-5a09e34c78.zip b/.yarn/cache/levelup-npm-4.4.0-3053c0e5bc-5a09e34c78.zip new file mode 100644 index 00000000000..c1580e76f28 Binary files /dev/null and b/.yarn/cache/levelup-npm-4.4.0-3053c0e5bc-5a09e34c78.zip differ diff --git a/.yarn/cache/leven-npm-2.1.0-19f0a16606-f7b4a01b15.zip b/.yarn/cache/leven-npm-2.1.0-19f0a16606-f7b4a01b15.zip new file mode 100644 index 00000000000..6eba0706ba5 Binary files /dev/null and b/.yarn/cache/leven-npm-2.1.0-19f0a16606-f7b4a01b15.zip differ diff --git a/.yarn/cache/levn-npm-0.3.0-48d774b1c2-0d084a5242.zip b/.yarn/cache/levn-npm-0.3.0-48d774b1c2-0d084a5242.zip new file mode 100644 index 00000000000..a7966131ffc Binary files /dev/null and b/.yarn/cache/levn-npm-0.3.0-48d774b1c2-0d084a5242.zip differ diff --git a/.yarn/cache/levn-npm-0.4.1-d183b2d7bb-12c5021c85.zip b/.yarn/cache/levn-npm-0.4.1-d183b2d7bb-12c5021c85.zip new file mode 100644 index 00000000000..dda4d01a391 Binary files /dev/null and b/.yarn/cache/levn-npm-0.4.1-d183b2d7bb-12c5021c85.zip differ diff --git a/.yarn/cache/lie-npm-3.1.1-91350720d9-6da9f2121d.zip b/.yarn/cache/lie-npm-3.1.1-91350720d9-6da9f2121d.zip new file mode 100644 index 00000000000..dfbd4858e59 Binary files /dev/null and b/.yarn/cache/lie-npm-3.1.1-91350720d9-6da9f2121d.zip differ diff --git a/.yarn/cache/lines-and-columns-npm-1.1.6-23e74fab67-198a5436b1.zip b/.yarn/cache/lines-and-columns-npm-1.1.6-23e74fab67-198a5436b1.zip new file mode 100644 index 00000000000..7a35cefdf5f Binary files /dev/null and b/.yarn/cache/lines-and-columns-npm-1.1.6-23e74fab67-198a5436b1.zip differ diff --git a/.yarn/cache/listr2-npm-3.5.0-6aad1da502-cf30837462.zip b/.yarn/cache/listr2-npm-3.5.0-6aad1da502-cf30837462.zip new file mode 100644 index 00000000000..7a4d1dfaa14 Binary files /dev/null and b/.yarn/cache/listr2-npm-3.5.0-6aad1da502-cf30837462.zip differ diff --git a/.yarn/cache/load-json-file-npm-4.0.0-c9f09d85eb-8f5d6d93ba.zip b/.yarn/cache/load-json-file-npm-4.0.0-c9f09d85eb-8f5d6d93ba.zip new file mode 100644 index 00000000000..48ad7d38145 Binary files /dev/null and b/.yarn/cache/load-json-file-npm-4.0.0-c9f09d85eb-8f5d6d93ba.zip differ diff --git a/.yarn/cache/load-json-file-npm-6.2.0-516f143724-4429e430eb.zip b/.yarn/cache/load-json-file-npm-6.2.0-516f143724-4429e430eb.zip new file mode 100644 index 00000000000..5daf3f6af44 Binary files /dev/null and b/.yarn/cache/load-json-file-npm-6.2.0-516f143724-4429e430eb.zip differ diff --git a/.yarn/cache/load-yaml-file-npm-0.2.0-0369385ceb-d86d7ec7b1.zip b/.yarn/cache/load-yaml-file-npm-0.2.0-0369385ceb-d86d7ec7b1.zip new file mode 100644 index 00000000000..c178cce5305 Binary files /dev/null and b/.yarn/cache/load-yaml-file-npm-0.2.0-0369385ceb-d86d7ec7b1.zip differ diff --git a/.yarn/cache/loader-runner-npm-4.2.0-427f0e7134-e61aea8b69.zip b/.yarn/cache/loader-runner-npm-4.2.0-427f0e7134-e61aea8b69.zip new file mode 100644 index 00000000000..e891a9e6038 Binary files /dev/null and b/.yarn/cache/loader-runner-npm-4.2.0-427f0e7134-e61aea8b69.zip differ diff --git a/.yarn/cache/loader-utils-npm-1.4.0-a56254a277-d150b15e7a.zip b/.yarn/cache/loader-utils-npm-1.4.0-a56254a277-d150b15e7a.zip new file mode 100644 index 00000000000..c13e2dc4858 Binary files /dev/null and b/.yarn/cache/loader-utils-npm-1.4.0-a56254a277-d150b15e7a.zip differ diff --git a/.yarn/cache/loader-utils-npm-2.0.2-c693411911-9078d1ed47.zip b/.yarn/cache/loader-utils-npm-2.0.2-c693411911-9078d1ed47.zip new file mode 100644 index 00000000000..9a9db60cf69 Binary files /dev/null and b/.yarn/cache/loader-utils-npm-2.0.2-c693411911-9078d1ed47.zip differ diff --git a/.yarn/cache/localforage-npm-1.10.0-cf9ea9a436-f2978b434d.zip b/.yarn/cache/localforage-npm-1.10.0-cf9ea9a436-f2978b434d.zip new file mode 100644 index 00000000000..b9f85f79538 Binary files /dev/null and b/.yarn/cache/localforage-npm-1.10.0-cf9ea9a436-f2978b434d.zip differ diff --git a/.yarn/cache/locate-path-npm-2.0.0-673d28b0ea-02d581edbb.zip b/.yarn/cache/locate-path-npm-2.0.0-673d28b0ea-02d581edbb.zip new file mode 100644 index 00000000000..0841fd1c173 Binary files /dev/null and b/.yarn/cache/locate-path-npm-2.0.0-673d28b0ea-02d581edbb.zip differ diff --git a/.yarn/cache/locate-path-npm-5.0.0-46580c43e4-83e51725e6.zip b/.yarn/cache/locate-path-npm-5.0.0-46580c43e4-83e51725e6.zip new file mode 100644 index 00000000000..e24713496cc Binary files /dev/null and b/.yarn/cache/locate-path-npm-5.0.0-46580c43e4-83e51725e6.zip differ diff --git a/.yarn/cache/locate-path-npm-6.0.0-06a1e4c528-72eb661788.zip b/.yarn/cache/locate-path-npm-6.0.0-06a1e4c528-72eb661788.zip new file mode 100644 index 00000000000..b67b77440bb Binary files /dev/null and b/.yarn/cache/locate-path-npm-6.0.0-06a1e4c528-72eb661788.zip differ diff --git a/.yarn/cache/lodash-npm-4.17.21-6382451519-eb835a2e51.zip b/.yarn/cache/lodash-npm-4.17.21-6382451519-eb835a2e51.zip new file mode 100644 index 00000000000..22ac44c4efb Binary files /dev/null and b/.yarn/cache/lodash-npm-4.17.21-6382451519-eb835a2e51.zip differ diff --git a/.yarn/cache/lodash.camelcase-npm-4.3.0-bf268e3bf0-cb9227612f.zip b/.yarn/cache/lodash.camelcase-npm-4.3.0-bf268e3bf0-cb9227612f.zip new file mode 100644 index 00000000000..2e9ae3fcb1b Binary files /dev/null and b/.yarn/cache/lodash.camelcase-npm-4.3.0-bf268e3bf0-cb9227612f.zip differ diff --git a/.yarn/cache/lodash.clone-npm-4.5.0-d9f712430b-5839f22acf.zip b/.yarn/cache/lodash.clone-npm-4.5.0-d9f712430b-5839f22acf.zip new file mode 100644 index 00000000000..2ad436e4045 Binary files /dev/null and b/.yarn/cache/lodash.clone-npm-4.5.0-d9f712430b-5839f22acf.zip differ diff --git a/.yarn/cache/lodash.clonedeep-npm-4.5.0-fbc3cda4e5-92c46f094b.zip b/.yarn/cache/lodash.clonedeep-npm-4.5.0-fbc3cda4e5-92c46f094b.zip new file mode 100644 index 00000000000..5765f760d7b Binary files /dev/null and b/.yarn/cache/lodash.clonedeep-npm-4.5.0-fbc3cda4e5-92c46f094b.zip differ diff --git a/.yarn/cache/lodash.clonedeepwith-npm-4.5.0-67373e487a-9fbf4ebfa0.zip b/.yarn/cache/lodash.clonedeepwith-npm-4.5.0-67373e487a-9fbf4ebfa0.zip new file mode 100644 index 00000000000..1ed9b32a123 Binary files /dev/null and b/.yarn/cache/lodash.clonedeepwith-npm-4.5.0-67373e487a-9fbf4ebfa0.zip differ diff --git a/.yarn/cache/lodash.debounce-npm-4.0.8-f1d6e09799-a3f527d22c.zip b/.yarn/cache/lodash.debounce-npm-4.0.8-f1d6e09799-a3f527d22c.zip new file mode 100644 index 00000000000..1b5cf136405 Binary files /dev/null and b/.yarn/cache/lodash.debounce-npm-4.0.8-f1d6e09799-a3f527d22c.zip differ diff --git a/.yarn/cache/lodash.find-npm-4.6.0-dd2db8c53f-b737f849a4.zip b/.yarn/cache/lodash.find-npm-4.6.0-dd2db8c53f-b737f849a4.zip new file mode 100644 index 00000000000..6b4e3f7c9ae Binary files /dev/null and b/.yarn/cache/lodash.find-npm-4.6.0-dd2db8c53f-b737f849a4.zip differ diff --git a/.yarn/cache/lodash.flattendeep-npm-4.4.0-26b2b4cbd7-8521c919ac.zip b/.yarn/cache/lodash.flattendeep-npm-4.4.0-26b2b4cbd7-8521c919ac.zip new file mode 100644 index 00000000000..7e35ec6c7b5 Binary files /dev/null and b/.yarn/cache/lodash.flattendeep-npm-4.4.0-26b2b4cbd7-8521c919ac.zip differ diff --git a/.yarn/cache/lodash.get-npm-4.4.2-7bda64ed87-e403047ddb.zip b/.yarn/cache/lodash.get-npm-4.4.2-7bda64ed87-e403047ddb.zip new file mode 100644 index 00000000000..63cd7ccfc2f Binary files /dev/null and b/.yarn/cache/lodash.get-npm-4.4.2-7bda64ed87-e403047ddb.zip differ diff --git a/.yarn/cache/lodash.isequal-npm-4.5.0-f8b0f64d63-da27515dc5.zip b/.yarn/cache/lodash.isequal-npm-4.5.0-f8b0f64d63-da27515dc5.zip new file mode 100644 index 00000000000..d011a65e17a Binary files /dev/null and b/.yarn/cache/lodash.isequal-npm-4.5.0-f8b0f64d63-da27515dc5.zip differ diff --git a/.yarn/cache/lodash.ismatch-npm-4.4.0-e538fd6c3d-a393917578.zip b/.yarn/cache/lodash.ismatch-npm-4.4.0-e538fd6c3d-a393917578.zip new file mode 100644 index 00000000000..223a6692b10 Binary files /dev/null and b/.yarn/cache/lodash.ismatch-npm-4.4.0-e538fd6c3d-a393917578.zip differ diff --git a/.yarn/cache/lodash.matches-npm-4.6.0-4ac5f4f696-002617abb6.zip b/.yarn/cache/lodash.matches-npm-4.6.0-4ac5f4f696-002617abb6.zip new file mode 100644 index 00000000000..059bc28a181 Binary files /dev/null and b/.yarn/cache/lodash.matches-npm-4.6.0-4ac5f4f696-002617abb6.zip differ diff --git a/.yarn/cache/lodash.memoize-npm-3.0.4-40c36c3de4-fc52e0916b.zip b/.yarn/cache/lodash.memoize-npm-3.0.4-40c36c3de4-fc52e0916b.zip new file mode 100644 index 00000000000..bf961ba8583 Binary files /dev/null and b/.yarn/cache/lodash.memoize-npm-3.0.4-40c36c3de4-fc52e0916b.zip differ diff --git a/.yarn/cache/lodash.merge-npm-4.6.2-77cb4416bf-ad580b4bdb.zip b/.yarn/cache/lodash.merge-npm-4.6.2-77cb4416bf-ad580b4bdb.zip new file mode 100644 index 00000000000..f6bc72b461e Binary files /dev/null and b/.yarn/cache/lodash.merge-npm-4.6.2-77cb4416bf-ad580b4bdb.zip differ diff --git a/.yarn/cache/lodash.sample-npm-4.2.1-ec9e9fdf4d-8d93c1db13.zip b/.yarn/cache/lodash.sample-npm-4.2.1-ec9e9fdf4d-8d93c1db13.zip new file mode 100644 index 00000000000..52c2ed81888 Binary files /dev/null and b/.yarn/cache/lodash.sample-npm-4.2.1-ec9e9fdf4d-8d93c1db13.zip differ diff --git a/.yarn/cache/lodash.set-npm-4.3.2-7586c942c2-a9122f49ee.zip b/.yarn/cache/lodash.set-npm-4.3.2-7586c942c2-a9122f49ee.zip new file mode 100644 index 00000000000..4055fea4f25 Binary files /dev/null and b/.yarn/cache/lodash.set-npm-4.3.2-7586c942c2-a9122f49ee.zip differ diff --git a/.yarn/cache/lodash.truncate-npm-4.4.2-bc50fe1663-b463d8a382.zip b/.yarn/cache/lodash.truncate-npm-4.4.2-bc50fe1663-b463d8a382.zip new file mode 100644 index 00000000000..edf9509868d Binary files /dev/null and b/.yarn/cache/lodash.truncate-npm-4.4.2-bc50fe1663-b463d8a382.zip differ diff --git a/.yarn/cache/log-symbols-npm-2.2.0-9541ad4da6-4c95e3b65f.zip b/.yarn/cache/log-symbols-npm-2.2.0-9541ad4da6-4c95e3b65f.zip new file mode 100644 index 00000000000..7a3fd5229f4 Binary files /dev/null and b/.yarn/cache/log-symbols-npm-2.2.0-9541ad4da6-4c95e3b65f.zip differ diff --git a/.yarn/cache/log-symbols-npm-4.1.0-0a13492d8b-fce1497b31.zip b/.yarn/cache/log-symbols-npm-4.1.0-0a13492d8b-fce1497b31.zip new file mode 100644 index 00000000000..6a7e0761597 Binary files /dev/null and b/.yarn/cache/log-symbols-npm-4.1.0-0a13492d8b-fce1497b31.zip differ diff --git a/.yarn/cache/log-update-npm-4.0.0-9d0554261c-ae2f85bbab.zip b/.yarn/cache/log-update-npm-4.0.0-9d0554261c-ae2f85bbab.zip new file mode 100644 index 00000000000..66a2c50de73 Binary files /dev/null and b/.yarn/cache/log-update-npm-4.0.0-9d0554261c-ae2f85bbab.zip differ diff --git a/.yarn/cache/log4js-npm-6.3.0-7d1ea76e6b-da2812bbe4.zip b/.yarn/cache/log4js-npm-6.3.0-7d1ea76e6b-da2812bbe4.zip new file mode 100644 index 00000000000..c5d7915c8d2 Binary files /dev/null and b/.yarn/cache/log4js-npm-6.3.0-7d1ea76e6b-da2812bbe4.zip differ diff --git a/.yarn/cache/logform-npm-2.3.0-13155f7f21-a82d36823d.zip b/.yarn/cache/logform-npm-2.3.0-13155f7f21-a82d36823d.zip new file mode 100644 index 00000000000..f33814333cf Binary files /dev/null and b/.yarn/cache/logform-npm-2.3.0-13155f7f21-a82d36823d.zip differ diff --git a/.yarn/cache/long-npm-4.0.0-ecd96a31ed-16afbe8f74.zip b/.yarn/cache/long-npm-4.0.0-ecd96a31ed-16afbe8f74.zip new file mode 100644 index 00000000000..228e6f9948c Binary files /dev/null and b/.yarn/cache/long-npm-4.0.0-ecd96a31ed-16afbe8f74.zip differ diff --git a/.yarn/cache/long-npm-5.2.0-bbbd23a9e6-37aa4e67b9.zip b/.yarn/cache/long-npm-5.2.0-bbbd23a9e6-37aa4e67b9.zip new file mode 100644 index 00000000000..29b047c6b93 Binary files /dev/null and b/.yarn/cache/long-npm-5.2.0-bbbd23a9e6-37aa4e67b9.zip differ diff --git a/.yarn/cache/lower-case-npm-2.0.2-151055f1c2-83a0a5f159.zip b/.yarn/cache/lower-case-npm-2.0.2-151055f1c2-83a0a5f159.zip new file mode 100644 index 00000000000..0f0a86e1cce Binary files /dev/null and b/.yarn/cache/lower-case-npm-2.0.2-151055f1c2-83a0a5f159.zip differ diff --git a/.yarn/cache/lowercase-keys-npm-1.0.1-0979e653b8-4d04502659.zip b/.yarn/cache/lowercase-keys-npm-1.0.1-0979e653b8-4d04502659.zip new file mode 100644 index 00000000000..524b896420f Binary files /dev/null and b/.yarn/cache/lowercase-keys-npm-1.0.1-0979e653b8-4d04502659.zip differ diff --git a/.yarn/cache/lowercase-keys-npm-2.0.0-1876065a32-24d7ebd56c.zip b/.yarn/cache/lowercase-keys-npm-2.0.0-1876065a32-24d7ebd56c.zip new file mode 100644 index 00000000000..80588e7bfec Binary files /dev/null and b/.yarn/cache/lowercase-keys-npm-2.0.0-1876065a32-24d7ebd56c.zip differ diff --git a/.yarn/cache/lru-cache-npm-5.1.1-f475882a51-c154ae1cbb.zip b/.yarn/cache/lru-cache-npm-5.1.1-f475882a51-c154ae1cbb.zip new file mode 100644 index 00000000000..3f6ba116e96 Binary files /dev/null and b/.yarn/cache/lru-cache-npm-5.1.1-f475882a51-c154ae1cbb.zip differ diff --git a/.yarn/cache/lru-cache-npm-6.0.0-b4c8668fe1-f97f499f89.zip b/.yarn/cache/lru-cache-npm-6.0.0-b4c8668fe1-f97f499f89.zip new file mode 100644 index 00000000000..1635dac9b28 Binary files /dev/null and b/.yarn/cache/lru-cache-npm-6.0.0-b4c8668fe1-f97f499f89.zip differ diff --git a/.yarn/cache/lru-cache-npm-7.3.1-b157dca680-34bb50c015.zip b/.yarn/cache/lru-cache-npm-7.3.1-b157dca680-34bb50c015.zip new file mode 100644 index 00000000000..d3b10a53fc6 Binary files /dev/null and b/.yarn/cache/lru-cache-npm-7.3.1-b157dca680-34bb50c015.zip differ diff --git a/.yarn/cache/ltgt-npm-2.2.1-443b5da86d-7e3874296f.zip b/.yarn/cache/ltgt-npm-2.2.1-443b5da86d-7e3874296f.zip new file mode 100644 index 00000000000..e95dc01215f Binary files /dev/null and b/.yarn/cache/ltgt-npm-2.2.1-443b5da86d-7e3874296f.zip differ diff --git a/.yarn/cache/make-dir-npm-3.1.0-d1d7505142-484200020a.zip b/.yarn/cache/make-dir-npm-3.1.0-d1d7505142-484200020a.zip new file mode 100644 index 00000000000..e466cd8a165 Binary files /dev/null and b/.yarn/cache/make-dir-npm-3.1.0-d1d7505142-484200020a.zip differ diff --git a/.yarn/cache/make-error-npm-1.3.6-ccb85d9458-b86e5e0e25.zip b/.yarn/cache/make-error-npm-1.3.6-ccb85d9458-b86e5e0e25.zip new file mode 100644 index 00000000000..7f7dc189d20 Binary files /dev/null and b/.yarn/cache/make-error-npm-1.3.6-ccb85d9458-b86e5e0e25.zip differ diff --git a/.yarn/cache/make-fetch-happen-npm-10.0.3-e552879254-edf3ba5119.zip b/.yarn/cache/make-fetch-happen-npm-10.0.3-e552879254-edf3ba5119.zip new file mode 100644 index 00000000000..f28390c3e25 Binary files /dev/null and b/.yarn/cache/make-fetch-happen-npm-10.0.3-e552879254-edf3ba5119.zip differ diff --git a/.yarn/cache/make-fetch-happen-npm-9.1.0-23184ad7f6-0eb371c85f.zip b/.yarn/cache/make-fetch-happen-npm-9.1.0-23184ad7f6-0eb371c85f.zip new file mode 100644 index 00000000000..6031318f023 Binary files /dev/null and b/.yarn/cache/make-fetch-happen-npm-9.1.0-23184ad7f6-0eb371c85f.zip differ diff --git a/.yarn/cache/map-obj-npm-1.0.1-fa55100fac-9949e7baec.zip b/.yarn/cache/map-obj-npm-1.0.1-fa55100fac-9949e7baec.zip new file mode 100644 index 00000000000..b55f3f14c26 Binary files /dev/null and b/.yarn/cache/map-obj-npm-1.0.1-fa55100fac-9949e7baec.zip differ diff --git a/.yarn/cache/map-obj-npm-4.3.0-d53e32935d-fbc554934d.zip b/.yarn/cache/map-obj-npm-4.3.0-d53e32935d-fbc554934d.zip new file mode 100644 index 00000000000..d775463548f Binary files /dev/null and b/.yarn/cache/map-obj-npm-4.3.0-d53e32935d-fbc554934d.zip differ diff --git a/.yarn/cache/mathjs-npm-10.4.3-9f80458c54-ed2343b2ab.zip b/.yarn/cache/mathjs-npm-10.4.3-9f80458c54-ed2343b2ab.zip new file mode 100644 index 00000000000..5e45999a2f4 Binary files /dev/null and b/.yarn/cache/mathjs-npm-10.4.3-9f80458c54-ed2343b2ab.zip differ diff --git a/.yarn/cache/md5.js-npm-1.3.5-130901125a-098494d885.zip b/.yarn/cache/md5.js-npm-1.3.5-130901125a-098494d885.zip new file mode 100644 index 00000000000..b9cd75b5ea5 Binary files /dev/null and b/.yarn/cache/md5.js-npm-1.3.5-130901125a-098494d885.zip differ diff --git a/.yarn/cache/media-typer-npm-0.3.0-8674f8f0f5-af1b38516c.zip b/.yarn/cache/media-typer-npm-0.3.0-8674f8f0f5-af1b38516c.zip new file mode 100644 index 00000000000..1bc09780863 Binary files /dev/null and b/.yarn/cache/media-typer-npm-0.3.0-8674f8f0f5-af1b38516c.zip differ diff --git a/.yarn/cache/mem-fs-editor-npm-9.4.0-97c608fb01-427b71d59a.zip b/.yarn/cache/mem-fs-editor-npm-9.4.0-97c608fb01-427b71d59a.zip new file mode 100644 index 00000000000..e50b7b91830 Binary files /dev/null and b/.yarn/cache/mem-fs-editor-npm-9.4.0-97c608fb01-427b71d59a.zip differ diff --git a/.yarn/cache/mem-fs-npm-2.2.1-5a394345d4-e44fb4acf8.zip b/.yarn/cache/mem-fs-npm-2.2.1-5a394345d4-e44fb4acf8.zip new file mode 100644 index 00000000000..ce69feeeeaa Binary files /dev/null and b/.yarn/cache/mem-fs-npm-2.2.1-5a394345d4-e44fb4acf8.zip differ diff --git a/.yarn/cache/memdown-npm-5.1.0-e769608fe2-23e4414034.zip b/.yarn/cache/memdown-npm-5.1.0-e769608fe2-23e4414034.zip new file mode 100644 index 00000000000..e677ccc9100 Binary files /dev/null and b/.yarn/cache/memdown-npm-5.1.0-e769608fe2-23e4414034.zip differ diff --git a/.yarn/cache/memory-fs-npm-0.5.0-8be5938449-a9f25b0a8e.zip b/.yarn/cache/memory-fs-npm-0.5.0-8be5938449-a9f25b0a8e.zip new file mode 100644 index 00000000000..5798a1f67a4 Binary files /dev/null and b/.yarn/cache/memory-fs-npm-0.5.0-8be5938449-a9f25b0a8e.zip differ diff --git a/.yarn/cache/memory-pager-npm-1.5.0-46e20e6c81-d1a2e68458.zip b/.yarn/cache/memory-pager-npm-1.5.0-46e20e6c81-d1a2e68458.zip new file mode 100644 index 00000000000..2bebede336a Binary files /dev/null and b/.yarn/cache/memory-pager-npm-1.5.0-46e20e6c81-d1a2e68458.zip differ diff --git a/.yarn/cache/memory-streams-npm-0.1.3-8b67a57781-aebb6dc54c.zip b/.yarn/cache/memory-streams-npm-0.1.3-8b67a57781-aebb6dc54c.zip new file mode 100644 index 00000000000..608233918aa Binary files /dev/null and b/.yarn/cache/memory-streams-npm-0.1.3-8b67a57781-aebb6dc54c.zip differ diff --git a/.yarn/cache/meow-npm-8.1.2-bcfe48d4f3-bc23bf1b44.zip b/.yarn/cache/meow-npm-8.1.2-bcfe48d4f3-bc23bf1b44.zip new file mode 100644 index 00000000000..26b795a846e Binary files /dev/null and b/.yarn/cache/meow-npm-8.1.2-bcfe48d4f3-bc23bf1b44.zip differ diff --git a/.yarn/cache/merge-stream-npm-2.0.0-2ac83efea5-6fa4dcc8d8.zip b/.yarn/cache/merge-stream-npm-2.0.0-2ac83efea5-6fa4dcc8d8.zip new file mode 100644 index 00000000000..1cf9d57dce0 Binary files /dev/null and b/.yarn/cache/merge-stream-npm-2.0.0-2ac83efea5-6fa4dcc8d8.zip differ diff --git a/.yarn/cache/merge2-npm-1.4.1-a2507bd06c-7268db63ed.zip b/.yarn/cache/merge2-npm-1.4.1-a2507bd06c-7268db63ed.zip new file mode 100644 index 00000000000..76aa4f0b4ec Binary files /dev/null and b/.yarn/cache/merge2-npm-1.4.1-a2507bd06c-7268db63ed.zip differ diff --git a/.yarn/cache/micro-memoize-npm-4.0.9-ebbd2df842-c755539864.zip b/.yarn/cache/micro-memoize-npm-4.0.9-ebbd2df842-c755539864.zip new file mode 100644 index 00000000000..ab5b1f5e0ef Binary files /dev/null and b/.yarn/cache/micro-memoize-npm-4.0.9-ebbd2df842-c755539864.zip differ diff --git a/.yarn/cache/micromatch-npm-4.0.4-9fdcbb7a0e-ef3d1c88e7.zip b/.yarn/cache/micromatch-npm-4.0.4-9fdcbb7a0e-ef3d1c88e7.zip new file mode 100644 index 00000000000..a89e8251929 Binary files /dev/null and b/.yarn/cache/micromatch-npm-4.0.4-9fdcbb7a0e-ef3d1c88e7.zip differ diff --git a/.yarn/cache/miller-rabin-npm-4.0.1-3426ac0bf7-00cd1ab838.zip b/.yarn/cache/miller-rabin-npm-4.0.1-3426ac0bf7-00cd1ab838.zip new file mode 100644 index 00000000000..5e0fde11cb9 Binary files /dev/null and b/.yarn/cache/miller-rabin-npm-4.0.1-3426ac0bf7-00cd1ab838.zip differ diff --git a/.yarn/cache/mime-db-npm-1.51.0-d5e42b45ad-613b1ac9d6.zip b/.yarn/cache/mime-db-npm-1.51.0-d5e42b45ad-613b1ac9d6.zip new file mode 100644 index 00000000000..08bd4c6ee14 Binary files /dev/null and b/.yarn/cache/mime-db-npm-1.51.0-d5e42b45ad-613b1ac9d6.zip differ diff --git a/.yarn/cache/mime-npm-2.6.0-88b89d8de0-1497ba7b9f.zip b/.yarn/cache/mime-npm-2.6.0-88b89d8de0-1497ba7b9f.zip new file mode 100644 index 00000000000..644ef2b53fb Binary files /dev/null and b/.yarn/cache/mime-npm-2.6.0-88b89d8de0-1497ba7b9f.zip differ diff --git a/.yarn/cache/mime-types-npm-2.1.34-3cd0bb907c-67013de9e9.zip b/.yarn/cache/mime-types-npm-2.1.34-3cd0bb907c-67013de9e9.zip new file mode 100644 index 00000000000..f3bdb304a4c Binary files /dev/null and b/.yarn/cache/mime-types-npm-2.1.34-3cd0bb907c-67013de9e9.zip differ diff --git a/.yarn/cache/mimic-fn-npm-2.1.0-4fbeb3abb4-d2421a3444.zip b/.yarn/cache/mimic-fn-npm-2.1.0-4fbeb3abb4-d2421a3444.zip new file mode 100644 index 00000000000..1cc2414f46c Binary files /dev/null and b/.yarn/cache/mimic-fn-npm-2.1.0-4fbeb3abb4-d2421a3444.zip differ diff --git a/.yarn/cache/mimic-response-npm-1.0.1-f6f85dde84-034c78753b.zip b/.yarn/cache/mimic-response-npm-1.0.1-f6f85dde84-034c78753b.zip new file mode 100644 index 00000000000..acf641b2dae Binary files /dev/null and b/.yarn/cache/mimic-response-npm-1.0.1-f6f85dde84-034c78753b.zip differ diff --git a/.yarn/cache/min-indent-npm-1.0.1-77031f50e1-bfc6dd03c5.zip b/.yarn/cache/min-indent-npm-1.0.1-77031f50e1-bfc6dd03c5.zip new file mode 100644 index 00000000000..5ab689d40a3 Binary files /dev/null and b/.yarn/cache/min-indent-npm-1.0.1-77031f50e1-bfc6dd03c5.zip differ diff --git a/.yarn/cache/minimalistic-assert-npm-1.0.1-dc8bb23d29-cc7974a926.zip b/.yarn/cache/minimalistic-assert-npm-1.0.1-dc8bb23d29-cc7974a926.zip new file mode 100644 index 00000000000..8c95a3ede5c Binary files /dev/null and b/.yarn/cache/minimalistic-assert-npm-1.0.1-dc8bb23d29-cc7974a926.zip differ diff --git a/.yarn/cache/minimalistic-crypto-utils-npm-1.0.1-e66b10822e-6e8a0422b3.zip b/.yarn/cache/minimalistic-crypto-utils-npm-1.0.1-e66b10822e-6e8a0422b3.zip new file mode 100644 index 00000000000..c4225afc009 Binary files /dev/null and b/.yarn/cache/minimalistic-crypto-utils-npm-1.0.1-e66b10822e-6e8a0422b3.zip differ diff --git a/.yarn/cache/minimatch-npm-3.0.4-6e76f51c23-66ac295f8a.zip b/.yarn/cache/minimatch-npm-3.0.4-6e76f51c23-66ac295f8a.zip new file mode 100644 index 00000000000..746542f9ea9 Binary files /dev/null and b/.yarn/cache/minimatch-npm-3.0.4-6e76f51c23-66ac295f8a.zip differ diff --git a/.yarn/cache/minimatch-npm-5.0.0-969101c1d1-810d4165fa.zip b/.yarn/cache/minimatch-npm-5.0.0-969101c1d1-810d4165fa.zip new file mode 100644 index 00000000000..fe6a7b89159 Binary files /dev/null and b/.yarn/cache/minimatch-npm-5.0.0-969101c1d1-810d4165fa.zip differ diff --git a/.yarn/cache/minimist-npm-1.2.5-ced0e1f617-86706ce5b3.zip b/.yarn/cache/minimist-npm-1.2.5-ced0e1f617-86706ce5b3.zip new file mode 100644 index 00000000000..c5b7cfe0b47 Binary files /dev/null and b/.yarn/cache/minimist-npm-1.2.5-ced0e1f617-86706ce5b3.zip differ diff --git a/.yarn/cache/minimist-options-npm-4.1.0-64ca250fc1-8c040b3068.zip b/.yarn/cache/minimist-options-npm-4.1.0-64ca250fc1-8c040b3068.zip new file mode 100644 index 00000000000..192e11c5b4d Binary files /dev/null and b/.yarn/cache/minimist-options-npm-4.1.0-64ca250fc1-8c040b3068.zip differ diff --git a/.yarn/cache/minipass-collect-npm-1.0.2-3b4676eab5-14df761028.zip b/.yarn/cache/minipass-collect-npm-1.0.2-3b4676eab5-14df761028.zip new file mode 100644 index 00000000000..582f61ca2a8 Binary files /dev/null and b/.yarn/cache/minipass-collect-npm-1.0.2-3b4676eab5-14df761028.zip differ diff --git a/.yarn/cache/minipass-fetch-npm-1.4.1-2d67357feb-ec93697bdb.zip b/.yarn/cache/minipass-fetch-npm-1.4.1-2d67357feb-ec93697bdb.zip new file mode 100644 index 00000000000..7670c1f2519 Binary files /dev/null and b/.yarn/cache/minipass-fetch-npm-1.4.1-2d67357feb-ec93697bdb.zip differ diff --git a/.yarn/cache/minipass-flush-npm-1.0.5-efe79d9826-56269a0b22.zip b/.yarn/cache/minipass-flush-npm-1.0.5-efe79d9826-56269a0b22.zip new file mode 100644 index 00000000000..913b687a4d7 Binary files /dev/null and b/.yarn/cache/minipass-flush-npm-1.0.5-efe79d9826-56269a0b22.zip differ diff --git a/.yarn/cache/minipass-json-stream-npm-1.0.1-96490706d6-791b696a27.zip b/.yarn/cache/minipass-json-stream-npm-1.0.1-96490706d6-791b696a27.zip new file mode 100644 index 00000000000..8f95147be40 Binary files /dev/null and b/.yarn/cache/minipass-json-stream-npm-1.0.1-96490706d6-791b696a27.zip differ diff --git a/.yarn/cache/minipass-npm-3.1.6-f032df1661-57a0404141.zip b/.yarn/cache/minipass-npm-3.1.6-f032df1661-57a0404141.zip new file mode 100644 index 00000000000..0f2d4ae31f9 Binary files /dev/null and b/.yarn/cache/minipass-npm-3.1.6-f032df1661-57a0404141.zip differ diff --git a/.yarn/cache/minipass-pipeline-npm-1.2.4-5924cb077f-b14240dac0.zip b/.yarn/cache/minipass-pipeline-npm-1.2.4-5924cb077f-b14240dac0.zip new file mode 100644 index 00000000000..4deae416db7 Binary files /dev/null and b/.yarn/cache/minipass-pipeline-npm-1.2.4-5924cb077f-b14240dac0.zip differ diff --git a/.yarn/cache/minipass-sized-npm-1.0.3-306d86f432-79076749fc.zip b/.yarn/cache/minipass-sized-npm-1.0.3-306d86f432-79076749fc.zip new file mode 100644 index 00000000000..b6f4644f623 Binary files /dev/null and b/.yarn/cache/minipass-sized-npm-1.0.3-306d86f432-79076749fc.zip differ diff --git a/.yarn/cache/minizlib-npm-2.1.2-ea89cd0cfb-f1fdeac0b0.zip b/.yarn/cache/minizlib-npm-2.1.2-ea89cd0cfb-f1fdeac0b0.zip new file mode 100644 index 00000000000..efb1b7f6b69 Binary files /dev/null and b/.yarn/cache/minizlib-npm-2.1.2-ea89cd0cfb-f1fdeac0b0.zip differ diff --git a/.yarn/cache/mkdirp-classic-npm-0.5.3-3b5c991910-3f4e088208.zip b/.yarn/cache/mkdirp-classic-npm-0.5.3-3b5c991910-3f4e088208.zip new file mode 100644 index 00000000000..8663492ed45 Binary files /dev/null and b/.yarn/cache/mkdirp-classic-npm-0.5.3-3b5c991910-3f4e088208.zip differ diff --git a/.yarn/cache/mkdirp-infer-owner-npm-2.0.0-de1fb05d31-d8f4ecd32f.zip b/.yarn/cache/mkdirp-infer-owner-npm-2.0.0-de1fb05d31-d8f4ecd32f.zip new file mode 100644 index 00000000000..e38a4d3d997 Binary files /dev/null and b/.yarn/cache/mkdirp-infer-owner-npm-2.0.0-de1fb05d31-d8f4ecd32f.zip differ diff --git a/.yarn/cache/mkdirp-npm-0.5.5-6bc76534fc-3bce20ea52.zip b/.yarn/cache/mkdirp-npm-0.5.5-6bc76534fc-3bce20ea52.zip new file mode 100644 index 00000000000..c9be0e595fd Binary files /dev/null and b/.yarn/cache/mkdirp-npm-0.5.5-6bc76534fc-3bce20ea52.zip differ diff --git a/.yarn/cache/mkdirp-npm-1.0.4-37f6ef56b9-a96865108c.zip b/.yarn/cache/mkdirp-npm-1.0.4-37f6ef56b9-a96865108c.zip new file mode 100644 index 00000000000..4625e914a4a Binary files /dev/null and b/.yarn/cache/mkdirp-npm-1.0.4-37f6ef56b9-a96865108c.zip differ diff --git a/.yarn/cache/mocha-npm-9.1.3-cf8df742ce-4185038f1d.zip b/.yarn/cache/mocha-npm-9.1.3-cf8df742ce-4185038f1d.zip new file mode 100644 index 00000000000..25a2f7d523a Binary files /dev/null and b/.yarn/cache/mocha-npm-9.1.3-cf8df742ce-4185038f1d.zip differ diff --git a/.yarn/cache/mocha-sinon-npm-2.1.2-8583aedf2f-605cfdd9af.zip b/.yarn/cache/mocha-sinon-npm-2.1.2-8583aedf2f-605cfdd9af.zip new file mode 100644 index 00000000000..289b330e2ec Binary files /dev/null and b/.yarn/cache/mocha-sinon-npm-2.1.2-8583aedf2f-605cfdd9af.zip differ diff --git a/.yarn/cache/modify-values-npm-1.0.1-9b2377e166-8296610c60.zip b/.yarn/cache/modify-values-npm-1.0.1-9b2377e166-8296610c60.zip new file mode 100644 index 00000000000..759e1f53dae Binary files /dev/null and b/.yarn/cache/modify-values-npm-1.0.1-9b2377e166-8296610c60.zip differ diff --git a/.yarn/cache/module-deps-npm-6.2.3-948059fe9d-cccead8f81.zip b/.yarn/cache/module-deps-npm-6.2.3-948059fe9d-cccead8f81.zip new file mode 100644 index 00000000000..26b8036b501 Binary files /dev/null and b/.yarn/cache/module-deps-npm-6.2.3-948059fe9d-cccead8f81.zip differ diff --git a/.yarn/cache/mongodb-npm-3.7.3-c479129d1e-ef7690fe6e.zip b/.yarn/cache/mongodb-npm-3.7.3-c479129d1e-ef7690fe6e.zip new file mode 100644 index 00000000000..8827e1ecdae Binary files /dev/null and b/.yarn/cache/mongodb-npm-3.7.3-c479129d1e-ef7690fe6e.zip differ diff --git a/.yarn/cache/mri-npm-1.1.4-d22a399f26-e65b9aed3b.zip b/.yarn/cache/mri-npm-1.1.4-d22a399f26-e65b9aed3b.zip new file mode 100644 index 00000000000..5eb6997d644 Binary files /dev/null and b/.yarn/cache/mri-npm-1.1.4-d22a399f26-e65b9aed3b.zip differ diff --git a/.yarn/cache/ms-npm-2.0.0-9e1101a471-0e6a22b8b7.zip b/.yarn/cache/ms-npm-2.0.0-9e1101a471-0e6a22b8b7.zip new file mode 100644 index 00000000000..1cb6ffa5d58 Binary files /dev/null and b/.yarn/cache/ms-npm-2.0.0-9e1101a471-0e6a22b8b7.zip differ diff --git a/.yarn/cache/ms-npm-2.1.2-ec0c1512ff-673cdb2c31.zip b/.yarn/cache/ms-npm-2.1.2-ec0c1512ff-673cdb2c31.zip new file mode 100644 index 00000000000..725e9b8c176 Binary files /dev/null and b/.yarn/cache/ms-npm-2.1.2-ec0c1512ff-673cdb2c31.zip differ diff --git a/.yarn/cache/ms-npm-2.1.3-81ff3cfac1-aa92de6080.zip b/.yarn/cache/ms-npm-2.1.3-81ff3cfac1-aa92de6080.zip new file mode 100644 index 00000000000..2b635f28eba Binary files /dev/null and b/.yarn/cache/ms-npm-2.1.3-81ff3cfac1-aa92de6080.zip differ diff --git a/.yarn/cache/multimatch-npm-5.0.0-9938abf6fa-82c8030a53.zip b/.yarn/cache/multimatch-npm-5.0.0-9938abf6fa-82c8030a53.zip new file mode 100644 index 00000000000..ac8e20c55f7 Binary files /dev/null and b/.yarn/cache/multimatch-npm-5.0.0-9938abf6fa-82c8030a53.zip differ diff --git a/.yarn/cache/mute-stream-npm-0.0.8-489a7d6c2b-ff48d251fc.zip b/.yarn/cache/mute-stream-npm-0.0.8-489a7d6c2b-ff48d251fc.zip new file mode 100644 index 00000000000..33156aeabac Binary files /dev/null and b/.yarn/cache/mute-stream-npm-0.0.8-489a7d6c2b-ff48d251fc.zip differ diff --git a/.yarn/cache/nan-npm-2.14.2-e3ede8ce5d-7a269139b6.zip b/.yarn/cache/nan-npm-2.14.2-e3ede8ce5d-7a269139b6.zip new file mode 100644 index 00000000000..9d6c599f165 Binary files /dev/null and b/.yarn/cache/nan-npm-2.14.2-e3ede8ce5d-7a269139b6.zip differ diff --git a/.yarn/cache/nan-npm-2.15.0-505c98ef4d-33e1bb4dfc.zip b/.yarn/cache/nan-npm-2.15.0-505c98ef4d-33e1bb4dfc.zip new file mode 100644 index 00000000000..51c58f139b1 Binary files /dev/null and b/.yarn/cache/nan-npm-2.15.0-505c98ef4d-33e1bb4dfc.zip differ diff --git a/.yarn/cache/nanoid-npm-3.1.25-c8f62ce160-e2353828c7.zip b/.yarn/cache/nanoid-npm-3.1.25-c8f62ce160-e2353828c7.zip new file mode 100644 index 00000000000..173acad33ac Binary files /dev/null and b/.yarn/cache/nanoid-npm-3.1.25-c8f62ce160-e2353828c7.zip differ diff --git a/.yarn/cache/natural-compare-npm-1.4.0-97b75b362d-23ad088b08.zip b/.yarn/cache/natural-compare-npm-1.4.0-97b75b362d-23ad088b08.zip new file mode 100644 index 00000000000..db454c31c56 Binary files /dev/null and b/.yarn/cache/natural-compare-npm-1.4.0-97b75b362d-23ad088b08.zip differ diff --git a/.yarn/cache/natural-orderby-npm-2.0.3-e519eaa77c-039be7f0b6.zip b/.yarn/cache/natural-orderby-npm-2.0.3-e519eaa77c-039be7f0b6.zip new file mode 100644 index 00000000000..14ffa632366 Binary files /dev/null and b/.yarn/cache/natural-orderby-npm-2.0.3-e519eaa77c-039be7f0b6.zip differ diff --git a/.yarn/cache/negotiator-npm-0.6.2-ba538e167a-dfddaff6c0.zip b/.yarn/cache/negotiator-npm-0.6.2-ba538e167a-dfddaff6c0.zip new file mode 100644 index 00000000000..a5031fcece0 Binary files /dev/null and b/.yarn/cache/negotiator-npm-0.6.2-ba538e167a-dfddaff6c0.zip differ diff --git a/.yarn/cache/negotiator-npm-0.6.3-9d50e36171-b8ffeb1e26.zip b/.yarn/cache/negotiator-npm-0.6.3-9d50e36171-b8ffeb1e26.zip new file mode 100644 index 00000000000..e8c5cf48996 Binary files /dev/null and b/.yarn/cache/negotiator-npm-0.6.3-9d50e36171-b8ffeb1e26.zip differ diff --git a/.yarn/cache/neo-async-npm-2.6.2-75d6902586-deac9f8d00.zip b/.yarn/cache/neo-async-npm-2.6.2-75d6902586-deac9f8d00.zip new file mode 100644 index 00000000000..cbf9a7699e0 Binary files /dev/null and b/.yarn/cache/neo-async-npm-2.6.2-75d6902586-deac9f8d00.zip differ diff --git a/.yarn/cache/neon-load-or-build-npm-2.2.2-548a286943-3cafba0e26.zip b/.yarn/cache/neon-load-or-build-npm-2.2.2-548a286943-3cafba0e26.zip new file mode 100644 index 00000000000..96d1001a7cf Binary files /dev/null and b/.yarn/cache/neon-load-or-build-npm-2.2.2-548a286943-3cafba0e26.zip differ diff --git a/.yarn/cache/neon-tag-prebuild-https-d665d28b1f-00a16bf27c.zip b/.yarn/cache/neon-tag-prebuild-https-d665d28b1f-00a16bf27c.zip new file mode 100644 index 00000000000..609ef81c0b0 Binary files /dev/null and b/.yarn/cache/neon-tag-prebuild-https-d665d28b1f-00a16bf27c.zip differ diff --git a/.yarn/cache/net-npm-1.0.2-1d5514df5b-d97e215d92.zip b/.yarn/cache/net-npm-1.0.2-1d5514df5b-d97e215d92.zip new file mode 100644 index 00000000000..e74ce9a1d62 Binary files /dev/null and b/.yarn/cache/net-npm-1.0.2-1d5514df5b-d97e215d92.zip differ diff --git a/.yarn/cache/nice-try-npm-1.0.5-963856b16f-0b4af3b5bb.zip b/.yarn/cache/nice-try-npm-1.0.5-963856b16f-0b4af3b5bb.zip new file mode 100644 index 00000000000..e022a139d68 Binary files /dev/null and b/.yarn/cache/nice-try-npm-1.0.5-963856b16f-0b4af3b5bb.zip differ diff --git a/.yarn/cache/nise-npm-5.1.0-8fc543b66e-e3843cc125.zip b/.yarn/cache/nise-npm-5.1.0-8fc543b66e-e3843cc125.zip new file mode 100644 index 00000000000..dd9576df82f Binary files /dev/null and b/.yarn/cache/nise-npm-5.1.0-8fc543b66e-e3843cc125.zip differ diff --git a/.yarn/cache/no-case-npm-3.0.4-12884c3d98-0b2ebc113d.zip b/.yarn/cache/no-case-npm-3.0.4-12884c3d98-0b2ebc113d.zip new file mode 100644 index 00000000000..1e5347b7f0f Binary files /dev/null and b/.yarn/cache/no-case-npm-3.0.4-12884c3d98-0b2ebc113d.zip differ diff --git a/.yarn/cache/node-abi-npm-2.30.1-36a2c4e28a-3f4b0c912c.zip b/.yarn/cache/node-abi-npm-2.30.1-36a2c4e28a-3f4b0c912c.zip new file mode 100644 index 00000000000..78cb2c39d72 Binary files /dev/null and b/.yarn/cache/node-abi-npm-2.30.1-36a2c4e28a-3f4b0c912c.zip differ diff --git a/.yarn/cache/node-fetch-npm-2.6.7-777aa2a6df-8d816ffd1e.zip b/.yarn/cache/node-fetch-npm-2.6.7-777aa2a6df-8d816ffd1e.zip new file mode 100644 index 00000000000..db222e2a92c Binary files /dev/null and b/.yarn/cache/node-fetch-npm-2.6.7-777aa2a6df-8d816ffd1e.zip differ diff --git a/.yarn/cache/node-graceful-npm-3.1.0-645605305d-159d06ca29.zip b/.yarn/cache/node-graceful-npm-3.1.0-645605305d-159d06ca29.zip new file mode 100644 index 00000000000..74872fcd534 Binary files /dev/null and b/.yarn/cache/node-graceful-npm-3.1.0-645605305d-159d06ca29.zip differ diff --git a/.yarn/cache/node-gyp-build-npm-4.3.0-87bdf5216f-1ecab16d9f.zip b/.yarn/cache/node-gyp-build-npm-4.3.0-87bdf5216f-1ecab16d9f.zip new file mode 100644 index 00000000000..9e7e4405174 Binary files /dev/null and b/.yarn/cache/node-gyp-build-npm-4.3.0-87bdf5216f-1ecab16d9f.zip differ diff --git a/.yarn/cache/node-gyp-npm-8.4.0-ee07b38f64-a5a0045f6a.zip b/.yarn/cache/node-gyp-npm-8.4.0-ee07b38f64-a5a0045f6a.zip new file mode 100644 index 00000000000..34f604b4259 Binary files /dev/null and b/.yarn/cache/node-gyp-npm-8.4.0-ee07b38f64-a5a0045f6a.zip differ diff --git a/.yarn/cache/node-gyp-npm-8.4.1-13c90a9c9b-341710b5da.zip b/.yarn/cache/node-gyp-npm-8.4.1-13c90a9c9b-341710b5da.zip new file mode 100644 index 00000000000..496903f8472 Binary files /dev/null and b/.yarn/cache/node-gyp-npm-8.4.1-13c90a9c9b-341710b5da.zip differ diff --git a/.yarn/cache/node-inspect-extracted-npm-1.0.8-53baa7fd4f-41ecca97d3.zip b/.yarn/cache/node-inspect-extracted-npm-1.0.8-53baa7fd4f-41ecca97d3.zip new file mode 100644 index 00000000000..1a2f3947974 Binary files /dev/null and b/.yarn/cache/node-inspect-extracted-npm-1.0.8-53baa7fd4f-41ecca97d3.zip differ diff --git a/.yarn/cache/node-preload-npm-0.2.1-5b6aef1c8e-4586f91ac7.zip b/.yarn/cache/node-preload-npm-0.2.1-5b6aef1c8e-4586f91ac7.zip new file mode 100644 index 00000000000..231965077c2 Binary files /dev/null and b/.yarn/cache/node-preload-npm-0.2.1-5b6aef1c8e-4586f91ac7.zip differ diff --git a/.yarn/cache/node-releases-npm-2.0.1-77b8e327f7-b20dd8d4bc.zip b/.yarn/cache/node-releases-npm-2.0.1-77b8e327f7-b20dd8d4bc.zip new file mode 100644 index 00000000000..f80933576c9 Binary files /dev/null and b/.yarn/cache/node-releases-npm-2.0.1-77b8e327f7-b20dd8d4bc.zip differ diff --git a/.yarn/cache/nodeforage-npm-1.1.2-38c6ac6257-a670ece8b5.zip b/.yarn/cache/nodeforage-npm-1.1.2-38c6ac6257-a670ece8b5.zip new file mode 100644 index 00000000000..c74ecca2ae9 Binary files /dev/null and b/.yarn/cache/nodeforage-npm-1.1.2-38c6ac6257-a670ece8b5.zip differ diff --git a/.yarn/cache/nodemon-npm-2.0.15-5e88e7aef5-0569b09b71.zip b/.yarn/cache/nodemon-npm-2.0.15-5e88e7aef5-0569b09b71.zip new file mode 100644 index 00000000000..40d0df3920f Binary files /dev/null and b/.yarn/cache/nodemon-npm-2.0.15-5e88e7aef5-0569b09b71.zip differ diff --git a/.yarn/cache/nofilter-npm-3.1.0-3c5ba47d92-58aa85a5b4.zip b/.yarn/cache/nofilter-npm-3.1.0-3c5ba47d92-58aa85a5b4.zip new file mode 100644 index 00000000000..61d740fb024 Binary files /dev/null and b/.yarn/cache/nofilter-npm-3.1.0-3c5ba47d92-58aa85a5b4.zip differ diff --git a/.yarn/cache/nopt-npm-1.0.10-f3db192976-f62575acea.zip b/.yarn/cache/nopt-npm-1.0.10-f3db192976-f62575acea.zip new file mode 100644 index 00000000000..1f5b95d5220 Binary files /dev/null and b/.yarn/cache/nopt-npm-1.0.10-f3db192976-f62575acea.zip differ diff --git a/.yarn/cache/nopt-npm-5.0.0-304b40fbfe-d35fdec187.zip b/.yarn/cache/nopt-npm-5.0.0-304b40fbfe-d35fdec187.zip new file mode 100644 index 00000000000..163bffbb60d Binary files /dev/null and b/.yarn/cache/nopt-npm-5.0.0-304b40fbfe-d35fdec187.zip differ diff --git a/.yarn/cache/normalize-package-data-npm-2.5.0-af0345deed-7999112efc.zip b/.yarn/cache/normalize-package-data-npm-2.5.0-af0345deed-7999112efc.zip new file mode 100644 index 00000000000..829ee1dac0b Binary files /dev/null and b/.yarn/cache/normalize-package-data-npm-2.5.0-af0345deed-7999112efc.zip differ diff --git a/.yarn/cache/normalize-package-data-npm-3.0.3-1a49056685-bbcee00339.zip b/.yarn/cache/normalize-package-data-npm-3.0.3-1a49056685-bbcee00339.zip new file mode 100644 index 00000000000..6f43f29545d Binary files /dev/null and b/.yarn/cache/normalize-package-data-npm-3.0.3-1a49056685-bbcee00339.zip differ diff --git a/.yarn/cache/normalize-path-npm-3.0.0-658ba7d77f-88eeb4da89.zip b/.yarn/cache/normalize-path-npm-3.0.0-658ba7d77f-88eeb4da89.zip new file mode 100644 index 00000000000..855af70e63b Binary files /dev/null and b/.yarn/cache/normalize-path-npm-3.0.0-658ba7d77f-88eeb4da89.zip differ diff --git a/.yarn/cache/normalize-url-npm-4.5.1-603d40bc18-9a9dee01df.zip b/.yarn/cache/normalize-url-npm-4.5.1-603d40bc18-9a9dee01df.zip new file mode 100644 index 00000000000..65664646ca5 Binary files /dev/null and b/.yarn/cache/normalize-url-npm-4.5.1-603d40bc18-9a9dee01df.zip differ diff --git a/.yarn/cache/npm-bundled-npm-1.1.2-e299e533ef-6e599155ef.zip b/.yarn/cache/npm-bundled-npm-1.1.2-e299e533ef-6e599155ef.zip new file mode 100644 index 00000000000..65958555cd9 Binary files /dev/null and b/.yarn/cache/npm-bundled-npm-1.1.2-e299e533ef-6e599155ef.zip differ diff --git a/.yarn/cache/npm-install-checks-npm-4.0.0-4dabe69bc2-8308ff48e6.zip b/.yarn/cache/npm-install-checks-npm-4.0.0-4dabe69bc2-8308ff48e6.zip new file mode 100644 index 00000000000..bdeb39c5e68 Binary files /dev/null and b/.yarn/cache/npm-install-checks-npm-4.0.0-4dabe69bc2-8308ff48e6.zip differ diff --git a/.yarn/cache/npm-normalize-package-bin-npm-1.0.1-2cf38a5d95-ae7f15155a.zip b/.yarn/cache/npm-normalize-package-bin-npm-1.0.1-2cf38a5d95-ae7f15155a.zip new file mode 100644 index 00000000000..e76c781b8f6 Binary files /dev/null and b/.yarn/cache/npm-normalize-package-bin-npm-1.0.1-2cf38a5d95-ae7f15155a.zip differ diff --git a/.yarn/cache/npm-package-arg-npm-8.1.5-02a51cea62-ae76afbceb.zip b/.yarn/cache/npm-package-arg-npm-8.1.5-02a51cea62-ae76afbceb.zip new file mode 100644 index 00000000000..011c0b0a69a Binary files /dev/null and b/.yarn/cache/npm-package-arg-npm-8.1.5-02a51cea62-ae76afbceb.zip differ diff --git a/.yarn/cache/npm-packlist-npm-3.0.0-9671ff7386-8550ecdec5.zip b/.yarn/cache/npm-packlist-npm-3.0.0-9671ff7386-8550ecdec5.zip new file mode 100644 index 00000000000..37854d61bc1 Binary files /dev/null and b/.yarn/cache/npm-packlist-npm-3.0.0-9671ff7386-8550ecdec5.zip differ diff --git a/.yarn/cache/npm-pick-manifest-npm-6.1.1-880ed92d15-7a7b9475ae.zip b/.yarn/cache/npm-pick-manifest-npm-6.1.1-880ed92d15-7a7b9475ae.zip new file mode 100644 index 00000000000..56f60d63f78 Binary files /dev/null and b/.yarn/cache/npm-pick-manifest-npm-6.1.1-880ed92d15-7a7b9475ae.zip differ diff --git a/.yarn/cache/npm-registry-fetch-npm-12.0.2-4e28b8c5f6-88ef49b6fa.zip b/.yarn/cache/npm-registry-fetch-npm-12.0.2-4e28b8c5f6-88ef49b6fa.zip new file mode 100644 index 00000000000..d791e046299 Binary files /dev/null and b/.yarn/cache/npm-registry-fetch-npm-12.0.2-4e28b8c5f6-88ef49b6fa.zip differ diff --git a/.yarn/cache/npm-run-path-npm-2.0.2-96c8b48857-acd5ad8164.zip b/.yarn/cache/npm-run-path-npm-2.0.2-96c8b48857-acd5ad8164.zip new file mode 100644 index 00000000000..dae249c86d8 Binary files /dev/null and b/.yarn/cache/npm-run-path-npm-2.0.2-96c8b48857-acd5ad8164.zip differ diff --git a/.yarn/cache/npm-run-path-npm-4.0.1-7aebd8bab3-5374c0cea4.zip b/.yarn/cache/npm-run-path-npm-4.0.1-7aebd8bab3-5374c0cea4.zip new file mode 100644 index 00000000000..18ef7040d5c Binary files /dev/null and b/.yarn/cache/npm-run-path-npm-4.0.1-7aebd8bab3-5374c0cea4.zip differ diff --git a/.yarn/cache/npmlog-npm-4.1.2-cfb32957b5-edbda9f95e.zip b/.yarn/cache/npmlog-npm-4.1.2-cfb32957b5-edbda9f95e.zip new file mode 100644 index 00000000000..15a8695aabd Binary files /dev/null and b/.yarn/cache/npmlog-npm-4.1.2-cfb32957b5-edbda9f95e.zip differ diff --git a/.yarn/cache/npmlog-npm-5.0.1-366cab64a2-516b266302.zip b/.yarn/cache/npmlog-npm-5.0.1-366cab64a2-516b266302.zip new file mode 100644 index 00000000000..d2eec072ea8 Binary files /dev/null and b/.yarn/cache/npmlog-npm-5.0.1-366cab64a2-516b266302.zip differ diff --git a/.yarn/cache/npmlog-npm-6.0.1-f597f2e057-f1a4078a73.zip b/.yarn/cache/npmlog-npm-6.0.1-f597f2e057-f1a4078a73.zip new file mode 100644 index 00000000000..42b9a3c09a1 Binary files /dev/null and b/.yarn/cache/npmlog-npm-6.0.1-f597f2e057-f1a4078a73.zip differ diff --git a/.yarn/cache/number-is-nan-npm-1.0.1-845325a0fe-13656bc9aa.zip b/.yarn/cache/number-is-nan-npm-1.0.1-845325a0fe-13656bc9aa.zip new file mode 100644 index 00000000000..4ef9a25659d Binary files /dev/null and b/.yarn/cache/number-is-nan-npm-1.0.1-845325a0fe-13656bc9aa.zip differ diff --git a/.yarn/cache/nyc-npm-15.1.0-f134b19668-82a7031982.zip b/.yarn/cache/nyc-npm-15.1.0-f134b19668-82a7031982.zip new file mode 100644 index 00000000000..6df8022792a Binary files /dev/null and b/.yarn/cache/nyc-npm-15.1.0-f134b19668-82a7031982.zip differ diff --git a/.yarn/cache/oauth-sign-npm-0.9.0-7aa9422221-8f5497a127.zip b/.yarn/cache/oauth-sign-npm-0.9.0-7aa9422221-8f5497a127.zip new file mode 100644 index 00000000000..04d5896f778 Binary files /dev/null and b/.yarn/cache/oauth-sign-npm-0.9.0-7aa9422221-8f5497a127.zip differ diff --git a/.yarn/cache/object-assign-npm-4.1.1-1004ad6dec-fcc6e4ea8c.zip b/.yarn/cache/object-assign-npm-4.1.1-1004ad6dec-fcc6e4ea8c.zip new file mode 100644 index 00000000000..8c8ab03b1b7 Binary files /dev/null and b/.yarn/cache/object-assign-npm-4.1.1-1004ad6dec-fcc6e4ea8c.zip differ diff --git a/.yarn/cache/object-inspect-npm-1.11.0-c9d4bd1487-8c64f89ce3.zip b/.yarn/cache/object-inspect-npm-1.11.0-c9d4bd1487-8c64f89ce3.zip new file mode 100644 index 00000000000..ad7eb8319e7 Binary files /dev/null and b/.yarn/cache/object-inspect-npm-1.11.0-c9d4bd1487-8c64f89ce3.zip differ diff --git a/.yarn/cache/object-is-npm-1.1.5-48a862602b-989b18c4cb.zip b/.yarn/cache/object-is-npm-1.1.5-48a862602b-989b18c4cb.zip new file mode 100644 index 00000000000..9968bdd5f62 Binary files /dev/null and b/.yarn/cache/object-is-npm-1.1.5-48a862602b-989b18c4cb.zip differ diff --git a/.yarn/cache/object-keys-npm-1.1.1-1bf2f1be93-b363c5e764.zip b/.yarn/cache/object-keys-npm-1.1.1-1bf2f1be93-b363c5e764.zip new file mode 100644 index 00000000000..34022827ec9 Binary files /dev/null and b/.yarn/cache/object-keys-npm-1.1.1-1bf2f1be93-b363c5e764.zip differ diff --git a/.yarn/cache/object-treeify-npm-1.1.33-2273de9233-3af7f88934.zip b/.yarn/cache/object-treeify-npm-1.1.33-2273de9233-3af7f88934.zip new file mode 100644 index 00000000000..1da2e62b483 Binary files /dev/null and b/.yarn/cache/object-treeify-npm-1.1.33-2273de9233-3af7f88934.zip differ diff --git a/.yarn/cache/object.assign-npm-4.1.2-d52edada1c-d621d832ed.zip b/.yarn/cache/object.assign-npm-4.1.2-d52edada1c-d621d832ed.zip new file mode 100644 index 00000000000..0031b978160 Binary files /dev/null and b/.yarn/cache/object.assign-npm-4.1.2-d52edada1c-d621d832ed.zip differ diff --git a/.yarn/cache/object.entries-npm-1.1.5-7a8fcbc43e-d658696f74.zip b/.yarn/cache/object.entries-npm-1.1.5-7a8fcbc43e-d658696f74.zip new file mode 100644 index 00000000000..716d52aa78f Binary files /dev/null and b/.yarn/cache/object.entries-npm-1.1.5-7a8fcbc43e-d658696f74.zip differ diff --git a/.yarn/cache/object.values-npm-1.1.5-f1de7f3742-0f17e99741.zip b/.yarn/cache/object.values-npm-1.1.5-f1de7f3742-0f17e99741.zip new file mode 100644 index 00000000000..e03d02d7ddd Binary files /dev/null and b/.yarn/cache/object.values-npm-1.1.5-f1de7f3742-0f17e99741.zip differ diff --git a/.yarn/cache/oclif-npm-2.4.5-2547df0920-8c8901c5f3.zip b/.yarn/cache/oclif-npm-2.4.5-2547df0920-8c8901c5f3.zip new file mode 100644 index 00000000000..3f9f006528e Binary files /dev/null and b/.yarn/cache/oclif-npm-2.4.5-2547df0920-8c8901c5f3.zip differ diff --git a/.yarn/cache/on-finished-npm-2.3.0-4ce92f72c6-1db595bd96.zip b/.yarn/cache/on-finished-npm-2.3.0-4ce92f72c6-1db595bd96.zip new file mode 100644 index 00000000000..3afaa2a9b0f Binary files /dev/null and b/.yarn/cache/on-finished-npm-2.3.0-4ce92f72c6-1db595bd96.zip differ diff --git a/.yarn/cache/once-npm-1.4.0-ccf03ef07a-cd0a885013.zip b/.yarn/cache/once-npm-1.4.0-ccf03ef07a-cd0a885013.zip new file mode 100644 index 00000000000..1b943eec95a Binary files /dev/null and b/.yarn/cache/once-npm-1.4.0-ccf03ef07a-cd0a885013.zip differ diff --git a/.yarn/cache/one-time-npm-1.0.0-aeaad5e524-fd008d7e99.zip b/.yarn/cache/one-time-npm-1.0.0-aeaad5e524-fd008d7e99.zip new file mode 100644 index 00000000000..59188f657a2 Binary files /dev/null and b/.yarn/cache/one-time-npm-1.0.0-aeaad5e524-fd008d7e99.zip differ diff --git a/.yarn/cache/onetime-npm-5.1.2-3ed148fa42-2478859ef8.zip b/.yarn/cache/onetime-npm-5.1.2-3ed148fa42-2478859ef8.zip new file mode 100644 index 00000000000..958e05b7dd9 Binary files /dev/null and b/.yarn/cache/onetime-npm-5.1.2-3ed148fa42-2478859ef8.zip differ diff --git a/.yarn/cache/ono-npm-6.0.1-088f000ca0-182db954b7.zip b/.yarn/cache/ono-npm-6.0.1-088f000ca0-182db954b7.zip new file mode 100644 index 00000000000..31cf602e8ee Binary files /dev/null and b/.yarn/cache/ono-npm-6.0.1-088f000ca0-182db954b7.zip differ diff --git a/.yarn/cache/openapi-schemas-npm-1.0.3-d820c175a8-170dbf4d10.zip b/.yarn/cache/openapi-schemas-npm-1.0.3-d820c175a8-170dbf4d10.zip new file mode 100644 index 00000000000..91420155439 Binary files /dev/null and b/.yarn/cache/openapi-schemas-npm-1.0.3-d820c175a8-170dbf4d10.zip differ diff --git a/.yarn/cache/openapi-types-npm-1.3.5-f765461ce7-c2d20ea228.zip b/.yarn/cache/openapi-types-npm-1.3.5-f765461ce7-c2d20ea228.zip new file mode 100644 index 00000000000..5d2441648d8 Binary files /dev/null and b/.yarn/cache/openapi-types-npm-1.3.5-f765461ce7-c2d20ea228.zip differ diff --git a/.yarn/cache/optional-require-npm-1.1.8-b94e3971c9-437db76f71.zip b/.yarn/cache/optional-require-npm-1.1.8-b94e3971c9-437db76f71.zip new file mode 100644 index 00000000000..5342f381b2a Binary files /dev/null and b/.yarn/cache/optional-require-npm-1.1.8-b94e3971c9-437db76f71.zip differ diff --git a/.yarn/cache/optionator-npm-0.8.3-bc555bc5b7-b8695ddf3d.zip b/.yarn/cache/optionator-npm-0.8.3-bc555bc5b7-b8695ddf3d.zip new file mode 100644 index 00000000000..9e9590b2c10 Binary files /dev/null and b/.yarn/cache/optionator-npm-0.8.3-bc555bc5b7-b8695ddf3d.zip differ diff --git a/.yarn/cache/optionator-npm-0.9.1-577e397aae-dbc6fa0656.zip b/.yarn/cache/optionator-npm-0.9.1-577e397aae-dbc6fa0656.zip new file mode 100644 index 00000000000..6e6efe345ba Binary files /dev/null and b/.yarn/cache/optionator-npm-0.9.1-577e397aae-dbc6fa0656.zip differ diff --git a/.yarn/cache/ora-npm-5.4.1-4f0343adb7-28d476ee6c.zip b/.yarn/cache/ora-npm-5.4.1-4f0343adb7-28d476ee6c.zip new file mode 100644 index 00000000000..11eecc63e3e Binary files /dev/null and b/.yarn/cache/ora-npm-5.4.1-4f0343adb7-28d476ee6c.zip differ diff --git a/.yarn/cache/os-browserify-npm-0.3.0-cbc91c79a5-16e37ba3c0.zip b/.yarn/cache/os-browserify-npm-0.3.0-cbc91c79a5-16e37ba3c0.zip new file mode 100644 index 00000000000..b30422a9dc0 Binary files /dev/null and b/.yarn/cache/os-browserify-npm-0.3.0-cbc91c79a5-16e37ba3c0.zip differ diff --git a/.yarn/cache/os-tmpdir-npm-1.0.2-e305b0689b-5666560f7b.zip b/.yarn/cache/os-tmpdir-npm-1.0.2-e305b0689b-5666560f7b.zip new file mode 100644 index 00000000000..d68d7106382 Binary files /dev/null and b/.yarn/cache/os-tmpdir-npm-1.0.2-e305b0689b-5666560f7b.zip differ diff --git a/.yarn/cache/p-cancelable-npm-1.1.0-d147d5996f-2db3814fef.zip b/.yarn/cache/p-cancelable-npm-1.1.0-d147d5996f-2db3814fef.zip new file mode 100644 index 00000000000..19c7d3aa421 Binary files /dev/null and b/.yarn/cache/p-cancelable-npm-1.1.0-d147d5996f-2db3814fef.zip differ diff --git a/.yarn/cache/p-finally-npm-1.0.0-35fbaa57c6-93a654c53d.zip b/.yarn/cache/p-finally-npm-1.0.0-35fbaa57c6-93a654c53d.zip new file mode 100644 index 00000000000..091273a2af0 Binary files /dev/null and b/.yarn/cache/p-finally-npm-1.0.0-35fbaa57c6-93a654c53d.zip differ diff --git a/.yarn/cache/p-limit-npm-1.3.0-fdb471d864-281c1c0b8c.zip b/.yarn/cache/p-limit-npm-1.3.0-fdb471d864-281c1c0b8c.zip new file mode 100644 index 00000000000..96906babdc5 Binary files /dev/null and b/.yarn/cache/p-limit-npm-1.3.0-fdb471d864-281c1c0b8c.zip differ diff --git a/.yarn/cache/p-limit-npm-2.3.0-94a0310039-84ff17f1a3.zip b/.yarn/cache/p-limit-npm-2.3.0-94a0310039-84ff17f1a3.zip new file mode 100644 index 00000000000..099c3a07e03 Binary files /dev/null and b/.yarn/cache/p-limit-npm-2.3.0-94a0310039-84ff17f1a3.zip differ diff --git a/.yarn/cache/p-limit-npm-3.1.0-05d2ede37f-7c3690c4db.zip b/.yarn/cache/p-limit-npm-3.1.0-05d2ede37f-7c3690c4db.zip new file mode 100644 index 00000000000..b87d97ccf80 Binary files /dev/null and b/.yarn/cache/p-limit-npm-3.1.0-05d2ede37f-7c3690c4db.zip differ diff --git a/.yarn/cache/p-locate-npm-2.0.0-3a2ee263dd-e2dceb9b49.zip b/.yarn/cache/p-locate-npm-2.0.0-3a2ee263dd-e2dceb9b49.zip new file mode 100644 index 00000000000..f6f9f09b9ea Binary files /dev/null and b/.yarn/cache/p-locate-npm-2.0.0-3a2ee263dd-e2dceb9b49.zip differ diff --git a/.yarn/cache/p-locate-npm-4.1.0-eec6872537-513bd14a45.zip b/.yarn/cache/p-locate-npm-4.1.0-eec6872537-513bd14a45.zip new file mode 100644 index 00000000000..bf0aef9ee19 Binary files /dev/null and b/.yarn/cache/p-locate-npm-4.1.0-eec6872537-513bd14a45.zip differ diff --git a/.yarn/cache/p-locate-npm-5.0.0-92cc7c7a3e-1623088f36.zip b/.yarn/cache/p-locate-npm-5.0.0-92cc7c7a3e-1623088f36.zip new file mode 100644 index 00000000000..077f1c6eeba Binary files /dev/null and b/.yarn/cache/p-locate-npm-5.0.0-92cc7c7a3e-1623088f36.zip differ diff --git a/.yarn/cache/p-map-npm-3.0.0-e4f17c4167-49b0fcbc66.zip b/.yarn/cache/p-map-npm-3.0.0-e4f17c4167-49b0fcbc66.zip new file mode 100644 index 00000000000..cb604862fb4 Binary files /dev/null and b/.yarn/cache/p-map-npm-3.0.0-e4f17c4167-49b0fcbc66.zip differ diff --git a/.yarn/cache/p-map-npm-4.0.0-4677ae07c7-cb0ab21ec0.zip b/.yarn/cache/p-map-npm-4.0.0-4677ae07c7-cb0ab21ec0.zip new file mode 100644 index 00000000000..092fe42ff79 Binary files /dev/null and b/.yarn/cache/p-map-npm-4.0.0-4677ae07c7-cb0ab21ec0.zip differ diff --git a/.yarn/cache/p-queue-npm-6.6.2-b173c5bfa8-832642fcc4.zip b/.yarn/cache/p-queue-npm-6.6.2-b173c5bfa8-832642fcc4.zip new file mode 100644 index 00000000000..da69f7750a9 Binary files /dev/null and b/.yarn/cache/p-queue-npm-6.6.2-b173c5bfa8-832642fcc4.zip differ diff --git a/.yarn/cache/p-timeout-npm-3.2.0-7fdb33f733-3dd0eaa048.zip b/.yarn/cache/p-timeout-npm-3.2.0-7fdb33f733-3dd0eaa048.zip new file mode 100644 index 00000000000..eaf8f71c75c Binary files /dev/null and b/.yarn/cache/p-timeout-npm-3.2.0-7fdb33f733-3dd0eaa048.zip differ diff --git a/.yarn/cache/p-transform-npm-1.3.0-99cf79f22a-d1e2d6ad75.zip b/.yarn/cache/p-transform-npm-1.3.0-99cf79f22a-d1e2d6ad75.zip new file mode 100644 index 00000000000..d3f68804cf4 Binary files /dev/null and b/.yarn/cache/p-transform-npm-1.3.0-99cf79f22a-d1e2d6ad75.zip differ diff --git a/.yarn/cache/p-try-npm-1.0.0-7373139e40-3b5303f77e.zip b/.yarn/cache/p-try-npm-1.0.0-7373139e40-3b5303f77e.zip new file mode 100644 index 00000000000..e12bd247e1a Binary files /dev/null and b/.yarn/cache/p-try-npm-1.0.0-7373139e40-3b5303f77e.zip differ diff --git a/.yarn/cache/p-try-npm-2.2.0-e0390dbaf8-f8a8e9a769.zip b/.yarn/cache/p-try-npm-2.2.0-e0390dbaf8-f8a8e9a769.zip new file mode 100644 index 00000000000..bdcd88a3958 Binary files /dev/null and b/.yarn/cache/p-try-npm-2.2.0-e0390dbaf8-f8a8e9a769.zip differ diff --git a/.yarn/cache/package-hash-npm-4.0.0-1e83d2429d-32c49e3a0e.zip b/.yarn/cache/package-hash-npm-4.0.0-1e83d2429d-32c49e3a0e.zip new file mode 100644 index 00000000000..417b6fcdff1 Binary files /dev/null and b/.yarn/cache/package-hash-npm-4.0.0-1e83d2429d-32c49e3a0e.zip differ diff --git a/.yarn/cache/package-json-npm-6.5.0-30e58237bb-cc9f890d36.zip b/.yarn/cache/package-json-npm-6.5.0-30e58237bb-cc9f890d36.zip new file mode 100644 index 00000000000..c6a25913385 Binary files /dev/null and b/.yarn/cache/package-json-npm-6.5.0-30e58237bb-cc9f890d36.zip differ diff --git a/.yarn/cache/pacote-npm-12.0.3-99a2ca9e19-730e2b3446.zip b/.yarn/cache/pacote-npm-12.0.3-99a2ca9e19-730e2b3446.zip new file mode 100644 index 00000000000..9499e4014fd Binary files /dev/null and b/.yarn/cache/pacote-npm-12.0.3-99a2ca9e19-730e2b3446.zip differ diff --git a/.yarn/cache/pad-component-npm-0.0.1-96c929da6f-2d92ad68b6.zip b/.yarn/cache/pad-component-npm-0.0.1-96c929da6f-2d92ad68b6.zip new file mode 100644 index 00000000000..51c75891e22 Binary files /dev/null and b/.yarn/cache/pad-component-npm-0.0.1-96c929da6f-2d92ad68b6.zip differ diff --git a/.yarn/cache/pako-npm-1.0.11-b8f1b69d3e-1be2bfa1f8.zip b/.yarn/cache/pako-npm-1.0.11-b8f1b69d3e-1be2bfa1f8.zip new file mode 100644 index 00000000000..4a6767ba86d Binary files /dev/null and b/.yarn/cache/pako-npm-1.0.11-b8f1b69d3e-1be2bfa1f8.zip differ diff --git a/.yarn/cache/parent-module-npm-1.0.1-1fae11b095-6ba8b25514.zip b/.yarn/cache/parent-module-npm-1.0.1-1fae11b095-6ba8b25514.zip new file mode 100644 index 00000000000..5b900e17fba Binary files /dev/null and b/.yarn/cache/parent-module-npm-1.0.1-1fae11b095-6ba8b25514.zip differ diff --git a/.yarn/cache/parents-npm-1.0.1-2009842484-094fc817d5.zip b/.yarn/cache/parents-npm-1.0.1-2009842484-094fc817d5.zip new file mode 100644 index 00000000000..22484d43b1e Binary files /dev/null and b/.yarn/cache/parents-npm-1.0.1-2009842484-094fc817d5.zip differ diff --git a/.yarn/cache/parse-asn1-npm-5.1.6-6cc3a6eeae-9243311d1f.zip b/.yarn/cache/parse-asn1-npm-5.1.6-6cc3a6eeae-9243311d1f.zip new file mode 100644 index 00000000000..f67be7dd7c7 Binary files /dev/null and b/.yarn/cache/parse-asn1-npm-5.1.6-6cc3a6eeae-9243311d1f.zip differ diff --git a/.yarn/cache/parse-conflict-json-npm-2.0.1-7cdcd9a753-398728731f.zip b/.yarn/cache/parse-conflict-json-npm-2.0.1-7cdcd9a753-398728731f.zip new file mode 100644 index 00000000000..0ea0ac9ae27 Binary files /dev/null and b/.yarn/cache/parse-conflict-json-npm-2.0.1-7cdcd9a753-398728731f.zip differ diff --git a/.yarn/cache/parse-json-npm-4.0.0-a6f7771010-0fe227d410.zip b/.yarn/cache/parse-json-npm-4.0.0-a6f7771010-0fe227d410.zip new file mode 100644 index 00000000000..4832780ee93 Binary files /dev/null and b/.yarn/cache/parse-json-npm-4.0.0-a6f7771010-0fe227d410.zip differ diff --git a/.yarn/cache/parse-json-npm-5.2.0-00a63b1199-62085b17d6.zip b/.yarn/cache/parse-json-npm-5.2.0-00a63b1199-62085b17d6.zip new file mode 100644 index 00000000000..141b5217132 Binary files /dev/null and b/.yarn/cache/parse-json-npm-5.2.0-00a63b1199-62085b17d6.zip differ diff --git a/.yarn/cache/parse-ms-npm-2.1.0-de852c39bb-d5c66c76cc.zip b/.yarn/cache/parse-ms-npm-2.1.0-de852c39bb-d5c66c76cc.zip new file mode 100644 index 00000000000..e4949cde813 Binary files /dev/null and b/.yarn/cache/parse-ms-npm-2.1.0-de852c39bb-d5c66c76cc.zip differ diff --git a/.yarn/cache/parseurl-npm-1.3.3-1542397e00-407cee8e0a.zip b/.yarn/cache/parseurl-npm-1.3.3-1542397e00-407cee8e0a.zip new file mode 100644 index 00000000000..794eb17d7fd Binary files /dev/null and b/.yarn/cache/parseurl-npm-1.3.3-1542397e00-407cee8e0a.zip differ diff --git a/.yarn/cache/pascal-case-npm-3.1.2-35f5b9bff6-ba98bfd595.zip b/.yarn/cache/pascal-case-npm-3.1.2-35f5b9bff6-ba98bfd595.zip new file mode 100644 index 00000000000..fc44c753779 Binary files /dev/null and b/.yarn/cache/pascal-case-npm-3.1.2-35f5b9bff6-ba98bfd595.zip differ diff --git a/.yarn/cache/password-prompt-npm-1.1.2-086b60f9fe-4763ec1b48.zip b/.yarn/cache/password-prompt-npm-1.1.2-086b60f9fe-4763ec1b48.zip new file mode 100644 index 00000000000..1bda08ebce4 Binary files /dev/null and b/.yarn/cache/password-prompt-npm-1.1.2-086b60f9fe-4763ec1b48.zip differ diff --git a/.yarn/cache/path-browserify-npm-0.0.1-bb8b2a97b1-ae8dcd45d0.zip b/.yarn/cache/path-browserify-npm-0.0.1-bb8b2a97b1-ae8dcd45d0.zip new file mode 100644 index 00000000000..a2ffa13a25f Binary files /dev/null and b/.yarn/cache/path-browserify-npm-0.0.1-bb8b2a97b1-ae8dcd45d0.zip differ diff --git a/.yarn/cache/path-browserify-npm-1.0.1-f975d99a99-c6d7fa3764.zip b/.yarn/cache/path-browserify-npm-1.0.1-f975d99a99-c6d7fa3764.zip new file mode 100644 index 00000000000..7c06907e503 Binary files /dev/null and b/.yarn/cache/path-browserify-npm-1.0.1-f975d99a99-c6d7fa3764.zip differ diff --git a/.yarn/cache/path-exists-npm-3.0.0-e80371aa68-96e92643aa.zip b/.yarn/cache/path-exists-npm-3.0.0-e80371aa68-96e92643aa.zip new file mode 100644 index 00000000000..bdaa46fd30e Binary files /dev/null and b/.yarn/cache/path-exists-npm-3.0.0-e80371aa68-96e92643aa.zip differ diff --git a/.yarn/cache/path-exists-npm-4.0.0-e9e4f63eb0-505807199d.zip b/.yarn/cache/path-exists-npm-4.0.0-e9e4f63eb0-505807199d.zip new file mode 100644 index 00000000000..b5048416993 Binary files /dev/null and b/.yarn/cache/path-exists-npm-4.0.0-e9e4f63eb0-505807199d.zip differ diff --git a/.yarn/cache/path-is-absolute-npm-1.0.1-31bc695ffd-060840f92c.zip b/.yarn/cache/path-is-absolute-npm-1.0.1-31bc695ffd-060840f92c.zip new file mode 100644 index 00000000000..ce195de7058 Binary files /dev/null and b/.yarn/cache/path-is-absolute-npm-1.0.1-31bc695ffd-060840f92c.zip differ diff --git a/.yarn/cache/path-key-npm-2.0.1-b1a971833d-f7ab0ad42f.zip b/.yarn/cache/path-key-npm-2.0.1-b1a971833d-f7ab0ad42f.zip new file mode 100644 index 00000000000..39c58f4af10 Binary files /dev/null and b/.yarn/cache/path-key-npm-2.0.1-b1a971833d-f7ab0ad42f.zip differ diff --git a/.yarn/cache/path-key-npm-3.1.1-0e66ea8321-55cd7a9dd4.zip b/.yarn/cache/path-key-npm-3.1.1-0e66ea8321-55cd7a9dd4.zip new file mode 100644 index 00000000000..dd7212e2cd4 Binary files /dev/null and b/.yarn/cache/path-key-npm-3.1.1-0e66ea8321-55cd7a9dd4.zip differ diff --git a/.yarn/cache/path-parse-npm-1.0.7-09564527b7-49abf3d811.zip b/.yarn/cache/path-parse-npm-1.0.7-09564527b7-49abf3d811.zip new file mode 100644 index 00000000000..30362e2c384 Binary files /dev/null and b/.yarn/cache/path-parse-npm-1.0.7-09564527b7-49abf3d811.zip differ diff --git a/.yarn/cache/path-platform-npm-0.11.15-8cf3865ad1-239f2eae72.zip b/.yarn/cache/path-platform-npm-0.11.15-8cf3865ad1-239f2eae72.zip new file mode 100644 index 00000000000..af6ff835df6 Binary files /dev/null and b/.yarn/cache/path-platform-npm-0.11.15-8cf3865ad1-239f2eae72.zip differ diff --git a/.yarn/cache/path-to-regexp-npm-1.8.0-a1904f5c44-709f6f083c.zip b/.yarn/cache/path-to-regexp-npm-1.8.0-a1904f5c44-709f6f083c.zip new file mode 100644 index 00000000000..3528453a618 Binary files /dev/null and b/.yarn/cache/path-to-regexp-npm-1.8.0-a1904f5c44-709f6f083c.zip differ diff --git a/.yarn/cache/path-type-npm-3.0.0-252361a0eb-735b35e256.zip b/.yarn/cache/path-type-npm-3.0.0-252361a0eb-735b35e256.zip new file mode 100644 index 00000000000..3a59d9b0e3f Binary files /dev/null and b/.yarn/cache/path-type-npm-3.0.0-252361a0eb-735b35e256.zip differ diff --git a/.yarn/cache/path-type-npm-4.0.0-10d47fc86a-5b1e2daa24.zip b/.yarn/cache/path-type-npm-4.0.0-10d47fc86a-5b1e2daa24.zip new file mode 100644 index 00000000000..f37ca5bcc1a Binary files /dev/null and b/.yarn/cache/path-type-npm-4.0.0-10d47fc86a-5b1e2daa24.zip differ diff --git a/.yarn/cache/pathval-npm-1.1.1-ce0311d7e0-090e314771.zip b/.yarn/cache/pathval-npm-1.1.1-ce0311d7e0-090e314771.zip new file mode 100644 index 00000000000..b5cdc46250e Binary files /dev/null and b/.yarn/cache/pathval-npm-1.1.1-ce0311d7e0-090e314771.zip differ diff --git a/.yarn/cache/pbkdf2-npm-3.1.2-d67bbb584f-2c950a100b.zip b/.yarn/cache/pbkdf2-npm-3.1.2-d67bbb584f-2c950a100b.zip new file mode 100644 index 00000000000..ac14daba0d5 Binary files /dev/null and b/.yarn/cache/pbkdf2-npm-3.1.2-d67bbb584f-2c950a100b.zip differ diff --git a/.yarn/cache/performance-now-npm-2.1.0-45e3ce7e49-534e641aa8.zip b/.yarn/cache/performance-now-npm-2.1.0-45e3ce7e49-534e641aa8.zip new file mode 100644 index 00000000000..fa9ee04fea4 Binary files /dev/null and b/.yarn/cache/performance-now-npm-2.1.0-45e3ce7e49-534e641aa8.zip differ diff --git a/.yarn/cache/picocolors-npm-1.0.0-d81e0b1927-a2e8092dd8.zip b/.yarn/cache/picocolors-npm-1.0.0-d81e0b1927-a2e8092dd8.zip new file mode 100644 index 00000000000..2d7c3d573a5 Binary files /dev/null and b/.yarn/cache/picocolors-npm-1.0.0-d81e0b1927-a2e8092dd8.zip differ diff --git a/.yarn/cache/picomatch-npm-2.3.0-5e60e6c82d-16818720ea.zip b/.yarn/cache/picomatch-npm-2.3.0-5e60e6c82d-16818720ea.zip new file mode 100644 index 00000000000..d410471cc6a Binary files /dev/null and b/.yarn/cache/picomatch-npm-2.3.0-5e60e6c82d-16818720ea.zip differ diff --git a/.yarn/cache/pid-cwd-npm-1.2.0-c7bf6feeb4-5a7872f39b.zip b/.yarn/cache/pid-cwd-npm-1.2.0-c7bf6feeb4-5a7872f39b.zip new file mode 100644 index 00000000000..63d5a1b9e09 Binary files /dev/null and b/.yarn/cache/pid-cwd-npm-1.2.0-c7bf6feeb4-5a7872f39b.zip differ diff --git a/.yarn/cache/pify-npm-2.3.0-8b63310934-9503aaeaf4.zip b/.yarn/cache/pify-npm-2.3.0-8b63310934-9503aaeaf4.zip new file mode 100644 index 00000000000..4cbc70a0abe Binary files /dev/null and b/.yarn/cache/pify-npm-2.3.0-8b63310934-9503aaeaf4.zip differ diff --git a/.yarn/cache/pify-npm-3.0.0-679ee405c8-6cdcbc3567.zip b/.yarn/cache/pify-npm-3.0.0-679ee405c8-6cdcbc3567.zip new file mode 100644 index 00000000000..95bf84187da Binary files /dev/null and b/.yarn/cache/pify-npm-3.0.0-679ee405c8-6cdcbc3567.zip differ diff --git a/.yarn/cache/pify-npm-4.0.1-062756097b-9c4e34278c.zip b/.yarn/cache/pify-npm-4.0.1-062756097b-9c4e34278c.zip new file mode 100644 index 00000000000..817aa876022 Binary files /dev/null and b/.yarn/cache/pify-npm-4.0.1-062756097b-9c4e34278c.zip differ diff --git a/.yarn/cache/pino-multi-stream-npm-5.3.0-ecb9b754cb-10ddb85983.zip b/.yarn/cache/pino-multi-stream-npm-5.3.0-ecb9b754cb-10ddb85983.zip new file mode 100644 index 00000000000..ff8066cad1a Binary files /dev/null and b/.yarn/cache/pino-multi-stream-npm-5.3.0-ecb9b754cb-10ddb85983.zip differ diff --git a/.yarn/cache/pino-npm-6.13.3-50e2aceb53-a580decd47.zip b/.yarn/cache/pino-npm-6.13.3-50e2aceb53-a580decd47.zip new file mode 100644 index 00000000000..3b894655171 Binary files /dev/null and b/.yarn/cache/pino-npm-6.13.3-50e2aceb53-a580decd47.zip differ diff --git a/.yarn/cache/pino-pretty-npm-4.8.0-0c822e28cb-8e2e4cdb80.zip b/.yarn/cache/pino-pretty-npm-4.8.0-0c822e28cb-8e2e4cdb80.zip new file mode 100644 index 00000000000..2c078dd27f1 Binary files /dev/null and b/.yarn/cache/pino-pretty-npm-4.8.0-0c822e28cb-8e2e4cdb80.zip differ diff --git a/.yarn/cache/pino-std-serializers-npm-3.2.0-9fd67503a4-77e29675b1.zip b/.yarn/cache/pino-std-serializers-npm-3.2.0-9fd67503a4-77e29675b1.zip new file mode 100644 index 00000000000..fa0c61ed0b2 Binary files /dev/null and b/.yarn/cache/pino-std-serializers-npm-3.2.0-9fd67503a4-77e29675b1.zip differ diff --git a/.yarn/cache/pkg-dir-npm-2.0.0-2b4bf4abd1-8c72b71230.zip b/.yarn/cache/pkg-dir-npm-2.0.0-2b4bf4abd1-8c72b71230.zip new file mode 100644 index 00000000000..166c7b8a03e Binary files /dev/null and b/.yarn/cache/pkg-dir-npm-2.0.0-2b4bf4abd1-8c72b71230.zip differ diff --git a/.yarn/cache/pkg-dir-npm-4.2.0-2b5d0a8d32-9863e3f351.zip b/.yarn/cache/pkg-dir-npm-4.2.0-2b5d0a8d32-9863e3f351.zip new file mode 100644 index 00000000000..4718605f435 Binary files /dev/null and b/.yarn/cache/pkg-dir-npm-4.2.0-2b5d0a8d32-9863e3f351.zip differ diff --git a/.yarn/cache/preferred-pm-npm-3.0.3-68a4791e4b-0de0948cb6.zip b/.yarn/cache/preferred-pm-npm-3.0.3-68a4791e4b-0de0948cb6.zip new file mode 100644 index 00000000000..5e0ad900036 Binary files /dev/null and b/.yarn/cache/preferred-pm-npm-3.0.3-68a4791e4b-0de0948cb6.zip differ diff --git a/.yarn/cache/prelude-ls-npm-1.1.2-a0daac0886-c4867c8748.zip b/.yarn/cache/prelude-ls-npm-1.1.2-a0daac0886-c4867c8748.zip new file mode 100644 index 00000000000..7d74dd7e5cb Binary files /dev/null and b/.yarn/cache/prelude-ls-npm-1.1.2-a0daac0886-c4867c8748.zip differ diff --git a/.yarn/cache/prelude-ls-npm-1.2.1-3e4d272a55-cd192ec0d0.zip b/.yarn/cache/prelude-ls-npm-1.2.1-3e4d272a55-cd192ec0d0.zip new file mode 100644 index 00000000000..38e7969199e Binary files /dev/null and b/.yarn/cache/prelude-ls-npm-1.2.1-3e4d272a55-cd192ec0d0.zip differ diff --git a/.yarn/cache/prepend-http-npm-2.0.0-e1fc4332f2-7694a95254.zip b/.yarn/cache/prepend-http-npm-2.0.0-e1fc4332f2-7694a95254.zip new file mode 100644 index 00000000000..e068e24ed81 Binary files /dev/null and b/.yarn/cache/prepend-http-npm-2.0.0-e1fc4332f2-7694a95254.zip differ diff --git a/.yarn/cache/pretty-bytes-npm-5.6.0-0061079c9f-9c082500d1.zip b/.yarn/cache/pretty-bytes-npm-5.6.0-0061079c9f-9c082500d1.zip new file mode 100644 index 00000000000..767e74fc050 Binary files /dev/null and b/.yarn/cache/pretty-bytes-npm-5.6.0-0061079c9f-9c082500d1.zip differ diff --git a/.yarn/cache/pretty-format-npm-27.3.1-872f4f2791-2979eae85a.zip b/.yarn/cache/pretty-format-npm-27.3.1-872f4f2791-2979eae85a.zip new file mode 100644 index 00000000000..c2e9c8fbca4 Binary files /dev/null and b/.yarn/cache/pretty-format-npm-27.3.1-872f4f2791-2979eae85a.zip differ diff --git a/.yarn/cache/pretty-ms-npm-7.0.1-d748cac064-d76c492028.zip b/.yarn/cache/pretty-ms-npm-7.0.1-d748cac064-d76c492028.zip new file mode 100644 index 00000000000..65b96314a9e Binary files /dev/null and b/.yarn/cache/pretty-ms-npm-7.0.1-d748cac064-d76c492028.zip differ diff --git a/.yarn/cache/proc-log-npm-1.0.0-cf9ff93bba-249605d5b2.zip b/.yarn/cache/proc-log-npm-1.0.0-cf9ff93bba-249605d5b2.zip new file mode 100644 index 00000000000..670c566b51b Binary files /dev/null and b/.yarn/cache/proc-log-npm-1.0.0-cf9ff93bba-249605d5b2.zip differ diff --git a/.yarn/cache/process-nextick-args-npm-2.0.1-b8d7971609-1d38588e52.zip b/.yarn/cache/process-nextick-args-npm-2.0.1-b8d7971609-1d38588e52.zip new file mode 100644 index 00000000000..33fadfd3e8a Binary files /dev/null and b/.yarn/cache/process-nextick-args-npm-2.0.1-b8d7971609-1d38588e52.zip differ diff --git a/.yarn/cache/process-npm-0.11.10-aeb3b641ae-bfcce49814.zip b/.yarn/cache/process-npm-0.11.10-aeb3b641ae-bfcce49814.zip new file mode 100644 index 00000000000..1bb2720229e Binary files /dev/null and b/.yarn/cache/process-npm-0.11.10-aeb3b641ae-bfcce49814.zip differ diff --git a/.yarn/cache/process-on-spawn-npm-1.0.0-676960b4dd-597769e3db.zip b/.yarn/cache/process-on-spawn-npm-1.0.0-676960b4dd-597769e3db.zip new file mode 100644 index 00000000000..14961629a8a Binary files /dev/null and b/.yarn/cache/process-on-spawn-npm-1.0.0-676960b4dd-597769e3db.zip differ diff --git a/.yarn/cache/progress-npm-2.0.3-d1f87e2ac6-f67403fe7b.zip b/.yarn/cache/progress-npm-2.0.3-d1f87e2ac6-f67403fe7b.zip new file mode 100644 index 00000000000..0585bd0a626 Binary files /dev/null and b/.yarn/cache/progress-npm-2.0.3-d1f87e2ac6-f67403fe7b.zip differ diff --git a/.yarn/cache/promise-all-reject-late-npm-1.0.1-19ba0dce9c-d7d61ac412.zip b/.yarn/cache/promise-all-reject-late-npm-1.0.1-19ba0dce9c-d7d61ac412.zip new file mode 100644 index 00000000000..3f9a0755cbf Binary files /dev/null and b/.yarn/cache/promise-all-reject-late-npm-1.0.1-19ba0dce9c-d7d61ac412.zip differ diff --git a/.yarn/cache/promise-call-limit-npm-1.0.1-18d83007c3-e69aed17f5.zip b/.yarn/cache/promise-call-limit-npm-1.0.1-18d83007c3-e69aed17f5.zip new file mode 100644 index 00000000000..49d9e5115e2 Binary files /dev/null and b/.yarn/cache/promise-call-limit-npm-1.0.1-18d83007c3-e69aed17f5.zip differ diff --git a/.yarn/cache/promise-inflight-npm-1.0.1-5bb925afac-2274948309.zip b/.yarn/cache/promise-inflight-npm-1.0.1-5bb925afac-2274948309.zip new file mode 100644 index 00000000000..fa2a77c45bf Binary files /dev/null and b/.yarn/cache/promise-inflight-npm-1.0.1-5bb925afac-2274948309.zip differ diff --git a/.yarn/cache/promise-retry-npm-2.0.1-871f0b01b7-f96a3f6d90.zip b/.yarn/cache/promise-retry-npm-2.0.1-871f0b01b7-f96a3f6d90.zip new file mode 100644 index 00000000000..9cefe07769b Binary files /dev/null and b/.yarn/cache/promise-retry-npm-2.0.1-871f0b01b7-f96a3f6d90.zip differ diff --git a/.yarn/cache/proper-lockfile-npm-3.2.0-4c500143f0-1be1bb702b.zip b/.yarn/cache/proper-lockfile-npm-3.2.0-4c500143f0-1be1bb702b.zip new file mode 100644 index 00000000000..809eb353e0c Binary files /dev/null and b/.yarn/cache/proper-lockfile-npm-3.2.0-4c500143f0-1be1bb702b.zip differ diff --git a/.yarn/cache/protobufjs-npm-6.11.2-9b422ce98e-80e9d9610c.zip b/.yarn/cache/protobufjs-npm-6.11.2-9b422ce98e-80e9d9610c.zip new file mode 100644 index 00000000000..f284ecb0b0d Binary files /dev/null and b/.yarn/cache/protobufjs-npm-6.11.2-9b422ce98e-80e9d9610c.zip differ diff --git a/.yarn/cache/protocol-buffers-encodings-npm-1.1.1-07111209e8-1b22d6d05b.zip b/.yarn/cache/protocol-buffers-encodings-npm-1.1.1-07111209e8-1b22d6d05b.zip new file mode 100644 index 00000000000..1fcfbe9317f Binary files /dev/null and b/.yarn/cache/protocol-buffers-encodings-npm-1.1.1-07111209e8-1b22d6d05b.zip differ diff --git a/.yarn/cache/prr-npm-1.0.1-608d442761-3bca2db047.zip b/.yarn/cache/prr-npm-1.0.1-608d442761-3bca2db047.zip new file mode 100644 index 00000000000..30374d9eb66 Binary files /dev/null and b/.yarn/cache/prr-npm-1.0.1-608d442761-3bca2db047.zip differ diff --git a/.yarn/cache/ps-list-npm-7.2.0-7b32c6b513-38969f4fb8.zip b/.yarn/cache/ps-list-npm-7.2.0-7b32c6b513-38969f4fb8.zip new file mode 100644 index 00000000000..6318131a918 Binary files /dev/null and b/.yarn/cache/ps-list-npm-7.2.0-7b32c6b513-38969f4fb8.zip differ diff --git a/.yarn/cache/psl-npm-1.8.0-226099d70e-6150048ed2.zip b/.yarn/cache/psl-npm-1.8.0-226099d70e-6150048ed2.zip new file mode 100644 index 00000000000..1611ec10a31 Binary files /dev/null and b/.yarn/cache/psl-npm-1.8.0-226099d70e-6150048ed2.zip differ diff --git a/.yarn/cache/pstree.remy-npm-1.1.8-2dd5d55de2-5cb53698d6.zip b/.yarn/cache/pstree.remy-npm-1.1.8-2dd5d55de2-5cb53698d6.zip new file mode 100644 index 00000000000..dccb458a69b Binary files /dev/null and b/.yarn/cache/pstree.remy-npm-1.1.8-2dd5d55de2-5cb53698d6.zip differ diff --git a/.yarn/cache/public-encrypt-npm-4.0.3-b25e19fada-215d446e43.zip b/.yarn/cache/public-encrypt-npm-4.0.3-b25e19fada-215d446e43.zip new file mode 100644 index 00000000000..0eb1ae59f27 Binary files /dev/null and b/.yarn/cache/public-encrypt-npm-4.0.3-b25e19fada-215d446e43.zip differ diff --git a/.yarn/cache/public-ip-npm-4.0.4-8624c1184b-9a0c3194b2.zip b/.yarn/cache/public-ip-npm-4.0.4-8624c1184b-9a0c3194b2.zip new file mode 100644 index 00000000000..8cb4cf38792 Binary files /dev/null and b/.yarn/cache/public-ip-npm-4.0.4-8624c1184b-9a0c3194b2.zip differ diff --git a/.yarn/cache/pump-npm-3.0.0-0080bf6a7a-e42e9229fb.zip b/.yarn/cache/pump-npm-3.0.0-0080bf6a7a-e42e9229fb.zip new file mode 100644 index 00000000000..05856836216 Binary files /dev/null and b/.yarn/cache/pump-npm-3.0.0-0080bf6a7a-e42e9229fb.zip differ diff --git a/.yarn/cache/punycode-npm-1.3.2-3727a84cea-b8807fd594.zip b/.yarn/cache/punycode-npm-1.3.2-3727a84cea-b8807fd594.zip new file mode 100644 index 00000000000..22be1b6037e Binary files /dev/null and b/.yarn/cache/punycode-npm-1.3.2-3727a84cea-b8807fd594.zip differ diff --git a/.yarn/cache/punycode-npm-1.4.1-be4c23e6d2-fa6e698cb5.zip b/.yarn/cache/punycode-npm-1.4.1-be4c23e6d2-fa6e698cb5.zip new file mode 100644 index 00000000000..a273278cc2b Binary files /dev/null and b/.yarn/cache/punycode-npm-1.4.1-be4c23e6d2-fa6e698cb5.zip differ diff --git a/.yarn/cache/punycode-npm-2.1.1-26eb3e15cf-823bf443c6.zip b/.yarn/cache/punycode-npm-2.1.1-26eb3e15cf-823bf443c6.zip new file mode 100644 index 00000000000..4946f0581f5 Binary files /dev/null and b/.yarn/cache/punycode-npm-2.1.1-26eb3e15cf-823bf443c6.zip differ diff --git a/.yarn/cache/pupa-npm-2.1.1-fb256825ba-49529e5037.zip b/.yarn/cache/pupa-npm-2.1.1-fb256825ba-49529e5037.zip new file mode 100644 index 00000000000..2cb125c126b Binary files /dev/null and b/.yarn/cache/pupa-npm-2.1.1-fb256825ba-49529e5037.zip differ diff --git a/.yarn/cache/q-npm-1.5.1-a28b3cfeaf-147baa93c8.zip b/.yarn/cache/q-npm-1.5.1-a28b3cfeaf-147baa93c8.zip new file mode 100644 index 00000000000..6ad6a8bdaf0 Binary files /dev/null and b/.yarn/cache/q-npm-1.5.1-a28b3cfeaf-147baa93c8.zip differ diff --git a/.yarn/cache/qjobs-npm-1.2.0-e3396bd5d4-eb64c00724.zip b/.yarn/cache/qjobs-npm-1.2.0-e3396bd5d4-eb64c00724.zip new file mode 100644 index 00000000000..bb41b25c366 Binary files /dev/null and b/.yarn/cache/qjobs-npm-1.2.0-e3396bd5d4-eb64c00724.zip differ diff --git a/.yarn/cache/qqjs-npm-0.3.11-a7e926aa2c-7962df855b.zip b/.yarn/cache/qqjs-npm-0.3.11-a7e926aa2c-7962df855b.zip new file mode 100644 index 00000000000..8f7a1d63330 Binary files /dev/null and b/.yarn/cache/qqjs-npm-0.3.11-a7e926aa2c-7962df855b.zip differ diff --git a/.yarn/cache/qs-npm-6.5.2-dbf9d8386b-24af7b9928.zip b/.yarn/cache/qs-npm-6.5.2-dbf9d8386b-24af7b9928.zip new file mode 100644 index 00000000000..58739e4d802 Binary files /dev/null and b/.yarn/cache/qs-npm-6.5.2-dbf9d8386b-24af7b9928.zip differ diff --git a/.yarn/cache/qs-npm-6.7.0-15161a344c-dfd5f6adef.zip b/.yarn/cache/qs-npm-6.7.0-15161a344c-dfd5f6adef.zip new file mode 100644 index 00000000000..1b86b457be0 Binary files /dev/null and b/.yarn/cache/qs-npm-6.7.0-15161a344c-dfd5f6adef.zip differ diff --git a/.yarn/cache/querystring-es3-npm-0.2.1-f4632f2760-691e8d6b8b.zip b/.yarn/cache/querystring-es3-npm-0.2.1-f4632f2760-691e8d6b8b.zip new file mode 100644 index 00000000000..a37d5c27136 Binary files /dev/null and b/.yarn/cache/querystring-es3-npm-0.2.1-f4632f2760-691e8d6b8b.zip differ diff --git a/.yarn/cache/querystring-npm-0.2.0-421b870c92-8258d6734f.zip b/.yarn/cache/querystring-npm-0.2.0-421b870c92-8258d6734f.zip new file mode 100644 index 00000000000..161c2bf5801 Binary files /dev/null and b/.yarn/cache/querystring-npm-0.2.0-421b870c92-8258d6734f.zip differ diff --git a/.yarn/cache/queue-microtask-npm-1.2.3-fcc98e4e2d-b676f8c040.zip b/.yarn/cache/queue-microtask-npm-1.2.3-fcc98e4e2d-b676f8c040.zip new file mode 100644 index 00000000000..31453282a46 Binary files /dev/null and b/.yarn/cache/queue-microtask-npm-1.2.3-fcc98e4e2d-b676f8c040.zip differ diff --git a/.yarn/cache/quick-format-unescaped-npm-4.0.4-7e22c9b7dc-7bc32b9935.zip b/.yarn/cache/quick-format-unescaped-npm-4.0.4-7e22c9b7dc-7bc32b9935.zip new file mode 100644 index 00000000000..8ce3d464d80 Binary files /dev/null and b/.yarn/cache/quick-format-unescaped-npm-4.0.4-7e22c9b7dc-7bc32b9935.zip differ diff --git a/.yarn/cache/quick-lru-npm-4.0.1-ef8aa17c9c-bea46e1abf.zip b/.yarn/cache/quick-lru-npm-4.0.1-ef8aa17c9c-bea46e1abf.zip new file mode 100644 index 00000000000..f63e9fdfb89 Binary files /dev/null and b/.yarn/cache/quick-lru-npm-4.0.1-ef8aa17c9c-bea46e1abf.zip differ diff --git a/.yarn/cache/randombytes-npm-2.1.0-e3da76bccf-d779499376.zip b/.yarn/cache/randombytes-npm-2.1.0-e3da76bccf-d779499376.zip new file mode 100644 index 00000000000..cfc11435d25 Binary files /dev/null and b/.yarn/cache/randombytes-npm-2.1.0-e3da76bccf-d779499376.zip differ diff --git a/.yarn/cache/randomfill-npm-1.0.4-a08651a679-33734bb578.zip b/.yarn/cache/randomfill-npm-1.0.4-a08651a679-33734bb578.zip new file mode 100644 index 00000000000..0bd86f4bdfb Binary files /dev/null and b/.yarn/cache/randomfill-npm-1.0.4-a08651a679-33734bb578.zip differ diff --git a/.yarn/cache/range-parser-npm-1.2.1-1a470fa390-0a268d4fea.zip b/.yarn/cache/range-parser-npm-1.2.1-1a470fa390-0a268d4fea.zip new file mode 100644 index 00000000000..7b40d591399 Binary files /dev/null and b/.yarn/cache/range-parser-npm-1.2.1-1a470fa390-0a268d4fea.zip differ diff --git a/.yarn/cache/raw-body-npm-2.4.0-14d9d633af-6343906939.zip b/.yarn/cache/raw-body-npm-2.4.0-14d9d633af-6343906939.zip new file mode 100644 index 00000000000..3888b70fdfd Binary files /dev/null and b/.yarn/cache/raw-body-npm-2.4.0-14d9d633af-6343906939.zip differ diff --git a/.yarn/cache/rc-npm-1.2.8-d6768ac936-2e26e052f8.zip b/.yarn/cache/rc-npm-1.2.8-d6768ac936-2e26e052f8.zip new file mode 100644 index 00000000000..f7372f98eb1 Binary files /dev/null and b/.yarn/cache/rc-npm-1.2.8-d6768ac936-2e26e052f8.zip differ diff --git a/.yarn/cache/react-is-npm-17.0.2-091bbb8db6-9d6d111d89.zip b/.yarn/cache/react-is-npm-17.0.2-091bbb8db6-9d6d111d89.zip new file mode 100644 index 00000000000..8b0c3e54609 Binary files /dev/null and b/.yarn/cache/react-is-npm-17.0.2-091bbb8db6-9d6d111d89.zip differ diff --git a/.yarn/cache/read-cmd-shim-npm-2.0.0-bf49908226-024f0a092d.zip b/.yarn/cache/read-cmd-shim-npm-2.0.0-bf49908226-024f0a092d.zip new file mode 100644 index 00000000000..101b9d2a9e7 Binary files /dev/null and b/.yarn/cache/read-cmd-shim-npm-2.0.0-bf49908226-024f0a092d.zip differ diff --git a/.yarn/cache/read-only-stream-npm-2.0.0-020991ee6f-aa48979d1f.zip b/.yarn/cache/read-only-stream-npm-2.0.0-020991ee6f-aa48979d1f.zip new file mode 100644 index 00000000000..986897a3e8f Binary files /dev/null and b/.yarn/cache/read-only-stream-npm-2.0.0-020991ee6f-aa48979d1f.zip differ diff --git a/.yarn/cache/read-package-json-fast-npm-2.0.3-f163572d18-fca37b3b21.zip b/.yarn/cache/read-package-json-fast-npm-2.0.3-f163572d18-fca37b3b21.zip new file mode 100644 index 00000000000..58beb5b15d0 Binary files /dev/null and b/.yarn/cache/read-package-json-fast-npm-2.0.3-f163572d18-fca37b3b21.zip differ diff --git a/.yarn/cache/read-pkg-npm-3.0.0-41471436cb-398903ebae.zip b/.yarn/cache/read-pkg-npm-3.0.0-41471436cb-398903ebae.zip new file mode 100644 index 00000000000..e0a22aff7b9 Binary files /dev/null and b/.yarn/cache/read-pkg-npm-3.0.0-41471436cb-398903ebae.zip differ diff --git a/.yarn/cache/read-pkg-npm-5.2.0-50426bd8dc-eb696e6052.zip b/.yarn/cache/read-pkg-npm-5.2.0-50426bd8dc-eb696e6052.zip new file mode 100644 index 00000000000..9749e742a83 Binary files /dev/null and b/.yarn/cache/read-pkg-npm-5.2.0-50426bd8dc-eb696e6052.zip differ diff --git a/.yarn/cache/read-pkg-up-npm-3.0.0-3d7faf047f-16175573f2.zip b/.yarn/cache/read-pkg-up-npm-3.0.0-3d7faf047f-16175573f2.zip new file mode 100644 index 00000000000..f1f0a309ae7 Binary files /dev/null and b/.yarn/cache/read-pkg-up-npm-3.0.0-3d7faf047f-16175573f2.zip differ diff --git a/.yarn/cache/read-pkg-up-npm-7.0.1-11895bed9a-e4e93ce70e.zip b/.yarn/cache/read-pkg-up-npm-7.0.1-11895bed9a-e4e93ce70e.zip new file mode 100644 index 00000000000..04f7307c72c Binary files /dev/null and b/.yarn/cache/read-pkg-up-npm-7.0.1-11895bed9a-e4e93ce70e.zip differ diff --git a/.yarn/cache/readable-stream-npm-1.0.34-db63158f3f-85042c537e.zip b/.yarn/cache/readable-stream-npm-1.0.34-db63158f3f-85042c537e.zip new file mode 100644 index 00000000000..eb4518d0e84 Binary files /dev/null and b/.yarn/cache/readable-stream-npm-1.0.34-db63158f3f-85042c537e.zip differ diff --git a/.yarn/cache/readable-stream-npm-2.3.7-77b22a9818-e4920cf754.zip b/.yarn/cache/readable-stream-npm-2.3.7-77b22a9818-e4920cf754.zip new file mode 100644 index 00000000000..eb8e6e005e5 Binary files /dev/null and b/.yarn/cache/readable-stream-npm-2.3.7-77b22a9818-e4920cf754.zip differ diff --git a/.yarn/cache/readable-stream-npm-3.6.0-23a4a5eb56-d4ea81502d.zip b/.yarn/cache/readable-stream-npm-3.6.0-23a4a5eb56-d4ea81502d.zip new file mode 100644 index 00000000000..ede5b314bfd Binary files /dev/null and b/.yarn/cache/readable-stream-npm-3.6.0-23a4a5eb56-d4ea81502d.zip differ diff --git a/.yarn/cache/readdir-scoped-modules-npm-1.1.0-651d6882ac-6d9f334e40.zip b/.yarn/cache/readdir-scoped-modules-npm-1.1.0-651d6882ac-6d9f334e40.zip new file mode 100644 index 00000000000..71f5e898022 Binary files /dev/null and b/.yarn/cache/readdir-scoped-modules-npm-1.1.0-651d6882ac-6d9f334e40.zip differ diff --git a/.yarn/cache/readdirp-npm-3.6.0-f950cc74ab-1ced032e6e.zip b/.yarn/cache/readdirp-npm-3.6.0-f950cc74ab-1ced032e6e.zip new file mode 100644 index 00000000000..f3687812b2c Binary files /dev/null and b/.yarn/cache/readdirp-npm-3.6.0-f950cc74ab-1ced032e6e.zip differ diff --git a/.yarn/cache/rechoir-npm-0.6.2-0df5f171ec-fe76bf9c21.zip b/.yarn/cache/rechoir-npm-0.6.2-0df5f171ec-fe76bf9c21.zip new file mode 100644 index 00000000000..f571eebe779 Binary files /dev/null and b/.yarn/cache/rechoir-npm-0.6.2-0df5f171ec-fe76bf9c21.zip differ diff --git a/.yarn/cache/rechoir-npm-0.7.1-0c7e5c1201-2a04aab4e2.zip b/.yarn/cache/rechoir-npm-0.7.1-0c7e5c1201-2a04aab4e2.zip new file mode 100644 index 00000000000..24cb0af8e6a Binary files /dev/null and b/.yarn/cache/rechoir-npm-0.7.1-0c7e5c1201-2a04aab4e2.zip differ diff --git a/.yarn/cache/redent-npm-3.0.0-31892f4906-fa1ef20404.zip b/.yarn/cache/redent-npm-3.0.0-31892f4906-fa1ef20404.zip new file mode 100644 index 00000000000..f0b77dfb50c Binary files /dev/null and b/.yarn/cache/redent-npm-3.0.0-31892f4906-fa1ef20404.zip differ diff --git a/.yarn/cache/redeyed-npm-2.1.1-7cbceb60bb-39a1426e37.zip b/.yarn/cache/redeyed-npm-2.1.1-7cbceb60bb-39a1426e37.zip new file mode 100644 index 00000000000..94faac8204a Binary files /dev/null and b/.yarn/cache/redeyed-npm-2.1.1-7cbceb60bb-39a1426e37.zip differ diff --git a/.yarn/cache/regenerate-npm-1.4.2-b296c5b63a-3317a09b2f.zip b/.yarn/cache/regenerate-npm-1.4.2-b296c5b63a-3317a09b2f.zip new file mode 100644 index 00000000000..fc54b3c436c Binary files /dev/null and b/.yarn/cache/regenerate-npm-1.4.2-b296c5b63a-3317a09b2f.zip differ diff --git a/.yarn/cache/regenerate-unicode-properties-npm-9.0.0-73b46c97bd-62df21c274.zip b/.yarn/cache/regenerate-unicode-properties-npm-9.0.0-73b46c97bd-62df21c274.zip new file mode 100644 index 00000000000..039e77d0954 Binary files /dev/null and b/.yarn/cache/regenerate-unicode-properties-npm-9.0.0-73b46c97bd-62df21c274.zip differ diff --git a/.yarn/cache/regenerator-runtime-npm-0.13.9-6d02340eec-65ed455fe5.zip b/.yarn/cache/regenerator-runtime-npm-0.13.9-6d02340eec-65ed455fe5.zip new file mode 100644 index 00000000000..29291038f14 Binary files /dev/null and b/.yarn/cache/regenerator-runtime-npm-0.13.9-6d02340eec-65ed455fe5.zip differ diff --git a/.yarn/cache/regenerator-transform-npm-0.14.5-40045884e9-a467a3b652.zip b/.yarn/cache/regenerator-transform-npm-0.14.5-40045884e9-a467a3b652.zip new file mode 100644 index 00000000000..5587bdbd63b Binary files /dev/null and b/.yarn/cache/regenerator-transform-npm-0.14.5-40045884e9-a467a3b652.zip differ diff --git a/.yarn/cache/regexpp-npm-3.2.0-2513f32cfc-a78dc5c715.zip b/.yarn/cache/regexpp-npm-3.2.0-2513f32cfc-a78dc5c715.zip new file mode 100644 index 00000000000..9dac209df2f Binary files /dev/null and b/.yarn/cache/regexpp-npm-3.2.0-2513f32cfc-a78dc5c715.zip differ diff --git a/.yarn/cache/regexpu-core-npm-4.8.0-b5aa95540a-df92e3e648.zip b/.yarn/cache/regexpu-core-npm-4.8.0-b5aa95540a-df92e3e648.zip new file mode 100644 index 00000000000..e12247ead3b Binary files /dev/null and b/.yarn/cache/regexpu-core-npm-4.8.0-b5aa95540a-df92e3e648.zip differ diff --git a/.yarn/cache/regextras-npm-0.7.1-f017685aa7-ffcd5bfd58.zip b/.yarn/cache/regextras-npm-0.7.1-f017685aa7-ffcd5bfd58.zip new file mode 100644 index 00000000000..1709a22dea3 Binary files /dev/null and b/.yarn/cache/regextras-npm-0.7.1-f017685aa7-ffcd5bfd58.zip differ diff --git a/.yarn/cache/registry-auth-token-npm-4.2.1-200e2be697-aa72060b57.zip b/.yarn/cache/registry-auth-token-npm-4.2.1-200e2be697-aa72060b57.zip new file mode 100644 index 00000000000..1915a129ce5 Binary files /dev/null and b/.yarn/cache/registry-auth-token-npm-4.2.1-200e2be697-aa72060b57.zip differ diff --git a/.yarn/cache/registry-url-npm-5.1.0-f58d0ca7ff-bcea86c84a.zip b/.yarn/cache/registry-url-npm-5.1.0-f58d0ca7ff-bcea86c84a.zip new file mode 100644 index 00000000000..de154212942 Binary files /dev/null and b/.yarn/cache/registry-url-npm-5.1.0-f58d0ca7ff-bcea86c84a.zip differ diff --git a/.yarn/cache/regjsgen-npm-0.5.2-4c9c408ab2-87c83d8488.zip b/.yarn/cache/regjsgen-npm-0.5.2-4c9c408ab2-87c83d8488.zip new file mode 100644 index 00000000000..abf89960d4b Binary files /dev/null and b/.yarn/cache/regjsgen-npm-0.5.2-4c9c408ab2-87c83d8488.zip differ diff --git a/.yarn/cache/regjsparser-npm-0.7.0-a4d515e434-fefff9adca.zip b/.yarn/cache/regjsparser-npm-0.7.0-a4d515e434-fefff9adca.zip new file mode 100644 index 00000000000..a4d1d9168d7 Binary files /dev/null and b/.yarn/cache/regjsparser-npm-0.7.0-a4d515e434-fefff9adca.zip differ diff --git a/.yarn/cache/release-zalgo-npm-1.0.0-aa3e59962f-b59849dc31.zip b/.yarn/cache/release-zalgo-npm-1.0.0-aa3e59962f-b59849dc31.zip new file mode 100644 index 00000000000..4f5eaf96d6e Binary files /dev/null and b/.yarn/cache/release-zalgo-npm-1.0.0-aa3e59962f-b59849dc31.zip differ diff --git a/.yarn/cache/remove-trailing-separator-npm-1.1.0-16d7231316-d3c20b5a2d.zip b/.yarn/cache/remove-trailing-separator-npm-1.1.0-16d7231316-d3c20b5a2d.zip new file mode 100644 index 00000000000..33c88a9ac0d Binary files /dev/null and b/.yarn/cache/remove-trailing-separator-npm-1.1.0-16d7231316-d3c20b5a2d.zip differ diff --git a/.yarn/cache/replace-ext-npm-1.0.1-ab0bac6614-4994ea1aaa.zip b/.yarn/cache/replace-ext-npm-1.0.1-ab0bac6614-4994ea1aaa.zip new file mode 100644 index 00000000000..74a8b72530d Binary files /dev/null and b/.yarn/cache/replace-ext-npm-1.0.1-ab0bac6614-4994ea1aaa.zip differ diff --git a/.yarn/cache/request-npm-2.88.2-f4a57c72c4-4e112c087f.zip b/.yarn/cache/request-npm-2.88.2-f4a57c72c4-4e112c087f.zip new file mode 100644 index 00000000000..9e727dd45cf Binary files /dev/null and b/.yarn/cache/request-npm-2.88.2-f4a57c72c4-4e112c087f.zip differ diff --git a/.yarn/cache/request-promise-core-npm-1.1.4-cb9fff6c90-c798bafd55.zip b/.yarn/cache/request-promise-core-npm-1.1.4-cb9fff6c90-c798bafd55.zip new file mode 100644 index 00000000000..f6e11509c2f Binary files /dev/null and b/.yarn/cache/request-promise-core-npm-1.1.4-cb9fff6c90-c798bafd55.zip differ diff --git a/.yarn/cache/request-promise-native-npm-1.0.9-6ae8e592e8-3e2c694eef.zip b/.yarn/cache/request-promise-native-npm-1.0.9-6ae8e592e8-3e2c694eef.zip new file mode 100644 index 00000000000..58494d2c50f Binary files /dev/null and b/.yarn/cache/request-promise-native-npm-1.0.9-6ae8e592e8-3e2c694eef.zip differ diff --git a/.yarn/cache/require-at-npm-1.0.6-eee905f868-7753a6ebad.zip b/.yarn/cache/require-at-npm-1.0.6-eee905f868-7753a6ebad.zip new file mode 100644 index 00000000000..52c0206474e Binary files /dev/null and b/.yarn/cache/require-at-npm-1.0.6-eee905f868-7753a6ebad.zip differ diff --git a/.yarn/cache/require-directory-npm-2.1.1-8608aee50b-fb47e70bf0.zip b/.yarn/cache/require-directory-npm-2.1.1-8608aee50b-fb47e70bf0.zip new file mode 100644 index 00000000000..5af5579b18e Binary files /dev/null and b/.yarn/cache/require-directory-npm-2.1.1-8608aee50b-fb47e70bf0.zip differ diff --git a/.yarn/cache/require-from-string-npm-2.0.2-8557e0db12-a03ef68954.zip b/.yarn/cache/require-from-string-npm-2.0.2-8557e0db12-a03ef68954.zip new file mode 100644 index 00000000000..a91f2d57b23 Binary files /dev/null and b/.yarn/cache/require-from-string-npm-2.0.2-8557e0db12-a03ef68954.zip differ diff --git a/.yarn/cache/require-main-filename-npm-2.0.0-03eef65c84-e9e294695f.zip b/.yarn/cache/require-main-filename-npm-2.0.0-03eef65c84-e9e294695f.zip new file mode 100644 index 00000000000..9a8a6919522 Binary files /dev/null and b/.yarn/cache/require-main-filename-npm-2.0.0-03eef65c84-e9e294695f.zip differ diff --git a/.yarn/cache/requires-port-npm-1.0.0-fd036b488a-eee0e303ad.zip b/.yarn/cache/requires-port-npm-1.0.0-fd036b488a-eee0e303ad.zip new file mode 100644 index 00000000000..b130302a580 Binary files /dev/null and b/.yarn/cache/requires-port-npm-1.0.0-fd036b488a-eee0e303ad.zip differ diff --git a/.yarn/cache/resolve-cwd-npm-3.0.0-e6f4e296bf-546e081601.zip b/.yarn/cache/resolve-cwd-npm-3.0.0-e6f4e296bf-546e081601.zip new file mode 100644 index 00000000000..d629f22465c Binary files /dev/null and b/.yarn/cache/resolve-cwd-npm-3.0.0-e6f4e296bf-546e081601.zip differ diff --git a/.yarn/cache/resolve-from-npm-4.0.0-f758ec21bf-f4ba0b8494.zip b/.yarn/cache/resolve-from-npm-4.0.0-f758ec21bf-f4ba0b8494.zip new file mode 100644 index 00000000000..86f591e3e89 Binary files /dev/null and b/.yarn/cache/resolve-from-npm-4.0.0-f758ec21bf-f4ba0b8494.zip differ diff --git a/.yarn/cache/resolve-from-npm-5.0.0-15c9db4d33-4ceeb9113e.zip b/.yarn/cache/resolve-from-npm-5.0.0-15c9db4d33-4ceeb9113e.zip new file mode 100644 index 00000000000..c7a552b61b2 Binary files /dev/null and b/.yarn/cache/resolve-from-npm-5.0.0-15c9db4d33-4ceeb9113e.zip differ diff --git a/.yarn/cache/resolve-npm-1.22.0-f641ddcc95-a2d14cc437.zip b/.yarn/cache/resolve-npm-1.22.0-f641ddcc95-a2d14cc437.zip new file mode 100644 index 00000000000..1804da23d5e Binary files /dev/null and b/.yarn/cache/resolve-npm-1.22.0-f641ddcc95-a2d14cc437.zip differ diff --git a/.yarn/cache/resolve-patch-bad885c6ea-c79ecaea36.zip b/.yarn/cache/resolve-patch-bad885c6ea-c79ecaea36.zip new file mode 100644 index 00000000000..b7e8b9abf59 Binary files /dev/null and b/.yarn/cache/resolve-patch-bad885c6ea-c79ecaea36.zip differ diff --git a/.yarn/cache/responselike-npm-1.0.2-d0bf50cde4-2e9e70f1dc.zip b/.yarn/cache/responselike-npm-1.0.2-d0bf50cde4-2e9e70f1dc.zip new file mode 100644 index 00000000000..28377c26a43 Binary files /dev/null and b/.yarn/cache/responselike-npm-1.0.2-d0bf50cde4-2e9e70f1dc.zip differ diff --git a/.yarn/cache/restore-cursor-npm-3.1.0-52c5a4c98f-f877dd8741.zip b/.yarn/cache/restore-cursor-npm-3.1.0-52c5a4c98f-f877dd8741.zip new file mode 100644 index 00000000000..f11afe99bba Binary files /dev/null and b/.yarn/cache/restore-cursor-npm-3.1.0-52c5a4c98f-f877dd8741.zip differ diff --git a/.yarn/cache/ret-npm-0.2.2-f5d3022812-774964bb41.zip b/.yarn/cache/ret-npm-0.2.2-f5d3022812-774964bb41.zip new file mode 100644 index 00000000000..495e63ba04e Binary files /dev/null and b/.yarn/cache/ret-npm-0.2.2-f5d3022812-774964bb41.zip differ diff --git a/.yarn/cache/retry-npm-0.12.0-72ac7fb4cc-623bd7d2e5.zip b/.yarn/cache/retry-npm-0.12.0-72ac7fb4cc-623bd7d2e5.zip new file mode 100644 index 00000000000..12e25fcd41c Binary files /dev/null and b/.yarn/cache/retry-npm-0.12.0-72ac7fb4cc-623bd7d2e5.zip differ diff --git a/.yarn/cache/reusify-npm-1.0.4-95ac4aec11-c3076ebcc2.zip b/.yarn/cache/reusify-npm-1.0.4-95ac4aec11-c3076ebcc2.zip new file mode 100644 index 00000000000..595aa09ad18 Binary files /dev/null and b/.yarn/cache/reusify-npm-1.0.4-95ac4aec11-c3076ebcc2.zip differ diff --git a/.yarn/cache/rfdc-npm-1.3.0-272f288ad8-fb2ba8512e.zip b/.yarn/cache/rfdc-npm-1.3.0-272f288ad8-fb2ba8512e.zip new file mode 100644 index 00000000000..c6d5d0c9448 Binary files /dev/null and b/.yarn/cache/rfdc-npm-1.3.0-272f288ad8-fb2ba8512e.zip differ diff --git a/.yarn/cache/rimraf-npm-2.7.1-9a71f3cc37-cdc7f6eacb.zip b/.yarn/cache/rimraf-npm-2.7.1-9a71f3cc37-cdc7f6eacb.zip new file mode 100644 index 00000000000..096f552789d Binary files /dev/null and b/.yarn/cache/rimraf-npm-2.7.1-9a71f3cc37-cdc7f6eacb.zip differ diff --git a/.yarn/cache/rimraf-npm-3.0.2-2cb7dac69a-87f4164e39.zip b/.yarn/cache/rimraf-npm-3.0.2-2cb7dac69a-87f4164e39.zip new file mode 100644 index 00000000000..6d2f54108a1 Binary files /dev/null and b/.yarn/cache/rimraf-npm-3.0.2-2cb7dac69a-87f4164e39.zip differ diff --git a/.yarn/cache/ripemd160-npm-2.0.2-7b1fb8dc76-006accc405.zip b/.yarn/cache/ripemd160-npm-2.0.2-7b1fb8dc76-006accc405.zip new file mode 100644 index 00000000000..05c1425cd62 Binary files /dev/null and b/.yarn/cache/ripemd160-npm-2.0.2-7b1fb8dc76-006accc405.zip differ diff --git a/.yarn/cache/run-async-npm-2.4.1-a94bb90861-a2c88aa15d.zip b/.yarn/cache/run-async-npm-2.4.1-a94bb90861-a2c88aa15d.zip new file mode 100644 index 00000000000..34c485e0b08 Binary files /dev/null and b/.yarn/cache/run-async-npm-2.4.1-a94bb90861-a2c88aa15d.zip differ diff --git a/.yarn/cache/run-parallel-npm-1.2.0-3f47ff2034-cb4f97ad25.zip b/.yarn/cache/run-parallel-npm-1.2.0-3f47ff2034-cb4f97ad25.zip new file mode 100644 index 00000000000..fefbad56f94 Binary files /dev/null and b/.yarn/cache/run-parallel-npm-1.2.0-3f47ff2034-cb4f97ad25.zip differ diff --git a/.yarn/cache/rxjs-npm-6.6.7-055046ea3c-bc334edef1.zip b/.yarn/cache/rxjs-npm-6.6.7-055046ea3c-bc334edef1.zip new file mode 100644 index 00000000000..ba92cebbd37 Binary files /dev/null and b/.yarn/cache/rxjs-npm-6.6.7-055046ea3c-bc334edef1.zip differ diff --git a/.yarn/cache/rxjs-npm-7.5.4-1527612cf9-6f55f835f2.zip b/.yarn/cache/rxjs-npm-7.5.4-1527612cf9-6f55f835f2.zip new file mode 100644 index 00000000000..c8357c41a72 Binary files /dev/null and b/.yarn/cache/rxjs-npm-7.5.4-1527612cf9-6f55f835f2.zip differ diff --git a/.yarn/cache/safe-buffer-npm-5.1.2-c27fedf6c4-f2f1f7943c.zip b/.yarn/cache/safe-buffer-npm-5.1.2-c27fedf6c4-f2f1f7943c.zip new file mode 100644 index 00000000000..53c2813c6fe Binary files /dev/null and b/.yarn/cache/safe-buffer-npm-5.1.2-c27fedf6c4-f2f1f7943c.zip differ diff --git a/.yarn/cache/safe-buffer-npm-5.2.1-3481c8aa9b-b99c4b41fd.zip b/.yarn/cache/safe-buffer-npm-5.2.1-3481c8aa9b-b99c4b41fd.zip new file mode 100644 index 00000000000..c80798aecd8 Binary files /dev/null and b/.yarn/cache/safe-buffer-npm-5.2.1-3481c8aa9b-b99c4b41fd.zip differ diff --git a/.yarn/cache/safe-regex2-npm-2.0.0-eadecc9909-f5e182fca0.zip b/.yarn/cache/safe-regex2-npm-2.0.0-eadecc9909-f5e182fca0.zip new file mode 100644 index 00000000000..8ae7c9e5e53 Binary files /dev/null and b/.yarn/cache/safe-regex2-npm-2.0.0-eadecc9909-f5e182fca0.zip differ diff --git a/.yarn/cache/safe-stable-stringify-npm-1.1.1-1c282e1c55-e32a30720e.zip b/.yarn/cache/safe-stable-stringify-npm-1.1.1-1c282e1c55-e32a30720e.zip new file mode 100644 index 00000000000..d8c28486490 Binary files /dev/null and b/.yarn/cache/safe-stable-stringify-npm-1.1.1-1c282e1c55-e32a30720e.zip differ diff --git a/.yarn/cache/safer-buffer-npm-2.1.2-8d5c0b705e-cab8f25ae6.zip b/.yarn/cache/safer-buffer-npm-2.1.2-8d5c0b705e-cab8f25ae6.zip new file mode 100644 index 00000000000..1a93be64235 Binary files /dev/null and b/.yarn/cache/safer-buffer-npm-2.1.2-8d5c0b705e-cab8f25ae6.zip differ diff --git a/.yarn/cache/saslprep-npm-1.0.3-8db649c346-4fdc0b70fb.zip b/.yarn/cache/saslprep-npm-1.0.3-8db649c346-4fdc0b70fb.zip new file mode 100644 index 00000000000..218c5655f28 Binary files /dev/null and b/.yarn/cache/saslprep-npm-1.0.3-8db649c346-4fdc0b70fb.zip differ diff --git a/.yarn/cache/sax-npm-1.2.1-fd2ad7b223-8dca7d5e1c.zip b/.yarn/cache/sax-npm-1.2.1-fd2ad7b223-8dca7d5e1c.zip new file mode 100644 index 00000000000..e6fce3ebd7b Binary files /dev/null and b/.yarn/cache/sax-npm-1.2.1-fd2ad7b223-8dca7d5e1c.zip differ diff --git a/.yarn/cache/sax-npm-1.2.4-178f05f12f-d3df7d32b8.zip b/.yarn/cache/sax-npm-1.2.4-178f05f12f-d3df7d32b8.zip new file mode 100644 index 00000000000..d1150109474 Binary files /dev/null and b/.yarn/cache/sax-npm-1.2.4-178f05f12f-d3df7d32b8.zip differ diff --git a/.yarn/cache/schema-utils-npm-2.7.1-f84d18c473-32c62fc9e2.zip b/.yarn/cache/schema-utils-npm-2.7.1-f84d18c473-32c62fc9e2.zip new file mode 100644 index 00000000000..696f0c4de52 Binary files /dev/null and b/.yarn/cache/schema-utils-npm-2.7.1-f84d18c473-32c62fc9e2.zip differ diff --git a/.yarn/cache/schema-utils-npm-3.1.1-8704647575-fb73f3d759.zip b/.yarn/cache/schema-utils-npm-3.1.1-8704647575-fb73f3d759.zip new file mode 100644 index 00000000000..696037a50c2 Binary files /dev/null and b/.yarn/cache/schema-utils-npm-3.1.1-8704647575-fb73f3d759.zip differ diff --git a/.yarn/cache/scoped-regex-npm-2.1.0-6fbe8a6c4c-4e820444cb.zip b/.yarn/cache/scoped-regex-npm-2.1.0-6fbe8a6c4c-4e820444cb.zip new file mode 100644 index 00000000000..26dc5e86bc9 Binary files /dev/null and b/.yarn/cache/scoped-regex-npm-2.1.0-6fbe8a6c4c-4e820444cb.zip differ diff --git a/.yarn/cache/seedrandom-npm-3.0.5-6946e8f8db-728b56bc3b.zip b/.yarn/cache/seedrandom-npm-3.0.5-6946e8f8db-728b56bc3b.zip new file mode 100644 index 00000000000..c2f6b0903af Binary files /dev/null and b/.yarn/cache/seedrandom-npm-3.0.5-6946e8f8db-728b56bc3b.zip differ diff --git a/.yarn/cache/semver-diff-npm-3.1.1-1207a795e9-8bbe5a5d7a.zip b/.yarn/cache/semver-diff-npm-3.1.1-1207a795e9-8bbe5a5d7a.zip new file mode 100644 index 00000000000..29223bb3d60 Binary files /dev/null and b/.yarn/cache/semver-diff-npm-3.1.1-1207a795e9-8bbe5a5d7a.zip differ diff --git a/.yarn/cache/semver-npm-5.7.1-40bcea106b-57fd0acfd0.zip b/.yarn/cache/semver-npm-5.7.1-40bcea106b-57fd0acfd0.zip new file mode 100644 index 00000000000..68795d87761 Binary files /dev/null and b/.yarn/cache/semver-npm-5.7.1-40bcea106b-57fd0acfd0.zip differ diff --git a/.yarn/cache/semver-npm-6.3.0-b3eace8bfd-1b26ecf6db.zip b/.yarn/cache/semver-npm-6.3.0-b3eace8bfd-1b26ecf6db.zip new file mode 100644 index 00000000000..6320ec2b1bf Binary files /dev/null and b/.yarn/cache/semver-npm-6.3.0-b3eace8bfd-1b26ecf6db.zip differ diff --git a/.yarn/cache/semver-npm-7.0.0-218e8c00ca-272c11bf8d.zip b/.yarn/cache/semver-npm-7.0.0-218e8c00ca-272c11bf8d.zip new file mode 100644 index 00000000000..74b3f93cfdb Binary files /dev/null and b/.yarn/cache/semver-npm-7.0.0-218e8c00ca-272c11bf8d.zip differ diff --git a/.yarn/cache/semver-npm-7.3.5-618cf5db6a-5eafe6102b.zip b/.yarn/cache/semver-npm-7.3.5-618cf5db6a-5eafe6102b.zip new file mode 100644 index 00000000000..edf67273840 Binary files /dev/null and b/.yarn/cache/semver-npm-7.3.5-618cf5db6a-5eafe6102b.zip differ diff --git a/.yarn/cache/semver-store-npm-0.3.0-0fc88fd5b9-b38f747123.zip b/.yarn/cache/semver-store-npm-0.3.0-0fc88fd5b9-b38f747123.zip new file mode 100644 index 00000000000..3075d8a1670 Binary files /dev/null and b/.yarn/cache/semver-store-npm-0.3.0-0fc88fd5b9-b38f747123.zip differ diff --git a/.yarn/cache/serialize-javascript-npm-6.0.0-0bb8a3c88d-56f90b562a.zip b/.yarn/cache/serialize-javascript-npm-6.0.0-0bb8a3c88d-56f90b562a.zip new file mode 100644 index 00000000000..46090b6c5b5 Binary files /dev/null and b/.yarn/cache/serialize-javascript-npm-6.0.0-0bb8a3c88d-56f90b562a.zip differ diff --git a/.yarn/cache/set-blocking-npm-2.0.0-49e2cffa24-6e65a05f7c.zip b/.yarn/cache/set-blocking-npm-2.0.0-49e2cffa24-6e65a05f7c.zip new file mode 100644 index 00000000000..fe99c6f42ca Binary files /dev/null and b/.yarn/cache/set-blocking-npm-2.0.0-49e2cffa24-6e65a05f7c.zip differ diff --git a/.yarn/cache/setimmediate-npm-1.0.5-54587459b6-c9a6f2c5b5.zip b/.yarn/cache/setimmediate-npm-1.0.5-54587459b6-c9a6f2c5b5.zip new file mode 100644 index 00000000000..ec2aee7ef9e Binary files /dev/null and b/.yarn/cache/setimmediate-npm-1.0.5-54587459b6-c9a6f2c5b5.zip differ diff --git a/.yarn/cache/setprototypeof-npm-1.1.1-706b6318ec-a8bee29c1c.zip b/.yarn/cache/setprototypeof-npm-1.1.1-706b6318ec-a8bee29c1c.zip new file mode 100644 index 00000000000..db6f60e87d9 Binary files /dev/null and b/.yarn/cache/setprototypeof-npm-1.1.1-706b6318ec-a8bee29c1c.zip differ diff --git a/.yarn/cache/sha.js-npm-2.4.11-14868df4ca-ebd3f59d4b.zip b/.yarn/cache/sha.js-npm-2.4.11-14868df4ca-ebd3f59d4b.zip new file mode 100644 index 00000000000..6d55cc05ab3 Binary files /dev/null and b/.yarn/cache/sha.js-npm-2.4.11-14868df4ca-ebd3f59d4b.zip differ diff --git a/.yarn/cache/shallow-clone-npm-3.0.1-dab5873d0d-39b3dd9630.zip b/.yarn/cache/shallow-clone-npm-3.0.1-dab5873d0d-39b3dd9630.zip new file mode 100644 index 00000000000..64ce2a1a85e Binary files /dev/null and b/.yarn/cache/shallow-clone-npm-3.0.1-dab5873d0d-39b3dd9630.zip differ diff --git a/.yarn/cache/shasum-npm-1.0.2-bcace62f08-61d908825c.zip b/.yarn/cache/shasum-npm-1.0.2-bcace62f08-61d908825c.zip new file mode 100644 index 00000000000..a98491cd9c7 Binary files /dev/null and b/.yarn/cache/shasum-npm-1.0.2-bcace62f08-61d908825c.zip differ diff --git a/.yarn/cache/shasum-object-npm-1.0.0-5c621ed8ed-fc3531b7ae.zip b/.yarn/cache/shasum-object-npm-1.0.0-5c621ed8ed-fc3531b7ae.zip new file mode 100644 index 00000000000..9f05710f387 Binary files /dev/null and b/.yarn/cache/shasum-object-npm-1.0.0-5c621ed8ed-fc3531b7ae.zip differ diff --git a/.yarn/cache/shebang-command-npm-1.2.0-8990ba5d1d-9eed175030.zip b/.yarn/cache/shebang-command-npm-1.2.0-8990ba5d1d-9eed175030.zip new file mode 100644 index 00000000000..9b734d105d7 Binary files /dev/null and b/.yarn/cache/shebang-command-npm-1.2.0-8990ba5d1d-9eed175030.zip differ diff --git a/.yarn/cache/shebang-command-npm-2.0.0-eb2b01921d-6b52fe8727.zip b/.yarn/cache/shebang-command-npm-2.0.0-eb2b01921d-6b52fe8727.zip new file mode 100644 index 00000000000..727c5471e2c Binary files /dev/null and b/.yarn/cache/shebang-command-npm-2.0.0-eb2b01921d-6b52fe8727.zip differ diff --git a/.yarn/cache/shebang-regex-npm-1.0.0-c3612b74e9-404c5a752c.zip b/.yarn/cache/shebang-regex-npm-1.0.0-c3612b74e9-404c5a752c.zip new file mode 100644 index 00000000000..607d724c336 Binary files /dev/null and b/.yarn/cache/shebang-regex-npm-1.0.0-c3612b74e9-404c5a752c.zip differ diff --git a/.yarn/cache/shebang-regex-npm-3.0.0-899a0cd65e-1a2bcae50d.zip b/.yarn/cache/shebang-regex-npm-3.0.0-899a0cd65e-1a2bcae50d.zip new file mode 100644 index 00000000000..3e891cda952 Binary files /dev/null and b/.yarn/cache/shebang-regex-npm-3.0.0-899a0cd65e-1a2bcae50d.zip differ diff --git a/.yarn/cache/shell-quote-npm-1.7.3-76a78a6d77-aca58e73a3.zip b/.yarn/cache/shell-quote-npm-1.7.3-76a78a6d77-aca58e73a3.zip new file mode 100644 index 00000000000..ab19bd91e81 Binary files /dev/null and b/.yarn/cache/shell-quote-npm-1.7.3-76a78a6d77-aca58e73a3.zip differ diff --git a/.yarn/cache/shelljs-npm-0.8.5-44be43f84a-7babc46f73.zip b/.yarn/cache/shelljs-npm-0.8.5-44be43f84a-7babc46f73.zip new file mode 100644 index 00000000000..cfc41f90c1f Binary files /dev/null and b/.yarn/cache/shelljs-npm-0.8.5-44be43f84a-7babc46f73.zip differ diff --git a/.yarn/cache/shellwords-ts-npm-3.0.0-f21ef3e36f-32faa081b1.zip b/.yarn/cache/shellwords-ts-npm-3.0.0-f21ef3e36f-32faa081b1.zip new file mode 100644 index 00000000000..784da5d483a Binary files /dev/null and b/.yarn/cache/shellwords-ts-npm-3.0.0-f21ef3e36f-32faa081b1.zip differ diff --git a/.yarn/cache/should-equal-npm-2.0.0-ae8768ed44-3f3580a223.zip b/.yarn/cache/should-equal-npm-2.0.0-ae8768ed44-3f3580a223.zip new file mode 100644 index 00000000000..200272a5ea3 Binary files /dev/null and b/.yarn/cache/should-equal-npm-2.0.0-ae8768ed44-3f3580a223.zip differ diff --git a/.yarn/cache/should-format-npm-3.0.3-74f60dd776-5304e89b4d.zip b/.yarn/cache/should-format-npm-3.0.3-74f60dd776-5304e89b4d.zip new file mode 100644 index 00000000000..a3a315e1e6a Binary files /dev/null and b/.yarn/cache/should-format-npm-3.0.3-74f60dd776-5304e89b4d.zip differ diff --git a/.yarn/cache/should-npm-13.2.3-fbb7954a33-74bcc0eb85.zip b/.yarn/cache/should-npm-13.2.3-fbb7954a33-74bcc0eb85.zip new file mode 100644 index 00000000000..28269d1119d Binary files /dev/null and b/.yarn/cache/should-npm-13.2.3-fbb7954a33-74bcc0eb85.zip differ diff --git a/.yarn/cache/should-type-adaptors-npm-1.1.0-730d8324e4-94dd1d225c.zip b/.yarn/cache/should-type-adaptors-npm-1.1.0-730d8324e4-94dd1d225c.zip new file mode 100644 index 00000000000..7ee2c9fa986 Binary files /dev/null and b/.yarn/cache/should-type-adaptors-npm-1.1.0-730d8324e4-94dd1d225c.zip differ diff --git a/.yarn/cache/should-type-npm-1.4.0-6590b6ee32-88d9324c6c.zip b/.yarn/cache/should-type-npm-1.4.0-6590b6ee32-88d9324c6c.zip new file mode 100644 index 00000000000..c20cb0bbf59 Binary files /dev/null and b/.yarn/cache/should-type-npm-1.4.0-6590b6ee32-88d9324c6c.zip differ diff --git a/.yarn/cache/should-util-npm-1.0.1-f3701a5e03-c3be15e0fd.zip b/.yarn/cache/should-util-npm-1.0.1-f3701a5e03-c3be15e0fd.zip new file mode 100644 index 00000000000..a95a60e23fb Binary files /dev/null and b/.yarn/cache/should-util-npm-1.0.1-f3701a5e03-c3be15e0fd.zip differ diff --git a/.yarn/cache/side-channel-npm-1.0.4-e1f38b9e06-351e41b947.zip b/.yarn/cache/side-channel-npm-1.0.4-e1f38b9e06-351e41b947.zip new file mode 100644 index 00000000000..3761d612205 Binary files /dev/null and b/.yarn/cache/side-channel-npm-1.0.4-e1f38b9e06-351e41b947.zip differ diff --git a/.yarn/cache/signal-exit-npm-3.0.7-bd270458a3-a2f098f247.zip b/.yarn/cache/signal-exit-npm-3.0.7-bd270458a3-a2f098f247.zip new file mode 100644 index 00000000000..98720bd8c76 Binary files /dev/null and b/.yarn/cache/signal-exit-npm-3.0.7-bd270458a3-a2f098f247.zip differ diff --git a/.yarn/cache/signed-varint-npm-2.0.1-18301876e5-a9fd2d954d.zip b/.yarn/cache/signed-varint-npm-2.0.1-18301876e5-a9fd2d954d.zip new file mode 100644 index 00000000000..e5b624d2677 Binary files /dev/null and b/.yarn/cache/signed-varint-npm-2.0.1-18301876e5-a9fd2d954d.zip differ diff --git a/.yarn/cache/simple-concat-npm-1.0.1-48df70de29-4d211042cc.zip b/.yarn/cache/simple-concat-npm-1.0.1-48df70de29-4d211042cc.zip new file mode 100644 index 00000000000..6b694bed924 Binary files /dev/null and b/.yarn/cache/simple-concat-npm-1.0.1-48df70de29-4d211042cc.zip differ diff --git a/.yarn/cache/simple-swizzle-npm-0.2.2-8dee37fad1-a7f3f2ab5c.zip b/.yarn/cache/simple-swizzle-npm-0.2.2-8dee37fad1-a7f3f2ab5c.zip new file mode 100644 index 00000000000..8420b563a92 Binary files /dev/null and b/.yarn/cache/simple-swizzle-npm-0.2.2-8dee37fad1-a7f3f2ab5c.zip differ diff --git a/.yarn/cache/simple-wcswidth-npm-1.0.1-ac1dd0a592-dc5bf4cb13.zip b/.yarn/cache/simple-wcswidth-npm-1.0.1-ac1dd0a592-dc5bf4cb13.zip new file mode 100644 index 00000000000..559982382ae Binary files /dev/null and b/.yarn/cache/simple-wcswidth-npm-1.0.1-ac1dd0a592-dc5bf4cb13.zip differ diff --git a/.yarn/cache/sinon-chai-npm-3.7.0-8e6588805e-49a353d8eb.zip b/.yarn/cache/sinon-chai-npm-3.7.0-8e6588805e-49a353d8eb.zip new file mode 100644 index 00000000000..cddd77be94b Binary files /dev/null and b/.yarn/cache/sinon-chai-npm-3.7.0-8e6588805e-49a353d8eb.zip differ diff --git a/.yarn/cache/sinon-npm-11.1.2-5325724cb2-1d01377e23.zip b/.yarn/cache/sinon-npm-11.1.2-5325724cb2-1d01377e23.zip new file mode 100644 index 00000000000..72291de7ed5 Binary files /dev/null and b/.yarn/cache/sinon-npm-11.1.2-5325724cb2-1d01377e23.zip differ diff --git a/.yarn/cache/slash-npm-3.0.0-b87de2279a-94a93fff61.zip b/.yarn/cache/slash-npm-3.0.0-b87de2279a-94a93fff61.zip new file mode 100644 index 00000000000..40d6b511433 Binary files /dev/null and b/.yarn/cache/slash-npm-3.0.0-b87de2279a-94a93fff61.zip differ diff --git a/.yarn/cache/slice-ansi-npm-2.1.0-02505ccc06-4e82995aa5.zip b/.yarn/cache/slice-ansi-npm-2.1.0-02505ccc06-4e82995aa5.zip new file mode 100644 index 00000000000..23b558a2620 Binary files /dev/null and b/.yarn/cache/slice-ansi-npm-2.1.0-02505ccc06-4e82995aa5.zip differ diff --git a/.yarn/cache/slice-ansi-npm-3.0.0-d9999864af-5ec6d022d1.zip b/.yarn/cache/slice-ansi-npm-3.0.0-d9999864af-5ec6d022d1.zip new file mode 100644 index 00000000000..0129e70bff8 Binary files /dev/null and b/.yarn/cache/slice-ansi-npm-3.0.0-d9999864af-5ec6d022d1.zip differ diff --git a/.yarn/cache/slice-ansi-npm-4.0.0-6eeca1d10e-4a82d7f085.zip b/.yarn/cache/slice-ansi-npm-4.0.0-6eeca1d10e-4a82d7f085.zip new file mode 100644 index 00000000000..ef2012f3731 Binary files /dev/null and b/.yarn/cache/slice-ansi-npm-4.0.0-6eeca1d10e-4a82d7f085.zip differ diff --git a/.yarn/cache/slocket-npm-1.0.5-6a604ece59-4ea3cba56c.zip b/.yarn/cache/slocket-npm-1.0.5-6a604ece59-4ea3cba56c.zip new file mode 100644 index 00000000000..fa991bc4a28 Binary files /dev/null and b/.yarn/cache/slocket-npm-1.0.5-6a604ece59-4ea3cba56c.zip differ diff --git a/.yarn/cache/smart-buffer-npm-4.2.0-5ac3f668bb-b5167a7142.zip b/.yarn/cache/smart-buffer-npm-4.2.0-5ac3f668bb-b5167a7142.zip new file mode 100644 index 00000000000..d587b3db7ad Binary files /dev/null and b/.yarn/cache/smart-buffer-npm-4.2.0-5ac3f668bb-b5167a7142.zip differ diff --git a/.yarn/cache/socket.io-adapter-npm-2.3.3-4fd6b5d0bd-73890e0a33.zip b/.yarn/cache/socket.io-adapter-npm-2.3.3-4fd6b5d0bd-73890e0a33.zip new file mode 100644 index 00000000000..fb09b6f5bc3 Binary files /dev/null and b/.yarn/cache/socket.io-adapter-npm-2.3.3-4fd6b5d0bd-73890e0a33.zip differ diff --git a/.yarn/cache/socket.io-npm-4.4.0-dc1419e09a-3e680f6969.zip b/.yarn/cache/socket.io-npm-4.4.0-dc1419e09a-3e680f6969.zip new file mode 100644 index 00000000000..ad22ea5106b Binary files /dev/null and b/.yarn/cache/socket.io-npm-4.4.0-dc1419e09a-3e680f6969.zip differ diff --git a/.yarn/cache/socket.io-parser-npm-4.0.4-1dfc284556-c173b4f374.zip b/.yarn/cache/socket.io-parser-npm-4.0.4-1dfc284556-c173b4f374.zip new file mode 100644 index 00000000000..bf0c96fa23c Binary files /dev/null and b/.yarn/cache/socket.io-parser-npm-4.0.4-1dfc284556-c173b4f374.zip differ diff --git a/.yarn/cache/socks-npm-2.6.1-09133d0d22-2ca9d616e4.zip b/.yarn/cache/socks-npm-2.6.1-09133d0d22-2ca9d616e4.zip new file mode 100644 index 00000000000..4644164e602 Binary files /dev/null and b/.yarn/cache/socks-npm-2.6.1-09133d0d22-2ca9d616e4.zip differ diff --git a/.yarn/cache/socks-proxy-agent-npm-6.1.1-a3843946ba-9a8a4f791b.zip b/.yarn/cache/socks-proxy-agent-npm-6.1.1-a3843946ba-9a8a4f791b.zip new file mode 100644 index 00000000000..2a5b6ceea57 Binary files /dev/null and b/.yarn/cache/socks-proxy-agent-npm-6.1.1-a3843946ba-9a8a4f791b.zip differ diff --git a/.yarn/cache/sonic-boom-npm-1.4.1-e42b921f99-189fa8fe5c.zip b/.yarn/cache/sonic-boom-npm-1.4.1-e42b921f99-189fa8fe5c.zip new file mode 100644 index 00000000000..7e23ef07671 Binary files /dev/null and b/.yarn/cache/sonic-boom-npm-1.4.1-e42b921f99-189fa8fe5c.zip differ diff --git a/.yarn/cache/sonic-boom-npm-2.3.1-0ba04b648c-4f5022de97.zip b/.yarn/cache/sonic-boom-npm-2.3.1-0ba04b648c-4f5022de97.zip new file mode 100644 index 00000000000..3a5ef019545 Binary files /dev/null and b/.yarn/cache/sonic-boom-npm-2.3.1-0ba04b648c-4f5022de97.zip differ diff --git a/.yarn/cache/sort-keys-npm-4.2.0-bf52ceef80-1535ffd5a7.zip b/.yarn/cache/sort-keys-npm-4.2.0-bf52ceef80-1535ffd5a7.zip new file mode 100644 index 00000000000..d8553ba2211 Binary files /dev/null and b/.yarn/cache/sort-keys-npm-4.2.0-bf52ceef80-1535ffd5a7.zip differ diff --git a/.yarn/cache/source-map-npm-0.5.7-7c3f035429-5dc2043b93.zip b/.yarn/cache/source-map-npm-0.5.7-7c3f035429-5dc2043b93.zip new file mode 100644 index 00000000000..de83a424251 Binary files /dev/null and b/.yarn/cache/source-map-npm-0.5.7-7c3f035429-5dc2043b93.zip differ diff --git a/.yarn/cache/source-map-npm-0.6.1-1a3621db16-59ce8640cf.zip b/.yarn/cache/source-map-npm-0.6.1-1a3621db16-59ce8640cf.zip new file mode 100644 index 00000000000..5f6c0e46b75 Binary files /dev/null and b/.yarn/cache/source-map-npm-0.6.1-1a3621db16-59ce8640cf.zip differ diff --git a/.yarn/cache/source-map-npm-0.7.3-e3b4f7982a-cd24efb3b8.zip b/.yarn/cache/source-map-npm-0.7.3-e3b4f7982a-cd24efb3b8.zip new file mode 100644 index 00000000000..8803e46123a Binary files /dev/null and b/.yarn/cache/source-map-npm-0.7.3-e3b4f7982a-cd24efb3b8.zip differ diff --git a/.yarn/cache/source-map-support-npm-0.5.21-09ca99e250-43e98d700d.zip b/.yarn/cache/source-map-support-npm-0.5.21-09ca99e250-43e98d700d.zip new file mode 100644 index 00000000000..5fc27c8438d Binary files /dev/null and b/.yarn/cache/source-map-support-npm-0.5.21-09ca99e250-43e98d700d.zip differ diff --git a/.yarn/cache/sparse-bitfield-npm-3.0.3-cb80d0c89f-174da88dbb.zip b/.yarn/cache/sparse-bitfield-npm-3.0.3-cb80d0c89f-174da88dbb.zip new file mode 100644 index 00000000000..7c43c8bc9d0 Binary files /dev/null and b/.yarn/cache/sparse-bitfield-npm-3.0.3-cb80d0c89f-174da88dbb.zip differ diff --git a/.yarn/cache/spawn-command-npm-0.0.2-014d4d5d9f-e35c5d2817.zip b/.yarn/cache/spawn-command-npm-0.0.2-014d4d5d9f-e35c5d2817.zip new file mode 100644 index 00000000000..7808b8c239a Binary files /dev/null and b/.yarn/cache/spawn-command-npm-0.0.2-014d4d5d9f-e35c5d2817.zip differ diff --git a/.yarn/cache/spawn-wrap-npm-2.0.0-368c0a5bad-5a518e3762.zip b/.yarn/cache/spawn-wrap-npm-2.0.0-368c0a5bad-5a518e3762.zip new file mode 100644 index 00000000000..b726e6f9b68 Binary files /dev/null and b/.yarn/cache/spawn-wrap-npm-2.0.0-368c0a5bad-5a518e3762.zip differ diff --git a/.yarn/cache/spdx-correct-npm-3.1.1-47f574c27a-77ce438344.zip b/.yarn/cache/spdx-correct-npm-3.1.1-47f574c27a-77ce438344.zip new file mode 100644 index 00000000000..6f5caaecb9f Binary files /dev/null and b/.yarn/cache/spdx-correct-npm-3.1.1-47f574c27a-77ce438344.zip differ diff --git a/.yarn/cache/spdx-exceptions-npm-2.3.0-2b68dad75a-cb69a26fa3.zip b/.yarn/cache/spdx-exceptions-npm-2.3.0-2b68dad75a-cb69a26fa3.zip new file mode 100644 index 00000000000..faebf4211d0 Binary files /dev/null and b/.yarn/cache/spdx-exceptions-npm-2.3.0-2b68dad75a-cb69a26fa3.zip differ diff --git a/.yarn/cache/spdx-expression-parse-npm-3.0.1-b718cbb35a-a1c6e104a2.zip b/.yarn/cache/spdx-expression-parse-npm-3.0.1-b718cbb35a-a1c6e104a2.zip new file mode 100644 index 00000000000..dcb97d090ac Binary files /dev/null and b/.yarn/cache/spdx-expression-parse-npm-3.0.1-b718cbb35a-a1c6e104a2.zip differ diff --git a/.yarn/cache/spdx-license-ids-npm-3.0.11-a8d9a5ff74-1da1acb090.zip b/.yarn/cache/spdx-license-ids-npm-3.0.11-a8d9a5ff74-1da1acb090.zip new file mode 100644 index 00000000000..c8712c25e9d Binary files /dev/null and b/.yarn/cache/spdx-license-ids-npm-3.0.11-a8d9a5ff74-1da1acb090.zip differ diff --git a/.yarn/cache/split-ca-npm-1.0.1-8e5f2e1d22-1e7409938a.zip b/.yarn/cache/split-ca-npm-1.0.1-8e5f2e1d22-1e7409938a.zip new file mode 100644 index 00000000000..b0173ca605b Binary files /dev/null and b/.yarn/cache/split-ca-npm-1.0.1-8e5f2e1d22-1e7409938a.zip differ diff --git a/.yarn/cache/split-npm-1.0.1-88871d88a2-12f4554a57.zip b/.yarn/cache/split-npm-1.0.1-88871d88a2-12f4554a57.zip new file mode 100644 index 00000000000..b2109d88cb2 Binary files /dev/null and b/.yarn/cache/split-npm-1.0.1-88871d88a2-12f4554a57.zip differ diff --git a/.yarn/cache/split2-npm-3.2.2-4ccd21b4f7-8127ddbedd.zip b/.yarn/cache/split2-npm-3.2.2-4ccd21b4f7-8127ddbedd.zip new file mode 100644 index 00000000000..1dddb3f7bea Binary files /dev/null and b/.yarn/cache/split2-npm-3.2.2-4ccd21b4f7-8127ddbedd.zip differ diff --git a/.yarn/cache/sprintf-js-npm-1.0.3-73f0a322fa-19d79aec21.zip b/.yarn/cache/sprintf-js-npm-1.0.3-73f0a322fa-19d79aec21.zip new file mode 100644 index 00000000000..dd2402eabcb Binary files /dev/null and b/.yarn/cache/sprintf-js-npm-1.0.3-73f0a322fa-19d79aec21.zip differ diff --git a/.yarn/cache/ssh2-npm-1.5.0-8a0e3032ea-6a2252c12d.zip b/.yarn/cache/ssh2-npm-1.5.0-8a0e3032ea-6a2252c12d.zip new file mode 100644 index 00000000000..1a111c54747 Binary files /dev/null and b/.yarn/cache/ssh2-npm-1.5.0-8a0e3032ea-6a2252c12d.zip differ diff --git a/.yarn/cache/sshpk-npm-1.16.1-feb759e7e0-5e76afd1ce.zip b/.yarn/cache/sshpk-npm-1.16.1-feb759e7e0-5e76afd1ce.zip new file mode 100644 index 00000000000..769764ba1f4 Binary files /dev/null and b/.yarn/cache/sshpk-npm-1.16.1-feb759e7e0-5e76afd1ce.zip differ diff --git a/.yarn/cache/ssri-npm-8.0.1-a369e72ce2-bc447f5af8.zip b/.yarn/cache/ssri-npm-8.0.1-a369e72ce2-bc447f5af8.zip new file mode 100644 index 00000000000..ca725795a32 Binary files /dev/null and b/.yarn/cache/ssri-npm-8.0.1-a369e72ce2-bc447f5af8.zip differ diff --git a/.yarn/cache/stack-trace-npm-0.0.10-9460b173e1-473036ad32.zip b/.yarn/cache/stack-trace-npm-0.0.10-9460b173e1-473036ad32.zip new file mode 100644 index 00000000000..6674dc5c4f5 Binary files /dev/null and b/.yarn/cache/stack-trace-npm-0.0.10-9460b173e1-473036ad32.zip differ diff --git a/.yarn/cache/stack-utils-npm-2.0.5-e0438f409a-76b69da0f5.zip b/.yarn/cache/stack-utils-npm-2.0.5-e0438f409a-76b69da0f5.zip new file mode 100644 index 00000000000..3c5047d9812 Binary files /dev/null and b/.yarn/cache/stack-utils-npm-2.0.5-e0438f409a-76b69da0f5.zip differ diff --git a/.yarn/cache/statuses-npm-1.5.0-f88f91b2e9-c469b9519d.zip b/.yarn/cache/statuses-npm-1.5.0-f88f91b2e9-c469b9519d.zip new file mode 100644 index 00000000000..5517a94471c Binary files /dev/null and b/.yarn/cache/statuses-npm-1.5.0-f88f91b2e9-c469b9519d.zip differ diff --git a/.yarn/cache/stealthy-require-npm-1.1.1-0105ec8207-6805b857a9.zip b/.yarn/cache/stealthy-require-npm-1.1.1-0105ec8207-6805b857a9.zip new file mode 100644 index 00000000000..fca25b4234f Binary files /dev/null and b/.yarn/cache/stealthy-require-npm-1.1.1-0105ec8207-6805b857a9.zip differ diff --git a/.yarn/cache/stream-browserify-npm-2.0.2-145ceec889-8de7bcab55.zip b/.yarn/cache/stream-browserify-npm-2.0.2-145ceec889-8de7bcab55.zip new file mode 100644 index 00000000000..e6453f1a4d4 Binary files /dev/null and b/.yarn/cache/stream-browserify-npm-2.0.2-145ceec889-8de7bcab55.zip differ diff --git a/.yarn/cache/stream-browserify-npm-3.0.0-4c0bd97245-4c47ef64d6.zip b/.yarn/cache/stream-browserify-npm-3.0.0-4c0bd97245-4c47ef64d6.zip new file mode 100644 index 00000000000..57e1f6e3074 Binary files /dev/null and b/.yarn/cache/stream-browserify-npm-3.0.0-4c0bd97245-4c47ef64d6.zip differ diff --git a/.yarn/cache/stream-combiner2-npm-1.1.1-72d11c75e4-dd32d179fa.zip b/.yarn/cache/stream-combiner2-npm-1.1.1-72d11c75e4-dd32d179fa.zip new file mode 100644 index 00000000000..1e10605ad0d Binary files /dev/null and b/.yarn/cache/stream-combiner2-npm-1.1.1-72d11c75e4-dd32d179fa.zip differ diff --git a/.yarn/cache/stream-http-npm-3.2.0-c6d720ac4f-c9b78453ae.zip b/.yarn/cache/stream-http-npm-3.2.0-c6d720ac4f-c9b78453ae.zip new file mode 100644 index 00000000000..d2087b909fa Binary files /dev/null and b/.yarn/cache/stream-http-npm-3.2.0-c6d720ac4f-c9b78453ae.zip differ diff --git a/.yarn/cache/stream-splicer-npm-2.0.1-add41315d2-7bb3563961.zip b/.yarn/cache/stream-splicer-npm-2.0.1-add41315d2-7bb3563961.zip new file mode 100644 index 00000000000..c38ae2dc70d Binary files /dev/null and b/.yarn/cache/stream-splicer-npm-2.0.1-add41315d2-7bb3563961.zip differ diff --git a/.yarn/cache/streamroller-npm-2.2.4-84aaab4674-83060ded80.zip b/.yarn/cache/streamroller-npm-2.2.4-84aaab4674-83060ded80.zip new file mode 100644 index 00000000000..c0167be10f3 Binary files /dev/null and b/.yarn/cache/streamroller-npm-2.2.4-84aaab4674-83060ded80.zip differ diff --git a/.yarn/cache/string-width-npm-1.0.2-01031f9add-5c79439e95.zip b/.yarn/cache/string-width-npm-1.0.2-01031f9add-5c79439e95.zip new file mode 100644 index 00000000000..a1384227f1a Binary files /dev/null and b/.yarn/cache/string-width-npm-1.0.2-01031f9add-5c79439e95.zip differ diff --git a/.yarn/cache/string-width-npm-2.1.1-0c2c6ae53f-d6173abe08.zip b/.yarn/cache/string-width-npm-2.1.1-0c2c6ae53f-d6173abe08.zip new file mode 100644 index 00000000000..4547a8bf7c3 Binary files /dev/null and b/.yarn/cache/string-width-npm-2.1.1-0c2c6ae53f-d6173abe08.zip differ diff --git a/.yarn/cache/string-width-npm-3.1.0-e031bfa4e0-57f7ca73d2.zip b/.yarn/cache/string-width-npm-3.1.0-e031bfa4e0-57f7ca73d2.zip new file mode 100644 index 00000000000..706d03c8c70 Binary files /dev/null and b/.yarn/cache/string-width-npm-3.1.0-e031bfa4e0-57f7ca73d2.zip differ diff --git a/.yarn/cache/string-width-npm-4.2.3-2c27177bae-e52c10dc3f.zip b/.yarn/cache/string-width-npm-4.2.3-2c27177bae-e52c10dc3f.zip new file mode 100644 index 00000000000..9b4c088118f Binary files /dev/null and b/.yarn/cache/string-width-npm-4.2.3-2c27177bae-e52c10dc3f.zip differ diff --git a/.yarn/cache/string.prototype.trimend-npm-1.0.4-a656b8fe24-17e5aa45c3.zip b/.yarn/cache/string.prototype.trimend-npm-1.0.4-a656b8fe24-17e5aa45c3.zip new file mode 100644 index 00000000000..3a6cb8db614 Binary files /dev/null and b/.yarn/cache/string.prototype.trimend-npm-1.0.4-a656b8fe24-17e5aa45c3.zip differ diff --git a/.yarn/cache/string.prototype.trimstart-npm-1.0.4-b31f5e7c85-3fb06818d3.zip b/.yarn/cache/string.prototype.trimstart-npm-1.0.4-b31f5e7c85-3fb06818d3.zip new file mode 100644 index 00000000000..477439a7209 Binary files /dev/null and b/.yarn/cache/string.prototype.trimstart-npm-1.0.4-b31f5e7c85-3fb06818d3.zip differ diff --git a/.yarn/cache/string_decoder-npm-0.10.31-851f3f7302-fe00f8e303.zip b/.yarn/cache/string_decoder-npm-0.10.31-851f3f7302-fe00f8e303.zip new file mode 100644 index 00000000000..52b4bfdbad1 Binary files /dev/null and b/.yarn/cache/string_decoder-npm-0.10.31-851f3f7302-fe00f8e303.zip differ diff --git a/.yarn/cache/string_decoder-npm-1.1.1-e46a6c1353-9ab7e56f9d.zip b/.yarn/cache/string_decoder-npm-1.1.1-e46a6c1353-9ab7e56f9d.zip new file mode 100644 index 00000000000..8f86a62f82d Binary files /dev/null and b/.yarn/cache/string_decoder-npm-1.1.1-e46a6c1353-9ab7e56f9d.zip differ diff --git a/.yarn/cache/string_decoder-npm-1.3.0-2422117fd0-8417646695.zip b/.yarn/cache/string_decoder-npm-1.3.0-2422117fd0-8417646695.zip new file mode 100644 index 00000000000..e12cf759105 Binary files /dev/null and b/.yarn/cache/string_decoder-npm-1.3.0-2422117fd0-8417646695.zip differ diff --git a/.yarn/cache/strip-ansi-npm-3.0.1-6aec1365b9-9b974de611.zip b/.yarn/cache/strip-ansi-npm-3.0.1-6aec1365b9-9b974de611.zip new file mode 100644 index 00000000000..a1c9f6a0b62 Binary files /dev/null and b/.yarn/cache/strip-ansi-npm-3.0.1-6aec1365b9-9b974de611.zip differ diff --git a/.yarn/cache/strip-ansi-npm-4.0.0-d4de985014-d9186e6c0c.zip b/.yarn/cache/strip-ansi-npm-4.0.0-d4de985014-d9186e6c0c.zip new file mode 100644 index 00000000000..f39efd27f2a Binary files /dev/null and b/.yarn/cache/strip-ansi-npm-4.0.0-d4de985014-d9186e6c0c.zip differ diff --git a/.yarn/cache/strip-ansi-npm-5.2.0-275214c316-bdb5f76ade.zip b/.yarn/cache/strip-ansi-npm-5.2.0-275214c316-bdb5f76ade.zip new file mode 100644 index 00000000000..2231cf5894b Binary files /dev/null and b/.yarn/cache/strip-ansi-npm-5.2.0-275214c316-bdb5f76ade.zip differ diff --git a/.yarn/cache/strip-ansi-npm-6.0.1-caddc7cb40-f3cd25890a.zip b/.yarn/cache/strip-ansi-npm-6.0.1-caddc7cb40-f3cd25890a.zip new file mode 100644 index 00000000000..1a63f3baa20 Binary files /dev/null and b/.yarn/cache/strip-ansi-npm-6.0.1-caddc7cb40-f3cd25890a.zip differ diff --git a/.yarn/cache/strip-bom-buf-npm-1.0.0-056a57a073-246665fa1c.zip b/.yarn/cache/strip-bom-buf-npm-1.0.0-056a57a073-246665fa1c.zip new file mode 100644 index 00000000000..b8d65a7dd6d Binary files /dev/null and b/.yarn/cache/strip-bom-buf-npm-1.0.0-056a57a073-246665fa1c.zip differ diff --git a/.yarn/cache/strip-bom-npm-2.0.0-5c4b64ed5a-08efb746bc.zip b/.yarn/cache/strip-bom-npm-2.0.0-5c4b64ed5a-08efb746bc.zip new file mode 100644 index 00000000000..b33e06cceb2 Binary files /dev/null and b/.yarn/cache/strip-bom-npm-2.0.0-5c4b64ed5a-08efb746bc.zip differ diff --git a/.yarn/cache/strip-bom-npm-3.0.0-71e8f81ff9-8d50ff27b7.zip b/.yarn/cache/strip-bom-npm-3.0.0-71e8f81ff9-8d50ff27b7.zip new file mode 100644 index 00000000000..e6e88c61081 Binary files /dev/null and b/.yarn/cache/strip-bom-npm-3.0.0-71e8f81ff9-8d50ff27b7.zip differ diff --git a/.yarn/cache/strip-bom-npm-4.0.0-97d367a64d-9dbcfbaf50.zip b/.yarn/cache/strip-bom-npm-4.0.0-97d367a64d-9dbcfbaf50.zip new file mode 100644 index 00000000000..7f5558f2a91 Binary files /dev/null and b/.yarn/cache/strip-bom-npm-4.0.0-97d367a64d-9dbcfbaf50.zip differ diff --git a/.yarn/cache/strip-bom-stream-npm-2.0.0-e1d65f77cc-3e2ff494d9.zip b/.yarn/cache/strip-bom-stream-npm-2.0.0-e1d65f77cc-3e2ff494d9.zip new file mode 100644 index 00000000000..ed5441580e0 Binary files /dev/null and b/.yarn/cache/strip-bom-stream-npm-2.0.0-e1d65f77cc-3e2ff494d9.zip differ diff --git a/.yarn/cache/strip-eof-npm-1.0.0-d82eaf947c-40bc8ddd7e.zip b/.yarn/cache/strip-eof-npm-1.0.0-d82eaf947c-40bc8ddd7e.zip new file mode 100644 index 00000000000..41df014751a Binary files /dev/null and b/.yarn/cache/strip-eof-npm-1.0.0-d82eaf947c-40bc8ddd7e.zip differ diff --git a/.yarn/cache/strip-final-newline-npm-2.0.0-340c4f7c66-69412b5e25.zip b/.yarn/cache/strip-final-newline-npm-2.0.0-340c4f7c66-69412b5e25.zip new file mode 100644 index 00000000000..92534423478 Binary files /dev/null and b/.yarn/cache/strip-final-newline-npm-2.0.0-340c4f7c66-69412b5e25.zip differ diff --git a/.yarn/cache/strip-indent-npm-3.0.0-519e75a28d-18f045d57d.zip b/.yarn/cache/strip-indent-npm-3.0.0-519e75a28d-18f045d57d.zip new file mode 100644 index 00000000000..d24c4848489 Binary files /dev/null and b/.yarn/cache/strip-indent-npm-3.0.0-519e75a28d-18f045d57d.zip differ diff --git a/.yarn/cache/strip-json-comments-npm-2.0.1-e7883b2d04-1074ccb632.zip b/.yarn/cache/strip-json-comments-npm-2.0.1-e7883b2d04-1074ccb632.zip new file mode 100644 index 00000000000..9c537fe050a Binary files /dev/null and b/.yarn/cache/strip-json-comments-npm-2.0.1-e7883b2d04-1074ccb632.zip differ diff --git a/.yarn/cache/strip-json-comments-npm-3.1.1-dcb2324823-492f73e272.zip b/.yarn/cache/strip-json-comments-npm-3.1.1-dcb2324823-492f73e272.zip new file mode 100644 index 00000000000..e74ed10a6fa Binary files /dev/null and b/.yarn/cache/strip-json-comments-npm-3.1.1-dcb2324823-492f73e272.zip differ diff --git a/.yarn/cache/subarg-npm-1.0.0-05f4a18d07-8359df72e9.zip b/.yarn/cache/subarg-npm-1.0.0-05f4a18d07-8359df72e9.zip new file mode 100644 index 00000000000..8d9fa16622c Binary files /dev/null and b/.yarn/cache/subarg-npm-1.0.0-05f4a18d07-8359df72e9.zip differ diff --git a/.yarn/cache/supports-color-npm-2.0.0-22c0f0adbc-602538c581.zip b/.yarn/cache/supports-color-npm-2.0.0-22c0f0adbc-602538c581.zip new file mode 100644 index 00000000000..c4608ecfe95 Binary files /dev/null and b/.yarn/cache/supports-color-npm-2.0.0-22c0f0adbc-602538c581.zip differ diff --git a/.yarn/cache/supports-color-npm-5.5.0-183ac537bc-95f6f4ba5a.zip b/.yarn/cache/supports-color-npm-5.5.0-183ac537bc-95f6f4ba5a.zip new file mode 100644 index 00000000000..aa46b9881db Binary files /dev/null and b/.yarn/cache/supports-color-npm-5.5.0-183ac537bc-95f6f4ba5a.zip differ diff --git a/.yarn/cache/supports-color-npm-7.2.0-606bfcf7da-3dda818de0.zip b/.yarn/cache/supports-color-npm-7.2.0-606bfcf7da-3dda818de0.zip new file mode 100644 index 00000000000..1fd9e12d4ec Binary files /dev/null and b/.yarn/cache/supports-color-npm-7.2.0-606bfcf7da-3dda818de0.zip differ diff --git a/.yarn/cache/supports-color-npm-8.1.1-289e937149-c052193a7e.zip b/.yarn/cache/supports-color-npm-8.1.1-289e937149-c052193a7e.zip new file mode 100644 index 00000000000..3fd0d6c6a42 Binary files /dev/null and b/.yarn/cache/supports-color-npm-8.1.1-289e937149-c052193a7e.zip differ diff --git a/.yarn/cache/supports-hyperlinks-npm-2.2.0-9b22a6271b-aef04fb41f.zip b/.yarn/cache/supports-hyperlinks-npm-2.2.0-9b22a6271b-aef04fb41f.zip new file mode 100644 index 00000000000..bbb1bbd4885 Binary files /dev/null and b/.yarn/cache/supports-hyperlinks-npm-2.2.0-9b22a6271b-aef04fb41f.zip differ diff --git a/.yarn/cache/supports-preserve-symlinks-flag-npm-1.0.0-f17c4d0028-53b1e247e6.zip b/.yarn/cache/supports-preserve-symlinks-flag-npm-1.0.0-f17c4d0028-53b1e247e6.zip new file mode 100644 index 00000000000..07a2c831552 Binary files /dev/null and b/.yarn/cache/supports-preserve-symlinks-flag-npm-1.0.0-f17c4d0028-53b1e247e6.zip differ diff --git a/.yarn/cache/swagger-jsdoc-npm-3.7.0-483149b581-436e6321f3.zip b/.yarn/cache/swagger-jsdoc-npm-3.7.0-483149b581-436e6321f3.zip new file mode 100644 index 00000000000..620d3f503b5 Binary files /dev/null and b/.yarn/cache/swagger-jsdoc-npm-3.7.0-483149b581-436e6321f3.zip differ diff --git a/.yarn/cache/swagger-methods-npm-2.0.2-81541b17dc-1321362be7.zip b/.yarn/cache/swagger-methods-npm-2.0.2-81541b17dc-1321362be7.zip new file mode 100644 index 00000000000..857293da87a Binary files /dev/null and b/.yarn/cache/swagger-methods-npm-2.0.2-81541b17dc-1321362be7.zip differ diff --git a/.yarn/cache/swagger-parser-npm-8.0.4-0123559a75-cd07ac3dbe.zip b/.yarn/cache/swagger-parser-npm-8.0.4-0123559a75-cd07ac3dbe.zip new file mode 100644 index 00000000000..80cec89a9de Binary files /dev/null and b/.yarn/cache/swagger-parser-npm-8.0.4-0123559a75-cd07ac3dbe.zip differ diff --git a/.yarn/cache/syntax-error-npm-1.4.0-8721590265-c1c3f048fe.zip b/.yarn/cache/syntax-error-npm-1.4.0-8721590265-c1c3f048fe.zip new file mode 100644 index 00000000000..4c6e07e6748 Binary files /dev/null and b/.yarn/cache/syntax-error-npm-1.4.0-8721590265-c1c3f048fe.zip differ diff --git a/.yarn/cache/table-npm-5.4.6-190b118384-9e35d3efa7.zip b/.yarn/cache/table-npm-5.4.6-190b118384-9e35d3efa7.zip new file mode 100644 index 00000000000..386d1baae9d Binary files /dev/null and b/.yarn/cache/table-npm-5.4.6-190b118384-9e35d3efa7.zip differ diff --git a/.yarn/cache/table-npm-6.7.3-a96402c315-61d732f511.zip b/.yarn/cache/table-npm-6.7.3-a96402c315-61d732f511.zip new file mode 100644 index 00000000000..cf29814d61d Binary files /dev/null and b/.yarn/cache/table-npm-6.7.3-a96402c315-61d732f511.zip differ diff --git a/.yarn/cache/taketalk-npm-1.0.0-2fc66802cb-b9a6ae2d6e.zip b/.yarn/cache/taketalk-npm-1.0.0-2fc66802cb-b9a6ae2d6e.zip new file mode 100644 index 00000000000..2dd756dc159 Binary files /dev/null and b/.yarn/cache/taketalk-npm-1.0.0-2fc66802cb-b9a6ae2d6e.zip differ diff --git a/.yarn/cache/tapable-npm-1.1.3-f1c2843426-53ff4e7c39.zip b/.yarn/cache/tapable-npm-1.1.3-f1c2843426-53ff4e7c39.zip new file mode 100644 index 00000000000..2699e99ef33 Binary files /dev/null and b/.yarn/cache/tapable-npm-1.1.3-f1c2843426-53ff4e7c39.zip differ diff --git a/.yarn/cache/tapable-npm-2.2.1-8cf5ff3039-3b7a1b4d86.zip b/.yarn/cache/tapable-npm-2.2.1-8cf5ff3039-3b7a1b4d86.zip new file mode 100644 index 00000000000..279942dd564 Binary files /dev/null and b/.yarn/cache/tapable-npm-2.2.1-8cf5ff3039-3b7a1b4d86.zip differ diff --git a/.yarn/cache/tar-fs-npm-2.0.1-0734c93785-26cd297ed2.zip b/.yarn/cache/tar-fs-npm-2.0.1-0734c93785-26cd297ed2.zip new file mode 100644 index 00000000000..9e828cfc1c3 Binary files /dev/null and b/.yarn/cache/tar-fs-npm-2.0.1-0734c93785-26cd297ed2.zip differ diff --git a/.yarn/cache/tar-fs-npm-2.1.1-e374d3b7a2-f5b9a70059.zip b/.yarn/cache/tar-fs-npm-2.1.1-e374d3b7a2-f5b9a70059.zip new file mode 100644 index 00000000000..f256de33b95 Binary files /dev/null and b/.yarn/cache/tar-fs-npm-2.1.1-e374d3b7a2-f5b9a70059.zip differ diff --git a/.yarn/cache/tar-npm-6.1.11-e6ac3cba9c-a04c07bb9e.zip b/.yarn/cache/tar-npm-6.1.11-e6ac3cba9c-a04c07bb9e.zip new file mode 100644 index 00000000000..d4e5d8f310b Binary files /dev/null and b/.yarn/cache/tar-npm-6.1.11-e6ac3cba9c-a04c07bb9e.zip differ diff --git a/.yarn/cache/tar-stream-npm-2.2.0-884c79b510-699831a8b9.zip b/.yarn/cache/tar-stream-npm-2.2.0-884c79b510-699831a8b9.zip new file mode 100644 index 00000000000..6d7267b3139 Binary files /dev/null and b/.yarn/cache/tar-stream-npm-2.2.0-884c79b510-699831a8b9.zip differ diff --git a/.yarn/cache/temp-dir-npm-2.0.0-e8af180805-cc4f0404bf.zip b/.yarn/cache/temp-dir-npm-2.0.0-e8af180805-cc4f0404bf.zip new file mode 100644 index 00000000000..d84cb672ad8 Binary files /dev/null and b/.yarn/cache/temp-dir-npm-2.0.0-e8af180805-cc4f0404bf.zip differ diff --git a/.yarn/cache/tempfile-npm-3.0.0-fcac8b1ecd-ebf07b7e58.zip b/.yarn/cache/tempfile-npm-3.0.0-fcac8b1ecd-ebf07b7e58.zip new file mode 100644 index 00000000000..9e2f63bf222 Binary files /dev/null and b/.yarn/cache/tempfile-npm-3.0.0-fcac8b1ecd-ebf07b7e58.zip differ diff --git a/.yarn/cache/terser-npm-5.10.0-1690d2acb8-1080faeb6d.zip b/.yarn/cache/terser-npm-5.10.0-1690d2acb8-1080faeb6d.zip new file mode 100644 index 00000000000..6cf0f7fecf1 Binary files /dev/null and b/.yarn/cache/terser-npm-5.10.0-1690d2acb8-1080faeb6d.zip differ diff --git a/.yarn/cache/terser-webpack-plugin-npm-5.3.3-659a8e4514-4b8d508d8a.zip b/.yarn/cache/terser-webpack-plugin-npm-5.3.3-659a8e4514-4b8d508d8a.zip new file mode 100644 index 00000000000..b02417795c3 Binary files /dev/null and b/.yarn/cache/terser-webpack-plugin-npm-5.3.3-659a8e4514-4b8d508d8a.zip differ diff --git a/.yarn/cache/test-exclude-npm-6.0.0-3fb03d69df-3b34a3d771.zip b/.yarn/cache/test-exclude-npm-6.0.0-3fb03d69df-3b34a3d771.zip new file mode 100644 index 00000000000..00b9c4c041c Binary files /dev/null and b/.yarn/cache/test-exclude-npm-6.0.0-3fb03d69df-3b34a3d771.zip differ diff --git a/.yarn/cache/text-extensions-npm-1.9.0-87655d768f-56a9962c1b.zip b/.yarn/cache/text-extensions-npm-1.9.0-87655d768f-56a9962c1b.zip new file mode 100644 index 00000000000..0c2ccda3802 Binary files /dev/null and b/.yarn/cache/text-extensions-npm-1.9.0-87655d768f-56a9962c1b.zip differ diff --git a/.yarn/cache/text-hex-npm-1.0.0-22389e4d56-1138f68adc.zip b/.yarn/cache/text-hex-npm-1.0.0-22389e4d56-1138f68adc.zip new file mode 100644 index 00000000000..ce4bf0be7e8 Binary files /dev/null and b/.yarn/cache/text-hex-npm-1.0.0-22389e4d56-1138f68adc.zip differ diff --git a/.yarn/cache/text-table-npm-0.2.0-d92a778b59-b6937a38c8.zip b/.yarn/cache/text-table-npm-0.2.0-d92a778b59-b6937a38c8.zip new file mode 100644 index 00000000000..08df4834d08 Binary files /dev/null and b/.yarn/cache/text-table-npm-0.2.0-d92a778b59-b6937a38c8.zip differ diff --git a/.yarn/cache/textextensions-npm-5.14.0-5251a1bdcc-1f610ccf2a.zip b/.yarn/cache/textextensions-npm-5.14.0-5251a1bdcc-1f610ccf2a.zip new file mode 100644 index 00000000000..e103b7493cc Binary files /dev/null and b/.yarn/cache/textextensions-npm-5.14.0-5251a1bdcc-1f610ccf2a.zip differ diff --git a/.yarn/cache/through-npm-2.3.8-df5f72a16e-a38c3e0598.zip b/.yarn/cache/through-npm-2.3.8-df5f72a16e-a38c3e0598.zip new file mode 100644 index 00000000000..425b87ec87d Binary files /dev/null and b/.yarn/cache/through-npm-2.3.8-df5f72a16e-a38c3e0598.zip differ diff --git a/.yarn/cache/through2-npm-2.0.5-77d90f13cd-beb0f338aa.zip b/.yarn/cache/through2-npm-2.0.5-77d90f13cd-beb0f338aa.zip new file mode 100644 index 00000000000..984ead670ad Binary files /dev/null and b/.yarn/cache/through2-npm-2.0.5-77d90f13cd-beb0f338aa.zip differ diff --git a/.yarn/cache/through2-npm-3.0.2-403f837012-47c9586c73.zip b/.yarn/cache/through2-npm-3.0.2-403f837012-47c9586c73.zip new file mode 100644 index 00000000000..7dbb5653351 Binary files /dev/null and b/.yarn/cache/through2-npm-3.0.2-403f837012-47c9586c73.zip differ diff --git a/.yarn/cache/through2-npm-4.0.2-da7b2da443-ac7430bd54.zip b/.yarn/cache/through2-npm-4.0.2-da7b2da443-ac7430bd54.zip new file mode 100644 index 00000000000..7fd3f010b26 Binary files /dev/null and b/.yarn/cache/through2-npm-4.0.2-da7b2da443-ac7430bd54.zip differ diff --git a/.yarn/cache/timers-browserify-npm-1.4.2-40215963ae-b7437e2286.zip b/.yarn/cache/timers-browserify-npm-1.4.2-40215963ae-b7437e2286.zip new file mode 100644 index 00000000000..294c6613732 Binary files /dev/null and b/.yarn/cache/timers-browserify-npm-1.4.2-40215963ae-b7437e2286.zip differ diff --git a/.yarn/cache/tiny-emitter-npm-2.1.0-2a4d94f487-fbcfb51457.zip b/.yarn/cache/tiny-emitter-npm-2.1.0-2a4d94f487-fbcfb51457.zip new file mode 100644 index 00000000000..00d74e1bc45 Binary files /dev/null and b/.yarn/cache/tiny-emitter-npm-2.1.0-2a4d94f487-fbcfb51457.zip differ diff --git a/.yarn/cache/tls-npm-0.0.1-d44eeeb72e-b0205b0efb.zip b/.yarn/cache/tls-npm-0.0.1-d44eeeb72e-b0205b0efb.zip new file mode 100644 index 00000000000..dd2fac6fe83 Binary files /dev/null and b/.yarn/cache/tls-npm-0.0.1-d44eeeb72e-b0205b0efb.zip differ diff --git a/.yarn/cache/tmp-npm-0.0.33-bcbf65df2a-902d7aceb7.zip b/.yarn/cache/tmp-npm-0.0.33-bcbf65df2a-902d7aceb7.zip new file mode 100644 index 00000000000..fa335bbc043 Binary files /dev/null and b/.yarn/cache/tmp-npm-0.0.33-bcbf65df2a-902d7aceb7.zip differ diff --git a/.yarn/cache/tmp-npm-0.1.0-fa18ef19c4-6bab8431de.zip b/.yarn/cache/tmp-npm-0.1.0-fa18ef19c4-6bab8431de.zip new file mode 100644 index 00000000000..bc7a66fe49e Binary files /dev/null and b/.yarn/cache/tmp-npm-0.1.0-fa18ef19c4-6bab8431de.zip differ diff --git a/.yarn/cache/tmp-npm-0.2.1-a9c8d9c0ca-8b12146541.zip b/.yarn/cache/tmp-npm-0.2.1-a9c8d9c0ca-8b12146541.zip new file mode 100644 index 00000000000..d47a2298adf Binary files /dev/null and b/.yarn/cache/tmp-npm-0.2.1-a9c8d9c0ca-8b12146541.zip differ diff --git a/.yarn/cache/to-fast-properties-npm-2.0.0-0dc60cc481-be2de62fe5.zip b/.yarn/cache/to-fast-properties-npm-2.0.0-0dc60cc481-be2de62fe5.zip new file mode 100644 index 00000000000..bed5e126bb7 Binary files /dev/null and b/.yarn/cache/to-fast-properties-npm-2.0.0-0dc60cc481-be2de62fe5.zip differ diff --git a/.yarn/cache/to-readable-stream-npm-1.0.0-4fa4da8130-2bd7778490.zip b/.yarn/cache/to-readable-stream-npm-1.0.0-4fa4da8130-2bd7778490.zip new file mode 100644 index 00000000000..85ae12722f6 Binary files /dev/null and b/.yarn/cache/to-readable-stream-npm-1.0.0-4fa4da8130-2bd7778490.zip differ diff --git a/.yarn/cache/to-regex-range-npm-5.0.1-f1e8263b00-f76fa01b3d.zip b/.yarn/cache/to-regex-range-npm-5.0.1-f1e8263b00-f76fa01b3d.zip new file mode 100644 index 00000000000..acdc9630b7f Binary files /dev/null and b/.yarn/cache/to-regex-range-npm-5.0.1-f1e8263b00-f76fa01b3d.zip differ diff --git a/.yarn/cache/toidentifier-npm-1.0.0-5dad252f90-199e6bfca1.zip b/.yarn/cache/toidentifier-npm-1.0.0-5dad252f90-199e6bfca1.zip new file mode 100644 index 00000000000..27ee34cbcc4 Binary files /dev/null and b/.yarn/cache/toidentifier-npm-1.0.0-5dad252f90-199e6bfca1.zip differ diff --git a/.yarn/cache/touch-npm-3.1.0-e2eacebbda-e0be589cb5.zip b/.yarn/cache/touch-npm-3.1.0-e2eacebbda-e0be589cb5.zip new file mode 100644 index 00000000000..84e3b238098 Binary files /dev/null and b/.yarn/cache/touch-npm-3.1.0-e2eacebbda-e0be589cb5.zip differ diff --git a/.yarn/cache/tough-cookie-npm-2.5.0-79a2fe43fe-16a8cd0902.zip b/.yarn/cache/tough-cookie-npm-2.5.0-79a2fe43fe-16a8cd0902.zip new file mode 100644 index 00000000000..74e27e7464c Binary files /dev/null and b/.yarn/cache/tough-cookie-npm-2.5.0-79a2fe43fe-16a8cd0902.zip differ diff --git a/.yarn/cache/tr46-npm-0.0.3-de53018915-726321c5ea.zip b/.yarn/cache/tr46-npm-0.0.3-de53018915-726321c5ea.zip new file mode 100644 index 00000000000..2e6949bca21 Binary files /dev/null and b/.yarn/cache/tr46-npm-0.0.3-de53018915-726321c5ea.zip differ diff --git a/.yarn/cache/tree-kill-npm-1.2.2-3da0e5a759-49117f5f41.zip b/.yarn/cache/tree-kill-npm-1.2.2-3da0e5a759-49117f5f41.zip new file mode 100644 index 00000000000..c9ef4013714 Binary files /dev/null and b/.yarn/cache/tree-kill-npm-1.2.2-3da0e5a759-49117f5f41.zip differ diff --git a/.yarn/cache/treeverse-npm-1.0.4-dc3cd6f6c7-712640acd8.zip b/.yarn/cache/treeverse-npm-1.0.4-dc3cd6f6c7-712640acd8.zip new file mode 100644 index 00000000000..893878885f1 Binary files /dev/null and b/.yarn/cache/treeverse-npm-1.0.4-dc3cd6f6c7-712640acd8.zip differ diff --git a/.yarn/cache/trim-newlines-npm-3.0.1-22f1f216de-b530f3fadf.zip b/.yarn/cache/trim-newlines-npm-3.0.1-22f1f216de-b530f3fadf.zip new file mode 100644 index 00000000000..78830598d8b Binary files /dev/null and b/.yarn/cache/trim-newlines-npm-3.0.1-22f1f216de-b530f3fadf.zip differ diff --git a/.yarn/cache/triple-beam-npm-1.3.0-eda4e2a46c-7d7b77d862.zip b/.yarn/cache/triple-beam-npm-1.3.0-eda4e2a46c-7d7b77d862.zip new file mode 100644 index 00000000000..5aab747e47c Binary files /dev/null and b/.yarn/cache/triple-beam-npm-1.3.0-eda4e2a46c-7d7b77d862.zip differ diff --git a/.yarn/cache/ts-loader-npm-8.3.0-2a35793883-93dd15b553.zip b/.yarn/cache/ts-loader-npm-8.3.0-2a35793883-93dd15b553.zip new file mode 100644 index 00000000000..dd3c3a73f81 Binary files /dev/null and b/.yarn/cache/ts-loader-npm-8.3.0-2a35793883-93dd15b553.zip differ diff --git a/.yarn/cache/ts-mocha-npm-8.0.0-958ec73bec-66062e82f9.zip b/.yarn/cache/ts-mocha-npm-8.0.0-958ec73bec-66062e82f9.zip new file mode 100644 index 00000000000..e97bb676702 Binary files /dev/null and b/.yarn/cache/ts-mocha-npm-8.0.0-958ec73bec-66062e82f9.zip differ diff --git a/.yarn/cache/ts-mock-imports-npm-1.3.8-ce172e5189-1600946484.zip b/.yarn/cache/ts-mock-imports-npm-1.3.8-ce172e5189-1600946484.zip new file mode 100644 index 00000000000..05ed4ffbdad Binary files /dev/null and b/.yarn/cache/ts-mock-imports-npm-1.3.8-ce172e5189-1600946484.zip differ diff --git a/.yarn/cache/ts-node-npm-10.4.0-04cb6e2279-3933ac0a93.zip b/.yarn/cache/ts-node-npm-10.4.0-04cb6e2279-3933ac0a93.zip new file mode 100644 index 00000000000..219edf1d6f5 Binary files /dev/null and b/.yarn/cache/ts-node-npm-10.4.0-04cb6e2279-3933ac0a93.zip differ diff --git a/.yarn/cache/ts-node-npm-7.0.1-dfa4b9e69b-07ed6ea180.zip b/.yarn/cache/ts-node-npm-7.0.1-dfa4b9e69b-07ed6ea180.zip new file mode 100644 index 00000000000..ac72cf0e948 Binary files /dev/null and b/.yarn/cache/ts-node-npm-7.0.1-dfa4b9e69b-07ed6ea180.zip differ diff --git a/.yarn/cache/tsconfig-paths-npm-3.12.0-b78aadfb3f-4999ec6cd1.zip b/.yarn/cache/tsconfig-paths-npm-3.12.0-b78aadfb3f-4999ec6cd1.zip new file mode 100644 index 00000000000..e96420d1450 Binary files /dev/null and b/.yarn/cache/tsconfig-paths-npm-3.12.0-b78aadfb3f-4999ec6cd1.zip differ diff --git a/.yarn/cache/tslib-npm-1.14.1-102499115e-dbe628ef87.zip b/.yarn/cache/tslib-npm-1.14.1-102499115e-dbe628ef87.zip new file mode 100644 index 00000000000..5569f012b5e Binary files /dev/null and b/.yarn/cache/tslib-npm-1.14.1-102499115e-dbe628ef87.zip differ diff --git a/.yarn/cache/tslib-npm-2.1.0-81c9ac9b82-aa189c8179.zip b/.yarn/cache/tslib-npm-2.1.0-81c9ac9b82-aa189c8179.zip new file mode 100644 index 00000000000..88d2e3b1d61 Binary files /dev/null and b/.yarn/cache/tslib-npm-2.1.0-81c9ac9b82-aa189c8179.zip differ diff --git a/.yarn/cache/tslib-npm-2.3.1-0e21e18015-de17a98d46.zip b/.yarn/cache/tslib-npm-2.3.1-0e21e18015-de17a98d46.zip new file mode 100644 index 00000000000..f2a05ef439a Binary files /dev/null and b/.yarn/cache/tslib-npm-2.3.1-0e21e18015-de17a98d46.zip differ diff --git a/.yarn/cache/tty-browserify-npm-0.0.1-d2494d5a73-93b745d43f.zip b/.yarn/cache/tty-browserify-npm-0.0.1-d2494d5a73-93b745d43f.zip new file mode 100644 index 00000000000..c1c641bbf59 Binary files /dev/null and b/.yarn/cache/tty-browserify-npm-0.0.1-d2494d5a73-93b745d43f.zip differ diff --git a/.yarn/cache/tunnel-agent-npm-0.6.0-64345ab7eb-05f6510358.zip b/.yarn/cache/tunnel-agent-npm-0.6.0-64345ab7eb-05f6510358.zip new file mode 100644 index 00000000000..5256e200850 Binary files /dev/null and b/.yarn/cache/tunnel-agent-npm-0.6.0-64345ab7eb-05f6510358.zip differ diff --git a/.yarn/cache/tweetnacl-npm-0.14.5-a3f766c0d1-6061daba17.zip b/.yarn/cache/tweetnacl-npm-0.14.5-a3f766c0d1-6061daba17.zip new file mode 100644 index 00000000000..2811987dbb0 Binary files /dev/null and b/.yarn/cache/tweetnacl-npm-0.14.5-a3f766c0d1-6061daba17.zip differ diff --git a/.yarn/cache/type-check-npm-0.3.2-a4a38bb0b6-dd3b149564.zip b/.yarn/cache/type-check-npm-0.3.2-a4a38bb0b6-dd3b149564.zip new file mode 100644 index 00000000000..ca46b9c9745 Binary files /dev/null and b/.yarn/cache/type-check-npm-0.3.2-a4a38bb0b6-dd3b149564.zip differ diff --git a/.yarn/cache/type-check-npm-0.4.0-60565800ce-ec688ebfc9.zip b/.yarn/cache/type-check-npm-0.4.0-60565800ce-ec688ebfc9.zip new file mode 100644 index 00000000000..85a029590ea Binary files /dev/null and b/.yarn/cache/type-check-npm-0.4.0-60565800ce-ec688ebfc9.zip differ diff --git a/.yarn/cache/type-detect-npm-4.0.8-8d8127b901-62b5628bff.zip b/.yarn/cache/type-detect-npm-4.0.8-8d8127b901-62b5628bff.zip new file mode 100644 index 00000000000..a3c01d86ab4 Binary files /dev/null and b/.yarn/cache/type-detect-npm-4.0.8-8d8127b901-62b5628bff.zip differ diff --git a/.yarn/cache/type-fest-npm-0.18.1-47b079775d-e96dcee18a.zip b/.yarn/cache/type-fest-npm-0.18.1-47b079775d-e96dcee18a.zip new file mode 100644 index 00000000000..e1ed23905d8 Binary files /dev/null and b/.yarn/cache/type-fest-npm-0.18.1-47b079775d-e96dcee18a.zip differ diff --git a/.yarn/cache/type-fest-npm-0.20.2-b36432617f-4fb3272df2.zip b/.yarn/cache/type-fest-npm-0.20.2-b36432617f-4fb3272df2.zip new file mode 100644 index 00000000000..8222fdcc36f Binary files /dev/null and b/.yarn/cache/type-fest-npm-0.20.2-b36432617f-4fb3272df2.zip differ diff --git a/.yarn/cache/type-fest-npm-0.21.3-5ff2a9c6fd-e6b32a3b38.zip b/.yarn/cache/type-fest-npm-0.21.3-5ff2a9c6fd-e6b32a3b38.zip new file mode 100644 index 00000000000..89f3fd57a9e Binary files /dev/null and b/.yarn/cache/type-fest-npm-0.21.3-5ff2a9c6fd-e6b32a3b38.zip differ diff --git a/.yarn/cache/type-fest-npm-0.6.0-76b229965b-b2188e6e4b.zip b/.yarn/cache/type-fest-npm-0.6.0-76b229965b-b2188e6e4b.zip new file mode 100644 index 00000000000..0456ef6d74a Binary files /dev/null and b/.yarn/cache/type-fest-npm-0.6.0-76b229965b-b2188e6e4b.zip differ diff --git a/.yarn/cache/type-fest-npm-0.8.1-351ad028fe-d61c4b2eba.zip b/.yarn/cache/type-fest-npm-0.8.1-351ad028fe-d61c4b2eba.zip new file mode 100644 index 00000000000..3e3da402a03 Binary files /dev/null and b/.yarn/cache/type-fest-npm-0.8.1-351ad028fe-d61c4b2eba.zip differ diff --git a/.yarn/cache/type-is-npm-1.6.18-6dee4d4961-2c8e47675d.zip b/.yarn/cache/type-is-npm-1.6.18-6dee4d4961-2c8e47675d.zip new file mode 100644 index 00000000000..3bfed96dcd5 Binary files /dev/null and b/.yarn/cache/type-is-npm-1.6.18-6dee4d4961-2c8e47675d.zip differ diff --git a/.yarn/cache/typed-function-npm-2.1.0-de442ec721-168c2c8f76.zip b/.yarn/cache/typed-function-npm-2.1.0-de442ec721-168c2c8f76.zip new file mode 100644 index 00000000000..17991fe426e Binary files /dev/null and b/.yarn/cache/typed-function-npm-2.1.0-de442ec721-168c2c8f76.zip differ diff --git a/.yarn/cache/typedarray-npm-0.0.6-37638b2241-33b39f3d0e.zip b/.yarn/cache/typedarray-npm-0.0.6-37638b2241-33b39f3d0e.zip new file mode 100644 index 00000000000..d03674ea221 Binary files /dev/null and b/.yarn/cache/typedarray-npm-0.0.6-37638b2241-33b39f3d0e.zip differ diff --git a/.yarn/cache/typedarray-to-buffer-npm-3.1.5-aadc11995e-99c11aaa8f.zip b/.yarn/cache/typedarray-to-buffer-npm-3.1.5-aadc11995e-99c11aaa8f.zip new file mode 100644 index 00000000000..0fb80961b53 Binary files /dev/null and b/.yarn/cache/typedarray-to-buffer-npm-3.1.5-aadc11995e-99c11aaa8f.zip differ diff --git a/.yarn/cache/typescript-npm-3.9.10-ab3ca8cc22-46c842e2cd.zip b/.yarn/cache/typescript-npm-3.9.10-ab3ca8cc22-46c842e2cd.zip new file mode 100644 index 00000000000..4e30cbf01b5 Binary files /dev/null and b/.yarn/cache/typescript-npm-3.9.10-ab3ca8cc22-46c842e2cd.zip differ diff --git a/.yarn/cache/typescript-patch-e9c475da82-dc7141ab55.zip b/.yarn/cache/typescript-patch-e9c475da82-dc7141ab55.zip new file mode 100644 index 00000000000..b9c59c18091 Binary files /dev/null and b/.yarn/cache/typescript-patch-e9c475da82-dc7141ab55.zip differ diff --git a/.yarn/cache/ua-parser-js-npm-0.7.31-aeb4c9aae9-e2f8324a83.zip b/.yarn/cache/ua-parser-js-npm-0.7.31-aeb4c9aae9-e2f8324a83.zip new file mode 100644 index 00000000000..935d44046ac Binary files /dev/null and b/.yarn/cache/ua-parser-js-npm-0.7.31-aeb4c9aae9-e2f8324a83.zip differ diff --git a/.yarn/cache/uglify-js-npm-3.14.4-690963fdb4-13217db521.zip b/.yarn/cache/uglify-js-npm-3.14.4-690963fdb4-13217db521.zip new file mode 100644 index 00000000000..99b92188172 Binary files /dev/null and b/.yarn/cache/uglify-js-npm-3.14.4-690963fdb4-13217db521.zip differ diff --git a/.yarn/cache/ultra-runner-npm-3.10.5-9f810878b0-4aed834863.zip b/.yarn/cache/ultra-runner-npm-3.10.5-9f810878b0-4aed834863.zip new file mode 100644 index 00000000000..0201932b1a5 Binary files /dev/null and b/.yarn/cache/ultra-runner-npm-3.10.5-9f810878b0-4aed834863.zip differ diff --git a/.yarn/cache/umd-npm-3.0.3-637d100527-264302acab.zip b/.yarn/cache/umd-npm-3.0.3-637d100527-264302acab.zip new file mode 100644 index 00000000000..de81b32b9a9 Binary files /dev/null and b/.yarn/cache/umd-npm-3.0.3-637d100527-264302acab.zip differ diff --git a/.yarn/cache/unbox-primitive-npm-1.0.1-50b9fde246-89d950e18f.zip b/.yarn/cache/unbox-primitive-npm-1.0.1-50b9fde246-89d950e18f.zip new file mode 100644 index 00000000000..27f2ec7f93d Binary files /dev/null and b/.yarn/cache/unbox-primitive-npm-1.0.1-50b9fde246-89d950e18f.zip differ diff --git a/.yarn/cache/undeclared-identifiers-npm-1.1.3-f4b85bcf76-e1f2a18d7b.zip b/.yarn/cache/undeclared-identifiers-npm-1.1.3-f4b85bcf76-e1f2a18d7b.zip new file mode 100644 index 00000000000..38bed5489f7 Binary files /dev/null and b/.yarn/cache/undeclared-identifiers-npm-1.1.3-f4b85bcf76-e1f2a18d7b.zip differ diff --git a/.yarn/cache/undefsafe-npm-2.0.5-8c3bbf9354-f42ab3b577.zip b/.yarn/cache/undefsafe-npm-2.0.5-8c3bbf9354-f42ab3b577.zip new file mode 100644 index 00000000000..ef05395eb3d Binary files /dev/null and b/.yarn/cache/undefsafe-npm-2.0.5-8c3bbf9354-f42ab3b577.zip differ diff --git a/.yarn/cache/unicode-canonical-property-names-ecmascript-npm-2.0.0-d2d8554a14-39be078afd.zip b/.yarn/cache/unicode-canonical-property-names-ecmascript-npm-2.0.0-d2d8554a14-39be078afd.zip new file mode 100644 index 00000000000..8578f8343bb Binary files /dev/null and b/.yarn/cache/unicode-canonical-property-names-ecmascript-npm-2.0.0-d2d8554a14-39be078afd.zip differ diff --git a/.yarn/cache/unicode-match-property-ecmascript-npm-2.0.0-97a00fd52c-1f34a7434a.zip b/.yarn/cache/unicode-match-property-ecmascript-npm-2.0.0-97a00fd52c-1f34a7434a.zip new file mode 100644 index 00000000000..456f930eb04 Binary files /dev/null and b/.yarn/cache/unicode-match-property-ecmascript-npm-2.0.0-97a00fd52c-1f34a7434a.zip differ diff --git a/.yarn/cache/unicode-match-property-value-ecmascript-npm-2.0.0-b52f4f7ca4-8fe6a09d90.zip b/.yarn/cache/unicode-match-property-value-ecmascript-npm-2.0.0-b52f4f7ca4-8fe6a09d90.zip new file mode 100644 index 00000000000..ca970a5f30b Binary files /dev/null and b/.yarn/cache/unicode-match-property-value-ecmascript-npm-2.0.0-b52f4f7ca4-8fe6a09d90.zip differ diff --git a/.yarn/cache/unicode-property-aliases-ecmascript-npm-2.0.0-1636cb7768-dda4d39128.zip b/.yarn/cache/unicode-property-aliases-ecmascript-npm-2.0.0-1636cb7768-dda4d39128.zip new file mode 100644 index 00000000000..cdaff76639d Binary files /dev/null and b/.yarn/cache/unicode-property-aliases-ecmascript-npm-2.0.0-1636cb7768-dda4d39128.zip differ diff --git a/.yarn/cache/unique-filename-npm-1.1.1-c885c5095b-cf4998c922.zip b/.yarn/cache/unique-filename-npm-1.1.1-c885c5095b-cf4998c922.zip new file mode 100644 index 00000000000..3187d5eba7e Binary files /dev/null and b/.yarn/cache/unique-filename-npm-1.1.1-c885c5095b-cf4998c922.zip differ diff --git a/.yarn/cache/unique-slug-npm-2.0.2-f6ba1ddeb7-5b6876a645.zip b/.yarn/cache/unique-slug-npm-2.0.2-f6ba1ddeb7-5b6876a645.zip new file mode 100644 index 00000000000..060fb64cf94 Binary files /dev/null and b/.yarn/cache/unique-slug-npm-2.0.2-f6ba1ddeb7-5b6876a645.zip differ diff --git a/.yarn/cache/unique-string-npm-2.0.0-3153c97e47-ef68f63913.zip b/.yarn/cache/unique-string-npm-2.0.0-3153c97e47-ef68f63913.zip new file mode 100644 index 00000000000..50776c31723 Binary files /dev/null and b/.yarn/cache/unique-string-npm-2.0.0-3153c97e47-ef68f63913.zip differ diff --git a/.yarn/cache/universal-user-agent-npm-6.0.0-b148fb997a-5092bbc80d.zip b/.yarn/cache/universal-user-agent-npm-6.0.0-b148fb997a-5092bbc80d.zip new file mode 100644 index 00000000000..8a41a76f877 Binary files /dev/null and b/.yarn/cache/universal-user-agent-npm-6.0.0-b148fb997a-5092bbc80d.zip differ diff --git a/.yarn/cache/universalify-npm-0.1.2-9b22d31d2d-40cdc60f6e.zip b/.yarn/cache/universalify-npm-0.1.2-9b22d31d2d-40cdc60f6e.zip new file mode 100644 index 00000000000..b49f2fc0bd4 Binary files /dev/null and b/.yarn/cache/universalify-npm-0.1.2-9b22d31d2d-40cdc60f6e.zip differ diff --git a/.yarn/cache/universalify-npm-2.0.0-03b8b418a8-2406a4edf4.zip b/.yarn/cache/universalify-npm-2.0.0-03b8b418a8-2406a4edf4.zip new file mode 100644 index 00000000000..fa6b36b077a Binary files /dev/null and b/.yarn/cache/universalify-npm-2.0.0-03b8b418a8-2406a4edf4.zip differ diff --git a/.yarn/cache/unorm-npm-1.6.0-43467eccf1-9a86546256.zip b/.yarn/cache/unorm-npm-1.6.0-43467eccf1-9a86546256.zip new file mode 100644 index 00000000000..cb039278e0c Binary files /dev/null and b/.yarn/cache/unorm-npm-1.6.0-43467eccf1-9a86546256.zip differ diff --git a/.yarn/cache/unpipe-npm-1.0.0-2ed2a3c2bf-4fa18d8d8d.zip b/.yarn/cache/unpipe-npm-1.0.0-2ed2a3c2bf-4fa18d8d8d.zip new file mode 100644 index 00000000000..380809cf655 Binary files /dev/null and b/.yarn/cache/unpipe-npm-1.0.0-2ed2a3c2bf-4fa18d8d8d.zip differ diff --git a/.yarn/cache/untildify-npm-4.0.0-4a8b569825-39ced9c418.zip b/.yarn/cache/untildify-npm-4.0.0-4a8b569825-39ced9c418.zip new file mode 100644 index 00000000000..a88f9ac1d5c Binary files /dev/null and b/.yarn/cache/untildify-npm-4.0.0-4a8b569825-39ced9c418.zip differ diff --git a/.yarn/cache/update-notifier-npm-5.1.0-6bf595ecee-461e5e5b00.zip b/.yarn/cache/update-notifier-npm-5.1.0-6bf595ecee-461e5e5b00.zip new file mode 100644 index 00000000000..385b3119f6b Binary files /dev/null and b/.yarn/cache/update-notifier-npm-5.1.0-6bf595ecee-461e5e5b00.zip differ diff --git a/.yarn/cache/uri-js-npm-4.4.1-66d11cbcaf-7167432de6.zip b/.yarn/cache/uri-js-npm-4.4.1-66d11cbcaf-7167432de6.zip new file mode 100644 index 00000000000..bd21deb73d1 Binary files /dev/null and b/.yarn/cache/uri-js-npm-4.4.1-66d11cbcaf-7167432de6.zip differ diff --git a/.yarn/cache/url-npm-0.10.3-37c0b27c3c-7b83ddb106.zip b/.yarn/cache/url-npm-0.10.3-37c0b27c3c-7b83ddb106.zip new file mode 100644 index 00000000000..e3fc763641a Binary files /dev/null and b/.yarn/cache/url-npm-0.10.3-37c0b27c3c-7b83ddb106.zip differ diff --git a/.yarn/cache/url-npm-0.11.0-32ce15acfb-50d100d3dd.zip b/.yarn/cache/url-npm-0.11.0-32ce15acfb-50d100d3dd.zip new file mode 100644 index 00000000000..30c964bfeef Binary files /dev/null and b/.yarn/cache/url-npm-0.11.0-32ce15acfb-50d100d3dd.zip differ diff --git a/.yarn/cache/url-parse-lax-npm-3.0.0-92aa8effa0-1040e35775.zip b/.yarn/cache/url-parse-lax-npm-3.0.0-92aa8effa0-1040e35775.zip new file mode 100644 index 00000000000..b267d703440 Binary files /dev/null and b/.yarn/cache/url-parse-lax-npm-3.0.0-92aa8effa0-1040e35775.zip differ diff --git a/.yarn/cache/utf-8-validate-npm-5.0.9-ed88df348e-90117f1b65.zip b/.yarn/cache/utf-8-validate-npm-5.0.9-ed88df348e-90117f1b65.zip new file mode 100644 index 00000000000..da790d70297 Binary files /dev/null and b/.yarn/cache/utf-8-validate-npm-5.0.9-ed88df348e-90117f1b65.zip differ diff --git a/.yarn/cache/utf8-npm-2.1.2-17bfd49a94-de5d18adb2.zip b/.yarn/cache/utf8-npm-2.1.2-17bfd49a94-de5d18adb2.zip new file mode 100644 index 00000000000..520ba555f3e Binary files /dev/null and b/.yarn/cache/utf8-npm-2.1.2-17bfd49a94-de5d18adb2.zip differ diff --git a/.yarn/cache/util-deprecate-npm-1.0.2-e3fe1a219c-474acf1146.zip b/.yarn/cache/util-deprecate-npm-1.0.2-e3fe1a219c-474acf1146.zip new file mode 100644 index 00000000000..c2309cfe4d1 Binary files /dev/null and b/.yarn/cache/util-deprecate-npm-1.0.2-e3fe1a219c-474acf1146.zip differ diff --git a/.yarn/cache/util-npm-0.10.3-f43de5ccbb-bd800f5d23.zip b/.yarn/cache/util-npm-0.10.3-f43de5ccbb-bd800f5d23.zip new file mode 100644 index 00000000000..cd1ac2f8b65 Binary files /dev/null and b/.yarn/cache/util-npm-0.10.3-f43de5ccbb-bd800f5d23.zip differ diff --git a/.yarn/cache/util-npm-0.10.4-7c577db41a-913f9a90d0.zip b/.yarn/cache/util-npm-0.10.4-7c577db41a-913f9a90d0.zip new file mode 100644 index 00000000000..4bf853ed908 Binary files /dev/null and b/.yarn/cache/util-npm-0.10.4-7c577db41a-913f9a90d0.zip differ diff --git a/.yarn/cache/util-npm-0.12.4-a022701e3b-8eac7a6e6b.zip b/.yarn/cache/util-npm-0.12.4-a022701e3b-8eac7a6e6b.zip new file mode 100644 index 00000000000..d60211d6089 Binary files /dev/null and b/.yarn/cache/util-npm-0.12.4-a022701e3b-8eac7a6e6b.zip differ diff --git a/.yarn/cache/utils-merge-npm-1.0.1-363bbdfbca-c810954932.zip b/.yarn/cache/utils-merge-npm-1.0.1-363bbdfbca-c810954932.zip new file mode 100644 index 00000000000..8164f057254 Binary files /dev/null and b/.yarn/cache/utils-merge-npm-1.0.1-363bbdfbca-c810954932.zip differ diff --git a/.yarn/cache/uuid-npm-3.3.2-62715051ac-8793629d27.zip b/.yarn/cache/uuid-npm-3.3.2-62715051ac-8793629d27.zip new file mode 100644 index 00000000000..65253ceeafd Binary files /dev/null and b/.yarn/cache/uuid-npm-3.3.2-62715051ac-8793629d27.zip differ diff --git a/.yarn/cache/uuid-npm-3.4.0-4fd8ef88ad-58de2feed6.zip b/.yarn/cache/uuid-npm-3.4.0-4fd8ef88ad-58de2feed6.zip new file mode 100644 index 00000000000..86d48ead38f Binary files /dev/null and b/.yarn/cache/uuid-npm-3.4.0-4fd8ef88ad-58de2feed6.zip differ diff --git a/.yarn/cache/v8-compile-cache-npm-2.3.0-961375f150-adb0a271ea.zip b/.yarn/cache/v8-compile-cache-npm-2.3.0-961375f150-adb0a271ea.zip new file mode 100644 index 00000000000..0e04423cd8d Binary files /dev/null and b/.yarn/cache/v8-compile-cache-npm-2.3.0-961375f150-adb0a271ea.zip differ diff --git a/.yarn/cache/validate-npm-package-license-npm-3.0.4-7af8adc7a8-35703ac889.zip b/.yarn/cache/validate-npm-package-license-npm-3.0.4-7af8adc7a8-35703ac889.zip new file mode 100644 index 00000000000..e47f64159a8 Binary files /dev/null and b/.yarn/cache/validate-npm-package-license-npm-3.0.4-7af8adc7a8-35703ac889.zip differ diff --git a/.yarn/cache/validate-npm-package-name-npm-3.0.0-e44c263962-ce4c68207a.zip b/.yarn/cache/validate-npm-package-name-npm-3.0.0-e44c263962-ce4c68207a.zip new file mode 100644 index 00000000000..28a83805d83 Binary files /dev/null and b/.yarn/cache/validate-npm-package-name-npm-3.0.0-e44c263962-ce4c68207a.zip differ diff --git a/.yarn/cache/validator-npm-13.7.0-624277e841-2b83283de1.zip b/.yarn/cache/validator-npm-13.7.0-624277e841-2b83283de1.zip new file mode 100644 index 00000000000..27c8c61d0e3 Binary files /dev/null and b/.yarn/cache/validator-npm-13.7.0-624277e841-2b83283de1.zip differ diff --git a/.yarn/cache/varint-npm-5.0.0-c2491b868a-527c65ad87.zip b/.yarn/cache/varint-npm-5.0.0-c2491b868a-527c65ad87.zip new file mode 100644 index 00000000000..5e951610617 Binary files /dev/null and b/.yarn/cache/varint-npm-5.0.0-c2491b868a-527c65ad87.zip differ diff --git a/.yarn/cache/varint-npm-5.0.2-fcb43e79c5-e1a66bf9a6.zip b/.yarn/cache/varint-npm-5.0.2-fcb43e79c5-e1a66bf9a6.zip new file mode 100644 index 00000000000..df72d8406ff Binary files /dev/null and b/.yarn/cache/varint-npm-5.0.2-fcb43e79c5-e1a66bf9a6.zip differ diff --git a/.yarn/cache/vary-npm-1.1.2-b49f70ae63-ae0123222c.zip b/.yarn/cache/vary-npm-1.1.2-b49f70ae63-ae0123222c.zip new file mode 100644 index 00000000000..6ef083146c5 Binary files /dev/null and b/.yarn/cache/vary-npm-1.1.2-b49f70ae63-ae0123222c.zip differ diff --git a/.yarn/cache/verror-npm-1.10.0-c3f839c579-c431df0bed.zip b/.yarn/cache/verror-npm-1.10.0-c3f839c579-c431df0bed.zip new file mode 100644 index 00000000000..e81972bdea8 Binary files /dev/null and b/.yarn/cache/verror-npm-1.10.0-c3f839c579-c431df0bed.zip differ diff --git a/.yarn/cache/vinyl-file-npm-3.0.0-4d55e6cd5d-e187a74d41.zip b/.yarn/cache/vinyl-file-npm-3.0.0-4d55e6cd5d-e187a74d41.zip new file mode 100644 index 00000000000..da36b22870d Binary files /dev/null and b/.yarn/cache/vinyl-file-npm-3.0.0-4d55e6cd5d-e187a74d41.zip differ diff --git a/.yarn/cache/vinyl-npm-2.2.1-6b14799ad3-1f663973f1.zip b/.yarn/cache/vinyl-npm-2.2.1-6b14799ad3-1f663973f1.zip new file mode 100644 index 00000000000..937d2e709fc Binary files /dev/null and b/.yarn/cache/vinyl-npm-2.2.1-6b14799ad3-1f663973f1.zip differ diff --git a/.yarn/cache/vm-browserify-npm-1.1.2-f96404b36f-10a1c50aab.zip b/.yarn/cache/vm-browserify-npm-1.1.2-f96404b36f-10a1c50aab.zip new file mode 100644 index 00000000000..8b6c71cea5b Binary files /dev/null and b/.yarn/cache/vm-browserify-npm-1.1.2-f96404b36f-10a1c50aab.zip differ diff --git a/.yarn/cache/void-elements-npm-2.0.1-85e6962130-700c07ba9c.zip b/.yarn/cache/void-elements-npm-2.0.1-85e6962130-700c07ba9c.zip new file mode 100644 index 00000000000..564afaaac60 Binary files /dev/null and b/.yarn/cache/void-elements-npm-2.0.1-85e6962130-700c07ba9c.zip differ diff --git a/.yarn/cache/walk-up-path-npm-1.0.0-54fda77042-b8019ac4fb.zip b/.yarn/cache/walk-up-path-npm-1.0.0-54fda77042-b8019ac4fb.zip new file mode 100644 index 00000000000..a5652f8700a Binary files /dev/null and b/.yarn/cache/walk-up-path-npm-1.0.0-54fda77042-b8019ac4fb.zip differ diff --git a/.yarn/cache/watchpack-npm-2.2.0-fca5986ad5-e275f48fae.zip b/.yarn/cache/watchpack-npm-2.2.0-fca5986ad5-e275f48fae.zip new file mode 100644 index 00000000000..ff163a79307 Binary files /dev/null and b/.yarn/cache/watchpack-npm-2.2.0-fca5986ad5-e275f48fae.zip differ diff --git a/.yarn/cache/wcwidth-npm-1.0.1-05fa596453-814e9d1ddc.zip b/.yarn/cache/wcwidth-npm-1.0.1-05fa596453-814e9d1ddc.zip new file mode 100644 index 00000000000..b18e4e3458c Binary files /dev/null and b/.yarn/cache/wcwidth-npm-1.0.1-05fa596453-814e9d1ddc.zip differ diff --git a/.yarn/cache/webidl-conversions-npm-3.0.1-60310f6a2b-c92a0a6ab9.zip b/.yarn/cache/webidl-conversions-npm-3.0.1-60310f6a2b-c92a0a6ab9.zip new file mode 100644 index 00000000000..96867a65b00 Binary files /dev/null and b/.yarn/cache/webidl-conversions-npm-3.0.1-60310f6a2b-c92a0a6ab9.zip differ diff --git a/.yarn/cache/webpack-cli-npm-4.9.1-1b8a5f360f-2aff0349c1.zip b/.yarn/cache/webpack-cli-npm-4.9.1-1b8a5f360f-2aff0349c1.zip new file mode 100644 index 00000000000..9a45657977f Binary files /dev/null and b/.yarn/cache/webpack-cli-npm-4.9.1-1b8a5f360f-2aff0349c1.zip differ diff --git a/.yarn/cache/webpack-merge-npm-4.2.2-f98139a8eb-ce58bc8ab5.zip b/.yarn/cache/webpack-merge-npm-4.2.2-f98139a8eb-ce58bc8ab5.zip new file mode 100644 index 00000000000..64e5ae86fcb Binary files /dev/null and b/.yarn/cache/webpack-merge-npm-4.2.2-f98139a8eb-ce58bc8ab5.zip differ diff --git a/.yarn/cache/webpack-merge-npm-5.8.0-e3c95fdc3c-88786ab910.zip b/.yarn/cache/webpack-merge-npm-5.8.0-e3c95fdc3c-88786ab910.zip new file mode 100644 index 00000000000..f26a4cd88e3 Binary files /dev/null and b/.yarn/cache/webpack-merge-npm-5.8.0-e3c95fdc3c-88786ab910.zip differ diff --git a/.yarn/cache/webpack-npm-5.64.1-77fbd9ac18-d2a1baddae.zip b/.yarn/cache/webpack-npm-5.64.1-77fbd9ac18-d2a1baddae.zip new file mode 100644 index 00000000000..74e0ff66a29 Binary files /dev/null and b/.yarn/cache/webpack-npm-5.64.1-77fbd9ac18-d2a1baddae.zip differ diff --git a/.yarn/cache/webpack-sources-npm-3.2.2-9b97404a4e-cc81f1f1bf.zip b/.yarn/cache/webpack-sources-npm-3.2.2-9b97404a4e-cc81f1f1bf.zip new file mode 100644 index 00000000000..2374cc61b7e Binary files /dev/null and b/.yarn/cache/webpack-sources-npm-3.2.2-9b97404a4e-cc81f1f1bf.zip differ diff --git a/.yarn/cache/whatwg-url-npm-5.0.0-374fb45e60-b8daed4ad3.zip b/.yarn/cache/whatwg-url-npm-5.0.0-374fb45e60-b8daed4ad3.zip new file mode 100644 index 00000000000..5deef333614 Binary files /dev/null and b/.yarn/cache/whatwg-url-npm-5.0.0-374fb45e60-b8daed4ad3.zip differ diff --git a/.yarn/cache/which-boxed-primitive-npm-1.0.2-e214f9ae5a-53ce774c73.zip b/.yarn/cache/which-boxed-primitive-npm-1.0.2-e214f9ae5a-53ce774c73.zip new file mode 100644 index 00000000000..fef0ce42437 Binary files /dev/null and b/.yarn/cache/which-boxed-primitive-npm-1.0.2-e214f9ae5a-53ce774c73.zip differ diff --git a/.yarn/cache/which-module-npm-2.0.0-daf3daa08d-809f7fd3df.zip b/.yarn/cache/which-module-npm-2.0.0-daf3daa08d-809f7fd3df.zip new file mode 100644 index 00000000000..5548e31dfb9 Binary files /dev/null and b/.yarn/cache/which-module-npm-2.0.0-daf3daa08d-809f7fd3df.zip differ diff --git a/.yarn/cache/which-npm-1.3.1-f0ebb8bdd8-f2e185c624.zip b/.yarn/cache/which-npm-1.3.1-f0ebb8bdd8-f2e185c624.zip new file mode 100644 index 00000000000..08d0d36d22d Binary files /dev/null and b/.yarn/cache/which-npm-1.3.1-f0ebb8bdd8-f2e185c624.zip differ diff --git a/.yarn/cache/which-npm-2.0.2-320ddf72f7-1a5c563d3c.zip b/.yarn/cache/which-npm-2.0.2-320ddf72f7-1a5c563d3c.zip new file mode 100644 index 00000000000..389ec5e25ec Binary files /dev/null and b/.yarn/cache/which-npm-2.0.2-320ddf72f7-1a5c563d3c.zip differ diff --git a/.yarn/cache/which-pm-npm-2.0.0-b9f68562bc-e556635eaf.zip b/.yarn/cache/which-pm-npm-2.0.0-b9f68562bc-e556635eaf.zip new file mode 100644 index 00000000000..50ef84e1ffa Binary files /dev/null and b/.yarn/cache/which-pm-npm-2.0.0-b9f68562bc-e556635eaf.zip differ diff --git a/.yarn/cache/which-typed-array-npm-1.1.7-7cf2d674e6-147837cf58.zip b/.yarn/cache/which-typed-array-npm-1.1.7-7cf2d674e6-147837cf58.zip new file mode 100644 index 00000000000..cc17650e934 Binary files /dev/null and b/.yarn/cache/which-typed-array-npm-1.1.7-7cf2d674e6-147837cf58.zip differ diff --git a/.yarn/cache/wide-align-npm-1.1.5-889d77e592-d5fc37cd56.zip b/.yarn/cache/wide-align-npm-1.1.5-889d77e592-d5fc37cd56.zip new file mode 100644 index 00000000000..4dc7fcc67a0 Binary files /dev/null and b/.yarn/cache/wide-align-npm-1.1.5-889d77e592-d5fc37cd56.zip differ diff --git a/.yarn/cache/widest-line-npm-3.1.0-717bf2680b-03db6c9d0a.zip b/.yarn/cache/widest-line-npm-3.1.0-717bf2680b-03db6c9d0a.zip new file mode 100644 index 00000000000..4b9315faf9d Binary files /dev/null and b/.yarn/cache/widest-line-npm-3.1.0-717bf2680b-03db6c9d0a.zip differ diff --git a/.yarn/cache/wildcard-npm-2.0.0-baedca033a-1f4fe4c03d.zip b/.yarn/cache/wildcard-npm-2.0.0-baedca033a-1f4fe4c03d.zip new file mode 100644 index 00000000000..b2f396e7817 Binary files /dev/null and b/.yarn/cache/wildcard-npm-2.0.0-baedca033a-1f4fe4c03d.zip differ diff --git a/.yarn/cache/winston-npm-3.3.3-3fa4527b42-89a0a8db4e.zip b/.yarn/cache/winston-npm-3.3.3-3fa4527b42-89a0a8db4e.zip new file mode 100644 index 00000000000..d3f28545b10 Binary files /dev/null and b/.yarn/cache/winston-npm-3.3.3-3fa4527b42-89a0a8db4e.zip differ diff --git a/.yarn/cache/winston-transport-npm-4.4.0-e1b3134c1e-953d78d152.zip b/.yarn/cache/winston-transport-npm-4.4.0-e1b3134c1e-953d78d152.zip new file mode 100644 index 00000000000..746de112fca Binary files /dev/null and b/.yarn/cache/winston-transport-npm-4.4.0-e1b3134c1e-953d78d152.zip differ diff --git a/.yarn/cache/word-wrap-npm-1.2.3-7fb15ab002-30b48f91fc.zip b/.yarn/cache/word-wrap-npm-1.2.3-7fb15ab002-30b48f91fc.zip new file mode 100644 index 00000000000..518977eb886 Binary files /dev/null and b/.yarn/cache/word-wrap-npm-1.2.3-7fb15ab002-30b48f91fc.zip differ diff --git a/.yarn/cache/wordwrap-npm-1.0.0-ae57a645e8-2a44b27881.zip b/.yarn/cache/wordwrap-npm-1.0.0-ae57a645e8-2a44b27881.zip new file mode 100644 index 00000000000..5463df0bfe1 Binary files /dev/null and b/.yarn/cache/wordwrap-npm-1.0.0-ae57a645e8-2a44b27881.zip differ diff --git a/.yarn/cache/workerpool-npm-6.1.5-61adb98c59-5defea1fd3.zip b/.yarn/cache/workerpool-npm-6.1.5-61adb98c59-5defea1fd3.zip new file mode 100644 index 00000000000..5d43a347613 Binary files /dev/null and b/.yarn/cache/workerpool-npm-6.1.5-61adb98c59-5defea1fd3.zip differ diff --git a/.yarn/cache/wrap-ansi-npm-2.1.0-1fd9d50973-2dacd4b363.zip b/.yarn/cache/wrap-ansi-npm-2.1.0-1fd9d50973-2dacd4b363.zip new file mode 100644 index 00000000000..da0cd5ebedd Binary files /dev/null and b/.yarn/cache/wrap-ansi-npm-2.1.0-1fd9d50973-2dacd4b363.zip differ diff --git a/.yarn/cache/wrap-ansi-npm-6.2.0-439a7246d8-6cd96a4101.zip b/.yarn/cache/wrap-ansi-npm-6.2.0-439a7246d8-6cd96a4101.zip new file mode 100644 index 00000000000..aa06055f0b7 Binary files /dev/null and b/.yarn/cache/wrap-ansi-npm-6.2.0-439a7246d8-6cd96a4101.zip differ diff --git a/.yarn/cache/wrap-ansi-npm-7.0.0-ad6e1a0554-a790b846fd.zip b/.yarn/cache/wrap-ansi-npm-7.0.0-ad6e1a0554-a790b846fd.zip new file mode 100644 index 00000000000..ab6ea6e871e Binary files /dev/null and b/.yarn/cache/wrap-ansi-npm-7.0.0-ad6e1a0554-a790b846fd.zip differ diff --git a/.yarn/cache/wrappy-npm-1.0.2-916de4d4b3-159da4805f.zip b/.yarn/cache/wrappy-npm-1.0.2-916de4d4b3-159da4805f.zip new file mode 100644 index 00000000000..6072a9f2e72 Binary files /dev/null and b/.yarn/cache/wrappy-npm-1.0.2-916de4d4b3-159da4805f.zip differ diff --git a/.yarn/cache/write-file-atomic-npm-3.0.3-d948a237da-c55b24617c.zip b/.yarn/cache/write-file-atomic-npm-3.0.3-d948a237da-c55b24617c.zip new file mode 100644 index 00000000000..3790688ede7 Binary files /dev/null and b/.yarn/cache/write-file-atomic-npm-3.0.3-d948a237da-c55b24617c.zip differ diff --git a/.yarn/cache/write-file-atomic-npm-4.0.1-96ec744721-8f78023253.zip b/.yarn/cache/write-file-atomic-npm-4.0.1-96ec744721-8f78023253.zip new file mode 100644 index 00000000000..b23a2f047d3 Binary files /dev/null and b/.yarn/cache/write-file-atomic-npm-4.0.1-96ec744721-8f78023253.zip differ diff --git a/.yarn/cache/write-json-file-npm-4.3.0-89a21c4468-33908c5919.zip b/.yarn/cache/write-json-file-npm-4.3.0-89a21c4468-33908c5919.zip new file mode 100644 index 00000000000..40028c87f10 Binary files /dev/null and b/.yarn/cache/write-json-file-npm-4.3.0-89a21c4468-33908c5919.zip differ diff --git a/.yarn/cache/ws-npm-7.5.5-8f4a2a84a8-bd2b437256.zip b/.yarn/cache/ws-npm-7.5.5-8f4a2a84a8-bd2b437256.zip new file mode 100644 index 00000000000..66c7c48b48e Binary files /dev/null and b/.yarn/cache/ws-npm-7.5.5-8f4a2a84a8-bd2b437256.zip differ diff --git a/.yarn/cache/ws-npm-8.2.3-03a35b8ad7-c869296ccb.zip b/.yarn/cache/ws-npm-8.2.3-03a35b8ad7-c869296ccb.zip new file mode 100644 index 00000000000..407549e6d71 Binary files /dev/null and b/.yarn/cache/ws-npm-8.2.3-03a35b8ad7-c869296ccb.zip differ diff --git a/.yarn/cache/xdg-basedir-npm-4.0.0-ed08d380e2-0073d5b59a.zip b/.yarn/cache/xdg-basedir-npm-4.0.0-ed08d380e2-0073d5b59a.zip new file mode 100644 index 00000000000..3bf6cb24221 Binary files /dev/null and b/.yarn/cache/xdg-basedir-npm-4.0.0-ed08d380e2-0073d5b59a.zip differ diff --git a/.yarn/cache/xml2js-npm-0.4.19-104b7b16eb-ca8b2fee43.zip b/.yarn/cache/xml2js-npm-0.4.19-104b7b16eb-ca8b2fee43.zip new file mode 100644 index 00000000000..0d04c18c2d7 Binary files /dev/null and b/.yarn/cache/xml2js-npm-0.4.19-104b7b16eb-ca8b2fee43.zip differ diff --git a/.yarn/cache/xmlbuilder-npm-9.0.7-44519dbccb-8193bb3238.zip b/.yarn/cache/xmlbuilder-npm-9.0.7-44519dbccb-8193bb3238.zip new file mode 100644 index 00000000000..2399092b6c5 Binary files /dev/null and b/.yarn/cache/xmlbuilder-npm-9.0.7-44519dbccb-8193bb3238.zip differ diff --git a/.yarn/cache/xtend-npm-4.0.2-7f2375736e-ac5dfa738b.zip b/.yarn/cache/xtend-npm-4.0.2-7f2375736e-ac5dfa738b.zip new file mode 100644 index 00000000000..1090c686302 Binary files /dev/null and b/.yarn/cache/xtend-npm-4.0.2-7f2375736e-ac5dfa738b.zip differ diff --git a/.yarn/cache/y18n-npm-4.0.3-ced95acdbc-014dfcd9b5.zip b/.yarn/cache/y18n-npm-4.0.3-ced95acdbc-014dfcd9b5.zip new file mode 100644 index 00000000000..5fab75d8d6d Binary files /dev/null and b/.yarn/cache/y18n-npm-4.0.3-ced95acdbc-014dfcd9b5.zip differ diff --git a/.yarn/cache/y18n-npm-5.0.8-5f3a0a7e62-54f0fb9562.zip b/.yarn/cache/y18n-npm-5.0.8-5f3a0a7e62-54f0fb9562.zip new file mode 100644 index 00000000000..bf39a466ce0 Binary files /dev/null and b/.yarn/cache/y18n-npm-5.0.8-5f3a0a7e62-54f0fb9562.zip differ diff --git a/.yarn/cache/yallist-npm-3.1.1-a568a556b4-48f7bb00dc.zip b/.yarn/cache/yallist-npm-3.1.1-a568a556b4-48f7bb00dc.zip new file mode 100644 index 00000000000..04dc748b873 Binary files /dev/null and b/.yarn/cache/yallist-npm-3.1.1-a568a556b4-48f7bb00dc.zip differ diff --git a/.yarn/cache/yallist-npm-4.0.0-b493d9e907-343617202a.zip b/.yarn/cache/yallist-npm-4.0.0-b493d9e907-343617202a.zip new file mode 100644 index 00000000000..f2d3306fed7 Binary files /dev/null and b/.yarn/cache/yallist-npm-4.0.0-b493d9e907-343617202a.zip differ diff --git a/.yarn/cache/yaml-npm-1.10.2-0e780aebdf-ce4ada136e.zip b/.yarn/cache/yaml-npm-1.10.2-0e780aebdf-ce4ada136e.zip new file mode 100644 index 00000000000..bb28507cd0f Binary files /dev/null and b/.yarn/cache/yaml-npm-1.10.2-0e780aebdf-ce4ada136e.zip differ diff --git a/.yarn/cache/yamljs-npm-0.3.0-b0b262e524-76b770d34c.zip b/.yarn/cache/yamljs-npm-0.3.0-b0b262e524-76b770d34c.zip new file mode 100644 index 00000000000..daeb8b7fa49 Binary files /dev/null and b/.yarn/cache/yamljs-npm-0.3.0-b0b262e524-76b770d34c.zip differ diff --git a/.yarn/cache/yargs-npm-15.4.1-ca1c444de1-40b974f508.zip b/.yarn/cache/yargs-npm-15.4.1-ca1c444de1-40b974f508.zip new file mode 100644 index 00000000000..592327647a7 Binary files /dev/null and b/.yarn/cache/yargs-npm-15.4.1-ca1c444de1-40b974f508.zip differ diff --git a/.yarn/cache/yargs-npm-16.2.0-547873d425-b14afbb51e.zip b/.yarn/cache/yargs-npm-16.2.0-547873d425-b14afbb51e.zip new file mode 100644 index 00000000000..d11c27d5104 Binary files /dev/null and b/.yarn/cache/yargs-npm-16.2.0-547873d425-b14afbb51e.zip differ diff --git a/.yarn/cache/yargs-parser-npm-18.1.3-0ba9c4f088-60e8c7d1b8.zip b/.yarn/cache/yargs-parser-npm-18.1.3-0ba9c4f088-60e8c7d1b8.zip new file mode 100644 index 00000000000..536423041d6 Binary files /dev/null and b/.yarn/cache/yargs-parser-npm-18.1.3-0ba9c4f088-60e8c7d1b8.zip differ diff --git a/.yarn/cache/yargs-parser-npm-20.2.4-1de20916a6-d251998a37.zip b/.yarn/cache/yargs-parser-npm-20.2.4-1de20916a6-d251998a37.zip new file mode 100644 index 00000000000..fe57a9c8d61 Binary files /dev/null and b/.yarn/cache/yargs-parser-npm-20.2.4-1de20916a6-d251998a37.zip differ diff --git a/.yarn/cache/yargs-parser-npm-20.2.9-a1d19e598d-8bb69015f2.zip b/.yarn/cache/yargs-parser-npm-20.2.9-a1d19e598d-8bb69015f2.zip new file mode 100644 index 00000000000..f230038cfc2 Binary files /dev/null and b/.yarn/cache/yargs-parser-npm-20.2.9-a1d19e598d-8bb69015f2.zip differ diff --git a/.yarn/cache/yargs-unparser-npm-2.0.0-930f3ff3f6-68f9a542c6.zip b/.yarn/cache/yargs-unparser-npm-2.0.0-930f3ff3f6-68f9a542c6.zip new file mode 100644 index 00000000000..2d106745e62 Binary files /dev/null and b/.yarn/cache/yargs-unparser-npm-2.0.0-930f3ff3f6-68f9a542c6.zip differ diff --git a/.yarn/cache/yeoman-environment-npm-3.9.1-6ff00ff453-60a19b9962.zip b/.yarn/cache/yeoman-environment-npm-3.9.1-6ff00ff453-60a19b9962.zip new file mode 100644 index 00000000000..ca83c8aef9e Binary files /dev/null and b/.yarn/cache/yeoman-environment-npm-3.9.1-6ff00ff453-60a19b9962.zip differ diff --git a/.yarn/cache/yeoman-generator-npm-5.6.1-a49b7654c4-ef036210b6.zip b/.yarn/cache/yeoman-generator-npm-5.6.1-a49b7654c4-ef036210b6.zip new file mode 100644 index 00000000000..84649c8d9c4 Binary files /dev/null and b/.yarn/cache/yeoman-generator-npm-5.6.1-a49b7654c4-ef036210b6.zip differ diff --git a/.yarn/cache/yn-npm-2.0.0-3ad11617c1-9d49527cb3.zip b/.yarn/cache/yn-npm-2.0.0-3ad11617c1-9d49527cb3.zip new file mode 100644 index 00000000000..86fd3519524 Binary files /dev/null and b/.yarn/cache/yn-npm-2.0.0-3ad11617c1-9d49527cb3.zip differ diff --git a/.yarn/cache/yn-npm-3.1.1-8ad4259784-2c487b0e14.zip b/.yarn/cache/yn-npm-3.1.1-8ad4259784-2c487b0e14.zip new file mode 100644 index 00000000000..4a3116218c3 Binary files /dev/null and b/.yarn/cache/yn-npm-3.1.1-8ad4259784-2c487b0e14.zip differ diff --git a/.yarn/cache/yocto-queue-npm-0.1.0-c6c9a7db29-f77b3d8d00.zip b/.yarn/cache/yocto-queue-npm-0.1.0-c6c9a7db29-f77b3d8d00.zip new file mode 100644 index 00000000000..f56730df001 Binary files /dev/null and b/.yarn/cache/yocto-queue-npm-0.1.0-c6c9a7db29-f77b3d8d00.zip differ diff --git a/.yarn/cache/yosay-npm-2.0.2-50f629c5fa-7e0220ef13.zip b/.yarn/cache/yosay-npm-2.0.2-50f629c5fa-7e0220ef13.zip new file mode 100644 index 00000000000..be33abd2670 Binary files /dev/null and b/.yarn/cache/yosay-npm-2.0.2-50f629c5fa-7e0220ef13.zip differ diff --git a/.yarn/cache/z-schema-npm-4.2.4-450fc6608e-9afc0b8d4f.zip b/.yarn/cache/z-schema-npm-4.2.4-450fc6608e-9afc0b8d4f.zip new file mode 100644 index 00000000000..0ee5ed1f347 Binary files /dev/null and b/.yarn/cache/z-schema-npm-4.2.4-450fc6608e-9afc0b8d4f.zip differ diff --git a/.yarn/cache/zeromq-npm-5.2.8-213a0f74bc-0fada0fe60.zip b/.yarn/cache/zeromq-npm-5.2.8-213a0f74bc-0fada0fe60.zip new file mode 100644 index 00000000000..4ee5d72406c Binary files /dev/null and b/.yarn/cache/zeromq-npm-5.2.8-213a0f74bc-0fada0fe60.zip differ diff --git a/.yarn/constraints.pro b/.yarn/constraints.pro new file mode 100644 index 00000000000..c8e7dcf2065 --- /dev/null +++ b/.yarn/constraints.pro @@ -0,0 +1,12 @@ +% Prevent two workspaces from depending on conflicting versions of a same dependency + +gen_enforced_dependency(WorkspaceCwd, DependencyIdent, DependencyRange2, DependencyType) :- + workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType), + workspace_has_dependency(OtherWorkspaceCwd, DependencyIdent, DependencyRange2, DependencyType2), + DependencyRange \= DependencyRange2. + +% Force all workspace dependencies to be made explicit + +gen_enforced_dependency(WorkspaceCwd, DependencyIdent, 'workspace:~', DependencyType) :- + workspace_ident(_, DependencyIdent), + workspace_has_dependency(WorkspaceCwd, DependencyIdent, _, DependencyType). diff --git a/.yarn/plugins/@yarnpkg/plugin-constraints.cjs b/.yarn/plugins/@yarnpkg/plugin-constraints.cjs new file mode 100644 index 00000000000..f3b0db0c024 --- /dev/null +++ b/.yarn/plugins/@yarnpkg/plugin-constraints.cjs @@ -0,0 +1,52 @@ +/* eslint-disable */ +//prettier-ignore +module.exports = { +name: "@yarnpkg/plugin-constraints", +factory: function (require) { +var plugin=(()=>{var Li=Object.create,Je=Object.defineProperty;var Hi=Object.getOwnPropertyDescriptor;var Gi=Object.getOwnPropertyNames;var Yi=Object.getPrototypeOf,Ui=Object.prototype.hasOwnProperty;var Zi=r=>Je(r,"__esModule",{value:!0});var I=(r,u)=>()=>(u||r((u={exports:{}}).exports,u),u.exports),Qi=(r,u)=>{for(var p in u)Je(r,p,{get:u[p],enumerable:!0})},Ji=(r,u,p)=>{if(u&&typeof u=="object"||typeof u=="function")for(let c of Gi(u))!Ui.call(r,c)&&c!=="default"&&Je(r,c,{get:()=>u[c],enumerable:!(p=Hi(u,c))||p.enumerable});return r},G=r=>Ji(Zi(Je(r!=null?Li(Yi(r)):{},"default",r&&r.__esModule&&"default"in r?{get:()=>r.default,enumerable:!0}:{value:r,enumerable:!0})),r);var Xr=I((Nu,_r)=>{var Ki;(function(r){var u=function(){return{"append/2":[new r.type.Rule(new r.type.Term("append",[new r.type.Var("X"),new r.type.Var("L")]),new r.type.Term("foldl",[new r.type.Term("append",[]),new r.type.Var("X"),new r.type.Term("[]",[]),new r.type.Var("L")]))],"append/3":[new r.type.Rule(new r.type.Term("append",[new r.type.Term("[]",[]),new r.type.Var("X"),new r.type.Var("X")]),null),new r.type.Rule(new r.type.Term("append",[new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("T")]),new r.type.Var("X"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("S")])]),new r.type.Term("append",[new r.type.Var("T"),new r.type.Var("X"),new r.type.Var("S")]))],"member/2":[new r.type.Rule(new r.type.Term("member",[new r.type.Var("X"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("_")])]),null),new r.type.Rule(new r.type.Term("member",[new r.type.Var("X"),new r.type.Term(".",[new r.type.Var("_"),new r.type.Var("Xs")])]),new r.type.Term("member",[new r.type.Var("X"),new r.type.Var("Xs")]))],"permutation/2":[new r.type.Rule(new r.type.Term("permutation",[new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("permutation",[new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("T")]),new r.type.Var("S")]),new r.type.Term(",",[new r.type.Term("permutation",[new r.type.Var("T"),new r.type.Var("P")]),new r.type.Term(",",[new r.type.Term("append",[new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("P")]),new r.type.Term("append",[new r.type.Var("X"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("Y")]),new r.type.Var("S")])])]))],"maplist/2":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("X")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("Xs")])]))],"maplist/3":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("A"),new r.type.Var("As")]),new r.type.Term(".",[new r.type.Var("B"),new r.type.Var("Bs")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("A"),new r.type.Var("B")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("As"),new r.type.Var("Bs")])]))],"maplist/4":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("A"),new r.type.Var("As")]),new r.type.Term(".",[new r.type.Var("B"),new r.type.Var("Bs")]),new r.type.Term(".",[new r.type.Var("C"),new r.type.Var("Cs")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("A"),new r.type.Var("B"),new r.type.Var("C")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("As"),new r.type.Var("Bs"),new r.type.Var("Cs")])]))],"maplist/5":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("A"),new r.type.Var("As")]),new r.type.Term(".",[new r.type.Var("B"),new r.type.Var("Bs")]),new r.type.Term(".",[new r.type.Var("C"),new r.type.Var("Cs")]),new r.type.Term(".",[new r.type.Var("D"),new r.type.Var("Ds")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("A"),new r.type.Var("B"),new r.type.Var("C"),new r.type.Var("D")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("As"),new r.type.Var("Bs"),new r.type.Var("Cs"),new r.type.Var("Ds")])]))],"maplist/6":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("A"),new r.type.Var("As")]),new r.type.Term(".",[new r.type.Var("B"),new r.type.Var("Bs")]),new r.type.Term(".",[new r.type.Var("C"),new r.type.Var("Cs")]),new r.type.Term(".",[new r.type.Var("D"),new r.type.Var("Ds")]),new r.type.Term(".",[new r.type.Var("E"),new r.type.Var("Es")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("A"),new r.type.Var("B"),new r.type.Var("C"),new r.type.Var("D"),new r.type.Var("E")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("As"),new r.type.Var("Bs"),new r.type.Var("Cs"),new r.type.Var("Ds"),new r.type.Var("Es")])]))],"maplist/7":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("A"),new r.type.Var("As")]),new r.type.Term(".",[new r.type.Var("B"),new r.type.Var("Bs")]),new r.type.Term(".",[new r.type.Var("C"),new r.type.Var("Cs")]),new r.type.Term(".",[new r.type.Var("D"),new r.type.Var("Ds")]),new r.type.Term(".",[new r.type.Var("E"),new r.type.Var("Es")]),new r.type.Term(".",[new r.type.Var("F"),new r.type.Var("Fs")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("A"),new r.type.Var("B"),new r.type.Var("C"),new r.type.Var("D"),new r.type.Var("E"),new r.type.Var("F")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("As"),new r.type.Var("Bs"),new r.type.Var("Cs"),new r.type.Var("Ds"),new r.type.Var("Es"),new r.type.Var("Fs")])]))],"maplist/8":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("A"),new r.type.Var("As")]),new r.type.Term(".",[new r.type.Var("B"),new r.type.Var("Bs")]),new r.type.Term(".",[new r.type.Var("C"),new r.type.Var("Cs")]),new r.type.Term(".",[new r.type.Var("D"),new r.type.Var("Ds")]),new r.type.Term(".",[new r.type.Var("E"),new r.type.Var("Es")]),new r.type.Term(".",[new r.type.Var("F"),new r.type.Var("Fs")]),new r.type.Term(".",[new r.type.Var("G"),new r.type.Var("Gs")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("A"),new r.type.Var("B"),new r.type.Var("C"),new r.type.Var("D"),new r.type.Var("E"),new r.type.Var("F"),new r.type.Var("G")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("As"),new r.type.Var("Bs"),new r.type.Var("Cs"),new r.type.Var("Ds"),new r.type.Var("Es"),new r.type.Var("Fs"),new r.type.Var("Gs")])]))],"include/3":[new r.type.Rule(new r.type.Term("include",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("include",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("T")]),new r.type.Var("L")]),new r.type.Term(",",[new r.type.Term("=..",[new r.type.Var("P"),new r.type.Var("A")]),new r.type.Term(",",[new r.type.Term("append",[new r.type.Var("A"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Term("[]",[])]),new r.type.Var("B")]),new r.type.Term(",",[new r.type.Term("=..",[new r.type.Var("F"),new r.type.Var("B")]),new r.type.Term(",",[new r.type.Term(";",[new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("F")]),new r.type.Term(",",[new r.type.Term("=",[new r.type.Var("L"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("S")])]),new r.type.Term("!",[])])]),new r.type.Term("=",[new r.type.Var("L"),new r.type.Var("S")])]),new r.type.Term("include",[new r.type.Var("P"),new r.type.Var("T"),new r.type.Var("S")])])])])]))],"exclude/3":[new r.type.Rule(new r.type.Term("exclude",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("exclude",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("T")]),new r.type.Var("S")]),new r.type.Term(",",[new r.type.Term("exclude",[new r.type.Var("P"),new r.type.Var("T"),new r.type.Var("E")]),new r.type.Term(",",[new r.type.Term("=..",[new r.type.Var("P"),new r.type.Var("L")]),new r.type.Term(",",[new r.type.Term("append",[new r.type.Var("L"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Term("[]",[])]),new r.type.Var("Q")]),new r.type.Term(",",[new r.type.Term("=..",[new r.type.Var("R"),new r.type.Var("Q")]),new r.type.Term(";",[new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("R")]),new r.type.Term(",",[new r.type.Term("!",[]),new r.type.Term("=",[new r.type.Var("S"),new r.type.Var("E")])])]),new r.type.Term("=",[new r.type.Var("S"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("E")])])])])])])]))],"foldl/4":[new r.type.Rule(new r.type.Term("foldl",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Var("I"),new r.type.Var("I")]),null),new r.type.Rule(new r.type.Term("foldl",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("T")]),new r.type.Var("I"),new r.type.Var("R")]),new r.type.Term(",",[new r.type.Term("=..",[new r.type.Var("P"),new r.type.Var("L")]),new r.type.Term(",",[new r.type.Term("append",[new r.type.Var("L"),new r.type.Term(".",[new r.type.Var("I"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Term("[]",[])])])]),new r.type.Var("L2")]),new r.type.Term(",",[new r.type.Term("=..",[new r.type.Var("P2"),new r.type.Var("L2")]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P2")]),new r.type.Term("foldl",[new r.type.Var("P"),new r.type.Var("T"),new r.type.Var("X"),new r.type.Var("R")])])])])]))],"select/3":[new r.type.Rule(new r.type.Term("select",[new r.type.Var("E"),new r.type.Term(".",[new r.type.Var("E"),new r.type.Var("Xs")]),new r.type.Var("Xs")]),null),new r.type.Rule(new r.type.Term("select",[new r.type.Var("E"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Ys")])]),new r.type.Term("select",[new r.type.Var("E"),new r.type.Var("Xs"),new r.type.Var("Ys")]))],"sum_list/2":[new r.type.Rule(new r.type.Term("sum_list",[new r.type.Term("[]",[]),new r.type.Num(0,!1)]),null),new r.type.Rule(new r.type.Term("sum_list",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Var("S")]),new r.type.Term(",",[new r.type.Term("sum_list",[new r.type.Var("Xs"),new r.type.Var("Y")]),new r.type.Term("is",[new r.type.Var("S"),new r.type.Term("+",[new r.type.Var("X"),new r.type.Var("Y")])])]))],"max_list/2":[new r.type.Rule(new r.type.Term("max_list",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Term("[]",[])]),new r.type.Var("X")]),null),new r.type.Rule(new r.type.Term("max_list",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Var("S")]),new r.type.Term(",",[new r.type.Term("max_list",[new r.type.Var("Xs"),new r.type.Var("Y")]),new r.type.Term(";",[new r.type.Term(",",[new r.type.Term(">=",[new r.type.Var("X"),new r.type.Var("Y")]),new r.type.Term(",",[new r.type.Term("=",[new r.type.Var("S"),new r.type.Var("X")]),new r.type.Term("!",[])])]),new r.type.Term("=",[new r.type.Var("S"),new r.type.Var("Y")])])]))],"min_list/2":[new r.type.Rule(new r.type.Term("min_list",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Term("[]",[])]),new r.type.Var("X")]),null),new r.type.Rule(new r.type.Term("min_list",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Var("S")]),new r.type.Term(",",[new r.type.Term("min_list",[new r.type.Var("Xs"),new r.type.Var("Y")]),new r.type.Term(";",[new r.type.Term(",",[new r.type.Term("=<",[new r.type.Var("X"),new r.type.Var("Y")]),new r.type.Term(",",[new r.type.Term("=",[new r.type.Var("S"),new r.type.Var("X")]),new r.type.Term("!",[])])]),new r.type.Term("=",[new r.type.Var("S"),new r.type.Var("Y")])])]))],"prod_list/2":[new r.type.Rule(new r.type.Term("prod_list",[new r.type.Term("[]",[]),new r.type.Num(1,!1)]),null),new r.type.Rule(new r.type.Term("prod_list",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Var("S")]),new r.type.Term(",",[new r.type.Term("prod_list",[new r.type.Var("Xs"),new r.type.Var("Y")]),new r.type.Term("is",[new r.type.Var("S"),new r.type.Term("*",[new r.type.Var("X"),new r.type.Var("Y")])])]))],"last/2":[new r.type.Rule(new r.type.Term("last",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Term("[]",[])]),new r.type.Var("X")]),null),new r.type.Rule(new r.type.Term("last",[new r.type.Term(".",[new r.type.Var("_"),new r.type.Var("Xs")]),new r.type.Var("X")]),new r.type.Term("last",[new r.type.Var("Xs"),new r.type.Var("X")]))],"prefix/2":[new r.type.Rule(new r.type.Term("prefix",[new r.type.Var("Part"),new r.type.Var("Whole")]),new r.type.Term("append",[new r.type.Var("Part"),new r.type.Var("_"),new r.type.Var("Whole")]))],"nth0/3":[new r.type.Rule(new r.type.Term("nth0",[new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z")]),new r.type.Term(";",[new r.type.Term("->",[new r.type.Term("var",[new r.type.Var("X")]),new r.type.Term("nth",[new r.type.Num(0,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("_")])]),new r.type.Term(",",[new r.type.Term(">=",[new r.type.Var("X"),new r.type.Num(0,!1)]),new r.type.Term(",",[new r.type.Term("nth",[new r.type.Num(0,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("_")]),new r.type.Term("!",[])])])]))],"nth1/3":[new r.type.Rule(new r.type.Term("nth1",[new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z")]),new r.type.Term(";",[new r.type.Term("->",[new r.type.Term("var",[new r.type.Var("X")]),new r.type.Term("nth",[new r.type.Num(1,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("_")])]),new r.type.Term(",",[new r.type.Term(">",[new r.type.Var("X"),new r.type.Num(0,!1)]),new r.type.Term(",",[new r.type.Term("nth",[new r.type.Num(1,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("_")]),new r.type.Term("!",[])])])]))],"nth0/4":[new r.type.Rule(new r.type.Term("nth0",[new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("W")]),new r.type.Term(";",[new r.type.Term("->",[new r.type.Term("var",[new r.type.Var("X")]),new r.type.Term("nth",[new r.type.Num(0,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("W")])]),new r.type.Term(",",[new r.type.Term(">=",[new r.type.Var("X"),new r.type.Num(0,!1)]),new r.type.Term(",",[new r.type.Term("nth",[new r.type.Num(0,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("W")]),new r.type.Term("!",[])])])]))],"nth1/4":[new r.type.Rule(new r.type.Term("nth1",[new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("W")]),new r.type.Term(";",[new r.type.Term("->",[new r.type.Term("var",[new r.type.Var("X")]),new r.type.Term("nth",[new r.type.Num(1,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("W")])]),new r.type.Term(",",[new r.type.Term(">",[new r.type.Var("X"),new r.type.Num(0,!1)]),new r.type.Term(",",[new r.type.Term("nth",[new r.type.Num(1,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("W")]),new r.type.Term("!",[])])])]))],"nth/5":[new r.type.Rule(new r.type.Term("nth",[new r.type.Var("N"),new r.type.Var("N"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Var("X"),new r.type.Var("Xs")]),null),new r.type.Rule(new r.type.Term("nth",[new r.type.Var("N"),new r.type.Var("O"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Var("Y"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Ys")])]),new r.type.Term(",",[new r.type.Term("is",[new r.type.Var("M"),new r.type.Term("+",[new r.type.Var("N"),new r.type.Num(1,!1)])]),new r.type.Term("nth",[new r.type.Var("M"),new r.type.Var("O"),new r.type.Var("Xs"),new r.type.Var("Y"),new r.type.Var("Ys")])]))],"length/2":function(c,w,_){var v=_.args[0],g=_.args[1];if(!r.type.is_variable(g)&&!r.type.is_integer(g))c.throw_error(r.error.type("integer",g,_.indicator));else if(r.type.is_integer(g)&&g.value<0)c.throw_error(r.error.domain("not_less_than_zero",g,_.indicator));else{var h=new r.type.Term("length",[v,new r.type.Num(0,!1),g]);r.type.is_integer(g)&&(h=new r.type.Term(",",[h,new r.type.Term("!",[])])),c.prepend([new r.type.State(w.goal.replace(h),w.substitution,w)])}},"length/3":[new r.type.Rule(new r.type.Term("length",[new r.type.Term("[]",[]),new r.type.Var("N"),new r.type.Var("N")]),null),new r.type.Rule(new r.type.Term("length",[new r.type.Term(".",[new r.type.Var("_"),new r.type.Var("X")]),new r.type.Var("A"),new r.type.Var("N")]),new r.type.Term(",",[new r.type.Term("succ",[new r.type.Var("A"),new r.type.Var("B")]),new r.type.Term("length",[new r.type.Var("X"),new r.type.Var("B"),new r.type.Var("N")])]))],"replicate/3":function(c,w,_){var v=_.args[0],g=_.args[1],h=_.args[2];if(r.type.is_variable(g))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_integer(g))c.throw_error(r.error.type("integer",g,_.indicator));else if(g.value<0)c.throw_error(r.error.domain("not_less_than_zero",g,_.indicator));else if(!r.type.is_variable(h)&&!r.type.is_list(h))c.throw_error(r.error.type("list",h,_.indicator));else{for(var x=new r.type.Term("[]"),T=0;T0;b--)T[b].equals(T[b-1])&&T.splice(b,1);for(var C=new r.type.Term("[]"),b=T.length-1;b>=0;b--)C=new r.type.Term(".",[T[b],C]);c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[C,g])),w.substitution,w)])}}},"msort/2":function(c,w,_){var v=_.args[0],g=_.args[1];if(r.type.is_variable(v))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_variable(g)&&!r.type.is_fully_list(g))c.throw_error(r.error.type("list",g,_.indicator));else{for(var h=[],x=v;x.indicator==="./2";)h.push(x.args[0]),x=x.args[1];if(r.type.is_variable(x))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_empty_list(x))c.throw_error(r.error.type("list",v,_.indicator));else{for(var T=h.sort(r.compare),b=new r.type.Term("[]"),C=T.length-1;C>=0;C--)b=new r.type.Term(".",[T[C],b]);c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[b,g])),w.substitution,w)])}}},"keysort/2":function(c,w,_){var v=_.args[0],g=_.args[1];if(r.type.is_variable(v))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_variable(g)&&!r.type.is_fully_list(g))c.throw_error(r.error.type("list",g,_.indicator));else{for(var h=[],x,T=v;T.indicator==="./2";){if(x=T.args[0],r.type.is_variable(x)){c.throw_error(r.error.instantiation(_.indicator));return}else if(!r.type.is_term(x)||x.indicator!=="-/2"){c.throw_error(r.error.type("pair",x,_.indicator));return}x.args[0].pair=x.args[1],h.push(x.args[0]),T=T.args[1]}if(r.type.is_variable(T))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_empty_list(T))c.throw_error(r.error.type("list",v,_.indicator));else{for(var b=h.sort(r.compare),C=new r.type.Term("[]"),N=b.length-1;N>=0;N--)C=new r.type.Term(".",[new r.type.Term("-",[b[N],b[N].pair]),C]),delete b[N].pair;c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[C,g])),w.substitution,w)])}}},"take/3":function(c,w,_){var v=_.args[0],g=_.args[1],h=_.args[2];if(r.type.is_variable(g)||r.type.is_variable(v))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_list(g))c.throw_error(r.error.type("list",g,_.indicator));else if(!r.type.is_integer(v))c.throw_error(r.error.type("integer",v,_.indicator));else if(!r.type.is_variable(h)&&!r.type.is_list(h))c.throw_error(r.error.type("list",h,_.indicator));else{for(var x=v.value,T=[],b=g;x>0&&b.indicator==="./2";)T.push(b.args[0]),b=b.args[1],x--;if(x===0){for(var C=new r.type.Term("[]"),x=T.length-1;x>=0;x--)C=new r.type.Term(".",[T[x],C]);c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[C,h])),w.substitution,w)])}}},"drop/3":function(c,w,_){var v=_.args[0],g=_.args[1],h=_.args[2];if(r.type.is_variable(g)||r.type.is_variable(v))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_list(g))c.throw_error(r.error.type("list",g,_.indicator));else if(!r.type.is_integer(v))c.throw_error(r.error.type("integer",v,_.indicator));else if(!r.type.is_variable(h)&&!r.type.is_list(h))c.throw_error(r.error.type("list",h,_.indicator));else{for(var x=v.value,T=[],b=g;x>0&&b.indicator==="./2";)T.push(b.args[0]),b=b.args[1],x--;x===0&&c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[b,h])),w.substitution,w)])}},"reverse/2":function(c,w,_){var v=_.args[0],g=_.args[1],h=r.type.is_instantiated_list(v),x=r.type.is_instantiated_list(g);if(r.type.is_variable(v)&&r.type.is_variable(g))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_variable(v)&&!r.type.is_fully_list(v))c.throw_error(r.error.type("list",v,_.indicator));else if(!r.type.is_variable(g)&&!r.type.is_fully_list(g))c.throw_error(r.error.type("list",g,_.indicator));else if(!h&&!x)c.throw_error(r.error.instantiation(_.indicator));else{for(var T=h?v:g,b=new r.type.Term("[]",[]);T.indicator==="./2";)b=new r.type.Term(".",[T.args[0],b]),T=T.args[1];c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[b,h?g:v])),w.substitution,w)])}},"list_to_set/2":function(c,w,_){var v=_.args[0],g=_.args[1];if(r.type.is_variable(v))c.throw_error(r.error.instantiation(_.indicator));else{for(var h=v,x=[];h.indicator==="./2";)x.push(h.args[0]),h=h.args[1];if(r.type.is_variable(h))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_term(h)||h.indicator!=="[]/0")c.throw_error(r.error.type("list",v,_.indicator));else{for(var T=[],b=new r.type.Term("[]",[]),C,N=0;N=0;N--)b=new r.type.Term(".",[T[N],b]);c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[g,b])),w.substitution,w)])}}}}},p=["append/2","append/3","member/2","permutation/2","maplist/2","maplist/3","maplist/4","maplist/5","maplist/6","maplist/7","maplist/8","include/3","exclude/3","foldl/4","sum_list/2","max_list/2","min_list/2","prod_list/2","last/2","prefix/2","nth0/3","nth1/3","nth0/4","nth1/4","length/2","replicate/3","select/3","sort/2","msort/2","keysort/2","take/3","drop/3","reverse/2","list_to_set/2"];typeof _r!="undefined"?_r.exports=function(c){r=c,new r.type.Module("lists",u(),p)}:new r.type.Module("lists",u(),p)})(Ki)});var et=I(M=>{"use strict";var Ve=process.platform==="win32",wr="aes-256-cbc",ji="sha256",Br="The current environment doesn't support interactive reading from TTY.",z=require("fs"),Fr=process.binding("tty_wrap").TTY,gr=require("child_process"),_e=require("path"),dr={prompt:"> ",hideEchoBack:!1,mask:"*",limit:[],limitMessage:"Input another, please.$<( [)limit(])>",defaultInput:"",trueValue:[],falseValue:[],caseSensitive:!1,keepWhitespace:!1,encoding:"utf8",bufferSize:1024,print:void 0,history:!0,cd:!1,phContent:void 0,preCheck:void 0},fe="none",oe,Ce,zr=!1,we,Ke,vr,es=0,hr="",Se=[],je,Wr=!1,mr=!1,$e=!1;function Lr(r){function u(p){return p.replace(/[^\w\u0080-\uFFFF]/g,function(c){return"#"+c.charCodeAt(0)+";"})}return Ke.concat(function(p){var c=[];return Object.keys(p).forEach(function(w){p[w]==="boolean"?r[w]&&c.push("--"+w):p[w]==="string"&&r[w]&&c.push("--"+w,u(r[w]))}),c}({display:"string",displayOnly:"boolean",keyIn:"boolean",hideEchoBack:"boolean",mask:"string",limit:"string",caseSensitive:"boolean"}))}function rs(r,u){function p(j){var U,Ue="",Ze;for(vr=vr||require("os").tmpdir();;){U=_e.join(vr,j+Ue);try{Ze=z.openSync(U,"wx")}catch(Qe){if(Qe.code==="EEXIST"){Ue++;continue}else throw Qe}z.closeSync(Ze);break}return U}var c,w,_,v={},g,h,x=p("readline-sync.stdout"),T=p("readline-sync.stderr"),b=p("readline-sync.exit"),C=p("readline-sync.done"),N=require("crypto"),W,ee,te;W=N.createHash(ji),W.update(""+process.pid+es+++Math.random()),te=W.digest("hex"),ee=N.createDecipher(wr,te),c=Lr(r),Ve?(w=process.env.ComSpec||"cmd.exe",process.env.Q='"',_=["/V:ON","/S","/C","(%Q%"+w+"%Q% /V:ON /S /C %Q%%Q%"+we+"%Q%"+c.map(function(j){return" %Q%"+j+"%Q%"}).join("")+" & (echo !ERRORLEVEL!)>%Q%"+b+"%Q%%Q%) 2>%Q%"+T+"%Q% |%Q%"+process.execPath+"%Q% %Q%"+__dirname+"\\encrypt.js%Q% %Q%"+wr+"%Q% %Q%"+te+"%Q% >%Q%"+x+"%Q% & (echo 1)>%Q%"+C+"%Q%"]):(w="/bin/sh",_=["-c",'("'+we+'"'+c.map(function(j){return" '"+j.replace(/'/g,"'\\''")+"'"}).join("")+'; echo $?>"'+b+'") 2>"'+T+'" |"'+process.execPath+'" "'+__dirname+'/encrypt.js" "'+wr+'" "'+te+'" >"'+x+'"; echo 1 >"'+C+'"']),$e&&$e("_execFileSync",c);try{gr.spawn(w,_,u)}catch(j){v.error=new Error(j.message),v.error.method="_execFileSync - spawn",v.error.program=w,v.error.args=_}for(;z.readFileSync(C,{encoding:r.encoding}).trim()!=="1";);return(g=z.readFileSync(b,{encoding:r.encoding}).trim())==="0"?v.input=ee.update(z.readFileSync(x,{encoding:"binary"}),"hex",r.encoding)+ee.final(r.encoding):(h=z.readFileSync(T,{encoding:r.encoding}).trim(),v.error=new Error(Br+(h?` +`+h:"")),v.error.method="_execFileSync",v.error.program=w,v.error.args=_,v.error.extMessage=h,v.error.exitCode=+g),z.unlinkSync(x),z.unlinkSync(T),z.unlinkSync(b),z.unlinkSync(C),v}function ts(r){var u,p={},c,w={env:process.env,encoding:r.encoding};if(we||(Ve?process.env.PSModulePath?(we="powershell.exe",Ke=["-ExecutionPolicy","Bypass","-File",__dirname+"\\read.ps1"]):(we="cscript.exe",Ke=["//nologo",__dirname+"\\read.cs.js"]):(we="/bin/sh",Ke=[__dirname+"/read.sh"])),Ve&&!process.env.PSModulePath&&(w.stdio=[process.stdin]),gr.execFileSync){u=Lr(r),$e&&$e("execFileSync",u);try{p.input=gr.execFileSync(we,u,w)}catch(_){c=_.stderr?(_.stderr+"").trim():"",p.error=new Error(Br+(c?` +`+c:"")),p.error.method="execFileSync",p.error.program=we,p.error.args=u,p.error.extMessage=c,p.error.exitCode=_.status,p.error.code=_.code,p.error.signal=_.signal}}else p=rs(r,w);return p.error||(p.input=p.input.replace(/^\s*'|'\s*$/g,""),r.display=""),p}function br(r){var u="",p=r.display,c=!r.display&&r.keyIn&&r.hideEchoBack&&!r.mask;function w(){var _=ts(r);if(_.error)throw _.error;return _.input}return mr&&mr(r),function(){var _,v,g;function h(){return _||(_=process.binding("fs"),v=process.binding("constants")),_}if(typeof fe=="string")if(fe=null,Ve){if(g=function(x){var T=x.replace(/^\D+/,"").split("."),b=0;return(T[0]=+T[0])&&(b+=T[0]*1e4),(T[1]=+T[1])&&(b+=T[1]*100),(T[2]=+T[2])&&(b+=T[2]),b}(process.version),!(g>=20302&&g<40204||g>=5e4&&g<50100||g>=50600&&g<60200)&&process.stdin.isTTY)process.stdin.pause(),fe=process.stdin.fd,Ce=process.stdin._handle;else try{fe=h().open("CONIN$",v.O_RDWR,parseInt("0666",8)),Ce=new Fr(fe,!0)}catch(x){}if(process.stdout.isTTY)oe=process.stdout.fd;else{try{oe=z.openSync("\\\\.\\CON","w")}catch(x){}if(typeof oe!="number")try{oe=h().open("CONOUT$",v.O_RDWR,parseInt("0666",8))}catch(x){}}}else{if(process.stdin.isTTY){process.stdin.pause();try{fe=z.openSync("/dev/tty","r"),Ce=process.stdin._handle}catch(x){}}else try{fe=z.openSync("/dev/tty","r"),Ce=new Fr(fe,!1)}catch(x){}if(process.stdout.isTTY)oe=process.stdout.fd;else try{oe=z.openSync("/dev/tty","w")}catch(x){}}}(),function(){var _,v,g=!r.hideEchoBack&&!r.keyIn,h,x,T,b,C;je="";function N(W){return W===zr?!0:Ce.setRawMode(W)!==0?!1:(zr=W,!0)}if(Wr||!Ce||typeof oe!="number"&&(r.display||!g)){u=w();return}if(r.display&&(z.writeSync(oe,r.display),r.display=""),!r.displayOnly){if(!N(!g)){u=w();return}for(x=r.keyIn?1:r.bufferSize,h=Buffer.allocUnsafe&&Buffer.alloc?Buffer.alloc(x):new Buffer(x),r.keyIn&&r.limit&&(v=new RegExp("[^"+r.limit+"]","g"+(r.caseSensitive?"":"i")));;){T=0;try{T=z.readSync(fe,h,0,x)}catch(W){if(W.code!=="EOF"){N(!1),u+=w();return}}if(T>0?(b=h.toString(r.encoding,0,T),je+=b):(b=` +`,je+=String.fromCharCode(0)),b&&typeof(C=(b.match(/^(.*?)[\r\n]/)||[])[1])=="string"&&(b=C,_=!0),b&&(b=b.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g,"")),b&&v&&(b=b.replace(v,"")),b&&(g||(r.hideEchoBack?r.mask&&z.writeSync(oe,new Array(b.length+1).join(r.mask)):z.writeSync(oe,b)),u+=b),!r.keyIn&&_||r.keyIn&&u.length>=x)break}!g&&!c&&z.writeSync(oe,` +`),N(!1)}}(),r.print&&!c&&r.print(p+(r.displayOnly?"":(r.hideEchoBack?new Array(u.length+1).join(r.mask):u)+` +`),r.encoding),r.displayOnly?"":hr=r.keepWhitespace||r.keyIn?u:u.trim()}function ns(r,u){var p=[];function c(w){w!=null&&(Array.isArray(w)?w.forEach(c):(!u||u(w))&&p.push(w))}return c(r),p}function Tr(r){return r.replace(/[\x00-\x7f]/g,function(u){return"\\x"+("00"+u.charCodeAt().toString(16)).substr(-2)})}function Z(){var r=Array.prototype.slice.call(arguments),u,p;return r.length&&typeof r[0]=="boolean"&&(p=r.shift(),p&&(u=Object.keys(dr),r.unshift(dr))),r.reduce(function(c,w){return w==null||(w.hasOwnProperty("noEchoBack")&&!w.hasOwnProperty("hideEchoBack")&&(w.hideEchoBack=w.noEchoBack,delete w.noEchoBack),w.hasOwnProperty("noTrim")&&!w.hasOwnProperty("keepWhitespace")&&(w.keepWhitespace=w.noTrim,delete w.noTrim),p||(u=Object.keys(w)),u.forEach(function(_){var v;if(!!w.hasOwnProperty(_))switch(v=w[_],_){case"mask":case"limitMessage":case"defaultInput":case"encoding":v=v!=null?v+"":"",v&&_!=="limitMessage"&&(v=v.replace(/[\r\n]/g,"")),c[_]=v;break;case"bufferSize":!isNaN(v=parseInt(v,10))&&typeof v=="number"&&(c[_]=v);break;case"displayOnly":case"keyIn":case"hideEchoBack":case"caseSensitive":case"keepWhitespace":case"history":case"cd":c[_]=!!v;break;case"limit":case"trueValue":case"falseValue":c[_]=ns(v,function(g){var h=typeof g;return h==="string"||h==="number"||h==="function"||g instanceof RegExp}).map(function(g){return typeof g=="string"?g.replace(/[\r\n]/g,""):g});break;case"print":case"phContent":case"preCheck":c[_]=typeof v=="function"?v:void 0;break;case"prompt":case"display":c[_]=v!=null?v:"";break}})),c},{})}function xr(r,u,p){return u.some(function(c){var w=typeof c;return w==="string"?p?r===c:r.toLowerCase()===c.toLowerCase():w==="number"?parseFloat(r)===c:w==="function"?c(r):c instanceof RegExp?c.test(r):!1})}function Vr(r,u){var p=_e.normalize(Ve?(process.env.HOMEDRIVE||"")+(process.env.HOMEPATH||""):process.env.HOME||"").replace(/[\/\\]+$/,"");return r=_e.normalize(r),u?r.replace(/^~(?=\/|\\|$)/,p):r.replace(new RegExp("^"+Tr(p)+"(?=\\/|\\\\|$)",Ve?"i":""),"~")}function Oe(r,u){var p="(?:\\(([\\s\\S]*?)\\))?(\\w+|.-.)(?:\\(([\\s\\S]*?)\\))?",c=new RegExp("(\\$)?(\\$<"+p+">)","g"),w=new RegExp("(\\$)?(\\$\\{"+p+"\\})","g");function _(v,g,h,x,T,b){var C;return g||typeof(C=u(T))!="string"?h:C?(x||"")+C+(b||""):""}return r.replace(c,_).replace(w,_)}function Hr(r,u,p){var c,w=[],_=-1,v=0,g="",h;function x(T,b){return b.length>3?(T.push(b[0]+"..."+b[b.length-1]),h=!0):b.length&&(T=T.concat(b)),T}return c=r.reduce(function(T,b){return T.concat((b+"").split(""))},[]).reduce(function(T,b){var C,N;return u||(b=b.toLowerCase()),C=/^\d$/.test(b)?1:/^[A-Z]$/.test(b)?2:/^[a-z]$/.test(b)?3:0,p&&C===0?g+=b:(N=b.charCodeAt(0),C&&C===_&&N===v+1?w.push(b):(T=x(T,w),w=[b],_=C),v=N),T},[]),c=x(c,w),g&&(c.push(g),h=!0),{values:c,suppressed:h}}function Gr(r,u){return r.join(r.length>2?", ":u?" / ":"/")}function Yr(r,u){var p,c,w={},_;if(u.phContent&&(p=u.phContent(r,u)),typeof p!="string")switch(r){case"hideEchoBack":case"mask":case"defaultInput":case"caseSensitive":case"keepWhitespace":case"encoding":case"bufferSize":case"history":case"cd":p=u.hasOwnProperty(r)?typeof u[r]=="boolean"?u[r]?"on":"off":u[r]+"":"";break;case"limit":case"trueValue":case"falseValue":c=u[u.hasOwnProperty(r+"Src")?r+"Src":r],u.keyIn?(w=Hr(c,u.caseSensitive),c=w.values):c=c.filter(function(v){var g=typeof v;return g==="string"||g==="number"}),p=Gr(c,w.suppressed);break;case"limitCount":case"limitCountNotZero":p=u[u.hasOwnProperty("limitSrc")?"limitSrc":"limit"].length,p=p||r!=="limitCountNotZero"?p+"":"";break;case"lastInput":p=hr;break;case"cwd":case"CWD":case"cwdHome":p=process.cwd(),r==="CWD"?p=_e.basename(p):r==="cwdHome"&&(p=Vr(p));break;case"date":case"time":case"localeDate":case"localeTime":p=new Date()["to"+r.replace(/^./,function(v){return v.toUpperCase()})+"String"]();break;default:typeof(_=(r.match(/^history_m(\d+)$/)||[])[1])=="string"&&(p=Se[Se.length-_]||"")}return p}function Ur(r){var u=/^(.)-(.)$/.exec(r),p="",c,w,_,v;if(!u)return null;for(c=u[1].charCodeAt(0),w=u[2].charCodeAt(0),v=c +And the length must be: $`,trueValue:null,falseValue:null,caseSensitive:!0},u,{history:!1,cd:!1,phContent:function(N){return N==="charlist"?p.text:N==="length"?c+"..."+w:null}}),v,g,h,x,T,b,C;for(u=u||{},v=Oe(u.charlist?u.charlist+"":"$",Ur),(isNaN(c=parseInt(u.min,10))||typeof c!="number")&&(c=12),(isNaN(w=parseInt(u.max,10))||typeof w!="number")&&(w=24),x=new RegExp("^["+Tr(v)+"]{"+c+","+w+"}$"),p=Hr([v],_.caseSensitive,!0),p.text=Gr(p.values,p.suppressed),g=u.confirmMessage!=null?u.confirmMessage:"Reinput a same one to confirm it: ",h=u.unmatchMessage!=null?u.unmatchMessage:"It differs from first one. Hit only the Enter key if you want to retry from first one.",r==null&&(r="Input new password: "),T=_.limitMessage;!C;)_.limit=x,_.limitMessage=T,b=M.question(r,_),_.limit=[b,""],_.limitMessage=h,C=M.question(g,_);return b};function Jr(r,u,p){var c;function w(_){return c=p(_),!isNaN(c)&&typeof c=="number"}return M.question(r,Z({limitMessage:"Input valid number, please."},u,{limit:w,cd:!1})),c}M.questionInt=function(r,u){return Jr(r,u,function(p){return parseInt(p,10)})};M.questionFloat=function(r,u){return Jr(r,u,parseFloat)};M.questionPath=function(r,u){var p,c="",w=Z({hideEchoBack:!1,limitMessage:`$Input valid path, please.$<( Min:)min>$<( Max:)max>`,history:!0,cd:!0},u,{keepWhitespace:!1,limit:function(_){var v,g,h;_=Vr(_,!0),c="";function x(T){T.split(/\/|\\/).reduce(function(b,C){var N=_e.resolve(b+=C+_e.sep);if(!z.existsSync(N))z.mkdirSync(N);else if(!z.statSync(N).isDirectory())throw new Error("Non directory already exists: "+N);return b},"")}try{if(v=z.existsSync(_),p=v?z.realpathSync(_):_e.resolve(_),!u.hasOwnProperty("exists")&&!v||typeof u.exists=="boolean"&&u.exists!==v)return c=(v?"Already exists":"No such file or directory")+": "+p,!1;if(!v&&u.create&&(u.isDirectory?x(p):(x(_e.dirname(p)),z.closeSync(z.openSync(p,"w"))),p=z.realpathSync(p)),v&&(u.min||u.max||u.isFile||u.isDirectory)){if(g=z.statSync(p),u.isFile&&!g.isFile())return c="Not file: "+p,!1;if(u.isDirectory&&!g.isDirectory())return c="Not directory: "+p,!1;if(u.min&&g.size<+u.min||u.max&&g.size>+u.max)return c="Size "+g.size+" is out of range: "+p,!1}if(typeof u.validate=="function"&&(h=u.validate(p))!==!0)return typeof h=="string"&&(c=h),!1}catch(T){return c=T+"",!1}return!0},phContent:function(_){return _==="error"?c:_!=="min"&&_!=="max"?null:u.hasOwnProperty(_)?u[_]+"":""}});return u=u||{},r==null&&(r='Input path (you can "cd" and "pwd"): '),M.question(r,w),p};function Kr(r,u){var p={},c={};return typeof r=="object"?(Object.keys(r).forEach(function(w){typeof r[w]=="function"&&(c[u.caseSensitive?w:w.toLowerCase()]=r[w])}),p.preCheck=function(w){var _;return p.args=Sr(w),_=p.args[0]||"",u.caseSensitive||(_=_.toLowerCase()),p.hRes=_!=="_"&&c.hasOwnProperty(_)?c[_].apply(w,p.args.slice(1)):c.hasOwnProperty("_")?c._.apply(w,p.args):null,{res:w,forceNext:!1}},c.hasOwnProperty("_")||(p.limit=function(){var w=p.args[0]||"";return u.caseSensitive||(w=w.toLowerCase()),c.hasOwnProperty(w)})):p.preCheck=function(w){return p.args=Sr(w),p.hRes=typeof r=="function"?r.apply(w,p.args):!0,{res:w,forceNext:!1}},p}M.promptCL=function(r,u){var p=Z({hideEchoBack:!1,limitMessage:"Requested command is not available.",caseSensitive:!1,history:!0},u),c=Kr(r,p);return p.limit=c.limit,p.preCheck=c.preCheck,M.prompt(p),c.args};M.promptLoop=function(r,u){for(var p=Z({hideEchoBack:!1,trueValue:null,falseValue:null,caseSensitive:!1,history:!0},u);!r(M.prompt(p)););};M.promptCLLoop=function(r,u){var p=Z({hideEchoBack:!1,limitMessage:"Requested command is not available.",caseSensitive:!1,history:!0},u),c=Kr(r,p);for(p.limit=c.limit,p.preCheck=c.preCheck;M.prompt(p),!c.hRes;);};M.promptSimShell=function(r){return M.prompt(Z({hideEchoBack:!1,history:!0},r,{prompt:function(){return Ve?"$>":(process.env.USER||"")+(process.env.HOSTNAME?"@"+process.env.HOSTNAME.replace(/\..*$/,""):"")+":$$ "}()}))};function jr(r,u,p){var c;return r==null&&(r="Are you sure? "),(!u||u.guide!==!1)&&(r+="")&&(r=r.replace(/\s*:?\s*$/,"")+" [y/n]: "),c=M.keyIn(r,Z(u,{hideEchoBack:!1,limit:p,trueValue:"y",falseValue:"n",caseSensitive:!1})),typeof c=="boolean"?c:""}M.keyInYN=function(r,u){return jr(r,u)};M.keyInYNStrict=function(r,u){return jr(r,u,"yn")};M.keyInPause=function(r,u){r==null&&(r="Continue..."),(!u||u.guide!==!1)&&(r+="")&&(r=r.replace(/\s+$/,"")+" (Hit any key)"),M.keyIn(r,Z({limit:null},u,{hideEchoBack:!0,mask:""}))};M.keyInSelect=function(r,u,p){var c=Z({hideEchoBack:!1},p,{trueValue:null,falseValue:null,caseSensitive:!1,phContent:function(h){return h==="itemsCount"?r.length+"":h==="firstItem"?(r[0]+"").trim():h==="lastItem"?(r[r.length-1]+"").trim():null}}),w="",_={},v=49,g=` +`;if(!Array.isArray(r)||!r.length||r.length>35)throw"`items` must be Array (max length: 35).";return r.forEach(function(h,x){var T=String.fromCharCode(v);w+=T,_[T]=x,g+="["+T+"] "+(h+"").trim()+` +`,v=v===57?97:v+1}),(!p||p.cancel!==!1)&&(w+="0",_["0"]=-1,g+="[0] "+(p&&p.cancel!=null&&typeof p.cancel!="boolean"?(p.cancel+"").trim():"CANCEL")+` +`),c.limit=w,g+=` +`,u==null&&(u="Choose one from list: "),(u+="")&&((!p||p.guide!==!1)&&(u=u.replace(/\s*:?\s*$/,"")+" [$]: "),g+=u),_[M.keyIn(g,c).toLowerCase()]};M.getRawInput=function(){return je};function De(r,u){var p;return u.length&&(p={},p[r]=u[0]),M.setDefaultOptions(p)[r]}M.setPrint=function(){return De("print",arguments)};M.setPrompt=function(){return De("prompt",arguments)};M.setEncoding=function(){return De("encoding",arguments)};M.setMask=function(){return De("mask",arguments)};M.setBufferSize=function(){return De("bufferSize",arguments)}});var kr=I((Mu,ie)=>{(function(){var r={major:0,minor:2,patch:66,status:"beta"};tau_file_system={files:{},open:function(e,n,t){var s=tau_file_system.files[e];if(!s){if(t==="read")return null;s={path:e,text:"",type:n,get:function(a,l){return l===this.text.length||l>this.text.length?"end_of_file":this.text.substring(l,l+a)},put:function(a,l){return l==="end_of_file"?(this.text+=a,!0):l==="past_end_of_file"?null:(this.text=this.text.substring(0,l)+a+this.text.substring(l+a.length),!0)},get_byte:function(a){if(a==="end_of_stream")return-1;var l=Math.floor(a/2);if(this.text.length<=l)return-1;var f=_(this.text[Math.floor(a/2)],0);return a%2==0?f&255:f/256>>>0},put_byte:function(a,l){var f=l==="end_of_stream"?this.text.length:Math.floor(l/2);if(this.text.length>>0,y=(y&255)<<8|a&255):(y=y&255,y=(a&255)<<8|y&255),this.text.length===f?this.text+=v(y):this.text=this.text.substring(0,f)+v(y)+this.text.substring(f+1),!0},flush:function(){return!0},close:function(){var a=tau_file_system.files[this.path];return a?!0:null}},tau_file_system.files[e]=s}return t==="write"&&(s.text=""),s}},tau_user_input={buffer:"",get:function(e,n){for(var t;tau_user_input.buffer.length\?\@\^\~\\]+|'(?:[^']*?(?:\\(?:x?\d+)?\\)*(?:'')*(?:\\')*)*')/,number:/^(?:0o[0-7]+|0x[0-9a-fA-F]+|0b[01]+|0'(?:''|\\[abfnrtv\\'"`]|\\x?\d+\\|[^\\])|\d+(?:\.\d+(?:[eE][+-]?\d+)?)?)/,string:/^(?:"([^"]|""|\\")*"|`([^`]|``|\\`)*`)/,l_brace:/^(?:\[)/,r_brace:/^(?:\])/,l_bracket:/^(?:\{)/,r_bracket:/^(?:\})/,bar:/^(?:\|)/,l_paren:/^(?:\()/,r_paren:/^(?:\))/};function te(e,n){return e.get_flag("char_conversion").id==="on"?n.replace(/./g,function(t){return e.get_char_conversion(t)}):n}function j(e){this.thread=e,this.text="",this.tokens=[]}j.prototype.set_last_tokens=function(e){return this.tokens=e},j.prototype.new_text=function(e){this.text=e,this.tokens=[]},j.prototype.get_tokens=function(e){var n,t=0,s=0,a=0,l=[],f=!1;if(e){var y=this.tokens[e-1];t=y.len,n=te(this.thread,this.text.substr(y.len)),s=y.line,a=y.start}else n=this.text;if(/^\s*$/.test(n))return null;for(;n!=="";){var d=[],m=!1;if(/^\n/.exec(n)!==null){s++,a=0,t++,n=n.replace(/\n/,""),f=!0;continue}for(var S in ee)if(ee.hasOwnProperty(S)){var P=ee[S].exec(n);P&&d.push({value:P[0],name:S,matches:P})}if(!d.length)return this.set_last_tokens([{value:n,matches:[],name:"lexical",line:s,start:a}]);var y=p(d,function(B,q){return B.value.length>=q.value.length?B:q});switch(y.start=a,y.line=s,n=n.replace(y.value,""),a+=y.value.length,t+=y.value.length,y.name){case"atom":y.raw=y.value,y.value.charAt(0)==="'"&&(y.value=C(y.value.substr(1,y.value.length-2),"'"),y.value===null&&(y.name="lexical",y.value="unknown escape sequence"));break;case"number":y.float=y.value.substring(0,2)!=="0x"&&y.value.match(/[.eE]/)!==null&&y.value!=="0'.",y.value=W(y.value),y.blank=m;break;case"string":var A=y.value.charAt(0);y.value=C(y.value.substr(1,y.value.length-2),A),y.value===null&&(y.name="lexical",y.value="unknown escape sequence");break;case"whitespace":var R=l[l.length-1];R&&(R.space=!0),m=!0;continue;case"r_bracket":l.length>0&&l[l.length-1].name==="l_bracket"&&(y=l.pop(),y.name="atom",y.value="{}",y.raw="{}",y.space=!1);break;case"r_brace":l.length>0&&l[l.length-1].name==="l_brace"&&(y=l.pop(),y.name="atom",y.value="[]",y.raw="[]",y.space=!1);break}y.len=t,l.push(y),m=!1}var k=this.set_last_tokens(l);return k.length===0?null:k};function U(e,n,t,s,a){if(!n[t])return{type:g,value:i.error.syntax(n[t-1],"expression expected",!0)};var l;if(s==="0"){var f=n[t];switch(f.name){case"number":return{type:h,len:t+1,value:new i.type.Num(f.value,f.float)};case"variable":return{type:h,len:t+1,value:new i.type.Var(f.value)};case"string":var y;switch(e.get_flag("double_quotes").id){case"atom":y=new o(f.value,[]);break;case"codes":y=new o("[]",[]);for(var d=f.value.length-1;d>=0;d--)y=new o(".",[new i.type.Num(_(f.value,d),!1),y]);break;case"chars":y=new o("[]",[]);for(var d=f.value.length-1;d>=0;d--)y=new o(".",[new i.type.Term(f.value.charAt(d),[]),y]);break}return{type:h,len:t+1,value:y};case"l_paren":var k=U(e,n,t+1,e.__get_max_priority(),!0);return k.type!==h?k:n[k.len]&&n[k.len].name==="r_paren"?(k.len++,k):{type:g,derived:!0,value:i.error.syntax(n[k.len]?n[k.len]:n[k.len-1],") or operator expected",!n[k.len])};case"l_bracket":var k=U(e,n,t+1,e.__get_max_priority(),!0);return k.type!==h?k:n[k.len]&&n[k.len].name==="r_bracket"?(k.len++,k.value=new o("{}",[k.value]),k):{type:g,derived:!0,value:i.error.syntax(n[k.len]?n[k.len]:n[k.len-1],"} or operator expected",!n[k.len])}}var m=Ue(e,n,t,a);return m.type===h||m.derived||(m=Ze(e,n,t),m.type===h||m.derived)?m:{type:g,derived:!1,value:i.error.syntax(n[t],"unexpected token")}}var S=e.__get_max_priority(),P=e.__get_next_priority(s),A=t;if(n[t].name==="atom"&&n[t+1]&&(n[t].space||n[t+1].name!=="l_paren")){var f=n[t++],R=e.__lookup_operator_classes(s,f.value);if(R&&R.indexOf("fy")>-1){var k=U(e,n,t,s,a);if(k.type!==g)return f.value==="-"&&!f.space&&i.type.is_number(k.value)?{value:new i.type.Num(-k.value.value,k.value.is_float),len:k.len,type:h}:{value:new i.type.Term(f.value,[k.value]),len:k.len,type:h};l=k}else if(R&&R.indexOf("fx")>-1){var k=U(e,n,t,P,a);if(k.type!==g)return{value:new i.type.Term(f.value,[k.value]),len:k.len,type:h};l=k}}t=A;var k=U(e,n,t,P,a);if(k.type===h){t=k.len;var f=n[t];if(n[t]&&(n[t].name==="atom"&&e.__lookup_operator_classes(s,f.value)||n[t].name==="bar"&&e.__lookup_operator_classes(s,"|"))){var L=P,B=s,R=e.__lookup_operator_classes(s,f.value);if(R.indexOf("xf")>-1)return{value:new i.type.Term(f.value,[k.value]),len:++k.len,type:h};if(R.indexOf("xfx")>-1){var q=U(e,n,t+1,L,a);return q.type===h?{value:new i.type.Term(f.value,[k.value,q.value]),len:q.len,type:h}:(q.derived=!0,q)}else if(R.indexOf("xfy")>-1){var q=U(e,n,t+1,B,a);return q.type===h?{value:new i.type.Term(f.value,[k.value,q.value]),len:q.len,type:h}:(q.derived=!0,q)}else if(k.type!==g)for(;;){t=k.len;var f=n[t];if(f&&f.name==="atom"&&e.__lookup_operator_classes(s,f.value)){var R=e.__lookup_operator_classes(s,f.value);if(R.indexOf("yf")>-1)k={value:new i.type.Term(f.value,[k.value]),len:++t,type:h};else if(R.indexOf("yfx")>-1){var q=U(e,n,++t,L,a);if(q.type===g)return q.derived=!0,q;t=q.len,k={value:new i.type.Term(f.value,[k.value,q.value]),len:t,type:h}}else break}else break}}else l={type:g,value:i.error.syntax(n[k.len-1],"operator expected")};return k}return k}function Ue(e,n,t,s){if(!n[t]||n[t].name==="atom"&&n[t].raw==="."&&!s&&(n[t].space||!n[t+1]||n[t+1].name!=="l_paren"))return{type:g,derived:!1,value:i.error.syntax(n[t-1],"unfounded token")};var a=n[t],l=[];if(n[t].name==="atom"&&n[t].raw!==","){if(t++,n[t-1].space)return{type:h,len:t,value:new i.type.Term(a.value,l)};if(n[t]&&n[t].name==="l_paren"){if(n[t+1]&&n[t+1].name==="r_paren")return{type:g,derived:!0,value:i.error.syntax(n[t+1],"argument expected")};var f=U(e,n,++t,"999",!0);if(f.type===g)return f.derived?f:{type:g,derived:!0,value:i.error.syntax(n[t]?n[t]:n[t-1],"argument expected",!n[t])};for(l.push(f.value),t=f.len;n[t]&&n[t].name==="atom"&&n[t].value===",";){if(f=U(e,n,t+1,"999",!0),f.type===g)return f.derived?f:{type:g,derived:!0,value:i.error.syntax(n[t+1]?n[t+1]:n[t],"argument expected",!n[t+1])};l.push(f.value),t=f.len}if(n[t]&&n[t].name==="r_paren")t++;else return{type:g,derived:!0,value:i.error.syntax(n[t]?n[t]:n[t-1],", or ) expected",!n[t])}}return{type:h,len:t,value:new i.type.Term(a.value,l)}}return{type:g,derived:!1,value:i.error.syntax(n[t],"term expected")}}function Ze(e,n,t){if(!n[t])return{type:g,derived:!1,value:i.error.syntax(n[t-1],"[ expected")};if(n[t]&&n[t].name==="l_brace"){var s=U(e,n,++t,"999",!0),a=[s.value],l=void 0;if(s.type===g)return n[t]&&n[t].name==="r_brace"?{type:h,len:t+1,value:new i.type.Term("[]",[])}:{type:g,derived:!0,value:i.error.syntax(n[t],"] expected")};for(t=s.len;n[t]&&n[t].name==="atom"&&n[t].value===",";){if(s=U(e,n,t+1,"999",!0),s.type===g)return s.derived?s:{type:g,derived:!0,value:i.error.syntax(n[t+1]?n[t+1]:n[t],"argument expected",!n[t+1])};a.push(s.value),t=s.len}var f=!1;if(n[t]&&n[t].name==="bar"){if(f=!0,s=U(e,n,t+1,"999",!0),s.type===g)return s.derived?s:{type:g,derived:!0,value:i.error.syntax(n[t+1]?n[t+1]:n[t],"argument expected",!n[t+1])};l=s.value,t=s.len}return n[t]&&n[t].name==="r_brace"?{type:h,len:t+1,value:he(a,l)}:{type:g,derived:!0,value:i.error.syntax(n[t]?n[t]:n[t-1],f?"] expected":", or | or ] expected",!n[t])}}return{type:g,derived:!1,value:i.error.syntax(n[t],"list expected")}}function Qe(e,n,t){var s=n[t].line,a=U(e,n,t,e.__get_max_priority(),!1),l=null,f;if(a.type!==g)if(t=a.len,n[t]&&n[t].name==="atom"&&n[t].raw===".")if(t++,i.type.is_term(a.value)){if(a.value.indicator===":-/2"?(l=new i.type.Rule(a.value.args[0],ve(a.value.args[1])),f={value:l,len:t,type:h}):a.value.indicator==="-->/2"?(l=Bi(new i.type.Rule(a.value.args[0],a.value.args[1]),e),l.body=ve(l.body),f={value:l,len:t,type:i.type.is_rule(l)?h:g}):(l=new i.type.Rule(a.value,null),f={value:l,len:t,type:h}),l){var y=l.singleton_variables();y.length>0&&e.throw_warning(i.warning.singleton(y,l.head.indicator,s))}return f}else return{type:g,value:i.error.syntax(n[t],"callable expected")};else return{type:g,value:i.error.syntax(n[t]?n[t]:n[t-1],". or operator expected")};return a}function Di(e,n,t){t=t||{},t.from=t.from?t.from:"$tau-js",t.reconsult=t.reconsult!==void 0?t.reconsult:!0;var s=new j(e),a={},l;s.new_text(n);var f=0,y=s.get_tokens(f);do{if(y===null||!y[f])break;var d=Qe(e,y,f);if(d.type===g)return new o("throw",[d.value]);if(d.value.body===null&&d.value.head.indicator==="?-/1"){var m=new X(e.session);m.add_goal(d.value.head.args[0]),m.answer(function(P){i.type.is_error(P)?e.throw_warning(P.args[0]):(P===!1||P===null)&&e.throw_warning(i.warning.failed_goal(d.value.head.args[0],d.len))}),f=d.len;var S=!0}else if(d.value.body===null&&d.value.head.indicator===":-/1"){var S=e.run_directive(d.value.head.args[0]);f=d.len,d.value.head.args[0].indicator==="char_conversion/2"&&(y=s.get_tokens(f),f=0)}else{l=d.value.head.indicator,t.reconsult!==!1&&a[l]!==!0&&!e.is_multifile_predicate(l)&&(e.session.rules[l]=w(e.session.rules[l]||[],function(A){return A.dynamic}),a[l]=!0);var S=e.add_rule(d.value,t);f=d.len}if(!S)return S}while(!0);return!0}function Xi(e,n){var t=new j(e);t.new_text(n);var s=0;do{var a=t.get_tokens(s);if(a===null)break;var l=U(e,a,0,e.__get_max_priority(),!1);if(l.type!==g){var f=l.len,y=f;if(a[f]&&a[f].name==="atom"&&a[f].raw===".")e.add_goal(ve(l.value));else{var d=a[f];return new o("throw",[i.error.syntax(d||a[f-1],". or operator expected",!d)])}s=l.len+1}else return new o("throw",[l.value])}while(!0);return!0}function Bi(e,n){e=e.rename(n);var t=n.next_free_variable(),s=pr(e.body,t,n);return s.error?s.value:(e.body=s.value,e.head.args=e.head.args.concat([t,s.variable]),e.head=new o(e.head.id,e.head.args),e)}function pr(e,n,t){var s;if(i.type.is_term(e)&&e.indicator==="!/0")return{value:e,variable:n,error:!1};if(i.type.is_term(e)&&e.indicator===",/2"){var a=pr(e.args[0],n,t);if(a.error)return a;var l=pr(e.args[1],a.variable,t);return l.error?l:{value:new o(",",[a.value,l.value]),variable:l.variable,error:!1}}else{if(i.type.is_term(e)&&e.indicator==="{}/1")return{value:e.args[0],variable:n,error:!1};if(i.type.is_empty_list(e))return{value:new o("true",[]),variable:n,error:!1};if(i.type.is_list(e)){s=t.next_free_variable();for(var f=e,y;f.indicator==="./2";)y=f,f=f.args[1];return i.type.is_variable(f)?{value:i.error.instantiation("DCG"),variable:n,error:!0}:i.type.is_empty_list(f)?(y.args[1]=s,{value:new o("=",[n,e]),variable:s,error:!1}):{value:i.error.type("list",e,"DCG"),variable:n,error:!0}}else return i.type.is_callable(e)?(s=t.next_free_variable(),e.args=e.args.concat([n,s]),e=new o(e.id,e.args),{value:e,variable:s,error:!1}):{value:i.error.type("callable",e,"DCG"),variable:n,error:!0}}}function ve(e){return i.type.is_variable(e)?new o("call",[e]):i.type.is_term(e)&&[",/2",";/2","->/2"].indexOf(e.indicator)!==-1?new o(e.id,[ve(e.args[0]),ve(e.args[1])]):e}function he(e,n){for(var t=n||new i.type.Term("[]",[]),s=e.length-1;s>=0;s--)t=new i.type.Term(".",[e[s],t]);return t}function Fi(e,n){for(var t=e.length-1;t>=0;t--)e[t]===n&&e.splice(t,1)}function yr(e){for(var n={},t=[],s=0;s=0;n--)if(e.charAt(n)==="/")return new o("/",[new o(e.substring(0,n)),new E(parseInt(e.substring(n+1)),!1)])}function O(e){this.id=e}function E(e,n){this.is_float=n!==void 0?n:parseInt(e)!==e,this.value=this.is_float?e:parseInt(e)}var $r=0;function o(e,n,t){this.ref=t||++$r,this.id=e,this.args=n||[],this.indicator=e+"/"+this.args.length}var Wi=0;function ne(e,n,t,s,a,l){this.id=Wi++,this.stream=e,this.mode=n,this.alias=t,this.type=s!==void 0?s:"text",this.reposition=a!==void 0?a:!0,this.eof_action=l!==void 0?l:"eof_code",this.position=this.mode==="append"?"end_of_stream":0,this.output=this.mode==="write"||this.mode==="append",this.input=this.mode==="read"}function Y(e){e=e||{},this.links=e}function V(e,n,t){n=n||new Y,t=t||null,this.goal=e,this.substitution=n,this.parent=t}function Q(e,n,t){this.head=e,this.body=n,this.dynamic=t||!1}function D(e){e=e===void 0||e<=0?1e3:e,this.rules={},this.src_predicates={},this.rename=0,this.modules=[],this.thread=new X(this),this.total_threads=1,this.renamed_variables={},this.public_predicates={},this.multifile_predicates={},this.limit=e,this.streams={user_input:new ne(typeof ie!="undefined"&&ie.exports?nodejs_user_input:tau_user_input,"read","user_input","text",!1,"reset"),user_output:new ne(typeof ie!="undefined"&&ie.exports?nodejs_user_output:tau_user_output,"write","user_output","text",!1,"eof_code")},this.file_system=typeof ie!="undefined"&&ie.exports?nodejs_file_system:tau_file_system,this.standard_input=this.streams.user_input,this.standard_output=this.streams.user_output,this.current_input=this.streams.user_input,this.current_output=this.streams.user_output,this.format_success=function(n){return n.substitution},this.format_error=function(n){return n.goal},this.flag={bounded:i.flag.bounded.value,max_integer:i.flag.max_integer.value,min_integer:i.flag.min_integer.value,integer_rounding_function:i.flag.integer_rounding_function.value,char_conversion:i.flag.char_conversion.value,debug:i.flag.debug.value,max_arity:i.flag.max_arity.value,unknown:i.flag.unknown.value,double_quotes:i.flag.double_quotes.value,occurs_check:i.flag.occurs_check.value,dialect:i.flag.dialect.value,version_data:i.flag.version_data.value,nodejs:i.flag.nodejs.value},this.__loaded_modules=[],this.__char_conversion={},this.__operators={1200:{":-":["fx","xfx"],"-->":["xfx"],"?-":["fx"]},1100:{";":["xfy"]},1050:{"->":["xfy"]},1e3:{",":["xfy"]},900:{"\\+":["fy"]},700:{"=":["xfx"],"\\=":["xfx"],"==":["xfx"],"\\==":["xfx"],"@<":["xfx"],"@=<":["xfx"],"@>":["xfx"],"@>=":["xfx"],"=..":["xfx"],is:["xfx"],"=:=":["xfx"],"=\\=":["xfx"],"<":["xfx"],"=<":["xfx"],">":["xfx"],">=":["xfx"]},600:{":":["xfy"]},500:{"+":["yfx"],"-":["yfx"],"/\\":["yfx"],"\\/":["yfx"]},400:{"*":["yfx"],"/":["yfx"],"//":["yfx"],rem:["yfx"],mod:["yfx"],"<<":["yfx"],">>":["yfx"]},200:{"**":["xfx"],"^":["xfy"],"-":["fy"],"+":["fy"],"\\":["fy"]}}}function X(e){this.epoch=Date.now(),this.session=e,this.session.total_threads++,this.total_steps=0,this.cpu_time=0,this.cpu_time_last=0,this.points=[],this.debugger=!1,this.debugger_states=[],this.level="top_level/0",this.__calls=[],this.current_limit=this.session.limit,this.warnings=[]}function Dr(e,n,t){this.id=e,this.rules=n,this.exports=t,i.module[e]=this}Dr.prototype.exports_predicate=function(e){return this.exports.indexOf(e)!==-1},O.prototype.unify=function(e,n){if(n&&u(e.variables(),this.id)!==-1&&!i.type.is_variable(e))return null;var t={};return t[this.id]=e,new Y(t)},E.prototype.unify=function(e,n){return i.type.is_number(e)&&this.value===e.value&&this.is_float===e.is_float?new Y:null},o.prototype.unify=function(e,n){if(i.type.is_term(e)&&this.indicator===e.indicator){for(var t=new Y,s=0;s=0){var s=this.args[0].value,a=Math.floor(s/26),l=s%26;return"ABCDEFGHIJKLMNOPQRSTUVWXYZ"[l]+(a!==0?a:"")}switch(this.indicator){case"[]/0":case"{}/0":case"!/0":return this.id;case"{}/1":return"{"+this.args[0].toString(e)+"}";case"./2":for(var f="["+this.args[0].toString(e),y=this.args[1];y.indicator==="./2";)f+=", "+y.args[0].toString(e),y=y.args[1];return y.indicator!=="[]/0"&&(f+="|"+y.toString(e)),f+="]",f;case",/2":return"("+this.args[0].toString(e)+", "+this.args[1].toString(e)+")";default:var d=this.id,m=e.session?e.session.lookup_operator(this.id,this.args.length):null;if(e.session===void 0||e.ignore_ops||m===null)return e.quoted&&!/^(!|,|;|[a-z][0-9a-zA-Z_]*)$/.test(d)&&d!=="{}"&&d!=="[]"&&(d="'"+N(d)+"'"),d+(this.args.length?"("+c(this.args,function(R){return R.toString(e)}).join(", ")+")":"");var S=m.priority>n.priority||m.priority===n.priority&&(m.class==="xfy"&&this.indicator!==n.indicator||m.class==="yfx"&&this.indicator!==n.indicator||this.indicator===n.indicator&&m.class==="yfx"&&t==="right"||this.indicator===n.indicator&&m.class==="xfy"&&t==="left");m.indicator=this.indicator;var P=S?"(":"",A=S?")":"";return this.args.length===0?"("+this.id+")":["fy","fx"].indexOf(m.class)!==-1?P+d+" "+this.args[0].toString(e,m)+A:["yf","xf"].indexOf(m.class)!==-1?P+this.args[0].toString(e,m)+" "+d+A:P+this.args[0].toString(e,m,"left")+" "+this.id+" "+this.args[1].toString(e,m,"right")+A}},ne.prototype.toString=function(e){return"("+this.id+")"},Y.prototype.toString=function(e){var n="{";for(var t in this.links)!this.links.hasOwnProperty(t)||(n!=="{"&&(n+=", "),n+=t+"/"+this.links[t].toString(e));return n+="}",n},V.prototype.toString=function(e){return this.goal===null?"<"+this.substitution.toString(e)+">":"<"+this.goal.toString(e)+", "+this.substitution.toString(e)+">"},Q.prototype.toString=function(e){return this.body?this.head.toString(e)+" :- "+this.body.toString(e)+".":this.head.toString(e)+"."},D.prototype.toString=function(e){for(var n="",t=0;t=0;a--)s=new o(".",[n[a],s]);return s}return new o(this.id,c(this.args,function(l){return l.apply(e)}),this.ref)},ne.prototype.apply=function(e){return this},Q.prototype.apply=function(e){return new Q(this.head.apply(e),this.body!==null?this.body.apply(e):null)},Y.prototype.apply=function(e){var n,t={};for(n in this.links)!this.links.hasOwnProperty(n)||(t[n]=this.links[n].apply(e));return new Y(t)},o.prototype.select=function(){for(var e=this;e.indicator===",/2";)e=e.args[0];return e},o.prototype.replace=function(e){return this.indicator===",/2"?this.args[0].indicator===",/2"?new o(",",[this.args[0].replace(e),this.args[1]]):e===null?this.args[1]:new o(",",[e,this.args[1]]):e},o.prototype.search=function(e){if(i.type.is_term(e)&&e.ref!==void 0&&this.ref===e.ref)return!0;for(var n=0;nn&&s0&&(n=this.head_point().substitution.domain());u(n,i.format_variable(this.session.rename))!==-1;)this.session.rename++;if(e.id==="_")return new O(i.format_variable(this.session.rename));this.session.renamed_variables[e.id]=i.format_variable(this.session.rename)}return new O(this.session.renamed_variables[e.id])},D.prototype.next_free_variable=function(){return this.thread.next_free_variable()},X.prototype.next_free_variable=function(){this.session.rename++;var e=[];for(this.points.length>0&&(e=this.head_point().substitution.domain());u(e,i.format_variable(this.session.rename))!==-1;)this.session.rename++;return new O(i.format_variable(this.session.rename))},D.prototype.is_public_predicate=function(e){return!this.public_predicates.hasOwnProperty(e)||this.public_predicates[e]===!0},X.prototype.is_public_predicate=function(e){return this.session.is_public_predicate(e)},D.prototype.is_multifile_predicate=function(e){return this.multifile_predicates.hasOwnProperty(e)&&this.multifile_predicates[e]===!0},X.prototype.is_multifile_predicate=function(e){return this.session.is_multifile_predicate(e)},D.prototype.prepend=function(e){return this.thread.prepend(e)},X.prototype.prepend=function(e){for(var n=e.length-1;n>=0;n--)this.points.push(e[n])},D.prototype.success=function(e,n){return this.thread.success(e,n)},X.prototype.success=function(e,n){var n=typeof n=="undefined"?e:n;this.prepend([new V(e.goal.replace(null),e.substitution,n)])},D.prototype.throw_error=function(e){return this.thread.throw_error(e)},X.prototype.throw_error=function(e){this.prepend([new V(new o("throw",[e]),new Y,null,null)])},D.prototype.step_rule=function(e,n){return this.thread.step_rule(e,n)},X.prototype.step_rule=function(e,n){var t=n.indicator;if(e==="user"&&(e=null),e===null&&this.session.rules.hasOwnProperty(t))return this.session.rules[t];for(var s=e===null?this.session.modules:u(this.session.modules,e)===-1?[]:[e],a=0;a1)&&this.again()},D.prototype.answers=function(e,n,t){return this.thread.answers(e,n,t)},X.prototype.answers=function(e,n,t){var s=n||1e3,a=this;if(n<=0){t&&t();return}this.answer(function(l){e(l),l!==!1?setTimeout(function(){a.answers(e,n-1,t)},1):t&&t()})},D.prototype.again=function(e){return this.thread.again(e)},X.prototype.again=function(e){for(var n,t=Date.now();this.__calls.length>0;){for(this.warnings=[],e!==!1&&(this.current_limit=this.session.limit);this.current_limit>0&&this.points.length>0&&this.head_point().goal!==null&&!i.type.is_error(this.head_point().goal);)if(this.current_limit--,this.step()===!0)return;var s=Date.now();this.cpu_time_last=s-t,this.cpu_time+=this.cpu_time_last;var a=this.__calls.shift();this.current_limit<=0?a(null):this.points.length===0?a(!1):i.type.is_error(this.head_point().goal)?(n=this.session.format_error(this.points.pop()),this.points=[],a(n)):(this.debugger&&this.debugger_states.push(this.head_point()),n=this.session.format_success(this.points.pop()),a(n))}},D.prototype.unfold=function(e){if(e.body===null)return!1;var n=e.head,t=e.body,s=t.select(),a=new X(this),l=[];a.add_goal(s),a.step();for(var f=a.points.length-1;f>=0;f--){var y=a.points[f],d=n.apply(y.substitution),m=t.replace(y.goal);m!==null&&(m=m.apply(y.substitution)),l.push(new Q(d,m))}var S=this.rules[n.indicator],P=u(S,e);return l.length>0&&P!==-1?(S.splice.apply(S,[P,1].concat(l)),!0):!1},X.prototype.unfold=function(e){return this.session.unfold(e)},O.prototype.interpret=function(e){return i.error.instantiation(e.level)},E.prototype.interpret=function(e){return this},o.prototype.interpret=function(e){return i.type.is_unitary_list(this)?this.args[0].interpret(e):i.operate(e,this)},O.prototype.compare=function(e){return this.ide.id?1:0},E.prototype.compare=function(e){if(this.value===e.value&&this.is_float===e.is_float)return 0;if(this.valuee.value)return 1},o.prototype.compare=function(e){if(this.args.lengthe.args.length||this.args.length===e.args.length&&this.id>e.id)return 1;for(var n=0;ns)return 1;if(e.constructor===E){if(e.is_float&&n.is_float)return 0;if(e.is_float)return-1;if(n.is_float)return 1}return 0},is_substitution:function(e){return e instanceof Y},is_state:function(e){return e instanceof V},is_rule:function(e){return e instanceof Q},is_variable:function(e){return e instanceof O},is_stream:function(e){return e instanceof ne},is_anonymous_var:function(e){return e instanceof O&&e.id==="_"},is_callable:function(e){return e instanceof o},is_number:function(e){return e instanceof E},is_integer:function(e){return e instanceof E&&!e.is_float},is_float:function(e){return e instanceof E&&e.is_float},is_term:function(e){return e instanceof o},is_atom:function(e){return e instanceof o&&e.args.length===0},is_ground:function(e){if(e instanceof O)return!1;if(e instanceof o){for(var n=0;n0},is_list:function(e){return e instanceof o&&(e.indicator==="[]/0"||e.indicator==="./2")},is_empty_list:function(e){return e instanceof o&&e.indicator==="[]/0"},is_non_empty_list:function(e){return e instanceof o&&e.indicator==="./2"},is_fully_list:function(e){for(;e instanceof o&&e.indicator==="./2";)e=e.args[1];return e instanceof O||e instanceof o&&e.indicator==="[]/0"},is_instantiated_list:function(e){for(;e instanceof o&&e.indicator==="./2";)e=e.args[1];return e instanceof o&&e.indicator==="[]/0"},is_unitary_list:function(e){return e instanceof o&&e.indicator==="./2"&&e.args[1]instanceof o&&e.args[1].indicator==="[]/0"},is_character:function(e){return e instanceof o&&(e.id.length===1||e.id.length>0&&e.id.length<=2&&_(e.id,0)>=65536)},is_character_code:function(e){return e instanceof E&&!e.is_float&&e.value>=0&&e.value<=1114111},is_byte:function(e){return e instanceof E&&!e.is_float&&e.value>=0&&e.value<=255},is_operator:function(e){return e instanceof o&&i.arithmetic.evaluation[e.indicator]},is_directive:function(e){return e instanceof o&&i.directive[e.indicator]!==void 0},is_builtin:function(e){return e instanceof o&&i.predicate[e.indicator]!==void 0},is_error:function(e){return e instanceof o&&e.indicator==="throw/1"},is_predicate_indicator:function(e){return e instanceof o&&e.indicator==="//2"&&e.args[0]instanceof o&&e.args[0].args.length===0&&e.args[1]instanceof E&&e.args[1].is_float===!1},is_flag:function(e){return e instanceof o&&e.args.length===0&&i.flag[e.id]!==void 0},is_value_flag:function(e,n){if(!i.type.is_flag(e))return!1;for(var t in i.flag[e.id].allowed)if(!!i.flag[e.id].allowed.hasOwnProperty(t)&&i.flag[e.id].allowed[t].equals(n))return!0;return!1},is_io_mode:function(e){return i.type.is_atom(e)&&["read","write","append"].indexOf(e.id)!==-1},is_stream_option:function(e){return i.type.is_term(e)&&(e.indicator==="alias/1"&&i.type.is_atom(e.args[0])||e.indicator==="reposition/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="true"||e.args[0].id==="false")||e.indicator==="type/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="text"||e.args[0].id==="binary")||e.indicator==="eof_action/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="error"||e.args[0].id==="eof_code"||e.args[0].id==="reset"))},is_stream_position:function(e){return i.type.is_integer(e)&&e.value>=0||i.type.is_atom(e)&&(e.id==="end_of_stream"||e.id==="past_end_of_stream")},is_stream_property:function(e){return i.type.is_term(e)&&(e.indicator==="input/0"||e.indicator==="output/0"||e.indicator==="alias/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0]))||e.indicator==="file_name/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0]))||e.indicator==="position/1"&&(i.type.is_variable(e.args[0])||i.type.is_stream_position(e.args[0]))||e.indicator==="reposition/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0])&&(e.args[0].id==="true"||e.args[0].id==="false"))||e.indicator==="type/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0])&&(e.args[0].id==="text"||e.args[0].id==="binary"))||e.indicator==="mode/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0])&&(e.args[0].id==="read"||e.args[0].id==="write"||e.args[0].id==="append"))||e.indicator==="eof_action/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0])&&(e.args[0].id==="error"||e.args[0].id==="eof_code"||e.args[0].id==="reset"))||e.indicator==="end_of_stream/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0])&&(e.args[0].id==="at"||e.args[0].id==="past"||e.args[0].id==="not")))},is_streamable:function(e){return e.__proto__.stream!==void 0},is_read_option:function(e){return i.type.is_term(e)&&["variables/1","variable_names/1","singletons/1"].indexOf(e.indicator)!==-1},is_write_option:function(e){return i.type.is_term(e)&&(e.indicator==="quoted/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="true"||e.args[0].id==="false")||e.indicator==="ignore_ops/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="true"||e.args[0].id==="false")||e.indicator==="numbervars/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="true"||e.args[0].id==="false"))},is_close_option:function(e){return i.type.is_term(e)&&e.indicator==="force/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="true"||e.args[0].id==="false")},is_modifiable_flag:function(e){return i.type.is_flag(e)&&i.flag[e.id].changeable},is_module:function(e){return e instanceof o&&e.indicator==="library/1"&&e.args[0]instanceof o&&e.args[0].args.length===0&&i.module[e.args[0].id]!==void 0}},arithmetic:{evaluation:{"e/0":{type_args:null,type_result:!0,fn:function(e){return Math.E}},"pi/0":{type_args:null,type_result:!0,fn:function(e){return Math.PI}},"tau/0":{type_args:null,type_result:!0,fn:function(e){return 2*Math.PI}},"epsilon/0":{type_args:null,type_result:!0,fn:function(e){return Number.EPSILON}},"+/1":{type_args:null,type_result:null,fn:function(e,n){return e}},"-/1":{type_args:null,type_result:null,fn:function(e,n){return-e}},"\\/1":{type_args:!1,type_result:!1,fn:function(e,n){return~e}},"abs/1":{type_args:null,type_result:null,fn:function(e,n){return Math.abs(e)}},"sign/1":{type_args:null,type_result:null,fn:function(e,n){return Math.sign(e)}},"float_integer_part/1":{type_args:!0,type_result:!1,fn:function(e,n){return parseInt(e)}},"float_fractional_part/1":{type_args:!0,type_result:!0,fn:function(e,n){return e-parseInt(e)}},"float/1":{type_args:null,type_result:!0,fn:function(e,n){return parseFloat(e)}},"floor/1":{type_args:!0,type_result:!1,fn:function(e,n){return Math.floor(e)}},"truncate/1":{type_args:!0,type_result:!1,fn:function(e,n){return parseInt(e)}},"round/1":{type_args:!0,type_result:!1,fn:function(e,n){return Math.round(e)}},"ceiling/1":{type_args:!0,type_result:!1,fn:function(e,n){return Math.ceil(e)}},"sin/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.sin(e)}},"cos/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.cos(e)}},"tan/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.tan(e)}},"asin/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.asin(e)}},"acos/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.acos(e)}},"atan/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.atan(e)}},"atan2/2":{type_args:null,type_result:!0,fn:function(e,n,t){return Math.atan2(e,n)}},"exp/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.exp(e)}},"sqrt/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.sqrt(e)}},"log/1":{type_args:null,type_result:!0,fn:function(e,n){return e>0?Math.log(e):i.error.evaluation("undefined",n.__call_indicator)}},"+/2":{type_args:null,type_result:null,fn:function(e,n,t){return e+n}},"-/2":{type_args:null,type_result:null,fn:function(e,n,t){return e-n}},"*/2":{type_args:null,type_result:null,fn:function(e,n,t){return e*n}},"//2":{type_args:null,type_result:!0,fn:function(e,n,t){return n?e/n:i.error.evaluation("zero_division",t.__call_indicator)}},"///2":{type_args:!1,type_result:!1,fn:function(e,n,t){return n?parseInt(e/n):i.error.evaluation("zero_division",t.__call_indicator)}},"**/2":{type_args:null,type_result:!0,fn:function(e,n,t){return Math.pow(e,n)}},"^/2":{type_args:null,type_result:null,fn:function(e,n,t){return Math.pow(e,n)}},"<>/2":{type_args:!1,type_result:!1,fn:function(e,n,t){return e>>n}},"/\\/2":{type_args:!1,type_result:!1,fn:function(e,n,t){return e&n}},"\\//2":{type_args:!1,type_result:!1,fn:function(e,n,t){return e|n}},"xor/2":{type_args:!1,type_result:!1,fn:function(e,n,t){return e^n}},"rem/2":{type_args:!1,type_result:!1,fn:function(e,n,t){return n?e%n:i.error.evaluation("zero_division",t.__call_indicator)}},"mod/2":{type_args:!1,type_result:!1,fn:function(e,n,t){return n?e-parseInt(e/n)*n:i.error.evaluation("zero_division",t.__call_indicator)}},"max/2":{type_args:null,type_result:null,fn:function(e,n,t){return Math.max(e,n)}},"min/2":{type_args:null,type_result:null,fn:function(e,n,t){return Math.min(e,n)}}}},directive:{"dynamic/1":function(e,n){var t=n.args[0];if(i.type.is_variable(t))e.throw_error(i.error.instantiation(n.indicator));else if(!i.type.is_compound(t)||t.indicator!=="//2")e.throw_error(i.error.type("predicate_indicator",t,n.indicator));else if(i.type.is_variable(t.args[0])||i.type.is_variable(t.args[1]))e.throw_error(i.error.instantiation(n.indicator));else if(!i.type.is_atom(t.args[0]))e.throw_error(i.error.type("atom",t.args[0],n.indicator));else if(!i.type.is_integer(t.args[1]))e.throw_error(i.error.type("integer",t.args[1],n.indicator));else{var s=n.args[0].args[0].id+"/"+n.args[0].args[1].value;e.session.public_predicates[s]=!0,e.session.rules[s]||(e.session.rules[s]=[])}},"multifile/1":function(e,n){var t=n.args[0];i.type.is_variable(t)?e.throw_error(i.error.instantiation(n.indicator)):!i.type.is_compound(t)||t.indicator!=="//2"?e.throw_error(i.error.type("predicate_indicator",t,n.indicator)):i.type.is_variable(t.args[0])||i.type.is_variable(t.args[1])?e.throw_error(i.error.instantiation(n.indicator)):i.type.is_atom(t.args[0])?i.type.is_integer(t.args[1])?e.session.multifile_predicates[n.args[0].args[0].id+"/"+n.args[0].args[1].value]=!0:e.throw_error(i.error.type("integer",t.args[1],n.indicator)):e.throw_error(i.error.type("atom",t.args[0],n.indicator))},"set_prolog_flag/2":function(e,n){var t=n.args[0],s=n.args[1];i.type.is_variable(t)||i.type.is_variable(s)?e.throw_error(i.error.instantiation(n.indicator)):i.type.is_atom(t)?i.type.is_flag(t)?i.type.is_value_flag(t,s)?i.type.is_modifiable_flag(t)?e.session.flag[t.id]=s:e.throw_error(i.error.permission("modify","flag",t)):e.throw_error(i.error.domain("flag_value",new o("+",[t,s]),n.indicator)):e.throw_error(i.error.domain("prolog_flag",t,n.indicator)):e.throw_error(i.error.type("atom",t,n.indicator))},"use_module/1":function(e,n){var t=n.args[0];if(i.type.is_variable(t))e.throw_error(i.error.instantiation(n.indicator));else if(!i.type.is_term(t))e.throw_error(i.error.type("term",t,n.indicator));else if(i.type.is_module(t)){var s=t.args[0].id;u(e.session.modules,s)===-1&&e.session.modules.push(s)}},"char_conversion/2":function(e,n){var t=n.args[0],s=n.args[1];i.type.is_variable(t)||i.type.is_variable(s)?e.throw_error(i.error.instantiation(n.indicator)):i.type.is_character(t)?i.type.is_character(s)?t.id===s.id?delete e.session.__char_conversion[t.id]:e.session.__char_conversion[t.id]=s.id:e.throw_error(i.error.type("character",s,n.indicator)):e.throw_error(i.error.type("character",t,n.indicator))},"op/3":function(e,n){var t=n.args[0],s=n.args[1],a=n.args[2];if(i.type.is_variable(t)||i.type.is_variable(s)||i.type.is_variable(a))e.throw_error(i.error.instantiation(n.indicator));else if(!i.type.is_integer(t))e.throw_error(i.error.type("integer",t,n.indicator));else if(!i.type.is_atom(s))e.throw_error(i.error.type("atom",s,n.indicator));else if(!i.type.is_atom(a))e.throw_error(i.error.type("atom",a,n.indicator));else if(t.value<0||t.value>1200)e.throw_error(i.error.domain("operator_priority",t,n.indicator));else if(a.id===",")e.throw_error(i.error.permission("modify","operator",a,n.indicator));else if(a.id==="|"&&(t.value<1001||s.id.length!==3))e.throw_error(i.error.permission("modify","operator",a,n.indicator));else if(["fy","fx","yf","xf","xfx","yfx","xfy"].indexOf(s.id)===-1)e.throw_error(i.error.domain("operator_specifier",s,n.indicator));else{var l={prefix:null,infix:null,postfix:null};for(var f in e.session.__operators)if(!!e.session.__operators.hasOwnProperty(f)){var y=e.session.__operators[f][a.id];y&&(u(y,"fx")!==-1&&(l.prefix={priority:f,type:"fx"}),u(y,"fy")!==-1&&(l.prefix={priority:f,type:"fy"}),u(y,"xf")!==-1&&(l.postfix={priority:f,type:"xf"}),u(y,"yf")!==-1&&(l.postfix={priority:f,type:"yf"}),u(y,"xfx")!==-1&&(l.infix={priority:f,type:"xfx"}),u(y,"xfy")!==-1&&(l.infix={priority:f,type:"xfy"}),u(y,"yfx")!==-1&&(l.infix={priority:f,type:"yfx"}))}var d;switch(s.id){case"fy":case"fx":d="prefix";break;case"yf":case"xf":d="postfix";break;default:d="infix";break}if(((l.prefix&&d==="prefix"||l.postfix&&d==="postfix"||l.infix&&d==="infix")&&l[d].type!==s.id||l.infix&&d==="postfix"||l.postfix&&d==="infix")&&t.value!==0)e.throw_error(i.error.permission("create","operator",a,n.indicator));else return l[d]&&(Fi(e.session.__operators[l[d].priority][a.id],s.id),e.session.__operators[l[d].priority][a.id].length===0&&delete e.session.__operators[l[d].priority][a.id]),t.value>0&&(e.session.__operators[t.value]||(e.session.__operators[t.value.toString()]={}),e.session.__operators[t.value][a.id]||(e.session.__operators[t.value][a.id]=[]),e.session.__operators[t.value][a.id].push(s.id)),!0}}},predicate:{"op/3":function(e,n,t){i.directive["op/3"](e,t)&&e.success(n)},"current_op/3":function(e,n,t){var s=t.args[0],a=t.args[1],l=t.args[2],f=[];for(var y in e.session.__operators)for(var d in e.session.__operators[y])for(var m=0;m/2"){var s=e.points,a=e.session.format_success,l=e.session.format_error;e.session.format_success=function(m){return m.substitution},e.session.format_error=function(m){return m.goal},e.points=[new V(t.args[0].args[0],n.substitution,n)];var f=function(m){e.points=s,e.session.format_success=a,e.session.format_error=l,m===!1?e.prepend([new V(n.goal.replace(t.args[1]),n.substitution,n)]):i.type.is_error(m)?e.throw_error(m.args[0]):m===null?(e.prepend([n]),e.__calls.shift()(null)):e.prepend([new V(n.goal.replace(t.args[0].args[1]).apply(m),n.substitution.apply(m),n)])};e.__calls.unshift(f)}else{var y=new V(n.goal.replace(t.args[0]),n.substitution,n),d=new V(n.goal.replace(t.args[1]),n.substitution,n);e.prepend([y,d])}},"!/0":function(e,n,t){var s,a,l=[];for(s=n,a=null;s.parent!==null&&s.parent.goal.search(t);)if(a=s,s=s.parent,s.goal!==null){var f=s.goal.select();if(f&&f.id==="call"&&f.search(t)){s=a;break}}for(var y=e.points.length-1;y>=0;y--){for(var d=e.points[y],m=d.parent;m!==null&&m!==s.parent;)m=m.parent;m===null&&m!==s.parent&&l.push(d)}e.points=l.reverse(),e.success(n)},"\\+/1":function(e,n,t){var s=t.args[0];i.type.is_variable(s)?e.throw_error(i.error.instantiation(e.level)):i.type.is_callable(s)?e.prepend([new V(n.goal.replace(new o(",",[new o(",",[new o("call",[s]),new o("!",[])]),new o("fail",[])])),n.substitution,n),new V(n.goal.replace(null),n.substitution,n)]):e.throw_error(i.error.type("callable",s,e.level))},"->/2":function(e,n,t){var s=n.goal.replace(new o(",",[t.args[0],new o(",",[new o("!"),t.args[1]])]));e.prepend([new V(s,n.substitution,n)])},"fail/0":function(e,n,t){},"false/0":function(e,n,t){},"true/0":function(e,n,t){e.success(n)},"call/1":ye(1),"call/2":ye(2),"call/3":ye(3),"call/4":ye(4),"call/5":ye(5),"call/6":ye(6),"call/7":ye(7),"call/8":ye(8),"once/1":function(e,n,t){var s=t.args[0];e.prepend([new V(n.goal.replace(new o(",",[new o("call",[s]),new o("!",[])])),n.substitution,n)])},"forall/2":function(e,n,t){var s=t.args[0],a=t.args[1];e.prepend([new V(n.goal.replace(new o("\\+",[new o(",",[new o("call",[s]),new o("\\+",[new o("call",[a])])])])),n.substitution,n)])},"repeat/0":function(e,n,t){e.prepend([new V(n.goal.replace(null),n.substitution,n),n])},"throw/1":function(e,n,t){i.type.is_variable(t.args[0])?e.throw_error(i.error.instantiation(e.level)):e.throw_error(t.args[0])},"catch/3":function(e,n,t){var s=e.points;e.points=[],e.prepend([new V(t.args[0],n.substitution,n)]);var a=e.session.format_success,l=e.session.format_error;e.session.format_success=function(y){return y.substitution},e.session.format_error=function(y){return y.goal};var f=function(y){var d=e.points;if(e.points=s,e.session.format_success=a,e.session.format_error=l,i.type.is_error(y)){for(var m=[],S=e.points.length-1;S>=0;S--){for(var R=e.points[S],P=R.parent;P!==null&&P!==n.parent;)P=P.parent;P===null&&P!==n.parent&&m.push(R)}e.points=m;var A=e.get_flag("occurs_check").indicator==="true/0",R=new V,k=i.unify(y.args[0],t.args[1],A);k!==null?(R.substitution=n.substitution.apply(k),R.goal=n.goal.replace(t.args[2]).apply(k),R.parent=n,e.prepend([R])):e.throw_error(y.args[0])}else if(y!==!1){for(var L=y===null?[]:[new V(n.goal.apply(y).replace(null),n.substitution.apply(y),n)],B=[],S=d.length-1;S>=0;S--){B.push(d[S]);var q=d[S].goal!==null?d[S].goal.select():null;if(i.type.is_term(q)&&q.indicator==="!/0")break}var F=c(B,function(H){return H.goal===null&&(H.goal=new o("true",[])),H=new V(n.goal.replace(new o("catch",[H.goal,t.args[1],t.args[2]])),n.substitution.apply(H.substitution),H.parent),H.exclude=t.args[0].variables(),H}).reverse();e.prepend(F),e.prepend(L),y===null&&(this.current_limit=0,e.__calls.shift()(null))}};e.__calls.unshift(f)},"=/2":function(e,n,t){var s=e.get_flag("occurs_check").indicator==="true/0",a=new V,l=i.unify(t.args[0],t.args[1],s);l!==null&&(a.goal=n.goal.apply(l).replace(null),a.substitution=n.substitution.apply(l),a.parent=n,e.prepend([a]))},"unify_with_occurs_check/2":function(e,n,t){var s=new V,a=i.unify(t.args[0],t.args[1],!0);a!==null&&(s.goal=n.goal.apply(a).replace(null),s.substitution=n.substitution.apply(a),s.parent=n,e.prepend([s]))},"\\=/2":function(e,n,t){var s=e.get_flag("occurs_check").indicator==="true/0",a=i.unify(t.args[0],t.args[1],s);a===null&&e.success(n)},"subsumes_term/2":function(e,n,t){var s=e.get_flag("occurs_check").indicator==="true/0",a=i.unify(t.args[1],t.args[0],s);a!==null&&t.args[1].apply(a).equals(t.args[1])&&e.success(n)},"findall/3":function(e,n,t){var s=t.args[0],a=t.args[1],l=t.args[2];if(i.type.is_variable(a))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(a))e.throw_error(i.error.type("callable",a,t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_list(l))e.throw_error(i.error.type("list",l,t.indicator));else{var f=e.next_free_variable(),y=new o(",",[a,new o("=",[f,s])]),d=e.points,m=e.session.limit,S=e.session.format_success;e.session.format_success=function(R){return R.substitution},e.add_goal(y,!0,n);var P=[],A=function(R){if(R!==!1&&R!==null&&!i.type.is_error(R))e.__calls.unshift(A),P.push(R.links[f.id]),e.session.limit=e.current_limit;else if(e.points=d,e.session.limit=m,e.session.format_success=S,i.type.is_error(R))e.throw_error(R.args[0]);else if(e.current_limit>0){for(var k=new o("[]"),L=P.length-1;L>=0;L--)k=new o(".",[P[L],k]);e.prepend([new V(n.goal.replace(new o("=",[l,k])),n.substitution,n)])}};e.__calls.unshift(A)}},"bagof/3":function(e,n,t){var s,a=t.args[0],l=t.args[1],f=t.args[2];if(i.type.is_variable(l))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(l))e.throw_error(i.error.type("callable",l,t.indicator));else if(!i.type.is_variable(f)&&!i.type.is_list(f))e.throw_error(i.error.type("list",f,t.indicator));else{var y=e.next_free_variable(),d;l.indicator==="^/2"?(d=l.args[0].variables(),l=l.args[1]):d=[],d=d.concat(a.variables());for(var m=l.variables().filter(function(F){return u(d,F)===-1}),S=new o("[]"),P=m.length-1;P>=0;P--)S=new o(".",[new O(m[P]),S]);var A=new o(",",[l,new o("=",[y,new o(",",[S,a])])]),R=e.points,k=e.session.limit,L=e.session.format_success;e.session.format_success=function(F){return F.substitution},e.add_goal(A,!0,n);var B=[],q=function(F){if(F!==!1&&F!==null&&!i.type.is_error(F)){e.__calls.unshift(q);var H=!1,J=F.links[y.id].args[0],me=F.links[y.id].args[1];for(var be in B)if(!!B.hasOwnProperty(be)){var Me=B[be];if(Me.variables.equals(J)){Me.answers.push(me),H=!0;break}}H||B.push({variables:J,answers:[me]}),e.session.limit=e.current_limit}else if(e.points=R,e.session.limit=k,e.session.format_success=L,i.type.is_error(F))e.throw_error(F.args[0]);else if(e.current_limit>0){for(var qe=[],ce=0;ce=0;xe--)Te=new o(".",[F[xe],Te]);qe.push(new V(n.goal.replace(new o(",",[new o("=",[S,B[ce].variables]),new o("=",[f,Te])])),n.substitution,n))}e.prepend(qe)}};e.__calls.unshift(q)}},"setof/3":function(e,n,t){var s,a=t.args[0],l=t.args[1],f=t.args[2];if(i.type.is_variable(l))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(l))e.throw_error(i.error.type("callable",l,t.indicator));else if(!i.type.is_variable(f)&&!i.type.is_list(f))e.throw_error(i.error.type("list",f,t.indicator));else{var y=e.next_free_variable(),d;l.indicator==="^/2"?(d=l.args[0].variables(),l=l.args[1]):d=[],d=d.concat(a.variables());for(var m=l.variables().filter(function(F){return u(d,F)===-1}),S=new o("[]"),P=m.length-1;P>=0;P--)S=new o(".",[new O(m[P]),S]);var A=new o(",",[l,new o("=",[y,new o(",",[S,a])])]),R=e.points,k=e.session.limit,L=e.session.format_success;e.session.format_success=function(F){return F.substitution},e.add_goal(A,!0,n);var B=[],q=function(F){if(F!==!1&&F!==null&&!i.type.is_error(F)){e.__calls.unshift(q);var H=!1,J=F.links[y.id].args[0],me=F.links[y.id].args[1];for(var be in B)if(!!B.hasOwnProperty(be)){var Me=B[be];if(Me.variables.equals(J)){Me.answers.push(me),H=!0;break}}H||B.push({variables:J,answers:[me]}),e.session.limit=e.current_limit}else if(e.points=R,e.session.limit=k,e.session.format_success=L,i.type.is_error(F))e.throw_error(F.args[0]);else if(e.current_limit>0){for(var qe=[],ce=0;ce=0;xe--)Te=new o(".",[F[xe],Te]);qe.push(new V(n.goal.replace(new o(",",[new o("=",[S,B[ce].variables]),new o("=",[f,Te])])),n.substitution,n))}e.prepend(qe)}};e.__calls.unshift(q)}},"functor/3":function(e,n,t){var s,a=t.args[0],l=t.args[1],f=t.args[2];if(i.type.is_variable(a)&&(i.type.is_variable(l)||i.type.is_variable(f)))e.throw_error(i.error.instantiation("functor/3"));else if(!i.type.is_variable(f)&&!i.type.is_integer(f))e.throw_error(i.error.type("integer",t.args[2],"functor/3"));else if(!i.type.is_variable(l)&&!i.type.is_atomic(l))e.throw_error(i.error.type("atomic",t.args[1],"functor/3"));else if(i.type.is_integer(l)&&i.type.is_integer(f)&&f.value!==0)e.throw_error(i.error.type("atom",t.args[1],"functor/3"));else if(i.type.is_variable(a)){if(t.args[2].value>=0){for(var y=[],d=0;d0&&s<=t.args[1].args.length){var a=new o("=",[t.args[1].args[s-1],t.args[2]]);e.prepend([new V(n.goal.replace(a),n.substitution,n)])}}},"=../2":function(e,n,t){var s;if(i.type.is_variable(t.args[0])&&(i.type.is_variable(t.args[1])||i.type.is_non_empty_list(t.args[1])&&i.type.is_variable(t.args[1].args[0])))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_fully_list(t.args[1]))e.throw_error(i.error.type("list",t.args[1],t.indicator));else if(i.type.is_variable(t.args[0])){if(!i.type.is_variable(t.args[1])){var l=[];for(s=t.args[1].args[1];s.indicator==="./2";)l.push(s.args[0]),s=s.args[1];i.type.is_variable(t.args[0])&&i.type.is_variable(s)?e.throw_error(i.error.instantiation(t.indicator)):l.length===0&&i.type.is_compound(t.args[1].args[0])?e.throw_error(i.error.type("atomic",t.args[1].args[0],t.indicator)):l.length>0&&(i.type.is_compound(t.args[1].args[0])||i.type.is_number(t.args[1].args[0]))?e.throw_error(i.error.type("atom",t.args[1].args[0],t.indicator)):l.length===0?e.prepend([new V(n.goal.replace(new o("=",[t.args[1].args[0],t.args[0]],n)),n.substitution,n)]):e.prepend([new V(n.goal.replace(new o("=",[new o(t.args[1].args[0].id,l),t.args[0]])),n.substitution,n)])}}else{if(i.type.is_atomic(t.args[0]))s=new o(".",[t.args[0],new o("[]")]);else{s=new o("[]");for(var a=t.args[0].args.length-1;a>=0;a--)s=new o(".",[t.args[0].args[a],s]);s=new o(".",[new o(t.args[0].id),s])}e.prepend([new V(n.goal.replace(new o("=",[s,t.args[1]])),n.substitution,n)])}},"copy_term/2":function(e,n,t){var s=t.args[0].rename(e);e.prepend([new V(n.goal.replace(new o("=",[s,t.args[1]])),n.substitution,n.parent)])},"term_variables/2":function(e,n,t){var s=t.args[0],a=t.args[1];if(!i.type.is_fully_list(a))e.throw_error(i.error.type("list",a,t.indicator));else{var l=he(c(yr(s.variables()),function(f){return new O(f)}));e.prepend([new V(n.goal.replace(new o("=",[a,l])),n.substitution,n)])}},"clause/2":function(e,n,t){if(i.type.is_variable(t.args[0]))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(t.args[0]))e.throw_error(i.error.type("callable",t.args[0],t.indicator));else if(!i.type.is_variable(t.args[1])&&!i.type.is_callable(t.args[1]))e.throw_error(i.error.type("callable",t.args[1],t.indicator));else if(e.session.rules[t.args[0].indicator]!==void 0)if(e.is_public_predicate(t.args[0].indicator)){var s=[];for(var a in e.session.rules[t.args[0].indicator])if(!!e.session.rules[t.args[0].indicator].hasOwnProperty(a)){var l=e.session.rules[t.args[0].indicator][a];e.session.renamed_variables={},l=l.rename(e),l.body===null&&(l.body=new o("true"));var f=new o(",",[new o("=",[l.head,t.args[0]]),new o("=",[l.body,t.args[1]])]);s.push(new V(n.goal.replace(f),n.substitution,n))}e.prepend(s)}else e.throw_error(i.error.permission("access","private_procedure",t.args[0].indicator,t.indicator))},"current_predicate/1":function(e,n,t){var s=t.args[0];if(!i.type.is_variable(s)&&(!i.type.is_compound(s)||s.indicator!=="//2"))e.throw_error(i.error.type("predicate_indicator",s,t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_variable(s.args[0])&&!i.type.is_atom(s.args[0]))e.throw_error(i.error.type("atom",s.args[0],t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_variable(s.args[1])&&!i.type.is_integer(s.args[1]))e.throw_error(i.error.type("integer",s.args[1],t.indicator));else{var a=[];for(var l in e.session.rules)if(!!e.session.rules.hasOwnProperty(l)){var f=l.lastIndexOf("/"),y=l.substr(0,f),d=parseInt(l.substr(f+1,l.length-(f+1))),m=new o("/",[new o(y),new E(d,!1)]),S=new o("=",[m,s]);a.push(new V(n.goal.replace(S),n.substitution,n))}e.prepend(a)}},"asserta/1":function(e,n,t){if(i.type.is_variable(t.args[0]))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(t.args[0]))e.throw_error(i.error.type("callable",t.args[0],t.indicator));else{var s,a;t.args[0].indicator===":-/2"?(s=t.args[0].args[0],a=ve(t.args[0].args[1])):(s=t.args[0],a=null),i.type.is_callable(s)?a!==null&&!i.type.is_callable(a)?e.throw_error(i.error.type("callable",a,t.indicator)):e.is_public_predicate(s.indicator)?(e.session.rules[s.indicator]===void 0&&(e.session.rules[s.indicator]=[]),e.session.public_predicates[s.indicator]=!0,e.session.rules[s.indicator]=[new Q(s,a,!0)].concat(e.session.rules[s.indicator]),e.success(n)):e.throw_error(i.error.permission("modify","static_procedure",s.indicator,t.indicator)):e.throw_error(i.error.type("callable",s,t.indicator))}},"assertz/1":function(e,n,t){if(i.type.is_variable(t.args[0]))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(t.args[0]))e.throw_error(i.error.type("callable",t.args[0],t.indicator));else{var s,a;t.args[0].indicator===":-/2"?(s=t.args[0].args[0],a=ve(t.args[0].args[1])):(s=t.args[0],a=null),i.type.is_callable(s)?a!==null&&!i.type.is_callable(a)?e.throw_error(i.error.type("callable",a,t.indicator)):e.is_public_predicate(s.indicator)?(e.session.rules[s.indicator]===void 0&&(e.session.rules[s.indicator]=[]),e.session.public_predicates[s.indicator]=!0,e.session.rules[s.indicator].push(new Q(s,a,!0)),e.success(n)):e.throw_error(i.error.permission("modify","static_procedure",s.indicator,t.indicator)):e.throw_error(i.error.type("callable",s,t.indicator))}},"retract/1":function(e,n,t){if(i.type.is_variable(t.args[0]))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(t.args[0]))e.throw_error(i.error.type("callable",t.args[0],t.indicator));else{var s,a;if(t.args[0].indicator===":-/2"?(s=t.args[0].args[0],a=t.args[0].args[1]):(s=t.args[0],a=new o("true")),typeof n.retract=="undefined")if(e.is_public_predicate(s.indicator)){if(e.session.rules[s.indicator]!==void 0){for(var l=[],f=0;fe.get_flag("max_arity").value)e.throw_error(i.error.representation("max_arity",t.indicator));else{var s=t.args[0].args[0].id+"/"+t.args[0].args[1].value;e.is_public_predicate(s)?(delete e.session.rules[s],e.success(n)):e.throw_error(i.error.permission("modify","static_procedure",s,t.indicator))}},"atom_length/2":function(e,n,t){if(i.type.is_variable(t.args[0]))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_atom(t.args[0]))e.throw_error(i.error.type("atom",t.args[0],t.indicator));else if(!i.type.is_variable(t.args[1])&&!i.type.is_integer(t.args[1]))e.throw_error(i.error.type("integer",t.args[1],t.indicator));else if(i.type.is_integer(t.args[1])&&t.args[1].value<0)e.throw_error(i.error.domain("not_less_than_zero",t.args[1],t.indicator));else{var s=new E(t.args[0].id.length,!1);e.prepend([new V(n.goal.replace(new o("=",[s,t.args[1]])),n.substitution,n)])}},"atom_concat/3":function(e,n,t){var s,a,l=t.args[0],f=t.args[1],y=t.args[2];if(i.type.is_variable(y)&&(i.type.is_variable(l)||i.type.is_variable(f)))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_atom(l))e.throw_error(i.error.type("atom",l,t.indicator));else if(!i.type.is_variable(f)&&!i.type.is_atom(f))e.throw_error(i.error.type("atom",f,t.indicator));else if(!i.type.is_variable(y)&&!i.type.is_atom(y))e.throw_error(i.error.type("atom",y,t.indicator));else{var d=i.type.is_variable(l),m=i.type.is_variable(f);if(!d&&!m)a=new o("=",[y,new o(l.id+f.id)]),e.prepend([new V(n.goal.replace(a),n.substitution,n)]);else if(d&&!m)s=y.id.substr(0,y.id.length-f.id.length),s+f.id===y.id&&(a=new o("=",[l,new o(s)]),e.prepend([new V(n.goal.replace(a),n.substitution,n)]));else if(m&&!d)s=y.id.substr(l.id.length),l.id+s===y.id&&(a=new o("=",[f,new o(s)]),e.prepend([new V(n.goal.replace(a),n.substitution,n)]));else{for(var S=[],P=0;P<=y.id.length;P++){var A=new o(y.id.substr(0,P)),R=new o(y.id.substr(P));a=new o(",",[new o("=",[A,l]),new o("=",[R,f])]),S.push(new V(n.goal.replace(a),n.substitution,n))}e.prepend(S)}}},"sub_atom/5":function(e,n,t){var s,a=t.args[0],l=t.args[1],f=t.args[2],y=t.args[3],d=t.args[4];if(i.type.is_variable(a))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_integer(l))e.throw_error(i.error.type("integer",l,t.indicator));else if(!i.type.is_variable(f)&&!i.type.is_integer(f))e.throw_error(i.error.type("integer",f,t.indicator));else if(!i.type.is_variable(y)&&!i.type.is_integer(y))e.throw_error(i.error.type("integer",y,t.indicator));else if(i.type.is_integer(l)&&l.value<0)e.throw_error(i.error.domain("not_less_than_zero",l,t.indicator));else if(i.type.is_integer(f)&&f.value<0)e.throw_error(i.error.domain("not_less_than_zero",f,t.indicator));else if(i.type.is_integer(y)&&y.value<0)e.throw_error(i.error.domain("not_less_than_zero",y,t.indicator));else{var m=[],S=[],P=[];if(i.type.is_variable(l))for(s=0;s<=a.id.length;s++)m.push(s);else m.push(l.value);if(i.type.is_variable(f))for(s=0;s<=a.id.length;s++)S.push(s);else S.push(f.value);if(i.type.is_variable(y))for(s=0;s<=a.id.length;s++)P.push(s);else P.push(y.value);var A=[];for(var R in m)if(!!m.hasOwnProperty(R)){s=m[R];for(var k in S)if(!!S.hasOwnProperty(k)){var L=S[k],B=a.id.length-s-L;if(u(P,B)!==-1&&s+L+B===a.id.length){var q=a.id.substr(s,L);if(a.id===a.id.substr(0,s)+q+a.id.substr(s+L,B)){var F=new o("=",[new o(q),d]),H=new o("=",[l,new E(s)]),J=new o("=",[f,new E(L)]),me=new o("=",[y,new E(B)]),be=new o(",",[new o(",",[new o(",",[H,J]),me]),F]);A.push(new V(n.goal.replace(be),n.substitution,n))}}}}e.prepend(A)}},"atom_chars/2":function(e,n,t){var s=t.args[0],a=t.args[1];if(i.type.is_variable(s)&&i.type.is_variable(a))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_atom(s))e.throw_error(i.error.type("atom",s,t.indicator));else if(i.type.is_variable(s)){for(var y=a,d=i.type.is_variable(s),m="";y.indicator==="./2";){if(i.type.is_character(y.args[0]))m+=y.args[0].id;else if(i.type.is_variable(y.args[0])&&d){e.throw_error(i.error.instantiation(t.indicator));return}else if(!i.type.is_variable(y.args[0])){e.throw_error(i.error.type("character",y.args[0],t.indicator));return}y=y.args[1]}i.type.is_variable(y)&&d?e.throw_error(i.error.instantiation(t.indicator)):!i.type.is_empty_list(y)&&!i.type.is_variable(y)?e.throw_error(i.error.type("list",a,t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[new o(m),s])),n.substitution,n)])}else{for(var l=new o("[]"),f=s.id.length-1;f>=0;f--)l=new o(".",[new o(s.id.charAt(f)),l]);e.prepend([new V(n.goal.replace(new o("=",[a,l])),n.substitution,n)])}},"atom_codes/2":function(e,n,t){var s=t.args[0],a=t.args[1];if(i.type.is_variable(s)&&i.type.is_variable(a))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_atom(s))e.throw_error(i.error.type("atom",s,t.indicator));else if(i.type.is_variable(s)){for(var y=a,d=i.type.is_variable(s),m="";y.indicator==="./2";){if(i.type.is_character_code(y.args[0]))m+=v(y.args[0].value);else if(i.type.is_variable(y.args[0])&&d){e.throw_error(i.error.instantiation(t.indicator));return}else if(!i.type.is_variable(y.args[0])){e.throw_error(i.error.representation("character_code",t.indicator));return}y=y.args[1]}i.type.is_variable(y)&&d?e.throw_error(i.error.instantiation(t.indicator)):!i.type.is_empty_list(y)&&!i.type.is_variable(y)?e.throw_error(i.error.type("list",a,t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[new o(m),s])),n.substitution,n)])}else{for(var l=new o("[]"),f=s.id.length-1;f>=0;f--)l=new o(".",[new E(_(s.id,f),!1),l]);e.prepend([new V(n.goal.replace(new o("=",[a,l])),n.substitution,n)])}},"char_code/2":function(e,n,t){var s=t.args[0],a=t.args[1];if(i.type.is_variable(s)&&i.type.is_variable(a))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_character(s))e.throw_error(i.error.type("character",s,t.indicator));else if(!i.type.is_variable(a)&&!i.type.is_integer(a))e.throw_error(i.error.type("integer",a,t.indicator));else if(!i.type.is_variable(a)&&!i.type.is_character_code(a))e.throw_error(i.error.representation("character_code",t.indicator));else if(i.type.is_variable(a)){var l=new E(_(s.id,0),!1);e.prepend([new V(n.goal.replace(new o("=",[l,a])),n.substitution,n)])}else{var f=new o(v(a.value));e.prepend([new V(n.goal.replace(new o("=",[f,s])),n.substitution,n)])}},"number_chars/2":function(e,n,t){var s,a=t.args[0],l=t.args[1];if(i.type.is_variable(a)&&i.type.is_variable(l))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(a)&&!i.type.is_number(a))e.throw_error(i.error.type("number",a,t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_list(l))e.throw_error(i.error.type("list",l,t.indicator));else{var f=i.type.is_variable(a);if(!i.type.is_variable(l)){var y=l,d=!0;for(s="";y.indicator==="./2";){if(i.type.is_character(y.args[0]))s+=y.args[0].id;else if(i.type.is_variable(y.args[0]))d=!1;else if(!i.type.is_variable(y.args[0])){e.throw_error(i.error.type("character",y.args[0],t.indicator));return}y=y.args[1]}if(d=d&&i.type.is_empty_list(y),!i.type.is_empty_list(y)&&!i.type.is_variable(y)){e.throw_error(i.error.type("list",l,t.indicator));return}if(!d&&f){e.throw_error(i.error.instantiation(t.indicator));return}else if(d)if(i.type.is_variable(y)&&f){e.throw_error(i.error.instantiation(t.indicator));return}else{var m=e.parse(s),S=m.value;!i.type.is_number(S)||m.tokens[m.tokens.length-1].space?e.throw_error(i.error.syntax_by_predicate("parseable_number",t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[a,S])),n.substitution,n)]);return}}if(!f){s=a.toString();for(var P=new o("[]"),A=s.length-1;A>=0;A--)P=new o(".",[new o(s.charAt(A)),P]);e.prepend([new V(n.goal.replace(new o("=",[l,P])),n.substitution,n)])}}},"number_codes/2":function(e,n,t){var s,a=t.args[0],l=t.args[1];if(i.type.is_variable(a)&&i.type.is_variable(l))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(a)&&!i.type.is_number(a))e.throw_error(i.error.type("number",a,t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_list(l))e.throw_error(i.error.type("list",l,t.indicator));else{var f=i.type.is_variable(a);if(!i.type.is_variable(l)){var y=l,d=!0;for(s="";y.indicator==="./2";){if(i.type.is_character_code(y.args[0]))s+=v(y.args[0].value);else if(i.type.is_variable(y.args[0]))d=!1;else if(!i.type.is_variable(y.args[0])){e.throw_error(i.error.type("character_code",y.args[0],t.indicator));return}y=y.args[1]}if(d=d&&i.type.is_empty_list(y),!i.type.is_empty_list(y)&&!i.type.is_variable(y)){e.throw_error(i.error.type("list",l,t.indicator));return}if(!d&&f){e.throw_error(i.error.instantiation(t.indicator));return}else if(d)if(i.type.is_variable(y)&&f){e.throw_error(i.error.instantiation(t.indicator));return}else{var m=e.parse(s),S=m.value;!i.type.is_number(S)||m.tokens[m.tokens.length-1].space?e.throw_error(i.error.syntax_by_predicate("parseable_number",t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[a,S])),n.substitution,n)]);return}}if(!f){s=a.toString();for(var P=new o("[]"),A=s.length-1;A>=0;A--)P=new o(".",[new E(_(s,A),!1),P]);e.prepend([new V(n.goal.replace(new o("=",[l,P])),n.substitution,n)])}}},"upcase_atom/2":function(e,n,t){var s=t.args[0],a=t.args[1];i.type.is_variable(s)?e.throw_error(i.error.instantiation(t.indicator)):i.type.is_atom(s)?!i.type.is_variable(a)&&!i.type.is_atom(a)?e.throw_error(i.error.type("atom",a,t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[a,new o(s.id.toUpperCase(),[])])),n.substitution,n)]):e.throw_error(i.error.type("atom",s,t.indicator))},"downcase_atom/2":function(e,n,t){var s=t.args[0],a=t.args[1];i.type.is_variable(s)?e.throw_error(i.error.instantiation(t.indicator)):i.type.is_atom(s)?!i.type.is_variable(a)&&!i.type.is_atom(a)?e.throw_error(i.error.type("atom",a,t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[a,new o(s.id.toLowerCase(),[])])),n.substitution,n)]):e.throw_error(i.error.type("atom",s,t.indicator))},"atomic_list_concat/2":function(e,n,t){var s=t.args[0],a=t.args[1];e.prepend([new V(n.goal.replace(new o("atomic_list_concat",[s,new o("",[]),a])),n.substitution,n)])},"atomic_list_concat/3":function(e,n,t){var s=t.args[0],a=t.args[1],l=t.args[2];if(i.type.is_variable(a)||i.type.is_variable(s)&&i.type.is_variable(l))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_list(s))e.throw_error(i.error.type("list",s,t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_atom(l))e.throw_error(i.error.type("atom",l,t.indicator));else if(i.type.is_variable(l)){for(var y="",d=s;i.type.is_term(d)&&d.indicator==="./2";){if(!i.type.is_atom(d.args[0])&&!i.type.is_number(d.args[0])){e.throw_error(i.error.type("atomic",d.args[0],t.indicator));return}y!==""&&(y+=a.id),i.type.is_atom(d.args[0])?y+=d.args[0].id:y+=""+d.args[0].value,d=d.args[1]}y=new o(y,[]),i.type.is_variable(d)?e.throw_error(i.error.instantiation(t.indicator)):!i.type.is_term(d)||d.indicator!=="[]/0"?e.throw_error(i.error.type("list",s,t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[y,l])),n.substitution,n)])}else{var f=he(c(l.id.split(a.id),function(m){return new o(m,[])}));e.prepend([new V(n.goal.replace(new o("=",[f,s])),n.substitution,n)])}},"@=/2":function(e,n,t){i.compare(t.args[0],t.args[1])>0&&e.success(n)},"@>=/2":function(e,n,t){i.compare(t.args[0],t.args[1])>=0&&e.success(n)},"compare/3":function(e,n,t){var s=t.args[0],a=t.args[1],l=t.args[2];if(!i.type.is_variable(s)&&!i.type.is_atom(s))e.throw_error(i.error.type("atom",s,t.indicator));else if(i.type.is_atom(s)&&["<",">","="].indexOf(s.id)===-1)e.throw_error(i.type.domain("order",s,t.indicator));else{var f=i.compare(a,l);f=f===0?"=":f===-1?"<":">",e.prepend([new V(n.goal.replace(new o("=",[s,new o(f,[])])),n.substitution,n)])}},"is/2":function(e,n,t){var s=t.args[1].interpret(e);i.type.is_number(s)?e.prepend([new V(n.goal.replace(new o("=",[t.args[0],s],e.level)),n.substitution,n)]):e.throw_error(s)},"between/3":function(e,n,t){var s=t.args[0],a=t.args[1],l=t.args[2];if(i.type.is_variable(s)||i.type.is_variable(a))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_integer(s))e.throw_error(i.error.type("integer",s,t.indicator));else if(!i.type.is_integer(a))e.throw_error(i.error.type("integer",a,t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_integer(l))e.throw_error(i.error.type("integer",l,t.indicator));else if(i.type.is_variable(l)){var f=[new V(n.goal.replace(new o("=",[l,s])),n.substitution,n)];s.value=l.value&&e.success(n)},"succ/2":function(e,n,t){var s=t.args[0],a=t.args[1];i.type.is_variable(s)&&i.type.is_variable(a)?e.throw_error(i.error.instantiation(t.indicator)):!i.type.is_variable(s)&&!i.type.is_integer(s)?e.throw_error(i.error.type("integer",s,t.indicator)):!i.type.is_variable(a)&&!i.type.is_integer(a)?e.throw_error(i.error.type("integer",a,t.indicator)):!i.type.is_variable(s)&&s.value<0?e.throw_error(i.error.domain("not_less_than_zero",s,t.indicator)):!i.type.is_variable(a)&&a.value<0?e.throw_error(i.error.domain("not_less_than_zero",a,t.indicator)):(i.type.is_variable(a)||a.value>0)&&(i.type.is_variable(s)?e.prepend([new V(n.goal.replace(new o("=",[s,new E(a.value-1,!1)])),n.substitution,n)]):e.prepend([new V(n.goal.replace(new o("=",[a,new E(s.value+1,!1)])),n.substitution,n)]))},"=:=/2":function(e,n,t){var s=i.arithmetic_compare(e,t.args[0],t.args[1]);i.type.is_term(s)?e.throw_error(s):s===0&&e.success(n)},"=\\=/2":function(e,n,t){var s=i.arithmetic_compare(e,t.args[0],t.args[1]);i.type.is_term(s)?e.throw_error(s):s!==0&&e.success(n)},"/2":function(e,n,t){var s=i.arithmetic_compare(e,t.args[0],t.args[1]);i.type.is_term(s)?e.throw_error(s):s>0&&e.success(n)},">=/2":function(e,n,t){var s=i.arithmetic_compare(e,t.args[0],t.args[1]);i.type.is_term(s)?e.throw_error(s):s>=0&&e.success(n)},"var/1":function(e,n,t){i.type.is_variable(t.args[0])&&e.success(n)},"atom/1":function(e,n,t){i.type.is_atom(t.args[0])&&e.success(n)},"atomic/1":function(e,n,t){i.type.is_atomic(t.args[0])&&e.success(n)},"compound/1":function(e,n,t){i.type.is_compound(t.args[0])&&e.success(n)},"integer/1":function(e,n,t){i.type.is_integer(t.args[0])&&e.success(n)},"float/1":function(e,n,t){i.type.is_float(t.args[0])&&e.success(n)},"number/1":function(e,n,t){i.type.is_number(t.args[0])&&e.success(n)},"nonvar/1":function(e,n,t){i.type.is_variable(t.args[0])||e.success(n)},"ground/1":function(e,n,t){t.variables().length===0&&e.success(n)},"acyclic_term/1":function(e,n,t){for(var s=n.substitution.apply(n.substitution),a=t.args[0].variables(),l=0;l0?k[k.length-1]:null,k!==null&&(A=U(e,k,0,e.__get_max_priority(),!1))}if(A.type===h&&A.len===k.length-1&&L.value==="."){A=A.value.rename(e);var B=new o("=",[a,A]);if(y.variables){var q=he(c(yr(A.variables()),function(F){return new O(F)}));B=new o(",",[B,new o("=",[y.variables,q])])}if(y.variable_names){var q=he(c(yr(A.variables()),function(H){var J;for(J in e.session.renamed_variables)if(e.session.renamed_variables.hasOwnProperty(J)&&e.session.renamed_variables[J]===H)break;return new o("=",[new o(J,[]),new O(H)])}));B=new o(",",[B,new o("=",[y.variable_names,q])])}if(y.singletons){var q=he(c(new Q(A,null).singleton_variables(),function(H){var J;for(J in e.session.renamed_variables)if(e.session.renamed_variables.hasOwnProperty(J)&&e.session.renamed_variables[J]===H)break;return new o("=",[new o(J,[]),new O(H)])}));B=new o(",",[B,new o("=",[y.singletons,q])])}e.prepend([new V(n.goal.replace(B),n.substitution,n)])}else A.type===h?e.throw_error(i.error.syntax(k[A.len],"unexpected token",!1)):e.throw_error(A.value)}}},"write/1":function(e,n,t){var s=t.args[0];e.prepend([new V(n.goal.replace(new o(",",[new o("current_output",[new O("S")]),new o("write",[new O("S"),s])])),n.substitution,n)])},"write/2":function(e,n,t){var s=t.args[0],a=t.args[1];e.prepend([new V(n.goal.replace(new o("write_term",[s,a,new o(".",[new o("quoted",[new o("false",[])]),new o(".",[new o("ignore_ops",[new o("false")]),new o(".",[new o("numbervars",[new o("true")]),new o("[]",[])])])])])),n.substitution,n)])},"writeq/1":function(e,n,t){var s=t.args[0];e.prepend([new V(n.goal.replace(new o(",",[new o("current_output",[new O("S")]),new o("writeq",[new O("S"),s])])),n.substitution,n)])},"writeq/2":function(e,n,t){var s=t.args[0],a=t.args[1];e.prepend([new V(n.goal.replace(new o("write_term",[s,a,new o(".",[new o("quoted",[new o("true",[])]),new o(".",[new o("ignore_ops",[new o("false")]),new o(".",[new o("numbervars",[new o("true")]),new o("[]",[])])])])])),n.substitution,n)])},"write_canonical/1":function(e,n,t){var s=t.args[0];e.prepend([new V(n.goal.replace(new o(",",[new o("current_output",[new O("S")]),new o("write_canonical",[new O("S"),s])])),n.substitution,n)])},"write_canonical/2":function(e,n,t){var s=t.args[0],a=t.args[1];e.prepend([new V(n.goal.replace(new o("write_term",[s,a,new o(".",[new o("quoted",[new o("true",[])]),new o(".",[new o("ignore_ops",[new o("true")]),new o(".",[new o("numbervars",[new o("false")]),new o("[]",[])])])])])),n.substitution,n)])},"write_term/2":function(e,n,t){var s=t.args[0],a=t.args[1];e.prepend([new V(n.goal.replace(new o(",",[new o("current_output",[new O("S")]),new o("write_term",[new O("S"),s,a])])),n.substitution,n)])},"write_term/3":function(e,n,t){var s=t.args[0],a=t.args[1],l=t.args[2],f=i.type.is_stream(s)?s:e.get_stream_by_alias(s.id);if(i.type.is_variable(s)||i.type.is_variable(l))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_list(l))e.throw_error(i.error.type("list",l,t.indicator));else if(!i.type.is_stream(s)&&!i.type.is_atom(s))e.throw_error(i.error.domain("stream_or_alias",s,t.indicator));else if(!i.type.is_stream(f)||f.stream===null)e.throw_error(i.error.existence("stream",s,t.indicator));else if(f.input)e.throw_error(i.error.permission("output","stream",s,t.indicator));else if(f.type==="binary")e.throw_error(i.error.permission("output","binary_stream",s,t.indicator));else if(f.position==="past_end_of_stream"&&f.eof_action==="error")e.throw_error(i.error.permission("output","past_end_of_stream",s,t.indicator));else{for(var y={},d=l,m;i.type.is_term(d)&&d.indicator==="./2";){if(m=d.args[0],i.type.is_variable(m)){e.throw_error(i.error.instantiation(t.indicator));return}else if(!i.type.is_write_option(m)){e.throw_error(i.error.domain("write_option",m,t.indicator));return}y[m.id]=m.args[0].id==="true",d=d.args[1]}if(d.indicator!=="[]/0"){i.type.is_variable(d)?e.throw_error(i.error.instantiation(t.indicator)):e.throw_error(i.error.type("list",l,t.indicator));return}else{y.session=e.session;var S=a.toString(y);f.stream.put(S,f.position),typeof f.position=="number"&&(f.position+=S.length),e.success(n)}}},"halt/0":function(e,n,t){e.points=[]},"halt/1":function(e,n,t){var s=t.args[0];i.type.is_variable(s)?e.throw_error(i.error.instantiation(t.indicator)):i.type.is_integer(s)?e.points=[]:e.throw_error(i.error.type("integer",s,t.indicator))},"current_prolog_flag/2":function(e,n,t){var s=t.args[0],a=t.args[1];if(!i.type.is_variable(s)&&!i.type.is_atom(s))e.throw_error(i.error.type("atom",s,t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_flag(s))e.throw_error(i.error.domain("prolog_flag",s,t.indicator));else{var l=[];for(var f in i.flag)if(!!i.flag.hasOwnProperty(f)){var y=new o(",",[new o("=",[new o(f),s]),new o("=",[e.get_flag(f),a])]);l.push(new V(n.goal.replace(y),n.substitution,n))}e.prepend(l)}},"set_prolog_flag/2":function(e,n,t){var s=t.args[0],a=t.args[1];i.type.is_variable(s)||i.type.is_variable(a)?e.throw_error(i.error.instantiation(t.indicator)):i.type.is_atom(s)?i.type.is_flag(s)?i.type.is_value_flag(s,a)?i.type.is_modifiable_flag(s)?(e.session.flag[s.id]=a,e.success(n)):e.throw_error(i.error.permission("modify","flag",s)):e.throw_error(i.error.domain("flag_value",new o("+",[s,a]),t.indicator)):e.throw_error(i.error.domain("prolog_flag",s,t.indicator)):e.throw_error(i.error.type("atom",s,t.indicator))}},flag:{bounded:{allowed:[new o("true"),new o("false")],value:new o("true"),changeable:!1},max_integer:{allowed:[new E(Number.MAX_SAFE_INTEGER)],value:new E(Number.MAX_SAFE_INTEGER),changeable:!1},min_integer:{allowed:[new E(Number.MIN_SAFE_INTEGER)],value:new E(Number.MIN_SAFE_INTEGER),changeable:!1},integer_rounding_function:{allowed:[new o("down"),new o("toward_zero")],value:new o("toward_zero"),changeable:!1},char_conversion:{allowed:[new o("on"),new o("off")],value:new o("on"),changeable:!0},debug:{allowed:[new o("on"),new o("off")],value:new o("off"),changeable:!0},max_arity:{allowed:[new o("unbounded")],value:new o("unbounded"),changeable:!1},unknown:{allowed:[new o("error"),new o("fail"),new o("warning")],value:new o("error"),changeable:!0},double_quotes:{allowed:[new o("chars"),new o("codes"),new o("atom")],value:new o("codes"),changeable:!0},occurs_check:{allowed:[new o("false"),new o("true")],value:new o("false"),changeable:!0},dialect:{allowed:[new o("tau")],value:new o("tau"),changeable:!1},version_data:{allowed:[new o("tau",[new E(r.major,!1),new E(r.minor,!1),new E(r.patch,!1),new o(r.status)])],value:new o("tau",[new E(r.major,!1),new E(r.minor,!1),new E(r.patch,!1),new o(r.status)]),changeable:!1},nodejs:{allowed:[new o("yes"),new o("no")],value:new o(typeof ie!="undefined"&&ie.exports?"yes":"no"),changeable:!1}},unify:function(e,n,t){t=t===void 0?!1:t;for(var s=[{left:e,right:n}],a={};s.length!==0;){var l=s.pop();if(e=l.left,n=l.right,i.type.is_term(e)&&i.type.is_term(n)){if(e.indicator!==n.indicator)return null;for(var f=0;fa.value?1:0:a}else return s},operate:function(e,n){if(i.type.is_operator(n)){for(var t=i.type.is_operator(n),s=[],a,l=!1,f=0;fe.get_flag("max_integer").value||a0?e.start+e.matches[0].length:e.start,a=t?new o("token_not_found"):new o("found",[new o(e.value.toString())]),l=new o(".",[new o("line",[new E(e.line+1)]),new o(".",[new o("column",[new E(s+1)]),new o(".",[a,new o("[]",[])])])]);return new o("error",[new o("syntax_error",[new o(n)]),l])},syntax_by_predicate:function(e,n){return new o("error",[new o("syntax_error",[new o(e)]),ae(n)])}},warning:{singleton:function(e,n,t){for(var s=new o("[]"),a=e.length-1;a>=0;a--)s=new o(".",[new O(e[a]),s]);return new o("warning",[new o("singleton_variables",[s,ae(n)]),new o(".",[new o("line",[new E(t,!1)]),new o("[]")])])},failed_goal:function(e,n){return new o("warning",[new o("failed_goal",[e]),new o(".",[new o("line",[new E(n,!1)]),new o("[]")])])}},format_variable:function(e){return"_"+e},format_answer:function(e,n,t){n instanceof D&&(n=n.thread);var t=t||{};if(t.session=n?n.session:void 0,i.type.is_error(e))return"uncaught exception: "+e.args[0].toString();if(e===!1)return"false.";if(e===null)return"limit exceeded ;";var s=0,a="";if(i.type.is_substitution(e)){var l=e.domain(!0);e=e.filter(function(d,m){return!i.type.is_variable(m)||l.indexOf(m.id)!==-1&&d!==m.id})}for(var f in e.links)!e.links.hasOwnProperty(f)||(s++,a!==""&&(a+=", "),a+=f.toString(t)+" = "+e.links[f].toString(t));var y=typeof n=="undefined"||n.points.length>0?" ;":".";return s===0?"true"+y:a+y},flatten_error:function(e){if(!i.type.is_error(e))return null;e=e.args[0];var n={};return n.type=e.args[0].id,n.thrown=n.type==="syntax_error"?null:e.args[1].id,n.expected=null,n.found=null,n.representation=null,n.existence=null,n.existence_type=null,n.line=null,n.column=null,n.permission_operation=null,n.permission_type=null,n.evaluation_type=null,n.type==="type_error"||n.type==="domain_error"?(n.expected=e.args[0].args[0].id,n.found=e.args[0].args[1].toString()):n.type==="syntax_error"?e.args[1].indicator==="./2"?(n.expected=e.args[0].args[0].id,n.found=e.args[1].args[1].args[1].args[0],n.found=n.found.id==="token_not_found"?n.found.id:n.found.args[0].id,n.line=e.args[1].args[0].args[0].value,n.column=e.args[1].args[1].args[0].args[0].value):n.thrown=e.args[1].id:n.type==="permission_error"?(n.found=e.args[0].args[2].toString(),n.permission_operation=e.args[0].args[0].id,n.permission_type=e.args[0].args[1].id):n.type==="evaluation_error"?n.evaluation_type=e.args[0].args[0].id:n.type==="representation_error"?n.representation=e.args[0].args[0].id:n.type==="existence_error"&&(n.existence=e.args[0].args[1].toString(),n.existence_type=e.args[0].args[0].id),n},create:function(e){return new i.type.Session(e)}};typeof ie!="undefined"?ie.exports=i:window.pl=i})()});var er=I((qu,rt)=>{var is=Array.isArray;rt.exports=is});var nt=I(($u,tt)=>{var ss=typeof global=="object"&&global&&global.Object===Object&&global;tt.exports=ss});var rr=I((Du,it)=>{var as=nt(),os=typeof self=="object"&&self&&self.Object===Object&&self,us=as||os||Function("return this")();it.exports=us});var tr=I((Xu,st)=>{var ls=rr(),cs=ls.Symbol;st.exports=cs});var lt=I((Bu,at)=>{var ot=tr(),ut=Object.prototype,fs=ut.hasOwnProperty,ps=ut.toString,Xe=ot?ot.toStringTag:void 0;function ys(r){var u=fs.call(r,Xe),p=r[Xe];try{r[Xe]=void 0;var c=!0}catch(_){}var w=ps.call(r);return c&&(u?r[Xe]=p:delete r[Xe]),w}at.exports=ys});var ft=I((Fu,ct)=>{var _s=Object.prototype,ws=_s.toString;function gs(r){return ws.call(r)}ct.exports=gs});var Pr=I((zu,pt)=>{var yt=tr(),ds=lt(),vs=ft(),hs="[object Null]",ms="[object Undefined]",_t=yt?yt.toStringTag:void 0;function bs(r){return r==null?r===void 0?ms:hs:_t&&_t in Object(r)?ds(r):vs(r)}pt.exports=bs});var gt=I((Wu,wt)=>{function Ts(r){return r!=null&&typeof r=="object"}wt.exports=Ts});var nr=I((Lu,dt)=>{var xs=Pr(),Vs=gt(),Ss="[object Symbol]";function ks(r){return typeof r=="symbol"||Vs(r)&&xs(r)==Ss}dt.exports=ks});var ht=I((Hu,vt)=>{var Ps=er(),Cs=nr(),Os=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,Is=/^\w*$/;function Es(r,u){if(Ps(r))return!1;var p=typeof r;return p=="number"||p=="symbol"||p=="boolean"||r==null||Cs(r)?!0:Is.test(r)||!Os.test(r)||u!=null&&r in Object(u)}vt.exports=Es});var ir=I((Gu,mt)=>{function As(r){var u=typeof r;return r!=null&&(u=="object"||u=="function")}mt.exports=As});var Tt=I((Yu,bt)=>{var Ns=Pr(),Rs=ir(),Ms="[object AsyncFunction]",qs="[object Function]",$s="[object GeneratorFunction]",Ds="[object Proxy]";function Xs(r){if(!Rs(r))return!1;var u=Ns(r);return u==qs||u==$s||u==Ms||u==Ds}bt.exports=Xs});var Vt=I((Uu,xt)=>{var Bs=rr(),Fs=Bs["__core-js_shared__"];xt.exports=Fs});var Pt=I((Zu,St)=>{var Cr=Vt(),kt=function(){var r=/[^.]+$/.exec(Cr&&Cr.keys&&Cr.keys.IE_PROTO||"");return r?"Symbol(src)_1."+r:""}();function zs(r){return!!kt&&kt in r}St.exports=zs});var Ot=I((Qu,Ct)=>{var Ws=Function.prototype,Ls=Ws.toString;function Hs(r){if(r!=null){try{return Ls.call(r)}catch(u){}try{return r+""}catch(u){}}return""}Ct.exports=Hs});var Et=I((Ju,It)=>{var Gs=Tt(),Ys=Pt(),Us=ir(),Zs=Ot(),Qs=/[\\^$.*+?()[\]{}|]/g,Js=/^\[object .+?Constructor\]$/,Ks=Function.prototype,js=Object.prototype,ea=Ks.toString,ra=js.hasOwnProperty,ta=RegExp("^"+ea.call(ra).replace(Qs,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");function na(r){if(!Us(r)||Ys(r))return!1;var u=Gs(r)?ta:Js;return u.test(Zs(r))}It.exports=na});var Nt=I((Ku,At)=>{function ia(r,u){return r==null?void 0:r[u]}At.exports=ia});var sr=I((ju,Rt)=>{var sa=Et(),aa=Nt();function oa(r,u){var p=aa(r,u);return sa(p)?p:void 0}Rt.exports=oa});var Be=I((el,Mt)=>{var ua=sr(),la=ua(Object,"create");Mt.exports=la});var Dt=I((rl,qt)=>{var $t=Be();function ca(){this.__data__=$t?$t(null):{},this.size=0}qt.exports=ca});var Bt=I((tl,Xt)=>{function fa(r){var u=this.has(r)&&delete this.__data__[r];return this.size-=u?1:0,u}Xt.exports=fa});var zt=I((nl,Ft)=>{var pa=Be(),ya="__lodash_hash_undefined__",_a=Object.prototype,wa=_a.hasOwnProperty;function ga(r){var u=this.__data__;if(pa){var p=u[r];return p===ya?void 0:p}return wa.call(u,r)?u[r]:void 0}Ft.exports=ga});var Lt=I((il,Wt)=>{var da=Be(),va=Object.prototype,ha=va.hasOwnProperty;function ma(r){var u=this.__data__;return da?u[r]!==void 0:ha.call(u,r)}Wt.exports=ma});var Gt=I((sl,Ht)=>{var ba=Be(),Ta="__lodash_hash_undefined__";function xa(r,u){var p=this.__data__;return this.size+=this.has(r)?0:1,p[r]=ba&&u===void 0?Ta:u,this}Ht.exports=xa});var Ut=I((al,Yt)=>{var Va=Dt(),Sa=Bt(),ka=zt(),Pa=Lt(),Ca=Gt();function Ie(r){var u=-1,p=r==null?0:r.length;for(this.clear();++u{function Oa(){this.__data__=[],this.size=0}Zt.exports=Oa});var Or=I((ul,Jt)=>{function Ia(r,u){return r===u||r!==r&&u!==u}Jt.exports=Ia});var Fe=I((ll,Kt)=>{var Ea=Or();function Aa(r,u){for(var p=r.length;p--;)if(Ea(r[p][0],u))return p;return-1}Kt.exports=Aa});var en=I((cl,jt)=>{var Na=Fe(),Ra=Array.prototype,Ma=Ra.splice;function qa(r){var u=this.__data__,p=Na(u,r);if(p<0)return!1;var c=u.length-1;return p==c?u.pop():Ma.call(u,p,1),--this.size,!0}jt.exports=qa});var tn=I((fl,rn)=>{var $a=Fe();function Da(r){var u=this.__data__,p=$a(u,r);return p<0?void 0:u[p][1]}rn.exports=Da});var sn=I((pl,nn)=>{var Xa=Fe();function Ba(r){return Xa(this.__data__,r)>-1}nn.exports=Ba});var on=I((yl,an)=>{var Fa=Fe();function za(r,u){var p=this.__data__,c=Fa(p,r);return c<0?(++this.size,p.push([r,u])):p[c][1]=u,this}an.exports=za});var ln=I((_l,un)=>{var Wa=Qt(),La=en(),Ha=tn(),Ga=sn(),Ya=on();function Ee(r){var u=-1,p=r==null?0:r.length;for(this.clear();++u{var Ua=sr(),Za=rr(),Qa=Ua(Za,"Map");cn.exports=Qa});var _n=I((gl,pn)=>{var yn=Ut(),Ja=ln(),Ka=fn();function ja(){this.size=0,this.__data__={hash:new yn,map:new(Ka||Ja),string:new yn}}pn.exports=ja});var gn=I((dl,wn)=>{function eo(r){var u=typeof r;return u=="string"||u=="number"||u=="symbol"||u=="boolean"?r!=="__proto__":r===null}wn.exports=eo});var ze=I((vl,dn)=>{var ro=gn();function to(r,u){var p=r.__data__;return ro(u)?p[typeof u=="string"?"string":"hash"]:p.map}dn.exports=to});var hn=I((hl,vn)=>{var no=ze();function io(r){var u=no(this,r).delete(r);return this.size-=u?1:0,u}vn.exports=io});var bn=I((ml,mn)=>{var so=ze();function ao(r){return so(this,r).get(r)}mn.exports=ao});var xn=I((bl,Tn)=>{var oo=ze();function uo(r){return oo(this,r).has(r)}Tn.exports=uo});var Sn=I((Tl,Vn)=>{var lo=ze();function co(r,u){var p=lo(this,r),c=p.size;return p.set(r,u),this.size+=p.size==c?0:1,this}Vn.exports=co});var Pn=I((xl,kn)=>{var fo=_n(),po=hn(),yo=bn(),_o=xn(),wo=Sn();function Ae(r){var u=-1,p=r==null?0:r.length;for(this.clear();++u{var On=Pn(),go="Expected a function";function Ir(r,u){if(typeof r!="function"||u!=null&&typeof u!="function")throw new TypeError(go);var p=function(){var c=arguments,w=u?u.apply(this,c):c[0],_=p.cache;if(_.has(w))return _.get(w);var v=r.apply(this,c);return p.cache=_.set(w,v)||_,v};return p.cache=new(Ir.Cache||On),p}Ir.Cache=On;Cn.exports=Ir});var An=I((Sl,En)=>{var vo=In(),ho=500;function mo(r){var u=vo(r,function(c){return p.size===ho&&p.clear(),c}),p=u.cache;return u}En.exports=mo});var Rn=I((kl,Nn)=>{var bo=An(),To=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,xo=/\\(\\)?/g,Vo=bo(function(r){var u=[];return r.charCodeAt(0)===46&&u.push(""),r.replace(To,function(p,c,w,_){u.push(w?_.replace(xo,"$1"):c||p)}),u});Nn.exports=Vo});var qn=I((Pl,Mn)=>{function So(r,u){for(var p=-1,c=r==null?0:r.length,w=Array(c);++p{var Dn=tr(),ko=qn(),Po=er(),Co=nr(),Oo=1/0,Xn=Dn?Dn.prototype:void 0,Bn=Xn?Xn.toString:void 0;function Fn(r){if(typeof r=="string")return r;if(Po(r))return ko(r,Fn)+"";if(Co(r))return Bn?Bn.call(r):"";var u=r+"";return u=="0"&&1/r==-Oo?"-0":u}$n.exports=Fn});var Ln=I((Ol,Wn)=>{var Io=zn();function Eo(r){return r==null?"":Io(r)}Wn.exports=Eo});var ar=I((Il,Hn)=>{var Ao=er(),No=ht(),Ro=Rn(),Mo=Ln();function qo(r,u){return Ao(r)?r:No(r,u)?[r]:Ro(Mo(r))}Hn.exports=qo});var or=I((El,Gn)=>{var $o=nr(),Do=1/0;function Xo(r){if(typeof r=="string"||$o(r))return r;var u=r+"";return u=="0"&&1/r==-Do?"-0":u}Gn.exports=Xo});var Er=I((Al,Yn)=>{var Bo=ar(),Fo=or();function zo(r,u){u=Bo(u,r);for(var p=0,c=u.length;r!=null&&p{var Wo=Er();function Lo(r,u,p){var c=r==null?void 0:Wo(r,u);return c===void 0?p:c}Un.exports=Lo});var li=I((Ul,ui)=>{var Jo=sr(),Ko=function(){try{var r=Jo(Object,"defineProperty");return r({},"",{}),r}catch(u){}}();ui.exports=Ko});var pi=I((Zl,ci)=>{var fi=li();function jo(r,u,p){u=="__proto__"&&fi?fi(r,u,{configurable:!0,enumerable:!0,value:p,writable:!0}):r[u]=p}ci.exports=jo});var _i=I((Ql,yi)=>{var eu=pi(),ru=Or(),tu=Object.prototype,nu=tu.hasOwnProperty;function iu(r,u,p){var c=r[u];(!(nu.call(r,u)&&ru(c,p))||p===void 0&&!(u in r))&&eu(r,u,p)}yi.exports=iu});var gi=I((Jl,wi)=>{var su=9007199254740991,au=/^(?:0|[1-9]\d*)$/;function ou(r,u){var p=typeof r;return u=u==null?su:u,!!u&&(p=="number"||p!="symbol"&&au.test(r))&&r>-1&&r%1==0&&r{var uu=_i(),lu=ar(),cu=gi(),vi=ir(),fu=or();function pu(r,u,p,c){if(!vi(r))return r;u=lu(u,r);for(var w=-1,_=u.length,v=_-1,g=r;g!=null&&++w<_;){var h=fu(u[w]),x=p;if(h==="__proto__"||h==="constructor"||h==="prototype")return r;if(w!=v){var T=g[h];x=c?c(T,h,g):void 0,x===void 0&&(x=vi(T)?T:cu(u[w+1])?[]:{})}uu(g,h,x),g=g[h]}return r}di.exports=pu});var bi=I((jl,mi)=>{var yu=hi();function _u(r,u,p){return r==null?r:yu(r,u,p)}mi.exports=_u});var xi=I((ec,Ti)=>{function wu(r){var u=r==null?0:r.length;return u?r[u-1]:void 0}Ti.exports=wu});var Si=I((rc,Vi)=>{function gu(r,u,p){var c=-1,w=r.length;u<0&&(u=-u>w?0:w+u),p=p>w?w:p,p<0&&(p+=w),w=u>p?0:p-u>>>0,u>>>=0;for(var _=Array(w);++c{var du=Er(),vu=Si();function hu(r,u){return u.length<2?r:du(r,vu(u,0,-1))}ki.exports=hu});var Oi=I((nc,Ci)=>{var mu=ar(),bu=xi(),Tu=Pi(),xu=or();function Vu(r,u){return u=mu(u,r),r=Tu(r,u),r==null||delete r[xu(bu(u))]}Ci.exports=Vu});var Ei=I((ic,Ii)=>{var Su=Oi();function ku(r,u){return r==null?!0:Su(r,u)}Ii.exports=ku});var Ou={};Qi(Ou,{default:()=>Eu});var $i=G(require("@yarnpkg/core"));var ni=G(require("@yarnpkg/cli")),ur=G(require("@yarnpkg/core")),ii=G(require("@yarnpkg/core")),Le=G(require("clipanion"));var ue=G(require("@yarnpkg/core")),le=G(require("@yarnpkg/core")),Ne=G(require("@yarnpkg/fslib")),jn=G(Xr()),Re=G(kr());var Nr=G(require("@yarnpkg/core")),Rr=G(Ar()),re=G(kr()),Zn=G(require("vm")),{is_atom:ge,is_variable:Ho,is_instantiated_list:Go}=re.default.type;function Qn(r,u,p){r.prepend(p.map(c=>new re.default.type.State(u.goal.replace(c),u.substitution,u)))}var Jn=new WeakMap;function Mr(r){let u=Jn.get(r.session);if(u==null)throw new Error("Assertion failed: A project should have been registered for the active session");return u}var Yo=new re.default.type.Module("constraints",{["project_workspaces_by_descriptor/3"]:(r,u,p)=>{let[c,w,_]=p.args;if(!ge(c)||!ge(w)){r.throw_error(re.default.error.instantiation(p.indicator));return}let v=Nr.structUtils.parseIdent(c.id),g=Nr.structUtils.makeDescriptor(v,w.id),x=Mr(r).tryWorkspaceByDescriptor(g);Ho(_)&&x!==null&&Qn(r,u,[new re.default.type.Term("=",[_,new re.default.type.Term(String(x.relativeCwd))])]),ge(_)&&x!==null&&x.relativeCwd===_.id&&r.success(u)},["workspace_field/3"]:(r,u,p)=>{let[c,w,_]=p.args;if(!ge(c)||!ge(w)){r.throw_error(re.default.error.instantiation(p.indicator));return}let g=Mr(r).tryWorkspaceByCwd(c.id);if(g==null)return;let h=(0,Rr.default)(g.manifest.raw,w.id);typeof h!="undefined"&&Qn(r,u,[new re.default.type.Term("=",[_,new re.default.type.Term(typeof h=="object"?JSON.stringify(h):h)])])},["workspace_field_test/3"]:(r,u,p)=>{let[c,w,_]=p.args;r.prepend([new re.default.type.State(u.goal.replace(new re.default.type.Term("workspace_field_test",[c,w,_,new re.default.type.Term("[]",[])])),u.substitution,u)])},["workspace_field_test/4"]:(r,u,p)=>{let[c,w,_,v]=p.args;if(!ge(c)||!ge(w)||!ge(_)||!Go(v)){r.throw_error(re.default.error.instantiation(p.indicator));return}let h=Mr(r).tryWorkspaceByCwd(c.id);if(h==null)return;let x=(0,Rr.default)(h.manifest.raw,w.id);if(typeof x=="undefined")return;let T={$$:x};for(let[C,N]of v.toJavaScript().entries())T[`$${C}`]=N;Zn.default.runInNewContext(_.id,T)&&r.success(u)}},["project_workspaces_by_descriptor/3","workspace_field/3","workspace_field_test/3","workspace_field_test/4"]);function Kn(r,u){Jn.set(r,u),r.consult(`:- use_module(library(${Yo.id})).`)}(0,jn.default)(Re.default);var We;(function(c){c.Dependencies="dependencies",c.DevDependencies="devDependencies",c.PeerDependencies="peerDependencies"})(We||(We={}));var ei=[We.Dependencies,We.DevDependencies,We.PeerDependencies];function K(r){if(r instanceof Re.default.type.Num)return r.value;if(r instanceof Re.default.type.Term)switch(r.indicator){case"throw/1":return K(r.args[0]);case"error/1":return K(r.args[0]);case"error/2":if(r.args[0]instanceof Re.default.type.Term&&r.args[0].indicator==="syntax_error/1")return Object.assign(K(r.args[0]),...K(r.args[1]));{let u=K(r.args[0]);return u.message+=` (in ${K(r.args[1])})`,u}case"syntax_error/1":return new ue.ReportError(ue.MessageName.PROLOG_SYNTAX_ERROR,`Syntax error: ${K(r.args[0])}`);case"existence_error/2":return new ue.ReportError(ue.MessageName.PROLOG_EXISTENCE_ERROR,`Existence error: ${K(r.args[0])} ${K(r.args[1])} not found`);case"instantiation_error/0":return new ue.ReportError(ue.MessageName.PROLOG_INSTANTIATION_ERROR,"Instantiation error: an argument is variable when an instantiated argument was expected");case"line/1":return{line:K(r.args[0])};case"column/1":return{column:K(r.args[0])};case"found/1":return{found:K(r.args[0])};case"./2":return[K(r.args[0])].concat(K(r.args[1]));case"//2":return`${K(r.args[0])}/${K(r.args[1])}`;default:return r.id}throw`couldn't pretty print because of unsupported node ${r}`}function ri(r){let u;try{u=K(r)}catch(p){throw typeof p=="string"?new ue.ReportError(ue.MessageName.PROLOG_UNKNOWN_ERROR,`Unknown error: ${r} (note: ${p})`):p}return typeof u.line!="undefined"&&typeof u.column!="undefined"&&(u.message+=` at line ${u.line}, column ${u.column}`),u}var ti=class{constructor(u,p){this.session=Re.default.create(),Kn(this.session,u),this.session.consult(":- use_module(library(lists))."),this.session.consult(p)}fetchNextAnswer(){return new Promise(u=>{this.session.answer(p=>{u(p)})})}async*makeQuery(u){let p=this.session.query(u);if(p!==!0)throw ri(p);for(;;){let c=await this.fetchNextAnswer();if(!c)break;if(c.id==="throw")throw ri(c);yield c}}};function ke(r){return r.id==="null"?null:`${r.toJavaScript()}`}function Uo(r){if(r.id==="null")return null;{let u=r.toJavaScript();if(typeof u!="string")return JSON.stringify(u);try{return JSON.stringify(JSON.parse(u))}catch{return JSON.stringify(u)}}}var pe=class{constructor(u){this.source="";this.project=u;let p=u.configuration.get("constraintsPath");Ne.xfs.existsSync(p)&&(this.source=Ne.xfs.readFileSync(p,"utf8"))}static async find(u){return new pe(u)}getProjectDatabase(){let u="";for(let p of ei)u+=`dependency_type(${p}). +`;for(let p of this.project.workspacesByCwd.values()){let c=p.relativeCwd;u+=`workspace(${de(c)}). +`,u+=`workspace_ident(${de(c)}, ${de(le.structUtils.stringifyIdent(p.locator))}). +`,u+=`workspace_version(${de(c)}, ${de(p.manifest.version)}). +`;for(let w of ei)for(let _ of p.manifest[w].values())u+=`workspace_has_dependency(${de(c)}, ${de(le.structUtils.stringifyIdent(_))}, ${de(_.range)}, ${w}). +`}return u+=`workspace(_) :- false. +`,u+=`workspace_ident(_, _) :- false. +`,u+=`workspace_version(_, _) :- false. +`,u+=`workspace_has_dependency(_, _, _, _) :- false. +`,u}getDeclarations(){let u="";return u+=`gen_enforced_dependency(_, _, _, _) :- false. +`,u+=`gen_enforced_field(_, _, _) :- false. +`,u}get fullSource(){return`${this.getProjectDatabase()} +${this.source} +${this.getDeclarations()}`}createSession(){return new ti(this.project,this.fullSource)}async process(){let u=this.createSession();return{enforcedDependencies:await this.genEnforcedDependencies(u),enforcedFields:await this.genEnforcedFields(u)}}async genEnforcedDependencies(u){let p=[];for await(let c of u.makeQuery("workspace(WorkspaceCwd), dependency_type(DependencyType), gen_enforced_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType).")){let w=Ne.ppath.resolve(this.project.cwd,ke(c.links.WorkspaceCwd)),_=ke(c.links.DependencyIdent),v=ke(c.links.DependencyRange),g=ke(c.links.DependencyType);if(w===null||_===null)throw new Error("Invalid rule");let h=this.project.getWorkspaceByCwd(w),x=le.structUtils.parseIdent(_);p.push({workspace:h,dependencyIdent:x,dependencyRange:v,dependencyType:g})}return le.miscUtils.sortMap(p,[({dependencyRange:c})=>c!==null?"0":"1",({workspace:c})=>le.structUtils.stringifyIdent(c.locator),({dependencyIdent:c})=>le.structUtils.stringifyIdent(c)])}async genEnforcedFields(u){let p=[];for await(let c of u.makeQuery("workspace(WorkspaceCwd), gen_enforced_field(WorkspaceCwd, FieldPath, FieldValue).")){let w=Ne.ppath.resolve(this.project.cwd,ke(c.links.WorkspaceCwd)),_=ke(c.links.FieldPath),v=Uo(c.links.FieldValue);if(w===null||_===null)throw new Error("Invalid rule");let g=this.project.getWorkspaceByCwd(w);p.push({workspace:g,fieldPath:_,fieldValue:v})}return le.miscUtils.sortMap(p,[({workspace:c})=>le.structUtils.stringifyIdent(c.locator),({fieldPath:c})=>c])}async*query(u){let p=this.createSession();for await(let c of p.makeQuery(u)){let w={};for(let[_,v]of Object.entries(c.links))_!=="_"&&(w[_]=ke(v));yield w}}};function de(r){return typeof r=="string"?`'${r}'`:"[]"}var He=class extends ni.BaseCommand{constructor(){super(...arguments);this.json=Le.Option.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.query=Le.Option.String()}async execute(){let u=await ur.Configuration.find(this.context.cwd,this.context.plugins),{project:p}=await ur.Project.find(u,this.context.cwd),c=await pe.find(p),w=this.query;return w.endsWith(".")||(w=`${w}.`),(await ii.StreamReport.start({configuration:u,json:this.json,stdout:this.context.stdout},async v=>{for await(let g of c.query(w)){let h=Array.from(Object.entries(g)),x=h.length,T=h.reduce((b,[C])=>Math.max(b,C.length),0);for(let b=0;b{let v=new Set,g=[];for(let h=0,x=this.fix?10:1;h{await h.persistManifest()}));for(let[h,x]of g)_.reportError(h,x)});return w.hasErrors()?w.exitCode():0}};Ye.paths=[["constraints"]],Ye.usage=fr.Command.Usage({category:"Constraints-related commands",description:"check that the project constraints are met",details:` + This command will run constraints on your project and emit errors for each one that is found but isn't met. If any error is emitted the process will exit with a non-zero exit code. + + If the \`--fix\` flag is used, Yarn will attempt to automatically fix the issues the best it can, following a multi-pass process (with a maximum of 10 iterations). Some ambiguous patterns cannot be autofixed, in which case you'll have to manually specify the right resolution. + + For more information as to how to write constraints, please consult our dedicated page on our website: https://yarnpkg.com/features/constraints. + `,examples:[["Check that all constraints are satisfied","yarn constraints"],["Autofix all unmet constraints","yarn constraints --fix"]]});var qi=Ye;async function Pu(r,u,p,{configuration:c,fix:w}){let _=new Map,v=new Map;for(let{workspace:g,dependencyIdent:h,dependencyRange:x,dependencyType:T}of p){let b=v.get(g);typeof b=="undefined"&&v.set(g,b=new Map);let C=b.get(h.identHash);typeof C=="undefined"&&b.set(h.identHash,C=new Map);let N=C.get(T);typeof N=="undefined"&&C.set(T,N=new Set),_.set(h.identHash,h),N.add(x)}for(let[g,h]of v)for(let[x,T]of h){let b=_.get(x);if(typeof b=="undefined")throw new Error("Assertion failed: The ident should have been registered");for(let[C,N]of T){let W=N.has(null)?[null]:[...N];if(W.length>2)u.push([se.MessageName.CONSTRAINTS_AMBIGUITY,`${$.structUtils.prettyWorkspace(c,g)} must depend on ${$.structUtils.prettyIdent(c,b)} via conflicting ranges ${W.slice(0,-1).map(ee=>$.structUtils.prettyRange(c,String(ee))).join(", ")}, and ${$.structUtils.prettyRange(c,String(W[W.length-1]))} (in ${C})`]);else if(W.length>1)u.push([se.MessageName.CONSTRAINTS_AMBIGUITY,`${$.structUtils.prettyWorkspace(c,g)} must depend on ${$.structUtils.prettyIdent(c,b)} via conflicting ranges ${$.structUtils.prettyRange(c,String(W[0]))} and ${$.structUtils.prettyRange(c,String(W[1]))} (in ${C})`]);else{let ee=g.manifest[C].get(b.identHash),[te]=W;te!==null?ee?ee.range!==te&&(w?(g.manifest[C].set(b.identHash,$.structUtils.makeDescriptor(b,te)),r.add(g)):u.push([se.MessageName.CONSTRAINTS_INCOMPATIBLE_DEPENDENCY,`${$.structUtils.prettyWorkspace(c,g)} must depend on ${$.structUtils.prettyIdent(c,b)} via ${$.structUtils.prettyRange(c,te)}, but uses ${$.structUtils.prettyRange(c,ee.range)} instead (in ${C})`])):w?(g.manifest[C].set(b.identHash,$.structUtils.makeDescriptor(b,te)),r.add(g)):u.push([se.MessageName.CONSTRAINTS_MISSING_DEPENDENCY,`${$.structUtils.prettyWorkspace(c,g)} must depend on ${$.structUtils.prettyIdent(c,b)} (via ${$.structUtils.prettyRange(c,te)}), but doesn't (in ${C})`]):ee&&(w?(g.manifest[C].delete(b.identHash),r.add(g)):u.push([se.MessageName.CONSTRAINTS_EXTRANEOUS_DEPENDENCY,`${$.structUtils.prettyWorkspace(c,g)} has an extraneous dependency on ${$.structUtils.prettyIdent(c,b)} (in ${C})`]))}}}}async function Cu(r,u,p,{configuration:c,fix:w}){let _=new Map;for(let{workspace:v,fieldPath:g,fieldValue:h}of p){let x=Pe.miscUtils.getMapWithDefault(_,v);Pe.miscUtils.getSetWithDefault(x,g).add(h)}for(let[v,g]of _)for(let[h,x]of g){let T=[...x];if(T.length>2)u.push([se.MessageName.CONSTRAINTS_AMBIGUITY,`${$.structUtils.prettyWorkspace(c,v)} must have a field ${$.formatUtils.pretty(c,h,"cyan")} set to conflicting values ${T.slice(0,-1).map(b=>$.formatUtils.pretty(c,String(b),"magenta")).join(", ")}, or ${$.formatUtils.pretty(c,String(T[T.length-1]),"magenta")}`]);else if(T.length>1)u.push([se.MessageName.CONSTRAINTS_AMBIGUITY,`${$.structUtils.prettyWorkspace(c,v)} must have a field ${$.formatUtils.pretty(c,h,"cyan")} set to conflicting values ${$.formatUtils.pretty(c,String(T[0]),"magenta")} or ${$.formatUtils.pretty(c,String(T[1]),"magenta")}`]);else{let b=(0,Ni.default)(v.manifest.raw,h),[C]=T;C!==null?b===void 0?w?(await qr(v,h,C),r.add(v)):u.push([se.MessageName.CONSTRAINTS_MISSING_FIELD,`${$.structUtils.prettyWorkspace(c,v)} must have a field ${$.formatUtils.pretty(c,h,"cyan")} set to ${$.formatUtils.pretty(c,String(C),"magenta")}, but doesn't`]):JSON.stringify(b)!==C&&(w?(await qr(v,h,C),r.add(v)):u.push([se.MessageName.CONSTRAINTS_INCOMPATIBLE_FIELD,`${$.structUtils.prettyWorkspace(c,v)} must have a field ${$.formatUtils.pretty(c,h,"cyan")} set to ${$.formatUtils.pretty(c,String(C),"magenta")}, but is set to ${$.formatUtils.pretty(c,JSON.stringify(b),"magenta")} instead`])):b!=null&&(w?(await qr(v,h,null),r.add(v)):u.push([se.MessageName.CONSTRAINTS_EXTRANEOUS_FIELD,`${$.structUtils.prettyWorkspace(c,v)} has an extraneous field ${$.formatUtils.pretty(c,h,"cyan")} set to ${$.formatUtils.pretty(c,JSON.stringify(b),"magenta")}`]))}}}async function qr(r,u,p){p===null?(0,Mi.default)(r.manifest.raw,u):(0,Ri.default)(r.manifest.raw,u,JSON.parse(p))}var Iu={configuration:{constraintsPath:{description:"The path of the constraints file.",type:$i.SettingsType.ABSOLUTE_PATH,default:"./constraints.pro"}},commands:[si,oi,qi]},Eu=Iu;return Ou;})(); +return plugin; +} +}; diff --git a/.yarn/plugins/@yarnpkg/plugin-outdated.cjs b/.yarn/plugins/@yarnpkg/plugin-outdated.cjs new file mode 100644 index 00000000000..75d378c029e --- /dev/null +++ b/.yarn/plugins/@yarnpkg/plugin-outdated.cjs @@ -0,0 +1,33 @@ +/* eslint-disable */ +//prettier-ignore +module.exports = { +name: "@yarnpkg/plugin-outdated", +factory: function (require) { +var plugin=(()=>{var Cr=Object.create,ge=Object.defineProperty,Er=Object.defineProperties,_r=Object.getOwnPropertyDescriptor,xr=Object.getOwnPropertyDescriptors,br=Object.getOwnPropertyNames,et=Object.getOwnPropertySymbols,Sr=Object.getPrototypeOf,tt=Object.prototype.hasOwnProperty,vr=Object.prototype.propertyIsEnumerable;var rt=(e,t,r)=>t in e?ge(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,k=(e,t)=>{for(var r in t||(t={}))tt.call(t,r)&&rt(e,r,t[r]);if(et)for(var r of et(t))vr.call(t,r)&&rt(e,r,t[r]);return e},q=(e,t)=>Er(e,xr(t)),Hr=e=>ge(e,"__esModule",{value:!0});var W=e=>{if(typeof require!="undefined")return require(e);throw new Error('Dynamic require of "'+e+'" is not supported')};var U=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),wr=(e,t)=>{for(var r in t)ge(e,r,{get:t[r],enumerable:!0})},Tr=(e,t,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of br(t))!tt.call(e,n)&&n!=="default"&&ge(e,n,{get:()=>t[n],enumerable:!(r=_r(t,n))||r.enumerable});return e},re=e=>Tr(Hr(ge(e!=null?Cr(Sr(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);var ve=U(V=>{"use strict";V.isInteger=e=>typeof e=="number"?Number.isInteger(e):typeof e=="string"&&e.trim()!==""?Number.isInteger(Number(e)):!1;V.find=(e,t)=>e.nodes.find(r=>r.type===t);V.exceedsLimit=(e,t,r=1,n)=>n===!1||!V.isInteger(e)||!V.isInteger(t)?!1:(Number(t)-Number(e))/Number(r)>=n;V.escapeNode=(e,t=0,r)=>{let n=e.nodes[t];!n||(r&&n.type===r||n.type==="open"||n.type==="close")&&n.escaped!==!0&&(n.value="\\"+n.value,n.escaped=!0)};V.encloseBrace=e=>e.type!=="brace"?!1:e.commas>>0+e.ranges>>0==0?(e.invalid=!0,!0):!1;V.isInvalidBrace=e=>e.type!=="brace"?!1:e.invalid===!0||e.dollar?!0:e.commas>>0+e.ranges>>0==0||e.open!==!0||e.close!==!0?(e.invalid=!0,!0):!1;V.isOpenOrClose=e=>e.type==="open"||e.type==="close"?!0:e.open===!0||e.close===!0;V.reduce=e=>e.reduce((t,r)=>(r.type==="text"&&t.push(r.value),r.type==="range"&&(r.type="text"),t),[]);V.flatten=(...e)=>{let t=[],r=n=>{for(let s=0;s{"use strict";var nt=ve();st.exports=(e,t={})=>{let r=(n,s={})=>{let a=t.escapeInvalid&&nt.isInvalidBrace(s),i=n.invalid===!0&&t.escapeInvalid===!0,o="";if(n.value)return(a||i)&&nt.isOpenOrClose(n)?"\\"+n.value:n.value;if(n.value)return n.value;if(n.nodes)for(let d of n.nodes)o+=r(d);return o};return r(e)}});var it=U((es,at)=>{"use strict";at.exports=function(e){return typeof e=="number"?e-e==0:typeof e=="string"&&e.trim()!==""?Number.isFinite?Number.isFinite(+e):isFinite(+e):!1}});var gt=U((ts,dt)=>{"use strict";var ot=it(),ce=(e,t,r)=>{if(ot(e)===!1)throw new TypeError("toRegexRange: expected the first argument to be a number");if(t===void 0||e===t)return String(e);if(ot(t)===!1)throw new TypeError("toRegexRange: expected the second argument to be a number.");let n=k({relaxZeros:!0},r);typeof n.strictZeros=="boolean"&&(n.relaxZeros=n.strictZeros===!1);let s=String(n.relaxZeros),a=String(n.shorthand),i=String(n.capture),o=String(n.wrap),d=e+":"+t+"="+s+a+i+o;if(ce.cache.hasOwnProperty(d))return ce.cache[d].result;let y=Math.min(e,t),f=Math.max(e,t);if(Math.abs(y-f)===1){let A=e+"|"+t;return n.capture?`(${A})`:n.wrap===!1?A:`(?:${A})`}let R=ht(e)||ht(t),p={min:e,max:t,a:y,b:f},H=[],m=[];if(R&&(p.isPadded=R,p.maxLen=String(p.max).length),y<0){let A=f<0?Math.abs(f):1;m=ut(A,Math.abs(y),p,n),y=p.a=0}return f>=0&&(H=ut(y,f,p,n)),p.negatives=m,p.positives=H,p.result=$r(m,H,n),n.capture===!0?p.result=`(${p.result})`:n.wrap!==!1&&H.length+m.length>1&&(p.result=`(?:${p.result})`),ce.cache[d]=p,p.result};function $r(e,t,r){let n=Ne(e,t,"-",!1,r)||[],s=Ne(t,e,"",!1,r)||[],a=Ne(e,t,"-?",!0,r)||[];return n.concat(a).concat(s).join("|")}function Lr(e,t){let r=1,n=1,s=lt(e,r),a=new Set([t]);for(;e<=s&&s<=t;)a.add(s),r+=1,s=lt(e,r);for(s=pt(t+1,n)-1;e1&&o.count.pop(),o.count.push(f.count[0]),o.string=o.pattern+ft(o.count),i=y+1;continue}r.isPadded&&(R=Dr(y,r,n)),f.string=R+f.pattern+ft(f.count),a.push(f),i=y+1,o=f}return a}function Ne(e,t,r,n,s){let a=[];for(let i of e){let{string:o}=i;!n&&!ct(t,"string",o)&&a.push(r+o),n&&ct(t,"string",o)&&a.push(r+o)}return a}function kr(e,t){let r=[];for(let n=0;nt?1:t>e?-1:0}function ct(e,t,r){return e.some(n=>n[t]===r)}function lt(e,t){return Number(String(e).slice(0,-t)+"9".repeat(t))}function pt(e,t){return e-e%Math.pow(10,t)}function ft(e){let[t=0,r=""]=e;return r||t>1?`{${t+(r?","+r:"")}}`:""}function Ir(e,t,r){return`[${e}${t-e==1?"":"-"}${t}]`}function ht(e){return/^-?(0+)\d/.test(e)}function Dr(e,t,r){if(!t.isPadded)return e;let n=Math.abs(t.maxLen-String(e).length),s=r.relaxZeros!==!1;switch(n){case 0:return"";case 1:return s?"0?":"0";case 2:return s?"0{0,2}":"00";default:return s?`0{0,${n}}`:`0{${n}}`}}ce.cache={};ce.clearCache=()=>ce.cache={};dt.exports=ce});var Pe=U((rs,xt)=>{"use strict";var Pr=W("util"),yt=gt(),Rt=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),Mr=e=>t=>e===!0?Number(t):String(t),Ie=e=>typeof e=="number"||typeof e=="string"&&e!=="",ye=e=>Number.isInteger(+e),De=e=>{let t=`${e}`,r=-1;if(t[0]==="-"&&(t=t.slice(1)),t==="0")return!1;for(;t[++r]==="0";);return r>0},Br=(e,t,r)=>typeof e=="string"||typeof t=="string"?!0:r.stringify===!0,Ur=(e,t,r)=>{if(t>0){let n=e[0]==="-"?"-":"";n&&(e=e.slice(1)),e=n+e.padStart(n?t-1:t,"0")}return r===!1?String(e):e},At=(e,t)=>{let r=e[0]==="-"?"-":"";for(r&&(e=e.slice(1),t--);e.length{e.negatives.sort((i,o)=>io?1:0),e.positives.sort((i,o)=>io?1:0);let r=t.capture?"":"?:",n="",s="",a;return e.positives.length&&(n=e.positives.join("|")),e.negatives.length&&(s=`-(${r}${e.negatives.join("|")})`),n&&s?a=`${n}|${s}`:a=n||s,t.wrap?`(${r}${a})`:a},mt=(e,t,r,n)=>{if(r)return yt(e,t,k({wrap:!1},n));let s=String.fromCharCode(e);if(e===t)return s;let a=String.fromCharCode(t);return`[${s}-${a}]`},Ct=(e,t,r)=>{if(Array.isArray(e)){let n=r.wrap===!0,s=r.capture?"":"?:";return n?`(${s}${e.join("|")})`:e.join("|")}return yt(e,t,r)},Et=(...e)=>new RangeError("Invalid range arguments: "+Pr.inspect(...e)),_t=(e,t,r)=>{if(r.strictRanges===!0)throw Et([e,t]);return[]},Fr=(e,t)=>{if(t.strictRanges===!0)throw new TypeError(`Expected step "${e}" to be a number`);return[]},Kr=(e,t,r=1,n={})=>{let s=Number(e),a=Number(t);if(!Number.isInteger(s)||!Number.isInteger(a)){if(n.strictRanges===!0)throw Et([e,t]);return[]}s===0&&(s=0),a===0&&(a=0);let i=s>a,o=String(e),d=String(t),y=String(r);r=Math.max(Math.abs(r),1);let f=De(o)||De(d)||De(y),R=f?Math.max(o.length,d.length,y.length):0,p=f===!1&&Br(e,t,n)===!1,H=n.transform||Mr(p);if(n.toRegex&&r===1)return mt(At(e,R),At(t,R),!0,n);let m={negatives:[],positives:[]},A=O=>m[O<0?"negatives":"positives"].push(Math.abs(O)),E=[],b=0;for(;i?s>=a:s<=a;)n.toRegex===!0&&r>1?A(s):E.push(Ur(H(s,b),R,p)),s=i?s-r:s+r,b++;return n.toRegex===!0?r>1?Gr(m,n):Ct(E,null,k({wrap:!1},n)):E},jr=(e,t,r=1,n={})=>{if(!ye(e)&&e.length>1||!ye(t)&&t.length>1)return _t(e,t,n);let s=n.transform||(p=>String.fromCharCode(p)),a=`${e}`.charCodeAt(0),i=`${t}`.charCodeAt(0),o=a>i,d=Math.min(a,i),y=Math.max(a,i);if(n.toRegex&&r===1)return mt(d,y,!1,n);let f=[],R=0;for(;o?a>=i:a<=i;)f.push(s(a,R)),a=o?a-r:a+r,R++;return n.toRegex===!0?Ct(f,null,{wrap:!1,options:n}):f},we=(e,t,r,n={})=>{if(t==null&&Ie(e))return[e];if(!Ie(e)||!Ie(t))return _t(e,t,n);if(typeof r=="function")return we(e,t,1,{transform:r});if(Rt(r))return we(e,t,0,r);let s=k({},n);return s.capture===!0&&(s.wrap=!0),r=r||s.step||1,ye(r)?ye(e)&&ye(t)?Kr(e,t,r,s):jr(e,t,Math.max(Math.abs(r),1),s):r!=null&&!Rt(r)?Fr(r,s):we(e,t,1,r)};xt.exports=we});var vt=U((ns,St)=>{"use strict";var qr=Pe(),bt=ve(),Wr=(e,t={})=>{let r=(n,s={})=>{let a=bt.isInvalidBrace(s),i=n.invalid===!0&&t.escapeInvalid===!0,o=a===!0||i===!0,d=t.escapeInvalid===!0?"\\":"",y="";if(n.isOpen===!0||n.isClose===!0)return d+n.value;if(n.type==="open")return o?d+n.value:"(";if(n.type==="close")return o?d+n.value:")";if(n.type==="comma")return n.prev.type==="comma"?"":o?n.value:"|";if(n.value)return n.value;if(n.nodes&&n.ranges>0){let f=bt.reduce(n.nodes),R=qr(...f,q(k({},t),{wrap:!1,toRegex:!0}));if(R.length!==0)return f.length>1&&R.length>1?`(${R})`:R}if(n.nodes)for(let f of n.nodes)y+=r(f,n);return y};return r(e)};St.exports=Wr});var Tt=U((ss,wt)=>{"use strict";var Qr=Pe(),Ht=He(),pe=ve(),le=(e="",t="",r=!1)=>{let n=[];if(e=[].concat(e),t=[].concat(t),!t.length)return e;if(!e.length)return r?pe.flatten(t).map(s=>`{${s}}`):t;for(let s of e)if(Array.isArray(s))for(let a of s)n.push(le(a,t,r));else for(let a of t)r===!0&&typeof a=="string"&&(a=`{${a}}`),n.push(Array.isArray(a)?le(s,a,r):s+a);return pe.flatten(n)},Xr=(e,t={})=>{let r=t.rangeLimit===void 0?1e3:t.rangeLimit,n=(s,a={})=>{s.queue=[];let i=a,o=a.queue;for(;i.type!=="brace"&&i.type!=="root"&&i.parent;)i=i.parent,o=i.queue;if(s.invalid||s.dollar){o.push(le(o.pop(),Ht(s,t)));return}if(s.type==="brace"&&s.invalid!==!0&&s.nodes.length===2){o.push(le(o.pop(),["{}"]));return}if(s.nodes&&s.ranges>0){let R=pe.reduce(s.nodes);if(pe.exceedsLimit(...R,t.step,r))throw new RangeError("expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.");let p=Qr(...R,t);p.length===0&&(p=Ht(s,t)),o.push(le(o.pop(),p)),s.nodes=[];return}let d=pe.encloseBrace(s),y=s.queue,f=s;for(;f.type!=="brace"&&f.type!=="root"&&f.parent;)f=f.parent,y=f.queue;for(let R=0;R{"use strict";$t.exports={MAX_LENGTH:1024*64,CHAR_0:"0",CHAR_9:"9",CHAR_UPPERCASE_A:"A",CHAR_LOWERCASE_A:"a",CHAR_UPPERCASE_Z:"Z",CHAR_LOWERCASE_Z:"z",CHAR_LEFT_PARENTHESES:"(",CHAR_RIGHT_PARENTHESES:")",CHAR_ASTERISK:"*",CHAR_AMPERSAND:"&",CHAR_AT:"@",CHAR_BACKSLASH:"\\",CHAR_BACKTICK:"`",CHAR_CARRIAGE_RETURN:"\r",CHAR_CIRCUMFLEX_ACCENT:"^",CHAR_COLON:":",CHAR_COMMA:",",CHAR_DOLLAR:"$",CHAR_DOT:".",CHAR_DOUBLE_QUOTE:'"',CHAR_EQUAL:"=",CHAR_EXCLAMATION_MARK:"!",CHAR_FORM_FEED:"\f",CHAR_FORWARD_SLASH:"/",CHAR_HASH:"#",CHAR_HYPHEN_MINUS:"-",CHAR_LEFT_ANGLE_BRACKET:"<",CHAR_LEFT_CURLY_BRACE:"{",CHAR_LEFT_SQUARE_BRACKET:"[",CHAR_LINE_FEED:` +`,CHAR_NO_BREAK_SPACE:"\xA0",CHAR_PERCENT:"%",CHAR_PLUS:"+",CHAR_QUESTION_MARK:"?",CHAR_RIGHT_ANGLE_BRACKET:">",CHAR_RIGHT_CURLY_BRACE:"}",CHAR_RIGHT_SQUARE_BRACKET:"]",CHAR_SEMICOLON:";",CHAR_SINGLE_QUOTE:"'",CHAR_SPACE:" ",CHAR_TAB:" ",CHAR_UNDERSCORE:"_",CHAR_VERTICAL_LINE:"|",CHAR_ZERO_WIDTH_NOBREAK_SPACE:"\uFEFF"}});var Dt=U((is,It)=>{"use strict";var zr=He(),{MAX_LENGTH:Ot,CHAR_BACKSLASH:Me,CHAR_BACKTICK:Zr,CHAR_COMMA:Vr,CHAR_DOT:Yr,CHAR_LEFT_PARENTHESES:Jr,CHAR_RIGHT_PARENTHESES:en,CHAR_LEFT_CURLY_BRACE:tn,CHAR_RIGHT_CURLY_BRACE:rn,CHAR_LEFT_SQUARE_BRACKET:kt,CHAR_RIGHT_SQUARE_BRACKET:Nt,CHAR_DOUBLE_QUOTE:nn,CHAR_SINGLE_QUOTE:sn,CHAR_NO_BREAK_SPACE:an,CHAR_ZERO_WIDTH_NOBREAK_SPACE:on}=Lt(),un=(e,t={})=>{if(typeof e!="string")throw new TypeError("Expected a string");let r=t||{},n=typeof r.maxLength=="number"?Math.min(Ot,r.maxLength):Ot;if(e.length>n)throw new SyntaxError(`Input length (${e.length}), exceeds max characters (${n})`);let s={type:"root",input:e,nodes:[]},a=[s],i=s,o=s,d=0,y=e.length,f=0,R=0,p,H={},m=()=>e[f++],A=E=>{if(E.type==="text"&&o.type==="dot"&&(o.type="text"),o&&o.type==="text"&&E.type==="text"){o.value+=E.value;return}return i.nodes.push(E),E.parent=i,E.prev=o,o=E,E};for(A({type:"bos"});f0){if(i.ranges>0){i.ranges=0;let E=i.nodes.shift();i.nodes=[E,{type:"text",value:zr(i)}]}A({type:"comma",value:p}),i.commas++;continue}if(p===Yr&&R>0&&i.commas===0){let E=i.nodes;if(R===0||E.length===0){A({type:"text",value:p});continue}if(o.type==="dot"){if(i.range=[],o.value+=p,o.type="range",i.nodes.length!==3&&i.nodes.length!==5){i.invalid=!0,i.ranges=0,o.type="text";continue}i.ranges++,i.args=[];continue}if(o.type==="range"){E.pop();let b=E[E.length-1];b.value+=o.value+p,o=b,i.ranges--;continue}A({type:"dot",value:p});continue}A({type:"text",value:p})}do if(i=a.pop(),i.type!=="root"){i.nodes.forEach(O=>{O.nodes||(O.type==="open"&&(O.isOpen=!0),O.type==="close"&&(O.isClose=!0),O.nodes||(O.type="text"),O.invalid=!0)});let E=a[a.length-1],b=E.nodes.indexOf(i);E.nodes.splice(b,1,...i.nodes)}while(a.length>0);return A({type:"eos"}),s};It.exports=un});var Bt=U((os,Mt)=>{"use strict";var Pt=He(),cn=vt(),ln=Tt(),pn=Dt(),z=(e,t={})=>{let r=[];if(Array.isArray(e))for(let n of e){let s=z.create(n,t);Array.isArray(s)?r.push(...s):r.push(s)}else r=[].concat(z.create(e,t));return t&&t.expand===!0&&t.nodupes===!0&&(r=[...new Set(r)]),r};z.parse=(e,t={})=>pn(e,t);z.stringify=(e,t={})=>typeof e=="string"?Pt(z.parse(e,t),t):Pt(e,t);z.compile=(e,t={})=>(typeof e=="string"&&(e=z.parse(e,t)),cn(e,t));z.expand=(e,t={})=>{typeof e=="string"&&(e=z.parse(e,t));let r=ln(e,t);return t.noempty===!0&&(r=r.filter(Boolean)),t.nodupes===!0&&(r=[...new Set(r)]),r};z.create=(e,t={})=>e===""||e.length<3?[e]:t.expand!==!0?z.compile(e,t):z.expand(e,t);Mt.exports=z});var Re=U((us,jt)=>{"use strict";var fn=W("path"),ne="\\\\/",Ut=`[^${ne}]`,ae="\\.",hn="\\+",dn="\\?",Te="\\/",gn="(?=.)",Gt="[^/]",Be=`(?:${Te}|$)`,Ft=`(?:^|${Te})`,Ue=`${ae}{1,2}${Be}`,yn=`(?!${ae})`,Rn=`(?!${Ft}${Ue})`,An=`(?!${ae}{0,1}${Be})`,mn=`(?!${Ue})`,Cn=`[^.${Te}]`,En=`${Gt}*?`,Kt={DOT_LITERAL:ae,PLUS_LITERAL:hn,QMARK_LITERAL:dn,SLASH_LITERAL:Te,ONE_CHAR:gn,QMARK:Gt,END_ANCHOR:Be,DOTS_SLASH:Ue,NO_DOT:yn,NO_DOTS:Rn,NO_DOT_SLASH:An,NO_DOTS_SLASH:mn,QMARK_NO_DOT:Cn,STAR:En,START_ANCHOR:Ft},_n=q(k({},Kt),{SLASH_LITERAL:`[${ne}]`,QMARK:Ut,STAR:`${Ut}*?`,DOTS_SLASH:`${ae}{1,2}(?:[${ne}]|$)`,NO_DOT:`(?!${ae})`,NO_DOTS:`(?!(?:^|[${ne}])${ae}{1,2}(?:[${ne}]|$))`,NO_DOT_SLASH:`(?!${ae}{0,1}(?:[${ne}]|$))`,NO_DOTS_SLASH:`(?!${ae}{1,2}(?:[${ne}]|$))`,QMARK_NO_DOT:`[^.${ne}]`,START_ANCHOR:`(?:^|[${ne}])`,END_ANCHOR:`(?:[${ne}]|$)`}),xn={alnum:"a-zA-Z0-9",alpha:"a-zA-Z",ascii:"\\x00-\\x7F",blank:" \\t",cntrl:"\\x00-\\x1F\\x7F",digit:"0-9",graph:"\\x21-\\x7E",lower:"a-z",print:"\\x20-\\x7E ",punct:"\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~",space:" \\t\\r\\n\\v\\f",upper:"A-Z",word:"A-Za-z0-9_",xdigit:"A-Fa-f0-9"};jt.exports={MAX_LENGTH:1024*64,POSIX_REGEX_SOURCE:xn,REGEX_BACKSLASH:/\\(?![*+?^${}(|)[\]])/g,REGEX_NON_SPECIAL_CHARS:/^[^@![\].,$*+?^{}()|\\/]+/,REGEX_SPECIAL_CHARS:/[-*+?.^${}(|)[\]]/,REGEX_SPECIAL_CHARS_BACKREF:/(\\?)((\W)(\3*))/g,REGEX_SPECIAL_CHARS_GLOBAL:/([-*+?.^${}(|)[\]])/g,REGEX_REMOVE_BACKSLASH:/(?:\[.*?[^\\]\]|\\(?=.))/g,REPLACEMENTS:{"***":"*","**/**":"**","**/**/**":"**"},CHAR_0:48,CHAR_9:57,CHAR_UPPERCASE_A:65,CHAR_LOWERCASE_A:97,CHAR_UPPERCASE_Z:90,CHAR_LOWERCASE_Z:122,CHAR_LEFT_PARENTHESES:40,CHAR_RIGHT_PARENTHESES:41,CHAR_ASTERISK:42,CHAR_AMPERSAND:38,CHAR_AT:64,CHAR_BACKWARD_SLASH:92,CHAR_CARRIAGE_RETURN:13,CHAR_CIRCUMFLEX_ACCENT:94,CHAR_COLON:58,CHAR_COMMA:44,CHAR_DOT:46,CHAR_DOUBLE_QUOTE:34,CHAR_EQUAL:61,CHAR_EXCLAMATION_MARK:33,CHAR_FORM_FEED:12,CHAR_FORWARD_SLASH:47,CHAR_GRAVE_ACCENT:96,CHAR_HASH:35,CHAR_HYPHEN_MINUS:45,CHAR_LEFT_ANGLE_BRACKET:60,CHAR_LEFT_CURLY_BRACE:123,CHAR_LEFT_SQUARE_BRACKET:91,CHAR_LINE_FEED:10,CHAR_NO_BREAK_SPACE:160,CHAR_PERCENT:37,CHAR_PLUS:43,CHAR_QUESTION_MARK:63,CHAR_RIGHT_ANGLE_BRACKET:62,CHAR_RIGHT_CURLY_BRACE:125,CHAR_RIGHT_SQUARE_BRACKET:93,CHAR_SEMICOLON:59,CHAR_SINGLE_QUOTE:39,CHAR_SPACE:32,CHAR_TAB:9,CHAR_UNDERSCORE:95,CHAR_VERTICAL_LINE:124,CHAR_ZERO_WIDTH_NOBREAK_SPACE:65279,SEP:fn.sep,extglobChars(e){return{"!":{type:"negate",open:"(?:(?!(?:",close:`))${e.STAR})`},"?":{type:"qmark",open:"(?:",close:")?"},"+":{type:"plus",open:"(?:",close:")+"},"*":{type:"star",open:"(?:",close:")*"},"@":{type:"at",open:"(?:",close:")"}}},globChars(e){return e===!0?_n:Kt}}});var Ae=U(Q=>{"use strict";var bn=W("path"),Sn=process.platform==="win32",{REGEX_BACKSLASH:vn,REGEX_REMOVE_BACKSLASH:Hn,REGEX_SPECIAL_CHARS:wn,REGEX_SPECIAL_CHARS_GLOBAL:Tn}=Re();Q.isObject=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);Q.hasRegexChars=e=>wn.test(e);Q.isRegexChar=e=>e.length===1&&Q.hasRegexChars(e);Q.escapeRegex=e=>e.replace(Tn,"\\$1");Q.toPosixSlashes=e=>e.replace(vn,"/");Q.removeBackslashes=e=>e.replace(Hn,t=>t==="\\"?"":t);Q.supportsLookbehinds=()=>{let e=process.version.slice(1).split(".").map(Number);return e.length===3&&e[0]>=9||e[0]===8&&e[1]>=10};Q.isWindows=e=>e&&typeof e.windows=="boolean"?e.windows:Sn===!0||bn.sep==="\\";Q.escapeLast=(e,t,r)=>{let n=e.lastIndexOf(t,r);return n===-1?e:e[n-1]==="\\"?Q.escapeLast(e,t,n-1):`${e.slice(0,n)}\\${e.slice(n)}`};Q.removePrefix=(e,t={})=>{let r=e;return r.startsWith("./")&&(r=r.slice(2),t.prefix="./"),r};Q.wrapOutput=(e,t={},r={})=>{let n=r.contains?"":"^",s=r.contains?"":"$",a=`${n}(?:${e})${s}`;return t.negated===!0&&(a=`(?:^(?!${a}).*$)`),a}});var Yt=U((ls,Vt)=>{"use strict";var qt=Ae(),{CHAR_ASTERISK:Ge,CHAR_AT:$n,CHAR_BACKWARD_SLASH:me,CHAR_COMMA:Ln,CHAR_DOT:Fe,CHAR_EXCLAMATION_MARK:Ke,CHAR_FORWARD_SLASH:Wt,CHAR_LEFT_CURLY_BRACE:je,CHAR_LEFT_PARENTHESES:qe,CHAR_LEFT_SQUARE_BRACKET:On,CHAR_PLUS:kn,CHAR_QUESTION_MARK:Qt,CHAR_RIGHT_CURLY_BRACE:Nn,CHAR_RIGHT_PARENTHESES:Xt,CHAR_RIGHT_SQUARE_BRACKET:In}=Re(),zt=e=>e===Wt||e===me,Zt=e=>{e.isPrefix!==!0&&(e.depth=e.isGlobstar?Infinity:1)},Dn=(e,t)=>{let r=t||{},n=e.length-1,s=r.parts===!0||r.scanToEnd===!0,a=[],i=[],o=[],d=e,y=-1,f=0,R=0,p=!1,H=!1,m=!1,A=!1,E=!1,b=!1,O=!1,N=!1,J=!1,G=!1,ie=0,F,C,v={value:"",depth:0,isGlob:!1},B=()=>y>=n,l=()=>d.charCodeAt(y+1),$=()=>(F=C,d.charCodeAt(++y));for(;y0&&(oe=d.slice(0,f),d=d.slice(f),R-=f),w&&m===!0&&R>0?(w=d.slice(0,R),u=d.slice(R)):m===!0?(w="",u=d):w=d,w&&w!==""&&w!=="/"&&w!==d&&zt(w.charCodeAt(w.length-1))&&(w=w.slice(0,-1)),r.unescape===!0&&(u&&(u=qt.removeBackslashes(u)),w&&O===!0&&(w=qt.removeBackslashes(w)));let c={prefix:oe,input:e,start:f,base:w,glob:u,isBrace:p,isBracket:H,isGlob:m,isExtglob:A,isGlobstar:E,negated:N,negatedExtglob:J};if(r.tokens===!0&&(c.maxDepth=0,zt(C)||i.push(v),c.tokens=i),r.parts===!0||r.tokens===!0){let K;for(let S=0;S{"use strict";var $e=Re(),Z=Ae(),{MAX_LENGTH:Le,POSIX_REGEX_SOURCE:Pn,REGEX_NON_SPECIAL_CHARS:Mn,REGEX_SPECIAL_CHARS_BACKREF:Bn,REPLACEMENTS:Jt}=$e,Un=(e,t)=>{if(typeof t.expandRange=="function")return t.expandRange(...e,t);e.sort();let r=`[${e.join("-")}]`;try{new RegExp(r)}catch(n){return e.map(s=>Z.escapeRegex(s)).join("..")}return r},fe=(e,t)=>`Missing ${e}: "${t}" - use "\\\\${t}" to match literal characters`,er=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");e=Jt[e]||e;let r=k({},t),n=typeof r.maxLength=="number"?Math.min(Le,r.maxLength):Le,s=e.length;if(s>n)throw new SyntaxError(`Input length: ${s}, exceeds maximum allowed length: ${n}`);let a={type:"bos",value:"",output:r.prepend||""},i=[a],o=r.capture?"":"?:",d=Z.isWindows(t),y=$e.globChars(d),f=$e.extglobChars(y),{DOT_LITERAL:R,PLUS_LITERAL:p,SLASH_LITERAL:H,ONE_CHAR:m,DOTS_SLASH:A,NO_DOT:E,NO_DOT_SLASH:b,NO_DOTS_SLASH:O,QMARK:N,QMARK_NO_DOT:J,STAR:G,START_ANCHOR:ie}=y,F=g=>`(${o}(?:(?!${ie}${g.dot?A:R}).)*?)`,C=r.dot?"":E,v=r.dot?N:J,B=r.bash===!0?F(r):G;r.capture&&(B=`(${B})`),typeof r.noext=="boolean"&&(r.noextglob=r.noext);let l={input:e,index:-1,start:0,dot:r.dot===!0,consumed:"",output:"",prefix:"",backtrack:!1,negated:!1,brackets:0,braces:0,parens:0,quotes:0,globstar:!1,tokens:i};e=Z.removePrefix(e,l),s=e.length;let $=[],w=[],oe=[],u=a,c,K=()=>l.index===s-1,S=l.peek=(g=1)=>e[l.index+g],ee=l.advance=()=>e[++l.index]||"",te=()=>e.slice(l.index+1),X=(g="",T=0)=>{l.consumed+=g,l.index+=T},_e=g=>{l.output+=g.output!=null?g.output:g.value,X(g.value)},Ar=()=>{let g=1;for(;S()==="!"&&(S(2)!=="("||S(3)==="?");)ee(),l.start++,g++;return g%2==0?!1:(l.negated=!0,l.start++,!0)},xe=g=>{l[g]++,oe.push(g)},ue=g=>{l[g]--,oe.pop()},x=g=>{if(u.type==="globstar"){let T=l.braces>0&&(g.type==="comma"||g.type==="brace"),h=g.extglob===!0||$.length&&(g.type==="pipe"||g.type==="paren");g.type!=="slash"&&g.type!=="paren"&&!T&&!h&&(l.output=l.output.slice(0,-u.output.length),u.type="star",u.value="*",u.output=B,l.output+=u.output)}if($.length&&g.type!=="paren"&&($[$.length-1].inner+=g.value),(g.value||g.output)&&_e(g),u&&u.type==="text"&&g.type==="text"){u.value+=g.value,u.output=(u.output||"")+g.value;return}g.prev=u,i.push(g),u=g},be=(g,T)=>{let h=q(k({},f[T]),{conditions:1,inner:""});h.prev=u,h.parens=l.parens,h.output=l.output;let _=(r.capture?"(":"")+h.open;xe("parens"),x({type:g,value:T,output:l.output?"":m}),x({type:"paren",extglob:!0,value:ee(),output:_}),$.push(h)},mr=g=>{let T=g.close+(r.capture?")":""),h;if(g.type==="negate"){let _=B;g.inner&&g.inner.length>1&&g.inner.includes("/")&&(_=F(r)),(_!==B||K()||/^\)+$/.test(te()))&&(T=g.close=`)$))${_}`),g.inner.includes("*")&&(h=te())&&/^\.[^\\/.]+$/.test(h)&&(T=g.close=`)${h})${_})`),g.prev.type==="bos"&&(l.negatedExtglob=!0)}x({type:"paren",extglob:!0,value:c,output:T}),ue("parens")};if(r.fastpaths!==!1&&!/(^[*!]|[/()[\]{}"])/.test(e)){let g=!1,T=e.replace(Bn,(h,_,I,j,M,ke)=>j==="\\"?(g=!0,h):j==="?"?_?_+j+(M?N.repeat(M.length):""):ke===0?v+(M?N.repeat(M.length):""):N.repeat(I.length):j==="."?R.repeat(I.length):j==="*"?_?_+j+(M?B:""):B:_?h:`\\${h}`);return g===!0&&(r.unescape===!0?T=T.replace(/\\/g,""):T=T.replace(/\\+/g,h=>h.length%2==0?"\\\\":h?"\\":"")),T===e&&r.contains===!0?(l.output=e,l):(l.output=Z.wrapOutput(T,l,t),l)}for(;!K();){if(c=ee(),c==="\0")continue;if(c==="\\"){let h=S();if(h==="/"&&r.bash!==!0||h==="."||h===";")continue;if(!h){c+="\\",x({type:"text",value:c});continue}let _=/^\\+/.exec(te()),I=0;if(_&&_[0].length>2&&(I=_[0].length,l.index+=I,I%2!=0&&(c+="\\")),r.unescape===!0?c=ee():c+=ee(),l.brackets===0){x({type:"text",value:c});continue}}if(l.brackets>0&&(c!=="]"||u.value==="["||u.value==="[^")){if(r.posix!==!1&&c===":"){let h=u.value.slice(1);if(h.includes("[")&&(u.posix=!0,h.includes(":"))){let _=u.value.lastIndexOf("["),I=u.value.slice(0,_),j=u.value.slice(_+2),M=Pn[j];if(M){u.value=I+M,l.backtrack=!0,ee(),!a.output&&i.indexOf(u)===1&&(a.output=m);continue}}}(c==="["&&S()!==":"||c==="-"&&S()==="]")&&(c=`\\${c}`),c==="]"&&(u.value==="["||u.value==="[^")&&(c=`\\${c}`),r.posix===!0&&c==="!"&&u.value==="["&&(c="^"),u.value+=c,_e({value:c});continue}if(l.quotes===1&&c!=='"'){c=Z.escapeRegex(c),u.value+=c,_e({value:c});continue}if(c==='"'){l.quotes=l.quotes===1?0:1,r.keepQuotes===!0&&x({type:"text",value:c});continue}if(c==="("){xe("parens"),x({type:"paren",value:c});continue}if(c===")"){if(l.parens===0&&r.strictBrackets===!0)throw new SyntaxError(fe("opening","("));let h=$[$.length-1];if(h&&l.parens===h.parens+1){mr($.pop());continue}x({type:"paren",value:c,output:l.parens?")":"\\)"}),ue("parens");continue}if(c==="["){if(r.nobracket===!0||!te().includes("]")){if(r.nobracket!==!0&&r.strictBrackets===!0)throw new SyntaxError(fe("closing","]"));c=`\\${c}`}else xe("brackets");x({type:"bracket",value:c});continue}if(c==="]"){if(r.nobracket===!0||u&&u.type==="bracket"&&u.value.length===1){x({type:"text",value:c,output:`\\${c}`});continue}if(l.brackets===0){if(r.strictBrackets===!0)throw new SyntaxError(fe("opening","["));x({type:"text",value:c,output:`\\${c}`});continue}ue("brackets");let h=u.value.slice(1);if(u.posix!==!0&&h[0]==="^"&&!h.includes("/")&&(c=`/${c}`),u.value+=c,_e({value:c}),r.literalBrackets===!1||Z.hasRegexChars(h))continue;let _=Z.escapeRegex(u.value);if(l.output=l.output.slice(0,-u.value.length),r.literalBrackets===!0){l.output+=_,u.value=_;continue}u.value=`(${o}${_}|${u.value})`,l.output+=u.value;continue}if(c==="{"&&r.nobrace!==!0){xe("braces");let h={type:"brace",value:c,output:"(",outputIndex:l.output.length,tokensIndex:l.tokens.length};w.push(h),x(h);continue}if(c==="}"){let h=w[w.length-1];if(r.nobrace===!0||!h){x({type:"text",value:c,output:c});continue}let _=")";if(h.dots===!0){let I=i.slice(),j=[];for(let M=I.length-1;M>=0&&(i.pop(),I[M].type!=="brace");M--)I[M].type!=="dots"&&j.unshift(I[M].value);_=Un(j,r),l.backtrack=!0}if(h.comma!==!0&&h.dots!==!0){let I=l.output.slice(0,h.outputIndex),j=l.tokens.slice(h.tokensIndex);h.value=h.output="\\{",c=_="\\}",l.output=I;for(let M of j)l.output+=M.output||M.value}x({type:"brace",value:c,output:_}),ue("braces"),w.pop();continue}if(c==="|"){$.length>0&&$[$.length-1].conditions++,x({type:"text",value:c});continue}if(c===","){let h=c,_=w[w.length-1];_&&oe[oe.length-1]==="braces"&&(_.comma=!0,h="|"),x({type:"comma",value:c,output:h});continue}if(c==="/"){if(u.type==="dot"&&l.index===l.start+1){l.start=l.index+1,l.consumed="",l.output="",i.pop(),u=a;continue}x({type:"slash",value:c,output:H});continue}if(c==="."){if(l.braces>0&&u.type==="dot"){u.value==="."&&(u.output=R);let h=w[w.length-1];u.type="dots",u.output+=c,u.value+=c,h.dots=!0;continue}if(l.braces+l.parens===0&&u.type!=="bos"&&u.type!=="slash"){x({type:"text",value:c,output:R});continue}x({type:"dot",value:c,output:R});continue}if(c==="?"){if(!(u&&u.value==="(")&&r.noextglob!==!0&&S()==="("&&S(2)!=="?"){be("qmark",c);continue}if(u&&u.type==="paren"){let _=S(),I=c;if(_==="<"&&!Z.supportsLookbehinds())throw new Error("Node.js v10 or higher is required for regex lookbehinds");(u.value==="("&&!/[!=<:]/.test(_)||_==="<"&&!/<([!=]|\w+>)/.test(te()))&&(I=`\\${c}`),x({type:"text",value:c,output:I});continue}if(r.dot!==!0&&(u.type==="slash"||u.type==="bos")){x({type:"qmark",value:c,output:J});continue}x({type:"qmark",value:c,output:N});continue}if(c==="!"){if(r.noextglob!==!0&&S()==="("&&(S(2)!=="?"||!/[!=<:]/.test(S(3)))){be("negate",c);continue}if(r.nonegate!==!0&&l.index===0){Ar();continue}}if(c==="+"){if(r.noextglob!==!0&&S()==="("&&S(2)!=="?"){be("plus",c);continue}if(u&&u.value==="("||r.regex===!1){x({type:"plus",value:c,output:p});continue}if(u&&(u.type==="bracket"||u.type==="paren"||u.type==="brace")||l.parens>0){x({type:"plus",value:c});continue}x({type:"plus",value:p});continue}if(c==="@"){if(r.noextglob!==!0&&S()==="("&&S(2)!=="?"){x({type:"at",extglob:!0,value:c,output:""});continue}x({type:"text",value:c});continue}if(c!=="*"){(c==="$"||c==="^")&&(c=`\\${c}`);let h=Mn.exec(te());h&&(c+=h[0],l.index+=h[0].length),x({type:"text",value:c});continue}if(u&&(u.type==="globstar"||u.star===!0)){u.type="star",u.star=!0,u.value+=c,u.output=B,l.backtrack=!0,l.globstar=!0,X(c);continue}let g=te();if(r.noextglob!==!0&&/^\([^?]/.test(g)){be("star",c);continue}if(u.type==="star"){if(r.noglobstar===!0){X(c);continue}let h=u.prev,_=h.prev,I=h.type==="slash"||h.type==="bos",j=_&&(_.type==="star"||_.type==="globstar");if(r.bash===!0&&(!I||g[0]&&g[0]!=="/")){x({type:"star",value:c,output:""});continue}let M=l.braces>0&&(h.type==="comma"||h.type==="brace"),ke=$.length&&(h.type==="pipe"||h.type==="paren");if(!I&&h.type!=="paren"&&!M&&!ke){x({type:"star",value:c,output:""});continue}for(;g.slice(0,3)==="/**";){let Se=e[l.index+4];if(Se&&Se!=="/")break;g=g.slice(3),X("/**",3)}if(h.type==="bos"&&K()){u.type="globstar",u.value+=c,u.output=F(r),l.output=u.output,l.globstar=!0,X(c);continue}if(h.type==="slash"&&h.prev.type!=="bos"&&!j&&K()){l.output=l.output.slice(0,-(h.output+u.output).length),h.output=`(?:${h.output}`,u.type="globstar",u.output=F(r)+(r.strictSlashes?")":"|$)"),u.value+=c,l.globstar=!0,l.output+=h.output+u.output,X(c);continue}if(h.type==="slash"&&h.prev.type!=="bos"&&g[0]==="/"){let Se=g[1]!==void 0?"|$":"";l.output=l.output.slice(0,-(h.output+u.output).length),h.output=`(?:${h.output}`,u.type="globstar",u.output=`${F(r)}${H}|${H}${Se})`,u.value+=c,l.output+=h.output+u.output,l.globstar=!0,X(c+ee()),x({type:"slash",value:"/",output:""});continue}if(h.type==="bos"&&g[0]==="/"){u.type="globstar",u.value+=c,u.output=`(?:^|${H}|${F(r)}${H})`,l.output=u.output,l.globstar=!0,X(c+ee()),x({type:"slash",value:"/",output:""});continue}l.output=l.output.slice(0,-u.output.length),u.type="globstar",u.output=F(r),u.value+=c,l.output+=u.output,l.globstar=!0,X(c);continue}let T={type:"star",value:c,output:B};if(r.bash===!0){T.output=".*?",(u.type==="bos"||u.type==="slash")&&(T.output=C+T.output),x(T);continue}if(u&&(u.type==="bracket"||u.type==="paren")&&r.regex===!0){T.output=c,x(T);continue}(l.index===l.start||u.type==="slash"||u.type==="dot")&&(u.type==="dot"?(l.output+=b,u.output+=b):r.dot===!0?(l.output+=O,u.output+=O):(l.output+=C,u.output+=C),S()!=="*"&&(l.output+=m,u.output+=m)),x(T)}for(;l.brackets>0;){if(r.strictBrackets===!0)throw new SyntaxError(fe("closing","]"));l.output=Z.escapeLast(l.output,"["),ue("brackets")}for(;l.parens>0;){if(r.strictBrackets===!0)throw new SyntaxError(fe("closing",")"));l.output=Z.escapeLast(l.output,"("),ue("parens")}for(;l.braces>0;){if(r.strictBrackets===!0)throw new SyntaxError(fe("closing","}"));l.output=Z.escapeLast(l.output,"{"),ue("braces")}if(r.strictSlashes!==!0&&(u.type==="star"||u.type==="bracket")&&x({type:"maybe_slash",value:"",output:`${H}?`}),l.backtrack===!0){l.output="";for(let g of l.tokens)l.output+=g.output!=null?g.output:g.value,g.suffix&&(l.output+=g.suffix)}return l};er.fastpaths=(e,t)=>{let r=k({},t),n=typeof r.maxLength=="number"?Math.min(Le,r.maxLength):Le,s=e.length;if(s>n)throw new SyntaxError(`Input length: ${s}, exceeds maximum allowed length: ${n}`);e=Jt[e]||e;let a=Z.isWindows(t),{DOT_LITERAL:i,SLASH_LITERAL:o,ONE_CHAR:d,DOTS_SLASH:y,NO_DOT:f,NO_DOTS:R,NO_DOTS_SLASH:p,STAR:H,START_ANCHOR:m}=$e.globChars(a),A=r.dot?R:f,E=r.dot?p:f,b=r.capture?"":"?:",O={negated:!1,prefix:""},N=r.bash===!0?".*?":H;r.capture&&(N=`(${N})`);let J=C=>C.noglobstar===!0?N:`(${b}(?:(?!${m}${C.dot?y:i}).)*?)`,G=C=>{switch(C){case"*":return`${A}${d}${N}`;case".*":return`${i}${d}${N}`;case"*.*":return`${A}${N}${i}${d}${N}`;case"*/*":return`${A}${N}${o}${d}${E}${N}`;case"**":return A+J(r);case"**/*":return`(?:${A}${J(r)}${o})?${E}${d}${N}`;case"**/*.*":return`(?:${A}${J(r)}${o})?${E}${N}${i}${d}${N}`;case"**/.*":return`(?:${A}${J(r)}${o})?${i}${d}${N}`;default:{let v=/^(.*?)\.(\w+)$/.exec(C);if(!v)return;let B=G(v[1]);return B?B+i+v[2]:void 0}}},ie=Z.removePrefix(e,O),F=G(ie);return F&&r.strictSlashes!==!0&&(F+=`${o}?`),F};tr.exports=er});var sr=U((fs,nr)=>{"use strict";var Gn=W("path"),Fn=Yt(),We=rr(),Qe=Ae(),Kn=Re(),jn=e=>e&&typeof e=="object"&&!Array.isArray(e),D=(e,t,r=!1)=>{if(Array.isArray(e)){let f=e.map(p=>D(p,t,r));return p=>{for(let H of f){let m=H(p);if(m)return m}return!1}}let n=jn(e)&&e.tokens&&e.input;if(e===""||typeof e!="string"&&!n)throw new TypeError("Expected pattern to be a non-empty string");let s=t||{},a=Qe.isWindows(t),i=n?D.compileRe(e,t):D.makeRe(e,t,!1,!0),o=i.state;delete i.state;let d=()=>!1;if(s.ignore){let f=q(k({},t),{ignore:null,onMatch:null,onResult:null});d=D(s.ignore,f,r)}let y=(f,R=!1)=>{let{isMatch:p,match:H,output:m}=D.test(f,i,t,{glob:e,posix:a}),A={glob:e,state:o,regex:i,posix:a,input:f,output:m,match:H,isMatch:p};return typeof s.onResult=="function"&&s.onResult(A),p===!1?(A.isMatch=!1,R?A:!1):d(f)?(typeof s.onIgnore=="function"&&s.onIgnore(A),A.isMatch=!1,R?A:!1):(typeof s.onMatch=="function"&&s.onMatch(A),R?A:!0)};return r&&(y.state=o),y};D.test=(e,t,r,{glob:n,posix:s}={})=>{if(typeof e!="string")throw new TypeError("Expected input to be a string");if(e==="")return{isMatch:!1,output:""};let a=r||{},i=a.format||(s?Qe.toPosixSlashes:null),o=e===n,d=o&&i?i(e):e;return o===!1&&(d=i?i(e):e,o=d===n),(o===!1||a.capture===!0)&&(a.matchBase===!0||a.basename===!0?o=D.matchBase(e,t,r,s):o=t.exec(d)),{isMatch:Boolean(o),match:o,output:d}};D.matchBase=(e,t,r,n=Qe.isWindows(r))=>(t instanceof RegExp?t:D.makeRe(t,r)).test(Gn.basename(e));D.isMatch=(e,t,r)=>D(t,r)(e);D.parse=(e,t)=>Array.isArray(e)?e.map(r=>D.parse(r,t)):We(e,q(k({},t),{fastpaths:!1}));D.scan=(e,t)=>Fn(e,t);D.compileRe=(e,t,r=!1,n=!1)=>{if(r===!0)return e.output;let s=t||{},a=s.contains?"":"^",i=s.contains?"":"$",o=`${a}(?:${e.output})${i}`;e&&e.negated===!0&&(o=`^(?!${o}).*$`);let d=D.toRegex(o,t);return n===!0&&(d.state=e),d};D.makeRe=(e,t={},r=!1,n=!1)=>{if(!e||typeof e!="string")throw new TypeError("Expected a non-empty string");let s={negated:!1,fastpaths:!0};return t.fastpaths!==!1&&(e[0]==="."||e[0]==="*")&&(s.output=We.fastpaths(e,t)),s.output||(s=We(e,t)),D.compileRe(s,t,r,n)};D.toRegex=(e,t)=>{try{let r=t||{};return new RegExp(e,r.flags||(r.nocase?"i":""))}catch(r){if(t&&t.debug===!0)throw r;return/$^/}};D.constants=Kn;nr.exports=D});var ir=U((hs,ar)=>{"use strict";ar.exports=sr()});var pr=U((ds,lr)=>{"use strict";var or=W("util"),ur=Bt(),se=ir(),Xe=Ae(),cr=e=>e===""||e==="./",L=(e,t,r)=>{t=[].concat(t),e=[].concat(e);let n=new Set,s=new Set,a=new Set,i=0,o=f=>{a.add(f.output),r&&r.onResult&&r.onResult(f)};for(let f=0;f!n.has(f));if(r&&y.length===0){if(r.failglob===!0)throw new Error(`No matches found for "${t.join(", ")}"`);if(r.nonull===!0||r.nullglob===!0)return r.unescape?t.map(f=>f.replace(/\\/g,"")):t}return y};L.match=L;L.matcher=(e,t)=>se(e,t);L.isMatch=(e,t,r)=>se(t,r)(e);L.any=L.isMatch;L.not=(e,t,r={})=>{t=[].concat(t).map(String);let n=new Set,s=[],a=o=>{r.onResult&&r.onResult(o),s.push(o.output)},i=L(e,t,q(k({},r),{onResult:a}));for(let o of s)i.includes(o)||n.add(o);return[...n]};L.contains=(e,t,r)=>{if(typeof e!="string")throw new TypeError(`Expected a string: "${or.inspect(e)}"`);if(Array.isArray(t))return t.some(n=>L.contains(e,n,r));if(typeof t=="string"){if(cr(e)||cr(t))return!1;if(e.includes(t)||e.startsWith("./")&&e.slice(2).includes(t))return!0}return L.isMatch(e,t,q(k({},r),{contains:!0}))};L.matchKeys=(e,t,r)=>{if(!Xe.isObject(e))throw new TypeError("Expected the first argument to be an object");let n=L(Object.keys(e),t,r),s={};for(let a of n)s[a]=e[a];return s};L.some=(e,t,r)=>{let n=[].concat(e);for(let s of[].concat(t)){let a=se(String(s),r);if(n.some(i=>a(i)))return!0}return!1};L.every=(e,t,r)=>{let n=[].concat(e);for(let s of[].concat(t)){let a=se(String(s),r);if(!n.every(i=>a(i)))return!1}return!0};L.all=(e,t,r)=>{if(typeof e!="string")throw new TypeError(`Expected a string: "${or.inspect(e)}"`);return[].concat(t).every(n=>se(n,r)(e))};L.capture=(e,t,r)=>{let n=Xe.isWindows(r),a=se.makeRe(String(e),q(k({},r),{capture:!0})).exec(n?Xe.toPosixSlashes(t):t);if(a)return a.slice(1).map(i=>i===void 0?"":i)};L.makeRe=(...e)=>se.makeRe(...e);L.scan=(...e)=>se.scan(...e);L.parse=(e,t)=>{let r=[];for(let n of[].concat(e||[]))for(let s of ur(String(n),t))r.push(se.parse(s,t));return r};L.braces=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");return t&&t.nobrace===!0||!/\{.*\}/.test(e)?[e]:ur(e,t)};L.braceExpand=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");return L.braces(e,q(k({},t),{expand:!0}))};lr.exports=L});var Zn={};wr(Zn,{default:()=>zn});var Oe=re(W("@yarnpkg/cli")),P=re(W("@yarnpkg/core")),Y=re(W("clipanion")),Rr=re(pr()),Ye=re(W("semver")),Je=re(W("typanion"));var he=re(W("@yarnpkg/core")),gr=re(W("@yarnpkg/plugin-essentials"));var Ce=re(W("semver")),fr=Boolean;function qn(e){var s;let[t,r,n]=(s=e.match(/(github|bitbucket|gitlab):(.+)/))!=null?s:[];return r?`https://${r}.${r==="bitbucket"?"org":"com"}/${n}`:`https://github.com/${e}`}function hr(e){let{homepage:t,repository:r}=e.raw;return t||(typeof r=="string"?qn(r):r==null?void 0:r.url)}function dr(e,t){return Ce.default.parse(t).prerelease.length?Ce.default.lt(e,t):Ce.default.lt(Ce.default.coerce(e),t)}var ze=class{constructor(t,r,n,s){this.configuration=t;this.project=r;this.workspace=n;this.cache=s}async fetch({pkg:t,range:r,url:n}){let s=gr.suggestUtils.fetchDescriptorFrom(t,r,{cache:this.cache,preserveModifier:!1,project:this.project,workspace:this.workspace}),a=n?this.fetchURL(t):Promise.resolve(void 0),[i,o]=await Promise.all([s,a]);if(!i){let d=he.structUtils.prettyIdent(this.configuration,t);throw new Error(`Could not fetch candidate for ${d}.`)}return{url:o,version:i.range}}async fetchURL(t){var a;let r=this.configuration.makeFetcher(),n=await r.fetch(t,{cache:this.cache,checksums:this.project.storedChecksums,fetcher:r,project:this.project,report:new he.ThrowReport,skipIntegrityCheck:!0}),s;try{s=await he.Manifest.find(n.prefixPath,{baseFs:n.packageFs})}finally{(a=n.releaseFs)==null||a.call(n)}return hr(s)}};var de=re(W("@yarnpkg/core")),Wn=/^([0-9]+\.)([0-9]+\.)(.+)$/,Qn=["name","current","latest","workspace","type","url"],Ze=class{constructor(t,r,n,s){this.report=t;this.configuration=r;this.dependencies=n;this.extraColumns=s;this.sizes=null;this.headers={current:"Current",latest:"Latest",name:"Package",type:"Package Type",url:"URL",workspace:"Workspace"}}print(){this.sizes=this.getColumnSizes(),this.printHeader(),this.dependencies.forEach(t=>{var n,s;let r=this.getDiffColor(t);this.printRow({current:t.current.padEnd(this.sizes.current),latest:this.formatVersion(t,"latest",r),name:this.applyColor(t.name.padEnd(this.sizes.name),r),type:t.type.padEnd(this.sizes.type),url:(n=t.url)==null?void 0:n.padEnd(this.sizes.url),workspace:(s=t.workspace)==null?void 0:s.padEnd(this.sizes.workspace)})})}applyColor(t,r){return de.formatUtils.pretty(this.configuration,t,r)}formatVersion(t,r,n){let s=t[r].padEnd(this.sizes[r]),a=s.match(Wn);if(!a)return s;let i=["red","yellow","green"].indexOf(n)+1,o=a.slice(1,i).join(""),d=a.slice(i).join("");return o+de.formatUtils.pretty(this.configuration,this.applyColor(d,n),"bold")}getDiffColor(t){return{major:"red",minor:"yellow",patch:"green"}[t.severity]}getColumnSizes(){let t={current:this.headers.current.length,latest:this.headers.latest.length,name:this.headers.name.length,type:this.headers.type.length,url:this.headers.url.length,workspace:this.headers.workspace.length};for(let r of this.dependencies)for(let[n,s]of Object.entries(r)){let a=t[n],i=(s||"").length;t[n]=a>i?a:i}return t}formatColumnHeader(t){return de.formatUtils.pretty(this.configuration,this.headers[t].padEnd(this.sizes[t]),"bold")}printHeader(){this.printRow({current:this.formatColumnHeader("current"),latest:this.formatColumnHeader("latest"),name:this.formatColumnHeader("name"),type:this.formatColumnHeader("type"),url:this.formatColumnHeader("url"),workspace:this.formatColumnHeader("workspace")})}printRow(t){let r=Qn.filter(n=>{var s;return(s=this.extraColumns[n])!=null?s:!0}).map(n=>t[n]).join(" ").trim();this.report.reportInfo(de.MessageName.UNNAMED,r)}};var Ve=["dependencies","devDependencies"],yr=["major","minor","patch"];var Ee=class extends Oe.BaseCommand{constructor(){super(...arguments);this.patterns=Y.Option.Rest();this.all=Y.Option.Boolean("-a,--all",!1,{description:"Include outdated dependencies from all workspaces"});this.check=Y.Option.Boolean("-c,--check",!1,{description:"Exit with exit code 1 when outdated dependencies are found"});this.json=Y.Option.Boolean("--json",!1,{description:"Format the output as JSON"});this.severity=Y.Option.String("-s,--severity",{description:"Filter results based on the severity of the update",validator:Je.default.isEnum(yr)});this.type=Y.Option.String("-t,--type",{description:"Filter results based on the dependency type",validator:Je.default.isEnum(Ve)});this.url=Y.Option.Boolean("--url",!1,{description:"Include the homepage URL of each package in the output"})}async execute(){let{cache:t,configuration:r,project:n,workspace:s}=await this.loadProject(),a=new ze(r,n,s,t),i=this.getWorkspaces(n,s),o=this.getDependencies(r,i);if(this.json){let y=await this.getOutdatedDependencies(a,o);this.context.stdout.write(JSON.stringify(y)+` +`);return}return(await P.StreamReport.start({configuration:r,stdout:this.context.stdout},async y=>{await this.checkOutdatedDependencies(r,o,a,y)})).exitCode()}async checkOutdatedDependencies(t,r,n,s){let a=null;await s.startTimerPromise("Checking for outdated dependencies",async()=>{let i=r.length,o=P.StreamReport.progressViaCounter(i);s.reportProgress(o),a=await this.getOutdatedDependencies(n,r,o)}),s.reportSeparator(),a.length?(new Ze(s,t,a,{url:this.url,workspace:this.all}).print(),s.reportSeparator(),this.printOutdatedCount(s,a.length)):this.printUpToDate(t,s)}async loadProject(){let t=await P.Configuration.find(this.context.cwd,this.context.plugins),[r,{project:n,workspace:s}]=await Promise.all([P.Cache.find(t),P.Project.find(t,this.context.cwd)]);if(await n.restoreInstallState(),!s)throw new Oe.WorkspaceRequiredError(n.cwd,this.context.cwd);return{cache:r,configuration:t,project:n,workspace:s}}getWorkspaces(t,r){return this.all?t.workspaces:[r]}get dependencyTypes(){return this.type?[this.type]:Ve}getDependencies(t,r){let n=[];for(let a of r){let{anchoredLocator:i,project:o}=a,d=o.storedPackages.get(i.locatorHash);d||this.throw(t,i);for(let y of this.dependencyTypes)for(let f of a.manifest[y].values()){let{range:R}=f;if(R.includes(":")&&!R.startsWith("npm:"))continue;let p=d.dependencies.get(f.identHash);p||this.throw(t,f);let H=o.storedResolutions.get(p.descriptorHash);H||this.throw(t,p);let m=o.storedPackages.get(H);m||this.throw(t,p),n.push({dependencyType:y,name:P.structUtils.stringifyIdent(f),pkg:m,workspace:a})}}if(!this.patterns.length)return n;let s=n.filter(({name:a})=>Rr.default.isMatch(a,this.patterns));if(!s.length)throw new Y.UsageError(`Pattern ${P.formatUtils.prettyList(t,this.patterns,P.FormatType.CODE)} doesn't match any packages referenced by any workspace`);return s}throw(t,r){let n=P.structUtils.prettyIdent(t,r);throw new Error(`Package for ${n} not found in the project`)}getSeverity(t,r){let n=Ye.default.coerce(t),s=Ye.default.coerce(r);return s.major>n.major?"major":s.minor>n.minor?"minor":"patch"}async getOutdatedDependencies(t,r,n){let s=r.map(async({dependencyType:a,name:i,pkg:o,workspace:d})=>{if(d.project.tryWorkspaceByLocator(o))return;let{url:y,version:f}=await t.fetch({pkg:o,range:"latest",url:this.url});if(n==null||n.tick(),dr(o.version,f))return{current:o.version,latest:f,name:i,severity:this.getSeverity(o.version,f),type:a,url:y,workspace:this.all?this.getWorkspaceName(d):void 0}});return(await Promise.all(s)).filter(fr).filter(({severity:a})=>!this.severity||a===this.severity).sort((a,i)=>a.name.localeCompare(i.name))}getWorkspaceName(t){return t.manifest.name?P.structUtils.stringifyIdent(t.manifest.name):t.computeCandidateName()}printOutdatedCount(t,r){let n=[P.MessageName.UNNAMED,r===1?"1 dependency is out of date":`${r} dependencies are out of date`];this.check?t.reportError(...n):t.reportWarning(...n)}printUpToDate(t,r){let n="\u2728 All your dependencies are up to date!";r.reportInfo(P.MessageName.UNNAMED,P.formatUtils.pretty(t,n,"green"))}};Ee.paths=[["outdated"]],Ee.usage=Y.Command.Usage({description:"view outdated dependencies",details:` + This command finds outdated dependencies in a project and prints the result in a table or JSON format. + + This command accepts glob patterns as arguments to filter the output. Make sure to escape the patterns, to prevent your own shell from trying to expand them. + `,examples:[["View outdated dependencies","yarn outdated"],["View outdated dependencies with the `@babel` scope","yarn outdated '@babel/*'"],["Filter results to only include devDependencies","yarn outdated --type devDependencies"],["Filter results to only include major version updates","yarn outdated --severity major"]]});var Xn={commands:[Ee]},zn=Xn;return Zn;})(); +/*! + * fill-range + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Licensed under the MIT License. + */ +/*! + * is-number + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Released under the MIT License. + */ +/*! + * to-regex-range + * + * Copyright (c) 2015-present, Jon Schlinkert. + * Released under the MIT License. + */ +return plugin; +} +}; diff --git a/.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs b/.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs new file mode 100644 index 00000000000..800a0e23460 --- /dev/null +++ b/.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs @@ -0,0 +1,28 @@ +/* eslint-disable */ +//prettier-ignore +module.exports = { +name: "@yarnpkg/plugin-workspace-tools", +factory: function (require) { +var plugin=(()=>{var wr=Object.create,ge=Object.defineProperty,Sr=Object.defineProperties,vr=Object.getOwnPropertyDescriptor,Hr=Object.getOwnPropertyDescriptors,$r=Object.getOwnPropertyNames,Je=Object.getOwnPropertySymbols,kr=Object.getPrototypeOf,et=Object.prototype.hasOwnProperty,Tr=Object.prototype.propertyIsEnumerable;var tt=(e,t,r)=>t in e?ge(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,I=(e,t)=>{for(var r in t||(t={}))et.call(t,r)&&tt(e,r,t[r]);if(Je)for(var r of Je(t))Tr.call(t,r)&&tt(e,r,t[r]);return e},F=(e,t)=>Sr(e,Hr(t)),Lr=e=>ge(e,"__esModule",{value:!0});var K=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Or=(e,t)=>{for(var r in t)ge(e,r,{get:t[r],enumerable:!0})},Nr=(e,t,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of $r(t))!et.call(e,n)&&n!=="default"&&ge(e,n,{get:()=>t[n],enumerable:!(r=vr(t,n))||r.enumerable});return e},Q=e=>Nr(Lr(ge(e!=null?wr(kr(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);var He=K(ee=>{"use strict";ee.isInteger=e=>typeof e=="number"?Number.isInteger(e):typeof e=="string"&&e.trim()!==""?Number.isInteger(Number(e)):!1;ee.find=(e,t)=>e.nodes.find(r=>r.type===t);ee.exceedsLimit=(e,t,r=1,n)=>n===!1||!ee.isInteger(e)||!ee.isInteger(t)?!1:(Number(t)-Number(e))/Number(r)>=n;ee.escapeNode=(e,t=0,r)=>{let n=e.nodes[t];!n||(r&&n.type===r||n.type==="open"||n.type==="close")&&n.escaped!==!0&&(n.value="\\"+n.value,n.escaped=!0)};ee.encloseBrace=e=>e.type!=="brace"?!1:e.commas>>0+e.ranges>>0==0?(e.invalid=!0,!0):!1;ee.isInvalidBrace=e=>e.type!=="brace"?!1:e.invalid===!0||e.dollar?!0:e.commas>>0+e.ranges>>0==0||e.open!==!0||e.close!==!0?(e.invalid=!0,!0):!1;ee.isOpenOrClose=e=>e.type==="open"||e.type==="close"?!0:e.open===!0||e.close===!0;ee.reduce=e=>e.reduce((t,r)=>(r.type==="text"&&t.push(r.value),r.type==="range"&&(r.type="text"),t),[]);ee.flatten=(...e)=>{let t=[],r=n=>{for(let s=0;s{"use strict";var at=He();st.exports=(e,t={})=>{let r=(n,s={})=>{let a=t.escapeInvalid&&at.isInvalidBrace(s),i=n.invalid===!0&&t.escapeInvalid===!0,o="";if(n.value)return(a||i)&&at.isOpenOrClose(n)?"\\"+n.value:n.value;if(n.value)return n.value;if(n.nodes)for(let h of n.nodes)o+=r(h);return o};return r(e)}});var ot=K((os,it)=>{"use strict";it.exports=function(e){return typeof e=="number"?e-e==0:typeof e=="string"&&e.trim()!==""?Number.isFinite?Number.isFinite(+e):isFinite(+e):!1}});var mt=K((us,ut)=>{"use strict";var ct=ot(),pe=(e,t,r)=>{if(ct(e)===!1)throw new TypeError("toRegexRange: expected the first argument to be a number");if(t===void 0||e===t)return String(e);if(ct(t)===!1)throw new TypeError("toRegexRange: expected the second argument to be a number.");let n=I({relaxZeros:!0},r);typeof n.strictZeros=="boolean"&&(n.relaxZeros=n.strictZeros===!1);let s=String(n.relaxZeros),a=String(n.shorthand),i=String(n.capture),o=String(n.wrap),h=e+":"+t+"="+s+a+i+o;if(pe.cache.hasOwnProperty(h))return pe.cache[h].result;let m=Math.min(e,t),f=Math.max(e,t);if(Math.abs(m-f)===1){let y=e+"|"+t;return n.capture?`(${y})`:n.wrap===!1?y:`(?:${y})`}let R=pt(e)||pt(t),p={min:e,max:t,a:m,b:f},v=[],_=[];if(R&&(p.isPadded=R,p.maxLen=String(p.max).length),m<0){let y=f<0?Math.abs(f):1;_=lt(y,Math.abs(m),p,n),m=p.a=0}return f>=0&&(v=lt(m,f,p,n)),p.negatives=_,p.positives=v,p.result=Ir(_,v,n),n.capture===!0?p.result=`(${p.result})`:n.wrap!==!1&&v.length+_.length>1&&(p.result=`(?:${p.result})`),pe.cache[h]=p,p.result};function Ir(e,t,r){let n=Pe(e,t,"-",!1,r)||[],s=Pe(t,e,"",!1,r)||[],a=Pe(e,t,"-?",!0,r)||[];return n.concat(a).concat(s).join("|")}function Mr(e,t){let r=1,n=1,s=ft(e,r),a=new Set([t]);for(;e<=s&&s<=t;)a.add(s),r+=1,s=ft(e,r);for(s=ht(t+1,n)-1;e1&&o.count.pop(),o.count.push(f.count[0]),o.string=o.pattern+dt(o.count),i=m+1;continue}r.isPadded&&(R=Ur(m,r,n)),f.string=R+f.pattern+dt(f.count),a.push(f),i=m+1,o=f}return a}function Pe(e,t,r,n,s){let a=[];for(let i of e){let{string:o}=i;!n&&!gt(t,"string",o)&&a.push(r+o),n&>(t,"string",o)&&a.push(r+o)}return a}function Pr(e,t){let r=[];for(let n=0;nt?1:t>e?-1:0}function gt(e,t,r){return e.some(n=>n[t]===r)}function ft(e,t){return Number(String(e).slice(0,-t)+"9".repeat(t))}function ht(e,t){return e-e%Math.pow(10,t)}function dt(e){let[t=0,r=""]=e;return r||t>1?`{${t+(r?","+r:"")}}`:""}function Dr(e,t,r){return`[${e}${t-e==1?"":"-"}${t}]`}function pt(e){return/^-?(0+)\d/.test(e)}function Ur(e,t,r){if(!t.isPadded)return e;let n=Math.abs(t.maxLen-String(e).length),s=r.relaxZeros!==!1;switch(n){case 0:return"";case 1:return s?"0?":"0";case 2:return s?"0{0,2}":"00";default:return s?`0{0,${n}}`:`0{${n}}`}}pe.cache={};pe.clearCache=()=>pe.cache={};ut.exports=pe});var Ue=K((cs,At)=>{"use strict";var qr=require("util"),Rt=mt(),yt=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),Kr=e=>t=>e===!0?Number(t):String(t),De=e=>typeof e=="number"||typeof e=="string"&&e!=="",Ae=e=>Number.isInteger(+e),Ge=e=>{let t=`${e}`,r=-1;if(t[0]==="-"&&(t=t.slice(1)),t==="0")return!1;for(;t[++r]==="0";);return r>0},Wr=(e,t,r)=>typeof e=="string"||typeof t=="string"?!0:r.stringify===!0,jr=(e,t,r)=>{if(t>0){let n=e[0]==="-"?"-":"";n&&(e=e.slice(1)),e=n+e.padStart(n?t-1:t,"0")}return r===!1?String(e):e},_t=(e,t)=>{let r=e[0]==="-"?"-":"";for(r&&(e=e.slice(1),t--);e.length{e.negatives.sort((i,o)=>io?1:0),e.positives.sort((i,o)=>io?1:0);let r=t.capture?"":"?:",n="",s="",a;return e.positives.length&&(n=e.positives.join("|")),e.negatives.length&&(s=`-(${r}${e.negatives.join("|")})`),n&&s?a=`${n}|${s}`:a=n||s,t.wrap?`(${r}${a})`:a},bt=(e,t,r,n)=>{if(r)return Rt(e,t,I({wrap:!1},n));let s=String.fromCharCode(e);if(e===t)return s;let a=String.fromCharCode(t);return`[${s}-${a}]`},Et=(e,t,r)=>{if(Array.isArray(e)){let n=r.wrap===!0,s=r.capture?"":"?:";return n?`(${s}${e.join("|")})`:e.join("|")}return Rt(e,t,r)},xt=(...e)=>new RangeError("Invalid range arguments: "+qr.inspect(...e)),Ct=(e,t,r)=>{if(r.strictRanges===!0)throw xt([e,t]);return[]},Qr=(e,t)=>{if(t.strictRanges===!0)throw new TypeError(`Expected step "${e}" to be a number`);return[]},Xr=(e,t,r=1,n={})=>{let s=Number(e),a=Number(t);if(!Number.isInteger(s)||!Number.isInteger(a)){if(n.strictRanges===!0)throw xt([e,t]);return[]}s===0&&(s=0),a===0&&(a=0);let i=s>a,o=String(e),h=String(t),m=String(r);r=Math.max(Math.abs(r),1);let f=Ge(o)||Ge(h)||Ge(m),R=f?Math.max(o.length,h.length,m.length):0,p=f===!1&&Wr(e,t,n)===!1,v=n.transform||Kr(p);if(n.toRegex&&r===1)return bt(_t(e,R),_t(t,R),!0,n);let _={negatives:[],positives:[]},y=H=>_[H<0?"negatives":"positives"].push(Math.abs(H)),b=[],E=0;for(;i?s>=a:s<=a;)n.toRegex===!0&&r>1?y(s):b.push(jr(v(s,E),R,p)),s=i?s-r:s+r,E++;return n.toRegex===!0?r>1?Fr(_,n):Et(b,null,I({wrap:!1},n)):b},Zr=(e,t,r=1,n={})=>{if(!Ae(e)&&e.length>1||!Ae(t)&&t.length>1)return Ct(e,t,n);let s=n.transform||(p=>String.fromCharCode(p)),a=`${e}`.charCodeAt(0),i=`${t}`.charCodeAt(0),o=a>i,h=Math.min(a,i),m=Math.max(a,i);if(n.toRegex&&r===1)return bt(h,m,!1,n);let f=[],R=0;for(;o?a>=i:a<=i;)f.push(s(a,R)),a=o?a-r:a+r,R++;return n.toRegex===!0?Et(f,null,{wrap:!1,options:n}):f},ke=(e,t,r,n={})=>{if(t==null&&De(e))return[e];if(!De(e)||!De(t))return Ct(e,t,n);if(typeof r=="function")return ke(e,t,1,{transform:r});if(yt(r))return ke(e,t,0,r);let s=I({},n);return s.capture===!0&&(s.wrap=!0),r=r||s.step||1,Ae(r)?Ae(e)&&Ae(t)?Xr(e,t,r,s):Zr(e,t,Math.max(Math.abs(r),1),s):r!=null&&!yt(r)?Qr(r,s):ke(e,t,1,r)};At.exports=ke});var vt=K((ls,wt)=>{"use strict";var Yr=Ue(),St=He(),zr=(e,t={})=>{let r=(n,s={})=>{let a=St.isInvalidBrace(s),i=n.invalid===!0&&t.escapeInvalid===!0,o=a===!0||i===!0,h=t.escapeInvalid===!0?"\\":"",m="";if(n.isOpen===!0||n.isClose===!0)return h+n.value;if(n.type==="open")return o?h+n.value:"(";if(n.type==="close")return o?h+n.value:")";if(n.type==="comma")return n.prev.type==="comma"?"":o?n.value:"|";if(n.value)return n.value;if(n.nodes&&n.ranges>0){let f=St.reduce(n.nodes),R=Yr(...f,F(I({},t),{wrap:!1,toRegex:!0}));if(R.length!==0)return f.length>1&&R.length>1?`(${R})`:R}if(n.nodes)for(let f of n.nodes)m+=r(f,n);return m};return r(e)};wt.exports=zr});var kt=K((ps,Ht)=>{"use strict";var Vr=Ue(),$t=$e(),he=He(),fe=(e="",t="",r=!1)=>{let n=[];if(e=[].concat(e),t=[].concat(t),!t.length)return e;if(!e.length)return r?he.flatten(t).map(s=>`{${s}}`):t;for(let s of e)if(Array.isArray(s))for(let a of s)n.push(fe(a,t,r));else for(let a of t)r===!0&&typeof a=="string"&&(a=`{${a}}`),n.push(Array.isArray(a)?fe(s,a,r):s+a);return he.flatten(n)},Jr=(e,t={})=>{let r=t.rangeLimit===void 0?1e3:t.rangeLimit,n=(s,a={})=>{s.queue=[];let i=a,o=a.queue;for(;i.type!=="brace"&&i.type!=="root"&&i.parent;)i=i.parent,o=i.queue;if(s.invalid||s.dollar){o.push(fe(o.pop(),$t(s,t)));return}if(s.type==="brace"&&s.invalid!==!0&&s.nodes.length===2){o.push(fe(o.pop(),["{}"]));return}if(s.nodes&&s.ranges>0){let R=he.reduce(s.nodes);if(he.exceedsLimit(...R,t.step,r))throw new RangeError("expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.");let p=Vr(...R,t);p.length===0&&(p=$t(s,t)),o.push(fe(o.pop(),p)),s.nodes=[];return}let h=he.encloseBrace(s),m=s.queue,f=s;for(;f.type!=="brace"&&f.type!=="root"&&f.parent;)f=f.parent,m=f.queue;for(let R=0;R{"use strict";Tt.exports={MAX_LENGTH:1024*64,CHAR_0:"0",CHAR_9:"9",CHAR_UPPERCASE_A:"A",CHAR_LOWERCASE_A:"a",CHAR_UPPERCASE_Z:"Z",CHAR_LOWERCASE_Z:"z",CHAR_LEFT_PARENTHESES:"(",CHAR_RIGHT_PARENTHESES:")",CHAR_ASTERISK:"*",CHAR_AMPERSAND:"&",CHAR_AT:"@",CHAR_BACKSLASH:"\\",CHAR_BACKTICK:"`",CHAR_CARRIAGE_RETURN:"\r",CHAR_CIRCUMFLEX_ACCENT:"^",CHAR_COLON:":",CHAR_COMMA:",",CHAR_DOLLAR:"$",CHAR_DOT:".",CHAR_DOUBLE_QUOTE:'"',CHAR_EQUAL:"=",CHAR_EXCLAMATION_MARK:"!",CHAR_FORM_FEED:"\f",CHAR_FORWARD_SLASH:"/",CHAR_HASH:"#",CHAR_HYPHEN_MINUS:"-",CHAR_LEFT_ANGLE_BRACKET:"<",CHAR_LEFT_CURLY_BRACE:"{",CHAR_LEFT_SQUARE_BRACKET:"[",CHAR_LINE_FEED:` +`,CHAR_NO_BREAK_SPACE:"\xA0",CHAR_PERCENT:"%",CHAR_PLUS:"+",CHAR_QUESTION_MARK:"?",CHAR_RIGHT_ANGLE_BRACKET:">",CHAR_RIGHT_CURLY_BRACE:"}",CHAR_RIGHT_SQUARE_BRACKET:"]",CHAR_SEMICOLON:";",CHAR_SINGLE_QUOTE:"'",CHAR_SPACE:" ",CHAR_TAB:" ",CHAR_UNDERSCORE:"_",CHAR_VERTICAL_LINE:"|",CHAR_ZERO_WIDTH_NOBREAK_SPACE:"\uFEFF"}});var Mt=K((hs,Ot)=>{"use strict";var en=$e(),{MAX_LENGTH:Nt,CHAR_BACKSLASH:qe,CHAR_BACKTICK:tn,CHAR_COMMA:rn,CHAR_DOT:nn,CHAR_LEFT_PARENTHESES:sn,CHAR_RIGHT_PARENTHESES:an,CHAR_LEFT_CURLY_BRACE:on,CHAR_RIGHT_CURLY_BRACE:un,CHAR_LEFT_SQUARE_BRACKET:It,CHAR_RIGHT_SQUARE_BRACKET:Bt,CHAR_DOUBLE_QUOTE:cn,CHAR_SINGLE_QUOTE:ln,CHAR_NO_BREAK_SPACE:pn,CHAR_ZERO_WIDTH_NOBREAK_SPACE:fn}=Lt(),hn=(e,t={})=>{if(typeof e!="string")throw new TypeError("Expected a string");let r=t||{},n=typeof r.maxLength=="number"?Math.min(Nt,r.maxLength):Nt;if(e.length>n)throw new SyntaxError(`Input length (${e.length}), exceeds max characters (${n})`);let s={type:"root",input:e,nodes:[]},a=[s],i=s,o=s,h=0,m=e.length,f=0,R=0,p,v={},_=()=>e[f++],y=b=>{if(b.type==="text"&&o.type==="dot"&&(o.type="text"),o&&o.type==="text"&&b.type==="text"){o.value+=b.value;return}return i.nodes.push(b),b.parent=i,b.prev=o,o=b,b};for(y({type:"bos"});f0){if(i.ranges>0){i.ranges=0;let b=i.nodes.shift();i.nodes=[b,{type:"text",value:en(i)}]}y({type:"comma",value:p}),i.commas++;continue}if(p===nn&&R>0&&i.commas===0){let b=i.nodes;if(R===0||b.length===0){y({type:"text",value:p});continue}if(o.type==="dot"){if(i.range=[],o.value+=p,o.type="range",i.nodes.length!==3&&i.nodes.length!==5){i.invalid=!0,i.ranges=0,o.type="text";continue}i.ranges++,i.args=[];continue}if(o.type==="range"){b.pop();let E=b[b.length-1];E.value+=o.value+p,o=E,i.ranges--;continue}y({type:"dot",value:p});continue}y({type:"text",value:p})}do if(i=a.pop(),i.type!=="root"){i.nodes.forEach(H=>{H.nodes||(H.type==="open"&&(H.isOpen=!0),H.type==="close"&&(H.isClose=!0),H.nodes||(H.type="text"),H.invalid=!0)});let b=a[a.length-1],E=b.nodes.indexOf(i);b.nodes.splice(E,1,...i.nodes)}while(a.length>0);return y({type:"eos"}),s};Ot.exports=hn});var Gt=K((ds,Pt)=>{"use strict";var Dt=$e(),dn=vt(),gn=kt(),mn=Mt(),z=(e,t={})=>{let r=[];if(Array.isArray(e))for(let n of e){let s=z.create(n,t);Array.isArray(s)?r.push(...s):r.push(s)}else r=[].concat(z.create(e,t));return t&&t.expand===!0&&t.nodupes===!0&&(r=[...new Set(r)]),r};z.parse=(e,t={})=>mn(e,t);z.stringify=(e,t={})=>typeof e=="string"?Dt(z.parse(e,t),t):Dt(e,t);z.compile=(e,t={})=>(typeof e=="string"&&(e=z.parse(e,t)),dn(e,t));z.expand=(e,t={})=>{typeof e=="string"&&(e=z.parse(e,t));let r=gn(e,t);return t.noempty===!0&&(r=r.filter(Boolean)),t.nodupes===!0&&(r=[...new Set(r)]),r};z.create=(e,t={})=>e===""||e.length<3?[e]:t.expand!==!0?z.compile(e,t):z.expand(e,t);Pt.exports=z});var Re=K((gs,Ut)=>{"use strict";var An=require("path"),se="\\\\/",qt=`[^${se}]`,ue="\\.",Rn="\\+",yn="\\?",Te="\\/",_n="(?=.)",Kt="[^/]",Ke=`(?:${Te}|$)`,Wt=`(?:^|${Te})`,We=`${ue}{1,2}${Ke}`,bn=`(?!${ue})`,En=`(?!${Wt}${We})`,xn=`(?!${ue}{0,1}${Ke})`,Cn=`(?!${We})`,wn=`[^.${Te}]`,Sn=`${Kt}*?`,jt={DOT_LITERAL:ue,PLUS_LITERAL:Rn,QMARK_LITERAL:yn,SLASH_LITERAL:Te,ONE_CHAR:_n,QMARK:Kt,END_ANCHOR:Ke,DOTS_SLASH:We,NO_DOT:bn,NO_DOTS:En,NO_DOT_SLASH:xn,NO_DOTS_SLASH:Cn,QMARK_NO_DOT:wn,STAR:Sn,START_ANCHOR:Wt},vn=F(I({},jt),{SLASH_LITERAL:`[${se}]`,QMARK:qt,STAR:`${qt}*?`,DOTS_SLASH:`${ue}{1,2}(?:[${se}]|$)`,NO_DOT:`(?!${ue})`,NO_DOTS:`(?!(?:^|[${se}])${ue}{1,2}(?:[${se}]|$))`,NO_DOT_SLASH:`(?!${ue}{0,1}(?:[${se}]|$))`,NO_DOTS_SLASH:`(?!${ue}{1,2}(?:[${se}]|$))`,QMARK_NO_DOT:`[^.${se}]`,START_ANCHOR:`(?:^|[${se}])`,END_ANCHOR:`(?:[${se}]|$)`}),Hn={alnum:"a-zA-Z0-9",alpha:"a-zA-Z",ascii:"\\x00-\\x7F",blank:" \\t",cntrl:"\\x00-\\x1F\\x7F",digit:"0-9",graph:"\\x21-\\x7E",lower:"a-z",print:"\\x20-\\x7E ",punct:"\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~",space:" \\t\\r\\n\\v\\f",upper:"A-Z",word:"A-Za-z0-9_",xdigit:"A-Fa-f0-9"};Ut.exports={MAX_LENGTH:1024*64,POSIX_REGEX_SOURCE:Hn,REGEX_BACKSLASH:/\\(?![*+?^${}(|)[\]])/g,REGEX_NON_SPECIAL_CHARS:/^[^@![\].,$*+?^{}()|\\/]+/,REGEX_SPECIAL_CHARS:/[-*+?.^${}(|)[\]]/,REGEX_SPECIAL_CHARS_BACKREF:/(\\?)((\W)(\3*))/g,REGEX_SPECIAL_CHARS_GLOBAL:/([-*+?.^${}(|)[\]])/g,REGEX_REMOVE_BACKSLASH:/(?:\[.*?[^\\]\]|\\(?=.))/g,REPLACEMENTS:{"***":"*","**/**":"**","**/**/**":"**"},CHAR_0:48,CHAR_9:57,CHAR_UPPERCASE_A:65,CHAR_LOWERCASE_A:97,CHAR_UPPERCASE_Z:90,CHAR_LOWERCASE_Z:122,CHAR_LEFT_PARENTHESES:40,CHAR_RIGHT_PARENTHESES:41,CHAR_ASTERISK:42,CHAR_AMPERSAND:38,CHAR_AT:64,CHAR_BACKWARD_SLASH:92,CHAR_CARRIAGE_RETURN:13,CHAR_CIRCUMFLEX_ACCENT:94,CHAR_COLON:58,CHAR_COMMA:44,CHAR_DOT:46,CHAR_DOUBLE_QUOTE:34,CHAR_EQUAL:61,CHAR_EXCLAMATION_MARK:33,CHAR_FORM_FEED:12,CHAR_FORWARD_SLASH:47,CHAR_GRAVE_ACCENT:96,CHAR_HASH:35,CHAR_HYPHEN_MINUS:45,CHAR_LEFT_ANGLE_BRACKET:60,CHAR_LEFT_CURLY_BRACE:123,CHAR_LEFT_SQUARE_BRACKET:91,CHAR_LINE_FEED:10,CHAR_NO_BREAK_SPACE:160,CHAR_PERCENT:37,CHAR_PLUS:43,CHAR_QUESTION_MARK:63,CHAR_RIGHT_ANGLE_BRACKET:62,CHAR_RIGHT_CURLY_BRACE:125,CHAR_RIGHT_SQUARE_BRACKET:93,CHAR_SEMICOLON:59,CHAR_SINGLE_QUOTE:39,CHAR_SPACE:32,CHAR_TAB:9,CHAR_UNDERSCORE:95,CHAR_VERTICAL_LINE:124,CHAR_ZERO_WIDTH_NOBREAK_SPACE:65279,SEP:An.sep,extglobChars(e){return{"!":{type:"negate",open:"(?:(?!(?:",close:`))${e.STAR})`},"?":{type:"qmark",open:"(?:",close:")?"},"+":{type:"plus",open:"(?:",close:")+"},"*":{type:"star",open:"(?:",close:")*"},"@":{type:"at",open:"(?:",close:")"}}},globChars(e){return e===!0?vn:jt}}});var ye=K(X=>{"use strict";var $n=require("path"),kn=process.platform==="win32",{REGEX_BACKSLASH:Tn,REGEX_REMOVE_BACKSLASH:Ln,REGEX_SPECIAL_CHARS:On,REGEX_SPECIAL_CHARS_GLOBAL:Nn}=Re();X.isObject=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);X.hasRegexChars=e=>On.test(e);X.isRegexChar=e=>e.length===1&&X.hasRegexChars(e);X.escapeRegex=e=>e.replace(Nn,"\\$1");X.toPosixSlashes=e=>e.replace(Tn,"/");X.removeBackslashes=e=>e.replace(Ln,t=>t==="\\"?"":t);X.supportsLookbehinds=()=>{let e=process.version.slice(1).split(".").map(Number);return e.length===3&&e[0]>=9||e[0]===8&&e[1]>=10};X.isWindows=e=>e&&typeof e.windows=="boolean"?e.windows:kn===!0||$n.sep==="\\";X.escapeLast=(e,t,r)=>{let n=e.lastIndexOf(t,r);return n===-1?e:e[n-1]==="\\"?X.escapeLast(e,t,n-1):`${e.slice(0,n)}\\${e.slice(n)}`};X.removePrefix=(e,t={})=>{let r=e;return r.startsWith("./")&&(r=r.slice(2),t.prefix="./"),r};X.wrapOutput=(e,t={},r={})=>{let n=r.contains?"":"^",s=r.contains?"":"$",a=`${n}(?:${e})${s}`;return t.negated===!0&&(a=`(?:^(?!${a}).*$)`),a}});var er=K((As,Ft)=>{"use strict";var Qt=ye(),{CHAR_ASTERISK:je,CHAR_AT:In,CHAR_BACKWARD_SLASH:_e,CHAR_COMMA:Bn,CHAR_DOT:Fe,CHAR_EXCLAMATION_MARK:Xt,CHAR_FORWARD_SLASH:Zt,CHAR_LEFT_CURLY_BRACE:Qe,CHAR_LEFT_PARENTHESES:Xe,CHAR_LEFT_SQUARE_BRACKET:Mn,CHAR_PLUS:Pn,CHAR_QUESTION_MARK:Yt,CHAR_RIGHT_CURLY_BRACE:Dn,CHAR_RIGHT_PARENTHESES:zt,CHAR_RIGHT_SQUARE_BRACKET:Gn}=Re(),Vt=e=>e===Zt||e===_e,Jt=e=>{e.isPrefix!==!0&&(e.depth=e.isGlobstar?Infinity:1)},Un=(e,t)=>{let r=t||{},n=e.length-1,s=r.parts===!0||r.scanToEnd===!0,a=[],i=[],o=[],h=e,m=-1,f=0,R=0,p=!1,v=!1,_=!1,y=!1,b=!1,E=!1,H=!1,L=!1,k=!1,J=0,ie,g,w={value:"",depth:0,isGlob:!1},D=()=>m>=n,W=()=>h.charCodeAt(m+1),l=()=>(ie=g,h.charCodeAt(++m));for(;m0&&(T=h.slice(0,f),h=h.slice(f),R-=f),x&&_===!0&&R>0?(x=h.slice(0,R),U=h.slice(R)):_===!0?(x="",U=h):x=h,x&&x!==""&&x!=="/"&&x!==h&&Vt(x.charCodeAt(x.length-1))&&(x=x.slice(0,-1)),r.unescape===!0&&(U&&(U=Qt.removeBackslashes(U)),x&&H===!0&&(x=Qt.removeBackslashes(x)));let u={prefix:T,input:e,start:f,base:x,glob:U,isBrace:p,isBracket:v,isGlob:_,isExtglob:y,isGlobstar:b,negated:L};if(r.tokens===!0&&(u.maxDepth=0,Vt(g)||i.push(w),u.tokens=i),r.parts===!0||r.tokens===!0){let c;for(let $=0;${"use strict";var Le=Re(),V=ye(),{MAX_LENGTH:Oe,POSIX_REGEX_SOURCE:qn,REGEX_NON_SPECIAL_CHARS:Kn,REGEX_SPECIAL_CHARS_BACKREF:Wn,REPLACEMENTS:rr}=Le,jn=(e,t)=>{if(typeof t.expandRange=="function")return t.expandRange(...e,t);e.sort();let r=`[${e.join("-")}]`;try{new RegExp(r)}catch(n){return e.map(s=>V.escapeRegex(s)).join("..")}return r},de=(e,t)=>`Missing ${e}: "${t}" - use "\\\\${t}" to match literal characters`,nr=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");e=rr[e]||e;let r=I({},t),n=typeof r.maxLength=="number"?Math.min(Oe,r.maxLength):Oe,s=e.length;if(s>n)throw new SyntaxError(`Input length: ${s}, exceeds maximum allowed length: ${n}`);let a={type:"bos",value:"",output:r.prepend||""},i=[a],o=r.capture?"":"?:",h=V.isWindows(t),m=Le.globChars(h),f=Le.extglobChars(m),{DOT_LITERAL:R,PLUS_LITERAL:p,SLASH_LITERAL:v,ONE_CHAR:_,DOTS_SLASH:y,NO_DOT:b,NO_DOT_SLASH:E,NO_DOTS_SLASH:H,QMARK:L,QMARK_NO_DOT:k,STAR:J,START_ANCHOR:ie}=m,g=A=>`(${o}(?:(?!${ie}${A.dot?y:R}).)*?)`,w=r.dot?"":b,D=r.dot?L:k,W=r.bash===!0?g(r):J;r.capture&&(W=`(${W})`),typeof r.noext=="boolean"&&(r.noextglob=r.noext);let l={input:e,index:-1,start:0,dot:r.dot===!0,consumed:"",output:"",prefix:"",backtrack:!1,negated:!1,brackets:0,braces:0,parens:0,quotes:0,globstar:!1,tokens:i};e=V.removePrefix(e,l),s=e.length;let x=[],T=[],U=[],u=a,c,$=()=>l.index===s-1,B=l.peek=(A=1)=>e[l.index+A],Y=l.advance=()=>e[++l.index],re=()=>e.slice(l.index+1),oe=(A="",O=0)=>{l.consumed+=A,l.index+=O},xe=A=>{l.output+=A.output!=null?A.output:A.value,oe(A.value)},xr=()=>{let A=1;for(;B()==="!"&&(B(2)!=="("||B(3)==="?");)Y(),l.start++,A++;return A%2==0?!1:(l.negated=!0,l.start++,!0)},Ce=A=>{l[A]++,U.push(A)},ce=A=>{l[A]--,U.pop()},C=A=>{if(u.type==="globstar"){let O=l.braces>0&&(A.type==="comma"||A.type==="brace"),d=A.extglob===!0||x.length&&(A.type==="pipe"||A.type==="paren");A.type!=="slash"&&A.type!=="paren"&&!O&&!d&&(l.output=l.output.slice(0,-u.output.length),u.type="star",u.value="*",u.output=W,l.output+=u.output)}if(x.length&&A.type!=="paren"&&!f[A.value]&&(x[x.length-1].inner+=A.value),(A.value||A.output)&&xe(A),u&&u.type==="text"&&A.type==="text"){u.value+=A.value,u.output=(u.output||"")+A.value;return}A.prev=u,i.push(A),u=A},we=(A,O)=>{let d=F(I({},f[O]),{conditions:1,inner:""});d.prev=u,d.parens=l.parens,d.output=l.output;let S=(r.capture?"(":"")+d.open;Ce("parens"),C({type:A,value:O,output:l.output?"":_}),C({type:"paren",extglob:!0,value:Y(),output:S}),x.push(d)},Cr=A=>{let O=A.close+(r.capture?")":"");if(A.type==="negate"){let d=W;A.inner&&A.inner.length>1&&A.inner.includes("/")&&(d=g(r)),(d!==W||$()||/^\)+$/.test(re()))&&(O=A.close=`)$))${d}`),A.prev.type==="bos"&&(l.negatedExtglob=!0)}C({type:"paren",extglob:!0,value:c,output:O}),ce("parens")};if(r.fastpaths!==!1&&!/(^[*!]|[/()[\]{}"])/.test(e)){let A=!1,O=e.replace(Wn,(d,S,M,j,q,Me)=>j==="\\"?(A=!0,d):j==="?"?S?S+j+(q?L.repeat(q.length):""):Me===0?D+(q?L.repeat(q.length):""):L.repeat(M.length):j==="."?R.repeat(M.length):j==="*"?S?S+j+(q?W:""):W:S?d:`\\${d}`);return A===!0&&(r.unescape===!0?O=O.replace(/\\/g,""):O=O.replace(/\\+/g,d=>d.length%2==0?"\\\\":d?"\\":"")),O===e&&r.contains===!0?(l.output=e,l):(l.output=V.wrapOutput(O,l,t),l)}for(;!$();){if(c=Y(),c==="\0")continue;if(c==="\\"){let d=B();if(d==="/"&&r.bash!==!0||d==="."||d===";")continue;if(!d){c+="\\",C({type:"text",value:c});continue}let S=/^\\+/.exec(re()),M=0;if(S&&S[0].length>2&&(M=S[0].length,l.index+=M,M%2!=0&&(c+="\\")),r.unescape===!0?c=Y()||"":c+=Y()||"",l.brackets===0){C({type:"text",value:c});continue}}if(l.brackets>0&&(c!=="]"||u.value==="["||u.value==="[^")){if(r.posix!==!1&&c===":"){let d=u.value.slice(1);if(d.includes("[")&&(u.posix=!0,d.includes(":"))){let S=u.value.lastIndexOf("["),M=u.value.slice(0,S),j=u.value.slice(S+2),q=qn[j];if(q){u.value=M+q,l.backtrack=!0,Y(),!a.output&&i.indexOf(u)===1&&(a.output=_);continue}}}(c==="["&&B()!==":"||c==="-"&&B()==="]")&&(c=`\\${c}`),c==="]"&&(u.value==="["||u.value==="[^")&&(c=`\\${c}`),r.posix===!0&&c==="!"&&u.value==="["&&(c="^"),u.value+=c,xe({value:c});continue}if(l.quotes===1&&c!=='"'){c=V.escapeRegex(c),u.value+=c,xe({value:c});continue}if(c==='"'){l.quotes=l.quotes===1?0:1,r.keepQuotes===!0&&C({type:"text",value:c});continue}if(c==="("){Ce("parens"),C({type:"paren",value:c});continue}if(c===")"){if(l.parens===0&&r.strictBrackets===!0)throw new SyntaxError(de("opening","("));let d=x[x.length-1];if(d&&l.parens===d.parens+1){Cr(x.pop());continue}C({type:"paren",value:c,output:l.parens?")":"\\)"}),ce("parens");continue}if(c==="["){if(r.nobracket===!0||!re().includes("]")){if(r.nobracket!==!0&&r.strictBrackets===!0)throw new SyntaxError(de("closing","]"));c=`\\${c}`}else Ce("brackets");C({type:"bracket",value:c});continue}if(c==="]"){if(r.nobracket===!0||u&&u.type==="bracket"&&u.value.length===1){C({type:"text",value:c,output:`\\${c}`});continue}if(l.brackets===0){if(r.strictBrackets===!0)throw new SyntaxError(de("opening","["));C({type:"text",value:c,output:`\\${c}`});continue}ce("brackets");let d=u.value.slice(1);if(u.posix!==!0&&d[0]==="^"&&!d.includes("/")&&(c=`/${c}`),u.value+=c,xe({value:c}),r.literalBrackets===!1||V.hasRegexChars(d))continue;let S=V.escapeRegex(u.value);if(l.output=l.output.slice(0,-u.value.length),r.literalBrackets===!0){l.output+=S,u.value=S;continue}u.value=`(${o}${S}|${u.value})`,l.output+=u.value;continue}if(c==="{"&&r.nobrace!==!0){Ce("braces");let d={type:"brace",value:c,output:"(",outputIndex:l.output.length,tokensIndex:l.tokens.length};T.push(d),C(d);continue}if(c==="}"){let d=T[T.length-1];if(r.nobrace===!0||!d){C({type:"text",value:c,output:c});continue}let S=")";if(d.dots===!0){let M=i.slice(),j=[];for(let q=M.length-1;q>=0&&(i.pop(),M[q].type!=="brace");q--)M[q].type!=="dots"&&j.unshift(M[q].value);S=jn(j,r),l.backtrack=!0}if(d.comma!==!0&&d.dots!==!0){let M=l.output.slice(0,d.outputIndex),j=l.tokens.slice(d.tokensIndex);d.value=d.output="\\{",c=S="\\}",l.output=M;for(let q of j)l.output+=q.output||q.value}C({type:"brace",value:c,output:S}),ce("braces"),T.pop();continue}if(c==="|"){x.length>0&&x[x.length-1].conditions++,C({type:"text",value:c});continue}if(c===","){let d=c,S=T[T.length-1];S&&U[U.length-1]==="braces"&&(S.comma=!0,d="|"),C({type:"comma",value:c,output:d});continue}if(c==="/"){if(u.type==="dot"&&l.index===l.start+1){l.start=l.index+1,l.consumed="",l.output="",i.pop(),u=a;continue}C({type:"slash",value:c,output:v});continue}if(c==="."){if(l.braces>0&&u.type==="dot"){u.value==="."&&(u.output=R);let d=T[T.length-1];u.type="dots",u.output+=c,u.value+=c,d.dots=!0;continue}if(l.braces+l.parens===0&&u.type!=="bos"&&u.type!=="slash"){C({type:"text",value:c,output:R});continue}C({type:"dot",value:c,output:R});continue}if(c==="?"){if(!(u&&u.value==="(")&&r.noextglob!==!0&&B()==="("&&B(2)!=="?"){we("qmark",c);continue}if(u&&u.type==="paren"){let S=B(),M=c;if(S==="<"&&!V.supportsLookbehinds())throw new Error("Node.js v10 or higher is required for regex lookbehinds");(u.value==="("&&!/[!=<:]/.test(S)||S==="<"&&!/<([!=]|\w+>)/.test(re()))&&(M=`\\${c}`),C({type:"text",value:c,output:M});continue}if(r.dot!==!0&&(u.type==="slash"||u.type==="bos")){C({type:"qmark",value:c,output:k});continue}C({type:"qmark",value:c,output:L});continue}if(c==="!"){if(r.noextglob!==!0&&B()==="("&&(B(2)!=="?"||!/[!=<:]/.test(B(3)))){we("negate",c);continue}if(r.nonegate!==!0&&l.index===0){xr();continue}}if(c==="+"){if(r.noextglob!==!0&&B()==="("&&B(2)!=="?"){we("plus",c);continue}if(u&&u.value==="("||r.regex===!1){C({type:"plus",value:c,output:p});continue}if(u&&(u.type==="bracket"||u.type==="paren"||u.type==="brace")||l.parens>0){C({type:"plus",value:c});continue}C({type:"plus",value:p});continue}if(c==="@"){if(r.noextglob!==!0&&B()==="("&&B(2)!=="?"){C({type:"at",extglob:!0,value:c,output:""});continue}C({type:"text",value:c});continue}if(c!=="*"){(c==="$"||c==="^")&&(c=`\\${c}`);let d=Kn.exec(re());d&&(c+=d[0],l.index+=d[0].length),C({type:"text",value:c});continue}if(u&&(u.type==="globstar"||u.star===!0)){u.type="star",u.star=!0,u.value+=c,u.output=W,l.backtrack=!0,l.globstar=!0,oe(c);continue}let A=re();if(r.noextglob!==!0&&/^\([^?]/.test(A)){we("star",c);continue}if(u.type==="star"){if(r.noglobstar===!0){oe(c);continue}let d=u.prev,S=d.prev,M=d.type==="slash"||d.type==="bos",j=S&&(S.type==="star"||S.type==="globstar");if(r.bash===!0&&(!M||A[0]&&A[0]!=="/")){C({type:"star",value:c,output:""});continue}let q=l.braces>0&&(d.type==="comma"||d.type==="brace"),Me=x.length&&(d.type==="pipe"||d.type==="paren");if(!M&&d.type!=="paren"&&!q&&!Me){C({type:"star",value:c,output:""});continue}for(;A.slice(0,3)==="/**";){let Se=e[l.index+4];if(Se&&Se!=="/")break;A=A.slice(3),oe("/**",3)}if(d.type==="bos"&&$()){u.type="globstar",u.value+=c,u.output=g(r),l.output=u.output,l.globstar=!0,oe(c);continue}if(d.type==="slash"&&d.prev.type!=="bos"&&!j&&$()){l.output=l.output.slice(0,-(d.output+u.output).length),d.output=`(?:${d.output}`,u.type="globstar",u.output=g(r)+(r.strictSlashes?")":"|$)"),u.value+=c,l.globstar=!0,l.output+=d.output+u.output,oe(c);continue}if(d.type==="slash"&&d.prev.type!=="bos"&&A[0]==="/"){let Se=A[1]!==void 0?"|$":"";l.output=l.output.slice(0,-(d.output+u.output).length),d.output=`(?:${d.output}`,u.type="globstar",u.output=`${g(r)}${v}|${v}${Se})`,u.value+=c,l.output+=d.output+u.output,l.globstar=!0,oe(c+Y()),C({type:"slash",value:"/",output:""});continue}if(d.type==="bos"&&A[0]==="/"){u.type="globstar",u.value+=c,u.output=`(?:^|${v}|${g(r)}${v})`,l.output=u.output,l.globstar=!0,oe(c+Y()),C({type:"slash",value:"/",output:""});continue}l.output=l.output.slice(0,-u.output.length),u.type="globstar",u.output=g(r),u.value+=c,l.output+=u.output,l.globstar=!0,oe(c);continue}let O={type:"star",value:c,output:W};if(r.bash===!0){O.output=".*?",(u.type==="bos"||u.type==="slash")&&(O.output=w+O.output),C(O);continue}if(u&&(u.type==="bracket"||u.type==="paren")&&r.regex===!0){O.output=c,C(O);continue}(l.index===l.start||u.type==="slash"||u.type==="dot")&&(u.type==="dot"?(l.output+=E,u.output+=E):r.dot===!0?(l.output+=H,u.output+=H):(l.output+=w,u.output+=w),B()!=="*"&&(l.output+=_,u.output+=_)),C(O)}for(;l.brackets>0;){if(r.strictBrackets===!0)throw new SyntaxError(de("closing","]"));l.output=V.escapeLast(l.output,"["),ce("brackets")}for(;l.parens>0;){if(r.strictBrackets===!0)throw new SyntaxError(de("closing",")"));l.output=V.escapeLast(l.output,"("),ce("parens")}for(;l.braces>0;){if(r.strictBrackets===!0)throw new SyntaxError(de("closing","}"));l.output=V.escapeLast(l.output,"{"),ce("braces")}if(r.strictSlashes!==!0&&(u.type==="star"||u.type==="bracket")&&C({type:"maybe_slash",value:"",output:`${v}?`}),l.backtrack===!0){l.output="";for(let A of l.tokens)l.output+=A.output!=null?A.output:A.value,A.suffix&&(l.output+=A.suffix)}return l};nr.fastpaths=(e,t)=>{let r=I({},t),n=typeof r.maxLength=="number"?Math.min(Oe,r.maxLength):Oe,s=e.length;if(s>n)throw new SyntaxError(`Input length: ${s}, exceeds maximum allowed length: ${n}`);e=rr[e]||e;let a=V.isWindows(t),{DOT_LITERAL:i,SLASH_LITERAL:o,ONE_CHAR:h,DOTS_SLASH:m,NO_DOT:f,NO_DOTS:R,NO_DOTS_SLASH:p,STAR:v,START_ANCHOR:_}=Le.globChars(a),y=r.dot?R:f,b=r.dot?p:f,E=r.capture?"":"?:",H={negated:!1,prefix:""},L=r.bash===!0?".*?":v;r.capture&&(L=`(${L})`);let k=w=>w.noglobstar===!0?L:`(${E}(?:(?!${_}${w.dot?m:i}).)*?)`,J=w=>{switch(w){case"*":return`${y}${h}${L}`;case".*":return`${i}${h}${L}`;case"*.*":return`${y}${L}${i}${h}${L}`;case"*/*":return`${y}${L}${o}${h}${b}${L}`;case"**":return y+k(r);case"**/*":return`(?:${y}${k(r)}${o})?${b}${h}${L}`;case"**/*.*":return`(?:${y}${k(r)}${o})?${b}${L}${i}${h}${L}`;case"**/.*":return`(?:${y}${k(r)}${o})?${i}${h}${L}`;default:{let D=/^(.*?)\.(\w+)$/.exec(w);if(!D)return;let W=J(D[1]);return W?W+i+D[2]:void 0}}},ie=V.removePrefix(e,H),g=J(ie);return g&&r.strictSlashes!==!0&&(g+=`${o}?`),g};tr.exports=nr});var ir=K((ys,ar)=>{"use strict";var Fn=require("path"),Qn=er(),Ze=sr(),Ye=ye(),Xn=Re(),Zn=e=>e&&typeof e=="object"&&!Array.isArray(e),P=(e,t,r=!1)=>{if(Array.isArray(e)){let f=e.map(p=>P(p,t,r));return p=>{for(let v of f){let _=v(p);if(_)return _}return!1}}let n=Zn(e)&&e.tokens&&e.input;if(e===""||typeof e!="string"&&!n)throw new TypeError("Expected pattern to be a non-empty string");let s=t||{},a=Ye.isWindows(t),i=n?P.compileRe(e,t):P.makeRe(e,t,!1,!0),o=i.state;delete i.state;let h=()=>!1;if(s.ignore){let f=F(I({},t),{ignore:null,onMatch:null,onResult:null});h=P(s.ignore,f,r)}let m=(f,R=!1)=>{let{isMatch:p,match:v,output:_}=P.test(f,i,t,{glob:e,posix:a}),y={glob:e,state:o,regex:i,posix:a,input:f,output:_,match:v,isMatch:p};return typeof s.onResult=="function"&&s.onResult(y),p===!1?(y.isMatch=!1,R?y:!1):h(f)?(typeof s.onIgnore=="function"&&s.onIgnore(y),y.isMatch=!1,R?y:!1):(typeof s.onMatch=="function"&&s.onMatch(y),R?y:!0)};return r&&(m.state=o),m};P.test=(e,t,r,{glob:n,posix:s}={})=>{if(typeof e!="string")throw new TypeError("Expected input to be a string");if(e==="")return{isMatch:!1,output:""};let a=r||{},i=a.format||(s?Ye.toPosixSlashes:null),o=e===n,h=o&&i?i(e):e;return o===!1&&(h=i?i(e):e,o=h===n),(o===!1||a.capture===!0)&&(a.matchBase===!0||a.basename===!0?o=P.matchBase(e,t,r,s):o=t.exec(h)),{isMatch:Boolean(o),match:o,output:h}};P.matchBase=(e,t,r,n=Ye.isWindows(r))=>(t instanceof RegExp?t:P.makeRe(t,r)).test(Fn.basename(e));P.isMatch=(e,t,r)=>P(t,r)(e);P.parse=(e,t)=>Array.isArray(e)?e.map(r=>P.parse(r,t)):Ze(e,F(I({},t),{fastpaths:!1}));P.scan=(e,t)=>Qn(e,t);P.compileRe=(e,t,r=!1,n=!1)=>{if(r===!0)return e.output;let s=t||{},a=s.contains?"":"^",i=s.contains?"":"$",o=`${a}(?:${e.output})${i}`;e&&e.negated===!0&&(o=`^(?!${o}).*$`);let h=P.toRegex(o,t);return n===!0&&(h.state=e),h};P.makeRe=(e,t,r=!1,n=!1)=>{if(!e||typeof e!="string")throw new TypeError("Expected a non-empty string");let s=t||{},a={negated:!1,fastpaths:!0},i="",o;return e.startsWith("./")&&(e=e.slice(2),i=a.prefix="./"),s.fastpaths!==!1&&(e[0]==="."||e[0]==="*")&&(o=Ze.fastpaths(e,t)),o===void 0?(a=Ze(e,t),a.prefix=i+(a.prefix||"")):a.output=o,P.compileRe(a,t,r,n)};P.toRegex=(e,t)=>{try{let r=t||{};return new RegExp(e,r.flags||(r.nocase?"i":""))}catch(r){if(t&&t.debug===!0)throw r;return/$^/}};P.constants=Xn;ar.exports=P});var ur=K((_s,or)=>{"use strict";or.exports=ir()});var hr=K((bs,cr)=>{"use strict";var lr=require("util"),pr=Gt(),ae=ur(),ze=ye(),fr=e=>typeof e=="string"&&(e===""||e==="./"),N=(e,t,r)=>{t=[].concat(t),e=[].concat(e);let n=new Set,s=new Set,a=new Set,i=0,o=f=>{a.add(f.output),r&&r.onResult&&r.onResult(f)};for(let f=0;f!n.has(f));if(r&&m.length===0){if(r.failglob===!0)throw new Error(`No matches found for "${t.join(", ")}"`);if(r.nonull===!0||r.nullglob===!0)return r.unescape?t.map(f=>f.replace(/\\/g,"")):t}return m};N.match=N;N.matcher=(e,t)=>ae(e,t);N.isMatch=(e,t,r)=>ae(t,r)(e);N.any=N.isMatch;N.not=(e,t,r={})=>{t=[].concat(t).map(String);let n=new Set,s=[],a=o=>{r.onResult&&r.onResult(o),s.push(o.output)},i=N(e,t,F(I({},r),{onResult:a}));for(let o of s)i.includes(o)||n.add(o);return[...n]};N.contains=(e,t,r)=>{if(typeof e!="string")throw new TypeError(`Expected a string: "${lr.inspect(e)}"`);if(Array.isArray(t))return t.some(n=>N.contains(e,n,r));if(typeof t=="string"){if(fr(e)||fr(t))return!1;if(e.includes(t)||e.startsWith("./")&&e.slice(2).includes(t))return!0}return N.isMatch(e,t,F(I({},r),{contains:!0}))};N.matchKeys=(e,t,r)=>{if(!ze.isObject(e))throw new TypeError("Expected the first argument to be an object");let n=N(Object.keys(e),t,r),s={};for(let a of n)s[a]=e[a];return s};N.some=(e,t,r)=>{let n=[].concat(e);for(let s of[].concat(t)){let a=ae(String(s),r);if(n.some(i=>a(i)))return!0}return!1};N.every=(e,t,r)=>{let n=[].concat(e);for(let s of[].concat(t)){let a=ae(String(s),r);if(!n.every(i=>a(i)))return!1}return!0};N.all=(e,t,r)=>{if(typeof e!="string")throw new TypeError(`Expected a string: "${lr.inspect(e)}"`);return[].concat(t).every(n=>ae(n,r)(e))};N.capture=(e,t,r)=>{let n=ze.isWindows(r),a=ae.makeRe(String(e),F(I({},r),{capture:!0})).exec(n?ze.toPosixSlashes(t):t);if(a)return a.slice(1).map(i=>i===void 0?"":i)};N.makeRe=(...e)=>ae.makeRe(...e);N.scan=(...e)=>ae.scan(...e);N.parse=(e,t)=>{let r=[];for(let n of[].concat(e||[]))for(let s of pr(String(n),t))r.push(ae.parse(s,t));return r};N.braces=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");return t&&t.nobrace===!0||!/\{.*\}/.test(e)?[e]:pr(e,t)};N.braceExpand=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");return N.braces(e,F(I({},t),{expand:!0}))};cr.exports=N});var gr=K((Es,dr)=>{"use strict";dr.exports=(e,...t)=>new Promise(r=>{r(e(...t))})});var Ar=K((xs,Ve)=>{"use strict";var Yn=gr(),mr=e=>{if(e<1)throw new TypeError("Expected `concurrency` to be a number from 1 and up");let t=[],r=0,n=()=>{r--,t.length>0&&t.shift()()},s=(o,h,...m)=>{r++;let f=Yn(o,...m);h(f),f.then(n,n)},a=(o,h,...m)=>{rnew Promise(m=>a(o,m,...h));return Object.defineProperties(i,{activeCount:{get:()=>r},pendingCount:{get:()=>t.length}}),i};Ve.exports=mr;Ve.exports.default=mr});var Vn={};Or(Vn,{default:()=>es});var ve=Q(require("@yarnpkg/cli")),ne=Q(require("@yarnpkg/core")),rt=Q(require("@yarnpkg/core")),le=Q(require("clipanion")),me=class extends ve.BaseCommand{constructor(){super(...arguments);this.json=le.Option.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.production=le.Option.Boolean("--production",!1,{description:"Only install regular dependencies by omitting dev dependencies"});this.all=le.Option.Boolean("-A,--all",!1,{description:"Install the entire project"});this.workspaces=le.Option.Rest()}async execute(){let t=await ne.Configuration.find(this.context.cwd,this.context.plugins),{project:r,workspace:n}=await ne.Project.find(t,this.context.cwd),s=await ne.Cache.find(t);await r.restoreInstallState({restoreResolutions:!1});let a;if(this.all)a=new Set(r.workspaces);else if(this.workspaces.length===0){if(!n)throw new ve.WorkspaceRequiredError(r.cwd,this.context.cwd);a=new Set([n])}else a=new Set(this.workspaces.map(o=>r.getWorkspaceByIdent(rt.structUtils.parseIdent(o))));for(let o of a)for(let h of this.production?["dependencies"]:ne.Manifest.hardDependencies)for(let m of o.manifest.getForScope(h).values()){let f=r.tryWorkspaceByDescriptor(m);f!==null&&a.add(f)}for(let o of r.workspaces)a.has(o)?this.production&&o.manifest.devDependencies.clear():(o.manifest.installConfig=o.manifest.installConfig||{},o.manifest.installConfig.selfReferences=!1,o.manifest.dependencies.clear(),o.manifest.devDependencies.clear(),o.manifest.peerDependencies.clear(),o.manifest.scripts.clear());return(await ne.StreamReport.start({configuration:t,json:this.json,stdout:this.context.stdout,includeLogs:!0},async o=>{await r.install({cache:s,report:o,persistProject:!1})})).exitCode()}};me.paths=[["workspaces","focus"]],me.usage=le.Command.Usage({category:"Workspace-related commands",description:"install a single workspace and its dependencies",details:"\n This command will run an install as if the specified workspaces (and all other workspaces they depend on) were the only ones in the project. If no workspaces are explicitly listed, the active one will be assumed.\n\n Note that this command is only very moderately useful when using zero-installs, since the cache will contain all the packages anyway - meaning that the only difference between a full install and a focused install would just be a few extra lines in the `.pnp.cjs` file, at the cost of introducing an extra complexity.\n\n If the `-A,--all` flag is set, the entire project will be installed. Combine with `--production` to replicate the old `yarn install --production`.\n "});var nt=me;var Ne=Q(require("@yarnpkg/cli")),Ie=Q(require("@yarnpkg/core")),be=Q(require("@yarnpkg/core")),Z=Q(require("@yarnpkg/core")),Rr=Q(require("@yarnpkg/plugin-git")),G=Q(require("clipanion")),Be=Q(hr()),yr=Q(require("os")),_r=Q(Ar()),te=Q(require("typanion")),Ee=class extends Ne.BaseCommand{constructor(){super(...arguments);this.recursive=G.Option.Boolean("-R,--recursive",!1,{description:"Find packages via dependencies/devDependencies instead of using the workspaces field"});this.from=G.Option.Array("--from",[],{description:"An array of glob pattern idents from which to base any recursion"});this.all=G.Option.Boolean("-A,--all",!1,{description:"Run the command on all workspaces of a project"});this.verbose=G.Option.Boolean("-v,--verbose",!1,{description:"Prefix each output line with the name of the originating workspace"});this.parallel=G.Option.Boolean("-p,--parallel",!1,{description:"Run the commands in parallel"});this.interlaced=G.Option.Boolean("-i,--interlaced",!1,{description:"Print the output of commands in real-time instead of buffering it"});this.jobs=G.Option.String("-j,--jobs",{description:"The maximum number of parallel tasks that the execution will be limited to; or `unlimited`",validator:te.isOneOf([te.isEnum(["unlimited"]),te.applyCascade(te.isNumber(),[te.isInteger(),te.isAtLeast(1)])])});this.topological=G.Option.Boolean("-t,--topological",!1,{description:"Run the command after all workspaces it depends on (regular) have finished"});this.topologicalDev=G.Option.Boolean("--topological-dev",!1,{description:"Run the command after all workspaces it depends on (regular + dev) have finished"});this.include=G.Option.Array("--include",[],{description:"An array of glob pattern idents; only matching workspaces will be traversed"});this.exclude=G.Option.Array("--exclude",[],{description:"An array of glob pattern idents; matching workspaces won't be traversed"});this.publicOnly=G.Option.Boolean("--no-private",{description:"Avoid running the command on private workspaces"});this.since=G.Option.String("--since",{description:"Only include workspaces that have been changed since the specified ref.",tolerateBoolean:!0});this.commandName=G.Option.String();this.args=G.Option.Proxy()}async execute(){let t=await Ie.Configuration.find(this.context.cwd,this.context.plugins),{project:r,workspace:n}=await Ie.Project.find(t,this.context.cwd);if(!this.all&&!n)throw new Ne.WorkspaceRequiredError(r.cwd,this.context.cwd);let s=this.cli.process([this.commandName,...this.args]),a=s.path.length===1&&s.path[0]==="run"&&typeof s.scriptName!="undefined"?s.scriptName:null;if(s.path.length===0)throw new G.UsageError("Invalid subcommand name for iteration - use the 'run' keyword if you wish to execute a script");let i=this.all?r.topLevelWorkspace:n,o=this.since?Array.from(await Rr.gitUtils.fetchChangedWorkspaces({ref:this.since,project:r})):[i,...this.from.length>0?i.getRecursiveWorkspaceChildren():[]],h=g=>Be.default.isMatch(Z.structUtils.stringifyIdent(g.locator),this.from),m=this.from.length>0?o.filter(h):o,f=new Set([...m,...m.map(g=>[...this.recursive?this.since?g.getRecursiveWorkspaceDependents():g.getRecursiveWorkspaceDependencies():g.getRecursiveWorkspaceChildren()]).flat()]),R=[],p=!1;if(a==null?void 0:a.includes(":")){for(let g of r.workspaces)if(g.manifest.scripts.has(a)&&(p=!p,p===!1))break}for(let g of f)a&&!g.manifest.scripts.has(a)&&!p||a===process.env.npm_lifecycle_event&&g.cwd===n.cwd||this.include.length>0&&!Be.default.isMatch(Z.structUtils.stringifyIdent(g.locator),this.include)||this.exclude.length>0&&Be.default.isMatch(Z.structUtils.stringifyIdent(g.locator),this.exclude)||this.publicOnly&&g.manifest.private===!0||R.push(g);let v=this.parallel?this.jobs==="unlimited"?Infinity:this.jobs||Math.max(1,(0,yr.cpus)().length/2):1,_=v===1?!1:this.parallel,y=_?this.interlaced:!0,b=(0,_r.default)(v),E=new Map,H=new Set,L=0,k=null,J=!1,ie=await be.StreamReport.start({configuration:t,stdout:this.context.stdout},async g=>{let w=async(D,{commandIndex:W})=>{if(J)return-1;!_&&this.verbose&&W>1&&g.reportSeparator();let l=zn(D,{configuration:t,verbose:this.verbose,commandIndex:W}),[x,T]=br(g,{prefix:l,interlaced:y}),[U,u]=br(g,{prefix:l,interlaced:y});try{this.verbose&&g.reportInfo(null,`${l} Process started`);let c=Date.now(),$=await this.cli.run([this.commandName,...this.args],{cwd:D.cwd,stdout:x,stderr:U})||0;x.end(),U.end(),await T,await u;let B=Date.now();if(this.verbose){let Y=t.get("enableTimers")?`, completed in ${Z.formatUtils.pretty(t,B-c,Z.formatUtils.Type.DURATION)}`:"";g.reportInfo(null,`${l} Process exited (exit code ${$})${Y}`)}return $===130&&(J=!0,k=$),$}catch(c){throw x.end(),U.end(),await T,await u,c}};for(let D of R)E.set(D.anchoredLocator.locatorHash,D);for(;E.size>0&&!g.hasErrors();){let D=[];for(let[x,T]of E){if(H.has(T.anchoredDescriptor.descriptorHash))continue;let U=!0;if(this.topological||this.topologicalDev){let u=this.topologicalDev?new Map([...T.manifest.dependencies,...T.manifest.devDependencies]):T.manifest.dependencies;for(let c of u.values()){let $=r.tryWorkspaceByDescriptor(c);if(U=$===null||!E.has($.anchoredLocator.locatorHash),!U)break}}if(!!U&&(H.add(T.anchoredDescriptor.descriptorHash),D.push(b(async()=>{let u=await w(T,{commandIndex:++L});return E.delete(x),H.delete(T.anchoredDescriptor.descriptorHash),u})),!_))break}if(D.length===0){let x=Array.from(E.values()).map(T=>Z.structUtils.prettyLocator(t,T.anchoredLocator)).join(", ");g.reportError(be.MessageName.CYCLIC_DEPENDENCIES,`Dependency cycle detected (${x})`);return}let l=(await Promise.all(D)).find(x=>x!==0);k===null&&(k=typeof l!="undefined"?1:k),(this.topological||this.topologicalDev)&&typeof l!="undefined"&&g.reportError(be.MessageName.UNNAMED,"The command failed for workspaces that are depended upon by other workspaces; can't satisfy the dependency graph")}});return k!==null?k:ie.exitCode()}};Ee.paths=[["workspaces","foreach"]],Ee.usage=G.Command.Usage({category:"Workspace-related commands",description:"run a command on all workspaces",details:"\n This command will run a given sub-command on current and all its descendant workspaces. Various flags can alter the exact behavior of the command:\n\n - If `-p,--parallel` is set, the commands will be ran in parallel; they'll by default be limited to a number of parallel tasks roughly equal to half your core number, but that can be overridden via `-j,--jobs`, or disabled by setting `-j unlimited`.\n\n - If `-p,--parallel` and `-i,--interlaced` are both set, Yarn will print the lines from the output as it receives them. If `-i,--interlaced` wasn't set, it would instead buffer the output from each process and print the resulting buffers only after their source processes have exited.\n\n - If `-t,--topological` is set, Yarn will only run the command after all workspaces that it depends on through the `dependencies` field have successfully finished executing. If `--topological-dev` is set, both the `dependencies` and `devDependencies` fields will be considered when figuring out the wait points.\n\n - If `-A,--all` is set, Yarn will run the command on all the workspaces of a project. By default yarn runs the command only on current and all its descendant workspaces.\n\n - If `-R,--recursive` is set, Yarn will find workspaces to run the command on by recursively evaluating `dependencies` and `devDependencies` fields, instead of looking at the `workspaces` fields.\n\n - If `--from` is set, Yarn will use the packages matching the 'from' glob as the starting point for any recursive search.\n\n - If `--since` is set, Yarn will only run the command on workspaces that have been modified since the specified ref. By default Yarn will use the refs specified by the `changesetBaseRefs` configuration option.\n\n - The command may apply to only some workspaces through the use of `--include` which acts as a whitelist. The `--exclude` flag will do the opposite and will be a list of packages that mustn't execute the script. Both flags accept glob patterns (if valid Idents and supported by [micromatch](https://github.com/micromatch/micromatch)). Make sure to escape the patterns, to prevent your own shell from trying to expand them.\n\n Adding the `-v,--verbose` flag will cause Yarn to print more information; in particular the name of the workspace that generated the output will be printed at the front of each line.\n\n If the command is `run` and the script being run does not exist the child workspace will be skipped without error.\n ",examples:[["Publish current and all descendant packages","yarn workspaces foreach npm publish --tolerate-republish"],["Run build script on current and all descendant packages","yarn workspaces foreach run build"],["Run build script on current and all descendant packages in parallel, building package dependencies first","yarn workspaces foreach -pt run build"],["Run build script on several packages and all their dependencies, building dependencies first","yarn workspaces foreach -ptR --from '{workspace-a,workspace-b}' run build"]]});var Er=Ee;function br(e,{prefix:t,interlaced:r}){let n=e.createStreamReporter(t),s=new Z.miscUtils.DefaultStream;s.pipe(n,{end:!1}),s.on("finish",()=>{n.end()});let a=new Promise(o=>{n.on("finish",()=>{o(s.active)})});if(r)return[s,a];let i=new Z.miscUtils.BufferStream;return i.pipe(s,{end:!1}),i.on("finish",()=>{s.end()}),[i,a]}function zn(e,{configuration:t,commandIndex:r,verbose:n}){if(!n)return null;let s=Z.structUtils.convertToIdent(e.locator),i=`[${Z.structUtils.stringifyIdent(s)}]:`,o=["#2E86AB","#A23B72","#F18F01","#C73E1D","#CCE2A3"],h=o[r%o.length];return Z.formatUtils.pretty(t,i,h)}var Jn={commands:[nt,Er]},es=Jn;return Vn;})(); +/*! + * fill-range + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Licensed under the MIT License. + */ +/*! + * is-number + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Released under the MIT License. + */ +/*! + * to-regex-range + * + * Copyright (c) 2015-present, Jon Schlinkert. + * Released under the MIT License. + */ +return plugin; +} +}; diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 00000000000..48024a3aa88 --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1,38 @@ +constraintsPath: ./.yarn/constraints.pro + +npmPublishRegistry: "https://registry.npmjs.org" + +packageExtensions: + "@protobufjs/inquire@*": + dependencies: + long: ^5.2.0 + eslint-module-utils@*: + dependencies: + eslint-import-resolver-node: ^0.3.6 + karma-mocha@*: + dependencies: + mocha: ^9.1.2 + pino@*: + dependencies: + pino-pretty: ^4.0.3 + "@dashevo/protobufjs@*": + dependencies: + chalk: ^3.0.0 + escodegen: ^2.0.0 + espree: ^9.1.0 + estraverse: ^5.3.0 + glob: ^7.2.0 + minimist: ^1.2.5 + semver: ^7.3.5 + uglify-js: ^3.14.4 + ts-node@*: + dependencies: + typescript: ^3.9.5 + +plugins: + - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs + spec: "@yarnpkg/plugin-workspace-tools" + - path: .yarn/plugins/@yarnpkg/plugin-outdated.cjs + spec: "https://mskelton.dev/yarn-outdated/v2" + - path: .yarn/plugins/@yarnpkg/plugin-constraints.cjs + spec: "@yarnpkg/plugin-constraints" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000000..7b412ceaa5d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,892 @@ +## [0.23.0-dev.4](https://github.com/dashevo/platform/compare/v0.23.0-dev.3...v0.23.0-dev.4) (2022-07-12) + + +### Bug Fixes + +* **dashmate:** replace `seeds` by `bootstrap-peers` in config.toml ([#460](https://github.com/dashevo/platform/issues/460)) +* **drive:** various fixes in synchronize masternode identities logic and logging ([#461](https://github.com/dashevo/platform/issues/461)) + + +### Build System + +* **test-suite:** fix docker image build + +## [0.23.0-dev.3](https://github.com/dashevo/platform/compare/v0.22.13...v0.23.0-dev.3) (2022-06-30) + + +### ⚠ BREAKING CHANGES + +* Previous invalid data contracts in blockchain might be valid now (#445) +* `getIdentityIdsByPublicKeyHash` endpoint is removed. `getIdentitiesByPublicKeyHash` now responds with an array of identities, instead of an array of cbored arrays of identities. (#437) +* All indices must have 'asc' order (#435) +* Some state transitions in the chain could change validation result due to changes in fee logic. Previously invalid state transition in chain could become valid since BLS signing is fixed (#392) +* Previously invalidated `DataContractUpdateTransitions` with `unique` equals `false` will become valid (#427) +* Document query logic can behave differently in some cases (#398) + +### Features + +* **bench:** state transition benchmark ([#418](https://github.com/dashevo/platform/issues/418)) +* **dashmate:** add --force flag to stop command ([#434](https://github.com/dashevo/platform/issues/434)) +* **dashmate:** upgrade docker compose to v2 ([#441](https://github.com/dashevo/platform/issues/441)) +* **dpp:** allow 1 char document type and 1 char property name ([#445](https://github.com/dashevo/platform/issues/445)) +* integrate dash-spv into monorepo +* limit the number of shares for masternode by 16 ([#432](https://github.com/dashevo/platform/issues/432)) +* move dash-spv in packages after import +* re-enable proof responses ([#440](https://github.com/dashevo/platform/issues/440)) +* validate fee calculating worst case operations ([#392](https://github.com/dashevo/platform/issues/392)) + + +### Bug Fixes + +* **ci:** docker images incorrectly tagged with v ([#413](https://github.com/dashevo/platform/issues/413)) +* **dpp:** data contract index update validation ([#427](https://github.com/dashevo/platform/issues/427)) +* **drive:** change transaction is started check ([#451](https://github.com/dashevo/platform/issues/451)) +* non-deterministic fees due to data contract cache ([#444](https://github.com/dashevo/platform/issues/444)) +* **sdk:** identity update method can't sign publicKeys in some cases ([#421](https://github.com/dashevo/platform/issues/421)) +* **wallet-lib:** separate persistent storage by walletId ([#407](https://github.com/dashevo/platform/issues/407)) + + +### Documentation + +* add input description + + +### Code Refactoring + +* **drive:** use RS Drive query validation logic ([#398](https://github.com/dashevo/platform/issues/398)) +* simplified public key to identity structure ([#437](https://github.com/dashevo/platform/issues/437)) + + +### Tests + +* **dpp:** fix invalid findIndexDuplicates test in DPP ([#448](https://github.com/dashevo/platform/issues/448)) + + +### Miscellaneous Chores + +* **dpp:** allow only `asc` order for indices ([#435](https://github.com/dashevo/platform/issues/435)) +* **drive:** log synchronize masternode identities ([#449](https://github.com/dashevo/platform/issues/449)) +* **test-suite:** move wallet storage persistence in the outer folder ([#416](https://github.com/dashevo/platform/issues/416)) +* update readme + + +### [0.22.13](https://github.com/dashevo/platform/compare/v0.22.12...v0.22.13) (2022-06-17) + + +### Features + +* support DIP24 devnet LLMQ type ([#438](https://github.com/dashevo/platform/issues/438)) + +### [0.22.12](https://github.com/dashevo/platform/compare/v0.22.11...v0.22.12) (2022-06-07) + + +### Bug Fixes + +* **sdk:** incomplete bundle for web ([#400](https://github.com/dashevo/platform/issues/400)) +* **wallet-lib:** separate persistent storage by walletId ([#407](https://github.com/dashevo/platform/issues/407)) + +### [0.22.11](https://github.com/dashevo/platform/compare/v0.22.10...v0.22.11) (2022-05-31) + + +### Bug Fixes + +* incorrect image versions and variables for testnet config ([#415](https://github.com/dashevo/platform/issues/415)) + +### [0.22.10](https://github.com/dashevo/platform/compare/v0.22.9...v0.22.10) (2022-05-26) + + +### Bug Fixes + +* CommitmentTxPayload#toBuffer method was using version instead of qfcVersion for serialization ([#410](https://github.com/dashevo/platform/issues/410)) + + +### Continuous Integration + +* dispatch trigger and parallelization ([#406](https://github.com/dashevo/platform/issues/406)) + +### [0.22.9](https://github.com/dashevo/platform/compare/v0.22.8...v0.22.9) (2022-05-24) + + +### Bug Fixes + +* incorrect parsing of commitment payload ([#408](https://github.com/dashevo/platform/issues/408)) + +### [0.22.8](https://github.com/dashevo/platform/compare/v0.22.7...v0.22.8) (2022-05-23) + + +### Bug Fixes + +* `verifyChainLock` was returning `false` instead of `ResponseQuery` ([#402](https://github.com/dashevo/platform/issues/402)) +* **dashmate:** switch `drive` and `dapi` to stable versions ([#381](https://github.com/dashevo/platform/issues/381)) +* **wallet-lib:** hook tx chain broadcast on mempool response ([#388](https://github.com/dashevo/platform/issues/388)) + + +## [0.23.0-dev.2](https://github.com/dashevo/platform/compare/v0.23.0-dev.1...v0.23.0-dev.2) (2022-05-20) + + +### ⚠ BREAKING CHANGES + +* Identity master key can be used only to update identity (#384) +* SDK's identity update method now requires correspond private keys. Identity public keys in state transitions must be signed + +### Features + +* bench suite ([#335](https://github.com/dashevo/platform/issues/335)) +* **bench-suite:** add fees to documents benchmark ([#379](https://github.com/dashevo/platform/issues/379)) +* **bench-suite:** function benchmark and other improvements ([#344](https://github.com/dashevo/platform/issues/344)) +* calculate state transition fees using operations ([#376](https://github.com/dashevo/platform/issues/376)) +* create withdrawal keys for masternode identities ([#320](https://github.com/dashevo/platform/issues/320)) +* **dpp:** BIP13_SCRIPT_HASH identity public key type ([#353](https://github.com/dashevo/platform/issues/353)) +* **dpp:** calculate signature verification costs for fees ([#387](https://github.com/dashevo/platform/issues/387)) +* **dpp:** fee operations and execution context ([#369](https://github.com/dashevo/platform/issues/369)) +* **drive:** collect fee operation to execution context ([#370](https://github.com/dashevo/platform/issues/370)) +* Identity master key can be used only to update identity ([#384](https://github.com/dashevo/platform/issues/384)) +* identity public key proofs ([#349](https://github.com/dashevo/platform/issues/349)) +* integrate with Tenderdash v0.8-dev ([#314](https://github.com/dashevo/platform/issues/314)) + + +### Bug Fixes + +* change allowed security level for withdrawal purpose to critical ([#352](https://github.com/dashevo/platform/issues/352)) +* **dapi-grpc:** outdated autogenerated code ([#331](https://github.com/dashevo/platform/issues/331)) +* **dashmate:** switch `drive` and `dapi` to stable versions ([#381](https://github.com/dashevo/platform/issues/381)) +* **wallet-lib:** hook tx chain broadcast on mempool response ([#388](https://github.com/dashevo/platform/issues/388)) + + +### Documentation + +* update badges in individual package readmes ([#361](https://github.com/dashevo/platform/issues/361)) + + +### Continuous Integration + +* add `latest-dev` docker tag ([#382](https://github.com/dashevo/platform/issues/382)) + + +### Miscellaneous Chores + +* **dashmate:** use 0.23-dev images + + +### [0.22.7](https://github.com/dashevo/platform/compare/v0.22.6...v0.22.7) (2022-05-02) + + +### Bug Fixes + +* invalid version to parse `CommitmentTxPayload` ([#373](https://github.com/dashevo/platform/issues/373)) + +### [0.22.6](https://github.com/dashevo/platform/compare/v0.22.5...v0.22.6) (2022-05-02) + + +### Bug Fixes + +* can't parse `CommitmentTxPayload` ([#371](https://github.com/dashevo/platform/issues/371)) + +### [0.22.5](https://github.com/dashevo/platform/compare/v0.22.4...v0.22.5) (2022-04-29) + + +### Bug Fixes + +* broken QuorumEntry unserialization ([#366](https://github.com/dashevo/platform/issues/366)) + +### [0.22.4](https://github.com/dashevo/platform/compare/v0.22.3...v0.22.4) (2022-04-29) + + +### ⚠ BREAKING CHANGES + +* Core v0.17 is not supported anymore + +### Bug Fixes + +* invalid `merkleRootQuorums` calculation ([#362](https://github.com/dashevo/platform/issues/362)) + +### [0.22.3](https://github.com/dashevo/platform/compare/v0.22.2...v0.22.3) (2022-04-27) + + +### ⚠ BREAKING CHANGES + +* **wallet-lib:** storage layer refactoring (#232) + +### Features + +* **wallet-lib:** adds balance and metadata information from registered identity ([#337](https://github.com/dashevo/platform/issues/337)) +* **wallet-lib:** provide transaction history item as a date object ([#336](https://github.com/dashevo/platform/issues/336)) +* **wallet-lib:** rework storage for multiple key chains ([#231](https://github.com/dashevo/platform/issues/231)) +* **wallet-lib:** satoshisBalanceImpact in transaction history ([#319](https://github.com/dashevo/platform/issues/319)) +* **wallet-lib:** storage layer refactoring ([#232](https://github.com/dashevo/platform/issues/232)) + + +### Bug Fixes + +* **dashmate:** broken migrations ([#355](https://github.com/dashevo/platform/issues/355)) +* **wallet-lib:** optimize storage version check ([#348](https://github.com/dashevo/platform/issues/348)) +* **wallet-lib:** persistent storage regression ([#302](https://github.com/dashevo/platform/issues/302)) + + +### [0.22.2](https://github.com/dashevo/platform/compare/v0.22.1...v0.22.2) (2022-04-21) + + +### Bug Fixes + +* docker-test-suite missing test files + + +### Tests + +* **dpp:** double test in identity validation ([#330](https://github.com/dashevo/platform/issues/330)) +* fixes sdk timeouts in platform test suite ([#309](https://github.com/dashevo/platform/issues/309)) + + +### Miscellaneous Chores + +* update Core to v0.18.0.0-rc1 ([#351](https://github.com/dashevo/platform/issues/351)) + + +## [0.23.0-dev.1](https://github.com/dashevo/platform/compare/v0.22.0...v0.23.0-dev.1) (2022-04-08) + + +### ⚠ BREAKING CHANGES + +* plain proRegTx for masternode identifier (#318) +* **wallet-lib:** storage layer refactoring (#232) + +### Features + +* **dpp:** add `withdraw` purpose for `IdentityPublicKey` ([#317](https://github.com/dashevo/platform/issues/317)) +* update identity ([#292](https://github.com/dashevo/platform/issues/292)) +* **wallet-lib:** rework storage for multiple key chains ([#231](https://github.com/dashevo/platform/issues/231)) +* **wallet-lib:** satoshisBalanceImpact in transaction history ([#319](https://github.com/dashevo/platform/issues/319)) +* **wallet-lib:** storage layer refactoring ([#232](https://github.com/dashevo/platform/issues/232)) + + +### Bug Fixes + +* **dashmate:** config/core/miner must have required property 'interval' ([#311](https://github.com/dashevo/platform/issues/311)) +* do not hash proRegTx for masternode identifier ([#318](https://github.com/dashevo/platform/issues/318)) + + +### Performance Improvements + +* **dapi:** cache block headers and chainlocks ([#235](https://github.com/dashevo/platform/issues/235), [#296](https://github.com/dashevo/platform/issues/296)) +* **dapi:** remove unnecessary Core RPC calls for core streams ([#194](https://github.com/dashevo/platform/issues/194)) + + +### Continuous Integration + +* enable multiarch builds ([#316](https://github.com/dashevo/platform/issues/316)) + + +### Miscellaneous Chores + +* **drive:** add more block execution timers ([#329](https://github.com/dashevo/platform/issues/329)) + + +### Tests + +* fixes wallet.spec.js + +### [0.22.1](https://github.com/dashevo/platform/compare/v0.22.0...v0.22.1) (2022-03-25) + + +### Bug Fixes + +* **dashmate:** cannot read properties of undefined (reading 'masternodeRewardShares’) ([#310](https://github.com/dashevo/platform/issues/310)) +* **dashmate:** config/core/miner must have required property 'interval' ([#311](https://github.com/dashevo/platform/issues/311)) + + +### Tests + +* fix platform-test-suite-execution in browser environment ([#289](https://github.com/dashevo/platform/issues/289)) + + +## [0.22.0](https://github.com/dashevo/platform/compare/v0.21.8...v0.22.0) (2022-03-21) + +### ⚠ BREAKING CHANGES + +* `name` is required for document index definition +* `platform.contracts.broadcast` method in SDK renamed to `platform.contracts.publish` +* Identity public key requires `purpose` and `securityLevel` properties +* `$id` property can't be used in document indices +* Indexed properties now require size constraints +* `getIdentitiesByPublicKeyHashes` returns array of arrays of identities +* `getIdentityIdsByPublicKeyHashes` returns array of arrays of identity ids +* Document array properties temporarily cannot be indexed. Will be enabled in v0.23 +* Range operations in document queries can be used only in the last where clause +* sorting (`orderBy`) in document queries is required for range operations +* `elementMatch`, `contains` and `includes` operations are temporarily disabled in document query. Will be enabled in v0.23 +* `$ref` in data contract is temporarily disabled +* `startAt` and `startAfter` accept now only document id instead of document offset +* `in` operator can be used only in two last where clauses +* Cryptographical proofs for platform state are temporarily disabled. Will be enabled in upcoming releases +* Platform data is not compatible with previous platform versions. Please reset your node. + + +### Features + +* identity public key purpose and security levels ([#46](https://github.com/dashevo/platform/issues/46)) +* allow using non-unique Identity public keys ([#168](https://github.com/dashevo/platform/issues/168)) +* distribute dashmate with NPM ([#148](https://github.com/dashevo/platform/issues/148)) +* create and update masternode identities ([#160](https://github.com/dashevo/platform/issues/160), [#170](https://github.com/dashevo/platform/issues/170), [#257](https://github.com/dashevo/platform/issues/257), [#272](https://github.com/dashevo/platform/issues/272), [#279](https://github.com/dashevo/platform/issues/279), [#287](https://github.com/dashevo/platform/issues/287)) +* added WalletStore ([#197](https://github.com/dashevo/platform/issues/197)) +* register system contracts on `initChain` ([#182](https://github.com/dashevo/platform/issues/182), [#192](https://github.com/dashevo/platform/issues/192)) +* integrate new storage (GroveDB) and secondary indices (RS Drive) ([#77](https://github.com/dashevo/platform/issues/77), [#177](https://github.com/dashevo/platform/issues/177), [#178](https://github.com/dashevo/platform/issues/178), [#199](https://github.com/dashevo/platform/issues/199), [#201](https://github.com/dashevo/platform/issues/201), [#225](https://github.com/dashevo/platform/issues/225), [#259](https://github.com/dashevo/platform/issues/259), [#280](https://github.com/dashevo/platform/issues/280), [#303](https://github.com/dashevo/platform/issues/303)) +* fallback to chain asset lock proof ([#297](https://github.com/dashevo/platform/issues/297)) +* add an ability to update data contract ([#52](https://github.com/dashevo/platform/issues/52), [#83](https://github.com/dashevo/platform/issues/83), [#223](https://github.com/dashevo/platform/issues/223)) +* add required `name` property to index definition ([#74](https://github.com/dashevo/platform/issues/74)) +* use document for `startAt` and `startAfter` in document query ([#227](https://github.com/dashevo/platform/pull/227), [#255](https://github.com/dashevo/platform/issues/255)) +* **dashmate:** enable mainnet for dashmate ([#2](https://github.com/dashevo/platform/issues/2)) +* **dashmate:** json output for status commands ([#31](https://github.com/dashevo/platform/issues/31), [#262](https://github.com/dashevo/platform/issues/262)) +* **dashmate:** add an ability to configure node subnet mask ([#237](https://github.com/dashevo/platform/issues/237)) +* **dpp:** add `readOnly` flag to `IdentityPublicKey` ([#142](https://github.com/dashevo/platform/issues/142), [#239](https://github.com/dashevo/platform/issues/239)) +* **dpp:** allow using BLS key to sign state transitions ([#268](https://github.com/dashevo/platform/issues/268), [#275](https://github.com/dashevo/platform/issues/275)) +* **drive:** network address in `ValidatorUpdate` ABCI ([#140](https://github.com/dashevo/platform/issues/140), [#155](https://github.com/dashevo/platform/issues/155), [#184](https://github.com/dashevo/platform/issues/184)) +* **drive:** add performance timers to measure block execution ([#281](https://github.com/dashevo/platform/issues/281)) +* **dapi:** `subscribeToBlockHeadersWithChainLocks` endpoint ([#153](https://github.com/dashevo/platform/issues/153)) +* **wallet-lib:** ChainStore ([#196](https://github.com/dashevo/platform/issues/196)) +* **dapi-client:** get and verify block headers with dash-spv ([#211](https://github.com/dashevo/platform/issues/211)) +* **dapi-client:** handle asynchronous errors ([#233](https://github.com/dashevo/platform/issues/233)) + + +### Bug Fixes + +* **dashmate:** `cannot read properties of undefined (reading 'dpns')` on reset ([#47](https://github.com/dashevo/platform/issues/47)) +* **drive:** missed JS ABCI yarn cache ([#156](https://github.com/dashevo/platform/issues/156)) +* **build:** `zeromq` build is not working on linux ([#236](https://github.com/dashevo/platform/issues/236)) +* cannot install `protobufjs` in some cases ([#266](https://github.com/dashevo/platform/issues/266), [#267](https://github.com/dashevo/platform/issues/267)) +* **dashmate:** `rimraf` module could not remove config directory ([#248](https://github.com/dashevo/platform/issues/248)) +* **dashmate:** logs were incorrectly mounted ([#261](https://github.com/dashevo/platform/issues/261)) +* **drive:** documents have mixed owner ids ([#283](https://github.com/dashevo/platform/issues/283)) +* cannot read properties of undefined (reading 'getIp') ([#285](https://github.com/dashevo/platform/issues/285)) +* InstantLock waiting period for transaction... ([#293](https://github.com/dashevo/platform/issues/293)) +* **dpp:** re2 memory leak ([#301](https://github.com/dashevo/platform/issues/301)) +* **drive:** internal error on verify instant lock ([#295](https://github.com/dashevo/platform/issues/295)) + + +### Documentation + +* improved sidebar and usage in DAPI client ([#3](https://github.com/dashevo/platform/issues/3)) +* provide getTransactionHistory ([#5](https://github.com/dashevo/platform/issues/5)) +* minor Readme fixes ([#163](https://github.com/dashevo/platform/issues/163)) +* add readme to docs folder ([#175](https://github.com/dashevo/platform/issues/175)) +* escape literal '|' in table ([#164](https://github.com/dashevo/platform/issues/164)) +* indicate which network(s) this repo supports ([#174](https://github.com/dashevo/platform/issues/174)) +* ignore folder with empty docs during build ([#212](https://github.com/dashevo/platform/issues/212)) + + +### Tests + +* **wallet-lib:** enable skipped test after the fix for grpc-js lib ([#71](https://github.com/dashevo/platform/issues/71)) + + +### Miscellaneous Chores + +* fix wrong version in a release PR title ([#82](https://github.com/dashevo/platform/issues/82)) +* missed merk darwin x64 pre-build binary ([#144](https://github.com/dashevo/platform/issues/144)) +* undefined "-w" argument in restart script ([#85](https://github.com/dashevo/platform/issues/85)) +* **drive:** send initial core chain locked height on init chain ([#180](https://github.com/dashevo/platform/issues/180)) +* update to use current @oclif/core ([#154](https://github.com/dashevo/platform/issues/154)) +* remove `fixCumulativeFeesBug` feature flag ([#191](https://github.com/dashevo/platform/issues/191)) +* update tenderdash and core images ([#188](https://github.com/dashevo/platform/issues/188), [#252](https://github.com/dashevo/platform/issues/252), [#269](https://github.com/dashevo/platform/issues/269)) +* **dpp:** temporarily disable $refs in data contract definitions ([#300](https://github.com/dashevo/platform/issues/300)) +* **dpp:** size constraints for indexed properties ([#179](https://github.com/dashevo/platform/issues/179), [#273](https://github.com/dashevo/platform/issues/273)) + + +### Build System + +* **test-suite:** docker image build doesn't work ([#172](https://github.com/dashevo/platform/issues/172)) +* fix configure test suite script for grep 2.5.1 ([#187](https://github.com/dashevo/platform/issues/187)) + + +### Code Refactoring + +* **dapi:** rename tx-filter-stream.js to core-streams.js ([#169](https://github.com/dashevo/platform/issues/169)) + + +## [0.22.0-dev.16](https://github.com/dashevo/platform/compare/v0.22.0-dev.15...v0.22.0-dev.16) (2022-03-18) + + +### ⚠ BREAKING CHANGES + +* previously created platform state might be not compatible + +### Features + +* **dpp:** temporarily disable $refs in data contract definitions ([#300](https://github.com/dashevo/platform/issues/300)) +* fallback to chain asset lock proof ([#297](https://github.com/dashevo/platform/issues/297)) + + +### Bug Fixes + +* **dpp:** re2 memory leak ([#301](https://github.com/dashevo/platform/issues/301)) +* **drive:** document query and delete issues ([#303](https://github.com/dashevo/platform/issues/303)) +* **drive:** internal error on verify instant lock ([#295](https://github.com/dashevo/platform/issues/295)) + +## [0.22.0-dev.15](https://github.com/dashevo/platform/compare/v0.22.0-dev.14...v0.22.0-dev.15) (2022-03-11) + + +### Bug Fixes + +* InstantLock waiting period for transaction.. ([#293](https://github.com/dashevo/platform/issues/293)) + +## [0.22.0-dev.14](https://github.com/dashevo/platform/compare/v0.22.0-dev.13...v0.22.0-dev.14) (2022-03-10) + + +### ⚠ BREAKING CHANGES + +* The fixed masternode identities logic breaks compatibility with previous invalid state. + +### Bug Fixes + +* **drive:** non-deterministic behaviour in masternode identities logic ([#287](https://github.com/dashevo/platform/issues/287)) + +## [0.22.0-dev.13](https://github.com/dashevo/platform/compare/v0.22.0-dev.12...v0.22.0-dev.13) (2022-03-09) + + +### Bug Fixes + +* cannot read properties of undefined (reading 'getIp') ([#285](https://github.com/dashevo/platform/issues/285)) + +## [0.22.0-dev.12](https://github.com/dashevo/platform/compare/v0.22.0-dev.11...v0.22.0-dev.12) (2022-03-08) + + +### Bug Fixes + +* **drive:** documents have mixed owner ids ([#283](https://github.com/dashevo/platform/issues/283)) + +## [0.22.0-dev.11](https://github.com/dashevo/platform/compare/v0.22.0-dev.10...v0.22.0-dev.11) (2022-03-08) + + +### ⚠ BREAKING CHANGES + +* `in` query operator doesn't work with multiple values (#280) + +### Features + +* **drive:** add performance timers to measure block execution ([#281](https://github.com/dashevo/platform/issues/281)) + + +### Bug Fixes + +* `in` query operator doesn't work with multiple values ([#280](https://github.com/dashevo/platform/issues/280)) +* can't find masternode raward shares data contract ([#279](https://github.com/dashevo/platform/issues/279)) + +## [0.22.0-dev.10](https://github.com/dashevo/platform/compare/v0.22.0-dev.9...v0.22.0-dev.10) (2022-03-07) + + +### Bug Fixes + +* **dpp:** Invalid DER format public key ([#275](https://github.com/dashevo/platform/issues/275)) + +## [0.22.0-dev.9](https://github.com/dashevo/platform/compare/v0.22.0-dev.8...v0.22.0-dev.9) (2022-03-04) + + +### ⚠ BREAKING CHANGES + +* **dpp:** lower indexed string properties constraints (#273) + +### Features + +* **dpp:** lower indexed string properties constraints ([#273](https://github.com/dashevo/platform/issues/273)) + + +### Bug Fixes + +* masternode reward shares ([#272](https://github.com/dashevo/platform/issues/272)) + +## [0.22.0-dev.8](https://github.com/dashevo/platform/compare/v0.21.8...v0.22.0-dev.8) (2022-03-01) + + +### ⚠ BREAKING CHANGES + +* New state is not compatible with previous versions +* Document queries have limitations compared with previous versions +* Proofs are temporary disabled + +### Features + +* **dapi-client:** get and verify block headers with dash-spv ([#211](https://github.com/dashevo/platform/issues/211)) +* **dapi-client:** handle asynchronous errors ([#233](https://github.com/dashevo/platform/issues/233)) +* **dashmate:** add an ability to configure node subnet mask ([#237](https://github.com/dashevo/platform/issues/237)) +* **dpp:** allow using BLS key to sign state transitions ([#268](https://github.com/dashevo/platform/issues/268)) +* **dpp:** do not allow to index array properties ([#225](https://github.com/dashevo/platform/issues/225)) +* **drive:** create/update identities based on SML changes ([#170](https://github.com/dashevo/platform/issues/170)) +* integrate RS Drive and GroveDB ([#177](https://github.com/dashevo/platform/issues/177)) + + +### Bug Fixes + +* **dashmate:** `group:status` command was missing a `format` flag ([#262](https://github.com/dashevo/platform/issues/262)) +* `startAt` and `startAfter` invalid decoding ([#255](https://github.com/dashevo/platform/issues/255)) +* **build:** `zeromq` build is not working on linux ([#236](https://github.com/dashevo/platform/issues/236)) +* cannot install `protobufjs` in some cases ([#266](https://github.com/dashevo/platform/issues/266)) +* **dashmate:** `rimraf` module could not remove config directory ([#248](https://github.com/dashevo/platform/issues/248)) +* **dashmate:** logs were incorrectly mounted ([#261](https://github.com/dashevo/platform/issues/261)) +* **dpp:** Identity public key `readOnly` flag was read as `undefined` instead of `false` ([#239](https://github.com/dashevo/platform/issues/239)) +* **drive:** unable to reconstruct SML ([#257](https://github.com/dashevo/platform/issues/257)) +* **drive:** invalid query errors are fatal ([#259](https://github.com/dashevo/platform/issues/259)) +* **sdk:** can't update cached data contract ([#223](https://github.com/dashevo/platform/issues/223)) + + +### Documentation + +* ignore folder with empty docs during build ([#212](https://github.com/dashevo/platform/issues/212)) + + +### Build System + +* `protobufjs` isn't installing from git sometimes ([#267](https://github.com/dashevo/platform/issues/267)) + + +### Miscellaneous Chores + +* **dashmate:** update Core to 0.18.0.0-beta4 ([#269](https://github.com/dashevo/platform/issues/269)) +* **release:** revert version back +* update tenderdash and core images ([#252](https://github.com/dashevo/platform/issues/252)) + + + +## [0.21.8](https://github.com/dashevo/platform/compare/v0.21.7...v0.21.8) (2022-02-15) + + +### Bug Fixes + +* sorting unconfirmed tx as oldest ([#206](https://github.com/dashevo/platform/issues/206)) +* **wallet-lib:** get transaction history missing txs ([#246](https://github.com/dashevo/platform/issues/246)) + + +### Tests + +* **platform-suite:** add -b flag to abort after first error ([#222](https://github.com/dashevo/platform/issues/222)) + + +### Miscellaneous Chores + +* updates @dashevo/dashcore-lib to v0.19.30 ([#238](https://github.com/dashevo/platform/issues/238)) + + +## [0.22.0-dev.7](https://github.com/dashevo/platform/compare/v0.21.7...v0.22.0-dev.7) (2022-01-19) + + +### Features + +* added WalletStore ([#197](https://github.com/dashevo/platform/issues/197)) +* **drive:** allow using `in` and `startsWith` only in last `where` condition ([#201](https://github.com/dashevo/platform/issues/201)) +* **drive:** allow using `orderBy` for fields having `in` and `startsWith` in last `where` clause ([#199](https://github.com/dashevo/platform/issues/199)) +* register system contracts on `initChain` ([#182](https://github.com/dashevo/platform/issues/182)) +* **wallet-lib:** ChainStore ([#196](https://github.com/dashevo/platform/issues/196)) + + +### Bug Fixes + +* **sdk:** system contract ids were hardcoded in SDKs Client module ([#192](https://github.com/dashevo/platform/issues/192)) + + +### Build System + +* fix configure test suite script for grep 2.5.1 ([#187](https://github.com/dashevo/platform/issues/187)) + + +### Miscellaneous Chores + +* **dashmate:** update tenderdash to 0.7.0-dev ([#188](https://github.com/dashevo/platform/issues/188)) +* remove `fixCumulativeFeesBug` feature flag ([#191](https://github.com/dashevo/platform/issues/191)) + + + +## [0.21.7](https://github.com/dashevo/platform/compare/v0.21.6...v0.21.7) (2022-01-17) + + +### ⚠ BREAKING CHANGES + +* **dashmate:** `platform.drive.abci.docker.build.path' and 'platform.dapi.api.docker.build.path' are removed in favor of `platform.sourcePath' + +### Features + +* **dashmate:** build DAPI and Drive from monorepo path ([#145](https://github.com/dashevo/platform/issues/145)) +* distribute dashmate with NPM ([#148](https://github.com/dashevo/platform/issues/148)) +* support Apple Silicone ([#143](https://github.com/dashevo/platform/issues/143)) + + +### Bug Fixes + +* instantlock waiting period for transaction timed out + + +### Miscellaneous Chores + +* fix wrong version in a release PR title ([#82](https://github.com/dashevo/platform/issues/82)) +* missed merk darwin x64 pre-build binary ([#144](https://github.com/dashevo/platform/issues/144)) +* undefined "-w" argument in restart script ([#85](https://github.com/dashevo/platform/issues/85)) + + +### Documentation + +* escape literal '|' in table ([#164](https://github.com/dashevo/platform/issues/164)) + + +### Tests + +* **wallet-lib:** fix hanging functional test ([#186](https://github.com/dashevo/platform/issues/186)) + +## [0.22.0-dev.6](https://github.com/dashevo/platform/compare/v0.22.0-dev.5...v0.22.0-dev.6) (2022-01-11) + + +### ⚠ BREAKING CHANGES + +* **drive:** temporary restrictions for a document query (#77) + +### Features + +* **dapi:** `subscribeToBlockHeadersWithChainLocks` endpoint ([#153](https://github.com/dashevo/platform/issues/153)) + + +### Bug Fixes + +* **drive:** missed `nodeAddress` field on `EndBlock` ([#184](https://github.com/dashevo/platform/issues/184)) + + +### Miscellaneous Chores + +* **drive:** temporary restrictions for a document query ([#77](https://github.com/dashevo/platform/issues/77)) + + +### Build System + +* **test-suite:** docker image build doesn't work ([#172](https://github.com/dashevo/platform/issues/172)) + + +### Code Refactoring + +* **dapi:** rename tx-filter-stream.js to core-streams.js ([#169](https://github.com/dashevo/platform/issues/169)) + + +### Documentation + +* add readme to docs folder ([#175](https://github.com/dashevo/platform/issues/175)) +* escape literal '|' in table ([#164](https://github.com/dashevo/platform/issues/164)) +* indicate which network(s) this repo supports ([#174](https://github.com/dashevo/platform/issues/174)) + +## [0.22.0-dev.5](https://github.com/dashevo/platform/compare/v0.22.0-dev.4...v0.22.0-dev.5) (2022-01-07) + + +### ⚠ BREAKING CHANGES + +* **dpp:** `$id` can't be used in secondary indices +* **dpp:** Indexed properties now require size constraints +* allow using non-unique Identity public keys (#168) +* **dashmate:** `platform.drive.abci.docker.build.path' and 'platform.dapi.api.docker.build.path' are removed in favor of `platform.sourcePath' + +### Features + +* allow adding non-unique indices for newly defined properties ([#83](https://github.com/dashevo/platform/issues/83)) +* allow using non-unique Identity public keys ([#168](https://github.com/dashevo/platform/issues/168)) +* **dashmate:** build DAPI and Drive from monorepo path ([#145](https://github.com/dashevo/platform/issues/145)) +* distribute dashmate with NPM ([#148](https://github.com/dashevo/platform/issues/148)) +* **dpp:** `$id` can't be used in secondary indices ([#178](https://github.com/dashevo/platform/issues/178)) +* **dpp:** size constraints for indexed properties ([#179](https://github.com/dashevo/platform/issues/179)) +* masternode reward shares contract ([#160](https://github.com/dashevo/platform/issues/160)) + + +### Bug Fixes + +* downgrade dash-core image to v0.17 ([#171](https://github.com/dashevo/platform/issues/171)) + + +### Documentation + +* minor Readme fixes ([#163](https://github.com/dashevo/platform/issues/163)) + + +### Miscellaneous Chores + +* **drive:** send initial core chain locked height on init chain ([#180](https://github.com/dashevo/platform/issues/180)) +* update to use current @oclif/core ([#154](https://github.com/dashevo/platform/issues/154)) + +## [0.22.0-dev.4](https://github.com/dashevo/platform/compare/v0.22.0-dev.3...v0.22.0-dev.4) (2021-12-24) + + +### Bug Fixes + +* **drive:** `ValidatorSetUpdate` doesn't contain `nodeAddress` ([#155](https://github.com/dashevo/platform/issues/155)) +* **drive:** missed JS ABCI yarn cache ([#156](https://github.com/dashevo/platform/issues/156)) + +## [0.22.0-dev.3](https://github.com/dashevo/platform/compare/v0.21.6...v0.22.0-dev.3) (2021-12-21) + + +### ⚠ BREAKING CHANGES + +* add required `name` property to index definition (#74) +* add an ability to update data contract (#52) +* Identity public key now has two more fields, purpose and securityLevel, and keys without those fields won't be valid anymore + +### Features + +* add an ability to update data contract ([#52](https://github.com/dashevo/platform/issues/52)) +* add required `name` property to index definition ([#74](https://github.com/dashevo/platform/issues/74)) +* **dashmate:** json output for status commands ([#31](https://github.com/dashevo/platform/issues/31)) +* **dpp:** add `readOnly` flag to `IdentityPublicKey` ([#142](https://github.com/dashevo/platform/issues/142)) +* **drive:** network address in `ValidatorUpdate` ABCI ([#140](https://github.com/dashevo/platform/issues/140)) +* enable mainnet for dashmate ([#2](https://github.com/dashevo/platform/issues/2)) +* identity public key purpose and security levels ([#46](https://github.com/dashevo/platform/issues/46)) +* support Apple Silicone ([#143](https://github.com/dashevo/platform/issues/143)) +* **wallet-lib:** do not sync transactions if mnemonic is absent +* **wallet-lib:** dump wallet storage ([#8](https://github.com/dashevo/platform/issues/8)) + + +### Bug Fixes + +* **dashmate:** `cannot read properties of undefined (reading 'dpns')` on reset ([#47](https://github.com/dashevo/platform/issues/47)) + + +### Documentation + +* improved sidebar and usage in DAPI client ([#3](https://github.com/dashevo/platform/issues/3)) +* provide getTransactionHistory ([#5](https://github.com/dashevo/platform/issues/5)) + + +### Tests + +* **wallet-lib:** enable skipped test after the fix for grpc-js lib ([#71](https://github.com/dashevo/platform/issues/71)) + + +### Miscellaneous Chores + +* fix wrong version in a release PR title ([#82](https://github.com/dashevo/platform/issues/82)) +* missed merk darwin x64 pre-build binary ([#144](https://github.com/dashevo/platform/issues/144)) +* undefined "-w" argument in restart script ([#85](https://github.com/dashevo/platform/issues/85)) + + +## [0.21.6](https://github.com/dashevo/platform/compare/v0.21.5...v0.21.6) (2021-12-13) + + +### Bug Fixes + +* **dashmate:** RPC error on stopping node ([#61](https://github.com/dashevo/platform/issues/61)) +* **wallet-lib:** "Failure: Type not convertible to Uint8Array" ([#60](https://github.com/dashevo/platform/issues/60)) +* **wallet-lib:** eventemitter memory leak ([#56](https://github.com/dashevo/platform/issues/56)) +* **wallet-lib:** invalid deserialization of persistent storage ([#76](https://github.com/dashevo/platform/issues/76)) + + +### Documentation + +* publish consolidated docs using mkdocs ([#42](https://github.com/dashevo/platform/issues/42)) + + +### Miscellaneous Chores + +* changelogs generation script ([#62](https://github.com/dashevo/platform/issues/62)) +* enable yarn PnP to achieve zero installs ([#63](https://github.com/dashevo/platform/issues/63)) +* exit if some env variables are empty during setup ([#75](https://github.com/dashevo/platform/issues/75)) +* fix `test:drive` script ([#78](https://github.com/dashevo/platform/issues/78)) +* migrate from NPM to Yarn 3 ([#50](https://github.com/dashevo/platform/issues/50)) +* remove temporary reset script ([#64](https://github.com/dashevo/platform/issues/64)) +* update oclif and remove pnpify ([#73](https://github.com/dashevo/platform/issues/73)) + + +### Build System + +* fix bash syntax issue in release script ([#79](https://github.com/dashevo/platform/issues/79)) +* release process automation ([#67](https://github.com/dashevo/platform/issues/67)) + +## [0.21.5](https://github.com/dashevo/platform/compare/v0.21.4...v0.21.5) (2021-11-25) + + +### Bug Fixes + +* new instant lock is not compatible with DashCore 0.17 ([#57](https://github.com/dashevo/platform/issues/57)) +* **wallet-lib:** tx chaining mempool conflict errors ([#57](https://github.com/dashevo/platform/issues/44)) + + +### Continuous Integration +* use correct Dockerfile in test suite release ([#58](https://github.com/dashevo/platform/issues/58)) +* set correct docker tag outputs in release workflow ([#55](https://github.com/dashevo/platform/issues/55)) +* enable NPM login on for release workflow ([#54](https://github.com/dashevo/platform/issues/54)) + + +## [0.21.4](https://github.com/dashevo/platform/compare/v0.21.0...v0.21.4) (2021-11-23) + + +### Bug Fixes + +* **dapi-client:** expect 100 but got 122 in SML provider test ([#22](https://github.com/dashevo/platform/issues/22)) +* **dapi-client:** retry doesn’t work with 502 errors ([#35](https://github.com/dashevo/platform/issues/35)) +* **dapi:** Identifier expects Buffer ([#28](https://github.com/dashevo/platform/issues/28)) +* **dashmate:** ajv schema errors ([#14](https://github.com/dashevo/platform/issues/14)) +* **dashmate:** reset command doesn't work if setup failed ([#23](https://github.com/dashevo/platform/issues/23)) +* **dashmate:** cannot read properties error on group:reset ([#47](https://github.com/dashevo/platform/issues/47)) +* **dashmate:** json output for status commands ([#31](https://github.com/dashevo/platform/issues/31)) +* **dashmate:** enable mainnet for dashmate ([#2](https://github.com/dashevo/platform/issues/2)) +* **dpp:** rename generateEntropy to entropyGenerator ([#13](https://github.com/dashevo/platform/issues/13)) +* **sdk:** dpp hash function import ([#15](https://github.com/dashevo/platform/issues/15)) +* **sdk:** override ts-node target for unit tests ([#21](https://github.com/dashevo/platform/issues/21)) +* **sdk:** this is undefined during unit tests ([#18](https://github.com/dashevo/platform/issues/18)) + + +### Features + +* **dashmate:** force option for `group:stop` command ([#36](https://github.com/dashevo/platform/issues/36)) +* **dashmate:** provide docker build logs for verbose mode ([#19](https://github.com/dashevo/platform/issues/19)) +* migrate to DashCore 0.18.0.0-beta1 ([#51](https://github.com/dashevo/platform/issues/51)) +* **wallet-lib:** dump wallet storage ([#8](https://github.com/dashevo/platform/issues/8)) +* **wallet-lib:** do not sync transactions if mnemonic is absent ([#7](https://github.com/dashevo/platform/issues/7)) + + +### Performance Improvements + +* **test-suite:** speedup test suite up to 6 times ([#30](https://github.com/dashevo/platform/issues/30)) + + +### Build System +* build only necessary packages ([#27](https://github.com/dashevo/platform/issues/27)) +* run npm scripts in parallel ([#33](https://github.com/dashevo/platform/issues/33)) +* cache native npm modules during docker build ([#20](https://github.com/dashevo/platform/issues/20)) +* setup semantic pull requests ([#11](https://github.com/dashevo/platform/issues/11)) +* **sdk:** upgrade to webpack 5 ([#6](https://github.com/dashevo/platform/issues/6)) + + +### Continuous Integration +* simplify release workflow ([#48](https://github.com/dashevo/platform/issues/48)) +* show docker logs on failure ([#43](https://github.com/dashevo/platform/issues/43)) +* check mismatch dependencies ([#26](https://github.com/dashevo/platform/issues/26)) +* run package tests in parallel ([#25](https://github.com/dashevo/platform/issues/25)) + + +### Tests +* adjust timeouts ([#45](https://github.com/dashevo/platform/issues/45)) +* **test-suite:** skipSynchronizationBeforeHeight option with new wallet ([#34](https://github.com/dashevo/platform/issues/34)) +* **dpp:** fix invalid network floating error ([#32](https://github.com/dashevo/platform/issues/32)) +* **dpp:** grpc common bootstrap not working ([#16](https://github.com/dashevo/platform/issues/16)) + + +### Documentation +* markdown link fixes ([#49](https://github.com/dashevo/platform/issues/49)) +* add README.md for the whole platform as a project ([#38](https://github.com/dashevo/platform/issues/38)) +* add contributing.md ([#37](https://github.com/dashevo/platform/issues/37)) +* **sdk:** provide getTransactionHistory ([#5](https://github.com/dashevo/platform/issues/5)) +* improved sidebar and usage in DAPI client ([#3](https://github.com/dashevo/platform/issues/3)) + + +### Styles +* fix ES linter errors ([#24](https://github.com/dashevo/platform/issues/24)) + + +### BREAKING CHANGES + +* supports only new DashCore InstantLock format https://github.com/dashpay/dips/blob/master/dip-0022.md + + +# Previous versions + +Before 0.21.x, packages were located in separate repositories and have own changelogs: + +* [DAPI Client](https://github.com/dashevo/js-dapi-client/blob/master/CHANGELOG.md) +* [DAPI gRPC](https://github.com/dashevo/dapi-grpc/blob/master/CHANGELOG.md) +* [DAPI](https://github.com/dashevo/dapi/blob/master/CHANGELOG.md) +* [Dashmate](https://github.com/dashevo/dashmate/blob/master/CHANGELOG.md) +* [DashPay contract](https://github.com/dashevo/dashpay-contract/blob/master/CHANGELOG.md) +* [Feature Flags Contract](https://github.com/dashevo/feature-flags-contract/blob/master/CHANGELOG.md) +* [Dash SDK](https://github.com/dashevo/js-dash-sdk/blob/master/CHANGELOG.md) +* [Dash Platform Protocol JS](https://github.com/dashevo/js-dpp/blob/master/CHANGELOG.md) +* [Drive](https://github.com/dashevo/js-drive/blob/master/CHANGELOG.md) +* [Dash Platform Test Suite](https://github.com/dashevo/platform-test-suite/blob/master/CHANGELOG.md) +* [Wallet Library](https://github.com/dashevo/wallet-lib/blob/master/CHANGELOG.md) diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000000..452505da027 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @shumkov @antouhou \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000000..9eb50a49cb1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,143 @@ +Contributing to Dash Platform +============================= + +The Dash Platform project operates an open contributor model where anyone is +welcome to contribute towards development in the form of peer review, testing +and patches. This document explains the practical process and guidelines for +contributing. + + +Branches, Bugfixes and New Features +----------------------------------- + +The current stable release is in the `master` branch. This branch is meant to be +stable, so the only PRs made to master should be bugfixes. Development of the +next release happens on the `vX-dev` branch, where `X` is the next version +number to be released. All new feature PRs must be made to the current dev +branch. + +The body of the PR should also follow the default PR template that appears when +you open a PR on GitHub. Please fill all template fields with a sufficient +description of what the patch does, together with any justification/reasoning. +You should include references to any discussions (for example other tickets or +mailing list discussions). + +If a pull request is not (yet) ready to be considered for merging, please set +its status to "Draft" on GitHub. + + +Conventional Commits +-------------------- + +All commits and pull request titles should follow the Conventional Commits +specification. PR titles follow the `(optional scope): ` +scheme. Please see the specification linked below for valid types. When making a +change to a specific component, please specify the name of the component inside +the scope. For example, if you are developing a new feature for the SDK, the PR +title should look like this: `feat(sdk): amazing new feature`. + +For more details on allowed types and more information about Conventional +Commits, please see the [Conventional Commits +specification](https://www.conventionalcommits.org/en/v1.0.0/). For available +scopes, please see the [.github/semantic.yml](.github/semantic.yml) file. + +In general, [commits should be +atomic](https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention) +and diffs should be easy to read. For this reason, do not mix any formatting +fixes or code movement with actual code changes. + + +Code Conventions +---------------- + +Please ensure that the code you write adheres to the code style adopted in the +project, and that all linting checks are passing. We use [AirBnB +style](https://github.com/airbnb/javascript) for JS code. + + +Testing +------- + +The code must be accompanied by tests to check the functionality. Tests for +individual components are stored inside `packages//tests`, while +e2e test are inside `packages/platform-test-suite`. + +Test case names should start with a lowercase "should", i.e. "should do x". Unit +and integration tests should mirror the file structure of `/src` or `/lib` +(depending on the component). + +Code should generally be covered by unit and integration tests, and functional +or e2e tests should be written for larger chunks of functionality (when +appropriate). Unit and integration tests should not make any network calls, and +unit tests should mock all dependencies. + + +Squashing Commits +----------------- + +If your pull request is accepted for merging, you may be asked by a maintainer +to squash and/or [rebase](https://git-scm.com/docs/git-rebase) your commits +before it will be merged. The basic squashing workflow is shown below. + + git checkout your_branch_name + git rebase -i HEAD~n + # n is normally the number of commits in the pull request. + # Set commits (except the one in the first line) from 'pick' to 'squash', save and quit. + # On the next screen, edit/refine commit messages. + # Save and quit. + git push -f # (force push to GitHub) + +If you have problems with squashing (or other workflows with `git`), you can +alternatively enable "Allow edits from maintainers" in the right GitHub sidebar +and ask for help in the pull request. + +Please refrain from creating several pull requests for the same change. Use the +pull request that is already open (or was created earlier) to amend changes. +This preserves the discussion and review that happened earlier for the +respective change set. + +The length of time required for peer review is unpredictable and will vary from +pull request to pull request. + + +Pull Request Philosophy +----------------------- + +Patchsets should always be focused. For example, a pull request could add a +feature, fix a bug, or refactor code; but not a mixture. Please also avoid super +pull requests which attempt to do too much, are overly large, or overly complex +as this makes review difficult. + + +"Decision Making" Process +------------------------- + +Whether a pull request is merged into Dash Platform rests with the project merge +maintainers. + +Maintainers will take into consideration if a patch is in line with the general +principles of the project and meets the minimum standards for inclusion. + +In general, all pull requests must: + +- Have a clear use case, fix a demonstrable bug or serve the greater good of the + project (for example refactoring for modularisation); +- Have unit tests and functional tests where appropriate; +- Follow code style guidelines; +- Not break the existing test suite; +- Where bugs are fixed, where possible, there should be unit tests demonstrating + the bug and also proving the fix. This helps prevent regression. + + +Release process +--------------- + +Coming soon. + +Copyright +--------- + +By contributing to this repository, you agree to license your work under the MIT +license unless specified otherwise at the top of the file itself. Any work +contributed where you are not the original author must contain its license +header with the original author(s) and source. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000000..64853f4c769 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017-2021 Dash Core Group, Inc. + +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/README.md b/README.md new file mode 100644 index 00000000000..638fa03557e --- /dev/null +++ b/README.md @@ -0,0 +1,163 @@ +

+ + babel + +

+ +

+ Seriously fast decentralized applications for the Dash network +

+ +

+ GitHub CI Status + Devs Chat + General Chat + Follow on Twitter +

+ +Dash Platform is a technology stack for building decentralized applications on +the Dash network. The two main architectural components, Drive and DAPI, turn +the Dash P2P network into a cloud that developers can integrate with their +applications. + +If you are looking for how to contribute to the project or need any help with +building an app on the Dash Platform - message us on the [Devs +Discord](https://chat.dashdevs.org/)! + +## Note: Dash Platform is currently available on the Dash Testnet only + +## Intro + +This is a multi-package repository - sometimes also known as monorepository - +that contains all packages that comprise the Dash platform - for example, Drive, +which is the storage component of Dash Platform, the JavaScript SDK, wallet-lib, +DAPI, and others. Every individual package contains its own readme. Packages are +located in the [packages](./packages) directory. + +### Supported networks + +Dash Platform is currently undergoing testing and final development necessary to +support its release on the Dash production network (mainnet). The packages in +this repository may be used on the following networks: + +- [x] **Development networks** ([**devnets**](https://dashplatform.readme.io/docs/reference-glossary#devnet)) +- [x] [**Testnet**](https://dashplatform.readme.io/docs/reference-glossary#testnet) +- [ ] [Mainnet](https://dashplatform.readme.io/docs/reference-glossary#mainnet) + +## Install & Build + +**Important**: Building the dev environment requires 2GB+ RAM - whatever the OS needs, plus 1.5GB for itself. + +1. Clone and enter the repo + ```bash + git clone https://github.com/dashevo/platform ./platform/ + pushd ./platform/ + ``` +2. Install prerequisites: + - gcc toolchain + ```bash + sudo apt install -y build-essential + ``` + - [node.js](https://nodejs.org/) v16.10.0+ + ```bash + curl https://webinstall.dev/node@16 | bash + ``` + - [docker](https://docs.docker.com/get-docker/) v20.10+ + ```bash + sudo apt update + + sudo sh -eux < test + # Example: run tests for the JS DAPI client + yarn workspace @dashevo/dapi-client test + ``` + See [./packages/README.md](./packages/README.md) for the list of available packages. +- To completely reset all local data and builds: + ```bash + yarn reset + ``` + +## FAQ + +### Where can I find support? + +For questions and support, please join our [Devs +Discord](https://chat.dashdevs.org/) + +### Where are the docs? + +Our docs are hosted on +[readme.io](https://dashplatform.readme.io/docs/introduction-what-is-dash-platform). +You can create issues and feature requests in the +[issues](https://github.com/dashevo/platform/issues) for this repository. + +### Want to report a bug or request a feature? + +Please read through our [CONTRIBUTING.md](CONTRIBUTING.md) and fill out the +issue template at [platform/issues](https://github.com/dashevo/platform/issues)! + +### Want to contribute to Dash Platform? + +Check out: + +- Our [Developers Discord](https://chat.dashdevs.org/) +- Our [CONTRIBUTING.md](CONTRIBUTING.md) to get started with setting up the + repo. +- Our [news](https://www.dash.org/news/) and [blog](https://www.dash.org/blog/) which contains release posts and + explanations. + +## License + +[MIT](LICENSE.md) diff --git a/db/.gitignore b/db/.gitignore new file mode 100644 index 00000000000..d6b7ef32c84 --- /dev/null +++ b/db/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000000..2e4763ffdc0 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,16 @@ +# Repository documentation + +Several of the packages in this repository contain developer documentation. This +folder is used to aggregate docs from several packages and then produce a +consolidated [GitHub Pages site](https://dashevo.github.io/platform/) using +MkDocs. The GitHub workflow described in [docs.yml](/.github/workflows/docs.yml) +builds the documents and publishes them. + +## Viewing documentation locally + +You can use [MkDocs](https://www.mkdocs.org/getting-started/) to serve the +documents locally. From the root of the repository, do the following: + +- Run [`./scripts/prepare_docs.sh`](/scripts/prepare_docs.sh) +- Run `mkdocs serve` +- Open the returned URL (typically http://127.0.0.1:8000/) \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000000..61a8e2b14a9 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,25 @@ +

+ + babel + +

+ +

+ Seriously fast decentralized applications for the Dash network +

+ +

+ GitHub CI Status + Devs Chat + General Chat + Follow on Twitter +

+ +Dash Platform is a technology stack for building decentralized applications on +the Dash network. The two main architectural components, Drive and DAPI, turn +the Dash P2P network into a cloud that developers can integrate with their +applications. + +If you are looking for how to contribute to the project or need any help with +building an app on the Dash Platform - message us on the [Devs +Discord](https://chat.dashdevs.org/)! \ No newline at end of file diff --git a/logs/.gitignore b/logs/.gitignore new file mode 100644 index 00000000000..d6b7ef32c84 --- /dev/null +++ b/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000000..096bd34240d --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1 @@ +site_name: Dash Platform diff --git a/package.json b/package.json new file mode 100644 index 00000000000..9bf21359391 --- /dev/null +++ b/package.json @@ -0,0 +1,76 @@ +{ + "name": "@dashevo/platform", + "version": "0.23.0-dev.4", + "private": true, + "scripts": { + "setup": "yarn install && yarn run build && yarn run configure", + "start": "yarn run dashmate group start --verbose -w", + "restart": "yarn run dashmate group restart --verbose", + "stop": "yarn run dashmate group stop --verbose", + "reset": "yarn run clean:data --force && yarn run setup", + "test": "ultra -r $* test", + "test:dapi": "ultra -r --filter \"packages/@(dapi|platform-test-suite)\" test", + "test:dapi-grpc": "ultra -r --filter \"packages/@(dapi|js-dapi-client|dapi-grpc|js-dash-sdk|js-drive|wallet-lib|platform-test-suite)\" test", + "test:dashpay-contract": "ultra -r --filter \"packages/@(dashpay-contract|js-dash-sdk|js-drive|js-dapi-client|js-dpp|wallet-lib|dapi|platform-test-suite)\" test", + "test:dpns-contract": "ultra -r --filter \"packages/@(dpns-contract|js-dash-sdk|js-drive|js-dapi-client|js-dpp|wallet-lib|dapi|platform-test-suite)\" test", + "test:feature-flags-contract": "ultra -r --filter \"packages/@(feature-flags-contract|js-dash-sdk|js-drive|js-dapi-client|js-dpp|wallet-lib|dapi|platform-test-suite)\" test", + "test:dapi-client": "ultra -r --filter \"packages/@(js-dapi-client|wallet-lib|js-dash-sdk|platform-test-suite)\" test", + "test:sdk": "ultra -r --filter \"packages/@(js-dash-sdk|platform-test-suite)\" test", + "test:spv": "ultra -r --filter \"packages/@(dash-spv|js-dapi-client)\" test", + "test:dpp": "ultra -r test", + "test:drive": "ultra -r --filter \"packages/@(js-drive|platform-test-suite)\" test", + "test:grpc-common": "ultra -r --filter \"packages/@(js-grpc-common|dapi-grpc|dapi|js-dapi-client|js-dash-sdk|js-drive|wallet-lib|platform-test-suite)\" test", + "test:suite": "yarn workspace @dashevo/platform-test-suite test", + "test:suite:browsers": "yarn workspace @dashevo/platform-test-suite test:browsers", + "test:wallet-lib": "ultra -r --filter \"packages/@(wallet-lib|js-dash-sdk|platform-test-suite)\" test", + "build": "ultra --recursive $* --build", + "lint": "ultra --recursive $* lint", + "configure:dashmate": "yarn exec ./scripts/configure_dashmate.sh", + "configure:network": "yarn exec ./scripts/setup_local_network.sh", + "configure:tests": "yarn exec ./scripts/configure_test_suite.sh", + "configure:dotenv": "yarn exec ./scripts/configure_dotenv.sh", + "configure": "yarn run configure:dashmate && yarn run configure:network && yarn run configure:tests && yarn run configure:dotenv", + "clean:data": "yarn run dashmate group reset --verbose --group=local --hard", + "clean": "yarn run clean:data && yarn run build", + "dashmate": "yarn exec packages/dashmate/bin/dashmate", + "release": "yarn exec ./scripts/release/release.sh", + "bench": "yarn workspace @dashevo/bench-suite bench" + }, + "packageManager": "yarn@3.1.0", + "ultra": { + "concurrent": [ + "clean" + ] + }, + "devDependencies": { + "add-stream": "^1.0.0", + "conventional-changelog": "^3.1.24", + "conventional-changelog-dash": "github:dashevo/conventional-changelog-dash", + "semver": "^7.3.2", + "tempfile": "^3.0.0", + "ultra-runner": "^3.10.5" + }, + "workspaces": [ + "packages/js-grpc-common", + "packages/bench-suite", + "packages/dapi-grpc", + "packages/js-abci", + "packages/js-dpp", + "packages/dashpay-contract", + "packages/dpns-contract", + "packages/feature-flags-contract", + "packages/js-dapi-client", + "packages/wallet-lib", + "packages/js-dash-sdk", + "packages/dapi", + "packages/js-drive", + "packages/dashmate", + "packages/platform-test-suite", + "packages/masternode-reward-shares-contract", + "packages/dash-spv" + ], + "resolutions": { + "elliptic": "6.5.3", + "bn.js": "4.12.0" + } +} diff --git a/packages/README.md b/packages/README.md new file mode 100644 index 00000000000..ae470d2a40e --- /dev/null +++ b/packages/README.md @@ -0,0 +1,32 @@ +### Core Packages + +| Package | Version | Description | +|---------|---------|-------------| +| [`@dashevo/dpp`](/packages/js-dpp) | [![npm](https://img.shields.io/npm/v/@dashevo/dpp.svg?maxAge=3600)](https://www.npmjs.com/package/@dashevo/dpp) | JS implementation of Dash Platform Protocol, the core data structures used by the Platform | +| [`@dashevo/dpns-contract`](/packages/dpns-contract) | [![npm](https://img.shields.io/npm/v/@dashevo/dpns-contract.svg?maxAge=3600)](https://www.npmjs.com/package/@dashevo/dpns-contract) | Data Contract for DashPay Naming Service | +| [`@dashevo/grpc-common`](/packages/js-grpc-common) | [![npm](https://img.shields.io/npm/v/@dashevo/grpc-common.svg?maxAge=3600)](https://www.npmjs.com/package/@dashevo/grpc-common) | Common gRPC packages | +| [`@dashevo/feature-flags-contract`](/packages/feature-flags-contract) | [![npm](https://img.shields.io/npm/v/@dashevo/feature-flags-contract.svg?maxAge=3600)](https://www.npmjs.com/package/@dashevo/feature-flags-contract) | System data contract to enable feature flags | + +### Fullnode & Masternode tools + +| Package | Version | Description | +|---------|---------|-------------| +| [`dashmate`](/packages/dashmate) | [![npm](https://img.shields.io/npm/v/dashmate.svg?maxAge=3600)](https://www.npmjs.com/package/dashmate) | A tool for managing full nodes and masternodes | + +### Platform Client Packages + +| Package | Version | Description | +|---------|---------|-------------| +| [`dash`](/packages/js-dash-sdk) | [![npm](https://img.shields.io/npm/v/dash.svg?maxAge=3600)](https://www.npmjs.com/package/dash) | JavaScript SDK and light client | +| [`@dashevo/wallet-lib`](/packages/wallet-lib) | [![npm](https://img.shields.io/npm/v/@dashevo/wallet-lib.svg?maxAge=3600)](https://www.npmjs.com/package/@dashevo/wallet-lib) | JavaScript light client wallet library | +| [`@dashevo/dapi-client`](/packages/js-dapi-client) | [![npm](https://img.shields.io/npm/v/@dashevo/dapi-client.svg?maxAge=3600)](https://www.npmjs.com/package/@dashevo/js-dapi-client) | JavaScript client to connect to DAPI | +| [`@dashevo/dapi-grpc`](/packages/dapi-grpc) | [![npm](https://img.shields.io/npm/v/@dashevo/dapi-grpc.svg?maxAge=3600)](https://www.npmjs.com/package/@dashevo/dapi-grpc) | gRPC clients for various platforms (Web, Android, iOS, Java, Python) to interact with DAPI | +| [`@dashevo/dashpay-contract`](/packages/dashpay-contract) | [![npm](https://img.shields.io/npm/v/@dashevo/dashpay-contract.svg?maxAge=3600)](https://www.npmjs.com/package/@dashevo/dashpay-contract) | DashPay social payments app data contract | + +### Platform Node Packages + +| Package | Version | Description | +|---------|---------|-------------| +| [`@dashevo/drive`](/packages/js-drive) | [![docker](https://img.shields.io/docker/v/dashpay/drive?label=docker&&maxAge=3600)](https://hub.docker.com/r/dashpay/drive) | Platform replicated state machine | +| [`@dashevo/dapi`](/packages/dapi) | [![docker](https://img.shields.io/docker/v/dashpay/dapi?label=docker&maxAge=3600)](https://hub.docker.com/r/dashpay/dapi) | Platform Decentralized API | + diff --git a/packages/bench-suite/.env.example b/packages/bench-suite/.env.example new file mode 100644 index 00000000000..bb7450a2498 --- /dev/null +++ b/packages/bench-suite/.env.example @@ -0,0 +1,17 @@ +# DAPI seed ("ip:port") +DAPI_SEED= + +# Private key to fund wallets used in tests +FAUCET_ADDRESS= +FAUCET_PRIVATE_KEY= + +# Path to Drive JSON logs +DRIVE_LOG_PATH= + +# Network +NETWORK= + +# VERBOSE=1 + +# Start to sync wallet from specific height to speed up the sync process +# SKIP_SYNC_BEFORE_HEIGHT= diff --git a/packages/bench-suite/.eslintrc b/packages/bench-suite/.eslintrc new file mode 100644 index 00000000000..53708855109 --- /dev/null +++ b/packages/bench-suite/.eslintrc @@ -0,0 +1,36 @@ +{ + "extends": "airbnb-base", + "env": { + "es2021": true, + "node": true + }, + "parser": "babel-eslint", + "rules": { + "no-plusplus": 0, + "eol-last": [ + "error", + "always" + ], + "no-continue": "off", + "class-methods-use-this": "off", + "no-await-in-loop": "off", + "no-restricted-syntax": [ + "error", + { + "selector": "LabeledStatement", + "message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand." + }, + { + "selector": "WithStatement", + "message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize." + } + ], + "curly": [ + "error", + "all" + ] + }, + "globals": { + "BigInt": true + } +} diff --git a/packages/bench-suite/README.md b/packages/bench-suite/README.md new file mode 100644 index 00000000000..c85578e5739 --- /dev/null +++ b/packages/bench-suite/README.md @@ -0,0 +1,37 @@ +## Bench Suite + +> Dash Platform benchmark tool + +### Benchmarks + +Benchmark configs are located in [benchmarks](./benchmarks) directory. New benchmarks can be easily added to [benchmarks/index.js](./benchmarks/index.js) + +At this moment two types of benchmark are implemented: documents and function benchmarks. + +#### Documents + +This benchmark publishes a data contract and documents defined in configuration and collects timings from Drive logs. + +### Function + +Function benchmark allow to call a function or functions and collect metrics using [performance tools](https://nodejs.org/docs/latest-v16.x/api/perf_hooks.html). + +### Running benchmarks + +```bash +yarn setup +yarn start +yarn bench +``` + +## Maintainer + +[@shumkov](https://github.com/shumkov) + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs. + +## License + +[MIT](LICENSE) © Dash Core Group, Inc. diff --git a/packages/bench-suite/benchmarks/basicValidation.js b/packages/bench-suite/benchmarks/basicValidation.js new file mode 100644 index 00000000000..f29249be31e --- /dev/null +++ b/packages/bench-suite/benchmarks/basicValidation.js @@ -0,0 +1,320 @@ +const { PrivateKey } = require('@dashevo/dashcore-lib'); + +const DashPlatformProtocol = require('@dashevo/dpp'); + +const dpnsDocumentTypes = require('@dashevo/dpns-contract/schema/dpns-contract-documents'); + +const Identity = require('@dashevo/dpp/lib/identity/Identity'); +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); + +const crypto = require('crypto'); + +const TYPES = require('../lib/benchmarks/types'); +const createProperties = require('../lib/util/createProperties'); + +class StateRepository { + /** + * @type {DataContract} + */ + #dataContract; + + /** + * @returns {Promise} + */ + async fetchDataContract() { + return this.#dataContract; + } + + /** + * @param {DataContract} dataContract + */ + setDataContract(dataContract) { + this.#dataContract = dataContract; + } +} + +module.exports = { + title: 'DPP validate basic', + type: TYPES.FUNCTION, + + /** + * How many times repeat tests + * + * @type {number} + */ + repeats: 100, + + /** + * Run before all tests + * + * @param {Context} context + * @returns {Promise} + */ + async beforeAll(context) { + context.stateRepository = new StateRepository(); + + context.dpp = new DashPlatformProtocol({ + stateRepository: context.stateRepository, + }); + + await context.dpp.initialize(); + + context.privateKey = new PrivateKey(); + + context.identity = new Identity({ + protocolVersion: 1, + id: Identifier.from('8vAaZuDCm2p1dGEnVQXHiiBTx7uvQqvjmzsGayqKYeDY'), + publicKeys: [ + { + id: 0, + type: 0, + purpose: 0, + securityLevel: 0, + data: context.privateKey.publicKey.toBuffer(), + readOnly: false, + }, + ], + balance: 0, + revision: 0, + }); + }, + + /** + * Multiple tests can be defined + * + * Each test will be run `repeats` times + */ + tests: { + 'Validate DPNS domain document': { + /** + * Run once before running tests + * + * @param {Context} context + */ + beforeAll(context) { + context.dataContract = context.dpp.dataContract.create( + context.identity.getId(), + dpnsDocumentTypes, + ); + + context.stateRepository.setDataContract(context.dataContract); + }, + + /** + * Run before each test run + * + * @param context + * @returns {Promise} + */ + async beforeEach(context) { + const label = crypto.randomBytes(10).toString('hex'); + + const document = context.dpp.document.create( + context.dataContract, + context.identity.getId(), + 'domain', + { + label, + normalizedLabel: label.toLowerCase(), + normalizedParentDomainName: 'dash', + preorderSalt: crypto.randomBytes(32), + records: { + dashUniqueIdentityId: generateRandomIdentifier(), + }, + subdomainRules: { + allowSubdomains: false, + }, + }, + ); + + const stateTransition = context.dpp.document.createStateTransition({ + create: [document], + }); + + await stateTransition.sign( + context.identity.getPublicKeys()[0], + context.privateKey, + ); + + context.stateTransition = stateTransition.toBuffer(); + }, + + /** + * Run test `repeats` times + * + * @param {Context} context + * @returns {Promise} + */ + async test(context) { + await context.dpp.stateTransition.createFromBuffer( + context.stateTransition, + ); + }, + }, + 'Validate 100 strings': { + /** + * Run once before running tests + * + * @param {Context} context + */ + beforeAll(context) { + context.dataContract = context.dpp.dataContract.create( + context.identity.getId(), + { + plain: { + type: 'object', + properties: createProperties(100, { + type: 'string', + }), + additionalProperties: false, + }, + }, + ); + + context.stateRepository.setDataContract(context.dataContract); + }, + + /** + * Run before each test run + * + * @param context + * @returns {Promise} + */ + async beforeEach(context) { + const properties = {}; + + for (let i = 0; i < 100; i++) { + const name = `property${i}`; + + properties[name] = crypto.randomBytes(20) + .toString('hex'); + } + + const document = context.dpp.document.create( + context.dataContract, + context.identity.getId(), + 'plain', + properties, + ); + + const stateTransition = context.dpp.document.createStateTransition({ + create: [document], + }); + + await stateTransition.sign( + context.identity.getPublicKeys()[0], + context.privateKey, + ); + + context.stateTransition = stateTransition.toBuffer(); + }, + + /** + * Run test `repeats` times + * + * @param {Context} context + * @returns {Promise} + */ + async test(context) { + await context.dpp.stateTransition.createFromBuffer( + context.stateTransition, + ); + }, + }, + 'Validate 100 regexps': { + /** + * Run once before running tests + * + * @param {Context} context + */ + beforeAll(context) { + context.dataContract = context.dpp.dataContract.create( + context.identity.getId(), + { + regexp: { + type: 'object', + properties: createProperties(100, { + type: 'string', + pattern: '^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$', + maxLength: 63, + }), + additionalProperties: false, + }, + }, + ); + + context.stateRepository.setDataContract(context.dataContract); + }, + + /** + * Run before each test run + * + * @param context + * @returns {Promise} + */ + async beforeEach(context) { + const properties = {}; + + for (let i = 0; i < 100; i++) { + const name = `property${i}`; + + properties[name] = crypto.randomBytes(20).toString('hex'); + } + + const document = context.dpp.document.create( + context.dataContract, + context.identity.getId(), + 'regexp', + properties, + ); + + const stateTransition = context.dpp.document.createStateTransition({ + create: [document], + }); + + await stateTransition.sign( + context.identity.getPublicKeys()[0], + context.privateKey, + ); + + context.stateTransition = stateTransition.toBuffer(); + }, + + /** + * Run test `repeats` times + * + * @param {Context} context + * @returns {Promise} + */ + async test(context) { + await context.dpp.stateTransition.createFromBuffer( + context.stateTransition, + ); + }, + }, + }, + + /** + * Test timeout + * + * @type {number} + */ + timeout: 3000, + + /** + * Statistical function + * + * Available functions: https://mathjs.org/docs/reference/functions.html#statistics-functions + * + * @type {string} + */ + avgFunction: 'median', + + /** + * Show all or only statistic result + * + * @type {boolean} + */ + avgOnly: false, +}; diff --git a/packages/bench-suite/benchmarks/documents/100string.js b/packages/bench-suite/benchmarks/documents/100string.js new file mode 100644 index 00000000000..5d7987f6ccd --- /dev/null +++ b/packages/bench-suite/benchmarks/documents/100string.js @@ -0,0 +1,97 @@ +const crypto = require('crypto'); + +const TYPES = require('../../lib/benchmarks/types'); + +const createProperties = require('../../lib/util/createProperties'); + +module.exports = { + title: '100 Strings', + + type: TYPES.DOCUMENTS, + + /** + * Define document types + * + * It can be function or object + * + * @type {Object|Function} + */ + documentTypes: { + plain: { + type: 'object', + properties: createProperties(100, { + type: 'string', + }), + additionalProperties: false, + }, + regexps100: { + type: 'object', + properties: createProperties(100, { + type: 'string', + pattern: '^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$', + maxLength: 63, + }), + additionalProperties: false, + }, + }, + + /** + * Number of documents to create for each type + * + * We get 35x3 results running against local network + * since metrics are gathering from all 3 nodes + * + * @type {number} + */ + documentsCount: 10, + + /** + * Return document data for specific document type to create + * + * Functions will be called "documentsCount" times + */ + documentsData: { + /** + * Calls if specific document type function is not created + * + * @param {number} i - Call index + * @param {string} type - Document type + * @returns {Object} + */ + $all() { + const document = {}; + + for (let i = 0; i < 100; i++) { + const name = `property${i}`; + + document[name] = crypto.randomBytes(20) + .toString('hex'); + } + + return document; + }, + }, + + /** + * How many credits this benchmark requires to run + * + * @type {number} + */ + requiredCredits: 1000000, + + /** + * Statistical function + * + * Available functions: https://mathjs.org/docs/reference/functions.html#statistics-functions + * + * @type {string} + */ + avgFunction: 'median', + + /** + * Show all or only statistic result + * + * @type {boolean} + */ + avgOnly: false, +}; diff --git a/packages/bench-suite/benchmarks/documents/dpns.js b/packages/bench-suite/benchmarks/documents/dpns.js new file mode 100644 index 00000000000..58fd6df477f --- /dev/null +++ b/packages/bench-suite/benchmarks/documents/dpns.js @@ -0,0 +1,88 @@ +const crypto = require('crypto'); + +const dpnsDocumentTypes = require('@dashevo/dpns-contract/schema/dpns-contract-documents'); + +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); + +const TYPES = require('../../lib/benchmarks/types'); + +module.exports = { + title: 'DPNS data contract', + + type: TYPES.DOCUMENTS, + + /** + * Define document types + * + * It can be function or object + * + * @type {Object|Function} + */ + documentTypes: { + domain: dpnsDocumentTypes.domain, + }, + + /** + * Number of documents to create for each type + * + * We get 35x3 results running against local network + * since metrics are gathering from all 3 nodes + * + * @type {number} + */ + documentsCount: 10, + + /** + * Return document data for specific document type to create + * + * Functions will be called "documentsCount" times + */ + documentsData: { + /** + * Calls for document type "domain" + * + * @param {number} i - Call index + * @param {string} type - Document type + * @returns {Object} + */ + domain() { + const label = crypto.randomBytes(10).toString('hex'); + + return { + label, + normalizedLabel: label.toLowerCase(), + normalizedParentDomainName: 'dash', + preorderSalt: crypto.randomBytes(32), + records: { + dashUniqueIdentityId: generateRandomIdentifier(), + }, + subdomainRules: { + allowSubdomains: false, + }, + }; + }, + }, + + /** + * How many credits this benchmark requires to run + * + * @type {number} + */ + requiredCredits: 1000000, + + /** + * Statistical function + * + * Available functions: https://mathjs.org/docs/reference/functions.html#statistics-functions + * + * @type {string} + */ + avgFunction: 'median', + + /** + * Show all or only statistic result + * + * @type {boolean} + */ + avgOnly: false, +}; diff --git a/packages/bench-suite/benchmarks/documents/indices.js b/packages/bench-suite/benchmarks/documents/indices.js new file mode 100644 index 00000000000..0e74efbc1be --- /dev/null +++ b/packages/bench-suite/benchmarks/documents/indices.js @@ -0,0 +1,101 @@ +const crypto = require('crypto'); + +const TYPES = require('../../lib/benchmarks/types'); + +const createIndices = require('../../lib/util/createIndices'); +const createProperties = require('../../lib/util/createProperties'); + +module.exports = { + title: '100 Indices', + + type: TYPES.DOCUMENTS, + + /** + * Define document types + * + * It can be function or object + * + * @type {Object|Function} + */ + documentTypes: { + indices: { + type: 'object', + indices: createIndices(100), + properties: createProperties(100, { + type: 'string', + maxLength: 63, + }), + additionalProperties: false, + }, + uniqueIndices: { + type: 'object', + indices: createIndices(100, true), + properties: createProperties(100, { + type: 'string', + maxLength: 63, + }), + additionalProperties: false, + }, + }, + + /** + * Number of documents to create for each type + * + * We get 35x3 results running against local network + * since metrics are gathering from all 3 nodes + * + * @type {number} + */ + documentsCount: 10, + + /** + * Return document data for specific document type to create + * + * Functions will be called "documentsCount" times + */ + documentsData: { + /** + * Calls if specific document type function is not created + * + * @param {number} i - Call index + * @param {string} type - Document type + * @returns {Object} + */ + $all() { + // Broadcast the same documents for each document type + const document = {}; + + for (let i = 0; i < 100; i++) { + const name = `property${i}`; + + document[name] = crypto.randomBytes(20) + .toString('hex'); + } + + return document; + }, + }, + + /** + * How many credits this benchmark requires to run + * + * @type {number} + */ + requiredCredits: 2000000000, + + /** + * Statistical function + * + * Available functions: https://mathjs.org/docs/reference/functions.html#statistics-functions + * + * @type {string} + */ + avgFunction: 'median', + + /** + * Show all or only statistic result + * + * @type {boolean} + */ + avgOnly: false, +}; diff --git a/packages/bench-suite/benchmarks/fees/dataContractCreate.js b/packages/bench-suite/benchmarks/fees/dataContractCreate.js new file mode 100644 index 00000000000..d731346fa01 --- /dev/null +++ b/packages/bench-suite/benchmarks/fees/dataContractCreate.js @@ -0,0 +1,91 @@ +const { signStateTransition } = require('dash/build/src/SDK/Client/Platform/signStateTransition'); + +const TYPES = require('../../lib/benchmarks/types'); +const createIndices = require('../../lib/util/createIndices'); +const createProperties = require('../../lib/util/createProperties'); + +module.exports = { + title: 'Fees', + + type: TYPES.STATE_TRANSITIONS, + + /** + * Number of state transitions to broadcast + * + * @type {number} + */ + stateTransitionsCount: 10, + + /** + * Define document types + * + * It can be function or object + * + * @type {Object|Function} + */ + stateTransitions: { + /** + * @param {Context} context - Test context + * @param {number} i - Call index + * @returns {AbstractStateTransition} + */ + 'Publish Data Contract': async (context) => { + const { platform } = context.dash; + + const dataContract = await platform.contracts.create( + { + indices: { + type: 'object', + indices: createIndices(100), + properties: createProperties(100, { + type: 'string', + maxLength: 63, + }), + additionalProperties: false, + }, + uniqueIndices: { + type: 'object', + indices: createIndices(100, true), + properties: createProperties(100, { + type: 'string', + maxLength: 63, + }), + additionalProperties: false, + }, + }, + context.identity, + ); + + const stateTransition = platform.dpp.dataContract.createDataContractCreateTransition( + dataContract, + ); + + await signStateTransition(platform, stateTransition, context.identity); + + return stateTransition; + }, + }, + + /** + * How many credits this benchmark requires to run + * + * @type {number} + */ + requiredCredits: 2000000000, + + /** + * Statistical function + * + * Available functions: https://mathjs.org/docs/reference/functions.html#statistics-functions + * + * @type {string} + */ + avgFunction: 'median', + + /** + * Show all or only statistic result + * + * @type {boolean} + */ + avgOnly: false, +}; diff --git a/packages/bench-suite/benchmarks/index.js b/packages/bench-suite/benchmarks/index.js new file mode 100644 index 00000000000..0bd564ce90a --- /dev/null +++ b/packages/bench-suite/benchmarks/index.js @@ -0,0 +1,8 @@ +/* eslint-disable global-require */ + +module.exports = [ + require('./documents/100string'), + require('./documents/indices'), + require('./documents/dpns'), + require('./fees/dataContractCreate'), +]; diff --git a/packages/bench-suite/bin/bench.js b/packages/bench-suite/bin/bench.js new file mode 100644 index 00000000000..50011181231 --- /dev/null +++ b/packages/bench-suite/bin/bench.js @@ -0,0 +1,28 @@ +const path = require('path'); + +const dotenvSafe = require('dotenv-safe'); + +const parseDAPISeedsString = require('../lib/client/parseDAPISeedsString'); + +dotenvSafe.config({ + path: path.resolve(__dirname, '..', '.env'), +}); + +const Runner = require('../lib/Runner'); + +const runner = new Runner({ + driveLogPath: process.env.DRIVE_LOG_PATH, + verbose: process.env.VERBOSE === '1' || process.env.VERBOSE === 'true', + client: { + seeds: parseDAPISeedsString(process.env.DAPI_SEED), + skipSyncBeforeHeight: Number(process.env.SKIP_SYNC_BEFORE_HEIGHT), + network: process.env.NETWORK, + faucetPrivateKey: process.env.FAUCET_PRIVATE_KEY, + }, +}); + +const benchmarksPath = path.join(__dirname, '..', 'benchmarks', 'index.js'); + +runner.loadBenchmarks(benchmarksPath); + +runner.run(); diff --git a/packages/bench-suite/lib/Runner.js b/packages/bench-suite/lib/Runner.js new file mode 100644 index 00000000000..0ec30f765e0 --- /dev/null +++ b/packages/bench-suite/lib/Runner.js @@ -0,0 +1,104 @@ +const Mocha = require('mocha'); + +const setupContext = require('./setupContext'); + +const DriveMetricsCollector = require('./metrics/drive/DriveMetricsCollector'); + +const BENCHMARKS = require('./benchmarks'); + +class Runner { + /** + * @type {Mocha} + */ + #mocha; + + /** + * @type {Object} + */ + #options; + + /** + * @type {DriveMetricsCollector} + */ + #driveMetricsCollector; + + /** + * @type {AbstractBenchmark[]} + */ + #benchmarks = []; + + /** + * @param {Object} options + * @param {string} options.driveLogPath + * @param {boolean} [options.verbose=false] + */ + constructor(options = {}) { + this.#options = options; + + this.#mocha = new Mocha({ + reporter: options.verbose ? 'spec' : 'nyan', + timeout: 650000, + bail: true, + fullTrace: options.verbose, + }); + + this.#driveMetricsCollector = new DriveMetricsCollector(options.driveLogPath); + } + + /** + * @param {string} filePath + */ + loadBenchmarks(filePath) { + // eslint-disable-next-line global-require,import/no-dynamic-require + const benchmarks = require(filePath); + + for (const benchmarkConfig of benchmarks) { + const BenchmarkClass = BENCHMARKS[benchmarkConfig.type]; + + if (!BenchmarkClass) { + throw new Error(`Invalid benchmark type ${benchmarkConfig.type}`); + } + + const benchmark = new BenchmarkClass(benchmarkConfig, this.#options); + + this.#mocha.suite.addSuite( + benchmark.createMochaTestSuite(this.#mocha.suite.ctx), + ); + + this.#benchmarks.push(benchmark); + } + } + + /** + * Run benchmarks + */ + run() { + setupContext(this.#mocha, this.#benchmarks, this.#options); + + this.#mocha.run(async (failures) => { + if (failures) { + process.exitCode = 1; + + return; + } + + // Collect metrics from Drive logs + this.#benchmarks.forEach((benchmark) => { + if (benchmark.getMetricMatches) { + this.#driveMetricsCollector.addMatches(benchmark.getMetricMatches()); + } + }); + + await this.#driveMetricsCollector.collect(); + + // Print results + this.#benchmarks.forEach((benchmark) => { + benchmark.printResults(); + }); + + process.exit(0); + }); + } +} + +module.exports = Runner; diff --git a/packages/bench-suite/lib/benchmarks/AbstractBenchmark.js b/packages/bench-suite/lib/benchmarks/AbstractBenchmark.js new file mode 100644 index 00000000000..1b8bd3e53b3 --- /dev/null +++ b/packages/bench-suite/lib/benchmarks/AbstractBenchmark.js @@ -0,0 +1,60 @@ +class AbstractBenchmark { + /** + * @type {Object} + */ + config; + + /** + * @type {Match[]} + */ + matches = []; + + /** + * @type {Object} + */ + runnerOptions; + + /** + * @param {Object} config + * @param {Object} runnerOptions + */ + constructor(config, runnerOptions) { + this.config = config; + this.runnerOptions = runnerOptions; + } + + /** + * @returns {number} + */ + getRequiredCredits() { + return 0; + } + + /** + * @returns {Match[]} + */ + getMetricMatches() { + return this.matches; + } + + /** + * @abstract + * @param {Context} context + * @param {Client} context.dash + * @param {Identity} context.identity + * @returns {Mocha.Suite} + */ + // eslint-disable-next-line no-unused-vars + createMochaTestSuite(context) { + + } + + /** + * @abstract + */ + printResults() { + + } +} + +module.exports = AbstractBenchmark; diff --git a/packages/bench-suite/lib/benchmarks/DocumentsBenchmark.js b/packages/bench-suite/lib/benchmarks/DocumentsBenchmark.js new file mode 100644 index 00000000000..8f0e239df3f --- /dev/null +++ b/packages/bench-suite/lib/benchmarks/DocumentsBenchmark.js @@ -0,0 +1,116 @@ +const { Suite, Test } = require('mocha'); + +const AbstractBenchmark = require('./AbstractBenchmark'); +const printMetrics = require('../metrics/drive/printMetrics'); +const createStateTransitionMatch = require('../metrics/drive/createStateTransitionMatch'); + +class DocumentsBenchmark extends AbstractBenchmark { + /** + * @type {Object} + */ + #metrics = {}; + + /** + * @param {Context} context + * @param {Client} context.dash + * @param {Identity} context.identity + * @returns {Mocha.Suite} + */ + createMochaTestSuite(context) { + const suite = new Suite(this.config.title, context); + + suite.timeout(650000); + + const documentTypes = typeof this.config.documentTypes === 'function' + ? this.config.documentTypes() + : this.config.documentTypes; + + suite.beforeAll('Publish Data Contract', async () => { + const dataContract = await context.dash.platform.contracts.create( + documentTypes, + context.identity, + ); + + if (this.runnerOptions.verbose) { + // eslint-disable-next-line no-console + console.dir(context.identity.toJSON(), { depth: Infinity }); + + // eslint-disable-next-line no-console + console.dir(dataContract.toJSON(), { depth: Infinity }); + } + + await context.dash.platform.contracts.publish( + dataContract, + context.identity, + ); + + context.dash.getApps().set(this.config.title, { + contractId: dataContract.getId(), + contract: dataContract, + }); + }); + + for (const documentType of Object.keys(documentTypes)) { + const documentTypeSuite = new Suite(documentType, suite.ctx); + + let documentDataFunction = this.config.documentsData[documentType]; + if (!documentDataFunction) { + documentDataFunction = this.config.documentsData.$all; + } + + for (let i = 0; i < this.config.documentsCount; i++) { + suite.addTest(new Test(`Create document ${documentType}`, async () => { + const documentProperties = await documentDataFunction(i, documentType); + + const document = await context.dash.platform.documents.create( + `${this.config.title}.${documentType}`, + context.identity, + documentProperties, + ); + + if (this.runnerOptions.verbose) { + // eslint-disable-next-line no-console + console.dir(document.toJSON(), { depth: Infinity }); + } + + const stateTransition = await context.dash.platform.documents.broadcast({ + create: [document], + }, context.identity); + + const match = createStateTransitionMatch( + stateTransition, + documentType, + this.#metrics, + ); + + this.matches.push(match); + })); + } + + suite.addSuite(documentTypeSuite); + } + + return suite; + } + + /** + * Print metrics + */ + printResults() { + // eslint-disable-next-line no-console + console.log(`\n\n${this.config.title}\n${'-'.repeat(this.config.title.length)}`); + + Object.entries(this.#metrics).forEach(([documentType, metrics]) => { + printMetrics(documentType, metrics, this.config); + }); + } + + /** + * @returns {number} + */ + getRequiredCredits() { + return this.config.requiredCredits; + } +} + +module.exports = DocumentsBenchmark; diff --git a/packages/bench-suite/lib/benchmarks/FunctionBenchmark.js b/packages/bench-suite/lib/benchmarks/FunctionBenchmark.js new file mode 100644 index 00000000000..a5b0fcd400c --- /dev/null +++ b/packages/bench-suite/lib/benchmarks/FunctionBenchmark.js @@ -0,0 +1,168 @@ +const { + Suite, + Test, +} = require('mocha'); + +const { Table } = require('console-table-printer'); + +const { performance, PerformanceObserver } = require('perf_hooks'); + +const mathjs = require('mathjs'); + +const AbstractBenchmark = require('./AbstractBenchmark'); + +class FunctionBenchmark extends AbstractBenchmark { + /** + * @type {Object} + */ + #perfMeasures = {}; + + /** + * @param {Context} context + * @param {Client} context.dash + * @param {Identity} context.identity + * @returns {Mocha.Suite} + */ + createMochaTestSuite(context) { + const benchmarkSuite = new Suite(this.config.title, context); + + benchmarkSuite.timeout(this.config.timeout); + + if (this.config.beforeAll) { + benchmarkSuite.beforeAll(this.config.beforeAll.bind(benchmarkSuite.ctx, benchmarkSuite.ctx)); + } + + if (this.config.afterAll) { + benchmarkSuite.beforeAll(this.config.beforeAll.bind(benchmarkSuite.ctx, benchmarkSuite.ctx)); + } + + for (const [title, functions] of Object.entries(this.config.tests)) { + const testSuite = new Suite(title, benchmarkSuite.ctx); + + this.#perfMeasures[title] = []; + + const perfObserver = new PerformanceObserver((list) => { + this.#perfMeasures[title].push(list.getEntries()); + }); + + testSuite.beforeAll('Start performance observer', () => { + perfObserver.observe({ entryTypes: ['measure', 'function'] }); + }); + + if (functions.beforeAll) { + testSuite.beforeAll(functions.beforeAll.bind(testSuite.ctx, testSuite.ctx)); + } + + if (functions.beforeEach) { + testSuite.beforeEach(functions.beforeEach.bind(testSuite.ctx, testSuite.ctx)); + } + + if (functions.afterAll) { + testSuite.afterAll(functions.afterAll.bind(testSuite.ctx, testSuite.ctx)); + } + + if (functions.afterEach) { + testSuite.afterEach(functions.afterEach.bind(testSuite.ctx, testSuite.ctx)); + } + + testSuite.afterAll('Stop performance observer', () => { + performance.clearMeasures(); + performance.clearMarks(); + perfObserver.disconnect(); + }); + + const measureName = 'overall'; + + for (let i = 0; i < this.config.repeats; i++) { + testSuite.addTest(new Test(`Test ${i}`, async () => { + const startMarkTitle = `${measureName}-start`; + const endMarkTitle = `${measureName}-end`; + + performance.mark(startMarkTitle); + + await functions.test(testSuite.ctx, i); + + performance.mark(endMarkTitle); + + performance.measure(measureName, startMarkTitle, endMarkTitle); + })); + } + + benchmarkSuite.addSuite(testSuite); + } + + return benchmarkSuite; + } + + printResults() { + // eslint-disable-next-line no-console + console.log(`\n\n${this.config.title}\n${'-'.repeat(this.config.title.length)}`); + + for (const [title] of Object.entries(this.config.tests)) { + this.#printTestMeasures(title, this.#perfMeasures[title]); + } + } + + /** + * @private + * @param {string} title + * @param {PerformanceMeasure[][]} measures + */ + #printTestMeasures(title, measures) { + const avgs = {}; + + const rows = measures.map((testMeasures) => ( + testMeasures.reduce((row, measure) => { + const duration = Number(measure.duration.toFixed(3)); + + // eslint-disable-next-line no-param-reassign + row[measure.name] = duration; + + if (!avgs[measure.name]) { + avgs[measure.name] = []; + } + + avgs[measure.name].push(duration); + + return row; + }, {}) + )); + + const table = new Table(); + + const keys = Object.keys(rows[0]); + + if (this.config.avgOnly) { + const avgRow = {}; + + // eslint-disable-next-line array-callback-return + keys.map((key) => { + avgRow[key] = '...'; + }); + + table.addRow(avgRow); + } else { + table.addRows(rows); + } + + const avgFunction = mathjs[this.config.avgFunction]; + + const avgRow = {}; + + keys.forEach((key) => { + avgRow[key] = avgFunction(avgs[key]).toFixed(3); + }); + + table.addRow(avgRow, { + color: 'white_bold', + separator: true, + }); + + // eslint-disable-next-line no-console + console.log(`\n\n${title} tests ran ${this.config.repeats} times:`); + + table.printTable(); + } +} + +module.exports = FunctionBenchmark; diff --git a/packages/bench-suite/lib/benchmarks/StateTransitionsBenchmark.js b/packages/bench-suite/lib/benchmarks/StateTransitionsBenchmark.js new file mode 100644 index 00000000000..4bc0787aeed --- /dev/null +++ b/packages/bench-suite/lib/benchmarks/StateTransitionsBenchmark.js @@ -0,0 +1,81 @@ +const { + Suite, + Test, +} = require('mocha'); + +const AbstractBenchmark = require('./AbstractBenchmark'); +const printMetrics = require('../metrics/drive/printMetrics'); +const createStateTransitionMatch = require('../metrics/drive/createStateTransitionMatch'); + +class StateTransitionsBenchmark extends AbstractBenchmark { + /** + * @type {Object} + */ + #metrics = {}; + + /** + * @param {Context} context + * @param {Client} context.dash + * @param {Identity} context.identity + * @returns {Mocha.Suite} + */ + createMochaTestSuite(context) { + const suite = new Suite(this.config.title, context); + + suite.timeout(650000); + + for (const stateTransitionTitle of Object.keys(this.config.stateTransitions)) { + const stateTransitionSuite = new Suite(stateTransitionTitle, suite.ctx); + + for (let i = 0; i < this.config.stateTransitionsCount; i++) { + suite.addTest(new Test(`Broadcast state transition "${i + 1}"`, async () => { + const stateTransition = await this.config.stateTransitions[stateTransitionTitle]( + context, + i, + ); + + if (this.runnerOptions.verbose) { + // eslint-disable-next-line no-console + console.dir(stateTransition.toJSON(), { depth: Infinity }); + } + + await context.dash.platform.broadcastStateTransition(stateTransition); + + const match = createStateTransitionMatch( + stateTransition, + stateTransitionTitle, + this.#metrics, + ); + + this.matches.push(match); + })); + } + + suite.addSuite(stateTransitionSuite); + } + + return suite; + } + + /** + * Print metrics + */ + printResults() { + // eslint-disable-next-line no-console + console.log(`\n\n${this.config.title}\n${'-'.repeat(this.config.title.length)}`); + + Object.entries(this.#metrics) + .forEach(([documentType, metrics]) => { + printMetrics(documentType, metrics, this.config); + }); + } + + /** + * @returns {number} + */ + getRequiredCredits() { + return this.config.requiredCredits; + } +} + +module.exports = StateTransitionsBenchmark; diff --git a/packages/bench-suite/lib/benchmarks/index.js b/packages/bench-suite/lib/benchmarks/index.js new file mode 100644 index 00000000000..b6e9cb59d3d --- /dev/null +++ b/packages/bench-suite/lib/benchmarks/index.js @@ -0,0 +1,11 @@ +const TYPES = require('./types'); + +const DocumentsBenchmark = require('./DocumentsBenchmark'); +const FunctionBenchmark = require('./FunctionBenchmark'); +const StateTransitionsBenchmark = require('./StateTransitionsBenchmark'); + +module.exports = { + [TYPES.DOCUMENTS]: DocumentsBenchmark, + [TYPES.STATE_TRANSITIONS]: StateTransitionsBenchmark, + [TYPES.FUNCTION]: FunctionBenchmark, +}; diff --git a/packages/bench-suite/lib/benchmarks/types.js b/packages/bench-suite/lib/benchmarks/types.js new file mode 100644 index 00000000000..5a5c415cb27 --- /dev/null +++ b/packages/bench-suite/lib/benchmarks/types.js @@ -0,0 +1,5 @@ +module.exports = { + DOCUMENTS: 'documents', + STATE_TRANSITIONS: 'stateTransitions', + FUNCTION: 'function', +}; diff --git a/packages/bench-suite/lib/client/createClientWithFundedWallet.js b/packages/bench-suite/lib/client/createClientWithFundedWallet.js new file mode 100644 index 00000000000..81ccfef91d5 --- /dev/null +++ b/packages/bench-suite/lib/client/createClientWithFundedWallet.js @@ -0,0 +1,58 @@ +const Dash = require('dash'); + +const clone = require('lodash.clone'); + +const fundWallet = require('@dashevo/wallet-lib/src/utils/fundWallet'); + +/** + * Create and fund DashJS client + * + * @param {number} amount + * @param {Object} config + * @param {{host: string, httpPort: string, grpcPort: string}[]} config.seeds + * @param {string} config.network + * @param {string} config.faucetPrivateKey + * @param {number} [config.skipSyncBeforeHeight] + * + * @returns {Promise} + */ +async function createClientWithFundedWallet(amount, config) { + let walletOptions = { + waitForInstantLockTimeout: 120000, + }; + + if (config.skipSyncBeforeHeight) { + walletOptions.unsafeOptions = { + skipSynchronizationBeforeHeight: config.skipSyncBeforeHeight, + }; + } + + const clientOpts = { + seeds: config.seeds, + network: config.network, + wallet: walletOptions, + }; + + const faucetClient = new Dash.Client({ + ...clientOpts, + wallet: { + ...walletOptions, + privateKey: config.faucetPrivateKey, + }, + }); + + walletOptions = clone(walletOptions); + + const client = new Dash.Client({ + ...clientOpts, + wallet: walletOptions, + }); + + await fundWallet(faucetClient.wallet, client.wallet, amount); + + await faucetClient.disconnect(); + + return client; +} + +module.exports = createClientWithFundedWallet; diff --git a/packages/bench-suite/lib/client/parseDAPISeedsString.js b/packages/bench-suite/lib/client/parseDAPISeedsString.js new file mode 100644 index 00000000000..ca900a80a70 --- /dev/null +++ b/packages/bench-suite/lib/client/parseDAPISeedsString.js @@ -0,0 +1,19 @@ +/** + * @param {string} seedsString + * @returns {{host: string, httpPort: string, grpcPort: string}[]} + */ +function parseDAPISeedsString(seedsString) { + return seedsString + .split(',') + .map((seed) => { + const [host, httpPort, grpcPort] = seed.split(':'); + + return { + host, + httpPort, + grpcPort, + }; + }); +} + +module.exports = parseDAPISeedsString; diff --git a/packages/bench-suite/lib/metrics/Match.js b/packages/bench-suite/lib/metrics/Match.js new file mode 100644 index 00000000000..a7d75acdd18 --- /dev/null +++ b/packages/bench-suite/lib/metrics/Match.js @@ -0,0 +1,35 @@ +const lodashMatches = require('lodash.matches'); + +class Match { + /** + * @type {Function} + */ + #isMatch; + + /** + * @type {Function} + */ + #callback; + + /** + * @param {Object} pattern + * @param {Function} callback + */ + constructor(pattern, callback) { + this.#isMatch = lodashMatches(pattern); + this.#callback = callback; + } + + /** + * Call match callback if data is matched + * + * @param {Object} data + */ + applyMatch(data) { + if (this.#isMatch(data)) { + this.#callback(data); + } + } +} + +module.exports = Match; diff --git a/packages/bench-suite/lib/metrics/drive/DriveMetricsCollector.js b/packages/bench-suite/lib/metrics/drive/DriveMetricsCollector.js new file mode 100644 index 00000000000..08af3ea2f7f --- /dev/null +++ b/packages/bench-suite/lib/metrics/drive/DriveMetricsCollector.js @@ -0,0 +1,70 @@ +const readline = require('readline'); +const events = require('events'); +const fs = require('fs'); + +class DriveMetricsCollector extends events.EventEmitter { + /** + * @type {string} + */ + #driveLogPath; + + /** + * @type {Match[]} + */ + #matches = []; + + /** + * @param {string} driveLogPath + */ + constructor(driveLogPath) { + super(); + + this.#driveLogPath = driveLogPath; + } + + /** + * Add matches + * + * @param {Match[]} matches + */ + addMatches(matches) { + this.#matches.push(...matches); + } + + /** + * @returns {Promise} + */ + async collect() { + const rl = readline.createInterface({ + input: fs.createReadStream(this.#driveLogPath), + crlfDelay: Infinity, + }); + + rl.on('line', (line) => { + if (line === '') { + return; + } + + let logData; + try { + logData = JSON.parse(line); + } catch (e) { + return; + } + + this.#applyMatches(logData); + }); + + await events.once(rl, 'close'); + } + + /** + * + * @param {Object} data + */ + #applyMatches(data) { + this.#matches.forEach((match) => match.applyMatch(data)); + } +} + +module.exports = DriveMetricsCollector; diff --git a/packages/bench-suite/lib/metrics/drive/createStateTransitionMatch.js b/packages/bench-suite/lib/metrics/drive/createStateTransitionMatch.js new file mode 100644 index 00000000000..ea98aef70c6 --- /dev/null +++ b/packages/bench-suite/lib/metrics/drive/createStateTransitionMatch.js @@ -0,0 +1,32 @@ +const crypto = require('crypto'); +const Match = require('../Match'); + +/** + * @param {AbstractStateTransition} stateTransition + * @param {string} metricTitle + * @param {Object} metrics + * @return {Match} + */ +function createStateTransitionMatch(stateTransition, metricTitle, metrics) { + const stHash = crypto + .createHash('sha256') + .update(stateTransition.toBuffer()) + .digest() + .toString('hex') + .toUpperCase(); + + return new Match({ + txId: stHash, + txType: stateTransition.getType(), + abciMethod: 'deliverTx', + }, (data) => { + if (!metrics[metricTitle]) { + // eslint-disable-next-line no-param-reassign + metrics[metricTitle] = []; + } + + metrics[metricTitle].push(data); + }); +} + +module.exports = createStateTransitionMatch; diff --git a/packages/bench-suite/lib/metrics/drive/printMetrics.js b/packages/bench-suite/lib/metrics/drive/printMetrics.js new file mode 100644 index 00000000000..8b89c92079b --- /dev/null +++ b/packages/bench-suite/lib/metrics/drive/printMetrics.js @@ -0,0 +1,129 @@ +const { Table } = require('console-table-printer'); + +const mathjs = require('mathjs'); + +/** + * @param {string} title + * @param {Object[]} metrics + * @param {Object} config + */ +function printMetrics(title, metrics, config) { + // eslint-disable-next-line no-console + console.log(`${metrics.length} "${title}" metrics collected:`); + + const overall = []; + const validateBasic = []; + const validateFee = []; + const validateSignature = []; + const validateState = []; + const apply = []; + + metrics.forEach((metric) => { + overall.push(metric.timings.overall); + validateBasic.push(metric.timings.validateBasic); + validateFee.push(metric.timings.validateFee); + validateSignature.push(metric.timings.validateSignature); + validateState.push(metric.timings.validateState); + apply.push(metric.timings.apply); + }); + + const timingTable = new Table({ + columns: [ + { name: 'overall' }, + { name: 'validateBasic' }, + { name: 'validateFee' }, + { name: 'validateSignature' }, + { name: 'validateState' }, + { name: 'apply' }, + ], + }); + + if (config.avgOnly) { + timingTable.addRow({ + overall: '...', + validateBasic: '...', + validateFee: '...', + validateSignature: '...', + validateState: '...', + apply: '...', + }); + } else { + timingTable.addRows( + metrics.map((metric) => metric.timings), + ); + } + + const avgFunction = mathjs[config.avgFunction]; + + timingTable.addRow({ + overall: avgFunction(overall) + .toFixed(3), + validateBasic: avgFunction(validateBasic) + .toFixed(3), + validateFee: avgFunction(validateFee) + .toFixed(3), + validateSignature: avgFunction(validateSignature) + .toFixed(3), + validateState: avgFunction(validateState) + .toFixed(3), + apply: avgFunction(apply) + .toFixed(3), + }, { + color: 'white_bold', + separator: true, + }); + + timingTable.printTable(); + + // eslint-disable-next-line no-console + console.log(`\n\n"${title}" fees:`); + + const feeTable = new Table({ + columns: [ + { name: 'predicted storage' }, + { name: 'actual storage' }, + { name: 'predicted processing' }, + { name: 'actual processing' }, + { name: 'predicted final' }, + { name: 'actual final' }, + { name: 'predicted operations' }, + { name: 'actual operations' }, + ], + }); + + const { + predicted, + actual, + } = metrics[0].fees; + + feeTable.addRow({ + 'predicted storage': predicted.storage, + 'actual storage': actual.storage, + 'predicted processing': predicted.processing, + 'actual processing': actual.processing, + 'predicted final': predicted.final, + 'actual final': actual.final, + 'predicted operations': predicted.operations.length, + 'actual operations': actual.operations.length, + }); + + feeTable.printTable(); + + // eslint-disable-next-line no-console + console.log(`\n\n${predicted.operations.length} "${title}" predicted fee operations:\n`); + + predicted.operations.forEach((operation) => { + // eslint-disable-next-line no-console + console.log(operation); + }); + + // eslint-disable-next-line no-console + console.log(`\n\n${actual.operations.length} "${title}" actual fee operations:\n`); + + actual.operations.forEach((operation) => { + // eslint-disable-next-line no-console + console.log(operation); + }); +} + +module.exports = printMetrics; diff --git a/packages/bench-suite/lib/setupContext.js b/packages/bench-suite/lib/setupContext.js new file mode 100644 index 00000000000..b040867078d --- /dev/null +++ b/packages/bench-suite/lib/setupContext.js @@ -0,0 +1,45 @@ +const { convertCreditsToSatoshi } = require('@dashevo/dpp/lib/identity/creditsConverter'); + +const createClientWithFundedWallet = require('./client/createClientWithFundedWallet'); + +/** + * @param {Mocha} mocha + * @param {AbstractBenchmark[]} benchmarks + * @param {Object} options + */ +function setupContext(mocha, benchmarks, options) { + const context = mocha.suite.ctx; + + // Create and fund client if required + const requiredCredits = benchmarks.reduce( + (sum, benchmark) => benchmark.getRequiredCredits() + sum, + 0, + ); + + if (requiredCredits > 0) { + let satoshis = convertCreditsToSatoshi(requiredCredits); + + if (satoshis < 10000) { + satoshis = 10000; + } + + mocha.suite.beforeAll('Create and connect client', async () => { + context.dash = await createClientWithFundedWallet( + satoshis + 5000, + options.client, + ); + }); + + mocha.suite.beforeAll('Create identity', async () => { + context.identity = await context.dash.platform.identities.register(satoshis); + }); + + mocha.suite.afterAll('Disconnect client', async () => { + if (context.dash) { + await context.dash.disconnect(); + } + }); + } +} + +module.exports = setupContext; diff --git a/packages/bench-suite/lib/util/createIndices.js b/packages/bench-suite/lib/util/createIndices.js new file mode 100644 index 00000000000..9cca689b728 --- /dev/null +++ b/packages/bench-suite/lib/util/createIndices.js @@ -0,0 +1,36 @@ +/** + * @param {number} count + * @param {boolean} [unique=false] + */ +function createIndices(count, unique = false) { + const indices = []; + + const indexCount = (count < 10 ? count : 10); + + let propertyIndex = 0; + + const basePropertyCount = Math.floor(count / indexCount); + const propertyLeftovers = count % indexCount; + + for (let i = 0; i < indexCount; i++) { + const properties = []; + + for (let x = 0; x < basePropertyCount + ((i < propertyLeftovers) ? 1 : 0); x++) { + const name = `property${propertyIndex}`; + + propertyIndex++; + + properties.push({ [name]: 'asc' }); + } + + indices.push({ + name: `index${i}`, + properties, + unique: unique && i < 3, + }); + } + + return indices; +} + +module.exports = createIndices; diff --git a/packages/bench-suite/lib/util/createProperties.js b/packages/bench-suite/lib/util/createProperties.js new file mode 100644 index 00000000000..11ba4876908 --- /dev/null +++ b/packages/bench-suite/lib/util/createProperties.js @@ -0,0 +1,17 @@ +/** + * @param {number} count + * @param {Object} subSchema + */ +function createProperties(count, subSchema) { + const properties = {}; + + for (let i = 0; i < count; i++) { + const name = `property${i}`; + + properties[name] = subSchema; + } + + return properties; +} + +module.exports = createProperties; diff --git a/packages/bench-suite/package.json b/packages/bench-suite/package.json new file mode 100644 index 00000000000..701dfc917c9 --- /dev/null +++ b/packages/bench-suite/package.json @@ -0,0 +1,60 @@ +{ + "name": "@dashevo/bench-suite", + "private": true, + "version": "0.23.0-dev.4", + "description": "Dash Platform benchmark tool", + "scripts": { + "bench": "node ./bin/bench.js", + "lint": "eslint ." + }, + "dependencies": { + "@dashevo/dashcore-lib": "~0.19.39", + "@dashevo/dpns-contract": "workspace:~", + "@dashevo/dpp": "workspace:~", + "@dashevo/wallet-lib": "workspace:~", + "console-table-printer": "^2.11.0", + "dash": "workspace:~", + "dotenv-safe": "^8.2.0", + "lodash.clone": "~4.5.0", + "lodash.matches": "^4.6.0", + "mathjs": "^10.4.3", + "mocha": "^9.1.2" + }, + "devDependencies": { + "babel-eslint": "^10.1.0", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/dashevo/dapi.git" + }, + "contributors": [ + { + "name": "Ivan Shumkov", + "email": "ivan@shumkov.ru", + "url": "https://github.com/shumkov" + }, + { + "name": "Djavid Gabibiyan", + "email": "djavid@dash.org", + "url": "https://github.com/jawid-h" + }, + { + "name": "Anton Suprunchuk", + "email": "anton.suprunchuk@dash.org", + "url": "https://github.com/antouhou" + }, + { + "name": "Konstantin Shuplenkov", + "email": "konstantin.shuplenkov@dash.org", + "url": "https://github.com/shuplenkov" + } + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/dashevo/platform/issues" + }, + "homepage": "https://github.com/dashevo/platform#readme" +} diff --git a/packages/dapi-grpc/.eslintignore b/packages/dapi-grpc/.eslintignore new file mode 100644 index 00000000000..77ae260f313 --- /dev/null +++ b/packages/dapi-grpc/.eslintignore @@ -0,0 +1,3 @@ +clients/*/v*/web/ +clients/*/v*/nodejs/*_protoc.js +clients/*/v*/nodejs/*_pbjs.js diff --git a/packages/dapi-grpc/.eslintrc b/packages/dapi-grpc/.eslintrc new file mode 100644 index 00000000000..d8877872a6f --- /dev/null +++ b/packages/dapi-grpc/.eslintrc @@ -0,0 +1,16 @@ +{ + "extends": "airbnb-base", + "rules": { + "import/no-extraneous-dependencies": ["error", { "packageDir": "." }], + "no-plusplus": 0, + "eol-last": [ + "error", + "always" + ], + "class-methods-use-this": "off", + "curly": [ + "error", + "all" + ] + } +} diff --git a/packages/dapi-grpc/.mocharc.yml b/packages/dapi-grpc/.mocharc.yml new file mode 100644 index 00000000000..1f6e57d579e --- /dev/null +++ b/packages/dapi-grpc/.mocharc.yml @@ -0,0 +1,3 @@ +file: + - lib/test/bootstrap.js +recursive: true diff --git a/packages/dapi-grpc/.npmignore b/packages/dapi-grpc/.npmignore new file mode 100644 index 00000000000..2b3ec03d4f9 --- /dev/null +++ b/packages/dapi-grpc/.npmignore @@ -0,0 +1,8 @@ +clients/*/v*/objective-c +clients/*/v*/python +clients/*/v*/java + +node_modules + +# Ultra runner build cache +.ultra.cache.json diff --git a/packages/dapi-grpc/CHANGELOG.md b/packages/dapi-grpc/CHANGELOG.md new file mode 100644 index 00000000000..ee5c7486fa7 --- /dev/null +++ b/packages/dapi-grpc/CHANGELOG.md @@ -0,0 +1,171 @@ +# [0.21.1](https://github.com/dashevo/dapi-grpc/compare/v0.20.0...v0.21.0) (2021-10-12) + + +### Bug Fixes + +* cannot read properties of undefined (reading 'MethodInfo') ([#152](https://github.com/dashevo/dapi-grpc/pull/152)) + + + +# [0.21.0](https://github.com/dashevo/dapi-grpc/compare/v0.20.0...v0.21.0) (2021-10-12) + + +### Features + +* support returning of a multiproof ([#127](https://github.com/dashevo/dapi-grpc/issues/127)) +* implement getConsensusParams method ([#126](https://github.com/dashevo/dapi-grpc/issues/126), [#130](https://github.com/dashevo/dapi-grpc/issues/130), [#132](https://github.com/dashevo/dapi-grpc/issues/132), [#134](https://github.com/dashevo/dapi-grpc/issues/134)) + + +### Bug Fixes + +* height type was uint32 instead of int64 ([#123](https://github.com/dashevo/dapi-grpc/issues/123)) + + +### BREAKING CHANGES + +* `getStoreTreeProof` now returns `StoreTreeProof` message instead of `Buffer` + + + +# [0.20.0](https://github.com/dashevo/dapi-grpc/compare/v0.19.0...v0.20.0) (2021-07-08) + + +### Features + +* add additional information to `GetTransactionResponse` ([#118](https://github.com/dashevo/dapi-grpc/issues/118), [#120](https://github.com/dashevo/dapi-grpc/issues/120)) +* add metadata and additional info to platform proofs ([#115](https://github.com/dashevo/dapi-grpc/issues/115), [#114](https://github.com/dashevo/dapi-grpc/issues/114), [#112](https://github.com/dashevo/dapi-grpc/issues/112)) + + + +# [0.19.0](https://github.com/dashevo/dapi-grpc/compare/v0.18.0...v0.19.0) (2021-04-30) + + +### Features + +* restructure core status response ([#107](https://github.com/dashevo/dapi-grpc/issues/107)) + + +### BREAKING CHANGES + +* structure of `getStatus` method response has changed and not compatible with the previous version. + + + +# [0.18.0](https://github.com/dashevo/dapi-grpc/compare/v0.17.0...v0.18.0) (2021-03-03) + + +### Bug Fixes + +* lock file contained not patched protobufjs ([#104](https://github.com/dashevo/dapi-grpc/issues/104)) + + +### Features + + +* `waitForStateTransitionResult` endpoint ([#99](https://github.com/dashevo/dapi-grpc/issues/99), [#101](https://github.com/dashevo/dapi-grpc/issues/101)) + + + +# [0.17.0](https://github.com/dashevo/dapi-grpc/compare/v0.16.0...v0.17.0) (2020-12-29) + + +### Features + +* add proofs to platform responses ([#96](https://github.com/dashevo/dapi-grpc/issues/96)) + + + +# [0.16.0](https://github.com/dashevo/dapi-grpc/compare/v0.15.0...v0.16.0) (2020-10-26) + + +### Bug Fixes + +* protobuf converts empty Buffer to undefined ([#94](https://github.com/dashevo/dapi-grpc/issues/94)) + + +### Features + +* add `getIdentitiesByPublicKeyHashes` and `getIdentityIdsByPublicKeyHashes` to platform service ([#89](https://github.com/dashevo/dapi-grpc/issues/89), [#92](https://github.com/dashevo/dapi-grpc/issues/92)) +* use bytes for identifiers ([#91](https://github.com/dashevo/dapi-grpc/issues/91)) + + +### BREAKING CHANGES + +* `getIdentityIdByFirstPublicKey` and `getIdentityByFirstPublicKey` removed +* `GetDataContractRequest`, `GetDocumentsRequest`, `GetIdentityRequest` now accepts bytes + + + +# [0.15.0](https://github.com/dashevo/dapi-grpc/compare/v0.14.0...v0.15.0) (2020-09-04) + + +### Features + +* build version specific clients ([#85](https://github.com/dashevo/dapi-grpc/issues/86), [#86](https://github.com/dashevo/dapi-grpc/issues/86)) +* combine `Core` and `TxFilterStream` services ([#84]((https://github.com/dashevo/dapi-grpc/issues/84))) +* update gRPC-Web to 1.2.0 version ([#83](https://github.com/dashevo/dapi-grpc/issues/83)) + + +### BREAKING CHANGES + +* paths to generated clients are changed +* `TxFilterStream` is removed. `subscribeToTransactionsWithProofs` included in Core service. + + + +# [0.14.0](https://github.com/dashevo/dapi-grpc/compare/v0.13.0...v0.14.0) (2020-07-22) + + +### Features + +* allow passing of options to calls in NodeJS clients ([#74](https://github.com/dashevo/dapi-grpc/issues/74)) +* strip URL passed on to client and leave only ip/host:port pair ([#75](https://github.com/dashevo/dapi-grpc/issues/75)) + + +### Refactoring + +* refactor: remove java artifacts ([#78](https://github.com/dashevo/dapi-grpc/issues/78)) + + +### Tests + +* update Mocha config ([#77](https://github.com/dashevo/dapi-grpc/issues/77)) + + + +# [0.13.0](https://github.com/dashevo/dapi-grpc/compare/v0.12.1...v0.13.0) (2020-06-08) + + +### Features + +* get identity by public key endpoints ([#71](https://github.com/dashevo/dapi-grpc/issues/71)) +* add python to the list of clients generated ([#60](https://github.com/dashevo/dapi-grpc/issues/60)) +* use protocol version interceptor in JS clients ([#63](https://github.com/dashevo/dapi-grpc/issues/63), [#68](https://github.com/dashevo/dapi-grpc/issues/68)) + + + +## [0.12.1](https://github.com/dashevo/dapi-grpc/compare/v0.12.0...v0.12.1) (2020-02-13) + + +### Bug Fixes + +* namespacing of the `platform` service in the build ([#57](https://github.com/dashevo/dapi-grpc/issues/57)) ([2b22219](https://github.com/dashevo/dapi-grpc/commit/2b22219d319588413058f11e800a9603c0ee7a0c)) + + + +# [0.12.0](https://github.com/dashevo/dapi-grpc/compare/v0.11.0...v0.12.0) (2020-01-27) + + +### Bug Fixes + +* core services ([1fde938](https://github.com/dashevo/dapi-grpc/commit/1fde938b2c48c9f79555203af1c615ff82b83ac5)) +* platform bugs ([210cdd7](https://github.com/dashevo/dapi-grpc/commit/210cdd7709c009c0303d50c98089f22f8b96ebd8)) + + +### Features + +* add more methods to Core service ([41f3ad0](https://github.com/dashevo/dapi-grpc/commit/41f3ad0ad6aee3acf4b1760949cde36d8df7d6f2)) +* fetchIdentity endpoint ([75d32d8](https://github.com/dashevo/dapi-grpc/commit/75d32d883be4d7a113fe34f1d008e1d9bcc3c7e1)) +* introduce Platform service ([c88b891](https://github.com/dashevo/dapi-grpc/commit/c88b891ecfac8987cd76c773b2f783ad7a155540)) + + diff --git a/packages/dapi-grpc/LICENSE b/packages/dapi-grpc/LICENSE new file mode 100644 index 00000000000..f735c60619c --- /dev/null +++ b/packages/dapi-grpc/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017-2019 Dash Core Group, Inc. + +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/packages/dapi-grpc/README.md b/packages/dapi-grpc/README.md new file mode 100644 index 00000000000..b6e8be0a839 --- /dev/null +++ b/packages/dapi-grpc/README.md @@ -0,0 +1,143 @@ +# DAPI GRPC + +[![Build Status](https://github.com/dashevo/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashevo/platform/actions/workflows/release.yml) +[![NPM version](https://img.shields.io/npm/v/@dashevo/dapi-grpc.svg)](https://npmjs.org/package/@dashevo/dapi-grpc) +[![Release Date](https://img.shields.io/github/release-date/dashevo/platform)](https://github.com/dashevo/platform/releases/latest) +[![license](https://img.shields.io/github/license/dashevo/dapi-grpc.svg)](LICENSE) + +Decentralized API GRPC definition files and generated clients + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contributing](#contributing) +- [License](#license) + +## Install + +Ensure you have the latest [NodeJS](https://nodejs.org/en/download/) installed. + +#### From repository + +Clone the repo: + +```shell +git clone https://github.com/dashevo/dapi-grpc +``` + +Install npm packages: + +```shell +npm install +``` + +#### From NPM + +```sh +npm install @dashevo/dapi-grpc +``` + +## Usage + +Node users are able to access exported elements by requiring them under v0 property. + +### Core Client + +Provide a client to perform core request. + +```js +const { + v0: { + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +const client = new CorePromiseClient(url); +``` + +Provided method allow to then perform the request, by passing a specific request parameter (see below example). +All methods share the same API : +- First parameter expect a specific request instance of a Request class (such as GetBlockRequest, GetTransactionRequest). +- Second parameter is optional for metadata object. +- Third parameter is optional for options. + +Here is a usage example for requesting a Block by its hash and handling its response : + +```js +const { + v0: { + CorePromiseClient, + GetBlockRequest, + GetBlockResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const client = new CorePromiseClient(url); + +async function getBlockByHash(hash, options = {}) { + const getBlockRequest = new GetBlockRequest(); + getBlockRequest.setHash(hash); + + const response = await client.getBlock( + getBlockRequest, + {}, + options, + ); + const blockBinaryArray = response.getBlock(); + + return Buffer.from(blockBinaryArray); +} +``` + +Available methods : + +- getStatus +- getBlock +- broadcastTransaction +- getTransaction +- getEstimatedTransactionFee +- subscribeToBlockHeadersWithChainLocks +- subscribeToTransactionsWithProofs + +For streams, such as subscribeToTransactionsWithProofs and subscribeToBlockHeadersWithChainLocks, a [grpc-web stream](https://github.com/grpc/grpc-web) will be returned. +More info on their usage can be read over their repository. + +### Platform Client + +Provide a client to perform platform request. +Method's API and usage is similar to CorePromiseClient. + +```js +const { + v0: { + PlatformPromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +const client = new PlatformPromiseClient(url); +``` + +Available methods : + +- broadcastStateTransition +- getIdentity +- getDataContract +- getDocuments +- getIdentitiesByPublicKeyHashes +- waitForStateTransitionResult +- getConsensusParams +- setProtocolVersion + +## Maintainer + +[@shumkov](https://github.com/shumkov) + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs. + +## License + +[MIT](LICENSE) © Dash Core Group, Inc. + diff --git a/packages/dapi-grpc/browser.js b/packages/dapi-grpc/browser.js new file mode 100644 index 00000000000..679aff83122 --- /dev/null +++ b/packages/dapi-grpc/browser.js @@ -0,0 +1,10 @@ +const core = require('./clients/core/v0/web/core_grpc_web_pb'); +const platform = require('./clients/platform/v0/web/platform_grpc_web_pb'); + +module.exports = { + v0: { + + ...core, + ...platform, + }, +}; diff --git a/packages/dapi-grpc/clients/core/v0/java/org/dash/platform/dapi/v0/CoreGrpc.java b/packages/dapi-grpc/clients/core/v0/java/org/dash/platform/dapi/v0/CoreGrpc.java new file mode 100644 index 00000000000..1e439ca6482 --- /dev/null +++ b/packages/dapi-grpc/clients/core/v0/java/org/dash/platform/dapi/v0/CoreGrpc.java @@ -0,0 +1,706 @@ +package org.dash.platform.dapi.v0; + +import static io.grpc.MethodDescriptor.generateFullMethodName; + +/** + */ +@javax.annotation.Generated( + value = "by gRPC proto compiler", + comments = "Source: core.proto") +@io.grpc.stub.annotations.GrpcGenerated +public final class CoreGrpc { + + private CoreGrpc() {} + + public static final String SERVICE_NAME = "org.dash.platform.dapi.v0.Core"; + + // Static method descriptors that strictly reflect the proto. + private static volatile io.grpc.MethodDescriptor getGetStatusMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "getStatus", + requestType = org.dash.platform.dapi.v0.CoreOuterClass.GetStatusRequest.class, + responseType = org.dash.platform.dapi.v0.CoreOuterClass.GetStatusResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetStatusMethod() { + io.grpc.MethodDescriptor getGetStatusMethod; + if ((getGetStatusMethod = CoreGrpc.getGetStatusMethod) == null) { + synchronized (CoreGrpc.class) { + if ((getGetStatusMethod = CoreGrpc.getGetStatusMethod) == null) { + CoreGrpc.getGetStatusMethod = getGetStatusMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "getStatus")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.GetStatusRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.GetStatusResponse.getDefaultInstance())) + .setSchemaDescriptor(new CoreMethodDescriptorSupplier("getStatus")) + .build(); + } + } + } + return getGetStatusMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetBlockMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "getBlock", + requestType = org.dash.platform.dapi.v0.CoreOuterClass.GetBlockRequest.class, + responseType = org.dash.platform.dapi.v0.CoreOuterClass.GetBlockResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetBlockMethod() { + io.grpc.MethodDescriptor getGetBlockMethod; + if ((getGetBlockMethod = CoreGrpc.getGetBlockMethod) == null) { + synchronized (CoreGrpc.class) { + if ((getGetBlockMethod = CoreGrpc.getGetBlockMethod) == null) { + CoreGrpc.getGetBlockMethod = getGetBlockMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "getBlock")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.GetBlockRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.GetBlockResponse.getDefaultInstance())) + .setSchemaDescriptor(new CoreMethodDescriptorSupplier("getBlock")) + .build(); + } + } + } + return getGetBlockMethod; + } + + private static volatile io.grpc.MethodDescriptor getBroadcastTransactionMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "broadcastTransaction", + requestType = org.dash.platform.dapi.v0.CoreOuterClass.BroadcastTransactionRequest.class, + responseType = org.dash.platform.dapi.v0.CoreOuterClass.BroadcastTransactionResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getBroadcastTransactionMethod() { + io.grpc.MethodDescriptor getBroadcastTransactionMethod; + if ((getBroadcastTransactionMethod = CoreGrpc.getBroadcastTransactionMethod) == null) { + synchronized (CoreGrpc.class) { + if ((getBroadcastTransactionMethod = CoreGrpc.getBroadcastTransactionMethod) == null) { + CoreGrpc.getBroadcastTransactionMethod = getBroadcastTransactionMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "broadcastTransaction")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.BroadcastTransactionRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.BroadcastTransactionResponse.getDefaultInstance())) + .setSchemaDescriptor(new CoreMethodDescriptorSupplier("broadcastTransaction")) + .build(); + } + } + } + return getBroadcastTransactionMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetTransactionMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "getTransaction", + requestType = org.dash.platform.dapi.v0.CoreOuterClass.GetTransactionRequest.class, + responseType = org.dash.platform.dapi.v0.CoreOuterClass.GetTransactionResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetTransactionMethod() { + io.grpc.MethodDescriptor getGetTransactionMethod; + if ((getGetTransactionMethod = CoreGrpc.getGetTransactionMethod) == null) { + synchronized (CoreGrpc.class) { + if ((getGetTransactionMethod = CoreGrpc.getGetTransactionMethod) == null) { + CoreGrpc.getGetTransactionMethod = getGetTransactionMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "getTransaction")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.GetTransactionRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.GetTransactionResponse.getDefaultInstance())) + .setSchemaDescriptor(new CoreMethodDescriptorSupplier("getTransaction")) + .build(); + } + } + } + return getGetTransactionMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetEstimatedTransactionFeeMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "getEstimatedTransactionFee", + requestType = org.dash.platform.dapi.v0.CoreOuterClass.GetEstimatedTransactionFeeRequest.class, + responseType = org.dash.platform.dapi.v0.CoreOuterClass.GetEstimatedTransactionFeeResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetEstimatedTransactionFeeMethod() { + io.grpc.MethodDescriptor getGetEstimatedTransactionFeeMethod; + if ((getGetEstimatedTransactionFeeMethod = CoreGrpc.getGetEstimatedTransactionFeeMethod) == null) { + synchronized (CoreGrpc.class) { + if ((getGetEstimatedTransactionFeeMethod = CoreGrpc.getGetEstimatedTransactionFeeMethod) == null) { + CoreGrpc.getGetEstimatedTransactionFeeMethod = getGetEstimatedTransactionFeeMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "getEstimatedTransactionFee")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.GetEstimatedTransactionFeeRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.GetEstimatedTransactionFeeResponse.getDefaultInstance())) + .setSchemaDescriptor(new CoreMethodDescriptorSupplier("getEstimatedTransactionFee")) + .build(); + } + } + } + return getGetEstimatedTransactionFeeMethod; + } + + private static volatile io.grpc.MethodDescriptor getSubscribeToBlockHeadersWithChainLocksMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "subscribeToBlockHeadersWithChainLocks", + requestType = org.dash.platform.dapi.v0.CoreOuterClass.BlockHeadersWithChainLocksRequest.class, + responseType = org.dash.platform.dapi.v0.CoreOuterClass.BlockHeadersWithChainLocksResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + public static io.grpc.MethodDescriptor getSubscribeToBlockHeadersWithChainLocksMethod() { + io.grpc.MethodDescriptor getSubscribeToBlockHeadersWithChainLocksMethod; + if ((getSubscribeToBlockHeadersWithChainLocksMethod = CoreGrpc.getSubscribeToBlockHeadersWithChainLocksMethod) == null) { + synchronized (CoreGrpc.class) { + if ((getSubscribeToBlockHeadersWithChainLocksMethod = CoreGrpc.getSubscribeToBlockHeadersWithChainLocksMethod) == null) { + CoreGrpc.getSubscribeToBlockHeadersWithChainLocksMethod = getSubscribeToBlockHeadersWithChainLocksMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "subscribeToBlockHeadersWithChainLocks")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.BlockHeadersWithChainLocksRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.BlockHeadersWithChainLocksResponse.getDefaultInstance())) + .setSchemaDescriptor(new CoreMethodDescriptorSupplier("subscribeToBlockHeadersWithChainLocks")) + .build(); + } + } + } + return getSubscribeToBlockHeadersWithChainLocksMethod; + } + + private static volatile io.grpc.MethodDescriptor getSubscribeToTransactionsWithProofsMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "subscribeToTransactionsWithProofs", + requestType = org.dash.platform.dapi.v0.CoreOuterClass.TransactionsWithProofsRequest.class, + responseType = org.dash.platform.dapi.v0.CoreOuterClass.TransactionsWithProofsResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + public static io.grpc.MethodDescriptor getSubscribeToTransactionsWithProofsMethod() { + io.grpc.MethodDescriptor getSubscribeToTransactionsWithProofsMethod; + if ((getSubscribeToTransactionsWithProofsMethod = CoreGrpc.getSubscribeToTransactionsWithProofsMethod) == null) { + synchronized (CoreGrpc.class) { + if ((getSubscribeToTransactionsWithProofsMethod = CoreGrpc.getSubscribeToTransactionsWithProofsMethod) == null) { + CoreGrpc.getSubscribeToTransactionsWithProofsMethod = getSubscribeToTransactionsWithProofsMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "subscribeToTransactionsWithProofs")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.TransactionsWithProofsRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.CoreOuterClass.TransactionsWithProofsResponse.getDefaultInstance())) + .setSchemaDescriptor(new CoreMethodDescriptorSupplier("subscribeToTransactionsWithProofs")) + .build(); + } + } + } + return getSubscribeToTransactionsWithProofsMethod; + } + + /** + * Creates a new async stub that supports all call types for the service + */ + public static CoreStub newStub(io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public CoreStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new CoreStub(channel, callOptions); + } + }; + return CoreStub.newStub(factory, channel); + } + + /** + * Creates a new blocking-style stub that supports unary and streaming output calls on the service + */ + public static CoreBlockingStub newBlockingStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public CoreBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new CoreBlockingStub(channel, callOptions); + } + }; + return CoreBlockingStub.newStub(factory, channel); + } + + /** + * Creates a new ListenableFuture-style stub that supports unary calls on the service + */ + public static CoreFutureStub newFutureStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public CoreFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new CoreFutureStub(channel, callOptions); + } + }; + return CoreFutureStub.newStub(factory, channel); + } + + /** + */ + public static abstract class CoreImplBase implements io.grpc.BindableService { + + /** + */ + public void getStatus(org.dash.platform.dapi.v0.CoreOuterClass.GetStatusRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetStatusMethod(), responseObserver); + } + + /** + */ + public void getBlock(org.dash.platform.dapi.v0.CoreOuterClass.GetBlockRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetBlockMethod(), responseObserver); + } + + /** + */ + public void broadcastTransaction(org.dash.platform.dapi.v0.CoreOuterClass.BroadcastTransactionRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getBroadcastTransactionMethod(), responseObserver); + } + + /** + */ + public void getTransaction(org.dash.platform.dapi.v0.CoreOuterClass.GetTransactionRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetTransactionMethod(), responseObserver); + } + + /** + */ + public void getEstimatedTransactionFee(org.dash.platform.dapi.v0.CoreOuterClass.GetEstimatedTransactionFeeRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetEstimatedTransactionFeeMethod(), responseObserver); + } + + /** + */ + public void subscribeToBlockHeadersWithChainLocks(org.dash.platform.dapi.v0.CoreOuterClass.BlockHeadersWithChainLocksRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getSubscribeToBlockHeadersWithChainLocksMethod(), responseObserver); + } + + /** + */ + public void subscribeToTransactionsWithProofs(org.dash.platform.dapi.v0.CoreOuterClass.TransactionsWithProofsRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getSubscribeToTransactionsWithProofsMethod(), responseObserver); + } + + @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() { + return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) + .addMethod( + getGetStatusMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + org.dash.platform.dapi.v0.CoreOuterClass.GetStatusRequest, + org.dash.platform.dapi.v0.CoreOuterClass.GetStatusResponse>( + this, METHODID_GET_STATUS))) + .addMethod( + getGetBlockMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + org.dash.platform.dapi.v0.CoreOuterClass.GetBlockRequest, + org.dash.platform.dapi.v0.CoreOuterClass.GetBlockResponse>( + this, METHODID_GET_BLOCK))) + .addMethod( + getBroadcastTransactionMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + org.dash.platform.dapi.v0.CoreOuterClass.BroadcastTransactionRequest, + org.dash.platform.dapi.v0.CoreOuterClass.BroadcastTransactionResponse>( + this, METHODID_BROADCAST_TRANSACTION))) + .addMethod( + getGetTransactionMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + org.dash.platform.dapi.v0.CoreOuterClass.GetTransactionRequest, + org.dash.platform.dapi.v0.CoreOuterClass.GetTransactionResponse>( + this, METHODID_GET_TRANSACTION))) + .addMethod( + getGetEstimatedTransactionFeeMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + org.dash.platform.dapi.v0.CoreOuterClass.GetEstimatedTransactionFeeRequest, + org.dash.platform.dapi.v0.CoreOuterClass.GetEstimatedTransactionFeeResponse>( + this, METHODID_GET_ESTIMATED_TRANSACTION_FEE))) + .addMethod( + getSubscribeToBlockHeadersWithChainLocksMethod(), + io.grpc.stub.ServerCalls.asyncServerStreamingCall( + new MethodHandlers< + org.dash.platform.dapi.v0.CoreOuterClass.BlockHeadersWithChainLocksRequest, + org.dash.platform.dapi.v0.CoreOuterClass.BlockHeadersWithChainLocksResponse>( + this, METHODID_SUBSCRIBE_TO_BLOCK_HEADERS_WITH_CHAIN_LOCKS))) + .addMethod( + getSubscribeToTransactionsWithProofsMethod(), + io.grpc.stub.ServerCalls.asyncServerStreamingCall( + new MethodHandlers< + org.dash.platform.dapi.v0.CoreOuterClass.TransactionsWithProofsRequest, + org.dash.platform.dapi.v0.CoreOuterClass.TransactionsWithProofsResponse>( + this, METHODID_SUBSCRIBE_TO_TRANSACTIONS_WITH_PROOFS))) + .build(); + } + } + + /** + */ + public static final class CoreStub extends io.grpc.stub.AbstractAsyncStub { + private CoreStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected CoreStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new CoreStub(channel, callOptions); + } + + /** + */ + public void getStatus(org.dash.platform.dapi.v0.CoreOuterClass.GetStatusRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetStatusMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void getBlock(org.dash.platform.dapi.v0.CoreOuterClass.GetBlockRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetBlockMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void broadcastTransaction(org.dash.platform.dapi.v0.CoreOuterClass.BroadcastTransactionRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getBroadcastTransactionMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void getTransaction(org.dash.platform.dapi.v0.CoreOuterClass.GetTransactionRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetTransactionMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void getEstimatedTransactionFee(org.dash.platform.dapi.v0.CoreOuterClass.GetEstimatedTransactionFeeRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetEstimatedTransactionFeeMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void subscribeToBlockHeadersWithChainLocks(org.dash.platform.dapi.v0.CoreOuterClass.BlockHeadersWithChainLocksRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncServerStreamingCall( + getChannel().newCall(getSubscribeToBlockHeadersWithChainLocksMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void subscribeToTransactionsWithProofs(org.dash.platform.dapi.v0.CoreOuterClass.TransactionsWithProofsRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncServerStreamingCall( + getChannel().newCall(getSubscribeToTransactionsWithProofsMethod(), getCallOptions()), request, responseObserver); + } + } + + /** + */ + public static final class CoreBlockingStub extends io.grpc.stub.AbstractBlockingStub { + private CoreBlockingStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected CoreBlockingStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new CoreBlockingStub(channel, callOptions); + } + + /** + */ + public org.dash.platform.dapi.v0.CoreOuterClass.GetStatusResponse getStatus(org.dash.platform.dapi.v0.CoreOuterClass.GetStatusRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetStatusMethod(), getCallOptions(), request); + } + + /** + */ + public org.dash.platform.dapi.v0.CoreOuterClass.GetBlockResponse getBlock(org.dash.platform.dapi.v0.CoreOuterClass.GetBlockRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetBlockMethod(), getCallOptions(), request); + } + + /** + */ + public org.dash.platform.dapi.v0.CoreOuterClass.BroadcastTransactionResponse broadcastTransaction(org.dash.platform.dapi.v0.CoreOuterClass.BroadcastTransactionRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getBroadcastTransactionMethod(), getCallOptions(), request); + } + + /** + */ + public org.dash.platform.dapi.v0.CoreOuterClass.GetTransactionResponse getTransaction(org.dash.platform.dapi.v0.CoreOuterClass.GetTransactionRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetTransactionMethod(), getCallOptions(), request); + } + + /** + */ + public org.dash.platform.dapi.v0.CoreOuterClass.GetEstimatedTransactionFeeResponse getEstimatedTransactionFee(org.dash.platform.dapi.v0.CoreOuterClass.GetEstimatedTransactionFeeRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetEstimatedTransactionFeeMethod(), getCallOptions(), request); + } + + /** + */ + public java.util.Iterator subscribeToBlockHeadersWithChainLocks( + org.dash.platform.dapi.v0.CoreOuterClass.BlockHeadersWithChainLocksRequest request) { + return io.grpc.stub.ClientCalls.blockingServerStreamingCall( + getChannel(), getSubscribeToBlockHeadersWithChainLocksMethod(), getCallOptions(), request); + } + + /** + */ + public java.util.Iterator subscribeToTransactionsWithProofs( + org.dash.platform.dapi.v0.CoreOuterClass.TransactionsWithProofsRequest request) { + return io.grpc.stub.ClientCalls.blockingServerStreamingCall( + getChannel(), getSubscribeToTransactionsWithProofsMethod(), getCallOptions(), request); + } + } + + /** + */ + public static final class CoreFutureStub extends io.grpc.stub.AbstractFutureStub { + private CoreFutureStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected CoreFutureStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new CoreFutureStub(channel, callOptions); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getStatus( + org.dash.platform.dapi.v0.CoreOuterClass.GetStatusRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetStatusMethod(), getCallOptions()), request); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getBlock( + org.dash.platform.dapi.v0.CoreOuterClass.GetBlockRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetBlockMethod(), getCallOptions()), request); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture broadcastTransaction( + org.dash.platform.dapi.v0.CoreOuterClass.BroadcastTransactionRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getBroadcastTransactionMethod(), getCallOptions()), request); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getTransaction( + org.dash.platform.dapi.v0.CoreOuterClass.GetTransactionRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetTransactionMethod(), getCallOptions()), request); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getEstimatedTransactionFee( + org.dash.platform.dapi.v0.CoreOuterClass.GetEstimatedTransactionFeeRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetEstimatedTransactionFeeMethod(), getCallOptions()), request); + } + } + + private static final int METHODID_GET_STATUS = 0; + private static final int METHODID_GET_BLOCK = 1; + private static final int METHODID_BROADCAST_TRANSACTION = 2; + private static final int METHODID_GET_TRANSACTION = 3; + private static final int METHODID_GET_ESTIMATED_TRANSACTION_FEE = 4; + private static final int METHODID_SUBSCRIBE_TO_BLOCK_HEADERS_WITH_CHAIN_LOCKS = 5; + private static final int METHODID_SUBSCRIBE_TO_TRANSACTIONS_WITH_PROOFS = 6; + + private static final class MethodHandlers implements + io.grpc.stub.ServerCalls.UnaryMethod, + io.grpc.stub.ServerCalls.ServerStreamingMethod, + io.grpc.stub.ServerCalls.ClientStreamingMethod, + io.grpc.stub.ServerCalls.BidiStreamingMethod { + private final CoreImplBase serviceImpl; + private final int methodId; + + MethodHandlers(CoreImplBase serviceImpl, int methodId) { + this.serviceImpl = serviceImpl; + this.methodId = methodId; + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_GET_STATUS: + serviceImpl.getStatus((org.dash.platform.dapi.v0.CoreOuterClass.GetStatusRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_BLOCK: + serviceImpl.getBlock((org.dash.platform.dapi.v0.CoreOuterClass.GetBlockRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_BROADCAST_TRANSACTION: + serviceImpl.broadcastTransaction((org.dash.platform.dapi.v0.CoreOuterClass.BroadcastTransactionRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_TRANSACTION: + serviceImpl.getTransaction((org.dash.platform.dapi.v0.CoreOuterClass.GetTransactionRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_ESTIMATED_TRANSACTION_FEE: + serviceImpl.getEstimatedTransactionFee((org.dash.platform.dapi.v0.CoreOuterClass.GetEstimatedTransactionFeeRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_SUBSCRIBE_TO_BLOCK_HEADERS_WITH_CHAIN_LOCKS: + serviceImpl.subscribeToBlockHeadersWithChainLocks((org.dash.platform.dapi.v0.CoreOuterClass.BlockHeadersWithChainLocksRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_SUBSCRIBE_TO_TRANSACTIONS_WITH_PROOFS: + serviceImpl.subscribeToTransactionsWithProofs((org.dash.platform.dapi.v0.CoreOuterClass.TransactionsWithProofsRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + default: + throw new AssertionError(); + } + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public io.grpc.stub.StreamObserver invoke( + io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + default: + throw new AssertionError(); + } + } + } + + private static abstract class CoreBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { + CoreBaseDescriptorSupplier() {} + + @java.lang.Override + public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { + return org.dash.platform.dapi.v0.CoreOuterClass.getDescriptor(); + } + + @java.lang.Override + public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { + return getFileDescriptor().findServiceByName("Core"); + } + } + + private static final class CoreFileDescriptorSupplier + extends CoreBaseDescriptorSupplier { + CoreFileDescriptorSupplier() {} + } + + private static final class CoreMethodDescriptorSupplier + extends CoreBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { + private final String methodName; + + CoreMethodDescriptorSupplier(String methodName) { + this.methodName = methodName; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { + return getServiceDescriptor().findMethodByName(methodName); + } + } + + private static volatile io.grpc.ServiceDescriptor serviceDescriptor; + + public static io.grpc.ServiceDescriptor getServiceDescriptor() { + io.grpc.ServiceDescriptor result = serviceDescriptor; + if (result == null) { + synchronized (CoreGrpc.class) { + result = serviceDescriptor; + if (result == null) { + serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) + .setSchemaDescriptor(new CoreFileDescriptorSupplier()) + .addMethod(getGetStatusMethod()) + .addMethod(getGetBlockMethod()) + .addMethod(getBroadcastTransactionMethod()) + .addMethod(getGetTransactionMethod()) + .addMethod(getGetEstimatedTransactionFeeMethod()) + .addMethod(getSubscribeToBlockHeadersWithChainLocksMethod()) + .addMethod(getSubscribeToTransactionsWithProofsMethod()) + .build(); + } + } + } + return result; + } +} diff --git a/packages/dapi-grpc/clients/core/v0/nodejs/CorePromiseClient.js b/packages/dapi-grpc/clients/core/v0/nodejs/CorePromiseClient.js new file mode 100644 index 00000000000..1ba176cc6c3 --- /dev/null +++ b/packages/dapi-grpc/clients/core/v0/nodejs/CorePromiseClient.js @@ -0,0 +1,318 @@ +const grpc = require('@grpc/grpc-js'); +const { promisify } = require('util'); + +const { + convertObjectToMetadata, + utils: { + isObject, + }, + client: { + interceptors: { + jsonToProtobufInterceptorFactory, + }, + converters: { + jsonToProtobufFactory, + protobufToJsonFactory, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + org: { + dash: { + platform: { + dapi: { + v0: { + GetStatusRequest: PBJSGetStatusRequest, + GetStatusResponse: PBJSGetStatusResponse, + GetBlockRequest: PBJSGetBlockRequest, + GetBlockResponse: PBJSGetBlockResponse, + BroadcastTransactionRequest: PBJSBroadcastTransactionRequest, + BroadcastTransactionResponse: PBJSBroadcastTransactionResponse, + GetTransactionRequest: PBJSGetTransactionRequest, + GetTransactionResponse: PBJSGetTransactionResponse, + BlockHeadersWithChainLocksRequest: PBJSBlockHeadersWithChainLocksRequest, + BlockHeadersWithChainLocksResponse: PBJSBlockHeadersWithChainLocksResponse, + GetEstimatedTransactionFeeRequest: PBJSGetEstimatedTransactionFeeRequest, + GetEstimatedTransactionFeeResponse: PBJSGetEstimatedTransactionFeeResponse, + TransactionsWithProofsRequest: PBJSTransactionsWithProofsRequest, + TransactionsWithProofsResponse: PBJSTransactionsWithProofsResponse, + }, + }, + }, + }, + }, +} = require('./core_pbjs'); + +const { + GetStatusResponse: ProtocGetStatusResponse, + GetBlockResponse: ProtocGetBlockResponse, + BroadcastTransactionResponse: ProtocBroadcastTransactionResponse, + GetTransactionResponse: ProtocGetTransactionResponse, + BlockHeadersWithChainLocksResponse: ProtocBlockHeadersWithChainLocksResponse, + GetEstimatedTransactionFeeResponse: ProtocGetEstimatedTransactionFeeResponse, + TransactionsWithProofsResponse: ProtocTransactionsWithProofsResponse, +} = require('./core_protoc'); + +const getCoreDefinition = require('../../../../lib/getCoreDefinition'); +const stripHostname = require('../../../../lib/utils/stripHostname'); + +const CoreNodeJSClient = getCoreDefinition(0); + +class CorePromiseClient { + /** + * @param {string} hostname + * @param {?Object} credentials + * @param {?Object} options + */ + constructor(hostname, credentials = grpc.credentials.createInsecure(), options = {}) { + const strippedHostname = stripHostname(hostname); + + this.client = new CoreNodeJSClient(strippedHostname, credentials, options); + + this.client.getStatus = promisify( + this.client.getStatus.bind(this.client), + ); + + this.client.getBlock = promisify( + this.client.getBlock.bind(this.client), + ); + + this.client.broadcastTransaction = promisify( + this.client.broadcastTransaction.bind(this.client), + ); + + this.client.getTransaction = promisify( + this.client.getTransaction.bind(this.client), + ); + + this.client.getEstimatedTransactionFee = promisify( + this.client.getEstimatedTransactionFee.bind(this.client), + ); + } + + /** + * @param {!GetStatusRequest} getStatusRequest + * @param {?Object} metadata + * @param {CallOptions} [options={}] + * @return {Promise} + */ + getStatus(getStatusRequest, metadata = {}, options = {}) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.getStatus( + getStatusRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocGetStatusResponse, + PBJSGetStatusResponse, + ), + protobufToJsonFactory( + PBJSGetStatusRequest, + ), + ), + ], + ...options, + }, + ); + } + + /** + * @param {!GetBlockRequest} getBlockRequest + * @param {?Object} metadata + * @param {CallOptions} [options={}] + * @return {Promise} + */ + getBlock(getBlockRequest, metadata = {}, options = {}) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.getBlock( + getBlockRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocGetBlockResponse, + PBJSGetBlockResponse, + ), + protobufToJsonFactory( + PBJSGetBlockRequest, + ), + ), + ], + ...options, + }, + ); + } + + /** + * @param {!BroadcastTransactionRequest} broadcastTransactionRequest + * @param {?Object} metadata + * @param {CallOptions} [options={}] + * @return {Promise} + */ + broadcastTransaction(broadcastTransactionRequest, metadata = {}, options = {}) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.broadcastTransaction( + broadcastTransactionRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocBroadcastTransactionResponse, + PBJSBroadcastTransactionResponse, + ), + protobufToJsonFactory( + PBJSBroadcastTransactionRequest, + ), + ), + ], + ...options, + }, + ); + } + + /** + * @param {!GetTransactionRequest} getTransactionRequest + * @param {?Object} metadata + * @param {CallOptions} [options={}] + * @return {Promise} + */ + getTransaction(getTransactionRequest, metadata = {}, options = {}) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.getTransaction( + getTransactionRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocGetTransactionResponse, + PBJSGetTransactionResponse, + ), + protobufToJsonFactory( + PBJSGetTransactionRequest, + ), + ), + ], + ...options, + }, + ); + } + + /** + * @param {!GetEstimatedTransactionFeeRequest} getEstimatedTransactionFeeRequest + * @param {?Object} metadata + * @param {CallOptions} [options={}] + * @returns {Promise} + */ + getEstimatedTransactionFee(getEstimatedTransactionFeeRequest, metadata = {}, options = {}) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.getEstimatedTransactionFee( + getEstimatedTransactionFeeRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocGetEstimatedTransactionFeeResponse, + PBJSGetEstimatedTransactionFeeResponse, + ), + protobufToJsonFactory( + PBJSGetEstimatedTransactionFeeRequest, + ), + ), + ], + ...options, + }, + ); + } + + /** + * @param {!BlockHeadersWithChainLocksRequest} blockHeadersWithChainLocksRequest + * @param {?Object} metadata + * @param {CallOptions} [options={}] + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ + subscribeToBlockHeadersWithChainLocks( + blockHeadersWithChainLocksRequest, + metadata = {}, + options = {}, + ) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.subscribeToBlockHeadersWithChainLocks( + blockHeadersWithChainLocksRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocBlockHeadersWithChainLocksResponse, + PBJSBlockHeadersWithChainLocksResponse, + ), + protobufToJsonFactory( + PBJSBlockHeadersWithChainLocksRequest, + ), + ), + ], + ...options, + }, + ); + } + + /** + * @param {TransactionsWithProofsRequest} transactionsWithProofsRequest The request proto + * @param {?Object} metadata User defined call metadata + * @param {CallOptions} [options={}] + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ + subscribeToTransactionsWithProofs(transactionsWithProofsRequest, metadata = {}, options = {}) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.subscribeToTransactionsWithProofs( + transactionsWithProofsRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocTransactionsWithProofsResponse, + PBJSTransactionsWithProofsResponse, + ), + protobufToJsonFactory( + PBJSTransactionsWithProofsRequest, + ), + ), + ], + ...options, + }, + ); + } +} + +module.exports = CorePromiseClient; diff --git a/packages/dapi-grpc/clients/core/v0/nodejs/core_pbjs.js b/packages/dapi-grpc/clients/core/v0/nodejs/core_pbjs.js new file mode 100644 index 00000000000..cd62cc98c1b --- /dev/null +++ b/packages/dapi-grpc/clients/core/v0/nodejs/core_pbjs.js @@ -0,0 +1,6188 @@ +/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ +"use strict"; + +var $protobuf = require("@dashevo/protobufjs/minimal"); + +// Common aliases +var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util; + +// Exported root namespace +var $root = $protobuf.roots.core_root || ($protobuf.roots.core_root = {}); + +$root.org = (function() { + + /** + * Namespace org. + * @exports org + * @namespace + */ + var org = {}; + + org.dash = (function() { + + /** + * Namespace dash. + * @memberof org + * @namespace + */ + var dash = {}; + + dash.platform = (function() { + + /** + * Namespace platform. + * @memberof org.dash + * @namespace + */ + var platform = {}; + + platform.dapi = (function() { + + /** + * Namespace dapi. + * @memberof org.dash.platform + * @namespace + */ + var dapi = {}; + + dapi.v0 = (function() { + + /** + * Namespace v0. + * @memberof org.dash.platform.dapi + * @namespace + */ + var v0 = {}; + + v0.Core = (function() { + + /** + * Constructs a new Core service. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a Core + * @extends $protobuf.rpc.Service + * @constructor + * @param {$protobuf.RPCImpl} rpcImpl RPC implementation + * @param {boolean} [requestDelimited=false] Whether requests are length-delimited + * @param {boolean} [responseDelimited=false] Whether responses are length-delimited + */ + function Core(rpcImpl, requestDelimited, responseDelimited) { + $protobuf.rpc.Service.call(this, rpcImpl, requestDelimited, responseDelimited); + } + + (Core.prototype = Object.create($protobuf.rpc.Service.prototype)).constructor = Core; + + /** + * Creates new Core service using the specified rpc implementation. + * @function create + * @memberof org.dash.platform.dapi.v0.Core + * @static + * @param {$protobuf.RPCImpl} rpcImpl RPC implementation + * @param {boolean} [requestDelimited=false] Whether requests are length-delimited + * @param {boolean} [responseDelimited=false] Whether responses are length-delimited + * @returns {Core} RPC service. Useful where requests and/or responses are streamed. + */ + Core.create = function create(rpcImpl, requestDelimited, responseDelimited) { + return new this(rpcImpl, requestDelimited, responseDelimited); + }; + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Core#getStatus}. + * @memberof org.dash.platform.dapi.v0.Core + * @typedef getStatusCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.GetStatusResponse} [response] GetStatusResponse + */ + + /** + * Calls getStatus. + * @function getStatus + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.IGetStatusRequest} request GetStatusRequest message or plain object + * @param {org.dash.platform.dapi.v0.Core.getStatusCallback} callback Node-style callback called with the error, if any, and GetStatusResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Core.prototype.getStatus = function getStatus(request, callback) { + return this.rpcCall(getStatus, $root.org.dash.platform.dapi.v0.GetStatusRequest, $root.org.dash.platform.dapi.v0.GetStatusResponse, request, callback); + }, "name", { value: "getStatus" }); + + /** + * Calls getStatus. + * @function getStatus + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.IGetStatusRequest} request GetStatusRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Core#getBlock}. + * @memberof org.dash.platform.dapi.v0.Core + * @typedef getBlockCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.GetBlockResponse} [response] GetBlockResponse + */ + + /** + * Calls getBlock. + * @function getBlock + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.IGetBlockRequest} request GetBlockRequest message or plain object + * @param {org.dash.platform.dapi.v0.Core.getBlockCallback} callback Node-style callback called with the error, if any, and GetBlockResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Core.prototype.getBlock = function getBlock(request, callback) { + return this.rpcCall(getBlock, $root.org.dash.platform.dapi.v0.GetBlockRequest, $root.org.dash.platform.dapi.v0.GetBlockResponse, request, callback); + }, "name", { value: "getBlock" }); + + /** + * Calls getBlock. + * @function getBlock + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.IGetBlockRequest} request GetBlockRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Core#broadcastTransaction}. + * @memberof org.dash.platform.dapi.v0.Core + * @typedef broadcastTransactionCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.BroadcastTransactionResponse} [response] BroadcastTransactionResponse + */ + + /** + * Calls broadcastTransaction. + * @function broadcastTransaction + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.IBroadcastTransactionRequest} request BroadcastTransactionRequest message or plain object + * @param {org.dash.platform.dapi.v0.Core.broadcastTransactionCallback} callback Node-style callback called with the error, if any, and BroadcastTransactionResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Core.prototype.broadcastTransaction = function broadcastTransaction(request, callback) { + return this.rpcCall(broadcastTransaction, $root.org.dash.platform.dapi.v0.BroadcastTransactionRequest, $root.org.dash.platform.dapi.v0.BroadcastTransactionResponse, request, callback); + }, "name", { value: "broadcastTransaction" }); + + /** + * Calls broadcastTransaction. + * @function broadcastTransaction + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.IBroadcastTransactionRequest} request BroadcastTransactionRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Core#getTransaction}. + * @memberof org.dash.platform.dapi.v0.Core + * @typedef getTransactionCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.GetTransactionResponse} [response] GetTransactionResponse + */ + + /** + * Calls getTransaction. + * @function getTransaction + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.IGetTransactionRequest} request GetTransactionRequest message or plain object + * @param {org.dash.platform.dapi.v0.Core.getTransactionCallback} callback Node-style callback called with the error, if any, and GetTransactionResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Core.prototype.getTransaction = function getTransaction(request, callback) { + return this.rpcCall(getTransaction, $root.org.dash.platform.dapi.v0.GetTransactionRequest, $root.org.dash.platform.dapi.v0.GetTransactionResponse, request, callback); + }, "name", { value: "getTransaction" }); + + /** + * Calls getTransaction. + * @function getTransaction + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.IGetTransactionRequest} request GetTransactionRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Core#getEstimatedTransactionFee}. + * @memberof org.dash.platform.dapi.v0.Core + * @typedef getEstimatedTransactionFeeCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} [response] GetEstimatedTransactionFeeResponse + */ + + /** + * Calls getEstimatedTransactionFee. + * @function getEstimatedTransactionFee + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.IGetEstimatedTransactionFeeRequest} request GetEstimatedTransactionFeeRequest message or plain object + * @param {org.dash.platform.dapi.v0.Core.getEstimatedTransactionFeeCallback} callback Node-style callback called with the error, if any, and GetEstimatedTransactionFeeResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Core.prototype.getEstimatedTransactionFee = function getEstimatedTransactionFee(request, callback) { + return this.rpcCall(getEstimatedTransactionFee, $root.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest, $root.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse, request, callback); + }, "name", { value: "getEstimatedTransactionFee" }); + + /** + * Calls getEstimatedTransactionFee. + * @function getEstimatedTransactionFee + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.IGetEstimatedTransactionFeeRequest} request GetEstimatedTransactionFeeRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Core#subscribeToBlockHeadersWithChainLocks}. + * @memberof org.dash.platform.dapi.v0.Core + * @typedef subscribeToBlockHeadersWithChainLocksCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} [response] BlockHeadersWithChainLocksResponse + */ + + /** + * Calls subscribeToBlockHeadersWithChainLocks. + * @function subscribeToBlockHeadersWithChainLocks + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.IBlockHeadersWithChainLocksRequest} request BlockHeadersWithChainLocksRequest message or plain object + * @param {org.dash.platform.dapi.v0.Core.subscribeToBlockHeadersWithChainLocksCallback} callback Node-style callback called with the error, if any, and BlockHeadersWithChainLocksResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Core.prototype.subscribeToBlockHeadersWithChainLocks = function subscribeToBlockHeadersWithChainLocks(request, callback) { + return this.rpcCall(subscribeToBlockHeadersWithChainLocks, $root.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest, $root.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse, request, callback); + }, "name", { value: "subscribeToBlockHeadersWithChainLocks" }); + + /** + * Calls subscribeToBlockHeadersWithChainLocks. + * @function subscribeToBlockHeadersWithChainLocks + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.IBlockHeadersWithChainLocksRequest} request BlockHeadersWithChainLocksRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Core#subscribeToTransactionsWithProofs}. + * @memberof org.dash.platform.dapi.v0.Core + * @typedef subscribeToTransactionsWithProofsCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.TransactionsWithProofsResponse} [response] TransactionsWithProofsResponse + */ + + /** + * Calls subscribeToTransactionsWithProofs. + * @function subscribeToTransactionsWithProofs + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.ITransactionsWithProofsRequest} request TransactionsWithProofsRequest message or plain object + * @param {org.dash.platform.dapi.v0.Core.subscribeToTransactionsWithProofsCallback} callback Node-style callback called with the error, if any, and TransactionsWithProofsResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Core.prototype.subscribeToTransactionsWithProofs = function subscribeToTransactionsWithProofs(request, callback) { + return this.rpcCall(subscribeToTransactionsWithProofs, $root.org.dash.platform.dapi.v0.TransactionsWithProofsRequest, $root.org.dash.platform.dapi.v0.TransactionsWithProofsResponse, request, callback); + }, "name", { value: "subscribeToTransactionsWithProofs" }); + + /** + * Calls subscribeToTransactionsWithProofs. + * @function subscribeToTransactionsWithProofs + * @memberof org.dash.platform.dapi.v0.Core + * @instance + * @param {org.dash.platform.dapi.v0.ITransactionsWithProofsRequest} request TransactionsWithProofsRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + return Core; + })(); + + v0.GetStatusRequest = (function() { + + /** + * Properties of a GetStatusRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetStatusRequest + */ + + /** + * Constructs a new GetStatusRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetStatusRequest. + * @implements IGetStatusRequest + * @constructor + * @param {org.dash.platform.dapi.v0.IGetStatusRequest=} [properties] Properties to set + */ + function GetStatusRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Creates a new GetStatusRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetStatusRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetStatusRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetStatusRequest} GetStatusRequest instance + */ + GetStatusRequest.create = function create(properties) { + return new GetStatusRequest(properties); + }; + + /** + * Encodes the specified GetStatusRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetStatusRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetStatusRequest} message GetStatusRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetStatusRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + return writer; + }; + + /** + * Encodes the specified GetStatusRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetStatusRequest} message GetStatusRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetStatusRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetStatusRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetStatusRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetStatusRequest} GetStatusRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetStatusRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetStatusRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetStatusRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetStatusRequest} GetStatusRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetStatusRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetStatusRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetStatusRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetStatusRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + return null; + }; + + /** + * Creates a GetStatusRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetStatusRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetStatusRequest} GetStatusRequest + */ + GetStatusRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetStatusRequest) + return object; + return new $root.org.dash.platform.dapi.v0.GetStatusRequest(); + }; + + /** + * Creates a plain object from a GetStatusRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetStatusRequest + * @static + * @param {org.dash.platform.dapi.v0.GetStatusRequest} message GetStatusRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetStatusRequest.toObject = function toObject() { + return {}; + }; + + /** + * Converts this GetStatusRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetStatusRequest + * @instance + * @returns {Object.} JSON object + */ + GetStatusRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetStatusRequest; + })(); + + v0.GetStatusResponse = (function() { + + /** + * Properties of a GetStatusResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetStatusResponse + * @property {org.dash.platform.dapi.v0.GetStatusResponse.IVersion|null} [version] GetStatusResponse version + * @property {org.dash.platform.dapi.v0.GetStatusResponse.ITime|null} [time] GetStatusResponse time + * @property {org.dash.platform.dapi.v0.GetStatusResponse.Status|null} [status] GetStatusResponse status + * @property {number|null} [syncProgress] GetStatusResponse syncProgress + * @property {org.dash.platform.dapi.v0.GetStatusResponse.IChain|null} [chain] GetStatusResponse chain + * @property {org.dash.platform.dapi.v0.GetStatusResponse.IMasternode|null} [masternode] GetStatusResponse masternode + * @property {org.dash.platform.dapi.v0.GetStatusResponse.INetwork|null} [network] GetStatusResponse network + */ + + /** + * Constructs a new GetStatusResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetStatusResponse. + * @implements IGetStatusResponse + * @constructor + * @param {org.dash.platform.dapi.v0.IGetStatusResponse=} [properties] Properties to set + */ + function GetStatusResponse(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetStatusResponse version. + * @member {org.dash.platform.dapi.v0.GetStatusResponse.IVersion|null|undefined} version + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @instance + */ + GetStatusResponse.prototype.version = null; + + /** + * GetStatusResponse time. + * @member {org.dash.platform.dapi.v0.GetStatusResponse.ITime|null|undefined} time + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @instance + */ + GetStatusResponse.prototype.time = null; + + /** + * GetStatusResponse status. + * @member {org.dash.platform.dapi.v0.GetStatusResponse.Status} status + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @instance + */ + GetStatusResponse.prototype.status = 0; + + /** + * GetStatusResponse syncProgress. + * @member {number} syncProgress + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @instance + */ + GetStatusResponse.prototype.syncProgress = 0; + + /** + * GetStatusResponse chain. + * @member {org.dash.platform.dapi.v0.GetStatusResponse.IChain|null|undefined} chain + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @instance + */ + GetStatusResponse.prototype.chain = null; + + /** + * GetStatusResponse masternode. + * @member {org.dash.platform.dapi.v0.GetStatusResponse.IMasternode|null|undefined} masternode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @instance + */ + GetStatusResponse.prototype.masternode = null; + + /** + * GetStatusResponse network. + * @member {org.dash.platform.dapi.v0.GetStatusResponse.INetwork|null|undefined} network + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @instance + */ + GetStatusResponse.prototype.network = null; + + /** + * Creates a new GetStatusResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetStatusResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetStatusResponse} GetStatusResponse instance + */ + GetStatusResponse.create = function create(properties) { + return new GetStatusResponse(properties); + }; + + /** + * Encodes the specified GetStatusResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetStatusResponse} message GetStatusResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetStatusResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.version != null && Object.hasOwnProperty.call(message, "version")) + $root.org.dash.platform.dapi.v0.GetStatusResponse.Version.encode(message.version, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.time != null && Object.hasOwnProperty.call(message, "time")) + $root.org.dash.platform.dapi.v0.GetStatusResponse.Time.encode(message.time, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + if (message.status != null && Object.hasOwnProperty.call(message, "status")) + writer.uint32(/* id 3, wireType 0 =*/24).int32(message.status); + if (message.syncProgress != null && Object.hasOwnProperty.call(message, "syncProgress")) + writer.uint32(/* id 4, wireType 1 =*/33).double(message.syncProgress); + if (message.chain != null && Object.hasOwnProperty.call(message, "chain")) + $root.org.dash.platform.dapi.v0.GetStatusResponse.Chain.encode(message.chain, writer.uint32(/* id 5, wireType 2 =*/42).fork()).ldelim(); + if (message.masternode != null && Object.hasOwnProperty.call(message, "masternode")) + $root.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.encode(message.masternode, writer.uint32(/* id 6, wireType 2 =*/50).fork()).ldelim(); + if (message.network != null && Object.hasOwnProperty.call(message, "network")) + $root.org.dash.platform.dapi.v0.GetStatusResponse.Network.encode(message.network, writer.uint32(/* id 7, wireType 2 =*/58).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified GetStatusResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetStatusResponse} message GetStatusResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetStatusResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetStatusResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetStatusResponse} GetStatusResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetStatusResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetStatusResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.version = $root.org.dash.platform.dapi.v0.GetStatusResponse.Version.decode(reader, reader.uint32()); + break; + case 2: + message.time = $root.org.dash.platform.dapi.v0.GetStatusResponse.Time.decode(reader, reader.uint32()); + break; + case 3: + message.status = reader.int32(); + break; + case 4: + message.syncProgress = reader.double(); + break; + case 5: + message.chain = $root.org.dash.platform.dapi.v0.GetStatusResponse.Chain.decode(reader, reader.uint32()); + break; + case 6: + message.masternode = $root.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.decode(reader, reader.uint32()); + break; + case 7: + message.network = $root.org.dash.platform.dapi.v0.GetStatusResponse.Network.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetStatusResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetStatusResponse} GetStatusResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetStatusResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetStatusResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetStatusResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.version != null && message.hasOwnProperty("version")) { + var error = $root.org.dash.platform.dapi.v0.GetStatusResponse.Version.verify(message.version); + if (error) + return "version." + error; + } + if (message.time != null && message.hasOwnProperty("time")) { + var error = $root.org.dash.platform.dapi.v0.GetStatusResponse.Time.verify(message.time); + if (error) + return "time." + error; + } + if (message.status != null && message.hasOwnProperty("status")) + switch (message.status) { + default: + return "status: enum value expected"; + case 0: + case 1: + case 2: + case 3: + break; + } + if (message.syncProgress != null && message.hasOwnProperty("syncProgress")) + if (typeof message.syncProgress !== "number") + return "syncProgress: number expected"; + if (message.chain != null && message.hasOwnProperty("chain")) { + var error = $root.org.dash.platform.dapi.v0.GetStatusResponse.Chain.verify(message.chain); + if (error) + return "chain." + error; + } + if (message.masternode != null && message.hasOwnProperty("masternode")) { + var error = $root.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.verify(message.masternode); + if (error) + return "masternode." + error; + } + if (message.network != null && message.hasOwnProperty("network")) { + var error = $root.org.dash.platform.dapi.v0.GetStatusResponse.Network.verify(message.network); + if (error) + return "network." + error; + } + return null; + }; + + /** + * Creates a GetStatusResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetStatusResponse} GetStatusResponse + */ + GetStatusResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetStatusResponse) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetStatusResponse(); + if (object.version != null) { + if (typeof object.version !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetStatusResponse.version: object expected"); + message.version = $root.org.dash.platform.dapi.v0.GetStatusResponse.Version.fromObject(object.version); + } + if (object.time != null) { + if (typeof object.time !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetStatusResponse.time: object expected"); + message.time = $root.org.dash.platform.dapi.v0.GetStatusResponse.Time.fromObject(object.time); + } + switch (object.status) { + case "NOT_STARTED": + case 0: + message.status = 0; + break; + case "SYNCING": + case 1: + message.status = 1; + break; + case "READY": + case 2: + message.status = 2; + break; + case "ERROR": + case 3: + message.status = 3; + break; + } + if (object.syncProgress != null) + message.syncProgress = Number(object.syncProgress); + if (object.chain != null) { + if (typeof object.chain !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetStatusResponse.chain: object expected"); + message.chain = $root.org.dash.platform.dapi.v0.GetStatusResponse.Chain.fromObject(object.chain); + } + if (object.masternode != null) { + if (typeof object.masternode !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetStatusResponse.masternode: object expected"); + message.masternode = $root.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.fromObject(object.masternode); + } + if (object.network != null) { + if (typeof object.network !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetStatusResponse.network: object expected"); + message.network = $root.org.dash.platform.dapi.v0.GetStatusResponse.Network.fromObject(object.network); + } + return message; + }; + + /** + * Creates a plain object from a GetStatusResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse} message GetStatusResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetStatusResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.version = null; + object.time = null; + object.status = options.enums === String ? "NOT_STARTED" : 0; + object.syncProgress = 0; + object.chain = null; + object.masternode = null; + object.network = null; + } + if (message.version != null && message.hasOwnProperty("version")) + object.version = $root.org.dash.platform.dapi.v0.GetStatusResponse.Version.toObject(message.version, options); + if (message.time != null && message.hasOwnProperty("time")) + object.time = $root.org.dash.platform.dapi.v0.GetStatusResponse.Time.toObject(message.time, options); + if (message.status != null && message.hasOwnProperty("status")) + object.status = options.enums === String ? $root.org.dash.platform.dapi.v0.GetStatusResponse.Status[message.status] : message.status; + if (message.syncProgress != null && message.hasOwnProperty("syncProgress")) + object.syncProgress = options.json && !isFinite(message.syncProgress) ? String(message.syncProgress) : message.syncProgress; + if (message.chain != null && message.hasOwnProperty("chain")) + object.chain = $root.org.dash.platform.dapi.v0.GetStatusResponse.Chain.toObject(message.chain, options); + if (message.masternode != null && message.hasOwnProperty("masternode")) + object.masternode = $root.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.toObject(message.masternode, options); + if (message.network != null && message.hasOwnProperty("network")) + object.network = $root.org.dash.platform.dapi.v0.GetStatusResponse.Network.toObject(message.network, options); + return object; + }; + + /** + * Converts this GetStatusResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @instance + * @returns {Object.} JSON object + */ + GetStatusResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + GetStatusResponse.Version = (function() { + + /** + * Properties of a Version. + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @interface IVersion + * @property {number|null} [protocol] Version protocol + * @property {number|null} [software] Version software + * @property {string|null} [agent] Version agent + */ + + /** + * Constructs a new Version. + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @classdesc Represents a Version. + * @implements IVersion + * @constructor + * @param {org.dash.platform.dapi.v0.GetStatusResponse.IVersion=} [properties] Properties to set + */ + function Version(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Version protocol. + * @member {number} protocol + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Version + * @instance + */ + Version.prototype.protocol = 0; + + /** + * Version software. + * @member {number} software + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Version + * @instance + */ + Version.prototype.software = 0; + + /** + * Version agent. + * @member {string} agent + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Version + * @instance + */ + Version.prototype.agent = ""; + + /** + * Creates a new Version instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Version + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.IVersion=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Version} Version instance + */ + Version.create = function create(properties) { + return new Version(properties); + }; + + /** + * Encodes the specified Version message. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.Version.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Version + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.IVersion} message Version message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Version.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.protocol != null && Object.hasOwnProperty.call(message, "protocol")) + writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.protocol); + if (message.software != null && Object.hasOwnProperty.call(message, "software")) + writer.uint32(/* id 2, wireType 0 =*/16).uint32(message.software); + if (message.agent != null && Object.hasOwnProperty.call(message, "agent")) + writer.uint32(/* id 3, wireType 2 =*/26).string(message.agent); + return writer; + }; + + /** + * Encodes the specified Version message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.Version.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Version + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.IVersion} message Version message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Version.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Version message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Version + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Version} Version + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Version.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetStatusResponse.Version(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.protocol = reader.uint32(); + break; + case 2: + message.software = reader.uint32(); + break; + case 3: + message.agent = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Version message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Version + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Version} Version + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Version.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Version message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Version + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Version.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.protocol != null && message.hasOwnProperty("protocol")) + if (!$util.isInteger(message.protocol)) + return "protocol: integer expected"; + if (message.software != null && message.hasOwnProperty("software")) + if (!$util.isInteger(message.software)) + return "software: integer expected"; + if (message.agent != null && message.hasOwnProperty("agent")) + if (!$util.isString(message.agent)) + return "agent: string expected"; + return null; + }; + + /** + * Creates a Version message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Version + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Version} Version + */ + Version.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetStatusResponse.Version) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetStatusResponse.Version(); + if (object.protocol != null) + message.protocol = object.protocol >>> 0; + if (object.software != null) + message.software = object.software >>> 0; + if (object.agent != null) + message.agent = String(object.agent); + return message; + }; + + /** + * Creates a plain object from a Version message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Version + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.Version} message Version + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Version.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.protocol = 0; + object.software = 0; + object.agent = ""; + } + if (message.protocol != null && message.hasOwnProperty("protocol")) + object.protocol = message.protocol; + if (message.software != null && message.hasOwnProperty("software")) + object.software = message.software; + if (message.agent != null && message.hasOwnProperty("agent")) + object.agent = message.agent; + return object; + }; + + /** + * Converts this Version to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Version + * @instance + * @returns {Object.} JSON object + */ + Version.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Version; + })(); + + GetStatusResponse.Time = (function() { + + /** + * Properties of a Time. + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @interface ITime + * @property {number|null} [now] Time now + * @property {number|null} [offset] Time offset + * @property {number|null} [median] Time median + */ + + /** + * Constructs a new Time. + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @classdesc Represents a Time. + * @implements ITime + * @constructor + * @param {org.dash.platform.dapi.v0.GetStatusResponse.ITime=} [properties] Properties to set + */ + function Time(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Time now. + * @member {number} now + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Time + * @instance + */ + Time.prototype.now = 0; + + /** + * Time offset. + * @member {number} offset + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Time + * @instance + */ + Time.prototype.offset = 0; + + /** + * Time median. + * @member {number} median + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Time + * @instance + */ + Time.prototype.median = 0; + + /** + * Creates a new Time instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Time + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.ITime=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Time} Time instance + */ + Time.create = function create(properties) { + return new Time(properties); + }; + + /** + * Encodes the specified Time message. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.Time.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Time + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.ITime} message Time message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Time.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.now != null && Object.hasOwnProperty.call(message, "now")) + writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.now); + if (message.offset != null && Object.hasOwnProperty.call(message, "offset")) + writer.uint32(/* id 2, wireType 0 =*/16).int32(message.offset); + if (message.median != null && Object.hasOwnProperty.call(message, "median")) + writer.uint32(/* id 3, wireType 0 =*/24).uint32(message.median); + return writer; + }; + + /** + * Encodes the specified Time message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.Time.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Time + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.ITime} message Time message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Time.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Time message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Time + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Time} Time + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Time.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetStatusResponse.Time(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.now = reader.uint32(); + break; + case 2: + message.offset = reader.int32(); + break; + case 3: + message.median = reader.uint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Time message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Time + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Time} Time + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Time.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Time message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Time + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Time.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.now != null && message.hasOwnProperty("now")) + if (!$util.isInteger(message.now)) + return "now: integer expected"; + if (message.offset != null && message.hasOwnProperty("offset")) + if (!$util.isInteger(message.offset)) + return "offset: integer expected"; + if (message.median != null && message.hasOwnProperty("median")) + if (!$util.isInteger(message.median)) + return "median: integer expected"; + return null; + }; + + /** + * Creates a Time message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Time + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Time} Time + */ + Time.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetStatusResponse.Time) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetStatusResponse.Time(); + if (object.now != null) + message.now = object.now >>> 0; + if (object.offset != null) + message.offset = object.offset | 0; + if (object.median != null) + message.median = object.median >>> 0; + return message; + }; + + /** + * Creates a plain object from a Time message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Time + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.Time} message Time + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Time.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.now = 0; + object.offset = 0; + object.median = 0; + } + if (message.now != null && message.hasOwnProperty("now")) + object.now = message.now; + if (message.offset != null && message.hasOwnProperty("offset")) + object.offset = message.offset; + if (message.median != null && message.hasOwnProperty("median")) + object.median = message.median; + return object; + }; + + /** + * Converts this Time to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Time + * @instance + * @returns {Object.} JSON object + */ + Time.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Time; + })(); + + /** + * Status enum. + * @name org.dash.platform.dapi.v0.GetStatusResponse.Status + * @enum {number} + * @property {number} NOT_STARTED=0 NOT_STARTED value + * @property {number} SYNCING=1 SYNCING value + * @property {number} READY=2 READY value + * @property {number} ERROR=3 ERROR value + */ + GetStatusResponse.Status = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "NOT_STARTED"] = 0; + values[valuesById[1] = "SYNCING"] = 1; + values[valuesById[2] = "READY"] = 2; + values[valuesById[3] = "ERROR"] = 3; + return values; + })(); + + GetStatusResponse.Chain = (function() { + + /** + * Properties of a Chain. + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @interface IChain + * @property {string|null} [name] Chain name + * @property {number|null} [headersCount] Chain headersCount + * @property {number|null} [blocksCount] Chain blocksCount + * @property {Uint8Array|null} [bestBlockHash] Chain bestBlockHash + * @property {number|null} [difficulty] Chain difficulty + * @property {Uint8Array|null} [chainWork] Chain chainWork + * @property {boolean|null} [isSynced] Chain isSynced + * @property {number|null} [syncProgress] Chain syncProgress + */ + + /** + * Constructs a new Chain. + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @classdesc Represents a Chain. + * @implements IChain + * @constructor + * @param {org.dash.platform.dapi.v0.GetStatusResponse.IChain=} [properties] Properties to set + */ + function Chain(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Chain name. + * @member {string} name + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @instance + */ + Chain.prototype.name = ""; + + /** + * Chain headersCount. + * @member {number} headersCount + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @instance + */ + Chain.prototype.headersCount = 0; + + /** + * Chain blocksCount. + * @member {number} blocksCount + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @instance + */ + Chain.prototype.blocksCount = 0; + + /** + * Chain bestBlockHash. + * @member {Uint8Array} bestBlockHash + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @instance + */ + Chain.prototype.bestBlockHash = $util.newBuffer([]); + + /** + * Chain difficulty. + * @member {number} difficulty + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @instance + */ + Chain.prototype.difficulty = 0; + + /** + * Chain chainWork. + * @member {Uint8Array} chainWork + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @instance + */ + Chain.prototype.chainWork = $util.newBuffer([]); + + /** + * Chain isSynced. + * @member {boolean} isSynced + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @instance + */ + Chain.prototype.isSynced = false; + + /** + * Chain syncProgress. + * @member {number} syncProgress + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @instance + */ + Chain.prototype.syncProgress = 0; + + /** + * Creates a new Chain instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.IChain=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Chain} Chain instance + */ + Chain.create = function create(properties) { + return new Chain(properties); + }; + + /** + * Encodes the specified Chain message. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.Chain.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.IChain} message Chain message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Chain.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.name != null && Object.hasOwnProperty.call(message, "name")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.name); + if (message.headersCount != null && Object.hasOwnProperty.call(message, "headersCount")) + writer.uint32(/* id 2, wireType 0 =*/16).uint32(message.headersCount); + if (message.blocksCount != null && Object.hasOwnProperty.call(message, "blocksCount")) + writer.uint32(/* id 3, wireType 0 =*/24).uint32(message.blocksCount); + if (message.bestBlockHash != null && Object.hasOwnProperty.call(message, "bestBlockHash")) + writer.uint32(/* id 4, wireType 2 =*/34).bytes(message.bestBlockHash); + if (message.difficulty != null && Object.hasOwnProperty.call(message, "difficulty")) + writer.uint32(/* id 5, wireType 1 =*/41).double(message.difficulty); + if (message.chainWork != null && Object.hasOwnProperty.call(message, "chainWork")) + writer.uint32(/* id 6, wireType 2 =*/50).bytes(message.chainWork); + if (message.isSynced != null && Object.hasOwnProperty.call(message, "isSynced")) + writer.uint32(/* id 7, wireType 0 =*/56).bool(message.isSynced); + if (message.syncProgress != null && Object.hasOwnProperty.call(message, "syncProgress")) + writer.uint32(/* id 8, wireType 1 =*/65).double(message.syncProgress); + return writer; + }; + + /** + * Encodes the specified Chain message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.Chain.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.IChain} message Chain message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Chain.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Chain message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Chain} Chain + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Chain.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetStatusResponse.Chain(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.name = reader.string(); + break; + case 2: + message.headersCount = reader.uint32(); + break; + case 3: + message.blocksCount = reader.uint32(); + break; + case 4: + message.bestBlockHash = reader.bytes(); + break; + case 5: + message.difficulty = reader.double(); + break; + case 6: + message.chainWork = reader.bytes(); + break; + case 7: + message.isSynced = reader.bool(); + break; + case 8: + message.syncProgress = reader.double(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Chain message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Chain} Chain + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Chain.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Chain message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Chain.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.name != null && message.hasOwnProperty("name")) + if (!$util.isString(message.name)) + return "name: string expected"; + if (message.headersCount != null && message.hasOwnProperty("headersCount")) + if (!$util.isInteger(message.headersCount)) + return "headersCount: integer expected"; + if (message.blocksCount != null && message.hasOwnProperty("blocksCount")) + if (!$util.isInteger(message.blocksCount)) + return "blocksCount: integer expected"; + if (message.bestBlockHash != null && message.hasOwnProperty("bestBlockHash")) + if (!(message.bestBlockHash && typeof message.bestBlockHash.length === "number" || $util.isString(message.bestBlockHash))) + return "bestBlockHash: buffer expected"; + if (message.difficulty != null && message.hasOwnProperty("difficulty")) + if (typeof message.difficulty !== "number") + return "difficulty: number expected"; + if (message.chainWork != null && message.hasOwnProperty("chainWork")) + if (!(message.chainWork && typeof message.chainWork.length === "number" || $util.isString(message.chainWork))) + return "chainWork: buffer expected"; + if (message.isSynced != null && message.hasOwnProperty("isSynced")) + if (typeof message.isSynced !== "boolean") + return "isSynced: boolean expected"; + if (message.syncProgress != null && message.hasOwnProperty("syncProgress")) + if (typeof message.syncProgress !== "number") + return "syncProgress: number expected"; + return null; + }; + + /** + * Creates a Chain message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Chain} Chain + */ + Chain.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetStatusResponse.Chain) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetStatusResponse.Chain(); + if (object.name != null) + message.name = String(object.name); + if (object.headersCount != null) + message.headersCount = object.headersCount >>> 0; + if (object.blocksCount != null) + message.blocksCount = object.blocksCount >>> 0; + if (object.bestBlockHash != null) + if (typeof object.bestBlockHash === "string") + $util.base64.decode(object.bestBlockHash, message.bestBlockHash = $util.newBuffer($util.base64.length(object.bestBlockHash)), 0); + else if (object.bestBlockHash.length >= 0) + message.bestBlockHash = object.bestBlockHash; + if (object.difficulty != null) + message.difficulty = Number(object.difficulty); + if (object.chainWork != null) + if (typeof object.chainWork === "string") + $util.base64.decode(object.chainWork, message.chainWork = $util.newBuffer($util.base64.length(object.chainWork)), 0); + else if (object.chainWork.length >= 0) + message.chainWork = object.chainWork; + if (object.isSynced != null) + message.isSynced = Boolean(object.isSynced); + if (object.syncProgress != null) + message.syncProgress = Number(object.syncProgress); + return message; + }; + + /** + * Creates a plain object from a Chain message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.Chain} message Chain + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Chain.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.name = ""; + object.headersCount = 0; + object.blocksCount = 0; + if (options.bytes === String) + object.bestBlockHash = ""; + else { + object.bestBlockHash = []; + if (options.bytes !== Array) + object.bestBlockHash = $util.newBuffer(object.bestBlockHash); + } + object.difficulty = 0; + if (options.bytes === String) + object.chainWork = ""; + else { + object.chainWork = []; + if (options.bytes !== Array) + object.chainWork = $util.newBuffer(object.chainWork); + } + object.isSynced = false; + object.syncProgress = 0; + } + if (message.name != null && message.hasOwnProperty("name")) + object.name = message.name; + if (message.headersCount != null && message.hasOwnProperty("headersCount")) + object.headersCount = message.headersCount; + if (message.blocksCount != null && message.hasOwnProperty("blocksCount")) + object.blocksCount = message.blocksCount; + if (message.bestBlockHash != null && message.hasOwnProperty("bestBlockHash")) + object.bestBlockHash = options.bytes === String ? $util.base64.encode(message.bestBlockHash, 0, message.bestBlockHash.length) : options.bytes === Array ? Array.prototype.slice.call(message.bestBlockHash) : message.bestBlockHash; + if (message.difficulty != null && message.hasOwnProperty("difficulty")) + object.difficulty = options.json && !isFinite(message.difficulty) ? String(message.difficulty) : message.difficulty; + if (message.chainWork != null && message.hasOwnProperty("chainWork")) + object.chainWork = options.bytes === String ? $util.base64.encode(message.chainWork, 0, message.chainWork.length) : options.bytes === Array ? Array.prototype.slice.call(message.chainWork) : message.chainWork; + if (message.isSynced != null && message.hasOwnProperty("isSynced")) + object.isSynced = message.isSynced; + if (message.syncProgress != null && message.hasOwnProperty("syncProgress")) + object.syncProgress = options.json && !isFinite(message.syncProgress) ? String(message.syncProgress) : message.syncProgress; + return object; + }; + + /** + * Converts this Chain to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Chain + * @instance + * @returns {Object.} JSON object + */ + Chain.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Chain; + })(); + + GetStatusResponse.Masternode = (function() { + + /** + * Properties of a Masternode. + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @interface IMasternode + * @property {org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status|null} [status] Masternode status + * @property {Uint8Array|null} [proTxHash] Masternode proTxHash + * @property {number|null} [posePenalty] Masternode posePenalty + * @property {boolean|null} [isSynced] Masternode isSynced + * @property {number|null} [syncProgress] Masternode syncProgress + */ + + /** + * Constructs a new Masternode. + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @classdesc Represents a Masternode. + * @implements IMasternode + * @constructor + * @param {org.dash.platform.dapi.v0.GetStatusResponse.IMasternode=} [properties] Properties to set + */ + function Masternode(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Masternode status. + * @member {org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status} status + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @instance + */ + Masternode.prototype.status = 0; + + /** + * Masternode proTxHash. + * @member {Uint8Array} proTxHash + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @instance + */ + Masternode.prototype.proTxHash = $util.newBuffer([]); + + /** + * Masternode posePenalty. + * @member {number} posePenalty + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @instance + */ + Masternode.prototype.posePenalty = 0; + + /** + * Masternode isSynced. + * @member {boolean} isSynced + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @instance + */ + Masternode.prototype.isSynced = false; + + /** + * Masternode syncProgress. + * @member {number} syncProgress + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @instance + */ + Masternode.prototype.syncProgress = 0; + + /** + * Creates a new Masternode instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.IMasternode=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Masternode} Masternode instance + */ + Masternode.create = function create(properties) { + return new Masternode(properties); + }; + + /** + * Encodes the specified Masternode message. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.Masternode.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.IMasternode} message Masternode message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Masternode.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.status != null && Object.hasOwnProperty.call(message, "status")) + writer.uint32(/* id 1, wireType 0 =*/8).int32(message.status); + if (message.proTxHash != null && Object.hasOwnProperty.call(message, "proTxHash")) + writer.uint32(/* id 2, wireType 2 =*/18).bytes(message.proTxHash); + if (message.posePenalty != null && Object.hasOwnProperty.call(message, "posePenalty")) + writer.uint32(/* id 3, wireType 0 =*/24).uint32(message.posePenalty); + if (message.isSynced != null && Object.hasOwnProperty.call(message, "isSynced")) + writer.uint32(/* id 4, wireType 0 =*/32).bool(message.isSynced); + if (message.syncProgress != null && Object.hasOwnProperty.call(message, "syncProgress")) + writer.uint32(/* id 5, wireType 1 =*/41).double(message.syncProgress); + return writer; + }; + + /** + * Encodes the specified Masternode message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.Masternode.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.IMasternode} message Masternode message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Masternode.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Masternode message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Masternode} Masternode + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Masternode.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetStatusResponse.Masternode(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.status = reader.int32(); + break; + case 2: + message.proTxHash = reader.bytes(); + break; + case 3: + message.posePenalty = reader.uint32(); + break; + case 4: + message.isSynced = reader.bool(); + break; + case 5: + message.syncProgress = reader.double(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Masternode message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Masternode} Masternode + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Masternode.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Masternode message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Masternode.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.status != null && message.hasOwnProperty("status")) + switch (message.status) { + default: + return "status: enum value expected"; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + break; + } + if (message.proTxHash != null && message.hasOwnProperty("proTxHash")) + if (!(message.proTxHash && typeof message.proTxHash.length === "number" || $util.isString(message.proTxHash))) + return "proTxHash: buffer expected"; + if (message.posePenalty != null && message.hasOwnProperty("posePenalty")) + if (!$util.isInteger(message.posePenalty)) + return "posePenalty: integer expected"; + if (message.isSynced != null && message.hasOwnProperty("isSynced")) + if (typeof message.isSynced !== "boolean") + return "isSynced: boolean expected"; + if (message.syncProgress != null && message.hasOwnProperty("syncProgress")) + if (typeof message.syncProgress !== "number") + return "syncProgress: number expected"; + return null; + }; + + /** + * Creates a Masternode message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Masternode} Masternode + */ + Masternode.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetStatusResponse.Masternode) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetStatusResponse.Masternode(); + switch (object.status) { + case "UNKNOWN": + case 0: + message.status = 0; + break; + case "WAITING_FOR_PROTX": + case 1: + message.status = 1; + break; + case "POSE_BANNED": + case 2: + message.status = 2; + break; + case "REMOVED": + case 3: + message.status = 3; + break; + case "OPERATOR_KEY_CHANGED": + case 4: + message.status = 4; + break; + case "PROTX_IP_CHANGED": + case 5: + message.status = 5; + break; + case "READY": + case 6: + message.status = 6; + break; + case "ERROR": + case 7: + message.status = 7; + break; + } + if (object.proTxHash != null) + if (typeof object.proTxHash === "string") + $util.base64.decode(object.proTxHash, message.proTxHash = $util.newBuffer($util.base64.length(object.proTxHash)), 0); + else if (object.proTxHash.length >= 0) + message.proTxHash = object.proTxHash; + if (object.posePenalty != null) + message.posePenalty = object.posePenalty >>> 0; + if (object.isSynced != null) + message.isSynced = Boolean(object.isSynced); + if (object.syncProgress != null) + message.syncProgress = Number(object.syncProgress); + return message; + }; + + /** + * Creates a plain object from a Masternode message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.Masternode} message Masternode + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Masternode.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.status = options.enums === String ? "UNKNOWN" : 0; + if (options.bytes === String) + object.proTxHash = ""; + else { + object.proTxHash = []; + if (options.bytes !== Array) + object.proTxHash = $util.newBuffer(object.proTxHash); + } + object.posePenalty = 0; + object.isSynced = false; + object.syncProgress = 0; + } + if (message.status != null && message.hasOwnProperty("status")) + object.status = options.enums === String ? $root.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status[message.status] : message.status; + if (message.proTxHash != null && message.hasOwnProperty("proTxHash")) + object.proTxHash = options.bytes === String ? $util.base64.encode(message.proTxHash, 0, message.proTxHash.length) : options.bytes === Array ? Array.prototype.slice.call(message.proTxHash) : message.proTxHash; + if (message.posePenalty != null && message.hasOwnProperty("posePenalty")) + object.posePenalty = message.posePenalty; + if (message.isSynced != null && message.hasOwnProperty("isSynced")) + object.isSynced = message.isSynced; + if (message.syncProgress != null && message.hasOwnProperty("syncProgress")) + object.syncProgress = options.json && !isFinite(message.syncProgress) ? String(message.syncProgress) : message.syncProgress; + return object; + }; + + /** + * Converts this Masternode to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Masternode + * @instance + * @returns {Object.} JSON object + */ + Masternode.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Status enum. + * @name org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status + * @enum {number} + * @property {number} UNKNOWN=0 UNKNOWN value + * @property {number} WAITING_FOR_PROTX=1 WAITING_FOR_PROTX value + * @property {number} POSE_BANNED=2 POSE_BANNED value + * @property {number} REMOVED=3 REMOVED value + * @property {number} OPERATOR_KEY_CHANGED=4 OPERATOR_KEY_CHANGED value + * @property {number} PROTX_IP_CHANGED=5 PROTX_IP_CHANGED value + * @property {number} READY=6 READY value + * @property {number} ERROR=7 ERROR value + */ + Masternode.Status = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "UNKNOWN"] = 0; + values[valuesById[1] = "WAITING_FOR_PROTX"] = 1; + values[valuesById[2] = "POSE_BANNED"] = 2; + values[valuesById[3] = "REMOVED"] = 3; + values[valuesById[4] = "OPERATOR_KEY_CHANGED"] = 4; + values[valuesById[5] = "PROTX_IP_CHANGED"] = 5; + values[valuesById[6] = "READY"] = 6; + values[valuesById[7] = "ERROR"] = 7; + return values; + })(); + + return Masternode; + })(); + + GetStatusResponse.NetworkFee = (function() { + + /** + * Properties of a NetworkFee. + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @interface INetworkFee + * @property {number|null} [relay] NetworkFee relay + * @property {number|null} [incremental] NetworkFee incremental + */ + + /** + * Constructs a new NetworkFee. + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @classdesc Represents a NetworkFee. + * @implements INetworkFee + * @constructor + * @param {org.dash.platform.dapi.v0.GetStatusResponse.INetworkFee=} [properties] Properties to set + */ + function NetworkFee(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * NetworkFee relay. + * @member {number} relay + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee + * @instance + */ + NetworkFee.prototype.relay = 0; + + /** + * NetworkFee incremental. + * @member {number} incremental + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee + * @instance + */ + NetworkFee.prototype.incremental = 0; + + /** + * Creates a new NetworkFee instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.INetworkFee=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} NetworkFee instance + */ + NetworkFee.create = function create(properties) { + return new NetworkFee(properties); + }; + + /** + * Encodes the specified NetworkFee message. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.INetworkFee} message NetworkFee message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + NetworkFee.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.relay != null && Object.hasOwnProperty.call(message, "relay")) + writer.uint32(/* id 1, wireType 1 =*/9).double(message.relay); + if (message.incremental != null && Object.hasOwnProperty.call(message, "incremental")) + writer.uint32(/* id 2, wireType 1 =*/17).double(message.incremental); + return writer; + }; + + /** + * Encodes the specified NetworkFee message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.INetworkFee} message NetworkFee message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + NetworkFee.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a NetworkFee message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} NetworkFee + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + NetworkFee.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.relay = reader.double(); + break; + case 2: + message.incremental = reader.double(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a NetworkFee message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} NetworkFee + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + NetworkFee.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a NetworkFee message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + NetworkFee.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.relay != null && message.hasOwnProperty("relay")) + if (typeof message.relay !== "number") + return "relay: number expected"; + if (message.incremental != null && message.hasOwnProperty("incremental")) + if (typeof message.incremental !== "number") + return "incremental: number expected"; + return null; + }; + + /** + * Creates a NetworkFee message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} NetworkFee + */ + NetworkFee.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee(); + if (object.relay != null) + message.relay = Number(object.relay); + if (object.incremental != null) + message.incremental = Number(object.incremental); + return message; + }; + + /** + * Creates a plain object from a NetworkFee message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} message NetworkFee + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + NetworkFee.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.relay = 0; + object.incremental = 0; + } + if (message.relay != null && message.hasOwnProperty("relay")) + object.relay = options.json && !isFinite(message.relay) ? String(message.relay) : message.relay; + if (message.incremental != null && message.hasOwnProperty("incremental")) + object.incremental = options.json && !isFinite(message.incremental) ? String(message.incremental) : message.incremental; + return object; + }; + + /** + * Converts this NetworkFee to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee + * @instance + * @returns {Object.} JSON object + */ + NetworkFee.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return NetworkFee; + })(); + + GetStatusResponse.Network = (function() { + + /** + * Properties of a Network. + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @interface INetwork + * @property {number|null} [peersCount] Network peersCount + * @property {org.dash.platform.dapi.v0.GetStatusResponse.INetworkFee|null} [fee] Network fee + */ + + /** + * Constructs a new Network. + * @memberof org.dash.platform.dapi.v0.GetStatusResponse + * @classdesc Represents a Network. + * @implements INetwork + * @constructor + * @param {org.dash.platform.dapi.v0.GetStatusResponse.INetwork=} [properties] Properties to set + */ + function Network(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Network peersCount. + * @member {number} peersCount + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Network + * @instance + */ + Network.prototype.peersCount = 0; + + /** + * Network fee. + * @member {org.dash.platform.dapi.v0.GetStatusResponse.INetworkFee|null|undefined} fee + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Network + * @instance + */ + Network.prototype.fee = null; + + /** + * Creates a new Network instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Network + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.INetwork=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Network} Network instance + */ + Network.create = function create(properties) { + return new Network(properties); + }; + + /** + * Encodes the specified Network message. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.Network.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Network + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.INetwork} message Network message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Network.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.peersCount != null && Object.hasOwnProperty.call(message, "peersCount")) + writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.peersCount); + if (message.fee != null && Object.hasOwnProperty.call(message, "fee")) + $root.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.encode(message.fee, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified Network message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetStatusResponse.Network.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Network + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.INetwork} message Network message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Network.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Network message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Network + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Network} Network + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Network.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetStatusResponse.Network(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.peersCount = reader.uint32(); + break; + case 2: + message.fee = $root.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Network message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Network + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Network} Network + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Network.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Network message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Network + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Network.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.peersCount != null && message.hasOwnProperty("peersCount")) + if (!$util.isInteger(message.peersCount)) + return "peersCount: integer expected"; + if (message.fee != null && message.hasOwnProperty("fee")) { + var error = $root.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.verify(message.fee); + if (error) + return "fee." + error; + } + return null; + }; + + /** + * Creates a Network message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Network + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetStatusResponse.Network} Network + */ + Network.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetStatusResponse.Network) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetStatusResponse.Network(); + if (object.peersCount != null) + message.peersCount = object.peersCount >>> 0; + if (object.fee != null) { + if (typeof object.fee !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetStatusResponse.Network.fee: object expected"); + message.fee = $root.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.fromObject(object.fee); + } + return message; + }; + + /** + * Creates a plain object from a Network message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Network + * @static + * @param {org.dash.platform.dapi.v0.GetStatusResponse.Network} message Network + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Network.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.peersCount = 0; + object.fee = null; + } + if (message.peersCount != null && message.hasOwnProperty("peersCount")) + object.peersCount = message.peersCount; + if (message.fee != null && message.hasOwnProperty("fee")) + object.fee = $root.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.toObject(message.fee, options); + return object; + }; + + /** + * Converts this Network to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetStatusResponse.Network + * @instance + * @returns {Object.} JSON object + */ + Network.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Network; + })(); + + return GetStatusResponse; + })(); + + v0.GetBlockRequest = (function() { + + /** + * Properties of a GetBlockRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetBlockRequest + * @property {number|null} [height] GetBlockRequest height + * @property {string|null} [hash] GetBlockRequest hash + */ + + /** + * Constructs a new GetBlockRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetBlockRequest. + * @implements IGetBlockRequest + * @constructor + * @param {org.dash.platform.dapi.v0.IGetBlockRequest=} [properties] Properties to set + */ + function GetBlockRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetBlockRequest height. + * @member {number} height + * @memberof org.dash.platform.dapi.v0.GetBlockRequest + * @instance + */ + GetBlockRequest.prototype.height = 0; + + /** + * GetBlockRequest hash. + * @member {string} hash + * @memberof org.dash.platform.dapi.v0.GetBlockRequest + * @instance + */ + GetBlockRequest.prototype.hash = ""; + + // OneOf field names bound to virtual getters and setters + var $oneOfFields; + + /** + * GetBlockRequest block. + * @member {"height"|"hash"|undefined} block + * @memberof org.dash.platform.dapi.v0.GetBlockRequest + * @instance + */ + Object.defineProperty(GetBlockRequest.prototype, "block", { + get: $util.oneOfGetter($oneOfFields = ["height", "hash"]), + set: $util.oneOfSetter($oneOfFields) + }); + + /** + * Creates a new GetBlockRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetBlockRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetBlockRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetBlockRequest} GetBlockRequest instance + */ + GetBlockRequest.create = function create(properties) { + return new GetBlockRequest(properties); + }; + + /** + * Encodes the specified GetBlockRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.GetBlockRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetBlockRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetBlockRequest} message GetBlockRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetBlockRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.height != null && Object.hasOwnProperty.call(message, "height")) + writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.height); + if (message.hash != null && Object.hasOwnProperty.call(message, "hash")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.hash); + return writer; + }; + + /** + * Encodes the specified GetBlockRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetBlockRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetBlockRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetBlockRequest} message GetBlockRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetBlockRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetBlockRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetBlockRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetBlockRequest} GetBlockRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetBlockRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetBlockRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.height = reader.uint32(); + break; + case 2: + message.hash = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetBlockRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetBlockRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetBlockRequest} GetBlockRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetBlockRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetBlockRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetBlockRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetBlockRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.height != null && message.hasOwnProperty("height")) { + properties.block = 1; + if (!$util.isInteger(message.height)) + return "height: integer expected"; + } + if (message.hash != null && message.hasOwnProperty("hash")) { + if (properties.block === 1) + return "block: multiple values"; + properties.block = 1; + if (!$util.isString(message.hash)) + return "hash: string expected"; + } + return null; + }; + + /** + * Creates a GetBlockRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetBlockRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetBlockRequest} GetBlockRequest + */ + GetBlockRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetBlockRequest) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetBlockRequest(); + if (object.height != null) + message.height = object.height >>> 0; + if (object.hash != null) + message.hash = String(object.hash); + return message; + }; + + /** + * Creates a plain object from a GetBlockRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetBlockRequest + * @static + * @param {org.dash.platform.dapi.v0.GetBlockRequest} message GetBlockRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetBlockRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (message.height != null && message.hasOwnProperty("height")) { + object.height = message.height; + if (options.oneofs) + object.block = "height"; + } + if (message.hash != null && message.hasOwnProperty("hash")) { + object.hash = message.hash; + if (options.oneofs) + object.block = "hash"; + } + return object; + }; + + /** + * Converts this GetBlockRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetBlockRequest + * @instance + * @returns {Object.} JSON object + */ + GetBlockRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetBlockRequest; + })(); + + v0.GetBlockResponse = (function() { + + /** + * Properties of a GetBlockResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetBlockResponse + * @property {Uint8Array|null} [block] GetBlockResponse block + */ + + /** + * Constructs a new GetBlockResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetBlockResponse. + * @implements IGetBlockResponse + * @constructor + * @param {org.dash.platform.dapi.v0.IGetBlockResponse=} [properties] Properties to set + */ + function GetBlockResponse(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetBlockResponse block. + * @member {Uint8Array} block + * @memberof org.dash.platform.dapi.v0.GetBlockResponse + * @instance + */ + GetBlockResponse.prototype.block = $util.newBuffer([]); + + /** + * Creates a new GetBlockResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetBlockResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetBlockResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetBlockResponse} GetBlockResponse instance + */ + GetBlockResponse.create = function create(properties) { + return new GetBlockResponse(properties); + }; + + /** + * Encodes the specified GetBlockResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.GetBlockResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetBlockResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetBlockResponse} message GetBlockResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetBlockResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.block != null && Object.hasOwnProperty.call(message, "block")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.block); + return writer; + }; + + /** + * Encodes the specified GetBlockResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetBlockResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetBlockResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetBlockResponse} message GetBlockResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetBlockResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetBlockResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetBlockResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetBlockResponse} GetBlockResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetBlockResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetBlockResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.block = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetBlockResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetBlockResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetBlockResponse} GetBlockResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetBlockResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetBlockResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetBlockResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetBlockResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.block != null && message.hasOwnProperty("block")) + if (!(message.block && typeof message.block.length === "number" || $util.isString(message.block))) + return "block: buffer expected"; + return null; + }; + + /** + * Creates a GetBlockResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetBlockResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetBlockResponse} GetBlockResponse + */ + GetBlockResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetBlockResponse) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetBlockResponse(); + if (object.block != null) + if (typeof object.block === "string") + $util.base64.decode(object.block, message.block = $util.newBuffer($util.base64.length(object.block)), 0); + else if (object.block.length >= 0) + message.block = object.block; + return message; + }; + + /** + * Creates a plain object from a GetBlockResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetBlockResponse + * @static + * @param {org.dash.platform.dapi.v0.GetBlockResponse} message GetBlockResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetBlockResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + if (options.bytes === String) + object.block = ""; + else { + object.block = []; + if (options.bytes !== Array) + object.block = $util.newBuffer(object.block); + } + if (message.block != null && message.hasOwnProperty("block")) + object.block = options.bytes === String ? $util.base64.encode(message.block, 0, message.block.length) : options.bytes === Array ? Array.prototype.slice.call(message.block) : message.block; + return object; + }; + + /** + * Converts this GetBlockResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetBlockResponse + * @instance + * @returns {Object.} JSON object + */ + GetBlockResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetBlockResponse; + })(); + + v0.BroadcastTransactionRequest = (function() { + + /** + * Properties of a BroadcastTransactionRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface IBroadcastTransactionRequest + * @property {Uint8Array|null} [transaction] BroadcastTransactionRequest transaction + * @property {boolean|null} [allowHighFees] BroadcastTransactionRequest allowHighFees + * @property {boolean|null} [bypassLimits] BroadcastTransactionRequest bypassLimits + */ + + /** + * Constructs a new BroadcastTransactionRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a BroadcastTransactionRequest. + * @implements IBroadcastTransactionRequest + * @constructor + * @param {org.dash.platform.dapi.v0.IBroadcastTransactionRequest=} [properties] Properties to set + */ + function BroadcastTransactionRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * BroadcastTransactionRequest transaction. + * @member {Uint8Array} transaction + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionRequest + * @instance + */ + BroadcastTransactionRequest.prototype.transaction = $util.newBuffer([]); + + /** + * BroadcastTransactionRequest allowHighFees. + * @member {boolean} allowHighFees + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionRequest + * @instance + */ + BroadcastTransactionRequest.prototype.allowHighFees = false; + + /** + * BroadcastTransactionRequest bypassLimits. + * @member {boolean} bypassLimits + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionRequest + * @instance + */ + BroadcastTransactionRequest.prototype.bypassLimits = false; + + /** + * Creates a new BroadcastTransactionRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionRequest + * @static + * @param {org.dash.platform.dapi.v0.IBroadcastTransactionRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.BroadcastTransactionRequest} BroadcastTransactionRequest instance + */ + BroadcastTransactionRequest.create = function create(properties) { + return new BroadcastTransactionRequest(properties); + }; + + /** + * Encodes the specified BroadcastTransactionRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.BroadcastTransactionRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionRequest + * @static + * @param {org.dash.platform.dapi.v0.IBroadcastTransactionRequest} message BroadcastTransactionRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BroadcastTransactionRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.transaction != null && Object.hasOwnProperty.call(message, "transaction")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.transaction); + if (message.allowHighFees != null && Object.hasOwnProperty.call(message, "allowHighFees")) + writer.uint32(/* id 2, wireType 0 =*/16).bool(message.allowHighFees); + if (message.bypassLimits != null && Object.hasOwnProperty.call(message, "bypassLimits")) + writer.uint32(/* id 3, wireType 0 =*/24).bool(message.bypassLimits); + return writer; + }; + + /** + * Encodes the specified BroadcastTransactionRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.BroadcastTransactionRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionRequest + * @static + * @param {org.dash.platform.dapi.v0.IBroadcastTransactionRequest} message BroadcastTransactionRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BroadcastTransactionRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a BroadcastTransactionRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.BroadcastTransactionRequest} BroadcastTransactionRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BroadcastTransactionRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.BroadcastTransactionRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.transaction = reader.bytes(); + break; + case 2: + message.allowHighFees = reader.bool(); + break; + case 3: + message.bypassLimits = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a BroadcastTransactionRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.BroadcastTransactionRequest} BroadcastTransactionRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BroadcastTransactionRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a BroadcastTransactionRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + BroadcastTransactionRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.transaction != null && message.hasOwnProperty("transaction")) + if (!(message.transaction && typeof message.transaction.length === "number" || $util.isString(message.transaction))) + return "transaction: buffer expected"; + if (message.allowHighFees != null && message.hasOwnProperty("allowHighFees")) + if (typeof message.allowHighFees !== "boolean") + return "allowHighFees: boolean expected"; + if (message.bypassLimits != null && message.hasOwnProperty("bypassLimits")) + if (typeof message.bypassLimits !== "boolean") + return "bypassLimits: boolean expected"; + return null; + }; + + /** + * Creates a BroadcastTransactionRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.BroadcastTransactionRequest} BroadcastTransactionRequest + */ + BroadcastTransactionRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.BroadcastTransactionRequest) + return object; + var message = new $root.org.dash.platform.dapi.v0.BroadcastTransactionRequest(); + if (object.transaction != null) + if (typeof object.transaction === "string") + $util.base64.decode(object.transaction, message.transaction = $util.newBuffer($util.base64.length(object.transaction)), 0); + else if (object.transaction.length >= 0) + message.transaction = object.transaction; + if (object.allowHighFees != null) + message.allowHighFees = Boolean(object.allowHighFees); + if (object.bypassLimits != null) + message.bypassLimits = Boolean(object.bypassLimits); + return message; + }; + + /** + * Creates a plain object from a BroadcastTransactionRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionRequest + * @static + * @param {org.dash.platform.dapi.v0.BroadcastTransactionRequest} message BroadcastTransactionRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + BroadcastTransactionRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if (options.bytes === String) + object.transaction = ""; + else { + object.transaction = []; + if (options.bytes !== Array) + object.transaction = $util.newBuffer(object.transaction); + } + object.allowHighFees = false; + object.bypassLimits = false; + } + if (message.transaction != null && message.hasOwnProperty("transaction")) + object.transaction = options.bytes === String ? $util.base64.encode(message.transaction, 0, message.transaction.length) : options.bytes === Array ? Array.prototype.slice.call(message.transaction) : message.transaction; + if (message.allowHighFees != null && message.hasOwnProperty("allowHighFees")) + object.allowHighFees = message.allowHighFees; + if (message.bypassLimits != null && message.hasOwnProperty("bypassLimits")) + object.bypassLimits = message.bypassLimits; + return object; + }; + + /** + * Converts this BroadcastTransactionRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionRequest + * @instance + * @returns {Object.} JSON object + */ + BroadcastTransactionRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return BroadcastTransactionRequest; + })(); + + v0.BroadcastTransactionResponse = (function() { + + /** + * Properties of a BroadcastTransactionResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface IBroadcastTransactionResponse + * @property {string|null} [transactionId] BroadcastTransactionResponse transactionId + */ + + /** + * Constructs a new BroadcastTransactionResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a BroadcastTransactionResponse. + * @implements IBroadcastTransactionResponse + * @constructor + * @param {org.dash.platform.dapi.v0.IBroadcastTransactionResponse=} [properties] Properties to set + */ + function BroadcastTransactionResponse(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * BroadcastTransactionResponse transactionId. + * @member {string} transactionId + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionResponse + * @instance + */ + BroadcastTransactionResponse.prototype.transactionId = ""; + + /** + * Creates a new BroadcastTransactionResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionResponse + * @static + * @param {org.dash.platform.dapi.v0.IBroadcastTransactionResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.BroadcastTransactionResponse} BroadcastTransactionResponse instance + */ + BroadcastTransactionResponse.create = function create(properties) { + return new BroadcastTransactionResponse(properties); + }; + + /** + * Encodes the specified BroadcastTransactionResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.BroadcastTransactionResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionResponse + * @static + * @param {org.dash.platform.dapi.v0.IBroadcastTransactionResponse} message BroadcastTransactionResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BroadcastTransactionResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.transactionId != null && Object.hasOwnProperty.call(message, "transactionId")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.transactionId); + return writer; + }; + + /** + * Encodes the specified BroadcastTransactionResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.BroadcastTransactionResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionResponse + * @static + * @param {org.dash.platform.dapi.v0.IBroadcastTransactionResponse} message BroadcastTransactionResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BroadcastTransactionResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a BroadcastTransactionResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.BroadcastTransactionResponse} BroadcastTransactionResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BroadcastTransactionResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.BroadcastTransactionResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.transactionId = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a BroadcastTransactionResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.BroadcastTransactionResponse} BroadcastTransactionResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BroadcastTransactionResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a BroadcastTransactionResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + BroadcastTransactionResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.transactionId != null && message.hasOwnProperty("transactionId")) + if (!$util.isString(message.transactionId)) + return "transactionId: string expected"; + return null; + }; + + /** + * Creates a BroadcastTransactionResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.BroadcastTransactionResponse} BroadcastTransactionResponse + */ + BroadcastTransactionResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.BroadcastTransactionResponse) + return object; + var message = new $root.org.dash.platform.dapi.v0.BroadcastTransactionResponse(); + if (object.transactionId != null) + message.transactionId = String(object.transactionId); + return message; + }; + + /** + * Creates a plain object from a BroadcastTransactionResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionResponse + * @static + * @param {org.dash.platform.dapi.v0.BroadcastTransactionResponse} message BroadcastTransactionResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + BroadcastTransactionResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.transactionId = ""; + if (message.transactionId != null && message.hasOwnProperty("transactionId")) + object.transactionId = message.transactionId; + return object; + }; + + /** + * Converts this BroadcastTransactionResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.BroadcastTransactionResponse + * @instance + * @returns {Object.} JSON object + */ + BroadcastTransactionResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return BroadcastTransactionResponse; + })(); + + v0.GetTransactionRequest = (function() { + + /** + * Properties of a GetTransactionRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetTransactionRequest + * @property {string|null} [id] GetTransactionRequest id + */ + + /** + * Constructs a new GetTransactionRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetTransactionRequest. + * @implements IGetTransactionRequest + * @constructor + * @param {org.dash.platform.dapi.v0.IGetTransactionRequest=} [properties] Properties to set + */ + function GetTransactionRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetTransactionRequest id. + * @member {string} id + * @memberof org.dash.platform.dapi.v0.GetTransactionRequest + * @instance + */ + GetTransactionRequest.prototype.id = ""; + + /** + * Creates a new GetTransactionRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetTransactionRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetTransactionRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetTransactionRequest} GetTransactionRequest instance + */ + GetTransactionRequest.create = function create(properties) { + return new GetTransactionRequest(properties); + }; + + /** + * Encodes the specified GetTransactionRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.GetTransactionRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetTransactionRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetTransactionRequest} message GetTransactionRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetTransactionRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.id != null && Object.hasOwnProperty.call(message, "id")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.id); + return writer; + }; + + /** + * Encodes the specified GetTransactionRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetTransactionRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetTransactionRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetTransactionRequest} message GetTransactionRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetTransactionRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetTransactionRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetTransactionRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetTransactionRequest} GetTransactionRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetTransactionRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetTransactionRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetTransactionRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetTransactionRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetTransactionRequest} GetTransactionRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetTransactionRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetTransactionRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetTransactionRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetTransactionRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.id != null && message.hasOwnProperty("id")) + if (!$util.isString(message.id)) + return "id: string expected"; + return null; + }; + + /** + * Creates a GetTransactionRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetTransactionRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetTransactionRequest} GetTransactionRequest + */ + GetTransactionRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetTransactionRequest) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetTransactionRequest(); + if (object.id != null) + message.id = String(object.id); + return message; + }; + + /** + * Creates a plain object from a GetTransactionRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetTransactionRequest + * @static + * @param {org.dash.platform.dapi.v0.GetTransactionRequest} message GetTransactionRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetTransactionRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.id = ""; + if (message.id != null && message.hasOwnProperty("id")) + object.id = message.id; + return object; + }; + + /** + * Converts this GetTransactionRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetTransactionRequest + * @instance + * @returns {Object.} JSON object + */ + GetTransactionRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetTransactionRequest; + })(); + + v0.GetTransactionResponse = (function() { + + /** + * Properties of a GetTransactionResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetTransactionResponse + * @property {Uint8Array|null} [transaction] GetTransactionResponse transaction + * @property {Uint8Array|null} [blockHash] GetTransactionResponse blockHash + * @property {number|null} [height] GetTransactionResponse height + * @property {number|null} [confirmations] GetTransactionResponse confirmations + * @property {boolean|null} [isInstantLocked] GetTransactionResponse isInstantLocked + * @property {boolean|null} [isChainLocked] GetTransactionResponse isChainLocked + */ + + /** + * Constructs a new GetTransactionResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetTransactionResponse. + * @implements IGetTransactionResponse + * @constructor + * @param {org.dash.platform.dapi.v0.IGetTransactionResponse=} [properties] Properties to set + */ + function GetTransactionResponse(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetTransactionResponse transaction. + * @member {Uint8Array} transaction + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @instance + */ + GetTransactionResponse.prototype.transaction = $util.newBuffer([]); + + /** + * GetTransactionResponse blockHash. + * @member {Uint8Array} blockHash + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @instance + */ + GetTransactionResponse.prototype.blockHash = $util.newBuffer([]); + + /** + * GetTransactionResponse height. + * @member {number} height + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @instance + */ + GetTransactionResponse.prototype.height = 0; + + /** + * GetTransactionResponse confirmations. + * @member {number} confirmations + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @instance + */ + GetTransactionResponse.prototype.confirmations = 0; + + /** + * GetTransactionResponse isInstantLocked. + * @member {boolean} isInstantLocked + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @instance + */ + GetTransactionResponse.prototype.isInstantLocked = false; + + /** + * GetTransactionResponse isChainLocked. + * @member {boolean} isChainLocked + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @instance + */ + GetTransactionResponse.prototype.isChainLocked = false; + + /** + * Creates a new GetTransactionResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetTransactionResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetTransactionResponse} GetTransactionResponse instance + */ + GetTransactionResponse.create = function create(properties) { + return new GetTransactionResponse(properties); + }; + + /** + * Encodes the specified GetTransactionResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.GetTransactionResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetTransactionResponse} message GetTransactionResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetTransactionResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.transaction != null && Object.hasOwnProperty.call(message, "transaction")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.transaction); + if (message.blockHash != null && Object.hasOwnProperty.call(message, "blockHash")) + writer.uint32(/* id 2, wireType 2 =*/18).bytes(message.blockHash); + if (message.height != null && Object.hasOwnProperty.call(message, "height")) + writer.uint32(/* id 3, wireType 0 =*/24).uint32(message.height); + if (message.confirmations != null && Object.hasOwnProperty.call(message, "confirmations")) + writer.uint32(/* id 4, wireType 0 =*/32).uint32(message.confirmations); + if (message.isInstantLocked != null && Object.hasOwnProperty.call(message, "isInstantLocked")) + writer.uint32(/* id 5, wireType 0 =*/40).bool(message.isInstantLocked); + if (message.isChainLocked != null && Object.hasOwnProperty.call(message, "isChainLocked")) + writer.uint32(/* id 6, wireType 0 =*/48).bool(message.isChainLocked); + return writer; + }; + + /** + * Encodes the specified GetTransactionResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetTransactionResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetTransactionResponse} message GetTransactionResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetTransactionResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetTransactionResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetTransactionResponse} GetTransactionResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetTransactionResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetTransactionResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.transaction = reader.bytes(); + break; + case 2: + message.blockHash = reader.bytes(); + break; + case 3: + message.height = reader.uint32(); + break; + case 4: + message.confirmations = reader.uint32(); + break; + case 5: + message.isInstantLocked = reader.bool(); + break; + case 6: + message.isChainLocked = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetTransactionResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetTransactionResponse} GetTransactionResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetTransactionResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetTransactionResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetTransactionResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.transaction != null && message.hasOwnProperty("transaction")) + if (!(message.transaction && typeof message.transaction.length === "number" || $util.isString(message.transaction))) + return "transaction: buffer expected"; + if (message.blockHash != null && message.hasOwnProperty("blockHash")) + if (!(message.blockHash && typeof message.blockHash.length === "number" || $util.isString(message.blockHash))) + return "blockHash: buffer expected"; + if (message.height != null && message.hasOwnProperty("height")) + if (!$util.isInteger(message.height)) + return "height: integer expected"; + if (message.confirmations != null && message.hasOwnProperty("confirmations")) + if (!$util.isInteger(message.confirmations)) + return "confirmations: integer expected"; + if (message.isInstantLocked != null && message.hasOwnProperty("isInstantLocked")) + if (typeof message.isInstantLocked !== "boolean") + return "isInstantLocked: boolean expected"; + if (message.isChainLocked != null && message.hasOwnProperty("isChainLocked")) + if (typeof message.isChainLocked !== "boolean") + return "isChainLocked: boolean expected"; + return null; + }; + + /** + * Creates a GetTransactionResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetTransactionResponse} GetTransactionResponse + */ + GetTransactionResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetTransactionResponse) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetTransactionResponse(); + if (object.transaction != null) + if (typeof object.transaction === "string") + $util.base64.decode(object.transaction, message.transaction = $util.newBuffer($util.base64.length(object.transaction)), 0); + else if (object.transaction.length >= 0) + message.transaction = object.transaction; + if (object.blockHash != null) + if (typeof object.blockHash === "string") + $util.base64.decode(object.blockHash, message.blockHash = $util.newBuffer($util.base64.length(object.blockHash)), 0); + else if (object.blockHash.length >= 0) + message.blockHash = object.blockHash; + if (object.height != null) + message.height = object.height >>> 0; + if (object.confirmations != null) + message.confirmations = object.confirmations >>> 0; + if (object.isInstantLocked != null) + message.isInstantLocked = Boolean(object.isInstantLocked); + if (object.isChainLocked != null) + message.isChainLocked = Boolean(object.isChainLocked); + return message; + }; + + /** + * Creates a plain object from a GetTransactionResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @static + * @param {org.dash.platform.dapi.v0.GetTransactionResponse} message GetTransactionResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetTransactionResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if (options.bytes === String) + object.transaction = ""; + else { + object.transaction = []; + if (options.bytes !== Array) + object.transaction = $util.newBuffer(object.transaction); + } + if (options.bytes === String) + object.blockHash = ""; + else { + object.blockHash = []; + if (options.bytes !== Array) + object.blockHash = $util.newBuffer(object.blockHash); + } + object.height = 0; + object.confirmations = 0; + object.isInstantLocked = false; + object.isChainLocked = false; + } + if (message.transaction != null && message.hasOwnProperty("transaction")) + object.transaction = options.bytes === String ? $util.base64.encode(message.transaction, 0, message.transaction.length) : options.bytes === Array ? Array.prototype.slice.call(message.transaction) : message.transaction; + if (message.blockHash != null && message.hasOwnProperty("blockHash")) + object.blockHash = options.bytes === String ? $util.base64.encode(message.blockHash, 0, message.blockHash.length) : options.bytes === Array ? Array.prototype.slice.call(message.blockHash) : message.blockHash; + if (message.height != null && message.hasOwnProperty("height")) + object.height = message.height; + if (message.confirmations != null && message.hasOwnProperty("confirmations")) + object.confirmations = message.confirmations; + if (message.isInstantLocked != null && message.hasOwnProperty("isInstantLocked")) + object.isInstantLocked = message.isInstantLocked; + if (message.isChainLocked != null && message.hasOwnProperty("isChainLocked")) + object.isChainLocked = message.isChainLocked; + return object; + }; + + /** + * Converts this GetTransactionResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetTransactionResponse + * @instance + * @returns {Object.} JSON object + */ + GetTransactionResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetTransactionResponse; + })(); + + v0.BlockHeadersWithChainLocksRequest = (function() { + + /** + * Properties of a BlockHeadersWithChainLocksRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface IBlockHeadersWithChainLocksRequest + * @property {Uint8Array|null} [fromBlockHash] BlockHeadersWithChainLocksRequest fromBlockHash + * @property {number|null} [fromBlockHeight] BlockHeadersWithChainLocksRequest fromBlockHeight + * @property {number|null} [count] BlockHeadersWithChainLocksRequest count + */ + + /** + * Constructs a new BlockHeadersWithChainLocksRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a BlockHeadersWithChainLocksRequest. + * @implements IBlockHeadersWithChainLocksRequest + * @constructor + * @param {org.dash.platform.dapi.v0.IBlockHeadersWithChainLocksRequest=} [properties] Properties to set + */ + function BlockHeadersWithChainLocksRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * BlockHeadersWithChainLocksRequest fromBlockHash. + * @member {Uint8Array} fromBlockHash + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest + * @instance + */ + BlockHeadersWithChainLocksRequest.prototype.fromBlockHash = $util.newBuffer([]); + + /** + * BlockHeadersWithChainLocksRequest fromBlockHeight. + * @member {number} fromBlockHeight + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest + * @instance + */ + BlockHeadersWithChainLocksRequest.prototype.fromBlockHeight = 0; + + /** + * BlockHeadersWithChainLocksRequest count. + * @member {number} count + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest + * @instance + */ + BlockHeadersWithChainLocksRequest.prototype.count = 0; + + // OneOf field names bound to virtual getters and setters + var $oneOfFields; + + /** + * BlockHeadersWithChainLocksRequest fromBlock. + * @member {"fromBlockHash"|"fromBlockHeight"|undefined} fromBlock + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest + * @instance + */ + Object.defineProperty(BlockHeadersWithChainLocksRequest.prototype, "fromBlock", { + get: $util.oneOfGetter($oneOfFields = ["fromBlockHash", "fromBlockHeight"]), + set: $util.oneOfSetter($oneOfFields) + }); + + /** + * Creates a new BlockHeadersWithChainLocksRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest + * @static + * @param {org.dash.platform.dapi.v0.IBlockHeadersWithChainLocksRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} BlockHeadersWithChainLocksRequest instance + */ + BlockHeadersWithChainLocksRequest.create = function create(properties) { + return new BlockHeadersWithChainLocksRequest(properties); + }; + + /** + * Encodes the specified BlockHeadersWithChainLocksRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest + * @static + * @param {org.dash.platform.dapi.v0.IBlockHeadersWithChainLocksRequest} message BlockHeadersWithChainLocksRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BlockHeadersWithChainLocksRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.fromBlockHash != null && Object.hasOwnProperty.call(message, "fromBlockHash")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.fromBlockHash); + if (message.fromBlockHeight != null && Object.hasOwnProperty.call(message, "fromBlockHeight")) + writer.uint32(/* id 2, wireType 0 =*/16).uint32(message.fromBlockHeight); + if (message.count != null && Object.hasOwnProperty.call(message, "count")) + writer.uint32(/* id 3, wireType 0 =*/24).uint32(message.count); + return writer; + }; + + /** + * Encodes the specified BlockHeadersWithChainLocksRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest + * @static + * @param {org.dash.platform.dapi.v0.IBlockHeadersWithChainLocksRequest} message BlockHeadersWithChainLocksRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BlockHeadersWithChainLocksRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a BlockHeadersWithChainLocksRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} BlockHeadersWithChainLocksRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BlockHeadersWithChainLocksRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.fromBlockHash = reader.bytes(); + break; + case 2: + message.fromBlockHeight = reader.uint32(); + break; + case 3: + message.count = reader.uint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a BlockHeadersWithChainLocksRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} BlockHeadersWithChainLocksRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BlockHeadersWithChainLocksRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a BlockHeadersWithChainLocksRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + BlockHeadersWithChainLocksRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.fromBlockHash != null && message.hasOwnProperty("fromBlockHash")) { + properties.fromBlock = 1; + if (!(message.fromBlockHash && typeof message.fromBlockHash.length === "number" || $util.isString(message.fromBlockHash))) + return "fromBlockHash: buffer expected"; + } + if (message.fromBlockHeight != null && message.hasOwnProperty("fromBlockHeight")) { + if (properties.fromBlock === 1) + return "fromBlock: multiple values"; + properties.fromBlock = 1; + if (!$util.isInteger(message.fromBlockHeight)) + return "fromBlockHeight: integer expected"; + } + if (message.count != null && message.hasOwnProperty("count")) + if (!$util.isInteger(message.count)) + return "count: integer expected"; + return null; + }; + + /** + * Creates a BlockHeadersWithChainLocksRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} BlockHeadersWithChainLocksRequest + */ + BlockHeadersWithChainLocksRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest) + return object; + var message = new $root.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest(); + if (object.fromBlockHash != null) + if (typeof object.fromBlockHash === "string") + $util.base64.decode(object.fromBlockHash, message.fromBlockHash = $util.newBuffer($util.base64.length(object.fromBlockHash)), 0); + else if (object.fromBlockHash.length >= 0) + message.fromBlockHash = object.fromBlockHash; + if (object.fromBlockHeight != null) + message.fromBlockHeight = object.fromBlockHeight >>> 0; + if (object.count != null) + message.count = object.count >>> 0; + return message; + }; + + /** + * Creates a plain object from a BlockHeadersWithChainLocksRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest + * @static + * @param {org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} message BlockHeadersWithChainLocksRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + BlockHeadersWithChainLocksRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.count = 0; + if (message.fromBlockHash != null && message.hasOwnProperty("fromBlockHash")) { + object.fromBlockHash = options.bytes === String ? $util.base64.encode(message.fromBlockHash, 0, message.fromBlockHash.length) : options.bytes === Array ? Array.prototype.slice.call(message.fromBlockHash) : message.fromBlockHash; + if (options.oneofs) + object.fromBlock = "fromBlockHash"; + } + if (message.fromBlockHeight != null && message.hasOwnProperty("fromBlockHeight")) { + object.fromBlockHeight = message.fromBlockHeight; + if (options.oneofs) + object.fromBlock = "fromBlockHeight"; + } + if (message.count != null && message.hasOwnProperty("count")) + object.count = message.count; + return object; + }; + + /** + * Converts this BlockHeadersWithChainLocksRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest + * @instance + * @returns {Object.} JSON object + */ + BlockHeadersWithChainLocksRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return BlockHeadersWithChainLocksRequest; + })(); + + v0.BlockHeadersWithChainLocksResponse = (function() { + + /** + * Properties of a BlockHeadersWithChainLocksResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface IBlockHeadersWithChainLocksResponse + * @property {org.dash.platform.dapi.v0.IBlockHeaders|null} [blockHeaders] BlockHeadersWithChainLocksResponse blockHeaders + * @property {Uint8Array|null} [chainLock] BlockHeadersWithChainLocksResponse chainLock + */ + + /** + * Constructs a new BlockHeadersWithChainLocksResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a BlockHeadersWithChainLocksResponse. + * @implements IBlockHeadersWithChainLocksResponse + * @constructor + * @param {org.dash.platform.dapi.v0.IBlockHeadersWithChainLocksResponse=} [properties] Properties to set + */ + function BlockHeadersWithChainLocksResponse(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * BlockHeadersWithChainLocksResponse blockHeaders. + * @member {org.dash.platform.dapi.v0.IBlockHeaders|null|undefined} blockHeaders + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse + * @instance + */ + BlockHeadersWithChainLocksResponse.prototype.blockHeaders = null; + + /** + * BlockHeadersWithChainLocksResponse chainLock. + * @member {Uint8Array} chainLock + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse + * @instance + */ + BlockHeadersWithChainLocksResponse.prototype.chainLock = $util.newBuffer([]); + + // OneOf field names bound to virtual getters and setters + var $oneOfFields; + + /** + * BlockHeadersWithChainLocksResponse responses. + * @member {"blockHeaders"|"chainLock"|undefined} responses + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse + * @instance + */ + Object.defineProperty(BlockHeadersWithChainLocksResponse.prototype, "responses", { + get: $util.oneOfGetter($oneOfFields = ["blockHeaders", "chainLock"]), + set: $util.oneOfSetter($oneOfFields) + }); + + /** + * Creates a new BlockHeadersWithChainLocksResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse + * @static + * @param {org.dash.platform.dapi.v0.IBlockHeadersWithChainLocksResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} BlockHeadersWithChainLocksResponse instance + */ + BlockHeadersWithChainLocksResponse.create = function create(properties) { + return new BlockHeadersWithChainLocksResponse(properties); + }; + + /** + * Encodes the specified BlockHeadersWithChainLocksResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse + * @static + * @param {org.dash.platform.dapi.v0.IBlockHeadersWithChainLocksResponse} message BlockHeadersWithChainLocksResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BlockHeadersWithChainLocksResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.blockHeaders != null && Object.hasOwnProperty.call(message, "blockHeaders")) + $root.org.dash.platform.dapi.v0.BlockHeaders.encode(message.blockHeaders, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.chainLock != null && Object.hasOwnProperty.call(message, "chainLock")) + writer.uint32(/* id 2, wireType 2 =*/18).bytes(message.chainLock); + return writer; + }; + + /** + * Encodes the specified BlockHeadersWithChainLocksResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse + * @static + * @param {org.dash.platform.dapi.v0.IBlockHeadersWithChainLocksResponse} message BlockHeadersWithChainLocksResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BlockHeadersWithChainLocksResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a BlockHeadersWithChainLocksResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} BlockHeadersWithChainLocksResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BlockHeadersWithChainLocksResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.blockHeaders = $root.org.dash.platform.dapi.v0.BlockHeaders.decode(reader, reader.uint32()); + break; + case 2: + message.chainLock = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a BlockHeadersWithChainLocksResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} BlockHeadersWithChainLocksResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BlockHeadersWithChainLocksResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a BlockHeadersWithChainLocksResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + BlockHeadersWithChainLocksResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.blockHeaders != null && message.hasOwnProperty("blockHeaders")) { + properties.responses = 1; + { + var error = $root.org.dash.platform.dapi.v0.BlockHeaders.verify(message.blockHeaders); + if (error) + return "blockHeaders." + error; + } + } + if (message.chainLock != null && message.hasOwnProperty("chainLock")) { + if (properties.responses === 1) + return "responses: multiple values"; + properties.responses = 1; + if (!(message.chainLock && typeof message.chainLock.length === "number" || $util.isString(message.chainLock))) + return "chainLock: buffer expected"; + } + return null; + }; + + /** + * Creates a BlockHeadersWithChainLocksResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} BlockHeadersWithChainLocksResponse + */ + BlockHeadersWithChainLocksResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse) + return object; + var message = new $root.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse(); + if (object.blockHeaders != null) { + if (typeof object.blockHeaders !== "object") + throw TypeError(".org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.blockHeaders: object expected"); + message.blockHeaders = $root.org.dash.platform.dapi.v0.BlockHeaders.fromObject(object.blockHeaders); + } + if (object.chainLock != null) + if (typeof object.chainLock === "string") + $util.base64.decode(object.chainLock, message.chainLock = $util.newBuffer($util.base64.length(object.chainLock)), 0); + else if (object.chainLock.length >= 0) + message.chainLock = object.chainLock; + return message; + }; + + /** + * Creates a plain object from a BlockHeadersWithChainLocksResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse + * @static + * @param {org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} message BlockHeadersWithChainLocksResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + BlockHeadersWithChainLocksResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (message.blockHeaders != null && message.hasOwnProperty("blockHeaders")) { + object.blockHeaders = $root.org.dash.platform.dapi.v0.BlockHeaders.toObject(message.blockHeaders, options); + if (options.oneofs) + object.responses = "blockHeaders"; + } + if (message.chainLock != null && message.hasOwnProperty("chainLock")) { + object.chainLock = options.bytes === String ? $util.base64.encode(message.chainLock, 0, message.chainLock.length) : options.bytes === Array ? Array.prototype.slice.call(message.chainLock) : message.chainLock; + if (options.oneofs) + object.responses = "chainLock"; + } + return object; + }; + + /** + * Converts this BlockHeadersWithChainLocksResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse + * @instance + * @returns {Object.} JSON object + */ + BlockHeadersWithChainLocksResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return BlockHeadersWithChainLocksResponse; + })(); + + v0.BlockHeaders = (function() { + + /** + * Properties of a BlockHeaders. + * @memberof org.dash.platform.dapi.v0 + * @interface IBlockHeaders + * @property {Array.|null} [headers] BlockHeaders headers + */ + + /** + * Constructs a new BlockHeaders. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a BlockHeaders. + * @implements IBlockHeaders + * @constructor + * @param {org.dash.platform.dapi.v0.IBlockHeaders=} [properties] Properties to set + */ + function BlockHeaders(properties) { + this.headers = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * BlockHeaders headers. + * @member {Array.} headers + * @memberof org.dash.platform.dapi.v0.BlockHeaders + * @instance + */ + BlockHeaders.prototype.headers = $util.emptyArray; + + /** + * Creates a new BlockHeaders instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.BlockHeaders + * @static + * @param {org.dash.platform.dapi.v0.IBlockHeaders=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.BlockHeaders} BlockHeaders instance + */ + BlockHeaders.create = function create(properties) { + return new BlockHeaders(properties); + }; + + /** + * Encodes the specified BlockHeaders message. Does not implicitly {@link org.dash.platform.dapi.v0.BlockHeaders.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.BlockHeaders + * @static + * @param {org.dash.platform.dapi.v0.IBlockHeaders} message BlockHeaders message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BlockHeaders.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.headers != null && message.headers.length) + for (var i = 0; i < message.headers.length; ++i) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.headers[i]); + return writer; + }; + + /** + * Encodes the specified BlockHeaders message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.BlockHeaders.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.BlockHeaders + * @static + * @param {org.dash.platform.dapi.v0.IBlockHeaders} message BlockHeaders message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BlockHeaders.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a BlockHeaders message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.BlockHeaders + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.BlockHeaders} BlockHeaders + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BlockHeaders.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.BlockHeaders(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (!(message.headers && message.headers.length)) + message.headers = []; + message.headers.push(reader.bytes()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a BlockHeaders message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.BlockHeaders + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.BlockHeaders} BlockHeaders + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BlockHeaders.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a BlockHeaders message. + * @function verify + * @memberof org.dash.platform.dapi.v0.BlockHeaders + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + BlockHeaders.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.headers != null && message.hasOwnProperty("headers")) { + if (!Array.isArray(message.headers)) + return "headers: array expected"; + for (var i = 0; i < message.headers.length; ++i) + if (!(message.headers[i] && typeof message.headers[i].length === "number" || $util.isString(message.headers[i]))) + return "headers: buffer[] expected"; + } + return null; + }; + + /** + * Creates a BlockHeaders message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.BlockHeaders + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.BlockHeaders} BlockHeaders + */ + BlockHeaders.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.BlockHeaders) + return object; + var message = new $root.org.dash.platform.dapi.v0.BlockHeaders(); + if (object.headers) { + if (!Array.isArray(object.headers)) + throw TypeError(".org.dash.platform.dapi.v0.BlockHeaders.headers: array expected"); + message.headers = []; + for (var i = 0; i < object.headers.length; ++i) + if (typeof object.headers[i] === "string") + $util.base64.decode(object.headers[i], message.headers[i] = $util.newBuffer($util.base64.length(object.headers[i])), 0); + else if (object.headers[i].length >= 0) + message.headers[i] = object.headers[i]; + } + return message; + }; + + /** + * Creates a plain object from a BlockHeaders message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.BlockHeaders + * @static + * @param {org.dash.platform.dapi.v0.BlockHeaders} message BlockHeaders + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + BlockHeaders.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.headers = []; + if (message.headers && message.headers.length) { + object.headers = []; + for (var j = 0; j < message.headers.length; ++j) + object.headers[j] = options.bytes === String ? $util.base64.encode(message.headers[j], 0, message.headers[j].length) : options.bytes === Array ? Array.prototype.slice.call(message.headers[j]) : message.headers[j]; + } + return object; + }; + + /** + * Converts this BlockHeaders to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.BlockHeaders + * @instance + * @returns {Object.} JSON object + */ + BlockHeaders.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return BlockHeaders; + })(); + + v0.GetEstimatedTransactionFeeRequest = (function() { + + /** + * Properties of a GetEstimatedTransactionFeeRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetEstimatedTransactionFeeRequest + * @property {number|null} [blocks] GetEstimatedTransactionFeeRequest blocks + */ + + /** + * Constructs a new GetEstimatedTransactionFeeRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetEstimatedTransactionFeeRequest. + * @implements IGetEstimatedTransactionFeeRequest + * @constructor + * @param {org.dash.platform.dapi.v0.IGetEstimatedTransactionFeeRequest=} [properties] Properties to set + */ + function GetEstimatedTransactionFeeRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetEstimatedTransactionFeeRequest blocks. + * @member {number} blocks + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest + * @instance + */ + GetEstimatedTransactionFeeRequest.prototype.blocks = 0; + + /** + * Creates a new GetEstimatedTransactionFeeRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetEstimatedTransactionFeeRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} GetEstimatedTransactionFeeRequest instance + */ + GetEstimatedTransactionFeeRequest.create = function create(properties) { + return new GetEstimatedTransactionFeeRequest(properties); + }; + + /** + * Encodes the specified GetEstimatedTransactionFeeRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetEstimatedTransactionFeeRequest} message GetEstimatedTransactionFeeRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetEstimatedTransactionFeeRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.blocks != null && Object.hasOwnProperty.call(message, "blocks")) + writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.blocks); + return writer; + }; + + /** + * Encodes the specified GetEstimatedTransactionFeeRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetEstimatedTransactionFeeRequest} message GetEstimatedTransactionFeeRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetEstimatedTransactionFeeRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetEstimatedTransactionFeeRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} GetEstimatedTransactionFeeRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetEstimatedTransactionFeeRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.blocks = reader.uint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetEstimatedTransactionFeeRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} GetEstimatedTransactionFeeRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetEstimatedTransactionFeeRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetEstimatedTransactionFeeRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetEstimatedTransactionFeeRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.blocks != null && message.hasOwnProperty("blocks")) + if (!$util.isInteger(message.blocks)) + return "blocks: integer expected"; + return null; + }; + + /** + * Creates a GetEstimatedTransactionFeeRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} GetEstimatedTransactionFeeRequest + */ + GetEstimatedTransactionFeeRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest(); + if (object.blocks != null) + message.blocks = object.blocks >>> 0; + return message; + }; + + /** + * Creates a plain object from a GetEstimatedTransactionFeeRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest + * @static + * @param {org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} message GetEstimatedTransactionFeeRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetEstimatedTransactionFeeRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.blocks = 0; + if (message.blocks != null && message.hasOwnProperty("blocks")) + object.blocks = message.blocks; + return object; + }; + + /** + * Converts this GetEstimatedTransactionFeeRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest + * @instance + * @returns {Object.} JSON object + */ + GetEstimatedTransactionFeeRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetEstimatedTransactionFeeRequest; + })(); + + v0.GetEstimatedTransactionFeeResponse = (function() { + + /** + * Properties of a GetEstimatedTransactionFeeResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetEstimatedTransactionFeeResponse + * @property {number|null} [fee] GetEstimatedTransactionFeeResponse fee + */ + + /** + * Constructs a new GetEstimatedTransactionFeeResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetEstimatedTransactionFeeResponse. + * @implements IGetEstimatedTransactionFeeResponse + * @constructor + * @param {org.dash.platform.dapi.v0.IGetEstimatedTransactionFeeResponse=} [properties] Properties to set + */ + function GetEstimatedTransactionFeeResponse(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetEstimatedTransactionFeeResponse fee. + * @member {number} fee + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse + * @instance + */ + GetEstimatedTransactionFeeResponse.prototype.fee = 0; + + /** + * Creates a new GetEstimatedTransactionFeeResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetEstimatedTransactionFeeResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} GetEstimatedTransactionFeeResponse instance + */ + GetEstimatedTransactionFeeResponse.create = function create(properties) { + return new GetEstimatedTransactionFeeResponse(properties); + }; + + /** + * Encodes the specified GetEstimatedTransactionFeeResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetEstimatedTransactionFeeResponse} message GetEstimatedTransactionFeeResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetEstimatedTransactionFeeResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.fee != null && Object.hasOwnProperty.call(message, "fee")) + writer.uint32(/* id 1, wireType 1 =*/9).double(message.fee); + return writer; + }; + + /** + * Encodes the specified GetEstimatedTransactionFeeResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetEstimatedTransactionFeeResponse} message GetEstimatedTransactionFeeResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetEstimatedTransactionFeeResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetEstimatedTransactionFeeResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} GetEstimatedTransactionFeeResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetEstimatedTransactionFeeResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.fee = reader.double(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetEstimatedTransactionFeeResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} GetEstimatedTransactionFeeResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetEstimatedTransactionFeeResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetEstimatedTransactionFeeResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetEstimatedTransactionFeeResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.fee != null && message.hasOwnProperty("fee")) + if (typeof message.fee !== "number") + return "fee: number expected"; + return null; + }; + + /** + * Creates a GetEstimatedTransactionFeeResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} GetEstimatedTransactionFeeResponse + */ + GetEstimatedTransactionFeeResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse(); + if (object.fee != null) + message.fee = Number(object.fee); + return message; + }; + + /** + * Creates a plain object from a GetEstimatedTransactionFeeResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse + * @static + * @param {org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} message GetEstimatedTransactionFeeResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetEstimatedTransactionFeeResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.fee = 0; + if (message.fee != null && message.hasOwnProperty("fee")) + object.fee = options.json && !isFinite(message.fee) ? String(message.fee) : message.fee; + return object; + }; + + /** + * Converts this GetEstimatedTransactionFeeResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse + * @instance + * @returns {Object.} JSON object + */ + GetEstimatedTransactionFeeResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetEstimatedTransactionFeeResponse; + })(); + + v0.TransactionsWithProofsRequest = (function() { + + /** + * Properties of a TransactionsWithProofsRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface ITransactionsWithProofsRequest + * @property {org.dash.platform.dapi.v0.IBloomFilter|null} [bloomFilter] TransactionsWithProofsRequest bloomFilter + * @property {Uint8Array|null} [fromBlockHash] TransactionsWithProofsRequest fromBlockHash + * @property {number|null} [fromBlockHeight] TransactionsWithProofsRequest fromBlockHeight + * @property {number|null} [count] TransactionsWithProofsRequest count + * @property {boolean|null} [sendTransactionHashes] TransactionsWithProofsRequest sendTransactionHashes + */ + + /** + * Constructs a new TransactionsWithProofsRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a TransactionsWithProofsRequest. + * @implements ITransactionsWithProofsRequest + * @constructor + * @param {org.dash.platform.dapi.v0.ITransactionsWithProofsRequest=} [properties] Properties to set + */ + function TransactionsWithProofsRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * TransactionsWithProofsRequest bloomFilter. + * @member {org.dash.platform.dapi.v0.IBloomFilter|null|undefined} bloomFilter + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @instance + */ + TransactionsWithProofsRequest.prototype.bloomFilter = null; + + /** + * TransactionsWithProofsRequest fromBlockHash. + * @member {Uint8Array} fromBlockHash + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @instance + */ + TransactionsWithProofsRequest.prototype.fromBlockHash = $util.newBuffer([]); + + /** + * TransactionsWithProofsRequest fromBlockHeight. + * @member {number} fromBlockHeight + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @instance + */ + TransactionsWithProofsRequest.prototype.fromBlockHeight = 0; + + /** + * TransactionsWithProofsRequest count. + * @member {number} count + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @instance + */ + TransactionsWithProofsRequest.prototype.count = 0; + + /** + * TransactionsWithProofsRequest sendTransactionHashes. + * @member {boolean} sendTransactionHashes + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @instance + */ + TransactionsWithProofsRequest.prototype.sendTransactionHashes = false; + + // OneOf field names bound to virtual getters and setters + var $oneOfFields; + + /** + * TransactionsWithProofsRequest fromBlock. + * @member {"fromBlockHash"|"fromBlockHeight"|undefined} fromBlock + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @instance + */ + Object.defineProperty(TransactionsWithProofsRequest.prototype, "fromBlock", { + get: $util.oneOfGetter($oneOfFields = ["fromBlockHash", "fromBlockHeight"]), + set: $util.oneOfSetter($oneOfFields) + }); + + /** + * Creates a new TransactionsWithProofsRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @static + * @param {org.dash.platform.dapi.v0.ITransactionsWithProofsRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.TransactionsWithProofsRequest} TransactionsWithProofsRequest instance + */ + TransactionsWithProofsRequest.create = function create(properties) { + return new TransactionsWithProofsRequest(properties); + }; + + /** + * Encodes the specified TransactionsWithProofsRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.TransactionsWithProofsRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @static + * @param {org.dash.platform.dapi.v0.ITransactionsWithProofsRequest} message TransactionsWithProofsRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + TransactionsWithProofsRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.bloomFilter != null && Object.hasOwnProperty.call(message, "bloomFilter")) + $root.org.dash.platform.dapi.v0.BloomFilter.encode(message.bloomFilter, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.fromBlockHash != null && Object.hasOwnProperty.call(message, "fromBlockHash")) + writer.uint32(/* id 2, wireType 2 =*/18).bytes(message.fromBlockHash); + if (message.fromBlockHeight != null && Object.hasOwnProperty.call(message, "fromBlockHeight")) + writer.uint32(/* id 3, wireType 0 =*/24).uint32(message.fromBlockHeight); + if (message.count != null && Object.hasOwnProperty.call(message, "count")) + writer.uint32(/* id 4, wireType 0 =*/32).uint32(message.count); + if (message.sendTransactionHashes != null && Object.hasOwnProperty.call(message, "sendTransactionHashes")) + writer.uint32(/* id 5, wireType 0 =*/40).bool(message.sendTransactionHashes); + return writer; + }; + + /** + * Encodes the specified TransactionsWithProofsRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.TransactionsWithProofsRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @static + * @param {org.dash.platform.dapi.v0.ITransactionsWithProofsRequest} message TransactionsWithProofsRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + TransactionsWithProofsRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a TransactionsWithProofsRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.TransactionsWithProofsRequest} TransactionsWithProofsRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + TransactionsWithProofsRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.TransactionsWithProofsRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.bloomFilter = $root.org.dash.platform.dapi.v0.BloomFilter.decode(reader, reader.uint32()); + break; + case 2: + message.fromBlockHash = reader.bytes(); + break; + case 3: + message.fromBlockHeight = reader.uint32(); + break; + case 4: + message.count = reader.uint32(); + break; + case 5: + message.sendTransactionHashes = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a TransactionsWithProofsRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.TransactionsWithProofsRequest} TransactionsWithProofsRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + TransactionsWithProofsRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a TransactionsWithProofsRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + TransactionsWithProofsRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.bloomFilter != null && message.hasOwnProperty("bloomFilter")) { + var error = $root.org.dash.platform.dapi.v0.BloomFilter.verify(message.bloomFilter); + if (error) + return "bloomFilter." + error; + } + if (message.fromBlockHash != null && message.hasOwnProperty("fromBlockHash")) { + properties.fromBlock = 1; + if (!(message.fromBlockHash && typeof message.fromBlockHash.length === "number" || $util.isString(message.fromBlockHash))) + return "fromBlockHash: buffer expected"; + } + if (message.fromBlockHeight != null && message.hasOwnProperty("fromBlockHeight")) { + if (properties.fromBlock === 1) + return "fromBlock: multiple values"; + properties.fromBlock = 1; + if (!$util.isInteger(message.fromBlockHeight)) + return "fromBlockHeight: integer expected"; + } + if (message.count != null && message.hasOwnProperty("count")) + if (!$util.isInteger(message.count)) + return "count: integer expected"; + if (message.sendTransactionHashes != null && message.hasOwnProperty("sendTransactionHashes")) + if (typeof message.sendTransactionHashes !== "boolean") + return "sendTransactionHashes: boolean expected"; + return null; + }; + + /** + * Creates a TransactionsWithProofsRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.TransactionsWithProofsRequest} TransactionsWithProofsRequest + */ + TransactionsWithProofsRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.TransactionsWithProofsRequest) + return object; + var message = new $root.org.dash.platform.dapi.v0.TransactionsWithProofsRequest(); + if (object.bloomFilter != null) { + if (typeof object.bloomFilter !== "object") + throw TypeError(".org.dash.platform.dapi.v0.TransactionsWithProofsRequest.bloomFilter: object expected"); + message.bloomFilter = $root.org.dash.platform.dapi.v0.BloomFilter.fromObject(object.bloomFilter); + } + if (object.fromBlockHash != null) + if (typeof object.fromBlockHash === "string") + $util.base64.decode(object.fromBlockHash, message.fromBlockHash = $util.newBuffer($util.base64.length(object.fromBlockHash)), 0); + else if (object.fromBlockHash.length >= 0) + message.fromBlockHash = object.fromBlockHash; + if (object.fromBlockHeight != null) + message.fromBlockHeight = object.fromBlockHeight >>> 0; + if (object.count != null) + message.count = object.count >>> 0; + if (object.sendTransactionHashes != null) + message.sendTransactionHashes = Boolean(object.sendTransactionHashes); + return message; + }; + + /** + * Creates a plain object from a TransactionsWithProofsRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @static + * @param {org.dash.platform.dapi.v0.TransactionsWithProofsRequest} message TransactionsWithProofsRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + TransactionsWithProofsRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.bloomFilter = null; + object.count = 0; + object.sendTransactionHashes = false; + } + if (message.bloomFilter != null && message.hasOwnProperty("bloomFilter")) + object.bloomFilter = $root.org.dash.platform.dapi.v0.BloomFilter.toObject(message.bloomFilter, options); + if (message.fromBlockHash != null && message.hasOwnProperty("fromBlockHash")) { + object.fromBlockHash = options.bytes === String ? $util.base64.encode(message.fromBlockHash, 0, message.fromBlockHash.length) : options.bytes === Array ? Array.prototype.slice.call(message.fromBlockHash) : message.fromBlockHash; + if (options.oneofs) + object.fromBlock = "fromBlockHash"; + } + if (message.fromBlockHeight != null && message.hasOwnProperty("fromBlockHeight")) { + object.fromBlockHeight = message.fromBlockHeight; + if (options.oneofs) + object.fromBlock = "fromBlockHeight"; + } + if (message.count != null && message.hasOwnProperty("count")) + object.count = message.count; + if (message.sendTransactionHashes != null && message.hasOwnProperty("sendTransactionHashes")) + object.sendTransactionHashes = message.sendTransactionHashes; + return object; + }; + + /** + * Converts this TransactionsWithProofsRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsRequest + * @instance + * @returns {Object.} JSON object + */ + TransactionsWithProofsRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return TransactionsWithProofsRequest; + })(); + + v0.BloomFilter = (function() { + + /** + * Properties of a BloomFilter. + * @memberof org.dash.platform.dapi.v0 + * @interface IBloomFilter + * @property {Uint8Array|null} [vData] BloomFilter vData + * @property {number|null} [nHashFuncs] BloomFilter nHashFuncs + * @property {number|null} [nTweak] BloomFilter nTweak + * @property {number|null} [nFlags] BloomFilter nFlags + */ + + /** + * Constructs a new BloomFilter. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a BloomFilter. + * @implements IBloomFilter + * @constructor + * @param {org.dash.platform.dapi.v0.IBloomFilter=} [properties] Properties to set + */ + function BloomFilter(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * BloomFilter vData. + * @member {Uint8Array} vData + * @memberof org.dash.platform.dapi.v0.BloomFilter + * @instance + */ + BloomFilter.prototype.vData = $util.newBuffer([]); + + /** + * BloomFilter nHashFuncs. + * @member {number} nHashFuncs + * @memberof org.dash.platform.dapi.v0.BloomFilter + * @instance + */ + BloomFilter.prototype.nHashFuncs = 0; + + /** + * BloomFilter nTweak. + * @member {number} nTweak + * @memberof org.dash.platform.dapi.v0.BloomFilter + * @instance + */ + BloomFilter.prototype.nTweak = 0; + + /** + * BloomFilter nFlags. + * @member {number} nFlags + * @memberof org.dash.platform.dapi.v0.BloomFilter + * @instance + */ + BloomFilter.prototype.nFlags = 0; + + /** + * Creates a new BloomFilter instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.BloomFilter + * @static + * @param {org.dash.platform.dapi.v0.IBloomFilter=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.BloomFilter} BloomFilter instance + */ + BloomFilter.create = function create(properties) { + return new BloomFilter(properties); + }; + + /** + * Encodes the specified BloomFilter message. Does not implicitly {@link org.dash.platform.dapi.v0.BloomFilter.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.BloomFilter + * @static + * @param {org.dash.platform.dapi.v0.IBloomFilter} message BloomFilter message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BloomFilter.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.vData != null && Object.hasOwnProperty.call(message, "vData")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.vData); + if (message.nHashFuncs != null && Object.hasOwnProperty.call(message, "nHashFuncs")) + writer.uint32(/* id 2, wireType 0 =*/16).uint32(message.nHashFuncs); + if (message.nTweak != null && Object.hasOwnProperty.call(message, "nTweak")) + writer.uint32(/* id 3, wireType 0 =*/24).uint32(message.nTweak); + if (message.nFlags != null && Object.hasOwnProperty.call(message, "nFlags")) + writer.uint32(/* id 4, wireType 0 =*/32).uint32(message.nFlags); + return writer; + }; + + /** + * Encodes the specified BloomFilter message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.BloomFilter.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.BloomFilter + * @static + * @param {org.dash.platform.dapi.v0.IBloomFilter} message BloomFilter message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BloomFilter.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a BloomFilter message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.BloomFilter + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.BloomFilter} BloomFilter + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BloomFilter.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.BloomFilter(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.vData = reader.bytes(); + break; + case 2: + message.nHashFuncs = reader.uint32(); + break; + case 3: + message.nTweak = reader.uint32(); + break; + case 4: + message.nFlags = reader.uint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a BloomFilter message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.BloomFilter + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.BloomFilter} BloomFilter + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BloomFilter.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a BloomFilter message. + * @function verify + * @memberof org.dash.platform.dapi.v0.BloomFilter + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + BloomFilter.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.vData != null && message.hasOwnProperty("vData")) + if (!(message.vData && typeof message.vData.length === "number" || $util.isString(message.vData))) + return "vData: buffer expected"; + if (message.nHashFuncs != null && message.hasOwnProperty("nHashFuncs")) + if (!$util.isInteger(message.nHashFuncs)) + return "nHashFuncs: integer expected"; + if (message.nTweak != null && message.hasOwnProperty("nTweak")) + if (!$util.isInteger(message.nTweak)) + return "nTweak: integer expected"; + if (message.nFlags != null && message.hasOwnProperty("nFlags")) + if (!$util.isInteger(message.nFlags)) + return "nFlags: integer expected"; + return null; + }; + + /** + * Creates a BloomFilter message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.BloomFilter + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.BloomFilter} BloomFilter + */ + BloomFilter.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.BloomFilter) + return object; + var message = new $root.org.dash.platform.dapi.v0.BloomFilter(); + if (object.vData != null) + if (typeof object.vData === "string") + $util.base64.decode(object.vData, message.vData = $util.newBuffer($util.base64.length(object.vData)), 0); + else if (object.vData.length >= 0) + message.vData = object.vData; + if (object.nHashFuncs != null) + message.nHashFuncs = object.nHashFuncs >>> 0; + if (object.nTweak != null) + message.nTweak = object.nTweak >>> 0; + if (object.nFlags != null) + message.nFlags = object.nFlags >>> 0; + return message; + }; + + /** + * Creates a plain object from a BloomFilter message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.BloomFilter + * @static + * @param {org.dash.platform.dapi.v0.BloomFilter} message BloomFilter + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + BloomFilter.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if (options.bytes === String) + object.vData = ""; + else { + object.vData = []; + if (options.bytes !== Array) + object.vData = $util.newBuffer(object.vData); + } + object.nHashFuncs = 0; + object.nTweak = 0; + object.nFlags = 0; + } + if (message.vData != null && message.hasOwnProperty("vData")) + object.vData = options.bytes === String ? $util.base64.encode(message.vData, 0, message.vData.length) : options.bytes === Array ? Array.prototype.slice.call(message.vData) : message.vData; + if (message.nHashFuncs != null && message.hasOwnProperty("nHashFuncs")) + object.nHashFuncs = message.nHashFuncs; + if (message.nTweak != null && message.hasOwnProperty("nTweak")) + object.nTweak = message.nTweak; + if (message.nFlags != null && message.hasOwnProperty("nFlags")) + object.nFlags = message.nFlags; + return object; + }; + + /** + * Converts this BloomFilter to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.BloomFilter + * @instance + * @returns {Object.} JSON object + */ + BloomFilter.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return BloomFilter; + })(); + + v0.TransactionsWithProofsResponse = (function() { + + /** + * Properties of a TransactionsWithProofsResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface ITransactionsWithProofsResponse + * @property {org.dash.platform.dapi.v0.IRawTransactions|null} [rawTransactions] TransactionsWithProofsResponse rawTransactions + * @property {org.dash.platform.dapi.v0.IInstantSendLockMessages|null} [instantSendLockMessages] TransactionsWithProofsResponse instantSendLockMessages + * @property {Uint8Array|null} [rawMerkleBlock] TransactionsWithProofsResponse rawMerkleBlock + */ + + /** + * Constructs a new TransactionsWithProofsResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a TransactionsWithProofsResponse. + * @implements ITransactionsWithProofsResponse + * @constructor + * @param {org.dash.platform.dapi.v0.ITransactionsWithProofsResponse=} [properties] Properties to set + */ + function TransactionsWithProofsResponse(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * TransactionsWithProofsResponse rawTransactions. + * @member {org.dash.platform.dapi.v0.IRawTransactions|null|undefined} rawTransactions + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsResponse + * @instance + */ + TransactionsWithProofsResponse.prototype.rawTransactions = null; + + /** + * TransactionsWithProofsResponse instantSendLockMessages. + * @member {org.dash.platform.dapi.v0.IInstantSendLockMessages|null|undefined} instantSendLockMessages + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsResponse + * @instance + */ + TransactionsWithProofsResponse.prototype.instantSendLockMessages = null; + + /** + * TransactionsWithProofsResponse rawMerkleBlock. + * @member {Uint8Array} rawMerkleBlock + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsResponse + * @instance + */ + TransactionsWithProofsResponse.prototype.rawMerkleBlock = $util.newBuffer([]); + + // OneOf field names bound to virtual getters and setters + var $oneOfFields; + + /** + * TransactionsWithProofsResponse responses. + * @member {"rawTransactions"|"instantSendLockMessages"|"rawMerkleBlock"|undefined} responses + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsResponse + * @instance + */ + Object.defineProperty(TransactionsWithProofsResponse.prototype, "responses", { + get: $util.oneOfGetter($oneOfFields = ["rawTransactions", "instantSendLockMessages", "rawMerkleBlock"]), + set: $util.oneOfSetter($oneOfFields) + }); + + /** + * Creates a new TransactionsWithProofsResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsResponse + * @static + * @param {org.dash.platform.dapi.v0.ITransactionsWithProofsResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.TransactionsWithProofsResponse} TransactionsWithProofsResponse instance + */ + TransactionsWithProofsResponse.create = function create(properties) { + return new TransactionsWithProofsResponse(properties); + }; + + /** + * Encodes the specified TransactionsWithProofsResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.TransactionsWithProofsResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsResponse + * @static + * @param {org.dash.platform.dapi.v0.ITransactionsWithProofsResponse} message TransactionsWithProofsResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + TransactionsWithProofsResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.rawTransactions != null && Object.hasOwnProperty.call(message, "rawTransactions")) + $root.org.dash.platform.dapi.v0.RawTransactions.encode(message.rawTransactions, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.instantSendLockMessages != null && Object.hasOwnProperty.call(message, "instantSendLockMessages")) + $root.org.dash.platform.dapi.v0.InstantSendLockMessages.encode(message.instantSendLockMessages, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + if (message.rawMerkleBlock != null && Object.hasOwnProperty.call(message, "rawMerkleBlock")) + writer.uint32(/* id 3, wireType 2 =*/26).bytes(message.rawMerkleBlock); + return writer; + }; + + /** + * Encodes the specified TransactionsWithProofsResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.TransactionsWithProofsResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsResponse + * @static + * @param {org.dash.platform.dapi.v0.ITransactionsWithProofsResponse} message TransactionsWithProofsResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + TransactionsWithProofsResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a TransactionsWithProofsResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.TransactionsWithProofsResponse} TransactionsWithProofsResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + TransactionsWithProofsResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.TransactionsWithProofsResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.rawTransactions = $root.org.dash.platform.dapi.v0.RawTransactions.decode(reader, reader.uint32()); + break; + case 2: + message.instantSendLockMessages = $root.org.dash.platform.dapi.v0.InstantSendLockMessages.decode(reader, reader.uint32()); + break; + case 3: + message.rawMerkleBlock = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a TransactionsWithProofsResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.TransactionsWithProofsResponse} TransactionsWithProofsResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + TransactionsWithProofsResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a TransactionsWithProofsResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + TransactionsWithProofsResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.rawTransactions != null && message.hasOwnProperty("rawTransactions")) { + properties.responses = 1; + { + var error = $root.org.dash.platform.dapi.v0.RawTransactions.verify(message.rawTransactions); + if (error) + return "rawTransactions." + error; + } + } + if (message.instantSendLockMessages != null && message.hasOwnProperty("instantSendLockMessages")) { + if (properties.responses === 1) + return "responses: multiple values"; + properties.responses = 1; + { + var error = $root.org.dash.platform.dapi.v0.InstantSendLockMessages.verify(message.instantSendLockMessages); + if (error) + return "instantSendLockMessages." + error; + } + } + if (message.rawMerkleBlock != null && message.hasOwnProperty("rawMerkleBlock")) { + if (properties.responses === 1) + return "responses: multiple values"; + properties.responses = 1; + if (!(message.rawMerkleBlock && typeof message.rawMerkleBlock.length === "number" || $util.isString(message.rawMerkleBlock))) + return "rawMerkleBlock: buffer expected"; + } + return null; + }; + + /** + * Creates a TransactionsWithProofsResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.TransactionsWithProofsResponse} TransactionsWithProofsResponse + */ + TransactionsWithProofsResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.TransactionsWithProofsResponse) + return object; + var message = new $root.org.dash.platform.dapi.v0.TransactionsWithProofsResponse(); + if (object.rawTransactions != null) { + if (typeof object.rawTransactions !== "object") + throw TypeError(".org.dash.platform.dapi.v0.TransactionsWithProofsResponse.rawTransactions: object expected"); + message.rawTransactions = $root.org.dash.platform.dapi.v0.RawTransactions.fromObject(object.rawTransactions); + } + if (object.instantSendLockMessages != null) { + if (typeof object.instantSendLockMessages !== "object") + throw TypeError(".org.dash.platform.dapi.v0.TransactionsWithProofsResponse.instantSendLockMessages: object expected"); + message.instantSendLockMessages = $root.org.dash.platform.dapi.v0.InstantSendLockMessages.fromObject(object.instantSendLockMessages); + } + if (object.rawMerkleBlock != null) + if (typeof object.rawMerkleBlock === "string") + $util.base64.decode(object.rawMerkleBlock, message.rawMerkleBlock = $util.newBuffer($util.base64.length(object.rawMerkleBlock)), 0); + else if (object.rawMerkleBlock.length >= 0) + message.rawMerkleBlock = object.rawMerkleBlock; + return message; + }; + + /** + * Creates a plain object from a TransactionsWithProofsResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsResponse + * @static + * @param {org.dash.platform.dapi.v0.TransactionsWithProofsResponse} message TransactionsWithProofsResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + TransactionsWithProofsResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (message.rawTransactions != null && message.hasOwnProperty("rawTransactions")) { + object.rawTransactions = $root.org.dash.platform.dapi.v0.RawTransactions.toObject(message.rawTransactions, options); + if (options.oneofs) + object.responses = "rawTransactions"; + } + if (message.instantSendLockMessages != null && message.hasOwnProperty("instantSendLockMessages")) { + object.instantSendLockMessages = $root.org.dash.platform.dapi.v0.InstantSendLockMessages.toObject(message.instantSendLockMessages, options); + if (options.oneofs) + object.responses = "instantSendLockMessages"; + } + if (message.rawMerkleBlock != null && message.hasOwnProperty("rawMerkleBlock")) { + object.rawMerkleBlock = options.bytes === String ? $util.base64.encode(message.rawMerkleBlock, 0, message.rawMerkleBlock.length) : options.bytes === Array ? Array.prototype.slice.call(message.rawMerkleBlock) : message.rawMerkleBlock; + if (options.oneofs) + object.responses = "rawMerkleBlock"; + } + return object; + }; + + /** + * Converts this TransactionsWithProofsResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.TransactionsWithProofsResponse + * @instance + * @returns {Object.} JSON object + */ + TransactionsWithProofsResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return TransactionsWithProofsResponse; + })(); + + v0.RawTransactions = (function() { + + /** + * Properties of a RawTransactions. + * @memberof org.dash.platform.dapi.v0 + * @interface IRawTransactions + * @property {Array.|null} [transactions] RawTransactions transactions + */ + + /** + * Constructs a new RawTransactions. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a RawTransactions. + * @implements IRawTransactions + * @constructor + * @param {org.dash.platform.dapi.v0.IRawTransactions=} [properties] Properties to set + */ + function RawTransactions(properties) { + this.transactions = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * RawTransactions transactions. + * @member {Array.} transactions + * @memberof org.dash.platform.dapi.v0.RawTransactions + * @instance + */ + RawTransactions.prototype.transactions = $util.emptyArray; + + /** + * Creates a new RawTransactions instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.RawTransactions + * @static + * @param {org.dash.platform.dapi.v0.IRawTransactions=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.RawTransactions} RawTransactions instance + */ + RawTransactions.create = function create(properties) { + return new RawTransactions(properties); + }; + + /** + * Encodes the specified RawTransactions message. Does not implicitly {@link org.dash.platform.dapi.v0.RawTransactions.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.RawTransactions + * @static + * @param {org.dash.platform.dapi.v0.IRawTransactions} message RawTransactions message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + RawTransactions.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.transactions != null && message.transactions.length) + for (var i = 0; i < message.transactions.length; ++i) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.transactions[i]); + return writer; + }; + + /** + * Encodes the specified RawTransactions message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.RawTransactions.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.RawTransactions + * @static + * @param {org.dash.platform.dapi.v0.IRawTransactions} message RawTransactions message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + RawTransactions.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a RawTransactions message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.RawTransactions + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.RawTransactions} RawTransactions + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + RawTransactions.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.RawTransactions(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (!(message.transactions && message.transactions.length)) + message.transactions = []; + message.transactions.push(reader.bytes()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a RawTransactions message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.RawTransactions + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.RawTransactions} RawTransactions + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + RawTransactions.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a RawTransactions message. + * @function verify + * @memberof org.dash.platform.dapi.v0.RawTransactions + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + RawTransactions.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.transactions != null && message.hasOwnProperty("transactions")) { + if (!Array.isArray(message.transactions)) + return "transactions: array expected"; + for (var i = 0; i < message.transactions.length; ++i) + if (!(message.transactions[i] && typeof message.transactions[i].length === "number" || $util.isString(message.transactions[i]))) + return "transactions: buffer[] expected"; + } + return null; + }; + + /** + * Creates a RawTransactions message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.RawTransactions + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.RawTransactions} RawTransactions + */ + RawTransactions.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.RawTransactions) + return object; + var message = new $root.org.dash.platform.dapi.v0.RawTransactions(); + if (object.transactions) { + if (!Array.isArray(object.transactions)) + throw TypeError(".org.dash.platform.dapi.v0.RawTransactions.transactions: array expected"); + message.transactions = []; + for (var i = 0; i < object.transactions.length; ++i) + if (typeof object.transactions[i] === "string") + $util.base64.decode(object.transactions[i], message.transactions[i] = $util.newBuffer($util.base64.length(object.transactions[i])), 0); + else if (object.transactions[i].length >= 0) + message.transactions[i] = object.transactions[i]; + } + return message; + }; + + /** + * Creates a plain object from a RawTransactions message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.RawTransactions + * @static + * @param {org.dash.platform.dapi.v0.RawTransactions} message RawTransactions + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + RawTransactions.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.transactions = []; + if (message.transactions && message.transactions.length) { + object.transactions = []; + for (var j = 0; j < message.transactions.length; ++j) + object.transactions[j] = options.bytes === String ? $util.base64.encode(message.transactions[j], 0, message.transactions[j].length) : options.bytes === Array ? Array.prototype.slice.call(message.transactions[j]) : message.transactions[j]; + } + return object; + }; + + /** + * Converts this RawTransactions to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.RawTransactions + * @instance + * @returns {Object.} JSON object + */ + RawTransactions.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return RawTransactions; + })(); + + v0.InstantSendLockMessages = (function() { + + /** + * Properties of an InstantSendLockMessages. + * @memberof org.dash.platform.dapi.v0 + * @interface IInstantSendLockMessages + * @property {Array.|null} [messages] InstantSendLockMessages messages + */ + + /** + * Constructs a new InstantSendLockMessages. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents an InstantSendLockMessages. + * @implements IInstantSendLockMessages + * @constructor + * @param {org.dash.platform.dapi.v0.IInstantSendLockMessages=} [properties] Properties to set + */ + function InstantSendLockMessages(properties) { + this.messages = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * InstantSendLockMessages messages. + * @member {Array.} messages + * @memberof org.dash.platform.dapi.v0.InstantSendLockMessages + * @instance + */ + InstantSendLockMessages.prototype.messages = $util.emptyArray; + + /** + * Creates a new InstantSendLockMessages instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.InstantSendLockMessages + * @static + * @param {org.dash.platform.dapi.v0.IInstantSendLockMessages=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.InstantSendLockMessages} InstantSendLockMessages instance + */ + InstantSendLockMessages.create = function create(properties) { + return new InstantSendLockMessages(properties); + }; + + /** + * Encodes the specified InstantSendLockMessages message. Does not implicitly {@link org.dash.platform.dapi.v0.InstantSendLockMessages.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.InstantSendLockMessages + * @static + * @param {org.dash.platform.dapi.v0.IInstantSendLockMessages} message InstantSendLockMessages message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + InstantSendLockMessages.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.messages != null && message.messages.length) + for (var i = 0; i < message.messages.length; ++i) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.messages[i]); + return writer; + }; + + /** + * Encodes the specified InstantSendLockMessages message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.InstantSendLockMessages.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.InstantSendLockMessages + * @static + * @param {org.dash.platform.dapi.v0.IInstantSendLockMessages} message InstantSendLockMessages message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + InstantSendLockMessages.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes an InstantSendLockMessages message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.InstantSendLockMessages + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.InstantSendLockMessages} InstantSendLockMessages + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + InstantSendLockMessages.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.InstantSendLockMessages(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (!(message.messages && message.messages.length)) + message.messages = []; + message.messages.push(reader.bytes()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes an InstantSendLockMessages message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.InstantSendLockMessages + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.InstantSendLockMessages} InstantSendLockMessages + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + InstantSendLockMessages.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies an InstantSendLockMessages message. + * @function verify + * @memberof org.dash.platform.dapi.v0.InstantSendLockMessages + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + InstantSendLockMessages.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.messages != null && message.hasOwnProperty("messages")) { + if (!Array.isArray(message.messages)) + return "messages: array expected"; + for (var i = 0; i < message.messages.length; ++i) + if (!(message.messages[i] && typeof message.messages[i].length === "number" || $util.isString(message.messages[i]))) + return "messages: buffer[] expected"; + } + return null; + }; + + /** + * Creates an InstantSendLockMessages message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.InstantSendLockMessages + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.InstantSendLockMessages} InstantSendLockMessages + */ + InstantSendLockMessages.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.InstantSendLockMessages) + return object; + var message = new $root.org.dash.platform.dapi.v0.InstantSendLockMessages(); + if (object.messages) { + if (!Array.isArray(object.messages)) + throw TypeError(".org.dash.platform.dapi.v0.InstantSendLockMessages.messages: array expected"); + message.messages = []; + for (var i = 0; i < object.messages.length; ++i) + if (typeof object.messages[i] === "string") + $util.base64.decode(object.messages[i], message.messages[i] = $util.newBuffer($util.base64.length(object.messages[i])), 0); + else if (object.messages[i].length >= 0) + message.messages[i] = object.messages[i]; + } + return message; + }; + + /** + * Creates a plain object from an InstantSendLockMessages message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.InstantSendLockMessages + * @static + * @param {org.dash.platform.dapi.v0.InstantSendLockMessages} message InstantSendLockMessages + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + InstantSendLockMessages.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.messages = []; + if (message.messages && message.messages.length) { + object.messages = []; + for (var j = 0; j < message.messages.length; ++j) + object.messages[j] = options.bytes === String ? $util.base64.encode(message.messages[j], 0, message.messages[j].length) : options.bytes === Array ? Array.prototype.slice.call(message.messages[j]) : message.messages[j]; + } + return object; + }; + + /** + * Converts this InstantSendLockMessages to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.InstantSendLockMessages + * @instance + * @returns {Object.} JSON object + */ + InstantSendLockMessages.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return InstantSendLockMessages; + })(); + + return v0; + })(); + + return dapi; + })(); + + return platform; + })(); + + return dash; + })(); + + return org; +})(); + +module.exports = $root; diff --git a/packages/dapi-grpc/clients/core/v0/nodejs/core_protoc.js b/packages/dapi-grpc/clients/core/v0/nodejs/core_protoc.js new file mode 100644 index 00000000000..e04a080875e --- /dev/null +++ b/packages/dapi-grpc/clients/core/v0/nodejs/core_protoc.js @@ -0,0 +1,5887 @@ +// source: core.proto +/** + * @fileoverview + * @enhanceable + * @suppress {missingRequire} reports error on implicit type usages. + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +// @ts-nocheck + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = (function() { + if (this) { return this; } + if (typeof window !== 'undefined') { return window; } + if (typeof global !== 'undefined') { return global; } + if (typeof self !== 'undefined') { return self; } + return Function('return this')(); +}.call(null)); + +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BlockHeaders', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.FromBlockCase', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.ResponsesCase', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BloomFilter', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetBlockRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetBlockRequest.BlockCase', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetBlockResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Network', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Status', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Time', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Version', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetTransactionRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetTransactionResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.InstantSendLockMessages', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.RawTransactions', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.FromBlockCase', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.ResponsesCase', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse.Version, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse.Version'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse.Time, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse.Time'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse.Network, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse.Network'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetBlockRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetBlockRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetBlockRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetBlockResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetBlockResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetBlockResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.displayName = 'proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.displayName = 'proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetTransactionRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetTransactionRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetTransactionRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetTransactionResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetTransactionResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetTransactionResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.displayName = 'proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.displayName = 'proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BlockHeaders = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.org.dash.platform.dapi.v0.BlockHeaders.repeatedFields_, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BlockHeaders, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BlockHeaders.displayName = 'proto.org.dash.platform.dapi.v0.BlockHeaders'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.displayName = 'proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BloomFilter = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BloomFilter, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BloomFilter.displayName = 'proto.org.dash.platform.dapi.v0.BloomFilter'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.displayName = 'proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.RawTransactions = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.org.dash.platform.dapi.v0.RawTransactions.repeatedFields_, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.RawTransactions, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.RawTransactions.displayName = 'proto.org.dash.platform.dapi.v0.RawTransactions'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.org.dash.platform.dapi.v0.InstantSendLockMessages.repeatedFields_, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.InstantSendLockMessages, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.InstantSendLockMessages.displayName = 'proto.org.dash.platform.dapi.v0.InstantSendLockMessages'; +} + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest.toObject = function(includeInstance, msg) { + var f, obj = { + + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusRequest} + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusRequest; + return proto.org.dash.platform.dapi.v0.GetStatusRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusRequest} + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.toObject = function(includeInstance, msg) { + var f, obj = { + version: (f = msg.getVersion()) && proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.toObject(includeInstance, f), + time: (f = msg.getTime()) && proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.toObject(includeInstance, f), + status: jspb.Message.getFieldWithDefault(msg, 3, 0), + syncProgress: jspb.Message.getFloatingPointFieldWithDefault(msg, 4, 0.0), + chain: (f = msg.getChain()) && proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.toObject(includeInstance, f), + masternode: (f = msg.getMasternode()) && proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.toObject(includeInstance, f), + network: (f = msg.getNetwork()) && proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Version; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.deserializeBinaryFromReader); + msg.setVersion(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Time; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.deserializeBinaryFromReader); + msg.setTime(value); + break; + case 3: + var value = /** @type {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Status} */ (reader.readEnum()); + msg.setStatus(value); + break; + case 4: + var value = /** @type {number} */ (reader.readDouble()); + msg.setSyncProgress(value); + break; + case 5: + var value = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.deserializeBinaryFromReader); + msg.setChain(value); + break; + case 6: + var value = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.deserializeBinaryFromReader); + msg.setMasternode(value); + break; + case 7: + var value = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Network; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.deserializeBinaryFromReader); + msg.setNetwork(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getVersion(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.serializeBinaryToWriter + ); + } + f = message.getTime(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.serializeBinaryToWriter + ); + } + f = message.getStatus(); + if (f !== 0.0) { + writer.writeEnum( + 3, + f + ); + } + f = message.getSyncProgress(); + if (f !== 0.0) { + writer.writeDouble( + 4, + f + ); + } + f = message.getChain(); + if (f != null) { + writer.writeMessage( + 5, + f, + proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.serializeBinaryToWriter + ); + } + f = message.getMasternode(); + if (f != null) { + writer.writeMessage( + 6, + f, + proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.serializeBinaryToWriter + ); + } + f = message.getNetwork(); + if (f != null) { + writer.writeMessage( + 7, + f, + proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.serializeBinaryToWriter + ); + } +}; + + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Status = { + NOT_STARTED: 0, + SYNCING: 1, + READY: 2, + ERROR: 3 +}; + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.toObject = function(includeInstance, msg) { + var f, obj = { + protocol: jspb.Message.getFieldWithDefault(msg, 1, 0), + software: jspb.Message.getFieldWithDefault(msg, 2, 0), + agent: jspb.Message.getFieldWithDefault(msg, 3, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Version; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setProtocol(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint32()); + msg.setSoftware(value); + break; + case 3: + var value = /** @type {string} */ (reader.readString()); + msg.setAgent(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getProtocol(); + if (f !== 0) { + writer.writeUint32( + 1, + f + ); + } + f = message.getSoftware(); + if (f !== 0) { + writer.writeUint32( + 2, + f + ); + } + f = message.getAgent(); + if (f.length > 0) { + writer.writeString( + 3, + f + ); + } +}; + + +/** + * optional uint32 protocol = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.getProtocol = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.setProtocol = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional uint32 software = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.getSoftware = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.setSoftware = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional string agent = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.getAgent = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.setAgent = function(value) { + return jspb.Message.setProto3StringField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.toObject = function(includeInstance, msg) { + var f, obj = { + now: jspb.Message.getFieldWithDefault(msg, 1, 0), + offset: jspb.Message.getFieldWithDefault(msg, 2, 0), + median: jspb.Message.getFieldWithDefault(msg, 3, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Time; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setNow(value); + break; + case 2: + var value = /** @type {number} */ (reader.readInt32()); + msg.setOffset(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setMedian(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getNow(); + if (f !== 0) { + writer.writeUint32( + 1, + f + ); + } + f = message.getOffset(); + if (f !== 0) { + writer.writeInt32( + 2, + f + ); + } + f = message.getMedian(); + if (f !== 0) { + writer.writeUint32( + 3, + f + ); + } +}; + + +/** + * optional uint32 now = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.getNow = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.setNow = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional int32 offset = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.getOffset = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.setOffset = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional uint32 median = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.getMedian = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.setMedian = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.toObject = function(includeInstance, msg) { + var f, obj = { + name: jspb.Message.getFieldWithDefault(msg, 1, ""), + headersCount: jspb.Message.getFieldWithDefault(msg, 2, 0), + blocksCount: jspb.Message.getFieldWithDefault(msg, 3, 0), + bestBlockHash: msg.getBestBlockHash_asB64(), + difficulty: jspb.Message.getFloatingPointFieldWithDefault(msg, 5, 0.0), + chainWork: msg.getChainWork_asB64(), + isSynced: jspb.Message.getBooleanFieldWithDefault(msg, 7, false), + syncProgress: jspb.Message.getFloatingPointFieldWithDefault(msg, 8, 0.0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setName(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint32()); + msg.setHeadersCount(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setBlocksCount(value); + break; + case 4: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setBestBlockHash(value); + break; + case 5: + var value = /** @type {number} */ (reader.readDouble()); + msg.setDifficulty(value); + break; + case 6: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setChainWork(value); + break; + case 7: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsSynced(value); + break; + case 8: + var value = /** @type {number} */ (reader.readDouble()); + msg.setSyncProgress(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getName(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getHeadersCount(); + if (f !== 0) { + writer.writeUint32( + 2, + f + ); + } + f = message.getBlocksCount(); + if (f !== 0) { + writer.writeUint32( + 3, + f + ); + } + f = message.getBestBlockHash_asU8(); + if (f.length > 0) { + writer.writeBytes( + 4, + f + ); + } + f = message.getDifficulty(); + if (f !== 0.0) { + writer.writeDouble( + 5, + f + ); + } + f = message.getChainWork_asU8(); + if (f.length > 0) { + writer.writeBytes( + 6, + f + ); + } + f = message.getIsSynced(); + if (f) { + writer.writeBool( + 7, + f + ); + } + f = message.getSyncProgress(); + if (f !== 0.0) { + writer.writeDouble( + 8, + f + ); + } +}; + + +/** + * optional string name = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setName = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional uint32 headers_count = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getHeadersCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setHeadersCount = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional uint32 blocks_count = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getBlocksCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setBlocksCount = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional bytes best_block_hash = 4; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getBestBlockHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * optional bytes best_block_hash = 4; + * This is a type-conversion wrapper around `getBestBlockHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getBestBlockHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getBestBlockHash())); +}; + + +/** + * optional bytes best_block_hash = 4; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getBestBlockHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getBestBlockHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getBestBlockHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setBestBlockHash = function(value) { + return jspb.Message.setProto3BytesField(this, 4, value); +}; + + +/** + * optional double difficulty = 5; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getDifficulty = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 5, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setDifficulty = function(value) { + return jspb.Message.setProto3FloatField(this, 5, value); +}; + + +/** + * optional bytes chain_work = 6; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getChainWork = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, "")); +}; + + +/** + * optional bytes chain_work = 6; + * This is a type-conversion wrapper around `getChainWork()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getChainWork_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getChainWork())); +}; + + +/** + * optional bytes chain_work = 6; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getChainWork()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getChainWork_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getChainWork())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setChainWork = function(value) { + return jspb.Message.setProto3BytesField(this, 6, value); +}; + + +/** + * optional bool is_synced = 7; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getIsSynced = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 7, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setIsSynced = function(value) { + return jspb.Message.setProto3BooleanField(this, 7, value); +}; + + +/** + * optional double sync_progress = 8; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getSyncProgress = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 8, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setSyncProgress = function(value) { + return jspb.Message.setProto3FloatField(this, 8, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.toObject = function(includeInstance, msg) { + var f, obj = { + status: jspb.Message.getFieldWithDefault(msg, 1, 0), + proTxHash: msg.getProTxHash_asB64(), + posePenalty: jspb.Message.getFieldWithDefault(msg, 3, 0), + isSynced: jspb.Message.getBooleanFieldWithDefault(msg, 4, false), + syncProgress: jspb.Message.getFloatingPointFieldWithDefault(msg, 5, 0.0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status} */ (reader.readEnum()); + msg.setStatus(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setProTxHash(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setPosePenalty(value); + break; + case 4: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsSynced(value); + break; + case 5: + var value = /** @type {number} */ (reader.readDouble()); + msg.setSyncProgress(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getStatus(); + if (f !== 0.0) { + writer.writeEnum( + 1, + f + ); + } + f = message.getProTxHash_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getPosePenalty(); + if (f !== 0) { + writer.writeUint32( + 3, + f + ); + } + f = message.getIsSynced(); + if (f) { + writer.writeBool( + 4, + f + ); + } + f = message.getSyncProgress(); + if (f !== 0.0) { + writer.writeDouble( + 5, + f + ); + } +}; + + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status = { + UNKNOWN: 0, + WAITING_FOR_PROTX: 1, + POSE_BANNED: 2, + REMOVED: 3, + OPERATOR_KEY_CHANGED: 4, + PROTX_IP_CHANGED: 5, + READY: 6, + ERROR: 7 +}; + +/** + * optional Status status = 1; + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getStatus = function() { + return /** @type {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.setStatus = function(value) { + return jspb.Message.setProto3EnumField(this, 1, value); +}; + + +/** + * optional bytes pro_tx_hash = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getProTxHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes pro_tx_hash = 2; + * This is a type-conversion wrapper around `getProTxHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getProTxHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getProTxHash())); +}; + + +/** + * optional bytes pro_tx_hash = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getProTxHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getProTxHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getProTxHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.setProTxHash = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional uint32 pose_penalty = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getPosePenalty = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.setPosePenalty = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional bool is_synced = 4; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getIsSynced = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 4, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.setIsSynced = function(value) { + return jspb.Message.setProto3BooleanField(this, 4, value); +}; + + +/** + * optional double sync_progress = 5; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getSyncProgress = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 5, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.setSyncProgress = function(value) { + return jspb.Message.setProto3FloatField(this, 5, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.toObject = function(includeInstance, msg) { + var f, obj = { + relay: jspb.Message.getFloatingPointFieldWithDefault(msg, 1, 0.0), + incremental: jspb.Message.getFloatingPointFieldWithDefault(msg, 2, 0.0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readDouble()); + msg.setRelay(value); + break; + case 2: + var value = /** @type {number} */ (reader.readDouble()); + msg.setIncremental(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getRelay(); + if (f !== 0.0) { + writer.writeDouble( + 1, + f + ); + } + f = message.getIncremental(); + if (f !== 0.0) { + writer.writeDouble( + 2, + f + ); + } +}; + + +/** + * optional double relay = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.prototype.getRelay = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 1, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.prototype.setRelay = function(value) { + return jspb.Message.setProto3FloatField(this, 1, value); +}; + + +/** + * optional double incremental = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.prototype.getIncremental = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 2, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.prototype.setIncremental = function(value) { + return jspb.Message.setProto3FloatField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.toObject = function(includeInstance, msg) { + var f, obj = { + peersCount: jspb.Message.getFieldWithDefault(msg, 1, 0), + fee: (f = msg.getFee()) && proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Network; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setPeersCount(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.deserializeBinaryFromReader); + msg.setFee(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getPeersCount(); + if (f !== 0) { + writer.writeUint32( + 1, + f + ); + } + f = message.getFee(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.serializeBinaryToWriter + ); + } +}; + + +/** + * optional uint32 peers_count = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.getPeersCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.setPeersCount = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional NetworkFee fee = 2; + * @return {?proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.getFee = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} returns this +*/ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.setFee = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.clearFee = function() { + return this.setFee(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.hasFee = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional Version version = 1; + * @return {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getVersion = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.GetStatusResponse.Version, 1)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Version|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setVersion = function(value) { + return jspb.Message.setWrapperField(this, 1, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.clearVersion = function() { + return this.setVersion(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.hasVersion = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional Time time = 2; + * @return {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getTime = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.GetStatusResponse.Time, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Time|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setTime = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.clearTime = function() { + return this.setTime(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.hasTime = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional Status status = 3; + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Status} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getStatus = function() { + return /** @type {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Status} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Status} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setStatus = function(value) { + return jspb.Message.setProto3EnumField(this, 3, value); +}; + + +/** + * optional double sync_progress = 4; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getSyncProgress = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 4, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setSyncProgress = function(value) { + return jspb.Message.setProto3FloatField(this, 4, value); +}; + + +/** + * optional Chain chain = 5; + * @return {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getChain = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain, 5)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setChain = function(value) { + return jspb.Message.setWrapperField(this, 5, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.clearChain = function() { + return this.setChain(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.hasChain = function() { + return jspb.Message.getField(this, 5) != null; +}; + + +/** + * optional Masternode masternode = 6; + * @return {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getMasternode = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode, 6)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setMasternode = function(value) { + return jspb.Message.setWrapperField(this, 6, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.clearMasternode = function() { + return this.setMasternode(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.hasMasternode = function() { + return jspb.Message.getField(this, 6) != null; +}; + + +/** + * optional Network network = 7; + * @return {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getNetwork = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.GetStatusResponse.Network, 7)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Network|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setNetwork = function(value) { + return jspb.Message.setWrapperField(this, 7, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.clearNetwork = function() { + return this.setNetwork(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.hasNetwork = function() { + return jspb.Message.getField(this, 7) != null; +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_ = [[1,2]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.BlockCase = { + BLOCK_NOT_SET: 0, + HEIGHT: 1, + HASH: 2 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.GetBlockRequest.BlockCase} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.getBlockCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.GetBlockRequest.BlockCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetBlockRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetBlockRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.toObject = function(includeInstance, msg) { + var f, obj = { + height: jspb.Message.getFieldWithDefault(msg, 1, 0), + hash: jspb.Message.getFieldWithDefault(msg, 2, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetBlockRequest} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetBlockRequest; + return proto.org.dash.platform.dapi.v0.GetBlockRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetBlockRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetBlockRequest} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setHeight(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setHash(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetBlockRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetBlockRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = /** @type {number} */ (jspb.Message.getField(message, 1)); + if (f != null) { + writer.writeUint32( + 1, + f + ); + } + f = /** @type {string} */ (jspb.Message.getField(message, 2)); + if (f != null) { + writer.writeString( + 2, + f + ); + } +}; + + +/** + * optional uint32 height = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.getHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetBlockRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.setHeight = function(value) { + return jspb.Message.setOneofField(this, 1, proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetBlockRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.clearHeight = function() { + return jspb.Message.setOneofField(this, 1, proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.hasHeight = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional string hash = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.getHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.GetBlockRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.setHash = function(value) { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetBlockRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.clearHash = function() { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.hasHash = function() { + return jspb.Message.getField(this, 2) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetBlockResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetBlockResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.toObject = function(includeInstance, msg) { + var f, obj = { + block: msg.getBlock_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetBlockResponse} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetBlockResponse; + return proto.org.dash.platform.dapi.v0.GetBlockResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetBlockResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetBlockResponse} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setBlock(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetBlockResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetBlockResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getBlock_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } +}; + + +/** + * optional bytes block = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.prototype.getBlock = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes block = 1; + * This is a type-conversion wrapper around `getBlock()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.prototype.getBlock_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getBlock())); +}; + + +/** + * optional bytes block = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getBlock()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.prototype.getBlock_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getBlock())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetBlockResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.prototype.setBlock = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.toObject = function(includeInstance, msg) { + var f, obj = { + transaction: msg.getTransaction_asB64(), + allowHighFees: jspb.Message.getBooleanFieldWithDefault(msg, 2, false), + bypassLimits: jspb.Message.getBooleanFieldWithDefault(msg, 3, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest; + return proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setTransaction(value); + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setAllowHighFees(value); + break; + case 3: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setBypassLimits(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getTransaction_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getAllowHighFees(); + if (f) { + writer.writeBool( + 2, + f + ); + } + f = message.getBypassLimits(); + if (f) { + writer.writeBool( + 3, + f + ); + } +}; + + +/** + * optional bytes transaction = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.getTransaction = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes transaction = 1; + * This is a type-conversion wrapper around `getTransaction()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.getTransaction_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getTransaction())); +}; + + +/** + * optional bytes transaction = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getTransaction()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.getTransaction_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getTransaction())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.setTransaction = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bool allow_high_fees = 2; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.getAllowHighFees = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.setAllowHighFees = function(value) { + return jspb.Message.setProto3BooleanField(this, 2, value); +}; + + +/** + * optional bool bypass_limits = 3; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.getBypassLimits = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 3, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.setBypassLimits = function(value) { + return jspb.Message.setProto3BooleanField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.toObject = function(includeInstance, msg) { + var f, obj = { + transactionId: jspb.Message.getFieldWithDefault(msg, 1, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse; + return proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setTransactionId(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getTransactionId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } +}; + + +/** + * optional string transaction_id = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.prototype.getTransactionId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.prototype.setTransactionId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetTransactionRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.toObject = function(includeInstance, msg) { + var f, obj = { + id: jspb.Message.getFieldWithDefault(msg, 1, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetTransactionRequest; + return proto.org.dash.platform.dapi.v0.GetTransactionRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setId(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetTransactionRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } +}; + + +/** + * optional string id = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.prototype.getId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.prototype.setId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetTransactionResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.toObject = function(includeInstance, msg) { + var f, obj = { + transaction: msg.getTransaction_asB64(), + blockHash: msg.getBlockHash_asB64(), + height: jspb.Message.getFieldWithDefault(msg, 3, 0), + confirmations: jspb.Message.getFieldWithDefault(msg, 4, 0), + isInstantLocked: jspb.Message.getBooleanFieldWithDefault(msg, 5, false), + isChainLocked: jspb.Message.getBooleanFieldWithDefault(msg, 6, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetTransactionResponse; + return proto.org.dash.platform.dapi.v0.GetTransactionResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setTransaction(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setBlockHash(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setHeight(value); + break; + case 4: + var value = /** @type {number} */ (reader.readUint32()); + msg.setConfirmations(value); + break; + case 5: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsInstantLocked(value); + break; + case 6: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsChainLocked(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetTransactionResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getTransaction_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getBlockHash_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getHeight(); + if (f !== 0) { + writer.writeUint32( + 3, + f + ); + } + f = message.getConfirmations(); + if (f !== 0) { + writer.writeUint32( + 4, + f + ); + } + f = message.getIsInstantLocked(); + if (f) { + writer.writeBool( + 5, + f + ); + } + f = message.getIsChainLocked(); + if (f) { + writer.writeBool( + 6, + f + ); + } +}; + + +/** + * optional bytes transaction = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getTransaction = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes transaction = 1; + * This is a type-conversion wrapper around `getTransaction()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getTransaction_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getTransaction())); +}; + + +/** + * optional bytes transaction = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getTransaction()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getTransaction_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getTransaction())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.setTransaction = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bytes block_hash = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getBlockHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes block_hash = 2; + * This is a type-conversion wrapper around `getBlockHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getBlockHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getBlockHash())); +}; + + +/** + * optional bytes block_hash = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getBlockHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getBlockHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getBlockHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.setBlockHash = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional uint32 height = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.setHeight = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional uint32 confirmations = 4; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getConfirmations = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.setConfirmations = function(value) { + return jspb.Message.setProto3IntField(this, 4, value); +}; + + +/** + * optional bool is_instant_locked = 5; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getIsInstantLocked = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 5, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.setIsInstantLocked = function(value) { + return jspb.Message.setProto3BooleanField(this, 5, value); +}; + + +/** + * optional bool is_chain_locked = 6; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getIsChainLocked = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 6, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.setIsChainLocked = function(value) { + return jspb.Message.setProto3BooleanField(this, 6, value); +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_ = [[1,2]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.FromBlockCase = { + FROM_BLOCK_NOT_SET: 0, + FROM_BLOCK_HASH: 1, + FROM_BLOCK_HEIGHT: 2 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.FromBlockCase} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.getFromBlockCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.FromBlockCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.toObject = function(includeInstance, msg) { + var f, obj = { + fromBlockHash: msg.getFromBlockHash_asB64(), + fromBlockHeight: jspb.Message.getFieldWithDefault(msg, 2, 0), + count: jspb.Message.getFieldWithDefault(msg, 3, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest; + return proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setFromBlockHash(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint32()); + msg.setFromBlockHeight(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setCount(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 1)); + if (f != null) { + writer.writeBytes( + 1, + f + ); + } + f = /** @type {number} */ (jspb.Message.getField(message, 2)); + if (f != null) { + writer.writeUint32( + 2, + f + ); + } + f = message.getCount(); + if (f !== 0) { + writer.writeUint32( + 3, + f + ); + } +}; + + +/** + * optional bytes from_block_hash = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.getFromBlockHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes from_block_hash = 1; + * This is a type-conversion wrapper around `getFromBlockHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.getFromBlockHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getFromBlockHash())); +}; + + +/** + * optional bytes from_block_hash = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getFromBlockHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.getFromBlockHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getFromBlockHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.setFromBlockHash = function(value) { + return jspb.Message.setOneofField(this, 1, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.clearFromBlockHash = function() { + return jspb.Message.setOneofField(this, 1, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.hasFromBlockHash = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional uint32 from_block_height = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.getFromBlockHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.setFromBlockHeight = function(value) { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.clearFromBlockHeight = function() { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.hasFromBlockHeight = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional uint32 count = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.getCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.setCount = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.oneofGroups_ = [[1,2]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.ResponsesCase = { + RESPONSES_NOT_SET: 0, + BLOCK_HEADERS: 1, + CHAIN_LOCK: 2 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.ResponsesCase} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.getResponsesCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.ResponsesCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.toObject = function(includeInstance, msg) { + var f, obj = { + blockHeaders: (f = msg.getBlockHeaders()) && proto.org.dash.platform.dapi.v0.BlockHeaders.toObject(includeInstance, f), + chainLock: msg.getChainLock_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse; + return proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.org.dash.platform.dapi.v0.BlockHeaders; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.BlockHeaders.deserializeBinaryFromReader); + msg.setBlockHeaders(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setChainLock(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getBlockHeaders(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.org.dash.platform.dapi.v0.BlockHeaders.serializeBinaryToWriter + ); + } + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 2)); + if (f != null) { + writer.writeBytes( + 2, + f + ); + } +}; + + +/** + * optional BlockHeaders block_headers = 1; + * @return {?proto.org.dash.platform.dapi.v0.BlockHeaders} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.getBlockHeaders = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.BlockHeaders} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.BlockHeaders, 1)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.BlockHeaders|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.setBlockHeaders = function(value) { + return jspb.Message.setOneofWrapperField(this, 1, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.clearBlockHeaders = function() { + return this.setBlockHeaders(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.hasBlockHeaders = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional bytes chain_lock = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.getChainLock = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes chain_lock = 2; + * This is a type-conversion wrapper around `getChainLock()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.getChainLock_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getChainLock())); +}; + + +/** + * optional bytes chain_lock = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getChainLock()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.getChainLock_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getChainLock())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.setChainLock = function(value) { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.clearChainLock = function() { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.hasChainLock = function() { + return jspb.Message.getField(this, 2) != null; +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BlockHeaders.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BlockHeaders} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.toObject = function(includeInstance, msg) { + var f, obj = { + headersList: msg.getHeadersList_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeaders} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BlockHeaders; + return proto.org.dash.platform.dapi.v0.BlockHeaders.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BlockHeaders} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeaders} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.addHeaders(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BlockHeaders.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BlockHeaders} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getHeadersList_asU8(); + if (f.length > 0) { + writer.writeRepeatedBytes( + 1, + f + ); + } +}; + + +/** + * repeated bytes headers = 1; + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.getHeadersList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * repeated bytes headers = 1; + * This is a type-conversion wrapper around `getHeadersList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.getHeadersList_asB64 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsB64( + this.getHeadersList())); +}; + + +/** + * repeated bytes headers = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getHeadersList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.getHeadersList_asU8 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsU8( + this.getHeadersList())); +}; + + +/** + * @param {!(Array|Array)} value + * @return {!proto.org.dash.platform.dapi.v0.BlockHeaders} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.setHeadersList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @param {number=} opt_index + * @return {!proto.org.dash.platform.dapi.v0.BlockHeaders} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.addHeaders = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeaders} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.clearHeadersList = function() { + return this.setHeadersList([]); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.toObject = function(includeInstance, msg) { + var f, obj = { + blocks: jspb.Message.getFieldWithDefault(msg, 1, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest; + return proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setBlocks(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getBlocks(); + if (f !== 0) { + writer.writeUint32( + 1, + f + ); + } +}; + + +/** + * optional uint32 blocks = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.prototype.getBlocks = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.prototype.setBlocks = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.toObject = function(includeInstance, msg) { + var f, obj = { + fee: jspb.Message.getFloatingPointFieldWithDefault(msg, 1, 0.0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse; + return proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readDouble()); + msg.setFee(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getFee(); + if (f !== 0.0) { + writer.writeDouble( + 1, + f + ); + } +}; + + +/** + * optional double fee = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.prototype.getFee = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 1, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.prototype.setFee = function(value) { + return jspb.Message.setProto3FloatField(this, 1, value); +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_ = [[2,3]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.FromBlockCase = { + FROM_BLOCK_NOT_SET: 0, + FROM_BLOCK_HASH: 2, + FROM_BLOCK_HEIGHT: 3 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.FromBlockCase} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getFromBlockCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.FromBlockCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + bloomFilter: (f = msg.getBloomFilter()) && proto.org.dash.platform.dapi.v0.BloomFilter.toObject(includeInstance, f), + fromBlockHash: msg.getFromBlockHash_asB64(), + fromBlockHeight: jspb.Message.getFieldWithDefault(msg, 3, 0), + count: jspb.Message.getFieldWithDefault(msg, 4, 0), + sendTransactionHashes: jspb.Message.getBooleanFieldWithDefault(msg, 5, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest; + return proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.org.dash.platform.dapi.v0.BloomFilter; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.BloomFilter.deserializeBinaryFromReader); + msg.setBloomFilter(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setFromBlockHash(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setFromBlockHeight(value); + break; + case 4: + var value = /** @type {number} */ (reader.readUint32()); + msg.setCount(value); + break; + case 5: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setSendTransactionHashes(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getBloomFilter(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.org.dash.platform.dapi.v0.BloomFilter.serializeBinaryToWriter + ); + } + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 2)); + if (f != null) { + writer.writeBytes( + 2, + f + ); + } + f = /** @type {number} */ (jspb.Message.getField(message, 3)); + if (f != null) { + writer.writeUint32( + 3, + f + ); + } + f = message.getCount(); + if (f !== 0) { + writer.writeUint32( + 4, + f + ); + } + f = message.getSendTransactionHashes(); + if (f) { + writer.writeBool( + 5, + f + ); + } +}; + + +/** + * optional BloomFilter bloom_filter = 1; + * @return {?proto.org.dash.platform.dapi.v0.BloomFilter} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getBloomFilter = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.BloomFilter} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.BloomFilter, 1)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.BloomFilter|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this +*/ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.setBloomFilter = function(value) { + return jspb.Message.setWrapperField(this, 1, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.clearBloomFilter = function() { + return this.setBloomFilter(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.hasBloomFilter = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional bytes from_block_hash = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getFromBlockHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes from_block_hash = 2; + * This is a type-conversion wrapper around `getFromBlockHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getFromBlockHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getFromBlockHash())); +}; + + +/** + * optional bytes from_block_hash = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getFromBlockHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getFromBlockHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getFromBlockHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.setFromBlockHash = function(value) { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.clearFromBlockHash = function() { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.hasFromBlockHash = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional uint32 from_block_height = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getFromBlockHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.setFromBlockHeight = function(value) { + return jspb.Message.setOneofField(this, 3, proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.clearFromBlockHeight = function() { + return jspb.Message.setOneofField(this, 3, proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.hasFromBlockHeight = function() { + return jspb.Message.getField(this, 3) != null; +}; + + +/** + * optional uint32 count = 4; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.setCount = function(value) { + return jspb.Message.setProto3IntField(this, 4, value); +}; + + +/** + * optional bool send_transaction_hashes = 5; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getSendTransactionHashes = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 5, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.setSendTransactionHashes = function(value) { + return jspb.Message.setProto3BooleanField(this, 5, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BloomFilter.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BloomFilter} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BloomFilter.toObject = function(includeInstance, msg) { + var f, obj = { + vData: msg.getVData_asB64(), + nHashFuncs: jspb.Message.getFieldWithDefault(msg, 2, 0), + nTweak: jspb.Message.getFieldWithDefault(msg, 3, 0), + nFlags: jspb.Message.getFieldWithDefault(msg, 4, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BloomFilter} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BloomFilter; + return proto.org.dash.platform.dapi.v0.BloomFilter.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BloomFilter} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BloomFilter} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setVData(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint32()); + msg.setNHashFuncs(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setNTweak(value); + break; + case 4: + var value = /** @type {number} */ (reader.readUint32()); + msg.setNFlags(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BloomFilter.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BloomFilter} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BloomFilter.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getVData_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getNHashFuncs(); + if (f !== 0) { + writer.writeUint32( + 2, + f + ); + } + f = message.getNTweak(); + if (f !== 0) { + writer.writeUint32( + 3, + f + ); + } + f = message.getNFlags(); + if (f !== 0) { + writer.writeUint32( + 4, + f + ); + } +}; + + +/** + * optional bytes v_data = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.getVData = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes v_data = 1; + * This is a type-conversion wrapper around `getVData()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.getVData_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getVData())); +}; + + +/** + * optional bytes v_data = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getVData()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.getVData_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getVData())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.BloomFilter} returns this + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.setVData = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional uint32 n_hash_funcs = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.getNHashFuncs = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.BloomFilter} returns this + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.setNHashFuncs = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional uint32 n_tweak = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.getNTweak = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.BloomFilter} returns this + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.setNTweak = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional uint32 n_flags = 4; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.getNFlags = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.BloomFilter} returns this + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.setNFlags = function(value) { + return jspb.Message.setProto3IntField(this, 4, value); +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_ = [[1,2,3]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.ResponsesCase = { + RESPONSES_NOT_SET: 0, + RAW_TRANSACTIONS: 1, + INSTANT_SEND_LOCK_MESSAGES: 2, + RAW_MERKLE_BLOCK: 3 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.ResponsesCase} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.getResponsesCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.ResponsesCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + rawTransactions: (f = msg.getRawTransactions()) && proto.org.dash.platform.dapi.v0.RawTransactions.toObject(includeInstance, f), + instantSendLockMessages: (f = msg.getInstantSendLockMessages()) && proto.org.dash.platform.dapi.v0.InstantSendLockMessages.toObject(includeInstance, f), + rawMerkleBlock: msg.getRawMerkleBlock_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse; + return proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.org.dash.platform.dapi.v0.RawTransactions; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.RawTransactions.deserializeBinaryFromReader); + msg.setRawTransactions(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.InstantSendLockMessages; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.InstantSendLockMessages.deserializeBinaryFromReader); + msg.setInstantSendLockMessages(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setRawMerkleBlock(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getRawTransactions(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.org.dash.platform.dapi.v0.RawTransactions.serializeBinaryToWriter + ); + } + f = message.getInstantSendLockMessages(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.InstantSendLockMessages.serializeBinaryToWriter + ); + } + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 3)); + if (f != null) { + writer.writeBytes( + 3, + f + ); + } +}; + + +/** + * optional RawTransactions raw_transactions = 1; + * @return {?proto.org.dash.platform.dapi.v0.RawTransactions} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.getRawTransactions = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.RawTransactions} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.RawTransactions, 1)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.RawTransactions|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.setRawTransactions = function(value) { + return jspb.Message.setOneofWrapperField(this, 1, proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.clearRawTransactions = function() { + return this.setRawTransactions(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.hasRawTransactions = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional InstantSendLockMessages instant_send_lock_messages = 2; + * @return {?proto.org.dash.platform.dapi.v0.InstantSendLockMessages} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.getInstantSendLockMessages = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.InstantSendLockMessages} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.InstantSendLockMessages, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.InstantSendLockMessages|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.setInstantSendLockMessages = function(value) { + return jspb.Message.setOneofWrapperField(this, 2, proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.clearInstantSendLockMessages = function() { + return this.setInstantSendLockMessages(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.hasInstantSendLockMessages = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional bytes raw_merkle_block = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.getRawMerkleBlock = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes raw_merkle_block = 3; + * This is a type-conversion wrapper around `getRawMerkleBlock()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.getRawMerkleBlock_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getRawMerkleBlock())); +}; + + +/** + * optional bytes raw_merkle_block = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getRawMerkleBlock()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.getRawMerkleBlock_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getRawMerkleBlock())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.setRawMerkleBlock = function(value) { + return jspb.Message.setOneofField(this, 3, proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.clearRawMerkleBlock = function() { + return jspb.Message.setOneofField(this, 3, proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.hasRawMerkleBlock = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.org.dash.platform.dapi.v0.RawTransactions.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.RawTransactions.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.RawTransactions} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.RawTransactions.toObject = function(includeInstance, msg) { + var f, obj = { + transactionsList: msg.getTransactionsList_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.RawTransactions} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.RawTransactions; + return proto.org.dash.platform.dapi.v0.RawTransactions.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.RawTransactions} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.RawTransactions} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.addTransactions(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.RawTransactions.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.RawTransactions} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.RawTransactions.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getTransactionsList_asU8(); + if (f.length > 0) { + writer.writeRepeatedBytes( + 1, + f + ); + } +}; + + +/** + * repeated bytes transactions = 1; + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.getTransactionsList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * repeated bytes transactions = 1; + * This is a type-conversion wrapper around `getTransactionsList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.getTransactionsList_asB64 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsB64( + this.getTransactionsList())); +}; + + +/** + * repeated bytes transactions = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getTransactionsList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.getTransactionsList_asU8 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsU8( + this.getTransactionsList())); +}; + + +/** + * @param {!(Array|Array)} value + * @return {!proto.org.dash.platform.dapi.v0.RawTransactions} returns this + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.setTransactionsList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @param {number=} opt_index + * @return {!proto.org.dash.platform.dapi.v0.RawTransactions} returns this + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.addTransactions = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.org.dash.platform.dapi.v0.RawTransactions} returns this + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.clearTransactionsList = function() { + return this.setTransactionsList([]); +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.InstantSendLockMessages.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.toObject = function(includeInstance, msg) { + var f, obj = { + messagesList: msg.getMessagesList_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.InstantSendLockMessages; + return proto.org.dash.platform.dapi.v0.InstantSendLockMessages.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.addMessages(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.InstantSendLockMessages.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getMessagesList_asU8(); + if (f.length > 0) { + writer.writeRepeatedBytes( + 1, + f + ); + } +}; + + +/** + * repeated bytes messages = 1; + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.getMessagesList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * repeated bytes messages = 1; + * This is a type-conversion wrapper around `getMessagesList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.getMessagesList_asB64 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsB64( + this.getMessagesList())); +}; + + +/** + * repeated bytes messages = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getMessagesList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.getMessagesList_asU8 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsU8( + this.getMessagesList())); +}; + + +/** + * @param {!(Array|Array)} value + * @return {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} returns this + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.setMessagesList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @param {number=} opt_index + * @return {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} returns this + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.addMessages = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} returns this + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.clearMessagesList = function() { + return this.setMessagesList([]); +}; + + +goog.object.extend(exports, proto.org.dash.platform.dapi.v0); diff --git a/packages/dapi-grpc/clients/core/v0/objective-c/Core.pbobjc.h b/packages/dapi-grpc/clients/core/v0/objective-c/Core.pbobjc.h new file mode 100644 index 00000000000..7a4dec55e5b --- /dev/null +++ b/packages/dapi-grpc/clients/core/v0/objective-c/Core.pbobjc.h @@ -0,0 +1,635 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: core.proto + +// This CPP symbol can be defined to use imports that match up to the framework +// imports needed when using CocoaPods. +#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS) + #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0 +#endif + +#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS + #import +#else + #import "GPBProtocolBuffers.h" +#endif + +#if GOOGLE_PROTOBUF_OBJC_VERSION < 30004 +#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources. +#endif +#if 30004 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION +#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources. +#endif + +// @@protoc_insertion_point(imports) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +CF_EXTERN_C_BEGIN + +@class BlockHeaders; +@class BloomFilter; +@class GetStatusResponse_Chain; +@class GetStatusResponse_Masternode; +@class GetStatusResponse_Network; +@class GetStatusResponse_NetworkFee; +@class GetStatusResponse_Time; +@class GetStatusResponse_Version; +@class InstantSendLockMessages; +@class RawTransactions; + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark - Enum GetStatusResponse_Status + +typedef GPB_ENUM(GetStatusResponse_Status) { + /** + * Value used if any message's field encounters a value that is not defined + * by this enum. The message will also have C functions to get/set the rawValue + * of the field. + **/ + GetStatusResponse_Status_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue, + GetStatusResponse_Status_NotStarted = 0, + GetStatusResponse_Status_Syncing = 1, + GetStatusResponse_Status_Ready = 2, + GetStatusResponse_Status_Error = 3, +}; + +GPBEnumDescriptor *GetStatusResponse_Status_EnumDescriptor(void); + +/** + * Checks to see if the given value is defined by the enum or was not known at + * the time this source was generated. + **/ +BOOL GetStatusResponse_Status_IsValidValue(int32_t value); + +#pragma mark - Enum GetStatusResponse_Masternode_Status + +typedef GPB_ENUM(GetStatusResponse_Masternode_Status) { + /** + * Value used if any message's field encounters a value that is not defined + * by this enum. The message will also have C functions to get/set the rawValue + * of the field. + **/ + GetStatusResponse_Masternode_Status_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue, + GetStatusResponse_Masternode_Status_Unknown = 0, + GetStatusResponse_Masternode_Status_WaitingForProtx = 1, + GetStatusResponse_Masternode_Status_PoseBanned = 2, + GetStatusResponse_Masternode_Status_Removed = 3, + GetStatusResponse_Masternode_Status_OperatorKeyChanged = 4, + GetStatusResponse_Masternode_Status_ProtxIpChanged = 5, + GetStatusResponse_Masternode_Status_Ready = 6, + GetStatusResponse_Masternode_Status_Error = 7, +}; + +GPBEnumDescriptor *GetStatusResponse_Masternode_Status_EnumDescriptor(void); + +/** + * Checks to see if the given value is defined by the enum or was not known at + * the time this source was generated. + **/ +BOOL GetStatusResponse_Masternode_Status_IsValidValue(int32_t value); + +#pragma mark - CoreRoot + +/** + * Exposes the extension registry for this file. + * + * The base class provides: + * @code + * + (GPBExtensionRegistry *)extensionRegistry; + * @endcode + * which is a @c GPBExtensionRegistry that includes all the extensions defined by + * this file and all files that it depends on. + **/ +GPB_FINAL @interface CoreRoot : GPBRootObject +@end + +#pragma mark - GetStatusRequest + +GPB_FINAL @interface GetStatusRequest : GPBMessage + +@end + +#pragma mark - GetStatusResponse + +typedef GPB_ENUM(GetStatusResponse_FieldNumber) { + GetStatusResponse_FieldNumber_Version = 1, + GetStatusResponse_FieldNumber_Time = 2, + GetStatusResponse_FieldNumber_Status = 3, + GetStatusResponse_FieldNumber_SyncProgress = 4, + GetStatusResponse_FieldNumber_Chain = 5, + GetStatusResponse_FieldNumber_Masternode = 6, + GetStatusResponse_FieldNumber_Network = 7, +}; + +GPB_FINAL @interface GetStatusResponse : GPBMessage + +@property(nonatomic, readwrite, strong, null_resettable) GetStatusResponse_Version *version; +/** Test to see if @c version has been set. */ +@property(nonatomic, readwrite) BOOL hasVersion; + +@property(nonatomic, readwrite, strong, null_resettable) GetStatusResponse_Time *time; +/** Test to see if @c time has been set. */ +@property(nonatomic, readwrite) BOOL hasTime; + +@property(nonatomic, readwrite) GetStatusResponse_Status status; + +@property(nonatomic, readwrite) double syncProgress; + +@property(nonatomic, readwrite, strong, null_resettable) GetStatusResponse_Chain *chain; +/** Test to see if @c chain has been set. */ +@property(nonatomic, readwrite) BOOL hasChain; + +@property(nonatomic, readwrite, strong, null_resettable) GetStatusResponse_Masternode *masternode; +/** Test to see if @c masternode has been set. */ +@property(nonatomic, readwrite) BOOL hasMasternode; + +@property(nonatomic, readwrite, strong, null_resettable) GetStatusResponse_Network *network; +/** Test to see if @c network has been set. */ +@property(nonatomic, readwrite) BOOL hasNetwork; + +@end + +/** + * Fetches the raw value of a @c GetStatusResponse's @c status property, even + * if the value was not defined by the enum at the time the code was generated. + **/ +int32_t GetStatusResponse_Status_RawValue(GetStatusResponse *message); +/** + * Sets the raw value of an @c GetStatusResponse's @c status property, allowing + * it to be set to a value that was not defined by the enum at the time the code + * was generated. + **/ +void SetGetStatusResponse_Status_RawValue(GetStatusResponse *message, int32_t value); + +#pragma mark - GetStatusResponse_Version + +typedef GPB_ENUM(GetStatusResponse_Version_FieldNumber) { + GetStatusResponse_Version_FieldNumber_Protocol = 1, + GetStatusResponse_Version_FieldNumber_Software = 2, + GetStatusResponse_Version_FieldNumber_Agent = 3, +}; + +GPB_FINAL @interface GetStatusResponse_Version : GPBMessage + +@property(nonatomic, readwrite) uint32_t protocol; + +@property(nonatomic, readwrite) uint32_t software; + +@property(nonatomic, readwrite, copy, null_resettable) NSString *agent; + +@end + +#pragma mark - GetStatusResponse_Time + +typedef GPB_ENUM(GetStatusResponse_Time_FieldNumber) { + GetStatusResponse_Time_FieldNumber_Now = 1, + GetStatusResponse_Time_FieldNumber_Offset = 2, + GetStatusResponse_Time_FieldNumber_Median = 3, +}; + +GPB_FINAL @interface GetStatusResponse_Time : GPBMessage + +@property(nonatomic, readwrite) uint32_t now; + +@property(nonatomic, readwrite) int32_t offset; + +@property(nonatomic, readwrite) uint32_t median; + +@end + +#pragma mark - GetStatusResponse_Chain + +typedef GPB_ENUM(GetStatusResponse_Chain_FieldNumber) { + GetStatusResponse_Chain_FieldNumber_Name = 1, + GetStatusResponse_Chain_FieldNumber_HeadersCount = 2, + GetStatusResponse_Chain_FieldNumber_BlocksCount = 3, + GetStatusResponse_Chain_FieldNumber_BestBlockHash = 4, + GetStatusResponse_Chain_FieldNumber_Difficulty = 5, + GetStatusResponse_Chain_FieldNumber_ChainWork = 6, + GetStatusResponse_Chain_FieldNumber_IsSynced = 7, + GetStatusResponse_Chain_FieldNumber_SyncProgress = 8, +}; + +GPB_FINAL @interface GetStatusResponse_Chain : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSString *name; + +@property(nonatomic, readwrite) uint32_t headersCount; + +@property(nonatomic, readwrite) uint32_t blocksCount; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *bestBlockHash; + +@property(nonatomic, readwrite) double difficulty; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *chainWork; + +@property(nonatomic, readwrite) BOOL isSynced; + +@property(nonatomic, readwrite) double syncProgress; + +@end + +#pragma mark - GetStatusResponse_Masternode + +typedef GPB_ENUM(GetStatusResponse_Masternode_FieldNumber) { + GetStatusResponse_Masternode_FieldNumber_Status = 1, + GetStatusResponse_Masternode_FieldNumber_ProTxHash = 2, + GetStatusResponse_Masternode_FieldNumber_PosePenalty = 3, + GetStatusResponse_Masternode_FieldNumber_IsSynced = 4, + GetStatusResponse_Masternode_FieldNumber_SyncProgress = 5, +}; + +GPB_FINAL @interface GetStatusResponse_Masternode : GPBMessage + +@property(nonatomic, readwrite) GetStatusResponse_Masternode_Status status; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *proTxHash; + +@property(nonatomic, readwrite) uint32_t posePenalty; + +@property(nonatomic, readwrite) BOOL isSynced; + +@property(nonatomic, readwrite) double syncProgress; + +@end + +/** + * Fetches the raw value of a @c GetStatusResponse_Masternode's @c status property, even + * if the value was not defined by the enum at the time the code was generated. + **/ +int32_t GetStatusResponse_Masternode_Status_RawValue(GetStatusResponse_Masternode *message); +/** + * Sets the raw value of an @c GetStatusResponse_Masternode's @c status property, allowing + * it to be set to a value that was not defined by the enum at the time the code + * was generated. + **/ +void SetGetStatusResponse_Masternode_Status_RawValue(GetStatusResponse_Masternode *message, int32_t value); + +#pragma mark - GetStatusResponse_NetworkFee + +typedef GPB_ENUM(GetStatusResponse_NetworkFee_FieldNumber) { + GetStatusResponse_NetworkFee_FieldNumber_Relay = 1, + GetStatusResponse_NetworkFee_FieldNumber_Incremental = 2, +}; + +GPB_FINAL @interface GetStatusResponse_NetworkFee : GPBMessage + +@property(nonatomic, readwrite) double relay; + +@property(nonatomic, readwrite) double incremental; + +@end + +#pragma mark - GetStatusResponse_Network + +typedef GPB_ENUM(GetStatusResponse_Network_FieldNumber) { + GetStatusResponse_Network_FieldNumber_PeersCount = 1, + GetStatusResponse_Network_FieldNumber_Fee = 2, +}; + +GPB_FINAL @interface GetStatusResponse_Network : GPBMessage + +@property(nonatomic, readwrite) uint32_t peersCount; + +@property(nonatomic, readwrite, strong, null_resettable) GetStatusResponse_NetworkFee *fee; +/** Test to see if @c fee has been set. */ +@property(nonatomic, readwrite) BOOL hasFee; + +@end + +#pragma mark - GetBlockRequest + +typedef GPB_ENUM(GetBlockRequest_FieldNumber) { + GetBlockRequest_FieldNumber_Height = 1, + GetBlockRequest_FieldNumber_Hash_p = 2, +}; + +typedef GPB_ENUM(GetBlockRequest_Block_OneOfCase) { + GetBlockRequest_Block_OneOfCase_GPBUnsetOneOfCase = 0, + GetBlockRequest_Block_OneOfCase_Height = 1, + GetBlockRequest_Block_OneOfCase_Hash_p = 2, +}; + +GPB_FINAL @interface GetBlockRequest : GPBMessage + +@property(nonatomic, readonly) GetBlockRequest_Block_OneOfCase blockOneOfCase; + +@property(nonatomic, readwrite) uint32_t height; + +@property(nonatomic, readwrite, copy, null_resettable) NSString *hash_p; + +@end + +/** + * Clears whatever value was set for the oneof 'block'. + **/ +void GetBlockRequest_ClearBlockOneOfCase(GetBlockRequest *message); + +#pragma mark - GetBlockResponse + +typedef GPB_ENUM(GetBlockResponse_FieldNumber) { + GetBlockResponse_FieldNumber_Block = 1, +}; + +GPB_FINAL @interface GetBlockResponse : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSData *block; + +@end + +#pragma mark - BroadcastTransactionRequest + +typedef GPB_ENUM(BroadcastTransactionRequest_FieldNumber) { + BroadcastTransactionRequest_FieldNumber_Transaction = 1, + BroadcastTransactionRequest_FieldNumber_AllowHighFees = 2, + BroadcastTransactionRequest_FieldNumber_BypassLimits = 3, +}; + +GPB_FINAL @interface BroadcastTransactionRequest : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSData *transaction; + +@property(nonatomic, readwrite) BOOL allowHighFees; + +@property(nonatomic, readwrite) BOOL bypassLimits; + +@end + +#pragma mark - BroadcastTransactionResponse + +typedef GPB_ENUM(BroadcastTransactionResponse_FieldNumber) { + BroadcastTransactionResponse_FieldNumber_TransactionId = 1, +}; + +GPB_FINAL @interface BroadcastTransactionResponse : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSString *transactionId; + +@end + +#pragma mark - GetTransactionRequest + +typedef GPB_ENUM(GetTransactionRequest_FieldNumber) { + GetTransactionRequest_FieldNumber_Id_p = 1, +}; + +GPB_FINAL @interface GetTransactionRequest : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSString *id_p; + +@end + +#pragma mark - GetTransactionResponse + +typedef GPB_ENUM(GetTransactionResponse_FieldNumber) { + GetTransactionResponse_FieldNumber_Transaction = 1, + GetTransactionResponse_FieldNumber_BlockHash = 2, + GetTransactionResponse_FieldNumber_Height = 3, + GetTransactionResponse_FieldNumber_Confirmations = 4, + GetTransactionResponse_FieldNumber_IsInstantLocked = 5, + GetTransactionResponse_FieldNumber_IsChainLocked = 6, +}; + +GPB_FINAL @interface GetTransactionResponse : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSData *transaction; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *blockHash; + +@property(nonatomic, readwrite) uint32_t height; + +@property(nonatomic, readwrite) uint32_t confirmations; + +@property(nonatomic, readwrite) BOOL isInstantLocked; + +@property(nonatomic, readwrite) BOOL isChainLocked; + +@end + +#pragma mark - BlockHeadersWithChainLocksRequest + +typedef GPB_ENUM(BlockHeadersWithChainLocksRequest_FieldNumber) { + BlockHeadersWithChainLocksRequest_FieldNumber_FromBlockHash = 1, + BlockHeadersWithChainLocksRequest_FieldNumber_FromBlockHeight = 2, + BlockHeadersWithChainLocksRequest_FieldNumber_Count = 3, +}; + +typedef GPB_ENUM(BlockHeadersWithChainLocksRequest_FromBlock_OneOfCase) { + BlockHeadersWithChainLocksRequest_FromBlock_OneOfCase_GPBUnsetOneOfCase = 0, + BlockHeadersWithChainLocksRequest_FromBlock_OneOfCase_FromBlockHash = 1, + BlockHeadersWithChainLocksRequest_FromBlock_OneOfCase_FromBlockHeight = 2, +}; + +GPB_FINAL @interface BlockHeadersWithChainLocksRequest : GPBMessage + +@property(nonatomic, readonly) BlockHeadersWithChainLocksRequest_FromBlock_OneOfCase fromBlockOneOfCase; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *fromBlockHash; + +@property(nonatomic, readwrite) uint32_t fromBlockHeight; + +@property(nonatomic, readwrite) uint32_t count; + +@end + +/** + * Clears whatever value was set for the oneof 'fromBlock'. + **/ +void BlockHeadersWithChainLocksRequest_ClearFromBlockOneOfCase(BlockHeadersWithChainLocksRequest *message); + +#pragma mark - BlockHeadersWithChainLocksResponse + +typedef GPB_ENUM(BlockHeadersWithChainLocksResponse_FieldNumber) { + BlockHeadersWithChainLocksResponse_FieldNumber_BlockHeaders = 1, + BlockHeadersWithChainLocksResponse_FieldNumber_ChainLock = 2, +}; + +typedef GPB_ENUM(BlockHeadersWithChainLocksResponse_Responses_OneOfCase) { + BlockHeadersWithChainLocksResponse_Responses_OneOfCase_GPBUnsetOneOfCase = 0, + BlockHeadersWithChainLocksResponse_Responses_OneOfCase_BlockHeaders = 1, + BlockHeadersWithChainLocksResponse_Responses_OneOfCase_ChainLock = 2, +}; + +GPB_FINAL @interface BlockHeadersWithChainLocksResponse : GPBMessage + +@property(nonatomic, readonly) BlockHeadersWithChainLocksResponse_Responses_OneOfCase responsesOneOfCase; + +@property(nonatomic, readwrite, strong, null_resettable) BlockHeaders *blockHeaders; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *chainLock; + +@end + +/** + * Clears whatever value was set for the oneof 'responses'. + **/ +void BlockHeadersWithChainLocksResponse_ClearResponsesOneOfCase(BlockHeadersWithChainLocksResponse *message); + +#pragma mark - BlockHeaders + +typedef GPB_ENUM(BlockHeaders_FieldNumber) { + BlockHeaders_FieldNumber_HeadersArray = 1, +}; + +GPB_FINAL @interface BlockHeaders : GPBMessage + +@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *headersArray; +/** The number of items in @c headersArray without causing the array to be created. */ +@property(nonatomic, readonly) NSUInteger headersArray_Count; + +@end + +#pragma mark - GetEstimatedTransactionFeeRequest + +typedef GPB_ENUM(GetEstimatedTransactionFeeRequest_FieldNumber) { + GetEstimatedTransactionFeeRequest_FieldNumber_Blocks = 1, +}; + +GPB_FINAL @interface GetEstimatedTransactionFeeRequest : GPBMessage + +@property(nonatomic, readwrite) uint32_t blocks; + +@end + +#pragma mark - GetEstimatedTransactionFeeResponse + +typedef GPB_ENUM(GetEstimatedTransactionFeeResponse_FieldNumber) { + GetEstimatedTransactionFeeResponse_FieldNumber_Fee = 1, +}; + +GPB_FINAL @interface GetEstimatedTransactionFeeResponse : GPBMessage + +@property(nonatomic, readwrite) double fee; + +@end + +#pragma mark - TransactionsWithProofsRequest + +typedef GPB_ENUM(TransactionsWithProofsRequest_FieldNumber) { + TransactionsWithProofsRequest_FieldNumber_BloomFilter = 1, + TransactionsWithProofsRequest_FieldNumber_FromBlockHash = 2, + TransactionsWithProofsRequest_FieldNumber_FromBlockHeight = 3, + TransactionsWithProofsRequest_FieldNumber_Count = 4, + TransactionsWithProofsRequest_FieldNumber_SendTransactionHashes = 5, +}; + +typedef GPB_ENUM(TransactionsWithProofsRequest_FromBlock_OneOfCase) { + TransactionsWithProofsRequest_FromBlock_OneOfCase_GPBUnsetOneOfCase = 0, + TransactionsWithProofsRequest_FromBlock_OneOfCase_FromBlockHash = 2, + TransactionsWithProofsRequest_FromBlock_OneOfCase_FromBlockHeight = 3, +}; + +GPB_FINAL @interface TransactionsWithProofsRequest : GPBMessage + +@property(nonatomic, readwrite, strong, null_resettable) BloomFilter *bloomFilter; +/** Test to see if @c bloomFilter has been set. */ +@property(nonatomic, readwrite) BOOL hasBloomFilter; + +@property(nonatomic, readonly) TransactionsWithProofsRequest_FromBlock_OneOfCase fromBlockOneOfCase; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *fromBlockHash; + +@property(nonatomic, readwrite) uint32_t fromBlockHeight; + +@property(nonatomic, readwrite) uint32_t count; + +@property(nonatomic, readwrite) BOOL sendTransactionHashes; + +@end + +/** + * Clears whatever value was set for the oneof 'fromBlock'. + **/ +void TransactionsWithProofsRequest_ClearFromBlockOneOfCase(TransactionsWithProofsRequest *message); + +#pragma mark - BloomFilter + +typedef GPB_ENUM(BloomFilter_FieldNumber) { + BloomFilter_FieldNumber_VData = 1, + BloomFilter_FieldNumber_NHashFuncs = 2, + BloomFilter_FieldNumber_NTweak = 3, + BloomFilter_FieldNumber_NFlags = 4, +}; + +GPB_FINAL @interface BloomFilter : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSData *vData; + +@property(nonatomic, readwrite) uint32_t nHashFuncs; + +@property(nonatomic, readwrite) uint32_t nTweak; + +@property(nonatomic, readwrite) uint32_t nFlags; + +@end + +#pragma mark - TransactionsWithProofsResponse + +typedef GPB_ENUM(TransactionsWithProofsResponse_FieldNumber) { + TransactionsWithProofsResponse_FieldNumber_RawTransactions = 1, + TransactionsWithProofsResponse_FieldNumber_InstantSendLockMessages = 2, + TransactionsWithProofsResponse_FieldNumber_RawMerkleBlock = 3, +}; + +typedef GPB_ENUM(TransactionsWithProofsResponse_Responses_OneOfCase) { + TransactionsWithProofsResponse_Responses_OneOfCase_GPBUnsetOneOfCase = 0, + TransactionsWithProofsResponse_Responses_OneOfCase_RawTransactions = 1, + TransactionsWithProofsResponse_Responses_OneOfCase_InstantSendLockMessages = 2, + TransactionsWithProofsResponse_Responses_OneOfCase_RawMerkleBlock = 3, +}; + +GPB_FINAL @interface TransactionsWithProofsResponse : GPBMessage + +@property(nonatomic, readonly) TransactionsWithProofsResponse_Responses_OneOfCase responsesOneOfCase; + +@property(nonatomic, readwrite, strong, null_resettable) RawTransactions *rawTransactions; + +@property(nonatomic, readwrite, strong, null_resettable) InstantSendLockMessages *instantSendLockMessages; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *rawMerkleBlock; + +@end + +/** + * Clears whatever value was set for the oneof 'responses'. + **/ +void TransactionsWithProofsResponse_ClearResponsesOneOfCase(TransactionsWithProofsResponse *message); + +#pragma mark - RawTransactions + +typedef GPB_ENUM(RawTransactions_FieldNumber) { + RawTransactions_FieldNumber_TransactionsArray = 1, +}; + +GPB_FINAL @interface RawTransactions : GPBMessage + +@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *transactionsArray; +/** The number of items in @c transactionsArray without causing the array to be created. */ +@property(nonatomic, readonly) NSUInteger transactionsArray_Count; + +@end + +#pragma mark - InstantSendLockMessages + +typedef GPB_ENUM(InstantSendLockMessages_FieldNumber) { + InstantSendLockMessages_FieldNumber_MessagesArray = 1, +}; + +GPB_FINAL @interface InstantSendLockMessages : GPBMessage + +@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *messagesArray; +/** The number of items in @c messagesArray without causing the array to be created. */ +@property(nonatomic, readonly) NSUInteger messagesArray_Count; + +@end + +NS_ASSUME_NONNULL_END + +CF_EXTERN_C_END + +#pragma clang diagnostic pop + +// @@protoc_insertion_point(global_scope) diff --git a/packages/dapi-grpc/clients/core/v0/objective-c/Core.pbobjc.m b/packages/dapi-grpc/clients/core/v0/objective-c/Core.pbobjc.m new file mode 100644 index 00000000000..86ff51575a1 --- /dev/null +++ b/packages/dapi-grpc/clients/core/v0/objective-c/Core.pbobjc.m @@ -0,0 +1,1779 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: core.proto + +// This CPP symbol can be defined to use imports that match up to the framework +// imports needed when using CocoaPods. +#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS) + #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0 +#endif + +#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS + #import +#else + #import "GPBProtocolBuffers_RuntimeSupport.h" +#endif + +#import + +#import "Core.pbobjc.h" +// @@protoc_insertion_point(imports) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdirect-ivar-access" +#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" + +#pragma mark - Objective C Class declarations +// Forward declarations of Objective C classes that we can use as +// static values in struct initializers. +// We don't use [Foo class] because it is not a static value. +GPBObjCClassDeclaration(BlockHeaders); +GPBObjCClassDeclaration(BloomFilter); +GPBObjCClassDeclaration(GetStatusResponse); +GPBObjCClassDeclaration(GetStatusResponse_Chain); +GPBObjCClassDeclaration(GetStatusResponse_Masternode); +GPBObjCClassDeclaration(GetStatusResponse_Network); +GPBObjCClassDeclaration(GetStatusResponse_NetworkFee); +GPBObjCClassDeclaration(GetStatusResponse_Time); +GPBObjCClassDeclaration(GetStatusResponse_Version); +GPBObjCClassDeclaration(InstantSendLockMessages); +GPBObjCClassDeclaration(RawTransactions); + +#pragma mark - CoreRoot + +@implementation CoreRoot + +// No extensions in the file and no imports, so no need to generate +// +extensionRegistry. + +@end + +#pragma mark - CoreRoot_FileDescriptor + +static GPBFileDescriptor *CoreRoot_FileDescriptor(void) { + // This is called by +initialize so there is no need to worry + // about thread safety of the singleton. + static GPBFileDescriptor *descriptor = NULL; + if (!descriptor) { + GPB_DEBUG_CHECK_RUNTIME_VERSIONS(); + descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"org.dash.platform.dapi.v0" + syntax:GPBFileSyntaxProto3]; + } + return descriptor; +} + +#pragma mark - GetStatusRequest + +@implementation GetStatusRequest + + +typedef struct GetStatusRequest__storage_ { + uint32_t _has_storage_[1]; +} GetStatusRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetStatusRequest class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:NULL + fieldCount:0 + storageSize:sizeof(GetStatusRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetStatusResponse + +@implementation GetStatusResponse + +@dynamic hasVersion, version; +@dynamic hasTime, time; +@dynamic status; +@dynamic syncProgress; +@dynamic hasChain, chain; +@dynamic hasMasternode, masternode; +@dynamic hasNetwork, network; + +typedef struct GetStatusResponse__storage_ { + uint32_t _has_storage_[1]; + GetStatusResponse_Status status; + GetStatusResponse_Version *version; + GetStatusResponse_Time *time; + GetStatusResponse_Chain *chain; + GetStatusResponse_Masternode *masternode; + GetStatusResponse_Network *network; + double syncProgress; +} GetStatusResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "version", + .dataTypeSpecific.clazz = GPBObjCClass(GetStatusResponse_Version), + .number = GetStatusResponse_FieldNumber_Version, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetStatusResponse__storage_, version), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "time", + .dataTypeSpecific.clazz = GPBObjCClass(GetStatusResponse_Time), + .number = GetStatusResponse_FieldNumber_Time, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetStatusResponse__storage_, time), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "status", + .dataTypeSpecific.enumDescFunc = GetStatusResponse_Status_EnumDescriptor, + .number = GetStatusResponse_FieldNumber_Status, + .hasIndex = 2, + .offset = (uint32_t)offsetof(GetStatusResponse__storage_, status), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeEnum, + }, + { + .name = "syncProgress", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_FieldNumber_SyncProgress, + .hasIndex = 3, + .offset = (uint32_t)offsetof(GetStatusResponse__storage_, syncProgress), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeDouble, + }, + { + .name = "chain", + .dataTypeSpecific.clazz = GPBObjCClass(GetStatusResponse_Chain), + .number = GetStatusResponse_FieldNumber_Chain, + .hasIndex = 4, + .offset = (uint32_t)offsetof(GetStatusResponse__storage_, chain), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "masternode", + .dataTypeSpecific.clazz = GPBObjCClass(GetStatusResponse_Masternode), + .number = GetStatusResponse_FieldNumber_Masternode, + .hasIndex = 5, + .offset = (uint32_t)offsetof(GetStatusResponse__storage_, masternode), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "network", + .dataTypeSpecific.clazz = GPBObjCClass(GetStatusResponse_Network), + .number = GetStatusResponse_FieldNumber_Network, + .hasIndex = 6, + .offset = (uint32_t)offsetof(GetStatusResponse__storage_, network), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetStatusResponse class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetStatusResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +int32_t GetStatusResponse_Status_RawValue(GetStatusResponse *message) { + GPBDescriptor *descriptor = [GetStatusResponse descriptor]; + GPBFieldDescriptor *field = [descriptor fieldWithNumber:GetStatusResponse_FieldNumber_Status]; + return GPBGetMessageRawEnumField(message, field); +} + +void SetGetStatusResponse_Status_RawValue(GetStatusResponse *message, int32_t value) { + GPBDescriptor *descriptor = [GetStatusResponse descriptor]; + GPBFieldDescriptor *field = [descriptor fieldWithNumber:GetStatusResponse_FieldNumber_Status]; + GPBSetMessageRawEnumField(message, field, value); +} + +#pragma mark - Enum GetStatusResponse_Status + +GPBEnumDescriptor *GetStatusResponse_Status_EnumDescriptor(void) { + static _Atomic(GPBEnumDescriptor*) descriptor = nil; + if (!descriptor) { + static const char *valueNames = + "NotStarted\000Syncing\000Ready\000Error\000"; + static const int32_t values[] = { + GetStatusResponse_Status_NotStarted, + GetStatusResponse_Status_Syncing, + GetStatusResponse_Status_Ready, + GetStatusResponse_Status_Error, + }; + GPBEnumDescriptor *worker = + [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GetStatusResponse_Status) + valueNames:valueNames + values:values + count:(uint32_t)(sizeof(values) / sizeof(int32_t)) + enumVerifier:GetStatusResponse_Status_IsValidValue]; + GPBEnumDescriptor *expected = nil; + if (!atomic_compare_exchange_strong(&descriptor, &expected, worker)) { + [worker release]; + } + } + return descriptor; +} + +BOOL GetStatusResponse_Status_IsValidValue(int32_t value__) { + switch (value__) { + case GetStatusResponse_Status_NotStarted: + case GetStatusResponse_Status_Syncing: + case GetStatusResponse_Status_Ready: + case GetStatusResponse_Status_Error: + return YES; + default: + return NO; + } +} + +#pragma mark - GetStatusResponse_Version + +@implementation GetStatusResponse_Version + +@dynamic protocol; +@dynamic software; +@dynamic agent; + +typedef struct GetStatusResponse_Version__storage_ { + uint32_t _has_storage_[1]; + uint32_t protocol; + uint32_t software; + NSString *agent; +} GetStatusResponse_Version__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "protocol", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Version_FieldNumber_Protocol, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetStatusResponse_Version__storage_, protocol), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "software", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Version_FieldNumber_Software, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetStatusResponse_Version__storage_, software), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "agent", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Version_FieldNumber_Agent, + .hasIndex = 2, + .offset = (uint32_t)offsetof(GetStatusResponse_Version__storage_, agent), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeString, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetStatusResponse_Version class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetStatusResponse_Version__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + [localDescriptor setupContainingMessageClass:GPBObjCClass(GetStatusResponse)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetStatusResponse_Time + +@implementation GetStatusResponse_Time + +@dynamic now; +@dynamic offset; +@dynamic median; + +typedef struct GetStatusResponse_Time__storage_ { + uint32_t _has_storage_[1]; + uint32_t now; + int32_t offset; + uint32_t median; +} GetStatusResponse_Time__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "now", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Time_FieldNumber_Now, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetStatusResponse_Time__storage_, now), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "offset", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Time_FieldNumber_Offset, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetStatusResponse_Time__storage_, offset), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeInt32, + }, + { + .name = "median", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Time_FieldNumber_Median, + .hasIndex = 2, + .offset = (uint32_t)offsetof(GetStatusResponse_Time__storage_, median), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetStatusResponse_Time class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetStatusResponse_Time__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + [localDescriptor setupContainingMessageClass:GPBObjCClass(GetStatusResponse)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetStatusResponse_Chain + +@implementation GetStatusResponse_Chain + +@dynamic name; +@dynamic headersCount; +@dynamic blocksCount; +@dynamic bestBlockHash; +@dynamic difficulty; +@dynamic chainWork; +@dynamic isSynced; +@dynamic syncProgress; + +typedef struct GetStatusResponse_Chain__storage_ { + uint32_t _has_storage_[1]; + uint32_t headersCount; + uint32_t blocksCount; + NSString *name; + NSData *bestBlockHash; + NSData *chainWork; + double difficulty; + double syncProgress; +} GetStatusResponse_Chain__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "name", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Chain_FieldNumber_Name, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetStatusResponse_Chain__storage_, name), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeString, + }, + { + .name = "headersCount", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Chain_FieldNumber_HeadersCount, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetStatusResponse_Chain__storage_, headersCount), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "blocksCount", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Chain_FieldNumber_BlocksCount, + .hasIndex = 2, + .offset = (uint32_t)offsetof(GetStatusResponse_Chain__storage_, blocksCount), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "bestBlockHash", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Chain_FieldNumber_BestBlockHash, + .hasIndex = 3, + .offset = (uint32_t)offsetof(GetStatusResponse_Chain__storage_, bestBlockHash), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "difficulty", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Chain_FieldNumber_Difficulty, + .hasIndex = 4, + .offset = (uint32_t)offsetof(GetStatusResponse_Chain__storage_, difficulty), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeDouble, + }, + { + .name = "chainWork", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Chain_FieldNumber_ChainWork, + .hasIndex = 5, + .offset = (uint32_t)offsetof(GetStatusResponse_Chain__storage_, chainWork), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "isSynced", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Chain_FieldNumber_IsSynced, + .hasIndex = 6, + .offset = 7, // Stored in _has_storage_ to save space. + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBool, + }, + { + .name = "syncProgress", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Chain_FieldNumber_SyncProgress, + .hasIndex = 8, + .offset = (uint32_t)offsetof(GetStatusResponse_Chain__storage_, syncProgress), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeDouble, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetStatusResponse_Chain class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetStatusResponse_Chain__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + [localDescriptor setupContainingMessageClass:GPBObjCClass(GetStatusResponse)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetStatusResponse_Masternode + +@implementation GetStatusResponse_Masternode + +@dynamic status; +@dynamic proTxHash; +@dynamic posePenalty; +@dynamic isSynced; +@dynamic syncProgress; + +typedef struct GetStatusResponse_Masternode__storage_ { + uint32_t _has_storage_[1]; + GetStatusResponse_Masternode_Status status; + uint32_t posePenalty; + NSData *proTxHash; + double syncProgress; +} GetStatusResponse_Masternode__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "status", + .dataTypeSpecific.enumDescFunc = GetStatusResponse_Masternode_Status_EnumDescriptor, + .number = GetStatusResponse_Masternode_FieldNumber_Status, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetStatusResponse_Masternode__storage_, status), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeEnum, + }, + { + .name = "proTxHash", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Masternode_FieldNumber_ProTxHash, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetStatusResponse_Masternode__storage_, proTxHash), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "posePenalty", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Masternode_FieldNumber_PosePenalty, + .hasIndex = 2, + .offset = (uint32_t)offsetof(GetStatusResponse_Masternode__storage_, posePenalty), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "isSynced", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Masternode_FieldNumber_IsSynced, + .hasIndex = 3, + .offset = 4, // Stored in _has_storage_ to save space. + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBool, + }, + { + .name = "syncProgress", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Masternode_FieldNumber_SyncProgress, + .hasIndex = 5, + .offset = (uint32_t)offsetof(GetStatusResponse_Masternode__storage_, syncProgress), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeDouble, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetStatusResponse_Masternode class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetStatusResponse_Masternode__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + [localDescriptor setupContainingMessageClass:GPBObjCClass(GetStatusResponse)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +int32_t GetStatusResponse_Masternode_Status_RawValue(GetStatusResponse_Masternode *message) { + GPBDescriptor *descriptor = [GetStatusResponse_Masternode descriptor]; + GPBFieldDescriptor *field = [descriptor fieldWithNumber:GetStatusResponse_Masternode_FieldNumber_Status]; + return GPBGetMessageRawEnumField(message, field); +} + +void SetGetStatusResponse_Masternode_Status_RawValue(GetStatusResponse_Masternode *message, int32_t value) { + GPBDescriptor *descriptor = [GetStatusResponse_Masternode descriptor]; + GPBFieldDescriptor *field = [descriptor fieldWithNumber:GetStatusResponse_Masternode_FieldNumber_Status]; + GPBSetMessageRawEnumField(message, field, value); +} + +#pragma mark - Enum GetStatusResponse_Masternode_Status + +GPBEnumDescriptor *GetStatusResponse_Masternode_Status_EnumDescriptor(void) { + static _Atomic(GPBEnumDescriptor*) descriptor = nil; + if (!descriptor) { + static const char *valueNames = + "Unknown\000WaitingForProtx\000PoseBanned\000Remov" + "ed\000OperatorKeyChanged\000ProtxIpChanged\000Rea" + "dy\000Error\000"; + static const int32_t values[] = { + GetStatusResponse_Masternode_Status_Unknown, + GetStatusResponse_Masternode_Status_WaitingForProtx, + GetStatusResponse_Masternode_Status_PoseBanned, + GetStatusResponse_Masternode_Status_Removed, + GetStatusResponse_Masternode_Status_OperatorKeyChanged, + GetStatusResponse_Masternode_Status_ProtxIpChanged, + GetStatusResponse_Masternode_Status_Ready, + GetStatusResponse_Masternode_Status_Error, + }; + GPBEnumDescriptor *worker = + [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GetStatusResponse_Masternode_Status) + valueNames:valueNames + values:values + count:(uint32_t)(sizeof(values) / sizeof(int32_t)) + enumVerifier:GetStatusResponse_Masternode_Status_IsValidValue]; + GPBEnumDescriptor *expected = nil; + if (!atomic_compare_exchange_strong(&descriptor, &expected, worker)) { + [worker release]; + } + } + return descriptor; +} + +BOOL GetStatusResponse_Masternode_Status_IsValidValue(int32_t value__) { + switch (value__) { + case GetStatusResponse_Masternode_Status_Unknown: + case GetStatusResponse_Masternode_Status_WaitingForProtx: + case GetStatusResponse_Masternode_Status_PoseBanned: + case GetStatusResponse_Masternode_Status_Removed: + case GetStatusResponse_Masternode_Status_OperatorKeyChanged: + case GetStatusResponse_Masternode_Status_ProtxIpChanged: + case GetStatusResponse_Masternode_Status_Ready: + case GetStatusResponse_Masternode_Status_Error: + return YES; + default: + return NO; + } +} + +#pragma mark - GetStatusResponse_NetworkFee + +@implementation GetStatusResponse_NetworkFee + +@dynamic relay; +@dynamic incremental; + +typedef struct GetStatusResponse_NetworkFee__storage_ { + uint32_t _has_storage_[1]; + double relay; + double incremental; +} GetStatusResponse_NetworkFee__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "relay", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_NetworkFee_FieldNumber_Relay, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetStatusResponse_NetworkFee__storage_, relay), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeDouble, + }, + { + .name = "incremental", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_NetworkFee_FieldNumber_Incremental, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetStatusResponse_NetworkFee__storage_, incremental), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeDouble, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetStatusResponse_NetworkFee class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetStatusResponse_NetworkFee__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + [localDescriptor setupContainingMessageClass:GPBObjCClass(GetStatusResponse)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetStatusResponse_Network + +@implementation GetStatusResponse_Network + +@dynamic peersCount; +@dynamic hasFee, fee; + +typedef struct GetStatusResponse_Network__storage_ { + uint32_t _has_storage_[1]; + uint32_t peersCount; + GetStatusResponse_NetworkFee *fee; +} GetStatusResponse_Network__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "peersCount", + .dataTypeSpecific.clazz = Nil, + .number = GetStatusResponse_Network_FieldNumber_PeersCount, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetStatusResponse_Network__storage_, peersCount), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "fee", + .dataTypeSpecific.clazz = GPBObjCClass(GetStatusResponse_NetworkFee), + .number = GetStatusResponse_Network_FieldNumber_Fee, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetStatusResponse_Network__storage_, fee), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetStatusResponse_Network class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetStatusResponse_Network__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + [localDescriptor setupContainingMessageClass:GPBObjCClass(GetStatusResponse)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetBlockRequest + +@implementation GetBlockRequest + +@dynamic blockOneOfCase; +@dynamic height; +@dynamic hash_p; + +typedef struct GetBlockRequest__storage_ { + uint32_t _has_storage_[2]; + uint32_t height; + NSString *hash_p; +} GetBlockRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "height", + .dataTypeSpecific.clazz = Nil, + .number = GetBlockRequest_FieldNumber_Height, + .hasIndex = -1, + .offset = (uint32_t)offsetof(GetBlockRequest__storage_, height), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeUInt32, + }, + { + .name = "hash_p", + .dataTypeSpecific.clazz = Nil, + .number = GetBlockRequest_FieldNumber_Hash_p, + .hasIndex = -1, + .offset = (uint32_t)offsetof(GetBlockRequest__storage_, hash_p), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeString, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetBlockRequest class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetBlockRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + static const char *oneofs[] = { + "block", + }; + [localDescriptor setupOneofs:oneofs + count:(uint32_t)(sizeof(oneofs) / sizeof(char*)) + firstHasIndex:-1]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +void GetBlockRequest_ClearBlockOneOfCase(GetBlockRequest *message) { + GPBDescriptor *descriptor = [GetBlockRequest descriptor]; + GPBOneofDescriptor *oneof = [descriptor.oneofs objectAtIndex:0]; + GPBClearOneof(message, oneof); +} +#pragma mark - GetBlockResponse + +@implementation GetBlockResponse + +@dynamic block; + +typedef struct GetBlockResponse__storage_ { + uint32_t _has_storage_[1]; + NSData *block; +} GetBlockResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "block", + .dataTypeSpecific.clazz = Nil, + .number = GetBlockResponse_FieldNumber_Block, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetBlockResponse__storage_, block), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetBlockResponse class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetBlockResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - BroadcastTransactionRequest + +@implementation BroadcastTransactionRequest + +@dynamic transaction; +@dynamic allowHighFees; +@dynamic bypassLimits; + +typedef struct BroadcastTransactionRequest__storage_ { + uint32_t _has_storage_[1]; + NSData *transaction; +} BroadcastTransactionRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "transaction", + .dataTypeSpecific.clazz = Nil, + .number = BroadcastTransactionRequest_FieldNumber_Transaction, + .hasIndex = 0, + .offset = (uint32_t)offsetof(BroadcastTransactionRequest__storage_, transaction), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "allowHighFees", + .dataTypeSpecific.clazz = Nil, + .number = BroadcastTransactionRequest_FieldNumber_AllowHighFees, + .hasIndex = 1, + .offset = 2, // Stored in _has_storage_ to save space. + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBool, + }, + { + .name = "bypassLimits", + .dataTypeSpecific.clazz = Nil, + .number = BroadcastTransactionRequest_FieldNumber_BypassLimits, + .hasIndex = 3, + .offset = 4, // Stored in _has_storage_ to save space. + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBool, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[BroadcastTransactionRequest class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(BroadcastTransactionRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - BroadcastTransactionResponse + +@implementation BroadcastTransactionResponse + +@dynamic transactionId; + +typedef struct BroadcastTransactionResponse__storage_ { + uint32_t _has_storage_[1]; + NSString *transactionId; +} BroadcastTransactionResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "transactionId", + .dataTypeSpecific.clazz = Nil, + .number = BroadcastTransactionResponse_FieldNumber_TransactionId, + .hasIndex = 0, + .offset = (uint32_t)offsetof(BroadcastTransactionResponse__storage_, transactionId), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeString, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[BroadcastTransactionResponse class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(BroadcastTransactionResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetTransactionRequest + +@implementation GetTransactionRequest + +@dynamic id_p; + +typedef struct GetTransactionRequest__storage_ { + uint32_t _has_storage_[1]; + NSString *id_p; +} GetTransactionRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "id_p", + .dataTypeSpecific.clazz = Nil, + .number = GetTransactionRequest_FieldNumber_Id_p, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetTransactionRequest__storage_, id_p), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeString, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetTransactionRequest class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetTransactionRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetTransactionResponse + +@implementation GetTransactionResponse + +@dynamic transaction; +@dynamic blockHash; +@dynamic height; +@dynamic confirmations; +@dynamic isInstantLocked; +@dynamic isChainLocked; + +typedef struct GetTransactionResponse__storage_ { + uint32_t _has_storage_[1]; + uint32_t height; + uint32_t confirmations; + NSData *transaction; + NSData *blockHash; +} GetTransactionResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "transaction", + .dataTypeSpecific.clazz = Nil, + .number = GetTransactionResponse_FieldNumber_Transaction, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetTransactionResponse__storage_, transaction), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "blockHash", + .dataTypeSpecific.clazz = Nil, + .number = GetTransactionResponse_FieldNumber_BlockHash, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetTransactionResponse__storage_, blockHash), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "height", + .dataTypeSpecific.clazz = Nil, + .number = GetTransactionResponse_FieldNumber_Height, + .hasIndex = 2, + .offset = (uint32_t)offsetof(GetTransactionResponse__storage_, height), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "confirmations", + .dataTypeSpecific.clazz = Nil, + .number = GetTransactionResponse_FieldNumber_Confirmations, + .hasIndex = 3, + .offset = (uint32_t)offsetof(GetTransactionResponse__storage_, confirmations), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "isInstantLocked", + .dataTypeSpecific.clazz = Nil, + .number = GetTransactionResponse_FieldNumber_IsInstantLocked, + .hasIndex = 4, + .offset = 5, // Stored in _has_storage_ to save space. + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBool, + }, + { + .name = "isChainLocked", + .dataTypeSpecific.clazz = Nil, + .number = GetTransactionResponse_FieldNumber_IsChainLocked, + .hasIndex = 6, + .offset = 7, // Stored in _has_storage_ to save space. + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBool, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetTransactionResponse class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetTransactionResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - BlockHeadersWithChainLocksRequest + +@implementation BlockHeadersWithChainLocksRequest + +@dynamic fromBlockOneOfCase; +@dynamic fromBlockHash; +@dynamic fromBlockHeight; +@dynamic count; + +typedef struct BlockHeadersWithChainLocksRequest__storage_ { + uint32_t _has_storage_[2]; + uint32_t fromBlockHeight; + uint32_t count; + NSData *fromBlockHash; +} BlockHeadersWithChainLocksRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "fromBlockHash", + .dataTypeSpecific.clazz = Nil, + .number = BlockHeadersWithChainLocksRequest_FieldNumber_FromBlockHash, + .hasIndex = -1, + .offset = (uint32_t)offsetof(BlockHeadersWithChainLocksRequest__storage_, fromBlockHash), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeBytes, + }, + { + .name = "fromBlockHeight", + .dataTypeSpecific.clazz = Nil, + .number = BlockHeadersWithChainLocksRequest_FieldNumber_FromBlockHeight, + .hasIndex = -1, + .offset = (uint32_t)offsetof(BlockHeadersWithChainLocksRequest__storage_, fromBlockHeight), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeUInt32, + }, + { + .name = "count", + .dataTypeSpecific.clazz = Nil, + .number = BlockHeadersWithChainLocksRequest_FieldNumber_Count, + .hasIndex = 0, + .offset = (uint32_t)offsetof(BlockHeadersWithChainLocksRequest__storage_, count), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[BlockHeadersWithChainLocksRequest class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(BlockHeadersWithChainLocksRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + static const char *oneofs[] = { + "fromBlock", + }; + [localDescriptor setupOneofs:oneofs + count:(uint32_t)(sizeof(oneofs) / sizeof(char*)) + firstHasIndex:-1]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +void BlockHeadersWithChainLocksRequest_ClearFromBlockOneOfCase(BlockHeadersWithChainLocksRequest *message) { + GPBDescriptor *descriptor = [BlockHeadersWithChainLocksRequest descriptor]; + GPBOneofDescriptor *oneof = [descriptor.oneofs objectAtIndex:0]; + GPBClearOneof(message, oneof); +} +#pragma mark - BlockHeadersWithChainLocksResponse + +@implementation BlockHeadersWithChainLocksResponse + +@dynamic responsesOneOfCase; +@dynamic blockHeaders; +@dynamic chainLock; + +typedef struct BlockHeadersWithChainLocksResponse__storage_ { + uint32_t _has_storage_[2]; + BlockHeaders *blockHeaders; + NSData *chainLock; +} BlockHeadersWithChainLocksResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "blockHeaders", + .dataTypeSpecific.clazz = GPBObjCClass(BlockHeaders), + .number = BlockHeadersWithChainLocksResponse_FieldNumber_BlockHeaders, + .hasIndex = -1, + .offset = (uint32_t)offsetof(BlockHeadersWithChainLocksResponse__storage_, blockHeaders), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "chainLock", + .dataTypeSpecific.clazz = Nil, + .number = BlockHeadersWithChainLocksResponse_FieldNumber_ChainLock, + .hasIndex = -1, + .offset = (uint32_t)offsetof(BlockHeadersWithChainLocksResponse__storage_, chainLock), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeBytes, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[BlockHeadersWithChainLocksResponse class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(BlockHeadersWithChainLocksResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + static const char *oneofs[] = { + "responses", + }; + [localDescriptor setupOneofs:oneofs + count:(uint32_t)(sizeof(oneofs) / sizeof(char*)) + firstHasIndex:-1]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +void BlockHeadersWithChainLocksResponse_ClearResponsesOneOfCase(BlockHeadersWithChainLocksResponse *message) { + GPBDescriptor *descriptor = [BlockHeadersWithChainLocksResponse descriptor]; + GPBOneofDescriptor *oneof = [descriptor.oneofs objectAtIndex:0]; + GPBClearOneof(message, oneof); +} +#pragma mark - BlockHeaders + +@implementation BlockHeaders + +@dynamic headersArray, headersArray_Count; + +typedef struct BlockHeaders__storage_ { + uint32_t _has_storage_[1]; + NSMutableArray *headersArray; +} BlockHeaders__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "headersArray", + .dataTypeSpecific.clazz = Nil, + .number = BlockHeaders_FieldNumber_HeadersArray, + .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(BlockHeaders__storage_, headersArray), + .flags = GPBFieldRepeated, + .dataType = GPBDataTypeBytes, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[BlockHeaders class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(BlockHeaders__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetEstimatedTransactionFeeRequest + +@implementation GetEstimatedTransactionFeeRequest + +@dynamic blocks; + +typedef struct GetEstimatedTransactionFeeRequest__storage_ { + uint32_t _has_storage_[1]; + uint32_t blocks; +} GetEstimatedTransactionFeeRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "blocks", + .dataTypeSpecific.clazz = Nil, + .number = GetEstimatedTransactionFeeRequest_FieldNumber_Blocks, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetEstimatedTransactionFeeRequest__storage_, blocks), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetEstimatedTransactionFeeRequest class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetEstimatedTransactionFeeRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetEstimatedTransactionFeeResponse + +@implementation GetEstimatedTransactionFeeResponse + +@dynamic fee; + +typedef struct GetEstimatedTransactionFeeResponse__storage_ { + uint32_t _has_storage_[1]; + double fee; +} GetEstimatedTransactionFeeResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "fee", + .dataTypeSpecific.clazz = Nil, + .number = GetEstimatedTransactionFeeResponse_FieldNumber_Fee, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetEstimatedTransactionFeeResponse__storage_, fee), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeDouble, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetEstimatedTransactionFeeResponse class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetEstimatedTransactionFeeResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - TransactionsWithProofsRequest + +@implementation TransactionsWithProofsRequest + +@dynamic fromBlockOneOfCase; +@dynamic hasBloomFilter, bloomFilter; +@dynamic fromBlockHash; +@dynamic fromBlockHeight; +@dynamic count; +@dynamic sendTransactionHashes; + +typedef struct TransactionsWithProofsRequest__storage_ { + uint32_t _has_storage_[2]; + uint32_t fromBlockHeight; + uint32_t count; + BloomFilter *bloomFilter; + NSData *fromBlockHash; +} TransactionsWithProofsRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "bloomFilter", + .dataTypeSpecific.clazz = GPBObjCClass(BloomFilter), + .number = TransactionsWithProofsRequest_FieldNumber_BloomFilter, + .hasIndex = 0, + .offset = (uint32_t)offsetof(TransactionsWithProofsRequest__storage_, bloomFilter), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "fromBlockHash", + .dataTypeSpecific.clazz = Nil, + .number = TransactionsWithProofsRequest_FieldNumber_FromBlockHash, + .hasIndex = -1, + .offset = (uint32_t)offsetof(TransactionsWithProofsRequest__storage_, fromBlockHash), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeBytes, + }, + { + .name = "fromBlockHeight", + .dataTypeSpecific.clazz = Nil, + .number = TransactionsWithProofsRequest_FieldNumber_FromBlockHeight, + .hasIndex = -1, + .offset = (uint32_t)offsetof(TransactionsWithProofsRequest__storage_, fromBlockHeight), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeUInt32, + }, + { + .name = "count", + .dataTypeSpecific.clazz = Nil, + .number = TransactionsWithProofsRequest_FieldNumber_Count, + .hasIndex = 1, + .offset = (uint32_t)offsetof(TransactionsWithProofsRequest__storage_, count), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "sendTransactionHashes", + .dataTypeSpecific.clazz = Nil, + .number = TransactionsWithProofsRequest_FieldNumber_SendTransactionHashes, + .hasIndex = 2, + .offset = 3, // Stored in _has_storage_ to save space. + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBool, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[TransactionsWithProofsRequest class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(TransactionsWithProofsRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + static const char *oneofs[] = { + "fromBlock", + }; + [localDescriptor setupOneofs:oneofs + count:(uint32_t)(sizeof(oneofs) / sizeof(char*)) + firstHasIndex:-1]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +void TransactionsWithProofsRequest_ClearFromBlockOneOfCase(TransactionsWithProofsRequest *message) { + GPBDescriptor *descriptor = [TransactionsWithProofsRequest descriptor]; + GPBOneofDescriptor *oneof = [descriptor.oneofs objectAtIndex:0]; + GPBClearOneof(message, oneof); +} +#pragma mark - BloomFilter + +@implementation BloomFilter + +@dynamic vData; +@dynamic nHashFuncs; +@dynamic nTweak; +@dynamic nFlags; + +typedef struct BloomFilter__storage_ { + uint32_t _has_storage_[1]; + uint32_t nHashFuncs; + uint32_t nTweak; + uint32_t nFlags; + NSData *vData; +} BloomFilter__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "vData", + .dataTypeSpecific.clazz = Nil, + .number = BloomFilter_FieldNumber_VData, + .hasIndex = 0, + .offset = (uint32_t)offsetof(BloomFilter__storage_, vData), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "nHashFuncs", + .dataTypeSpecific.clazz = Nil, + .number = BloomFilter_FieldNumber_NHashFuncs, + .hasIndex = 1, + .offset = (uint32_t)offsetof(BloomFilter__storage_, nHashFuncs), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "nTweak", + .dataTypeSpecific.clazz = Nil, + .number = BloomFilter_FieldNumber_NTweak, + .hasIndex = 2, + .offset = (uint32_t)offsetof(BloomFilter__storage_, nTweak), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "nFlags", + .dataTypeSpecific.clazz = Nil, + .number = BloomFilter_FieldNumber_NFlags, + .hasIndex = 3, + .offset = (uint32_t)offsetof(BloomFilter__storage_, nFlags), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[BloomFilter class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(BloomFilter__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - TransactionsWithProofsResponse + +@implementation TransactionsWithProofsResponse + +@dynamic responsesOneOfCase; +@dynamic rawTransactions; +@dynamic instantSendLockMessages; +@dynamic rawMerkleBlock; + +typedef struct TransactionsWithProofsResponse__storage_ { + uint32_t _has_storage_[2]; + RawTransactions *rawTransactions; + InstantSendLockMessages *instantSendLockMessages; + NSData *rawMerkleBlock; +} TransactionsWithProofsResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "rawTransactions", + .dataTypeSpecific.clazz = GPBObjCClass(RawTransactions), + .number = TransactionsWithProofsResponse_FieldNumber_RawTransactions, + .hasIndex = -1, + .offset = (uint32_t)offsetof(TransactionsWithProofsResponse__storage_, rawTransactions), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "instantSendLockMessages", + .dataTypeSpecific.clazz = GPBObjCClass(InstantSendLockMessages), + .number = TransactionsWithProofsResponse_FieldNumber_InstantSendLockMessages, + .hasIndex = -1, + .offset = (uint32_t)offsetof(TransactionsWithProofsResponse__storage_, instantSendLockMessages), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "rawMerkleBlock", + .dataTypeSpecific.clazz = Nil, + .number = TransactionsWithProofsResponse_FieldNumber_RawMerkleBlock, + .hasIndex = -1, + .offset = (uint32_t)offsetof(TransactionsWithProofsResponse__storage_, rawMerkleBlock), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeBytes, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[TransactionsWithProofsResponse class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(TransactionsWithProofsResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + static const char *oneofs[] = { + "responses", + }; + [localDescriptor setupOneofs:oneofs + count:(uint32_t)(sizeof(oneofs) / sizeof(char*)) + firstHasIndex:-1]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +void TransactionsWithProofsResponse_ClearResponsesOneOfCase(TransactionsWithProofsResponse *message) { + GPBDescriptor *descriptor = [TransactionsWithProofsResponse descriptor]; + GPBOneofDescriptor *oneof = [descriptor.oneofs objectAtIndex:0]; + GPBClearOneof(message, oneof); +} +#pragma mark - RawTransactions + +@implementation RawTransactions + +@dynamic transactionsArray, transactionsArray_Count; + +typedef struct RawTransactions__storage_ { + uint32_t _has_storage_[1]; + NSMutableArray *transactionsArray; +} RawTransactions__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "transactionsArray", + .dataTypeSpecific.clazz = Nil, + .number = RawTransactions_FieldNumber_TransactionsArray, + .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(RawTransactions__storage_, transactionsArray), + .flags = GPBFieldRepeated, + .dataType = GPBDataTypeBytes, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[RawTransactions class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(RawTransactions__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - InstantSendLockMessages + +@implementation InstantSendLockMessages + +@dynamic messagesArray, messagesArray_Count; + +typedef struct InstantSendLockMessages__storage_ { + uint32_t _has_storage_[1]; + NSMutableArray *messagesArray; +} InstantSendLockMessages__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "messagesArray", + .dataTypeSpecific.clazz = Nil, + .number = InstantSendLockMessages_FieldNumber_MessagesArray, + .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(InstantSendLockMessages__storage_, messagesArray), + .flags = GPBFieldRepeated, + .dataType = GPBDataTypeBytes, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[InstantSendLockMessages class] + rootClass:[CoreRoot class] + file:CoreRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(InstantSendLockMessages__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + + +#pragma clang diagnostic pop + +// @@protoc_insertion_point(global_scope) diff --git a/packages/dapi-grpc/clients/core/v0/objective-c/Core.pbrpc.h b/packages/dapi-grpc/clients/core/v0/objective-c/Core.pbrpc.h new file mode 100644 index 00000000000..a5016dbd657 --- /dev/null +++ b/packages/dapi-grpc/clients/core/v0/objective-c/Core.pbrpc.h @@ -0,0 +1,149 @@ +// Code generated by gRPC proto compiler. DO NOT EDIT! +// source: core.proto + +#import + +#if !defined(GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO) || !GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO +#import "Core.pbobjc.h" +#endif + +#if !defined(GPB_GRPC_PROTOCOL_ONLY) || !GPB_GRPC_PROTOCOL_ONLY +#import +#import +#import +#import +#endif + +@class BlockHeadersWithChainLocksRequest; +@class BlockHeadersWithChainLocksResponse; +@class BroadcastTransactionRequest; +@class BroadcastTransactionResponse; +@class GetBlockRequest; +@class GetBlockResponse; +@class GetEstimatedTransactionFeeRequest; +@class GetEstimatedTransactionFeeResponse; +@class GetStatusRequest; +@class GetStatusResponse; +@class GetTransactionRequest; +@class GetTransactionResponse; +@class TransactionsWithProofsRequest; +@class TransactionsWithProofsResponse; + +#if !defined(GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO) || !GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO +#endif + +@class GRPCUnaryProtoCall; +@class GRPCStreamingProtoCall; +@class GRPCCallOptions; +@protocol GRPCProtoResponseHandler; +@class GRPCProtoCall; + + +NS_ASSUME_NONNULL_BEGIN + +@protocol Core2 + +#pragma mark getStatus(GetStatusRequest) returns (GetStatusResponse) + +- (GRPCUnaryProtoCall *)getStatusWithMessage:(GetStatusRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +#pragma mark getBlock(GetBlockRequest) returns (GetBlockResponse) + +- (GRPCUnaryProtoCall *)getBlockWithMessage:(GetBlockRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +#pragma mark broadcastTransaction(BroadcastTransactionRequest) returns (BroadcastTransactionResponse) + +- (GRPCUnaryProtoCall *)broadcastTransactionWithMessage:(BroadcastTransactionRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +#pragma mark getTransaction(GetTransactionRequest) returns (GetTransactionResponse) + +- (GRPCUnaryProtoCall *)getTransactionWithMessage:(GetTransactionRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +#pragma mark getEstimatedTransactionFee(GetEstimatedTransactionFeeRequest) returns (GetEstimatedTransactionFeeResponse) + +- (GRPCUnaryProtoCall *)getEstimatedTransactionFeeWithMessage:(GetEstimatedTransactionFeeRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +#pragma mark subscribeToBlockHeadersWithChainLocks(BlockHeadersWithChainLocksRequest) returns (stream BlockHeadersWithChainLocksResponse) + +- (GRPCUnaryProtoCall *)subscribeToBlockHeadersWithChainLocksWithMessage:(BlockHeadersWithChainLocksRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +#pragma mark subscribeToTransactionsWithProofs(TransactionsWithProofsRequest) returns (stream TransactionsWithProofsResponse) + +- (GRPCUnaryProtoCall *)subscribeToTransactionsWithProofsWithMessage:(TransactionsWithProofsRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +@end + +/** + * The methods in this protocol belong to a set of old APIs that have been deprecated. They do not + * recognize call options provided in the initializer. Using the v2 protocol is recommended. + */ +@protocol Core + +#pragma mark getStatus(GetStatusRequest) returns (GetStatusResponse) + +- (void)getStatusWithRequest:(GetStatusRequest *)request handler:(void(^)(GetStatusResponse *_Nullable response, NSError *_Nullable error))handler; + +- (GRPCProtoCall *)RPCTogetStatusWithRequest:(GetStatusRequest *)request handler:(void(^)(GetStatusResponse *_Nullable response, NSError *_Nullable error))handler; + + +#pragma mark getBlock(GetBlockRequest) returns (GetBlockResponse) + +- (void)getBlockWithRequest:(GetBlockRequest *)request handler:(void(^)(GetBlockResponse *_Nullable response, NSError *_Nullable error))handler; + +- (GRPCProtoCall *)RPCTogetBlockWithRequest:(GetBlockRequest *)request handler:(void(^)(GetBlockResponse *_Nullable response, NSError *_Nullable error))handler; + + +#pragma mark broadcastTransaction(BroadcastTransactionRequest) returns (BroadcastTransactionResponse) + +- (void)broadcastTransactionWithRequest:(BroadcastTransactionRequest *)request handler:(void(^)(BroadcastTransactionResponse *_Nullable response, NSError *_Nullable error))handler; + +- (GRPCProtoCall *)RPCTobroadcastTransactionWithRequest:(BroadcastTransactionRequest *)request handler:(void(^)(BroadcastTransactionResponse *_Nullable response, NSError *_Nullable error))handler; + + +#pragma mark getTransaction(GetTransactionRequest) returns (GetTransactionResponse) + +- (void)getTransactionWithRequest:(GetTransactionRequest *)request handler:(void(^)(GetTransactionResponse *_Nullable response, NSError *_Nullable error))handler; + +- (GRPCProtoCall *)RPCTogetTransactionWithRequest:(GetTransactionRequest *)request handler:(void(^)(GetTransactionResponse *_Nullable response, NSError *_Nullable error))handler; + + +#pragma mark getEstimatedTransactionFee(GetEstimatedTransactionFeeRequest) returns (GetEstimatedTransactionFeeResponse) + +- (void)getEstimatedTransactionFeeWithRequest:(GetEstimatedTransactionFeeRequest *)request handler:(void(^)(GetEstimatedTransactionFeeResponse *_Nullable response, NSError *_Nullable error))handler; + +- (GRPCProtoCall *)RPCTogetEstimatedTransactionFeeWithRequest:(GetEstimatedTransactionFeeRequest *)request handler:(void(^)(GetEstimatedTransactionFeeResponse *_Nullable response, NSError *_Nullable error))handler; + + +#pragma mark subscribeToBlockHeadersWithChainLocks(BlockHeadersWithChainLocksRequest) returns (stream BlockHeadersWithChainLocksResponse) + +- (void)subscribeToBlockHeadersWithChainLocksWithRequest:(BlockHeadersWithChainLocksRequest *)request eventHandler:(void(^)(BOOL done, BlockHeadersWithChainLocksResponse *_Nullable response, NSError *_Nullable error))eventHandler; + +- (GRPCProtoCall *)RPCTosubscribeToBlockHeadersWithChainLocksWithRequest:(BlockHeadersWithChainLocksRequest *)request eventHandler:(void(^)(BOOL done, BlockHeadersWithChainLocksResponse *_Nullable response, NSError *_Nullable error))eventHandler; + + +#pragma mark subscribeToTransactionsWithProofs(TransactionsWithProofsRequest) returns (stream TransactionsWithProofsResponse) + +- (void)subscribeToTransactionsWithProofsWithRequest:(TransactionsWithProofsRequest *)request eventHandler:(void(^)(BOOL done, TransactionsWithProofsResponse *_Nullable response, NSError *_Nullable error))eventHandler; + +- (GRPCProtoCall *)RPCTosubscribeToTransactionsWithProofsWithRequest:(TransactionsWithProofsRequest *)request eventHandler:(void(^)(BOOL done, TransactionsWithProofsResponse *_Nullable response, NSError *_Nullable error))eventHandler; + + +@end + + +#if !defined(GPB_GRPC_PROTOCOL_ONLY) || !GPB_GRPC_PROTOCOL_ONLY +/** + * Basic service implementation, over gRPC, that only does + * marshalling and parsing. + */ +@interface Core : GRPCProtoService +- (instancetype)initWithHost:(NSString *)host callOptions:(GRPCCallOptions *_Nullable)callOptions NS_DESIGNATED_INITIALIZER; ++ (instancetype)serviceWithHost:(NSString *)host callOptions:(GRPCCallOptions *_Nullable)callOptions; +// The following methods belong to a set of old APIs that have been deprecated. +- (instancetype)initWithHost:(NSString *)host; ++ (instancetype)serviceWithHost:(NSString *)host; +@end +#endif + +NS_ASSUME_NONNULL_END + diff --git a/packages/dapi-grpc/clients/core/v0/objective-c/Core.pbrpc.m b/packages/dapi-grpc/clients/core/v0/objective-c/Core.pbrpc.m new file mode 100644 index 00000000000..2c16c550ac9 --- /dev/null +++ b/packages/dapi-grpc/clients/core/v0/objective-c/Core.pbrpc.m @@ -0,0 +1,199 @@ +// Code generated by gRPC proto compiler. DO NOT EDIT! +// source: core.proto + +#if !defined(GPB_GRPC_PROTOCOL_ONLY) || !GPB_GRPC_PROTOCOL_ONLY +#import "Core.pbrpc.h" +#import "Core.pbobjc.h" +#import +#import + + +@implementation Core + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" + +// Designated initializer +- (instancetype)initWithHost:(NSString *)host callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [super initWithHost:host + packageName:@"org.dash.platform.dapi.v0" + serviceName:@"Core" + callOptions:callOptions]; +} + +- (instancetype)initWithHost:(NSString *)host { + return [super initWithHost:host + packageName:@"org.dash.platform.dapi.v0" + serviceName:@"Core"]; +} + +#pragma clang diagnostic pop + +// Override superclass initializer to disallow different package and service names. +- (instancetype)initWithHost:(NSString *)host + packageName:(NSString *)packageName + serviceName:(NSString *)serviceName { + return [self initWithHost:host]; +} + +- (instancetype)initWithHost:(NSString *)host + packageName:(NSString *)packageName + serviceName:(NSString *)serviceName + callOptions:(GRPCCallOptions *)callOptions { + return [self initWithHost:host callOptions:callOptions]; +} + +#pragma mark - Class Methods + ++ (instancetype)serviceWithHost:(NSString *)host { + return [[self alloc] initWithHost:host]; +} + ++ (instancetype)serviceWithHost:(NSString *)host callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [[self alloc] initWithHost:host callOptions:callOptions]; +} + +#pragma mark - Method Implementations + +#pragma mark getStatus(GetStatusRequest) returns (GetStatusResponse) + +- (void)getStatusWithRequest:(GetStatusRequest *)request handler:(void(^)(GetStatusResponse *_Nullable response, NSError *_Nullable error))handler{ + [[self RPCTogetStatusWithRequest:request handler:handler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTogetStatusWithRequest:(GetStatusRequest *)request handler:(void(^)(GetStatusResponse *_Nullable response, NSError *_Nullable error))handler{ + return [self RPCToMethod:@"getStatus" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[GetStatusResponse class] + responsesWriteable:[GRXWriteable writeableWithSingleHandler:handler]]; +} +- (GRPCUnaryProtoCall *)getStatusWithMessage:(GetStatusRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"getStatus" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[GetStatusResponse class]]; +} + +#pragma mark getBlock(GetBlockRequest) returns (GetBlockResponse) + +- (void)getBlockWithRequest:(GetBlockRequest *)request handler:(void(^)(GetBlockResponse *_Nullable response, NSError *_Nullable error))handler{ + [[self RPCTogetBlockWithRequest:request handler:handler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTogetBlockWithRequest:(GetBlockRequest *)request handler:(void(^)(GetBlockResponse *_Nullable response, NSError *_Nullable error))handler{ + return [self RPCToMethod:@"getBlock" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[GetBlockResponse class] + responsesWriteable:[GRXWriteable writeableWithSingleHandler:handler]]; +} +- (GRPCUnaryProtoCall *)getBlockWithMessage:(GetBlockRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"getBlock" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[GetBlockResponse class]]; +} + +#pragma mark broadcastTransaction(BroadcastTransactionRequest) returns (BroadcastTransactionResponse) + +- (void)broadcastTransactionWithRequest:(BroadcastTransactionRequest *)request handler:(void(^)(BroadcastTransactionResponse *_Nullable response, NSError *_Nullable error))handler{ + [[self RPCTobroadcastTransactionWithRequest:request handler:handler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTobroadcastTransactionWithRequest:(BroadcastTransactionRequest *)request handler:(void(^)(BroadcastTransactionResponse *_Nullable response, NSError *_Nullable error))handler{ + return [self RPCToMethod:@"broadcastTransaction" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[BroadcastTransactionResponse class] + responsesWriteable:[GRXWriteable writeableWithSingleHandler:handler]]; +} +- (GRPCUnaryProtoCall *)broadcastTransactionWithMessage:(BroadcastTransactionRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"broadcastTransaction" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[BroadcastTransactionResponse class]]; +} + +#pragma mark getTransaction(GetTransactionRequest) returns (GetTransactionResponse) + +- (void)getTransactionWithRequest:(GetTransactionRequest *)request handler:(void(^)(GetTransactionResponse *_Nullable response, NSError *_Nullable error))handler{ + [[self RPCTogetTransactionWithRequest:request handler:handler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTogetTransactionWithRequest:(GetTransactionRequest *)request handler:(void(^)(GetTransactionResponse *_Nullable response, NSError *_Nullable error))handler{ + return [self RPCToMethod:@"getTransaction" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[GetTransactionResponse class] + responsesWriteable:[GRXWriteable writeableWithSingleHandler:handler]]; +} +- (GRPCUnaryProtoCall *)getTransactionWithMessage:(GetTransactionRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"getTransaction" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[GetTransactionResponse class]]; +} + +#pragma mark getEstimatedTransactionFee(GetEstimatedTransactionFeeRequest) returns (GetEstimatedTransactionFeeResponse) + +- (void)getEstimatedTransactionFeeWithRequest:(GetEstimatedTransactionFeeRequest *)request handler:(void(^)(GetEstimatedTransactionFeeResponse *_Nullable response, NSError *_Nullable error))handler{ + [[self RPCTogetEstimatedTransactionFeeWithRequest:request handler:handler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTogetEstimatedTransactionFeeWithRequest:(GetEstimatedTransactionFeeRequest *)request handler:(void(^)(GetEstimatedTransactionFeeResponse *_Nullable response, NSError *_Nullable error))handler{ + return [self RPCToMethod:@"getEstimatedTransactionFee" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[GetEstimatedTransactionFeeResponse class] + responsesWriteable:[GRXWriteable writeableWithSingleHandler:handler]]; +} +- (GRPCUnaryProtoCall *)getEstimatedTransactionFeeWithMessage:(GetEstimatedTransactionFeeRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"getEstimatedTransactionFee" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[GetEstimatedTransactionFeeResponse class]]; +} + +#pragma mark subscribeToBlockHeadersWithChainLocks(BlockHeadersWithChainLocksRequest) returns (stream BlockHeadersWithChainLocksResponse) + +- (void)subscribeToBlockHeadersWithChainLocksWithRequest:(BlockHeadersWithChainLocksRequest *)request eventHandler:(void(^)(BOOL done, BlockHeadersWithChainLocksResponse *_Nullable response, NSError *_Nullable error))eventHandler{ + [[self RPCTosubscribeToBlockHeadersWithChainLocksWithRequest:request eventHandler:eventHandler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTosubscribeToBlockHeadersWithChainLocksWithRequest:(BlockHeadersWithChainLocksRequest *)request eventHandler:(void(^)(BOOL done, BlockHeadersWithChainLocksResponse *_Nullable response, NSError *_Nullable error))eventHandler{ + return [self RPCToMethod:@"subscribeToBlockHeadersWithChainLocks" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[BlockHeadersWithChainLocksResponse class] + responsesWriteable:[GRXWriteable writeableWithEventHandler:eventHandler]]; +} +- (GRPCUnaryProtoCall *)subscribeToBlockHeadersWithChainLocksWithMessage:(BlockHeadersWithChainLocksRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"subscribeToBlockHeadersWithChainLocks" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[BlockHeadersWithChainLocksResponse class]]; +} + +#pragma mark subscribeToTransactionsWithProofs(TransactionsWithProofsRequest) returns (stream TransactionsWithProofsResponse) + +- (void)subscribeToTransactionsWithProofsWithRequest:(TransactionsWithProofsRequest *)request eventHandler:(void(^)(BOOL done, TransactionsWithProofsResponse *_Nullable response, NSError *_Nullable error))eventHandler{ + [[self RPCTosubscribeToTransactionsWithProofsWithRequest:request eventHandler:eventHandler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTosubscribeToTransactionsWithProofsWithRequest:(TransactionsWithProofsRequest *)request eventHandler:(void(^)(BOOL done, TransactionsWithProofsResponse *_Nullable response, NSError *_Nullable error))eventHandler{ + return [self RPCToMethod:@"subscribeToTransactionsWithProofs" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[TransactionsWithProofsResponse class] + responsesWriteable:[GRXWriteable writeableWithEventHandler:eventHandler]]; +} +- (GRPCUnaryProtoCall *)subscribeToTransactionsWithProofsWithMessage:(TransactionsWithProofsRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"subscribeToTransactionsWithProofs" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[TransactionsWithProofsResponse class]]; +} + +@end +#endif diff --git a/packages/dapi-grpc/clients/core/v0/python/core_pb2.py b/packages/dapi-grpc/clients/core/v0/python/core_pb2.py new file mode 100644 index 00000000000..cc79763de81 --- /dev/null +++ b/packages/dapi-grpc/clients/core/v0/python/core_pb2.py @@ -0,0 +1,1534 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: core.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='core.proto', + package='org.dash.platform.dapi.v0', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\ncore.proto\x12\x19org.dash.platform.dapi.v0\"\x12\n\x10GetStatusRequest\"\x8e\n\n\x11GetStatusResponse\x12\x45\n\x07version\x18\x01 \x01(\x0b\x32\x34.org.dash.platform.dapi.v0.GetStatusResponse.Version\x12?\n\x04time\x18\x02 \x01(\x0b\x32\x31.org.dash.platform.dapi.v0.GetStatusResponse.Time\x12\x43\n\x06status\x18\x03 \x01(\x0e\x32\x33.org.dash.platform.dapi.v0.GetStatusResponse.Status\x12\x15\n\rsync_progress\x18\x04 \x01(\x01\x12\x41\n\x05\x63hain\x18\x05 \x01(\x0b\x32\x32.org.dash.platform.dapi.v0.GetStatusResponse.Chain\x12K\n\nmasternode\x18\x06 \x01(\x0b\x32\x37.org.dash.platform.dapi.v0.GetStatusResponse.Masternode\x12\x45\n\x07network\x18\x07 \x01(\x0b\x32\x34.org.dash.platform.dapi.v0.GetStatusResponse.Network\x1a<\n\x07Version\x12\x10\n\x08protocol\x18\x01 \x01(\r\x12\x10\n\x08software\x18\x02 \x01(\r\x12\r\n\x05\x61gent\x18\x03 \x01(\t\x1a\x33\n\x04Time\x12\x0b\n\x03now\x18\x01 \x01(\r\x12\x0e\n\x06offset\x18\x02 \x01(\x05\x12\x0e\n\x06median\x18\x03 \x01(\r\x1a\xad\x01\n\x05\x43hain\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x15\n\rheaders_count\x18\x02 \x01(\r\x12\x14\n\x0c\x62locks_count\x18\x03 \x01(\r\x12\x17\n\x0f\x62\x65st_block_hash\x18\x04 \x01(\x0c\x12\x12\n\ndifficulty\x18\x05 \x01(\x01\x12\x12\n\nchain_work\x18\x06 \x01(\x0c\x12\x11\n\tis_synced\x18\x07 \x01(\x08\x12\x15\n\rsync_progress\x18\x08 \x01(\x01\x1a\xc4\x02\n\nMasternode\x12N\n\x06status\x18\x01 \x01(\x0e\x32>.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status\x12\x13\n\x0bpro_tx_hash\x18\x02 \x01(\x0c\x12\x14\n\x0cpose_penalty\x18\x03 \x01(\r\x12\x11\n\tis_synced\x18\x04 \x01(\x08\x12\x15\n\rsync_progress\x18\x05 \x01(\x01\"\x90\x01\n\x06Status\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x15\n\x11WAITING_FOR_PROTX\x10\x01\x12\x0f\n\x0bPOSE_BANNED\x10\x02\x12\x0b\n\x07REMOVED\x10\x03\x12\x18\n\x14OPERATOR_KEY_CHANGED\x10\x04\x12\x14\n\x10PROTX_IP_CHANGED\x10\x05\x12\t\n\x05READY\x10\x06\x12\t\n\x05\x45RROR\x10\x07\x1a\x30\n\nNetworkFee\x12\r\n\x05relay\x18\x01 \x01(\x01\x12\x13\n\x0bincremental\x18\x02 \x01(\x01\x1a\x64\n\x07Network\x12\x13\n\x0bpeers_count\x18\x01 \x01(\r\x12\x44\n\x03\x66\x65\x65\x18\x02 \x01(\x0b\x32\x37.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee\"<\n\x06Status\x12\x0f\n\x0bNOT_STARTED\x10\x00\x12\x0b\n\x07SYNCING\x10\x01\x12\t\n\x05READY\x10\x02\x12\t\n\x05\x45RROR\x10\x03\"<\n\x0fGetBlockRequest\x12\x10\n\x06height\x18\x01 \x01(\rH\x00\x12\x0e\n\x04hash\x18\x02 \x01(\tH\x00\x42\x07\n\x05\x62lock\"!\n\x10GetBlockResponse\x12\r\n\x05\x62lock\x18\x01 \x01(\x0c\"b\n\x1b\x42roadcastTransactionRequest\x12\x13\n\x0btransaction\x18\x01 \x01(\x0c\x12\x17\n\x0f\x61llow_high_fees\x18\x02 \x01(\x08\x12\x15\n\rbypass_limits\x18\x03 \x01(\x08\"6\n\x1c\x42roadcastTransactionResponse\x12\x16\n\x0etransaction_id\x18\x01 \x01(\t\"#\n\x15GetTransactionRequest\x12\n\n\x02id\x18\x01 \x01(\t\"\x9c\x01\n\x16GetTransactionResponse\x12\x13\n\x0btransaction\x18\x01 \x01(\x0c\x12\x12\n\nblock_hash\x18\x02 \x01(\x0c\x12\x0e\n\x06height\x18\x03 \x01(\r\x12\x15\n\rconfirmations\x18\x04 \x01(\r\x12\x19\n\x11is_instant_locked\x18\x05 \x01(\x08\x12\x17\n\x0fis_chain_locked\x18\x06 \x01(\x08\"x\n!BlockHeadersWithChainLocksRequest\x12\x19\n\x0f\x66rom_block_hash\x18\x01 \x01(\x0cH\x00\x12\x1b\n\x11\x66rom_block_height\x18\x02 \x01(\rH\x00\x12\r\n\x05\x63ount\x18\x03 \x01(\rB\x0c\n\nfrom_block\"\x89\x01\n\"BlockHeadersWithChainLocksResponse\x12@\n\rblock_headers\x18\x01 \x01(\x0b\x32\'.org.dash.platform.dapi.v0.BlockHeadersH\x00\x12\x14\n\nchain_lock\x18\x02 \x01(\x0cH\x00\x42\x0b\n\tresponses\"\x1f\n\x0c\x42lockHeaders\x12\x0f\n\x07headers\x18\x01 \x03(\x0c\"3\n!GetEstimatedTransactionFeeRequest\x12\x0e\n\x06\x62locks\x18\x01 \x01(\r\"1\n\"GetEstimatedTransactionFeeResponse\x12\x0b\n\x03\x66\x65\x65\x18\x01 \x01(\x01\"\xd3\x01\n\x1dTransactionsWithProofsRequest\x12<\n\x0c\x62loom_filter\x18\x01 \x01(\x0b\x32&.org.dash.platform.dapi.v0.BloomFilter\x12\x19\n\x0f\x66rom_block_hash\x18\x02 \x01(\x0cH\x00\x12\x1b\n\x11\x66rom_block_height\x18\x03 \x01(\rH\x00\x12\r\n\x05\x63ount\x18\x04 \x01(\r\x12\x1f\n\x17send_transaction_hashes\x18\x05 \x01(\x08\x42\x0c\n\nfrom_block\"U\n\x0b\x42loomFilter\x12\x0e\n\x06v_data\x18\x01 \x01(\x0c\x12\x14\n\x0cn_hash_funcs\x18\x02 \x01(\r\x12\x0f\n\x07n_tweak\x18\x03 \x01(\r\x12\x0f\n\x07n_flags\x18\x04 \x01(\r\"\xeb\x01\n\x1eTransactionsWithProofsResponse\x12\x46\n\x10raw_transactions\x18\x01 \x01(\x0b\x32*.org.dash.platform.dapi.v0.RawTransactionsH\x00\x12X\n\x1ainstant_send_lock_messages\x18\x02 \x01(\x0b\x32\x32.org.dash.platform.dapi.v0.InstantSendLockMessagesH\x00\x12\x1a\n\x10raw_merkle_block\x18\x03 \x01(\x0cH\x00\x42\x0b\n\tresponses\"\'\n\x0fRawTransactions\x12\x14\n\x0ctransactions\x18\x01 \x03(\x0c\"+\n\x17InstantSendLockMessages\x12\x10\n\x08messages\x18\x01 \x03(\x0c\x32\xb6\x07\n\x04\x43ore\x12\x66\n\tgetStatus\x12+.org.dash.platform.dapi.v0.GetStatusRequest\x1a,.org.dash.platform.dapi.v0.GetStatusResponse\x12\x63\n\x08getBlock\x12*.org.dash.platform.dapi.v0.GetBlockRequest\x1a+.org.dash.platform.dapi.v0.GetBlockResponse\x12\x87\x01\n\x14\x62roadcastTransaction\x12\x36.org.dash.platform.dapi.v0.BroadcastTransactionRequest\x1a\x37.org.dash.platform.dapi.v0.BroadcastTransactionResponse\x12u\n\x0egetTransaction\x12\x30.org.dash.platform.dapi.v0.GetTransactionRequest\x1a\x31.org.dash.platform.dapi.v0.GetTransactionResponse\x12\x99\x01\n\x1agetEstimatedTransactionFee\x12<.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest\x1a=.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse\x12\xa6\x01\n%subscribeToBlockHeadersWithChainLocks\x12<.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest\x1a=.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse0\x01\x12\x9a\x01\n!subscribeToTransactionsWithProofs\x12\x38.org.dash.platform.dapi.v0.TransactionsWithProofsRequest\x1a\x39.org.dash.platform.dapi.v0.TransactionsWithProofsResponse0\x01\x62\x06proto3' +) + + + +_GETSTATUSRESPONSE_MASTERNODE_STATUS = _descriptor.EnumDescriptor( + name='Status', + full_name='org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='WAITING_FOR_PROTX', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='POSE_BANNED', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='REMOVED', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OPERATOR_KEY_CHANGED', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PROTX_IP_CHANGED', index=5, number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='READY', index=6, number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='ERROR', index=7, number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=998, + serialized_end=1142, +) +_sym_db.RegisterEnumDescriptor(_GETSTATUSRESPONSE_MASTERNODE_STATUS) + +_GETSTATUSRESPONSE_STATUS = _descriptor.EnumDescriptor( + name='Status', + full_name='org.dash.platform.dapi.v0.GetStatusResponse.Status', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='NOT_STARTED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SYNCING', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='READY', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='ERROR', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=1296, + serialized_end=1356, +) +_sym_db.RegisterEnumDescriptor(_GETSTATUSRESPONSE_STATUS) + + +_GETSTATUSREQUEST = _descriptor.Descriptor( + name='GetStatusRequest', + full_name='org.dash.platform.dapi.v0.GetStatusRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=41, + serialized_end=59, +) + + +_GETSTATUSRESPONSE_VERSION = _descriptor.Descriptor( + name='Version', + full_name='org.dash.platform.dapi.v0.GetStatusResponse.Version', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='protocol', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Version.protocol', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='software', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Version.software', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='agent', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Version.agent', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=526, + serialized_end=586, +) + +_GETSTATUSRESPONSE_TIME = _descriptor.Descriptor( + name='Time', + full_name='org.dash.platform.dapi.v0.GetStatusResponse.Time', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='now', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Time.now', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='offset', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Time.offset', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='median', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Time.median', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=588, + serialized_end=639, +) + +_GETSTATUSRESPONSE_CHAIN = _descriptor.Descriptor( + name='Chain', + full_name='org.dash.platform.dapi.v0.GetStatusResponse.Chain', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Chain.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='headers_count', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Chain.headers_count', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='blocks_count', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Chain.blocks_count', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='best_block_hash', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Chain.best_block_hash', index=3, + number=4, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='difficulty', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Chain.difficulty', index=4, + number=5, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='chain_work', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Chain.chain_work', index=5, + number=6, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_synced', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Chain.is_synced', index=6, + number=7, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='sync_progress', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Chain.sync_progress', index=7, + number=8, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=642, + serialized_end=815, +) + +_GETSTATUSRESPONSE_MASTERNODE = _descriptor.Descriptor( + name='Masternode', + full_name='org.dash.platform.dapi.v0.GetStatusResponse.Masternode', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='status', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Masternode.status', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='pro_tx_hash', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Masternode.pro_tx_hash', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='pose_penalty', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Masternode.pose_penalty', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_synced', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Masternode.is_synced', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='sync_progress', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Masternode.sync_progress', index=4, + number=5, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _GETSTATUSRESPONSE_MASTERNODE_STATUS, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=818, + serialized_end=1142, +) + +_GETSTATUSRESPONSE_NETWORKFEE = _descriptor.Descriptor( + name='NetworkFee', + full_name='org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='relay', full_name='org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.relay', index=0, + number=1, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='incremental', full_name='org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.incremental', index=1, + number=2, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1144, + serialized_end=1192, +) + +_GETSTATUSRESPONSE_NETWORK = _descriptor.Descriptor( + name='Network', + full_name='org.dash.platform.dapi.v0.GetStatusResponse.Network', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='peers_count', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Network.peers_count', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='fee', full_name='org.dash.platform.dapi.v0.GetStatusResponse.Network.fee', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1194, + serialized_end=1294, +) + +_GETSTATUSRESPONSE = _descriptor.Descriptor( + name='GetStatusResponse', + full_name='org.dash.platform.dapi.v0.GetStatusResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='version', full_name='org.dash.platform.dapi.v0.GetStatusResponse.version', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='time', full_name='org.dash.platform.dapi.v0.GetStatusResponse.time', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='status', full_name='org.dash.platform.dapi.v0.GetStatusResponse.status', index=2, + number=3, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='sync_progress', full_name='org.dash.platform.dapi.v0.GetStatusResponse.sync_progress', index=3, + number=4, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='chain', full_name='org.dash.platform.dapi.v0.GetStatusResponse.chain', index=4, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='masternode', full_name='org.dash.platform.dapi.v0.GetStatusResponse.masternode', index=5, + number=6, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='network', full_name='org.dash.platform.dapi.v0.GetStatusResponse.network', index=6, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_GETSTATUSRESPONSE_VERSION, _GETSTATUSRESPONSE_TIME, _GETSTATUSRESPONSE_CHAIN, _GETSTATUSRESPONSE_MASTERNODE, _GETSTATUSRESPONSE_NETWORKFEE, _GETSTATUSRESPONSE_NETWORK, ], + enum_types=[ + _GETSTATUSRESPONSE_STATUS, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=62, + serialized_end=1356, +) + + +_GETBLOCKREQUEST = _descriptor.Descriptor( + name='GetBlockRequest', + full_name='org.dash.platform.dapi.v0.GetBlockRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='height', full_name='org.dash.platform.dapi.v0.GetBlockRequest.height', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='hash', full_name='org.dash.platform.dapi.v0.GetBlockRequest.hash', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='block', full_name='org.dash.platform.dapi.v0.GetBlockRequest.block', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=1358, + serialized_end=1418, +) + + +_GETBLOCKRESPONSE = _descriptor.Descriptor( + name='GetBlockResponse', + full_name='org.dash.platform.dapi.v0.GetBlockResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='block', full_name='org.dash.platform.dapi.v0.GetBlockResponse.block', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1420, + serialized_end=1453, +) + + +_BROADCASTTRANSACTIONREQUEST = _descriptor.Descriptor( + name='BroadcastTransactionRequest', + full_name='org.dash.platform.dapi.v0.BroadcastTransactionRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='transaction', full_name='org.dash.platform.dapi.v0.BroadcastTransactionRequest.transaction', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='allow_high_fees', full_name='org.dash.platform.dapi.v0.BroadcastTransactionRequest.allow_high_fees', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='bypass_limits', full_name='org.dash.platform.dapi.v0.BroadcastTransactionRequest.bypass_limits', index=2, + number=3, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1455, + serialized_end=1553, +) + + +_BROADCASTTRANSACTIONRESPONSE = _descriptor.Descriptor( + name='BroadcastTransactionResponse', + full_name='org.dash.platform.dapi.v0.BroadcastTransactionResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='transaction_id', full_name='org.dash.platform.dapi.v0.BroadcastTransactionResponse.transaction_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1555, + serialized_end=1609, +) + + +_GETTRANSACTIONREQUEST = _descriptor.Descriptor( + name='GetTransactionRequest', + full_name='org.dash.platform.dapi.v0.GetTransactionRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='org.dash.platform.dapi.v0.GetTransactionRequest.id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1611, + serialized_end=1646, +) + + +_GETTRANSACTIONRESPONSE = _descriptor.Descriptor( + name='GetTransactionResponse', + full_name='org.dash.platform.dapi.v0.GetTransactionResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='transaction', full_name='org.dash.platform.dapi.v0.GetTransactionResponse.transaction', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='block_hash', full_name='org.dash.platform.dapi.v0.GetTransactionResponse.block_hash', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='height', full_name='org.dash.platform.dapi.v0.GetTransactionResponse.height', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='confirmations', full_name='org.dash.platform.dapi.v0.GetTransactionResponse.confirmations', index=3, + number=4, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_instant_locked', full_name='org.dash.platform.dapi.v0.GetTransactionResponse.is_instant_locked', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_chain_locked', full_name='org.dash.platform.dapi.v0.GetTransactionResponse.is_chain_locked', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1649, + serialized_end=1805, +) + + +_BLOCKHEADERSWITHCHAINLOCKSREQUEST = _descriptor.Descriptor( + name='BlockHeadersWithChainLocksRequest', + full_name='org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='from_block_hash', full_name='org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.from_block_hash', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='from_block_height', full_name='org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.from_block_height', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='count', full_name='org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.count', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='from_block', full_name='org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.from_block', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=1807, + serialized_end=1927, +) + + +_BLOCKHEADERSWITHCHAINLOCKSRESPONSE = _descriptor.Descriptor( + name='BlockHeadersWithChainLocksResponse', + full_name='org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='block_headers', full_name='org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.block_headers', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='chain_lock', full_name='org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.chain_lock', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='responses', full_name='org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.responses', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=1930, + serialized_end=2067, +) + + +_BLOCKHEADERS = _descriptor.Descriptor( + name='BlockHeaders', + full_name='org.dash.platform.dapi.v0.BlockHeaders', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='headers', full_name='org.dash.platform.dapi.v0.BlockHeaders.headers', index=0, + number=1, type=12, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2069, + serialized_end=2100, +) + + +_GETESTIMATEDTRANSACTIONFEEREQUEST = _descriptor.Descriptor( + name='GetEstimatedTransactionFeeRequest', + full_name='org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='blocks', full_name='org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.blocks', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2102, + serialized_end=2153, +) + + +_GETESTIMATEDTRANSACTIONFEERESPONSE = _descriptor.Descriptor( + name='GetEstimatedTransactionFeeResponse', + full_name='org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='fee', full_name='org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.fee', index=0, + number=1, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2155, + serialized_end=2204, +) + + +_TRANSACTIONSWITHPROOFSREQUEST = _descriptor.Descriptor( + name='TransactionsWithProofsRequest', + full_name='org.dash.platform.dapi.v0.TransactionsWithProofsRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='bloom_filter', full_name='org.dash.platform.dapi.v0.TransactionsWithProofsRequest.bloom_filter', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='from_block_hash', full_name='org.dash.platform.dapi.v0.TransactionsWithProofsRequest.from_block_hash', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='from_block_height', full_name='org.dash.platform.dapi.v0.TransactionsWithProofsRequest.from_block_height', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='count', full_name='org.dash.platform.dapi.v0.TransactionsWithProofsRequest.count', index=3, + number=4, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='send_transaction_hashes', full_name='org.dash.platform.dapi.v0.TransactionsWithProofsRequest.send_transaction_hashes', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='from_block', full_name='org.dash.platform.dapi.v0.TransactionsWithProofsRequest.from_block', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=2207, + serialized_end=2418, +) + + +_BLOOMFILTER = _descriptor.Descriptor( + name='BloomFilter', + full_name='org.dash.platform.dapi.v0.BloomFilter', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='v_data', full_name='org.dash.platform.dapi.v0.BloomFilter.v_data', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='n_hash_funcs', full_name='org.dash.platform.dapi.v0.BloomFilter.n_hash_funcs', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='n_tweak', full_name='org.dash.platform.dapi.v0.BloomFilter.n_tweak', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='n_flags', full_name='org.dash.platform.dapi.v0.BloomFilter.n_flags', index=3, + number=4, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2420, + serialized_end=2505, +) + + +_TRANSACTIONSWITHPROOFSRESPONSE = _descriptor.Descriptor( + name='TransactionsWithProofsResponse', + full_name='org.dash.platform.dapi.v0.TransactionsWithProofsResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='raw_transactions', full_name='org.dash.platform.dapi.v0.TransactionsWithProofsResponse.raw_transactions', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='instant_send_lock_messages', full_name='org.dash.platform.dapi.v0.TransactionsWithProofsResponse.instant_send_lock_messages', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='raw_merkle_block', full_name='org.dash.platform.dapi.v0.TransactionsWithProofsResponse.raw_merkle_block', index=2, + number=3, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='responses', full_name='org.dash.platform.dapi.v0.TransactionsWithProofsResponse.responses', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=2508, + serialized_end=2743, +) + + +_RAWTRANSACTIONS = _descriptor.Descriptor( + name='RawTransactions', + full_name='org.dash.platform.dapi.v0.RawTransactions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='transactions', full_name='org.dash.platform.dapi.v0.RawTransactions.transactions', index=0, + number=1, type=12, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2745, + serialized_end=2784, +) + + +_INSTANTSENDLOCKMESSAGES = _descriptor.Descriptor( + name='InstantSendLockMessages', + full_name='org.dash.platform.dapi.v0.InstantSendLockMessages', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='messages', full_name='org.dash.platform.dapi.v0.InstantSendLockMessages.messages', index=0, + number=1, type=12, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2786, + serialized_end=2829, +) + +_GETSTATUSRESPONSE_VERSION.containing_type = _GETSTATUSRESPONSE +_GETSTATUSRESPONSE_TIME.containing_type = _GETSTATUSRESPONSE +_GETSTATUSRESPONSE_CHAIN.containing_type = _GETSTATUSRESPONSE +_GETSTATUSRESPONSE_MASTERNODE.fields_by_name['status'].enum_type = _GETSTATUSRESPONSE_MASTERNODE_STATUS +_GETSTATUSRESPONSE_MASTERNODE.containing_type = _GETSTATUSRESPONSE +_GETSTATUSRESPONSE_MASTERNODE_STATUS.containing_type = _GETSTATUSRESPONSE_MASTERNODE +_GETSTATUSRESPONSE_NETWORKFEE.containing_type = _GETSTATUSRESPONSE +_GETSTATUSRESPONSE_NETWORK.fields_by_name['fee'].message_type = _GETSTATUSRESPONSE_NETWORKFEE +_GETSTATUSRESPONSE_NETWORK.containing_type = _GETSTATUSRESPONSE +_GETSTATUSRESPONSE.fields_by_name['version'].message_type = _GETSTATUSRESPONSE_VERSION +_GETSTATUSRESPONSE.fields_by_name['time'].message_type = _GETSTATUSRESPONSE_TIME +_GETSTATUSRESPONSE.fields_by_name['status'].enum_type = _GETSTATUSRESPONSE_STATUS +_GETSTATUSRESPONSE.fields_by_name['chain'].message_type = _GETSTATUSRESPONSE_CHAIN +_GETSTATUSRESPONSE.fields_by_name['masternode'].message_type = _GETSTATUSRESPONSE_MASTERNODE +_GETSTATUSRESPONSE.fields_by_name['network'].message_type = _GETSTATUSRESPONSE_NETWORK +_GETSTATUSRESPONSE_STATUS.containing_type = _GETSTATUSRESPONSE +_GETBLOCKREQUEST.oneofs_by_name['block'].fields.append( + _GETBLOCKREQUEST.fields_by_name['height']) +_GETBLOCKREQUEST.fields_by_name['height'].containing_oneof = _GETBLOCKREQUEST.oneofs_by_name['block'] +_GETBLOCKREQUEST.oneofs_by_name['block'].fields.append( + _GETBLOCKREQUEST.fields_by_name['hash']) +_GETBLOCKREQUEST.fields_by_name['hash'].containing_oneof = _GETBLOCKREQUEST.oneofs_by_name['block'] +_BLOCKHEADERSWITHCHAINLOCKSREQUEST.oneofs_by_name['from_block'].fields.append( + _BLOCKHEADERSWITHCHAINLOCKSREQUEST.fields_by_name['from_block_hash']) +_BLOCKHEADERSWITHCHAINLOCKSREQUEST.fields_by_name['from_block_hash'].containing_oneof = _BLOCKHEADERSWITHCHAINLOCKSREQUEST.oneofs_by_name['from_block'] +_BLOCKHEADERSWITHCHAINLOCKSREQUEST.oneofs_by_name['from_block'].fields.append( + _BLOCKHEADERSWITHCHAINLOCKSREQUEST.fields_by_name['from_block_height']) +_BLOCKHEADERSWITHCHAINLOCKSREQUEST.fields_by_name['from_block_height'].containing_oneof = _BLOCKHEADERSWITHCHAINLOCKSREQUEST.oneofs_by_name['from_block'] +_BLOCKHEADERSWITHCHAINLOCKSRESPONSE.fields_by_name['block_headers'].message_type = _BLOCKHEADERS +_BLOCKHEADERSWITHCHAINLOCKSRESPONSE.oneofs_by_name['responses'].fields.append( + _BLOCKHEADERSWITHCHAINLOCKSRESPONSE.fields_by_name['block_headers']) +_BLOCKHEADERSWITHCHAINLOCKSRESPONSE.fields_by_name['block_headers'].containing_oneof = _BLOCKHEADERSWITHCHAINLOCKSRESPONSE.oneofs_by_name['responses'] +_BLOCKHEADERSWITHCHAINLOCKSRESPONSE.oneofs_by_name['responses'].fields.append( + _BLOCKHEADERSWITHCHAINLOCKSRESPONSE.fields_by_name['chain_lock']) +_BLOCKHEADERSWITHCHAINLOCKSRESPONSE.fields_by_name['chain_lock'].containing_oneof = _BLOCKHEADERSWITHCHAINLOCKSRESPONSE.oneofs_by_name['responses'] +_TRANSACTIONSWITHPROOFSREQUEST.fields_by_name['bloom_filter'].message_type = _BLOOMFILTER +_TRANSACTIONSWITHPROOFSREQUEST.oneofs_by_name['from_block'].fields.append( + _TRANSACTIONSWITHPROOFSREQUEST.fields_by_name['from_block_hash']) +_TRANSACTIONSWITHPROOFSREQUEST.fields_by_name['from_block_hash'].containing_oneof = _TRANSACTIONSWITHPROOFSREQUEST.oneofs_by_name['from_block'] +_TRANSACTIONSWITHPROOFSREQUEST.oneofs_by_name['from_block'].fields.append( + _TRANSACTIONSWITHPROOFSREQUEST.fields_by_name['from_block_height']) +_TRANSACTIONSWITHPROOFSREQUEST.fields_by_name['from_block_height'].containing_oneof = _TRANSACTIONSWITHPROOFSREQUEST.oneofs_by_name['from_block'] +_TRANSACTIONSWITHPROOFSRESPONSE.fields_by_name['raw_transactions'].message_type = _RAWTRANSACTIONS +_TRANSACTIONSWITHPROOFSRESPONSE.fields_by_name['instant_send_lock_messages'].message_type = _INSTANTSENDLOCKMESSAGES +_TRANSACTIONSWITHPROOFSRESPONSE.oneofs_by_name['responses'].fields.append( + _TRANSACTIONSWITHPROOFSRESPONSE.fields_by_name['raw_transactions']) +_TRANSACTIONSWITHPROOFSRESPONSE.fields_by_name['raw_transactions'].containing_oneof = _TRANSACTIONSWITHPROOFSRESPONSE.oneofs_by_name['responses'] +_TRANSACTIONSWITHPROOFSRESPONSE.oneofs_by_name['responses'].fields.append( + _TRANSACTIONSWITHPROOFSRESPONSE.fields_by_name['instant_send_lock_messages']) +_TRANSACTIONSWITHPROOFSRESPONSE.fields_by_name['instant_send_lock_messages'].containing_oneof = _TRANSACTIONSWITHPROOFSRESPONSE.oneofs_by_name['responses'] +_TRANSACTIONSWITHPROOFSRESPONSE.oneofs_by_name['responses'].fields.append( + _TRANSACTIONSWITHPROOFSRESPONSE.fields_by_name['raw_merkle_block']) +_TRANSACTIONSWITHPROOFSRESPONSE.fields_by_name['raw_merkle_block'].containing_oneof = _TRANSACTIONSWITHPROOFSRESPONSE.oneofs_by_name['responses'] +DESCRIPTOR.message_types_by_name['GetStatusRequest'] = _GETSTATUSREQUEST +DESCRIPTOR.message_types_by_name['GetStatusResponse'] = _GETSTATUSRESPONSE +DESCRIPTOR.message_types_by_name['GetBlockRequest'] = _GETBLOCKREQUEST +DESCRIPTOR.message_types_by_name['GetBlockResponse'] = _GETBLOCKRESPONSE +DESCRIPTOR.message_types_by_name['BroadcastTransactionRequest'] = _BROADCASTTRANSACTIONREQUEST +DESCRIPTOR.message_types_by_name['BroadcastTransactionResponse'] = _BROADCASTTRANSACTIONRESPONSE +DESCRIPTOR.message_types_by_name['GetTransactionRequest'] = _GETTRANSACTIONREQUEST +DESCRIPTOR.message_types_by_name['GetTransactionResponse'] = _GETTRANSACTIONRESPONSE +DESCRIPTOR.message_types_by_name['BlockHeadersWithChainLocksRequest'] = _BLOCKHEADERSWITHCHAINLOCKSREQUEST +DESCRIPTOR.message_types_by_name['BlockHeadersWithChainLocksResponse'] = _BLOCKHEADERSWITHCHAINLOCKSRESPONSE +DESCRIPTOR.message_types_by_name['BlockHeaders'] = _BLOCKHEADERS +DESCRIPTOR.message_types_by_name['GetEstimatedTransactionFeeRequest'] = _GETESTIMATEDTRANSACTIONFEEREQUEST +DESCRIPTOR.message_types_by_name['GetEstimatedTransactionFeeResponse'] = _GETESTIMATEDTRANSACTIONFEERESPONSE +DESCRIPTOR.message_types_by_name['TransactionsWithProofsRequest'] = _TRANSACTIONSWITHPROOFSREQUEST +DESCRIPTOR.message_types_by_name['BloomFilter'] = _BLOOMFILTER +DESCRIPTOR.message_types_by_name['TransactionsWithProofsResponse'] = _TRANSACTIONSWITHPROOFSRESPONSE +DESCRIPTOR.message_types_by_name['RawTransactions'] = _RAWTRANSACTIONS +DESCRIPTOR.message_types_by_name['InstantSendLockMessages'] = _INSTANTSENDLOCKMESSAGES +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +GetStatusRequest = _reflection.GeneratedProtocolMessageType('GetStatusRequest', (_message.Message,), { + 'DESCRIPTOR' : _GETSTATUSREQUEST, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetStatusRequest) + }) +_sym_db.RegisterMessage(GetStatusRequest) + +GetStatusResponse = _reflection.GeneratedProtocolMessageType('GetStatusResponse', (_message.Message,), { + + 'Version' : _reflection.GeneratedProtocolMessageType('Version', (_message.Message,), { + 'DESCRIPTOR' : _GETSTATUSRESPONSE_VERSION, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetStatusResponse.Version) + }) + , + + 'Time' : _reflection.GeneratedProtocolMessageType('Time', (_message.Message,), { + 'DESCRIPTOR' : _GETSTATUSRESPONSE_TIME, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetStatusResponse.Time) + }) + , + + 'Chain' : _reflection.GeneratedProtocolMessageType('Chain', (_message.Message,), { + 'DESCRIPTOR' : _GETSTATUSRESPONSE_CHAIN, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetStatusResponse.Chain) + }) + , + + 'Masternode' : _reflection.GeneratedProtocolMessageType('Masternode', (_message.Message,), { + 'DESCRIPTOR' : _GETSTATUSRESPONSE_MASTERNODE, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetStatusResponse.Masternode) + }) + , + + 'NetworkFee' : _reflection.GeneratedProtocolMessageType('NetworkFee', (_message.Message,), { + 'DESCRIPTOR' : _GETSTATUSRESPONSE_NETWORKFEE, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee) + }) + , + + 'Network' : _reflection.GeneratedProtocolMessageType('Network', (_message.Message,), { + 'DESCRIPTOR' : _GETSTATUSRESPONSE_NETWORK, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetStatusResponse.Network) + }) + , + 'DESCRIPTOR' : _GETSTATUSRESPONSE, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetStatusResponse) + }) +_sym_db.RegisterMessage(GetStatusResponse) +_sym_db.RegisterMessage(GetStatusResponse.Version) +_sym_db.RegisterMessage(GetStatusResponse.Time) +_sym_db.RegisterMessage(GetStatusResponse.Chain) +_sym_db.RegisterMessage(GetStatusResponse.Masternode) +_sym_db.RegisterMessage(GetStatusResponse.NetworkFee) +_sym_db.RegisterMessage(GetStatusResponse.Network) + +GetBlockRequest = _reflection.GeneratedProtocolMessageType('GetBlockRequest', (_message.Message,), { + 'DESCRIPTOR' : _GETBLOCKREQUEST, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetBlockRequest) + }) +_sym_db.RegisterMessage(GetBlockRequest) + +GetBlockResponse = _reflection.GeneratedProtocolMessageType('GetBlockResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETBLOCKRESPONSE, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetBlockResponse) + }) +_sym_db.RegisterMessage(GetBlockResponse) + +BroadcastTransactionRequest = _reflection.GeneratedProtocolMessageType('BroadcastTransactionRequest', (_message.Message,), { + 'DESCRIPTOR' : _BROADCASTTRANSACTIONREQUEST, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.BroadcastTransactionRequest) + }) +_sym_db.RegisterMessage(BroadcastTransactionRequest) + +BroadcastTransactionResponse = _reflection.GeneratedProtocolMessageType('BroadcastTransactionResponse', (_message.Message,), { + 'DESCRIPTOR' : _BROADCASTTRANSACTIONRESPONSE, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.BroadcastTransactionResponse) + }) +_sym_db.RegisterMessage(BroadcastTransactionResponse) + +GetTransactionRequest = _reflection.GeneratedProtocolMessageType('GetTransactionRequest', (_message.Message,), { + 'DESCRIPTOR' : _GETTRANSACTIONREQUEST, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetTransactionRequest) + }) +_sym_db.RegisterMessage(GetTransactionRequest) + +GetTransactionResponse = _reflection.GeneratedProtocolMessageType('GetTransactionResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETTRANSACTIONRESPONSE, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetTransactionResponse) + }) +_sym_db.RegisterMessage(GetTransactionResponse) + +BlockHeadersWithChainLocksRequest = _reflection.GeneratedProtocolMessageType('BlockHeadersWithChainLocksRequest', (_message.Message,), { + 'DESCRIPTOR' : _BLOCKHEADERSWITHCHAINLOCKSREQUEST, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest) + }) +_sym_db.RegisterMessage(BlockHeadersWithChainLocksRequest) + +BlockHeadersWithChainLocksResponse = _reflection.GeneratedProtocolMessageType('BlockHeadersWithChainLocksResponse', (_message.Message,), { + 'DESCRIPTOR' : _BLOCKHEADERSWITHCHAINLOCKSRESPONSE, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse) + }) +_sym_db.RegisterMessage(BlockHeadersWithChainLocksResponse) + +BlockHeaders = _reflection.GeneratedProtocolMessageType('BlockHeaders', (_message.Message,), { + 'DESCRIPTOR' : _BLOCKHEADERS, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.BlockHeaders) + }) +_sym_db.RegisterMessage(BlockHeaders) + +GetEstimatedTransactionFeeRequest = _reflection.GeneratedProtocolMessageType('GetEstimatedTransactionFeeRequest', (_message.Message,), { + 'DESCRIPTOR' : _GETESTIMATEDTRANSACTIONFEEREQUEST, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest) + }) +_sym_db.RegisterMessage(GetEstimatedTransactionFeeRequest) + +GetEstimatedTransactionFeeResponse = _reflection.GeneratedProtocolMessageType('GetEstimatedTransactionFeeResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETESTIMATEDTRANSACTIONFEERESPONSE, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse) + }) +_sym_db.RegisterMessage(GetEstimatedTransactionFeeResponse) + +TransactionsWithProofsRequest = _reflection.GeneratedProtocolMessageType('TransactionsWithProofsRequest', (_message.Message,), { + 'DESCRIPTOR' : _TRANSACTIONSWITHPROOFSREQUEST, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.TransactionsWithProofsRequest) + }) +_sym_db.RegisterMessage(TransactionsWithProofsRequest) + +BloomFilter = _reflection.GeneratedProtocolMessageType('BloomFilter', (_message.Message,), { + 'DESCRIPTOR' : _BLOOMFILTER, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.BloomFilter) + }) +_sym_db.RegisterMessage(BloomFilter) + +TransactionsWithProofsResponse = _reflection.GeneratedProtocolMessageType('TransactionsWithProofsResponse', (_message.Message,), { + 'DESCRIPTOR' : _TRANSACTIONSWITHPROOFSRESPONSE, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.TransactionsWithProofsResponse) + }) +_sym_db.RegisterMessage(TransactionsWithProofsResponse) + +RawTransactions = _reflection.GeneratedProtocolMessageType('RawTransactions', (_message.Message,), { + 'DESCRIPTOR' : _RAWTRANSACTIONS, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.RawTransactions) + }) +_sym_db.RegisterMessage(RawTransactions) + +InstantSendLockMessages = _reflection.GeneratedProtocolMessageType('InstantSendLockMessages', (_message.Message,), { + 'DESCRIPTOR' : _INSTANTSENDLOCKMESSAGES, + '__module__' : 'core_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.InstantSendLockMessages) + }) +_sym_db.RegisterMessage(InstantSendLockMessages) + + + +_CORE = _descriptor.ServiceDescriptor( + name='Core', + full_name='org.dash.platform.dapi.v0.Core', + file=DESCRIPTOR, + index=0, + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_start=2832, + serialized_end=3782, + methods=[ + _descriptor.MethodDescriptor( + name='getStatus', + full_name='org.dash.platform.dapi.v0.Core.getStatus', + index=0, + containing_service=None, + input_type=_GETSTATUSREQUEST, + output_type=_GETSTATUSRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getBlock', + full_name='org.dash.platform.dapi.v0.Core.getBlock', + index=1, + containing_service=None, + input_type=_GETBLOCKREQUEST, + output_type=_GETBLOCKRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='broadcastTransaction', + full_name='org.dash.platform.dapi.v0.Core.broadcastTransaction', + index=2, + containing_service=None, + input_type=_BROADCASTTRANSACTIONREQUEST, + output_type=_BROADCASTTRANSACTIONRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getTransaction', + full_name='org.dash.platform.dapi.v0.Core.getTransaction', + index=3, + containing_service=None, + input_type=_GETTRANSACTIONREQUEST, + output_type=_GETTRANSACTIONRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getEstimatedTransactionFee', + full_name='org.dash.platform.dapi.v0.Core.getEstimatedTransactionFee', + index=4, + containing_service=None, + input_type=_GETESTIMATEDTRANSACTIONFEEREQUEST, + output_type=_GETESTIMATEDTRANSACTIONFEERESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='subscribeToBlockHeadersWithChainLocks', + full_name='org.dash.platform.dapi.v0.Core.subscribeToBlockHeadersWithChainLocks', + index=5, + containing_service=None, + input_type=_BLOCKHEADERSWITHCHAINLOCKSREQUEST, + output_type=_BLOCKHEADERSWITHCHAINLOCKSRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='subscribeToTransactionsWithProofs', + full_name='org.dash.platform.dapi.v0.Core.subscribeToTransactionsWithProofs', + index=6, + containing_service=None, + input_type=_TRANSACTIONSWITHPROOFSREQUEST, + output_type=_TRANSACTIONSWITHPROOFSRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), +]) +_sym_db.RegisterServiceDescriptor(_CORE) + +DESCRIPTOR.services_by_name['Core'] = _CORE + +# @@protoc_insertion_point(module_scope) diff --git a/packages/dapi-grpc/clients/core/v0/python/core_pb2_grpc.py b/packages/dapi-grpc/clients/core/v0/python/core_pb2_grpc.py new file mode 100644 index 00000000000..e8a4eec3513 --- /dev/null +++ b/packages/dapi-grpc/clients/core/v0/python/core_pb2_grpc.py @@ -0,0 +1,264 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +import core_pb2 as core__pb2 + + +class CoreStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.getStatus = channel.unary_unary( + '/org.dash.platform.dapi.v0.Core/getStatus', + request_serializer=core__pb2.GetStatusRequest.SerializeToString, + response_deserializer=core__pb2.GetStatusResponse.FromString, + ) + self.getBlock = channel.unary_unary( + '/org.dash.platform.dapi.v0.Core/getBlock', + request_serializer=core__pb2.GetBlockRequest.SerializeToString, + response_deserializer=core__pb2.GetBlockResponse.FromString, + ) + self.broadcastTransaction = channel.unary_unary( + '/org.dash.platform.dapi.v0.Core/broadcastTransaction', + request_serializer=core__pb2.BroadcastTransactionRequest.SerializeToString, + response_deserializer=core__pb2.BroadcastTransactionResponse.FromString, + ) + self.getTransaction = channel.unary_unary( + '/org.dash.platform.dapi.v0.Core/getTransaction', + request_serializer=core__pb2.GetTransactionRequest.SerializeToString, + response_deserializer=core__pb2.GetTransactionResponse.FromString, + ) + self.getEstimatedTransactionFee = channel.unary_unary( + '/org.dash.platform.dapi.v0.Core/getEstimatedTransactionFee', + request_serializer=core__pb2.GetEstimatedTransactionFeeRequest.SerializeToString, + response_deserializer=core__pb2.GetEstimatedTransactionFeeResponse.FromString, + ) + self.subscribeToBlockHeadersWithChainLocks = channel.unary_stream( + '/org.dash.platform.dapi.v0.Core/subscribeToBlockHeadersWithChainLocks', + request_serializer=core__pb2.BlockHeadersWithChainLocksRequest.SerializeToString, + response_deserializer=core__pb2.BlockHeadersWithChainLocksResponse.FromString, + ) + self.subscribeToTransactionsWithProofs = channel.unary_stream( + '/org.dash.platform.dapi.v0.Core/subscribeToTransactionsWithProofs', + request_serializer=core__pb2.TransactionsWithProofsRequest.SerializeToString, + response_deserializer=core__pb2.TransactionsWithProofsResponse.FromString, + ) + + +class CoreServicer(object): + """Missing associated documentation comment in .proto file.""" + + def getStatus(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def getBlock(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def broadcastTransaction(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def getTransaction(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def getEstimatedTransactionFee(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def subscribeToBlockHeadersWithChainLocks(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def subscribeToTransactionsWithProofs(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_CoreServicer_to_server(servicer, server): + rpc_method_handlers = { + 'getStatus': grpc.unary_unary_rpc_method_handler( + servicer.getStatus, + request_deserializer=core__pb2.GetStatusRequest.FromString, + response_serializer=core__pb2.GetStatusResponse.SerializeToString, + ), + 'getBlock': grpc.unary_unary_rpc_method_handler( + servicer.getBlock, + request_deserializer=core__pb2.GetBlockRequest.FromString, + response_serializer=core__pb2.GetBlockResponse.SerializeToString, + ), + 'broadcastTransaction': grpc.unary_unary_rpc_method_handler( + servicer.broadcastTransaction, + request_deserializer=core__pb2.BroadcastTransactionRequest.FromString, + response_serializer=core__pb2.BroadcastTransactionResponse.SerializeToString, + ), + 'getTransaction': grpc.unary_unary_rpc_method_handler( + servicer.getTransaction, + request_deserializer=core__pb2.GetTransactionRequest.FromString, + response_serializer=core__pb2.GetTransactionResponse.SerializeToString, + ), + 'getEstimatedTransactionFee': grpc.unary_unary_rpc_method_handler( + servicer.getEstimatedTransactionFee, + request_deserializer=core__pb2.GetEstimatedTransactionFeeRequest.FromString, + response_serializer=core__pb2.GetEstimatedTransactionFeeResponse.SerializeToString, + ), + 'subscribeToBlockHeadersWithChainLocks': grpc.unary_stream_rpc_method_handler( + servicer.subscribeToBlockHeadersWithChainLocks, + request_deserializer=core__pb2.BlockHeadersWithChainLocksRequest.FromString, + response_serializer=core__pb2.BlockHeadersWithChainLocksResponse.SerializeToString, + ), + 'subscribeToTransactionsWithProofs': grpc.unary_stream_rpc_method_handler( + servicer.subscribeToTransactionsWithProofs, + request_deserializer=core__pb2.TransactionsWithProofsRequest.FromString, + response_serializer=core__pb2.TransactionsWithProofsResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'org.dash.platform.dapi.v0.Core', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class Core(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def getStatus(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/org.dash.platform.dapi.v0.Core/getStatus', + core__pb2.GetStatusRequest.SerializeToString, + core__pb2.GetStatusResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def getBlock(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/org.dash.platform.dapi.v0.Core/getBlock', + core__pb2.GetBlockRequest.SerializeToString, + core__pb2.GetBlockResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def broadcastTransaction(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/org.dash.platform.dapi.v0.Core/broadcastTransaction', + core__pb2.BroadcastTransactionRequest.SerializeToString, + core__pb2.BroadcastTransactionResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def getTransaction(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/org.dash.platform.dapi.v0.Core/getTransaction', + core__pb2.GetTransactionRequest.SerializeToString, + core__pb2.GetTransactionResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def getEstimatedTransactionFee(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/org.dash.platform.dapi.v0.Core/getEstimatedTransactionFee', + core__pb2.GetEstimatedTransactionFeeRequest.SerializeToString, + core__pb2.GetEstimatedTransactionFeeResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def subscribeToBlockHeadersWithChainLocks(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_stream(request, target, '/org.dash.platform.dapi.v0.Core/subscribeToBlockHeadersWithChainLocks', + core__pb2.BlockHeadersWithChainLocksRequest.SerializeToString, + core__pb2.BlockHeadersWithChainLocksResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def subscribeToTransactionsWithProofs(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_stream(request, target, '/org.dash.platform.dapi.v0.Core/subscribeToTransactionsWithProofs', + core__pb2.TransactionsWithProofsRequest.SerializeToString, + core__pb2.TransactionsWithProofsResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/packages/dapi-grpc/clients/core/v0/web/README.md b/packages/dapi-grpc/clients/core/v0/web/README.md new file mode 100644 index 00000000000..ddc53757beb --- /dev/null +++ b/packages/dapi-grpc/clients/core/v0/web/README.md @@ -0,0 +1,9 @@ +# GRPC-Web client + +This is a directory for the generated GRPC-Web client. + +## Build + +```bash +npm run build +``` diff --git a/packages/dapi-grpc/clients/core/v0/web/core_grpc_web_pb.js b/packages/dapi-grpc/clients/core/v0/web/core_grpc_web_pb.js new file mode 100644 index 00000000000..541ffb11648 --- /dev/null +++ b/packages/dapi-grpc/clients/core/v0/web/core_grpc_web_pb.js @@ -0,0 +1,495 @@ +/** + * @fileoverview gRPC-Web generated client stub for org.dash.platform.dapi.v0 + * @enhanceable + * @public + */ + +// GENERATED CODE -- DO NOT EDIT! + + +/* eslint-disable */ +// @ts-nocheck + + + +const grpc = {}; +grpc.web = require('grpc-web'); + +const proto = {}; +proto.org = {}; +proto.org.dash = {}; +proto.org.dash.platform = {}; +proto.org.dash.platform.dapi = {}; +proto.org.dash.platform.dapi.v0 = require('./core_pb.js'); + +/** + * @param {string} hostname + * @param {?Object} credentials + * @param {?grpc.web.ClientOptions} options + * @constructor + * @struct + * @final + */ +proto.org.dash.platform.dapi.v0.CoreClient = + function(hostname, credentials, options) { + if (!options) options = {}; + options.format = 'text'; + + /** + * @private @const {!grpc.web.GrpcWebClientBase} The client + */ + this.client_ = new grpc.web.GrpcWebClientBase(options); + + /** + * @private @const {string} The hostname + */ + this.hostname_ = hostname; + +}; + + +/** + * @param {string} hostname + * @param {?Object} credentials + * @param {?grpc.web.ClientOptions} options + * @constructor + * @struct + * @final + */ +proto.org.dash.platform.dapi.v0.CorePromiseClient = + function(hostname, credentials, options) { + if (!options) options = {}; + options.format = 'text'; + + /** + * @private @const {!grpc.web.GrpcWebClientBase} The client + */ + this.client_ = new grpc.web.GrpcWebClientBase(options); + + /** + * @private @const {string} The hostname + */ + this.hostname_ = hostname; + +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.GetStatusRequest, + * !proto.org.dash.platform.dapi.v0.GetStatusResponse>} + */ +const methodDescriptor_Core_getStatus = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Core/getStatus', + grpc.web.MethodType.UNARY, + proto.org.dash.platform.dapi.v0.GetStatusRequest, + proto.org.dash.platform.dapi.v0.GetStatusResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.GetStatusRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.GetStatusResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetStatusRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.org.dash.platform.dapi.v0.GetStatusResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.CoreClient.prototype.getStatus = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/getStatus', + request, + metadata || {}, + methodDescriptor_Core_getStatus, + callback); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetStatusRequest} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.org.dash.platform.dapi.v0.CorePromiseClient.prototype.getStatus = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/getStatus', + request, + metadata || {}, + methodDescriptor_Core_getStatus); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.GetBlockRequest, + * !proto.org.dash.platform.dapi.v0.GetBlockResponse>} + */ +const methodDescriptor_Core_getBlock = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Core/getBlock', + grpc.web.MethodType.UNARY, + proto.org.dash.platform.dapi.v0.GetBlockRequest, + proto.org.dash.platform.dapi.v0.GetBlockResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.GetBlockRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.GetBlockResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetBlockRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.org.dash.platform.dapi.v0.GetBlockResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.CoreClient.prototype.getBlock = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/getBlock', + request, + metadata || {}, + methodDescriptor_Core_getBlock, + callback); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetBlockRequest} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.org.dash.platform.dapi.v0.CorePromiseClient.prototype.getBlock = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/getBlock', + request, + metadata || {}, + methodDescriptor_Core_getBlock); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest, + * !proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse>} + */ +const methodDescriptor_Core_broadcastTransaction = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Core/broadcastTransaction', + grpc.web.MethodType.UNARY, + proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest, + proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.CoreClient.prototype.broadcastTransaction = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/broadcastTransaction', + request, + metadata || {}, + methodDescriptor_Core_broadcastTransaction, + callback); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.org.dash.platform.dapi.v0.CorePromiseClient.prototype.broadcastTransaction = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/broadcastTransaction', + request, + metadata || {}, + methodDescriptor_Core_broadcastTransaction); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.GetTransactionRequest, + * !proto.org.dash.platform.dapi.v0.GetTransactionResponse>} + */ +const methodDescriptor_Core_getTransaction = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Core/getTransaction', + grpc.web.MethodType.UNARY, + proto.org.dash.platform.dapi.v0.GetTransactionRequest, + proto.org.dash.platform.dapi.v0.GetTransactionResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.GetTransactionResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.org.dash.platform.dapi.v0.GetTransactionResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.CoreClient.prototype.getTransaction = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/getTransaction', + request, + metadata || {}, + methodDescriptor_Core_getTransaction, + callback); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.org.dash.platform.dapi.v0.CorePromiseClient.prototype.getTransaction = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/getTransaction', + request, + metadata || {}, + methodDescriptor_Core_getTransaction); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest, + * !proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse>} + */ +const methodDescriptor_Core_getEstimatedTransactionFee = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Core/getEstimatedTransactionFee', + grpc.web.MethodType.UNARY, + proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest, + proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.CoreClient.prototype.getEstimatedTransactionFee = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/getEstimatedTransactionFee', + request, + metadata || {}, + methodDescriptor_Core_getEstimatedTransactionFee, + callback); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.org.dash.platform.dapi.v0.CorePromiseClient.prototype.getEstimatedTransactionFee = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/getEstimatedTransactionFee', + request, + metadata || {}, + methodDescriptor_Core_getEstimatedTransactionFee); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest, + * !proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse>} + */ +const methodDescriptor_Core_subscribeToBlockHeadersWithChainLocks = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Core/subscribeToBlockHeadersWithChainLocks', + grpc.web.MethodType.SERVER_STREAMING, + proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest, + proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} request The request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!grpc.web.ClientReadableStream} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.CoreClient.prototype.subscribeToBlockHeadersWithChainLocks = + function(request, metadata) { + return this.client_.serverStreaming(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/subscribeToBlockHeadersWithChainLocks', + request, + metadata || {}, + methodDescriptor_Core_subscribeToBlockHeadersWithChainLocks); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} request The request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!grpc.web.ClientReadableStream} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.CorePromiseClient.prototype.subscribeToBlockHeadersWithChainLocks = + function(request, metadata) { + return this.client_.serverStreaming(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/subscribeToBlockHeadersWithChainLocks', + request, + metadata || {}, + methodDescriptor_Core_subscribeToBlockHeadersWithChainLocks); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest, + * !proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse>} + */ +const methodDescriptor_Core_subscribeToTransactionsWithProofs = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Core/subscribeToTransactionsWithProofs', + grpc.web.MethodType.SERVER_STREAMING, + proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest, + proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} request The request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!grpc.web.ClientReadableStream} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.CoreClient.prototype.subscribeToTransactionsWithProofs = + function(request, metadata) { + return this.client_.serverStreaming(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/subscribeToTransactionsWithProofs', + request, + metadata || {}, + methodDescriptor_Core_subscribeToTransactionsWithProofs); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} request The request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!grpc.web.ClientReadableStream} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.CorePromiseClient.prototype.subscribeToTransactionsWithProofs = + function(request, metadata) { + return this.client_.serverStreaming(this.hostname_ + + '/org.dash.platform.dapi.v0.Core/subscribeToTransactionsWithProofs', + request, + metadata || {}, + methodDescriptor_Core_subscribeToTransactionsWithProofs); +}; + + +module.exports = proto.org.dash.platform.dapi.v0; + diff --git a/packages/dapi-grpc/clients/core/v0/web/core_pb.js b/packages/dapi-grpc/clients/core/v0/web/core_pb.js new file mode 100644 index 00000000000..e04a080875e --- /dev/null +++ b/packages/dapi-grpc/clients/core/v0/web/core_pb.js @@ -0,0 +1,5887 @@ +// source: core.proto +/** + * @fileoverview + * @enhanceable + * @suppress {missingRequire} reports error on implicit type usages. + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +// @ts-nocheck + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = (function() { + if (this) { return this; } + if (typeof window !== 'undefined') { return window; } + if (typeof global !== 'undefined') { return global; } + if (typeof self !== 'undefined') { return self; } + return Function('return this')(); +}.call(null)); + +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BlockHeaders', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.FromBlockCase', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.ResponsesCase', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BloomFilter', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetBlockRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetBlockRequest.BlockCase', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetBlockResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Network', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Status', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Time', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetStatusResponse.Version', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetTransactionRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetTransactionResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.InstantSendLockMessages', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.RawTransactions', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.FromBlockCase', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.ResponsesCase', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse.Version, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse.Version'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse.Time, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse.Time'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetStatusResponse.Network, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.displayName = 'proto.org.dash.platform.dapi.v0.GetStatusResponse.Network'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetBlockRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetBlockRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetBlockRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetBlockResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetBlockResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetBlockResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.displayName = 'proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.displayName = 'proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetTransactionRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetTransactionRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetTransactionRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetTransactionResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetTransactionResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetTransactionResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.displayName = 'proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.displayName = 'proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BlockHeaders = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.org.dash.platform.dapi.v0.BlockHeaders.repeatedFields_, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BlockHeaders, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BlockHeaders.displayName = 'proto.org.dash.platform.dapi.v0.BlockHeaders'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.displayName = 'proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BloomFilter = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BloomFilter, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BloomFilter.displayName = 'proto.org.dash.platform.dapi.v0.BloomFilter'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.displayName = 'proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.RawTransactions = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.org.dash.platform.dapi.v0.RawTransactions.repeatedFields_, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.RawTransactions, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.RawTransactions.displayName = 'proto.org.dash.platform.dapi.v0.RawTransactions'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.org.dash.platform.dapi.v0.InstantSendLockMessages.repeatedFields_, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.InstantSendLockMessages, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.InstantSendLockMessages.displayName = 'proto.org.dash.platform.dapi.v0.InstantSendLockMessages'; +} + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest.toObject = function(includeInstance, msg) { + var f, obj = { + + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusRequest} + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusRequest; + return proto.org.dash.platform.dapi.v0.GetStatusRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusRequest} + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.toObject = function(includeInstance, msg) { + var f, obj = { + version: (f = msg.getVersion()) && proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.toObject(includeInstance, f), + time: (f = msg.getTime()) && proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.toObject(includeInstance, f), + status: jspb.Message.getFieldWithDefault(msg, 3, 0), + syncProgress: jspb.Message.getFloatingPointFieldWithDefault(msg, 4, 0.0), + chain: (f = msg.getChain()) && proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.toObject(includeInstance, f), + masternode: (f = msg.getMasternode()) && proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.toObject(includeInstance, f), + network: (f = msg.getNetwork()) && proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Version; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.deserializeBinaryFromReader); + msg.setVersion(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Time; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.deserializeBinaryFromReader); + msg.setTime(value); + break; + case 3: + var value = /** @type {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Status} */ (reader.readEnum()); + msg.setStatus(value); + break; + case 4: + var value = /** @type {number} */ (reader.readDouble()); + msg.setSyncProgress(value); + break; + case 5: + var value = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.deserializeBinaryFromReader); + msg.setChain(value); + break; + case 6: + var value = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.deserializeBinaryFromReader); + msg.setMasternode(value); + break; + case 7: + var value = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Network; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.deserializeBinaryFromReader); + msg.setNetwork(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getVersion(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.serializeBinaryToWriter + ); + } + f = message.getTime(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.serializeBinaryToWriter + ); + } + f = message.getStatus(); + if (f !== 0.0) { + writer.writeEnum( + 3, + f + ); + } + f = message.getSyncProgress(); + if (f !== 0.0) { + writer.writeDouble( + 4, + f + ); + } + f = message.getChain(); + if (f != null) { + writer.writeMessage( + 5, + f, + proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.serializeBinaryToWriter + ); + } + f = message.getMasternode(); + if (f != null) { + writer.writeMessage( + 6, + f, + proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.serializeBinaryToWriter + ); + } + f = message.getNetwork(); + if (f != null) { + writer.writeMessage( + 7, + f, + proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.serializeBinaryToWriter + ); + } +}; + + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Status = { + NOT_STARTED: 0, + SYNCING: 1, + READY: 2, + ERROR: 3 +}; + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.toObject = function(includeInstance, msg) { + var f, obj = { + protocol: jspb.Message.getFieldWithDefault(msg, 1, 0), + software: jspb.Message.getFieldWithDefault(msg, 2, 0), + agent: jspb.Message.getFieldWithDefault(msg, 3, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Version; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setProtocol(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint32()); + msg.setSoftware(value); + break; + case 3: + var value = /** @type {string} */ (reader.readString()); + msg.setAgent(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getProtocol(); + if (f !== 0) { + writer.writeUint32( + 1, + f + ); + } + f = message.getSoftware(); + if (f !== 0) { + writer.writeUint32( + 2, + f + ); + } + f = message.getAgent(); + if (f.length > 0) { + writer.writeString( + 3, + f + ); + } +}; + + +/** + * optional uint32 protocol = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.getProtocol = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.setProtocol = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional uint32 software = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.getSoftware = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.setSoftware = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional string agent = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.getAgent = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Version.prototype.setAgent = function(value) { + return jspb.Message.setProto3StringField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.toObject = function(includeInstance, msg) { + var f, obj = { + now: jspb.Message.getFieldWithDefault(msg, 1, 0), + offset: jspb.Message.getFieldWithDefault(msg, 2, 0), + median: jspb.Message.getFieldWithDefault(msg, 3, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Time; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setNow(value); + break; + case 2: + var value = /** @type {number} */ (reader.readInt32()); + msg.setOffset(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setMedian(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getNow(); + if (f !== 0) { + writer.writeUint32( + 1, + f + ); + } + f = message.getOffset(); + if (f !== 0) { + writer.writeInt32( + 2, + f + ); + } + f = message.getMedian(); + if (f !== 0) { + writer.writeUint32( + 3, + f + ); + } +}; + + +/** + * optional uint32 now = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.getNow = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.setNow = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional int32 offset = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.getOffset = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.setOffset = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional uint32 median = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.getMedian = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Time.prototype.setMedian = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.toObject = function(includeInstance, msg) { + var f, obj = { + name: jspb.Message.getFieldWithDefault(msg, 1, ""), + headersCount: jspb.Message.getFieldWithDefault(msg, 2, 0), + blocksCount: jspb.Message.getFieldWithDefault(msg, 3, 0), + bestBlockHash: msg.getBestBlockHash_asB64(), + difficulty: jspb.Message.getFloatingPointFieldWithDefault(msg, 5, 0.0), + chainWork: msg.getChainWork_asB64(), + isSynced: jspb.Message.getBooleanFieldWithDefault(msg, 7, false), + syncProgress: jspb.Message.getFloatingPointFieldWithDefault(msg, 8, 0.0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setName(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint32()); + msg.setHeadersCount(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setBlocksCount(value); + break; + case 4: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setBestBlockHash(value); + break; + case 5: + var value = /** @type {number} */ (reader.readDouble()); + msg.setDifficulty(value); + break; + case 6: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setChainWork(value); + break; + case 7: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsSynced(value); + break; + case 8: + var value = /** @type {number} */ (reader.readDouble()); + msg.setSyncProgress(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getName(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getHeadersCount(); + if (f !== 0) { + writer.writeUint32( + 2, + f + ); + } + f = message.getBlocksCount(); + if (f !== 0) { + writer.writeUint32( + 3, + f + ); + } + f = message.getBestBlockHash_asU8(); + if (f.length > 0) { + writer.writeBytes( + 4, + f + ); + } + f = message.getDifficulty(); + if (f !== 0.0) { + writer.writeDouble( + 5, + f + ); + } + f = message.getChainWork_asU8(); + if (f.length > 0) { + writer.writeBytes( + 6, + f + ); + } + f = message.getIsSynced(); + if (f) { + writer.writeBool( + 7, + f + ); + } + f = message.getSyncProgress(); + if (f !== 0.0) { + writer.writeDouble( + 8, + f + ); + } +}; + + +/** + * optional string name = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setName = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional uint32 headers_count = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getHeadersCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setHeadersCount = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional uint32 blocks_count = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getBlocksCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setBlocksCount = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional bytes best_block_hash = 4; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getBestBlockHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * optional bytes best_block_hash = 4; + * This is a type-conversion wrapper around `getBestBlockHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getBestBlockHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getBestBlockHash())); +}; + + +/** + * optional bytes best_block_hash = 4; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getBestBlockHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getBestBlockHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getBestBlockHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setBestBlockHash = function(value) { + return jspb.Message.setProto3BytesField(this, 4, value); +}; + + +/** + * optional double difficulty = 5; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getDifficulty = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 5, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setDifficulty = function(value) { + return jspb.Message.setProto3FloatField(this, 5, value); +}; + + +/** + * optional bytes chain_work = 6; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getChainWork = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, "")); +}; + + +/** + * optional bytes chain_work = 6; + * This is a type-conversion wrapper around `getChainWork()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getChainWork_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getChainWork())); +}; + + +/** + * optional bytes chain_work = 6; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getChainWork()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getChainWork_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getChainWork())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setChainWork = function(value) { + return jspb.Message.setProto3BytesField(this, 6, value); +}; + + +/** + * optional bool is_synced = 7; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getIsSynced = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 7, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setIsSynced = function(value) { + return jspb.Message.setProto3BooleanField(this, 7, value); +}; + + +/** + * optional double sync_progress = 8; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.getSyncProgress = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 8, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain.prototype.setSyncProgress = function(value) { + return jspb.Message.setProto3FloatField(this, 8, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.toObject = function(includeInstance, msg) { + var f, obj = { + status: jspb.Message.getFieldWithDefault(msg, 1, 0), + proTxHash: msg.getProTxHash_asB64(), + posePenalty: jspb.Message.getFieldWithDefault(msg, 3, 0), + isSynced: jspb.Message.getBooleanFieldWithDefault(msg, 4, false), + syncProgress: jspb.Message.getFloatingPointFieldWithDefault(msg, 5, 0.0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status} */ (reader.readEnum()); + msg.setStatus(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setProTxHash(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setPosePenalty(value); + break; + case 4: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsSynced(value); + break; + case 5: + var value = /** @type {number} */ (reader.readDouble()); + msg.setSyncProgress(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getStatus(); + if (f !== 0.0) { + writer.writeEnum( + 1, + f + ); + } + f = message.getProTxHash_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getPosePenalty(); + if (f !== 0) { + writer.writeUint32( + 3, + f + ); + } + f = message.getIsSynced(); + if (f) { + writer.writeBool( + 4, + f + ); + } + f = message.getSyncProgress(); + if (f !== 0.0) { + writer.writeDouble( + 5, + f + ); + } +}; + + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status = { + UNKNOWN: 0, + WAITING_FOR_PROTX: 1, + POSE_BANNED: 2, + REMOVED: 3, + OPERATOR_KEY_CHANGED: 4, + PROTX_IP_CHANGED: 5, + READY: 6, + ERROR: 7 +}; + +/** + * optional Status status = 1; + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getStatus = function() { + return /** @type {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.Status} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.setStatus = function(value) { + return jspb.Message.setProto3EnumField(this, 1, value); +}; + + +/** + * optional bytes pro_tx_hash = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getProTxHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes pro_tx_hash = 2; + * This is a type-conversion wrapper around `getProTxHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getProTxHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getProTxHash())); +}; + + +/** + * optional bytes pro_tx_hash = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getProTxHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getProTxHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getProTxHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.setProTxHash = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional uint32 pose_penalty = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getPosePenalty = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.setPosePenalty = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional bool is_synced = 4; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getIsSynced = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 4, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.setIsSynced = function(value) { + return jspb.Message.setProto3BooleanField(this, 4, value); +}; + + +/** + * optional double sync_progress = 5; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.getSyncProgress = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 5, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode.prototype.setSyncProgress = function(value) { + return jspb.Message.setProto3FloatField(this, 5, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.toObject = function(includeInstance, msg) { + var f, obj = { + relay: jspb.Message.getFloatingPointFieldWithDefault(msg, 1, 0.0), + incremental: jspb.Message.getFloatingPointFieldWithDefault(msg, 2, 0.0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readDouble()); + msg.setRelay(value); + break; + case 2: + var value = /** @type {number} */ (reader.readDouble()); + msg.setIncremental(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getRelay(); + if (f !== 0.0) { + writer.writeDouble( + 1, + f + ); + } + f = message.getIncremental(); + if (f !== 0.0) { + writer.writeDouble( + 2, + f + ); + } +}; + + +/** + * optional double relay = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.prototype.getRelay = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 1, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.prototype.setRelay = function(value) { + return jspb.Message.setProto3FloatField(this, 1, value); +}; + + +/** + * optional double incremental = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.prototype.getIncremental = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 2, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.prototype.setIncremental = function(value) { + return jspb.Message.setProto3FloatField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.toObject = function(includeInstance, msg) { + var f, obj = { + peersCount: jspb.Message.getFieldWithDefault(msg, 1, 0), + fee: (f = msg.getFee()) && proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetStatusResponse.Network; + return proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setPeersCount(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.deserializeBinaryFromReader); + msg.setFee(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getPeersCount(); + if (f !== 0) { + writer.writeUint32( + 1, + f + ); + } + f = message.getFee(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee.serializeBinaryToWriter + ); + } +}; + + +/** + * optional uint32 peers_count = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.getPeersCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.setPeersCount = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional NetworkFee fee = 2; + * @return {?proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.getFee = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.GetStatusResponse.NetworkFee|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} returns this +*/ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.setFee = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.clearFee = function() { + return this.setFee(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.Network.prototype.hasFee = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional Version version = 1; + * @return {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getVersion = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.GetStatusResponse.Version} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.GetStatusResponse.Version, 1)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Version|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setVersion = function(value) { + return jspb.Message.setWrapperField(this, 1, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.clearVersion = function() { + return this.setVersion(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.hasVersion = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional Time time = 2; + * @return {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getTime = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.GetStatusResponse.Time} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.GetStatusResponse.Time, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Time|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setTime = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.clearTime = function() { + return this.setTime(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.hasTime = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional Status status = 3; + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Status} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getStatus = function() { + return /** @type {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Status} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetStatusResponse.Status} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setStatus = function(value) { + return jspb.Message.setProto3EnumField(this, 3, value); +}; + + +/** + * optional double sync_progress = 4; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getSyncProgress = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 4, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setSyncProgress = function(value) { + return jspb.Message.setProto3FloatField(this, 4, value); +}; + + +/** + * optional Chain chain = 5; + * @return {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getChain = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain, 5)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Chain|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setChain = function(value) { + return jspb.Message.setWrapperField(this, 5, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.clearChain = function() { + return this.setChain(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.hasChain = function() { + return jspb.Message.getField(this, 5) != null; +}; + + +/** + * optional Masternode masternode = 6; + * @return {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getMasternode = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode, 6)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Masternode|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setMasternode = function(value) { + return jspb.Message.setWrapperField(this, 6, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.clearMasternode = function() { + return this.setMasternode(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.hasMasternode = function() { + return jspb.Message.getField(this, 6) != null; +}; + + +/** + * optional Network network = 7; + * @return {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.getNetwork = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.GetStatusResponse.Network} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.GetStatusResponse.Network, 7)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.GetStatusResponse.Network|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.setNetwork = function(value) { + return jspb.Message.setWrapperField(this, 7, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetStatusResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.clearNetwork = function() { + return this.setNetwork(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetStatusResponse.prototype.hasNetwork = function() { + return jspb.Message.getField(this, 7) != null; +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_ = [[1,2]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.BlockCase = { + BLOCK_NOT_SET: 0, + HEIGHT: 1, + HASH: 2 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.GetBlockRequest.BlockCase} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.getBlockCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.GetBlockRequest.BlockCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetBlockRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetBlockRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.toObject = function(includeInstance, msg) { + var f, obj = { + height: jspb.Message.getFieldWithDefault(msg, 1, 0), + hash: jspb.Message.getFieldWithDefault(msg, 2, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetBlockRequest} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetBlockRequest; + return proto.org.dash.platform.dapi.v0.GetBlockRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetBlockRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetBlockRequest} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setHeight(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setHash(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetBlockRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetBlockRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = /** @type {number} */ (jspb.Message.getField(message, 1)); + if (f != null) { + writer.writeUint32( + 1, + f + ); + } + f = /** @type {string} */ (jspb.Message.getField(message, 2)); + if (f != null) { + writer.writeString( + 2, + f + ); + } +}; + + +/** + * optional uint32 height = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.getHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetBlockRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.setHeight = function(value) { + return jspb.Message.setOneofField(this, 1, proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetBlockRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.clearHeight = function() { + return jspb.Message.setOneofField(this, 1, proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.hasHeight = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional string hash = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.getHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.GetBlockRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.setHash = function(value) { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetBlockRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.clearHash = function() { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.GetBlockRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetBlockRequest.prototype.hasHash = function() { + return jspb.Message.getField(this, 2) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetBlockResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetBlockResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.toObject = function(includeInstance, msg) { + var f, obj = { + block: msg.getBlock_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetBlockResponse} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetBlockResponse; + return proto.org.dash.platform.dapi.v0.GetBlockResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetBlockResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetBlockResponse} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setBlock(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetBlockResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetBlockResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getBlock_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } +}; + + +/** + * optional bytes block = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.prototype.getBlock = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes block = 1; + * This is a type-conversion wrapper around `getBlock()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.prototype.getBlock_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getBlock())); +}; + + +/** + * optional bytes block = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getBlock()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.prototype.getBlock_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getBlock())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetBlockResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetBlockResponse.prototype.setBlock = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.toObject = function(includeInstance, msg) { + var f, obj = { + transaction: msg.getTransaction_asB64(), + allowHighFees: jspb.Message.getBooleanFieldWithDefault(msg, 2, false), + bypassLimits: jspb.Message.getBooleanFieldWithDefault(msg, 3, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest; + return proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setTransaction(value); + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setAllowHighFees(value); + break; + case 3: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setBypassLimits(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getTransaction_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getAllowHighFees(); + if (f) { + writer.writeBool( + 2, + f + ); + } + f = message.getBypassLimits(); + if (f) { + writer.writeBool( + 3, + f + ); + } +}; + + +/** + * optional bytes transaction = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.getTransaction = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes transaction = 1; + * This is a type-conversion wrapper around `getTransaction()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.getTransaction_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getTransaction())); +}; + + +/** + * optional bytes transaction = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getTransaction()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.getTransaction_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getTransaction())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.setTransaction = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bool allow_high_fees = 2; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.getAllowHighFees = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.setAllowHighFees = function(value) { + return jspb.Message.setProto3BooleanField(this, 2, value); +}; + + +/** + * optional bool bypass_limits = 3; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.getBypassLimits = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 3, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionRequest.prototype.setBypassLimits = function(value) { + return jspb.Message.setProto3BooleanField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.toObject = function(includeInstance, msg) { + var f, obj = { + transactionId: jspb.Message.getFieldWithDefault(msg, 1, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse; + return proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setTransactionId(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getTransactionId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } +}; + + +/** + * optional string transaction_id = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.prototype.getTransactionId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.BroadcastTransactionResponse.prototype.setTransactionId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetTransactionRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.toObject = function(includeInstance, msg) { + var f, obj = { + id: jspb.Message.getFieldWithDefault(msg, 1, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetTransactionRequest; + return proto.org.dash.platform.dapi.v0.GetTransactionRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setId(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetTransactionRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } +}; + + +/** + * optional string id = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.prototype.getId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionRequest.prototype.setId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetTransactionResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.toObject = function(includeInstance, msg) { + var f, obj = { + transaction: msg.getTransaction_asB64(), + blockHash: msg.getBlockHash_asB64(), + height: jspb.Message.getFieldWithDefault(msg, 3, 0), + confirmations: jspb.Message.getFieldWithDefault(msg, 4, 0), + isInstantLocked: jspb.Message.getBooleanFieldWithDefault(msg, 5, false), + isChainLocked: jspb.Message.getBooleanFieldWithDefault(msg, 6, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetTransactionResponse; + return proto.org.dash.platform.dapi.v0.GetTransactionResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setTransaction(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setBlockHash(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setHeight(value); + break; + case 4: + var value = /** @type {number} */ (reader.readUint32()); + msg.setConfirmations(value); + break; + case 5: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsInstantLocked(value); + break; + case 6: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsChainLocked(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetTransactionResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getTransaction_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getBlockHash_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getHeight(); + if (f !== 0) { + writer.writeUint32( + 3, + f + ); + } + f = message.getConfirmations(); + if (f !== 0) { + writer.writeUint32( + 4, + f + ); + } + f = message.getIsInstantLocked(); + if (f) { + writer.writeBool( + 5, + f + ); + } + f = message.getIsChainLocked(); + if (f) { + writer.writeBool( + 6, + f + ); + } +}; + + +/** + * optional bytes transaction = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getTransaction = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes transaction = 1; + * This is a type-conversion wrapper around `getTransaction()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getTransaction_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getTransaction())); +}; + + +/** + * optional bytes transaction = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getTransaction()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getTransaction_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getTransaction())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.setTransaction = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bytes block_hash = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getBlockHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes block_hash = 2; + * This is a type-conversion wrapper around `getBlockHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getBlockHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getBlockHash())); +}; + + +/** + * optional bytes block_hash = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getBlockHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getBlockHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getBlockHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.setBlockHash = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional uint32 height = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.setHeight = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional uint32 confirmations = 4; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getConfirmations = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.setConfirmations = function(value) { + return jspb.Message.setProto3IntField(this, 4, value); +}; + + +/** + * optional bool is_instant_locked = 5; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getIsInstantLocked = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 5, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.setIsInstantLocked = function(value) { + return jspb.Message.setProto3BooleanField(this, 5, value); +}; + + +/** + * optional bool is_chain_locked = 6; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.getIsChainLocked = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 6, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetTransactionResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetTransactionResponse.prototype.setIsChainLocked = function(value) { + return jspb.Message.setProto3BooleanField(this, 6, value); +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_ = [[1,2]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.FromBlockCase = { + FROM_BLOCK_NOT_SET: 0, + FROM_BLOCK_HASH: 1, + FROM_BLOCK_HEIGHT: 2 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.FromBlockCase} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.getFromBlockCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.FromBlockCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.toObject = function(includeInstance, msg) { + var f, obj = { + fromBlockHash: msg.getFromBlockHash_asB64(), + fromBlockHeight: jspb.Message.getFieldWithDefault(msg, 2, 0), + count: jspb.Message.getFieldWithDefault(msg, 3, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest; + return proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setFromBlockHash(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint32()); + msg.setFromBlockHeight(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setCount(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 1)); + if (f != null) { + writer.writeBytes( + 1, + f + ); + } + f = /** @type {number} */ (jspb.Message.getField(message, 2)); + if (f != null) { + writer.writeUint32( + 2, + f + ); + } + f = message.getCount(); + if (f !== 0) { + writer.writeUint32( + 3, + f + ); + } +}; + + +/** + * optional bytes from_block_hash = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.getFromBlockHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes from_block_hash = 1; + * This is a type-conversion wrapper around `getFromBlockHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.getFromBlockHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getFromBlockHash())); +}; + + +/** + * optional bytes from_block_hash = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getFromBlockHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.getFromBlockHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getFromBlockHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.setFromBlockHash = function(value) { + return jspb.Message.setOneofField(this, 1, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.clearFromBlockHash = function() { + return jspb.Message.setOneofField(this, 1, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.hasFromBlockHash = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional uint32 from_block_height = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.getFromBlockHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.setFromBlockHeight = function(value) { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.clearFromBlockHeight = function() { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.hasFromBlockHeight = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional uint32 count = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.getCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksRequest.prototype.setCount = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.oneofGroups_ = [[1,2]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.ResponsesCase = { + RESPONSES_NOT_SET: 0, + BLOCK_HEADERS: 1, + CHAIN_LOCK: 2 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.ResponsesCase} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.getResponsesCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.ResponsesCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.toObject = function(includeInstance, msg) { + var f, obj = { + blockHeaders: (f = msg.getBlockHeaders()) && proto.org.dash.platform.dapi.v0.BlockHeaders.toObject(includeInstance, f), + chainLock: msg.getChainLock_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse; + return proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.org.dash.platform.dapi.v0.BlockHeaders; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.BlockHeaders.deserializeBinaryFromReader); + msg.setBlockHeaders(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setChainLock(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getBlockHeaders(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.org.dash.platform.dapi.v0.BlockHeaders.serializeBinaryToWriter + ); + } + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 2)); + if (f != null) { + writer.writeBytes( + 2, + f + ); + } +}; + + +/** + * optional BlockHeaders block_headers = 1; + * @return {?proto.org.dash.platform.dapi.v0.BlockHeaders} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.getBlockHeaders = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.BlockHeaders} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.BlockHeaders, 1)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.BlockHeaders|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.setBlockHeaders = function(value) { + return jspb.Message.setOneofWrapperField(this, 1, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.clearBlockHeaders = function() { + return this.setBlockHeaders(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.hasBlockHeaders = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional bytes chain_lock = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.getChainLock = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes chain_lock = 2; + * This is a type-conversion wrapper around `getChainLock()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.getChainLock_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getChainLock())); +}; + + +/** + * optional bytes chain_lock = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getChainLock()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.getChainLock_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getChainLock())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.setChainLock = function(value) { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.clearChainLock = function() { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.BlockHeadersWithChainLocksResponse.prototype.hasChainLock = function() { + return jspb.Message.getField(this, 2) != null; +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BlockHeaders.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BlockHeaders} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.toObject = function(includeInstance, msg) { + var f, obj = { + headersList: msg.getHeadersList_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeaders} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BlockHeaders; + return proto.org.dash.platform.dapi.v0.BlockHeaders.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BlockHeaders} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeaders} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.addHeaders(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BlockHeaders.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BlockHeaders} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getHeadersList_asU8(); + if (f.length > 0) { + writer.writeRepeatedBytes( + 1, + f + ); + } +}; + + +/** + * repeated bytes headers = 1; + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.getHeadersList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * repeated bytes headers = 1; + * This is a type-conversion wrapper around `getHeadersList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.getHeadersList_asB64 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsB64( + this.getHeadersList())); +}; + + +/** + * repeated bytes headers = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getHeadersList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.getHeadersList_asU8 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsU8( + this.getHeadersList())); +}; + + +/** + * @param {!(Array|Array)} value + * @return {!proto.org.dash.platform.dapi.v0.BlockHeaders} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.setHeadersList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @param {number=} opt_index + * @return {!proto.org.dash.platform.dapi.v0.BlockHeaders} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.addHeaders = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.org.dash.platform.dapi.v0.BlockHeaders} returns this + */ +proto.org.dash.platform.dapi.v0.BlockHeaders.prototype.clearHeadersList = function() { + return this.setHeadersList([]); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.toObject = function(includeInstance, msg) { + var f, obj = { + blocks: jspb.Message.getFieldWithDefault(msg, 1, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest; + return proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setBlocks(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getBlocks(); + if (f !== 0) { + writer.writeUint32( + 1, + f + ); + } +}; + + +/** + * optional uint32 blocks = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.prototype.getBlocks = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeRequest.prototype.setBlocks = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.toObject = function(includeInstance, msg) { + var f, obj = { + fee: jspb.Message.getFloatingPointFieldWithDefault(msg, 1, 0.0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse; + return proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readDouble()); + msg.setFee(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getFee(); + if (f !== 0.0) { + writer.writeDouble( + 1, + f + ); + } +}; + + +/** + * optional double fee = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.prototype.getFee = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 1, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetEstimatedTransactionFeeResponse.prototype.setFee = function(value) { + return jspb.Message.setProto3FloatField(this, 1, value); +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_ = [[2,3]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.FromBlockCase = { + FROM_BLOCK_NOT_SET: 0, + FROM_BLOCK_HASH: 2, + FROM_BLOCK_HEIGHT: 3 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.FromBlockCase} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getFromBlockCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.FromBlockCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + bloomFilter: (f = msg.getBloomFilter()) && proto.org.dash.platform.dapi.v0.BloomFilter.toObject(includeInstance, f), + fromBlockHash: msg.getFromBlockHash_asB64(), + fromBlockHeight: jspb.Message.getFieldWithDefault(msg, 3, 0), + count: jspb.Message.getFieldWithDefault(msg, 4, 0), + sendTransactionHashes: jspb.Message.getBooleanFieldWithDefault(msg, 5, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest; + return proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.org.dash.platform.dapi.v0.BloomFilter; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.BloomFilter.deserializeBinaryFromReader); + msg.setBloomFilter(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setFromBlockHash(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setFromBlockHeight(value); + break; + case 4: + var value = /** @type {number} */ (reader.readUint32()); + msg.setCount(value); + break; + case 5: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setSendTransactionHashes(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getBloomFilter(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.org.dash.platform.dapi.v0.BloomFilter.serializeBinaryToWriter + ); + } + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 2)); + if (f != null) { + writer.writeBytes( + 2, + f + ); + } + f = /** @type {number} */ (jspb.Message.getField(message, 3)); + if (f != null) { + writer.writeUint32( + 3, + f + ); + } + f = message.getCount(); + if (f !== 0) { + writer.writeUint32( + 4, + f + ); + } + f = message.getSendTransactionHashes(); + if (f) { + writer.writeBool( + 5, + f + ); + } +}; + + +/** + * optional BloomFilter bloom_filter = 1; + * @return {?proto.org.dash.platform.dapi.v0.BloomFilter} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getBloomFilter = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.BloomFilter} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.BloomFilter, 1)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.BloomFilter|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this +*/ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.setBloomFilter = function(value) { + return jspb.Message.setWrapperField(this, 1, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.clearBloomFilter = function() { + return this.setBloomFilter(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.hasBloomFilter = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional bytes from_block_hash = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getFromBlockHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes from_block_hash = 2; + * This is a type-conversion wrapper around `getFromBlockHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getFromBlockHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getFromBlockHash())); +}; + + +/** + * optional bytes from_block_hash = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getFromBlockHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getFromBlockHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getFromBlockHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.setFromBlockHash = function(value) { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.clearFromBlockHash = function() { + return jspb.Message.setOneofField(this, 2, proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.hasFromBlockHash = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional uint32 from_block_height = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getFromBlockHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.setFromBlockHeight = function(value) { + return jspb.Message.setOneofField(this, 3, proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.clearFromBlockHeight = function() { + return jspb.Message.setOneofField(this, 3, proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.hasFromBlockHeight = function() { + return jspb.Message.getField(this, 3) != null; +}; + + +/** + * optional uint32 count = 4; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.setCount = function(value) { + return jspb.Message.setProto3IntField(this, 4, value); +}; + + +/** + * optional bool send_transaction_hashes = 5; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.getSendTransactionHashes = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 5, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsRequest.prototype.setSendTransactionHashes = function(value) { + return jspb.Message.setProto3BooleanField(this, 5, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BloomFilter.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BloomFilter} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BloomFilter.toObject = function(includeInstance, msg) { + var f, obj = { + vData: msg.getVData_asB64(), + nHashFuncs: jspb.Message.getFieldWithDefault(msg, 2, 0), + nTweak: jspb.Message.getFieldWithDefault(msg, 3, 0), + nFlags: jspb.Message.getFieldWithDefault(msg, 4, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BloomFilter} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BloomFilter; + return proto.org.dash.platform.dapi.v0.BloomFilter.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BloomFilter} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BloomFilter} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setVData(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint32()); + msg.setNHashFuncs(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setNTweak(value); + break; + case 4: + var value = /** @type {number} */ (reader.readUint32()); + msg.setNFlags(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BloomFilter.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BloomFilter} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BloomFilter.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getVData_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getNHashFuncs(); + if (f !== 0) { + writer.writeUint32( + 2, + f + ); + } + f = message.getNTweak(); + if (f !== 0) { + writer.writeUint32( + 3, + f + ); + } + f = message.getNFlags(); + if (f !== 0) { + writer.writeUint32( + 4, + f + ); + } +}; + + +/** + * optional bytes v_data = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.getVData = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes v_data = 1; + * This is a type-conversion wrapper around `getVData()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.getVData_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getVData())); +}; + + +/** + * optional bytes v_data = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getVData()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.getVData_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getVData())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.BloomFilter} returns this + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.setVData = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional uint32 n_hash_funcs = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.getNHashFuncs = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.BloomFilter} returns this + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.setNHashFuncs = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional uint32 n_tweak = 3; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.getNTweak = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.BloomFilter} returns this + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.setNTweak = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional uint32 n_flags = 4; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.getNFlags = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.BloomFilter} returns this + */ +proto.org.dash.platform.dapi.v0.BloomFilter.prototype.setNFlags = function(value) { + return jspb.Message.setProto3IntField(this, 4, value); +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_ = [[1,2,3]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.ResponsesCase = { + RESPONSES_NOT_SET: 0, + RAW_TRANSACTIONS: 1, + INSTANT_SEND_LOCK_MESSAGES: 2, + RAW_MERKLE_BLOCK: 3 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.ResponsesCase} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.getResponsesCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.ResponsesCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + rawTransactions: (f = msg.getRawTransactions()) && proto.org.dash.platform.dapi.v0.RawTransactions.toObject(includeInstance, f), + instantSendLockMessages: (f = msg.getInstantSendLockMessages()) && proto.org.dash.platform.dapi.v0.InstantSendLockMessages.toObject(includeInstance, f), + rawMerkleBlock: msg.getRawMerkleBlock_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse; + return proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.org.dash.platform.dapi.v0.RawTransactions; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.RawTransactions.deserializeBinaryFromReader); + msg.setRawTransactions(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.InstantSendLockMessages; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.InstantSendLockMessages.deserializeBinaryFromReader); + msg.setInstantSendLockMessages(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setRawMerkleBlock(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getRawTransactions(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.org.dash.platform.dapi.v0.RawTransactions.serializeBinaryToWriter + ); + } + f = message.getInstantSendLockMessages(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.InstantSendLockMessages.serializeBinaryToWriter + ); + } + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 3)); + if (f != null) { + writer.writeBytes( + 3, + f + ); + } +}; + + +/** + * optional RawTransactions raw_transactions = 1; + * @return {?proto.org.dash.platform.dapi.v0.RawTransactions} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.getRawTransactions = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.RawTransactions} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.RawTransactions, 1)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.RawTransactions|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.setRawTransactions = function(value) { + return jspb.Message.setOneofWrapperField(this, 1, proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.clearRawTransactions = function() { + return this.setRawTransactions(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.hasRawTransactions = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional InstantSendLockMessages instant_send_lock_messages = 2; + * @return {?proto.org.dash.platform.dapi.v0.InstantSendLockMessages} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.getInstantSendLockMessages = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.InstantSendLockMessages} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.InstantSendLockMessages, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.InstantSendLockMessages|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.setInstantSendLockMessages = function(value) { + return jspb.Message.setOneofWrapperField(this, 2, proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.clearInstantSendLockMessages = function() { + return this.setInstantSendLockMessages(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.hasInstantSendLockMessages = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional bytes raw_merkle_block = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.getRawMerkleBlock = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes raw_merkle_block = 3; + * This is a type-conversion wrapper around `getRawMerkleBlock()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.getRawMerkleBlock_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getRawMerkleBlock())); +}; + + +/** + * optional bytes raw_merkle_block = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getRawMerkleBlock()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.getRawMerkleBlock_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getRawMerkleBlock())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.setRawMerkleBlock = function(value) { + return jspb.Message.setOneofField(this, 3, proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.clearRawMerkleBlock = function() { + return jspb.Message.setOneofField(this, 3, proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.TransactionsWithProofsResponse.prototype.hasRawMerkleBlock = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.org.dash.platform.dapi.v0.RawTransactions.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.RawTransactions.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.RawTransactions} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.RawTransactions.toObject = function(includeInstance, msg) { + var f, obj = { + transactionsList: msg.getTransactionsList_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.RawTransactions} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.RawTransactions; + return proto.org.dash.platform.dapi.v0.RawTransactions.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.RawTransactions} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.RawTransactions} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.addTransactions(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.RawTransactions.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.RawTransactions} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.RawTransactions.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getTransactionsList_asU8(); + if (f.length > 0) { + writer.writeRepeatedBytes( + 1, + f + ); + } +}; + + +/** + * repeated bytes transactions = 1; + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.getTransactionsList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * repeated bytes transactions = 1; + * This is a type-conversion wrapper around `getTransactionsList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.getTransactionsList_asB64 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsB64( + this.getTransactionsList())); +}; + + +/** + * repeated bytes transactions = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getTransactionsList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.getTransactionsList_asU8 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsU8( + this.getTransactionsList())); +}; + + +/** + * @param {!(Array|Array)} value + * @return {!proto.org.dash.platform.dapi.v0.RawTransactions} returns this + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.setTransactionsList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @param {number=} opt_index + * @return {!proto.org.dash.platform.dapi.v0.RawTransactions} returns this + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.addTransactions = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.org.dash.platform.dapi.v0.RawTransactions} returns this + */ +proto.org.dash.platform.dapi.v0.RawTransactions.prototype.clearTransactionsList = function() { + return this.setTransactionsList([]); +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.InstantSendLockMessages.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.toObject = function(includeInstance, msg) { + var f, obj = { + messagesList: msg.getMessagesList_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.InstantSendLockMessages; + return proto.org.dash.platform.dapi.v0.InstantSendLockMessages.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.addMessages(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.InstantSendLockMessages.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getMessagesList_asU8(); + if (f.length > 0) { + writer.writeRepeatedBytes( + 1, + f + ); + } +}; + + +/** + * repeated bytes messages = 1; + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.getMessagesList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * repeated bytes messages = 1; + * This is a type-conversion wrapper around `getMessagesList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.getMessagesList_asB64 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsB64( + this.getMessagesList())); +}; + + +/** + * repeated bytes messages = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getMessagesList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.getMessagesList_asU8 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsU8( + this.getMessagesList())); +}; + + +/** + * @param {!(Array|Array)} value + * @return {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} returns this + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.setMessagesList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @param {number=} opt_index + * @return {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} returns this + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.addMessages = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.org.dash.platform.dapi.v0.InstantSendLockMessages} returns this + */ +proto.org.dash.platform.dapi.v0.InstantSendLockMessages.prototype.clearMessagesList = function() { + return this.setMessagesList([]); +}; + + +goog.object.extend(exports, proto.org.dash.platform.dapi.v0); diff --git a/packages/dapi-grpc/clients/platform/v0/java/org/dash/platform/dapi/v0/PlatformGrpc.java b/packages/dapi-grpc/clients/platform/v0/java/org/dash/platform/dapi/v0/PlatformGrpc.java new file mode 100644 index 00000000000..9975405239b --- /dev/null +++ b/packages/dapi-grpc/clients/platform/v0/java/org/dash/platform/dapi/v0/PlatformGrpc.java @@ -0,0 +1,720 @@ +package org.dash.platform.dapi.v0; + +import static io.grpc.MethodDescriptor.generateFullMethodName; + +/** + */ +@javax.annotation.Generated( + value = "by gRPC proto compiler", + comments = "Source: platform.proto") +@io.grpc.stub.annotations.GrpcGenerated +public final class PlatformGrpc { + + private PlatformGrpc() {} + + public static final String SERVICE_NAME = "org.dash.platform.dapi.v0.Platform"; + + // Static method descriptors that strictly reflect the proto. + private static volatile io.grpc.MethodDescriptor getBroadcastStateTransitionMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "broadcastStateTransition", + requestType = org.dash.platform.dapi.v0.PlatformOuterClass.BroadcastStateTransitionRequest.class, + responseType = org.dash.platform.dapi.v0.PlatformOuterClass.BroadcastStateTransitionResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getBroadcastStateTransitionMethod() { + io.grpc.MethodDescriptor getBroadcastStateTransitionMethod; + if ((getBroadcastStateTransitionMethod = PlatformGrpc.getBroadcastStateTransitionMethod) == null) { + synchronized (PlatformGrpc.class) { + if ((getBroadcastStateTransitionMethod = PlatformGrpc.getBroadcastStateTransitionMethod) == null) { + PlatformGrpc.getBroadcastStateTransitionMethod = getBroadcastStateTransitionMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "broadcastStateTransition")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.BroadcastStateTransitionRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.BroadcastStateTransitionResponse.getDefaultInstance())) + .setSchemaDescriptor(new PlatformMethodDescriptorSupplier("broadcastStateTransition")) + .build(); + } + } + } + return getBroadcastStateTransitionMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetIdentityMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "getIdentity", + requestType = org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentityRequest.class, + responseType = org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentityResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetIdentityMethod() { + io.grpc.MethodDescriptor getGetIdentityMethod; + if ((getGetIdentityMethod = PlatformGrpc.getGetIdentityMethod) == null) { + synchronized (PlatformGrpc.class) { + if ((getGetIdentityMethod = PlatformGrpc.getGetIdentityMethod) == null) { + PlatformGrpc.getGetIdentityMethod = getGetIdentityMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "getIdentity")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentityRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentityResponse.getDefaultInstance())) + .setSchemaDescriptor(new PlatformMethodDescriptorSupplier("getIdentity")) + .build(); + } + } + } + return getGetIdentityMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetDataContractMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "getDataContract", + requestType = org.dash.platform.dapi.v0.PlatformOuterClass.GetDataContractRequest.class, + responseType = org.dash.platform.dapi.v0.PlatformOuterClass.GetDataContractResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetDataContractMethod() { + io.grpc.MethodDescriptor getGetDataContractMethod; + if ((getGetDataContractMethod = PlatformGrpc.getGetDataContractMethod) == null) { + synchronized (PlatformGrpc.class) { + if ((getGetDataContractMethod = PlatformGrpc.getGetDataContractMethod) == null) { + PlatformGrpc.getGetDataContractMethod = getGetDataContractMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "getDataContract")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.GetDataContractRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.GetDataContractResponse.getDefaultInstance())) + .setSchemaDescriptor(new PlatformMethodDescriptorSupplier("getDataContract")) + .build(); + } + } + } + return getGetDataContractMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetDocumentsMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "getDocuments", + requestType = org.dash.platform.dapi.v0.PlatformOuterClass.GetDocumentsRequest.class, + responseType = org.dash.platform.dapi.v0.PlatformOuterClass.GetDocumentsResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetDocumentsMethod() { + io.grpc.MethodDescriptor getGetDocumentsMethod; + if ((getGetDocumentsMethod = PlatformGrpc.getGetDocumentsMethod) == null) { + synchronized (PlatformGrpc.class) { + if ((getGetDocumentsMethod = PlatformGrpc.getGetDocumentsMethod) == null) { + PlatformGrpc.getGetDocumentsMethod = getGetDocumentsMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "getDocuments")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.GetDocumentsRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.GetDocumentsResponse.getDefaultInstance())) + .setSchemaDescriptor(new PlatformMethodDescriptorSupplier("getDocuments")) + .build(); + } + } + } + return getGetDocumentsMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetIdentitiesByPublicKeyHashesMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "getIdentitiesByPublicKeyHashes", + requestType = org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentitiesByPublicKeyHashesRequest.class, + responseType = org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentitiesByPublicKeyHashesResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetIdentitiesByPublicKeyHashesMethod() { + io.grpc.MethodDescriptor getGetIdentitiesByPublicKeyHashesMethod; + if ((getGetIdentitiesByPublicKeyHashesMethod = PlatformGrpc.getGetIdentitiesByPublicKeyHashesMethod) == null) { + synchronized (PlatformGrpc.class) { + if ((getGetIdentitiesByPublicKeyHashesMethod = PlatformGrpc.getGetIdentitiesByPublicKeyHashesMethod) == null) { + PlatformGrpc.getGetIdentitiesByPublicKeyHashesMethod = getGetIdentitiesByPublicKeyHashesMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "getIdentitiesByPublicKeyHashes")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentitiesByPublicKeyHashesRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentitiesByPublicKeyHashesResponse.getDefaultInstance())) + .setSchemaDescriptor(new PlatformMethodDescriptorSupplier("getIdentitiesByPublicKeyHashes")) + .build(); + } + } + } + return getGetIdentitiesByPublicKeyHashesMethod; + } + + private static volatile io.grpc.MethodDescriptor getWaitForStateTransitionResultMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "waitForStateTransitionResult", + requestType = org.dash.platform.dapi.v0.PlatformOuterClass.WaitForStateTransitionResultRequest.class, + responseType = org.dash.platform.dapi.v0.PlatformOuterClass.WaitForStateTransitionResultResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getWaitForStateTransitionResultMethod() { + io.grpc.MethodDescriptor getWaitForStateTransitionResultMethod; + if ((getWaitForStateTransitionResultMethod = PlatformGrpc.getWaitForStateTransitionResultMethod) == null) { + synchronized (PlatformGrpc.class) { + if ((getWaitForStateTransitionResultMethod = PlatformGrpc.getWaitForStateTransitionResultMethod) == null) { + PlatformGrpc.getWaitForStateTransitionResultMethod = getWaitForStateTransitionResultMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "waitForStateTransitionResult")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.WaitForStateTransitionResultRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.WaitForStateTransitionResultResponse.getDefaultInstance())) + .setSchemaDescriptor(new PlatformMethodDescriptorSupplier("waitForStateTransitionResult")) + .build(); + } + } + } + return getWaitForStateTransitionResultMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetConsensusParamsMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "getConsensusParams", + requestType = org.dash.platform.dapi.v0.PlatformOuterClass.GetConsensusParamsRequest.class, + responseType = org.dash.platform.dapi.v0.PlatformOuterClass.GetConsensusParamsResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetConsensusParamsMethod() { + io.grpc.MethodDescriptor getGetConsensusParamsMethod; + if ((getGetConsensusParamsMethod = PlatformGrpc.getGetConsensusParamsMethod) == null) { + synchronized (PlatformGrpc.class) { + if ((getGetConsensusParamsMethod = PlatformGrpc.getGetConsensusParamsMethod) == null) { + PlatformGrpc.getGetConsensusParamsMethod = getGetConsensusParamsMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "getConsensusParams")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.GetConsensusParamsRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.dash.platform.dapi.v0.PlatformOuterClass.GetConsensusParamsResponse.getDefaultInstance())) + .setSchemaDescriptor(new PlatformMethodDescriptorSupplier("getConsensusParams")) + .build(); + } + } + } + return getGetConsensusParamsMethod; + } + + /** + * Creates a new async stub that supports all call types for the service + */ + public static PlatformStub newStub(io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public PlatformStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new PlatformStub(channel, callOptions); + } + }; + return PlatformStub.newStub(factory, channel); + } + + /** + * Creates a new blocking-style stub that supports unary and streaming output calls on the service + */ + public static PlatformBlockingStub newBlockingStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public PlatformBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new PlatformBlockingStub(channel, callOptions); + } + }; + return PlatformBlockingStub.newStub(factory, channel); + } + + /** + * Creates a new ListenableFuture-style stub that supports unary calls on the service + */ + public static PlatformFutureStub newFutureStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public PlatformFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new PlatformFutureStub(channel, callOptions); + } + }; + return PlatformFutureStub.newStub(factory, channel); + } + + /** + */ + public static abstract class PlatformImplBase implements io.grpc.BindableService { + + /** + */ + public void broadcastStateTransition(org.dash.platform.dapi.v0.PlatformOuterClass.BroadcastStateTransitionRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getBroadcastStateTransitionMethod(), responseObserver); + } + + /** + */ + public void getIdentity(org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentityRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetIdentityMethod(), responseObserver); + } + + /** + */ + public void getDataContract(org.dash.platform.dapi.v0.PlatformOuterClass.GetDataContractRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetDataContractMethod(), responseObserver); + } + + /** + */ + public void getDocuments(org.dash.platform.dapi.v0.PlatformOuterClass.GetDocumentsRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetDocumentsMethod(), responseObserver); + } + + /** + */ + public void getIdentitiesByPublicKeyHashes(org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentitiesByPublicKeyHashesRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetIdentitiesByPublicKeyHashesMethod(), responseObserver); + } + + /** + */ + public void waitForStateTransitionResult(org.dash.platform.dapi.v0.PlatformOuterClass.WaitForStateTransitionResultRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getWaitForStateTransitionResultMethod(), responseObserver); + } + + /** + */ + public void getConsensusParams(org.dash.platform.dapi.v0.PlatformOuterClass.GetConsensusParamsRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetConsensusParamsMethod(), responseObserver); + } + + @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() { + return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) + .addMethod( + getBroadcastStateTransitionMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + org.dash.platform.dapi.v0.PlatformOuterClass.BroadcastStateTransitionRequest, + org.dash.platform.dapi.v0.PlatformOuterClass.BroadcastStateTransitionResponse>( + this, METHODID_BROADCAST_STATE_TRANSITION))) + .addMethod( + getGetIdentityMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentityRequest, + org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentityResponse>( + this, METHODID_GET_IDENTITY))) + .addMethod( + getGetDataContractMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + org.dash.platform.dapi.v0.PlatformOuterClass.GetDataContractRequest, + org.dash.platform.dapi.v0.PlatformOuterClass.GetDataContractResponse>( + this, METHODID_GET_DATA_CONTRACT))) + .addMethod( + getGetDocumentsMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + org.dash.platform.dapi.v0.PlatformOuterClass.GetDocumentsRequest, + org.dash.platform.dapi.v0.PlatformOuterClass.GetDocumentsResponse>( + this, METHODID_GET_DOCUMENTS))) + .addMethod( + getGetIdentitiesByPublicKeyHashesMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentitiesByPublicKeyHashesRequest, + org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentitiesByPublicKeyHashesResponse>( + this, METHODID_GET_IDENTITIES_BY_PUBLIC_KEY_HASHES))) + .addMethod( + getWaitForStateTransitionResultMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + org.dash.platform.dapi.v0.PlatformOuterClass.WaitForStateTransitionResultRequest, + org.dash.platform.dapi.v0.PlatformOuterClass.WaitForStateTransitionResultResponse>( + this, METHODID_WAIT_FOR_STATE_TRANSITION_RESULT))) + .addMethod( + getGetConsensusParamsMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + org.dash.platform.dapi.v0.PlatformOuterClass.GetConsensusParamsRequest, + org.dash.platform.dapi.v0.PlatformOuterClass.GetConsensusParamsResponse>( + this, METHODID_GET_CONSENSUS_PARAMS))) + .build(); + } + } + + /** + */ + public static final class PlatformStub extends io.grpc.stub.AbstractAsyncStub { + private PlatformStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected PlatformStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new PlatformStub(channel, callOptions); + } + + /** + */ + public void broadcastStateTransition(org.dash.platform.dapi.v0.PlatformOuterClass.BroadcastStateTransitionRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getBroadcastStateTransitionMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void getIdentity(org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentityRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetIdentityMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void getDataContract(org.dash.platform.dapi.v0.PlatformOuterClass.GetDataContractRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetDataContractMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void getDocuments(org.dash.platform.dapi.v0.PlatformOuterClass.GetDocumentsRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetDocumentsMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void getIdentitiesByPublicKeyHashes(org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentitiesByPublicKeyHashesRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetIdentitiesByPublicKeyHashesMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void waitForStateTransitionResult(org.dash.platform.dapi.v0.PlatformOuterClass.WaitForStateTransitionResultRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getWaitForStateTransitionResultMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void getConsensusParams(org.dash.platform.dapi.v0.PlatformOuterClass.GetConsensusParamsRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetConsensusParamsMethod(), getCallOptions()), request, responseObserver); + } + } + + /** + */ + public static final class PlatformBlockingStub extends io.grpc.stub.AbstractBlockingStub { + private PlatformBlockingStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected PlatformBlockingStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new PlatformBlockingStub(channel, callOptions); + } + + /** + */ + public org.dash.platform.dapi.v0.PlatformOuterClass.BroadcastStateTransitionResponse broadcastStateTransition(org.dash.platform.dapi.v0.PlatformOuterClass.BroadcastStateTransitionRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getBroadcastStateTransitionMethod(), getCallOptions(), request); + } + + /** + */ + public org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentityResponse getIdentity(org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentityRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetIdentityMethod(), getCallOptions(), request); + } + + /** + */ + public org.dash.platform.dapi.v0.PlatformOuterClass.GetDataContractResponse getDataContract(org.dash.platform.dapi.v0.PlatformOuterClass.GetDataContractRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetDataContractMethod(), getCallOptions(), request); + } + + /** + */ + public org.dash.platform.dapi.v0.PlatformOuterClass.GetDocumentsResponse getDocuments(org.dash.platform.dapi.v0.PlatformOuterClass.GetDocumentsRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetDocumentsMethod(), getCallOptions(), request); + } + + /** + */ + public org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentitiesByPublicKeyHashesResponse getIdentitiesByPublicKeyHashes(org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentitiesByPublicKeyHashesRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetIdentitiesByPublicKeyHashesMethod(), getCallOptions(), request); + } + + /** + */ + public org.dash.platform.dapi.v0.PlatformOuterClass.WaitForStateTransitionResultResponse waitForStateTransitionResult(org.dash.platform.dapi.v0.PlatformOuterClass.WaitForStateTransitionResultRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getWaitForStateTransitionResultMethod(), getCallOptions(), request); + } + + /** + */ + public org.dash.platform.dapi.v0.PlatformOuterClass.GetConsensusParamsResponse getConsensusParams(org.dash.platform.dapi.v0.PlatformOuterClass.GetConsensusParamsRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetConsensusParamsMethod(), getCallOptions(), request); + } + } + + /** + */ + public static final class PlatformFutureStub extends io.grpc.stub.AbstractFutureStub { + private PlatformFutureStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected PlatformFutureStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new PlatformFutureStub(channel, callOptions); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture broadcastStateTransition( + org.dash.platform.dapi.v0.PlatformOuterClass.BroadcastStateTransitionRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getBroadcastStateTransitionMethod(), getCallOptions()), request); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getIdentity( + org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentityRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetIdentityMethod(), getCallOptions()), request); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getDataContract( + org.dash.platform.dapi.v0.PlatformOuterClass.GetDataContractRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetDataContractMethod(), getCallOptions()), request); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getDocuments( + org.dash.platform.dapi.v0.PlatformOuterClass.GetDocumentsRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetDocumentsMethod(), getCallOptions()), request); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getIdentitiesByPublicKeyHashes( + org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentitiesByPublicKeyHashesRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetIdentitiesByPublicKeyHashesMethod(), getCallOptions()), request); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture waitForStateTransitionResult( + org.dash.platform.dapi.v0.PlatformOuterClass.WaitForStateTransitionResultRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getWaitForStateTransitionResultMethod(), getCallOptions()), request); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getConsensusParams( + org.dash.platform.dapi.v0.PlatformOuterClass.GetConsensusParamsRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetConsensusParamsMethod(), getCallOptions()), request); + } + } + + private static final int METHODID_BROADCAST_STATE_TRANSITION = 0; + private static final int METHODID_GET_IDENTITY = 1; + private static final int METHODID_GET_DATA_CONTRACT = 2; + private static final int METHODID_GET_DOCUMENTS = 3; + private static final int METHODID_GET_IDENTITIES_BY_PUBLIC_KEY_HASHES = 4; + private static final int METHODID_WAIT_FOR_STATE_TRANSITION_RESULT = 5; + private static final int METHODID_GET_CONSENSUS_PARAMS = 6; + + private static final class MethodHandlers implements + io.grpc.stub.ServerCalls.UnaryMethod, + io.grpc.stub.ServerCalls.ServerStreamingMethod, + io.grpc.stub.ServerCalls.ClientStreamingMethod, + io.grpc.stub.ServerCalls.BidiStreamingMethod { + private final PlatformImplBase serviceImpl; + private final int methodId; + + MethodHandlers(PlatformImplBase serviceImpl, int methodId) { + this.serviceImpl = serviceImpl; + this.methodId = methodId; + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_BROADCAST_STATE_TRANSITION: + serviceImpl.broadcastStateTransition((org.dash.platform.dapi.v0.PlatformOuterClass.BroadcastStateTransitionRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_IDENTITY: + serviceImpl.getIdentity((org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentityRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_DATA_CONTRACT: + serviceImpl.getDataContract((org.dash.platform.dapi.v0.PlatformOuterClass.GetDataContractRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_DOCUMENTS: + serviceImpl.getDocuments((org.dash.platform.dapi.v0.PlatformOuterClass.GetDocumentsRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_IDENTITIES_BY_PUBLIC_KEY_HASHES: + serviceImpl.getIdentitiesByPublicKeyHashes((org.dash.platform.dapi.v0.PlatformOuterClass.GetIdentitiesByPublicKeyHashesRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_WAIT_FOR_STATE_TRANSITION_RESULT: + serviceImpl.waitForStateTransitionResult((org.dash.platform.dapi.v0.PlatformOuterClass.WaitForStateTransitionResultRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_CONSENSUS_PARAMS: + serviceImpl.getConsensusParams((org.dash.platform.dapi.v0.PlatformOuterClass.GetConsensusParamsRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + default: + throw new AssertionError(); + } + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public io.grpc.stub.StreamObserver invoke( + io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + default: + throw new AssertionError(); + } + } + } + + private static abstract class PlatformBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { + PlatformBaseDescriptorSupplier() {} + + @java.lang.Override + public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { + return org.dash.platform.dapi.v0.PlatformOuterClass.getDescriptor(); + } + + @java.lang.Override + public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { + return getFileDescriptor().findServiceByName("Platform"); + } + } + + private static final class PlatformFileDescriptorSupplier + extends PlatformBaseDescriptorSupplier { + PlatformFileDescriptorSupplier() {} + } + + private static final class PlatformMethodDescriptorSupplier + extends PlatformBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { + private final String methodName; + + PlatformMethodDescriptorSupplier(String methodName) { + this.methodName = methodName; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { + return getServiceDescriptor().findMethodByName(methodName); + } + } + + private static volatile io.grpc.ServiceDescriptor serviceDescriptor; + + public static io.grpc.ServiceDescriptor getServiceDescriptor() { + io.grpc.ServiceDescriptor result = serviceDescriptor; + if (result == null) { + synchronized (PlatformGrpc.class) { + result = serviceDescriptor; + if (result == null) { + serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) + .setSchemaDescriptor(new PlatformFileDescriptorSupplier()) + .addMethod(getBroadcastStateTransitionMethod()) + .addMethod(getGetIdentityMethod()) + .addMethod(getGetDataContractMethod()) + .addMethod(getGetDocumentsMethod()) + .addMethod(getGetIdentitiesByPublicKeyHashesMethod()) + .addMethod(getWaitForStateTransitionResultMethod()) + .addMethod(getGetConsensusParamsMethod()) + .build(); + } + } + } + return result; + } +} diff --git a/packages/dapi-grpc/clients/platform/v0/nodejs/PlatformPromiseClient.js b/packages/dapi-grpc/clients/platform/v0/nodejs/PlatformPromiseClient.js new file mode 100644 index 00000000000..d1cbcf67cef --- /dev/null +++ b/packages/dapi-grpc/clients/platform/v0/nodejs/PlatformPromiseClient.js @@ -0,0 +1,337 @@ +const grpc = require('@grpc/grpc-js'); +const { promisify } = require('util'); + +const { + convertObjectToMetadata, + utils: { + isObject, + }, + client: { + interceptors: { + jsonToProtobufInterceptorFactory, + }, + converters: { + jsonToProtobufFactory, + protobufToJsonFactory, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + org: { + dash: { + platform: { + dapi: { + v0: { + BroadcastStateTransitionRequest: PBJSBroadcastStateTransitionRequest, + BroadcastStateTransitionResponse: PBJSBroadcastStateTransitionResponse, + GetIdentityRequest: PBJSGetIdentityRequest, + GetIdentityResponse: PBJSGetIdentityResponse, + GetDataContractRequest: PBJSGetDataContractRequest, + GetDataContractResponse: PBJSGetDataContractResponse, + GetDocumentsRequest: PBJSGetDocumentsRequest, + GetDocumentsResponse: PBJSGetDocumentsResponse, + GetIdentitiesByPublicKeyHashesRequest: PBJSGetIdentitiesByPublicKeyHashesRequest, + GetIdentitiesByPublicKeyHashesResponse: PBJSGetIdentitiesByPublicKeyHashesResponse, + WaitForStateTransitionResultRequest: PBJSWaitForStateTransitionResultRequest, + WaitForStateTransitionResultResponse: PBJSWaitForStateTransitionResultResponse, + GetConsensusParamsRequest: PBJSGetConsensusParamsRequest, + GetConsensusParamsResponse: PBJSGetConsensusParamsResponse, + }, + }, + }, + }, + }, +} = require('./platform_pbjs'); + +const { + BroadcastStateTransitionResponse: ProtocBroadcastStateTransitionResponse, + GetIdentityResponse: ProtocGetIdentityResponse, + GetDataContractResponse: ProtocGetDataContractResponse, + GetDocumentsResponse: ProtocGetDocumentsResponse, + GetIdentitiesByPublicKeyHashesResponse: ProtocGetIdentitiesByPublicKeyHashesResponse, + WaitForStateTransitionResultResponse: ProtocWaitForStateTransitionResultResponse, + GetConsensusParamsResponse: ProtocGetConsensusParamsResponse, +} = require('./platform_protoc'); + +const getPlatformDefinition = require('../../../../lib/getPlatformDefinition'); +const stripHostname = require('../../../../lib/utils/stripHostname'); + +const PlatformNodeJSClient = getPlatformDefinition(0); + +class PlatformPromiseClient { + /** + * @param {string} hostname + * @param {?Object} credentials + * @param {?Object} options + */ + constructor(hostname, credentials = grpc.credentials.createInsecure(), options = {}) { + const strippedHostname = stripHostname(hostname); + + this.client = new PlatformNodeJSClient(strippedHostname, credentials, options); + + this.client.broadcastStateTransition = promisify( + this.client.broadcastStateTransition.bind(this.client), + ); + + this.client.getIdentity = promisify( + this.client.getIdentity.bind(this.client), + ); + + this.client.getDataContract = promisify( + this.client.getDataContract.bind(this.client), + ); + + this.client.getDocuments = promisify( + this.client.getDocuments.bind(this.client), + ); + + this.client.getIdentitiesByPublicKeyHashes = promisify( + this.client.getIdentitiesByPublicKeyHashes.bind(this.client), + ); + + this.client.waitForStateTransitionResult = promisify( + this.client.waitForStateTransitionResult.bind(this.client), + ); + + this.client.getConsensusParams = promisify( + this.client.getConsensusParams.bind(this.client), + ); + + this.protocolVersion = undefined; + } + + /** + * @param {!BroadcastStateTransitionRequest} broadcastStateTransitionRequest + * @param {?Object} metadata + * @param {CallOptions} [options={}] + * @return {Promise} + */ + broadcastStateTransition(broadcastStateTransitionRequest, metadata = {}, options = {}) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.broadcastStateTransition( + broadcastStateTransitionRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocBroadcastStateTransitionResponse, + PBJSBroadcastStateTransitionResponse, + ), + protobufToJsonFactory( + PBJSBroadcastStateTransitionRequest, + ), + ), + ], + ...options, + }, + ); + } + + /** + * @param {!GetIdentityRequest} getIdentityRequest + * @param {?Object} metadata + * @param {CallOptions} [options={}] + * @return {Promise} + */ + getIdentity(getIdentityRequest, metadata = {}, options = {}) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.getIdentity( + getIdentityRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocGetIdentityResponse, + PBJSGetIdentityResponse, + ), + protobufToJsonFactory( + PBJSGetIdentityRequest, + ), + ), + ], + ...options, + }, + ); + } + + /** + * + * @param {!GetDataContractRequest} getDataContractRequest + * @param {?Object} metadata + * @param {CallOptions} [options={}] + * @returns {Promise} + */ + getDataContract(getDataContractRequest, metadata = {}, options = {}) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.getDataContract( + getDataContractRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocGetDataContractResponse, + PBJSGetDataContractResponse, + ), + protobufToJsonFactory( + PBJSGetDataContractRequest, + ), + ), + ], + ...options, + }, + ); + } + + /** + * + * @param {!GetDocumentsRequest} getDocumentsRequest + * @param {?Object} metadata + * @param {CallOptions} [options={}] + * @returns {Promise} + */ + getDocuments(getDocumentsRequest, metadata = {}, options = {}) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.getDocuments( + getDocumentsRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocGetDocumentsResponse, + PBJSGetDocumentsResponse, + ), + protobufToJsonFactory( + PBJSGetDocumentsRequest, + ), + ), + ], + ...options, + }, + ); + } + + /** + * @param {!GetIdentitiesByPublicKeyHashesRequest} getIdentitiesByPublicKeyHashesRequest + * @param {?Object} metadata + * @param {CallOptions} [options={}] + * @returns {Promise} + */ + getIdentitiesByPublicKeyHashes( + getIdentitiesByPublicKeyHashesRequest, metadata = {}, options = {}, + ) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.getIdentitiesByPublicKeyHashes( + getIdentitiesByPublicKeyHashesRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocGetIdentitiesByPublicKeyHashesResponse, + PBJSGetIdentitiesByPublicKeyHashesResponse, + ), + protobufToJsonFactory( + PBJSGetIdentitiesByPublicKeyHashesRequest, + ), + ), + ], + ...options, + }, + ); + } + + /** + * @param {!WaitForStateTransitionResultRequest} waitForStateTransitionResultRequest + * @param {?Object} metadata + * @param {CallOptions} [options={}] + * @returns {Promise} + */ + waitForStateTransitionResult( + waitForStateTransitionResultRequest, metadata = {}, options = {}, + ) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.waitForStateTransitionResult( + waitForStateTransitionResultRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocWaitForStateTransitionResultResponse, + PBJSWaitForStateTransitionResultResponse, + ), + protobufToJsonFactory( + PBJSWaitForStateTransitionResultRequest, + ), + ), + ], + ...options, + }, + ); + } + + /** + * @param {!GetConsensusParamsRequest} getConsensusParamsRequest + * @param {?Object} metadata + * @param {CallOptions} [options={}] + * @returns {Promise} + */ + getConsensusParams( + getConsensusParamsRequest, metadata = {}, options = {}, + ) { + if (!isObject(metadata)) { + throw new Error('metadata must be an object'); + } + + return this.client.getConsensusParams( + getConsensusParamsRequest, + convertObjectToMetadata(metadata), + { + interceptors: [ + jsonToProtobufInterceptorFactory( + jsonToProtobufFactory( + ProtocGetConsensusParamsResponse, + PBJSGetConsensusParamsResponse, + ), + protobufToJsonFactory( + PBJSGetConsensusParamsRequest, + ), + ), + ], + ...options, + }, + ); + } + + /** + * @param {string} protocolVersion + */ + setProtocolVersion(protocolVersion) { + this.setProtocolVersion = protocolVersion; + } +} + +module.exports = PlatformPromiseClient; diff --git a/packages/dapi-grpc/clients/platform/v0/nodejs/platform_pbjs.js b/packages/dapi-grpc/clients/platform/v0/nodejs/platform_pbjs.js new file mode 100644 index 00000000000..1fe4c8bab92 --- /dev/null +++ b/packages/dapi-grpc/clients/platform/v0/nodejs/platform_pbjs.js @@ -0,0 +1,4911 @@ +/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ +"use strict"; + +var $protobuf = require("@dashevo/protobufjs/minimal"); + +// Common aliases +var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util; + +// Exported root namespace +var $root = $protobuf.roots.platform_root || ($protobuf.roots.platform_root = {}); + +$root.org = (function() { + + /** + * Namespace org. + * @exports org + * @namespace + */ + var org = {}; + + org.dash = (function() { + + /** + * Namespace dash. + * @memberof org + * @namespace + */ + var dash = {}; + + dash.platform = (function() { + + /** + * Namespace platform. + * @memberof org.dash + * @namespace + */ + var platform = {}; + + platform.dapi = (function() { + + /** + * Namespace dapi. + * @memberof org.dash.platform + * @namespace + */ + var dapi = {}; + + dapi.v0 = (function() { + + /** + * Namespace v0. + * @memberof org.dash.platform.dapi + * @namespace + */ + var v0 = {}; + + v0.Platform = (function() { + + /** + * Constructs a new Platform service. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a Platform + * @extends $protobuf.rpc.Service + * @constructor + * @param {$protobuf.RPCImpl} rpcImpl RPC implementation + * @param {boolean} [requestDelimited=false] Whether requests are length-delimited + * @param {boolean} [responseDelimited=false] Whether responses are length-delimited + */ + function Platform(rpcImpl, requestDelimited, responseDelimited) { + $protobuf.rpc.Service.call(this, rpcImpl, requestDelimited, responseDelimited); + } + + (Platform.prototype = Object.create($protobuf.rpc.Service.prototype)).constructor = Platform; + + /** + * Creates new Platform service using the specified rpc implementation. + * @function create + * @memberof org.dash.platform.dapi.v0.Platform + * @static + * @param {$protobuf.RPCImpl} rpcImpl RPC implementation + * @param {boolean} [requestDelimited=false] Whether requests are length-delimited + * @param {boolean} [responseDelimited=false] Whether responses are length-delimited + * @returns {Platform} RPC service. Useful where requests and/or responses are streamed. + */ + Platform.create = function create(rpcImpl, requestDelimited, responseDelimited) { + return new this(rpcImpl, requestDelimited, responseDelimited); + }; + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Platform#broadcastStateTransition}. + * @memberof org.dash.platform.dapi.v0.Platform + * @typedef broadcastStateTransitionCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} [response] BroadcastStateTransitionResponse + */ + + /** + * Calls broadcastStateTransition. + * @function broadcastStateTransition + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IBroadcastStateTransitionRequest} request BroadcastStateTransitionRequest message or plain object + * @param {org.dash.platform.dapi.v0.Platform.broadcastStateTransitionCallback} callback Node-style callback called with the error, if any, and BroadcastStateTransitionResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Platform.prototype.broadcastStateTransition = function broadcastStateTransition(request, callback) { + return this.rpcCall(broadcastStateTransition, $root.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest, $root.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse, request, callback); + }, "name", { value: "broadcastStateTransition" }); + + /** + * Calls broadcastStateTransition. + * @function broadcastStateTransition + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IBroadcastStateTransitionRequest} request BroadcastStateTransitionRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Platform#getIdentity}. + * @memberof org.dash.platform.dapi.v0.Platform + * @typedef getIdentityCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.GetIdentityResponse} [response] GetIdentityResponse + */ + + /** + * Calls getIdentity. + * @function getIdentity + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IGetIdentityRequest} request GetIdentityRequest message or plain object + * @param {org.dash.platform.dapi.v0.Platform.getIdentityCallback} callback Node-style callback called with the error, if any, and GetIdentityResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Platform.prototype.getIdentity = function getIdentity(request, callback) { + return this.rpcCall(getIdentity, $root.org.dash.platform.dapi.v0.GetIdentityRequest, $root.org.dash.platform.dapi.v0.GetIdentityResponse, request, callback); + }, "name", { value: "getIdentity" }); + + /** + * Calls getIdentity. + * @function getIdentity + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IGetIdentityRequest} request GetIdentityRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Platform#getDataContract}. + * @memberof org.dash.platform.dapi.v0.Platform + * @typedef getDataContractCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.GetDataContractResponse} [response] GetDataContractResponse + */ + + /** + * Calls getDataContract. + * @function getDataContract + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IGetDataContractRequest} request GetDataContractRequest message or plain object + * @param {org.dash.platform.dapi.v0.Platform.getDataContractCallback} callback Node-style callback called with the error, if any, and GetDataContractResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Platform.prototype.getDataContract = function getDataContract(request, callback) { + return this.rpcCall(getDataContract, $root.org.dash.platform.dapi.v0.GetDataContractRequest, $root.org.dash.platform.dapi.v0.GetDataContractResponse, request, callback); + }, "name", { value: "getDataContract" }); + + /** + * Calls getDataContract. + * @function getDataContract + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IGetDataContractRequest} request GetDataContractRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Platform#getDocuments}. + * @memberof org.dash.platform.dapi.v0.Platform + * @typedef getDocumentsCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.GetDocumentsResponse} [response] GetDocumentsResponse + */ + + /** + * Calls getDocuments. + * @function getDocuments + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IGetDocumentsRequest} request GetDocumentsRequest message or plain object + * @param {org.dash.platform.dapi.v0.Platform.getDocumentsCallback} callback Node-style callback called with the error, if any, and GetDocumentsResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Platform.prototype.getDocuments = function getDocuments(request, callback) { + return this.rpcCall(getDocuments, $root.org.dash.platform.dapi.v0.GetDocumentsRequest, $root.org.dash.platform.dapi.v0.GetDocumentsResponse, request, callback); + }, "name", { value: "getDocuments" }); + + /** + * Calls getDocuments. + * @function getDocuments + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IGetDocumentsRequest} request GetDocumentsRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Platform#getIdentitiesByPublicKeyHashes}. + * @memberof org.dash.platform.dapi.v0.Platform + * @typedef getIdentitiesByPublicKeyHashesCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} [response] GetIdentitiesByPublicKeyHashesResponse + */ + + /** + * Calls getIdentitiesByPublicKeyHashes. + * @function getIdentitiesByPublicKeyHashes + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IGetIdentitiesByPublicKeyHashesRequest} request GetIdentitiesByPublicKeyHashesRequest message or plain object + * @param {org.dash.platform.dapi.v0.Platform.getIdentitiesByPublicKeyHashesCallback} callback Node-style callback called with the error, if any, and GetIdentitiesByPublicKeyHashesResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Platform.prototype.getIdentitiesByPublicKeyHashes = function getIdentitiesByPublicKeyHashes(request, callback) { + return this.rpcCall(getIdentitiesByPublicKeyHashes, $root.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest, $root.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse, request, callback); + }, "name", { value: "getIdentitiesByPublicKeyHashes" }); + + /** + * Calls getIdentitiesByPublicKeyHashes. + * @function getIdentitiesByPublicKeyHashes + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IGetIdentitiesByPublicKeyHashesRequest} request GetIdentitiesByPublicKeyHashesRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Platform#waitForStateTransitionResult}. + * @memberof org.dash.platform.dapi.v0.Platform + * @typedef waitForStateTransitionResultCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} [response] WaitForStateTransitionResultResponse + */ + + /** + * Calls waitForStateTransitionResult. + * @function waitForStateTransitionResult + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IWaitForStateTransitionResultRequest} request WaitForStateTransitionResultRequest message or plain object + * @param {org.dash.platform.dapi.v0.Platform.waitForStateTransitionResultCallback} callback Node-style callback called with the error, if any, and WaitForStateTransitionResultResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Platform.prototype.waitForStateTransitionResult = function waitForStateTransitionResult(request, callback) { + return this.rpcCall(waitForStateTransitionResult, $root.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest, $root.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse, request, callback); + }, "name", { value: "waitForStateTransitionResult" }); + + /** + * Calls waitForStateTransitionResult. + * @function waitForStateTransitionResult + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IWaitForStateTransitionResultRequest} request WaitForStateTransitionResultRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link org.dash.platform.dapi.v0.Platform#getConsensusParams}. + * @memberof org.dash.platform.dapi.v0.Platform + * @typedef getConsensusParamsCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {org.dash.platform.dapi.v0.GetConsensusParamsResponse} [response] GetConsensusParamsResponse + */ + + /** + * Calls getConsensusParams. + * @function getConsensusParams + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IGetConsensusParamsRequest} request GetConsensusParamsRequest message or plain object + * @param {org.dash.platform.dapi.v0.Platform.getConsensusParamsCallback} callback Node-style callback called with the error, if any, and GetConsensusParamsResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(Platform.prototype.getConsensusParams = function getConsensusParams(request, callback) { + return this.rpcCall(getConsensusParams, $root.org.dash.platform.dapi.v0.GetConsensusParamsRequest, $root.org.dash.platform.dapi.v0.GetConsensusParamsResponse, request, callback); + }, "name", { value: "getConsensusParams" }); + + /** + * Calls getConsensusParams. + * @function getConsensusParams + * @memberof org.dash.platform.dapi.v0.Platform + * @instance + * @param {org.dash.platform.dapi.v0.IGetConsensusParamsRequest} request GetConsensusParamsRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + return Platform; + })(); + + v0.Proof = (function() { + + /** + * Properties of a Proof. + * @memberof org.dash.platform.dapi.v0 + * @interface IProof + * @property {Uint8Array|null} [merkleProof] Proof merkleProof + * @property {Uint8Array|null} [signatureLlmqHash] Proof signatureLlmqHash + * @property {Uint8Array|null} [signature] Proof signature + */ + + /** + * Constructs a new Proof. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a Proof. + * @implements IProof + * @constructor + * @param {org.dash.platform.dapi.v0.IProof=} [properties] Properties to set + */ + function Proof(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Proof merkleProof. + * @member {Uint8Array} merkleProof + * @memberof org.dash.platform.dapi.v0.Proof + * @instance + */ + Proof.prototype.merkleProof = $util.newBuffer([]); + + /** + * Proof signatureLlmqHash. + * @member {Uint8Array} signatureLlmqHash + * @memberof org.dash.platform.dapi.v0.Proof + * @instance + */ + Proof.prototype.signatureLlmqHash = $util.newBuffer([]); + + /** + * Proof signature. + * @member {Uint8Array} signature + * @memberof org.dash.platform.dapi.v0.Proof + * @instance + */ + Proof.prototype.signature = $util.newBuffer([]); + + /** + * Creates a new Proof instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.Proof + * @static + * @param {org.dash.platform.dapi.v0.IProof=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.Proof} Proof instance + */ + Proof.create = function create(properties) { + return new Proof(properties); + }; + + /** + * Encodes the specified Proof message. Does not implicitly {@link org.dash.platform.dapi.v0.Proof.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.Proof + * @static + * @param {org.dash.platform.dapi.v0.IProof} message Proof message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Proof.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.merkleProof != null && Object.hasOwnProperty.call(message, "merkleProof")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.merkleProof); + if (message.signatureLlmqHash != null && Object.hasOwnProperty.call(message, "signatureLlmqHash")) + writer.uint32(/* id 2, wireType 2 =*/18).bytes(message.signatureLlmqHash); + if (message.signature != null && Object.hasOwnProperty.call(message, "signature")) + writer.uint32(/* id 3, wireType 2 =*/26).bytes(message.signature); + return writer; + }; + + /** + * Encodes the specified Proof message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.Proof.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.Proof + * @static + * @param {org.dash.platform.dapi.v0.IProof} message Proof message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Proof.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Proof message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.Proof + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.Proof} Proof + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Proof.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.Proof(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.merkleProof = reader.bytes(); + break; + case 2: + message.signatureLlmqHash = reader.bytes(); + break; + case 3: + message.signature = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Proof message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.Proof + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.Proof} Proof + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Proof.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Proof message. + * @function verify + * @memberof org.dash.platform.dapi.v0.Proof + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Proof.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.merkleProof != null && message.hasOwnProperty("merkleProof")) + if (!(message.merkleProof && typeof message.merkleProof.length === "number" || $util.isString(message.merkleProof))) + return "merkleProof: buffer expected"; + if (message.signatureLlmqHash != null && message.hasOwnProperty("signatureLlmqHash")) + if (!(message.signatureLlmqHash && typeof message.signatureLlmqHash.length === "number" || $util.isString(message.signatureLlmqHash))) + return "signatureLlmqHash: buffer expected"; + if (message.signature != null && message.hasOwnProperty("signature")) + if (!(message.signature && typeof message.signature.length === "number" || $util.isString(message.signature))) + return "signature: buffer expected"; + return null; + }; + + /** + * Creates a Proof message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.Proof + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.Proof} Proof + */ + Proof.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.Proof) + return object; + var message = new $root.org.dash.platform.dapi.v0.Proof(); + if (object.merkleProof != null) + if (typeof object.merkleProof === "string") + $util.base64.decode(object.merkleProof, message.merkleProof = $util.newBuffer($util.base64.length(object.merkleProof)), 0); + else if (object.merkleProof.length >= 0) + message.merkleProof = object.merkleProof; + if (object.signatureLlmqHash != null) + if (typeof object.signatureLlmqHash === "string") + $util.base64.decode(object.signatureLlmqHash, message.signatureLlmqHash = $util.newBuffer($util.base64.length(object.signatureLlmqHash)), 0); + else if (object.signatureLlmqHash.length >= 0) + message.signatureLlmqHash = object.signatureLlmqHash; + if (object.signature != null) + if (typeof object.signature === "string") + $util.base64.decode(object.signature, message.signature = $util.newBuffer($util.base64.length(object.signature)), 0); + else if (object.signature.length >= 0) + message.signature = object.signature; + return message; + }; + + /** + * Creates a plain object from a Proof message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.Proof + * @static + * @param {org.dash.platform.dapi.v0.Proof} message Proof + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Proof.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if (options.bytes === String) + object.merkleProof = ""; + else { + object.merkleProof = []; + if (options.bytes !== Array) + object.merkleProof = $util.newBuffer(object.merkleProof); + } + if (options.bytes === String) + object.signatureLlmqHash = ""; + else { + object.signatureLlmqHash = []; + if (options.bytes !== Array) + object.signatureLlmqHash = $util.newBuffer(object.signatureLlmqHash); + } + if (options.bytes === String) + object.signature = ""; + else { + object.signature = []; + if (options.bytes !== Array) + object.signature = $util.newBuffer(object.signature); + } + } + if (message.merkleProof != null && message.hasOwnProperty("merkleProof")) + object.merkleProof = options.bytes === String ? $util.base64.encode(message.merkleProof, 0, message.merkleProof.length) : options.bytes === Array ? Array.prototype.slice.call(message.merkleProof) : message.merkleProof; + if (message.signatureLlmqHash != null && message.hasOwnProperty("signatureLlmqHash")) + object.signatureLlmqHash = options.bytes === String ? $util.base64.encode(message.signatureLlmqHash, 0, message.signatureLlmqHash.length) : options.bytes === Array ? Array.prototype.slice.call(message.signatureLlmqHash) : message.signatureLlmqHash; + if (message.signature != null && message.hasOwnProperty("signature")) + object.signature = options.bytes === String ? $util.base64.encode(message.signature, 0, message.signature.length) : options.bytes === Array ? Array.prototype.slice.call(message.signature) : message.signature; + return object; + }; + + /** + * Converts this Proof to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.Proof + * @instance + * @returns {Object.} JSON object + */ + Proof.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Proof; + })(); + + v0.ResponseMetadata = (function() { + + /** + * Properties of a ResponseMetadata. + * @memberof org.dash.platform.dapi.v0 + * @interface IResponseMetadata + * @property {number|Long|null} [height] ResponseMetadata height + * @property {number|null} [coreChainLockedHeight] ResponseMetadata coreChainLockedHeight + */ + + /** + * Constructs a new ResponseMetadata. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a ResponseMetadata. + * @implements IResponseMetadata + * @constructor + * @param {org.dash.platform.dapi.v0.IResponseMetadata=} [properties] Properties to set + */ + function ResponseMetadata(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * ResponseMetadata height. + * @member {number|Long} height + * @memberof org.dash.platform.dapi.v0.ResponseMetadata + * @instance + */ + ResponseMetadata.prototype.height = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * ResponseMetadata coreChainLockedHeight. + * @member {number} coreChainLockedHeight + * @memberof org.dash.platform.dapi.v0.ResponseMetadata + * @instance + */ + ResponseMetadata.prototype.coreChainLockedHeight = 0; + + /** + * Creates a new ResponseMetadata instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.ResponseMetadata + * @static + * @param {org.dash.platform.dapi.v0.IResponseMetadata=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.ResponseMetadata} ResponseMetadata instance + */ + ResponseMetadata.create = function create(properties) { + return new ResponseMetadata(properties); + }; + + /** + * Encodes the specified ResponseMetadata message. Does not implicitly {@link org.dash.platform.dapi.v0.ResponseMetadata.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.ResponseMetadata + * @static + * @param {org.dash.platform.dapi.v0.IResponseMetadata} message ResponseMetadata message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ResponseMetadata.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.height != null && Object.hasOwnProperty.call(message, "height")) + writer.uint32(/* id 1, wireType 0 =*/8).int64(message.height); + if (message.coreChainLockedHeight != null && Object.hasOwnProperty.call(message, "coreChainLockedHeight")) + writer.uint32(/* id 2, wireType 0 =*/16).uint32(message.coreChainLockedHeight); + return writer; + }; + + /** + * Encodes the specified ResponseMetadata message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.ResponseMetadata.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.ResponseMetadata + * @static + * @param {org.dash.platform.dapi.v0.IResponseMetadata} message ResponseMetadata message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ResponseMetadata.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a ResponseMetadata message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.ResponseMetadata + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.ResponseMetadata} ResponseMetadata + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ResponseMetadata.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.ResponseMetadata(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.height = reader.int64(); + break; + case 2: + message.coreChainLockedHeight = reader.uint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a ResponseMetadata message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.ResponseMetadata + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.ResponseMetadata} ResponseMetadata + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ResponseMetadata.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a ResponseMetadata message. + * @function verify + * @memberof org.dash.platform.dapi.v0.ResponseMetadata + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + ResponseMetadata.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.height != null && message.hasOwnProperty("height")) + if (!$util.isInteger(message.height) && !(message.height && $util.isInteger(message.height.low) && $util.isInteger(message.height.high))) + return "height: integer|Long expected"; + if (message.coreChainLockedHeight != null && message.hasOwnProperty("coreChainLockedHeight")) + if (!$util.isInteger(message.coreChainLockedHeight)) + return "coreChainLockedHeight: integer expected"; + return null; + }; + + /** + * Creates a ResponseMetadata message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.ResponseMetadata + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.ResponseMetadata} ResponseMetadata + */ + ResponseMetadata.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.ResponseMetadata) + return object; + var message = new $root.org.dash.platform.dapi.v0.ResponseMetadata(); + if (object.height != null) + if ($util.Long) + (message.height = $util.Long.fromValue(object.height)).unsigned = false; + else if (typeof object.height === "string") + message.height = parseInt(object.height, 10); + else if (typeof object.height === "number") + message.height = object.height; + else if (typeof object.height === "object") + message.height = new $util.LongBits(object.height.low >>> 0, object.height.high >>> 0).toNumber(); + if (object.coreChainLockedHeight != null) + message.coreChainLockedHeight = object.coreChainLockedHeight >>> 0; + return message; + }; + + /** + * Creates a plain object from a ResponseMetadata message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.ResponseMetadata + * @static + * @param {org.dash.platform.dapi.v0.ResponseMetadata} message ResponseMetadata + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ResponseMetadata.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.height = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.height = options.longs === String ? "0" : 0; + object.coreChainLockedHeight = 0; + } + if (message.height != null && message.hasOwnProperty("height")) + if (typeof message.height === "number") + object.height = options.longs === String ? String(message.height) : message.height; + else + object.height = options.longs === String ? $util.Long.prototype.toString.call(message.height) : options.longs === Number ? new $util.LongBits(message.height.low >>> 0, message.height.high >>> 0).toNumber() : message.height; + if (message.coreChainLockedHeight != null && message.hasOwnProperty("coreChainLockedHeight")) + object.coreChainLockedHeight = message.coreChainLockedHeight; + return object; + }; + + /** + * Converts this ResponseMetadata to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.ResponseMetadata + * @instance + * @returns {Object.} JSON object + */ + ResponseMetadata.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return ResponseMetadata; + })(); + + v0.StateTransitionBroadcastError = (function() { + + /** + * Properties of a StateTransitionBroadcastError. + * @memberof org.dash.platform.dapi.v0 + * @interface IStateTransitionBroadcastError + * @property {number|null} [code] StateTransitionBroadcastError code + * @property {string|null} [message] StateTransitionBroadcastError message + * @property {Uint8Array|null} [data] StateTransitionBroadcastError data + */ + + /** + * Constructs a new StateTransitionBroadcastError. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a StateTransitionBroadcastError. + * @implements IStateTransitionBroadcastError + * @constructor + * @param {org.dash.platform.dapi.v0.IStateTransitionBroadcastError=} [properties] Properties to set + */ + function StateTransitionBroadcastError(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * StateTransitionBroadcastError code. + * @member {number} code + * @memberof org.dash.platform.dapi.v0.StateTransitionBroadcastError + * @instance + */ + StateTransitionBroadcastError.prototype.code = 0; + + /** + * StateTransitionBroadcastError message. + * @member {string} message + * @memberof org.dash.platform.dapi.v0.StateTransitionBroadcastError + * @instance + */ + StateTransitionBroadcastError.prototype.message = ""; + + /** + * StateTransitionBroadcastError data. + * @member {Uint8Array} data + * @memberof org.dash.platform.dapi.v0.StateTransitionBroadcastError + * @instance + */ + StateTransitionBroadcastError.prototype.data = $util.newBuffer([]); + + /** + * Creates a new StateTransitionBroadcastError instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.StateTransitionBroadcastError + * @static + * @param {org.dash.platform.dapi.v0.IStateTransitionBroadcastError=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.StateTransitionBroadcastError} StateTransitionBroadcastError instance + */ + StateTransitionBroadcastError.create = function create(properties) { + return new StateTransitionBroadcastError(properties); + }; + + /** + * Encodes the specified StateTransitionBroadcastError message. Does not implicitly {@link org.dash.platform.dapi.v0.StateTransitionBroadcastError.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.StateTransitionBroadcastError + * @static + * @param {org.dash.platform.dapi.v0.IStateTransitionBroadcastError} message StateTransitionBroadcastError message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + StateTransitionBroadcastError.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.code != null && Object.hasOwnProperty.call(message, "code")) + writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.code); + if (message.message != null && Object.hasOwnProperty.call(message, "message")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.message); + if (message.data != null && Object.hasOwnProperty.call(message, "data")) + writer.uint32(/* id 3, wireType 2 =*/26).bytes(message.data); + return writer; + }; + + /** + * Encodes the specified StateTransitionBroadcastError message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.StateTransitionBroadcastError.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.StateTransitionBroadcastError + * @static + * @param {org.dash.platform.dapi.v0.IStateTransitionBroadcastError} message StateTransitionBroadcastError message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + StateTransitionBroadcastError.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a StateTransitionBroadcastError message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.StateTransitionBroadcastError + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.StateTransitionBroadcastError} StateTransitionBroadcastError + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + StateTransitionBroadcastError.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.StateTransitionBroadcastError(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.code = reader.uint32(); + break; + case 2: + message.message = reader.string(); + break; + case 3: + message.data = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a StateTransitionBroadcastError message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.StateTransitionBroadcastError + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.StateTransitionBroadcastError} StateTransitionBroadcastError + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + StateTransitionBroadcastError.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a StateTransitionBroadcastError message. + * @function verify + * @memberof org.dash.platform.dapi.v0.StateTransitionBroadcastError + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + StateTransitionBroadcastError.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.code != null && message.hasOwnProperty("code")) + if (!$util.isInteger(message.code)) + return "code: integer expected"; + if (message.message != null && message.hasOwnProperty("message")) + if (!$util.isString(message.message)) + return "message: string expected"; + if (message.data != null && message.hasOwnProperty("data")) + if (!(message.data && typeof message.data.length === "number" || $util.isString(message.data))) + return "data: buffer expected"; + return null; + }; + + /** + * Creates a StateTransitionBroadcastError message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.StateTransitionBroadcastError + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.StateTransitionBroadcastError} StateTransitionBroadcastError + */ + StateTransitionBroadcastError.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.StateTransitionBroadcastError) + return object; + var message = new $root.org.dash.platform.dapi.v0.StateTransitionBroadcastError(); + if (object.code != null) + message.code = object.code >>> 0; + if (object.message != null) + message.message = String(object.message); + if (object.data != null) + if (typeof object.data === "string") + $util.base64.decode(object.data, message.data = $util.newBuffer($util.base64.length(object.data)), 0); + else if (object.data.length >= 0) + message.data = object.data; + return message; + }; + + /** + * Creates a plain object from a StateTransitionBroadcastError message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.StateTransitionBroadcastError + * @static + * @param {org.dash.platform.dapi.v0.StateTransitionBroadcastError} message StateTransitionBroadcastError + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + StateTransitionBroadcastError.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.code = 0; + object.message = ""; + if (options.bytes === String) + object.data = ""; + else { + object.data = []; + if (options.bytes !== Array) + object.data = $util.newBuffer(object.data); + } + } + if (message.code != null && message.hasOwnProperty("code")) + object.code = message.code; + if (message.message != null && message.hasOwnProperty("message")) + object.message = message.message; + if (message.data != null && message.hasOwnProperty("data")) + object.data = options.bytes === String ? $util.base64.encode(message.data, 0, message.data.length) : options.bytes === Array ? Array.prototype.slice.call(message.data) : message.data; + return object; + }; + + /** + * Converts this StateTransitionBroadcastError to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.StateTransitionBroadcastError + * @instance + * @returns {Object.} JSON object + */ + StateTransitionBroadcastError.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return StateTransitionBroadcastError; + })(); + + v0.BroadcastStateTransitionRequest = (function() { + + /** + * Properties of a BroadcastStateTransitionRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface IBroadcastStateTransitionRequest + * @property {Uint8Array|null} [stateTransition] BroadcastStateTransitionRequest stateTransition + */ + + /** + * Constructs a new BroadcastStateTransitionRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a BroadcastStateTransitionRequest. + * @implements IBroadcastStateTransitionRequest + * @constructor + * @param {org.dash.platform.dapi.v0.IBroadcastStateTransitionRequest=} [properties] Properties to set + */ + function BroadcastStateTransitionRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * BroadcastStateTransitionRequest stateTransition. + * @member {Uint8Array} stateTransition + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionRequest + * @instance + */ + BroadcastStateTransitionRequest.prototype.stateTransition = $util.newBuffer([]); + + /** + * Creates a new BroadcastStateTransitionRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionRequest + * @static + * @param {org.dash.platform.dapi.v0.IBroadcastStateTransitionRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} BroadcastStateTransitionRequest instance + */ + BroadcastStateTransitionRequest.create = function create(properties) { + return new BroadcastStateTransitionRequest(properties); + }; + + /** + * Encodes the specified BroadcastStateTransitionRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionRequest + * @static + * @param {org.dash.platform.dapi.v0.IBroadcastStateTransitionRequest} message BroadcastStateTransitionRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BroadcastStateTransitionRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.stateTransition != null && Object.hasOwnProperty.call(message, "stateTransition")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.stateTransition); + return writer; + }; + + /** + * Encodes the specified BroadcastStateTransitionRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionRequest + * @static + * @param {org.dash.platform.dapi.v0.IBroadcastStateTransitionRequest} message BroadcastStateTransitionRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BroadcastStateTransitionRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a BroadcastStateTransitionRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} BroadcastStateTransitionRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BroadcastStateTransitionRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.stateTransition = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a BroadcastStateTransitionRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} BroadcastStateTransitionRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BroadcastStateTransitionRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a BroadcastStateTransitionRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + BroadcastStateTransitionRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.stateTransition != null && message.hasOwnProperty("stateTransition")) + if (!(message.stateTransition && typeof message.stateTransition.length === "number" || $util.isString(message.stateTransition))) + return "stateTransition: buffer expected"; + return null; + }; + + /** + * Creates a BroadcastStateTransitionRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} BroadcastStateTransitionRequest + */ + BroadcastStateTransitionRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest) + return object; + var message = new $root.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest(); + if (object.stateTransition != null) + if (typeof object.stateTransition === "string") + $util.base64.decode(object.stateTransition, message.stateTransition = $util.newBuffer($util.base64.length(object.stateTransition)), 0); + else if (object.stateTransition.length >= 0) + message.stateTransition = object.stateTransition; + return message; + }; + + /** + * Creates a plain object from a BroadcastStateTransitionRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionRequest + * @static + * @param {org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} message BroadcastStateTransitionRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + BroadcastStateTransitionRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + if (options.bytes === String) + object.stateTransition = ""; + else { + object.stateTransition = []; + if (options.bytes !== Array) + object.stateTransition = $util.newBuffer(object.stateTransition); + } + if (message.stateTransition != null && message.hasOwnProperty("stateTransition")) + object.stateTransition = options.bytes === String ? $util.base64.encode(message.stateTransition, 0, message.stateTransition.length) : options.bytes === Array ? Array.prototype.slice.call(message.stateTransition) : message.stateTransition; + return object; + }; + + /** + * Converts this BroadcastStateTransitionRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionRequest + * @instance + * @returns {Object.} JSON object + */ + BroadcastStateTransitionRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return BroadcastStateTransitionRequest; + })(); + + v0.BroadcastStateTransitionResponse = (function() { + + /** + * Properties of a BroadcastStateTransitionResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface IBroadcastStateTransitionResponse + */ + + /** + * Constructs a new BroadcastStateTransitionResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a BroadcastStateTransitionResponse. + * @implements IBroadcastStateTransitionResponse + * @constructor + * @param {org.dash.platform.dapi.v0.IBroadcastStateTransitionResponse=} [properties] Properties to set + */ + function BroadcastStateTransitionResponse(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Creates a new BroadcastStateTransitionResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionResponse + * @static + * @param {org.dash.platform.dapi.v0.IBroadcastStateTransitionResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} BroadcastStateTransitionResponse instance + */ + BroadcastStateTransitionResponse.create = function create(properties) { + return new BroadcastStateTransitionResponse(properties); + }; + + /** + * Encodes the specified BroadcastStateTransitionResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionResponse + * @static + * @param {org.dash.platform.dapi.v0.IBroadcastStateTransitionResponse} message BroadcastStateTransitionResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BroadcastStateTransitionResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + return writer; + }; + + /** + * Encodes the specified BroadcastStateTransitionResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionResponse + * @static + * @param {org.dash.platform.dapi.v0.IBroadcastStateTransitionResponse} message BroadcastStateTransitionResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BroadcastStateTransitionResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a BroadcastStateTransitionResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} BroadcastStateTransitionResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BroadcastStateTransitionResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a BroadcastStateTransitionResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} BroadcastStateTransitionResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BroadcastStateTransitionResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a BroadcastStateTransitionResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + BroadcastStateTransitionResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + return null; + }; + + /** + * Creates a BroadcastStateTransitionResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} BroadcastStateTransitionResponse + */ + BroadcastStateTransitionResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse) + return object; + return new $root.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse(); + }; + + /** + * Creates a plain object from a BroadcastStateTransitionResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionResponse + * @static + * @param {org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} message BroadcastStateTransitionResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + BroadcastStateTransitionResponse.toObject = function toObject() { + return {}; + }; + + /** + * Converts this BroadcastStateTransitionResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.BroadcastStateTransitionResponse + * @instance + * @returns {Object.} JSON object + */ + BroadcastStateTransitionResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return BroadcastStateTransitionResponse; + })(); + + v0.GetIdentityRequest = (function() { + + /** + * Properties of a GetIdentityRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetIdentityRequest + * @property {Uint8Array|null} [id] GetIdentityRequest id + * @property {boolean|null} [prove] GetIdentityRequest prove + */ + + /** + * Constructs a new GetIdentityRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetIdentityRequest. + * @implements IGetIdentityRequest + * @constructor + * @param {org.dash.platform.dapi.v0.IGetIdentityRequest=} [properties] Properties to set + */ + function GetIdentityRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetIdentityRequest id. + * @member {Uint8Array} id + * @memberof org.dash.platform.dapi.v0.GetIdentityRequest + * @instance + */ + GetIdentityRequest.prototype.id = $util.newBuffer([]); + + /** + * GetIdentityRequest prove. + * @member {boolean} prove + * @memberof org.dash.platform.dapi.v0.GetIdentityRequest + * @instance + */ + GetIdentityRequest.prototype.prove = false; + + /** + * Creates a new GetIdentityRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetIdentityRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetIdentityRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetIdentityRequest} GetIdentityRequest instance + */ + GetIdentityRequest.create = function create(properties) { + return new GetIdentityRequest(properties); + }; + + /** + * Encodes the specified GetIdentityRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.GetIdentityRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetIdentityRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetIdentityRequest} message GetIdentityRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetIdentityRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.id != null && Object.hasOwnProperty.call(message, "id")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.id); + if (message.prove != null && Object.hasOwnProperty.call(message, "prove")) + writer.uint32(/* id 2, wireType 0 =*/16).bool(message.prove); + return writer; + }; + + /** + * Encodes the specified GetIdentityRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetIdentityRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetIdentityRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetIdentityRequest} message GetIdentityRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetIdentityRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetIdentityRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetIdentityRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetIdentityRequest} GetIdentityRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetIdentityRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetIdentityRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.bytes(); + break; + case 2: + message.prove = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetIdentityRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetIdentityRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetIdentityRequest} GetIdentityRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetIdentityRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetIdentityRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetIdentityRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetIdentityRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.id != null && message.hasOwnProperty("id")) + if (!(message.id && typeof message.id.length === "number" || $util.isString(message.id))) + return "id: buffer expected"; + if (message.prove != null && message.hasOwnProperty("prove")) + if (typeof message.prove !== "boolean") + return "prove: boolean expected"; + return null; + }; + + /** + * Creates a GetIdentityRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetIdentityRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetIdentityRequest} GetIdentityRequest + */ + GetIdentityRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetIdentityRequest) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetIdentityRequest(); + if (object.id != null) + if (typeof object.id === "string") + $util.base64.decode(object.id, message.id = $util.newBuffer($util.base64.length(object.id)), 0); + else if (object.id.length >= 0) + message.id = object.id; + if (object.prove != null) + message.prove = Boolean(object.prove); + return message; + }; + + /** + * Creates a plain object from a GetIdentityRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetIdentityRequest + * @static + * @param {org.dash.platform.dapi.v0.GetIdentityRequest} message GetIdentityRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetIdentityRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if (options.bytes === String) + object.id = ""; + else { + object.id = []; + if (options.bytes !== Array) + object.id = $util.newBuffer(object.id); + } + object.prove = false; + } + if (message.id != null && message.hasOwnProperty("id")) + object.id = options.bytes === String ? $util.base64.encode(message.id, 0, message.id.length) : options.bytes === Array ? Array.prototype.slice.call(message.id) : message.id; + if (message.prove != null && message.hasOwnProperty("prove")) + object.prove = message.prove; + return object; + }; + + /** + * Converts this GetIdentityRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetIdentityRequest + * @instance + * @returns {Object.} JSON object + */ + GetIdentityRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetIdentityRequest; + })(); + + v0.GetIdentityResponse = (function() { + + /** + * Properties of a GetIdentityResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetIdentityResponse + * @property {Uint8Array|null} [identity] GetIdentityResponse identity + * @property {org.dash.platform.dapi.v0.IProof|null} [proof] GetIdentityResponse proof + * @property {org.dash.platform.dapi.v0.IResponseMetadata|null} [metadata] GetIdentityResponse metadata + */ + + /** + * Constructs a new GetIdentityResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetIdentityResponse. + * @implements IGetIdentityResponse + * @constructor + * @param {org.dash.platform.dapi.v0.IGetIdentityResponse=} [properties] Properties to set + */ + function GetIdentityResponse(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetIdentityResponse identity. + * @member {Uint8Array} identity + * @memberof org.dash.platform.dapi.v0.GetIdentityResponse + * @instance + */ + GetIdentityResponse.prototype.identity = $util.newBuffer([]); + + /** + * GetIdentityResponse proof. + * @member {org.dash.platform.dapi.v0.IProof|null|undefined} proof + * @memberof org.dash.platform.dapi.v0.GetIdentityResponse + * @instance + */ + GetIdentityResponse.prototype.proof = null; + + /** + * GetIdentityResponse metadata. + * @member {org.dash.platform.dapi.v0.IResponseMetadata|null|undefined} metadata + * @memberof org.dash.platform.dapi.v0.GetIdentityResponse + * @instance + */ + GetIdentityResponse.prototype.metadata = null; + + /** + * Creates a new GetIdentityResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetIdentityResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetIdentityResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetIdentityResponse} GetIdentityResponse instance + */ + GetIdentityResponse.create = function create(properties) { + return new GetIdentityResponse(properties); + }; + + /** + * Encodes the specified GetIdentityResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.GetIdentityResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetIdentityResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetIdentityResponse} message GetIdentityResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetIdentityResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.identity != null && Object.hasOwnProperty.call(message, "identity")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.identity); + if (message.proof != null && Object.hasOwnProperty.call(message, "proof")) + $root.org.dash.platform.dapi.v0.Proof.encode(message.proof, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + if (message.metadata != null && Object.hasOwnProperty.call(message, "metadata")) + $root.org.dash.platform.dapi.v0.ResponseMetadata.encode(message.metadata, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified GetIdentityResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetIdentityResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetIdentityResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetIdentityResponse} message GetIdentityResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetIdentityResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetIdentityResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetIdentityResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetIdentityResponse} GetIdentityResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetIdentityResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetIdentityResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.identity = reader.bytes(); + break; + case 2: + message.proof = $root.org.dash.platform.dapi.v0.Proof.decode(reader, reader.uint32()); + break; + case 3: + message.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetIdentityResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetIdentityResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetIdentityResponse} GetIdentityResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetIdentityResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetIdentityResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetIdentityResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetIdentityResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.identity != null && message.hasOwnProperty("identity")) + if (!(message.identity && typeof message.identity.length === "number" || $util.isString(message.identity))) + return "identity: buffer expected"; + if (message.proof != null && message.hasOwnProperty("proof")) { + var error = $root.org.dash.platform.dapi.v0.Proof.verify(message.proof); + if (error) + return "proof." + error; + } + if (message.metadata != null && message.hasOwnProperty("metadata")) { + var error = $root.org.dash.platform.dapi.v0.ResponseMetadata.verify(message.metadata); + if (error) + return "metadata." + error; + } + return null; + }; + + /** + * Creates a GetIdentityResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetIdentityResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetIdentityResponse} GetIdentityResponse + */ + GetIdentityResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetIdentityResponse) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetIdentityResponse(); + if (object.identity != null) + if (typeof object.identity === "string") + $util.base64.decode(object.identity, message.identity = $util.newBuffer($util.base64.length(object.identity)), 0); + else if (object.identity.length >= 0) + message.identity = object.identity; + if (object.proof != null) { + if (typeof object.proof !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetIdentityResponse.proof: object expected"); + message.proof = $root.org.dash.platform.dapi.v0.Proof.fromObject(object.proof); + } + if (object.metadata != null) { + if (typeof object.metadata !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetIdentityResponse.metadata: object expected"); + message.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.fromObject(object.metadata); + } + return message; + }; + + /** + * Creates a plain object from a GetIdentityResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetIdentityResponse + * @static + * @param {org.dash.platform.dapi.v0.GetIdentityResponse} message GetIdentityResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetIdentityResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if (options.bytes === String) + object.identity = ""; + else { + object.identity = []; + if (options.bytes !== Array) + object.identity = $util.newBuffer(object.identity); + } + object.proof = null; + object.metadata = null; + } + if (message.identity != null && message.hasOwnProperty("identity")) + object.identity = options.bytes === String ? $util.base64.encode(message.identity, 0, message.identity.length) : options.bytes === Array ? Array.prototype.slice.call(message.identity) : message.identity; + if (message.proof != null && message.hasOwnProperty("proof")) + object.proof = $root.org.dash.platform.dapi.v0.Proof.toObject(message.proof, options); + if (message.metadata != null && message.hasOwnProperty("metadata")) + object.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.toObject(message.metadata, options); + return object; + }; + + /** + * Converts this GetIdentityResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetIdentityResponse + * @instance + * @returns {Object.} JSON object + */ + GetIdentityResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetIdentityResponse; + })(); + + v0.GetDataContractRequest = (function() { + + /** + * Properties of a GetDataContractRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetDataContractRequest + * @property {Uint8Array|null} [id] GetDataContractRequest id + * @property {boolean|null} [prove] GetDataContractRequest prove + */ + + /** + * Constructs a new GetDataContractRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetDataContractRequest. + * @implements IGetDataContractRequest + * @constructor + * @param {org.dash.platform.dapi.v0.IGetDataContractRequest=} [properties] Properties to set + */ + function GetDataContractRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetDataContractRequest id. + * @member {Uint8Array} id + * @memberof org.dash.platform.dapi.v0.GetDataContractRequest + * @instance + */ + GetDataContractRequest.prototype.id = $util.newBuffer([]); + + /** + * GetDataContractRequest prove. + * @member {boolean} prove + * @memberof org.dash.platform.dapi.v0.GetDataContractRequest + * @instance + */ + GetDataContractRequest.prototype.prove = false; + + /** + * Creates a new GetDataContractRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetDataContractRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetDataContractRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetDataContractRequest} GetDataContractRequest instance + */ + GetDataContractRequest.create = function create(properties) { + return new GetDataContractRequest(properties); + }; + + /** + * Encodes the specified GetDataContractRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.GetDataContractRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetDataContractRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetDataContractRequest} message GetDataContractRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetDataContractRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.id != null && Object.hasOwnProperty.call(message, "id")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.id); + if (message.prove != null && Object.hasOwnProperty.call(message, "prove")) + writer.uint32(/* id 2, wireType 0 =*/16).bool(message.prove); + return writer; + }; + + /** + * Encodes the specified GetDataContractRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetDataContractRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetDataContractRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetDataContractRequest} message GetDataContractRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetDataContractRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetDataContractRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetDataContractRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetDataContractRequest} GetDataContractRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetDataContractRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetDataContractRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.bytes(); + break; + case 2: + message.prove = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetDataContractRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetDataContractRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetDataContractRequest} GetDataContractRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetDataContractRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetDataContractRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetDataContractRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetDataContractRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.id != null && message.hasOwnProperty("id")) + if (!(message.id && typeof message.id.length === "number" || $util.isString(message.id))) + return "id: buffer expected"; + if (message.prove != null && message.hasOwnProperty("prove")) + if (typeof message.prove !== "boolean") + return "prove: boolean expected"; + return null; + }; + + /** + * Creates a GetDataContractRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetDataContractRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetDataContractRequest} GetDataContractRequest + */ + GetDataContractRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetDataContractRequest) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetDataContractRequest(); + if (object.id != null) + if (typeof object.id === "string") + $util.base64.decode(object.id, message.id = $util.newBuffer($util.base64.length(object.id)), 0); + else if (object.id.length >= 0) + message.id = object.id; + if (object.prove != null) + message.prove = Boolean(object.prove); + return message; + }; + + /** + * Creates a plain object from a GetDataContractRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetDataContractRequest + * @static + * @param {org.dash.platform.dapi.v0.GetDataContractRequest} message GetDataContractRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetDataContractRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if (options.bytes === String) + object.id = ""; + else { + object.id = []; + if (options.bytes !== Array) + object.id = $util.newBuffer(object.id); + } + object.prove = false; + } + if (message.id != null && message.hasOwnProperty("id")) + object.id = options.bytes === String ? $util.base64.encode(message.id, 0, message.id.length) : options.bytes === Array ? Array.prototype.slice.call(message.id) : message.id; + if (message.prove != null && message.hasOwnProperty("prove")) + object.prove = message.prove; + return object; + }; + + /** + * Converts this GetDataContractRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetDataContractRequest + * @instance + * @returns {Object.} JSON object + */ + GetDataContractRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetDataContractRequest; + })(); + + v0.GetDataContractResponse = (function() { + + /** + * Properties of a GetDataContractResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetDataContractResponse + * @property {Uint8Array|null} [dataContract] GetDataContractResponse dataContract + * @property {org.dash.platform.dapi.v0.IProof|null} [proof] GetDataContractResponse proof + * @property {org.dash.platform.dapi.v0.IResponseMetadata|null} [metadata] GetDataContractResponse metadata + */ + + /** + * Constructs a new GetDataContractResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetDataContractResponse. + * @implements IGetDataContractResponse + * @constructor + * @param {org.dash.platform.dapi.v0.IGetDataContractResponse=} [properties] Properties to set + */ + function GetDataContractResponse(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetDataContractResponse dataContract. + * @member {Uint8Array} dataContract + * @memberof org.dash.platform.dapi.v0.GetDataContractResponse + * @instance + */ + GetDataContractResponse.prototype.dataContract = $util.newBuffer([]); + + /** + * GetDataContractResponse proof. + * @member {org.dash.platform.dapi.v0.IProof|null|undefined} proof + * @memberof org.dash.platform.dapi.v0.GetDataContractResponse + * @instance + */ + GetDataContractResponse.prototype.proof = null; + + /** + * GetDataContractResponse metadata. + * @member {org.dash.platform.dapi.v0.IResponseMetadata|null|undefined} metadata + * @memberof org.dash.platform.dapi.v0.GetDataContractResponse + * @instance + */ + GetDataContractResponse.prototype.metadata = null; + + /** + * Creates a new GetDataContractResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetDataContractResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetDataContractResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetDataContractResponse} GetDataContractResponse instance + */ + GetDataContractResponse.create = function create(properties) { + return new GetDataContractResponse(properties); + }; + + /** + * Encodes the specified GetDataContractResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.GetDataContractResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetDataContractResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetDataContractResponse} message GetDataContractResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetDataContractResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.dataContract != null && Object.hasOwnProperty.call(message, "dataContract")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.dataContract); + if (message.proof != null && Object.hasOwnProperty.call(message, "proof")) + $root.org.dash.platform.dapi.v0.Proof.encode(message.proof, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + if (message.metadata != null && Object.hasOwnProperty.call(message, "metadata")) + $root.org.dash.platform.dapi.v0.ResponseMetadata.encode(message.metadata, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified GetDataContractResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetDataContractResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetDataContractResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetDataContractResponse} message GetDataContractResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetDataContractResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetDataContractResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetDataContractResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetDataContractResponse} GetDataContractResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetDataContractResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetDataContractResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.dataContract = reader.bytes(); + break; + case 2: + message.proof = $root.org.dash.platform.dapi.v0.Proof.decode(reader, reader.uint32()); + break; + case 3: + message.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetDataContractResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetDataContractResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetDataContractResponse} GetDataContractResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetDataContractResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetDataContractResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetDataContractResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetDataContractResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.dataContract != null && message.hasOwnProperty("dataContract")) + if (!(message.dataContract && typeof message.dataContract.length === "number" || $util.isString(message.dataContract))) + return "dataContract: buffer expected"; + if (message.proof != null && message.hasOwnProperty("proof")) { + var error = $root.org.dash.platform.dapi.v0.Proof.verify(message.proof); + if (error) + return "proof." + error; + } + if (message.metadata != null && message.hasOwnProperty("metadata")) { + var error = $root.org.dash.platform.dapi.v0.ResponseMetadata.verify(message.metadata); + if (error) + return "metadata." + error; + } + return null; + }; + + /** + * Creates a GetDataContractResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetDataContractResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetDataContractResponse} GetDataContractResponse + */ + GetDataContractResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetDataContractResponse) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetDataContractResponse(); + if (object.dataContract != null) + if (typeof object.dataContract === "string") + $util.base64.decode(object.dataContract, message.dataContract = $util.newBuffer($util.base64.length(object.dataContract)), 0); + else if (object.dataContract.length >= 0) + message.dataContract = object.dataContract; + if (object.proof != null) { + if (typeof object.proof !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetDataContractResponse.proof: object expected"); + message.proof = $root.org.dash.platform.dapi.v0.Proof.fromObject(object.proof); + } + if (object.metadata != null) { + if (typeof object.metadata !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetDataContractResponse.metadata: object expected"); + message.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.fromObject(object.metadata); + } + return message; + }; + + /** + * Creates a plain object from a GetDataContractResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetDataContractResponse + * @static + * @param {org.dash.platform.dapi.v0.GetDataContractResponse} message GetDataContractResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetDataContractResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if (options.bytes === String) + object.dataContract = ""; + else { + object.dataContract = []; + if (options.bytes !== Array) + object.dataContract = $util.newBuffer(object.dataContract); + } + object.proof = null; + object.metadata = null; + } + if (message.dataContract != null && message.hasOwnProperty("dataContract")) + object.dataContract = options.bytes === String ? $util.base64.encode(message.dataContract, 0, message.dataContract.length) : options.bytes === Array ? Array.prototype.slice.call(message.dataContract) : message.dataContract; + if (message.proof != null && message.hasOwnProperty("proof")) + object.proof = $root.org.dash.platform.dapi.v0.Proof.toObject(message.proof, options); + if (message.metadata != null && message.hasOwnProperty("metadata")) + object.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.toObject(message.metadata, options); + return object; + }; + + /** + * Converts this GetDataContractResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetDataContractResponse + * @instance + * @returns {Object.} JSON object + */ + GetDataContractResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetDataContractResponse; + })(); + + v0.GetDocumentsRequest = (function() { + + /** + * Properties of a GetDocumentsRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetDocumentsRequest + * @property {Uint8Array|null} [dataContractId] GetDocumentsRequest dataContractId + * @property {string|null} [documentType] GetDocumentsRequest documentType + * @property {Uint8Array|null} [where] GetDocumentsRequest where + * @property {Uint8Array|null} [orderBy] GetDocumentsRequest orderBy + * @property {number|null} [limit] GetDocumentsRequest limit + * @property {Uint8Array|null} [startAfter] GetDocumentsRequest startAfter + * @property {Uint8Array|null} [startAt] GetDocumentsRequest startAt + * @property {boolean|null} [prove] GetDocumentsRequest prove + */ + + /** + * Constructs a new GetDocumentsRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetDocumentsRequest. + * @implements IGetDocumentsRequest + * @constructor + * @param {org.dash.platform.dapi.v0.IGetDocumentsRequest=} [properties] Properties to set + */ + function GetDocumentsRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetDocumentsRequest dataContractId. + * @member {Uint8Array} dataContractId + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @instance + */ + GetDocumentsRequest.prototype.dataContractId = $util.newBuffer([]); + + /** + * GetDocumentsRequest documentType. + * @member {string} documentType + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @instance + */ + GetDocumentsRequest.prototype.documentType = ""; + + /** + * GetDocumentsRequest where. + * @member {Uint8Array} where + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @instance + */ + GetDocumentsRequest.prototype.where = $util.newBuffer([]); + + /** + * GetDocumentsRequest orderBy. + * @member {Uint8Array} orderBy + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @instance + */ + GetDocumentsRequest.prototype.orderBy = $util.newBuffer([]); + + /** + * GetDocumentsRequest limit. + * @member {number} limit + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @instance + */ + GetDocumentsRequest.prototype.limit = 0; + + /** + * GetDocumentsRequest startAfter. + * @member {Uint8Array} startAfter + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @instance + */ + GetDocumentsRequest.prototype.startAfter = $util.newBuffer([]); + + /** + * GetDocumentsRequest startAt. + * @member {Uint8Array} startAt + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @instance + */ + GetDocumentsRequest.prototype.startAt = $util.newBuffer([]); + + /** + * GetDocumentsRequest prove. + * @member {boolean} prove + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @instance + */ + GetDocumentsRequest.prototype.prove = false; + + // OneOf field names bound to virtual getters and setters + var $oneOfFields; + + /** + * GetDocumentsRequest start. + * @member {"startAfter"|"startAt"|undefined} start + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @instance + */ + Object.defineProperty(GetDocumentsRequest.prototype, "start", { + get: $util.oneOfGetter($oneOfFields = ["startAfter", "startAt"]), + set: $util.oneOfSetter($oneOfFields) + }); + + /** + * Creates a new GetDocumentsRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetDocumentsRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetDocumentsRequest} GetDocumentsRequest instance + */ + GetDocumentsRequest.create = function create(properties) { + return new GetDocumentsRequest(properties); + }; + + /** + * Encodes the specified GetDocumentsRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.GetDocumentsRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetDocumentsRequest} message GetDocumentsRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetDocumentsRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.dataContractId != null && Object.hasOwnProperty.call(message, "dataContractId")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.dataContractId); + if (message.documentType != null && Object.hasOwnProperty.call(message, "documentType")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.documentType); + if (message.where != null && Object.hasOwnProperty.call(message, "where")) + writer.uint32(/* id 3, wireType 2 =*/26).bytes(message.where); + if (message.orderBy != null && Object.hasOwnProperty.call(message, "orderBy")) + writer.uint32(/* id 4, wireType 2 =*/34).bytes(message.orderBy); + if (message.limit != null && Object.hasOwnProperty.call(message, "limit")) + writer.uint32(/* id 5, wireType 0 =*/40).uint32(message.limit); + if (message.startAfter != null && Object.hasOwnProperty.call(message, "startAfter")) + writer.uint32(/* id 6, wireType 2 =*/50).bytes(message.startAfter); + if (message.startAt != null && Object.hasOwnProperty.call(message, "startAt")) + writer.uint32(/* id 7, wireType 2 =*/58).bytes(message.startAt); + if (message.prove != null && Object.hasOwnProperty.call(message, "prove")) + writer.uint32(/* id 8, wireType 0 =*/64).bool(message.prove); + return writer; + }; + + /** + * Encodes the specified GetDocumentsRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetDocumentsRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetDocumentsRequest} message GetDocumentsRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetDocumentsRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetDocumentsRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetDocumentsRequest} GetDocumentsRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetDocumentsRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetDocumentsRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.dataContractId = reader.bytes(); + break; + case 2: + message.documentType = reader.string(); + break; + case 3: + message.where = reader.bytes(); + break; + case 4: + message.orderBy = reader.bytes(); + break; + case 5: + message.limit = reader.uint32(); + break; + case 6: + message.startAfter = reader.bytes(); + break; + case 7: + message.startAt = reader.bytes(); + break; + case 8: + message.prove = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetDocumentsRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetDocumentsRequest} GetDocumentsRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetDocumentsRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetDocumentsRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetDocumentsRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.dataContractId != null && message.hasOwnProperty("dataContractId")) + if (!(message.dataContractId && typeof message.dataContractId.length === "number" || $util.isString(message.dataContractId))) + return "dataContractId: buffer expected"; + if (message.documentType != null && message.hasOwnProperty("documentType")) + if (!$util.isString(message.documentType)) + return "documentType: string expected"; + if (message.where != null && message.hasOwnProperty("where")) + if (!(message.where && typeof message.where.length === "number" || $util.isString(message.where))) + return "where: buffer expected"; + if (message.orderBy != null && message.hasOwnProperty("orderBy")) + if (!(message.orderBy && typeof message.orderBy.length === "number" || $util.isString(message.orderBy))) + return "orderBy: buffer expected"; + if (message.limit != null && message.hasOwnProperty("limit")) + if (!$util.isInteger(message.limit)) + return "limit: integer expected"; + if (message.startAfter != null && message.hasOwnProperty("startAfter")) { + properties.start = 1; + if (!(message.startAfter && typeof message.startAfter.length === "number" || $util.isString(message.startAfter))) + return "startAfter: buffer expected"; + } + if (message.startAt != null && message.hasOwnProperty("startAt")) { + if (properties.start === 1) + return "start: multiple values"; + properties.start = 1; + if (!(message.startAt && typeof message.startAt.length === "number" || $util.isString(message.startAt))) + return "startAt: buffer expected"; + } + if (message.prove != null && message.hasOwnProperty("prove")) + if (typeof message.prove !== "boolean") + return "prove: boolean expected"; + return null; + }; + + /** + * Creates a GetDocumentsRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetDocumentsRequest} GetDocumentsRequest + */ + GetDocumentsRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetDocumentsRequest) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetDocumentsRequest(); + if (object.dataContractId != null) + if (typeof object.dataContractId === "string") + $util.base64.decode(object.dataContractId, message.dataContractId = $util.newBuffer($util.base64.length(object.dataContractId)), 0); + else if (object.dataContractId.length >= 0) + message.dataContractId = object.dataContractId; + if (object.documentType != null) + message.documentType = String(object.documentType); + if (object.where != null) + if (typeof object.where === "string") + $util.base64.decode(object.where, message.where = $util.newBuffer($util.base64.length(object.where)), 0); + else if (object.where.length >= 0) + message.where = object.where; + if (object.orderBy != null) + if (typeof object.orderBy === "string") + $util.base64.decode(object.orderBy, message.orderBy = $util.newBuffer($util.base64.length(object.orderBy)), 0); + else if (object.orderBy.length >= 0) + message.orderBy = object.orderBy; + if (object.limit != null) + message.limit = object.limit >>> 0; + if (object.startAfter != null) + if (typeof object.startAfter === "string") + $util.base64.decode(object.startAfter, message.startAfter = $util.newBuffer($util.base64.length(object.startAfter)), 0); + else if (object.startAfter.length >= 0) + message.startAfter = object.startAfter; + if (object.startAt != null) + if (typeof object.startAt === "string") + $util.base64.decode(object.startAt, message.startAt = $util.newBuffer($util.base64.length(object.startAt)), 0); + else if (object.startAt.length >= 0) + message.startAt = object.startAt; + if (object.prove != null) + message.prove = Boolean(object.prove); + return message; + }; + + /** + * Creates a plain object from a GetDocumentsRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @static + * @param {org.dash.platform.dapi.v0.GetDocumentsRequest} message GetDocumentsRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetDocumentsRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if (options.bytes === String) + object.dataContractId = ""; + else { + object.dataContractId = []; + if (options.bytes !== Array) + object.dataContractId = $util.newBuffer(object.dataContractId); + } + object.documentType = ""; + if (options.bytes === String) + object.where = ""; + else { + object.where = []; + if (options.bytes !== Array) + object.where = $util.newBuffer(object.where); + } + if (options.bytes === String) + object.orderBy = ""; + else { + object.orderBy = []; + if (options.bytes !== Array) + object.orderBy = $util.newBuffer(object.orderBy); + } + object.limit = 0; + object.prove = false; + } + if (message.dataContractId != null && message.hasOwnProperty("dataContractId")) + object.dataContractId = options.bytes === String ? $util.base64.encode(message.dataContractId, 0, message.dataContractId.length) : options.bytes === Array ? Array.prototype.slice.call(message.dataContractId) : message.dataContractId; + if (message.documentType != null && message.hasOwnProperty("documentType")) + object.documentType = message.documentType; + if (message.where != null && message.hasOwnProperty("where")) + object.where = options.bytes === String ? $util.base64.encode(message.where, 0, message.where.length) : options.bytes === Array ? Array.prototype.slice.call(message.where) : message.where; + if (message.orderBy != null && message.hasOwnProperty("orderBy")) + object.orderBy = options.bytes === String ? $util.base64.encode(message.orderBy, 0, message.orderBy.length) : options.bytes === Array ? Array.prototype.slice.call(message.orderBy) : message.orderBy; + if (message.limit != null && message.hasOwnProperty("limit")) + object.limit = message.limit; + if (message.startAfter != null && message.hasOwnProperty("startAfter")) { + object.startAfter = options.bytes === String ? $util.base64.encode(message.startAfter, 0, message.startAfter.length) : options.bytes === Array ? Array.prototype.slice.call(message.startAfter) : message.startAfter; + if (options.oneofs) + object.start = "startAfter"; + } + if (message.startAt != null && message.hasOwnProperty("startAt")) { + object.startAt = options.bytes === String ? $util.base64.encode(message.startAt, 0, message.startAt.length) : options.bytes === Array ? Array.prototype.slice.call(message.startAt) : message.startAt; + if (options.oneofs) + object.start = "startAt"; + } + if (message.prove != null && message.hasOwnProperty("prove")) + object.prove = message.prove; + return object; + }; + + /** + * Converts this GetDocumentsRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetDocumentsRequest + * @instance + * @returns {Object.} JSON object + */ + GetDocumentsRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetDocumentsRequest; + })(); + + v0.GetDocumentsResponse = (function() { + + /** + * Properties of a GetDocumentsResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetDocumentsResponse + * @property {Array.|null} [documents] GetDocumentsResponse documents + * @property {org.dash.platform.dapi.v0.IProof|null} [proof] GetDocumentsResponse proof + * @property {org.dash.platform.dapi.v0.IResponseMetadata|null} [metadata] GetDocumentsResponse metadata + */ + + /** + * Constructs a new GetDocumentsResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetDocumentsResponse. + * @implements IGetDocumentsResponse + * @constructor + * @param {org.dash.platform.dapi.v0.IGetDocumentsResponse=} [properties] Properties to set + */ + function GetDocumentsResponse(properties) { + this.documents = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetDocumentsResponse documents. + * @member {Array.} documents + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse + * @instance + */ + GetDocumentsResponse.prototype.documents = $util.emptyArray; + + /** + * GetDocumentsResponse proof. + * @member {org.dash.platform.dapi.v0.IProof|null|undefined} proof + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse + * @instance + */ + GetDocumentsResponse.prototype.proof = null; + + /** + * GetDocumentsResponse metadata. + * @member {org.dash.platform.dapi.v0.IResponseMetadata|null|undefined} metadata + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse + * @instance + */ + GetDocumentsResponse.prototype.metadata = null; + + /** + * Creates a new GetDocumentsResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetDocumentsResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetDocumentsResponse} GetDocumentsResponse instance + */ + GetDocumentsResponse.create = function create(properties) { + return new GetDocumentsResponse(properties); + }; + + /** + * Encodes the specified GetDocumentsResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.GetDocumentsResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetDocumentsResponse} message GetDocumentsResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetDocumentsResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.documents != null && message.documents.length) + for (var i = 0; i < message.documents.length; ++i) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.documents[i]); + if (message.proof != null && Object.hasOwnProperty.call(message, "proof")) + $root.org.dash.platform.dapi.v0.Proof.encode(message.proof, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + if (message.metadata != null && Object.hasOwnProperty.call(message, "metadata")) + $root.org.dash.platform.dapi.v0.ResponseMetadata.encode(message.metadata, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified GetDocumentsResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetDocumentsResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetDocumentsResponse} message GetDocumentsResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetDocumentsResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetDocumentsResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetDocumentsResponse} GetDocumentsResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetDocumentsResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetDocumentsResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (!(message.documents && message.documents.length)) + message.documents = []; + message.documents.push(reader.bytes()); + break; + case 2: + message.proof = $root.org.dash.platform.dapi.v0.Proof.decode(reader, reader.uint32()); + break; + case 3: + message.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetDocumentsResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetDocumentsResponse} GetDocumentsResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetDocumentsResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetDocumentsResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetDocumentsResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.documents != null && message.hasOwnProperty("documents")) { + if (!Array.isArray(message.documents)) + return "documents: array expected"; + for (var i = 0; i < message.documents.length; ++i) + if (!(message.documents[i] && typeof message.documents[i].length === "number" || $util.isString(message.documents[i]))) + return "documents: buffer[] expected"; + } + if (message.proof != null && message.hasOwnProperty("proof")) { + var error = $root.org.dash.platform.dapi.v0.Proof.verify(message.proof); + if (error) + return "proof." + error; + } + if (message.metadata != null && message.hasOwnProperty("metadata")) { + var error = $root.org.dash.platform.dapi.v0.ResponseMetadata.verify(message.metadata); + if (error) + return "metadata." + error; + } + return null; + }; + + /** + * Creates a GetDocumentsResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetDocumentsResponse} GetDocumentsResponse + */ + GetDocumentsResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetDocumentsResponse) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetDocumentsResponse(); + if (object.documents) { + if (!Array.isArray(object.documents)) + throw TypeError(".org.dash.platform.dapi.v0.GetDocumentsResponse.documents: array expected"); + message.documents = []; + for (var i = 0; i < object.documents.length; ++i) + if (typeof object.documents[i] === "string") + $util.base64.decode(object.documents[i], message.documents[i] = $util.newBuffer($util.base64.length(object.documents[i])), 0); + else if (object.documents[i].length >= 0) + message.documents[i] = object.documents[i]; + } + if (object.proof != null) { + if (typeof object.proof !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetDocumentsResponse.proof: object expected"); + message.proof = $root.org.dash.platform.dapi.v0.Proof.fromObject(object.proof); + } + if (object.metadata != null) { + if (typeof object.metadata !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetDocumentsResponse.metadata: object expected"); + message.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.fromObject(object.metadata); + } + return message; + }; + + /** + * Creates a plain object from a GetDocumentsResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse + * @static + * @param {org.dash.platform.dapi.v0.GetDocumentsResponse} message GetDocumentsResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetDocumentsResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.documents = []; + if (options.defaults) { + object.proof = null; + object.metadata = null; + } + if (message.documents && message.documents.length) { + object.documents = []; + for (var j = 0; j < message.documents.length; ++j) + object.documents[j] = options.bytes === String ? $util.base64.encode(message.documents[j], 0, message.documents[j].length) : options.bytes === Array ? Array.prototype.slice.call(message.documents[j]) : message.documents[j]; + } + if (message.proof != null && message.hasOwnProperty("proof")) + object.proof = $root.org.dash.platform.dapi.v0.Proof.toObject(message.proof, options); + if (message.metadata != null && message.hasOwnProperty("metadata")) + object.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.toObject(message.metadata, options); + return object; + }; + + /** + * Converts this GetDocumentsResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse + * @instance + * @returns {Object.} JSON object + */ + GetDocumentsResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetDocumentsResponse; + })(); + + v0.GetIdentitiesByPublicKeyHashesRequest = (function() { + + /** + * Properties of a GetIdentitiesByPublicKeyHashesRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetIdentitiesByPublicKeyHashesRequest + * @property {Array.|null} [publicKeyHashes] GetIdentitiesByPublicKeyHashesRequest publicKeyHashes + * @property {boolean|null} [prove] GetIdentitiesByPublicKeyHashesRequest prove + */ + + /** + * Constructs a new GetIdentitiesByPublicKeyHashesRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetIdentitiesByPublicKeyHashesRequest. + * @implements IGetIdentitiesByPublicKeyHashesRequest + * @constructor + * @param {org.dash.platform.dapi.v0.IGetIdentitiesByPublicKeyHashesRequest=} [properties] Properties to set + */ + function GetIdentitiesByPublicKeyHashesRequest(properties) { + this.publicKeyHashes = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetIdentitiesByPublicKeyHashesRequest publicKeyHashes. + * @member {Array.} publicKeyHashes + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest + * @instance + */ + GetIdentitiesByPublicKeyHashesRequest.prototype.publicKeyHashes = $util.emptyArray; + + /** + * GetIdentitiesByPublicKeyHashesRequest prove. + * @member {boolean} prove + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest + * @instance + */ + GetIdentitiesByPublicKeyHashesRequest.prototype.prove = false; + + /** + * Creates a new GetIdentitiesByPublicKeyHashesRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetIdentitiesByPublicKeyHashesRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} GetIdentitiesByPublicKeyHashesRequest instance + */ + GetIdentitiesByPublicKeyHashesRequest.create = function create(properties) { + return new GetIdentitiesByPublicKeyHashesRequest(properties); + }; + + /** + * Encodes the specified GetIdentitiesByPublicKeyHashesRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetIdentitiesByPublicKeyHashesRequest} message GetIdentitiesByPublicKeyHashesRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetIdentitiesByPublicKeyHashesRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.publicKeyHashes != null && message.publicKeyHashes.length) + for (var i = 0; i < message.publicKeyHashes.length; ++i) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.publicKeyHashes[i]); + if (message.prove != null && Object.hasOwnProperty.call(message, "prove")) + writer.uint32(/* id 2, wireType 0 =*/16).bool(message.prove); + return writer; + }; + + /** + * Encodes the specified GetIdentitiesByPublicKeyHashesRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetIdentitiesByPublicKeyHashesRequest} message GetIdentitiesByPublicKeyHashesRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetIdentitiesByPublicKeyHashesRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetIdentitiesByPublicKeyHashesRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} GetIdentitiesByPublicKeyHashesRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetIdentitiesByPublicKeyHashesRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (!(message.publicKeyHashes && message.publicKeyHashes.length)) + message.publicKeyHashes = []; + message.publicKeyHashes.push(reader.bytes()); + break; + case 2: + message.prove = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetIdentitiesByPublicKeyHashesRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} GetIdentitiesByPublicKeyHashesRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetIdentitiesByPublicKeyHashesRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetIdentitiesByPublicKeyHashesRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetIdentitiesByPublicKeyHashesRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.publicKeyHashes != null && message.hasOwnProperty("publicKeyHashes")) { + if (!Array.isArray(message.publicKeyHashes)) + return "publicKeyHashes: array expected"; + for (var i = 0; i < message.publicKeyHashes.length; ++i) + if (!(message.publicKeyHashes[i] && typeof message.publicKeyHashes[i].length === "number" || $util.isString(message.publicKeyHashes[i]))) + return "publicKeyHashes: buffer[] expected"; + } + if (message.prove != null && message.hasOwnProperty("prove")) + if (typeof message.prove !== "boolean") + return "prove: boolean expected"; + return null; + }; + + /** + * Creates a GetIdentitiesByPublicKeyHashesRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} GetIdentitiesByPublicKeyHashesRequest + */ + GetIdentitiesByPublicKeyHashesRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest(); + if (object.publicKeyHashes) { + if (!Array.isArray(object.publicKeyHashes)) + throw TypeError(".org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.publicKeyHashes: array expected"); + message.publicKeyHashes = []; + for (var i = 0; i < object.publicKeyHashes.length; ++i) + if (typeof object.publicKeyHashes[i] === "string") + $util.base64.decode(object.publicKeyHashes[i], message.publicKeyHashes[i] = $util.newBuffer($util.base64.length(object.publicKeyHashes[i])), 0); + else if (object.publicKeyHashes[i].length >= 0) + message.publicKeyHashes[i] = object.publicKeyHashes[i]; + } + if (object.prove != null) + message.prove = Boolean(object.prove); + return message; + }; + + /** + * Creates a plain object from a GetIdentitiesByPublicKeyHashesRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest + * @static + * @param {org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} message GetIdentitiesByPublicKeyHashesRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetIdentitiesByPublicKeyHashesRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.publicKeyHashes = []; + if (options.defaults) + object.prove = false; + if (message.publicKeyHashes && message.publicKeyHashes.length) { + object.publicKeyHashes = []; + for (var j = 0; j < message.publicKeyHashes.length; ++j) + object.publicKeyHashes[j] = options.bytes === String ? $util.base64.encode(message.publicKeyHashes[j], 0, message.publicKeyHashes[j].length) : options.bytes === Array ? Array.prototype.slice.call(message.publicKeyHashes[j]) : message.publicKeyHashes[j]; + } + if (message.prove != null && message.hasOwnProperty("prove")) + object.prove = message.prove; + return object; + }; + + /** + * Converts this GetIdentitiesByPublicKeyHashesRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest + * @instance + * @returns {Object.} JSON object + */ + GetIdentitiesByPublicKeyHashesRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetIdentitiesByPublicKeyHashesRequest; + })(); + + v0.GetIdentitiesByPublicKeyHashesResponse = (function() { + + /** + * Properties of a GetIdentitiesByPublicKeyHashesResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetIdentitiesByPublicKeyHashesResponse + * @property {Array.|null} [identities] GetIdentitiesByPublicKeyHashesResponse identities + * @property {org.dash.platform.dapi.v0.IProof|null} [proof] GetIdentitiesByPublicKeyHashesResponse proof + * @property {org.dash.platform.dapi.v0.IResponseMetadata|null} [metadata] GetIdentitiesByPublicKeyHashesResponse metadata + */ + + /** + * Constructs a new GetIdentitiesByPublicKeyHashesResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetIdentitiesByPublicKeyHashesResponse. + * @implements IGetIdentitiesByPublicKeyHashesResponse + * @constructor + * @param {org.dash.platform.dapi.v0.IGetIdentitiesByPublicKeyHashesResponse=} [properties] Properties to set + */ + function GetIdentitiesByPublicKeyHashesResponse(properties) { + this.identities = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetIdentitiesByPublicKeyHashesResponse identities. + * @member {Array.} identities + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse + * @instance + */ + GetIdentitiesByPublicKeyHashesResponse.prototype.identities = $util.emptyArray; + + /** + * GetIdentitiesByPublicKeyHashesResponse proof. + * @member {org.dash.platform.dapi.v0.IProof|null|undefined} proof + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse + * @instance + */ + GetIdentitiesByPublicKeyHashesResponse.prototype.proof = null; + + /** + * GetIdentitiesByPublicKeyHashesResponse metadata. + * @member {org.dash.platform.dapi.v0.IResponseMetadata|null|undefined} metadata + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse + * @instance + */ + GetIdentitiesByPublicKeyHashesResponse.prototype.metadata = null; + + /** + * Creates a new GetIdentitiesByPublicKeyHashesResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetIdentitiesByPublicKeyHashesResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} GetIdentitiesByPublicKeyHashesResponse instance + */ + GetIdentitiesByPublicKeyHashesResponse.create = function create(properties) { + return new GetIdentitiesByPublicKeyHashesResponse(properties); + }; + + /** + * Encodes the specified GetIdentitiesByPublicKeyHashesResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetIdentitiesByPublicKeyHashesResponse} message GetIdentitiesByPublicKeyHashesResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetIdentitiesByPublicKeyHashesResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.identities != null && message.identities.length) + for (var i = 0; i < message.identities.length; ++i) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.identities[i]); + if (message.proof != null && Object.hasOwnProperty.call(message, "proof")) + $root.org.dash.platform.dapi.v0.Proof.encode(message.proof, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + if (message.metadata != null && Object.hasOwnProperty.call(message, "metadata")) + $root.org.dash.platform.dapi.v0.ResponseMetadata.encode(message.metadata, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified GetIdentitiesByPublicKeyHashesResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetIdentitiesByPublicKeyHashesResponse} message GetIdentitiesByPublicKeyHashesResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetIdentitiesByPublicKeyHashesResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetIdentitiesByPublicKeyHashesResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} GetIdentitiesByPublicKeyHashesResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetIdentitiesByPublicKeyHashesResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (!(message.identities && message.identities.length)) + message.identities = []; + message.identities.push(reader.bytes()); + break; + case 2: + message.proof = $root.org.dash.platform.dapi.v0.Proof.decode(reader, reader.uint32()); + break; + case 3: + message.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetIdentitiesByPublicKeyHashesResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} GetIdentitiesByPublicKeyHashesResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetIdentitiesByPublicKeyHashesResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetIdentitiesByPublicKeyHashesResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetIdentitiesByPublicKeyHashesResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.identities != null && message.hasOwnProperty("identities")) { + if (!Array.isArray(message.identities)) + return "identities: array expected"; + for (var i = 0; i < message.identities.length; ++i) + if (!(message.identities[i] && typeof message.identities[i].length === "number" || $util.isString(message.identities[i]))) + return "identities: buffer[] expected"; + } + if (message.proof != null && message.hasOwnProperty("proof")) { + var error = $root.org.dash.platform.dapi.v0.Proof.verify(message.proof); + if (error) + return "proof." + error; + } + if (message.metadata != null && message.hasOwnProperty("metadata")) { + var error = $root.org.dash.platform.dapi.v0.ResponseMetadata.verify(message.metadata); + if (error) + return "metadata." + error; + } + return null; + }; + + /** + * Creates a GetIdentitiesByPublicKeyHashesResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} GetIdentitiesByPublicKeyHashesResponse + */ + GetIdentitiesByPublicKeyHashesResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse(); + if (object.identities) { + if (!Array.isArray(object.identities)) + throw TypeError(".org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.identities: array expected"); + message.identities = []; + for (var i = 0; i < object.identities.length; ++i) + if (typeof object.identities[i] === "string") + $util.base64.decode(object.identities[i], message.identities[i] = $util.newBuffer($util.base64.length(object.identities[i])), 0); + else if (object.identities[i].length >= 0) + message.identities[i] = object.identities[i]; + } + if (object.proof != null) { + if (typeof object.proof !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.proof: object expected"); + message.proof = $root.org.dash.platform.dapi.v0.Proof.fromObject(object.proof); + } + if (object.metadata != null) { + if (typeof object.metadata !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.metadata: object expected"); + message.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.fromObject(object.metadata); + } + return message; + }; + + /** + * Creates a plain object from a GetIdentitiesByPublicKeyHashesResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse + * @static + * @param {org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} message GetIdentitiesByPublicKeyHashesResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetIdentitiesByPublicKeyHashesResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.identities = []; + if (options.defaults) { + object.proof = null; + object.metadata = null; + } + if (message.identities && message.identities.length) { + object.identities = []; + for (var j = 0; j < message.identities.length; ++j) + object.identities[j] = options.bytes === String ? $util.base64.encode(message.identities[j], 0, message.identities[j].length) : options.bytes === Array ? Array.prototype.slice.call(message.identities[j]) : message.identities[j]; + } + if (message.proof != null && message.hasOwnProperty("proof")) + object.proof = $root.org.dash.platform.dapi.v0.Proof.toObject(message.proof, options); + if (message.metadata != null && message.hasOwnProperty("metadata")) + object.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.toObject(message.metadata, options); + return object; + }; + + /** + * Converts this GetIdentitiesByPublicKeyHashesResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse + * @instance + * @returns {Object.} JSON object + */ + GetIdentitiesByPublicKeyHashesResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetIdentitiesByPublicKeyHashesResponse; + })(); + + v0.WaitForStateTransitionResultRequest = (function() { + + /** + * Properties of a WaitForStateTransitionResultRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface IWaitForStateTransitionResultRequest + * @property {Uint8Array|null} [stateTransitionHash] WaitForStateTransitionResultRequest stateTransitionHash + * @property {boolean|null} [prove] WaitForStateTransitionResultRequest prove + */ + + /** + * Constructs a new WaitForStateTransitionResultRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a WaitForStateTransitionResultRequest. + * @implements IWaitForStateTransitionResultRequest + * @constructor + * @param {org.dash.platform.dapi.v0.IWaitForStateTransitionResultRequest=} [properties] Properties to set + */ + function WaitForStateTransitionResultRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * WaitForStateTransitionResultRequest stateTransitionHash. + * @member {Uint8Array} stateTransitionHash + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest + * @instance + */ + WaitForStateTransitionResultRequest.prototype.stateTransitionHash = $util.newBuffer([]); + + /** + * WaitForStateTransitionResultRequest prove. + * @member {boolean} prove + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest + * @instance + */ + WaitForStateTransitionResultRequest.prototype.prove = false; + + /** + * Creates a new WaitForStateTransitionResultRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest + * @static + * @param {org.dash.platform.dapi.v0.IWaitForStateTransitionResultRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} WaitForStateTransitionResultRequest instance + */ + WaitForStateTransitionResultRequest.create = function create(properties) { + return new WaitForStateTransitionResultRequest(properties); + }; + + /** + * Encodes the specified WaitForStateTransitionResultRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest + * @static + * @param {org.dash.platform.dapi.v0.IWaitForStateTransitionResultRequest} message WaitForStateTransitionResultRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + WaitForStateTransitionResultRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.stateTransitionHash != null && Object.hasOwnProperty.call(message, "stateTransitionHash")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.stateTransitionHash); + if (message.prove != null && Object.hasOwnProperty.call(message, "prove")) + writer.uint32(/* id 2, wireType 0 =*/16).bool(message.prove); + return writer; + }; + + /** + * Encodes the specified WaitForStateTransitionResultRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest + * @static + * @param {org.dash.platform.dapi.v0.IWaitForStateTransitionResultRequest} message WaitForStateTransitionResultRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + WaitForStateTransitionResultRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a WaitForStateTransitionResultRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} WaitForStateTransitionResultRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + WaitForStateTransitionResultRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.stateTransitionHash = reader.bytes(); + break; + case 2: + message.prove = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a WaitForStateTransitionResultRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} WaitForStateTransitionResultRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + WaitForStateTransitionResultRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a WaitForStateTransitionResultRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + WaitForStateTransitionResultRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.stateTransitionHash != null && message.hasOwnProperty("stateTransitionHash")) + if (!(message.stateTransitionHash && typeof message.stateTransitionHash.length === "number" || $util.isString(message.stateTransitionHash))) + return "stateTransitionHash: buffer expected"; + if (message.prove != null && message.hasOwnProperty("prove")) + if (typeof message.prove !== "boolean") + return "prove: boolean expected"; + return null; + }; + + /** + * Creates a WaitForStateTransitionResultRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} WaitForStateTransitionResultRequest + */ + WaitForStateTransitionResultRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest) + return object; + var message = new $root.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest(); + if (object.stateTransitionHash != null) + if (typeof object.stateTransitionHash === "string") + $util.base64.decode(object.stateTransitionHash, message.stateTransitionHash = $util.newBuffer($util.base64.length(object.stateTransitionHash)), 0); + else if (object.stateTransitionHash.length >= 0) + message.stateTransitionHash = object.stateTransitionHash; + if (object.prove != null) + message.prove = Boolean(object.prove); + return message; + }; + + /** + * Creates a plain object from a WaitForStateTransitionResultRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest + * @static + * @param {org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} message WaitForStateTransitionResultRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + WaitForStateTransitionResultRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if (options.bytes === String) + object.stateTransitionHash = ""; + else { + object.stateTransitionHash = []; + if (options.bytes !== Array) + object.stateTransitionHash = $util.newBuffer(object.stateTransitionHash); + } + object.prove = false; + } + if (message.stateTransitionHash != null && message.hasOwnProperty("stateTransitionHash")) + object.stateTransitionHash = options.bytes === String ? $util.base64.encode(message.stateTransitionHash, 0, message.stateTransitionHash.length) : options.bytes === Array ? Array.prototype.slice.call(message.stateTransitionHash) : message.stateTransitionHash; + if (message.prove != null && message.hasOwnProperty("prove")) + object.prove = message.prove; + return object; + }; + + /** + * Converts this WaitForStateTransitionResultRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest + * @instance + * @returns {Object.} JSON object + */ + WaitForStateTransitionResultRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return WaitForStateTransitionResultRequest; + })(); + + v0.WaitForStateTransitionResultResponse = (function() { + + /** + * Properties of a WaitForStateTransitionResultResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface IWaitForStateTransitionResultResponse + * @property {org.dash.platform.dapi.v0.IStateTransitionBroadcastError|null} [error] WaitForStateTransitionResultResponse error + * @property {org.dash.platform.dapi.v0.IProof|null} [proof] WaitForStateTransitionResultResponse proof + * @property {org.dash.platform.dapi.v0.IResponseMetadata|null} [metadata] WaitForStateTransitionResultResponse metadata + */ + + /** + * Constructs a new WaitForStateTransitionResultResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a WaitForStateTransitionResultResponse. + * @implements IWaitForStateTransitionResultResponse + * @constructor + * @param {org.dash.platform.dapi.v0.IWaitForStateTransitionResultResponse=} [properties] Properties to set + */ + function WaitForStateTransitionResultResponse(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * WaitForStateTransitionResultResponse error. + * @member {org.dash.platform.dapi.v0.IStateTransitionBroadcastError|null|undefined} error + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse + * @instance + */ + WaitForStateTransitionResultResponse.prototype.error = null; + + /** + * WaitForStateTransitionResultResponse proof. + * @member {org.dash.platform.dapi.v0.IProof|null|undefined} proof + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse + * @instance + */ + WaitForStateTransitionResultResponse.prototype.proof = null; + + /** + * WaitForStateTransitionResultResponse metadata. + * @member {org.dash.platform.dapi.v0.IResponseMetadata|null|undefined} metadata + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse + * @instance + */ + WaitForStateTransitionResultResponse.prototype.metadata = null; + + // OneOf field names bound to virtual getters and setters + var $oneOfFields; + + /** + * WaitForStateTransitionResultResponse responses. + * @member {"error"|"proof"|undefined} responses + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse + * @instance + */ + Object.defineProperty(WaitForStateTransitionResultResponse.prototype, "responses", { + get: $util.oneOfGetter($oneOfFields = ["error", "proof"]), + set: $util.oneOfSetter($oneOfFields) + }); + + /** + * Creates a new WaitForStateTransitionResultResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse + * @static + * @param {org.dash.platform.dapi.v0.IWaitForStateTransitionResultResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} WaitForStateTransitionResultResponse instance + */ + WaitForStateTransitionResultResponse.create = function create(properties) { + return new WaitForStateTransitionResultResponse(properties); + }; + + /** + * Encodes the specified WaitForStateTransitionResultResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse + * @static + * @param {org.dash.platform.dapi.v0.IWaitForStateTransitionResultResponse} message WaitForStateTransitionResultResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + WaitForStateTransitionResultResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.error != null && Object.hasOwnProperty.call(message, "error")) + $root.org.dash.platform.dapi.v0.StateTransitionBroadcastError.encode(message.error, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.proof != null && Object.hasOwnProperty.call(message, "proof")) + $root.org.dash.platform.dapi.v0.Proof.encode(message.proof, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + if (message.metadata != null && Object.hasOwnProperty.call(message, "metadata")) + $root.org.dash.platform.dapi.v0.ResponseMetadata.encode(message.metadata, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified WaitForStateTransitionResultResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse + * @static + * @param {org.dash.platform.dapi.v0.IWaitForStateTransitionResultResponse} message WaitForStateTransitionResultResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + WaitForStateTransitionResultResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a WaitForStateTransitionResultResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} WaitForStateTransitionResultResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + WaitForStateTransitionResultResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.error = $root.org.dash.platform.dapi.v0.StateTransitionBroadcastError.decode(reader, reader.uint32()); + break; + case 2: + message.proof = $root.org.dash.platform.dapi.v0.Proof.decode(reader, reader.uint32()); + break; + case 3: + message.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a WaitForStateTransitionResultResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} WaitForStateTransitionResultResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + WaitForStateTransitionResultResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a WaitForStateTransitionResultResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + WaitForStateTransitionResultResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.error != null && message.hasOwnProperty("error")) { + properties.responses = 1; + { + var error = $root.org.dash.platform.dapi.v0.StateTransitionBroadcastError.verify(message.error); + if (error) + return "error." + error; + } + } + if (message.proof != null && message.hasOwnProperty("proof")) { + if (properties.responses === 1) + return "responses: multiple values"; + properties.responses = 1; + { + var error = $root.org.dash.platform.dapi.v0.Proof.verify(message.proof); + if (error) + return "proof." + error; + } + } + if (message.metadata != null && message.hasOwnProperty("metadata")) { + var error = $root.org.dash.platform.dapi.v0.ResponseMetadata.verify(message.metadata); + if (error) + return "metadata." + error; + } + return null; + }; + + /** + * Creates a WaitForStateTransitionResultResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} WaitForStateTransitionResultResponse + */ + WaitForStateTransitionResultResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse) + return object; + var message = new $root.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse(); + if (object.error != null) { + if (typeof object.error !== "object") + throw TypeError(".org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.error: object expected"); + message.error = $root.org.dash.platform.dapi.v0.StateTransitionBroadcastError.fromObject(object.error); + } + if (object.proof != null) { + if (typeof object.proof !== "object") + throw TypeError(".org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.proof: object expected"); + message.proof = $root.org.dash.platform.dapi.v0.Proof.fromObject(object.proof); + } + if (object.metadata != null) { + if (typeof object.metadata !== "object") + throw TypeError(".org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.metadata: object expected"); + message.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.fromObject(object.metadata); + } + return message; + }; + + /** + * Creates a plain object from a WaitForStateTransitionResultResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse + * @static + * @param {org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} message WaitForStateTransitionResultResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + WaitForStateTransitionResultResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.metadata = null; + if (message.error != null && message.hasOwnProperty("error")) { + object.error = $root.org.dash.platform.dapi.v0.StateTransitionBroadcastError.toObject(message.error, options); + if (options.oneofs) + object.responses = "error"; + } + if (message.proof != null && message.hasOwnProperty("proof")) { + object.proof = $root.org.dash.platform.dapi.v0.Proof.toObject(message.proof, options); + if (options.oneofs) + object.responses = "proof"; + } + if (message.metadata != null && message.hasOwnProperty("metadata")) + object.metadata = $root.org.dash.platform.dapi.v0.ResponseMetadata.toObject(message.metadata, options); + return object; + }; + + /** + * Converts this WaitForStateTransitionResultResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse + * @instance + * @returns {Object.} JSON object + */ + WaitForStateTransitionResultResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return WaitForStateTransitionResultResponse; + })(); + + v0.ConsensusParamsBlock = (function() { + + /** + * Properties of a ConsensusParamsBlock. + * @memberof org.dash.platform.dapi.v0 + * @interface IConsensusParamsBlock + * @property {string|null} [maxBytes] ConsensusParamsBlock maxBytes + * @property {string|null} [maxGas] ConsensusParamsBlock maxGas + * @property {string|null} [timeIotaMs] ConsensusParamsBlock timeIotaMs + */ + + /** + * Constructs a new ConsensusParamsBlock. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a ConsensusParamsBlock. + * @implements IConsensusParamsBlock + * @constructor + * @param {org.dash.platform.dapi.v0.IConsensusParamsBlock=} [properties] Properties to set + */ + function ConsensusParamsBlock(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * ConsensusParamsBlock maxBytes. + * @member {string} maxBytes + * @memberof org.dash.platform.dapi.v0.ConsensusParamsBlock + * @instance + */ + ConsensusParamsBlock.prototype.maxBytes = ""; + + /** + * ConsensusParamsBlock maxGas. + * @member {string} maxGas + * @memberof org.dash.platform.dapi.v0.ConsensusParamsBlock + * @instance + */ + ConsensusParamsBlock.prototype.maxGas = ""; + + /** + * ConsensusParamsBlock timeIotaMs. + * @member {string} timeIotaMs + * @memberof org.dash.platform.dapi.v0.ConsensusParamsBlock + * @instance + */ + ConsensusParamsBlock.prototype.timeIotaMs = ""; + + /** + * Creates a new ConsensusParamsBlock instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.ConsensusParamsBlock + * @static + * @param {org.dash.platform.dapi.v0.IConsensusParamsBlock=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.ConsensusParamsBlock} ConsensusParamsBlock instance + */ + ConsensusParamsBlock.create = function create(properties) { + return new ConsensusParamsBlock(properties); + }; + + /** + * Encodes the specified ConsensusParamsBlock message. Does not implicitly {@link org.dash.platform.dapi.v0.ConsensusParamsBlock.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.ConsensusParamsBlock + * @static + * @param {org.dash.platform.dapi.v0.IConsensusParamsBlock} message ConsensusParamsBlock message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ConsensusParamsBlock.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.maxBytes != null && Object.hasOwnProperty.call(message, "maxBytes")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.maxBytes); + if (message.maxGas != null && Object.hasOwnProperty.call(message, "maxGas")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.maxGas); + if (message.timeIotaMs != null && Object.hasOwnProperty.call(message, "timeIotaMs")) + writer.uint32(/* id 3, wireType 2 =*/26).string(message.timeIotaMs); + return writer; + }; + + /** + * Encodes the specified ConsensusParamsBlock message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.ConsensusParamsBlock.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.ConsensusParamsBlock + * @static + * @param {org.dash.platform.dapi.v0.IConsensusParamsBlock} message ConsensusParamsBlock message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ConsensusParamsBlock.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a ConsensusParamsBlock message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.ConsensusParamsBlock + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.ConsensusParamsBlock} ConsensusParamsBlock + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ConsensusParamsBlock.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.ConsensusParamsBlock(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.maxBytes = reader.string(); + break; + case 2: + message.maxGas = reader.string(); + break; + case 3: + message.timeIotaMs = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a ConsensusParamsBlock message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.ConsensusParamsBlock + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.ConsensusParamsBlock} ConsensusParamsBlock + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ConsensusParamsBlock.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a ConsensusParamsBlock message. + * @function verify + * @memberof org.dash.platform.dapi.v0.ConsensusParamsBlock + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + ConsensusParamsBlock.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.maxBytes != null && message.hasOwnProperty("maxBytes")) + if (!$util.isString(message.maxBytes)) + return "maxBytes: string expected"; + if (message.maxGas != null && message.hasOwnProperty("maxGas")) + if (!$util.isString(message.maxGas)) + return "maxGas: string expected"; + if (message.timeIotaMs != null && message.hasOwnProperty("timeIotaMs")) + if (!$util.isString(message.timeIotaMs)) + return "timeIotaMs: string expected"; + return null; + }; + + /** + * Creates a ConsensusParamsBlock message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.ConsensusParamsBlock + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.ConsensusParamsBlock} ConsensusParamsBlock + */ + ConsensusParamsBlock.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.ConsensusParamsBlock) + return object; + var message = new $root.org.dash.platform.dapi.v0.ConsensusParamsBlock(); + if (object.maxBytes != null) + message.maxBytes = String(object.maxBytes); + if (object.maxGas != null) + message.maxGas = String(object.maxGas); + if (object.timeIotaMs != null) + message.timeIotaMs = String(object.timeIotaMs); + return message; + }; + + /** + * Creates a plain object from a ConsensusParamsBlock message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.ConsensusParamsBlock + * @static + * @param {org.dash.platform.dapi.v0.ConsensusParamsBlock} message ConsensusParamsBlock + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ConsensusParamsBlock.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.maxBytes = ""; + object.maxGas = ""; + object.timeIotaMs = ""; + } + if (message.maxBytes != null && message.hasOwnProperty("maxBytes")) + object.maxBytes = message.maxBytes; + if (message.maxGas != null && message.hasOwnProperty("maxGas")) + object.maxGas = message.maxGas; + if (message.timeIotaMs != null && message.hasOwnProperty("timeIotaMs")) + object.timeIotaMs = message.timeIotaMs; + return object; + }; + + /** + * Converts this ConsensusParamsBlock to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.ConsensusParamsBlock + * @instance + * @returns {Object.} JSON object + */ + ConsensusParamsBlock.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return ConsensusParamsBlock; + })(); + + v0.ConsensusParamsEvidence = (function() { + + /** + * Properties of a ConsensusParamsEvidence. + * @memberof org.dash.platform.dapi.v0 + * @interface IConsensusParamsEvidence + * @property {string|null} [maxAgeNumBlocks] ConsensusParamsEvidence maxAgeNumBlocks + * @property {string|null} [maxAgeDuration] ConsensusParamsEvidence maxAgeDuration + * @property {string|null} [maxBytes] ConsensusParamsEvidence maxBytes + */ + + /** + * Constructs a new ConsensusParamsEvidence. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a ConsensusParamsEvidence. + * @implements IConsensusParamsEvidence + * @constructor + * @param {org.dash.platform.dapi.v0.IConsensusParamsEvidence=} [properties] Properties to set + */ + function ConsensusParamsEvidence(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * ConsensusParamsEvidence maxAgeNumBlocks. + * @member {string} maxAgeNumBlocks + * @memberof org.dash.platform.dapi.v0.ConsensusParamsEvidence + * @instance + */ + ConsensusParamsEvidence.prototype.maxAgeNumBlocks = ""; + + /** + * ConsensusParamsEvidence maxAgeDuration. + * @member {string} maxAgeDuration + * @memberof org.dash.platform.dapi.v0.ConsensusParamsEvidence + * @instance + */ + ConsensusParamsEvidence.prototype.maxAgeDuration = ""; + + /** + * ConsensusParamsEvidence maxBytes. + * @member {string} maxBytes + * @memberof org.dash.platform.dapi.v0.ConsensusParamsEvidence + * @instance + */ + ConsensusParamsEvidence.prototype.maxBytes = ""; + + /** + * Creates a new ConsensusParamsEvidence instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.ConsensusParamsEvidence + * @static + * @param {org.dash.platform.dapi.v0.IConsensusParamsEvidence=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.ConsensusParamsEvidence} ConsensusParamsEvidence instance + */ + ConsensusParamsEvidence.create = function create(properties) { + return new ConsensusParamsEvidence(properties); + }; + + /** + * Encodes the specified ConsensusParamsEvidence message. Does not implicitly {@link org.dash.platform.dapi.v0.ConsensusParamsEvidence.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.ConsensusParamsEvidence + * @static + * @param {org.dash.platform.dapi.v0.IConsensusParamsEvidence} message ConsensusParamsEvidence message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ConsensusParamsEvidence.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.maxAgeNumBlocks != null && Object.hasOwnProperty.call(message, "maxAgeNumBlocks")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.maxAgeNumBlocks); + if (message.maxAgeDuration != null && Object.hasOwnProperty.call(message, "maxAgeDuration")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.maxAgeDuration); + if (message.maxBytes != null && Object.hasOwnProperty.call(message, "maxBytes")) + writer.uint32(/* id 3, wireType 2 =*/26).string(message.maxBytes); + return writer; + }; + + /** + * Encodes the specified ConsensusParamsEvidence message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.ConsensusParamsEvidence.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.ConsensusParamsEvidence + * @static + * @param {org.dash.platform.dapi.v0.IConsensusParamsEvidence} message ConsensusParamsEvidence message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ConsensusParamsEvidence.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a ConsensusParamsEvidence message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.ConsensusParamsEvidence + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.ConsensusParamsEvidence} ConsensusParamsEvidence + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ConsensusParamsEvidence.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.ConsensusParamsEvidence(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.maxAgeNumBlocks = reader.string(); + break; + case 2: + message.maxAgeDuration = reader.string(); + break; + case 3: + message.maxBytes = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a ConsensusParamsEvidence message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.ConsensusParamsEvidence + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.ConsensusParamsEvidence} ConsensusParamsEvidence + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ConsensusParamsEvidence.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a ConsensusParamsEvidence message. + * @function verify + * @memberof org.dash.platform.dapi.v0.ConsensusParamsEvidence + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + ConsensusParamsEvidence.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.maxAgeNumBlocks != null && message.hasOwnProperty("maxAgeNumBlocks")) + if (!$util.isString(message.maxAgeNumBlocks)) + return "maxAgeNumBlocks: string expected"; + if (message.maxAgeDuration != null && message.hasOwnProperty("maxAgeDuration")) + if (!$util.isString(message.maxAgeDuration)) + return "maxAgeDuration: string expected"; + if (message.maxBytes != null && message.hasOwnProperty("maxBytes")) + if (!$util.isString(message.maxBytes)) + return "maxBytes: string expected"; + return null; + }; + + /** + * Creates a ConsensusParamsEvidence message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.ConsensusParamsEvidence + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.ConsensusParamsEvidence} ConsensusParamsEvidence + */ + ConsensusParamsEvidence.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.ConsensusParamsEvidence) + return object; + var message = new $root.org.dash.platform.dapi.v0.ConsensusParamsEvidence(); + if (object.maxAgeNumBlocks != null) + message.maxAgeNumBlocks = String(object.maxAgeNumBlocks); + if (object.maxAgeDuration != null) + message.maxAgeDuration = String(object.maxAgeDuration); + if (object.maxBytes != null) + message.maxBytes = String(object.maxBytes); + return message; + }; + + /** + * Creates a plain object from a ConsensusParamsEvidence message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.ConsensusParamsEvidence + * @static + * @param {org.dash.platform.dapi.v0.ConsensusParamsEvidence} message ConsensusParamsEvidence + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ConsensusParamsEvidence.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.maxAgeNumBlocks = ""; + object.maxAgeDuration = ""; + object.maxBytes = ""; + } + if (message.maxAgeNumBlocks != null && message.hasOwnProperty("maxAgeNumBlocks")) + object.maxAgeNumBlocks = message.maxAgeNumBlocks; + if (message.maxAgeDuration != null && message.hasOwnProperty("maxAgeDuration")) + object.maxAgeDuration = message.maxAgeDuration; + if (message.maxBytes != null && message.hasOwnProperty("maxBytes")) + object.maxBytes = message.maxBytes; + return object; + }; + + /** + * Converts this ConsensusParamsEvidence to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.ConsensusParamsEvidence + * @instance + * @returns {Object.} JSON object + */ + ConsensusParamsEvidence.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return ConsensusParamsEvidence; + })(); + + v0.GetConsensusParamsRequest = (function() { + + /** + * Properties of a GetConsensusParamsRequest. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetConsensusParamsRequest + * @property {number|Long|null} [height] GetConsensusParamsRequest height + * @property {boolean|null} [prove] GetConsensusParamsRequest prove + */ + + /** + * Constructs a new GetConsensusParamsRequest. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetConsensusParamsRequest. + * @implements IGetConsensusParamsRequest + * @constructor + * @param {org.dash.platform.dapi.v0.IGetConsensusParamsRequest=} [properties] Properties to set + */ + function GetConsensusParamsRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetConsensusParamsRequest height. + * @member {number|Long} height + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsRequest + * @instance + */ + GetConsensusParamsRequest.prototype.height = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * GetConsensusParamsRequest prove. + * @member {boolean} prove + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsRequest + * @instance + */ + GetConsensusParamsRequest.prototype.prove = false; + + /** + * Creates a new GetConsensusParamsRequest instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetConsensusParamsRequest=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetConsensusParamsRequest} GetConsensusParamsRequest instance + */ + GetConsensusParamsRequest.create = function create(properties) { + return new GetConsensusParamsRequest(properties); + }; + + /** + * Encodes the specified GetConsensusParamsRequest message. Does not implicitly {@link org.dash.platform.dapi.v0.GetConsensusParamsRequest.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetConsensusParamsRequest} message GetConsensusParamsRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetConsensusParamsRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.height != null && Object.hasOwnProperty.call(message, "height")) + writer.uint32(/* id 1, wireType 0 =*/8).int64(message.height); + if (message.prove != null && Object.hasOwnProperty.call(message, "prove")) + writer.uint32(/* id 2, wireType 0 =*/16).bool(message.prove); + return writer; + }; + + /** + * Encodes the specified GetConsensusParamsRequest message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetConsensusParamsRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsRequest + * @static + * @param {org.dash.platform.dapi.v0.IGetConsensusParamsRequest} message GetConsensusParamsRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetConsensusParamsRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetConsensusParamsRequest message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetConsensusParamsRequest} GetConsensusParamsRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetConsensusParamsRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetConsensusParamsRequest(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.height = reader.int64(); + break; + case 2: + message.prove = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetConsensusParamsRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetConsensusParamsRequest} GetConsensusParamsRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetConsensusParamsRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetConsensusParamsRequest message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetConsensusParamsRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.height != null && message.hasOwnProperty("height")) + if (!$util.isInteger(message.height) && !(message.height && $util.isInteger(message.height.low) && $util.isInteger(message.height.high))) + return "height: integer|Long expected"; + if (message.prove != null && message.hasOwnProperty("prove")) + if (typeof message.prove !== "boolean") + return "prove: boolean expected"; + return null; + }; + + /** + * Creates a GetConsensusParamsRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsRequest + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetConsensusParamsRequest} GetConsensusParamsRequest + */ + GetConsensusParamsRequest.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetConsensusParamsRequest) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetConsensusParamsRequest(); + if (object.height != null) + if ($util.Long) + (message.height = $util.Long.fromValue(object.height)).unsigned = false; + else if (typeof object.height === "string") + message.height = parseInt(object.height, 10); + else if (typeof object.height === "number") + message.height = object.height; + else if (typeof object.height === "object") + message.height = new $util.LongBits(object.height.low >>> 0, object.height.high >>> 0).toNumber(); + if (object.prove != null) + message.prove = Boolean(object.prove); + return message; + }; + + /** + * Creates a plain object from a GetConsensusParamsRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsRequest + * @static + * @param {org.dash.platform.dapi.v0.GetConsensusParamsRequest} message GetConsensusParamsRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetConsensusParamsRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.height = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.height = options.longs === String ? "0" : 0; + object.prove = false; + } + if (message.height != null && message.hasOwnProperty("height")) + if (typeof message.height === "number") + object.height = options.longs === String ? String(message.height) : message.height; + else + object.height = options.longs === String ? $util.Long.prototype.toString.call(message.height) : options.longs === Number ? new $util.LongBits(message.height.low >>> 0, message.height.high >>> 0).toNumber() : message.height; + if (message.prove != null && message.hasOwnProperty("prove")) + object.prove = message.prove; + return object; + }; + + /** + * Converts this GetConsensusParamsRequest to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsRequest + * @instance + * @returns {Object.} JSON object + */ + GetConsensusParamsRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetConsensusParamsRequest; + })(); + + v0.GetConsensusParamsResponse = (function() { + + /** + * Properties of a GetConsensusParamsResponse. + * @memberof org.dash.platform.dapi.v0 + * @interface IGetConsensusParamsResponse + * @property {org.dash.platform.dapi.v0.IConsensusParamsBlock|null} [block] GetConsensusParamsResponse block + * @property {org.dash.platform.dapi.v0.IConsensusParamsEvidence|null} [evidence] GetConsensusParamsResponse evidence + */ + + /** + * Constructs a new GetConsensusParamsResponse. + * @memberof org.dash.platform.dapi.v0 + * @classdesc Represents a GetConsensusParamsResponse. + * @implements IGetConsensusParamsResponse + * @constructor + * @param {org.dash.platform.dapi.v0.IGetConsensusParamsResponse=} [properties] Properties to set + */ + function GetConsensusParamsResponse(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetConsensusParamsResponse block. + * @member {org.dash.platform.dapi.v0.IConsensusParamsBlock|null|undefined} block + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsResponse + * @instance + */ + GetConsensusParamsResponse.prototype.block = null; + + /** + * GetConsensusParamsResponse evidence. + * @member {org.dash.platform.dapi.v0.IConsensusParamsEvidence|null|undefined} evidence + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsResponse + * @instance + */ + GetConsensusParamsResponse.prototype.evidence = null; + + /** + * Creates a new GetConsensusParamsResponse instance using the specified properties. + * @function create + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetConsensusParamsResponse=} [properties] Properties to set + * @returns {org.dash.platform.dapi.v0.GetConsensusParamsResponse} GetConsensusParamsResponse instance + */ + GetConsensusParamsResponse.create = function create(properties) { + return new GetConsensusParamsResponse(properties); + }; + + /** + * Encodes the specified GetConsensusParamsResponse message. Does not implicitly {@link org.dash.platform.dapi.v0.GetConsensusParamsResponse.verify|verify} messages. + * @function encode + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetConsensusParamsResponse} message GetConsensusParamsResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetConsensusParamsResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.block != null && Object.hasOwnProperty.call(message, "block")) + $root.org.dash.platform.dapi.v0.ConsensusParamsBlock.encode(message.block, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.evidence != null && Object.hasOwnProperty.call(message, "evidence")) + $root.org.dash.platform.dapi.v0.ConsensusParamsEvidence.encode(message.evidence, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified GetConsensusParamsResponse message, length delimited. Does not implicitly {@link org.dash.platform.dapi.v0.GetConsensusParamsResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsResponse + * @static + * @param {org.dash.platform.dapi.v0.IGetConsensusParamsResponse} message GetConsensusParamsResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetConsensusParamsResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetConsensusParamsResponse message from the specified reader or buffer. + * @function decode + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {org.dash.platform.dapi.v0.GetConsensusParamsResponse} GetConsensusParamsResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetConsensusParamsResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.org.dash.platform.dapi.v0.GetConsensusParamsResponse(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.block = $root.org.dash.platform.dapi.v0.ConsensusParamsBlock.decode(reader, reader.uint32()); + break; + case 2: + message.evidence = $root.org.dash.platform.dapi.v0.ConsensusParamsEvidence.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetConsensusParamsResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {org.dash.platform.dapi.v0.GetConsensusParamsResponse} GetConsensusParamsResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetConsensusParamsResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetConsensusParamsResponse message. + * @function verify + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetConsensusParamsResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.block != null && message.hasOwnProperty("block")) { + var error = $root.org.dash.platform.dapi.v0.ConsensusParamsBlock.verify(message.block); + if (error) + return "block." + error; + } + if (message.evidence != null && message.hasOwnProperty("evidence")) { + var error = $root.org.dash.platform.dapi.v0.ConsensusParamsEvidence.verify(message.evidence); + if (error) + return "evidence." + error; + } + return null; + }; + + /** + * Creates a GetConsensusParamsResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsResponse + * @static + * @param {Object.} object Plain object + * @returns {org.dash.platform.dapi.v0.GetConsensusParamsResponse} GetConsensusParamsResponse + */ + GetConsensusParamsResponse.fromObject = function fromObject(object) { + if (object instanceof $root.org.dash.platform.dapi.v0.GetConsensusParamsResponse) + return object; + var message = new $root.org.dash.platform.dapi.v0.GetConsensusParamsResponse(); + if (object.block != null) { + if (typeof object.block !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetConsensusParamsResponse.block: object expected"); + message.block = $root.org.dash.platform.dapi.v0.ConsensusParamsBlock.fromObject(object.block); + } + if (object.evidence != null) { + if (typeof object.evidence !== "object") + throw TypeError(".org.dash.platform.dapi.v0.GetConsensusParamsResponse.evidence: object expected"); + message.evidence = $root.org.dash.platform.dapi.v0.ConsensusParamsEvidence.fromObject(object.evidence); + } + return message; + }; + + /** + * Creates a plain object from a GetConsensusParamsResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsResponse + * @static + * @param {org.dash.platform.dapi.v0.GetConsensusParamsResponse} message GetConsensusParamsResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetConsensusParamsResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.block = null; + object.evidence = null; + } + if (message.block != null && message.hasOwnProperty("block")) + object.block = $root.org.dash.platform.dapi.v0.ConsensusParamsBlock.toObject(message.block, options); + if (message.evidence != null && message.hasOwnProperty("evidence")) + object.evidence = $root.org.dash.platform.dapi.v0.ConsensusParamsEvidence.toObject(message.evidence, options); + return object; + }; + + /** + * Converts this GetConsensusParamsResponse to JSON. + * @function toJSON + * @memberof org.dash.platform.dapi.v0.GetConsensusParamsResponse + * @instance + * @returns {Object.} JSON object + */ + GetConsensusParamsResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return GetConsensusParamsResponse; + })(); + + return v0; + })(); + + return dapi; + })(); + + return platform; + })(); + + return dash; + })(); + + return org; +})(); + +module.exports = $root; diff --git a/packages/dapi-grpc/clients/platform/v0/nodejs/platform_protoc.js b/packages/dapi-grpc/clients/platform/v0/nodejs/platform_protoc.js new file mode 100644 index 00000000000..5f738ea47ca --- /dev/null +++ b/packages/dapi-grpc/clients/platform/v0/nodejs/platform_protoc.js @@ -0,0 +1,4716 @@ +// source: platform.proto +/** + * @fileoverview + * @enhanceable + * @suppress {missingRequire} reports error on implicit type usages. + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +// @ts-nocheck + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = (function() { + if (this) { return this; } + if (typeof window !== 'undefined') { return window; } + if (typeof global !== 'undefined') { return global; } + if (typeof self !== 'undefined') { return self; } + return Function('return this')(); +}.call(null)); + +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.ConsensusParamsBlock', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetDataContractRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetDataContractResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetDocumentsRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetDocumentsRequest.StartCase', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetDocumentsResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetIdentityRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetIdentityResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.Proof', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.ResponseMetadata', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.ResponsesCase', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.Proof = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.Proof, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.Proof.displayName = 'proto.org.dash.platform.dapi.v0.Proof'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.ResponseMetadata, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.ResponseMetadata.displayName = 'proto.org.dash.platform.dapi.v0.ResponseMetadata'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.displayName = 'proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.displayName = 'proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.displayName = 'proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetIdentityRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetIdentityRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetIdentityRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetIdentityResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetIdentityResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetIdentityResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetDataContractRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetDataContractRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetDataContractRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetDataContractResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetDataContractResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetDataContractResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetDocumentsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetDocumentsRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetDocumentsRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.org.dash.platform.dapi.v0.GetDocumentsResponse.repeatedFields_, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetDocumentsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetDocumentsResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetDocumentsResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.repeatedFields_, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.repeatedFields_, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.displayName = 'proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.displayName = 'proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.ConsensusParamsBlock, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.displayName = 'proto.org.dash.platform.dapi.v0.ConsensusParamsBlock'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.displayName = 'proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse'; +} + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.Proof.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.Proof} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.Proof.toObject = function(includeInstance, msg) { + var f, obj = { + merkleProof: msg.getMerkleProof_asB64(), + signatureLlmqHash: msg.getSignatureLlmqHash_asB64(), + signature: msg.getSignature_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.Proof.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.Proof; + return proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.Proof} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setMerkleProof(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSignatureLlmqHash(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSignature(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.Proof} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getMerkleProof_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getSignatureLlmqHash_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 3, + f + ); + } +}; + + +/** + * optional bytes merkle_proof = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getMerkleProof = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes merkle_proof = 1; + * This is a type-conversion wrapper around `getMerkleProof()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getMerkleProof_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getMerkleProof())); +}; + + +/** + * optional bytes merkle_proof = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getMerkleProof()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getMerkleProof_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getMerkleProof())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.Proof} returns this + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.setMerkleProof = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bytes signature_llmq_hash = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getSignatureLlmqHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes signature_llmq_hash = 2; + * This is a type-conversion wrapper around `getSignatureLlmqHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getSignatureLlmqHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSignatureLlmqHash())); +}; + + +/** + * optional bytes signature_llmq_hash = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSignatureLlmqHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getSignatureLlmqHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSignatureLlmqHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.Proof} returns this + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.setSignatureLlmqHash = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional bytes signature = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getSignature = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes signature = 3; + * This is a type-conversion wrapper around `getSignature()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSignature())); +}; + + +/** + * optional bytes signature = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSignature()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.Proof} returns this + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.setSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.ResponseMetadata} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject = function(includeInstance, msg) { + var f, obj = { + height: jspb.Message.getFieldWithDefault(msg, 1, 0), + coreChainLockedHeight: jspb.Message.getFieldWithDefault(msg, 2, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.ResponseMetadata; + return proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.ResponseMetadata} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readInt64()); + msg.setHeight(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint32()); + msg.setCoreChainLockedHeight(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.ResponseMetadata} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getHeight(); + if (f !== 0) { + writer.writeInt64( + 1, + f + ); + } + f = message.getCoreChainLockedHeight(); + if (f !== 0) { + writer.writeUint32( + 2, + f + ); + } +}; + + +/** + * optional int64 height = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.prototype.getHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.ResponseMetadata} returns this + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.prototype.setHeight = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional uint32 core_chain_locked_height = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.prototype.getCoreChainLockedHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.ResponseMetadata} returns this + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.prototype.setCoreChainLockedHeight = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.toObject = function(includeInstance, msg) { + var f, obj = { + code: jspb.Message.getFieldWithDefault(msg, 1, 0), + message: jspb.Message.getFieldWithDefault(msg, 2, ""), + data: msg.getData_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError; + return proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setCode(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setMessage(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setData(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getCode(); + if (f !== 0) { + writer.writeUint32( + 1, + f + ); + } + f = message.getMessage(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getData_asU8(); + if (f.length > 0) { + writer.writeBytes( + 3, + f + ); + } +}; + + +/** + * optional uint32 code = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.getCode = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} returns this + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.setCode = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional string message = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.getMessage = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} returns this + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.setMessage = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional bytes data = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.getData = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes data = 3; + * This is a type-conversion wrapper around `getData()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.getData_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getData())); +}; + + +/** + * optional bytes data = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getData()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.getData_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getData())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} returns this + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.setData = function(value) { + return jspb.Message.setProto3BytesField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.toObject = function(includeInstance, msg) { + var f, obj = { + stateTransition: msg.getStateTransition_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest; + return proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setStateTransition(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getStateTransition_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } +}; + + +/** + * optional bytes state_transition = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.prototype.getStateTransition = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes state_transition = 1; + * This is a type-conversion wrapper around `getStateTransition()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.prototype.getStateTransition_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getStateTransition())); +}; + + +/** + * optional bytes state_transition = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getStateTransition()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.prototype.getStateTransition_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getStateTransition())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.prototype.setStateTransition = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.toObject = function(includeInstance, msg) { + var f, obj = { + + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse; + return proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetIdentityRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.toObject = function(includeInstance, msg) { + var f, obj = { + id: msg.getId_asB64(), + prove: jspb.Message.getBooleanFieldWithDefault(msg, 2, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetIdentityRequest; + return proto.org.dash.platform.dapi.v0.GetIdentityRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setId(value); + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setProve(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetIdentityRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getId_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getProve(); + if (f) { + writer.writeBool( + 2, + f + ); + } +}; + + +/** + * optional bytes id = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.getId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes id = 1; + * This is a type-conversion wrapper around `getId()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.getId_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getId())); +}; + + +/** + * optional bytes id = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getId()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.getId_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getId())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.setId = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bool prove = 2; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.getProve = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.setProve = function(value) { + return jspb.Message.setProto3BooleanField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetIdentityResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.toObject = function(includeInstance, msg) { + var f, obj = { + identity: msg.getIdentity_asB64(), + proof: (f = msg.getProof()) && proto.org.dash.platform.dapi.v0.Proof.toObject(includeInstance, f), + metadata: (f = msg.getMetadata()) && proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetIdentityResponse; + return proto.org.dash.platform.dapi.v0.GetIdentityResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setIdentity(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.Proof; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader); + msg.setProof(value); + break; + case 3: + var value = new proto.org.dash.platform.dapi.v0.ResponseMetadata; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader); + msg.setMetadata(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetIdentityResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getIdentity_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getProof(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter + ); + } + f = message.getMetadata(); + if (f != null) { + writer.writeMessage( + 3, + f, + proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter + ); + } +}; + + +/** + * optional bytes identity = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.getIdentity = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes identity = 1; + * This is a type-conversion wrapper around `getIdentity()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.getIdentity_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getIdentity())); +}; + + +/** + * optional bytes identity = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getIdentity()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.getIdentity_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getIdentity())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.setIdentity = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional Proof proof = 2; + * @return {?proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.getProof = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.Proof} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.Proof, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.Proof|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.setProof = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.clearProof = function() { + return this.setProof(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.hasProof = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional ResponseMetadata metadata = 3; + * @return {?proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.getMetadata = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ResponseMetadata} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ResponseMetadata, 3)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ResponseMetadata|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.setMetadata = function(value) { + return jspb.Message.setWrapperField(this, 3, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.clearMetadata = function() { + return this.setMetadata(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.hasMetadata = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetDataContractRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.toObject = function(includeInstance, msg) { + var f, obj = { + id: msg.getId_asB64(), + prove: jspb.Message.getBooleanFieldWithDefault(msg, 2, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetDataContractRequest; + return proto.org.dash.platform.dapi.v0.GetDataContractRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setId(value); + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setProve(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetDataContractRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getId_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getProve(); + if (f) { + writer.writeBool( + 2, + f + ); + } +}; + + +/** + * optional bytes id = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.getId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes id = 1; + * This is a type-conversion wrapper around `getId()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.getId_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getId())); +}; + + +/** + * optional bytes id = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getId()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.getId_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getId())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.setId = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bool prove = 2; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.getProve = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.setProve = function(value) { + return jspb.Message.setProto3BooleanField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetDataContractResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.toObject = function(includeInstance, msg) { + var f, obj = { + dataContract: msg.getDataContract_asB64(), + proof: (f = msg.getProof()) && proto.org.dash.platform.dapi.v0.Proof.toObject(includeInstance, f), + metadata: (f = msg.getMetadata()) && proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetDataContractResponse; + return proto.org.dash.platform.dapi.v0.GetDataContractResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setDataContract(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.Proof; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader); + msg.setProof(value); + break; + case 3: + var value = new proto.org.dash.platform.dapi.v0.ResponseMetadata; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader); + msg.setMetadata(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetDataContractResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getDataContract_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getProof(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter + ); + } + f = message.getMetadata(); + if (f != null) { + writer.writeMessage( + 3, + f, + proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter + ); + } +}; + + +/** + * optional bytes data_contract = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.getDataContract = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes data_contract = 1; + * This is a type-conversion wrapper around `getDataContract()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.getDataContract_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getDataContract())); +}; + + +/** + * optional bytes data_contract = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getDataContract()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.getDataContract_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getDataContract())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.setDataContract = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional Proof proof = 2; + * @return {?proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.getProof = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.Proof} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.Proof, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.Proof|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.setProof = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.clearProof = function() { + return this.setProof(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.hasProof = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional ResponseMetadata metadata = 3; + * @return {?proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.getMetadata = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ResponseMetadata} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ResponseMetadata, 3)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ResponseMetadata|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.setMetadata = function(value) { + return jspb.Message.setWrapperField(this, 3, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.clearMetadata = function() { + return this.setMetadata(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.hasMetadata = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_ = [[6,7]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.StartCase = { + START_NOT_SET: 0, + START_AFTER: 6, + START_AT: 7 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.GetDocumentsRequest.StartCase} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.GetDocumentsRequest.StartCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetDocumentsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + dataContractId: msg.getDataContractId_asB64(), + documentType: jspb.Message.getFieldWithDefault(msg, 2, ""), + where: msg.getWhere_asB64(), + orderBy: msg.getOrderBy_asB64(), + limit: jspb.Message.getFieldWithDefault(msg, 5, 0), + startAfter: msg.getStartAfter_asB64(), + startAt: msg.getStartAt_asB64(), + prove: jspb.Message.getBooleanFieldWithDefault(msg, 8, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetDocumentsRequest; + return proto.org.dash.platform.dapi.v0.GetDocumentsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setDataContractId(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setDocumentType(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setWhere(value); + break; + case 4: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setOrderBy(value); + break; + case 5: + var value = /** @type {number} */ (reader.readUint32()); + msg.setLimit(value); + break; + case 6: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setStartAfter(value); + break; + case 7: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setStartAt(value); + break; + case 8: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setProve(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetDocumentsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getDataContractId_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getDocumentType(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getWhere_asU8(); + if (f.length > 0) { + writer.writeBytes( + 3, + f + ); + } + f = message.getOrderBy_asU8(); + if (f.length > 0) { + writer.writeBytes( + 4, + f + ); + } + f = message.getLimit(); + if (f !== 0) { + writer.writeUint32( + 5, + f + ); + } + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 6)); + if (f != null) { + writer.writeBytes( + 6, + f + ); + } + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 7)); + if (f != null) { + writer.writeBytes( + 7, + f + ); + } + f = message.getProve(); + if (f) { + writer.writeBool( + 8, + f + ); + } +}; + + +/** + * optional bytes data_contract_id = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getDataContractId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes data_contract_id = 1; + * This is a type-conversion wrapper around `getDataContractId()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getDataContractId_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getDataContractId())); +}; + + +/** + * optional bytes data_contract_id = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getDataContractId()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getDataContractId_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getDataContractId())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setDataContractId = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional string document_type = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getDocumentType = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setDocumentType = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional bytes where = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getWhere = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes where = 3; + * This is a type-conversion wrapper around `getWhere()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getWhere_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getWhere())); +}; + + +/** + * optional bytes where = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getWhere()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getWhere_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getWhere())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setWhere = function(value) { + return jspb.Message.setProto3BytesField(this, 3, value); +}; + + +/** + * optional bytes order_by = 4; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getOrderBy = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * optional bytes order_by = 4; + * This is a type-conversion wrapper around `getOrderBy()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getOrderBy_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getOrderBy())); +}; + + +/** + * optional bytes order_by = 4; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getOrderBy()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getOrderBy_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getOrderBy())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setOrderBy = function(value) { + return jspb.Message.setProto3BytesField(this, 4, value); +}; + + +/** + * optional uint32 limit = 5; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getLimit = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setLimit = function(value) { + return jspb.Message.setProto3IntField(this, 5, value); +}; + + +/** + * optional bytes start_after = 6; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartAfter = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, "")); +}; + + +/** + * optional bytes start_after = 6; + * This is a type-conversion wrapper around `getStartAfter()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartAfter_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getStartAfter())); +}; + + +/** + * optional bytes start_after = 6; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getStartAfter()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartAfter_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getStartAfter())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setStartAfter = function(value) { + return jspb.Message.setOneofField(this, 6, proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.clearStartAfter = function() { + return jspb.Message.setOneofField(this, 6, proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.hasStartAfter = function() { + return jspb.Message.getField(this, 6) != null; +}; + + +/** + * optional bytes start_at = 7; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartAt = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, "")); +}; + + +/** + * optional bytes start_at = 7; + * This is a type-conversion wrapper around `getStartAt()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartAt_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getStartAt())); +}; + + +/** + * optional bytes start_at = 7; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getStartAt()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartAt_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getStartAt())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setStartAt = function(value) { + return jspb.Message.setOneofField(this, 7, proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.clearStartAt = function() { + return jspb.Message.setOneofField(this, 7, proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.hasStartAt = function() { + return jspb.Message.getField(this, 7) != null; +}; + + +/** + * optional bool prove = 8; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getProve = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 8, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setProve = function(value) { + return jspb.Message.setProto3BooleanField(this, 8, value); +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetDocumentsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + documentsList: msg.getDocumentsList_asB64(), + proof: (f = msg.getProof()) && proto.org.dash.platform.dapi.v0.Proof.toObject(includeInstance, f), + metadata: (f = msg.getMetadata()) && proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetDocumentsResponse; + return proto.org.dash.platform.dapi.v0.GetDocumentsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.addDocuments(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.Proof; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader); + msg.setProof(value); + break; + case 3: + var value = new proto.org.dash.platform.dapi.v0.ResponseMetadata; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader); + msg.setMetadata(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetDocumentsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getDocumentsList_asU8(); + if (f.length > 0) { + writer.writeRepeatedBytes( + 1, + f + ); + } + f = message.getProof(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter + ); + } + f = message.getMetadata(); + if (f != null) { + writer.writeMessage( + 3, + f, + proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter + ); + } +}; + + +/** + * repeated bytes documents = 1; + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.getDocumentsList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * repeated bytes documents = 1; + * This is a type-conversion wrapper around `getDocumentsList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.getDocumentsList_asB64 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsB64( + this.getDocumentsList())); +}; + + +/** + * repeated bytes documents = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getDocumentsList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.getDocumentsList_asU8 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsU8( + this.getDocumentsList())); +}; + + +/** + * @param {!(Array|Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.setDocumentsList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @param {number=} opt_index + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.addDocuments = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.clearDocumentsList = function() { + return this.setDocumentsList([]); +}; + + +/** + * optional Proof proof = 2; + * @return {?proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.getProof = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.Proof} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.Proof, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.Proof|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.setProof = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.clearProof = function() { + return this.setProof(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.hasProof = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional ResponseMetadata metadata = 3; + * @return {?proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.getMetadata = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ResponseMetadata} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ResponseMetadata, 3)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ResponseMetadata|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.setMetadata = function(value) { + return jspb.Message.setWrapperField(this, 3, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.clearMetadata = function() { + return this.setMetadata(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.hasMetadata = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.toObject = function(includeInstance, msg) { + var f, obj = { + publicKeyHashesList: msg.getPublicKeyHashesList_asB64(), + prove: jspb.Message.getBooleanFieldWithDefault(msg, 2, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest; + return proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.addPublicKeyHashes(value); + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setProve(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getPublicKeyHashesList_asU8(); + if (f.length > 0) { + writer.writeRepeatedBytes( + 1, + f + ); + } + f = message.getProve(); + if (f) { + writer.writeBool( + 2, + f + ); + } +}; + + +/** + * repeated bytes public_key_hashes = 1; + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.getPublicKeyHashesList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * repeated bytes public_key_hashes = 1; + * This is a type-conversion wrapper around `getPublicKeyHashesList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.getPublicKeyHashesList_asB64 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsB64( + this.getPublicKeyHashesList())); +}; + + +/** + * repeated bytes public_key_hashes = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getPublicKeyHashesList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.getPublicKeyHashesList_asU8 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsU8( + this.getPublicKeyHashesList())); +}; + + +/** + * @param {!(Array|Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.setPublicKeyHashesList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @param {number=} opt_index + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.addPublicKeyHashes = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.clearPublicKeyHashesList = function() { + return this.setPublicKeyHashesList([]); +}; + + +/** + * optional bool prove = 2; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.getProve = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.setProve = function(value) { + return jspb.Message.setProto3BooleanField(this, 2, value); +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.toObject = function(includeInstance, msg) { + var f, obj = { + identitiesList: msg.getIdentitiesList_asB64(), + proof: (f = msg.getProof()) && proto.org.dash.platform.dapi.v0.Proof.toObject(includeInstance, f), + metadata: (f = msg.getMetadata()) && proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse; + return proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.addIdentities(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.Proof; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader); + msg.setProof(value); + break; + case 3: + var value = new proto.org.dash.platform.dapi.v0.ResponseMetadata; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader); + msg.setMetadata(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getIdentitiesList_asU8(); + if (f.length > 0) { + writer.writeRepeatedBytes( + 1, + f + ); + } + f = message.getProof(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter + ); + } + f = message.getMetadata(); + if (f != null) { + writer.writeMessage( + 3, + f, + proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter + ); + } +}; + + +/** + * repeated bytes identities = 1; + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.getIdentitiesList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * repeated bytes identities = 1; + * This is a type-conversion wrapper around `getIdentitiesList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.getIdentitiesList_asB64 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsB64( + this.getIdentitiesList())); +}; + + +/** + * repeated bytes identities = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getIdentitiesList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.getIdentitiesList_asU8 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsU8( + this.getIdentitiesList())); +}; + + +/** + * @param {!(Array|Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.setIdentitiesList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @param {number=} opt_index + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.addIdentities = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.clearIdentitiesList = function() { + return this.setIdentitiesList([]); +}; + + +/** + * optional Proof proof = 2; + * @return {?proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.getProof = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.Proof} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.Proof, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.Proof|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.setProof = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.clearProof = function() { + return this.setProof(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.hasProof = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional ResponseMetadata metadata = 3; + * @return {?proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.getMetadata = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ResponseMetadata} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ResponseMetadata, 3)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ResponseMetadata|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.setMetadata = function(value) { + return jspb.Message.setWrapperField(this, 3, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.clearMetadata = function() { + return this.setMetadata(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.hasMetadata = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.toObject = function(includeInstance, msg) { + var f, obj = { + stateTransitionHash: msg.getStateTransitionHash_asB64(), + prove: jspb.Message.getBooleanFieldWithDefault(msg, 2, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest; + return proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setStateTransitionHash(value); + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setProve(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getStateTransitionHash_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getProve(); + if (f) { + writer.writeBool( + 2, + f + ); + } +}; + + +/** + * optional bytes state_transition_hash = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.getStateTransitionHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes state_transition_hash = 1; + * This is a type-conversion wrapper around `getStateTransitionHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.getStateTransitionHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getStateTransitionHash())); +}; + + +/** + * optional bytes state_transition_hash = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getStateTransitionHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.getStateTransitionHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getStateTransitionHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} returns this + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.setStateTransitionHash = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bool prove = 2; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.getProve = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} returns this + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.setProve = function(value) { + return jspb.Message.setProto3BooleanField(this, 2, value); +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.oneofGroups_ = [[1,2]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.ResponsesCase = { + RESPONSES_NOT_SET: 0, + ERROR: 1, + PROOF: 2 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.ResponsesCase} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.getResponsesCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.ResponsesCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.toObject = function(includeInstance, msg) { + var f, obj = { + error: (f = msg.getError()) && proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.toObject(includeInstance, f), + proof: (f = msg.getProof()) && proto.org.dash.platform.dapi.v0.Proof.toObject(includeInstance, f), + metadata: (f = msg.getMetadata()) && proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse; + return proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.deserializeBinaryFromReader); + msg.setError(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.Proof; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader); + msg.setProof(value); + break; + case 3: + var value = new proto.org.dash.platform.dapi.v0.ResponseMetadata; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader); + msg.setMetadata(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getError(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.serializeBinaryToWriter + ); + } + f = message.getProof(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter + ); + } + f = message.getMetadata(); + if (f != null) { + writer.writeMessage( + 3, + f, + proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter + ); + } +}; + + +/** + * optional StateTransitionBroadcastError error = 1; + * @return {?proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.getError = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError, 1)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.setError = function(value) { + return jspb.Message.setOneofWrapperField(this, 1, proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} returns this + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.clearError = function() { + return this.setError(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.hasError = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional Proof proof = 2; + * @return {?proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.getProof = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.Proof} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.Proof, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.Proof|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.setProof = function(value) { + return jspb.Message.setOneofWrapperField(this, 2, proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} returns this + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.clearProof = function() { + return this.setProof(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.hasProof = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional ResponseMetadata metadata = 3; + * @return {?proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.getMetadata = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ResponseMetadata} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ResponseMetadata, 3)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ResponseMetadata|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.setMetadata = function(value) { + return jspb.Message.setWrapperField(this, 3, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} returns this + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.clearMetadata = function() { + return this.setMetadata(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.hasMetadata = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.toObject = function(includeInstance, msg) { + var f, obj = { + maxBytes: jspb.Message.getFieldWithDefault(msg, 1, ""), + maxGas: jspb.Message.getFieldWithDefault(msg, 2, ""), + timeIotaMs: jspb.Message.getFieldWithDefault(msg, 3, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.ConsensusParamsBlock; + return proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setMaxBytes(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setMaxGas(value); + break; + case 3: + var value = /** @type {string} */ (reader.readString()); + msg.setTimeIotaMs(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getMaxBytes(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getMaxGas(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getTimeIotaMs(); + if (f.length > 0) { + writer.writeString( + 3, + f + ); + } +}; + + +/** + * optional string max_bytes = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.getMaxBytes = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} returns this + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.setMaxBytes = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional string max_gas = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.getMaxGas = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} returns this + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.setMaxGas = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional string time_iota_ms = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.getTimeIotaMs = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} returns this + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.setTimeIotaMs = function(value) { + return jspb.Message.setProto3StringField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.toObject = function(includeInstance, msg) { + var f, obj = { + maxAgeNumBlocks: jspb.Message.getFieldWithDefault(msg, 1, ""), + maxAgeDuration: jspb.Message.getFieldWithDefault(msg, 2, ""), + maxBytes: jspb.Message.getFieldWithDefault(msg, 3, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence; + return proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setMaxAgeNumBlocks(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setMaxAgeDuration(value); + break; + case 3: + var value = /** @type {string} */ (reader.readString()); + msg.setMaxBytes(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getMaxAgeNumBlocks(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getMaxAgeDuration(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getMaxBytes(); + if (f.length > 0) { + writer.writeString( + 3, + f + ); + } +}; + + +/** + * optional string max_age_num_blocks = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.getMaxAgeNumBlocks = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} returns this + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.setMaxAgeNumBlocks = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional string max_age_duration = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.getMaxAgeDuration = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} returns this + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.setMaxAgeDuration = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional string max_bytes = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.getMaxBytes = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} returns this + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.setMaxBytes = function(value) { + return jspb.Message.setProto3StringField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + height: jspb.Message.getFieldWithDefault(msg, 1, 0), + prove: jspb.Message.getBooleanFieldWithDefault(msg, 2, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest; + return proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readInt64()); + msg.setHeight(value); + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setProve(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getHeight(); + if (f !== 0) { + writer.writeInt64( + 1, + f + ); + } + f = message.getProve(); + if (f) { + writer.writeBool( + 2, + f + ); + } +}; + + +/** + * optional int64 height = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.prototype.getHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.prototype.setHeight = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional bool prove = 2; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.prototype.getProve = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.prototype.setProve = function(value) { + return jspb.Message.setProto3BooleanField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + block: (f = msg.getBlock()) && proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.toObject(includeInstance, f), + evidence: (f = msg.getEvidence()) && proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse; + return proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.org.dash.platform.dapi.v0.ConsensusParamsBlock; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.deserializeBinaryFromReader); + msg.setBlock(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.deserializeBinaryFromReader); + msg.setEvidence(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getBlock(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.serializeBinaryToWriter + ); + } + f = message.getEvidence(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.serializeBinaryToWriter + ); + } +}; + + +/** + * optional ConsensusParamsBlock block = 1; + * @return {?proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.getBlock = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ConsensusParamsBlock, 1)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ConsensusParamsBlock|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.setBlock = function(value) { + return jspb.Message.setWrapperField(this, 1, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.clearBlock = function() { + return this.setBlock(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.hasBlock = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional ConsensusParamsEvidence evidence = 2; + * @return {?proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.getEvidence = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.setEvidence = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.clearEvidence = function() { + return this.setEvidence(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.hasEvidence = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +goog.object.extend(exports, proto.org.dash.platform.dapi.v0); diff --git a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h new file mode 100644 index 00000000000..404d3cc63b8 --- /dev/null +++ b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h @@ -0,0 +1,431 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: platform.proto + +// This CPP symbol can be defined to use imports that match up to the framework +// imports needed when using CocoaPods. +#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS) + #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0 +#endif + +#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS + #import +#else + #import "GPBProtocolBuffers.h" +#endif + +#if GOOGLE_PROTOBUF_OBJC_VERSION < 30004 +#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources. +#endif +#if 30004 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION +#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources. +#endif + +// @@protoc_insertion_point(imports) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +CF_EXTERN_C_BEGIN + +@class ConsensusParamsBlock; +@class ConsensusParamsEvidence; +@class Proof; +@class ResponseMetadata; +@class StateTransitionBroadcastError; + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark - PlatformRoot + +/** + * Exposes the extension registry for this file. + * + * The base class provides: + * @code + * + (GPBExtensionRegistry *)extensionRegistry; + * @endcode + * which is a @c GPBExtensionRegistry that includes all the extensions defined by + * this file and all files that it depends on. + **/ +GPB_FINAL @interface PlatformRoot : GPBRootObject +@end + +#pragma mark - Proof + +typedef GPB_ENUM(Proof_FieldNumber) { + Proof_FieldNumber_MerkleProof = 1, + Proof_FieldNumber_SignatureLlmqHash = 2, + Proof_FieldNumber_Signature = 3, +}; + +GPB_FINAL @interface Proof : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSData *merkleProof; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *signatureLlmqHash; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *signature; + +@end + +#pragma mark - ResponseMetadata + +typedef GPB_ENUM(ResponseMetadata_FieldNumber) { + ResponseMetadata_FieldNumber_Height = 1, + ResponseMetadata_FieldNumber_CoreChainLockedHeight = 2, +}; + +GPB_FINAL @interface ResponseMetadata : GPBMessage + +@property(nonatomic, readwrite) int64_t height; + +@property(nonatomic, readwrite) uint32_t coreChainLockedHeight; + +@end + +#pragma mark - StateTransitionBroadcastError + +typedef GPB_ENUM(StateTransitionBroadcastError_FieldNumber) { + StateTransitionBroadcastError_FieldNumber_Code = 1, + StateTransitionBroadcastError_FieldNumber_Message = 2, + StateTransitionBroadcastError_FieldNumber_Data_p = 3, +}; + +GPB_FINAL @interface StateTransitionBroadcastError : GPBMessage + +@property(nonatomic, readwrite) uint32_t code; + +@property(nonatomic, readwrite, copy, null_resettable) NSString *message; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *data_p; + +@end + +#pragma mark - BroadcastStateTransitionRequest + +typedef GPB_ENUM(BroadcastStateTransitionRequest_FieldNumber) { + BroadcastStateTransitionRequest_FieldNumber_StateTransition = 1, +}; + +GPB_FINAL @interface BroadcastStateTransitionRequest : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSData *stateTransition; + +@end + +#pragma mark - BroadcastStateTransitionResponse + +GPB_FINAL @interface BroadcastStateTransitionResponse : GPBMessage + +@end + +#pragma mark - GetIdentityRequest + +typedef GPB_ENUM(GetIdentityRequest_FieldNumber) { + GetIdentityRequest_FieldNumber_Id_p = 1, + GetIdentityRequest_FieldNumber_Prove = 2, +}; + +GPB_FINAL @interface GetIdentityRequest : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSData *id_p; + +@property(nonatomic, readwrite) BOOL prove; + +@end + +#pragma mark - GetIdentityResponse + +typedef GPB_ENUM(GetIdentityResponse_FieldNumber) { + GetIdentityResponse_FieldNumber_Identity = 1, + GetIdentityResponse_FieldNumber_Proof = 2, + GetIdentityResponse_FieldNumber_Metadata = 3, +}; + +GPB_FINAL @interface GetIdentityResponse : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSData *identity; + +@property(nonatomic, readwrite, strong, null_resettable) Proof *proof; +/** Test to see if @c proof has been set. */ +@property(nonatomic, readwrite) BOOL hasProof; + +@property(nonatomic, readwrite, strong, null_resettable) ResponseMetadata *metadata; +/** Test to see if @c metadata has been set. */ +@property(nonatomic, readwrite) BOOL hasMetadata; + +@end + +#pragma mark - GetDataContractRequest + +typedef GPB_ENUM(GetDataContractRequest_FieldNumber) { + GetDataContractRequest_FieldNumber_Id_p = 1, + GetDataContractRequest_FieldNumber_Prove = 2, +}; + +GPB_FINAL @interface GetDataContractRequest : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSData *id_p; + +@property(nonatomic, readwrite) BOOL prove; + +@end + +#pragma mark - GetDataContractResponse + +typedef GPB_ENUM(GetDataContractResponse_FieldNumber) { + GetDataContractResponse_FieldNumber_DataContract = 1, + GetDataContractResponse_FieldNumber_Proof = 2, + GetDataContractResponse_FieldNumber_Metadata = 3, +}; + +GPB_FINAL @interface GetDataContractResponse : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSData *dataContract; + +@property(nonatomic, readwrite, strong, null_resettable) Proof *proof; +/** Test to see if @c proof has been set. */ +@property(nonatomic, readwrite) BOOL hasProof; + +@property(nonatomic, readwrite, strong, null_resettable) ResponseMetadata *metadata; +/** Test to see if @c metadata has been set. */ +@property(nonatomic, readwrite) BOOL hasMetadata; + +@end + +#pragma mark - GetDocumentsRequest + +typedef GPB_ENUM(GetDocumentsRequest_FieldNumber) { + GetDocumentsRequest_FieldNumber_DataContractId = 1, + GetDocumentsRequest_FieldNumber_DocumentType = 2, + GetDocumentsRequest_FieldNumber_Where = 3, + GetDocumentsRequest_FieldNumber_OrderBy = 4, + GetDocumentsRequest_FieldNumber_Limit = 5, + GetDocumentsRequest_FieldNumber_StartAfter = 6, + GetDocumentsRequest_FieldNumber_StartAt = 7, + GetDocumentsRequest_FieldNumber_Prove = 8, +}; + +typedef GPB_ENUM(GetDocumentsRequest_Start_OneOfCase) { + GetDocumentsRequest_Start_OneOfCase_GPBUnsetOneOfCase = 0, + GetDocumentsRequest_Start_OneOfCase_StartAfter = 6, + GetDocumentsRequest_Start_OneOfCase_StartAt = 7, +}; + +GPB_FINAL @interface GetDocumentsRequest : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSData *dataContractId; + +@property(nonatomic, readwrite, copy, null_resettable) NSString *documentType; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *where; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *orderBy; + +@property(nonatomic, readwrite) uint32_t limit; + +@property(nonatomic, readonly) GetDocumentsRequest_Start_OneOfCase startOneOfCase; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *startAfter; + +@property(nonatomic, readwrite, copy, null_resettable) NSData *startAt; + +@property(nonatomic, readwrite) BOOL prove; + +@end + +/** + * Clears whatever value was set for the oneof 'start'. + **/ +void GetDocumentsRequest_ClearStartOneOfCase(GetDocumentsRequest *message); + +#pragma mark - GetDocumentsResponse + +typedef GPB_ENUM(GetDocumentsResponse_FieldNumber) { + GetDocumentsResponse_FieldNumber_DocumentsArray = 1, + GetDocumentsResponse_FieldNumber_Proof = 2, + GetDocumentsResponse_FieldNumber_Metadata = 3, +}; + +GPB_FINAL @interface GetDocumentsResponse : GPBMessage + +@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *documentsArray; +/** The number of items in @c documentsArray without causing the array to be created. */ +@property(nonatomic, readonly) NSUInteger documentsArray_Count; + +@property(nonatomic, readwrite, strong, null_resettable) Proof *proof; +/** Test to see if @c proof has been set. */ +@property(nonatomic, readwrite) BOOL hasProof; + +@property(nonatomic, readwrite, strong, null_resettable) ResponseMetadata *metadata; +/** Test to see if @c metadata has been set. */ +@property(nonatomic, readwrite) BOOL hasMetadata; + +@end + +#pragma mark - GetIdentitiesByPublicKeyHashesRequest + +typedef GPB_ENUM(GetIdentitiesByPublicKeyHashesRequest_FieldNumber) { + GetIdentitiesByPublicKeyHashesRequest_FieldNumber_PublicKeyHashesArray = 1, + GetIdentitiesByPublicKeyHashesRequest_FieldNumber_Prove = 2, +}; + +GPB_FINAL @interface GetIdentitiesByPublicKeyHashesRequest : GPBMessage + +@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *publicKeyHashesArray; +/** The number of items in @c publicKeyHashesArray without causing the array to be created. */ +@property(nonatomic, readonly) NSUInteger publicKeyHashesArray_Count; + +@property(nonatomic, readwrite) BOOL prove; + +@end + +#pragma mark - GetIdentitiesByPublicKeyHashesResponse + +typedef GPB_ENUM(GetIdentitiesByPublicKeyHashesResponse_FieldNumber) { + GetIdentitiesByPublicKeyHashesResponse_FieldNumber_IdentitiesArray = 1, + GetIdentitiesByPublicKeyHashesResponse_FieldNumber_Proof = 2, + GetIdentitiesByPublicKeyHashesResponse_FieldNumber_Metadata = 3, +}; + +GPB_FINAL @interface GetIdentitiesByPublicKeyHashesResponse : GPBMessage + +@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *identitiesArray; +/** The number of items in @c identitiesArray without causing the array to be created. */ +@property(nonatomic, readonly) NSUInteger identitiesArray_Count; + +@property(nonatomic, readwrite, strong, null_resettable) Proof *proof; +/** Test to see if @c proof has been set. */ +@property(nonatomic, readwrite) BOOL hasProof; + +@property(nonatomic, readwrite, strong, null_resettable) ResponseMetadata *metadata; +/** Test to see if @c metadata has been set. */ +@property(nonatomic, readwrite) BOOL hasMetadata; + +@end + +#pragma mark - WaitForStateTransitionResultRequest + +typedef GPB_ENUM(WaitForStateTransitionResultRequest_FieldNumber) { + WaitForStateTransitionResultRequest_FieldNumber_StateTransitionHash = 1, + WaitForStateTransitionResultRequest_FieldNumber_Prove = 2, +}; + +GPB_FINAL @interface WaitForStateTransitionResultRequest : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSData *stateTransitionHash; + +@property(nonatomic, readwrite) BOOL prove; + +@end + +#pragma mark - WaitForStateTransitionResultResponse + +typedef GPB_ENUM(WaitForStateTransitionResultResponse_FieldNumber) { + WaitForStateTransitionResultResponse_FieldNumber_Error = 1, + WaitForStateTransitionResultResponse_FieldNumber_Proof = 2, + WaitForStateTransitionResultResponse_FieldNumber_Metadata = 3, +}; + +typedef GPB_ENUM(WaitForStateTransitionResultResponse_Responses_OneOfCase) { + WaitForStateTransitionResultResponse_Responses_OneOfCase_GPBUnsetOneOfCase = 0, + WaitForStateTransitionResultResponse_Responses_OneOfCase_Error = 1, + WaitForStateTransitionResultResponse_Responses_OneOfCase_Proof = 2, +}; + +GPB_FINAL @interface WaitForStateTransitionResultResponse : GPBMessage + +@property(nonatomic, readonly) WaitForStateTransitionResultResponse_Responses_OneOfCase responsesOneOfCase; + +@property(nonatomic, readwrite, strong, null_resettable) StateTransitionBroadcastError *error; + +@property(nonatomic, readwrite, strong, null_resettable) Proof *proof; + +@property(nonatomic, readwrite, strong, null_resettable) ResponseMetadata *metadata; +/** Test to see if @c metadata has been set. */ +@property(nonatomic, readwrite) BOOL hasMetadata; + +@end + +/** + * Clears whatever value was set for the oneof 'responses'. + **/ +void WaitForStateTransitionResultResponse_ClearResponsesOneOfCase(WaitForStateTransitionResultResponse *message); + +#pragma mark - ConsensusParamsBlock + +typedef GPB_ENUM(ConsensusParamsBlock_FieldNumber) { + ConsensusParamsBlock_FieldNumber_MaxBytes = 1, + ConsensusParamsBlock_FieldNumber_MaxGas = 2, + ConsensusParamsBlock_FieldNumber_TimeIotaMs = 3, +}; + +GPB_FINAL @interface ConsensusParamsBlock : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSString *maxBytes; + +@property(nonatomic, readwrite, copy, null_resettable) NSString *maxGas; + +@property(nonatomic, readwrite, copy, null_resettable) NSString *timeIotaMs; + +@end + +#pragma mark - ConsensusParamsEvidence + +typedef GPB_ENUM(ConsensusParamsEvidence_FieldNumber) { + ConsensusParamsEvidence_FieldNumber_MaxAgeNumBlocks = 1, + ConsensusParamsEvidence_FieldNumber_MaxAgeDuration = 2, + ConsensusParamsEvidence_FieldNumber_MaxBytes = 3, +}; + +GPB_FINAL @interface ConsensusParamsEvidence : GPBMessage + +@property(nonatomic, readwrite, copy, null_resettable) NSString *maxAgeNumBlocks; + +@property(nonatomic, readwrite, copy, null_resettable) NSString *maxAgeDuration; + +@property(nonatomic, readwrite, copy, null_resettable) NSString *maxBytes; + +@end + +#pragma mark - GetConsensusParamsRequest + +typedef GPB_ENUM(GetConsensusParamsRequest_FieldNumber) { + GetConsensusParamsRequest_FieldNumber_Height = 1, + GetConsensusParamsRequest_FieldNumber_Prove = 2, +}; + +GPB_FINAL @interface GetConsensusParamsRequest : GPBMessage + +@property(nonatomic, readwrite) int64_t height; + +@property(nonatomic, readwrite) BOOL prove; + +@end + +#pragma mark - GetConsensusParamsResponse + +typedef GPB_ENUM(GetConsensusParamsResponse_FieldNumber) { + GetConsensusParamsResponse_FieldNumber_Block = 1, + GetConsensusParamsResponse_FieldNumber_Evidence = 2, +}; + +GPB_FINAL @interface GetConsensusParamsResponse : GPBMessage + +@property(nonatomic, readwrite, strong, null_resettable) ConsensusParamsBlock *block; +/** Test to see if @c block has been set. */ +@property(nonatomic, readwrite) BOOL hasBlock; + +@property(nonatomic, readwrite, strong, null_resettable) ConsensusParamsEvidence *evidence; +/** Test to see if @c evidence has been set. */ +@property(nonatomic, readwrite) BOOL hasEvidence; + +@end + +NS_ASSUME_NONNULL_END + +CF_EXTERN_C_END + +#pragma clang diagnostic pop + +// @@protoc_insertion_point(global_scope) diff --git a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.m b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.m new file mode 100644 index 00000000000..fdf7a0558f9 --- /dev/null +++ b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.m @@ -0,0 +1,1272 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: platform.proto + +// This CPP symbol can be defined to use imports that match up to the framework +// imports needed when using CocoaPods. +#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS) + #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0 +#endif + +#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS + #import +#else + #import "GPBProtocolBuffers_RuntimeSupport.h" +#endif + +#import "Platform.pbobjc.h" +// @@protoc_insertion_point(imports) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdirect-ivar-access" +#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" + +#pragma mark - Objective C Class declarations +// Forward declarations of Objective C classes that we can use as +// static values in struct initializers. +// We don't use [Foo class] because it is not a static value. +GPBObjCClassDeclaration(ConsensusParamsBlock); +GPBObjCClassDeclaration(ConsensusParamsEvidence); +GPBObjCClassDeclaration(Proof); +GPBObjCClassDeclaration(ResponseMetadata); +GPBObjCClassDeclaration(StateTransitionBroadcastError); + +#pragma mark - PlatformRoot + +@implementation PlatformRoot + +// No extensions in the file and no imports, so no need to generate +// +extensionRegistry. + +@end + +#pragma mark - PlatformRoot_FileDescriptor + +static GPBFileDescriptor *PlatformRoot_FileDescriptor(void) { + // This is called by +initialize so there is no need to worry + // about thread safety of the singleton. + static GPBFileDescriptor *descriptor = NULL; + if (!descriptor) { + GPB_DEBUG_CHECK_RUNTIME_VERSIONS(); + descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"org.dash.platform.dapi.v0" + syntax:GPBFileSyntaxProto3]; + } + return descriptor; +} + +#pragma mark - Proof + +@implementation Proof + +@dynamic merkleProof; +@dynamic signatureLlmqHash; +@dynamic signature; + +typedef struct Proof__storage_ { + uint32_t _has_storage_[1]; + NSData *merkleProof; + NSData *signatureLlmqHash; + NSData *signature; +} Proof__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "merkleProof", + .dataTypeSpecific.clazz = Nil, + .number = Proof_FieldNumber_MerkleProof, + .hasIndex = 0, + .offset = (uint32_t)offsetof(Proof__storage_, merkleProof), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "signatureLlmqHash", + .dataTypeSpecific.clazz = Nil, + .number = Proof_FieldNumber_SignatureLlmqHash, + .hasIndex = 1, + .offset = (uint32_t)offsetof(Proof__storage_, signatureLlmqHash), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "signature", + .dataTypeSpecific.clazz = Nil, + .number = Proof_FieldNumber_Signature, + .hasIndex = 2, + .offset = (uint32_t)offsetof(Proof__storage_, signature), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[Proof class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(Proof__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - ResponseMetadata + +@implementation ResponseMetadata + +@dynamic height; +@dynamic coreChainLockedHeight; + +typedef struct ResponseMetadata__storage_ { + uint32_t _has_storage_[1]; + uint32_t coreChainLockedHeight; + int64_t height; +} ResponseMetadata__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "height", + .dataTypeSpecific.clazz = Nil, + .number = ResponseMetadata_FieldNumber_Height, + .hasIndex = 0, + .offset = (uint32_t)offsetof(ResponseMetadata__storage_, height), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeInt64, + }, + { + .name = "coreChainLockedHeight", + .dataTypeSpecific.clazz = Nil, + .number = ResponseMetadata_FieldNumber_CoreChainLockedHeight, + .hasIndex = 1, + .offset = (uint32_t)offsetof(ResponseMetadata__storage_, coreChainLockedHeight), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[ResponseMetadata class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(ResponseMetadata__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - StateTransitionBroadcastError + +@implementation StateTransitionBroadcastError + +@dynamic code; +@dynamic message; +@dynamic data_p; + +typedef struct StateTransitionBroadcastError__storage_ { + uint32_t _has_storage_[1]; + uint32_t code; + NSString *message; + NSData *data_p; +} StateTransitionBroadcastError__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "code", + .dataTypeSpecific.clazz = Nil, + .number = StateTransitionBroadcastError_FieldNumber_Code, + .hasIndex = 0, + .offset = (uint32_t)offsetof(StateTransitionBroadcastError__storage_, code), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "message", + .dataTypeSpecific.clazz = Nil, + .number = StateTransitionBroadcastError_FieldNumber_Message, + .hasIndex = 1, + .offset = (uint32_t)offsetof(StateTransitionBroadcastError__storage_, message), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeString, + }, + { + .name = "data_p", + .dataTypeSpecific.clazz = Nil, + .number = StateTransitionBroadcastError_FieldNumber_Data_p, + .hasIndex = 2, + .offset = (uint32_t)offsetof(StateTransitionBroadcastError__storage_, data_p), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[StateTransitionBroadcastError class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(StateTransitionBroadcastError__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - BroadcastStateTransitionRequest + +@implementation BroadcastStateTransitionRequest + +@dynamic stateTransition; + +typedef struct BroadcastStateTransitionRequest__storage_ { + uint32_t _has_storage_[1]; + NSData *stateTransition; +} BroadcastStateTransitionRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "stateTransition", + .dataTypeSpecific.clazz = Nil, + .number = BroadcastStateTransitionRequest_FieldNumber_StateTransition, + .hasIndex = 0, + .offset = (uint32_t)offsetof(BroadcastStateTransitionRequest__storage_, stateTransition), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[BroadcastStateTransitionRequest class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(BroadcastStateTransitionRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - BroadcastStateTransitionResponse + +@implementation BroadcastStateTransitionResponse + + +typedef struct BroadcastStateTransitionResponse__storage_ { + uint32_t _has_storage_[1]; +} BroadcastStateTransitionResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[BroadcastStateTransitionResponse class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:NULL + fieldCount:0 + storageSize:sizeof(BroadcastStateTransitionResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetIdentityRequest + +@implementation GetIdentityRequest + +@dynamic id_p; +@dynamic prove; + +typedef struct GetIdentityRequest__storage_ { + uint32_t _has_storage_[1]; + NSData *id_p; +} GetIdentityRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "id_p", + .dataTypeSpecific.clazz = Nil, + .number = GetIdentityRequest_FieldNumber_Id_p, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetIdentityRequest__storage_, id_p), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "prove", + .dataTypeSpecific.clazz = Nil, + .number = GetIdentityRequest_FieldNumber_Prove, + .hasIndex = 1, + .offset = 2, // Stored in _has_storage_ to save space. + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBool, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetIdentityRequest class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetIdentityRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetIdentityResponse + +@implementation GetIdentityResponse + +@dynamic identity; +@dynamic hasProof, proof; +@dynamic hasMetadata, metadata; + +typedef struct GetIdentityResponse__storage_ { + uint32_t _has_storage_[1]; + NSData *identity; + Proof *proof; + ResponseMetadata *metadata; +} GetIdentityResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "identity", + .dataTypeSpecific.clazz = Nil, + .number = GetIdentityResponse_FieldNumber_Identity, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetIdentityResponse__storage_, identity), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "proof", + .dataTypeSpecific.clazz = GPBObjCClass(Proof), + .number = GetIdentityResponse_FieldNumber_Proof, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetIdentityResponse__storage_, proof), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "metadata", + .dataTypeSpecific.clazz = GPBObjCClass(ResponseMetadata), + .number = GetIdentityResponse_FieldNumber_Metadata, + .hasIndex = 2, + .offset = (uint32_t)offsetof(GetIdentityResponse__storage_, metadata), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetIdentityResponse class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetIdentityResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetDataContractRequest + +@implementation GetDataContractRequest + +@dynamic id_p; +@dynamic prove; + +typedef struct GetDataContractRequest__storage_ { + uint32_t _has_storage_[1]; + NSData *id_p; +} GetDataContractRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "id_p", + .dataTypeSpecific.clazz = Nil, + .number = GetDataContractRequest_FieldNumber_Id_p, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetDataContractRequest__storage_, id_p), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "prove", + .dataTypeSpecific.clazz = Nil, + .number = GetDataContractRequest_FieldNumber_Prove, + .hasIndex = 1, + .offset = 2, // Stored in _has_storage_ to save space. + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBool, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetDataContractRequest class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetDataContractRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetDataContractResponse + +@implementation GetDataContractResponse + +@dynamic dataContract; +@dynamic hasProof, proof; +@dynamic hasMetadata, metadata; + +typedef struct GetDataContractResponse__storage_ { + uint32_t _has_storage_[1]; + NSData *dataContract; + Proof *proof; + ResponseMetadata *metadata; +} GetDataContractResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "dataContract", + .dataTypeSpecific.clazz = Nil, + .number = GetDataContractResponse_FieldNumber_DataContract, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetDataContractResponse__storage_, dataContract), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "proof", + .dataTypeSpecific.clazz = GPBObjCClass(Proof), + .number = GetDataContractResponse_FieldNumber_Proof, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetDataContractResponse__storage_, proof), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "metadata", + .dataTypeSpecific.clazz = GPBObjCClass(ResponseMetadata), + .number = GetDataContractResponse_FieldNumber_Metadata, + .hasIndex = 2, + .offset = (uint32_t)offsetof(GetDataContractResponse__storage_, metadata), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetDataContractResponse class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetDataContractResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetDocumentsRequest + +@implementation GetDocumentsRequest + +@dynamic startOneOfCase; +@dynamic dataContractId; +@dynamic documentType; +@dynamic where; +@dynamic orderBy; +@dynamic limit; +@dynamic startAfter; +@dynamic startAt; +@dynamic prove; + +typedef struct GetDocumentsRequest__storage_ { + uint32_t _has_storage_[2]; + uint32_t limit; + NSData *dataContractId; + NSString *documentType; + NSData *where; + NSData *orderBy; + NSData *startAfter; + NSData *startAt; +} GetDocumentsRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "dataContractId", + .dataTypeSpecific.clazz = Nil, + .number = GetDocumentsRequest_FieldNumber_DataContractId, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetDocumentsRequest__storage_, dataContractId), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "documentType", + .dataTypeSpecific.clazz = Nil, + .number = GetDocumentsRequest_FieldNumber_DocumentType, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetDocumentsRequest__storage_, documentType), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeString, + }, + { + .name = "where", + .dataTypeSpecific.clazz = Nil, + .number = GetDocumentsRequest_FieldNumber_Where, + .hasIndex = 2, + .offset = (uint32_t)offsetof(GetDocumentsRequest__storage_, where), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "orderBy", + .dataTypeSpecific.clazz = Nil, + .number = GetDocumentsRequest_FieldNumber_OrderBy, + .hasIndex = 3, + .offset = (uint32_t)offsetof(GetDocumentsRequest__storage_, orderBy), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "limit", + .dataTypeSpecific.clazz = Nil, + .number = GetDocumentsRequest_FieldNumber_Limit, + .hasIndex = 4, + .offset = (uint32_t)offsetof(GetDocumentsRequest__storage_, limit), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeUInt32, + }, + { + .name = "startAfter", + .dataTypeSpecific.clazz = Nil, + .number = GetDocumentsRequest_FieldNumber_StartAfter, + .hasIndex = -1, + .offset = (uint32_t)offsetof(GetDocumentsRequest__storage_, startAfter), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeBytes, + }, + { + .name = "startAt", + .dataTypeSpecific.clazz = Nil, + .number = GetDocumentsRequest_FieldNumber_StartAt, + .hasIndex = -1, + .offset = (uint32_t)offsetof(GetDocumentsRequest__storage_, startAt), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeBytes, + }, + { + .name = "prove", + .dataTypeSpecific.clazz = Nil, + .number = GetDocumentsRequest_FieldNumber_Prove, + .hasIndex = 5, + .offset = 6, // Stored in _has_storage_ to save space. + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBool, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetDocumentsRequest class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetDocumentsRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + static const char *oneofs[] = { + "start", + }; + [localDescriptor setupOneofs:oneofs + count:(uint32_t)(sizeof(oneofs) / sizeof(char*)) + firstHasIndex:-1]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +void GetDocumentsRequest_ClearStartOneOfCase(GetDocumentsRequest *message) { + GPBDescriptor *descriptor = [GetDocumentsRequest descriptor]; + GPBOneofDescriptor *oneof = [descriptor.oneofs objectAtIndex:0]; + GPBClearOneof(message, oneof); +} +#pragma mark - GetDocumentsResponse + +@implementation GetDocumentsResponse + +@dynamic documentsArray, documentsArray_Count; +@dynamic hasProof, proof; +@dynamic hasMetadata, metadata; + +typedef struct GetDocumentsResponse__storage_ { + uint32_t _has_storage_[1]; + NSMutableArray *documentsArray; + Proof *proof; + ResponseMetadata *metadata; +} GetDocumentsResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "documentsArray", + .dataTypeSpecific.clazz = Nil, + .number = GetDocumentsResponse_FieldNumber_DocumentsArray, + .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GetDocumentsResponse__storage_, documentsArray), + .flags = GPBFieldRepeated, + .dataType = GPBDataTypeBytes, + }, + { + .name = "proof", + .dataTypeSpecific.clazz = GPBObjCClass(Proof), + .number = GetDocumentsResponse_FieldNumber_Proof, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetDocumentsResponse__storage_, proof), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "metadata", + .dataTypeSpecific.clazz = GPBObjCClass(ResponseMetadata), + .number = GetDocumentsResponse_FieldNumber_Metadata, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetDocumentsResponse__storage_, metadata), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetDocumentsResponse class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetDocumentsResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetIdentitiesByPublicKeyHashesRequest + +@implementation GetIdentitiesByPublicKeyHashesRequest + +@dynamic publicKeyHashesArray, publicKeyHashesArray_Count; +@dynamic prove; + +typedef struct GetIdentitiesByPublicKeyHashesRequest__storage_ { + uint32_t _has_storage_[1]; + NSMutableArray *publicKeyHashesArray; +} GetIdentitiesByPublicKeyHashesRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "publicKeyHashesArray", + .dataTypeSpecific.clazz = Nil, + .number = GetIdentitiesByPublicKeyHashesRequest_FieldNumber_PublicKeyHashesArray, + .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GetIdentitiesByPublicKeyHashesRequest__storage_, publicKeyHashesArray), + .flags = GPBFieldRepeated, + .dataType = GPBDataTypeBytes, + }, + { + .name = "prove", + .dataTypeSpecific.clazz = Nil, + .number = GetIdentitiesByPublicKeyHashesRequest_FieldNumber_Prove, + .hasIndex = 0, + .offset = 1, // Stored in _has_storage_ to save space. + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBool, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetIdentitiesByPublicKeyHashesRequest class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetIdentitiesByPublicKeyHashesRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetIdentitiesByPublicKeyHashesResponse + +@implementation GetIdentitiesByPublicKeyHashesResponse + +@dynamic identitiesArray, identitiesArray_Count; +@dynamic hasProof, proof; +@dynamic hasMetadata, metadata; + +typedef struct GetIdentitiesByPublicKeyHashesResponse__storage_ { + uint32_t _has_storage_[1]; + NSMutableArray *identitiesArray; + Proof *proof; + ResponseMetadata *metadata; +} GetIdentitiesByPublicKeyHashesResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "identitiesArray", + .dataTypeSpecific.clazz = Nil, + .number = GetIdentitiesByPublicKeyHashesResponse_FieldNumber_IdentitiesArray, + .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GetIdentitiesByPublicKeyHashesResponse__storage_, identitiesArray), + .flags = GPBFieldRepeated, + .dataType = GPBDataTypeBytes, + }, + { + .name = "proof", + .dataTypeSpecific.clazz = GPBObjCClass(Proof), + .number = GetIdentitiesByPublicKeyHashesResponse_FieldNumber_Proof, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetIdentitiesByPublicKeyHashesResponse__storage_, proof), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "metadata", + .dataTypeSpecific.clazz = GPBObjCClass(ResponseMetadata), + .number = GetIdentitiesByPublicKeyHashesResponse_FieldNumber_Metadata, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetIdentitiesByPublicKeyHashesResponse__storage_, metadata), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetIdentitiesByPublicKeyHashesResponse class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetIdentitiesByPublicKeyHashesResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - WaitForStateTransitionResultRequest + +@implementation WaitForStateTransitionResultRequest + +@dynamic stateTransitionHash; +@dynamic prove; + +typedef struct WaitForStateTransitionResultRequest__storage_ { + uint32_t _has_storage_[1]; + NSData *stateTransitionHash; +} WaitForStateTransitionResultRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "stateTransitionHash", + .dataTypeSpecific.clazz = Nil, + .number = WaitForStateTransitionResultRequest_FieldNumber_StateTransitionHash, + .hasIndex = 0, + .offset = (uint32_t)offsetof(WaitForStateTransitionResultRequest__storage_, stateTransitionHash), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBytes, + }, + { + .name = "prove", + .dataTypeSpecific.clazz = Nil, + .number = WaitForStateTransitionResultRequest_FieldNumber_Prove, + .hasIndex = 1, + .offset = 2, // Stored in _has_storage_ to save space. + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBool, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[WaitForStateTransitionResultRequest class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(WaitForStateTransitionResultRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - WaitForStateTransitionResultResponse + +@implementation WaitForStateTransitionResultResponse + +@dynamic responsesOneOfCase; +@dynamic error; +@dynamic proof; +@dynamic hasMetadata, metadata; + +typedef struct WaitForStateTransitionResultResponse__storage_ { + uint32_t _has_storage_[2]; + StateTransitionBroadcastError *error; + Proof *proof; + ResponseMetadata *metadata; +} WaitForStateTransitionResultResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "error", + .dataTypeSpecific.clazz = GPBObjCClass(StateTransitionBroadcastError), + .number = WaitForStateTransitionResultResponse_FieldNumber_Error, + .hasIndex = -1, + .offset = (uint32_t)offsetof(WaitForStateTransitionResultResponse__storage_, error), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "proof", + .dataTypeSpecific.clazz = GPBObjCClass(Proof), + .number = WaitForStateTransitionResultResponse_FieldNumber_Proof, + .hasIndex = -1, + .offset = (uint32_t)offsetof(WaitForStateTransitionResultResponse__storage_, proof), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "metadata", + .dataTypeSpecific.clazz = GPBObjCClass(ResponseMetadata), + .number = WaitForStateTransitionResultResponse_FieldNumber_Metadata, + .hasIndex = 0, + .offset = (uint32_t)offsetof(WaitForStateTransitionResultResponse__storage_, metadata), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[WaitForStateTransitionResultResponse class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(WaitForStateTransitionResultResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + static const char *oneofs[] = { + "responses", + }; + [localDescriptor setupOneofs:oneofs + count:(uint32_t)(sizeof(oneofs) / sizeof(char*)) + firstHasIndex:-1]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +void WaitForStateTransitionResultResponse_ClearResponsesOneOfCase(WaitForStateTransitionResultResponse *message) { + GPBDescriptor *descriptor = [WaitForStateTransitionResultResponse descriptor]; + GPBOneofDescriptor *oneof = [descriptor.oneofs objectAtIndex:0]; + GPBClearOneof(message, oneof); +} +#pragma mark - ConsensusParamsBlock + +@implementation ConsensusParamsBlock + +@dynamic maxBytes; +@dynamic maxGas; +@dynamic timeIotaMs; + +typedef struct ConsensusParamsBlock__storage_ { + uint32_t _has_storage_[1]; + NSString *maxBytes; + NSString *maxGas; + NSString *timeIotaMs; +} ConsensusParamsBlock__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "maxBytes", + .dataTypeSpecific.clazz = Nil, + .number = ConsensusParamsBlock_FieldNumber_MaxBytes, + .hasIndex = 0, + .offset = (uint32_t)offsetof(ConsensusParamsBlock__storage_, maxBytes), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeString, + }, + { + .name = "maxGas", + .dataTypeSpecific.clazz = Nil, + .number = ConsensusParamsBlock_FieldNumber_MaxGas, + .hasIndex = 1, + .offset = (uint32_t)offsetof(ConsensusParamsBlock__storage_, maxGas), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeString, + }, + { + .name = "timeIotaMs", + .dataTypeSpecific.clazz = Nil, + .number = ConsensusParamsBlock_FieldNumber_TimeIotaMs, + .hasIndex = 2, + .offset = (uint32_t)offsetof(ConsensusParamsBlock__storage_, timeIotaMs), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeString, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[ConsensusParamsBlock class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(ConsensusParamsBlock__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - ConsensusParamsEvidence + +@implementation ConsensusParamsEvidence + +@dynamic maxAgeNumBlocks; +@dynamic maxAgeDuration; +@dynamic maxBytes; + +typedef struct ConsensusParamsEvidence__storage_ { + uint32_t _has_storage_[1]; + NSString *maxAgeNumBlocks; + NSString *maxAgeDuration; + NSString *maxBytes; +} ConsensusParamsEvidence__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "maxAgeNumBlocks", + .dataTypeSpecific.clazz = Nil, + .number = ConsensusParamsEvidence_FieldNumber_MaxAgeNumBlocks, + .hasIndex = 0, + .offset = (uint32_t)offsetof(ConsensusParamsEvidence__storage_, maxAgeNumBlocks), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeString, + }, + { + .name = "maxAgeDuration", + .dataTypeSpecific.clazz = Nil, + .number = ConsensusParamsEvidence_FieldNumber_MaxAgeDuration, + .hasIndex = 1, + .offset = (uint32_t)offsetof(ConsensusParamsEvidence__storage_, maxAgeDuration), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeString, + }, + { + .name = "maxBytes", + .dataTypeSpecific.clazz = Nil, + .number = ConsensusParamsEvidence_FieldNumber_MaxBytes, + .hasIndex = 2, + .offset = (uint32_t)offsetof(ConsensusParamsEvidence__storage_, maxBytes), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeString, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[ConsensusParamsEvidence class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(ConsensusParamsEvidence__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetConsensusParamsRequest + +@implementation GetConsensusParamsRequest + +@dynamic height; +@dynamic prove; + +typedef struct GetConsensusParamsRequest__storage_ { + uint32_t _has_storage_[1]; + int64_t height; +} GetConsensusParamsRequest__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "height", + .dataTypeSpecific.clazz = Nil, + .number = GetConsensusParamsRequest_FieldNumber_Height, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetConsensusParamsRequest__storage_, height), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeInt64, + }, + { + .name = "prove", + .dataTypeSpecific.clazz = Nil, + .number = GetConsensusParamsRequest_FieldNumber_Prove, + .hasIndex = 1, + .offset = 2, // Stored in _has_storage_ to save space. + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), + .dataType = GPBDataTypeBool, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetConsensusParamsRequest class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetConsensusParamsRequest__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + +#pragma mark - GetConsensusParamsResponse + +@implementation GetConsensusParamsResponse + +@dynamic hasBlock, block; +@dynamic hasEvidence, evidence; + +typedef struct GetConsensusParamsResponse__storage_ { + uint32_t _has_storage_[1]; + ConsensusParamsBlock *block; + ConsensusParamsEvidence *evidence; +} GetConsensusParamsResponse__storage_; + +// This method is threadsafe because it is initially called +// in +initialize for each subclass. ++ (GPBDescriptor *)descriptor { + static GPBDescriptor *descriptor = nil; + if (!descriptor) { + static GPBMessageFieldDescription fields[] = { + { + .name = "block", + .dataTypeSpecific.clazz = GPBObjCClass(ConsensusParamsBlock), + .number = GetConsensusParamsResponse_FieldNumber_Block, + .hasIndex = 0, + .offset = (uint32_t)offsetof(GetConsensusParamsResponse__storage_, block), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "evidence", + .dataTypeSpecific.clazz = GPBObjCClass(ConsensusParamsEvidence), + .number = GetConsensusParamsResponse_FieldNumber_Evidence, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetConsensusParamsResponse__storage_, evidence), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + }; + GPBDescriptor *localDescriptor = + [GPBDescriptor allocDescriptorForClass:[GetConsensusParamsResponse class] + rootClass:[PlatformRoot class] + file:PlatformRoot_FileDescriptor() + fields:fields + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) + storageSize:sizeof(GetConsensusParamsResponse__storage_) + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; + #if defined(DEBUG) && DEBUG + NSAssert(descriptor == nil, @"Startup recursed!"); + #endif // DEBUG + descriptor = localDescriptor; + } + return descriptor; +} + +@end + + +#pragma clang diagnostic pop + +// @@protoc_insertion_point(global_scope) diff --git a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbrpc.h b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbrpc.h new file mode 100644 index 00000000000..d04b2b24175 --- /dev/null +++ b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbrpc.h @@ -0,0 +1,149 @@ +// Code generated by gRPC proto compiler. DO NOT EDIT! +// source: platform.proto + +#import + +#if !defined(GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO) || !GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO +#import "Platform.pbobjc.h" +#endif + +#if !defined(GPB_GRPC_PROTOCOL_ONLY) || !GPB_GRPC_PROTOCOL_ONLY +#import +#import +#import +#import +#endif + +@class BroadcastStateTransitionRequest; +@class BroadcastStateTransitionResponse; +@class GetConsensusParamsRequest; +@class GetConsensusParamsResponse; +@class GetDataContractRequest; +@class GetDataContractResponse; +@class GetDocumentsRequest; +@class GetDocumentsResponse; +@class GetIdentitiesByPublicKeyHashesRequest; +@class GetIdentitiesByPublicKeyHashesResponse; +@class GetIdentityRequest; +@class GetIdentityResponse; +@class WaitForStateTransitionResultRequest; +@class WaitForStateTransitionResultResponse; + +#if !defined(GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO) || !GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO +#endif + +@class GRPCUnaryProtoCall; +@class GRPCStreamingProtoCall; +@class GRPCCallOptions; +@protocol GRPCProtoResponseHandler; +@class GRPCProtoCall; + + +NS_ASSUME_NONNULL_BEGIN + +@protocol Platform2 + +#pragma mark broadcastStateTransition(BroadcastStateTransitionRequest) returns (BroadcastStateTransitionResponse) + +- (GRPCUnaryProtoCall *)broadcastStateTransitionWithMessage:(BroadcastStateTransitionRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +#pragma mark getIdentity(GetIdentityRequest) returns (GetIdentityResponse) + +- (GRPCUnaryProtoCall *)getIdentityWithMessage:(GetIdentityRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +#pragma mark getDataContract(GetDataContractRequest) returns (GetDataContractResponse) + +- (GRPCUnaryProtoCall *)getDataContractWithMessage:(GetDataContractRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +#pragma mark getDocuments(GetDocumentsRequest) returns (GetDocumentsResponse) + +- (GRPCUnaryProtoCall *)getDocumentsWithMessage:(GetDocumentsRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +#pragma mark getIdentitiesByPublicKeyHashes(GetIdentitiesByPublicKeyHashesRequest) returns (GetIdentitiesByPublicKeyHashesResponse) + +- (GRPCUnaryProtoCall *)getIdentitiesByPublicKeyHashesWithMessage:(GetIdentitiesByPublicKeyHashesRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +#pragma mark waitForStateTransitionResult(WaitForStateTransitionResultRequest) returns (WaitForStateTransitionResultResponse) + +- (GRPCUnaryProtoCall *)waitForStateTransitionResultWithMessage:(WaitForStateTransitionResultRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +#pragma mark getConsensusParams(GetConsensusParamsRequest) returns (GetConsensusParamsResponse) + +- (GRPCUnaryProtoCall *)getConsensusParamsWithMessage:(GetConsensusParamsRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions; + +@end + +/** + * The methods in this protocol belong to a set of old APIs that have been deprecated. They do not + * recognize call options provided in the initializer. Using the v2 protocol is recommended. + */ +@protocol Platform + +#pragma mark broadcastStateTransition(BroadcastStateTransitionRequest) returns (BroadcastStateTransitionResponse) + +- (void)broadcastStateTransitionWithRequest:(BroadcastStateTransitionRequest *)request handler:(void(^)(BroadcastStateTransitionResponse *_Nullable response, NSError *_Nullable error))handler; + +- (GRPCProtoCall *)RPCTobroadcastStateTransitionWithRequest:(BroadcastStateTransitionRequest *)request handler:(void(^)(BroadcastStateTransitionResponse *_Nullable response, NSError *_Nullable error))handler; + + +#pragma mark getIdentity(GetIdentityRequest) returns (GetIdentityResponse) + +- (void)getIdentityWithRequest:(GetIdentityRequest *)request handler:(void(^)(GetIdentityResponse *_Nullable response, NSError *_Nullable error))handler; + +- (GRPCProtoCall *)RPCTogetIdentityWithRequest:(GetIdentityRequest *)request handler:(void(^)(GetIdentityResponse *_Nullable response, NSError *_Nullable error))handler; + + +#pragma mark getDataContract(GetDataContractRequest) returns (GetDataContractResponse) + +- (void)getDataContractWithRequest:(GetDataContractRequest *)request handler:(void(^)(GetDataContractResponse *_Nullable response, NSError *_Nullable error))handler; + +- (GRPCProtoCall *)RPCTogetDataContractWithRequest:(GetDataContractRequest *)request handler:(void(^)(GetDataContractResponse *_Nullable response, NSError *_Nullable error))handler; + + +#pragma mark getDocuments(GetDocumentsRequest) returns (GetDocumentsResponse) + +- (void)getDocumentsWithRequest:(GetDocumentsRequest *)request handler:(void(^)(GetDocumentsResponse *_Nullable response, NSError *_Nullable error))handler; + +- (GRPCProtoCall *)RPCTogetDocumentsWithRequest:(GetDocumentsRequest *)request handler:(void(^)(GetDocumentsResponse *_Nullable response, NSError *_Nullable error))handler; + + +#pragma mark getIdentitiesByPublicKeyHashes(GetIdentitiesByPublicKeyHashesRequest) returns (GetIdentitiesByPublicKeyHashesResponse) + +- (void)getIdentitiesByPublicKeyHashesWithRequest:(GetIdentitiesByPublicKeyHashesRequest *)request handler:(void(^)(GetIdentitiesByPublicKeyHashesResponse *_Nullable response, NSError *_Nullable error))handler; + +- (GRPCProtoCall *)RPCTogetIdentitiesByPublicKeyHashesWithRequest:(GetIdentitiesByPublicKeyHashesRequest *)request handler:(void(^)(GetIdentitiesByPublicKeyHashesResponse *_Nullable response, NSError *_Nullable error))handler; + + +#pragma mark waitForStateTransitionResult(WaitForStateTransitionResultRequest) returns (WaitForStateTransitionResultResponse) + +- (void)waitForStateTransitionResultWithRequest:(WaitForStateTransitionResultRequest *)request handler:(void(^)(WaitForStateTransitionResultResponse *_Nullable response, NSError *_Nullable error))handler; + +- (GRPCProtoCall *)RPCTowaitForStateTransitionResultWithRequest:(WaitForStateTransitionResultRequest *)request handler:(void(^)(WaitForStateTransitionResultResponse *_Nullable response, NSError *_Nullable error))handler; + + +#pragma mark getConsensusParams(GetConsensusParamsRequest) returns (GetConsensusParamsResponse) + +- (void)getConsensusParamsWithRequest:(GetConsensusParamsRequest *)request handler:(void(^)(GetConsensusParamsResponse *_Nullable response, NSError *_Nullable error))handler; + +- (GRPCProtoCall *)RPCTogetConsensusParamsWithRequest:(GetConsensusParamsRequest *)request handler:(void(^)(GetConsensusParamsResponse *_Nullable response, NSError *_Nullable error))handler; + + +@end + + +#if !defined(GPB_GRPC_PROTOCOL_ONLY) || !GPB_GRPC_PROTOCOL_ONLY +/** + * Basic service implementation, over gRPC, that only does + * marshalling and parsing. + */ +@interface Platform : GRPCProtoService +- (instancetype)initWithHost:(NSString *)host callOptions:(GRPCCallOptions *_Nullable)callOptions NS_DESIGNATED_INITIALIZER; ++ (instancetype)serviceWithHost:(NSString *)host callOptions:(GRPCCallOptions *_Nullable)callOptions; +// The following methods belong to a set of old APIs that have been deprecated. +- (instancetype)initWithHost:(NSString *)host; ++ (instancetype)serviceWithHost:(NSString *)host; +@end +#endif + +NS_ASSUME_NONNULL_END + diff --git a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbrpc.m b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbrpc.m new file mode 100644 index 00000000000..d7f2001b05f --- /dev/null +++ b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbrpc.m @@ -0,0 +1,199 @@ +// Code generated by gRPC proto compiler. DO NOT EDIT! +// source: platform.proto + +#if !defined(GPB_GRPC_PROTOCOL_ONLY) || !GPB_GRPC_PROTOCOL_ONLY +#import "Platform.pbrpc.h" +#import "Platform.pbobjc.h" +#import +#import + + +@implementation Platform + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" + +// Designated initializer +- (instancetype)initWithHost:(NSString *)host callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [super initWithHost:host + packageName:@"org.dash.platform.dapi.v0" + serviceName:@"Platform" + callOptions:callOptions]; +} + +- (instancetype)initWithHost:(NSString *)host { + return [super initWithHost:host + packageName:@"org.dash.platform.dapi.v0" + serviceName:@"Platform"]; +} + +#pragma clang diagnostic pop + +// Override superclass initializer to disallow different package and service names. +- (instancetype)initWithHost:(NSString *)host + packageName:(NSString *)packageName + serviceName:(NSString *)serviceName { + return [self initWithHost:host]; +} + +- (instancetype)initWithHost:(NSString *)host + packageName:(NSString *)packageName + serviceName:(NSString *)serviceName + callOptions:(GRPCCallOptions *)callOptions { + return [self initWithHost:host callOptions:callOptions]; +} + +#pragma mark - Class Methods + ++ (instancetype)serviceWithHost:(NSString *)host { + return [[self alloc] initWithHost:host]; +} + ++ (instancetype)serviceWithHost:(NSString *)host callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [[self alloc] initWithHost:host callOptions:callOptions]; +} + +#pragma mark - Method Implementations + +#pragma mark broadcastStateTransition(BroadcastStateTransitionRequest) returns (BroadcastStateTransitionResponse) + +- (void)broadcastStateTransitionWithRequest:(BroadcastStateTransitionRequest *)request handler:(void(^)(BroadcastStateTransitionResponse *_Nullable response, NSError *_Nullable error))handler{ + [[self RPCTobroadcastStateTransitionWithRequest:request handler:handler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTobroadcastStateTransitionWithRequest:(BroadcastStateTransitionRequest *)request handler:(void(^)(BroadcastStateTransitionResponse *_Nullable response, NSError *_Nullable error))handler{ + return [self RPCToMethod:@"broadcastStateTransition" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[BroadcastStateTransitionResponse class] + responsesWriteable:[GRXWriteable writeableWithSingleHandler:handler]]; +} +- (GRPCUnaryProtoCall *)broadcastStateTransitionWithMessage:(BroadcastStateTransitionRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"broadcastStateTransition" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[BroadcastStateTransitionResponse class]]; +} + +#pragma mark getIdentity(GetIdentityRequest) returns (GetIdentityResponse) + +- (void)getIdentityWithRequest:(GetIdentityRequest *)request handler:(void(^)(GetIdentityResponse *_Nullable response, NSError *_Nullable error))handler{ + [[self RPCTogetIdentityWithRequest:request handler:handler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTogetIdentityWithRequest:(GetIdentityRequest *)request handler:(void(^)(GetIdentityResponse *_Nullable response, NSError *_Nullable error))handler{ + return [self RPCToMethod:@"getIdentity" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[GetIdentityResponse class] + responsesWriteable:[GRXWriteable writeableWithSingleHandler:handler]]; +} +- (GRPCUnaryProtoCall *)getIdentityWithMessage:(GetIdentityRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"getIdentity" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[GetIdentityResponse class]]; +} + +#pragma mark getDataContract(GetDataContractRequest) returns (GetDataContractResponse) + +- (void)getDataContractWithRequest:(GetDataContractRequest *)request handler:(void(^)(GetDataContractResponse *_Nullable response, NSError *_Nullable error))handler{ + [[self RPCTogetDataContractWithRequest:request handler:handler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTogetDataContractWithRequest:(GetDataContractRequest *)request handler:(void(^)(GetDataContractResponse *_Nullable response, NSError *_Nullable error))handler{ + return [self RPCToMethod:@"getDataContract" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[GetDataContractResponse class] + responsesWriteable:[GRXWriteable writeableWithSingleHandler:handler]]; +} +- (GRPCUnaryProtoCall *)getDataContractWithMessage:(GetDataContractRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"getDataContract" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[GetDataContractResponse class]]; +} + +#pragma mark getDocuments(GetDocumentsRequest) returns (GetDocumentsResponse) + +- (void)getDocumentsWithRequest:(GetDocumentsRequest *)request handler:(void(^)(GetDocumentsResponse *_Nullable response, NSError *_Nullable error))handler{ + [[self RPCTogetDocumentsWithRequest:request handler:handler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTogetDocumentsWithRequest:(GetDocumentsRequest *)request handler:(void(^)(GetDocumentsResponse *_Nullable response, NSError *_Nullable error))handler{ + return [self RPCToMethod:@"getDocuments" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[GetDocumentsResponse class] + responsesWriteable:[GRXWriteable writeableWithSingleHandler:handler]]; +} +- (GRPCUnaryProtoCall *)getDocumentsWithMessage:(GetDocumentsRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"getDocuments" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[GetDocumentsResponse class]]; +} + +#pragma mark getIdentitiesByPublicKeyHashes(GetIdentitiesByPublicKeyHashesRequest) returns (GetIdentitiesByPublicKeyHashesResponse) + +- (void)getIdentitiesByPublicKeyHashesWithRequest:(GetIdentitiesByPublicKeyHashesRequest *)request handler:(void(^)(GetIdentitiesByPublicKeyHashesResponse *_Nullable response, NSError *_Nullable error))handler{ + [[self RPCTogetIdentitiesByPublicKeyHashesWithRequest:request handler:handler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTogetIdentitiesByPublicKeyHashesWithRequest:(GetIdentitiesByPublicKeyHashesRequest *)request handler:(void(^)(GetIdentitiesByPublicKeyHashesResponse *_Nullable response, NSError *_Nullable error))handler{ + return [self RPCToMethod:@"getIdentitiesByPublicKeyHashes" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[GetIdentitiesByPublicKeyHashesResponse class] + responsesWriteable:[GRXWriteable writeableWithSingleHandler:handler]]; +} +- (GRPCUnaryProtoCall *)getIdentitiesByPublicKeyHashesWithMessage:(GetIdentitiesByPublicKeyHashesRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"getIdentitiesByPublicKeyHashes" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[GetIdentitiesByPublicKeyHashesResponse class]]; +} + +#pragma mark waitForStateTransitionResult(WaitForStateTransitionResultRequest) returns (WaitForStateTransitionResultResponse) + +- (void)waitForStateTransitionResultWithRequest:(WaitForStateTransitionResultRequest *)request handler:(void(^)(WaitForStateTransitionResultResponse *_Nullable response, NSError *_Nullable error))handler{ + [[self RPCTowaitForStateTransitionResultWithRequest:request handler:handler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTowaitForStateTransitionResultWithRequest:(WaitForStateTransitionResultRequest *)request handler:(void(^)(WaitForStateTransitionResultResponse *_Nullable response, NSError *_Nullable error))handler{ + return [self RPCToMethod:@"waitForStateTransitionResult" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[WaitForStateTransitionResultResponse class] + responsesWriteable:[GRXWriteable writeableWithSingleHandler:handler]]; +} +- (GRPCUnaryProtoCall *)waitForStateTransitionResultWithMessage:(WaitForStateTransitionResultRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"waitForStateTransitionResult" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[WaitForStateTransitionResultResponse class]]; +} + +#pragma mark getConsensusParams(GetConsensusParamsRequest) returns (GetConsensusParamsResponse) + +- (void)getConsensusParamsWithRequest:(GetConsensusParamsRequest *)request handler:(void(^)(GetConsensusParamsResponse *_Nullable response, NSError *_Nullable error))handler{ + [[self RPCTogetConsensusParamsWithRequest:request handler:handler] start]; +} +// Returns a not-yet-started RPC object. +- (GRPCProtoCall *)RPCTogetConsensusParamsWithRequest:(GetConsensusParamsRequest *)request handler:(void(^)(GetConsensusParamsResponse *_Nullable response, NSError *_Nullable error))handler{ + return [self RPCToMethod:@"getConsensusParams" + requestsWriter:[GRXWriter writerWithValue:request] + responseClass:[GetConsensusParamsResponse class] + responsesWriteable:[GRXWriteable writeableWithSingleHandler:handler]]; +} +- (GRPCUnaryProtoCall *)getConsensusParamsWithMessage:(GetConsensusParamsRequest *)message responseHandler:(id)handler callOptions:(GRPCCallOptions *_Nullable)callOptions { + return [self RPCToMethod:@"getConsensusParams" + message:message + responseHandler:handler + callOptions:callOptions + responseClass:[GetConsensusParamsResponse class]]; +} + +@end +#endif diff --git a/packages/dapi-grpc/clients/platform/v0/python/platform_pb2.py b/packages/dapi-grpc/clients/platform/v0/python/platform_pb2.py new file mode 100644 index 00000000000..8c75cd81205 --- /dev/null +++ b/packages/dapi-grpc/clients/platform/v0/python/platform_pb2.py @@ -0,0 +1,1128 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: platform.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='platform.proto', + package='org.dash.platform.dapi.v0', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x0eplatform.proto\x12\x19org.dash.platform.dapi.v0\"M\n\x05Proof\x12\x14\n\x0cmerkle_proof\x18\x01 \x01(\x0c\x12\x1b\n\x13signature_llmq_hash\x18\x02 \x01(\x0c\x12\x11\n\tsignature\x18\x03 \x01(\x0c\"D\n\x10ResponseMetadata\x12\x0e\n\x06height\x18\x01 \x01(\x03\x12 \n\x18\x63ore_chain_locked_height\x18\x02 \x01(\r\"L\n\x1dStateTransitionBroadcastError\x12\x0c\n\x04\x63ode\x18\x01 \x01(\r\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\";\n\x1f\x42roadcastStateTransitionRequest\x12\x18\n\x10state_transition\x18\x01 \x01(\x0c\"\"\n BroadcastStateTransitionResponse\"/\n\x12GetIdentityRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\"\x97\x01\n\x13GetIdentityResponse\x12\x10\n\x08identity\x18\x01 \x01(\x0c\x12/\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.Proof\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\"3\n\x16GetDataContractRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\"\xa0\x01\n\x17GetDataContractResponse\x12\x15\n\rdata_contract\x18\x01 \x01(\x0c\x12/\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.Proof\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\"\xb9\x01\n\x13GetDocumentsRequest\x12\x18\n\x10\x64\x61ta_contract_id\x18\x01 \x01(\x0c\x12\x15\n\rdocument_type\x18\x02 \x01(\t\x12\r\n\x05where\x18\x03 \x01(\x0c\x12\x10\n\x08order_by\x18\x04 \x01(\x0c\x12\r\n\x05limit\x18\x05 \x01(\r\x12\x15\n\x0bstart_after\x18\x06 \x01(\x0cH\x00\x12\x12\n\x08start_at\x18\x07 \x01(\x0cH\x00\x12\r\n\x05prove\x18\x08 \x01(\x08\x42\x07\n\x05start\"\x99\x01\n\x14GetDocumentsResponse\x12\x11\n\tdocuments\x18\x01 \x03(\x0c\x12/\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.Proof\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\"Q\n%GetIdentitiesByPublicKeyHashesRequest\x12\x19\n\x11public_key_hashes\x18\x01 \x03(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\"\xac\x01\n&GetIdentitiesByPublicKeyHashesResponse\x12\x12\n\nidentities\x18\x01 \x03(\x0c\x12/\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.Proof\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\"S\n#WaitForStateTransitionResultRequest\x12\x1d\n\x15state_transition_hash\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\"\xf0\x01\n$WaitForStateTransitionResultResponse\x12I\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x38.org.dash.platform.dapi.v0.StateTransitionBroadcastErrorH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x0b\n\tresponses\"P\n\x14\x43onsensusParamsBlock\x12\x11\n\tmax_bytes\x18\x01 \x01(\t\x12\x0f\n\x07max_gas\x18\x02 \x01(\t\x12\x14\n\x0ctime_iota_ms\x18\x03 \x01(\t\"b\n\x17\x43onsensusParamsEvidence\x12\x1a\n\x12max_age_num_blocks\x18\x01 \x01(\t\x12\x18\n\x10max_age_duration\x18\x02 \x01(\t\x12\x11\n\tmax_bytes\x18\x03 \x01(\t\":\n\x19GetConsensusParamsRequest\x12\x0e\n\x06height\x18\x01 \x01(\x03\x12\r\n\x05prove\x18\x02 \x01(\x08\"\xa2\x01\n\x1aGetConsensusParamsResponse\x12>\n\x05\x62lock\x18\x01 \x01(\x0b\x32/.org.dash.platform.dapi.v0.ConsensusParamsBlock\x12\x44\n\x08\x65vidence\x18\x02 \x01(\x0b\x32\x32.org.dash.platform.dapi.v0.ConsensusParamsEvidence2\xc7\x07\n\x08Platform\x12\x93\x01\n\x18\x62roadcastStateTransition\x12:.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest\x1a;.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse\x12l\n\x0bgetIdentity\x12-.org.dash.platform.dapi.v0.GetIdentityRequest\x1a..org.dash.platform.dapi.v0.GetIdentityResponse\x12x\n\x0fgetDataContract\x12\x31.org.dash.platform.dapi.v0.GetDataContractRequest\x1a\x32.org.dash.platform.dapi.v0.GetDataContractResponse\x12o\n\x0cgetDocuments\x12..org.dash.platform.dapi.v0.GetDocumentsRequest\x1a/.org.dash.platform.dapi.v0.GetDocumentsResponse\x12\xa5\x01\n\x1egetIdentitiesByPublicKeyHashes\x12@.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest\x1a\x41.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse\x12\x9f\x01\n\x1cwaitForStateTransitionResult\x12>.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest\x1a?.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse\x12\x81\x01\n\x12getConsensusParams\x12\x34.org.dash.platform.dapi.v0.GetConsensusParamsRequest\x1a\x35.org.dash.platform.dapi.v0.GetConsensusParamsResponseb\x06proto3' +) + + + + +_PROOF = _descriptor.Descriptor( + name='Proof', + full_name='org.dash.platform.dapi.v0.Proof', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='merkle_proof', full_name='org.dash.platform.dapi.v0.Proof.merkle_proof', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='signature_llmq_hash', full_name='org.dash.platform.dapi.v0.Proof.signature_llmq_hash', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='signature', full_name='org.dash.platform.dapi.v0.Proof.signature', index=2, + number=3, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=45, + serialized_end=122, +) + + +_RESPONSEMETADATA = _descriptor.Descriptor( + name='ResponseMetadata', + full_name='org.dash.platform.dapi.v0.ResponseMetadata', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='height', full_name='org.dash.platform.dapi.v0.ResponseMetadata.height', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='core_chain_locked_height', full_name='org.dash.platform.dapi.v0.ResponseMetadata.core_chain_locked_height', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=124, + serialized_end=192, +) + + +_STATETRANSITIONBROADCASTERROR = _descriptor.Descriptor( + name='StateTransitionBroadcastError', + full_name='org.dash.platform.dapi.v0.StateTransitionBroadcastError', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='code', full_name='org.dash.platform.dapi.v0.StateTransitionBroadcastError.code', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='message', full_name='org.dash.platform.dapi.v0.StateTransitionBroadcastError.message', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='data', full_name='org.dash.platform.dapi.v0.StateTransitionBroadcastError.data', index=2, + number=3, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=194, + serialized_end=270, +) + + +_BROADCASTSTATETRANSITIONREQUEST = _descriptor.Descriptor( + name='BroadcastStateTransitionRequest', + full_name='org.dash.platform.dapi.v0.BroadcastStateTransitionRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='state_transition', full_name='org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.state_transition', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=272, + serialized_end=331, +) + + +_BROADCASTSTATETRANSITIONRESPONSE = _descriptor.Descriptor( + name='BroadcastStateTransitionResponse', + full_name='org.dash.platform.dapi.v0.BroadcastStateTransitionResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=333, + serialized_end=367, +) + + +_GETIDENTITYREQUEST = _descriptor.Descriptor( + name='GetIdentityRequest', + full_name='org.dash.platform.dapi.v0.GetIdentityRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='org.dash.platform.dapi.v0.GetIdentityRequest.id', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='prove', full_name='org.dash.platform.dapi.v0.GetIdentityRequest.prove', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=369, + serialized_end=416, +) + + +_GETIDENTITYRESPONSE = _descriptor.Descriptor( + name='GetIdentityResponse', + full_name='org.dash.platform.dapi.v0.GetIdentityResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='identity', full_name='org.dash.platform.dapi.v0.GetIdentityResponse.identity', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='proof', full_name='org.dash.platform.dapi.v0.GetIdentityResponse.proof', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='metadata', full_name='org.dash.platform.dapi.v0.GetIdentityResponse.metadata', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=419, + serialized_end=570, +) + + +_GETDATACONTRACTREQUEST = _descriptor.Descriptor( + name='GetDataContractRequest', + full_name='org.dash.platform.dapi.v0.GetDataContractRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='org.dash.platform.dapi.v0.GetDataContractRequest.id', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='prove', full_name='org.dash.platform.dapi.v0.GetDataContractRequest.prove', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=572, + serialized_end=623, +) + + +_GETDATACONTRACTRESPONSE = _descriptor.Descriptor( + name='GetDataContractResponse', + full_name='org.dash.platform.dapi.v0.GetDataContractResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='data_contract', full_name='org.dash.platform.dapi.v0.GetDataContractResponse.data_contract', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='proof', full_name='org.dash.platform.dapi.v0.GetDataContractResponse.proof', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='metadata', full_name='org.dash.platform.dapi.v0.GetDataContractResponse.metadata', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=626, + serialized_end=786, +) + + +_GETDOCUMENTSREQUEST = _descriptor.Descriptor( + name='GetDocumentsRequest', + full_name='org.dash.platform.dapi.v0.GetDocumentsRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='data_contract_id', full_name='org.dash.platform.dapi.v0.GetDocumentsRequest.data_contract_id', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='document_type', full_name='org.dash.platform.dapi.v0.GetDocumentsRequest.document_type', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='where', full_name='org.dash.platform.dapi.v0.GetDocumentsRequest.where', index=2, + number=3, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='order_by', full_name='org.dash.platform.dapi.v0.GetDocumentsRequest.order_by', index=3, + number=4, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='limit', full_name='org.dash.platform.dapi.v0.GetDocumentsRequest.limit', index=4, + number=5, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='start_after', full_name='org.dash.platform.dapi.v0.GetDocumentsRequest.start_after', index=5, + number=6, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='start_at', full_name='org.dash.platform.dapi.v0.GetDocumentsRequest.start_at', index=6, + number=7, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='prove', full_name='org.dash.platform.dapi.v0.GetDocumentsRequest.prove', index=7, + number=8, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='start', full_name='org.dash.platform.dapi.v0.GetDocumentsRequest.start', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=789, + serialized_end=974, +) + + +_GETDOCUMENTSRESPONSE = _descriptor.Descriptor( + name='GetDocumentsResponse', + full_name='org.dash.platform.dapi.v0.GetDocumentsResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='documents', full_name='org.dash.platform.dapi.v0.GetDocumentsResponse.documents', index=0, + number=1, type=12, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='proof', full_name='org.dash.platform.dapi.v0.GetDocumentsResponse.proof', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='metadata', full_name='org.dash.platform.dapi.v0.GetDocumentsResponse.metadata', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=977, + serialized_end=1130, +) + + +_GETIDENTITIESBYPUBLICKEYHASHESREQUEST = _descriptor.Descriptor( + name='GetIdentitiesByPublicKeyHashesRequest', + full_name='org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='public_key_hashes', full_name='org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.public_key_hashes', index=0, + number=1, type=12, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='prove', full_name='org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prove', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1132, + serialized_end=1213, +) + + +_GETIDENTITIESBYPUBLICKEYHASHESRESPONSE = _descriptor.Descriptor( + name='GetIdentitiesByPublicKeyHashesResponse', + full_name='org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='identities', full_name='org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.identities', index=0, + number=1, type=12, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='proof', full_name='org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.proof', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='metadata', full_name='org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.metadata', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1216, + serialized_end=1388, +) + + +_WAITFORSTATETRANSITIONRESULTREQUEST = _descriptor.Descriptor( + name='WaitForStateTransitionResultRequest', + full_name='org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='state_transition_hash', full_name='org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.state_transition_hash', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='prove', full_name='org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prove', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1390, + serialized_end=1473, +) + + +_WAITFORSTATETRANSITIONRESULTRESPONSE = _descriptor.Descriptor( + name='WaitForStateTransitionResultResponse', + full_name='org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='error', full_name='org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.error', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='proof', full_name='org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.proof', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='metadata', full_name='org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.metadata', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='responses', full_name='org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.responses', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=1476, + serialized_end=1716, +) + + +_CONSENSUSPARAMSBLOCK = _descriptor.Descriptor( + name='ConsensusParamsBlock', + full_name='org.dash.platform.dapi.v0.ConsensusParamsBlock', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='max_bytes', full_name='org.dash.platform.dapi.v0.ConsensusParamsBlock.max_bytes', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='max_gas', full_name='org.dash.platform.dapi.v0.ConsensusParamsBlock.max_gas', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='time_iota_ms', full_name='org.dash.platform.dapi.v0.ConsensusParamsBlock.time_iota_ms', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1718, + serialized_end=1798, +) + + +_CONSENSUSPARAMSEVIDENCE = _descriptor.Descriptor( + name='ConsensusParamsEvidence', + full_name='org.dash.platform.dapi.v0.ConsensusParamsEvidence', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='max_age_num_blocks', full_name='org.dash.platform.dapi.v0.ConsensusParamsEvidence.max_age_num_blocks', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='max_age_duration', full_name='org.dash.platform.dapi.v0.ConsensusParamsEvidence.max_age_duration', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='max_bytes', full_name='org.dash.platform.dapi.v0.ConsensusParamsEvidence.max_bytes', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1800, + serialized_end=1898, +) + + +_GETCONSENSUSPARAMSREQUEST = _descriptor.Descriptor( + name='GetConsensusParamsRequest', + full_name='org.dash.platform.dapi.v0.GetConsensusParamsRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='height', full_name='org.dash.platform.dapi.v0.GetConsensusParamsRequest.height', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='prove', full_name='org.dash.platform.dapi.v0.GetConsensusParamsRequest.prove', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1900, + serialized_end=1958, +) + + +_GETCONSENSUSPARAMSRESPONSE = _descriptor.Descriptor( + name='GetConsensusParamsResponse', + full_name='org.dash.platform.dapi.v0.GetConsensusParamsResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='block', full_name='org.dash.platform.dapi.v0.GetConsensusParamsResponse.block', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='evidence', full_name='org.dash.platform.dapi.v0.GetConsensusParamsResponse.evidence', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1961, + serialized_end=2123, +) + +_GETIDENTITYRESPONSE.fields_by_name['proof'].message_type = _PROOF +_GETIDENTITYRESPONSE.fields_by_name['metadata'].message_type = _RESPONSEMETADATA +_GETDATACONTRACTRESPONSE.fields_by_name['proof'].message_type = _PROOF +_GETDATACONTRACTRESPONSE.fields_by_name['metadata'].message_type = _RESPONSEMETADATA +_GETDOCUMENTSREQUEST.oneofs_by_name['start'].fields.append( + _GETDOCUMENTSREQUEST.fields_by_name['start_after']) +_GETDOCUMENTSREQUEST.fields_by_name['start_after'].containing_oneof = _GETDOCUMENTSREQUEST.oneofs_by_name['start'] +_GETDOCUMENTSREQUEST.oneofs_by_name['start'].fields.append( + _GETDOCUMENTSREQUEST.fields_by_name['start_at']) +_GETDOCUMENTSREQUEST.fields_by_name['start_at'].containing_oneof = _GETDOCUMENTSREQUEST.oneofs_by_name['start'] +_GETDOCUMENTSRESPONSE.fields_by_name['proof'].message_type = _PROOF +_GETDOCUMENTSRESPONSE.fields_by_name['metadata'].message_type = _RESPONSEMETADATA +_GETIDENTITIESBYPUBLICKEYHASHESRESPONSE.fields_by_name['proof'].message_type = _PROOF +_GETIDENTITIESBYPUBLICKEYHASHESRESPONSE.fields_by_name['metadata'].message_type = _RESPONSEMETADATA +_WAITFORSTATETRANSITIONRESULTRESPONSE.fields_by_name['error'].message_type = _STATETRANSITIONBROADCASTERROR +_WAITFORSTATETRANSITIONRESULTRESPONSE.fields_by_name['proof'].message_type = _PROOF +_WAITFORSTATETRANSITIONRESULTRESPONSE.fields_by_name['metadata'].message_type = _RESPONSEMETADATA +_WAITFORSTATETRANSITIONRESULTRESPONSE.oneofs_by_name['responses'].fields.append( + _WAITFORSTATETRANSITIONRESULTRESPONSE.fields_by_name['error']) +_WAITFORSTATETRANSITIONRESULTRESPONSE.fields_by_name['error'].containing_oneof = _WAITFORSTATETRANSITIONRESULTRESPONSE.oneofs_by_name['responses'] +_WAITFORSTATETRANSITIONRESULTRESPONSE.oneofs_by_name['responses'].fields.append( + _WAITFORSTATETRANSITIONRESULTRESPONSE.fields_by_name['proof']) +_WAITFORSTATETRANSITIONRESULTRESPONSE.fields_by_name['proof'].containing_oneof = _WAITFORSTATETRANSITIONRESULTRESPONSE.oneofs_by_name['responses'] +_GETCONSENSUSPARAMSRESPONSE.fields_by_name['block'].message_type = _CONSENSUSPARAMSBLOCK +_GETCONSENSUSPARAMSRESPONSE.fields_by_name['evidence'].message_type = _CONSENSUSPARAMSEVIDENCE +DESCRIPTOR.message_types_by_name['Proof'] = _PROOF +DESCRIPTOR.message_types_by_name['ResponseMetadata'] = _RESPONSEMETADATA +DESCRIPTOR.message_types_by_name['StateTransitionBroadcastError'] = _STATETRANSITIONBROADCASTERROR +DESCRIPTOR.message_types_by_name['BroadcastStateTransitionRequest'] = _BROADCASTSTATETRANSITIONREQUEST +DESCRIPTOR.message_types_by_name['BroadcastStateTransitionResponse'] = _BROADCASTSTATETRANSITIONRESPONSE +DESCRIPTOR.message_types_by_name['GetIdentityRequest'] = _GETIDENTITYREQUEST +DESCRIPTOR.message_types_by_name['GetIdentityResponse'] = _GETIDENTITYRESPONSE +DESCRIPTOR.message_types_by_name['GetDataContractRequest'] = _GETDATACONTRACTREQUEST +DESCRIPTOR.message_types_by_name['GetDataContractResponse'] = _GETDATACONTRACTRESPONSE +DESCRIPTOR.message_types_by_name['GetDocumentsRequest'] = _GETDOCUMENTSREQUEST +DESCRIPTOR.message_types_by_name['GetDocumentsResponse'] = _GETDOCUMENTSRESPONSE +DESCRIPTOR.message_types_by_name['GetIdentitiesByPublicKeyHashesRequest'] = _GETIDENTITIESBYPUBLICKEYHASHESREQUEST +DESCRIPTOR.message_types_by_name['GetIdentitiesByPublicKeyHashesResponse'] = _GETIDENTITIESBYPUBLICKEYHASHESRESPONSE +DESCRIPTOR.message_types_by_name['WaitForStateTransitionResultRequest'] = _WAITFORSTATETRANSITIONRESULTREQUEST +DESCRIPTOR.message_types_by_name['WaitForStateTransitionResultResponse'] = _WAITFORSTATETRANSITIONRESULTRESPONSE +DESCRIPTOR.message_types_by_name['ConsensusParamsBlock'] = _CONSENSUSPARAMSBLOCK +DESCRIPTOR.message_types_by_name['ConsensusParamsEvidence'] = _CONSENSUSPARAMSEVIDENCE +DESCRIPTOR.message_types_by_name['GetConsensusParamsRequest'] = _GETCONSENSUSPARAMSREQUEST +DESCRIPTOR.message_types_by_name['GetConsensusParamsResponse'] = _GETCONSENSUSPARAMSRESPONSE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Proof = _reflection.GeneratedProtocolMessageType('Proof', (_message.Message,), { + 'DESCRIPTOR' : _PROOF, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.Proof) + }) +_sym_db.RegisterMessage(Proof) + +ResponseMetadata = _reflection.GeneratedProtocolMessageType('ResponseMetadata', (_message.Message,), { + 'DESCRIPTOR' : _RESPONSEMETADATA, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.ResponseMetadata) + }) +_sym_db.RegisterMessage(ResponseMetadata) + +StateTransitionBroadcastError = _reflection.GeneratedProtocolMessageType('StateTransitionBroadcastError', (_message.Message,), { + 'DESCRIPTOR' : _STATETRANSITIONBROADCASTERROR, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.StateTransitionBroadcastError) + }) +_sym_db.RegisterMessage(StateTransitionBroadcastError) + +BroadcastStateTransitionRequest = _reflection.GeneratedProtocolMessageType('BroadcastStateTransitionRequest', (_message.Message,), { + 'DESCRIPTOR' : _BROADCASTSTATETRANSITIONREQUEST, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.BroadcastStateTransitionRequest) + }) +_sym_db.RegisterMessage(BroadcastStateTransitionRequest) + +BroadcastStateTransitionResponse = _reflection.GeneratedProtocolMessageType('BroadcastStateTransitionResponse', (_message.Message,), { + 'DESCRIPTOR' : _BROADCASTSTATETRANSITIONRESPONSE, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.BroadcastStateTransitionResponse) + }) +_sym_db.RegisterMessage(BroadcastStateTransitionResponse) + +GetIdentityRequest = _reflection.GeneratedProtocolMessageType('GetIdentityRequest', (_message.Message,), { + 'DESCRIPTOR' : _GETIDENTITYREQUEST, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetIdentityRequest) + }) +_sym_db.RegisterMessage(GetIdentityRequest) + +GetIdentityResponse = _reflection.GeneratedProtocolMessageType('GetIdentityResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETIDENTITYRESPONSE, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetIdentityResponse) + }) +_sym_db.RegisterMessage(GetIdentityResponse) + +GetDataContractRequest = _reflection.GeneratedProtocolMessageType('GetDataContractRequest', (_message.Message,), { + 'DESCRIPTOR' : _GETDATACONTRACTREQUEST, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetDataContractRequest) + }) +_sym_db.RegisterMessage(GetDataContractRequest) + +GetDataContractResponse = _reflection.GeneratedProtocolMessageType('GetDataContractResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETDATACONTRACTRESPONSE, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetDataContractResponse) + }) +_sym_db.RegisterMessage(GetDataContractResponse) + +GetDocumentsRequest = _reflection.GeneratedProtocolMessageType('GetDocumentsRequest', (_message.Message,), { + 'DESCRIPTOR' : _GETDOCUMENTSREQUEST, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetDocumentsRequest) + }) +_sym_db.RegisterMessage(GetDocumentsRequest) + +GetDocumentsResponse = _reflection.GeneratedProtocolMessageType('GetDocumentsResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETDOCUMENTSRESPONSE, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetDocumentsResponse) + }) +_sym_db.RegisterMessage(GetDocumentsResponse) + +GetIdentitiesByPublicKeyHashesRequest = _reflection.GeneratedProtocolMessageType('GetIdentitiesByPublicKeyHashesRequest', (_message.Message,), { + 'DESCRIPTOR' : _GETIDENTITIESBYPUBLICKEYHASHESREQUEST, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest) + }) +_sym_db.RegisterMessage(GetIdentitiesByPublicKeyHashesRequest) + +GetIdentitiesByPublicKeyHashesResponse = _reflection.GeneratedProtocolMessageType('GetIdentitiesByPublicKeyHashesResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETIDENTITIESBYPUBLICKEYHASHESRESPONSE, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse) + }) +_sym_db.RegisterMessage(GetIdentitiesByPublicKeyHashesResponse) + +WaitForStateTransitionResultRequest = _reflection.GeneratedProtocolMessageType('WaitForStateTransitionResultRequest', (_message.Message,), { + 'DESCRIPTOR' : _WAITFORSTATETRANSITIONRESULTREQUEST, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest) + }) +_sym_db.RegisterMessage(WaitForStateTransitionResultRequest) + +WaitForStateTransitionResultResponse = _reflection.GeneratedProtocolMessageType('WaitForStateTransitionResultResponse', (_message.Message,), { + 'DESCRIPTOR' : _WAITFORSTATETRANSITIONRESULTRESPONSE, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse) + }) +_sym_db.RegisterMessage(WaitForStateTransitionResultResponse) + +ConsensusParamsBlock = _reflection.GeneratedProtocolMessageType('ConsensusParamsBlock', (_message.Message,), { + 'DESCRIPTOR' : _CONSENSUSPARAMSBLOCK, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.ConsensusParamsBlock) + }) +_sym_db.RegisterMessage(ConsensusParamsBlock) + +ConsensusParamsEvidence = _reflection.GeneratedProtocolMessageType('ConsensusParamsEvidence', (_message.Message,), { + 'DESCRIPTOR' : _CONSENSUSPARAMSEVIDENCE, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.ConsensusParamsEvidence) + }) +_sym_db.RegisterMessage(ConsensusParamsEvidence) + +GetConsensusParamsRequest = _reflection.GeneratedProtocolMessageType('GetConsensusParamsRequest', (_message.Message,), { + 'DESCRIPTOR' : _GETCONSENSUSPARAMSREQUEST, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetConsensusParamsRequest) + }) +_sym_db.RegisterMessage(GetConsensusParamsRequest) + +GetConsensusParamsResponse = _reflection.GeneratedProtocolMessageType('GetConsensusParamsResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETCONSENSUSPARAMSRESPONSE, + '__module__' : 'platform_pb2' + # @@protoc_insertion_point(class_scope:org.dash.platform.dapi.v0.GetConsensusParamsResponse) + }) +_sym_db.RegisterMessage(GetConsensusParamsResponse) + + + +_PLATFORM = _descriptor.ServiceDescriptor( + name='Platform', + full_name='org.dash.platform.dapi.v0.Platform', + file=DESCRIPTOR, + index=0, + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_start=2126, + serialized_end=3093, + methods=[ + _descriptor.MethodDescriptor( + name='broadcastStateTransition', + full_name='org.dash.platform.dapi.v0.Platform.broadcastStateTransition', + index=0, + containing_service=None, + input_type=_BROADCASTSTATETRANSITIONREQUEST, + output_type=_BROADCASTSTATETRANSITIONRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getIdentity', + full_name='org.dash.platform.dapi.v0.Platform.getIdentity', + index=1, + containing_service=None, + input_type=_GETIDENTITYREQUEST, + output_type=_GETIDENTITYRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getDataContract', + full_name='org.dash.platform.dapi.v0.Platform.getDataContract', + index=2, + containing_service=None, + input_type=_GETDATACONTRACTREQUEST, + output_type=_GETDATACONTRACTRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getDocuments', + full_name='org.dash.platform.dapi.v0.Platform.getDocuments', + index=3, + containing_service=None, + input_type=_GETDOCUMENTSREQUEST, + output_type=_GETDOCUMENTSRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getIdentitiesByPublicKeyHashes', + full_name='org.dash.platform.dapi.v0.Platform.getIdentitiesByPublicKeyHashes', + index=4, + containing_service=None, + input_type=_GETIDENTITIESBYPUBLICKEYHASHESREQUEST, + output_type=_GETIDENTITIESBYPUBLICKEYHASHESRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='waitForStateTransitionResult', + full_name='org.dash.platform.dapi.v0.Platform.waitForStateTransitionResult', + index=5, + containing_service=None, + input_type=_WAITFORSTATETRANSITIONRESULTREQUEST, + output_type=_WAITFORSTATETRANSITIONRESULTRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getConsensusParams', + full_name='org.dash.platform.dapi.v0.Platform.getConsensusParams', + index=6, + containing_service=None, + input_type=_GETCONSENSUSPARAMSREQUEST, + output_type=_GETCONSENSUSPARAMSRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), +]) +_sym_db.RegisterServiceDescriptor(_PLATFORM) + +DESCRIPTOR.services_by_name['Platform'] = _PLATFORM + +# @@protoc_insertion_point(module_scope) diff --git a/packages/dapi-grpc/clients/platform/v0/python/platform_pb2_grpc.py b/packages/dapi-grpc/clients/platform/v0/python/platform_pb2_grpc.py new file mode 100644 index 00000000000..152a69dc73a --- /dev/null +++ b/packages/dapi-grpc/clients/platform/v0/python/platform_pb2_grpc.py @@ -0,0 +1,264 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +import platform_pb2 as platform__pb2 + + +class PlatformStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.broadcastStateTransition = channel.unary_unary( + '/org.dash.platform.dapi.v0.Platform/broadcastStateTransition', + request_serializer=platform__pb2.BroadcastStateTransitionRequest.SerializeToString, + response_deserializer=platform__pb2.BroadcastStateTransitionResponse.FromString, + ) + self.getIdentity = channel.unary_unary( + '/org.dash.platform.dapi.v0.Platform/getIdentity', + request_serializer=platform__pb2.GetIdentityRequest.SerializeToString, + response_deserializer=platform__pb2.GetIdentityResponse.FromString, + ) + self.getDataContract = channel.unary_unary( + '/org.dash.platform.dapi.v0.Platform/getDataContract', + request_serializer=platform__pb2.GetDataContractRequest.SerializeToString, + response_deserializer=platform__pb2.GetDataContractResponse.FromString, + ) + self.getDocuments = channel.unary_unary( + '/org.dash.platform.dapi.v0.Platform/getDocuments', + request_serializer=platform__pb2.GetDocumentsRequest.SerializeToString, + response_deserializer=platform__pb2.GetDocumentsResponse.FromString, + ) + self.getIdentitiesByPublicKeyHashes = channel.unary_unary( + '/org.dash.platform.dapi.v0.Platform/getIdentitiesByPublicKeyHashes', + request_serializer=platform__pb2.GetIdentitiesByPublicKeyHashesRequest.SerializeToString, + response_deserializer=platform__pb2.GetIdentitiesByPublicKeyHashesResponse.FromString, + ) + self.waitForStateTransitionResult = channel.unary_unary( + '/org.dash.platform.dapi.v0.Platform/waitForStateTransitionResult', + request_serializer=platform__pb2.WaitForStateTransitionResultRequest.SerializeToString, + response_deserializer=platform__pb2.WaitForStateTransitionResultResponse.FromString, + ) + self.getConsensusParams = channel.unary_unary( + '/org.dash.platform.dapi.v0.Platform/getConsensusParams', + request_serializer=platform__pb2.GetConsensusParamsRequest.SerializeToString, + response_deserializer=platform__pb2.GetConsensusParamsResponse.FromString, + ) + + +class PlatformServicer(object): + """Missing associated documentation comment in .proto file.""" + + def broadcastStateTransition(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def getIdentity(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def getDataContract(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def getDocuments(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def getIdentitiesByPublicKeyHashes(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def waitForStateTransitionResult(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def getConsensusParams(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_PlatformServicer_to_server(servicer, server): + rpc_method_handlers = { + 'broadcastStateTransition': grpc.unary_unary_rpc_method_handler( + servicer.broadcastStateTransition, + request_deserializer=platform__pb2.BroadcastStateTransitionRequest.FromString, + response_serializer=platform__pb2.BroadcastStateTransitionResponse.SerializeToString, + ), + 'getIdentity': grpc.unary_unary_rpc_method_handler( + servicer.getIdentity, + request_deserializer=platform__pb2.GetIdentityRequest.FromString, + response_serializer=platform__pb2.GetIdentityResponse.SerializeToString, + ), + 'getDataContract': grpc.unary_unary_rpc_method_handler( + servicer.getDataContract, + request_deserializer=platform__pb2.GetDataContractRequest.FromString, + response_serializer=platform__pb2.GetDataContractResponse.SerializeToString, + ), + 'getDocuments': grpc.unary_unary_rpc_method_handler( + servicer.getDocuments, + request_deserializer=platform__pb2.GetDocumentsRequest.FromString, + response_serializer=platform__pb2.GetDocumentsResponse.SerializeToString, + ), + 'getIdentitiesByPublicKeyHashes': grpc.unary_unary_rpc_method_handler( + servicer.getIdentitiesByPublicKeyHashes, + request_deserializer=platform__pb2.GetIdentitiesByPublicKeyHashesRequest.FromString, + response_serializer=platform__pb2.GetIdentitiesByPublicKeyHashesResponse.SerializeToString, + ), + 'waitForStateTransitionResult': grpc.unary_unary_rpc_method_handler( + servicer.waitForStateTransitionResult, + request_deserializer=platform__pb2.WaitForStateTransitionResultRequest.FromString, + response_serializer=platform__pb2.WaitForStateTransitionResultResponse.SerializeToString, + ), + 'getConsensusParams': grpc.unary_unary_rpc_method_handler( + servicer.getConsensusParams, + request_deserializer=platform__pb2.GetConsensusParamsRequest.FromString, + response_serializer=platform__pb2.GetConsensusParamsResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'org.dash.platform.dapi.v0.Platform', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class Platform(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def broadcastStateTransition(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/org.dash.platform.dapi.v0.Platform/broadcastStateTransition', + platform__pb2.BroadcastStateTransitionRequest.SerializeToString, + platform__pb2.BroadcastStateTransitionResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def getIdentity(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/org.dash.platform.dapi.v0.Platform/getIdentity', + platform__pb2.GetIdentityRequest.SerializeToString, + platform__pb2.GetIdentityResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def getDataContract(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/org.dash.platform.dapi.v0.Platform/getDataContract', + platform__pb2.GetDataContractRequest.SerializeToString, + platform__pb2.GetDataContractResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def getDocuments(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/org.dash.platform.dapi.v0.Platform/getDocuments', + platform__pb2.GetDocumentsRequest.SerializeToString, + platform__pb2.GetDocumentsResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def getIdentitiesByPublicKeyHashes(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/org.dash.platform.dapi.v0.Platform/getIdentitiesByPublicKeyHashes', + platform__pb2.GetIdentitiesByPublicKeyHashesRequest.SerializeToString, + platform__pb2.GetIdentitiesByPublicKeyHashesResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def waitForStateTransitionResult(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/org.dash.platform.dapi.v0.Platform/waitForStateTransitionResult', + platform__pb2.WaitForStateTransitionResultRequest.SerializeToString, + platform__pb2.WaitForStateTransitionResultResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def getConsensusParams(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/org.dash.platform.dapi.v0.Platform/getConsensusParams', + platform__pb2.GetConsensusParamsRequest.SerializeToString, + platform__pb2.GetConsensusParamsResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/packages/dapi-grpc/clients/platform/v0/web/README.md b/packages/dapi-grpc/clients/platform/v0/web/README.md new file mode 100644 index 00000000000..ddc53757beb --- /dev/null +++ b/packages/dapi-grpc/clients/platform/v0/web/README.md @@ -0,0 +1,9 @@ +# GRPC-Web client + +This is a directory for the generated GRPC-Web client. + +## Build + +```bash +npm run build +``` diff --git a/packages/dapi-grpc/clients/platform/v0/web/platform_grpc_web_pb.js b/packages/dapi-grpc/clients/platform/v0/web/platform_grpc_web_pb.js new file mode 100644 index 00000000000..86d6e9a9b06 --- /dev/null +++ b/packages/dapi-grpc/clients/platform/v0/web/platform_grpc_web_pb.js @@ -0,0 +1,505 @@ +/** + * @fileoverview gRPC-Web generated client stub for org.dash.platform.dapi.v0 + * @enhanceable + * @public + */ + +// GENERATED CODE -- DO NOT EDIT! + + +/* eslint-disable */ +// @ts-nocheck + + + +const grpc = {}; +grpc.web = require('grpc-web'); + +const proto = {}; +proto.org = {}; +proto.org.dash = {}; +proto.org.dash.platform = {}; +proto.org.dash.platform.dapi = {}; +proto.org.dash.platform.dapi.v0 = require('./platform_pb.js'); + +/** + * @param {string} hostname + * @param {?Object} credentials + * @param {?grpc.web.ClientOptions} options + * @constructor + * @struct + * @final + */ +proto.org.dash.platform.dapi.v0.PlatformClient = + function(hostname, credentials, options) { + if (!options) options = {}; + options.format = 'text'; + + /** + * @private @const {!grpc.web.GrpcWebClientBase} The client + */ + this.client_ = new grpc.web.GrpcWebClientBase(options); + + /** + * @private @const {string} The hostname + */ + this.hostname_ = hostname; + +}; + + +/** + * @param {string} hostname + * @param {?Object} credentials + * @param {?grpc.web.ClientOptions} options + * @constructor + * @struct + * @final + */ +proto.org.dash.platform.dapi.v0.PlatformPromiseClient = + function(hostname, credentials, options) { + if (!options) options = {}; + options.format = 'text'; + + /** + * @private @const {!grpc.web.GrpcWebClientBase} The client + */ + this.client_ = new grpc.web.GrpcWebClientBase(options); + + /** + * @private @const {string} The hostname + */ + this.hostname_ = hostname; + +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest, + * !proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse>} + */ +const methodDescriptor_Platform_broadcastStateTransition = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Platform/broadcastStateTransition', + grpc.web.MethodType.UNARY, + proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest, + proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.PlatformClient.prototype.broadcastStateTransition = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/broadcastStateTransition', + request, + metadata || {}, + methodDescriptor_Platform_broadcastStateTransition, + callback); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.org.dash.platform.dapi.v0.PlatformPromiseClient.prototype.broadcastStateTransition = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/broadcastStateTransition', + request, + metadata || {}, + methodDescriptor_Platform_broadcastStateTransition); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.GetIdentityRequest, + * !proto.org.dash.platform.dapi.v0.GetIdentityResponse>} + */ +const methodDescriptor_Platform_getIdentity = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Platform/getIdentity', + grpc.web.MethodType.UNARY, + proto.org.dash.platform.dapi.v0.GetIdentityRequest, + proto.org.dash.platform.dapi.v0.GetIdentityResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.GetIdentityResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.org.dash.platform.dapi.v0.GetIdentityResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.PlatformClient.prototype.getIdentity = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/getIdentity', + request, + metadata || {}, + methodDescriptor_Platform_getIdentity, + callback); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.org.dash.platform.dapi.v0.PlatformPromiseClient.prototype.getIdentity = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/getIdentity', + request, + metadata || {}, + methodDescriptor_Platform_getIdentity); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.GetDataContractRequest, + * !proto.org.dash.platform.dapi.v0.GetDataContractResponse>} + */ +const methodDescriptor_Platform_getDataContract = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Platform/getDataContract', + grpc.web.MethodType.UNARY, + proto.org.dash.platform.dapi.v0.GetDataContractRequest, + proto.org.dash.platform.dapi.v0.GetDataContractResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.GetDataContractResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.org.dash.platform.dapi.v0.GetDataContractResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.PlatformClient.prototype.getDataContract = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/getDataContract', + request, + metadata || {}, + methodDescriptor_Platform_getDataContract, + callback); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.org.dash.platform.dapi.v0.PlatformPromiseClient.prototype.getDataContract = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/getDataContract', + request, + metadata || {}, + methodDescriptor_Platform_getDataContract); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.GetDocumentsRequest, + * !proto.org.dash.platform.dapi.v0.GetDocumentsResponse>} + */ +const methodDescriptor_Platform_getDocuments = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Platform/getDocuments', + grpc.web.MethodType.UNARY, + proto.org.dash.platform.dapi.v0.GetDocumentsRequest, + proto.org.dash.platform.dapi.v0.GetDocumentsResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.GetDocumentsResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.org.dash.platform.dapi.v0.GetDocumentsResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.PlatformClient.prototype.getDocuments = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/getDocuments', + request, + metadata || {}, + methodDescriptor_Platform_getDocuments, + callback); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.org.dash.platform.dapi.v0.PlatformPromiseClient.prototype.getDocuments = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/getDocuments', + request, + metadata || {}, + methodDescriptor_Platform_getDocuments); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest, + * !proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse>} + */ +const methodDescriptor_Platform_getIdentitiesByPublicKeyHashes = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Platform/getIdentitiesByPublicKeyHashes', + grpc.web.MethodType.UNARY, + proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest, + proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.PlatformClient.prototype.getIdentitiesByPublicKeyHashes = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/getIdentitiesByPublicKeyHashes', + request, + metadata || {}, + methodDescriptor_Platform_getIdentitiesByPublicKeyHashes, + callback); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.org.dash.platform.dapi.v0.PlatformPromiseClient.prototype.getIdentitiesByPublicKeyHashes = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/getIdentitiesByPublicKeyHashes', + request, + metadata || {}, + methodDescriptor_Platform_getIdentitiesByPublicKeyHashes); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest, + * !proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse>} + */ +const methodDescriptor_Platform_waitForStateTransitionResult = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Platform/waitForStateTransitionResult', + grpc.web.MethodType.UNARY, + proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest, + proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.PlatformClient.prototype.waitForStateTransitionResult = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/waitForStateTransitionResult', + request, + metadata || {}, + methodDescriptor_Platform_waitForStateTransitionResult, + callback); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.org.dash.platform.dapi.v0.PlatformPromiseClient.prototype.waitForStateTransitionResult = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/waitForStateTransitionResult', + request, + metadata || {}, + methodDescriptor_Platform_waitForStateTransitionResult); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest, + * !proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse>} + */ +const methodDescriptor_Platform_getConsensusParams = new grpc.web.MethodDescriptor( + '/org.dash.platform.dapi.v0.Platform/getConsensusParams', + grpc.web.MethodType.UNARY, + proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest, + proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse, + /** + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.deserializeBinary +); + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.org.dash.platform.dapi.v0.PlatformClient.prototype.getConsensusParams = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/getConsensusParams', + request, + metadata || {}, + methodDescriptor_Platform_getConsensusParams, + callback); +}; + + +/** + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.org.dash.platform.dapi.v0.PlatformPromiseClient.prototype.getConsensusParams = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/org.dash.platform.dapi.v0.Platform/getConsensusParams', + request, + metadata || {}, + methodDescriptor_Platform_getConsensusParams); +}; + + +module.exports = proto.org.dash.platform.dapi.v0; + diff --git a/packages/dapi-grpc/clients/platform/v0/web/platform_pb.js b/packages/dapi-grpc/clients/platform/v0/web/platform_pb.js new file mode 100644 index 00000000000..5f738ea47ca --- /dev/null +++ b/packages/dapi-grpc/clients/platform/v0/web/platform_pb.js @@ -0,0 +1,4716 @@ +// source: platform.proto +/** + * @fileoverview + * @enhanceable + * @suppress {missingRequire} reports error on implicit type usages. + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +// @ts-nocheck + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = (function() { + if (this) { return this; } + if (typeof window !== 'undefined') { return window; } + if (typeof global !== 'undefined') { return global; } + if (typeof self !== 'undefined') { return self; } + return Function('return this')(); +}.call(null)); + +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.ConsensusParamsBlock', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetDataContractRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetDataContractResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetDocumentsRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetDocumentsRequest.StartCase', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetDocumentsResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetIdentityRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.GetIdentityResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.Proof', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.ResponseMetadata', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse', null, global); +goog.exportSymbol('proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.ResponsesCase', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.Proof = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.Proof, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.Proof.displayName = 'proto.org.dash.platform.dapi.v0.Proof'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.ResponseMetadata, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.ResponseMetadata.displayName = 'proto.org.dash.platform.dapi.v0.ResponseMetadata'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.displayName = 'proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.displayName = 'proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.displayName = 'proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetIdentityRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetIdentityRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetIdentityRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetIdentityResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetIdentityResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetIdentityResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetDataContractRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetDataContractRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetDataContractRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetDataContractResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetDataContractResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetDataContractResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetDocumentsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetDocumentsRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetDocumentsRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.org.dash.platform.dapi.v0.GetDocumentsResponse.repeatedFields_, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetDocumentsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetDocumentsResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetDocumentsResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.repeatedFields_, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.repeatedFields_, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.displayName = 'proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.oneofGroups_); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.displayName = 'proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.ConsensusParamsBlock, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.displayName = 'proto.org.dash.platform.dapi.v0.ConsensusParamsBlock'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.displayName = 'proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.displayName = 'proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.displayName = 'proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse'; +} + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.Proof.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.Proof} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.Proof.toObject = function(includeInstance, msg) { + var f, obj = { + merkleProof: msg.getMerkleProof_asB64(), + signatureLlmqHash: msg.getSignatureLlmqHash_asB64(), + signature: msg.getSignature_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.Proof.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.Proof; + return proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.Proof} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setMerkleProof(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSignatureLlmqHash(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSignature(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.Proof} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getMerkleProof_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getSignatureLlmqHash_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 3, + f + ); + } +}; + + +/** + * optional bytes merkle_proof = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getMerkleProof = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes merkle_proof = 1; + * This is a type-conversion wrapper around `getMerkleProof()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getMerkleProof_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getMerkleProof())); +}; + + +/** + * optional bytes merkle_proof = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getMerkleProof()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getMerkleProof_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getMerkleProof())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.Proof} returns this + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.setMerkleProof = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bytes signature_llmq_hash = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getSignatureLlmqHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes signature_llmq_hash = 2; + * This is a type-conversion wrapper around `getSignatureLlmqHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getSignatureLlmqHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSignatureLlmqHash())); +}; + + +/** + * optional bytes signature_llmq_hash = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSignatureLlmqHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getSignatureLlmqHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSignatureLlmqHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.Proof} returns this + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.setSignatureLlmqHash = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional bytes signature = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getSignature = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes signature = 3; + * This is a type-conversion wrapper around `getSignature()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSignature())); +}; + + +/** + * optional bytes signature = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSignature()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.getSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.Proof} returns this + */ +proto.org.dash.platform.dapi.v0.Proof.prototype.setSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.ResponseMetadata} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject = function(includeInstance, msg) { + var f, obj = { + height: jspb.Message.getFieldWithDefault(msg, 1, 0), + coreChainLockedHeight: jspb.Message.getFieldWithDefault(msg, 2, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.ResponseMetadata; + return proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.ResponseMetadata} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readInt64()); + msg.setHeight(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint32()); + msg.setCoreChainLockedHeight(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.ResponseMetadata} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getHeight(); + if (f !== 0) { + writer.writeInt64( + 1, + f + ); + } + f = message.getCoreChainLockedHeight(); + if (f !== 0) { + writer.writeUint32( + 2, + f + ); + } +}; + + +/** + * optional int64 height = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.prototype.getHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.ResponseMetadata} returns this + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.prototype.setHeight = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional uint32 core_chain_locked_height = 2; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.prototype.getCoreChainLockedHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.ResponseMetadata} returns this + */ +proto.org.dash.platform.dapi.v0.ResponseMetadata.prototype.setCoreChainLockedHeight = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.toObject = function(includeInstance, msg) { + var f, obj = { + code: jspb.Message.getFieldWithDefault(msg, 1, 0), + message: jspb.Message.getFieldWithDefault(msg, 2, ""), + data: msg.getData_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError; + return proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setCode(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setMessage(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setData(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getCode(); + if (f !== 0) { + writer.writeUint32( + 1, + f + ); + } + f = message.getMessage(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getData_asU8(); + if (f.length > 0) { + writer.writeBytes( + 3, + f + ); + } +}; + + +/** + * optional uint32 code = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.getCode = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} returns this + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.setCode = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional string message = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.getMessage = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} returns this + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.setMessage = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional bytes data = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.getData = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes data = 3; + * This is a type-conversion wrapper around `getData()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.getData_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getData())); +}; + + +/** + * optional bytes data = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getData()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.getData_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getData())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} returns this + */ +proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.prototype.setData = function(value) { + return jspb.Message.setProto3BytesField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.toObject = function(includeInstance, msg) { + var f, obj = { + stateTransition: msg.getStateTransition_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest; + return proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setStateTransition(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getStateTransition_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } +}; + + +/** + * optional bytes state_transition = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.prototype.getStateTransition = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes state_transition = 1; + * This is a type-conversion wrapper around `getStateTransition()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.prototype.getStateTransition_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getStateTransition())); +}; + + +/** + * optional bytes state_transition = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getStateTransition()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.prototype.getStateTransition_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getStateTransition())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest} returns this + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest.prototype.setStateTransition = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.toObject = function(includeInstance, msg) { + var f, obj = { + + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse; + return proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetIdentityRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.toObject = function(includeInstance, msg) { + var f, obj = { + id: msg.getId_asB64(), + prove: jspb.Message.getBooleanFieldWithDefault(msg, 2, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetIdentityRequest; + return proto.org.dash.platform.dapi.v0.GetIdentityRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setId(value); + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setProve(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetIdentityRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getId_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getProve(); + if (f) { + writer.writeBool( + 2, + f + ); + } +}; + + +/** + * optional bytes id = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.getId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes id = 1; + * This is a type-conversion wrapper around `getId()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.getId_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getId())); +}; + + +/** + * optional bytes id = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getId()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.getId_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getId())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.setId = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bool prove = 2; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.getProve = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentityRequest.prototype.setProve = function(value) { + return jspb.Message.setProto3BooleanField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetIdentityResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.toObject = function(includeInstance, msg) { + var f, obj = { + identity: msg.getIdentity_asB64(), + proof: (f = msg.getProof()) && proto.org.dash.platform.dapi.v0.Proof.toObject(includeInstance, f), + metadata: (f = msg.getMetadata()) && proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetIdentityResponse; + return proto.org.dash.platform.dapi.v0.GetIdentityResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setIdentity(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.Proof; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader); + msg.setProof(value); + break; + case 3: + var value = new proto.org.dash.platform.dapi.v0.ResponseMetadata; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader); + msg.setMetadata(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetIdentityResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getIdentity_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getProof(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter + ); + } + f = message.getMetadata(); + if (f != null) { + writer.writeMessage( + 3, + f, + proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter + ); + } +}; + + +/** + * optional bytes identity = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.getIdentity = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes identity = 1; + * This is a type-conversion wrapper around `getIdentity()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.getIdentity_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getIdentity())); +}; + + +/** + * optional bytes identity = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getIdentity()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.getIdentity_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getIdentity())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.setIdentity = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional Proof proof = 2; + * @return {?proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.getProof = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.Proof} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.Proof, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.Proof|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.setProof = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.clearProof = function() { + return this.setProof(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.hasProof = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional ResponseMetadata metadata = 3; + * @return {?proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.getMetadata = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ResponseMetadata} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ResponseMetadata, 3)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ResponseMetadata|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.setMetadata = function(value) { + return jspb.Message.setWrapperField(this, 3, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentityResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.clearMetadata = function() { + return this.setMetadata(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetIdentityResponse.prototype.hasMetadata = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetDataContractRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.toObject = function(includeInstance, msg) { + var f, obj = { + id: msg.getId_asB64(), + prove: jspb.Message.getBooleanFieldWithDefault(msg, 2, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetDataContractRequest; + return proto.org.dash.platform.dapi.v0.GetDataContractRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setId(value); + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setProve(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetDataContractRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getId_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getProve(); + if (f) { + writer.writeBool( + 2, + f + ); + } +}; + + +/** + * optional bytes id = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.getId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes id = 1; + * This is a type-conversion wrapper around `getId()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.getId_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getId())); +}; + + +/** + * optional bytes id = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getId()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.getId_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getId())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.setId = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bool prove = 2; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.getProve = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDataContractRequest.prototype.setProve = function(value) { + return jspb.Message.setProto3BooleanField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetDataContractResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.toObject = function(includeInstance, msg) { + var f, obj = { + dataContract: msg.getDataContract_asB64(), + proof: (f = msg.getProof()) && proto.org.dash.platform.dapi.v0.Proof.toObject(includeInstance, f), + metadata: (f = msg.getMetadata()) && proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetDataContractResponse; + return proto.org.dash.platform.dapi.v0.GetDataContractResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setDataContract(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.Proof; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader); + msg.setProof(value); + break; + case 3: + var value = new proto.org.dash.platform.dapi.v0.ResponseMetadata; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader); + msg.setMetadata(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetDataContractResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getDataContract_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getProof(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter + ); + } + f = message.getMetadata(); + if (f != null) { + writer.writeMessage( + 3, + f, + proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter + ); + } +}; + + +/** + * optional bytes data_contract = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.getDataContract = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes data_contract = 1; + * This is a type-conversion wrapper around `getDataContract()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.getDataContract_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getDataContract())); +}; + + +/** + * optional bytes data_contract = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getDataContract()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.getDataContract_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getDataContract())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.setDataContract = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional Proof proof = 2; + * @return {?proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.getProof = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.Proof} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.Proof, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.Proof|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.setProof = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.clearProof = function() { + return this.setProof(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.hasProof = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional ResponseMetadata metadata = 3; + * @return {?proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.getMetadata = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ResponseMetadata} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ResponseMetadata, 3)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ResponseMetadata|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.setMetadata = function(value) { + return jspb.Message.setWrapperField(this, 3, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDataContractResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.clearMetadata = function() { + return this.setMetadata(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDataContractResponse.prototype.hasMetadata = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_ = [[6,7]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.StartCase = { + START_NOT_SET: 0, + START_AFTER: 6, + START_AT: 7 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.GetDocumentsRequest.StartCase} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.GetDocumentsRequest.StartCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetDocumentsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + dataContractId: msg.getDataContractId_asB64(), + documentType: jspb.Message.getFieldWithDefault(msg, 2, ""), + where: msg.getWhere_asB64(), + orderBy: msg.getOrderBy_asB64(), + limit: jspb.Message.getFieldWithDefault(msg, 5, 0), + startAfter: msg.getStartAfter_asB64(), + startAt: msg.getStartAt_asB64(), + prove: jspb.Message.getBooleanFieldWithDefault(msg, 8, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetDocumentsRequest; + return proto.org.dash.platform.dapi.v0.GetDocumentsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setDataContractId(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setDocumentType(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setWhere(value); + break; + case 4: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setOrderBy(value); + break; + case 5: + var value = /** @type {number} */ (reader.readUint32()); + msg.setLimit(value); + break; + case 6: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setStartAfter(value); + break; + case 7: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setStartAt(value); + break; + case 8: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setProve(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetDocumentsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getDataContractId_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getDocumentType(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getWhere_asU8(); + if (f.length > 0) { + writer.writeBytes( + 3, + f + ); + } + f = message.getOrderBy_asU8(); + if (f.length > 0) { + writer.writeBytes( + 4, + f + ); + } + f = message.getLimit(); + if (f !== 0) { + writer.writeUint32( + 5, + f + ); + } + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 6)); + if (f != null) { + writer.writeBytes( + 6, + f + ); + } + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 7)); + if (f != null) { + writer.writeBytes( + 7, + f + ); + } + f = message.getProve(); + if (f) { + writer.writeBool( + 8, + f + ); + } +}; + + +/** + * optional bytes data_contract_id = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getDataContractId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes data_contract_id = 1; + * This is a type-conversion wrapper around `getDataContractId()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getDataContractId_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getDataContractId())); +}; + + +/** + * optional bytes data_contract_id = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getDataContractId()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getDataContractId_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getDataContractId())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setDataContractId = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional string document_type = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getDocumentType = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setDocumentType = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional bytes where = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getWhere = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes where = 3; + * This is a type-conversion wrapper around `getWhere()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getWhere_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getWhere())); +}; + + +/** + * optional bytes where = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getWhere()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getWhere_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getWhere())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setWhere = function(value) { + return jspb.Message.setProto3BytesField(this, 3, value); +}; + + +/** + * optional bytes order_by = 4; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getOrderBy = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * optional bytes order_by = 4; + * This is a type-conversion wrapper around `getOrderBy()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getOrderBy_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getOrderBy())); +}; + + +/** + * optional bytes order_by = 4; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getOrderBy()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getOrderBy_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getOrderBy())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setOrderBy = function(value) { + return jspb.Message.setProto3BytesField(this, 4, value); +}; + + +/** + * optional uint32 limit = 5; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getLimit = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setLimit = function(value) { + return jspb.Message.setProto3IntField(this, 5, value); +}; + + +/** + * optional bytes start_after = 6; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartAfter = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, "")); +}; + + +/** + * optional bytes start_after = 6; + * This is a type-conversion wrapper around `getStartAfter()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartAfter_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getStartAfter())); +}; + + +/** + * optional bytes start_after = 6; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getStartAfter()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartAfter_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getStartAfter())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setStartAfter = function(value) { + return jspb.Message.setOneofField(this, 6, proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.clearStartAfter = function() { + return jspb.Message.setOneofField(this, 6, proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.hasStartAfter = function() { + return jspb.Message.getField(this, 6) != null; +}; + + +/** + * optional bytes start_at = 7; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartAt = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, "")); +}; + + +/** + * optional bytes start_at = 7; + * This is a type-conversion wrapper around `getStartAt()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartAt_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getStartAt())); +}; + + +/** + * optional bytes start_at = 7; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getStartAt()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getStartAt_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getStartAt())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setStartAt = function(value) { + return jspb.Message.setOneofField(this, 7, proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_[0], value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.clearStartAt = function() { + return jspb.Message.setOneofField(this, 7, proto.org.dash.platform.dapi.v0.GetDocumentsRequest.oneofGroups_[0], undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.hasStartAt = function() { + return jspb.Message.getField(this, 7) != null; +}; + + +/** + * optional bool prove = 8; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.getProve = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 8, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsRequest.prototype.setProve = function(value) { + return jspb.Message.setProto3BooleanField(this, 8, value); +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetDocumentsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + documentsList: msg.getDocumentsList_asB64(), + proof: (f = msg.getProof()) && proto.org.dash.platform.dapi.v0.Proof.toObject(includeInstance, f), + metadata: (f = msg.getMetadata()) && proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetDocumentsResponse; + return proto.org.dash.platform.dapi.v0.GetDocumentsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.addDocuments(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.Proof; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader); + msg.setProof(value); + break; + case 3: + var value = new proto.org.dash.platform.dapi.v0.ResponseMetadata; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader); + msg.setMetadata(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetDocumentsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getDocumentsList_asU8(); + if (f.length > 0) { + writer.writeRepeatedBytes( + 1, + f + ); + } + f = message.getProof(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter + ); + } + f = message.getMetadata(); + if (f != null) { + writer.writeMessage( + 3, + f, + proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter + ); + } +}; + + +/** + * repeated bytes documents = 1; + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.getDocumentsList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * repeated bytes documents = 1; + * This is a type-conversion wrapper around `getDocumentsList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.getDocumentsList_asB64 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsB64( + this.getDocumentsList())); +}; + + +/** + * repeated bytes documents = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getDocumentsList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.getDocumentsList_asU8 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsU8( + this.getDocumentsList())); +}; + + +/** + * @param {!(Array|Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.setDocumentsList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @param {number=} opt_index + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.addDocuments = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.clearDocumentsList = function() { + return this.setDocumentsList([]); +}; + + +/** + * optional Proof proof = 2; + * @return {?proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.getProof = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.Proof} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.Proof, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.Proof|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.setProof = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.clearProof = function() { + return this.setProof(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.hasProof = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional ResponseMetadata metadata = 3; + * @return {?proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.getMetadata = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ResponseMetadata} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ResponseMetadata, 3)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ResponseMetadata|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.setMetadata = function(value) { + return jspb.Message.setWrapperField(this, 3, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.clearMetadata = function() { + return this.setMetadata(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.prototype.hasMetadata = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.toObject = function(includeInstance, msg) { + var f, obj = { + publicKeyHashesList: msg.getPublicKeyHashesList_asB64(), + prove: jspb.Message.getBooleanFieldWithDefault(msg, 2, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest; + return proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.addPublicKeyHashes(value); + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setProve(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getPublicKeyHashesList_asU8(); + if (f.length > 0) { + writer.writeRepeatedBytes( + 1, + f + ); + } + f = message.getProve(); + if (f) { + writer.writeBool( + 2, + f + ); + } +}; + + +/** + * repeated bytes public_key_hashes = 1; + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.getPublicKeyHashesList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * repeated bytes public_key_hashes = 1; + * This is a type-conversion wrapper around `getPublicKeyHashesList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.getPublicKeyHashesList_asB64 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsB64( + this.getPublicKeyHashesList())); +}; + + +/** + * repeated bytes public_key_hashes = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getPublicKeyHashesList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.getPublicKeyHashesList_asU8 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsU8( + this.getPublicKeyHashesList())); +}; + + +/** + * @param {!(Array|Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.setPublicKeyHashesList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @param {number=} opt_index + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.addPublicKeyHashes = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.clearPublicKeyHashesList = function() { + return this.setPublicKeyHashesList([]); +}; + + +/** + * optional bool prove = 2; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.getProve = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesRequest.prototype.setProve = function(value) { + return jspb.Message.setProto3BooleanField(this, 2, value); +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.toObject = function(includeInstance, msg) { + var f, obj = { + identitiesList: msg.getIdentitiesList_asB64(), + proof: (f = msg.getProof()) && proto.org.dash.platform.dapi.v0.Proof.toObject(includeInstance, f), + metadata: (f = msg.getMetadata()) && proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse; + return proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.addIdentities(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.Proof; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader); + msg.setProof(value); + break; + case 3: + var value = new proto.org.dash.platform.dapi.v0.ResponseMetadata; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader); + msg.setMetadata(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getIdentitiesList_asU8(); + if (f.length > 0) { + writer.writeRepeatedBytes( + 1, + f + ); + } + f = message.getProof(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter + ); + } + f = message.getMetadata(); + if (f != null) { + writer.writeMessage( + 3, + f, + proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter + ); + } +}; + + +/** + * repeated bytes identities = 1; + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.getIdentitiesList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * repeated bytes identities = 1; + * This is a type-conversion wrapper around `getIdentitiesList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.getIdentitiesList_asB64 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsB64( + this.getIdentitiesList())); +}; + + +/** + * repeated bytes identities = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getIdentitiesList()` + * @return {!Array} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.getIdentitiesList_asU8 = function() { + return /** @type {!Array} */ (jspb.Message.bytesListAsU8( + this.getIdentitiesList())); +}; + + +/** + * @param {!(Array|Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.setIdentitiesList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @param {number=} opt_index + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.addIdentities = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.clearIdentitiesList = function() { + return this.setIdentitiesList([]); +}; + + +/** + * optional Proof proof = 2; + * @return {?proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.getProof = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.Proof} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.Proof, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.Proof|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.setProof = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.clearProof = function() { + return this.setProof(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.hasProof = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional ResponseMetadata metadata = 3; + * @return {?proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.getMetadata = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ResponseMetadata} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ResponseMetadata, 3)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ResponseMetadata|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.setMetadata = function(value) { + return jspb.Message.setWrapperField(this, 3, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.clearMetadata = function() { + return this.setMetadata(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetIdentitiesByPublicKeyHashesResponse.prototype.hasMetadata = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.toObject = function(includeInstance, msg) { + var f, obj = { + stateTransitionHash: msg.getStateTransitionHash_asB64(), + prove: jspb.Message.getBooleanFieldWithDefault(msg, 2, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest; + return proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setStateTransitionHash(value); + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setProve(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getStateTransitionHash_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getProve(); + if (f) { + writer.writeBool( + 2, + f + ); + } +}; + + +/** + * optional bytes state_transition_hash = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.getStateTransitionHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes state_transition_hash = 1; + * This is a type-conversion wrapper around `getStateTransitionHash()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.getStateTransitionHash_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getStateTransitionHash())); +}; + + +/** + * optional bytes state_transition_hash = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getStateTransitionHash()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.getStateTransitionHash_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getStateTransitionHash())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} returns this + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.setStateTransitionHash = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bool prove = 2; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.getProve = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest} returns this + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.prototype.setProve = function(value) { + return jspb.Message.setProto3BooleanField(this, 2, value); +}; + + + +/** + * Oneof group definitions for this message. Each group defines the field + * numbers belonging to that group. When of these fields' value is set, all + * other fields in the group are cleared. During deserialization, if multiple + * fields are encountered for a group, only the last value seen will be kept. + * @private {!Array>} + * @const + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.oneofGroups_ = [[1,2]]; + +/** + * @enum {number} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.ResponsesCase = { + RESPONSES_NOT_SET: 0, + ERROR: 1, + PROOF: 2 +}; + +/** + * @return {proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.ResponsesCase} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.getResponsesCase = function() { + return /** @type {proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.ResponsesCase} */(jspb.Message.computeOneofCase(this, proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.oneofGroups_[0])); +}; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.toObject = function(includeInstance, msg) { + var f, obj = { + error: (f = msg.getError()) && proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.toObject(includeInstance, f), + proof: (f = msg.getProof()) && proto.org.dash.platform.dapi.v0.Proof.toObject(includeInstance, f), + metadata: (f = msg.getMetadata()) && proto.org.dash.platform.dapi.v0.ResponseMetadata.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse; + return proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.deserializeBinaryFromReader); + msg.setError(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.Proof; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.Proof.deserializeBinaryFromReader); + msg.setProof(value); + break; + case 3: + var value = new proto.org.dash.platform.dapi.v0.ResponseMetadata; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ResponseMetadata.deserializeBinaryFromReader); + msg.setMetadata(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getError(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError.serializeBinaryToWriter + ); + } + f = message.getProof(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.Proof.serializeBinaryToWriter + ); + } + f = message.getMetadata(); + if (f != null) { + writer.writeMessage( + 3, + f, + proto.org.dash.platform.dapi.v0.ResponseMetadata.serializeBinaryToWriter + ); + } +}; + + +/** + * optional StateTransitionBroadcastError error = 1; + * @return {?proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.getError = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError, 1)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.StateTransitionBroadcastError|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.setError = function(value) { + return jspb.Message.setOneofWrapperField(this, 1, proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} returns this + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.clearError = function() { + return this.setError(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.hasError = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional Proof proof = 2; + * @return {?proto.org.dash.platform.dapi.v0.Proof} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.getProof = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.Proof} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.Proof, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.Proof|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.setProof = function(value) { + return jspb.Message.setOneofWrapperField(this, 2, proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.oneofGroups_[0], value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} returns this + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.clearProof = function() { + return this.setProof(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.hasProof = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional ResponseMetadata metadata = 3; + * @return {?proto.org.dash.platform.dapi.v0.ResponseMetadata} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.getMetadata = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ResponseMetadata} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ResponseMetadata, 3)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ResponseMetadata|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.setMetadata = function(value) { + return jspb.Message.setWrapperField(this, 3, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse} returns this + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.clearMetadata = function() { + return this.setMetadata(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.prototype.hasMetadata = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.toObject = function(includeInstance, msg) { + var f, obj = { + maxBytes: jspb.Message.getFieldWithDefault(msg, 1, ""), + maxGas: jspb.Message.getFieldWithDefault(msg, 2, ""), + timeIotaMs: jspb.Message.getFieldWithDefault(msg, 3, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.ConsensusParamsBlock; + return proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setMaxBytes(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setMaxGas(value); + break; + case 3: + var value = /** @type {string} */ (reader.readString()); + msg.setTimeIotaMs(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getMaxBytes(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getMaxGas(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getTimeIotaMs(); + if (f.length > 0) { + writer.writeString( + 3, + f + ); + } +}; + + +/** + * optional string max_bytes = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.getMaxBytes = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} returns this + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.setMaxBytes = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional string max_gas = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.getMaxGas = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} returns this + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.setMaxGas = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional string time_iota_ms = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.getTimeIotaMs = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} returns this + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.prototype.setTimeIotaMs = function(value) { + return jspb.Message.setProto3StringField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.toObject = function(includeInstance, msg) { + var f, obj = { + maxAgeNumBlocks: jspb.Message.getFieldWithDefault(msg, 1, ""), + maxAgeDuration: jspb.Message.getFieldWithDefault(msg, 2, ""), + maxBytes: jspb.Message.getFieldWithDefault(msg, 3, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence; + return proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setMaxAgeNumBlocks(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setMaxAgeDuration(value); + break; + case 3: + var value = /** @type {string} */ (reader.readString()); + msg.setMaxBytes(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getMaxAgeNumBlocks(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getMaxAgeDuration(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getMaxBytes(); + if (f.length > 0) { + writer.writeString( + 3, + f + ); + } +}; + + +/** + * optional string max_age_num_blocks = 1; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.getMaxAgeNumBlocks = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} returns this + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.setMaxAgeNumBlocks = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional string max_age_duration = 2; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.getMaxAgeDuration = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} returns this + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.setMaxAgeDuration = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional string max_bytes = 3; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.getMaxBytes = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * @param {string} value + * @return {!proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} returns this + */ +proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.prototype.setMaxBytes = function(value) { + return jspb.Message.setProto3StringField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + height: jspb.Message.getFieldWithDefault(msg, 1, 0), + prove: jspb.Message.getBooleanFieldWithDefault(msg, 2, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest; + return proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readInt64()); + msg.setHeight(value); + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setProve(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getHeight(); + if (f !== 0) { + writer.writeInt64( + 1, + f + ); + } + f = message.getProve(); + if (f) { + writer.writeBool( + 2, + f + ); + } +}; + + +/** + * optional int64 height = 1; + * @return {number} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.prototype.getHeight = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.prototype.setHeight = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional bool prove = 2; + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.prototype.getProve = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest} returns this + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsRequest.prototype.setProve = function(value) { + return jspb.Message.setProto3BooleanField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.toObject = function(opt_includeInstance) { + return proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + block: (f = msg.getBlock()) && proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.toObject(includeInstance, f), + evidence: (f = msg.getEvidence()) && proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse; + return proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.org.dash.platform.dapi.v0.ConsensusParamsBlock; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.deserializeBinaryFromReader); + msg.setBlock(value); + break; + case 2: + var value = new proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence; + reader.readMessage(value,proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.deserializeBinaryFromReader); + msg.setEvidence(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getBlock(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.org.dash.platform.dapi.v0.ConsensusParamsBlock.serializeBinaryToWriter + ); + } + f = message.getEvidence(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence.serializeBinaryToWriter + ); + } +}; + + +/** + * optional ConsensusParamsBlock block = 1; + * @return {?proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.getBlock = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ConsensusParamsBlock} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ConsensusParamsBlock, 1)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ConsensusParamsBlock|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.setBlock = function(value) { + return jspb.Message.setWrapperField(this, 1, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.clearBlock = function() { + return this.setBlock(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.hasBlock = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional ConsensusParamsEvidence evidence = 2; + * @return {?proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.getEvidence = function() { + return /** @type{?proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence} */ ( + jspb.Message.getWrapperField(this, proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence, 2)); +}; + + +/** + * @param {?proto.org.dash.platform.dapi.v0.ConsensusParamsEvidence|undefined} value + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} returns this +*/ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.setEvidence = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse} returns this + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.clearEvidence = function() { + return this.setEvidence(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetConsensusParamsResponse.prototype.hasEvidence = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +goog.object.extend(exports, proto.org.dash.platform.dapi.v0); diff --git a/packages/dapi-grpc/lib/getCoreDefinition.js b/packages/dapi-grpc/lib/getCoreDefinition.js new file mode 100644 index 00000000000..359b60bbf20 --- /dev/null +++ b/packages/dapi-grpc/lib/getCoreDefinition.js @@ -0,0 +1,11 @@ +const path = require('path'); + +const { loadPackageDefinition } = require('@dashevo/grpc-common'); + +function getCoreDefinition(version) { + const protoPath = path.join(__dirname, `../protos/core/v${version}/core.proto`); + + return loadPackageDefinition(protoPath, `org.dash.platform.dapi.v${version}.Core`); +} + +module.exports = getCoreDefinition; diff --git a/packages/dapi-grpc/lib/getPlatformDefinition.js b/packages/dapi-grpc/lib/getPlatformDefinition.js new file mode 100644 index 00000000000..805635682e2 --- /dev/null +++ b/packages/dapi-grpc/lib/getPlatformDefinition.js @@ -0,0 +1,11 @@ +const path = require('path'); + +const { loadPackageDefinition } = require('@dashevo/grpc-common'); + +function getPlatformDefinition(version) { + const protoPath = path.join(__dirname, `../protos/platform/v${version}/platform.proto`); + + return loadPackageDefinition(protoPath, `org.dash.platform.dapi.v${version}.Platform`); +} + +module.exports = getPlatformDefinition; diff --git a/packages/dapi-grpc/lib/test/.eslintrc b/packages/dapi-grpc/lib/test/.eslintrc new file mode 100644 index 00000000000..5092d807856 --- /dev/null +++ b/packages/dapi-grpc/lib/test/.eslintrc @@ -0,0 +1,9 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "globals": { + "expect": true + } +} diff --git a/packages/dapi-grpc/lib/test/bootstrap.js b/packages/dapi-grpc/lib/test/bootstrap.js new file mode 100644 index 00000000000..cf0bf22dd45 --- /dev/null +++ b/packages/dapi-grpc/lib/test/bootstrap.js @@ -0,0 +1,25 @@ +const { expect, use } = require('chai'); +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); +const dirtyChai = require('dirty-chai'); +const chaiAsPromised = require('chai-as-promised'); + +use(sinonChai); +use(chaiAsPromised); +use(dirtyChai); + +process.env.NODE_ENV = 'test'; + +beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + +afterEach(function afterEach() { + this.sinon.restore(); +}); + +global.expect = expect; diff --git a/packages/dapi-grpc/lib/utils/stripHostname.js b/packages/dapi-grpc/lib/utils/stripHostname.js new file mode 100644 index 00000000000..8ae77c96d21 --- /dev/null +++ b/packages/dapi-grpc/lib/utils/stripHostname.js @@ -0,0 +1,15 @@ +const { URL } = require('url'); + +/** + * Remove everything except (hostname/ip):port pair + * + * @param {string} hostname + * + * @returns {string} + */ +function stripHostname(hostname) { + const url = new URL(hostname); + return url.host; +} + +module.exports = stripHostname; diff --git a/packages/dapi-grpc/node.js b/packages/dapi-grpc/node.js new file mode 100644 index 00000000000..62082a6e30c --- /dev/null +++ b/packages/dapi-grpc/node.js @@ -0,0 +1,48 @@ +const CorePromiseClient = require('./clients/core/v0/nodejs/CorePromiseClient'); +const PlatformPromiseClient = require('./clients/platform/v0/nodejs/PlatformPromiseClient'); + +const protocCoreMessages = require('./clients/core/v0/nodejs/core_protoc'); +const protocPlatformMessages = require('./clients/platform/v0/nodejs/platform_protoc'); + +const getCoreDefinition = require('./lib/getCoreDefinition'); +const getPlatformDefinition = require('./lib/getPlatformDefinition'); + +const { + org: { + dash: { + platform: { + dapi: { + v0: pbjsCoreMessages, + }, + }, + }, + }, +} = require('./clients/core/v0/nodejs/core_pbjs'); + +const { + org: { + dash: { + platform: { + dapi: { + v0: pbjsPlatformMessages, + }, + }, + }, + }, +} = require('./clients/platform/v0/nodejs/platform_pbjs'); + +module.exports = { + getCoreDefinition, + getPlatformDefinition, + v0: { + CorePromiseClient, + PlatformPromiseClient, + pbjs: { + + ...pbjsCoreMessages, + ...pbjsPlatformMessages, + }, + ...protocCoreMessages, + ...protocPlatformMessages, + }, +}; diff --git a/packages/dapi-grpc/package.json b/packages/dapi-grpc/package.json new file mode 100644 index 00000000000..febbb8a7a5d --- /dev/null +++ b/packages/dapi-grpc/package.json @@ -0,0 +1,55 @@ +{ + "name": "@dashevo/dapi-grpc", + "version": "0.23.0-dev.4", + "description": "DAPI GRPC definition file and generated clients", + "browser": "browser.js", + "main": "node.js", + "scripts": { + "build": "yarn exec scripts/build.sh", + "lint": "eslint .", + "prepublishOnly": "yarn run build", + "test": "yarn run test:unit", + "test:unit": "mocha './test/unit/**/*.spec.js'" + }, + "contributors": [ + { + "name": "Ivan Shumkov", + "email": "shumkov@dash.org", + "url": "https://github.com/shumkov" + }, + { + "name": "Anton Suprunchuk", + "email": "anton.suprunchuk@dash.org", + "url": "https://github.com/antouhou" + } + ], + "repository": { + "type": "git", + "url": "git+https://github.com/dashevo/dapi-grpc.git" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/dashevo/dapi-grpc/issues" + }, + "homepage": "https://github.com/dashevo/dapi-grpc#readme", + "dependencies": { + "@dashevo/grpc-common": "workspace:~", + "@dashevo/protobufjs": "6.10.5", + "@grpc/grpc-js": "^1.3.7", + "google-protobuf": "^3.12.2", + "grpc-web": "1.2.1", + "long": "^5.2.0" + }, + "devDependencies": { + "chai": "^4.3.4", + "chai-as-promised": "^7.1.1", + "dirty-chai": "^2.0.1", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2", + "mocha": "^9.1.2", + "mocha-sinon": "^2.1.2", + "sinon": "^11.1.2", + "sinon-chai": "^3.7.0" + } +} diff --git a/packages/dapi-grpc/protos/core/v0/core.proto b/packages/dapi-grpc/protos/core/v0/core.proto new file mode 100644 index 00000000000..2ba33d19534 --- /dev/null +++ b/packages/dapi-grpc/protos/core/v0/core.proto @@ -0,0 +1,184 @@ +syntax = "proto3"; + +package org.dash.platform.dapi.v0; + +service Core { + rpc getStatus (GetStatusRequest) returns (GetStatusResponse); + rpc getBlock (GetBlockRequest) returns (GetBlockResponse); + rpc broadcastTransaction (BroadcastTransactionRequest) returns (BroadcastTransactionResponse); + rpc getTransaction (GetTransactionRequest) returns (GetTransactionResponse); + rpc getEstimatedTransactionFee (GetEstimatedTransactionFeeRequest) returns (GetEstimatedTransactionFeeResponse); + rpc subscribeToBlockHeadersWithChainLocks (BlockHeadersWithChainLocksRequest) returns (stream BlockHeadersWithChainLocksResponse); + rpc subscribeToTransactionsWithProofs (TransactionsWithProofsRequest) returns (stream TransactionsWithProofsResponse); +} + +message GetStatusRequest { + +} + +message GetStatusResponse { + message Version { + uint32 protocol = 1; + uint32 software = 2; + string agent = 3; + } + + message Time { + uint32 now = 1; + int32 offset = 2; + uint32 median = 3; + } + + enum Status { + NOT_STARTED = 0; + SYNCING = 1; + READY = 2; + ERROR = 3; + } + + message Chain { + string name = 1; + uint32 headers_count = 2; + uint32 blocks_count = 3; + bytes best_block_hash = 4; + double difficulty = 5; + bytes chain_work = 6; + bool is_synced = 7; + double sync_progress = 8; + } + + message Masternode { + enum Status { + UNKNOWN = 0; + WAITING_FOR_PROTX = 1; + POSE_BANNED = 2; + REMOVED = 3; + OPERATOR_KEY_CHANGED = 4; + PROTX_IP_CHANGED = 5; + READY = 6; + ERROR = 7; + } + + Status status = 1; + bytes pro_tx_hash = 2; + uint32 pose_penalty = 3; + bool is_synced = 4; + double sync_progress = 5; + } + + message NetworkFee { + double relay = 1; + double incremental = 2; + } + + message Network { + uint32 peers_count = 1; + NetworkFee fee = 2; + } + + Version version = 1; + Time time = 2; + Status status = 3; + double sync_progress = 4; + Chain chain = 5; + Masternode masternode = 6; + Network network = 7; +} + +message GetBlockRequest { + oneof block { + uint32 height = 1; + string hash = 2; + } +} + +message GetBlockResponse { + bytes block = 1; +} + +message BroadcastTransactionRequest { + bytes transaction = 1; + bool allow_high_fees = 2; + bool bypass_limits = 3; +} + +message BroadcastTransactionResponse { + string transaction_id = 1; +} + +message GetTransactionRequest { + string id = 1; +} + +message GetTransactionResponse { + bytes transaction = 1; + bytes block_hash = 2; + uint32 height = 3; + uint32 confirmations = 4; + bool is_instant_locked = 5; + bool is_chain_locked = 6; +} + +message BlockHeadersWithChainLocksRequest { + oneof from_block { + bytes from_block_hash = 1; + uint32 from_block_height = 2; + } + + uint32 count = 3; +} + +message BlockHeadersWithChainLocksResponse { + oneof responses { + BlockHeaders block_headers = 1; + bytes chain_lock = 2; + } +} + +message BlockHeaders { + repeated bytes headers = 1; +} + +message GetEstimatedTransactionFeeRequest { + uint32 blocks = 1; +} + +message GetEstimatedTransactionFeeResponse { + double fee = 1; +} + +message TransactionsWithProofsRequest { + BloomFilter bloom_filter = 1; + + oneof from_block { + bytes from_block_hash = 2; + uint32 from_block_height = 3; + } + + uint32 count = 4; + + bool send_transaction_hashes = 5; +} + +message BloomFilter { + bytes v_data = 1; + uint32 n_hash_funcs = 2; + uint32 n_tweak = 3; + uint32 n_flags = 4; +} + +message TransactionsWithProofsResponse { + oneof responses { + RawTransactions raw_transactions = 1; + InstantSendLockMessages instant_send_lock_messages = 2; + bytes raw_merkle_block = 3; + } +} + +message RawTransactions { + repeated bytes transactions = 1; +} + +message InstantSendLockMessages { + repeated bytes messages = 1; +} diff --git a/packages/dapi-grpc/protos/platform/v0/platform.proto b/packages/dapi-grpc/protos/platform/v0/platform.proto new file mode 100644 index 00000000000..e03d3784e81 --- /dev/null +++ b/packages/dapi-grpc/protos/platform/v0/platform.proto @@ -0,0 +1,129 @@ +syntax = "proto3"; + +package org.dash.platform.dapi.v0; + +service Platform { + rpc broadcastStateTransition (BroadcastStateTransitionRequest) returns (BroadcastStateTransitionResponse); + rpc getIdentity (GetIdentityRequest) returns (GetIdentityResponse); + rpc getDataContract (GetDataContractRequest) returns (GetDataContractResponse); + rpc getDocuments (GetDocumentsRequest) returns (GetDocumentsResponse); + rpc getIdentitiesByPublicKeyHashes (GetIdentitiesByPublicKeyHashesRequest) returns (GetIdentitiesByPublicKeyHashesResponse); + rpc waitForStateTransitionResult (WaitForStateTransitionResultRequest) returns (WaitForStateTransitionResultResponse); + rpc getConsensusParams (GetConsensusParamsRequest) returns (GetConsensusParamsResponse); +} + +message Proof { + bytes merkle_proof = 1; + bytes signature_llmq_hash = 2; + bytes signature = 3; +} + +message ResponseMetadata { + int64 height = 1; + uint32 core_chain_locked_height = 2; +} + +message StateTransitionBroadcastError { + uint32 code = 1; + string message = 2; + bytes data = 3; +} + +message BroadcastStateTransitionRequest { + bytes state_transition = 1; +} + +message BroadcastStateTransitionResponse { + +} + +message GetIdentityRequest { + bytes id = 1; + bool prove = 2; +} + +message GetIdentityResponse { + bytes identity = 1; + Proof proof = 2; + ResponseMetadata metadata = 3; +} + + message GetDataContractRequest { + bytes id = 1; + bool prove = 2; +} + +message GetDataContractResponse { + bytes data_contract = 1; + Proof proof = 2; + ResponseMetadata metadata = 3; +} + +message GetDocumentsRequest { + bytes data_contract_id = 1; + string document_type = 2; + + bytes where = 3; + bytes order_by = 4; + + uint32 limit = 5; + + oneof start { + bytes start_after = 6; + bytes start_at = 7; + } + + bool prove = 8; +} + +message GetDocumentsResponse { + repeated bytes documents = 1; + Proof proof = 2; + ResponseMetadata metadata = 3; +} + +message GetIdentitiesByPublicKeyHashesRequest { + repeated bytes public_key_hashes = 1; + bool prove = 2; +} + +message GetIdentitiesByPublicKeyHashesResponse { + repeated bytes identities = 1; + Proof proof = 2; + ResponseMetadata metadata = 3; +} + +message WaitForStateTransitionResultRequest { + bytes state_transition_hash = 1; + bool prove = 2; +} + +message WaitForStateTransitionResultResponse { + oneof responses { + StateTransitionBroadcastError error = 1; + Proof proof = 2; + } + ResponseMetadata metadata = 3; +} + +message ConsensusParamsBlock { + string max_bytes = 1; + string max_gas = 2; + string time_iota_ms = 3; +} + +message ConsensusParamsEvidence { + string max_age_num_blocks = 1; + string max_age_duration = 2; + string max_bytes = 3; +} + +message GetConsensusParamsRequest { + int64 height = 1; + bool prove = 2; +} + +message GetConsensusParamsResponse { + ConsensusParamsBlock block = 1; + ConsensusParamsEvidence evidence = 2; +} diff --git a/packages/dapi-grpc/scripts/build.sh b/packages/dapi-grpc/scripts/build.sh new file mode 100755 index 00000000000..0f2ee580379 --- /dev/null +++ b/packages/dapi-grpc/scripts/build.sh @@ -0,0 +1,183 @@ +#!/usr/bin/env bash + +CORE_PROTO_PATH="$PWD/protos/core/v0" +CORE_CLIENTS_PATH="$PWD/clients/core/v0" + +PLATFORM_PROTO_PATH="$PWD/protos/platform/v0" +PLATFORM_CLIENTS_PATH="$PWD/clients/platform/v0" + +CORE_WEB_OUT_PATH="$CORE_CLIENTS_PATH/web" +PLATFORM_WEB_OUT_PATH="$PLATFORM_CLIENTS_PATH/web" + +CORE_JAVA_OUT_PATH="$CORE_CLIENTS_PATH/java" +PLATFORM_JAVA_OUT_PATH="$PLATFORM_CLIENTS_PATH/java" + +CORE_OBJ_C_OUT_PATH="$CORE_CLIENTS_PATH/objective-c" +PLATFORM_OBJ_C_OUT_PATH="$PLATFORM_CLIENTS_PATH/objective-c" + +CORE_PYTHON_OUT_PATH="$CORE_CLIENTS_PATH/python" +PLATFORM_PYTHON_OUT_PATH="$PLATFORM_CLIENTS_PATH/python" + +GRPC_WEB_COMMON_IMAGE="strophy/grpc-web-common:1.3.2" +PROTOC_IMAGE="strophy/protoc:3.3.2" + +################################################# +# Generate JavaScript client for `Core` service # +################################################# + +rm -rf "$CORE_WEB_OUT_PATH/*" + +docker run -v "$CORE_PROTO_PATH:$CORE_PROTO_PATH" \ + -v "$CORE_WEB_OUT_PATH:$CORE_WEB_OUT_PATH" \ + --rm \ + "$GRPC_WEB_COMMON_IMAGE" \ + protoc -I="$CORE_PROTO_PATH" "core.proto" \ + --js_out="import_style=commonjs:$CORE_WEB_OUT_PATH" \ + --grpc-web_out="import_style=commonjs,mode=grpcwebtext:$CORE_WEB_OUT_PATH" + +# Clean node message classes + +rm -rf "$CORE_CLIENTS_PATH/nodejs/*_protoc.js" +rm -rf "$CORE_CLIENTS_PATH/nodejs/*_pbjs.js" + +# Copy compiled modules with message classes + +cp "$CORE_WEB_OUT_PATH/core_pb.js" "$CORE_CLIENTS_PATH/nodejs/core_protoc.js" + +# Generate node message classes +pbjs \ + -t static-module \ + -w commonjs \ + -r core_root \ + -o "$CORE_CLIENTS_PATH/nodejs/core_pbjs.js" \ + "$CORE_PROTO_PATH/core.proto" + +##################################################### +# Generate JavaScript client for `Platform` service # +##################################################### + +rm -rf "$PLATFORM_WEB_OUT_PATH/*" + +docker run -v "$PLATFORM_PROTO_PATH:$PLATFORM_PROTO_PATH" \ + -v "$PLATFORM_WEB_OUT_PATH:$PLATFORM_WEB_OUT_PATH" \ + --rm \ + "$GRPC_WEB_COMMON_IMAGE" \ + protoc -I="$PLATFORM_PROTO_PATH" "platform.proto" \ + --js_out="import_style=commonjs:$PLATFORM_WEB_OUT_PATH" \ + --grpc-web_out="import_style=commonjs,mode=grpcwebtext:$PLATFORM_WEB_OUT_PATH" + +# Clean node message classes + +rm -rf "$PLATFORM_CLIENTS_PATH/nodejs/*_protoc.js" +rm -rf "$PLATFORM_CLIENTS_PATH/nodejs/*_pbjs.js" + +# Copy compiled modules with message classes + +cp "$PLATFORM_WEB_OUT_PATH/platform_pb.js" "$PLATFORM_CLIENTS_PATH/nodejs/platform_protoc.js" + +pbjs \ + -t static-module \ + -w commonjs \ + -r platform_root \ + -o "$PLATFORM_CLIENTS_PATH/nodejs/platform_pbjs.js" \ + "$PLATFORM_PROTO_PATH/platform.proto" + +################################### +# Generate Java client for `Core` # +################################### + +rm -rf "$CORE_JAVA_OUT_PATH/*" + +docker run -v "$CORE_PROTO_PATH:$CORE_PROTO_PATH" \ + -v "$CORE_JAVA_OUT_PATH:$CORE_JAVA_OUT_PATH" \ + --rm \ + "$PROTOC_IMAGE" \ + --plugin=protoc-gen-grpc=/usr/bin/protoc-gen-grpc-java \ + --grpc-java_out="$CORE_JAVA_OUT_PATH" \ + --proto_path="$CORE_PROTO_PATH" \ + -I="$CORE_PROTO_PATH" \ + "core.proto" + +####################################### +# Generate Java client for `Platform` # +####################################### + +rm -rf "$PLATFORM_JAVA_OUT_PATH/*" + +docker run -v "$PLATFORM_PROTO_PATH:$PLATFORM_PROTO_PATH" \ + -v "$PLATFORM_JAVA_OUT_PATH:$PLATFORM_JAVA_OUT_PATH" \ + --rm \ + "$PROTOC_IMAGE" \ + --plugin=protoc-gen-grpc=/usr/bin/protoc-gen-grpc-java \ + --grpc-java_out="$PLATFORM_JAVA_OUT_PATH" \ + --proto_path="$PLATFORM_PROTO_PATH" \ + -I="$PLATFORM_PROTO_PATH" \ + "platform.proto" + +########################################## +# Generate Objective-C client for `Core` # +########################################## + +rm -rf "$CORE_OBJ_C_OUT_PATH/*" + +docker run -v "$CORE_PROTO_PATH:$CORE_PROTO_PATH" \ + -v "$CORE_OBJ_C_OUT_PATH:$CORE_OBJ_C_OUT_PATH" \ + --rm \ + "$PROTOC_IMAGE" \ + --plugin=protoc-gen-grpc=/usr/bin/grpc_objective_c_plugin \ + --objc_out="$CORE_OBJ_C_OUT_PATH" \ + --grpc_out="$CORE_OBJ_C_OUT_PATH" \ + --proto_path="$CORE_PROTO_PATH" \ + -I="$CORE_PROTO_PATH" \ + "core.proto" + +############################################## +# Generate Objective-C client for `Platform` # +############################################## + +rm -rf "$PLATFORM_OBJ_C_OUT_PATH/*" + +docker run -v "$PLATFORM_PROTO_PATH:$PLATFORM_PROTO_PATH" \ + -v "$PLATFORM_OBJ_C_OUT_PATH:$PLATFORM_OBJ_C_OUT_PATH" \ + --rm \ + "$PROTOC_IMAGE" \ + --plugin=protoc-gen-grpc=/usr/bin/grpc_objective_c_plugin \ + --objc_out="$PLATFORM_OBJ_C_OUT_PATH" \ + --grpc_out="$PLATFORM_OBJ_C_OUT_PATH" \ + --proto_path="$PLATFORM_PROTO_PATH" \ + -I="$PLATFORM_PROTO_PATH" \ + "platform.proto" + +##################################### +# Generate Python client for `Core` # +##################################### + +rm -rf "$CORE_PYTHON_OUT_PATH/*" + +docker run -v "$CORE_PROTO_PATH:$CORE_PROTO_PATH" \ + -v "$CORE_PYTHON_OUT_PATH:$CORE_PYTHON_OUT_PATH" \ + --rm \ + "$PROTOC_IMAGE" \ + --plugin=protoc-gen-grpc=/usr/bin/grpc_python_plugin \ + --python_out="$CORE_PYTHON_OUT_PATH" \ + --grpc_out="$CORE_PYTHON_OUT_PATH" \ + --proto_path="$CORE_PROTO_PATH" \ + -I="$CORE_PROTO_PATH" \ + "core.proto" + +######################################### +# Generate Python client for `Platform` # +######################################### + +rm -rf "$PLATFORM_PYTHON_OUT_PATH/*" + +docker run -v "$PLATFORM_PROTO_PATH:$PLATFORM_PROTO_PATH" \ + -v "$PLATFORM_PYTHON_OUT_PATH:$PLATFORM_PYTHON_OUT_PATH" \ + --rm \ + "$PROTOC_IMAGE" \ + --plugin=protoc-gen-grpc=/usr/bin/grpc_python_plugin \ + --python_out="$PLATFORM_PYTHON_OUT_PATH" \ + --grpc_out="$PLATFORM_PYTHON_OUT_PATH" \ + --proto_path="$PLATFORM_PROTO_PATH" \ + -I="$PLATFORM_PROTO_PATH" \ + "platform.proto" diff --git a/packages/dapi-grpc/test/.eslintrc b/packages/dapi-grpc/test/.eslintrc new file mode 100644 index 00000000000..5092d807856 --- /dev/null +++ b/packages/dapi-grpc/test/.eslintrc @@ -0,0 +1,9 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "globals": { + "expect": true + } +} diff --git a/packages/dapi-grpc/test/unit/clients/core/v0/nodejs/CorePromiseClient.spec.js b/packages/dapi-grpc/test/unit/clients/core/v0/nodejs/CorePromiseClient.spec.js new file mode 100644 index 00000000000..7710a63e2c2 --- /dev/null +++ b/packages/dapi-grpc/test/unit/clients/core/v0/nodejs/CorePromiseClient.spec.js @@ -0,0 +1,139 @@ +const { v0: { CorePromiseClient } } = require('../../../../../..'); + +describe('CorePromiseClient', () => { + let corePromiseClient; + let request; + let response; + + beforeEach(function main() { + request = 'test request'; + response = 'test response'; + + corePromiseClient = new CorePromiseClient('https://localhost/'); + corePromiseClient.client = { + getStatus: this.sinon.stub().resolves(response), + getBlock: this.sinon.stub().resolves(response), + broadcastTransaction: this.sinon.stub().resolves(response), + getTransaction: this.sinon.stub().resolves(response), + getEstimatedTransactionFee: this.sinon.stub().resolves(response), + subscribeToTransactionsWithProofs: this.sinon.stub().resolves(response), + }; + }); + + describe('#getStatus', () => { + it('should return status', async () => { + const result = await corePromiseClient.getStatus(request); + + expect(result).to.equal(response); + expect(corePromiseClient.client.getStatus).to.be.calledOnceWith(request); + }); + + it('should throw an error when metadata is not an object', async () => { + try { + corePromiseClient.getStatus({}, 'metadata'); + + expect.fail('Error was not thrown'); + } catch (e) { + expect(e.message).to.equal('metadata must be an object'); + } + }); + }); + + describe('#getBlock', () => { + it('should get block', async () => { + const result = await corePromiseClient.getBlock(request); + + expect(result).to.equal(response); + expect(corePromiseClient.client.getBlock).to.be.calledOnceWith(request); + }); + + it('should throw an error when metadata is not an object', async () => { + try { + corePromiseClient.getBlock({}, 'metadata'); + + expect.fail('Error was not thrown'); + } catch (e) { + expect(e.message).to.equal('metadata must be an object'); + } + }); + }); + + describe('#broadcastTransaction', () => { + it('should broadcast transaction', async () => { + const result = await corePromiseClient.broadcastTransaction(request); + + expect(result).to.equal(response); + expect(corePromiseClient.client.broadcastTransaction).to.be.calledOnceWith(request); + }); + + it('should throw an error when metadata is not an object', async () => { + try { + corePromiseClient.broadcastTransaction({}, 'metadata'); + + expect.fail('Error was not thrown'); + } catch (e) { + expect(e.message).to.equal('metadata must be an object'); + } + }); + }); + + describe('#getTransaction', () => { + it('should get transaction', async () => { + const result = await corePromiseClient.getTransaction(request); + + expect(result).to.equal(response); + expect(corePromiseClient.client.getTransaction) + .to.be.calledOnceWith(request); + }); + + it('should throw an error when metadata is not an object', async () => { + try { + corePromiseClient.getTransaction({}, 'metadata'); + + expect.fail('Error was not thrown'); + } catch (e) { + expect(e.message).to.equal('metadata must be an object'); + } + }); + }); + + describe('#getEstimatedTransactionFee', () => { + it('should return status', async () => { + const result = await corePromiseClient.getEstimatedTransactionFee(request); + + expect(result).to.equal(response); + expect(corePromiseClient.client.getEstimatedTransactionFee).to.be.calledOnceWith(request); + }); + + it('should throw an error when metadata is not an object', async () => { + try { + corePromiseClient.getEstimatedTransactionFee({}, 'metadata'); + + expect.fail('Error was not thrown'); + } catch (e) { + expect(e.message).to.equal('metadata must be an object'); + } + }); + }); + + describe('#subscribeToTransactionsWithProofs', () => { + it('should subscribe to transactions with proofs', async () => { + const result = await corePromiseClient + .subscribeToTransactionsWithProofs(request); + + expect(result).to.equal(response); + expect(corePromiseClient.client.subscribeToTransactionsWithProofs) + .to.be.calledOnceWith(request); + }); + + it('should throw an error when metadata is not an object', async () => { + try { + corePromiseClient.subscribeToTransactionsWithProofs({}, 'metadata'); + + expect.fail('Error was not thrown'); + } catch (e) { + expect(e.message).to.equal('metadata must be an object'); + } + }); + }); +}); diff --git a/packages/dapi-grpc/test/unit/clients/platform/v0/nodejs/PlatformPromiseClient.spec.js b/packages/dapi-grpc/test/unit/clients/platform/v0/nodejs/PlatformPromiseClient.spec.js new file mode 100644 index 00000000000..ad9c47854cb --- /dev/null +++ b/packages/dapi-grpc/test/unit/clients/platform/v0/nodejs/PlatformPromiseClient.spec.js @@ -0,0 +1,87 @@ +const { v0: { PlatformPromiseClient } } = require('../../../../../..'); + +describe('PlatformPromiseClient', () => { + let platformPromiseClient; + let request; + let response; + + beforeEach(function main() { + request = 'test request'; + response = 'test response'; + + platformPromiseClient = new PlatformPromiseClient('https://localhost/'); + platformPromiseClient.client = { + broadcastStateTransition: this.sinon.stub().resolves(response), + getIdentity: this.sinon.stub().resolves(response), + getDataContract: this.sinon.stub().resolves(response), + getDocuments: this.sinon.stub().resolves(response), + }; + }); + + describe('#broadcastStateTransition', () => { + it('should broadcast state transition', async () => { + const result = await platformPromiseClient.broadcastStateTransition(request); + + expect(result).to.equal(response); + expect(platformPromiseClient.client.broadcastStateTransition).to.be.calledOnceWith(request); + }); + + it('should throw an error when metadata is not an object', async () => { + try { + platformPromiseClient.broadcastStateTransition({}, 'metadata'); + + expect.fail('Error was not thrown'); + } catch (e) { + expect(e.message).to.equal('metadata must be an object'); + } + }); + }); + + describe('#getIdentity', () => { + it('should get identity', async () => { + const result = await platformPromiseClient.getIdentity(request); + + expect(result).to.equal(response); + expect(platformPromiseClient.client.getIdentity) + .to.be.calledOnceWith(request); + }); + + it('should throw an error when metadata is not an object', async () => { + try { + platformPromiseClient.getIdentity({}, 'metadata'); + + expect.fail('Error was not thrown'); + } catch (e) { + expect(e.message).to.equal('metadata must be an object'); + } + }); + }); + + describe('#getDataContract', () => { + it('should get data contract', async () => { + const result = await platformPromiseClient.getDataContract(request); + + expect(result).to.equal(response); + expect(platformPromiseClient.client.getDataContract).to.be.calledOnceWith(request); + }); + + it('should throw an error when metadata is not an object', async () => { + try { + platformPromiseClient.getDataContract({}, 'metadata'); + + expect.fail('Error was not thrown'); + } catch (e) { + expect(e.message).to.equal('metadata must be an object'); + } + }); + }); + + describe('#getDocuments', () => { + it('should get documents', async () => { + const result = await platformPromiseClient.getDocuments(request); + + expect(result).to.equal(response); + expect(platformPromiseClient.client.getDocuments).to.be.calledOnceWith(request); + }); + }); +}); diff --git a/packages/dapi-grpc/test/unit/getCoreDefinition.spec.js b/packages/dapi-grpc/test/unit/getCoreDefinition.spec.js new file mode 100644 index 00000000000..d0dbdc78e09 --- /dev/null +++ b/packages/dapi-grpc/test/unit/getCoreDefinition.spec.js @@ -0,0 +1,33 @@ +const getCoreDefinition = require('../../lib/getCoreDefinition'); + +describe('getCoreDefinition', () => { + describe('v0', () => { + it('should return loaded GRPC package definition', async () => { + const coreDefinition = getCoreDefinition(0); + + expect(coreDefinition).to.be.an('function'); + expect(coreDefinition).to.have.property('service'); + + expect(coreDefinition.service).to.have.property('broadcastTransaction'); + expect(coreDefinition.service.broadcastTransaction.path).to.equal('/org.dash.platform.dapi.v0.Core/broadcastTransaction'); + + expect(coreDefinition.service).to.have.property('getTransaction'); + expect(coreDefinition.service.getTransaction.path).to.equal('/org.dash.platform.dapi.v0.Core/getTransaction'); + + expect(coreDefinition.service).to.have.property('getStatus'); + expect(coreDefinition.service.getStatus.path).to.equal('/org.dash.platform.dapi.v0.Core/getStatus'); + + expect(coreDefinition.service).to.have.property('getBlock'); + expect(coreDefinition.service.getBlock.path).to.equal('/org.dash.platform.dapi.v0.Core/getBlock'); + + expect(coreDefinition.service).to.have.property('getEstimatedTransactionFee'); + expect(coreDefinition.service.getEstimatedTransactionFee.path).to.equal('/org.dash.platform.dapi.v0.Core/getEstimatedTransactionFee'); + + expect(coreDefinition.service).to.have.property('subscribeToBlockHeadersWithChainLocks'); + expect(coreDefinition.service.subscribeToBlockHeadersWithChainLocks.path).to.equal('/org.dash.platform.dapi.v0.Core/subscribeToBlockHeadersWithChainLocks'); + + expect(coreDefinition.service).to.have.property('subscribeToTransactionsWithProofs'); + expect(coreDefinition.service.subscribeToTransactionsWithProofs.path).to.equal('/org.dash.platform.dapi.v0.Core/subscribeToTransactionsWithProofs'); + }); + }); +}); diff --git a/packages/dapi-grpc/test/unit/getPlatformDefinition.spec.js b/packages/dapi-grpc/test/unit/getPlatformDefinition.spec.js new file mode 100644 index 00000000000..723c1ccdb28 --- /dev/null +++ b/packages/dapi-grpc/test/unit/getPlatformDefinition.spec.js @@ -0,0 +1,24 @@ +const getPlatformDefinition = require('../../lib/getPlatformDefinition'); + +describe('getPlatformDefinition', () => { + describe('v0', () => { + it('should return loaded GRPC package definition', async () => { + const platformDefinition = getPlatformDefinition(0); + + expect(platformDefinition).to.be.an('function'); + expect(platformDefinition).to.have.property('service'); + + expect(platformDefinition.service).to.have.property('broadcastStateTransition'); + expect(platformDefinition.service.broadcastStateTransition.path).to.equal('/org.dash.platform.dapi.v0.Platform/broadcastStateTransition'); + + expect(platformDefinition.service).to.have.property('getIdentity'); + expect(platformDefinition.service.getIdentity.path).to.equal('/org.dash.platform.dapi.v0.Platform/getIdentity'); + + expect(platformDefinition.service).to.have.property('getDataContract'); + expect(platformDefinition.service.getDataContract.path).to.equal('/org.dash.platform.dapi.v0.Platform/getDataContract'); + + expect(platformDefinition.service).to.have.property('getDocuments'); + expect(platformDefinition.service.getDocuments.path).to.equal('/org.dash.platform.dapi.v0.Platform/getDocuments'); + }); + }); +}); diff --git a/packages/dapi-grpc/test/unit/utils/stripHostname.spec.js b/packages/dapi-grpc/test/unit/utils/stripHostname.spec.js new file mode 100644 index 00000000000..750beb3fd10 --- /dev/null +++ b/packages/dapi-grpc/test/unit/utils/stripHostname.spec.js @@ -0,0 +1,23 @@ +const stripHostname = require('../../../lib/utils/stripHostname'); + +describe('stripHostname', () => { + let hostname; + + beforeEach(() => { + hostname = 'http://ip:3030/'; + }); + + it('should strip everything and leave only hostname:port pair', () => { + const result = stripHostname(hostname); + + expect(result).to.equal('ip:3030'); + }); + + it('should strip everything and leave only ip:port pair', () => { + hostname = 'http://127.0.0.1:3030/?some=params'; + + const result = stripHostname(hostname); + + expect(result).to.equal('127.0.0.1:3030'); + }); +}); diff --git a/packages/dapi/.env.example b/packages/dapi/.env.example new file mode 100644 index 00000000000..803d981b472 --- /dev/null +++ b/packages/dapi/.env.example @@ -0,0 +1,42 @@ +#DAPI config settings sample and defaults +#To overwrite: create a new .env file in the project root then +#copy this file content and use custom values where applicable + +# Set to true if you are going to run DAPI on the livenet. +LIVENET = false + +# Ports on which DAPI server will listen for client requests +API_JSON_RPC_PORT = 2501 +API_GRPC_PORT = 2500 +TX_FILTER_STREAM_GRPC_PORT = 2510 + +# Protocol for connecting to dashcore RPC +DASHCORE_RPC_PROTOCOL = http + +# DashCore service connection setting +DASHCORE_RPC_USER = dashrpc +DASHCORE_RPC_PASS = password +DASHCORE_RPC_HOST = 127.0.0.1 +DASHCORE_RPC_PORT = 30002 +DASHCORE_ZMQ_HOST = 127.0.0.1 +DASHCORE_ZMQ_PORT = 30003 +DASHCORE_P2P_HOST = 127.0.0.1 +DASHCORE_P2P_PORT = 30001 +DASHCORE_P2P_NETWORK = testnet + +# Can be `testnet`, `regtest` and `livenet` +NETWORK = testnet + +# Time in ms for garbage collecting inactive bloomfiltering clients +BLOOM_FILTER_PERSISTENCE_TIMEOUT = 60000 + +BLOCK_HEADERS_CACHE_SIZE=500 + +TENDERMINT_RPC_HOST=localhost +TENDERMINT_RPC_PORT=26657 + +# SERVICE_IMAGE_DRIVE= # Drive image name, if omitted dashpay/dashrive is used +# SERVICE_IMAGE_DAPI= # DAPI image name, if omitted dashpay/dapi is used +# SERVICE_IMAGE_CORE= # Dash Core image name, if omitted dashpay/dashcore is used + +NODE_ENV=production diff --git a/packages/dapi/.eslintignore b/packages/dapi/.eslintignore new file mode 100644 index 00000000000..4ebc8aea50e --- /dev/null +++ b/packages/dapi/.eslintignore @@ -0,0 +1 @@ +coverage diff --git a/packages/dapi/.eslintrc b/packages/dapi/.eslintrc new file mode 100644 index 00000000000..7777fe3afec --- /dev/null +++ b/packages/dapi/.eslintrc @@ -0,0 +1,21 @@ +{ + "extends": "airbnb-base", + "env": { + "node": true + }, + "rules": { + "no-plusplus": 0, + "no-await-in-loop": "off", + "no-restricted-syntax": [ + "error", + { + "selector": "LabeledStatement", + "message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand." + }, + { + "selector": "WithStatement", + "message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize." + } + ] + } +} diff --git a/packages/dapi/.mocharc.yml b/packages/dapi/.mocharc.yml new file mode 100644 index 00000000000..96d89cbed92 --- /dev/null +++ b/packages/dapi/.mocharc.yml @@ -0,0 +1,4 @@ +exit: true +timeout: 10000 +file: + - ./lib/test/bootstrap.js diff --git a/packages/dapi/CHANGELOG.md b/packages/dapi/CHANGELOG.md new file mode 100644 index 00000000000..5a8d4bcceca --- /dev/null +++ b/packages/dapi/CHANGELOG.md @@ -0,0 +1,290 @@ +# [0.21.0](https://github.com/dashevo/dapi/compare/v0.20.0...v0.21.0) (2021-10-14) + + +### Features + +* `getConsensusParams` endpoint ([#393](https://github.com/dashevo/dapi/issues/393)) +* support multiple store tree proofs in responses ([#398](https://github.com/dashevo/dapi/issues/398)) +* comprehensive error codes ([#394](https://github.com/dashevo/dapi/issues/394), [#397](https://github.com/dashevo/dapi/issues/397), [#405](https://github.com/dashevo/dapi/issues/405), [#406](https://github.com/dashevo/dapi/issues/406), [67c4da3](https://github.com/dashevo/dapi/commit/67c4da395750af774e68f9c75d3af4c6b2fd0643)) + + +### BREAKING CHANGES + +* DAPI responds with new error codes + + + +# [0.20.0](https://github.com/dashevo/dapi/compare/v0.19.0...v0.20.0) (2021-07-22) + + +### Features + +* add more information to proofs ([#373](https://github.com/dashevo/dapi/issues/373)) +* strict data contract schema validation ([#372](https://github.com/dashevo/dapi/issues/372)) + + +### Bug Fixes + +* `prove` options was set as a string ([#387](https://github.com/dashevo/dapi/issues/387)) +* `EAI_AGAIN` error code was not handled ([#381](https://github.com/dashevo/dapi/issues/381)) +* `subscribeToNewTransactionsWithProofs` doesn't emit transactions and instant lock in some cases ([#384](https://github.com/dashevo/dapi/issues/384), [#375](https://github.com/dashevo/dapi/issues/375)) + + +### BREAKING CHANGES + +* data will be `null` in case proof is requested from platform endpoints +* not compatible with contracts created using `dpp` older than v0.20 + + + +# [0.19.0](https://github.com/dashevo/dapi/compare/v0.18.1...v0.19.0) (2021-05-05) + + +### Features + +* enable Docker build npm cache ([#348](https://github.com/dashevo/dapi/issues/348)) +* remove insight API ([#351](https://github.com/dashevo/dapi/issues/351), [#344](https://github.com/dashevo/dapi/issues/344), [#345](https://github.com/dashevo/dapi/issues/345), [#346](https://github.com/dashevo/dapi/issues/346), [#345](https://github.com/dashevo/dapi/issues/345), [#347](https://github.com/dashevo/dapi/issues/347), [#362](https://github.com/dashevo/dapi/issues/362)) + + +### Bug Fixes + +* error loading shared library libzmq.so.5 ([51e66f7](https://github.com/dashevo/dapi/commit/51e66f76c9fbdecef000fc6acd5d2ab5dd20f01d)) + + +### BREAKING CHANGES + +* `getStatus` response format is changed and is not compatible with older version + + + +# [0.18.1](https://github.com/dashevo/dapi/compare/v0.18.0...v0.18.1) (2021-03-08) + + +### Chores + +* update dependencies to stable versions ([cb9070](https://github.com/dashevo/dapi/commit/cb9070a2e58c66eb24a16d01e5d9c28bb9eef95d)) + + + +# [0.18.0](https://github.com/dashevo/dapi/compare/v0.17.1...v0.18.0) (2021-03-03) + + +### Features + +* handle Unavailable ABCI error ([#337](https://github.com/dashevo/dapi/issues/337)) +* `waitForStateTransitionResult` endpoint ([#331](https://github.com/dashevo/dapi/issues/331), [#338](https://github.com/dashevo/dapi/issues/338), [#340](https://github.com/dashevo/dapi/issues/340), [#341](https://github.com/dashevo/dapi/issues/341)) +* replace `broadcast_tx_commit` with `broadcast_tx_sync` ([#330](https://github.com/dashevo/dapi/issues/330)) + + +### BREAKING CHANGES + +* `broadcastStateTransition` doesn't wait for state transition commit. Use `waitForStateTransitionResult` to get ST acknowledgment. + + + +## [0.17.1](https://github.com/dashevo/dapi/compare/v0.17.0...v0.17.1) (2021-01-19) + + +### Bug Fixes + +* **core:** timeOffset from Insight expected to be uint32 ([#332](https://github.com/dashevo/dapi/issues/332)) + + + +# [0.17.0](https://github.com/dashevo/dapi/compare/v0.16.2...v0.17.0) (2020-12-30) + + +### Bug Fixes + +* internal error if state transaction was broadcasted twice ([#328](https://github.com/dashevo/dapi/issues/328)) + + +### Features + +* provide state tree proofs ([#323](https://github.com/dashevo/dapi/issues/323)) +* add instant send locks to the transaction stream ([#318](https://github.com/dashevo/dapi/issues/318), [#327](https://github.com/dashevo/dapi/issues/327)) +* use new drive response format ([#316](https://github.com/dashevo/dapi/issues/316)) +* update dashcore-lib to 0.19.5 ([#312](https://github.com/dashevo/dapi/issues/312)) + + + +## [0.16.2](https://github.com/dashevo/dapi/compare/v0.16.1...v0.16.2) (2020-12-21) + + +### Bug Fixes + +* crash in dapi-tx-filter-stream "can't read property trim() of undefined" + + + +## [0.16.1](https://github.com/dashevo/dapi/compare/v0.16.0...v0.16.1) (2020-11-16) + + +### Bug Fixes + +* `count too big` being thrown in `subscribeToTransactionsWithProofsHandler` ([#315](https://github.com/dashevo/dapi/issues/315)) + + + +# [0.16.0](https://github.com/dashevo/dapi/compare/v0.15.0...v0.16.0) (2020-10-27) + + +### Features + +* `getIdentitiesByPublicKeyHashes` and `getIdentityIdsByPublicKeyHashes` endpoints ([#304](https://github.com/dashevo/dapi/issues/304), [#307](https://github.com/dashevo/dapi/issues/307)) +* debug mode to respond internal error with message and stack ([#302](https://github.com/dashevo/dapi/issues/302)) +* use Drive 0.16 endpoints ([#308](https://github.com/dashevo/dapi/issues/308), [#309](https://github.com/dashevo/dapi/issues/309)) + +### BREAKING CHANGES + +* `getIdentityByFirstPublicKey` and `getIdentityIdByFirstPublicKey` removed + + + +# [0.15.0](https://github.com/dashevo/dapi/compare/v0.14.0...v0.15.0) (2020-09-04) + + +### Features + +* update to DAPI gRPC 0.15 ([#298](https://github.com/dashevo/dapi/issues/298)) +* remove getUTXO & getAddressSummary rpc methods ([#292](https://github.com/dashevo/dapi/issues/292), [#293](https://github.com/dashevo/dapi/issues/293)) +* rename sendTransaction and applyStateTransition to broadcast ([#287](https://github.com/dashevo/dapi/pull/287)) + + +### BREAKING CHANGES + +* `broadcastTransaction` and `broadcastStatTransition` gRPC method names are using instead of `sendTransaction` and `applyStateTransition` +* TxFilterStream `subscribeToTransactionsWithProofs` endpoint uses `Core` gRPC service +* see [DAPI gRPC breaking changes](https://github.com/dashevo/dapi-grpc/releases/tag/v0.15.0) + + + +# [0.14.0](https://github.com/dashevo/dapi/compare/v0.13.0...v0.14.0) (2020-07-23) + +### Bug Fixes + +* internal error when `fromBlockHeight` submitted as 0 to `subscribeToTransactionsWithProofs` ([#285](https://github.com/dashevo/dapi/issues/285)) + + +### Features + +* update dependencies (dpp to 0.14.0, dashcore-lib to 0.18.11) ([#283](https://github.com/dashevo/dapi/issues/283)) +* reduce artifical slowdown of the transaction stream ([#275](https://github.com/dashevo/dapi/issues/275)) +* use test-suite to run functional tests ([#276](https://github.com/dashevo/dapi/issues/276), [#280](https://github.com/dashevo/dapi/issues/280)) + + + +# [0.13.0](https://github.com/dashevo/dapi/compare/v0.12.0...v0.13.0) (2020-06-08) + + +### Bug Fixes + +* invalid JSON RPC internal error code ([#271](https://github.com/dashevo/dapi/pull/271)) +* incorrect behaviour on undefined data in `handleAbciResponseError` ([#265](https://github.com/dashevo/dapi/pull/265)) + + +### Features + +* get identity by public key endpoints ([#263](https://github.com/dashevo/dapi/pull/263), [#266](https://github.com/dashevo/dapi/pull/266)) + + +### Tests + +* identity topup functional test ([#268](https://github.com/dashevo/dapi/pull/268)) +* functional for validating public key uniqueness ([#269](https://github.com/dashevo/dapi/pull/269)) + + +### Code Refactoring + +* actualize drive env variables ([#270](https://github.com/dashevo/dapi/pull/270)) + + +### BREAKING CHANGES + +* previously internal errors were respond with wrong error code `-32602` (invalid argument). The error code is changed + to `-32603` (internal error). +* see [DPP breaking changes](https://github.com/dashevo/js-dpp/releases/tag/v0.13.0) + + +# [0.12.0](https://github.com/dashevo/dapi/compare/v0.11.1...v0.12.0) (2020-04-18) + +### Bug Fixes + +* in case of `Timed out waiting for tx to be included in a block` DAPI responds with Internal error ([#258](https://github.com/dashevo/dapi/issues/258)) + +### Code Refactoring + +* remove Platform JSON RPC endpoints ([#256](https://github.com/dashevo/dapi/issues/256)) +* rename `TENDERMINT_CORE_...` envs to `TENDERMINT_RPC_...` ([98c6ad0](https://github.com/dashevo/dapi/commit/98c6ad02c1f8cf2ad76f30bec052f9a1f6eac34f)) +* remove rate limiter errors ([#254]((https://github.com/dashevo/dapi/issues/254))) + +### Features + +* handle insufficient funds ABCI error ([#257](https://github.com/dashevo/dapi/issues/257)) +* update deploy script to tag image for every Semver segment ([#260](https://github.com/dashevo/dapi/issues/260)) +* update according to merge of Drive and Machine ([#255](https://github.com/dashevo/dapi/issues/255), [#259](https://github.com/dashevo/dapi/issues/259)) + +### BREAKING CHANGES + +* `fetchDocuments`, `fetchDataContract`, `fetchIdentity`, `applyStateTransition` JSON RPC endpoints are removed. Use gRPC analogues. +* rename `TENDERMINT_CORE_...` envs to `TENDERMINT_RPC_...` +* see [DPP breaking changes](https://github.com/dashevo/js-dpp/releases/tag/v0.12.0) + + +## [0.11.1](https://github.com/dashevo/dapi/compare/v0.11.0...v0.11.1) (2020-03-17) + +### Bug Fixes + +* throw correct JSON RPC error on invalid Insight params (#252, [52b1276](https://github.com/dashevo/dapi/commit/52b12765b2a369099d7700bdb077a9d6454d99b5)) + + +# [0.11.0](https://github.com/dashevo/dapi/compare/v0.9.0...v0.11.0) (2020-03-09) + +### Bug Fixes + +* Core gRPC service is not initialized ([86dff35](https://github.com/dashevo/dapi/commit/86dff354415669e206e543b3b83704eaf62ceb32)) +* load .env at correct time for tx-filter-stream ([7b091e0](https://github.com/dashevo/dapi/commit/7b091e0cefcd7d6c63829bd6229a0c3e8d4b692f)) +* prevent to update dependencies with major version `0` to minor versions ([ea7de93](https://github.com/dashevo/js-dpp/commit/ea7de9379a38b856f4a7b779786986afacd75b0d)) +* handle errors in `getTransaction` endpoints ([e0d36ae](https://github.com/dashevo/dapi/commit/e0d36aebc717f67e90fc44a2256007031ab2f9ba)) +* handle errors in `sendTransaction` endpoint ([cd2e6c8](https://github.com/dashevo/dapi/commit/cd2e6c821b7e6822c4b582c758eeeae26627b173)) +* handle errors in `getBlock` endpoint ([6d474b4](https://github.com/dashevo/dapi/commit/6d474b46edf5b98f2424b6e20836a6296b5a413e)) +* handle rate, time and resource limit ABCI errors ([4c979a3](https://github.com/dashevo/dapi/commit/4c979a3044bc025352962b35292fceedd2d3e7c9)) +* handle Tendermint errors in applyStateTransition ([f8764e9](https://github.com/dashevo/dapi/commit/f8764e901c09445e66319fc5d2ff7cf8bc0dd7da)) +* "not found" instead of "invalid argument" in gRPC endpoints ([126c929](https://github.com/dashevo/dapi/commit/126c92905d63e2b63f9949d3c58d3a469e680201)) + + +### Features + +* remove insecure API endpoints and code ([11b3df3](https://github.com/dashevo/dapi/commit/11b3df3c3dd0fef9d892320f35745b1b68b5b66c)) +* introduce `generateToAddress` endpoint ([3a2f497](https://github.com/dashevo/dapi/commit/3a2f49737f5cc75c02a3abffb64b2060b14beb39)) +* upgrade DPP to 0.11 ([3b36078](https://github.com/dashevo/dapi/commit/3b360787697d9cfb7f5088058cf11ea12a516c50)) + + +### Tests + +* functional test for `getStatus` endpoint ([3f3ec06](https://github.com/dashevo/dapi/commit/3f3ec0606c3a2b6875fa40c17943ac080bc945eb)) +* forced json rpc client tests ([5259535](https://github.com/dashevo/dapi/commit/52595357bef4ee0c0ed9d704a2232cfa59b9a11c)) + + +### BREAKING CHANGES + +* A ton of insecure endpoints were removed so it's easier to list what left. + * JSON RPC (deprecated) + * `generateToAddress` + * `getAddressSummary` + * `getBestBlockHash` + * `getBlockHash` + * `getMnListDiff` + * `getUTXO` + * Core gRPC + * `subscribeToTransactionsWithProofs` + * `getBlock` + * `getStatus` + * `getTransaction` + * `sendTransaction` + * Platform gRPC + * `applyStateTransition` + * `getDataContract` + * `getDocuments` + * `getIdentity` +* see [DPP breaking changes](https://github.com/dashevo/js-dpp/releases/tag/v0.11.0) diff --git a/packages/dapi/Dockerfile b/packages/dapi/Dockerfile new file mode 100644 index 00000000000..875fc26afab --- /dev/null +++ b/packages/dapi/Dockerfile @@ -0,0 +1,65 @@ +# syntax = docker/dockerfile:1.3 +FROM node:16-alpine as builder + +ARG NODE_ENV=production +ENV NODE_ENV ${NODE_ENV} + +RUN apk update && \ + apk --no-cache upgrade && \ + apk add --no-cache git \ + openssh-client \ + python3 \ + alpine-sdk \ + zeromq-dev + +# Enable corepack https://github.com/nodejs/corepack +RUN corepack enable + +WORKDIR /platform + +# Copy yarn files +COPY .yarn ./.yarn +COPY package.json yarn.lock .yarnrc.yml .pnp.* ./ + +# Copy only necessary packages from monorepo +COPY packages/dapi packages/dapi +COPY packages/dapi-grpc packages/dapi-grpc +COPY packages/js-dpp packages/js-dpp +COPY packages/js-grpc-common packages/js-grpc-common +COPY packages/feature-flags-contract packages/feature-flags-contract +COPY packages/masternode-reward-shares-contract packages/masternode-reward-shares-contract +COPY packages/dpns-contract packages/dpns-contract +COPY packages/dashpay-contract packages/dashpay-contract + +# Print build output +RUN yarn config set enableInlineBuilds true + +# Install DAPI-specific dependencies using previous +# node_modules directory to reuse built binaries +RUN --mount=type=cache,target=/tmp/unplugged \ + cp -R /tmp/unplugged /platform/.yarn/ && \ + yarn workspaces focus --production @dashevo/dapi && \ + cp -R /platform/.yarn/unplugged /tmp/ + + +FROM node:16-alpine + +ARG NODE_ENV=production +ENV NODE_ENV ${NODE_ENV} + +LABEL maintainer="Dash Developers " +LABEL description="DAPI Node.JS" + +# Install ZMQ shared library +RUN apk update && apk add --no-cache zeromq-dev + +# Install latest yarn +RUN yarn set version 3.1.0 + +WORKDIR /platform + +COPY --from=builder /platform /platform + +RUN cp /platform/packages/dapi/.env.example /platform/packages/dapi/.env + +EXPOSE 2500 2501 2510 diff --git a/packages/dapi/LICENSE b/packages/dapi/LICENSE new file mode 100644 index 00000000000..d292663c94c --- /dev/null +++ b/packages/dapi/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2018 Dash Core Group, Inc. + +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/packages/dapi/README.md b/packages/dapi/README.md new file mode 100644 index 00000000000..27a53d27e26 --- /dev/null +++ b/packages/dapi/README.md @@ -0,0 +1,69 @@ +# DAPI + +[![Build Status](https://github.com/dashevo/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashevo/platform/actions/workflows/release.yml) +[![API stability](https://img.shields.io/badge/stability-stable-green.svg)](https://nodejs.org/api/documentation.html#documentation_stability_index) + +A decentralized API for the Dash network + +## Table of Contents +- [Install](#install) + - [Dependencies](#dependencies) +- [Usage](#usage) +- [Configuration](#configuration) +- [Making requests](#making-basic-requests) +- [API Reference](#api-reference) +- [Contributing](#contributing) +- [License](#license) + +## Install + +```sh +npm install +``` + +### Dependencies + +DAPI targets the latest LTS release of Node.js. Currently, this is Node v10.13. + +DAPI requires the latest version of [dashcore](https://github.com/dashevo/dash-evo-branches/tree/evo) with Evolution features (special branch repo). + +1. **Install core.** You can use the docker image (`dashcore:evo`) or clone code from [the repository](https://github.com/dashevo/dash-evo-branches/tree/evo), switch to the `evo` branch, and build it by yourself. Note: you need to build image with ZMQ and wallet support. You can follow the build instructions located [here](https://github.com/dashevo/dash-evo-branches/tree/evo/doc) +2. **Configure core.** DAPI needs dashcore's ZMQ interface to be exposed and all indexes enabled. You can find the example config for dashcore [here](doc/dependencies_configs/dash.conf). To start dashcore process with this config, copy it somewhere to your system, and then run `./src/dashd -conf=/path/to/your/config`. + +## Usage + +After you've installed all the dependencies, you can start DAPI by running the `npm start` command inside the DAPI repo directory. + +```sh +npm start +``` + +## Configuration + +DAPI is configured via environment variables either explicitly passed or present in the `.env` dotfile. For example, to change the DAPI port, execute DAPI with the following arguments: `RPC_SERVER_PORT=3010 npm start`. Consult the sample environment [file](.env.example). You can see the full list of available options [here](doc/CONFIGURATION.md). + +## Making basic requests + +DAPI uses [JSON-RPC 2.0](https://www.jsonrpc.org/specification) as the main interface. If you want to confirm that DAPI is functioning and synced, you can request the best block height. + +Send the following json to your DAPI instance: + +```json +{"jsonrpc": "2.0","method": "getBestBlockHeight", "id": 1} +``` + +Note that you always need to specify an id, otherwise the server will respond with an empty body, as mentioned in the [spec](https://www.jsonrpc.org/specification#notification). + +## API Reference + +A list of all available RPC commands, along with their various arguments and expected responses can be found [here](doc/REFERENCE.md) + +Implementation of these commands can be viewed [here](lib/rpcServer/commands). + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs. + +## License + +[MIT](LICENSE) © Dash Core Group, Inc. diff --git a/packages/dapi/doc/CONFIGURATION.md b/packages/dapi/doc/CONFIGURATION.md new file mode 100644 index 00000000000..068e72f519b --- /dev/null +++ b/packages/dapi/doc/CONFIGURATION.md @@ -0,0 +1,26 @@ +[Back to the main page](/README.md) + +# DAPI configuration + +DAPI is configured via environment variables. So, for example, in order to change rpc server port, you need to run `RPC_SERVER_PORT=3010 npm start`. + +## Full list of available options + +* `LIVENET` - boolean. Set to true if you are going to run DAPI on the livenet. Defaults to `false`. +* `RPC_SERVER_PORT` - integer. Port on which DAPI server will listen. Defaults to `3000` +* `DASHCORE_RPC_PROTOCOL` string. Protocol for connecting to dashcore RPC. Defaults to `http` +* `DASHCORE_RPC_USER`. Defaults to `dashrpc` +* `DASHCORE_RPC_PASS`. Defaults to `password` +* `DASHCORE_RPC_HOST`. Defaults to `127.0.0.1` +* `DASHCORE_RPC_PORT`. Defaults to `30002` +* `DASHCORE_ZMQ_HOST`. Defaults to `127.0.0.1` +* `DASHCORE_ZMQ_PORT`. Defaults to `30003` +* `DASHCORE_P2P_HOST`. Defaults to `127.0.0.1` +* `DASHCORE_P2P_PORT`. Defaults to `30001` +* `DRIVE_RPC_HOST`. Defaults to `127.0.0.1` +* `DRIVE_RPC_PORT`. Defaults to `6000` +* `DASHCORE_P2P_NETWORK`. Can be `testnet`, `regtest` and `livenet`. Defaults to `testnet` +* `NETWORK` Can be `testnet`, `regtest` and `livenet` Defaults to `testnet` +* `BLOOM_FILTER_PERSISTENCE_TIMEOUT` - integer. Bloom filter persistence timeout in milliseconds. Defaults to 1 minute. + +[Back to the main page](/README.md) diff --git a/packages/dapi/doc/README.md b/packages/dapi/doc/README.md new file mode 100644 index 00000000000..ea87395ae1b --- /dev/null +++ b/packages/dapi/doc/README.md @@ -0,0 +1,7 @@ +# DAPI documentation. + +This is the home for the DAPI documentation. + +### Contents + +* [Configuration](./CONFIGURATION.md) \ No newline at end of file diff --git a/packages/dapi/doc/REFERENCE.md b/packages/dapi/doc/REFERENCE.md new file mode 100644 index 00000000000..c3411afc8aa --- /dev/null +++ b/packages/dapi/doc/REFERENCE.md @@ -0,0 +1,82 @@ +[Back to the main page](/README.md) + +## API Reference + +### Table of Contents + +- [Layer 1 endpoints](#layer-1-endpoints) + + - [generate](#generate) + - [getBestBlockHash](#getbestblockhash) + - [getBlockHash](#getblockhash) + - [getMnListDiff](#getmnlistdiff) + +## Layer 1 endpoints + +### generate + +Note: regtest only + +Generates blocks on demand for regression tests. + +##### Params + +| name | type | description | +|---------|--------|----------------------------------------| +| args.amount | number | Amount of blocks to generate | + +##### Response + +| name | type | description | +|--------------|------------------------|------------------------------------------------------------| +| blockHashes | promise (string array) | Returns a promise containing strings of block hashes | + +--- + +### getBestBlockHash + +Returns best block hash (hash of the chaintip) + +*takes no arguments* + +##### Response + +| name | type | description | +|--------------|------------------|----------------------------------------| +| blockHash | promise (string) | hash of chaintip | + +--- + +### getBlockHash + +Returns block hash for a given height. + +##### Params + +| name | type | description | +|--------------|--------|----------------------------------------| +| args.height | number | block height | + +##### Response + +| name | type | description | +|------------|------------------|---------------------------------------------| +| blockHash | promise (string) | promise containing a string of a block hash | + +--- + +### getMnListDiff + +*needs definition* + +##### Params + +| name | type | description | +|---------|--------|----------------------------------------| +| packet | string | ST Packet object serialized using CBOR | + +##### Response + +| name | type | description | +|---------|--------|----------------------------------------| +| packet | string | ST Packet object serialized using CBOR | diff --git a/packages/dapi/doc/dependencies_configs/dash.conf b/packages/dapi/doc/dependencies_configs/dash.conf new file mode 100755 index 00000000000..f81fa8ae390 --- /dev/null +++ b/packages/dapi/doc/dependencies_configs/dash.conf @@ -0,0 +1,33 @@ +# general +daemon=0 # set it to 1 if you want to run dash as a daemon process +logtimestamps=1 +maxconnections=256 +debug=0 +printtoconsole=1 + +# Enabling indices +txindex=1 +addressindex=1 +timestampindex=1 +spentindex=1 + +# Enabling ZeroMQ notifications +zmqpubrawtx=tcp://0.0.0.0:30003 +zmqpubrawtxlock=tcp://0.0.0.0:30003 +zmqpubhashblock=tcp://0.0.0.0:30003 +#zmqpubhashtx=tcp://0.0.0.0:30003 +#zmqpubhashtxlock=tcp://0.0.0.0:30003 +#zmqpubrawblock=tcp://0.0.0.0:30003 + +# JSONRPC +server=1 +rpcuser=dashrpc +rpcpassword=password +rpcport=30002 +rpcbind=0.0.0.0 +rpcallowip=0.0.0.0/0 +rpcworkqueue=64 + +# external network +listen=1 +bind=0.0.0.0 diff --git a/packages/dapi/doc/swaggerDef.js b/packages/dapi/doc/swaggerDef.js new file mode 100644 index 00000000000..7f1bee9b3f1 --- /dev/null +++ b/packages/dapi/doc/swaggerDef.js @@ -0,0 +1,75 @@ +/** +* This file is used by swagger-jsdoc as the root document object of the +* OpenAPI document (https://swagger.io/specification/#oasObject). +* +* The file is only used as input to the swagger-jsdoc CLI application when +* generating the OAS API documentation (`swagger-jdsoc -d ...`). +*/ + +module.exports = { + openapi: '3.0.0', + 'x-api-id': 'dapi', + info: { + title: 'DAPI', + version: '0.2.2', + description: 'Dash Decentralized API (DAPI)', + }, + servers: [ + { + url: '{url}:{port}', + description: 'User-defined network', + variables: { + url: { + default: 'http://dapi.dash.org', + }, + port: { + default: '3000', + }, + }, + }, + ], + paths: {}, + /** Readme.io swagger extensions + * ------------------------------ + * + * x-send-defaults (default: false) + * - Whether to send the defaults specified in your swagger file, or render + * them as placeholders + */ + 'x-send-defaults': true, + /** + * x-headers (default: undefined) + * - Array of static headers to add to each request. Must be provided as an + * array of JSON objects with `key` and `value` properties. + */ + 'x-headers': [], + /** + * x-explorer-enabled (default: true) + * - Enable the API explorer + */ + 'x-explorer-enabled': true, + /** + * x-proxy-enabled (default: true) + * - Whether the Readme CORs proxy is enabled or not. If your API correctly + * returns CORs headers, you can safely turn this off. + */ + 'x-proxy-enabled': true, + /** + * x-samples-enabled (default: true) + * - Enable code examples + */ + 'x-samples-enabled': true, + /** + * x-samples-language + * - Languages to generate code samples for + * Default: ['curl', 'node', 'ruby', 'javascript', 'python'] + * Supported: node, curl, ruby, javascript, objectivec, python, java, php, csharp, swift, go + */ + 'x-samples-languages': [ + 'curl', + 'node', + 'ruby', + 'javascript', + 'python', + ], +}; diff --git a/packages/dapi/lib/bloomFilter/emitter/BloomFilterEmitter.js b/packages/dapi/lib/bloomFilter/emitter/BloomFilterEmitter.js new file mode 100644 index 00000000000..c10fc2b8071 --- /dev/null +++ b/packages/dapi/lib/bloomFilter/emitter/BloomFilterEmitter.js @@ -0,0 +1,38 @@ +const { EventEmitter } = require('events'); + +class BloomFilterEmitter extends EventEmitter { + /** + * @param {BloomFilter} bloomFilter + * @param {testFunction} testFunction + */ + constructor(bloomFilter, testFunction) { + super(); + + this.bloomFilter = bloomFilter; + this.testFunction = testFunction; + } + + /** + * Test data against bloom filter + * + * @param {*} data + * @return {boolean} + */ + test(data) { + const result = this.testFunction(this.bloomFilter, data); + + if (result) { + this.emit('match', data); + } + + return result; + } +} + +/** + * @typedef testFunction + * @param {BloomFilter} filter + * @param {*} data + */ + +module.exports = BloomFilterEmitter; diff --git a/packages/dapi/lib/bloomFilter/emitter/BloomFilterEmitterCollection.js b/packages/dapi/lib/bloomFilter/emitter/BloomFilterEmitterCollection.js new file mode 100644 index 00000000000..5500e01edd0 --- /dev/null +++ b/packages/dapi/lib/bloomFilter/emitter/BloomFilterEmitterCollection.js @@ -0,0 +1,59 @@ +/** + * @class BloomFilterEmitterCollection + * @property {BloomFilterEmitter} filters + */ +class BloomFilterEmitterCollection { + constructor() { + this.filters = new Set(); + } + + /** + * Add bloom filter + * + * @param {BloomFilterEmitter} bloomFilterEmitter + * @return {BloomFilterEmitterCollection} + */ + add(bloomFilterEmitter) { + this.filters.add(bloomFilterEmitter); + + return this; + } + + /** + * Remove bloom filter + * + * @param {BloomFilterEmitter} bloomFilterEmitter + * @return {BloomFilterEmitterCollection} + */ + remove(bloomFilterEmitter) { + this.filters.delete(bloomFilterEmitter); + + return this; + } + + /** + * Test data against bloom filters + * + * @param {*} data + * @return {BloomFilterEmitterCollection} + */ + test(data) { + this.filters.forEach((filter) => { + filter.test(data); + }); + } + + /** + * Emit event on all bloom filters + * + * @param {string} event + * @param {*} data + */ + emit(event, data) { + this.filters.forEach((filter) => { + filter.emit(event, data); + }); + } +} + +module.exports = BloomFilterEmitterCollection; diff --git a/packages/dapi/lib/chainDataProvider/BlockHeadersCache.js b/packages/dapi/lib/chainDataProvider/BlockHeadersCache.js new file mode 100644 index 00000000000..1a7c068326b --- /dev/null +++ b/packages/dapi/lib/chainDataProvider/BlockHeadersCache.js @@ -0,0 +1,28 @@ +const LRU = require('lru-cache'); +const config = require('../config'); + +const options = { + max: config.blockHeaders.cache.maxSize, + maxAge: config.blockHeaders.cache.maxAge, + length: (n) => n && n.length, +}; + +class BlockHeadersCache { + constructor() { + this.cache = new LRU(options); + } + + get(key) { + return this.cache.get(key); + } + + set(key, value) { + this.cache.set(key, value); + } + + purge() { + this.cache.reset(); + } +} + +module.exports = BlockHeadersCache; diff --git a/packages/dapi/lib/chainDataProvider/ChainDataProvider.js b/packages/dapi/lib/chainDataProvider/ChainDataProvider.js new file mode 100644 index 00000000000..be7a6b25318 --- /dev/null +++ b/packages/dapi/lib/chainDataProvider/ChainDataProvider.js @@ -0,0 +1,172 @@ +const ChainLockSigMessage = require('@dashevo/dashcore-lib/lib/zmqMessages/ChainLockSigMessage'); +const { EventEmitter } = require('events'); +const { BlockHeader, ChainLock } = require('@dashevo/dashcore-lib'); +const log = require('../log'); + +/** + * Data access layer with caching support + */ +class ChainDataProvider extends EventEmitter { + /** + * + * @param coreRpcClient {CoreRpcClient} + * @param zmqClient {ZmqClient} + * @param blockHeadersCache {BlockHeadersCache} + */ + constructor(coreRpcClient, zmqClient, blockHeadersCache) { + super(); + + this.coreRpcAPI = coreRpcClient; + this.zmqClient = zmqClient; + this.blockHeadersCache = blockHeadersCache; + + this.chainLock = null; + } + + /** + * @private + * @param blockHash {Buffer} + */ + blockHashHandler(blockHash) { + this.emit(this.events.NEW_BLOCK_HEADER, blockHash.toString('hex')); + } + + /** + * @private + * @param rawChainLock {Object|ChainLock} JSON-object from getBestChainLock or ChainLock instance + */ + chainLockHandler(rawChainLock) { + const chainLock = new ChainLock(rawChainLock); + + this.chainLock = chainLock; + + this.emit(this.events.NEW_CHAIN_LOCK, chainLock); + } + + /** + * + * @param {Buffer} rawChainLockSigBuffer + */ + rawChainLockSigHandler(rawChainLockSigBuffer) { + try { + const { chainLock } = new ChainLockSigMessage(rawChainLockSigBuffer); + + this.chainLockHandler(chainLock); + } catch (e) { + // eslint-disable no-empty + } + } + + /** + * Grabs most recent chainlock + * @returns {Promise} + */ + async init() { + try { + const chainLock = await this.coreRpcAPI.getBestChainLock(); + + this.chainLockHandler(chainLock); + } catch (e) { + if (e.code === -32603) { + log.info('No chain lock available in dashcore node'); + } else { + throw e; + } + } + + this.zmqClient.on(this.zmqClient.topics.rawchainlocksig, + (buffer) => this.rawChainLockSigHandler(buffer)); + this.zmqClient.on(this.zmqClient.topics.hashblock, + (buffer) => this.blockHashHandler(buffer)); + } + + /** + * Get block hash by height + * @param height {number} + * @returns {Promise} + */ + async getBlockHash(height) { + return this.coreRpcAPI.getBlockHash(height); + } + + /** + * Get block header by block hash + * @param blockHash {string} + * @returns {Promise} + */ + async getBlockHeader(blockHash) { + const cached = this.blockHeadersCache.get(blockHash); + + if (cached) { + return cached; + } + + const rawBlockHeader = await this.coreRpcAPI.getBlockHeader(blockHash); + const blockHeaderBuffer = Buffer.from(rawBlockHeader, 'hex'); + const blockHeader = new BlockHeader(blockHeaderBuffer); + + this.blockHeadersCache.set(blockHash, blockHeader); + + return new BlockHeader(blockHeaderBuffer); + } + + /** + * Receive set of block headers with cache support + * @param fromHash {string} + * @param fromHeight {number} + * @param count {number} + * @returns {Promise} + */ + async getBlockHeaders(fromHash, fromHeight, count) { + let startHash = fromHash; + let fetchCount = count; + + const blockHeights = Array.from({ length: count }) + .map((e, i) => fromHeight + i); + + const cachedBlockHeaders = blockHeights + .map((blockHeight) => this.blockHeadersCache.get(blockHeight)); + const [firstCachedItem] = cachedBlockHeaders; + + let lastCachedIndex = -1; + + if (firstCachedItem) { + const firstMissingIndex = cachedBlockHeaders.indexOf(undefined); + + if (firstMissingIndex !== -1) { + lastCachedIndex = firstMissingIndex - 1; + + const blockHeader = cachedBlockHeaders[lastCachedIndex]; + + startHash = blockHeader.hash; + fetchCount -= lastCachedIndex; + } else { + // return cache if we do not miss anything + return cachedBlockHeaders; + } + } + + const missingBlockHeaders = await this.coreRpcAPI.getBlockHeaders(startHash, fetchCount); + const rawBlockHeaders = [...((cachedBlockHeaders.slice(0, + lastCachedIndex !== -1 ? lastCachedIndex : 0)).map((e) => e.toString('hex'))), ...missingBlockHeaders]; + + missingBlockHeaders.forEach((e, i) => this.blockHeadersCache.set(fromHeight + i, new BlockHeader(Buffer.from(e, 'hex')))); + + return rawBlockHeaders.map((rawBlockHeader) => new BlockHeader(Buffer.from(rawBlockHeader, 'hex'))); + } + + /** + * Return best chain lock + * @returns {ChainLock|null} + */ + getBestChainLock() { + return this.chainLock; + } +} + +ChainDataProvider.prototype.events = { + NEW_BLOCK_HEADER: 'NEW_BLOCK_HEADER', + NEW_CHAIN_LOCK: 'NEW_CHAIN_LOCK', +}; + +module.exports = ChainDataProvider; diff --git a/packages/dapi/lib/config/index.js b/packages/dapi/lib/config/index.js new file mode 100644 index 00000000000..659404d9234 --- /dev/null +++ b/packages/dapi/lib/config/index.js @@ -0,0 +1,101 @@ +const OPTIONS = { + LIVENET: 'LIVENET', + API_JSON_RPC_PORT: 'API_JSON_RPC_PORT', + API_GRPC_PORT: 'API_GRPC_PORT', + TX_FILTER_STREAM_GRPC_PORT: 'TX_FILTER_STREAM_GRPC_PORT', + DASHCORE_RPC_PROTOCOL: 'DASHCORE_RPC_PROTOCOL', + DASHCORE_RPC_USER: 'DASHCORE_RPC_USER', + DASHCORE_RPC_PASS: 'DASHCORE_RPC_PASS', + DASHCORE_RPC_HOST: 'DASHCORE_RPC_HOST', + DASHCORE_RPC_PORT: 'DASHCORE_RPC_PORT', + DASHCORE_ZMQ_HOST: 'DASHCORE_ZMQ_HOST', + DASHCORE_ZMQ_PORT: 'DASHCORE_ZMQ_PORT', + DASHCORE_P2P_HOST: 'DASHCORE_P2P_HOST', + DASHCORE_P2P_PORT: 'DASHCORE_P2P_PORT', + DASHCORE_P2P_NETWORK: 'DASHCORE_P2P_NETWORK', + DRIVE_RPC_HOST: 'DRIVE_RPC_HOST', + DRIVE_RPC_PORT: 'DRIVE_RPC_PORT', + BLOCK_HEADERS_CACHE_SIZE: 'BLOCK_HEADERS_CACHE_SIZE', + NETWORK: 'NETWORK', + BLOOM_FILTER_PERSISTENCE_TIMEOUT: 'BLOOM_FILTER_PERSISTENCE_TIMEOUT', + TENDERMINT_RPC_HOST: 'TENDERMINT_RPC_HOST', + TENDERMINT_RPC_PORT: 'TENDERMINT_RPC_PORT', +}; + +const DEFAULT_CONFIG = {}; + +DEFAULT_CONFIG[OPTIONS.LIVENET] = false; +DEFAULT_CONFIG[OPTIONS.API_JSON_RPC_PORT] = 2501; +DEFAULT_CONFIG[OPTIONS.API_GRPC_PORT] = 2500; +DEFAULT_CONFIG[OPTIONS.TX_FILTER_STREAM_GRPC_PORT] = 2510; +DEFAULT_CONFIG[OPTIONS.DASHCORE_RPC_PROTOCOL] = 'http'; +DEFAULT_CONFIG[OPTIONS.DASHCORE_RPC_USER] = 'dashrpc'; +DEFAULT_CONFIG[OPTIONS.DASHCORE_RPC_PASS] = 'password'; +DEFAULT_CONFIG[OPTIONS.DASHCORE_RPC_HOST] = '127.0.0.1'; +DEFAULT_CONFIG[OPTIONS.DASHCORE_RPC_PORT] = 30002; +DEFAULT_CONFIG[OPTIONS.DASHCORE_ZMQ_HOST] = '127.0.0.1'; +DEFAULT_CONFIG[OPTIONS.DASHCORE_ZMQ_PORT] = 30003; +DEFAULT_CONFIG[OPTIONS.DASHCORE_P2P_HOST] = '127.0.0.1'; +DEFAULT_CONFIG[OPTIONS.DASHCORE_P2P_PORT] = 30001; +DEFAULT_CONFIG[OPTIONS.DASHCORE_P2P_NETWORK] = 'testnet'; +DEFAULT_CONFIG[OPTIONS.DRIVE_RPC_HOST] = '127.0.0.1'; +DEFAULT_CONFIG[OPTIONS.DRIVE_RPC_PORT] = 6000; +DEFAULT_CONFIG[OPTIONS.BLOCK_HEADERS_CACHE_SIZE] = 500; +DEFAULT_CONFIG[OPTIONS.NETWORK] = 'testnet'; +DEFAULT_CONFIG[OPTIONS.BLOOM_FILTER_PERSISTENCE_TIMEOUT] = 1000 * 60; + +const envConfig = {}; +Object + .keys(OPTIONS) + .forEach((optionName) => { + if (process.env[optionName]) { + envConfig[optionName] = process.env[optionName]; + } + }); + +const config = { ...DEFAULT_CONFIG, ...envConfig }; + +module.exports = { + livenet: Boolean(config[OPTIONS.LIVENET]), + rpcServer: { + port: parseInt(config[OPTIONS.API_JSON_RPC_PORT], 10), + }, + grpcServer: { + port: parseInt(config[OPTIONS.API_GRPC_PORT], 10), + }, + txFilterStream: { + grpcServer: { + port: parseInt(config[OPTIONS.TX_FILTER_STREAM_GRPC_PORT], 10), + }, + }, + dashcore: { + rpc: { + protocol: config[OPTIONS.DASHCORE_RPC_PROTOCOL], + user: config[OPTIONS.DASHCORE_RPC_USER], + pass: config[OPTIONS.DASHCORE_RPC_PASS], + host: config[OPTIONS.DASHCORE_RPC_HOST], + port: parseInt(config[OPTIONS.DASHCORE_RPC_PORT], 10), + }, + zmq: { + host: config[OPTIONS.DASHCORE_ZMQ_HOST], + port: parseInt(config[OPTIONS.DASHCORE_ZMQ_PORT], 10), + }, + p2p: { + host: config[OPTIONS.DASHCORE_P2P_HOST], + port: parseInt(config[OPTIONS.DASHCORE_P2P_PORT], 10), + network: config[OPTIONS.DASHCORE_P2P_NETWORK], + }, + }, + network: config[OPTIONS.NETWORK].toLowerCase(), + bloomFilterPersistenceTimeout: config[OPTIONS.BLOOM_FILTER_PERSISTENCE_TIMEOUT], + tendermintCore: { + host: config[OPTIONS.TENDERMINT_RPC_HOST], + port: parseInt(config[OPTIONS.TENDERMINT_RPC_PORT], 10), + }, + blockHeaders: { + cache: { + maxSize: Number(config[OPTIONS.BLOCK_HEADERS_CACHE_SIZE]), + maxAge: 1000 * 60 * 60, + }, + }, +}; diff --git a/packages/dapi/lib/config/validator.js b/packages/dapi/lib/config/validator.js new file mode 100644 index 00000000000..e35a8d7cb24 --- /dev/null +++ b/packages/dapi/lib/config/validator.js @@ -0,0 +1,67 @@ +const { isUnsignedInteger } = require('@dashevo/dashcore-lib').util.js; + +/** + * @param host + * @param parameterName + * @returns {{isValid: boolean, validationError: null|string}} + */ +function validateHost(host, parameterName) { + const validationResult = { + isValid: typeof host === 'string' && host.length > 0, + validationError: null, + }; + if (!validationResult.isValid) { + validationResult.validationError = `${parameterName} value is not valid. Valid host or ip address expected, found: ${host}`; + } + return validationResult; +} + +/** + * @param {number|string} port + * @param {string} parameterName + * @returns {{isValid: boolean, validationError: null|string}} + */ +function validatePort(port, parameterName) { + const validationResult = { + isValid: isUnsignedInteger(Number(port)) && Number(port) <= 65535, + validationError: null, + }; + if (!validationResult.isValid) { + validationResult.validationError = `${parameterName} value is not valid. Valid port expected, found: ${port}`; + } + return validationResult; +} + +/** + * @param {Object} config + * @returns {{isValid: boolean, validationErrors: (string|null)[]}} + */ +function validateConfig(config) { + const validationResults = []; + validationResults.push(validateHost(config.dashcore.p2p.host, 'DASHCORE_P2P_HOST')); + validationResults.push(validatePort(config.dashcore.p2p.port, 'DASHCORE_P2P_PORT')); + validationResults.push(validateHost(config.dashcore.rpc.host, 'DASHCORE_RPC_HOST')); + validationResults.push(validatePort(config.dashcore.rpc.port, 'DASHCORE_RPC_PORT')); + validationResults.push(validateHost(config.dashcore.zmq.host, 'DASHCORE_ZMQ_HOST')); + validationResults.push(validatePort(config.dashcore.zmq.port, 'DASHCORE_ZMQ_PORT')); + validationResults.push(validateHost(config.tendermintCore.host, 'TENDERMINT_RPC_HOST')); + validationResults.push(validatePort(config.tendermintCore.port, 'TENDERMINT_RPC_PORT')); + validationResults.push(validatePort(config.rpcServer.port.toString(), 'API_JSON_RPC_PORT')); + validationResults.push(validatePort(config.grpcServer.port.toString(), 'API_GRPC_PORT')); + validationResults.push(validatePort(config.txFilterStream.grpcServer.port.toString(), 'TX_FILTER_STREAM_GRPC_PORT')); + + const validationErrors = validationResults + .filter((validationResult) => !validationResult.isValid) + .map((validationResult) => validationResult.validationError); + + return { + isValid: validationErrors.length < 1, + validationErrors, + }; +} + +module.exports = { + validateHost, + validatePort, + validateConfig, +}; diff --git a/packages/dapi/lib/dpp/DriveStateRepository.js b/packages/dapi/lib/dpp/DriveStateRepository.js new file mode 100644 index 00000000000..6bceb50d117 --- /dev/null +++ b/packages/dapi/lib/dpp/DriveStateRepository.js @@ -0,0 +1,41 @@ +const { + v0: { + GetDataContractResponse, + }, +} = require('@dashevo/dapi-grpc'); + +/** + * @implements StateRepository + */ +class DriveStateRepository { + /** + * @param {DriveClient} driveClient + * @param {DashPlatformProtocol} dpp + */ + constructor(driveClient, dpp) { + this.driveClient = driveClient; + this.dpp = dpp; + } + + /** + * Fetches data contract from Drive + * @param {Identifier} contractIdentifier + * @return {Promise} + */ + async fetchDataContract(contractIdentifier) { + const dataContractProtoBuffer = await this.driveClient.fetchDataContract( + contractIdentifier, false, + ); + + const dataContractResponse = GetDataContractResponse.deserializeBinary( + dataContractProtoBuffer, + ); + + return this.dpp.dataContract.createFromBuffer( + Buffer.from(dataContractResponse.getDataContract()), + { skipValidation: true }, + ); + } +} + +module.exports = DriveStateRepository; diff --git a/packages/dapi/lib/errors/ArgumentsValidationError.js b/packages/dapi/lib/errors/ArgumentsValidationError.js new file mode 100644 index 00000000000..5fd730ef4cc --- /dev/null +++ b/packages/dapi/lib/errors/ArgumentsValidationError.js @@ -0,0 +1,11 @@ +class ArgumentsValidationError extends Error { + constructor(message, originalStack, data) { + super(message); + if (originalStack) { + this.stack = originalStack; + } + this.data = data; + } +} + +module.exports = ArgumentsValidationError; diff --git a/packages/dapi/lib/errors/DashCoreRpcError.js b/packages/dapi/lib/errors/DashCoreRpcError.js new file mode 100644 index 00000000000..208496e6f4b --- /dev/null +++ b/packages/dapi/lib/errors/DashCoreRpcError.js @@ -0,0 +1,13 @@ +class DashCoreRpcError extends Error { + constructor(message, originalStack, code) { + super(message); + if (originalStack) { + this.stack = originalStack; + } + if (code) { + this.code = code; + } + } +} + +module.exports = DashCoreRpcError; diff --git a/packages/dapi/lib/errors/TransactionWaitPeriodExceededError.js b/packages/dapi/lib/errors/TransactionWaitPeriodExceededError.js new file mode 100644 index 00000000000..4713e599546 --- /dev/null +++ b/packages/dapi/lib/errors/TransactionWaitPeriodExceededError.js @@ -0,0 +1,25 @@ +class TransactionWaitPeriodExceededError extends Error { + /** + * @param {string} transactionHash + * @param originalStack + */ + constructor(transactionHash, originalStack) { + const message = `Transaction waiting period for ${transactionHash} exceeded`; + super(message); + if (originalStack) { + this.stack = originalStack; + } + + this.transactionHash = transactionHash; + } + + /** + * Returns transaction hash + * @return {string} + */ + getTransactionHash() { + return this.transactionHash; + } +} + +module.exports = TransactionWaitPeriodExceededError; diff --git a/packages/dapi/lib/externalApis/dashcore/ZmqClient.js b/packages/dapi/lib/externalApis/dashcore/ZmqClient.js new file mode 100644 index 00000000000..5dc7198605e --- /dev/null +++ b/packages/dapi/lib/externalApis/dashcore/ZmqClient.js @@ -0,0 +1,91 @@ +const { EventEmitter } = require('events'); +const zeromq = require('zeromq'); +const { ZMQ_TOPICS } = require('./constants'); + +const defaultOptions = { topics: ZMQ_TOPICS, maxRetryCount: 20 }; + +class ZmqClient extends EventEmitter { + constructor(host, port, options = defaultOptions) { + super(); + this.subscriberSocket = zeromq.socket('sub'); + this.connectionString = `tcp://${host}:${port}`; + this.topics = options.topics; + this.maxRetryCount = options.maxRetryCount; + this.resetConnectionFailuresCount(); + } + + resetConnectionFailuresCount() { + this.connectionFailuresCount = 0; + } + + /** + * Starts listening to zmq messages + * @returns {Promise} + */ + start() { + return new Promise((resolve) => { + this.subscriberSocket.once('connect', () => resolve()); + this.subscriberSocket.on('connect', () => { + this.resetConnectionFailuresCount(); + }); + this.initErrorHandlers(); + this.initMessageHandlers(); + this.startMonitor(); + this.subscriberSocket.connect(this.connectionString); + }); + } + + /** + * @private + * Starts connection monitor to monitor connection status + */ + startMonitor() { + this.subscriberSocket.monitor(500, 0); + } + + /** + * @private + */ + incrementErrorCount() { + this.connectionFailuresCount += 1; + if (this.connectionFailuresCount >= this.maxRetryCount) { + throw new Error(`Failed to connect to ZMQ after ${this.maxRetryCount} tries`); + } + } + + /** + * Init connection error handlers. Requires connection monitor to be started + */ + initErrorHandlers() { + this.subscriberSocket.on('connect_delay', () => { + this.emit(ZmqClient.events.CONNECTION_DELAY, 'Dashcore ZMQ connection delay'); + this.incrementErrorCount(); + }); + this.subscriberSocket.on('disconnect', () => { + this.emit(ZmqClient.events.DISCONNECTED, 'Dashcore ZMQ connection is lost'); + this.incrementErrorCount(); + }); + this.subscriberSocket.on('monitor_error', (error) => { + this.emit(ZmqClient.events.MONITOR_ERROR, error); + this.incrementErrorCount(); + setTimeout(() => this.startMonitor(), 1000); + }); + } + + /** + * Subscribes to zmq messages + */ + initMessageHandlers() { + Object.keys(this.topics).forEach((key) => this.subscriberSocket.subscribe(this.topics[key])); + this.subscriberSocket.on('message', this.emit.bind(this)); + } +} + +ZmqClient.events = { + CONNECTION_DELAY: 'CONNECTION_DELAY', + DISCONNECTED: 'DISCONNECTED', + MONITOR_ERROR: 'MONITOR_ERROR', + ERROR: 'ERROR', +}; + +module.exports = ZmqClient; diff --git a/packages/dapi/lib/externalApis/dashcore/constants.js b/packages/dapi/lib/externalApis/dashcore/constants.js new file mode 100644 index 00000000000..04094aedc5d --- /dev/null +++ b/packages/dapi/lib/externalApis/dashcore/constants.js @@ -0,0 +1,20 @@ +const constants = { + ZMQ_TOPICS: { + hashtx: 'hashtx', + hashtxlock: 'hashtxlock', + hashblock: 'hashblock', + rawblock: 'rawblock', + rawtx: 'rawtx', + rawtxlock: 'rawtxlock', + rawtxlocksig: 'rawtxlocksig', + rawchainlock: 'rawchainlock', + rawchainlocksig: 'rawchainlocksig', + }, + DASHCORE_RPC_COMMANDS: { + protx: { + diff: 'diff', + }, + }, +}; + +module.exports = constants; diff --git a/packages/dapi/lib/externalApis/dashcore/rpc.js b/packages/dapi/lib/externalApis/dashcore/rpc.js new file mode 100644 index 00000000000..d0c59c326b9 --- /dev/null +++ b/packages/dapi/lib/externalApis/dashcore/rpc.js @@ -0,0 +1,369 @@ +const RpcClient = require('@dashevo/dashd-rpc'); +const DashCoreRpcError = require('../../errors/DashCoreRpcError'); +const constants = require('./constants'); +const config = require('../../config'); + +const client = new RpcClient(config.dashcore.rpc); + +/** + * Layer 1 endpoints + * These functions represent endpoints on the transactional layer + * and can be requested from any random DAPI node. + * Once a DAPI-client is assigned to a quorum it should exclude its quorum nodes + * from the set of nodes serving L1 endpoints for privacy reasons + */ + +function generateToAddress(blocksNumber, address) { + return new Promise((resolve, reject) => { // not exist? + client.generateToAddress(blocksNumber, address, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); + }); +} + +const getBestBlockHash = () => new Promise((resolve, reject) => { + client.getbestblockhash((err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getBestBlockHeight = () => new Promise((resolve, reject) => { + client.getblockcount((err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getBestChainLock = () => new Promise((resolve, reject) => { + client.getbestchainlock((err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getBlock = (hash, isParsed = 1) => new Promise((resolve, reject) => { + client.getblock(hash, isParsed, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getBlockHash = (index) => new Promise((resolve, reject) => { + client.getblockhash(index, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getBlockHeader = (blockHash, verbose = false) => new Promise((resolve, reject) => { + client.getblockheader(blockHash, verbose, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getBlockHeaders = ( + fromBlockHash, + limit = 1, + verbose = false, +) => new Promise((resolve, reject) => { + client.getblockheaders(fromBlockHash, limit, verbose, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getMasternodesList = () => new Promise((resolve, reject) => { + client.masternodelist((err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getMempoolInfo = () => new Promise((resolve, reject) => { + client.getmempoolinfo((err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getRawMemPool = (verbose) => new Promise((resolve, reject) => { + client.getrawmempool(verbose, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getMnListDiff = (baseBlockHash, blockHash) => new Promise((resolve, reject) => { + client.protx(constants.DASHCORE_RPC_COMMANDS.protx.diff, baseBlockHash, blockHash, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getMnSync = (command) => new Promise((resolve, reject) => { + client.mnsync(command, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getMasternode = (command) => new Promise((resolve, reject) => { + client.masternode(command, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getRawTransaction = (txid, verboseMode = 0) => new Promise((resolve, reject) => { + client.getrawtransaction(txid, verboseMode, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getRawBlock = (blockhash) => getBlock(blockhash, false); + +// This is only for in-wallet transaction +const getTransaction = (txid) => new Promise((resolve, reject) => { + client.gettransaction(txid, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getTransactionFirstInputAddress = (txId) => new Promise((resolve, reject) => { + client.gettransaction(txId, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.details.address); + } + }); +}); + +const getUser = (txId) => new Promise((resolve, reject) => { // not exist? + client.getuser(txId, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +// Address indexing needs to be enabled +const getUTXO = (addr) => new Promise((resolve, reject) => { + client.getaddressutxos(addr, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +/** + * + * @param {string} bloomFilter - hex string representing serialized bloom filter + * @param {string} fromBlockHash - block hash as a hex string + * @param {number} [count] - how many blocks to scan. Max 2000 + * @return {Promise} - serialized merkle blocks + */ +const getMerkleBlocks = (bloomFilter, fromBlockHash, count) => new Promise((resolve, reject) => { + client.getMerkleBlocks(bloomFilter, fromBlockHash, count, (error, response) => { + if (error) { + reject(new DashCoreRpcError(error.message)); + } else { + resolve(response.result); + } + }); +}); + +/** + * Layer 2 endpoints + * These functions represent endpoints on the data layer + * and can be requested only from members of the quorum assigned to a specific DAPI-client + */ + +const sendRawTransition = (ts) => new Promise((resolve, reject) => { // not exist? + client.sendrawtransition(ts, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +/** + * Layer 1 or Layer 2 endpoints + * depending on context these functions are either Layer 1 or Layer 2 + * e.g. sendRawTransaction can be used to send a normal tx => Layer 1, + * but can also be used like its alias sendRawTransition to send + * a state transition updating a BU account => Layer 2. + * A DAPI-client will need to know if it has already been assigned + * a quorum in order to choose which set of DAPI nodes to use + * for posting a tx to this endpoint - + * all DAPI nodes or just it's quorum member nodes + */ + +const sendRawTransaction = (tx) => new Promise((resolve, reject) => { + client.sendrawtransaction(tx, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const sendRawIxTransaction = (tx) => new Promise((resolve, reject) => { + client.sendrawtransaction(tx, false, true, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getNetworkInfo = () => new Promise((resolve, reject) => { + client.getnetworkinfo((err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getBlockchainInfo = () => new Promise((resolve, reject) => { + client.getblockchaininfo((err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +const getBlockStats = (hashOrHeight, topics) => new Promise((resolve, reject) => { + client.getblockstats(hashOrHeight, topics, (err, r) => { + if (err) { + reject(new DashCoreRpcError(err.message, null, err.code)); + } else { + resolve(r.result); + } + }); +}); + +/** + * @typedef CoreRpcClient + * @type {{ + * getMempoolInfo: (function(): Promise), + * sendRawTransaction: (function(*=): Promise), + * getBlock: (function(*=, *=): Promise), + * getUser: (function(*=): Promise), + * getUTXO: (function(*=): Promise), + * getBlockHash: (function(*=): Promise), + * getBestBlockHash: (function(): Promise), + * getBestChainLock: (function(): Promise), + * getMnListDiff: (function(*=, *=): Promise), + * getMnSync: (function(*=, *=): Promise), + * getMasternode: (function(*=, *=): Promise), + * getBlockHeaders: (function(*=, *=, *=): Promise), + * getRawTransaction: (function(*=): Promise), + * getTransactionFirstInputAddress: (function(*=): Promise), + * getBlockHeader: (function(*=): Promise), + * getBlockchainInfo: (function(): Promise), + * getNetworkInfo: (function(): Promise), + * sendRawIxTransaction: (function(*=): Promise), + * getRawBlock: (function(*=): (*|Promise)), + * getQuorum: (function(*=): Promise), + * getMasternodesList: (function(): Promise), + * getBestBlockHeight: (function(): Promise), + * sendRawTransition: (function(*=): Promise), + * generateToAddress: (function(*=): Promise), + * getTransaction: (function(*=): Promise), + * getMerkleBlocks: (function(string, string, number): Promise)}} + */ +module.exports = { + generateToAddress, + getBestBlockHash, + getBestBlockHeight, + getBestChainLock, + getBlockHash, + getBlock, + getBlockHeader, + getBlockHeaders, + getBlockStats, + getMasternodesList, + getMempoolInfo, + getMnListDiff, + getMnSync, + getMasternode, + sendRawTransition, + sendRawTransaction, + sendRawIxTransaction, + getRawTransaction, + getRawBlock, + getTransaction, + getTransactionFirstInputAddress, + getUser, + getUTXO, + getMerkleBlocks, + getBlockchainInfo, + getNetworkInfo, + getRawMemPool, +}; diff --git a/packages/dapi/lib/externalApis/drive/DriveClient.js b/packages/dapi/lib/externalApis/drive/DriveClient.js new file mode 100644 index 00000000000..b0af479d3a4 --- /dev/null +++ b/packages/dapi/lib/externalApis/drive/DriveClient.js @@ -0,0 +1,195 @@ +const jayson = require('jayson/promise'); + +const cbor = require('cbor'); + +const RPCError = require('../../rpcServer/RPCError'); +const createGrpcErrorFromDriveResponse = require('../../grpcServer/handlers/createGrpcErrorFromDriveResponse'); + +class DriveClient { + /** + * @param options + * @param {string} options.host + * @param {number} options.port + */ + constructor({ host, port }) { + this.client = jayson.client.http({ host, port }); + } + + /** + * Makes request to Drive and handle response + * + * @param {string} path + * @param {Object} data + * @param {boolean} prove + * + * @return {Promise} + */ + async request(path, data = {}, prove = false) { + const encodedData = cbor.encode(data); + + const requestOptions = { + path, + data: encodedData.toString('hex'), + }; + + requestOptions.prove = prove; + + const { result, error } = await this.client.request( + 'abci_query', + requestOptions, + ); + + // Handle JSON RPC error + if (error) { + throw new RPCError( + error.code || -32602, error.message || 'Internal error', error.data, + ); + } + + // Check and handle ABCI errors + const { response } = result; + + if (response.code === undefined || response.code === 0) { + // no errors found return the serialized response value + return Buffer.from(response.value, 'base64'); + } + + throw createGrpcErrorFromDriveResponse(response.code, response.info); + } + + /** + * Makes request to Drive and handle CBOR'ed response + * + * @param {string} path + * @param {Object} data + * @param {boolean} prove + * + * @return {Promise<{ data: Buffer, [proof]: {rootTreeProof: Buffer, storeTreeProof: Buffer}}>} + */ + async requestCbor(path, data = {}, prove = false) { + const responseBuffer = await this.request(path, data, prove); + + return cbor.decode(responseBuffer); + } + + /** + * Fetch serialized data contract + * + * @param {Buffer|Identifier} contractId + * @param {boolean} prove - include proofs into the response + * + * @return {Promise} + */ + async fetchDataContract(contractId, prove) { + return this.request( + '/dataContracts', + { + id: contractId, + }, + prove, + ); + } + + /** + * Fetch serialized documents + * + * @param {Buffer} contractId + * @param {string} type - Documents type to fetch + * + * @param options + * @param {Object} options.where - Mongo-like query + * @param {Object} options.orderBy - Mongo-like sort field + * @param {number} options.limit - how many objects to fetch + * @param {Buffer} options.startAt - skip documents up to specific document ID + * @param {Buffer} options.startAfter - exclusive skip + * @param {boolean} prove - include proofs into the response + * + * @return {Promise} + */ + async fetchDocuments(contractId, type, options, prove) { + return this.request( + '/dataContracts/documents', + { + ...options, + contractId, + type, + }, + prove, + ); + } + + /** + * Fetch serialized identity + * + * @param {Buffer} id + * @param {boolean} prove - include proofs into the response + * + * @return {Promise} + */ + async fetchIdentity(id, prove) { + return this.request( + '/identities', + { + id, + }, + prove, + ); + } + + /** + * Fetch serialized identities by it's public key hashes + * + * @param {Buffer[]} publicKeyHashes + * @param {boolean} prove - include proofs into the response + * + * @return {Promise} + */ + async fetchIdentitiesByPublicKeyHashes(publicKeyHashes, prove) { + return this.request( + '/identities/by-public-key-hash', + { + publicKeyHashes, + }, + prove, + ); + } + + /** + * Fetch serialized identity ids by it's public key hashes + * + * @param {Buffer[]} publicKeyHashes + * @param {boolean} prove - include proofs into the response + * + * @return {Promise} + */ + async fetchIdentityIdsByPublicKeyHashes(publicKeyHashes, prove) { + return this.request( + '/identities/by-public-key-hash/id', + { + publicKeyHashes, + }, + prove, + ); + } + + /** + * Fetch proofs by ids + * + * @param {{dataContractId: Identifier, documentId: Identifier, type: string}[]} [documents] + * @param {Buffer[]} [identityIds] + * @param {Buffer[]} [dataContractIds] + * @return {Promise<{data: Buffer}>} + */ + async fetchProofs({ documents, identityIds, dataContractIds }) { + return this.requestCbor( + '/proofs', + { + documents, + identityIds, + dataContractIds, + }, + ); + } +} + +module.exports = DriveClient; diff --git a/packages/dapi/lib/externalApis/drive/fetchProofForStateTransitionFactory.js b/packages/dapi/lib/externalApis/drive/fetchProofForStateTransitionFactory.js new file mode 100644 index 00000000000..0dca9a4cd5d --- /dev/null +++ b/packages/dapi/lib/externalApis/drive/fetchProofForStateTransitionFactory.js @@ -0,0 +1,46 @@ +/** + * @param {DriveClient} driveClient + * @return {fetchProofForStateTransition} + */ +function fetchProofForStateTransitionFactory(driveClient) { + /** + * @typedef {fetchProofForStateTransition} + * @param {AbstractStateTransition} stateTransition + * @return {Promise} + */ + async function fetchProofForStateTransition(stateTransition) { + const modifiedIds = stateTransition.getModifiedDataIds(); + + let proof; + let metadata; + if (stateTransition.isDocumentStateTransition()) { + ({ documentsProof: proof, metadata } = await driveClient.fetchProofs( + { + documents: stateTransition.getTransitions().map((documentTransition) => ({ + dataContractId: documentTransition.getDataContractId().toBuffer(), + documentId: documentTransition.getId().toBuffer(), + type: documentTransition.getType(), + })), + }, + )); + } else if (stateTransition.isIdentityStateTransition()) { + ({ identitiesProof: proof, metadata } = await driveClient.fetchProofs( + { + identityIds: modifiedIds.map((identifier) => identifier.toBuffer()), + }, + )); + } else if (stateTransition.isDataContractStateTransition()) { + ({ dataContractsProof: proof, metadata } = await driveClient.fetchProofs( + { + dataContractIds: modifiedIds.map((identifier) => identifier.toBuffer()), + }, + )); + } + + return { proof, metadata }; + } + + return fetchProofForStateTransition; +} + +module.exports = fetchProofForStateTransitionFactory; diff --git a/packages/dapi/lib/externalApis/tenderdash/BlockchainListener.js b/packages/dapi/lib/externalApis/tenderdash/BlockchainListener.js new file mode 100644 index 00000000000..8f5db453647 --- /dev/null +++ b/packages/dapi/lib/externalApis/tenderdash/BlockchainListener.js @@ -0,0 +1,62 @@ +const EventEmitter = require('events'); + +const TX_QUERY = 'tm.event = \'Tx\''; +const NEW_BLOCK_QUERY = 'tm.event = \'NewBlock\''; +const EVENTS = { + NEW_BLOCK: 'block', +}; + +class BlockchainListener extends EventEmitter { + /** + * @param {WsClient} tenderdashWsClient + */ + constructor(tenderdashWsClient) { + super(); + this.wsClient = tenderdashWsClient; + } + + /** + * Returns an event name for a specific hash + * + * @param {string} transactionHashString + * @return {string} + */ + static getTransactionEventName(transactionHashString) { + return `transaction:${transactionHashString}`; + } + + /** + * Subscribe to blocks and transaction results + */ + start() { + // Emit transaction results + this.wsClient.subscribe(TX_QUERY); + this.wsClient.on(TX_QUERY, (message) => { + const [hashString] = (message.events || []).map((event) => { + const hashAttribute = event.attributes.find((attribute) => attribute.key === 'hash'); + + if (!hashAttribute) { + return null; + } + + return hashAttribute.value; + }).filter((hash) => hash !== null); + + if (!hashString) { + return; + } + + this.emit(BlockchainListener.getTransactionEventName(hashString), message); + }); + + // Emit blocks and contained transactions + this.wsClient.subscribe(NEW_BLOCK_QUERY); + this.wsClient.on(NEW_BLOCK_QUERY, (message) => this.emit(EVENTS.NEW_BLOCK, message)); + } +} + +BlockchainListener.TX_QUERY = TX_QUERY; +BlockchainListener.NEW_BLOCK_QUERY = NEW_BLOCK_QUERY; +BlockchainListener.EVENTS = EVENTS; + +module.exports = BlockchainListener; diff --git a/packages/dapi/lib/externalApis/tenderdash/WsClient.js b/packages/dapi/lib/externalApis/tenderdash/WsClient.js new file mode 100644 index 00000000000..15538819786 --- /dev/null +++ b/packages/dapi/lib/externalApis/tenderdash/WsClient.js @@ -0,0 +1,208 @@ +const { EventEmitter } = require('events'); +const WebSocket = require('ws'); + +class WsClient extends EventEmitter { + constructor(options = {}) { + super(); + + const protocol = (options && options.protocol) ? options.protocol.toString() : 'ws'; + const host = (options && options.host) ? options.host.toString() : '0.0.0.0'; + const port = (options && options.port) ? options.port.toString() : '26657'; + const path = (options && options.path) ? options.path.toString() : 'websocket'; + + this.url = `${protocol}://${host}:${port}/${path}`; + this.isConnected = false; + this.autoReconnectInterval = 1000; + this.subscribedQueries = new Map(); + } + + /** + * @private + * @return + */ + open() { + if (this.ws) { + this.disconnect(); + } + + this.ws = new WebSocket(this.url); + + const reconnect = () => { + if (this.connectionRetries <= this.maxRetries || this.maxRetries === -1) { + if (this.maxRetries !== -1) { + this.connectionRetries += 1; + } + + setTimeout(this.open.bind(this), this.autoReconnectInterval); + } else { + this.disconnect(); + + const event = { + type: 'connect:max_retry_exceeded', + address: this.url, + }; + + this.emit(event.type, event); + } + }; + + const onOpenListener = () => { + this.isConnected = true; + + const event = { + type: 'connect', + address: this.url, + }; + + this.emit(event.type, event); + + for (const query of this.subscribedQueries.keys()) { + this.subscribe(query); + } + }; + + const onCloseListener = (e) => { + if (e.code === 1000) { // close normal + this.disconnect(); + + return; + } + + reconnect(); + }; + + const onErrorListener = (e) => { + switch (e.code) { + case 'EAI_AGAIN': + reconnect(); + break; + case 'ECONNREFUSED': + reconnect(); + break; + default: + this.disconnect(); + this.emit('error', e); + break; + } + }; + + const onMessageListener = (rawData) => { + const { result } = JSON.parse(rawData); + + if (result !== undefined && Object.keys(result).length > 0) { + this.emit(result.query, result); + } + }; + + this.ws.on('open', onOpenListener); + this.ws.on('close', onCloseListener); + this.ws.on('error', onErrorListener); + this.ws.on('message', onMessageListener); + } + + /** + * + * @param {object} connectionOptions + * @param {number} connectionOptions.maxRetries + * @return {Promise} + */ + async connect(connectionOptions = {}) { + // by default, we don't set any max number of retries + this.maxRetries = connectionOptions.maxRetries || -1; + this.connectionRetries = 0; + this.subscribedQueries.clear(); + + return new Promise((resolve, reject) => { + // If a max number of retries is set, we reject when exceeding retry number + if (this.maxRetries !== -1) { + this.on('connect:max_retry_exceeded', () => reject(new Error('Connection dropped. Max retries exceeded.'))); + } + + this.open(); + + // We only return socket when we actually established a connection + this.on('connect', () => resolve()); + }); + } + + /** + * + * @return {boolean} + */ + close() { + if (this.ws) { + if (this.isConnected) { + this.disconnect(); + } + + this.ws = null; + this.subscribedQueries.clear(); + + return true; + } + + return false; + } + + disconnect() { + this.ws.removeAllListeners(); + try { + this.ws.terminate(); + } catch (e) { + // do nothing + } + + this.isConnected = false; + } + + /** + * + * @param {string} query + */ + subscribe(query) { + const id = 0; + + const request = { + jsonrpc: '2.0', + method: 'subscribe', + id, + params: { + query, + }, + }; + + this.ws.send(JSON.stringify(request)); + + const count = this.subscribedQueries.get(query) || 0; + this.subscribedQueries.set(query, count + 1); + } + + /** + * + * @param {string} query + */ + unsubscribe(query) { + const count = this.subscribedQueries.get(query) - 1; + + if (count > 0) { + this.subscribedQueries.set(query, count); + } else { + const id = 0; + + const request = { + jsonrpc: '2.0', + method: 'unsubscribe', + id, + params: { + query, + }, + }; + + this.ws.send(JSON.stringify(request)); + + this.subscribedQueries.delete(query); + } + } +} + +module.exports = WsClient; diff --git a/packages/dapi/lib/externalApis/tenderdash/getConsensusParamsFactory.js b/packages/dapi/lib/externalApis/tenderdash/getConsensusParamsFactory.js new file mode 100644 index 00000000000..82242c980d7 --- /dev/null +++ b/packages/dapi/lib/externalApis/tenderdash/getConsensusParamsFactory.js @@ -0,0 +1,51 @@ +const RPCError = require('../../rpcServer/RPCError'); + +/** + * @param {RpcClient} rpcClient + * @return {getConsensusParams} + */ +function getConsensusParamsFactory(rpcClient) { + /** + * @typedef getConsensusParams + * @param {number} [height] + * @returns {Promise<{ + * block: { + * max_bytes: string, + * max_gas: string, + * time_iota_ms: string + * }, + * evidence: { + * max_age_num_blocks: string, + * max_age_duration: string, + * max_bytes: string, + * } + * }>} + */ + async function getConsensusParams(height = undefined) { + const params = {}; + + if (height !== undefined) { + params.height = height.toString(); + } + + const { result, error } = await rpcClient.request('consensus_params', params); + + // Handle JSON RPC error + if (error) { + throw new RPCError( + error.code || -32602, + error.message || 'Internal error', + error.data, + ); + } + + return { + block: result.consensus_params.block, + evidence: result.consensus_params.evidence, + }; + } + + return getConsensusParams; +} + +module.exports = getConsensusParamsFactory; diff --git a/packages/dapi/lib/externalApis/tenderdash/waitForHeightFactory.js b/packages/dapi/lib/externalApis/tenderdash/waitForHeightFactory.js new file mode 100644 index 00000000000..1787c316d9b --- /dev/null +++ b/packages/dapi/lib/externalApis/tenderdash/waitForHeightFactory.js @@ -0,0 +1,41 @@ +const BlockchainListener = require('./BlockchainListener'); + +/** + * @param {BlockchainListener} blockchainListener + */ +function waitForHeightFactory(blockchainListener) { + let currentHeight = 0; + + blockchainListener.on(BlockchainListener.EVENTS.NEW_BLOCK, (message) => { + currentHeight = parseInt(message.data.value.block.header.height, 10); + }); + + /** + * @typedef {waitForHeight} + * @param {number} height + * @return {Promise} + */ + function waitForHeight(height) { + return new Promise((resolve) => { + if (currentHeight >= height) { + resolve(); + + return; + } + + const handler = () => { + if (currentHeight >= height) { + blockchainListener.off(BlockchainListener.EVENTS.NEW_BLOCK, handler); + + resolve(); + } + }; + + blockchainListener.on(BlockchainListener.EVENTS.NEW_BLOCK, handler); + }); + } + + return waitForHeight; +} + +module.exports = waitForHeightFactory; diff --git a/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/getExistingTransactionResult.js b/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/getExistingTransactionResult.js new file mode 100644 index 00000000000..cd0da32728d --- /dev/null +++ b/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/getExistingTransactionResult.js @@ -0,0 +1,43 @@ +const TransactionOkResult = require('./transactionResult/TransactionOkResult'); +const TransactionErrorResult = require('./transactionResult/TransactionErrorResult'); +const RPCError = require('../../../rpcServer/RPCError'); + +/** + * @param {RpcClient} rpcClient + * @return {getExistingTransactionResult} + */ +function getExistingTransactionResultFactory(rpcClient) { + /** + * @typedef {getExistingTransactionResult} + * @param {string} hashString + * @return {Promise} + */ + async function getExistingTransactionResult(hashString) { + const params = { hash: hashString }; + + const { result, error } = await rpcClient.request('tx', params); + + // Handle JSON RPC error + if (error) { + throw new RPCError( + error.code || -32602, + error.message || 'Internal error', + error.data, + ); + } + + const TransactionResultClass = result.tx_result.code === 0 + ? TransactionOkResult + : TransactionErrorResult; + + return new TransactionResultClass( + result.tx_result, + parseInt(result.height, 10), + Buffer.from(result.tx, 'base64'), + ); + } + + return getExistingTransactionResult; +} + +module.exports = getExistingTransactionResultFactory; diff --git a/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/AbstractTransactionResult.js b/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/AbstractTransactionResult.js new file mode 100644 index 00000000000..8771fdb8188 --- /dev/null +++ b/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/AbstractTransactionResult.js @@ -0,0 +1,41 @@ +class AbstractTransactionResult { + /** + * @param {Object} result + * @param {number} height + * @param {Buffer} transaction + */ + constructor(result, height, transaction) { + this.deliverResult = result; + this.height = height; + this.transaction = transaction; + } + + /** + * Get TX result + * + * @return {Object} + */ + getResult() { + return this.deliverResult; + } + + /** + * Get transaction block height + * + * @return {number} + */ + getHeight() { + return this.height; + } + + /** + * Get transaction + * + * @return {Buffer} + */ + getTransaction() { + return this.transaction; + } +} + +module.exports = AbstractTransactionResult; diff --git a/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/TransactionErrorResult.js b/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/TransactionErrorResult.js new file mode 100644 index 00000000000..79b547bdca9 --- /dev/null +++ b/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/TransactionErrorResult.js @@ -0,0 +1,7 @@ +const AbstractTransactionResult = require('./AbstractTransactionResult'); + +class TransactionErrorResult extends AbstractTransactionResult { + +} + +module.exports = TransactionErrorResult; diff --git a/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/TransactionOkResult.js b/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/TransactionOkResult.js new file mode 100644 index 00000000000..0f827dc83ce --- /dev/null +++ b/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/TransactionOkResult.js @@ -0,0 +1,7 @@ +const AbstractTransactionResult = require('./AbstractTransactionResult'); + +class TransactionOkResult extends AbstractTransactionResult { + +} + +module.exports = TransactionOkResult; diff --git a/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionResult.js b/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionResult.js new file mode 100644 index 00000000000..602cb320a81 --- /dev/null +++ b/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionResult.js @@ -0,0 +1,54 @@ +const BlockchainListener = require('../BlockchainListener'); +const TransactionErrorResult = require('./transactionResult/TransactionErrorResult'); +const TransactionOkResult = require('./transactionResult/TransactionOkResult'); + +/** + * @typedef {waitForTransactionResult} + * @param {BlockchainListener} blockchainListener + * @param {string} hashString - Transaction hash string + * @return {{ + * promise: Promise, + * detach: Function + * }} + */ +function waitForTransactionResult(blockchainListener, hashString) { + const topic = BlockchainListener.getTransactionEventName(hashString); + + let handler; + + const promise = new Promise((resolve) => { + handler = ({ data: { value: { TxResult: txResult } } }) => { + blockchainListener.off(topic, handler); + + const { result: deliverResult, tx, height } = txResult; + + const txBuffer = Buffer.from(tx, 'base64'); + + let TransactionResultClass = TransactionOkResult; + if (deliverResult && deliverResult.code !== undefined && deliverResult.code !== 0) { + TransactionResultClass = TransactionErrorResult; + } + + resolve( + new TransactionResultClass( + deliverResult, + parseInt(height, 10), + txBuffer, + ), + ); + }; + + blockchainListener.on(topic, handler); + }); + + const detach = () => { + blockchainListener.off(topic, handler); + }; + + return { + promise, + detach, + }; +} + +module.exports = waitForTransactionResult; diff --git a/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionToBeProvableFactory.js b/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionToBeProvableFactory.js new file mode 100644 index 00000000000..15f39eb9142 --- /dev/null +++ b/packages/dapi/lib/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionToBeProvableFactory.js @@ -0,0 +1,77 @@ +const TransactionWaitPeriodExceededError = require('../../../errors/TransactionWaitPeriodExceededError'); +const TransactionOkResult = require('./transactionResult/TransactionOkResult'); + +/** + * @param {waitForTransactionResult} waitForTransactionResult + * @param {getExistingTransactionResult} getExistingTransactionResult + * @param {waitForHeight} waitForHeight + * @return {waitForTransactionToBeProvable} + */ +function waitForTransactionToBeProvableFactory( + waitForTransactionResult, + getExistingTransactionResult, + waitForHeight, +) { + /** + * Returns result for a transaction or rejects after a timeout + * + * @typedef {waitForTransactionToBeProvable} + * @param {BlockchainListener} blockchainListener + * @param {string} hashString - transaction hash to resolve data for + * @param {number} [timeout] - timeout to reject after + * @return {Promise} + */ + function waitForTransactionToBeProvable(blockchainListener, hashString, timeout = 60000) { + const { + promise: waitForTransactionResultPromise, + detach: detachTransactionResult, + } = waitForTransactionResult(blockchainListener, hashString); + + const existingTransactionResultPromise = getExistingTransactionResult(hashString); + + const transactionResultPromise = Promise.race([ + // Try to fetch existing tx result + existingTransactionResultPromise.then((result) => { + // Do not wait for upcoming result if existing is present + detachTransactionResult(); + + return result; + }).catch((error) => { + // Do not resolve promise and wait for results if transaction is not found + if (error.code === -32603 && error.data.startsWith(`tx (${hashString}) not found`)) { + return new Promise(() => {}); + } + + return Promise.reject(error); + }), + + // Wait for upcoming results if transaction result doesn't not exist yet + waitForTransactionResultPromise, + ]); + + return Promise.race([ + // Wait for transaction results and commitment + transactionResultPromise.then(async (result) => { + if (result instanceof TransactionOkResult) { + await waitForHeight(result.getHeight() + 1); + } + + return result; + }), + + // Throw wait period exceeded error after timeout + new Promise((resolve, reject) => { + setTimeout(() => { + // Detaching handlers + detachTransactionResult(); + + reject(new TransactionWaitPeriodExceededError(hashString)); + }, timeout); + }), + ]); + } + + return waitForTransactionToBeProvable; +} + +module.exports = waitForTransactionToBeProvableFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/ProcessMediator.js b/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/ProcessMediator.js new file mode 100644 index 00000000000..a5aeecbf6db --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/ProcessMediator.js @@ -0,0 +1,13 @@ +const { EventEmitter } = require('events'); + +class ProcessMediator extends EventEmitter {} + +ProcessMediator.EVENTS = { + HISTORICAL_DATA_SENT: 'historicalDataSent', + BLOCK_HEADERS: 'blockHeaders', + CHAIN_LOCK: 'chainLock', + HISTORICAL_BLOCK_HEADERS_SENT: 'historicalBlockHeadersSent', + CLIENT_DISCONNECTED: 'clientDisconnected', +}; + +module.exports = ProcessMediator; diff --git a/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/constants.js b/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/constants.js new file mode 100644 index 00000000000..4b1d64f8c70 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/constants.js @@ -0,0 +1,3 @@ +module.exports = { + NEW_BLOCK_HEADERS_PROPAGATE_INTERVAL: 1000, +}; diff --git a/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/getHistoricalBlockHeadersIteratorFactory.js b/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/getHistoricalBlockHeadersIteratorFactory.js new file mode 100644 index 00000000000..42692341085 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/getHistoricalBlockHeadersIteratorFactory.js @@ -0,0 +1,50 @@ +const MAX_HEADERS_PER_REQUEST = 500; + +/** + * @param {number} batchIndex + * @param {number} numberOfBatches + * @param {number} totalCount + * @return {number} + */ +function getBlocksToScan(batchIndex, numberOfBatches, totalCount) { + const isLastBatch = batchIndex + 1 === numberOfBatches; + return isLastBatch + ? totalCount - batchIndex * MAX_HEADERS_PER_REQUEST + : MAX_HEADERS_PER_REQUEST; +} + +/** + * @param {ChainDataProvider} chainDataProvider + * @return {getHistoricalBlockHeadersIterator} + */ +function getHistoricalBlockHeadersIteratorFactory(chainDataProvider) { + /** + * @typedef getHistoricalBlockHeadersIterator + * @param fromBlockHeight {number} + * @param count {number} + * @return {AsyncIterableIterator} + */ + async function* getHistoricalBlockHeadersIterator( + fromBlockHeight, + count, + ) { + const numberOfBatches = Math.ceil(count / MAX_HEADERS_PER_REQUEST); + + for (let batchIndex = 0; batchIndex < numberOfBatches; batchIndex++) { + const currentHeight = fromBlockHeight + batchIndex * MAX_HEADERS_PER_REQUEST; + + const blocksToScan = getBlocksToScan(batchIndex, numberOfBatches, count); + + const blockHash = await chainDataProvider.getBlockHash(currentHeight); + + const blockHeaders = await chainDataProvider.getBlockHeaders(blockHash, + currentHeight, blocksToScan); + + yield blockHeaders; + } + } + + return getHistoricalBlockHeadersIterator; +} + +module.exports = getHistoricalBlockHeadersIteratorFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/subscribeToBlockHeadersWithChainLocksHandlerFactory.js b/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/subscribeToBlockHeadersWithChainLocksHandlerFactory.js new file mode 100644 index 00000000000..f952c4deb17 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/subscribeToBlockHeadersWithChainLocksHandlerFactory.js @@ -0,0 +1,174 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + NotFoundGrpcError, + }, + stream: { + AcknowledgingWritable, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + BlockHeadersWithChainLocksResponse, + BlockHeaders, + }, +} = require('@dashevo/dapi-grpc'); +const ProcessMediator = require('./ProcessMediator'); +const wait = require('../../../utils/wait'); + +/** + * Prepare and send block headers response + * + * @param {AcknowledgingWritable} call + * @param {BlockHeader[]} blockHeaders + * @returns {Promise} + */ +async function sendBlockHeadersResponse(call, blockHeaders) { + const blockHeadersProto = new BlockHeaders(); + blockHeadersProto.setHeadersList( + blockHeaders.map((blockHeader) => blockHeader.toBuffer()), + ); + + const response = new BlockHeadersWithChainLocksResponse(); + response.setBlockHeaders(blockHeadersProto); + + await call.write(response); +} + +/** + * Prepare and send chain lock response + * + * @param {AcknowledgingWritable} call + * @param {ChainLock} chainLock + * @returns {Promise} + */ +async function sendChainLockResponse(call, chainLock) { + const response = new BlockHeadersWithChainLocksResponse(); + response.setChainLock(chainLock.toBuffer()); + + await call.write(response); +} + +/** + * @param {getHistoricalBlockHeadersIterator} getHistoricalBlockHeadersIterator + * @param {CoreRpcClient} coreAPI + * @param {ChainDataProvider} chainDataProvider + * @param {ZmqClient} zmqClient + * @param {subscribeToNewBlockHeaders} subscribeToNewBlockHeaders + * @return {subscribeToBlockHeadersWithChainLocksHandler} + */ +function subscribeToBlockHeadersWithChainLocksHandlerFactory( + getHistoricalBlockHeadersIterator, + coreAPI, + chainDataProvider, + zmqClient, + subscribeToNewBlockHeaders, +) { + /** + * @typedef subscribeToBlockHeadersWithChainLocksHandler + * @param {grpc.ServerWriteableStream} call + */ + async function subscribeToBlockHeadersWithChainLocksHandler(call) { + const { request } = call; + + const fromBlockHash = Buffer.from(request.getFromBlockHash_asU8()).toString('hex'); + const fromBlockHeight = request.getFromBlockHeight(); + + if (!fromBlockHash && fromBlockHeight === 0) { + throw new InvalidArgumentGrpcError('Minimum value for `fromBlockHeight` is 1'); + } + + const from = fromBlockHash || fromBlockHeight; + const count = request.getCount(); + + const newHeadersRequested = count === 0; + + const acknowledgingCall = new AcknowledgingWritable(call); + + const mediator = new ProcessMediator(); + + mediator.on( + ProcessMediator.EVENTS.BLOCK_HEADERS, + async (blockHeaders) => { + await sendBlockHeadersResponse(acknowledgingCall, blockHeaders); + }, + ); + + mediator.on( + ProcessMediator.EVENTS.CHAIN_LOCK, + async (chainLock) => { + await sendChainLockResponse(acknowledgingCall, chainLock); + }, + ); + + if (newHeadersRequested) { + subscribeToNewBlockHeaders(mediator, chainDataProvider); + } + + let fromBlock; + + try { + fromBlock = await coreAPI.getBlockStats(from, ['height']); + } catch (e) { + if (e.code === -5 || e.code === -8) { + // -5 -> invalid block height or block is not on best chain + // -8 -> block hash not found + throw new NotFoundGrpcError(`Block ${from} not found`); + } + throw e; + } + + const bestBlockHeight = await coreAPI.getBestBlockHeight(); + + const historicalCount = count === 0 ? bestBlockHeight - fromBlock.height + 1 : count; + + if (fromBlock.height + historicalCount > bestBlockHeight + 1) { + throw new InvalidArgumentGrpcError('`count` value exceeds the chain tip'); + } + + const bestChainLock = chainDataProvider.getBestChainLock(); + + if (bestChainLock) { + await sendChainLockResponse(acknowledgingCall, bestChainLock); + } + + const historicalDataIterator = getHistoricalBlockHeadersIterator( + fromBlock.height, + historicalCount, + ); + + for await (const blockHeaders of historicalDataIterator) { + // Wait between the calls to Core just to reduce the load + await wait(50); + + await sendBlockHeadersResponse(acknowledgingCall, blockHeaders); + + if (newHeadersRequested) { + // removing sent headers from cache + mediator.emit( + ProcessMediator.EVENTS.HISTORICAL_BLOCK_HEADERS_SENT, + blockHeaders.map((header) => header.hash), + ); + } + } + + // notify new block headers listener that we've sent historical data + mediator.emit(ProcessMediator.EVENTS.HISTORICAL_DATA_SENT); + + if (!newHeadersRequested) { + call.end(); + } + + call.on('cancelled', () => { + call.end(); + mediator.emit(ProcessMediator.EVENTS.CLIENT_DISCONNECTED); + }); + } + + return subscribeToBlockHeadersWithChainLocksHandler; +} + +module.exports = subscribeToBlockHeadersWithChainLocksHandlerFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/subscribeToNewBlockHeaders.js b/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/subscribeToNewBlockHeaders.js new file mode 100644 index 00000000000..80ab8713da6 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/blockheaders-stream/subscribeToNewBlockHeaders.js @@ -0,0 +1,80 @@ +const ProcessMediator = require('./ProcessMediator'); +const wait = require('../../../utils/wait'); +const { NEW_BLOCK_HEADERS_PROPAGATE_INTERVAL } = require('./constants'); + +/** + * @typedef subscribeToNewBlockHeaders + * @param {ProcessMediator} mediator + * @param {ChainDataProvider} chainDataProvider + */ +function subscribeToNewBlockHeaders(mediator, chainDataProvider) { + const pendingHeadersHashes = new Set(); + + let lastChainLock; + + let isClientConnected = true; + + /** + * @param {string} blockHash + */ + const blockHashHandler = (blockHash) => { + pendingHeadersHashes.add(blockHash); + }; + + /** + * + * @param chainLock {ChainLock} + */ + const chainLockHandler = (chainLock) => { + lastChainLock = chainLock; + }; + + chainDataProvider.on(chainDataProvider.events.NEW_BLOCK_HEADER, blockHashHandler); + chainDataProvider.on(chainDataProvider.events.NEW_CHAIN_LOCK, chainLockHandler); + + mediator.on(ProcessMediator.EVENTS.HISTORICAL_BLOCK_HEADERS_SENT, (hashes) => { + // Remove data from cache by hashes + hashes.forEach((hash) => { + pendingHeadersHashes.delete(hash); + }); + }); + + // Receive an event when all historical data is sent to the user. + mediator.once(ProcessMediator.EVENTS.HISTORICAL_DATA_SENT, async () => { + // TODO: WARNING! If error is thrown within this function, it does not propagate + // and do not fire UnhandledPromiseRejection + + // Run a loop until client is disconnected and send cached as well + // as new data (through the cache) continuously after that. + // Cache is populated from ZMQ events. + while (isClientConnected) { + if (pendingHeadersHashes.size) { + // TODO: figure out whether it's possible to omit new BlockHeader() conversion + // and directly send bytes to the client + const blockHeaders = await Promise.all(Array.from(pendingHeadersHashes) + .map((hash) => chainDataProvider.getBlockHeader(hash))); + + mediator.emit(ProcessMediator.EVENTS.BLOCK_HEADERS, blockHeaders); + pendingHeadersHashes.clear(); + } + + if (lastChainLock) { + mediator.emit(ProcessMediator.EVENTS.CHAIN_LOCK, lastChainLock); + lastChainLock = null; + } + + // TODO: pick a right time interval having in mind that issuance of the block headers + // is not frequent + await wait(NEW_BLOCK_HEADERS_PROPAGATE_INTERVAL); + } + }); + + mediator.once(ProcessMediator.EVENTS.CLIENT_DISCONNECTED, () => { + isClientConnected = false; + mediator.removeAllListeners(); + chainDataProvider.removeListener(chainDataProvider.events.NEW_BLOCK_HEADER, blockHashHandler); + chainDataProvider.removeListener(chainDataProvider.events.NEW_CHAIN_LOCK, chainLockHandler); + }); +} + +module.exports = subscribeToNewBlockHeaders; diff --git a/packages/dapi/lib/grpcServer/handlers/core/broadcastTransactionHandlerFactory.js b/packages/dapi/lib/grpcServer/handlers/core/broadcastTransactionHandlerFactory.js new file mode 100644 index 00000000000..f1155f71b4c --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/core/broadcastTransactionHandlerFactory.js @@ -0,0 +1,87 @@ +const { + v0: { + BroadcastTransactionResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const { + server: { + error: { + AlreadyExistsGrpcError, + InvalidArgumentGrpcError, + FailedPreconditionGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { Transaction } = require('@dashevo/dashcore-lib'); + +/** + * @param {CoreRpcClient} coreRPCClient + * @returns {broadcastTransactionHandler} + */ +function broadcastTransactionHandlerFactory(coreRPCClient) { + /** + * @typedef broadcastTransactionHandler + * @param {Object} call + * @returns {Promise} + */ + async function broadcastTransactionHandler(call) { + const { request } = call; + + const serializedTransactionBinary = request.getTransaction(); + + if (!serializedTransactionBinary) { + throw new InvalidArgumentGrpcError('transaction is not specified'); + } + + const serializedTransaction = Buffer.from(serializedTransactionBinary); + + // check transaction + + let transactionInstance; + try { + transactionInstance = new Transaction(serializedTransaction); + } catch (e) { + throw new InvalidArgumentGrpcError(`invalid transaction: ${e.message}`); + } + + const transactionIsValid = transactionInstance.verify(); + + if (transactionIsValid !== true) { + throw new InvalidArgumentGrpcError(`invalid transaction: ${transactionIsValid}`); + } + + let transactionId; + try { + transactionId = await coreRPCClient.sendRawTransaction(serializedTransaction.toString('hex')); + } catch (e) { + // RPC_DESERIALIZATION_ERROR + // RPC_VERIFY_ERROR + if ([-22, -25].includes(e.code)) { + throw new InvalidArgumentGrpcError(`invalid transaction: ${e.message}`); + } + + // RPC_VERIFY_REJECTED + if (e.code === -26) { + throw new FailedPreconditionGrpcError(`Transaction is rejected: ${e.message}`); + } + + if (e.code === -27) { + // RPC_VERIFY_ALREADY_IN_CHAIN + throw new AlreadyExistsGrpcError(`Transaction already in chain: ${e.message}`); + } + + throw e; + } + + const response = new BroadcastTransactionResponse(); + response.setTransactionId(transactionId); + + return response; + } + + return broadcastTransactionHandler; +} + +module.exports = broadcastTransactionHandlerFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/core/coreHandlersFactory.js b/packages/dapi/lib/grpcServer/handlers/core/coreHandlersFactory.js new file mode 100644 index 00000000000..2d6477f2afa --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/core/coreHandlersFactory.js @@ -0,0 +1,118 @@ +const { + client: { + converters: { + jsonToProtobufFactory, + protobufToJsonFactory, + }, + }, + server: { + jsonToProtobufHandlerWrapper, + error: { + wrapInErrorHandlerFactory, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + BroadcastTransactionRequest, + GetTransactionRequest, + GetStatusRequest, + GetBlockRequest, + pbjs: { + BroadcastTransactionRequest: PBJSBroadcastTransactionRequest, + BroadcastTransactionResponse: PBJSBroadcastTransactionResponse, + GetTransactionRequest: PBJSGetTransactionRequest, + GetTransactionResponse: PBJSGetTransactionResponse, + GetStatusRequest: PBJSGetStatusRequest, + GetStatusResponse: PBJSGetStatusResponse, + GetBlockRequest: PBJSGetBlockRequest, + GetBlockResponse: PBJSGetBlockResponse, + }, + }, +} = require('@dashevo/dapi-grpc'); + +const log = require('../../../log'); + +const getBlockHandlerFactory = require( + './getBlockHandlerFactory', +); +const getStatusHandlerFactory = require( + './getStatusHandlerFactory', +); +const getTransactionHandlerFactory = require( + './getTransactionHandlerFactory', +); +const broadcastTransactionHandlerFactory = require( + './broadcastTransactionHandlerFactory', +); + +/** + * @param {CoreRpcClient} coreRPCClient + * @param {boolean} isProductionEnvironment + * @returns {Object} + */ +function coreHandlersFactory(coreRPCClient, isProductionEnvironment) { + const wrapInErrorHandler = wrapInErrorHandlerFactory(log, isProductionEnvironment); + + // getBlock + const getBlockHandler = getBlockHandlerFactory(coreRPCClient); + const wrappedGetBlock = jsonToProtobufHandlerWrapper( + jsonToProtobufFactory( + GetBlockRequest, + PBJSGetBlockRequest, + ), + protobufToJsonFactory( + PBJSGetBlockResponse, + ), + wrapInErrorHandler(getBlockHandler), + ); + + // getStatus + const getStatusHandler = getStatusHandlerFactory(coreRPCClient); + const wrappedGetStatus = jsonToProtobufHandlerWrapper( + jsonToProtobufFactory( + GetStatusRequest, + PBJSGetStatusRequest, + ), + protobufToJsonFactory( + PBJSGetStatusResponse, + ), + wrapInErrorHandler(getStatusHandler), + ); + + // getTransaction + const getTransactionHandler = getTransactionHandlerFactory(coreRPCClient); + const wrappedGetTransaction = jsonToProtobufHandlerWrapper( + jsonToProtobufFactory( + GetTransactionRequest, + PBJSGetTransactionRequest, + ), + protobufToJsonFactory( + PBJSGetTransactionResponse, + ), + wrapInErrorHandler(getTransactionHandler), + ); + + // broadcastTransaction + const broadcastTransactionHandler = broadcastTransactionHandlerFactory(coreRPCClient); + const wrappedBroadcastTransaction = jsonToProtobufHandlerWrapper( + jsonToProtobufFactory( + BroadcastTransactionRequest, + PBJSBroadcastTransactionRequest, + ), + protobufToJsonFactory( + PBJSBroadcastTransactionResponse, + ), + wrapInErrorHandler(broadcastTransactionHandler), + ); + + return { + getBlock: wrappedGetBlock, + getStatus: wrappedGetStatus, + getTransaction: wrappedGetTransaction, + broadcastTransaction: wrappedBroadcastTransaction, + }; +} + +module.exports = coreHandlersFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/core/getBlockHandlerFactory.js b/packages/dapi/lib/grpcServer/handlers/core/getBlockHandlerFactory.js new file mode 100644 index 00000000000..27bcb946117 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/core/getBlockHandlerFactory.js @@ -0,0 +1,75 @@ +const { + v0: { + GetBlockResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const { + server: { + error: { + InvalidArgumentGrpcError, + NotFoundGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +/** + * @param {CoreRpcClient} coreRPCClient + * @returns {getBlockHandler} + */ +function getBlockHandlerFactory(coreRPCClient) { + /** + * @typedef getBlockHandler + * @param {Object} call + * @return {Promise} + */ + async function getBlockHandler(call) { + const { request } = call; + + const height = request.getHeight(); + let hash = request.getHash(); + + if (!hash && !height) { + throw new InvalidArgumentGrpcError('hash or height is not specified'); + } + + let serializedBlock; + + if (!hash) { + try { + hash = await coreRPCClient.getBlockHash(height); + } catch (e) { + if (e.code === -8) { + // Block height out of range + throw new NotFoundGrpcError('Invalid block height'); + } + if (e.code === -1) { + // Invalid argument (not integer or integer out of range) + throw new InvalidArgumentGrpcError(e.message); + } + + throw e; + } + } + + try { + serializedBlock = await coreRPCClient.getRawBlock(hash); + } catch (e) { + if (e.code === -5) { + throw new NotFoundGrpcError('Block not found'); + } + + throw e; + } + + const response = new GetBlockResponse(); + const serializedBlockBuffer = Buffer.from(serializedBlock, 'hex'); + response.setBlock(serializedBlockBuffer); + + return response; + } + + return getBlockHandler; +} + +module.exports = getBlockHandlerFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/core/getStatusHandlerFactory.js b/packages/dapi/lib/grpcServer/handlers/core/getStatusHandlerFactory.js new file mode 100644 index 00000000000..89ea21f3761 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/core/getStatusHandlerFactory.js @@ -0,0 +1,111 @@ +const { + v0: { + GetStatusResponse, + }, +} = require('@dashevo/dapi-grpc'); + +/** + * @param {CoreRpcClient} coreRPCClient + * @returns {getStatusHandler} + */ +function getStatusHandlerFactory(coreRPCClient) { + /** + * @typedef getStatusHandler + * @return {Promise} + */ + async function getStatusHandler() { + const [ + blockchainInfoResponse, + networkInfoResponse, + mnSyncStatusResponse, + masternodeStatusResponse, + ] = await Promise.all([ + coreRPCClient.getBlockchainInfo(), + coreRPCClient.getNetworkInfo(), + coreRPCClient.getMnSync('status'), + coreRPCClient.getMasternode('status'), + ]); + + const response = new GetStatusResponse(); + + const version = new GetStatusResponse.Version(); + version.setProtocol(networkInfoResponse.protocolversion); + version.setSoftware(networkInfoResponse.version); + version.setAgent(networkInfoResponse.subversion); + + const time = new GetStatusResponse.Time(); + time.setNow(Math.floor(Date.now() / 1000)); + time.setOffset(networkInfoResponse.timeoffset); + time.setMedian(blockchainInfoResponse.mediantime); + + const chain = new GetStatusResponse.Chain(); + chain.setName(blockchainInfoResponse.chain); + chain.setBlocksCount(blockchainInfoResponse.blocks); + chain.setHeadersCount(blockchainInfoResponse.headers); + chain.setBestBlockHash(Buffer.from(blockchainInfoResponse.bestblockhash, 'hex')); + chain.setDifficulty(blockchainInfoResponse.difficulty); + chain.setChainWork(Buffer.from(blockchainInfoResponse.chainwork, 'hex')); + chain.setIsSynced(mnSyncStatusResponse.IsBlockchainSynced); + chain.setSyncProgress(blockchainInfoResponse.verificationprogress); + + const masternode = new GetStatusResponse.Masternode(); + + const masternodeStatus = GetStatusResponse.Masternode.Status[masternodeStatusResponse.state]; + + masternode.setStatus(masternodeStatus); + masternode.setProTxHash(Buffer.from(masternodeStatusResponse.proTxHash, 'hex')); + masternode.setPosePenalty(masternodeStatusResponse.dmnState.PoSePenalty); + masternode.setIsSynced(mnSyncStatusResponse.IsSynced); + + let syncProgress; + switch (mnSyncStatusResponse.AssetID) { + case 999: + syncProgress = 1; + break; + case 0: + syncProgress = 0; + break; + case 1: + syncProgress = 1 / 3; + break; + case 4: + syncProgress = 2 / 3; + break; + default: + syncProgress = 0; + } + + masternode.setSyncProgress(syncProgress); + + const network = new GetStatusResponse.Network(); + network.setPeersCount(networkInfoResponse.connections); + + const networkFee = new GetStatusResponse.NetworkFee(); + networkFee.setRelay(networkInfoResponse.relayfee); + networkFee.setIncremental(networkInfoResponse.incrementalfee); + + network.setFee(networkFee); + + response.setVersion(version); + response.setTime(time); + response.setSyncProgress(blockchainInfoResponse.verificationprogress); + response.setChain(chain); + response.setMasternode(masternode); + response.setNetwork(network); + + let status = GetStatusResponse.Status.NOT_STARTED; + if (mnSyncStatusResponse.IsBlockchainSynced && mnSyncStatusResponse.IsSynced) { + status = GetStatusResponse.Status.READY; + } else if (blockchainInfoResponse.verificationprogress > 0) { + status = GetStatusResponse.Status.SYNCING; + } + + response.setStatus(status); + + return response; + } + + return getStatusHandler; +} + +module.exports = getStatusHandlerFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/core/getTransactionHandlerFactory.js b/packages/dapi/lib/grpcServer/handlers/core/getTransactionHandlerFactory.js new file mode 100644 index 00000000000..f483286c867 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/core/getTransactionHandlerFactory.js @@ -0,0 +1,68 @@ +const { + v0: { + GetTransactionResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const { Transaction } = require('@dashevo/dashcore-lib'); + +const { + server: { + error: { + InvalidArgumentGrpcError, + NotFoundGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +/** + * @param {CoreRpcClient} coreRPCClient + * @returns {getTransactionHandler} + */ +function getTransactionHandlerFactory(coreRPCClient) { + /** + * @typedef getTransactionHandler + * @param {Object} call + * @returns {Promise} + */ + async function getTransactionHandler(call) { + const { request } = call; + + const id = request.getId(); + + if (!id) { + throw new InvalidArgumentGrpcError('id is not specified'); + } + + let rawTransaction; + const verboseMode = 1; + try { + rawTransaction = await coreRPCClient.getRawTransaction(id, verboseMode); + } catch (e) { + if (e.code === -5) { + throw new NotFoundGrpcError('Transaction not found'); + } + + throw e; + } + + const transaction = new Transaction(rawTransaction.hex); + + const response = new GetTransactionResponse(); + + const blockHash = rawTransaction.blockhash ? Buffer.from(rawTransaction.blockhash, 'hex') : Buffer.alloc(0); + + response.setTransaction(transaction.toBuffer()); + response.setBlockHash(blockHash); + response.setHeight(rawTransaction.height); + response.setConfirmations(rawTransaction.confirmations); + response.setIsInstantLocked(rawTransaction.instantlock_internal); + response.setIsChainLocked(rawTransaction.chainlock); + + return response; + } + + return getTransactionHandler; +} + +module.exports = getTransactionHandlerFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/createGrpcErrorFromDriveResponse.js b/packages/dapi/lib/grpcServer/handlers/createGrpcErrorFromDriveResponse.js new file mode 100644 index 00000000000..e8dcdca9588 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/createGrpcErrorFromDriveResponse.js @@ -0,0 +1,143 @@ +const cbor = require('cbor'); + +const { + server: { + error: { + InternalGrpcError, + InvalidArgumentGrpcError, + DeadlineExceededGrpcError, + ResourceExhaustedGrpcError, + NotFoundGrpcError, + FailedPreconditionGrpcError, + UnavailableGrpcError, + GrpcError, + }, + }, +} = require('@dashevo/grpc-common'); +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const AlreadyExistsGrpcError = require('@dashevo/grpc-common/lib/server/error/AlreadyExistsGrpcError'); +const createConsensusError = require('@dashevo/dpp/lib/errors/consensus/createConsensusError'); + +/** + * @param {Object} data + * @returns {{"drive-error-data-bin": Buffer}||{}} + */ +function createRawMetadata(data) { + if (Object.keys(data).length === 0) { + return {}; + } + + return { + 'drive-error-data-bin': cbor.encode(data), + }; +} + +const COMMON_ERROR_CLASSES = { + [GrpcErrorCodes.INVALID_ARGUMENT]: InvalidArgumentGrpcError, + [GrpcErrorCodes.DEADLINE_EXCEEDED]: DeadlineExceededGrpcError, + [GrpcErrorCodes.NOT_FOUND]: NotFoundGrpcError, + [GrpcErrorCodes.ALREADY_EXISTS]: AlreadyExistsGrpcError, + [GrpcErrorCodes.RESOURCE_EXHAUSTED]: ResourceExhaustedGrpcError, + [GrpcErrorCodes.FAILED_PRECONDITION]: FailedPreconditionGrpcError, + [GrpcErrorCodes.UNAVAILABLE]: UnavailableGrpcError, +}; + +/** + * @typedef createGrpcErrorFromDriveResponse + * @param {number} code + * @param {string} info + * @return {GrpcError} + */ +function createGrpcErrorFromDriveResponse(code, info) { + if (code === undefined) { + return new InternalGrpcError(new Error('Drive’s error code is empty')); + } + + const decodedInfo = info ? cbor.decode(Buffer.from(info, 'base64')) : { }; + + // eslint-disable-next-line prefer-destructuring + const message = decodedInfo.message; + const data = decodedInfo.data || {}; + + // gRPC error codes + if (code <= 16) { + const CommonErrorClass = COMMON_ERROR_CLASSES[code.toString()]; + if (CommonErrorClass) { + return new CommonErrorClass( + message, + createRawMetadata(data), + ); + } + + // Restore stack for internal error + if (code === GrpcErrorCodes.INTERNAL) { + const error = new Error(message); + + // in case of verbose internal error + if (data.stack) { + error.stack = data.stack; + + delete data.stack; + } + + return new InternalGrpcError(error, createRawMetadata(data)); + } + + return new GrpcError( + code, + message, + createRawMetadata(data), + ); + } + + // Undefined Drive and DAPI errors + if (code >= 17 && code < 1000) { + return new GrpcError( + GrpcErrorCodes.UNKNOWN, + message, + createRawMetadata(data), + ); + } + + // DPP errors + if (code >= 1000 && code < 5000) { + const consensusError = createConsensusError(code, data.arguments || []); + + // Basic + if (code >= 1000 && code < 2000) { + return new InvalidArgumentGrpcError( + consensusError.message, + { code, ...createRawMetadata(data) }, + ); + } + + // Signature + if (code >= 2000 && code < 3000) { + return new GrpcError( + GrpcErrorCodes.UNAUTHENTICATED, + consensusError.message, + { code, ...createRawMetadata(data) }, + ); + } + + // Fee + if (code >= 3000 && code < 4000) { + return new FailedPreconditionGrpcError( + consensusError.message, + { code, ...createRawMetadata(data) }, + ); + } + + // State + if (code >= 4000 && code < 5000) { + return new InvalidArgumentGrpcError( + consensusError.message, + { code, ...createRawMetadata(data) }, + ); + } + } + + return new InternalGrpcError(new Error(`Unknown Drive’s error code: ${code}`)); +} + +module.exports = createGrpcErrorFromDriveResponse; diff --git a/packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js b/packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js new file mode 100644 index 00000000000..0c67810f7f4 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js @@ -0,0 +1,63 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + AlreadyExistsGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + BroadcastStateTransitionResponse, + }, +} = require('@dashevo/dapi-grpc'); + +/** + * @param {jaysonClient} rpcClient + * @param {createGrpcErrorFromDriveResponse} createGrpcErrorFromDriveResponse + * + * @returns {broadcastStateTransitionHandler} + */ +function broadcastStateTransitionHandlerFactory(rpcClient, createGrpcErrorFromDriveResponse) { + /** + * @typedef broadcastStateTransitionHandler + * + * @param {Object} call + * + * @return {Promise} + */ + async function broadcastStateTransitionHandler(call) { + const { request } = call; + const stByteArray = request.getStateTransition(); + + if (!stByteArray) { + throw new InvalidArgumentGrpcError('State Transition is not specified'); + } + + const tx = Buffer.from(stByteArray).toString('base64'); + + const { result, error: jsonRpcError } = await rpcClient.request('broadcast_tx_sync', { tx }); + + if (jsonRpcError) { + if (jsonRpcError.data === 'tx already exists in cache') { + throw new AlreadyExistsGrpcError('State transition already in chain', jsonRpcError); + } + + const error = new Error(); + Object.assign(error, jsonRpcError); + + throw error; + } + + if (result.code !== 0) { + throw createGrpcErrorFromDriveResponse(result.code, result.info); + } + + return new BroadcastStateTransitionResponse(); + } + + return broadcastStateTransitionHandler; +} + +module.exports = broadcastStateTransitionHandlerFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/platform/getConsensusParamsHandlerFactory.js b/packages/dapi/lib/grpcServer/handlers/platform/getConsensusParamsHandlerFactory.js new file mode 100644 index 00000000000..54834f61f58 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/platform/getConsensusParamsHandlerFactory.js @@ -0,0 +1,84 @@ +const InvalidArgumentGrpcError = require('@dashevo/grpc-common/lib/server/error/InvalidArgumentGrpcError'); + +const { + server: { + error: { + InternalGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + GetConsensusParamsResponse, + ConsensusParamsBlock, + ConsensusParamsEvidence, + }, +} = require('@dashevo/dapi-grpc'); + +const FailedPreconditionGrpcError = require('@dashevo/grpc-common/lib/server/error/FailedPreconditionGrpcError'); +const RPCError = require('../../../rpcServer/RPCError'); + +/** + * + * @param {getConsensusParams} getConsensusParams + * @returns {getConsensusParamsHandler} + */ +function getConsensusParamsHandlerFactory(getConsensusParams) { + /** + * @typedef getConsensusParamsHandler + * @param {Object} call + * @returns {Promise<>} + */ + async function getConsensusParamsHandler(call) { + const { request } = call; + + const prove = request.getProve(); + + if (prove) { + throw new InvalidArgumentGrpcError('Prove is not implemented yet'); + } + + // If height is not set - gRPC returns 0 + // in this case we use undefined + const height = request.getHeight() || undefined; + + let consensusParams; + + try { + consensusParams = await getConsensusParams(height); + } catch (e) { + if (e instanceof RPCError) { + if (e.code === 32603) { + throw new FailedPreconditionGrpcError(`Invalid height: ${e.data}`); + } + + throw new InternalGrpcError(e); + } + + throw e; + } + + const response = new GetConsensusParamsResponse(); + + const block = new ConsensusParamsBlock(); + block.setMaxBytes(consensusParams.block.max_bytes); + block.setMaxGas(consensusParams.block.max_gas); + block.setTimeIotaMs(consensusParams.block.time_iota_ms); + + response.setBlock(block); + + const evidence = new ConsensusParamsEvidence(); + evidence.setMaxAgeNumBlocks(consensusParams.evidence.max_age_num_blocks); + evidence.setMaxAgeDuration(consensusParams.evidence.max_age_duration); + evidence.setMaxBytes(consensusParams.evidence.max_bytes); + + response.setEvidence(evidence); + + return response; + } + + return getConsensusParamsHandler; +} + +module.exports = getConsensusParamsHandlerFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/platform/getDataContractHandlerFactory.js b/packages/dapi/lib/grpcServer/handlers/platform/getDataContractHandlerFactory.js new file mode 100644 index 00000000000..9a937d76929 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/platform/getDataContractHandlerFactory.js @@ -0,0 +1,45 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + GetDataContractResponse, + }, +} = require('@dashevo/dapi-grpc'); + +/** + * @param {DriveClient} driveClient + * + * @returns {getDataContractHandler} + */ +function getDataContractHandlerFactory(driveClient) { + /** + * @typedef getDataContractHandler + * + * @param {Object} call + * + * @returns {Promise} + */ + async function getDataContractHandler(call) { + const { request } = call; + const id = request.getId(); + const prove = request.getProve(); + + if (id === null) { + throw new InvalidArgumentGrpcError('id is not specified'); + } + + const dataContractResponseBuffer = await driveClient.fetchDataContract(Buffer.from(id), prove); + + return GetDataContractResponse.deserializeBinary(dataContractResponseBuffer); + } + + return getDataContractHandler; +} + +module.exports = getDataContractHandlerFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/platform/getDocumentsHandlerFactory.js b/packages/dapi/lib/grpcServer/handlers/platform/getDocumentsHandlerFactory.js new file mode 100644 index 00000000000..c0ab19285b4 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/platform/getDocumentsHandlerFactory.js @@ -0,0 +1,120 @@ +const cbor = require('cbor'); + +const { + server: { + error: { + InvalidArgumentGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + GetDocumentsResponse, + }, +} = require('@dashevo/dapi-grpc'); + +/** + * + * @param {DriveClient} driveClient + * + * @returns {getDocumentsHandler} + */ +function getDocumentsHandlerFactory(driveClient) { + /** + * @typedef getDocumentsHandler + * + * @param {Object} call + * + * @returns {Promise} + */ + async function getDocumentsHandler(call) { + const { request } = call; + + // Data Contract ID + const dataContractId = request.getDataContractId(); + + if (!dataContractId) { + throw new InvalidArgumentGrpcError('dataContractId is not specified'); + } + + // Documents type + + const documentType = request.getDocumentType(); + + if (!documentType) { + throw new InvalidArgumentGrpcError('documentType is not specified'); + } + + // Where + + const whereBinary = request.getWhere_asU8(); + + let where; + if (whereBinary.length > 0) { + where = cbor.decode( + Buffer.from(whereBinary), + ); + } + + // Order by + + const orderByBinary = request.getOrderBy_asU8(); + + let orderBy; + if (orderByBinary.length > 0) { + orderBy = cbor.decode( + Buffer.from(orderByBinary), + ); + } + + // Limit + + const limitOrDefault = request.getLimit(); + + let limit; + if (limitOrDefault !== 0) { + limit = limitOrDefault; + } + + // Start after + + const startAfterBinary = request.getStartAfter_asU8(); + + let startAfter; + if (startAfterBinary.length > 0) { + startAfter = Buffer.from(startAfterBinary); + } + + // Start at + + const startAtBinary = request.getStartAt_asU8(); + + let startAt; + if (startAtBinary.length > 0) { + startAt = Buffer.from(startAtBinary); + } + + const options = { + where, + orderBy, + limit, + startAfter, + startAt, + }; + + // Prove + + const prove = request.getProve(); + + const documentResponseBuffer = await driveClient.fetchDocuments( + Buffer.from(dataContractId), documentType, options, prove, + ); + + return GetDocumentsResponse.deserializeBinary(documentResponseBuffer); + } + + return getDocumentsHandler; +} + +module.exports = getDocumentsHandlerFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/platform/getIdentitiesByPublicKeyHashesHandlerFactory.js b/packages/dapi/lib/grpcServer/handlers/platform/getIdentitiesByPublicKeyHashesHandlerFactory.js new file mode 100644 index 00000000000..79494595857 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/platform/getIdentitiesByPublicKeyHashesHandlerFactory.js @@ -0,0 +1,48 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + GetIdentitiesByPublicKeyHashesResponse, + }, +} = require('@dashevo/dapi-grpc'); + +/** + * + * @param {DriveClient} driveClient + * @return {getIdentitiesByPublicKeyHashesHandler} + */ +function getIdentitiesByPublicKeyHashesHandlerFactory( + driveClient, +) { + /** + * @typedef getIdentitiesByPublicKeyHashesHandler + * @param {Object} call + * @return {Promise} + */ + async function getIdentitiesByPublicKeyHashesHandler(call) { + const { request } = call; + + const publicKeyHashes = request.getPublicKeyHashesList(); + + if (publicKeyHashes.length === 0) { + throw new InvalidArgumentGrpcError('No public key hashes were provided'); + } + + const prove = request.getProve(); + + const identitiesResponseBuffer = await driveClient + .fetchIdentitiesByPublicKeyHashes(publicKeyHashes.map(Buffer.from), prove); + + return GetIdentitiesByPublicKeyHashesResponse.deserializeBinary(identitiesResponseBuffer); + } + + return getIdentitiesByPublicKeyHashesHandler; +} + +module.exports = getIdentitiesByPublicKeyHashesHandlerFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/platform/getIdentityHandlerFactory.js b/packages/dapi/lib/grpcServer/handlers/platform/getIdentityHandlerFactory.js new file mode 100644 index 00000000000..cd41a25f669 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/platform/getIdentityHandlerFactory.js @@ -0,0 +1,48 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + GetIdentityResponse, + }, +} = require('@dashevo/dapi-grpc'); + +/** + * @param {DriveClient} driveClient + * + * @returns {getIdentityHandler} + */ +function getIdentityHandlerFactory(driveClient) { + /** + * @typedef getIdentityHandler + * + * @param {Object} call + * + * @return {Promise} + */ + async function getIdentityHandler(call) { + const { request } = call; + + const id = request.getId(); + + if (!id) { + throw new InvalidArgumentGrpcError('id is not specified'); + } + + const prove = request.getProve(); + + const identityResponseBuffer = await driveClient + .fetchIdentity(Buffer.from(id), prove); + + return GetIdentityResponse.deserializeBinary(identityResponseBuffer); + } + + return getIdentityHandler; +} + +module.exports = getIdentityHandlerFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/platform/platformHandlersFactory.js b/packages/dapi/lib/grpcServer/handlers/platform/platformHandlersFactory.js new file mode 100644 index 00000000000..b10edb35108 --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/platform/platformHandlersFactory.js @@ -0,0 +1,235 @@ +const { + client: { + converters: { + jsonToProtobufFactory, + protobufToJsonFactory, + }, + }, + server: { + jsonToProtobufHandlerWrapper, + error: { + wrapInErrorHandlerFactory, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + BroadcastStateTransitionRequest, + GetIdentityRequest, + GetDataContractRequest, + GetDocumentsRequest, + GetIdentitiesByPublicKeyHashesRequest, + WaitForStateTransitionResultRequest, + GetConsensusParamsRequest, + pbjs: { + BroadcastStateTransitionRequest: PBJSBroadcastStateTransitionRequest, + BroadcastStateTransitionResponse: PBJSBroadcastStateTransitionResponse, + GetIdentityRequest: PBJSGetIdentityRequest, + GetIdentityResponse: PBJSGetIdentityResponse, + GetDataContractRequest: PBJSGetDataContractRequest, + GetDataContractResponse: PBJSGetDataContractResponse, + GetDocumentsRequest: PBJSGetDocumentsRequest, + GetDocumentsResponse: PBJSGetDocumentsResponse, + GetIdentitiesByPublicKeyHashesResponse: PBJSGetIdentitiesByPublicKeyHashesResponse, + GetIdentitiesByPublicKeyHashesRequest: PBJSGetIdentitiesByPublicKeyHashesRequest, + WaitForStateTransitionResultRequest: PBJSWaitForStateTransitionResultRequest, + WaitForStateTransitionResultResponse: PBJSWaitForStateTransitionResultResponse, + GetConsensusParamsRequest: PBJSGetConsensusParamsRequest, + GetConsensusParamsResponse: PBJSGetConsensusParamsResponse, + }, + }, +} = require('@dashevo/dapi-grpc'); + +const log = require('../../../log'); + +const createGrpcErrorFromDriveResponse = require('../createGrpcErrorFromDriveResponse'); + +const getIdentityHandlerFactory = require( + './getIdentityHandlerFactory', +); +const broadcastStateTransitionHandlerFactory = require( + './broadcastStateTransitionHandlerFactory', +); +const getDocumentsHandlerFactory = require( + './getDocumentsHandlerFactory', +); +const getDataContractHandlerFactory = require( + './getDataContractHandlerFactory', +); +const getIdentitiesByPublicKeyHashesHandlerFactory = require( + './getIdentitiesByPublicKeyHashesHandlerFactory', +); +const waitForStateTransitionResultHandlerFactory = require( + './waitForStateTransitionResultHandlerFactory', +); +const getConsensusParamsHandlerFactory = require( + './getConsensusParamsHandlerFactory', +); + +const fetchProofForStateTransitionFactory = require('../../../externalApis/drive/fetchProofForStateTransitionFactory'); +const waitForTransactionToBeProvableFactory = require('../../../externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionToBeProvableFactory'); +const waitForTransactionResult = require('../../../externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionResult'); +const waitForHeightFactory = require('../../../externalApis/tenderdash/waitForHeightFactory'); +const getExistingTransactionResultFactory = require('../../../externalApis/tenderdash/waitForTransactionToBeProvable/getExistingTransactionResult'); +const getConsensusParamsFactory = require('../../../externalApis/tenderdash/getConsensusParamsFactory'); + +/** + * @param {jaysonClient} rpcClient + * @param {BlockchainListener} blockchainListener + * @param {DriveClient} driveClient + * @param {DashPlatformProtocol} dpp + * @param {boolean} isProductionEnvironment + * @returns {Object} + */ +function platformHandlersFactory( + rpcClient, + blockchainListener, + driveClient, + dpp, + isProductionEnvironment, +) { + const wrapInErrorHandler = wrapInErrorHandlerFactory(log, isProductionEnvironment); + + // broadcastStateTransition + const broadcastStateTransitionHandler = broadcastStateTransitionHandlerFactory( + rpcClient, + createGrpcErrorFromDriveResponse, + ); + + const wrappedBroadcastStateTransition = jsonToProtobufHandlerWrapper( + jsonToProtobufFactory( + BroadcastStateTransitionRequest, + PBJSBroadcastStateTransitionRequest, + ), + protobufToJsonFactory( + PBJSBroadcastStateTransitionResponse, + ), + wrapInErrorHandler(broadcastStateTransitionHandler), + ); + + // getIdentity + const getIdentityHandler = getIdentityHandlerFactory( + driveClient, + ); + + const wrappedGetIdentity = jsonToProtobufHandlerWrapper( + jsonToProtobufFactory( + GetIdentityRequest, + PBJSGetIdentityRequest, + ), + protobufToJsonFactory( + PBJSGetIdentityResponse, + ), + wrapInErrorHandler(getIdentityHandler), + ); + + // getDocuments + const getDocumentsHandler = getDocumentsHandlerFactory( + driveClient, + ); + + const wrappedGetDocuments = jsonToProtobufHandlerWrapper( + jsonToProtobufFactory( + GetDocumentsRequest, + PBJSGetDocumentsRequest, + ), + protobufToJsonFactory( + PBJSGetDocumentsResponse, + ), + wrapInErrorHandler(getDocumentsHandler), + ); + + // getDataContract + const getDataContractHandler = getDataContractHandlerFactory( + driveClient, + ); + + const wrappedGetDataContract = jsonToProtobufHandlerWrapper( + jsonToProtobufFactory( + GetDataContractRequest, + PBJSGetDataContractRequest, + ), + protobufToJsonFactory( + PBJSGetDataContractResponse, + ), + wrapInErrorHandler(getDataContractHandler), + ); + + // getIdentitiesByPublicKeyHashes + const getIdentitiesByPublicKeyHashesHandler = getIdentitiesByPublicKeyHashesHandlerFactory( + driveClient, + ); + + const wrappedGetIdentitiesByPublicKeyHashes = jsonToProtobufHandlerWrapper( + jsonToProtobufFactory( + GetIdentitiesByPublicKeyHashesRequest, + PBJSGetIdentitiesByPublicKeyHashesRequest, + ), + protobufToJsonFactory( + PBJSGetIdentitiesByPublicKeyHashesResponse, + ), + wrapInErrorHandler(getIdentitiesByPublicKeyHashesHandler), + ); + + // waitForStateTransitionResult + const fetchProofForStateTransition = fetchProofForStateTransitionFactory(driveClient); + + const getExistingTransactionResult = getExistingTransactionResultFactory( + rpcClient, + ); + + const waitForHeight = waitForHeightFactory(blockchainListener); + + const waitForTransactionToBeProvable = waitForTransactionToBeProvableFactory( + waitForTransactionResult, + getExistingTransactionResult, + waitForHeight, + ); + + const waitForStateTransitionResultHandler = waitForStateTransitionResultHandlerFactory( + fetchProofForStateTransition, + waitForTransactionToBeProvable, + blockchainListener, + dpp, + createGrpcErrorFromDriveResponse, + ); + + const wrappedWaitForStateTransitionResult = jsonToProtobufHandlerWrapper( + jsonToProtobufFactory( + WaitForStateTransitionResultRequest, + PBJSWaitForStateTransitionResultRequest, + ), + protobufToJsonFactory( + PBJSWaitForStateTransitionResultResponse, + ), + wrapInErrorHandler(waitForStateTransitionResultHandler), + ); + + // get Consensus Params + const getConsensusParams = getConsensusParamsFactory(rpcClient); + const getConsensusParamsHandler = getConsensusParamsHandlerFactory(getConsensusParams); + + const wrappedGetConsensusParams = jsonToProtobufHandlerWrapper( + jsonToProtobufFactory( + GetConsensusParamsRequest, + PBJSGetConsensusParamsRequest, + ), + protobufToJsonFactory( + PBJSGetConsensusParamsResponse, + ), + wrapInErrorHandler(getConsensusParamsHandler), + ); + + return { + broadcastStateTransition: wrappedBroadcastStateTransition, + getIdentity: wrappedGetIdentity, + getDocuments: wrappedGetDocuments, + getDataContract: wrappedGetDataContract, + getIdentitiesByPublicKeyHashes: wrappedGetIdentitiesByPublicKeyHashes, + waitForStateTransitionResult: wrappedWaitForStateTransitionResult, + getConsensusParams: wrappedGetConsensusParams, + }; +} + +module.exports = platformHandlersFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/platform/waitForStateTransitionResultHandlerFactory.js b/packages/dapi/lib/grpcServer/handlers/platform/waitForStateTransitionResultHandlerFactory.js new file mode 100644 index 00000000000..9c2ac49e7eb --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/platform/waitForStateTransitionResultHandlerFactory.js @@ -0,0 +1,135 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + DeadlineExceededGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + WaitForStateTransitionResultResponse, + StateTransitionBroadcastError, + Proof, + ResponseMetadata, + }, +} = require('@dashevo/dapi-grpc'); + +const cbor = require('cbor'); +const TransactionWaitPeriodExceededError = require('../../../errors/TransactionWaitPeriodExceededError'); +const TransactionErrorResult = require('../../../externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/TransactionErrorResult'); + +/** + * + * @param {fetchProofForStateTransition} fetchProofForStateTransition + * @param {waitForTransactionToBeProvable} waitForTransactionToBeProvable + * @param {BlockchainListener} blockchainListener + * @param {DashPlatformProtocol} dpp + * @param {createGrpcErrorFromDriveResponse} createGrpcErrorFromDriveResponse + * @param {number} stateTransitionWaitTimeout + * @return {waitForStateTransitionResultHandler} + */ +function waitForStateTransitionResultHandlerFactory( + fetchProofForStateTransition, + waitForTransactionToBeProvable, + blockchainListener, + dpp, + createGrpcErrorFromDriveResponse, + stateTransitionWaitTimeout = 80000, +) { + /** + * @param {Object} txDeliverResult + * @return {StateTransitionBroadcastError} + */ + function createStateTransitionDeliverError(txDeliverResult) { + const grpcError = createGrpcErrorFromDriveResponse(txDeliverResult.code, txDeliverResult.info); + + const error = new StateTransitionBroadcastError(); + + error.setCode(txDeliverResult.code); + error.setMessage(grpcError.getMessage()); + error.setData(cbor.encode(grpcError.getRawMetadata())); + + return error; + } + + /** + * @typedef waitForStateTransitionResultHandler + * @param {Object} call + * @return {Promise} + */ + async function waitForStateTransitionResultHandler(call) { + const { request } = call; + + const stateTransitionHash = request.getStateTransitionHash(); + const prove = request.getProve(); + + if (!stateTransitionHash) { + throw new InvalidArgumentGrpcError('state transition hash is not specified'); + } + + const hashString = Buffer.from(stateTransitionHash).toString('hex').toUpperCase(); + + let result; + + try { + result = await waitForTransactionToBeProvable( + blockchainListener, + hashString, + stateTransitionWaitTimeout, + ); + } catch (e) { + if (e instanceof TransactionWaitPeriodExceededError) { + throw new DeadlineExceededGrpcError( + `Waiting period for state transition ${e.getTransactionHash()} exceeded`, + { + stateTransitionHash: e.getTransactionHash(), + }, + ); + } + + throw e; + } + + const response = new WaitForStateTransitionResultResponse(); + + if (result instanceof TransactionErrorResult) { + const error = createStateTransitionDeliverError(result.getResult()); + + response.setError(error); + + return response; + } + + if (prove) { + const stateTransition = await dpp.stateTransition.createFromBuffer( + result.getTransaction(), + { skipValidation: true }, + ); + + const { proof: proofObject, metadata } = await fetchProofForStateTransition(stateTransition); + + const responseMetadata = new ResponseMetadata(); + + responseMetadata.setHeight(metadata.height); + responseMetadata.setCoreChainLockedHeight(metadata.coreChainLockedHeight); + + response.setMetadata(responseMetadata); + + const proof = new Proof(); + + proof.setMerkleProof(proofObject.merkleProof); + proof.setSignatureLlmqHash(proofObject.signatureLlmqHash); + proof.setSignature(proofObject.signature); + + response.setProof(proof); + } + + return response; + } + + return waitForStateTransitionResultHandler; +} + +module.exports = waitForStateTransitionResultHandlerFactory; diff --git a/packages/dapi/lib/grpcServer/handlers/tx-filter-stream/subscribeToTransactionsWithProofsHandlerFactory.js b/packages/dapi/lib/grpcServer/handlers/tx-filter-stream/subscribeToTransactionsWithProofsHandlerFactory.js new file mode 100644 index 00000000000..922578c4cfa --- /dev/null +++ b/packages/dapi/lib/grpcServer/handlers/tx-filter-stream/subscribeToTransactionsWithProofsHandlerFactory.js @@ -0,0 +1,246 @@ +const { BloomFilter } = require('@dashevo/dashcore-lib'); + +const { + server: { + error: { + InvalidArgumentGrpcError, + NotFoundGrpcError, + }, + stream: { + AcknowledgingWritable, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + TransactionsWithProofsResponse, + RawTransactions, + InstantSendLockMessages, + }, +} = require('@dashevo/dapi-grpc'); + +const ProcessMediator = require('../../../transactionsFilter/ProcessMediator'); +const wait = require('../../../utils/wait'); + +/** + * Prepare the response and send transactions response + * + * @param {AcknowledgingWritable} call + * @param {Transaction[]} transactions + * @returns {Promise} + */ +async function sendTransactionsResponse(call, transactions) { + const rawTransactions = new RawTransactions(); + rawTransactions.setTransactionsList( + transactions.map((tx) => tx.toBuffer()), + ); + + const response = new TransactionsWithProofsResponse(); + response.setRawTransactions(rawTransactions); + + await call.write(response); +} + +/** + * Prepare the response and send merkle block response + * + * @param {AcknowledgingWritable} call + * @param {MerkleBlock} merkleBlock + * @returns {Promise} + */ +async function sendMerkleBlockResponse(call, merkleBlock) { + const response = new TransactionsWithProofsResponse(); + response.setRawMerkleBlock(merkleBlock.toBuffer()); + + await call.write(response); +} + +/** + * Prepare the response and send transactions response + * + * @param {AcknowledgingWritable} call + * @param {InstantLock} instantLock + * @returns {Promise} + */ +async function sendInstantLockResponse(call, instantLock) { + const instantSendLockMessages = new InstantSendLockMessages(); + instantSendLockMessages.setMessagesList([instantLock.toBuffer()]); + + const response = new TransactionsWithProofsResponse(); + response.setInstantSendLockMessages(instantSendLockMessages); + + await call.write(response); +} + +/** + * + * @param {getHistoricalTransactionsIterator} getHistoricalTransactionsIterator + * @param {subscribeToNewTransactions} subscribeToNewTransactions + * @param {BloomFilterEmitterCollection} bloomFilterEmitterCollection + * @param {testFunction} testTransactionAgainstFilter + * @param {CoreRpcClient} coreAPI + * @param {getMemPoolTransactions} getMemPoolTransactions + * @return {subscribeToTransactionsWithProofsHandler} + */ +function subscribeToTransactionsWithProofsHandlerFactory( + getHistoricalTransactionsIterator, + subscribeToNewTransactions, + bloomFilterEmitterCollection, + testTransactionAgainstFilter, + coreAPI, + getMemPoolTransactions, +) { + /** + * @typedef subscribeToTransactionsWithProofsHandler + * @param {grpc.ServerWriteableStream} call + */ + async function subscribeToTransactionsWithProofsHandler(call) { + const { request } = call; + + const bloomFilterMessage = request.getBloomFilter(); + + const bloomFilter = { + vData: bloomFilterMessage.getVData_asU8(), + nHashFuncs: bloomFilterMessage.getNHashFuncs(), + nTweak: bloomFilterMessage.getNTweak(), + nFlags: bloomFilterMessage.getNFlags(), + }; + + const fromBlockHash = Buffer.from(request.getFromBlockHash_asU8()).toString('hex'); + const fromBlockHeight = request.getFromBlockHeight(); + + if (!fromBlockHash && fromBlockHeight === 0) { + throw new InvalidArgumentGrpcError('Minimum value for `fromBlockHeight` is 1'); + } + + const from = fromBlockHash || fromBlockHeight; + const count = request.getCount(); + + // Create a new bloom filter emitter when client connects + let filter; + + try { + filter = new BloomFilter(bloomFilter); + } catch (e) { + throw new InvalidArgumentGrpcError(`Invalid bloom filter: ${e.message}`); + } + + const isNewTransactionsRequested = count === 0; + + const acknowledgingCall = new AcknowledgingWritable(call); + + const mediator = new ProcessMediator(); + + mediator.on( + ProcessMediator.EVENTS.TRANSACTION, + async (tx) => { + await sendTransactionsResponse(acknowledgingCall, [tx]); + }, + ); + + mediator.on( + ProcessMediator.EVENTS.MERKLE_BLOCK, + async (merkleBlock) => { + await sendMerkleBlockResponse(acknowledgingCall, merkleBlock); + }, + ); + + mediator.on( + ProcessMediator.EVENTS.INSTANT_LOCK, + async (instantLock) => { + await sendInstantLockResponse(acknowledgingCall, instantLock); + }, + ); + + if (isNewTransactionsRequested) { + subscribeToNewTransactions( + mediator, + filter, + testTransactionAgainstFilter, + bloomFilterEmitterCollection, + ); + } + + // Send historical transactions + let fromBlock; + + try { + fromBlock = await coreAPI.getBlockStats(from, ['height']); + } catch (e) { + if (e.code === -5 || e.code === -8) { + // -5 -> invalid block height or block is not on best chain + // -8 -> block hash not found + throw new NotFoundGrpcError(`Block ${from} not found`); + } + throw e; + } + + const bestBlockHeight = await coreAPI.getBestBlockHeight(); + + let historicalCount = count; + + // if block 'count' is 0 (new transactions are requested) + // or 'count' is bigger than chain tip we need to read all blocks + // from specified block hash including the most recent one + // + // Theoretically, if count is bigger than chain tips, + // we should throw an error 'count is too big', + // however at the time of writing this logic, height chain sync isn't yet implemented, + // so the client library doesn't know the exact height and + // may pass count number larger than expected. + // This condition should be converted to throwing an error once + // the header stream is implemented + if (count === 0 || fromBlock.height + count > bestBlockHeight + 1) { + historicalCount = bestBlockHeight - fromBlock.height + 1; + } + + const historicalDataIterator = getHistoricalTransactionsIterator( + filter, + fromBlock.height, + historicalCount, + ); + + for await (const { merkleBlock, transactions, index } of historicalDataIterator) { + if (index > 0) { + // Wait a second between the calls to Core just to reduce the load + await wait(50); + } + + await sendTransactionsResponse(acknowledgingCall, transactions); + await sendMerkleBlockResponse(acknowledgingCall, merkleBlock); + + if (isNewTransactionsRequested) { + // removing sent transactions and blocks from cache + mediator.emit(ProcessMediator.EVENTS.HISTORICAL_BLOCK_SENT, merkleBlock.header.hash); + } + } + + // notify new txs listener that we've sent historical data + mediator.emit(ProcessMediator.EVENTS.HISTORICAL_DATA_SENT); + + if (isNewTransactionsRequested) { + // Read and test transactions from mempool + const memPoolTransactions = await getMemPoolTransactions(); + memPoolTransactions.forEach( + bloomFilterEmitterCollection.test.bind(bloomFilterEmitterCollection), + ); + + mediator.emit(ProcessMediator.EVENTS.MEMPOOL_DATA_SENT); + } else { + // End stream if user asked only for historical data + call.end(); + } + + call.on('cancelled', () => { + call.end(); + + // remove bloom filter emitter + mediator.emit(ProcessMediator.EVENTS.CLIENT_DISCONNECTED); + }); + } + + return subscribeToTransactionsWithProofsHandler; +} + +module.exports = subscribeToTransactionsWithProofsHandlerFactory; diff --git a/packages/dapi/lib/log/Logger.js b/packages/dapi/lib/log/Logger.js new file mode 100644 index 00000000000..2f00f55712c --- /dev/null +++ b/packages/dapi/lib/log/Logger.js @@ -0,0 +1,67 @@ +const fs = require('fs'); +const { EOL } = require('os'); +const util = require('util'); + +class Logger { + constructor(options = { level: 'INFO' }) { + this.outputFilePath = options.outputFilePath; + this.LEVELS = Object.freeze([ + 'FATAL', + 'ERROR', + 'WARN', + 'NOTICE', + 'INFO', + 'DEBUG', + 'VERBOSE', + ]); + this.level = (options.level && this.LEVELS.indexOf(options.level.toUpperCase())) || 4; + if (this.level < 0) { + throw new Error(`Logger: No log level matches ${options.level}`); + } + + // Create function for each of the different type of levels + this.LEVELS.forEach((name, index) => { + this[name] = index; + this[name.toLowerCase()] = (...restArgs) => { + const args = Array.prototype.slice.call(restArgs);// We take all args passed by + args.unshift(name); // We add the level as first args + this.log(...args); // And we convert again to arguments + }; + }); + } + + log(...restArgs) { + let log = ''; + let level = 4;// By default we display from info to fatal. + const args = Array.prototype.slice.call(restArgs); + + // We need to check if the first args is one of the level designed. + if (args && args.length > 1 && this.LEVELS.includes(args[0].toUpperCase())) { + level = this.LEVELS.indexOf(args[0].toUpperCase()); + args.shift();// Remove the level in order to avoid displaying it. + } + args.forEach((el) => { + if (typeof el === 'string') { + log += ` ${el}`; + } else { + log += ` ${util.inspect(el, false, null)}`; + } + }); + if (level <= this.level) { + if (this.outputFilePath) { + const appendFileAsync = util.promisify(fs.appendFile); + try { + appendFileAsync(this.outputFilePath, EOL + log.trim(), { encoding: 'utf8' }); + } catch (error) { + // eslint-disable-next-line no-console + console.error(`Error: Logger: ${error}`); + } + } else { + // eslint-disable-next-line no-console + console.log(log); + } + } + } +} + +module.exports = Logger; diff --git a/packages/dapi/lib/log/index.js b/packages/dapi/lib/log/index.js new file mode 100644 index 00000000000..5cb1dfa1054 --- /dev/null +++ b/packages/dapi/lib/log/index.js @@ -0,0 +1 @@ +module.exports = console; diff --git a/packages/dapi/lib/rpcServer/README.md b/packages/dapi/lib/rpcServer/README.md new file mode 100644 index 00000000000..7020cc2c516 --- /dev/null +++ b/packages/dapi/lib/rpcServer/README.md @@ -0,0 +1,7 @@ +Commands located at `./commands` folder. + +Guideline for writing rpc commands: + +- No http calls in commands. Move http calls to separate api classes +- Always use `try catch` in command body, as it is last step where error can be caught; +- Always return error to command callback properly \ No newline at end of file diff --git a/packages/dapi/lib/rpcServer/RPCError.js b/packages/dapi/lib/rpcServer/RPCError.js new file mode 100644 index 00000000000..6454347f351 --- /dev/null +++ b/packages/dapi/lib/rpcServer/RPCError.js @@ -0,0 +1,15 @@ +class RPCError extends Error { + constructor(code, message, data, originalStack) { + super(); + + this.code = code; + this.message = message; + this.data = data; + + if (originalStack) { + this.stack = originalStack; + } + } +} + +module.exports = RPCError; diff --git a/packages/dapi/lib/rpcServer/commands/generateToAddress.js b/packages/dapi/lib/rpcServer/commands/generateToAddress.js new file mode 100644 index 00000000000..743c49a4b80 --- /dev/null +++ b/packages/dapi/lib/rpcServer/commands/generateToAddress.js @@ -0,0 +1,87 @@ +const Validator = require('../../utils/Validator'); +const argsSchema = require('../schemas/generateToAddress.json'); + +const validator = new Validator(argsSchema); +/** + * @param coreAPI + * @return {generateToAddress} + */ +const generateToAddressFactory = (coreAPI) => { + /** + * Layer 1 endpoint + * WORKS ONLY IN REGTEST MODE. + * Generates blocks on demand for regression tests. + * @typedef generateToAddress + * @param args - command arguments + * @param {number} args.blocksNumber - Number of blocks to generate + * @param {string} args.address - The address that will receive the newly generated Dash + * + * @return {Promise} - generated block hashes + */ + async function generateToAddress(args) { + validator.validate(args); + + const { blocksNumber, address } = args; + + return coreAPI.generateToAddress(blocksNumber, address); + } + + return generateToAddress; +}; + +/* eslint-disable max-len */ +/** + * @swagger + * /generateToAddress: + * post: + * operationId: generateToAddress + * deprecated: false + * summary: generate + * description: Generates blocks on demand sending funds to address + * tags: + * - L1 + * responses: + * 200: + * description: Successful response. Promise (string array) containing strings of block hashes. + * requestBody: + * content: + * application/json: + * schema: + * type: object + * required: + * - method + * - id + * - jsonrpc + * - params + * properties: + * method: + * type: string + * default: generate + * description: Method name + * id: + * type: integer + * default: 1 + * format: int32 + * description: Request ID + * jsonrpc: + * type: string + * default: '2.0' + * description: JSON-RPC Version (2.0) + * params: + * title: Parameters + * type: object + * required: + * - blocksNumber + * - address + * properties: + * blocksNumber: + * type: integer + * default: 1 + * description: Number of blocks to generate + * address: + * type: string + * description: Address to sends funds to + */ +/* eslint-enable max-len */ + +module.exports = generateToAddressFactory; diff --git a/packages/dapi/lib/rpcServer/commands/getBestBlockHash.js b/packages/dapi/lib/rpcServer/commands/getBestBlockHash.js new file mode 100644 index 00000000000..397c779fd9c --- /dev/null +++ b/packages/dapi/lib/rpcServer/commands/getBestBlockHash.js @@ -0,0 +1,59 @@ +/** + * @param {Object} coreAPI + * @return {getBestBlockHash} + */ +const getBestBlockHashFactory = (coreAPI) => { + /** + * Layer 1 endpoint + * Returns block hash of the chaintip + * @typedef getBestBlockHash + * @return {Promise} - latest block hash + */ + async function getBestBlockHash() { + return coreAPI.getBestBlockHash(); + } + + return getBestBlockHash; +}; + +/* eslint-disable max-len */ +/** + * @swagger + * /getBestBlockHash: + * post: + * operationId: getBestBlockHash + * deprecated: false + * summary: getBestBlockHash + * description: Returns block hash of the chaintip + * tags: + * - L1 + * responses: + * 200: + * description: Successful response. Promise (string) containing the latest block hash. + * requestBody: + * content: + * application/json: + * schema: + * type: object + * required: + * - method + * - id + * - jsonrpc + * properties: + * method: + * type: string + * default: getBestBlockHash + * description: Method name + * id: + * type: integer + * default: 1 + * format: int32 + * description: Request ID + * jsonrpc: + * type: string + * default: '2.0' + * description: JSON-RPC Version (2.0) + */ +/* eslint-enable max-len */ + +module.exports = getBestBlockHashFactory; diff --git a/packages/dapi/lib/rpcServer/commands/getBlockHash.js b/packages/dapi/lib/rpcServer/commands/getBlockHash.js new file mode 100644 index 00000000000..864faf71a08 --- /dev/null +++ b/packages/dapi/lib/rpcServer/commands/getBlockHash.js @@ -0,0 +1,80 @@ +const Validator = require('../../utils/Validator'); +const argsSchema = require('../schemas/getBlockHash.json'); + +const validator = new Validator(argsSchema); + +/** + * @param {Object} coreAPI + * @return {getBlockHash} + */ +const getBlockHashFactory = (coreAPI) => { + /** + * Layer 1 endpoint + * Returns block hash for the given height + * @typedef getBlockHash + * @param args + * @param {number} args.height - block height + * @return {Promise} - block hash + */ + async function getBlockHash(args) { + validator.validate(args); + const { height } = args; + return coreAPI.getBlockHash(height); + } + + return getBlockHash; +}; + +/* eslint-disable max-len */ +/** + * @swagger + * /getBlockHash: + * post: + * operationId: getBlockHash + * deprecated: false + * summary: getBlockHash + * description: Returns the block hash for the given height + * tags: + * - L1 + * responses: + * 200: + * description: Successful response. Promise (string) containing the requested block hash. + * requestBody: + * content: + * application/json: + * schema: + * type: object + * required: + * - method + * - id + * - jsonrpc + * - params + * properties: + * method: + * type: string + * default: getBlockHash + * description: Method name + * id: + * type: integer + * default: 1 + * format: int32 + * description: Request ID + * jsonrpc: + * type: string + * default: '2.0' + * description: JSON-RPC Version (2.0) + * params: + * title: Parameters + * type: object + * required: + * - height + * properties: + * height: + * type: integer + * default: 1 + * description: Block height + * minimum: 0 + */ +/* eslint-enable max-len */ + +module.exports = getBlockHashFactory; diff --git a/packages/dapi/lib/rpcServer/commands/getMnListDiff.js b/packages/dapi/lib/rpcServer/commands/getMnListDiff.js new file mode 100644 index 00000000000..fbbe1d28560 --- /dev/null +++ b/packages/dapi/lib/rpcServer/commands/getMnListDiff.js @@ -0,0 +1,86 @@ +const Validator = require('../../utils/Validator'); +const argsSchema = require('../schemas/getMnListDiff.json'); + +const validator = new Validator(argsSchema); +/** + * Returns getMnListDiff function + * @param coreAPI + * @return {getMnListDiff} + */ +const getMnListDiffFactory = (coreAPI) => { + /** + * Layer 1 endpoint + * Returns calculated balance for the address + * @typedef getMnListDiff + * @param args - command arguments + * @param baseBlockHash {string} + * @param blockHash {string} + * @return {Promise} + */ + async function getMnListDiff(args) { + validator.validate(args); + const { baseBlockHash, blockHash } = args; + + return coreAPI.getMnListDiff(baseBlockHash, blockHash); + } + + return getMnListDiff; +}; + +/* eslint-disable max-len */ +/** + * @swagger + * /getMnListDiff: + * post: + * operationId: getMnListDiff + * deprecated: false + * summary: getMnListDiff + * description: "Returns masternode list diff for the provided block hashes" + * tags: + * - L1 + * responses: + * 200: + * description: Successful response. Promise (object array) containing a diff of the masternode list based on the provided block hashes. + * requestBody: + * content: + * application/json: + * schema: + * type: object + * required: + * - method + * - id + * - jsonrpc + * - params + * properties: + * method: + * type: string + * default: getMnListDiff + * description: Method name + * id: + * type: integer + * default: 1 + * format: int32 + * description: Request ID + * jsonrpc: + * type: string + * default: '2.0' + * description: JSON-RPC Version (2.0) + * params: + * title: Parameters + * type: object + * required: + * - baseBlockHash + * - blockHash + * properties: + * baseBlockHash: + * type: string + * default: '0000000000000000000000000000000000000000000000000000000000000000' + * description: Block hash + * blockHash: + * type: string + * default: '0000000000000000000000000000000000000000000000000000000000000000' + * description: Block hash + */ +/* eslint-enable max-len */ + +module.exports = getMnListDiffFactory; diff --git a/packages/dapi/lib/rpcServer/errorHandlerDecorator.js b/packages/dapi/lib/rpcServer/errorHandlerDecorator.js new file mode 100644 index 00000000000..f00743d9420 --- /dev/null +++ b/packages/dapi/lib/rpcServer/errorHandlerDecorator.js @@ -0,0 +1,44 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); +const RPCError = require('./RPCError'); +const ArgumentsValidationError = require('../errors/ArgumentsValidationError'); +const DashCoreRpcError = require('../errors/DashCoreRpcError'); + +function isOperationalError(error) { + return ( + (error instanceof ArgumentsValidationError) + || (error instanceof DashCoreRpcError) + || (error instanceof InvalidArgumentGrpcError) + ); +} + +/** + * Decorates function with an error handler + * @param {function} command + * @param {Logger} log + * @return {function(*=): Promise} + */ +function errorHandlerDecorator(command, log) { + return function callCommand(args) { + return command(args) + .catch((e) => { + if (e instanceof RPCError) { + throw e; + } else if (isOperationalError(e)) { + throw new RPCError(-32602, e.message, e.data); + } + // In case if this is not a user error, print it to log and return 'Internal Error' to user + if (log && typeof log.error === 'function') { + log.error(e); + } + throw new RPCError(-32603, 'Internal error'); + }); + }; +} + +module.exports = errorHandlerDecorator; diff --git a/packages/dapi/lib/rpcServer/schemas/addresses.json b/packages/dapi/lib/rpcServer/schemas/addresses.json new file mode 100644 index 00000000000..85e281a7640 --- /dev/null +++ b/packages/dapi/lib/rpcServer/schemas/addresses.json @@ -0,0 +1,34 @@ +{ + "type": "object", + "properties": { + "address": { + "type": ["array", "string"], + "oneOf": [ + { "type": "string", "maxLength": 34, "minLength": 26}, + { "type": "array" } + ] + }, + "noTxList": { + "type": ["boolean"] + }, + "from": { + "type": ["integer"], + "minimum": 0 + }, + "to": { + "type": ["integer"], + "minimum": 0 + }, + "fromHeight": { + "type": ["integer"], + "minimum": 0 + }, + "toHeight": { + "type": ["integer"], + "minimum": 0 + } + }, + "required": [ + "address" + ] +} diff --git a/packages/dapi/lib/rpcServer/schemas/generateToAddress.json b/packages/dapi/lib/rpcServer/schemas/generateToAddress.json new file mode 100644 index 00000000000..ff6d2261266 --- /dev/null +++ b/packages/dapi/lib/rpcServer/schemas/generateToAddress.json @@ -0,0 +1,16 @@ +{ + "type": "object", + "properties": { + "blocksNumber": { + "type": "integer", + "minimum": 1 + }, + "address": { + "type": "string" + } + }, + "required": [ + "blocksNumber", + "address" + ] +} diff --git a/packages/dapi/lib/rpcServer/schemas/getBlockHash.json b/packages/dapi/lib/rpcServer/schemas/getBlockHash.json new file mode 100644 index 00000000000..55a0f55d689 --- /dev/null +++ b/packages/dapi/lib/rpcServer/schemas/getBlockHash.json @@ -0,0 +1,12 @@ +{ + "type": "object", + "properties": { + "height": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "height" + ] +} \ No newline at end of file diff --git a/packages/dapi/lib/rpcServer/schemas/getMnListDiff.json b/packages/dapi/lib/rpcServer/schemas/getMnListDiff.json new file mode 100644 index 00000000000..efdfc0c4e43 --- /dev/null +++ b/packages/dapi/lib/rpcServer/schemas/getMnListDiff.json @@ -0,0 +1,15 @@ +{ + "type": "object", + "properties": { + "baseBlockHash": { + "type": "string" + }, + "blockHash": { + "type": "string" + } + }, + "required": [ + "baseBlockHash", + "blockHash" + ] +} \ No newline at end of file diff --git a/packages/dapi/lib/rpcServer/server.js b/packages/dapi/lib/rpcServer/server.js new file mode 100644 index 00000000000..86e265af327 --- /dev/null +++ b/packages/dapi/lib/rpcServer/server.js @@ -0,0 +1,65 @@ +const jayson = require('jayson/promise'); +const { isRegtest, isDevnet } = require('../utils'); +const errorHandlerDecorator = require('./errorHandlerDecorator'); + +const getBestBlockHash = require('./commands/getBestBlockHash'); +const getBlockHash = require('./commands/getBlockHash'); +const getMnListDiff = require('./commands/getMnListDiff'); +const generateToAddress = require('./commands/generateToAddress'); + +// Following commands are not implemented yet: +// const getVersion = require('./commands/getVersion'); + +const createCommands = (dashcoreAPI) => ({ + getBestBlockHash: getBestBlockHash(dashcoreAPI), + getBlockHash: getBlockHash(dashcoreAPI), + getMnListDiff: getMnListDiff(dashcoreAPI), +}); + +const createRegtestCommands = (dashcoreAPI) => ({ + generateToAddress: generateToAddress(dashcoreAPI), +}); + +/** + * Starts RPC server + * @param options + * @param {number} options.port - port to listen for incoming RPC connections + * @param {string} options.networkType + * @param {object} options.dashcoreAPI + * @param {AbstractDriveAdapter} options.driveAPI - Drive api adapter + * @param {object} options.tendermintRpcClient + * @param {DashPlatformProtocol} options.dpp + * @param {object} options.log + */ +const start = ({ + port, + networkType, + dashcoreAPI, + log, +}) => { + const commands = createCommands( + dashcoreAPI, + ); + + const areRegtestCommandsEnabled = isRegtest(networkType) || isDevnet(networkType); + + const allCommands = areRegtestCommandsEnabled + ? Object.assign(commands, createRegtestCommands(dashcoreAPI)) + : commands; + + /* + Decorate all commands with decorator that will intercept errors and format + them before passing to user. + */ + Object.keys(allCommands).forEach((commandName) => { + allCommands[commandName] = errorHandlerDecorator(allCommands[commandName], log); + }); + + const server = jayson.server(allCommands); + server.http().listen(port); +}; + +module.exports = { + createCommands, + start, +}; diff --git a/packages/dapi/lib/test/.eslintrc b/packages/dapi/lib/test/.eslintrc new file mode 100644 index 00000000000..4c2b11fe817 --- /dev/null +++ b/packages/dapi/lib/test/.eslintrc @@ -0,0 +1,9 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + } +} diff --git a/packages/dapi/lib/test/bootstrap.js b/packages/dapi/lib/test/bootstrap.js new file mode 100644 index 00000000000..be9b2060d3a --- /dev/null +++ b/packages/dapi/lib/test/bootstrap.js @@ -0,0 +1,65 @@ +const path = require('path'); +const dotenvSafe = require('dotenv-safe'); +const dotenvExpand = require('dotenv-expand'); +const { expect, use } = require('chai'); +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); +const dirtyChai = require('dirty-chai'); +const chaiAsPromised = require('chai-as-promised'); + +const DashCoreOptions = require('@dashevo/dp-services-ctl/lib/services/dashCore/DashCoreOptions'); + +use(sinonChai); +use(chaiAsPromised); +use(dirtyChai); + +process.env.NODE_ENV = 'test'; + +const dotenvConfig = dotenvSafe.config({ + path: path.resolve(__dirname, '..', '..', '.env'), +}); +dotenvExpand(dotenvConfig); + +const rootPath = process.cwd(); + +const dapiContainerOptions = { + volumes: [ + `${rootPath}/lib:/platform/packages/dapi/lib`, + `${rootPath}/scripts:/platform/packages/dapi/scripts`, + ], +}; + +const dapiOptions = { + cacheNodeModules: true, + localAppPath: rootPath, + container: dapiContainerOptions, +}; + +if (process.env.SERVICE_IMAGE_DAPI) { + dapiOptions.container = { + image: process.env.SERVICE_IMAGE_DAPI, + ...dapiContainerOptions, + }; +} + +if (process.env.SERVICE_IMAGE_CORE) { + DashCoreOptions.setDefaultCustomOptions({ + container: { + image: process.env.SERVICE_IMAGE_CORE, + }, + }); +} + +beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + +afterEach(function afterEach() { + this.sinon.restore(); +}); + +global.expect = expect; diff --git a/packages/dapi/lib/test/mock/GrpcCallMock.js b/packages/dapi/lib/test/mock/GrpcCallMock.js new file mode 100644 index 00000000000..43f00164fd5 --- /dev/null +++ b/packages/dapi/lib/test/mock/GrpcCallMock.js @@ -0,0 +1,22 @@ +const { EventEmitter } = require('events'); + +/** + * @method write + * @method end + * @property {Object} request + */ +class GrpcCallMock extends EventEmitter { + /** + * @param {SinonSandbox} sinon + * @param {Object} request + */ + constructor(sinon, request = {}) { + super(); + + this.write = sinon.stub(); + this.end = sinon.stub(); + this.request = request; + } +} + +module.exports = GrpcCallMock; diff --git a/packages/dapi/lib/transactionsFilter/ProcessMediator.js b/packages/dapi/lib/transactionsFilter/ProcessMediator.js new file mode 100644 index 00000000000..1def0dd2315 --- /dev/null +++ b/packages/dapi/lib/transactionsFilter/ProcessMediator.js @@ -0,0 +1,15 @@ +const { EventEmitter } = require('events'); + +class ProcessMediator extends EventEmitter {} + +ProcessMediator.EVENTS = { + HISTORICAL_DATA_SENT: 'historicalDataSent', + TRANSACTION: 'transaction', + MERKLE_BLOCK: 'merkleBlock', + CLIENT_DISCONNECTED: 'clientDisconnected', + HISTORICAL_BLOCK_SENT: 'historicalBlockSent', + INSTANT_LOCK: 'instantLock', + MEMPOOL_DATA_SENT: 'memPoolDataSent', +}; + +module.exports = ProcessMediator; diff --git a/packages/dapi/lib/transactionsFilter/TransactionHashesCache.js b/packages/dapi/lib/transactionsFilter/TransactionHashesCache.js new file mode 100644 index 00000000000..f74b7df2de4 --- /dev/null +++ b/packages/dapi/lib/transactionsFilter/TransactionHashesCache.js @@ -0,0 +1,257 @@ +const { + MerkleBlock, + util: { buffer: BufferUtils }, +} = require('@dashevo/dashcore-lib'); + +const BLOCKS_TO_STAY_IN_INSTANT_LOCK_CACHE = 10; + +// cache the lookup once, in module scope. +const { hasOwnProperty } = Object.prototype; + +class TransactionHashesCache { + constructor() { + this.transactions = []; + this.merkleBlocks = []; + // TODO: blocks cache can be quite large, as we create one cache instance per + // connected user. It also seems that we aren't using blocks cache for anything particular, + // so we can rework this class to not rely on the block cache + this.blocks = []; + this.cacheSize = 10; + + // Instant lock cache + this.transactionHashesMap = Object.create(null); + this.blocksProcessed = 0; + this.unretrievedInstantLocks = new Map(); + } + + isInInstantLockCache(transactionHash) { + return typeof this.transactionHashesMap[transactionHash] !== 'undefined'; + } + + /** + * Add a transaction if previously not added before + * + * @param {Transaction} transaction + * + * @returns {boolean} - false if already exists + */ + addTransaction(transaction) { + const isAdded = this.transactions + .filter(({ transaction: tx }) => tx.hash === transaction.hash) + .length > 0; + + if (!isAdded) { + this.transactions.push({ + transaction, + isRetrieved: false, + }); + } + + if (!this.isInInstantLockCache(transaction.hash)) { + this.transactionHashesMap[transaction.hash] = this.blocksProcessed; + } + + return !isAdded; + } + + /** + * Add a block + * + * @param {Block} block + * + * @returns {void} + */ + addBlock(block) { + // Process instant lock related functionality + this.blocksProcessed += 1; + const removeAfterHeight = this.blocksProcessed - BLOCKS_TO_STAY_IN_INSTANT_LOCK_CACHE; + for (const hash in this.transactionHashesMap) { + if (hasOwnProperty.call(this.transactionHashesMap, hash)) { + const isInCache = this.isInInstantLockCache(hash); + const needsToBeRemoved = isInCache && this.transactionHashesMap[hash] < removeAfterHeight; + if (needsToBeRemoved) { + this.removeTransactionHashFromInstantSendLockWaitingList(hash); + } + } + } + + const blockTransactionHashes = block.transactions.map((tx) => tx.hash); + const cacheTransactionHashes = this.transactions + .map(({ transaction }) => transaction.hash); + + let haveMatchingTransactions = false; + const matchedTransactionFlags = blockTransactionHashes + .map((hash) => { + const isIncluded = cacheTransactionHashes.includes(hash); + + if (!haveMatchingTransactions && isIncluded) { + haveMatchingTransactions = true; + } + + return isIncluded; + }); + + if (!haveMatchingTransactions) { + return; + } + + // Merkle block accepts only buffers + const transactionHashesBuffers = blockTransactionHashes + .map((hash) => Buffer.from(hash, 'hex')); + + const merkleBlock = MerkleBlock.build( + block.header, + transactionHashesBuffers, + matchedTransactionFlags, + ); + + // TODO: we have to figure out how to fix this hack + // Reverse merkle hashes of the merkle block as tey are ... reversed + if (merkleBlock.hashes) { + merkleBlock.hashes = merkleBlock.hashes.map((hash) => { + const hashBuffer = Buffer.from(hash, 'hex'); + const reverseBuffer = BufferUtils.reverse(hashBuffer); + return reverseBuffer.toString('hex'); + }); + } + + // Push the block to the cache + this.merkleBlocks.push({ + merkleBlock, + isRetrieved: false, + }); + + this.blocks.push(block); + + if (this.blocks.length > this.cacheSize) { + // Shift an array keeping cache within size constraints + const firstBlock = this.blocks.shift(); + + this.removeDataByBlock(firstBlock); + } + } + + removeTransactionHashFromInstantSendLockWaitingList(transactionHash) { + if (this.isInInstantLockCache(transactionHash)) { + delete this.transactionHashesMap[transactionHash]; + } + } + + /** + * Remove transactions, block and merkleBlock + * + * @param {string} blockHash + */ + removeDataByBlockHash(blockHash) { + const [block] = this.blocks.filter((b) => b.hash === blockHash); + + if (block) { + this.removeDataByBlock(block); + } + } + + /** + * @private + * + * Removes data by block + * + * @param {Block} block + */ + removeDataByBlock(block) { + const blockTransactionHashes = block.transactions + .map((tx) => tx.hash); + + // Removing matching transactions + for (let i = this.transactions.length - 1; i >= 0; i--) { + const { transaction } = this.transactions[i]; + if (blockTransactionHashes.includes(transaction.hash)) { + this.transactions.splice(i, 1); + } + } + + // Removing merkle block + for (let i = this.merkleBlocks.length - 1; i >= 0; i--) { + const { merkleBlock } = this.merkleBlocks[i]; + if (merkleBlock.header.hash === block.hash) { + this.merkleBlocks.splice(i, 1); + break; + } + } + + // Removing block + for (let i = this.blocks.length - 1; i >= 0; i--) { + const cachedBlock = this.blocks[i]; + if (cachedBlock.hash === block.hash) { + this.blocks.splice(i, 1); + break; + } + } + } + + /** + * Get block count + * + * @returns {int} + */ + getBlockCount() { + return this.blocks.length; + } + + /** + * Get unretrieved transactions + * + * @returns {Transaction[]} + */ + getUnretrievedTransactions() { + const unretrievedTransactions = this.transactions + .filter(({ isRetrieved }) => !isRetrieved); + + // mark transactions as sent + unretrievedTransactions.forEach((tx) => { + // eslint-disable-next-line no-param-reassign + tx.isRetrieved = true; + }); + + return unretrievedTransactions.map(({ transaction }) => transaction); + } + + /** + * Get unsent merkle blocks + * + * @returns {MerkleBlock[]} + */ + getUnretrievedMerkleBlocks() { + const unretrievedMerkleBlocks = this.merkleBlocks + .filter(({ isRetrieved }) => !isRetrieved); + + // mark merkle blocks as sent + unretrievedMerkleBlocks.forEach((merkleBlock) => { + // eslint-disable-next-line no-param-reassign + merkleBlock.isRetrieved = true; + }); + + return unretrievedMerkleBlocks.map(({ merkleBlock }) => merkleBlock); + } + + /** + * Add Instant Lock + * @param {InstantLock} instantLock + */ + addInstantLock(instantLock) { + this.unretrievedInstantLocks.set(instantLock.txid, instantLock); + } + + /** + * Get unretrieved Instant Locks + * @returns {InstantLock[]} + */ + getUnretrievedInstantLocks() { + const instantLocks = [...this.unretrievedInstantLocks.values()]; + + this.unretrievedInstantLocks.clear(); + + return instantLocks; + } +} + +module.exports = TransactionHashesCache; diff --git a/packages/dapi/lib/transactionsFilter/emitBlockEventToFilterCollectionFactory.js b/packages/dapi/lib/transactionsFilter/emitBlockEventToFilterCollectionFactory.js new file mode 100644 index 00000000000..6f7fe300857 --- /dev/null +++ b/packages/dapi/lib/transactionsFilter/emitBlockEventToFilterCollectionFactory.js @@ -0,0 +1,22 @@ +const { Block } = require('@dashevo/dashcore-lib'); + +/** + * @param {BloomFilterEmitterCollection} bloomFilterEmitterCollection + * @return {emitBlockEventToFilterCollection} + */ +function emitBlockEventToFilterCollectionFactory(bloomFilterEmitterCollection) { + /** + * Emit `block` event to bloom filter collection + * + * @param {Buffer} rawBlock + */ + function emitBlockEventToFilterCollection(rawBlock) { + const block = new Block(rawBlock); + + bloomFilterEmitterCollection.emit('block', block); + } + + return emitBlockEventToFilterCollection; +} + +module.exports = emitBlockEventToFilterCollectionFactory; diff --git a/packages/dapi/lib/transactionsFilter/emitInstantLockToFilterCollectionFactory.js b/packages/dapi/lib/transactionsFilter/emitInstantLockToFilterCollectionFactory.js new file mode 100644 index 00000000000..09ee5cda271 --- /dev/null +++ b/packages/dapi/lib/transactionsFilter/emitInstantLockToFilterCollectionFactory.js @@ -0,0 +1,30 @@ +const { Transaction, InstantLock } = require('@dashevo/dashcore-lib'); + +/** + * @param {BloomFilterEmitterCollection} bloomFilterEmitterCollection + * @return {emitInstantLockToFilterCollection} + */ +function emitInstantLockToFilterCollectionFactory(bloomFilterEmitterCollection) { + /** + * Emit `islock` event to bloom filter collection + * + * @param {Buffer} rawTransactionLock + */ + function emitInstantLockToFilterCollection(rawTransactionLock) { + const transaction = new Transaction().fromBuffer(rawTransactionLock); + const txBuffer = transaction.toBuffer(); + + const txLockBuffer = rawTransactionLock.slice(txBuffer.length, rawTransactionLock.length); + + const instantLock = new InstantLock(txLockBuffer); + + bloomFilterEmitterCollection.emit('instantLock', { + transaction, + instantLock, + }); + } + + return emitInstantLockToFilterCollection; +} + +module.exports = emitInstantLockToFilterCollectionFactory; diff --git a/packages/dapi/lib/transactionsFilter/getHistoricalTransactionsIteratorFactory.js b/packages/dapi/lib/transactionsFilter/getHistoricalTransactionsIteratorFactory.js new file mode 100644 index 00000000000..c528c0bb0b3 --- /dev/null +++ b/packages/dapi/lib/transactionsFilter/getHistoricalTransactionsIteratorFactory.js @@ -0,0 +1,90 @@ +const { + MerkleBlock, + Transaction, + util: { buffer: BufferUtils }, +} = require('@dashevo/dashcore-lib'); + +const MAX_HEADERS_PER_REQUEST = 2000; + +/** + * @param {CoreRpcClient} coreRpcApi + * @param {string[]} transactionHashes + * @return {Promise} + */ +async function getTransactions(coreRpcApi, transactionHashes) { + const rawTransactions = await Promise.all(transactionHashes.map( + (transactionHash) => coreRpcApi.getRawTransaction(transactionHash), + )); + return rawTransactions.map((tx) => new Transaction(tx)); +} + +/** + * @param {number} batchIndex + * @param {number} numberOfBatches + * @param {number} totalCount + * @return {number} + */ +function getBlocksToScan(batchIndex, numberOfBatches, totalCount) { + const isLastBatch = batchIndex + 1 === numberOfBatches; + return isLastBatch + ? totalCount - batchIndex * MAX_HEADERS_PER_REQUEST + : MAX_HEADERS_PER_REQUEST; +} + +/** + * @param {CoreRpcClient} coreRpcApi + * @return {getHistoricalTransactionsIterator} + */ +function getHistoricalTransactionsIteratorFactory(coreRpcApi) { + /** + * @typedef getHistoricalTransactionsIterator + * @param filter + * @param fromBlockHeight {number} + * @param count {number} + * @return {AsyncIterableIterator<{merkleBlock: MerkleBlock, transactions: Transaction[]}>} + */ + async function* getHistoricalTransactionsIterator( + filter, + fromBlockHeight, + count, + ) { + const numberOfBatches = Math.ceil(count / MAX_HEADERS_PER_REQUEST); + + let merkleBlockIndex = 0; + + for (let batchIndex = 0; batchIndex < numberOfBatches; batchIndex++) { + const currentHeight = fromBlockHeight + batchIndex * MAX_HEADERS_PER_REQUEST; + const blocksToScan = getBlocksToScan(batchIndex, numberOfBatches, count); + + const blockHash = await coreRpcApi.getBlockHash(currentHeight); + + const rawMerkleBlocks = await coreRpcApi.getMerkleBlocks( + filter.toBuffer().toString('hex'), + blockHash, + blocksToScan, + ); + + for (const rawMerkleBlock of rawMerkleBlocks) { + const merkleBlock = new MerkleBlock(Buffer.from(rawMerkleBlock, 'hex')); + const reverseTransactionHashes = merkleBlock.getMatchedTransactionHashes(); + + const transactionHashes = reverseTransactionHashes + .map((hash) => { + const buffer = Buffer.from(hash, 'hex'); + const reverseBuffer = BufferUtils.reverse(buffer); + return reverseBuffer.toString('hex'); + }); + + const transactions = await getTransactions(coreRpcApi, transactionHashes); + + yield { merkleBlock, transactions, index: merkleBlockIndex }; + + merkleBlockIndex++; + } + } + } + + return getHistoricalTransactionsIterator; +} + +module.exports = getHistoricalTransactionsIteratorFactory; diff --git a/packages/dapi/lib/transactionsFilter/getMemPoolTransactionsFactory.js b/packages/dapi/lib/transactionsFilter/getMemPoolTransactionsFactory.js new file mode 100644 index 00000000000..95f89017e44 --- /dev/null +++ b/packages/dapi/lib/transactionsFilter/getMemPoolTransactionsFactory.js @@ -0,0 +1,29 @@ +const { Transaction } = require('@dashevo/dashcore-lib'); + +/** + * @param {CoreRpcClient} coreAPI + * @returns {getMemPoolTransactions} + */ +function getMemPoolTransactionsFactory(coreAPI) { + /** + * @typedef getMemPoolTransactions + * @returns {Promise} + */ + async function getMemPoolTransactions() { + const result = []; + const memPoolTransactionIds = await coreAPI.getRawMemPool(false); + + for (const txId of memPoolTransactionIds) { + const rawTransaction = await coreAPI.getRawTransaction(txId); + + const transaction = new Transaction(rawTransaction); + result.push(transaction); + } + + return result; + } + + return getMemPoolTransactions; +} + +module.exports = getMemPoolTransactionsFactory; diff --git a/packages/dapi/lib/transactionsFilter/subscribeToNewTransactions.js b/packages/dapi/lib/transactionsFilter/subscribeToNewTransactions.js new file mode 100644 index 00000000000..b95e05f3cba --- /dev/null +++ b/packages/dapi/lib/transactionsFilter/subscribeToNewTransactions.js @@ -0,0 +1,120 @@ +const TransactionHashesCache = require('./TransactionHashesCache'); +const BloomFilterEmitter = require('../bloomFilter/emitter/BloomFilterEmitter'); + +const ProcessMediator = require('./ProcessMediator'); + +const wait = require('../utils/wait'); + +/** + * @typedef subscribeToNewTransactions + * @param {ProcessMediator} mediator + * @param {BloomFilter} filter + * @param {testFunction} testTransactionAgainstFilter + * @param {BloomFilterEmitterCollection} bloomFilterEmitterCollection + */ +function subscribeToNewTransactions( + mediator, + filter, + testTransactionAgainstFilter, + bloomFilterEmitterCollection, +) { + const filterEmitter = new BloomFilterEmitter(filter, testTransactionAgainstFilter); + + const transactionsAndBlocksCache = new TransactionHashesCache(); + + let isClientConnected = true; + + // store and emit transaction or a locked transaction hash when they match the bloom filter + filterEmitter.on('match', (transaction) => { + // Store the matched transaction + // in order to build a merkle block with sent transactions + transactionsAndBlocksCache.addTransaction(transaction); + }); + + // prepare and emit merkle block with previously sent transactions when they got mined + filterEmitter.on('block', (block) => { + // in case we've missed some or all transactions and got a block + if (transactionsAndBlocksCache.getBlockCount() === 0) { + // test transactions and emit `match` events + block.transactions.forEach((tx) => filterEmitter.test(tx)); + } + + // put block in the cache executing queue logic + transactionsAndBlocksCache.addBlock(block); + }); + + // Collect instant locked transactions and locks + // while we sending historical and mempool data + const preMempoolSentInstantLockListener = ({ instantLock, transaction }) => { + if (!testTransactionAgainstFilter(filter, transaction)) { + return; + } + + transactionsAndBlocksCache.addInstantLock(instantLock); + }; + + filterEmitter.on('instantLock', preMempoolSentInstantLockListener); + + // Receive an event when a historical block is sent to user, + // so we can update our cache to an actual state, + // removing transactions, blocks and merkle blocks from cache + mediator.on(ProcessMediator.EVENTS.HISTORICAL_BLOCK_SENT, (blockHash) => { + transactionsAndBlocksCache.removeDataByBlockHash(blockHash); + }); + + // Receive an event when all historical and mempool data (is any) is sent to the user. + mediator.once(ProcessMediator.EVENTS.MEMPOOL_DATA_SENT, async () => { + // When mempool transactions are sent we start to send + // instant locks right away instead of collecting them + filterEmitter.removeListener('instantLock', preMempoolSentInstantLockListener); + + filterEmitter.on('instantLock', ({ instantLock }) => { + const isTransactionInWaitingList = transactionsAndBlocksCache + .isInInstantLockCache(instantLock.txid); + + if (isTransactionInWaitingList) { + transactionsAndBlocksCache + .removeTransactionHashFromInstantSendLockWaitingList(instantLock.txid); + mediator.emit(ProcessMediator.EVENTS.INSTANT_LOCK, instantLock); + } + }); + + // Run a loop until client is disconnected and send cached as well + // as new data (through the cache) continuously after that. + // Cache is populated from ZMQ events. + while (isClientConnected) { + // TODO We can send multiple items to optimize throughput + // Proto messages already support that + const unsentTransactions = transactionsAndBlocksCache.getUnretrievedTransactions(); + unsentTransactions + .forEach((tx) => mediator.emit(ProcessMediator.EVENTS.TRANSACTION, tx)); + + const unretrievedInstantLocks = transactionsAndBlocksCache.getUnretrievedInstantLocks(); + unretrievedInstantLocks.forEach((instantLock) => { + if (transactionsAndBlocksCache.isInInstantLockCache(instantLock.txid)) { + mediator.emit(ProcessMediator.EVENTS.INSTANT_LOCK, instantLock); + } + }); + + const unsentMerkleBlocks = transactionsAndBlocksCache.getUnretrievedMerkleBlocks(); + unsentMerkleBlocks + .forEach((merkleBlock) => mediator.emit(ProcessMediator.EVENTS.MERKLE_BLOCK, merkleBlock)); + + await wait(50); + } + }); + + // Add the bloom filter emitter to the collection + bloomFilterEmitterCollection.add(filterEmitter); + + mediator.once(ProcessMediator.EVENTS.CLIENT_DISCONNECTED, () => { + isClientConnected = false; + + mediator.removeAllListeners(); + filterEmitter.removeAllListeners(); + + bloomFilterEmitterCollection.remove(filterEmitter); + }); +} + +module.exports = subscribeToNewTransactions; diff --git a/packages/dapi/lib/transactionsFilter/testRawTransactionAgainstFilterCollectionFactory.js b/packages/dapi/lib/transactionsFilter/testRawTransactionAgainstFilterCollectionFactory.js new file mode 100644 index 00000000000..21b72751230 --- /dev/null +++ b/packages/dapi/lib/transactionsFilter/testRawTransactionAgainstFilterCollectionFactory.js @@ -0,0 +1,23 @@ +const { Transaction } = require('@dashevo/dashcore-lib'); + +/** + * @param {BloomFilterEmitterCollection} bloomFilterEmitterCollection + * @return {testRawTransactionAgainstFilterCollection} + */ +function testRawTransactionAgainstFilterCollectionFactory(bloomFilterEmitterCollection) { + /** + * Test a raw transaction against bloom filter collection + * + * @typedef testRawTransactionAgainstFilterCollection + * @param {Buffer} rawTransaction + */ + function testRawTransactionAgainstFilterCollection(rawTransaction) { + const transaction = new Transaction(rawTransaction); + + bloomFilterEmitterCollection.test(transaction); + } + + return testRawTransactionAgainstFilterCollection; +} + +module.exports = testRawTransactionAgainstFilterCollectionFactory; diff --git a/packages/dapi/lib/transactionsFilter/testTransactionAgainstFilter.js b/packages/dapi/lib/transactionsFilter/testTransactionAgainstFilter.js new file mode 100644 index 00000000000..7a0f0bdfb32 --- /dev/null +++ b/packages/dapi/lib/transactionsFilter/testTransactionAgainstFilter.js @@ -0,0 +1,116 @@ +const { BloomFilter } = require('@dashevo/dashcore-lib'); + +/** + * @param {string} transactionHash + * @param {Number} inputIndex + * @returns {Buffer} + */ +function inputIndexToBuffer(transactionHash, inputIndex) { + const binaryTransactionHash = Buffer.from(transactionHash, 'hex'); + const indexBuffer = Buffer.alloc(4); + + indexBuffer.writeUInt32LE(inputIndex, 0); + + return Buffer.concat([binaryTransactionHash, indexBuffer]); +} + +/** + * @param {BloomFilter} filter + * @param {Script} script + * @returns {boolean} + */ +function filterContainsScript(filter, script) { + if (!script) { + return false; + } + + const matchedChunk = script.chunks.find((chunk) => { + if (chunk.opcodenum === 0 || !chunk.buf) { + return false; + } + + return filter.contains(chunk.buf); + }); + + return Boolean(matchedChunk); +} + +/** + * @param {BloomFilter} filter + * @param {Transaction} transaction + * @returns {boolean} + */ +function checkOutputs(filter, transaction) { + if (!Array.isArray(transaction.outputs)) { + return false; + } + + const matchedOutput = transaction.outputs.find((output, index) => { + const isMatchFound = filterContainsScript(filter, output.script); + + const alwaysUpdateFilterOnMatch = filter.nFlags === BloomFilter.BLOOM_UPDATE_ALL; + const updateFilterOnPubKeyMatch = filter.nFlags === BloomFilter.BLOOM_UPDATE_P2PUBKEY_ONLY; + + const isScriptPubKeyOut = output.script.isPublicKeyOut() || output.script.isMultisigOut(); + + const isFilterUpdateNeeded = alwaysUpdateFilterOnMatch + || (updateFilterOnPubKeyMatch && isScriptPubKeyOut); + + if (isMatchFound && isFilterUpdateNeeded) { + filter.insert(inputIndexToBuffer(transaction.hash, index)); + } + + return isMatchFound; + }); + + return Boolean(matchedOutput); +} + +/** + * @param {BloomFilter} filter + * @param {Transaction} transaction + * @return boolean + */ +function checkInputs(filter, transaction) { + if (!Array.isArray(transaction.inputs)) { + return false; + } + + const matchedInput = transaction.inputs.find((input) => { + const isPrevTxExist = Boolean(input.prevTxId); + const containsPreviousOutput = isPrevTxExist && filter.contains( + inputIndexToBuffer(input.prevTxId, input.outputIndex), + ); + + return containsPreviousOutput || filterContainsScript(filter, input.script); + }); + + return Boolean(matchedInput); +} + +/** + * @param {BloomFilter} filter + * @param {Transaction} transaction + * @return boolean + */ +function checkHash(filter, transaction) { + const binaryHash = Buffer.from(transaction.hash, 'hex'); + + return filter.contains(binaryHash); +} + +/** + * BIP37 transaction filtering + * + * @type testFunction + * @param {BloomFilter} filter + * @param {Transaction} data + * @return boolean + */ +function testTransactionAgainstFilter(filter, data) { + return checkHash(filter, data) + || checkOutputs(filter, data) + || checkInputs(filter, data); +} + +module.exports = testTransactionAgainstFilter; diff --git a/packages/dapi/lib/utils/Validator.js b/packages/dapi/lib/utils/Validator.js new file mode 100644 index 00000000000..925f747a8e1 --- /dev/null +++ b/packages/dapi/lib/utils/Validator.js @@ -0,0 +1,22 @@ +const { default: Ajv } = require('ajv/dist/2020'); +const ArgumentsValidationError = require('../errors/ArgumentsValidationError'); + +class Validator { + constructor(schema) { + this.validateArguments = new Ajv({ + strictTypes: true, + strictTuples: true, + strictRequired: true, + addUsedSchema: false, + strict: true, + }).compile(schema); + } + + validate(args) { + if (!this.validateArguments(args)) { + throw new ArgumentsValidationError(`params${this.validateArguments.errors[0].instancePath} ${this.validateArguments.errors[0].message}`); + } + } +} + +module.exports = Validator; diff --git a/packages/dapi/lib/utils/index.js b/packages/dapi/lib/utils/index.js new file mode 100644 index 00000000000..f5202b0e988 --- /dev/null +++ b/packages/dapi/lib/utils/index.js @@ -0,0 +1,18 @@ +const utils = { + /** + * @param {string} network + * @return {boolean} + */ + isRegtest(network) { + return network === 'regtest'; + }, + /** + * @param {string} network + * @return {boolean} + */ + isDevnet(network) { + return /^devnet/.test(network); + }, +}; + +module.exports = utils; diff --git a/packages/dapi/lib/utils/wait.js b/packages/dapi/lib/utils/wait.js new file mode 100644 index 00000000000..f4795fca085 --- /dev/null +++ b/packages/dapi/lib/utils/wait.js @@ -0,0 +1,5 @@ +module.exports = function wait(milliseconds) { + return new Promise((resolve) => { + setTimeout(resolve, milliseconds); + }); +}; diff --git a/packages/dapi/package.json b/packages/dapi/package.json new file mode 100644 index 00000000000..53fb7b71a82 --- /dev/null +++ b/packages/dapi/package.json @@ -0,0 +1,83 @@ +{ + "name": "@dashevo/dapi", + "private": true, + "version": "0.23.0-dev.4", + "description": "A decentralized API for the Dash network", + "scripts": { + "api": "node scripts/api.js", + "core-streams": "node scripts/core-streams.js", + "test": "yarn run test:coverage && yarn run test:functional", + "test:coverage": "nyc --check-coverage --lines=50 --branches=50 --functions=50 yarn run mocha --recursive test/unit test/integration", + "test:unit": "mocha --recursive test/unit", + "test:integration": "mocha --recursive test/integration", + "test:functional": "mocha --recursive test/functional", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "oas:setversion": "jq -r .version package.json | xargs -I{} sed -i \"s/version:.*/version: '{}',/\" doc/swaggerDef.js", + "oas:generate": "yarn run oas:setversion && swagger-jsdoc -d doc/swaggerDef.js lib/rpcServer/**/*.js -o doc/swagger.json" + }, + "ultra": { + "concurrent": [ + "test" + ] + }, + "nyc": { + "include": [ + "lib/**/*.js" + ], + "exclude": [ + "**/node_modules/**", + "**/test/**", + "**/coverage/**" + ], + "all": true + }, + "dependencies": { + "@dashevo/dapi-grpc": "workspace:~", + "@dashevo/dashcore-lib": "~0.19.39", + "@dashevo/dashd-rpc": "^2.3.2", + "@dashevo/dpp": "workspace:~", + "@dashevo/grpc-common": "workspace:~", + "@grpc/grpc-js": "^1.3.7", + "ajv": "^8.6.0", + "bs58": "^4.0.1", + "cbor": "^8.0.0", + "dotenv": "^8.6.0", + "dotenv-expand": "^5.1.0", + "dotenv-safe": "^8.2.0", + "jayson": "^3.3.4", + "lodash": "^4.17.19", + "lru-cache": "^5.1.1", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "ws": "^7.5.3", + "zeromq": "^5.2.8" + }, + "devDependencies": { + "@dashevo/dapi-client": "workspace:~", + "@dashevo/dp-services-ctl": "github:dashevo/js-dp-services-ctl#v0.19-dev", + "chai": "^4.3.4", + "chai-as-promised": "^7.1.1", + "dirty-chai": "^2.0.1", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2", + "mocha": "^9.1.2", + "mocha-sinon": "^2.1.2", + "nyc": "^15.1.0", + "semver": "^7.3.2", + "sinon": "^11.1.2", + "sinon-chai": "^3.7.0", + "swagger-jsdoc": "^3.5.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/dashevo/dapi.git" + }, + "author": "", + "license": "MIT", + "bugs": { + "url": "https://github.com/dashevo/dapi/issues" + }, + "homepage": "https://github.com/dashevo/dapi#readme" +} diff --git a/packages/dapi/scripts/api.js b/packages/dapi/scripts/api.js new file mode 100644 index 00000000000..e5b5db1a368 --- /dev/null +++ b/packages/dapi/scripts/api.js @@ -0,0 +1,151 @@ +// Entry point for DAPI. +const dotenv = require('dotenv'); +const grpc = require('@grpc/grpc-js'); + +const { + server: { + createServer, + }, +} = require('@dashevo/grpc-common'); + +const { + getCoreDefinition, + getPlatformDefinition, +} = require('@dashevo/dapi-grpc'); + +const DashPlatformProtocol = require('@dashevo/dpp'); + +const { client: RpcClient } = require('jayson/promise'); + +const WsClient = require('../lib/externalApis/tenderdash/WsClient'); + +// Load config from .env +dotenv.config(); + +const config = require('../lib/config'); +const { validateConfig } = require('../lib/config/validator'); +const log = require('../lib/log'); +const rpcServer = require('../lib/rpcServer/server'); +const DriveClient = require('../lib/externalApis/drive/DriveClient'); +const dashCoreRpcClient = require('../lib/externalApis/dashcore/rpc'); +const BlockchainListener = require('../lib/externalApis/tenderdash/BlockchainListener'); +const DriveStateRepository = require('../lib/dpp/DriveStateRepository'); + +const coreHandlersFactory = require( + '../lib/grpcServer/handlers/core/coreHandlersFactory', +); +const platformHandlersFactory = require( + '../lib/grpcServer/handlers/platform/platformHandlersFactory', +); + +async function main() { + /* Application start */ + const configValidationResult = validateConfig(config); + if (!configValidationResult.isValid) { + configValidationResult.validationErrors.forEach(log.error); + log.log('Aborting DAPI startup due to config validation errors'); + process.exit(); + } + + const isProductionEnvironment = process.env.NODE_ENV === 'production'; + + log.info('Connecting to Drive'); + const driveClient = new DriveClient({ + host: config.tendermintCore.host, + port: config.tendermintCore.port, + }); + + const rpcClient = RpcClient.http({ + host: config.tendermintCore.host, + port: config.tendermintCore.port, + }); + + const tenderDashWsClient = new WsClient({ + host: config.tendermintCore.host, + port: config.tendermintCore.port, + }); + + const dppForParsingContracts = new DashPlatformProtocol(); + await dppForParsingContracts.initialize(); + const driveStateRepository = new DriveStateRepository(driveClient, dppForParsingContracts); + + log.info(`Connecting to Tenderdash on ${config.tendermintCore.host}:${config.tendermintCore.port}`); + + tenderDashWsClient.on('error', (e) => { + log.error('Tenderdash connection error', e); + + process.exit(1); + }); + + await tenderDashWsClient.connect(); + + const blockchainListener = new BlockchainListener(tenderDashWsClient); + blockchainListener.start(); + + log.info('Connection to Tenderdash established.'); + + // Start JSON RPC server + log.info('Starting JSON RPC server'); + rpcServer.start({ + port: config.rpcServer.port, + networkType: config.network, + dashcoreAPI: dashCoreRpcClient, + log, + }); + log.info(`JSON RPC server is listening on port ${config.rpcServer.port}`); + + const dpp = new DashPlatformProtocol({ + stateRepository: driveStateRepository, + }); + await dpp.initialize(); + + // Start GRPC server + log.info('Starting GRPC server'); + + const coreHandlers = coreHandlersFactory( + dashCoreRpcClient, + isProductionEnvironment, + ); + const platformHandlers = platformHandlersFactory( + rpcClient, + blockchainListener, + driveClient, + dpp, + isProductionEnvironment, + ); + + const grpcApiServer = createServer(getCoreDefinition(0), coreHandlers); + + grpcApiServer.addService(getPlatformDefinition(0).service, platformHandlers); + + grpcApiServer.bindAsync( + `0.0.0.0:${config.grpcServer.port}`, + grpc.ServerCredentials.createInsecure(), + () => { + grpcApiServer.start(); + }, + ); + + log.info(`GRPC API RPC server is listening on port ${config.grpcServer.port}`); + + // Display message that everything is ok + log.info(`DAPI Core process is up and running in ${config.livenet ? 'livenet' : 'testnet'} mode`); + log.info(`Network is ${config.network}`); +} + +main().catch((e) => { + log.error(e.stack); + + process.exit(1); +}); + +process.on('unhandledRejection', (e) => { + log.error(e); + + process.exit(1); +}); + +// break on ^C +process.on('SIGINT', () => { + process.exit(); +}); diff --git a/packages/dapi/scripts/core-streams.js b/packages/dapi/scripts/core-streams.js new file mode 100644 index 00000000000..6d8908234f0 --- /dev/null +++ b/packages/dapi/scripts/core-streams.js @@ -0,0 +1,212 @@ +const dotenv = require('dotenv'); +const grpc = require('@grpc/grpc-js'); + +const { + client: { + converters: { + jsonToProtobufFactory, + protobufToJsonFactory, + }, + }, + server: { + createServer, + jsonToProtobufHandlerWrapper, + error: { + wrapInErrorHandlerFactory, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + TransactionsWithProofsRequest, + BlockHeadersWithChainLocksRequest, + pbjs: { + TransactionsWithProofsRequest: PBJSTransactionsWithProofsRequest, + TransactionsWithProofsResponse: PBJSTransactionsWithProofsResponse, + BlockHeadersWithChainLocksRequest: PBJSBlockHeadersWithChainLocksRequest, + BlockHeadersWithChainLocksResponse: PBJSBlockHeadersWithChainLocksResponse, + }, + }, + getCoreDefinition, +} = require('@dashevo/dapi-grpc'); + +const ChainDataProvider = require('../lib/chainDataProvider/ChainDataProvider'); + +// Load config from .env +dotenv.config(); + +const config = require('../lib/config'); +const { validateConfig } = require('../lib/config/validator'); +const log = require('../lib/log'); + +const BlockHeadersCache = require('../lib/chainDataProvider/BlockHeadersCache'); +const ZmqClient = require('../lib/externalApis/dashcore/ZmqClient'); +const dashCoreRpcClient = require('../lib/externalApis/dashcore/rpc'); + +const BloomFilterEmitterCollection = require('../lib/bloomFilter/emitter/BloomFilterEmitterCollection'); + +const testTransactionAgainstFilterCollectionFactory = require('../lib/transactionsFilter/testRawTransactionAgainstFilterCollectionFactory'); +const emitBlockEventToFilterCollectionFactory = require('../lib/transactionsFilter/emitBlockEventToFilterCollectionFactory'); +const testTransactionsAgainstFilter = require('../lib/transactionsFilter/testTransactionAgainstFilter'); +const emitInstantLockToFilterCollectionFactory = require('../lib/transactionsFilter/emitInstantLockToFilterCollectionFactory'); +const subscribeToTransactionsWithProofsHandlerFactory = require('../lib/grpcServer/handlers/tx-filter-stream/subscribeToTransactionsWithProofsHandlerFactory'); +const subscribeToBlockHeadersWithChainLocksHandlerFactory = require('../lib/grpcServer/handlers/blockheaders-stream/subscribeToBlockHeadersWithChainLocksHandlerFactory'); +const getHistoricalBlockHeadersIteratorFactory = require('../lib/grpcServer/handlers/blockheaders-stream/getHistoricalBlockHeadersIteratorFactory'); +const subscribeToNewBlockHeaders = require('../lib/grpcServer/handlers/blockheaders-stream/subscribeToNewBlockHeaders'); + +const subscribeToNewTransactions = require('../lib/transactionsFilter/subscribeToNewTransactions'); +const getHistoricalTransactionsIteratorFactory = require('../lib/transactionsFilter/getHistoricalTransactionsIteratorFactory'); +const getMemPoolTransactionsFactory = require('../lib/transactionsFilter/getMemPoolTransactionsFactory'); + +async function main() { + // Validate config + const configValidationResult = validateConfig(config); + if (!configValidationResult.isValid) { + configValidationResult.validationErrors.forEach(log.error); + log.error('Aborting DAPI startup due to config validation errors'); + process.exit(); + } + + const isProductionEnvironment = process.env.NODE_ENV === 'production'; + + // Subscribe to events from Dash Core + const dashCoreZmqClient = new ZmqClient(config.dashcore.zmq.host, config.dashcore.zmq.port); + + // Bind logs on ZMQ connection events + dashCoreZmqClient.on(ZmqClient.events.DISCONNECTED, log.warn); + dashCoreZmqClient.on(ZmqClient.events.CONNECTION_DELAY, log.warn); + dashCoreZmqClient.on(ZmqClient.events.MONITOR_ERROR, log.warn); + + // Wait until zmq connection is established + log.info(`Connecting to dashcore ZMQ on ${dashCoreZmqClient.connectionString}`); + + await dashCoreZmqClient.start(); + + log.info('Connection to ZMQ established.'); + + // Add ZMQ event listeners + const bloomFilterEmitterCollection = new BloomFilterEmitterCollection(); + const emitBlockEventToFilterCollection = emitBlockEventToFilterCollectionFactory( + bloomFilterEmitterCollection, + ); + const testRawTransactionAgainstFilterCollection = testTransactionAgainstFilterCollectionFactory( + bloomFilterEmitterCollection, + ); + const emitInstantLockToFilterCollection = emitInstantLockToFilterCollectionFactory( + bloomFilterEmitterCollection, + ); + + // Send raw transactions via `subscribeToTransactionsWithProofs` stream if matched + dashCoreZmqClient.on( + dashCoreZmqClient.topics.rawtx, + testRawTransactionAgainstFilterCollection, + ); + + // Send merkle blocks via `subscribeToTransactionsWithProofs` stream + dashCoreZmqClient.on( + dashCoreZmqClient.topics.rawblock, + emitBlockEventToFilterCollection, + ); + + // TODO: check if we can receive this event before 'rawtx', and if we can, + // we need to test tx in this message first before emitng lock to the bloom + // filter collection + // Send transaction instant locks via `subscribeToTransactionsWithProofs` stream + dashCoreZmqClient.on( + dashCoreZmqClient.topics.rawtxlocksig, + emitInstantLockToFilterCollection, + ); + + const blockHeadersCache = new BlockHeadersCache(); + + const chainDataProvider = new ChainDataProvider(dashCoreRpcClient, + dashCoreZmqClient, blockHeadersCache); + await chainDataProvider.init(); + + // Start GRPC server + log.info('Starting GRPC server'); + + const wrapInErrorHandler = wrapInErrorHandlerFactory(log, isProductionEnvironment); + + const getHistoricalTransactionsIterator = getHistoricalTransactionsIteratorFactory( + dashCoreRpcClient, + ); + + const getHistoricalBlockHeadersIterator = getHistoricalBlockHeadersIteratorFactory( + chainDataProvider, + ); + + const getMemPoolTransactions = getMemPoolTransactionsFactory( + dashCoreRpcClient, + testTransactionsAgainstFilter, + ); + + const subscribeToTransactionsWithProofsHandler = subscribeToTransactionsWithProofsHandlerFactory( + getHistoricalTransactionsIterator, + subscribeToNewTransactions, + bloomFilterEmitterCollection, + testTransactionsAgainstFilter, + dashCoreRpcClient, + getMemPoolTransactions, + ); + + const wrappedSubscribeToTransactionsWithProofs = jsonToProtobufHandlerWrapper( + jsonToProtobufFactory( + TransactionsWithProofsRequest, + PBJSTransactionsWithProofsRequest, + ), + protobufToJsonFactory( + PBJSTransactionsWithProofsResponse, + ), + wrapInErrorHandler(subscribeToTransactionsWithProofsHandler), + ); + + // eslint-disable-next-line operator-linebreak + const subscribeToBlockHeadersWithChainLocksHandler = + subscribeToBlockHeadersWithChainLocksHandlerFactory( + getHistoricalBlockHeadersIterator, + dashCoreRpcClient, + chainDataProvider, + dashCoreZmqClient, + subscribeToNewBlockHeaders, + ); + + const wrappedSubscribeToBlockHeadersWithChainLocks = jsonToProtobufHandlerWrapper( + jsonToProtobufFactory( + BlockHeadersWithChainLocksRequest, + PBJSBlockHeadersWithChainLocksRequest, + ), + protobufToJsonFactory( + PBJSBlockHeadersWithChainLocksResponse, + ), + wrapInErrorHandler(subscribeToBlockHeadersWithChainLocksHandler), + ); + + const grpcServer = createServer( + getCoreDefinition(0), + { + subscribeToTransactionsWithProofs: wrappedSubscribeToTransactionsWithProofs, + subscribeToBlockHeadersWithChainLocks: wrappedSubscribeToBlockHeadersWithChainLocks, + }, + ); + + grpcServer.bindAsync( + `0.0.0.0:${config.txFilterStream.grpcServer.port}`, + grpc.ServerCredentials.createInsecure(), + () => { + grpcServer.start(); + }, + ); + + log.info(`GRPC server is listening on port ${config.txFilterStream.grpcServer.port}`); + + // Display message that everything is ok + log.info(`DAPI TxFilterStream process is up and running in ${config.livenet ? 'livenet' : 'testnet'} mode`); + log.info(`Network is ${config.network}`); +} + +main().catch((e) => { + log.error(e.stack); + process.exit(); +}); diff --git a/packages/dapi/test/.eslintrc b/packages/dapi/test/.eslintrc new file mode 100644 index 00000000000..5092d807856 --- /dev/null +++ b/packages/dapi/test/.eslintrc @@ -0,0 +1,9 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "globals": { + "expect": true + } +} diff --git a/packages/dapi/test/functional/grpcServer/handlers/tx-filter-stream/subscribeToTransactionsWithProofsHandlerFactory.spec.js b/packages/dapi/test/functional/grpcServer/handlers/tx-filter-stream/subscribeToTransactionsWithProofsHandlerFactory.spec.js new file mode 100644 index 00000000000..38636763c63 --- /dev/null +++ b/packages/dapi/test/functional/grpcServer/handlers/tx-filter-stream/subscribeToTransactionsWithProofsHandlerFactory.spec.js @@ -0,0 +1,536 @@ +const { + startDapi, +} = require('@dashevo/dp-services-ctl'); + +const { + Address, + PrivateKey, + Transaction, + Networks, + BloomFilter, + MerkleBlock, +} = require('@dashevo/dashcore-lib'); + +const wait = require('../../../../../lib/utils/wait'); + +describe.skip('subscribeToTransactionsWithProofsHandlerFactory', function main() { + this.timeout(200000); + + let coreAPI; + let dapiClient; + let removeDapi; + + let addressString; + let address; + let privateKey; + + let historicalTransactions; + + let bloomFilter; + let fromBlockHash; + + let merkleBlockStrings; + + beforeEach(async () => { + historicalTransactions = []; + + bloomFilter = BloomFilter.create(1, 0.00001); + + const { + dashCore, + dapiTxFilterStream, + remove, + } = await startDapi({ + dapi: { + cacheNodeModules: true, + localAppPath: process.cwd(), + container: { + volumes: [ + `${process.cwd()}/lib:/platform/packages/dapi/lib`, + `${process.cwd()}/scripts:/platform/packages/dapi/scripts`, + ], + }, + }, + }); + + removeDapi = remove; + + coreAPI = dashCore.getApi(); + dapiClient = dapiTxFilterStream.getApi(); + + ({ result: addressString } = await coreAPI.getNewAddress()); + const { result: privateKeyString } = await coreAPI.dumpPrivKey(addressString); + + address = Address.fromString(addressString, Networks.testnet); + privateKey = new PrivateKey(privateKeyString); + + bloomFilter.insert(address.hashBuffer); + + await coreAPI.generateToAddress(500, addressString); + + // Store current best block hash to cut off noise txs and merkle blocks + ({ result: fromBlockHash } = await coreAPI.getBestBlockHash()); + + // Prepare historical transactions + const filterUnspentInputs = (input) => input.address === addressString; + for (let i = 0; i < 10; i++) { + const { result: unspent } = await coreAPI.listUnspent(); + const inputs = unspent.filter((input) => filterUnspentInputs(input)); + + const transaction = new Transaction() + .from(inputs.filter((inp) => inp.amount > 0.000107)[0]) + .to(address, 10000) + .change(address) + .fee(668) + .sign(privateKey); + + historicalTransactions.push(transaction); + + await coreAPI.sendRawTransaction(transaction.serialize()); + await coreAPI.generateToAddress(1, addressString); + } + + ({ result: merkleBlockStrings } = await coreAPI.getMerkleBlocks( + bloomFilter.toBuffer().toString('hex'), + fromBlockHash, + )); + }); + + afterEach(async () => { + await removeDapi(); + }); + + it('should respond with only historical data', async () => { + const receivedTransactions = []; + const receivedMerkleBlocks = []; + + const bloomFilterObject = bloomFilter.toObject(); + + const stream = await dapiClient.core.subscribeToTransactionsWithProofs( + { + vData: new Uint8Array(bloomFilterObject.vData), + nHashFuncs: bloomFilterObject.nHashFuncs, + nTweak: bloomFilterObject.nTweak, + nFlags: bloomFilterObject.nFlags, + }, + { + fromBlockHash: Buffer.from(fromBlockHash, 'hex'), + count: 11, + }, + ); + + stream.on('data', (response) => { + const merkleBlock = response.getRawMerkleBlock(); + const transactions = response.getRawTransactions(); + + if (merkleBlock) { + receivedMerkleBlocks.push( + Buffer.from(merkleBlock).toString('hex'), + ); + } + + if (transactions) { + transactions.getTransactionsList() + .forEach((tx) => { + receivedTransactions.push( + new Transaction(Buffer.from(tx)), + ); + }); + } + }); + + let streamEnded = false; + stream.on('end', () => { + streamEnded = true; + }); + + let streamError; + stream.on('error', (e) => { + streamError = e; + }); + + while (!streamEnded) { + if (streamError) { + throw streamError; + } + await wait(1000); + } + + expect(streamEnded).to.be.true(); + + const receivedTransactionsHashes = receivedTransactions + .map((tx) => tx.hash); + + const historicalTransactionsHashes = historicalTransactions + .map((tx) => tx.hash); + + historicalTransactionsHashes.forEach((txHash) => { + expect(receivedTransactionsHashes).to.include(txHash); + }); + + expect(receivedMerkleBlocks).to.deep.equal(merkleBlockStrings); + }); + + it('should respond with both historical and new data', async () => { + const receivedTransactions = []; + const receivedMerkleBlocks = []; + + const bloomFilterObject = bloomFilter.toObject(); + + const stream = await dapiClient.core.subscribeToTransactionsWithProofs( + { + vData: new Uint8Array(bloomFilterObject.vData), + nHashFuncs: bloomFilterObject.nHashFuncs, + nTweak: bloomFilterObject.nTweak, + nFlags: bloomFilterObject.nFlags, + }, + { + fromBlockHash: Buffer.from(fromBlockHash, 'hex'), + }, + ); + + stream.on('data', (response) => { + const merkleBlock = response.getRawMerkleBlock(); + const transactions = response.getRawTransactions(); + + if (merkleBlock) { + receivedMerkleBlocks.push( + Buffer.from(merkleBlock).toString('hex'), + ); + } + + if (transactions) { + transactions.getTransactionsList() + .forEach((tx) => { + receivedTransactions.push( + new Transaction(Buffer.from(tx)), + ); + }); + } + }); + + let streamEnded = false; + stream.on('end', () => { + streamEnded = true; + }); + + let streamError; + stream.on('error', (e) => { + streamError = e; + }); + + await wait(20000); + + if (streamEnded) { + throw new Error('Stream has ended'); + } + + if (streamError) { + throw streamError; + } + + const { result: unspent } = await coreAPI.listUnspent(); + const inputs = unspent.filter((input) => input.address === addressString); + + const transaction = new Transaction() + .from(inputs.slice(-1)[0]) + .to(address, 10000) + .change(address) + .fee(668) + .sign(privateKey); + + historicalTransactions.push(transaction); + + await coreAPI.sendRawTransaction(transaction.serialize()); + await coreAPI.generateToAddress(1, addressString); + + await wait(20000); + + ({ result: merkleBlockStrings } = await coreAPI.getMerkleBlocks( + bloomFilter.toBuffer().toString('hex'), + fromBlockHash, + )); + + const receivedTransactionsHashes = receivedTransactions + .map((tx) => tx.hash); + + const historicalTransactionsHashes = historicalTransactions + .map((tx) => tx.hash); + + historicalTransactionsHashes.forEach((txHash) => { + expect(receivedTransactionsHashes).to.include(txHash); + }); + + const rcvMB = receivedMerkleBlocks + .map((s) => Buffer.from(s, 'hex')) + .map((b) => new MerkleBlock(b)) + .map((b) => b.toObject()); + + const hstMB = merkleBlockStrings + .map((s) => Buffer.from(s, 'hex')) + .map((b) => new MerkleBlock(b)) + .map((b) => b.toObject()); + + expect(rcvMB).to.deep.equal(hstMB); + }); + + it('should respond with a proper historical and new data in case of reorganization', async () => { + const receivedTransactions = []; + const receivedMerkleBlocks = []; + + const bloomFilterObject = bloomFilter.toObject(); + + const stream = await dapiClient.core.subscribeToTransactionsWithProofs( + { + vData: new Uint8Array(bloomFilterObject.vData), + nHashFuncs: bloomFilterObject.nHashFuncs, + nTweak: bloomFilterObject.nTweak, + nFlags: bloomFilterObject.nFlags, + }, + { + fromBlockHash: Buffer.from(fromBlockHash, 'hex'), + }, + ); + + stream.on('data', (response) => { + const merkleBlock = response.getRawMerkleBlock(); + const transactions = response.getRawTransactions(); + + if (merkleBlock) { + receivedMerkleBlocks.push( + Buffer.from(merkleBlock).toString('hex'), + ); + } + + if (transactions) { + transactions.getTransactionsList() + .forEach((tx) => { + receivedTransactions.push( + new Transaction(Buffer.from(tx)), + ); + }); + } + }); + + let streamEnded = false; + stream.on('end', () => { + streamEnded = true; + }); + + let streamError; + stream.on('error', (e) => { + streamError = e; + }); + + await wait(20000); + + if (streamEnded) { + throw new Error('Stream has ended'); + } + + if (streamError) { + throw streamError; + } + + const { result: unspent } = await coreAPI.listUnspent(); + const inputs = unspent.filter((input) => input.address === addressString); + + const transaction = new Transaction() + .from(inputs.filter((inp) => inp.amount > 0.000107)[0]) + .to(address, 10000) + .change(address) + .fee(668) + .sign(privateKey); + + historicalTransactions.push(transaction); + + await coreAPI.sendRawTransaction(transaction.serialize()); + + const { result: randomAddress } = await coreAPI.getNewAddress(); + await coreAPI.generateToAddress(1, randomAddress); + + await wait(20000); + + ({ result: merkleBlockStrings } = await coreAPI.getMerkleBlocks( + bloomFilter.toBuffer().toString('hex'), + fromBlockHash, + )); + + const receivedTransactionsHashes = receivedTransactions + .map((tx) => tx.hash); + + const historicalTransactionsHashes = historicalTransactions + .map((tx) => tx.hash); + + historicalTransactionsHashes.forEach((txHash) => { + expect(receivedTransactionsHashes).to.include(txHash); + }); + + const rcvMB = receivedMerkleBlocks + .map((s) => Buffer.from(s, 'hex')) + .map((b) => new MerkleBlock(b)) + .map((b) => b.toObject()); + + const hstMB = merkleBlockStrings + .map((s) => Buffer.from(s, 'hex')) + .map((b) => new MerkleBlock(b)) + .map((b) => b.toObject()); + + expect(rcvMB).to.deep.equal(hstMB); + + const receivedTransactionsSize = receivedTransactions.length; + + const { result: hashToInvalidate } = await coreAPI.getBestBlockHash(); + await coreAPI.invalidateBlock(hashToInvalidate); + + const { result: anotherRandomAddress } = await coreAPI.getNewAddress(); + await coreAPI.generateToAddress(1, anotherRandomAddress); + + await wait(20000); + + const receivedTransactionsSizeAfterReorg = receivedTransactions.length; + + expect(receivedTransactionsSize).to.equal(receivedTransactionsSizeAfterReorg); + + ({ result: merkleBlockStrings } = await coreAPI.getMerkleBlocks( + bloomFilter.toBuffer().toString('hex'), + fromBlockHash, + )); + + const lastHistoricalMerkleBlock = new MerkleBlock( + Buffer.from( + merkleBlockStrings[merkleBlockStrings.length - 1], + 'hex', + ), + ); + + const lastReceivedMerkleBlock = new MerkleBlock( + Buffer.from( + receivedMerkleBlocks[receivedMerkleBlocks.length - 1], + 'hex', + ), + ); + + expect(lastHistoricalMerkleBlock.toObject()).to.deep + .equal(lastReceivedMerkleBlock.toObject()); + }); + + it('should respond with only new data', async () => { + const receivedTransactions = []; + const receivedMerkleBlocks = []; + + const bloomFilterObject = bloomFilter.toObject(); + + // Generate one other block without matching txs + const { result: randomAddress } = await coreAPI.getNewAddress(); + await coreAPI.generateToAddress(1, randomAddress); + const { result: bestBlockHash } = await coreAPI.getBestBlockHash(); + + // Send some transaction so it would located in mempool + // by the time we're going to connect (we should not receive it) + const { result: unspent } = await coreAPI.listUnspent(); + const inputs = unspent.filter((input) => input.address === addressString); + + const transaction = new Transaction() + .from(inputs.filter((inp) => inp.amount > 0.000107)[0]) + .to(address, 10000) + .change(address) + .fee(668) + .sign(privateKey); + + historicalTransactions.push(transaction); + + await coreAPI.sendRawTransaction(transaction.serialize()); + + // Connect to the stream + const stream = await dapiClient.core.subscribeToTransactionsWithProofs( + { + vData: new Uint8Array(bloomFilterObject.vData), + nHashFuncs: bloomFilterObject.nHashFuncs, + nTweak: bloomFilterObject.nTweak, + nFlags: bloomFilterObject.nFlags, + }, + { + fromBlockHash: Buffer.from(bestBlockHash, 'hex'), + }, + ); + + stream.on('data', (response) => { + const merkleBlock = response.getRawMerkleBlock(); + const transactions = response.getRawTransactions(); + + if (merkleBlock) { + receivedMerkleBlocks.push( + Buffer.from(merkleBlock).toString('hex'), + ); + } + + if (transactions) { + transactions.getTransactionsList() + .forEach((tx) => { + receivedTransactions.push( + new Transaction(Buffer.from(tx)), + ); + }); + } + }); + + let streamEnded = false; + stream.on('end', () => { + streamEnded = true; + }); + + let streamError; + stream.on('error', (e) => { + streamError = e; + }); + + await wait(20000); + + // We should not receive tx until it is mined as we connected to late + expect(receivedTransactions).to.have.a.lengthOf(0); + + if (streamEnded) { + throw new Error('Stream has ended'); + } + + if (streamError) { + throw streamError; + } + + // Mine the transaction + await coreAPI.generateToAddress(1, randomAddress); + + await wait(20000); + + ({ result: merkleBlockStrings } = await coreAPI.getMerkleBlocks( + bloomFilter.toBuffer().toString('hex'), + bestBlockHash, + )); + + // We should receive only one tx + expect(receivedTransactions).to.have.a.lengthOf(1); + + const lastReceivedTransaction = receivedTransactions[receivedTransactions.length - 1]; + const lastHistoricalTransaction = historicalTransactions[historicalTransactions.length - 1]; + + expect(lastReceivedTransaction.hash).to.deep.equal(lastHistoricalTransaction.hash); + + const lastHistoricalMerkleBlock = new MerkleBlock( + Buffer.from( + merkleBlockStrings[merkleBlockStrings.length - 1], + 'hex', + ), + ); + + const lastReceivedMerkleBlock = new MerkleBlock( + Buffer.from( + receivedMerkleBlocks[receivedMerkleBlocks.length - 1], + 'hex', + ), + ); + + expect(lastHistoricalMerkleBlock.toObject()).to.deep + .equal(lastReceivedMerkleBlock.toObject()); + }); +}); diff --git a/packages/dapi/test/integration/dpp/DriveStateRepository.spec.js b/packages/dapi/test/integration/dpp/DriveStateRepository.spec.js new file mode 100644 index 00000000000..ff675af03fb --- /dev/null +++ b/packages/dapi/test/integration/dpp/DriveStateRepository.spec.js @@ -0,0 +1,68 @@ +const chai = require('chai'); +const sinon = require('sinon'); + +const chaiAsPromised = require('chai-as-promised'); +const dirtyChai = require('dirty-chai'); + +const DashPlatformProtocol = require('@dashevo/dpp'); + +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); + +const { + v0: { + GetDataContractResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const DriveStateRepository = require('../../../lib/dpp/DriveStateRepository'); + +chai.use(chaiAsPromised); +chai.use(dirtyChai); + +const { expect } = chai; + +describe('DriveStateRepository', () => { + let dpp; + let driveClientMock; + let stateRepository; + let dataContractFixture; + let proto; + + beforeEach(async function before() { + dataContractFixture = getDataContractFixture(); + + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + sinon.spy(dpp.dataContract, 'createFromBuffer'); + + proto = new GetDataContractResponse(); + proto.setDataContract(dataContractFixture.toBuffer()); + + driveClientMock = sinon.stub(); + driveClientMock.fetchDataContract = this.sinon.stub().resolves( + proto.serializeBinary(), + ); + + stateRepository = new DriveStateRepository(driveClientMock, dpp); + }); + + describe('#fetchDataContract', () => { + it('should fetch and parse data contract', async () => { + const contractId = generateRandomIdentifier(); + const result = await stateRepository.fetchDataContract(contractId); + + expect(result.toObject()).to.be.deep.equal(dataContractFixture.toObject()); + + expect(dpp.dataContract.createFromBuffer).to.be.calledOnceWithExactly( + proto.getDataContract_asU8(), + { + skipValidation: true, + }, + ); + + expect(driveClientMock.fetchDataContract).to.be.calledOnce(); + expect(driveClientMock.fetchDataContract).to.be.calledWithExactly(contractId, false); + }); + }); +}); diff --git a/packages/dapi/test/integration/externalApis/tenderdash/BlockchainListener.spec.js b/packages/dapi/test/integration/externalApis/tenderdash/BlockchainListener.spec.js new file mode 100644 index 00000000000..390feae4f87 --- /dev/null +++ b/packages/dapi/test/integration/externalApis/tenderdash/BlockchainListener.spec.js @@ -0,0 +1,119 @@ +const EventEmitter = require('events'); +const crypto = require('crypto'); + +const BlockchainListener = require('../../../../lib/externalApis/tenderdash/BlockchainListener'); + +describe('BlockchainListener', () => { + let sinon; + let wsClientMock; + let blockchainListener; + let txQueryMessageMock; + let transactionHash; + let blockMessageMock; + + beforeEach(function beforeEach() { + ({ sinon } = this); + wsClientMock = new EventEmitter(); + wsClientMock.subscribe = sinon.stub(); + blockchainListener = new BlockchainListener(wsClientMock); + blockchainListener.start(); + + sinon.spy(blockchainListener, 'on'); + sinon.spy(blockchainListener, 'off'); + sinon.spy(blockchainListener, 'emit'); + + const txBase64Mock = 'aaaa'; + transactionHash = crypto.createHash('sha256') + .update(Buffer.from(txBase64Mock, 'base64')) + .digest() + .toString('hex'); + + txQueryMessageMock = { + events: [ + { + type: 'tm', + attributes: [ + { + key: 'event', + value: 'Tx', + index: false, + }, + ], + }, + { + type: 'tx', + attributes: [ + { + key: 'hash', + value: transactionHash, + index: false, + }, + ], + }, + { + type: 'tx', + attributes: [ + { + key: 'height', + value: '38', + index: false, + }, + ], + }, + ], + }; + + blockMessageMock = { + data: { + value: { + block: { + data: { + txs: [], + }, + }, + }, + }, + }; + }); + + describe('.getTransactionEventName', () => { + it('should return event name', () => { + const topic = BlockchainListener.getTransactionEventName(transactionHash); + expect(topic).to.be.equal(`transaction:${transactionHash}`); + }); + }); + + describe('#start', () => { + it('should subscribe to transaction events from WS client', () => { + expect(wsClientMock.subscribe).to.be.calledTwice(); + expect(wsClientMock.subscribe.firstCall).to.be.calledWithExactly( + BlockchainListener.TX_QUERY, + ); + expect(wsClientMock.subscribe.secondCall).to.be.calledWithExactly( + BlockchainListener.NEW_BLOCK_QUERY, + ); + }); + + it('should emit block when new block is arrived', (done) => { + blockchainListener.on(BlockchainListener.EVENTS.NEW_BLOCK, (message) => { + expect(message).to.be.deep.equal(blockMessageMock); + + done(); + }); + + wsClientMock.emit(BlockchainListener.NEW_BLOCK_QUERY, blockMessageMock); + }); + + it('should emit transaction when transaction is arrived', (done) => { + const topic = BlockchainListener.getTransactionEventName(transactionHash); + + blockchainListener.on(topic, (message) => { + expect(message).to.be.deep.equal(txQueryMessageMock); + + done(); + }); + + wsClientMock.emit(BlockchainListener.TX_QUERY, txQueryMessageMock); + }); + }); +}); diff --git a/packages/dapi/test/integration/grpcServer/handlers/blockheaders-stream/subscribeToNewBlockHeaders.spec.js b/packages/dapi/test/integration/grpcServer/handlers/blockheaders-stream/subscribeToNewBlockHeaders.spec.js new file mode 100644 index 00000000000..4c3895d1630 --- /dev/null +++ b/packages/dapi/test/integration/grpcServer/handlers/blockheaders-stream/subscribeToNewBlockHeaders.spec.js @@ -0,0 +1,251 @@ +const { BlockHeader, Block, ChainLock } = require('@dashevo/dashcore-lib'); +const ZmqClient = require('../../../../../lib/externalApis/dashcore/ZmqClient'); +const dashCoreRpcClient = require('../../../../../lib/externalApis/dashcore/rpc'); + +const subscribeToNewBlockHeaders = require('../../../../../lib/grpcServer/handlers/blockheaders-stream/subscribeToNewBlockHeaders'); +const ChainDataProvider = require('../../../../../lib/chainDataProvider/ChainDataProvider'); +const blockHeadersCache = require('../../../../../lib/chainDataProvider/BlockHeadersCache'); +const { NEW_BLOCK_HEADERS_PROPAGATE_INTERVAL } = require('../../../../../lib/grpcServer/handlers/blockheaders-stream/constants'); +const ProcessMediator = require('../../../../../lib/grpcServer/handlers/blockheaders-stream/ProcessMediator'); +const wait = require('../../../../../lib/utils/wait'); + +describe('subscribeToNewBlockHeaders', async () => { + let mediator; + let zmqClient; + + const blockHeaders = {}; + const chainLocks = {}; + + this.sinon.stub(dashCoreRpcClient, 'getBlockHeader') + .callsFake(async (hash) => blockHeaders[hash].toBuffer().toString('hex')); + + const mockCoreAPI = this.sinon.stub(); + const mockZmqClient = this.sinon.stub(); + + const chainDataProvider = new ChainDataProvider(mockCoreAPI, mockZmqClient); + await chainDataProvider.init(); + + beforeEach(async () => { + mediator = new ProcessMediator(); + + dashCoreRpcClient.getBlockHeader.resetHistory(); + blockHeadersCache.purge(); + + zmqClient = new ZmqClient(); + this.sinon.stub(zmqClient.subscriberSocket, 'connect') + .callsFake(() => { + zmqClient.subscriberSocket.emit('connect'); + }); + + await zmqClient.start(); + + const blockHeaderOne = new BlockHeader({ + version: 536870913, + prevHash: '0000000000000000000000000000000000000000000000000000000000000000', + merkleRoot: 'c4970326400177ce67ec582425a698b85ae03cae2b0d168e87eed697f1388e4b', + time: 1507208925, + timestamp: 1507208645, + bits: 0, + nonce: 1449878271, + }); + + const blockOne = new Block({ + header: blockHeaderOne.toObject(), + transactions: [], + }); + + const blockHeaderTwo = new BlockHeader({ + version: 536870913, + prevHash: blockOne.hash, + merkleRoot: 'c4970326400177ce67ec582425a698b85ae03cae2b0d168e87eed697f1388e4c', + time: 1507208926, + timestamp: 1507208646, + bits: 0, + nonce: 1449878272, + }); + + const blockTwo = new Block({ + header: blockHeaderTwo.toObject(), + transactions: [], + }); + + const blockHeaderThree = new BlockHeader({ + version: 536870913, + prevHash: blockTwo.hash, + merkleRoot: 'c4970326400177ce67ec582425a698b85ae03cae2b0d168e87eed697f1388e4d', + time: 1507208927, + timestamp: 1507208647, + bits: 0, + nonce: 1449878273, + }); + + blockHeaders[blockHeaderOne.hash] = blockHeaderOne; + blockHeaders[blockHeaderTwo.hash] = blockHeaderTwo; + blockHeaders[blockHeaderThree.hash] = blockHeaderThree; + + const chainLockOne = new ChainLock({ + height: 2, + signature: Buffer.alloc(32).fill(1), + blockHash: Buffer.alloc(32).fill(2), + }); + + const chainLockTwo = new ChainLock({ + height: 3, + signature: Buffer.alloc(32).fill(3), + blockHash: Buffer.alloc(32).fill(4), + }); + + const chainLockThree = new ChainLock({ + height: 4, + signature: Buffer.alloc(32).fill(5), + blockHash: Buffer.alloc(32).fill(6), + }); + + chainLocks[chainLockOne.height] = chainLockOne; + chainLocks[chainLockTwo.height] = chainLockTwo; + chainLocks[chainLockThree.height] = chainLockThree; + }); + + it('should add blocks and latest chain lock in cache and send them back when historical data is sent', async () => { + const receivedHeaders = {}; + let latestChainLock = null; + + mediator.on(ProcessMediator.EVENTS.BLOCK_HEADERS, (headers) => { + headers.forEach((header) => { + receivedHeaders[header.hash] = header; + }); + }); + + mediator.on(ProcessMediator.EVENTS.CHAIN_LOCK, (chainLock) => { + latestChainLock = chainLock; + }); + + subscribeToNewBlockHeaders( + mediator, + chainDataProvider, + ); + + const hashes = Object.keys(blockHeaders); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.hashblock, Buffer.from(hashes[0], 'hex')); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.hashblock, Buffer.from(hashes[1], 'hex')); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.hashblock, Buffer.from(hashes[2], 'hex')); + + const locksHeights = Object.keys(chainLocks); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.rawchainlock, chainLocks[locksHeights[0]].toBuffer()); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.rawchainlock, chainLocks[locksHeights[1]].toBuffer()); + + mediator.emit(ProcessMediator.EVENTS.HISTORICAL_DATA_SENT); + + await new Promise((resolve) => setImmediate(resolve)); + mediator.emit(ProcessMediator.EVENTS.CLIENT_DISCONNECTED); + + expect(receivedHeaders).to.deep.equal(blockHeaders); + expect(latestChainLock).to.deep.equal(chainLocks[locksHeights[1]]); + }); + + it('should remove historical data from cache and send only data that is left', async () => { + const receivedHeaders = {}; + + mediator.on(ProcessMediator.EVENTS.BLOCK_HEADERS, (headers) => { + headers.forEach((header) => { + receivedHeaders[header.hash] = header; + }); + }); + + subscribeToNewBlockHeaders( + mediator, + chainDataProvider, + ); + + const hashes = Object.keys(blockHeaders); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.hashblock, Buffer.from(hashes[0], 'hex')); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.hashblock, Buffer.from(hashes[1], 'hex')); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.hashblock, Buffer.from(hashes[2], 'hex')); + + mediator.emit(ProcessMediator.EVENTS.HISTORICAL_BLOCK_HEADERS_SENT, [hashes[0]]); + + mediator.emit(ProcessMediator.EVENTS.HISTORICAL_DATA_SENT); + + await new Promise((resolve) => setImmediate(resolve)); + mediator.emit(ProcessMediator.EVENTS.CLIENT_DISCONNECTED); + + const expectedHeaders = { ...blockHeaders }; + delete expectedHeaders[hashes[0]]; + expect(receivedHeaders).to.deep.equal(expectedHeaders); + }); + + it('should send fresh chain locks', async () => { + const receivedChainLocks = {}; + + mediator.on(ProcessMediator.EVENTS.CHAIN_LOCK, (chainLock) => { + receivedChainLocks[chainLock.height] = chainLock; + }); + + subscribeToNewBlockHeaders( + mediator, + chainDataProvider, + ); + + const locksHeights = Object.keys(chainLocks); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.rawchainlock, chainLocks[locksHeights[0]].toBuffer()); + mediator.emit(ProcessMediator.EVENTS.HISTORICAL_DATA_SENT); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.rawchainlock, chainLocks[locksHeights[1]].toBuffer()); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.rawchainlock, chainLocks[locksHeights[2]].toBuffer()); + await wait(NEW_BLOCK_HEADERS_PROPAGATE_INTERVAL + 100); + mediator.emit(ProcessMediator.EVENTS.CLIENT_DISCONNECTED); + const expectedChainLocks = { ...chainLocks }; + delete expectedChainLocks[locksHeights[1]]; + expect(receivedChainLocks).to.deep.equal(expectedChainLocks); + }); + + it('should use cache when historical data is sent', async () => { + const spyCache = this.sinon.spy(blockHeadersCache); + const receivedHeaders = {}; + + mediator.on(ProcessMediator.EVENTS.BLOCK_HEADERS, (headers) => { + headers.forEach((header) => { + receivedHeaders[header.hash] = header; + }); + }); + + subscribeToNewBlockHeaders( + mediator, + chainDataProvider, + ); + + const hashes = Object.keys(blockHeaders); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.hashblock, Buffer.from(hashes[0], 'hex')); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.hashblock, Buffer.from(hashes[1], 'hex')); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.hashblock, Buffer.from(hashes[2], 'hex')); + + const locksHeights = Object.keys(chainLocks); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.rawchainlock, chainLocks[locksHeights[0]].toBuffer()); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.rawchainlock, chainLocks[locksHeights[1]].toBuffer()); + + mediator.emit(ProcessMediator.EVENTS.HISTORICAL_DATA_SENT); + + await new Promise((resolve) => setImmediate(resolve)); + mediator.emit(ProcessMediator.EVENTS.CLIENT_DISCONNECTED); + + expect(dashCoreRpcClient.getBlockHeader.callCount).to.be.equal(3); + dashCoreRpcClient.getBlockHeader.resetHistory(); + + subscribeToNewBlockHeaders( + mediator, + chainDataProvider, + ); + + zmqClient.subscriberSocket.emit('message', zmqClient.topics.hashblock, Buffer.from(hashes[0], 'hex')); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.hashblock, Buffer.from(hashes[1], 'hex')); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.hashblock, Buffer.from(hashes[2], 'hex')); + + zmqClient.subscriberSocket.emit('message', zmqClient.topics.rawchainlock, chainLocks[locksHeights[0]].toBuffer()); + zmqClient.subscriberSocket.emit('message', zmqClient.topics.rawchainlock, chainLocks[locksHeights[1]].toBuffer()); + + mediator.emit(ProcessMediator.EVENTS.HISTORICAL_DATA_SENT); + + expect(dashCoreRpcClient.getBlockHeader.callCount).to.be.equal(0); + expect(spyCache.set.callCount).to.be.equal(3); + expect(spyCache.get.callCount).to.be.equal(6); + }); +}); diff --git a/packages/dapi/test/integration/grpcServer/handlers/platform/waitForStateTransitionResultHandlerFactory.js b/packages/dapi/test/integration/grpcServer/handlers/platform/waitForStateTransitionResultHandlerFactory.js new file mode 100644 index 00000000000..7288f6ccdbe --- /dev/null +++ b/packages/dapi/test/integration/grpcServer/handlers/platform/waitForStateTransitionResultHandlerFactory.js @@ -0,0 +1,353 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + DeadlineExceededGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + WaitForStateTransitionResultResponse, + WaitForStateTransitionResultRequest, + StateTransitionBroadcastError, + Proof, + }, +} = require('@dashevo/dapi-grpc'); +const createDPPMock = require('@dashevo/dpp/lib/test/mocks/createDPPMock'); +const getIdentityCreateTransitionFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityCreateTransitionFixture'); + +const { EventEmitter } = require('events'); + +const cbor = require('cbor'); +const NotFoundGrpcError = require('@dashevo/grpc-common/lib/server/error/NotFoundGrpcError'); +const BlockchainListener = require('../../../../../lib/externalApis/tenderdash/BlockchainListener'); + +const GrpcCallMock = require('../../../../../lib/test/mock/GrpcCallMock'); +const fetchProofForStateTransitionFactory = require('../../../../../lib/externalApis/drive/fetchProofForStateTransitionFactory'); +const waitForTransactionToBeProvableFactory = require('../../../../../lib/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionToBeProvableFactory'); +const waitForTransactionResult = require('../../../../../lib/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionResult'); + +const waitForStateTransitionResultHandlerFactory = require('../../../../../lib/grpcServer/handlers/platform/waitForStateTransitionResultHandlerFactory'); +const waitForHeightFactory = require('../../../../../lib/externalApis/tenderdash/waitForHeightFactory'); + +describe('waitForStateTransitionResultHandlerFactory', () => { + let call; + let waitForStateTransitionResultHandler; + let driveClientMock; + let tenderDashWsClientMock; + let blockchainListener; + let dppMock; + let hash; + let proofFixture; + let wsMessagesFixture; + let stateTransitionFixture; + let request; + let fetchProofForStateTransition; + let waitForTransactionToBeProvable; + let transactionNotFoundError; + let createGrpcErrorFromDriveResponseMock; + let errorInfo; + + beforeEach(function beforeEach() { + const hashString = '56458F2D8A8617EA322931B72C103CDD93820004E534295183A6EF215B93C76E'; + hash = Buffer.from(hashString, 'hex'); + + errorInfo = { + message: 'Identity not found', + metadata: { + error: 'some data', + }, + }; + + wsMessagesFixture = { + success: { + query: "tm.event = 'Tx'", + data: { + type: 'tendermint/event/Tx', + value: { + TxResult: { + height: '145', + tx: 'pWR0eXBlA2lhc3NldExvY2ujZXByb29momR0eXBlAGtpbnN0YW50TG9ja1ilAR272lhhsS11I/IKpeDUL1LePc0tXC/pGbpntZ8FDSBuAAAAAHvUKCicVybMXMiWz60mTKDN2H7HesE1zhNhy9w+zKjYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa291dHB1dEluZGV4AGt0cmFuc2FjdGlvbljfAwAAAAFft1DH/7MLyiiZTQ0v9kxxx5IO+g3OowKiGXGr/gzTXAEAAABrSDBFAiEA9zBXt5ZbkZ0miGrXtJPF9abrNUHafXIGRHXeritMEZECIBO0nrmvNv/jff27bDehIf3kD+WHQACWj5UvryJNQvyAASECG117xwKATG95Jur1SvBo/vAjYHx5AnYYOwsN3zL8Wyf/////AgEAAAAAAAAAFmoU7MiTGZFsxDcto0FsSOKqkcWmk/5OiAAAAAAAABl2qRTk6MFuEOFzT3vBIbU1Hio2UuiDzYisAAAAAGlzaWduYXR1cmVYQSANwCdg67KHh/OiSv9FW8qNFj+8OBvwnm3Ybg2Ju0tGNmkw3jAkdOgHLqAkmHCtiSvqZ7IhGDXhU5YtHCk6PIOIamlkZW50aXR5SWRYIJmUCrEaSl7bW6UkE3rBhlQjTBhJ4v1m0ORUXh434DTDb3Byb3RvY29sVmVyc2lvbgA=', + result: {}, + }, + }, + }, + events: [ + { + type: 'tm', + attributes: [ + { + key: 'event', + value: 'Tx', + index: false, + }, + ], + }, + { + type: 'tx', + attributes: [ + { + key: 'hash', + value: '56458F2D8A8617EA322931B72C103CDD93820004E534295183A6EF215B93C76E', + index: false, + }, + ], + }, + { + type: 'tx', + attributes: [ + { + key: 'height', + value: '145', + index: false, + }, + ], + }, + ], + }, + error: { + query: "tm.event = 'Tx'", + data: { + type: 'tendermint/event/Tx', + value: { + TxResult: { + height: '135', + tx: 'pWR0eXBlAmlhc3NldExvY2ujZXByb29momR0eXBlAGtpbnN0YW50TG9ja1ilAR272lhhsS11I/IKpeDUL1LePc0tXC/pGbpntZ8FDSBuAAAAAMfKlZZZ3oAHaxO0bEIYXCSEpwTuR/baTwASqjgFgDAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa291dHB1dEluZGV4AGt0cmFuc2FjdGlvbljfAwAAAAFl5SQeBBDkK7Us9JcOU+Gp1oi4NIl/01A+5GAKeHi2JwEAAABrSDBFAiEAq9XMPgtU9J0imH6YJ/RtbxwJsavuhIpECU5Lw9h0xpoCIEgkU1njDQCe06YqRyeVYc6wK8G7Y/M5X+XicfJKo5P6ASEDK3jwtdIToEQAgTPMXxpjon4geQaNbbRNT/Xz50UgdHH/////AgEAAAAAAAAAFmoU8HHK+aRqNJOWXjNlOO3iWwvV45CDkAAAAAAAABl2qRTFVGzrfaB6ZhmvE8h2unBNgcJIMIisAAAAAGlzaWduYXR1cmVYQR/OHDEQUcSxczLBvMP9Z0HmRaDoCS6tTyFLbWhn7bAfJTlPF9hIbh13260WSCiDceJjWaYB0JuOGsqu2ZB5F0dDanB1YmxpY0tleXOBo2JpZABkZGF0YVghA85GJWE321+kW0HIwl3M6wO9BIHDxY80HlQgc1wRalT5ZHR5cGUAb3Byb3RvY29sVmVyc2lvbgA=', + result: { + code: 1043, + info: cbor.encode(errorInfo).toString('base64'), + }, + }, + }, + }, + events: + [ + { + type: 'tm', + attributes: [ + { + key: 'event', + value: 'Tx', + index: false, + }, + ], + }, + { + type: 'tx', + attributes: [ + { + key: 'hash', + value: '56458F2D8A8617EA322931B72C103CDD93820004E534295183A6EF215B93C76E', + index: false, + }, + ], + }, + { + type: 'tx', + attributes: [ + { + key: 'height', + value: '135', + index: false, + }, + ], + }, + ], + }, + }; + + proofFixture = { + merkleProof: Buffer.alloc(1, 1), + }; + + call = new GrpcCallMock(this.sinon, { + getStateTransitionHash: this.sinon.stub().returns(hash), + getProve: this.sinon.stub().returns(false), + }); + + tenderDashWsClientMock = new EventEmitter(); + tenderDashWsClientMock.subscribe = this.sinon.stub(); + + stateTransitionFixture = getIdentityCreateTransitionFixture(); + + dppMock = createDPPMock(this.sinon); + dppMock.stateTransition.createFromBuffer.resolves(stateTransitionFixture); + + driveClientMock = { + fetchProofs: this.sinon.stub().resolves({ + identitiesProof: proofFixture, + metadata: { + height: 42, + coreChainLockedHeight: 41, + }, + }), + }; + + blockchainListener = new BlockchainListener(tenderDashWsClientMock); + blockchainListener.start(); + + fetchProofForStateTransition = fetchProofForStateTransitionFactory(driveClientMock); + + const waitForHeight = waitForHeightFactory( + blockchainListener, + ); + + transactionNotFoundError = new Error(); + + transactionNotFoundError.code = -32603; + transactionNotFoundError.data = `tx (${hashString}) not found, err: %!w()`; + + const getExistingTransactionResult = this.sinon.stub().rejects(transactionNotFoundError); + + waitForTransactionToBeProvable = waitForTransactionToBeProvableFactory( + waitForTransactionResult, + getExistingTransactionResult, + waitForHeight, + ); + + createGrpcErrorFromDriveResponseMock = this.sinon.stub().returns( + new NotFoundGrpcError(errorInfo.message, errorInfo.metadata), + ); + + waitForStateTransitionResultHandler = waitForStateTransitionResultHandlerFactory( + fetchProofForStateTransition, + waitForTransactionToBeProvable, + blockchainListener, + dppMock, + createGrpcErrorFromDriveResponseMock, + 1000, + ); + }); + + it('should wait for state transition empty result', async () => { + const promise = waitForStateTransitionResultHandler(call); + + setTimeout(() => { + tenderDashWsClientMock.emit('tm.event = \'Tx\'', wsMessagesFixture.success); + }, 10); + setTimeout(() => { + tenderDashWsClientMock.emit(BlockchainListener.NEW_BLOCK_QUERY, { + data: { value: { block: { header: { height: '145' } } } }, + }); + }, 10); + setTimeout(() => { + tenderDashWsClientMock.emit(BlockchainListener.NEW_BLOCK_QUERY, { + data: { value: { block: { header: { height: '146' } } } }, + }); + }, 10); + + const result = await promise; + + expect(result).to.be.an.instanceOf(WaitForStateTransitionResultResponse); + expect(result.getProof()).to.be.undefined(); + expect(result.getError()).to.be.undefined(); + }); + + it('should wait for state transition and return result with proof', async () => { + call.request.getProve.returns(true); + + const promise = waitForStateTransitionResultHandler(call); + + setTimeout(() => { + tenderDashWsClientMock.emit('tm.event = \'Tx\'', wsMessagesFixture.success); + }, 10); + setTimeout(() => { + tenderDashWsClientMock.emit(BlockchainListener.NEW_BLOCK_QUERY, { + data: { value: { block: { header: { height: '145' } } } }, + }); + }, 10); + setTimeout(() => { + tenderDashWsClientMock.emit(BlockchainListener.NEW_BLOCK_QUERY, { + data: { value: { block: { header: { height: '146' } } } }, + }); + }, 10); + + const result = await promise; + + expect(result).to.be.an.instanceOf(WaitForStateTransitionResultResponse); + expect(result.getError()).to.be.undefined(); + const proof = result.getProof(); + + expect(proof).to.be.an.instanceOf(Proof); + const merkleProof = proof.getMerkleProof(); + + expect(merkleProof).to.deep.equal(proofFixture.merkleProof); + + expect(driveClientMock.fetchProofs).to.be.calledOnceWithExactly({ + identityIds: stateTransitionFixture.getModifiedDataIds() + .map((identifier) => identifier.toBuffer()), + }); + }); + + it('should wait for state transition and return result with error', (done) => { + waitForStateTransitionResultHandler(call).then((result) => { + expect(result).to.be.an.instanceOf(WaitForStateTransitionResultResponse); + expect(result.getProof()).to.be.undefined(); + + const error = result.getError(); + expect(error).to.be.an.instanceOf(StateTransitionBroadcastError); + + const errorData = error.getData(); + const errorCode = error.getCode(); + const errorMessage = error.getMessage(); + + expect(createGrpcErrorFromDriveResponseMock).to.be.calledOnceWithExactly( + wsMessagesFixture.error.data.value.TxResult.result.code, + wsMessagesFixture.error.data.value.TxResult.result.info, + ); + + expect(errorCode).to.equal(wsMessagesFixture.error.data.value.TxResult.result.code); + expect(errorData).to.deep.equal(cbor.encode(errorInfo.metadata)); + expect(errorMessage).to.equal(errorInfo.message); + + done(); + }); + + process.nextTick(() => { + tenderDashWsClientMock.emit('tm.event = \'Tx\'', wsMessagesFixture.error); + }); + }); + + it('should throw an InvalidArgumentGrpcError if stateTransitionHash wasn\'t set', async () => { + request = new WaitForStateTransitionResultRequest(); + + call.request = WaitForStateTransitionResultRequest.deserializeBinary(request.serializeBinary()); + + try { + await waitForStateTransitionResultHandler(call); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('state transition hash is not specified'); + } + }); + + it('should throw DeadlineExceededGrpcError after the timeout', async () => { + const hashString = 'ABFF'; + + request = new WaitForStateTransitionResultRequest(); + + const stHash = Buffer.from(hashString, 'hex'); + + request.setStateTransitionHash(stHash); + + transactionNotFoundError.data = `tx (${hashString}) not found, err: %!w()`; + + call.request = WaitForStateTransitionResultRequest.deserializeBinary(request.serializeBinary()); + + try { + await waitForStateTransitionResultHandler(call); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(DeadlineExceededGrpcError); + expect(e.getMessage()).to.equal(`Waiting period for state transition ${hashString} exceeded`); + expect(e.getRawMetadata()).to.be.deep.equal({ + stateTransitionHash: hashString, + }); + } + }); +}); diff --git a/packages/dapi/test/integration/transactionsFilter/subscribeToNewTransactions.spec.js b/packages/dapi/test/integration/transactionsFilter/subscribeToNewTransactions.spec.js new file mode 100644 index 00000000000..c7c72dabe46 --- /dev/null +++ b/packages/dapi/test/integration/transactionsFilter/subscribeToNewTransactions.spec.js @@ -0,0 +1,463 @@ +const { + Transaction, + Block, + BlockHeader, + MerkleBlock, + PrivateKey, + BloomFilter, + InstantLock, + util: { buffer: BufferUtils }, +} = require('@dashevo/dashcore-lib'); + +const BloomFilterEmitterCollection = require('../../../lib/bloomFilter/emitter/BloomFilterEmitterCollection'); +const ProcessMediator = require('../../../lib/transactionsFilter/ProcessMediator'); + +const subscribeToNewTransactions = require('../../../lib/transactionsFilter/subscribeToNewTransactions'); +const testTransactionsAgainstFilter = require('../../../lib/transactionsFilter/testTransactionAgainstFilter'); +const emitInstantLockToFilterCollectionFactory = require('../../../lib/transactionsFilter/emitInstantLockToFilterCollectionFactory'); + +/** + * Reverse the hash + * + * @param {string} hash + * @returns {string} + */ +function reverseHash(hash) { + return BufferUtils.reverse( + Buffer.from(hash, 'hex'), + ).toString('hex'); +} + +describe('subscribeToNewTransactions', () => { + let bloomFilter; + let bloomFilterEmitterCollection; + let mediator; + let transactions; + let blocks; + let instantLocks; + let instantLockZmqMessagesMocks; + let emitInstantLockToFilterCollection; + + beforeEach(() => { + const address = new PrivateKey().toAddress(); + const anotherAddress = new PrivateKey().toAddress(); + + transactions = []; + transactions.push(new Transaction().to(address, 41)); + transactions.push(new Transaction().to(address, 42)); + transactions.push(new Transaction().to(anotherAddress, 43)); + + transactions.push(new Transaction().to(address, 77)); + transactions.push(new Transaction().to(anotherAddress, 78)); + + const blockHeaderOne = new BlockHeader({ + version: 536870913, + prevHash: '0000000000000000000000000000000000000000000000000000000000000000', + merkleRoot: 'c4970326400177ce67ec582425a698b85ae03cae2b0d168e87eed697f1388e4b', + time: 1507208925, + timestamp: 1507208645, + bits: '1d00dda1', + nonce: 1449878272, + }); + + const blockOne = new Block({ + header: blockHeaderOne.toObject(), + transactions: [transactions[0], transactions[1], transactions[2]], + }); + + const blockHeaderTwo = new BlockHeader({ + version: 536870913, + prevHash: blockOne.hash, + merkleRoot: 'c4970326400177ce67ec582425a698b85ae03cae2b0d168e87eed697f1388e4c', + time: 1507208926, + timestamp: 1507208645, + bits: '1d00dda1', + nonce: 1449878272, + }); + + const blockTwo = new Block({ + header: blockHeaderTwo.toObject(), + transactions: [transactions[3], transactions[4]], + }); + + blocks = []; + blocks.push(blockOne); + blocks.push(blockTwo); + + const instantLockOne = InstantLock.fromObject({ + version: 1, + inputs: [ + { + outpointHash: '6e200d059fb567ba19e92f5c2dcd3dde522fd4e0a50af223752db16158dabb1d', + outpointIndex: 0, + }, + ], + txid: transactions[4].hash, + cyclehash: '7c30826123d0f29fe4c4a8895d7ba4eb469b1fafa6ad7b23896a1a591766a536', + signature: '8967c46529a967b3822e1ba8a173066296d02593f0f59b3a78a30a7eef9c8a120847729e62e4a32954339286b79fe7590221331cd28d576887a263f45b595d499272f656c3f5176987c976239cac16f972d796ad82931d532102a4f95eec7d80', + }); + const instantLockTwo = InstantLock.fromObject({ + version: 1, + inputs: [ + { + outpointHash: '6e200d059fb567ba19e92f5c2dcd3dde522fd4e0a50af223752db16158dabb1d', + outpointIndex: 0, + }, + ], + txid: transactions[3].hash, + cyclehash: '0dc8d0df62b076a7757ab5ca07dde0f1e2bfaf83f94299fd9a77577e6cc7022e', + signature: '8967c46529a967b3822e1ba8a173066296d02593f0f59b3a78a30a7eef9c8a120847729e62e4a32954339286b79fe7590221331cd28d576887a263f45b595d499272f656c3f5176987c976239cac16f972d796ad82931d532102a4f95eec7d80', + }); + const instantLockThree = InstantLock.fromObject({ + version: 1, + inputs: [ + { + outpointHash: '6e200d059fb567ba19e92f5c2dcd3dde522fd4e0a50af223752db16158dabb1d', + outpointIndex: 0, + }, + ], + txid: transactions[0].hash, + cyclehash: '0dc8d0df62b076a7757ab5ca07dde0f1e2bfaf83f94299fd9a77577e6cc7022e', + signature: '8967c46529a967b3822e1ba8a173066296d02593f0f59b3a78a30a7eef9c8a120847729e62e4a32954339286b79fe7590221331cd28d576887a263f45b595d499272f656c3f5176987c976239cac16f972d796ad82931d532102a4f95eec7d80', + }); + + instantLocks = []; + instantLocks.push(instantLockOne); + instantLocks.push(instantLockTwo); + instantLocks.push(instantLockThree); + + instantLockZmqMessagesMocks = [ + Buffer.concat([transactions[4].toBuffer(), instantLockOne.toBuffer()]), + Buffer.concat([transactions[3].toBuffer(), instantLockTwo.toBuffer()]), + Buffer.concat([transactions[0].toBuffer(), instantLockThree.toBuffer()]), + ]; + + bloomFilter = BloomFilter.create(1, 0.0001); + bloomFilter.insert(address.hashBuffer); + + bloomFilterEmitterCollection = new BloomFilterEmitterCollection(); + mediator = new ProcessMediator(); + + emitInstantLockToFilterCollection = emitInstantLockToFilterCollectionFactory( + bloomFilterEmitterCollection, + ); + }); + + it('should add transactions and blocks in cache and send them back when historical data is sent', () => { + const receivedTransactions = []; + const receivedBlocks = []; + + mediator.on(ProcessMediator.EVENTS.TRANSACTION, (tx) => { + receivedTransactions.push(tx); + }); + + mediator.on(ProcessMediator.EVENTS.MERKLE_BLOCK, (merkleBlock) => { + receivedBlocks.push(merkleBlock); + }); + + subscribeToNewTransactions( + mediator, + bloomFilter, + testTransactionsAgainstFilter, + bloomFilterEmitterCollection, + ); + + bloomFilterEmitterCollection.test(transactions[0]); + bloomFilterEmitterCollection.test(transactions[1]); + bloomFilterEmitterCollection.test(transactions[2]); + + bloomFilterEmitterCollection.emit('block', blocks[0]); + + mediator.emit(ProcessMediator.EVENTS.MEMPOOL_DATA_SENT); + mediator.emit(ProcessMediator.EVENTS.CLIENT_DISCONNECTED); + + expect(receivedTransactions).to.deep.equal([ + transactions[0], + transactions[1], + ]); + + const expectedMerkleBlock = MerkleBlock.build( + blocks[0].header, + [ + Buffer.from(transactions[0].hash, 'hex'), + Buffer.from(transactions[1].hash, 'hex'), + Buffer.from(transactions[2].hash, 'hex'), + ], + [true, true, false], + ); + + expectedMerkleBlock.hashes = expectedMerkleBlock.hashes + .map((hash) => reverseHash(hash)); + + expect(receivedBlocks).to.have.a.lengthOf(1); + expect(receivedBlocks[0]).to.deep.equal(expectedMerkleBlock); + }); + + it('should scan block for matching transactions if it is the first one arrived', () => { + const receivedTransactions = []; + const receivedBlocks = []; + + mediator.on(ProcessMediator.EVENTS.TRANSACTION, (tx) => { + receivedTransactions.push(tx); + }); + + mediator.on(ProcessMediator.EVENTS.MERKLE_BLOCK, (merkleBlock) => { + receivedBlocks.push(merkleBlock); + }); + + subscribeToNewTransactions( + mediator, + bloomFilter, + testTransactionsAgainstFilter, + bloomFilterEmitterCollection, + ); + + bloomFilterEmitterCollection.test(transactions[2]); + + bloomFilterEmitterCollection.emit('block', blocks[0]); + + mediator.emit(ProcessMediator.EVENTS.MEMPOOL_DATA_SENT); + mediator.emit(ProcessMediator.EVENTS.CLIENT_DISCONNECTED); + + expect(receivedTransactions).to.deep.equal([ + transactions[0], + transactions[1], + ]); + + const expectedMerkleBlock = MerkleBlock.build( + blocks[0].header, + [ + Buffer.from(transactions[0].hash, 'hex'), + Buffer.from(transactions[1].hash, 'hex'), + Buffer.from(transactions[2].hash, 'hex'), + ], + [true, true, false], + ); + + expectedMerkleBlock.hashes = expectedMerkleBlock.hashes + .map((hash) => reverseHash(hash)); + + expect(receivedBlocks).to.have.a.lengthOf(1); + expect(receivedBlocks[0]).to.deep.equal(expectedMerkleBlock); + }); + + it('should remove historical data from cache and send only data that is left', () => { + const receivedTransactions = []; + const receivedBlocks = []; + + mediator.on(ProcessMediator.EVENTS.TRANSACTION, (tx) => { + receivedTransactions.push(tx); + }); + + mediator.on(ProcessMediator.EVENTS.MERKLE_BLOCK, (merkleBlock) => { + receivedBlocks.push(merkleBlock); + }); + + subscribeToNewTransactions( + mediator, + bloomFilter, + testTransactionsAgainstFilter, + bloomFilterEmitterCollection, + ); + + bloomFilterEmitterCollection.test(transactions[0]); + bloomFilterEmitterCollection.test(transactions[1]); + bloomFilterEmitterCollection.test(transactions[2]); + + bloomFilterEmitterCollection.emit('block', blocks[0]); + + bloomFilterEmitterCollection.test(transactions[3]); + bloomFilterEmitterCollection.test(transactions[4]); + + bloomFilterEmitterCollection.emit('block', blocks[1]); + + mediator.emit(ProcessMediator.EVENTS.HISTORICAL_BLOCK_SENT, blocks[0].hash); + + mediator.emit(ProcessMediator.EVENTS.MEMPOOL_DATA_SENT); + mediator.emit(ProcessMediator.EVENTS.CLIENT_DISCONNECTED); + + expect(receivedTransactions).to.deep.equal([ + transactions[3], + ]); + + const expectedMerkleBlock = MerkleBlock.build( + blocks[1].header, + [ + Buffer.from(transactions[3].hash, 'hex'), + Buffer.from(transactions[4].hash, 'hex'), + ], + [true, false], + ); + + expectedMerkleBlock.hashes = expectedMerkleBlock.hashes + .map((hash) => reverseHash(hash)); + + expect(receivedBlocks).to.have.a.lengthOf(1); + expect(receivedBlocks[0]).to.deep.equal(expectedMerkleBlock); + }); + + it('should send instant locks for new transactions', () => { + const receivedTransactions = []; + const receivedBlocks = []; + const receivedInstantLocks = []; + + mediator.on(ProcessMediator.EVENTS.TRANSACTION, (tx) => { + receivedTransactions.push(tx); + }); + + mediator.on(ProcessMediator.EVENTS.MERKLE_BLOCK, (merkleBlock) => { + receivedBlocks.push(merkleBlock); + }); + + mediator.on(ProcessMediator.EVENTS.INSTANT_LOCK, (instantLock) => { + receivedInstantLocks.push(instantLock); + }); + + subscribeToNewTransactions( + mediator, + bloomFilter, + testTransactionsAgainstFilter, + bloomFilterEmitterCollection, + ); + + // Read historical data + + bloomFilterEmitterCollection.test(transactions[0]); + bloomFilterEmitterCollection.test(transactions[1]); + bloomFilterEmitterCollection.test(transactions[2]); + + bloomFilterEmitterCollection.emit('block', blocks[0]); + + bloomFilterEmitterCollection.test(transactions[3]); + bloomFilterEmitterCollection.test(transactions[4]); + + emitInstantLockToFilterCollection(instantLockZmqMessagesMocks[0]); + emitInstantLockToFilterCollection(instantLockZmqMessagesMocks[1]); + emitInstantLockToFilterCollection(instantLockZmqMessagesMocks[2]); + + bloomFilterEmitterCollection.emit('block', blocks[1]); + + mediator.emit(ProcessMediator.EVENTS.HISTORICAL_BLOCK_SENT, blocks[0].hash); + + mediator.emit(ProcessMediator.EVENTS.MEMPOOL_DATA_SENT); + mediator.emit(ProcessMediator.EVENTS.CLIENT_DISCONNECTED); + + expect(receivedTransactions).to.deep.equal([ + transactions[3], + ]); + + const expectedMerkleBlock = MerkleBlock.build( + blocks[1].header, + [ + Buffer.from(transactions[3].hash, 'hex'), + Buffer.from(transactions[4].hash, 'hex'), + ], + [true, false], + ); + + expectedMerkleBlock.hashes = expectedMerkleBlock.hashes + .map((hash) => reverseHash(hash)); + + expect(receivedBlocks).to.have.a.lengthOf(1); + expect(receivedBlocks[0]).to.deep.equal(expectedMerkleBlock); + + // Deep copy instant lock + const expectedInstantLock = InstantLock.fromBuffer(instantLocks[1].toBuffer()); + + expect(receivedInstantLocks).to.have.length(2); + expect(receivedInstantLocks[0]).to.be.deep.equal(expectedInstantLock); + expect(receivedInstantLocks[0].txid).to.be.equal(receivedTransactions[0].hash); + + // The second transaction is the transaction that was added to the cache during historical sync, + // which isn't covered by this test, but we still expect to receive an instant lock here, + // since it waits for some time in the cache before being completely removed. + const expectedInstantLockTwo = InstantLock.fromBuffer(instantLocks[2].toBuffer()); + + expect(receivedInstantLocks[1]).to.be.deep.equal(expectedInstantLockTwo); + expect(receivedInstantLocks[1].txid).to.be.equal(transactions[0].hash); + }); + + it('should remove transaction from instant lock waiting list if it sits in the cache for too long', () => { + const receivedTransactions = []; + const receivedBlocks = []; + const receivedInstantLocks = []; + + mediator.on(ProcessMediator.EVENTS.TRANSACTION, (tx) => { + receivedTransactions.push(tx); + }); + + mediator.on(ProcessMediator.EVENTS.MERKLE_BLOCK, (merkleBlock) => { + receivedBlocks.push(merkleBlock); + }); + + mediator.on(ProcessMediator.EVENTS.INSTANT_LOCK, (instantLock) => { + receivedInstantLocks.push(instantLock); + }); + + subscribeToNewTransactions( + mediator, + bloomFilter, + testTransactionsAgainstFilter, + bloomFilterEmitterCollection, + ); + + bloomFilterEmitterCollection.test(transactions[0]); + bloomFilterEmitterCollection.test(transactions[1]); + bloomFilterEmitterCollection.test(transactions[2]); + + // emit 10 'block' events to get transaction 0 to be removed from the instant lock cache + for (let i = 0; i <= 10; i++) { + bloomFilterEmitterCollection.emit('block', blocks[0]); + } + + bloomFilterEmitterCollection.test(transactions[3]); + + // Not part of bloom filter + bloomFilterEmitterCollection.test(transactions[4]); + + // transaction 4. Not part of bloom filter + emitInstantLockToFilterCollection(instantLockZmqMessagesMocks[0]); + // transaction 3 + emitInstantLockToFilterCollection(instantLockZmqMessagesMocks[1]); + // transaction 0 + emitInstantLockToFilterCollection(instantLockZmqMessagesMocks[2]); + + // transaction 3 and 4 + bloomFilterEmitterCollection.emit('block', blocks[1]); + + mediator.emit(ProcessMediator.EVENTS.HISTORICAL_BLOCK_SENT, blocks[0].hash); + + mediator.emit(ProcessMediator.EVENTS.MEMPOOL_DATA_SENT); + mediator.emit(ProcessMediator.EVENTS.CLIENT_DISCONNECTED); + + expect(receivedTransactions).to.deep.equal([ + transactions[3], + ]); + + const expectedMerkleBlock = MerkleBlock.build( + blocks[1].header, + [ + Buffer.from(transactions[3].hash, 'hex'), + Buffer.from(transactions[4].hash, 'hex'), + ], + [true, false], + ); + + expectedMerkleBlock.hashes = expectedMerkleBlock.hashes + .map((hash) => reverseHash(hash)); + + expect(receivedBlocks).to.have.a.lengthOf(10); + + // Unlike in the test above, because we've emitted some blocks, the second + // instant lock should be removed from the cache + // expected instant lock for transaction 3 + const expectedInstantLock = InstantLock.fromBuffer(instantLocks[1].toBuffer()); + + // Actual + // transaction 3 + // transaction 0 + expect(receivedInstantLocks).to.have.length(1); + expect(receivedInstantLocks[0]).to.be.deep.equal(expectedInstantLock); + expect(receivedInstantLocks[0].txid).to.be.equal(receivedTransactions[0].hash); + }); +}); diff --git a/packages/dapi/test/integration/transactionsFilter/testTransactionAgainstFilter.spec.js b/packages/dapi/test/integration/transactionsFilter/testTransactionAgainstFilter.spec.js new file mode 100644 index 00000000000..bc82d2a7dbd --- /dev/null +++ b/packages/dapi/test/integration/transactionsFilter/testTransactionAgainstFilter.spec.js @@ -0,0 +1,69 @@ +const { mocha: { startDashCore } } = require('@dashevo/dp-services-ctl'); + +const { + Transaction, + PrivateKey, + BloomFilter, + Address, + Networks, + MerkleBlock, +} = require('@dashevo/dashcore-lib'); + +const testTransactionAgainstFilter = require('../../../lib/transactionsFilter/testTransactionAgainstFilter'); + +describe('testTransactionAgainstFilter', () => { + let coreApi; + + startDashCore().then((core) => { + coreApi = core.getApi(); + }); + + it('should match the same transaction as Core', async () => { + // Create a transactions + const { result: addressBase58 } = await coreApi.getnewaddress(); + const { result: privateKeyString } = await coreApi.dumpprivkey(addressBase58); + + const address = Address.fromString(addressBase58, Networks.testnet); + const privateKey = new PrivateKey(privateKeyString); + + await coreApi.generateToAddress(101, addressBase58); + + const { result: unspent } = await coreApi.listunspent(); + const inputs = unspent.filter((input) => input.address === addressBase58); + + const transaction = new Transaction() + .from(inputs) + .to(address, 10000) + .change(address) + .sign(privateKey); + + // Create a bloom filter + const filter = BloomFilter.create(1, 0.0001); + filter.insert(address.hashBuffer); + + // Test transaction with `testTransactionAgainstFilter` function + const result = testTransactionAgainstFilter(filter, transaction); + expect(result).to.be.true(); + + // Test transaction with Core + await coreApi.sendrawtransaction(transaction.serialize()); + + await coreApi.generateToAddress(1, addressBase58); + + const { result: firstBlockHash } = await coreApi.getBlockHash(1); + + const { result: merkleBlockStrings } = await coreApi.getMerkleBlocks( + filter.toBuffer().toString('hex'), + firstBlockHash, + ); + + expect(merkleBlockStrings).to.be.an('array'); + + const merkleBlockWithTransaction = merkleBlockStrings + .map((merkleBlockString) => new MerkleBlock(Buffer.from(merkleBlockString, 'hex'))) + .find((merkleBlock) => merkleBlock.hasTransaction(transaction)); + + expect(merkleBlockWithTransaction).to.be.instanceOf(MerkleBlock); + expect(merkleBlockWithTransaction.hasTransaction(transaction)).to.be.true(); + }); +}); diff --git a/packages/dapi/test/mocks/config.js b/packages/dapi/test/mocks/config.js new file mode 100644 index 00000000000..a2bf8360cdd --- /dev/null +++ b/packages/dapi/test/mocks/config.js @@ -0,0 +1,35 @@ +module.exports = { + getConfigFixture() { + return { + dashcore: { + p2p: { + host: '123', + port: '123', + }, + rpc: { + host: '123', + port: '123', + }, + zmq: { + port: '123', + host: '123', + }, + }, + tendermintCore: { + host: '123', + port: '123', + }, + rpcServer: { + port: '123', + }, + grpcServer: { + port: '123', + }, + txFilterStream: { + grpcServer: { + port: '123', + }, + }, + }; + }, +}; diff --git a/packages/dapi/test/mocks/coreAPIFixture.js b/packages/dapi/test/mocks/coreAPIFixture.js new file mode 100644 index 00000000000..185f5921472 --- /dev/null +++ b/packages/dapi/test/mocks/coreAPIFixture.js @@ -0,0 +1,91 @@ +/* eslint-disable no-unused-vars */ +// Unused variables represent signatures for clarity +module.exports = { + async estimateFee(numberOfBlocks) { return 1; }, + async getAddressSummary(address) { return {}; }, + async getAddressTotalReceived(address) { return 1000; }, + async getAddressTotalSent(address) { return 900; }, + async getAddressUnconfirmedBalance(address) { return 1100; }, + async getBalance(address) { return 100; }, + async getBestBlockHash() { return '000000000074fc08fb6a92cb8994b14307038261e4266abc6994fa03955a1a59'; }, + async getBestBlockHeight() { return 243789; }, + async getBlockHash() { return 'hash'; }, + async getBlockHeaders() { return [{}]; }, + async getBlockHeader() { return {}; }, + async getBlocks(blockDate, limit) { return [{}]; }, + async getHistoricBlockchainDataSyncStatus() { + return {}; + }, + async getMasternodesList() { return [{ ip: '127.0.0.1' }]; }, + async getMempoolInfo() { + return { + size: 0, + bytes: 0, + usage: 384, + maxmempool: 300000000, + mempoolminfee: 0.00000000, + }; + }, + async getPeerDataSyncStatus() { return ''; }, + async getMnListDiff() { + return { + baseBlockHash: '0000000000000000000000000000000000000000000000000000000000000000', + blockHash: '0000000000000000000000000000000000000000000000000000000000000000', + deletedMNs: [], + mnList: [], + merkleRootMNList: '0000000000000000000000000000000000000000000000000000000000000000', + }; + }, + async getRawBlock(blockHash) { return {}; }, + async getStatus(query) { return {}; }, + async getRawTransaction(txid) { return {}; }, + async getUser(usernameOrUserId) { return {}; }, + async getUTXO(address) { return []; }, + async sendRawTransaction(rawTransaction) { return 'txid'; }, + async sendRawIxTransaction(rawTransaction) { return 'txid'; }, + async generateToAddress(blocksNumber, address) { return new Array(blocksNumber); }, + async sendRawTransition(rawStateTransition) { return 'tsid'; }, + // Todo: not yet final spec so it may change + async getQuorum() { + return { + quorum: [ + { + proRegTxHash: '3450cdbaa92432dd19672738342cb4f2467f1a8b142c31142ea39e14f3ab8c18', + service: '165.227.144.38:19999', + keyIDOperator: 'e6be850bfe045d2cd2b0e5789010b1a910dd7d27', + keyIDVoting: 'e6be850bfe045d2cd2b0e5789010b1a910dd7d27', + isValid: true, + }, + { + proRegTxHash: '47b3adaa8ed42c6c67abb317e631cf674381cd8fd87033bcb92f3e2d21d08360', + service: '159.89.110.184:19999', + keyIDOperator: '4d5fce2325deb034ae75a625a3e2f09395e27bf7', + keyIDVoting: '4d5fce2325deb034ae75a625a3e2f09395e27bf7', + isValid: true, + }, + { + proRegTxHash: '049d0c6dd63bb50c0bfee9106ad7ce5f9b4e9ef4487552cd4638317b3b05ffee', + service: '142.93.170.82:19999', + keyIDOperator: 'cfdee11fc2b4ebf6e1cafb262269de4919698942', + keyIDVoting: 'cfdee11fc2b4ebf6e1cafb262269de4919698942', + isValid: true, + }, + ], + + proofs: { + merkleHashes: ['71e9bc59632243e13f2d4c463296cd5a7737beb397799fc5fc9ada93b69bf48c'], + merkleFlags: 0x1d, + blockHash: 'b5d2cd463831d63b7b3b05f0c0bfefee7ce5f9b4e9ef448755e049d0c6d9106a', + totalTransactions: 1, + }, + // TODO: after dashcore-lib specialtx + quorumCommitmentTransaction: { + quorumHash: 'd63bb5d2cd4638317b3b05f0c0bfee049d0c6d9106afee7ce5f9b4e9ef448755', + prop1: '', + prop2: '', + prop3: '', + prop4: '', + }, + }; + }, +}; diff --git a/packages/dapi/test/unit/Logger.js b/packages/dapi/test/unit/Logger.js new file mode 100644 index 00000000000..45ec0da6065 --- /dev/null +++ b/packages/dapi/test/unit/Logger.js @@ -0,0 +1,20 @@ +const assert = require('assert'); +const Logger = require('../../lib/log/Logger'); + +describe('Logger', () => { + it('should create a new Logger object', () => { + const actual = typeof new Logger(); + const expected = 'object'; + assert.equal(actual, expected); + }); + it('should default to the INFO log level', () => { + const actual = new Logger().level; + const expected = 4; + assert.equal(actual, expected); + }); + it('should default to logging to the console', () => { + const actual = new Logger().outputFilePath; + const expected = undefined; + assert.equal(actual, expected); + }); +}); diff --git a/packages/dapi/test/unit/chainDataProvider/chainDataProvider.spec.js b/packages/dapi/test/unit/chainDataProvider/chainDataProvider.spec.js new file mode 100644 index 00000000000..39724765ea7 --- /dev/null +++ b/packages/dapi/test/unit/chainDataProvider/chainDataProvider.spec.js @@ -0,0 +1,287 @@ +const { BlockHeader } = require('@dashevo/dashcore-lib'); +const ChainDataProvider = require('../../../lib/chainDataProvider/ChainDataProvider'); +const BlockHeadersCache = require('../../../lib/chainDataProvider/BlockHeadersCache'); + +const headers = [ + { + version: 2, + prevHash: '00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc2c', + merkleRoot: 'b4fd581bc4bfe51a5a66d8b823bd6ee2b492f0ddc44cf7e820550714cedc117f', + time: 1398712771, + bits: '1e0fffff', + nonce: 31475, + }, + { + version: 2, + prevHash: '0000047d24635e347be3aaaeb66c26be94901a2f962feccd4f95090191f208c1', + merkleRoot: '0d6d332e68eb8ecc66a5baaa95dc4b10c0b32841aed57dc99a5ae0b2f9e4294d', + time: 1398712772, + nonce: 6523, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000c6264fab4ba2d23990396f42a76aa4822f03cbc7634b79f4dfea36fccc2', + merkleRoot: '1cc711129405a328c58d1948e748c3b8f3d610e66d9901db88c42c5247829658', + time: 1398712774, + nonce: 53194, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '0000057d5c945acbe476bc17bbbaeb2fc1c1b18673e7582c48ac04af61f4d811', + merkleRoot: '7e6b1b1457308bf6ccb1e325c64607ba7dfac05e26c08887cd28f97d4d4ab3e2', + time: 1398712782, + nonce: 193159, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '000002258bd58bf4cdcde282abc030437c103dbb12d2a7dbc978d07bcf386b42', + merkleRoot: '4cf4f3b788e8dc847a9e0ff3b279340207c555a6bc0736f93e95dbdc2e3c2f16', + time: 1398712784, + nonce: 41103, + bits: '1e0ffff0', + }]; + +describe('ChainDataProvider', () => { + const fakeHeaders = headers.map((e) => new BlockHeader(e)); + + let coreAPIMock; + let blockHeadersCache; + let chainDataProvider; + let cacheSpy; + + beforeEach(async function it() { + const blockHash = fakeHeaders[0].hash; + + coreAPIMock = { + getBestChainLock: this.sinon.stub(), + getBlockHeader: this.sinon.stub(), + getBlockHeaders: this.sinon.stub(), + getBlockStats: this.sinon.stub(), + }; + const zmqClientMock = { on: this.sinon.stub(), topics: { rawblock: '', rawtx: '' } }; + + blockHeadersCache = new BlockHeadersCache(); + cacheSpy = this.sinon.spy(blockHeadersCache); + chainDataProvider = new ChainDataProvider(coreAPIMock, zmqClientMock, blockHeadersCache); + + coreAPIMock.getBestChainLock.resolves({ + height: 1, + signature: Buffer.from('fakeSig'), + blockHash, + }); + + await chainDataProvider.init(); + coreAPIMock.getBestChainLock.resetHistory(); + + blockHeadersCache.purge(); + + cacheSpy.set.resetHistory(); + cacheSpy.get.resetHistory(); + }); + + it('should call for chainlock on init', async () => { + await chainDataProvider.init(); + + expect(coreAPIMock.getBestChainLock).to.be.calledOnceWithExactly(); + }); + + it('should call for rpc on getBlockHeader when cache is empty', async () => { + const [fakeBlockHeader] = fakeHeaders; + + coreAPIMock.getBlockHeader.resolves(fakeBlockHeader.toString()); + + await chainDataProvider.getBlockHeader(fakeBlockHeader.hash); + + expect(coreAPIMock.getBlockHeader).to.be.calledOnceWithExactly(fakeBlockHeader.hash); + + expect(cacheSpy.get).to.be.calledWith(fakeBlockHeader.hash); + expect(cacheSpy.get).to.always.returned(undefined); + }); + + it('should return cache on getBlockHeader if it is cached', async () => { + const [fakeBlockHeader] = fakeHeaders; + + blockHeadersCache.set(fakeBlockHeader.hash, fakeBlockHeader.toBuffer()); + cacheSpy.set.resetHistory(); + + await chainDataProvider.getBlockHeader(fakeBlockHeader.hash); + + expect(cacheSpy.get.callCount).to.be.equal(1); + expect(cacheSpy.set.callCount).to.be.equal(0); + + expect(coreAPIMock.getBlockHeader.callCount).to.be.equal(0); + }); + + // the case where we request for N blocks, and theres nothing at all in the cache + it('should call for rpc when nothing is cached', async () => { + const [first, second, third, fourth, fifth] = fakeHeaders; + + coreAPIMock.getBlockStats.resolves({ height: 1 }); + coreAPIMock.getBlockHeaders.resolves(fakeHeaders.map((e) => e.toString())); + + await chainDataProvider.getBlockHeaders(first.hash, 1, 5); + + expect(cacheSpy.get.callCount).to.be.equal(5); + expect(cacheSpy.set.callCount).to.be.equal(5); + + expect(cacheSpy.get).to.always.returned(undefined); + + expect(cacheSpy.set.getCall(0).args[0]).to.be.equal(1); + expect(cacheSpy.set.getCall(0).args[1].toString()).to.be.equal(first.toString()); + + expect(cacheSpy.set.getCall(1).args[0]).to.be.equal(2); + expect(cacheSpy.set.getCall(1).args[1].toString()).to.be.equal(second.toString()); + + expect(cacheSpy.set.getCall(2).args[0]).to.be.equal(3); + expect(cacheSpy.set.getCall(2).args[1].toString()).to.be.equal(third.toString()); + + expect(cacheSpy.set.getCall(3).args[0]).to.be.equal(4); + expect(cacheSpy.set.getCall(3).args[1].toString()).to.be.equal(fourth.toString()); + + expect(cacheSpy.set.getCall(4).args[0]).to.be.equal(5); + expect(cacheSpy.set.getCall(4).args[1].toString()).to.be.equal(fifth.toString()); + }); + + // the case when we request for cached blocks (all N block are cached) + it('should use cache and do not call for blockHeaders', async () => { + const [first, second, third] = fakeHeaders; + + coreAPIMock.getBlockStats.resolves({ height: 1 }); + + blockHeadersCache.set(1, first); + blockHeadersCache.set(2, second); + blockHeadersCache.set(3, third); + + cacheSpy.set.resetHistory(); + + await chainDataProvider.getBlockHeaders(first.hash, 1, 3); + + expect(coreAPIMock.getBlockHeaders.callCount).to.be.equal(0); + + expect(cacheSpy.get.callCount).to.be.equal(3); + expect(cacheSpy.set.callCount).to.be.equal(0); + + expect(cacheSpy.get.getCall(0).returnValue.toString()).to.deep.equal(first.toString()); + expect(cacheSpy.get.getCall(1).returnValue.toString()).to.deep.equal(second.toString()); + expect(cacheSpy.get.getCall(2).returnValue.toString()).to.deep.equal(third.toString()); + }); + + // the case where we are missing some blocks in the tail + // f.e. we request for 5 blocks, and what we have in cache is [1,2,3,undefined,undefined] + // we should call for 3 blocks (3,4,5) and set 2 missing in the cache + it('should use cache when miss something in the tail', async () => { + const [first, second, third, fourth, fifth] = fakeHeaders; + + coreAPIMock.getBlockStats.resolves({ height: 1 }); + coreAPIMock.getBlockHeaders.resolves([third.toString(), fourth.toString(), + fifth.toString()]); + + // should use cache and does not hit rpc + blockHeadersCache.set(1, first); + blockHeadersCache.set(2, second); + blockHeadersCache.set(3, third); + blockHeadersCache.set(4, undefined); + blockHeadersCache.set(5, undefined); + cacheSpy.set.resetHistory(); + + await chainDataProvider.getBlockHeaders(first.hash, 1, 5); + + expect(coreAPIMock.getBlockHeaders).to.be.calledOnceWithExactly(third.hash, 3); + + expect(cacheSpy.get.callCount).to.be.equal(5); + expect(cacheSpy.set.callCount).to.be.equal(3); + + expect(cacheSpy.get.getCall(0).returnValue.toString()).to.deep.equal(first.toString()); + expect(cacheSpy.get.getCall(1).returnValue.toString()).to.deep.equal(second.toString()); + expect(cacheSpy.get.getCall(2).returnValue.toString()).to.deep.equal(third.toString()); + expect(cacheSpy.get.getCall(3).returnValue).to.deep.equal(undefined); + expect(cacheSpy.get.getCall(4).returnValue).to.deep.equal(undefined); + }); + + // the case when we miss something in the middle + // f.e we request for 5 blocks, and cache is [1,2,undefined,undefined,5] + // should take second block as a start point and request for 4 blocks (to the end) + it('should use cache when missing in the middle', async () => { + const [first, second, third, fourth, fifth] = fakeHeaders; + + coreAPIMock.getBlockStats.resolves({ height: 1 }); + coreAPIMock.getBlockHeaders.resolves([second.toString(), third.toString(), + fourth.toString(), fifth.toString()]); + + blockHeadersCache.set(1, first); + blockHeadersCache.set(2, second); + blockHeadersCache.set(3, undefined); + blockHeadersCache.set(4, undefined); + blockHeadersCache.set(5, fifth); + cacheSpy.set.resetHistory(); + + await chainDataProvider.getBlockHeaders(first.hash, 1, 5); + + expect(cacheSpy.get.callCount).to.be.equal(5); + expect(cacheSpy.set.callCount).to.be.equal(4); + + expect(cacheSpy.get.getCall(0).returnValue.toString()).to.deep.equal(first.toString()); + expect(cacheSpy.get.getCall(1).returnValue.toString()).to.deep.equal(second.toString()); + expect(cacheSpy.get.getCall(2).returnValue).to.deep.equal(undefined); + expect(cacheSpy.get.getCall(3).returnValue).to.deep.equal(undefined); + expect(cacheSpy.get.getCall(4).returnValue).to.deep.equal(fifth); + }); + + // the case where we have something in the cache, but the first blocks are not + // f.e. we request for 5 blocks, and cache is [undefined,undefined,3,4,5] + it('should not use cache when miss something in the beginning', async () => { + const [first,, third, fourth, fifth] = fakeHeaders; + + coreAPIMock.getBlockStats.resolves({ height: 1 }); + coreAPIMock.getBlockHeaders.resolves(fakeHeaders.map((e) => e.toString())); + + blockHeadersCache.set(1, undefined); + blockHeadersCache.set(2, undefined); + blockHeadersCache.set(3, third); + blockHeadersCache.set(4, fourth); + blockHeadersCache.set(5, fifth); + cacheSpy.set.resetHistory(); + + await chainDataProvider.getBlockHeaders(first.toString(), 1, 5); + + expect(coreAPIMock.getBlockHeaders).to.be + .calledOnceWithExactly(first.toString(), 5); + + expect(cacheSpy.get.callCount).to.be.equal(5); + expect(cacheSpy.set.callCount).to.be.equal(5); + + expect(cacheSpy.get.getCall(0).returnValue).to.deep.equal(undefined); + expect(cacheSpy.get.getCall(1).returnValue).to.deep.equal(undefined); + expect(cacheSpy.get.getCall(2).returnValue.toString()).to.deep.equal(third.toString()); + expect(cacheSpy.get.getCall(3).returnValue.toString()).to.deep.equal(fourth.toString()); + expect(cacheSpy.get.getCall(4).returnValue.toString()).to.deep.equal(fifth.toString()); + }); + + // the same as above, but with additional gap + // [undefined,2,undefined,4,5] + it('should not use cache when miss something in the beginning', async () => { + const [first, second, third, fourth, fifth] = fakeHeaders; + + coreAPIMock.getBlockStats.resolves({ height: 1 }); + coreAPIMock.getBlockHeaders.resolves([first.toString(), second.toString(), + third.toString(), fourth.toString(), fifth.toString()]); + + blockHeadersCache.set(1, undefined); + blockHeadersCache.set(2, second); + blockHeadersCache.set(3, undefined); + blockHeadersCache.set(4, third); + blockHeadersCache.set(5, fourth); + cacheSpy.set.resetHistory(); + + await chainDataProvider.getBlockHeaders(first.toString(), 1, 5); + + expect(coreAPIMock.getBlockHeaders).to.be + .calledOnceWithExactly(first.toString(), 5); + + expect(cacheSpy.get.callCount).to.be.equal(5); + expect(cacheSpy.set.callCount).to.be.equal(5); + }); +}); diff --git a/packages/dapi/test/unit/config/index.js b/packages/dapi/test/unit/config/index.js new file mode 100644 index 00000000000..1579893c842 --- /dev/null +++ b/packages/dapi/test/unit/config/index.js @@ -0,0 +1,74 @@ +/* eslint-disable no-unused-expressions */ +const chai = require('chai'); + +const { getConfigFixture } = require('../../mocks/config'); + +const { validateConfig, validateHost, validatePort } = require('../../../lib/config/validator'); + +const { expect } = chai; + +describe('config/validator', () => { + describe('validateConfig', () => { + it('Should return an object with isValid and validationErrors fields', () => { + const config = getConfigFixture(); + + const validationResult = validateConfig(config); + + expect(validationResult).to.have.a.property('isValid'); + expect(validationResult.isValid).to.be.a('boolean'); + + expect(validationResult).to.have.a.property('validationErrors'); + expect(validationResult.validationErrors).to.be.an('array'); + }); + it('Should return and empty array in validationErrors if there is no errors', () => { + const config = getConfigFixture(); + + const validationResult = validateConfig(config); + + expect(validationResult.isValid).to.be.true; + expect(validationResult.validationErrors.length).to.be.equal(0); + }); + it('Should return errors in array if there are invalid fields in the config', () => { + const config = getConfigFixture(); + + config.dashcore.p2p.host = 1; + config.dashcore.p2p.port = '$/*'; + const validationResult = validateConfig(config); + + expect(validationResult.isValid).to.be.false; + expect(validationResult.validationErrors.length).to.be.equal(2); + }); + }); + describe('validateHost', () => { + it('Should return true in isValid field that host is an alphanumeric value', () => { + // It is an alphanumeric to support docker + // eslint-disable-next-line no-underscore-dangle + expect(validateHost('asd.com').isValid).to.be.true; + expect(validateHost('127.0.0.1').isValid).to.be.true; + expect(validateHost('asd').isValid).to.be.true; + expect(validateHost('true').isValid).to.be.true; + expect(validateHost('127.0.0').isValid).to.be.true; + expect(validateHost('127.0.0.1:123').isValid).to.be.true; + + expect(validateHost(1).isValid).to.be.false; + expect(validateHost({}).isValid).to.be.false; + expect(validateHost(true).isValid).to.be.false; + expect(validateHost(undefined).isValid).to.be.false; + expect(validateHost(null).isValid).to.be.false; + expect(validateHost('').isValid).to.be.false; + }); + }); + describe('validatePort', () => { + it('Should return true in isValid field if value is a valid port', () => { + // eslint-disable-next-line no-underscore-dangle + expect(validatePort('1000').isValid).to.be.true; + expect(validatePort('1').isValid).to.be.true; + expect(validatePort('22').isValid).to.be.true; + + expect(validatePort('asd').isValid).to.be.false; + expect(validatePort('true').isValid).to.be.false; + expect(validatePort('-1').isValid).to.be.false; + expect(validatePort('654321').isValid).to.be.false; + }); + }); +}); diff --git a/packages/dapi/test/unit/externalApis/dashcore/ZmqClient.js b/packages/dapi/test/unit/externalApis/dashcore/ZmqClient.js new file mode 100644 index 00000000000..7733ae0ac3a --- /dev/null +++ b/packages/dapi/test/unit/externalApis/dashcore/ZmqClient.js @@ -0,0 +1,19 @@ +// const chai = require('chai'); +// const ZmqClient = require('../../../lib/api/dashcore/ZmqClient'); +// +// const { expect } = chai; +// +// describe('ZmqClient', () => { +// describe('#factory', () => { +// it('should return ZmqClient object', () => { +// const res = new ZmqClient(); +// expect(res).to.be.instanceOf(ZmqClient); +// }); +// it('should return error with invalid transaction', async () => { +// const res = new ZmqClient(); +// res.start(); +// res.incrementErrorCount(); +// // res.subscriberSocket.disconnect(); +// }); +// }); +// }); diff --git a/packages/dapi/test/unit/externalApis/drive/DriveClient.js b/packages/dapi/test/unit/externalApis/drive/DriveClient.js new file mode 100644 index 00000000000..a6c8bb69e3b --- /dev/null +++ b/packages/dapi/test/unit/externalApis/drive/DriveClient.js @@ -0,0 +1,256 @@ +const chai = require('chai'); +const sinon = require('sinon'); +const cbor = require('cbor'); + +const chaiAsPromised = require('chai-as-promised'); +const dirtyChai = require('dirty-chai'); + +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); +const InvalidArgumentGrpcError = require('@dashevo/grpc-common/lib/server/error/InvalidArgumentGrpcError'); +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const DriveClient = require('../../../../lib/externalApis/drive/DriveClient'); + +const RPCError = require('../../../../lib/rpcServer/RPCError'); + +chai.use(chaiAsPromised); +chai.use(dirtyChai); + +const { expect } = chai; + +describe('DriveClient', () => { + describe('constructor', () => { + it('Should create drive client with given options', () => { + const drive = new DriveClient({ host: '127.0.0.1', port: 3000 }); + + expect(drive.client.options.host).to.be.equal('127.0.0.1'); + expect(drive.client.options.port).to.be.equal(3000); + }); + }); + + it('should throw RPCError if JSON RPC call failed', async () => { + const drive = new DriveClient({ host: '127.0.0.1', port: 3000 }); + + const error = new Error('Some RPC error'); + + sinon.stub(drive.client, 'request') + .resolves({ error }); + + try { + await drive.fetchDataContract('someId'); + } catch (e) { + expect(e).to.be.an.instanceOf(RPCError); + expect(e.message).to.be.equal(error.message); + expect(e.code).to.be.equal(-32602); + } + }); + + it('should throw ABCI error if response have one', async () => { + const drive = new DriveClient({ host: '127.0.0.1', port: 3000 }); + + sinon.stub(drive.client, 'request') + .resolves({ + result: { + response: { + code: GrpcErrorCodes.INVALID_ARGUMENT, + info: cbor.encode({ + data: { + name: 'someData', + }, + message: 'some message', + }).toString('base64'), + }, + }, + }); + + try { + await drive.fetchDataContract('someId'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidArgumentGrpcError); + expect(e.getCode()).to.equal(3); + expect(e.getMessage()).to.equal('some message'); + expect(e.getRawMetadata()).to.deep.equal({ + 'drive-error-data-bin': cbor.encode({ + name: 'someData', + }), + }); + } + }); + + describe('#fetchDataContract', () => { + it('Should call \'fetchContract\' RPC with the given parameters', async () => { + const drive = new DriveClient({ host: '127.0.0.1', port: 3000 }); + + const contractId = 'someId'; + const data = Buffer.from('someData'); + const proof = Buffer.from('proof'); + + const buffer = cbor.encode({ data, proof }); + + sinon.stub(drive.client, 'request') + .resolves({ + result: { + response: { code: 0, value: buffer.toString('base64') }, + }, + }); + + const result = await drive.fetchDataContract(contractId, true); + + expect(drive.client.request).to.have.been.calledOnceWithExactly('abci_query', { + path: '/dataContracts', + data: cbor.encode({ id: contractId }).toString('hex'), // cbor encoded empty object + prove: true, + }); + expect(result).to.be.deep.equal(buffer); + }); + }); + + describe('#fetchDocuments', () => { + it('Should call \'fetchDocuments\' RPC with the given parameters', async () => { + const drive = new DriveClient({ host: '127.0.0.1', port: 3000 }); + + const contractId = 'someId'; + const type = 'object'; + const options = { + where: 'id === someId', + }; + + const data = []; + const proof = Buffer.from('proof'); + const buffer = cbor.encode({ data, proof }); + + sinon.stub(drive.client, 'request') + .resolves({ + result: { + response: { code: 0, value: buffer.toString('base64') }, + }, + }); + + const result = await drive.fetchDocuments(contractId, type, options, true); + + expect(drive.client.request).to.have.been.calledOnceWithExactly('abci_query', { + path: '/dataContracts/documents', + data: cbor.encode({ ...options, contractId, type }).toString('hex'), // cbor encoded empty object + prove: true, + }); + expect(result).to.be.deep.equal(buffer); + }); + }); + + describe('#fetchIdentity', () => { + it('Should call \'fetchIdentity\' RPC with the given parameters', async () => { + const drive = new DriveClient({ host: '127.0.0.1', port: 3000 }); + + const identityId = 'someId'; + const data = Buffer.from('someData'); + const buffer = cbor.encode({ data }); + + sinon.stub(drive.client, 'request') + .resolves({ + result: { + response: { code: 0, value: buffer.toString('base64') }, + }, + }); + + const result = await drive.fetchIdentity(identityId, false); + + expect(drive.client.request).to.have.been.calledOnceWithExactly('abci_query', { + path: '/identities', + data: cbor.encode({ id: identityId }).toString('hex'), + prove: false, // cbor encoded empty object + }); + expect(result).to.be.deep.equal(buffer); + }); + }); + + describe('#fetchIdentitiesByPublicKeyHashes', () => { + it('Should call \'fetchIdentitiesByPublicKeyHashes\' RPC with the given parameters', async () => { + const drive = new DriveClient({ host: '127.0.0.1', port: 3000 }); + + const identity = getIdentityFixture(); + const proof = Buffer.from('proof'); + + const buffer = cbor.encode({ data: [identity], proof }); + const publicKeyHashes = [Buffer.alloc(1)]; + + sinon.stub(drive.client, 'request') + .resolves({ + result: { + response: { code: 0, value: buffer }, + }, + }); + + const result = await drive.fetchIdentitiesByPublicKeyHashes(publicKeyHashes, true); + + expect(drive.client.request).to.have.been.calledOnceWithExactly('abci_query', { + path: '/identities/by-public-key-hash', + data: cbor.encode({ publicKeyHashes }).toString('hex'), + prove: true, + }); + expect(result).to.be.deep.equal(buffer); + }); + }); + + describe('#fetchIdentityIdsByPublicKeyHashes', () => { + it('Should call \'fetchIdentityIdsByPublicKeyHashes\' RPC with the given parameters', async () => { + const drive = new DriveClient({ host: '127.0.0.1', port: 3000 }); + + const identityId = generateRandomIdentifier(); + const publicKeyHashes = [Buffer.alloc(1)]; + const proof = Buffer.from('proof'); + const buffer = cbor.encode({ data: [identityId], proof }); + + sinon.stub(drive.client, 'request') + .resolves({ + result: { + response: { code: 0, value: buffer }, + }, + }); + + const result = await drive.fetchIdentityIdsByPublicKeyHashes(publicKeyHashes, true); + + expect(drive.client.request).to.have.been.calledOnceWithExactly('abci_query', { + path: '/identities/by-public-key-hash/id', + data: cbor.encode({ publicKeyHashes }).toString('hex'), + prove: true, + }); + expect(result).to.be.deep.equal(buffer); + }); + }); + + describe('#fetchProofs', () => { + it('should call \'fetchProofs\' RPC with the given parameters', async () => { + const drive = new DriveClient({ host: '127.0.0.1', port: 3000 }); + + const documents = undefined; + const identityIds = [Buffer.from('id')]; + const dataContractIds = [Buffer.from('anotherId')]; + + const proof = Buffer.from('proof'); + const buffer = cbor.encode({ data: proof }); + + sinon.stub(drive.client, 'request') + .resolves({ + result: { + response: { code: 0, value: buffer }, + }, + }); + + const result = await drive.fetchProofs({ documents, identityIds, dataContractIds }); + + expect(drive.client.request).to.have.been.calledOnceWithExactly('abci_query', { + path: '/proofs', + data: cbor.encode({ + documents, + identityIds, + dataContractIds, + }).toString('hex'), + prove: false, + }); + + expect(result).to.be.deep.equal({ + data: proof, + }); + }); + }); +}); diff --git a/packages/dapi/test/unit/externalApis/tenderdash/getConsensusParamsFactory.js b/packages/dapi/test/unit/externalApis/tenderdash/getConsensusParamsFactory.js new file mode 100644 index 00000000000..49a504a5a24 --- /dev/null +++ b/packages/dapi/test/unit/externalApis/tenderdash/getConsensusParamsFactory.js @@ -0,0 +1,76 @@ +const getConsensusParamsFactory = require('../../../../lib/externalApis/tenderdash/getConsensusParamsFactory'); +const RPCError = require('../../../../lib/rpcServer/RPCError'); + +describe('getConsensusParamsFactory', () => { + let getConsensusParams; + let rpcClientMock; + let response; + + beforeEach(function beforeEach() { + response = { + id: '', + jsonrpc: '2.0', + error: '', + result: { + consensus_params: { + block: { + max_bytes: '22020096', + max_gas: '1000', + time_iota_ms: '1000', + }, + evidence: { + max_age_num_blocks: '100000', + max_age_duration: '200000', + max_bytes: '22020096', + }, + validator: { + pub_key_types: [ + 'ed25519', + ], + }, + }, + }, + }; + + rpcClientMock = { + request: this.sinon.stub().resolves(response), + }; + + getConsensusParams = getConsensusParamsFactory(rpcClientMock); + }); + + it('should return valid result', async () => { + const result = await getConsensusParams(42); + + expect(result).to.deep.equal({ + block: response.result.consensus_params.block, + evidence: response.result.consensus_params.evidence, + }); + + expect(rpcClientMock.request).to.be.calledOnceWith('consensus_params', { height: '42' }); + }); + + it('should throw RPCError', async () => { + rpcClientMock.request.resolves({ + id: '', + jsonrpc: '2.0', + result: {}, + error: { + code: -32601, + message: 'internal error', + data: 'additional data', + }, + }); + + try { + await getConsensusParams(); + + expect.fail('should throw RPCError'); + } catch (e) { + expect(e).to.be.an.instanceOf(RPCError); + expect(e.code).to.equal(-32601); + expect(e.message).to.equal('internal error'); + expect(e.data).to.equal('additional data'); + } + }); +}); diff --git a/packages/dapi/test/unit/externalApis/tenderdash/waitForHeightFactory.spec.js b/packages/dapi/test/unit/externalApis/tenderdash/waitForHeightFactory.spec.js new file mode 100644 index 00000000000..b95ffe04db1 --- /dev/null +++ b/packages/dapi/test/unit/externalApis/tenderdash/waitForHeightFactory.spec.js @@ -0,0 +1,47 @@ +const EventEmitter = require('events'); + +const waitForHeightFactory = require('../../../../lib/externalApis/tenderdash/waitForHeightFactory'); +const BlockchainListener = require('../../../../lib/externalApis/tenderdash/BlockchainListener'); + +describe('waitForHeightFactory', () => { + let blockchainListenerMock; + let waitForHeight; + let blockMessageMock; + + beforeEach(() => { + blockchainListenerMock = new EventEmitter(); + + blockMessageMock = { + data: { + value: { + block: { + header: { + height: '123', + }, + data: { + txs: [], + }, + }, + }, + }, + }; + + waitForHeight = waitForHeightFactory( + blockchainListenerMock, + ); + }); + + it('should resolve promise when the current block height is getting equal to specified height', () => { + const promise = waitForHeight(123); + + blockchainListenerMock.emit(BlockchainListener.EVENTS.NEW_BLOCK, blockMessageMock); + + expect(promise).to.be.fulfilled(); + }); + + it('should resolve promise if specified height is equal or higher than the current block height', () => { + const promise = waitForHeight(120); + + expect(promise).to.be.fulfilled(); + }); +}); diff --git a/packages/dapi/test/unit/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionResult.spec.js b/packages/dapi/test/unit/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionResult.spec.js new file mode 100644 index 00000000000..a47bbf20d56 --- /dev/null +++ b/packages/dapi/test/unit/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionResult.spec.js @@ -0,0 +1,80 @@ +const EventEmitter = require('events'); + +const waitForTransactionResult = require('../../../../../lib/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionResult'); + +const BlockchainListener = require('../../../../../lib/externalApis/tenderdash/BlockchainListener'); + +const TransactionOkResult = require('../../../../../lib/externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/TransactionOkResult'); +const TransactionErrorResult = require('../../../../../lib/externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/TransactionErrorResult'); + +describe('waitForTransactionResult', () => { + let blockchainListenerMock; + let hashString; + let topic; + let tx; + let height; + + beforeEach(function beforeEach() { + blockchainListenerMock = new EventEmitter(); + + this.sinon.spy(blockchainListenerMock); + + hashString = 'abc'; + + topic = BlockchainListener.getTransactionEventName(hashString); + + tx = 'aGVsbG8h'; + + height = 100; + }); + + it('should resolve TransactionOkResult when transaction result is emitted', async () => { + const { promise } = waitForTransactionResult(blockchainListenerMock, hashString); + + const result = { + code: 0, + }; + + const data = { data: { value: { TxResult: { result, tx, height } } } }; + + blockchainListenerMock.emit(topic, data); + + const transactionResult = await promise; + + expect(transactionResult).to.be.instanceOf(TransactionOkResult); + expect(transactionResult.getResult()).to.equal(result); + expect(transactionResult.getHeight()).to.equal(100); + expect(transactionResult.getTransaction()).to.deep.equal(Buffer.from(tx, 'base64')); + + expect(blockchainListenerMock.off).to.be.calledOnceWith(topic); + }); + + it('should resolve TransactionErrorResult when transaction result is emitted', async () => { + const { promise } = waitForTransactionResult(blockchainListenerMock, hashString); + + const result = { + code: 1, + }; + + const data = { data: { value: { TxResult: { result, tx, height } } } }; + + blockchainListenerMock.emit(topic, data); + + const transactionResult = await promise; + + expect(transactionResult).to.be.instanceOf(TransactionErrorResult); + expect(transactionResult.getResult()).to.equal(result); + expect(transactionResult.getHeight()).to.equal(100); + expect(transactionResult.getTransaction()).to.deep.equal(Buffer.from(tx, 'base64')); + + expect(blockchainListenerMock.off).to.be.calledOnceWith(topic); + }); + + it('should remove listeners on detach', () => { + const { detach } = waitForTransactionResult(blockchainListenerMock, hashString); + + detach(); + + expect(blockchainListenerMock.off).to.be.calledOnceWith(topic); + }); +}); diff --git a/packages/dapi/test/unit/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionToBeProvableFactory.spec.js b/packages/dapi/test/unit/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionToBeProvableFactory.spec.js new file mode 100644 index 00000000000..5fbf5a3fc6e --- /dev/null +++ b/packages/dapi/test/unit/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionToBeProvableFactory.spec.js @@ -0,0 +1,203 @@ +const waitForTransactionToBeProvableFactory = require('../../../../../lib/externalApis/tenderdash/waitForTransactionToBeProvable/waitForTransactionToBeProvableFactory'); + +const TransactionOkResult = require('../../../../../lib/externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/TransactionOkResult'); +const TransactionErrorResult = require('../../../../../lib/externalApis/tenderdash/waitForTransactionToBeProvable/transactionResult/TransactionErrorResult'); +const TransactionWaitPeriodExceededError = require('../../../../../lib/errors/TransactionWaitPeriodExceededError'); + +describe('waitForTransactionToBeProvableFactory', () => { + let waitForTransactionToBeProvable; + let waitForTransactionResultMock; + let waitForTransactionResultResponse; + let getExistingTransactionResultMock; + let blockchainListenerMock; + let waitForHeightMock; + let hashString; + let timeout; + let height; + let okResult; + let errorResult; + let transactionNotFoundError; + + beforeEach(function beforeEach() { + blockchainListenerMock = { }; + hashString = 'abc'; + timeout = 60000; + height = 100; + + getExistingTransactionResultMock = this.sinon.stub(); + + waitForTransactionResultResponse = { + promise: null, + detach: this.sinon.stub(), + }; + + waitForTransactionResultMock = this.sinon.stub().returns( + waitForTransactionResultResponse, + ); + + waitForHeightMock = this.sinon.stub().resolves(); + + waitForTransactionToBeProvable = waitForTransactionToBeProvableFactory( + waitForTransactionResultMock, + getExistingTransactionResultMock, + waitForHeightMock, + ); + + okResult = new TransactionOkResult({}, height, Buffer.alloc(0)); + errorResult = new TransactionErrorResult({}, height, Buffer.alloc(0)); + + transactionNotFoundError = new Error(); + + transactionNotFoundError.code = -32603; + transactionNotFoundError.data = `tx (${hashString}) not found, err: %!w()`; + }); + + it('should return existing transaction ok result when next block arrived', async () => { + getExistingTransactionResultMock.resolves(okResult); + + waitForTransactionResultResponse.promise = new Promise(() => {}); + + waitForHeightMock.promise = Promise.resolve(); + + const actualResult = await waitForTransactionToBeProvable( + blockchainListenerMock, + hashString, + timeout, + ); + + expect(actualResult).to.equal(okResult); + + expect(getExistingTransactionResultMock).to.be.calledOnceWithExactly( + hashString, + ); + + expect(waitForTransactionResultMock).to.be.calledOnceWithExactly( + blockchainListenerMock, + hashString, + ); + + expect(waitForHeightMock).to.be.calledOnceWithExactly( + height + 1, + ); + + expect(waitForTransactionResultResponse.detach).to.be.called(); + }); + + it('should return existing transaction error result and do not wait for next block', async () => { + getExistingTransactionResultMock.resolves(errorResult); + + waitForTransactionResultResponse.promise = new Promise(() => {}); + + const actualResult = await waitForTransactionToBeProvable( + blockchainListenerMock, + hashString, + timeout, + ); + + expect(actualResult).to.equal(errorResult); + + expect(getExistingTransactionResultMock).to.be.calledOnceWithExactly( + hashString, + ); + + expect(waitForTransactionResultMock).to.be.calledOnceWithExactly( + blockchainListenerMock, + hashString, + ); + + expect(waitForHeightMock).to.not.be.called(); + + expect(waitForTransactionResultResponse.detach).to.be.called(); + }); + + it('should return upcoming transaction ok result when next block arrived', async () => { + getExistingTransactionResultMock.rejects(transactionNotFoundError); + + waitForTransactionResultResponse.promise = Promise.resolve(okResult); + + const actualResult = await waitForTransactionToBeProvable( + blockchainListenerMock, + hashString, + timeout, + ); + + expect(actualResult).to.equal(okResult); + + expect(getExistingTransactionResultMock).to.be.calledOnceWithExactly( + hashString, + ); + + expect(waitForTransactionResultMock).to.be.calledOnceWithExactly( + blockchainListenerMock, + hashString, + ); + + expect(waitForHeightMock).to.be.calledOnceWithExactly( + height + 1, + ); + + expect(waitForTransactionResultResponse.detach).to.not.be.called(); + }); + + it('should return upcoming transaction error result and do not wait for next block', async () => { + getExistingTransactionResultMock.rejects(transactionNotFoundError); + + waitForTransactionResultResponse.promise = Promise.resolve(errorResult); + + const actualResult = await waitForTransactionToBeProvable( + blockchainListenerMock, + hashString, + timeout, + ); + + expect(actualResult).to.equal(errorResult); + + expect(getExistingTransactionResultMock).to.be.calledOnceWithExactly( + hashString, + ); + + expect(waitForTransactionResultMock).to.be.calledOnceWithExactly( + blockchainListenerMock, + hashString, + ); + + expect(waitForHeightMock).to.not.be.called(); + + expect(waitForTransactionResultResponse.detach).to.not.be.called(); + }); + + it('should throw TransactionWaitPeriodExceededError on timeout', async () => { + timeout = 5; + + getExistingTransactionResultMock.rejects(transactionNotFoundError); + + waitForTransactionResultResponse.promise = new Promise(() => {}); + + try { + await waitForTransactionToBeProvable( + blockchainListenerMock, + hashString, + timeout, + ); + + expect.fail('should throw TransactionWaitPeriodExceededError'); + } catch (e) { + expect(e).to.be.instanceOf(TransactionWaitPeriodExceededError); + + expect(e.getTransactionHash()).to.equal(hashString); + + expect(getExistingTransactionResultMock).to.be.calledOnceWithExactly( + hashString, + ); + + expect(waitForTransactionResultMock).to.be.calledOnceWithExactly( + blockchainListenerMock, + hashString, + ); + + expect(waitForHeightMock).to.not.be.called(); + + expect(waitForTransactionResultResponse.detach).to.be.calledOnce(); + } + }); +}); diff --git a/packages/dapi/test/unit/grpcServer/handlers/blockheaders-stream/subscribeToBlockHeadersWithChainLocksHandlerFactory.spec.js b/packages/dapi/test/unit/grpcServer/handlers/blockheaders-stream/subscribeToBlockHeadersWithChainLocksHandlerFactory.spec.js new file mode 100644 index 00000000000..c4fed92c388 --- /dev/null +++ b/packages/dapi/test/unit/grpcServer/handlers/blockheaders-stream/subscribeToBlockHeadersWithChainLocksHandlerFactory.spec.js @@ -0,0 +1,225 @@ +const { ChainLock } = require('@dashevo/dashcore-lib'); + +const { + server: { + error: { + NotFoundGrpcError, + InvalidArgumentGrpcError, + }, + stream: { + AcknowledgingWritable, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + BlockHeadersWithChainLocksRequest, + BlockHeadersWithChainLocksResponse, + BlockHeaders, + }, +} = require('@dashevo/dapi-grpc'); + +const GrpcCallMock = require('../../../../../lib/test/mock/GrpcCallMock'); +const subscribeToBlockHeadersWithChainLocksHandlerFactory = require( + '../../../../../lib/grpcServer/handlers/blockheaders-stream/subscribeToBlockHeadersWithChainLocksHandlerFactory', +); +const ChainDataProvider = require('../../../../../lib/chainDataProvider/ChainDataProvider'); + +let coreAPIMock; +let zmqClientMock; + +describe('subscribeToBlockHeadersWithChainLocksHandlerFactory', () => { + let call; + let subscribeToBlockHeadersWithChainLocksHandler; + let getHistoricalBlockHeadersIteratorMock; + let subscribeToNewBlockHeadersMock; + let chainDataProvider; + + const blockHash = Buffer.from('00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc2c', 'hex'); + + beforeEach(function beforeEach() { + coreAPIMock = { + getBlock: this.sinon.stub(), + getBlockStats: this.sinon.stub(), + getBlockHeaders: this.sinon.stub(), + getBestBlockHeight: this.sinon.stub(), + getBlockHash: this.sinon.stub(), + getBestChainLock: this.sinon.stub(), + }; + subscribeToNewBlockHeadersMock = this.sinon.stub(); + + async function* asyncGenerator() { + yield [{ toBuffer: () => Buffer.from('fake', 'utf-8') }]; + } + + getHistoricalBlockHeadersIteratorMock = () => asyncGenerator(); + zmqClientMock = { on: this.sinon.stub(), topics: { hashblock: 'fake' } }; + + chainDataProvider = new ChainDataProvider(coreAPIMock, zmqClientMock); + + // eslint-disable-next-line operator-linebreak + subscribeToBlockHeadersWithChainLocksHandler = + subscribeToBlockHeadersWithChainLocksHandlerFactory( + getHistoricalBlockHeadersIteratorMock, + coreAPIMock, + chainDataProvider, + zmqClientMock, + subscribeToNewBlockHeadersMock, + ); + }); + + it('should subscribe to newBlockHeaders', async function it() { + this.sinon.stub(AcknowledgingWritable.prototype, 'write'); + + let request = new BlockHeadersWithChainLocksRequest(); + + request.setFromBlockHash(blockHash); + request.setCount(0); + + request = BlockHeadersWithChainLocksRequest.deserializeBinary(request.serializeBinary()); + + call = new GrpcCallMock(this.sinon, request); + + coreAPIMock.getBestChainLock.resolves({ + height: 1, + signature: Buffer.from('fakeSig'), + blockHash, + }); + coreAPIMock.getBlockStats.resolves({ height: 1 }); + + await subscribeToBlockHeadersWithChainLocksHandler(call); + expect(subscribeToNewBlockHeadersMock).to.have.been.called(); + expect(coreAPIMock.getBlockStats).to.be.calledOnceWithExactly(blockHash + .toString('hex'), ['height']); + }); + + it('should subscribe from block hash', async function it() { + const writableStub = this.sinon.stub(AcknowledgingWritable.prototype, 'write'); + let request = new BlockHeadersWithChainLocksRequest(); + + request.setFromBlockHash(blockHash); + request.setCount(0); + + request = BlockHeadersWithChainLocksRequest.deserializeBinary(request.serializeBinary()); + + call = new GrpcCallMock(this.sinon, request); + + // monkey-patching + chainDataProvider.chainLock = new ChainLock({ + height: 1, + signature: Buffer.from('fakesig', 'hex'), + blockHash: Buffer.from('fakeHash', 'hex'), + }); + + coreAPIMock.getBlockStats.resolves({ height: 1 }); + + await subscribeToBlockHeadersWithChainLocksHandler(call); + + expect(coreAPIMock.getBlockStats).to.be.calledOnceWithExactly( + blockHash.toString('hex'), + ['height'], + ); + + const clSigResponse = new BlockHeadersWithChainLocksResponse(); + clSigResponse.setChainLock(new ChainLock({ + height: 1, + signature: Buffer.from('fakesig', 'hex'), + blockHash: Buffer.from('fakeHash', 'hex'), + }).toBuffer()); + + expect(writableStub.getCall(0).args).to.deep.equal( + [clSigResponse], + ); + + const blockHeadersProto = new BlockHeaders(); + blockHeadersProto.setHeadersList( + [Buffer.from('fake', 'utf-8')], + ); + const iteratorResponse = new BlockHeadersWithChainLocksResponse(); + iteratorResponse.setBlockHeaders(blockHeadersProto); + + expect(writableStub.getCall(1).args).to.deep.equal( + [iteratorResponse], + ); + }); + + it('should subscribe from block height', async function it() { + this.sinon.stub(AcknowledgingWritable.prototype, 'write'); + + const blockHeight = 1; + const count = 5; + + let request = new BlockHeadersWithChainLocksRequest(); + request.setFromBlockHeight(blockHeight); + request.setCount(count); + + request = BlockHeadersWithChainLocksRequest.deserializeBinary(request.serializeBinary()); + + call = new GrpcCallMock(this.sinon, request); + + coreAPIMock.getBestChainLock.resolves({ + height: 1, + signature: Buffer.from('fakeSig'), + blockHash: Buffer.from('fakeHash'), + }); + + coreAPIMock.getBlockStats.resolves({ height: 1 }); + + await subscribeToBlockHeadersWithChainLocksHandler(call); + + expect(coreAPIMock.getBlockStats).to.be.calledOnceWithExactly( + blockHeight, + ['height'], + ); + + expect(subscribeToNewBlockHeadersMock).to.not.have.been.called(); + }); + + it('should handle getBlockStats RPC method errors', async function it() { + let request = new BlockHeadersWithChainLocksRequest(); + + request.setFromBlockHash(blockHash); + request.setCount(0); + + request = BlockHeadersWithChainLocksRequest.deserializeBinary(request.serializeBinary()); + + call = new GrpcCallMock(this.sinon, request); + + try { + coreAPIMock.getBlockStats.throws({ code: -5 }); + + await subscribeToBlockHeadersWithChainLocksHandler(call); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(NotFoundGrpcError); + expect(e.message).to.be.equal(`Block ${blockHash.toString('hex')} not found`); + } + + try { + coreAPIMock.getBlockStats.throws({ code: -8 }); + + await subscribeToBlockHeadersWithChainLocksHandler(call); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(NotFoundGrpcError); + expect(e.message).to.be.equal(`Block ${blockHash.toString('hex')} not found`); + } + + try { + request.setCount(10); + + coreAPIMock.getBlockStats.resolves({ height: 10 }); + + coreAPIMock.getBestBlockHeight.resolves(11); + + await subscribeToBlockHeadersWithChainLocksHandler(call); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + } + }); +}); diff --git a/packages/dapi/test/unit/grpcServer/handlers/core/broadcastTransactionHandlerFactory.js b/packages/dapi/test/unit/grpcServer/handlers/core/broadcastTransactionHandlerFactory.js new file mode 100644 index 00000000000..f0569bb5405 --- /dev/null +++ b/packages/dapi/test/unit/grpcServer/handlers/core/broadcastTransactionHandlerFactory.js @@ -0,0 +1,119 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + AlreadyExistsGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { Transaction } = require('@dashevo/dashcore-lib'); + +const { + v0: { + BroadcastTransactionResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const broadcastTransactionHandlerFactory = require('../../../../../lib/grpcServer/handlers/core/broadcastTransactionHandlerFactory'); + +const GrpcCallMock = require('../../../../../lib/test/mock/GrpcCallMock'); + +describe('broadcastTransactionHandlerFactory', () => { + let call; + let coreRPCClientMock; + let request; + let serializedTransaction; + let transactionId; + let broadcastTransactionHandler; + + beforeEach(function beforeEach() { + const rawTransaction = '0300000001086a3640a4a88a85d5720ecb69a93d0aef1cfa759d1242835e4abaf4168b924d000000006b483045022100ed608a9742913c94e057798297a6a96ed40c41dc61209e6887df51ea5755234802207f5733ef592f3df59bc6d39749ac5f1a771b32263ce16982b1aacc80ff1358cd012103323aa9dd83ba005b1b1e61b36cba27c2e0f64bacb57c34243fc7ef2751fff6edffffffff021027000000000000166a1481b21f3898087a0d1905140c7db8d7db00acd13954a09a3b000000001976a91481b21f3898087a0d1905140c7db8d7db00acd13988ac00000000'; + + serializedTransaction = new Transaction(rawTransaction).toBuffer(); + + transactionId = 'id'; + + request = { + getTransaction: this.sinon.stub().returns(serializedTransaction), + }; + + call = new GrpcCallMock(this.sinon, request); + + coreRPCClientMock = { + sendRawTransaction: this.sinon.stub().resolves(transactionId), + }; + + broadcastTransactionHandler = broadcastTransactionHandlerFactory(coreRPCClientMock); + }); + + it('should return valid result', async () => { + const result = await broadcastTransactionHandler(call); + + expect(result).to.be.an.instanceOf(BroadcastTransactionResponse); + expect(result.getTransactionId()).to.equal(transactionId); + expect(coreRPCClientMock.sendRawTransaction).to.be.calledOnceWith(serializedTransaction.toString('hex')); + }); + + it('should throw InvalidArgumentGrpcError error if transaction is not specified', async () => { + serializedTransaction = null; + request.getTransaction.returns(serializedTransaction); + + try { + await broadcastTransactionHandler(call); + + expect.fail('should thrown InvalidArgumentGrpcError error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('transaction is not specified'); + expect(coreRPCClientMock.sendRawTransaction).to.be.not.called(); + } + }); + + it('should throw InvalidArgumentGrpcError error if transaction is not valid', async () => { + serializedTransaction = '03000000011846a52a9e766cbb0a6153bb78af9858cc71070aea44cd8282ba8e5c5de7331b000000006a4730440220267c9903049b8962f67ed7809e0f5cf32324d999b7a85c9d883298600bb880ab022048478303e2281e26cefa496e7b3aeac0c5cccbe6e0429adf8532438f996c4a0c012103fe92ef7d837791caaf44be835a1782b4b1f0865c4c6ae73a4a92b14c8a37cc78ffffffff0000000000'; + request.getTransaction.returns(serializedTransaction); + + try { + await broadcastTransactionHandler(call); + + expect.fail('should thrown InvalidArgumentGrpcError error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.be.a('string').and.satisfy((msg) => msg.startsWith('invalid transaction:')); + expect(coreRPCClientMock.sendRawTransaction).to.be.not.called(); + } + }); + + it('should throw InvalidArgumentGrpcError error if transaction cannot be decoded', async () => { + serializedTransaction = new Uint8Array(Buffer.from('invalid data')); + request.getTransaction.returns(serializedTransaction); + + try { + await broadcastTransactionHandler(call); + + expect.fail('should thrown InvalidArgumentGrpcError error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.be.a('string').and.satisfy((msg) => msg.startsWith('invalid transaction:')); + expect(coreRPCClientMock.sendRawTransaction).to.be.not.called(); + } + }); + + it('should throw AlreadyExistsGrpcError error if transaction already in chain', async () => { + const error = new Error(); + error.code = -27; + error.message = 'dup-tx-something'; + + coreRPCClientMock.sendRawTransaction.throws(error); + + try { + await broadcastTransactionHandler(call); + + expect.fail('should thrown AlreadyExistsGrpcError error'); + } catch (e) { + expect(e).to.be.instanceOf(AlreadyExistsGrpcError); + expect(e.getMessage()).to.equal(`Transaction already in chain: ${error.message}`); + } + }); +}); diff --git a/packages/dapi/test/unit/grpcServer/handlers/core/getBlockHandlerFactory.js b/packages/dapi/test/unit/grpcServer/handlers/core/getBlockHandlerFactory.js new file mode 100644 index 00000000000..693f2c118ae --- /dev/null +++ b/packages/dapi/test/unit/grpcServer/handlers/core/getBlockHandlerFactory.js @@ -0,0 +1,184 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + NotFoundGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + GetBlockResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const { Block } = require('@dashevo/dashcore-lib'); + +const getBlockHandlerFactory = require('../../../../../lib/grpcServer/handlers/core/getBlockHandlerFactory'); + +const GrpcCallMock = require('../../../../../lib/test/mock/GrpcCallMock'); + +describe('getBlockHandlerFactory', () => { + let call; + let hash; + let height; + let getBlockHandler; + let coreRPCClientMock; + let request; + let block; + + beforeEach(function beforeEach() { + hash = ''; + height = 0; + + const serializedBlock = '02000000b67a40f3cd5804437a108f105533739c37e6229bc1adcab385140b59fd0f0000a71c1aade44bf8425bec0deb611c20b16da3442818ef20489ca1e2512be43eef814cdb52f0ff0f1edbf701000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0a510101062f503253482fffffffff0100743ba40b000000232103a69850243c993c0645a6e8b38c774174174cc766cd3ec2140afd24d831b84c41ac00000000'; + + block = new Block(Buffer.from(serializedBlock, 'hex')); + request = { + getHeight: this.sinon.stub().returns(height), + getHash: this.sinon.stub().returns(hash), + }; + + call = new GrpcCallMock(this.sinon, request); + + coreRPCClientMock = { + getRawBlock: this.sinon.stub().resolves(serializedBlock), + getBlockHash: this.sinon.stub().resolves(hash), + }; + + getBlockHandler = getBlockHandlerFactory(coreRPCClientMock); + }); + + it('should return valid result is hash is specified', async () => { + hash = 'hash'; + request.getHash.returns(hash); + + const result = await getBlockHandler(call); + + expect(result).to.be.an.instanceOf(GetBlockResponse); + + expect(coreRPCClientMock.getRawBlock).to.be.calledOnceWith(hash); + expect(coreRPCClientMock.getBlockHash).to.be.not.called(); + + const blockBinary = result.getBlock(); + + expect(blockBinary).to.be.an.instanceOf(Buffer); + + const returnedBlock = new Block(blockBinary); + + expect(returnedBlock.toJSON()).to.deep.equal(block.toJSON()); + }); + + it('should return valid result is height is specified', async () => { + height = 42; + request.getHeight.returns(height); + + const result = await getBlockHandler(call); + + expect(result).to.be.an.instanceOf(GetBlockResponse); + + expect(coreRPCClientMock.getRawBlock).to.be.called(); + expect(coreRPCClientMock.getBlockHash).to.be.calledOnceWith(height); + + const blockBinary = result.getBlock(); + + expect(blockBinary).to.be.an.instanceOf(Buffer); + + const returnedBlock = new Block(blockBinary); + + expect(returnedBlock.toJSON()).to.deep.equal(block.toJSON()); + }); + + it('should throw an InvalidArgumentGrpcError if hash and height are not specified', async () => { + try { + await getBlockHandler(call); + + expect.fail('should thrown InvalidArgumentGrpcError error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('hash or height is not specified'); + expect(coreRPCClientMock.getRawBlock).to.be.not.called(); + expect(coreRPCClientMock.getBlockHash).to.be.not.called(); + } + }); + it('should throw an InvalidArgumentGrpcError if getRawBlock throws error with code -1', async () => { + const error = new Error('JSON value is not an integer as expected'); + error.code = -1; + + coreRPCClientMock.getBlockHash.throws(error); + + height = 'abc'; + request.getHeight.returns(height); + + try { + await getBlockHandler(call); + + expect.fail('should thrown InvalidArgumentGrpcError error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('JSON value is not an integer as expected'); + expect(coreRPCClientMock.getBlockHash).to.be.calledOnceWith(height); + } + }); + + it('should throw an NotFoundGrpcError if block with specified height is not found', async () => { + const error = new Error('Block height out of range'); + error.code = -8; + + coreRPCClientMock.getBlockHash.throws(error); + + height = 111111111; + request.getHeight.returns(height); + + try { + await getBlockHandler(call); + + expect.fail('should thrown NotFoundGrpcError error'); + } catch (e) { + expect(e).to.be.instanceOf(NotFoundGrpcError); + expect(e.getMessage()).to.equal('Invalid block height'); + expect(coreRPCClientMock.getBlockHash).to.be.calledOnceWith(height); + } + }); + + it('should throw an InvalidArgumentGrpcError if getRawBlock throws error with code -5', async () => { + const error = new Error(); + error.code = -5; + + coreRPCClientMock.getRawBlock.throws(error); + + hash = 'hash'; + request.getHash.returns(hash); + + try { + await getBlockHandler(call); + + expect.fail('should thrown InvalidArgumentGrpcError error'); + } catch (e) { + expect(e).to.be.instanceOf(NotFoundGrpcError); + expect(e.getMessage()).to.equal('Block not found'); + expect(coreRPCClientMock.getBlockHash).to.be.not.called(); + expect(coreRPCClientMock.getRawBlock).to.be.calledOnceWith(hash); + } + }); + + it('should throw an InternalGrpcError if getRawBlockByHash throws unknown error', async () => { + const error = new Error('Unknown error'); + + coreRPCClientMock.getRawBlock.throws(error); + + hash = 'hash'; + request.getHash.returns(hash); + + try { + await getBlockHandler(call); + + expect.fail('should thrown InvalidArgumentGrpcError error'); + } catch (e) { + expect(e).to.deep.equal(error); + expect(coreRPCClientMock.getBlockHash).to.be.not.called(); + expect(coreRPCClientMock.getRawBlock).to.be.calledOnceWith(hash); + } + }); +}); diff --git a/packages/dapi/test/unit/grpcServer/handlers/core/getStatusHandlerFactory.js b/packages/dapi/test/unit/grpcServer/handlers/core/getStatusHandlerFactory.js new file mode 100644 index 00000000000..1a1558a444c --- /dev/null +++ b/packages/dapi/test/unit/grpcServer/handlers/core/getStatusHandlerFactory.js @@ -0,0 +1,150 @@ +const { + v0: { + GetStatusResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const getStatusHandlerFactory = require('../../../../../lib/grpcServer/handlers/core/getStatusHandlerFactory'); + +const GrpcCallMock = require('../../../../../lib/test/mock/GrpcCallMock'); + +describe('getStatusHandlerFactory', () => { + let call; + let getStatusHandler; + let coreRPCClientMock; + let now; + + let blockchainInfo; + let networkInfo; + let mnSyncInfo; + let masternodeStatus; + + beforeEach(function beforeEach() { + mnSyncInfo = { + AssetID: 999, + AssetName: 'MASTERNODE_SYNC_FINISHED', + AssetStartTime: 1615466139, + Attempt: 0, + IsBlockchainSynced: true, + IsSynced: true, + }; + + blockchainInfo = { + chain: 'test', + blocks: 460991, + headers: 460991, + bestblockhash: '0000007464fd8cae97830d794bf03efbeaa4b8c3258a3def67a89cdbd060f827', + difficulty: 0.002261509525429119, + mediantime: 1615546573, + verificationprogress: 0.9999993798366165, + initialblockdownload: false, + chainwork: '000000000000000000000000000000000000000000000000022f149b98e063dc', + warnings: 'Warning: unknown new rules activated (versionbit 3)', + }; + + networkInfo = { + version: 170000, + subversion: '/Dash Core:0.17.0/', + protocolversion: 70218, + localservices: '0000000000000405', + localrelay: true, + timeoffset: 0, + networkactive: true, + connections: 8, + socketevents: 'select', + relayfee: 0.00001, + incrementalfee: 0.00001, + warnings: 'Warning: unknown new rules activated (versionbit 3)', + }; + + masternodeStatus = { + outpoint: 'd1be3a1aa0b9516d06ed180607c168724c21d8ccf6c5a3f5983769830724c357-0', + service: '45.32.237.76:19999', + proTxHash: '04d06d16b3eca2f104ef9749d0c1c17d183eb1b4fe3a16808fd70464f03bcd63', + collateralHash: 'd1be3a1aa0b9516d06ed180607c168724c21d8ccf6c5a3f5983769830724c357', + collateralIndex: 0, + dmnState: { + service: '45.32.237.76:19999', + registeredHeight: 7402, + lastPaidHeight: 59721, + PoSePenalty: 0, + PoSeRevivedHeight: 61915, + PoSeBanHeight: -1, + revocationReason: 0, + ownerAddress: 'yT8DDY5NkX4ZtBkUVz7y1RgzbakCnMPogh', + votingAddress: 'yMLrhooXyJtpV3R2ncsxvkrh6wRennNPoG', + payoutAddress: 'yTsGq4wV8WF5GKLaYV2C43zrkr2sfTtysT', + pubKeyOperator: '02a2e2673109a5e204f8a82baf628bb5f09a8dfc671859e84d2661cae03e6c6e198a037e968253e94cd099d07b98e94e', + }, + state: 'READY', + status: 'Ready', + }; + + call = new GrpcCallMock(this.sinon); + + coreRPCClientMock = { + getBlockchainInfo: this.sinon.stub().resolves(blockchainInfo), + getNetworkInfo: this.sinon.stub().resolves(networkInfo), + getMnSync: this.sinon.stub().resolves(mnSyncInfo), + getMasternode: this.sinon.stub().resolves(masternodeStatus), + }; + + now = new Date(); + this.sinon.useFakeTimers(now.getTime()); + + getStatusHandler = getStatusHandlerFactory(coreRPCClientMock); + }); + + it('should return valid result', async () => { + const result = await getStatusHandler(call); + + expect(result).to.be.an.instanceOf(GetStatusResponse); + + // Validate protobuf object values + result.serializeBinary(); + + const version = result.getVersion(); + expect(version.getProtocol()).to.equal(networkInfo.protocolversion); + expect(version.getSoftware()).to.equal(networkInfo.version); + expect(version.getAgent()).to.equal(networkInfo.subversion); + + const time = result.getTime(); + expect(time.getNow()).to.be.an('number'); + expect(time.getNow()).to.equal(Math.floor(now.getTime() / 1000)); + expect(time.getOffset()).to.be.equal(networkInfo.timeoffset); + expect(time.getMedian()).to.be.equal(blockchainInfo.mediantime); + + const chain = result.getChain(); + expect(chain.getName()).to.be.equal(blockchainInfo.chain); + expect(chain.getBlocksCount()).to.be.equal(blockchainInfo.blocks); + expect(chain.getHeadersCount()).to.be.equal(blockchainInfo.headers); + expect(chain.getBestBlockHash()).to.be.an.instanceOf(Buffer); + expect(chain.getBestBlockHash().toString('hex')).to.be.equal(blockchainInfo.bestblockhash); + expect(chain.getDifficulty()).to.be.equal(blockchainInfo.difficulty); + expect(chain.getChainWork()).to.be.an.instanceOf(Buffer); + expect(chain.getChainWork().toString('hex')).to.be.equal(blockchainInfo.chainwork); + expect(chain.getIsSynced()).to.be.equal(mnSyncInfo.IsBlockchainSynced); + expect(chain.getSyncProgress()).to.be.equal(blockchainInfo.verificationprogress); + + const masternode = result.getMasternode(); + expect(masternode.getStatus()).to.be.equal(GetStatusResponse.Masternode.Status.READY); + expect(masternode.getProTxHash()).to.be.an.instanceOf(Buffer); + expect(masternode.getProTxHash().toString('hex')).to.be.equal(masternodeStatus.proTxHash); + expect(masternode.getPosePenalty()).to.be.equal(masternodeStatus.dmnState.PoSePenalty); + expect(masternode.getIsSynced()).to.be.equal(mnSyncInfo.IsSynced); + expect(masternode.getSyncProgress()).to.be.equal(1); + + const network = result.getNetwork(); + expect(network.getPeersCount()).to.be.equal(networkInfo.connections); + + const fee = network.getFee(); + expect(fee.getRelay()).to.be.equal(networkInfo.relayfee); + expect(fee.getIncremental()).to.be.equal(networkInfo.incrementalfee); + + expect(result.getStatus()).to.be.equal(GetStatusResponse.Status.READY); + expect(coreRPCClientMock.getBlockchainInfo).to.be.calledOnce(); + expect(coreRPCClientMock.getNetworkInfo).to.be.calledOnce(); + expect(coreRPCClientMock.getMnSync).to.be.calledOnceWith('status'); + expect(coreRPCClientMock.getMasternode).to.be.calledOnceWith('status'); + }); +}); diff --git a/packages/dapi/test/unit/grpcServer/handlers/core/getTransactionHandlerFactory.js b/packages/dapi/test/unit/grpcServer/handlers/core/getTransactionHandlerFactory.js new file mode 100644 index 00000000000..6e3da5c9789 --- /dev/null +++ b/packages/dapi/test/unit/grpcServer/handlers/core/getTransactionHandlerFactory.js @@ -0,0 +1,104 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + NotFoundGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + GetTransactionResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const { Transaction } = require('@dashevo/dashcore-lib'); + +const getTransactionHandlerFactory = require('../../../../../lib/grpcServer/handlers/core/getTransactionHandlerFactory'); + +const GrpcCallMock = require('../../../../../lib/test/mock/GrpcCallMock'); + +describe('getTransactionHandlerFactory', () => { + let call; + let request; + let id; + let rawTransactionFixture; + let getTransactionHandler; + let coreRPCClientMock; + + beforeEach(function beforeEach() { + id = 'id'; + rawTransactionFixture = '0200000001d3145639d750ce104d740f7b2bb46381202e4798f9eb678cb361467195aa5b96000000006a4730440220156ea3d61ea7dce612a1608beed374138c8bf58aad7d76292d39d648e2b1346b022051f28f100bfa9d0dae0e2d8d76d00007f32611c8d69d49203f059ffc3c2fac58012102c9228c1cd1e778062de766376580e9dbeb301a4aa2cb0c535ada5a58f6ec5532ffffffff018dec9f00000000001976a914bf7f49e8e8c8aa0fcf1af8e53e117d25db30207288ac00000000'; + + request = { + getId: this.sinon.stub().returns(id), + }; + + call = new GrpcCallMock(this.sinon, request); + + coreRPCClientMock = { + getRawTransaction: this.sinon.stub().resolves({ + hex: rawTransactionFixture, + blockhash: Buffer.alloc(1, 32).toString('hex'), + height: 42, + confirmations: 3, + instantlock_internal: true, + chainlock: false, + }), + }; + + getTransactionHandler = getTransactionHandlerFactory(coreRPCClientMock); + }); + + it('should return valid result', async () => { + const result = await getTransactionHandler(call); + + expect(result).to.be.an.instanceOf(GetTransactionResponse); + + const transactionSerialized = result.getTransaction(); + + expect(transactionSerialized).to.be.an.instanceOf(Buffer); + + const returnedTransaction = new Transaction(transactionSerialized); + + expect(returnedTransaction.toString()).to.deep.equal(rawTransactionFixture); + expect(coreRPCClientMock.getRawTransaction).to.be.calledOnceWith(id); + expect(result.getBlockHash()).to.deep.equal(Buffer.alloc(1, 32)); + expect(result.getHeight()).to.equal(42); + expect(result.getConfirmations()).to.equal(3); + expect(result.getIsInstantLocked()).to.be.true(); + expect(result.getIsChainLocked()).to.be.false(); + }); + + it('should throw InvalidArgumentGrpcError error if id is not specified', async () => { + id = null; + request.getId.returns(id); + + try { + await getTransactionHandler(call); + + expect.fail('should thrown InvalidArgumentGrpcError error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('id is not specified'); + expect(coreRPCClientMock.getRawTransaction).to.be.not.called(); + } + }); + + it('should throw NotFoundGrpcError if transaction is not found', async () => { + const error = new Error(); + error.code = -5; + coreRPCClientMock.getRawTransaction.throws(error); + + try { + await getTransactionHandler(call); + + expect.fail('should thrown InvalidArgumentGrpcError error'); + } catch (e) { + expect(e).to.be.instanceOf(NotFoundGrpcError); + expect(e.getMessage()).to.equal('Transaction not found'); + expect(coreRPCClientMock.getRawTransaction).to.be.calledOnceWith(id); + } + }); +}); diff --git a/packages/dapi/test/unit/grpcServer/handlers/createGrpcErrorFromDriveResponse.js b/packages/dapi/test/unit/grpcServer/handlers/createGrpcErrorFromDriveResponse.js new file mode 100644 index 00000000000..4e3e4423229 --- /dev/null +++ b/packages/dapi/test/unit/grpcServer/handlers/createGrpcErrorFromDriveResponse.js @@ -0,0 +1,159 @@ +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const GrpcError = require('@dashevo/grpc-common/lib/server/error/GrpcError'); +const cbor = require('cbor'); +const InternalGrpcError = require('@dashevo/grpc-common/lib/server/error/InternalGrpcError'); +const InvalidArgumentGrpcError = require('@dashevo/grpc-common/lib/server/error/InvalidArgumentGrpcError'); +const FailedPreconditionGrpcError = require('@dashevo/grpc-common/lib/server/error/FailedPreconditionGrpcError'); +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); +const createGrpcErrorFromDriveResponse = require( + '../../../../lib/grpcServer/handlers/createGrpcErrorFromDriveResponse', +); + +describe('createGrpcErrorFromDriveResponse', () => { + let message; + let info; + let encodedInfo; + + beforeEach(() => { + message = 'message'; + info = { + message, + data: { + error: 'some data', + }, + }; + + encodedInfo = cbor.encode(info).toString('base64'); + }); + + Object.entries(GrpcErrorCodes) + // We have special tests below for these error codes + .filter(([, code]) => ( + ![GrpcErrorCodes.VERSION_MISMATCH, GrpcErrorCodes.INTERNAL].includes(code) + )) + .forEach(([codeClass, code]) => { + it(`should throw ${codeClass} if response code is ${code}`, () => { + const error = createGrpcErrorFromDriveResponse(code, encodedInfo); + + expect(error).to.be.an.instanceOf(GrpcError); + expect(error.getMessage()).to.equal(message); + expect(error.getCode()).to.equal(code); + expect(error.getRawMetadata()).to.deep.equal({ + 'drive-error-data-bin': cbor.encode(info.data), + }); + }); + }); + + it('should throw GrpcError if error code = 17', () => { + const error = createGrpcErrorFromDriveResponse(17, encodedInfo); + + expect(error).to.be.an.instanceOf(GrpcError); + expect(error.getMessage()).to.equal(message); + expect(error.getCode()).to.equal(GrpcErrorCodes.UNKNOWN); + expect(error.getRawMetadata()).to.deep.equal({ + 'drive-error-data-bin': cbor.encode(info.data), + }); + }); + + it('should throw basic consensus error if error code = 1000', () => { + const data = { }; + info = { data }; + + const error = createGrpcErrorFromDriveResponse(1000, cbor.encode(info).toString('base64')); + + expect(error).to.be.an.instanceOf(InvalidArgumentGrpcError); + expect(error.getRawMetadata()).to.deep.equal({ + code: 1000, + }); + }); + + it('should throw signature consensus error if error code = 2000', () => { + const id = generateRandomIdentifier(); + + const data = { arguments: [id] }; + info = { data }; + + const error = createGrpcErrorFromDriveResponse( + 2000, + cbor.encode(info).toString('base64'), + ); + + expect(error).to.be.an.instanceOf(GrpcError); + expect(error.getCode()).to.equal(GrpcErrorCodes.UNAUTHENTICATED); + expect(error.getRawMetadata()).to.deep.equal({ + code: 2000, + 'drive-error-data-bin': cbor.encode(data), + }); + }); + + it('should throw fee consensus error if error code = 3000', () => { + const data = { arguments: [20, 10] }; + info = { data }; + + const error = createGrpcErrorFromDriveResponse(3000, cbor.encode(info).toString('base64')); + + expect(error).to.be.an.instanceOf(FailedPreconditionGrpcError); + expect(error.getRawMetadata()).to.deep.equal({ + code: 3000, + 'drive-error-data-bin': cbor.encode(data), + }); + }); + + it('should throw state consensus error if error code = 4000', () => { + const dataContractId = generateRandomIdentifier(); + + const data = { arguments: [dataContractId] }; + info = { data }; + + const error = createGrpcErrorFromDriveResponse( + 4000, + cbor.encode(info).toString('base64'), + ); + + expect(error).to.be.an.instanceOf(InvalidArgumentGrpcError); + expect(error.getRawMetadata()).to.deep.equal({ + code: 4000, + 'drive-error-data-bin': cbor.encode(data), + }); + }); + + it('should throw Unknown error code >= 5000', () => { + const error = createGrpcErrorFromDriveResponse(5000, encodedInfo); + + expect(error).to.be.an.instanceOf(GrpcError); + expect(error.getMessage()).to.equal('Internal error'); + expect(error.getError().message).to.deep.equal('Unknown Drive’s error code: 5000'); + }); + + it('should return InternalGrpcError if codes is undefined', () => { + const error = createGrpcErrorFromDriveResponse(); + + expect(error).to.be.an.instanceOf(InternalGrpcError); + expect(error.getMessage()).to.equal('Internal error'); + expect(error.getError().message).to.deep.equal('Drive’s error code is empty'); + }); + + it('should return InternalGrpcError if code = 13', () => { + const errorInfo = { + message, + data: { + ...info.data, + stack: 'long \n long \n long \n string', + }, + }; + + const error = createGrpcErrorFromDriveResponse( + GrpcErrorCodes.INTERNAL, + cbor.encode(errorInfo).toString('base64'), + ); + + expect(error).to.be.an.instanceOf(InternalGrpcError); + expect(error.getMessage()).to.equal('Internal error'); + expect(error.getCode()).to.equal(GrpcErrorCodes.INTERNAL); + expect(error.getError().message).to.deep.equal(message); + expect(error.getError().stack).to.deep.equal(errorInfo.data.stack); + expect(error.getRawMetadata()).to.deep.equal({ + 'drive-error-data-bin': cbor.encode(info.data), + }); + }); +}); diff --git a/packages/dapi/test/unit/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js b/packages/dapi/test/unit/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js new file mode 100644 index 00000000000..103688a06a0 --- /dev/null +++ b/packages/dapi/test/unit/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js @@ -0,0 +1,176 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + AlreadyExistsGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + BroadcastStateTransitionResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const DashPlatformProtocol = require('@dashevo/dpp'); +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); + +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const NotFoundGrpcError = require('@dashevo/grpc-common/lib/server/error/NotFoundGrpcError'); +const cbor = require('cbor'); +const GrpcCallMock = require('../../../../../lib/test/mock/GrpcCallMock'); + +const broadcastStateTransitionHandlerFactory = require( + '../../../../../lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory', +); + +describe('broadcastStateTransitionHandlerFactory', () => { + let call; + let rpcClientMock; + let broadcastStateTransitionHandler; + let response; + let stateTransitionFixture; + let log; + let code; + let createGrpcErrorFromDriveResponseMock; + + beforeEach(async function beforeEach() { + const dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + const dataContractFixture = getDataContractFixture(); + stateTransitionFixture = dpp.dataContract.createDataContractCreateTransition( + dataContractFixture, + ); + + call = new GrpcCallMock(this.sinon, { + getStateTransition: this.sinon.stub().returns(stateTransitionFixture.toBuffer()), + }); + + log = JSON.stringify({ + error: { + message: 'some message', + data: { + error: 'some data', + }, + }, + }); + + code = 0; + + response = { + id: '', + jsonrpc: '2.0', + error: '', + result: { + check_tx: { code, log }, + deliver_tx: { code, log }, + hash: + 'B762539A7C17C33A65C46727BFCF2C701390E6AD7DE5190B6CC1CF843CA7E262', + height: '24', + code, + }, + }; + + rpcClientMock = { + request: this.sinon.stub().resolves(response), + }; + + createGrpcErrorFromDriveResponseMock = this.sinon.stub(); + + broadcastStateTransitionHandler = broadcastStateTransitionHandlerFactory( + rpcClientMock, + createGrpcErrorFromDriveResponseMock, + ); + }); + + afterEach(function afterEach() { + this.sinon.restore(); + }); + + it('should throw an InvalidArgumentGrpcError if stateTransition is not specified', async () => { + call.request.getStateTransition.returns(null); + + try { + await broadcastStateTransitionHandler(call); + + expect.fail('InvalidArgumentGrpcError was not thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('State Transition is not specified'); + expect(rpcClientMock.request).to.not.be.called(); + } + }); + + it('should return valid result', async () => { + const result = await broadcastStateTransitionHandler(call); + + const tx = stateTransitionFixture.toBuffer().toString('base64'); + + expect(result).to.be.an.instanceOf(BroadcastStateTransitionResponse); + expect(rpcClientMock.request).to.be.calledOnceWith('broadcast_tx_sync', { tx }); + }); + + it('should throw an error if transaction broadcast returns error', async () => { + const error = { code: -1, message: "Something didn't work", data: 'Some data' }; + + response.error = error; + + try { + await broadcastStateTransitionHandler(call); + + expect.fail('should throw an error'); + } catch (e) { + expect(e.message).to.equal(error.message); + expect(e.data).to.equal(error.data); + expect(e.code).to.equal(error.code); + } + }); + + it('should throw FailedPreconditionGrpcError if transaction was broadcasted twice', async () => { + response.error = { + code: -32603, + message: 'Internal error', + data: 'tx already exists in cache', + }; + + try { + await broadcastStateTransitionHandler(call); + + expect.fail('should throw AlreadyExistsGrpcError'); + } catch (e) { + expect(e).to.be.an.instanceOf(AlreadyExistsGrpcError); + expect(e.getMessage()).to.equal('State transition already in chain'); + } + }); + + it('should throw call createGrpcErrorFromDriveResponse if error code is not 0', async () => { + const message = 'not found'; + const metadata = { + data: 'some data', + }; + + createGrpcErrorFromDriveResponseMock.returns( + new NotFoundGrpcError(message, metadata), + ); + + response.result.code = GrpcErrorCodes.NOT_FOUND; + response.result.info = cbor.encode({ message, metadata }).toString('base64'); + + try { + await broadcastStateTransitionHandler(call); + + expect.fail('should throw AlreadyExistsGrpcError'); + } catch (e) { + expect(e).to.be.an.instanceOf(NotFoundGrpcError); + expect(e.getMessage()).to.equal(message); + expect(e.getRawMetadata()).to.deep.equal(metadata); + expect(e.getCode()).to.equal(response.result.code); + expect(createGrpcErrorFromDriveResponseMock).to.be.calledWithExactly( + response.result.code, + response.result.info, + ); + } + }); +}); diff --git a/packages/dapi/test/unit/grpcServer/handlers/platform/getConsensusParamsHandlerFactory.js b/packages/dapi/test/unit/grpcServer/handlers/platform/getConsensusParamsHandlerFactory.js new file mode 100644 index 00000000000..eac501f6c7c --- /dev/null +++ b/packages/dapi/test/unit/grpcServer/handlers/platform/getConsensusParamsHandlerFactory.js @@ -0,0 +1,159 @@ +const { + v0: { + GetConsensusParamsResponse, + ConsensusParamsBlock, + ConsensusParamsEvidence, + }, +} = require('@dashevo/dapi-grpc'); +const { + server: { + error: { + InternalGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); +const FailedPreconditionGrpcError = require('@dashevo/grpc-common/lib/server/error/FailedPreconditionGrpcError'); +const InvalidArgumentGrpcError = require('@dashevo/grpc-common/lib/server/error/InvalidArgumentGrpcError'); +const getConsensusParamsHandlerFactory = require('../../../../../lib/grpcServer/handlers/platform/getConsensusParamsHandlerFactory'); +const GrpcCallMock = require('../../../../../lib/test/mock/GrpcCallMock'); +const RPCError = require('../../../../../lib/rpcServer/RPCError'); + +describe('getConsensusParamsHandlerFactory', () => { + let getConsensusParamsHandler; + let getConsensusParamsMock; + let consensusParamsFixture; + let request; + let call; + + beforeEach(function beforeEach() { + request = { + getHeight: this.sinon.stub().returns(0), // gRPC returns 0 if in parameter is empty + getProve: this.sinon.stub().returns(false), + }; + + call = new GrpcCallMock(this.sinon, request); + + consensusParamsFixture = { + block: { + max_bytes: '22020096', + max_gas: '1000', + time_iota_ms: '1000', + }, + evidence: { + max_age_num_blocks: '100000', + max_age_duration: '200000', + max_bytes: '22020096', + }, + validator: { + pub_key_types: [ + 'ed25519', + ], + }, + }; + + getConsensusParamsMock = this.sinon.stub().resolves(consensusParamsFixture); + + getConsensusParamsHandler = getConsensusParamsHandlerFactory( + getConsensusParamsMock, + ); + }); + + it('should return valid data', async () => { + const result = await getConsensusParamsHandler(call); + + expect(result).to.be.an.instanceOf(GetConsensusParamsResponse); + + const block = result.getBlock(); + expect(block).to.be.an.instanceOf(ConsensusParamsBlock); + expect(block.getMaxBytes()).to.equal(consensusParamsFixture.block.max_bytes); + expect(block.getMaxGas()).to.equal(consensusParamsFixture.block.max_gas); + expect(block.getTimeIotaMs()).to.equal(consensusParamsFixture.block.time_iota_ms); + + const evidence = result.getEvidence(); + expect(evidence).to.be.an.instanceOf(ConsensusParamsEvidence); + expect(evidence.getMaxBytes()).to.equal(consensusParamsFixture.evidence.max_bytes); + expect(evidence.getMaxAgeDuration()).to.equal(consensusParamsFixture.evidence.max_age_duration); + expect(evidence.getMaxAgeNumBlocks()) + .to.equal(consensusParamsFixture.evidence.max_age_num_blocks); + + expect(getConsensusParamsMock).to.be.calledOnceWith(undefined); + }); + + it('should throw FailedPreconditionGrpcError', async () => { + const error = new RPCError(32603, 'invalid height', 'some data'); + getConsensusParamsMock.throws(error); + + try { + await getConsensusParamsHandler(call); + + expect.fail('should throw FailedPreconditionGrpcError'); + } catch (e) { + expect(e).to.be.an.instanceOf(FailedPreconditionGrpcError); + expect(e.getMessage()).to.equal('Invalid height: some data'); + expect(e.getCode()).to.equal(9); + } + }); + + it('should throw InternalGrpcError', async () => { + const error = new RPCError(32602, 'invalid height', 'some data'); + getConsensusParamsMock.throws(error); + + try { + await getConsensusParamsHandler(call); + + expect.fail('should throw InternalGrpcError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InternalGrpcError); + expect(e.getError()).to.equal(e.getError()); + } + }); + + it('should throw InvalidArgumentGrpcError', async () => { + request.getProve.returns(true); + + try { + await getConsensusParamsHandler(call); + + expect.fail('should throw InvalidArgumentGrpcError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('Prove is not implemented yet'); + } + }); + + it('should throw unknown error', async () => { + const error = new Error('unknown error'); + getConsensusParamsMock.throws(error); + + try { + await getConsensusParamsHandler(call); + + expect.fail('should throw InternalGrpcError'); + } catch (e) { + expect(e).to.equal(e); + } + }); + + it('should return valid data for height', async () => { + request.getHeight.returns(42); + + const result = await getConsensusParamsHandler(call); + + expect(result).to.be.an.instanceOf(GetConsensusParamsResponse); + + const block = result.getBlock(); + expect(block).to.be.an.instanceOf(ConsensusParamsBlock); + expect(block.getMaxBytes()).to.equal(consensusParamsFixture.block.max_bytes); + expect(block.getMaxGas()).to.equal(consensusParamsFixture.block.max_gas); + expect(block.getTimeIotaMs()).to.equal(consensusParamsFixture.block.time_iota_ms); + + const evidence = result.getEvidence(); + expect(evidence).to.be.an.instanceOf(ConsensusParamsEvidence); + expect(evidence.getMaxBytes()).to.equal(consensusParamsFixture.evidence.max_bytes); + expect(evidence.getMaxAgeDuration()).to.equal(consensusParamsFixture.evidence.max_age_duration); + expect(evidence.getMaxAgeNumBlocks()) + .to.equal(consensusParamsFixture.evidence.max_age_num_blocks); + + expect(getConsensusParamsMock).to.be.calledOnceWith(42); + }); +}); diff --git a/packages/dapi/test/unit/grpcServer/handlers/platform/getDataContractHandlerFactory.js b/packages/dapi/test/unit/grpcServer/handlers/platform/getDataContractHandlerFactory.js new file mode 100644 index 00000000000..b1fcbefc35c --- /dev/null +++ b/packages/dapi/test/unit/grpcServer/handlers/platform/getDataContractHandlerFactory.js @@ -0,0 +1,131 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + GetDataContractResponse, + Proof, + }, +} = require('@dashevo/dapi-grpc'); + +/* eslint-disable import/no-extraneous-dependencies */ +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); + +const GrpcCallMock = require('../../../../../lib/test/mock/GrpcCallMock'); + +const getDataContractHandlerFactory = require( + '../../../../../lib/grpcServer/handlers/platform/getDataContractHandlerFactory', +); + +describe('getDataContractHandlerFactory', () => { + let call; + let getDataContractHandler; + let driveStateRepositoryMock; + let request; + let id; + let dataContractFixture; + let proofFixture; + let proofMock; + let response; + + beforeEach(function beforeEach() { + id = generateRandomIdentifier(); + request = { + getId: this.sinon.stub().returns(id), + getProve: this.sinon.stub().returns(true), + }; + + call = new GrpcCallMock(this.sinon, request); + + dataContractFixture = getDataContractFixture(); + proofFixture = { + merkleProof: Buffer.alloc(1, 1), + }; + + proofMock = new Proof(); + proofMock.setMerkleProof(proofFixture.merkleProof); + + response = new GetDataContractResponse(); + response.setProof(proofMock); + response.setDataContract(dataContractFixture.toBuffer()); + + driveStateRepositoryMock = { + fetchDataContract: this.sinon.stub().resolves(response.serializeBinary()), + }; + + getDataContractHandler = getDataContractHandlerFactory( + driveStateRepositoryMock, + ); + }); + + it('should return valid data', async () => { + const result = await getDataContractHandler(call); + + expect(result).to.be.an.instanceOf(GetDataContractResponse); + + const contractBinary = result.getDataContract(); + expect(contractBinary).to.be.an.instanceOf(Uint8Array); + + expect(contractBinary).to.deep.equal(dataContractFixture.toBuffer()); + + const proof = result.getProof(); + + expect(proof).to.be.an.instanceOf(Proof); + const merkleProof = proof.getMerkleProof(); + + expect(merkleProof).to.deep.equal(proofFixture.merkleProof); + + expect(driveStateRepositoryMock.fetchDataContract).to.be.calledOnceWith(id.toBuffer(), true); + }); + + it('should not include proof', async () => { + request.getProve.returns(false); + response.setProof(null); + driveStateRepositoryMock.fetchDataContract.resolves(response.serializeBinary()); + + const result = await getDataContractHandler(call); + + expect(result).to.be.an.instanceOf(GetDataContractResponse); + const proof = result.getProof(); + + expect(proof).to.be.undefined(); + + expect(driveStateRepositoryMock.fetchDataContract).to.be.calledOnceWith(id.toBuffer(), false); + }); + + it('should throw InvalidArgumentGrpcError error if id is not specified', async () => { + id = null; + request.getId.returns(id); + + try { + await getDataContractHandler(call); + + expect.fail('should thrown InvalidArgumentGrpcError error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('id is not specified'); + expect(driveStateRepositoryMock.fetchDataContract).to.be.not.called(); + } + }); + + it('should throw error if driveStateRepository throws an error', async () => { + const message = 'Some error'; + const abciResponseError = new Error(message); + + driveStateRepositoryMock.fetchDataContract.throws(abciResponseError); + + try { + await getDataContractHandler(call); + + expect.fail('should throw error'); + } catch (e) { + expect(e).to.equal(abciResponseError); + } + }); +}); diff --git a/packages/dapi/test/unit/grpcServer/handlers/platform/getDocumentsHandlerFactory.js b/packages/dapi/test/unit/grpcServer/handlers/platform/getDocumentsHandlerFactory.js new file mode 100644 index 00000000000..96c138ae20d --- /dev/null +++ b/packages/dapi/test/unit/grpcServer/handlers/platform/getDocumentsHandlerFactory.js @@ -0,0 +1,197 @@ +const cbor = require('cbor'); + +const { + server: { + error: { + InvalidArgumentGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + GetDocumentsResponse, + Proof, + }, +} = require('@dashevo/dapi-grpc'); + +/* eslint-disable import/no-extraneous-dependencies */ +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); + +const GrpcCallMock = require('../../../../../lib/test/mock/GrpcCallMock'); + +const getDocumentsHandlerFactory = require( + '../../../../../lib/grpcServer/handlers/platform/getDocumentsHandlerFactory', +); + +describe('getDocumentsHandlerFactory', () => { + let call; + let getDocumentsHandler; + let driveStateRepositoryMock; + let request; + let documentsFixture; + let dataContractId; + let documentType; + let where; + let orderBy; + let limit; + let startAfter; + let startAt; + let documentsSerialized; + let proofFixture; + let response; + let proofMock; + + beforeEach(function beforeEach() { + dataContractId = generateRandomIdentifier(); + documentType = 'document'; + where = [['name', '==', 'John']]; + orderBy = [{ order: 'asc' }]; + limit = 20; + startAfter = new Uint8Array(generateRandomIdentifier().toBuffer()); + startAt = new Uint8Array([]); + + request = { + getDataContractId: this.sinon.stub().returns(dataContractId), + getDocumentType: this.sinon.stub().returns(documentType), + getWhere_asU8: this.sinon.stub().returns(new Uint8Array(cbor.encode(where))), + getOrderBy_asU8: this.sinon.stub().returns(new Uint8Array(cbor.encode(orderBy))), + getLimit: this.sinon.stub().returns(limit), + getStartAfter_asU8: this.sinon.stub().returns(startAfter), + getStartAt_asU8: this.sinon.stub().returns(startAt), + getProve: this.sinon.stub().returns(false), + }; + + call = new GrpcCallMock(this.sinon, request); + + const [document] = getDocumentsFixture(); + + documentsFixture = [document]; + + documentsSerialized = documentsFixture.map((documentItem) => documentItem.toBuffer()); + proofFixture = { + merkleProof: Buffer.alloc(1, 1), + }; + + proofMock = new Proof(); + proofMock.setMerkleProof(proofFixture.merkleProof); + + response = new GetDocumentsResponse(); + response.setProof(proofMock); + response.setDocumentsList(documentsSerialized); + + driveStateRepositoryMock = { + fetchDocuments: this.sinon.stub().resolves(response.serializeBinary()), + }; + + getDocumentsHandler = getDocumentsHandlerFactory( + driveStateRepositoryMock, + ); + }); + + it('should return valid result', async () => { + response.setProof(null); + + driveStateRepositoryMock.fetchDocuments.resolves(response.serializeBinary()); + + const result = await getDocumentsHandler(call); + + expect(result).to.be.an.instanceOf(GetDocumentsResponse); + + const documentsBinary = result.getDocumentsList(); + expect(documentsBinary).to.be.an('array'); + expect(documentsBinary).to.have.lengthOf(documentsFixture.length); + + expect(driveStateRepositoryMock.fetchDocuments).to.be.calledOnceWith( + dataContractId.toBuffer(), + documentType, + { + where, + orderBy, + limit, + startAfter: Buffer.from(startAfter), + startAt: undefined, + }, + false, + ); + + expect(documentsBinary[0]).to.deep.equal(documentsSerialized[0]); + + const proof = result.getProof(); + + expect(proof).to.be.undefined(); + }); + + it('should return proof', async () => { + request.getProve.returns(true); + + const result = await getDocumentsHandler(call); + + expect(result).to.be.an.instanceOf(GetDocumentsResponse); + + expect(driveStateRepositoryMock.fetchDocuments).to.be.calledOnceWith( + dataContractId.toBuffer(), + documentType, + { + where, + orderBy, + limit, + startAfter: Buffer.from(startAfter), + startAt: undefined, + }, + true, + ); + + const proof = result.getProof(); + + expect(proof).to.be.an.instanceOf(Proof); + const merkleProof = proof.getMerkleProof(); + + expect(merkleProof).to.deep.equal(proofFixture.merkleProof); + }); + + it('should throw InvalidArgumentGrpcError if dataContractId is not specified', async () => { + dataContractId = null; + request.getDataContractId.returns(dataContractId); + + try { + await getDocumentsHandler(call); + + expect.fail('should throw InvalidArgumentGrpcError error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('dataContractId is not specified'); + expect(driveStateRepositoryMock.fetchDocuments).to.be.not.called(); + } + }); + + it('should throw InvalidArgumentGrpcError if documentType is not specified', async () => { + documentType = null; + request.getDocumentType.returns(documentType); + + try { + await getDocumentsHandler(call); + + expect.fail('should throw InvalidArgumentGrpcError error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('documentType is not specified'); + expect(driveStateRepositoryMock.fetchDocuments).to.be.not.called(); + } + }); + + it('should throw error if fetchDocuments throws an error', async () => { + const error = new Error('Some error'); + + driveStateRepositoryMock.fetchDocuments.throws(error); + + try { + await getDocumentsHandler(call); + + expect.fail('should throw InvalidArgumentGrpcError error'); + } catch (e) { + expect(e).to.equal(error); + } + }); +}); diff --git a/packages/dapi/test/unit/grpcServer/handlers/platform/getIdentitiesByPublicKeyHashesHandlerFactory.js b/packages/dapi/test/unit/grpcServer/handlers/platform/getIdentitiesByPublicKeyHashesHandlerFactory.js new file mode 100644 index 00000000000..fd1f504852a --- /dev/null +++ b/packages/dapi/test/unit/grpcServer/handlers/platform/getIdentitiesByPublicKeyHashesHandlerFactory.js @@ -0,0 +1,129 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + GetIdentitiesByPublicKeyHashesResponse, + Proof, + }, +} = require('@dashevo/dapi-grpc'); + +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); + +const getIdentitiesByPublicKeyHashesHandlerFactory = require( + '../../../../../lib/grpcServer/handlers/platform/getIdentitiesByPublicKeyHashesHandlerFactory', +); + +const GrpcCallMock = require('../../../../../lib/test/mock/GrpcCallMock'); + +describe('getIdentitiesByPublicKeyHashesHandlerFactory', () => { + let call; + let driveStateRepositoryMock; + let getIdentitiesByPublicKeyHashesHandler; + let identity; + let publicKeyHash; + let proofFixture; + let proofMock; + let response; + + beforeEach(function beforeEach() { + publicKeyHash = Buffer.from('556c2910d46fda2b327ef9d9bda850cc84d30db0', 'hex'); + + call = new GrpcCallMock(this.sinon, { + getPublicKeyHashesList: this.sinon.stub().returns( + [publicKeyHash], + ), + getProve: this.sinon.stub().returns(false), + }); + + identity = getIdentityFixture(); + + proofFixture = { + merkleProof: Buffer.alloc(1, 1), + }; + + proofMock = new Proof(); + proofMock.setMerkleProof(proofFixture.merkleProof); + + response = new GetIdentitiesByPublicKeyHashesResponse(); + response.setProof(proofMock); + response.setIdentitiesList([identity.toBuffer()]); + + driveStateRepositoryMock = { + fetchIdentitiesByPublicKeyHashes: this.sinon.stub().resolves(response.serializeBinary()), + }; + + getIdentitiesByPublicKeyHashesHandler = getIdentitiesByPublicKeyHashesHandlerFactory( + driveStateRepositoryMock, + ); + }); + + it('should return valid result', async () => { + response.setProof(null); + driveStateRepositoryMock.fetchIdentitiesByPublicKeyHashes.resolves(response.serializeBinary()); + + const result = await getIdentitiesByPublicKeyHashesHandler(call); + + expect(result).to.be.an.instanceOf(GetIdentitiesByPublicKeyHashesResponse); + + expect(result.getIdentitiesList()).to.deep.equal( + [identity.toBuffer()], + ); + + expect(driveStateRepositoryMock.fetchIdentitiesByPublicKeyHashes) + .to.be.calledOnceWith([publicKeyHash], false); + + const proof = result.getProof(); + + expect(proof).to.be.undefined(); + }); + + it('should return proof', async () => { + call.request.getProve.returns(true); + const result = await getIdentitiesByPublicKeyHashesHandler(call); + + expect(result).to.be.an.instanceOf(GetIdentitiesByPublicKeyHashesResponse); + + const proof = result.getProof(); + + expect(proof).to.be.an.instanceOf(Proof); + const merkleProof = proof.getMerkleProof(); + + expect(merkleProof).to.deep.equal(proofFixture.merkleProof); + }); + + it('should throw an InvalidArgumentGrpcError if no hashes were submitted', async () => { + call.request.getPublicKeyHashesList.returns([]); + + try { + await getIdentitiesByPublicKeyHashesHandler(call); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('No public key hashes were provided'); + expect(driveStateRepositoryMock.fetchIdentitiesByPublicKeyHashes).to.not.be.called(); + } + }); + + it('should throw an error when fetchIdentity throws an error', async () => { + const error = new Error('Unknown error'); + + driveStateRepositoryMock.fetchIdentitiesByPublicKeyHashes.throws(error); + + try { + await getIdentitiesByPublicKeyHashesHandler(call); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.equal(error); + expect(driveStateRepositoryMock.fetchIdentitiesByPublicKeyHashes) + .to.be.calledOnceWith([publicKeyHash]); + } + }); +}); diff --git a/packages/dapi/test/unit/grpcServer/handlers/platform/getIdentityHandlerFactory.js b/packages/dapi/test/unit/grpcServer/handlers/platform/getIdentityHandlerFactory.js new file mode 100644 index 00000000000..a90bdb19262 --- /dev/null +++ b/packages/dapi/test/unit/grpcServer/handlers/platform/getIdentityHandlerFactory.js @@ -0,0 +1,122 @@ +const { + server: { + error: { + InvalidArgumentGrpcError, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + GetIdentityResponse, + Proof, + }, +} = require('@dashevo/dapi-grpc'); + +/* eslint-disable import/no-extraneous-dependencies */ +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); + +const getIdentityHandlerFactory = require('../../../../../lib/grpcServer/handlers/platform/getIdentityHandlerFactory'); + +const GrpcCallMock = require('../../../../../lib/test/mock/GrpcCallMock'); + +describe('getIdentityHandlerFactory', () => { + let call; + let driveStateRepositoryMock; + let id; + let getIdentityHandler; + let identity; + let proofFixture; + let proofMock; + let response; + + beforeEach(function beforeEach() { + id = generateRandomIdentifier(); + call = new GrpcCallMock(this.sinon, { + getId: this.sinon.stub().returns(id), + getProve: this.sinon.stub().returns(false), + }); + + identity = getIdentityFixture(); + + proofFixture = { + merkleProof: Buffer.alloc(1, 1), + }; + + proofMock = new Proof(); + proofMock.setMerkleProof(proofFixture.merkleProof); + + response = new GetIdentityResponse(); + response.setProof(proofMock); + response.setIdentity(identity.toBuffer()); + + driveStateRepositoryMock = { + fetchIdentity: this.sinon.stub().resolves(response.serializeBinary()), + }; + + getIdentityHandler = getIdentityHandlerFactory( + driveStateRepositoryMock, + ); + }); + + it('should return valid result', async () => { + response.setProof(null); + driveStateRepositoryMock.fetchIdentity.resolves(response.serializeBinary()); + + const result = await getIdentityHandler(call); + + expect(result).to.be.an.instanceOf(GetIdentityResponse); + expect(result.getIdentity()).to.deep.equal(identity.toBuffer()); + expect(driveStateRepositoryMock.fetchIdentity).to.be.calledOnceWith(id.toBuffer(), false); + + const proof = result.getProof(); + expect(proof).to.be.undefined(); + }); + + it('should return proof', async () => { + call.request.getProve.returns(true); + + const result = await getIdentityHandler(call); + + expect(result).to.be.an.instanceOf(GetIdentityResponse); + + const proof = result.getProof(); + + expect(proof).to.be.an.instanceOf(Proof); + const merkleProof = proof.getMerkleProof(); + + expect(merkleProof).to.deep.equal(proofFixture.merkleProof); + + expect(driveStateRepositoryMock.fetchIdentity).to.be.calledOnceWith(id.toBuffer(), true); + }); + + it('should throw an InvalidArgumentGrpcError if id is not specified', async () => { + call.request.getId.returns(null); + + try { + await getIdentityHandler(call); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('id is not specified'); + expect(driveStateRepositoryMock.fetchIdentity).to.not.be.called(); + } + }); + + it('should throw an error when fetchIdentity throws unknown error', async () => { + const error = new Error('Unknown error'); + + driveStateRepositoryMock.fetchIdentity.throws(error); + + try { + await getIdentityHandler(call); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.equal(error); + expect(driveStateRepositoryMock.fetchIdentity).to.be.calledOnceWith(id.toBuffer()); + } + }); +}); diff --git a/packages/dapi/test/unit/grpcServer/handlers/tx-filter-stream/subscribeToTransactionsWithProofsHandlerFactory.spec.js b/packages/dapi/test/unit/grpcServer/handlers/tx-filter-stream/subscribeToTransactionsWithProofsHandlerFactory.spec.js new file mode 100644 index 00000000000..8024a7ea77e --- /dev/null +++ b/packages/dapi/test/unit/grpcServer/handlers/tx-filter-stream/subscribeToTransactionsWithProofsHandlerFactory.spec.js @@ -0,0 +1,392 @@ +const { expect, use } = require('chai'); +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); +const dirtyChai = require('dirty-chai'); +const chaiAsPromised = require('chai-as-promised'); +const { Transaction } = require('@dashevo/dashcore-lib'); + +const { + server: { + error: { + InvalidArgumentGrpcError, + NotFoundGrpcError, + }, + stream: { + AcknowledgingWritable, + }, + }, +} = require('@dashevo/grpc-common'); + +const { + v0: { + TransactionsWithProofsRequest, + TransactionsWithProofsResponse, + RawTransactions, + BloomFilter, + }, +} = require('@dashevo/dapi-grpc'); + +const { BloomFilter: CoreBloomFilter } = require('@dashevo/dashcore-lib'); + +const GrpcCallMock = require('../../../../../lib/test/mock/GrpcCallMock'); +const subscribeToTransactionsWithProofsHandlerFactory = require( + '../../../../../lib/grpcServer/handlers/tx-filter-stream/subscribeToTransactionsWithProofsHandlerFactory', +); + +const ProcessMediator = require('../../../../../lib/transactionsFilter/ProcessMediator'); + +use(sinonChai); +use(chaiAsPromised); +use(dirtyChai); + +describe('subscribeToTransactionsWithProofsHandlerFactory', () => { + beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } + }); + + afterEach(function afterEach() { + this.sinon.restore(); + }); + + let call; + let subscribeToTransactionsWithProofsHandler; + let bloomFilterEmitterCollectionMock; + let historicalTxData; + let getHistoricalTransactionsIteratorMock; + let subscribeToNewTransactionsMock; + let testTransactionAgainstFilterMock; + let coreAPIMock; + let getMemPoolTransactionsMock; + + beforeEach(function beforeEach() { + const bloomFilterMessage = new BloomFilter(); + + bloomFilterMessage.setVData(new Uint8Array()); + bloomFilterMessage.setNTweak(1000); + bloomFilterMessage.setNFlags(100); + bloomFilterMessage.setNHashFuncs(10); + + const request = new TransactionsWithProofsRequest(); + + request.setBloomFilter(bloomFilterMessage); + + call = new GrpcCallMock(this.sinon, request); + + bloomFilterEmitterCollectionMock = { + test: this.sinon.stub(), + }; + + historicalTxData = []; + getHistoricalTransactionsIteratorMock = this.sinon.spy(function* generator() { + for (let i = 0; i < historicalTxData.length; i++) { + yield historicalTxData[i]; + } + }); + + subscribeToNewTransactionsMock = this.sinon.stub(); + testTransactionAgainstFilterMock = this.sinon.stub(); + + coreAPIMock = { + getBlock: this.sinon.stub(), + getBlockStats: this.sinon.stub(), + getBestBlockHeight: this.sinon.stub(), + getBlockHash: this.sinon.stub(), + }; + + getMemPoolTransactionsMock = this.sinon.stub().returns([]); + + subscribeToTransactionsWithProofsHandler = subscribeToTransactionsWithProofsHandlerFactory( + getHistoricalTransactionsIteratorMock, + subscribeToNewTransactionsMock, + bloomFilterEmitterCollectionMock, + testTransactionAgainstFilterMock, + coreAPIMock, + getMemPoolTransactionsMock, + ); + + this.sinon.spy(ProcessMediator.prototype, 'emit'); + this.sinon.spy(ProcessMediator.prototype, 'on'); + }); + + it('should respond with error if bloom filter is not valid', async () => { + const bloomFilterMessage = new BloomFilter(); + + bloomFilterMessage.setVData(new Uint8Array()); + bloomFilterMessage.setNTweak(1000); + bloomFilterMessage.setNFlags(100); + bloomFilterMessage.setNHashFuncs(100); + + const request = new TransactionsWithProofsRequest(); + + request.setBloomFilter(bloomFilterMessage); + request.setFromBlockHeight(1); + + call.request = request; + + try { + await subscribeToTransactionsWithProofsHandler(call); + + expect.fail('Error was not thrown'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('Invalid bloom filter: ' + + '"nHashFuncs" exceeded max size "50"'); + + expect(call.write).to.not.have.been.called(); + expect(call.end).to.not.have.been.called(); + expect(getMemPoolTransactionsMock).to.not.have.been.called(); + } + }); + + it('should respond with error if requested data length exceeded blockchain length', async () => { + const blockHash = Buffer.from('00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc2c', 'hex'); + + call.request.setFromBlockHash(blockHash); + call.request.setCount(100); + + call.request = TransactionsWithProofsRequest.deserializeBinary(call.request.serializeBinary()); + + coreAPIMock.getBlockStats.resolves({ height: 1 }); + coreAPIMock.getBestBlockHeight.resolves(10); + + try { + await subscribeToTransactionsWithProofsHandler(call); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal( + 'count is too big, could not fetch more than blockchain length', + ); + + expect(coreAPIMock.getBlockStats).to.be.calledOnceWithExactly( + blockHash.toString('hex'), + ['height'], + ); + expect(call.write).to.not.have.been.called(); + expect(call.end).to.not.have.been.called(); + expect(getMemPoolTransactionsMock).to.not.have.been.called(); + } + }); + + it('should subscribe to new transactions if count is not specified', async function it() { + const blockHash = Buffer.from('00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc2c', 'hex'); + + const writableStub = this.sinon.stub(AcknowledgingWritable.prototype, 'write'); + + call.request.setFromBlockHash(blockHash); + call.request.setCount(0); + + call.request = TransactionsWithProofsRequest.deserializeBinary(call.request.serializeBinary()); + + call = new GrpcCallMock(this.sinon, call.request); + + coreAPIMock.getBlockStats.resolves({ height: 1 }); + coreAPIMock.getBestBlockHeight.resolves(10); + + historicalTxData.push({ + merkleBlock: { + toBuffer: () => Buffer.from('someHash'), + header: { + hash: 'someHash', + }, + }, + transactions: [ + { + toBuffer: () => Buffer.from( + 'edefad1c70ee6736a0a0c2f9be7f22cfcf77ae2c120704a98cdc9aebdab7ffc5', 'hex', + ), + }, + ], + }); + + const memPoolTransaction = new Transaction(Buffer.from('03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff1703f06a101299dbcd32279d9e01e508000000002f4e614effffffff0285464209000000001976a9146a341485a9444b35dc9cb90d24e7483de7d37e0088ac7f464209000000001976a914ad037df64c0d0ec5d0395eb9a543f93fcc26092388ac00000000260100f06a1000c69a125eeb5ce6fa55c48966174a90253a79ce3350ccc4918ba2cb1463513c88', 'hex')); + + getMemPoolTransactionsMock.returns([memPoolTransaction]); + + await subscribeToTransactionsWithProofsHandler(call); + + const filter = new CoreBloomFilter({ + vData: new Uint8Array([]), + nTweak: 1000, + nFlags: 100, + nHashFuncs: 10, + }); + + expect(coreAPIMock.getBlockStats).to.be.calledOnceWithExactly( + blockHash.toString('hex'), + ['height'], + ); + expect(getHistoricalTransactionsIteratorMock).to.have.been + .calledOnceWith( + filter, + 1, + 10, + ); + expect(getMemPoolTransactionsMock).to.have.been.calledOnce(); + + expect(subscribeToNewTransactionsMock).to.have.been.calledOnce(); + expect(writableStub).to.have.been.calledTwice(); + + const firstResponse = new TransactionsWithProofsResponse(); + const rawTransactions = new RawTransactions(); + rawTransactions.setTransactionsList( + historicalTxData[0].transactions.map((tx) => tx.toBuffer()), + ); + firstResponse.setRawTransactions(rawTransactions); + + const secondResponse = new TransactionsWithProofsResponse(); + secondResponse.setRawMerkleBlock(historicalTxData[0].merkleBlock.toBuffer()); + + expect(writableStub.getCall(0).args).to.deep.equal( + [firstResponse], + ); + + expect(writableStub.getCall(1).args).to.deep.equal( + [secondResponse], + ); + + expect(bloomFilterEmitterCollectionMock.test).to.be.calledOnceWith(memPoolTransaction); + }); + + it('should end call and emit CLIENT_DISCONNECTED event when client disconnects', async () => { + const blockHash = Buffer.from('00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc2c', 'hex'); + + call.request.setFromBlockHash(blockHash); + + call.request = TransactionsWithProofsRequest.deserializeBinary(call.request.serializeBinary()); + + coreAPIMock.getBlockStats.resolves({ height: 1 }); + + await subscribeToTransactionsWithProofsHandler(call); + + // Client disconnects + call.emit('cancelled'); + + // Bloom filters was removed when client disconnects + expect(ProcessMediator.prototype.emit.getCall(2)).to.be.calledWith( + ProcessMediator.EVENTS.CLIENT_DISCONNECTED, + ); + + expect(coreAPIMock.getBlockStats).to.be.calledOnceWithExactly( + blockHash.toString('hex'), + ['height'], + ); + expect(call.write).to.not.have.been.called(); + expect(call.end).to.have.been.calledOnce(); + expect(getMemPoolTransactionsMock).to.have.been.calledOnce(); + }); + + it('should respond with Not Found error if fromBlockHeight is bigger than block count', async () => { + call.request.setFromBlockHeight(100); + call.request.setCount(0); + + const error = new Error(); + error.code = -8; + + coreAPIMock.getBlockStats.throws(error); + + try { + await subscribeToTransactionsWithProofsHandler(call); + + expect.fail('should fail with NotFoundGrpcError'); + } catch (e) { + expect(e).to.be.instanceOf(NotFoundGrpcError); + + expect(e.getMessage()).to.equal('Block 100 not found'); + + expect(call.write).to.not.have.been.called(); + expect(call.end).to.not.have.been.called(); + } + }); + + it('should respond with not found error if Block not found', async () => { + const blockHash = Buffer.from('00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc2c', 'hex'); + + call.request.setFromBlockHash(blockHash); + call.request.setCount(0); + + call.request = TransactionsWithProofsRequest.deserializeBinary(call.request.serializeBinary()); + + const error = new Error(); + error.code = -5; + + coreAPIMock.getBlockStats.throws(error); + + try { + await subscribeToTransactionsWithProofsHandler(call); + + expect.fail('should fail with NotFoundGrpcError'); + } catch (e) { + expect(e).to.be.instanceOf(NotFoundGrpcError); + expect(e.getMessage()).to.equal(`Block ${blockHash.toString('hex')} not found`); + + expect(coreAPIMock.getBlockStats).to.be.calledOnceWithExactly( + blockHash.toString('hex'), + ['height'], + ); + expect(call.write).to.not.have.been.called(); + expect(call.end).to.not.have.been.called(); + } + }); + + it('should respond with Not Found error if fromBlockHash is not part of the best block chain', async () => { + const blockHash = Buffer.from('00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc2c', 'hex'); + + call.request.setFromBlockHash(Buffer.from(blockHash, 'hex')); + call.request.setCount(0); + + call.request = TransactionsWithProofsRequest.deserializeBinary(call.request.serializeBinary()); + + const error = new Error(); + error.code = -8; + + coreAPIMock.getBlockStats.throws(error); + + try { + await subscribeToTransactionsWithProofsHandler(call); + + expect.fail('should throw NotFoundGrpcError'); + } catch (e) { + expect(e).to.be.instanceOf(NotFoundGrpcError); + expect(e.getMessage()).to.equal(`Block ${blockHash.toString('hex')} not found`); + + expect(coreAPIMock.getBlockStats).to.be.calledOnceWithExactly( + blockHash.toString('hex'), + ['height'], + ); + expect(call.write).to.not.have.been.called(); + expect(call.end).to.not.have.been.called(); + } + }); + + it('should respond with error if `fromBlockHeight is 0 and `fromBlockHash` is not set', async () => { + const bloomFilterMessage = new BloomFilter(); + + bloomFilterMessage.setVData(new Uint8Array()); + bloomFilterMessage.setNTweak(1000); + bloomFilterMessage.setNFlags(100); + bloomFilterMessage.setNHashFuncs(10); + + const request = new TransactionsWithProofsRequest(); + + request.setFromBlockHeight(0); + request.setBloomFilter(bloomFilterMessage); + + call.request = request; + + try { + await subscribeToTransactionsWithProofsHandler(call); + + expect.fail('should fail with InvalidArgumentGrpcError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidArgumentGrpcError); + expect(e.getMessage()).to.equal('Minimum value for `fromBlockHeight` is 1'); + expect(call.write).to.not.have.been.called(); + expect(call.end).to.not.have.been.called(); + expect(getMemPoolTransactionsMock).to.not.have.been.called(); + } + }); +}); diff --git a/packages/dapi/test/unit/rpcServer/RPCError.js b/packages/dapi/test/unit/rpcServer/RPCError.js new file mode 100644 index 00000000000..2e6415edeb4 --- /dev/null +++ b/packages/dapi/test/unit/rpcServer/RPCError.js @@ -0,0 +1,39 @@ +const chai = require('chai'); +const RPCError = require('../../../lib/rpcServer/RPCError'); + +const { expect } = chai; + +describe('lib/rpcServer/RPCError', () => { + describe('#factory', () => { + it('should create RPCError instance without params', () => { + const res = new RPCError(); + expect(res).to.be.instanceof(RPCError); + }); + }); + describe('#factory', () => { + it('should create RPCError instance with code', () => { + const res = new RPCError(200); + expect(res).to.be.instanceof(RPCError); + }); + }); + describe('#factory', () => { + it('should create RPCError instance with code & message', () => { + const res = new RPCError(200, 'my_message'); + expect(res).to.be.instanceof(RPCError); + }); + }); + describe('#factory', () => { + it('should create RPCError instance with code, message and data', () => { + const data = {}; + const res = new RPCError(200, 'my_message', data); + expect(res).to.be.instanceof(RPCError); + }); + }); + describe('#factory', () => { + it('should create RPCError instance with code, message, data and originalStack', () => { + const data = {}; + const res = new RPCError(200, 'my_message', data, 'my_stack'); + expect(res).to.be.instanceof(RPCError); + }); + }); +}); diff --git a/packages/dapi/test/unit/rpcServer/commands/generateToAddress.js b/packages/dapi/test/unit/rpcServer/commands/generateToAddress.js new file mode 100644 index 00000000000..9b6bfcb6b86 --- /dev/null +++ b/packages/dapi/test/unit/rpcServer/commands/generateToAddress.js @@ -0,0 +1,65 @@ +const chai = require('chai'); +const sinon = require('sinon'); +const chaiAsPromised = require('chai-as-promised'); +const generateToAddressFactory = require('../../../../lib/rpcServer/commands/generateToAddress'); +const coreAPIFixture = require('../../../mocks/coreAPIFixture'); + +const { expect } = chai; +chai.use(chaiAsPromised); +let spy; + +describe('generateToAddress', () => { + describe('#factory', () => { + it('should return a function', () => { + const generate = generateToAddressFactory(coreAPIFixture); + expect(generate).to.be.a('function'); + }); + }); + + before(() => { + spy = sinon.spy(coreAPIFixture, 'generateToAddress'); + }); + + beforeEach(() => { + spy.resetHistory(); + }); + + after(() => { + spy.restore(); + }); + + it('Should return an array of block hashes', async () => { + const generateToAddress = generateToAddressFactory(coreAPIFixture); + + expect(spy.callCount).to.be.equal(0); + + const blockHashes = await generateToAddress({ blocksNumber: 10, address: '123456' }); + + expect(blockHashes).to.be.an('array'); + expect(blockHashes.length).to.be.equal(10); + expect(spy.callCount).to.be.equal(1); + }); + + it('Should throw an error if arguments are not valid', async () => { + const generateToAddress = generateToAddressFactory(coreAPIFixture); + expect(spy.callCount).to.be.equal(0); + + await expect(generateToAddress({ blocksNumber: -1, address: '123' })).to.be.rejectedWith('must be >= 1'); + expect(spy.callCount).to.be.equal(0); + + await expect(generateToAddress({ blocksNumber: 0.5, address: '123' })).to.be.rejectedWith('must be integer'); + expect(spy.callCount).to.be.equal(0); + + await expect(generateToAddress({})).to.be.rejectedWith('must have required property'); + expect(spy.callCount).to.be.equal(0); + + await expect(generateToAddress()).to.be.rejectedWith('must be object'); + expect(spy.callCount).to.be.equal(0); + + await expect(generateToAddress({ blocksNumber: 'string', address: '123' })).to.be.rejectedWith('must be integer'); + expect(spy.callCount).to.be.equal(0); + + await expect(generateToAddress({ blocksNumber: 1, address: 1 })).to.be.rejectedWith('must be string'); + expect(spy.callCount).to.be.equal(0); + }); +}); diff --git a/packages/dapi/test/unit/rpcServer/commands/getBestBlockHash.js b/packages/dapi/test/unit/rpcServer/commands/getBestBlockHash.js new file mode 100644 index 00000000000..23dc36b268a --- /dev/null +++ b/packages/dapi/test/unit/rpcServer/commands/getBestBlockHash.js @@ -0,0 +1,38 @@ +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +const sinon = require('sinon'); +const getBestBlockHashFactory = require('../../../../lib/rpcServer/commands/getBestBlockHash'); +const coreAPIFixture = require('../../../mocks/coreAPIFixture'); + +chai.use(chaiAsPromised); +const { expect } = chai; +let spy; + +describe('getBestBlockHash', () => { + describe('#factory', () => { + it('should return a function', () => { + const getBestBlockHash = getBestBlockHashFactory(coreAPIFixture); + expect(getBestBlockHash).to.be.a('function'); + }); + }); + + before(() => { + spy = sinon.spy(coreAPIFixture, 'getBestBlockHash'); + }); + + beforeEach(() => { + spy.resetHistory(); + }); + + after(() => { + spy.restore(); + }); + + it('Should return a number', async () => { + const getBestBlockHash = getBestBlockHashFactory(coreAPIFixture); + expect(spy.callCount).to.be.equal(0); + const bestBlockHash = await getBestBlockHash(); + expect(bestBlockHash).to.be.an('string'); + expect(spy.callCount).to.be.equal(1); + }); +}); diff --git a/packages/dapi/test/unit/rpcServer/commands/getBlockHash.js b/packages/dapi/test/unit/rpcServer/commands/getBlockHash.js new file mode 100644 index 00000000000..71b4477f1fa --- /dev/null +++ b/packages/dapi/test/unit/rpcServer/commands/getBlockHash.js @@ -0,0 +1,55 @@ +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +const sinon = require('sinon'); +const getBlockHashFactory = require('../../../../lib/rpcServer/commands/getBlockHash'); +const coreAPIFixture = require('../../../mocks/coreAPIFixture'); + +chai.use(chaiAsPromised); +const { expect } = chai; +let spy; + +describe('getBlockHash', () => { + describe('#factory', () => { + it('should return a function', () => { + const getBlockHash = getBlockHashFactory(coreAPIFixture); + expect(getBlockHash).to.be.a('function'); + }); + }); + + before(() => { + spy = sinon.spy(coreAPIFixture, 'getBlockHash'); + }); + + beforeEach(() => { + spy.resetHistory(); + }); + + after(() => { + spy.restore(); + }); + + it('Should return block hash', async () => { + const getBlockHash = getBlockHashFactory(coreAPIFixture); + expect(spy.callCount).to.be.equal(0); + const blockHash = await getBlockHash({ height: 100 }); + expect(blockHash).to.be.a('string'); + expect(spy.callCount).to.be.equal(1); + }); + + it('Should throw an error if arguments are not valid', async () => { + const getBlockHash = getBlockHashFactory(coreAPIFixture); + expect(spy.callCount).to.be.equal(0); + await expect(getBlockHash({ height: -1 })).to.be.rejectedWith('params/height must be >= 0'); + expect(spy.callCount).to.be.equal(0); + await expect(getBlockHash({ height: 0.5 })).to.be.rejectedWith('params/height must be integer'); + expect(spy.callCount).to.be.equal(0); + await expect(getBlockHash({})).to.be.rejectedWith('must have required property'); + expect(spy.callCount).to.be.equal(0); + await expect(getBlockHash()).to.be.rejectedWith('params must be object'); + expect(spy.callCount).to.be.equal(0); + await expect(getBlockHash({ height: 'string' })).to.be.rejectedWith('params/height must be integer'); + expect(spy.callCount).to.be.equal(0); + await expect(getBlockHash([-1])).to.be.rejectedWith('params must be object'); + expect(spy.callCount).to.be.equal(0); + }); +}); diff --git a/packages/dapi/test/unit/rpcServer/commands/getMnListDiff.js b/packages/dapi/test/unit/rpcServer/commands/getMnListDiff.js new file mode 100644 index 00000000000..d757928b4d0 --- /dev/null +++ b/packages/dapi/test/unit/rpcServer/commands/getMnListDiff.js @@ -0,0 +1,50 @@ +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +const sinon = require('sinon'); +const getMNListDiffFactory = require('../../../../lib/rpcServer/commands/getMnListDiff'); +const coreAPIFixture = require('../../../mocks/coreAPIFixture'); + +chai.use(chaiAsPromised); +const { expect } = chai; +let spy; +let baseBlockHash; +let blockHash; + +describe('getMNListDiff', () => { + describe('#factory', () => { + it('should return a function', () => { + const getMNListDiff = getMNListDiffFactory(coreAPIFixture); + expect(getMNListDiff).to.be.a('function'); + }); + }); + + before(() => { + spy = sinon.spy(coreAPIFixture, 'getMnListDiff'); + }); + + beforeEach(() => { + spy.resetHistory(); + + baseBlockHash = '0000000000000000000000000000000000000000000000000000000000000000'; + blockHash = '0000000000000000000000000000000000000000000000000000000000000000'; + }); + + after(() => { + spy.restore(); + }); + + it('Should return a masternode list difference list object', async () => { + const getMNListDiff = getMNListDiffFactory(coreAPIFixture); + expect(spy.callCount).to.be.equal(0); + + const mnDiffList = await getMNListDiff({ baseBlockHash, blockHash }); + expect(mnDiffList).to.be.an('object'); + expect(mnDiffList.baseBlockHash.length).to.equal(64); + expect(mnDiffList.blockHash.length).to.equal(64); + expect(mnDiffList.merkleRootMNList.length).to.equal(64); + expect(mnDiffList.deletedMNs).to.be.an('Array'); + expect(mnDiffList.mnList).to.be.an('Array'); + + expect(spy.callCount).to.be.equal(1); + }); +}); diff --git a/packages/dapi/test/unit/rpcServer/errorHandlerDecorator.js b/packages/dapi/test/unit/rpcServer/errorHandlerDecorator.js new file mode 100644 index 00000000000..9b9761839b5 --- /dev/null +++ b/packages/dapi/test/unit/rpcServer/errorHandlerDecorator.js @@ -0,0 +1,38 @@ +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +const RPCError = require('../../../lib/rpcServer/RPCError'); +const ArgumentValidationError = require('../../../lib/errors/ArgumentsValidationError'); +const errorHandlerDecorator = require('../../../lib/rpcServer/errorHandlerDecorator'); + +chai.use(chaiAsPromised); +const { expect } = chai; + +describe('lib/rpcServer/errorHandlerDecorator', () => { + it('should be errorHandlerDecorator function', () => { + const res = errorHandlerDecorator; + expect(res).to.be.a('function'); + }); + it('should return function', () => { + const res = errorHandlerDecorator(); + expect(res).to.be.a('function'); + }); + it('should throw error when call errorHandlerDecorator with non existing command', () => { + const res = errorHandlerDecorator('fake'); + expect(() => res('my_arg')).to.throw('command is not a function'); + }); + it('Should not modify the error if the error is an instance of an RPCError', () => { + const throwingFunction = async () => { throw new RPCError(-1, 'Some message'); }; + const decoratedFunction = errorHandlerDecorator(throwingFunction); + return expect(decoratedFunction()).to.be.rejectedWith(RPCError, 'Some message'); + }); + it('Should throw RPCError with same message in case of arguments validation error ', () => { + const throwingFunction = async () => { throw new ArgumentValidationError('Test message #2'); }; + const decoratedFunction = errorHandlerDecorator(throwingFunction); + return expect(decoratedFunction()).to.be.rejectedWith(RPCError, 'Test message #2'); + }); + it('Should throw RPCError with text "Internal Error" in case if error was not recognized', () => { + const throwingFunction = async () => { throw new Error('Test message #3'); }; + const decoratedFunction = errorHandlerDecorator(throwingFunction); + return expect(decoratedFunction()).to.be.rejectedWith(RPCError, 'Internal error'); + }); +}); diff --git a/packages/dapi/test/unit/transactionsFilter/TransactionHashesCache.spec.js b/packages/dapi/test/unit/transactionsFilter/TransactionHashesCache.spec.js new file mode 100644 index 00000000000..29aa88ed8ea --- /dev/null +++ b/packages/dapi/test/unit/transactionsFilter/TransactionHashesCache.spec.js @@ -0,0 +1,283 @@ +const { MerkleBlock } = require('@dashevo/dashcore-lib'); + +const sinon = require('sinon'); +const { expect } = require('chai'); + +const TransactionHashesCache = require('../../../lib/transactionsFilter/TransactionHashesCache'); + +describe('TransactionHashesCache', () => { + let transactions; + let blocks; + let merkleBlocks; + let transactionHashesCache; + + beforeEach(() => { + transactions = [ + { hash: '000000000000000000000000000000000000000000000000000000000000001b' }, + { hash: '000000000000000000000000000000000000000000000000000000000000002b' }, + { hash: '000000000000000000000000000000000000000000000000000000000000003b' }, + { hash: '000000000000000000000000000000000000000000000000000000000000004b' }, + { hash: '000000000000000000000000000000000000000000000000000000000000005b' }, + { hash: '000000000000000000000000000000000000000000000000000000000000006b' }, + ]; + + blocks = [ + { + hash: '000000000000000000000000000000000000000000000000000000000000001b', + transactions: [transactions[0], transactions[1]], + header: { + hash: '000000000000000000000000000000000000000000000000000000000000001b', + }, + }, + { + hash: '000000000000000000000000000000000000000000000000000000000000002b', + transactions: [transactions[2], transactions[3]], + header: { + hash: '000000000000000000000000000000000000000000000000000000000000002b', + }, + }, + { + hash: '000000000000000000000000000000000000000000000000000000000000003b', + transactions: [transactions[4], transactions[5]], + header: { + hash: '000000000000000000000000000000000000000000000000000000000000003b', + }, + }, + + { + hash: '000000000000000000000000000000000000000000000000000000000000004b', + transactions: [transactions[0], transactions[1]], + header: { + hash: '000000000000000000000000000000000000000000000000000000000000004b', + }, + }, + ]; + + merkleBlocks = blocks.map((block) => ({ header: { hash: block.hash } })); + + sinon.stub(MerkleBlock, 'build'); + + blocks.forEach((block, index) => { + MerkleBlock.build + .withArgs(block.header, sinon.match.any, sinon.match.any) + .returns(merkleBlocks[index]); + }); + + transactionHashesCache = new TransactionHashesCache(); + }); + + afterEach(() => { + MerkleBlock.build.restore(); + }); + + describe('#addTransaction', () => { + it('should add transaction', () => { + const [firstTx] = transactions; + + transactionHashesCache.addTransaction(firstTx); + + expect(transactionHashesCache.transactions).to.deep.equal([ + { + transaction: firstTx, + isRetrieved: false, + }, + ]); + }); + + it('should add transaction to instant send waiting list', () => { + const [firstTx] = transactions; + + transactionHashesCache.addTransaction(firstTx); + + expect(transactionHashesCache.blocksProcessed).to.be.equal(0); + expect(transactionHashesCache.transactionHashesMap).to.deep.equal({ + [firstTx.hash]: 0, + }); + }); + }); + + describe('#addBlock', () => { + it('should add a block if it has matched transactions', () => { + const [block] = blocks; + + transactionHashesCache.addTransaction(transactions[0]); + transactionHashesCache.addTransaction(transactions[1]); + + transactionHashesCache.addBlock(block); + + expect(transactionHashesCache.blocks).to.deep.equal( + [block], + ); + }); + + it('should remove data if cache size is reached', () => { + transactionHashesCache.cacheSize = 2; + + transactionHashesCache.addTransaction(transactions[0]); + transactionHashesCache.addTransaction(transactions[1]); + transactionHashesCache.addTransaction(transactions[2]); + transactionHashesCache.addTransaction(transactions[3]); + transactionHashesCache.addTransaction(transactions[4]); + transactionHashesCache.addTransaction(transactions[5]); + + transactionHashesCache.addBlock(blocks[0]); + transactionHashesCache.addBlock(blocks[1]); + transactionHashesCache.addBlock(blocks[2]); + + expect(transactionHashesCache.transactions).to.deep.equal([ + { transaction: transactions[2], isRetrieved: false }, + { transaction: transactions[3], isRetrieved: false }, + { transaction: transactions[4], isRetrieved: false }, + { transaction: transactions[5], isRetrieved: false }, + ]); + + expect(transactionHashesCache.merkleBlocks).to.deep.equal([ + { merkleBlock: merkleBlocks[1], isRetrieved: false }, + { merkleBlock: merkleBlocks[2], isRetrieved: false }, + ]); + + expect(transactionHashesCache.blocks).to.deep.equal( + [blocks[1], blocks[2]], + ); + }); + + it('should increment blocks count on every transaction in the cache', () => { + const [firstTx, secondTx] = transactions; + + transactionHashesCache.addTransaction(firstTx); + + expect(transactionHashesCache.blocksProcessed).to.be.equal(0); + expect(transactionHashesCache.transactionHashesMap).to.deep.equal( + { + [firstTx.hash]: 0, + }, + ); + + transactionHashesCache.addBlock(blocks[0]); + transactionHashesCache.addTransaction(secondTx); + transactionHashesCache.addBlock(blocks[1]); + + expect(transactionHashesCache.blocksProcessed).to.be.equal(2); + expect(transactionHashesCache.transactionHashesMap).to.deep.equal({ + [firstTx.hash]: 0, + [secondTx.hash]: 1, + }); + }); + }); + + describe('#getBlockCount', () => { + it('should return block count', () => { + transactionHashesCache.addTransaction(transactions[0]); + transactionHashesCache.addTransaction(transactions[1]); + transactionHashesCache.addTransaction(transactions[2]); + transactionHashesCache.addTransaction(transactions[3]); + + transactionHashesCache.addBlock(blocks[0]); + transactionHashesCache.addBlock(blocks[1]); + + expect(transactionHashesCache.getBlockCount()).to.equal(2); + }); + }); + + describe('#removeDataByBlockHash', () => { + it('should remove data by block hash', () => { + transactionHashesCache.addTransaction(transactions[0]); + transactionHashesCache.addTransaction(transactions[1]); + transactionHashesCache.addTransaction(transactions[2]); + transactionHashesCache.addTransaction(transactions[3]); + transactionHashesCache.addTransaction(transactions[4]); + transactionHashesCache.addTransaction(transactions[5]); + + transactionHashesCache.addBlock(blocks[0]); + transactionHashesCache.addBlock(blocks[1]); + + transactionHashesCache.removeDataByBlockHash(blocks[0].hash); + + expect(transactionHashesCache.transactions).to.deep.equal([ + { transaction: transactions[2], isRetrieved: false }, + { transaction: transactions[3], isRetrieved: false }, + { transaction: transactions[4], isRetrieved: false }, + { transaction: transactions[5], isRetrieved: false }, + ]); + + expect(transactionHashesCache.merkleBlocks).to.deep.equal([ + { merkleBlock: merkleBlocks[1], isRetrieved: false }, + ]); + + expect(transactionHashesCache.blocks).to.deep.equal( + [blocks[1]], + ); + }); + }); + + describe('#getUnretrievedTrasactions', () => { + it('should return unsent transactions and mark them as sent', () => { + transactionHashesCache.addTransaction(transactions[0]); + transactionHashesCache.addTransaction(transactions[1]); + + expect(transactionHashesCache.getUnretrievedTransactions()).to.deep.equal([ + transactions[0], + transactions[1], + ]); + + expect(transactionHashesCache.getUnretrievedTransactions()).to.deep.equal([]); + }); + }); + + describe('#getUnretrievedMerkleBlocks', () => { + it('should return unsent merkle blocks and mark them as sent', () => { + transactionHashesCache.addTransaction(transactions[0]); + transactionHashesCache.addTransaction(transactions[1]); + transactionHashesCache.addTransaction(transactions[2]); + transactionHashesCache.addTransaction(transactions[3]); + + transactionHashesCache.addBlock(blocks[0]); + transactionHashesCache.addBlock(blocks[1]); + + expect(transactionHashesCache.getUnretrievedMerkleBlocks()).to.deep.equal([ + merkleBlocks[0], + merkleBlocks[1], + ]); + + expect(transactionHashesCache.getUnretrievedMerkleBlocks()).to.deep.equal([]); + }); + }); + + describe('#isInInstantLockCache', () => { + it('should return true if the transaction in the cache', () => { + const [firstTx] = transactions; + + transactionHashesCache.addTransaction(firstTx); + + expect(transactionHashesCache.isInInstantLockCache(firstTx.hash)).to.be.true(); + }); + + it('should return false if transaction is not in cache', () => { + const [firstTx, secondTx] = transactions; + + transactionHashesCache.addTransaction(firstTx); + + expect(transactionHashesCache.isInInstantLockCache(secondTx.hash)).to.be.false(); + }); + }); + + describe('#removeTransactionHashFromInstantSendLockWaitingList', () => { + it('should remove transaction from a waiting list', () => { + const [firstTx, secondTx] = transactions; + + transactionHashesCache.addTransaction(firstTx); + transactionHashesCache.addTransaction(secondTx); + + expect(transactionHashesCache.transactionHashesMap).to.be.deep.equal({ + [firstTx.hash]: 0, + [secondTx.hash]: 0, + }); + + transactionHashesCache.removeTransactionHashFromInstantSendLockWaitingList(firstTx.hash); + + expect(transactionHashesCache.transactionHashesMap).to.be.deep.equal({ + [secondTx.hash]: 0, + }); + }); + }); +}); diff --git a/packages/dapi/test/unit/transactionsFilter/getHistoricalBlockHeadersIteratorFactory.spec.js b/packages/dapi/test/unit/transactionsFilter/getHistoricalBlockHeadersIteratorFactory.spec.js new file mode 100644 index 00000000000..dc3df284377 --- /dev/null +++ b/packages/dapi/test/unit/transactionsFilter/getHistoricalBlockHeadersIteratorFactory.spec.js @@ -0,0 +1,65 @@ +const { BlockHeader } = require('@dashevo/dashcore-lib'); + +const getHistoricalBlockHeadersIteratorFactory = require('../../../lib/grpcServer/handlers/blockheaders-stream/getHistoricalBlockHeadersIteratorFactory'); + +describe('getHistoricalBlockHeadersIteratorFactory', () => { + let coreRpcMock; + let blockHeaderMock; + let chainDataProvider; + + beforeEach(function beforeEach() { + coreRpcMock = { + getBlock: this.sinon.stub(), + getBlockStats: this.sinon.stub(), + getBlockHash: this.sinon.stub(), + getBlockHeaders: this.sinon.stub(), + }; + + chainDataProvider = { + getBlockHeaders: this.sinon.stub(), + getBlockHash: this.sinon.stub(), + }; + + blockHeaderMock = new BlockHeader({ + version: 536870913, + prevHash: '0000000000000000000000000000000000000000000000000000000000000000', + merkleRoot: 'c4970326400177ce67ec582425a698b85ae03cae2b0d168e87eed697f1388e4b', + time: 1507208925, + timestamp: 1507208645, + bits: 0, + nonce: 1449878271, + }); + + chainDataProvider.getBlockHeaders.resetHistory(); + }); + + it('should proceed straight to done if all ranges are empty', async () => { + coreRpcMock.getBlockStats.resolves({ height: 1 }); + coreRpcMock.getBlockHeaders.resolves([blockHeaderMock.toBuffer().toString('hex')]); + + const fromBlockHeight = 1; + const count = 1337; + + const getHistoricalBlockHeadersIterator = getHistoricalBlockHeadersIteratorFactory( + chainDataProvider, + ); + + const blockHeadersIterator = getHistoricalBlockHeadersIterator( + fromBlockHeight, + count, + ); + + const r1 = await blockHeadersIterator.next(); + const r2 = await blockHeadersIterator.next(); + const r3 = await blockHeadersIterator.next(); + const r4 = await blockHeadersIterator.next(); + + expect(r1.done).to.be.false(); + expect(r2.done).to.be.false(); + expect(r3.done).to.be.false(); + expect(r4.done).to.be.true(); + + expect(chainDataProvider.getBlockHash.callCount).to.be.equal(3); + expect(chainDataProvider.getBlockHeaders.callCount).to.be.equal(3); + }); +}); diff --git a/packages/dapi/test/unit/transactionsFilter/getHistoricalTransactionsIteratorFactory.spec.js b/packages/dapi/test/unit/transactionsFilter/getHistoricalTransactionsIteratorFactory.spec.js new file mode 100644 index 00000000000..98f92acf374 --- /dev/null +++ b/packages/dapi/test/unit/transactionsFilter/getHistoricalTransactionsIteratorFactory.spec.js @@ -0,0 +1,301 @@ +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const chaiAsPromised = require('chai-as-promised'); +const sinon = require('sinon'); +const { MerkleBlock, Transaction, BloomFilter } = require('@dashevo/dashcore-lib'); + +const getHistoricalTransactionsIteratorFactory = require('../../../lib/transactionsFilter/getHistoricalTransactionsIteratorFactory'); + +const { expect } = chai; +chai.use(dirtyChai); +chai.use(chaiAsPromised); + +describe('getHistoricalTransactionsIteratorFactory', () => { + let mockData; + let rawMerkleBlock; + let coreRpcMock; + let bloomFilter; + + beforeEach(() => { + rawMerkleBlock = '03000000' // Version + + '35ce79ae46a65f0d0115d831584d0a6882117f75a65386f8f14e150000000000' // prevHash + + 'a0055d45ad9b35e77fb01c59a4feb9976921493d2557a5ac0798b49e82ea1e99' // MerkleRoot + + '6a04a055' // Time + + 'c380181b' // Bits + + '00270c9b' // Nonce + + '0c000000' // Transaction Count + + '08' // Hash Count + + '9d0a368bc9923c6cb966135a4ceda30cc5f259f72c8843ce015056375f8a06ec' // Hash1 + + '39e5cd533567ac0a8602bcc4c29e2f01a4abb0fe68ffbc7be6c393db188b72e0' // Hash2 + + 'cd75b421157eca03eff664bdc165730f91ef2fa52df19ff415ab5acb30045425' // Hash3 + + '2ef9795147caaeecee5bc2520704bb372cde06dbd2e871750f31336fd3f02be3' // Hash4 + + '2241d3448560f8b1d3a07ea5c31e79eb595632984a20f50944809a61fdd9fe0b' // Hash5 + + '45afbfe270014d5593cb065562f1fed726f767fe334d8b3f4379025cfa5be8c5' // Hash6 + + '198c03da0ccf871db91fe436e2795908eac5cc7d164232182e9445f7f9db1ab2' // Hash7 + + 'ed07c181ce5ba7cb66d205bc970f43e1ca11996d611aa8e91e305eb8608c543c' // Hash8 + + '02' // Num Flag Bytes + + 'db3f'; + mockData = { + blocks: [{ + hash: '45afbfe270014d5593cb065562f1fed726f767fe334d8b3f4379025cfa5be8c5', + height: 2002, + }, + { + hash: '000000000000000000000000000000000000000000000000000000000000001c', + height: 2582, + }, + { + hash: 'ed07c181ce5ba7cb66d205bc970f43e1ca11996d611aa8e91e305eb8608c543c', + height: 4002, + }, + { + hash: '9d0a368bc9923c6cb966135a4ceda30cc5f259f72c8843ce015056375f8a06ec', + height: 6002, + }, + { + hash: '198c03da0ccf871db91fe436e2795908eac5cc7d164232182e9445f7f9db1ab2', + height: 6125, + }, + // { + // hash: 'ed07c181ce5ba7cb66d205bc970f43e1ca11996d611aa8e91e305eb8608c543c', + // height: 8125, + // } + ], + transactions: [], + }; + coreRpcMock = { + getMerkleBlocks: sinon.stub(), + getRawTransaction: sinon.stub(), + getBlock: sinon.stub(), + getBlockStats: sinon.stub(), + getBlockHash: sinon.stub(), + }; + coreRpcMock.getMerkleBlocks + .withArgs() + .resolves([rawMerkleBlock]); + + coreRpcMock.getRawTransaction + .withArgs('cd75b421157eca03eff664bdc165730f91ef2fa52df19ff415ab5acb30045425') + .resolves('020000000a1b8972f91733804588910d466c0e77cfa65afad2a025a106a82ec6e32d617760050000006b483045022100a576b4e5bf5db550c95ae3d4c56773cdf3e7c632b0c52b6daf70fbcc37a405b502201e091da13954d861040ce382b51c50384ed12095308e0b5d92892361f7e94379812103d57433a7d82e48cd246ef29b9098b24dcf7eebc565586ab26e2bafe5b4d25750ffffffff0df9aeac7e6feeac4a935df9a0220dc498a0fc95a798bcb63a5ab58e5e8e5b6e0d0000006b4830450221008409d69f155c250eb0b4e6a0cf6dca3eb4910c3fd00c1ff245b4c9cc5f319a6b02206985a6d417e707d2d64a19225882c090e4527ec216e219f5ebea8e24bb7278038121036ac21c340c9041eb634f05ab5b6523514bc24784f132b945eef9fd148d616a2dffffffff00b4819721a0e85781c32d65d0f19b3884d7b8d3fe4f0ea3f9c1ea99087e297d010000006a47304402203cc336d6e6dd9a4986eea95dc9b5c7059949503ab227adc7425a6815ab4b9a0102202325b9d275bd4d7ed533d7974f73efae7ad27227f785ec95741e246444e82f978121033c5d60a3ecb98a3cb2214da030e8c28ecaae4663a7f389988c03b123ac0520cbffffffff85d4b234e8da90562da99f35e32cfc0ac93b8d8f041a6701c5e2e82619a9288b0e0000006b483045022100b0c50f44d12831d0f81ce28f498fbd51870b358dacafdc1764f09b021d2c72dd0220511845b6202274950a6a55c4023745f40991463e82a96851f1caec00b5a8a08c812102274dcc3b3e116ad930197f445554c0f3a7514e754c2672555fba335ec69cc5a3ffffffff8c46e46c9a44c4e3998caae7ff9fed881d4e91093fceb61fc33ddc0d5a1b919f040000006a47304402204dcb8938cdf74e80a87c69fa0da61988a12d73ed3495360d5a2d15a441f5e441022033f6b881ad83b10c6affb86a7d562ad421308693e457826e47fa25e57e51f249812103353ec1efb7ebe7aac6fa8431370613bc96d1b1b6cfef8706d7c3e10191fb947fffffffffc9a1d3293ec8604027ee6e1deec58924efacc896009c8b85003b6269553cf5b1010000006a473044022038af9647a90b082d49fa8471dd14a1955b5f1a8984d50dfe08b960b5c8206d440220076d9cf76adf5f86bc8dac5e84c6c14dc6f4a8605ad4832bd305fdc55b8f871b81210294a545f4bf055ee5e4b52bc98b40273ebe7ce172f70d75e48cafcbaaa43d0168ffffffff9d964428902aeb56acfc268b27ab828dc356597ff96c7b789062a1b90cbdf8be020000006b483045022100dce2c3c2bf6ed6b3d2d6eb55d530881b37fca13d08812accd9ed79c76eed75ed02206b0806366fabe8f70c9b1e08f7359861aea480a9a2dd2e3b15145ef06d46d7ed812102faaf13ed5f32f439bfbc5a9d6b5fa392eaace8c596d6b608fd48e1e40b697c90fffffffffc17a363992de5501cb6969b9e835fb333494382990ab319b0343c9d38218abf030000006a473044022012c80bec0b961cf7f8286ac525492a27dc09de299b59e6c294088eb04bb4a1d0022001cc1a8e1bf796f8a4a1e9fd1098e3dbc6bf949be7b4b8424388e3c83792188e812103905c6ad7c04f4594d135d26d980a9c2e16b50946886731b51ed61a9bdd49900bffffffff1892cd6c3038df794331ce437f8060cc2398a3744ec0f7488d4816e43b714ad6050000006a473044022044f3946ba17f87bffcf3fd4c5cfc43c530b97d4a97e3a258372f2074029cf4e802204d22e1afac51ef05c706afc43693b75542f2a82ef89b5026189135e0b1b54f44812102a37654d73b59ab13140a5adfdb79698fe23f6f05c5bb881704b3de2435161e1cffffffff66d6108688b82371210a987b1e22e9aa69a17c98803cc2f4e20e9957552053ea050000006b483045022100d4c315756462c0be66c786617fa8054fb266b5f6c7214cbc7cb00a41869bd26c022001288863647ece1a75718abd9bce8519b99a3e4509ef5554d8a92a633da525588121025ef06ce1ef91f2e535188d8c9ce1863f9ae6d37df8f143e6f558395f849fccb5ffffffff0ae4969800000000001976a91435b4e7ccf37e7933e2b9494f840b792b234e227388ace4969800000000001976a9144accca5ad3fa16538449b20ab51734dbdc9ef22288ace4969800000000001976a9145ac4de0775a027d0f0ec54e4c3d55fe57229fc4e88ace4969800000000001976a9147d224284b61bb5c3c0a8f32324052efd4781e28b88ace4969800000000001976a914900be7732a34e46d00dbaace243f65486448f8e388ace4969800000000001976a914a16dece64baadfec54d6ca0bf77f7380a66150c388ace4969800000000001976a914cc0822f608a13008b559d9536e69251f71e1096e88ace4969800000000001976a914f43dc02e4228e32df7c9d04fe3a59f69be05da6788ace4969800000000001976a914fe70994bce368c86578ae95845e1be7a908df06388ace4969800000000001976a914ff2f09b4f1e2f6e48b1e2a470ca1db3fe20baf6988ac00000000') + .withArgs('2ef9795147caaeecee5bc2520704bb372cde06dbd2e871750f31336fd3f02be3') + .resolves('020000000a1b8972f91733804588910d466c0e77cfa65afad2a025a106a82ec6e32d617760050000006b483045022100a576b4e5bf5db550c95ae3d4c56773cdf3e7c632b0c52b6daf70fbcc37a405b502201e091da13954d861040ce382b51c50384ed12095308e0b5d92892361f7e94379812103d57433a7d82e48cd246ef29b9098b24dcf7eebc565586ab26e2bafe5b4d25750ffffffff0df9aeac7e6feeac4a935df9a0220dc498a0fc95a798bcb63a5ab58e5e8e5b6e0d0000006b4830450221008409d69f155c250eb0b4e6a0cf6dca3eb4910c3fd00c1ff245b4c9cc5f319a6b02206985a6d417e707d2d64a19225882c090e4527ec216e219f5ebea8e24bb7278038121036ac21c340c9041eb634f05ab5b6523514bc24784f132b945eef9fd148d616a2dffffffff00b4819721a0e85781c32d65d0f19b3884d7b8d3fe4f0ea3f9c1ea99087e297d010000006a47304402203cc336d6e6dd9a4986eea95dc9b5c7059949503ab227adc7425a6815ab4b9a0102202325b9d275bd4d7ed533d7974f73efae7ad27227f785ec95741e246444e82f978121033c5d60a3ecb98a3cb2214da030e8c28ecaae4663a7f389988c03b123ac0520cbffffffff85d4b234e8da90562da99f35e32cfc0ac93b8d8f041a6701c5e2e82619a9288b0e0000006b483045022100b0c50f44d12831d0f81ce28f498fbd51870b358dacafdc1764f09b021d2c72dd0220511845b6202274950a6a55c4023745f40991463e82a96851f1caec00b5a8a08c812102274dcc3b3e116ad930197f445554c0f3a7514e754c2672555fba335ec69cc5a3ffffffff8c46e46c9a44c4e3998caae7ff9fed881d4e91093fceb61fc33ddc0d5a1b919f040000006a47304402204dcb8938cdf74e80a87c69fa0da61988a12d73ed3495360d5a2d15a441f5e441022033f6b881ad83b10c6affb86a7d562ad421308693e457826e47fa25e57e51f249812103353ec1efb7ebe7aac6fa8431370613bc96d1b1b6cfef8706d7c3e10191fb947fffffffffc9a1d3293ec8604027ee6e1deec58924efacc896009c8b85003b6269553cf5b1010000006a473044022038af9647a90b082d49fa8471dd14a1955b5f1a8984d50dfe08b960b5c8206d440220076d9cf76adf5f86bc8dac5e84c6c14dc6f4a8605ad4832bd305fdc55b8f871b81210294a545f4bf055ee5e4b52bc98b40273ebe7ce172f70d75e48cafcbaaa43d0168ffffffff9d964428902aeb56acfc268b27ab828dc356597ff96c7b789062a1b90cbdf8be020000006b483045022100dce2c3c2bf6ed6b3d2d6eb55d530881b37fca13d08812accd9ed79c76eed75ed02206b0806366fabe8f70c9b1e08f7359861aea480a9a2dd2e3b15145ef06d46d7ed812102faaf13ed5f32f439bfbc5a9d6b5fa392eaace8c596d6b608fd48e1e40b697c90fffffffffc17a363992de5501cb6969b9e835fb333494382990ab319b0343c9d38218abf030000006a473044022012c80bec0b961cf7f8286ac525492a27dc09de299b59e6c294088eb04bb4a1d0022001cc1a8e1bf796f8a4a1e9fd1098e3dbc6bf949be7b4b8424388e3c83792188e812103905c6ad7c04f4594d135d26d980a9c2e16b50946886731b51ed61a9bdd49900bffffffff1892cd6c3038df794331ce437f8060cc2398a3744ec0f7488d4816e43b714ad6050000006a473044022044f3946ba17f87bffcf3fd4c5cfc43c530b97d4a97e3a258372f2074029cf4e802204d22e1afac51ef05c706afc43693b75542f2a82ef89b5026189135e0b1b54f44812102a37654d73b59ab13140a5adfdb79698fe23f6f05c5bb881704b3de2435161e1cffffffff66d6108688b82371210a987b1e22e9aa69a17c98803cc2f4e20e9957552053ea050000006b483045022100d4c315756462c0be66c786617fa8054fb266b5f6c7214cbc7cb00a41869bd26c022001288863647ece1a75718abd9bce8519b99a3e4509ef5554d8a92a633da525588121025ef06ce1ef91f2e535188d8c9ce1863f9ae6d37df8f143e6f558395f849fccb5ffffffff0ae4969800000000001976a91435b4e7ccf37e7933e2b9494f840b792b234e227388ace4969800000000001976a9144accca5ad3fa16538449b20ab51734dbdc9ef22288ace4969800000000001976a9145ac4de0775a027d0f0ec54e4c3d55fe57229fc4e88ace4969800000000001976a9147d224284b61bb5c3c0a8f32324052efd4781e28b88ace4969800000000001976a914900be7732a34e46d00dbaace243f65486448f8e388ace4969800000000001976a914a16dece64baadfec54d6ca0bf77f7380a66150c388ace4969800000000001976a914cc0822f608a13008b559d9536e69251f71e1096e88ace4969800000000001976a914f43dc02e4228e32df7c9d04fe3a59f69be05da6788ace4969800000000001976a914fe70994bce368c86578ae95845e1be7a908df06388ace4969800000000001976a914ff2f09b4f1e2f6e48b1e2a470ca1db3fe20baf6988ac00000000') + .withArgs('2241d3448560f8b1d3a07ea5c31e79eb595632984a20f50944809a61fdd9fe0b') + .resolves('020000000a1b8972f91733804588910d466c0e77cfa65afad2a025a106a82ec6e32d617760050000006b483045022100a576b4e5bf5db550c95ae3d4c56773cdf3e7c632b0c52b6daf70fbcc37a405b502201e091da13954d861040ce382b51c50384ed12095308e0b5d92892361f7e94379812103d57433a7d82e48cd246ef29b9098b24dcf7eebc565586ab26e2bafe5b4d25750ffffffff0df9aeac7e6feeac4a935df9a0220dc498a0fc95a798bcb63a5ab58e5e8e5b6e0d0000006b4830450221008409d69f155c250eb0b4e6a0cf6dca3eb4910c3fd00c1ff245b4c9cc5f319a6b02206985a6d417e707d2d64a19225882c090e4527ec216e219f5ebea8e24bb7278038121036ac21c340c9041eb634f05ab5b6523514bc24784f132b945eef9fd148d616a2dffffffff00b4819721a0e85781c32d65d0f19b3884d7b8d3fe4f0ea3f9c1ea99087e297d010000006a47304402203cc336d6e6dd9a4986eea95dc9b5c7059949503ab227adc7425a6815ab4b9a0102202325b9d275bd4d7ed533d7974f73efae7ad27227f785ec95741e246444e82f978121033c5d60a3ecb98a3cb2214da030e8c28ecaae4663a7f389988c03b123ac0520cbffffffff85d4b234e8da90562da99f35e32cfc0ac93b8d8f041a6701c5e2e82619a9288b0e0000006b483045022100b0c50f44d12831d0f81ce28f498fbd51870b358dacafdc1764f09b021d2c72dd0220511845b6202274950a6a55c4023745f40991463e82a96851f1caec00b5a8a08c812102274dcc3b3e116ad930197f445554c0f3a7514e754c2672555fba335ec69cc5a3ffffffff8c46e46c9a44c4e3998caae7ff9fed881d4e91093fceb61fc33ddc0d5a1b919f040000006a47304402204dcb8938cdf74e80a87c69fa0da61988a12d73ed3495360d5a2d15a441f5e441022033f6b881ad83b10c6affb86a7d562ad421308693e457826e47fa25e57e51f249812103353ec1efb7ebe7aac6fa8431370613bc96d1b1b6cfef8706d7c3e10191fb947fffffffffc9a1d3293ec8604027ee6e1deec58924efacc896009c8b85003b6269553cf5b1010000006a473044022038af9647a90b082d49fa8471dd14a1955b5f1a8984d50dfe08b960b5c8206d440220076d9cf76adf5f86bc8dac5e84c6c14dc6f4a8605ad4832bd305fdc55b8f871b81210294a545f4bf055ee5e4b52bc98b40273ebe7ce172f70d75e48cafcbaaa43d0168ffffffff9d964428902aeb56acfc268b27ab828dc356597ff96c7b789062a1b90cbdf8be020000006b483045022100dce2c3c2bf6ed6b3d2d6eb55d530881b37fca13d08812accd9ed79c76eed75ed02206b0806366fabe8f70c9b1e08f7359861aea480a9a2dd2e3b15145ef06d46d7ed812102faaf13ed5f32f439bfbc5a9d6b5fa392eaace8c596d6b608fd48e1e40b697c90fffffffffc17a363992de5501cb6969b9e835fb333494382990ab319b0343c9d38218abf030000006a473044022012c80bec0b961cf7f8286ac525492a27dc09de299b59e6c294088eb04bb4a1d0022001cc1a8e1bf796f8a4a1e9fd1098e3dbc6bf949be7b4b8424388e3c83792188e812103905c6ad7c04f4594d135d26d980a9c2e16b50946886731b51ed61a9bdd49900bffffffff1892cd6c3038df794331ce437f8060cc2398a3744ec0f7488d4816e43b714ad6050000006a473044022044f3946ba17f87bffcf3fd4c5cfc43c530b97d4a97e3a258372f2074029cf4e802204d22e1afac51ef05c706afc43693b75542f2a82ef89b5026189135e0b1b54f44812102a37654d73b59ab13140a5adfdb79698fe23f6f05c5bb881704b3de2435161e1cffffffff66d6108688b82371210a987b1e22e9aa69a17c98803cc2f4e20e9957552053ea050000006b483045022100d4c315756462c0be66c786617fa8054fb266b5f6c7214cbc7cb00a41869bd26c022001288863647ece1a75718abd9bce8519b99a3e4509ef5554d8a92a633da525588121025ef06ce1ef91f2e535188d8c9ce1863f9ae6d37df8f143e6f558395f849fccb5ffffffff0ae4969800000000001976a91435b4e7ccf37e7933e2b9494f840b792b234e227388ace4969800000000001976a9144accca5ad3fa16538449b20ab51734dbdc9ef22288ace4969800000000001976a9145ac4de0775a027d0f0ec54e4c3d55fe57229fc4e88ace4969800000000001976a9147d224284b61bb5c3c0a8f32324052efd4781e28b88ace4969800000000001976a914900be7732a34e46d00dbaace243f65486448f8e388ace4969800000000001976a914a16dece64baadfec54d6ca0bf77f7380a66150c388ace4969800000000001976a914cc0822f608a13008b559d9536e69251f71e1096e88ace4969800000000001976a914f43dc02e4228e32df7c9d04fe3a59f69be05da6788ace4969800000000001976a914fe70994bce368c86578ae95845e1be7a908df06388ace4969800000000001976a914ff2f09b4f1e2f6e48b1e2a470ca1db3fe20baf6988ac00000000') + .withArgs('45afbfe270014d5593cb065562f1fed726f767fe334d8b3f4379025cfa5be8c5') + .resolves('020000000a1b8972f91733804588910d466c0e77cfa65afad2a025a106a82ec6e32d617760050000006b483045022100a576b4e5bf5db550c95ae3d4c56773cdf3e7c632b0c52b6daf70fbcc37a405b502201e091da13954d861040ce382b51c50384ed12095308e0b5d92892361f7e94379812103d57433a7d82e48cd246ef29b9098b24dcf7eebc565586ab26e2bafe5b4d25750ffffffff0df9aeac7e6feeac4a935df9a0220dc498a0fc95a798bcb63a5ab58e5e8e5b6e0d0000006b4830450221008409d69f155c250eb0b4e6a0cf6dca3eb4910c3fd00c1ff245b4c9cc5f319a6b02206985a6d417e707d2d64a19225882c090e4527ec216e219f5ebea8e24bb7278038121036ac21c340c9041eb634f05ab5b6523514bc24784f132b945eef9fd148d616a2dffffffff00b4819721a0e85781c32d65d0f19b3884d7b8d3fe4f0ea3f9c1ea99087e297d010000006a47304402203cc336d6e6dd9a4986eea95dc9b5c7059949503ab227adc7425a6815ab4b9a0102202325b9d275bd4d7ed533d7974f73efae7ad27227f785ec95741e246444e82f978121033c5d60a3ecb98a3cb2214da030e8c28ecaae4663a7f389988c03b123ac0520cbffffffff85d4b234e8da90562da99f35e32cfc0ac93b8d8f041a6701c5e2e82619a9288b0e0000006b483045022100b0c50f44d12831d0f81ce28f498fbd51870b358dacafdc1764f09b021d2c72dd0220511845b6202274950a6a55c4023745f40991463e82a96851f1caec00b5a8a08c812102274dcc3b3e116ad930197f445554c0f3a7514e754c2672555fba335ec69cc5a3ffffffff8c46e46c9a44c4e3998caae7ff9fed881d4e91093fceb61fc33ddc0d5a1b919f040000006a47304402204dcb8938cdf74e80a87c69fa0da61988a12d73ed3495360d5a2d15a441f5e441022033f6b881ad83b10c6affb86a7d562ad421308693e457826e47fa25e57e51f249812103353ec1efb7ebe7aac6fa8431370613bc96d1b1b6cfef8706d7c3e10191fb947fffffffffc9a1d3293ec8604027ee6e1deec58924efacc896009c8b85003b6269553cf5b1010000006a473044022038af9647a90b082d49fa8471dd14a1955b5f1a8984d50dfe08b960b5c8206d440220076d9cf76adf5f86bc8dac5e84c6c14dc6f4a8605ad4832bd305fdc55b8f871b81210294a545f4bf055ee5e4b52bc98b40273ebe7ce172f70d75e48cafcbaaa43d0168ffffffff9d964428902aeb56acfc268b27ab828dc356597ff96c7b789062a1b90cbdf8be020000006b483045022100dce2c3c2bf6ed6b3d2d6eb55d530881b37fca13d08812accd9ed79c76eed75ed02206b0806366fabe8f70c9b1e08f7359861aea480a9a2dd2e3b15145ef06d46d7ed812102faaf13ed5f32f439bfbc5a9d6b5fa392eaace8c596d6b608fd48e1e40b697c90fffffffffc17a363992de5501cb6969b9e835fb333494382990ab319b0343c9d38218abf030000006a473044022012c80bec0b961cf7f8286ac525492a27dc09de299b59e6c294088eb04bb4a1d0022001cc1a8e1bf796f8a4a1e9fd1098e3dbc6bf949be7b4b8424388e3c83792188e812103905c6ad7c04f4594d135d26d980a9c2e16b50946886731b51ed61a9bdd49900bffffffff1892cd6c3038df794331ce437f8060cc2398a3744ec0f7488d4816e43b714ad6050000006a473044022044f3946ba17f87bffcf3fd4c5cfc43c530b97d4a97e3a258372f2074029cf4e802204d22e1afac51ef05c706afc43693b75542f2a82ef89b5026189135e0b1b54f44812102a37654d73b59ab13140a5adfdb79698fe23f6f05c5bb881704b3de2435161e1cffffffff66d6108688b82371210a987b1e22e9aa69a17c98803cc2f4e20e9957552053ea050000006b483045022100d4c315756462c0be66c786617fa8054fb266b5f6c7214cbc7cb00a41869bd26c022001288863647ece1a75718abd9bce8519b99a3e4509ef5554d8a92a633da525588121025ef06ce1ef91f2e535188d8c9ce1863f9ae6d37df8f143e6f558395f849fccb5ffffffff0ae4969800000000001976a91435b4e7ccf37e7933e2b9494f840b792b234e227388ace4969800000000001976a9144accca5ad3fa16538449b20ab51734dbdc9ef22288ace4969800000000001976a9145ac4de0775a027d0f0ec54e4c3d55fe57229fc4e88ace4969800000000001976a9147d224284b61bb5c3c0a8f32324052efd4781e28b88ace4969800000000001976a914900be7732a34e46d00dbaace243f65486448f8e388ace4969800000000001976a914a16dece64baadfec54d6ca0bf77f7380a66150c388ace4969800000000001976a914cc0822f608a13008b559d9536e69251f71e1096e88ace4969800000000001976a914f43dc02e4228e32df7c9d04fe3a59f69be05da6788ace4969800000000001976a914fe70994bce368c86578ae95845e1be7a908df06388ace4969800000000001976a914ff2f09b4f1e2f6e48b1e2a470ca1db3fe20baf6988ac00000000'); + + mockData.blocks.forEach((mockedBlockData) => { + coreRpcMock.getBlock.withArgs(mockedBlockData.hash).resolves(mockedBlockData); + coreRpcMock.getBlockStats.withArgs(mockedBlockData.hash, ['height']).resolves({ height: mockedBlockData.height }); + }); + + mockData.blocks.forEach((mockedBlockData) => { + coreRpcMock.getBlockHash.withArgs(mockedBlockData.height).resolves(mockedBlockData.hash); + }); + + bloomFilter = BloomFilter.create(1, 0.001); + }); + + it('count is lesser than max block headers', async () => { + const fetchHistoricalTransactions = getHistoricalTransactionsIteratorFactory(coreRpcMock); + const fromBlockHeight = mockData.blocks[0].height; + const count = 580; + + const merkleBlocksAndTransactions = fetchHistoricalTransactions( + bloomFilter, + fromBlockHeight, + count, + ); + + const { value: { merkleBlock, transactions } } = await merkleBlocksAndTransactions.next(); + + expect(coreRpcMock.getBlockHash.callCount).to.be.equal(1); + expect(coreRpcMock.getBlockHash.getCall(0).calledWith(mockData.blocks[0].height)).to.be.true(); + + expect(coreRpcMock.getMerkleBlocks.callCount).to.be.equal(1); + expect( + coreRpcMock.getMerkleBlocks.getCall(0).calledWith( + bloomFilter.toBuffer().toString('hex'), mockData.blocks[0].hash, 580, + ), + ).to.be.true(); + + expect(merkleBlock).to.be.an.instanceof(MerkleBlock); + expect(transactions).to.be.an('array'); + transactions.forEach((rawTx) => { + expect(rawTx).to.be.an.instanceof(Transaction); + }); + + const { done } = await merkleBlocksAndTransactions.next(); + + expect(done).to.be.true(); + }); + + it('count is bigger than max block headers', async () => { + const fetchHistoricalTransactions = getHistoricalTransactionsIteratorFactory(coreRpcMock); + const fromBlockHeight = mockData.blocks[0].height; + const count = 4123; + + const merkleBlocksIterator = fetchHistoricalTransactions( + bloomFilter, + fromBlockHeight, + count, + ); + + const { value: { merkleBlock, transactions } } = await merkleBlocksIterator.next(); + + expect(coreRpcMock.getBlockHash.callCount).to.be.equal(1); + expect(coreRpcMock.getBlockHash.getCall(0).calledWith(mockData.blocks[0].height)).to.be.true(); + + expect(coreRpcMock.getMerkleBlocks.callCount).to.be.equal(1); + expect( + coreRpcMock.getMerkleBlocks.getCall(0).calledWith( + bloomFilter.toBuffer().toString('hex'), mockData.blocks[0].hash, 2000, + ), + ).to.be.true(); + + expect(merkleBlock).to.be.an.instanceof(MerkleBlock); + expect(transactions).to.be.an('array'); + transactions.forEach((rawTx) => { + expect(rawTx).to.be.an.instanceof(Transaction); + }); + + await merkleBlocksIterator.next(); + + expect(coreRpcMock.getBlockHash.callCount).to.be.equal(2); + expect(coreRpcMock.getBlockHash.getCall(1).calledWith(mockData.blocks[2].height)).to.be.true(); + + expect(coreRpcMock.getMerkleBlocks.callCount).to.be.equal(2); + expect( + coreRpcMock.getMerkleBlocks.getCall(1).calledWith( + bloomFilter.toBuffer().toString('hex'), mockData.blocks[2].hash, 2000, + ), + ).to.be.true(); + + await merkleBlocksIterator.next(); + + expect(coreRpcMock.getBlockHash.callCount).to.be.equal(3); + expect(coreRpcMock.getBlockHash.getCall(2).calledWith(mockData.blocks[3].height)).to.be.true(); + + expect(coreRpcMock.getMerkleBlocks.callCount).to.be.equal(3); + expect( + coreRpcMock.getMerkleBlocks.getCall(2).calledWith( + bloomFilter.toBuffer().toString('hex'), mockData.blocks[3].hash, 123, + ), + ).to.be.true(); + + const { done } = await merkleBlocksIterator.next(); + + expect(done).to.be.true(); + }); + + it('should return one merkle block at a time, even if there is more than two blocks found in a range', async () => { + coreRpcMock.getMerkleBlocks + .withArgs() + .resolves([rawMerkleBlock, rawMerkleBlock]); + + const fetchHistoricalTransactions = getHistoricalTransactionsIteratorFactory(coreRpcMock); + const fromBlockHeight = mockData.blocks[0].height; + const count = 580; + + const merkleBlocksAndTransactions = fetchHistoricalTransactions( + bloomFilter, + fromBlockHeight, + count, + ); + + const { value: { merkleBlock, transactions } } = await merkleBlocksAndTransactions.next(); + + expect(coreRpcMock.getBlockHash.callCount).to.be.equal(1); + expect(coreRpcMock.getBlockHash.getCall(0).calledWith(mockData.blocks[0].height)).to.be.true(); + expect(coreRpcMock.getMerkleBlocks.callCount).to.be.equal(1); + expect( + coreRpcMock.getMerkleBlocks.getCall(0).calledWith( + bloomFilter.toBuffer().toString('hex'), mockData.blocks[0].hash, 580, + ), + ).to.be.true(); + + expect(merkleBlock).to.be.an.instanceof(MerkleBlock); + expect(transactions).to.be.an('array'); + transactions.forEach((rawTx) => { + expect(rawTx).to.be.an.instanceof(Transaction); + }); + + const { + value: { + merkleBlock: secondMerkleBlock, + transactions: secondSetOfTransactions, + }, + } = await merkleBlocksAndTransactions.next(); + + expect(secondMerkleBlock).to.be.an.instanceof(MerkleBlock); + expect(secondSetOfTransactions).to.be.an('array'); + secondSetOfTransactions.forEach((rawTx) => { + expect(rawTx).to.be.an.instanceof(Transaction); + }); + + expect(coreRpcMock.getBlockHash.callCount).to.be.equal(1); + expect(coreRpcMock.getMerkleBlocks.callCount).to.be.equal(1); + + const { done } = await merkleBlocksAndTransactions.next(); + + expect(done).to.be.true(); + }); + + it('should skip interval with no merkle blocks', async () => { + coreRpcMock.getMerkleBlocks + .withArgs(bloomFilter, mockData.blocks[2].hash, 2000) + .resolves([]); + + const fromBlockHeight = mockData.blocks[0].height; + const count = 4123; + + const fetchHistoricalTransactions = getHistoricalTransactionsIteratorFactory(coreRpcMock); + + const merkleBlocksIterator = fetchHistoricalTransactions( + bloomFilter, + fromBlockHeight, + count, + ); + + await merkleBlocksIterator.next(); + + expect(coreRpcMock.getBlockHash.callCount).to.be.equal(1); + expect(coreRpcMock.getMerkleBlocks.callCount).to.be.equal(1); + + await merkleBlocksIterator.next(); + + expect(coreRpcMock.getBlockHash.callCount).to.be.equal(2); + expect(coreRpcMock.getMerkleBlocks.getCall(1) + .calledWith(bloomFilter.toBuffer().toString('hex'), mockData.blocks[2].hash, 2000)).to.be.true(); + + await merkleBlocksIterator.next(); + + // As there will be one interval (4002-6002) with no merkle blocks, + // all call count should increase by 2 + expect(coreRpcMock.getBlockHash.callCount).to.be.equal(3); + expect(coreRpcMock.getMerkleBlocks.callCount).to.be.equal(3); + }); + + it('should proceed straight to done if all ranges are empty', async () => { + coreRpcMock.getMerkleBlocks + .withArgs() + .resolves([]); + + const fromBlockHeight = mockData.blocks[0].height; + const count = 4123; + + const fetchHistoricalTransactions = getHistoricalTransactionsIteratorFactory(coreRpcMock); + + const merkleBlocksIterator = fetchHistoricalTransactions( + bloomFilter, + fromBlockHeight, + count, + ); + + const { done } = await merkleBlocksIterator.next(); + + expect(coreRpcMock.getBlockHash.callCount).to.be.equal(3); + expect(coreRpcMock.getMerkleBlocks.callCount).to.be.equal(3); + expect(done).to.be.true(); + }); +}); diff --git a/packages/dapi/test/unit/transactionsFilter/testTransactionAgainstFilter.spec.js b/packages/dapi/test/unit/transactionsFilter/testTransactionAgainstFilter.spec.js new file mode 100644 index 00000000000..1a6b020ed48 --- /dev/null +++ b/packages/dapi/test/unit/transactionsFilter/testTransactionAgainstFilter.spec.js @@ -0,0 +1,323 @@ +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const { + Transaction, + PrivateKey, + Script, + Address, + BloomFilter, +} = require('@dashevo/dashcore-lib'); + +const { Output, Input } = Transaction; +const { expect } = chai; + +chai.use(dirtyChai); + +const testTransactionAgainstFilter = require('../../../lib/transactionsFilter/testTransactionAgainstFilter'); + +describe('testTransactionAgainstFilter', () => { + it('should match on address in output', () => { + const filter = BloomFilter.create(1, 0.0001); + const address = new PrivateKey().toAddress(); + const tx = new Transaction().to(address, 10); + filter.insert(address.hashBuffer); + + const result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + }); + + it('should not match on address if there is no such output in transaction', () => { + const filter = BloomFilter.create(1, 0.0001); + const addressInFilter = new PrivateKey().toAddress(); + const addressInTransaction = new PrivateKey().toAddress(); + const tx = new Transaction().to(addressInTransaction, 10); + filter.insert(addressInFilter.hashBuffer); + + const result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.false(); + }); + + it('should match when input script contains desired data', () => { + const filter = BloomFilter.create(1, 0.0001); + const address = new PrivateKey().toAddress(); + const tx = new Transaction().to(address, 10); + + filter.insert(address.hashBuffer); + + const vout = 0; + const input = new Input({ + prevTxId: tx.id, + output: tx.outputs[vout], + outputIndex: vout, + script: Script.buildPublicKeyHashOut(address), + }); + + const txWIthInput = new Transaction().addInput(input); + + const result = testTransactionAgainstFilter(filter, txWIthInput); + expect(result).to.be.true(); + }); + + it("should not match when input script doesn't contain desired data", () => { + const filter = BloomFilter.create(1, 0.0001); + const addressInFilter = new PrivateKey().toAddress(); + const addressInTransaction = new PrivateKey().toAddress(); + const tx = new Transaction().to(addressInTransaction, 10); + + filter.insert(addressInFilter.hashBuffer); + + const vout = 0; + const input = new Input({ + prevTxId: tx.id, + output: tx.outputs[vout], + outputIndex: vout, + script: Script.buildPublicKeyHashOut(addressInTransaction), + }); + + const txWIthInput = new Transaction().addInput(input); + + const result = testTransactionAgainstFilter(filter, txWIthInput); + expect(result).to.be.false(); + }); + + it('should add outpoint to the filter if BLOOM_UPDATE_ALL flag is set in the filter' + + ' and match transaction with that outpoint in input', () => { + const filter = BloomFilter.create(1, 0.0001); + const address = new PrivateKey().toAddress(); + const tx = new Transaction().to(address, 10); + + filter.nFlags = BloomFilter.BLOOM_UPDATE_ALL; + filter.insert(address.hashBuffer); + + let result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + + const vout = 0; + const txWIthInput = new Transaction().from({ + txid: tx.id, + vout, + script: tx.outputs[vout].script, + satoshis: tx.outputs[vout].satoshis, + }); + + result = testTransactionAgainstFilter(filter, txWIthInput); + expect(result).to.be.true(); + }); + + it('should not add outpoint to the filter if BLOOM_UPDATE_NONE flag is' + + ' set in the filter', () => { + const filter = BloomFilter.create(1, 0.0001); + const address = new PrivateKey().toAddress(); + const tx = new Transaction().to(address, 10); + + filter.nFlags = BloomFilter.BLOOM_UPDATE_NONE; + filter.insert(address.hashBuffer); + + let result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + + const vout = 0; + const txWIthInput = new Transaction().from({ + txid: tx.id, + vout, + script: tx.outputs[vout].script, + satoshis: tx.outputs[vout].satoshis, + }); + + result = testTransactionAgainstFilter(filter, txWIthInput); + expect(result).to.be.false(); + }); + + it('should add outpoint to the filter if BLOOM_UPDATE_P2PUBKEY_ONLY,' + + ' and output is pub key out', () => { + const filter = BloomFilter.create(1, 0.0001); + const pubKey = new PrivateKey().toPublicKey(); + const output = new Output({ + satoshis: 10, + script: Script.buildPublicKeyOut(pubKey), + }); + const tx = new Transaction().addOutput(output); + + filter.nFlags = BloomFilter.BLOOM_UPDATE_P2PUBKEY_ONLY; + filter.insert(pubKey.toBuffer()); + + let result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + + const vout = 0; + const txWIthInput = new Transaction().from({ + txid: tx.id, + vout, + script: tx.outputs[vout].script, + satoshis: tx.outputs[vout].satoshis, + }); + + result = testTransactionAgainstFilter(filter, txWIthInput); + expect(result).to.be.true(); + }); + + it('should not add outpoint to the filter if BLOOM_UPDATE_P2PUBKEY_ONLY,' + + ' and output is to pub key hash', () => { + const filter = BloomFilter.create(1, 0.0001); + const address = new PrivateKey().toAddress(); + const tx = new Transaction().to(address, 10); + + filter.nFlags = BloomFilter.BLOOM_UPDATE_P2PUBKEY_ONLY; + filter.insert(address.hashBuffer); + + let result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + + const vout = 0; + const txWIthInput = new Transaction().from({ + txid: tx.id, + vout, + script: tx.outputs[vout].script, + satoshis: tx.outputs[vout].satoshis, + }); + + result = testTransactionAgainstFilter(filter, txWIthInput); + expect(result).to.be.false(); + }); + + it('should add outpoint to the filter if BLOOM_UPDATE_P2PUBKEY_ONLY' + + ' is set and matched output is multisig', () => { + const filter = BloomFilter.create(3, 0.0001); + const pubKeys = [ + new PrivateKey().toPublicKey(), + new PrivateKey().toPublicKey(), + new PrivateKey().toPublicKey(), + ]; + const output = new Output({ + satoshis: 10, + script: Script.buildMultisigOut(pubKeys, 2), + }); + const tx = new Transaction().addOutput(output); + + filter.nFlags = BloomFilter.BLOOM_UPDATE_P2PUBKEY_ONLY; + pubKeys.forEach((pubKey) => filter.insert(pubKey.toBuffer())); + + let result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + + const vout = 0; + const txWIthInput = new Transaction().from({ + txid: tx.id, + vout, + script: tx.outputs[vout].script, + satoshis: tx.outputs[vout].satoshis, + }); + + result = testTransactionAgainstFilter(filter, txWIthInput); + expect(result).to.be.true(); + }); + + it('should not add outpoint to the filter if output is multisig and ' + + 'BLOOM_UPDATE_P2PUBKEY_ONLY flag is not set', () => { + const filter = BloomFilter.create(3, 0.0001); + const pubKeys = [ + new PrivateKey().toPublicKey(), + new PrivateKey().toPublicKey(), + new PrivateKey().toPublicKey(), + ]; + const output = new Output({ + satoshis: 10, + script: Script.buildMultisigOut(pubKeys, 2), + }); + const tx = new Transaction().addOutput(output); + + filter.nFlags = BloomFilter.BLOOM_UPDATE_NONE; + pubKeys.forEach((pubKey) => filter.insert(pubKey.toBuffer())); + + let result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + + const vout = 0; + const txWIthInput = new Transaction().from({ + txid: tx.id, + vout, + script: tx.outputs[vout].script, + satoshis: tx.outputs[vout].satoshis, + }); + + result = testTransactionAgainstFilter(filter, txWIthInput); + expect(result).to.be.false(); + }); + + it('should pass the same test vector as dashcore implementation does', () => { + const txHex = '01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339ffffffff021bff3d11000000001976a91404943fdd508053c75000106d3bc6e2754dbcff1988ac2f15de00000000001976a914a266436d2965547608b9e15d9032a7b9d64fa43188ac00000000'; + const txHash = 'b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b'; + const inputSignature = '30450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a01'; + const inputPubKey = '046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339'; + const outputAddress = '04943fdd508053c75000106d3bc6e2754dbcff19'; + const outputAddress2 = 'a266436d2965547608b9e15d9032a7b9d64fa431'; + const outputIndex = '90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b00000000'; + const randomHash = '00000009e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436'; + const randomAddress = '0000006d2965547608b9e15d9032a7b9d64fa431'; + const irrelevantOutputIndex = '90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b00000001'; + const irrelevantOutputIndex2 = '000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b00000000'; + + const tx = new Transaction(txHex); + + let filter = BloomFilter.create(1, 0.0001, 0, BloomFilter.BLOOM_UPDATE_ALL); + filter.insert(Buffer.from(txHash, 'hex')); + let result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + + filter = BloomFilter.create(1, 0.0001, 0, BloomFilter.BLOOM_UPDATE_ALL); + filter.insert(Buffer.from(inputSignature, 'hex')); + result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + + filter = BloomFilter.create(1, 0.0001, 0, BloomFilter.BLOOM_UPDATE_ALL); + filter.insert(Buffer.from(inputPubKey, 'hex')); + result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + + filter = BloomFilter.create(1, 0.0001, 0, BloomFilter.BLOOM_UPDATE_ALL); + filter.insert(Buffer.from(outputAddress, 'hex')); + result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + + filter = BloomFilter.create(1, 0.0001, 0, BloomFilter.BLOOM_UPDATE_ALL); + filter.insert(Buffer.from(outputAddress2, 'hex')); + result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + + filter = BloomFilter.create(1, 0.0001, 0, BloomFilter.BLOOM_UPDATE_ALL); + filter.insert(Buffer.from(outputIndex, 'hex')); + result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + + filter = BloomFilter.create(1, 0.0001, 0, BloomFilter.BLOOM_UPDATE_ALL); + filter.insert(Buffer.from(randomHash, 'hex')); + result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.false(); + + filter = BloomFilter.create(1, 0.0001, 0, BloomFilter.BLOOM_UPDATE_ALL); + filter.insert(Buffer.from(randomAddress, 'hex')); + result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.false(); + + filter = BloomFilter.create(1, 0.0001, 0, BloomFilter.BLOOM_UPDATE_ALL); + filter.insert(Buffer.from(irrelevantOutputIndex, 'hex')); + result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.false(); + + filter = BloomFilter.create(1, 0.0001, 0, BloomFilter.BLOOM_UPDATE_ALL); + filter.insert(Buffer.from(irrelevantOutputIndex2, 'hex')); + result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.false(); + }); + + it('should be able to handle coinbase tx', () => { + const tx = new Transaction('03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff1703f06a101299dbcd32279d9e01e508000000002f4e614effffffff0285464209000000001976a9146a341485a9444b35dc9cb90d24e7483de7d37e0088ac7f464209000000001976a914ad037df64c0d0ec5d0395eb9a543f93fcc26092388ac00000000260100f06a1000c69a125eeb5ce6fa55c48966174a90253a79ce3350ccc4918ba2cb1463513c88'); + + const address = new Address('XkNPrBSJtrHZUvUqb3JF4g5rMB3uzaJfEL'); + const filter = BloomFilter.create(1, 0.0001); + filter.insert(address.hashBuffer); + + const result = testTransactionAgainstFilter(filter, tx); + expect(result).to.be.true(); + }); +}); diff --git a/packages/dapi/test/unit/utils.js b/packages/dapi/test/unit/utils.js new file mode 100644 index 00000000000..93c63dc1f9a --- /dev/null +++ b/packages/dapi/test/unit/utils.js @@ -0,0 +1,23 @@ +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); + +chai.use(chaiAsPromised); +const { expect } = chai; + +const assert = require('assert'); +const Logger = require('../../lib/log/Logger'); + +// TODO: Write unit tests +describe('Utils - Utils', () => { + const logger = new Logger(); + const logger2 = new Logger('test.log'); + it('should be able to start a logger', () => { + // TODO: test logger2 by checking file exist. + assert.equal(typeof logger, 'object'); // bogus placeholder + assert.equal(typeof logger2, 'object'); // bogus placeholder + }); + + it('should not be able to start a logger with invalid level', () => { + expect(() => new Logger({ level: 'FAKE' })).to.throw('Logger: No log level matches FAKE'); + }); +}); diff --git a/packages/dapi/test/unit/utils/index.js b/packages/dapi/test/unit/utils/index.js new file mode 100644 index 00000000000..790678be551 --- /dev/null +++ b/packages/dapi/test/unit/utils/index.js @@ -0,0 +1,36 @@ +/* eslint-disable no-unused-expressions */ +// Suppressed to use chai without dirty-chai +// TODO: Move to Jest instead of mocha/chai/sinon/nyc/etc +const chai = require('chai'); + +const utils = require('../../../lib/utils'); + +const { expect } = chai; + +describe('utils', () => { + describe('#isRegtest', () => { + it('Should return true only if "regtest" string passed', () => { + expect(utils.isRegtest('regtest')).to.be.true; + + expect(utils.isRegtest('regtest=')).to.be.false; + expect(utils.isRegtest('_regtest')).to.be.false; + expect(utils.isRegtest('retest')).to.be.false; + expect(utils.isRegtest('devnet')).to.be.false; + expect(utils.isRegtest('mainnet')).to.be.false; + expect(utils.isRegtest('testnet')).to.be.false; + }); + }); + describe('#isDevnet', () => { + it('Should return true only if string that starts from "devnet" passed', () => { + expect(utils.isDevnet('devnet')).to.be.true; + expect(utils.isDevnet('devnet=mysuperdevnet')).to.be.true; + + expect(utils.isDevnet('_devnet')).to.be.false; + expect(utils.isDevnet('regtest=')).to.be.false; + expect(utils.isDevnet('_regtest')).to.be.false; + expect(utils.isDevnet('retest')).to.be.false; + expect(utils.isDevnet('mainnet')).to.be.false; + expect(utils.isDevnet('testnet')).to.be.false; + }); + }); +}); diff --git a/packages/dash-spv/.eslintignore b/packages/dash-spv/.eslintignore new file mode 100644 index 00000000000..5772aab4a64 --- /dev/null +++ b/packages/dash-spv/.eslintignore @@ -0,0 +1,2 @@ +dist/ +.nyc_output/ diff --git a/packages/dash-spv/.github/workflows/test_and_release.yml b/packages/dash-spv/.github/workflows/test_and_release.yml deleted file mode 100644 index 293a3daa5b8..00000000000 --- a/packages/dash-spv/.github/workflows/test_and_release.yml +++ /dev/null @@ -1,77 +0,0 @@ -name: Test and Release - -on: - workflow_dispatch: - release: - types: - - published - pull_request: - branches: - - master - - v[0-9]+.[0-9]+-dev - -jobs: - test: - name: Run Dash SPV tests - runs-on: ubuntu-20.04 - timeout-minutes: 10 - steps: - - uses: actions/checkout@v2 - - - uses: actions/setup-node@v2 - with: - node-version: '12' - - - name: Enable NPM cache - uses: actions/cache@v2 - with: - path: '~/.npm' - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- - - - name: Install NPM dependencies - run: npm ci - - - name: Run ESLinter - run: npm run lint - - release: - name: Release NPM package - runs-on: ubuntu-20.04 - needs: test - if: ${{ github.event_name == 'release' }} - steps: - - uses: actions/checkout@v2 - - - name: Check package version matches tag - uses: geritol/match-tag-to-package-version@0.1.0 - env: - TAG_PREFIX: refs/tags/v - - - name: Enable NPM cache - uses: actions/cache@v2 - with: - path: '~/.npm' - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- - - - name: Install NPM dependencies - run: npm ci - - - name: Set release tag - uses: actions/github-script@v3 - id: tag - with: - result-encoding: string - script: | - const tag = context.payload.release.tag_name; - const [, major, minor] = tag.match(/^v([0-9]+)\.([0-9]+)/); - return (tag.includes('dev') ? `${major}.${minor}-dev` : 'latest'); - - - name: Publish NPM package - uses: JS-DevTools/npm-publish@v1 - with: - token: ${{ secrets.NPM_TOKEN }} - tag: ${{ steps.tag.outputs.result }} diff --git a/packages/dash-spv/.gitignore b/packages/dash-spv/.gitignore index 7ae4900aba5..095d365a56f 100644 --- a/packages/dash-spv/.gitignore +++ b/packages/dash-spv/.gitignore @@ -1,3 +1,4 @@ +dist/ node_modules launch.json .idea/ diff --git a/packages/dash-spv/LICENSE b/packages/dash-spv/LICENSE new file mode 100644 index 00000000000..95bf3c7cb64 --- /dev/null +++ b/packages/dash-spv/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2022 Dash Core Group, Inc. + +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/packages/dash-spv/README.md b/packages/dash-spv/README.md index ae462be5d6e..3659fd1bfb4 100644 --- a/packages/dash-spv/README.md +++ b/packages/dash-spv/README.md @@ -1,6 +1,6 @@ -# dash-spv (DEPRECATED) +# dash-spv -Moved to the https://github.com/dashevo/platform +Set of SPV function used by js-dapi-client package ## License diff --git a/packages/dash-spv/lib/blockstore.js b/packages/dash-spv/lib/blockstore.js index 832d2481e67..1ed82a8c1cf 100644 --- a/packages/dash-spv/lib/blockstore.js +++ b/packages/dash-spv/lib/blockstore.js @@ -53,5 +53,4 @@ class BlockStore { } } - module.exports = BlockStore; diff --git a/packages/dash-spv/lib/consensus.js b/packages/dash-spv/lib/consensus.js index 54adedfe97d..8ef5fefd047 100644 --- a/packages/dash-spv/lib/consensus.js +++ b/packages/dash-spv/lib/consensus.js @@ -6,7 +6,7 @@ const MIN_TIMESTAMP_HEADERS = 11; const MIN_DGW_HEADERS = 24; function getMedianTimestamp(headers) { - const timestamps = headers.map(h => h.time); + const timestamps = headers.map((h) => h.time); const median = (arr) => { const mid = Math.floor(arr.length / 2); const nums = [...arr].sort((a, b) => a - b); @@ -22,7 +22,7 @@ function hasGreaterThanMedianTimestamp(newHeader, previousHeaders) { const headerNormalised = utils.normalizeHeader(newHeader); const normalizedLatestHeaders = previousHeaders.slice( Math.max(previousHeaders.length - MIN_TIMESTAMP_HEADERS, 0), - ).map(h => utils.normalizeHeader(h)); + ).map((h) => utils.normalizeHeader(h)); return getMedianTimestamp(normalizedLatestHeaders) < headerNormalised.time; } @@ -32,7 +32,7 @@ function isValidBlockHeader(newHeader, previousHeaders, network = 'mainnet') { && newHeader.validTimestamp() && hasGreaterThanMedianTimestamp(newHeader, previousHeaders) && hasValidTarget( - utils.getDgwBlock(newHeader), previousHeaders.map(h => utils.getDgwBlock(h)), network, + utils.getDgwBlock(newHeader), previousHeaders.map((h) => utils.getDgwBlock(h)), network, ); } return newHeader.validProofOfWork() diff --git a/packages/dash-spv/lib/merkleproofs.js b/packages/dash-spv/lib/merkleproofs.js index d9b3a12c144..dca09952c35 100644 --- a/packages/dash-spv/lib/merkleproofs.js +++ b/packages/dash-spv/lib/merkleproofs.js @@ -11,10 +11,10 @@ const merkleproofs = { validateTxProofs: (merkleBlock, transactions) => { let txToFilter = transactions.slice(); if (typeof transactions[0] === 'string') { - txToFilter = txToFilter.map(tx => DashUtil.toHash(tx).toString('hex')); + txToFilter = txToFilter.map((tx) => DashUtil.toHash(tx).toString('hex')); } return merkleBlock.validMerkleTree - && txToFilter.filter(tx => merkleBlock.hasTransaction(tx)).length === transactions.length; + && txToFilter.filter((tx) => merkleBlock.hasTransaction(tx)).length === transactions.length; }, }; diff --git a/packages/dash-spv/lib/spvchain.js b/packages/dash-spv/lib/spvchain.js index f35ae5a90e5..7f905907691 100644 --- a/packages/dash-spv/lib/spvchain.js +++ b/packages/dash-spv/lib/spvchain.js @@ -1,7 +1,7 @@ const BlockStore = require('./blockstore'); const config = require('../config/config'); const Consensus = require('./consensus'); -const utils = require('../lib/utils'); +const utils = require('./utils'); const SpvChain = class { constructor(chainType, confirms = 100, startBlock) { @@ -130,9 +130,9 @@ const SpvChain = class { /** @private */ isDuplicate(compareHash) { - return this.getAllBranches().map(branch => branch.map(node => node.hash)) - .concat(this.orphanBlocks.map(orphan => orphan.hash)) - .filter(hash => hash === compareHash).length > 0; + return this.getAllBranches().map((branch) => branch.map((node) => node.hash)) + .concat(this.orphanBlocks.map((orphan) => orphan.hash)) + .filter((hash) => hash === compareHash).length > 0; } /** @private */ @@ -254,7 +254,7 @@ const SpvChain = class { return blockInDB; } - return this.getLongestChain().filter(h => h.hash === hash)[0]; + return this.getLongestChain().filter((h) => h.hash === hash)[0]; }); } @@ -295,7 +295,7 @@ const SpvChain = class { return true; } } - const normalizedHeaders = headers.map(h => utils.normalizeHeader(h)); + const normalizedHeaders = headers.map((h) => utils.normalizeHeader(h)); const isOrphan = !SpvChain.isParentChild(normalizedHeaders[0], this.getTipHeader()); const allValid = normalizedHeaders.reduce( diff --git a/packages/dash-spv/package-lock.json b/packages/dash-spv/package-lock.json deleted file mode 100644 index 57ca9b0d3cb..00000000000 --- a/packages/dash-spv/package-lock.json +++ /dev/null @@ -1,5192 +0,0 @@ -{ - "name": "@dashevo/dash-spv", - "version": "1.1.6", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "@dashevo/dash-spv", - "version": "1.1.6", - "license": "MIT", - "dependencies": { - "@dashevo/dark-gravity-wave": "^1.1.1", - "@dashevo/dash-util": "^2.0.3", - "@dashevo/dashcore-lib": "^0.19.29", - "levelup": "^4.0.1", - "memdown": "^3.0.0" - }, - "devDependencies": { - "eslint": "^5.16.0", - "eslint-config-airbnb-base": "^13.1.0", - "eslint-plugin-import": "^2.17.3", - "mocha": "^5.2.0", - "should": "^13.2.3" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.0.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "dependencies": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "node_modules/@dashevo/dark-gravity-wave": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/dark-gravity-wave/-/dark-gravity-wave-1.1.1.tgz", - "integrity": "sha512-rt0PzGzqplqERWVIMLlBxm4mJqjFTYNUFRhIccbfaF/MDyd0/585krGOWIhe0Sis9XQNA/FJlxxRjtPXIcyyCg==" - }, - "node_modules/@dashevo/dash-util": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dashevo/dash-util/-/dash-util-2.0.3.tgz", - "integrity": "sha512-fnc76NYVBhuTLhuUVidnV9sKSsMxmkxkhUOjiD/ny6Ipyo+qxwKeFAn8SvdVzlEpflNA613B+hsxmTBBGazl4A==", - "dependencies": { - "bn.js": "^4.6.4", - "buffer-reverse": "^1.0.1" - } - }, - "node_modules/@dashevo/dashcore-lib": { - "version": "0.19.29", - "resolved": "https://registry.npmjs.org/@dashevo/dashcore-lib/-/dashcore-lib-0.19.29.tgz", - "integrity": "sha512-EPuazTO40JmrLVjwQSRzC1VQ8XhB2e92BUvzM0rBQjFKE1UnTTNm5R7chZNSKvUZaHWAb/mtYXov5hSPED2naw==", - "dependencies": { - "@dashevo/x11-hash-js": "^1.0.2", - "@types/node": "^12.12.47", - "bloom-filter": "^0.2.0", - "bls-signatures": "^0.2.5", - "bn.js": "=4.11.8", - "bs58": "=4.0.1", - "elliptic": "6.5.3", - "eslint-config-prettier": "^8.3.0", - "inherits": "=2.0.1", - "lodash": "^4.17.20", - "unorm": "^1.6.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "peer": true - }, - "node_modules/@dashevo/dashcore-lib/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true - }, - "node_modules/@dashevo/dashcore-lib/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "peer": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/eslint": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.7.0.tgz", - "integrity": "sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==", - "peer": true, - "dependencies": { - "@eslint/eslintrc": "^1.0.5", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "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.1.0", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.2.0", - "espree": "^9.3.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/eslint-scope": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", - "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "peer": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/espree": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", - "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", - "peer": true, - "dependencies": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "peer": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "peer": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "peer": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", - "peer": true - }, - "node_modules/@dashevo/dashcore-lib/node_modules/globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "peer": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "peer": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "peer": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "peer": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "peer": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "peer": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "peer": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@dashevo/dashcore-lib/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "peer": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@dashevo/x11-hash-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@dashevo/x11-hash-js/-/x11-hash-js-1.0.2.tgz", - "integrity": "sha512-3vvnZweBca4URBXHF+FTrM4sdTpp3IMt73G1zUKQEdYm/kJkIKN94qpFai7YZDl87k64RCH+ckRZk6ruQPz5KQ==" - }, - "node_modules/@eslint/eslintrc": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", - "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", - "peer": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.2.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "peer": true - }, - "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/espree": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", - "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", - "peer": true, - "dependencies": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "peer": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "peer": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", - "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", - "peer": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "peer": true - }, - "node_modules/@types/node": { - "version": "12.20.42", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", - "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" - }, - "node_modules/abstract-leveldown": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.0.3.tgz", - "integrity": "sha512-jzewKKpZbaYUa6HTThnrl+GrJhzjEAeuc7hTVpZdzg7kupXZFoqQDFwyOwLNbmJKJlmzw8yiipMPkDiuKkT06Q==", - "dependencies": { - "level-concat-iterator": "~2.0.0", - "xtend": "~4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "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" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", - "dev": true, - "dependencies": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "node_modules/base-x": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.5.tgz", - "integrity": "sha512-C3picSgzPSLE+jW3tcBzJoGwitOtazb5B+5YmAxZm2ybmTi9LNgAtDO/jjVEBZwHoXmDBZ9m/IELj3elJVRBcA==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/bloom-filter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bloom-filter/-/bloom-filter-0.2.0.tgz", - "integrity": "sha1-hNY7v5Fy2DA+ZMH/FuudvzOpgaM=" - }, - "node_modules/bls-signatures": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/bls-signatures/-/bls-signatures-0.2.5.tgz", - "integrity": "sha512-5TzQNCtR4zWE4lM08EOMIT8l3b4h8g5LNKu50fUYP1PnupaLGSLklAcTto4lnH7VXpyhsar+74L9wNJII4E/4Q==" - }, - "node_modules/bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", - "dependencies": { - "base-x": "^3.0.2" - } - }, - "node_modules/buffer-reverse": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz", - "integrity": "sha1-SSg8jvpvkBvAH6MwTQYCeXGuL2A=" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "node_modules/cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "dependencies": { - "restore-cursor": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - }, - "node_modules/deferred-leveldown": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.0.1.tgz", - "integrity": "sha512-BXohsvTedWOLkj2n/TY+yqVlrCWa2Zs8LSxh3uCAgFOru7/pjxKyZAexGa1j83BaKloER4PqUyQ9rGPJLt9bqA==", - "dependencies": { - "abstract-leveldown": "~6.0.0", - "inherits": "^2.0.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/deferred-leveldown/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "dependencies": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dependencies": { - "prr": "~1.0.1" - }, - "bin": { - "errno": "cli.js" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", - "dev": true, - "dependencies": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", - "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^4.0.3", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.2.2", - "js-yaml": "^3.13.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^5.5.1", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^6.14.0 || ^8.10.0 || >=9.10.0" - } - }, - "node_modules/eslint-config-airbnb-base": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.1.0.tgz", - "integrity": "sha512-XWwQtf3U3zIoKO1BbHh6aUhJZQweOwSt4c2JrPDg9FP3Ltv3+YfEv7jIDB8275tVnO/qOHbfuYg3kzw6Je7uWw==", - "dev": true, - "dependencies": { - "eslint-restricted-globals": "^0.1.1", - "object.assign": "^4.1.0", - "object.entries": "^1.0.4" - }, - "engines": { - "node": ">= 4" - }, - "peerDependencies": { - "eslint": "^4.19.1 || ^5.3.0", - "eslint-plugin-import": "^2.14.0" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", - "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", - "dev": true, - "dependencies": { - "debug": "^2.6.9", - "resolve": "^1.5.0" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/eslint-module-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz", - "integrity": "sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw==", - "dev": true, - "dependencies": { - "debug": "^2.6.8", - "pkg-dir": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-module-utils/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/eslint-plugin-import": { - "version": "2.17.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.17.3.tgz", - "integrity": "sha512-qeVf/UwXFJbeyLbxuY8RgqDyEKCkqV7YC+E5S5uOjAp4tOc8zj01JP3ucoBM8JcEqd1qRasJSg6LLlisirfy0Q==", - "dev": true, - "dependencies": { - "array-includes": "^3.0.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.0", - "has": "^1.0.3", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "read-pkg-up": "^2.0.0", - "resolve": "^1.11.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "2.x - 5.x" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "dependencies": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/eslint-restricted-globals": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz", - "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=", - "dev": true - }, - "node_modules/eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", - "dev": true, - "dependencies": { - "acorn": "^6.0.7", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/external-editor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", - "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", - "dev": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "node_modules/figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "dependencies": { - "flat-cache": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "dependencies": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/flatted": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" - }, - "node_modules/glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "peer": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/hash.js/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/immediate": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", - "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=" - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" - }, - "node_modules/inquirer": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.3.1.tgz", - "integrity": "sha512-MmL624rfkFt4TG9y/Jvmt8vdmOo836U7Y0Hxr2aFk3RelZEGX4Igk0KabWrcaaZaTv9uzglOqWh1Vly+FAWAXA==", - "dev": true, - "dependencies": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.11", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/inquirer/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/inquirer/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "node_modules/is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "peer": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, - "node_modules/is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "dependencies": { - "has": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" - }, - "node_modules/level-concat-iterator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz", - "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/level-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", - "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", - "dependencies": { - "errno": "~0.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/level-iterator-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.1.tgz", - "integrity": "sha512-pSZWqXK6/yHQkZKCHrR59nKpU5iqorKM22C/BOHTb/cwNQ2EOZG+bovmFFGcOgaBoF3KxqJEI27YwewhJQTzsw==", - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^3.0.2", - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/levelup": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.0.2.tgz", - "integrity": "sha512-cx9PmLENwbGA3svWBEbeO2HazpOSOYSXH4VA+ahVpYyurvD+SDSfURl29VBY2qgyk+Vfy2dJd71SBRckj/EZVA==", - "dependencies": { - "deferred-leveldown": "~5.0.0", - "level-errors": "~2.0.0", - "level-iterator-stream": "~4.0.0", - "xtend": "~4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "peer": true - }, - "node_modules/ltgt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", - "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" - }, - "node_modules/memdown": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-3.0.0.tgz", - "integrity": "sha512-tbV02LfZMWLcHcq4tw++NuqMO+FZX8tNJEiD2aNRm48ZZusVg5N8NART+dmBkepJVye986oixErf7jfXboMGMA==", - "dependencies": { - "abstract-leveldown": "~5.0.0", - "functional-red-black-tree": "~1.0.1", - "immediate": "~3.2.3", - "inherits": "~2.0.1", - "ltgt": "~2.2.0", - "safe-buffer": "~5.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/memdown/node_modules/abstract-leveldown": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz", - "integrity": "sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A==", - "dependencies": { - "xtend": "~4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "node_modules/mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", - "dev": true, - "dependencies": { - "minimist": "0.0.8" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", - "dev": true, - "dependencies": { - "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.5", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/mocha/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/mocha/node_modules/glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.entries": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", - "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "dependencies": { - "mimic-fn": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "node_modules/path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "dependencies": { - "pify": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "dependencies": { - "find-up": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "dependencies": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readable-stream/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "node_modules/regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true, - "engines": { - "node": ">=6.5.0" - } - }, - "node_modules/resolve": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", - "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", - "dev": true, - "dependencies": { - "path-parse": "^1.0.6" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "dependencies": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "dependencies": { - "is-promise": "^2.1.0" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/rxjs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", - "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/should": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", - "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", - "dev": true, - "dependencies": { - "should-equal": "^2.0.0", - "should-format": "^3.0.3", - "should-type": "^1.4.0", - "should-type-adaptors": "^1.0.1", - "should-util": "^1.0.0" - } - }, - "node_modules/should-equal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", - "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", - "dev": true, - "dependencies": { - "should-type": "^1.4.0" - } - }, - "node_modules/should-format": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", - "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", - "dev": true, - "dependencies": { - "should-type": "^1.3.0", - "should-type-adaptors": "^1.0.1" - } - }, - "node_modules/should-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", - "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", - "dev": true - }, - "node_modules/should-type-adaptors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", - "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", - "dev": true, - "dependencies": { - "should-type": "^1.3.0", - "should-util": "^1.0.0" - } - }, - "node_modules/should-util": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.0.tgz", - "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=", - "dev": true - }, - "node_modules/signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "node_modules/slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", - "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", - "dev": true - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/string_decoder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", - "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/table": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.0.tgz", - "integrity": "sha512-nHFDrxmbrkU7JAFKqKbDJXfzrX2UBsWmrieXFTGxiI5e4ncg3VqsZeI4EzNmX0ncp4XNGVeoxIWJXfCIXwrsvw==", - "dev": true, - "dependencies": { - "ajv": "^6.9.1", - "lodash": "^4.17.11", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/table/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/table/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/table/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/unorm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", - "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "peer": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "dependencies": { - "mkdirp": "^0.5.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "engines": { - "node": ">=0.4" - } - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "@dashevo/dark-gravity-wave": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/dark-gravity-wave/-/dark-gravity-wave-1.1.1.tgz", - "integrity": "sha512-rt0PzGzqplqERWVIMLlBxm4mJqjFTYNUFRhIccbfaF/MDyd0/585krGOWIhe0Sis9XQNA/FJlxxRjtPXIcyyCg==" - }, - "@dashevo/dash-util": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dashevo/dash-util/-/dash-util-2.0.3.tgz", - "integrity": "sha512-fnc76NYVBhuTLhuUVidnV9sKSsMxmkxkhUOjiD/ny6Ipyo+qxwKeFAn8SvdVzlEpflNA613B+hsxmTBBGazl4A==", - "requires": { - "bn.js": "^4.6.4", - "buffer-reverse": "^1.0.1" - } - }, - "@dashevo/dashcore-lib": { - "version": "0.19.29", - "resolved": "https://registry.npmjs.org/@dashevo/dashcore-lib/-/dashcore-lib-0.19.29.tgz", - "integrity": "sha512-EPuazTO40JmrLVjwQSRzC1VQ8XhB2e92BUvzM0rBQjFKE1UnTTNm5R7chZNSKvUZaHWAb/mtYXov5hSPED2naw==", - "requires": { - "@dashevo/x11-hash-js": "^1.0.2", - "@types/node": "^12.12.47", - "bloom-filter": "^0.2.0", - "bls-signatures": "^0.2.5", - "bn.js": "=4.11.8", - "bs58": "=4.0.1", - "elliptic": "6.5.3", - "eslint-config-prettier": "^8.3.0", - "inherits": "=2.0.1", - "lodash": "^4.17.20", - "unorm": "^1.6.0" - }, - "dependencies": { - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "peer": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "peer": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "peer": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "peer": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "peer": true - }, - "eslint": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.7.0.tgz", - "integrity": "sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==", - "peer": true, - "requires": { - "@eslint/eslintrc": "^1.0.5", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "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.1.0", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.2.0", - "espree": "^9.3.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - } - }, - "eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "requires": {} - }, - "eslint-scope": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", - "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", - "peer": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "peer": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "peer": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", - "peer": true - }, - "espree": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", - "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", - "peer": true, - "requires": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "peer": true - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "peer": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "peer": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", - "peer": true - }, - "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "peer": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "peer": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "peer": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "peer": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "peer": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "peer": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "peer": true - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "peer": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "peer": true, - "requires": { - "glob": "^7.1.3" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "peer": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "peer": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "peer": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "peer": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "peer": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "peer": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "@dashevo/x11-hash-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@dashevo/x11-hash-js/-/x11-hash-js-1.0.2.tgz", - "integrity": "sha512-3vvnZweBca4URBXHF+FTrM4sdTpp3IMt73G1zUKQEdYm/kJkIKN94qpFai7YZDl87k64RCH+ckRZk6ruQPz5KQ==" - }, - "@eslint/eslintrc": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", - "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", - "peer": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.2.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "peer": true - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "peer": true - }, - "eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", - "peer": true - }, - "espree": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", - "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", - "peer": true, - "requires": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" - } - }, - "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "peer": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "peer": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "peer": true - } - } - }, - "@humanwhocodes/config-array": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", - "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", - "peer": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "peer": true - }, - "@types/node": { - "version": "12.20.42", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", - "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" - }, - "abstract-leveldown": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.0.3.tgz", - "integrity": "sha512-jzewKKpZbaYUa6HTThnrl+GrJhzjEAeuc7hTVpZdzg7kupXZFoqQDFwyOwLNbmJKJlmzw8yiipMPkDiuKkT06Q==", - "requires": { - "level-concat-iterator": "~2.0.0", - "xtend": "~4.0.0" - } - }, - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==" - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" - } - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base-x": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.5.tgz", - "integrity": "sha512-C3picSgzPSLE+jW3tcBzJoGwitOtazb5B+5YmAxZm2ybmTi9LNgAtDO/jjVEBZwHoXmDBZ9m/IELj3elJVRBcA==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "bloom-filter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bloom-filter/-/bloom-filter-0.2.0.tgz", - "integrity": "sha1-hNY7v5Fy2DA+ZMH/FuudvzOpgaM=" - }, - "bls-signatures": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/bls-signatures/-/bls-signatures-0.2.5.tgz", - "integrity": "sha512-5TzQNCtR4zWE4lM08EOMIT8l3b4h8g5LNKu50fUYP1PnupaLGSLklAcTto4lnH7VXpyhsar+74L9wNJII4E/4Q==" - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", - "requires": { - "base-x": "^3.0.2" - } - }, - "buffer-reverse": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz", - "integrity": "sha1-SSg8jvpvkBvAH6MwTQYCeXGuL2A=" - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "requires": { - "ms": "2.1.2" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - }, - "deferred-leveldown": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.0.1.tgz", - "integrity": "sha512-BXohsvTedWOLkj2n/TY+yqVlrCWa2Zs8LSxh3uCAgFOru7/pjxKyZAexGa1j83BaKloER4PqUyQ9rGPJLt9bqA==", - "requires": { - "abstract-leveldown": "~6.0.0", - "inherits": "^2.0.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - } - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "requires": { - "esutils": "^2.0.2" - } - }, - "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", - "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^4.0.3", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.2.2", - "js-yaml": "^3.13.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^5.5.1", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0" - } - }, - "eslint-config-airbnb-base": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.1.0.tgz", - "integrity": "sha512-XWwQtf3U3zIoKO1BbHh6aUhJZQweOwSt4c2JrPDg9FP3Ltv3+YfEv7jIDB8275tVnO/qOHbfuYg3kzw6Je7uWw==", - "dev": true, - "requires": { - "eslint-restricted-globals": "^0.1.1", - "object.assign": "^4.1.0", - "object.entries": "^1.0.4" - } - }, - "eslint-import-resolver-node": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", - "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.5.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-module-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz", - "integrity": "sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw==", - "dev": true, - "requires": { - "debug": "^2.6.8", - "pkg-dir": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-plugin-import": { - "version": "2.17.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.17.3.tgz", - "integrity": "sha512-qeVf/UwXFJbeyLbxuY8RgqDyEKCkqV7YC+E5S5uOjAp4tOc8zj01JP3ucoBM8JcEqd1qRasJSg6LLlisirfy0Q==", - "dev": true, - "requires": { - "array-includes": "^3.0.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.0", - "has": "^1.0.3", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "read-pkg-up": "^2.0.0", - "resolve": "^1.11.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-restricted-globals": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz", - "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=", - "dev": true - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", - "dev": true - }, - "espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", - "dev": true, - "requires": { - "acorn": "^6.0.7", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - } - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" - }, - "external-editor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", - "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - } - }, - "flatted": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" - }, - "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "peer": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - } - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" - }, - "immediate": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", - "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=" - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" - }, - "inquirer": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.3.1.tgz", - "integrity": "sha512-MmL624rfkFt4TG9y/Jvmt8vdmOo836U7Y0Hxr2aFk3RelZEGX4Igk0KabWrcaaZaTv9uzglOqWh1Vly+FAWAXA==", - "dev": true, - "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.11", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "peer": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "peer": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" - }, - "level-concat-iterator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz", - "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==" - }, - "level-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", - "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", - "requires": { - "errno": "~0.1.1" - } - }, - "level-iterator-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.1.tgz", - "integrity": "sha512-pSZWqXK6/yHQkZKCHrR59nKpU5iqorKM22C/BOHTb/cwNQ2EOZG+bovmFFGcOgaBoF3KxqJEI27YwewhJQTzsw==", - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^3.0.2", - "xtend": "^4.0.0" - } - }, - "levelup": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.0.2.tgz", - "integrity": "sha512-cx9PmLENwbGA3svWBEbeO2HazpOSOYSXH4VA+ahVpYyurvD+SDSfURl29VBY2qgyk+Vfy2dJd71SBRckj/EZVA==", - "requires": { - "deferred-leveldown": "~5.0.0", - "level-errors": "~2.0.0", - "level-iterator-stream": "~4.0.0", - "xtend": "~4.0.0" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "peer": true - }, - "ltgt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", - "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" - }, - "memdown": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-3.0.0.tgz", - "integrity": "sha512-tbV02LfZMWLcHcq4tw++NuqMO+FZX8tNJEiD2aNRm48ZZusVg5N8NART+dmBkepJVye986oixErf7jfXboMGMA==", - "requires": { - "abstract-leveldown": "~5.0.0", - "functional-red-black-tree": "~1.0.1", - "immediate": "~3.2.3", - "inherits": "~2.0.1", - "ltgt": "~2.2.0", - "safe-buffer": "~5.1.1" - }, - "dependencies": { - "abstract-leveldown": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz", - "integrity": "sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A==", - "requires": { - "xtend": "~4.0.0" - } - } - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.5", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.entries": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", - "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - } - } - }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true - }, - "resolve": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", - "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "rxjs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", - "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "should": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", - "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", - "dev": true, - "requires": { - "should-equal": "^2.0.0", - "should-format": "^3.0.3", - "should-type": "^1.4.0", - "should-type-adaptors": "^1.0.1", - "should-util": "^1.0.0" - } - }, - "should-equal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", - "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", - "dev": true, - "requires": { - "should-type": "^1.4.0" - } - }, - "should-format": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", - "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", - "dev": true, - "requires": { - "should-type": "^1.3.0", - "should-type-adaptors": "^1.0.1" - } - }, - "should-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", - "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", - "dev": true - }, - "should-type-adaptors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", - "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", - "dev": true, - "requires": { - "should-type": "^1.3.0", - "should-util": "^1.0.0" - } - }, - "should-util": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.0.tgz", - "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - } - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", - "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string_decoder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", - "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.0.tgz", - "integrity": "sha512-nHFDrxmbrkU7JAFKqKbDJXfzrX2UBsWmrieXFTGxiI5e4ncg3VqsZeI4EzNmX0ncp4XNGVeoxIWJXfCIXwrsvw==", - "dev": true, - "requires": { - "ajv": "^6.9.1", - "lodash": "^4.17.11", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "peer": true - }, - "unorm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", - "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==" - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "peer": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "peer": true - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" - } - } -} diff --git a/packages/dash-spv/package.json b/packages/dash-spv/package.json index 5ebee42a534..339705ec544 100644 --- a/packages/dash-spv/package.json +++ b/packages/dash-spv/package.json @@ -1,7 +1,7 @@ { "name": "@dashevo/dash-spv", - "version": "1.1.6", - "description": "Temporary repo until spv functions moved into dashcore-lib", + "version": "1.1.7-1", + "description": "Repository containing SPV functions used by @dashevo", "main": "index.js", "scripts": { "test": "mocha test/ --no-timeouts --recursive", @@ -13,15 +13,15 @@ "dependencies": { "@dashevo/dark-gravity-wave": "^1.1.1", "@dashevo/dash-util": "^2.0.3", - "@dashevo/dashcore-lib": "^0.19.29", - "levelup": "^4.0.1", - "memdown": "^3.0.0" + "@dashevo/dashcore-lib": "~0.19.39", + "levelup": "^4.4.0", + "memdown": "^5.1.0" }, "devDependencies": { - "eslint": "^5.16.0", - "eslint-config-airbnb-base": "^13.1.0", - "eslint-plugin-import": "^2.17.3", - "mocha": "^5.2.0", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2", + "mocha": "^9.1.2", "should": "^13.2.3" } } diff --git a/packages/dash-spv/test/data/headers.js b/packages/dash-spv/test/data/headers.js index d9e7bedc842..eb9cae85ee1 100644 --- a/packages/dash-spv/test/data/headers.js +++ b/packages/dash-spv/test/data/headers.js @@ -213,4 +213,4 @@ const headers = [ }, ]; -module.exports = headers.map(h => utils.normalizeHeader(h)); +module.exports = headers.map((h) => utils.normalizeHeader(h)); diff --git a/packages/dashmate/.eslintrc b/packages/dashmate/.eslintrc new file mode 100644 index 00000000000..031c6d09510 --- /dev/null +++ b/packages/dashmate/.eslintrc @@ -0,0 +1,28 @@ +{ + "extends": "airbnb-base", + "rules": { + "no-plusplus": 0, + "eol-last": [ + "error", + "always" + ], + "no-continue": "off", + "class-methods-use-this": "off", + "no-await-in-loop": "off", + "no-restricted-syntax": [ + "error", + { + "selector": "LabeledStatement", + "message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand." + }, + { + "selector": "WithStatement", + "message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize." + } + ], + "curly": [ + "error", + "all" + ] + } +} diff --git a/packages/dashmate/.gitignore b/packages/dashmate/.gitignore new file mode 100644 index 00000000000..07158d47887 --- /dev/null +++ b/packages/dashmate/.gitignore @@ -0,0 +1,6 @@ +# Oclif +*-debug.log +*-error.log +.yo-repository +dist +tmp diff --git a/packages/dashmate/CHANGELOG.md b/packages/dashmate/CHANGELOG.md new file mode 100644 index 00000000000..22c5f6c98e2 --- /dev/null +++ b/packages/dashmate/CHANGELOG.md @@ -0,0 +1,547 @@ +# [0.21.0](https://github.com/dashevo/dashmate/compare/v0.20.2...v0.21.0) (2021-10-25) + + +### Features + +* update network parameters and migrations for `testnet-6` ([#457](https://github.com/dashevo/dashmate/pull/457)) +* add a script to configure test suite ([#410](https://github.com/dashevo/dashmate/issues/410), [#447](https://github.com/dashevo/dashmate/issues/447)) + + +### Bug Fixes + +* incorrect queue predictions returned by status command ([#416](https://github.com/dashevo/dashmate/issues/416)) +* drive log files were missing upon setup ([#428](https://github.com/dashevo/dashmate/issues/428)) +* error running status command on the seed node ([#424](https://github.com/dashevo/dashmate/issues/424)) +* migration to v0.20.0 fails ([#426](https://github.com/dashevo/dashmate/issues/426)) +* no such file or directory, drive_pretty_1.log ([#431](https://github.com/dashevo/dashmate/issues/431)) +* unavailable log settings on the seed node ([#427](https://github.com/dashevo/dashmate/issues/427)) +* `debug-logs` option doesn't apply to file logs ([#442](https://github.com/dashevo/dashmate/issues/442)) +* Dash JSON-RPC: Request Error: socket hang up ([#444](https://github.com/dashevo/dashmate/issues/444)) +* issues related to keeping log files in /tmp ([#437](https://github.com/dashevo/dashmate/issues/437)) +* various sentinel container errors on full nodes ([#443](https://github.com/dashevo/dashmate/issues/443)) +* core database contains a block from the future ([#448](https://github.com/dashevo/dashmate/issues/448)) + + + +## [0.20.2](https://github.com/dashevo/dashmate/compare/v0.20.1...v0.20.2) (2021-08-04) + + +### Features + +* update network parameters and migrations for `testnet-5` ([#408](https://github.com/dashevo/dashmate/pull/408)) + + +### BREAKING CHANGES: + +* cannot connect to networks prior to `testnet-5` + + + +## [0.20.1](https://github.com/dashevo/dashmate/compare/v0.20.0...v0.20.1) (2021-07-28) + + +### Bug Fixes + +* InvalidResponse error when connecting to older versions of dapi ([#406](https://github.com/dashevo/dashmate/issues/406)) + + + +# [0.20.0](https://github.com/dashevo/dashmate/compare/v0.19.1...v0.20.0) (2021-07-22) + + +### Bug Fixes + +* bad-txns-premature-spend-of-coinbase error ([#357](https://github.com/dashevo/dashmate/issues/400)) +* setup local command stuck in some envs ([#351](https://github.com/dashevo/dashmate/issues/351), [#352](https://github.com/dashevo/dashmate/issues/352), [#367](https://github.com/dashevo/dashmate/issues/367), [#370](https://github.com/dashevo/dashmate/issues/370)) +* setup local command stuck with more than 3 nodes ([#390](https://github.com/dashevo/dashmate/issues/390)) +* missing llmq conf for devnets ([#400](https://github.com/dashevo/dashmate/issues/400)) +* windows tmp directory install error ([#393](https://github.com/dashevo/dashmate/issues/393), thanks to @ICJR) +* set quorum type in tenderdash config ([a6ccf5c](https://github.com/dashevo/dashmate/commit/a6ccf5cf84f4a3d28ea71192b6ad9287215a0538)) +* failed wallet sync from time to time ([258691c](https://github.com/dashevo/dashmate/commit/258691c1ca3bf1a294b375ee39d75fd91b7b3237)) +* tenderdash local connectivity issues ([#363](https://github.com/dashevo/dashmate/issues/363)) + + +### Features + +* wait nodes to be ready ([#369](https://github.com/dashevo/dashmate/issues/369)) +* add a temporary reset script for the local network ([#361](https://github.com/dashevo/dashmate/issues/380), [#371](https://github.com/dashevo/dashmate/issues/380), [#380](https://github.com/dashevo/dashmate/issues/380)) +* add an option to enable debug logs ([#349](https://github.com/dashevo/dashmate/issues/349)) +* enable debug logs option for setup local command ([#362](https://github.com/dashevo/dashmate/issues/362)) +* enable and request miner interval for setup local ([#360](https://github.com/dashevo/dashmate/pull/360)) +* configure tenderdash log level ([#364](https://github.com/dashevo/dashmate/pull/364)) +* migrate Drive state tree to blake3 ([#402](https://github.com/dashevo/dashmate/issues/402)) +* support minor Core updates ([#379](https://github.com/dashevo/dashmate/issues/379)) +* configure validator set LLMQ type ([#376](https://github.com/dashevo/dashmate/issues/376)) +* update DPP to 0.20.0 ([#381](https://github.com/dashevo/dashmate/issues/381)) +* update to Tenderdash v0.5.0 ([#358](https://github.com/dashevo/dashmate/issues/358), [#395](https://github.com/dashevo/dashmate/issues/395), [#399](https://github.com/dashevo/dashmate/issues/399)) +* efficient service build cache ([#389](https://github.com/dashevo/dashmate/issues/389)) + + +### BREAKING CHANGES: + +* setup local command requests miner interval and debug logs +* Tenderdash v0.4.0 is not supported anymore +* building services from path required compose v2 installed and `DOCKER_COMPOSE_V2` env to be set +* platform services 0.19 and lower not supported + + + +## [0.19.1](https://github.com/dashevo/dashmate/compare/v0.19.0...v0.19.1) (2021-05-18) + + +### Features + +* update Core to 0.17.0.0-rc5 ([#353](https://github.com/dashevo/dashmate/issues/353)) + + + +# [0.19.0](https://github.com/dashevo/dashmate/compare/v0.18.2...v0.19.0) (2021-05-12) + + +### Features + +* node groups ([#253](https://github.com/dashevo/dashmate/issues/253), [#337](https://github.com/dashevo/dashmate/issues/337), [#343](https://github.com/dashevo/dashmate/issues/343), [#338](https://github.com/dashevo/dashmate/issues/338), [#321](https://github.com/dashevo/dashmate/issues/321), [#313](https://github.com/dashevo/dashmate/issues/313), [#309](https://github.com/dashevo/dashmate/issues/309), [#314](https://github.com/dashevo/dashmate/issues/314), [#311](https://github.com/dashevo/dashmate/issues/311), [#307](https://github.com/dashevo/dashmate/issues/307), [#300](https://github.com/dashevo/dashmate/issues/300), [#298](https://github.com/dashevo/dashmate/issues/298), [#296](https://github.com/dashevo/dashmate/issues/296), [#291](https://github.com/dashevo/dashmate/issues/291), [#292](https://github.com/dashevo/dashmate/issues/292), [#282](https://github.com/dashevo/dashmate/issues/282)) +* rename mn-bootstrap to dashmate ([#324](https://github.com/dashevo/dashmate/issues/324)) +* ChainLock Asset Lock Proofs support ([#333](https://github.com/dashevo/dashmate/issues/333)) +* feature flags ([#329](https://github.com/dashevo/dashmate/issues/329), [#336](https://github.com/dashevo/dashmate/issues/336), [#350](https://github.com/dashevo/dashmate/issues/329), [#334](https://github.com/dashevo/dashmate/issues/334)) +* update DAPI to 0.19 ([#330](https://github.com/dashevo/dashmate/issues/330)) +* display tasks elapsed time in verbose mode ([#320](https://github.com/dashevo/dashmate/issues/320)) +* NPM cache for DAPI and Drive builds ([#302](https://github.com/dashevo/dashmate/issues/302)) +* tenderdash empty blocks configuration ([#315](https://github.com/dashevo/dashmate/issues/315)) +* check docker version ([#310](https://github.com/dashevo/dashmate/issues/310)) +* skip Instant Lock verification in SDK ([#299](https://github.com/dashevo/dashmate/issues/299)) +* update drive to 0.19 ([#303](https://github.com/dashevo/dashmate/issues/303)) +* register masternodes on testnet given funding privkey ([#288](https://github.com/dashevo/dashmate/issues/288)) +* wait for node to be ready option ([#295](https://github.com/dashevo/dashmate/issues/295)) +* wait for tenderdash on start ([#289](https://github.com/dashevo/dashmate/issues/289)) +* activate sporks during local setup ([#286](https://github.com/dashevo/dashmate/issues/286)) + + +### Bug Fixes + +* with docker compose 1.29 container.inspect throws error if the container isn't running ([#325](https://github.com/dashevo/dashmate/issues/325)) + + +### Documentation + +* add update docs ([#345](https://github.com/dashevo/dashmate/issues/345)) + + +### BREAKING CHANGES + +* the `setup local` command generates a local configs group +* the `local` config now is a template and should be used to start a node +* `mn` commands renamed to `dashmate`. Configs are now stored in `.dashmate` dir. + + + +## [0.18.2](https://github.com/dashevo/dashmate/compare/v0.18.1...v0.18.2) (2021-04-14) + + +### Features + +* update to core 0.17.0.0-rc4 ([#326](https://github.com/dashevo/dashmate/issues/326)) + + + +## [0.18.1](https://github.com/dashevo/dashmate/compare/v0.18.0...v0.18.1) (2021-03-09) + + +### Features + +* update Drive and DAPI images ([0273d33](https://github.com/dashevo/dashmate/commit/0273d33d524bd6dfa7facdd708fe79d4a2e83328)) + + + +# [0.18.0](https://github.com/dashevo/dashmate/compare/v0.17.4...v0.18.0) (2021-03-03) + + +### Bug Fixes + +* platform sync shows Infinity% ([#281](https://github.com/dashevo/dashmate/issues/281)) +* status command returns TypeError ([#251](https://github.com/dashevo/dashmate/issues/251)) +* uncaught errors when remote services down ([#241](https://github.com/dashevo/dashmate/issues/241)) + + +### Features + +* enable `llmq-qvved-sync` on testnet ([#267](https://github.com/dashevo/dashmate/issues/267)) +* include sentinel image version in config ([#265](https://github.com/dashevo/dashmate/issues/265)) +* update dashd to `0.17.0.0-rc3-hotfix1` ([#276](https://github.com/dashevo/dashmate/issues/276)) +* hard and soft resets, `--platform-only` option ([#249](https://github.com/dashevo/dashmate/issues/249), [#258](https://github.com/dashevo/dashmate/issues/258), [#272](https://github.com/dashevo/dashmate/issues/272)) +* update Tenderdash to 0.34.3 ([#274](https://github.com/dashevo/dashmate/issues/274)) + + +### Chores + +* remove evonet-specific code ([#268](https://github.com/dashevo/dashmate/issues/274)) + + + +## [0.17.4](https://github.com/dashevo/dashmate/compare/v0.17.3...v0.17.4) (2021-02-03) + + +### Features + +* output Drive logs into files ([#252](https://github.com/dashevo/dashmate/issues/252)) + + + +## [0.17.3](https://github.com/dashevo/dashmate/compare/v0.17.2...v0.17.3) (2021-01-19) + + +### Bug Fixes + +* DashPay contract is not set for testnet ([#247](https://github.com/dashevo/dashmate/issues/247)) + + + +## [0.17.2](https://github.com/dashevo/dashmate/compare/v0.17.1...v0.17.2) (2021-01-13) + + +### Features + +* add seed nodes for testnet ([#239](https://github.com/dashevo/dashmate/issues/239)) + + + +## [0.17.1](https://github.com/dashevo/dashmate/compare/v0.17.0...v0.17.1) (2021-01-12) + + +### Bug Fixes + +* validator state not found after reset ([#238](https://github.com/dashevo/dashmate/issues/238)) + + + +# [0.17.0](https://github.com/dashevo/dashmate/compare/v0.16.1...v0.17.0) (2021-01-11) + + +### Features + +* add verbose mode to commands ([#187](https://github.com/dashevo/dashmate/issues/187), [#230](https://github.com/dashevo/dashmate/issues/230)) +* update dependencies [#177](https://github.com/dashevo/dashmate/issues/177), [#188](https://github.com/dashevo/dashmate/issues/188), [#211](https://github.com/dashevo/dashmate/issues/211), ([#231](https://github.com/dashevo/dashmate/issues/231)) +* introduce setup command ([#200](https://github.com/dashevo/dashmate/issues/200), [#214](https://github.com/dashevo/dashmate/issues/214), [#219](https://github.com/dashevo/dashmate/issues/219)) +* configure `passFakeAssetLockProofForTests` ([#222](https://github.com/dashevo/dashmate/issues/222)) +* expose `rawchainlocksig` and `zmqpubrawtxlocksig` from Core ([#221](https://github.com/dashevo/dashmate/issues/221)) +* pass dashpay contract id and block height to drive ([#220](https://github.com/dashevo/dashmate/issues/220)) +* add `skipAssetLockConfirmationValidation` option for drive ([#216](https://github.com/dashevo/dashmate/issues/216)) +* config migration ([#199](https://github.com/dashevo/dashmate/issues/199)) +* more status command output ([#124](https://github.com/dashevo/dashmate/issues/124), [#229](https://github.com/dashevo/dashmate/issues/229)) +* update Insight API ([#206](https://github.com/dashevo/dashmate/issues/206), [#207](https://github.com/dashevo/dashmate/issues/207)) +* register dashpay contract ([#125](https://github.com/dashevo/dashmate/issues/125)) +* implement rate limiter in config ([#183](https://github.com/dashevo/dashmate/issues/183)) +* update envoy for multi-arch support ([#179](https://github.com/dashevo/dashmate/issues/179)) +* add network parameters to configs ([#150](https://github.com/dashevo/dashmate/issues/150)) +* add ZMQ envs for Drive ([#180](https://github.com/dashevo/dashmate/issues/180)) +* update testnet config ([#232](https://github.com/dashevo/dashmate/issues/232)) + + +### Bug Fixes + +* pass correct params to error message ([#228](https://github.com/dashevo/dashmate/issues/228)) +* rmdir and tenderdash errors ([#227](https://github.com/dashevo/dashmate/issues/227)) +* configs are removed during writing ([#224](https://github.com/dashevo/dashmate/issues/224)) +* platform init doesn't work with many faulty nodes ([#217](https://github.com/dashevo/dashmate/issues/217)) +* syntax error in nginx config ([#205](https://github.com/dashevo/dashmate/issues/205)) +* templates dir not found in travis ([#201](https://github.com/dashevo/dashmate/issues/201), [#203](https://github.com/dashevo/dashmate/issues/203)) +* a bunch of small fixes ([#194](https://github.com/dashevo/dashmate/issues/194)) +* lint errors and dash core config ([#192](https://github.com/dashevo/dashmate/issues/192)) +* add section to dashd testnet config ([#175](https://github.com/dashevo/dashmate/issues/175)) + + + +## [0.16.1](https://github.com/dashevo/dashmate/compare/v0.16.0...v0.16.1) (2020-10-30) + + +### Bug Fixes + +* add section to dashd testnet config ([#175](https://github.com/dashevo/dashmate/issues/175)) + + + +# [0.16.0](https://github.com/dashevo/dashmate/compare/v0.15.1...v0.16.0) (2020-10-29) + + +### Bug Fixes + +* "No available addresses" in setup command on the platform init step ([#164](https://github.com/dashevo/dashmate/issues/164)) + + +### Features + +* make `NODE_ENV` and logging level configurable ([#172](https://github.com/dashevo/dashmate/issues/172)) +* obtain and pass DPNS contract block height ([#170](https://github.com/dashevo/dashmate/issues/170), [#173](https://github.com/dashevo/dashmate/issues/173)) +* update to Dash SDK 0.16 ([#160](https://github.com/dashevo/dashmate/issues/163), [#163](https://github.com/dashevo/dashmate/issues/163), [#163](https://github.com/dashevo/dashmate/issues/163), [#166](https://github.com/dashevo/dashmate/issues/166)) +* restart command ([#152](https://github.com/dashevo/dashmate/issues/152)) +* switch insight-api docker image to shumkov/insight-api:3.0.0 ([#157](https://github.com/dashevo/dashmate/issues/157)) +* update Dash Core to 0.16 ([#153](https://github.com/dashevo/dashmate/issues/153), [#155](https://github.com/dashevo/dashmate/issues/155)) + + +### Documentation + +* cannot mint dash on evonet ([#171](https://github.com/dashevo/dashmate/issues/171)) + + +### BREAKING CHANGES + +* `platform.dpns.contractId` config options is moved to `platform.dpns.contract.id` +* data created with 0.15 version and less in not compatible. Please reset your node before upgrade +* see [Drive breaking changes](https://github.com/dashevo/js-drive/releases/tag/v0.16.0) +* see [DAPI breaking changes](https://github.com/dashevo/dapi/releases/tag/v0.16.0) + + + +## [0.15.1](https://github.com/dashevo/dashmate/compare/v0.15.0...v0.15.1) (2020-09-08) + + +### Bug Fixes + +* services.core.ports contains an invalid type ([#149](https://github.com/dashevo/dashmate/issues/149)) + + + +# [0.15.0](https://github.com/dashevo/dashmate/compare/v0.14.0...v0.15.0) (2020-09-04) + + +### Bug Fixes + +* ignored mint address option ([#143](https://github.com/dashevo/dashmate/issues/143)) +* Dash Client was created before Tendermint is started ([#131](https://github.com/dashevo/dashmate/issues/131)) +* gRPC buffer size settings in NGINX was too small ([#127](https://github.com/dashevo/dashmate/issues/127)) +* transaction filter stream doesn't work with gRPC-Web ([#116](https://github.com/dashevo/dashmate/issues/116)) + + +### Features + +* replace env files and presets with new `config` command ([#119](https://github.com/dashevo/dashmate/issues/119), [#138](https://github.com/dashevo/dashmate/issues/138)) +* remove unnecessary block generation ([#141](https://github.com/dashevo/dashmate/issues/141)) +* block mining with local development ([#137](https://github.com/dashevo/dashmate/issues/137)) +* move container datadirs to named docker volumes ([#123](https://github.com/dashevo/dashmate/issues/123), [#139](https://github.com/dashevo/dashmate/issues/139), [#140](https://github.com/dashevo/dashmate/issues/140), [#142](https://github.com/dashevo/dashmate/issues/142)) +* nginx responds with unimplemented in case of unsupported version ([#134](https://github.com/dashevo/dashmate/issues/134)) +* move `subscribeToTransactionsWithProofs` to `Core` service ([#121](https://github.com/dashevo/dashmate/issues/121)) +* use new DPNS contract ([#117](https://github.com/dashevo/dashmate/issues/117)) +* generate empty blocks every 3 minutes ([#114](https://github.com/dashevo/dashmate/issues/114)) +* use `generateToAddress` instead of `generate` ([#111](https://github.com/dashevo/dashmate/issues/111)) +* add docker image update support to setup-for-local-development ([#113](https://github.com/dashevo/dashmate/issues/113)) + + +### Code Refactoring + +* use MongoDB init script to initiate replica ([#147](https://github.com/dashevo/dashmate/issues/147)) +* remove getUTXO dependency for SDK ([#133](https://github.com/dashevo/dashmate/issues/139)) + + +### BREAKING CHANGES + +* node data from `data` dir is not using anymore and should be removed +* see [Drive breaking changes](https://github.com/dashevo/js-drive/releases/tag/v0.15.0) +* see [DAPI breaking changes](https://github.com/dashevo/dapi/releases/tag/v0.15.0) + + + +# [0.14.0](https://github.com/dashevo/dashmate/compare/v0.13.4...v0.14.0) (2020-07-24) + + +### Bug Fixes + +* missing `build` section for `tx_filter_stream_service` service ([#94](https://github.com/dashevo/dashmate/issues/94)) +* missing env variables for `dapi-tx-filter-stream` service ([#99](https://github.com/dashevo/dashmate/issues/99)) +* faucet inputs where locked after platform initialization script ([#88](https://github.com/dashevo/dashmate/issues/88)) +* original Tendermint image creates wrong mount points ([#86](https://github.com/dashevo/dashmate/issues/86)) + + +### Features + +* update Evonet preset to 0.14 ([#108](https://github.com/dashevo/dashmate/issues/108), [#105](https://github.com/dashevo/dashmate/issues/105)) +* update Drive and DAPI versions to 0.14 ([#98](https://github.com/dashevo/dashmate/issues/98)) +* implement `status` command ([#49](https://github.com/dashevo/dashmate/issues/49), [#93](https://github.com/dashevo/dashmate/issues/93), [#96](https://github.com/dashevo/dashmate/issues/96)) +* move from Listr to Listr2 ([#84](https://github.com/dashevo/dashmate/issues/84)) +* implement `setup-for-local-development` command ([#82](https://github.com/dashevo/dashmate/issues/82), [#101](https://github.com/dashevo/dashmate/issues/101)) +* implement `update` option for `start` command ([#80](https://github.com/dashevo/dashmate/issues/80)) +* build docker images from local directories ([#59](https://github.com/dashevo/dashmate/issues/59), [#66](https://github.com/dashevo/dashmate/issues/66), [#90](https://github.com/dashevo/dashmate/issues/90)) + + +### Documentation + +* document `status` command in README ([#97](https://github.com/dashevo/dashmate/issues/97)) +* add release date badge ([#85](https://github.com/dashevo/dashmate/issues/85)) +* add development usage for local docker build ([#67](https://github.com/dashevo/dashmate/issues/67)) + + +### BREAKING CHANGES + +* data created with previous versions of Dash Platform is incompatible we the new one, so you need to reset data before you start the node + + + +## [0.13.4](https://github.com/dashevo/dashmate/compare/v0.13.3...v0.13.4) (2020-06-18) + + +### Bug Fixes + +* tendermint throw fatal error on start in linux environment ([#76](https://github.com/dashevo/dashmate/issues/76)) + + + +## [0.13.3](https://github.com/dashevo/dashmate/compare/v0.13.2...v0.13.3) (2020-06-18) + + +### Bug Fixes + +* parsing docker container name on first start ([#75](https://github.com/dashevo/dashmate/issues/75)) + + + +## [0.13.2](https://github.com/dashevo/dashmate/compare/v0.13.1...v0.13.2) (2020-06-16) + + +### Bug Fixes + +* DAPI rate limits disabled for evonet for some reason ([#73](https://github.com/dashevo/dashmate/issues/73)) + + + +## [0.13.1](https://github.com/dashevo/dashmate/compare/v0.12.6...v0.13.1) (2020-06-12) + + +### Features + +* update Evonet configs ([fd0158a](https://github.com/dashevo/dashmate/commit/fd0158a45f1c624628fe7a2735124db1c9f20338)) + + + +# [0.13.0](https://github.com/dashevo/dashmate/compare/v0.12.6...v0.13.0) (2020-06-09) + + +### Bug Fixes + +* do not start stopped services on the docker deamon restart ([#55](https://github.com/dashevo/dashmate/issues/55)) +* switch to dashpay org for sentinel ([#62](https://github.com/dashevo/dashmate/issues/62)) + + +### Features + +* start/stop node commands ([#45](https://github.com/dashevo/dashmate/issues/45), [#48](https://github.com/dashevo/dashmate/issues/48)) +* data reset command ([#43](https://github.com/dashevo/dashmate/issues/43), [#60](https://github.com/dashevo/dashmate/issues/60)) +* masternode registration commands ([#30](https://github.com/dashevo/dashmate/issues/30), [#44](https://github.com/dashevo/dashmate/issues/44), [#54](https://github.com/dashevo/dashmate/issues/54), [#69](https://github.com/dashevo/dashmate/issues/69)) +* remove sleep from docker compose ([#57](https://github.com/dashevo/dashmate/issues/57)) +* allow to start full node ([#42](https://github.com/dashevo/dashmate/issues/42)) +* update configs and docker images ([#64](https://github.com/dashevo/dashmate/issues/42)) + + +### Documentation + +* update README.md to clarify install instructions ([#33](https://github.com/dashevo/dashmate/issues/33), [#65](https://github.com/dashevo/dashmate/issues/65)) + + +### BREAKING CHANGES + +* Dash Platform v0.12 data in incompatible with 0.13, so you need to reset data before you start the node + + + +# [0.12.6](https://github.com/dashevo/dashmate/compare/v0.12.5...v0.12.6) (2020-05-23) + + +### Features + +* update Evonet configs ([#56](https://github.com/dashevo/dashmate/issues/56)) + + + +# [0.12.5](https://github.com/dashevo/dashmate/compare/v0.12.4...v0.12.5) (2020-05-01) + + +### Bug Fixes + +* use updated sentinel image ([#41](https://github.com/dashevo/dashmate/issues/41)) + + + +# [0.12.4](https://github.com/dashevo/dashmate/compare/v0.12.3...v0.12.4) (2020-04-30) + + +### Bug Fixes + +* MongoDB replica set doesn't work sometimes ([#40](https://github.com/dashevo/dashmate/issues/40)) ([a5e31cd](https://github.com/dashevo/dashmate/commit/a5e31cd341bfd3e18240e3ee4c8f5dfeebfd249c)) + + + +# [0.12.3](https://github.com/dashevo/dashmate/compare/v0.12.2...v0.12.3) (2020-04-28) + + +### Bug Fixes + +* outdated genesis config for Tendermint ([#37](https://github.com/dashevo/dashmate/issues/37)) +* outdated persistent node IDs in Tendermint config ([#38](https://github.com/dashevo/dashmate/issues/38)) + + + +## [0.12.2](https://github.com/dashevo/dashmate/compare/v0.12.1...v0.12.2) (2020-04-22) + + +### Bug Fixes + +* update DPNS identities for evonet ([#31](https://github.com/dashevo/dashmate/issues/31)) + + +## [0.12.1](https://github.com/dashevo/dashmate/compare/v0.11.1...v0.12.0) (2020-04-21) + + +## Bug Fixes + +* `latest` envoy docker image tag is not present anymore ([#29](https://github.com/dashevo/dashmate/issues/29)) + + +# [0.12.0](https://github.com/dashevo/dashmate/compare/v0.11.1...v0.12.0) (2020-04-19) + + +### Bug Fixes + +* dash-cli doesn't work without default config ([#18](https://github.com/dashevo/dashmate/issues/18)) +* explicitly load core conf file ([#23](https://github.com/dashevo/dashmate/issues/23)) +* invalid gRPC Web configuration ([#25](https://github.com/dashevo/dashmate/issues/25), [#26](https://github.com/dashevo/dashmate/issues/26)) +* remove spork private key from сore config ([#11](https://github.com/dashevo/dashmate/issues/11)) + + +### Code Refactoring + +* tidy up services and configs ([#27](https://github.com/dashevo/dashmate/issues/27)) + + +### Features + +* add testnet preset ([#15](https://github.com/dashevo/dashmate/issues/15)) +* update to new Drive ([#21](https://github.com/dashevo/dashmate/issues/21), [#24](https://github.com/dashevo/dashmate/issues/24)) + + +### BREAKING CHANGES + +* data and config dir paths are changed +* `tendermint` service now called `drive_tendermint` +* `machine` is removed due to merging Machine into Drive +* new version of Drive is incompatible with 0.11 so you need to wipe data before run 0.12: + * drop `drive_mongodb` and `drive_leveldb` volumes + * `docker-commpose --env-file=.env. run drive_tendermint unsafe_reset_all` + + +## 0.11.1 (2020-03-17) + + +### Bug Fixes + +* update configs for Evonet ([#7](https://github.com/dashevo/dashmate/issues/7)) + + +# 0.11.0 (2020-03-09) + + +### Features + +* update configurations and docker-compose file for `local` and `evonet` envs ([230ea62](https://github.com/dashevo/dashmate/commit/230ea62a856b986127eb3b8e52bf7a19a5169818)) + + +### BREAKING CHANGES + +* `testnet` and `mainnet` is not supported anymore diff --git a/packages/dashmate/LICENSE b/packages/dashmate/LICENSE new file mode 100644 index 00000000000..5d0bd389978 --- /dev/null +++ b/packages/dashmate/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017 The Dash Core Group, Inc. + +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/packages/dashmate/README.md b/packages/dashmate/README.md new file mode 100644 index 00000000000..614f3de73cb --- /dev/null +++ b/packages/dashmate/README.md @@ -0,0 +1,401 @@ +# Dashmate + +[![Build Status](https://github.com/dashevo/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashevo/platform/actions/workflows/release.yml) +[![Release Date](https://img.shields.io/github/release-date/dashevo/platform)](https://github.com/dashevo/platform/releases/latest) +[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg)](https://github.com/RichardLitt/standard-readme) + +Distribution package for Dash Masternode installation + +## Table of Contents + +- [Install](#install) +- [Update](#update) +- [Usage](#usage) + - [Command line interface](#cli) + - [Setup node](#setup-node) + - [Configure node](#configure-node) + - [Start node](#start-node) + - [Stop node](#stop-node) + - [Restart node](#restart-node) + - [Show node status](#show-node-status) + - [Reset node data](#reset-node-data) + - [Full node](#full-node) + - [Node groups](#node-groups) + - [Development](#development) + - [Docker Compose](#docker-compose) +- [Contributing](#contributing) +- [License](#license) + +## Install + +### Dependencies + +* [Docker](https://docs.docker.com/engine/installation/) (v20.10+) +* [Node.js](https://nodejs.org/en/download/) (v16.0+, NPM v8.0+) + +For Linux installations you may optionally wish to follow the [post-installation steps](https://docs.docker.com/engine/install/linux-postinstall/) to manage Docker as a non-root user, otherwise you will have to run CLI and Docker commands with `sudo`. + +### Distribution package + +Use NPM to install dashmate globally in your system: +```bash +$ npm install -g dashmate +``` + +## Update + +```bash +$ dashmate stop +$ npm update -g dashmate +$ dashmate update +$ dashmate start +``` + +If the platform layer has been wiped, you must additionally reset platform data: + +```bash +$ dashmate stop +$ npm update -g dashmate +$ dashmate reset --platform-only --hard +$ dashmate update +$ dashmate setup -k +$ dashmate start +``` + +## Usage + +The package contains a CLI, Docker Compose and configuration files. + +### CLI + +The CLI can be used to perform routine tasks. Invoke the CLI with `dashmate` if linked during installation, or with `node bin/dashmate` if not linked. To list available commands, either run `dashmate` with no parameters or execute `dashmate help`. To list the help on any command just execute the command, followed by the `--help` option. + +### Setup node + +The `setup` command is used to quickly configure common node configurations. Arguments may be provided as options, otherwise they will be queried interactively with sensible values suggested. + +``` +USAGE + $ dashmate setup [PRESET] [NODE-TYPE] + +ARGUMENTS + PRESET (testnet|local) Node configuration preset + NODE-TYPE (masternode|fullnode) Node type + +OPTIONS + -d, --[no-]debug-logs enable debug logs + -i, --external-ip=external-ip external ip + -k, --operator-bls-private-key=operator-bls-private-key operator bls private key + -m, --miner-interval=miner-interval interval between blocks + -p, --funding-private-key=funding-private-key private key with more than 1000 dash for funding collateral + -v, --verbose use verbose mode for output + --node-count=node-count number of nodes to setup +``` + +Supported presets: + * `testnet` - a masternode or full node for testnet + * `local` - a node group to run a local dash network with the specified number of masternodes. To operate a group of nodes, use the [group commands](#node-groups) + +To setup a testnet masternode: +```bash +$ dashmate setup testnet masternode +``` + +#### Masternode registration + +If a funding private key is provided with the `--funding-private-key` option, the tool will automatically register your node on the network as a masternode. This functionality is only available when using the `testnet` preset. + +### Configure node + +The `config` command is used to manage your node configuration before starting the node. Several system configurations are provided as a starting point: + + - base - basic config for use as template + - local - template for local node configs + - testnet - testnet node configuration + +You can modify and use the system configs directly, or create your own. You can base your own configs on one of the system configs using the `dashmate config create CONFIG [FROM]` command. You must set a default config with `dashmate config default CONFIG` or specify a config with the `--config=` option when running commands. The `base` config is initially set as default. + +``` +USAGE + $ dashmate config + +OPTIONS + -v, --verbose use verbose mode for output + --config=config configuration name to use + +DESCRIPTION + Display configuration options for default config + +COMMANDS + config:create Create config + config:default Manage default config + config:envs Export config to envs + config:get Get config option + config:list List available configs + config:remove Remove config + config:set Set config option +``` + +### Start node + +The `start` command is used to start a node with the default or specified config. + +``` +USAGE + $ dashmate start + +OPTIONS + -v, --verbose use verbose mode for output + -w, --wait-for-readiness wait for nodes to be ready + --config=config configuration name to use +``` + +To start a masternode: +```bash +$ dashmate start +``` + +### Stop node + +The `stop` command is used to stop a running node. + +``` +USAGE + $ dashmate stop + +OPTIONS + -f, --force force stop nodes (skips running check) + -v, --verbose use verbose mode for output + --config=config configuration name to use +``` + +To stop a node: +```bash +$ dashmate stop +``` + +### Restart node + +The `restart` command is used to restart a node with the default or specified config. + +``` +USAGE + $ dashmate restart + +OPTIONS + -v, --verbose use verbose mode for output + --config=config configuration name to use +``` + +### Show node status + +The `status` command outputs status information relating to either the host, masternode or services. + +``` +USAGE + $ dashmate status + +OPTIONS + -v, --verbose use verbose mode for output + --config=config configuration name to use + +COMMANDS + status:core Show core status details + status:host Show host status details + status:masternode Show masternode status details + status:platform Show platform status details + status:services Show service status details +``` + +To show the host status: +```bash +$ dashmate status host +``` + +### Reset node data + +The `reset` command removes all data corresponding to the specified config and allows you to start a node from scratch. + +``` +USAGE + $ dashmate reset [--config ] [-v] [-h] [-f] [-p] + +FLAGS + -f, --force skip running services check + -h, --hard reset config as well as data + -p, --platform-only reset platform data only + -v, --verbose use verbose mode for output + --config= configuration name to use + +DESCRIPTION + Reset node data +``` + +With the hard reset mode enabled, the corresponding config will be reset as well. To proceed, running the node [setup](#setup-node) is required. + +To reset a node: +```bash +$ dashmate reset +``` + +### Full node + +It is also possible to start a full node instead of a masternode. Modify the config setting as follows: + +```bash +dashmate config set core.masternode.enable false +``` + +### Node groups + +CLI allows to [setup](#setup-node) and operate multiple nodes. Only the `local` preset is supported at the moment. + +#### Default group + +The [setup](#setup-node) command set corresponding group as default. To output the current default group or set another one as default use `group:default` command. + +``` +USAGE + $ dashmate group default [GROUP] + +ARGUMENTS + GROUP group name + +OPTIONS + -v, --verbose use verbose mode for output +``` + +#### List group configs + +The `group:list` command outputs a list of group configs. + +``` +USAGE + $ dashmate group list + +OPTIONS + -v, --verbose use verbose mode for output + --group=group group name to use +``` + +#### Start group nodes + +The `group:start` command is used to start a group of nodes belonging to the default group or a specified group. + +``` +USAGE + $ dashmate group start + +OPTIONS + -v, --verbose use verbose mode for output + -w, --wait-for-readiness wait for nodes to be ready + --group=group group name to use +``` + +#### Stop group nodes + +The `group:stop` command is used to stop group nodes belonging to the default group or a specified group. + +``` +USAGE + $ dashmate group stop + +OPTIONS + -f, --force force stop nodes (skips running check) + -v, --verbose use verbose mode for output + --group=group group name to use +``` + +#### Restart group nodes + +The `group:restart` command is used to restart group nodes belonging to the default group or a specified group. + +``` +USAGE + $ dashmate group restart + +OPTIONS + -v, --verbose use verbose mode for output + --group=group group name to use +``` + +#### Show group status + +The `group:status` command outputs group status information. + +``` +USAGE + $ dashmate group status + +OPTIONS + -v, --verbose use verbose mode for output + --group=group group name to use +``` + +#### Reset group nodes + +The `group:reset` command removes all data corresponding to the specified group and allows you to start group nodes from scratch. + +``` +USAGE + $ dashmate group reset [--group ] [-v] [--hard] [-f] [-p] + +FLAGS + -f, --force reset even running node + -p, --platform-only reset platform data only + -v, --verbose use verbose mode for output + --group= group name to use + --hard reset config as well as data + +DESCRIPTION + Reset group nodes +``` + +With the hard reset mode enabled, corresponding configs will be reset as well. To proceed, running the node [setup](#setup-node) is required. + +#### Create config group + +To group nodes together, set a group name to `group` option in corresponding configs. + +Create a group of two testnet nodes: +```bash +# create a new config using `testnet` config as template +dashmate config create testnet_2 testnet + +# combine configs into the group +dashmate config set --config=testnet group testnet +dashmate config set --config=testnet_2 group testnet + +# set the group as default +dashmate group default testnet +``` + +To start the group of nodes, ports and other required options need to be updated. + +### Development + +To start a local dash network, the `setup` command with the `local` preset can be used to generate configs, mine some dash, register masternodes and populate the nodes with the data required for local development. + +To allow developers quickly test changes to DAPI and Drive, a local path for this repository may be specified via the `platform.sourcePath` config options. A Docker image will be built from the provided path and then used by Dashmate. + +### Docker Compose + +If you want to use Docker Compose directly, you will need to pass a configuration as a dotenv file. You can output a config to a dotenv file for Docker Compose as follows: + +```bash +$ dashmate config envs --config=testnet --output-file .env.testnet +``` + +Then specify the created dotenv file as an option for the `docker compose` command: + +```bash +$ docker compose --env-file=.env.testnet up -d +``` + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs. + +## License + +[MIT](LICENSE) © Dash Core Group, Inc. diff --git a/packages/dashmate/bin/dashmate b/packages/dashmate/bin/dashmate new file mode 100755 index 00000000000..a1810f958c1 --- /dev/null +++ b/packages/dashmate/bin/dashmate @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +require('@oclif/core').run() + .then(require('@oclif/core/flush')) + // eslint-disable-next-line import/no-extraneous-dependencies + .catch(require('@oclif/core/handle')); diff --git a/packages/dashmate/bin/dashmate.cmd b/packages/dashmate/bin/dashmate.cmd new file mode 100644 index 00000000000..968fc30758e --- /dev/null +++ b/packages/dashmate/bin/dashmate.cmd @@ -0,0 +1,3 @@ +@echo off + +node "%~dp0\run" %* diff --git a/packages/dashmate/configs/migrations.js b/packages/dashmate/configs/migrations.js new file mode 100644 index 00000000000..705d08f283e --- /dev/null +++ b/packages/dashmate/configs/migrations.js @@ -0,0 +1,367 @@ +/* eslint-disable no-param-reassign */ +const lodashSet = require('lodash.set'); +const lodashGet = require('lodash.get'); + +const systemConfigs = require('./system'); + +const { NETWORK_TESTNET } = require('../src/constants'); + +module.exports = { + '0.17.2': (configFile) => { + Object.entries(configFile.configs).filter(([, config]) => config.network === NETWORK_TESTNET) + .forEach((config) => { + // Set DashPay contract ID and block height for testnet + // Set seed nodes for testnet tenderdash + lodashSet(config, 'platform.drive.tenderdash.p2p.seeds', systemConfigs.testnet.platform.drive.tenderdash.p2p.seeds); + lodashSet(config, 'platform.drive.tenderdash.p2p.persistentPeers', []); + }); + + return configFile; + }, + '0.17.3': (configFile) => { + Object.entries(configFile.configs).filter(([, config]) => config.network === NETWORK_TESTNET) + .forEach((config) => { + // Set DashPay contract ID and block height for testnet + lodashSet(config, 'platform.dashpay', systemConfigs.testnet.platform.dashpay); + }); + + return configFile; + }, + '0.17.4': (configFile) => { + Object.entries(configFile.configs).forEach(([name, config]) => { + let baseConfig = systemConfigs.base; + if (systemConfigs[name]) { + baseConfig = systemConfigs[name]; + } + + const previousStdoutLogLevel = lodashGet( + config, + 'platform.drive.abci.log.level', + ); + + // Set Drive's new logging variables + lodashSet(config, 'platform.drive.abci.log', baseConfig.platform.drive.abci.log); + + // Keep previous log level for stdout + if (previousStdoutLogLevel) { + lodashSet(config, 'platform.drive.abci.log.stdout.level', previousStdoutLogLevel); + } + }); + }, + '0.18.0': (configFile) => { + // Update docker images + Object.entries(configFile.configs) + .forEach(([, config]) => { + lodashSet(config, 'core.sentinel', systemConfigs.base.core.sentinel); + + lodashSet(config, 'core.docker.image', systemConfigs.base.core.docker.image); + + lodashSet( + config, + 'platform.drive.tenderdash.docker.image', + systemConfigs.base.platform.drive.tenderdash.docker.image, + ); + + lodashSet( + config, + 'platform.drive.abci.docker.image', + systemConfigs.base.platform.drive.abci.docker.image, + ); + + lodashSet( + config, + 'platform.dapi.api.docker.image', + systemConfigs.base.platform.dapi.api.docker.image, + ); + }); + + return configFile; + }, + '0.19.0': (configFile) => { + // Add default group name if not present + if (typeof configFile.defaultGroupName === 'undefined') { + configFile.defaultGroupName = null; + } + + Object.entries(configFile.configs) + .forEach(([, config]) => { + // Add groups + if (typeof config.group === 'undefined') { + config.group = null; + } + + if (typeof config.compose !== 'undefined') { + // Remove platform option for non platform configs + if (!config.compose.file.includes('docker-compose.platform.yml')) { + delete config.platform; + } + + // Remove compose option + delete config.compose; + } + + if (typeof config.platform !== 'undefined') { + // Add Tenderdash node ID + config.platform.drive.tenderdash.nodeId = null; + + // Add build options for DAPI and Drive + config.platform.drive.abci.docker.build = { + path: null, + }; + + config.platform.dapi.api.docker.build = { + path: null, + }; + + // Add consensus options + config.platform.drive.tenderdash.consensus = systemConfigs.base + .platform.drive.tenderdash.consensus; + + // Remove fallbacks + if (typeof config.platform.drive.skipAssetLockConfirmationValidation !== 'undefined') { + delete config.platform.drive.skipAssetLockConfirmationValidation; + } + + if (typeof config.platform.drive.passFakeAssetLockProofForTests !== 'undefined') { + delete config.platform.drive.passFakeAssetLockProofForTests; + } + + if (!config.platform.featureFlags) { + config.platform.featureFlags = systemConfigs.base.platform.featureFlags; + } + + // Remove Insight API configuration + if (config.platform.dapi.insight) { + delete config.platform.dapi.insight; + } + } + + // Update image versions + config.core.docker.image = systemConfigs.base.core.docker.image; + config.platform.dapi.api.docker.image = systemConfigs.base.platform.dapi.api.docker.image; + config.platform.drive.abci.docker.image = systemConfigs.base.platform + .drive.abci.docker.image; + }); + + // Update testnet seeds, genesis and contracts + configFile.configs.testnet.platform.drive.tenderdash.p2p.seeds = systemConfigs.testnet.platform + .drive.tenderdash.p2p.seeds; + configFile.configs.testnet.platform.drive.tenderdash.genesis = systemConfigs.testnet.platform + .drive.tenderdash.genesis; + + configFile.configs.testnet.platform.dpns = systemConfigs.testnet.platform.dpns; + configFile.configs.testnet.platform.dashpay = systemConfigs.testnet.platform.dashpay; + configFile.configs.testnet.platform.featureFlags = systemConfigs.testnet.platform.featureFlags; + + // Replace local config to group template + configFile.configs.local = systemConfigs.local; + + return configFile; + }, + '0.19.1': (configFile) => { + Object.entries(configFile.configs) + .forEach(([, config]) => { + // Update image version + config.core.docker.image = systemConfigs.base.core.docker.image; + }); + + return configFile; + }, + '0.19.2': (configFile) => { + Object.entries(configFile.configs) + .forEach(([, config]) => { + // Update image version + config.core.docker.image = systemConfigs.base.core.docker.image; + config.core.sentinel.docker.image = systemConfigs.base.core.sentinel.docker.image; + }); + + return configFile; + }, + '0.20.0': (configFile) => { + Object.entries(configFile.configs) + .forEach(([, config]) => { + // Core debug + if (typeof config.core.debug === 'undefined') { + config.core.debug = 0; + } + + if (config.platform) { + // Set empty block interval back to 3 + if (config.platform.drive.tenderdash.consensus.createEmptyBlocks.createEmptyBlocksInterval === '10s') { + // noinspection JSPrimitiveTypeWrapperUsage + config.platform.drive.tenderdash.consensus.createEmptyBlocks.createEmptyBlocksInterval = '3m'; + } + + // Tenderdash logging levels + if (typeof config.platform.drive.tenderdash.log === 'undefined') { + config.platform.drive.tenderdash.log = systemConfigs.base.platform.drive.tenderdash.log; + } + + // Remove validator set + if (typeof config.platform.drive.tenderdash.validatorKey === 'undefined') { + delete config.platform.drive.tenderdash.validatorKey; + } + + // Update images + config.platform.drive.tenderdash.docker.image = systemConfigs.base.platform + .drive.tenderdash.docker.image; + + config.platform.drive.abci.docker.image = systemConfigs.base.platform + .drive.abci.docker.image; + + config.platform.dapi.api.docker.image = systemConfigs.base.platform + .dapi.api.docker.image; + + config.core.docker.image = systemConfigs.base.core.docker.image; + + config.core.sentinel.docker.image = systemConfigs.base.core.sentinel.docker.image; + } + }); + + // Set validator set LLMQ Type + configFile.configs.base.platform.drive.abci.validatorSet.llmqType = systemConfigs.base + .platform.drive.abci.validatorSet.llmqType; + + configFile.configs.local.platform.drive.abci.validatorSet.llmqType = systemConfigs.local + .platform.drive.abci.validatorSet.llmqType; + + Object.entries(configFile.configs) + .filter(([, config]) => config.group === 'local' && config.platform) + .forEach(([, config]) => { + config.platform.drive.abci.validatorSet.llmqType = systemConfigs.local + .platform.drive.abci.validatorSet.llmqType; + }); + + // Update testnet seeds, genesis and contracts + configFile.configs.testnet.platform.drive.tenderdash.p2p.seeds = systemConfigs.testnet.platform + .drive.tenderdash.p2p.seeds; + configFile.configs.testnet.platform.drive.tenderdash.genesis = systemConfigs.testnet.platform + .drive.tenderdash.genesis; + + configFile.configs.testnet.platform.dpns = systemConfigs.testnet.platform.dpns; + configFile.configs.testnet.platform.dashpay = systemConfigs.testnet.platform.dashpay; + configFile.configs.testnet.platform.featureFlags = systemConfigs.testnet.platform.featureFlags; + + return configFile; + }, + '0.20.2': (configFile) => { + // Update contracts + configFile.configs.testnet.platform.drive.tenderdash.genesis = systemConfigs.testnet.platform + .drive.tenderdash.genesis; + configFile.configs.testnet.platform.dpns = systemConfigs.testnet.platform.dpns; + configFile.configs.testnet.platform.dashpay = systemConfigs.testnet.platform.dashpay; + configFile.configs.testnet.platform.featureFlags = systemConfigs.testnet.platform.featureFlags; + + return configFile; + }, + '0.21.0': (configFile) => { + Object.entries(configFile.configs) + .forEach(([, config]) => { + // Add median time to config + config.core.miner.mediantime = systemConfigs.base.core.miner.mediantime; + + if (config.platform) { + // Update images + config.platform.drive.tenderdash.docker.image = systemConfigs.base.platform + .drive.tenderdash.docker.image; + + config.platform.drive.abci.docker.image = systemConfigs.base.platform + .drive.abci.docker.image; + + config.platform.dapi.api.docker.image = systemConfigs.base.platform + .dapi.api.docker.image; + } + }); + + // Update contracts + configFile.configs.testnet.platform.drive.tenderdash.genesis = systemConfigs.testnet.platform + .drive.tenderdash.genesis; + configFile.configs.testnet.platform.dpns = systemConfigs.testnet.platform.dpns; + configFile.configs.testnet.platform.dashpay = systemConfigs.testnet.platform.dashpay; + configFile.configs.testnet.platform.featureFlags = systemConfigs.testnet.platform.featureFlags; + + return configFile; + }, + '0.21.7': (configFile) => { + Object.entries(configFile.configs) + .forEach(([, config]) => { + if (config.platform) { + // Remove build setting + delete config.platform.drive.abci.docker.build; + + delete config.platform.dapi.api.docker.build; + + config.platform.sourcePath = null; + } + }); + + return configFile; + }, + '0.22.0': (configFile) => { + Object.entries(configFile.configs) + .forEach(([, config]) => { + config.docker = systemConfigs[config.group || 'base'].docker; + + // Update images + config.core.docker.image = systemConfigs.base.core.docker.image; + + if (config.platform) { + if (!config.platform.masternodeRewardShares) { + config.platform.masternodeRewardShares = systemConfigs.base.platform + .masternodeRewardShares; + } + + config.platform.drive.tenderdash.docker.image = systemConfigs.base.platform + .drive.tenderdash.docker.image; + + config.platform.drive.abci.docker.image = systemConfigs.base.platform + .drive.abci.docker.image; + + config.platform.dapi.api.docker.image = systemConfigs.base.platform + .dapi.api.docker.image; + + delete config.platform.drive.mongodb; + } + }); + + // Update testnet contracts + configFile.configs.testnet.platform.drive.tenderdash.genesis = systemConfigs.testnet.platform + .drive.tenderdash.genesis; + configFile.configs.testnet.platform.dpns = systemConfigs.testnet.platform.dpns; + configFile.configs.testnet.platform.dashpay = systemConfigs.testnet.platform.dashpay; + configFile.configs.testnet.platform.featureFlags = systemConfigs.testnet.platform.featureFlags; + configFile.configs.testnet.platform.masternodeRewardShares = systemConfigs.testnet.platform + .masternodeRewardShares; + + return configFile; + }, + '0.22.2': (configFile) => { + Object.entries(configFile.configs) + .forEach(([, config]) => { + config.core.docker.image = systemConfigs.base.core.docker.image; + }); + + return configFile; + }, + '0.23.0': (configFile) => { + Object.entries(configFile.configs) + .forEach(([, config]) => { + if (config.platform) { + // Update images + config.platform.dpns = systemConfigs.base.platform.dpns; + config.platform.featureFlags = systemConfigs.base.platform.featureFlags; + config.platform.dashpay = systemConfigs.base.platform.dashpay; + config.platform.masternodeRewardShares = systemConfigs.base.platform + .masternodeRewardShares; + } + }); + + configFile.configs.testnet.platform.dpns = systemConfigs.testnet.platform.dpns; + configFile.configs.testnet.platform.dashpay = systemConfigs.testnet.platform.dashpay; + configFile.configs.testnet.platform.featureFlags = systemConfigs.testnet.platform.featureFlags; + configFile.configs.testnet.platform.masternodeRewardShares = systemConfigs.testnet.platform + .masternodeRewardShares; + + return configFile; + }, +}; diff --git a/packages/dashmate/configs/schema/configFileJsonSchema.js b/packages/dashmate/configs/schema/configFileJsonSchema.js new file mode 100644 index 00000000000..4a29ae4e9a0 --- /dev/null +++ b/packages/dashmate/configs/schema/configFileJsonSchema.js @@ -0,0 +1,20 @@ +module.exports = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + configFormatVersion: { + type: 'string', + }, + defaultConfigName: { + type: ['string', 'null'], + }, + defaultGroupName: { + type: ['string', 'null'], + }, + configs: { + type: 'object', + }, + }, + required: ['configFormatVersion', 'defaultConfigName', 'defaultGroupName', 'configs'], + additionalProperties: false, +}; diff --git a/packages/dashmate/configs/schema/configJsonSchema.js b/packages/dashmate/configs/schema/configJsonSchema.js new file mode 100644 index 00000000000..e46ffc825cc --- /dev/null +++ b/packages/dashmate/configs/schema/configJsonSchema.js @@ -0,0 +1,605 @@ +const { NETWORKS } = require('../../src/constants'); + +module.exports = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + definitions: { + docker: { + type: 'object', + properties: { + image: { + type: 'string', + minLength: 1, + }, + }, + required: ['image'], + additionalProperties: false, + }, + dockerBuild: { + type: 'object', + properties: { + image: { + type: 'string', + minLength: 1, + }, + build: { + type: 'object', + properties: { + path: { + type: ['string', 'null'], + minLength: 1, + }, + }, + additionalProperties: false, + required: ['path'], + }, + }, + required: ['image', 'build'], + additionalProperties: false, + }, + port: { + type: 'integer', + minimum: 0, + }, + tenderdashNodeAddress: { + type: 'object', + properties: { + id: { + type: 'string', + minLength: 1, + }, + host: { + type: 'string', + minLength: 1, + }, + port: { + $ref: '#/definitions/port', + }, + }, + required: ['id', 'host', 'port'], + additionalProperties: false, + }, + abciLogFile: { + type: 'object', + properties: { + level: { + type: 'string', + enum: ['fatal', 'error', 'warn', 'info', 'debug', 'trace', 'silent'], + }, + path: { + type: 'string', + minLength: 1, + }, + }, + additionalProperties: false, + required: ['level', 'path'], + }, + tenderdashLogModule: { + type: 'string', + enum: ['debug', 'info', 'error'], + }, + }, + properties: { + description: { + type: ['string', 'null'], + }, + group: { + type: ['string', 'null'], + }, + docker: { + type: 'object', + properties: { + network: { + type: 'object', + properties: { + subnet: { + type: 'string', + }, + }, + additionalProperties: false, + required: ['subnet'], + }, + }, + additionalProperties: false, + required: ['network'], + }, + core: { + type: 'object', + properties: { + docker: { + $ref: '#/definitions/docker', + }, + p2p: { + type: 'object', + properties: { + port: { + $ref: '#/definitions/port', + }, + seeds: { + type: 'array', + items: { + type: 'object', + properties: { + host: { + type: 'string', + minLength: 1, + }, + port: { + $ref: '#/definitions/port', + }, + }, + required: ['host', 'port'], + additionalProperties: false, + }, + }, + }, + required: ['port', 'seeds'], + additionalProperties: false, + }, + rpc: { + type: 'object', + properties: { + port: { + $ref: '#/definitions/port', + }, + user: { + type: 'string', + minLength: 1, + }, + password: { + type: 'string', + minLength: 1, + }, + }, + required: ['port', 'user', 'password'], + additionalProperties: false, + }, + spork: { + type: 'object', + properties: { + address: { + type: ['string', 'null'], + }, + privateKey: { + type: ['string', 'null'], + }, + }, + required: ['address', 'privateKey'], + additionalProperties: false, + }, + masternode: { + type: 'object', + properties: { + enable: { + type: 'boolean', + }, + operator: { + type: 'object', + properties: { + privateKey: { + type: ['string', 'null'], + }, + }, + required: ['privateKey'], + additionalProperties: false, + }, + }, + required: ['enable', 'operator'], + additionalProperties: false, + }, + miner: { + type: 'object', + properties: { + enable: { + type: 'boolean', + }, + interval: { + type: 'string', + pattern: '^[0-9]+(.[0-9]+)?(m|s|h)$', + }, + mediantime: { + type: ['integer', 'null'], + minimum: 0, + }, + address: { + type: ['string', 'null'], + }, + }, + required: ['enable', 'interval', 'mediantime', 'address'], + additionalProperties: false, + }, + sentinel: { + type: 'object', + properties: { + docker: { + $ref: '#/definitions/docker', + }, + }, + required: ['docker'], + additionalProperties: false, + }, + devnetName: { + type: ['string', 'null'], + minLength: 1, + }, + debug: { + type: 'integer', + enum: [0, 1], + }, + }, + required: ['docker', 'p2p', 'rpc', 'spork', 'masternode', 'miner', 'devnetName', 'debug'], + additionalProperties: false, + }, + platform: { + type: 'object', + properties: { + dapi: { + type: 'object', + properties: { + envoy: { + type: 'object', + properties: { + docker: { + $ref: '#/definitions/docker', + }, + http: { + type: 'object', + properties: { + port: { + $ref: '#/definitions/port', + }, + }, + required: ['port'], + additionalProperties: false, + }, + grpc: { + type: 'object', + properties: { + port: { + $ref: '#/definitions/port', + }, + }, + required: ['port'], + additionalProperties: false, + }, + rateLimiter: { + type: 'object', + properties: { + maxTokens: { + type: 'integer', + minimum: 0, + }, + tokensPerFill: { + type: 'integer', + minimum: 0, + }, + fillInterval: { + type: 'string', + pattern: '^[0-9]+(ms|s|m|h)$', + }, + enabled: { + type: 'boolean', + }, + }, + required: ['enabled', 'fillInterval', 'tokensPerFill', 'maxTokens'], + additionalProperties: false, + }, + }, + required: ['docker', 'http', 'grpc', 'rateLimiter'], + additionalProperties: false, + }, + api: { + type: 'object', + properties: { + docker: { + $ref: '#/definitions/docker', + }, + }, + required: ['docker'], + additionalProperties: false, + }, + }, + required: ['envoy', 'api'], + additionalProperties: false, + }, + drive: { + type: 'object', + properties: { + abci: { + type: 'object', + properties: { + docker: { + $ref: '#/definitions/docker', + }, + log: { + type: 'object', + properties: { + stdout: { + type: 'object', + properties: { + level: { + $ref: '#/definitions/abciLogFile/properties/level', + }, + }, + additionalProperties: false, + required: ['level'], + }, + prettyFile: { + $ref: '#/definitions/abciLogFile', + }, + jsonFile: { + $ref: '#/definitions/abciLogFile', + }, + }, + additionalProperties: false, + required: ['stdout', 'prettyFile', 'jsonFile'], + }, + validatorSet: { + type: 'object', + properties: { + llmqType: { + type: 'number', + // https://github.com/dashevo/dashcore-lib/blob/286c33a9d29d33f05d874c47a9b33764a0be0cf1/lib/constants/index.js#L42-L57 + enum: [1, 2, 3, 4, 100, 101, 102], + }, + }, + additionalProperties: false, + required: ['llmqType'], + }, + }, + additionalProperties: false, + required: ['docker', 'log', 'validatorSet'], + }, + tenderdash: { + type: 'object', + properties: { + docker: { + $ref: '#/definitions/docker', + }, + p2p: { + type: 'object', + properties: { + port: { + $ref: '#/definitions/port', + }, + persistentPeers: { + type: 'array', + items: { + $ref: '#/definitions/tenderdashNodeAddress', + }, + }, + seeds: { + type: 'array', + items: { + $ref: '#/definitions/tenderdashNodeAddress', + }, + }, + }, + required: ['port', 'persistentPeers', 'seeds'], + additionalProperties: false, + }, + consensus: { + type: 'object', + properties: { + createEmptyBlocks: { + type: 'boolean', + }, + createEmptyBlocksInterval: { + type: 'string', + pattern: '^[0-9]+(.[0-9]+)?(m|s|h)$', + }, + }, + additionalProperties: false, + required: ['createEmptyBlocks', 'createEmptyBlocksInterval'], + }, + log: { + type: 'object', + properties: { + level: { + type: 'object', + properties: { + 'abci-client': { + $ref: '#/definitions/tenderdashLogModule', + }, + blockchain: { + $ref: '#/definitions/tenderdashLogModule', + }, + consensus: { + $ref: '#/definitions/tenderdashLogModule', + }, + main: { + $ref: '#/definitions/tenderdashLogModule', + }, + mempool: { + $ref: '#/definitions/tenderdashLogModule', + }, + p2p: { + $ref: '#/definitions/tenderdashLogModule', + }, + 'rpc-server': { + $ref: '#/definitions/tenderdashLogModule', + }, + state: { + $ref: '#/definitions/tenderdashLogModule', + }, + statesync: { + $ref: '#/definitions/tenderdashLogModule', + }, + '*': { + $ref: '#/definitions/tenderdashLogModule', + }, + }, + minProperties: 1, + additionalProperties: false, + }, + format: { + type: 'string', + enum: ['plain', 'json'], + }, + }, + required: ['level', 'format'], + additionalProperties: false, + }, + rpc: { + type: 'object', + properties: { + port: { + $ref: '#/definitions/port', + }, + }, + required: ['port'], + additionalProperties: false, + }, + nodeKey: { + type: 'object', + }, + genesis: { + type: 'object', + }, + nodeId: { + type: ['string', 'null'], + }, + }, + required: ['docker', 'p2p', 'rpc', 'consensus', 'nodeKey', 'genesis', 'nodeId'], + additionalProperties: false, + }, + }, + required: ['abci', 'tenderdash'], + additionalProperties: false, + }, + dpns: { + type: 'object', + properties: { + contract: { + type: 'object', + properties: { + id: { + type: ['string', 'null'], + minLength: 1, + }, + }, + required: ['id'], + additionalProperties: false, + }, + ownerId: { + type: ['string', 'null'], + minLength: 1, + }, + masterPublicKey: { + type: ['string', 'null'], + minLength: 1, + }, + secondPublicKey: { + type: ['string', 'null'], + minLength: 1, + }, + }, + required: ['contract', 'ownerId', 'masterPublicKey', 'secondPublicKey'], + additionalProperties: false, + }, + dashpay: { + type: 'object', + properties: { + contract: { + type: 'object', + properties: { + id: { + type: ['string', 'null'], + minLength: 1, + }, + }, + required: ['id'], + additionalProperties: false, + }, + masterPublicKey: { + type: ['string', 'null'], + minLength: 1, + }, + secondPublicKey: { + type: ['string', 'null'], + minLength: 1, + }, + }, + required: ['contract', 'masterPublicKey', 'secondPublicKey'], + additionalProperties: false, + }, + featureFlags: { + type: 'object', + properties: { + contract: { + type: 'object', + properties: { + id: { + type: ['string', 'null'], + minLength: 1, + }, + }, + required: ['id'], + additionalProperties: false, + }, + ownerId: { + type: ['string', 'null'], + minLength: 1, + }, + masterPublicKey: { + type: ['string', 'null'], + minLength: 1, + }, + secondPublicKey: { + type: ['string', 'null'], + minLength: 1, + }, + }, + required: ['contract', 'ownerId', 'masterPublicKey', 'secondPublicKey'], + additionalProperties: false, + }, + sourcePath: { + type: ['string', 'null'], + minLength: 1, + }, + masternodeRewardShares: { + type: 'object', + properties: { + contract: { + type: 'object', + properties: { + id: { + type: ['string', 'null'], + minLength: 1, + }, + }, + required: ['id'], + additionalProperties: false, + }, + masterPublicKey: { + type: ['string', 'null'], + minLength: 1, + }, + secondPublicKey: { + type: ['string', 'null'], + minLength: 1, + }, + }, + required: ['contract', 'masterPublicKey', 'secondPublicKey'], + additionalProperties: false, + }, + }, + required: ['dapi', 'drive', 'dpns', 'dashpay', 'featureFlags', 'sourcePath', 'masternodeRewardShares'], + additionalProperties: false, + }, + externalIp: { + type: ['string', 'null'], + format: 'ipv4', + }, + network: { + type: 'string', + enum: NETWORKS, + }, + environment: { + type: 'string', + enum: ['development', 'production'], + }, + }, + required: ['description', 'group', 'core', 'externalIp', 'network', 'environment'], + additionalProperties: false, +}; diff --git a/packages/dashmate/configs/system/base.js b/packages/dashmate/configs/system/base.js new file mode 100644 index 00000000000..1b379232545 --- /dev/null +++ b/packages/dashmate/configs/system/base.js @@ -0,0 +1,187 @@ +const path = require('path'); + +const { + contractId: dpnsContractId, + ownerId: dpnsOwnerId, +} = require('@dashevo/dpns-contract/lib/systemIds'); + +const { + contractId: dashpayContractId, +} = require('@dashevo/dashpay-contract/lib/systemIds'); + +const { + contractId: featureFlagsContractId, + ownerId: featureFlagsOwnerId, +} = require('@dashevo/feature-flags-contract/lib/systemIds'); + +const { + contractId: masternodeRewardSharesContractId, +} = require('@dashevo/masternode-reward-shares-contract/lib/systemIds'); + +const { + NETWORK_TESTNET, + HOME_DIR_PATH, +} = require('../../src/constants'); + +module.exports = { + description: 'base config for use as template', + group: null, + docker: { + network: { + subnet: '172.24.24.0/24', + }, + }, + core: { + docker: { + image: 'dashpay/dashd:18.0.0-rc6', + }, + p2p: { + port: 20001, + seeds: [], + }, + rpc: { + port: 20002, + user: 'dashrpc', + password: 'rpcpassword', + }, + spork: { + address: null, + privateKey: null, + }, + masternode: { + enable: true, + operator: { + privateKey: null, + }, + }, + miner: { + enable: false, + interval: '2.5m', + mediantime: null, + address: null, + }, + sentinel: { + docker: { + image: 'dashpay/sentinel:1.6.0', + }, + }, + debug: 0, + devnetName: null, + }, + platform: { + dapi: { + envoy: { + docker: { + image: 'envoyproxy/envoy:v1.16-latest', + }, + http: { + port: 3000, + }, + grpc: { + port: 3010, + }, + rateLimiter: { + maxTokens: 300, + tokensPerFill: 150, + fillInterval: '60s', + enabled: true, + }, + }, + api: { + docker: { + image: 'dashpay/dapi:0.23-dev', + }, + }, + }, + drive: { + abci: { + docker: { + image: 'dashpay/drive:0.23-dev', + }, + log: { + stdout: { + level: 'info', + }, + prettyFile: { + level: 'silent', + path: path.join(HOME_DIR_PATH, 'logs', 'base', 'drive-pretty.log'), + }, + jsonFile: { + level: 'silent', + path: path.join(HOME_DIR_PATH, 'logs', 'base', 'drive-json.log'), + }, + }, + validatorSet: { + llmqType: 4, + }, + }, + tenderdash: { + docker: { + image: 'dashpay/tenderdash:0.8.0-dev.4', + }, + p2p: { + port: 26656, + persistentPeers: [], + seeds: [], + }, + rpc: { + port: 26657, + }, + consensus: { + createEmptyBlocks: true, + createEmptyBlocksInterval: '3m', + }, + log: { + level: { + main: 'info', + state: 'info', + statesync: 'info', + '*': 'error', + }, + format: 'plain', + }, + nodeKey: { + + }, + genesis: { + + }, + nodeId: null, + }, + }, + dpns: { + contract: { + id: dpnsContractId, + }, + ownerId: dpnsOwnerId, + masterPublicKey: null, + secondPublicKey: null, + }, + dashpay: { + contract: { + id: dashpayContractId, + }, + masterPublicKey: null, + secondPublicKey: null, + }, + featureFlags: { + contract: { + id: featureFlagsContractId, + }, + ownerId: featureFlagsOwnerId, + masterPublicKey: null, + secondPublicKey: null, + }, + sourcePath: null, + masternodeRewardShares: { + contract: { + id: masternodeRewardSharesContractId, + }, + masterPublicKey: null, + secondPublicKey: null, + }, + }, + externalIp: null, + network: NETWORK_TESTNET, + environment: 'production', +}; diff --git a/packages/dashmate/configs/system/index.js b/packages/dashmate/configs/system/index.js new file mode 100644 index 00000000000..56b87171120 --- /dev/null +++ b/packages/dashmate/configs/system/index.js @@ -0,0 +1,11 @@ +const baseConfig = require('./base'); +const localConfig = require('./local'); +const testnetConfig = require('./testnet'); +const mainnetConfig = require('./mainnet'); + +module.exports = { + base: baseConfig, + local: localConfig, + testnet: testnetConfig, + mainnet: mainnetConfig, +}; diff --git a/packages/dashmate/configs/system/local.js b/packages/dashmate/configs/system/local.js new file mode 100644 index 00000000000..78976c12ebf --- /dev/null +++ b/packages/dashmate/configs/system/local.js @@ -0,0 +1,35 @@ +const lodashMerge = require('lodash.merge'); + +const { + NETWORK_LOCAL, +} = require('../../src/constants'); + +const baseConfig = require('./base'); + +module.exports = lodashMerge({}, baseConfig, { + description: 'template for local configs', + docker: { + network: { + subnet: '172.24.24.0/24', + }, + }, + platform: { + dapi: { + envoy: { + rateLimiter: { + enabled: false, + }, + }, + }, + drive: { + abci: { + validatorSet: { + llmqType: 100, + }, + }, + }, + }, + externalIp: null, + environment: 'development', + network: NETWORK_LOCAL, +}); diff --git a/packages/dashmate/configs/system/mainnet.js b/packages/dashmate/configs/system/mainnet.js new file mode 100644 index 00000000000..3bcf0e127cb --- /dev/null +++ b/packages/dashmate/configs/system/mainnet.js @@ -0,0 +1,32 @@ +const lodashMerge = require('lodash.merge'); + +const { + NETWORK_MAINNET, +} = require('../../src/constants'); + +const baseConfig = require('./base'); + +const mainnetConfig = lodashMerge({}, baseConfig, { + description: 'node with mainnet configuration', + docker: { + network: { + subnet: '172.26.24.0/24', + }, + }, + core: { + docker: { + image: 'dashpay/dashd:0.17.0.3', + }, + p2p: { + port: 9999, + }, + rpc: { + port: 9998, + }, + }, + network: NETWORK_MAINNET, +}); + +delete mainnetConfig.platform; + +module.exports = mainnetConfig; diff --git a/packages/dashmate/configs/system/testnet.js b/packages/dashmate/configs/system/testnet.js new file mode 100644 index 00000000000..aae309dda39 --- /dev/null +++ b/packages/dashmate/configs/system/testnet.js @@ -0,0 +1,98 @@ +const lodashMerge = require('lodash.merge'); +const path = require('path'); + +const { + NETWORK_TESTNET, + HOME_DIR_PATH, +} = require('../../src/constants'); + +const baseConfig = require('./base'); + +module.exports = lodashMerge({}, baseConfig, { + description: 'node with testnet configuration', + docker: { + network: { + subnet: '172.25.24.0/24', + }, + }, + core: { + p2p: { + port: 19999, + }, + rpc: { + port: 19998, + }, + }, + platform: { + drive: { + abci: { + log: { + prettyFile: { + path: path.join(HOME_DIR_PATH, 'logs', 'testnet', 'drive-pretty.log'), + }, + jsonFile: { + path: path.join(HOME_DIR_PATH, 'logs', 'testnet', 'drive-json.log'), + }, + }, + }, + tenderdash: { + p2p: { + seeds: [ + { + id: '74907790a03b51ac062c8a1453dafd72a08668a3', + host: '54.189.200.56', + port: 26656, + }, + { + id: '2006632eb20e670923d13d4f53abc24468eaad4d', + host: '52.43.162.96', + port: 26656, + }, + ], + }, + genesis: { + genesis_time: '2021-07-22T12:57:05.429Z', + chain_id: 'dash-testnet-8', + initial_height: '0', + initial_core_chain_locked_height: 542300, + initial_proposal_core_chain_lock: null, + consensus_params: { + block: { + max_bytes: '22020096', + max_gas: '-1', + time_iota_ms: '5000', + }, + evidence: { + max_age: '100000', + max_age_num_blocks: '100000', + max_age_duration: '172800000000000', + }, + validator: { + pub_key_types: [ + 'bls12381', + ], + }, + version: {}, + }, + threshold_public_key: null, + quorum_type: '4', + quorum_hash: null, + app_hash: '', + }, + }, + }, + dpns: { + masterPublicKey: '022a5ffc9f92e005a02401c375f575b3aed5606fb24ddef5b3a05d55c66ba2a2f6', + }, + dashpay: { + masterPublicKey: '02c6bf10f8cc078866ed5466a0b5ea3a4e8db2a764ea5aa9cb75f22658664eb149', + }, + featureFlags: { + masterPublicKey: '033d57d03ba602acecfb6fd4ad66c5fdb9a739e163faefa901926bdf28063f9251', + }, + masternodeRewardShares: { + masterPublicKey: '02182c19827a5e3151feb965b2c6e6bbe57bb1f2fe7579595d76b672966da4e8e6', + }, + }, + network: NETWORK_TESTNET, +}); diff --git a/packages/dashmate/docker-compose.platform.build.yml b/packages/dashmate/docker-compose.platform.build.yml new file mode 100644 index 00000000000..6b63fef066a --- /dev/null +++ b/packages/dashmate/docker-compose.platform.build.yml @@ -0,0 +1,20 @@ +version: '3.7' + +services: + drive_abci: + build: + context: ${PLATFORM_SOURCE_PATH:?err} + dockerfile: ${PLATFORM_SOURCE_PATH:?err}/packages/js-drive/Dockerfile + image: drive:local + + dapi_api: + build: + context: ${PLATFORM_SOURCE_PATH:?err} + dockerfile: ${PLATFORM_SOURCE_PATH:?err}/packages/dapi/Dockerfile + image: dapi:local + + dapi_tx_filter_stream: + build: + context: ${PLATFORM_SOURCE_PATH:?err} + dockerfile: ${PLATFORM_SOURCE_PATH:?err}/packages/dapi/Dockerfile + image: dapi:local diff --git a/packages/dashmate/docker-compose.platform.yml b/packages/dashmate/docker-compose.platform.yml new file mode 100644 index 00000000000..ca37f0a2bc2 --- /dev/null +++ b/packages/dashmate/docker-compose.platform.yml @@ -0,0 +1,111 @@ +version: '3.7' + +services: + drive_abci: + image: ${PLATFORM_DRIVE_ABCI_DOCKER_IMAGE:?err} + restart: unless-stopped + depends_on: + - core + volumes: + - drive_abci_data:/platform/packages/js-drive/db + - ${PLATFORM_DRIVE_ABCI_LOG_PRETTY_DIRECTORY_PATH:?err}:/var/log/pretty + - ${PLATFORM_DRIVE_ABCI_LOG_JSON_DIRECTORY_PATH:?err}:/var/log/json + environment: + - CORE_JSON_RPC_USERNAME=${CORE_RPC_USER:?err} + - CORE_JSON_RPC_PASSWORD=${CORE_RPC_PASSWORD:?err} + - CORE_JSON_RPC_HOST=core + - CORE_JSON_RPC_PORT=${CORE_RPC_PORT:?err} + - CORE_ZMQ_HOST=core + - CORE_ZMQ_PORT=29998 + - DPNS_MASTER_PUBLIC_KEY=${PLATFORM_DPNS_MASTER_PUBLIC_KEY} + - DPNS_SECOND_PUBLIC_KEY=${PLATFORM_DPNS_SECOND_PUBLIC_KEY} + - DASHPAY_MASTER_PUBLIC_KEY=${PLATFORM_DASHPAY_MASTER_PUBLIC_KEY} + - DASHPAY_SECOND_PUBLIC_KEY=${PLATFORM_DASHPAY_SECOND_PUBLIC_KEY} + - FEATURE_FLAGS_MASTER_PUBLIC_KEY=${PLATFORM_FEATURE_FLAGS_MASTER_PUBLIC_KEY} + - FEATURE_FLAGS_SECOND_PUBLIC_KEY=${PLATFORM_FEATURE_FLAGS_SECOND_PUBLIC_KEY} + - MASTERNODE_REWARD_SHARES_MASTER_PUBLIC_KEY=${PLATFORM_MASTERNODE_REWARD_SHARES_MASTER_PUBLIC_KEY} + - MASTERNODE_REWARD_SHARES_SECOND_PUBLIC_KEY=${PLATFORM_MASTERNODE_REWARD_SHARES_SECOND_PUBLIC_KEY} + - NODE_ENV=${ENVIRONMENT:?err} + - LOG_STDOUT_LEVEL=${PLATFORM_DRIVE_ABCI_LOG_STDOUT_LEVEL:?err} + - LOG_PRETTY_FILE_LEVEL=${PLATFORM_DRIVE_ABCI_LOG_PRETTY_FILE_LEVEL:?err} + - LOG_PRETTY_FILE_PATH=/var/log/pretty/${PLATFORM_DRIVE_ABCI_LOG_PRETTY_FILE_NAME:?err} + - LOG_JSON_FILE_LEVEL=${PLATFORM_DRIVE_ABCI_LOG_JSON_FILE_LEVEL:?err} + - LOG_JSON_FILE_PATH=/var/log/json/${PLATFORM_DRIVE_ABCI_LOG_JSON_FILE_NAME:?err} + - INITIAL_CORE_CHAINLOCKED_HEIGHT=${PLATFORM_DRIVE_TENDERDASH_GENESIS_INITIAL_CORE_CHAIN_LOCKED_HEIGHT:-1} + - VALIDATOR_SET_LLMQ_TYPE=${PLATFORM_DRIVE_ABCI_VALIDATOR_SET_LLMQ_TYPE:?err} + - NETWORK=${NETWORK} + - TENDERDASH_P2P_PORT=${PLATFORM_DRIVE_TENDERDASH_P2P_PORT} + command: yarn workspace @dashevo/drive abci + + drive_tenderdash: + image: ${PLATFORM_DRIVE_TENDERDASH_DOCKER_IMAGE:?err} + restart: unless-stopped + depends_on: + - drive_abci + ports: + - ${PLATFORM_DRIVE_TENDERDASH_P2P_PORT:?err}:${PLATFORM_DRIVE_TENDERDASH_P2P_PORT:?err} # P2P + - 127.0.0.1:${PLATFORM_DRIVE_TENDERDASH_RPC_PORT:?err}:${PLATFORM_DRIVE_TENDERDASH_RPC_PORT:?err} # RPC + volumes: + - drive_tenderdash:/tenderdash + - ${DASHMATE_HOME_DIR:?err}/${CONFIG_NAME:?err}/platform/drive/tenderdash:/tenderdash/config:ro + + dapi_api: + image: ${PLATFORM_DAPI_API_DOCKER_IMAGE:?err} + restart: unless-stopped + depends_on: + - drive_tenderdash + - core + environment: + - API_JSON_RPC_PORT=3004 + - API_GRPC_PORT=3005 + - DASHCORE_RPC_HOST=core + - DASHCORE_RPC_PORT=${CORE_RPC_PORT:?err} + - DASHCORE_RPC_USER=${CORE_RPC_USER:?err} + - DASHCORE_RPC_PASS=${CORE_RPC_PASSWORD:?err} + - DASHCORE_ZMQ_HOST=core + - DASHCORE_ZMQ_PORT=29998 + - DASHCORE_P2P_HOST=core + - DASHCORE_P2P_PORT=${CORE_P2P_PORT:?err} + - DASHCORE_P2P_NETWORK=devnet + - NETWORK=devnet + - TENDERMINT_RPC_HOST=drive_tenderdash + - TENDERMINT_RPC_PORT=26657 + - NODE_ENV=${ENVIRONMENT:?err} + command: yarn workspace @dashevo/dapi api + + dapi_tx_filter_stream: + image: ${PLATFORM_DAPI_API_DOCKER_IMAGE:?err} + restart: unless-stopped + depends_on: + - core + environment: + - TX_FILTER_STREAM_GRPC_PORT=3006 + - DASHCORE_RPC_HOST=core + - DASHCORE_RPC_PORT=${CORE_RPC_PORT:?err} + - DASHCORE_RPC_USER=${CORE_RPC_USER:?err} + - DASHCORE_RPC_PASS=${CORE_RPC_PASSWORD:?err} + - DASHCORE_ZMQ_HOST=core + - DASHCORE_ZMQ_PORT=29998 + - DASHCORE_P2P_HOST=core + - DASHCORE_P2P_PORT=${CORE_P2P_PORT:?err} + - DASHCORE_P2P_NETWORK=devnet + - NETWORK=devnet + - TENDERMINT_RPC_HOST=drive_tenderdash + - TENDERMINT_RPC_PORT=26657 + command: yarn workspace @dashevo/dapi core-streams + + dapi_envoy: + image: ${PLATFORM_DAPI_ENVOY_DOCKER_IMAGE:?err} + restart: unless-stopped + ports: + - ${PLATFORM_DAPI_ENVOY_HTTP_PORT:?err}:10000 # JSON RPC and gRPC Web + - ${PLATFORM_DAPI_ENVOY_GRPC_PORT:?err}:50051 # gRPC Native + depends_on: + - dapi_api + - dapi_tx_filter_stream + volumes: + - ${DASHMATE_HOME_DIR:?err}/${CONFIG_NAME:?err}/platform/dapi/envoy/envoy.yaml:/etc/envoy/envoy.yaml + +volumes: + drive_abci_data: + drive_tenderdash: diff --git a/packages/dashmate/docker-compose.sentinel.yml b/packages/dashmate/docker-compose.sentinel.yml new file mode 100644 index 00000000000..88efb5be6dd --- /dev/null +++ b/packages/dashmate/docker-compose.sentinel.yml @@ -0,0 +1,16 @@ +version: '3.7' + +services: + sentinel: + image: ${CORE_SENTINEL_DOCKER_IMAGE:?err} + restart: unless-stopped + depends_on: + - core + environment: + - DEBUG=false + - RPCUSER=${CORE_RPC_USER:?err} + - RPCPASSWORD=${CORE_RPC_PASSWORD:?err} + - RPCHOST=core + - RPCPORT=${CORE_RPC_PORT:?err} + - NETWORK=${NETWORK?:err} + - SENTINEL_ARGS=-b diff --git a/packages/dashmate/docker-compose.yml b/packages/dashmate/docker-compose.yml new file mode 100644 index 00000000000..db5e227cf56 --- /dev/null +++ b/packages/dashmate/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3.7' + +services: + core: + image: ${CORE_DOCKER_IMAGE:?err} + restart: unless-stopped + ports: + - ${CORE_P2P_PORT:?err}:${CORE_P2P_PORT:?err} # P2P + - 127.0.0.1:${CORE_RPC_PORT:?err}:${CORE_RPC_PORT:?err} #RPC + volumes: + - core_data:/dash + - ${DASHMATE_HOME_DIR:?err}/${CONFIG_NAME:?err}/core/dash.conf:/dash/.dashcore/dash.conf + command: + - dashd + - -masternodeblsprivkey=${CORE_MASTERNODE_OPERATOR_PRIVATE_KEY} + +volumes: + core_data: + +networks: + default: + ipam: + config: + - subnet: ${DOCKER_NETWORK_SUBNET:?err} diff --git a/packages/dashmate/package.json b/packages/dashmate/package.json new file mode 100644 index 00000000000..6db99e0fa6c --- /dev/null +++ b/packages/dashmate/package.json @@ -0,0 +1,129 @@ +{ + "name": "dashmate", + "version": "0.23.0-dev.4", + "description": "Distribution package for Dash Masternode installation", + "main": "src/index.js", + "scripts": { + "lint": "eslint .", + "postpack": "rm -f oclif.manifest.json", + "posttest": "yarn lint", + "prepack": "oclif manifest && oclif readme", + "version": "oclif readme && git add README.md" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/dashevo/dashmate.git" + }, + "bin": "bin/dashmate", + "contributors": [ + { + "name": "Ivan Shumkov", + "email": "ivan@shumkov.ru", + "url": "https://github.com/shumkov" + }, + { + "name": "Djavid Gabibiyan", + "email": "djavid@dash.org", + "url": "https://github.com/jawid-h" + }, + { + "name": "Anton Suprunchuk", + "email": "anton.suprunchuk@dash.org", + "url": "https://github.com/antouhou" + }, + { + "name": "Konstantin Shuplenkov", + "email": "konstantin.shuplenkov@dash.org", + "url": "https://github.com/shuplenkov" + } + ], + "engines": { + "node": ">=12" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/dashevo/dashmate/issues" + }, + "homepage": "https://github.com/dashevo/dashmate#readme", + "dependencies": { + "@dashevo/dashcore-lib": "~0.19.39", + "@dashevo/dashd-rpc": "^2.3.2", + "@dashevo/dashpay-contract": "workspace:~", + "@dashevo/docker-compose": "^0.24.1", + "@dashevo/dpns-contract": "workspace:~", + "@dashevo/dpp": "workspace:~", + "@dashevo/feature-flags-contract": "workspace:~", + "@dashevo/masternode-reward-shares-contract": "workspace:~", + "@dashevo/wallet-lib": "workspace:~", + "@oclif/core": "^1.3.4", + "@oclif/plugin-help": "^5.1.11", + "ajv": "^8.6.0", + "ajv-formats": "^2.1.1", + "awilix": "^4.2.6", + "bls-signatures": "^0.2.5", + "chalk": "^4.1.0", + "dash": "workspace:~", + "dockerode": "^3.2.0", + "dot": "^1.1.3", + "dotenv": "^8.6.0", + "enquirer": "^2.3.6", + "glob": "^7.1.6", + "hasbin": "^1.2.3", + "jayson": "^3.3.4", + "listr2": "3.5.0", + "lodash.clonedeep": "^4.5.0", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "lodash.merge": "^4.6.2", + "lodash.set": "^4.3.2", + "memory-streams": "^0.1.3", + "node-fetch": "^2.6.1", + "node-graceful": "^3.0.1", + "pretty-bytes": "^5.3.0", + "pretty-ms": "^7.0.0", + "public-ip": "^4.0.1", + "rxjs": "^6.6.7", + "semver": "^7.3.2", + "strip-ansi": "^6.0.1", + "table": "^5.4.6" + }, + "devDependencies": { + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2", + "globby": "^11", + "oclif": "^2.4.5" + }, + "files": [ + "bin", + "configs", + "docker", + "src", + "templates", + "docker-compose.*", + "oclif.manifest.json", + "npm-shrinkwrap.json" + ], + "oclif": { + "additionalHelpFlags": [ + "-h" + ], + "commands": "./src/commands", + "bin": "dashmate", + "plugins": [ + "@oclif/plugin-help" + ], + "topics": { + "group": { + "description": "Orchestrate group of nodes" + }, + "wallet": { + "description": "Wallet related commands" + }, + "status": { + "description": "Show node status details" + } + }, + "topicSeparator": " " + } +} diff --git a/packages/dashmate/src/commands/config/create.js b/packages/dashmate/src/commands/config/create.js new file mode 100644 index 00000000000..ceab9a3f2b9 --- /dev/null +++ b/packages/dashmate/src/commands/config/create.js @@ -0,0 +1,41 @@ +const BaseCommand = require('../../oclif/command/BaseCommand'); + +class ConfigCreateCommand extends BaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {ConfigFile} configFile + * @return {Promise} + */ + async runWithDependencies( + { + config: configName, + from: fromConfigName, + }, + flags, + configFile, + ) { + configFile.createConfig(configName, fromConfigName); + + // eslint-disable-next-line no-console + console.log(`${configName} created`); + } +} + +ConfigCreateCommand.description = `Create config + +Creates a new configuration +`; + +ConfigCreateCommand.args = [{ + name: 'config', + required: true, + description: 'config name', +}, { + name: 'from', + required: false, + description: 'base new config on existing config', + default: 'base', +}]; + +module.exports = ConfigCreateCommand; diff --git a/packages/dashmate/src/commands/config/default.js b/packages/dashmate/src/commands/config/default.js new file mode 100644 index 00000000000..58fcae6990a --- /dev/null +++ b/packages/dashmate/src/commands/config/default.js @@ -0,0 +1,41 @@ +const BaseCommand = require('../../oclif/command/BaseCommand'); + +class ConfigDefaultCommand extends BaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {ConfigFile} configFile + * @return {Promise} + */ + async runWithDependencies( + { + config: configName, + }, + flags, + configFile, + ) { + if (configName === null) { + // eslint-disable-next-line no-console + console.log(configFile.getDefaultConfigName()); + } else { + configFile.setDefaultConfigName(configName); + + // eslint-disable-next-line no-console + console.log(`${configName} config set as default`); + } + } +} + +ConfigDefaultCommand.description = `Manage default config + +Shows default config name or sets another config as default +`; + +ConfigDefaultCommand.args = [{ + name: 'config', + required: false, + description: 'config name', + default: null, +}]; + +module.exports = ConfigDefaultCommand; diff --git a/packages/dashmate/src/commands/config/envs.js b/packages/dashmate/src/commands/config/envs.js new file mode 100644 index 00000000000..de807ef4f67 --- /dev/null +++ b/packages/dashmate/src/commands/config/envs.js @@ -0,0 +1,57 @@ +const fs = require('fs'); +const path = require('path'); + +const { Flags } = require('@oclif/core'); + +const { HOME_DIR_PATH } = require('../../constants'); + +const ConfigBaseCommand = require('../../oclif/command/ConfigBaseCommand'); + +class ConfigEnvsCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + args, + { + 'output-file': outputFile, + }, + config, + ) { + let envOutput = ''; + + for (const [key, value] of Object.entries(config.toEnvs())) { + envOutput += `${key}=${value}\n`; + } + + envOutput += `DASHMATE_HOME_DIR=${HOME_DIR_PATH}\n`; + + if (outputFile !== null) { + const outputFilePath = path.resolve(process.cwd(), outputFile); + + fs.writeFileSync(outputFilePath, envOutput, 'utf8'); + } else { + // eslint-disable-next-line no-console + console.log(envOutput); + } + } +} + +ConfigEnvsCommand.description = `Export config to envs + +Export configuration options as Docker Compose envs +`; + +ConfigEnvsCommand.flags = { + ...ConfigBaseCommand.flags, + 'output-file': Flags.string({ + char: 'o', + description: 'output to file', + default: null, + }), +}; + +module.exports = ConfigEnvsCommand; diff --git a/packages/dashmate/src/commands/config/get.js b/packages/dashmate/src/commands/config/get.js new file mode 100644 index 00000000000..03091dff4ca --- /dev/null +++ b/packages/dashmate/src/commands/config/get.js @@ -0,0 +1,39 @@ +const ConfigBaseCommand = require('../../oclif/command/ConfigBaseCommand'); + +class ConfigGetCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + { + option: optionPath, + }, + flags, + config, + ) { + // eslint-disable-next-line no-console + console.log( + config.get(optionPath), + ); + } +} + +ConfigGetCommand.description = `Get config option + +Gets a configuration option from the specified config +`; + +ConfigGetCommand.args = [{ + name: 'option', + required: true, + description: 'option path', +}]; + +ConfigGetCommand.flags = { + ...ConfigBaseCommand.flags, +}; + +module.exports = ConfigGetCommand; diff --git a/packages/dashmate/src/commands/config/index.js b/packages/dashmate/src/commands/config/index.js new file mode 100644 index 00000000000..f325d7fe1de --- /dev/null +++ b/packages/dashmate/src/commands/config/index.js @@ -0,0 +1,36 @@ +const { inspect } = require('util'); + +const ConfigBaseCommand = require('../../oclif/command/ConfigBaseCommand'); + +class ConfigCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + args, + flags, + config, + ) { + const output = `${config.getName()} config:\n\n${inspect( + config.getOptions(), + { colors: true, depth: null, maxArrayLength: 2 }, + )}`; + + // eslint-disable-next-line no-console + console.log(output); + } +} + +ConfigCommand.description = `Show default config + +Display configuration options for default config +`; + +ConfigCommand.flags = { + ...ConfigBaseCommand.flags, +}; + +module.exports = ConfigCommand; diff --git a/packages/dashmate/src/commands/config/list.js b/packages/dashmate/src/commands/config/list.js new file mode 100644 index 00000000000..428230b0328 --- /dev/null +++ b/packages/dashmate/src/commands/config/list.js @@ -0,0 +1,29 @@ +const { table } = require('table'); + +const BaseCommand = require('../../oclif/command/BaseCommand'); + +class ConfigListCommand extends BaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {ConfigFile} configFile + * @return {Promise} + */ + async runWithDependencies( + args, + flags, + configFile, + ) { + const rows = configFile.getAllConfigs() + .map((config) => [config.getName(), config.get('description')]); + + const output = table(rows); + + // eslint-disable-next-line no-console + console.log(output); + } +} + +ConfigListCommand.description = 'List available configs'; + +module.exports = ConfigListCommand; diff --git a/packages/dashmate/src/commands/config/remove.js b/packages/dashmate/src/commands/config/remove.js new file mode 100644 index 00000000000..86e2e30d156 --- /dev/null +++ b/packages/dashmate/src/commands/config/remove.js @@ -0,0 +1,41 @@ +const BaseCommand = require('../../oclif/command/BaseCommand'); + +const systemConfigs = require('../../../configs/system'); + +class ConfigRemoveCommand extends BaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {ConfigFile} configFile + * @return {Promise} + */ + async runWithDependencies( + { + config: configName, + }, + flags, + configFile, + ) { + if (Object.keys(systemConfigs).includes(configName)) { + throw new Error(`system config ${configName} can't be removed`); + } + + configFile.removeConfig(configName); + + // eslint-disable-next-line no-console + console.log(`${configName} removed`); + } +} + +ConfigRemoveCommand.description = `Remove config + +Removes a configuration +`; + +ConfigRemoveCommand.args = [{ + name: 'config', + required: true, + description: 'config name', +}]; + +module.exports = ConfigRemoveCommand; diff --git a/packages/dashmate/src/commands/config/set.js b/packages/dashmate/src/commands/config/set.js new file mode 100644 index 00000000000..80f984f7be0 --- /dev/null +++ b/packages/dashmate/src/commands/config/set.js @@ -0,0 +1,49 @@ +const ConfigBaseCommand = require('../../oclif/command/ConfigBaseCommand'); + +class ConfigSetCommand extends ConfigBaseCommand { + /** + * @param args + * @param flags + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + { + option: optionPath, + value: optionValue, + }, + flags, + config, + ) { + if (optionValue === 'null') { + // eslint-disable-next-line no-param-reassign + optionValue = null; + } + + config.set(optionPath, optionValue); + + // eslint-disable-next-line no-console + console.log(`${optionPath} set to ${config.get(optionPath)}`); + } +} + +ConfigSetCommand.description = `Set config option + +Sets a configuration option in the default config +`; + +ConfigSetCommand.args = [{ + name: 'option', + required: true, + description: 'option path', +}, { + name: 'value', + required: true, + description: 'the option value', +}]; + +ConfigSetCommand.flags = { + ...ConfigBaseCommand.flags, +}; + +module.exports = ConfigSetCommand; diff --git a/packages/dashmate/src/commands/group/default.js b/packages/dashmate/src/commands/group/default.js new file mode 100644 index 00000000000..85d6017b362 --- /dev/null +++ b/packages/dashmate/src/commands/group/default.js @@ -0,0 +1,41 @@ +const BaseCommand = require('../../oclif/command/BaseCommand'); + +class GroupDefaultCommand extends BaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {ConfigFile} configFile + * @return {Promise} + */ + async runWithDependencies( + { + group: groupName, + }, + flags, + configFile, + ) { + if (groupName === null) { + // eslint-disable-next-line no-console + console.log(configFile.getDefaultGroupName()); + } else { + configFile.setDefaultGroupName(groupName); + + // eslint-disable-next-line no-console + console.log(`${groupName} group set as default`); + } + } +} + +GroupDefaultCommand.description = `Manage default group + +Shows default group name or sets another group as default +`; + +GroupDefaultCommand.args = [{ + name: 'group', + required: false, + description: 'group name', + default: null, +}]; + +module.exports = GroupDefaultCommand; diff --git a/packages/dashmate/src/commands/group/list.js b/packages/dashmate/src/commands/group/list.js new file mode 100644 index 00000000000..1a7bce844dd --- /dev/null +++ b/packages/dashmate/src/commands/group/list.js @@ -0,0 +1,32 @@ +const { table } = require('table'); + +const GroupBaseCommand = require('../../oclif/command/GroupBaseCommand'); + +class GroupListCommand extends GroupBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {Config[]} configGroup + * @return {Promise} + */ + async runWithDependencies( + args, + flags, + configGroup, + ) { + const rows = configGroup.map((config) => [config.getName(), config.get('description')]); + + const output = table(rows); + + // eslint-disable-next-line no-console + console.log(output); + } +} + +GroupListCommand.description = 'List available groups'; + +GroupListCommand.flags = { + ...GroupBaseCommand.flags, +}; + +module.exports = GroupListCommand; diff --git a/packages/dashmate/src/commands/group/reset.js b/packages/dashmate/src/commands/group/reset.js new file mode 100644 index 00000000000..d108cb302dc --- /dev/null +++ b/packages/dashmate/src/commands/group/reset.js @@ -0,0 +1,147 @@ +const { Listr } = require('listr2'); + +const { Flags } = require('@oclif/core'); + +const GroupBaseCommand = require('../../oclif/command/GroupBaseCommand'); +const MuteOneLineError = require('../../oclif/errors/MuteOneLineError'); + +class GroupResetCommand extends GroupBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {isSystemConfig} isSystemConfig + * @param {resetNodeTask} resetNodeTask + * @param {Config[]} configGroup + * @param {configureCoreTask} configureCoreTask + * @param {configureTenderdashTask} configureTenderdashTask + * @param {generateToAddressTask} generateToAddressTask + * @param {ConfigFile} configFile + * @param {Object[]} systemConfigs + * @return {Promise} + */ + async runWithDependencies( + args, + { + verbose: isVerbose, + hard: isHardReset, + force: isForce, + 'platform-only': isPlatformOnlyReset, + }, + isSystemConfig, + resetNodeTask, + configGroup, + configureCoreTask, + configureTenderdashTask, + generateToAddressTask, + configFile, + systemConfigs, + ) { + const groupName = configGroup[0].get('group'); + + if (isHardReset && !isSystemConfig(groupName)) { + throw new Error(`Cannot hard reset non-system config group "${configGroup[0].get('group')}"`); + } + + const baseConfig = systemConfigs.base; + + const amount = 100; + + const tasks = new Listr( + [ + { + title: `Reset ${groupName} nodes`, + task: () => new Listr(configGroup.map((config) => ({ + title: `Reset ${config.getName()} node`, + task: (ctx) => { + ctx.skipPlatformInitialization = true; + + if (config.has('platform')) { + config.set('platform.dpns', baseConfig.platform.dpns); + config.set('platform.dashpay', baseConfig.platform.dashpay); + config.set('platform.featureFlags', baseConfig.platform.featureFlags); + config.set('platform.masternodeRewardShares', baseConfig.platform.masternodeRewardShares); + + // TODO: Should stay the same + config.set('platform.drive.tenderdash.nodeId', baseConfig.platform.drive.tenderdash.nodeId); + config.set('platform.drive.tenderdash.nodeKey', baseConfig.platform.drive.tenderdash.nodeKey); + config.set('platform.drive.tenderdash.genesis', baseConfig.platform.drive.tenderdash.genesis); + } + + if (!ctx.isPlatformOnlyReset) { + config.set('core.masternode.operator.privateKey', baseConfig.core.masternode.operator.privateKey); + } + + return resetNodeTask(config); + }, + }))), + }, + { + enabled: (ctx) => ctx.isHardReset, + title: 'Delete node configs', + task: () => ( + configGroup.forEach((config) => configFile.removeConfig(config.getName())) + ), + }, + { + enabled: (ctx) => !ctx.isHardReset, + title: 'Configure Tenderdash nodes', + task: () => configureTenderdashTask(configGroup), + }, + { + enabled: (ctx) => !ctx.isHardReset && !ctx.isPlatformOnlyReset, + title: 'Configure Core nodes', + task: () => configureCoreTask(configGroup), + }, + { + // in case we don't need to register masternodes + title: `Generate ${amount} dash to local wallet`, + enabled: (ctx) => !ctx.isHardReset, + skip: (ctx) => !!ctx.fundingPrivateKeyString, + task: () => generateToAddressTask(configGroup[0], amount), + }, + ], + { + renderer: isVerbose ? 'verbose' : 'default', + rendererOptions: { + showTimer: isVerbose, + clearOutput: false, + collapse: false, + showSubtasks: true, + }, + }, + ); + + try { + await tasks.run({ + isHardReset, + isForce, + isPlatformOnlyReset, + isVerbose, + }); + } catch (e) { + throw new MuteOneLineError(e); + } + } +} + +GroupResetCommand.description = 'Reset group nodes'; + +GroupResetCommand.flags = { + ...GroupBaseCommand.flags, + hard: Flags.boolean({ + description: 'reset config as well as data', + default: false, + }), + force: Flags.boolean({ + char: 'f', + description: 'reset even running node', + default: false, + }), + 'platform-only': Flags.boolean({ + char: 'p', + description: 'reset platform data only', + default: false, + }), +}; + +module.exports = GroupResetCommand; diff --git a/packages/dashmate/src/commands/group/restart.js b/packages/dashmate/src/commands/group/restart.js new file mode 100644 index 00000000000..6570523ecf2 --- /dev/null +++ b/packages/dashmate/src/commands/group/restart.js @@ -0,0 +1,74 @@ +const { Listr } = require('listr2'); +const GroupBaseCommand = require('../../oclif/command/GroupBaseCommand'); +const MuteOneLineError = require('../../oclif/errors/MuteOneLineError'); + +class GroupRestartCommand extends GroupBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {DockerCompose} dockerCompose + * @param {stopNodeTask} stopNodeTask + * @param {startGroupNodesTask} startGroupNodesTask + * @param {Config[]} configGroup + * @return {Promise} + */ + async runWithDependencies( + args, + { + verbose: isVerbose, + }, + dockerCompose, + stopNodeTask, + startGroupNodesTask, + configGroup, + ) { + const groupName = configGroup[0].get('group'); + + const tasks = new Listr({ + title: `Restart ${groupName} nodes`, + task: async () => ( + new Listr([ + { + title: 'Stop nodes', + task: () => ( + // So we stop the miner first, as there's a chance that MNs will get banned + // if the miner is still running when stopping them + new Listr(configGroup.reverse().map((config) => ({ + task: () => stopNodeTask(config), + }))) + ), + }, + { + title: 'Start nodes', + task: () => startGroupNodesTask(configGroup), + }, + ]) + ), + }, + { + renderer: isVerbose ? 'verbose' : 'default', + rendererOptions: { + showTimer: isVerbose, + clearOutput: false, + collapse: false, + showSubtasks: true, + }, + }); + + try { + await tasks.run({ + isVerbose, + }); + } catch (e) { + throw new MuteOneLineError(e); + } + } +} + +GroupRestartCommand.description = 'Restart group nodes'; + +GroupRestartCommand.flags = { + ...GroupBaseCommand.flags, +}; + +module.exports = GroupRestartCommand; diff --git a/packages/dashmate/src/commands/group/start.js b/packages/dashmate/src/commands/group/start.js new file mode 100644 index 00000000000..46e8b9002af --- /dev/null +++ b/packages/dashmate/src/commands/group/start.js @@ -0,0 +1,67 @@ +const { Listr } = require('listr2'); + +const { Flags } = require('@oclif/core'); + +const GroupBaseCommand = require('../../oclif/command/GroupBaseCommand'); +const MuteOneLineError = require('../../oclif/errors/MuteOneLineError'); + +class GroupStartCommand extends GroupBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {DockerCompose} dockerCompose + * @param {startNodeTask} startNodeTask + * @param {Config[]} configGroup + * @param {startGroupNodesTask} startGroupNodesTask + * @return {Promise} + */ + async runWithDependencies( + args, + { + 'wait-for-readiness': waitForReadiness, + verbose: isVerbose, + }, + dockerCompose, + startNodeTask, + configGroup, + startGroupNodesTask, + ) { + const groupName = configGroup[0].get('group'); + + const tasks = new Listr( + [ + { + title: `Start ${groupName} nodes`, + task: () => startGroupNodesTask(configGroup), + }, + ], + { + renderer: isVerbose ? 'verbose' : 'default', + rendererOptions: { + showTimer: isVerbose, + clearOutput: false, + collapse: false, + showSubtasks: true, + }, + }, + ); + + try { + await tasks.run({ + waitForReadiness, + isVerbose, + }); + } catch (e) { + throw new MuteOneLineError(e); + } + } +} + +GroupStartCommand.description = 'Start group nodes'; + +GroupStartCommand.flags = { + ...GroupBaseCommand.flags, + 'wait-for-readiness': Flags.boolean({ char: 'w', description: 'wait for nodes to be ready', default: false }), +}; + +module.exports = GroupStartCommand; diff --git a/packages/dashmate/src/commands/group/status.js b/packages/dashmate/src/commands/group/status.js new file mode 100644 index 00000000000..cca231e12b1 --- /dev/null +++ b/packages/dashmate/src/commands/group/status.js @@ -0,0 +1,40 @@ +const { Flags } = require('@oclif/core'); +const { OUTPUT_FORMATS } = require('../../constants'); + +const GroupBaseCommand = require('../../oclif/command/GroupBaseCommand'); + +class GroupStatusCommand extends GroupBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {outputStatusOverview} outputStatusOverview + * @param {Config[]} configGroup + * @return {Promise} + */ + async runWithDependencies( + args, + flags, + outputStatusOverview, + configGroup, + ) { + for (const config of configGroup) { + // eslint-disable-next-line no-console + console.log(`Node ${config.getName()}`); + + await outputStatusOverview(config, flags.format); + } + } +} + +GroupStatusCommand.description = 'Show group status overview'; + +GroupStatusCommand.flags = { + ...GroupBaseCommand.flags, + format: Flags.string({ + description: 'display output format', + default: OUTPUT_FORMATS.PLAIN, + options: Object.values(OUTPUT_FORMATS), + }), +}; + +module.exports = GroupStatusCommand; diff --git a/packages/dashmate/src/commands/group/stop.js b/packages/dashmate/src/commands/group/stop.js new file mode 100644 index 00000000000..a220ccea4a4 --- /dev/null +++ b/packages/dashmate/src/commands/group/stop.js @@ -0,0 +1,73 @@ +const { Flags } = require('@oclif/core'); +const { Listr } = require('listr2'); +const GroupBaseCommand = require('../../oclif/command/GroupBaseCommand'); +const MuteOneLineError = require('../../oclif/errors/MuteOneLineError'); + +class GroupStopCommand extends GroupBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {DockerCompose} dockerCompose + * @param {stopNodeTask} stopNodeTask + * @param {Config[]} configGroup + * @return {Promise} + */ + async runWithDependencies( + args, + { + force: isForce, + verbose: isVerbose, + }, + dockerCompose, + stopNodeTask, + configGroup, + ) { + const groupName = configGroup[0].get('group'); + + const tasks = new Listr( + [ + { + title: `Stop ${groupName} nodes`, + task: () => ( + // So we stop the miner first, as there's a chance that MNs will get banned + // if the miner is still running when stopping them + new Listr(configGroup.reverse().map((config) => ({ + task: () => stopNodeTask(config), + }))) + ), + }, + ], + { + renderer: isVerbose ? 'verbose' : 'default', + rendererOptions: { + showTimer: isVerbose, + clearOutput: false, + collapse: false, + showSubtasks: true, + }, + }, + ); + + try { + await tasks.run({ + isVerbose, + isForce, + }); + } catch (e) { + throw new MuteOneLineError(e); + } + } +} + +GroupStopCommand.description = 'Stop group nodes'; + +GroupStopCommand.flags = { + ...GroupBaseCommand.flags, + force: Flags.boolean({ + char: 'f', + description: 'force stop even if any is running', + default: false, + }), +}; + +module.exports = GroupStopCommand; diff --git a/packages/dashmate/src/commands/platform/feature-flag.js b/packages/dashmate/src/commands/platform/feature-flag.js new file mode 100644 index 00000000000..66c0c8f5325 --- /dev/null +++ b/packages/dashmate/src/commands/platform/feature-flag.js @@ -0,0 +1,90 @@ +const { Listr } = require('listr2'); + +const featureFlagTypes = require('@dashevo/feature-flags-contract/lib/featureFlagTypes'); + +const ConfigBaseCommand = require('../../oclif/command/ConfigBaseCommand'); +const MuteOneLineError = require('../../oclif/errors/MuteOneLineError'); + +class FeatureFlagCommand extends ConfigBaseCommand { + /** + * + * @param {Object} args + * @param {Object} flags + * @param {featureFlagTask} featureFlagTask + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + { + name: featureFlagName, + height, + 'hd-private-key': hdPrivateKey, + 'dapi-address': dapiAddress, + }, + { + verbose: isVerbose, + }, + featureFlagTask, + config, + ) { + const tasks = new Listr([ + { + title: 'Initialize Feature Flags', + task: () => featureFlagTask(config), + }, + ], + { + renderer: isVerbose ? 'verbose' : 'default', + rendererOptions: { + showTimer: isVerbose, + clearOutput: false, + collapse: false, + showSubtasks: true, + }, + }); + + try { + await tasks.run({ + featureFlagName, + height, + hdPrivateKey, + dapiAddress, + }); + } catch (e) { + throw new MuteOneLineError(e); + } + } +} + +FeatureFlagCommand.description = `Feature flags +... +Register feature flags +`; + +FeatureFlagCommand.args = [{ + name: 'name', + required: true, + description: 'name of the feature flag to process', + options: Object.values(featureFlagTypes), +}, +{ + name: 'height', + required: true, + description: 'height at which feature flag should be enabled', +}, +{ + name: 'hd-private-key', + required: true, + description: 'feature flag hd private key', +}, +{ + name: 'dapi-address', + required: true, + description: 'DAPI address to send feature flags transitions to', +}]; + +FeatureFlagCommand.flags = { + ...ConfigBaseCommand.flags, +}; + +module.exports = FeatureFlagCommand; diff --git a/packages/dashmate/src/commands/reset.js b/packages/dashmate/src/commands/reset.js new file mode 100644 index 00000000000..17a25f2ad8f --- /dev/null +++ b/packages/dashmate/src/commands/reset.js @@ -0,0 +1,81 @@ +const { Listr } = require('listr2'); + +const { Flags } = require('@oclif/core'); + +const ConfigBaseCommand = require('../oclif/command/ConfigBaseCommand'); + +const MuteOneLineError = require('../oclif/errors/MuteOneLineError'); + +class ResetCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {isSystemConfig} isSystemConfig + * @param {Config} config + * @param {resetNodeTask} resetNodeTask + * + * @return {Promise} + */ + async runWithDependencies( + args, + { + verbose: isVerbose, + hard: isHardReset, + force: isForce, + 'platform-only': isPlatformOnlyReset, + }, + isSystemConfig, + config, + resetNodeTask, + ) { + if (isHardReset && !isSystemConfig(config.getName())) { + throw new Error(`Cannot hard reset non-system config "${config.getName()}"`); + } + + if (!config.has('platform') && isPlatformOnlyReset) { + throw new Error('Cannot reset platform only if platform services are not enabled in config'); + } + + const tasks = new Listr([ + { + title: `Reset ${config.getName()} node`, + task: () => resetNodeTask(config), + }, + ], + { + renderer: isVerbose ? 'verbose' : 'default', + rendererOptions: { + showTimer: isVerbose, + clearOutput: false, + collapse: false, + showSubtasks: true, + }, + }); + + try { + await tasks.run({ + isHardReset, + isPlatformOnlyReset, + isForce, + isVerbose, + }); + } catch (e) { + throw new MuteOneLineError(e); + } + } +} + +ResetCommand.description = `Reset node data + +Reset node data +`; + +ResetCommand.flags = { + ...ConfigBaseCommand.flags, + hard: Flags.boolean({ char: 'h', description: 'reset config as well as data', default: false }), + force: Flags.boolean({ char: 'f', description: 'skip running services check', default: false }), + 'platform-only': Flags.boolean({ char: 'p', description: 'reset platform data only', default: false }), + verbose: Flags.boolean({ char: 'v', description: 'use verbose mode for output', default: false }), +}; + +module.exports = ResetCommand; diff --git a/packages/dashmate/src/commands/restart.js b/packages/dashmate/src/commands/restart.js new file mode 100644 index 00000000000..f1b47b657d9 --- /dev/null +++ b/packages/dashmate/src/commands/restart.js @@ -0,0 +1,62 @@ +const { Listr } = require('listr2'); + +const ConfigBaseCommand = require('../oclif/command/ConfigBaseCommand'); + +const MuteOneLineError = require('../oclif/errors/MuteOneLineError'); + +class RestartCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {DockerCompose} dockerCompose + * @param {restartNodeTask} restartNodeTask + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + args, + { + verbose: isVerbose, + }, + dockerCompose, + restartNodeTask, + config, + ) { + const tasks = new Listr( + [ + { + title: `Restarting ${config.getName()} node`, + task: () => restartNodeTask(config), + }, + ], + { + renderer: isVerbose ? 'verbose' : 'default', + rendererOptions: { + showTimer: isVerbose, + clearOutput: false, + collapse: false, + showSubtasks: true, + }, + }, + ); + + try { + await tasks.run({ + isVerbose, + }); + } catch (e) { + throw new MuteOneLineError(e); + } + } +} + +RestartCommand.description = `Restart node +... +Restart node +`; + +RestartCommand.flags = { + ...ConfigBaseCommand.flags, +}; + +module.exports = RestartCommand; diff --git a/packages/dashmate/src/commands/setup.js b/packages/dashmate/src/commands/setup.js new file mode 100644 index 00000000000..57b73ef239e --- /dev/null +++ b/packages/dashmate/src/commands/setup.js @@ -0,0 +1,141 @@ +const { Listr } = require('listr2'); + +const { Flags } = require('@oclif/core'); + +const BaseCommand = require('../oclif/command/BaseCommand'); + +const MuteOneLineError = require('../oclif/errors/MuteOneLineError'); + +const { + PRESET_LOCAL, + PRESETS, + NODE_TYPES, + NODE_TYPE_MASTERNODE, + MASTERNODE_DASH_AMOUNT, +} = require('../constants'); + +class SetupCommand extends BaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {generateBlsKeys} generateBlsKeys + * @param {setupLocalPresetTask} setupLocalPresetTask + * @param {setupRegularPresetTask} setupRegularPresetTask + * @return {Promise} + */ + async runWithDependencies( + { + preset, + 'node-type': nodeType, + }, + { + 'external-ip': externalIp, + 'operator-bls-private-key': operatorBlsPrivateKey, + 'funding-private-key': fundingPrivateKeyString, + 'node-count': nodeCount, + 'debug-logs': debugLogs, + 'miner-interval': minerInterval, + verbose: isVerbose, + }, + generateBlsKeys, + setupLocalPresetTask, + setupRegularPresetTask, + ) { + if (preset === PRESET_LOCAL) { + if (nodeType === undefined) { + // eslint-disable-next-line no-param-reassign + nodeType = 'masternode'; + } + + if (nodeType !== NODE_TYPE_MASTERNODE) { + throw new Error('Local development preset uses only masternode type of node'); + } + } + + if (nodeCount !== null && (nodeCount < 3)) { + throw new Error('node-count flag should be not less than 3'); + } + + const tasks = new Listr([ + { + title: 'Set configuration preset', + task: async (ctx, task) => { + if (ctx.preset === undefined) { + ctx.preset = await task.prompt([ + { + type: 'select', + message: 'Select configuration preset', + choices: PRESETS, + initial: 'testnet', + }, + ]); + } + }, + }, + { + task: (ctx) => { + if (ctx.preset === PRESET_LOCAL) { + return setupLocalPresetTask(); + } + + return setupRegularPresetTask(); + }, + }, + ], + { + renderer: isVerbose ? 'verbose' : 'default', + rendererOptions: { + showTimer: isVerbose, + clearOutput: false, + collapse: false, + showSubtasks: true, + }, + }); + + try { + await tasks.run({ + preset, + nodeType, + nodeCount, + debugLogs, + minerInterval, + externalIp, + operatorBlsPrivateKey, + fundingPrivateKeyString, + isVerbose, + }); + } catch (e) { + throw new MuteOneLineError(e); + } + } +} + +SetupCommand.description = `Set up node config + +Set up node config +`; + +SetupCommand.args = [{ + name: 'preset', + required: false, + description: 'Node configuration preset', + options: PRESETS, +}, +{ + name: 'node-type', + required: false, + description: 'Node type', + options: NODE_TYPES, +}]; + +SetupCommand.flags = { + 'debug-logs': Flags.boolean({ char: 'd', description: 'enable debug logs', allowNo: true }), + 'external-ip': Flags.string({ char: 'i', description: 'external ip' }), + 'operator-bls-private-key': Flags.string({ char: 'k', description: 'operator bls private key' }), + 'funding-private-key': Flags.string({ char: 'p', description: `private key with more than ${MASTERNODE_DASH_AMOUNT} dash for funding collateral` }), + 'node-count': Flags.integer({ description: 'number of nodes to setup' }), + 'miner-interval': Flags.string({ char: 'm', description: 'interval between blocks' }), + verbose: Flags.boolean({ char: 'v', description: 'use verbose mode for output', default: false }), +}; + +module.exports = SetupCommand; diff --git a/packages/dashmate/src/commands/start.js b/packages/dashmate/src/commands/start.js new file mode 100644 index 00000000000..8c753f21d40 --- /dev/null +++ b/packages/dashmate/src/commands/start.js @@ -0,0 +1,73 @@ +const { Listr } = require('listr2'); + +const { Flags } = require('@oclif/core'); + +const ConfigBaseCommand = require('../oclif/command/ConfigBaseCommand'); + +const MuteOneLineError = require('../oclif/errors/MuteOneLineError'); + +class StartCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {DockerCompose} dockerCompose + * @param {startNodeTask} startNodeTask + * @param {waitForNodeToBeReadyTask} waitForNodeToBeReadyTask + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + args, + { + 'wait-for-readiness': waitForReadiness, + verbose: isVerbose, + }, + dockerCompose, + startNodeTask, + waitForNodeToBeReadyTask, + config, + ) { + const tasks = new Listr( + [ + { + title: `Start ${config.getName()} node`, + task: () => startNodeTask(config), + }, + { + title: 'Wait for nodes to be ready', + enabled: () => waitForReadiness, + task: () => waitForNodeToBeReadyTask(config), + }, + ], + { + renderer: isVerbose ? 'verbose' : 'default', + rendererOptions: { + showTimer: isVerbose, + clearOutput: false, + collapse: false, + showSubtasks: true, + }, + }, + ); + + try { + await tasks.run({ + isVerbose, + }); + } catch (e) { + throw new MuteOneLineError(e); + } + } +} + +StartCommand.description = `Start node + +Start node +`; + +StartCommand.flags = { + ...ConfigBaseCommand.flags, + 'wait-for-readiness': Flags.boolean({ char: 'w', description: 'wait for nodes to be ready', default: false }), +}; + +module.exports = StartCommand; diff --git a/packages/dashmate/src/commands/status/core.js b/packages/dashmate/src/commands/status/core.js new file mode 100644 index 00000000000..2ab4117a2a1 --- /dev/null +++ b/packages/dashmate/src/commands/status/core.js @@ -0,0 +1,215 @@ +const fetch = require('node-fetch'); +const chalk = require('chalk'); + +const { Flags } = require('@oclif/core'); +const { OUTPUT_FORMATS } = require('../../constants'); + +const ConfigBaseCommand = require('../../oclif/command/ConfigBaseCommand'); +const CoreService = require('../../core/CoreService'); +const printObject = require('../../printers/printObject'); + +const ContainerIsNotPresentError = require('../../docker/errors/ContainerIsNotPresentError'); + +class CoreStatusCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {DockerCompose} dockerCompose + * @param {createRpcClient} createRpcClient + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + args, + flags, + dockerCompose, + createRpcClient, + config, + ) { + const coreService = new CoreService( + config, + createRpcClient( + { + port: config.get('core.rpc.port'), + user: config.get('core.rpc.user'), + pass: config.get('core.rpc.password'), + }, + ), + dockerCompose.docker.getContainer('core'), + ); + + const insightURLs = { + testnet: 'https://testnet-insight.dashevo.org/insight-api', + mainnet: 'https://insight.dash.org/insight-api', + }; + + // Collect data + const { + result: { + blocks: coreBlocks, + chain: coreChain, + difficulty: coreDifficulty, + headers: coreHeaders, + verificationprogress: coreVerificationProgress, + }, + } = await coreService.getRpcClient().getBlockchainInfo(); + const { result: networkInfo } = await coreService.getRpcClient().getNetworkInfo(); + const { result: mnsyncStatus } = await coreService.getRpcClient().mnsync('status'); + const { result: peerInfo } = await coreService.getRpcClient().getPeerInfo(); + + let latestVersion; + try { + const latestVersionRes = await fetch('https://api.github.com/repos/dashpay/dash/releases/latest'); + latestVersion = (await latestVersionRes.json()).tag_name.substring(1); + } catch (e) { + if (e.name === 'FetchError') { + latestVersion = '0'; + } else { + throw e; + } + } + + let corePortState; + try { + const corePortStateRes = await fetch(`https://mnowatch.org/${config.get('core.p2p.port')}/`); + corePortState = await corePortStateRes.text(); + } catch (e) { + if (e.name === 'FetchError') { + corePortState = 'ERROR'; + } else { + throw e; + } + } + + let coreVersion = networkInfo.subversion.replace(/\/|\(.*?\)|Dash Core:/g, ''); + let explorerBlockHeight; + if (insightURLs[config.get('network')]) { + try { + const explorerBlockHeightRes = await fetch(`${insightURLs[config.get('network')]}/status`); + ({ + info: { + blocks: explorerBlockHeight, + }, + } = await explorerBlockHeightRes.json()); + } catch (e) { + if (e.name === 'FetchError') { + explorerBlockHeight = 0; + } else { + throw e; + } + } + } + + let sentinelVersion; + let sentinelState; + if (config.get('core.masternode.enable')) { + sentinelVersion = (await dockerCompose.execCommand( + config.toEnvs(), + 'sentinel', + 'python bin/sentinel.py -v', + )).out.split(/\r?\n/)[0].replace(/Dash Sentinel v/, ''); + // eslint-disable-next-line prefer-destructuring + sentinelState = (await dockerCompose.execCommand( + config.toEnvs(), + 'sentinel', + 'python bin/sentinel.py', + )).out.split(/\r?\n/)[0]; + } + + // Determine status + let status; + try { + ({ + State: { + Status: status, + }, + } = await dockerCompose.inspectService(config.toEnvs(), 'core')); + } catch (e) { + if (e instanceof ContainerIsNotPresentError) { + status = 'not started'; + } + } + if (status === 'running' && mnsyncStatus.AssetName !== 'MASTERNODE_SYNC_FINISHED') { + status = `syncing ${(coreVerificationProgress * 100).toFixed(2)}%`; + } + + // Apply colors + if (status === 'running') { + status = chalk.green(status); + } else if (status.startsWith('syncing')) { + status = chalk.yellow(status); + } else { + status = chalk.red(status); + } + + if (coreVersion === latestVersion) { + coreVersion = chalk.green(coreVersion); + } else if (coreVersion.match(/\d+.\d+/)[0] === latestVersion.match(/\d+.\d+/)[0]) { + coreVersion = chalk.yellow(coreVersion); + } else { + coreVersion = chalk.red(coreVersion); + } + + if (corePortState === 'OPEN') { + corePortState = chalk.green(corePortState); + } else { + corePortState = chalk.red(corePortState); + } + + let blocks; + if (coreBlocks === coreHeaders || coreBlocks >= explorerBlockHeight) { + blocks = chalk.green(coreBlocks); + } else if ((explorerBlockHeight - coreBlocks) < 3) { + blocks = chalk.yellow(coreBlocks); + } else { + blocks = chalk.red(coreBlocks); + } + + if (config.get('core.masternode.enable')) { + if (sentinelState === '') { + sentinelState = chalk.green('No errors'); + } else { + sentinelState = chalk.red(sentinelState); + } + } + + const outputRows = { + Version: coreVersion, + 'Latest version': latestVersion, + Network: coreChain, + Status: status, + 'Sync asset': mnsyncStatus.AssetName, + 'Peer count': peerInfo.length, + 'P2P service': `${config.get('externalIp')}:${config.get('core.p2p.port')}`, + 'P2P port': `${config.get('core.p2p.port')} ${corePortState}`, + 'RPC service': `127.0.0.1:${config.get('core.rpc.port')}`, + 'Block height': blocks, + 'Header height': coreHeaders, + Difficulty: coreDifficulty, + }; + + if (config.get('core.masternode.enable')) { + outputRows['Sentinel version'] = sentinelVersion; + outputRows['Sentinel status'] = (sentinelState); + } + + if (insightURLs[config.get('network')]) { + outputRows['Remote block height'] = explorerBlockHeight; + } + + printObject(outputRows, flags.format); + } +} + +CoreStatusCommand.description = 'Show core status details'; + +CoreStatusCommand.flags = { + ...ConfigBaseCommand.flags, + format: Flags.string({ + description: 'display output format', + default: OUTPUT_FORMATS.PLAIN, + options: Object.values(OUTPUT_FORMATS), + }), +}; + +module.exports = CoreStatusCommand; diff --git a/packages/dashmate/src/commands/status/host.js b/packages/dashmate/src/commands/status/host.js new file mode 100644 index 00000000000..5d700909713 --- /dev/null +++ b/packages/dashmate/src/commands/status/host.js @@ -0,0 +1,44 @@ +const os = require('os'); +const publicIp = require('public-ip'); +const prettyMs = require('pretty-ms'); +const prettyByte = require('pretty-bytes'); + +const { Flags } = require('@oclif/core'); +const { OUTPUT_FORMATS } = require('../../constants'); + +const ConfigBaseCommand = require('../../oclif/command/ConfigBaseCommand'); +const printObject = require('../../printers/printObject'); + +class HostStatusCommand extends ConfigBaseCommand { + /** + * @return {Promise} + */ + async runWithDependencies(args, flags) { + const outputRows = { + Hostname: os.hostname(), + Uptime: prettyMs(os.uptime() * 1000), + Platform: os.platform(), + Arch: os.arch(), + Username: os.userInfo().username, + Diskfree: 0, // Waiting for feature: https://github.com/nodejs/node/pull/31351 + Memory: `${prettyByte(os.totalmem())} / ${prettyByte(os.freemem())}`, + CPUs: os.cpus().length, + IP: await publicIp.v4(), + }; + + printObject(outputRows, flags.format); + } +} + +HostStatusCommand.description = 'Show host status details'; + +HostStatusCommand.flags = { + ...ConfigBaseCommand.flags, + format: Flags.string({ + description: 'display output format', + default: OUTPUT_FORMATS.PLAIN, + options: Object.values(OUTPUT_FORMATS), + }), +}; + +module.exports = HostStatusCommand; diff --git a/packages/dashmate/src/commands/status/index.js b/packages/dashmate/src/commands/status/index.js new file mode 100644 index 00000000000..cca444accc2 --- /dev/null +++ b/packages/dashmate/src/commands/status/index.js @@ -0,0 +1,35 @@ +const { Flags } = require('@oclif/core'); +const { OUTPUT_FORMATS } = require('../../constants'); + +const ConfigBaseCommand = require('../../oclif/command/ConfigBaseCommand'); + +class StatusCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {outputStatusOverview} outputStatusOverview + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + args, + flags, + outputStatusOverview, + config, + ) { + await outputStatusOverview(config, flags.format); + } +} + +StatusCommand.description = 'Show status overview'; + +StatusCommand.flags = { + ...ConfigBaseCommand.flags, + format: Flags.string({ + description: 'display output format', + default: OUTPUT_FORMATS.PLAIN, + options: Object.values(OUTPUT_FORMATS), + }), +}; + +module.exports = StatusCommand; diff --git a/packages/dashmate/src/commands/status/masternode.js b/packages/dashmate/src/commands/status/masternode.js new file mode 100644 index 00000000000..5e30194514d --- /dev/null +++ b/packages/dashmate/src/commands/status/masternode.js @@ -0,0 +1,166 @@ +const chalk = require('chalk'); + +const { Flags } = require('@oclif/core'); +const { OUTPUT_FORMATS } = require('../../constants'); + +const ConfigBaseCommand = require('../../oclif/command/ConfigBaseCommand'); +const CoreService = require('../../core/CoreService'); +const blocksToTime = require('../../util/blocksToTime'); +const getPaymentQueuePosition = require('../../util/getPaymentQueuePosition'); +const printObject = require('../../printers/printObject'); + +const ContainerIsNotPresentError = require('../../docker/errors/ContainerIsNotPresentError'); + +class MasternodeStatusCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {DockerCompose} dockerCompose + * @param {createRpcClient} createRpcClient + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + args, + flags, + dockerCompose, + createRpcClient, + config, + ) { + const coreService = new CoreService( + config, + createRpcClient( + { + port: config.get('core.rpc.port'), + user: config.get('core.rpc.user'), + pass: config.get('core.rpc.password'), + }, + ), + dockerCompose.docker.getContainer('core'), + ); + + if (config.get('core.masternode.enable') === false) { + // eslint-disable-next-line no-console + console.log('This is not a masternode!'); + this.exit(); + } + + // Collect data + const { result: mnsyncStatus } = await coreService.getRpcClient().mnsync('status'); + const { + result: { + blocks: coreBlocks, + verificationprogress: coreVerificationProgress, + }, + } = await coreService.getRpcClient().getBlockchainInfo(); + + const { + result: { + enabled: masternodeEnabledCount, + }, + } = await coreService.getRpcClient().masternode('count'); + + const { + result: { + dmnState: masternodeDmnState, + state: masternodeState, + status: masternodeStatus, + proTxHash: masternodeProTxHash, + }, + } = await coreService.getRpcClient().masternode('status'); + + let sentinelState = (await dockerCompose.execCommand( + config.toEnvs(), + 'sentinel', + 'python bin/sentinel.py', + )).out.split(/\r?\n/)[0]; + + // Determine status + let status; + try { + ({ + State: { + Status: status, + }, + } = await dockerCompose.inspectService(config.toEnvs(), 'core')); + } catch (e) { + if (e instanceof ContainerIsNotPresentError) { + status = 'not started'; + } + } + if (status === 'running' && mnsyncStatus.AssetName !== 'MASTERNODE_SYNC_FINISHED') { + status = `syncing ${(coreVerificationProgress * 100).toFixed(2)}%`; + } + + // Determine payment queue position + let paymentQueuePosition; + let lastPaidTime; + if (masternodeState === 'READY') { + paymentQueuePosition = getPaymentQueuePosition( + masternodeDmnState, masternodeEnabledCount, coreBlocks, + ); + + // Determine last paid time + if (masternodeDmnState.lastPaidHeight === 0) { + lastPaidTime = 'Never'; + } else { + lastPaidTime = `${blocksToTime(coreBlocks - masternodeDmnState.lastPaidHeight)} ago`; + } + } + + // Apply colors + if (status === 'running') { + status = chalk.green(status); + } else if (status.startsWith('syncing')) { + status = chalk.yellow(status); + } else { + status = chalk.red(status); + } + + if (sentinelState === '') { + sentinelState = chalk.green('No errors'); + } else { + sentinelState = chalk.red(sentinelState); + } + + let masternodePoSePenalty; + if (masternodeStatus === 'Ready') { + if (masternodeDmnState.PoSePenalty === 0) { + masternodePoSePenalty = chalk.green(masternodeDmnState.PoSePenalty); + } else if (masternodeDmnState.PoSePenalty < masternodeEnabledCount) { + masternodePoSePenalty = chalk.yellow(masternodeDmnState.PoSePenalty); + } else { + masternodePoSePenalty = chalk.red(masternodeDmnState.PoSePenalty); + } + } + + const outputRows = { + 'Masternode status': (masternodeState === 'READY' ? chalk.green : chalk.red)(masternodeStatus), + 'Sentinel status': (sentinelState !== '' ? sentinelState : 'No errors'), + }; + + if (masternodeState === 'READY') { + outputRows['ProTx Hash'] = masternodeProTxHash; + outputRows['PoSe Penalty'] = masternodePoSePenalty; + outputRows['Last paid block'] = masternodeDmnState.lastPaidHeight; + outputRows['Last paid time'] = lastPaidTime; + outputRows['Payment queue position'] = `${paymentQueuePosition}/${masternodeEnabledCount}`; + outputRows['Next payment time'] = `in ${blocksToTime(paymentQueuePosition)}`; + } + + printObject(outputRows, flags.format); + } +} + +MasternodeStatusCommand.description = 'Show masternode status details'; + +MasternodeStatusCommand.flags = { + ...ConfigBaseCommand.flags, + format: Flags.string({ + description: 'display output format', + default: OUTPUT_FORMATS.PLAIN, + options: Object.values(OUTPUT_FORMATS), + }), +}; + +module.exports = MasternodeStatusCommand; diff --git a/packages/dashmate/src/commands/status/platform.js b/packages/dashmate/src/commands/status/platform.js new file mode 100644 index 00000000000..702a8b68dc8 --- /dev/null +++ b/packages/dashmate/src/commands/status/platform.js @@ -0,0 +1,223 @@ +const fetch = require('node-fetch'); +const chalk = require('chalk'); + +const { Flags } = require('@oclif/core'); +const { OUTPUT_FORMATS } = require('../../constants'); + +const ConfigBaseCommand = require('../../oclif/command/ConfigBaseCommand'); +const CoreService = require('../../core/CoreService'); +const printObject = require('../../printers/printObject'); + +const ContainerIsNotPresentError = require('../../docker/errors/ContainerIsNotPresentError'); +const ServiceIsNotRunningError = require('../../docker/errors/ServiceIsNotRunningError'); + +class PlatformStatusCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {DockerCompose} dockerCompose + * @param {createRpcClient} createRpcClient + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + args, + flags, + dockerCompose, + createRpcClient, + config, + ) { + if (config.get('network') === 'mainnet') { + // eslint-disable-next-line no-console + console.log('Platform is not supported on mainnet yet!'); + this.exit(); + } + + const coreService = new CoreService( + config, + createRpcClient( + { + port: config.get('core.rpc.port'), + user: config.get('core.rpc.user'), + pass: config.get('core.rpc.password'), + }, + ), + dockerCompose.docker.getContainer('core'), + ); + + const explorerURLs = { + testnet: 'https://rpc.cloudwheels.net:26657', + mainnet: '', + }; + + if (!(await dockerCompose.isServiceRunning(config.toEnvs(), 'drive_tenderdash'))) { + throw new ServiceIsNotRunningError(config.get('network'), 'drive_tenderdash'); + } + + // Collect core data + const { + result: { + IsSynced: coreIsSynced, + }, + } = await coreService.getRpcClient().mnsync('status'); + + // Collecting platform data fails if Tenderdash is waiting for core to sync + if (coreIsSynced === false) { + // eslint-disable-next-line no-console + console.log('Platform status is not available until core sync is complete!'); + this.exit(); + } + + // Collect platform data + const tenderdashStatusRes = await fetch(`http://localhost:${config.get('platform.drive.tenderdash.rpc.port')}/status`); + const { + result: { + node_info: { + version: platformVersion, + network: platformNetwork, + }, + sync_info: { + catching_up: platformCatchingUp, + latest_app_hash: platformLatestAppHash, + latest_block_height: platformLatestBlockHeight, + }, + }, + } = await tenderdashStatusRes.json(); + + const tenderdashNetInfoRes = await fetch(`http://localhost:${config.get('platform.drive.tenderdash.rpc.port')}/net_info`); + const { + result: { + n_peers: platformPeers, + }, + } = await tenderdashNetInfoRes.json(); + + let explorerLatestBlockHeight; + if (explorerURLs[config.get('network')]) { + try { + const explorerBlockHeightRes = await fetch(`${explorerURLs[config.get('network')]}/status`); + ({ + result: { + sync_info: { + latest_block_height: explorerLatestBlockHeight, + }, + }, + } = await explorerBlockHeightRes.json()); + } catch (e) { + if (e.name === 'FetchError') { + explorerLatestBlockHeight = 0; + } else { + throw e; + } + } + } + + // Check ports + let httpPortState; + let gRpcPortState; + let p2pPortState; + try { + const httpPortStateRes = await fetch(`https://mnowatch.org/${config.get('platform.dapi.envoy.http.port')}/`); + httpPortState = await httpPortStateRes.text(); + const gRpcPortStateRes = await fetch(`https://mnowatch.org/${config.get('platform.dapi.envoy.grpc.port')}/`); + gRpcPortState = await gRpcPortStateRes.text(); + const p2pPortStateRes = await fetch(`https://mnowatch.org/${config.get('platform.drive.tenderdash.p2p.port')}/`); + p2pPortState = await p2pPortStateRes.text(); + } catch (e) { + if (e.name === 'FetchError') { + httpPortState = 'ERROR'; + gRpcPortState = 'ERROR'; + p2pPortState = 'ERROR'; + } else { + throw e; + } + } + + // Determine status + let status; + try { + ({ + State: { + Status: status, + }, + } = await dockerCompose.inspectService(config.toEnvs(), 'drive_tenderdash')); + } catch (e) { + if (e instanceof ContainerIsNotPresentError) { + status = 'not started'; + } + } + if (status === 'running' && platformCatchingUp === true && explorerURLs[config.get('network')]) { + status = `syncing ${((platformLatestBlockHeight / explorerLatestBlockHeight) * 100).toFixed(2)}%`; + } + + // Apply colors + if (status === 'running') { + status = chalk.green(status); + } else if (status.includes('syncing')) { + status = chalk.yellow(status); + } else { + status = chalk.red(status); + } + + let blocks; + if (explorerURLs[config.get('network')]) { + if (platformLatestBlockHeight >= explorerLatestBlockHeight) { + blocks = chalk.green(platformLatestBlockHeight); + } else { + blocks = chalk.red(platformLatestBlockHeight); + } + } else { + blocks = platformLatestBlockHeight; + } + + if (httpPortState === 'OPEN') { + httpPortState = chalk.green(httpPortState); + } else { + httpPortState = chalk.red(httpPortState); + } + if (gRpcPortState === 'OPEN') { + gRpcPortState = chalk.green(gRpcPortState); + } else { + gRpcPortState = chalk.red(gRpcPortState); + } + if (p2pPortState === 'OPEN') { + p2pPortState = chalk.green(p2pPortState); + } else { + p2pPortState = chalk.red(p2pPortState); + } + + const outputRows = { + 'Tenderdash Version': platformVersion, + Network: platformNetwork, + Status: status, + 'Block height': blocks, + 'Peer count': platformPeers, + 'App hash': platformLatestAppHash, + 'HTTP service': `${config.get('externalIp')}:${config.get('platform.dapi.envoy.http.port')}`, + 'HTTP port': `${config.get('platform.dapi.envoy.http.port')} ${httpPortState}`, + 'gRPC service': `${config.get('externalIp')}:${config.get('platform.dapi.envoy.grpc.port')}`, + 'gRPC port': `${config.get('platform.dapi.envoy.grpc.port')} ${gRpcPortState}`, + 'P2P service': `${config.get('externalIp')}:${config.get('platform.drive.tenderdash.p2p.port')}`, + 'P2P port': `${config.get('platform.drive.tenderdash.p2p.port')} ${p2pPortState}`, + 'RPC service': `127.0.0.1:${config.get('platform.drive.tenderdash.rpc.port')}`, + }; + + if (explorerURLs[config.get('network')]) { + outputRows['Remote block height'] = explorerLatestBlockHeight; + } + + printObject(outputRows, flags.format); + } +} + +PlatformStatusCommand.description = 'Show platform status details'; + +PlatformStatusCommand.flags = { + ...ConfigBaseCommand.flags, + format: Flags.string({ + description: 'display output format', + default: OUTPUT_FORMATS.PLAIN, + options: Object.values(OUTPUT_FORMATS), + }), +}; + +module.exports = PlatformStatusCommand; diff --git a/packages/dashmate/src/commands/status/services.js b/packages/dashmate/src/commands/status/services.js new file mode 100644 index 00000000000..0e4f3fbd1c9 --- /dev/null +++ b/packages/dashmate/src/commands/status/services.js @@ -0,0 +1,97 @@ +const chalk = require('chalk'); + +const { Flags } = require('@oclif/core'); +const { OUTPUT_FORMATS } = require('../../constants'); + +const printArrayOfObjects = require('../../printers/printArrayOfObjects'); + +const ConfigBaseCommand = require('../../oclif/command/ConfigBaseCommand'); + +const ContainerIsNotPresentError = require('../../docker/errors/ContainerIsNotPresentError'); + +class ServicesStatusCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {DockerCompose} dockerCompose + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + args, + flags, + dockerCompose, + config, + ) { + const serviceHumanNames = { + core: 'Core', + }; + + if (config.get('core.masternode.enable')) { + Object.assign(serviceHumanNames, { + sentinel: 'Sentinel', + }); + } + + if (config.get('network') !== 'mainnet') { + Object.assign(serviceHumanNames, { + drive_abci: 'Drive ABCI', + drive_tenderdash: 'Drive Tenderdash', + dapi_api: 'DAPI API', + dapi_tx_filter_stream: 'DAPI Transactions Filter Stream', + dapi_envoy: 'DAPI Envoy', + }); + } + + const outputRows = []; + + for (const [serviceName, serviceDescription] of Object.entries(serviceHumanNames)) { + let containerId; + let status; + let image; + + try { + ({ + Id: containerId, + State: { + Status: status, + }, + Config: { + Image: image, + }, + } = await dockerCompose.inspectService(config.toEnvs(), serviceName)); + } catch (e) { + if (e instanceof ContainerIsNotPresentError) { + status = 'not started'; + } + } + + let statusText; + if (status) { + statusText = (status === 'running' ? chalk.green : chalk.red)(status); + } + + outputRows.push({ + Service: serviceDescription, + 'Container ID': containerId ? containerId.slice(0, 12) : undefined, + Image: image, + Status: statusText, + }); + } + + printArrayOfObjects(outputRows, flags.format); + } +} + +ServicesStatusCommand.description = 'Show service status details'; + +ServicesStatusCommand.flags = { + ...ConfigBaseCommand.flags, + format: Flags.string({ + description: 'display output format', + default: OUTPUT_FORMATS.PLAIN, + options: Object.values(OUTPUT_FORMATS), + }), +}; + +module.exports = ServicesStatusCommand; diff --git a/packages/dashmate/src/commands/stop.js b/packages/dashmate/src/commands/stop.js new file mode 100644 index 00000000000..e0316bc2107 --- /dev/null +++ b/packages/dashmate/src/commands/stop.js @@ -0,0 +1,66 @@ +const { Listr } = require('listr2'); + +const { Flags } = require('@oclif/core'); + +const ConfigBaseCommand = require('../oclif/command/ConfigBaseCommand'); + +const MuteOneLineError = require('../oclif/errors/MuteOneLineError'); + +class StopCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {stopNodeTask} stopNodeTask + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + args, + { + force: isForce, + verbose: isVerbose, + }, + stopNodeTask, + config, + ) { + const tasks = new Listr([ + { + task: async () => stopNodeTask(config), + }, + ], + { + renderer: isVerbose ? 'verbose' : 'default', + rendererOptions: { + showTimer: isVerbose, + clearOutput: false, + collapse: false, + showSubtasks: true, + }, + }); + + try { + await tasks.run({ + isForce, + isVerbose, + }); + } catch (e) { + throw new MuteOneLineError(e); + } + } +} + +StopCommand.description = `Stop node + +Stop node +`; + +StopCommand.flags = { + ...ConfigBaseCommand.flags, + force: Flags.boolean({ + char: 'f', + description: 'force stop even if any is running', + default: false, + }), +}; + +module.exports = StopCommand; diff --git a/packages/dashmate/src/commands/update.js b/packages/dashmate/src/commands/update.js new file mode 100644 index 00000000000..caebc4f1818 --- /dev/null +++ b/packages/dashmate/src/commands/update.js @@ -0,0 +1,60 @@ +const { Listr } = require('listr2'); + +const ConfigBaseCommand = require('../oclif/command/ConfigBaseCommand'); + +const MuteOneLineError = require('../oclif/errors/MuteOneLineError'); + +class UpdateCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {DockerCompose} dockerCompose + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + args, + { + verbose: isVerbose, + }, + dockerCompose, + config, + ) { + const tasks = new Listr( + [ + { + title: 'Download updates', + task: () => dockerCompose.pull(config.toEnvs()), + }, + ], + { + renderer: isVerbose ? 'verbose' : 'default', + rendererOptions: { + showTimer: isVerbose, + clearOutput: false, + collapse: false, + showSubtasks: true, + }, + }, + ); + + try { + await tasks.run({ + isVerbose, + }); + } catch (e) { + throw new MuteOneLineError(e); + } + } +} + +UpdateCommand.description = `Update node + +Download and update node software +`; + +UpdateCommand.flags = { + ...ConfigBaseCommand.flags, +}; + +module.exports = UpdateCommand; diff --git a/packages/dashmate/src/commands/wallet/mint.js b/packages/dashmate/src/commands/wallet/mint.js new file mode 100644 index 00000000000..698213aeb99 --- /dev/null +++ b/packages/dashmate/src/commands/wallet/mint.js @@ -0,0 +1,79 @@ +const { Listr } = require('listr2'); + +const { Flags } = require('@oclif/core'); + +const ConfigBaseCommand = require('../../oclif/command/ConfigBaseCommand'); +const MuteOneLineError = require('../../oclif/errors/MuteOneLineError'); + +const { NETWORK_LOCAL } = require('../../constants'); + +class MintCommand extends ConfigBaseCommand { + /** + * @param {Object} args + * @param {Object} flags + * @param {generateToAddressTask} generateToAddressTask + * @param {Config} config + * @return {Promise} + */ + async runWithDependencies( + { + amount, + }, + { + address, + verbose: isVerbose, + }, + generateToAddressTask, + config, + ) { + const network = config.get('network'); + + if (network !== NETWORK_LOCAL) { + throw new Error('Only local network supports generation of dash'); + } + + const tasks = new Listr([ + { + title: `Generate ${amount} dash to address`, + task: () => generateToAddressTask(config, amount), + }, + ], + { + renderer: isVerbose ? 'verbose' : 'default', + rendererOptions: { + showTimer: isVerbose, + clearOutput: false, + collapse: false, + showSubtasks: true, + }, + }); + + try { + await tasks.run({ + address, + network, + }); + } catch (e) { + throw new MuteOneLineError(e); + } + } +} + +MintCommand.description = `Mint dash +... +Mint specified amount of dash to a new address or specified one +`; + +MintCommand.flags = { + ...ConfigBaseCommand.flags, + address: Flags.string({ char: 'a', description: 'recipient address instead of a new one', default: null }), +}; + +MintCommand.args = [{ + name: 'amount', + required: true, + description: 'amount of dash to be generated to address', + parse: (input) => parseInt(input, 10), +}]; + +module.exports = MintCommand; diff --git a/packages/dashmate/src/config/Config.js b/packages/dashmate/src/config/Config.js new file mode 100644 index 00000000000..92254905600 --- /dev/null +++ b/packages/dashmate/src/config/Config.js @@ -0,0 +1,200 @@ +const Ajv = require('ajv'); + +const nodePath = require('path'); + +const lodashGet = require('lodash.get'); +const lodashSet = require('lodash.set'); +const lodashCloneDeep = require('lodash.clonedeep'); + +const addFormats = require('ajv-formats'); +const configJsonSchema = require('../../configs/schema/configJsonSchema'); + +const convertObjectToEnvs = require('./convertObjectToEnvs'); + +const InvalidOptionPathError = require('./errors/InvalidOptionPathError'); +const InvalidOptionError = require('./errors/InvalidOptionError'); +const InvalidOptionsError = require('./errors/InvalidOptionsError'); +const OptionIsNotSetError = require('./errors/OptionIsNotSetError'); + +class Config { + /** + * @param {string} name + * @param {Object} options + */ + constructor(name, options = {}) { + this.name = name; + + this.setOptions(options); + } + + /** + * Get name + * + * @return {string} + */ + getName() { + return this.name; + } + + /** + * Is option present + * + * @param {string} path + * @return {boolean} + */ + has(path) { + return lodashGet(this.options, path) !== undefined; + } + + /** + * Get config option + * + * @param {string} path + * @param {boolean} [isRequired=false] + * + * @return {*} + */ + get(path, isRequired = false) { + const value = lodashGet(this.options, path); + + if (value === undefined) { + throw new InvalidOptionPathError(path); + } + + if (isRequired && value === null) { + throw new OptionIsNotSetError(this, path); + } + + return value; + } + + /** + * Set config option + * + * @param {string} path + * @param {*} value + * + * @return {Config} + */ + set(path, value) { + const clonedOptions = lodashCloneDeep(this.options); + + lodashSet(clonedOptions, path, lodashCloneDeep(value)); + + const isValid = Config.ajv.validate(configJsonSchema, clonedOptions); + + if (!isValid) { + if (Config.ajv.errors[0].keyword === 'additionalProperties') { + throw new InvalidOptionPathError(path); + } + + const message = Config.ajv.errorsText(undefined, { dataVar: 'config' }); + + throw new InvalidOptionError( + path, + value, + Config.ajv.errors, + message, + ); + } + + this.options = clonedOptions; + + return this; + } + + /** + * Get options + * + * @return {Object} + */ + getOptions() { + return this.options; + } + + /** + * Set options + * + * @param {Object} options + * + * @return {Config} + */ + setOptions(options) { + const clonedOptions = lodashCloneDeep(options); + + const isValid = Config.ajv.validate(configJsonSchema, clonedOptions); + + if (!isValid) { + const message = Config.ajv.errorsText(undefined, { dataVar: 'config' }); + + throw new InvalidOptionsError( + clonedOptions, + Config.ajv.errors, + message, + ); + } + + this.options = clonedOptions; + + return this; + } + + /** + * + * @return {{CONFIG_NAME: string, COMPOSE_PROJECT_NAME: string}} + */ + toEnvs() { + const dockerComposeFiles = ['docker-compose.yml']; + + if (this.get('core.masternode.enable') === true) { + dockerComposeFiles.push('docker-compose.sentinel.yml'); + } + + if (this.has('platform')) { + dockerComposeFiles.push('docker-compose.platform.yml'); + + if (this.get('platform.sourcePath') !== null) { + dockerComposeFiles.push('docker-compose.platform.build.yml'); + } + } + + let envs = { + CONFIG_NAME: this.getName(), + COMPOSE_PROJECT_NAME: `dash_masternode_${this.getName()}`, + COMPOSE_FILE: dockerComposeFiles.join(':'), + COMPOSE_PATH_SEPARATOR: ':', + COMPOSE_DOCKER_CLI_BUILD: 1, + DOCKER_BUILDKIT: 1, + ...convertObjectToEnvs(this.getOptions()), + }; + + if (this.has('platform')) { + envs = { + ...envs, + + PLATFORM_DRIVE_ABCI_LOG_PRETTY_DIRECTORY_PATH: nodePath.dirname( + this.get('platform.drive.abci.log.prettyFile.path'), + ), + + PLATFORM_DRIVE_ABCI_LOG_JSON_DIRECTORY_PATH: nodePath.dirname( + this.get('platform.drive.abci.log.jsonFile.path'), + ), + + PLATFORM_DRIVE_ABCI_LOG_PRETTY_FILE_NAME: nodePath.basename( + this.get('platform.drive.abci.log.prettyFile.path'), + ), + + PLATFORM_DRIVE_ABCI_LOG_JSON_FILE_NAME: nodePath.basename( + this.get('platform.drive.abci.log.jsonFile.path'), + ), + }; + } + + return envs; + } +} + +Config.ajv = new Ajv({ coerceTypes: true }); +addFormats(Config.ajv, { mode: 'fast', formats: ['ipv4'] }); + +module.exports = Config; diff --git a/packages/dashmate/src/config/configFile/ConfigFile.js b/packages/dashmate/src/config/configFile/ConfigFile.js new file mode 100644 index 00000000000..adab07151df --- /dev/null +++ b/packages/dashmate/src/config/configFile/ConfigFile.js @@ -0,0 +1,233 @@ +const Config = require('../Config'); + +const ConfigAlreadyPresentError = require('../errors/ConfigAlreadyPresentError'); +const ConfigIsNotPresentError = require('../errors/ConfigIsNotPresentError'); +const GroupIsNotPresentError = require('../errors/GroupIsNotPresentError'); + +class ConfigFile { + /** + * @param {Config[]} configs + * @param {string} configFormatVersion + * @param {string|null} defaultConfigName + * @param {string|null} defaultGroupName + */ + constructor(configs, configFormatVersion, defaultConfigName, defaultGroupName) { + this.configsMap = configs.reduce((configsMap, config) => { + // eslint-disable-next-line no-param-reassign + configsMap[config.getName()] = config; + + return configsMap; + }, {}); + + this.configFormatVersion = configFormatVersion; + this.defaultConfigName = defaultConfigName; + this.defaultGroupName = defaultGroupName; + } + + /** + * Get call configs + * + * @returns {Config[]} + */ + getAllConfigs() { + return Object.values(this.configsMap); + } + + /** + * Set current config name + * + * @param {string|null} name + * @returns {ConfigFile} + */ + setDefaultConfigName(name) { + if (name !== null && !this.isConfigExists(name)) { + throw new ConfigIsNotPresentError(name); + } + + this.defaultConfigName = name; + + return this; + } + + /** + * Get current config name if set + * + * @returns {string|null} + */ + getDefaultConfigName() { + return this.defaultConfigName; + } + + /** + * Get current config if set + * + * @returns {Config|null} + */ + getDefaultConfig() { + if (this.getDefaultConfigName() === null) { + return null; + } + + return this.getConfig( + this.getDefaultConfigName(), + ); + } + + /** + * Set current config format version + * + * @param {string} version + * @returns {ConfigFile} + */ + setConfigFormatVersion(version) { + this.configFormatVersion = version; + + return this; + } + + /** + * Get current config format version if set + * + * @returns {string|null} + */ + getConfigFormatVersion() { + return this.configFormatVersion; + } + + /** + * Check is group exists + * + * @param {string} name + * @return {boolean} + */ + isGroupExists(name) { + return Object.entries(this.configsMap) + .filter(([, config]) => config.get('group') === name).length !== 0; + } + + /** + * Set default group name + * + * @param {string} defaultGroupName + */ + setDefaultGroupName(defaultGroupName) { + if (!this.isGroupExists(defaultGroupName)) { + throw new GroupIsNotPresentError(defaultGroupName); + } + + this.defaultGroupName = defaultGroupName; + } + + /** + * Get default group name + * + * @return {string} + */ + getDefaultGroupName() { + return this.defaultGroupName; + } + + /** + * Get group configs + * + * @param {string} name + * @return {Config[]} + */ + getGroupConfigs(name) { + if (!this.isGroupExists(name)) { + throw new GroupIsNotPresentError(name); + } + + return Object.entries(this.configsMap) + .filter(([, config]) => config.get('group') === name) + .map(([, config]) => config); + } + + /** + * Get config by name + * + * @param {string} name + */ + getConfig(name) { + if (!this.isConfigExists(name)) { + throw new ConfigIsNotPresentError(name); + } + + return this.configsMap[name]; + } + + /** + * Is config exists + * + * @param {string} name + * @returns {boolean} + */ + isConfigExists(name) { + return Object.prototype.hasOwnProperty.call(this.configsMap, name); + } + + /** + * Create a new config + * + * @param {string} name + * @param {string} fromConfigName - Set options from another config + * @returns {ConfigFile} + */ + createConfig(name, fromConfigName) { + if (this.isConfigExists(name)) { + throw new ConfigAlreadyPresentError(name); + } + + const fromConfig = this.getConfig(fromConfigName); + + this.configsMap[name] = new Config(name, fromConfig.getOptions()); + + return this.configsMap[name]; + } + + /** + * Remove config by name + * + * @param {string} name + * @returns {ConfigFile} + */ + removeConfig(name) { + if (!this.isConfigExists(name)) { + throw new ConfigIsNotPresentError(name); + } + + if (this.getDefaultConfigName() === name) { + this.setDefaultConfigName(null); + } + + delete this.configsMap[name]; + + return this; + } + + /** + * Get config file as plain object + * + * @return {{ + * configs: Object, + * defaultGroupName: string, + * configFormatVersion: (string|null), + * defaultConfigName: (string|null) + * }} + */ + toObject() { + return { + configFormatVersion: this.getConfigFormatVersion(), + defaultConfigName: this.getDefaultConfigName(), + defaultGroupName: this.getDefaultGroupName(), + configs: this.getAllConfigs().reduce((configsMap, config) => { + // eslint-disable-next-line no-param-reassign + configsMap[config.getName()] = config.getOptions(); + + return configsMap; + }, {}), + }; + } +} + +module.exports = ConfigFile; diff --git a/packages/dashmate/src/config/configFile/ConfigFileJsonRepository.js b/packages/dashmate/src/config/configFile/ConfigFileJsonRepository.js new file mode 100644 index 00000000000..010e620ac9d --- /dev/null +++ b/packages/dashmate/src/config/configFile/ConfigFileJsonRepository.js @@ -0,0 +1,88 @@ +const fs = require('fs'); + +const Ajv = require('ajv'); + +const Config = require('../Config'); +const ConfigFile = require('./ConfigFile'); + +const { CONFIG_FILE_PATH } = require('../../constants'); + +const configFileJsonSchema = require('../../../configs/schema/configFileJsonSchema'); + +const ConfigFileNotFoundError = require('../errors/ConfigFileNotFoundError'); +const InvalidConfigFileFormatError = require('../errors/InvalidConfigFileFormatError'); + +const packageJson = require('../../../package.json'); + +class ConfigFileJsonRepository { + /** + * @param {migrateConfigFile} migrateConfigFile + */ + constructor(migrateConfigFile) { + this.migrateConfigFile = migrateConfigFile; + this.ajv = new Ajv(); + } + + /** + * Load configs from file + * + * @returns {Promise} + */ + async read() { + if (!fs.existsSync(CONFIG_FILE_PATH)) { + throw new ConfigFileNotFoundError(CONFIG_FILE_PATH); + } + + const configFileJSON = fs.readFileSync(CONFIG_FILE_PATH, 'utf8'); + + let configFileData; + try { + configFileData = JSON.parse(configFileJSON); + } catch (e) { + throw new InvalidConfigFileFormatError(CONFIG_FILE_PATH, e); + } + + const migratedConfigFileData = this.migrateConfigFile( + configFileData, + configFileData.configFormatVersion, + packageJson.version, + ); + + const isValid = this.ajv.validate(configFileJsonSchema, migratedConfigFileData); + + if (!isValid) { + const error = new Error(this.ajv.errorsText(undefined, { dataVar: 'configFile' })); + + throw new InvalidConfigFileFormatError(CONFIG_FILE_PATH, error); + } + + let configs; + try { + configs = Object.entries(migratedConfigFileData.configs) + .map(([name, options]) => new Config(name, options)); + } catch (e) { + throw new InvalidConfigFileFormatError(CONFIG_FILE_PATH, e); + } + + return new ConfigFile( + configs, + packageJson.version, + migratedConfigFileData.defaultConfigName, + migratedConfigFileData.defaultGroupName, + ); + } + + /** + * Save configs to file + * + * @param {ConfigFile} configFile + * @returns {Promise} + */ + async write(configFile) { + const configFileJSON = JSON.stringify(configFile.toObject(), undefined, 2); + + fs.writeFileSync(CONFIG_FILE_PATH, configFileJSON, 'utf8'); + } +} + +module.exports = ConfigFileJsonRepository; diff --git a/packages/dashmate/src/config/configFile/migrateConfigFile.js b/packages/dashmate/src/config/configFile/migrateConfigFile.js new file mode 100644 index 00000000000..9503b256ab8 --- /dev/null +++ b/packages/dashmate/src/config/configFile/migrateConfigFile.js @@ -0,0 +1,19 @@ +const semver = require('semver'); + +const configOptionMigrations = require('../../../configs/migrations'); + +function migrateConfigFile(configFile, fromVersion, toVersion) { + if (fromVersion === toVersion) { + return configFile; + } + + return Object.keys(configOptionMigrations) + .filter((version) => (semver.gt(version, fromVersion) && semver.lte(version, toVersion))) + .sort(semver.compare) + .reduce((migratedOptions, version) => { + const migrationFunction = configOptionMigrations[version]; + return migrationFunction(configFile); + }, configFile); +} + +module.exports = migrateConfigFile; diff --git a/packages/dashmate/src/config/convertObjectToEnvs.js b/packages/dashmate/src/config/convertObjectToEnvs.js new file mode 100644 index 00000000000..4c9086aa443 --- /dev/null +++ b/packages/dashmate/src/config/convertObjectToEnvs.js @@ -0,0 +1,55 @@ +function isCamelCase(str) { + return !!str.match(/^[a-z]+[A-Z]/); +} + +function camelToSnakeCase(str) { + if (isCamelCase(str)) { + return str.replace(/[A-Z]/g, '_$&'); + } + + return str; +} + +/** + * @param {Object} envs + * @param {*} value + * @param {string} [key=''] + */ +function buildEnvs(envs, value, key = '') { + if (typeof value === 'object' && value !== null) { + if (key.length > 0) { + // eslint-disable-next-line no-param-reassign + key += '_'; + } + + for (const [k, v] of Object.entries(value)) { + buildEnvs( + envs, + v, + `${key}${camelToSnakeCase(k).toUpperCase()}`, + ); + } + } else { + if (value === null || value === undefined) { + // eslint-disable-next-line no-param-reassign + value = ''; + } + + // eslint-disable-next-line no-param-reassign + envs[key] = value.toString(); + } +} + +/** + * @param {Object} object + * @returns {Object} + */ +function convertObjectToEnvs(object) { + const envs = {}; + + buildEnvs(envs, object); + + return envs; +} + +module.exports = convertObjectToEnvs; diff --git a/packages/dashmate/src/config/errors/ConfigAlreadyPresentError.js b/packages/dashmate/src/config/errors/ConfigAlreadyPresentError.js new file mode 100644 index 00000000000..4553758b108 --- /dev/null +++ b/packages/dashmate/src/config/errors/ConfigAlreadyPresentError.js @@ -0,0 +1,21 @@ +const AbstractError = require('../../errors/AbstractError'); + +class ConfigAlreadyPresentError extends AbstractError { + /** + * @param {string} configName + */ + constructor(configName) { + super(`Config with name '${configName}' already present`); + + this.configName = configName; + } + + /** + * @returns {string} + */ + getConfigName() { + return this.configName; + } +} + +module.exports = ConfigAlreadyPresentError; diff --git a/packages/dashmate/src/config/errors/ConfigFileNotFoundError.js b/packages/dashmate/src/config/errors/ConfigFileNotFoundError.js new file mode 100644 index 00000000000..56d65fa86f0 --- /dev/null +++ b/packages/dashmate/src/config/errors/ConfigFileNotFoundError.js @@ -0,0 +1,21 @@ +const AbstractError = require('../../errors/AbstractError'); + +class ConfigFileNotFoundError extends AbstractError { + /** + * @param {string} configFilePath + */ + constructor(configFilePath) { + super(`Config file '${configFilePath}' not found`); + + this.configFilePath = configFilePath; + } + + /** + * @returns {string} + */ + getConfigFilePath() { + return this.configFilePath; + } +} + +module.exports = ConfigFileNotFoundError; diff --git a/packages/dashmate/src/config/errors/ConfigIsNotPresentError.js b/packages/dashmate/src/config/errors/ConfigIsNotPresentError.js new file mode 100644 index 00000000000..29af6dca17f --- /dev/null +++ b/packages/dashmate/src/config/errors/ConfigIsNotPresentError.js @@ -0,0 +1,21 @@ +const AbstractError = require('../../errors/AbstractError'); + +class ConfigIsNotPresentError extends AbstractError { + /** + * @param {string} configName + */ + constructor(configName) { + super(`Config with name '${configName}' is not present`); + + this.configName = configName; + } + + /** + * @returns {string} + */ + getConfigName() { + return this.configName; + } +} + +module.exports = ConfigIsNotPresentError; diff --git a/packages/dashmate/src/config/errors/CouldNotCreateHomeDirError.js b/packages/dashmate/src/config/errors/CouldNotCreateHomeDirError.js new file mode 100644 index 00000000000..ed6ae62fe9c --- /dev/null +++ b/packages/dashmate/src/config/errors/CouldNotCreateHomeDirError.js @@ -0,0 +1,21 @@ +const AbstractError = require('../../errors/AbstractError'); + +class CouldNotCreateHomeDirError extends AbstractError { + /** + * @param {string} homeDirPath + */ + constructor(homeDirPath) { + super(`Could not create home dir at '${homeDirPath}'`); + + this.homeDirPath = homeDirPath; + } + + /** + * @returns {string} + */ + getHomeDirPath() { + return this.homeDirPath; + } +} + +module.exports = CouldNotCreateHomeDirError; diff --git a/packages/dashmate/src/config/errors/GroupIsNotPresentError.js b/packages/dashmate/src/config/errors/GroupIsNotPresentError.js new file mode 100644 index 00000000000..107a6c79c37 --- /dev/null +++ b/packages/dashmate/src/config/errors/GroupIsNotPresentError.js @@ -0,0 +1,21 @@ +const AbstractError = require('../../errors/AbstractError'); + +class GroupIsNotPresentError extends AbstractError { + /** + * @param {string} groupName + */ + constructor(groupName) { + super(`Group with name '${groupName}' is not present`); + + this.groupName = groupName; + } + + /** + * @returns {string} + */ + getGroupName() { + return this.groupName; + } +} + +module.exports = GroupIsNotPresentError; diff --git a/packages/dashmate/src/config/errors/HomeDirIsNotWritableError.js b/packages/dashmate/src/config/errors/HomeDirIsNotWritableError.js new file mode 100644 index 00000000000..e86f4eaea0a --- /dev/null +++ b/packages/dashmate/src/config/errors/HomeDirIsNotWritableError.js @@ -0,0 +1,21 @@ +const AbstractError = require('../../errors/AbstractError'); + +class HomeDirIsNotWritableError extends AbstractError { + /** + * @param {string} homeDirPath + */ + constructor(homeDirPath) { + super(`Home dir '${homeDirPath}' is not writeable`); + + this.homeDirPath = homeDirPath; + } + + /** + * @returns {string} + */ + getHomeDirPath() { + return this.homeDirPath; + } +} + +module.exports = HomeDirIsNotWritableError; diff --git a/packages/dashmate/src/config/errors/InvalidConfigFileFormatError.js b/packages/dashmate/src/config/errors/InvalidConfigFileFormatError.js new file mode 100644 index 00000000000..c7dfd4eb897 --- /dev/null +++ b/packages/dashmate/src/config/errors/InvalidConfigFileFormatError.js @@ -0,0 +1,30 @@ +const AbstractError = require('../../errors/AbstractError'); + +class InvalidConfigFileFormatError extends AbstractError { + /** + * @param {string} configFilePath + * @param {Error} error + */ + constructor(configFilePath, error) { + super(`Invalid '${configFilePath}' config format: ${error.message}`); + + this.error = error; + this.configFilePath = configFilePath; + } + + /** + * @returns {Error} + */ + getError() { + return this.error; + } + + /** + * @returns {string} + */ + getConfigFilePath() { + return this.configFilePath; + } +} + +module.exports = InvalidConfigFileFormatError; diff --git a/packages/dashmate/src/config/errors/InvalidOptionError.js b/packages/dashmate/src/config/errors/InvalidOptionError.js new file mode 100644 index 00000000000..77f8578708b --- /dev/null +++ b/packages/dashmate/src/config/errors/InvalidOptionError.js @@ -0,0 +1,40 @@ +const AbstractError = require('../../errors/AbstractError'); + +class InvalidOptionPathError extends AbstractError { + /** + * @param {string} path + * @param {*} value + * @param {ErrorObject[]} errors + * @param {string} message + */ + constructor(path, value, errors, message) { + super(message); + + this.path = path; + this.value = value; + this.error = errors; + } + + /** + * @returns {string} + */ + getPath() { + return this.path; + } + + /** + * @returns {*} + */ + getValue() { + return this.value; + } + + /** + * @returns {ErrorObject[]} + */ + getErrors() { + return this.errors; + } +} + +module.exports = InvalidOptionPathError; diff --git a/packages/dashmate/src/config/errors/InvalidOptionPathError.js b/packages/dashmate/src/config/errors/InvalidOptionPathError.js new file mode 100644 index 00000000000..b11d150acd5 --- /dev/null +++ b/packages/dashmate/src/config/errors/InvalidOptionPathError.js @@ -0,0 +1,21 @@ +const AbstractError = require('../../errors/AbstractError'); + +class InvalidOptionPathError extends AbstractError { + /** + * @param {string} path + */ + constructor(path) { + super(`There is no option with '${path}' path`); + + this.path = path; + } + + /** + * @returns {string} + */ + getPath() { + return this.path; + } +} + +module.exports = InvalidOptionPathError; diff --git a/packages/dashmate/src/config/errors/InvalidOptionsError.js b/packages/dashmate/src/config/errors/InvalidOptionsError.js new file mode 100644 index 00000000000..dd0b7c4b044 --- /dev/null +++ b/packages/dashmate/src/config/errors/InvalidOptionsError.js @@ -0,0 +1,31 @@ +const AbstractError = require('../../errors/AbstractError'); + +class InvalidOptionsError extends AbstractError { + /** + * @param {Object} options + * @param {ErrorObject[]} errors + * @param {string} message + */ + constructor(options, errors, message) { + super(message); + + this.options = options; + this.errors = errors; + } + + /** + * @returns {Object} + */ + getOptions() { + return this.options; + } + + /** + * @returns {ErrorObject[]} + */ + getErrors() { + return this.errors; + } +} + +module.exports = InvalidOptionsError; diff --git a/packages/dashmate/src/config/errors/OptionIsNotSetError.js b/packages/dashmate/src/config/errors/OptionIsNotSetError.js new file mode 100644 index 00000000000..8f0650a2874 --- /dev/null +++ b/packages/dashmate/src/config/errors/OptionIsNotSetError.js @@ -0,0 +1,37 @@ +const AbstractError = require('../../errors/AbstractError'); + +class OptionIsNotSetError extends AbstractError { + /** + * @param {Config} config + * @param {string} path + */ + constructor(config, path) { + super(`${path} option is not set in ${config.getName()} config`); + + this.config = config; + this.path = path; + } + + /** + * @returns {string} + */ + getPath() { + return this.path; + } + + /** + * @returns {Config} + */ + getConfig() { + return this.config; + } + + /** + * @returns {ErrorObject[]} + */ + getErrors() { + return this.errors; + } +} + +module.exports = OptionIsNotSetError; diff --git a/packages/dashmate/src/config/systemConfigs/createSystemConfigsFactory.js b/packages/dashmate/src/config/systemConfigs/createSystemConfigsFactory.js new file mode 100644 index 00000000000..12d789d36ee --- /dev/null +++ b/packages/dashmate/src/config/systemConfigs/createSystemConfigsFactory.js @@ -0,0 +1,32 @@ +const Config = require('../Config'); + +const ConfigFile = require('../configFile/ConfigFile'); + +const packageJson = require('../../../package.json'); + +/** + * @param {Object} systemConfigs + * @return {createSystemConfigs} + */ +function createSystemConfigsFactory(systemConfigs) { + /** + * @typedef {createSystemConfigs} + * @returns {ConfigFile} + */ + function createSystemConfigs() { + const configs = Object.entries(systemConfigs).map(([name, options]) => ( + new Config(name, options) + )); + + return new ConfigFile( + configs, + packageJson.version, + 'base', + null, + ); + } + + return createSystemConfigs; +} + +module.exports = createSystemConfigsFactory; diff --git a/packages/dashmate/src/config/systemConfigs/isSystemConfigFactory.js b/packages/dashmate/src/config/systemConfigs/isSystemConfigFactory.js new file mode 100644 index 00000000000..b5f85fd1fb7 --- /dev/null +++ b/packages/dashmate/src/config/systemConfigs/isSystemConfigFactory.js @@ -0,0 +1,19 @@ +/** + * @param {Object} systemConfigs + * @return {isSystemConfig} + */ +function isSystemConfigFactory(systemConfigs) { + /** + * @typedef {isSystemConfig} + * @param configName + * @return {boolean} + */ + function isSystemConfig(configName) { + const systemConfigNames = Object.keys(systemConfigs); + return systemConfigNames.includes(configName); + } + + return isSystemConfig; +} + +module.exports = isSystemConfigFactory; diff --git a/packages/dashmate/src/constants.js b/packages/dashmate/src/constants.js new file mode 100644 index 00000000000..c9bb589de8b --- /dev/null +++ b/packages/dashmate/src/constants.js @@ -0,0 +1,56 @@ +const path = require('path'); +const os = require('os'); + +const networks = { + NETWORK_LOCAL: 'local', + NETWORK_DEVNET: 'devnet', + NETWORK_TESTNET: 'testnet', + NETWORK_MAINNET: 'mainnet', +}; + +const presets = { + PRESET_LOCAL: 'local', + PRESET_TESTNET: 'testnet', + PRESET_MAINNET: 'mainnet', +}; + +const nodeTypes = { + NODE_TYPE_MASTERNODE: 'masternode', + NODE_TYPE_FULLNODE: 'fullnode', +}; + +const quorumNames = { + LLMQ_TYPE_TEST: 'llmq_test', +}; + +const quorumTypes = { + LLMQ_TYPE_TEST: 100, +}; + +const MASTERNODE_DASH_AMOUNT = 1000; + +const HOME_DIR_PATH = process.env.DASHMATE_HOME_DIR + ? process.env.DASHMATE_HOME_DIR + : path.resolve(os.homedir(), '.dashmate'); +const CONFIG_FILE_PATH = path.join(HOME_DIR_PATH, 'config.json'); + +const OUTPUT_FORMATS = { + JSON: 'json', + PLAIN: 'plain', +}; + +module.exports = { + ...networks, + ...presets, + ...nodeTypes, + ...quorumNames, + NETWORKS: Object.values(networks), + PRESETS: Object.values(presets), + NODE_TYPES: Object.values(nodeTypes), + QUORUM_NAMES: Object.values(quorumNames), + QUORUM_TYPES: quorumTypes, + MASTERNODE_DASH_AMOUNT, + HOME_DIR_PATH, + CONFIG_FILE_PATH, + OUTPUT_FORMATS, +}; diff --git a/packages/dashmate/src/core/CoreService.js b/packages/dashmate/src/core/CoreService.js new file mode 100644 index 00000000000..23ae6b00642 --- /dev/null +++ b/packages/dashmate/src/core/CoreService.js @@ -0,0 +1,58 @@ +class CoreService { + /** + * + * @param {Config} config + * @param {RpcClient} rpcClient + * @param {Container} dockerContainer + */ + constructor(config, rpcClient, dockerContainer) { + this.config = config; + this.rpcClient = rpcClient; + this.dockerContainer = dockerContainer; + } + + /** + * @return {Config} + */ + getConfig() { + return this.config; + } + + /** + * Get RPC Client + * + * @return {RpcClient} + */ + getRpcClient() { + return this.rpcClient; + } + + /** + * Is Core running? + * + * @return {Promise} + */ + async isRunning() { + const { State: { Status: status } } = await this.dockerContainer.inspect(); + + return status === 'running'; + } + + /** + * Stop Core service + * + * @return {Promise} + */ + async stop() { + if (!await this.isRunning()) { + return false; + } + + await this.dockerContainer.stop(); + await this.dockerContainer.remove(); + + return true; + } +} + +module.exports = CoreService; diff --git a/packages/dashmate/src/core/activateCoreSpork.js b/packages/dashmate/src/core/activateCoreSpork.js new file mode 100644 index 00000000000..46a6532ef9a --- /dev/null +++ b/packages/dashmate/src/core/activateCoreSpork.js @@ -0,0 +1,11 @@ +/** + * @typedef activateCoreSpork + * @param {RpcClient} rpcClient + * @param {string} spork + * @returns {Promise} + */ +async function activateCoreSpork(rpcClient, spork) { + await rpcClient.spork(spork, 0); +} + +module.exports = activateCoreSpork; diff --git a/packages/dashmate/src/core/createRpcClient.js b/packages/dashmate/src/core/createRpcClient.js new file mode 100644 index 00000000000..e15c3cac83a --- /dev/null +++ b/packages/dashmate/src/core/createRpcClient.js @@ -0,0 +1,29 @@ +const RpcClient = require('@dashevo/dashd-rpc/promise'); + +/** + * Create Core JSON RPC Client + * + * @typedef createRpcClient + * @param {Object} [config] + * @param {string} [config.protocol=http] + * @param {string} [config.user=dashrpc] + * @param {string} [config.pass=password] + * @param {string} [config.host=127.0.0.1] + * @param {number} [config.port=20002] + * @return {RpcClient|PromisifyModule} + */ +function createRpcClient(config = {}) { + // eslint-disable-next-line no-param-reassign + config = { + protocol: 'http', + user: 'dashrpc', + pass: 'password', + host: '127.0.0.1', + port: 20002, + ...config, + }; + + return new RpcClient(config); +} + +module.exports = createRpcClient; diff --git a/packages/dashmate/src/core/generateBlsKeys.js b/packages/dashmate/src/core/generateBlsKeys.js new file mode 100644 index 00000000000..58fbce377f8 --- /dev/null +++ b/packages/dashmate/src/core/generateBlsKeys.js @@ -0,0 +1,24 @@ +const crypto = require('crypto'); +const BlsSignatures = require('bls-signatures'); + +/** + * Generate BLS keys + * + * @typedef {generateBlsKeys} + * @return {Promise<{privateKey: *, address: *}>} + */ +async function generateBlsKeys() { + const blsSignatures = await BlsSignatures(); + const { PrivateKey: BlsPrivateKey } = blsSignatures; + + const randomBytes = new Uint8Array(crypto.randomBytes(256)); + const operatorPrivateKey = BlsPrivateKey.fromBytes(randomBytes, true); + const operatorPublicKey = operatorPrivateKey.getPublicKey(); + + return { + publicKey: Buffer.from(operatorPublicKey.serialize()).toString('hex'), + privateKey: Buffer.from(operatorPrivateKey.serialize()).toString('hex'), + }; +} + +module.exports = generateBlsKeys; diff --git a/packages/dashmate/src/core/quorum/waitForMasternodeProbes.js b/packages/dashmate/src/core/quorum/waitForMasternodeProbes.js new file mode 100644 index 00000000000..61a02ff49e1 --- /dev/null +++ b/packages/dashmate/src/core/quorum/waitForMasternodeProbes.js @@ -0,0 +1,93 @@ +const { LLMQ_TYPE_TEST } = require('../../constants'); + +/** + * Checks all mastrenodoes probes to incterconnected masternodes + * + * @param {RpcClient[]} rpcClients + * @param {Function} bumpMockTime + * + * @return {Promise} + */ +async function checkProbes(rpcClients, bumpMockTime) { + let masternodes = await Promise.all( + rpcClients.map((rpc) => { + const promise = rpc.masternode('status'); + + return promise.then(({ result }) => ({ rpc, status: result })); + }), + ); + + masternodes = masternodes.filter((entry) => !entry.status); + + for (const { rpc, status } of masternodes) { + const { result: { session, quorumConnections } } = await rpc.quorum('dkgstatus', 2); + + if (session.length === 0) { + continue; + } + + const llmqConnection = quorumConnections + .find((connection) => connection.llmqType === LLMQ_TYPE_TEST); + + if (!llmqConnection) { + await bumpMockTime(); + + return false; + } + + for (const connection of llmqConnection.quorumConnections) { + if (connection.proTxHash === status.proTxHash) { + continue; + } + + if (!connection.outbound) { + const { result: mnInfo } = await rpc.protx('info', connection.proTxHash); + + for (const masternode in masternodes) { + if (connection.proTxHash === masternode.status.proTxHash) { + // MN is expected to be online and functioning, so let's verify that the last successful + // probe is not too old. Probes are retried after 50 minutes, while DKGs consider + // a probe as failed after 60 minutes + if (mnInfo.metaInfo.lastOutboundSuccessElapsed > 55 * 60) { + await bumpMockTime(); + + return false; + } + // MN is expected to be offline, so let's only check that + // the last probe is not too long ago + } else if (mnInfo.metaInfo.lastOutboundAttemptElapsed > 55 * 60 + && mnInfo.metaInfo.lastOutboundSuccessElapsed > 55 * 60) { + await bumpMockTime(); + + return false; + } + } + } + } + } + + return true; +} + +/** + * + * @param {RpcClient[]} rpcClients + * @param {Function} bumpMockTime + * @param {number} [timeout] + * @return {Promise} + */ +async function waitForMasternodeProbes(rpcClients, bumpMockTime, timeout = 30000) { + const deadline = Date.now() + timeout; + + let isReady = false; + + while (!isReady) { + isReady = await checkProbes(rpcClients, bumpMockTime); + + if (Date.now() > deadline) { + throw new Error(`waitForMasternodeProbes deadline of ${timeout} exceeded`); + } + } +} + +module.exports = waitForMasternodeProbes; diff --git a/packages/dashmate/src/core/quorum/waitForQuorumCommitements.js b/packages/dashmate/src/core/quorum/waitForQuorumCommitements.js new file mode 100644 index 00000000000..0c32063d9de --- /dev/null +++ b/packages/dashmate/src/core/quorum/waitForQuorumCommitements.js @@ -0,0 +1,58 @@ +const wait = require('../../util/wait'); + +const { QUORUM_TYPES } = require('../../constants'); + +/** + * + * @param {string} quorumHash + * @param {RpcClient[]} rpcClients + * @return {Promise} + */ +async function checkDKGSessionCommitments(quorumHash, rpcClients) { + for (const rpc of rpcClients) { + const { result: dkgStatus } = await rpc.quorum('dkgstatus'); + + const testQuorumCommitment = dkgStatus.minableCommitments + .find((commitment) => commitment.llmqType === QUORUM_TYPES.LLMQ_TYPE_TEST); + + if (!testQuorumCommitment) { + return false; + } + + if (testQuorumCommitment.quorumHash !== quorumHash) { + return false; + } + } + + return true; +} + +/** + * + * @param {RpcClient[]} rpcClients + * @param {string} quorumHash + * @param {number} [timeout] + * @param {number} [waitBeforeRetry] + * @return {Promise} + */ +async function waitForQuorumCommitments( + rpcClients, + quorumHash, + timeout = 60000, + waitBeforeRetry = 100, +) { + const deadline = Date.now() + timeout; + let isReady = false; + + while (!isReady) { + await wait(waitBeforeRetry); + + isReady = await checkDKGSessionCommitments(quorumHash, rpcClients); + + if (Date.now() > deadline) { + throw new Error(`waitForQuorumCommitments deadline of ${timeout} exceeded`); + } + } +} + +module.exports = waitForQuorumCommitments; diff --git a/packages/dashmate/src/core/quorum/waitForQuorumConnections.js b/packages/dashmate/src/core/quorum/waitForQuorumConnections.js new file mode 100644 index 00000000000..64ab55b499f --- /dev/null +++ b/packages/dashmate/src/core/quorum/waitForQuorumConnections.js @@ -0,0 +1,73 @@ +const wait = require('../../util/wait'); + +const { LLMQ_TYPE_TEST } = require('../../constants'); + +/** + * @param {RpcClient} rpcClient + * @param {number} expectedConnectionsCount + * @return {Promise} + */ +async function checkQuorumConnections(rpcClient, expectedConnectionsCount) { + const { result: dkgStatus } = await rpcClient.quorum('dkgstatus'); + + if (dkgStatus.session.length === 0) { + return false; + } + + const llmqConnection = dkgStatus.quorumConnections + .find((connection) => connection.llmqType === LLMQ_TYPE_TEST); + + if (!llmqConnection) { + return false; + } + + const connectionsCount = llmqConnection.quorumConnections + .filter((connection) => connection.connected) + .length; + + return connectionsCount >= expectedConnectionsCount; +} + +/** + * + * @param {RpcClient[]} rpcClients + * @param {number} expectedConnectionsCount + * @param {Function} bumpMockTime + * @param {number} [timeout] + * @return {Promise} + */ +async function waitForQuorumConnections( + rpcClients, + expectedConnectionsCount, + bumpMockTime, + timeout = 300000, +) { + const deadline = Date.now() + timeout; + const readyNodes = new Set(); + const nodesToWait = 3; + + while (readyNodes.size < nodesToWait) { + await Promise.all(rpcClients.map(async (rpcClient, i) => { + const isReady = await checkQuorumConnections( + rpcClient, + expectedConnectionsCount, + ); + + if (isReady) { + readyNodes.add(i); + } + })); + + if (readyNodes.size < nodesToWait) { + await bumpMockTime(); + + await wait(1000); + } + + if (Date.now() > deadline) { + throw new Error(`waitForQuorumConnections deadline of ${timeout} exceeded`); + } + } +} + +module.exports = waitForQuorumConnections; diff --git a/packages/dashmate/src/core/quorum/waitForQuorumPhase.js b/packages/dashmate/src/core/quorum/waitForQuorumPhase.js new file mode 100644 index 00000000000..55e5858183c --- /dev/null +++ b/packages/dashmate/src/core/quorum/waitForQuorumPhase.js @@ -0,0 +1,100 @@ +const wait = require('../../util/wait'); + +const { LLMQ_TYPE_TEST } = require('../../constants'); + +/** + * + * @param {RpcClient[]} rpcClients + * @param {string} quorumHash + * @param {number} phase + * @param {number} expectedMemberCount + * @param {string} [checkReceivedMessagesType] + * @param {number} [checkReceivedMessagesCount] + * @return {Promise} + */ +async function checkDKGSessionPhase( + rpcClients, + quorumHash, + phase, + expectedMemberCount, + checkReceivedMessagesType, + checkReceivedMessagesCount = 0, +) { + let memberCount = 0; + + for (const rpcClient of rpcClients) { + const { result: dkgStatus } = await rpcClient.quorum('dkgstatus'); + const { session } = dkgStatus; + + const llmqSession = session.find((s) => s.llmqType === LLMQ_TYPE_TEST); + + if (!llmqSession) { + continue; + } + + memberCount += 1; + + const quorumHashDoesntMatch = llmqSession.status.quorumHash !== quorumHash; + + const sessionPhaseDoesntMatch = !llmqSession.status.phase && llmqSession.status.phase !== phase; + + const receivedMessagesDoNotMatch = checkReceivedMessagesType + && (llmqSession[checkReceivedMessagesType] < checkReceivedMessagesCount); + + const checkFailed = quorumHashDoesntMatch + || sessionPhaseDoesntMatch + || receivedMessagesDoNotMatch; + + if (checkFailed) { + return false; + } + } + + return memberCount === expectedMemberCount; +} + +/** + * + * @param {RpcClient[]} rpcClients + * @param {string} quorumHash + * @param {number} phase + * @param {number} expectedMemberCount + * @param {string} [checkReceivedMessagesType] + * @param {number} [checkReceivedMessagesCount] + * @param {number} [timeout] + * @param {number} [checkInterval] + * @return {Promise} + */ +async function waitForQuorumPhase( + rpcClients, + quorumHash, + phase, + expectedMemberCount, + checkReceivedMessagesType, + checkReceivedMessagesCount, + timeout = 30000, + checkInterval = 100, +) { + const deadline = Date.now() + timeout; + + let isReady = false; + + while (isReady) { + await wait(checkInterval); + + isReady = await checkDKGSessionPhase( + rpcClients, + quorumHash, + phase, + expectedMemberCount, + checkReceivedMessagesType, + checkReceivedMessagesCount, + ); + + if (Date.now() > deadline) { + throw new Error(`waitForQuorumPhase deadline of ${timeout} exceeded`); + } + } +} + +module.exports = waitForQuorumPhase; diff --git a/packages/dashmate/src/core/startCoreFactory.js b/packages/dashmate/src/core/startCoreFactory.js new file mode 100644 index 00000000000..29755cac978 --- /dev/null +++ b/packages/dashmate/src/core/startCoreFactory.js @@ -0,0 +1,84 @@ +const CoreService = require('./CoreService'); + +/** + * @param {createRpcClient} createRpcClient + * @param {waitForCoreStart} waitForCoreStart + * @param {waitForCoreSync} waitForCoreSync + * @param {DockerCompose} dockerCompose + * @return {startCore} + */ +function startCoreFactory( + createRpcClient, + waitForCoreStart, + waitForCoreSync, + dockerCompose, +) { + /** + * @typedef startCore + * @param {Config} config + * @param {Object} [options] + * @param {boolean} [options.wallet=false] + * @param {boolean} [options.addressIndex=false] + * @return {CoreService} + */ + async function startCore(config, options = {}) { + // eslint-disable-next-line no-param-reassign + options = { + wallet: false, + addressIndex: false, + ...options, + }; + + // Run Core service + + const coreCommand = [ + 'dashd', + ]; + const isMasternode = config.get('core.masternode.enable'); + const operatorKey = config.get('core.masternode.operator.privateKey'); + + if (options.wallet) { + coreCommand.push('--disablewallet=0'); + } else if (isMasternode && Boolean(operatorKey)) { + coreCommand.push(`-masternodeblsprivkey=${operatorKey}`); + } + + if (options.addressIndex) { + coreCommand.push('--addressindex=1'); + } + + const coreContainer = await dockerCompose.runService( + config.toEnvs(), + 'core', + coreCommand, + [ + '--service-ports', + '--detach', + ], + ); + + const rpcClient = createRpcClient( + { + port: config.get('core.rpc.port'), + user: config.get('core.rpc.user'), + pass: config.get('core.rpc.password'), + }, + ); + + const coreService = new CoreService( + config, + rpcClient, + coreContainer, + ); + + // Wait Core to start + + await waitForCoreStart(coreService); + + return coreService; + } + + return startCore; +} + +module.exports = startCoreFactory; diff --git a/packages/dashmate/src/core/waitForBlocks.js b/packages/dashmate/src/core/waitForBlocks.js new file mode 100644 index 00000000000..fb5a36d074f --- /dev/null +++ b/packages/dashmate/src/core/waitForBlocks.js @@ -0,0 +1,24 @@ +const wait = require('../util/wait'); + +/** + * Wait for blocks to be generated + * @typedef waitForBlocks + * @param {CoreService} coreService + * @param {number} blocks + * @param {function(confirmations: number)} [progressCallback] + * @returns {Promise} + */ +async function waitForBlocks(coreService, blocks, progressCallback = () => {}) { + let { result: currentBlock } = await coreService.getRpcClient().getBlockCount(); + const lastBlock = currentBlock + blocks; + + do { + await wait(20000); + + ({ result: currentBlock } = await coreService.getRpcClient().getBlockCount()); + + await progressCallback(blocks - (lastBlock - currentBlock)); + } while (currentBlock < lastBlock); +} + +module.exports = waitForBlocks; diff --git a/packages/dashmate/src/core/waitForConfirmations.js b/packages/dashmate/src/core/waitForConfirmations.js new file mode 100644 index 00000000000..fdef427c5bb --- /dev/null +++ b/packages/dashmate/src/core/waitForConfirmations.js @@ -0,0 +1,34 @@ +const wait = require('../util/wait'); + +/** + * Wait for confirmations to be reached + * @typedef waitForConfirmations + * @param {CoreService} coreService + * @param {string} txHash + * @param {number} confirmations + * @param {function(confirmations: number)} [progressCallback] + * @returns {Promise} + */ +async function waitForConfirmations( + coreService, + txHash, + confirmations, + progressCallback = () => {}, +) { + let confirmationsReached = 0; + + do { + await wait(20000); + ({ result: { confirmations: confirmationsReached } } = await coreService + .getRpcClient() + .getrawtransaction(txHash, 1)); + + if (confirmationsReached === undefined) { + confirmationsReached = 0; + } + + await progressCallback(confirmationsReached); + } while (confirmationsReached < confirmations); +} + +module.exports = waitForConfirmations; diff --git a/packages/dashmate/src/core/waitForCorePeersConnected.js b/packages/dashmate/src/core/waitForCorePeersConnected.js new file mode 100644 index 00000000000..97bbeacaeb3 --- /dev/null +++ b/packages/dashmate/src/core/waitForCorePeersConnected.js @@ -0,0 +1,24 @@ +const wait = require('../util/wait'); + +/** + * Wait Core to connect to peers + * + * @typedef {waitForCorePeersConnected} + * @param {RpcClient} rpcClient + * @return {Promise} + */ +async function waitForCorePeersConnected(rpcClient) { + let hasPeers = false; + + do { + const { result: peers } = await rpcClient.getPeerInfo(); + + hasPeers = peers && peers.length > 0; + + if (!hasPeers) { + await wait(10000); + } + } while (!hasPeers); +} + +module.exports = waitForCorePeersConnected; diff --git a/packages/dashmate/src/core/waitForCoreStart.js b/packages/dashmate/src/core/waitForCoreStart.js new file mode 100644 index 00000000000..00bf1f86e27 --- /dev/null +++ b/packages/dashmate/src/core/waitForCoreStart.js @@ -0,0 +1,33 @@ +const wait = require('../util/wait'); + +/** + * Wait for Core to start + * + * @typedef {waitForCoreStart} + * @param {CoreService} coreService + * @return {Promise} + */ +async function waitForCoreStart(coreService) { + let retries = 0; + let isReady = false; + const maxRetries = 120; // ~2 minutes + + do { + try { + // just any random request + await coreService.getRpcClient().ping(); + + isReady = true; + } catch (e) { + // just wait 1 second before next try + await wait(1000); + ++retries; + } + } while (!isReady && retries < maxRetries); + + if (!isReady) { + throw new Error('Could not connect to Core RPC'); + } +} + +module.exports = waitForCoreStart; diff --git a/packages/dashmate/src/core/waitForCoreSync.js b/packages/dashmate/src/core/waitForCoreSync.js new file mode 100644 index 00000000000..919a891e9a8 --- /dev/null +++ b/packages/dashmate/src/core/waitForCoreSync.js @@ -0,0 +1,31 @@ +const wait = require('../util/wait'); + +/** + * Wait Core to be synced + * + * @typedef {waitForCoreSync} + * @param {RpcClient} rpcClient + * @param {function(progress: number)} [progressCallback] + * @return {Promise} + */ +async function waitForCoreSync(rpcClient, progressCallback = () => {}) { + let isSynced = false; + let isBlockchainSynced = false; + let verificationProgress = 0.0; + + do { + ({ + result: { IsSynced: isSynced, IsBlockchainSynced: isBlockchainSynced }, + } = await rpcClient.mnsync('status')); + ({ + result: { verificationprogress: verificationProgress }, + } = await rpcClient.getBlockchainInfo()); + + if (!isSynced || !isBlockchainSynced) { + await wait(10000); + progressCallback(verificationProgress); + } + } while (!isSynced || !isBlockchainSynced); +} + +module.exports = waitForCoreSync; diff --git a/packages/dashmate/src/core/waitForMasternodesSync.js b/packages/dashmate/src/core/waitForMasternodesSync.js new file mode 100644 index 00000000000..8125d4f577d --- /dev/null +++ b/packages/dashmate/src/core/waitForMasternodesSync.js @@ -0,0 +1,47 @@ +const wait = require('../util/wait'); + +/** + * Wait Core to be synced + * + * @typedef {waitForCoreSync} + * @param {RpcClient} rpcClient + * @param {function(progress: number)} [progressCallback] + * @return {Promise} + */ +async function waitForMasternodesSync(rpcClient, progressCallback = () => {}) { + let isSynced = false; + let verificationProgress = 0.0; + + do { + try { + await rpcClient.mnsync('next'); + } catch (e) { + // Core RPC is not started yet + if (!e.message.includes('Dash JSON-RPC: Request Error: ') && e.code !== -28) { + throw e; + } + + progressCallback(verificationProgress); + + // Wait for Core RPC is started + await wait(50); + + continue; + } + + ({ + result: { IsSynced: isSynced }, + } = await rpcClient.mnsync('status')); + ({ + result: { verificationprogress: verificationProgress }, + } = await rpcClient.getBlockchainInfo()); + + if (!isSynced) { + progressCallback(verificationProgress); + + await wait(300); + } + } while (!isSynced); +} + +module.exports = waitForMasternodesSync; diff --git a/packages/dashmate/src/core/waitForNodesToHaveTheSameHeight.js b/packages/dashmate/src/core/waitForNodesToHaveTheSameHeight.js new file mode 100644 index 00000000000..1649f114442 --- /dev/null +++ b/packages/dashmate/src/core/waitForNodesToHaveTheSameHeight.js @@ -0,0 +1,57 @@ +/** + * Wait for all nodes to reach the height of the most advanced chain + * @param {RpcClient[]} rpcClients + * @param {number} [timeout] - timeout throw error if blocks aren't synced on all nodes + * @param {number} [waitTime] - wait interval + * @return {Promise} + */ +async function waitForNodesToHaveTheSameHeight(rpcClients, timeout = 60000, waitTime = 1000) { + const heights = await Promise.all( + rpcClients.map(async (rpc) => { + const promise = rpc.getBlockCount(); + + return promise.then(({ result }) => result); + }), + ); + + const maxHeight = Math.max(...heights); + + const deadline = Date.now() + timeout; + + let isReady = false; + + while (!isReady) { + const tips = await Promise.all( + rpcClients.map((rpc) => { + const promise = rpc.waitForBlockHeight(maxHeight, waitTime); + + return promise.then(({ result }) => result); + }), + ); + + const allTipsAreSameHeight = tips + .filter((tip) => tip.height !== maxHeight) + .length === 0; + + let allTipsAreSameHash = false; + + if (allTipsAreSameHeight) { + allTipsAreSameHash = tips + .filter((tip) => tip.hash !== tips[0].hash) + .length === 0; + + if (!allTipsAreSameHash) { + throw new Error('Block sync failed, mismatched block hashes'); + } + + // Exit the cycle once reached this point + isReady = true; + } + + if (Date.now() > deadline) { + throw new Error(`Syncing blocks to height ${maxHeight} timed out`); + } + } +} + +module.exports = waitForNodesToHaveTheSameHeight; diff --git a/packages/dashmate/src/core/waitForNodesToHaveTheSameSporks.js b/packages/dashmate/src/core/waitForNodesToHaveTheSameSporks.js new file mode 100644 index 00000000000..d0f868e84f0 --- /dev/null +++ b/packages/dashmate/src/core/waitForNodesToHaveTheSameSporks.js @@ -0,0 +1,40 @@ +const isEqual = require('lodash.isequal'); + +/** + * @param {CoreService[]} coreServices + * @return {Promise} + */ +async function checkSporksAreTheSame(coreServices) { + const { result: initialSporks } = await coreServices[0].getRpcClient().spork('show'); + + for (const coreService of coreServices.slice(1)) { + const { result: sporks } = await coreService.getRpcClient().spork('show'); + + if (!isEqual(initialSporks, sporks)) { + return false; + } + } + + return true; +} + +/** + * @param {CoreService[]} coreServices + * @param {number} [timeout] + * @return {Promise} + */ +async function waitForNodesToHaveTheSameSporks(coreServices, timeout = 30000) { + const deadline = Date.now() + timeout; + + let isReady = false; + + while (!isReady) { + isReady = await checkSporksAreTheSame(coreServices); + + if (Date.now() > deadline) { + throw new Error(`Syncing sporks deadline of ${timeout} exceeded`); + } + } +} + +module.exports = waitForNodesToHaveTheSameSporks; diff --git a/packages/dashmate/src/core/wallet/createNewAddress.js b/packages/dashmate/src/core/wallet/createNewAddress.js new file mode 100644 index 00000000000..afb2eb5f475 --- /dev/null +++ b/packages/dashmate/src/core/wallet/createNewAddress.js @@ -0,0 +1,18 @@ +/** + * Create new wallet address + * + * @typedef {createNewAddress} + * @param {CoreService} coreService + * @return {Promise<{privateKey: *, address: *}>} + */ +async function createNewAddress(coreService) { + const { result: address } = await coreService.getRpcClient().getNewAddress(); + const { result: privateKey } = await coreService.getRpcClient().dumpPrivKey(address); + + return { + address, + privateKey, + }; +} + +module.exports = createNewAddress; diff --git a/packages/dashmate/src/core/wallet/generateBlocks.js b/packages/dashmate/src/core/wallet/generateBlocks.js new file mode 100644 index 00000000000..da0564d3d04 --- /dev/null +++ b/packages/dashmate/src/core/wallet/generateBlocks.js @@ -0,0 +1,36 @@ +const { PrivateKey } = require('@dashevo/dashcore-lib'); + +/** + * + * @typedef {generateBlocks} + * @param {CoreService} coreService + * @param {number} blocks + * @param {string} network + * @param {function(balance: number)} [progressCallback] + * @returns {Promise} + */ +async function generateBlocks( + coreService, + blocks, + network, + progressCallback = () => {}, +) { + const privateKey = new PrivateKey(); + const address = privateKey.toAddress(network).toString(); + + let generatedBlocks = 0; + + do { + const { result: blockHashes } = await coreService + .getRpcClient() + .generateToAddress(blocks, address, 10000000); + + generatedBlocks += blockHashes.length; + + if (blockHashes.length > 0) { + await progressCallback(generatedBlocks); + } + } while (generatedBlocks < blocks); +} + +module.exports = generateBlocks; diff --git a/packages/dashmate/src/core/wallet/generateToAddress.js b/packages/dashmate/src/core/wallet/generateToAddress.js new file mode 100644 index 00000000000..bb364686c34 --- /dev/null +++ b/packages/dashmate/src/core/wallet/generateToAddress.js @@ -0,0 +1,33 @@ +const { toDash } = require('../../util/satoshiConverter'); + +/** + * + * @typedef generateToAddress + * @param {CoreService} coreService + * @param {number} amount + * @param {string} address + * @param {function(balance: number)} [progressCallback] + * @returns {Promise} + */ +async function generateToAddress( + coreService, + amount, + address, + progressCallback = () => {}, +) { + let addressBalance = 0; + + do { + await coreService.getRpcClient().generateToAddress(1, address, 10000000); + + const { result: { balance } } = await coreService.getRpcClient().getAddressBalance({ + addresses: [address], + }); + + addressBalance = toDash(balance); + + await progressCallback(addressBalance); + } while (addressBalance < amount); +} + +module.exports = generateToAddress; diff --git a/packages/dashmate/src/core/wallet/getAddressBalance.js b/packages/dashmate/src/core/wallet/getAddressBalance.js new file mode 100644 index 00000000000..cc307abab58 --- /dev/null +++ b/packages/dashmate/src/core/wallet/getAddressBalance.js @@ -0,0 +1,19 @@ +const { toDash } = require('../../util/satoshiConverter'); + +/** + * Get balance of the address + * + * @typedef {getAddressBalance} + * @param {CoreService} coreService + * @param {string} address + * @return {Promise} + */ +async function getAddressBalance(coreService, address) { + const { result: { balance } } = await coreService.getRpcClient().getAddressBalance({ + addresses: [address], + }); + + return toDash(balance); +} + +module.exports = getAddressBalance; diff --git a/packages/dashmate/src/core/wallet/getInputsForAmountFactory.js b/packages/dashmate/src/core/wallet/getInputsForAmountFactory.js new file mode 100644 index 00000000000..8b7470449e7 --- /dev/null +++ b/packages/dashmate/src/core/wallet/getInputsForAmountFactory.js @@ -0,0 +1,38 @@ +/** + * Get inputs to build a transaction + * + * @param {RpcClient} coreClient + * @return {getInputsForAmount} + */ +function getInputsForAmountFactory(coreClient) { + /** + * @typedef {getInputsForAmount} + * @param {string} address + * @param {number} amountInSatoshi + * @return {Promise} + */ + async function getInputsForAmount(address, amountInSatoshi) { + const { result: utxos } = await coreClient.getAddressUtxos({ addresses: [address] }); + + const sortedUtxos = utxos + .sort((a, b) => a.satoshis > b.satoshis); + + const inputs = []; + let sum = 0; + let i = 0; + + do { + const input = sortedUtxos[i]; + inputs.push(input); + sum += input.satoshis; + + ++i; + } while (sum < amountInSatoshi && i < sortedUtxos.length); + + return inputs; + } + + return getInputsForAmount; +} + +module.exports = getInputsForAmountFactory; diff --git a/packages/dashmate/src/core/wallet/importPrivateKey.js b/packages/dashmate/src/core/wallet/importPrivateKey.js new file mode 100644 index 00000000000..b09fc8ea7e2 --- /dev/null +++ b/packages/dashmate/src/core/wallet/importPrivateKey.js @@ -0,0 +1,13 @@ +/** + * Import private key into wallet + * + * @typedef {importPrivateKey} + * @param {CoreService} coreService + * @param {string} privateKey + * @return {Promise} + */ +async function importPrivateKey(coreService, privateKey) { + return coreService.getRpcClient().importPrivKey(privateKey); +} + +module.exports = importPrivateKey; diff --git a/packages/dashmate/src/core/wallet/registerMasternode.js b/packages/dashmate/src/core/wallet/registerMasternode.js new file mode 100644 index 00000000000..e9544253502 --- /dev/null +++ b/packages/dashmate/src/core/wallet/registerMasternode.js @@ -0,0 +1,45 @@ +/** + * Get balance of the address + * + * @typedef {registerMasternode} + * @param {CoreService} coreService + * @param {string} collateralHash + * @param {string} ownerAddress + * @param {string} operatorPublicKey + * @param {string} fundSourceAddress + * @param {number} operatorReward + * @param {Config} config + * @return {Promise} + */ +async function registerMasternode( + coreService, + collateralHash, + ownerAddress, + operatorPublicKey, + fundSourceAddress, + operatorReward, + config, +) { + // get collateral index + const { result: masternodeOutputs } = await coreService.getRpcClient().masternode('outputs'); + + const collateralIndex = parseInt(masternodeOutputs[collateralHash], 10); + + const ipAndPort = `${config.get('externalIp', true)}:${config.get('core.p2p.port')}`; + + const { result: proRegTxId } = await coreService.getRpcClient().protx( + 'register', + collateralHash, // The txid of the 1000 Dash collateral funding transaction + collateralIndex, // The output index of the 1000 Dash funding transaction + ipAndPort, // Masternode IP address and port, in the format x.x.x.x:yyyy + ownerAddress, // The new Dash address for the owner/voting address + operatorPublicKey, // The Operator BLS public key + ownerAddress, // The new Dash address, or the address of a delegate, used for proposal voting + operatorReward, // The percentage of the block reward allocated to the operator as payment + fundSourceAddress, // A new or existing Dash address to receive the owner’s masternode rewards + ); + + return proRegTxId; +} + +module.exports = registerMasternode; diff --git a/packages/dashmate/src/core/wallet/sendToAddress.js b/packages/dashmate/src/core/wallet/sendToAddress.js new file mode 100644 index 00000000000..8679218c01c --- /dev/null +++ b/packages/dashmate/src/core/wallet/sendToAddress.js @@ -0,0 +1,62 @@ +const { Transaction } = require('@dashevo/dashcore-lib'); +const { toSatoshi } = require('../../util/satoshiConverter'); + +/** + * Send Dash to address + * + * @typedef {sendToAddress} + * @param {CoreService} coreService + * @param {string} fundSourcePrivateKey + * @param {string} fundSourceAddress + * @param {string} address + * @param {number} amount Amount in dash + * @return {Promise} + */ +async function sendToAddress( + coreService, + fundSourcePrivateKey, + fundSourceAddress, + address, + amount, +) { + const maxFee = 200000; + const feePerKb = 2000; + + const amountToSend = toSatoshi(amount); + + const { result: utxos } = await coreService + .getRpcClient() + .getAddressUtxos({ addresses: [fundSourceAddress] }); + + const sortedUtxos = utxos + .sort((a, b) => a.satoshis > b.satoshis); + + const inputs = []; + let sum = 0; + let i = 0; + + do { + const input = sortedUtxos[i]; + inputs.push(input); + sum += input.satoshis; + + ++i; + } while (sum < amountToSend + maxFee && i < sortedUtxos.length); + + const transaction = new Transaction(); + transaction.from(inputs) + .to(address, amountToSend) + .change(fundSourceAddress) + .feePerKb(feePerKb) + .sign(fundSourcePrivateKey); + + const { result: hash } = await coreService + .getRpcClient() + .sendrawtransaction( + transaction.serialize(), + ); + + return hash; +} + +module.exports = sendToAddress; diff --git a/packages/dashmate/src/core/wallet/waitForBalanceToConfirm.js b/packages/dashmate/src/core/wallet/waitForBalanceToConfirm.js new file mode 100644 index 00000000000..138d766ff93 --- /dev/null +++ b/packages/dashmate/src/core/wallet/waitForBalanceToConfirm.js @@ -0,0 +1,42 @@ +const { PrivateKey } = require('@dashevo/dashcore-lib'); +const wait = require('../../util/wait'); +const { toDash } = require('../../util/satoshiConverter'); +const { NETWORK_LOCAL } = require('../../constants'); + +/** + * + * @typedef waitForBalanceToConfirm + * @param {CoreService} coreService + * @param {string} network + * @param {string} address + * @param {function(balance: number)} [progressCallback] + * @returns {Promise} + */ +async function waitForBalanceToConfirm( + coreService, + network, + address, + progressCallback = () => {}, +) { + const privateKey = new PrivateKey(); + const randomAddress = privateKey.toAddress(network).toString(); + + let balanceImmature = 0; + do { + if (network === NETWORK_LOCAL) { + await coreService.getRpcClient().generateToAddress(1, randomAddress, 10000000); + } else { + await wait(2000); + } + + ({ result: { balance_immature: balanceImmature } } = await coreService + .getRpcClient() + .getAddressBalance({ + addresses: [address], + })); + + await progressCallback(toDash(balanceImmature)); + } while (balanceImmature > 0); +} + +module.exports = waitForBalanceToConfirm; diff --git a/packages/dashmate/src/createDIContainer.js b/packages/dashmate/src/createDIContainer.js new file mode 100644 index 00000000000..42c4b7fa2b6 --- /dev/null +++ b/packages/dashmate/src/createDIContainer.js @@ -0,0 +1,183 @@ +const { + createContainer: createAwilixContainer, + InjectionMode, + asFunction, + asValue, + asClass, +} = require('awilix'); + +const Docker = require('dockerode'); + +const ensureHomeDirFactory = require('./ensureHomeDirFactory'); +const ConfigFileJsonRepository = require('./config/configFile/ConfigFileJsonRepository'); +const createSystemConfigsFactory = require('./config/systemConfigs/createSystemConfigsFactory'); +const isSystemConfigFactory = require('./config/systemConfigs/isSystemConfigFactory'); +const migrateConfigFile = require('./config/configFile/migrateConfigFile'); +const systemConfigs = require('../configs/system'); + +const renderServiceTemplatesFactory = require('./templates/renderServiceTemplatesFactory'); +const writeServiceConfigsFactory = require('./templates/writeServiceConfigsFactory'); + +const DockerCompose = require('./docker/DockerCompose'); +const StartedContainers = require('./docker/StartedContainers'); +const stopAllContainersFactory = require('./docker/stopAllContainersFactory'); +const dockerPullFactory = require('./docker/dockerPullFactory'); +const resolveDockerHostIpFactory = require('./docker/resolveDockerHostIpFactory'); + +const startCoreFactory = require('./core/startCoreFactory'); +const createRpcClient = require('./core/createRpcClient'); +const waitForCoreStart = require('./core/waitForCoreStart'); +const waitForCoreSync = require('./core/waitForCoreSync'); +const waitForMasternodesSync = require('./core/waitForMasternodesSync'); +const waitForBlocks = require('./core/waitForBlocks'); +const waitForConfirmations = require('./core/waitForConfirmations'); +const generateBlsKeys = require('./core/generateBlsKeys'); +const activateCoreSpork = require('./core/activateCoreSpork'); +const waitForCorePeersConnected = require('./core/waitForCorePeersConnected'); + +const createNewAddress = require('./core/wallet/createNewAddress'); +const generateBlocks = require('./core/wallet/generateBlocks'); +const generateToAddress = require('./core/wallet/generateToAddress'); +const importPrivateKey = require('./core/wallet/importPrivateKey'); +const getAddressBalance = require('./core/wallet/getAddressBalance'); +const sendToAddress = require('./core/wallet/sendToAddress'); +const registerMasternode = require('./core/wallet/registerMasternode'); +const waitForBalanceToConfirm = require('./core/wallet/waitForBalanceToConfirm'); + +const generateToAddressTaskFactory = require('./listr/tasks/wallet/generateToAddressTaskFactory'); +const registerMasternodeTaskFactory = require('./listr/tasks/registerMasternodeTaskFactory'); +const featureFlagTaskFactory = require('./listr/tasks/platform/featureFlagTaskFactory'); +const tenderdashInitTaskFactory = require('./listr/tasks/platform/tenderdashInitTaskFactory'); +const startNodeTaskFactory = require('./listr/tasks/startNodeTaskFactory'); + +const createTenderdashRpcClient = require('./tenderdash/createTenderdashRpcClient'); +const initializeTenderdashNodeFactory = require('./tenderdash/initializeTenderdashNodeFactory'); +const setupLocalPresetTaskFactory = require('./listr/tasks/setup/setupLocalPresetTaskFactory'); +const setupRegularPresetTaskFactory = require('./listr/tasks/setup/setupRegularPresetTaskFactory'); +const outputStatusOverviewFactory = require('./status/outputStatusOverviewFactory'); +const stopNodeTaskFactory = require('./listr/tasks/stopNodeTaskFactory'); +const restartNodeTaskFactory = require('./listr/tasks/restartNodeTaskFactory'); +const resetNodeTaskFactory = require('./listr/tasks/resetNodeTaskFactory'); +const configureCoreTaskFactory = require('./listr/tasks/setup/local/configureCoreTaskFactory'); +const configureTenderdashTaskFactory = require('./listr/tasks/setup/local/configureTenderdashTaskFactory'); +const waitForNodeToBeReadyTaskFactory = require('./listr/tasks/platform/waitForNodeToBeReadyTaskFactory'); +const enableCoreQuorumsTaskFactory = require('./listr/tasks/setup/local/enableCoreQuorumsTaskFactory'); +const startGroupNodesTaskFactory = require('./listr/tasks/startGroupNodesTaskFactory'); +const buildServicesTaskFactory = require('./listr/tasks/buildServicesTaskFactory'); + +const generateHDPrivateKeys = require('./util/generateHDPrivateKeys'); + +async function createDIContainer() { + const container = createAwilixContainer({ + injectionMode: InjectionMode.CLASSIC, + }); + + /** + * Config + */ + container.register({ + ensureHomeDir: asFunction(ensureHomeDirFactory).singleton(), + configFileRepository: asClass(ConfigFileJsonRepository).singleton(), + systemConfigs: asValue(systemConfigs), + createSystemConfigs: asFunction(createSystemConfigsFactory).singleton(), + isSystemConfig: asFunction(isSystemConfigFactory).singleton(), + migrateConfigFile: asValue(migrateConfigFile), + // `configFile` and `config` are registering on command init + }); + + /** + * Utils + */ + container.register({ + generateHDPrivateKeys: asValue(generateHDPrivateKeys), + }); + + /** + * Templates + */ + container.register({ + renderServiceTemplates: asFunction(renderServiceTemplatesFactory).singleton(), + writeServiceConfigs: asFunction(writeServiceConfigsFactory).singleton(), + }); + + /** + * Docker + */ + container.register({ + docker: asFunction(() => ( + new Docker() + )).singleton(), + dockerCompose: asClass(DockerCompose).singleton(), + startedContainers: asFunction(() => ( + new StartedContainers() + )).singleton(), + stopAllContainers: asFunction(stopAllContainersFactory).singleton(), + dockerPull: asFunction(dockerPullFactory).singleton(), + resolveDockerHostIp: asFunction(resolveDockerHostIpFactory).singleton(), + }); + + /** + * Core + */ + container.register({ + createRpcClient: asValue(createRpcClient), + waitForCoreStart: asValue(waitForCoreStart), + waitForCoreSync: asValue(waitForCoreSync), + waitForMasternodesSync: asValue(waitForMasternodesSync), + startCore: asFunction(startCoreFactory).singleton(), + waitForBlocks: asValue(waitForBlocks), + waitForConfirmations: asValue(waitForConfirmations), + generateBlsKeys: asValue(generateBlsKeys), + activateCoreSpork: asValue(activateCoreSpork), + waitForCorePeersConnected: asValue(waitForCorePeersConnected), + }); + + /** + * Core Wallet + */ + container.register({ + createNewAddress: asValue(createNewAddress), + generateBlocks: asValue(generateBlocks), + generateToAddress: asValue(generateToAddress), + importPrivateKey: asValue(importPrivateKey), + getAddressBalance: asValue(getAddressBalance), + sendToAddress: asValue(sendToAddress), + registerMasternode: asValue(registerMasternode), + waitForBalanceToConfirm: asValue(waitForBalanceToConfirm), + }); + + /** + * Tenderdash + */ + container.register({ + createTenderdashRpcClient: asValue(createTenderdashRpcClient), + initializeTenderdashNode: asFunction(initializeTenderdashNodeFactory).singleton(), + }); + + /** + * Tasks + */ + container.register({ + buildServicesTask: asFunction(buildServicesTaskFactory).singleton(), + startGroupNodesTask: asFunction(startGroupNodesTaskFactory).singleton(), + generateToAddressTask: asFunction(generateToAddressTaskFactory).singleton(), + registerMasternodeTask: asFunction(registerMasternodeTaskFactory).singleton(), + featureFlagTask: asFunction(featureFlagTaskFactory).singleton(), + tenderdashInitTask: asFunction(tenderdashInitTaskFactory).singleton(), + startNodeTask: asFunction(startNodeTaskFactory).singleton(), + stopNodeTask: asFunction(stopNodeTaskFactory).singleton(), + restartNodeTask: asFunction(restartNodeTaskFactory).singleton(), + resetNodeTask: asFunction(resetNodeTaskFactory).singleton(), + setupLocalPresetTask: asFunction(setupLocalPresetTaskFactory).singleton(), + setupRegularPresetTask: asFunction(setupRegularPresetTaskFactory).singleton(), + configureCoreTask: asFunction(configureCoreTaskFactory).singleton(), + configureTenderdashTask: asFunction(configureTenderdashTaskFactory).singleton(), + outputStatusOverview: asFunction(outputStatusOverviewFactory), + waitForNodeToBeReadyTask: asFunction(waitForNodeToBeReadyTaskFactory).singleton(), + enableCoreQuorumsTask: asFunction(enableCoreQuorumsTaskFactory).singleton(), + }); + + return container; +} + +module.exports = createDIContainer; diff --git a/packages/dashmate/src/docker/DockerCompose.js b/packages/dashmate/src/docker/DockerCompose.js new file mode 100644 index 00000000000..3872a9a408c --- /dev/null +++ b/packages/dashmate/src/docker/DockerCompose.js @@ -0,0 +1,413 @@ +const path = require('path'); + +const dockerCompose = require('@dashevo/docker-compose'); + +const hasbin = require('hasbin'); +const semver = require('semver'); + +const { exec } = require('child_process'); + +const DockerComposeError = require('./errors/DockerComposeError'); +const ServiceAlreadyRunningError = require('./errors/ServiceAlreadyRunningError'); +const ServiceIsNotRunningError = require('./errors/ServiceIsNotRunningError'); +const ContainerIsNotPresentError = require('./errors/ContainerIsNotPresentError'); + +const { HOME_DIR_PATH } = require('../constants'); + +class DockerCompose { + /** + * @param {Docker} docker + * @param {StartedContainers} startedContainers + */ + constructor(docker, startedContainers) { + this.docker = docker; + this.startedContainers = startedContainers; + this.isDockerSetupVerified = false; + } + + /** + * Run service + * + * @param {Object} envs + * @param {string} serviceName + * @param {array} [command] + * @param {array} [options] + * @return {Promise} + */ + async runService(envs, serviceName, command = [], options = []) { + await this.throwErrorIfNotInstalled(); + + if (await this.isServiceRunning(envs, serviceName)) { + throw new ServiceAlreadyRunningError(serviceName); + } + + let containerName; + + try { + ({ out: containerName } = await dockerCompose.run( + serviceName, + command, + { + ...this.getOptions(envs), + commandOptions: options, + }, + )); + } catch (e) { + throw new DockerComposeError(e); + } + + containerName = containerName.trim().split(/\r?\n/).pop(); + + this.startedContainers.addContainer(containerName); + + return this.docker.getContainer(containerName); + } + + /** + * Is service running? + * + * @param {Object} envs + * @param {string} [serviceName] + * @return {Promise} + */ + async isServiceRunning(envs, serviceName = undefined) { + await this.throwErrorIfNotInstalled(); + + const coreContainerIds = await this.getContainersList(envs, serviceName); + + for (const containerId of coreContainerIds) { + const container = this.docker.getContainer(containerId); + + let status; + + try { + ({ State: { Status: status } } = await container.inspect()); + } catch (e) { + if (!e.message.includes(`No such container: ${containerId}`)) { + throw e; + } + } + + if (status === 'running') { + return true; + } + } + + return false; + } + + /** + * Up docker compose + * + * @param {Object} envs + * @return {Promise} + */ + async up(envs) { + await this.throwErrorIfNotInstalled(); + + try { + await dockerCompose.upAll({ + ...this.getOptions(envs), + commandOptions: ['--no-build'], + }); + } catch (e) { + throw new DockerComposeError(e); + } + } + + /** + * Build docker compose images + * + * @param {Object} envs + * @param {string} [serviceName] + * @return {Promise} + */ + // eslint-disable-next-line no-unused-vars + async build(envs, serviceName = undefined) { + await this.throwErrorIfNotInstalled(); + + try { + // Temporarily build with buildx bake until docker compose build selects correct builder + // https://github.com/docker/compose-cli/issues/1840 + const childProcess = exec( + 'docker buildx bake --progress plain --load -f docker-compose.platform.build.yml', + this.getOptions(envs), + ); + + childProcess.isReady = new Promise((resolve, reject) => { + childProcess.on('exit', (code) => { + if (code === 0) { + resolve(childProcess); + } else { + reject(childProcess); + } + }); + }); + + return childProcess; + } catch (e) { + throw new DockerComposeError(e); + } + } + + /** + * Stop all docker compose containers + * + * @param {Object} envs + * @return {Promise} + */ + async stop(envs) { + await this.throwErrorIfNotInstalled(); + + try { + await dockerCompose.stop(this.getOptions(envs)); + } catch (e) { + throw new DockerComposeError(e); + } + } + + /** + * Inspect service + * + * @param {Object} envs + * @param {string} serviceName + * @return {Promise} + */ + async inspectService(envs, serviceName) { + await this.throwErrorIfNotInstalled(); + + const containerIds = await this.getContainersList(envs, serviceName); + + if (containerIds.length === 0) { + throw new ContainerIsNotPresentError(serviceName); + } + + const container = this.docker.getContainer(containerIds[0]); + + return container.inspect(); + } + + /** + * Execute command + * + * @param {Object} envs + * @param {string} serviceName + * @param {string} command + * @param {string[]} [commandOptions] + * @return {Promise} + */ + async execCommand(envs, serviceName, command, commandOptions = []) { + await this.throwErrorIfNotInstalled(); + + if (!(await this.isServiceRunning(envs, serviceName))) { + throw new ServiceIsNotRunningError(envs.CONFIG_NAME, serviceName); + } + + let commandOutput; + + const options = { + ...this.getOptions(envs), + commandOptions, + }; + + try { + commandOutput = await dockerCompose.exec( + serviceName, + command, + options, + ); + } catch (e) { + throw new DockerComposeError(e); + } + + return commandOutput; + } + + /** + * Get list of Docker containers + * + * @param {Object} envs + * @param {string} [filterServiceNames] + * @param {boolean} returnServiceNames + * @return {string[]} + */ + async getContainersList( + envs, + filterServiceNames = undefined, + returnServiceNames = false, + ) { + let psOutput; + const commandOptions = []; + + if (returnServiceNames) { + commandOptions.push('--services'); + } else { + commandOptions.push('--quiet'); + } + + commandOptions.push(filterServiceNames); + + try { + ({ out: psOutput } = await dockerCompose.ps({ + ...this.getOptions(envs), + commandOptions, + })); + } catch (e) { + if (e.err && e.err.startsWith('no such service:')) { + return []; + } + + throw new DockerComposeError(e); + } + + return psOutput + .trim() + .split(/\r?\n/) + .filter(Boolean); + } + + /** + * Get list of Docker volumes + * @param {Object} envs + * @return {Promise} + */ + async getVolumeNames(envs) { + let volumeOutput; + try { + ({ out: volumeOutput } = await dockerCompose.configVolumes({ + ...this.getOptions(envs), + })); + } catch (e) { + throw new DockerComposeError(e); + } + + return volumeOutput + .trim() + .split(/\r?\n/); + } + + /** + * Down docker compose + * + * @param {Object} envs + * @return {Promise} + */ + async down(envs) { + await this.throwErrorIfNotInstalled(); + + try { + await dockerCompose.down({ + ...this.getOptions(envs), + commandOptions: ['-v', '--remove-orphans'], + }); + } catch (e) { + throw new DockerComposeError(e); + } + } + + /** + * Remove docker compose + * + * @param {Object} envs + * @param {string[]} [serviceNames] + * @return {Promise} + */ + async rm(envs, serviceNames) { + await this.throwErrorIfNotInstalled(); + + try { + await dockerCompose.rm({ + ...this.getOptions(envs), + commandOptions: ['--stop', '-v'], + }, ...serviceNames); + } catch (e) { + throw new DockerComposeError(e); + } + } + + /** + * Pull docker compose + * + * @param {Object} envs + * @return {Promise} + */ + async pull(envs) { + await this.throwErrorIfNotInstalled(); + + try { + await dockerCompose.pullAll({ + ...this.getOptions(envs), + commandOptions: ['-q'], + }); + } catch (e) { + throw new DockerComposeError(e); + } + } + + /** + * @private + * @return {Promise} + */ + async throwErrorIfNotInstalled() { + if (this.isDockerSetupVerified) { + return; + } + + this.isDockerSetupVerified = true; + + // Check docker + if (!hasbin.sync('docker')) { + throw new Error('Docker is not installed'); + } + + const dockerVersion = await new Promise((resolve, reject) => { + this.docker.version((err, data) => { + if (err) { + return reject(err); + } + + return resolve(data.Version); + }); + }); + + if (semver.lt(dockerVersion.trim(), DockerCompose.DOCKER_MIN_VERSION)) { + throw new Error(`Update Docker to version ${DockerCompose.DOCKER_MIN_VERSION} or higher`); + } + + let version; + + // Check docker compose + try { + ({ out: version } = await dockerCompose.version()); + } catch (e) { + throw new Error('Docker Compose V2 is not available in your system'); + } + + if (semver.lt(version.trim(), DockerCompose.DOCKER_COMPOSE_MIN_VERSION)) { + throw new Error(`Update Docker Compose to version ${DockerCompose.DOCKER_COMPOSE_MIN_VERSION} or higher`); + } + } + + /** + * @private + * @param {Object} envs + * @return {{cwd: string, env: Object}} + */ + getOptions(envs) { + const env = { + ...process.env, + ...envs, + DASHMATE_HOME_DIR: HOME_DIR_PATH, + }; + + return { + cwd: path.join(__dirname, '..', '..'), + env, + }; + } +} + +DockerCompose.DOCKER_COMPOSE_MIN_VERSION = '2.0.0'; +DockerCompose.DOCKER_MIN_VERSION = '20.10.0'; + +module.exports = DockerCompose; diff --git a/packages/dashmate/src/docker/StartedContainers.js b/packages/dashmate/src/docker/StartedContainers.js new file mode 100644 index 00000000000..556324dc32c --- /dev/null +++ b/packages/dashmate/src/docker/StartedContainers.js @@ -0,0 +1,29 @@ +/** + * Store all started docker container IDs + */ + +class StartedContainers { + constructor() { + this.containers = new Set(); + } + + /** + * Add started docker container ID + * + * @param {string} containerId + */ + addContainer(containerId) { + this.containers.add(containerId); + } + + /** + * Get all started docker container IDs + * + * @return {string[]} + */ + getContainers() { + return [...this.containers]; + } +} + +module.exports = StartedContainers; diff --git a/packages/dashmate/src/docker/dockerPullFactory.js b/packages/dashmate/src/docker/dockerPullFactory.js new file mode 100644 index 00000000000..3ef0e90faee --- /dev/null +++ b/packages/dashmate/src/docker/dockerPullFactory.js @@ -0,0 +1,36 @@ +/** + * @param {Docker} docker + * @return {dockerPull} + */ +function dockerPullFactory(docker) { + /** + * @typedef {dockerPull} + * @param {string} image + * @return {Promise<*>} + */ + function dockerPull(image) { + return new Promise((resolve, reject) => { + docker.pull(image, (err, stream) => { + if (err) { + reject(err); + + return; + } + + docker.modem.followProgress(stream, (progressErr, output) => { + if (progressErr) { + reject(progressErr); + + return; + } + + resolve(output); + }); + }); + }); + } + + return dockerPull; +} + +module.exports = dockerPullFactory; diff --git a/packages/dashmate/src/docker/errors/ContainerIsNotPresentError.js b/packages/dashmate/src/docker/errors/ContainerIsNotPresentError.js new file mode 100644 index 00000000000..0ec44d273e6 --- /dev/null +++ b/packages/dashmate/src/docker/errors/ContainerIsNotPresentError.js @@ -0,0 +1,23 @@ +const AbstractError = require('../../errors/AbstractError'); + +class ContainerIsNotPresentError extends AbstractError { + /** + * @param {string} serviceName + */ + constructor(serviceName) { + super(`Container ${serviceName} is not present`); + + this.serviceName = serviceName; + } + + /** + * Get service name + * + * @return {string} + */ + getServiceName() { + return this.serviceName; + } +} + +module.exports = ContainerIsNotPresentError; diff --git a/packages/dashmate/src/docker/errors/DockerComposeError.js b/packages/dashmate/src/docker/errors/DockerComposeError.js new file mode 100644 index 00000000000..6a18c8e9b82 --- /dev/null +++ b/packages/dashmate/src/docker/errors/DockerComposeError.js @@ -0,0 +1,23 @@ +const AbstractError = require('../../errors/AbstractError'); + +class DockerComposeError extends AbstractError { + /** + * @param {{err: string, out: string, exitCode: number}} dockerComposeExecutionResult + */ + constructor(dockerComposeExecutionResult) { + super(`Docker Compose error: ${dockerComposeExecutionResult.err || dockerComposeExecutionResult.message}`); + + this.dockerComposeExecutionResult = dockerComposeExecutionResult; + } + + /** + * Get docker compose execution result + * + * @return {{err: string, out: string, exitCode: number}} + */ + getDockerComposeResult() { + return this.dockerComposeExecutionResult; + } +} + +module.exports = DockerComposeError; diff --git a/packages/dashmate/src/docker/errors/ServiceAlreadyRunningError.js b/packages/dashmate/src/docker/errors/ServiceAlreadyRunningError.js new file mode 100644 index 00000000000..2d2ae39e2fa --- /dev/null +++ b/packages/dashmate/src/docker/errors/ServiceAlreadyRunningError.js @@ -0,0 +1,23 @@ +const AbstractError = require('../../errors/AbstractError'); + +class ServiceAlreadyRunningError extends AbstractError { + /** + * @param {string} serviceName + */ + constructor(serviceName) { + super(`Service ${serviceName} is already running. Please stop it before`); + + this.serviceName = serviceName; + } + + /** + * Get service name + * + * @return {string} + */ + getServiceName() { + return this.serviceName; + } +} + +module.exports = ServiceAlreadyRunningError; diff --git a/packages/dashmate/src/docker/errors/ServiceIsNotRunningError.js b/packages/dashmate/src/docker/errors/ServiceIsNotRunningError.js new file mode 100644 index 00000000000..b98ea7b1593 --- /dev/null +++ b/packages/dashmate/src/docker/errors/ServiceIsNotRunningError.js @@ -0,0 +1,34 @@ +const AbstractError = require('../../errors/AbstractError'); + +class ServiceIsNotRunningError extends AbstractError { + /** + * @param {string} configName + * @param {string} serviceName + */ + constructor(configName, serviceName) { + super(`Service ${serviceName} for ${configName} is not running. Please run the service first.`); + + this.configName = configName; + this.serviceName = serviceName; + } + + /** + * Get config name + * + * @return {string} + */ + getConfigName() { + return this.configName; + } + + /** + * Get service name + * + * @return {string} + */ + getServiceName() { + return this.serviceName; + } +} + +module.exports = ServiceIsNotRunningError; diff --git a/packages/dashmate/src/docker/resolveDockerHostIpFactory.js b/packages/dashmate/src/docker/resolveDockerHostIpFactory.js new file mode 100644 index 00000000000..0640c3d2fa7 --- /dev/null +++ b/packages/dashmate/src/docker/resolveDockerHostIpFactory.js @@ -0,0 +1,54 @@ +const os = require('os'); + +const { WritableStream } = require('memory-streams'); + +const isWSL = require('../util/isWSL'); + +/** + * @param {Docker} docker + * @param {dockerPull} dockerPull + * @return {resolveDockerHostIp} + */ +function resolveDockerHostIpFactory(docker, dockerPull) { + /** + * @typedef {resolveDockerHostIp} + * @return {Promise} + */ + async function resolveDockerHostIp() { + await dockerPull('alpine'); + + const platform = os.platform(); + + const hostConfig = { + AutoRemove: true, + }; + + if (platform !== 'darwin' && platform !== 'win32' && !isWSL()) { + hostConfig.ExtraHosts = ['host.docker.internal:host-gateway']; + } + + const writableStream = new WritableStream(); + + const [result] = await docker.run( + 'alpine', + [], + writableStream, + { + Entrypoint: ['sh', '-c', 'ping -c1 host.docker.internal | sed -nE \'s/^PING[^(]+\\(([^)]+)\\).*/\\1/p\''], + HostConfig: hostConfig, + }, + ); + + const output = writableStream.toString(); + + if (result.StatusCode !== 0) { + throw new Error(`Can't get host.docker.internal IP address: ${output}`); + } + + return output.trim(); + } + + return resolveDockerHostIp; +} + +module.exports = resolveDockerHostIpFactory; diff --git a/packages/dashmate/src/docker/stopAllContainersFactory.js b/packages/dashmate/src/docker/stopAllContainersFactory.js new file mode 100644 index 00000000000..3519030c42f --- /dev/null +++ b/packages/dashmate/src/docker/stopAllContainersFactory.js @@ -0,0 +1,37 @@ +/** + * + * @param docker + * @return {stopAllContainers} + */ +function stopAllContainersFactory(docker) { + /** + * @typedef {stopAllContainers} + * @param {string[]} containersIds + * @param {Object} [options] + * @param {boolean} [options.remove] + * @return {Promise} + */ + async function stopAllContainers(containersIds, options = {}) { + await Promise.all(containersIds.map(async (containerId) => { + // stop all containers + try { + const container = docker.getContainer(containerId); + const { State: { Status: status } } = await container.inspect(); + + if (status === 'running') { + await container.stop(); + + if (options.remove) { + await container.remove(); + } + } + } catch (e) { + // just do nothing + } + })); + } + + return stopAllContainers; +} + +module.exports = stopAllContainersFactory; diff --git a/packages/dashmate/src/ensureHomeDirFactory.js b/packages/dashmate/src/ensureHomeDirFactory.js new file mode 100644 index 00000000000..e761f95bea9 --- /dev/null +++ b/packages/dashmate/src/ensureHomeDirFactory.js @@ -0,0 +1,40 @@ +const fs = require('fs'); + +const CouldNotCreateHomeDirError = require('./config/errors/CouldNotCreateHomeDirError'); +const HomeDirIsNotWritableError = require('./config/errors/HomeDirIsNotWritableError'); + +const { HOME_DIR_PATH } = require('./constants'); + +/** + * @return {ensureHomeDir} + */ +function ensureHomeDirFactory() { + /** + * @typedef {ensureHomeDir} + * @return {string} homeDirPath + */ + function ensureHomeDir() { + if (fs.existsSync(HOME_DIR_PATH)) { + try { + // eslint-disable-next-line no-bitwise + fs.accessSync(__dirname, fs.constants.R_OK | fs.constants.W_OK); + } catch (e) { + throw new HomeDirIsNotWritableError(HOME_DIR_PATH); + } + + return HOME_DIR_PATH; + } + + try { + fs.mkdirSync(HOME_DIR_PATH); + } catch (e) { + throw new CouldNotCreateHomeDirError(HOME_DIR_PATH); + } + + return HOME_DIR_PATH; + } + + return ensureHomeDir; +} + +module.exports = ensureHomeDirFactory; diff --git a/packages/dashmate/src/errors/AbstractError.js b/packages/dashmate/src/errors/AbstractError.js new file mode 100644 index 00000000000..ce6806a562f --- /dev/null +++ b/packages/dashmate/src/errors/AbstractError.js @@ -0,0 +1,17 @@ +class AbstractError extends Error { + /** + * @param {string} message + */ + constructor(message) { + super(); + + this.name = this.constructor.name; + this.message = message; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + } +} + +module.exports = AbstractError; diff --git a/packages/dashmate/src/listr/tasks/buildServicesTaskFactory.js b/packages/dashmate/src/listr/tasks/buildServicesTaskFactory.js new file mode 100644 index 00000000000..9463ca48e8c --- /dev/null +++ b/packages/dashmate/src/listr/tasks/buildServicesTaskFactory.js @@ -0,0 +1,37 @@ +const { Listr } = require('listr2'); + +/** + * + * @param {DockerCompose} dockerCompose + * @return {buildServicesTask} + */ +function buildServicesTaskFactory( + dockerCompose, +) { + /** + * @typedef {buildServicesTask} + * @param {Config} config + * @return {Listr} + */ + function buildServicesTask(config) { + return new Listr({ + title: 'Build services', + task: async (ctx, task) => { + const envs = config.toEnvs(); + + const buildProcess = await dockerCompose.build(envs); + + if (ctx.isVerbose) { + buildProcess.stdout.pipe(task.stdout()); + buildProcess.stderr.pipe(task.stdout()); + } + + await buildProcess.isReady; + }, + }); + } + + return buildServicesTask; +} + +module.exports = buildServicesTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/platform/featureFlagTaskFactory.js b/packages/dashmate/src/listr/tasks/platform/featureFlagTaskFactory.js new file mode 100644 index 00000000000..d29218fd0dc --- /dev/null +++ b/packages/dashmate/src/listr/tasks/platform/featureFlagTaskFactory.js @@ -0,0 +1,85 @@ +const { Listr } = require('listr2'); + +const Dash = require('dash'); +const Identifier = require('@dashevo/dpp/lib/Identifier'); + +/** + * + * @return {featureFlagTask} + */ +function featureFlagTaskFactory() { + /** + * @typedef {featureFlagTask} + * @param {Config} config + * @return {Listr} + */ + function featureFlagTask( + config, + ) { + return new Listr([ + { + title: 'Initialize SDK', + task: async (ctx) => { + const clientOpts = { + network: config.get('network'), + }; + + if (ctx.dapiAddress) { + clientOpts.dapiAddresses = [ctx.dapiAddress]; + } + + ctx.client = new Dash.Client({ + ...clientOpts, + wallet: { + HDPrivateKey: ctx.hdPrivateKey, + }, + }); + + const featureFlagsContractId = config.get('platform.featureFlags.contract.id'); + + const featureFlagsContract = await ctx.client.platform.contracts.get( + featureFlagsContractId, + ); + + ctx.client.getApps().set('featureFlags', { + contractId: Identifier.from(featureFlagsContractId), + contract: featureFlagsContract, + }); + }, + }, + { + title: 'Enable feature flag', + task: async (ctx) => { + const featureFlagsFlag = `featureFlags.${ctx.featureFlagName}`; + + const ownerIdentityId = config.get('platform.featureFlags.ownerId'); + + const ownerIdentity = await ctx.client.platform.identities.get(ownerIdentityId); + + const featureFlagDocument = await ctx.client.platform.documents.create( + featureFlagsFlag, + ownerIdentity, + { + enabled: true, + enableAtHeight: Number(ctx.height), + }, + ); + + // Sign and submit the document(s) + await ctx.client.platform.documents.broadcast({ + create: [featureFlagDocument], + }, ownerIdentity); + }, + options: { persistentOutput: true }, + }, + { + title: 'Disconnect SDK', + task: async (ctx) => ctx.client.disconnect(), + }, + ]); + } + + return featureFlagTask; +} + +module.exports = featureFlagTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/platform/tenderdashInitTaskFactory.js b/packages/dashmate/src/listr/tasks/platform/tenderdashInitTaskFactory.js new file mode 100644 index 00000000000..d587e04a743 --- /dev/null +++ b/packages/dashmate/src/listr/tasks/platform/tenderdashInitTaskFactory.js @@ -0,0 +1,56 @@ +const { Listr } = require('listr2'); + +/** + * @param {initializeTenderdashNode} initializeTenderdashNode + * @param {Docker} docker + * @return {tenderdashInitTask} + */ +function tenderdashInitTaskFactory( + initializeTenderdashNode, + docker, +) { + /** + * @typedef {tenderdashInitTask} + * @param {Config} config + * @return {Listr} + */ + function tenderdashInitTask( + config, + ) { + return new Listr([ + { + title: 'Generate node keys and data', + task: async (ctx, task) => { + const isNodeKeyPresent = Object.keys(config.get('platform.drive.tenderdash.nodeKey')).length !== 0; + const isGenesisPresent = Object.keys(config.get('platform.drive.tenderdash.genesis')).length !== 0; + + const { Volumes: existingVolumes } = await docker.listVolumes(); + const { COMPOSE_PROJECT_NAME: composeProjectName } = config.toEnvs(); + const isDataVolumePresent = existingVolumes.find((v) => v.Name === `${composeProjectName}_drive_tenderdash`); + + if (isNodeKeyPresent && isGenesisPresent && isDataVolumePresent) { + task.skip('Node already initialized'); + + return; + } + + const [nodeKey, genesis, nodeId] = await initializeTenderdashNode(config); + + config.set('platform.drive.tenderdash.nodeId', nodeId); + + if (!isNodeKeyPresent) { + config.set('platform.drive.tenderdash.nodeKey', nodeKey); + } + + if (!isGenesisPresent) { + config.set('platform.drive.tenderdash.genesis', genesis); + } + }, + }, + ]); + } + + return tenderdashInitTask; +} + +module.exports = tenderdashInitTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/platform/waitForNodeToBeReadyTaskFactory.js b/packages/dashmate/src/listr/tasks/platform/waitForNodeToBeReadyTaskFactory.js new file mode 100644 index 00000000000..93073d9dd41 --- /dev/null +++ b/packages/dashmate/src/listr/tasks/platform/waitForNodeToBeReadyTaskFactory.js @@ -0,0 +1,45 @@ +const { Listr } = require('listr2'); +const wait = require('../../../util/wait'); + +/** + * + * @param {createTenderdashRpcClient} createTenderdashRpcClient + * @return {waitForNodeToBeReadyTask} + */ +function waitForNodeToBeReadyTaskFactory( + createTenderdashRpcClient, +) { + /** + * @typedef waitForNodeToBeReadyTask + * @param {Config} config + * @return {Promise} + */ + async function waitForNodeToBeReadyTask(config) { + return new Listr([ + { + task: async () => { + const port = config.get('platform.drive.tenderdash.rpc.port'); + + const tenderdashRpcClient = createTenderdashRpcClient({ port }); + + let success = false; + do { + const response = await tenderdashRpcClient.request('status', {}).catch(() => {}); + + if (response) { + success = !response.result.sync_info.catching_up; + } + + if (!success) { + await wait(500); + } + } while (!success); + }, + }, + ]); + } + + return waitForNodeToBeReadyTask; +} + +module.exports = waitForNodeToBeReadyTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/registerMasternodeTaskFactory.js b/packages/dashmate/src/listr/tasks/registerMasternodeTaskFactory.js new file mode 100644 index 00000000000..aa3f1e9bd82 --- /dev/null +++ b/packages/dashmate/src/listr/tasks/registerMasternodeTaskFactory.js @@ -0,0 +1,286 @@ +const { Listr } = require('listr2'); + +const { Observable } = require('rxjs'); + +const { + NETWORK_LOCAL, + MASTERNODE_DASH_AMOUNT, +} = require('../../constants'); + +/** + * + * @param {startCore} startCore + * @param {createNewAddress} createNewAddress + * @param {generateToAddress} generateToAddress + * @param {generateBlocks} generateBlocks + * @param {waitForCoreSync} waitForCoreSync + * @param {importPrivateKey} importPrivateKey + * @param {getAddressBalance} getAddressBalance + * @param {sendToAddress} sendToAddress + * @param {waitForConfirmations} waitForConfirmations + * @param {registerMasternode} registerMasternode + * @param {waitForBalanceToConfirm} waitForBalanceToConfirm + * @return {registerMasternodeTask} + */ +function registerMasternodeTaskFactory( + startCore, + createNewAddress, + generateToAddress, + generateBlocks, + waitForCoreSync, + importPrivateKey, + getAddressBalance, + sendToAddress, + waitForConfirmations, + registerMasternode, + waitForBalanceToConfirm, +) { + /** + * @typedef {registerMasternodeTask} + * @param {Config} config + * @param {number} operatorReward + * @return {Listr} + */ + function registerMasternodeTask(config, operatorReward = 0) { + return new Listr([ + { + title: 'Start Core', + enabled: (ctx) => { + ctx.coreServicePassed = Boolean(ctx.coreService); + + return !ctx.coreServicePassed; + }, + task: async (ctx) => { + ctx.coreServicePassed = false; + ctx.coreService = await startCore(config, { wallet: true, addressIndex: true }); + }, + }, + { + title: 'Import funding private key', + task: async (ctx, task) => { + await importPrivateKey(ctx.coreService, ctx.fundingPrivateKeyString); + + // eslint-disable-next-line no-param-reassign + task.output = `${ctx.fundingPrivateKeyString} imported.`; + }, + options: { persistentOutput: true }, + }, + { + title: 'Sync Core with network', + enabled: () => config.get('network') !== NETWORK_LOCAL, + task: async (ctx) => ( + new Observable(async (observer) => { + await waitForCoreSync( + ctx.coreService.getRpcClient(), + (verificationProgress) => { + observer.next(`${(verificationProgress * 100).toFixed(2)}% complete`); + }, + ); + + observer.complete(); + + return this; + }) + ), + }, + { + title: 'Check funding address balance', + task: async (ctx, task) => { + // eslint-disable-next-line no-param-reassign + task.title = `Check funding address ${ctx.fundingAddress} balance`; + + const balance = await getAddressBalance(ctx.coreService, ctx.fundingAddress); + + if (balance <= MASTERNODE_DASH_AMOUNT) { + throw new Error(`You need to have more than ${MASTERNODE_DASH_AMOUNT} Dash on your funding address`); + } + }, + }, + { + title: 'Create a new collateral address', + task: async (ctx, task) => { + ctx.collateral = await createNewAddress(ctx.coreService); + + // eslint-disable-next-line no-param-reassign + task.output = `Address: ${ctx.collateral.address}\nPrivate key: ${ctx.collateral.privateKey}`; + }, + options: { persistentOutput: true }, + }, + { + title: 'Create a new owner addresses', + task: async (ctx, task) => { + ctx.owner = await createNewAddress(ctx.coreService); + + // eslint-disable-next-line no-param-reassign + task.output = `Address: ${ctx.owner.address}\nPrivate key: ${ctx.owner.privateKey}`; + }, + options: { persistentOutput: true }, + }, + { + title: 'Create a new reward addresses', + task: async (ctx, task) => { + ctx.reward = await createNewAddress(ctx.coreService); + + // eslint-disable-next-line no-param-reassign + task.output = `Address: ${ctx.reward.address}\nPrivate key: ${ctx.reward.privateKey}`; + }, + options: { persistentOutput: true }, + }, + { + title: 'Send 0.1 dash from funding address to reward address', + task: async (ctx) => { + ctx.collateralTxId = await sendToAddress( + ctx.coreService, + ctx.fundingPrivateKeyString, + ctx.fundingAddress, + ctx.reward.address, + 0.1, + ); + }, + options: { persistentOutput: true }, + }, + { + title: 'Wait for balance to confirm', + task: async (ctx) => ( + new Observable(async (observer) => { + await waitForBalanceToConfirm( + ctx.coreService, + config.get('network'), + ctx.reward.address, + (balance) => { + observer.next(`${balance} dash to confirm`); + }, + ); + observer.complete(); + + return this; + }) + ), + }, + { + title: `Send ${MASTERNODE_DASH_AMOUNT} dash from funding address to collateral address`, + task: async (ctx, task) => { + ctx.collateralTxId = await sendToAddress( + ctx.coreService, + ctx.fundingPrivateKeyString, + ctx.fundingAddress, + ctx.collateral.address, + MASTERNODE_DASH_AMOUNT, + ); + + // eslint-disable-next-line no-param-reassign + task.output = `Collateral transaction ID: ${ctx.collateralTxId}`; + }, + options: { persistentOutput: true }, + }, + { + title: 'Wait for 1 confirmation', + enabled: () => config.get('network') !== NETWORK_LOCAL, + task: async (ctx) => ( + new Observable(async (observer) => { + await waitForConfirmations( + ctx.coreService, + ctx.collateralTxId, + 1, + (confirmations) => { + observer.next(`${confirmations} ${confirmations > 1 ? 'confirmations' : 'confirmation'}`); + }, + ); + + observer.complete(); + + return this; + }) + ), + }, + { + title: 'Wait for balance to confirm', + enabled: () => config.get('network') === NETWORK_LOCAL, + task: async (ctx) => ( + new Observable(async (observer) => { + await waitForBalanceToConfirm( + ctx.coreService, + config.get('network'), + ctx.collateral.address, + (balance) => { + observer.next(`${balance} dash to confirm`); + }, + ); + + observer.complete(); + + return this; + }) + ), + }, + { + title: 'Broadcast masternode registration transaction', + task: async (ctx, task) => { + ctx.proTxHash = await registerMasternode( + ctx.coreService, + ctx.collateralTxId, + ctx.owner.address, + ctx.operator.publicKey, + ctx.reward.address, + operatorReward, + config, + ); + + // eslint-disable-next-line no-param-reassign + task.output = `ProRegTx transaction ID: ${ctx.proTxHash}\n` + + `Owner Private Key: ${ctx.owner.privateKey}`; + }, + options: { persistentOutput: true }, + }, + { + title: 'Wait for 1 confirmation', + enabled: () => config.get('network') !== NETWORK_LOCAL, + task: async (ctx) => ( + new Observable(async (observer) => { + await waitForConfirmations( + ctx.coreService, + ctx.collateralTxId, + 1, + (confirmations) => { + observer.next(`${confirmations} ${confirmations > 1 ? 'confirmations' : 'confirmation'}`); + }, + ); + + observer.complete(); + + return this; + }) + ), + }, + { + title: 'Mine 1 block to confirm', + enabled: () => config.get('network') === NETWORK_LOCAL, + task: async (ctx) => ( + new Observable(async (observer) => { + await generateBlocks( + ctx.coreService, + 1, + config.get('network'), + (blocks) => { + observer.next(`${blocks} ${blocks > 1 ? 'blocks' : 'block'} mined`); + }, + ); + + observer.complete(); + + return this; + }) + ), + }, + { + title: 'Stop Core', + enabled: (ctx) => !ctx.coreServicePassed, + task: async (ctx) => ctx.coreService.stop(), + }, + ]); + } + + return registerMasternodeTask; +} + +module.exports = registerMasternodeTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/resetNodeTaskFactory.js b/packages/dashmate/src/listr/tasks/resetNodeTaskFactory.js new file mode 100644 index 00000000000..2fc7f35d1a5 --- /dev/null +++ b/packages/dashmate/src/listr/tasks/resetNodeTaskFactory.js @@ -0,0 +1,95 @@ +const { Listr } = require('listr2'); + +/** + * @param {DockerCompose} dockerCompose + * @param {Docker} docker + * @param {tenderdashInitTask} tenderdashInitTask + * @param {startNodeTask} startNodeTask + * @param {generateToAddressTask} generateToAddressTask + * @param {systemConfigs} systemConfigs + * @return {resetNodeTask} + */ +function resetNodeTaskFactory( + dockerCompose, + docker, + tenderdashInitTask, + startNodeTask, + generateToAddressTask, + systemConfigs, +) { + /** + * @typedef {resetNodeTask} + * @param {Config} config + */ + function resetNodeTask(config) { + return new Listr([ + { + title: 'Check services are not running', + skip: (ctx) => ctx.isForce, + task: async () => { + if (await dockerCompose.isServiceRunning(config.toEnvs())) { + throw new Error('Running services detected. Please ensure all services are stopped for this config before starting'); + } + }, + }, + { + title: 'Remove all services and associated data', + enabled: (ctx) => !ctx.isPlatformOnlyReset, + task: async () => dockerCompose.down(config.toEnvs()), + }, + { + title: 'Remove platform services and associated data', + enabled: (ctx) => ctx.isPlatformOnlyReset && config.has('platform'), + task: async () => { + // Remove containers + const coreContainerNames = ['core', 'sentinel']; + const containerNames = await dockerCompose + .getContainersList(config.toEnvs(), undefined, true); + const platformContainerNames = containerNames + .filter((containerName) => !coreContainerNames.includes(containerName)); + + await dockerCompose.rm(config.toEnvs(), platformContainerNames); + + // Remove volumes + const coreVolumeNames = ['core_data']; + const { COMPOSE_PROJECT_NAME: composeProjectName } = config.toEnvs(); + + const projectVolumeNames = await dockerCompose.getVolumeNames(config.toEnvs()); + + await Promise.all( + projectVolumeNames + .filter((volumeName) => !coreVolumeNames.includes(volumeName)) + .map((volumeName) => `${composeProjectName}_${volumeName}`) + .map(async (volumeName) => docker.getVolume(volumeName).remove()), + ); + }, + }, + { + title: `Reset config ${config.getName()}`, + enabled: (ctx) => ctx.isHardReset, + task: (ctx) => { + const name = config.get('group') || config.getName(); + + if (ctx.isPlatformOnlyReset) { + // TODO: This won't work for user created configs + const { platform: systemPlatformConfig } = systemConfigs[name]; + config.set('platform', systemPlatformConfig); + } else { + config.setOptions(systemConfigs[name]); + } + }, + }, + { + title: 'Initialize Tenderdash', + enabled: (ctx) => ( + !ctx.isHardReset && !ctx.skipPlatformInitialization && config.has('platform') + ), + task: () => tenderdashInitTask(config), + }, + ]); + } + + return resetNodeTask; +} + +module.exports = resetNodeTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/restartNodeTaskFactory.js b/packages/dashmate/src/listr/tasks/restartNodeTaskFactory.js new file mode 100644 index 00000000000..26586b9c990 --- /dev/null +++ b/packages/dashmate/src/listr/tasks/restartNodeTaskFactory.js @@ -0,0 +1,32 @@ +const { Listr } = require('listr2'); + +/** + * @param {startNodeTask} startNodeTask + * @param {stopNodeTask} stopNodeTask + * + * @return {restartNodeTask} + */ +function restartNodeTaskFactory(startNodeTask, stopNodeTask) { + /** + * Restart node + * @typedef {restartNodeTask} + * + * @param {Config} config + * + * @return {Listr} + */ + function restartNodeTask(config) { + return new Listr([ + { + task: () => stopNodeTask(config), + }, + { + task: () => startNodeTask(config), + }, + ]); + } + + return restartNodeTask; +} + +module.exports = restartNodeTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/setup/local/configureCoreTaskFactory.js b/packages/dashmate/src/listr/tasks/setup/local/configureCoreTaskFactory.js new file mode 100644 index 00000000000..a7bdae22a1e --- /dev/null +++ b/packages/dashmate/src/listr/tasks/setup/local/configureCoreTaskFactory.js @@ -0,0 +1,358 @@ +const { Listr } = require('listr2'); +const { Observable } = require('rxjs'); + +const { + PrivateKey, +} = require('@dashevo/dashcore-lib'); + +const waitForNodesToHaveTheSameSporks = require('../../../../core/waitForNodesToHaveTheSameSporks'); +const waitForNodesToHaveTheSameHeight = require('../../../../core/waitForNodesToHaveTheSameHeight'); + +const { NETWORK_LOCAL, MASTERNODE_DASH_AMOUNT } = require('../../../../constants'); + +/** + * @param {renderServiceTemplates} renderServiceTemplates + * @param {writeServiceConfigs} writeServiceConfigs + * @param {startCore} startCore + * @param {generateBlocks} generateBlocks + * @param {waitForCoreSync} waitForCoreSync + * @param {activateCoreSpork} activateCoreSpork + * @param {generateToAddressTask} generateToAddressTask + * @param {registerMasternodeTask} registerMasternodeTask + * @param {generateBlsKeys} generateBlsKeys + * @param {enableCoreQuorumsTask} enableCoreQuorumsTask + * @param {waitForMasternodesSync} waitForMasternodesSync + * @return {configureCoreTask} + */ +function configureCoreTaskFactory( + renderServiceTemplates, + writeServiceConfigs, + startCore, + generateBlocks, + waitForCoreSync, + activateCoreSpork, + generateToAddressTask, + registerMasternodeTask, + generateBlsKeys, + enableCoreQuorumsTask, + waitForMasternodesSync, +) { + const WAIT_FOR_NODES_TIMEOUT = 60 * 5 * 1000; + + /** + * @typedef {configureCoreTask} + * @param {Config[]} configGroup + * @return {Listr} + */ + function configureCoreTask(configGroup) { + return new Listr([ + { + task: async (ctx) => { + const network = configGroup[0].get('network'); + const sporkPrivKey = new PrivateKey(undefined, network); + const sporkAddress = sporkPrivKey.toAddress(network).toString(); + + const seedNodes = configGroup.filter((config) => config.getName() === 'local_seed') + .map((config) => ({ + host: config.get('externalIp'), + port: config.get('core.p2p.port'), + })); + + configGroup.forEach((config) => { + // Set seeds + if (config.getName() !== 'local_seed') { + config.set( + 'core.p2p.seeds', + seedNodes, + ); + } + + // Set sporks key + config.set( + 'core.spork.address', + sporkAddress, + ); + + config.set( + 'core.spork.privateKey', + sporkPrivKey.toWIF(), + ); + + // Write configs + const configFiles = renderServiceTemplates(config); + writeServiceConfigs(config.getName(), configFiles); + }); + + return new Listr([ + { + title: 'Starting seed node as a wallet', + task: async () => { + const config = configGroup.find((c) => c.getName() === 'local_seed'); + + ctx.coreService = await startCore(config, { wallet: true, addressIndex: true }); + }, + }, + { + title: 'Activating DIP3', + task: () => new Observable(async (observer) => { + const dip3ActivationHeight = 500; + const blocksToGenerateInOneStep = 10; + + let blocksGenerated = 0; + let { + result: currentBlockHeight, + } = await ctx.coreService.getRpcClient().getBlockCount(); + + do { + ({ + result: currentBlockHeight, + } = await ctx.coreService.getRpcClient().getBlockCount()); + + await generateBlocks( + ctx.coreService, + blocksToGenerateInOneStep, + NETWORK_LOCAL, + // eslint-disable-next-line no-loop-func + (blocks) => { + blocksGenerated += blocks; + + observer.next(`${blocksGenerated} blocks generated`); + }, + ); + } while (dip3ActivationHeight > currentBlockHeight); + + observer.complete(); + + return this; + }), + }, + { + title: 'Generating funds to use as a collateral for masternodes', + task: () => { + const amount = MASTERNODE_DASH_AMOUNT * configGroup.length; + return generateToAddressTask( + configGroup.find((c) => c.getName() === 'local_seed'), + amount, + ); + }, + }, + { + title: 'Register masternodes', + task: async () => { + const masternodeConfigs = configGroup.filter((config) => config.get('core.masternode.enable')); + + const subTasks = masternodeConfigs.map((config, masternodeNumber) => ({ + title: `Register ${config.getName()} masternode`, + skip: () => { + if (config.get('core.masternode.operator.privateKey')) { + return `Masternode operator private key ('core.masternode.operator.privateKey') is already set in ${config.getName()} config`; + } + + return false; + }, + task: () => new Listr([ + { + title: 'Generate a masternode operator key', + task: async (task) => { + ctx.operator = await generateBlsKeys(); + + config.set('core.masternode.operator.privateKey', ctx.operator.privateKey); + + // eslint-disable-next-line no-param-reassign + task.output = `Public key: ${ctx.operator.publicKey}\nPrivate key: ${ctx.operator.privateKey}`; + }, + options: { persistentOutput: true }, + }, + { + // first masternode has 10% operatorReward + task: () => registerMasternodeTask(config, masternodeNumber === 0 ? 10 : 0), + }, + ]), + })); + + // eslint-disable-next-line consistent-return + return new Listr(subTasks); + }, + }, + { + title: 'Stopping wallet', + task: async () => { + await ctx.coreService.stop(); + }, + }, + { + title: 'Starting nodes', + task: async () => { + ctx.coreServices = await Promise.all( + configGroup.map((config) => startCore(config)), + ); + + ctx.rpcClients = ctx.coreServices.map((coreService) => coreService.getRpcClient()); + + ctx.seedCoreService = ctx.coreServices.find((coreService) => ( + coreService.getConfig().getName() === 'local_seed' + )); + + ctx.seedRpcClient = ctx.seedCoreService.getRpcClient(); + + ctx.mockTime = 0; + ctx.bumpMockTime = async (time = 1) => { + ctx.mockTime += time; + + await Promise.all( + ctx.rpcClients.map((rpcClient) => rpcClient.setMockTime(ctx.mockTime)), + ); + }; + }, + }, + { + title: 'Force masternodes to sync', + task: async () => { + await Promise.all(ctx.coreServices.map((coreService) => ( + // TODO: Rename function "wait -> force" + waitForMasternodesSync(coreService.getRpcClient()) + ))); + }, + }, + { + title: 'Set initial mock time', + task: async () => { + // Set initial mock time from the last block + const { result: bestBlockHash } = await ctx.seedRpcClient.getBestBlockHash(); + const { result: bestBlock } = await ctx.seedRpcClient.getBlock(bestBlockHash); + + await ctx.bumpMockTime(bestBlock.time); + + // Sync nodes + await ctx.bumpMockTime(); + + await generateBlocks( + ctx.seedCoreService, + 1, + NETWORK_LOCAL, + ); + }, + }, + { + title: 'Wait for nodes to have the same height', + task: () => waitForNodesToHaveTheSameHeight( + ctx.rpcClients, + WAIT_FOR_NODES_TIMEOUT, + ), + }, + { + title: 'Enable sporks', + task: async () => { + const sporks = [ + 'SPORK_2_INSTANTSEND_ENABLED', + 'SPORK_3_INSTANTSEND_BLOCK_FILTERING', + 'SPORK_9_SUPERBLOCKS_ENABLED', + 'SPORK_17_QUORUM_DKG_ENABLED', + 'SPORK_19_CHAINLOCKS_ENABLED', + ]; + + await Promise.all( + sporks.map(async (spork) => ( + activateCoreSpork(ctx.seedCoreService.getRpcClient(), spork))), + ); + }, + }, + { + title: 'Wait for nodes to have the same sporks', + task: () => waitForNodesToHaveTheSameSporks(ctx.coreServices), + }, + { + title: 'Activating DIP8 to enable ChainLocks', + task: () => new Observable(async (observer) => { + let isDip8Activated = false; + let blockchainInfo; + + let blocksGenerated = 0; + + const blocksToGenerateInOneStep = 10; + + do { + ({ + result: blockchainInfo, + } = await ctx.seedCoreService.getRpcClient().getBlockchainInfo()); + + isDip8Activated = blockchainInfo.bip9_softforks.dip0008.status === 'active'; + + if (isDip8Activated) { + break; + } + + await generateBlocks( + ctx.seedCoreService, + blocksToGenerateInOneStep, + NETWORK_LOCAL, + // eslint-disable-next-line no-loop-func + (blocks) => { + blocksGenerated += blocks; + + observer.next(`${blocksGenerated} blocks generated`); + }, + ); + } while (!isDip8Activated); + + observer.next(`DIP8 has been activated at height ${blockchainInfo.bip9_softforks.dip0008.since}`); + + observer.complete(); + + return this; + }), + }, + { + title: 'Wait for nodes to have the same height', + task: () => waitForNodesToHaveTheSameHeight( + ctx.rpcClients, + WAIT_FOR_NODES_TIMEOUT, + ), + }, + { + title: 'Make sure masternodes are enabled', + task: async () => { + const { result: masternodesStatus } = await ctx.seedRpcClient.masternodelist('status'); + + const hasNotEnabled = Boolean( + Object.values(masternodesStatus) + .find((status) => status !== 'ENABLED'), + ); + + if (hasNotEnabled) { + throw new Error('Not all masternodes are enabled'); + } + }, + }, + { + title: 'Wait for quorums to be enabled', + task: () => enableCoreQuorumsTask(), + }, + { + title: 'Setting initial core chain locked height', + task: async (_, task) => { + const rpcClient = ctx.seedCoreService.getRpcClient(); + const { result: initialCoreChainLockedHeight } = await rpcClient.getBlockCount(); + + ctx.initialCoreChainLockedHeight = initialCoreChainLockedHeight; + + // eslint-disable-next-line no-param-reassign + task.output = `Initial chain locked core height is set to: ${ctx.initialCoreChainLockedHeight}`; + }, + }, + { + title: 'Stopping nodes', + task: async () => (Promise.all( + ctx.coreServices.map((coreService) => coreService.stop()), + )), + }, + ]); + }, + }, + ]); + } + + return configureCoreTask; +} + +module.exports = configureCoreTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/setup/local/configureTenderdashTaskFactory.js b/packages/dashmate/src/listr/tasks/setup/local/configureTenderdashTaskFactory.js new file mode 100644 index 00000000000..e26b6e993d2 --- /dev/null +++ b/packages/dashmate/src/listr/tasks/setup/local/configureTenderdashTaskFactory.js @@ -0,0 +1,83 @@ +const { Listr } = require('listr2'); + +/** + * @param {tenderdashInitTask} tenderdashInitTask + * @param {renderServiceTemplates} renderServiceTemplates + * @param {writeServiceConfigs} writeServiceConfigs + * @return {configureTenderdashTask} + */ +function configureTenderdashTaskFactory( + tenderdashInitTask, + renderServiceTemplates, + writeServiceConfigs, +) { + /** + * @typedef {configureTenderdashTask} + * @param {Config[]} configGroup + * @return {Listr} + */ + function configureTenderdashTask(configGroup) { + return new Listr([ + { + task: async (ctx) => { + const platformConfigs = configGroup.filter((config) => config.has('platform')); + + const subTasks = platformConfigs.map((config) => ({ + title: `Initialize ${config.getName()} Tenderdash`, + task: () => tenderdashInitTask(config), + })); + + // Interconnect Tenderdash nodes + subTasks.push({ + task: async () => { + const randomChainIdPart = Math.floor(Math.random() * 60) + 1; + const chainId = `dash_masternode_local_${randomChainIdPart}`; + + const genesisTime = platformConfigs[0].get('platform.drive.tenderdash.genesis.genesis_time'); + + platformConfigs.forEach((config, index) => { + config.set('platform.drive.tenderdash.genesis.genesis_time', genesisTime); + config.set('platform.drive.tenderdash.genesis.chain_id', chainId); + config.set( + 'platform.drive.tenderdash.genesis.initial_core_chain_locked_height', + ctx.initialCoreChainLockedHeight, + ); + + const p2pPeers = platformConfigs + .filter((_, i) => i !== index) + .map((innerConfig) => { + const nodeId = innerConfig.get('platform.drive.tenderdash.nodeId'); + const port = innerConfig.get('platform.drive.tenderdash.p2p.port'); + + return { + id: nodeId, + host: config.get('externalIp'), + port, + }; + }); + + config.set('platform.drive.tenderdash.p2p.persistentPeers', p2pPeers); + + config.set( + 'platform.drive.tenderdash.genesis.quorum_type', + config.get('platform.drive.abci.validatorSet.llmqType').toString(), + ); + + config.set('platform.drive.tenderdash.genesis.quorum_hash', Buffer.alloc(20).toString('hex')); + + const configFiles = renderServiceTemplates(config); + writeServiceConfigs(config.getName(), configFiles); + }); + }, + }); + + return new Listr(subTasks); + }, + }, + ]); + } + + return configureTenderdashTask; +} + +module.exports = configureTenderdashTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/setup/local/enableCoreQuorumsTaskFactory.js b/packages/dashmate/src/listr/tasks/setup/local/enableCoreQuorumsTaskFactory.js new file mode 100644 index 00000000000..bda1b8a1548 --- /dev/null +++ b/packages/dashmate/src/listr/tasks/setup/local/enableCoreQuorumsTaskFactory.js @@ -0,0 +1,297 @@ +const { Listr } = require('listr2'); +const isEqual = require('lodash.isequal'); + +const wait = require('../../../../util/wait'); + +const { LLMQ_TYPE_TEST, NETWORK_LOCAL } = require('../../../../constants'); + +const waitForQuorumPhase = require('../../../../core/quorum/waitForQuorumPhase'); +const waitForNodesToHaveTheSameHeight = require('../../../../core/waitForNodesToHaveTheSameHeight'); +const waitForQuorumConnections = require('../../../../core/quorum/waitForQuorumConnections'); +const waitForMasternodeProbes = require('../../../../core/quorum/waitForMasternodeProbes'); +const waitForQuorumCommitments = require('../../../../core/quorum/waitForQuorumCommitements'); + +/** + * @param {generateBlocks} generateBlocks + * @return {enableCoreQuorumsTask} + */ +function enableCoreQuorumsTaskFactory(generateBlocks) { + /** + * @typedef {enableCoreQuorumsTask} + * @return {Listr} + */ + function enableCoreQuorumsTask() { + const WAIT_FOR_NODES_TIMEOUT = 60 * 5 * 1000; + + return new Listr([ + { + task: (ctx) => { + // Those are default values for the quorum size 3 with all nodes + // behaving correctly with "llmq_test" quorum + ctx.expectedMembers = 3; + ctx.expectedCommitments = 3; + ctx.expectedConnections = 2; + + ctx.expectedContributions = 3; + ctx.expectedJustifications = 0; + ctx.expectedComplaints = 0; + + ctx.masternodeRpcClients = ctx.coreServices + .filter((coreService) => coreService.getConfig().getName() !== 'local_seed') + .map((coreService) => coreService.getRpcClient()); + }, + }, + { + title: 'Start DKG session', + task: async (ctx) => { + const { result: initialQuorumList } = await ctx.seedRpcClient.quorum('list'); + + ctx.initialQuorumList = initialQuorumList; + + const { result: bestBlockHeight } = await ctx.seedRpcClient.getBlockCount(); + + // move forward to next DKG + const blocksUntilNextDKG = 24 - (bestBlockHeight % 24); + if (blocksUntilNextDKG !== 0) { + await ctx.bumpMockTime(); + + await generateBlocks( + ctx.seedCoreService, + blocksUntilNextDKG, + NETWORK_LOCAL, + ); + } + + await waitForNodesToHaveTheSameHeight( + ctx.rpcClients, + WAIT_FOR_NODES_TIMEOUT, + ); + }, + }, + { + title: 'Waiting for phase 1 (init)', + task: async (ctx) => { + const { result: quorumHash } = await ctx.seedRpcClient.getBestBlockHash(); + + ctx.quorumHash = quorumHash; + + await waitForQuorumPhase( + ctx.masternodeRpcClients, + ctx.quorumHash, + 1, + ctx.expectedMembers, + ); + + await waitForQuorumConnections( + ctx.masternodeRpcClients, + ctx.expectedConnections, + ctx.bumpMockTime, + ); + + const { result: sporks } = await ctx.seedRpcClient.spork('show'); + const isSpork21Active = sporks.SPORK_21_QUORUM_ALL_CONNECTED === 0; + + if (isSpork21Active) { + await waitForMasternodeProbes( + ctx.masternodeRpcClients, + ctx.bumpMockTime, + ); + } + + await ctx.bumpMockTime(); + + await generateBlocks( + ctx.seedCoreService, + 2, + NETWORK_LOCAL, + ); + + await waitForNodesToHaveTheSameHeight( + ctx.rpcClients, + WAIT_FOR_NODES_TIMEOUT, + ); + }, + }, + { + title: 'Waiting for phase 2 (contribute)', + task: async (ctx) => { + await waitForQuorumPhase( + ctx.masternodeRpcClients, + ctx.quorumHash, + 2, + ctx.expectedMembers, + ); + + await ctx.bumpMockTime(); + + await generateBlocks( + ctx.seedCoreService, + 2, + NETWORK_LOCAL, + ); + + await waitForNodesToHaveTheSameHeight( + ctx.rpcClients, + WAIT_FOR_NODES_TIMEOUT, + ); + }, + }, + { + title: 'Waiting for phase 3 (complain)', + task: async (ctx) => { + await waitForQuorumPhase( + ctx.masternodeRpcClients, + ctx.quorumHash, + 3, + ctx.expectedMembers, + 'receivedComplaints', + ctx.expectedComplaints, + ); + + await ctx.bumpMockTime(); + + await generateBlocks( + ctx.seedCoreService, + 2, + NETWORK_LOCAL, + ); + + await waitForNodesToHaveTheSameHeight( + ctx.rpcClients, + WAIT_FOR_NODES_TIMEOUT, + ); + }, + }, + { + title: 'Waiting for phase 4 (justify)', + task: async (ctx) => { + await waitForQuorumPhase( + ctx.masternodeRpcClients, + ctx.quorumHash, + 4, + ctx.expectedMembers, + 'receivedJustifications', + ctx.expectedJustifications, + ); + + await ctx.bumpMockTime(); + + await generateBlocks( + ctx.seedCoreService, + 2, + NETWORK_LOCAL, + ); + + await waitForNodesToHaveTheSameHeight( + ctx.rpcClients, + WAIT_FOR_NODES_TIMEOUT, + ); + }, + }, + { + title: 'Waiting for phase 5 (commit)', + task: async (ctx) => { + await waitForQuorumPhase( + ctx.masternodeRpcClients, + ctx.quorumHash, + 5, + ctx.expectedMembers, + 'receivedPrematureCommitments', + ctx.expectedCommitments, + ); + + await ctx.bumpMockTime(); + + await generateBlocks( + ctx.seedCoreService, + 2, + NETWORK_LOCAL, + ); + + await waitForNodesToHaveTheSameHeight( + ctx.rpcClients, + WAIT_FOR_NODES_TIMEOUT, + ); + }, + }, + { + title: 'Waiting for phase 6 (mining)', + task: async (ctx) => { + await waitForQuorumPhase( + ctx.masternodeRpcClients, + ctx.quorumHash, + 6, + ctx.expectedMembers, + ); + }, + }, + { + title: 'Waiting final commitment', + task: (ctx) => waitForQuorumCommitments( + ctx.masternodeRpcClients, + ctx.quorumHash, + ), + }, + { + title: 'Mining final commitment', + task: async (ctx, task) => { + await ctx.bumpMockTime(); + + await generateBlocks( + ctx.seedCoreService, + 1, + NETWORK_LOCAL, + ); + + let { result: newQuorumList } = await ctx.seedRpcClient.quorum('list'); + + while (isEqual(ctx.initialQuorumList, newQuorumList)) { + await wait(300); + + await ctx.bumpMockTime(); + + await generateBlocks( + ctx.seedCoreService, + 1, + NETWORK_LOCAL, + ); + + await waitForNodesToHaveTheSameHeight( + ctx.rpcClients, + WAIT_FOR_NODES_TIMEOUT, + ); + + ({ result: newQuorumList } = await ctx.seedRpcClient.quorum('list')); + } + + const { result: quorumList } = await ctx.seedRpcClient.quorum('list', 1); + + // eslint-disable-next-line prefer-destructuring + ctx.quorumHash = quorumList[LLMQ_TYPE_TEST][0]; + + const { result: quorumInfo } = await ctx.seedRpcClient.quorum('info', 100, ctx.quorumHash); + + // Mine 8 (SIGN_HEIGHT_OFFSET) more blocks to make sure + // that the new quorum gets eligable for signing sessions + await generateBlocks( + ctx.seedCoreService, + 8, + NETWORK_LOCAL, + ); + + await waitForNodesToHaveTheSameHeight( + ctx.rpcClients, + WAIT_FOR_NODES_TIMEOUT, + ); + + // eslint-disable-next-line no-param-reassign + task.output = `New quorum mined: height: ${quorumInfo.height}, quorum hash: ${ctx.quorumHash}, mined in block: ${quorumInfo.minedBlock}`; + }, + }, + ]); + } + + return enableCoreQuorumsTask; +} + +module.exports = enableCoreQuorumsTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/setup/setupLocalPresetTaskFactory.js b/packages/dashmate/src/listr/tasks/setup/setupLocalPresetTaskFactory.js new file mode 100644 index 00000000000..18532308057 --- /dev/null +++ b/packages/dashmate/src/listr/tasks/setup/setupLocalPresetTaskFactory.js @@ -0,0 +1,262 @@ +const { Listr } = require('listr2'); + +const path = require('path'); + +const { + PRESET_LOCAL, + HOME_DIR_PATH, +} = require('../../../constants'); + +/** + * @param {ConfigFile} configFile + * @param {configureCoreTask} configureCoreTask + * @param {configureTenderdashTask} configureTenderdashTask + * @param {resolveDockerHostIp} resolveDockerHostIp + * @param {configFileRepository} configFileRepository + * @param {generateHDPrivateKeys} generateHDPrivateKeys + */ +function setupLocalPresetTaskFactory( + configFile, + configureCoreTask, + configureTenderdashTask, + resolveDockerHostIp, + configFileRepository, + generateHDPrivateKeys, +) { + /** + * @typedef {setupLocalPresetTask} + * @return {Listr} + */ + function setupLocalPresetTask() { + return new Listr([ + { + title: 'Set the number of nodes', + enabled: (ctx) => ctx.nodeCount === undefined, + task: async (ctx, task) => { + ctx.nodeCount = await task.prompt({ + type: 'Numeral', + message: 'Enter the number of masternodes', + initial: 3, + float: false, + min: 3, + validate: (state) => { + if (+state < 3) { + return 'You must set not less than 3'; + } + + return true; + }, + }); + }, + }, + { + title: 'Enable debug logs', + enabled: (ctx) => ctx.debugLogs === undefined, + task: async (ctx, task) => { + ctx.debugLogs = await task.prompt({ + type: 'Toggle', + message: 'Enable debug logs?', + enabled: 'yes', + disabled: 'no', + initial: 'no', + }); + }, + }, + { + title: 'Set the core miner interval', + enabled: (ctx) => ctx.minerInterval === undefined, + task: async (ctx, task) => { + ctx.minerInterval = await task.prompt({ + type: 'input', + message: 'Enter the interval between core blocks', + initial: configFile.getConfig('base').options.core.miner.interval, + validate: (state) => { + if (state.match(/\d+(\.\d+)?(m|s)/)) { + return true; + } + + return 'Please enter a valid integer or decimal duration with m or s units'; + }, + }); + }, + }, + { + title: 'Create local group configs', + task: async (ctx, task) => { + ctx.configGroup = new Array(ctx.nodeCount) + .fill(undefined) + .map((value, i) => `local_${i + 1}`) + // we need to add one more node (number of masternodes + 1) as a seed node + .concat(['local_seed']) + .map((configName) => ( + configFile.isConfigExists(configName) + ? configFile.getConfig(configName) + : configFile.createConfig(configName, PRESET_LOCAL) + )); + + const hostDockerInternalIp = await resolveDockerHostIp(); + + const network = ctx.configGroup[0].get('network'); + + const { + hdPrivateKey: dpnsPrivateKey, + derivedPrivateKeys: [ + dpnsDerivedMasterPrivateKey, + dpnsDerivedSecondPrivateKey, + ], + } = await generateHDPrivateKeys(network, [0, 1]); + + const { + hdPrivateKey: featureFlagsPrivateKey, + derivedPrivateKeys: [ + featureFlagsDerivedMasterPrivateKey, + featureFlagsDerivedSecondPrivateKey, + ], + } = await generateHDPrivateKeys(network, [0, 1]); + + const { + hdPrivateKey: dashpayPrivateKey, + derivedPrivateKeys: [ + dashpayDerivedMasterPrivateKey, + dashpayDerivedSecondPrivateKey, + ], + } = await generateHDPrivateKeys(network, [0, 1]); + + const { + hdPrivateKey: masternodeRewardSharesPrivateKey, + derivedPrivateKeys: [ + masternodeRewardSharesDerivedMasterPrivateKey, + masternodeRewardSharesDerivedSecondPrivateKey, + ], + } = await generateHDPrivateKeys(network, [0, 1]); + + // eslint-disable-next-line no-param-reassign + task.output = `DPNS Private Key: ${dpnsPrivateKey.toString()}`; + + // eslint-disable-next-line no-param-reassign + task.output = `Feature Flags Private Key: ${featureFlagsPrivateKey.toString()}`; + + // eslint-disable-next-line no-param-reassign + task.output = `Dashpay Private Key: ${dashpayPrivateKey.toString()}`; + + // eslint-disable-next-line no-param-reassign + task.output = `Masternode Reward Shares Private Key: ${masternodeRewardSharesPrivateKey.toString()}`; + + const subTasks = ctx.configGroup.map((config, i) => ( + { + title: `Create ${config.getName()} config`, + task: () => { + const nodeIndex = i + 1; + + config.set('group', 'local'); + config.set('core.p2p.port', 20001 + (i * 100)); + config.set('core.rpc.port', 20002 + (i * 100)); + config.set('externalIp', hostDockerInternalIp); + + config.set('docker.network.subnet', `172.24.${nodeIndex}.0/24`); + + // Setup Core debug logs + if (ctx.debugLogs) { + config.set('core.debug', 1); + } + + // Although not all nodes are miners, all nodes should be aware of + // the miner interval to be able to sync mocked time + config.set('core.miner.interval', ctx.minerInterval); + + if (config.getName() === 'local_seed') { + config.set('description', 'seed node for local network'); + + config.set('core.masternode.enable', false); + config.set('core.miner.enable', true); + + // Enable miner for the seed node + config.set('core.miner.enable', true); + + // Disable platform for the seed node + config.set('platform', undefined); + } else { + config.set('description', `local node #${nodeIndex}`); + + config.set('platform.dapi.envoy.http.port', 3000 + (i * 100)); + config.set('platform.dapi.envoy.grpc.port', 3010 + (i * 100)); + config.set('platform.drive.tenderdash.p2p.port', 26656 + (i * 100)); + config.set('platform.drive.tenderdash.rpc.port', 26657 + (i * 100)); + + // Setup logs + if (ctx.debugLogs) { + config.set('platform.drive.abci.log.stdout.level', 'trace'); + config.set('platform.drive.abci.log.prettyFile.level', 'trace'); + + config.set('platform.drive.tenderdash.log.level', { + '*': 'debug', + }); + } + + if (!config.get('platform.drive.abci.log.prettyFile.path')) { + const drivePrettyLogFile = path.join(HOME_DIR_PATH, 'logs', config.getName(), 'drive_pretty.log'); + config.set('platform.drive.abci.log.prettyFile.path', drivePrettyLogFile); + } + + if (!config.get('platform.drive.abci.log.jsonFile.path')) { + const driveJsonLogFile = path.join(HOME_DIR_PATH, 'logs', config.getName(), 'drive_json.log'); + config.set('platform.drive.abci.log.jsonFile.path', driveJsonLogFile); + } + + config.set('platform.dpns.masterPublicKey', dpnsDerivedMasterPrivateKey.privateKey.toPublicKey().toString()); + config.set('platform.dpns.secondPublicKey', dpnsDerivedSecondPrivateKey.privateKey.toPublicKey().toString()); + + config.set('platform.featureFlags.masterPublicKey', featureFlagsDerivedMasterPrivateKey.privateKey.toPublicKey().toString()); + config.set('platform.featureFlags.secondPublicKey', featureFlagsDerivedSecondPrivateKey.privateKey.toPublicKey().toString()); + + config.set('platform.dashpay.masterPublicKey', dashpayDerivedMasterPrivateKey.privateKey.toPublicKey().toString()); + config.set('platform.dashpay.secondPublicKey', dashpayDerivedSecondPrivateKey.privateKey.toPublicKey().toString()); + + config.set( + 'platform.masternodeRewardShares.masterPublicKey', + masternodeRewardSharesDerivedMasterPrivateKey.privateKey + .toPublicKey().toString(), + ); config.set( + 'platform.masternodeRewardShares.secondPublicKey', + masternodeRewardSharesDerivedSecondPrivateKey.privateKey + .toPublicKey().toString(), + ); + } + }, + options: { + persistentOutput: true, + }, + } + )); + + subTasks.push({ + title: 'Save configs', + task: async () => { + configFile.setDefaultGroupName(PRESET_LOCAL); + + // Persist configs + await configFileRepository.write(configFile); + }, + }); + + return new Listr(subTasks); + }, + options: { + persistentOutput: true, + }, + }, + { + title: 'Configure Core nodes', + task: (ctx) => configureCoreTask(ctx.configGroup), + }, + { + title: 'Configure Tenderdash nodes', + task: (ctx) => configureTenderdashTask(ctx.configGroup), + }, + ]); + } + + return setupLocalPresetTask; +} + +module.exports = setupLocalPresetTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/setup/setupRegularPresetTaskFactory.js b/packages/dashmate/src/listr/tasks/setup/setupRegularPresetTaskFactory.js new file mode 100644 index 00000000000..ab69e5f8482 --- /dev/null +++ b/packages/dashmate/src/listr/tasks/setup/setupRegularPresetTaskFactory.js @@ -0,0 +1,161 @@ +const { Listr } = require('listr2'); + +const publicIp = require('public-ip'); + +const BlsSignatures = require('bls-signatures'); + +const { PrivateKey } = require('@dashevo/dashcore-lib'); + +const { + NODE_TYPES, + NODE_TYPE_MASTERNODE, + PRESET_MAINNET, +} = require('../../../constants'); + +/** + * @param {ConfigFile} configFile + * @param {generateBlsKeys} generateBlsKeys + * @param {tenderdashInitTask} tenderdashInitTask + * @param {registerMasternodeTask} registerMasternodeTask + * @param {renderServiceTemplates} renderServiceTemplates + * @param {writeServiceConfigs} writeServiceConfigs + */ +function setupRegularPresetTaskFactory( + configFile, + generateBlsKeys, + tenderdashInitTask, + registerMasternodeTask, + renderServiceTemplates, + writeServiceConfigs, +) { + /** + * @typedef {setupRegularPresetTask} + * @return {Listr} + */ + function setupRegularPresetTask() { + return new Listr([ + { + task: (ctx) => { + ctx.config = configFile.getConfig(ctx.preset); + }, + }, + { + title: 'Set node type', + task: async (ctx, task) => { + if (ctx.nodeType === undefined) { + ctx.nodeType = await task.prompt([ + { + type: 'select', + message: 'Select node type', + choices: NODE_TYPES, + initial: NODE_TYPE_MASTERNODE, + }, + ]); + } + + ctx.config.set('core.masternode.enable', ctx.nodeType === NODE_TYPE_MASTERNODE); + + // eslint-disable-next-line no-param-reassign + task.output = `Selected ${ctx.nodeType} type\n`; + }, + options: { persistentOutput: true }, + }, + { + title: 'Configure external IP address', + task: async (ctx, task) => { + if (ctx.externalIp === undefined) { + ctx.externalIp = await task.prompt([ + { + type: 'input', + message: 'Enter node public IP (Enter to accept detected IP)', + initial: () => publicIp.v4(), + }, + ]); + } + + ctx.config.set('externalIp', ctx.externalIp); + + // eslint-disable-next-line no-param-reassign + task.output = `${ctx.externalIp} is set\n`; + }, + options: { persistentOutput: true }, + }, + { + title: 'Set masternode operator private key', + enabled: (ctx) => ctx.nodeType === NODE_TYPE_MASTERNODE, + task: async (ctx, task) => { + if (ctx.operatorBlsPrivateKey === undefined) { + const { privateKey: generatedPrivateKeyHex } = await generateBlsKeys(); + + ctx.operatorBlsPrivateKey = await task.prompt([ + { + type: 'input', + message: 'Enter operator BLS private key (Enter to accept generated key)', + initial: generatedPrivateKeyHex, + }, + ]); + } + + const operatorBlsPrivateKeyBuffer = Buffer.from(ctx.operatorBlsPrivateKey, 'hex'); + + const blsSignatures = await BlsSignatures(); + const { PrivateKey: BlsPrivateKey } = blsSignatures; + + const privateKey = BlsPrivateKey.fromBytes(operatorBlsPrivateKeyBuffer, true); + const publicKey = privateKey.getPublicKey(); + const publicKeyHex = Buffer.from(publicKey.serialize()).toString('hex'); + + ctx.config.set('core.masternode.operator.privateKey', ctx.operatorBlsPrivateKey); + + ctx.operator = { + publicKey: publicKeyHex, + }; + + // eslint-disable-next-line no-param-reassign + task.output = `BLS public key: ${publicKeyHex}\nBLS private key: ${ctx.operatorBlsPrivateKey}`; + }, + options: { persistentOutput: true }, + }, + { + title: 'Register masternode', + enabled: (ctx) => ( + ctx.nodeType === NODE_TYPE_MASTERNODE + && ctx.fundingPrivateKeyString !== undefined + ), + task: (ctx) => { + if (ctx.preset === PRESET_MAINNET) { + throw new Error('For your own security, this tool will not process mainnet private keys. You should consider the private key you entered to be compromised.'); + } + + const fundingPrivateKey = new PrivateKey(ctx.fundingPrivateKeyString, ctx.preset); + ctx.fundingAddress = fundingPrivateKey.toAddress(ctx.preset).toString(); + + // Write configs + const configFiles = renderServiceTemplates(ctx.config); + writeServiceConfigs(ctx.config.getName(), configFiles); + + return registerMasternodeTask(ctx.config); + }, + options: { persistentOutput: true }, + }, + { + title: 'Initialize Tenderdash', + enabled: (ctx) => ctx.preset !== PRESET_MAINNET, + task: (ctx) => tenderdashInitTask(ctx.config), + }, + { + title: 'Set default config', + task: (ctx, task) => { + configFile.setDefaultConfigName(ctx.preset); + + // eslint-disable-next-line no-param-reassign + task.output = `${ctx.config.getName()} set as default config\n`; + }, + }, + ]); + } + + return setupRegularPresetTask; +} + +module.exports = setupRegularPresetTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/startGroupNodesTaskFactory.js b/packages/dashmate/src/listr/tasks/startGroupNodesTaskFactory.js new file mode 100644 index 00000000000..c954b3a83c7 --- /dev/null +++ b/packages/dashmate/src/listr/tasks/startGroupNodesTaskFactory.js @@ -0,0 +1,170 @@ +const { Listr } = require('listr2'); + +const { PrivateKey } = require('@dashevo/dashcore-lib'); +const { NETWORK_LOCAL } = require('../../constants'); + +/** + * + * @param {DockerCompose} dockerCompose + * @param {waitForCorePeersConnected} waitForCorePeersConnected + * @param {waitForMasternodesSync} waitForMasternodesSync + * @param {createRpcClient} createRpcClient + * @param {Docker} docker + * @param {startNodeTask} startNodeTask + * @param {waitForNodeToBeReadyTask} waitForNodeToBeReadyTask + * @param {buildServicesTask} buildServicesTask + * @return {startGroupNodesTask} + */ +function startGroupNodesTaskFactory( + dockerCompose, + waitForCorePeersConnected, + waitForMasternodesSync, + createRpcClient, + docker, + startNodeTask, + waitForNodeToBeReadyTask, + buildServicesTask, +) { + /** + * @typedef {startGroupNodesTask} + * @param {Config[]} configGroup + * @return {Object} + */ + function startGroupNodesTask(configGroup) { + const minerConfig = configGroup.find((config) => ( + config.get('core.miner.enable') + )); + + const platformBuildConfig = configGroup.find((config) => ( + config.has('platform.sourcePath') && config.get('platform.sourcePath') !== null + )); + + return new Listr([ + { + enabled: () => platformBuildConfig, + task: () => buildServicesTask(platformBuildConfig), + }, + { + title: 'Starting nodes', + task: async (ctx) => { + ctx.skipBuildServices = true; + + const tasks = configGroup.map((config) => ({ + title: `Starting ${config.getName()} node`, + task: () => startNodeTask(config), + })); + + return new Listr(tasks, { concurrent: true }); + }, + }, + { + title: 'Wait for Core peers to be connected', + enabled: () => minerConfig && minerConfig.get('network') === NETWORK_LOCAL, + task: () => { + const tasks = configGroup.map((config) => ({ + title: `Checking ${config.getName()} peers`, + task: async () => { + const rpcClient = createRpcClient({ + port: config.get('core.rpc.port'), + user: config.get('core.rpc.user'), + pass: config.get('core.rpc.password'), + }); + + await waitForCorePeersConnected(rpcClient); + }, + })); + + return new Listr(tasks, { concurrent: true }); + }, + }, + { + title: 'Mock core node time', + enabled: () => minerConfig && minerConfig.get('network') === NETWORK_LOCAL, + task: async () => { + // TASK RATIONALE: + // During DKG sessions, nodes can make only 1 quorum request per 10 minutes. + // If mocktime is not adjusted, quorums will start failing to form after some time. + const minerInterval = minerConfig.get('core.miner.interval'); + // 2.5 minutes - mimics the behaviour of the real network + const secondsToAdd = 150; + + const tasks = configGroup.map((config) => ({ + title: `Adjust ${config.getName()} mock time`, + task: async () => { + /* eslint-disable no-useless-escape */ + await dockerCompose.execCommand( + config.toEnvs(), + 'core', + [ + 'bash', + '-c', + ` + response=\$(dash-cli getblockchaininfo); + mocktime=\$(echo \${response} | grep -o -E '\"mediantime\"\: [0-9]+' | cut -d ' ' -f2); + while true; do + mocktime=\$((mocktime + ${secondsToAdd})); + dash-cli setmocktime \$mocktime; + sleep ${minerInterval}; + done + `, + ], + ['--detach'], + ); + /* eslint-enable no-useless-escape */ + }, + })); + + return new Listr(tasks, { concurrent: true }); + }, + }, + { + title: 'Start a miner', + enabled: () => minerConfig && minerConfig.get('network') === NETWORK_LOCAL, + task: async () => { + let minerAddress = minerConfig.get('core.miner.address'); + + if (minerAddress === null) { + const privateKey = new PrivateKey(); + minerAddress = privateKey.toAddress('regtest').toString(); + + minerConfig.set('core.miner.address', minerAddress); + } + + const minerInterval = minerConfig.get('core.miner.interval'); + + await dockerCompose.execCommand( + minerConfig.toEnvs(), + 'core', + [ + 'bash', + '-c', + `while true; do + dash-cli generatetoaddress 1 ${minerAddress}; + sleep ${minerInterval}; + done`, + ], + ['--detach'], + ); + }, + }, + { + title: 'Wait for nodes to be ready', + enabled: (ctx) => Boolean(ctx.waitForReadiness), + task: () => { + const tasks = configGroup + .filter((config) => config.has('platform')) + .map((config) => ({ + title: `Wait for ${config.getName()} node`, + task: () => waitForNodeToBeReadyTask(config), + })); + + return new Listr(tasks, { concurrent: true }); + }, + }, + ]); + } + + return startGroupNodesTask; +} + +module.exports = startGroupNodesTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/startNodeTaskFactory.js b/packages/dashmate/src/listr/tasks/startNodeTaskFactory.js new file mode 100644 index 00000000000..7465611706c --- /dev/null +++ b/packages/dashmate/src/listr/tasks/startNodeTaskFactory.js @@ -0,0 +1,126 @@ +const fs = require('fs'); +const path = require('path'); + +const { Listr } = require('listr2'); +const { Observable } = require('rxjs'); + +const { NETWORK_LOCAL } = require('../../constants'); + +/** + * + * @param {DockerCompose} dockerCompose + * @param {waitForCorePeersConnected} waitForCorePeersConnected + * @param {waitForMasternodesSync} waitForMasternodesSync + * @param {createRpcClient} createRpcClient + * @param {buildServicesTask} buildServicesTask + * @return {startNodeTask} + */ +function startNodeTaskFactory( + dockerCompose, + waitForCorePeersConnected, + waitForMasternodesSync, + createRpcClient, + buildServicesTask, +) { + /** + * @typedef {startNodeTask} + * @param {Config} config + * @return {Object} + */ + function startNodeTask(config) { + // Check external IP is set + config.get('externalIp', true); + + const isMinerEnabled = config.get('core.miner.enable'); + + if (isMinerEnabled === true && config.get('network') !== NETWORK_LOCAL) { + throw new Error(`'core.miner.enabled' option only works with local network. Your network is ${config.get('network')}.`); + } + + // Check Drive log files are created + if (config.has('platform')) { + const prettyFilePath = config.get('platform.drive.abci.log.prettyFile.path'); + + // Remove directory that could potentially be created by Docker mount + if (fs.existsSync(prettyFilePath) && fs.lstatSync(prettyFilePath).isDirectory()) { + fs.rmSync(prettyFilePath, { recursive: true }); + } + + if (!fs.existsSync(prettyFilePath)) { + fs.mkdirSync(path.dirname(prettyFilePath), { recursive: true }); + fs.writeFileSync(prettyFilePath, ''); + } + + const jsonFilePath = config.get('platform.drive.abci.log.jsonFile.path'); + + // Remove directory that could potentially be created by Docker mount + if (fs.existsSync(jsonFilePath) && fs.lstatSync(jsonFilePath).isDirectory()) { + fs.rmSync(jsonFilePath, { recursive: true }); + } + + if (!fs.existsSync(jsonFilePath)) { + fs.mkdirSync(path.dirname(jsonFilePath), { recursive: true }); + fs.writeFileSync(jsonFilePath, ''); + } + } + + return new Listr([ + { + title: 'Check node is not started', + task: async () => { + if (await dockerCompose.isServiceRunning(config.toEnvs())) { + throw new Error('Running services detected. Please ensure all services are stopped for this config before starting'); + } + }, + }, + { + enabled: (ctx) => !ctx.skipBuildServices + && config.has('platform.sourcePath') + && config.get('platform.sourcePath') !== null, + task: () => buildServicesTask(config), + }, + { + title: 'Start services', + task: async () => { + const isMasternode = config.get('core.masternode.enable'); + if (isMasternode) { + // Check operatorPrivateKey is set + config.get('core.masternode.operator.privateKey', true); + } + + const envs = config.toEnvs(); + + await dockerCompose.up(envs); + }, + }, + { + title: 'Force nodes to sync', + enabled: () => config.get('network') === NETWORK_LOCAL, + task: async () => { + const rpcClient = createRpcClient({ + port: config.get('core.rpc.port'), + user: config.get('core.rpc.user'), + pass: config.get('core.rpc.password'), + }); + + return new Observable(async (observer) => { + await waitForMasternodesSync( + rpcClient, + (verificationProgress) => { + observer.next(`${(verificationProgress * 100).toFixed(2)}% complete`); + }, + ); + + observer.complete(); + + return this; + }); + }, + }, + ]); + } + + return startNodeTask; +} + +module.exports = startNodeTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/stopNodeTaskFactory.js b/packages/dashmate/src/listr/tasks/stopNodeTaskFactory.js new file mode 100644 index 00000000000..14f7cef1e40 --- /dev/null +++ b/packages/dashmate/src/listr/tasks/stopNodeTaskFactory.js @@ -0,0 +1,56 @@ +const { Listr } = require('listr2'); + +/** + * @param {DockerCompose} dockerCompose + * @param {createRpcClient} createRpcClient + * @return {stopNodeTask} + */ +function stopNodeTaskFactory( + dockerCompose, + createRpcClient, +) { + /** + * Stop node + * @typedef stopNodeTask + * @param {Config} config + * + * @return {Listr} + */ + function stopNodeTask(config) { + return new Listr([ + { + title: 'Check node is running', + skip: (ctx) => ctx.isForce, + task: async () => { + if (!await dockerCompose.isServiceRunning(config.toEnvs())) { + throw new Error('Node is not running'); + } + }, + }, + { + title: 'Save core node time', + enabled: () => config.get('group') === 'local', + skip: (ctx) => ctx.isForce, + task: async () => { + const rpcClient = createRpcClient({ + port: config.get('core.rpc.port'), + user: config.get('core.rpc.user'), + pass: config.get('core.rpc.password'), + }); + + const { result: { mediantime } } = await rpcClient.getBlockchainInfo(); + + config.set('core.miner.mediantime', mediantime); + }, + }, + { + title: `Stopping ${config.getName()} node`, + task: async () => dockerCompose.stop(config.toEnvs()), + }, + ]); + } + + return stopNodeTask; +} + +module.exports = stopNodeTaskFactory; diff --git a/packages/dashmate/src/listr/tasks/wallet/generateToAddressTaskFactory.js b/packages/dashmate/src/listr/tasks/wallet/generateToAddressTaskFactory.js new file mode 100644 index 00000000000..4f2c52a17fb --- /dev/null +++ b/packages/dashmate/src/listr/tasks/wallet/generateToAddressTaskFactory.js @@ -0,0 +1,122 @@ +const { Listr } = require('listr2'); + +const { Observable } = require('rxjs'); + +/** + * + * @param {startCore} startCore + * @param {createNewAddress} createNewAddress + * @param {generateToAddress} generateToAddress + * @param {generateBlocks} generateBlocks + * @param {waitForBalanceToConfirm} waitForBalanceToConfirm + * @return {generateToAddressTask} + */ +function generateToAddressTaskFactory( + startCore, + createNewAddress, + generateToAddress, + generateBlocks, + waitForBalanceToConfirm, +) { + /** + * @typedef {generateToAddressTask} + * @param {Config} config + * @param {number} amount + * @return {Listr} + */ + function generateToAddressTask(config, amount) { + return new Listr([ + { + title: 'Start Core', + enabled: (ctx) => { + ctx.coreServicePassed = Boolean(ctx.coreService); + + return !ctx.coreServicePassed; + }, + task: async (ctx) => { + ctx.coreServicePassed = false; + ctx.coreService = await startCore(config, { wallet: true }); + }, + }, + { + title: 'Create a new address', + skip: (ctx) => { + if (ctx.address) { + return `Use specified address ${ctx.address}`; + } + + return false; + }, + task: async (ctx, task) => { + ({ + address: ctx.address, + privateKey: ctx.privateKey, + } = await createNewAddress(ctx.coreService)); + + // eslint-disable-next-line no-param-reassign + task.output = `Address: ${ctx.address}\nPrivate key: ${ctx.privateKey}`; + }, + options: { persistentOutput: true }, + }, + { + title: `Generate ≈${amount} dash to address`, + task: (ctx, task) => { + // eslint-disable-next-line no-param-reassign + task.title += ` ${ctx.address}`; + + return new Observable(async (observer) => { + await generateToAddress( + ctx.coreService, + amount, + ctx.address, + (balance) => { + ctx.balance = balance; + observer.next(`${balance} dash generated`); + }, + ); + + // eslint-disable-next-line no-param-reassign + task.output = `Generated ${ctx.balance} dash`; + + // Set for further tasks + ctx.fundingAddress = ctx.address; + ctx.fundingPrivateKeyString = ctx.privateKey; + + observer.complete(); + + return this; + }); + }, + options: { persistentOutput: true }, + }, + { + title: 'Wait for balance to confirm', + task: async (ctx) => ( + new Observable(async (observer) => { + await waitForBalanceToConfirm( + ctx.coreService, + config.get('network'), + ctx.address, + (balance) => { + observer.next(`${balance} dash to confirm`); + }, + ); + + observer.complete(); + + return this; + }) + ), + }, + { + title: 'Stop Core', + enabled: (ctx) => !ctx.coreServicePassed, + task: async (ctx) => ctx.coreService.stop(), + }, + ]); + } + + return generateToAddressTask; +} + +module.exports = generateToAddressTaskFactory; diff --git a/packages/dashmate/src/oclif/command/BaseCommand.js b/packages/dashmate/src/oclif/command/BaseCommand.js new file mode 100644 index 00000000000..5414e904c6c --- /dev/null +++ b/packages/dashmate/src/oclif/command/BaseCommand.js @@ -0,0 +1,134 @@ +const { Command, Flags, settings } = require('@oclif/core'); + +const { asValue } = require('awilix'); + +const graceful = require('node-graceful'); + +const dotenv = require('dotenv'); + +const getFunctionParams = require('../../util/getFunctionParams'); + +const createDIContainer = require('../../createDIContainer'); + +const ConfigFileNotFoundError = require('../../config/errors/ConfigFileNotFoundError'); + +/** + * @abstract + */ +class BaseCommand extends Command { + async init() { + // Read environment variables from .env file + dotenv.config(); + + const { args, flags } = await this.parse(this.constructor); + + this.parsedArgs = args; + this.parsedFlags = flags; + + this.container = await createDIContainer(); + + // Set up home dir + /** + * @type {ensureHomeDir} + */ + const ensureHomeDir = this.container.resolve('ensureHomeDir'); + + ensureHomeDir(); + + // Load configs + /** + * @type {ConfigFileJsonRepository} + */ + const configFileRepository = this.container.resolve('configFileRepository'); + + let configFile; + try { + // Load config collection from config file + configFile = await configFileRepository.read(); + } catch (e) { + // Create default config collection if config file is not present + // on the first start for example + + if (!(e instanceof ConfigFileNotFoundError)) { + throw e; + } + + /** + * @type {createSystemConfigs} + */ + const createSystemConfigs = this.container.resolve('createSystemConfigs'); + + configFile = createSystemConfigs(); + } + + // Register config collection in the container + this.container.register({ + configFile: asValue(configFile), + }); + + // Graceful exit + const stopAllContainers = this.container.resolve('stopAllContainers'); + const startedContainers = this.container.resolve('startedContainers'); + + graceful.exitOnDouble = false; + graceful.on('exit', async () => { + // remove all attached listeners from other libraries to mute there output + process.removeAllListeners('uncaughtException'); + process.removeAllListeners('unhandledRejection'); + + process.on('unhandledRejection', () => {}); + process.on('uncaughtException', () => {}); + + // stop and remove all started containers + await stopAllContainers(startedContainers.getContainers()); + }); + } + + async run() { + if (!this.runWithDependencies) { + throw new Error('`run` or `runWithDependencies` must be implemented'); + } + + const params = getFunctionParams(this.runWithDependencies, 2); + + const dependencies = params.map((paramName) => this.container.resolve(paramName)); + + return this.runWithDependencies(this.parsedArgs, this.parsedFlags, ...dependencies); + } + + async finally(err) { + // Save configs collection + if (this.container) { + const configFileRepository = this.container.resolve('configFileRepository'); + + if (this.container.has('configFile')) { + const configFile = this.container.resolve('configFile'); + + await configFileRepository.write(configFile); + } + + // Stop all running containers + const stopAllContainers = this.container.resolve('stopAllContainers'); + const startedContainers = this.container.resolve('startedContainers'); + + await stopAllContainers( + startedContainers.getContainers(), + { + remove: !settings.debug, + }, + ); + } + + return super.finally(err); + } +} + +BaseCommand.flags = { + verbose: Flags.boolean({ + char: 'v', + description: 'use verbose mode for output', + default: false, + }), +}; + +module.exports = BaseCommand; diff --git a/packages/dashmate/src/oclif/command/ConfigBaseCommand.js b/packages/dashmate/src/oclif/command/ConfigBaseCommand.js new file mode 100644 index 00000000000..e06489e7bdd --- /dev/null +++ b/packages/dashmate/src/oclif/command/ConfigBaseCommand.js @@ -0,0 +1,60 @@ +const { Flags } = require('@oclif/core'); + +const { asValue } = require('awilix'); + +const BaseCommand = require('./BaseCommand'); +const ConfigIsNotPresentError = require('../../config/errors/ConfigIsNotPresentError'); + +/** + * @abstract + */ +class GroupBaseCommand extends BaseCommand { + async run() { + const configFile = this.container.resolve('configFile'); + + let configName; + if (this.parsedFlags.config !== null) { + if (!configFile.isConfigExists(this.parsedFlags.config)) { + throw new ConfigIsNotPresentError(this.parsedFlags.config); + } + + configName = this.parsedFlags.config; + } else { + const defaultConfigName = configFile.getDefaultConfigName(); + + if (defaultConfigName === null) { + throw new Error('Default config is not set. Please use \'--config\' option or set default config'); + } + + if (!configFile.isConfigExists(defaultConfigName)) { + throw new Error(`Default config ${defaultConfigName} does not exist. Please use '--config' option or change default config`); + } + + configName = defaultConfigName; + } + + const config = configFile.getConfig(configName); + + this.container.register({ + config: asValue(config), + }); + + const renderServiceTemplates = this.container.resolve('renderServiceTemplates'); + const writeServiceConfigs = this.container.resolve('writeServiceConfigs'); + + const serviceConfigFiles = renderServiceTemplates(config); + writeServiceConfigs(config.getName(), serviceConfigFiles); + + return super.run(); + } +} + +GroupBaseCommand.flags = { + config: Flags.string({ + description: 'configuration name to use', + default: null, + }), + ...BaseCommand.flags, +}; + +module.exports = GroupBaseCommand; diff --git a/packages/dashmate/src/oclif/command/GroupBaseCommand.js b/packages/dashmate/src/oclif/command/GroupBaseCommand.js new file mode 100644 index 00000000000..53476be1f4d --- /dev/null +++ b/packages/dashmate/src/oclif/command/GroupBaseCommand.js @@ -0,0 +1,62 @@ +const { Flags } = require('@oclif/core'); + +const { asValue } = require('awilix'); + +const BaseCommand = require('./BaseCommand'); +const GroupIsNotPresentError = require('../../config/errors/GroupIsNotPresentError'); + +/** + * @abstract + */ +class GroupBaseCommand extends BaseCommand { + async run() { + const configFile = this.container.resolve('configFile'); + + let groupName; + if (this.parsedFlags.group !== null) { + if (!configFile.isGroupExists(this.parsedFlags.group)) { + throw new GroupIsNotPresentError(this.parsedFlags.group); + } + + groupName = this.parsedFlags.group; + } else { + const defaultGroupName = configFile.getDefaultGroupName(); + + if (defaultGroupName === null) { + throw new Error('Default group is not set. Please use `--group` option or set default group'); + } + + if (!configFile.isGroupExists(defaultGroupName)) { + throw new Error(`Default group ${defaultGroupName} does not exist. Please use '--group' option or change default group`); + } + + groupName = defaultGroupName; + } + + const group = configFile.getGroupConfigs(groupName); + + this.container.register({ + configGroup: asValue(group), + }); + + const renderServiceTemplates = this.container.resolve('renderServiceTemplates'); + const writeServiceConfigs = this.container.resolve('writeServiceConfigs'); + + for (const config of group) { + const serviceConfigFiles = renderServiceTemplates(config); + writeServiceConfigs(config.getName(), serviceConfigFiles); + } + + return super.run(); + } +} + +GroupBaseCommand.flags = { + group: Flags.string({ + description: 'group name to use', + default: null, + }), + ...BaseCommand.flags, +}; + +module.exports = GroupBaseCommand; diff --git a/packages/dashmate/src/oclif/errors/MuteOneLineError.js b/packages/dashmate/src/oclif/errors/MuteOneLineError.js new file mode 100644 index 00000000000..97c5d093eaf --- /dev/null +++ b/packages/dashmate/src/oclif/errors/MuteOneLineError.js @@ -0,0 +1,30 @@ +const { settings } = require('@oclif/core'); + +const AbstractError = require('../../errors/AbstractError'); + +class MuteOneLineError extends AbstractError { + /** + * @param {Error} error + */ + constructor(error) { + super('SIGINT'); + + if (settings.debug || (error.message && error.message.trimEnd().includes('\n'))) { + this.name = error.name; + this.message = error.message; + this.stack = error.stack; + } + + this.error = error; + } + + /** + * Get thrown error + * @return {Error} + */ + getError() { + return this.error; + } +} + +module.exports = MuteOneLineError; diff --git a/packages/dashmate/src/printers/errors/UnsupportedFormatError.js b/packages/dashmate/src/printers/errors/UnsupportedFormatError.js new file mode 100644 index 00000000000..7f13e7defc0 --- /dev/null +++ b/packages/dashmate/src/printers/errors/UnsupportedFormatError.js @@ -0,0 +1,23 @@ +const AbstractError = require('../../errors/AbstractError'); + +class UnsupportedFormatError extends AbstractError { + /** + * @param {string} formatName + */ + constructor(formatName) { + super(`Unsupported format: ${formatName}`); + + this.formatName = formatName; + } + + /** + * Get config name + * + * @return {string} + */ + getformatName() { + return this.formatName; + } +} + +module.exports = UnsupportedFormatError; diff --git a/packages/dashmate/src/printers/printArrayOfObjects.js b/packages/dashmate/src/printers/printArrayOfObjects.js new file mode 100644 index 00000000000..a88fd2f7500 --- /dev/null +++ b/packages/dashmate/src/printers/printArrayOfObjects.js @@ -0,0 +1,50 @@ +const stripAnsi = require('strip-ansi'); +const { table } = require('table'); + +const { OUTPUT_FORMATS } = require('../constants'); + +const UnsupportedFormatError = require('./errors/UnsupportedFormatError'); + +/** + * Prints object using specified output format + * + * @param {[Object[]]} array + * @param {string} format + */ +function printArrayofObjects(array, format) { + let output; + switch (format) { + case OUTPUT_FORMATS.PLAIN: { + // Init array with headings + const rows = [Object.keys(array[0])]; + array.map((obj) => rows.push(Object.values(obj))); + + const tableConfig = { + drawHorizontalLine: (index, size) => index === 0 || index === 1 || index === size, + }; + + output = table(rows, tableConfig); + break; + } + case OUTPUT_FORMATS.JSON: { + const cleanArray = []; + array.forEach((outputRow, i) => { + const cleanRow = {}; + Object.keys(outputRow).forEach((key) => { + cleanRow[key] = stripAnsi(outputRow[key]); + }); + cleanArray[i] = cleanRow; + }); + output = JSON.stringify(cleanArray); + break; + } + default: { + throw new UnsupportedFormatError(format); + } + } + + // eslint-disable-next-line no-console + console.log(output); +} + +module.exports = printArrayofObjects; diff --git a/packages/dashmate/src/printers/printObject.js b/packages/dashmate/src/printers/printObject.js new file mode 100644 index 00000000000..59c0c6c5d10 --- /dev/null +++ b/packages/dashmate/src/printers/printObject.js @@ -0,0 +1,39 @@ +const stripAnsi = require('strip-ansi'); +const { table } = require('table'); + +const { OUTPUT_FORMATS } = require('../constants'); + +const UnsupportedFormatError = require('./errors/UnsupportedFormatError'); + +/** + * Prints object using specified output format + * + * @param {Object} object + * @param {string} format + */ +function printObject(object, format) { + let output; + switch (format) { + case OUTPUT_FORMATS.PLAIN: { + const rows = Object.entries(object); + output = table(rows, { singleLine: true }); + break; + } + case OUTPUT_FORMATS.JSON: { + const cleanObject = {}; + Object.keys(object).forEach((key) => { + cleanObject[key] = stripAnsi(object[key]); + }); + output = JSON.stringify(cleanObject); + break; + } + default: { + throw new UnsupportedFormatError(format); + } + } + + // eslint-disable-next-line no-console + console.log(output); +} + +module.exports = printObject; diff --git a/packages/dashmate/src/status/outputStatusOverviewFactory.js b/packages/dashmate/src/status/outputStatusOverviewFactory.js new file mode 100644 index 00000000000..bd252fd6cbc --- /dev/null +++ b/packages/dashmate/src/status/outputStatusOverviewFactory.js @@ -0,0 +1,263 @@ +const fetch = require('node-fetch'); +const chalk = require('chalk'); + +const ContainerIsNotPresentError = require('../docker/errors/ContainerIsNotPresentError'); +const ServiceIsNotRunningError = require('../docker/errors/ServiceIsNotRunningError'); + +const CoreService = require('../core/CoreService'); +const blocksToTime = require('../util/blocksToTime'); +const getPaymentQueuePosition = require('../util/getPaymentQueuePosition'); +const printObject = require('../printers/printObject'); + +/** + * + * @param {DockerCompose} dockerCompose + * @param {createRpcClient} createRpcClient + * @return {outputStatusOverview} + */ +function outputStatusOverviewFactory( + dockerCompose, + createRpcClient, +) { + /** + * @typedef {outputStatusOverview} + * @param {Config} config + * @param {string} format + * @return void + */ + async function outputStatusOverview(config, format) { + const coreService = new CoreService( + config, + createRpcClient( + { + port: config.get('core.rpc.port'), + user: config.get('core.rpc.user'), + pass: config.get('core.rpc.password'), + }, + ), + dockerCompose.docker.getContainer('core'), + ); + + if (!(await dockerCompose.isServiceRunning(config.toEnvs(), 'core'))) { + throw new ServiceIsNotRunningError(config.get('network'), 'core'); + } + + // Collect core data + const { + result: { + AssetName: coreSyncAsset, + IsSynced: coreIsSynced, + }, + } = await coreService.getRpcClient().mnsync('status'); + + let { + result: { + subversion: coreVersion, + }, + } = await coreService.getRpcClient().getNetworkInfo(); + coreVersion = coreVersion.replace(/\/|\(.*?\)|Dash Core:/g, ''); + + const { + result: { + blocks: coreBlocks, + chain: coreChain, + verificationprogress: coreVerificationProgress, + }, + } = await coreService.getRpcClient().getBlockchainInfo(); + + // Collect masternode data + let masternodeState; + let masternodeStatus; + let masternodeEnabledCount; + if (config.get('core.masternode.enable')) { + ({ + result: { + dmnState: masternodeState, + status: masternodeStatus, + }, + } = await coreService.getRpcClient().masternode('status')); + ({ + result: { + enabled: masternodeEnabledCount, + }, + } = await coreService.getRpcClient().masternode('count')); + } + + // Collect platform data + let platformVersion; + let platformBlockHeight; + let platformCatchingUp; + let platformStatus; + + if (config.get('network') !== 'mainnet' && config.name !== 'local_seed') { + if (!(await dockerCompose.isServiceRunning(config.toEnvs(), 'drive_tenderdash'))) { + try { + ({ + State: { + Status: platformStatus, + }, + } = await dockerCompose.inspectService(config.toEnvs(), 'drive_tenderdash')); + } catch (e) { + if (e instanceof ContainerIsNotPresentError) { + platformStatus = 'not started'; + } + } + } else if (coreIsSynced === true) { + // Collecting platform data fails if Tenderdash is waiting for core to sync + try { + const platformStatusRes = await fetch(`http://localhost:${config.get('platform.drive.tenderdash.rpc.port')}/status`); + ({ + result: { + node_info: { + version: platformVersion, + }, + sync_info: { + latest_block_height: platformBlockHeight, + catching_up: platformCatchingUp, + }, + }, + } = await platformStatusRes.json()); + } catch (e) { + if (e.name === 'FetchError') { + platformVersion = 'unknown'; + platformBlockHeight = 0; + platformCatchingUp = false; + } else { + throw e; + } + } + } + } + + const platformExplorerURLs = { + testnet: 'https://rpc.cloudwheels.net:26657', + mainnet: '', + local: '', + }; + + let explorerBlockHeight; + if (platformExplorerURLs[config.get('network')] !== '') { + try { + const explorerBlockHeightRes = await fetch(`${platformExplorerURLs[config.get('network')]}/status`); + ({ + result: { + sync_info: { + latest_block_height: explorerBlockHeight, + }, + }, + } = await explorerBlockHeightRes.json()); + } catch (e) { + if (e.name === 'FetchError') { + explorerBlockHeight = 0; + } else { + throw e; + } + } + } else { + explorerBlockHeight = 0; + } + + // Determine status + let coreStatus; + try { + ({ + State: { + Status: coreStatus, + }, + } = await dockerCompose.inspectService(config.toEnvs(), 'core')); + } catch (e) { + if (e instanceof ContainerIsNotPresentError) { + coreStatus = 'not started'; + } + } + if (coreStatus === 'running' && coreSyncAsset !== 'MASTERNODE_SYNC_FINISHED') { + coreStatus = `syncing ${(coreVerificationProgress * 100).toFixed(2)}%`; + } + + if (config.get('network') !== 'mainnet') { + try { + ({ + State: { + Status: platformStatus, + }, + } = await dockerCompose.inspectService(config.toEnvs(), 'drive_tenderdash')); + } catch (e) { + if (e instanceof ContainerIsNotPresentError) { + platformStatus = 'not started'; + } + } + if (platformStatus === 'running' && coreIsSynced === false) { + platformStatus = 'waiting for core sync'; + } else if (platformStatus === 'running' && platformCatchingUp === true) { + platformStatus = `syncing ${((platformBlockHeight / explorerBlockHeight) * 100).toFixed(2)}%`; + } + } + + // Determine payment queue position + let paymentQueuePosition; + if (config.get('core.masternode.enable') && masternodeStatus === 'Ready') { + paymentQueuePosition = getPaymentQueuePosition( + masternodeState, masternodeEnabledCount, coreBlocks, + ); + } + + // Apply colors + if (coreStatus === 'running') { + coreStatus = chalk.green(coreStatus); + } else if (coreStatus.includes('syncing')) { + coreStatus = chalk.yellow(coreStatus); + } else { + coreStatus = chalk.red(coreStatus); + } + + if (config.get('network') !== 'mainnet' && config.name !== 'local_seed') { + if (platformStatus === 'running') { + platformStatus = chalk.green(platformStatus); + } else if (platformStatus.startsWith('syncing')) { + platformStatus = chalk.yellow(platformStatus); + } else { + platformStatus = chalk.red(platformStatus); + } + } + + if (masternodeStatus === 'Ready') { + masternodeStatus = chalk.green(masternodeStatus); + } else { + masternodeStatus = chalk.red(masternodeStatus); + } + + const outputRows = { + Network: coreChain, + 'Core Version': coreVersion.replace(/\/|\(.*?\)/g, ''), + 'Core Status': coreStatus, + }; + + if (config.get('core.masternode.enable')) { + outputRows['Masternode Status'] = masternodeStatus; + } + + if (config.get('network') !== 'mainnet' && config.name !== 'local_seed') { + if (coreIsSynced === true + && platformStatus !== chalk.red('not started') + && platformStatus !== chalk.red('restarting')) { + outputRows['Platform Version'] = platformVersion; + } + outputRows['Platform Status'] = platformStatus; + } + if (config.get('core.masternode.enable')) { + if (masternodeStatus === 'Ready') { + outputRows['PoSe Penalty'] = masternodeState.PoSePenalty; + outputRows['Last paid block'] = masternodeState.lastPaidHeight; + outputRows['Last paid time'] = `${blocksToTime(coreBlocks - masternodeState.lastPaidHeight)} ago`; + outputRows['Payment queue position'] = `${paymentQueuePosition}/${masternodeEnabledCount}`; + outputRows['Next payment time'] = `in ${blocksToTime(paymentQueuePosition)}`; + } + } + + printObject(outputRows, format); + } + + return outputStatusOverview; +} + +module.exports = outputStatusOverviewFactory; diff --git a/packages/dashmate/src/templates/renderServiceTemplatesFactory.js b/packages/dashmate/src/templates/renderServiceTemplatesFactory.js new file mode 100644 index 00000000000..5fefa6ebc5e --- /dev/null +++ b/packages/dashmate/src/templates/renderServiceTemplatesFactory.js @@ -0,0 +1,68 @@ +const fs = require('fs'); +const path = require('path'); +const dots = require('dot'); +const glob = require('glob'); + +/** + * @return {renderServiceTemplates} + */ +function renderServiceTemplatesFactory() { + /** + * Render templates for services + * + * @typedef {renderServiceTemplates} + * @param {Config} config + * + * @return {Object} + */ + function renderServiceTemplates(config) { + dots.templateSettings.strip = false; + + const templatesPath = path.join(__dirname, '..', '..', 'templates'); + + const templatePaths = glob + .sync(`${templatesPath}/**/*.dot`) + .filter((templatePath) => { + // Do not render platform templates if it's not configured + if (templatePath.includes('templates/platform') && !config.has('platform')) { + return false; + } + + // Don't create blank tenderdash configs + if (templatePath.includes('templates/platform/drive/tenderdash')) { + const skipEmpty = { + genesis: 'genesis', + node_key: 'nodeKey', + }; + + for (const [configName, optionName] of Object.entries(skipEmpty)) { + const option = config.get(`platform.drive.tenderdash.${optionName}`); + + if (templatePath.includes(configName) && Object.values(option).length === 0) { + return false; + } + } + } + + return true; + }); + + const configFiles = {}; + for (const templatePath of templatePaths) { + const templateString = fs.readFileSync(templatePath, 'utf-8'); + const template = dots.template(templateString); + + const configPath = templatePath + .substring(templatesPath.length + 1) + .replace('.dot', ''); + + configFiles[configPath] = template(config.options); + } + + return configFiles; + } + + return renderServiceTemplates; +} + +module.exports = renderServiceTemplatesFactory; diff --git a/packages/dashmate/src/templates/writeServiceConfigsFactory.js b/packages/dashmate/src/templates/writeServiceConfigsFactory.js new file mode 100644 index 00000000000..a10eef65fcc --- /dev/null +++ b/packages/dashmate/src/templates/writeServiceConfigsFactory.js @@ -0,0 +1,40 @@ +const fs = require('fs'); +const path = require('path'); + +const { HOME_DIR_PATH } = require('../constants'); + +/** + * @return {writeServiceConfigs} + */ +function writeServiceConfigsFactory() { + /** + * Write service config files + * + * @typedef {writeServiceConfigs} + * @param {string} configName + * @param {Object} configFiles + * + * @return {void} + */ + function writeServiceConfigs(configName, configFiles) { + // Drop all files from configs directory + const configDir = path.join(HOME_DIR_PATH, configName); + + fs.rmSync(configDir, { recursive: true, force: true }); + + for (const filePath of Object.keys(configFiles)) { + const absoluteFilePath = path.join(configDir, filePath); + const absoluteFileDir = path.dirname(absoluteFilePath); + + // Recreate it + fs.mkdirSync(absoluteFileDir, { recursive: true }); + + // Write specified config files + fs.writeFileSync(absoluteFilePath, configFiles[filePath], 'utf8'); + } + } + + return writeServiceConfigs; +} + +module.exports = writeServiceConfigsFactory; diff --git a/packages/dashmate/src/tenderdash/createTenderdashRpcClient.js b/packages/dashmate/src/tenderdash/createTenderdashRpcClient.js new file mode 100644 index 00000000000..8bfdcfb602b --- /dev/null +++ b/packages/dashmate/src/tenderdash/createTenderdashRpcClient.js @@ -0,0 +1,17 @@ +const { client: JsonRpcClient } = require('jayson/promise'); + +/** + * Create Tenderdash RPC client + * + * @param {Object} [options] + * @param {string} [options.host] + * @param {number} [options.port] + */ +function createTenderdashRpcClient({ host, port } = {}) { + return JsonRpcClient.http({ + host: host || '127.0.0.1', + port: port || 26657, + }); +} + +module.exports = createTenderdashRpcClient; diff --git a/packages/dashmate/src/tenderdash/initializeTenderdashNodeFactory.js b/packages/dashmate/src/tenderdash/initializeTenderdashNodeFactory.js new file mode 100644 index 00000000000..c3a322232d0 --- /dev/null +++ b/packages/dashmate/src/tenderdash/initializeTenderdashNodeFactory.js @@ -0,0 +1,96 @@ +const { WritableStream } = require('memory-streams'); + +/** + * + * @param {DockerCompose} dockerCompose + * @param {Docker} docker + * @param {dockerPull} dockerPull + * @return {initializeTenderdashNode} + */ +function initializeTenderdashNodeFactory(dockerCompose, docker, dockerPull) { + /** + * @typedef {initializeTenderdashNode} + * @param {Config} config + * @return {Promise} + */ + async function initializeTenderdashNode(config) { + if (await dockerCompose.isServiceRunning(config.toEnvs(), 'drive_tenderdash')) { + throw new Error('Can\'t initialize Tenderdash. Already running.'); + } + + const { COMPOSE_PROJECT_NAME: composeProjectName } = config.toEnvs(); + const volumeName = 'drive_tenderdash'; + const volumeNameFullName = `${composeProjectName}_${volumeName}`; + + const volume = docker.getVolume(volumeNameFullName); + + const isVolumeDefined = await volume.inspect() + .then(() => true) + .catch(() => false); + + if (!isVolumeDefined) { + // Create volume with tenderdash data + await docker.createVolume({ + Name: volumeNameFullName, + Labels: { + 'com.docker.compose.project': composeProjectName, + 'com.docker.compose.version': '1.27.4', + 'com.docker.compose.volume': volumeName, + }, + }); + } + + // Initialize Tenderdash + + const tenderdashImage = config.get('platform.drive.tenderdash.docker.image', true); + + await dockerPull(tenderdashImage); + + const writableStream = new WritableStream(); + + const command = [ + '/usr/bin/tenderdash init validator> /dev/null', + 'echo "["', + 'cat $TMHOME/config/node_key.json', + 'echo ","', + 'cat $TMHOME/config/genesis.json', + 'echo ",\\""', + '/usr/bin/tenderdash show-node-id', + 'echo "\\""', + 'echo "]"', + 'rm -rf $TMHOME/config', + ].join('&&'); + + const [result] = await docker.run( + tenderdashImage, + [], + writableStream, + { + Entrypoint: ['sh', '-c', command], + HostConfig: { + AutoRemove: true, + Binds: [`${volumeNameFullName}:/tenderdash`], + }, + }, + ); + + if (result.StatusCode !== 0) { + let message = writableStream.toString(); + + if (result.StatusCode === 1 && message === '') { + message = 'already initialized. Please reset node data'; + } + + throw new Error(`Can't initialize tenderdash: ${message}`); + } + + let stringifiedJSON = writableStream.toString(); + stringifiedJSON = stringifiedJSON.split('module=main').slice(-1).pop().replace(/\r\n/g, ''); + + return JSON.parse(stringifiedJSON); + } + + return initializeTenderdashNode; +} + +module.exports = initializeTenderdashNodeFactory; diff --git a/packages/dashmate/src/util/blocksToTime.js b/packages/dashmate/src/util/blocksToTime.js new file mode 100644 index 00000000000..16f18a68f74 --- /dev/null +++ b/packages/dashmate/src/util/blocksToTime.js @@ -0,0 +1,15 @@ +function blocksToTime(blocks) { + let time; + const blockTime = 2.625; + const mins = blockTime * blocks; + if (mins > 2880) { + time = `${(mins / 60 / 24).toFixed(2)} days`; + } else if (mins > 300) { + time = `${(mins / 60).toFixed(2)} hours`; + } else { + time = `${(mins).toFixed(2)} minutes`; + } + return time; +} + +module.exports = blocksToTime; diff --git a/packages/dashmate/src/util/generateHDPrivateKeys.js b/packages/dashmate/src/util/generateHDPrivateKeys.js new file mode 100644 index 00000000000..83ea77cb062 --- /dev/null +++ b/packages/dashmate/src/util/generateHDPrivateKeys.js @@ -0,0 +1,37 @@ +const { Wallet } = require('@dashevo/wallet-lib'); + +/** + * Get identity HDPrivateKey for network + * + * @typedef {generateHDPrivateKeys} + * + * @param {string} network + * @param {number[]} keyIndexes + * + * @returns {Promise<{ + * hdPrivateKey: HDPrivateKey, + * derivedPrivateKeys: HDPrivateKey[], + * }>} + */ +async function generateHDPrivateKeys(network, keyIndexes = [0]) { + const wallet = new Wallet({ network, offlineMode: true }); + const account = await wallet.getAccount(); + + const derivedPrivateKeys = []; + keyIndexes.forEach((keyIndex) => { + const derivedPrivateKey = account.identities.getIdentityHDKeyByIndex(0, keyIndex); + + derivedPrivateKeys.push(derivedPrivateKey); + }); + + const hdPrivateKey = wallet.exportWallet('HDPrivateKey'); + + await wallet.disconnect(); + + return { + hdPrivateKey, + derivedPrivateKeys, + }; +} + +module.exports = generateHDPrivateKeys; diff --git a/packages/dashmate/src/util/getFunctionParams.js b/packages/dashmate/src/util/getFunctionParams.js new file mode 100644 index 00000000000..18ef78bedb0 --- /dev/null +++ b/packages/dashmate/src/util/getFunctionParams.js @@ -0,0 +1,59 @@ +const STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,)]*))/mg; +const ARGUMENT_NAMES = /([^\s,]+)/g; + +/** + * Get function params + * + * @param {Function} fn + * @param {number} skip Skip params + * @return {array} + */ +function getFunctionParams(fn, skip = 0) { + const functionString = fn.toString().replace(STRIP_COMMENTS, ''); + + let params = functionString.slice( + functionString.indexOf('(') + 1, + functionString.indexOf(')'), + ).match(ARGUMENT_NAMES); + + if (params === null) { + params = []; + } + + const filteredParams = []; + let openDestructors = 0; + let skippedCount = 0; + + for (let i = 0; i < params.length; i++) { + switch (params[i]) { + case '{': + openDestructors++; + + break; + case '}': + openDestructors--; + + if (openDestructors === 0 && skippedCount < skip) { + skippedCount++; + } + + break; + default: + if (openDestructors > 0) { + break; + } + + if (skippedCount < skip) { + skippedCount++; + + break; + } + + filteredParams.push(params[i]); + } + } + + return filteredParams; +} + +module.exports = getFunctionParams; diff --git a/packages/dashmate/src/util/getPaymentQueuePosition.js b/packages/dashmate/src/util/getPaymentQueuePosition.js new file mode 100644 index 00000000000..0a4efd58386 --- /dev/null +++ b/packages/dashmate/src/util/getPaymentQueuePosition.js @@ -0,0 +1,22 @@ +function getPaymentQueuePosition(masternodeState, masternodeEnabledCount, coreBlocks) { + let paymentQueuePosition; + // Masternode has been unbanned recently + if (masternodeState.PoSeRevivedHeight > masternodeState.lastPaidHeight) { + paymentQueuePosition = masternodeState.PoSeRevivedHeight + + masternodeEnabledCount + - coreBlocks; + // Masternode has never been paid + } else if (masternodeState.lastPaidHeight === 0) { + paymentQueuePosition = masternodeState.registeredHeight + + masternodeEnabledCount + - coreBlocks; + // Masternode was previously paid and is in normal queue + } else { + paymentQueuePosition = masternodeState.lastPaidHeight + + masternodeEnabledCount + - coreBlocks; + } + return paymentQueuePosition; +} + +module.exports = getPaymentQueuePosition; diff --git a/packages/dashmate/src/util/isWSL.js b/packages/dashmate/src/util/isWSL.js new file mode 100644 index 00000000000..c259ddbd299 --- /dev/null +++ b/packages/dashmate/src/util/isWSL.js @@ -0,0 +1,20 @@ +const os = require('os'); +const fs = require('fs'); + +function isWSL() { + if (process.platform !== 'linux') { + return false; + } + + if (os.release().toLowerCase().includes('microsoft')) { + return true; + } + + try { + return fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft'); + } catch (_) { + return false; + } +} + +module.exports = isWSL; diff --git a/packages/dashmate/src/util/satoshiConverter.js b/packages/dashmate/src/util/satoshiConverter.js new file mode 100644 index 00000000000..48200fb0899 --- /dev/null +++ b/packages/dashmate/src/util/satoshiConverter.js @@ -0,0 +1,28 @@ +const SATOSHI_MULTIPLIER = 10 ** 8; + +/** + * Convert satoshis to Dash + * + * @param {number} satoshi + * + * @returns {number} + */ +function toDash(satoshi) { + return satoshi / SATOSHI_MULTIPLIER; +} + +/** + * Convert dash to satoshis + * + * @param {number} dash + * + * @return {number} + */ +function toSatoshi(dash) { + return dash * SATOSHI_MULTIPLIER; +} + +module.exports = { + toDash, + toSatoshi, +}; diff --git a/packages/dashmate/src/util/wait.js b/packages/dashmate/src/util/wait.js new file mode 100644 index 00000000000..b0d45b4c5ce --- /dev/null +++ b/packages/dashmate/src/util/wait.js @@ -0,0 +1,10 @@ +/** + * Asynchronously wait for a specified number of milliseconds. + * @param {Number} ms - Number of milliseconds to wait. + * @return {Promise} The promise to await on. + */ +async function wait(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +module.exports = wait; diff --git a/packages/dashmate/templates/core/dash.conf.dot b/packages/dashmate/templates/core/dash.conf.dot new file mode 100644 index 00000000000..3847f0899e5 --- /dev/null +++ b/packages/dashmate/templates/core/dash.conf.dot @@ -0,0 +1,71 @@ +# general +daemon=0 # leave this set to 0 for Docker +logtimestamps=1 +maxconnections=256 +printtoconsole=1 +debug={{=it.core.debug }} + +# JSONRPC +server=1 +rpcuser={{=it.core.rpc.user}} +rpcpassword={{=it.core.rpc.password}} + +rpcallowip=127.0.0.1 +rpcallowip=172.16.0.0/12 +rpcallowip=192.168.0.0/16 + +rpcworkqueue=64 +rpcthreads=16 + +# external network +listen=1 +dnsseed=0 +allowprivatenet=0 +externalip={{=it.externalIp}} +{{? it.network === 'local'}} +whitelist={{=it.externalIp}} +{{?}} + +{{? it.network !== 'mainnet'}}# Indices +txindex=1 +addressindex=1 +timestampindex=1 +spentindex=1 +{{?}} + +# ZeroMQ notifications +zmqpubrawtx=tcp://0.0.0.0:29998 +zmqpubrawtxlock=tcp://0.0.0.0:29998 +zmqpubhashblock=tcp://0.0.0.0:29998 +zmqpubrawchainlocksig=tcp://0.0.0.0:29998 +zmqpubrawchainlock=tcp://0.0.0.0:29998 +zmqpubrawtxlocksig=tcp://0.0.0.0:29998 + +{{? it.network === 'testnet'}}testnet=1 +[test] +{{?? it.network === 'local'}} +regtest=1 +[regtest] +sporkaddr={{=it.core.spork.address}} +{{? it.core.spork.privateKey}}sporkkey={{=it.core.spork.privateKey}}{{?}} +{{? it.core.miner.mediantime}}mocktime={{=it.core.miner.mediantime}}{{?}} +{{~it.core.p2p.seeds :seed}} +addnode={{=seed.host}}:{{=seed.port}}{{~}} +{{?? it.network === 'devnet'}} +devnet={{=it.core.devnetName}} +llmqchainlocks=llmq_devnet +llmqinstantsend=llmq_devnet +[devnet] +{{~it.core.p2p.seeds :seed}} +addnode={{=seed.host}}:{{=seed.port}}{{~}} + +sporkaddr={{=it.core.spork.address}} +minimumdifficultyblocks=1000 +highsubsidyblocks=500 +highsubsidyfactor=10{{?}} + +# network +port={{=it.core.p2p.port}} +bind=0.0.0.0 +rpcbind=0.0.0.0 +rpcport={{=it.core.rpc.port}} diff --git a/packages/dashmate/templates/platform/dapi/envoy/envoy.yaml.dot b/packages/dashmate/templates/platform/dapi/envoy/envoy.yaml.dot new file mode 100644 index 00000000000..9690960faaf --- /dev/null +++ b/packages/dashmate/templates/platform/dapi/envoy/envoy.yaml.dot @@ -0,0 +1,333 @@ +static_resources: + listeners: + - name: web_grpc_gateweb + address: + socket_address: { address: 0.0.0.0, port_value: 10000 } + filter_chains: + - filters: + - name: envoy.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/stdout + http_filters: + - name: envoy.filters.http.local_ratelimit + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit + stat_prefix: http_local_rate_limiter + # see documentation https://www.envoyproxy.io/docs/envoy/latest/api-v3/type/v3/token_bucket.proto#envoy-v3-api-msg-type-v3-tokenbucket + token_bucket: + max_tokens: {{=it.platform.dapi.envoy.rateLimiter.maxTokens}} + tokens_per_fill: {{=it.platform.dapi.envoy.rateLimiter.tokensPerFill}} + fill_interval: {{=it.platform.dapi.envoy.rateLimiter.fillInterval}} + filter_enabled: + runtime_key: local_rate_limit_enabled + default_value: + numerator: {{? it.platform.dapi.envoy.rateLimiter.enabled}}100{{??}}0{{?}} + denominator: HUNDRED + filter_enforced: + runtime_key: local_rate_limit_enforced + default_value: + numerator: 100 + denominator: HUNDRED + response_headers_to_add: + - append: false + header: + key: x-local-rate-limit + value: 'true' + - name: envoy.filters.http.cors + - name: envoy.filters.http.grpc_web + - name: envoy.filters.http.router + route_config: + name: local_route + virtual_hosts: + - name: dapi_services + domains: ["*"] + routes: + # tx subscription endpoint configuration + - match: + prefix: "/org.dash.platform.dapi.v0.Core/subscribeToTransactionsWithProofs" + headers: + - name: ":method" + safe_regex_match: + google_re2: {} + regex: "GET|POST|OPTIONS" + route: + cluster: tx_filter_stream + timeout: 0s + # block headers subscription endpoint configuration + - match: + prefix: "/org.dash.platform.dapi.v0.Core/subscribeToBlockHeadersWithChainLocks" + headers: + - name: ":method" + safe_regex_match: + google_re2: {} + regex: "GET|POST|OPTIONS" + route: + cluster: tx_filter_stream + timeout: 0s + # core endpoint configutration + - match: + prefix: "/org.dash.platform.dapi.v0.Core" + headers: + - name: ":method" + safe_regex_match: + google_re2: {} + regex: "GET|POST|OPTIONS" + route: + cluster: core_and_platform + # platform endpoint configutration + - match: + prefix: "/org.dash.platform.dapi.v0.Platform" + headers: + - name: ":method" + safe_regex_match: + google_re2: {} + regex: "GET|POST|OPTIONS" + route: + cluster: core_and_platform + # health-check endpoint configuration + - match: + prefix: "/grpc.health.v1.Health" + headers: + - name: ":method" + safe_regex_match: + google_re2: {} + regex: "GET|POST|OPTIONS" + route: + cluster: tx_filter_stream + # configuration of the static responses of unsupported api versions + # core static response + - match: + safe_regex: + google_re2: {} + regex: "\/org\\.dash\\.platform\\.dapi\\.v[1-9]+\\.Core" + response_headers_to_add: + - header: + key: "Content-Type" + value: "application/grpc-web+proto" + - header: + key: "grpc-status" + value: "12" + - header: + key: "grpc-message" + value: "Specified service version is not supported" + direct_response: + status: 204 + # platform static respose + - match: + safe_regex: + google_re2: {} + regex: "\/org\\.dash\\.platform\\.dapi\\.v[1-9]+\\.Platform" + response_headers_to_add: + - header: + key: "Content-Type" + value: "application/grpc-web+proto" + - header: + key: "grpc-status" + value: "12" + - header: + key: "grpc-message" + value: "Specified service version is not supported" + direct_response: + status: 204 + # root endpoint configuration + - match: + prefix: "/" + headers: + - name: ":method" + safe_regex_match: + google_re2: {} + regex: "GET|POST|OPTIONS" + route: + cluster: dapi_api_srv + # cors configuration + cors: + allow_origin_string_match: + - prefix: "*" + allow_methods: GET,POST,OPTIONS + allow_headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Transfer-Encoding,Custom-Header-1,X-Accept-Content-Transfer-Encoding,X-Accept-Response-Streaming,X-User-Agent,X-Grpc-Web + expose_headers: Content-Transfer-Encoding,Grpc-Message,Grpc-Status,Custom-Header-1 + max_age: "1728000" + - name: native_grpc_gateway + address: + socket_address: + address: 0.0.0.0 + port_value: 50051 + filter_chains: + - filters: + - name: envoy.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + codec_type: HTTP2 + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/stdout + log_format: + json_format: + timestamp: "%START_TIME%" + client: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%" + uri: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%" + upstream: "%UPSTREAM_HOST%" + "http-status": "%RESPONSE_CODE%" + "grpc-status": "%GRPC_STATUS%" + "rx-bytes": "%BYTES_RECEIVED%" + "tx-bytes": "%BYTES_SENT%" + http_filters: + - name: envoy.filters.http.local_ratelimit + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit + stat_prefix: http_local_rate_limiter + token_bucket: + max_tokens: {{=it.platform.dapi.envoy.rateLimiter.maxTokens}} + tokens_per_fill: {{=it.platform.dapi.envoy.rateLimiter.tokensPerFill}} + fill_interval: {{=it.platform.dapi.envoy.rateLimiter.fillInterval}} + filter_enabled: + runtime_key: local_rate_limit_enabled + default_value: + numerator: {{? it.platform.dapi.envoy.rateLimiter.enabled}}100{{??}}0{{?}} + denominator: HUNDRED + filter_enforced: + runtime_key: local_rate_limit_enforced + default_value: + numerator: 100 + denominator: HUNDRED + response_headers_to_add: + - append: false + header: + key: x-local-rate-limit + value: 'true' + - name: envoy.filters.http.lua + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua + inline_code: | + function envoy_on_request(request_handle) + local max_bytes = 128 * 1024 + local body_size = request_handle:body():length() + if body_size > max_bytes then + request_handle:respond({[":status"] = "413"}) + end + end + - name: envoy.filters.http.router + route_config: + name: local_route + virtual_hosts: + - name: dapi_services + domains: ["*"] + routes: + # tx subscription endpoint configuration + - match: + prefix: "/org.dash.platform.dapi.v0.Core/subscribeToTransactionsWithProofs" + route: + cluster: tx_filter_stream + timeout: 0s + # block headers subscription endpoint configuration + - match: + prefix: "/org.dash.platform.dapi.v0.Core/subscribeToBlockHeadersWithChainLocks" + route: + cluster: tx_filter_stream + timeout: 0s + - match: + prefix: "/org.dash.platform.dapi.v0.Core" + route: + cluster: core_and_platform + - match: + prefix: "/org.dash.platform.dapi.v0.Platform" + route: + cluster: core_and_platform + - match: + prefix: "/grpc.health.v1.Health" + route: + cluster: tx_filter_stream + # configuration of the static responses of unsupported api versions + # core static response + - match: + safe_regex: + google_re2: {} + regex: "\/org\\.dash\\.platform\\.dapi\\.v[1-9]+\\.Core" + response_headers_to_add: + - header: + key: "Content-Type" + value: "application/grpc-web+proto" + - header: + key: "grpc-status" + value: "12" + - header: + key: "grpc-message" + value: "Specified service version is not supported" + direct_response: + status: 204 + # platform static respose + - match: + safe_regex: + google_re2: {} + regex: "\/org\\.dash\\.platform\\.dapi\\.v[1-9]+\\.Platform" + response_headers_to_add: + - header: + key: "Content-Type" + value: "application/grpc-web+proto" + - header: + key: "grpc-status" + value: "12" + - header: + key: "grpc-message" + value: "Specified service version is not supported" + direct_response: + status: 204 + clusters: + - name: core_and_platform + connect_timeout: 0.25s + type: logical_dns + http2_protocol_options: {} + lb_policy: round_robin + load_assignment: + cluster_name: core_and_platform + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: dapi_api + port_value: 3005 + - name: tx_filter_stream + connect_timeout: 0.25s + type: logical_dns + http2_protocol_options: {} + lb_policy: round_robin + load_assignment: + cluster_name: core_and_platform + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: dapi_tx_filter_stream + port_value: 3006 + - name: dapi_api_srv + connect_timeout: 30s + type: logical_dns + dns_lookup_family: V4_ONLY + lb_policy: round_robin + load_assignment: + cluster_name: core_and_platform + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: dapi_api + port_value: 3004 + +admin: + access_log_path: "/dev/null" + address: + socket_address: + address: 0.0.0.0 + port_value: 8081 diff --git a/packages/dashmate/templates/platform/drive/tenderdash/config.toml.dot b/packages/dashmate/templates/platform/drive/tenderdash/config.toml.dot new file mode 100644 index 00000000000..f46a05ce745 --- /dev/null +++ b/packages/dashmate/templates/platform/drive/tenderdash/config.toml.dot @@ -0,0 +1,414 @@ +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +# NOTE: Any path below can be absolute (e.g. "/var/myawesomeapp/data") or +# relative to the home directory (e.g. "data"). The home directory is +# "$HOME/.tendermint" by default, but could be changed via $TMHOME env variable +# or --home cmd flag. + +####################################################################### +### Main Base Config Options ### +####################################################################### + +# TCP or UNIX socket address of the ABCI application, +# or the name of an ABCI application compiled in with the Tendermint binary +proxy-app = "tcp://drive_abci:26658" + +# A custom human readable name for this node +#moniker = "" + +# Database backend: goleveldb | cleveldb | boltdb | rocksdb | badgerdb +# * goleveldb (github.com/syndtr/goleveldb - most popular implementation) +# - pure go +# - stable +# * cleveldb (uses levigo wrapper) +# - fast +# - requires gcc +# - use cleveldb build tag (go build -tags cleveldb) +# * boltdb (uses etcd's fork of bolt - github.com/etcd-io/bbolt) +# - EXPERIMENTAL +# - may be faster is some use-cases (random reads - indexer) +# - use boltdb build tag (go build -tags boltdb) +# * rocksdb (uses github.com/tecbot/gorocksdb) +# - EXPERIMENTAL +# - requires gcc +# - use rocksdb build tag (go build -tags rocksdb) +# * badgerdb (uses github.com/dgraph-io/badger) +# - EXPERIMENTAL +# - use badgerdb build tag (go build -tags badgerdb) +db-backend = "goleveldb" + +# Database directory +db-dir = "data" + +# Output level for logging, including package level options +# log-level = "{{~Object.keys(it.platform.drive.tenderdash.log.level) :module:index}}{{? index }},{{?}}{{=module}}:{{=it.platform.drive.tenderdash.log.level[module]}}{{~}}" +log-level = "debug" + +# Output format: 'plain' (colored text) or 'json' +log-format = "{{=it.platform.drive.tenderdash.log.format}}" + +##### additional base config options ##### + +# Path to the JSON file containing the initial validator set and other meta data +genesis-file = "config/genesis.json" + +# Set to whether we are a masternode or not +mode = {{?it.core.masternode.enable}}"validator"{{??}}"full"{{?}} + +# Path to the JSON file containing the private key to use for node authentication in the p2p protocol +node-key-file = "config/node_key.json" + +# Mechanism to connect to the ABCI application: socket | grpc +abci = "socket" + +# If true, query the ABCI app on connecting to a new peer +# so the app can decide if we should keep the connection or not +filter-peers = false + +[priv-validator] + +# Path to the JSON file containing the private key to use as a validator in the consensus protocol +key-file = "data/priv_validator_key.json" + +# Path to the JSON file containing the last sign state of a validator +state-file = "data/priv_validator_state.json" + +# TCP or UNIX socket address for Tendermint to listen on for +# connections from an external PrivValidator process +laddr = "" + +# Local Dash Core Host to connect to +# If this is set, the node follows a Dash Core PrivValidator process +core-rpc-host = "core:{{= it.core.rpc.port}}" + +# Local Dash Core RPC Username +core-rpc-username = "{{= it.core.rpc.user}}" + +# Local Dash Core RPC Password +core-rpc-password = "{{= it.core.rpc.password}}" + +####################################################################### +### Advanced Configuration Options ### +####################################################################### + +####################################################### +### RPC Server Configuration Options ### +####################################################### +[rpc] + +# TCP or UNIX socket address for the RPC server to listen on +laddr = "tcp://0.0.0.0:{{=it.platform.drive.tenderdash.rpc.port}}" + +# A list of origins a cross-domain request can be executed from +# Default value '[]' disables cors support +# Use '["*"]' to allow any origin +cors-allowed-origins = [] + +# A list of methods the client is allowed to use with cross-domain requests +cors-allowed-methods = ["HEAD", "GET", "POST", ] + +# A list of non simple headers the client is allowed to use with cross-domain requests +cors-allowed-headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time", ] + +# TCP or UNIX socket address for the gRPC server to listen on +# NOTE: This server only supports /broadcast_tx_commit +grpc-laddr = "" + +# Maximum number of simultaneous connections. +# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +grpc-max-open-connections = 900 + +# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool +unsafe = false + +# Maximum number of simultaneous connections (including WebSocket). +# Does not include gRPC connections. See grpc_max_open_connections +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +max-open-connections = 900 + +# Maximum number of unique clientIDs that can /subscribe +# If you're using /broadcast_tx_commit, set to the estimated maximum number +# of broadcast_tx_commit calls per block. +max-subscription-clients = 100 + +# Maximum number of unique queries a given client can /subscribe to +# If you're using GRPC (or Local RPC client) and /broadcast_tx_commit, set to +# the estimated # maximum number of broadcast_tx_commit calls per block. +max-subscriptions-per-client = 5 + +# How long to wait for a tx to be committed during /broadcast_tx_commit. +# WARNING: Using a value larger than 10s will result in increasing the +# global HTTP write timeout, which applies to all connections and endpoints. +# See https://github.com/tendermint/tendermint/issues/3435 +timeout-broadcast-tx-commit = "10s" + +# Maximum size of request body, in bytes +max-body-bytes = 1000000 + +# Maximum size of request header, in bytes +max-header-bytes = 1048576 + +# The path to a file containing certificate that is used to create the HTTPS server. +# Migth be either absolute path or path related to tendermint's config directory. +# If the certificate is signed by a certificate authority, +# the certFile should be the concatenation of the server's certificate, any intermediates, +# and the CA's certificate. +# NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. +# Otherwise, HTTP server is run. +tls-cert-file = "" + +# The path to a file containing matching private key that is used to create the HTTPS server. +# Migth be either absolute path or path related to tendermint's config directory. +# NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. +# Otherwise, HTTP server is run. +tls-key-file = "" + +# pprof listen address (https://golang.org/pkg/net/http/pprof) +pprof-laddr = "" + +####################################################### +### P2P Configuration Options ### +####################################################### +[p2p] + +# Address to listen for incoming connections +laddr = "tcp://0.0.0.0:{{=it.platform.drive.tenderdash.p2p.port}}" + +# Address to advertise to peers for them to dial +# If empty, will use the same port as the laddr, +# and will introspect on the listener or use UPnP +# to figure out the address. +external-address = "{{? it.externalIp}}{{=it.externalIp}}:{{=it.platform.drive.tenderdash.p2p.port}}{{?}}" + +# Comma separated list of nodes to keep persistent connections to +persistent-peers = "{{~it.platform.drive.tenderdash.p2p.persistentPeers :peer:index}}{{? index }},{{?}}{{=peer.id}}@{{=peer.host}}:{{=peer.port}}{{~}}" + +bootstrap-peers = "{{~it.platform.drive.tenderdash.p2p.seeds :seed:index}}{{? index }},{{?}}{{=seed.id}}@{{=seed.host}}:{{=seed.port}}{{~}}" + +# UPNP port forwarding +upnp = false + +# Path to address book +addr-book-file = "data/addrbook.json" + +# Set true for strict address routability rules +# Set false for private or local networks +addr-book-strict = {{?it.network == 'local'}}false{{??}}true{{?}} + +# Maximum number of inbound peers +max-num-inbound-peers = 40 + +# Maximum number of outbound peers to connect to, excluding persistent peers +max-num-outbound-peers = 10 + +# List of node IDs, to which a connection will be (re)established ignoring any existing limits +unconditional-peer-ids = "{{?it.network == 'local'}}{{~it.platform.drive.tenderdash.p2p.persistentPeers :peer:index}}{{? index }},{{?}}{{=peer.id}}{{~}}{{?}}" + +# Maximum pause when redialing a persistent peer (if zero, exponential backoff is used) +persistent-peers-max-dial-period = "0s" + +# Time to wait before flushing messages out on the connection +flush-throttle-timeout = "100ms" + +# Maximum size of a message packet payload, in bytes +max-packet-msg-payload-size = 1024 + +# Rate at which packets can be sent, in bytes/second +send-rate = 5120000 + +# Rate at which packets can be received, in bytes/second +recv-rate = 5120000 + +# Set true to enable the peer-exchange reactor +pex = {{?it.network == 'local'}}false{{??}}true{{?}} + +# Seed mode, in which node constantly crawls the network and looks for +# peers. If another node asks it for addresses, it responds and disconnects. +# +# Does not work if the peer-exchange reactor is disabled. +seed-mode = false + +# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) +private-peer-ids = "" + +# Toggle to disable guard against peers connecting from the same ip. +allow-duplicate-ip = {{?it.network == 'local'}}true{{??}}false{{?}} + +# Peer connection configuration. +handshake-timeout = "20s" +dial-timeout = "3s" + +####################################################### +### Mempool Configurattion Option ### +####################################################### +[mempool] + +recheck = true +broadcast = true +wal-dir = "" + +# Maximum number of transactions in the mempool +size = 5000 + +# Limit the total size of all txs in the mempool. +# This only accounts for raw transactions (e.g. given 1MB transactions and +# max_txs_bytes=5MB, mempool will only accept 5 transactions). +max-txs-bytes = 1073741824 + +# Size of the cache (used to filter transactions we saw earlier) in transactions +cache-size = 10000 + +# Do not remove invalid transactions from the cache (default: false) +# Set to true if it's not possible for any invalid transaction to become valid +# again in the future. +keep-invalid-txs-in-cache = true + +# Maximum size of a single transaction. +# NOTE: the max size of a tx transmitted over the network is {max_tx_bytes}. +max-tx-bytes = 1048576 + +# Maximum size of a batch of transactions to send to a peer +# Including space needed by encoding (one varint per transaction). +max-batch-bytes = 10485760 + +####################################################### +### State Sync Configuration Options ### +####################################################### +[statesync] +# State sync rapidly bootstraps a new node by discovering, fetching, and restoring a state machine +# snapshot from peers instead of fetching and replaying historical blocks. Requires some peers in +# the network to take and serve state machine snapshots. State sync is not attempted if the node +# has any local state (LastBlockHeight > 0). The node will have a truncated block history, +# starting from the height of the snapshot. +enable = false + +# RPC servers (comma-separated) for light client verification of the synced state machine and +# retrieval of state data for node bootstrapping. Also needs a trusted height and corresponding +# header hash obtained from a trusted source, and a period during which validators can be trusted. +# +# For Cosmos SDK-based chains, trust_period should usually be about 2/3 of the unbonding time (~2 +# weeks) during which they can be financially punished (slashed) for misbehavior. +rpc-servers = "" +trust-height = 0 +trust-hash = "" +trust-period = "168h0m0s" + +# Time to spend discovering snapshots before initiating a restore. +discovery-time = "15s" + +# Temporary directory for state sync snapshot chunks, defaults to the OS tempdir (typically /tmp). +# Will create a new, randomly named directory within, and remove it when done. +temp-dir = "" + +####################################################### +### Fast Sync Configuration Connections ### +####################################################### +[blocksync] + +# Fast Sync version to use: +# 1) "v0" (default) - the legacy fast sync implementation +# 2) "v1" - refactor of v0 version for better testability +# 2) "v2" - complete redesign of v0, optimized for testability & readability +version = "v0" + +# If this node is many blocks behind the tip of the chain, FastSync +# allows them to catchup quickly by downloading blocks in parallel +# and verifying their commits +enable = true + +####################################################### +### Consensus Configuration Options ### +####################################################### +[consensus] + +wal-file = "data/cs.wal/wal" + +# How many blocks are inspected in order to determine if we need to create an additional proof block. +create-proof-block-range = 1 +# How long we wait for a proposal block before prevoting nil +timeout-propose = "3s" +# How much timeout_propose increases with each round +timeout-propose-delta = "500ms" +# How long we wait after receiving +2/3 prevotes for “anything” (ie. not a single block or nil) +timeout-prevote = "1s" +# How much the timeout-prevote increases with each round +timeout-prevote-delta = "500ms" +# How long we wait after receiving +2/3 precommits for “anything” (ie. not a single block or nil) +timeout-precommit = "1s" +# How much the timeout_precommit increases with each round +timeout-precommit-delta = "500ms" +# How long we wait after committing a block, before starting on the new +# height (this gives us a chance to receive some more precommits, even +# though we already have +2/3). +timeout-commit = "1s" + +# How many blocks to look back to check existence of the node's consensus votes before joining consensus +# When non-zero, the node will panic upon restart +# if the same consensus key was used to sign {double_sign_check_height} last blocks. +# So, validators should stop the state machine, wait for some blocks, and then restart the state machine to avoid panic. +double-sign-check-height = 0 + +# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) +skip-timeout-commit = false + +# EmptyBlocks mode and possible interval between empty blocks +create-empty-blocks = {{? it.platform.drive.tenderdash.consensus.createEmptyBlocks }}true{{??}}false{{?}} +create-empty-blocks-interval = "{{= it.platform.drive.tenderdash.consensus.createEmptyBlocksInterval }}" + +# Set the Dash Core quorum type +quorum-type = {{=it.platform.drive.abci.validatorSet.llmqType}} + +# Set the App hash size +app-hash-size = 32 + +# Reactor sleep duration parameters +peer-gossip-sleep-duration = "100ms" +peer-query-maj23-sleep-duration = "2s" + +####################################################### +### Transaction Indexer Configuration Options ### +####################################################### +[tx-index] + +# What indexer to use for transactions +# +# The application will set which txs to index. In some cases a node operator will be able +# to decide which txs to index based on configuration set in the application. +# +# Options: +# 1) "null" +# 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). +# - When "kv" is chosen "tx.height" and "tx.hash" will always be indexed. +indexer = "kv" + +####################################################### +### Instrumentation Configuration Options ### +####################################################### +[instrumentation] + +# When true, Prometheus metrics are served under /metrics on +# PrometheusListenAddr. +# Check out the documentation for the list of available metrics. +prometheus = false + +# Address to listen for Prometheus collector(s) connections +prometheus-listen-addr = ":26660" + +# Maximum number of simultaneous connections. +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +max-open-connections = 3 + +# Instrumentation namespace +namespace = "drive_tendermint" diff --git a/packages/dashmate/templates/platform/drive/tenderdash/genesis.json.dot b/packages/dashmate/templates/platform/drive/tenderdash/genesis.json.dot new file mode 100644 index 00000000000..dc096b69cad --- /dev/null +++ b/packages/dashmate/templates/platform/drive/tenderdash/genesis.json.dot @@ -0,0 +1 @@ +{{=JSON.stringify(it.platform.drive.tenderdash.genesis,null,2)}} diff --git a/packages/dashmate/templates/platform/drive/tenderdash/node_key.json.dot b/packages/dashmate/templates/platform/drive/tenderdash/node_key.json.dot new file mode 100644 index 00000000000..4a1d1bfe86b --- /dev/null +++ b/packages/dashmate/templates/platform/drive/tenderdash/node_key.json.dot @@ -0,0 +1 @@ +{{=JSON.stringify(it.platform.drive.tenderdash.nodeKey,null,2)}} diff --git a/packages/dashpay-contract/.eslintrc b/packages/dashpay-contract/.eslintrc new file mode 100644 index 00000000000..e2c337296e8 --- /dev/null +++ b/packages/dashpay-contract/.eslintrc @@ -0,0 +1,22 @@ +{ + "extends": "airbnb-base", + "rules": { + "no-plusplus": 0, + "eol-last": [ + "error", + "always" + ], + "class-methods-use-this": "off", + "curly": [ + "error", + "all" + ] + }, + "env": { + "node": true, + "mocha": true + }, + "globals": { + "expect": true + } +} diff --git a/packages/dashpay-contract/.mocharc.yml b/packages/dashpay-contract/.mocharc.yml new file mode 100644 index 00000000000..1f6e57d579e --- /dev/null +++ b/packages/dashpay-contract/.mocharc.yml @@ -0,0 +1,3 @@ +file: + - lib/test/bootstrap.js +recursive: true diff --git a/packages/dashpay-contract/CHANGELOG.md b/packages/dashpay-contract/CHANGELOG.md new file mode 100644 index 00000000000..04958c51308 --- /dev/null +++ b/packages/dashpay-contract/CHANGELOG.md @@ -0,0 +1,18 @@ +# [0.4.0](https://github.com/dashevo/dashpay-contract/compare/v0.3.0...v0.4.0) (2021-08-17) + + +### Features + +* update contract to work with DPP 0.20 ([#12](https://github.com/dashevo/dashpay-contract/issues/12)) + + + +# [0.2.0](https://github.com/dashevo/dashpay-contract/compare/v0.1.0...v0.2.0) (2021-02-02) + + +### Features + +* update contract ([#7](https://github.com/dashevo/dashpay-contract/issues/7)) + + + diff --git a/packages/dashpay-contract/LICENSE b/packages/dashpay-contract/LICENSE new file mode 100644 index 00000000000..42f3aa0e9c6 --- /dev/null +++ b/packages/dashpay-contract/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019-2020 Dash Core Group, Inc. + +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/packages/dashpay-contract/README.md b/packages/dashpay-contract/README.md new file mode 100644 index 00000000000..96e68c6afee --- /dev/null +++ b/packages/dashpay-contract/README.md @@ -0,0 +1,68 @@ +# DashPay Contract + +[![NPM Version](https://img.shields.io/npm/v/@dashevo/dashpay-contract)](https://www.npmjs.com/package/@dashevo/dashpay-contract) +[![Build Status](https://github.com/dashevo/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashevo/platform/actions/workflows/release.yml) +[![Release Date](https://img.shields.io/github/release-date/dashevo/platform)](https://github.com/dashevo/platform/releases/latest) +[![license](https://img.shields.io/github/license/dashevo/dashpay-contract.svg)](LICENSE) +[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen)](https://github.com/RichardLitt/standard-readme) + +Reference contract of the DashPay Application Contract + +DashPay allows Dash user to create a DashPay profile on the Platform chain. +This profile can be used to interact with other DashPay profiles, may it be for the purpose of sending/receiving transactions without having to go around the process of querying the public address of the recipient. + +## Table of Contents +- [Install](#install) +- [Usage](#usage) +- [API](#api) +- [Contributing](#contributing) +- [License](#license) + +## Install + +Ensure you have the latest [NodeJS](https://nodejs.org/en/download/) installed. + +#### From repository + +Clone the repo: + +```shell +git clone https://github.com/dashevo/dashpay-contract +``` + +Install npm packages: + +```shell +npm install +``` + +#### From NPM + +```sh +npm install @dashevo/dashpay-contract +``` + +## Usage + + +#### Publish the contract + +```shell +npm run publish-contract +``` + +#### Running the tests + +To run tests, simply run + +```shell +npm test +``` + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs. + +## License + +[MIT](LICENSE) © Dash Core Group, Inc. diff --git a/packages/dashpay-contract/lib/systemIds.js b/packages/dashpay-contract/lib/systemIds.js new file mode 100644 index 00000000000..1ebde45f137 --- /dev/null +++ b/packages/dashpay-contract/lib/systemIds.js @@ -0,0 +1,4 @@ +module.exports = { + ownerId: '5PhRFRrWZc5Mj8NqtpHNXCmmEQkcZE8akyDkKhsUVD4k', + contractId: 'Bwr4WHCPz5rFVAD87RqTs3izo4zpzwsEdKPWUT1NS1C7', +}; diff --git a/packages/dashpay-contract/lib/test/.eslintrc b/packages/dashpay-contract/lib/test/.eslintrc new file mode 100644 index 00000000000..720ced73852 --- /dev/null +++ b/packages/dashpay-contract/lib/test/.eslintrc @@ -0,0 +1,12 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + }, + "globals": { + "expect": true + } +} diff --git a/packages/dashpay-contract/lib/test/bootstrap.js b/packages/dashpay-contract/lib/test/bootstrap.js new file mode 100644 index 00000000000..461846fa5e7 --- /dev/null +++ b/packages/dashpay-contract/lib/test/bootstrap.js @@ -0,0 +1,22 @@ +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); + +const { expect, use } = require('chai'); +const dirtyChai = require('dirty-chai'); + +use(dirtyChai); +use(sinonChai); + +beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + +afterEach(function afterEach() { + this.sinon.restore(); +}); + +global.expect = expect; diff --git a/packages/dashpay-contract/package.json b/packages/dashpay-contract/package.json new file mode 100644 index 00000000000..a11eb595a02 --- /dev/null +++ b/packages/dashpay-contract/package.json @@ -0,0 +1,38 @@ +{ + "name": "@dashevo/dashpay-contract", + "version": "0.23.0-dev.4", + "description": "Reference contract of the DashPay DPA on Dash Evolution", + "scripts": { + "lint": "eslint .", + "test": "yarn run test:unit", + "test:unit": "mocha 'test/unit/*.spec.js'" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/dashevo/dashpay-contract.git" + }, + "author": "Dash Core Team", + "contributors": [ + { + "name": "Alex Werner", + "email": "alex@dash.org", + "url": "https://github.com/alex-werner" + } + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/dashevo/dashpay-contract/issues" + }, + "homepage": "https://github.com/dashevo/dashpay-contract#readme", + "devDependencies": { + "@dashevo/dpp": "workspace:~", + "chai": "^4.3.4", + "dirty-chai": "^2.0.1", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2", + "mocha": "^9.1.2", + "sinon": "^11.1.2", + "sinon-chai": "^3.7.0" + } +} diff --git a/packages/dashpay-contract/schema/dashpay.schema.json b/packages/dashpay-contract/schema/dashpay.schema.json new file mode 100644 index 00000000000..85b681551da --- /dev/null +++ b/packages/dashpay-contract/schema/dashpay.schema.json @@ -0,0 +1,229 @@ +{ + "profile": { + "type": "object", + "indices": [ + { + "name": "ownerId", + "properties": [ + { + "$ownerId": "asc" + } + ], + "unique": true + }, + { + "name": "ownerIdAndUpdatedAt", + "properties": [ + { + "$ownerId": "asc" + }, + { + "$updatedAt": "asc" + } + ] + } + ], + "properties": { + "avatarUrl": { + "type": "string", + "format": "url", + "maxLength": 2048 + }, + "avatarHash": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "description": "SHA256 hash of the bytes of the image specified by avatarUrl" + }, + "avatarFingerprint": { + "type": "array", + "byteArray": true, + "minItems": 8, + "maxItems": 8, + "description": "dHash the image specified by avatarUrl" + }, + "publicMessage": { + "type": "string", + "maxLength": 140 + }, + "displayName": { + "type": "string", + "maxLength": 25 + } + }, + "required": [ + "$createdAt", + "$updatedAt" + ], + "additionalProperties": false + }, + "contactInfo": { + "type": "object", + "indices": [ + { + "name": "ownerIdAndKeys", + "properties": [ + { + "$ownerId": "asc" + }, + { + "rootEncryptionKeyIndex": "asc" + }, + { + "derivationEncryptionKeyIndex": "asc" + } + ], + "unique": true + }, + { + "name": "ownerIdAndUpdatedAt", + "properties": [ + { + "$ownerId": "asc" + }, + { + "$updatedAt": "asc" + } + ] + } + ], + "properties": { + "encToUserId": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32 + }, + "rootEncryptionKeyIndex": { + "type": "integer", + "minimum": 0 + }, + "derivationEncryptionKeyIndex": { + "type": "integer", + "minimum": 0 + }, + "privateData": { + "type": "array", + "byteArray": true, + "minItems": 48, + "maxItems": 2048, + "description": "This is the encrypted values of aliasName + note + displayHidden encoded as an array in cbor" + } + }, + "required": [ + "$createdAt", + "$updatedAt", + "encToUserId", + "privateData", + "rootEncryptionKeyIndex", + "derivationEncryptionKeyIndex" + ], + "additionalProperties": false + }, + "contactRequest": { + "type": "object", + "indices": [ + { + "name": "ownerIdUserIdAndAccountRef", + "properties": [ + { + "$ownerId": "asc" + }, + { + "toUserId": "asc" + }, + { + "accountReference": "asc" + } + ], + "unique": true + }, + { + "name": "ownerIdUserId", + "properties": [ + { + "$ownerId": "asc" + }, + { + "toUserId": "asc" + } + ] + }, + { + "name": "userIdCreatedAt", + "properties": [ + { + "toUserId": "asc" + }, + { + "$createdAt": "asc" + } + ] + }, + { + "name": "ownerIdCreatedAt", + "properties": [ + { + "$ownerId": "asc" + }, + { + "$createdAt": "asc" + } + ] + } + ], + "properties": { + "toUserId": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "encryptedPublicKey": { + "type": "array", + "byteArray": true, + "minItems": 96, + "maxItems": 96 + }, + "senderKeyIndex": { + "type": "integer", + "minimum": 0 + }, + "recipientKeyIndex": { + "type": "integer", + "minimum": 0 + }, + "accountReference": { + "type": "integer", + "minimum": 0 + }, + "encryptedAccountLabel": { + "type": "array", + "byteArray": true, + "minItems": 48, + "maxItems": 80 + }, + "autoAcceptProof": { + "type": "array", + "byteArray": true, + "minItems": 38, + "maxItems": 102 + }, + "coreHeightCreatedAt": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "$createdAt", + "toUserId", + "encryptedPublicKey", + "senderKeyIndex", + "recipientKeyIndex", + "accountReference" + ], + "additionalProperties": false + } +} diff --git a/packages/dashpay-contract/test/.eslintrc b/packages/dashpay-contract/test/.eslintrc new file mode 100644 index 00000000000..720ced73852 --- /dev/null +++ b/packages/dashpay-contract/test/.eslintrc @@ -0,0 +1,12 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + }, + "globals": { + "expect": true + } +} diff --git a/packages/dashpay-contract/test/unit/schema.spec.js b/packages/dashpay-contract/test/unit/schema.spec.js new file mode 100644 index 00000000000..92ef8114031 --- /dev/null +++ b/packages/dashpay-contract/test/unit/schema.spec.js @@ -0,0 +1,754 @@ +const { expect } = require('chai'); +const DashPlatformProtocol = require('@dashevo/dpp'); +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); +const schema = require('../../schema/dashpay.schema.json'); + +const whitepaperMasternodeText = 'Full nodes are servers running on a P2P network that allow peers to use them to receive updates about the events on the network. These nodes utilize significant amounts of traffic and other resources that incur a substantial cost. As a result, a steady decrease in the amount of these nodes has been observed for some time on the Bitcoin network and as a result, block propagation times have been upwards of 40 seconds. Many solutions have been proposed such as a new reward scheme by Microsoft Research and the Bitnodes incentive program'; +const encoded32Chars = '4fafc98bbfe597f7ba2c9f767d52036d'; +const encoded64Chars = '4fafc98bbfe597f7ba2c9f767d52036d2226175960a908e355e5c575711eb166'; + +describe('Dashpay Contract', () => { + let dpp; + let contract; + let identityId; + + beforeEach(async function beforeEach() { + const fetchContractStub = this.sinon.stub(); + + dpp = new DashPlatformProtocol({ + stateRepository: { + fetchDataContract: fetchContractStub, + }, + }); + + await dpp.initialize(); + + identityId = generateRandomIdentifier(); + + contract = dpp.dataContract.create(identityId, schema); + + fetchContractStub.resolves(contract); + }); + + it('should have a valid contract definition', async function shouldHaveValidContract() { + this.timeout(5000); + + const validationResult = await dpp.dataContract.validate(contract); + + expect(validationResult.isValid()).to.be.true(); + }); + + describe('Documents', () => { + describe('Profile', () => { + let profileData; + + beforeEach(() => { + profileData = { + displayName: 'Bob', + publicMessage: 'Hello Dashpay!', + }; + }); + + describe('displayName', () => { + it('should have less than 25 chars length', async () => { + profileData.displayName = 'AliceAndBobAndCarolAndDanAndEveAndFrankAndIvanAndMikeAndWalterAndWendy'; + + try { + dpp.document.create(contract, identityId, 'profile', profileData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxLength'); + expect(error.instancePath).to.equal('/displayName'); + } + }); + }); + + describe('publicMessage', () => { + it('should have less than 256 chars length', async () => { + profileData.publicMessage = whitepaperMasternodeText; + + try { + dpp.document.create(contract, identityId, 'profile', profileData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxLength'); + expect(error.instancePath).to.equal('/publicMessage'); + } + }); + }); + + describe('avatarUrl', () => { + it('should not be empty', async () => { + profileData.avatarUrl = ''; + + try { + dpp.document.create(contract, identityId, 'profile', profileData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('format'); + expect(error.instancePath).to.equal('/avatarUrl'); + } + }); + + it('should have less than 2048 chars length', async () => { + profileData.avatarUrl = `https://github.com/dashpay/dash/wiki/Whitepaper?text=${encodeURI(whitepaperMasternodeText)}${encodeURI(whitepaperMasternodeText)}${encodeURI(whitepaperMasternodeText)}${encodeURI(whitepaperMasternodeText)}${encodeURI(whitepaperMasternodeText)}`; + + try { + dpp.document.create(contract, identityId, 'profile', profileData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxLength'); + expect(error.instancePath).to.equal('/avatarUrl'); + } + }); + + it('should be of type URL', async () => { + profileData.avatarUrl = 'notAUrl'; + + try { + dpp.document.create(contract, identityId, 'profile', profileData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('format'); + expect(error.instancePath).to.equal('/avatarUrl'); + } + }); + }); + + describe('avatarHash', () => { + it('should have minimum length of 32', async () => { + profileData.avatarHash = Buffer.alloc(0); + + try { + dpp.document.create(contract, identityId, 'profile', profileData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minItems'); + expect(error.instancePath).to.equal('/avatarHash'); + } + }); + + it('should have maximum length of 32', async () => { + profileData.avatarHash = Buffer.alloc(33); + + try { + dpp.document.create(contract, identityId, 'profile', profileData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxItems'); + expect(error.instancePath).to.equal('/avatarHash'); + } + }); + + it('should be of type array', async () => { + profileData.avatarHash = 'notAnArray'; + + try { + dpp.document.create(contract, identityId, 'profile', profileData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.instancePath).to.equal('/avatarHash'); + } + }); + }); + + describe('avatarFingerprint', () => { + it('should have minimum length of 8', async () => { + profileData.avatarFingerprint = Buffer.alloc(0); + + try { + dpp.document.create(contract, identityId, 'profile', profileData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minItems'); + expect(error.instancePath).to.equal('/avatarFingerprint'); + } + }); + + it('should have maximum length of 8', async () => { + profileData.avatarFingerprint = Buffer.alloc(33); + + try { + dpp.document.create(contract, identityId, 'profile', profileData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxItems'); + expect(error.instancePath).to.equal('/avatarFingerprint'); + } + }); + + it('should be of type array', async () => { + profileData.avatarFingerprint = 'notAnArray'; + + try { + dpp.document.create(contract, identityId, 'profile', profileData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.instancePath).to.equal('/avatarFingerprint'); + } + }); + }); + + it('should not have additional properties', async () => { + profileData.someOtherProperty = 42; + + try { + dpp.document.create(contract, identityId, 'profile', profileData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('additionalProperties'); + expect(error.params.additionalProperty).to.equal('someOtherProperty'); + } + }); + + it('should be valid', async () => { + const profile = dpp.document.create(contract, identityId, 'profile', profileData); + + const result = await dpp.document.validate(profile); + + expect(result.isValid()).to.be.true(); + }); + }); + + describe('Contact info', () => { + let contactInfoData; + + beforeEach(() => { + contactInfoData = { + encToUserId: Buffer.alloc(32), + privateData: Buffer.alloc(48), + rootEncryptionKeyIndex: 0, + derivationEncryptionKeyIndex: 0, + }; + }); + + describe('encToUserId', () => { + it('should be defined', async () => { + delete contactInfoData.encToUserId; + + try { + dpp.document.create(contract, identityId, 'contactInfo', contactInfoData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('encToUserId'); + } + }); + + it('should have exactly 32 chars length', async () => { + contactInfoData.encToUserId = Buffer.from(`${encoded64Chars}11`, 'hex'); + + try { + dpp.document.create(contract, identityId, 'contactInfo', contactInfoData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxItems'); + expect(error.instancePath).to.equal('/encToUserId'); + } + }); + + it('should have more or 32 chars length', async () => { + contactInfoData.encToUserId = Buffer.from(encoded32Chars, 'hex'); + + try { + dpp.document.create(contract, identityId, 'contactInfo', contactInfoData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minItems'); + expect(error.instancePath).to.equal('/encToUserId'); + } + }); + }); + + describe('rootEncryptionKeyIndex', () => { + it('should be defined', async () => { + delete contactInfoData.rootEncryptionKeyIndex; + + try { + dpp.document.create(contract, identityId, 'contactInfo', contactInfoData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('rootEncryptionKeyIndex'); + } + }); + + it('should not be less than 0', async () => { + contactInfoData.rootEncryptionKeyIndex = -1; + + try { + dpp.document.create(contract, identityId, 'contactInfo', contactInfoData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.instancePath).to.equal('/rootEncryptionKeyIndex'); + } + }); + }); + + describe('privateData', () => { + it('should be defined', async () => { + delete contactInfoData.privateData; + + try { + dpp.document.create(contract, identityId, 'contactInfo', contactInfoData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('privateData'); + } + }); + }); + + it('should not have additional properties', async () => { + contactInfoData.someOtherProperty = 42; + + try { + dpp.document.create(contract, identityId, 'contactInfo', contactInfoData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('additionalProperties'); + expect(error.params.additionalProperty).to.equal('someOtherProperty'); + } + }); + }); + + describe('Contact Request', () => { + let contactRequestData; + + beforeEach(() => { + contactRequestData = { + toUserId: Buffer.alloc(32), + encryptedPublicKey: Buffer.alloc(96), + senderKeyIndex: 0, + recipientKeyIndex: 0, + accountReference: 0, + }; + }); + + describe('toUserId', () => { + it('should be defined', async () => { + delete contactRequestData.toUserId; + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('toUserId'); + } + }); + }); + + describe('encryptedPublicKey', () => { + it('should be defined', async () => { + delete contactRequestData.encryptedPublicKey; + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('encryptedPublicKey'); + } + }); + }); + + describe('senderKeyIndex', () => { + it('should be defined', async () => { + delete contactRequestData.senderKeyIndex; + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('senderKeyIndex'); + } + }); + + it('should not be less than 0', async () => { + contactRequestData.senderKeyIndex = -1; + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.instancePath).to.equal('/senderKeyIndex'); + } + }); + }); + + describe('recipientKeyIndex', () => { + it('should be defined', async () => { + delete contactRequestData.recipientKeyIndex; + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('recipientKeyIndex'); + } + }); + + it('should not be less than 0', async () => { + contactRequestData.recipientKeyIndex = -1; + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.instancePath).to.equal('/recipientKeyIndex'); + } + }); + }); + + describe('encryptedAccountLabel', () => { + it('should have minimum length of 48', async () => { + contactRequestData.encryptedAccountLabel = Buffer.alloc(0); + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minItems'); + expect(error.instancePath).to.equal('/encryptedAccountLabel'); + } + }); + + it('should have maximum length of 80', async () => { + contactRequestData.encryptedAccountLabel = Buffer.alloc(82); + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxItems'); + expect(error.instancePath).to.equal('/encryptedAccountLabel'); + } + }); + + it('should be of type array', async () => { + contactRequestData.encryptedAccountLabel = 'notAnArray'; + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.instancePath).to.equal('/encryptedAccountLabel'); + } + }); + }); + + describe('autoAcceptProof', () => { + it('should have minimum length of 38', async () => { + contactRequestData.autoAcceptProof = Buffer.alloc(0); + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minItems'); + expect(error.instancePath).to.equal('/autoAcceptProof'); + } + }); + + it('should have maximum length of 102', async () => { + contactRequestData.autoAcceptProof = Buffer.alloc(104); + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxItems'); + expect(error.instancePath).to.equal('/autoAcceptProof'); + } + }); + + it('should be of type array', async () => { + contactRequestData.autoAcceptProof = 'notAnArray'; + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.instancePath).to.equal('/autoAcceptProof'); + } + }); + }); + + describe('accountReference', () => { + it('should be defined', async () => { + delete contactRequestData.accountReference; + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('accountReference'); + } + }); + + it('should not be less than 0', async () => { + contactRequestData.accountReference = -1; + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.instancePath).to.equal('/accountReference'); + } + }); + }); + + describe('coreHeightCreatedAt', () => { + it('should not be less than 1', async () => { + contactRequestData.coreHeightCreatedAt = -1; + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.instancePath).to.equal('/coreHeightCreatedAt'); + } + }); + }); + + it('should not have additional properties', async () => { + contactRequestData.someOtherProperty = 42; + + try { + dpp.document.create(contract, identityId, 'contactRequest', contactRequestData); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('additionalProperties'); + expect(error.params.additionalProperty).to.equal('someOtherProperty'); + } + }); + }); + }); +}); diff --git a/packages/dpns-contract/.eslintrc b/packages/dpns-contract/.eslintrc new file mode 100644 index 00000000000..eec7881491c --- /dev/null +++ b/packages/dpns-contract/.eslintrc @@ -0,0 +1,15 @@ +{ + "extends": "airbnb-base", + "rules": { + "no-plusplus": 0, + "eol-last": [ + "error", + "always" + ], + "class-methods-use-this": "off", + "curly": [ + "error", + "all" + ] + } +} \ No newline at end of file diff --git a/packages/dpns-contract/.mocharc.yml b/packages/dpns-contract/.mocharc.yml new file mode 100644 index 00000000000..96eed0105b5 --- /dev/null +++ b/packages/dpns-contract/.mocharc.yml @@ -0,0 +1,3 @@ +file: + - test/bootstrap.js +recursive: true diff --git a/packages/dpns-contract/LICENSE b/packages/dpns-contract/LICENSE new file mode 100644 index 00000000000..3be95833750 --- /dev/null +++ b/packages/dpns-contract/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2019 Dash Core Group, Inc. + +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/packages/dpns-contract/README.md b/packages/dpns-contract/README.md new file mode 100644 index 00000000000..d3bcac5a31d --- /dev/null +++ b/packages/dpns-contract/README.md @@ -0,0 +1,33 @@ +# DPNS Contract + +[![Build Status](https://github.com/dashevo/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashevo/platform/actions/workflows/release.yml) +[![NPM version](https://img.shields.io/npm/v/@dashevo/dpns-contract.svg?style=flat-square)](https://npmjs.org/package/@dashevo/dpns-contract) + +JSON Contracts for Dash Platform Name Service + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contributing](#contributing) +- [License](#license) + +## Install + +```sh +npm install @dashevo/dpns-contract +``` + +## Usage + +```sh +# TODO ... +``` + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs. + +## License + +[MIT](LICENSE) © Dash Core Group, Inc. diff --git a/packages/dpns-contract/lib/systemIds.js b/packages/dpns-contract/lib/systemIds.js new file mode 100644 index 00000000000..cc6db980c1b --- /dev/null +++ b/packages/dpns-contract/lib/systemIds.js @@ -0,0 +1,4 @@ +module.exports = { + ownerId: '4EfA9Jrvv3nnCFdSf7fad59851iiTRZ6Wcu6YVJ4iSeF', + contractId: 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', +}; diff --git a/packages/dpns-contract/package.json b/packages/dpns-contract/package.json new file mode 100644 index 00000000000..748c7510045 --- /dev/null +++ b/packages/dpns-contract/package.json @@ -0,0 +1,44 @@ +{ + "name": "@dashevo/dpns-contract", + "version": "0.23.0-dev.4", + "description": "A contract and helper scripts for DPNS DApp", + "scripts": { + "lint": "eslint .", + "test": "yarn run test:unit", + "test:unit": "mocha 'test/unit/**/*.spec.js'" + }, + "contributors": [ + { + "name": "Ivan Shumkov", + "email": "ivan@shumkov.ru", + "url": "https://github.com/shumkov" + }, + { + "name": "Djavid Gabibiyan", + "email": "djavid@dash.org", + "url": "https://github.com/jawid-h" + }, + { + "name": "Anton Suprunchuk", + "email": "anton.suprunchuk@dash.org", + "url": "https://github.com/antouhou" + }, + { + "name": "Konstantin Shuplenkov", + "email": "konstantin.shuplenkov@dash.org", + "url": "https://github.com/shuplenkov" + } + ], + "license": "MIT", + "devDependencies": { + "@dashevo/dpp": "workspace:~", + "chai": "^4.3.4", + "dirty-chai": "^2.0.1", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2", + "mocha": "^9.1.2", + "sinon": "^11.1.2", + "sinon-chai": "^3.7.0" + } +} diff --git a/packages/dpns-contract/schema/dpns-contract-documents.json b/packages/dpns-contract/schema/dpns-contract-documents.json new file mode 100644 index 00000000000..10446bbdaa7 --- /dev/null +++ b/packages/dpns-contract/schema/dpns-contract-documents.json @@ -0,0 +1,145 @@ +{ + "domain": { + "type": "object", + "indices": [ + { + "name": "parentNameAndLabel", + "properties": [ + { + "normalizedParentDomainName": "asc" + }, + { + "normalizedLabel": "asc" + } + ], + "unique": true + }, + { + "name": "dashIdentityId", + "properties": [ + { + "records.dashUniqueIdentityId": "asc" + } + ], + "unique": true + }, + { + "name": "dashAlias", + "properties": [ + { + "records.dashAliasIdentityId": "asc" + } + ] + } + ], + "properties": { + "label": { + "type": "string", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$", + "minLength": 3, + "maxLength": 63, + "description": "Domain label. e.g. 'Bob'." + }, + "normalizedLabel": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$", + "maxLength": 63, + "description": "Domain label in lowercase for case-insensitive uniqueness validation. e.g. 'bob'", + "$comment": "Must be equal to the label in lowercase. This property will be deprecated due to case insensitive indices" + }, + "normalizedParentDomainName": { + "type": "string", + "pattern": "^$|^[[a-z0-9][a-z0-9-\\.]{0,61}[a-z0-9]$", + "minLength": 0, + "maxLength": 63, + "description": "A full parent domain name in lowercase for case-insensitive uniqueness validation. e.g. 'dash'", + "$comment": "Must either be equal to an existing domain or empty to create a top level domain. Only the data contract owner can create top level domains." + }, + "preorderSalt": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "description": "Salt used in the preorder document" + }, + "records": { + "type": "object", + "properties": { + "dashUniqueIdentityId": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier", + "description": "Identity ID to be used to create the primary name the Identity", + "$comment": "Must be equal to the document owner" + }, + "dashAliasIdentityId": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier", + "description": "Identity ID to be used to create alias names for the Identity", + "$comment": "Must be equal to the document owner" + } + }, + "$comment": "Constraint with max and min properties ensure that only one identity record is used - either a `dashUniqueIdentityId` or a `dashAliasIdentityId`", + "minProperties": 1, + "maxProperties": 1, + "additionalProperties": false + }, + "subdomainRules": { + "type": "object", + "properties": { + "allowSubdomains": { + "type": "boolean", + "description": "This option defines who can create subdomains: true - anyone; false - only the domain owner", + "$comment": "Only the domain owner is allowed to create subdomains for non top-level domains" + } + }, + "description": "Subdomain rules allow domain owners to define rules for subdomains", + "additionalProperties": false, + "required": ["allowSubdomains"] + } + }, + "required": [ + "label", + "normalizedLabel", + "normalizedParentDomainName", + "preorderSalt", + "records", + "subdomainRules" + ], + "additionalProperties": false, + "$comment": "In order to register a domain you need to create a preorder. The preorder step is needed to prevent man-in-the-middle attacks. normalizedLabel + '.' + normalizedParentDomain must not be longer than 253 chars length as defined by RFC 1035. Domain documents are immutable: modification and deletion are restricted" + }, + "preorder": { + "type": "object", + "indices": [ + { + "name": "saltedHash", + "properties": [ + { + "saltedDomainHash": "asc" + } + ], + "unique": true + } + ], + "properties": { + "saltedDomainHash": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "description": "Double sha-256 of the concatenation of a 32 byte random salt and a normalized domain name" + } + }, + "required": [ + "saltedDomainHash" + ], + "additionalProperties": false, + "$comment": "Preorder documents are immutable: modification and deletion are restricted" + } +} diff --git a/packages/dpns-contract/test/.eslintrc b/packages/dpns-contract/test/.eslintrc new file mode 100644 index 00000000000..720ced73852 --- /dev/null +++ b/packages/dpns-contract/test/.eslintrc @@ -0,0 +1,12 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + }, + "globals": { + "expect": true + } +} diff --git a/packages/dpns-contract/test/bootstrap.js b/packages/dpns-contract/test/bootstrap.js new file mode 100644 index 00000000000..461846fa5e7 --- /dev/null +++ b/packages/dpns-contract/test/bootstrap.js @@ -0,0 +1,22 @@ +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); + +const { expect, use } = require('chai'); +const dirtyChai = require('dirty-chai'); + +use(dirtyChai); +use(sinonChai); + +beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + +afterEach(function afterEach() { + this.sinon.restore(); +}); + +global.expect = expect; diff --git a/packages/dpns-contract/test/unit/dpnsContract.spec.js b/packages/dpns-contract/test/unit/dpnsContract.spec.js new file mode 100644 index 00000000000..93c7b06113e --- /dev/null +++ b/packages/dpns-contract/test/unit/dpnsContract.spec.js @@ -0,0 +1,711 @@ +const crypto = require('crypto'); + +const DashPlatformProtocol = require('@dashevo/dpp'); + +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); + +const dpnsContractDocumentsSchema = require('../../schema/dpns-contract-documents.json'); + +describe('DPNS Contract', () => { + let dpp; + let dataContract; + let identityId; + + beforeEach(async function beforeEach() { + const fetchContractStub = this.sinon.stub(); + + dpp = new DashPlatformProtocol({ + stateRepository: { + fetchDataContract: fetchContractStub, + }, + }); + + await dpp.initialize(); + + identityId = generateRandomIdentifier(); + + dataContract = dpp.dataContract.create(identityId, dpnsContractDocumentsSchema); + + fetchContractStub.resolves(dataContract); + }); + + it('should have a valid contract definition', async function shouldHaveValidContract() { + this.timeout(5000); + + const validationResult = await dpp.dataContract.validate(dataContract); + + expect(validationResult.isValid()).to.be.true(); + }); + + describe('documents', () => { + describe('preorder', () => { + let rawPreorderDocument; + + beforeEach(() => { + rawPreorderDocument = { + saltedDomainHash: crypto.randomBytes(32), + }; + }); + + describe('saltedDomainHash', () => { + it('should be defined', async () => { + delete rawPreorderDocument.saltedDomainHash; + + try { + dpp.document.create(dataContract, identityId, 'preorder', rawPreorderDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('saltedDomainHash'); + } + }); + + it('should not be empty', async () => { + rawPreorderDocument.saltedDomainHash = Buffer.alloc(0); + + try { + dpp.document.create(dataContract, identityId, 'preorder', rawPreorderDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minItems'); + expect(error.instancePath).to.equal('/saltedDomainHash'); + } + }); + + it('should be not less than 32 bytes', async () => { + rawPreorderDocument.saltedDomainHash = crypto.randomBytes(10); + + try { + dpp.document.create(dataContract, identityId, 'preorder', rawPreorderDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minItems'); + expect(error.instancePath).to.equal('/saltedDomainHash'); + } + }); + + it('should be not longer than 32 bytes', async () => { + rawPreorderDocument.saltedDomainHash = crypto.randomBytes(40); + + try { + dpp.document.create(dataContract, identityId, 'preorder', rawPreorderDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxItems'); + expect(error.instancePath).to.equal('/saltedDomainHash'); + } + }); + }); + + it('should not have additional properties', async () => { + rawPreorderDocument.someOtherProperty = 42; + + try { + dpp.document.create(dataContract, identityId, 'preorder', rawPreorderDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('additionalProperties'); + expect(error.params.additionalProperty).to.equal('someOtherProperty'); + } + }); + + it('should be valid', async () => { + const preorder = dpp.document.create(dataContract, identityId, 'preorder', rawPreorderDocument); + + const result = await dpp.document.validate(preorder); + + expect(result.isValid()).to.be.true(); + }); + }); + + describe('domain', () => { + let rawDomainDocument; + + beforeEach(() => { + rawDomainDocument = { + label: 'Wallet', + normalizedLabel: 'wallet', + normalizedParentDomainName: 'dash', + preorderSalt: crypto.randomBytes(32), + records: { + dashUniqueIdentityId: generateRandomIdentifier(), + }, + subdomainRules: { + allowSubdomains: false, + }, + }; + }); + + describe('label', () => { + it('should be present', async () => { + delete rawDomainDocument.label; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('label'); + } + }); + + it('should follow pattern', async () => { + rawDomainDocument.label = 'invalid label'; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('pattern'); + expect(error.instancePath).to.equal('/label'); + } + }); + + it('should be longer than 3 chars', async () => { + rawDomainDocument.label = 'ab'; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minLength'); + expect(error.instancePath).to.equal('/label'); + } + }); + + it('should be less than 63 chars', async () => { + rawDomainDocument.label = 'a'.repeat(64); + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxLength'); + expect(error.instancePath).to.equal('/label'); + } + }); + }); + + describe('normalizedLabel', () => { + it('should be defined', async () => { + delete rawDomainDocument.normalizedLabel; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('normalizedLabel'); + } + }); + + it('should follow pattern', async () => { + rawDomainDocument.normalizedLabel = 'InValiD label'; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('pattern'); + expect(error.instancePath).to.equal('/normalizedLabel'); + } + }); + + it('should be less than 63 chars', async () => { + rawDomainDocument.normalizedLabel = 'a'.repeat(64); + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxLength'); + expect(error.instancePath).to.equal('/normalizedLabel'); + } + }); + }); + + describe('normalizedParentDomainName', () => { + it('should be defined', async () => { + delete rawDomainDocument.normalizedParentDomainName; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('normalizedParentDomainName'); + } + }); + + it('should be less than 190 chars', async () => { + rawDomainDocument.normalizedParentDomainName = 'a'.repeat(191); + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxLength'); + expect(error.instancePath).to.equal('/normalizedParentDomainName'); + } + }); + + it('should follow pattern', async () => { + rawDomainDocument.normalizedParentDomainName = '&'.repeat(50); + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('pattern'); + expect(error.instancePath).to.equal('/normalizedParentDomainName'); + } + }); + }); + + describe('preorderSalt', () => { + it('should be defined', async () => { + delete rawDomainDocument.preorderSalt; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('preorderSalt'); + } + }); + + it('should not be empty', async () => { + rawDomainDocument.preorderSalt = Buffer.alloc(0); + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minItems'); + expect(error.instancePath).to.equal('/preorderSalt'); + } + }); + + it('should be not less than 32 bytes', async () => { + rawDomainDocument.preorderSalt = crypto.randomBytes(10); + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minItems'); + expect(error.instancePath).to.equal('/preorderSalt'); + } + }); + + it('should be not longer than 32 bytes', async () => { + rawDomainDocument.preorderSalt = crypto.randomBytes(40); + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxItems'); + expect(error.instancePath).to.equal('/preorderSalt'); + } + }); + }); + + it('should not have additional properties', async () => { + rawDomainDocument.someOtherProperty = 42; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('additionalProperties'); + expect(error.params.additionalProperty).to.equal('someOtherProperty'); + } + }); + + it('should be valid', async () => { + const domain = dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + const result = await dpp.document.validate(domain); + + expect(result.isValid()).to.be.true(); + }); + + describe('Records', () => { + it('should be defined', async () => { + delete rawDomainDocument.records; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('records'); + } + }); + + it('should not be empty', async () => { + rawDomainDocument.records = {}; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minProperties'); + expect(error.instancePath).to.equal('/records'); + } + }); + + it('should not have additional properties', async () => { + rawDomainDocument.records = { + someOtherProperty: 42, + }; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('additionalProperties'); + expect(error.instancePath).to.equal('/records'); + expect(error.params.additionalProperty).to.equal('someOtherProperty'); + } + }); + + describe('Dash Identity', () => { + it('should have either `dashUniqueIdentityId` or `dashAliasIdentityId`', async () => { + rawDomainDocument.records = { + dashUniqueIdentityId: identityId, + dashAliasIdentityId: identityId, + }; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxProperties'); + expect(error.instancePath).to.equal('/records'); + } + }); + + describe('dashUniqueIdentityId', () => { + it('should no less than 32 bytes', async () => { + rawDomainDocument.records = { + dashUniqueIdentityId: crypto.randomBytes(30), + }; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minItems'); + expect(error.instancePath).to.equal('/records/dashUniqueIdentityId'); + } + }); + + it('should no more than 32 bytes', async () => { + rawDomainDocument.records = { + dashUniqueIdentityId: crypto.randomBytes(64), + }; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxItems'); + expect(error.instancePath).to.equal('/records/dashUniqueIdentityId'); + } + }); + }); + + describe('dashAliasIdentityId', () => { + it('should no less than 32 bytes', async () => { + rawDomainDocument.records = { + dashAliasIdentityId: crypto.randomBytes(30), + }; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minItems'); + expect(error.instancePath).to.equal('/records/dashAliasIdentityId'); + } + }); + + it('should no more than 32 bytes', async () => { + rawDomainDocument.records = { + dashAliasIdentityId: crypto.randomBytes(64), + }; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxItems'); + expect(error.instancePath).to.equal('/records/dashAliasIdentityId'); + } + }); + }); + }); + }); + + describe('subdomainRules', () => { + it('should be defined', async () => { + delete rawDomainDocument.subdomainRules; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('subdomainRules'); + } + }); + + it('should not have additional properties', async () => { + rawDomainDocument.subdomainRules.someOtherProperty = 42; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('additionalProperties'); + expect(error.instancePath).to.equal('/subdomainRules'); + } + }); + + describe('allowSubdomains', () => { + it('should be boolean', async () => { + rawDomainDocument.subdomainRules.allowSubdomains = 'data'; + + try { + dpp.document.create(dataContract, identityId, 'domain', rawDomainDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.instancePath).to.equal('/subdomainRules/allowSubdomains'); + } + }); + }); + }); + }); + }); +}); diff --git a/packages/feature-flags-contract/.eslintrc b/packages/feature-flags-contract/.eslintrc new file mode 100644 index 00000000000..eec7881491c --- /dev/null +++ b/packages/feature-flags-contract/.eslintrc @@ -0,0 +1,15 @@ +{ + "extends": "airbnb-base", + "rules": { + "no-plusplus": 0, + "eol-last": [ + "error", + "always" + ], + "class-methods-use-this": "off", + "curly": [ + "error", + "all" + ] + } +} \ No newline at end of file diff --git a/packages/feature-flags-contract/.mocharc.yml b/packages/feature-flags-contract/.mocharc.yml new file mode 100644 index 00000000000..96eed0105b5 --- /dev/null +++ b/packages/feature-flags-contract/.mocharc.yml @@ -0,0 +1,3 @@ +file: + - test/bootstrap.js +recursive: true diff --git a/packages/feature-flags-contract/CHANGELOG.md b/packages/feature-flags-contract/CHANGELOG.md new file mode 100644 index 00000000000..59bf64d9984 --- /dev/null +++ b/packages/feature-flags-contract/CHANGELOG.md @@ -0,0 +1,22 @@ +# [0.2.0](https://github.com/dashevo/feature-flags-contract/compare/v0.1.0...v0.2.0) (2021-07-14) + + +### Features + +* update schema to work with DPP 0.20 ([#9](https://github.com/dashevo/feature-flags-contract/issues/9), [88c450c](https://github.com/dashevo/feature-flags-contract/commit/88c450ca92c61811084f065b853c27c54e7945d9)) +* remove VERIFY_LLMQ_SIGS_WITH_CORE feature flag ([#10](https://github.com/dashevo/feature-flags-contract/issues/10)) + + +### BREAKING CHANGES + +* `VERIFY_LLMQ_SIGS_WITH_CORE` feature flag is removed + + + +# [0.1.0](https://github.com/dashevo/feature-flags-contract/compare/v0.1.0) (2021-05-03) + + +### Features + +* add feature flag types module ([#6](https://github.com/dashevo/feature-flags-contract/issues/6)) +* add `verifyLLMQSignaturesWithCore` feature flag ([#3](https://github.com/dashevo/feature-flags-contract/issues/3)) diff --git a/packages/feature-flags-contract/LICENSE b/packages/feature-flags-contract/LICENSE new file mode 100644 index 00000000000..3be95833750 --- /dev/null +++ b/packages/feature-flags-contract/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2019 Dash Core Group, Inc. + +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/packages/feature-flags-contract/README.md b/packages/feature-flags-contract/README.md new file mode 100644 index 00000000000..795dbe1c036 --- /dev/null +++ b/packages/feature-flags-contract/README.md @@ -0,0 +1,33 @@ +# Feature Flags Contract + +[![Build Status](https://github.com/dashevo/feature-flags-contract/actions/workflows/test_and_release.yml/badge.svg)](https://github.com/dashevo/feature-flags-contract/actions/workflows/test_and_release.yml) +[![NPM version](https://img.shields.io/npm/v/@dashevo/feature-flags-contract.svg?style=flat-square)](https://npmjs.org/package/@dashevo/feature-flags-contract) + +Data Contract to store Dash Platform feature flags + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contributing](#contributing) +- [License](#license) + +## Install + +```sh +npm install @dashevo/dpns-contract +``` + +## Usage + +```sh +# TODO ... +``` + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs. + +## License + +[MIT](LICENSE) © Dash Core Group, Inc. diff --git a/packages/feature-flags-contract/lib/featureFlagTypes.js b/packages/feature-flags-contract/lib/featureFlagTypes.js new file mode 100644 index 00000000000..1f82698c34e --- /dev/null +++ b/packages/feature-flags-contract/lib/featureFlagTypes.js @@ -0,0 +1,11 @@ +/** + * @classdesc FeatureFlagTypes interface definition + * + * @name FeatureFlagTypes + * @class + * + * @property {string} FeatureFlagTypes.UPDATE_CONSENSUS_PARAMS + */ +module.exports = { + UPDATE_CONSENSUS_PARAMS: 'updateConsensusParams', +}; diff --git a/packages/feature-flags-contract/lib/systemIds.js b/packages/feature-flags-contract/lib/systemIds.js new file mode 100644 index 00000000000..afe15e8b956 --- /dev/null +++ b/packages/feature-flags-contract/lib/systemIds.js @@ -0,0 +1,4 @@ +module.exports = { + ownerId: 'H9sjb2bHG8t7gq5SwNdqzMWG7KR6sf3CbziFzthCkDD6', + contractId: 'HY1keaRK5bcDmujNCQq5pxNyvAiHHpoHQgLN5ppiu4kh', +}; diff --git a/packages/feature-flags-contract/package.json b/packages/feature-flags-contract/package.json new file mode 100644 index 00000000000..99485e0a4ee --- /dev/null +++ b/packages/feature-flags-contract/package.json @@ -0,0 +1,45 @@ +{ + "name": "@dashevo/feature-flags-contract", + "version": "0.23.0-dev.4", + "description": "Data Contract to store Dash Platform feature flags", + "scripts": { + "build": "", + "lint": "eslint .", + "test": "yarn run test:unit", + "test:unit": "mocha 'test/unit/**/*.spec.js'" + }, + "contributors": [ + { + "name": "Ivan Shumkov", + "email": "ivan@shumkov.ru", + "url": "https://github.com/shumkov" + }, + { + "name": "Djavid Gabibiyan", + "email": "djavid@dash.org", + "url": "https://github.com/jawid-h" + }, + { + "name": "Anton Suprunchuk", + "email": "anton.suprunchuk@dash.org", + "url": "https://github.com/antouhou" + }, + { + "name": "Konstantin Shuplenkov", + "email": "konstantin.shuplenkov@dash.org", + "url": "https://github.com/shuplenkov" + } + ], + "license": "MIT", + "devDependencies": { + "@dashevo/dpp": "workspace:~", + "chai": "^4.3.4", + "dirty-chai": "^2.0.1", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2", + "mocha": "^9.1.2", + "sinon": "^11.1.2", + "sinon-chai": "^3.7.0" + } +} diff --git a/packages/feature-flags-contract/schema/feature-flags-documents.json b/packages/feature-flags-contract/schema/feature-flags-documents.json new file mode 100644 index 00000000000..eb909a6a454 --- /dev/null +++ b/packages/feature-flags-contract/schema/feature-flags-documents.json @@ -0,0 +1,92 @@ +{ + "updateConsensusParams": { + "description": "Updates Tenderdash consensus params", + "$comment": "It's better to use descending order for `enableAtHeight`, but it's not supported yet.", + "type": "object", + "indices": [ + { + "name": "enableAtHeight", + "properties": [ + { + "enableAtHeight": "asc" + } + ], + "unique": true + } + ], + "properties": { + "enableAtHeight": { + "description": "Block height on which params will be applied", + "type": "integer", + "minimum": 1 + }, + "block": { + "description": "Parameters limiting the size of a block and time between consecutive blocks", + "type": "object", + "properties": { + "maxBytes": { + "description": "Max size of a block, in bytes", + "type": "integer", + "minimum": 1 + }, + "maxGas": { + "description": "Max sum of GasWanted in a proposed block", + "type": "integer", + "minimum": 1 + } + }, + "minProperties": 1, + "additionalProperties": false + }, + "evidence": { + "description": "Parameters limiting the validity of evidence of byzantine behaviour", + "type": "object", + "properties": { + "maxAgeNumBlocks": { + "description": "Max age of evidence, in blocks", + "type": "integer", + "minimum": 1 + }, + "maxAgeDuration": { + "description": "Max age of evidence, in time", + "type": "object", + "properties": { + "seconds": { + "type": "integer", + "minimum": 1 + }, + "nanos": { + "type": "integer", + "minimum": 0 + } + }, + "additionalProperties": false, + "required": ["seconds", "nanos"] + }, + "maxBytes": { + "description": "Maximum size in bytes of total evidence allowed to be entered into a block", + "type": "integer", + "minimum": 1 + } + }, + "minProperties": 1, + "additionalProperties": false + }, + "version": { + "type": "object", + "properties": { + "appVersion": { + "description": "The ABCI application version", + "type": "integer", + "minimum": 1 + } + }, + "minProperties": 1, + "additionalProperties": false + } + }, + "minProperties": 3, + "additionalProperties": false, + "required": ["$createdAt", "enableAtHeight"] + } +} diff --git a/packages/feature-flags-contract/test/.eslintrc b/packages/feature-flags-contract/test/.eslintrc new file mode 100644 index 00000000000..720ced73852 --- /dev/null +++ b/packages/feature-flags-contract/test/.eslintrc @@ -0,0 +1,12 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + }, + "globals": { + "expect": true + } +} diff --git a/packages/feature-flags-contract/test/bootstrap.js b/packages/feature-flags-contract/test/bootstrap.js new file mode 100644 index 00000000000..461846fa5e7 --- /dev/null +++ b/packages/feature-flags-contract/test/bootstrap.js @@ -0,0 +1,22 @@ +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); + +const { expect, use } = require('chai'); +const dirtyChai = require('dirty-chai'); + +use(dirtyChai); +use(sinonChai); + +beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + +afterEach(function afterEach() { + this.sinon.restore(); +}); + +global.expect = expect; diff --git a/packages/feature-flags-contract/test/unit/featureFlagsContract.spec.js b/packages/feature-flags-contract/test/unit/featureFlagsContract.spec.js new file mode 100644 index 00000000000..04c65487769 --- /dev/null +++ b/packages/feature-flags-contract/test/unit/featureFlagsContract.spec.js @@ -0,0 +1,608 @@ +const DashPlatformProtocol = require('@dashevo/dpp'); + +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); + +const featureFlagsContractDocumentsSchema = require('../../schema/feature-flags-documents.json'); + +describe('Feature Flags contract', () => { + let dpp; + let dataContract; + let identityId; + + beforeEach(async function beforeEach() { + const fetchContractStub = this.sinon.stub(); + + dpp = new DashPlatformProtocol({ + stateRepository: { + fetchDataContract: fetchContractStub, + }, + }); + + await dpp.initialize(); + + identityId = generateRandomIdentifier(); + + dataContract = dpp.dataContract.create(identityId, featureFlagsContractDocumentsSchema); + + fetchContractStub.resolves(dataContract); + }); + + it('should have a valid contract definition', async function shouldHaveValidContract() { + this.timeout(5000); + + const validationResult = await dpp.dataContract.validate(dataContract); + + expect(validationResult.isValid()).to.be.true(); + }); + + describe('documents', () => { + describe('updateConsensusParams', () => { + let rawUpdateConsensusParamsDocument; + + beforeEach(() => { + rawUpdateConsensusParamsDocument = { + enableAtHeight: 42, + }; + }); + + it('should have at least three properties', () => { + rawUpdateConsensusParamsDocument = { + $createdAt: (new Date()).getTime(), + $updatedAt: (new Date()).getTime(), + }; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('enableAtHeight'); + } + }); + + it('should not have additional properties', async () => { + rawUpdateConsensusParamsDocument.someOtherProperty = 42; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('additionalProperties'); + expect(error.params.additionalProperty).to.equal('someOtherProperty'); + } + }); + + describe('enabledAtHeight', () => { + it('should be present', async () => { + delete rawUpdateConsensusParamsDocument.enableAtHeight; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('enableAtHeight'); + } + }); + + it('should be integer', () => { + rawUpdateConsensusParamsDocument.enableAtHeight = 'string'; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.params.type).to.equal('integer'); + } + }); + + it('should be at least 1', () => { + rawUpdateConsensusParamsDocument.enableAtHeight = 0; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.params.limit).to.equal(1); + } + }); + }); + + describe('block', () => { + beforeEach(() => { + rawUpdateConsensusParamsDocument.block = { + maxBytes: 42, + maxGas: 42, + }; + }); + + it('should have at least on property', async () => { + rawUpdateConsensusParamsDocument.block = {}; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minProperties'); + expect(error.params.limit).to.equal(1); + } + }); + + describe('maxBytes', () => { + it('should be an integer', async () => { + rawUpdateConsensusParamsDocument.block = { + maxBytes: 'string', + }; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.params.type).to.equal('integer'); + } + }); + + it('should be at least 1', async () => { + rawUpdateConsensusParamsDocument.block = { + maxBytes: 0, + }; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.params.limit).to.equal(1); + } + }); + }); + + describe('maxGas', () => { + it('should be an integer', async () => { + rawUpdateConsensusParamsDocument.block = { + maxGas: 'string', + }; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.params.type).to.equal('integer'); + } + }); + + it('should be at least 1', async () => { + rawUpdateConsensusParamsDocument.block = { + maxGas: 0, + }; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.params.limit).to.equal(1); + } + }); + }); + + it('should be valid', async () => { + const updateConsensusParams = dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + const result = await dpp.document.validate(updateConsensusParams); + + expect(result.isValid()).to.be.true(); + }); + }); + + describe('evidence', () => { + beforeEach(() => { + rawUpdateConsensusParamsDocument.evidence = { + maxAgeNumBlocks: 42, + maxBytes: 42, + maxAgeDuration: { + seconds: 42, + nanos: 42, + }, + }; + }); + + it('should have at least on property', async () => { + rawUpdateConsensusParamsDocument.evidence = {}; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minProperties'); + expect(error.params.limit).to.equal(1); + } + }); + + describe('maxBytes', () => { + it('should be an integer', async () => { + rawUpdateConsensusParamsDocument.evidence = { + maxBytes: 'string', + }; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.params.type).to.equal('integer'); + } + }); + + it('should be at least 1', async () => { + rawUpdateConsensusParamsDocument.evidence = { + maxBytes: 0, + }; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.params.limit).to.equal(1); + } + }); + }); + + describe('maxAgeNumBlocks', () => { + it('should be an integer', async () => { + rawUpdateConsensusParamsDocument.evidence = { + maxAgeNumBlocks: 'string', + }; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.params.type).to.equal('integer'); + } + }); + + it('should be at least 1', async () => { + rawUpdateConsensusParamsDocument.evidence = { + maxAgeNumBlocks: 0, + }; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.params.limit).to.equal(1); + } + }); + }); + + describe('maxAgeDuration', () => { + describe('seconds', () => { + it('should be present', async () => { + delete rawUpdateConsensusParamsDocument.evidence.maxAgeDuration.seconds; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('seconds'); + } + }); + + it('should be integer', () => { + rawUpdateConsensusParamsDocument.evidence.maxAgeDuration.seconds = 'string'; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.params.type).to.equal('integer'); + } + }); + + it('should be at least 1', () => { + rawUpdateConsensusParamsDocument.evidence.maxAgeDuration.seconds = 0; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.params.limit).to.equal(1); + } + }); + }); + + describe('nanos', () => { + it('should be present', async () => { + delete rawUpdateConsensusParamsDocument.evidence.maxAgeDuration.nanos; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('nanos'); + } + }); + + it('should be integer', () => { + rawUpdateConsensusParamsDocument.evidence.maxAgeDuration.nanos = 'string'; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.params.type).to.equal('integer'); + } + }); + + it('should be at least 0', () => { + rawUpdateConsensusParamsDocument.evidence.maxAgeDuration.nanos = -1; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.params.limit).to.equal(0); + } + }); + }); + }); + + it('should be valid', async () => { + const updateConsensusParams = dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + const result = await dpp.document.validate(updateConsensusParams); + + expect(result.isValid()).to.be.true(); + }); + }); + + describe('version', () => { + beforeEach(() => { + rawUpdateConsensusParamsDocument.version = { + appVersion: 42, + }; + }); + + it('should have at least on property', async () => { + rawUpdateConsensusParamsDocument.version = {}; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minProperties'); + expect(error.params.limit).to.equal(1); + } + }); + + describe('appVersion', () => { + it('should be an integer', async () => { + rawUpdateConsensusParamsDocument.version = { + appVersion: 'string', + }; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.params.type).to.equal('integer'); + } + }); + + it('should be at least 1', async () => { + rawUpdateConsensusParamsDocument.version = { + appVersion: 0, + }; + + try { + dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.params.limit).to.equal(1); + } + }); + }); + + it('should be valid', async () => { + const updateConsensusParams = dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + const result = await dpp.document.validate(updateConsensusParams); + + expect(result.isValid()).to.be.true(); + }); + }); + + it('should be valid', async () => { + const updateConsensusParams = dpp.document.create(dataContract, identityId, 'updateConsensusParams', rawUpdateConsensusParamsDocument); + + const result = await dpp.document.validate(updateConsensusParams); + + expect(result.isValid()).to.be.true(); + }); + }); + }); +}); diff --git a/packages/js-dapi-client/.eslintignore b/packages/js-dapi-client/.eslintignore new file mode 100644 index 00000000000..5772aab4a64 --- /dev/null +++ b/packages/js-dapi-client/.eslintignore @@ -0,0 +1,2 @@ +dist/ +.nyc_output/ diff --git a/packages/js-dapi-client/.eslintrc.yml b/packages/js-dapi-client/.eslintrc.yml new file mode 100644 index 00000000000..8a7b8f358c7 --- /dev/null +++ b/packages/js-dapi-client/.eslintrc.yml @@ -0,0 +1,13 @@ +extends: + - airbnb-base + - plugin:jsdoc/recommended +rules: + eol-last: + - error + - always + class-methods-use-this: off + curly: + - error + - all +plugins: + - jsdoc diff --git a/packages/js-dapi-client/.gitignore b/packages/js-dapi-client/.gitignore new file mode 100644 index 00000000000..849ddff3b7e --- /dev/null +++ b/packages/js-dapi-client/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/packages/js-dapi-client/.mocharc.yml b/packages/js-dapi-client/.mocharc.yml new file mode 100644 index 00000000000..8cf850c03a8 --- /dev/null +++ b/packages/js-dapi-client/.mocharc.yml @@ -0,0 +1,4 @@ +file: + - lib/test/bootstrap.js +recursive: true +timeout: 3000 diff --git a/packages/js-dapi-client/.npmignore b/packages/js-dapi-client/.npmignore new file mode 100644 index 00000000000..405c06eb5df --- /dev/null +++ b/packages/js-dapi-client/.npmignore @@ -0,0 +1,10 @@ +# Note that this file must be the same as gitignore, except for dist folde + +# do not track dependencies +node_modules + +# ignore generated code coverage output +.nyc_output + +# Ultra runner build cache +.ultra.cache.json diff --git a/packages/js-dapi-client/CHANGELOG.md b/packages/js-dapi-client/CHANGELOG.md new file mode 100644 index 00000000000..f2b2263203a --- /dev/null +++ b/packages/js-dapi-client/CHANGELOG.md @@ -0,0 +1,366 @@ +## [0.21.2](https://github.com/dashevo/dapi-client/compare/v0.21.1...v0.21.2) (2021-10-22) + + +### Features + +* DAPI addresses white list must not be applied in Regtest mode ([#289](https://github.com/dashevo/js-dapi-client/pull/289)) + + +## [0.21.1](https://github.com/dashevo/dapi-client/compare/v0.21.0...v0.21.1) (2021-10-21) + + +### Features + +* DAPI addresses white list ([#288](https://github.com/dashevo/dapi-client/issues/288)) + + + +# [0.21.0](https://github.com/dashevo/dapi-client/compare/v0.20.0...v0.21.0) (2021-10-12) + + +### Features + +* improve error handling ([#268](https://github.com/dashevo/dapi-client/issues/268)) +* convenient response errors ([#272](https://github.com/dashevo/dapi-client/issues/272), [#280](https://github.com/dashevo/dapi-client/issues/280), [#282](https://github.com/dashevo/dapi-client/issues/282), [#284](https://github.com/dashevo/dapi-client/issues/284), [#286](https://github.com/dashevo/dapi-client/issues/286)) +* implement getConsensusParams method ([#261](https://github.com/dashevo/dapi-client/issues/261)) +* support returning of a multiproof ([#257](https://github.com/dashevo/dapi-client/issues/257)) + + +### BREAKING CHANGES + +* `getBlockByHash` and `getBlockByHeight` will throw `NotFoundError` instead of `null` result + + + +## [0.20.1](https://github.com/dashevo/dapi-client/compare/v0.20.0...v0.20.1) (2021-05-26) + + +### Bug Fixes + +* crash when connecting to an older version of dapi ([#252](https://github.com/dashevo/dapi-client/issues/252), [#253](https://github.com/dashevo/dapi-client/issues/253)) + + + +# [0.20.0](https://github.com/dashevo/dapi-client/compare/v0.19.3...v0.20.0) (2021-07-09) + + +### Features + +* provide metadata in responses ([#243](https://github.com/dashevo/dapi-client/issues/243), [#246](https://github.com/dashevo/dapi-client/issues/246)) +* throw `NotFoundError` if data is not found ([#247](https://github.com/dashevo/dapi-client/issues/247)) + + +### BREAKING CHANGES + +* platform methods respond with response classes instead of plain data +* Core method `getTransaction` responds with response class instead of plain data +* `getTransaction`, `getDataContract` and `getIdentity` methods throw `NotFoundError` if no data was found + + + +## [0.19.3](https://github.com/dashevo/dapi-client/compare/v0.19.2...v0.19.3) (2021-05-26) + + +### Bug Fixes + +* update cbor to fix serialization of queries with buffers ([178943b](https://github.com/dashevo/dapi-client/commit/178943b465b30ed838cb03a5a10c6f939db92a39)) + + + +## [0.19.2](https://github.com/dashevo/dapi-client/compare/v0.19.1...v0.19.2) (2021-05-20) + + +### Chores + +* Update DPP to a 0.19.2 ([#237](https://github.com/dashevo/dapi-client/issues/237)) + + + +## [0.19.1](https://github.com/dashevo/dapi-client/compare/v0.19.0...v0.19.1) (2021-05-04) + + +### Bug Fixes + +* `getStatus` reponse was returning buffers as Base64 encoded strings ([#235](https://github.com/dashevo/dapi-client/issues/235)) + + + +# [0.19.0](https://github.com/dashevo/dapi-client/compare/v0.18.0...v0.19.0) (2021-05-03) + + +### Features + +* `getStatus` method handler response updated ([#229](https://github.com/dashevo/dapi-client/issues/229), [#230](https://github.com/dashevo/dapi-client/issues/230)) + + +### Bug Fixes + +* critical security vulnerability in axios@0.19.2 ([#233](https://github.com/dashevo/dapi-client/issues/233)) + + +### BREAKING CHANGES + +* `getStatus` method handler response have changed + + + +# [0.18.0](https://github.com/dashevo/dapi-client/compare/v0.17.2...v0.18.0) (2021-03-03) + + +### Bug Fixes + +* BLS was throwing an error inside `uncaughtException` handler ([#226](https://github.com/dashevo/dapi-client/issues/226)) + + +### Features + + +* `waitForStateTransitionResult` method ([#221](https://github.com/dashevo/dapi-client/issues/221), ([#223](https://github.com/dashevo/dapi-client/issues/223)) + + +### Chores + +* remove temporary `timeout` option from `broadcastStateTransition` ([2abdf5](https://github.com/dashevo/js-dapi-client/commit/2abdf5dc45859bc142dd0f293d9f071190f2a59d)) + + + +## [0.17.2](https://github.com/dashevo/dapi-client/compare/v0.17.1...v0.17.2) (2020-12-30) + + +### Bug Fixes + +* broadcastStateTransitions is timing out on testnet ([#219](https://github.com/dashevo/dapi-client/issues/219)) + + + +## [0.17.1](https://github.com/dashevo/dapi-client/compare/v0.17.0...v0.17.1) (2020-12-30) + + +### Bug Fixes + +* merkleRootQuorums from the diff doesn’t match calculated quorum root after diff is applied ([#217](https://github.com/dashevo/dapi-client/issues/217)) + + + +# [0.17.0](https://github.com/dashevo/dapi-client/compare/v0.16.0...v0.17.0) (2020-12-29) + + +### Features + +* introduce testnet network ([#214](https://github.com/dashevo/dapi-client/issues/214)) +* update `dpp` and `dashcore-lib` ([#207](https://github.com/dashevo/dapi-client/issues/207), [#210](https://github.com/dashevo/dapi-client/issues/210), [#211](https://github.com/dashevo/dapi-client/issues/211), [#212](https://github.com/dashevo/dapi-client/issues/212)) + + +### Bug Fixes + +* SML unhandled error on blockchain reorg ([#215](https://github.com/dashevo/dapi-client/issues/215)) + + +### BREAKING CHANGES + +* DAPI client is now connecting to a testnet by default + + + +# [0.16.0](https://github.com/dashevo/dapi-client/compare/v0.15.0...v0.16.0) (2020-10-27) + + +### Features + +* `getIdentitiesByPublicKeyHashes` and `getIdentityIdsByPublicKeyHashes` methods ([#191](https://github.com/dashevo/dapi-client/issues/191), [#196](https://github.com/dashevo/dapi-client/issues/196), [#205](https://github.com/dashevo/dapi-client/issues/205)) +* `getDataContract`, `getDocuments`, `getIdentity` accept `Buffer` ([#201](https://github.com/dashevo/dapi-client/issues/201)) + + +### Documentation + +* fix URLs in README ([#193](https://github.com/dashevo/dapi-client/issues/193)) + + +### BREAKING CHANGES + +* `getIdentityByFirstPublicKey` and `getIdentityIdByFirstPublicKey` removed +* `getDataContract`, `getDocuments`, `getIdentity` accept `Buffer` or `TypedArray` + + + +# [0.15.0](https://github.com/dashevo/dapi-client/compare/v0.14.0...v0.15.0) (2020-09-04) + + +### Bug Fixes + +* internal error when submitting `fromBlockHeight` as `0` to `subscribeToTransactionsWithProofs` ([#174](https://github.com/dashevo/js-dapi-client/pull/174)) + + +### Features + +* retry request on `UNIMPLEMENTED` error ([#185](https://github.com/dashevo/dapi-client/issues/185)) +* update DAPI gRPC to 0.15 ([#179](https://github.com/dashevo/dapi-client/issues/179), [#186](https://github.com/dashevo/dapi-client/issues/186)) +* remove `getUTXO` and `getAddressSummary` core methods ([#178](https://github.com/dashevo/js-dapi-client/pull/178)) +* rename `sendTransaction` and `applyStateTransition` ([#175](https://github.com/dashevo/js-dapi-client/pull/175)) + + +### BREAKING CHANGES + +* `broadcastTransaction` and `broadcastStatTransition` gRPC method names are using instead of `sendTransaction` and `applyStateTransition` +* `getUTXO` and `getAddressSummary` core methods are removed +* see [DAPI gRPC breaking changes](https://github.com/dashevo/dapi-grpc/releases/tag/v0.15.0) + + + +# [0.14.0](https://github.com/dashevo/dapi-client/compare/v0.13.6...v0.14.0) (2020-07-23) + +We completely rewrote DAPI Client to improve code quality, usability, and testability. + +In the new version, you can specify not just seeds to connect but also specific DAPI addresses +and even inject own logic to obtain/select nodes. API methods accept the same options +as the `DAPIClient` constructor so you can specify different behavior for each API call. + +Previously, faulty nodes were excluded for a specific API call. Now they are banning +for a period of time, and this time increments exponentially in the event of repeated faults. + + +### Bug Fixes + +* cannot read property 'getHttpPort' of undefined ([#173](https://github.com/dashevo/dapi-client/issues/173)) +* internal error when submitting `fromBlockHeight` as 0 to `subscribeToTransactionsWithProofs` ([#174](https://github.com/dashevo/dapi-client/issues/174)) +* ambiguity in `addresses` option ([#170](https://github.com/dashevo/dapi-client/issues/170)) +* JSON RPC does not retry on `ETIMEDOUT` ([#156](https://github.com/dashevo/dapi-client/issues/156)) +* 2 seconds timeout not enough for some requests ([#151](https://github.com/dashevo/dapi-client/issues/151)) +* construct DAPIClient with network option didn't work properly ([#150](https://github.com/dashevo/dapi-client/issues/150)) +* global default timeout applies for streams ([#152](https://github.com/dashevo/dapi-client/issues/152)) + + +### Features + +* add ports to string representation of DAPIAddress ([#171](https://github.com/dashevo/dapi-client/issues/171)) ([1f4ffb7](https://github.com/dashevo/dapi-client/commit/1f4ffb7ed2cd8079eccf938ede2d43f37a5f80d3)) +* allow to specify network with other connection options ([#160](https://github.com/dashevo/dapi-client/issues/160)) ([cfbc5cd](https://github.com/dashevo/dapi-client/commit/cfbc5cd649358420df99f76f1ca84b8c7ae826a4)) +* update DAPI gRPC to 0.14.0-dev.1 ([#149](https://github.com/dashevo/dapi-client/issues/149)) ([4598def](https://github.com/dashevo/dapi-client/commit/4598def13dbdba9c9c1392c65e2c97ceb322c34c)) +* timeout options for gRPC requests and simplified URL for gRPC client ([#146](https://github.com/dashevo/dapi-client/issues/146)) ([35685b9](https://github.com/dashevo/dapi-client/commit/35685b98fa05fc4436630f165113419b3f48833f)) + + +### Documentation + +* readme standard updates ([#167](https://github.com/dashevo/dapi-client/issues/147)) + + +### Code Refactoring + +* rewrite DAPI Client from scratch ([#140](https://github.com/dashevo/dapi-client/issues/140)) + + +### BREAKING CHANGES + +* DAPI Client options [are changed](https://github.com/dashevo/dapi-client/blob/1ec21652f1615ba95ea537c38632692f81deefa3/lib/DAPIClient.js#L42-L51) +* Core and Platform methods moved to specific namespaces (ie. `client.platform.getIdentity()`, `client.core.getStatus()`) + + + +## [0.13.6](https://github.com/dashevo/dapi-client/compare/v0.13.5...v0.13.6) (2020-06-30) + + +### Features + +* update dapi-client to `0.18.11` ([#163](https://github.com/dashevo/dapi-client/issues/163)) + + + +## [0.13.5](https://github.com/dashevo/dapi-client/compare/v0.13.4...v0.13.5) (2020-06-30) + + +### Features + +* update `dashcore-lib` to `0.18.10` ([#162](https://github.com/dashevo/dapi-client/issues/162)) + + + +## [0.13.4](https://github.com/dashevo/dapi-client/compare/v0.13.3...v0.13.4) (2020-06-30) + + +### Bug Fixes + +* network is not set to `SimplifiedMNListDiff` ([#161](https://github.com/dashevo/dapi-client/issues/161)) + + + +## [0.13.3](https://github.com/dashevo/dapi-client/compare/v0.13.2...v0.13.3) (2020-06-18) + + +### Bug Fixes + +* calling method `getIp` of `undefined` ([#159](https://github.com/dashevo/dapi-client/issues/159)) + + + +## [0.13.2](https://github.com/dashevo/dapi-client/compare/v0.13.1...v0.13.2) (2020-06-11) + + +### Bug Fixes + +* retries don't work for MN discovery ([#157](https://github.com/dashevo/dapi-client/issues/157)) + + + +## [0.13.1](https://github.com/dashevo/dapi-client/compare/v0.13.0...v0.13.1) (2020-06-11) + + +### Bug Fixes + +* JSON RPC doesn't retry on `ETIMEDOUT ([#155](https://github.com/dashevo/dapi-client/issues/155)) + + + +# [0.13.0](https://github.com/dashevo/dapi-client/compare/v0.12.0...v0.13.0) (2020-06-08) + + +### Bug Fixes + +* missed grpc-common peer dependency caused error ([#135](https://github.com/dashevo/dapi-client/pull/135)) + + +## Features + +* implement transports with retries ([#130](https://github.com/dashevo/dapi-client/pull/130), [#141](https://github.com/dashevo/dapi-client/pull/141)) +* get identity by public key endpoints ([#133](https://github.com/dashevo/dapi-client/pull/133)) + + +### Documentation + +* add typing ([#143](https://github.com/dashevo/dapi-client/pull/143)) +* JSDoc formatting ([#132](https://github.com/dashevo/dapi-client/pull/132)) +* `subscribeToTransactionsWithProofs` ([#137](https://github.com/dashevo/dapi-client/pull/137)) + + + +# [0.12.0](https://github.com/dashevo/dapi-client/compare/v0.11.0...v0.12.0) (2020-04-20) + + +### Code Refactoring + +* remove `forceJsonRpc` option ([#126](https://github.com/dashevo/dapi-client/issues/126)) + + +### BREAKING CHANGES + +* platform methods no longer available through JSON RPC + + +# [0.11.0](https://github.com/dashevo/dapi-client/compare/v0.8.0...v0.11.0) (2020-03-01) + +### Bug Fixes + +* return null if get "Not Found" gRPC error ([86af3f7](https://github.com/dashevo/dapi-client/commit/86af3f78d26e45dbe9ae1d49b6c215f5af9d0cba)) +* gRPC web connection url should contain protocol ([0c7ad1f](https://github.com/dashevo/dapi-client/commit/0c7ad1f13ac1ec75a319c97514f19671f48c2b66)) + + +### Features + +* introduce `generateToAddress` endpoint ([f8b446b](https://github.com/dashevo/dapi-client/commit/f8b446ba41b0794b2d2007b0ad79e29f4a561b8e)) +* bring back `getAddressSummary` endpoint ([d6de22c](https://github.com/dashevo/dapi-client/commit/d6de22cf8cbeb0ac7bb55ec5ae9e09f9900e3028)) +* implement basic Core gRPC endpoints ([6fe4d4a](https://github.com/dashevo/dapi-client/commit/6fe4d4a79bce750210672ee7f2df9cc14d4437fd)) +* remove obsolete API endpoints and code ([982a514](https://github.com/dashevo/dapi-client/commit/982a51437b94b3cb6ae0ba1b9031daef0a468940)) + + +### BREAKING CHANGES + +* Removed unsupported `generate` endpoint +* Removed insecure endpoints diff --git a/packages/js-dapi-client/LICENSE b/packages/js-dapi-client/LICENSE new file mode 100644 index 00000000000..436d70488ef --- /dev/null +++ b/packages/js-dapi-client/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2019 Dash Core Group, Inc. + +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/packages/js-dapi-client/README.md b/packages/js-dapi-client/README.md new file mode 100644 index 00000000000..b8595ebe788 --- /dev/null +++ b/packages/js-dapi-client/README.md @@ -0,0 +1,113 @@ +# DAPI Client + +[![NPM Version](https://img.shields.io/npm/v/@dashevo/dapi-client)](https://www.npmjs.com/package/@dashevo/dapi-client) +[![Build Status](https://github.com/dashevo/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashevo/platform/actions/workflows/release.yml) +[![Release Date](https://img.shields.io/github/release-date/dashevo/platform)](https://github.com/dashevo/platform/releases/latest) +[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen)](https://github.com/RichardLitt/standard-readme) + +Client library used to access Dash DAPI endpoints + +This library enables HTTP-based interaction with the Dash blockchain and Dash +Platform via the decentralized API ([DAPI](https://github.com/dashevo/dapi)) +hosted on Dash masternodes. + + - `DAPI-Client` provides automatic server (masternode) discovery using either a default seed node or a user-supplied one + - `DAPI-Client` maps to DAPI's [RPC](https://github.com/dashevo/platform/tree/master/packages/dapi/lib/rpcServer/commands) and [gRPC](https://github.com/dashevo/platform/tree/master/packages/dapi/lib/grpcServer/handlers) endpoints + +## Table of Contents +- [Install](#install) +- [Usage](#usage) +- [Documentation](#documentation) +- [Contributing](#contributing) +- [License](#license) + +## Install + +```sh +npm install @dashevo/dapi-client +``` + +## Usage + +### Basic + +```javascript +const DAPIClient = require('@dashevo/dapi-client'); +const client = new DAPIClient(); + +client.core.getStatus().then((coreStatus) => { + console.dir(coreStatus); +}); +``` + +### Custom seed node + +Custom seed nodes are necessary for connecting the client to devnets since the client library is unaware of them otherwise. + +```javascript +const DAPIClient = require('@dashevo/dapi-client'); + +var client = new DAPIClient({ + seeds: [{ + host: 'seed-1.evonet.networks.dash.org', + httpPort: 3000, + grpcPort: 3010, + }], +}); + +client.core.getBestBlockHash().then((r) => { + console.log(r); +}); +``` + +**Note**: The seed node shown above (`seed-1.evonet.networks.dash.org`) is for the Dash Evonet testing network. + +### Custom addresses + +Custom addresses may be directly specified in cases where it is beneficial to know exactly what node(s) are being accessed (e.g. debugging, local development, etc.). + +```javascript +const DAPIClient = require('@dashevo/dapi-client'); + +var client = new DAPIClient({ + dapiAddresses: [ + '127.0.0.1:3000:3010', + '127.0.0.2:3000:3010', + ], +}); + +client.core.getBestBlockHash().then((r) => { + console.log(r); +}); +``` + +### Command specific options + +DAPI Client options can be passed directly to any command to override any predefined client options and modify the client's behavior for that specific call. + +```javascript +const DAPIClient = require('@dashevo/dapi-client'); + +// Set options to direct the request to a specific address and disable retries +const options = { + dapiAddresses: ['127.0.0.1'], + retries: 0, +}; + +client.core.getBestBlockHash(options).then((r) => { + console.log(r); +}); +``` + +## Documentation + +More extensive documentation available at https://dashevo.github.io/platform/DAPI-Client/. + + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs. + +## License + +[MIT](LICENSE) © Dash Core Group, Inc. diff --git a/packages/js-dapi-client/docs/.nojekyll b/packages/js-dapi-client/docs/.nojekyll new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/js-dapi-client/docs/README.md b/packages/js-dapi-client/docs/README.md new file mode 100644 index 00000000000..a6005b889a3 --- /dev/null +++ b/packages/js-dapi-client/docs/README.md @@ -0,0 +1,40 @@ +## DAPI-Client + +[![NPM Version](https://img.shields.io/npm/v/@dashevo/dapi-client)](https://www.npmjs.com/package/@dashevo/dapi-client) +[![Build Status](https://github.com/dashevo/js-dapi-client/actions/workflows/test_and_release.yml/badge.svg)](https://github.com/dashevo/js-dapi-client/actions/workflows/test_and_release.yml) +[![Release Date](https://img.shields.io/github/release-date/dashevo/dapi-client)](https://github.com/dashevo/dapi-client/releases/latest) +[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen)](https://github.com/RichardLitt/standard-readme) + +Client library used to access Dash DAPI endpoints + +This library enables HTTP-based interaction with the Dash blockchain and Dash +Platform via the decentralized API ([DAPI](https://github.com/dashevo/dapi)) +hosted on Dash masternodes. + + - `DAPI-Client` provides automatic server (masternode) discovery using either a default seed node or a user-supplied one + - `DAPI-Client` maps to DAPI's [RPC](https://github.com/dashevo/dapi/tree/master/lib/rpcServer/commands) and [gRPC](https://github.com/dashevo/dapi/tree/master/lib/grpcServer/handlers) endpoints + +### Install + +### ES5/ES6 via NPM + +In order to use this library in Node, you will need to add it to your project as a dependency. + +Having [NodeJS](https://nodejs.org/) installed, just type in your terminal : + +```sh +npm install @dashevo/dapi-client +``` + +### CDN Standalone + +For browser usage, you can also directly rely on unpkg : + +``` + +``` + + +## Licence + +[MIT](https://github.com/dashevo/dapi-client/blob/master/LICENCE.md) © Dash Core Group, Inc. diff --git a/packages/js-dapi-client/docs/_sidebar.md b/packages/js-dapi-client/docs/_sidebar.md new file mode 100644 index 00000000000..2d2059938e0 --- /dev/null +++ b/packages/js-dapi-client/docs/_sidebar.md @@ -0,0 +1,24 @@ +- Getting started + - [Quick start](getting-started/quickstart.md) +- Usage + - DAPIClient + - [new DAPIClient()](usage/application/DAPIClient.md) + - Core + - [.broadcastTransaction()](usage/application/core/broadcastTransaction.md) + - [.generateToAddress()](usage/application/core/generateToAddress.md) + - [.getBestBlockHash()](usage/application/core/getBestBlockHash.md) + - [.getBlockByHash()](usage/application/core/getBlockByHash.md) + - [.getBlockByHeight()](usage/application/core/getBlockByHeight.md) + - [.getBlockHash()](usage/application/core/getBlockHash.md) + - [.getMnListDiff()](usage/application/core/getMnListDiff.md) + - [.getStatus()](usage/application/core/getStatus.md) + - [.getTransaction()](usage/application/core/getTransaction.md) + - [.subscribeToTransactionsWithProofs()](usage/application/core/subscribeToTransactionsWithProofs.md) + - Platform + - [.broadcastStateTransition()](usage/application/platform/broadcastStateTransition.md) + - [.getDataContract()](usage/application/platform/getDataContract.md) + - [.getDocuments()](usage/application/platform/getDocuments.md) + - [.getIdentityByFirstPublicKey()](usage/application/platform/getIdentityByFirstPublicKey.md) + - [.getIdentity()](usage/application/platform/getIdentity.md) + - [.getIdentityIdByFirstPublicKey()](usage/application/platform/getIdentityIdByFirstPublicKey.md) +- [License](https://github.com/dashevo/dapi-client/blob/master/LICENSE) diff --git a/packages/js-dapi-client/docs/getting-started/quickstart.md b/packages/js-dapi-client/docs/getting-started/quickstart.md new file mode 100644 index 00000000000..3b519430c78 --- /dev/null +++ b/packages/js-dapi-client/docs/getting-started/quickstart.md @@ -0,0 +1,37 @@ +# Quick start + +## ES5/ES6 via NPM + +In order to use this library in Node, you will need to add it to your project as a dependency. + +Having [NodeJS](https://nodejs.org/) installed, just type in your terminal : + +```sh +npm install @dashevo/dapi-client +``` + +## CDN Standalone + +For browser usage, you can also directly rely on unpkg : + +``` + +``` + +You can see an [example usage here](https://github.com/dashevo/js-dapi-client/blob/master/examples/web/web.usage.html) + +## Initialization + +```js +const DAPIClient = require('@dashevo/dapi-client'); +const client = new DAPIClient(); + +(async () => { + const bestBlockHash = await client.core.getBestBlockHash(); + console.log(bestBlockHash); +})(); +``` + +## Quicknotes + +This package allows you to fetch & send information from both the payment chain (layer 1) and the application chain (layer 2, a.k.a Platform chain). diff --git a/packages/js-dapi-client/docs/index.html b/packages/js-dapi-client/docs/index.html new file mode 100644 index 00000000000..4ea84d5291d --- /dev/null +++ b/packages/js-dapi-client/docs/index.html @@ -0,0 +1,39 @@ + + + + + DAPI-Client - Client library used to access Dash DAPI endpoints + + + + + + + +
+ + + + + + diff --git a/packages/js-dapi-client/docs/usage/application/DAPIClient.md b/packages/js-dapi-client/docs/usage/application/DAPIClient.md new file mode 100644 index 00000000000..9c1e7862567 --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/DAPIClient.md @@ -0,0 +1,29 @@ +**Usage**: `new DAPIClient(options)` +**Description**: This method creates a new DAPIClient instance. + +Parameters: + +| parameters | type | required[def value] | Description | +|-------------------------------------------|---------------------|-----------------------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **options** | Object | | | +| **options.dapiAddressProvider** | DAPIAddressProvider | no[ListDAPIAddressProvider] | Allow to override the default dapiAddressProvider (do not allow seeds or dapiAddresses params) | +| **options.seeds** | string[] | no[seeds] | Allow to override default seeds (to connect to specific node) | +| **options.network** | string|Network | no[=evonet] | Allow to setup the network to be used (livenet, testnet, evonet,..) | +| **options.timeout** | number | no[=2000] | Used to specify the timeout time in milliseconds. | +| **options.retries** | number | no[=3] | Used to specify the number of retries before aborting and erroring a request. | +| **options.baseBanTime** | number | no[=6000] | | + +Returns : DAPIClient instance. + +```js +const DAPIClient = require('@dashevo/dapi-client'); +const client = new DAPIClient({ + timeout: 5000, + retries: 3, + network: 'livenet' +}); +``` + +**Notes**: +- Accessing the SimplifiedMasternodeListDAPIAddressProvider (or its overwrote instance), can be accessed via `client.dapiAddressProvider`. + diff --git a/packages/js-dapi-client/docs/usage/application/core/broadcastTransaction.md b/packages/js-dapi-client/docs/usage/application/core/broadcastTransaction.md new file mode 100644 index 00000000000..683828c35a8 --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/core/broadcastTransaction.md @@ -0,0 +1,15 @@ +**Usage**: `await client.core.broadcastTransaction(transaction)` +**Description**: Allow to broadcast a valid **signed** transaction to the network. + +Parameters: + +| parameters | type | required | Description | +|---------------------------|---------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **transaction** | Buffer | yes | A valid Buffer representation of a transaction | +| **options** | Object | | | +| **options.allowHighFees** | Boolean | no[=false] | As safety measure, "absurd" fees are rejected when considered to high. This allow to overwrite that comportement | +| **options.bypassLimits** | Boolean | no[=false] | Allow to bypass default transaction policy rules limitation | + +Returns : transactionId (string). + +N.B : The TransactionID provided is subject to [transaction malleability](https://dashcore.readme.io/docs/core-guide-transactions-transaction-malleability), and is not a source of truth (the transaction might be included in a block with a different txid). diff --git a/packages/js-dapi-client/docs/usage/application/core/generateToAddress.md b/packages/js-dapi-client/docs/usage/application/core/generateToAddress.md new file mode 100644 index 00000000000..e3eb7fafcfc --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/core/generateToAddress.md @@ -0,0 +1,13 @@ +**Usage**: `await client.core.generateToAddress(blockMumber, address, options)` +**Description**: Allow to broadcast a valid **signed** transaction to the network. +**Notes**: Will only works on regtest. + +Parameters: + +| parameters | type | required | Description | +|---------------------------|---------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **blocksNumber** | Number | yes | A number of block to see generated on the regtest network | +| **address** | String | yes | The address that will receive the newly generated Dash | +| **options** | DAPIClientOptions | no | | + +Returns : {Promise} - a set of generated blockhashes. diff --git a/packages/js-dapi-client/docs/usage/application/core/getBestBlockHash.md b/packages/js-dapi-client/docs/usage/application/core/getBestBlockHash.md new file mode 100644 index 00000000000..7527ad6ff6c --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/core/getBestBlockHash.md @@ -0,0 +1,10 @@ +**Usage**: `await client.core.getBestBlockHash(options)` +**Description**: Allow to fetch the best (highest/latest block hash) from the network + +Parameters: + +| parameters | type | required | Description | +|---------------------------|---------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **options** | DAPIClientOptions | no | | + +Returns : {Promise} - The best block hash diff --git a/packages/js-dapi-client/docs/usage/application/core/getBlockByHash.md b/packages/js-dapi-client/docs/usage/application/core/getBlockByHash.md new file mode 100644 index 00000000000..a4362bab22b --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/core/getBlockByHash.md @@ -0,0 +1,11 @@ +**Usage**: `await client.core.getBlockByHash(hash, options)` +**Description**: Allow to fetch a specific block by its hash + +Parameters: + +| parameters | type | required | Description | +|---------------------------|---------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **hash** | String | yes | A valid block hash | +| **options** | DAPIClientOptions | no | | + +Returns : {Promise} - The specified bufferized block diff --git a/packages/js-dapi-client/docs/usage/application/core/getBlockByHeight.md b/packages/js-dapi-client/docs/usage/application/core/getBlockByHeight.md new file mode 100644 index 00000000000..20b6b437fd6 --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/core/getBlockByHeight.md @@ -0,0 +1,11 @@ +**Usage**: `await client.core.getBlockByHeight(height, options)` +**Description**: Allow to fetch a specific block by its height + +Parameters: + +| parameters | type | required | Description | +|---------------------------|---------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **height** | Number | yes | A valid block height | +| **options** | DAPIClientOptions | no | | + +Returns : {Promise} - The specified bufferized block diff --git a/packages/js-dapi-client/docs/usage/application/core/getBlockHash.md b/packages/js-dapi-client/docs/usage/application/core/getBlockHash.md new file mode 100644 index 00000000000..1eb61e3dc5f --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/core/getBlockHash.md @@ -0,0 +1,11 @@ +**Usage**: `await client.core.getBlockHash(height, options)` +**Description**: Allow to fetch a specific block hash from its height + +Parameters: + +| parameters | type | required | Description | +|---------------------------|---------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **height** | Number | yes | A valid block height | +| **options** | DAPIClientOptions | no | | + +Returns : {Promise} - the corresponding block hash diff --git a/packages/js-dapi-client/docs/usage/application/core/getMnListDiff.md b/packages/js-dapi-client/docs/usage/application/core/getMnListDiff.md new file mode 100644 index 00000000000..9c1ee698ab0 --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/core/getMnListDiff.md @@ -0,0 +1,12 @@ +**Usage**: `await client.core.getMnListDiff(baseBlockHash, blockHash, options)` +**Description**: Allow to fetch a specific block hash from its height + +Parameters: + +| parameters | type | required | Description | +|---------------------------|---------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **baseBlockHash** | String | yes | hash or height of start block | +| **blockHash** | String | yes | hash or height of end block | +| **options** | DAPIClientOptions | no | | + +Returns : {Promise} - The Masternode List Diff of the specified period diff --git a/packages/js-dapi-client/docs/usage/application/core/getStatus.md b/packages/js-dapi-client/docs/usage/application/core/getStatus.md new file mode 100644 index 00000000000..8a3df443a2a --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/core/getStatus.md @@ -0,0 +1,29 @@ +**Usage**: `await client.core.getStatus(options)` +**Description**: Allow to fetch a specific block hash from its height + +Parameters: + +| parameters | type | required | Description | +|---------------------------|---------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **options** | DAPIClientOptions | no | | + +Returns : {Promise} - Status object + +```js +const status = await client.core.getStatus() +/** +{ + coreVersion: 150000, + protocolVersion: 70216, + blocks: 10630, + timeOffset: 0, + connections: 58, + proxy: '', + difficulty: 0.001745769130443678, + testnet: false, + relayFee: 0.00001, + errors: '', + network: 'testnet' +} +**/ +``` diff --git a/packages/js-dapi-client/docs/usage/application/core/getTransaction.md b/packages/js-dapi-client/docs/usage/application/core/getTransaction.md new file mode 100644 index 00000000000..6a4d2ca19b7 --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/core/getTransaction.md @@ -0,0 +1,11 @@ +**Usage**: `await client.core.getTransaction(id, options)` +**Description**: Allow to fetch a transaction by ID + +Parameters: + +| parameters | type | required | Description | +|---------------------------|---------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **id** | string | yes | A valid transaction id to fetch | +| **options** | DAPIClientOptions | no | | + +Returns : {Promise} - The bufferized transaction diff --git a/packages/js-dapi-client/docs/usage/application/getDataContract.md b/packages/js-dapi-client/docs/usage/application/getDataContract.md new file mode 100644 index 00000000000..58fecc05c12 --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/getDataContract.md @@ -0,0 +1,11 @@ +**Usage**: `async client.platform.getDataContract(contractId)` +**Description**: Fetch Data Contract by id + +Parameters: + +| parameters | type | required | Description | +|------------------------|--------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **contractId** | String | yes | A valid registered contractId | + +Returns : Promise + diff --git a/packages/js-dapi-client/docs/usage/application/getDocuments.md b/packages/js-dapi-client/docs/usage/application/getDocuments.md new file mode 100644 index 00000000000..498b0dbc822 --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/getDocuments.md @@ -0,0 +1,17 @@ +**Usage**: `async client.platform.getDocuments(contractId, type, options)` +**Description**: Fetch Documents from Drive + +Parameters: + +| parameters | type | required | Description | +|------------------------|--------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **contractId** | String | yes | A valid registered contractId | +| **type** | String | yes | DAP object type to fetch (e.g: 'preorder' in DPNS) | +| **options.where** | Object | yes | Mongo-like query | +| **options.orderBy** | Object | yes | Mongo-like sort field | +| **options.limit** | Number | yes | Limit the number of object to fetch | +| **options.startAt** | Number | yes | number of objects to skip | +| **options.startAfter** | Number | yes | exclusive skip | + +Returns : Promise + diff --git a/packages/js-dapi-client/docs/usage/application/getIdentity.md b/packages/js-dapi-client/docs/usage/application/getIdentity.md new file mode 100644 index 00000000000..9c9291c13f3 --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/getIdentity.md @@ -0,0 +1,11 @@ +**Usage**: `async client.platform.getIdentity(id)` +**Description**: Fetch the identity by id + +Parameters: + +| parameters | type | required | Description | +|------------------------|--------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **id** | String | yes | A valid registered identity | + +Returns : Promise + diff --git a/packages/js-dapi-client/docs/usage/application/getIdentityByFirstPublicKey.md b/packages/js-dapi-client/docs/usage/application/getIdentityByFirstPublicKey.md new file mode 100644 index 00000000000..835681b7889 --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/getIdentityByFirstPublicKey.md @@ -0,0 +1,10 @@ +**Usage**: `async client.platform.getIdentityByFirstPublicKey(publicKeyHash)` +**Description**: Fetch the identity using the public key hash of the identity's first key + +Parameters: + +| parameters | type | required | Description | +|------------------------|--------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **publicKeyHash** | String | yes | A valid public key hash | + +Returns : Promise diff --git a/packages/js-dapi-client/docs/usage/application/getIdentityIdByFirstPublicKey.md b/packages/js-dapi-client/docs/usage/application/getIdentityIdByFirstPublicKey.md new file mode 100644 index 00000000000..d394eb655ea --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/getIdentityIdByFirstPublicKey.md @@ -0,0 +1,10 @@ +**Usage**: `async client.platform.getIdentityIdByFirstPublicKey(publicKeyHash)` +**Description**: Fetch the identity ID using the public key hash of the identity's first key + +Parameters: + +| parameters | type | required | Description | +|------------------------|--------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **publicKeyHash** | String | yes | A valid public key hash | + +Returns : Promise diff --git a/packages/js-dapi-client/docs/usage/application/platform/broadcastStateTransition.md b/packages/js-dapi-client/docs/usage/application/platform/broadcastStateTransition.md new file mode 100644 index 00000000000..8bfcf1a77d2 --- /dev/null +++ b/packages/js-dapi-client/docs/usage/application/platform/broadcastStateTransition.md @@ -0,0 +1,11 @@ +**Usage**: `async client.platform.broadcastStateTransition(stateTransition, options)` +**Description**: Send State Transition to machine + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **stateTransition** | Buffer | yes | A valid bufferized state transition | +| **options** | DAPIClientOptions | no | A valid state transition | + +Returns : Promise diff --git a/packages/js-dapi-client/docs/usage/utils/subscribeToTransactionsWithProofs.md b/packages/js-dapi-client/docs/usage/utils/subscribeToTransactionsWithProofs.md new file mode 100644 index 00000000000..96602882b5d --- /dev/null +++ b/packages/js-dapi-client/docs/usage/utils/subscribeToTransactionsWithProofs.md @@ -0,0 +1,45 @@ +**Usage**: `await client.core.subscribeToTransactionsWithProofs(bloomFilter, options = { count: 0 })`\ +**Description**: For any provided bloomfilter, it will return a ClientReadableStream streaming the transaction matching the filter. + + +Parameters: + +| parameters | type | required | Description | +|----------------------------|------------------|----------------| ------------------------------------------------------------------------------------------------ | +| **bloomFilter.vData** | Uint8Array/Array | yes | The filter itself is simply a bit field of arbitrary byte-aligned size. The maximum size is 36,000 bytes. | +| **bloomFilter.nHashFuncs** | Number | yes | The number of hash functions to use in this filter. The maximum value allowed in this field is 50. | +| **bloomFilter.nTweak** | Number | yes | A random value to add to the seed value in the hash function used by the bloom filter. | +| **bloomFilter.nFlags** | Number | yes | A set of flags that control how matched items are added to the filter. | +| **options.fromBlockHash** | String | yes | Specifies block hash to start syncing from | +| **options.fromBlockHeight**| Number | yes | Specifies block height to start syncing from | +| **options.count** | Number | no (default: 0)| Number of blocks to sync, if set to 0 syncing is continuously sends new data as well | + +Returns : Promise|!grpc.web.ClientReadableStream + +Example : + +```js +const filter; // A BloomFilter object +const stream = await client.subscribeToTransactionsWithProofs(filter, { fromBlockHeight: 0 }); + +stream + .on('data', (response) => { + const merkleBlock = response.getRawMerkleBlock(); + const transactions = response.getRawTransactions(); + + if (merkleBlock) { + const merkleBlockHex = Buffer.from(merkleBlock).toString('hex'); + } + + if (transactions) { + transactions.getTransactionsList() + .forEach((tx) => { + // tx are probabilistic, so you will have to verify it's yours + const tx = new Transaction(Buffer.from(tx)); + }); + } + }) + .on('error', (err) => { + // do something with err + }); +``` diff --git a/packages/js-dapi-client/examples/web/web.usage.html b/packages/js-dapi-client/examples/web/web.usage.html new file mode 100644 index 00000000000..1ed73959f9c --- /dev/null +++ b/packages/js-dapi-client/examples/web/web.usage.html @@ -0,0 +1,19 @@ + + + + + Title + + + + + + + + diff --git a/packages/js-dapi-client/karma.conf.js b/packages/js-dapi-client/karma.conf.js new file mode 100644 index 00000000000..3268efb96f0 --- /dev/null +++ b/packages/js-dapi-client/karma.conf.js @@ -0,0 +1,47 @@ +const karmaMocha = require('karma-mocha'); +const karmaMochaReporter = require('karma-mocha-reporter'); +const karmaChai = require('karma-chai'); +const karmaChromeLauncher = require('karma-chrome-launcher'); +const karmaFirefoxLauncher = require('karma-firefox-launcher'); +const karmaWebpack = require('karma-webpack'); + +const webpackConfig = require('./webpack.config'); + +module.exports = (config) => { + config.set({ + frameworks: ['mocha', 'chai', 'webpack'], + files: [ + 'lib/test/karma/loader.js', + ], + preprocessors: { + 'lib/test/karma/loader.js': ['webpack'], + }, + webpack: { + mode: 'development', + resolve: webpackConfig[0].resolve, + plugins: webpackConfig[0].plugins, + }, + reporters: ['mocha'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: false, + browsers: ['ChromeHeadless', 'FirefoxHeadless'], + singleRun: false, + concurrency: Infinity, + plugins: [ + karmaMocha, + karmaMochaReporter, + karmaChai, + karmaChromeLauncher, + karmaFirefoxLauncher, + karmaWebpack, + ], + customLaunchers: { + FirefoxHeadless: { + base: 'Firefox', + flags: ['-headless'], + }, + }, + }); +}; diff --git a/packages/js-dapi-client/lib/BlockHeadersProvider/BlockHeadersProvider.js b/packages/js-dapi-client/lib/BlockHeadersProvider/BlockHeadersProvider.js new file mode 100644 index 00000000000..30a956c1546 --- /dev/null +++ b/packages/js-dapi-client/lib/BlockHeadersProvider/BlockHeadersProvider.js @@ -0,0 +1,121 @@ +const EventEmitter = require('events'); +const { SpvChain } = require('@dashevo/dash-spv'); + +const BlockHeadersReader = require('./BlockHeadersReader'); + +/** + * @typedef {BlockHeadersProviderOptions} BlockHeadersProviderOptions + * @property {string} [network=testnet] + * @property {number} [maxParallelStreams=5] max parallel streams to read historical block headers + * @property {number} [targetBatchSize=100000] a target batch size per stream + * @property {number} [maxRetries=10] max amount of retries per stream connection + * @property {number} [autoStart=false] auto start fetching verifying block headers + */ +const defaultOptions = { + network: 'testnet', + maxParallelStreams: 5, + targetBatchSize: 100000, + fromBlockHeight: 1, + maxRetries: 10, + autoStart: false, +}; + +const EVENTS = { + ERROR: 'error', +}; + +class BlockHeadersProvider extends EventEmitter { + /** + * @param {BlockHeadersProviderOptions} options + */ + constructor(options = {}) { + super(); + this.options = { + ...defaultOptions, + ...options, + }; + + this.spvChain = new SpvChain(this.options.network); + this.started = false; + } + + /** + * @param {CoreMethodsFacade} coreMethods + */ + setCoreMethods(coreMethods) { + this.coreMethods = coreMethods; + } + + /** + * @param {BlockHeadersReader} blockHeadersReader + */ + setBlockHeadersReader(blockHeadersReader) { + this.blockHeadersReader = blockHeadersReader; + } + + /** + * + * @param spvChain + */ + setSpvChain(spvChain) { + this.spvChain = spvChain; + } + + async start() { + if (!this.coreMethods) { + throw new Error('Core methods have not been provided. Please use "setCoreMethods"'); + } + + if (this.started) { + throw new Error('BlockHeaderProvider has already been started'); + } + + const { chain: { blocksCount: bestBlockHeight } } = await this.coreMethods.getStatus(); + + if (!this.blockHeadersReader) { + this.blockHeadersReader = new BlockHeadersReader( + { + coreMethods: this.coreMethods, + maxParallelStreams: this.options.maxParallelStreams, + targetBatchSize: this.options.targetBatchSize, + maxRetries: this.options.maxRetries, + }, + ); + } + + this.blockHeadersReader.on(BlockHeadersReader.EVENTS.ERROR, (e) => { + this.emit(EVENTS.ERROR, e); + }); + + this.blockHeadersReader.on(BlockHeadersReader.EVENTS.HISTORICAL_DATA_OBTAINED, () => { + this.blockHeadersReader.subscribeToNew(bestBlockHeight) + .catch((e) => { + this.emit(EVENTS.ERROR, e); + }); + }); + + this.blockHeadersReader.on(BlockHeadersReader.EVENTS.BLOCK_HEADERS, (headers, reject) => { + try { + this.spvChain.addHeaders(headers.map((header) => Buffer.from(header))); + } catch (e) { + if (e.message === 'Some headers are invalid') { + reject(e); + } else { + this.emit(EVENTS.ERROR, e); + } + } + }); + + await this.blockHeadersReader.readHistorical( + this.options.fromBlockHeight, + bestBlockHeight - 1, + ); + + this.started = true; + } +} + +BlockHeadersProvider.EVENTS = EVENTS; +BlockHeadersProvider.defaultOptions = defaultOptions; + +module.exports = BlockHeadersProvider; diff --git a/packages/js-dapi-client/lib/BlockHeadersProvider/BlockHeadersReader.js b/packages/js-dapi-client/lib/BlockHeadersProvider/BlockHeadersReader.js new file mode 100644 index 00000000000..c1cde03fe09 --- /dev/null +++ b/packages/js-dapi-client/lib/BlockHeadersProvider/BlockHeadersReader.js @@ -0,0 +1,229 @@ +const { EventEmitter } = require('events'); + +const EVENTS = { + BLOCK_HEADERS: 'BLOCK_HEADERS', + HISTORICAL_DATA_OBTAINED: 'HISTORICAL_DATA_OBTAINED', + ERROR: 'error', +}; + +const COMMANDS = { + HANDLE_FINISHED_STREAM: 'HANDLE_FINISHED_STREAM', + HANDLE_STREAM_RETRY: 'HANDLE_STREAM_RETRY', + HANDLE_STREAM_ERROR: 'HANDLE_STREAM_ERROR', +}; + +/** + * @typedef BlockHeadersReaderOptions + * @property {CoreMethodsFacade} [coreMethods] + * @property {number} [maxParallelStreams] + * @property {number} [targetBatchSize] + * @property {number} [maxRetries] + */ + +class BlockHeadersReader extends EventEmitter { + /** + * @param {BlockHeadersReaderOptions} options + */ + constructor(options = {}) { + super(); + this.coreMethods = options.coreMethods; + this.maxParallelStreams = options.maxParallelStreams; + this.targetBatchSize = options.targetBatchSize; + this.maxRetries = options.maxRetries; + + /** + * Holds references to the historical streams + * + * @type {*[]} + */ + this.historicalStreams = []; + } + + /** + * Reads historical block heights using multiple streams + * + * @param {number} fromBlockHeight + * @param {number} toBlockHeight + * @returns {Promise} + */ + async readHistorical(fromBlockHeight, toBlockHeight) { + if (this.historicalStreams.length) { + throw new Error('Historical streams are already running'); + } + + const totalAmount = toBlockHeight - fromBlockHeight + 1; + if (totalAmount === 0) { + return; + } + + if (totalAmount < 0) { + throw new Error(`Invalid total amount of headers to read: ${totalAmount}`); + } + + // Resubscribe to the stream in case of error, and replace the stream in the array + this.on(COMMANDS.HANDLE_STREAM_RETRY, (oldStream, newStream) => { + const index = this.historicalStreams.indexOf(oldStream); + this.historicalStreams[index] = newStream; + }); + + // Remove stream from the array in case of error + this.on(COMMANDS.HANDLE_STREAM_ERROR, (stream, e) => { + const index = this.historicalStreams.indexOf(stream); + this.historicalStreams.splice(index, 1); + this.emit(EVENTS.ERROR, e); + }); + + // Remove finished stream from the array and emit HISTORICAL_DATA_OBTAINED event + this.on(COMMANDS.HANDLE_FINISHED_STREAM, (stream) => { + const index = this.historicalStreams.indexOf(stream); + this.historicalStreams.splice(index, 1); + if (this.historicalStreams.length === 0) { + this.emit(EVENTS.HISTORICAL_DATA_OBTAINED); + } + }); + + const numStreams = Math.min( + Math.max(Math.round(totalAmount / this.targetBatchSize), 1), + this.maxParallelStreams, + ); + + const actualBatchSize = Math.ceil(totalAmount / numStreams); + for (let batchIndex = 0; batchIndex < numStreams; batchIndex += 1) { + const startingHeight = (batchIndex * actualBatchSize) + 1; + const count = Math.min(actualBatchSize, toBlockHeight - startingHeight + 1); + + const subscribeWithRetries = this.subscribeToHistoricalBatch(this.maxRetries); + + // eslint-disable-next-line no-await-in-loop + const stream = await subscribeWithRetries(startingHeight, count); + this.historicalStreams.push(stream); + } + } + + stopReadingHistorical() { + this.removeAllListeners(COMMANDS.HANDLE_STREAM_RETRY); + this.removeAllListeners(COMMANDS.HANDLE_STREAM_ERROR); + this.removeAllListeners(COMMANDS.HANDLE_FINISHED_STREAM); + this.historicalStreams.forEach((stream) => stream.destroy()); + this.historicalStreams = []; + } + + /** + * Subscribes to continuously arriving block headers + * + * @param {number} fromBlockHeight + * @returns {Promise} + */ + async subscribeToNew(fromBlockHeight) { + const stream = await this.coreMethods.subscribeToBlockHeadersWithChainLocks({ + fromBlockHeight, + }); + + stream.on('data', (data) => { + const blockHeaders = data.getBlockHeaders(); + + if (blockHeaders) { + /** + * Kills stream in case of deliberate rejection from the outside + * + * @param e + */ + const rejectHeaders = (e) => { + stream.destroy(e); + }; + + this.emit(EVENTS.BLOCK_HEADERS, blockHeaders.getHeadersList(), rejectHeaders); + } + }); + + stream.on('error', (e) => { + this.emit(EVENTS.ERROR, e); + }); + + return stream; + } + + /** + * A HOF that returns a function to subscribe to historical block headers and chain locks + * and handles retry logic + * + * @private + * @param {number} [maxRetries=0] - maximum amount of retries + * @returns {function(*, *): Promise} + */ + subscribeToHistoricalBatch(maxRetries = 0) { + let currentRetries = 0; + + /** + * Subscribes to the stream of historical data and handles retry logic + * + * @param {number} fromBlockHeight + * @param {number} count + * @returns {Promise} + */ + const subscribeWithRetries = async (fromBlockHeight, count) => { + let headersObtained = 0; + + const stream = await this.coreMethods.subscribeToBlockHeadersWithChainLocks({ + fromBlockHeight, + count, + }); + + stream.on('data', (data) => { + const blockHeaders = data.getBlockHeaders(); + + if (blockHeaders) { + const headersList = blockHeaders.getHeadersList(); + + let rejected = false; + + /** + * Kills stream in case of deliberate rejection from the outside + * + * @param e + */ + const rejectHeaders = (e) => { + rejected = true; + stream.destroy(e); + }; + + this.emit(EVENTS.BLOCK_HEADERS, headersList, rejectHeaders); + + if (!rejected) { + headersObtained += headersList.length; + } + } + }); + + stream.on('error', (streamError) => { + if (currentRetries < maxRetries) { + const newFromBlockHeight = fromBlockHeight + headersObtained; + const newCount = count - headersObtained; + + subscribeWithRetries(newFromBlockHeight, newCount) + .then((newStream) => { + currentRetries += 1; + this.emit(COMMANDS.HANDLE_STREAM_RETRY, stream, newStream); + }).catch((e) => { + this.emit(COMMANDS.HANDLE_STREAM_ERROR, stream, e); + }); + } else { + this.emit(COMMANDS.HANDLE_STREAM_ERROR, stream, streamError); + } + }); + + stream.on('end', () => { + this.emit(COMMANDS.HANDLE_FINISHED_STREAM, stream); + }); + + return stream; + }; + + return subscribeWithRetries; + } +} + +BlockHeadersReader.EVENTS = EVENTS; +BlockHeadersReader.COMMANDS = COMMANDS; + +module.exports = BlockHeadersReader; diff --git a/packages/js-dapi-client/lib/BlockHeadersProvider/createBlockHeadersProviderFromOptions.js b/packages/js-dapi-client/lib/BlockHeadersProvider/createBlockHeadersProviderFromOptions.js new file mode 100644 index 00000000000..a6db548cc19 --- /dev/null +++ b/packages/js-dapi-client/lib/BlockHeadersProvider/createBlockHeadersProviderFromOptions.js @@ -0,0 +1,77 @@ +const networks = require('@dashevo/dashcore-lib/lib/networks'); +const DAPIClientError = require('../errors/DAPIClientError'); +const BlockHeadersProvider = require('./BlockHeadersProvider'); + +const validateNumber = (value, name, min = NaN, max = NaN) => { + if (typeof value !== 'number') { + throw new DAPIClientError(`'${name}' is not a number`); + } + + if (!Number.isNaN(min) && value < min) { + throw new DAPIClientError(`'${name}' can not be less than ${min}`); + } + + if (!Number.isNaN(max) && value > min) { + throw new DAPIClientError(`'${name}' can not be more than ${max}`); + } +}; + +/** + * @typedef {createBlockHeadersProviderFromOptions} + * @param {DAPIClientOptions} options + * @param {CoreMethodsFacade} coreMethods + * @returns {BlockHeadersProvider} + */ +function createBlockHeadersProviderFromOptions(options, coreMethods) { + let blockHeadersProvider; + if (options.blockHeadersProvider) { + if (options.blockHeadersProviderOptions) { + throw new DAPIClientError("Can't use 'blockHeadersProviderOptions' with 'blockHeadersProvider' option"); + } + + blockHeadersProvider = options.blockHeadersProvider; + } + + if (options.blockHeadersProviderOptions) { + const blockHeadersProviderOptions = { + ...BlockHeadersProvider.defaultOptions, + ...options.blockHeadersProviderOptions, + }; + + const { + network, + autoStart, + maxParallelStreams, + targetBatchSize, + fromBlockHeight, + maxRetries, + } = blockHeadersProviderOptions; + + if (network && !networks.get(network)) { + throw new DAPIClientError(`Invalid network '${options.network}'`); + } + + if (typeof autoStart !== 'boolean') { + throw new DAPIClientError('\'autoStart\' option must have boolean type'); + } + + validateNumber(maxParallelStreams, 'maxParallelStreams', 1); + validateNumber(targetBatchSize, 'targetBatchSize', 1); + validateNumber(fromBlockHeight, 'fromBlockHeight', 1); + validateNumber(maxRetries, 'maxRetries', 0); + + blockHeadersProvider = new BlockHeadersProvider( + blockHeadersProviderOptions, + ); + } + + if (!blockHeadersProvider) { + blockHeadersProvider = new BlockHeadersProvider(); + } + + blockHeadersProvider.setCoreMethods(coreMethods); + + return blockHeadersProvider; +} + +module.exports = createBlockHeadersProviderFromOptions; diff --git a/packages/js-dapi-client/lib/BlockHeadersProvider/interfaces/BlockHeadersReaderInterface.js b/packages/js-dapi-client/lib/BlockHeadersProvider/interfaces/BlockHeadersReaderInterface.js new file mode 100644 index 00000000000..941b129a7c0 --- /dev/null +++ b/packages/js-dapi-client/lib/BlockHeadersProvider/interfaces/BlockHeadersReaderInterface.js @@ -0,0 +1,25 @@ +/** + * @interface BlockHeadersReader + */ + +/** + * @private + * @function + * @async + * @name BlockHeadersReader#createBatchFetcher + * @returns {(function(*, *): Promise)|*} + */ + +/** + * @function + * @async + * @name BlockHeadersReader#subscribeToNew + * @returns {Promise} + */ + +/** + * @function + * @async + * @name BlockHeadersReader#readHistorical + * @returns {Promise} + */ diff --git a/packages/js-dapi-client/lib/DAPIClient.js b/packages/js-dapi-client/lib/DAPIClient.js new file mode 100644 index 00000000000..9176bb78905 --- /dev/null +++ b/packages/js-dapi-client/lib/DAPIClient.js @@ -0,0 +1,94 @@ +const EventEmitter = require('events'); + +const GrpcTransport = require('./transport/GrpcTransport/GrpcTransport'); +const JsonRpcTransport = require('./transport/JsonRpcTransport/JsonRpcTransport'); + +const CoreMethodsFacade = require('./methods/core/CoreMethodsFacade'); +const PlatformMethodsFacade = require('./methods/platform/PlatformMethodsFacade'); + +const createDAPIAddressProviderFromOptions = require('./dapiAddressProvider/createDAPIAddressProviderFromOptions'); +const requestJsonRpc = require('./transport/JsonRpcTransport/requestJsonRpc'); +const createGrpcTransportError = require('./transport/GrpcTransport/createGrpcTransportError'); +const createJsonTransportError = require('./transport/JsonRpcTransport/createJsonTransportError'); + +const BlockHeadersProvider = require('./BlockHeadersProvider/BlockHeadersProvider'); +const createBlockHeadersProviderFromOptions = require('./BlockHeadersProvider/createBlockHeadersProviderFromOptions'); + +const EVENTS = { + ERROR: 'error', +}; + +class DAPIClient extends EventEmitter { + /** + * @param {DAPIClientOptions} [options] + */ + constructor(options = {}) { + super(); + + this.options = { + network: 'testnet', + timeout: 10000, + retries: 5, + blockHeadersProviderOptions: BlockHeadersProvider.defaultOptions, + ...options, + }; + + this.dapiAddressProvider = createDAPIAddressProviderFromOptions(this.options); + + const grpcTransport = new GrpcTransport( + createDAPIAddressProviderFromOptions, + this.dapiAddressProvider, + createGrpcTransportError, + this.options, + ); + + const jsonRpcTransport = new JsonRpcTransport( + createDAPIAddressProviderFromOptions, + requestJsonRpc, + this.dapiAddressProvider, + createJsonTransportError, + this.options, + ); + + this.core = new CoreMethodsFacade(jsonRpcTransport, grpcTransport); + this.platform = new PlatformMethodsFacade(grpcTransport); + + this.initBlockHeadersProvider(); + } + + /** + * @private + */ + initBlockHeadersProvider() { + this.blockHeadersProvider = createBlockHeadersProviderFromOptions(this.options, this.core); + + this.blockHeadersProvider.on(BlockHeadersProvider.EVENTS.ERROR, (e) => { + this.emit(EVENTS.ERROR, e); + }); + + if (this.options.blockHeadersProviderOptions.autoStart) { + this.blockHeadersProvider.start().catch((e) => { + this.emit(EVENTS.ERROR, e); + }); + } + } +} + +DAPIClient.EVENTS = EVENTS; + +/** + * @typedef {DAPIClientOptions} DAPIClientOptions + * @property {DAPIAddressProvider} [dapiAddressProvider] + * @property {Array} [dapiAddresses] + * @property {Array} [seeds] + * @property {Array} [dapiAddressesWhiteList] + * @property {string|Network} [network=testnet] + * @property {number} [timeout=2000] + * @property {number} [retries=3] + * @property {number} [baseBanTime=60000] + * @property {boolean} [throwDeadlineExceeded] + * @property {BlockHeadersProvider} [blockHeadersProvider] + * @property {BlockHeadersProviderOptions} [blockHeadersProviderOptions] + */ + +module.exports = DAPIClient; diff --git a/packages/js-dapi-client/lib/SimplifiedMasternodeListProvider/SimplifiedMasternodeListProvider.js b/packages/js-dapi-client/lib/SimplifiedMasternodeListProvider/SimplifiedMasternodeListProvider.js new file mode 100644 index 00000000000..1b0c3cf5d8f --- /dev/null +++ b/packages/js-dapi-client/lib/SimplifiedMasternodeListProvider/SimplifiedMasternodeListProvider.js @@ -0,0 +1,111 @@ +const SimplifiedMNList = require('@dashevo/dashcore-lib/lib/deterministicmnlist/SimplifiedMNList'); +const SimplifiedMNListDiff = require('@dashevo/dashcore-lib/lib/deterministicmnlist/SimplifiedMNListDiff'); + +class SimplifiedMasternodeListProvider { + /** + * + * @param {JsonRpcTransport} jsonRpcTransport - JsonRpcTransport instance + * @param {object} [options] - Options + * @param {number} [options.updateInterval=60000] + * @param {string} [options.network] + */ + constructor(jsonRpcTransport, options = {}) { + this.jsonRpcTransport = jsonRpcTransport; + + this.options = { + updateInterval: 60000, + ...options, + }; + + this.simplifiedMNList = new SimplifiedMNList(undefined, this.options.network); + + this.lastUpdateDate = 0; + + this.baseBlockHash = SimplifiedMasternodeListProvider.NULL_HASH; + } + + /** + * Returns simplified masternode list + * + * @returns {Promise} + */ + async getSimplifiedMNList() { + if (this.needsUpdate()) { + await this.updateMasternodeList(); + } + + return this.simplifiedMNList; + } + + /** + * Checks whether simplified masternode list needs update + * + * @private + * @returns {boolean} + */ + needsUpdate() { + return Date.now() - this.options.updateInterval > this.lastUpdateDate; + } + + /** + * Updates simplified masternodes list. No need to call it manually + * + * @private + */ + async updateMasternodeList() { + const diff = await this.getSimplifiedMNListDiff(); + + try { + this.simplifiedMNList.applyDiff(diff); + } catch (e) { + if (e.message === 'Cannot apply diff: previous blockHash needs to equal the new diff\'s baseBlockHash') { + this.reset(); + + await this.updateMasternodeList(); + + return; + } + + throw e; + } + + this.baseBlockHash = diff.blockHash; + + this.lastUpdateDate = Date.now(); + } + + /** + * Fetches masternode diff from DAPI + * + * @private + * @returns {Promise} + */ + async getSimplifiedMNListDiff() { + const blockHash = await this.jsonRpcTransport.request('getBestBlockHash'); + + const rawSimplifiedMNListDiff = await this.jsonRpcTransport.request( + 'getMnListDiff', + { baseBlockHash: this.baseBlockHash, blockHash }, + { addresses: [this.jsonRpcTransport.getLastUsedAddress()] }, + ); + + return new SimplifiedMNListDiff(rawSimplifiedMNListDiff, this.options.network); + } + + /** + * Reset simplifiedMNList + * + * @private + */ + reset() { + this.simplifiedMNList = new SimplifiedMNList(undefined, this.options.network); + + this.lastUpdateDate = 0; + + this.baseBlockHash = SimplifiedMasternodeListProvider.NULL_HASH; + } +} + +SimplifiedMasternodeListProvider.NULL_HASH = '0000000000000000000000000000000000000000000000000000000000000000'; + +module.exports = SimplifiedMasternodeListProvider; diff --git a/packages/js-dapi-client/lib/dapiAddressProvider/DAPIAddress.js b/packages/js-dapi-client/lib/dapiAddressProvider/DAPIAddress.js new file mode 100644 index 00000000000..56249aee6c5 --- /dev/null +++ b/packages/js-dapi-client/lib/dapiAddressProvider/DAPIAddress.js @@ -0,0 +1,183 @@ +const DAPIAddressHostMissingError = require('./errors/DAPIAddressHostMissingError'); + +class DAPIAddress { + /** + * @param {RawDAPIAddress|DAPIAddress|string} address + */ + constructor(address) { + if (address instanceof DAPIAddress) { + return new DAPIAddress(address.toJSON()); + } + + if (typeof address === 'string') { + const [host, httpPort, grpcPort] = address.split(':'); + + // eslint-disable-next-line no-param-reassign + address = { + host, + httpPort: httpPort ? parseInt(httpPort, 10) : DAPIAddress.DEFAULT_HTTP_PORT, + grpcPort: grpcPort ? parseInt(grpcPort, 10) : DAPIAddress.DEFAULT_GRPC_PORT, + }; + } + + if (!address.host) { + throw new DAPIAddressHostMissingError(); + } + + this.host = address.host; + this.httpPort = address.httpPort || DAPIAddress.DEFAULT_HTTP_PORT; + this.grpcPort = address.grpcPort || DAPIAddress.DEFAULT_GRPC_PORT; + this.proRegTxHash = address.proRegTxHash; + + this.banCount = 0; + this.banStartTime = undefined; + } + + /** + * Get host + * + * @returns {string} + */ + getHost() { + return this.host; + } + + /** + * Set host + * + * @param {string} host + * @returns {DAPIAddress} + */ + setHost(host) { + this.host = host; + + return this; + } + + /** + * Get HTTP port + * + * @returns {number} + */ + getHttpPort() { + return this.httpPort; + } + + /** + * Set HTTP port + * + * @param {number} port + * @returns {DAPIAddress} + */ + setHttpPort(port) { + this.httpPort = port; + + return this; + } + + /** + * Get gRPC port + * + * @returns {number} + */ + getGrpcPort() { + return this.grpcPort; + } + + /** + * Set gRPC port + * + * @param {number} port + * @returns {DAPIAddress} + */ + setGrpcPort(port) { + this.grpcPort = port; + + return this; + } + + /** + * Get ProRegTx hash + * + * @returns {string} + */ + getProRegTxHash() { + return this.proRegTxHash; + } + + /** + * @returns {number} + */ + getBanStartTime() { + return this.banStartTime; + } + + /** + * @returns {number} + */ + getBanCount() { + return this.banCount; + } + + /** + * Mark address as banned + * + * @returns {DAPIAddress} + */ + markAsBanned() { + this.banCount += 1; + this.banStartTime = Date.now(); + + return this; + } + + /** + * Mark address as live + * + * @returns {DAPIAddress} + */ + markAsLive() { + this.banCount = 0; + this.banStartTime = undefined; + + return this; + } + + /** + * @returns {boolean} + */ + isBanned() { + return this.banCount > 0; + } + + /** + * Return DAPIAddress as plain object + * + * @returns {RawDAPIAddress} + */ + toJSON() { + return { + host: this.getHost(), + httpPort: this.getHttpPort(), + grpcPort: this.getGrpcPort(), + proRegTxHash: this.getProRegTxHash(), + }; + } + + toString() { + return `${this.getHost()}:${this.getHttpPort()}:${this.getGrpcPort()}`; + } +} + +DAPIAddress.DEFAULT_HTTP_PORT = 3000; +DAPIAddress.DEFAULT_GRPC_PORT = 3010; + +/** + * @typedef {object} RawDAPIAddress + * @property {string} host + * @property {number} [httpPort=3000] + * @property {number} [grpcPort=3010] + * @property {string} [proRegTxHash] + */ + +module.exports = DAPIAddress; diff --git a/packages/js-dapi-client/lib/dapiAddressProvider/DAPIAddressProviderInterface.js b/packages/js-dapi-client/lib/dapiAddressProvider/DAPIAddressProviderInterface.js new file mode 100644 index 00000000000..b9ce46d5f5f --- /dev/null +++ b/packages/js-dapi-client/lib/dapiAddressProvider/DAPIAddressProviderInterface.js @@ -0,0 +1,17 @@ +/** + * @interface DAPIAddressProvider + */ + +/** + * @function + * @async + * @name DAPIAddressProvider#getLiveAddress + * @returns {Promise} + */ + +/** + * @function + * @async + * @name DAPIAddressProvider#hasLiveAddresses + * @returns {boolean} + */ diff --git a/packages/js-dapi-client/lib/dapiAddressProvider/ListDAPIAddressProvider.js b/packages/js-dapi-client/lib/dapiAddressProvider/ListDAPIAddressProvider.js new file mode 100644 index 00000000000..beecf82efea --- /dev/null +++ b/packages/js-dapi-client/lib/dapiAddressProvider/ListDAPIAddressProvider.js @@ -0,0 +1,97 @@ +const sample = require('lodash.sample'); +const networks = require('@dashevo/dashcore-lib/lib/networks'); + +class ListDAPIAddressProvider { + /** + * @param {DAPIAddress[]} addresses + * @param {DAPIClientOptions} [options] + */ + constructor(addresses, options = {}) { + this.options = { + baseBanTime: 60 * 1000, + ...options, + }; + + this.addresses = addresses; + } + + /** + * Get random address + * + * @returns {Promise} + */ + async getLiveAddress() { + const liveAddresses = this.getLiveAddresses(); + + const liveAddress = sample(liveAddresses); + + if (liveAddress === undefined) { + return liveAddress; + } + + // This is a temporary fix for a localhost masternode. + // On mac os, internal docker IP is used to register masternode, and it's + // not really possible to bind to that address, so that workaround is introduced. + const network = networks.get(this.options.network); + if (network && network.regtestEnabled) { + liveAddress.host = '127.0.0.1'; + } + + return liveAddress; + } + + /** + * Get all addresses + * + * @returns {DAPIAddress[]} + */ + getAllAddresses() { + return this.addresses; + } + + /** + * Set addresses + * + * @param {DAPIAddress[]} addresses + * @returns {ListDAPIAddressProvider} + */ + setAddresses(addresses) { + this.addresses = addresses; + + return this; + } + + /** + * Check if we have live addresses left + * + * @returns {Promise} - True if there are live address left + */ + async hasLiveAddresses() { + const liveAddresses = this.getLiveAddresses(); + + return liveAddresses.length > 0; + } + + /** + * Get live addresses + * + * @returns {DAPIAddress[]} + */ + getLiveAddresses() { + const now = Date.now(); + + return this.addresses.filter((address) => { + if (!address.isBanned()) { + return true; + } + + // Exponentially increase ban time based on ban count + const coefficient = Math.exp(address.getBanCount() - 1); + const banPeriod = Math.floor(coefficient) * this.options.baseBanTime; + + return now > address.getBanStartTime() + banPeriod; + }); + } +} + +module.exports = ListDAPIAddressProvider; diff --git a/packages/js-dapi-client/lib/dapiAddressProvider/SimplifiedMasternodeListDAPIAddressProvider.js b/packages/js-dapi-client/lib/dapiAddressProvider/SimplifiedMasternodeListDAPIAddressProvider.js new file mode 100644 index 00000000000..cacacaec60f --- /dev/null +++ b/packages/js-dapi-client/lib/dapiAddressProvider/SimplifiedMasternodeListDAPIAddressProvider.js @@ -0,0 +1,70 @@ +const DAPIAddress = require('./DAPIAddress'); + +class SimplifiedMasternodeListDAPIAddressProvider { + /** + * @param {SimplifiedMasternodeListProvider} smlProvider + * @param {ListDAPIAddressProvider} listDAPIAddressProvider + * @param {DAPIAddress[]} addressWhiteList + */ + constructor(smlProvider, listDAPIAddressProvider, addressWhiteList) { + this.smlProvider = smlProvider; + this.listDAPIAddressProvider = listDAPIAddressProvider; + this.addressWhiteStrings = addressWhiteList.map((dapiAddress) => dapiAddress.toString()); + } + + /** + * Get random live DAPI address from SML + * + * @returns {Promise} + */ + async getLiveAddress() { + const sml = await this.smlProvider.getSimplifiedMNList(); + const validMasternodeList = sml.getValidMasternodesList(); + + const addressesByRegProTxHashes = {}; + this.listDAPIAddressProvider.getAllAddresses().forEach((address) => { + if (!address.getProRegTxHash()) { + return; + } + + addressesByRegProTxHashes[address.getProRegTxHash()] = address; + }); + + const updatedAddresses = validMasternodeList.map((smlEntry) => { + let address = addressesByRegProTxHashes[smlEntry.proRegTxHash]; + + if (!address) { + address = new DAPIAddress({ + host: smlEntry.getIp(), + proRegTxHash: smlEntry.proRegTxHash, + }); + } else { + address.setHost(smlEntry.getIp()); + } + + return address; + }); + + let filteredAddresses = updatedAddresses; + if (this.addressWhiteStrings.length > 0) { + filteredAddresses = updatedAddresses.filter((dapiAddress) => ( + this.addressWhiteStrings.includes(dapiAddress.toString()) + )); + } + + this.listDAPIAddressProvider.setAddresses(filteredAddresses); + + return this.listDAPIAddressProvider.getLiveAddress(); + } + + /** + * Check if we have live addresses left + * + * @returns {Promise} + */ + async hasLiveAddresses() { + return this.listDAPIAddressProvider.hasLiveAddresses(); + } +} + +module.exports = SimplifiedMasternodeListDAPIAddressProvider; diff --git a/packages/js-dapi-client/lib/dapiAddressProvider/createDAPIAddressProviderFromOptions.js b/packages/js-dapi-client/lib/dapiAddressProvider/createDAPIAddressProviderFromOptions.js new file mode 100644 index 00000000000..73403ddc240 --- /dev/null +++ b/packages/js-dapi-client/lib/dapiAddressProvider/createDAPIAddressProviderFromOptions.js @@ -0,0 +1,118 @@ +const networks = require('@dashevo/dashcore-lib/lib/networks'); + +const DAPIAddress = require('./DAPIAddress'); + +const ListDAPIAddressProvider = require('./ListDAPIAddressProvider'); + +const SimplifiedMasternodeListProvider = require('../SimplifiedMasternodeListProvider/SimplifiedMasternodeListProvider'); +const SimplifiedMasternodeListDAPIAddressProvider = require('./SimplifiedMasternodeListDAPIAddressProvider'); + +const JsonRpcTransport = require('../transport/JsonRpcTransport/JsonRpcTransport'); +const requestJsonRpc = require('../transport/JsonRpcTransport/requestJsonRpc'); +const createJsonTransportError = require('../transport/JsonRpcTransport/createJsonTransportError'); + +const DAPIClientError = require('../errors/DAPIClientError'); + +const networkConfigs = require('../networkConfigs'); + +/** + * @typedef {createDAPIAddressProviderFromOptions} + * @param {DAPIClientOptions} options + * @returns { + * DAPIAddressProvider| + * ListDAPIAddressProvider| + * SimplifiedMasternodeListDAPIAddressProvider| + * null + * } + */ +function createDAPIAddressProviderFromOptions(options) { + if (options.network && !networks.get(options.network)) { + throw new DAPIClientError(`Invalid network '${options.network}'`); + } + + if (options.dapiAddressProvider) { + if (options.dapiAddresses) { + throw new DAPIClientError("Can't use 'dapiAddresses' with 'dapiAddressProvider' option"); + } + + if (options.seeds) { + throw new DAPIClientError("Can't use 'seeds' with 'dapiAddressProvider' option"); + } + + if (options.dapiAddressesWhiteList) { + throw new DAPIClientError("Can't use 'dapiAddressesWhiteList' with 'dapiAddressProvider' option"); + } + + return options.dapiAddressProvider; + } + + if (options.dapiAddresses) { + if (options.seeds) { + throw new DAPIClientError("Can't use 'seeds' with 'dapiAddresses' option"); + } + + if (options.dapiAddressesWhiteList) { + throw new DAPIClientError("Can't use 'dapiAddressesWhiteList' with 'dapiAddresses' option"); + } + + return new ListDAPIAddressProvider( + options.dapiAddresses.map((rawAddress) => new DAPIAddress(rawAddress)), + options, + ); + } + + if (options.seeds) { + let dapiAddressesWhiteList = options.dapiAddressesWhiteList || []; + + // Since we don't have PoSe atm, 3rd party masternodes sometimes provide wrong data + // that breaks test suite and application logic. Temporary solution is to hardcode + // reliable DCG testnet masternodes to connect. Should be removed when PoSe is introduced. + const network = networks.get(options.network); + let isRegtest = false; + if (network) { + isRegtest = network.regtestEnabled; + } + + if (options.network === 'testnet' && dapiAddressesWhiteList.length === 0 && !isRegtest) { + dapiAddressesWhiteList = networkConfigs.testnet.dapiAddressesWhiteList; + } + + const listDAPIAddressProvider = new ListDAPIAddressProvider( + options.seeds.map((rawAddress) => new DAPIAddress(rawAddress)), + options, + ); + + const jsonRpcTransport = new JsonRpcTransport( + createDAPIAddressProviderFromOptions, + requestJsonRpc, + listDAPIAddressProvider, + createJsonTransportError, + options, + ); + + const smlProvider = new SimplifiedMasternodeListProvider( + jsonRpcTransport, + { network: options.network }, + ); + + return new SimplifiedMasternodeListDAPIAddressProvider( + smlProvider, + listDAPIAddressProvider, + dapiAddressesWhiteList.map((rawAddress) => new DAPIAddress(rawAddress)), + ); + } + + if (options.network) { + if (!networkConfigs[options.network]) { + throw new DAPIClientError(`There is no connection config for network '${options.network}'`); + } + + const networkConfig = { ...options, ...networkConfigs[options.network] }; + + return createDAPIAddressProviderFromOptions(networkConfig); + } + + return null; +} + +module.exports = createDAPIAddressProviderFromOptions; diff --git a/packages/js-dapi-client/lib/dapiAddressProvider/errors/DAPIAddressHostMissingError.js b/packages/js-dapi-client/lib/dapiAddressProvider/errors/DAPIAddressHostMissingError.js new file mode 100644 index 00000000000..a8feb72082e --- /dev/null +++ b/packages/js-dapi-client/lib/dapiAddressProvider/errors/DAPIAddressHostMissingError.js @@ -0,0 +1,9 @@ +const DAPIClientError = require('../../errors/DAPIClientError'); + +class DAPIAddressHostMissingError extends DAPIClientError { + constructor() { + super('Host is required for DAPI address'); + } +} + +module.exports = DAPIAddressHostMissingError; diff --git a/packages/js-dapi-client/lib/errors/DAPIClientError.js b/packages/js-dapi-client/lib/errors/DAPIClientError.js new file mode 100644 index 00000000000..0d55cc2b212 --- /dev/null +++ b/packages/js-dapi-client/lib/errors/DAPIClientError.js @@ -0,0 +1,17 @@ +class DAPIClientError extends Error { + /** + * + * @param {string} message + */ + constructor(message) { + super(message); + + this.name = this.constructor.name; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + } +} + +module.exports = DAPIClientError; diff --git a/packages/js-dapi-client/lib/index.js b/packages/js-dapi-client/lib/index.js new file mode 100644 index 00000000000..d6b2f77704f --- /dev/null +++ b/packages/js-dapi-client/lib/index.js @@ -0,0 +1,9 @@ +const DAPIClient = require('./DAPIClient'); + +const NotFoundError = require('./transport/GrpcTransport/errors/NotFoundError'); + +DAPIClient.Errors = { + NotFoundError, +}; + +module.exports = DAPIClient; diff --git a/packages/js-dapi-client/lib/methods/core/CoreMethodsFacade.js b/packages/js-dapi-client/lib/methods/core/CoreMethodsFacade.js new file mode 100644 index 00000000000..306add3c953 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/core/CoreMethodsFacade.js @@ -0,0 +1,37 @@ +const broadcastTransactionFactory = require('./broadcastTransactionFactory'); +const generateToAddressFactory = require('./generateToAddressFactory'); +const getBestBlockHashFactory = require('./getBestBlockHashFactory'); +const getBlockByHashFactory = require('./getBlockByHashFactory'); +const getBlockByHeightFactory = require('./getBlockByHeightFactory'); +const getBlockHashFactory = require('./getBlockHashFactory'); +const getMnListDiffFactory = require('./getMnListDiffFactory'); +const getStatusFactory = require('./getStatusFactory'); +const getTransactionFactory = require('./getTransaction/getTransactionFactory'); +const subscribeToTransactionsWithProofsFactory = require('./subscribeToTransactionsWithProofsFactory'); +const subscribeToBlockHeadersWithChainLocksFactory = require('./subscribeToBlockHeadersWithChainLocksFactory'); + +class CoreMethodsFacade { + /** + * @param {JsonRpcTransport} jsonRpcTransport + * @param {GrpcTransport} grpcTransport + */ + constructor(jsonRpcTransport, grpcTransport) { + this.broadcastTransaction = broadcastTransactionFactory(grpcTransport); + this.generateToAddress = generateToAddressFactory(jsonRpcTransport); + this.getBestBlockHash = getBestBlockHashFactory(jsonRpcTransport); + this.getBlockByHash = getBlockByHashFactory(grpcTransport); + this.getBlockByHeight = getBlockByHeightFactory(grpcTransport); + this.getBlockHash = getBlockHashFactory(jsonRpcTransport); + this.getMnListDiff = getMnListDiffFactory(jsonRpcTransport); + this.getStatus = getStatusFactory(grpcTransport); + this.getTransaction = getTransactionFactory(grpcTransport); + this.subscribeToTransactionsWithProofs = subscribeToTransactionsWithProofsFactory( + grpcTransport, + ); + this.subscribeToBlockHeadersWithChainLocks = subscribeToBlockHeadersWithChainLocksFactory( + grpcTransport, + ); + } +} + +module.exports = CoreMethodsFacade; diff --git a/packages/js-dapi-client/lib/methods/core/broadcastTransactionFactory.js b/packages/js-dapi-client/lib/methods/core/broadcastTransactionFactory.js new file mode 100644 index 00000000000..c6474da198a --- /dev/null +++ b/packages/js-dapi-client/lib/methods/core/broadcastTransactionFactory.js @@ -0,0 +1,46 @@ +const { + v0: { + CorePromiseClient, + BroadcastTransactionRequest, + }, +} = require('@dashevo/dapi-grpc'); + +/** + * @param {GrpcTransport} grpcTransport + * @returns {broadcastTransaction} + */ +function broadcastTransactionFactory(grpcTransport) { + /** + * Broadcast Transaction + * + * @typedef {broadcastTransaction} + * @param {Buffer} transaction + * @param {DAPIClientOptions & BroadcastTransactionOptions} [options] + * @returns {string} + */ + async function broadcastTransaction(transaction, options = {}) { + const broadcastTransactionRequest = new BroadcastTransactionRequest(); + broadcastTransactionRequest.setTransaction(transaction); + broadcastTransactionRequest.setAllowHighFees(options.allowHighFees || false); + broadcastTransactionRequest.setBypassLimits(options.bypassLimits || false); + + const response = await grpcTransport.request( + CorePromiseClient, + 'broadcastTransaction', + broadcastTransactionRequest, + options, + ); + + return response.getTransactionId(); + } + + return broadcastTransaction; +} + +/** + * @typedef {object} BroadcastTransactionOptions + * @property {boolean} [allowHighFees=false] + * @property {boolean} [bypassLimits=false] + */ + +module.exports = broadcastTransactionFactory; diff --git a/packages/js-dapi-client/lib/methods/core/generateToAddressFactory.js b/packages/js-dapi-client/lib/methods/core/generateToAddressFactory.js new file mode 100644 index 00000000000..27e422567a1 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/core/generateToAddressFactory.js @@ -0,0 +1,26 @@ +/** + * @param {JsonRpcTransport} jsonRpcTransport + * @returns {generateToAddress} + */ +function generateToAddressFactory(jsonRpcTransport) { + /** + * ONLY FOR TESTING PURPOSES WITH REGTEST. WILL NOT WORK ON TESTNET/LIVENET. + * + * @typedef {generateToAddress} + * @param {number} blocksNumber - Number of blocks to generate + * @param {string} address - The address that will receive the newly generated Dash + * @param {DAPIClientOptions} [options] + * @returns {Promise} - block hashes + */ + function generateToAddress(blocksNumber, address, options = {}) { + return jsonRpcTransport.request( + 'generateToAddress', + { blocksNumber, address }, + options, + ); + } + + return generateToAddress; +} + +module.exports = generateToAddressFactory; diff --git a/packages/js-dapi-client/lib/methods/core/getBestBlockHashFactory.js b/packages/js-dapi-client/lib/methods/core/getBestBlockHashFactory.js new file mode 100644 index 00000000000..50489626145 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/core/getBestBlockHashFactory.js @@ -0,0 +1,21 @@ +/** + * + * @param {JsonRpcTransport} jsonRpcTransport + * @returns {getBestBlockHash} + */ +function getBestBlockHashFactory(jsonRpcTransport) { + /** + * Returns block hash of chaintip + * + * @typedef {getBestBlockHash} + * @param {DAPIClientOptions} [options] + * @returns {Promise} + */ + function getBestBlockHash(options = {}) { + return jsonRpcTransport.request('getBestBlockHash', {}, options); + } + + return getBestBlockHash; +} + +module.exports = getBestBlockHashFactory; diff --git a/packages/js-dapi-client/lib/methods/core/getBlockByHashFactory.js b/packages/js-dapi-client/lib/methods/core/getBlockByHashFactory.js new file mode 100644 index 00000000000..7c6293d72c1 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/core/getBlockByHashFactory.js @@ -0,0 +1,39 @@ +const { + v0: { + GetBlockRequest, + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +/** + * @param {GrpcTransport} grpcTransport + * @returns {getBlockByHash} + */ +function getBlockByHashFactory(grpcTransport) { + /** + * Get block by hash + * + * @typedef {getBlockByHash} + * @param {string} hash + * @param {DAPIClientOptions} [options] + * @returns {Promise} + */ + async function getBlockByHash(hash, options = {}) { + const getBlockRequest = new GetBlockRequest(); + getBlockRequest.setHash(hash); + + const response = await grpcTransport.request( + CorePromiseClient, + 'getBlock', + getBlockRequest, + options, + ); + const blockBinaryArray = response.getBlock(); + + return Buffer.from(blockBinaryArray); + } + + return getBlockByHash; +} + +module.exports = getBlockByHashFactory; diff --git a/packages/js-dapi-client/lib/methods/core/getBlockByHeightFactory.js b/packages/js-dapi-client/lib/methods/core/getBlockByHeightFactory.js new file mode 100644 index 00000000000..39a5f0bbe38 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/core/getBlockByHeightFactory.js @@ -0,0 +1,40 @@ +const { + v0: { + GetBlockRequest, + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +/** + * @param {GrpcTransport} grpcTransport + * @returns {getBlockByHeight} + */ +function getBlockByHeightFactory(grpcTransport) { + /** + * Get block by height + * + * @typedef {getBlockByHeight} + * @param {number} height + * @param {DAPIClientOptions} [options] + * @returns {Promise} + */ + async function getBlockByHeight(height, options = {}) { + const getBlockRequest = new GetBlockRequest(); + getBlockRequest.setHeight(height); + + const response = await grpcTransport.request( + CorePromiseClient, + 'getBlock', + getBlockRequest, + options, + ); + + const blockBinaryArray = response.getBlock(); + + return Buffer.from(blockBinaryArray); + } + + return getBlockByHeight; +} + +module.exports = getBlockByHeightFactory; diff --git a/packages/js-dapi-client/lib/methods/core/getBlockHashFactory.js b/packages/js-dapi-client/lib/methods/core/getBlockHashFactory.js new file mode 100644 index 00000000000..8e798a35e94 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/core/getBlockHashFactory.js @@ -0,0 +1,21 @@ +/** + * @param {JsonRpcTransport} jsonRpcTransport + * @returns {getBlockHash} + */ +function getBlockHashFactory(jsonRpcTransport) { + /** + * Returns block hash for the given height + * + * @typedef {getBlockHash} + * @param {number} height + * @param {DAPIClientOptions} [options] + * @returns {Promise} - block hash + */ + function getBlockHash(height, options = {}) { + return jsonRpcTransport.request('getBlockHash', { height }, options); + } + + return getBlockHash; +} + +module.exports = getBlockHashFactory; diff --git a/packages/js-dapi-client/lib/methods/core/getMnListDiffFactory.js b/packages/js-dapi-client/lib/methods/core/getMnListDiffFactory.js new file mode 100644 index 00000000000..0d9bbef841c --- /dev/null +++ b/packages/js-dapi-client/lib/methods/core/getMnListDiffFactory.js @@ -0,0 +1,22 @@ +/** + * @param {JsonRpcTransport} jsonRpcTransport + * @returns {getMnListDiff} + */ +function getMnListDiffFactory(jsonRpcTransport) { + /** + * Get deterministic masternodelist diff + * + * @typedef {getMnListDiff} + * @param {string} baseBlockHash - hash or height of start block + * @param {string} blockHash - hash or height of end block + * @param {DAPIClientOptions} [options] + * @returns {Promise} + */ + function getMnListDiff(baseBlockHash, blockHash, options = {}) { + return jsonRpcTransport.request('getMnListDiff', { baseBlockHash, blockHash }, options); + } + + return getMnListDiff; +} + +module.exports = getMnListDiffFactory; diff --git a/packages/js-dapi-client/lib/methods/core/getStatusFactory.js b/packages/js-dapi-client/lib/methods/core/getStatusFactory.js new file mode 100644 index 00000000000..0a43d066ad4 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/core/getStatusFactory.js @@ -0,0 +1,69 @@ +const { + v0: { + GetStatusRequest, + GetStatusResponse, + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +/** + * @param {GrpcTransport} grpcTransport + * @returns {getStatus} + */ +function getStatusFactory(grpcTransport) { + /** + * Get Core chain status + * + * @typedef {getStatus} + * @param {DAPIClientOptions} [options] + * @returns {Promise} + */ + async function getStatus(options = {}) { + const getStatusRequest = new GetStatusRequest(); + + const response = await grpcTransport.request( + CorePromiseClient, + 'getStatus', + getStatusRequest, + options, + ); + + const responseObject = response.toObject(); + + // Respond with Buffers instead of base64 for binary fields + + if (response.getChain()) { + if (response.getChain().getBestBlockHash()) { + responseObject.chain.bestBlockHash = Buffer.from(response.getChain().getBestBlockHash()); + } + + if (response.getChain().getChainWork()) { + responseObject.chain.chainWork = Buffer.from(response.getChain().getChainWork()); + } + } + + if (response.getMasternode()) { + if (response.getMasternode().getProTxHash()) { + responseObject.masternode.proTxHash = Buffer.from(response.getMasternode().getProTxHash()); + } + } + + // Respond with constant names instead of constant values + + responseObject.status = Object.keys(GetStatusResponse.Status) + .find((key) => GetStatusResponse.Status[key] === responseObject.status); + + if (responseObject.masternode) { + responseObject.masternode.status = Object.keys(GetStatusResponse.Masternode.Status) + .find((key) => ( + GetStatusResponse.Masternode.Status[key] === responseObject.masternode.status + )); + } + + return responseObject; + } + + return getStatus; +} + +module.exports = getStatusFactory; diff --git a/packages/js-dapi-client/lib/methods/core/getTransaction/GetTransactionResponse.js b/packages/js-dapi-client/lib/methods/core/getTransaction/GetTransactionResponse.js new file mode 100644 index 00000000000..d3d280da9e4 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/core/getTransaction/GetTransactionResponse.js @@ -0,0 +1,94 @@ +const InvalidResponseError = require('../../platform/response/errors/InvalidResponseError'); + +class GetTransactionResponse { + /** + * + * @param {object} properties + * @param {Buffer} properties.transaction + * @param {Buffer} properties.blockHash + * @param {number} properties.height + * @param {number} properties.confirmations + * @param {boolean} properties.isInstantLocked + * @param {boolean} properties.isChainLocked + */ + constructor(properties) { + this.transaction = properties.transaction; + this.blockHash = properties.blockHash; + this.height = properties.height; + this.confirmations = properties.confirmations; + this.instantLocked = properties.isInstantLocked; + this.chainLocked = properties.isChainLocked; + } + + /** + * Get transaction + * + * @returns {Buffer} + */ + getTransaction() { + return this.transaction; + } + + /** + * Get block hash + * + * @returns {Buffer} + */ + getBlockHash() { + return this.blockHash; + } + + /** + * Get height + * + * @returns {number} + */ + getHeight() { + return this.height; + } + + /** + * Get number of confirmations + * + * @returns {number} + */ + getConfirmations() { + return this.confirmations; + } + + /** + * Is transaction instant locked + * + * @returns {boolean} + */ + isInstantLocked() { + return this.instantLocked; + } + + /** + * Is transaction chain locked + * + * @returns {boolean} + */ + isChainLocked() { + return this.chainLocked; + } + + static createFromProto(proto) { + const transactionBinaryArray = proto.getTransaction(); + if (!transactionBinaryArray) { + throw new InvalidResponseError('Transaction is not defined'); + } + + return new GetTransactionResponse({ + transaction: Buffer.from(transactionBinaryArray), + blockHash: Buffer.from(proto.getBlockHash()), + height: proto.getHeight(), + confirmations: proto.getConfirmations(), + isInstantLocked: proto.getIsInstantLocked(), + isChainLocked: proto.getIsChainLocked(), + }); + } +} + +module.exports = GetTransactionResponse; diff --git a/packages/js-dapi-client/lib/methods/core/getTransaction/getTransactionFactory.js b/packages/js-dapi-client/lib/methods/core/getTransaction/getTransactionFactory.js new file mode 100644 index 00000000000..b8897f923b9 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/core/getTransaction/getTransactionFactory.js @@ -0,0 +1,59 @@ +const { + v0: { + GetTransactionRequest, + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +const GetTransactionResponse = require('./GetTransactionResponse'); +const InvalidResponseError = require('../../platform/response/errors/InvalidResponseError'); + +/** + * @param {GrpcTransport} grpcTransport + * @returns {getTransaction} + */ +function getTransactionFactory(grpcTransport) { + /** + * Get Transaction by ID + * + * @typedef {getTransaction} + * @param {string} id + * @param {DAPIClientOptions} [options] + * @returns {Promise} + */ + async function getTransaction(id, options = {}) { + const getTransactionRequest = new GetTransactionRequest(); + getTransactionRequest.setId(id); + + let lastError; + + // TODO: simple retry before the dapi versioning is properly implemented + for (let i = 0; i < 3; i += 1) { + try { + // eslint-disable-next-line no-await-in-loop + const response = await grpcTransport.request( + CorePromiseClient, + 'getTransaction', + getTransactionRequest, + options, + ); + + return GetTransactionResponse.createFromProto(response); + } catch (e) { + if (e instanceof InvalidResponseError) { + lastError = e; + } else { + throw e; + } + } + } + + // If we made it past the cycle it means that the retry didn't work, + // and we're throwing the last error encountered + throw lastError; + } + + return getTransaction; +} + +module.exports = getTransactionFactory; diff --git a/packages/js-dapi-client/lib/methods/core/subscribeToBlockHeadersWithChainLocksFactory.js b/packages/js-dapi-client/lib/methods/core/subscribeToBlockHeadersWithChainLocksFactory.js new file mode 100644 index 00000000000..397b3ffd939 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/core/subscribeToBlockHeadersWithChainLocksFactory.js @@ -0,0 +1,69 @@ +const { + v0: { + BlockHeadersWithChainLocksRequest, + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +const DAPIClientError = require('../../errors/DAPIClientError'); + +/** + * @param {GrpcTransport} grpcTransport + * @returns {subscribeToBlockHeadersWithChainLocks} + */ +function subscribeToBlockHeadersWithChainLocksFactory(grpcTransport) { + /** + * @typedef {subscribeToBlockHeadersWithChainLocks} + * @param {DAPIClientOptions & subscribeToBlockHeadersWithChainLocksOptions} [options] + * @returns { + * EventEmitter|!grpc.web.ClientReadableStream + * } + */ + async function subscribeToBlockHeadersWithChainLocks(options = { }) { + // eslint-disable-next-line no-param-reassign + options = { + count: 0, + // Override global timeout option + // and timeout for this method by default + timeout: undefined, + ...options, + }; + + if (options.fromBlockHeight === 0) { + throw new DAPIClientError('Invalid argument: minimum value for `fromBlockHeight` is 1'); + } + + const request = new BlockHeadersWithChainLocksRequest(); + + if (options.fromBlockHeight !== undefined) { + request.setFromBlockHeight(options.fromBlockHeight); + } + + if (options.fromBlockHash) { + request.setFromBlockHash( + Buffer.from(options.fromBlockHash, 'hex'), + ); + } + + request.setCount(options.count); + + return grpcTransport.request( + CorePromiseClient, + 'subscribeToBlockHeadersWithChainLocks', + request, + options, + ); + } + + return subscribeToBlockHeadersWithChainLocks; +} + +/** + * @typedef {object} subscribeToBlockHeadersWithChainLocksOptions + * @property {string} [fromBlockHash] - Specifies block hash to start syncing from + * @property {number} [fromBlockHeight] - Specifies block height to start syncing from + * @property {number} [count=0] - Number of blocks to sync, + * if set to 0 syncing is continuously sends new data as well + */ + +module.exports = subscribeToBlockHeadersWithChainLocksFactory; diff --git a/packages/js-dapi-client/lib/methods/core/subscribeToTransactionsWithProofsFactory.js b/packages/js-dapi-client/lib/methods/core/subscribeToTransactionsWithProofsFactory.js new file mode 100644 index 00000000000..5678042ecfc --- /dev/null +++ b/packages/js-dapi-client/lib/methods/core/subscribeToTransactionsWithProofsFactory.js @@ -0,0 +1,93 @@ +const { + v0: { + TransactionsWithProofsRequest, + CorePromiseClient, + BloomFilter: BloomFilterMessage, + }, +} = require('@dashevo/dapi-grpc'); + +const DAPIClientError = require('../../errors/DAPIClientError'); + +/** + * @param {GrpcTransport} grpcTransport + * @returns {subscribeToTransactionsWithProofs} + */ +function subscribeToTransactionsWithProofsFactory(grpcTransport) { + /** + * @typedef {subscribeToTransactionsWithProofs} + * @param {object} bloomFilter + * @param {Uint8Array|Array} bloomFilter.vData - The filter itself is simply a bit + * field of arbitrary byte-aligned size. The maximum size is 36,000 bytes. + * @param {number} bloomFilter.nHashFuncs - The number of hash functions to use in this filter. + * The maximum value allowed in this field is 50. + * @param {number} bloomFilter.nTweak - A random value to add to the seed value in the + * hash function used by the bloom filter. + * @param {number} bloomFilter.nFlags - A set of flags that control how matched items + * are added to the filter. + * @param {DAPIClientOptions & subscribeToTransactionsWithProofsOptions} [options] + * @returns { + * EventEmitter|!grpc.web.ClientReadableStream + * } + */ + async function subscribeToTransactionsWithProofs(bloomFilter, options = { }) { + // eslint-disable-next-line no-param-reassign + options = { + count: 0, + // Override global timeout option + // and timeout for this method by default + timeout: undefined, + ...options, + }; + + if (options.fromBlockHeight === 0) { + throw new DAPIClientError('Invalid argument: minimum value for `fromBlockHeight` is 1'); + } + + const bloomFilterMessage = new BloomFilterMessage(); + + let { vData } = bloomFilter; + + if (Array.isArray(vData)) { + vData = new Uint8Array(vData); + } + + bloomFilterMessage.setVData(vData); + bloomFilterMessage.setNHashFuncs(bloomFilter.nHashFuncs); + bloomFilterMessage.setNTweak(bloomFilter.nTweak); + bloomFilterMessage.setNFlags(bloomFilter.nFlags); + + const request = new TransactionsWithProofsRequest(); + request.setBloomFilter(bloomFilterMessage); + + if (options.fromBlockHeight !== undefined) { + request.setFromBlockHeight(options.fromBlockHeight); + } + + if (options.fromBlockHash) { + request.setFromBlockHash( + Buffer.from(options.fromBlockHash, 'hex'), + ); + } + + request.setCount(options.count); + + return grpcTransport.request( + CorePromiseClient, + 'subscribeToTransactionsWithProofs', + request, + options, + ); + } + + return subscribeToTransactionsWithProofs; +} + +/** + * @typedef {object} subscribeToTransactionsWithProofsOptions + * @property {string} [fromBlockHash] - Specifies block hash to start syncing from + * @property {number} [fromBlockHeight] - Specifies block height to start syncing from + * @property {number} [count=0] - Number of blocks to sync, + * if set to 0 syncing is continuously sends new data as well + */ + +module.exports = subscribeToTransactionsWithProofsFactory; diff --git a/packages/js-dapi-client/lib/methods/platform/PlatformMethodsFacade.js b/packages/js-dapi-client/lib/methods/platform/PlatformMethodsFacade.js new file mode 100644 index 00000000000..b523b46980d --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/PlatformMethodsFacade.js @@ -0,0 +1,24 @@ +const broadcastStateTransitionFactory = require('./broadcastStateTransition/broadcastStateTransitionFactory'); +const getDataContractFactory = require('./getDataContract/getDataContractFactory'); +const getDocumentsFactory = require('./getDocuments/getDocumentsFactory'); +const getIdentityFactory = require('./getIdentity/getIdentityFactory'); +const getIdentitiesByPublicKeyHashesFactory = require('./getIdentitiesByPublicKeyHashes/getIdentitiesByPublicKeyHashesFactory'); +const waitForStateTransitionResultFactory = require('./waitForStateTransitionResult/waitForStateTransitionResultFactory'); +const getConsensusParamsFactory = require('./getConsensusParams/getConsensusParamsFactory'); + +class PlatformMethodsFacade { + /** + * @param {GrpcTransport} grpcTransport + */ + constructor(grpcTransport) { + this.broadcastStateTransition = broadcastStateTransitionFactory(grpcTransport); + this.getDataContract = getDataContractFactory(grpcTransport); + this.getDocuments = getDocumentsFactory(grpcTransport); + this.getIdentity = getIdentityFactory(grpcTransport); + this.getIdentitiesByPublicKeyHashes = getIdentitiesByPublicKeyHashesFactory(grpcTransport); + this.waitForStateTransitionResult = waitForStateTransitionResultFactory(grpcTransport); + this.getConsensusParams = getConsensusParamsFactory(grpcTransport); + } +} + +module.exports = PlatformMethodsFacade; diff --git a/packages/js-dapi-client/lib/methods/platform/broadcastStateTransition/BroadcastStateTransitionResponse.js b/packages/js-dapi-client/lib/methods/platform/broadcastStateTransition/BroadcastStateTransitionResponse.js new file mode 100644 index 00000000000..3567f2a7427 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/broadcastStateTransition/BroadcastStateTransitionResponse.js @@ -0,0 +1,10 @@ +class BroadcastStateTransitionResponse { + /** + * @returns {BroadcastStateTransitionResponse} + */ + static createFromProto() { + return new BroadcastStateTransitionResponse(); + } +} + +module.exports = BroadcastStateTransitionResponse; diff --git a/packages/js-dapi-client/lib/methods/platform/broadcastStateTransition/broadcastStateTransitionFactory.js b/packages/js-dapi-client/lib/methods/platform/broadcastStateTransition/broadcastStateTransitionFactory.js new file mode 100644 index 00000000000..2d9712ed2b1 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/broadcastStateTransition/broadcastStateTransitionFactory.js @@ -0,0 +1,57 @@ +const { + v0: { + BroadcastStateTransitionRequest, + PlatformPromiseClient, + }, +} = require('@dashevo/dapi-grpc'); +const BroadcastStateTransitionResponse = require('./BroadcastStateTransitionResponse'); +const InvalidResponseError = require('../response/errors/InvalidResponseError'); + +/** + * @param {GrpcTransport} grpcTransport + * @returns {broadcastStateTransition} + */ +function broadcastStateTransitionFactory(grpcTransport) { + /** + * Broadcast State Transaction + * + * @typedef {broadcastStateTransition} + * @param {Buffer} stateTransition + * @param {DAPIClientOptions} [options] + * @returns {Promise} + */ + async function broadcastStateTransition(stateTransition, options = {}) { + const broadcastStateTransitionRequest = new BroadcastStateTransitionRequest(); + broadcastStateTransitionRequest.setStateTransition(stateTransition); + + let lastError; + + // TODO: simple retry before the dapi versioning is properly implemented + for (let i = 0; i < 3; i += 1) { + try { + // eslint-disable-next-line no-await-in-loop + const broadcastStateTransitionResponse = await grpcTransport.request( + PlatformPromiseClient, + 'broadcastStateTransition', + broadcastStateTransitionRequest, + options, + ); + return BroadcastStateTransitionResponse.createFromProto(broadcastStateTransitionResponse); + } catch (e) { + if (e instanceof InvalidResponseError) { + lastError = e; + } else { + throw e; + } + } + } + + // If we made it past the cycle it means that the retry didn't work, + // and we're throwing the last error encountered + throw lastError; + } + + return broadcastStateTransition; +} + +module.exports = broadcastStateTransitionFactory; diff --git a/packages/js-dapi-client/lib/methods/platform/getConsensusParams/ConsensusParamsBlock.js b/packages/js-dapi-client/lib/methods/platform/getConsensusParams/ConsensusParamsBlock.js new file mode 100644 index 00000000000..31e05d884fb --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/getConsensusParams/ConsensusParamsBlock.js @@ -0,0 +1,36 @@ +class ConsensusParamsBlock { + /** + * + * @param {string} maxBytes + * @param {string} maxGas + * @param {string} timeIotaMs + */ + constructor(maxBytes, maxGas, timeIotaMs) { + this.maxBytes = maxBytes; + this.maxGas = maxGas; + this.timeIotaMs = timeIotaMs; + } + + /** + * @returns {string} + */ + getMaxBytes() { + return this.maxBytes; + } + + /** + * @returns {string} + */ + getMaxGas() { + return this.maxGas; + } + + /** + * @returns {string} + */ + getTimeIotaMs() { + return this.timeIotaMs; + } +} + +module.exports = ConsensusParamsBlock; diff --git a/packages/js-dapi-client/lib/methods/platform/getConsensusParams/ConsensusParamsEvidence.js b/packages/js-dapi-client/lib/methods/platform/getConsensusParams/ConsensusParamsEvidence.js new file mode 100644 index 00000000000..dd5bd03a6e0 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/getConsensusParams/ConsensusParamsEvidence.js @@ -0,0 +1,36 @@ +class ConsensusParamsEvidence { + /** + * + * @param {string} maxAgeNumBlocks + * @param {string} maxAgeDuration + * @param {string} maxBytes + */ + constructor(maxAgeNumBlocks, maxAgeDuration, maxBytes) { + this.maxAgeNumBlocks = maxAgeNumBlocks; + this.maxAgeDuration = maxAgeDuration; + this.maxBytes = maxBytes; + } + + /** + * @returns {string} + */ + getMaxAgeNumBlocks() { + return this.maxAgeNumBlocks; + } + + /** + * @returns {string} + */ + getMaxAgeDuration() { + return this.maxAgeDuration; + } + + /** + * @returns {string} + */ + getMaxBytes() { + return this.maxBytes; + } +} + +module.exports = ConsensusParamsEvidence; diff --git a/packages/js-dapi-client/lib/methods/platform/getConsensusParams/getConsensusParamsFactory.js b/packages/js-dapi-client/lib/methods/platform/getConsensusParams/getConsensusParamsFactory.js new file mode 100644 index 00000000000..02bbc9de0ff --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/getConsensusParams/getConsensusParamsFactory.js @@ -0,0 +1,62 @@ +const { + v0: { + PlatformPromiseClient, + GetConsensusParamsRequest, + }, +} = require('@dashevo/dapi-grpc'); +const InvalidResponseError = require('../response/errors/InvalidResponseError'); +const GetConsensusParamsResponse = require('./getConsensusParamsResponse'); + +/** + * @param {GrpcTransport} grpcTransport + * @returns {getConsensusParams} + */ +function getConsensusParamsFactory(grpcTransport) { + /** + * Fetch Consensus params + * + * @typedef getConsensusParams + * @param {number} [height] + * @param {prove: boolean} [options] + * @returns {Promise} + */ + async function getConsensusParams(height = undefined, options = {}) { + const getConsensusParamsRequest = new GetConsensusParamsRequest(); + if (height !== undefined) { + getConsensusParamsRequest.setHeight(height); + } + + getConsensusParamsRequest.setProve(!!options.prove); + + let lastError; + + // TODO: simple retry before the dapi versioning is properly implemented + for (let i = 0; i < 3; i += 1) { + try { + // eslint-disable-next-line no-await-in-loop + const getConsensusParamsResponse = await grpcTransport.request( + PlatformPromiseClient, + 'getConsensusParams', + getConsensusParamsRequest, + options, + ); + + return GetConsensusParamsResponse.createFromProto(getConsensusParamsResponse); + } catch (e) { + if (e instanceof InvalidResponseError) { + lastError = e; + } else { + throw e; + } + } + } + + // If we made it past the cycle it means that the retry didn't work, + // and we're throwing the last error encountered + throw lastError; + } + + return getConsensusParams; +} + +module.exports = getConsensusParamsFactory; diff --git a/packages/js-dapi-client/lib/methods/platform/getConsensusParams/getConsensusParamsResponse.js b/packages/js-dapi-client/lib/methods/platform/getConsensusParams/getConsensusParamsResponse.js new file mode 100644 index 00000000000..35a9cafe143 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/getConsensusParams/getConsensusParamsResponse.js @@ -0,0 +1,61 @@ +const InvalidResponseError = require('../response/errors/InvalidResponseError'); +const ConsensusParamsBlock = require('./ConsensusParamsBlock'); +const ConsensusParamsEvidence = require('./ConsensusParamsEvidence'); + +class GetConsensusParamsResponse { + /** + * + * @param {ConsensusParamsBlock} block + * @param {ConsensusParamsEvidence} evidence + */ + constructor(block, evidence) { + this.block = block; + this.evidence = evidence; + } + + /** + * @returns {ConsensusParamsBlock} + */ + getBlock() { + return this.block; + } + + /** + * @returns {ConsensusParamsEvidence} + */ + getEvidence() { + return this.evidence; + } + + /** + * @param proto + * @returns {GetConsensusParamsResponse} + */ + static createFromProto(proto) { + const protoBlock = proto.getBlock(); + const protoEvidence = proto.getEvidence(); + + if (!protoBlock && !protoEvidence) { + throw new InvalidResponseError('Consensus params are not defined'); + } + + const block = new ConsensusParamsBlock( + protoBlock.getMaxBytes(), + protoBlock.getMaxGas(), + protoBlock.getTimeIotaMs(), + ); + + const evidence = new ConsensusParamsEvidence( + protoEvidence.getMaxAgeNumBlocks(), + protoEvidence.getMaxAgeDuration(), + protoEvidence.getMaxBytes(), + ); + + return new GetConsensusParamsResponse( + block, + evidence, + ); + } +} + +module.exports = GetConsensusParamsResponse; diff --git a/packages/js-dapi-client/lib/methods/platform/getDataContract/GetDataContractResponse.js b/packages/js-dapi-client/lib/methods/platform/getDataContract/GetDataContractResponse.js new file mode 100644 index 00000000000..f60206c0d61 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/getDataContract/GetDataContractResponse.js @@ -0,0 +1,43 @@ +const AbstractResponse = require('../response/AbstractResponse'); +const InvalidResponseError = require('../response/errors/InvalidResponseError'); + +class GetDataContractResponse extends AbstractResponse { + /** + * @param {Buffer} dataContract + * @param {Metadata} metadata + * @param {Proof} [proof] + */ + constructor(dataContract, metadata, proof = undefined) { + super(metadata, proof); + + this.dataContract = dataContract; + } + + /** + * @returns {Buffer} + */ + getDataContract() { + return this.dataContract; + } + + /** + * @param proto + * @returns {GetDataContractResponse} + */ + static createFromProto(proto) { + const dataContract = proto.getDataContract(); + const { metadata, proof } = AbstractResponse.createMetadataAndProofFromProto(proto); + + if (!dataContract && !proof) { + throw new InvalidResponseError('DataContract is not defined'); + } + + return new GetDataContractResponse( + Buffer.from(dataContract), + metadata, + proof, + ); + } +} + +module.exports = GetDataContractResponse; diff --git a/packages/js-dapi-client/lib/methods/platform/getDataContract/getDataContractFactory.js b/packages/js-dapi-client/lib/methods/platform/getDataContract/getDataContractFactory.js new file mode 100644 index 00000000000..62ac8972de6 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/getDataContract/getDataContractFactory.js @@ -0,0 +1,69 @@ +const { + v0: { + PlatformPromiseClient, + GetDataContractRequest, + }, +} = require('@dashevo/dapi-grpc'); + +const GetDataContractResponse = require('./GetDataContractResponse'); +const InvalidResponseError = require('../response/errors/InvalidResponseError'); + +/** + * @param {GrpcTransport} grpcTransport + * @returns {getDataContract} + */ +function getDataContractFactory(grpcTransport) { + /** + * Fetch Data Contract by id + * + * @typedef {getDataContract} + * @param {Buffer} contractId + * @param {DAPIClientOptions & {prove: boolean}} [options] + * @returns {Promise} + */ + async function getDataContract(contractId, options = {}) { + const getDataContractRequest = new GetDataContractRequest(); + + // need to convert objects inherited from Buffer to pure buffer as google protobuf + // doesn't support extended buffers + // https://github.com/protocolbuffers/protobuf/blob/master/js/binary/utils.js#L1049 + if (Buffer.isBuffer(contractId)) { + // eslint-disable-next-line no-param-reassign + contractId = Buffer.from(contractId); + } + + getDataContractRequest.setId(contractId); + getDataContractRequest.setProve(!!options.prove); + + let lastError; + + // TODO: simple retry before the dapi versioning is properly implemented + for (let i = 0; i < 3; i += 1) { + try { + // eslint-disable-next-line no-await-in-loop + const getDataContractResponse = await grpcTransport.request( + PlatformPromiseClient, + 'getDataContract', + getDataContractRequest, + options, + ); + + return GetDataContractResponse.createFromProto(getDataContractResponse); + } catch (e) { + if (e instanceof InvalidResponseError) { + lastError = e; + } else { + throw e; + } + } + } + + // If we made it past the cycle it means that the retry didn't work, + // and we're throwing the last error encountered + throw lastError; + } + + return getDataContract; +} + +module.exports = getDataContractFactory; diff --git a/packages/js-dapi-client/lib/methods/platform/getDocuments/GetDocumentsResponse.js b/packages/js-dapi-client/lib/methods/platform/getDocuments/GetDocumentsResponse.js new file mode 100644 index 00000000000..5f2c43c2d28 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/getDocuments/GetDocumentsResponse.js @@ -0,0 +1,37 @@ +const AbstractResponse = require('../response/AbstractResponse'); + +class GetDocumentsResponse extends AbstractResponse { + /** + * @param {Buffer[]} documents + * @param {Metadata} metadata + * @param {Proof} [proof] + */ + constructor(documents, metadata, proof = undefined) { + super(metadata, proof); + + this.documents = documents; + } + + /** + * @returns {Buffer[]} + */ + getDocuments() { + return this.documents; + } + + /** + * @param proto + * @returns {GetDocumentsResponse} + */ + static createFromProto(proto) { + const { metadata, proof } = AbstractResponse.createMetadataAndProofFromProto(proto); + + return new GetDocumentsResponse( + proto.getDocumentsList().map((document) => Buffer.from(document)), + metadata, + proof, + ); + } +} + +module.exports = GetDocumentsResponse; diff --git a/packages/js-dapi-client/lib/methods/platform/getDocuments/getDocumentsFactory.js b/packages/js-dapi-client/lib/methods/platform/getDocuments/getDocumentsFactory.js new file mode 100644 index 00000000000..25cc99dd88a --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/getDocuments/getDocumentsFactory.js @@ -0,0 +1,107 @@ +const cbor = require('cbor'); + +const { + v0: { + PlatformPromiseClient, + GetDocumentsRequest, + }, +} = require('@dashevo/dapi-grpc'); + +const GetDocumentsResponse = require('./GetDocumentsResponse'); +const InvalidResponseError = require('../response/errors/InvalidResponseError'); + +/** + * @param {GrpcTransport} grpcTransport + * @returns {getDocuments} + */ +function getDocumentsFactory(grpcTransport) { + /** + * Fetch Documents from Drive + * + * @typedef {getDocuments} + * @param {Buffer} contractId - Data Contract ID + * @param {string} type - Document type + * @param {DAPIClientOptions & getDocumentsOptions & {prove: boolean}} [options] + * @returns {Promise} + */ + async function getDocuments(contractId, type, options = {}) { + const { + where, + orderBy, + limit, + startAt, + startAfter, + } = options; + + let whereSerialized; + if (where) { + whereSerialized = cbor.encode(where); + } + + let orderBySerialized; + if (orderBy) { + orderBySerialized = cbor.encode(orderBy); + } + + const getDocumentsRequest = new GetDocumentsRequest(); + // need to convert Identifier to pure buffer as google protobuf doesn't support extended buffers + // https://github.com/protocolbuffers/protobuf/blob/master/js/binary/utils.js#L1049 + + // need to convert objects inherited from Buffer to pure buffer as google protobuf + // doesn't support extended buffers + // https://github.com/protocolbuffers/protobuf/blob/master/js/binary/utils.js#L1049 + if (Buffer.isBuffer(contractId)) { + // eslint-disable-next-line no-param-reassign + contractId = Buffer.from(contractId); + } + + getDocumentsRequest.setDataContractId(contractId); + getDocumentsRequest.setDocumentType(type); + getDocumentsRequest.setWhere(whereSerialized); + getDocumentsRequest.setOrderBy(orderBySerialized); + getDocumentsRequest.setLimit(limit); + getDocumentsRequest.setStartAfter(startAfter); + getDocumentsRequest.setStartAt(startAt); + getDocumentsRequest.setProve(!!options.prove); + + let lastError; + + // TODO: simple retry before the dapi versioning is properly implemented + for (let i = 0; i < 3; i += 1) { + try { + // eslint-disable-next-line no-await-in-loop + const getDocumentsResponse = await grpcTransport.request( + PlatformPromiseClient, + 'getDocuments', + getDocumentsRequest, + options, + ); + + return GetDocumentsResponse.createFromProto(getDocumentsResponse); + } catch (e) { + if (e instanceof InvalidResponseError) { + lastError = e; + } else { + throw e; + } + } + } + + // If we made it past the cycle it means that the retry didn't work, + // and we're throwing the last error encountered + throw lastError; + } + + return getDocuments; +} + +/** + * @typedef {object} getDocumentsOptions + * @property {object} [where] + * @property {object} [orderBy] + * @property {object} [limit] + * @property {object} [startAt] + * @property {object} [startAfter] + */ + +module.exports = getDocumentsFactory; diff --git a/packages/js-dapi-client/lib/methods/platform/getIdentitiesByPublicKeyHashes/GetIdentitiesByPublicKeyHashesResponse.js b/packages/js-dapi-client/lib/methods/platform/getIdentitiesByPublicKeyHashes/GetIdentitiesByPublicKeyHashesResponse.js new file mode 100644 index 00000000000..b4022a10616 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/getIdentitiesByPublicKeyHashes/GetIdentitiesByPublicKeyHashesResponse.js @@ -0,0 +1,37 @@ +const AbstractResponse = require('../response/AbstractResponse'); + +class GetIdentitiesByPublicKeyHashesResponse extends AbstractResponse { + /** + * @param {Buffer[]} identities + * @param {Metadata} metadata + * @param {Proof} [proof] + */ + constructor(identities, metadata, proof = undefined) { + super(metadata, proof); + + this.identities = identities; + } + + /** + * @returns {Buffer[]} + */ + getIdentities() { + return this.identities; + } + + /** + * @param proto + * @returns {GetIdentitiesByPublicKeyHashesResponse} + */ + static createFromProto(proto) { + const { metadata, proof } = AbstractResponse.createMetadataAndProofFromProto(proto); + + return new GetIdentitiesByPublicKeyHashesResponse( + proto.getIdentitiesList_asU8().map((identity) => Buffer.from(identity)), + metadata, + proof, + ); + } +} + +module.exports = GetIdentitiesByPublicKeyHashesResponse; diff --git a/packages/js-dapi-client/lib/methods/platform/getIdentitiesByPublicKeyHashes/getIdentitiesByPublicKeyHashesFactory.js b/packages/js-dapi-client/lib/methods/platform/getIdentitiesByPublicKeyHashes/getIdentitiesByPublicKeyHashesFactory.js new file mode 100644 index 00000000000..05005dc90ee --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/getIdentitiesByPublicKeyHashes/getIdentitiesByPublicKeyHashesFactory.js @@ -0,0 +1,63 @@ +const { + v0: { + PlatformPromiseClient, + GetIdentitiesByPublicKeyHashesRequest, + }, +} = require('@dashevo/dapi-grpc'); + +const GetIdentitiesByPublicKeyHashesResponse = require('./GetIdentitiesByPublicKeyHashesResponse'); +const InvalidResponseError = require('../response/errors/InvalidResponseError'); + +/** + * @param {GrpcTransport} grpcTransport + * @returns {getIdentitiesByPublicKeyHashes} + */ +function getIdentitiesByPublicKeyHashesFactory(grpcTransport) { + /** + * Fetch the identities by public key hashes + * + * @typedef {getIdentitiesByPublicKeyHashes} + * @param {Buffer[]} publicKeyHashes + * @param {DAPIClientOptions & {prove: boolean}} [options] + * @returns {Promise} + */ + async function getIdentitiesByPublicKeyHashes(publicKeyHashes, options = {}) { + const getIdentitiesByPublicKeyHashesRequest = new GetIdentitiesByPublicKeyHashesRequest(); + getIdentitiesByPublicKeyHashesRequest.setPublicKeyHashesList( + publicKeyHashes, + ); + getIdentitiesByPublicKeyHashesRequest.setProve(!!options.prove); + + let lastError; + + // TODO: simple retry before the dapi versioning is properly implemented + for (let i = 0; i < 3; i += 1) { + try { + // eslint-disable-next-line no-await-in-loop + const getIdentitiesByPublicKeyHashesResponse = await grpcTransport.request( + PlatformPromiseClient, + 'getIdentitiesByPublicKeyHashes', + getIdentitiesByPublicKeyHashesRequest, + options, + ); + + return GetIdentitiesByPublicKeyHashesResponse + .createFromProto(getIdentitiesByPublicKeyHashesResponse); + } catch (e) { + if (e instanceof InvalidResponseError) { + lastError = e; + } else { + throw e; + } + } + } + + // If we made it past the cycle it means that the retry didn't work, + // and we're throwing the last error encountered + throw lastError; + } + + return getIdentitiesByPublicKeyHashes; +} + +module.exports = getIdentitiesByPublicKeyHashesFactory; diff --git a/packages/js-dapi-client/lib/methods/platform/getIdentity/GetIdentityResponse.js b/packages/js-dapi-client/lib/methods/platform/getIdentity/GetIdentityResponse.js new file mode 100644 index 00000000000..a6147701db9 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/getIdentity/GetIdentityResponse.js @@ -0,0 +1,43 @@ +const AbstractResponse = require('../response/AbstractResponse'); +const InvalidResponseError = require('../response/errors/InvalidResponseError'); + +class GetIdentityResponse extends AbstractResponse { + /** + * @param {Buffer} identity + * @param {Metadata} metadata + * @param {Proof} [proof] + */ + constructor(identity, metadata, proof = undefined) { + super(metadata, proof); + + this.identity = identity; + } + + /** + * @returns {Buffer} + */ + getIdentity() { + return this.identity; + } + + /** + * @param proto + * @returns {GetIdentityResponse} + */ + static createFromProto(proto) { + const identity = proto.getIdentity(); + const { metadata, proof } = AbstractResponse.createMetadataAndProofFromProto(proto); + + if (!identity && !proof) { + throw new InvalidResponseError('Identity is not defined'); + } + + return new GetIdentityResponse( + Buffer.from(proto.getIdentity()), + metadata, + proof, + ); + } +} + +module.exports = GetIdentityResponse; diff --git a/packages/js-dapi-client/lib/methods/platform/getIdentity/getIdentityFactory.js b/packages/js-dapi-client/lib/methods/platform/getIdentity/getIdentityFactory.js new file mode 100644 index 00000000000..243dde26349 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/getIdentity/getIdentityFactory.js @@ -0,0 +1,68 @@ +const { + v0: { + PlatformPromiseClient, + GetIdentityRequest, + }, +} = require('@dashevo/dapi-grpc'); + +const GetIdentityResponse = require('./GetIdentityResponse'); +const InvalidResponseError = require('../response/errors/InvalidResponseError'); + +/** + * @param {GrpcTransport} grpcTransport + * @returns {getIdentity} + */ +function getIdentityFactory(grpcTransport) { + /** + * Fetch the identity by id + * + * @typedef {getIdentity} + * @param {Buffer} id + * @param {DAPIClientOptions & {prove: boolean}} [options] + * @returns {Promise} + */ + async function getIdentity(id, options = {}) { + const getIdentityRequest = new GetIdentityRequest(); + // need to convert objects inherited from Buffer to pure buffer as google protobuf + // doesn't support extended buffers + // https://github.com/protocolbuffers/protobuf/blob/master/js/binary/utils.js#L1049 + if (Buffer.isBuffer(id)) { + // eslint-disable-next-line no-param-reassign + id = Buffer.from(id); + } + + getIdentityRequest.setId(id); + getIdentityRequest.setProve(!!options.prove); + + let lastError; + + // TODO: simple retry before the dapi versioning is properly implemented + for (let i = 0; i < 3; i += 1) { + try { + // eslint-disable-next-line no-await-in-loop + const getIdentityResponse = await grpcTransport.request( + PlatformPromiseClient, + 'getIdentity', + getIdentityRequest, + options, + ); + + return GetIdentityResponse.createFromProto(getIdentityResponse); + } catch (e) { + if (e instanceof InvalidResponseError) { + lastError = e; + } else { + throw e; + } + } + } + + // If we made it past the cycle it means that the retry didn't work, + // and we're throwing the last error encountered + throw lastError; + } + + return getIdentity; +} + +module.exports = getIdentityFactory; diff --git a/packages/js-dapi-client/lib/methods/platform/response/AbstractResponse.js b/packages/js-dapi-client/lib/methods/platform/response/AbstractResponse.js new file mode 100644 index 00000000000..e14a6c7cd6e --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/response/AbstractResponse.js @@ -0,0 +1,60 @@ +const InvalidResponseError = require('./errors/InvalidResponseError'); +const Metadata = require('./Metadata'); +const Proof = require('./Proof'); + +/** + * @abstract + */ +class AbstractResponse { + /** + * @param {Metadata} metadata + * @param {Proof} [proof] + */ + constructor(metadata, proof = undefined) { + this.metadata = metadata; + this.proof = proof; + } + + /** + * @returns {Metadata} - metadata + */ + getMetadata() { + return this.metadata; + } + + /** + * @returns {Proof} - data with required information for cryptographical verification + */ + getProof() { + return this.proof; + } + + /** + * + * @param proto + * + * @returns{{metadata: Metadata, proof: Proof|undefined}} + * @throws {InvalidResponseError} + */ + static createMetadataAndProofFromProto(proto) { + const metadata = proto.getMetadata(); + + if (metadata === undefined) { + throw new InvalidResponseError('Metadata is not defined'); + } + + const rawProof = proto.getProof(); + + let proof; + if (rawProof) { + proof = Proof.createFromProto(rawProof); + } + + return { + metadata: new Metadata(metadata.toObject()), + proof, + }; + } +} + +module.exports = AbstractResponse; diff --git a/packages/js-dapi-client/lib/methods/platform/response/Metadata.js b/packages/js-dapi-client/lib/methods/platform/response/Metadata.js new file mode 100644 index 00000000000..2d92db5b7d0 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/response/Metadata.js @@ -0,0 +1,31 @@ +class Metadata { + /** + * @param {object} properties + * @param {number} properties.height - block height + * @param {number} properties.coreChainLockedHeight - core chain locked height + */ + constructor(properties) { + this.height = properties.height; + this.coreChainLockedHeight = properties.coreChainLockedHeight; + } + + /** + * Get height + * + * @returns {number} - block height + */ + getHeight() { + return this.height; + } + + /** + * Get core chain locked height + * + * @returns {number} - core chain locked height + */ + getCoreChainLockedHeight() { + return this.coreChainLockedHeight; + } +} + +module.exports = Metadata; diff --git a/packages/js-dapi-client/lib/methods/platform/response/Proof.js b/packages/js-dapi-client/lib/methods/platform/response/Proof.js new file mode 100644 index 00000000000..52f749c6f95 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/response/Proof.js @@ -0,0 +1,49 @@ +class Proof { + /** + * @param {object} properties + * @param {Buffer} properties.merkleProof + * @param {Buffer} properties.signatureLLMQHash + * @param {Buffer} properties.signature + */ + constructor(properties) { + this.merkleProof = properties.merkleProof; + this.signatureLLMQHash = properties.signatureLLMQHash; + this.signature = properties.signature; + } + + /** + * @returns {Buffer} + */ + getMerkleProof() { + return this.merkleProof; + } + + /** + * @returns {Buffer} + */ + getSignatureLLMQHash() { + return this.signatureLLMQHash; + } + + /** + * @returns {Buffer} + */ + getSignature() { + return this.signature; + } + + /** + * @param {Object} proofProto + * + * @returns {Proof} + */ + static createFromProto(proofProto) { + return new Proof({ + merkleProof: Buffer.from(proofProto.getMerkleProof()), + signatureLLMQHash: Buffer.from(proofProto.getSignatureLlmqHash()), + signature: Buffer.from(proofProto.getSignature()), + }); + } +} + +module.exports = Proof; diff --git a/packages/js-dapi-client/lib/methods/platform/response/StoreTreeProofs.js b/packages/js-dapi-client/lib/methods/platform/response/StoreTreeProofs.js new file mode 100644 index 00000000000..5a3ad028286 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/response/StoreTreeProofs.js @@ -0,0 +1,45 @@ +class StoreTreeProofs { + /** + * @param {object} properties + * @param {Buffer} properties.publicKeyHashesToIdentityIdsProof + * @param {Buffer} properties.identitiesProof + * @param {Buffer} properties.documentsProof + * @param {Buffer} properties.dataContractsProof + */ + constructor(properties) { + this.publicKeyHashesToIdentityIdsProof = properties.publicKeyHashesToIdentityIdsProof; + this.identitiesProof = properties.identitiesProof; + this.documentsProof = properties.documentsProof; + this.dataContractsProof = properties.dataContractsProof; + } + + /** + * @returns {Buffer} + */ + getPublicKeyHashesToIdentityIdsProof() { + return this.publicKeyHashesToIdentityIdsProof; + } + + /** + * @returns {Buffer} + */ + getIdentitiesProof() { + return this.identitiesProof; + } + + /** + * @returns {Buffer} + */ + getDocumentsProof() { + return this.documentsProof; + } + + /** + * @returns {Buffer} + */ + getDataContractsProof() { + return this.dataContractsProof; + } +} + +module.exports = StoreTreeProofs; diff --git a/packages/js-dapi-client/lib/methods/platform/response/errors/InvalidResponseError.js b/packages/js-dapi-client/lib/methods/platform/response/errors/InvalidResponseError.js new file mode 100644 index 00000000000..d547af0d864 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/response/errors/InvalidResponseError.js @@ -0,0 +1,9 @@ +const DAPIClientError = require('../../../../errors/DAPIClientError'); + +class InvalidResponseError extends DAPIClientError { + constructor(message) { + super(`Invalid response: ${message}`); + } +} + +module.exports = InvalidResponseError; diff --git a/packages/js-dapi-client/lib/methods/platform/waitForStateTransitionResult/ErrorResult.js b/packages/js-dapi-client/lib/methods/platform/waitForStateTransitionResult/ErrorResult.js new file mode 100644 index 00000000000..8bc98691f89 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/waitForStateTransitionResult/ErrorResult.js @@ -0,0 +1,35 @@ +class ErrorResult { + /** + * @param {number} code + * @param {string} message + * @param {*} data + */ + constructor(code, message, data) { + this.code = code; + this.message = message; + this.data = data; + } + + /** + * @returns {number} + */ + getCode() { + return this.code; + } + + /** + * @returns {string} + */ + getMessage() { + return this.message; + } + + /** + * @returns {*} + */ + getData() { + return this.data; + } +} + +module.exports = ErrorResult; diff --git a/packages/js-dapi-client/lib/methods/platform/waitForStateTransitionResult/WaitForStateTransitionResultResponse.js b/packages/js-dapi-client/lib/methods/platform/waitForStateTransitionResult/WaitForStateTransitionResultResponse.js new file mode 100644 index 00000000000..b4e1dbb5e9c --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/waitForStateTransitionResult/WaitForStateTransitionResultResponse.js @@ -0,0 +1,63 @@ +const cbor = require('cbor'); + +const AbstractResponse = require('../response/AbstractResponse'); +const Metadata = require('../response/Metadata'); +const Proof = require('../response/Proof'); +const ErrorResult = require('./ErrorResult'); + +class WaitForStateTransitionResultResponse extends AbstractResponse { + /** + * @param {Metadata} metadata + * @param {Proof} [proof] + * @param {ErrorResult} [error] + */ + constructor(metadata, proof = undefined, error = undefined) { + super(metadata, proof); + + this.error = error; + } + + /** + * @returns {ErrorResult} + */ + getError() { + return this.error; + } + + /** + * @param proto + * @returns {WaitForStateTransitionResultResponse} + */ + static createFromProto(proto) { + let error; + let proof; + + if (proto.getProof()) { + proof = Proof.createFromProto(proto.getProof()); + } + + if (proto.getError()) { + let data; + const rawData = proto.getError().getData(); + if (rawData) { + data = cbor.decode(Buffer.from(rawData)); + } + + error = new ErrorResult( + proto.getError().getCode(), + proto.getError().getMessage(), + data, + ); + } + + const metadata = proto.getMetadata() ? new Metadata(proto.getMetadata().toObject()) : null; + + return new WaitForStateTransitionResultResponse( + metadata, + proof, + error, + ); + } +} + +module.exports = WaitForStateTransitionResultResponse; diff --git a/packages/js-dapi-client/lib/methods/platform/waitForStateTransitionResult/waitForStateTransitionResultFactory.js b/packages/js-dapi-client/lib/methods/platform/waitForStateTransitionResult/waitForStateTransitionResultFactory.js new file mode 100644 index 00000000000..bf2d712df90 --- /dev/null +++ b/packages/js-dapi-client/lib/methods/platform/waitForStateTransitionResult/waitForStateTransitionResultFactory.js @@ -0,0 +1,72 @@ +const { + v0: { + PlatformPromiseClient, + WaitForStateTransitionResultRequest, + }, +} = require('@dashevo/dapi-grpc'); + +const WaitForStateTransitionResultResponse = require('./WaitForStateTransitionResultResponse'); +const InvalidResponseError = require('../response/errors/InvalidResponseError'); + +/** + * + * @param {GrpcTransport} grpcTransport + * @returns {waitForStateTransitionResult} + */ +function waitForStateTransitionResultFactory(grpcTransport) { + /** + * @typedef waitForStateTransitionResult + * @param {Buffer} stateTransitionHash + * @param {DAPIClientOptions & getDocumentsOptions & {prove: boolean}} [options] + * @returns {Promise} + */ + async function waitForStateTransitionResult(stateTransitionHash, options = {}) { + // eslint-disable-next-line no-param-reassign + options = { + // Set default timeout + timeout: 60000, + prove: false, + retry: 0, + throwDeadlineExceeded: true, + ...options, + }; + + const waitForStateTransitionResultRequest = new WaitForStateTransitionResultRequest(); + + waitForStateTransitionResultRequest.setStateTransitionHash(stateTransitionHash); + waitForStateTransitionResultRequest.setProve(options.prove); + + let lastError; + + // TODO: simple retry before the dapi versioning is properly implemented + for (let i = 0; i < 3; i += 1) { + try { + // eslint-disable-next-line no-await-in-loop + const waitForStateTransitionResultResponse = await grpcTransport.request( + PlatformPromiseClient, + 'waitForStateTransitionResult', + waitForStateTransitionResultRequest, + options, + ); + + return WaitForStateTransitionResultResponse.createFromProto( + waitForStateTransitionResultResponse, + ); + } catch (e) { + if (e instanceof InvalidResponseError) { + lastError = e; + } else { + throw e; + } + } + } + + // If we made it past the cycle it means that the retry didn't work, + // and we're throwing the last error encountered + throw lastError; + } + + return waitForStateTransitionResult; +} + +module.exports = waitForStateTransitionResultFactory; diff --git a/packages/js-dapi-client/lib/networkConfigs.js b/packages/js-dapi-client/lib/networkConfigs.js new file mode 100644 index 00000000000..c74cfdf93c6 --- /dev/null +++ b/packages/js-dapi-client/lib/networkConfigs.js @@ -0,0 +1,181 @@ +module.exports = { + testnet: { + seeds: [ + 'seed-1.testnet.networks.dash.org', + 'seed-2.testnet.networks.dash.org', + 'seed-3.testnet.networks.dash.org', + 'seed-4.testnet.networks.dash.org', + 'seed-5.testnet.networks.dash.org', + ], + network: 'testnet', + // Since we don't have PoSe atm, 3rd party masternodes sometimes provide wrong data + // that breaks test suite and application logic. Temporary solution is to hardcode + // reliable DCG testnet masternodes to connect. Should be removed when PoSe is introduced. + dapiAddressesWhiteList: [ + '52.11.85.154', + '52.38.248.133', + '34.212.231.240', + '54.185.249.226', + '34.215.57.86', + '54.190.73.116', + '34.214.2.219', + '54.212.84.164', + '35.164.96.124', + '34.219.81.129', + '34.221.42.205', + '34.208.88.128', + '54.189.162.193', + '34.220.124.90', + '54.201.242.241', + '54.68.10.46', + '34.210.81.39', + '18.237.47.243', + '54.187.229.6', + '34.214.102.160', + '34.219.93.145', + '35.165.37.186', + '54.202.157.120', + '34.215.144.176', + '52.13.119.69', + '34.220.12.188', + '18.237.5.33', + '54.191.32.70', + '54.213.219.155', + '35.167.25.157', + '54.213.89.75', + '52.32.232.156', + '34.212.226.44', + '35.160.140.37', + '52.11.133.244', + '54.201.189.185', + '34.216.200.22', + '34.220.144.226', + '34.216.195.19', + '54.185.1.69', + '35.166.29.155', + '18.237.147.160', + '34.220.228.214', + '54.212.75.8', + '35.163.152.74', + '18.236.78.191', + '54.148.106.179', + '34.211.46.222', + '35.160.13.25', + '34.220.17.107', + '34.212.65.137', + '54.185.249.172', + '54.70.65.199', + '54.69.210.42', + '18.236.160.247', + '54.245.197.173', + '54.187.11.213', + '54.218.70.46', + '35.165.207.13', + '34.211.49.3', + '34.219.36.94', + '34.222.127.158', + '34.222.242.228', + '52.26.220.40', + '52.36.244.225', + '34.222.225.76', + '18.236.169.114', + '54.201.236.212', + '54.203.241.214', + '34.221.254.29', + '54.187.50.120', + '54.184.140.221', + '34.215.192.133', + '35.164.180.39', + '54.184.183.20', + '52.43.197.215', + '54.201.42.245', + '54.218.113.88', + '54.244.141.192', + '34.217.98.54', + '34.222.168.33', + '52.32.143.49', + '54.187.224.80', + '54.189.87.145', + '52.39.164.105', + '54.70.55.164', + '54.214.68.206', + '54.201.239.109', + '34.215.146.162', + '18.236.233.120', + '54.190.217.178', + '34.220.41.134', + '34.212.178.215', + '34.219.169.55', + '54.218.251.43', + '18.236.216.191', + '54.188.17.60', + '54.191.227.118', + '34.213.5.102', + '35.166.79.235', + '54.71.107.225', + '54.201.162.86', + '52.34.141.75', + '34.217.43.189', + '52.38.77.105', + '52.11.252.174', + '54.191.221.246', + '54.218.107.83', + '54.212.18.218', + '34.220.53.77', + '54.244.41.15', + '34.222.135.203', + '54.191.110.152', + '34.208.190.130', + '34.222.6.55', + '54.184.7.184', + '52.12.47.86', + '34.217.28.248', + '54.149.252.146', + '54.149.80.193', + '34.215.55.0', + '34.220.74.48', + '34.209.124.112', + '34.217.23.70', + '34.222.102.137', + '34.209.166.42', + '18.236.128.49', + '35.163.99.20', + '34.215.67.224', + '34.211.244.117', + '18.236.199.232', + '54.191.237.52', + '34.223.226.224', + '54.149.133.143', + '52.41.198.242', + '54.148.215.161', + '54.188.193.70', + '54.218.104.194', + '34.209.73.208', + '34.220.88.70', + '52.40.101.104', + '18.236.68.153', + '34.218.76.179', + '34.219.94.178', + '54.218.127.128', + '52.27.198.246', + '18.237.204.153', + '35.166.57.113', + '54.191.15.3', + '18.237.128.46', + ], + }, + evonet: { + seeds: [ + 'seed-1.evonet.networks.dash.org', + 'seed-2.evonet.networks.dash.org', + 'seed-3.evonet.networks.dash.org', + 'seed-4.evonet.networks.dash.org', + 'seed-5.evonet.networks.dash.org', + ], + network: 'evonet', + }, + local: { + dapiAddresses: ['127.0.0.1'], + network: 'regtest', + }, +}; diff --git a/packages/js-dapi-client/lib/test/.eslintrc b/packages/js-dapi-client/lib/test/.eslintrc new file mode 100644 index 00000000000..4c2b11fe817 --- /dev/null +++ b/packages/js-dapi-client/lib/test/.eslintrc @@ -0,0 +1,9 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + } +} diff --git a/packages/js-dapi-client/lib/test/bootstrap.js b/packages/js-dapi-client/lib/test/bootstrap.js new file mode 100644 index 00000000000..dc85f196965 --- /dev/null +++ b/packages/js-dapi-client/lib/test/bootstrap.js @@ -0,0 +1,23 @@ +const { expect, use } = require('chai'); +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); +const dirtyChai = require('dirty-chai'); +const chaiAsPromised = require('chai-as-promised'); + +use(sinonChai); +use(chaiAsPromised); +use(dirtyChai); + +beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + +afterEach(function afterEach() { + this.sinon.restore(); +}); + +global.expect = expect; diff --git a/packages/js-dapi-client/lib/test/fixtures/getHeadersFixture.js b/packages/js-dapi-client/lib/test/fixtures/getHeadersFixture.js new file mode 100644 index 00000000000..8c0fc19ce26 --- /dev/null +++ b/packages/js-dapi-client/lib/test/fixtures/getHeadersFixture.js @@ -0,0 +1,215 @@ +const { BlockHeader } = require('@dashevo/dashcore-lib'); + +const headers = [ + { + version: 2, + prevHash: '00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc2c', + merkleRoot: 'b4fd581bc4bfe51a5a66d8b823bd6ee2b492f0ddc44cf7e820550714cedc117f', + time: 1398712771, + bits: '1e0fffff', + nonce: 31475, + }, + { + version: 2, + prevHash: '0000047d24635e347be3aaaeb66c26be94901a2f962feccd4f95090191f208c1', + merkleRoot: '0d6d332e68eb8ecc66a5baaa95dc4b10c0b32841aed57dc99a5ae0b2f9e4294d', + time: 1398712772, + nonce: 6523, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000c6264fab4ba2d23990396f42a76aa4822f03cbc7634b79f4dfea36fccc2', + merkleRoot: '1cc711129405a328c58d1948e748c3b8f3d610e66d9901db88c42c5247829658', + time: 1398712774, + nonce: 53194, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '0000057d5c945acbe476bc17bbbaeb2fc1c1b18673e7582c48ac04af61f4d811', + merkleRoot: '7e6b1b1457308bf6ccb1e325c64607ba7dfac05e26c08887cd28f97d4d4ab3e2', + time: 1398712782, + nonce: 193159, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '000002258bd58bf4cdcde282abc030437c103dbb12d2a7dbc978d07bcf386b42', + merkleRoot: '4cf4f3b788e8dc847a9e0ff3b279340207c555a6bc0736f93e95dbdc2e3c2f16', + time: 1398712784, + nonce: 41103, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000b1406007f9148c4c173e2314437a315b79b5b79e73ed534a2e8b18a43fc', + merkleRoot: '99c25e1373c0a5277157b730624d110d04322ffaac49ac0c98cc2d01c80f4c4c', + time: 1398712787, + nonce: 62295, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000d93de4b31ec550d324876ee7c502a073911972d26c2c990b94d9d5b5a44', + merkleRoot: '769fc473dc8e5073e4fe3f69f0603972d3d72fbeb62a64de826c64e44260b3f3', + time: 1398712793, + nonce: 127755, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000154b50ded9d6298dc965e541cef117103c3376256185d0d70e92850853d', + merkleRoot: '8243e732675d2936bb27917844e714ff5e2012726e3f76e374e7f6236e78669e', + time: 1398712796, + nonce: 62099, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '0000089e53a33e48061fa4dbc0a52c40736c2eedddaf563ec25cfe406dd8a541', + merkleRoot: '243786d6706421ba9ebe9a0d153a5e79d9f781825dbbb0ed26ccc647f10984ee', + time: 1398712802, + nonce: 128139, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000e98a2b0b1505e3ed00dba28988cd81440de4ec48d817412c2178d332985', + merkleRoot: 'b158385f441d9299732ec264442d28b6009b8d24388ee7e23a6ce7e6051bfa31', + time: 1398712803, + nonce: 23451, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '000001128124cab4756edc387dce2b8762514916d953a4993971a40d78240978', + merkleRoot: '1f966cdf6ca90f211308554f90f33935cd8e25ab329ffedfa1066b00a581d5d5', + time: 1398712807, + nonce: 58648, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000ce430de949c85a145b02e33ebbaed3772dc8f3d668f66edc6852c24d002', + merkleRoot: '663360403b5fba9cd8744c3706f9660c7d3fee4e5a9ee98ce0ad5e5ad7824c1d', + time: 1398712821, + nonce: 312363, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000ac3a0c9df709260e41290d6902e5a4a073099f11fe8c1ce80aadc4bb331', + merkleRoot: 'c2ed22a3e6712b842359dfbb6f0a133ae122ffb601e4cf60e30b8c99f9438f4f', + time: 1398712821, + nonce: 8325, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000b526b34e733532d706c1f4cef93eefe707b87c2c3cb2978e1a84b97c501', + merkleRoot: '8994e231d4986bbea391d08f67ff9b6da758c86aa2fc9e9816f105462d18cf1b', + time: 1398712824, + nonce: 60659, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000799cc6963498a215e04402737fc1a08e696a91540756b0697f1ac7b23a8', + merkleRoot: 'f57a78dd55cb4530df55a8b7ba5aed632b1d3819950e78f90d1acdba5ac595d9', + time: 1398712825, + nonce: 18064, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000170cc708e9c3b491625dd8dc2d737730a738ebd349f2a770fb26944c04f', + merkleRoot: '5494a90a580a4f16ff80d2b0c7507d7a565415091d88abeaa752b517da92e06d', + time: 1398886700, + nonce: 1555411, + bits: '1e0fffff', + }, + { + version: 2, + prevHash: '000000ab6ae22728c701e15a13dfa022c88f17a4e0fd71f073b0a720229de22b', + merkleRoot: '0c03f4a03f47046d1295207ca99cc9db6757a6e4ff313a359d1182cfb8431a84', + time: 1405697551, + nonce: 1491503, + bits: '1e0fffff', + }, + { + version: 2, + confirmations: 86916, + prevHash: '00000541dfefddc46d4a7ef3873375db4a6f5b1c06a84b67346fb88e07f024d0', + merkleRoot: '2d6ad1b165c8b7dd4f038d8003d0229259db52e7e828d0d25af38c2f2f7e1990', + time: 1405697569, + nonce: 966245, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '000000d275d8dc93e7bf8c358463c97e12be9b2b17d4e64770aca7af8dcb93d8', + merkleRoot: 'e3552304f4c424ebe5bfc0911b1a3a6fcec15fe788acaf910f45238f16d522f7', + time: 1405697598, + nonce: 1661888, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '0000073a769b38812ca2e3a6d4958cab3f21ed3cf4cc80590be3db4b9cb17c8a', + merkleRoot: 'dd0e01125ffba80e7e7508d57da2725ccc648ed3ea3bac60ee19f81efcf8b934', + time: 1405697613, + nonce: 828461, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000816a46bd5ea15161c7864b76a263f890d60d4aad6b53cceb8c39ee6b153', + merkleRoot: '2847d5749c81f8c0b6b91a87a833532611feebb39a833d0e092ec7c8e141467e', + time: 1405697617, + nonce: 50599, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000976f999bcb32be3d5b7c2e684e8000d8566f22df2612f58b8574536addc', + merkleRoot: '78d7b2982419345c34391df68ff2d10c0475d5513a6b52f8caaae4f350af1eee', + time: 1405697628, + nonce: 613213, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000a4f6e00f23dde0b9197d5633b1637c0c22748adadda2d1a799b26115d0d', + merkleRoot: 'bc8af9a84bc94da13dd144c4e1bff2d8580d33f7613d2e4c9c504b4df5c6d9b4', + time: 1405697647, + nonce: 1050462, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000ad04d60ac3b1567b499c359b2f901be9826e358e071e5ffcb014d7e60ef', + merkleRoot: '3cbf4973f43b967773b06169246fbbc80258631280ea84165a1c269b3601943f', + time: 1405697671, + nonce: 1381148, + bits: '1e0ffff0', + }, + { + version: 2, + prevHash: '00000df1e666a530efa28ac37c8cf8b80970c65aa69497fc4fbb3252d2e5f661', + merkleRoot: '4050c8893b1d882da44d15e481b30e145ed6fe29ffa51ac99dc0e206696aa6dc', + time: 1405697678, + nonce: 352597, + bits: '1e0ffff0', + }, +]; + +/** + * @returns {BlockHeader[]} + */ +function getHeadersFixture() { + return headers + .map((header) => new BlockHeader({ ...header, bits: parseInt(header.bits, 16) })); +} + +module.exports = getHeadersFixture; diff --git a/packages/js-dapi-client/lib/test/fixtures/getMNListDiffsFixture.js b/packages/js-dapi-client/lib/test/fixtures/getMNListDiffsFixture.js new file mode 100644 index 00000000000..45f425f2df7 --- /dev/null +++ b/packages/js-dapi-client/lib/test/fixtures/getMNListDiffsFixture.js @@ -0,0 +1,1075 @@ +/** + * Get MN List Diff fixture + * + * @returns {object} - MN List Diff object + */ +function getMNListDiffsFixture() { + return [{ + baseBlockHash: '0000047d24635e347be3aaaeb66c26be94901a2f962feccd4f95090191f208c1', + blockHash: '000000000b0339e07bce8b3186a6a57a3c45d10e16c4bce18ef81b667bc822b2', + cbTxMerkleTree: '0500000004ab3194f5e8337068f4082d94c98cc489486f11bb0b82f7ec3e22516276a82276d4e7db56ced3f0fec5623e2d344d0f7b7c60ce1a0854119b1525f94ee169a494274151a52195396654eeaf86807cd7889c510d73a9b545cfdd5ec68caa5589066c43b93f280a553141e73d20edc1940cd7a8e504071c6fd3c265d5bda0a1e7da010f', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff4b02102704b10b1e5c08fabe6d6d33653137343463316631386364376335653162303361636430643232333230360100000000000000100000011c0000000d2f6e6f64655374726174756d2f00000000027eaac531010000001976a914cb594917ad4e5849688ec63f29a0f7f3badb5da688ac74aac531010000001976a914a3c5284d3cd896815ac815f2dd76a3a71cb3d8e688ac00000000260100102700003665ff5272eb8e51eb03705953a0b3b364fa6a179ea4448d04c4f37d39dc31cf', + newQuorums: [], + deletedQuorums: [], + deletedMNs: [], + mnList: [ + { + proRegTxHash: 'a1ecd9a9b06a536b6e4c7ca30a6c606fa348facb88f9099055b1c289c21e9142', + confirmedHash: '0000000ae0cd2a094068ba7ffb6ef20242b08b748ffbb517ebd56fc77680ef78', + service: '63.33.238.85:20046', + pubKeyOperator: '08b32c435d28b26ea4c42089edacadaf8016651931f22a8273feedc3f535592f2ea709aa3bf87f9e751073cf05b82aac', + votingAddress: 'yRsrjxkDjYjbDQntStCbTLFPbJoiPpNL32', + isValid: true, + }, + { + proRegTxHash: '71cf5017c4c5f69db5c17a8cfb4c28ffc14ad1715dba2a83f0c30e534291f828', + confirmedHash: '0000001d265e101abb16f78133ea20f57cb2651108b24b506eaf41ff282865f1', + service: '95.183.53.17:10007', + pubKeyOperator: '10d647e3107b77440e2e9957092aeadbba86d02eb95ec23e490c023936bbd4eda6cf8850f98d01bddf4db0a405bc6a37', + votingAddress: 'yS7M1mbiyT7jk2aTUwMcBA8hHd1jDSjgUh', + isValid: true, + }, + { + proRegTxHash: 'd1cba31fe1f01cc4e9d18245815cc18b5779c6c5b41faa070533e38a5b425b73', + confirmedHash: '0000001ffaa9dcfa755922d7715dc62dab33950dca2d62781b9d8f27bfa141dd', + service: '95.183.53.17:10012', + pubKeyOperator: '0f9764003b7ede1d0d01f2cf16fc0f706f5394d2da1bacda404615c60d5bcb0b22a76776fd9be00f1d4a4a668ff3fa22', + votingAddress: 'yRsuGQ1q9Cw6PbYuHx9GdLV89e5qMXDYnM', + isValid: true, + }, + { + proRegTxHash: 'c24aea30305d539887223fd923df775644b1d86db0aac8c654026e823b549cd7', + confirmedHash: '00000012b002b15f3b0e003502f37b181f158efa3392de3139cfffa4f79fafbb', + service: '95.183.53.17:10001', + pubKeyOperator: '845e9bf2879d98ece4aa8b78ca074e32f968bd93bac973a1abafd61f900b70e7178b6352d830d0fecc2653d0f04a9151', + votingAddress: 'yYrZkUYznVBbTsRPZ6yvaisT7Vv6EthwKM', + isValid: true, + }, + { + proRegTxHash: '9f291bc9b0e2787b71537af15d3252b737fd8883db82e9357017348335d785fe', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '18.202.52.170:20000', + pubKeyOperator: '157eff76f9632db9536c8af64a2283f3da7f91db86dacdfbf193ada958f69980e18d8d1f44d225fddcffc7176a941a26', + votingAddress: 'yWeq37yJ98RGK5xGDq3JXVt22fuiSU8KBX', + isValid: true, + }, + { + proRegTxHash: '1ea08097a3c977971205b7643d7a2ffc3983e9329aeb9a928a1c82dc103fba20', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '63.33.238.85:20006', + pubKeyOperator: '983c80e3e31fea6f3d56d54059e8c95a467285f33914182f1e274616cdbe2f1e1c6c0c7dce13710480ec4658208e9392', + votingAddress: 'yjUMHuCQ2V8Xp9iTSKE2u1NYfDar98zNCC', + isValid: true, + }, + { + proRegTxHash: '732396d0eee88b722c9575f92c5cfa40a5a6eeee214dbc62ee11df13e9701ba0', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '52.50.208.53:20005', + pubKeyOperator: '8dfa69a96f23bd77e72c1a00984bb0df5ce93a76ca1d20694e8ad20b1dfea530cb6ee0b964b78ebb2bc8bfac22f61647', + votingAddress: 'ygtDvoyioQ8Dc87A3moFgBrd97QxuiMiAR', + isValid: true, + }, + { + proRegTxHash: 'a3a2915965e5e263c5789932879557508ac41c1154c3810ae7763c69a18b9521', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '18.202.52.170:20024', + pubKeyOperator: '0a9117edbb85963c1c5fdbcdcaf33483ee37676e8a34c3f8d298418df77bbdf16791821a75354f0a4f2114c090a4798c', + votingAddress: 'yQqPooDXiLKcEeLToXCnk3ZM6VqMLvsJbs', + isValid: true, + }, + { + proRegTxHash: '2b1d349e17f96d66305946a7de42fb33d31fca2c5c2ad4c2aa410bc0ae85e941', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '34.255.15.20:20011', + pubKeyOperator: '8fd425f945936b02a97c8807d20272742d351356bff653f9467ab29b4bdb6f19bdf863ad5a325f63a0080b9dd80037a9', + votingAddress: 'ycvJs8DKEuYHZJx3Z1WLJcJ2MLDw1NKK1s', + isValid: true, + }, + { + proRegTxHash: '8ba0fe271323b4341badb2d07c47879ce92d9eeb2873a9188ecebd0d7e200601', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '52.50.208.53:20009', + pubKeyOperator: '8348757a2ed830f3a40f0e52ca4823f48c1ab5017dc424ee68c1e8fad27c0ab008bc974866db18bc76bc7d2bebb29976', + votingAddress: 'yZyThF2XQaJhceS78toTTzh6XbrUqTsziR', + isValid: true, + }, + { + proRegTxHash: '86b8061fb7fe866b492b84e85aa0548f68ff376c4cbc5893e46ae361a5e57241', + confirmedHash: '00000012b002b15f3b0e003502f37b181f158efa3392de3139cfffa4f79fafbb', + service: '109.235.71.56:19999', + pubKeyOperator: '8d1412ff39045ef39c2e19a75cb3ad986afc14c3139ed0a3392b41d471558676029a8137f95b0ba0e7315bf11c497f0f', + votingAddress: 'yeZknaGXQ3Sf7o22MxByRzeYdbRK2JKPDu', + isValid: true, + }, + { + proRegTxHash: '4054665b984881d62cececcee88b327976ea580360b312d1782bb47e26aa6781', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '63.33.238.85:20026', + pubKeyOperator: '07e472824512fd8004e7c81c3dff74c72f898c2a25f71246de316f6dbc976fe4e54d103e6276e457c415f53ba867e1d7', + votingAddress: 'yf7eFff86f8Wx4CcLfBiWndtoGU9Wr4Yum', + isValid: true, + }, + { + proRegTxHash: '34a11387d08ff7485ead341374c21ddc8464ac6db7fdb41c0ce87ab6dd00d7a1', + confirmedHash: '0000000ae0cd2a094068ba7ffb6ef20242b08b748ffbb517ebd56fc77680ef78', + service: '52.50.208.53:20045', + pubKeyOperator: '88a5857ea0eb8a5fe369bb672144867c4908300089472108afb9d54a70f7d6e4b339d01509dd9231da90b14cb401df2f', + votingAddress: 'yLMz775PpXrKMGx3SzDTDisH24hMExhWax', + isValid: true, + }, + { + proRegTxHash: 'fbb1a1aa283faeb8082a7331c5010f13272f7ce6cb24845b3d1f260f7cb75423', + confirmedHash: '0000002910988f956b7d9199e4fddfd9462d79aa1c6e43205dce157d208e4ab0', + service: '173.61.30.231:19012', + pubKeyOperator: '96a9d730b5800ad10d2fb52b0067b5145d763b227fccb90f37f14f94afd9a9927776f9af8cfcd271f9ce9d06b97af01a', + votingAddress: 'yc8Ji5CgQujxfxcP8eCqu82WoBD16tcGnt', + isValid: true, + }, + { + proRegTxHash: '211cde8df97129edd3a5558378d5f7974c08d95a8197190f0fac3aafbd5d84e3', + confirmedHash: '0000000ae0cd2a094068ba7ffb6ef20242b08b748ffbb517ebd56fc77680ef78', + service: '18.202.52.170:20048', + pubKeyOperator: '0a10b1fec64669c47086bc0f1d48ea6b37045f7e46c73c5ec41f7576653d7a6d7c79bd1215f16675bb31a59a7137241b', + votingAddress: 'yXqv5ad6ujBxqThyinopZHK1as8UWcLcVW', + isValid: true, + }, + { + proRegTxHash: '04d06d16b3eca2f104ef9749d0c1c17d183eb1b4fe3a16808fd70464f03bcd63', + confirmedHash: '00000005d7fd53d3400f4d79d22d41cd1684be4a512f6cfdc266ade5081b5279', + service: '45.32.237.76:19999', + pubKeyOperator: '02a2e2673109a5e204f8a82baf628bb5f09a8dfc671859e84d2661cae03e6c6e198a037e968253e94cd099d07b98e94e', + votingAddress: 'yMLrhooXyJtpV3R2ncsxvkrh6wRennNPoG', + isValid: true, + }, + { + proRegTxHash: 'af382fe86ca47698a3606d976e50251488167eeec3a5cda9fd9d9b3a2823ba83', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '18.202.52.170:20028', + pubKeyOperator: '9446b87f833f500e114d024d50024278f22d773111e8e5601e05178005298e5fc2933e400e235c0a51417872f68cc20d', + votingAddress: 'yXBtycNjthg5vzo4gs9q6AmoWwZfDxFArC', + isValid: true, + }, + { + proRegTxHash: 'f4a7b187cc6449322c86077f6fad7a2152e6254c688414d160cb36162673df03', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '34.255.15.20:20015', + pubKeyOperator: '067ad7a999ad2dc7f41c735a3dff1d50068f0fc0fde50a7da1c472728ff33f9dd6b20385aaf3c34d9a259dcef975c48b', + votingAddress: 'yaXKG1pWsRVSDq2Uf6LKNJWXzaRP6YjR5k', + isValid: true, + }, + { + proRegTxHash: '682b3e58e283081c51f2e8e7a7de5c7312a2e8074affaf389fafcc39c4805404', + confirmedHash: '00000018c824355520c6a850076c041b533d05cbe481f8187e541d7e2f856def', + service: '64.193.62.206:19999', + pubKeyOperator: '05f2269374676476f00068b7cb168d124b7b780a92e8564e18edf45d77497abd9debf186ee98001a0c9a6dfccbab7a0a', + votingAddress: 'yid7uAsVJzvSLrEekHuGNuY3KWCqJopyJ8', + isValid: true, + }, + { + proRegTxHash: 'dcf13b43b5bbb0b4600e513af574da87f2e923c91e1afc017b1ae954e82f84c4', + confirmedHash: '0000001ffaa9dcfa755922d7715dc62dab33950dca2d62781b9d8f27bfa141dd', + service: '159.65.233.52:19999', + pubKeyOperator: '15e97fb8029420a71f7125cbf963696c3fbf9636f6d2fa8997d35d37416e2c837182f2e7b7623498736253e5469eb894', + votingAddress: 'ycd2G5zUax8hSkCJ136SGw1WCy6V3jrgEB', + isValid: true, + }, + { + proRegTxHash: '5f69bf1e7c4ab649e93987149de9872e70052bafddd3943aef27d4a234859ea4', + confirmedHash: '0000000ae0cd2a094068ba7ffb6ef20242b08b748ffbb517ebd56fc77680ef78', + service: '34.255.15.20:20047', + pubKeyOperator: '9155ae06f2e689f4fa68d5ff89e0d95feeacb431cce7065615d2de64095024e1b60bdfe740e5da5facf13cbbe9d06960', + votingAddress: 'yPpM6PYxZhnvvbLb8zdwYa9xvLo397JLub', + isValid: true, + }, + { + proRegTxHash: '300715758c4dd712ab80899630ff0963d4ce8e824778fafae02c82272a420725', + confirmedHash: '00000000d03fe6c22a10c02e93a8c095954c2e0be0720479dcf779c3e7fc4566', + service: '173.61.30.231:19017', + pubKeyOperator: '8bb67827af87431673e737c49312c5a16fd284daf1c4050e530b604ec4f85f217080503f978a6bec89d1ad4bca089c32', + votingAddress: 'yPjodPW2v1VMJBteJedW38qtdbeUamfCyr', + isValid: true, + }, + { + proRegTxHash: '72e01d2c86efafb7c278d5b957e8561b3f83719d12cf0540d3cdc996932f19c5', + confirmedHash: '0000000004e6cf5b3f1d1d1f4aa8647730381fbe4ef79f31089b1ff361d51425', + service: '140.82.59.51:10003', + pubKeyOperator: '8e21a1a12d5638afbe0cc50b2d61e0deb6553dcd12b84dfd0606a61b2475031f814207f613debe5791798d77f1ea4708', + votingAddress: 'yXWgUSNHjZfa1XfYZ8eqNrqBJ6c8YiMCMT', + isValid: true, + }, + { + proRegTxHash: '077f1594689f7600ec1b8afa5bd1716e8eb66a894faf2169abf8c47c59d551c5', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '63.33.238.85:20022', + pubKeyOperator: '89fd3e2cc5690053c4252a2a95fccde944b141a3ac8e6b8c36c6b61e71d076f5cc4f9ba0f191d8051ea9b5c51cc58480', + votingAddress: 'yUW9m3m1fvbjfAUvXQHfvmxdbMpNvUhmU2', + isValid: true, + }, + { + proRegTxHash: 'dfc7416ae1d8829641d556469f45d2993892118d94fff8c146ff962d15f4d486', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '52.50.208.53:20001', + pubKeyOperator: '8312e0ba7e4ace816595ade43d2293d70c3dce6b3e7e0ce9e99016f99177277bb42e6d3c2d687ab3e8bed13fb0d34890', + votingAddress: 'yMwuLgJqfE6E382trVSq4fmgt4vX3W5pQx', + isValid: true, + }, + { + proRegTxHash: '759831c73c67a0bd43d2f877f8c38ce43225d89970e92999e703740335c11d46', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '52.50.208.53:20013', + pubKeyOperator: '8c8c7a5b96ed96dd5dbc40db042c301a9d70d5cb98ec073d41cc6a3c68d73ef0d6524cfa210ae1496a880e50fca3fcd1', + votingAddress: 'yUjYs33h2jZMhAJBG5H9S5e2Aa3ghUh5Jk', + isValid: true, + }, + { + proRegTxHash: '43095418a72a5468fa1a2a147abf80853de68f67e060e298bd1aa31c23772686', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '63.33.238.85:20034', + pubKeyOperator: '8077763a1a91d7595a05b06f805430cac72fc6737a0c0161624dadac33b21868d903d85bed5fc491be49f0653b8779bb', + votingAddress: 'ySETbyjEjE3E8GPReY1s5jjLhqnLYDLToB', + isValid: true, + }, + { + proRegTxHash: '83886e9f5d20ae160d94ac66ee73f522684c7aa8de5a9746ffe5cdd2257c8427', + confirmedHash: '0000001960431ec5a566e69f28ae0f6fa3199bd99ec527cccd02f7541d77300c', + service: '95.183.51.146:29999', + pubKeyOperator: '9426621a0df5cd8a4432c4050f39163a76ab39b2682aa3ea2064993265d66324be3d45ab22d5f9910c8ad09b96bbc952', + votingAddress: 'yg5fiNeT9MLoKpHNnxxpHL6Uaxs5pLzQYh', + isValid: true, + }, + { + proRegTxHash: '392758a012e0ecc60d02a9a393f2d38b4cb141116e70df6d998925db4c4c9667', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '18.202.52.170:20032', + pubKeyOperator: '8e74c08f84a93b8831dd42e76537e8a17964123293de69c1cb24097035e0803822eac311434638fc73a5ad4373947542', + votingAddress: 'yUYbteofbMSr6byjXfRbbrST2z3DpvU9fg', + isValid: true, + }, + { + proRegTxHash: '5672ef82831d55855cc0c2a826caf637c0d32e013f183f2073c5cb5481b5d8a9', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '52.50.208.53:20041', + pubKeyOperator: '10a2c0d103b24425b2359eac65b6997d2207c43700eed6371616a796c0a333e7401ad3a51b565a58bd7d0e604a0c80e0', + votingAddress: 'yTBbJc764WGxQKuihMpQzDoa7VKQjvZzCf', + isValid: true, + }, + { + proRegTxHash: 'f0567069d4f2a2e536e46173a097b318daf03edef989f6875ca06f5c4d49abc9', + confirmedHash: '0000001511beddb0eda1c353a019eab5569433e7088be2799f85c953d86871f8', + service: '95.183.53.17:10009', + pubKeyOperator: '865d6f26ed3f5309e4aed19583cf179bc779e21c967485f355b214ffb6ba461a01b575a9c62b3a02d08a37d01817af83', + votingAddress: 'yQYRfZvQxaQnjR28AkCtmvHFHP22p2PLKR', + isValid: true, + }, + { + proRegTxHash: '999d7bdf3c9247c61681148dafe7406c8407f0d07c4d699a1f501adc075248ca', + confirmedHash: '00000000d03fe6c22a10c02e93a8c095954c2e0be0720479dcf779c3e7fc4566', + service: '173.61.30.231:19016', + pubKeyOperator: '101d302d6c69d9ecb9e13e755947f3af22f63ed4ecbf466ff64bd35c3d86bf2e4d8455ab736715d8f064c8c8e4d3c585', + votingAddress: 'ycyDoK4GK4GR2qdNZ5UXjjS17VnJbQ8wm1', + isValid: true, + }, + { + proRegTxHash: '3ecdbedf3d9a13822f437a1f0c5ea44f290ab90f7c3bb42c1b5fd785b5f9596a', + confirmedHash: '000000376edfdde23aa3f08bc83fa4d347759fbca92bfb8adee2bc8426c3b9d2', + service: '108.61.192.47:19999', + pubKeyOperator: '0634f8b926631cb2b14c81720c6130b3f6f5429da1c9dc9c33918b2474b7ffff239caa9b59c7b1a782565052232d052a', + votingAddress: 'yNr4BzdbZy5kGGeuhoFThj2XjhaVyFQTxS', + isValid: true, + }, + { + proRegTxHash: '3d118ac5c2e1522bd55b22240affa2c57c081bb1a03801514911e37ecbabca4a', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '140.82.59.51:10001', + pubKeyOperator: '97e409f5889b8c033c412a939d2419824a2b0321e29c357a43bcc74644d1945c6a5fe7f8977edeb6210cb038039bc30f', + votingAddress: 'yYQphKcARz4S5g9iZxzPbyXgYwwcNzNYJU', + isValid: true, + }, + { + proRegTxHash: 'e157a38e02aad5da99fa7792d07eb8b773ccfef9084d892580b5a920741d72ea', + confirmedHash: '0000000bf6f060f5ad57947c355f8ffc9df1563ac2698a5ddea6c2c605cef576', + service: '173.61.30.231:19014', + pubKeyOperator: '983ca9ab507b3eb4e7b0d31ccef3f4553493ee5334116a3f79689f9b808a201ead332a26f7052fd17123cf142f96d85f', + votingAddress: 'yeLNezEUhMaBEp4Y3qiGwhphJbz244UQsT', + isValid: false, + }, + { + proRegTxHash: 'fb31a4e020f7ab36b07113f4c10e193c9cd4818d4609344a90b85dd2617d9a8a', + confirmedHash: '00000006b4dd9f4065fe69321f627d57c14d0c7a55b8dd7001049372918257d7', + service: '140.82.59.51:19999', + pubKeyOperator: '8b0c48578e5bfe77be25aec9e2745c8e699a6069411b3d9f90703a9f4dcce37bd62b511f3ff089422a7ba29d46e2b616', + votingAddress: 'yX2VYLpM9ybXZHTbTEmND581QZSNRHucWm', + isValid: true, + }, + { + proRegTxHash: '7c3746e847b2e4db9dd60885daf86a255029d2e9f32abffe9d18628653983e8a', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '63.33.238.85:20042', + pubKeyOperator: '86f43bc634d21567e456fea9ff6556e361d00da0ee46955244009847e492402c8dc82b4247330fab96ac9f0c538496b2', + votingAddress: 'yeueeuyrqLsEndqXCBTCZeGV9qaeBCYJ9m', + isValid: true, + }, + { + proRegTxHash: 'bc36e6c0d0c69173ea0c8a9a821548468e7713ab9bf748c117d5404b4450f86b', + confirmedHash: '00000024498196b428e004ddfad17ea89f7f3c9650701d17656efa8dd9c3d68c', + service: '95.183.53.128:10001', + pubKeyOperator: '0077eb37d4559f880e21dbc3840a1a8ec8c32787fab07bd12e7fde1ad5f94ae95d6e4694f3533799d14e18c683249742', + votingAddress: 'yZQf2eCE9mRL9Fi4ADof7PfkjazVQu1nSU', + isValid: true, + }, + { + proRegTxHash: '25eb70d8933184aa317be2cde762ae59c473716745b5511c0502c33bff8fc18b', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '63.33.238.85:20014', + pubKeyOperator: '1393265406093dc6bda557446b9808a40f13896a683bd9801511c75752812f4a1ad4ecd3d9e9cf4c3afc62bb6cacdfe3', + votingAddress: 'yYabYZb1wNdzdTK51EKrP4LW7L6AzgioV6', + isValid: true, + }, + { + proRegTxHash: '78c43475cc270d075a38bf9959c590492dc8682a6feb46157444d29de4a13b8b', + confirmedHash: '0000002f23cb3daea22301ce75043b100d78d4f58532aa9db486889b595f274e', + service: '173.61.30.231:19008', + pubKeyOperator: '1828028671209b5196d2204d5bc3ce3ecd554dee9ff231883f04e67bea856fcae19d7a6154039140e9e3a6c6cf3fad4d', + votingAddress: 'yS3ybhnzXDryoVqqxcK7YXfLyGy1pSctYr', + isValid: true, + }, + { + proRegTxHash: '0d9a0f5d015a07445f0aa0d6a3a2287772b3a4f56688e39d716b354e9e9eae2c', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '18.202.52.170:20016', + pubKeyOperator: '9179babbf6ca397dc089cbe29eaffb58ffc0afda1d6c7678ab3739d5b63c7f90428cbb4c4079823a3a71a25bf89f56b3', + votingAddress: 'yefQepGeNtoAL9TKdGMFEtQFQ8jvRmeZFy', + isValid: true, + }, + { + proRegTxHash: '64964c3ae5bfc2a2aa100fa3dd4e73b3b341f8b52cd8890d8223abc4d2b8670c', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '34.255.15.20:20035', + pubKeyOperator: '90a7a1f6b509e1f13c56262b8ebce0129cb751b16d8cd681634e62714553bc4dd773a88adf16184b9f951a9b8f0d1d54', + votingAddress: 'yd8FA6nRnyNzq9gwGJS1u7Wo11kCAqx1Gr', + isValid: true, + }, + { + proRegTxHash: '5bd32dd06449e0e3b8dbb80966d7fd85dc17fdaa20564bd48e8a326083e7bd0d', + confirmedHash: '00000015869589bf306f0865735ec17fd38d8487c4642492c6a5921d9d3712d9', + service: '18.202.52.170:19999', + pubKeyOperator: '16657cbdf9339c69126511f50b07253b69d7137f225d601d4674e2829091c4aade3afd83f9b1f3d8f0205b9e73603aaf', + votingAddress: 'yfe41ZS2jeq7XoZZKwQD9qJQWx9vAkxdoX', + isValid: true, + }, + { + proRegTxHash: '168b530d4d39ce974a02672d7fa7dc87734419bb23cd2ef2d76e53e60bf5e98d', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '34.255.15.20:20003', + pubKeyOperator: '8e5e237e8bc750e5237f3c63bdd80034be58f6698ea1a696c29ff7f81cc251dabc5f925b65289e428461e2c74ba894ef', + votingAddress: 'yMMMQ14LDHomAt1eDjcmTG75kpvWv9G3mM', + isValid: true, + }, + { + proRegTxHash: '0022afbe93054ca11ce9b67892661af4558597bacff0ab82bff05a2b4a89ca2d', + confirmedHash: '0000000bf6f060f5ad57947c355f8ffc9df1563ac2698a5ddea6c2c605cef576', + service: '173.61.30.231:19015', + pubKeyOperator: '94b7723262031b6cd2e79b07f36a794d3e684c538a6f2418fff01c027fab1ca4663ab0b92670ee1797fa71d8676362a0', + votingAddress: 'yhyMruXFX6waubpxGZx37FBF5r5DRb1QAZ', + isValid: true, + }, + { + proRegTxHash: '7bbb0071042e0a4aaf4fa2518d0bc717eba08654f65278957a9a1edfe68cd10e', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '52.50.208.53:20025', + pubKeyOperator: '0bb7f50754a1fcd59d9d13bb7487060a7e6c9226af162de0d173b132e033e8da6e0f53eff5a1fe3a3d63e853217611a4', + votingAddress: 'yR6qVjkfY4oCnTwXYQEVxqYdgDGNqgYXdL', + isValid: true, + }, + { + proRegTxHash: '8eecbc2ec317e3f05bc6daa16f800a20f9c6a311b9c5e870848a4e418a294aae', + confirmedHash: '00000024f9d5cd5a0b26d87d0eb18d41801e08d2676a2c07ce833c35fa9dc084', + service: '95.183.51.146:49999', + pubKeyOperator: '92f5e861ac88ddd95e3829afc45f9358ea0973e19da8e42eabbfe8f2d9e5fa32204e7f1de5e20c7e45ee51ab262cf7dd', + votingAddress: 'yWYP75RuRAQ37BGzUKASxcLHDP4b8AGxHY', + isValid: true, + }, + { + proRegTxHash: '944ac1d12972e795139af01657a2669a24aa581d496995889a4c8e31fe8b248f', + confirmedHash: '0000002baf0d12aff303eeda62cd81a4fa9a64c70be23186e936f4eafb0e6ff5', + service: '140.82.59.51:10005', + pubKeyOperator: '9685ef9d056c2497dbdbe95e605f09e6b7fb0475051cdca625b53e3f761f20ce7353949e6e433f5cdb9cfca7ea080569', + votingAddress: 'yZpCWsMtKZ2aGG46oE4HdNDEP5KdJHfCAk', + isValid: true, + }, + { + proRegTxHash: 'c48a44a9493eae641bea36992bc8c27eaaa33adb1884960f55cd259608d26d2f', + confirmedHash: '000000237725f8fe7d78153ae9c11193ee0cda18f8b48141acff8e1ac713da5b', + service: '173.61.30.231:19013', + pubKeyOperator: '8700add55a28ef22ec042a2f28e25fb4ef04b3024a7c56ad7eed4aebc736f312d18f355370dfb6a5fec9258f464b227e', + votingAddress: 'yTMDce5yEpiPqmgPrPmTj7yAmQPJERUSVy', + isValid: true, + }, + { + proRegTxHash: '74f428a70f701afc33b26dd89dba3e25a3ad42168284e64c79572cdc69b7bbaf', + confirmedHash: '000000373f1b228238e7a411bc2ad3719cbd9a475e880ea55e24fb5ea24aa3f5', + service: '173.61.30.231:19009', + pubKeyOperator: '925d20af1a6d0ccd3890f0aead4a05a59be22e005b6d732f855311915b351a9153b2c83d84611b2c9958f806c93f7b5f', + votingAddress: 'ybc3AmPjvoGD2b2gfd6iEsZiv4h4KAtS8S', + isValid: true, + }, + { + proRegTxHash: '1b5b9aa83d255f1735a80fd04eaf5717a51fc2c6eae8a42aea21b3e70081f290', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '34.255.15.20:20023', + pubKeyOperator: '82dd9b3523f18f7cb1eb08ada48b7940eaf46aa9ed6cd1d79fe702d5d5689cbb552da3fa27a5f07efcbbb05cae2d5585', + votingAddress: 'ygGmGKqQfgzdU2WeYL9DRZHBWjMEtXMaNw', + isValid: true, + }, + { + proRegTxHash: 'e9fff737c567958dc075b18f77365a2a0d0c06643dc9507b84b2c1acc0a623d0', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '63.33.238.85:20018', + pubKeyOperator: '1266fc3da4ce754a1c26d4f5656a5f9a3217cfdf70d2595c75b5e191041b3224c55a3542248a63a94b0ca059012aa7f6', + votingAddress: 'yWHoa7mF3UY62MVMEX2JqRE6wsJkHp5dsi', + isValid: true, + }, + { + proRegTxHash: '0e58d47c0950464240821082385e02c0ef6f6d4ae67dc3de317d9afc5c6400f0', + confirmedHash: '0000000cb8ba803c8ca0d944460c1fcf4c66f8d30857518f364418fecad4c599', + service: '35.161.101.35:19999', + pubKeyOperator: '995d3388b0289eccbaccfb505ebf86c8186507b5fe4b6f137ecdd7769340eda6cf44355b51493e35a722a40809cc4223', + votingAddress: 'yZ4kNmnaHuShGKvpxbSLEnA59RhWerqjst', + isValid: true, + }, + { + proRegTxHash: '3d72d66cd9931c93d5ebf7595f3486ab1d1de255544345220a1999751b8e30f0', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '34.255.15.20:20007', + pubKeyOperator: '9470f54b992d3359b3a1deeb973a9ae4dbd0aa139b713448cd04547138f5855dca3065bcc61b1f1ff3390fe040f58337', + votingAddress: 'yM7mTqt8e2JhvB2fgWna2qATcrawEU14Dz', + isValid: true, + }, + { + proRegTxHash: '72a6a2a5c2fb260fe3d41913ae019feb1d2489867e85f57cd1fa994bbe3458f1', + confirmedHash: '0000002666729a05b9de9021413132d9998be62719fb9f4c4aaac6f6a33e1318', + service: '95.183.51.146:19999', + pubKeyOperator: '987d8b49e8aca918aead0d50b28fd0f61ed166f28b6365acef6a9aaee144a692f5b3cce00a40719917a042d16d1849b8', + votingAddress: 'yMr6eatVutuPXxqBM5c2N8F2r1GKcfoe17', + isValid: true, + }, + { + proRegTxHash: '47949123a17a7ded7f9c2db6facd2aa710bac181c886e994229c16e281102111', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '63.33.238.85:20030', + pubKeyOperator: '8260a3c57731ad90a95499e04802e094e4daad6a2ccd242761c1849342bd8cad744dcc5ebc3301fb9513c30ae82e8923', + votingAddress: 'yh7z5Sgzm8NHz3LvoufxzK22HCYT5TT7wM', + isValid: true, + }, + { + proRegTxHash: '584d6a433ae01d2da9d3cd56be5a8558ceeb6e95e699c1ffcd4c61b362869ad1', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '63.33.238.85:20038', + pubKeyOperator: '873c0707fc70cc07e162664bc5bd0d61de2d8f2af0d0bf543c1411bf7a713360435923cfadaabc994de9156cabfd352e', + votingAddress: 'ye7nQkheoeDiHtYrYMoGNwUKwEu4iUC2h5', + isValid: true, + }, + { + proRegTxHash: '11eabc1e72394af02bbe86815975d054816fe69006fdc64c6d7a06b585e5c311', + confirmedHash: '0000001fd4305c32af36cbdf651cf1585e1a2a5b93a871186c63cfbe67c8eb8e', + service: '95.183.53.17:10004', + pubKeyOperator: '14926e7ba179612df5cb1cc4ebbe311cfa9679e41f14ed7b35d12cc33d419073f013bf751be85f2b50e28910df332463', + votingAddress: 'yT2owpXECuYHnZ8HuHuHWD9anynSQrfcDL', + isValid: true, + }, + { + proRegTxHash: 'c32e9e14c81665699b121e886146c2fa4b3b933ff3b71a534755a3431634af31', + confirmedHash: '0000000ca88728d3a57bbf3b80f3d73af03078e6b6ffb5a259343f2e3a3f1dfc', + service: '95.183.53.17:10002', + pubKeyOperator: '09f87f98c0ad49811131a31e94d875bb6c88f64226727a508094ea8e5f25f8f6cba8d2fb27f0f7e662233c565c1cf114', + votingAddress: 'yhmDRodXKq7kHLokEdCYfnGYvuuiBL9rGc', + isValid: true, + }, + { + proRegTxHash: 'eabca05e0a9d32e0c1a4404b785a3ac8be291b626dd8f1faf485a615999c4532', + confirmedHash: '00000026d70f93c4ddc95f201eeec5ac3e0355f567fc58f0303af34823d45d27', + service: '178.62.203.249:19999', + pubKeyOperator: '905caab51ff07a2f8d69972fd6ec09f6f9893cf6dfc49775f5a2db2ea7a8a525bbaf4e7e369d06590f6f2e8e4658d4dc', + votingAddress: 'yN5GKRn9zTKgaVTo1uJxihub6sbD6bFMG6', + isValid: true, + }, + { + proRegTxHash: '4599a965f2a0215421e2d18d3f0700b6aca162529388e0f83de8f093a4f53552', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '18.202.52.170:20036', + pubKeyOperator: '172cbd28e4bd100792c4455d761c523eacb2fedc49b6b20c67952e2ae5446d11931815254756a37d21d553131ce4a09e', + votingAddress: 'yN7pfUm9UbPywbHVg4ayiUynweWXFTc6nk', + isValid: true, + }, + { + proRegTxHash: '89a6dc42063e4a792ec225db64dd9426742a5d1738e8821625d2ab920a6187b2', + confirmedHash: '000000380f38c5a7dc5165cfd6d8ceb922fb7f601c4cdb6e8c34970400e50fb9', + service: '173.61.30.231:19011', + pubKeyOperator: '07ffa44583c9908f4aaca8dd97990c56043e475723f90940ef5fd7d493152540f25f58fb8c965ee5e1be4f850a661476', + votingAddress: 'ydyWnUXrJAUEW3sr56yX8zvpV7xPWexMf7', + isValid: true, + }, + { + proRegTxHash: '90f15e31b761fb2298e4799a4f66a2cafa2ee6a02a67c5c6288c6972085b2bf2', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '18.202.52.170:20020', + pubKeyOperator: '071be08369f71e4b4b4e587ff64eeff402dba0e9c6dbecfe3b3f80bcd2bbbb433c01e42191eb0f5e3a95e56d8cb4a085', + votingAddress: 'yaysyRwnbqWcxK5tty79DsZ9uixhqije7z', + isValid: true, + }, + { + proRegTxHash: 'f5d2276a70a9a4c7f0e9de32e533ce15602ae1ba3d60d3f6eb67e52c7c488074', + confirmedHash: '00000024f9d5cd5a0b26d87d0eb18d41801e08d2676a2c07ce833c35fa9dc084', + service: '95.183.51.146:59999', + pubKeyOperator: '95d5badff945693fd24158932b41e311e6fb3cca1e1e551eeed72cddba2e3b04abe86547a265fb7ee958875f9c33134d', + votingAddress: 'ycanojRxMxeW3R9kVN9Tck4NGsSNusrQBo', + isValid: true, + }, + { + proRegTxHash: '7ec4f1ad42d9785458b8563145da87d076b87ab05fc55377fe98a82b025e4974', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '52.50.208.53:20033', + pubKeyOperator: '81d241a9f83b7dcd577fa215b1b2745cffff34d26290139bfbe30e8884b1e34fad596de7555797429029cda262f3c406', + votingAddress: 'yPn9geUNYt58gTXh71K3gQqY2jHpf6R7w3', + isValid: true, + }, + { + proRegTxHash: 'b24c7154d38573b36882ecf94a29ce34830b5dad17f98b56ff0e6bdbeb82ea54', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '18.202.52.170:20044', + pubKeyOperator: '19c7b27ba7332cf4641137836b3a7ea78b9a53672a28d2ae5a4507dca234e5cf9a64406c98d96f120f3398075a23b0a6', + votingAddress: 'yScUURqWmpsuYqLhYQNB8qzR5VbC85H29G', + isValid: true, + }, + { + proRegTxHash: '6d0008b79442b8cdd9bdf2b4e71bdc6fb2031e7fc2c9b55540476db4d2c202f4', + confirmedHash: '0000000d6bf29f5ea6b994889aa66b6442bb3f7d35c09b60dcbbc0cc92ce87ed', + service: '45.63.120.150:19999', + pubKeyOperator: '95fea099d4a11d784125af21a4f837c4dc0cb626f48a756c0426baff1687d3aa63a1f0cd3e1c5dc7040dbe3c8cf00328', + votingAddress: 'yMEMdbWWVTD7JCLeF1G8YPjZYiTegWfwbr', + isValid: true, + }, + { + proRegTxHash: 'fa3b3b0d3522becb02ddd15dd075f3d6ecc6a5a50b43c6c9f6d4703a9a8509d5', + confirmedHash: '000000380f38c5a7dc5165cfd6d8ceb922fb7f601c4cdb6e8c34970400e50fb9', + service: '173.61.30.231:19010', + pubKeyOperator: '89e308c9d2d8a3cb35f9d7bb7220b1eca82c952b82111119670dacae18a509628c775287e4e796128cd6379b80dffd7d', + votingAddress: 'yZC4fLDV1enraYJeXbPGskMfgHVSAADPyg', + isValid: true, + }, + { + proRegTxHash: '4ac4449ed4de3e69a47e374b42aea77c6f70363f4e7e8b12855bc4e31d3d67d5', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '52.50.208.53:20037', + pubKeyOperator: '87203a9163f2c3d296dbb8af5d7faec7b1fd204a8b1a5ec29a1c1c420d35dc88dc681b8752dd3fc5337dda715bfbc29a', + votingAddress: 'ybfeSCyws5Eh6ntwQ31AWa4T55ggxE4RL6', + isValid: true, + }, + { + proRegTxHash: 'db57e8c4c993a3332ec7138b7a7c3e01d970182213bd766c89107db292d892f5', + confirmedHash: '0000001d265e101abb16f78133ea20f57cb2651108b24b506eaf41ff282865f1', + service: '95.183.53.17:10006', + pubKeyOperator: '0dee44e338280a8e534c9e8bea9cb9d73163070d90d511e5c83859c384790e12da189e791404126eb2fe080593ad9a73', + votingAddress: 'yi9GtcFVyD2GbXYKW2GMgtNCxiaJcCTPzB', + isValid: true, + }, + { + proRegTxHash: 'd5ec63baf37a1dc7715a5070a7ef1ada3fbb47e1bb075848405b32e5a6172af5', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '34.255.15.20:20019', + pubKeyOperator: '84d8be6b408c129aabee53bac357bddd9eab338cd6cd96333a797d7fdeed36d7dbea5166b67b2cd160d46ac1bea83257', + votingAddress: 'yNmpyeeJragNRp7iJux2fMsetXkm7oCdN5', + isValid: true, + }, + { + proRegTxHash: 'd959f417453a5fd69471bef2cb04653caaf23152e83f0df9718b3e6d761a8b35', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '18.202.52.170:20004', + pubKeyOperator: '110e94442ed21e4bd3fe5b2e9726c3df4993fc61a10f1a5f37b6504b5d64b6e02e4f3177a6786876a1b5f75668100009', + votingAddress: 'yNiUvHb3u1jLxzWjZ1EgBNm7UbUdHpo1Wx', + isValid: true, + }, + { + proRegTxHash: 'f443dd87ec7981e8630ae957f295d9d226d4bd3895f59dbd80b30137a92b3735', + confirmedHash: '0000001d265e101abb16f78133ea20f57cb2651108b24b506eaf41ff282865f1', + service: '95.183.53.17:10008', + pubKeyOperator: '9809c680a8b7852279f00438526b2d940e65a0e746725adf2bf00ffc054ad2601b9011cf1edbd391426afd1b204d696f', + votingAddress: 'yWtU7dWwTo6G5DTZxY1rorAbNjoycYARnT', + isValid: true, + }, + { + proRegTxHash: '3fcdbf25a409b06dfa2514a2df1671d0fb45dedf540ec9428b61547c366d4456', + confirmedHash: '00000022efda177193995b5216191fa997261a639dce7b2d8e443bc321e681c3', + service: '140.82.59.51:10002', + pubKeyOperator: '8a84f0696ae42026a72b89f066a4a55d3ee12545c672d0de9dfcd62ef63e8e0bd15d8febf1817ce2c76af812dbb9ab9a', + votingAddress: 'yiaLrGQSQZdwrvvB8QYSwkAH33dKGbeyF1', + isValid: true, + }, + { + proRegTxHash: 'c7a1e0341bb079e2402eb8955b720c93cf90ce65cdf20856220cd52bb7478116', + confirmedHash: '0000001ffaa9dcfa755922d7715dc62dab33950dca2d62781b9d8f27bfa141dd', + service: '95.183.53.17:10011', + pubKeyOperator: '802913fa3cc02a35fb8e1b26b644f8a2395078818f9bb3be8ad08fc8cb175f16c43e2b0aa2fc12a7f8dda3914946f702', + votingAddress: 'yf7n6338bCMiQXm1gfCSkQM9qLRajEvB9K', + isValid: true, + }, + { + proRegTxHash: '08e238ac320bc472aa664b99280663cb39073f189b0ce290a2cb11bbd80c41d6', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '52.50.208.53:20029', + pubKeyOperator: '10c0a1cc322597069f80ea22c04c4e5a442fff97ae4ec952a91eff9d8d9787760f20a227b1ada2025a6e9d74146e4467', + votingAddress: 'yTr8Ti4Gh16DHPfGNztzZ9Vhxu7tPnk7oM', + isValid: false, + }, + { + proRegTxHash: '3a8738860323ed3868f0e4a26d852bbea4feb3850674e5ca1c27fa15d0b707b6', + confirmedHash: '00000024f9d5cd5a0b26d87d0eb18d41801e08d2676a2c07ce833c35fa9dc084', + service: '95.183.51.146:60000', + pubKeyOperator: '8851d988149766aaaafca285ded50de031ce42036033e3239f4f903abda26740ba235e22d26a693136a5ac27555f3de8', + votingAddress: 'yZ6AHNESSFhC7utFe5uCAyX9DPZB72Hr6a', + isValid: true, + }, + { + proRegTxHash: '8d2f4505922cb82f7ec601deeba318ca7ed2f47b89274792dc9001ab62112ef9', + confirmedHash: '0000001662beffb7d48834d209f84394edee5601408439ca8e646b0d88cfd2ec', + service: '95.183.53.17:10010', + pubKeyOperator: '8c01a1351c0f42892d6b68c106ba584f91dcc2869f384830c968688d09becfd0f7468e7ac7f02983724a6e95a887a148', + votingAddress: 'yRsuGQ1q9Cw6PbYuHx9GdLV89e5qMXDYnM', + isValid: true, + }, + { + proRegTxHash: '5aa7b0778c53e048abacecf9e63558fea80ea270ffb13ed12cb71f9b5ea08739', + confirmedHash: '0000001fd4305c32af36cbdf651cf1585e1a2a5b93a871186c63cfbe67c8eb8e', + service: '95.183.53.17:10003', + pubKeyOperator: '940c2271fbfbe83cd9dadaf03da32e840466cd4eb0e358749d5f22da2ca22610c6cdcb664b1c082b84cd4516d73ce5d5', + votingAddress: 'yW9zeJrPv5yJf71qvNfyPrJg8Me9UdaDP3', + isValid: true, + }, + { + proRegTxHash: '2da32791d877b4dd542825055418cf7e70f08e6e32a6921f4164066a8d8bc359', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '173.61.30.231:19019', + pubKeyOperator: '932f6fc90c9dcaacdf9d836a2a7e60d090fe5e55b0b02f5a4f608a4b8235ba5aa7abc4e05f9387d1d942adc57c87f5b7', + votingAddress: 'yejV1vWQXEwYwH9gT9hXqAAg81opUvqe88', + isValid: true, + }, + { + proRegTxHash: 'f6ea9676ea585d4d3657632a6e70b8cbbf46c61cccd765cd91d7e93af6d618fa', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '34.255.15.20:20027', + pubKeyOperator: '18c60c196da02ae838b5cdddff0b84ffeaa5c72e2fae933d3a173914695f9d3f2f13a12567cf8a6445e1fd2472aad3a0', + votingAddress: 'yUzhsN5wo4PbQGMRXuKZXxU3yJViXYjVNj', + isValid: true, + }, + { + proRegTxHash: '6e182f88ed71c0580777cd753477f7cbdfb53f7b279b274cb7f3bd27a93651ba', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '18.202.52.170:20012', + pubKeyOperator: '95758d8a2466f990857e8d1db8761463d8537f3c2cc59b94db7017e6d51f47075657c5ea8d1f801c9c71b34f3cf8b57b', + votingAddress: 'yfSdBrz2gEFv24oCcCM2UPXaLfyh3KpWJG', + isValid: true, + }, + { + proRegTxHash: '0bff4a62a8b27a06511015cda40f6e6c1086d11f995ad49ffdd42254fa775a5a', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '52.50.208.53:20021', + pubKeyOperator: '919f8cf51ccd17f119db6f0bac1a37ad10e0b634c1a0cb76fab44b881fde5170247695275f0e7c10286fe58fff4a97ce', + votingAddress: 'yQSatEfTwQHHDSrsKzCTpNbzHSs938Bmm6', + isValid: true, + }, + { + proRegTxHash: '5210d1398d14202842ecf3e5c07f90993f4453eea0582e1f574aaf1d85fb26ba', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '34.255.15.20:20039', + pubKeyOperator: '83bb6205d6d010166f99a1f6f671492210caceb92271b1ea21695f8831545f0f51a80beb06b054b558aca09e49e1b1d1', + votingAddress: 'yWngu13S9tRxvaNj1QxvBEeV8ratvNoAER', + isValid: true, + }, + { + proRegTxHash: 'b470614a1a43f69910fb26429d1e4f2465f28bad7a5e409987ee748ac35e3f1a', + confirmedHash: '0000000ae0cd2a094068ba7ffb6ef20242b08b748ffbb517ebd56fc77680ef78', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yPTb1MFjyb5tryAoKdCjZgFd6rCapfYMGg', + isValid: false, + }, + { + proRegTxHash: '480f6d615c4c35f21a800f7470385151e532814984d018c1d60f4d6fd0fb0f7a', + confirmedHash: '000000000f37a8c431f7788019a1eb24d9c0a8c44daff1a12518bbfd65267d0a', + service: '140.82.59.51:10004', + pubKeyOperator: '11ffd9151f27ae5aa8f396270af2365903951a74b7b16a9e404b4c69e0ba84e1d5ba2a3259c4e7069d9bbf0bcfde73a6', + votingAddress: 'ycgE527gETTRjKHakursDRy9pDxiEixVkq', + isValid: true, + }, + { + proRegTxHash: 'c98c6303af03f7f3b2673ceece962134088e5dcc3c69a0977069c6201b26dc9b', + confirmedHash: '0000001fd4305c32af36cbdf651cf1585e1a2a5b93a871186c63cfbe67c8eb8e', + service: '95.183.53.17:10005', + pubKeyOperator: '8a209b5083c2b601ea18a04f0e92ee5befecf765486deb9643dc3b3fd193080c2659bba166f3873364964d5e8f7e4b93', + votingAddress: 'ySaqwxVfdvWnw8yMPBqoHaRQZc9YHvBk2c', + isValid: true, + }, + { + proRegTxHash: 'ea1de911da31e19643c92b544174fadc0b3afa85d06f82d75bc448d3c06caf1b', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '18.202.52.170:20008', + pubKeyOperator: '00381a3116667c251265178d35698c8a7c801a9765714c793d2dc03fbba5e9bcc9899a3b8fbbce3f07be22a36d0a4448', + votingAddress: 'yjTEePFXsGbfdgiGRtVarA7ibjbirSYwpA', + isValid: true, + }, + { + proRegTxHash: '7d35ad18be8975aca346289b2cac14d7bb5b7bda2b3999b7cd94390408a0f75b', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '34.255.15.20:20043', + pubKeyOperator: '0872f496d7ca93e1e7e0e2aafac36c5a45e86b0780de1a8c5ec665343e7204538f116c5a4ace4fa9cec823ebb8f04af1', + votingAddress: 'yTQxHQrL9b1HNpDHWNtCK8aaSaYQfXpBrN', + isValid: true, + }, + { + proRegTxHash: 'b29823c549c4da2a1ede0e66bb15a6c29224e67ad3df440a31976b4dc7c78d9c', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '34.255.15.20:20031', + pubKeyOperator: '1243421c72eb76a1d3058de901007b28211ca225466fd7fb046ecc0fbedb3d8ff59c144a46f720712e4525dae6bed584', + votingAddress: 'ycunNhazgfhB3SdJLAC4PY8KY7JC83ei7D', + isValid: true, + }, + { + proRegTxHash: '8136f262ed06c10ec97bef89b799142c4e5e30d3b85fbfcd4bb54ac3e42f4e9c', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '63.33.238.85:20002', + pubKeyOperator: '902c12f4e752167465dfaa1cb88b45878c3602a0543bfb36be2ace7bd9725f7c4fd76446dabe9948f251adb808ac3ad4', + votingAddress: 'yQWf5QAYxarP44aKP2cBFNJVCc2Yuf4p5b', + isValid: true, + }, + { + proRegTxHash: 'fb770587665b18dfb3fc196bf6f9d628a298f8b40a588e2ea9d56fc3760567dc', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '18.202.52.170:20040', + pubKeyOperator: '80f5458a1d7ae69cb8dbc6b7d69ac112e008bf34a985eb86cd973824023d8705cd02e2392f3c7682c3a9fac2a6c4ef48', + votingAddress: 'ySUfzAHxS8NAhCQFwd745zzWC31UTYUBmF', + isValid: false, + }, + { + proRegTxHash: '9d3664f872028a8ac0fe867129f4027e96ee9747a4690a29cae3d6e84311b47d', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '173.61.30.231:19018', + pubKeyOperator: '862599b105fae8d252fef9707d02988e9f302ce6ffa7d1566908979816af6752e1470dab2f6bbed45ca65e64e4b74a3f', + votingAddress: 'yhrgd6foTdjHsmWeg9bQKghS9xUYXchz5E', + isValid: true, + }, + { + proRegTxHash: '9f4f9f83ecbcd5739d7f1479ee14b508f2414d044a717acba0960566c4e6091d', + confirmedHash: '0000002c1c2e8842db3fdc3594dce9febe6d862cbd832b1995d756a466a9f483', + service: '45.32.211.155:19999', + pubKeyOperator: '08e37b3fcba972fe0c2c0ea15f8285c8bfb262ad4d8a6741a530154f1abc4edd367a22abd0cb1934647f033913cca58a', + votingAddress: 'ybAZoZ6iybhEwoCfb6utGfU753R1wcQSZT', + isValid: true, + }, + { + proRegTxHash: 'b4ff65fd0afe5a4c910414df44d473f0f4c750025fd019148617bc0eaf71e93d', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '52.50.208.53:20017', + pubKeyOperator: '8cbd9ea64deb7cdff20eb3895473799d89c6cd11b5929934bcf5e28b04961be6398f4030f66ba12c13d1b749dcbfc4d3', + votingAddress: 'yVDekRuyYRWVPPyr91JPRaFiWKb7uqkUry', + isValid: true, + }, + { + proRegTxHash: '714ecf3166a38d7db7c58bfa90adfadaba883f295a2800c46604e21b1ce31a7d', + confirmedHash: '00000032650ace4f57a6e4ad987de13cefd92e208f45653f9c491961c05c7e67', + service: '54.91.130.170:19999', + pubKeyOperator: '08a37fd91db686b551ab91b86ab073c2c44e1d0bab4f99c1edfbc2b12abafd1e9a96715afd16173ab749db890276929f', + votingAddress: 'yiMaBPtV2pfGmoajHsuWtRpwQa65CMj9c3', + isValid: true, + }, + { + proRegTxHash: '55c64c0e8fbff836d24f40c082c43274f95d302f2a53520749e5abd326c5fb1d', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '63.33.238.85:20010', + pubKeyOperator: '9625089978a7d330992669359de9168b481fa76ca0725ce4f55bd7561618109c9a8035f608723f68458ebeff4dba5ead', + votingAddress: 'ygy46nd1Cct5Tb2tQhk5qLeH9DYkgkAgB3', + isValid: true, + }, + { + proRegTxHash: 'b6f3631a9489cf74328e1d5cf7e0a1ee1115304cbe1254073f93b694514927bd', + confirmedHash: '0000001ffaa9dcfa755922d7715dc62dab33950dca2d62781b9d8f27bfa141dd', + service: '51.38.80.34:19999', + pubKeyOperator: '820710bf028cf0f81d0e8115f0654dffcdd83e598ddfdfd91bac653dbc534a3177844fe8c87e991727d581bcf775432e', + votingAddress: 'yWqzuhU5sZnVstKYbbzxhBQLJjxshtAqew', + isValid: true, + }, + { + proRegTxHash: 'ae1f65058e012b3bc10396ae868ff6ce2579aac4241f1cc991a8ff9715e7211f', + confirmedHash: '0000004c938fc648306ae0186125346093c9207b25e69a9b4a057f35afb4d66c', + service: '109.235.69.20:19999', + pubKeyOperator: '84175e1361b4f718341f496e3ad40644a99c292f184f7bf31ab2a711c6d3b63ad14fe4df227974fee5a8d4ba45fcf521', + votingAddress: 'yNabLbG96ES6edet9KAu2ThgT9VRQZCUoh', + isValid: false, + }, + { + proRegTxHash: '32e5ad5cf9a06eb13e0f65cb7ecde1a93ef24995d07355fac2ff05ebd5b9ddbf', + confirmedHash: '0000001960431ec5a566e69f28ae0f6fa3199bd99ec527cccd02f7541d77300c', + service: '95.183.51.146:39999', + pubKeyOperator: '1326ddac1044e0219dba7dccf6b43d1deed3e897717ca06757243b02516cfa67e24026f7a317cf575b40c10e7f6bf7f0', + votingAddress: 'yYhmQPak2w5L8KSwVw9R5wpqzPbAJ1fK7v', + isValid: true, + }, + ], + merkleRootMNList: 'cf31dc397df3c4048d44a49e176afa64b3b3a053597003eb518eeb7252ff6536', + }, + { + baseBlockHash: '000000000b0339e07bce8b3186a6a57a3c45d10e16c4bce18ef81b667bc822b2', + blockHash: '0000000005b3f97e0af8c72f9a96eca720237e374ca860938ba0d7a68471c4d6', + cbTxMerkleTree: '0200000002c9802d02435cfe09e4253bc1ba4875e9a2f920d5d6adf005d5b9306e5322e6f476d885273422c2fe18e8c420d09484f89eaeee7bb7f4e1ff54bddeb94e099a910103', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff4b02204e047867335c08fabe6d6d8b2b76b7000000000470393f63424273736170747365743a7265737574736574010000000000000010000015770000000d2f6e6f64655374726174756d2f000000000336c8a119010000001976a914cb594917ad4e5849688ec63f29a0f7f3badb5da688ac6c62c216010000001976a914a3c5284d3cd896815ac815f2dd76a3a71cb3d8e688acba65df02000000001976a9146d649e1c05e89d30809ef39cc8ee1002c0c8c84b88ac00000000260100204e0000b301c3d88e4072305bec5d09e2ed6b836b23af640bcdefd7b8ae7e2ca182dc17', + deletedMNs: [ + '5bd32dd06449e0e3b8dbb80966d7fd85dc17fdaa20564bd48e8a326083e7bd0d', + ], + newQuorums: [], + deletedQuorums: [], + mnList: [ + { + proRegTxHash: 'f487b2b02c554816bb44cfc35fed083951ff94a3ecb5ccacb578986615cbfdd8', + confirmedHash: '00000000041f86bfb8c2e5c4f166686f73f4930e4b6f1b9a8feb8480890ba724', + service: '173.61.30.231:19007', + pubKeyOperator: '07f818e5c2330ac4e7f0ef820f337addf8ab28b07c9d451304d807feda1d764c7074bccbbd941284b0d0276a96cf5e7f', + votingAddress: 'ySXL8BpEMVjFR6sNEbR1LGPuHfCbaWYmBJ', + isValid: true, + }, + { + proRegTxHash: 'fef106ff6420f9c6638c9676988a8fc655750caafb506c98cb5ff3d4fea99a41', + confirmedHash: '0000000005d5635228f113b50fb5ad66995a7476ed20374e6e159f1f9e62347b', + service: '45.48.177.222:19999', + pubKeyOperator: '842476e8d82327adfb9b617a7ac3f62868946c0c4b6b0e365747cfb8825b8b79ba0eb1fa62e8583ae7102f59bf70c7c7', + votingAddress: 'yf7QHemCfbmKEncwZxroTj8JtShXsC28V6', + isValid: false, + }, + { + proRegTxHash: '682b3e58e283081c51f2e8e7a7de5c7312a2e8074affaf389fafcc39c4805404', + confirmedHash: '00000018c824355520c6a850076c041b533d05cbe481f8187e541d7e2f856def', + service: '64.193.62.206:19999', + pubKeyOperator: '05f2269374676476f00068b7cb168d124b7b780a92e8564e18edf45d77497abd9debf186ee98001a0c9a6dfccbab7a0a', + votingAddress: 'yid7uAsVJzvSLrEekHuGNuY3KWCqJopyJ8', + isValid: false, + }, + { + proRegTxHash: 'dcf13b43b5bbb0b4600e513af574da87f2e923c91e1afc017b1ae954e82f84c4', + confirmedHash: '0000001ffaa9dcfa755922d7715dc62dab33950dca2d62781b9d8f27bfa141dd', + service: '159.65.233.52:19999', + pubKeyOperator: '15e97fb8029420a71f7125cbf963696c3fbf9636f6d2fa8997d35d37416e2c837182f2e7b7623498736253e5469eb894', + votingAddress: 'ycd2G5zUax8hSkCJ136SGw1WCy6V3jrgEB', + isValid: false, + }, + { + proRegTxHash: '05f876be752ae6461ff137383280810a4f2f1a6c28c70316b4723d1db0ea3367', + confirmedHash: '000000000d8fafe0cb68fd608a02c0cbf25518aa5ebd3956183d457a9f398ce9', + service: '173.61.30.231:19024', + pubKeyOperator: '89d9a3588ad0e5c40d8b1349e0e14aa74caf107f396208be015aa7d489db50f7e029b721350dbf480271580d293dbedc', + votingAddress: 'yeMCt9pipuFNULnn6vVXURK3iSiF6LsDVH', + isValid: true, + }, + { + proRegTxHash: 'a4d877cee62f82868034fb678436d87afbb13330d2b66a24ae1d357f0de55c68', + confirmedHash: '00000000069c41d7444a7da5d67f222224e9e37590c474f102ee1ae0da998f39', + service: '83.80.229.213:19999', + pubKeyOperator: '16415af54406658be9ea44d82b6b502bb90d93e32997484533a8a71a4ed98d12cea3709d84a5835b6ad8ed48d3101633', + votingAddress: 'yfKNLE5v4QTnMvj7y3JVoWEfQanD4qHWGk', + isValid: true, + }, + { + proRegTxHash: 'e6218b98482d5533f37cb384b9403ad482163bc76c783ac290d78a5fb54573e8', + confirmedHash: '000000001a4cfc8b64b92e78b3c3145ba9003dffdc0beadd36d1d4b45184800d', + service: '173.61.30.231:19020', + pubKeyOperator: '18f5940d64ab6139b8294268b11d21389ce73ece968df32f681905bb37b154c8a6a678aae4fbeac06e7182319650436d', + votingAddress: 'yMXa6jvp56ExjgKgs2tQm8Dm4LcppzPtvG', + isValid: true, + }, + { + proRegTxHash: 'ee870538e2c265e7c53af7f94934fdef16cc8016c2f36a1f266541cba96a1049', + confirmedHash: '000000000ca732b4a97c3ef8a8d567c96d0385e2f80b9f2268e8a0bd271b84f9', + service: '43.229.77.46:19999', + pubKeyOperator: '8de69524dd60930aacf252a19e34e5928dbb20144d1f336a45dd4248acdcbcafa929619913980156defa1113d1481139', + votingAddress: 'yUVxd8VafRftExWmz12oHUxrfB1kmZuaMe', + isValid: false, + }, + { + proRegTxHash: '41d50ce2fd17b88cad6d84b757772b41f0d52600b941baf171ff18caf67f2989', + confirmedHash: '00000000128fdb86bafb841c97fb21b7db7e61e53d14f168b1b88de65273b608', + service: '217.61.221.9:20002', + pubKeyOperator: '05e4d38cc8f31076eef71fe3bdbcf5bc8e956188603e53a12943476fba7a40d6909b75510cff435314a39e3605ed1082', + votingAddress: 'yMxcAcHc1yZVo5sjWDcZEmMV311TzFEG9E', + isValid: false, + }, + { + proRegTxHash: 'e157a38e02aad5da99fa7792d07eb8b773ccfef9084d892580b5a920741d72ea', + confirmedHash: '0000000bf6f060f5ad57947c355f8ffc9df1563ac2698a5ddea6c2c605cef576', + service: '173.61.30.231:19014', + pubKeyOperator: '983ca9ab507b3eb4e7b0d31ccef3f4553493ee5334116a3f79689f9b808a201ead332a26f7052fd17123cf142f96d85f', + votingAddress: 'yeLNezEUhMaBEp4Y3qiGwhphJbz244UQsT', + isValid: true, + }, + { + proRegTxHash: '1659e06c825212c9b11325760a18f6ea06194ec4efd603f03d8704f23d818a6f', + confirmedHash: '000000000ca93e850827b361743c25c8508e6e42efaaa331cc1b54326d9fd179', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yUTy9Fb2ULXdgyqYtMMbuUWpFLaDgUqT3f', + isValid: false, + }, + { + proRegTxHash: '1a3b5e4d4e06cc89b26ef6e4b962831ede8e3e6c7ceb0ea5de58f6c392aff3cf', + confirmedHash: '000000000be653cd1fbc213239cfec83ca68da657f24cc05305d0be75d34e392', + service: '173.61.30.231:19022', + pubKeyOperator: '18da04cb7eea7cb37741ac7c104ba757a2c23fce5903559685747e7a709451ed6bf71f5cc7a3f97a2c25d70c6e7f7cfe', + votingAddress: 'yViWUmv32CiYmf4CG8oY5PymnvhL4tJ9H3', + isValid: true, + }, + { + proRegTxHash: '5d40e68f65e7263d91e114b644ff7f8c9c376db63550d5ef9bc4228870c4f053', + confirmedHash: '00000000077bd01073605cc5956a9ee883f8c47c8c7a337ecbd14ec5aa91e294', + service: '173.61.30.231:19002', + pubKeyOperator: '98b26368c5f73198500cae0d7e1108833489e7f8bc5d7fa507014fdd0ad2b6a082012883a8acdbcf688423419bff7e24', + votingAddress: 'yecoEzHhCDtFmqFx6UTbAk8kTWZDGxmXBb', + isValid: true, + }, + { + proRegTxHash: 'e6986bb24b729ab531ba778bc7292a0a8abbf66b5996f45ca6a1dcbd5e46e0b3', + confirmedHash: '0000000004995804891a4c2c54dad4f684135f6b626979777839de454c8610bb', + service: '173.61.30.231:19021', + pubKeyOperator: '0e9df826d2152e0ceffbab634e6f7daf62ec82f6764ed792ecb94272917d321590f6c1224590b77c04bd836217623ca1', + votingAddress: 'yeBTHcCumHPMSMKCtYE6qLNf8Ca3ssVSui', + isValid: true, + }, + { + proRegTxHash: '27a6ff2f188c6190d44b657f54bd831f57228f918cbb7fd6026f5cf5c443d496', + confirmedHash: '00000000077bd01073605cc5956a9ee883f8c47c8c7a337ecbd14ec5aa91e294', + service: '173.61.30.231:19003', + pubKeyOperator: '110bdff9037c3e3926082ff9e9e9de9cd0a0dd416ac6d60a61781f1b3832a4bd068e92343be400fc31db6eb4404d0701', + votingAddress: 'yjW7bQrKBsMV8Wh19LgT8Z1uLkWY8P2EBd', + isValid: true, + }, + { + proRegTxHash: '08e238ac320bc472aa664b99280663cb39073f189b0ce290a2cb11bbd80c41d6', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '52.50.208.53:20029', + pubKeyOperator: '10c0a1cc322597069f80ea22c04c4e5a442fff97ae4ec952a91eff9d8d9787760f20a227b1ada2025a6e9d74146e4467', + votingAddress: 'yTr8Ti4Gh16DHPfGNztzZ9Vhxu7tPnk7oM', + isValid: true, + }, + { + proRegTxHash: '3a8738860323ed3868f0e4a26d852bbea4feb3850674e5ca1c27fa15d0b707b6', + confirmedHash: '00000024f9d5cd5a0b26d87d0eb18d41801e08d2676a2c07ce833c35fa9dc084', + service: '95.183.51.146:60000', + pubKeyOperator: '8851d988149766aaaafca285ded50de031ce42036033e3239f4f903abda26740ba235e22d26a693136a5ac27555f3de8', + votingAddress: 'yjUGNWwa87BKPy7nztcpcZkxRaT4xt6Xzo', + isValid: true, + }, + { + proRegTxHash: '76476a2678d5c1e9ea4951cdd00babd50f6c53f91427ba8dc8fe49f5dc1f5c97', + confirmedHash: '000000001087611a48b9237c0db4a849c5afcdc3aa7009a1cbc6058a1b4520bc', + service: '52.220.61.88:19999', + pubKeyOperator: '10142d44041c90621d111283fe46fd8b2450d4b9bebad194290fce09ba080679c748b1ba70e3959623f127af0d2bc9c4', + votingAddress: 'yWLN8dwGS8SxndBEW7Hwvn2yAD7hULTojP', + isValid: true, + }, + { + proRegTxHash: '7504ff244e65de04c91640380c0c996f1f5b09073a8eb387ceba1a3c1ba18ff7', + confirmedHash: '00000000061771e19a1adc5b3f48507cc92b65257c0f6fb6e918c0336b261456', + service: '173.61.30.231:19004', + pubKeyOperator: '1249d9527e8ccf8d237e828500cf7f8946963d45264460586ffd8fb1b76e16a541c54695089fbcf4b1b8e1ec79e93a70', + votingAddress: 'yYaSqRGYcWdkv2UCBAE4W4wV4rTFTmwz7p', + isValid: true, + }, + { + proRegTxHash: '67491f0cb0874d179d8ece6f3ff25f721b2eb016ab5768bfabdc5e6ca614aaf9', + confirmedHash: '000000001f25c0f6c1535ab47211b21185409c6af85df7a82e798e1ca00ed742', + service: '91.190.125.133:19999', + pubKeyOperator: '91e633b72726091f58e3bd1ede3a21de66abb2456c2f669be8bdcf76f3ab76aa2d75f7d03cf2f7d5761ab15e62e00613', + votingAddress: 'ygbXcRv8sqYJ3DcEkyRwTmZuFaKwmHTTEo', + isValid: true, + }, + { + proRegTxHash: 'be32ec53dbbfb64e5ba29e25e3716f6f4024291914ce4c858cd69f0b4e371dda', + confirmedHash: '0000000015717296254a7c6139a50c34ad481dc8fdf7b0ea4c8320dc3fff2759', + service: '173.61.30.231:19025', + pubKeyOperator: '00e38851ac0f784f3ec5cf15e00276bb541918596a657a4f67568a5c33361a655e5baefe0fb22e0affe37e95987670b9', + votingAddress: 'ySBU7oXuuTSJqtmUArMRFsKefJPtEDkESG', + isValid: true, + }, + { + proRegTxHash: 'b470614a1a43f69910fb26429d1e4f2465f28bad7a5e409987ee748ac35e3f1a', + confirmedHash: '0000000ae0cd2a094068ba7ffb6ef20242b08b748ffbb517ebd56fc77680ef78', + service: '52.50.208.53:20049', + pubKeyOperator: '93fcd68988f82faf350938dd57cc7449a669eb9b0d5095c24b0c6e61a04dc7408acc1909d79809da0a909f7f15d24411', + votingAddress: 'yPTb1MFjyb5tryAoKdCjZgFd6rCapfYMGg', + isValid: true, + }, + { + proRegTxHash: '400c7f8990e6f8a3993b7d5900ea0b58e18bf86ba9b147bdefcd0df4cda1887b', + confirmedHash: '000000000613ea19d2c5a0d6bbf861eebcba6c56b2e32c25306c30589906e8f3', + service: '89.17.41.106:19999', + pubKeyOperator: '848bfbe1bf50debe1322e14c9115adb3b96e5b8a3ae96beb7e2161281d9e56c30e43478d6f39835e3533a1c54377258b', + votingAddress: 'yWjnrJQzvgfVPPQJkRu4NUPue2CiKe8kSD', + isValid: true, + }, + { + proRegTxHash: '3667fe83d6c334eae930252ca9bdd22d3eed1aee1c3b5b40d7244b98bea2c77b', + confirmedHash: '000000000882cafe55ba050f6d84cb7095ceea8056d5dc0c004b2997cc02d605', + service: '173.61.30.231:19006', + pubKeyOperator: '8b6159beec3c3c1ba223fa988b5806a02edebcd16869a2e053b41b7db3e28f12136636974f5333317fc67a22d2b9b3db', + votingAddress: 'yYmWHHP4i812Lyj8PWT6FsuL6yikkH7hYC', + isValid: true, + }, + { + proRegTxHash: '95e048c6e09dd0367006df0dfe9737d69800526869590dc8acbc96fb94332c9c', + confirmedHash: '000000000882cafe55ba050f6d84cb7095ceea8056d5dc0c004b2997cc02d605', + service: '173.61.30.231:19005', + pubKeyOperator: '182ece65d7aef6b0d0a92c0e3451609607717f9cdb6d11cc6e31a2d625c7f40a8cace522b036481daf4e4425c41880a5', + votingAddress: 'ySfnordUG2758rcRfMz1328rmvzSUStEbe', + isValid: true, + }, + { + proRegTxHash: 'fb770587665b18dfb3fc196bf6f9d628a298f8b40a588e2ea9d56fc3760567dc', + confirmedHash: '0000004c7bba7e46b583731f0930c9a3b0033e268f87bccf9a5e44793d634a5a', + service: '18.202.52.170:20040', + pubKeyOperator: '80f5458a1d7ae69cb8dbc6b7d69ac112e008bf34a985eb86cd973824023d8705cd02e2392f3c7682c3a9fac2a6c4ef48', + votingAddress: 'ySUfzAHxS8NAhCQFwd745zzWC31UTYUBmF', + isValid: true, + }, + { + proRegTxHash: '2523dc6e034911b9004862e87b4d23a32ed6198aec177915df7893f51cd645bd', + confirmedHash: '000000000af78a45a0f04dec1b921497c682440927d76f9129fd29412f4d7815', + service: '140.82.59.51:10006', + pubKeyOperator: '8ce516fa5d72f29e08d842812ef5cf72de3672c23d6dc88f4b13f0a50c2b8050d0cee348b6d542ceb569a45504e73499', + votingAddress: 'yXiktt3kkmpfSvPGkKvAy4Qpjm8aJKea6D', + isValid: true, + }, + { + proRegTxHash: '50a5733b8430461139765ed886b998258bcca5a9df528e069d313e289df6a05e', + confirmedHash: '00000000077bd01073605cc5956a9ee883f8c47c8c7a337ecbd14ec5aa91e294', + service: '173.61.30.231:19001', + pubKeyOperator: '0418bfc9d8225bae5a889f1f74d47d539e9e7a8d441cb2b743b176e9d3a7ea4915fb40844cdb53a6faebdb4e826f9f78', + votingAddress: 'yPJh4D1sLdbXZG6Qu1X66FdNsu2qoBQ7Mz', + isValid: true, + }, + { + proRegTxHash: '6f0bdd7034ce8d3a6976a15e4b4442c274b5c1739fb63fc0a50f01425580e17e', + confirmedHash: '000000000be653cd1fbc213239cfec83ca68da657f24cc05305d0be75d34e392', + service: '173.61.30.231:19023', + pubKeyOperator: '8da7ee1a40750868badef2c17d5385480cae7543f8d4d6e5f3c85b37fdd00a6b4f47726b96e7e7c7a3ea68b5d5cb2196', + votingAddress: 'ybGQ7a6e7dkJY2jxdbDwdBtyjKZJ8VB7YC', + isValid: true, + }, + { + proRegTxHash: '14d924611e20307338c0937ad746226c0b50b01d47824c9ef08e141cc4635c9f', + confirmedHash: '000000000a1f3feaedf99baf968948ca14a153c010f7c9ff81540e74d1cbd214', + service: '178.62.203.249:29999', + pubKeyOperator: '12e0312b6ee98f2ef8b3ceceacb9af3ca00346d2f6bf5b710ee06f51a0bce5b7caf5f76bd867b95c10b4279dff9aa74e', + votingAddress: 'yMMe9BPZ711sSqW5CdK8VRTW1RWqcmS7K6', + isValid: false, + }, + ], + merkleRootMNList: '17dc82a12c7eaeb8d7efcd0b64af236b836bede2095dec5b3072408ed8c301b3', + }]; +} + +module.exports = getMNListDiffsFixture; diff --git a/packages/js-dapi-client/lib/test/fixtures/getMetadataFixture.js b/packages/js-dapi-client/lib/test/fixtures/getMetadataFixture.js new file mode 100644 index 00000000000..5e42ff1cbdc --- /dev/null +++ b/packages/js-dapi-client/lib/test/fixtures/getMetadataFixture.js @@ -0,0 +1,11 @@ +/** + * @returns {{coreChainLockedHeight: number, height: number}} + */ +function getMetadataFixture() { + return { + height: 10, + coreChainLockedHeight: 42, + }; +} + +module.exports = getMetadataFixture; diff --git a/packages/js-dapi-client/lib/test/fixtures/getProofFixture.js b/packages/js-dapi-client/lib/test/fixtures/getProofFixture.js new file mode 100644 index 00000000000..ee2f7cdeece --- /dev/null +++ b/packages/js-dapi-client/lib/test/fixtures/getProofFixture.js @@ -0,0 +1,16 @@ +/** + * @returns {{ + * merkleProof: Buffer, + * signature: Buffer, + * signatureLLMQHash: Buffer + * }} + */ +function getProofFixture() { + return { + signatureLLMQHash: Buffer.from('AQEBAQEBAQEBAQEB', 'base64'), + signature: Buffer.from('AgICAgICAgICAgIC', 'base64'), + merkleProof: Buffer.from('0100000001f0faf5f55674905a68eba1be2f946e667c1cb5010101', 'hex'), + }; +} + +module.exports = getProofFixture; diff --git a/packages/js-dapi-client/lib/test/karma/loader.js b/packages/js-dapi-client/lib/test/karma/loader.js new file mode 100644 index 00000000000..4319faa7f26 --- /dev/null +++ b/packages/js-dapi-client/lib/test/karma/loader.js @@ -0,0 +1,7 @@ +// This file is used for compiling tests with webpack into one file for using with karma +require('../bootstrap'); + +// noinspection JSUnresolvedFunction +const testsContext = require.context('../../../test', true, /^(?!.*functional).*\.js$/); + +testsContext.keys().forEach(testsContext); diff --git a/packages/js-dapi-client/lib/test/utils/wait.js b/packages/js-dapi-client/lib/test/utils/wait.js new file mode 100644 index 00000000000..56a945c899c --- /dev/null +++ b/packages/js-dapi-client/lib/test/utils/wait.js @@ -0,0 +1,12 @@ +/** + * Asynchronously wait for a specified number of milliseconds. + * + * @param {number} ms - Number of milliseconds to wait. + * + * @returns {Promise} The promise to await on. + */ +async function wait(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +module.exports = wait; diff --git a/packages/js-dapi-client/lib/transport/GrpcTransport/GrpcTransport.js b/packages/js-dapi-client/lib/transport/GrpcTransport/GrpcTransport.js new file mode 100644 index 00000000000..9a20e565ab1 --- /dev/null +++ b/packages/js-dapi-client/lib/transport/GrpcTransport/GrpcTransport.js @@ -0,0 +1,150 @@ +const MaxRetriesReachedError = require('../errors/response/MaxRetriesReachedError'); +const NoAvailableAddressesForRetryError = require('../errors/response/NoAvailableAddressesForRetryError'); +const NoAvailableAddressesError = require('../errors/NoAvailableAddressesError'); +const TimeoutError = require('./errors/TimeoutError'); +const RetriableResponseError = require('../errors/response/RetriableResponseError'); + +class GrpcTransport { + /** + * @param {createDAPIAddressProviderFromOptions} createDAPIAddressProviderFromOptions + * @param { + * ListDAPIAddressProvider| + * SimplifiedMasternodeListDAPIAddressProvider| + * DAPIAddressProvider + * } dapiAddressProvider + * @param {createGrpcTransportError} createGrpcTransportError + * @param {DAPIClientOptions} globalOptions + */ + constructor( + createDAPIAddressProviderFromOptions, + dapiAddressProvider, + createGrpcTransportError, + globalOptions, + ) { + this.createDAPIAddressProviderFromOptions = createDAPIAddressProviderFromOptions; + this.dapiAddressProvider = dapiAddressProvider; + this.createGrpcTransportError = createGrpcTransportError; + this.globalOptions = globalOptions; + + this.lastUsedAddress = null; + } + + /** + * Make request to DAPI node + * + * @param {Function} ClientClass + * @param {string} method + * @param {object} requestMessage + * @param {DAPIClientOptions} [options] + * + * @returns {Promise} + */ + async request(ClientClass, method, requestMessage, options = { }) { + const dapiAddressProvider = this.createDAPIAddressProviderFromOptions(options) + || this.dapiAddressProvider; + + const address = await dapiAddressProvider.getLiveAddress(); + + if (!address) { + throw new NoAvailableAddressesError(); + } + + // eslint-disable-next-line no-param-reassign + options = { + retries: this.globalOptions.retries, + timeout: this.globalOptions.timeout, + ...options, + }; + + const url = this.makeGrpcUrlFromAddress(address); + const client = new ClientClass(url); + + const requestOptions = {}; + if (options.timeout !== undefined) { + requestOptions.deadline = new Date(); + requestOptions.deadline.setMilliseconds( + requestOptions.deadline.getMilliseconds() + options.timeout, + ); + } + + try { + const result = await client[method](requestMessage, {}, requestOptions); + + this.lastUsedAddress = address; + + address.markAsLive(); + + return result; + } catch (error) { + this.lastUsedAddress = address; + + // for unknown errors + if (error.code === undefined) { + throw error; + } + + const responseError = this.createGrpcTransportError(error, address); + + if (!(responseError instanceof RetriableResponseError)) { + throw responseError; + } + + if (options.throwDeadlineExceeded && responseError instanceof TimeoutError) { + throw responseError; + } + + if (options.retries === 0) { + throw new MaxRetriesReachedError(responseError); + } + + const hasAddresses = await dapiAddressProvider.hasLiveAddresses(); + if (!hasAddresses) { + throw new NoAvailableAddressesForRetryError(responseError); + } + + return this.request( + ClientClass, + method, + requestMessage, + { + ...options, + retries: options.retries - 1, + }, + ); + } + } + + /** + * Get last used address + * + * @returns {DAPIAddress|null} + */ + getLastUsedAddress() { + return this.lastUsedAddress; + } + + /** + * + * Get gRPC url string + * + * @private + * @param {DAPIAddress} address + * @returns {string} + */ + makeGrpcUrlFromAddress(address) { + let port = address.getHttpPort(); + + // For NodeJS Client + if (typeof process !== 'undefined' + && process.versions != null + && process.versions.node != null) { + port = address.getGrpcPort(); + } + + const protocol = address.getHttpPort() === 443 ? 'https' : 'http'; + + return `${protocol}://${address.getHost()}:${port}`; + } +} + +module.exports = GrpcTransport; diff --git a/packages/js-dapi-client/lib/transport/GrpcTransport/createGrpcTransportError.js b/packages/js-dapi-client/lib/transport/GrpcTransport/createGrpcTransportError.js new file mode 100644 index 00000000000..e4290b0fa84 --- /dev/null +++ b/packages/js-dapi-client/lib/transport/GrpcTransport/createGrpcTransportError.js @@ -0,0 +1,147 @@ +const cbor = require('cbor'); + +const createConsensusError = require('@dashevo/dpp/lib/errors/consensus/createConsensusError'); +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const { Metadata } = require('@grpc/grpc-js/build/src/metadata'); + +const NotFoundError = require('./errors/NotFoundError'); +const TimeoutError = require('./errors/TimeoutError'); +const ResponseError = require('../errors/response/ResponseError'); +const ServerError = require('../errors/response/ServerError'); +const InvalidRequestError = require('../errors/response/InvalidRequestError'); +const InvalidRequestDPPError = require('../errors/response/InvalidRequestDPPError'); +const InternalServerError = require('./errors/InternalServerError'); + +const INVALID_REQUEST_CODES = [ + GrpcErrorCodes.INVALID_ARGUMENT, + GrpcErrorCodes.FAILED_PRECONDITION, + GrpcErrorCodes.ALREADY_EXISTS, + GrpcErrorCodes.UNAUTHENTICATED, + GrpcErrorCodes.OUT_OF_RANGE, + GrpcErrorCodes.PERMISSION_DENIED, +]; + +const SERVER_ERROR_CODES = [ + GrpcErrorCodes.RESOURCE_EXHAUSTED, + GrpcErrorCodes.UNAVAILABLE, + GrpcErrorCodes.CANCELLED, + GrpcErrorCodes.UNKNOWN, + GrpcErrorCodes.DATA_LOSS, + GrpcErrorCodes.UNIMPLEMENTED, + GrpcErrorCodes.ABORTED, + GrpcErrorCodes.INTERNAL, +]; + +const errorClasses = { + [GrpcErrorCodes.NOT_FOUND]: NotFoundError, + [GrpcErrorCodes.DEADLINE_EXCEEDED]: TimeoutError, +}; + +/** + * @typedef {createGrpcTransportError} + * @param {Error} grpcError + * @param {DAPIAddress} dapiAddress + * @returns {ResponseError} + */ +function createGrpcTransportError(grpcError, dapiAddress) { + // Extract error code and data + let data = {}; + let { code } = grpcError; + const message = grpcError.details || grpcError.message; + + if (grpcError.metadata) { + let encodedData; + + if (grpcError.metadata instanceof Metadata) { + const cboredMetaData = grpcError.metadata.get('drive-error-data-bin'); + if (cboredMetaData && cboredMetaData.length > 0) { + [encodedData] = cboredMetaData; + } + + // since gRPC doesn't allow to use custom error codes + // DAPI pass them as a part of metadata + const metaCode = grpcError.metadata.get('code'); + if (metaCode && metaCode.length > 0) { + [code] = metaCode; + } + } else { + if (grpcError.metadata['drive-error-data-bin']) { + encodedData = Buffer.from(grpcError.metadata['drive-error-data-bin'], 'base64'); + } + + const metaCode = grpcError.metadata.code; + if (metaCode !== undefined) { + code = Number(metaCode); + } + } + + if (encodedData) { + data = cbor.decode(encodedData); + } + } + + // Specialized classes + const ErrorClass = errorClasses[code.toString()]; + + if (ErrorClass) { + return new ErrorClass( + message, + data, + dapiAddress, + ); + } + + // Invalid request + if (INVALID_REQUEST_CODES.includes(code)) { + return new InvalidRequestError( + code, + message, + data, + dapiAddress, + ); + } + + if (code === GrpcErrorCodes.INTERNAL) { + if (grpcError.metadata) { + const metaStack = grpcError.metadata.get('stack-bin'); + if (metaStack && metaStack.length > 0) { + data.stack = cbor.decode(metaStack[0]); + } + } + + return new InternalServerError( + code, + message, + data, + dapiAddress, + ); + } + + // Server error + if (SERVER_ERROR_CODES.includes(code)) { + return new ServerError( + code, + message, + data, + dapiAddress, + ); + } + + // DPP consensus errors + if (code >= 1000 && code < 5000) { + const consensusError = createConsensusError(code, data.arguments || []); + + delete data.arguments; + + return new InvalidRequestDPPError(consensusError, data, dapiAddress); + } + + return new ResponseError( + code, + message, + data, + dapiAddress, + ); +} + +module.exports = createGrpcTransportError; diff --git a/packages/js-dapi-client/lib/transport/GrpcTransport/errors/InternalServerError.js b/packages/js-dapi-client/lib/transport/GrpcTransport/errors/InternalServerError.js new file mode 100644 index 00000000000..720b4ffc8cc --- /dev/null +++ b/packages/js-dapi-client/lib/transport/GrpcTransport/errors/InternalServerError.js @@ -0,0 +1,20 @@ +const ServerError = require('../../errors/response/RetriableResponseError'); + +class InternalServerError extends ServerError { + /** + * @param {number} code + * @param {string} message + * @param {object} data + * @param {DAPIAddress} dapiAddress + */ + constructor(code, message, data, dapiAddress) { + super(code, message, data, dapiAddress); + + // Replace current stack with remote stack from DAPI/Drive + if (data.stack) { + this.stack = `[REMOTE STACK] ${data.stack}`; + } + } +} + +module.exports = InternalServerError; diff --git a/packages/js-dapi-client/lib/transport/GrpcTransport/errors/NotFoundError.js b/packages/js-dapi-client/lib/transport/GrpcTransport/errors/NotFoundError.js new file mode 100644 index 00000000000..12a93f43849 --- /dev/null +++ b/packages/js-dapi-client/lib/transport/GrpcTransport/errors/NotFoundError.js @@ -0,0 +1,17 @@ +const grpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); + +const ResponseError = require('../../errors/response/ResponseError'); + +class NotFoundError extends ResponseError { + /** + * + * @param {string} message + * @param {object} data + * @param {DAPIAddress} dapiAddress + */ + constructor(message, data, dapiAddress) { + super(grpcErrorCodes.NOT_FOUND, message, data, dapiAddress); + } +} + +module.exports = NotFoundError; diff --git a/packages/js-dapi-client/lib/transport/GrpcTransport/errors/TimeoutError.js b/packages/js-dapi-client/lib/transport/GrpcTransport/errors/TimeoutError.js new file mode 100644 index 00000000000..57d4d572777 --- /dev/null +++ b/packages/js-dapi-client/lib/transport/GrpcTransport/errors/TimeoutError.js @@ -0,0 +1,16 @@ +const grpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); + +const RetriableResponseError = require('../../errors/response/RetriableResponseError'); + +class TimeoutError extends RetriableResponseError { + /** + * @param {string} message + * @param {object} data + * @param {DAPIAddress} dapiAddress + */ + constructor(message, data, dapiAddress) { + super(grpcErrorCodes.DEADLINE_EXCEEDED, message, data, dapiAddress); + } +} + +module.exports = TimeoutError; diff --git a/packages/js-dapi-client/lib/transport/JsonRpcTransport/JsonRpcTransport.js b/packages/js-dapi-client/lib/transport/JsonRpcTransport/JsonRpcTransport.js new file mode 100644 index 00000000000..645840e5ce8 --- /dev/null +++ b/packages/js-dapi-client/lib/transport/JsonRpcTransport/JsonRpcTransport.js @@ -0,0 +1,125 @@ +const MaxRetriesReachedError = require('../errors/response/MaxRetriesReachedError'); +const NoAvailableAddressesForRetryError = require('../errors/response/NoAvailableAddressesForRetryError'); +const NoAvailableAddressesError = require('../errors/NoAvailableAddressesError'); +const RetriableResponseError = require('../errors/response/RetriableResponseError'); + +class JsonRpcTransport { + /** + * @param {createDAPIAddressProviderFromOptions} createDAPIAddressProviderFromOptions + * @param {requestJsonRpc} requestJsonRpc + * @param { + * ListDAPIAddressProvider| + * SimplifiedMasternodeListDAPIAddressProvider| + * DAPIAddressProvider + * } dapiAddressProvider + * @param {createJsonTransportError} createJsonTransportError + * @param {DAPIClientOptions} globalOptions + */ + constructor( + createDAPIAddressProviderFromOptions, + requestJsonRpc, + dapiAddressProvider, + createJsonTransportError, + globalOptions, + ) { + this.createDAPIAddressProviderFromOptions = createDAPIAddressProviderFromOptions; + this.requestJsonRpc = requestJsonRpc; + this.dapiAddressProvider = dapiAddressProvider; + this.globalOptions = globalOptions; + + this.createJsonTransportError = createJsonTransportError; + + this.lastUsedAddress = null; + } + + /** + * Make request to DAPI node + * + * @param {string} method + * @param {object} [params] + * @param {DAPIClientOptions} [options] + * + * @returns {Promise} + */ + async request(method, params = {}, options = {}) { + const dapiAddressProvider = this.createDAPIAddressProviderFromOptions(options) + || this.dapiAddressProvider; + + const address = await dapiAddressProvider.getLiveAddress(); + + if (!address) { + throw new NoAvailableAddressesError(); + } + + // eslint-disable-next-line no-param-reassign + options = { + retries: this.globalOptions.retries, + timeout: this.globalOptions.timeout, + ...options, + }; + + const requestOptions = {}; + if (options.timeout !== undefined) { + requestOptions.timeout = options.timeout; + } + + try { + const result = await this.requestJsonRpc( + address.getHost(), + address.getHttpPort(), + method, + params, + requestOptions, + ); + + this.lastUsedAddress = address; + + address.markAsLive(); + + return result; + } catch (error) { + this.lastUsedAddress = address; + + if (error.code === undefined) { + throw error; + } + + address.markAsBanned(); + + const responseError = this.createJsonTransportError(error, address); + + if (!(responseError instanceof RetriableResponseError)) { + throw responseError; + } + + if (options.retries === 0) { + throw new MaxRetriesReachedError(responseError); + } + + const hasAddresses = await dapiAddressProvider.hasLiveAddresses(); + if (!hasAddresses) { + throw new NoAvailableAddressesForRetryError(responseError); + } + + return this.request( + method, + params, + { + ...options, + retries: options.retries - 1, + }, + ); + } + } + + /** + * Get last used address + * + * @returns {DAPIAddress|null} + */ + getLastUsedAddress() { + return this.lastUsedAddress; + } +} + +module.exports = JsonRpcTransport; diff --git a/packages/js-dapi-client/lib/transport/JsonRpcTransport/createJsonTransportError.js b/packages/js-dapi-client/lib/transport/JsonRpcTransport/createJsonTransportError.js new file mode 100644 index 00000000000..e037f8e042b --- /dev/null +++ b/packages/js-dapi-client/lib/transport/JsonRpcTransport/createJsonTransportError.js @@ -0,0 +1,66 @@ +const WrongHttpCodeError = require('./errors/WrongHttpCodeError'); +const JsonRpcError = require('./errors/JsonRpcError'); +const ServerError = require('../errors/response/ServerError'); +const ResponseError = require('../errors/response/ResponseError'); +const RetriableResponseError = require('../errors/response/RetriableResponseError'); + +/** + * @typedef {createJsonTransportError} + * @param {Error} error + * @param {DAPIAddress} dapiAddress + * @returns {ResponseError} + */ +function createJsonTransportError(error, dapiAddress) { + if (error instanceof WrongHttpCodeError) { + return new ServerError( + error.getCode(), + error.message, + {}, + dapiAddress, + ); + } + + if (error instanceof JsonRpcError) { + /** + * -32700 - Parse error - Invalid JSON was received by the server. + * -32600 - Invalid Request - The JSON sent is not a valid Request object. + * -32601 - Method not found - The method does not exist / is not available. + * -32602 - Invalid params - Invalid method parameter(s). + * -32603 - Internal error - Internal JSON-RPC error. + * -32000 to -32099 - Server error - Reserved for implementation-defined server-errors. + */ + if (error.code !== -32603 && !(error.code >= -32000 && error.code <= -32099)) { + return new ResponseError( + error.getCode(), + error.getMessage(), + error.getData(), + dapiAddress, + ); + } + + return new RetriableResponseError( + error.getCode(), + error.getMessage(), + error.getData(), + dapiAddress, + ); + } + + if (!['ECONNABORTED', 'ECONNREFUSED', 'ETIMEDOUT'].includes(error.code)) { + return new ResponseError( + error.code, + error.message, + {}, + dapiAddress, + ); + } + + return new RetriableResponseError( + error.code, + error.message, + {}, + dapiAddress, + ); +} + +module.exports = createJsonTransportError; diff --git a/packages/js-dapi-client/lib/transport/JsonRpcTransport/errors/JsonRpcError.js b/packages/js-dapi-client/lib/transport/JsonRpcTransport/errors/JsonRpcError.js new file mode 100644 index 00000000000..d8c287e6567 --- /dev/null +++ b/packages/js-dapi-client/lib/transport/JsonRpcTransport/errors/JsonRpcError.js @@ -0,0 +1,59 @@ +const DAPIClientError = require('../../../errors/DAPIClientError'); + +class JsonRpcError extends DAPIClientError { + /** + * @param {object} requestInfo + * @param {string} requestInfo.host + * @param {number} requestInfo.port + * @param {string} requestInfo.method + * @param {object} requestInfo.params + * @param {object} requestInfo.options + * @param {object} jsonRpcError + * @param {number} jsonRpcError.code + * @param {string} jsonRpcError.message + * @param {object} jsonRpcError.data + */ + constructor(requestInfo, jsonRpcError) { + super(jsonRpcError.message); + + this.requestInfo = requestInfo; + this.code = jsonRpcError.code; + this.data = jsonRpcError.data; + } + + /** + * @returns {{host: string, port: number, method: string, params: object, options: object}} + */ + getRequestInfo() { + return this.requestInfo; + } + + /** + * Get error message + * + * @returns {string} + */ + getMessage() { + return this.message; + } + + /** + * Get error data + * + * @returns {object} + */ + getData() { + return this.data; + } + + /** + * Get original error code + * + * @returns {number} + */ + getCode() { + return this.code; + } +} + +module.exports = JsonRpcError; diff --git a/packages/js-dapi-client/lib/transport/JsonRpcTransport/errors/WrongHttpCodeError.js b/packages/js-dapi-client/lib/transport/JsonRpcTransport/errors/WrongHttpCodeError.js new file mode 100644 index 00000000000..041d74869d4 --- /dev/null +++ b/packages/js-dapi-client/lib/transport/JsonRpcTransport/errors/WrongHttpCodeError.js @@ -0,0 +1,38 @@ +const DAPIClientError = require('../../../errors/DAPIClientError'); + +class WrongHttpCodeError extends DAPIClientError { + /** + * + * @param {object} requestInfo + * @param {string} requestInfo.host + * @param {number} requestInfo.port + * @param {string} requestInfo.method + * @param {object} requestInfo.params + * @param {object} requestInfo.options + * @param {number} statusCode + * @param {string} statusMessage + */ + constructor(requestInfo, statusCode, statusMessage) { + super(`DAPI JSON RPC wrong http code: ${statusMessage}`); + + this.requestInfo = requestInfo; + this.code = statusCode; + } + + /** + * @returns {{host: string, port: number, method: string, params: object, options: object}} + */ + getRequestInfo() { + return this.requestInfo; + } + + /** + * + * @returns {number} + */ + getCode() { + return this.code; + } +} + +module.exports = WrongHttpCodeError; diff --git a/packages/js-dapi-client/lib/transport/JsonRpcTransport/requestJsonRpc.js b/packages/js-dapi-client/lib/transport/JsonRpcTransport/requestJsonRpc.js new file mode 100644 index 00000000000..9a26ef77a6d --- /dev/null +++ b/packages/js-dapi-client/lib/transport/JsonRpcTransport/requestJsonRpc.js @@ -0,0 +1,69 @@ +const axios = require('axios'); + +const JsonRpcError = require('./errors/JsonRpcError'); +const WrongHttpCodeError = require('./errors/WrongHttpCodeError'); + +/** + * @typedef {requestJsonRpc} + * @param {string} host + * @param {number} port + * @param {string} method + * @param {object} params + * @param {object} [options] + * @returns {Promise<*>} + */ +async function requestJsonRpc(host, port, method, params, options = {}) { + const protocol = port === 443 ? 'https' : 'http'; + + const url = `${protocol}://${host}${port && port !== 443 ? `:${port}` : ''}`; + + const payload = { + jsonrpc: '2.0', + method, + params, + id: 1, + }; + + const postOptions = {}; + if (options.timeout !== undefined) { + postOptions.timeout = options.timeout; + } + + const requestInfo = { + host, + port, + method, + params, + options, + }; + + let response; + + try { + response = await axios.post( + url, + payload, + { timeout: options.timeout }, + ); + } catch (error) { + if (error.response && error.response.status >= 500) { + throw new WrongHttpCodeError(requestInfo, error.response.status, error.response.statusText); + } + + throw error; + } + + if (response.status !== 200) { + throw new WrongHttpCodeError(requestInfo, response.status, response.statusMessage); + } + + const { data } = response; + + if (data.error) { + throw new JsonRpcError(requestInfo, data.error); + } + + return data.result; +} + +module.exports = requestJsonRpc; diff --git a/packages/js-dapi-client/lib/transport/errors/NoAvailableAddressesError.js b/packages/js-dapi-client/lib/transport/errors/NoAvailableAddressesError.js new file mode 100644 index 00000000000..d6356322f5d --- /dev/null +++ b/packages/js-dapi-client/lib/transport/errors/NoAvailableAddressesError.js @@ -0,0 +1,9 @@ +const DAPIClientError = require('../../errors/DAPIClientError'); + +class NoAvailableAddressesError extends DAPIClientError { + constructor() { + super('No available addresses'); + } +} + +module.exports = NoAvailableAddressesError; diff --git a/packages/js-dapi-client/lib/transport/errors/response/InvalidRequestDPPError.js b/packages/js-dapi-client/lib/transport/errors/response/InvalidRequestDPPError.js new file mode 100644 index 00000000000..e0cc9162c22 --- /dev/null +++ b/packages/js-dapi-client/lib/transport/errors/response/InvalidRequestDPPError.js @@ -0,0 +1,24 @@ +const ResponseError = require('./ResponseError'); + +class InvalidRequestDPPError extends ResponseError { + /** + * + * @param {AbstractConsensusError} consensusError + * @param {object} data + * @param {DAPIAddress} dapiAddress + */ + constructor(consensusError, data, dapiAddress) { + super(consensusError.getCode(), consensusError.message, data, dapiAddress); + + this.consensusError = consensusError; + } + + /** + * @returns {AbstractConsensusError} + */ + getConsensusError() { + return this.consensusError; + } +} + +module.exports = InvalidRequestDPPError; diff --git a/packages/js-dapi-client/lib/transport/errors/response/InvalidRequestError.js b/packages/js-dapi-client/lib/transport/errors/response/InvalidRequestError.js new file mode 100644 index 00000000000..a93c98c3078 --- /dev/null +++ b/packages/js-dapi-client/lib/transport/errors/response/InvalidRequestError.js @@ -0,0 +1,7 @@ +const ResponseError = require('./ResponseError'); + +class InvalidRequestError extends ResponseError { + +} + +module.exports = InvalidRequestError; diff --git a/packages/js-dapi-client/lib/transport/errors/response/MaxRetriesReachedError.js b/packages/js-dapi-client/lib/transport/errors/response/MaxRetriesReachedError.js new file mode 100644 index 00000000000..5b26aee31f6 --- /dev/null +++ b/packages/js-dapi-client/lib/transport/errors/response/MaxRetriesReachedError.js @@ -0,0 +1,26 @@ +const ResponseError = require('./ResponseError'); + +class MaxRetriesReachedError extends ResponseError { + /** + * @param {ResponseError} cause + */ + constructor(cause) { + super( + cause.code, + `Max retries reached: ${cause.message}`, + cause.getData(), + cause.getDAPIAddress(), + ); + + this.cause = cause; + } + + /** + * @returns {ResponseError} + */ + getCause() { + return this.cause; + } +} + +module.exports = MaxRetriesReachedError; diff --git a/packages/js-dapi-client/lib/transport/errors/response/NoAvailableAddressesForRetryError.js b/packages/js-dapi-client/lib/transport/errors/response/NoAvailableAddressesForRetryError.js new file mode 100644 index 00000000000..ac9455faea2 --- /dev/null +++ b/packages/js-dapi-client/lib/transport/errors/response/NoAvailableAddressesForRetryError.js @@ -0,0 +1,26 @@ +const ResponseError = require('./ResponseError'); + +class NoAvailableAddressesForRetryError extends ResponseError { + /** + * @param {ResponseError} cause + */ + constructor(cause) { + super( + cause.code, + `No available addresses for retry: ${cause.message}`, + cause.getData(), + cause.getDAPIAddress(), + ); + + this.cause = cause; + } + + /** + * @returns {ResponseError} + */ + getCause() { + return this.cause; + } +} + +module.exports = NoAvailableAddressesForRetryError; diff --git a/packages/js-dapi-client/lib/transport/errors/response/ResponseError.js b/packages/js-dapi-client/lib/transport/errors/response/ResponseError.js new file mode 100644 index 00000000000..b45256bc06a --- /dev/null +++ b/packages/js-dapi-client/lib/transport/errors/response/ResponseError.js @@ -0,0 +1,40 @@ +const DAPIClientError = require('../../../errors/DAPIClientError'); + +class ResponseError extends DAPIClientError { + /** + * @param {number} code + * @param {string} message + * @param {object} data + * @param {DAPIAddress} dapiAddress + */ + constructor(code, message, data, dapiAddress) { + super(message); + + this.code = code; + this.data = data; + this.dapiAddress = dapiAddress; + } + + /** + * @returns {DAPIAddress} + */ + getDAPIAddress() { + return this.dapiAddress; + } + + /** + * @returns {number} + */ + getCode() { + return this.code; + } + + /** + * @returns {object} + */ + getData() { + return this.data; + } +} + +module.exports = ResponseError; diff --git a/packages/js-dapi-client/lib/transport/errors/response/RetriableResponseError.js b/packages/js-dapi-client/lib/transport/errors/response/RetriableResponseError.js new file mode 100644 index 00000000000..d15537763a6 --- /dev/null +++ b/packages/js-dapi-client/lib/transport/errors/response/RetriableResponseError.js @@ -0,0 +1,7 @@ +const ResponseError = require('./ResponseError'); + +class RetriableResponseError extends ResponseError { + +} + +module.exports = RetriableResponseError; diff --git a/packages/js-dapi-client/lib/transport/errors/response/ServerError.js b/packages/js-dapi-client/lib/transport/errors/response/ServerError.js new file mode 100644 index 00000000000..0af6ec6c03a --- /dev/null +++ b/packages/js-dapi-client/lib/transport/errors/response/ServerError.js @@ -0,0 +1,7 @@ +const RetriableResponseError = require('./RetriableResponseError'); + +class ServerError extends RetriableResponseError { + +} + +module.exports = ServerError; diff --git a/packages/js-dapi-client/package.json b/packages/js-dapi-client/package.json new file mode 100644 index 00000000000..decc53b89d9 --- /dev/null +++ b/packages/js-dapi-client/package.json @@ -0,0 +1,98 @@ +{ + "name": "@dashevo/dapi-client", + "version": "0.23.0-dev.4", + "description": "Client library used to access Dash DAPI endpoints", + "main": "lib/index.js", + "contributors": [ + { + "name": "Ivan Shumkov", + "email": "ivan@shumkov.ru", + "url": "https://github.com/shumkov" + }, + { + "name": "Djavid Gabibiyan", + "email": "djavid@dash.org", + "url": "https://github.com/jawid-h" + }, + { + "name": "Anton Suprunchuk", + "email": "anton.suprunchuk@dash.org", + "url": "https://github.com/antouhou" + }, + { + "name": "Konstantin Shuplenkov", + "email": "konstantin.shuplenkov@dash.org", + "url": "https://github.com/shuplenkov" + } + ], + "dependencies": { + "@dashevo/dapi-grpc": "workspace:~", + "@dashevo/dash-spv": "workspace:~", + "@dashevo/dashcore-lib": "~0.19.39", + "@dashevo/dpp": "workspace:~", + "@dashevo/grpc-common": "workspace:~", + "@grpc/grpc-js": "^1.3.7", + "axios": "^0.21.1", + "bs58": "^4.0.1", + "cbor": "^8.0.0", + "lodash.sample": "^4.2.1", + "node-inspect-extracted": "^1.0.8" + }, + "devDependencies": { + "@babel/core": "^7.15.5", + "assert-browserify": "^2.0.0", + "babel-loader": "^8.2.2", + "buffer": "^6.0.3", + "chai": "^4.3.4", + "chai-as-promised": "^7.1.1", + "comment-parser": "^0.7.6", + "core-js": "^3.17.2", + "crypto-browserify": "^3.12.0", + "dirty-chai": "^2.0.1", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2", + "eslint-plugin-jsdoc": "^27.0.0", + "events": "^3.3.0", + "karma": "^6.3.4", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^3.1.0", + "karma-firefox-launcher": "^2.1.1", + "karma-mocha": "^2.0.1", + "karma-mocha-reporter": "^2.2.5", + "karma-webpack": "^5.0.0", + "mocha": "^9.1.2", + "nyc": "^15.1.0", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "sinon": "^11.1.2", + "sinon-chai": "^3.7.0", + "stream-browserify": "^3.0.0", + "string_decoder": "^1.3.0", + "url": "^0.11.0", + "util": "^0.12.4", + "webpack": "^5.59.1", + "webpack-cli": "^4.9.1" + }, + "files": [ + "docs", + "lib" + ], + "scripts": { + "build:web": "webpack", + "lint": "eslint .", + "test": "yarn run test:coverage && yarn run test:browsers", + "test:unit": "mocha './test/unit/**/*.spec.js'", + "test:integration": "mocha './test/integration/**/*.spec.js'", + "test:node": "NODE_ENV=test mocha", + "test:browsers": "karma start ./karma.conf.js --single-run", + "test:coverage": "NODE_ENV=test nyc --check-coverage --stmts=98 --branch=98 --funcs=98 --lines=95 yarn run mocha 'test/unit/**/*.spec.js' 'test/integration/**/*.spec.js'", + "prepublishOnly": "yarn run build:web" + }, + "ultra": { + "concurrent": [ + "clean" + ] + }, + "license": "MIT" +} diff --git a/packages/js-dapi-client/test/.eslintrc b/packages/js-dapi-client/test/.eslintrc new file mode 100644 index 00000000000..720ced73852 --- /dev/null +++ b/packages/js-dapi-client/test/.eslintrc @@ -0,0 +1,12 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + }, + "globals": { + "expect": true + } +} diff --git a/packages/js-dapi-client/test/integration/BlockHeadersProvider/BlockHeadersProvider.spec.js b/packages/js-dapi-client/test/integration/BlockHeadersProvider/BlockHeadersProvider.spec.js new file mode 100644 index 00000000000..ee0dd2594ec --- /dev/null +++ b/packages/js-dapi-client/test/integration/BlockHeadersProvider/BlockHeadersProvider.spec.js @@ -0,0 +1,196 @@ +const stream = require('stream'); +const { BlockHeader } = require('@dashevo/dashcore-lib'); +const getHeadersFixture = require('../../../lib/test/fixtures/getHeadersFixture'); +const BlockHeadersProvider = require('../../../lib/BlockHeadersProvider/BlockHeadersProvider'); +const BlockHeadersReader = require('../../../lib/BlockHeadersProvider/BlockHeadersReader'); + +const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time)); +const sleepOneTick = () => new Promise((resolve) => { + if (typeof setImmediate === 'undefined') { + setTimeout(resolve, 10); + } else { + setImmediate(resolve); + } +}); + +describe('BlockHeadersProvider - integration', () => { + let coreApiMock; + let blockHeadersProvider; + let blockHeadersStream; + const mockedHeaders = getHeadersFixture(); + + beforeEach(function () { + coreApiMock = { + subscribeToBlockHeadersWithChainLocks: () => {}, + getStatus: this.sinon.stub().resolves({ + chain: { + blocksCount: Math.ceil(mockedHeaders.length / 2), + }, + }), + }; + + this.sinon.stub(coreApiMock, 'subscribeToBlockHeadersWithChainLocks').callsFake(async (args) => { + const { fromBlockHeight, count } = args; + let start = fromBlockHeight - 1; + + const lastItemIndex = count + ? start + count : mockedHeaders.length; + + blockHeadersStream = new stream.Readable({ + async read() { + if (start >= lastItemIndex) { + if (count) { + this.push(null); + } + + // Stop emission here + return; + } + + const headersToReturn = mockedHeaders.slice(start, lastItemIndex); + + // Simulate async emission + await sleepOneTick(); + + this.push({ + getBlockHeaders: () => ({ + getHeadersList: () => headersToReturn.map((header) => header.toBuffer()), + }), + }); + + start = lastItemIndex; + }, + objectMode: true, + }); + + return blockHeadersStream; + }); + + blockHeadersProvider = new BlockHeadersProvider(); + blockHeadersProvider.setCoreMethods(coreApiMock); + }); + + afterEach(() => { + if (blockHeadersStream) { + blockHeadersStream.destroy(); + } + }); + + it('should obtain all block headers and validate them against the SPV chain', async () => { + await blockHeadersProvider.start(); + + let longestChain = blockHeadersProvider.spvChain.getLongestChain(); + + while (longestChain.length !== mockedHeaders.length + 1) { + // eslint-disable-next-line no-await-in-loop + await sleep(100); + longestChain = blockHeadersProvider.spvChain.getLongestChain(); + } + + // slice(1): ignore genesis block + expect(longestChain.slice(1).map((header) => header.hash)) + .to.deep.equal(mockedHeaders.map((header) => header.hash)); + }); + + it('should retry to obtain historical headers in case of SPV failure', async () => { + blockHeadersProvider.start(); + + await sleepOneTick(); + + // Perform MITM attack :) + const badHeader = mockedHeaders[0].toObject(); + delete badHeader.hash; + badHeader.prevHash = Buffer.from('00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc22', 'hex'); + + blockHeadersStream.push({ + getBlockHeaders: () => ({ + getHeadersList: () => [new BlockHeader(badHeader).toBuffer()], + }), + }); + + // Continue waiting for the recovery + let longestChain = blockHeadersProvider.spvChain.getLongestChain(); + + while (longestChain.length !== mockedHeaders.length + 1) { + // eslint-disable-next-line no-await-in-loop + await sleep(100); + longestChain = blockHeadersProvider.spvChain.getLongestChain(); + } + + // slice(1): ignore genesis block + expect(longestChain.slice(1).map((header) => header.hash)) + .to.deep.equal(mockedHeaders.map((header) => header.hash)); + }); + + it('should throw error in case core methods are missing', async () => { + blockHeadersProvider.setCoreMethods(null); + try { + await blockHeadersProvider.start(); + } catch (e) { + expect(e).to.be.instanceOf(Error); + } + }); + + it('should throw error in case BlockHeadersProvider has already been started', async () => { + await blockHeadersProvider.start(); + + try { + await blockHeadersProvider.start(); + } catch (e) { + expect(e).to.be.instanceOf(Error); + } + }); + + it('should emit ERROR event in case BlockHeadersReader emits ERROR', async () => { + await blockHeadersProvider.start(); + + let emittedError; + blockHeadersProvider.on(BlockHeadersProvider.EVENTS.ERROR, (e) => { + emittedError = e; + }); + + const errorToThrow = new Error('test'); + blockHeadersProvider.blockHeadersReader + .emit(BlockHeadersProvider.EVENTS.ERROR, errorToThrow); + + expect(emittedError).to.be.equal(errorToThrow); + }); + + it('should emit ERROR event in case SpvChain fails to addHeaders', async function () { + const errorToThrow = new Error('test'); + blockHeadersProvider.spvChain.addHeaders = this.sinon.stub(); + blockHeadersProvider.spvChain.addHeaders.onFirstCall().throws(errorToThrow); + + await blockHeadersProvider.start(); + + let emittedError; + blockHeadersProvider.on(BlockHeadersProvider.EVENTS.ERROR, (e) => { + emittedError = e; + }); + + blockHeadersProvider.blockHeadersReader + .emit(BlockHeadersReader.EVENTS.BLOCK_HEADERS, []); + + expect(emittedError).to.be.equal(errorToThrow); + }); + + it('should emit ERROR event in case of a failure subscribing to the new block headers', async function () { + await blockHeadersProvider.start(); + + const errorToThrow = new Error('test'); + blockHeadersProvider.blockHeadersReader.subscribeToNew = this.sinon.stub() + .rejects(errorToThrow); + + let emittedError; + blockHeadersProvider.on(BlockHeadersProvider.EVENTS.ERROR, (e) => { + emittedError = e; + }); + + blockHeadersProvider.blockHeadersReader + .emit(BlockHeadersReader.EVENTS.HISTORICAL_DATA_OBTAINED); + + await sleepOneTick(); + + expect(emittedError).to.be.equal(errorToThrow); + }); +}); diff --git a/packages/js-dapi-client/test/integration/BlockHeadersProvider/BlockHeadersReader.spec.js b/packages/js-dapi-client/test/integration/BlockHeadersProvider/BlockHeadersReader.spec.js new file mode 100644 index 00000000000..53c5d46f081 --- /dev/null +++ b/packages/js-dapi-client/test/integration/BlockHeadersProvider/BlockHeadersReader.spec.js @@ -0,0 +1,383 @@ +const { Readable: ReadableStream } = require('stream'); + +const { expect } = require('chai'); +const BlockHeadersReader = require('../../../lib/BlockHeadersProvider/BlockHeadersReader'); +const getHeadersFixture = require('../../../lib/test/fixtures/getHeadersFixture'); + +const sleepOneTick = () => new Promise((resolve) => { + if (typeof setImmediate === 'undefined') { + setTimeout(resolve, 10); + } else { + setImmediate(resolve); + } +}); + +describe('BlockHeadersReader - integration', () => { + let options; + + let coreApiMock; + let blockHeadersReader; + let blockHeadersStream; + let subscribeToBlockHeadersWithChainLocksStub; + const mockedHeaders = getHeadersFixture(); + const headersBatchSize = 5; + + beforeEach(function () { + coreApiMock = { + subscribeToBlockHeadersWithChainLocks: () => {}, + }; + + subscribeToBlockHeadersWithChainLocksStub = this.sinon.stub(coreApiMock, 'subscribeToBlockHeadersWithChainLocks') + .callsFake(async (args) => { + const { fromBlockHeight, count } = args; + let start = fromBlockHeight - 1; + + const lastItemIndex = count + ? start + count : mockedHeaders.length; + + blockHeadersStream = new ReadableStream({ + async read() { + if (start >= lastItemIndex) { + if (count) { + this.push(null); + return; + } + + start = fromBlockHeight - 1; + } + + let end = start + headersBatchSize; + end = end > lastItemIndex ? lastItemIndex : end; + + const headersToReturn = mockedHeaders.slice(start, end); + + // Simulate async emission + await sleepOneTick(); + + this.push({ + getBlockHeaders: () => ({ + getHeadersList: () => headersToReturn, + }), + }); + + start = end; + }, + objectMode: true, + }); + return blockHeadersStream; + }); + + options = { + coreMethods: coreApiMock, + maxRetries: 0, + maxParallelStreams: 6, + targetBatchSize: 10, + }; + + blockHeadersReader = new BlockHeadersReader(options); + }); + + afterEach(() => { + if (blockHeadersStream) { + blockHeadersStream.destroy(); + } + }); + + describe('#subscribeToHistoricalBatch', () => { + it('should emit BLOCK_HEADERS event', async () => { + const maxRetries = 0; + const subscribeToHistoricalBatchWithRetry = blockHeadersReader.subscribeToHistoricalBatch( + maxRetries, + ); + await subscribeToHistoricalBatchWithRetry(1, mockedHeaders.length); + + let headersFromEvent = []; + + blockHeadersReader.on(BlockHeadersReader.EVENTS.BLOCK_HEADERS, (headers) => { + headersFromEvent = [...headersFromEvent, ...headers]; + }); + + while (headersFromEvent.length !== mockedHeaders.length) { + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + } + + expect(headersFromEvent).to.deep.equal(mockedHeaders); + }); + + it('should emit BLOCK_HEADERS event in case of error and retry attempt', async () => { + const maxRetries = 3; + const subscribeToHistoricalBatchWithRetry = blockHeadersReader.subscribeToHistoricalBatch( + maxRetries, + ); + await subscribeToHistoricalBatchWithRetry(1, mockedHeaders.length); + + let headersFromEvent = []; + + blockHeadersReader.on(BlockHeadersReader.EVENTS.BLOCK_HEADERS, (headers) => { + headersFromEvent = [...headersFromEvent, ...headers]; + }); + + let emittedError; + blockHeadersReader.on(BlockHeadersReader.EVENTS.ERROR, (e) => { + emittedError = e; + }); + + for (let i = 0; i < maxRetries; i += 1) { + // Sleep two ticks in a row to simulate an error after every emission + // of the chunk of data + + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + + blockHeadersStream.destroy(new Error()); + } + + while (headersFromEvent.length !== mockedHeaders.length) { + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + } + + expect(emittedError).to.not.exist(); + expect(headersFromEvent).to.deep.equal(mockedHeaders); + }); + + it('should emit HANDLE_STREAM_ERROR command in case of the stream error', async () => { + const maxRetries = 0; + const subscribeToHistoricalBatchWithRetry = blockHeadersReader.subscribeToHistoricalBatch( + maxRetries, + ); + await subscribeToHistoricalBatchWithRetry(1, 1); + + let emittedError; + let streamFromCommand; + blockHeadersReader.on(BlockHeadersReader.COMMANDS.HANDLE_STREAM_ERROR, (stream, e) => { + streamFromCommand = stream; + emittedError = e; + }); + const errorToThrow = new Error('test'); + blockHeadersStream.destroy(errorToThrow); + + await sleepOneTick(); + + expect(emittedError).to.equal(errorToThrow); + expect(streamFromCommand).to.equal(blockHeadersStream); + }); + + it('should emit HANDLE_STREAM_ERROR command in case of deliberate rejection of the headers', async () => { + const maxRetries = 0; + const subscribeToHistoricalBatchWithRetry = blockHeadersReader.subscribeToHistoricalBatch( + maxRetries, + ); + await subscribeToHistoricalBatchWithRetry(1, 1); + + const errorToRejectWith = new Error('test'); + let errorEmitted; + let streamFromCommand; + + blockHeadersReader.on(BlockHeadersReader.COMMANDS.HANDLE_STREAM_ERROR, (stream, e) => { + errorEmitted = e; + streamFromCommand = stream; + }); + + blockHeadersReader.on(BlockHeadersReader.EVENTS.BLOCK_HEADERS, (_, reject) => { + // Simulate rejection of the headers in case they are not valid + reject(errorToRejectWith); + }); + + while (!errorEmitted) { + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + } + + expect(errorEmitted).to.equal(errorToRejectWith); + expect(streamFromCommand).to.equal(blockHeadersStream); + }); + + it('should emit HANDLE_STREAM_ERROR command if retry attempts are exhausted', async () => { + const maxRetries = 3; + const subscribeToHistoricalBatchWithRetry = blockHeadersReader.subscribeToHistoricalBatch( + maxRetries, + ); + await subscribeToHistoricalBatchWithRetry(1, 1); + + const errorToThrow = new Error('test'); + let emittedError; + let streamFromCommand; + blockHeadersReader.on(BlockHeadersReader.COMMANDS.HANDLE_STREAM_ERROR, (stream, e) => { + emittedError = e; + streamFromCommand = stream; + }); + + for (let i = 0; i < maxRetries + 1; i += 1) { + blockHeadersStream.destroy(errorToThrow); + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + } + + while (!emittedError) { + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + } + + expect(emittedError).to.equal(errorToThrow); + expect(streamFromCommand).to.equal(blockHeadersStream); + }); + + it('should emit HANDLE_STREAM_ERROR command if stream failed to be created in retry attempt', async () => { + const maxRetries = 1; + const subscribeToHistoricalBatchWithRetry = blockHeadersReader.subscribeToHistoricalBatch( + maxRetries, + ); + await subscribeToHistoricalBatchWithRetry(1, 1); + + // Throw an error on a second call of subscribe + const errorToThrow = new Error('test'); + subscribeToBlockHeadersWithChainLocksStub.onSecondCall().rejects(errorToThrow); + + let emittedError; + let streamFromCommand; + blockHeadersReader.on(BlockHeadersReader.COMMANDS.HANDLE_STREAM_ERROR, (stream, e) => { + emittedError = e; + streamFromCommand = stream; + }); + + // Emit error from stream to trigger retry attempt + blockHeadersStream.destroy(new Error('test')); + + await sleepOneTick(); + expect(emittedError).to.equal(errorToThrow); + expect(streamFromCommand).to.equal(blockHeadersStream); + }); + }); + + describe('#subscribeToNew', () => { + beforeEach(async () => { + await blockHeadersReader.subscribeToNew(1); + }); + + it('should emit BLOCK_HEADERS event', async () => { + let headersFromEvent; + + blockHeadersReader.on(BlockHeadersReader.EVENTS.BLOCK_HEADERS, (headers) => { + headersFromEvent = headers; + blockHeadersStream.destroy(); + }); + + while (!headersFromEvent) { + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + } + + expect(headersFromEvent).to.deep.equal(mockedHeaders.slice(0, headersBatchSize)); + }); + + it('should emit ERROR event in case of the stream error', async () => { + let emittedError; + blockHeadersReader.on(BlockHeadersReader.EVENTS.ERROR, (e) => { + emittedError = e; + }); + const errorToThrow = new Error('test'); + blockHeadersStream.destroy(errorToThrow); + + while (!emittedError) { + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + } + + expect(emittedError).to.equal(errorToThrow); + }); + + it('should emit ERROR event in case of deliberate rejection of BLOCK_HEADERS', async () => { + const errorToRejectWith = new Error('test'); + let errorEmitted; + + blockHeadersReader.on(BlockHeadersReader.EVENTS.ERROR, (e) => { + errorEmitted = e; + }); + + blockHeadersReader.on(BlockHeadersReader.EVENTS.BLOCK_HEADERS, (_, reject) => { + // Simulate rejection of the headers in case they are not valid + reject(errorToRejectWith); + }); + + while (!errorEmitted) { + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + } + + expect(errorEmitted).to.equal(errorToRejectWith); + }); + }); + + describe('#readHistorical', () => { + let headersAmount; + beforeEach(async () => { + headersAmount = mockedHeaders.length; + await blockHeadersReader.readHistorical(1, headersAmount); + }); + + // after + + it('should emit BLOCK_HEADERS event', async () => { + let obtainedHeaders = []; + blockHeadersReader.on(BlockHeadersReader.EVENTS.BLOCK_HEADERS, (headers) => { + obtainedHeaders = [...obtainedHeaders, ...headers]; + }); + + while (obtainedHeaders.length !== mockedHeaders.length) { + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + } + + obtainedHeaders.sort((a, b) => a.timestamp - b.timestamp); + expect(obtainedHeaders).to.deep.equal(mockedHeaders); + }); + + it('should emit HISTORICAL_DATA_OBTAINED event once all historical block headers fetched', async () => { + let eventEmitted; + blockHeadersReader.on(BlockHeadersReader.EVENTS.HISTORICAL_DATA_OBTAINED, () => { + eventEmitted = true; + }); + + while (typeof eventEmitted === 'undefined') { + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + } + + expect(eventEmitted).to.be.true(); + }); + + it('should emit ERROR event in case of errors in streams', async () => { + const errorsEmitted = []; + const errorsToEmit = []; + + blockHeadersReader.on(BlockHeadersReader.EVENTS.ERROR, (e) => { + errorsEmitted.push(e); + }); + + [...blockHeadersReader.historicalStreams].forEach((stream, i) => { + const e = new Error(`test${i}`); + errorsToEmit.push(e); + stream.destroy(e); + }); + + while (errorsEmitted.length !== errorsToEmit.length) { + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + } + + expect(errorsEmitted).to.deep.equal(errorsToEmit); + }); + + it('should throw an error in attempt to run readHistorical for a second time', async () => { + try { + await blockHeadersReader.readHistorical(1, headersAmount); + } catch (e) { + expect(e.message).to.equal('Historical streams are already running'); + } + }); + }); +}); diff --git a/packages/js-dapi-client/test/integration/DAPIClient.spec.js b/packages/js-dapi-client/test/integration/DAPIClient.spec.js new file mode 100644 index 00000000000..be852d3dbe1 --- /dev/null +++ b/packages/js-dapi-client/test/integration/DAPIClient.spec.js @@ -0,0 +1,28 @@ +const DAPIClient = require('../../lib/DAPIClient'); + +const BlockHeadersProvider = require('../../lib/BlockHeadersProvider/BlockHeadersProvider'); + +describe('DAPIClient - integration', () => { + let dapiClient; + beforeEach(() => { + dapiClient = new DAPIClient(); + }); + + describe('BlockHeadersProvider', () => { + it('should instantiate a BlockHeadersProvider from default options', () => { + expect(dapiClient.blockHeadersProvider).to.be.instanceOf(BlockHeadersProvider); + }); + + it('should propagate ERROR event from BlockHeadersProvider', () => { + let emittedError; + dapiClient.on(DAPIClient.EVENTS.ERROR, (e) => { + emittedError = e; + }); + + const errorToEmit = new Error('test error'); + dapiClient.blockHeadersProvider.emit(BlockHeadersProvider.EVENTS.ERROR, errorToEmit); + + expect(emittedError).to.equal(errorToEmit); + }); + }); +}); diff --git a/packages/js-dapi-client/test/integration/SMLProvider/SimplifiedMasternodeListProvider.spec.js b/packages/js-dapi-client/test/integration/SMLProvider/SimplifiedMasternodeListProvider.spec.js new file mode 100644 index 00000000000..ffa67daea14 --- /dev/null +++ b/packages/js-dapi-client/test/integration/SMLProvider/SimplifiedMasternodeListProvider.spec.js @@ -0,0 +1,163 @@ +const SimplifiedMNList = require('@dashevo/dashcore-lib/lib/deterministicmnlist/SimplifiedMNList'); + +const SimplifiedMasternodeListProvider = require('../../../lib/SimplifiedMasternodeListProvider/SimplifiedMasternodeListProvider'); +const DAPIAddress = require('../../../lib/dapiAddressProvider/DAPIAddress'); + +const getMNListDiffsFixture = require('../../../lib/test/fixtures/getMNListDiffsFixture'); + +const wait = require('../../../lib/test/utils/wait'); + +describe('SimplifiedMasternodeListProvider', () => { + let jsonTransportMock; + let smlProvider; + let lastUsedAddress; + let mnListDiffsFixture; + + beforeEach(function beforeEach() { + lastUsedAddress = new DAPIAddress('127.0.0.1'); + + jsonTransportMock = { + request: this.sinon.stub(), + getLastUsedAddress: this.sinon.stub().returns(lastUsedAddress), + }; + + mnListDiffsFixture = getMNListDiffsFixture(); + + jsonTransportMock.request.withArgs('getBestBlockHash').onCall(0).resolves( + mnListDiffsFixture[0].blockHash, + ); + + jsonTransportMock.request.withArgs('getBestBlockHash').onCall(1).resolves( + mnListDiffsFixture[1].blockHash, + ); + + jsonTransportMock.request.withArgs('getMnListDiff').onCall(0).resolves( + mnListDiffsFixture[0], + ); + + jsonTransportMock.request.withArgs('getMnListDiff').onCall(1).resolves( + mnListDiffsFixture[1], + ); + + smlProvider = new SimplifiedMasternodeListProvider(jsonTransportMock, { + updateInterval: 50, + network: 'testnet', + }); + }); + + describe('#getSimplifiedMNList', () => { + it('should update SML and return list of valid masternodes', async () => { + expect(smlProvider.lastUpdateDate).to.equal(0); + expect(smlProvider.baseBlockHash).to.equal(SimplifiedMasternodeListProvider.NULL_HASH); + + const sml = await smlProvider.getSimplifiedMNList(); + + expect(sml).to.be.an.instanceOf(SimplifiedMNList); + expect(sml.mnList).to.have.lengthOf(mnListDiffsFixture[0].mnList.length); + + expect(smlProvider.lastUpdateDate).to.not.equal(0); + expect(smlProvider.baseBlockHash).to.equal(mnListDiffsFixture[0].blockHash); + + expect(jsonTransportMock.request).to.be.calledTwice(); + + expect(jsonTransportMock.request.getCall(0).args).to.deep.equal([ + 'getBestBlockHash', + ]); + + expect(jsonTransportMock.request.getCall(1).args).to.deep.equal([ + 'getMnListDiff', + { + baseBlockHash: SimplifiedMasternodeListProvider.NULL_HASH, + blockHash: mnListDiffsFixture[0].blockHash, + }, + { + addresses: [lastUsedAddress], + }, + ]); + }); + + it('should return the previous list of valid masternodes in case if update interval is not reached', async () => { + await smlProvider.getSimplifiedMNList(); + + expect(jsonTransportMock.request).to.be.calledTwice(); + + // noinspection DuplicatedCode + const sml = await smlProvider.getSimplifiedMNList(); + + expect(sml).to.be.an.instanceOf(SimplifiedMNList); + expect(sml.mnList).to.have.lengthOf(mnListDiffsFixture[0].mnList.length); + + expect(jsonTransportMock.request).to.be.calledTwice(); + }); + + it('should use updated baseBlockHash for the second call', async function it() { + this.timeout(5000); + + const firstSML = await smlProvider.getSimplifiedMNList(); + + expect(firstSML).to.be.an.instanceOf(SimplifiedMNList); + expect(firstSML.mnList).to.have.lengthOf(mnListDiffsFixture[0].mnList.length); + + expect(jsonTransportMock.request).to.be.calledTwice(); + + await wait(500); + + const secondSML = await smlProvider.getSimplifiedMNList(); + + expect(secondSML).to.be.an.instanceOf(SimplifiedMNList); + + expect(jsonTransportMock.request).to.be.callCount(4); + + expect(secondSML.mnList).to.have.lengthOf(122); + expect(jsonTransportMock.request).to.be.callCount(4); + + expect(jsonTransportMock.request.getCall(2).args).to.deep.equal([ + 'getBestBlockHash', + ]); + + expect(jsonTransportMock.request.getCall(3).args).to.deep.equal([ + 'getMnListDiff', + { + baseBlockHash: mnListDiffsFixture[0].blockHash, + blockHash: mnListDiffsFixture[1].blockHash, + }, + { + addresses: [lastUsedAddress], + }, + ]); + }); + + it('should reset simplifiedMNList and update masternode list from scratch', async function it() { + this.timeout(5000); + + jsonTransportMock.request.withArgs('getBestBlockHash').onCall(1).resolves( + mnListDiffsFixture[0].blockHash, + ); + + jsonTransportMock.request.withArgs('getMnListDiff').onCall(1).resolves( + mnListDiffsFixture[0], + ); + + jsonTransportMock.request.withArgs('getBestBlockHash').onCall(2).resolves( + mnListDiffsFixture[0].blockHash, + ); + + jsonTransportMock.request.withArgs('getMnListDiff').onCall(2).resolves( + mnListDiffsFixture[0], + ); + + expect(smlProvider.lastUpdateDate).to.equal(0); + expect(smlProvider.baseBlockHash).to.equal(SimplifiedMasternodeListProvider.NULL_HASH); + + await smlProvider.getSimplifiedMNList(); + await wait(200); + + const sml = await smlProvider.getSimplifiedMNList(); + + expect(sml).to.be.an.instanceOf(SimplifiedMNList); + expect(sml.mnList).to.have.lengthOf(mnListDiffsFixture[0].mnList.length); + + expect(jsonTransportMock.request).to.be.callCount(6); + }); + }); +}); diff --git a/packages/js-dapi-client/test/integration/methods/core/CoreMethodsFacade.spec.js b/packages/js-dapi-client/test/integration/methods/core/CoreMethodsFacade.spec.js new file mode 100644 index 00000000000..0e007be0676 --- /dev/null +++ b/packages/js-dapi-client/test/integration/methods/core/CoreMethodsFacade.spec.js @@ -0,0 +1,173 @@ +const { EventEmitter } = require('events'); + +const { + v0: { + BroadcastTransactionResponse, + GetBlockResponse, + GetTransactionResponse, + GetStatusResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const BloomFilter = require('@dashevo/dashcore-lib/lib/bloomfilter'); + +const CoreMethodsFacade = require('../../../../lib/methods/core/CoreMethodsFacade'); + +describe('CoreMethodsFacade', () => { + let jsonRpcTransportMock; + let grpcTransportMock; + let coreMethods; + + beforeEach(function beforeEach() { + jsonRpcTransportMock = { + request: this.sinon.stub(), + }; + grpcTransportMock = { + request: this.sinon.stub(), + }; + + coreMethods = new CoreMethodsFacade(jsonRpcTransportMock, grpcTransportMock); + }); + + describe('#broadcastTransaction', () => { + it('should broadcast transaction', async () => { + const response = new BroadcastTransactionResponse(); + response.setTransactionId('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176'); + grpcTransportMock.request.resolves(response); + + const transaction = Buffer.from('transaction'); + await coreMethods.broadcastTransaction(transaction); + + expect(grpcTransportMock.request).to.be.calledOnce(); + expect(jsonRpcTransportMock.request).to.be.not.called(); + }); + }); + + describe('#generateToAddress', () => { + it('should generate address', async () => { + const response = 'response'; + jsonRpcTransportMock.request.resolves(response); + await coreMethods.generateToAddress(1, 'yTMDce5yEpiPqmgPrPmTj7yAmQPJERUSVy'); + + expect(grpcTransportMock.request).to.be.not.called(); + expect(jsonRpcTransportMock.request).to.be.calledOnce(); + }); + }); + + describe('#getBestBlockHash', () => { + it('should get best block hash', async () => { + const response = '000000000b0339e07bce8b3186a6a57a3c45d10e16c4bce18ef81b667bc822b2'; + jsonRpcTransportMock.request.resolves(response); + await coreMethods.getBestBlockHash(); + + expect(grpcTransportMock.request).to.be.not.called(); + expect(jsonRpcTransportMock.request).to.be.calledOnce(); + }); + }); + + describe('#getBlockByHash', () => { + it('should get block by hash', async () => { + const block = Buffer.from('block'); + const response = new GetBlockResponse(); + response.setBlock(block); + grpcTransportMock.request.resolves(response); + await coreMethods.getBlockByHash('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176'); + + expect(grpcTransportMock.request).to.be.calledOnce(); + expect(jsonRpcTransportMock.request).to.be.not.called(); + }); + }); + + describe('#getBlockByHeight', () => { + it('should get block by height', async () => { + const block = Buffer.from('block'); + const response = new GetBlockResponse(); + response.setBlock(block); + grpcTransportMock.request.resolves(response); + await coreMethods.getBlockByHeight(1); + + expect(grpcTransportMock.request).to.be.calledOnce(); + expect(jsonRpcTransportMock.request).to.be.not.called(); + }); + }); + + describe('#getBlockHash', () => { + it('should get block hash', async () => { + const response = '000000000b0339e07bce8b3186a6a57a3c45d10e16c4bce18ef81b667bc822b2'; + jsonRpcTransportMock.request.resolves(response); + await coreMethods.getBlockHash(1); + + expect(grpcTransportMock.request).to.be.not.called(); + expect(jsonRpcTransportMock.request).to.be.calledOnce(); + }); + }); + + describe('#getMnListDiff', () => { + it('should get mn list diff', async () => { + const baseBlockHash = '0000047d24635e347be3aaaeb66c26be94901a2f962feccd4f95090191f208c1'; + const blockHash = '000000000b0339e07bce8b3186a6a57a3c45d10e16c4bce18ef81b667bc822b2'; + + const response = { + baseBlockHash, + blockHash, + deletedMNs: [], + mnList: [], + }; + jsonRpcTransportMock.request.resolves(response); + await coreMethods.getMnListDiff(baseBlockHash, blockHash); + + expect(grpcTransportMock.request).to.be.not.called(); + expect(jsonRpcTransportMock.request).to.be.calledOnce(); + }); + }); + + describe('#getStatus', () => { + it('should get status', async () => { + const response = new GetStatusResponse(); + + response.setStatus(GetStatusResponse.Status.READY); + + const masternode = new GetStatusResponse.Masternode(); + + masternode.setStatus(GetStatusResponse.Masternode.Status.READY); + + response.setMasternode(masternode); + + grpcTransportMock.request.resolves(response); + + await coreMethods.getStatus(); + + expect(jsonRpcTransportMock.request).to.be.not.called(); + expect(grpcTransportMock.request).to.be.calledOnce(); + }); + }); + + describe('#getTransaction', () => { + it('should get transaction', async () => { + const transaction = Buffer.from('transaction'); + const response = new GetTransactionResponse(); + response.setTransaction(transaction); + response.setBlockHash(Buffer.from('blockHash')); + response.setHeight(1); + response.setConfirmations(2); + + grpcTransportMock.request.resolves(response); + await coreMethods.getTransaction('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176'); + + expect(grpcTransportMock.request).to.be.calledOnce(); + expect(jsonRpcTransportMock.request).to.be.not.called(); + }); + }); + + describe('#subscribeToTransactionsWithProofs', () => { + it('should subscribe to transactions with proofs', async () => { + const bloomFilter = BloomFilter.create(1, 0.001); + const response = new EventEmitter(); + grpcTransportMock.request.resolves(response); + await coreMethods.subscribeToTransactionsWithProofs(bloomFilter); + + expect(grpcTransportMock.request).to.be.calledOnce(); + expect(jsonRpcTransportMock.request).to.be.not.called(); + }); + }); +}); diff --git a/packages/js-dapi-client/test/integration/methods/platform/PlatformMethodsFacade.spec.js b/packages/js-dapi-client/test/integration/methods/platform/PlatformMethodsFacade.spec.js new file mode 100644 index 00000000000..775f5ab1b52 --- /dev/null +++ b/packages/js-dapi-client/test/integration/methods/platform/PlatformMethodsFacade.spec.js @@ -0,0 +1,105 @@ +const { + v0: { + ResponseMetadata, + GetDataContractResponse, + GetDocumentsResponse, + GetIdentityResponse, + BroadcastStateTransitionResponse, + WaitForStateTransitionResultResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const DashPlatformProtocol = require('@dashevo/dpp'); + +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); + +const PlatformMethodsFacade = require('../../../../lib/methods/platform/PlatformMethodsFacade'); + +describe('PlatformMethodsFacade', () => { + let grpcTransportMock; + let platformMethods; + + beforeEach(function beforeEach() { + grpcTransportMock = { + request: this.sinon.stub(), + }; + + platformMethods = new PlatformMethodsFacade(grpcTransportMock); + }); + + describe('#broadcastStateTransition', () => { + it('should broadcast state transition', async () => { + const response = new BroadcastStateTransitionResponse(); + grpcTransportMock.request.resolves(response); + + const dpp = new DashPlatformProtocol(); + await dpp.initialize(); + const stateTransition = dpp.dataContract.createDataContractCreateTransition( + getDataContractFixture(), + ); + + await platformMethods.broadcastStateTransition(stateTransition); + + expect(grpcTransportMock.request).to.be.calledOnce(); + }); + }); + + describe('#getDataContract', () => { + it('should get data contract', async () => { + const response = new GetDataContractResponse(); + response.setMetadata(new ResponseMetadata()); + response.setDataContract(getDataContractFixture().toBuffer()); + grpcTransportMock.request.resolves(response); + + await platformMethods.getDataContract(getDataContractFixture().getId()); + + expect(grpcTransportMock.request).to.be.calledOnce(); + }); + }); + + describe('#getDocuments', () => { + it('should get documents', async () => { + const response = new GetDocumentsResponse(); + response.setMetadata(new ResponseMetadata()); + grpcTransportMock.request.resolves(response); + + await platformMethods.getDocuments( + '11c70af56a763b05943888fa3719ef56b3e826615fdda2d463c63f4034cb861c', + 'niceDocument', + ); + + expect(grpcTransportMock.request).to.be.calledOnce(); + }); + }); + + describe('#getIdentity', () => { + it('should get Identity', async () => { + const response = new GetIdentityResponse(); + + response.setMetadata(new ResponseMetadata()); + response.setIdentity(getIdentityFixture().toBuffer()); + + grpcTransportMock.request.resolves(response); + + await platformMethods.getIdentity('41nthkqvHBLnqiMkSbsdTNANzYu9bgdv4etKoRUunY1M'); + + expect(grpcTransportMock.request).to.be.calledOnce(); + }); + }); + + describe('#waitForStateTransitionResult', () => { + it('should wait for state transition', async () => { + const response = new WaitForStateTransitionResultResponse(); + response.setMetadata(new ResponseMetadata()); + grpcTransportMock.request.resolves(response); + + await platformMethods.waitForStateTransitionResult( + Buffer.from('6f49655a2906852a38e473dd47574fb70b8b7c4e5cee9ea8e7da3f07b970c421', 'hex'), + false, + ); + + expect(grpcTransportMock.request).to.be.calledOnce(); + }); + }); +}); diff --git a/packages/js-dapi-client/test/unit/BlockHeadersProvider/BlockHeadersReader.spec.js b/packages/js-dapi-client/test/unit/BlockHeadersProvider/BlockHeadersReader.spec.js new file mode 100644 index 00000000000..5cfc4bf4517 --- /dev/null +++ b/packages/js-dapi-client/test/unit/BlockHeadersProvider/BlockHeadersReader.spec.js @@ -0,0 +1,215 @@ +const EventEmitter = require('events'); +const { expect } = require('chai'); +const BlockHeadersReader = require('../../../lib/BlockHeadersProvider/BlockHeadersReader'); + +const sleepOneTick = () => new Promise((resolve) => { + if (typeof setImmediate === 'undefined') { + setTimeout(resolve, 10); + } else { + setImmediate(resolve); + } +}); + +describe('BlockHeadersReader - unit', () => { + let options; + + let coreApiMock; + let blockHeadersReader; + let streamMock; + beforeEach(function () { + coreApiMock = { + subscribeToBlockHeadersWithChainLocks: () => {}, + }; + + this.sinon.stub(coreApiMock, 'subscribeToBlockHeadersWithChainLocks').callsFake(async () => { + streamMock = new EventEmitter(); + streamMock.destroy = (e) => { + streamMock.emit('error', e); + }; + this.sinon.spy(streamMock, 'on'); + + return streamMock; + }); + + options = { + coreMethods: coreApiMock, + maxRetries: 5, + maxParallelStreams: 6, + targetBatchSize: 10, + }; + + blockHeadersReader = new BlockHeadersReader(options); + }); + + describe('#subscribeToHistoricalBatch', () => { + const numHeaders = 20; + + beforeEach(async () => { + const subscribeToHistoricalBatchWithRetries = blockHeadersReader.subscribeToHistoricalBatch( + options.maxRetries, + ); + await subscribeToHistoricalBatchWithRetries(1, numHeaders); + }); + + it('should subscribe to a stream', () => { + expect(blockHeadersReader.coreMethods.subscribeToBlockHeadersWithChainLocks) + .to.be.calledOnce(); + }); + + it('should hook on stream events', () => { + expect(streamMock.on).to.be.calledWith('data'); + expect(streamMock.on).to.be.calledWith('error'); + expect(streamMock.on).to.be.calledWith('end'); + }); + + it('should only fetch remaining amount of headers in case of the retry attempt', async () => { + const headers = Array.from({ length: numHeaders }).map((_, i) => `0x${i.toString(16)}`); + const firstBatch = headers.slice(0, headers.length / 3); + // Emit first batch of data + streamMock.emit('data', { + getBlockHeaders: () => ({ + getHeadersList: () => firstBatch, + }), + }); + + streamMock.emit('error', new Error('retry')); + + await sleepOneTick(); + + const subscribeStub = coreApiMock.subscribeToBlockHeadersWithChainLocks; + expect(subscribeStub).to.be.calledTwice(); + expect(subscribeStub.firstCall.args[0]).to.deep.equal({ + fromBlockHeight: 1, + count: headers.length, + }); + expect(subscribeStub.secondCall.args[0]).to.deep.equal({ + fromBlockHeight: firstBatch.length + 1, + count: headers.length - firstBatch.length, + }); + }); + }); + + describe('#subscribeToNew', () => { + let stream; + beforeEach(async () => { + stream = await blockHeadersReader.subscribeToNew(1); + }); + + it('should subscribe to a stream', () => { + expect(blockHeadersReader.coreMethods.subscribeToBlockHeadersWithChainLocks) + .to.be.calledOnce(); + }); + + it('should hook on stream events', () => { + expect(stream.on).to.be.calledWith('data'); + expect(stream.on).to.be.calledWith('error'); + }); + }); + + describe('#readHistorical', () => { + beforeEach(function () { + this.sinon.spy(blockHeadersReader, 'subscribeToHistoricalBatch'); + }); + + it('should create only one stream in case the amount of blocks is too small', async () => { + await blockHeadersReader.readHistorical(1, Math.ceil(options.targetBatchSize * 1.4)); + expect(blockHeadersReader.subscribeToHistoricalBatch).to.be.calledOnce(); + }); + + it('should evenly spread the load between streams', async () => { + const fromBlock = 1; + const toBlock = Math.round(options.targetBatchSize * 3.5 - 1); + const totalAmount = toBlock - fromBlock + 1; + const numStreams = Math.round(totalAmount / options.targetBatchSize); + + const itemsPerBatch = Math.ceil(totalAmount / numStreams); + + await blockHeadersReader.readHistorical(fromBlock, toBlock); + + const subscribeFunction = coreApiMock.subscribeToBlockHeadersWithChainLocks; + expect(subscribeFunction).to.be.calledThrice(); + expect(subscribeFunction.getCall(0).args[0]) + .to.deep.equal({ fromBlockHeight: fromBlock, count: itemsPerBatch }); + expect(subscribeFunction.getCall(1).args[0]) + .to.deep.equal({ fromBlockHeight: fromBlock + itemsPerBatch, count: itemsPerBatch }); + expect(subscribeFunction.getCall(2).args[0]) + .to.deep.equal({ + fromBlockHeight: fromBlock + 2 * itemsPerBatch, + count: totalAmount - itemsPerBatch * 2, + }); + }); + + it('should limit amount of streams in case batch size is too small compared to total amount', async () => { + await blockHeadersReader.readHistorical(1, options.targetBatchSize * 10); + expect(blockHeadersReader.subscribeToHistoricalBatch.callCount) + .to.equal(options.maxParallelStreams); + }); + + it('should throw an error in case the total amount of headers is less than 1', async () => { + const from = 2; + const to = 1; + try { + await blockHeadersReader.readHistorical(from, to); + } catch (e) { + expect(e.message).to.equal(`Invalid total amount of headers to read: ${to - from}`); + } + }); + + it('should replace stream in historicalStreams in case of retry attempt', async () => { + await blockHeadersReader.readHistorical(1, options.targetBatchSize * 5); + + let streamToReplaceWith; + blockHeadersReader.on(BlockHeadersReader.COMMANDS.HANDLE_STREAM_RETRY, (_, newStream) => { + streamToReplaceWith = newStream; + }); + + const streamToBreak = blockHeadersReader.historicalStreams[0]; + streamToBreak.emit('error', new Error('retry')); + + await sleepOneTick(); + + expect(blockHeadersReader.historicalStreams[0]).to.equal(streamToReplaceWith); + }); + + it('should remove stream from historicalStreams array in case of end', async () => { + await blockHeadersReader.readHistorical(1, options.targetBatchSize * 2); + const streamsAmount = blockHeadersReader.historicalStreams.length; + + const streamToFinish = blockHeadersReader.historicalStreams[0]; + streamToFinish.emit('end'); + + expect(blockHeadersReader.historicalStreams.length).to.equal(streamsAmount - 1); + expect(blockHeadersReader.historicalStreams.includes(streamToFinish)).to.be.false(); + }); + + it('should remove stream from historicalStreams array in case of error', async () => { + await blockHeadersReader.readHistorical(1, options.targetBatchSize * 2); + const streamsAmount = blockHeadersReader.historicalStreams.length; + + let streamToRemove; + blockHeadersReader.on(BlockHeadersReader.COMMANDS.HANDLE_STREAM_ERROR, (_, stream) => { + streamToRemove = stream; + }); + + let emittedError; + blockHeadersReader.on(BlockHeadersReader.EVENTS.ERROR, (e) => { + emittedError = e; + }); + + let lastError; + // Exhaust all retry attempts to actually throw an error + for (let i = 0; i < options.maxRetries + 1; i += 1) { + const [streamToBreak] = blockHeadersReader.historicalStreams; + lastError = new Error('retry'); + streamToBreak.emit('error', lastError); + // eslint-disable-next-line no-await-in-loop + await sleepOneTick(); + } + + expect(emittedError).to.deep.equal(lastError); + expect(streamToRemove).to.exist(); + expect(blockHeadersReader.historicalStreams.length).to.equal(streamsAmount - 1); + expect(blockHeadersReader.historicalStreams.includes(streamToRemove)).to.be.false(); + }); + }); +}); diff --git a/packages/js-dapi-client/test/unit/BlockHeadersProvider/createBlockHeadersProviderFromOptions.spec.js b/packages/js-dapi-client/test/unit/BlockHeadersProvider/createBlockHeadersProviderFromOptions.spec.js new file mode 100644 index 00000000000..4265b1c817f --- /dev/null +++ b/packages/js-dapi-client/test/unit/BlockHeadersProvider/createBlockHeadersProviderFromOptions.spec.js @@ -0,0 +1,63 @@ +const BlockHeadersProvider = require('../../../lib/BlockHeadersProvider/BlockHeadersProvider'); +const createBlockHeadersProviderFromOptions = require('../../../lib/BlockHeadersProvider/createBlockHeadersProviderFromOptions'); + +describe('#createBlockHeadersProviderFromOptions', () => { + const coreMethodsMock = {}; + let options; + + beforeEach(() => { + options = { + network: 'testnet', + }; + }); + + it('should create BlockHeadersProvider with default options', () => { + const provider = createBlockHeadersProviderFromOptions(options, coreMethodsMock); + expect(provider).to.be.instanceOf(BlockHeadersProvider); + }); + + it('should create BlockHeadersProvider from \'blockHeadersProvider\' option', () => { + const blockHeadersProvider = new BlockHeadersProvider(); + options.blockHeadersProvider = blockHeadersProvider; + const provider = createBlockHeadersProviderFromOptions(options, coreMethodsMock); + expect(provider).to.equal(blockHeadersProvider); + }); + + it('should create BlockHeadersProvider from \'blockHeadersProviderOptions\' option', () => { + options = { + ...options, + blockHeadersProviderOptions: { + maxRetries: 100, + maxParallelStreams: 5, + targetBatchSize: 10, + fromBlockHeight: 5, + }, + }; + + const provider = createBlockHeadersProviderFromOptions(options, coreMethodsMock); + Object.keys(options.blockHeadersProviderOptions).forEach((key) => { + expect(provider.options[key]).to.equal(options.blockHeadersProviderOptions[key]); + }); + }); + + it('should validate options', () => { + const badOptions = { + maxRetries: -1, + maxParallelStreams: 0, + targetBatchSize: 0, + fromBlockHeight: 0, + autoStart: 'true', + network: 'keknet', + }; + + Object.keys(badOptions).forEach((badOption) => { + options = { + ...options, + blockHeadersProviderOptions: { + [badOption]: badOptions[badOption], + }, + }; + expect(() => createBlockHeadersProviderFromOptions(options, coreMethodsMock)).to.throw(); + }); + }); +}); diff --git a/packages/js-dapi-client/test/unit/DAPIClient.spec.js b/packages/js-dapi-client/test/unit/DAPIClient.spec.js new file mode 100644 index 00000000000..dcd3a22818f --- /dev/null +++ b/packages/js-dapi-client/test/unit/DAPIClient.spec.js @@ -0,0 +1,111 @@ +const DAPIClient = require('../../lib/DAPIClient'); +const CoreMethodsFacade = require('../../lib/methods/core/CoreMethodsFacade'); +const PlatformMethodsFacade = require('../../lib/methods/platform/PlatformMethodsFacade'); +const SimplifiedMasternodeListDAPIAddressProvider = require('../../lib/dapiAddressProvider/SimplifiedMasternodeListDAPIAddressProvider'); +const ListDAPIAddressProvider = require('../../lib/dapiAddressProvider/ListDAPIAddressProvider'); +const BlockHeadersProvider = require('../../lib/BlockHeadersProvider/BlockHeadersProvider'); + +describe('DAPIClient', () => { + let options; + let dapiClient; + + describe('#constructor', () => { + it('should construct DAPIClient with options', async () => { + options = { + retries: 0, + newOption: true, + }; + + dapiClient = new DAPIClient(options); + + expect(dapiClient.options).to.deep.equal({ + network: 'testnet', + retries: 0, + newOption: true, + timeout: 10000, + blockHeadersProviderOptions: BlockHeadersProvider.defaultOptions, + }); + + expect(dapiClient.dapiAddressProvider).to.be.an.instanceOf( + SimplifiedMasternodeListDAPIAddressProvider, + ); + + expect(dapiClient.blockHeadersProvider).to.be.an.instanceOf( + BlockHeadersProvider, + ); + + expect(dapiClient.core).to.be.an.instanceOf(CoreMethodsFacade); + expect(dapiClient.platform).to.be.an.instanceOf(PlatformMethodsFacade); + }); + + it('should construct DAPIClient without options', async () => { + dapiClient = new DAPIClient(); + + expect(dapiClient.options).to.deep.equal({ + retries: 5, + timeout: 10000, + network: 'testnet', + blockHeadersProviderOptions: BlockHeadersProvider.defaultOptions, + }); + + expect(dapiClient.dapiAddressProvider).to.be.an.instanceOf( + SimplifiedMasternodeListDAPIAddressProvider, + ); + + expect(dapiClient.blockHeadersProvider).to.be.an.instanceOf( + BlockHeadersProvider, + ); + + expect(dapiClient.core).to.be.an.instanceOf(CoreMethodsFacade); + expect(dapiClient.platform).to.be.an.instanceOf(PlatformMethodsFacade); + }); + + it('should construct DAPIClient with address options', async () => { + options = { + retries: 0, + dapiAddresses: ['localhost'], + }; + + dapiClient = new DAPIClient(options); + + expect(dapiClient.options).to.deep.equal({ + retries: 0, + dapiAddresses: ['localhost'], + network: 'testnet', + timeout: 10000, + blockHeadersProviderOptions: BlockHeadersProvider.defaultOptions, + }); + + expect(dapiClient.dapiAddressProvider).to.be.an.instanceOf(ListDAPIAddressProvider); + + expect(dapiClient.blockHeadersProvider).to.be.an.instanceOf( + BlockHeadersProvider, + ); + + expect(dapiClient.core).to.be.an.instanceOf(CoreMethodsFacade); + expect(dapiClient.platform).to.be.an.instanceOf(PlatformMethodsFacade); + }); + + it('should construct DAPIClient with network options', async () => { + options = { + retries: 3, + network: 'local', + }; + + dapiClient = new DAPIClient(options); + expect(dapiClient.options).to.deep.equal({ + retries: 3, + network: 'local', + timeout: 10000, + blockHeadersProviderOptions: BlockHeadersProvider.defaultOptions, + }); + + expect(dapiClient.dapiAddressProvider).to.be.an.instanceOf(ListDAPIAddressProvider); + expect(dapiClient.blockHeadersProvider).to.be.an.instanceOf( + BlockHeadersProvider, + ); + expect(dapiClient.core).to.be.an.instanceOf(CoreMethodsFacade); + expect(dapiClient.platform).to.be.an.instanceOf(PlatformMethodsFacade); + }); + }); +}); diff --git a/packages/js-dapi-client/test/unit/dapiAddressProvider/DAPIAddress.spec.js b/packages/js-dapi-client/test/unit/dapiAddressProvider/DAPIAddress.spec.js new file mode 100644 index 00000000000..9d4b98c47cc --- /dev/null +++ b/packages/js-dapi-client/test/unit/dapiAddressProvider/DAPIAddress.spec.js @@ -0,0 +1,268 @@ +const DAPIAddress = require('../../../lib/dapiAddressProvider/DAPIAddress'); +const DAPIAddressHostMissingError = require( + '../../../lib/dapiAddressProvider/errors/DAPIAddressHostMissingError', +); + +describe('DAPIAddress', () => { + let host; + let httpPort; + let grpcPort; + + beforeEach(() => { + host = '127.0.0.1'; + httpPort = DAPIAddress.DEFAULT_HTTP_PORT + 1; + grpcPort = DAPIAddress.DEFAULT_GRPC_PORT + 1; + }); + + describe('#constructor', () => { + it('should construct DAPIAddress from string with host and both ports', () => { + const dapiAddress = new DAPIAddress(`${host}:${httpPort}:${grpcPort}`); + + expect(dapiAddress).to.be.an.instanceOf(DAPIAddress); + expect(dapiAddress.host).to.equal(host); + expect(dapiAddress.httpPort).to.equal(httpPort); + expect(dapiAddress.grpcPort).to.equal(grpcPort); + expect(dapiAddress.proRegTxHash).to.be.undefined(); + expect(dapiAddress.banCount).to.equal(0); + expect(dapiAddress.banStartTime).to.be.undefined(); + }); + + it('should construct DAPIAddress from string with host and HTTP port', () => { + const dapiAddress = new DAPIAddress(`${host}:${httpPort}`); + + expect(dapiAddress).to.be.an.instanceOf(DAPIAddress); + expect(dapiAddress.host).to.equal(host); + expect(dapiAddress.httpPort).to.equal(httpPort); + expect(dapiAddress.grpcPort).to.equal(DAPIAddress.DEFAULT_GRPC_PORT); + expect(dapiAddress.proRegTxHash).to.be.undefined(); + expect(dapiAddress.banCount).to.equal(0); + expect(dapiAddress.banStartTime).to.be.undefined(); + }); + + it('should construct DAPIAddress from DAPIAddress', () => { + const address = new DAPIAddress(host); + + const dapiAddress = new DAPIAddress(address); + + expect(dapiAddress).to.be.an.instanceOf(DAPIAddress); + expect(dapiAddress.toJSON()).to.deep.equal(address.toJSON()); + }); + + it('should construct DAPIAddress form RawDAPIAddress', () => { + const dapiAddress = new DAPIAddress({ + host, + }); + + expect(dapiAddress).to.be.an.instanceOf(DAPIAddress); + expect(dapiAddress.host).to.equal(host); + expect(dapiAddress.httpPort).to.equal(DAPIAddress.DEFAULT_HTTP_PORT); + expect(dapiAddress.grpcPort).to.equal(DAPIAddress.DEFAULT_GRPC_PORT); + expect(dapiAddress.proRegTxHash).to.be.undefined(); + expect(dapiAddress.banCount).to.equal(0); + expect(dapiAddress.banStartTime).to.be.undefined(); + }); + + it('should construct DAPIAddress with defined ports', () => { + const proRegTxHash = 'proRegTxHash'; + + const dapiAddress = new DAPIAddress({ + host, + httpPort, + grpcPort, + proRegTxHash, + }); + + expect(dapiAddress).to.be.an.instanceOf(DAPIAddress); + expect(dapiAddress.banCount).to.equal(0); + expect(dapiAddress.banStartTime).to.be.undefined(); + expect(dapiAddress.toJSON()).to.deep.equal({ + grpcPort, + host, + httpPort, + proRegTxHash, + }); + }); + + it('should not set banCount and banStartTime from RawDAPIAddress', async () => { + const dapiAddress = new DAPIAddress({ + host, + banCount: 100, + banStartTime: 1000, + }); + + expect(dapiAddress).to.be.an.instanceOf(DAPIAddress); + expect(dapiAddress.banCount).to.equal(0); + expect(dapiAddress.banStartTime).to.be.undefined(); + }); + + it('should throw DAPIAddressHostMissingError if host is missed', () => { + try { + // eslint-disable-next-line no-new + new DAPIAddress(''); + + expect.fail('should throw DAPIAddressHostMissingError'); + } catch (e) { + expect(e).to.be.an.instanceOf(DAPIAddressHostMissingError); + } + }); + }); + + describe('#getHost', () => { + it('should return host', () => { + const dapiAddress = new DAPIAddress(host); + + expect(dapiAddress.getHost()).to.equal(host); + }); + }); + + describe('#setHost', () => { + it('should set host', () => { + const otherHost = '192.168.1.1'; + + const dapiAddress = new DAPIAddress(host); + dapiAddress.setHost(otherHost); + + expect(dapiAddress.host).to.equal(otherHost); + }); + }); + + describe('#getHttpPort', () => { + it('should get HTTP port', () => { + const dapiAddress = new DAPIAddress({ + host, + httpPort, + }); + + expect(dapiAddress.getHttpPort()).to.equal(httpPort); + }); + }); + + describe('#setHttpPort', () => { + it('should set HTTP port', () => { + const dapiAddress = new DAPIAddress(host); + dapiAddress.setHttpPort(httpPort); + + expect(dapiAddress.getHttpPort()).to.equal(httpPort); + }); + }); + + describe('#getGrpcPort', () => { + it('should get GRPC port', () => { + const dapiAddress = new DAPIAddress({ + host, + grpcPort, + }); + + expect(dapiAddress.getGrpcPort()).to.equal(grpcPort); + }); + }); + + describe('#setGrpcPort', () => { + it('should set GRPC port', () => { + const dapiAddress = new DAPIAddress(host); + dapiAddress.setGrpcPort(grpcPort); + + expect(dapiAddress.getGrpcPort()).to.equal(grpcPort); + }); + }); + + describe('#getProRegTxHash', () => { + it('should get ProRegTxHash', () => { + const proRegTxHash = 'proRegTxHash'; + + const dapiAddress = new DAPIAddress({ + host, + proRegTxHash, + }); + + expect(dapiAddress.getProRegTxHash()).to.equal(proRegTxHash); + }); + }); + + describe('#getBanStartTime', () => { + it('should get ban start time', () => { + const now = Date.now(); + + const dapiAddress = new DAPIAddress(host); + dapiAddress.banStartTime = now; + + const banStartTime = dapiAddress.getBanStartTime(); + expect(banStartTime).to.equal(now); + }); + }); + + describe('#getBanCount', () => { + it('should get ban count', () => { + const dapiAddress = new DAPIAddress(host); + dapiAddress.banCount = 666; + + const banCount = dapiAddress.getBanCount(); + expect(banCount).to.equal(666); + }); + }); + + describe('#markAsBanned', () => { + it('should mark address as banned', () => { + const dapiAddress = new DAPIAddress(host); + dapiAddress.markAsBanned(); + + expect(dapiAddress.banCount).to.equal(1); + expect(dapiAddress.banStartTime).to.be.greaterThan(0); + }); + }); + + describe('#markAsLive', () => { + it('should mark address as live', () => { + const dapiAddress = new DAPIAddress(host); + dapiAddress.banCount = 1; + dapiAddress.banStartTime = Date.now(); + + dapiAddress.markAsLive(); + + expect(dapiAddress.banCount).to.equal(0); + expect(dapiAddress.banStartTime).to.be.undefined(); + }); + }); + + describe('#isBanned', () => { + it('should return true if address is banned', () => { + const dapiAddress = new DAPIAddress(host); + + dapiAddress.banCount = 1; + + const isBanned = dapiAddress.isBanned(); + expect(isBanned).to.be.true(); + }); + + it('should return false if address is not banned', () => { + const dapiAddress = new DAPIAddress(host); + + const isBanned = dapiAddress.isBanned(); + expect(isBanned).to.be.false(); + }); + }); + + describe('#toJSON', () => { + it('should return RawDAPIAddress', () => { + const dapiAddress = new DAPIAddress(host); + + expect(dapiAddress.toJSON()).to.deep.equal({ + host: dapiAddress.getHost(), + httpPort: dapiAddress.getHttpPort(), + grpcPort: dapiAddress.getGrpcPort(), + proRegTxHash: dapiAddress.getProRegTxHash(), + }); + }); + }); + + describe('toString', () => { + it('should return a string representation', () => { + const dapiAddress = new DAPIAddress(host); + + const dapiAddressString = `${dapiAddress.getHost()}:` + + `${dapiAddress.getHttpPort()}:${dapiAddress.getGrpcPort()}`; + + expect(`${dapiAddress}`).to.equal(dapiAddressString); + }); + }); +}); diff --git a/packages/js-dapi-client/test/unit/dapiAddressProvider/ListDAPIAddressProvider.spec.js b/packages/js-dapi-client/test/unit/dapiAddressProvider/ListDAPIAddressProvider.spec.js new file mode 100644 index 00000000000..8df8243f477 --- /dev/null +++ b/packages/js-dapi-client/test/unit/dapiAddressProvider/ListDAPIAddressProvider.spec.js @@ -0,0 +1,140 @@ +const ListDAPIAddressProvider = require('../../../lib/dapiAddressProvider/ListDAPIAddressProvider'); +const DAPIAddress = require('../../../lib/dapiAddressProvider/DAPIAddress'); + +describe('ListDAPIAddressProvider', () => { + let listDAPIAddressProvider; + let addresses; + let options; + let bannedAddress; + let notBannedAddress; + + beforeEach(() => { + bannedAddress = new DAPIAddress('192.168.1.1'); + bannedAddress.markAsBanned(); + + notBannedAddress = new DAPIAddress('192.168.1.2'); + + addresses = [ + bannedAddress, + notBannedAddress, + ]; + + options = {}; + + listDAPIAddressProvider = new ListDAPIAddressProvider( + addresses, + options, + ); + }); + + describe('#constructor', () => { + it('should set base ban time option', () => { + const baseBanTime = 1000; + + listDAPIAddressProvider = new ListDAPIAddressProvider( + addresses, + { baseBanTime }, + ); + + expect(listDAPIAddressProvider.options.baseBanTime).to.equal(baseBanTime); + }); + + it('should set default base ban time option if not passed', () => { + listDAPIAddressProvider = new ListDAPIAddressProvider( + addresses, + ); + + expect(listDAPIAddressProvider.options.baseBanTime).to.equal(60 * 1000); + }); + }); + + describe('#getLiveAddresses', () => { + it('should return live addresses', () => { + const bannedInThePastAddress = new DAPIAddress('192.168.1.3'); + bannedInThePastAddress.banCount = 1; + bannedInThePastAddress.banStartTime = Date.now() - 3 * 60 * 1000; + + const bannedManyTimesAddress = new DAPIAddress('192.168.1.4'); + bannedManyTimesAddress.banCount = 3; + bannedManyTimesAddress.banStartTime = Date.now() - 2 * 60 * 1000; + + listDAPIAddressProvider = new ListDAPIAddressProvider([ + bannedAddress, + notBannedAddress, + bannedInThePastAddress, + bannedManyTimesAddress, + ]); + + const liveAddresses = listDAPIAddressProvider.getLiveAddresses(); + + expect(liveAddresses).to.have.lengthOf(2); + expect(liveAddresses[0]).to.equal(notBannedAddress); + expect(liveAddresses[1]).to.equal(bannedInThePastAddress); + }); + + it('should return empty array if all addresses are banned', () => { + listDAPIAddressProvider.addresses.forEach((address) => { + address.markAsBanned(); + }); + + const liveAddresses = listDAPIAddressProvider.getLiveAddresses(); + + expect(liveAddresses).to.have.lengthOf(0); + }); + }); + + describe('#getLiveAddress', () => { + it('should return random live address', async () => { + const address = await listDAPIAddressProvider.getLiveAddress(); + + expect(address).to.equal(notBannedAddress); + }); + + it('should return undefined when there are no live addresses', async () => { + listDAPIAddressProvider.addresses.forEach((address) => { + address.markAsBanned(); + }); + + const address = await listDAPIAddressProvider.getLiveAddress(); + + expect(address).to.be.undefined(); + }); + }); + + describe('#hasLiveAddresses', () => { + it('should return true if we have at least one unbanned address', async () => { + const hasAddresses = await listDAPIAddressProvider.hasLiveAddresses(); + + expect(hasAddresses).to.be.true(); + }); + + it('should return false if all addresses are banned', async () => { + listDAPIAddressProvider.addresses.forEach((address) => { + address.markAsBanned(); + }); + + const hasAddresses = await listDAPIAddressProvider.hasLiveAddresses(); + + expect(hasAddresses).to.be.false(); + }); + }); + + describe('#getAllAddresses', () => { + it('should get all addresses', () => { + const allAddresses = listDAPIAddressProvider.getAllAddresses(); + + expect(allAddresses).to.deep.equal(listDAPIAddressProvider.addresses); + }); + }); + + describe('#setAddresses', () => { + it('should set addresses and overwrite previous', () => { + addresses = [ + notBannedAddress, + ]; + listDAPIAddressProvider.setAddresses(addresses); + + expect(listDAPIAddressProvider.addresses).to.deep.equal(addresses); + }); + }); +}); diff --git a/packages/js-dapi-client/test/unit/dapiAddressProvider/SimplifiedMasternodeListDAPIAddressProvider.spec.js b/packages/js-dapi-client/test/unit/dapiAddressProvider/SimplifiedMasternodeListDAPIAddressProvider.spec.js new file mode 100644 index 00000000000..3f19bf81bb9 --- /dev/null +++ b/packages/js-dapi-client/test/unit/dapiAddressProvider/SimplifiedMasternodeListDAPIAddressProvider.spec.js @@ -0,0 +1,166 @@ +const SimplifiedMNListEntry = require('@dashevo/dashcore-lib/lib/deterministicmnlist/SimplifiedMNListEntry'); + +const getMNListDiffsFixture = require('../../../lib/test/fixtures/getMNListDiffsFixture'); +const DAPIAddress = require('../../../lib/dapiAddressProvider/DAPIAddress'); + +const SimplifiedMasternodeListDAPIAddressProvider = require('../../../lib/dapiAddressProvider/SimplifiedMasternodeListDAPIAddressProvider'); + +describe('SimplifiedMasternodeListDAPIAddressProvider', () => { + let smlDAPIAddressProvider; + let smlProviderMock; + let listDAPIAddressProviderMock; + let smlMock; + let validMasternodeList; + let addresses; + + beforeEach(function beforeEach() { + const [mnListDiffFixture] = getMNListDiffsFixture(); + + validMasternodeList = [ + new SimplifiedMNListEntry(mnListDiffFixture.mnList[0]), + new SimplifiedMNListEntry(mnListDiffFixture.mnList[1]), + new SimplifiedMNListEntry(mnListDiffFixture.mnList[2]), + ]; + + addresses = [ + new DAPIAddress({ + host: validMasternodeList[0].getIp(), + proRegTxHash: validMasternodeList[0].proRegTxHash, + }), + new DAPIAddress({ + host: '127.0.0.1', + proRegTxHash: validMasternodeList[1].proRegTxHash, + }), + new DAPIAddress({ + host: '127.0.0.1', + }), + ]; + + smlMock = { + getValidMasternodesList: this.sinon.stub().returns(validMasternodeList), + }; + + smlProviderMock = { + getSimplifiedMNList: this.sinon.stub().resolves(smlMock), + }; + + listDAPIAddressProviderMock = { + getLiveAddress: this.sinon.stub().resolves(addresses[0]), + hasLiveAddresses: this.sinon.stub().resolves(true), + getAllAddresses: this.sinon.stub().returns(addresses), + setAddresses: this.sinon.stub(), + }; + + smlDAPIAddressProvider = new SimplifiedMasternodeListDAPIAddressProvider( + smlProviderMock, + listDAPIAddressProviderMock, + [], + ); + }); + + describe('#getLiveAddress', () => { + it('should return live address', async () => { + const liveAddress = await smlDAPIAddressProvider.getLiveAddress(); + + expect(liveAddress).to.equal(addresses[0]); + + expect(listDAPIAddressProviderMock.setAddresses).to.be.calledOnce(); + + expect(listDAPIAddressProviderMock.setAddresses.getCall(0).args).to.have.lengthOf(1); + expect(listDAPIAddressProviderMock.setAddresses.getCall(0).args[0]).to.be.an('array'); + expect(listDAPIAddressProviderMock.setAddresses.getCall(0).args[0]).to.have.lengthOf(3); + + const [ + firstAddress, + secondAddress, + thirdAddress, + ] = listDAPIAddressProviderMock.setAddresses.getCall(0).args[0]; + + expect(firstAddress).to.be.instanceOf(DAPIAddress); + expect(firstAddress).to.equal(addresses[0]); + expect(firstAddress.toJSON()).to.deep.equal({ + host: validMasternodeList[0].getIp(), + httpPort: DAPIAddress.DEFAULT_HTTP_PORT, + grpcPort: DAPIAddress.DEFAULT_GRPC_PORT, + proRegTxHash: validMasternodeList[0].proRegTxHash, + }); + + expect(secondAddress).to.be.instanceOf(DAPIAddress); + expect(secondAddress).to.equal(addresses[1]); + expect(secondAddress.toJSON()).to.deep.equal({ + host: validMasternodeList[1].getIp(), + httpPort: DAPIAddress.DEFAULT_HTTP_PORT, + grpcPort: DAPIAddress.DEFAULT_GRPC_PORT, + proRegTxHash: validMasternodeList[1].proRegTxHash, + }); + + expect(thirdAddress).to.be.instanceOf(DAPIAddress); + expect(thirdAddress).to.not.equal(addresses[2]); + expect(thirdAddress.toJSON()).to.deep.equal({ + host: validMasternodeList[2].getIp(), + httpPort: DAPIAddress.DEFAULT_HTTP_PORT, + grpcPort: DAPIAddress.DEFAULT_GRPC_PORT, + proRegTxHash: validMasternodeList[2].proRegTxHash, + }); + + expect(smlMock.getValidMasternodesList).to.be.calledOnceWithExactly(); + expect(smlProviderMock.getSimplifiedMNList).to.be.calledOnceWithExactly(); + expect(listDAPIAddressProviderMock.getAllAddresses).to.be.calledOnceWithExactly(); + expect(listDAPIAddressProviderMock.getLiveAddress).to.be.calledOnceWithExactly(); + }); + + it('should return filtered live address', async () => { + smlDAPIAddressProvider = new SimplifiedMasternodeListDAPIAddressProvider( + smlProviderMock, + listDAPIAddressProviderMock, + [new DAPIAddress(validMasternodeList[1].getIp())], + ); + + await smlDAPIAddressProvider.getLiveAddress(); + + expect(listDAPIAddressProviderMock.setAddresses).to.be.calledOnce(); + + expect(listDAPIAddressProviderMock.setAddresses.getCall(0).args).to.have.lengthOf(1); + expect(listDAPIAddressProviderMock.setAddresses.getCall(0).args[0]).to.be.an('array'); + expect(listDAPIAddressProviderMock.setAddresses.getCall(0).args[0]).to.have.lengthOf(2); + + const [ + secondAddress, + thirdAddress, + ] = listDAPIAddressProviderMock.setAddresses.getCall(0).args[0]; + + expect(secondAddress).to.be.instanceOf(DAPIAddress); + expect(secondAddress).to.equal(addresses[1]); + expect(secondAddress.toJSON()).to.deep.equal({ + host: validMasternodeList[1].getIp(), + httpPort: DAPIAddress.DEFAULT_HTTP_PORT, + grpcPort: DAPIAddress.DEFAULT_GRPC_PORT, + proRegTxHash: validMasternodeList[1].proRegTxHash, + }); + + expect(thirdAddress).to.be.instanceOf(DAPIAddress); + expect(thirdAddress).to.not.equal(addresses[2]); + expect(thirdAddress.toJSON()).to.deep.equal({ + host: validMasternodeList[2].getIp(), + httpPort: DAPIAddress.DEFAULT_HTTP_PORT, + grpcPort: DAPIAddress.DEFAULT_GRPC_PORT, + proRegTxHash: validMasternodeList[2].proRegTxHash, + }); + + expect(smlMock.getValidMasternodesList).to.be.calledOnceWithExactly(); + expect(smlProviderMock.getSimplifiedMNList).to.be.calledOnceWithExactly(); + expect(listDAPIAddressProviderMock.getAllAddresses).to.be.calledOnceWithExactly(); + expect(listDAPIAddressProviderMock.getLiveAddress).to.be.calledOnceWithExactly(); + }); + }); + + describe('#hasLiveAddresses', () => { + it('should return ListAddressProvider#hasLiveAddresses result', async () => { + const result = await smlDAPIAddressProvider.hasLiveAddresses(); + + expect(result).to.be.true(); + + expect(listDAPIAddressProviderMock.hasLiveAddresses).to.be.calledOnceWithExactly(); + }); + }); +}); diff --git a/packages/js-dapi-client/test/unit/dapiAddressProvider/createDAPIAddressProviderFromOptions.spec.js b/packages/js-dapi-client/test/unit/dapiAddressProvider/createDAPIAddressProviderFromOptions.spec.js new file mode 100644 index 00000000000..bc5255b1245 --- /dev/null +++ b/packages/js-dapi-client/test/unit/dapiAddressProvider/createDAPIAddressProviderFromOptions.spec.js @@ -0,0 +1,153 @@ +const createDAPIAddressProviderFromOptions = require( + '../../../lib/dapiAddressProvider/createDAPIAddressProviderFromOptions', +); +const ListDAPIAddressProvider = require('../../../lib/dapiAddressProvider/ListDAPIAddressProvider'); +const SimplifiedMasternodeListDAPIAddressProvider = require('../../../lib/dapiAddressProvider/SimplifiedMasternodeListDAPIAddressProvider'); + +const networkConfigs = require('../../../lib/networkConfigs'); + +const DAPIClientError = require('../../../lib/errors/DAPIClientError'); + +describe('createDAPIAddressProviderFromOptions', () => { + describe('dapiAddressProvider', () => { + let options; + let dapiAddressProvider; + + beforeEach(() => { + dapiAddressProvider = Object.create(null); + + options = { + network: 'evonet', + dapiAddressProvider, + }; + }); + + it('should return AddressProvider from `dapiAddressProvider` option', async () => { + const result = createDAPIAddressProviderFromOptions(options); + + expect(result).to.equal(dapiAddressProvider); + }); + + it('should throw DAPIClientError if `dapiAddresses` option is passed too', async () => { + options.dapiAddresses = ['localhost']; + + try { + createDAPIAddressProviderFromOptions(options); + + expect.fail('should throw DAPIClientError'); + } catch (e) { + expect(e).to.be.an.instanceOf(DAPIClientError); + } + }); + + it('should throw DAPIClientError if `seeds` option is passed too', async () => { + options.seeds = ['127.0.0.1']; + + try { + createDAPIAddressProviderFromOptions(options); + + expect.fail('should throw DAPIClientError'); + } catch (e) { + expect(e).to.be.an.instanceOf(DAPIClientError); + } + }); + + it('should throw DAPIClientError if `dapiAddressesWhiteList` option is passed too', async () => { + options.dapiAddressesWhiteList = ['127.0.0.1']; + + try { + createDAPIAddressProviderFromOptions(options); + + expect.fail('should throw DAPIClientError'); + } catch (e) { + expect(e).to.be.an.instanceOf(DAPIClientError); + } + }); + }); + + describe('dapiAddresses', () => { + let options; + + beforeEach(() => { + options = { + dapiAddresses: ['localhost'], + network: 'local', + }; + }); + + it('should return ListDAPIAddressProvider with addresses', async () => { + const result = createDAPIAddressProviderFromOptions(options); + + expect(result).to.be.an.instanceOf(ListDAPIAddressProvider); + }); + + it('should throw DAPIClientError if `seeds` option is passed too', async () => { + options.seeds = ['127.0.0.1']; + + try { + createDAPIAddressProviderFromOptions(options); + + expect.fail('should throw DAPIClientError'); + } catch (e) { + expect(e).to.be.an.instanceOf(DAPIClientError); + } + }); + + it('should throw DAPIClientError if `dapiAddressesWhiteList` option is passed too', async () => { + options.dapiAddressesWhiteList = ['127.0.0.1']; + + try { + createDAPIAddressProviderFromOptions(options); + + expect.fail('should throw DAPIClientError'); + } catch (e) { + expect(e).to.be.an.instanceOf(DAPIClientError); + } + }); + }); + + describe('seeds', () => { + let options; + + beforeEach(() => { + options = { + seeds: ['127.0.0.1'], + network: 'local', + }; + }); + + it('should return SimplifiedMasternodeListDAPIAddressProvider based on seeds', async () => { + const result = createDAPIAddressProviderFromOptions(options); + + expect(result).to.be.an.instanceOf(SimplifiedMasternodeListDAPIAddressProvider); + }); + }); + + describe('network', () => { + let options; + + beforeEach(() => { + options = { + network: Object.keys(networkConfigs)[0], + }; + }); + + it('should create address provider from `network` options', async () => { + const result = createDAPIAddressProviderFromOptions(options); + + expect(result).to.be.an.instanceOf(SimplifiedMasternodeListDAPIAddressProvider); + }); + + it('should throw DAPIClientError if there is no config for a specified network', async () => { + options.network = 'unknown'; + + try { + createDAPIAddressProviderFromOptions(options); + + expect.fail('should throw DAPIClientError'); + } catch (e) { + expect(e).to.be.an.instanceOf(DAPIClientError); + } + }); + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/core/broadcastTransactionFactory.spec.js b/packages/js-dapi-client/test/unit/methods/core/broadcastTransactionFactory.spec.js new file mode 100644 index 00000000000..1b7ef038185 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/core/broadcastTransactionFactory.spec.js @@ -0,0 +1,59 @@ +const { + v0: { + CorePromiseClient, + BroadcastTransactionRequest, + BroadcastTransactionResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const broadcastTransactionFactory = require( + '../../../../lib/methods/core/broadcastTransactionFactory', +); + +describe('broadcastTransactionFactory', () => { + let broadcastTransaction; + let grpcTransportMock; + let transaction; + let id; + + beforeEach(function beforeEach() { + grpcTransportMock = { + request: this.sinon.stub(), + }; + + broadcastTransaction = broadcastTransactionFactory( + grpcTransportMock, + ); + + transaction = Buffer.from('transaction'); + id = '4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176'; + }); + + it('should return transaction id', async () => { + const response = new BroadcastTransactionResponse(); + response.setTransactionId(id); + grpcTransportMock.request.resolves(response); + + const options = { + allowHighFees: false, + }; + + const result = await broadcastTransaction( + transaction, + options, + ); + + const request = new BroadcastTransactionRequest(); + request.setTransaction(transaction); + request.setAllowHighFees(options.allowHighFees); + request.setBypassLimits(false); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + CorePromiseClient, + 'broadcastTransaction', + request, + options, + ); + expect(result).to.equal(id); + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/core/generateToAddressFactory.spec.js b/packages/js-dapi-client/test/unit/methods/core/generateToAddressFactory.spec.js new file mode 100644 index 00000000000..c35d5c3c868 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/core/generateToAddressFactory.spec.js @@ -0,0 +1,32 @@ +const generateToAddressFactory = require( + '../../../../lib/methods/core/generateToAddressFactory', +); + +describe('generateToAddressFactory', () => { + let generateToAddress; + let jsonRpcTransport; + + beforeEach(function beforeEach() { + jsonRpcTransport = { + request: this.sinon.stub(), + }; + + generateToAddress = generateToAddressFactory(jsonRpcTransport); + }); + + it('should call generateToAddress method', async () => { + const resultData = 'result'; + const blocksNumber = 10; + const address = 'yTMDce5yEpiPqmgPrPmTj7yAmQPJERUSVy'; + const options = {}; + jsonRpcTransport.request.resolves(resultData); + + const result = await generateToAddress(blocksNumber, address, options); + expect(result).to.equal(resultData); + expect(jsonRpcTransport.request).to.be.calledOnceWithExactly( + 'generateToAddress', + { blocksNumber, address }, + options, + ); + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/core/getBestBlockHashFactory.spec.js b/packages/js-dapi-client/test/unit/methods/core/getBestBlockHashFactory.spec.js new file mode 100644 index 00000000000..28a2a20281e --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/core/getBestBlockHashFactory.spec.js @@ -0,0 +1,31 @@ +const getBestBlockHashFactory = require('../../../../lib/methods/core/getBestBlockHashFactory'); + +describe('getBestBlockHashFactory', () => { + let getBestBlockHash; + let jsonRpcTransport; + let bestBlockHash; + + beforeEach(function beforeEach() { + bestBlockHash = '000000000b0339e07bce8b3186a6a57a3c45d10e16c4bce18ef81b667bc822b2'; + + jsonRpcTransport = { + request: this.sinon.stub().resolves(bestBlockHash), + }; + getBestBlockHash = getBestBlockHashFactory(jsonRpcTransport); + }); + + it('should return best block hash', async () => { + const options = { + timeout: 1000, + }; + + const result = await getBestBlockHash(options); + + expect(result).to.deep.equal(bestBlockHash); + expect(jsonRpcTransport.request).to.be.calledOnceWithExactly( + 'getBestBlockHash', + {}, + options, + ); + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/core/getBlockByHashFactory.spec.js b/packages/js-dapi-client/test/unit/methods/core/getBlockByHashFactory.spec.js new file mode 100644 index 00000000000..d9548cf6b69 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/core/getBlockByHashFactory.spec.js @@ -0,0 +1,73 @@ +const { + v0: { + GetBlockRequest, + GetBlockResponse, + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +const getBlockByHashFactory = require('../../../../lib/methods/core/getBlockByHashFactory'); + +describe('getBlockByHashFactory', () => { + let getBlockByHash; + let grpcTransportMock; + let block; + + beforeEach(function beforeEach() { + block = Buffer.from('block'); + const response = new GetBlockResponse(); + response.setBlock(block); + + grpcTransportMock = { + request: this.sinon.stub().resolves(response), + }; + getBlockByHash = getBlockByHashFactory(grpcTransportMock); + }); + + it('should return block by hash', async () => { + const options = { + timeout: 1000, + }; + + const hash = '4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176'; + + const result = await getBlockByHash(hash, options); + + const request = new GetBlockRequest(); + request.setHash(hash); + + expect(result).to.be.instanceof(Buffer); + expect(result).to.deep.equal(block); + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + CorePromiseClient, + 'getBlock', + request, + options, + ); + }); + + it('should throw unknown error', async () => { + const error = new Error('Unknown found'); + + grpcTransportMock.request.throws(error); + + const hash = '4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176'; + + const request = new GetBlockRequest(); + request.setHash(hash); + + try { + await getBlockByHash(hash); + + expect.fail('should throw unknown error'); + } catch (e) { + expect(e).to.deep.equal(error); + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + CorePromiseClient, + 'getBlock', + request, + {}, + ); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/core/getBlockByHeightFactory.spec.js b/packages/js-dapi-client/test/unit/methods/core/getBlockByHeightFactory.spec.js new file mode 100644 index 00000000000..5a6ad896648 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/core/getBlockByHeightFactory.spec.js @@ -0,0 +1,73 @@ +const { + v0: { + GetBlockRequest, + GetBlockResponse, + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +const getBlockByHeightFactory = require('../../../../lib/methods/core/getBlockByHeightFactory'); + +describe('getBlockByHeightFactory', () => { + let getBlockByHeight; + let grpcTransportMock; + let block; + + beforeEach(function beforeEach() { + block = Buffer.from('block'); + const response = new GetBlockResponse(); + response.setBlock(block); + + grpcTransportMock = { + request: this.sinon.stub().resolves(response), + }; + getBlockByHeight = getBlockByHeightFactory(grpcTransportMock); + }); + + it('should return block by hash', async () => { + const options = { + timeout: 1000, + }; + + const height = 1; + + const result = await getBlockByHeight(height, options); + + const request = new GetBlockRequest(); + request.setHeight(height); + + expect(result).to.be.instanceof(Buffer); + expect(result).to.deep.equal(block); + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + CorePromiseClient, + 'getBlock', + request, + options, + ); + }); + + it('should throw unknown error', async () => { + const error = new Error('Unknown found'); + + grpcTransportMock.request.throws(error); + + const height = 1; + + const request = new GetBlockRequest(); + request.setHeight(height); + + try { + await getBlockByHeight(height); + + expect.fail('should throw unknown error'); + } catch (e) { + expect(e).to.deep.equal(error); + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + CorePromiseClient, + 'getBlock', + request, + {}, + ); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/core/getBlockHashFactory.spec.js b/packages/js-dapi-client/test/unit/methods/core/getBlockHashFactory.spec.js new file mode 100644 index 00000000000..9d14ff6de78 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/core/getBlockHashFactory.spec.js @@ -0,0 +1,33 @@ +const getBlockHashFactory = require('../../../../lib/methods/core/getBlockHashFactory'); + +describe('getBlockHashFactory', () => { + let getBlockHash; + let jsonRpcTransport; + let hash; + + beforeEach(function beforeEach() { + hash = '000000000b0339e07bce8b3186a6a57a3c45d10e16c4bce18ef81b667bc822b2'; + + jsonRpcTransport = { + request: this.sinon.stub().resolves(hash), + }; + getBlockHash = getBlockHashFactory(jsonRpcTransport); + }); + + it('should return best block hash', async () => { + const options = { + timeout: 1000, + }; + + const height = 1; + + const result = await getBlockHash(height, options); + + expect(result).to.deep.equal(hash); + expect(jsonRpcTransport.request).to.be.calledOnceWithExactly( + 'getBlockHash', + { height }, + options, + ); + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/core/getMnListDiffFactory.spec.js b/packages/js-dapi-client/test/unit/methods/core/getMnListDiffFactory.spec.js new file mode 100644 index 00000000000..9b7e996aa53 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/core/getMnListDiffFactory.spec.js @@ -0,0 +1,35 @@ +const getMnListDiffFactory = require('../../../../lib/methods/core/getMnListDiffFactory'); + +const getMNListDiffsFixture = require('../../../../lib/test/fixtures/getMNListDiffsFixture'); + +describe('getMnListDiff', () => { + let getMnListDiff; + let jsonRpcTransportMock; + let mnListDiff; + let baseBlockHash; + let blockHash; + + beforeEach(function beforeEach() { + baseBlockHash = '0000047d24635e347be3aaaeb66c26be94901a2f962feccd4f95090191f208c1'; + blockHash = '000000000b0339e07bce8b3186a6a57a3c45d10e16c4bce18ef81b667bc822b2'; + mnListDiff = getMNListDiffsFixture(); + + jsonRpcTransportMock = { + request: this.sinon.stub().resolves(mnListDiff), + }; + getMnListDiff = getMnListDiffFactory(jsonRpcTransportMock); + }); + + it('should return deterministic masternodelist diff', async () => { + const options = {}; + + const result = await getMnListDiff(baseBlockHash, blockHash, options); + + expect(result).to.deep.equal(mnListDiff); + expect(jsonRpcTransportMock.request).to.be.calledOnceWithExactly( + 'getMnListDiff', + { baseBlockHash, blockHash }, + options, + ); + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/core/getStatusFactory.spec.js b/packages/js-dapi-client/test/unit/methods/core/getStatusFactory.spec.js new file mode 100644 index 00000000000..b5376020dee --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/core/getStatusFactory.spec.js @@ -0,0 +1,69 @@ +const { + v0: { + GetStatusRequest, + GetStatusResponse, + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +const getStatusFactory = require('../../../../lib/methods/core/getStatusFactory'); + +describe('getStatusFactory', () => { + let getStatus; + let grpcTransportMock; + + beforeEach(function beforeEach() { + grpcTransportMock = { + request: this.sinon.stub(), + }; + getStatus = getStatusFactory(grpcTransportMock); + }); + + it('should return status', async () => { + const response = new GetStatusResponse(); + + response.setStatus(GetStatusResponse.Status.READY); + + const masternode = new GetStatusResponse.Masternode(); + + masternode.setStatus(GetStatusResponse.Masternode.Status.READY); + + const chain = new GetStatusResponse.Chain(); + chain.setBestBlockHash(Buffer.from('bestBlockHash')); + + response.setMasternode(masternode); + response.setChain(chain); + + grpcTransportMock.request.resolves(response); + + const options = { + timeout: 1000, + }; + + const result = await getStatus( + options, + ); + + const request = new GetStatusRequest(); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + CorePromiseClient, + 'getStatus', + request, + options, + ); + + const expectedResult = { + ...response.toObject(), + status: 'READY', + masternode: { + ...response.getMasternode().toObject(), + status: 'READY', + }, + }; + + expectedResult.chain.bestBlockHash = Buffer.from(expectedResult.chain.bestBlockHash, 'base64'); + + expect(result).to.deep.equal(expectedResult); + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/core/getTransaction/GetTransactionResponse.spec.js b/packages/js-dapi-client/test/unit/methods/core/getTransaction/GetTransactionResponse.spec.js new file mode 100644 index 00000000000..294e9a6d253 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/core/getTransaction/GetTransactionResponse.spec.js @@ -0,0 +1,94 @@ +const { + v0: { + GetTransactionResponse: ProtoGetTransactionResponse, + }, +} = require('@dashevo/dapi-grpc'); +const GetTransactionResponse = require('../../../../../lib/methods/core/getTransaction/GetTransactionResponse'); +const InvalidResponseError = require('../../../../../lib/methods/platform/response/errors/InvalidResponseError'); + +describe('GetTransactionResponse', () => { + let getTransactionResponse; + let response; + let proto; + + beforeEach(() => { + response = { + transaction: Buffer.from('transaction'), + blockHash: Buffer.from('blockHash'), + height: 10, + confirmations: 42, + instantLocked: true, + chainLocked: false, + }; + + proto = new ProtoGetTransactionResponse(); + proto.setTransaction(response.transaction); + proto.setBlockHash(response.blockHash); + proto.setHeight(response.height); + proto.setConfirmations(response.confirmations); + proto.setIsChainLocked(response.isChainLocked); + proto.setIsInstantLocked(response.isInstantLocked); + + getTransactionResponse = new GetTransactionResponse(response); + }); + + it('should return transaction', () => { + const transaction = getTransactionResponse.getTransaction(); + + expect(transaction).to.deep.equal(response.transaction); + }); + + it('should return block hash', () => { + const blockHash = getTransactionResponse.getBlockHash(); + + expect(blockHash).to.deep.equal(response.blockHash); + }); + + it('should return height', () => { + const height = getTransactionResponse.getHeight(); + + expect(height).to.deep.equal(response.height); + }); + + it('should return confirmations', () => { + const confirmations = getTransactionResponse.getConfirmations(); + + expect(confirmations).to.deep.equal(response.confirmations); + }); + + it('should return is transaction instantLocked', () => { + const isInstantLocked = getTransactionResponse.isInstantLocked(); + + expect(isInstantLocked).to.deep.equal(response.isInstantLocked); + }); + + it('should return is transaction chainLocked', () => { + const isChainLocked = getTransactionResponse.isChainLocked(); + + expect(isChainLocked).to.equal(response.isChainLocked); + }); + + it('should create an instance from proto', () => { + const instance = GetTransactionResponse.createFromProto(proto); + + expect(instance).to.be.an.instanceOf(GetTransactionResponse); + expect(instance.transaction).to.deep.equal(Buffer.from(proto.getTransaction())); + expect(instance.blockHash).to.deep.equal(Buffer.from(proto.getBlockHash())); + expect(instance.height).to.deep.equal(proto.getHeight()); + expect(instance.confirmations).to.deep.equal(proto.getConfirmations()); + expect(instance.instantLocked).to.deep.equal(proto.getIsInstantLocked()); + expect(instance.chainLocked).to.deep.equal(proto.getIsChainLocked()); + }); + + it('should throw InvalidResponseError if Transaction is not defined', () => { + proto.setTransaction(undefined); + + try { + GetTransactionResponse.createFromProto(proto); + + expect.fail('should throw InvalidResponseError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidResponseError); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/core/getTransaction/getTransactionFactory.spec.js b/packages/js-dapi-client/test/unit/methods/core/getTransaction/getTransactionFactory.spec.js new file mode 100644 index 00000000000..cbbe9312186 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/core/getTransaction/getTransactionFactory.spec.js @@ -0,0 +1,95 @@ +const { + v0: { + GetTransactionRequest, + GetTransactionResponse: ProtoGetTransactionResponse, + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +const getTransactionFactory = require('../../../../../lib/methods/core/getTransaction/getTransactionFactory'); +const GetTransactionResponse = require('../../../../../lib/methods/core/getTransaction/GetTransactionResponse'); + +describe('getTransactionFactory', () => { + let getTransaction; + let grpcTransportMock; + let transaction; + let blockHash; + let height; + let confirmations; + let isChainLocked; + let isInstantLocked; + + beforeEach(function beforeEach() { + transaction = Buffer.from('transaction'); + blockHash = Buffer.from('blockHash'); + height = 42; + confirmations = 3; + isChainLocked = true; + isInstantLocked = false; + + const response = new ProtoGetTransactionResponse(); + response.setTransaction(transaction); + response.setBlockHash(blockHash); + response.setHeight(height); + response.setConfirmations(confirmations); + response.setIsChainLocked(isChainLocked); + response.setIsInstantLocked(isInstantLocked); + + grpcTransportMock = { + request: this.sinon.stub().resolves(response), + }; + getTransaction = getTransactionFactory(grpcTransportMock); + }); + + it('should return transaction', async () => { + const options = { + timeout: 1000, + }; + + const id = '4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176'; + + const result = await getTransaction(id, options); + + const request = new GetTransactionRequest(); + request.setId(id); + + expect(result).to.be.instanceof(GetTransactionResponse); + expect(result.getTransaction()).to.deep.equal(transaction); + expect(result.getBlockHash()).to.deep.equal(blockHash); + expect(result.getConfirmations()).to.equal(confirmations); + expect(result.getHeight()).to.equal(height); + expect(result.isInstantLocked()).to.equal(isInstantLocked); + expect(result.isChainLocked()).to.equal(isChainLocked); + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + CorePromiseClient, + 'getTransaction', + request, + options, + ); + }); + + it('should throw unknown error', async () => { + const error = new Error('Unknown found'); + + grpcTransportMock.request.throws(error); + + const id = '4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176'; + + const request = new GetTransactionRequest(); + request.setId(id); + + try { + await getTransaction(id); + + expect.fail('should throw unknown error'); + } catch (e) { + expect(e).to.deep.equal(error); + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + CorePromiseClient, + 'getTransaction', + request, + {}, + ); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/core/subscribeToBlockHeadersWithChainLocksFactory.spec.js b/packages/js-dapi-client/test/unit/methods/core/subscribeToBlockHeadersWithChainLocksFactory.spec.js new file mode 100644 index 00000000000..32ce15e4a45 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/core/subscribeToBlockHeadersWithChainLocksFactory.spec.js @@ -0,0 +1,65 @@ +const { + v0: { + CorePromiseClient, + BlockHeadersWithChainLocksRequest, + }, +} = require('@dashevo/dapi-grpc'); +const { EventEmitter } = require('events'); + +const subscribeToBlockHeadersWithChainLocksFactory = require('../../../../lib/methods/core/subscribeToBlockHeadersWithChainLocksFactory'); + +const DAPIClientError = require('../../../../lib/errors/DAPIClientError'); + +describe('subscribeToBlockHeadersWithChainLocks', () => { + let subscribeToBlockHeadersWithChainLocks; + let grpcTransportMock; + let options; + let stream; + + beforeEach(function beforeEach() { + options = { + fromBlockHeight: 1, + count: 5, + fromBlockHash: '000000000b0339e07bce8b3186a6a57a3c45d10e16c4bce18ef81b667bc822b2', + timeout: 150000, + }; + + stream = new EventEmitter(); + grpcTransportMock = { + request: this.sinon.stub().resolves(stream), + }; + + // eslint-disable-next-line operator-linebreak + subscribeToBlockHeadersWithChainLocks = + subscribeToBlockHeadersWithChainLocksFactory(grpcTransportMock); + }); + + it('should return a stream', async () => { + try { + await subscribeToBlockHeadersWithChainLocks( + { ...options, fromBlockHeight: 0 }, + ); + } catch (e) { + expect(e).to.be.an.instanceOf(DAPIClientError); + } + + const actualStream = await subscribeToBlockHeadersWithChainLocks( + { ...options, fromBlockHeight: 1 }, + ); + + const request = new BlockHeadersWithChainLocksRequest(); + + request.setFromBlockHeight(1); + request.setFromBlockHash(Buffer.from(options.fromBlockHash, 'hex')); + request.setCount(options.count); + + expect(grpcTransportMock.request).to.be.calledWith( + CorePromiseClient, + 'subscribeToBlockHeadersWithChainLocks', + request, + options, + ); + + expect(actualStream).to.be.equal(stream); + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/core/subscribeToTransactionsWithProofsFactory.spec.js b/packages/js-dapi-client/test/unit/methods/core/subscribeToTransactionsWithProofsFactory.spec.js new file mode 100644 index 00000000000..21584c6c82b --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/core/subscribeToTransactionsWithProofsFactory.spec.js @@ -0,0 +1,119 @@ +const BloomFilter = require('@dashevo/dashcore-lib/lib/bloomfilter'); + +const { + v0: { + BloomFilter: BloomFilterMessage, + CorePromiseClient, + TransactionsWithProofsRequest, + }, +} = require('@dashevo/dapi-grpc'); + +const { EventEmitter } = require('events'); + +const subscribeToTransactionsWithProofsFactory = require('../../../../lib/methods/core/subscribeToTransactionsWithProofsFactory'); + +const DAPIClientError = require('../../../../lib/errors/DAPIClientError'); + +describe('subscribeToTransactionsWithProofsFactory', () => { + let subscribeToTransactionsWithProofs; + let grpcTransportMock; + let options; + let stream; + + beforeEach(function beforeEach() { + options = { + fromBlockHeight: 1, + count: 5, + fromBlockHash: '000000000b0339e07bce8b3186a6a57a3c45d10e16c4bce18ef81b667bc822b2', + timeout: 150000, + }; + + stream = new EventEmitter(); + grpcTransportMock = { + request: this.sinon.stub().resolves(stream), + }; + subscribeToTransactionsWithProofs = subscribeToTransactionsWithProofsFactory(grpcTransportMock); + }); + + it('should return a stream', async () => { + const bloomFilter = BloomFilter.create(1, 0.001); + + const actualStream = await subscribeToTransactionsWithProofs( + bloomFilter, + options, + ); + + const bloomFilterMessage = new BloomFilterMessage(); + + bloomFilterMessage.setVData(new Uint8Array(bloomFilter.vData)); + bloomFilterMessage.setNHashFuncs(bloomFilter.nHashFuncs); + bloomFilterMessage.setNTweak(bloomFilter.nTweak); + bloomFilterMessage.setNFlags(bloomFilter.nFlags); + + const request = new TransactionsWithProofsRequest(); + request.setBloomFilter(bloomFilterMessage); + request.setFromBlockHeight(options.fromBlockHeight); + request.setCount(options.count); + request.setFromBlockHash(Buffer.from(options.fromBlockHash, 'hex')); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + CorePromiseClient, + 'subscribeToTransactionsWithProofs', + request, + options, + ); + + expect(actualStream).to.be.equal(stream); + }); + + it('should apply default options', async () => { + const bloomFilter = BloomFilter.create(1, 0.001); + + const actualStream = await subscribeToTransactionsWithProofs( + bloomFilter, + ); + + const bloomFilterMessage = new BloomFilterMessage(); + + bloomFilterMessage.setVData(new Uint8Array(bloomFilter.vData)); + bloomFilterMessage.setNHashFuncs(bloomFilter.nHashFuncs); + bloomFilterMessage.setNTweak(bloomFilter.nTweak); + bloomFilterMessage.setNFlags(bloomFilter.nFlags); + + const request = new TransactionsWithProofsRequest(); + request.setBloomFilter(bloomFilterMessage); + request.setCount(0); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + CorePromiseClient, + 'subscribeToTransactionsWithProofs', + request, + { + count: 0, + timeout: undefined, + }, + ); + + expect(actualStream).to.be.equal(stream); + }); + + it('should throw error if `fromBlockHeight` is set to 0', async () => { + options = { + fromBlockHeight: 0, + count: 5, + fromBlockHash: '000000000b0339e07bce8b3186a6a57a3c45d10e16c4bce18ef81b667bc822b2', + timeout: 150000, + }; + + const bloomFilter = BloomFilter.create(1, 0.001); + + try { + await subscribeToTransactionsWithProofs( + bloomFilter, options, + ); + } catch (e) { + expect(e).to.be.an.instanceOf(DAPIClientError); + expect(e.message).to.equal('Invalid argument: minimum value for `fromBlockHeight` is 1'); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/broadcaseStateTransition/broadcastStateTransitionFactory.spec.js b/packages/js-dapi-client/test/unit/methods/platform/broadcaseStateTransition/broadcastStateTransitionFactory.spec.js new file mode 100644 index 00000000000..971d8463b4e --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/broadcaseStateTransition/broadcastStateTransitionFactory.spec.js @@ -0,0 +1,58 @@ +const { + v0: { + BroadcastStateTransitionRequest, + PlatformPromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +const DashPlatformProtocol = require('@dashevo/dpp'); + +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); + +const broadcastStateTransitionFactory = require('../../../../../lib/methods/platform/broadcastStateTransition/broadcastStateTransitionFactory'); +const BroadcastStateTransitionResponse = require('../../../../../lib/methods/platform/broadcastStateTransition/BroadcastStateTransitionResponse'); + +describe('broadcastStateTransitionFactory', () => { + let grpcTransportMock; + let broadcastStateTransition; + let options; + let stateTransitionFixture; + let response; + + beforeEach(async function beforeEach() { + response = new BroadcastStateTransitionResponse(); + + grpcTransportMock = { + request: this.sinon.stub().resolves(response), + }; + + const dataContractFixture = getDataContractFixture(); + const dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + stateTransitionFixture = dpp.dataContract.createDataContractCreateTransition( + dataContractFixture, + ); + + options = { + timeout: 1000, + }; + + broadcastStateTransition = broadcastStateTransitionFactory(grpcTransportMock); + }); + + it('should broadcast state transition', async () => { + const result = await broadcastStateTransition(stateTransitionFixture, options); + + const request = new BroadcastStateTransitionRequest(); + request.setStateTransition(stateTransitionFixture); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'broadcastStateTransition', + request, + options, + ); + expect(result).to.be.an.instanceOf(BroadcastStateTransitionResponse); + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/getConsensusParams/ConsensusParamsBlock.spec.js b/packages/js-dapi-client/test/unit/methods/platform/getConsensusParams/ConsensusParamsBlock.spec.js new file mode 100644 index 00000000000..245c0bceaac --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/getConsensusParams/ConsensusParamsBlock.spec.js @@ -0,0 +1,32 @@ +const ConsensusParamsBlock = require('../../../../../lib/methods/platform/getConsensusParams/ConsensusParamsBlock'); + +describe('ConsensusParamsBlock', () => { + let consensusParamsBlock; + let block; + + beforeEach(() => { + block = { + timeIotaMs: '1000', + maxGas: '-1', + maxBytes: '22020103', + }; + + consensusParamsBlock = new ConsensusParamsBlock( + block.maxBytes, + block.maxGas, + block.timeIotaMs, + ); + }); + + it('should return timeIotaMs', () => { + expect(consensusParamsBlock.getTimeIotaMs()).to.equal(block.timeIotaMs); + }); + + it('should return maxGas', () => { + expect(consensusParamsBlock.getMaxGas()).to.equal(block.maxGas); + }); + + it('should return maxBytes', () => { + expect(consensusParamsBlock.getMaxBytes()).to.equal(block.maxBytes); + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/getConsensusParams/ConsensusParamsEvidence.spec.js b/packages/js-dapi-client/test/unit/methods/platform/getConsensusParams/ConsensusParamsEvidence.spec.js new file mode 100644 index 00000000000..70c6074dc4c --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/getConsensusParams/ConsensusParamsEvidence.spec.js @@ -0,0 +1,32 @@ +const ConsensusParamsEvidence = require('../../../../../lib/methods/platform/getConsensusParams/ConsensusParamsEvidence'); + +describe('ConsensusParamsEvidence', () => { + let consensusParamsEvidence; + let evidence; + + beforeEach(() => { + evidence = { + maxAgeNumBlocks: '100007', + maxAgeDuration: '172807000000007', + maxBytes: '1048583', + }; + + consensusParamsEvidence = new ConsensusParamsEvidence( + evidence.maxAgeNumBlocks, + evidence.maxAgeDuration, + evidence.maxBytes, + ); + }); + + it('should return maxAgeNumBlocks', () => { + expect(consensusParamsEvidence.getMaxAgeNumBlocks()).to.equal(evidence.maxAgeNumBlocks); + }); + + it('should return maxAgeDuration', () => { + expect(consensusParamsEvidence.getMaxAgeDuration()).to.equal(evidence.maxAgeDuration); + }); + + it('should return maxBytes', () => { + expect(consensusParamsEvidence.getMaxBytes()).to.equal(evidence.maxBytes); + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/getConsensusParams/getConsensusParamsFactory.spec.js b/packages/js-dapi-client/test/unit/methods/platform/getConsensusParams/getConsensusParamsFactory.spec.js new file mode 100644 index 00000000000..3a95cf906c8 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/getConsensusParams/getConsensusParamsFactory.spec.js @@ -0,0 +1,154 @@ +const { + v0: { + PlatformPromiseClient, + GetConsensusParamsResponse, + GetConsensusParamsRequest, + ConsensusParamsBlock, + ConsensusParamsEvidence, + }, +} = require('@dashevo/dapi-grpc'); +const getConsensusParamsFactory = require('../../../../../lib/methods/platform/getConsensusParams/getConsensusParamsFactory'); +const InvalidResponseError = require('../../../../../lib/methods/platform/response/errors/InvalidResponseError'); + +describe('getConsensusParams', () => { + let getConsensusParams; + let grpcTransportMock; + let response; + let consensusParamsFixture; + + beforeEach(function beforeEach() { + consensusParamsFixture = { + block: { + timeIotaMs: 1000, + maxGas: -1, + maxBytes: 22020103, + }, + evidence: { + maxAgeNumBlocks: 100007, + maxAgeDuration: 172807000000007, + maxBytes: 1048583, + }, + }; + + const block = new ConsensusParamsBlock(); + block.setMaxBytes(consensusParamsFixture.block.maxBytes); + block.setMaxGas(consensusParamsFixture.block.maxGas); + block.setTimeIotaMs(consensusParamsFixture.block.timeIotaMs); + + const evidence = new ConsensusParamsEvidence(); + evidence.setMaxAgeNumBlocks(consensusParamsFixture.evidence.maxAgeNumBlocks); + evidence.setMaxAgeDuration(consensusParamsFixture.evidence.maxAgeDuration); + evidence.setMaxBytes(consensusParamsFixture.evidence.maxBytes); + + response = new GetConsensusParamsResponse(); + response.setBlock(block); + response.setEvidence(evidence); + + grpcTransportMock = { + request: this.sinon.stub().resolves(response), + }; + + getConsensusParams = getConsensusParamsFactory(grpcTransportMock); + }); + + it('should return consensus params', async () => { + const result = await getConsensusParams(); + const options = {}; + + const request = new GetConsensusParamsRequest(); + request.setProve(!!options.prove); + + expect(grpcTransportMock.request.getCall(0).args).to.have.deep.members([ + PlatformPromiseClient, + 'getConsensusParams', + request, + options, + ]); + + expect(result.getBlock()).to.deep.equal(consensusParamsFixture.block); + expect(result.getEvidence()).to.deep.equal(consensusParamsFixture.evidence); + }); + + it('should return consensus params for height', async () => { + const height = 42; + const result = await getConsensusParams(height); + + const options = { }; + + const request = new GetConsensusParamsRequest(); + request.setProve(!!options.prove); + request.setHeight(height); + + expect(grpcTransportMock.request.getCall(0).args).to.have.deep.members([ + PlatformPromiseClient, + 'getConsensusParams', + request, + options, + ]); + + expect(result.getBlock()).to.deep.equal(consensusParamsFixture.block); + expect(result.getEvidence()).to.deep.equal(consensusParamsFixture.evidence); + }); + + it('should return consensus params with proofs', async () => { + const options = { prove: true }; + + const result = await getConsensusParams(undefined, options); + + const request = new GetConsensusParamsRequest(); + request.setProve(!!options.prove); + + expect(grpcTransportMock.request.getCall(0).args).to.have.deep.members([ + PlatformPromiseClient, + 'getConsensusParams', + request, + options, + ]); + + expect(result.getBlock()).to.deep.equal(consensusParamsFixture.block); + expect(result.getEvidence()).to.deep.equal(consensusParamsFixture.evidence); + }); + + it('should throw InvalidResponseError', async () => { + const options = {}; + const error = new InvalidResponseError('Unknown error'); + + grpcTransportMock.request.throws(error); + + const request = new GetConsensusParamsRequest(); + request.setProve(!!options.prove); + + try { + await getConsensusParams(); + + expect.fail('should throw unknown error'); + } catch (e) { + expect(e).to.deep.equal(error); + expect(grpcTransportMock.request).to.be.calledThrice(); + } + }); + + it('should throw unknown error', async () => { + const options = {}; + const error = new Error('Unknown found'); + + grpcTransportMock.request.throws(error); + + const request = new GetConsensusParamsRequest(); + request.setProve(!!options.prove); + + try { + await getConsensusParams(); + + expect.fail('should throw unknown error'); + } catch (e) { + expect(e).to.deep.equal(error); + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'getConsensusParams', + request, + options, + ); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/getConsensusParams/getConsensusParamsResponse.spec.js b/packages/js-dapi-client/test/unit/methods/platform/getConsensusParams/getConsensusParamsResponse.spec.js new file mode 100644 index 00000000000..779a760f183 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/getConsensusParams/getConsensusParamsResponse.spec.js @@ -0,0 +1,115 @@ +const { + v0: { + GetConsensusParamsResponse: GetConsensusParamsResponseProto, + ConsensusParamsBlock: ConsensusParamsBlockProto, + ConsensusParamsEvidence: ConsensusParamsEvidenceProto, + }, +} = require('@dashevo/dapi-grpc'); +const GetConsensusParamsResponse = require('../../../../../lib/methods/platform/getConsensusParams/getConsensusParamsResponse'); +const InvalidResponseError = require('../../../../../lib/methods/platform/response/errors/InvalidResponseError'); +const ConsensusParamsBlock = require('../../../../../lib/methods/platform/getConsensusParams/ConsensusParamsBlock'); +const ConsensusParamsEvidence = require('../../../../../lib/methods/platform/getConsensusParams/ConsensusParamsEvidence'); + +describe('getConsensusParamsResponse', () => { + let getConsensusParamsResponse; + let consensusParamsFixture; + + beforeEach(() => { + consensusParamsFixture = { + block: { + timeIotaMs: 1000, + maxGas: -1, + maxBytes: 22020103, + }, + evidence: { + maxAgeNumBlocks: 100007, + maxAgeDuration: 172807000000007, + maxBytes: 1048583, + }, + }; + + const block = new ConsensusParamsBlock( + consensusParamsFixture.block.maxBytes, + consensusParamsFixture.block.maxGas, + consensusParamsFixture.block.timeIotaMs, + ); + + const evidence = new ConsensusParamsEvidence( + consensusParamsFixture.evidence.maxAgeNumBlocks, + consensusParamsFixture.evidence.maxAgeDuration, + consensusParamsFixture.evidence.maxBytes, + ); + + getConsensusParamsResponse = new GetConsensusParamsResponse( + block, + evidence, + ); + }); + + it('should return block', () => { + const block = getConsensusParamsResponse.getBlock(); + + expect(block).to.be.an.instanceOf(ConsensusParamsBlock); + expect(block.getMaxBytes()).to.deep.equal(consensusParamsFixture.block.maxBytes); + expect(block.getMaxGas()).to.deep.equal(consensusParamsFixture.block.maxGas); + expect(block.getTimeIotaMs()).to.deep.equal(consensusParamsFixture.block.timeIotaMs); + }); + + it('should return evidence', () => { + const evidence = getConsensusParamsResponse.getEvidence(); + + expect(evidence).to.be.an.instanceOf(ConsensusParamsEvidence); + expect(evidence.getMaxAgeNumBlocks()) + .to.deep.equal(consensusParamsFixture.evidence.maxAgeNumBlocks); + expect(evidence.getMaxAgeDuration()) + .to.deep.equal(consensusParamsFixture.evidence.maxAgeDuration); + expect(evidence.getMaxBytes()) + .to.deep.equal(consensusParamsFixture.evidence.maxBytes); + }); + + it('should create an instance from proto', () => { + const block = new ConsensusParamsBlockProto(); + block.setMaxBytes(consensusParamsFixture.block.maxBytes); + block.setMaxGas(consensusParamsFixture.block.maxGas); + block.setTimeIotaMs(consensusParamsFixture.block.timeIotaMs); + + const evidence = new ConsensusParamsEvidenceProto(); + evidence.setMaxAgeNumBlocks(consensusParamsFixture.evidence.maxAgeNumBlocks); + evidence.setMaxAgeDuration(consensusParamsFixture.evidence.maxAgeDuration); + evidence.setMaxBytes(consensusParamsFixture.evidence.maxBytes); + + const proto = new GetConsensusParamsResponseProto(); + proto.setBlock(block); + proto.setEvidence(evidence); + + getConsensusParamsResponse = GetConsensusParamsResponse.createFromProto(proto); + + expect(getConsensusParamsResponse.getBlock()).to.be.an.instanceOf(ConsensusParamsBlock); + expect(getConsensusParamsResponse.getBlock().getMaxBytes()) + .to.deep.equal(consensusParamsFixture.block.maxBytes); + expect(getConsensusParamsResponse.getBlock().getMaxGas()) + .to.deep.equal(consensusParamsFixture.block.maxGas); + expect(getConsensusParamsResponse.getBlock().getTimeIotaMs()) + .to.deep.equal(consensusParamsFixture.block.timeIotaMs); + + expect(getConsensusParamsResponse.getEvidence()).to.be.an.instanceOf(ConsensusParamsEvidence); + expect(getConsensusParamsResponse.getEvidence().getMaxAgeNumBlocks()) + .to.deep.equal(consensusParamsFixture.evidence.maxAgeNumBlocks); + expect(getConsensusParamsResponse.getEvidence().getMaxAgeDuration()) + .to.deep.equal(consensusParamsFixture.evidence.maxAgeDuration); + expect(getConsensusParamsResponse.getEvidence().getMaxBytes()) + .to.deep.equal(consensusParamsFixture.evidence.maxBytes); + }); + + it('should return InvalidResponseError if consensus params are not defined', () => { + const proto = new GetConsensusParamsResponseProto(); + + try { + getConsensusParamsResponse = GetConsensusParamsResponse.createFromProto(proto); + + expect.fail('should throw InvalidResponseError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidResponseError); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/getDataContract/GetDataContractResponse.spec.js b/packages/js-dapi-client/test/unit/methods/platform/getDataContract/GetDataContractResponse.spec.js new file mode 100644 index 00000000000..58e21b21243 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/getDataContract/GetDataContractResponse.spec.js @@ -0,0 +1,149 @@ +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const { + v0: { + GetDataContractResponse, + ResponseMetadata, + Proof: ProofResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const GetDataContractResponseClass = require('../../../../../lib/methods/platform/getDataContract/GetDataContractResponse'); +const getMetadataFixture = require('../../../../../lib/test/fixtures/getMetadataFixture'); +const getProofFixture = require('../../../../../lib/test/fixtures/getProofFixture'); +const InvalidResponseError = require('../../../../../lib/methods/platform/response/errors/InvalidResponseError'); +const Proof = require('../../../../../lib/methods/platform/response/Proof'); +const Metadata = require('../../../../../lib/methods/platform/response/Metadata'); + +describe('GetDataContractResponse', () => { + let getDataContractResponse; + let metadataFixture; + let dataContractFixture; + let proofFixture; + + beforeEach(() => { + metadataFixture = getMetadataFixture(); + dataContractFixture = getDataContractFixture(); + proofFixture = getProofFixture(); + + getDataContractResponse = new GetDataContractResponseClass( + dataContractFixture.toBuffer(), + new Metadata(metadataFixture), + ); + }); + + it('should return DataContract', () => { + const dataContract = getDataContractResponse.getDataContract(); + const proof = getDataContractResponse.getProof(); + + expect(dataContract).to.deep.equal(dataContractFixture.toBuffer()); + expect(proof).to.equal(undefined); + }); + + it('should return proof', () => { + getDataContractResponse = new GetDataContractResponseClass( + Buffer.alloc(0), + new Metadata(metadataFixture), + new Proof(proofFixture), + ); + + const dataContract = getDataContractResponse.getDataContract(); + const proof = getDataContractResponse.getProof(); + + expect(dataContract).to.deep.equal(Buffer.alloc(0)); + expect(proof).to.be.an.instanceOf(Proof); + expect(proof.getMerkleProof()).to.deep.equal(proofFixture.merkleProof); + expect(proof.getSignatureLLMQHash()).to.deep.equal(proofFixture.signatureLLMQHash); + expect(proof.getSignature()).to.deep.equal(proofFixture.signature); + }); + + it('should create an instance from proto', () => { + const proto = new GetDataContractResponse(); + proto.setDataContract(dataContractFixture.toBuffer()); + + const metadata = new ResponseMetadata(); + metadata.setHeight(metadataFixture.height); + metadata.setCoreChainLockedHeight(metadataFixture.coreChainLockedHeight); + + proto.setMetadata(metadata); + + getDataContractResponse = GetDataContractResponseClass.createFromProto(proto); + expect(getDataContractResponse).to.be.an.instanceOf(GetDataContractResponseClass); + expect(getDataContractResponse.getDataContract()).to.deep.equal(dataContractFixture.toBuffer()); + + expect(getDataContractResponse.getMetadata()) + .to.be.an.instanceOf(Metadata); + expect(getDataContractResponse.getMetadata().getHeight()) + .to.equal(metadataFixture.height); + expect(getDataContractResponse.getMetadata().getCoreChainLockedHeight()) + .to.equal(metadataFixture.coreChainLockedHeight); + + expect(getDataContractResponse.getProof()).to.equal(undefined); + }); + + it('should create an instance with proof from proto', () => { + const proofProto = new ProofResponse(); + + proofProto.setSignatureLlmqHash(proofFixture.signatureLLMQHash); + proofProto.setSignature(proofFixture.signature); + proofProto.setMerkleProof(proofFixture.merkleProof); + + const proto = new GetDataContractResponse(); + + proto.setDataContract(undefined); + proto.setProof(proofProto); + + const metadata = new ResponseMetadata(); + metadata.setHeight(metadataFixture.height); + metadata.setCoreChainLockedHeight(metadataFixture.coreChainLockedHeight); + + proto.setMetadata(metadata); + + getDataContractResponse = GetDataContractResponseClass.createFromProto(proto); + expect(getDataContractResponse).to.be.an.instanceOf(GetDataContractResponseClass); + expect(getDataContractResponse.getDataContract()).to.deep.equal(Buffer.alloc(0)); + + expect(getDataContractResponse.getMetadata()) + .to.be.an.instanceOf(Metadata); + expect(getDataContractResponse.getMetadata().getHeight()) + .to.equal(metadataFixture.height); + expect(getDataContractResponse.getMetadata().getCoreChainLockedHeight()) + .to.equal(metadataFixture.coreChainLockedHeight); + + const proof = getDataContractResponse.getProof(); + + expect(proof).to.be.an.instanceOf(Proof); + expect(proof.getMerkleProof()).to.deep.equal(proofFixture.merkleProof); + expect(proof.getSignatureLLMQHash()).to.deep.equal(proofFixture.signatureLLMQHash); + expect(proof.getSignature()).to.deep.equal(proofFixture.signature); + }); + + it('should throw InvalidResponseError if Metadata is not defined', () => { + const proto = new GetDataContractResponse(); + proto.setDataContract(dataContractFixture.toBuffer()); + + try { + getDataContractResponse = GetDataContractResponseClass.createFromProto(proto); + + expect.fail('should throw InvalidResponseError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidResponseError); + } + }); + + it('should throw InvalidResponseError if DataContract is not defined', () => { + const proto = new GetDataContractResponse(); + const metadata = new ResponseMetadata(); + metadata.setHeight(metadataFixture.height); + metadata.setCoreChainLockedHeight(metadataFixture.coreChainLockedHeight); + + proto.setMetadata(metadata); + + try { + getDataContractResponse = GetDataContractResponseClass.createFromProto(proto); + + expect.fail('should throw InvalidResponseError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidResponseError); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/getDataContract/getDataContractFactory.spec.js b/packages/js-dapi-client/test/unit/methods/platform/getDataContract/getDataContractFactory.spec.js new file mode 100644 index 00000000000..be5f09dd14b --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/getDataContract/getDataContractFactory.spec.js @@ -0,0 +1,138 @@ +const { + v0: { + PlatformPromiseClient, + GetDataContractRequest, + GetDataContractResponse, + ResponseMetadata, + Proof, + }, +} = require('@dashevo/dapi-grpc'); + +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); + +const getDataContractFactory = require('../../../../../lib/methods/platform/getDataContract/getDataContractFactory'); +const getMetadataFixture = require('../../../../../lib/test/fixtures/getMetadataFixture'); +const getProofFixture = require('../../../../../lib/test/fixtures/getProofFixture'); +const ProofClass = require('../../../../../lib/methods/platform/response/Proof'); + +describe('getDataContractFactory', () => { + let grpcTransportMock; + let getDataContract; + let options; + let response; + let dataContractFixture; + let metadataFixture; + let proofFixture; + let proof; + + beforeEach(function beforeEach() { + dataContractFixture = getDataContractFixture(); + + response = new GetDataContractResponse(); + response.setDataContract(dataContractFixture.toBuffer()); + + metadataFixture = getMetadataFixture(); + proofFixture = getProofFixture(); + + const metadata = new ResponseMetadata(); + metadata.setHeight(metadataFixture.height); + metadata.setCoreChainLockedHeight(metadataFixture.coreChainLockedHeight); + + response.setMetadata(metadata); + + grpcTransportMock = { + request: this.sinon.stub().resolves(response), + }; + + options = { + timeout: 1000, + }; + + getDataContract = getDataContractFactory(grpcTransportMock); + + proof = new Proof(); + + proof.setSignatureLlmqHash(proofFixture.signatureLLMQHash); + proof.setSignature(proofFixture.signature); + proof.setMerkleProof(proofFixture.merkleProof); + }); + + it('should return data contract', async () => { + const contractId = dataContractFixture.getId(); + const result = await getDataContract(contractId, options); + + const request = new GetDataContractRequest(); + request.setId(contractId); + request.setProve(false); + + expect(grpcTransportMock.request.getCall(0).args).to.have.deep.members([ + PlatformPromiseClient, + 'getDataContract', + request, + options, + ]); + expect(result.getDataContract()).to.deep.equal(dataContractFixture.toBuffer()); + expect(result.getProof()).to.equal(undefined); + expect(result.getMetadata()).to.deep.equal(metadataFixture); + expect(result.getMetadata().getHeight()).to.equal(metadataFixture.height); + expect(result.getMetadata().getCoreChainLockedHeight()).to.equal( + metadataFixture.coreChainLockedHeight, + ); + }); + + it('should return proof', async () => { + options.prove = true; + response.setProof(proof); + response.setDataContract(undefined); + + const contractId = dataContractFixture.getId(); + const result = await getDataContract(contractId, options); + + const request = new GetDataContractRequest(); + request.setId(contractId); + request.setProve(true); + + expect(grpcTransportMock.request.getCall(0).args).to.have.deep.members([ + PlatformPromiseClient, + 'getDataContract', + request, + options, + ]); + + expect(result.getDataContract()).to.deep.equal(Buffer.alloc(0)); + expect(result.getProof()).to.be.an.instanceOf(ProofClass); + expect(result.getProof().getMerkleProof()).to.deep.equal(proofFixture.merkleProof); + expect(result.getProof().getSignatureLLMQHash()).to.deep.equal(proofFixture.signatureLLMQHash); + expect(result.getProof().getSignature()).to.deep.equal(proofFixture.signature); + expect(result.getMetadata()).to.deep.equal(metadataFixture); + expect(result.getMetadata().getHeight()).to.equal(metadataFixture.height); + expect(result.getMetadata().getCoreChainLockedHeight()).to.equal( + metadataFixture.coreChainLockedHeight, + ); + }); + + it('should throw unknown error', async () => { + const error = new Error('Unknown found'); + const contractId = dataContractFixture.getId(); + + grpcTransportMock.request.throws(error); + + const request = new GetDataContractRequest(); + request.setId(contractId.toBuffer()); + request.setProve(false); + + try { + await getDataContract(contractId, options); + + expect.fail('should throw unknown error'); + } catch (e) { + expect(e).to.deep.equal(error); + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'getDataContract', + request, + options, + ); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/getDocuments/GetDocumentsResponse.spec.js b/packages/js-dapi-client/test/unit/methods/platform/getDocuments/GetDocumentsResponse.spec.js new file mode 100644 index 00000000000..691858510ab --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/getDocuments/GetDocumentsResponse.spec.js @@ -0,0 +1,130 @@ +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); +const { + v0: { + GetDocumentsResponse, + ResponseMetadata, + Proof: ProofResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const GetDocumentsResponseClass = require('../../../../../lib/methods/platform/getDocuments/GetDocumentsResponse'); +const getMetadataFixture = require('../../../../../lib/test/fixtures/getMetadataFixture'); +const InvalidResponseError = require('../../../../../lib/methods/platform/response/errors/InvalidResponseError'); +const getProofFixture = require('../../../../../lib/test/fixtures/getProofFixture'); +const Proof = require('../../../../../lib/methods/platform/response/Proof'); +const Metadata = require('../../../../../lib/methods/platform/response/Metadata'); + +describe('GetDocumentsResponse', () => { + let getDocumentsResponse; + let metadataFixture; + let documentsFixture; + let proto; + let serializedDocuments; + let proofFixture; + + beforeEach(() => { + metadataFixture = getMetadataFixture(); + documentsFixture = getDocumentsFixture(); + proofFixture = getProofFixture(); + + proto = new GetDocumentsResponse(); + + serializedDocuments = documentsFixture + .map((document) => Buffer.from(JSON.stringify(document))); + + proto.setDocumentsList(serializedDocuments); + + const metadata = new ResponseMetadata(); + metadata.setHeight(metadataFixture.height); + metadata.setCoreChainLockedHeight(metadataFixture.coreChainLockedHeight); + + proto.setMetadata(metadata); + + getDocumentsResponse = new GetDocumentsResponseClass( + serializedDocuments, + new Metadata(metadataFixture), + ); + }); + + it('should return documents', () => { + const documents = getDocumentsResponse.getDocuments(); + const proof = getDocumentsResponse.getProof(); + + expect(documents).to.deep.equal(serializedDocuments); + expect(proof).to.equal(undefined); + }); + + it('should return proof', async () => { + getDocumentsResponse = new GetDocumentsResponseClass( + [], + new Metadata(metadataFixture), + new Proof(proofFixture), + ); + + const documents = getDocumentsResponse.getDocuments(); + const proof = getDocumentsResponse.getProof(); + + expect(documents).to.deep.equal([]); + + expect(proof).to.be.an.instanceOf(Proof); + expect(proof.getMerkleProof()).to.deep.equal(proofFixture.merkleProof); + expect(proof.getSignatureLLMQHash()).to.deep.equal(proofFixture.signatureLLMQHash); + expect(proof.getSignature()).to.deep.equal(proofFixture.signature); + }); + + it('should create an instance from proto', () => { + getDocumentsResponse = GetDocumentsResponseClass.createFromProto(proto); + expect(getDocumentsResponse).to.be.an.instanceOf(GetDocumentsResponseClass); + expect(getDocumentsResponse.getDocuments()).to.deep.equal(serializedDocuments); + + expect(getDocumentsResponse.getMetadata()) + .to.be.an.instanceOf(Metadata); + expect(getDocumentsResponse.getMetadata().getHeight()) + .to.equal(metadataFixture.height); + expect(getDocumentsResponse.getMetadata().getCoreChainLockedHeight()) + .to.equal(metadataFixture.coreChainLockedHeight); + + expect(getDocumentsResponse.getProof()).to.equal(undefined); + }); + + it('should create an instance with proof from proto', () => { + const proofProto = new ProofResponse(); + + proofProto.setSignatureLlmqHash(proofFixture.signatureLLMQHash); + proofProto.setSignature(proofFixture.signature); + proofProto.setMerkleProof(proofFixture.merkleProof); + + proto.setDocumentsList([]); + proto.setProof(proofProto); + + getDocumentsResponse = GetDocumentsResponseClass.createFromProto(proto); + + expect(getDocumentsResponse).to.be.an.instanceOf(GetDocumentsResponseClass); + expect(getDocumentsResponse.getDocuments()).to.deep.members([]); + + expect(getDocumentsResponse.getMetadata()) + .to.be.an.instanceOf(Metadata); + expect(getDocumentsResponse.getMetadata().getHeight()) + .to.equal(metadataFixture.height); + expect(getDocumentsResponse.getMetadata().getCoreChainLockedHeight()) + .to.equal(metadataFixture.coreChainLockedHeight); + + const proof = getDocumentsResponse.getProof(); + expect(proof).to.be.an.instanceOf(Proof); + expect(proof.getMerkleProof()).to.deep.equal(proofFixture.merkleProof); + expect(proof.getSignatureLLMQHash()).to.deep.equal(proofFixture.signatureLLMQHash); + expect(proof.getSignature()).to.deep.equal(proofFixture.signature); + }); + + it('should throw InvalidResponseError if Metadata is not defined', () => { + proto.setMetadata(undefined); + + try { + getDocumentsResponse = GetDocumentsResponseClass.createFromProto(proto); + + expect.fail('should throw InvalidResponseError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidResponseError); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/getDocuments/getDocumentsFactory.spec.js b/packages/js-dapi-client/test/unit/methods/platform/getDocuments/getDocumentsFactory.spec.js new file mode 100644 index 00000000000..44e84fbe0c3 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/getDocuments/getDocumentsFactory.spec.js @@ -0,0 +1,164 @@ +const cbor = require('cbor'); +const Identifier = require('@dashevo/dpp/lib/Identifier'); + +const { + v0: { + PlatformPromiseClient, + GetDocumentsRequest, + GetDocumentsResponse, + ResponseMetadata, + Proof: ProofResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); + +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); +const getDocumentsFactory = require('../../../../../lib/methods/platform/getDocuments/getDocumentsFactory'); +const getMetadataFixture = require('../../../../../lib/test/fixtures/getMetadataFixture'); +const getProofFixture = require('../../../../../lib/test/fixtures/getProofFixture'); +const Proof = require('../../../../../lib/methods/platform/response/Proof'); + +describe('getDocumentsFactory', () => { + let grpcTransportMock; + let getDocuments; + let options; + let contractIdBuffer; + let contractIdIdentifier; + let type; + let documentsFixture; + let serializedDocuments; + let metadataFixture; + let proofFixture; + let proofResponse; + let response; + + beforeEach(function beforeEach() { + type = 'niceDocument'; + contractIdBuffer = Buffer.from('11c70af56a763b05943888fa3719ef56b3e826615fdda2d463c63f4034cb861c', 'hex'); + contractIdIdentifier = Identifier.from(contractIdBuffer); + + metadataFixture = getMetadataFixture(); + proofFixture = getProofFixture(); + + options = { + limit: 10, + orderBy: [ + ['order', 'asc'], + ], + startAt: generateRandomIdentifier(), + where: [['lastName', '==', 'unknown']], + startAfter: generateRandomIdentifier(), + }; + + documentsFixture = getDocumentsFixture(); + serializedDocuments = documentsFixture + .map((document) => Buffer.from(JSON.stringify(document))); + + const metadata = new ResponseMetadata(); + metadata.setHeight(metadataFixture.height); + metadata.setCoreChainLockedHeight(metadataFixture.coreChainLockedHeight); + + response = new GetDocumentsResponse(); + response.setDocumentsList(serializedDocuments); + response.setMetadata(metadata); + + grpcTransportMock = { + request: this.sinon.stub().resolves(response), + }; + + getDocuments = getDocumentsFactory(grpcTransportMock); + + proofResponse = new ProofResponse(); + proofResponse.setSignatureLlmqHash(proofFixture.signatureLLMQHash); + proofResponse.setSignature(proofFixture.signature); + proofResponse.setMerkleProof(proofFixture.merkleProof); + }); + + it('should return documents when contract id is buffer', async () => { + const result = await getDocuments(contractIdBuffer, type, options); + + const request = new GetDocumentsRequest(); + request.setDataContractId(contractIdBuffer); + request.setDocumentType(type); + request.setLimit(options.limit); + request.setWhere(cbor.encode(options.where)); + request.setOrderBy(cbor.encode(options.orderBy)); + request.setStartAfter(options.startAfter); + request.setStartAt(options.startAt); + request.setProve(false); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'getDocuments', + request, + options, + ); + expect(result.getDocuments()).to.deep.equal(serializedDocuments); + expect(result.getMetadata()).to.deep.equal(metadataFixture); + expect(result.getProof()).to.equal(undefined); + }); + + it('should return proof', async () => { + options.prove = true; + response.setDocumentsList([]); + response.setProof(proofResponse); + + const result = await getDocuments(contractIdBuffer, type, options); + + const request = new GetDocumentsRequest(); + request.setDataContractId(contractIdBuffer); + request.setDocumentType(type); + request.setLimit(options.limit); + request.setWhere(cbor.encode(options.where)); + request.setOrderBy(cbor.encode(options.orderBy)); + request.setStartAfter(options.startAfter); + request.setStartAt(options.startAt); + request.setProve(true); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'getDocuments', + request, + options, + ); + + expect(result.getDocuments()).to.deep.members([]); + + expect(result.getMetadata()).to.deep.equal(metadataFixture); + + expect(result.getProof()).to.be.an.instanceOf(Proof); + expect(result.getProof().getMerkleProof()).to.deep.equal(proofFixture.merkleProof); + expect(result.getProof().getSignatureLLMQHash()).to.deep.equal(proofFixture.signatureLLMQHash); + expect(result.getProof().getSignature()).to.deep.equal(proofFixture.signature); + expect(result.getMetadata()).to.deep.equal(metadataFixture); + expect(result.getMetadata().getHeight()).to.equal(metadataFixture.height); + expect(result.getMetadata().getCoreChainLockedHeight()).to.equal( + metadataFixture.coreChainLockedHeight, + ); + }); + + it('should return documents when contract id is identifier', async () => { + const result = await getDocuments(contractIdIdentifier, type, options); + + const request = new GetDocumentsRequest(); + request.setDataContractId(contractIdBuffer); + request.setDocumentType(type); + request.setLimit(options.limit); + request.setWhere(cbor.encode(options.where)); + request.setOrderBy(cbor.encode(options.orderBy)); + request.setStartAfter(options.startAfter); + request.setStartAt(options.startAt); + request.setProve(false); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'getDocuments', + request, + options, + ); + expect(result.getDocuments()).to.deep.equal(serializedDocuments); + expect(result.getMetadata()).to.deep.equal(metadataFixture); + expect(result.getProof()).to.equal(undefined); + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/getIdentitiesByPublicKeyHashes/GetIdentitiesByPublicKeyHashesResponse.spec.js b/packages/js-dapi-client/test/unit/methods/platform/getIdentitiesByPublicKeyHashes/GetIdentitiesByPublicKeyHashesResponse.spec.js new file mode 100644 index 00000000000..cc0f844f497 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/getIdentitiesByPublicKeyHashes/GetIdentitiesByPublicKeyHashesResponse.spec.js @@ -0,0 +1,127 @@ +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); +const { + v0: { + GetIdentitiesByPublicKeyHashesResponse, + ResponseMetadata, + Proof: ProofResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const GetIdentitiesByPublicKeyHashesResponseClass = require('../../../../../lib/methods/platform/getIdentitiesByPublicKeyHashes/GetIdentitiesByPublicKeyHashesResponse'); +const getMetadataFixture = require('../../../../../lib/test/fixtures/getMetadataFixture'); +const InvalidResponseError = require('../../../../../lib/methods/platform/response/errors/InvalidResponseError'); +const getProofFixture = require('../../../../../lib/test/fixtures/getProofFixture'); +const Proof = require('../../../../../lib/methods/platform/response/Proof'); +const Metadata = require('../../../../../lib/methods/platform/response/Metadata'); + +describe('GetIdentitiesByPublicKeyHashesResponse', () => { + let getIdentitiesResponse; + let metadataFixture; + let identityFixture; + let proto; + let proofFixture; + + beforeEach(() => { + metadataFixture = getMetadataFixture(); + identityFixture = getIdentityFixture(); + proofFixture = getProofFixture(); + + proto = new GetIdentitiesByPublicKeyHashesResponse(); + + proto.setIdentitiesList( + [identityFixture.toBuffer()], + ); + + const metadata = new ResponseMetadata(); + metadata.setHeight(metadataFixture.height); + metadata.setCoreChainLockedHeight(metadataFixture.coreChainLockedHeight); + + proto.setMetadata(metadata); + + getIdentitiesResponse = new GetIdentitiesByPublicKeyHashesResponseClass( + [identityFixture.toBuffer()], + new Metadata(metadataFixture), + ); + }); + + it('should return identities', () => { + const identities = getIdentitiesResponse.getIdentities(); + const proof = getIdentitiesResponse.getProof(); + + expect(identities).to.deep.members([identityFixture.toBuffer()]); + expect(proof).to.equal(undefined); + }); + + it('should return proof', () => { + getIdentitiesResponse = new GetIdentitiesByPublicKeyHashesResponseClass( + [], + new Metadata(metadataFixture), + new Proof(proofFixture), + ); + + const identities = getIdentitiesResponse.getIdentities(); + const proof = getIdentitiesResponse.getProof(); + + expect(identities).to.deep.members([]); + expect(proof).to.be.an.instanceOf(Proof); + expect(proof.getMerkleProof()).to.deep.equal(proofFixture.merkleProof); + expect(proof.getSignatureLLMQHash()).to.deep.equal(proofFixture.signatureLLMQHash); + expect(proof.getSignature()).to.deep.equal(proofFixture.signature); + }); + + it('should create an instance from proto', () => { + getIdentitiesResponse = GetIdentitiesByPublicKeyHashesResponseClass.createFromProto(proto); + expect(getIdentitiesResponse).to.be.an.instanceOf( + GetIdentitiesByPublicKeyHashesResponseClass, + ); + expect(getIdentitiesResponse.getIdentities()).to.deep.equal([identityFixture.toBuffer()]); + + expect(getIdentitiesResponse.getMetadata()) + .to.be.an.instanceOf(Metadata); + expect(getIdentitiesResponse.getMetadata().getHeight()) + .to.equal(metadataFixture.height); + expect(getIdentitiesResponse.getMetadata().getCoreChainLockedHeight()) + .to.equal(metadataFixture.coreChainLockedHeight); + + expect(getIdentitiesResponse.getProof()).to.equal(undefined); + }); + + it('should create an instance with proof from proto', () => { + const proofProto = new ProofResponse(); + + proofProto.setSignatureLlmqHash(proofFixture.signatureLLMQHash); + proofProto.setSignature(proofFixture.signature); + proofProto.setMerkleProof(proofFixture.merkleProof); + + proto.setIdentitiesList([]); + proto.setProof(proofProto); + + getIdentitiesResponse = GetIdentitiesByPublicKeyHashesResponseClass.createFromProto(proto); + expect(getIdentitiesResponse).to.be.an.instanceOf( + GetIdentitiesByPublicKeyHashesResponseClass, + ); + expect(getIdentitiesResponse.getIdentities()).to.deep.members([]); + expect(getIdentitiesResponse.getMetadata()).to.deep.equal(metadataFixture); + + expect(getIdentitiesResponse.getProof()) + .to.be.an.instanceOf(Proof); + expect(getIdentitiesResponse.getProof().getMerkleProof()) + .to.deep.equal(proofFixture.merkleProof); + expect(getIdentitiesResponse.getProof().getSignatureLLMQHash()) + .to.deep.equal(proofFixture.signatureLLMQHash); + expect(getIdentitiesResponse.getProof().getSignature()) + .to.deep.equal(proofFixture.signature); + }); + + it('should throw InvalidResponseError if Metadata is not defined', () => { + proto.setMetadata(undefined); + + try { + getIdentitiesResponse = GetIdentitiesByPublicKeyHashesResponseClass.createFromProto(proto); + + expect.fail('should throw InvalidResponseError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidResponseError); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/getIdentitiesByPublicKeyHashes/getIdentitiesByPublicKeyHashesFactory.spec.js b/packages/js-dapi-client/test/unit/methods/platform/getIdentitiesByPublicKeyHashes/getIdentitiesByPublicKeyHashesFactory.spec.js new file mode 100644 index 00000000000..567455e3229 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/getIdentitiesByPublicKeyHashes/getIdentitiesByPublicKeyHashesFactory.spec.js @@ -0,0 +1,138 @@ +const { + v0: { + PlatformPromiseClient, + GetIdentitiesByPublicKeyHashesRequest, + GetIdentitiesByPublicKeyHashesResponse, + ResponseMetadata, + Proof: ProofResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); +const getMetadataFixture = require('../../../../../lib/test/fixtures/getMetadataFixture'); +const getProofFixture = require('../../../../../lib/test/fixtures/getProofFixture'); + +const getIdentitiesByPublicKeyHashesFactory = require( + '../../../../../lib/methods/platform/getIdentitiesByPublicKeyHashes/getIdentitiesByPublicKeyHashesFactory', +); +const Proof = require('../../../../../lib/methods/platform/response/Proof'); + +describe('getIdentitiesByPublicKeyHashesFactory', () => { + let grpcTransportMock; + let getIdentitiesByPublicKeyHashes; + let options; + let response; + let identityFixture; + let publicKeyHash; + let metadataFixture; + let proofFixture; + let proofResponse; + + beforeEach(function beforeEach() { + identityFixture = getIdentityFixture(); + metadataFixture = getMetadataFixture(); + proofFixture = getProofFixture(); + + const metadata = new ResponseMetadata(); + metadata.setHeight(metadataFixture.height); + metadata.setCoreChainLockedHeight(metadataFixture.coreChainLockedHeight); + + response = new GetIdentitiesByPublicKeyHashesResponse(); + response.setIdentitiesList( + [identityFixture.toBuffer()], + ); + response.setMetadata(metadata); + + proofResponse = new ProofResponse(); + + proofResponse.setSignatureLlmqHash(proofFixture.signatureLLMQHash); + proofResponse.setSignature(proofFixture.signature); + proofResponse.setMerkleProof(proofFixture.merkleProof); + + publicKeyHash = identityFixture.getPublicKeyById(1).hash(); + + grpcTransportMock = { + request: this.sinon.stub().resolves(response), + }; + + options = { + timeout: 1000, + }; + + getIdentitiesByPublicKeyHashes = getIdentitiesByPublicKeyHashesFactory(grpcTransportMock); + }); + + it('should return public key hashes to identity map', async () => { + const result = await getIdentitiesByPublicKeyHashes([publicKeyHash], options); + + const request = new GetIdentitiesByPublicKeyHashesRequest(); + request.setPublicKeyHashesList([publicKeyHash]); + request.setProve(false); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'getIdentitiesByPublicKeyHashes', + request, + options, + ); + expect(result.getIdentities()).to.have.deep.equal([identityFixture.toBuffer()]); + expect(result.getMetadata()).to.deep.equal(metadataFixture); + expect(result.getProof()).to.equal(undefined); + }); + + it('should return proof', async () => { + options.prove = true; + response.setProof(proofResponse); + response.setIdentitiesList([]); + + const result = await getIdentitiesByPublicKeyHashes([publicKeyHash], options); + + const request = new GetIdentitiesByPublicKeyHashesRequest(); + request.setPublicKeyHashesList([publicKeyHash]); + request.setProve(true); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'getIdentitiesByPublicKeyHashes', + request, + options, + ); + expect(result.getIdentities()).to.have.deep.members([]); + + expect(result.getMetadata()).to.deep.equal(metadataFixture); + + expect(result.getProof()).to.be.an.instanceOf(Proof); + expect(result.getProof().getMerkleProof()).to.deep.equal(proofFixture.merkleProof); + expect(result.getProof().getSignatureLLMQHash()).to.deep.equal(proofFixture.signatureLLMQHash); + expect(result.getProof().getSignature()).to.deep.equal(proofFixture.signature); + expect(result.getMetadata()).to.deep.equal(metadataFixture); + expect(result.getMetadata().getHeight()).to.equal(metadataFixture.height); + expect(result.getMetadata().getCoreChainLockedHeight()).to.equal( + metadataFixture.coreChainLockedHeight, + ); + }); + + it('should throw unknown error', async () => { + const error = new Error('Unknown found'); + + grpcTransportMock.request.throws(error); + + const request = new GetIdentitiesByPublicKeyHashesRequest(); + request.setPublicKeyHashesList([publicKeyHash]); + request.setProve(false); + + try { + await getIdentitiesByPublicKeyHashes([publicKeyHash], options); + + expect.fail('should throw unknown error'); + } catch (e) { + expect(e).to.deep.equal(error); + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'getIdentitiesByPublicKeyHashes', + request, + options, + ); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/getIdentitiy/GetIdentityResponse.spec.js b/packages/js-dapi-client/test/unit/methods/platform/getIdentitiy/GetIdentityResponse.spec.js new file mode 100644 index 00000000000..ffba9a919fc --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/getIdentitiy/GetIdentityResponse.spec.js @@ -0,0 +1,129 @@ +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); +const { + v0: { + GetIdentityResponse, + ResponseMetadata, + Proof: ProofResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const GetIdentityResponseClass = require('../../../../../lib/methods/platform/getIdentity/GetIdentityResponse'); +const getMetadataFixture = require('../../../../../lib/test/fixtures/getMetadataFixture'); +const InvalidResponseError = require('../../../../../lib/methods/platform/response/errors/InvalidResponseError'); +const getProofFixture = require('../../../../../lib/test/fixtures/getProofFixture'); +const Proof = require('../../../../../lib/methods/platform/response/Proof'); +const Metadata = require('../../../../../lib/methods/platform/response/Metadata'); + +describe('GetIdentityResponse', () => { + let getIdentityResponse; + let metadataFixture; + let identityFixture; + let proto; + let proofFixture; + + beforeEach(() => { + metadataFixture = getMetadataFixture(); + identityFixture = getIdentityFixture(); + proofFixture = getProofFixture(); + + proto = new GetIdentityResponse(); + proto.setIdentity(identityFixture.toBuffer()); + + const metadata = new ResponseMetadata(); + metadata.setHeight(metadataFixture.height); + metadata.setCoreChainLockedHeight(metadataFixture.coreChainLockedHeight); + + proto.setMetadata(metadata); + + getIdentityResponse = new GetIdentityResponseClass( + identityFixture.toBuffer(), + new Metadata(metadataFixture), + ); + }); + + it('should return Identity', () => { + const identity = getIdentityResponse.getIdentity(); + const proof = getIdentityResponse.getProof(); + + expect(identity).to.deep.equal(identityFixture.toBuffer()); + expect(proof).to.equal(undefined); + }); + + it('should return proof', () => { + getIdentityResponse = new GetIdentityResponseClass( + Buffer.alloc(0), + new Metadata(metadataFixture), + new Proof(proofFixture), + ); + + const identity = getIdentityResponse.getIdentity(); + const proof = getIdentityResponse.getProof(); + + expect(identity).to.deep.equal(Buffer.alloc(0)); + expect(proof).to.be.an.instanceOf(Proof); + expect(proof.getMerkleProof()).to.deep.equal(proofFixture.merkleProof); + expect(proof.getSignatureLLMQHash()).to.deep.equal(proofFixture.signatureLLMQHash); + expect(proof.getSignature()).to.deep.equal(proofFixture.signature); + }); + + it('should create an instance from proto', () => { + getIdentityResponse = GetIdentityResponseClass.createFromProto(proto); + expect(getIdentityResponse).to.be.an.instanceOf(GetIdentityResponseClass); + expect(getIdentityResponse.getIdentity()).to.deep.equal(identityFixture.toBuffer()); + + expect(getIdentityResponse.getMetadata()) + .to.be.an.instanceOf(Metadata); + expect(getIdentityResponse.getMetadata().getHeight()) + .to.equal(metadataFixture.height); + expect(getIdentityResponse.getMetadata().getCoreChainLockedHeight()) + .to.equal(metadataFixture.coreChainLockedHeight); + + expect(getIdentityResponse.getProof()).to.equal(undefined); + }); + + it('should create an instance with proof from proto', () => { + const proofProto = new ProofResponse(); + + proofProto.setSignatureLlmqHash(proofFixture.signatureLLMQHash); + proofProto.setSignature(proofFixture.signature); + proofProto.setMerkleProof(proofFixture.merkleProof); + + proto.setIdentity(undefined); + proto.setProof(proofProto); + + getIdentityResponse = GetIdentityResponseClass.createFromProto(proto); + + expect(getIdentityResponse.getIdentity()).to.deep.equal(Buffer.alloc(0)); + expect(getIdentityResponse.getMetadata()).to.deep.equal(metadataFixture); + + const proof = getIdentityResponse.getProof(); + expect(proof).to.be.an.instanceOf(Proof); + expect(proof.getMerkleProof()).to.deep.equal(proofFixture.merkleProof); + expect(proof.getSignatureLLMQHash()).to.deep.equal(proofFixture.signatureLLMQHash); + expect(proof.getSignature()).to.deep.equal(proofFixture.signature); + }); + + it('should throw InvalidResponseError if Metadata is not defined', () => { + proto.setMetadata(undefined); + + try { + getIdentityResponse = GetIdentityResponseClass.createFromProto(proto); + + expect.fail('should throw InvalidResponseError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidResponseError); + } + }); + + it('should throw InvalidResponseError if Identity is not defined', () => { + proto.setIdentity(undefined); + + try { + getIdentityResponse = GetIdentityResponseClass.createFromProto(proto); + + expect.fail('should throw InvalidResponseError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidResponseError); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/getIdentitiy/getIdentityFactory.spec.js b/packages/js-dapi-client/test/unit/methods/platform/getIdentitiy/getIdentityFactory.spec.js new file mode 100644 index 00000000000..c40d4122d23 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/getIdentitiy/getIdentityFactory.spec.js @@ -0,0 +1,135 @@ +const { + v0: { + PlatformPromiseClient, + GetIdentityRequest, + GetIdentityResponse, + ResponseMetadata, + Proof: ProofResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); + +const getIdentityFactory = require('../../../../../lib/methods/platform/getIdentity/getIdentityFactory'); +const getMetadataFixture = require('../../../../../lib/test/fixtures/getMetadataFixture'); +const getProofFixture = require('../../../../../lib/test/fixtures/getProofFixture'); +const Proof = require('../../../../../lib/methods/platform/response/Proof'); + +describe('getIdentityFactory', () => { + let grpcTransportMock; + let getIdentity; + let options; + let response; + let identityFixture; + let identityId; + let metadataFixture; + let proofFixture; + let proofResponse; + + beforeEach(function beforeEach() { + identityFixture = getIdentityFixture(); + identityId = identityFixture.getId(); + + metadataFixture = getMetadataFixture(); + proofFixture = getProofFixture(); + + const metadata = new ResponseMetadata(); + metadata.setHeight(metadataFixture.height); + metadata.setCoreChainLockedHeight(metadataFixture.coreChainLockedHeight); + + response = new GetIdentityResponse(); + response.setIdentity(identityFixture.toBuffer()); + response.setMetadata(metadata); + + proofResponse = new ProofResponse(); + + proofResponse.setSignatureLlmqHash(proofFixture.signatureLLMQHash); + proofResponse.setSignature(proofFixture.signature); + proofResponse.setMerkleProof(proofFixture.merkleProof); + + grpcTransportMock = { + request: this.sinon.stub().resolves(response), + }; + + getIdentity = getIdentityFactory(grpcTransportMock); + + options = { + timeout: 1000, + }; + }); + + it('should return identity', async () => { + const result = await getIdentity(identityId, options); + + const request = new GetIdentityRequest(); + request.setId(identityId.toBuffer()); + request.setProve(false); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'getIdentity', + request, + options, + ); + expect(result.getIdentity()).to.deep.equal(identityFixture.toBuffer()); + expect(result.getMetadata()).to.deep.equal(metadataFixture); + expect(result.getProof()).to.equal(undefined); + }); + + it('should return proof', async () => { + options.prove = true; + response.setIdentity(undefined); + response.setProof(proofResponse); + + const result = await getIdentity(identityId, options); + + const request = new GetIdentityRequest(); + request.setId(identityId.toBuffer()); + request.setProve(true); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'getIdentity', + request, + options, + ); + + expect(result.getIdentity()).to.deep.equal(Buffer.alloc(0)); + + expect(result.getMetadata()).to.deep.equal(metadataFixture); + + expect(result.getProof()).to.be.an.instanceOf(Proof); + expect(result.getProof().getMerkleProof()).to.deep.equal(proofFixture.merkleProof); + expect(result.getProof().getSignatureLLMQHash()).to.deep.equal(proofFixture.signatureLLMQHash); + expect(result.getProof().getSignature()).to.deep.equal(proofFixture.signature); + expect(result.getMetadata()).to.deep.equal(metadataFixture); + expect(result.getMetadata().getHeight()).to.equal(metadataFixture.height); + expect(result.getMetadata().getCoreChainLockedHeight()).to.equal( + metadataFixture.coreChainLockedHeight, + ); + }); + + it('should throw unknown error', async () => { + const error = new Error('Unknown found'); + + grpcTransportMock.request.throws(error); + + const request = new GetIdentityRequest(); + request.setId(identityId.toBuffer()); + request.setProve(false); + + try { + await getIdentity(identityId, options); + + expect.fail('should throw unknown error'); + } catch (e) { + expect(e).to.deep.equal(error); + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'getIdentity', + request, + options, + ); + } + }); +}); diff --git a/packages/js-dapi-client/test/unit/methods/platform/waitForStateTransitionResult/waitForStateTransitionResultFactory.spec.js b/packages/js-dapi-client/test/unit/methods/platform/waitForStateTransitionResult/waitForStateTransitionResultFactory.spec.js new file mode 100644 index 00000000000..6fd0d28f3e9 --- /dev/null +++ b/packages/js-dapi-client/test/unit/methods/platform/waitForStateTransitionResult/waitForStateTransitionResultFactory.spec.js @@ -0,0 +1,136 @@ +const { + v0: { + PlatformPromiseClient, + WaitForStateTransitionResultRequest, + StateTransitionBroadcastError, + WaitForStateTransitionResultResponse, + Proof, + ResponseMetadata, + }, +} = require('@dashevo/dapi-grpc'); +const cbor = require('cbor'); + +const waitForStateTransitionResultFactory = require('../../../../../lib/methods/platform/waitForStateTransitionResult/waitForStateTransitionResultFactory'); +const getMetadataFixture = require('../../../../../lib/test/fixtures/getMetadataFixture'); + +describe('waitForStateTransitionResultFactory', () => { + let grpcTransportMock; + let options; + let response; + let hash; + let waitForStateTransitionResult; + let metadataFixture; + + beforeEach(function beforeEach() { + hash = Buffer.from('hash'); + metadataFixture = getMetadataFixture(); + + const metadata = new ResponseMetadata(); + metadata.setHeight(metadataFixture.height); + metadata.setCoreChainLockedHeight(metadataFixture.coreChainLockedHeight); + + response = new WaitForStateTransitionResultResponse(); + response.setMetadata(metadata); + + grpcTransportMock = { + request: this.sinon.stub().resolves(response), + }; + + options = { + timeout: 1000, + throwDeadlineExceeded: true, + retry: 0, + }; + + waitForStateTransitionResult = waitForStateTransitionResultFactory(grpcTransportMock); + }); + + it('should return response', async () => { + options.prove = false; + + const result = await waitForStateTransitionResult(hash, options); + + expect(result.getMetadata()).to.deep.equal(metadataFixture); + expect(result.getError()).to.equal(undefined); + expect(result.getProof()).to.equal(undefined); + + const request = new WaitForStateTransitionResultRequest(); + request.setStateTransitionHash(hash); + request.setProve(false); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'waitForStateTransitionResult', + request, + options, + ); + }); + + it('should return response with proof', async () => { + const proof = new Proof(); + + proof.setMerkleProof(Buffer.from('merkleProof')); + proof.setSignatureLlmqHash(Buffer.from('signatureLlmqHash')); + proof.setSignature(Buffer.from('signature')); + + response.setProof(proof); + + options.prove = true; + + const result = await waitForStateTransitionResult(hash, options); + + expect(result.getMetadata()).to.deep.equal(metadataFixture); + expect(result.getError()).to.equal(undefined); + expect(result.getProof()).to.be.deep.equal({ + merkleProof: Buffer.from('merkleProof'), + signatureLLMQHash: Buffer.from('signatureLlmqHash'), + signature: Buffer.from('signature'), + }); + expect(result.getProof().getSignature()).to.deep.equal(Buffer.from('signature')); + expect(result.getProof().getMerkleProof()).to.deep.equal(Buffer.from('merkleProof')); + expect(result.getProof().getSignatureLLMQHash()).to.deep.equal(Buffer.from('signatureLlmqHash')); + + const request = new WaitForStateTransitionResultRequest(); + request.setStateTransitionHash(hash); + request.setProve(true); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'waitForStateTransitionResult', + request, + options, + ); + }); + + it('should return response with error', async () => { + const error = new StateTransitionBroadcastError(); + error.setCode(2); + error.setMessage('Some error'); + error.setData(cbor.encode({ data: 'error data' })); + + response.setError(error); + + options.prove = true; + + const result = await waitForStateTransitionResult(hash, options); + + expect(result.getMetadata()).to.deep.equal(metadataFixture); + expect(result.getProof()).to.equal(undefined); + expect(result.getError()).to.be.deep.equal({ + code: 2, + message: 'Some error', + data: { data: 'error data' }, + }); + + const request = new WaitForStateTransitionResultRequest(); + request.setStateTransitionHash(hash); + request.setProve(true); + + expect(grpcTransportMock.request).to.be.calledOnceWithExactly( + PlatformPromiseClient, + 'waitForStateTransitionResult', + request, + options, + ); + }); +}); diff --git a/packages/js-dapi-client/test/unit/transport/GrpcTransport/GrpcTransport.spec.js b/packages/js-dapi-client/test/unit/transport/GrpcTransport/GrpcTransport.spec.js new file mode 100644 index 00000000000..46b22f0fea1 --- /dev/null +++ b/packages/js-dapi-client/test/unit/transport/GrpcTransport/GrpcTransport.spec.js @@ -0,0 +1,403 @@ +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); + +const GrpcError = require('@dashevo/grpc-common/lib/server/error/GrpcError'); +const GrpcTransport = require('../../../../lib/transport/GrpcTransport/GrpcTransport'); +const DAPIAddress = require('../../../../lib/dapiAddressProvider/DAPIAddress'); + +const MaxRetriesReachedError = require('../../../../lib/transport/errors/response/MaxRetriesReachedError'); +const NoAvailableAddressesForRetryError = require('../../../../lib/transport/errors/response/NoAvailableAddressesForRetryError'); +const NoAvailableAddressesError = require('../../../../lib/transport/errors/NoAvailableAddressesError'); +const ResponseError = require('../../../../lib/transport/errors/response/ResponseError'); +const TimeoutError = require('../../../../lib/transport/GrpcTransport/errors/TimeoutError'); +const RetriableResponseError = require('../../../../lib/transport/errors/response/RetriableResponseError'); + +describe('GrpcTransport', () => { + let grpcTransport; + let dapiAddressProviderMock; + let globalOptions; + let createDAPIAddressProviderFromOptionsMock; + let dapiAddress; + let host; + let url; + + beforeEach(function beforeEach() { + host = '127.0.0.1'; + dapiAddress = new DAPIAddress(host); + + dapiAddressProviderMock = { + getLiveAddress: this.sinon.stub().resolves(dapiAddress), + hasLiveAddresses: this.sinon.stub().resolves(false), + }; + + globalOptions = { + retries: 0, + }; + + createDAPIAddressProviderFromOptionsMock = this.sinon.stub().returns(null); + + grpcTransport = new GrpcTransport( + createDAPIAddressProviderFromOptionsMock, + dapiAddressProviderMock, + globalOptions, + ); + + // noinspection JSUnresolvedFunction + url = grpcTransport.makeGrpcUrlFromAddress(dapiAddress); + }); + + describe('#request', () => { + let method; + let clientClassMock; + let requestMessage; + let options; + let data; + let requestFunc; + let clock; + let createGrpcTransportErrorMock; + + beforeEach(function beforeEach() { + data = 'result'; + method = 'method'; + requestMessage = 'requestMessage'; + options = { + option: 'value', + }; + + requestFunc = this.sinon.stub().resolves(data); + + clientClassMock = this.sinon.stub().returns({ + [method]: requestFunc, + }); + + dapiAddressProviderMock.hasLiveAddresses.resolves(true); + + globalOptions = { + retries: 1, + }; + + createGrpcTransportErrorMock = this.sinon.stub(); + + grpcTransport = new GrpcTransport( + createDAPIAddressProviderFromOptionsMock, + dapiAddressProviderMock, + createGrpcTransportErrorMock, + globalOptions, + ); + }); + + afterEach(() => { + if (clock) { + clock.restore(); + } + }); + + describe('#request', () => { + it('should make a request', async () => { + const receivedData = await grpcTransport.request( + clientClassMock, + method, + requestMessage, + options, + ); + + expect(receivedData).to.equal(data); + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly(options); + expect(clientClassMock).to.be.calledOnceWithExactly(url); + expect(requestFunc).to.be.calledOnceWithExactly(requestMessage, {}, {}); + expect(grpcTransport.lastUsedAddress).to.deep.equal(dapiAddress); + }); + + it('should make a request with `deadline` option if `timeout` option is set', async function itContainer() { + // Freeze time by using fake timers + clock = this.sinon.useFakeTimers(); + + const timeout = 2000; + + const deadline = new Date(); + deadline.setMilliseconds((new Date()).getMilliseconds() + timeout); + + const receivedData = await grpcTransport.request( + clientClassMock, + method, + requestMessage, + { + timeout, + ...options, + }, + ); + + expect(receivedData).to.equal(data); + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly({ + timeout, + ...options, + }); + expect(clientClassMock).to.be.calledOnceWithExactly(url); + expect(requestFunc).to.be.calledOnceWithExactly( + requestMessage, {}, { + deadline, + }, + ); + expect(grpcTransport.lastUsedAddress).to.deep.equal(dapiAddress); + }); + + it('should throw NoAvailableAddressesError if there is no available addresses', async () => { + dapiAddressProviderMock.getLiveAddress.resolves(null); + + try { + await grpcTransport.request( + clientClassMock, + method, + requestMessage, + options, + ); + + expect.fail('should throw NoAvailableAddressesError'); + } catch (e) { + expect(e).to.be.an.instanceof(NoAvailableAddressesError); + expect(clientClassMock).to.not.be.called(); + } + }); + + it('should throw unknown error if it happened during the request', async () => { + const error = new Error('Unknown error'); + + requestFunc.throws(error); + + try { + await grpcTransport.request( + clientClassMock, + method, + requestMessage, + options, + ); + + expect.fail('should throw error'); + } catch (e) { + expect(e).to.deep.equal(error); + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly(options); + expect(clientClassMock).to.be.calledOnceWithExactly(url); + expect(requestFunc).to.be.calledOnceWithExactly(requestMessage, {}, {}); + } + }); + + it('should throw non-retriable response error', async () => { + const error = new GrpcError(GrpcErrorCodes.UNKNOWN, 'doesnt matter'); + + requestFunc.throws(error); + + const responseError = new ResponseError( + error.code, + error.message, + {}, + dapiAddress, + ); + + createGrpcTransportErrorMock.returns(responseError); + + try { + await grpcTransport.request( + clientClassMock, + method, + requestMessage, + options, + ); + + expect.fail('should throw ResponseError'); + } catch (e) { + expect(e).to.equal(responseError); + + expect(createGrpcTransportErrorMock).to.be.calledOnceWithExactly(error, dapiAddress); + + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly(options); + expect(clientClassMock).to.be.calledOnceWithExactly(url); + expect(requestFunc).to.be.calledOnceWithExactly(requestMessage, {}, {}); + } + }); + + it('should throw TimeoutError with throwDeadlineExceeded option enabled', async () => { + dapiAddressProviderMock.hasLiveAddresses.resolves(false); + + options.throwDeadlineExceeded = true; + + const error = new GrpcError(GrpcErrorCodes.DEADLINE_EXCEEDED, 'time is over'); + + requestFunc.throws(error); + + const responseError = new TimeoutError( + error.message, + {}, + dapiAddress, + ); + + createGrpcTransportErrorMock.returns(responseError); + + try { + await grpcTransport.request( + clientClassMock, + method, + requestMessage, + options, + ); + + expect.fail('should throw TimeoutError'); + } catch (e) { + expect(e).to.equal(responseError); + + expect(createGrpcTransportErrorMock).to.be.calledOnceWithExactly(error, dapiAddress); + + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly(options); + expect(clientClassMock).to.be.calledOnceWithExactly(url); + expect(requestFunc).to.be.calledOnceWithExactly(requestMessage, {}, {}); + } + }); + + it('should throw MaxRetriesReachedError if there are no more retries left', async () => { + const error = new GrpcError(GrpcErrorCodes.UNKNOWN, 'doesnt matter'); + + requestFunc.throws(error); + + const responseError = new RetriableResponseError( + error.code, + error.message, + {}, + dapiAddress, + ); + + createGrpcTransportErrorMock.returns(responseError); + + options.retries = 0; + + try { + await grpcTransport.request( + clientClassMock, + method, + requestMessage, + options, + ); + + expect.fail('should throw MaxRetriesReachedError'); + } catch (e) { + expect(e).to.be.an.instanceof(MaxRetriesReachedError); + expect(e.getCause()).to.equal(responseError); + + createGrpcTransportErrorMock.returns(responseError); + + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly(options); + expect(clientClassMock).to.be.calledOnceWithExactly(url); + expect(requestFunc).to.be.calledOnceWithExactly(requestMessage, {}, {}); + } + }); + + it('should throw NoAvailableAddressesForRetryError if there are no more available addresses to request', async () => { + dapiAddressProviderMock.hasLiveAddresses.resolves(false); + + const error = new GrpcError(GrpcErrorCodes.UNKNOWN, 'doesnt matter'); + + requestFunc.throws(error); + + const responseError = new RetriableResponseError( + error.code, + error.message, + {}, + dapiAddress, + ); + + createGrpcTransportErrorMock.returns(responseError); + + try { + await grpcTransport.request( + clientClassMock, + method, + requestMessage, + options, + ); + + expect.fail('should throw NoAvailableAddressesForRetryError'); + } catch (e) { + expect(e).to.be.an.instanceof(NoAvailableAddressesForRetryError); + expect(e.getCause()).to.deep.equal(responseError); + + expect(createGrpcTransportErrorMock).to.be.calledOnceWithExactly(error, dapiAddress); + + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly(options); + expect(clientClassMock).to.be.calledOnceWithExactly(url); + expect(requestFunc).to.be.calledOnceWithExactly(requestMessage, {}, {}); + } + }); + }); + + describe('#getLastUsedAddress', () => { + it('should return last used address', async () => { + await grpcTransport.request( + clientClassMock, + method, + requestMessage, + ); + + const getLastUsedAddress = grpcTransport.getLastUsedAddress(); + expect(getLastUsedAddress).to.deep.equal(grpcTransport.lastUsedAddress); + }); + }); + + describe('gRPC-Web', () => { + let originalVersion; + + before(() => { + originalVersion = process.versions; + Object.defineProperty(process, 'versions', { + value: null, + }); + }); + + after(() => { + Object.defineProperty(process, 'versions', { + value: originalVersion, + }); + }); + + it('should make a request in web environment', async () => { + const receivedData = await grpcTransport.request( + clientClassMock, + method, + requestMessage, + options, + ); + + expect(receivedData).to.deep.equal(data); + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly(options); + expect(clientClassMock).to.be.calledOnceWithExactly(`http://${host}:${dapiAddress.getHttpPort()}`); + expect(requestFunc).to.be.calledOnceWithExactly(requestMessage, {}, {}); + expect(grpcTransport.lastUsedAddress).to.deep.equal(dapiAddress); + }); + + it('should make a https request in web environment', async () => { + dapiAddress = new DAPIAddress({ + host, + httpPort: 443, + }); + + dapiAddressProviderMock.getLiveAddress.resolves(dapiAddress); + + grpcTransport = new GrpcTransport( + createDAPIAddressProviderFromOptionsMock, + dapiAddressProviderMock, + createGrpcTransportErrorMock, + globalOptions, + ); + + const receivedData = await grpcTransport.request( + clientClassMock, + method, + requestMessage, + options, + ); + + expect(receivedData).to.deep.equal(data); + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly(options); + expect(clientClassMock).to.be.calledOnceWithExactly(`https://${host}:${dapiAddress.getHttpPort()}`); + expect(requestFunc).to.be.calledOnceWithExactly(requestMessage, {}, {}); + expect(grpcTransport.lastUsedAddress).to.deep.equal(dapiAddress); + }); + }); + }); +}); diff --git a/packages/js-dapi-client/test/unit/transport/GrpcTransport/createGrpcTransportError.spec.js b/packages/js-dapi-client/test/unit/transport/GrpcTransport/createGrpcTransportError.spec.js new file mode 100644 index 00000000000..fc7934fc0a9 --- /dev/null +++ b/packages/js-dapi-client/test/unit/transport/GrpcTransport/createGrpcTransportError.spec.js @@ -0,0 +1,207 @@ +const GrpcError = require('@dashevo/grpc-common/lib/server/error/GrpcError'); +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const cbor = require('cbor'); +const SerializedObjectParsingError = require('@dashevo/dpp/lib/errors/consensus/basic/decode/SerializedObjectParsingError'); +const createGrpcTransportError = require('../../../../lib/transport/GrpcTransport/createGrpcTransportError'); +const DAPIAddress = require('../../../../lib/dapiAddressProvider/DAPIAddress'); +const NotFoundError = require('../../../../lib/transport/GrpcTransport/errors/NotFoundError'); +const InvalidRequestError = require('../../../../lib/transport/errors/response/InvalidRequestError'); +const InternalServerError = require('../../../../lib/transport/GrpcTransport/errors/InternalServerError'); +const ServerError = require('../../../../lib/transport/errors/response/ServerError'); +const InvalidRequestDPPError = require('../../../../lib/transport/errors/response/InvalidRequestDPPError'); +const ResponseError = require('../../../../lib/transport/errors/response/ResponseError'); + +describe('createGrpcTransportError', () => { + let dapiAddress; + let errorData; + let metadata; + + beforeEach(() => { + dapiAddress = new DAPIAddress('127.0.0.1:3001:3002'); + errorData = { + errorData: 'some data', + }; + + metadata = { + 'drive-error-data-bin': cbor.encode(errorData), + }; + }); + + it('should return NotFoundError', () => { + const grpcError = new GrpcError( + GrpcErrorCodes.NOT_FOUND, + 'Not found', + metadata, + ); + + const error = createGrpcTransportError( + grpcError, + dapiAddress, + ); + + expect(error).to.be.an.instanceOf(NotFoundError); + expect(error.message).to.equal(grpcError.message); + expect(error.getCode()).to.equal(GrpcErrorCodes.NOT_FOUND); + expect(error.getDAPIAddress()).to.deep.equal(dapiAddress); + expect(error.getData()).to.deep.equal(errorData); + }); + + it('should get code from metadata', () => { + metadata.code = GrpcErrorCodes.INVALID_ARGUMENT; + + const grpcError = new GrpcError( + GrpcErrorCodes.NOT_FOUND, + 'Not found', + metadata, + ); + + const error = createGrpcTransportError( + grpcError, + dapiAddress, + ); + + expect(error).to.be.an.instanceOf(InvalidRequestError); + expect(error.message).to.equal(grpcError.message); + expect(error.getCode()).to.equal(GrpcErrorCodes.INVALID_ARGUMENT); + expect(error.getDAPIAddress()).to.deep.equal(dapiAddress); + expect(error.getData()).to.deep.equal(errorData); + }); + + it('should get code from metadata in browser environment', () => { + metadata.code = GrpcErrorCodes.INVALID_ARGUMENT; + + const grpcError = new Error( + 'Not found', + ); + + grpcError.code = GrpcErrorCodes.NOT_FOUND; + grpcError.metadata = metadata; + + const error = createGrpcTransportError( + grpcError, + dapiAddress, + ); + + expect(error).to.be.an.instanceOf(InvalidRequestError); + expect(error.message).to.equal(grpcError.message); + expect(error.getCode()).to.equal(GrpcErrorCodes.INVALID_ARGUMENT); + expect(error.getDAPIAddress()).to.deep.equal(dapiAddress); + expect(error.getData()).to.deep.equal(errorData); + }); + + it('should return InvalidRequestError', () => { + const grpcError = new GrpcError( + GrpcErrorCodes.INVALID_ARGUMENT, + 'Invalid arguments', + metadata, + ); + + const error = createGrpcTransportError( + grpcError, + dapiAddress, + ); + + expect(error).to.be.an.instanceOf(InvalidRequestError); + expect(error.message).to.equal(grpcError.message); + expect(error.getCode()).to.equal(GrpcErrorCodes.INVALID_ARGUMENT); + expect(error.getDAPIAddress()).to.deep.equal(dapiAddress); + expect(error.getData()).to.deep.equal(errorData); + }); + + it('should return InternalServerError with stack', () => { + const errorWithStack = new Error('Some error'); + const grpcError = new GrpcError( + GrpcErrorCodes.INTERNAL, + 'Internal error', + { + ...metadata, + 'stack-bin': cbor.encode(errorWithStack.stack), + }, + ); + + const error = createGrpcTransportError( + grpcError, + dapiAddress, + ); + expect(error).to.be.an.instanceOf(InternalServerError); + expect(error.message).to.equal(grpcError.message); + expect(error.getCode()).to.equal(GrpcErrorCodes.INTERNAL); + expect(error.getDAPIAddress()).to.deep.equal(dapiAddress); + expect(error.getData()).to.deep.equal({ + ...errorData, + stack: errorWithStack.stack, + }); + expect(error.stack).to.deep.equal(`[REMOTE STACK] ${errorWithStack.stack}`); + }); + + it('should return ServerError', () => { + const grpcError = new GrpcError( + GrpcErrorCodes.UNAVAILABLE, + 'Unavailable', + metadata, + ); + + const error = createGrpcTransportError( + grpcError, + dapiAddress, + ); + + expect(error).to.be.an.instanceOf(ServerError); + expect(error.message).to.equal(grpcError.message); + expect(error.getCode()).to.equal(GrpcErrorCodes.UNAVAILABLE); + expect(error.getDAPIAddress()).to.deep.equal(dapiAddress); + expect(error.getData()).to.deep.equal(errorData); + }); + + it('should return InvalidRequestDPPError', () => { + const constructorArguments = ['arguments']; + + metadata = { + 'drive-error-data-bin': cbor.encode({ + arguments: constructorArguments, + ...errorData, + }), + }; + + const grpcError = new GrpcError( + 1001, + 'Parsing error', + metadata, + ); + + const error = createGrpcTransportError( + grpcError, + dapiAddress, + ); + + expect(error).to.be.an.instanceOf(InvalidRequestDPPError); + + expect(error.getCode()).to.equal(grpcError.code); + expect(error.getDAPIAddress()).to.deep.equal(dapiAddress); + expect(error.getData()).to.deep.equal(errorData); + + const consensusError = error.getConsensusError(); + + expect(consensusError).to.be.an.instanceOf(SerializedObjectParsingError); + expect(consensusError.getConstructorArguments()).to.deep.equal(constructorArguments); + }); + + it('should return ResponseError', () => { + const grpcError = new GrpcError( + 6000, + 'Unknown error', + metadata, + ); + + const error = createGrpcTransportError( + grpcError, + dapiAddress, + ); + + expect(error).to.be.an.instanceOf(ResponseError); + expect(error.message).to.equal(grpcError.message); + expect(error.getCode()).to.equal(grpcError.code); + expect(error.getDAPIAddress()).to.deep.equal(dapiAddress); + expect(error.getData()).to.deep.equal(errorData); + }); +}); diff --git a/packages/js-dapi-client/test/unit/transport/JsonRpcTransport/JsonRpcTransport.spec.js b/packages/js-dapi-client/test/unit/transport/JsonRpcTransport/JsonRpcTransport.spec.js new file mode 100644 index 00000000000..b6b2a6e6ad6 --- /dev/null +++ b/packages/js-dapi-client/test/unit/transport/JsonRpcTransport/JsonRpcTransport.spec.js @@ -0,0 +1,268 @@ +const JsonRpcTransport = require('../../../../lib/transport/JsonRpcTransport/JsonRpcTransport'); +const DAPIAddress = require('../../../../lib/dapiAddressProvider/DAPIAddress'); + +const MaxRetriesReachedError = require('../../../../lib/transport/errors/response/MaxRetriesReachedError'); +const NoAvailableAddressesForRetryError = require('../../../../lib/transport/errors/response/NoAvailableAddressesForRetryError'); +const NoAvailableAddressesError = require('../../../../lib/transport/errors/NoAvailableAddressesError'); +const ResponseError = require('../../../../lib/transport/errors/response/ResponseError'); +const JsonRpcError = require('../../../../lib/transport/JsonRpcTransport/errors/JsonRpcError'); +const RetriableResponseError = require('../../../../lib/transport/errors/response/RetriableResponseError'); + +describe('JsonRpcTransport', () => { + let jsonRpcTransport; + let globalOptions; + let createDAPIAddressProviderFromOptionsMock; + let dapiAddressProviderMock; + let dapiAddress; + let host; + let requestJsonRpcMock; + let createJsonTransportErrorMock; + + beforeEach(function beforeEach() { + host = '127.0.0.1'; + dapiAddress = new DAPIAddress(host); + + globalOptions = { + retries: 0, + }; + + dapiAddressProviderMock = { + getLiveAddress: this.sinon.stub().resolves(dapiAddress), + hasLiveAddresses: this.sinon.stub().resolves(false), + }; + + createDAPIAddressProviderFromOptionsMock = this.sinon.stub().returns(null); + + requestJsonRpcMock = this.sinon.stub(); + + createJsonTransportErrorMock = this.sinon.stub(); + + jsonRpcTransport = new JsonRpcTransport( + createDAPIAddressProviderFromOptionsMock, + requestJsonRpcMock, + dapiAddressProviderMock, + createJsonTransportErrorMock, + globalOptions, + ); + }); + + describe('#request', () => { + let method; + let params; + let options; + let data; + let requestInfo; + let jsonRpcErrorObject; + + beforeEach(() => { + params = { + data: 'some params', + }; + options = { + timeout: 1000, + }; + method = 'method'; + data = 'result'; + + requestInfo = {}; + + jsonRpcErrorObject = { + code: 1, + message: 'hello', + data: {}, + }; + + requestJsonRpcMock.resolves(data); + }); + + it('should make a request', async () => { + const receivedData = await jsonRpcTransport.request( + method, + params, + options, + ); + + expect(receivedData).to.equal(data); + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly(options); + expect(jsonRpcTransport.lastUsedAddress).to.deep.equal(dapiAddress); + expect(requestJsonRpcMock).to.be.calledOnceWithExactly( + dapiAddress.getHost(), + dapiAddress.getHttpPort(), + method, + params, + { timeout: options.timeout }, + ); + }); + + it('should throw unknown error', async () => { + const error = new Error('Unknown error'); + requestJsonRpcMock.throws(error); + + try { + await jsonRpcTransport.request( + method, + params, + ); + + expect.fail('should throw error'); + } catch (e) { + expect(e).to.deep.equal(error); + + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly({}); + expect(jsonRpcTransport.lastUsedAddress).to.deep.equal(dapiAddress); + expect(requestJsonRpcMock).to.be.calledOnceWithExactly( + dapiAddress.getHost(), + dapiAddress.getHttpPort(), + method, + params, + {}, + ); + } + }); + + it('should throw NoAvailableAddresses if there is no available addresses', async () => { + dapiAddressProviderMock.getLiveAddress.resolves(null); + + try { + await jsonRpcTransport.request( + method, + ); + + expect.fail('should throw NoAvailableAddresses'); + } catch (e) { + expect(e).to.be.an.instanceof(NoAvailableAddressesError); + expect(requestJsonRpcMock).to.not.be.called(); + } + }); + + it('should throw non-retriable response error', async () => { + const error = new JsonRpcError(requestInfo, jsonRpcErrorObject); + + requestJsonRpcMock.throws(error); + + const responseError = new ResponseError( + error.getCode(), + error.getMessage(), + error.getData(), + dapiAddress, + ); + + createJsonTransportErrorMock.returns(responseError); + + try { + await jsonRpcTransport.request( + method, + params, + ); + + expect.fail('should throw ResponseError'); + } catch (e) { + expect(e).to.equal(responseError); + + expect(createJsonTransportErrorMock).to.be.calledOnceWithExactly(error, dapiAddress); + + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly({}); + expect(jsonRpcTransport.lastUsedAddress).to.deep.equal(dapiAddress); + expect(requestJsonRpcMock).to.be.calledOnceWithExactly( + dapiAddress.getHost(), + dapiAddress.getHttpPort(), + method, + params, + {}, + ); + } + }); + + it('should throw MaxRetriesReachedError', async () => { + const error = new JsonRpcError(requestInfo, jsonRpcErrorObject); + + requestJsonRpcMock.throws(error); + + const responseError = new RetriableResponseError( + error.getCode(), + error.getMessage(), + error.getData(), + dapiAddress, + ); + + createJsonTransportErrorMock.returns(responseError); + + options.retries = 0; + + try { + await jsonRpcTransport.request( + method, + ); + + expect.fail('should throw MaxRetriesReachedError'); + } catch (e) { + expect(e).to.be.an.instanceof(MaxRetriesReachedError); + expect(e.getCause()).to.equal(responseError); + + expect(createJsonTransportErrorMock).to.be.calledOnceWithExactly(error, dapiAddress); + + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly({}); + expect(jsonRpcTransport.lastUsedAddress).to.deep.equal(dapiAddress); + expect(requestJsonRpcMock).to.be.calledOnceWithExactly( + dapiAddress.getHost(), + dapiAddress.getHttpPort(), + method, + {}, + {}, + ); + } + }); + + it('should throw NoAvailableAddressesForRetry error', async () => { + const error = new JsonRpcError(requestInfo, jsonRpcErrorObject); + + requestJsonRpcMock.throws(error); + + const responseError = new RetriableResponseError( + error.getCode(), + error.getMessage(), + error.getData(), + dapiAddress, + ); + + createJsonTransportErrorMock.returns(responseError); + + options.retries = 1; + + try { + await jsonRpcTransport.request( + method, + params, + options, + ); + + expect.fail('should throw NoAvailableAddressesForRetry'); + } catch (e) { + expect(e).to.be.an.instanceof(NoAvailableAddressesForRetryError); + expect(e.getCause()).to.equal(responseError); + + expect(createJsonTransportErrorMock).to.be.calledOnceWithExactly(error, dapiAddress); + + expect(createDAPIAddressProviderFromOptionsMock).to.be.calledOnceWithExactly(options); + expect(jsonRpcTransport.lastUsedAddress).to.deep.equal(dapiAddress); + expect(requestJsonRpcMock).to.be.calledOnceWithExactly( + dapiAddress.getHost(), + dapiAddress.getHttpPort(), + method, + params, + { timeout: options.timeout }, + ); + } + }); + }); + + describe('#getLastUsedAddress', () => { + it('should return lastUsedAddress', async () => { + jsonRpcTransport.lastUsedAddress = dapiAddress; + + const lastUsedAddress = jsonRpcTransport.getLastUsedAddress(); + + expect(lastUsedAddress).to.deep.equal(dapiAddress); + }); + }); +}); diff --git a/packages/js-dapi-client/test/unit/transport/JsonRpcTransport/createJsonTransportError.spec.js b/packages/js-dapi-client/test/unit/transport/JsonRpcTransport/createJsonTransportError.spec.js new file mode 100644 index 00000000000..15eab4e4d06 --- /dev/null +++ b/packages/js-dapi-client/test/unit/transport/JsonRpcTransport/createJsonTransportError.spec.js @@ -0,0 +1,104 @@ +const DAPIAddress = require('../../../../lib/dapiAddressProvider/DAPIAddress'); +const WrongHttpCodeError = require('../../../../lib/transport/JsonRpcTransport/errors/WrongHttpCodeError'); +const createJsonTransportError = require('../../../../lib/transport/JsonRpcTransport/createJsonTransportError'); +const ServerError = require('../../../../lib/transport/errors/response/ServerError'); +const JsonRpcError = require('../../../../lib/transport/JsonRpcTransport/errors/JsonRpcError'); +const ResponseError = require('../../../../lib/transport/errors/response/ResponseError'); +const RetriableResponseError = require('../../../../lib/transport/errors/response/RetriableResponseError'); + +describe('createJsonTransportError', () => { + let dapiAddress; + let requestInfo; + + beforeEach(() => { + dapiAddress = new DAPIAddress('127.0.0.1'); + }); + + it('should return ServerError', () => { + requestInfo = { + host: '127.0.0.1', + port: 80, + method: 'someMethod', + params: {}, + options: {}, + }; + const statusCode = 500; + const statusMessage = 'status message'; + + const error = new WrongHttpCodeError(requestInfo, statusCode, statusMessage); + + const jsonTransportError = createJsonTransportError(error, dapiAddress); + + expect(jsonTransportError).to.be.an.instanceOf(ServerError); + expect(jsonTransportError.getDAPIAddress()).to.deep.equal(dapiAddress); + expect(jsonTransportError.getCode()).to.equal(statusCode); + expect(jsonTransportError.getData()).to.deep.equal({}); + expect(jsonTransportError.message).to.equal(error.message); + }); + + it('should return ResponseError on JsonRpcError', () => { + const jsonRpcError = { + code: -32000, + message: 'error message', + data: { + info: 'some data', + }, + }; + + const error = new JsonRpcError(requestInfo, jsonRpcError); + + const jsonTransportError = createJsonTransportError(error, dapiAddress); + + expect(jsonTransportError).to.be.an.instanceOf(ResponseError); + expect(jsonTransportError.getDAPIAddress()).to.deep.equal(dapiAddress); + expect(jsonTransportError.getCode()).to.equal(jsonRpcError.code); + expect(jsonTransportError.getData()).to.deep.equal(jsonRpcError.data); + expect(jsonTransportError.message).to.equal(error.message); + }); + + it('should return RetriableResponseError on JsonRpcError', () => { + const jsonRpcError = { + code: -32603, + message: 'error message', + data: { + info: 'some data', + }, + }; + + const error = new JsonRpcError(requestInfo, jsonRpcError); + + const jsonTransportError = createJsonTransportError(error, dapiAddress); + + expect(jsonTransportError).to.be.an.instanceOf(RetriableResponseError); + expect(jsonTransportError.getDAPIAddress()).to.deep.equal(dapiAddress); + expect(jsonTransportError.getCode()).to.equal(jsonRpcError.code); + expect(jsonTransportError.getData()).to.deep.equal(jsonRpcError.data); + expect(jsonTransportError.message).to.equal(error.message); + }); + + it('should return ResponseError', () => { + const error = new Error('Unknown error'); + error.code = 'UNKNOWN'; + + const jsonTransportError = createJsonTransportError(error, dapiAddress); + + expect(jsonTransportError).to.be.an.instanceOf(ResponseError); + expect(jsonTransportError.getDAPIAddress()).to.deep.equal(dapiAddress); + expect(jsonTransportError.getCode()).to.equal(error.code); + expect(jsonTransportError.getData()).to.deep.equal({}); + expect(jsonTransportError.message).to.equal(error.message); + }); + + it('should return RetriableResponseError', () => { + const error = new Error('Aborted'); + error.code = 'ECONNABORTED'; + + const jsonTransportError = createJsonTransportError(error, dapiAddress); + + expect(jsonTransportError).to.be.an.instanceOf(RetriableResponseError); + expect(jsonTransportError.getDAPIAddress()).to.deep.equal(dapiAddress); + expect(jsonTransportError.getCode()).to.equal(error.code); + expect(jsonTransportError.getData()).to.deep.equal({}); + expect(jsonTransportError.message).to.equal(error.message); + }); +}); diff --git a/packages/js-dapi-client/test/unit/transport/JsonRpcTransport/requestJsonRpc.spec.js b/packages/js-dapi-client/test/unit/transport/JsonRpcTransport/requestJsonRpc.spec.js new file mode 100644 index 00000000000..dcedc80ad20 --- /dev/null +++ b/packages/js-dapi-client/test/unit/transport/JsonRpcTransport/requestJsonRpc.spec.js @@ -0,0 +1,171 @@ +const axios = require('axios'); +const requestJsonRpc = require('../../../../lib/transport/JsonRpcTransport/requestJsonRpc'); +const JsonRpcError = require('../../../../lib/transport/JsonRpcTransport/errors/JsonRpcError'); +const WrongHttpCodeError = require('../../../../lib/transport/JsonRpcTransport/errors/WrongHttpCodeError'); + +describe('requestJsonRpc', () => { + let host; + let port; + let timeout; + let params; + + beforeEach(function beforeEach() { + host = 'localhost'; + port = 80; + params = { data: 'test' }; + timeout = 1000; + + const options = { timeout }; + + const url = `http://${host}:${port}`; + const payload = { + jsonrpc: '2.0', + params, + id: 1, + }; + + const axiosStub = this.sinon.stub(axios, 'post'); + + axiosStub + .withArgs( + url, + { ...payload, method: 'shouldPass' }, + options, + ) + .resolves({ status: 200, data: { result: 'passed', error: null } }); + + axiosStub + .withArgs( + `https://${host}`, + { ...payload, method: 'httpsRequest' }, + options, + ) + .resolves({ status: 200, data: { result: 'passed', error: null } }); + + axiosStub + .withArgs( + url, + { ...payload, method: 'wrongData' }, + options, + ) + .resolves({ status: 400, data: { result: null, error: { message: 'Wrong data' } }, statusMessage: 'Status message' }); + + axiosStub + .withArgs( + url, + { ...payload, method: 'invalidData' }, + options, + ) + .resolves({ status: 200, data: { result: null, error: { message: 'invalid data' } } }); + + axiosStub + .withArgs( + url, + { ...payload, method: 'errorData' }, + { timeout: undefined }, + ) + .resolves({ status: 200, data: { result: null, error: { message: 'Invalid data for error.data', data: 'additional data here', code: -1 } } }); + }); + + afterEach(() => { + axios.post.restore(); + }); + + it('should make rpc request and return result', async () => { + const result = await requestJsonRpc( + host, + port, + 'shouldPass', + params, + { timeout }, + ); + + expect(result).to.equal('passed'); + }); + + it('should make https rpc request and return result', async () => { + port = 443; + + const result = await requestJsonRpc( + host, + port, + 'httpsRequest', + params, + { timeout }, + ); + + expect(result).to.equal('passed'); + }); + + it('should throw WrongHttpCodeError if response status is not 200', async () => { + const method = 'wrongData'; + const options = { timeout }; + + try { + await requestJsonRpc( + host, + port, + method, + params, + options, + ); + + expect.fail('should throw error'); + } catch (e) { + expect(e).to.be.an.instanceOf(WrongHttpCodeError); + expect(e.message).to.equal('DAPI JSON RPC wrong http code: Status message'); + expect(e.getCode()).to.equal(400); + expect(e.getRequestInfo()).to.deep.equal({ + host, + port, + method, + params, + options, + }); + } + }); + + it('should throw error if there is an error object in the response body', async () => { + try { + await requestJsonRpc( + host, + port, + 'invalidData', + params, + { timeout }, + ); + + expect.fail('should throw error'); + } catch (e) { + expect(e.message).to.equal('invalid data'); + } + }); + + it('should throw error if there is an error object with data in the response body', async () => { + const method = 'errorData'; + + try { + await requestJsonRpc( + host, + port, + method, + params, + ); + + expect.fail('should throw error'); + } catch (e) { + expect(e).to.be.an.instanceof(JsonRpcError); + expect(e.message).to.equal('Invalid data for error.data'); + expect(e.getRequestInfo()).to.deep.equal({ + host, + port, + method, + params, + options: {}, + }); + expect(e.getMessage()).to.equal('Invalid data for error.data'); + expect(e.getData()).to.equal('additional data here'); + expect(e.getCode()).to.equal(-1); + } + }); +}); diff --git a/packages/js-dapi-client/webpack.config.js b/packages/js-dapi-client/webpack.config.js new file mode 100644 index 00000000000..09f205ce600 --- /dev/null +++ b/packages/js-dapi-client/webpack.config.js @@ -0,0 +1,48 @@ +const path = require('path'); +const webpack = require('webpack'); + +const commonJSConfig = { + entry: ['core-js/stable', './lib/DAPIClient.js'], + mode: 'production', + resolve: { + fallback: { + fs: false, + http: false, + https: false, + crypto: require.resolve('crypto-browserify'), + buffer: require.resolve('buffer/'), + assert: require.resolve('assert-browserify'), + util: require.resolve('util/'), + stream: require.resolve('stream-browserify'), + path: require.resolve('path-browserify'), + url: require.resolve('url/'), + events: require.resolve('events/'), + string_decoder: require.resolve('string_decoder/'), + }, + }, + plugins: [ + new webpack.ProvidePlugin({ + Buffer: [require.resolve('buffer/'), 'Buffer'], + process: require.resolve('process/browser'), + }), + ], + module: { + rules: [ + { + test: /\.js$/, + exclude: /(node_modules)/, + use: { + loader: 'babel-loader', + }, + }, + ], + }, + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'dapi-client.min.js', + library: 'DAPIClient', + libraryTarget: 'umd', + }, +}; + +module.exports = [commonJSConfig]; diff --git a/packages/js-dash-sdk/.env.example b/packages/js-dash-sdk/.env.example new file mode 100644 index 00000000000..2259f834ef5 --- /dev/null +++ b/packages/js-dash-sdk/.env.example @@ -0,0 +1,4 @@ +DAPI_SEED= +FAUCET_PRIVATE_KEY= +NETWORK= +DPNS_CONTRACT_ID= diff --git a/packages/js-dash-sdk/.gitignore b/packages/js-dash-sdk/.gitignore new file mode 100644 index 00000000000..c6e29fc220c --- /dev/null +++ b/packages/js-dash-sdk/.gitignore @@ -0,0 +1,4 @@ +build +dist + + diff --git a/packages/js-dash-sdk/.mocharc.yml b/packages/js-dash-sdk/.mocharc.yml new file mode 100644 index 00000000000..267ac0c19db --- /dev/null +++ b/packages/js-dash-sdk/.mocharc.yml @@ -0,0 +1,4 @@ +exit: true +timeout: 5000 +file: + - ./src/test/bootstrap.js diff --git a/packages/js-dash-sdk/.npmignore b/packages/js-dash-sdk/.npmignore new file mode 100644 index 00000000000..9079f9e663b --- /dev/null +++ b/packages/js-dash-sdk/.npmignore @@ -0,0 +1,6 @@ +node_modules + +.DS_Store + +# Ultra runner build cache +.ultra.cache.json diff --git a/packages/js-dash-sdk/CHANGELOG.md b/packages/js-dash-sdk/CHANGELOG.md new file mode 100644 index 00000000000..c2f87878ca9 --- /dev/null +++ b/packages/js-dash-sdk/CHANGELOG.md @@ -0,0 +1,373 @@ +# [3.21.2](https://github.com/dashevo/DashJS/compare/v3.21.1...v3.21.2) (2021-10-26) + + +### Chores + +* update DPNS and DashPay contract ids ([#292](https://github.com/dashevo/DashJS/issues/292)) + + + +# [3.21.1](https://github.com/dashevo/DashJS/compare/v3.21.0...v3.21.1) (2021-10-26) + + +### Chores + +* update DPNS and DashPay contract ids ([#290](https://github.com/dashevo/DashJS/issues/290)) + + + +# [3.21.0](https://github.com/dashevo/DashJS/compare/v3.20.3...v3.21.0) (2021-10-21) + + +### Features + +* **Document:** throw error on null Data Сontract ([#244](https://github.com/dashevo/DashJS/issues/244)) +* support higher protocol version ([#271](https://github.com/dashevo/DashJS/issues/271)) +* add `driveProtocolVersion` option ([#249](https://github.com/dashevo/DashJS/issues/249)) +* convenient broadcast errors ([#262](https://github.com/dashevo/DashJS/issues/262), [#266](https://github.com/dashevo/DashJS/issues/266), [#273](https://github.com/dashevo/DashJS/issues/273)) + +### BREAKING CHANGE + +`StateTransitionBroadcastError#getData` is removed. Use `StateTransitionBroadcastError#getCause` to get additional details. + + + +## [3.20.3](https://github.com/dashevo/DashJS/compare/v3.20.2...v3.20.3) (2021-08-08) + +### Chores + +* update DashPay and DPNS contracts ([#240](https://github.com/dashevo/DashJS/issues/240)) + + + +## [3.20.2](https://github.com/dashevo/DashJS/compare/v3.20.1...v3.20.2) (2021-07-28) + + +### Bug Fixes + +* InvalidResponse error when connecting to older versions of dapi ([#241](https://github.com/dashevo/DashJS/issues/241)) + + + +## [3.20.1](https://github.com/dashevo/DashJS/compare/v3.20.0...v3.20.1) (2021-07-22) + + +### Features + +* update DashPay and DPNS contracts ([4a0f0f8](https://github.com/dashevo/DashJS/commit/4a0f0f84a37fd6f41aa75c718ddd8a5ebc43c452)) + + + +# [3.20.0](https://github.com/dashevo/DashJS/compare/v3.19.4...v3.20.0) (2021-07-13) + + +### Features + +* provide metadata for documents, identities and data contracts ([#231](https://github.com/dashevo/DashJS/issues/231)) +* update wallet options ([#229](https://github.com/dashevo/DashJS/issues/229)) + + +### BREAKING CHANGES + +* `wallet.defaultAccountIndex` should be used instead of `walletAccountIndex` option + + + +## [3.19.4](https://github.com/dashevo/DashJS/compare/v3.19.3...v3.19.4) (2021-05-18) + + +### Bug Fixes + +* Binary properties in platform queries not encoded properly ([#223](https://github.com/dashevo/js-dash-sdk/pull/223)) + +## [3.19.3](https://github.com/dashevo/DashJS/compare/v3.19.2...v3.19.3) (2021-05-18) + + +### Bug Fixes + +* CBOR not decoding buffers properly in browsers([#219](https://github.com/dashevo/js-dash-sdk/pull/219)) + + +## [3.19.2](https://github.com/dashevo/DashJS/compare/v3.19.1...v3.19.2) (2021-05-18) + + +### Bug Fixes + +* add some handler for metadata error ([#216](https://github.com/dashevo/DashJS/issues/216)) + + + +## [3.19.1](https://github.com/dashevo/DashJS/compare/v3.19.0...v3.19.1) (2021-05-10) + + +### Bug Fixes + +* invalid testnet DPNS contract id ([#214](https://github.com/dashevo/DashJS/issues/214)) + + + +# [3.19.0](https://github.com/dashevo/DashJS/compare/v3.18.2...v3.19.0) (2021-05-04) + +### Features + +* add `verifyInstantLock` to state repository ([#193](https://github.com/dashevo/DashJS/issues/193)) +* Chain Asset Lock proof ([#203](https://github.com/dashevo/DashJS/issues/203)) +* update to new getStatus endpoint ([#205](https://github.com/dashevo/DashJS/issues/205)) +* remove fake Instant Asset Locks proofs ([#198](https://github.com/dashevo/DashJS/issues/198)) + + +### BREAKING CHANGES + +* Fallbacks for Instant Asset lock proofs are removed. SDK is not compatible with local network created with mn-bootstrap v0.18 +* See [DPP breaking changes](https://github.com/dashevo/js-dpp/releases/tag/v0.19.0) +* See [Wallet lib breaking changes](https://github.com/dashevo/wallet-lib/releases/tag/v7.19.0) + + + +## [3.18.2](https://github.com/dashevo/DashJS/compare/v3.18.1...v3.18.2) (2021-04-28) + +### Bug Fixes + +* `transaction.isCoinbase` is not a function and other fixes from wallet-lib 7.18.1 ([#236](https://github.com/dashevo/wallet-lib/issues/236)) + + + +## [3.18.1](https://github.com/dashevo/DashJS/compare/v3.18.0...v3.18.1) (2021-03-03) + +### Features + +* handle wallet async errors ([#190](https://github.com/dashevo/DashJS/issues/190)) + + + +# [3.18.0](https://github.com/dashevo/DashJS/compare/v3.18.0...v3.17.0) (2021-03-03) + +### Features + +* make identity derivation DIP-11 compatible ([#188](https://github.com/dashevo/DashJS/issues/188) +* more reliable and secure ST ack ([#180](https://github.com/dashevo/DashJS/issues/180)) + + +### Bug Fixes + +* ensure name search uses lowercase label ([#183](https://github.com/dashevo/DashJS/issues/183)) + + +### BREAKING CHANGES + +* Identities registered in versions prior to that one won't sync, as identities are now using hardened derivation and derived from the wallet, not from the account. This change is made to make JS SDK compatible with Android and iOS apps and DIP 11. + + + +# [3.17.0](https://github.com/dashevo/DashJS/compare/v3.16.2...v3.17.0) (2020-12-30) + + +### Features + +* connect to testnet by default ([#176](https://github.com/dashevo/DashJS/issues/176)) +* update `dapi-client`, `dashcore-lib`, `dpp` and `wallet-lib` ([#152](https://github.com/dashevo/DashJS/issues/152), [#172](https://github.com/dashevo/DashJS/issues/172)) +* wait broadcasted data to be available from queries ([#165](https://github.com/dashevo/DashJS/issues/165)) +* asset lock proofs for identity funding ([#169](https://github.com/dashevo/js-dash-sdk/issues/169)) + + +### Bug Fixes + +* crypto.randomBytes stub causing tests to fail ([#173](https://github.com/dashevo/DashJS/issues/173)) + + +### BREAKING CHANGES + +* requires a version of DAPI with instant locks implemented, i.e. 0.17 or higher + + + +## [3.16.2](https://github.com/dashevo/DashJS/compare/v3.16.1...v3.16.2) (2020-11-17) + + +### Bug Fixes + +* cannot read property 'getBinaryProperties' of undefined ([#158](https://github.com/dashevo/DashJS/issues/158)) + + + +## [3.16.1](https://github.com/dashevo/DashJS/compare/v3.16.0...v3.16.1) (2020-10-30) + + +### Bug Fixes + +* `$id` and `$ownerId` are not converted to identifiers ([#148](https://github.com/dashevo/DashJS/issues/148)) + + + +# [3.16.0](https://github.com/dashevo/DashJS/compare/v3.15.2...v3.16.0) (2020-10-29) + +### Features + +* convert string identifiers in `where` conditions ([#145](https://github.com/dashevo/DashJS/issues/145)) +* make `broadcast` methods to return a state transition ([#146](https://github.com/dashevo/DashJS/issues/146)) +* introduce Identifier type for data contract, document and identity IDs ([#142](https://github.com/dashevo/DashJS/issues/142)) + + +### BREAKING CHANGES + +* `client.platform.contracts.broadcast` returns a `DataContractCreateTransition` instead of `DataContract` +* `client.platform.documents.broadcast` returns a `DocuemntsBatchTransition` instead of `Documents[]` +* `client.apps` is an instance of `ClientApps` class. Use `Client#getApps()` to get/update applications + + + +## [3.15.2](https://github.com/dashevo/DashJS/compare/v3.15.1...v3.15.2) (2020-09-14) + + +### Bug Fixes + +* Update wallet-lib to a version with sync process fixes ([#185](https://github.com/dashevo/wallet-lib/pull/185)) + + + +## [3.15.1](https://github.com/dashevo/DashJS/compare/v3.15.0...v3.15.1) (2020-09-07) + + +### Bug Fixes + +* invalid argument type: script error from dashcore-lib ([#138](https://github.com/dashevo/DashJS/issues/138)) + + + +# [3.15.0](https://github.com/dashevo/DashJS/compare/v3.14.1...v3.15.0) (2020-09-04) + + +### Features + +* update to new Wallet and DPNS contract ([#127](https://github.com/dashevo/js-dash-sdk/pull/127)) + + +### BREAKING CHANGES + +* `client.platform.names.register` now receive records as a second argument +* See [DPP breaking changes](https://github.com/dashevo/js-dpp/releases/tag/v0.15.0) + + +## [3.14.1](https://github.com/dashevo/DashJS/compare/v3.14.0...v3.14.1) (2020-07-24) + + +### Bug Fixes + +* outdated DPNS contract ID ([#118](https://github.com/dashevo/DashJS/issues/118)) + + + +# [3.14.0](https://github.com/dashevo/DashJS/compare/v3.13.4...v3.14.0) (2020-07-23) + + +### Features + +* implement DPNS methods ([#92](https://github.com/dashevo/DashJS/issues/92)) +* TypeScript compilation without webpack ([#97](https://github.com/dashevo/DashJS/issues/97), [#107](https://github.com/dashevo/DashJS/issues/107)) +* integrate with new DAPI Client and Wallet transport ([#105](https://github.com/dashevo/DashJS/issues/105), [#110](https://github.com/dashevo/DashJS/issues/110)) +* update DPP to 0.14.0 ([#112](https://github.com/dashevo/DashJS/issues/112)) +* use test-suite to run platform tests ([#106](https://github.com/dashevo/DashJS/issues/106)) + + +### Documentation + +* update documentation and definitions files ([#99]((https://github.com/dashevo/DashJS/issues/99)) + + +### BREAKING CHANGES + +* `seeds` option now is an array of DAPI addresses, that can be represented as a string, plan JS object (host, httpPort, grpcPort) or DAPIAddress instance +* see [DPP v0.14 breaking changes](https://github.com/dashevo/js-dpp/releases/tag/v0.14.0) +* `client.platform.names.get` method has been removed in favor of `client.platform.names.resolve` + + + +## [3.13.4](https://github.com/dashevo/DashJS/compare/v3.13.3...v3.13.4) (2020-07-01) + + +### Features + +* update Wallet and DashCore libs ([#95](https://github.com/dashevo/DashJS/issues/95)) + + + +## [3.13.3](https://github.com/dashevo/DashJS/compare/v3.13.2...v3.13.3) (2020-06-15) + +- **Features:** + * Updated wallet-lib to [7.13.3](https://github.com/dashevo/wallet-lib/blob/master/CHANGELOG.md#7133-2020-06-16) + * Updated js-dpp to [0.13.1](https://github.com/dashevo/js-dpp/blob/master/CHANGELOG.md#0131-2020-06-15) + +- **Bug fixes:** + * fix: wrong assetlock tx fee estimation (#85) + * fix: generate one-time private key for the asset lock transaction (#86) + +## [3.13.2](https://github.com/dashevo/DashJS/compare/v3.13.1...v3.13.2) (2020-06-12) + +- **Bug fixes:** + * more than one identity registration failed ([#83](https://github.com/dashevo/DashJS/issues/83)) + +## [3.13.1](https://github.com/dashevo/DashJS/compare/v3.13.0...v3.13.1) (2020-05-12) + +- **Features:** + * identity topups ([#71](https://github.com/dashevo/DashJS/pull/71)) + +# [3.13.0](https://github.com/dashevo/DashJS/compare/v3.0.2...v3.13.0) (2020-05-11) + +- **feat:** + - feat: update wallet lib to 7.1.4 (#80) + +# [3.0.2](https://github.com/dashevo/DashJS/compare/v3.0.1...v3.0.2) (2020-05-06) + +- **fix**: + - typescript support (#46) + +# [3.0.1](https://github.com/dashevo/DashJS/compare/v3.0.0...v3.0.1) (2020-04-27) + +- **fix**: + - changed dpp.documents (undefined) to dpp.document (#48) + +# [3.0.0](https://github.com/dashevo/DashJS/compare/v2.0.0...v3.0.0) (2020-04-24) + +- **breaking:** + - Identity registration will use HDKeys(0) instead 1 (https://github.com/dashevo/DashJS/pull/41/commits/4bbc54d265c679affbd043b03a88f8ed2f1d52fb) + - contract.broadcast() now returns dataContract (https://github.com/dashevo/DashJS/pull/41/commits/6f7e9225f317525388fb7701619da74b5d76222b#diff-486b5234782255b516fe9c1868c7d3b0R19) + - identities.broadcast() now return identity (https://github.com/dashevo/DashJS/pull/41/commits/4bbc54d265c679affbd043b03a88f8ed2f1d52fb#diff-27f47e1bd838b3993aed5eaa396a00e5R90) + - document.broadcast() creation is now performed via passing documents to be created in an array of property create. `{create:[document]}` (https://github.com/dashevo/DashJS/commit/91127d774a339c4204891f5863c91a64d521ddb8#diff-0202b3d53936b94585a8c0cfa0481bccR10) + +- **feat**: + - added replacement of a document. (#41) + - added deletion of a document (#41) + +- **impr**: + - update to dpp 0.12 (#41) + +- **fix**: + - properly release (throw) catched error (#41) + +- **Chore, Docs & Tests:** + - bumped wallet-lib to 6.1 (#41) + +# [2.0.0](https://github.com/dashevo/DashJS/compare/v1.1.2...v2.0.0) (2020-03-27) + +- **breaking:** + - renamed DashJS namespace to SDK namespace. + - renamed SDK namespace to Client namespace (DashJS.SDK -> SDK.Client). + - moved L1 primitive namespace from `SDK.*` to `SDK.Core.*`. + - moved L2 primitive namespace from `SDK.*` to `SDK.Platform.*`. + - exported file for web environment is now `Dash` instead of `DashJS` +- **feat**: + - Sign and verify message (#24) +- **impr**: + - Typings documentation (#30) + - Code cleanup (#31) + - Export all Dashcore Primitives (under `SDK.Core.*`) +- **fix**: + - fix(contracts): pass data contract without array (#32 in #31) + - fix: remove .serialize() before broadcasting records (#e047d515a12d0d14ff69b4fe3ea5b8b10bd6f890) + - Identity/register: updated getUTXOS usages on (#afda5bbafb940b2e15d5e773d0e8fc5fbc48ee13) + - fix(StateTransitionBuilder): records type detection (#b49f74b4b8e03e9d1020dd789c62f4310a4fc1ad) + - broadcasting of contracts (#f4b63e6be692841f1b138e5b058e531a0873f456, #4aa31fec0e5579d7ef8b9222576863a069b95fd3) +- **chore**: + - updated for new evonet (updated wallet-lib to 6.0.0) + - updated dapi-client to 0.11 (#fba4d55d3281bec5e65605787dd23a6ca3517476) + - updated DPNS contractID on evonet (#d0cf11d30cf7c9aaef1ffa4a2b8a955fbf5b1184) diff --git a/packages/js-dash-sdk/CODE_OF_CONDUCT.md b/packages/js-dash-sdk/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..ed4e1f730fc --- /dev/null +++ b/packages/js-dash-sdk/CODE_OF_CONDUCT.md @@ -0,0 +1,42 @@ +Code of Conduct +=============== + +This is an open-source repository that intends to provide valuable features to users and developers. +As such, we remember that we were all juniors and therefore we expect all contributors to be welcoming to contributions being made and improvements being proposed. We expect contributors to follow the same rules. +This is a virtual workplace, where we interact in ways that contribute to a healthy community, and we will maintain this repository as a harassment-free experience. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Giving and gracefully accepting constructive feedback +* Being welcoming to contributors +* Being respectful of differing viewpoints and experiences +* Focusing on what is best not just for us as individuals, but for the overall community + +Examples of unacceptable behavior by participants include: + +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [alex@dash.org](mailto:alex@dash.org). All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the reporter of any incident. + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. diff --git a/packages/js-dash-sdk/CONTRIBUTING.md b/packages/js-dash-sdk/CONTRIBUTING.md new file mode 100644 index 00000000000..7226fa18a78 --- /dev/null +++ b/packages/js-dash-sdk/CONTRIBUTING.md @@ -0,0 +1,67 @@ +Contributing to Dash SDK +====================== + +First off, thanks for taking the time to contribute! + +The following is a set of guidelines for contributing. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. + +#### Table Of Contents + +1. [Code of Conduct](#code-of-conduct) +1. [Styleguides](#styleguides) + + [Code](#code) + + [Conventional commits](#conventional-commits) + + [Issues](#issues) + + [Pull Requests](#pull-requests) + + +## Code of Conduct + +This project and everyone participating in it is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). +By participating, you are expected to uphold this code. Please report unacceptable behavior to [alex@dash.org](mailto:alex@dash.org). + +## Styleguides + +#### Code + +* Try to write your code following the style in the repo already. +* Comply with the standard being setup (example : ESLint, prettier,...) + +#### Conventional Commits + +A valid PR's commits and title are expected to comply with the conventional commits standard. The valid types are : + +- **feat** (Features): Used for a new feature being implemented +- **fix** (Bug Fixes): Used for bug fixes +- **impr** (Improvements): Used to describe an improvement to an existing feature (format can be description this) +- **docs** (Documentation): Changes happening on the documentation files +- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) +- **refact** (Code Refactoring): A code change that neither fixes a bug nor adds a feature +- **perf** (Performance Improvements): A code change that improves performance +- **ci** (Continuous Integrations) Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs) +- **chore**: Other changes that don't modify src or test files +- **revert**: Reverts a previous commit +- **test**: Adding missing tests or correcting existing tests +- **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm) + +Examples : + +- feat: added new Document parsing module +- impr(Document): parsing handle schema validation +- fix(Document): parsing leak memory fixed + +Please remember that we depend on commit titles to follow changes and research previous modifications. +Don't hesitate to use commit messages to explain more about the changes. + +#### Issues + +* Demonstrating the issue by creating a JSFiddle (you can inherit Dash SDK from unpkg) is definitely welcome. +* **Use a clear and descriptive title** for the issue to identify the suggestion. +* **Provide a comprehensive description of the suggested enhancement** in as much detail as possible. (a template is automatically generated for you when creating an issue / pr) +* (If applicable) **Provide specific examples to demonstrate the steps**. + +#### Pull Requests + +* **Use a clear and descriptive title** for the issue to identify the suggestion. +* Include any relevant issue numbers in the PR body, not the title. +* **Provide a comprehensive description of all changes made.** diff --git a/packages/js-dash-sdk/LICENSE b/packages/js-dash-sdk/LICENSE new file mode 100644 index 00000000000..cc11e7456af --- /dev/null +++ b/packages/js-dash-sdk/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Dash Evolution + +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/packages/js-dash-sdk/README.md b/packages/js-dash-sdk/README.md new file mode 100644 index 00000000000..87319fcfbd6 --- /dev/null +++ b/packages/js-dash-sdk/README.md @@ -0,0 +1,124 @@ +# Dash SDK + +[![NPM Version](https://img.shields.io/npm/v/dash)](https://www.npmjs.org/package/dash) +[![Build Status](https://github.com/dashevo/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashevo/platform/actions/workflows/release.yml) +[![Release Date](https://img.shields.io/github/release-date/dashevo/platform)](https://github.com/dashevo/platform/releases/latest) +[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen)](https://github.com/RichardLitt/standard-readme) + +Dash library for JavaScript/TypeScript ecosystem (Wallet, DAPI, Primitives, BLS, ...) + +Dash library allows you to connect to DAPI and receive or broadcast payments on the Dash Network, manage identifies, register data contracts, retrieve or submit documents on the Dash Platform, all within a single library. + +## Table of Contents +- [Install](#install) +- [Usage](#usage) +- [Dependencies](#dependencies) +- [Documentation](#documentation) +- [Contributing](#contributing) +- [License](#license) + +## Install + +### ES5/ES6 via NPM + +In order to use this library, you will need to add it to your project as a dependency. + +Having [NodeJS](https://nodejs.org/) installed, just type : `npm install dash` in your terminal. + +```sh +npm install dash +``` + + +### CDN Standalone + +For browser usage, you can also directly rely on unpkg : + +``` + +``` + +## Usage + +```js +const Dash = require("dash"); // or import Dash from "dash" + +const client = new Dash.Client({ + network: "testnet", + wallet: { + mnemonic: "arena light cheap control apple buffalo indicate rare motor valid accident isolate", + }, +}); + +// Accessing an account allow you to transact with the Dash Network +client.getWalletAccount().then(async (account) => { + console.log("Funding address", account.getUnusedAddress().address); + + const balance = account.getConfirmedBalance(); + console.log("Confirmed Balance", balance); + + if(balance > 0){ + // Creating an identity is the basis of all interactions with the Dash Platform + const identity = await client.platform.identities.register() + + // Prepare a new document containing a simple hello world sent to a hypothetical tutorial contract + const document = await platform.documents.create( + 'tutorialContract.note', + identity, + { message: 'Hello World' }, + ); + + // Broadcast the document into a new state transition + await platform.documents.broadcast({create:[document]}, identity); + } +}); +``` + +### Primitives and essentials +Dash SDK bundled into a standalone package, +so that the end user never have to worry about mananaging polyfills or related dependencies + +```javascript +const Dash = require('dash') + +const { + Essentials: { + Buffer // Node.JS Buffer polyfill. + }, + Core: { // @dashevo/dashcore-lib essentials + Transaction, + PrivateKey, + BlockHeader, + }, + PlatformProtocol: { // @dashevo/dpp essentials + Identity, + Identifier, + }, + WalletLib: { // @dashevo/wallet-lib essentials + EVENTS + }, + DAPIClient, // @dashevo/dapi-client +} = Dash; +``` + +## Dependencies + +The Dash SDK works using multiple dependencies that might interest you: +- [Wallet-Lib](https://github.com/dashevo/platform/tree/master/packages/wallet-lib) - Wallet management for handling, signing and broadcasting transactions (BIP-44 HD). +- [Dashcore-Lib](https://github.com/dashevo/dashcore-lib) - Provides the main L1 blockchain primitives (Block, Transaction,...). +- [DAPI-Client](https://github.com/dashevo/platform/tree/master/packages/js-dapi-client) - Client library for accessing DAPI endpoints. +- [DPP](https://github.com/dashevo/platform/tree/master/packages/js-dpp) - Implementation (JS) of Dash Platform Protocol. + +Some features might be more extensive in those libs, as Dash SDK only wraps around them to provide a single interface that is easy to use (and thus has less features). + +## Documentation + +More extensive documentation available at https://dashevo.github.io/platform/SDK/. + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs. + +## License + +[MIT](/LICENSE) © Dash Core Group, Inc. diff --git a/packages/js-dash-sdk/build-utils/ws.js b/packages/js-dash-sdk/build-utils/ws.js new file mode 100644 index 00000000000..826317a3a0a --- /dev/null +++ b/packages/js-dash-sdk/build-utils/ws.js @@ -0,0 +1,15 @@ +/** + * WebSocket shim for webpack browser builds + */ + +var ws; + +if (typeof WebSocket !== 'undefined') { + ws = WebSocket; +} else if (typeof MozWebSocket !== 'undefined') { + ws = MozWebSocket; +} else { + ws = window.WebSocket || window.MozWebSocket; +} + +module.exports = ws; diff --git a/packages/js-dash-sdk/docs/.nojekyll b/packages/js-dash-sdk/docs/.nojekyll new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/js-dash-sdk/docs/README.md b/packages/js-dash-sdk/docs/README.md new file mode 100644 index 00000000000..5ef7053d224 --- /dev/null +++ b/packages/js-dash-sdk/docs/README.md @@ -0,0 +1,82 @@ +# Dash SDK + +[![NPM Version](https://img.shields.io/npm/v/dash)](https://www.npmjs.org/package/dash) +[![Build Status](https://github.com/dashevo/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashevo/platform/actions/workflows/release.yml) +[![Release Date](https://img.shields.io/github/release-date/dashevo/platform)](https://github.com/dashevo/platform/releases/latest) +[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen)](https://github.com/RichardLitt/standard-readme) + +Dash library for JavaScript/TypeScript ecosystem (Wallet, DAPI, Primitives, BLS, ...) + +Dash library allows you to connect to DAPI and receive or broadcast payments on the Dash Network, manage identifies, register data contracts, retrieve or submit documents on the Dash Platform, all within a single library. + +## Install + +### Browser + +```html + +``` + +### Node + +In order to use this library, you will need to add our [NPM package](https://www.npmjs.com/dash) to your project. + +Having [NodeJS](https://nodejs.org/) installed, just type : + +```bash +npm install dash +``` + +## Usage + +```js +const Dash = require('dash'); + +const client = new Dash.Client({ + network: 'evonet', + wallet: { + mnemonic: 'arena light cheap control apple buffalo indicate rare motor valid accident isolate', + }, +}); + +// Accessing an account allows you to transact with the Dash Network +client.getWalletAccount().then(async (account) => { + console.log('Funding address', account.getUnusedAddress().address); + + const balance = account.getConfirmedBalance(); + console.log('Confirmed Balance', balance); + + if (balance > 0) { + // Creating an identity is the basis of all interactions with the Dash Platform + const identity = await client.platform.identities.register(); + + // Prepare a new document containing a simple hello world sent to a hypothetical tutorial contract + const document = await client.platform.documents.create( + 'tutorialContract.note', + identity, + { message: 'Hello World' }, + ); + + // Broadcast the document into a new state transition + await client.platform.documents.broadcast({ create: [document] }, identity); + } +}); +``` + +### Use-cases examples + +- [Generate a mnemonic](examples/generate-a-new-mnemonic.md) +- [Receive money and display balance](examples/receive-money-and-check-balance.md) +- [Pay to another address](examples/pay-to-another-address.md) +- [Use a local evonet](examples/use-local-evonet.md) +- [Publishing a new contract](examples/publishing-a-new-contract.md) +- [Use another BIP44 account](examples/use-different-account.md) + +### Tutorial + +- [Register an identity](https://dashplatform.readme.io/docs/tutorial-register-an-identity) +- [Register a Name for an Identity](https://dashplatform.readme.io/docs/tutorial-register-a-name-for-an-identity) + +## Licence + +[MIT](https://github.com/dashevo/dashjs/blob/master/LICENCE.md) © Dash Core Group, Inc. diff --git a/packages/js-dash-sdk/docs/_sidebar.md b/packages/js-dash-sdk/docs/_sidebar.md new file mode 100644 index 00000000000..85ef533138a --- /dev/null +++ b/packages/js-dash-sdk/docs/_sidebar.md @@ -0,0 +1,64 @@ +- Getting started + - [Quick start](getting-started/quickstart.md) + - [Quick introduction to core concepts](getting-started/core-concepts.md) + - [Working with multiple apps](getting-started/multiple-apps.md) + - [Dash Platform Applications](getting-started/dash-platform-applications.md) + - [About Schemas](getting-started/about-schemas.md) + - [With TypeScript](getting-started/with-typescript.md) + +- Examples + - [Generate a mnemonic](/examples/generate-a-new-mnemonic.md) + - [Receive money and display balance](/examples/receive-money-and-check-balance.md) + - [Pay to another address](/examples/pay-to-another-address.md) + - [Use a local evonet](/examples/use-local-evonet.md) + - [Fetch an identity from its name](/examples/fetch-an-identity-from-its-name.md) + - [Publishing new contract](/examples/publishing-a-new-contract.md) + - [Updating an existing contract](/examples/updating-a-contract.md) + - [Use another BIP44 account](/examples/use-different-account.md) + - [Sign and verify messages](/examples/sign-and-verify-messages.md) + +- Tutorial + - [Register an identity](https://dashplatform.readme.io/docs/tutorial-register-an-identity) + - [Register a Name for an Identity](https://dashplatform.readme.io/docs/tutorial-register-a-name-for-an-identity) + +- Snippets + - [Create and fund wallet](https://github.com/dashevo/DashJS/tree/master/examples/node/create-and-fund-wallet.js) + - [Register identity](https://github.com/dashevo/DashJS/tree/master/examples/node/register-identity.js) + - [Register name](https://github.com/dashevo/DashJS/tree/master/examples/node/register-name.js) + - [Retrieve Contract](https://github.com/dashevo/DashJS/tree/master/examples/node/retrieve-contract.js) + - [Retrieve Documents](https://github.com/dashevo/DashJS/tree/master/examples/node/retrieve-documents.js) + - [Retrieve Identity](https://github.com/dashevo/DashJS/tree/master/examples/node/retrieve-identity.js) + - [Retrieve Name](https://github.com/dashevo/DashJS/tree/master/examples/node/retrieve-name.js) + +- Usage + - [DAPI](usage/dapi.md) + - [Platform](platform/about-platform.md) + - [About platform](platform/about-platform.md) + - **Identities** + - [About identities](platform/identities/about-identity.md) + - [`.get()`](platform/identities/get.md) + - [`.register()`](platform/identities/register.md) + - [`.topUp()`](platform/identities/topUp.md) + - **Contracts** + - [About contracts](platform/contracts/about-contracts.md) + - [`.get()`](platform/contracts/get.md) + - [`.create()`](platform/contracts/create.md) + - [`.publish()`](platform/contracts/publish.md) + - [`.update()`](platform/contracts/update.md) + - **Documents** + - [About documents](platform/documents/about-documents.md) + - [`.get()`](platform/documents/get.md) + - [`.create()`](platform/documents/create.md) + - [`.broadcast()`](platform/documents/broadcast.md) + - **Names** + - [About DPNS](platform/names/about-dpns.md) + - [`.register()`](platform/names/register.md) + - [`.resolve()`](platform/names/resolve.md) + - [`.resolveByRecord()`](platform/names/resolveByRecord.md) + - [`.search()`](platform/names/search.md) + - [Wallet](wallet/about-wallet-lib.md) + - [Accounts](wallet/accounts.md) + - [Signing, Encrypt and Decrypt](wallet/signing-encrypt.md) + - [Dashcore Primitives](usage/dashcorelib-primitives.md) + +- [License](LICENSE) diff --git a/packages/js-dash-sdk/docs/examples/fetch-an-identity-from-its-name.md b/packages/js-dash-sdk/docs/examples/fetch-an-identity-from-its-name.md new file mode 100644 index 00000000000..96c79c72781 --- /dev/null +++ b/packages/js-dash-sdk/docs/examples/fetch-an-identity-from-its-name.md @@ -0,0 +1,22 @@ +## Fetching an identity from it's name + +Assuming you have created an identity and attached a name to it (see how to [register an identity](https://dashplatform.readme.io/docs/tutorial-register-an-identity) and how to [attach it to a name](https://dashplatform.readme.io/docs/tutorial-register-a-name-for-an-identity). +You will then be able to directly recover an identity from its names. See below: + +```js +const client = new Dash.Client({ + wallet: { + mnemonic: '', // Your app mnemonic, which holds the identity + }, +}); + +// This is the name previously registered in DPNS. +const identityName = 'alice'; +client.isReady().then(getIdentity); + +async function getIdentity() { + const {identities, names} = client.platform; + const identityId = (await names.get(identityName)).data.records.dashIdentity; + const identity = await identities.get(identityId); +} +``` diff --git a/packages/js-dash-sdk/docs/examples/generate-a-new-mnemonic.md b/packages/js-dash-sdk/docs/examples/generate-a-new-mnemonic.md new file mode 100644 index 00000000000..8c65e08bc97 --- /dev/null +++ b/packages/js-dash-sdk/docs/examples/generate-a-new-mnemonic.md @@ -0,0 +1,48 @@ +## Generate a new mnemonic + +In order to be able to keep your private keys private, we encourage to create your own mnemonic instead of using those from the examples (that might be empty). +Below, you will be proposed two options allowing you to create a new mnemonic, depending on the level of customisation you need. + +## Dash.Client + +By passing `null` to the mnemonic value of the wallet options, you can get Wallet-lib to generate a new mnemonic for you. + +```js +const Dash = require("dash"); +const client = new Dash.Client({ + network: "testnet", + wallet: { + mnemonic: null, + }, +}); +const mnemonic = client.wallet.exportWallet(); +console.log({mnemonic}); +``` + +## Dash.Mnemonic + +```js +const Dash = require("dash"); +const {Mnemonic} = Dash.Core; + +const mnemnonic = new Mnemonic.toString() +``` + +### Language selection + +```js +const {Mnemonic} = Dash.Core; +const {CHINESE, ENGLISH, FRENCH, ITALIAN, JAPANESE, SPANISH} = Mnemonic.Words; +console.log(new Mnemonic(Mnemonic.Words.FRENCH).toString()) +``` + +### Entropy size + +By default, the value for mnemonic is `128` (12 words), but you can generate a 24 words (or other) : + +```js +const {Mnemonic} = Dash.Core; +console.log(new Mnemonic(256).toString()) +``` + +You can even replace the word list by your own, providing a list of 2048 unique words. diff --git a/packages/js-dash-sdk/docs/examples/pay-to-another-address.md b/packages/js-dash-sdk/docs/examples/pay-to-another-address.md new file mode 100644 index 00000000000..142a05c5004 --- /dev/null +++ b/packages/js-dash-sdk/docs/examples/pay-to-another-address.md @@ -0,0 +1,25 @@ +## Paying to another address + +In order to pay, you need to have an [existing balance](../examples/receive-money-and-check-balance.md). +The below code will allow you to pay to a single address a specific amount of satoshis. + +```js +const Dash = require("dash"); +const mnemonic = ''// your mnemonic here. +const client = new Dash.Client({ + mnemonic, +}); + +client.isReady().then(payToRecipient); + +async function payToRecipient() { + const {account} = client; + const transaction = account.createTransaction({ + recipient:"yNPbcFfabtNmmxKdGwhHomdYfVs6gikbPf", + satoshis:10000 + }); + const transactionId = await account.broadcastTransaction(transaction); +} +``` + +See more on create [transaction options here](https://dashevo.github.io/wallet-lib/#/usage/account?id=create-transaction). diff --git a/packages/js-dash-sdk/docs/examples/publishing-a-new-contract.md b/packages/js-dash-sdk/docs/examples/publishing-a-new-contract.md new file mode 100644 index 00000000000..9c34dcd9218 --- /dev/null +++ b/packages/js-dash-sdk/docs/examples/publishing-a-new-contract.md @@ -0,0 +1,68 @@ +## Publishing a new contract + +Right now, Evonet does not support publishing a public contract. You will have to run a local instance or wait for updates to the [Dash Platform documentation](https://dashplatform.readme.io/docs) regarding how to run a devnet locally and publish a contract. + +For now you can try your luck using the [Dash Network Deploy tool](https://github.com/dashevo/dash-network-deploy) and refer to [how to use a local evonet](../examples/use-local-evonet.md). + +## Create your contract + +After having [registered an identity](https://dashplatform.readme.io/docs/tutorial-register-an-identity) +and [attached to a name](https://dashplatform.readme.io/docs/tutorial-register-a-name-for-an-identity) crafted your schema (you can see how on [about schemas](getting-started/about-schemas.md) and read the [DPNS schema](https://github.com/dashevo/dpns-contract/blob/v0.2-dev/src/schema/dpns-documents.json) as an example), you then can perform the below actions : + +```js +const schema = {};// You JSON schema defining the app. +const client = new Dash.Client({ + wallet: { + mnemonic: '', // Your app mnemonic, which holds the identity + }, +}); + +// This is the name previously registered in DPNS. +const appName = 'MyApp'; +client.isReady().then(registerContract); + +async function getIdentity(idName) { + const {identities, names} = client.platform; + const identityId = (await names.get(idName)).data.records.dashIdentity; + const identity = await identities.get(identityId); + return identity +} +async function registerContract() { + const {platform} = client; + const identity = await getIdentity(appName); + const contract = platform.contracts.create(schema, identity) + const contractId = await platform.contracts.publish(contract, identity); +} +``` + +## Fetch or publish documents on your app + +```js +const schema = {};// You JSON schema defining the app. + +// This is the name previously registered in DPNS. +const client = new Dash.Client({ + wallet: { + mnemonic: "", // Your app mnemonic, which holds the identity + }, + apps:{ + myapp:{ + contractId:""// The registered contract id + } + } +}); + +client.isReady().then(getDocuments); + +async function getDocuments() { + const {documents} = client.platform; + const docs = await documents.fetch('myapp.myfield',{}); +} + +async function publishDocument(){ + const identity = await getIdentity(appName); + const {documents} = client.platform; + const doc = await documents.create('myapp.myfield',identity, {myproperties:'my value'}); + await documents.broadcast(doc, identity) +} +``` diff --git a/packages/js-dash-sdk/docs/examples/receive-money-and-check-balance.md b/packages/js-dash-sdk/docs/examples/receive-money-and-check-balance.md new file mode 100644 index 00000000000..943be32ad1c --- /dev/null +++ b/packages/js-dash-sdk/docs/examples/receive-money-and-check-balance.md @@ -0,0 +1,76 @@ +## Receive money and display balance + +Initialize the SDK Client with your [generated mnemonic](../examples/generate-a-new-mnemonic.md) passed as an option. +By default, the SDK Client will work on Evonet, the only network having DAPI at the time of writing. + +```js +const Dash = require("dash"); +const mnemonic = ''// your mnemonic here. +const client = new Dash.Client({ + mnemonic, +}); +``` + +Having set up your `client` instance, you be able to access the `account` and `wallet` instance generated from your mnemonic. + +You can read more on [how to use a different account](../examples/use-different-account.md) as by default, you are on the first BIP44 account. + + +## Generate a receiving address + +In a client, you have two different type of payment address, `external`, which are those used to receive you money from outside. +And `internal`, which is used internally for the chance of a payment you do to someone. +For your privacy, you might want to generate a new address at each payment. + +```js + client.isReady().then(generateNewAddress); + async function generateNewAddress(){ + const {address} = client.account.getUnusedAddress(); + console.log(`New address: ${address}`) + } +``` + +This above code will generate a new unique (never used) address. + +## Display your balance + +There are three different balances, the `getTotalBalance()` that gives you the sum of `confirmed` and `unconfirmed` transactions (not included in a block). ``` +You probably most of the time want to rely of your confirmed balance when you check fund before a payment. +Value is in satoshis (smallest unit). + +```js + client.isReady().then(displayBalance); + async function displayBalance(){ + const balance = client.account.getConfirmedBalance(); + console.log(`Balance: ${balance}`) + } +``` + +Or you might want to look-up for the `.getUnconfirmedBalance()` to have only the unconfirmed amount. + +## Listen for event on received transaction + +When a new unconfirmed transaction is received, you can receive notice of it, to perform a validation on the address and perform an action if needed. + +```js + client.account.events.on('FETCHED/UNCONFIRMED_TRANSACTION', (data)=>{ + console.log('FETCHED/UNCONFIRMED_TRANSACTION'); + console.dir(data) + }); +``` + +The return element can be used in coordination with a `new Dash.Core.Transaction()` + +## Get an address + +In case you want to retrieve a specific address index : + +```js + const {address} = client.account.getAddress(2); +``` + +## Get an internal address + +```js + const {address} = client.account.getAddress(2, 'internal'); +``` diff --git a/packages/js-dash-sdk/docs/examples/sign-and-verify-messages.md b/packages/js-dash-sdk/docs/examples/sign-and-verify-messages.md new file mode 100644 index 00000000000..17974f6f61c --- /dev/null +++ b/packages/js-dash-sdk/docs/examples/sign-and-verify-messages.md @@ -0,0 +1,14 @@ +## Sign and verify messages + +Dash SDK exports the Message constructor inside the Core namespace `new Dash.Core.Message`. + +You can refer to its documentation : https://github.com/dashevo/dashcore-message/blob/master/README.md + +```js +const pk = new Dash.Core.PrivateKey(); +const message = new Dash.Core.Message('hello, world'); +const signed = account.sign(message, pk); +const verify = message.verify(pk.toAddress().toString(), signed.toString()); +``` + +See [code snippet](https://github.com/dashevo/platform/blob/master/packages/js-dash-sdk/examples/node/sign-and-verify-messages.js). diff --git a/packages/js-dash-sdk/docs/examples/updating-a-contract.md b/packages/js-dash-sdk/docs/examples/updating-a-contract.md new file mode 100644 index 00000000000..408ddd19df6 --- /dev/null +++ b/packages/js-dash-sdk/docs/examples/updating-a-contract.md @@ -0,0 +1,55 @@ +## Updating an existing contract + +To update your existing data contract you have to follow these steps: + +### Fetch your exising contract + +```js +const schema = {};// You JSON schema defining the app. +const client = new Dash.Client({ + wallet: { + mnemonic: '', // Your app mnemonic, which holds the identity + }, +}); + +const exisingContractId = ''; // Your existing data contract id + +const existingDataContract = await client.platform.contracts.get(exisingContractId); +``` + +### Update document definitions + +```js +// Update an existing document +const documentDefinition = existingDataContract.getDocumentDefinition('myDocumentType'); + +// adding optional field +documentDefinition.properties.newField = { + type: 'integer', + minimum: 1, +}; + +existingDataContract.setDocumentDefinition('myDocumentType', documentDefinition); + +// ... or, add a new one +const newDocumentDefinition = { + type: 'object', + properties: { + someField: { + type: 'integer', + minimum: 42, + }, + }, + required: ['someField'], +}; + +existingDataContract.setDocumentDefinition('myNewDocument', newDocumentDefinition); +``` + +### Broadcast your changes + +```js +await client.platform.contracts.update(existingDataContract, yourExistingIdentity); +``` + +**Note, that update will be only allowed if schema is backward compatible. Also, version incremented by 1 and only one of following fields updated: `$defs`, `documents` or `version`** diff --git a/packages/js-dash-sdk/docs/examples/use-different-account.md b/packages/js-dash-sdk/docs/examples/use-different-account.md new file mode 100644 index 00000000000..265936cb0af --- /dev/null +++ b/packages/js-dash-sdk/docs/examples/use-different-account.md @@ -0,0 +1,23 @@ +## Using a different account + +Because the Client uses mostly a mnemonic to initialize itself, you can access to the other account defined by the [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki). + +As an helper for users and internal reference for `client.platform`. +By default, accessing to `client.account` is equivalent of `client.wallet.getAccount({index:0})`. +Therefore usage might varies if you need to deal with platform or not. + + +### Access to account without platform +```js + const account = await client.getWalletAccount({index:1}); +``` + +### Access to account with platform. + +When calling `getWalletAccount`, the client will locally store the index options you have passed to it, which will be used for your platform related calls. + +```js +async function changeAccount(){ + await client.getWalletAccount({index:2}); +} +``` diff --git a/packages/js-dash-sdk/docs/examples/use-local-evonet.md b/packages/js-dash-sdk/docs/examples/use-local-evonet.md new file mode 100644 index 00000000000..37c4cfd7362 --- /dev/null +++ b/packages/js-dash-sdk/docs/examples/use-local-evonet.md @@ -0,0 +1,19 @@ +## Use a local evonet + +You can refer to https://github.com/dashevo/dash-network-deploy to deploy a devnet locally. + +You will then need to pass the seed ip, and [register the DPNS contract](https://github.com/dashevo/dpns-contract), and reference its `contractId` below. + +```js +const seeds = [{service: '54.245.133.124'}]; +const client = new Dash.Client({ + seeds, + apps: { + dpns: { + contractId: '77w8Xqn25HwJhjodrHW133aXhjuTsTv9ozQaYpSHACE3' + } + } +}); +``` + +After that, usage is the same. diff --git a/packages/js-dash-sdk/docs/getting-started/about-schemas.md b/packages/js-dash-sdk/docs/getting-started/about-schemas.md new file mode 100644 index 00000000000..ab39602f31f --- /dev/null +++ b/packages/js-dash-sdk/docs/getting-started/about-schemas.md @@ -0,0 +1,5 @@ +## About Schemas + +Schemas represents the application data structure, a JSON Schema language based set of rules that allows the creation of a Data Contract. + +You can read more in the [Dash Platform Documentation - Data contract section](https://dashplatform.readme.io/docs/explanation-platform-protocol-data-contract). diff --git a/packages/js-dash-sdk/docs/getting-started/core-concepts.md b/packages/js-dash-sdk/docs/getting-started/core-concepts.md new file mode 100644 index 00000000000..c17ad6bc162 --- /dev/null +++ b/packages/js-dash-sdk/docs/getting-started/core-concepts.md @@ -0,0 +1,28 @@ +## Core concepts + +The [Dash Core Developer Guide](https://dashcore.readme.io/docs/core-guide-introduction) will answer most of questions about the fundamentals of Dash. However, some elements provided by the SDK need to be grasped, so we will quickly cover some of those. + +## Wallet + +At the core of Dash is the Payment Chain. In order to be able to transact on it, one needs to have a set of [UTXOs](https://dashcore.readme.io/docs/core-guide-block-chain-transaction-data) that are controlled by a Wallet instance. + +In order to access your UTXO, you will have to provide a valid mnemonic that will unlock the Wallet and automatically fetch the associated UTXOs. + +When an SDK instance is created, you can access your wallet via the `client.wallet` variable, with the [wallet-lib Wallet doc](https://dashevo.github.io/wallet-lib/#/usage/wallet) + +## Account + +Since the introduction of deterministic wallets ([BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)), a Wallet is a representation of multiple accounts. + +It is the instance you will use most of the time for receiving or broadcasting payments. + +You can access your account with `client.getWalletAccount()` and see [how to use a different account](../examples/use-different-account.md) if you need to get a specific account index. + +## App Schema and Contracts + +The Dash Platform Chain, provides to developers the ability to create applications. Each application requires a set of rules and conditions describe in a portable document in the form of a JSON Schema. + +When registered, those applications schemas are called contracts and contains a contractId (namespace : `client.platform.contracts`). +By default, this library supports Dash Platform Name Service (DPNS) (to attach a name to an identity), under the namespace `client.platform.names` for testnet. + +You can read more on [how to use DPNS on a local Evonet devnet](../examples/use-local-evonet.md) or [how to use multiple apps](../getting-started/multiple-apps.md). diff --git a/packages/js-dash-sdk/docs/getting-started/dash-platform-applications.md b/packages/js-dash-sdk/docs/getting-started/dash-platform-applications.md new file mode 100644 index 00000000000..914e804244e --- /dev/null +++ b/packages/js-dash-sdk/docs/getting-started/dash-platform-applications.md @@ -0,0 +1,7 @@ +## DPNS + +DPNS is handled in the Dash SDK Client under the namespace `client.platform.names.*'`. [Read more here](../platform/names/about-dpns.md) + +## DashPay + +Registration of the contract on Evonet occurred in Q4 2020. Its functionality is not incorporated with the Dash SDK at this time. diff --git a/packages/js-dash-sdk/docs/getting-started/multiple-apps.md b/packages/js-dash-sdk/docs/getting-started/multiple-apps.md new file mode 100644 index 00000000000..15e6b220886 --- /dev/null +++ b/packages/js-dash-sdk/docs/getting-started/multiple-apps.md @@ -0,0 +1,23 @@ +# Working with multiple apps + +When working with other registered contracts, you will need to know their `contractId` and reference it in the SDK constructor. + +Assuming a contract DashPay has the following `contractId: "77w8Xqn25HwJhjodrHW133aXhjuTsTv9ozQaYpSHACE3"`. +You can then pass it as an option. + +```js +const client = new Dash.Client({ + apps: { + dashpay: { + contractId: '77w8Xqn25HwJhjodrHW133aXhjuTsTv9ozQaYpSHACE3' + } + } +}); +``` + +This allow the method `client.platform.documents.get` to provide you field selection. +Therefore, if the contract has a `profile` field that you wish to access, the SDK will allow you to use dot-syntax for access : + +```js +const bobProfile = await client.platform.documents.get('dashpay.profile', { name: 'bob' }); +``` diff --git a/packages/js-dash-sdk/docs/getting-started/quickstart.md b/packages/js-dash-sdk/docs/getting-started/quickstart.md new file mode 100644 index 00000000000..fbed9526a2e --- /dev/null +++ b/packages/js-dash-sdk/docs/getting-started/quickstart.md @@ -0,0 +1,63 @@ +# Quick start + +In order to use this library, you will need to add our [NPM package](https://www.npmjs.com/dash) to your project. + +Having [NodeJS](https://nodejs.org/) installed, just type : + +```bash +npm install dash +``` + +## Initialization + +Let's create a Dash SDK client instance specifying both our mnemonic and the schema we wish to work with. + +```js +const Dash = require('dash'); +const opts = { + apps: { + dashpay: { + contractId: '77w8Xqn25HwJhjodrHW133aXhjuTsTv9ozQaYpSHACE3', + }, + }, + wallet: { + mnemonic: "arena light cheap control apple buffalo indicate rare motor valid accident isolate", + }, +}; +const client = new Dash.Client(opts); +client.getWalletAccount().then(async (account) => { + // Do something +}) +``` + +Quick note : + +- If no mnemonic is provided, the sub-instance `client.Wallet` will not be initialized (writing capabilities of Dash Platform won't be usable). + +If you do not have a mnemonic, you can pass `null` to have one generated or omit that parameter to only use Dash.Client for `read-only` operations. + +## Make a payment + +```js +client.getWalletAccount().then(async (account) => { + const transaction = account.createTransaction({ + recipient: 'yixnmigzC236WmTXp9SBZ42csyp9By6Hw8', + amount: 0.12, + }); + account.broadcastTransaction(transaction); +}); +``` + +## Read a document + +At the time of writing, you will need to have registered a data contract yourself. See [publishing a new contract](../examples/publishing-a-new-contract.md). + +```js + +client.platform.documents.get( + 'tutorialContract.note', + { limit: 1 }, // Only retrieve 1 document +).then(async (documents) => { + console.log(documents); +}); +``` diff --git a/packages/js-dash-sdk/docs/getting-started/with-typescript.md b/packages/js-dash-sdk/docs/getting-started/with-typescript.md new file mode 100644 index 00000000000..f032ea953a8 --- /dev/null +++ b/packages/js-dash-sdk/docs/getting-started/with-typescript.md @@ -0,0 +1,31 @@ +In order to use Dash SDK with TypeScript. + +Create an index.ts file + +```js +import Dash from 'dash'; +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: null, // Will generate a new address, you should keep it. + }, +}; +const client = new Dash.Client(clientOpts); + +client.isReady().then(()=> console.log('isReady')); +``` + +Have a following `tsconfig.json` file + +```json +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "esModuleInterop": true + } +} +``` + +**Compile:** `tsc -p tsconfig.json` +**Run:** `node index.js` diff --git a/packages/js-dash-sdk/docs/index.html b/packages/js-dash-sdk/docs/index.html new file mode 100644 index 00000000000..7f388c62f89 --- /dev/null +++ b/packages/js-dash-sdk/docs/index.html @@ -0,0 +1,43 @@ + + + + + Dash - Dash client-side library for wallet payment/signing and application development in Javascript environments. + + + + + + + +
+ + + + + + diff --git a/packages/js-dash-sdk/docs/platform/about-platform.md b/packages/js-dash-sdk/docs/platform/about-platform.md new file mode 100644 index 00000000000..a180b0c18bd --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/about-platform.md @@ -0,0 +1,12 @@ +### About Dash Platform + +The Dash Platform provide a technology stack on top of the Dash Network allowing the creation of feature-rich decentralized application. + +You will learn more on the [Dash Platform Documentation - What is Dash Platform ?](https://dashplatform.readme.io/docs/introduction-what-is-dash-platform). + +### Platform components + +- DAPI: A decentralized API that is being run by all Masternode and offer a gRPC endpoints for retrieving payment chain metadata (block, transaction), aswell as application data (documents, contracts, identities). +- Drive : Application chain storage layer. Where the data defined by a Data Contract are managed. +- DPNS : A Naming serving + diff --git a/packages/js-dash-sdk/docs/platform/contracts/about-contracts.md b/packages/js-dash-sdk/docs/platform/contracts/about-contracts.md new file mode 100644 index 00000000000..f33dac4c4f7 --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/contracts/about-contracts.md @@ -0,0 +1,6 @@ +## What is a contract + +Contracts are a [registered](../../examples/publishing-a-new-contract.md) set of rules defined in a [JSON Application Schema](../../getting-started/core-concepts#schemas). + +See more on the Dash Platform documentation about [Data Contract](https://dashplatform.readme.io/docs/explanation-platform-protocol-data-contract). + diff --git a/packages/js-dash-sdk/docs/platform/contracts/create.md b/packages/js-dash-sdk/docs/platform/contracts/create.md new file mode 100644 index 00000000000..00553a80bfd --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/contracts/create.md @@ -0,0 +1,36 @@ +**Usage**: `client.platform.contracts.create(contractDefinitions, identity)` +**Description**: This method will return a Contract object initialized with the parameters defined and apply to the used identity. + +Parameters: + +| parameters | type | required | Description | +|--------------------------|-------------------|------------------ | ----------------------------------------------------------------- | +| **contractDefinitions** | JSONDataContract | yes | The defined [JSON Application Schema](https://dashplatform.readme.io/docs/explanation-platform-protocol-data-contract) | +| **identity** | Identity | yes | A valid [registered `application` identity](../identities/register.md) | + +**Example**: + +```js + const identityId = '';// Your identity identifier. + + // Your valid json contract definitions + const contractDefinitions = { + note: { + properties: { + message: { + type: "string" + } + }, + additionalProperties: false + } + }; + const identity = await client.platform.identities.get(identityId); + const contract = client.platform.contracts.create(contractDefinitions, identity); + + // You can use the validate method from DPP to validate the created contract + const validationResult = client.platform.dpp.dataContract.validate(contract); +``` + +**Note**: When your contract is created, it will only exist locally, use the [broadcast](../contracts/broadcast.md) method to register it. + +Returns : Contract. diff --git a/packages/js-dash-sdk/docs/platform/contracts/get.md b/packages/js-dash-sdk/docs/platform/contracts/get.md new file mode 100644 index 00000000000..1aa80d8b700 --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/contracts/get.md @@ -0,0 +1,12 @@ +**Usage**: `client.platform.contracts.get(contractId)` +**Description**: This method will allow you to fetch back a contract from its id. + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------|------------------ | ----------------------------------------------------------------- | +| **identifier** | string | yes | Will fetch back the contract matching the identifier | + +**Example**: `await client.platform.contracts.get('77w8Xqn25HwJhjodrHW133aXhjuTsTv9ozQaYpSHACE3')` + +Returns : Contract (or `null` if it's not a registered contract). diff --git a/packages/js-dash-sdk/docs/platform/contracts/publish.md b/packages/js-dash-sdk/docs/platform/contracts/publish.md new file mode 100644 index 00000000000..afa2667c810 --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/contracts/publish.md @@ -0,0 +1,20 @@ +**Usage**: `client.platform.contracts.publish(contract, identity)` +**Description**: This method will sign and broadcast any valid contract. + +Parameters: + +| parameters | type | required | Description | +|---------------------------|-----------|----------------| ------------------------------------------------------------------------------| +| **contract** | Contract | yes | A valid [created contract](../contracts/create.md) | +| **identity** | Identity | yes | A valid [registered `application` identity](../identities/register.md) | + +**Example**: +```js +const identityId = '';// Your identity identifier. +const identity = await client.platform.identities.get(identityId); +// See the contract.create documentation for more on how to create a dataContract +const contract = await client.platform.contracts.create(contractDefinitions, identity); +await platform.contracts.publish(contract, identity); +``` + +Returns : DataContractCreateTransition. diff --git a/packages/js-dash-sdk/docs/platform/contracts/update.md b/packages/js-dash-sdk/docs/platform/contracts/update.md new file mode 100644 index 00000000000..c441d9d20c6 --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/contracts/update.md @@ -0,0 +1,14 @@ +**Usage**: `client.platform.contracts.update(contract, identity)` +**Description**: This method will sign and broadcast an updated valid contract. + +Parameters: + +| parameters | type | required | Description | +|---------------------------|-----------|----------------| ------------------------------------------------------------------------------| +| **contract** | Contract | yes | A valid [created contract](/platform/contracts/create.md) | +| **identity** | Identity | yes | A valid [registered `application` identity](/platform/identities/register.md) | + +**Example**: +You may check [following document](/examples/updating-a-contract.md) for an example on how to update a contract. + +Returns : DataContractUpdateTransition. diff --git a/packages/js-dash-sdk/docs/platform/documents/about-documents.md b/packages/js-dash-sdk/docs/platform/documents/about-documents.md new file mode 100644 index 00000000000..36f1c3a8a5b --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/documents/about-documents.md @@ -0,0 +1,6 @@ +## What is a document + +Documents in Dash Platform are similar to those in standard document-oriented databases (MongoDB,...). +They represent a record consisting of one, or multiples field-value pairs and should respect the structure of the dataContract on which they are submitted in. + +See more on the Dash Platform documentation about [Data Contract](https://dashplatform.readme.io/docs/explanation-platform-protocol-data-contract). diff --git a/packages/js-dash-sdk/docs/platform/documents/broadcast.md b/packages/js-dash-sdk/docs/platform/documents/broadcast.md new file mode 100644 index 00000000000..daa641f27d6 --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/documents/broadcast.md @@ -0,0 +1,29 @@ +**Usage**: `client.platform.document.broadcast(documents, identity)` +**Description**: This method will broadcast the document on the Application Chain + +Parameters: + +| parameters | type | required | Description | +|----------------------------|------------|----------| ----------------------------------------------------------------------------| +| **documents** | Object | yes | | +| **documents.create** | Document[] | no | array of valid [created document](../documents/create.md) to create | +| **documents.replace** | Document[] | no | array of valid [created document](../documents/create.md) to replace | +| **documents.delete** | Document[] | no | array of valid [created document](../documents/create.md) to delete | +| **identity** | Identity | yes | A valid [registered identity](../identities/register.md) | + + +**Example**: +```js +const identityId = '';// Your identity identifier +const identity = await client.platform.identities.get(identityId); + +const helloWorldDocument = await platform.documents.create( + // Assume a contract helloWorldContract is registered with a field note + 'helloWorldContract.note', + identity, + { message: 'Hello World'}, + ); + +await platform.documents.broadcast({create: [helloWorldDocument]}, identity); +``` +Returns : documents. diff --git a/packages/js-dash-sdk/docs/platform/documents/create.md b/packages/js-dash-sdk/docs/platform/documents/create.md new file mode 100644 index 00000000000..105f63300d2 --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/documents/create.md @@ -0,0 +1,26 @@ +**Usage**: `client.platform.documents.create(typeLocator, identity, documentOpts)` +**Description**: This method will return a Document object initialized with the parameters defined and apply to the used identity. + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------|------------------ | ----------------------------------------------------------------- | +| **dotLocator** | string | yes | Field of a specific application, under the form `appName.fieldName` | +| **identity** | Identity| yes | A valid [registered identity](../identities/register.md) | +| **docOpts** | Object | yes | A valid data that match the data contract structure | + +**Example**: +```js +const identityId = '';// Your identity identifier +const identity = await client.platform.identities.get(identityId); + +const helloWorldDocument = await platform.documents.create( + // Assume a contract helloWorldContract is registered with a field note + 'helloWorldContract.note', + identity, + { message: 'Hello World'}, + ); +``` +**Note**: When your document is created, it will only exist locally, use the [broadcast](../documents/broadcast.md) method to register it. + +Returns: Document diff --git a/packages/js-dash-sdk/docs/platform/documents/get.md b/packages/js-dash-sdk/docs/platform/documents/get.md new file mode 100644 index 00000000000..179a5d7676f --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/documents/get.md @@ -0,0 +1,31 @@ +**Usage**: `client.platform.documents.get(dotLocator, opts)` +**Description**: This method will allow you to fetch back documents matching the provided parameters. + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------|------------------| -------------------------------------------------------------------| +| **dotLocator** | string | yes | Field of a specific application, under the form `appName.fieldName`| +| **opts** | object | no (default: {}) | Query options of the request | + +**Queries options**: + +| parameters | type | required | Description | +|-------------------|---------|------------------| ------------------------------------------------------------------| +| **where** | array | no | Mongo-like where query | +| **orderBy** | array | no | Mongo-like orderBy query | +| **limit** | integer | no | how many objects to fetch | +| **startAt** | integer | no | number of objects to skip | +| **startAfter** | integer | no | exclusive skip | + + +**Example**: +```js + const queryOpts = { + where: [ + ['normalizedLabel', '==', 'alice'], + ['normalizedParentDomainName', '==', 'dash'], + ], + }; + await client.platform.documents.get('dpns.domain', queryOpts); +``` diff --git a/packages/js-dash-sdk/docs/platform/identities/about-identity.md b/packages/js-dash-sdk/docs/platform/identities/about-identity.md new file mode 100644 index 00000000000..9802ff2a263 --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/identities/about-identity.md @@ -0,0 +1,11 @@ +## What is an identity + +An Identity is a blockchain-based identifier for individuals (users) and applications. +Identity is the atomic element that linked with additional applications can be extended to provide new functionality. + +Read more on the Dash Platform documentation about [Identity](https://dashplatform.readme.io/docs/explanation-identity). +You might also want to consult the usage for the [DPNS Name Service](../names/about-dpns.md) in order to attach a name to your created identity. + +## Credits + +Each identity contains a credit balance. The ratio is 1 duff = 1000 credits. diff --git a/packages/js-dash-sdk/docs/platform/identities/get.md b/packages/js-dash-sdk/docs/platform/identities/get.md new file mode 100644 index 00000000000..d6e06a9bb51 --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/identities/get.md @@ -0,0 +1,12 @@ +**Usage**: `client.platform.identities.get(identityId)` +**Description**: This method will allow you to fetch back an identity from its id. + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------|------------------ | ----------------------------------------------------------------- | +| **identifier** | string | yes | Will fetch back the identity matching the identifier | + +**Example**: `await client.platform.identities.get('3GegupTgRfdN9JMS8R6QXF3B2VbZtiw63eyudh1oMJAk')` + +Returns : Identity (or `null` if it does not exist). diff --git a/packages/js-dash-sdk/docs/platform/identities/register.md b/packages/js-dash-sdk/docs/platform/identities/register.md new file mode 100644 index 00000000000..e8a538d7afe --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/identities/register.md @@ -0,0 +1,14 @@ +**Usage**: `client.platform.identities.register()` +**Description**: This method will register a new identity for you. + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------|------------------| -------------------------------------------------------------------| +| fundingAmount | number | no | Defaults: 10000. Allow to set a funding amount in duffs (satoshis).| + +**Example**: `await client.platform.identities.register()` + +**Note**: The created identity will be associated to the active account. You might want to know more about how to [change your active account](../../examples/use-different-account.md). + +Returns : Identity. diff --git a/packages/js-dash-sdk/docs/platform/identities/topUp.md b/packages/js-dash-sdk/docs/platform/identities/topUp.md new file mode 100644 index 00000000000..758afbd61c3 --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/identities/topUp.md @@ -0,0 +1,22 @@ +**Usage**: `client.platform.identities.topUp(identity, amount)` +**Description**: This method will topup the provided identity's balance. + +_The identity balance might slightly vary from the topped up amount because of the transaction fee estimation._ + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------|------------------| ----------------------------------------------------------------------------------------| +| **identity** | Identity| yes | A valid [registered identity](../identities/register.md) | +| **amount** | number | yes | A duffs (satoshis) value corresponding to the amount you want to top up to the identity.| + +**Example**: +```js +const identityId = '';// Your identity identifier +const identity = await client.platform.identities.get(identityId); +await platform.identities.topUp(identity, 10000); + +console.log(`New identity balance: ${identity.balance}`) +``` + +Returns : Boolean. diff --git a/packages/js-dash-sdk/docs/platform/names/about-dpns.md b/packages/js-dash-sdk/docs/platform/names/about-dpns.md new file mode 100644 index 00000000000..279e0a84baa --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/names/about-dpns.md @@ -0,0 +1,9 @@ +## What is DPNS + +DPNS is a special Dash Platform Application that is intended to provide a naming service for the Application Chain. + +Decoupling name from the blockchain identity enables a unique user experience coupled with the highest security while remaining compatible with [Decentralized Identifiers](https://www.w3.org/TR/did-core/). + +Limitation : max length of 63 characters on charset `0-9`,`A-Z`(case insensitive), `-`. + +Domain names are linked to an Identity. diff --git a/packages/js-dash-sdk/docs/platform/names/register.md b/packages/js-dash-sdk/docs/platform/names/register.md new file mode 100644 index 00000000000..996c35a08de --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/names/register.md @@ -0,0 +1,17 @@ +**Usage**: `client.platform.names.register(name, identity)` +**Description**: This method will create a DPNS record matching your identity to the user or appname defined. + +Parameters: + +| parameters | type | required | Description | +|----------------------------------|-----------|----------------| ----------------------------------------------------------------------------- | +| **name** | String | yes | An alphanumeric (1-63 character) value used for human-identification (can contain `-` but not as the first or last character). If a name with no parent domain is entered, '.dash' is used. | +| **records** | Object | yes | records object having only one of the following items | +| **records.dashUniqueIdentityId** | String | no | Unique Identity ID for this name record | +| **records.dashAliasIdentityId** | String | no | Used to signify that this name is the alias for another id | +| **identity** | Identity | yes | A valid [registered identity](../identities/register.md) | + + +**Example**: `await client.platform.identities.register('alice', { dashUniqueIdentityId: identity.getId() }, identity)` + +Return: the created domain document diff --git a/packages/js-dash-sdk/docs/platform/names/resolve.md b/packages/js-dash-sdk/docs/platform/names/resolve.md new file mode 100644 index 00000000000..585486eb2ba --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/names/resolve.md @@ -0,0 +1,12 @@ +**Usage**: `client.platform.names.resolve(name.domain)` +**Description**: This method will allow you to resolve a DPNS record from its humanized name. + +Parameters: + +| parameters | type | required | Description | +|---------------------------|-----------|----------------| ----------------------------------------------------------------------------- | +| **name** | String | yes | An alphanumeric (2-63) value used for human-identification (can contains `-`) | + +**Example**: `await client.platform.names.resolve('alice.dash')` + +Returns : Document (or `null` if do not exist). diff --git a/packages/js-dash-sdk/docs/platform/names/resolveByRecord.md b/packages/js-dash-sdk/docs/platform/names/resolveByRecord.md new file mode 100644 index 00000000000..8eeade343ee --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/names/resolveByRecord.md @@ -0,0 +1,18 @@ +**Usage**: `client.platform.names.resolve(name.domain)` +**Description**: This method will allow you to resolve a DPNS record from its identity ID. + +Parameters: + +| parameters | type | required | Description | +|---------------------------|-----------|----------------| ----------------------------------------------------------------------------- | +| **name** | String | yes | An alphanumeric (2-63) value used for human-identification (can contains `-`) | + +**Example**: + +This example will describe how to resolve names by the identity id, but other records field will works too. +```js +const identityId = '3ge4yjGinQDhxh2aVpyLTQaoka45BkijkoybfAkDepoN'; +const document = await client.platform.names.resolveByRecord('dashIdentity',identityId); +``` + +Returns : array of Document. diff --git a/packages/js-dash-sdk/docs/platform/names/search.md b/packages/js-dash-sdk/docs/platform/names/search.md new file mode 100644 index 00000000000..275720bf11c --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/names/search.md @@ -0,0 +1,22 @@ +**Usage**: `client.platform.names.search(labelPrefix, parentDomain)` +**Description**: This method will allow you to search all records matching the label prefix on the specified parent domain. + +Parameters: + +| parameters | type | required | Description | +|---------------------------|-----------|----------------| ----------------------------------------------------------------------------- | +| **labelPrefix** | String | yes | label prefix to search for | +| **parentDomain** | String | yes | parent domain name on which to perform the search | + +**Example**: + +This example will describe how to search all names on the parent domain `dash` that starts with the label prefix `al`. +It will resolves names documents such as `alice`, `alex` etc... + +```js +const labelPrefix = 'al'; +const parentDomain = 'dash'; +const document = await client.platform.names.search(labelPrefix, parentDomain); +``` + +Returns : Documents matching the label prefix on the parent domain. diff --git a/packages/js-dash-sdk/docs/usage/dapi.md b/packages/js-dash-sdk/docs/usage/dapi.md new file mode 100644 index 00000000000..4b531a239b7 --- /dev/null +++ b/packages/js-dash-sdk/docs/usage/dapi.md @@ -0,0 +1,11 @@ +## About DAPI + +DAPI (Decentralized API) is a distributed and decentralized endpoints provided by the Masternode Network. + +## Get the DAPI-Client instance + +```js + const dapiClient = client.getDAPIClient(); +``` + +The usage is then [described here](https://dashplatform.readme.io/docs/explanation-dapi). diff --git a/packages/js-dash-sdk/docs/usage/dashcorelib-primitives.md b/packages/js-dash-sdk/docs/usage/dashcorelib-primitives.md new file mode 100644 index 00000000000..98bdb4de18a --- /dev/null +++ b/packages/js-dash-sdk/docs/usage/dashcorelib-primitives.md @@ -0,0 +1,114 @@ +## Transaction + +The Transaction primitive allows easy creation and manipulation of transactions. It also allows signing when provided with a privatekey. +Supports fee control and input/output access (which allows passing a specific script). +```js +import { Transaction } from 'dash'; +const tx = new Transaction(txProps) +``` + +Access the [Transaction documentation on dashevo/dashcore-lib](https://github.com/dashevo/dashcore-lib/blob/master/docs/transaction.md) + +## Address + +Standardized representation of a Dash Address. Address can be instantiated from a String, PrivateKey, PublicKey, HDPrivateKey or HdPublicKey. +Pay-to-script-hash (P2SH) multi-signature addresses from an array of PublicKeys are also supported. + +```js +import { Address } from 'dash'; +``` + +Access the [Address documentation on dashevo/dashcore-lib](https://github.com/dashevo/dashcore-lib/blob/master/docs/address.md) + +## Block + +Given a hexadecimal string representation of the block as input, the Block class allows you to have a deserialized representation of a Block or its header. It also allows validating the transactions in the block against the header merkle root. + +Transactions of the block can also be explored by iterating over elements in array (`block.transactions`). + +`import { Block } from 'dash'` + +Access the [Block documentation on dashevo/dashcore-lib](https://github.com/dashevo/dashcore-lib/blob/master/docs/block.md) + +## UnspentOutput + +Representation of an UnspentOutput (also called UTXO as in Unspent Transaction Output). +Mostly useful in association with a Transaction and for Scripts. + +`import { UnspentOutput } from 'dash'` + +Access the [UnspentOutput documentation on dashevo/dashcore-lib](https://github.com/dashevo/dashcore-lib/blob/master/docs/unspentoutput.md) + +## HDPublicKey + +Hierarchical Deterministic (HD) version of the PublicKey. +Used internally by Wallet-lib and for exchange between peers (DashPay) + +`import { HDPublicKey } from 'dash'` + +Access the [HDKeys documentation on dashevo/dashcore-lib](https://github.com/dashevo/dashcore-lib/blob/master/docs/hierarchical.md) + +## HDPrivateKey + +Hierarchical Deterministic (HD) version of the PrivateKey. +Used internally by Wallet-lib. + +`import { HDPrivateKey } from 'dash'` + +Access the [HDKeys documentation on dashevo/dashcore-lib](https://github.com/dashevo/dashcore-lib/blob/master/docs/hierarchical.md) + +## PublicKey + +`import { PublicKey } from 'dash'` + +Access the [PublicKey documentation on dashevo/dashcore-lib](https://github.com/dashevo/dashcore-lib/blob/master/docs/publickey.md) + +## PrivateKey + +`import { PrivateKey } from 'dash'` + +Access the [PrivateKey documentation on dashevo/dashcore-lib](https://github.com/dashevo/dashcore-lib/blob/master/docs/privatekey.md) + +## Mnemonic + +Implementation of [BIP39 Mnemonic code for generative deterministic keys](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki). +Allow to generate random mnemonic on the language set needed, validate a mnemonic or get the HDPrivateKey associated. + +`import { Mnemonic } from 'dash'` + +Access the [Mnemonic documentation on dashevo/dashcore-lib](https://github.com/dashevo/dashcore-lib/blob/master/docs/mnemonic.md) + +## Network + +A representation of the internal parameters relative to the network used. By default, all primitives works with 'livenet', this class allow to have an testnet instance to used on the other primitives (such as Addresses), or for Wallet-lib. + +`import { Network } from 'dash'` + + +Access the [Network documentation on dashevo/dashcore-lib](https://github.com/dashevo/dashcore-lib/blob/master/docs/networks.md) + +## Script + +In Dash, transaction have in their inputs and outputs some script, very simple programming language with a stack-based evaluation and which is not Turing Complete. +A valid Transaction is a transaction which output script are evaluated as valid. + +Some operations of this language, such as OP_RETURN has been used to store hashes and B64 data on the payment chain. +Learn more on our walkthrough [Transaction script manipulation with the OP_RETURN example](/docs/walkthroughs/op_return/or_return.md) + +`import { Script } from 'dash'` + +Access the [Script documentation on dashevo/dashcore-lib](https://github.com/dashevo/dashcore-lib/blob/master/docs/script.md) + + +## Input + +`import { Input } from 'dash'` + +Access the [Transaction documentation on dashevo/dashcore-lib](https://github.com/dashevo/dashcore-lib/blob/master/docs/transaction.md) + + +## Output + +`import { Output } from 'dash'` + +Access the [Transaction documentation on dashevo/dashcore-lib](https://github.com/dashevo/dashcore-lib/blob/master/docs/transaction.md) diff --git a/packages/js-dash-sdk/docs/walkthroughs/automatically-consolidate-UTXO/automatically-consolidate-UTXO.md b/packages/js-dash-sdk/docs/walkthroughs/automatically-consolidate-UTXO/automatically-consolidate-UTXO.md new file mode 100644 index 00000000000..24e09f8679f --- /dev/null +++ b/packages/js-dash-sdk/docs/walkthroughs/automatically-consolidate-UTXO/automatically-consolidate-UTXO.md @@ -0,0 +1 @@ +### Automatically consolidate UTXOS diff --git a/packages/js-dash-sdk/docs/walkthroughs/create-broadcast-contracts/create-broadcast-contracts.md b/packages/js-dash-sdk/docs/walkthroughs/create-broadcast-contracts/create-broadcast-contracts.md new file mode 100644 index 00000000000..e0347543f1a --- /dev/null +++ b/packages/js-dash-sdk/docs/walkthroughs/create-broadcast-contracts/create-broadcast-contracts.md @@ -0,0 +1 @@ +### Create / Broadcast contracts diff --git a/packages/js-dash-sdk/docs/walkthroughs/fetch-documents-on-front-end/fetch-documents-on-front-end.md b/packages/js-dash-sdk/docs/walkthroughs/fetch-documents-on-front-end/fetch-documents-on-front-end.md new file mode 100644 index 00000000000..0a69f5dc7ae --- /dev/null +++ b/packages/js-dash-sdk/docs/walkthroughs/fetch-documents-on-front-end/fetch-documents-on-front-end.md @@ -0,0 +1 @@ +### fetch-documents-on-front-end diff --git a/packages/js-dash-sdk/docs/walkthroughs/get-public-data/get-public-data.md b/packages/js-dash-sdk/docs/walkthroughs/get-public-data/get-public-data.md new file mode 100644 index 00000000000..2d7c96120e1 --- /dev/null +++ b/packages/js-dash-sdk/docs/walkthroughs/get-public-data/get-public-data.md @@ -0,0 +1 @@ +### get-public-data.md diff --git a/packages/js-dash-sdk/docs/walkthroughs/receive-payment-as-a-merchant/receive-payment-as-a-merchant.md b/packages/js-dash-sdk/docs/walkthroughs/receive-payment-as-a-merchant/receive-payment-as-a-merchant.md new file mode 100644 index 00000000000..fa52ac8395a --- /dev/null +++ b/packages/js-dash-sdk/docs/walkthroughs/receive-payment-as-a-merchant/receive-payment-as-a-merchant.md @@ -0,0 +1 @@ +### receive_payment_as_a_merchant diff --git a/packages/js-dash-sdk/docs/walkthroughs/transactions-with-scripts/transactions-with-scripts.md b/packages/js-dash-sdk/docs/walkthroughs/transactions-with-scripts/transactions-with-scripts.md new file mode 100644 index 00000000000..42e2444e735 --- /dev/null +++ b/packages/js-dash-sdk/docs/walkthroughs/transactions-with-scripts/transactions-with-scripts.md @@ -0,0 +1 @@ +### transactions-with-scripts with the OP_RETURN example diff --git a/packages/js-dash-sdk/docs/wallet/about-wallet-lib.md b/packages/js-dash-sdk/docs/wallet/about-wallet-lib.md new file mode 100644 index 00000000000..0418cee6a8b --- /dev/null +++ b/packages/js-dash-sdk/docs/wallet/about-wallet-lib.md @@ -0,0 +1,7 @@ +### About Wallet-lib + +When Dash.Client is initiated with a `mnemonic` property, a wallet instance is automatically created accessible via `client.wallet` as well as an `client.account` instance. + +In order to ensure the sync-up with the network has happened, you will need to wait for the method `client.isReady()` to resolve. + +You will find else where the [complete documentation of Wallet-lib](https://github.com/dashevo/platform/tree/master/packages/wallet-lib), as we only cover the most basic pieces in this documentation. diff --git a/packages/js-dash-sdk/docs/wallet/accounts.md b/packages/js-dash-sdk/docs/wallet/accounts.md new file mode 100644 index 00000000000..feea28cdafb --- /dev/null +++ b/packages/js-dash-sdk/docs/wallet/accounts.md @@ -0,0 +1,21 @@ +## Getting an account + +A Wallet is actually a holders of multiple Account that hold the keys needed to make a payment. +So the first thing will be on accessing your account : + +```js +const client = new Dash.Client({ + wallet: { + mnemonic: "maximum blast eight orchard waste wood gospel siren parent deer athlete impact", + }, +}); +client.isReady().then(()=>{ + const {account} = client; + // Do something with account +}); +``` + +As optional parameter, an integer representing the account `index` can be passed as parameter. By default, index account on call is 0. + +You will also see that we wait for isReady to resolve before making any operation. This allow us to access an account instance that will have synced-up with network and that our UTXOS set are ready to be used for payment/signing. + diff --git a/packages/js-dash-sdk/docs/wallet/signing-encrypt.md b/packages/js-dash-sdk/docs/wallet/signing-encrypt.md new file mode 100644 index 00000000000..b72362d21d9 --- /dev/null +++ b/packages/js-dash-sdk/docs/wallet/signing-encrypt.md @@ -0,0 +1,22 @@ +## Sign a Transaction/Transition a message + +```js +const tx = new Dash.Transaction({ +//txOpts +}); +const signedTx = client.account.sign(tx); +``` + +## Encrypt a message + +```js + const message = 'Something'; + const signedMessage = client.account.encrypt('AES',message,'secret'); +``` + +## Decrypt a message + +```js +const encrypted = 'U2FsdGVkX19JLa+1UpbMcut1/QFWLMlKUS+iqz+7Wl4='; +const message = client.account.decrypt('AES',encrypted,'secret'); +``` diff --git a/packages/js-dash-sdk/examples/node/create-and-fund-wallet.js b/packages/js-dash-sdk/examples/node/create-and-fund-wallet.js new file mode 100644 index 00000000000..5af736454f7 --- /dev/null +++ b/packages/js-dash-sdk/examples/node/create-and-fund-wallet.js @@ -0,0 +1,28 @@ +const Dash = require('dash'); +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: null, // Will generate a new address, you should keep it. + }, +}; +const client = new Dash.Client(clientOpts); + +const displayFundingAddress = async function () { + const {account, wallet} = client; + + const mnemonic = wallet.exportWallet(); + const address = account.getUnusedAddress().address; + console.log('Mnemonic:', mnemonic); + console.log('Total balance', account.getTotalBalance()); + console.log('Unused address:', address); + // Fund this address using the faucet : http://devnet-evonet-1117662964.us-west-2.elb.amazonaws.com/ +}; +const onReceivedTransaction = function(data){ + const {account} = client; + console.log('Received tx',data.txid); + console.log('Total pending confirmation', account.getUnconfirmedBalance()); + console.log('Total balance', account.getTotalBalance()); +} +client.account.on('FETCHED/UNCONFIRMED_TRANSACTION',onReceivedTransaction) +client.isReady().then(displayFundingAddress); + diff --git a/packages/js-dash-sdk/examples/node/register-contract.js b/packages/js-dash-sdk/examples/node/register-contract.js new file mode 100644 index 00000000000..f636c6c70ed --- /dev/null +++ b/packages/js-dash-sdk/examples/node/register-contract.js @@ -0,0 +1,34 @@ +const DashJS = require('dash'); + +const sdkOpts = { + network: 'testnet', + wallet: { + mnemonic: 'your mnemonic here', + }, +}; +const sdk = new DashJS.Client(sdkOpts); + +const registerContract = async function () { + await sdk.isReady(); + let platform = sdk.platform; + const identity = await platform.identities.get('your identity id here'); + + const contractDocuments = { + note: { + properties: { + message: { + type: "string" + } + }, + indices: [ + { "message": "asc"} + ], + additionalProperties: false + }}; + + const contract = await platform.contracts.create(contractDocuments, identity); + await platform.dpp.dataContract.validate(contract) + await platform.contracts.publish(contract, identity); +}; + +registerContract(); diff --git a/packages/js-dash-sdk/examples/node/register-identity.js b/packages/js-dash-sdk/examples/node/register-identity.js new file mode 100644 index 00000000000..85fd89fbd41 --- /dev/null +++ b/packages/js-dash-sdk/examples/node/register-identity.js @@ -0,0 +1,23 @@ +const Dash = require('dash'); +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'your mnemonic here', + }, +}; +const client = new Dash.Client(clientOpts); + +const createIdentity = async function () { + await client.isReady(); + + let platform = client.platform; + + platform + .identities + .register() + .then((identityId) => { + console.log({identityId}); + }); + +}; +createIdentity(); diff --git a/packages/js-dash-sdk/examples/node/register-name.js b/packages/js-dash-sdk/examples/node/register-name.js new file mode 100644 index 00000000000..584c948cd9e --- /dev/null +++ b/packages/js-dash-sdk/examples/node/register-name.js @@ -0,0 +1,19 @@ +const Dash = require('dash'); +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'your mnemonic here', + }, +}; +const identityId = 'your identity id'; +const client = new Dash.Client(clientOpts); + +const registerName = async function () { + let platform = client.platform; + await client.isReady(); + + const identity = await platform.identities.get(identityId); + const nameRegistration = await platform.names.register('alice', identity); + console.log({nameRegistration}); +}; +registerName(); diff --git a/packages/js-dash-sdk/examples/node/retrieve-contract.js b/packages/js-dash-sdk/examples/node/retrieve-contract.js new file mode 100644 index 00000000000..e414178798c --- /dev/null +++ b/packages/js-dash-sdk/examples/node/retrieve-contract.js @@ -0,0 +1,20 @@ +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet' +}; +const client = new Dash.Client(clientOpts); + +const getContract = async function () { + let platform = client.platform; + await client.isReady(); + + platform + .contracts + .get('77w8Xqn25HwJhjodrHW133aXhjuTsTv9ozQaYpSHACE3') + .then((contract) => { + console.dir({contract},{depth:5}); + }); + +}; +getContract(); diff --git a/packages/js-dash-sdk/examples/node/retrieve-documents.js b/packages/js-dash-sdk/examples/node/retrieve-documents.js new file mode 100644 index 00000000000..47a4760bc64 --- /dev/null +++ b/packages/js-dash-sdk/examples/node/retrieve-documents.js @@ -0,0 +1,22 @@ +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet' +}; +const client = new Dash.Client(clientOpts); + +const getDocuments = async function () { + let platform = client.platform; + await client.isReady(); + + const queryOpts = { + where: [ + ['normalizedLabel', 'startsWith', 'd'], + ['normalizedParentDomainName', '==', 'dash'], + ], + }; + + const documents = await platform.documents.get('dpns.domain', queryOpts); + console.dir({documents},{depth:5}); +}; +getDocuments(); diff --git a/packages/js-dash-sdk/examples/node/retrieve-identity.js b/packages/js-dash-sdk/examples/node/retrieve-identity.js new file mode 100644 index 00000000000..53be335f3dc --- /dev/null +++ b/packages/js-dash-sdk/examples/node/retrieve-identity.js @@ -0,0 +1,20 @@ +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet' +}; +const client = new Dash.Client(clientOpts); + +const getIdentity = async function () { + let platform = client.platform; + await client.isReady(); + + platform + .identities + .get('3GegupTgRfdN9JMS8R6QXF3B2VbZtiw63eyudh1oMJAk') + .then((identity) => { + console.log({identity}); + }); + +}; +getIdentity(); diff --git a/packages/js-dash-sdk/examples/node/retrieve-name.js b/packages/js-dash-sdk/examples/node/retrieve-name.js new file mode 100644 index 00000000000..27dae471fae --- /dev/null +++ b/packages/js-dash-sdk/examples/node/retrieve-name.js @@ -0,0 +1,14 @@ +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet' +}; +const client = new Dash.Client(clientOpts); + +const platform = client.platform; + +async function retrieveName(){ + const user = await platform.names.get('alice'); + console.dir({user}, {depth:5}); +} +retrieveName(); diff --git a/packages/js-dash-sdk/examples/node/sign-and-verify-messages.js b/packages/js-dash-sdk/examples/node/sign-and-verify-messages.js new file mode 100644 index 00000000000..79e8aa9b865 --- /dev/null +++ b/packages/js-dash-sdk/examples/node/sign-and-verify-messages.js @@ -0,0 +1,29 @@ +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: null, + }, +}; + +const client = new Dash.Client(clientOpts); + +const message = new Dash.Core.Message('hello, world'); + +const signAndVerify = async function () { + const {account, wallet} = client; + + const mnemonic = wallet.exportWallet(); + console.log('Mnemonic:', mnemonic); + + const idKey = account.getIdentityHDKey() + const idPrivateKey = idKey.privateKey; + const idAddress = idPrivateKey.toAddress().toString() + + const signed = account.sign(message, idPrivateKey); + const verify = message.verify(idAddress, signed.toString()); + console.log(verify); +}; +client.isReady().then(signAndVerify); + diff --git a/packages/js-dash-sdk/examples/schema.json b/packages/js-dash-sdk/examples/schema.json new file mode 100644 index 00000000000..7fb089482d2 --- /dev/null +++ b/packages/js-dash-sdk/examples/schema.json @@ -0,0 +1,58 @@ +{ + "contact": { + "indices": [ + { + "name": "userIdToUserId", + "unique": true, + "properties": [ + { + "$userId": "asc" + }, + { + "toUserId": "asc" + } + ] + } + ], + "required": [ + "toUserId", + "publicKey" + ], + "properties": { + "toUserId": { + "type": "string" + }, + "publicKey": { + "type": "string" + } + }, + "additionalProperties": false + }, + "profile": { + "indices": [ + { + "name": "userId", + "unique": true, + "properties": [ + { + "$userId": "asc" + } + ] + } + ], + "required": [ + "avatarUrl", + "about" + ], + "properties": { + "about": { + "type": "string" + }, + "avatarUrl": { + "type": "string", + "format": "url" + } + }, + "additionalProperties": false + } +} diff --git a/packages/js-dash-sdk/examples/web/usage.web.html b/packages/js-dash-sdk/examples/web/usage.web.html new file mode 100644 index 00000000000..89b3d375f42 --- /dev/null +++ b/packages/js-dash-sdk/examples/web/usage.web.html @@ -0,0 +1,12 @@ + + + + + Title + + + + + + + diff --git a/packages/js-dash-sdk/examples/web/usage.web.js b/packages/js-dash-sdk/examples/web/usage.web.js new file mode 100644 index 00000000000..40311a19cac --- /dev/null +++ b/packages/js-dash-sdk/examples/web/usage.web.js @@ -0,0 +1,28 @@ +const network = "testnet"; +const opts = { + network, + wallet: { + mnemonic: "arena light cheap control apple buffalo indicate rare motor valid accident isolate", + }, + apps: { + dashpay: { + contractId: ''// Provide the dashpay contract id here + } + } +}; +const clientInstance = new Dash.Client(opts); + +(async ()=>{ + const { platform } = clientInstance; + account = await clientInstance.getWalletAccount(); + + async function sendPayment() { + const tx = await account.createTransaction({recipient: 'yNPbcFfabtNmmxKdGwhHomdYfVs6gikbPf', satoshis: 12000}); + console.log(await account.broadcastTransaction(tx)); + } + + async function readDocument() { + const profile = await platform.documents.fetch('dashpay.profile', {}); + console.log(profile); + } +})() diff --git a/packages/js-dash-sdk/karma.conf.js b/packages/js-dash-sdk/karma.conf.js new file mode 100644 index 00000000000..0293cb89396 --- /dev/null +++ b/packages/js-dash-sdk/karma.conf.js @@ -0,0 +1,67 @@ +/* eslint-disable import/no-extraneous-dependencies */ +const webpack = require('webpack'); +const dotenvResult = require('dotenv-safe').config(); + +const webpackBaseConfig = require("./webpack.base.config"); + +const karmaMocha = require('karma-mocha'); +const karmaMochaReporter = require('karma-mocha-reporter'); +const karmaChai = require('karma-chai'); +const karmaChromeLauncher = require('karma-chrome-launcher'); +const karmaFirefoxLauncher = require('karma-firefox-launcher'); +const karmaWebpack = require('karma-webpack'); + +if (dotenvResult.error) { + throw dotenvResult.error; +} + +module.exports = (config) => { + config.set({ + frameworks: ['mocha', 'chai', 'webpack'], + files: [ + 'src/**/*.spec.ts', + 'src/test/karma/bootstrap.ts', + 'tests/functional/sdk.js', + ], + preprocessors: { + 'src/**/*.spec.ts': ['webpack'], + 'src/test/karma/bootstrap.ts': ['webpack'], + 'tests/functional/sdk.js': ['webpack'], + }, + webpack: { + ...webpackBaseConfig, + mode: 'development', + plugins: [ + ...webpackBaseConfig.plugins, + new webpack.EnvironmentPlugin( + dotenvResult.parsed, + ), + ], + }, + reporters: ['mocha'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: false, + browsers: ['ChromeHeadless', 'FirefoxHeadless'], + singleRun: false, + concurrency: Infinity, + browserNoActivityTimeout: 7 * 60 * 1000, // 30000 default + browserDisconnectTimeout: 3 * 2000, // 2000 default + pingTimeout: 3 * 5000, // 5000 default + plugins: [ + karmaMocha, + karmaMochaReporter, + karmaChai, + karmaChromeLauncher, + karmaFirefoxLauncher, + karmaWebpack, + ], + customLaunchers: { + FirefoxHeadless: { + base: 'Firefox', + flags: ['-headless'], + }, + }, + }); +}; diff --git a/packages/js-dash-sdk/nodemon.json b/packages/js-dash-sdk/nodemon.json new file mode 100644 index 00000000000..5f54551d00d --- /dev/null +++ b/packages/js-dash-sdk/nodemon.json @@ -0,0 +1,6 @@ +{ + "watch": ["src"], + "ext": ".ts,.js", + "ignore": [], + "exec": "ts-node ./src/index.ts" +} diff --git a/packages/js-dash-sdk/package.json b/packages/js-dash-sdk/package.json new file mode 100644 index 00000000000..50cacca604f --- /dev/null +++ b/packages/js-dash-sdk/package.json @@ -0,0 +1,98 @@ +{ + "name": "dash", + "version": "3.23.0-dev.4", + "description": "Dash library for JavaScript/TypeScript ecosystem (Wallet, DAPI, Primitives, BLS, ...)", + "main": "build/src/index.js", + "unpkg": "dist/dash.min.js", + "browser": "dist/dash.min.js", + "types": "dist/src/index.d.ts", + "scripts": { + "start:dev": "nodemon --exec 'yarn run build && yarn run test:unit'", + "start:ts": "tsc --watch", + "build": "yarn run build:ts && webpack --stats-error-details", + "build:ts": "tsc", + "lint": "", + "test": "yarn run test:unit && yarn run test:functional && yarn run test:browsers", + "test:browsers": "karma start ./karma.conf.js --single-run", + "test:unit": "TS_NODE_COMPILER_OPTIONS={\"target\":\"es6\"} ts-mocha \"src/**/*.spec.ts\"", + "test:functional": "yarn run build && mocha --recursive tests/functional/**/*.js", + "prepublishOnly": "yarn run build", + "prepare": "yarn run build" + }, + "ultra": { + "concurrent": [ + "test" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/dashevo/DashJS.git" + }, + "author": "Dash Core Group ", + "license": "MIT", + "bugs": { + "url": "https://github.com/dashevo/DashJS/issues" + }, + "homepage": "https://github.com/dashevo/DashJS#readme", + "dependencies": { + "@dashevo/dapi-client": "workspace:~", + "@dashevo/dashcore-lib": "~0.19.39", + "@dashevo/dashpay-contract": "workspace:~", + "@dashevo/dpns-contract": "workspace:~", + "@dashevo/dpp": "workspace:~", + "@dashevo/grpc-common": "workspace:~", + "@dashevo/masternode-reward-shares-contract": "workspace:~", + "@dashevo/wallet-lib": "workspace:~", + "bs58": "^4.0.1", + "node-inspect-extracted": "^1.0.8" + }, + "devDependencies": { + "@types/chai": "^4.2.12", + "@types/dirty-chai": "^2.0.2", + "@types/expect": "^24.3.0", + "@types/mocha": "^8.0.3", + "@types/node": "^14.6.0", + "@types/sinon": "^9.0.4", + "@types/sinon-chai": "^3.2.4", + "assert": "^2.0.0", + "browserify-zlib": "^0.2.0", + "buffer": "^6.0.3", + "chai": "^4.3.4", + "chance": "^1.1.6", + "crypto-browserify": "^3.12.0", + "dirty-chai": "^2.0.1", + "dotenv-safe": "^8.2.0", + "events": "^3.3.0", + "https-browserify": "^1.0.0", + "karma": "^6.3.4", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^3.1.0", + "karma-firefox-launcher": "^2.1.1", + "karma-mocha": "^2.0.1", + "karma-mocha-reporter": "^2.2.5", + "karma-webpack": "^5.0.0", + "mocha": "^9.1.2", + "net": "^1.0.2", + "nodemon": "^2.0.4", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "rimraf": "^3.0.2", + "sinon": "^11.1.2", + "sinon-chai": "^3.7.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "string_decoder": "^1.3.0", + "terser-webpack-plugin": "^5.3.1", + "tls": "^0.0.1", + "ts-loader": "^8.0.2", + "ts-mocha": "^8.0.0", + "ts-mock-imports": "^1.3.0", + "ts-node": "^10.4.0", + "typescript": "^3.9.5", + "url": "^0.11.0", + "util": "^0.12.4", + "webpack": "^5.59.1", + "webpack-cli": "^4.9.1" + } +} diff --git a/packages/js-dash-sdk/src/SDK/Client/Client.d.ts b/packages/js-dash-sdk/src/SDK/Client/Client.d.ts new file mode 100644 index 00000000000..4a059dd4919 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Client.d.ts @@ -0,0 +1,9 @@ +import DAPIClient from "@dashevo/dapi-client" + +export declare namespace SDK { + interface platformOpts { + client: DAPIClient; + apps: object; + state: object; + } +} diff --git a/packages/js-dash-sdk/src/SDK/Client/Client.spec.ts b/packages/js-dash-sdk/src/SDK/Client/Client.spec.ts new file mode 100644 index 00000000000..24c3a663a95 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Client.spec.ts @@ -0,0 +1,319 @@ +import { expect } from 'chai'; +import getResponseMetadataFixture from '../../test/fixtures/getResponseMetadataFixture'; +import { Client } from "./index"; +import 'mocha'; +import { Transaction, BlockHeader } from "@dashevo/dashcore-lib"; +import { createFakeInstantLock } from "../../utils/createFakeIntantLock"; +import stateTransitionTypes from '@dashevo/dpp/lib/stateTransition/stateTransitionTypes'; +import { StateTransitionBroadcastError } from '../../errors/StateTransitionBroadcastError'; + +// @ts-ignore +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); +// @ts-ignore +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const GetDataContractResponse = require("@dashevo/dapi-client/lib/methods/platform/getDataContract/GetDataContractResponse"); + +import { createIdentityFixtureInAccount } from '../../test/fixtures/createIdentityFixtureInAccount'; +import { createTransactionInAccount } from '../../test/fixtures/createTransactionFixtureInAccount'; +import { createAndAttachTransportMocksToClient } from '../../test/mocks/createAndAttachTransportMocksToClient'; + +const blockHeaderFixture = '00000020e2bddfb998d7be4cc4c6b126f04d6e4bd201687523ded527987431707e0200005520320b4e263bec33e08944656f7ce17efbc2c60caab7c8ed8a73d413d02d3a169d555ecdd6021e56d000000203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff050219250102ffffffff0240c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac40c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac0000000046020019250000476416132511031b71167f4bb7658eab5c3957d79636767f83e0e18e2b9ed7f8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd4901010019250000010001d02e9ee1b14c022ad6895450f3375a8e9a87f214912d4332fa997996d2000000320000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; + +describe('Dash - Client', function suite() { + this.timeout(30000); + + let testMnemonic; + let txStreamMock; + let transportMock; + let testHDKey; + let client; + let account; + let walletTransaction; + let dapiClientMock; + let identityFixture; + let documentsFixture; + let dataContractFixture; + + beforeEach(async function beforeEach() { + testMnemonic = 'agree country attract master mimic ball load beauty join gentle turtle hover'; + testHDKey = "tprv8ZgxMBicQKsPeGi4CikhacVPz6UmErenu1PoD3S4XcEDSPP8auRaS8hG3DQtsQ2i9HACgohHwF5sgMVJNksoKqYoZbis8o75Pp1koCme2Yo"; + + client = new Client({ + wallet: { + HDPrivateKey: testHDKey, + } + }); + + ({ txStreamMock, transportMock, dapiClientMock } = await createAndAttachTransportMocksToClient(client, this.sinon)); + + account = await client.getWalletAccount(); + + // add fake tx to the wallet so it will be able to create transactions + walletTransaction = await createTransactionInAccount(account); + // create an identity in the account so we can sign state transitions + identityFixture = createIdentityFixtureInAccount(account); + dataContractFixture = getDataContractFixture(); + documentsFixture = getDocumentsFixture(dataContractFixture); + + transportMock.getTransaction.resolves({ + transaction: new Transaction('03000000019ecd68f367aba679209b9c912ff1d2ef9147f90eba2a47b5fb0158e27fb15476000000006b483045022100af2ca966eaeef8f5493fd8bcf2248d60b3f6b8236c137e2d099c8ba35878bf9402204f653232768eb8b06969b13f0aa3579d653163f757009e0c261c9ffd32332ffb0121034244016aa525c632408bc627923590cf136b47035cd57aa6f1fa8b696d717304ffffffff021027000000000000166a140f177a991f37fe6cbb08fb3f21b9629fa47330e3a85b0100000000001976a914535c005bfef672162aa2c53f0f6630a57ade344588ac00000000'), + blockHash: Buffer.from('0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9', 'hex'), + height: 42, + confirmations: 10, + isInstantLocked: true, + isChainLocked: false, + }); + + transportMock.getBlockHeaderByHash + .returns(BlockHeader.fromString(blockHeaderFixture)); + + dapiClientMock.platform.getDataContract.resolves(new GetDataContractResponse(dataContractFixture.toBuffer(), getResponseMetadataFixture())); + }); + + it('should provide expected class', function () { + expect(Client.name).to.be.equal('Client'); + expect(Client.constructor.name).to.be.equal('Function'); + }); + + it('should be instantiable', function () { + const client = new Client(); + expect(client).to.exist; + expect(client.network).to.be.equal('testnet'); + expect(client.getDAPIClient().constructor.name).to.be.equal('DAPIClient'); + }); + + it('should not initiate wallet lib without mnemonic', function () { + const client = new Client(); + expect(client.wallet).to.be.equal(undefined); + }); + + it('should initiate wallet-lib with a mnemonic', async ()=>{ + const client = new Client({ + wallet: { + mnemonic: testMnemonic, + offlineMode: true, + } + }); + expect(client.wallet).to.exist; + expect(client.wallet!.offlineMode).to.be.equal(true); + + await client.wallet?.storage.stopWorker(); + await client.wallet?.disconnect(); + + const account = await client.getWalletAccount(); + await account.disconnect(); + }); + + it('should throw an error if client and wallet have different networks', async () => { + try { + new Client({ + network: 'testnet', + wallet: { + mnemonic: testMnemonic, + offlineMode: true, + network: 'evonet', + }, + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e.message).to.equal('Wallet and Client networks are different'); + } + }); + + describe('#platform.identities.register', async () => { + it('should register an identity', async () => { + const accountIdentitiesCountBeforeTest = account.identities.getIdentityIds().length; + + const identity = await client.platform.identities.register(); + + expect(identity).to.be.not.null; + + const serializedSt = dapiClientMock.platform.broadcastStateTransition.getCall(0).args[0]; + const interceptedIdentityStateTransition = await client.platform.dpp.stateTransition.createFromBuffer(serializedSt); + const interceptedAssetLockProof = interceptedIdentityStateTransition.getAssetLockProof(); + + const transaction = new Transaction(transportMock.sendTransaction.getCall(0).args[0]); + const isLock = createFakeInstantLock(transaction.hash); + + // Check intercepted st + expect(interceptedAssetLockProof.getInstantLock()).to.be.deep.equal(isLock); + expect(interceptedAssetLockProof.getTransaction().hash).to.be.equal(transaction.hash); + + const importedIdentityIds = account.identities.getIdentityIds(); + // Check that we've imported identities properly + expect(importedIdentityIds.length).to.be.equal(accountIdentitiesCountBeforeTest + 1); + expect(importedIdentityIds[1]).to.be.equal(interceptedIdentityStateTransition.getIdentityId().toString()); + }); + + it('should throw TransitionBroadcastError when transport resolves error', async () => { + const accountIdentitiesCountBeforeTest = account.identities.getIdentityIds().length; + + const errorResponse = { + error: { + code: 2, + message: "Error happened", + data: {}, + } + }; + + dapiClientMock.platform.waitForStateTransitionResult.resolves(errorResponse); + + let error; + try { + await client.platform.identities.register(); + } catch (e) { + error = e; + } + + expect(error).to.be.an.instanceOf(StateTransitionBroadcastError); + expect(error.getCode()).to.be.equal(errorResponse.error.code); + expect(error.getMessage()).to.be.equal(errorResponse.error.message); + + const importedIdentityIds = account.identities.getIdentityIds(); + // Check that no identities were imported + expect(importedIdentityIds.length).to.be.equal(accountIdentitiesCountBeforeTest); + }); + }); + + describe('#platform.identities.topUp', async () => { + it('should top up an identity', async () => { + // Registering an identity we're going to top up + const identity = await client.platform.identities.register(); + // Topping up the identity + await client.platform.identities.topUp(identity.getId(), 10000); + + expect(identity).to.be.not.null; + + const serializedSt = dapiClientMock.platform.broadcastStateTransition.getCall(1).args[0]; + const interceptedIdentityStateTransition = await client.platform.dpp.stateTransition.createFromBuffer(serializedSt); + const interceptedAssetLockProof = interceptedIdentityStateTransition.getAssetLockProof(); + + expect(interceptedIdentityStateTransition.getType()).to.be.equal(stateTransitionTypes.IDENTITY_TOP_UP); + + const transaction = new Transaction(transportMock.sendTransaction.getCall(1).args[0]); + const isLock = createFakeInstantLock(transaction.hash); + // Check intercepted st + expect(interceptedAssetLockProof.getInstantLock()).to.be.deep.equal(isLock); + expect(interceptedAssetLockProof.getTransaction().hash).to.be.equal(transaction.hash); + }); + + it('should throw TransitionBroadcastError when transport resolves error', async () => { + // Registering an identity we're going to top up + const identity = await client.platform.identities.register(); + + const errorResponse = { + error: { + code: 2, + message: "Error happened", + data: {}, + } + }; + + dapiClientMock.platform.waitForStateTransitionResult.resolves(errorResponse); + + let error; + try { + // Topping up the identity + await client.platform.identities.topUp(identity.getId(), 10000); + } catch (e) { + error = e; + } + + expect(error).to.be.an.instanceOf(StateTransitionBroadcastError); + expect(error.getCode()).to.be.equal(errorResponse.error.code); + expect(error.getMessage()).to.be.equal(errorResponse.error.message); + }); + }); + + describe('#platform.documents.broadcast', () => { + it('should throw TransitionBroadcastError when transport resolves error', async () => { + const errorResponse = { + error: { + code: 2, + message: "Error happened", + data: {}, + } + }; + + dapiClientMock.platform.waitForStateTransitionResult.resolves(errorResponse); + + let error; + try { + await client.platform.documents.broadcast({ + create: documentsFixture, + }, identityFixture); + } catch (e) { + error = e; + } + + expect(error).to.be.an.instanceOf(StateTransitionBroadcastError); + expect(error.getCode()).to.be.equal(errorResponse.error.code); + expect(error.getMessage()).to.be.equal(errorResponse.error.message); + }); + + it('should broadcast documents', async () => { + const proofResponse = { + proof: { } + } + + dapiClientMock.platform.waitForStateTransitionResult.resolves(proofResponse); + + await client.platform.documents.broadcast({ + create: documentsFixture, + }, identityFixture); + + const serializedSt = dapiClientMock.platform.broadcastStateTransition.getCall(0).args[0]; + const interceptedSt = await client.platform.dpp.stateTransition.createFromBuffer(serializedSt); + + // .to.be.true() doesn't work after TS compilation in Chrome + expect(await interceptedSt.verifySignature(identityFixture.getPublicKeyById(1))).to.be.equal(true); + + const documentTransitions = interceptedSt.getTransitions(); + + expect(documentTransitions.length).to.be.greaterThan(0); + expect(documentTransitions.length).to.be.equal(documentsFixture.length); + }); + }); + + describe('#platform.contracts.publish', () => { + it('should throw TransitionBroadcastError when transport resolves error', async () => { + const errorResponse = { + error: { + code: 2, + message: "Error happened", + data: {}, + } + }; + + dapiClientMock.platform.waitForStateTransitionResult.resolves(errorResponse); + + let error; + try { + await client.platform.contracts.publish(dataContractFixture, identityFixture); + } catch (e) { + error = e; + } + + expect(error).to.be.an.instanceOf(StateTransitionBroadcastError); + expect(error.getCode()).to.be.equal(errorResponse.error.code); + expect(error.getMessage()).to.be.equal(errorResponse.error.message); + }); + + it('should broadcast data contract', async () => { + dapiClientMock.platform.waitForStateTransitionResult.resolves({ + proof: { } + }); + + await client.platform.contracts.publish(dataContractFixture, identityFixture); + + const serializedSt = dapiClientMock.platform.broadcastStateTransition.getCall(0).args[0]; + const interceptedSt = await client.platform.dpp.stateTransition.createFromBuffer(serializedSt); + + // .to.be.true() doesn't work after TS compilation in Chrome + expect(await interceptedSt.verifySignature(identityFixture.getPublicKeyById(1))).to.be.equal(true); + expect(interceptedSt.getEntropy()).to.be.deep.equal(dataContractFixture.entropy); + expect(interceptedSt.getDataContract().toObject()).to.be.deep.equal(dataContractFixture.toObject()); + }); + }); +}); diff --git a/packages/js-dash-sdk/src/SDK/Client/Client.ts b/packages/js-dash-sdk/src/SDK/Client/Client.ts new file mode 100644 index 00000000000..61ab9783878 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Client.ts @@ -0,0 +1,181 @@ +import { EventEmitter } from 'events'; +import { Account, Wallet } from "@dashevo/wallet-lib"; +import DAPIClientTransport from "@dashevo/wallet-lib/src/transport/DAPIClientTransport/DAPIClientTransport" +import { Platform } from './Platform'; +import { Network } from "@dashevo/dashcore-lib"; +import DAPIClient from "@dashevo/dapi-client"; +import { contractId as dpnsContractId } from "@dashevo/dpns-contract/lib/systemIds"; +import { contractId as dashpayContractId } from "@dashevo/dashpay-contract/lib/systemIds"; +import { contractId as masternodeRewardSharesContractId } from "@dashevo/masternode-reward-shares-contract/lib/systemIds"; +import { ClientApps, ClientAppsOptions } from "./ClientApps"; + +export interface WalletOptions extends Wallet.IWalletOptions { + defaultAccountIndex?: number; +} + +/** + * Interface Client Options + * + * @param {ClientApps?} [apps] - applications + * @param {WalletOptions} [wallet] - Wallet options + * @param {DAPIAddressProvider} [dapiAddressProvider] - DAPI Address Provider instance + * @param {Array} [dapiAddresses] - DAPI addresses + * @param {string[]|RawDAPIAddress[]} [seeds] - DAPI seeds + * @param {string|Network} [network=evonet] - Network name + * @param {number} [timeout=2000] + * @param {number} [retries=3] + * @param {number} [baseBanTime=60000] + */ +export interface ClientOpts { + apps?: ClientAppsOptions, + wallet?: WalletOptions, + dapiAddressProvider?: any, + dapiAddresses?: any[], + seeds?: any[], + network?: Network | string, + timeout?: number, + retries?: number, + baseBanTime?: number, + driveProtocolVersion?: number, +} + +/** + * Client class that wraps all components together to allow integrated payments on both the Dash Network (layer 1) + * and the Dash Platform (layer 2). + */ +export class Client extends EventEmitter { + public network: string = 'testnet'; + public wallet: Wallet | undefined; + public account: Account | undefined; + public platform: Platform; + public defaultAccountIndex: number | undefined = 0; + private readonly dapiClient: DAPIClient; + private readonly apps: ClientApps; + private options: ClientOpts; + + /** + * Construct some instance of SDK Client + * + * @param {ClientOpts} [options] - options for SDK Client + */ + constructor(options: ClientOpts = {}) { + super(); + + this.options = options; + + this.network = this.options.network ? this.options.network.toString() : 'testnet'; + + // Initialize DAPI Client + const dapiClientOptions = { + network: this.network, + }; + + [ + 'dapiAddressProvider', + 'dapiAddresses', + 'seeds', + 'timeout', + 'retries', + 'baseBanTime' + ].forEach((optionName) => { + if (this.options.hasOwnProperty(optionName)) { + dapiClientOptions[optionName] = this.options[optionName]; + } + }); + + this.dapiClient = new DAPIClient(dapiClientOptions); + + // Initialize a wallet if `wallet` option is preset + if (this.options.wallet !== undefined) { + if (this.options.wallet.network !== undefined && this.options.wallet.network !== this.network) { + throw new Error('Wallet and Client networks are different'); + } + + const transport = new DAPIClientTransport(this.dapiClient); + + this.wallet = new Wallet({ + transport, + network: this.network, + ...this.options.wallet, + }); + + // @ts-ignore + this.wallet.on('error', (error, context) => ( + this.emit('error', error, { wallet: context }) + )); + } + + // @ts-ignore + this.defaultAccountIndex = this.options.wallet?.defaultAccountIndex || 0; + + this.apps = new ClientApps(Object.assign({ + dpns: { + contractId: dpnsContractId, + }, + dashpay: { + contractId: dashpayContractId, + }, + masternodeRewardShares: { + contractId: masternodeRewardSharesContractId, + } + }, this.options.apps)); + + this.platform = new Platform({ + client: this, + network: this.network, + driveProtocolVersion: this.options.driveProtocolVersion, + }); + } + + /** + * Get Wallet account + * + * @param {Account.Options} [options] + * @returns {Promise} + */ + async getWalletAccount(options: Account.Options = {}) : Promise { + if (!this.wallet) { + throw new Error('Wallet is not initialized, pass `wallet` option to Client'); + } + + options = { + index: this.defaultAccountIndex, + ...options, + } + + return this.wallet.getAccount(options); + } + + /** + * disconnect wallet from Dapi + * @returns {void} + */ + async disconnect() { + if (this.wallet) { + await this.wallet.disconnect(); + } + } + + /** + * Get DAPI Client instance + * + * @returns {DAPIClient} + */ + getDAPIClient() : DAPIClient { + return this.dapiClient; + } + + /** + * fetch list of applications + * + * @remarks + * check if returned value can be null on devnet + * + * @returns {ClientApps} applications list + */ + getApps(): ClientApps { + return this.apps; + } +} + +export default Client; diff --git a/packages/js-dash-sdk/src/SDK/Client/ClientApps.spec.ts b/packages/js-dash-sdk/src/SDK/Client/ClientApps.spec.ts new file mode 100644 index 00000000000..a361457ecbe --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/ClientApps.spec.ts @@ -0,0 +1,46 @@ +import Identifier from "@dashevo/dpp/lib/Identifier"; +import {expect} from 'chai'; +import {ClientApps} from "./ClientApps"; +import 'mocha'; + +describe('ClientApps', () => { + let apps; + let appsFromProps; + it('constructor', function () { + apps = new ClientApps(); + expect(apps.apps).to.deep.equal({}); + appsFromProps = new ClientApps({ + "dpns": { + contractId: '3VvS19qomuGSbEYWbTsRzeuRgawU3yK4fPMzLrbV62u8', + contract: null, + } + }); + }); + it('.set', function () { + apps.set('dpns', { + contractId: '3VvS19qomuGSbEYWbTsRzeuRgawU3yK4fPMzLrbV62u8', + contract: { someField: true } + }); + apps.set('tutorialContract', { + contractId: '3VvS19qomuGSbEYWbTsRzeuRgawU3yK4fPMzLrbV62u8', + contract: { someField: true } + }); + }); + it('should get', function () { + const getByName = apps.get('dpns'); + expect(getByName).to.deep.equal({ + "contractId": Identifier.from("3VvS19qomuGSbEYWbTsRzeuRgawU3yK4fPMzLrbV62u8"), + "contract": { someField: true } + }) + }); + + it('should .getNames()', function () { + const names = apps.getNames(); + expect(names).to.deep.equal(['dpns', 'tutorialContract']); + }); + it('should .has', function () { + expect(apps.has('dpns')).to.equal(true); + expect(apps.has('tutorialContract')).to.equal(true); + expect(apps.has('tutorialContractt')).to.equal(false); + }); +}); diff --git a/packages/js-dash-sdk/src/SDK/Client/ClientApps.ts b/packages/js-dash-sdk/src/SDK/Client/ClientApps.ts new file mode 100644 index 00000000000..533d10741d6 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/ClientApps.ts @@ -0,0 +1,73 @@ +import Identifier from "@dashevo/dpp/lib/Identifier"; + +/** + * Interface for ClientApps + */ +export interface ClientAppsOptions { + [name: string]: ClientAppDefinitionOptions, +} + +interface ClientAppDefinitionOptions { + contractId: Identifier|string, + contract?: any +} + +interface ClientAppDefinition { + contractId: Identifier, + contract?: any +} + +type ClientAppsList = Record; + +export class ClientApps { + private apps: ClientAppsList = {}; + + constructor(apps: ClientAppsOptions = {}) { + Object.entries(apps).forEach(([name, definition]) => this.set(name, definition)); + } + + /** + * Set app + * + * @param {string} name + * @param {ClientAppDefinitionOptions} definition + */ + set(name: string, definition: ClientAppDefinitionOptions) { + definition.contractId = Identifier.from(definition.contractId); + + this.apps[name] = definition; + } + + /** + * Get app definition by name + * + * @param {string} name + * @return {ClientAppDefinition} + */ + get(name: string): ClientAppDefinition { + if (!this.has(name)) { + throw new Error(`Application with name ${name} is not defined`); + } + + return this.apps[name]; + } + + /** + * Check if app is defined + * + * @param {string} name + * @return {boolean} + */ + has(name: string): boolean { + return Boolean(this.apps[name]); + } + + /** + * Get all apps + * + * @return {ClientAppsList} + */ + getNames(): Array { + return Object.keys(this.apps); + } +} diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/IPlatformStateProof.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/IPlatformStateProof.ts new file mode 100644 index 00000000000..158216bb3be --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/IPlatformStateProof.ts @@ -0,0 +1,3 @@ +export interface IPlatformStateProof { + merkleProof: Buffer, +} diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/IStateTransitionResult.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/IStateTransitionResult.ts new file mode 100644 index 00000000000..92529ad2085 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/IStateTransitionResult.ts @@ -0,0 +1,10 @@ +import { IPlatformStateProof } from "./IPlatformStateProof"; + +export interface IStateTransitionResult { + proof?: IPlatformStateProof, + error?: { + code: number, + message: string, + data: any, + } +} diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/Platform.spec.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/Platform.spec.ts new file mode 100644 index 00000000000..fa321b2c854 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/Platform.spec.ts @@ -0,0 +1,44 @@ +import { expect } from 'chai'; +import { Platform } from "./index"; +import 'mocha'; +import Client from '../Client'; +import { latestVersion as latestProtocolVersion } from "@dashevo/dpp/lib/version/protocolVersion"; + +describe('Dash - Platform', () => { + + it('should provide expected class', function () { + expect(Platform.name).to.be.equal('Platform') + expect(Platform.constructor.name).to.be.equal('Function') + }); + + it('should set protocol version for DPP though options', () => { + const platform = new Platform({ + client: new Client(), + network: 'testnet', + driveProtocolVersion: 42, + }); + + expect(platform.dpp.protocolVersion).to.equal(42); + }); + + it('should set protocol version for DPP using mapping', () => { + const platform = new Platform({ + client: new Client(), + network: 'testnet', + }); + + // @ts-ignore + const testnetProtocolVersion = Platform.networkToProtocolVersion.get('testnet'); + + expect(platform.dpp.protocolVersion).to.equal(testnetProtocolVersion); + }); + + it('should set protocol version for DPP using latest version', () => { + const platform = new Platform({ + client: new Client(), + network: 'unknown', + }); + + expect(platform.dpp.protocolVersion).to.equal(latestProtocolVersion); + }); +}); diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/Platform.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/Platform.ts new file mode 100644 index 00000000000..c48c370dee0 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/Platform.ts @@ -0,0 +1,198 @@ +// @ts-ignore +import DashPlatformProtocol from "@dashevo/dpp"; + +import Client from "../Client"; +import { IStateTransitionResult } from './IStateTransitionResult'; + +import createAssetLockTransaction from "./createAssetLockTransaction"; + +import broadcastDocument from "./methods/documents/broadcast"; +import createDocument from "./methods/documents/create"; +import getDocument from "./methods/documents/get"; + +import publishContract from "./methods/contracts/publish"; +import updateContract from "./methods/contracts/update"; +import createContract from "./methods/contracts/create"; +import getContract from "./methods/contracts/get"; + +import getIdentity from "./methods/identities/get"; +import registerIdentity from "./methods/identities/register"; +import topUpIdentity from "./methods/identities/topUp"; +import updateIdentity from "./methods/identities/update"; +import createIdentityCreateTransition from "./methods/identities/internal/createIdentityCreateTransition"; +import createIdentityTopUpTransition from "./methods/identities/internal/createIdnetityTopUpTransition"; +import createAssetLockProof from "./methods/identities/internal/createAssetLockProof"; +import waitForCoreChainLockedHeight from "./methods/identities/internal/waitForCoreChainLockedHeight"; + +import registerName from "./methods/names/register"; +import resolveName from "./methods/names/resolve"; +import resolveNameByRecord from "./methods/names/resolveByRecord"; +import searchName from "./methods/names/search"; +import broadcastStateTransition from "./broadcastStateTransition"; +import StateRepository from './StateRepository'; +import { latestVersion as latestProtocolVersion } from "@dashevo/dpp/lib/version/protocolVersion"; + +/** + * Interface for PlatformOpts + * + * @remarks + * required parameters include { client, apps } + */ +export interface PlatformOpts { + client: Client, + network: string, + driveProtocolVersion?: number, +} + +/** + * @param {Function} broadcast - broadcast records onto the platform + * @param {Function} create - create records which can be broadcasted + * @param {Function} get - get records from the platform + */ +interface Records { + broadcast: Function, + create: Function, + get: Function, +} + +/** + * @param {Function} register - register a domain + * @param {Function} resolve - resolve domain by a name + * @param {Function} resolveByRecord - resolve domain by it's record + * @param {Function} search - search domain + */ +interface DomainNames { + register: Function, + resolve: Function, + resolveByRecord: Function, + search: Function, +} + +interface Identities { + get: Function, + register: Function, + topUp: Function, + update: Function, + utils: { + createAssetLockTransaction: Function + createAssetLockProof: Function + createIdentityCreateTransition: Function + createIdentityTopUpTransition: Function + waitForCoreChainLockedHeight: Function + } +} + +interface DataContracts { + update: Function, + publish: Function, + create: Function, + get: Function, +} + +/** + * Class for Dash Platform + * + * @param documents - documents + * @param identities - identites + * @param names - names + * @param contracts - contracts + */ +export class Platform { + dpp: DashPlatformProtocol; + + public documents: Records; + /** + * @param {Function} get - get identities from the platform + * @param {Function} register - register identities on the platform + */ + public identities: Identities; + /** + * @param {Function} get - get names from the platform + * @param {Function} register - register names on the platform + */ + public names: DomainNames; + /** + * @param {Function} get - get contracts from the platform + * @param {Function} create - create contracts which can be broadcasted + * @param {Function} register - register contracts on the platform + */ + public contracts: DataContracts; + + /** + * Broadcasts state transition + * @param {Object} stateTransition + */ + public broadcastStateTransition(stateTransition: any): Promise { + return broadcastStateTransition(this, stateTransition); + }; + + client: Client; + + private static readonly networkToProtocolVersion: Map = new Map([ + ['testnet', 1], + ]); + + /** + * Construct some instance of Platform + * + * @param {PlatformOpts} options - options for Platform + */ + constructor(options: PlatformOpts) { + this.documents = { + broadcast: broadcastDocument.bind(this), + create: createDocument.bind(this), + get: getDocument.bind(this), + }; + this.contracts = { + publish: publishContract.bind(this), + update: updateContract.bind(this), + create: createContract.bind(this), + get: getContract.bind(this), + }; + this.names = { + register: registerName.bind(this), + resolve: resolveName.bind(this), + resolveByRecord: resolveNameByRecord.bind(this), + search: searchName.bind(this), + }; + this.identities = { + register: registerIdentity.bind(this), + get: getIdentity.bind(this), + topUp: topUpIdentity.bind(this), + update: updateIdentity.bind(this), + utils: { + createAssetLockProof: createAssetLockProof.bind(this), + createAssetLockTransaction: createAssetLockTransaction.bind(this), + createIdentityCreateTransition: createIdentityCreateTransition.bind(this), + createIdentityTopUpTransition: createIdentityTopUpTransition.bind(this), + waitForCoreChainLockedHeight: waitForCoreChainLockedHeight.bind(this), + } + }; + + this.client = options.client; + + const mappedProtocolVersion = Platform.networkToProtocolVersion.get( + options.network, + ); + + // use protocol version from options if set + // use mapped one otherwise + // fallback to one that set in dpp as the last option + const driveProtocolVersion = options.driveProtocolVersion !== undefined + ? options.driveProtocolVersion + : (mappedProtocolVersion !== undefined ? mappedProtocolVersion : latestProtocolVersion); + + const stateRepository = new StateRepository(this.client); + + this.dpp = new DashPlatformProtocol({ + stateRepository, + protocolVersion: driveProtocolVersion, + ...options, + }); + } + + async initialize() { + await this.dpp.initialize(); + } +} + diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/StateRepository.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/StateRepository.ts new file mode 100644 index 00000000000..67f3e97b38f --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/StateRepository.ts @@ -0,0 +1,55 @@ +import DataContract from "@dashevo/dpp/lib/dataContract/DataContract"; +import Identity from "@dashevo/dpp/lib/identity/Identity"; +import Identifier from "@dashevo/dpp/lib/Identifier"; +import Client from '../Client'; + +class StateRepository { + private readonly client: Client; + + constructor(client: Client) { + this.client = client; + } + + async fetchIdentity(id: Identifier|string): Promise { + return this.client.platform.identities.get(id); + } + + async fetchDataContract(identifier: Identifier|string): Promise { + return this.client.platform.contracts.get(identifier); + } + + async isAssetLockTransactionOutPointAlreadyUsed(): Promise { + // This check still exists on the client side, however there's no need to + // perform the check as in this client we always use a new transaction + // register/top up identity + return false; + } + + async verifyInstantLock(): Promise { + // verification will be implemented later with DAPI SPV functionality + return true; + } + + async fetchTransaction(id: string): Promise<{ data: Buffer, height: number }> { + const walletAccount = await this.client.getWalletAccount(); + // @ts-ignore + const { transaction } = await walletAccount.getTransaction(id); + + return { + // @ts-ignore + data: transaction.toBuffer(), + // we don't have transaction heights atm and it will be implemented later with DAPI SPV functionality + height: 1, + }; + } + + async fetchLatestPlatformBlockHeader(id: string): Promise<{ coreChainLockedHeight: number }> { + const coreChainLockedHeight = await this.client.wallet!.transport.getBestBlockHeight(); + + return { + coreChainLockedHeight, + }; + } +} + +export default StateRepository; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/broadcastStateTransition.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/broadcastStateTransition.ts new file mode 100644 index 00000000000..3c944a82aea --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/broadcastStateTransition.ts @@ -0,0 +1,95 @@ +import crypto from "crypto"; +import { Platform } from "./Platform"; +import { StateTransitionBroadcastError } from "../../../errors/StateTransitionBroadcastError"; +import { IStateTransitionResult } from "./IStateTransitionResult"; + +const ResponseError = require('@dashevo/dapi-client/lib/transport/errors/response/ResponseError'); +const InvalidRequestDPPError = require('@dashevo/dapi-client/lib/transport/errors/response/InvalidRequestDPPError'); + +const createGrpcTransportError = require('@dashevo/dapi-client/lib/transport/GrpcTransport/createGrpcTransportError'); + +const GrpcError = require('@dashevo/grpc-common/lib/server/error/GrpcError'); + +/** + * @param {Platform} platform + * @param {Object} [options] + * @param {boolean} [options.skipValidation=false] + * + * @param stateTransition + */ +export default async function broadcastStateTransition( + platform: Platform, + stateTransition: any, + options: { skipValidation?: boolean; } = {}, +): Promise { + const { client, dpp } = platform; + + if (!options.skipValidation) { + const result = await dpp.stateTransition.validateBasic(stateTransition); + + if (!result.isValid()) { + const consensusError = result.getFirstError(); + + throw new StateTransitionBroadcastError( + consensusError.getCode(), + consensusError.message, + consensusError, + ); + } + } + + // Subscribing to future result + const hash = crypto.createHash('sha256') + .update(stateTransition.toBuffer()) + .digest(); + + const serializedStateTransition = stateTransition.toBuffer(); + + try { + await client.getDAPIClient().platform.broadcastStateTransition(serializedStateTransition); + } catch (error) { + if (error instanceof ResponseError) { + let cause = error; + + // Pass DPP consensus error directly to avoid + // additional wrappers + if (cause instanceof InvalidRequestDPPError) { + cause = cause.getConsensusError(); + } + + throw new StateTransitionBroadcastError( + cause.getCode(), + cause.message, + cause, + ); + } + + throw error; + } + + // Waiting for result to return + const stateTransitionResult: IStateTransitionResult = await client.getDAPIClient().platform.waitForStateTransitionResult(hash, { prove: true }); + + let { error } = stateTransitionResult; + + if (error) { + // Create DAPI response error from gRPC error passed as gRPC response + const grpcError = new GrpcError(error.code, error.message, error.data); + + let cause = createGrpcTransportError(grpcError); + + // Pass DPP consensus error directly to avoid + // additional wrappers + if (cause instanceof InvalidRequestDPPError) { + cause = cause.getConsensusError(); + } + + throw new StateTransitionBroadcastError( + cause.getCode(), + cause.message, + cause, + ); + } + + return stateTransitionResult; +} diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/createAssetLockTransaction.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/createAssetLockTransaction.ts new file mode 100644 index 00000000000..a0985c879eb --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/createAssetLockTransaction.ts @@ -0,0 +1,66 @@ +import { PrivateKey, Transaction } from "@dashevo/dashcore-lib"; +import { utils } from "@dashevo/wallet-lib"; +import { Platform } from "./Platform"; + +// We're creating a new transaction every time and the index is always 0 +const ASSET_LOCK_OUTPUT_INDEX = 0; + +/** + * Creates a funding transaction for the platform identity and returns one-time key to sign the state transition + * @param {Platform} this + * @param {number} fundingAmount - amount of dash to fund the identity's credits + * @return {Promise<{transaction: Transaction, privateKey: PrivateKey}>} - transaction and one time private key + * that can be used to sign registration/top-up state transition + */ +export async function createAssetLockTransaction(this : Platform, fundingAmount): Promise<{ transaction: Transaction, privateKey: PrivateKey, outputIndex: number }> { + const platform = this; + const account = await platform.client.getWalletAccount(); + + // @ts-ignore + const assetLockOneTimePrivateKey = new PrivateKey(); + const assetLockOneTimePublicKey = assetLockOneTimePrivateKey.toPublicKey(); + + const identityAddress = assetLockOneTimePublicKey.toAddress(platform.client.network).toString(); + + const changeAddress = account.getUnusedAddress('internal').address; + + const lockTransaction = new Transaction(undefined); + + const output = { + satoshis: fundingAmount, + address: identityAddress + }; + + const utxos = account.getUTXOS(); + const balance = account.getTotalBalance(); + + if (balance < output.satoshis) { + throw new Error(`Not enough balance (${balance}) to cover burn amount of ${fundingAmount}`); + } + + const selection = utils.coinSelection(utxos, [output]); + + lockTransaction + .from(selection.utxos) + // @ts-ignore + .addBurnOutput(output.satoshis, assetLockOneTimePublicKey._getID()) + .change(changeAddress); + + const utxoAddresses = selection.utxos.map((utxo: any) => utxo.address.toString()); + + // @ts-ignore + const utxoHDPrivateKey = account.getPrivateKeys(utxoAddresses); + + // @ts-ignore + const signingKeys = utxoHDPrivateKey.map((hdprivateKey) => hdprivateKey.privateKey); + + const transaction = lockTransaction.sign(signingKeys); + + return { + transaction, + privateKey: assetLockOneTimePrivateKey, + outputIndex: ASSET_LOCK_OUTPUT_INDEX, + }; +} + +export default createAssetLockTransaction; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/index.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/index.ts new file mode 100644 index 00000000000..be13241c9eb --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/index.ts @@ -0,0 +1 @@ +export { Platform, PlatformOpts } from './Platform'; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/create.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/create.ts new file mode 100644 index 00000000000..34d78dcccdf --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/create.ts @@ -0,0 +1,17 @@ +import {Platform} from "../../Platform"; + +/** + * Create and prepare contracts for the platform + * + * @param {Platform} this - bound instance class + * @param contractDefinitions - contract definitions + * @param identity - identity + * @returns created contracts + */ +export async function create(this: Platform, contractDefinitions: any, identity: any): Promise { + await this.initialize(); + + return this.dpp.dataContract.create(identity.getId(), contractDefinitions); +} + +export default create; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/get.spec.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/get.spec.ts new file mode 100644 index 00000000000..351dcaa848e --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/get.spec.ts @@ -0,0 +1,94 @@ +import {expect} from 'chai'; +import getResponseMetadataFixture from '../../../../../test/fixtures/getResponseMetadataFixture'; +import get from "./get"; +import identitiesFixtures from "../../../../../../tests/fixtures/identities.json"; +import contractsFixtures from "../../../../../../tests/fixtures/contracts.json"; +import DataContractFactory from "@dashevo/dpp/lib/dataContract/DataContractFactory"; +import ValidationResult from "@dashevo/dpp/lib/validation/ValidationResult"; +import Identifier from "@dashevo/dpp/lib/Identifier"; +import 'mocha'; +import { ClientApps } from "../../../ClientApps"; +const GetDataContractResponse = require("@dashevo/dapi-client/lib/methods/platform/getDataContract/GetDataContractResponse"); +const NotFoundError = require('@dashevo/dapi-client/lib/transport/GrpcTransport/errors/NotFoundError'); + +const factory = new DataContractFactory( + undefined, + () => { + return new ValidationResult(); + }, + () => [42, contractsFixtures.ratePlatform]); +const dpp = { + dataContract: factory, + getProtocolVersion: () => 42, +} +factory.dpp = dpp; + +const apps = new ClientApps({ + ratePlatform: { + contractId: contractsFixtures.ratePlatform.$id + }, +}); +let client; +let askedFromDapi; +let initialize; + +describe('Client - Platform - Contracts - .get()', () => { + before(function before() { + askedFromDapi = 0; + const getDataContract = async (id) => { + const fixtureIdentifier = Identifier.from(contractsFixtures.ratePlatform.$id); + askedFromDapi += 1; + + if (id.equals(fixtureIdentifier)) { + const contract = await dpp.dataContract.createFromObject(contractsFixtures.ratePlatform); + return new GetDataContractResponse(contract.toBuffer(), getResponseMetadataFixture()); + } + + throw new NotFoundError(); + }; + + client = { + getDAPIClient: () => { + return { + platform: { + getDataContract + } + }; + }, + getApps(): ClientApps { + return apps + } + }; + + initialize = this.sinon.stub(); + }); + + describe('get a contract from string', () => { + it('should get from DAPIClient if there is none locally', async function () { + + // @ts-ignore + const contract = await get.call({apps, dpp, client, initialize}, contractsFixtures.ratePlatform.$id); + expect(contract.toJSON()).to.deep.equal(contractsFixtures.ratePlatform); + expect(contract.getMetadata().getBlockHeight()).to.equal(10); + expect(contract.getMetadata().getCoreChainLockedHeight()).to.equal(42); + expect(askedFromDapi).to.equal(1); + }); + + it('should get from local when already fetched once', async function () { + // @ts-ignore + const contract = await get.call({apps, dpp, client, initialize}, contractsFixtures.ratePlatform.$id); + expect(contract.toJSON()).to.deep.equal(contractsFixtures.ratePlatform); + expect(contract.getMetadata().getBlockHeight()).to.equal(10); + expect(contract.getMetadata().getCoreChainLockedHeight()).to.equal(42); + expect(askedFromDapi).to.equal(1); + }); + }) + + describe('other conditions', () => { + it('should deal when contract do not exist', async function () { + // @ts-ignore + const contract = await get.call({apps, dpp, client, initialize}, identitiesFixtures.bob.id); + expect(contract).to.equal(null); + }); + }); +}); diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/get.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/get.ts new file mode 100644 index 00000000000..e756ff7accb --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/get.ts @@ -0,0 +1,66 @@ +import {Platform} from "../../Platform"; + +// @ts-ignore +import Identifier from "@dashevo/dpp/lib/Identifier"; +import Metadata from "@dashevo/dpp/lib/Metadata"; +const NotFoundError = require('@dashevo/dapi-client/lib/transport/GrpcTransport/errors/NotFoundError'); + +declare type ContractIdentifier = string | Identifier; + +/** + * Get contracts from the platform + * + * @param {Platform} this - bound instance class + * @param {ContractIdentifier} identifier - identifier of the contract to fetch + * @returns contracts + */ +export async function get(this: Platform, identifier: ContractIdentifier): Promise { + await this.initialize(); + + const contractId : Identifier = Identifier.from(identifier); + + // Try to get contract from the cache + for (const appName of this.client.getApps().getNames()) { + const appDefinition = this.client.getApps().get(appName); + if (appDefinition.contractId.equals(contractId) && appDefinition.contract) { + return appDefinition.contract; + } + } + + // Fetch contract otherwise + let dataContractResponse; + try { + dataContractResponse = await this.client.getDAPIClient().platform.getDataContract(contractId); + } catch (e) { + if (e instanceof NotFoundError) { + return null; + } + + throw e; + } + + const contract = await this.dpp.dataContract.createFromBuffer(dataContractResponse.getDataContract()); + + let metadata = null; + const responseMetadata = dataContractResponse.getMetadata(); + if (responseMetadata) { + metadata = new Metadata({ + blockHeight: responseMetadata.getHeight(), + coreChainLockedHeight: responseMetadata.getCoreChainLockedHeight(), + }); + } + contract.setMetadata(metadata); + + // Store contract to the cache + + for (const appName of this.client.getApps().getNames()) { + const appDefinition = this.client.getApps().get(appName); + if (appDefinition.contractId.equals(contractId)) { + appDefinition.contract = contract; + } + } + + return contract; +} + +export default get; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/publish.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/publish.ts new file mode 100644 index 00000000000..0c788fbacbf --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/publish.ts @@ -0,0 +1,24 @@ +import { Platform } from "../../Platform"; +import broadcastStateTransition from "../../broadcastStateTransition"; +import { signStateTransition } from "../../signStateTransition"; + +/** + * Publish contract onto the platform + * + * @param {Platform} this - bound instance class + * @param dataContract - contract + * @param identity - identity + * @return {DataContractCreateTransition} + */ +export default async function publish(this: Platform, dataContract: any, identity: any): Promise { + await this.initialize(); + + const { dpp } = this; + + const dataContractCreateTransition = dpp.dataContract.createDataContractCreateTransition(dataContract); + + await signStateTransition(this, dataContractCreateTransition, identity, 1); + await broadcastStateTransition(this, dataContractCreateTransition); + + return dataContractCreateTransition; +} diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/update.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/update.ts new file mode 100644 index 00000000000..37be2e94b45 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/update.ts @@ -0,0 +1,39 @@ +import { Platform } from "../../Platform"; +import broadcastStateTransition from "../../broadcastStateTransition"; +import { signStateTransition } from "../../signStateTransition"; + +/** + * Publish contract onto the platform + * + * @param {Platform} this - bound instance class + * @param {DataContract} dataContract - contract + * @param identity - identity + * @return {DataContractUpdateTransition} + */ +export default async function update(this: Platform, dataContract: any, identity: any): Promise { + await this.initialize(); + + const { dpp } = this; + + // Clone contract + const updatedDataContract = await this.dpp.dataContract.createFromObject( + dataContract.toObject(), + ); + + updatedDataContract.incrementVersion(); + + const dataContractUpdateTransition = dpp.dataContract.createDataContractUpdateTransition(updatedDataContract); + + await signStateTransition(this, dataContractUpdateTransition, identity, 1); + await broadcastStateTransition(this, dataContractUpdateTransition); + + // Update app with updated data contract if available + for (const appName of this.client.getApps().getNames()) { + const appDefinition = this.client.getApps().get(appName); + if (appDefinition.contractId.equals(updatedDataContract.getId()) && appDefinition.contract) { + appDefinition.contract = updatedDataContract; + } + } + + return dataContractUpdateTransition; +} diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/broadcast.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/broadcast.ts new file mode 100644 index 00000000000..aef6f48734d --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/broadcast.ts @@ -0,0 +1,29 @@ +import { Platform } from "../../Platform"; +import broadcastStateTransition from '../../broadcastStateTransition'; +import Document from '@dashevo/dpp/lib/document/Document'; +import { signStateTransition } from "../../signStateTransition"; + +/** + * Broadcast document onto the platform + * + * @param {Platform} this - bound instance class + * @param {Object} documents + * @param {Document[]} [documents.create] + * @param {Document[]} [documents.replace] + * @param {Document[]} [documents.delete] + * @param identity - identity + */ +export default async function broadcast(this: Platform, documents: { create?: Document[], replace?: Document[], delete?: Document[]}, identity: any): Promise { + await this.initialize(); + + const { dpp } = this; + + const documentsBatchTransition = dpp.document.createStateTransition(documents); + + await signStateTransition(this, documentsBatchTransition, identity, 1); + + // Broadcast state transition also wait for the result to be obtained + await broadcastStateTransition(this, documentsBatchTransition); + + return documentsBatchTransition; +} diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/create.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/create.ts new file mode 100644 index 00000000000..3a1883ca9e7 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/create.ts @@ -0,0 +1,41 @@ +import {Platform} from "../../Platform"; + +declare interface createOpts { + [name:string]: any; +} + +/** + * Create and prepare documents for the platform + * + * @param {Platform} this - bound instance class + * @param {string} typeLocator - type locator + * @param identity - identity + * @param {Object} [data] - options + */ +export async function create(this: Platform, typeLocator: string, identity: any, data: createOpts = {}): Promise { + await this.initialize(); + + const { dpp } = this; + + const appNames = this.client.getApps().getNames(); + + //We can either provide of type `dashpay.profile` or if only one schema provided, of type `profile`. + const [appName, fieldType] = (typeLocator.includes('.')) ? typeLocator.split('.') : [appNames[0], typeLocator]; + + const { contractId } = this.client.getApps().get(appName); + + const dataContract = await this.contracts.get(contractId); + + if (dataContract === null) { + throw new Error(`Contract ${appName} not found. Ensure contractId ${contractId} is correct.`) + } + + return dpp.document.create( + dataContract, + identity.getId(), + fieldType, + data, + ); +} + +export default create; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/get.spec.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/get.spec.ts new file mode 100644 index 00000000000..71174e85905 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/get.spec.ts @@ -0,0 +1,179 @@ +import getDataContractFixture from '@dashevo/dpp/lib/test/fixtures/getDataContractFixture'; +import generateRandomIdentifier from '@dashevo/dpp/lib/test/utils/generateRandomIdentifier'; +import createDPPMock from '@dashevo/dpp/lib/test/mocks/createDPPMock'; +import getDocumentsFixture from '@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'; +import getResponseMetadataFixture from '../../../../../test/fixtures/getResponseMetadataFixture'; +const GetDocumentsResponse = require("@dashevo/dapi-client/lib/methods/platform/getDocuments/GetDocumentsResponse"); + +import get from './get'; +import { expect } from 'chai'; + +describe('Client - Platform - Documents - .get()', () => { + let platform; + let dataContract; + let appDefinition; + let getDocumentsMock; + let appsGetMock; + + beforeEach(function beforeEach() { + dataContract = getDataContractFixture(); + + appDefinition = { + contractId: dataContract.getId(), + contract: dataContract, + }; + + getDocumentsMock = this.sinon.stub().resolves(new GetDocumentsResponse([], getResponseMetadataFixture())); + appsGetMock = this.sinon.stub().returns(appDefinition); + + platform = { + dpp: createDPPMock(this.sinon), + client: { + getApps: () => ({ + has: this.sinon.stub().returns(true), + get: appsGetMock, + }), + getDAPIClient: () => ({ + platform: { + getDocuments: getDocumentsMock, + }, + }) + }, + initialize: this.sinon.stub(), + }; + }); + + it('should convert identifier properties inside where condition', async () => { + const id = generateRandomIdentifier(); + await get.call(platform, 'app.withByteArrays', { + where: [ + ['identifierField', '==', id.toString()], + ], + }); + + expect(getDocumentsMock.getCall(0).args).to.have.deep.members([ + appDefinition.contractId, + 'withByteArrays', + { + where: [ + ['identifierField', '==', id], + ], + }, + ]); + }); + + it('should convert $id and $ownerId to identifiers inside where condition', async () => { + const id = generateRandomIdentifier(); + const ownerId = generateRandomIdentifier(); + + await get.call(platform, 'app.withByteArrays', { + where: [ + ['$id', '==', id.toString()], + ['$ownerId', '==', ownerId.toString()], + ], + }); + + expect(getDocumentsMock.getCall(0).args).to.have.deep.members([ + appDefinition.contractId, + 'withByteArrays', + { + where: [ + ['$id', '==', id], + ['$ownerId', '==', ownerId], + ], + }, + ]); + }); + + it('should convert Document to identifiers inside where condition for "startAt" and "startAfter"', async () => { + const [docA, docB] = getDocumentsFixture(); + + await get.call(platform, 'app.withByteArrays', { + startAt: docA, + startAfter: docB, + }); + + expect(getDocumentsMock.getCall(0).args).to.have.deep.members([ + appDefinition.contractId, + 'withByteArrays', + { + startAt: docA.getId(), + startAfter: docB.getId(), + }, + ]); + }); + + it('should convert string to identifiers inside where condition for "startAt" and "startAfter"', async () => { + const [docA, docB] = getDocumentsFixture(); + + await get.call(platform, 'app.withByteArrays', { + startAt: docA.getId().toString('base58'), + startAfter: docB.getId().toString('base58'), + }); + + expect(getDocumentsMock.getCall(0).args).to.have.deep.members([ + appDefinition.contractId, + 'withByteArrays', + { + startAt: docA.getId(), + startAfter: docB.getId(), + }, + ]); + }); + + it('should convert nested identifier properties inside where condition if `elementMatch` is used', async () => { + const id = generateRandomIdentifier(); + + dataContract = getDataContractFixture(); + dataContract.documents.withByteArrays.properties.nestedObject = { + type: 'object', + properties: { + idField: { + type: "array", + byteArray: true, + contentMediaType: "application/x.dash.dpp.identifier", + minItems: 32, + maxItems: 32, + }, + anotherNested: { + type: 'object', + properties: { + anotherIdField: { + type: "array", + byteArray: true, + contentMediaType: "application/x.dash.dpp.identifier", + minItems: 32, + maxItems: 32, + }, + }, + }, + }, + }; + + appDefinition = { + contractId: dataContract.getId(), + contract: dataContract, + }; + + appsGetMock.reset(); + appsGetMock.returns(appDefinition); + + await get.call(platform, 'app.withByteArrays', { + where: [ + ['nestedObject', 'elementMatch', ['idField', '==', id.toString()]], + ['nestedObject', 'elementMatch', ['anotherNested', 'elementMatch', ['anotherIdField', '==', id.toString()]]] + ], + }); + + expect(getDocumentsMock.getCall(0).args).to.have.deep.members([ + appDefinition.contractId, + 'withByteArrays', + { + where: [ + ['nestedObject', 'elementMatch', ['idField', '==', id]], + ['nestedObject', 'elementMatch', ['anotherNested', 'elementMatch', ['anotherIdField', '==', id]]] + ], + }, + ]); + }); +}); diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/get.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/get.ts new file mode 100644 index 00000000000..9a3b294eec9 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/get.ts @@ -0,0 +1,165 @@ +import Identifier from '@dashevo/dpp/lib/Identifier'; +import Metadata from "@dashevo/dpp/lib/Metadata"; +import Document from '@dashevo/dpp/lib/document/Document'; + +import {Platform} from "../../Platform"; + +/** + * @param {WhereCondition[]} [where] - where + * @param {OrderByCondition[]} [orderBy] - order by + * @param {number} [limit] - limit + * @param {string|Buffer|Document|Identifier} [startAt] - start value (included) + * @param {string|Buffer|Document|Identifier} [startAfter] - start value (not included) + */ +declare interface fetchOpts { + where?: WhereCondition[]; + orderBy?: OrderByCondition[]; + limit?: number; + startAt?: string|Buffer|Document|Identifier; + startAfter?: string|Buffer|Document|Identifier; +} + +type OrderByCondition = [ + string, + 'asc' | 'desc', +]; + +type WhereCondition = [ + string, + '<' | '<=' | '==' | '>' | '>=' | 'in' | 'startsWith' | 'elementMatch' | 'length' | 'contains', + WhereCondition|any, +] + +/** + * Prefetch contract + * + * @param {Platform} this bound instance class + * @param {string} appName of the contract to fetch + */ +const ensureAppContractFetched = async function (this: Platform, appName) { + if (this.client.getApps().has(appName)) { + const appDefinition = this.client.getApps().get(appName); + + if (!appDefinition.contract) { + await this.contracts.get(appDefinition.contractId); + } + } +} + +/** + * Convert where condition identifier properties + * + * @param {WhereCondition} whereCondition + * @param {Object} binaryProperties + * @param {null|string} [parentProperty=null] + * + * @return {WhereCondition} + */ +function convertIdentifierProperties(whereCondition: WhereCondition, binaryProperties: Record, parentProperty: null|string = null) { + const [propertyName, operator, propertyValue] = whereCondition; + + const fullPropertyName = parentProperty ? `${parentProperty}.${propertyName}`: propertyName; + + if (operator === 'elementMatch') { + return [ + propertyName, + operator, + convertIdentifierProperties( + propertyValue, + binaryProperties, + fullPropertyName, + ), + ]; + } + + let convertedPropertyValue = propertyValue; + + const property = binaryProperties[fullPropertyName]; + + const isPropertyIdentifier = property && property.contentMediaType === Identifier.MEDIA_TYPE; + const isSystemIdentifier = ['$id', '$ownerId'].includes(propertyName); + + if (isSystemIdentifier || (isPropertyIdentifier && typeof propertyValue === 'string')) { + convertedPropertyValue = Identifier.from(propertyValue); + } + + return [propertyName, operator, convertedPropertyValue]; +} + +/** + * Get documents from the platform + * + * @param {Platform} this bound instance class + * @param {string} typeLocator type locator + * @param {fetchOpts} opts - MongoDB style query + * @returns documents + */ +export async function get(this: Platform, typeLocator: string, opts: fetchOpts): Promise { + if (!typeLocator.includes('.')) throw new Error('Accessing to field is done using format: appName.fieldName'); + + await this.initialize(); + + // locator is of `dashpay.profile` with dashpay the app and profile the field. + const [appName, fieldType] = typeLocator.split('.'); + // FIXME: we may later want a hashmap of schemas and contract IDs + + if (!this.client.getApps().has(appName)) { + throw new Error(`No app named ${appName} specified.`) + } + + const appDefinition = this.client.getApps().get(appName); + + if (!appDefinition.contractId) { + throw new Error(`Missing contract ID for ${appName}`) + } + + // If not present, will fetch contract based on appName and contractId store in this.apps. + await ensureAppContractFetched.call(this, appName); + + if (opts.where) { + const binaryProperties = appDefinition.contract.getBinaryProperties(fieldType); + + opts.where = opts.where.map((whereCondition) => convertIdentifierProperties(whereCondition, binaryProperties)); + } + + if (opts.startAt instanceof Document) { + opts.startAt = opts.startAt.getId(); + } else if (typeof opts.startAt === 'string') { + opts.startAt = Identifier.from(opts.startAt); + } + + if (opts.startAfter instanceof Document) { + opts.startAfter = opts.startAfter.getId(); + } else if (typeof opts.startAfter === 'string') { + opts.startAfter = Identifier.from(opts.startAfter); + } + + // @ts-ignore + const documentsResponse = await this.client.getDAPIClient().platform.getDocuments( + appDefinition.contractId, + fieldType, + opts + ); + + const rawDocuments = documentsResponse.getDocuments(); + + return Promise.all( + rawDocuments.map(async (rawDocument) => { + const document = await this.dpp.document.createFromBuffer(rawDocument); + + let metadata = null; + const responseMetadata = documentsResponse.getMetadata(); + if (responseMetadata) { + metadata = new Metadata({ + blockHeight: responseMetadata.getHeight(), + coreChainLockedHeight: responseMetadata.getCoreChainLockedHeight(), + }); + } + document.setMetadata(metadata); + + return document; + }), + ); +} + +export default get; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/get.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/get.ts new file mode 100644 index 00000000000..7f65e29c228 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/get.ts @@ -0,0 +1,46 @@ +import {Platform} from "../../Platform"; +// @ts-ignore +import Identifier from "@dashevo/dpp/lib/Identifier"; +import Metadata from "@dashevo/dpp/lib/Metadata"; +const NotFoundError = require('@dashevo/dapi-client/lib/transport/GrpcTransport/errors/NotFoundError'); + +/** + * Get an identity from the platform + * + * @param {Platform} this - bound instance class + * @param {string|Identifier} id - id + * @returns Identity + */ +export async function get(this: Platform, id: Identifier|string): Promise { + await this.initialize(); + + const identifier = Identifier.from(id); + + let identityResponse; + try { + identityResponse = await this.client.getDAPIClient().platform.getIdentity(identifier); + } catch (e) { + if (e instanceof NotFoundError) { + return null; + } + + throw e; + } + + const identity = this.dpp.identity.createFromBuffer(identityResponse.getIdentity()); + + let metadata = null; + const responseMetadata = identityResponse.getMetadata(); + if (responseMetadata) { + metadata = new Metadata({ + blockHeight: responseMetadata.getHeight(), + coreChainLockedHeight: responseMetadata.getCoreChainLockedHeight(), + }); + } + + identity.setMetadata(metadata); + + return identity; +} + +export default get; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/internal/createAssetLockProof.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/internal/createAssetLockProof.ts new file mode 100644 index 00000000000..1db49dec272 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/internal/createAssetLockProof.ts @@ -0,0 +1,124 @@ +import { Transaction } from "@dashevo/dashcore-lib"; +import { Platform } from "../../../Platform"; + +import waitForCoreChainLockedHeight from "./waitForCoreChainLockedHeight"; + +const { InstantLockTimeoutError, TxMetadataTimeoutError } = require('@dashevo/wallet-lib/src/errors'); + +/** + * Creates a funding transaction for the platform identity and returns one-time key to sign the state transition + * @param {Platform} this + * @param {Transaction} assetLockTransaction + * @param {number} outputIndex - index of the funding output in the asset lock transaction + * @return {AssetLockProof} - asset lock proof to be used in the state transition + * that can be used to sign registration/top-up state transition + */ +export async function createAssetLockProof(this : Platform, assetLockTransaction: Transaction, outputIndex: number): Promise { + const platform = this; + await platform.initialize(); + + const account = await platform.client.getWalletAccount(); + const { dpp } = platform; + + // Create poof that the transaction won't be double spend + + const { + promise: instantLockPromise, + cancel: cancelInstantLock + } = account.waitForInstantLock(assetLockTransaction.hash); + + const { + promise: txMetadataPromise, + cancel: cancelTxMetadata, + } = account.waitForTxMetadata(assetLockTransaction.hash); + + let cancelObtainCoreChainLockedHeight; + + let rejectTimer; + + // @ts-ignore + const rejectionTimeout = account.waitForTxMetadataTimeout > account.waitForInstantLockTimeout + // @ts-ignore + ? account.waitForTxMetadataTimeout + 360000 // wait for platform to sync core chain locked height + // @ts-ignore + : account.waitForInstantLockTimeout; + + return Promise.race([ + // Wait for Instant Lock + instantLockPromise + .then((instantLock) => { + clearTimeout(rejectTimer); + + cancelTxMetadata(); + + if (cancelObtainCoreChainLockedHeight) { + cancelObtainCoreChainLockedHeight(); + } + + // @ts-ignore + return dpp.identity.createInstantAssetLockProof( + instantLock, + assetLockTransaction, + outputIndex, + ); + }) + .catch((error) => { + if (error instanceof InstantLockTimeoutError) { + // Instant Lock is timed out. + // Allow chain proof to win the race + return new Promise(() => {}); + } + + return Promise.reject(error); + }), + + // Wait for transaction is mined and platform chain synced core height to the transaction height + txMetadataPromise + .then((assetLockMetadata) => { + // @ts-ignore + return waitForCoreChainLockedHeight(platform, assetLockMetadata.height) + .then(({ promise, cancel }) => { + cancelObtainCoreChainLockedHeight = cancel; + + return promise; + }) + .then(() => { + clearTimeout(rejectTimer); + cancelInstantLock(); + + // @ts-ignore + return dpp.identity.createChainAssetLockProof( + // @ts-ignore + assetLockMetadata.height, + assetLockTransaction.getOutPointBuffer(outputIndex), + ); + }) + }) + .catch((error) => { + if (error instanceof TxMetadataTimeoutError) { + // Instant Lock is timed out. + // Allow instant proof to win the race + return new Promise(() => {}); + } + + return Promise.reject(error); + }), + + // Common timeout for getting proofs + new Promise((_, reject) => { + rejectTimer = setTimeout(() => { + cancelTxMetadata(); + + if (cancelObtainCoreChainLockedHeight) { + cancelObtainCoreChainLockedHeight(); + } + + cancelInstantLock(); + + reject(new Error('Asset Lock Proof creation timeout')); + }, rejectionTimeout) + }) + ]); +} + +export default createAssetLockProof; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/internal/createIdentityCreateTransition.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/internal/createIdentityCreateTransition.ts new file mode 100644 index 00000000000..12e539da74a --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/internal/createIdentityCreateTransition.ts @@ -0,0 +1,78 @@ +import { PrivateKey } from "@dashevo/dashcore-lib"; +import { Platform } from "../../../Platform"; +import IdentityPublicKey from "@dashevo/dpp/lib/identity/IdentityPublicKey" + +/** + * Creates a funding transaction for the platform identity and returns one-time key to sign the state transition + * @param {Platform} this + * @param {AssetLockProof} assetLockProof - asset lock transaction proof for the identity create transition + * @param {PrivateKey} assetLockPrivateKey - private key used in asset lock + * @return {{identity: Identity, identityCreateTransition: IdentityCreateTransition}} - identity, state transition and index of the key used to create it + * that can be used to sign registration/top-up state transition + */ +export async function createIdentityCreateTransition(this : Platform, assetLockProof: any, assetLockPrivateKey: PrivateKey): Promise<{ identity: any, identityCreateTransition: any, identityIndex: number }> { + const platform = this; + await platform.initialize(); + + const account = await platform.client.getWalletAccount(); + const { dpp } = platform; + + const identityIndex = await account.getUnusedIdentityIndex(); + + // @ts-ignore + const { privateKey: identityMasterPrivateKey } = account.identities.getIdentityHDKeyByIndex(identityIndex, 0); + const identityMasterPublicKey = identityMasterPrivateKey.toPublicKey(); + + const { privateKey: identitySecondPrivateKey } = account.identities.getIdentityHDKeyByIndex(identityIndex, 1); + const identitySecondPublicKey = identitySecondPrivateKey.toPublicKey(); + + // Create Identity + // @ts-ignore + const identity = dpp.identity.create( + assetLockProof, [{ + key: identityMasterPublicKey, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER + }, + { + key: identitySecondPublicKey, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.HIGH + } + ] + ); + + // Create ST + const identityCreateTransition = dpp.identity.createIdentityCreateTransition(identity); + + // Create key proofs + + const [masterKey, secondKey] = identityCreateTransition.getPublicKeys(); + + await identityCreateTransition.signByPrivateKey(identityMasterPrivateKey, IdentityPublicKey.TYPES.ECDSA_SECP256K1); + + masterKey.setSignature(identityCreateTransition.getSignature()); + + identityCreateTransition.setSignature(undefined); + + await identityCreateTransition.signByPrivateKey(identitySecondPrivateKey, IdentityPublicKey.TYPES.ECDSA_SECP256K1); + + secondKey.setSignature(identityCreateTransition.getSignature()); + + identityCreateTransition.setSignature(undefined); + + + // Sign and validate state transition + + await identityCreateTransition.signByPrivateKey(assetLockPrivateKey, IdentityPublicKey.TYPES.ECDSA_SECP256K1); + + const result = await dpp.stateTransition.validateBasic(identityCreateTransition); + + if (!result.isValid()) { + throw new Error(`StateTransition is invalid - ${JSON.stringify(result.getErrors())}`); + } + + return { identity, identityCreateTransition, identityIndex }; +} + +export default createIdentityCreateTransition; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/internal/createIdnetityTopUpTransition.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/internal/createIdnetityTopUpTransition.ts new file mode 100644 index 00000000000..3a3d8ac71c9 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/internal/createIdnetityTopUpTransition.ts @@ -0,0 +1,36 @@ +import { PrivateKey } from "@dashevo/dashcore-lib"; +import { Platform } from "../../../Platform"; +import IdentityPublicKey from "@dashevo/dpp/lib/identity/IdentityPublicKey" + +/** + * Creates a funding transaction for the platform identity and returns one-time key to sign the state transition + * @param {Platform} this + * @param {AssetLockProof} assetLockProof - asset lock transaction proof for the identity create transition + * @param {PrivateKey} assetLockPrivateKey - private key used in asset lock + * @param {string|Buffer|Identifier} identityId + * @return {{identity: Identity, identityCreateTransition: IdentityCreateTransition}} - identity, state transition and index of the key used to create it + * that can be used to sign registration/top-up state transition + */ +export async function createIdentityTopUpTransition(this : Platform, assetLockProof: any, assetLockPrivateKey: PrivateKey, identityId: any): Promise { + const platform = this; + await platform.initialize(); + + const { dpp } = platform; + + // @ts-ignore + const identityTopUpTransition = dpp.identity.createIdentityTopUpTransition( + identityId, assetLockProof + ); + + await identityTopUpTransition.signByPrivateKey(assetLockPrivateKey, IdentityPublicKey.TYPES.ECDSA_SECP256K1); + + const result = await dpp.stateTransition.validateBasic(identityTopUpTransition); + + if (!result.isValid()) { + throw new Error(`StateTransition is invalid - ${JSON.stringify(result.getErrors())}`); + } + + return identityTopUpTransition; +} + +export default createIdentityTopUpTransition; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/internal/waitForCoreChainLockedHeight.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/internal/waitForCoreChainLockedHeight.ts new file mode 100644 index 00000000000..33c426427b6 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/internal/waitForCoreChainLockedHeight.ts @@ -0,0 +1,70 @@ +import { Platform } from "../../../Platform"; + +import { ownerId as dpnsOwnerId } from "@dashevo/dpns-contract/lib/systemIds"; + +export async function waitForCoreChainLockedHeight( + this : Platform, + expectedCoreHeight : number, +): Promise<{ promise: Promise, cancel: Function }> { + const platform = this; + await platform.initialize(); + + const interval = 5000; + + let isCanceled = false; + + let timeout: ReturnType; + + let coreChainLockedHeight = 0; + + const promise = new Promise((resolve, reject) => { + async function obtainCoreChainLockedHeight() { + try { + const identityResponse = await platform.identities.get(dpnsOwnerId); + + if (!identityResponse) { + reject(new Error('Identity using to obtain core chain locked height is not present')); + + return; + } + + const metadata = identityResponse.getMetadata(); + + coreChainLockedHeight = metadata.getCoreChainLockedHeight(); + } catch (e) { + reject(e); + + return; + } + + if (coreChainLockedHeight >= expectedCoreHeight) { + resolve(); + + return; + } + + if (!isCanceled) { + timeout = setTimeout(obtainCoreChainLockedHeight, interval); + } + } + + obtainCoreChainLockedHeight(); + }); + + function cancel() { + if (timeout) { + clearTimeout(timeout); + } + + isCanceled = true; + } + + return { + promise, + cancel, + } +} + +export default waitForCoreChainLockedHeight; + + diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/register.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/register.ts new file mode 100644 index 00000000000..5d83d611a5d --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/register.ts @@ -0,0 +1,55 @@ +import { Platform } from "../../Platform"; +import broadcastStateTransition from "../../broadcastStateTransition"; + +/** + * Register identities to the platform + * + * @param {number} [fundingAmount=10000] - funding amount in duffs + * @returns {Identity} identity - a register and funded identity + */ +export default async function register( + this: Platform, + fundingAmount : number = 100000 +): Promise { + await this.initialize(); + + const { client } = this; + + const account = await client.getWalletAccount(); + + const { + transaction: assetLockTransaction, + privateKey: assetLockPrivateKey, + outputIndex: assetLockOutputIndex + } = await this.identities.utils.createAssetLockTransaction(fundingAmount); + + // Broadcast Asset Lock transaction + await account.broadcastTransaction(assetLockTransaction); + + const assetLockProof = await this.identities.utils + .createAssetLockProof(assetLockTransaction, assetLockOutputIndex); + + const { identity, identityCreateTransition, identityIndex } = await this.identities.utils + .createIdentityCreateTransition(assetLockProof, assetLockPrivateKey); + + await broadcastStateTransition(this, identityCreateTransition); + + // If state transition was broadcast without any errors, import identity to the account + account.storage + .getWalletStore(account.walletId) + .insertIdentityIdAtIndex( + identity.getId().toString(), + identityIndex, + ); + + // Current identity object will not have metadata or balance information + const registeredIdentity = await this.identities.get(identity.getId().toString()); + + // We cannot just return registeredIdentity as we want to + // keep additional information (assetLockProof and transaction) instance + identity.setMetadata(registeredIdentity.getMetadata()); + identity.setBalance(registeredIdentity.getBalance()); + identity.setPublicKeys(registeredIdentity.getPublicKeys()); + + return identity; +} diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/topUp.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/topUp.ts new file mode 100644 index 00000000000..c4d76a45e5f --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/topUp.ts @@ -0,0 +1,44 @@ +import Identifier from "@dashevo/dpp/lib/Identifier"; +import {Platform} from "../../Platform"; + +import broadcastStateTransition from "../../broadcastStateTransition"; + +/** + * Register identities to the platform + * + * @param {Platform} this - bound instance class + * @param {Identifier|string} identityId - id of the identity to top up + * @param {number} amount - amount to top up in duffs + * @returns {boolean} + */ +export async function topUp(this: Platform, identityId: Identifier | string, amount: number): Promise { + await this.initialize(); + + const { client } = this; + + identityId = Identifier.from(identityId); + + const account = await client.getWalletAccount(); + + const { + transaction: assetLockTransaction, + privateKey: assetLockPrivateKey, + outputIndex: assetLockOutputIndex + } = await this.identities.utils.createAssetLockTransaction(amount); + + // Broadcast Asset Lock transaction + await account.broadcastTransaction(assetLockTransaction); + // Create a proof for the asset lock transaction + const assetLockProof = await this.identities.utils + .createAssetLockProof(assetLockTransaction, assetLockOutputIndex); + + const identityTopUpTransition = await this.identities.utils + .createIdentityTopUpTransition(assetLockProof, assetLockPrivateKey, identityId); + + // Broadcast ST + await broadcastStateTransition(this, identityTopUpTransition); + + return true; +} + +export default topUp; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/update.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/update.ts new file mode 100644 index 00000000000..29621afab82 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/update.ts @@ -0,0 +1,79 @@ +import Identity from "@dashevo/dpp/lib/identity/Identity"; +import { Platform } from "../../Platform"; +import IdentityPublicKey from "@dashevo/dpp/lib/identity/IdentityPublicKey" +import { signStateTransition } from '../../signStateTransition'; + +import broadcastStateTransition from "../../broadcastStateTransition"; + +/** + * Update platform identities + * + * @param {Platform} this - bound instance class + * @param {Identity} identity - identity to update + * @param {{add: IdentityPublicKey[]; disable: IdentityPublicKey[]}} publicKeys - public keys to add + * @param {Object} privateKeys - public keys to add + * + * @returns {boolean} + */ +export async function update( + this: Platform, + identity: Identity, + publicKeys: { add?: IdentityPublicKey[]; disable?: IdentityPublicKey[] }, + privateKeys: { string, any }, + ): Promise { + await this.initialize(); + + const { dpp } = this; + + const identityUpdateTransition = dpp.identity.createIdentityUpdateTransition( + identity, + publicKeys, + ); + + const signerKeyIndex = 0; + + // Create key proofs + if (identityUpdateTransition.getPublicKeysToAdd()) { + const signerKey = identity.getPublicKeys()[signerKeyIndex]; + + // must be run sequentially! will not work with Promise.all! + // more info at https://jrsinclair.com/articles/2019/how-to-run-async-js-in-parallel-or-sequential/ + + const starterPromise = Promise.resolve(null); + + await identityUpdateTransition.getPublicKeysToAdd().reduce( + (previousPromise, publicKey) => previousPromise.then(async () => { + const privateKey = privateKeys[publicKey.getId()]; + + if (!privateKey) { + throw new Error(`Private key for key ${publicKey.getId()} not found`); + } + + identityUpdateTransition.setSignaturePublicKeyId(signerKey.getId()); + + await identityUpdateTransition.signByPrivateKey(privateKey, publicKey.getType()); + + publicKey.setSignature(identityUpdateTransition.getSignature()); + + identityUpdateTransition.setSignature(undefined); + identityUpdateTransition.setSignaturePublicKeyId(undefined); + }), + starterPromise, + ); + } + + await signStateTransition(this, identityUpdateTransition, identity, signerKeyIndex); + + const result = await dpp.stateTransition.validateBasic(identityUpdateTransition); + + if (!result.isValid()) { + throw new Error(`StateTransition is invalid - ${JSON.stringify(result.getErrors())}`); + } + + // Broadcast ST + await broadcastStateTransition(this, identityUpdateTransition); + + return true; +} + +export default update; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/register.spec.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/register.spec.ts new file mode 100644 index 00000000000..eab823e77dd --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/register.spec.ts @@ -0,0 +1,126 @@ +import { expect } from 'chai'; +import { ImportMock } from 'ts-mock-imports'; +import generateRandomIdentifier from "@dashevo/dpp/lib/test/utils/generateRandomIdentifier" + +import cryptoModule from 'crypto'; + +import register from './register'; +import {ClientApps} from "../../../ClientApps"; + +describe('Platform', () => { + let randomBytesMock; + + before(() => { + randomBytesMock = ImportMock.mockFunction(cryptoModule, 'randomBytes', Buffer.alloc(32)); + }); + after(() => { + randomBytesMock.restore(); + }); + + describe('Names', () => { + describe('#register', () => { + let platformMock; + let identityMock; + + beforeEach(async function beforeEach() { + platformMock = { + client: { + getApps() { + return new ClientApps({ + dpns: { + contractId: generateRandomIdentifier(), + } + }); + } + }, + documents: { + create: this.sinon.stub(), + broadcast: this.sinon.stub(), + }, + initialize: this.sinon.stub(), + }; + + identityMock = { + getId: this.sinon.stub(), + getPublicKeyById: this.sinon.stub(), + }; + }); + + it('register top level domain', async () => { + const identityId = generateRandomIdentifier(); + identityMock.getId.returns(identityId); + + await register.call(platformMock, 'Dash', { + dashUniqueIdentityId: identityId, + }, identityMock); + + expect(platformMock.documents.create.getCall(0).args[0]).to.deep.equal('dpns.preorder'); + expect(platformMock.documents.create.getCall(0).args[1]).to.deep.equal(identityMock); + expect(platformMock.documents.create.getCall(0).args[2].saltedDomainHash.toString('hex')).to.deep.equal( + 'df46c47179745ea18c0fdc95910372dca8810127acc9afe3c9b326b07555e6b4', + ); + + expect(platformMock.documents.create.getCall(1).args).to.have.deep.members([ + 'dpns.domain', + identityMock, + { + 'label': 'Dash', + 'normalizedLabel': 'dash', + 'normalizedParentDomainName': '', + 'preorderSalt': Buffer.alloc(32), + 'records': { + 'dashUniqueIdentityId': identityId, + }, + 'subdomainRules': { + 'allowSubdomains': true, + }, + } + ]); + }); + + it('should register second level domain', async () => { + const identityId = generateRandomIdentifier(); + identityMock.getId.returns(identityId); + + await register.call(platformMock, 'User.dash', { + dashAliasIdentityId: identityId, + }, identityMock); + + expect(platformMock.documents.create.getCall(0).args[0]).to.deep.equal('dpns.preorder'); + expect(platformMock.documents.create.getCall(0).args[1]).to.deep.equal(identityMock); + expect(platformMock.documents.create.getCall(0).args[2].saltedDomainHash.toString('hex')).to.deep.equal( + '04a52b75ca842ee9fb14f2cdd27aa0982b9b2cfb2c0e95f640ca3f0c24f1bb9a', + ); + + expect(platformMock.documents.create.getCall(1).args).to.have.deep.members([ + 'dpns.domain', + identityMock, + { + 'label': 'User', + 'normalizedLabel': 'user', + 'normalizedParentDomainName': 'dash', + 'preorderSalt': Buffer.alloc(32), + 'records': { + 'dashAliasIdentityId': identityId, + }, + 'subdomainRules': { + 'allowSubdomains': false, + }, + } + ]); + }); + + it('should fail if DPNS app have no contract set up', async () => { + delete platformMock.client.getApps().get('dpns').contractId; + + try { + await register.call(platformMock, 'user.dash', { + dashUniqueIdentityId: generateRandomIdentifier(), + }, identityMock); + } catch (e) { + expect(e.message).to.equal('DPNS is required to register a new name.'); + } + }); + }); + }); +}); diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/register.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/register.ts new file mode 100644 index 00000000000..f664266a3ec --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/register.ts @@ -0,0 +1,112 @@ +import {Platform} from "../../Platform"; +import Identifier from "@dashevo/dpp/lib/Identifier"; + +const { hash } = require('@dashevo/dpp/lib/util/hash'); +const crypto = require('crypto'); + +/** + * Register names to the platform + * + * @param {Platform} this - bound instance class + * @param {string} name - name + * @param {Object} records - records object having only one of the following items + * @param {string} [records.dashUniqueIdentityId] + * @param {string} [records.dashAliasIdentityId] + * @param identity - identity + * + * @returns registered domain document + */ +export async function register(this: Platform, + name: string, + records: { + dashUniqueIdentityId?: Identifier|string, + dashAliasIdentityId?: Identifier|string, + }, + identity: { + getId(): Identifier; + getPublicKeyById(number: number):any; + }, +): Promise { + await this.initialize(); + + if (records.dashUniqueIdentityId) { + records.dashUniqueIdentityId = Identifier.from(records.dashUniqueIdentityId); + } + + if (records.dashAliasIdentityId) { + records.dashAliasIdentityId = Identifier.from(records.dashAliasIdentityId); + } + + const nameLabels = name.split('.'); + + const normalizedParentDomainName = nameLabels + .slice(1) + .join('.') + .toLowerCase(); + + const [label] = nameLabels; + const normalizedLabel = label.toLowerCase(); + + const preorderSalt = crypto.randomBytes(32); + + const isSecondLevelDomain = normalizedParentDomainName.length > 0; + + const fullDomainName = isSecondLevelDomain + ? `${normalizedLabel}.${normalizedParentDomainName}` + : normalizedLabel; + + const saltedDomainHash = hash( + Buffer.concat([ + preorderSalt, + Buffer.from(fullDomainName), + ]), + ); + + if (!this.client.getApps().has('dpns')) { + throw new Error('DPNS is required to register a new name.'); + } + + // 1. Create preorder document + const preorderDocument = await this.documents.create( + 'dpns.preorder', + identity, + { + saltedDomainHash, + }, + ); + + await this.documents.broadcast( + { + create: [preorderDocument], + }, + identity, + ); + + // 3. Create domain document + const domainDocument = await this.documents.create( + 'dpns.domain', + identity, + { + label, + normalizedLabel, + normalizedParentDomainName, + preorderSalt, + records, + subdomainRules: { + allowSubdomains: !isSecondLevelDomain, + }, + }, + ); + + // 4. Create and send domain state transition + await this.documents.broadcast( + { + create: [domainDocument], + }, + identity, + ); + + return domainDocument; +} + +export default register; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/resolve.spec.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/resolve.spec.ts new file mode 100644 index 00000000000..38447320ccc --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/resolve.spec.ts @@ -0,0 +1,67 @@ +import { expect } from 'chai'; + +import resolve from './resolve'; + +describe('Platform', () => { + describe('Names', () => { + describe('#resolve', () => { + let platformMock; + let parentDomainDocument; + let childDomainDocument; + + beforeEach(async function beforeEach() { + parentDomainDocument = { label: 'parent' }; + childDomainDocument = { label: 'child.parent' }; + + platformMock = { + documents: { + get: this.sinon.stub(), + }, + initialize: this.sinon.stub(), + }; + }); + + it('should resolve domain by it\'s name', async () => { + platformMock.documents.get.resolves([parentDomainDocument]); + + const receivedDocument = await resolve.call(platformMock, 'parent'); + + expect(platformMock.documents.get.callCount).to.equal(1); + expect(platformMock.documents.get.getCall(0).args).to.deep.equal( + [ + 'dpns.domain', + { + where: [ + ['normalizedParentDomainName', '==', ''], + ['normalizedLabel', '==', 'parent'], + ], + }, + ], + ); + + expect(receivedDocument).to.deep.equal(parentDomainDocument); + }); + + it('should return null if domain was not found', async () => { + platformMock.documents.get.resolves([]); + + const receivedDocument = await resolve.call(platformMock, 'otherName.parent'); + + expect(platformMock.documents.get.callCount).to.equal(1); + expect(platformMock.documents.get.getCall(0).args).to.deep.equal( + [ + 'dpns.domain', + { + where: [ + ['normalizedParentDomainName', '==', 'parent'], + ['normalizedLabel', '==', 'othername'] + ], + }, + ], + ); + + expect(receivedDocument).to.be.null; + }); + }); + }); +}); diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/resolve.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/resolve.ts new file mode 100644 index 00000000000..a40a830d895 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/resolve.ts @@ -0,0 +1,34 @@ +import { Platform } from "../../Platform"; + +/** + * This method will allow you to resolve a DPNS record from its humanized name. + * @param {string} name - the exact alphanumeric (2-63) value used for human-identification + * @returns {Document} document + */ +export async function resolve(this: Platform, name: string): Promise { + await this.initialize(); + + // setting up variables in case of TLD registration + let normalizedLabel = name.toLowerCase(); + let normalizedParentDomainName = ''; + + // in case of subdomain registration + // we should split label and parent domain name + if (name.includes('.')) { + const segments = name.toLowerCase().split('.'); + + normalizedLabel = segments[0]; + normalizedParentDomainName = segments.slice(1).join('.'); + } + + const [document] = await this.documents.get('dpns.domain', { + where: [ + ['normalizedParentDomainName', '==', normalizedParentDomainName], + ['normalizedLabel', '==', normalizedLabel], + ], + }); + + return document === undefined ? null : document; +} + +export default resolve; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/resolveByRecord.spec.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/resolveByRecord.spec.ts new file mode 100644 index 00000000000..dba1fb0e64d --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/resolveByRecord.spec.ts @@ -0,0 +1,62 @@ +import { expect } from 'chai'; + +import resolveByRecord from './resolveByRecord'; + +describe('Platform', () => { + describe('Names', () => { + describe('#resolveByRecord', () => { + let platformMock; + let parentDomainDocument; + let childDomainDocument; + + beforeEach(async function beforeEach() { + parentDomainDocument = { label: 'parent' }; + childDomainDocument = { label: 'child.parent' }; + + platformMock = { + documents: { + get: this.sinon.stub(), + }, + initialize: this.sinon.stub(), + }; + }); + + it('should resolve domain by it\'s record', async () => { + platformMock.documents.get.resolves([parentDomainDocument]); + + const receivedDocuments = await resolveByRecord.call( + platformMock, 'recordName', 'recordValue', + ); + + expect(platformMock.documents.get.callCount).to.equal(1); + expect(platformMock.documents.get.getCall(0).args).to.deep.equal([ + 'dpns.domain', + { + where: [['records.recordName', '==', 'recordValue']], + }, + ]); + + expect(receivedDocuments).to.deep.equal([parentDomainDocument]); + }); + + it('should return null if domain was not found', async () => { + platformMock.documents.get.resolves([]); + + const receivedDocuments = await resolveByRecord.call( + platformMock, 'recordName', 'recordValue', + ); + + expect(platformMock.documents.get.callCount).to.equal(1); + expect(platformMock.documents.get.getCall(0).args).to.deep.equal([ + 'dpns.domain', + { + where: [['records.recordName', '==', 'recordValue']], + }, + ]); + + expect(receivedDocuments).to.be.an('array'); + expect(receivedDocuments.length).to.equal(0); + }); + }); + }); +}); diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/resolveByRecord.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/resolveByRecord.ts new file mode 100644 index 00000000000..933d0c2eb87 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/resolveByRecord.ts @@ -0,0 +1,23 @@ +import { Platform } from "../../Platform"; +import Identifier from "@dashevo/dpp/lib/Identifier"; + +/** + * @param record - the exact name of the record to resolve + * @param value - the exact value for this record to resolve + * @returns {Document[]} - Resolved domains + */ +export async function resolveByRecord(this: Platform, record: string, value: any): Promise { + await this.initialize(); + + if (record === 'dashUniqueIdentityId' || record === 'dashAliasIdentityId') { + value = Identifier.from(value); + } + + return await this.documents.get('dpns.domain', { + where: [ + [`records.${record}`, '==', value], + ], + }); +} + +export default resolveByRecord; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/search.spec.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/search.spec.ts new file mode 100644 index 00000000000..943a5c31cde --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/search.spec.ts @@ -0,0 +1,69 @@ +import { expect } from 'chai'; + +import search from './search'; + +describe('Platform', () => { + describe('Names', () => { + describe('#search', () => { + let platformMock; + let parentDomainDocument; + let childDomainDocument; + + beforeEach(async function beforeEach() { + parentDomainDocument = { label: 'parent' }; + childDomainDocument = { label: 'child.parent' }; + + platformMock = { + documents: { + get: this.sinon.stub(), + }, + initialize: this.sinon.stub(), + }; + }); + + it('should return a list of searched domains', async () => { + platformMock.documents.get.resolves([parentDomainDocument]); + + const documentsList = await search.call( + platformMock, 'prefix', 'dash', + ); + + expect(platformMock.documents.get.callCount).to.equal(1); + expect(platformMock.documents.get.getCall(0).args).to.deep.equal([ + 'dpns.domain', + { + where: [ + ["normalizedParentDomainName", "==", "dash"], + ["normalizedLabel", "startsWith", "prefix"] + ], + orderBy: [['normalizedLabel', 'asc']], + }, + ]); + + expect(documentsList).to.have.deep.members([parentDomainDocument]); + }); + + it('should return an empty list if no domains where found', async () => { + platformMock.documents.get.resolves([]); + + const documentsList = await search.call( + platformMock, 'prefix', 'dash', + ); + + expect(platformMock.documents.get.callCount).to.equal(1); + expect(platformMock.documents.get.getCall(0).args).to.deep.equal([ + 'dpns.domain', + { + where: [ + ["normalizedParentDomainName", "==", "dash"], + ["normalizedLabel", "startsWith", "prefix"] + ], + orderBy: [['normalizedLabel', 'asc']] + }, + ]); + + expect(documentsList).to.deep.equal([]); + }); + }); + }); +}); diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/search.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/search.ts new file mode 100644 index 00000000000..5b8d34197c0 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/names/search.ts @@ -0,0 +1,28 @@ +import {Platform} from "../../Platform"; + +/** + * + * @param {string} labelPrefix - label prefix to search for + * @param {string} parentDomainName - parent domain name on which to perform the search + * @returns Documents[] - The array of documents that match the search parameters. + */ +export async function search(this: Platform, labelPrefix: string, parentDomainName: string = '') { + await this.initialize(); + + const normalizedParentDomainName = parentDomainName.toLowerCase(); + const normalizedLabelPrefix = labelPrefix.toLowerCase(); + + const documents = await this.documents.get('dpns.domain', { + where: [ + ['normalizedParentDomainName', '==', normalizedParentDomainName], + ['normalizedLabel', 'startsWith', normalizedLabelPrefix], + ], + orderBy: [ + ['normalizedLabel', 'asc'] + ] + }); + + return documents; +} + +export default search; diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/signStateTransition.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/signStateTransition.ts new file mode 100644 index 00000000000..90a2a6e0ca6 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/signStateTransition.ts @@ -0,0 +1,28 @@ +import {Platform} from "./Platform"; + +/** + * + * @param {Platform} platform + * @param {AbstractStateTransition} stateTransition + * @param {Identity} identity + * @param {number} [keyIndex] + * @return {AbstractStateTransition} + */ +export async function signStateTransition(platform: Platform, stateTransition: any, identity: any, keyIndex: number = 0): Promise { + const { client } = platform; + + const account = await client.getWalletAccount(); + + // @ts-ignore + const { privateKey } = account.identities.getIdentityHDKeyById( + identity.getId().toString(), + keyIndex, + ); + + await stateTransition.sign( + identity.getPublicKeyById(keyIndex), + privateKey, + ); + + return stateTransition; +} diff --git a/packages/js-dash-sdk/src/SDK/Client/index.ts b/packages/js-dash-sdk/src/SDK/Client/index.ts new file mode 100644 index 00000000000..82e03984991 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Client/index.ts @@ -0,0 +1 @@ +export { Client } from './Client'; diff --git a/packages/js-dash-sdk/src/SDK/Core/Core.d.ts b/packages/js-dash-sdk/src/SDK/Core/Core.d.ts new file mode 100644 index 00000000000..6f73a27d18c --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Core/Core.d.ts @@ -0,0 +1,32 @@ +import { + Transaction as _Transaction, + Address as _Address, + Block as _Block, + UnspentOutput as _UnspentOutput, + HDPublicKey as _HDPublicKey, + HDPrivateKey as _HDPrivateKey, + Mnemonic as _Mnemonic, + Network as _Network, + Input as _Input, + Output as _Output, + Script as _Script, + PublicKey as _PublicKey, + PrivateKey as _PrivateKey +} from '@dashevo/dashcore-lib'; + +export declare namespace Core { + export type Transaction = _Transaction; + + export type Address = _Address; + export type Block = _Block; + export type UnspentOutput = _UnspentOutput; + export type HDPublicKey = _HDPublicKey; + export type HDPrivateKey = _HDPrivateKey; + export type PublicKey = _PublicKey; + export type PrivateKey = _PrivateKey; + export type Mnemonic = _Mnemonic; + export type Network = _Network; + export type Script = _Script; + export type Input = _Input; + export type Output = _Output; +} diff --git a/packages/js-dash-sdk/src/SDK/Core/Core.ts b/packages/js-dash-sdk/src/SDK/Core/Core.ts new file mode 100644 index 00000000000..61b32b6af6e --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Core/Core.ts @@ -0,0 +1,2 @@ +import * as Core from '@dashevo/dashcore-lib'; +export { Core }; diff --git a/packages/js-dash-sdk/src/SDK/Core/index.ts b/packages/js-dash-sdk/src/SDK/Core/index.ts new file mode 100644 index 00000000000..2a354a9ef87 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Core/index.ts @@ -0,0 +1 @@ +export { Core } from './Core'; diff --git a/packages/js-dash-sdk/src/SDK/Platform/Platform.d.ts b/packages/js-dash-sdk/src/SDK/Platform/Platform.d.ts new file mode 100644 index 00000000000..1190c713eda --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Platform/Platform.d.ts @@ -0,0 +1,3 @@ +export declare namespace Platform { + +} diff --git a/packages/js-dash-sdk/src/SDK/Platform/Platform.ts b/packages/js-dash-sdk/src/SDK/Platform/Platform.ts new file mode 100644 index 00000000000..31524ded70b --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Platform/Platform.ts @@ -0,0 +1,7 @@ +// @ts-ignore +import { default as _DashPlatformProtocol } from '@dashevo/dpp'; + +export namespace Platform { + export let DashPlatformProtocol = _DashPlatformProtocol; +} +export { Platform as default }; diff --git a/packages/js-dash-sdk/src/SDK/Platform/index.ts b/packages/js-dash-sdk/src/SDK/Platform/index.ts new file mode 100644 index 00000000000..7ebf91b1944 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/Platform/index.ts @@ -0,0 +1 @@ +export { Platform } from './Platform'; diff --git a/packages/js-dash-sdk/src/SDK/SDK.spec.ts b/packages/js-dash-sdk/src/SDK/SDK.spec.ts new file mode 100644 index 00000000000..02750ee6f26 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/SDK.spec.ts @@ -0,0 +1,12 @@ +import { expect } from 'chai'; +import SDK from "./index"; +import 'mocha'; + +describe('Dash', () => { + + it('should provide expected class', function () { + expect(SDK).to.have.property('Client'); + expect(SDK.Client.name).to.be.equal('Client') + expect(SDK.Client.constructor.name).to.be.equal('Function') + }); +}); diff --git a/packages/js-dash-sdk/src/SDK/SDK.ts b/packages/js-dash-sdk/src/SDK/SDK.ts new file mode 100644 index 00000000000..aa0d50b6393 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/SDK.ts @@ -0,0 +1,49 @@ +import { Client as _Client } from './Client'; +import { Core as _Core } from './Core'; +import { Platform as _Platform } from './Platform'; +import { default as _DAPIClient } from '@dashevo/dapi-client'; + +import { StateTransitionBroadcastError } from '../errors/StateTransitionBroadcastError' + +import { + Wallet as _Wallet, + Account as _Account, + DerivableKeyChain as _KeyChain, + CONSTANTS as _WalletLibCONSTANTS, + EVENTS as _WalletLibEVENTS, + utils as _WalletLibUtils, + plugins as _WalletLibPlugins +} from '@dashevo/wallet-lib'; + +export namespace SDK { + export let DAPIClient = _DAPIClient; + export let Client = _Client; + + export let Core = _Core; + // TODO: consider marking as DEPRECATED and use PlatformProtocol below instead + export let Platform = _Platform; + + // Wallet-lib primitives + export let Wallet = _Wallet; + export let Account = _Account; + export let KeyChain = _KeyChain; + + // TODO: consider merging into Wallet above and mark as DEPRECATED + export let WalletLib = { + CONSTANTS: _WalletLibCONSTANTS, + EVENTS: _WalletLibEVENTS, + plugins: _WalletLibPlugins, + utils: _WalletLibUtils, + } + + export let PlatformProtocol = Platform.DashPlatformProtocol; + + export let Essentials = { + Buffer, + } + + export let Errors = { + StateTransitionBroadcastError + } +} +export { SDK as default }; diff --git a/packages/js-dash-sdk/src/SDK/index.ts b/packages/js-dash-sdk/src/SDK/index.ts new file mode 100644 index 00000000000..1a6362a1164 --- /dev/null +++ b/packages/js-dash-sdk/src/SDK/index.ts @@ -0,0 +1 @@ +export { SDK as default } from './SDK'; diff --git a/packages/js-dash-sdk/src/errors/StateTransitionBroadcastError.ts b/packages/js-dash-sdk/src/errors/StateTransitionBroadcastError.ts new file mode 100644 index 00000000000..589124a866b --- /dev/null +++ b/packages/js-dash-sdk/src/errors/StateTransitionBroadcastError.ts @@ -0,0 +1,51 @@ +export class StateTransitionBroadcastError extends Error { + code: number; + message: string; + cause: Error; + + /** + * @param {number} code + * @param {string} message + * @param {Error} cause + */ + constructor(code: number, message: string, cause: Error) { + super(message); + + this.code = code; + this.message = message; + this.cause = cause; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + + Object.setPrototypeOf(this, StateTransitionBroadcastError.prototype); + } + + /** + * Returns error code + * + * @return {number} + */ + getCode(): number { + return this.code; + } + + /** + * Returns error message + * + * @return {string} + */ + getMessage(): string { + return this.message; + } + + /** + * Get error that was a cause + * + * @return {Error} + */ + getCause(): any { + return this.cause; + } +} diff --git a/packages/js-dash-sdk/src/index.ts b/packages/js-dash-sdk/src/index.ts new file mode 100644 index 00000000000..ab350feed42 --- /dev/null +++ b/packages/js-dash-sdk/src/index.ts @@ -0,0 +1,2 @@ +import SDK from './SDK' +export = SDK; diff --git a/packages/js-dash-sdk/src/test/bootstrap.js b/packages/js-dash-sdk/src/test/bootstrap.js new file mode 100644 index 00000000000..f4fa0533099 --- /dev/null +++ b/packages/js-dash-sdk/src/test/bootstrap.js @@ -0,0 +1,39 @@ +const dotenvSafe = require('dotenv-safe'); +const path = require('path'); + +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); +const { expect, use } = require('chai'); +const dirtyChai = require('dirty-chai'); + +dotenvSafe.config({ + path: path.resolve(__dirname, '..', '..', '.env'), +}); + +use(dirtyChai); +use(sinonChai); + +before(function before() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + + +after(function after() { + this.sinon.restore(); +}); + +beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + +afterEach(function afterEach() { + this.sinon.restore(); +}); diff --git a/packages/js-dash-sdk/src/test/fixtures/createIdentityFixtureInAccount.ts b/packages/js-dash-sdk/src/test/fixtures/createIdentityFixtureInAccount.ts new file mode 100644 index 00000000000..30d635dc3bd --- /dev/null +++ b/packages/js-dash-sdk/src/test/fixtures/createIdentityFixtureInAccount.ts @@ -0,0 +1,37 @@ +import IdentityPublicKey from "@dashevo/dpp/lib/identity/IdentityPublicKey"; +// @ts-ignore +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); + +export function createIdentityFixtureInAccount(account) { + const identityFixture = getIdentityFixture(); + const identityFixtureIndex = 0; + const { privateKey: identityMasterPrivateKey } = account.identities.getIdentityHDKeyByIndex(identityFixtureIndex, 0); + const { privateKey: identitySecondPrivateKey } = account.identities.getIdentityHDKeyByIndex(identityFixtureIndex, 1); + + identityFixture.publicKeys[0] = new IdentityPublicKey({ + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: identityMasterPrivateKey.toPublicKey().toBuffer(), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + }); + + identityFixture.publicKeys[1] = new IdentityPublicKey({ + id: 1, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: identitySecondPrivateKey.toPublicKey().toBuffer(), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.HIGH, + readOnly: false, + }); + + account.storage + .getWalletStore(account.walletId) + .insertIdentityIdAtIndex( + identityFixture.getId().toString(), + identityFixtureIndex, + ); + + return identityFixture; +} diff --git a/packages/js-dash-sdk/src/test/fixtures/createTransactionFixtureInAccount.ts b/packages/js-dash-sdk/src/test/fixtures/createTransactionFixtureInAccount.ts new file mode 100644 index 00000000000..0505d5b2182 --- /dev/null +++ b/packages/js-dash-sdk/src/test/fixtures/createTransactionFixtureInAccount.ts @@ -0,0 +1,17 @@ +import { Transaction } from "@dashevo/dashcore-lib"; + +export async function createTransactionInAccount(account) { + // add fake tx to the wallet so it will be able to create transactions + const walletTransaction = new Transaction(undefined) + .from([{ + amount: 1500000, + script: '76a914f9996443a7d5e2694560f8715e5e8fe602133c6088ac', + outputIndex: 0, + txid: new Transaction(undefined).hash, + }]) + .to(account.getAddress(10).address, 1000000); + + await account.importTransactions([[walletTransaction.serialize(true)]]); + + return walletTransaction; +} diff --git a/packages/js-dash-sdk/src/test/fixtures/getResponseMetadataFixture.ts b/packages/js-dash-sdk/src/test/fixtures/getResponseMetadataFixture.ts new file mode 100644 index 00000000000..b509c668ff3 --- /dev/null +++ b/packages/js-dash-sdk/src/test/fixtures/getResponseMetadataFixture.ts @@ -0,0 +1,12 @@ +const Metadata = require("@dashevo/dapi-client/lib/methods/platform/response/Metadata"); + +function getResponseMetadataFixture() { + const metadata = { + height: 10, + coreChainLockedHeight: 42, + }; + + return new Metadata(metadata); +} + +export default getResponseMetadataFixture; \ No newline at end of file diff --git a/packages/js-dash-sdk/src/test/karma/bootstrap.ts b/packages/js-dash-sdk/src/test/karma/bootstrap.ts new file mode 100644 index 00000000000..f579523ef71 --- /dev/null +++ b/packages/js-dash-sdk/src/test/karma/bootstrap.ts @@ -0,0 +1,20 @@ +import sinon from 'sinon'; +import { use } from 'chai'; + +import dirtyChai from 'dirty-chai'; +import sinonChai from 'sinon-chai'; + +use(dirtyChai); +use(sinonChai); + +beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + +afterEach(function afterEach() { + this.sinon.restore(); +}); diff --git a/packages/js-dash-sdk/src/test/mocks/createAndAttachTransportMocksToClient.ts b/packages/js-dash-sdk/src/test/mocks/createAndAttachTransportMocksToClient.ts new file mode 100644 index 00000000000..9679fafbb24 --- /dev/null +++ b/packages/js-dash-sdk/src/test/mocks/createAndAttachTransportMocksToClient.ts @@ -0,0 +1,95 @@ +import { Transaction } from "@dashevo/dashcore-lib"; +import stateTransitionTypes from "@dashevo/dpp/lib/stateTransition/stateTransitionTypes"; +import Identity from "@dashevo/dpp/lib/identity/Identity"; + +import { createFakeInstantLock } from "../../utils/createFakeIntantLock"; +import getResponseMetadataFixture from '../fixtures/getResponseMetadataFixture'; +import { createDapiClientMock } from "./createDapiClientMock"; + +import { wait } from "../../utils/wait"; +const GetIdentityResponse = require("@dashevo/dapi-client/lib/methods/platform/getIdentity/GetIdentityResponse"); + +// @ts-ignore +const TxStreamMock = require('@dashevo/wallet-lib/src/test/mocks/TxStreamMock'); +// @ts-ignore +const TxStreamDataResponseMock = require('@dashevo/wallet-lib/src/test/mocks/TxStreamDataResponseMock'); +// @ts-ignore +const TransportMock = require('@dashevo/wallet-lib/src/test/mocks/TransportMock'); + +function makeTxStreamEmitISLocksForTransactions(transportMock, txStreamMock) { + transportMock.sendTransaction.callsFake((txString) => { + const transaction = new Transaction(txString); + const isLock = createFakeInstantLock(transaction.hash); + + setImmediate(() => { + // Emit IS lock for the transaction + txStreamMock.emit( + TxStreamMock.EVENTS.data, + new TxStreamDataResponseMock( + { instantSendLockMessages: [isLock.toBuffer()] } + ) + ); + }) + + // Emit the same transaction back to the client so it will know about the change transaction + txStreamMock.emit( + TxStreamMock.EVENTS.data, + new TxStreamDataResponseMock( + { rawTransactions: [transaction.toBuffer()] } + ) + ); + + return transaction.hash; + }); +} + +/** + * Makes stub remember the identity from the ST and respond with it + * @param {Client} client + * @param dapiClientMock + */ +function makeGetIdentityRespondWithIdentity(client, dapiClientMock) { + dapiClientMock.platform.broadcastStateTransition.callsFake(async (stBuffer) => { + let interceptedIdentityStateTransition = await client.platform.dpp.stateTransition.createFromBuffer(stBuffer); + + if (interceptedIdentityStateTransition.getType() === stateTransitionTypes.IDENTITY_CREATE) { + + + let identityToResolve = new Identity({ + protocolVersion: interceptedIdentityStateTransition.getProtocolVersion(), + id: interceptedIdentityStateTransition.getIdentityId().toBuffer(), + publicKeys: interceptedIdentityStateTransition.getPublicKeys().map((key) => key.toObject({ skipSignature: true })), + balance: interceptedIdentityStateTransition.getAssetLockProof().getOutput().satoshis, + revision: 0, + }); + dapiClientMock.platform.getIdentity.withArgs(identityToResolve.getId()).resolves(new GetIdentityResponse(identityToResolve.toBuffer(), getResponseMetadataFixture())); + } + }); +} + +export async function createAndAttachTransportMocksToClient(client, sinon) { + const txStreamMock = new TxStreamMock(); + const transportMock = new TransportMock(sinon, txStreamMock); + const dapiClientMock = createDapiClientMock(sinon); + + // Mock wallet-lib transport to intercept transactions + client.wallet.transport = transportMock; + // Mock dapi client for platform endpoints + client.dapiClient = dapiClientMock; + + // Starting account sync + const accountPromise = client.wallet.getAccount(); + // Breaking the event loop to emit an event + await wait(0); + // Emitting stream end event to mark finish of the account sync + txStreamMock.emit(TxStreamMock.EVENTS.end); + // Wait for account to resolve + await accountPromise; + + // Putting data in transport stubs + transportMock.getIdentitiesByPublicKeyHashes.resolves([]); + makeTxStreamEmitISLocksForTransactions(transportMock, txStreamMock); + makeGetIdentityRespondWithIdentity(client, dapiClientMock); + + return { txStreamMock, transportMock, dapiClientMock }; +} diff --git a/packages/js-dash-sdk/src/test/mocks/createDapiClientMock.ts b/packages/js-dash-sdk/src/test/mocks/createDapiClientMock.ts new file mode 100644 index 00000000000..e01783f6bb4 --- /dev/null +++ b/packages/js-dash-sdk/src/test/mocks/createDapiClientMock.ts @@ -0,0 +1,12 @@ +import { SinonSandbox } from "sinon"; + +export function createDapiClientMock(sinon: SinonSandbox) { + return { + platform: { + broadcastStateTransition: sinon.stub(), + getIdentity: sinon.stub(), + waitForStateTransitionResult: sinon.stub().resolves({}), + getDataContract: sinon.stub(), + } + } +} diff --git a/packages/js-dash-sdk/src/utils/createFakeIntantLock.ts b/packages/js-dash-sdk/src/utils/createFakeIntantLock.ts new file mode 100644 index 00000000000..c10aee58d75 --- /dev/null +++ b/packages/js-dash-sdk/src/utils/createFakeIntantLock.ts @@ -0,0 +1,16 @@ +import { InstantLock } from "@dashevo/dashcore-lib"; + +export function createFakeInstantLock(transactionHash: string): InstantLock { + return new InstantLock({ + version: 1, + txid: transactionHash, + signature: Buffer.alloc(96).toString('hex'), + cyclehash: '0dc8d0df62b076a7757ab5ca07dde0f1e2bfaf83f94299fd9a77577e6cc7022e', + inputs: [ + { + outpointHash: '6e200d059fb567ba19e92f5c2dcd3dde522fd4e0a50af223752db16158dabb1d', + outpointIndex: 0, + }, + ], + }); +} diff --git a/packages/js-dash-sdk/src/utils/wait.ts b/packages/js-dash-sdk/src/utils/wait.ts new file mode 100644 index 00000000000..4b4e13f1c43 --- /dev/null +++ b/packages/js-dash-sdk/src/utils/wait.ts @@ -0,0 +1,10 @@ +/** + * Asynchronously wait for a specified number of milliseconds. + * + * @param {number} ms - Number of milliseconds to wait. + * + * @returns {Promise} The promise to await on. + */ +export async function wait(ms): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/packages/js-dash-sdk/tests/fixtures/contracts.json b/packages/js-dash-sdk/tests/fixtures/contracts.json new file mode 100644 index 00000000000..e4cd4b76e55 --- /dev/null +++ b/packages/js-dash-sdk/tests/fixtures/contracts.json @@ -0,0 +1,31 @@ +{ + "ratePlatform": { + "$id": "ATU1EJjKhWZzhmuRdCvuNbpos7nwpbDqtrZ3yvxxjpKL", + "ownerId": "2ThFDAUqVp3PvHudj8xWUNL7rAdAVfHnmWyWp2AzSFQ8", + "$schema": "https://schema.dash.org/dpp-0-4-0/meta/data-contract", + "documents": { + "rate": { + "properties": { + "rating": { + "type": "integer", + "minimum": 0, + "maximum": 10 + } + }, + "indices": [ + { + "name": "rating", + "properties": [ + { + "rating": "asc" + } + ] + } + ], + "additionalProperties": false + } + }, + "protocolVersion": 0, + "version": 1 + } +} diff --git a/packages/js-dash-sdk/tests/fixtures/dp1.schema.json b/packages/js-dash-sdk/tests/fixtures/dp1.schema.json new file mode 100644 index 00000000000..7fb089482d2 --- /dev/null +++ b/packages/js-dash-sdk/tests/fixtures/dp1.schema.json @@ -0,0 +1,58 @@ +{ + "contact": { + "indices": [ + { + "name": "userIdToUserId", + "unique": true, + "properties": [ + { + "$userId": "asc" + }, + { + "toUserId": "asc" + } + ] + } + ], + "required": [ + "toUserId", + "publicKey" + ], + "properties": { + "toUserId": { + "type": "string" + }, + "publicKey": { + "type": "string" + } + }, + "additionalProperties": false + }, + "profile": { + "indices": [ + { + "name": "userId", + "unique": true, + "properties": [ + { + "$userId": "asc" + } + ] + } + ], + "required": [ + "avatarUrl", + "about" + ], + "properties": { + "about": { + "type": "string" + }, + "avatarUrl": { + "type": "string", + "format": "url" + } + }, + "additionalProperties": false + } +} diff --git a/packages/js-dash-sdk/tests/fixtures/identities.json b/packages/js-dash-sdk/tests/fixtures/identities.json new file mode 100644 index 00000000000..f4334447b2c --- /dev/null +++ b/packages/js-dash-sdk/tests/fixtures/identities.json @@ -0,0 +1,37 @@ +{ + "readme": { + "id": "98EpHfn7KJvHEtPztwaoS6ukAgBoNkNabgGL7nf2hkLu", + "type": 1, + "publicKeys": [ + { + "id": 1, + "type": 1, + "data": "A4k1moBor/sGMUdp465Z+o3U7cFB4kSEYh5BvNFyOg5d", + "isEnabled": true + } + ] + }, + "ratePlatformOwner": { + "id": "2ThFDAUqVp3PvHudj8xWUNL7rAdAVfHnmWyWp2AzSFQ8", + "publicKeys": [ + { + "id": 0, + "type": 0, + "data": "AySBYCeIUNMZ2i0apbHzpwxQPFgxtSTjUm34u8ojg7bv", + "isEnabled": true + } + ] + }, + "bob": { + "id": "Fvpr7GKiC3i6tDHiGdN4uuLsAFRUMbUnmnYcNTPW5z62", + "type": 1, + "publicKeys": [ + { + "id": 1, + "type": 1, + "data": "A2AXIu93hht/yM/tYGg+mQ5pppT4v7jOf4ngG9htwS4Q", + "isEnabled": true + } + ] + } +} diff --git a/packages/js-dash-sdk/tests/fixtures/mnemonic.json b/packages/js-dash-sdk/tests/fixtures/mnemonic.json new file mode 100644 index 00000000000..cfe90921c52 --- /dev/null +++ b/packages/js-dash-sdk/tests/fixtures/mnemonic.json @@ -0,0 +1,5 @@ +{ + "readme": "slogan quantum business chicken exile reward ride dawn cluster square mind unknown", + "ratePhezApp": "profit forget symptom head reveal chalk vivid pear shoulder pig lobster panda", + "user": "profit forget symptom head reveal chalk vivid pear shoulder pig lobster panda" +} diff --git a/packages/js-dash-sdk/tests/fixtures/ratePlatform.schema.json b/packages/js-dash-sdk/tests/fixtures/ratePlatform.schema.json new file mode 100644 index 00000000000..ec003c88d41 --- /dev/null +++ b/packages/js-dash-sdk/tests/fixtures/ratePlatform.schema.json @@ -0,0 +1,20 @@ +{ + "rate": { + "properties": { + "rating": { + "type": "integer", + "minimum": 0, + "maximum": 10 + } + }, + "indices": [{ + "name": "rating", + "properties": [ + { + "rating": "asc" + } + ] + }], + "additionalProperties": false + } +}; diff --git a/packages/js-dash-sdk/tests/fixtures/user-flow-1.json b/packages/js-dash-sdk/tests/fixtures/user-flow-1.json new file mode 100644 index 00000000000..e563e8599c1 --- /dev/null +++ b/packages/js-dash-sdk/tests/fixtures/user-flow-1.json @@ -0,0 +1,4 @@ +{ + "mnemonic": "vital tool cotton follow exhibit unfair drastic quote sweet secret bar stick", + "network": "testnet" +} diff --git a/packages/js-dash-sdk/tests/functional/sdk.js b/packages/js-dash-sdk/tests/functional/sdk.js new file mode 100644 index 00000000000..d0a7e83f8b0 --- /dev/null +++ b/packages/js-dash-sdk/tests/functional/sdk.js @@ -0,0 +1,77 @@ +const { expect } = require('chai'); + +const Identifier = require('@dashevo/dpp/lib/Identifier'); + +const { + Networks, +} = require('@dashevo/dashcore-lib'); + +const Dash = require(typeof process === 'undefined' ? '../../src/index.ts' : '../../'); + +describe('SDK', function suite() { + this.timeout(700000); + + let account; + let dpnsContractId; + let clientInstance; + + beforeEach(async () => { + dpnsContractId = Identifier.from(process.env.DPNS_CONTRACT_ID); + + const clientOpts = { + seeds: process.env.DAPI_SEED.split(','), + network: process.env.NETWORK, + wallet: { + mnemonic: null, + }, + apps: { + dpns: { + contractId: dpnsContractId, + } + } + }; + + clientInstance = new Dash.Client(clientOpts); + }); + + it('should init a Client', async () => { + expect(clientInstance.network).to.equal(process.env.NETWORK); + + expect(clientInstance.defaultAccountIndex).to.equal(0); + + expect(clientInstance.getApps().has('dpns')).to.be.true; + expect(clientInstance.getApps().get('dpns')).to.deep.equal({ + contractId: dpnsContractId, + }); + + const network = Networks.get(process.env.NETWORK).name; + expect(clientInstance.wallet.network).to.equal(network); + + expect(clientInstance.wallet.offlineMode).to.equal(false); + + expect(clientInstance.platform.dpp).to.exist; + + expect(clientInstance.platform.client).to.exist; + }); + + it('should initiate Wallet account', async () => { + account = await clientInstance.getWalletAccount(); + + expect(account.index).to.equal(0); + }) + + it('should sign and verify a message', async function () { + const idKey = account.identities.getIdentityHDKeyByIndex(0, 0); + // This transforms from a Wallet-Lib.PrivateKey to a Dashcore-lib.PrivateKey. + // It will quickly be annoying to perform this, and we therefore need to find a better solution for that. + const privateKey = Dash.Core.PrivateKey(idKey.privateKey); + const message = Dash.Core.Message('hello, world'); + const signed = message.sign(privateKey); + const verify = message.verify(idKey.privateKey.toAddress().toString(), signed.toString()); + expect(verify).to.equal(true); + }); + + it('should disconnect', async function () { + await clientInstance.disconnect(); + }); +}); diff --git a/packages/js-dash-sdk/tsconfig.json b/packages/js-dash-sdk/tsconfig.json new file mode 100644 index 00000000000..57249d6a100 --- /dev/null +++ b/packages/js-dash-sdk/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "lib": ["es6"], + "outDir": "./build", + "skipLibCheck": true, + "strict": true, + "allowUmdGlobalAccess": true, + "noImplicitAny": false, + "esModuleInterop": true, + "resolveJsonModule": true, + "allowJs": true, + "sourceMap": true, + "declaration": true, + "declarationDir": "./dist" + }, + "include": ["src"], + "typeRoots": ["node_modules/@types"] +} diff --git a/packages/js-dash-sdk/webpack.base.config.js b/packages/js-dash-sdk/webpack.base.config.js new file mode 100644 index 00000000000..792f16f9de3 --- /dev/null +++ b/packages/js-dash-sdk/webpack.base.config.js @@ -0,0 +1,58 @@ +const path = require('path'); +const webpack = require('webpack'); +const TerserPlugin = require("terser-webpack-plugin"); + +const baseConfig = { + entry: './src/index.ts', + devtool: 'eval', + module: { + rules: [ + { + test: /\.ts$/, + use: 'ts-loader', + exclude: /node_modules/, + }, + ], + }, + optimization: { + minimize: true, + minimizer: [new TerserPlugin({ + terserOptions: { + keep_classnames: true // fixes empty string in `object.constructor.name` + } + })], + }, + resolve: { + extensions: ['.ts', '.js', '.json'], + fallback: { + fs: false, + util: require.resolve('util/'), + crypto: require.resolve('crypto-browserify'), + http: require.resolve('stream-http'), + https: require.resolve('https-browserify'), + buffer: require.resolve('buffer/'), + url: require.resolve('url/'), + assert: require.resolve('assert/'), + stream: require.resolve('stream-browserify'), + path: require.resolve('path-browserify'), + os: require.resolve('os-browserify/browser'), + zlib: require.resolve('browserify-zlib'), + events: require.resolve('events/'), + string_decoder: require.resolve('string_decoder/'), + tls: require.resolve('tls/'), + net: require.resolve('net/'), + // Browser build have to use native WebSocket + ws: require.resolve('./build-utils/ws'), + }, + }, + plugins: [ + new webpack.ProvidePlugin({ + Buffer: [require.resolve('buffer/'), 'Buffer'], + process: require.resolve('process/browser'), + }), + ], + output: { + path: path.resolve(__dirname, 'dist'), + }, +} +module.exports = baseConfig; diff --git a/packages/js-dash-sdk/webpack.config.js b/packages/js-dash-sdk/webpack.config.js new file mode 100644 index 00000000000..0e19cf8a788 --- /dev/null +++ b/packages/js-dash-sdk/webpack.config.js @@ -0,0 +1,22 @@ +const path = require('path'); +const webpackBaseConfig = require("./webpack.base.config"); + +const webConfig = { + ...webpackBaseConfig, + entry: './build/src/index.js', + devtool: 'source-map', + mode: 'production', + target: 'web', + output: { + path: path.resolve(__dirname, 'dist'), + library: { + name: 'Dash', + type: 'umd' + }, + filename: 'dash.min.js', + // fixes ReferenceError: window is not defined + globalObject: "(typeof self !== 'undefined' ? self : this)" + }, +} + +module.exports = [webConfig]; diff --git a/packages/js-dpp/.babelrc b/packages/js-dpp/.babelrc new file mode 100644 index 00000000000..a29ac9986c1 --- /dev/null +++ b/packages/js-dpp/.babelrc @@ -0,0 +1,5 @@ +{ + "presets": [ + "@babel/preset-env" + ] +} diff --git a/packages/js-dpp/.editorconfig b/packages/js-dpp/.editorconfig new file mode 100644 index 00000000000..ab4df2a7241 --- /dev/null +++ b/packages/js-dpp/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf + + +[*.{md,markdown}] +trim_trailing_whitespace = false diff --git a/packages/js-dpp/.eslintignore b/packages/js-dpp/.eslintignore new file mode 100644 index 00000000000..1521c8b7652 --- /dev/null +++ b/packages/js-dpp/.eslintignore @@ -0,0 +1 @@ +dist diff --git a/packages/js-dpp/.eslintrc b/packages/js-dpp/.eslintrc new file mode 100644 index 00000000000..cbd305c321e --- /dev/null +++ b/packages/js-dpp/.eslintrc @@ -0,0 +1,15 @@ +{ + "extends": "airbnb-base", + "rules": { + "no-plusplus": 0, + "eol-last": [ + "error", + "always" + ], + "class-methods-use-this": "off", + "curly": [ + "error", + "all" + ] + } +} diff --git a/packages/js-dpp/.gitignore b/packages/js-dpp/.gitignore new file mode 100644 index 00000000000..d7d608dc8a5 --- /dev/null +++ b/packages/js-dpp/.gitignore @@ -0,0 +1,14 @@ +# ignore JetBrains IDE project specific files +.idea + +# do not track dependencies +node_modules + +# ignore generated code coverage output +.nyc_output + +# ignore build artifacts +dist + +# Env file +.env diff --git a/packages/js-dpp/.mocharc.yml b/packages/js-dpp/.mocharc.yml new file mode 100644 index 00000000000..8cf850c03a8 --- /dev/null +++ b/packages/js-dpp/.mocharc.yml @@ -0,0 +1,4 @@ +file: + - lib/test/bootstrap.js +recursive: true +timeout: 3000 diff --git a/packages/js-dpp/.npmignore b/packages/js-dpp/.npmignore new file mode 100644 index 00000000000..ee95fdbac2d --- /dev/null +++ b/packages/js-dpp/.npmignore @@ -0,0 +1,7 @@ +# Note that this file must be the same as gitignore, except for dist folder + +# do not track dependencies +node_modules + +# Ultra runner build cache +.ultra.cache.json diff --git a/packages/js-dpp/CHANGELOG.md b/packages/js-dpp/CHANGELOG.md new file mode 100644 index 00000000000..eb45f6a4308 --- /dev/null +++ b/packages/js-dpp/CHANGELOG.md @@ -0,0 +1,397 @@ +# [0.21.0](https://github.com/dashevo/js-dpp/compare/v0.20.1...v0.21.0) (2021-10-12) + + +### Features + +* introduce consensus error codes ([#341](https://github.com/dashevo/js-dpp/issues/341), [#342](https://github.com/dashevo/js-dpp/issues/342)) +* split validation logic into basic, signature, fee and state ([#331](https://github.com/dashevo/js-dpp/issues/331)) +* protocol versioning updates ([#325](https://github.com/dashevo/js-dpp/issues/325), [#326](https://github.com/dashevo/js-dpp/issues/326), [#329](https://github.com/dashevo/js-dpp/issues/329), [#330](https://github.com/dashevo/js-dpp/issues/330), [#336](https://github.com/dashevo/js-dpp/issues/336), [#337](https://github.com/dashevo/js-dpp/issues/337), [#344](https://github.com/dashevo/js-dpp/issues/344), [#346](https://github.com/dashevo/js-dpp/issues/346), [#349](https://github.com/dashevo/js-dpp/issues/349), [#351](https://github.com/dashevo/js-dpp/issues/351)) +* removed verify SML flag triggers ([#324](https://github.com/dashevo/js-dpp/issues/324)) + + +### Bug Fixes + +* building indices on arrays using `prefixItems` ([#343](https://github.com/dashevo/js-dpp/issues/343)) + + +### BREAKING CHANGES + +* `stateTransition.validateStructure` renamed to `stateTransition.validateBasic` and doesn't perform signature validation +* `stateTransition.validateData` renamed to `stateTransition.validateState` +* validation errors are changed +* removed old data triggers +* old serialized data is not compatible + + + +## [0.20.1](https://github.com/dashevo/js-dpp/compare/v0.20.0...v0.20.1) (2021-07-14) + + +### Features + +* remove verify SML flag triggers ([#324](https://github.com/dashevo/js-dpp/issues/324)) + + + +# [0.20.0](https://github.com/dashevo/js-dpp/compare/v0.19.2...v0.20.0) (2021-07-08) + + +### Features + +* add metadata to document, data contract and identity ([#318](https://github.com/dashevo/js-dpp/issues/318)) +* prevent possible ReDoS attacks for Data Contracts ([#311](https://github.com/dashevo/js-dpp/issues/311), [#317](https://github.com/dashevo/js-dpp/issues/317), [#315](https://github.com/dashevo/js-dpp/issues/315)) +* strcit data contract JSON schema validation ([#310](https://github.com/dashevo/js-dpp/issues/310), [#312](https://github.com/dashevo/js-dpp/issues/312)) + + +### BREAKING CHANGES + +* `dependencies` is not supported. Use `dependentRequired` and `dependentSchema` instead +* `additionalitems` is not supported. Use `items: false` and `prefixItems` instead +* `patternProperties` is prohibited for Data Contract +* error messages and properties are changed according to the new JSON Schema spec +* `pattern` keyword accept only RE2 compatible regular expressions +* Document type and properties minimum length is 3 chars +* `definitions` is now defined using `$defs` keyword +* JSON Schema strict validation is enabled. Previous contract schemas invalid in case they are not respecting strict mode constraints (reference [this link](https://ajv.js.org/strict-mode.html) for more information) +* usage of `if`, `then`, `else`, `allOf`, `anyOf`, `oneOf`, `patternProperties` in document properties is prohibited +* `.initialize()` must be called before using DashPlatformProtocol + + + +## [0.19.2](https://github.com/dashevo/js-dpp/compare/v0.19.1...v0.19.2) (2021-05-20) + + +### Bug Fixes + +* Cbor not decoding buffers properly in browsers ([#306](https://github.com/dashevo/js-dpp/issues/308)) + + + +## [0.19.1](https://github.com/dashevo/js-dpp/compare/v0.19.0...v0.19.1) (2021-05-04) + + +### Bug Fixes + +* `topLevelIdentity.getId` is not a function ([#306](https://github.com/dashevo/js-dpp/issues/306)) + + + +# [0.19.0](https://github.com/dashevo/js-dpp/compare/v0.18.0...v0.19.0) (2021-04-30) + + +### Features + +* add data triggers for feature flags documents ([#297](https://github.com/dashevo/js-dpp/issues/297), [#302](https://github.com/dashevo/js-dpp/issues/302)) +* ChainLock Asset Lock proof ([#296](https://github.com/dashevo/js-dpp/issues/296)) +* use `verifyInstantLock` instead of `fetchSMLStore` ([#294](https://github.com/dashevo/js-dpp/issues/294)) + + +### BREAKING CHANGES + +* `AssetLock` class was removed. +* `InstantAssetLockProof` requires `transaction` and `outputIndex` property. +* `IdentityCreateTransition` schema changed. `assetLock` property renamed to `assetLockProof` and expect `InstantAssetLockProof` or `ChainAssetLockProof`. `transaction` and `outputIndex` properties are removed. +* `IdentityTopUpTransition` schema changed. `assetLock` property renamed to `assetLockProof` and expect `InstantAssetLockProof` or `ChainAssetLockProof`. `transaction` and `outputIndex` properties are removed. + + + +# [0.18.0](https://github.com/dashevo/js-dpp/compare/v0.17.0...v0.18.0) (2021-03-03) + + +### Features + +* get modified data ids from state transitions ([#290](https://github.com/dashevo/js-dpp/issues/290)) + + +### Bug Fixes + +* BLS was throwing an error inside `uncaughtException` handler ([#293](https://github.com/dashevo/js-dpp/issues/293)) + + + +# [0.17.0](https://github.com/dashevo/js-dpp/compare/v0.16.0...v0.17.0) (2020-12-29) + + +### Features + +* dashpay data triggers ([#285](https://github.com/dashevo/js-dpp/issues/285)) +* update dashcore-lib ([#271](https://github.com/dashevo/js-dpp/issues/271), [#283](https://github.com/dashevo/js-dpp/issues/283), [#287](https://github.com/dashevo/js-dpp/issues/287)) +* fund identity with Asset Lock Proofs ([#276](https://github.com/dashevo/js-dpp/issues/276), [#277](https://github.com/dashevo/js-dpp/issues/277), [#280](https://github.com/dashevo/js-dpp/issues/280)) +* limit publicKeys items to 32 ([#278](https://github.com/dashevo/js-dpp/issues/278)) + + +### Bug Fixes + +* fs not found error on deploy ([#273](https://github.com/dashevo/js-dpp/issues/273)) + + +### BREAKING CHANGES + +* Identity Create and Topup Transitions expect Asset Lock object instead of asset lock outpoint +* `identity.create` and `identity.createIdentityTopUpTransition` expect asset lock transaction, output, and proof instead of outpoint +* renamed `skipAssetLockConfirmationValidation` DPP option to `skipAssetLockProofSignatureVerification` +* identity allows only 32 public keys + + + +# [0.16.0](https://github.com/dashevo/js-dpp/compare/v0.15.0...v0.16.0) (2020-10-26) + + +### Features + +* use Buffers for binary data ([#238](https://github.com/dashevo/js-dpp/issues/238), [#240](https://github.com/dashevo/js-dpp/issues/240), [#241](https://github.com/dashevo/js-dpp/issues/241), [#246](https://github.com/dashevo/js-dpp/issues/246), [#247](https://github.com/dashevo/js-dpp/issues/247), [#261](https://github.com/dashevo/js-dpp/issues/261), [#262](https://github.com/dashevo/js-dpp/issues/262), [#263](https://github.com/dashevo/js-dpp/issues/263), [#266](https://github.com/dashevo/js-dpp/issues/266)) +* Identifier property type ([#252](https://github.com/dashevo/js-dpp/issues/252), [#265](https://github.com/dashevo/js-dpp/issues/265), [#267](https://github.com/dashevo/js-dpp/issues/267), [#268](https://github.com/dashevo/js-dpp/issues/268)) +* `byteArray` JSON Schema keyword instead of `contentEncoding` ([#245](https://github.com/dashevo/js-dpp/issues/245), [#248](https://github.com/dashevo/js-dpp/issues/248), [#251](https://github.com/dashevo/js-dpp/issues/251), [#254](https://github.com/dashevo/js-dpp/issues/254), [#260]((https://github.com/dashevo/js-dpp/issues/260))) +* use 32 random bytes instead of blockchain address for entropy ([#250](https://github.com/dashevo/js-dpp/issues/250), [#259](https://github.com/dashevo/js-dpp/issues/259)) +* validate and store all identity keys instead of the first one ([#234](https://github.com/dashevo/js-dpp/issues/234), [#237], [#242](https://github.com/dashevo/js-dpp/issues/242)) +* validate document upon creation ([#255](https://github.com/dashevo/js-dpp/issues/255)) +* hash methods responds with Buffer ([#249](https://github.com/dashevo/js-dpp/issues/249)) +* introduce a BLS identity key type ([#239](https://github.com/dashevo/js-dpp/issues/239)) +* add revision property to identity ([#235](https://github.com/dashevo/js-dpp/issues/235)) +* `isEnabled` property removed from Identity Public Key [#236](https://github.com/dashevo/js-dpp/issues/236) + + +### BREAKING CHANGES + +* Node.JS 10 and lower are not supported +* data models use Buffers instead of strings for binary fields +* `serialize` methods renamed to `toBuffer` +* `createFromSerialized` methods renamed to `createFromBuffer` +* `StateRepository` accept `Identifier` and `Buffer` instead of strings +* identifiers like document, data contract and identity IDs are instances `Identifier` (compatible with `Buffer`) +* `contentEncoding` keyword isn't supported anymore. Use `byteArray: true` with `type: array` to store binary data +* Data Contract and Document entropy is now a random 32 bytes instead of blockchain address +* identity and identity create transition can't contain duplicate public keys anymore +* `DocumentFactory#create` throws an error if specified data is not valid +* `hash` methods respond with `Buffer` instead of hex encoded string +* ECDSA Public key (type `0`) must be a 33 long byte array. +* Identity's `revision` is required +* Identity Public Key's `isEnabled` is not accepted +* Data created or serialized by previous is incompatible + + + +# [0.15.0](https://github.com/dashevo/js-dpp/compare/v0.14.0...v0.15.0) (2020-09-04) + + +### Features + +* protocol versioning ([#217](https://github.com/dashevo/js-dpp/issues/217)) +* document binary properties ([#199](https://github.com/dashevo/js-dpp/issues/199), [#211](https://github.com/dashevo/js-dpp/issues/211), [#215](https://github.com/dashevo/js-dpp/issues/215), [#218](https://github.com/dashevo/js-dpp/issues/218), [#213](https://github.com/dashevo/js-dpp/issues/213)) +* handle unique and alias identities in DPNS data triggers ([#201](https://github.com/dashevo/js-dpp/issues/213)) +* add data trigger condition to check allowing subdomain rules ([#224](https://github.com/dashevo/js-dpp/issues/224), [#228](https://github.com/dashevo/js-dpp/pull/228)) +* reject `replace` and `delete` actions for DPNS preorder document ([#210](https://github.com/dashevo/js-dpp/issues/224)) + + +### Bug Fixes + +* empty where conditions were sent during unique indices validation ([#222](https://github.com/dashevo/js-dpp/issues/222)) +* duplicate key error in case of unique index on optional fields ([#230](https://github.com/dashevo/js-dpp/pull/230)) +* invalid arguments were submitted to search for parent domain ([#226](https://github.com/dashevo/js-dpp/issues/226)) +* invalid where clause was sent, invalid query error was not handled by unique index validation method ([#220](https://github.com/dashevo/js-dpp/issues/220)) +* undefined in data contract schema id ([#209](https://github.com/dashevo/js-dpp/issues/209)) +* data contract fixture was not isolated properly ([#207](https://github.com/dashevo/js-dpp/issues/207)) +* schema with key or id already exists ([#203](https://github.com/dashevo/js-dpp/issues/203)) + + +### BREAKING CHANGES + +* `protocolVersion` property equals to `0` is required for all data structures +* `Document` now awaits `DataContract` as a second argument in constructor +* `DocumentsBatchTransition` now awaits `DataContract` as a second argument in constructor +* a document compound unique index shouldn't contain both required and optional properties +* a document with a compound unique index must contain all indexed properties or non of them +* only second-level DPNS domain owner is allowed to create its subdomains +* DPNS preorder document is immutable now. Modification and deletion of preorder are restricted. +* `getDocumentsFixture.dataContract` is not available anymore +* DPNS data trigger expect `dashUniqueIdentityId` and `dashAliasIdentityId` records, instead of oboslete `dashIdentity` + + + +# [0.14.0](https://github.com/dashevo/js-dpp/compare/v0.13.1...v0.14.0) (2020-07-22) + + +### Bug Fixes + +* missing indexed string property constraint validation ([#196](https://github.com/dashevo/js-dpp/issues/196)) +* error when the indexed field has an undefined value ([#194](https://github.com/dashevo/js-dpp/issues/194)) +* conflicting schema ids in AJV cache ([#187](https://github.com/dashevo/js-dpp/issues/187)) + + +### Features + +* add `createdAt` and `updatedAt` timestamps to Document ([#192](https://github.com/dashevo/js-dpp/issues/192)) +* disable unsupported JSON Schema conditions ([#193](https://github.com/dashevo/js-dpp/issues/193)) + + +### Documentation + +* readme standard updates ([#189](https://github.com/dashevo/js-dpp/issues/189)) + + +### BREAKING CHANGES + +* Indexed strings should have `maxLength` constraint not greater than 1024 chars +* JSON Schema conditions (`allOf`, `if`, ...) are not allowed in Document JSON Schema + + + +## [0.13.1](https://github.com/dashevo/js-dpp/compare/v0.13.0...v0.13.1) (2020-06-15) + + +### Bug Fixes + +* conflicting schema ids in AJV cache ([#187](https://github.com/dashevo/js-dpp/issues/187)) + + + +# [0.13.0](https://github.com/dashevo/js-dpp/compare/v0.12.1...v0.13.0) (2020-06-08) + + +### Bug Fixes + +* document validation after validation the contract with the same id ([#166](https://github.com/dashevo/js-dpp/pull/166)) + + +### Features + +* support documents from multiple contracts in Documents Batch Transition ([#159](https://github.com/dashevo/js-dpp/pull/159)) +* add `hash` method to `IdentityPublicKey` ([#170](https://github.com/dashevo/js-dpp/pull/170), [#173](https://github.com/dashevo/js-dpp/pull/173)) +* `StateRepository#fetchTransaction` responses with verbose data ([#169](https://github.com/dashevo/js-dpp/pull/169)) +* check asset lock transaction is confirmed ([#168](https://github.com/dashevo/js-dpp/pull/168), [#184](https://github.com/dashevo/js-dpp/pull/184)) +* introduce Identity Topup Transition ([#167](https://github.com/dashevo/js-dpp/pull/167), [#178](https://github.com/dashevo/js-dpp/pull/178), [#180](https://github.com/dashevo/js-dpp/pull/180)) +* validate first identity public key uniqueness ([#175](https://github.com/dashevo/js-dpp/pull/175)) + + +### Code Refactoring + +* rename `LockTransaction` to `AssetLockTransaction` ([#177](https://github.com/dashevo/js-dpp/pull/177)) + + +### BREAKING CHANGES + +* the first public key in Identity should be unique +* expect `StateRepository#fetchTransaction` to respond with verbose transaction + + +## [0.12.1](https://github.com/dashevo/js-dpp/compare/v0.12.0...v0.12.1) (2020-04-22) + + +### Bug Fixes + +* data trigger should accept document transition ([#164](https://github.com/dashevo/js-dpp/issues/164)) + + +# [0.12.0](https://github.com/dashevo/js-dpp/compare/v0.11.0...v0.12.0) (2020-04-17) + + +### Bug Fixes + +* do not allow to change `ownerId` and `entropy` ([bff5807](https://github.com/dashevo/js-dpp/commit/bff580701322e2100e484989c476d583d26af38a)) +* json schema for `signaturePublicKeyId` ([#161](https://github.com/dashevo/js-dpp/issues/161)) +* wrong entropy size ([#157](https://github.com/dashevo/js-dpp/issues/157)) +* data contract definitions might be `null` or `undefined` ([#153](https://github.com/dashevo/js-dpp/issues/153)) +* identity existence validation in data contract structure validation ([#149](https://github.com/dashevo/js-dpp/issues/149)) +* state transition signature validation in data contract structure validation ([#150](https://github.com/dashevo/js-dpp/pull/150)) + + +### Code Refactoring + +* rename `$rev` to `$revision` ([#140](https://github.com/dashevo/js-dpp/issues/140)) +* rename `userId` to `ownerId` ([b9a5e83](https://github.com/dashevo/js-dpp/commit/b9a5e839608f94c964ff791bcbae4cb03a46028d)) +* remove `type` from Identity ([227dc4d](https://github.com/dashevo/js-dpp/commit/227dc4d96e72172fd17cc44b46dd3ca0ef3da301)) +* remove `version` from Data Contract ([f856ecc](https://github.com/dashevo/js-dpp/commit/f856ecc1b00e8f0962f96a9f84d84bd2322ad374)) +* rename `$ownerId` to `ownerId` in Data Contract ([#160](https://github.com/dashevo/js-dpp/pull/160)) +* rename `$contractId` to `$dataContractId` in Document ([158](https://github.com/dashevo/js-dpp/pull/158)) +* split document model and it's state transitions ([#126](https://github.com/dashevo/js-dpp/issues/126), [#156](https://github.com/dashevo/js-dpp/pull/156)) +* store document ID as a part of the document ([3d10a01](https://github.com/dashevo/js-dpp/commit/3d10a01577ca871cbf3fb1c4ea5f39904a27ca33)) +* Data Contract Create Transition now accepts raw data ([#136](https://github.com/dashevo/js-dpp/issues/136)) +* start types and indices from `0` instead of `1` ([#155](https://github.com/dashevo/js-dpp/pull/155)) +* put JSON Schemas into order ([#135](https://github.com/dashevo/js-dpp/pull/135)) + + +### Features + +* implement apply state transition function ([#138](https://github.com/dashevo/js-dpp/issues/138), [#139](https://github.com/dashevo/js-dpp/issues/139), [#142](https://github.com/dashevo/js-dpp/issues/142), [#141](https://github.com/dashevo/js-dpp/issues/141), [#147](https://github.com/dashevo/js-dpp/issues/147), [#143](https://github.com/dashevo/js-dpp/issues/143)) +* generate Data Contract ID from `ownerId` and entropy ([4c0dae1](https://github.com/dashevo/js-dpp/commit/4c0dae1a248d5a8af92f1023cdeed58377e51aae)) +* introduce balance to Identities ([#137](https://github.com/dashevo/js-dpp/issues/137), [b13a9bb](https://github.com/dashevo/js-dpp/commit/b13a9bb2dfb22ea355620e064675a600a3908018), [#146](https://github.com/dashevo/js-dpp/issues/146)) +* validate ST size is less than 16 Kb ([ff7aa51](https://github.com/dashevo/js-dpp/commit/ff7aa51dd88d4047637fb69e048a896dd92f3fd0), [70c3c54](https://github.com/dashevo/js-dpp/commit/70c3c541920a5bdc73845ac1ef835d7b21dfa92b)) +* validate state transition fee ([48c9fda](https://github.com/dashevo/js-dpp/commit/48c9fda5cf958eb2046c8a5a98e09e78c1e8085f), [#145](https://github.com/dashevo/js-dpp/issues/145), [0cb1d6f](https://github.com/dashevo/js-dpp/commit/0cb1d6f69650e91ed944a11f77aeb6541e5755f4)) +* create Identity factory now accepts locked out point and public keys ([#151](https://github.com/dashevo/js-dpp/issues/151)) +* `getDataContractFixture` accepts `ownerId` ([#148](https://github.com/dashevo/js-dpp/issues/148)) +* implement create identity create transition factory ([#152](https://github.com/dashevo/js-dpp/issues/152)) +* introduce `signByPrivateKey` and `verifySignatureByPublicKey` methods to ST ([4eb5cdc](https://github.com/dashevo/js-dpp/commit/4eb5cdc408df8fe95294f668743c75da17ac0083)) +* verbose invalid data errors ([#134](https://github.com/dashevo/js-dpp/pull/134)) + + +### BREAKING CHANGES + +* Data Contract ID is ownerId + entropy. You don't need to create an additional identity anymore. +* Data Contract Create Transition now accepts raw data +* size of serialized state transition must be less than 16 Kb +* `type` removed from Identity due to Data Contract doesn't require it anymore +* `version` removed from Data Contract +* `userId` renamed to `ownerId` +* Documents State Transition renamed to and it's structure is changed +* `applyStateTransition` methods no longer a part of identity, data contract and document facades +* renamed `$rev` field to `$revision` in raw document model +* applyIdentityStateTransition is now asynchronous +* Documents State Transition is now happening through separate state transition classes and renamed to Documents Batch Transition. Hence document class no longer have `$action` field. `$action` is now starting from 0. `$entropy` field is now a part of document create state transition. `createStateTransition` method of a document factory now accepts a map with actions as keys (`create`, `replace`, `delete`) and document arrays as values respectively. +* create Identity factory accepts locked out point and public keys instead of ID and `IndentityPublicKey` +* types and indices now starts from `0` instead of `1` +* Data Provider renamed to State Repository and store/remove functions introduced + + +## [0.11.1](https://github.com/dashevo/js-dpp/compare/v0.11.0...v0.11.1) (2020-03-17) + + +### Bug Fixes + +* documents validate against wrong Data Contract ([0db6e44](https://github.com/dashevo/js-dpp/commit/0db6e44cfa8309d46bb42b5a0174574604861b2b)) + + +# [0.11.0](https://github.com/dashevo/js-dpp/compare/v0.10.0...v0.11.0) (2020-03-09) + + +### Bug Fixes + +* missing public key during ST signature validation ([667402d](https://github.com/dashevo/js-dpp/commit/667402dd659d50d7c2d9da5c61c32f2964a4c8b8)) +* add npmignore ([c2e5f5d](https://github.com/dashevo/js-dpp/commit/c2e5f5d5b6c891b3280d02da659fb8eda613a43c)) +* prevent to update dependencies with major version `0` to minor versions ([ea7de93](https://github.com/dashevo/js-dpp/commit/ea7de9379a38b856f4a7b779786986afacd75b0d)) + + +### Features + +* catch `decode` errors and rethrow consensus error ([892be82](https://github.com/dashevo/js-dpp/commit/892be823d44ff6edab82d89fa8e54b88f6b63534)) +* limit data contract schema max depth ([f78df33](https://github.com/dashevo/js-dpp/commit/f78df334cf2f3e54744bcafdbbadeae54a5c980b)) +* limit serialized Data Contract size to 15Kb ([7c95197](https://github.com/dashevo/js-dpp/commit/7c9519733cd05ef2c0b8d388a5135f54371f1054)) +* remove Data Contract restriction option ([0edd6ff](https://github.com/dashevo/js-dpp/commit/0edd6ff85e2fe077f3c1c05c5fb8299417e1123e)) +* validate documents JSON Schemas during data contract validation ([d88817d](https://github.com/dashevo/js-dpp/commit/d88817d5b7438168d225b6cec36377dac3e30284)) +* ensure `maxItems` with `uniqueItems` for large non-scalar arrays ([3364325](https://github.com/dashevo/js-dpp/commit/3364325d23aaf72f37f2fdc663b29e8332d98f0e)) +* ensure `maxLength` in case of `pattern` or `format` ([297c754](https://github.com/dashevo/js-dpp/commit/297c7543bfbe6723f92d83c50facb75ac4bfa00c)) +* ensure all arrays items are defined ([43d7b8f](https://github.com/dashevo/js-dpp/commit/43d7b8f20886ec2c9f1bd6d16d6760d84a18c7c9)) +* ensure all object properties are defined ([d9f71df](https://github.com/dashevo/js-dpp/commit/d9f71df99618719201ebfb0a3267bda1ed5b77c4)) +* limit number of allowed indices ([5adff5d](https://github.com/dashevo/js-dpp/commit/5adff5d917c6e5bc11ee337ddb9f1775e8afc7d9)) +* `validateData` method accept raw data too ([e72a627](https://github.com/dashevo/js-dpp/commit/e72a6274a26002ddd88c08c15dc89b8c8f94564d)) +* prevent of defining `propertyNames` ([c40663f](https://github.com/dashevo/js-dpp/commit/c40663fc9c5db35a00c33ff43b24e2719ee84ee9)) +* prevent of defining remote `$ref` ([34bdb3f](https://github.com/dashevo/js-dpp/commit/34bdb3f9c78cd1f2d01264752a9fb712ca313de8)) +* prevent of using `default` keyword in Data Contract ([7629878](https://github.com/dashevo/js-dpp/commit/762987887112a89d4a153167e89a7ec97429994f)) +* throw error if 16Kb reached for payload in `encode` function ([c6aba8b](https://github.com/dashevo/js-dpp/commit/c6aba8bf38c4a0f8c6dd955624eab6bf07a20a9c)) +* accept `JsonSchemaValidator` as an option ([ee1bb0f](https://github.com/dashevo/js-dpp/commit/ee1bb0f180c8a3550da1f63c7a0200dac19f3966)) + + +### BREAKING CHANGES + +* Data Contract schema max depth is now limited by 500 +* Serialized Data Contract size is now limited to 15Kb +* `validate`, `createFromSerialized`, `createFromObject` methods of Data Contract Factory are now async +* `items` and `additionalItems` are required for arrays +* `properties` and `additionalProperties` are required for objects +* number of indices limited to 10 +* number of unique indices limited to 3 +* number of properties in an index limited to 10 +* required `maxItems` with `uniqueItems` for large non-scalar arrays +* required `maxLength` in case of `pattern` or `format` +* `propertyNames` keyword is restricted in document schema +* `default` keyword is restricted in Data Contract +* `encode` function throws error if payload is bigger than 16Kb diff --git a/packages/js-dpp/LICENSE b/packages/js-dpp/LICENSE new file mode 100644 index 00000000000..f735c60619c --- /dev/null +++ b/packages/js-dpp/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017-2019 Dash Core Group, Inc. + +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/packages/js-dpp/README.md b/packages/js-dpp/README.md new file mode 100644 index 00000000000..0f36bcd9028 --- /dev/null +++ b/packages/js-dpp/README.md @@ -0,0 +1,37 @@ +# Dash Platform Protocol JS + +[![NPM Version](https://img.shields.io/npm/v/@dashevo/dpp)](https://www.npmjs.com/package/@dashevo/dpp) +[![Build Status](https://github.com/dashevo/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashevo/platform/actions/workflows/release.yml) +[![Release Date](https://img.shields.io/github/release-date/dashevo/platform)](https://github.com/dashevo/platform/releases/latest) +[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen)](https://github.com/RichardLitt/standard-readme) + +The JavaScript implementation of the [Dash Platform Protocol](https://dashplatform.readme.io/docs/explanation-platform-protocol) + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contributing](#contributing) +- [License](#license) + +## Install + +```sh +npm install @dashevo/dpp +``` + +## Usage + +See [documentation](https://dashevo.github.io/platform/Dash-Platform-Protocol/usage/DashPlatformProtocol/) + +## Maintainer + +[@shumkov](https://github.com/shumkov) + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs. + +## License + +[MIT](LICENSE) © Dash Core Group, Inc. diff --git a/packages/js-dpp/docs/.nojekyll b/packages/js-dpp/docs/.nojekyll new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/js-dpp/docs/_sidebar.md b/packages/js-dpp/docs/_sidebar.md new file mode 100644 index 00000000000..a9b152396b6 --- /dev/null +++ b/packages/js-dpp/docs/_sidebar.md @@ -0,0 +1,14 @@ +- Usage + - [new DashPlatformProtocol()](usage/DashPlatformProtocol.md) + - [dpp.dataContract](usage/dataContract.md) + - [dpp.document](usage/document.md) + - [dpp.identity](usage/identity.md) + - [dpp.stateTransition](usage/stateTransition.md) +- Primitives + - [AbstractStateTransition](primitives/AbstractStateTransition.md) + - [DataContract](primitives/DataContract.md) + - [DataTrigger](primitives/DataTrigger.md) + - [Document](primitives/Document.md) + - [Identifier](primitives/Identifier.md) + - [Identity](primitives/Identity.md) +- [License](https://github.com/dashevo/js-dpp/blob/master/LICENSE) diff --git a/packages/js-dpp/docs/index.html b/packages/js-dpp/docs/index.html new file mode 100644 index 00000000000..f16f7492afb --- /dev/null +++ b/packages/js-dpp/docs/index.html @@ -0,0 +1,43 @@ + + + + + js-dpp - JavaScript implementation of the Dash Platform Protocol + + + + + + + +
+ + + + + + diff --git a/packages/js-dpp/docs/primitives/AbstractStateTransition.md b/packages/js-dpp/docs/primitives/AbstractStateTransition.md new file mode 100644 index 00000000000..607bb3df76f --- /dev/null +++ b/packages/js-dpp/docs/primitives/AbstractStateTransition.md @@ -0,0 +1,125 @@ +**Usage**: `new AbstractStateTransition(rawStateTransition)` + +**Description**: Instantiate a new AbstractStateTransition. + +**Parameters**: + +| parameters | type | required | Description | +|---------------------------------------|-----------------------|--------------------| --------------------------| +| **rawStateTransition** | RawStateTransition | yes | | +| **rawStateTransition.protocolVersion**| number | yes | | +| **rawStateTransition.type** | number | yes | | +| **rawStateTransition.signature** | string/null | yes | | + +**Returns**: A new valid instance of AbstractStateTransition + +## .getProtocolVersion() + +**Description**: Get protocol version + +**Parameters**: None. + +**Returns**: {number} + +## .getSignature() + +**Description**: Returns signature + +**Parameters**: None. + +**Returns**: {Buffer} + +## .setSignature(signature) + +**Description**: Set signature + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **signature** | Buffer | no | | + +**Returns**: {AbstractStateTransition} + +## .getId() + +**Description**: Get State Transition id + +**Parameters**: None. + +**Returns**: {Buffer} + +## .signByPrivateKey(privateKey) + +**Description**: Sign data with private key + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|-------------------------------------|----------| ----------------------------------------- | +| **signature** | string/Buffer/Uint8Array/PrivateKey | no | privateKey string must be hex or base58 | + +**Returns**: {AbstractStateTransition} + +## .verifySignatureByPublicKey(privateKey) + +**Description**: Verify signature with private key + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|-------------------------------------|----------| ----------------------------------------- | +| **signature** | string/Buffer/Uint8Array/PrivateKey | no | privateKey string must be hex or base58 | + +**Returns**: {boolean} + +## .calculateFee() + +**Description**: Calculate ST fee in credits + +**Parameters**: None. + +**Returns**: {number} + +## .toObject(options) + +**Description**: Return state transition as plain object + +**Parameters**: + +| parameters | type | required | Description | +|--------------------------------------|------------------------|--------------------| -------------------------------- | +| **options** | Object | no | | +| **options.skipSignature** | Boolean[=false] | no | | +| **options.skipIdentifiersConversion**| Boolean[=false] | no | | + +**Returns**: {RawStateTransition} + +## .toJSON() + +**Description**: Return state transition as JSON object + +**Parameters**: None. + +**Returns**: {JsonStateTransition} + +## .toBuffer(options) + +**Description**: Return serialized State Transition as buffer + +**Parameters**: + +| parameters | type | required | Description | +|--------------------------|------------------------|--------------------| -------------------------------- | +| **options** | Object | no | | +| **options.skipSignature**| Boolean[=false] | no | | + +**Returns**: {Buffer} + +## .hash() + +**Description**: Returns state transition hash + +**Parameters**: None. + +**Returns**: {Buffer} diff --git a/packages/js-dpp/docs/primitives/DataContract.md b/packages/js-dpp/docs/primitives/DataContract.md new file mode 100644 index 00000000000..244de4cfa7d --- /dev/null +++ b/packages/js-dpp/docs/primitives/DataContract.md @@ -0,0 +1,226 @@ +**Usage**: `new DataContract(rawDataContract)` +**Description**: Instantiate a DataContract. + +**Parameters**: + +| parameters | type | required | Description | +|---------------------------------------|------------------|--------------------| --------------------------| +| **rawDataContract** | RawDataContract | yes | | +| **rawDataContract.$id** | Buffer | yes | | +| **rawDataContract.$schema** | string | yes | | +| **rawDataContract.protocolVersion** | number | yes | | +| **rawDataContract.ownerId** | Buffer | yes | | +| **rawDataContract.documents** | Object | yes | | +| **rawDataContract.$defs** | Object | no | | + +**Returns**: A new valid instance of DataContract + +## .getProtocolVersion() + +**Description**: Get Data Contract protocol version + +**Parameters**: None. + +**Returns**: {number} + +## .getId() + +**Description**: Get Data Contract id + +**Parameters**: None. + +**Returns**: {Identifier} + +## .getOwnerId() + +**Description**: Get Data Contract owner id + +**Parameters**: None. + +**Returns**: {Identifier} + +## .getJsonSchemaId() + +**Description**: Get Data Contract JSON Schema ID + +**Parameters**: None. + +**Returns**: {string} + +## .getJsonMetaSchema() + +**Description**: Get Data Contract JSON Meta Schema + +**Parameters**: None. + +**Returns**: {string} + +## .setJsonMetaSchema(schema) + +**Description**: Allow to set JSON Meta Schema to this DataContract (overwrite previous value). + +**Parameters**: + +| parameters | type | required | Description | +|-----------------------|-----------------|--------------------| -------------------------------- | +| **schema** | string | yes | | + +**Returns**: {DataContract} + +## .setDocuments(documents) + +**Description**: Set documents for this DataContract (overwrite previous value). + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **documents** | Object | yes | | + +**Returns**: {DataContract} + +## .getDocuments() + +**Description**: Get Data Contract documents + +**Parameters**: None. + +**Returns**: {Object} - documents + +## .isDocumentDefined(type) + +**Description**: Returns true if document type has been defined + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|---------|--------------------| -------------------------------- | +| **type** | string | yes | | + +**Returns**: {Boolean} - whether document type has been defined + +## .setDocumentSchema(type, schema) + +**Description**: Setter for document schema. + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|---------|--------------------| -------------------------------- | +| **type** | string | yes | | +| **schema** | object | yes | | + +**Returns**: {DataContract} + +## .getDocumentSchema(type) + +**Description**: Get Data Contract Document Schema for the provided type + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|---------|--------------------| -------------------------------- | +| **type** | string | yes | | + +**Returns**: {Object} - document + +## .getDocumentSchemaRef(type) + +**Description**: Get Data Contract Document schema reference + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|---------|--------------------| -------------------------------- | +| **type** | string | yes | | + +**Returns**: {{$ref: string}} - reference + +## .setDefinitions($defs) + +**Description**: Setter for $defs. + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **$defs** | Object | yes | | + +**Returns**: {DataContract} + +## .getDefinitions() + +**Description**: Get Data Contract $defs + +**Parameters**: None. + +**Returns**: {Object} - $defs + +## .getBinaryProperties(type) + +**Description**: Get properties with `contentEncoding` constraint + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|---------|--------------------| -------------------------------- | +| **type** | string | yes | | + +**Returns**: {Object} + +## .toObject(options) + +**Description**: Return Data Contract as plain object + +**Parameters**: + +| parameters | type | required | Description | +|---------------------------|---------|----------| -------------------------------- | +| **options** | Object | no | | +| **options.skipIdentifiersConversion** | Boolean | no | | + +**Returns**: {RawDataContract} + +## .toJSON() + +**Description**: Return Data Contract as JSON object + +**Parameters**: None. + +**Returns**: {JsonDataContract} + +## .toBuffer() + +**Description**: Return Data Contract as a Buffer + +**Parameters**: None. + +**Returns**: {Buffer} + +## .hash() + +**Description**: Returns Data Contract hash + +**Parameters**: None. + +**Returns**: {Buffer} + +## .setEntropy(entropy) + +**Description**: Set Data Contract entropy + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **entropy** | Buffer | yes | | + +**Returns**: {DataContract} + +## .getEntropy() + +**Description**: Get Data Contract entropy + +**Parameters**: None. + +**Returns**: {Buffer} diff --git a/packages/js-dpp/docs/primitives/DataTrigger.md b/packages/js-dpp/docs/primitives/DataTrigger.md new file mode 100644 index 00000000000..ba3ac40e4bf --- /dev/null +++ b/packages/js-dpp/docs/primitives/DataTrigger.md @@ -0,0 +1,41 @@ +**Usage**: `new DataTrigger(dataContractId, documentType, transitionAction, trigger, topLevelIdentity)` +**Description**: Instantiate a DataTrigger. + +**Parameters**: + +| parameters | type | required | Description | +|---------------------------------------|---------------------------|--------------------| --------------------------| +| **dataContractId** | Buffer/Identifier | yes | | +| **documentType** | string | yes | | +| **transitionAction** | number | yes | | +| **trigger** |string/DocumentTransition[]| yes | | +| **topLevelIdentity** | Buffer/Identifier | yes | | + +**Returns**: A new valid instance of DataTrigger + +## .isMatchingTriggerForData(dataContractId, documentType, transitionAction) + +**Description**: Check this trigger is matching for specified data + +**Parameters**: + +| parameters | type | required | Description | +|---------------------------------------|---------------------------|--------------------| --------------------------| +| **dataContractId** | string | yes | | +| **documentType** | string | yes | | +| **transitionAction** | number | yes | | + +**Returns**: {boolean} + +## .execute(documentTransition, context) + +**Description**: Execute data trigger + +**Parameters**: + +| parameters | type | required | Description | +|---------------------------------------|-----------------------------|--------------------| --------------------------| +| **documentTransition** | DocumentTransition[] | yes | | +| **context** | DataTriggerExecutionContext | yes | | + +**Returns**: {Promise}} diff --git a/packages/js-dpp/docs/primitives/Document.md b/packages/js-dpp/docs/primitives/Document.md new file mode 100644 index 00000000000..4c13e8f0840 --- /dev/null +++ b/packages/js-dpp/docs/primitives/Document.md @@ -0,0 +1,242 @@ +**Usage**: `new Document(rawDocument)` +**Description**: Instantiate a Document. + +**Parameters**: + +| parameters | type | required | Description | +|---------------------------------------|-----------------|--------------------| --------------------------| +| **rawDocument** | RawDocument | yes | | +| **rawDocument.$id** | Buffer | yes | | +| **rawDocument.$dataContractId** | string | yes | | +| **rawDocument.$protocolVersion** | number | yes | | +| **rawDocument.$type** | string | yes | | +| **rawDocument.$ownerId** | Buffer | yes | | +| **rawDocument.$revision** | number | yes | | +| **rawDocument.$createdAt** | number | no | | +| **rawDocument.$updatedAt** | number | no | | +| **dataContract** | DataContract | yes | | + +**Returns**: A new valid instance of Document + +## Document.fromJSON(jsonDocument, dataContract) + +**Description**: Instantiate a Document. + +**Parameters**: + +| parameters | type | required | Description | +|---------------------------------------|-----------------|--------------------| --------------------------| +| **jsonDocument** | JsonDocument | yes | | +| **dataContract** | DataContract | yes | | + +**Returns**: {Document} - A new valid instance of Document + +## .getProtocolVersion() + +**Description**: Get Document protocol version + +**Parameters**: None. + +**Returns**: {number} + +## .getId() + +**Description**: Get Document id + +**Parameters**: None. + +**Returns**: {Identifier} + +## .getType() + +**Description**: Get Document type + +**Parameters**: None. + +**Returns**: {string} + +## .getDataContractId() + +**Description**: Get Document Contract Id + +**Parameters**: None. + +**Returns**: {Identifier} + +## .getDataContract() + +**Description**: Get Document Data Contract + +**Parameters**: None. + +**Returns**: {DataContract} + +## .getOwnerId() + +**Description**: Get Document owner id + +**Parameters**: None. + +**Returns**: {Identifier} + +## .setRevision(revision) + +**Description**: Set Document revision + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **revision** | number | yes | | + +**Returns**: {Document} + +## .getRevision() + +**Description**: Get Document revision + +**Parameters**: None. + +**Returns**: {number} + +## .setEntropy(entropy) + +**Description**: Set Document entropy + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **entropy** | Buffer | yes | | + +**Returns**: {Document} + +## .getEntropy() + +**Description**: Get Document entropy + +**Parameters**: None. + +**Returns**: {Buffer} + +## .setData(data) + +**Description**: Set document data (overwrite any previous data set) + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **data** | Object | yes | | + +**Returns**: {Document} + +## .getData() + +**Description**: Get Document data + +**Parameters**: None. + +**Returns**: {Object} + +## .get(path) + +**Description**: Retrieves the field specified by path + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **path** | String | yes | | + +**Returns**: {*} + +## .set(path, value) + +**Description**: Set the field specified by {path} + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **path** | String | yes | | +| **value** | * | yes | | + +**Returns**: {Document} + +## .setCreatedAt(date) + +**Description**: Set document creation date + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **date** | Date | yes | | + +**Returns**: {Document} + +## .getCreatedAt() + +**Description**: Get document creation date + +**Parameters**: None. + +**Returns**: {Date} + +## .setUpdatedAt(date) + +**Description**: Set document updated date + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **date** | Date | yes | | + +**Returns**: {Document} + +## .getUpdatedAt() + +**Description**: Get document updated date + +**Parameters**: None. + +**Returns**: {Date} + +## .toJSON() + +**Description**: Return Document as JSON object + +**Parameters**: None. + +**Returns**: {JsonDocument} + +## .toObject(options) + +**Description**: Return Document as plain object (without converting encoded fields) + +**Parameters**: + +| parameters | type | required | Description | +|---------------------------|------------------------|--------------------| -------------------------------- | +| **options** | Object | no | | +| **options.skipIdentifiersConversion** | boolean[=false] | no | | + +**Returns**: {RawDocument} + +## .toBuffer() + +**Description**: Return serialized Document as buffer + +**Parameters**: None. + +**Returns**: {Buffer} + +## .hash() + +**Description**: Returns Document hash + +**Parameters**: None. + +**Returns**: {Buffer} diff --git a/packages/js-dpp/docs/primitives/Identifier.md b/packages/js-dpp/docs/primitives/Identifier.md new file mode 100644 index 00000000000..e82b07fc417 --- /dev/null +++ b/packages/js-dpp/docs/primitives/Identifier.md @@ -0,0 +1,39 @@ +**Usage**: `new Identifier(buffer)` +**Description**: Instantiate a new Identifier. +Implements Buffer methods with base58 as default encoding. + +**Parameters**: + +| parameters | type | required | Description | +|---------------------------------------|-----------------------|--------------------| --------------------------| +| **buffer** | Buffer | yes | | + +**Returns**: A new valid instance of Identifier + +## .encodeCBOR(encoder) + +**Description**: Encode to CBOR + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **encoder** | Encoder | yes | | + +**Returns**: {boolean} + +## .toJSON() + +**Description**: Return Identity as JSON object + +**Parameters**: None. + +**Returns**: {RawIdentity} + +## .toBuffer() + +**Description**: Convert to Buffer + +**Parameters**: None. + +**Returns**: {Buffer} diff --git a/packages/js-dpp/docs/primitives/Identity.md b/packages/js-dpp/docs/primitives/Identity.md new file mode 100644 index 00000000000..1ccf43ec572 --- /dev/null +++ b/packages/js-dpp/docs/primitives/Identity.md @@ -0,0 +1,179 @@ +**Usage**: `new Identity(rawIdentity)` +**Description**: Instantiate a new Identity. + +**Parameters**: + +| parameters | type | required | Description | +|---------------------------------------|-----------------------|--------------------| --------------------------| +| **rawIdentity** | RawIdentity | yes | | +| **rawIdentity.id** | Buffer | yes | | +| **rawIdentity.protocolVersion** | number | yes | | +| **rawIdentity.publicKeys** | RawIdentityPublicKey[]| yes | | +| **rawIdentity.balance** | number | yes | | +| **rawIdentity.revision** | number | yes | | + +**Returns**: A new valid instance of Identity + +## .getProtocolVersion() + +**Description**: Get Identity protocol version + +**Parameters**: None. + +**Returns**: {number} + +## .getId() + +**Description**: Get Identity id + +**Parameters**: None. + +**Returns**: {Identifier} + +## .setPublicKeys(publicKeys) + +**Description**: Set Identity public keys + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **publicKeys** | RawIdentityPublicKey[] | yes | | + +**Returns**: {Identity} + +## .getPublicKeys() + +**Description**: Get Identity public keys revision + +**Parameters**: None. + +**Returns**: {IdentityPublicKey[]} + +## .getPublicKeyById(keyId) + +**Description**: Returns a public key for a given id + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **keyId** | number | yes | | + +**Returns**: {IdentityPublicKey} + +## .getBalance() + +**Description**: Returns balance + +**Parameters**: None. + +**Returns**: {number} + +## .setBalance(balance) + +**Description**: Set Identity balance + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|-----------|--------------------| -------------------------------- | +| **balance** | number | yes | | + +**Returns**: {Identity} + +## .increaseBalance(amount) + +**Description**: Increase Identity balance + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|--------|--------------------| -------------------------------- | +| **amount** | number | yes | | + +**Returns**: {Identity} + +## .reduceBalance(amount) + +**Description**: Reduce Identity balance + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|--------|--------------------| -------------------------------- | +| **amount** | number | yes | | + +**Returns**: {Identity} + +## .setAssetLock(assetLock) + +**Description**: Set Identity asset lock + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|-----------|--------------------| -------------------------------- | +| **assetLock** | AssetLock | yes | | + +**Returns**: {Identity} + +## .getAssetLock() + +**Description**: Returns Identity asset lock + +**Parameters**: None. + +**Returns**: {AssetLock} + +## .setRevision(revision) + +**Description**: Set Identity revision + +**Parameters**: + +| parameters | type | required | Description | +|--------------------|------------------------|--------------------| -------------------------------- | +| **revision** | number | yes | | + +**Returns**: {Identity} + +## .getRevision() + +**Description**: Get Identity revision + +**Parameters**: None. + +**Returns**: {number} + +## .toObject() + +**Description**: Return Identity as plain object + +**Parameters**: None. + +**Returns**: {Object} + +## .toJSON() + +**Description**: Return Identity as JSON object + +**Parameters**: None. + +**Returns**: {RawIdentity} + +## .toBuffer() + +**Description**: Return Identity as Buffer + +**Parameters**: None. + +**Returns**: {Buffer} + +## .hash() + +**Description**: Returns Identity hash + +**Parameters**: None. + +**Returns**: {Buffer} diff --git a/packages/js-dpp/docs/usage/DashPlatformProtocol.md b/packages/js-dpp/docs/usage/DashPlatformProtocol.md new file mode 100644 index 00000000000..e79cc2b7757 --- /dev/null +++ b/packages/js-dpp/docs/usage/DashPlatformProtocol.md @@ -0,0 +1,37 @@ +## new DashPlatformProtocol(options) + +**Description**: Instantiate DashPlatformProtocol. + +**Parameters**: + +| parameters | type | required | Description | +|-----------------------------------------------------------|--------------------|----------| -------------------------------------------------------| +| **options** | Object | no | | +| **options.stateRepository** | StateRepository | no | | +| **options.jsonSchemaValidator** | JsonSchemaValidator| no | | + +**Returns**: {DashPlatformProtocol} + +**Notes**: DPP will provide multiples facades: + +- [dpp.dataContract](dataContract.md) +- [dpp.document](document.md) +- [dpp.identity](identity.md) +- [dpp.stateTransition](stateTransition.md) + +## .getJsonSchemaValidator() + +**Description**: Return JSON Schema Validator + +**Parameters**: None + +**Returns**: {JsonSchemaValidator} + +## .getStateRepository() + +**Description**: Return State Repository + +**Parameters**: None + +**Returns**: {StateRepository} + diff --git a/packages/js-dpp/docs/usage/dataContract.md b/packages/js-dpp/docs/usage/dataContract.md new file mode 100644 index 00000000000..0ab267cf195 --- /dev/null +++ b/packages/js-dpp/docs/usage/dataContract.md @@ -0,0 +1,66 @@ +## dpp.dataContract.create(ownerId, documents) + +**Description**: Instantiate a new Data Contract. +This method will generate the entropy and dataContractId for the user. + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-----------------|-----------| -------------------------------------------------------| +| **ownerId** | Buffer | yes | | +| **documents** | Object | yes | | + +Returns : {[DataContract](../primitives/DataContract.md)} + +## dpp.dataContract.createFromObject(rawDataContract, options) + +**Description**: Instantiate a new Data Contract from plain object representation. +By default, the provided rawDataContract will be validated. + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-----------------|----------| --------------------------------------------------------| +| **rawDataContract** | RawDataContract | yes | | +| **options** | Object | no | | +| **options.skipValidation** | boolean[=false] | no | | + +Returns : {Promise<[DataContract](../primitives/DataContract.md)>} + +## dpp.dataContract.createFromBuffer(buffer, options) + +**Description**: Instantiate a new Data Contract from buffer. + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-----------------|----------| --------------------------------------------------------| +| **buffer** | Buffer | yes | | +| **options** | Object | no | | +| **options.skipValidation** | boolean[=false] | no | | + +Returns : {Promise<[DataContract](../primitives/DataContract.md)>} + +## dpp.dataContract.createDataContractCreateTransition(dataContract) + +**Description**: Create a new Data Contract State Transition + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-----------------|----------| --------------------------------------------------------| +| **dataContract** | DataContract | yes | | + +Returns : {DataContractCreateTransition} + +## dpp.dataContract.validate(dataContract) + +**Description**: Validate Data Contract + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|------------------------------|----------| --------------------------------------------------------| +| **dataContract** | DataContract/RawDataContract | yes | | + +Returns : {Promise} diff --git a/packages/js-dpp/docs/usage/document.md b/packages/js-dpp/docs/usage/document.md new file mode 100644 index 00000000000..ba28ece0c4a --- /dev/null +++ b/packages/js-dpp/docs/usage/document.md @@ -0,0 +1,73 @@ +## dpp.document.create(dataContract, ownerId, type, data = {}) + +**Description**: Instantiate a new Document for a specific contract, owner, type. +This method will populate it with specified data and validate upon creation. + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-----------------|-----------| -------------------------------------------------------| +| **dataContract** | DataContract | yes | | +| **ownerId** | Buffer | yes | | +| **type** | string | yes | | +| **data** | Object[={}] | no | | + +Returns : {[Document](../primitives/Document.md)} + +## dpp.document.createFromObject(rawDocument, options) + +**Description**: Instantiate a new Document from plain object representation. +By default, the provided Document will be validated. + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-----------------|----------| --------------------------------------------------------| +| **rawDocument** | RawDocument | yes | | +| **options** | Object | no | | +| **options.skipValidation** | boolean[=false] | no | | +| **options.action** | boolean | no | | + +Returns : {Promise<[Document](../primitives/Document.md)>} + +## dpp.document.createFromBuffer(buffer, options) + +**Description**: Instantiate a new Document from buffer. + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-----------------|----------| --------------------------------------------------------| +| **buffer** | Buffer | yes | | +| **options** | Object | no | | +| **options.skipValidation** | boolean[=false] | no | | +| **options.action** | boolean | no | | + +Returns : {Promise<[Document](../primitives/Document.md)>} + +## dpp.document.createStateTransition(documents) + +**Description**: Create Documents State Transition + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-----------------|----------| --------------------------------------------------------| +| **documents** | Object | yes | | +| **documents.create** | Document[] | no | | +| **documents.replace** | Document[] | no | | +| **documents.delete** | Document[] | no | | + +Returns : {DocumentsBatchTransition} + +## dpp.document.validate(document) + +**Description**: Validate document + +**Parameters**: + +| parameters | type | required | Description | +|-----------------|----------------------|----------| --------------------------------------------------------| +| **document** | Document/RawDocument | yes | | + +Returns : {Promise} diff --git a/packages/js-dpp/docs/usage/identity.md b/packages/js-dpp/docs/usage/identity.md new file mode 100644 index 00000000000..b71804eeb44 --- /dev/null +++ b/packages/js-dpp/docs/usage/identity.md @@ -0,0 +1,91 @@ +## dpp.identity.create(lockedOutPoint, publicKeys = []) + +**Description**: Instantiate a new Identity. + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|------------------------|-----------| -------------------------------------------------| +| **assetLockTransaction** | Transaction | yes | | +| **outputIndex** | number | yes | | +| **assetLockProof** | InstantAssetLockProof | yes | | +| **publicKeys** | PublicKey[] | yes | | + +Returns : {[Identity](../primitives/Identity.md)} + +## dpp.identity.createFromObject(rawIdentity, options) + +**Description**: Instantiate a new Identity from plain object representation. + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-----------------|----------| --------------------------------------------------------| +| **rawIdentity** | RawIdentity | yes | | +| **options** | Object | no | | +| **options.skipValidation** | boolean[=false] | no | | + +Returns : {[Identity](../primitives/Identity.md)} + +## dpp.identity.createFromBuffer(buffer, options) + +**Description**: Instantiate a new Identity from buffer. + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-----------------|----------| --------------------------------------------------------| +| **buffer** | Buffer | yes | | +| **options** | Object | no | | +| **options.skipValidation** | boolean[=false] | no | | + +Returns : {[Identity](../primitives/Identity.md)} + +## dpp.identity.validate(identity) + +**Description**: Validate Identity + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|------------------------------|----------| --------------------------------------------------------| +| **identity** | Identity/RawIdentity | yes | | + +Returns : {ValidationResult} + +## dpp.identity.createInstantAssetLockProof(instantLock) + +**Description**: Create a instant asset lock proof Identity. + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|------------------------|-----------| -------------------------------------------------| +| **instantLock** | InstantLock | yes | | + +Returns : {InstantAssetLookProof} + +## dpp.identity.createIdentityCreateTransition(identity) + +**Description**: Create Identity Create Transition + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-----------------|----------| --------------------------------------------------------| +| **identity** | Identity | yes | | + +Returns : {IdentityCreateTransition} + +## dpp.identity.createIdentityTopUpTransition(identityId, lockedOutPoint) + +**Description**: Create Identity Create Transition + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|--------------------------|----------| --------------------------------------------------------| +| **identityId** | Identifier/Buffer/String | yes | identity id to top up | +| **lockedOutPoint** | Buffer | yes | outpoint of the top up output of the L1 transaction | + +Returns : {IdentityTopUpTransition} diff --git a/packages/js-dpp/docs/usage/stateTransition.md b/packages/js-dpp/docs/usage/stateTransition.md new file mode 100644 index 00000000000..fb190f574d3 --- /dev/null +++ b/packages/js-dpp/docs/usage/stateTransition.md @@ -0,0 +1,113 @@ +## dpp.stateTransition.createFromJSON(rawStateTransition, options) + +**Description**: Create State Transition from JSON. + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-------------------------------------------------------------|----------| --------------------------------------------------------| +| **rawStateTransition** | RawDataContractCreateTransition/RawDocumentsBatchTransition | yes | | +| **options** | Object | no | | +| **options.skipValidation** | boolean[=false] | no | | + +Returns : {RawDataContractCreateTransition|DocumentsBatchTransition} + +# dpp.stateTransition.createFromObject(rawStateTransition, options) + +**Description**: Create State Transition from a plain object. + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-------------------------------------------------------------|----------| --------------------------------------------------------| +| **rawStateTransition** | RawDataContractCreateTransition/RawDocumentsBatchTransition | yes | | +| **options** | Object | no | | +| **options.skipValidation** | boolean[=false] | no | | + +Returns : {RawDataContractCreateTransition|DocumentsBatchTransition} + +## dpp.stateTransition.createFromBuffer(buffer, options) + +**Description**: Create State Transition from buffer. + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-----------------|----------| --------------------------------------------------------| +| **buffer** | Buffer | yes | | +| **options** | Object | no | | +| **options.skipValidation** | boolean[=false] | no | | + +Returns : {RawDataContractCreateTransition|DocumentsBatchTransition} + +## dpp.stateTransition.validate(stateTransition) + +**Description**: Validate State Transition + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-------------------------------------------|----------| --------------------------------------------------------| +| **stateTransition** | RawStateTransition/AbstractStateTransition| yes | | + +Returns : {ValidationResult} + +## dpp.stateTransition.validateBasic(stateTransition) + +**Description**: Validate State Transition structure and data + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|--------------------------------------------|----------| --------------------------------------------------------| +| **stateTransition** | AbstractStateTransition/RawStateTransition | yes | | + +Returns : {ValidationResult} + +## dpp.stateTransition.validateSignature(stateTransition) + +**Description**: Validate State Transition signature and ownership + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|---------------------------|----------| --------------------------------------------------------| +| **stateTransition** | AbstractStateTransition | yes | | + +Returns : {ValidationResult} + +## dpp.stateTransition.validateFee(stateTransition) + +**Description**: Validate State Transition fee + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-------------------------|----------| --------------------------------------------------------| +| **stateTransition** | AbstractStateTransition | yes | | + +Returns : {ValidationResult} + +## dpp.stateTransition.validateState(stateTransition) + +**Description**: Validate State Transition against existing state + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|---------------------------|----------| --------------------------------------------------------| +| **stateTransition** | AbstractStateTransition | yes | | + +Returns : {ValidationResult} + +## dpp.stateTransition.apply(stateTransition) + +**Description**: Apply state transition to the state + +**Parameters**: + +| parameters | type | required | Description | +|------------------------------|-------------------------|----------| -------------------------------------------------| +| **stateTransition** | AbstractStateTransition | yes | | + +Returns : {Promise} diff --git a/packages/js-dpp/karma.conf.js b/packages/js-dpp/karma.conf.js new file mode 100644 index 00000000000..9689ac1bc05 --- /dev/null +++ b/packages/js-dpp/karma.conf.js @@ -0,0 +1,53 @@ +const karmaMocha = require('karma-mocha'); +const karmaMochaReporter = require('karma-mocha-reporter'); +const karmaChai = require('karma-chai'); +const karmaChromeLauncher = require('karma-chrome-launcher'); +const karmaFirefoxLauncher = require('karma-firefox-launcher'); +const karmaWebpack = require('karma-webpack'); + +const webpackConfig = require('./webpack.config'); + +module.exports = (config) => { + config.set({ + frameworks: ['mocha', 'chai', 'webpack'], + files: [ + 'lib/test/karma/loader.js', + ], + exclude: [ + ], + preprocessors: { + 'lib/test/karma/loader.js': ['webpack'], + }, + webpack: { + mode: 'development', + optimization: { + minimize: false, + moduleIds: 'named', + }, + plugins: webpackConfig[0].plugins, + resolve: webpackConfig[0].resolve, + }, + reporters: ['mocha'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: false, + browsers: ['ChromeHeadless', 'FirefoxHeadless'], + singleRun: false, + concurrency: Infinity, + plugins: [ + karmaMocha, + karmaMochaReporter, + karmaChai, + karmaChromeLauncher, + karmaFirefoxLauncher, + karmaWebpack, + ], + customLaunchers: { + FirefoxHeadless: { + base: 'Firefox', + flags: ['-headless'], + }, + }, + }); +}; diff --git a/packages/js-dpp/lib/DashPlatformProtocol.js b/packages/js-dpp/lib/DashPlatformProtocol.js new file mode 100644 index 00000000000..5d1e6af2fb9 --- /dev/null +++ b/packages/js-dpp/lib/DashPlatformProtocol.js @@ -0,0 +1,120 @@ +const { getRE2Class } = require('@dashevo/wasm-re2'); +const BlsSignatures = require('./bls/bls'); +const createAjv = require('./ajv/createAjv'); + +const protocolVersion = require('./version/protocolVersion'); + +const JsonSchemaValidator = require('./validation/JsonSchemaValidator'); + +const DataContractFacade = require('./dataContract/DataContractFacade'); +const DocumentFacade = require('./document/DocumentFacade'); +const StateTransitionFacade = require('./stateTransition/StateTransitionFacade'); + +const IdentityFacade = require('./identity/IdentityFacade'); + +/** + * @class DashPlatformProtocol + */ +class DashPlatformProtocol { + /** + * @param {Object} options + * @param {StateRepository} [options.stateRepository] + * @param {JsonSchemaValidator} [options.jsonSchemaValidator] + * @param {number} [options.protocolVersion] + */ + constructor(options = {}) { + this.options = options; + + this.protocolVersion = this.options.protocolVersion !== undefined + ? this.options.protocolVersion + : protocolVersion.latestVersion; + + this.stateRepository = undefined; + this.jsonSchemaValidator = undefined; + this.initialized = undefined; + } + + /** + * Initialize + * + * @return {Promise} + */ + async initialize() { + if (this.initialized) { + return this.initialized; + } + + const bls = await BlsSignatures.getInstance(); + + this.initialized = getRE2Class().then((RE2) => { + this.stateRepository = this.options.stateRepository; + + this.jsonSchemaValidator = this.options.jsonSchemaValidator; + if (this.jsonSchemaValidator === undefined) { + const ajv = createAjv(RE2); + + this.jsonSchemaValidator = new JsonSchemaValidator(ajv); + } + + this.dataContract = new DataContractFacade( + this, + RE2, + ); + + this.document = new DocumentFacade( + this, + ); + + this.stateTransition = new StateTransitionFacade( + this, + RE2, + bls, + ); + + this.identity = new IdentityFacade( + this, + bls, + ); + + return true; + }); + + return this.initialized; + } + + /** + * @return {JsonSchemaValidator} + */ + getJsonSchemaValidator() { + return this.jsonSchemaValidator; + } + + /** + * Get State Repository + * + * @return {StateRepository} + */ + getStateRepository() { + return this.stateRepository; + } + + /** + * Get protocol version + * + * @return {number} + */ + getProtocolVersion() { + return this.protocolVersion; + } + + /** + * Set protocol version + * + * @param {number} version + */ + setProtocolVersion(version) { + this.protocolVersion = version; + } +} + +module.exports = DashPlatformProtocol; diff --git a/packages/js-dpp/lib/Identifier.js b/packages/js-dpp/lib/Identifier.js new file mode 100644 index 00000000000..038192896da --- /dev/null +++ b/packages/js-dpp/lib/Identifier.js @@ -0,0 +1,4 @@ +// Alias for compatibility with other libraries that used this module by path +// The path is deprecated and will be removed later + +module.exports = require('./identifier/Identifier'); diff --git a/packages/js-dpp/lib/Metadata.js b/packages/js-dpp/lib/Metadata.js new file mode 100644 index 00000000000..ac2b633f661 --- /dev/null +++ b/packages/js-dpp/lib/Metadata.js @@ -0,0 +1,29 @@ +class Metadata { + /** + * @param {Object} rawMetadata + * @param {number} rawMetadata.blockHeight + * @param {number} rawMetadata.coreChainLockedHeight + */ + constructor(rawMetadata) { + this.blockHeight = rawMetadata.blockHeight; + this.coreChainLockedHeight = rawMetadata.coreChainLockedHeight; + } + + /** + * Get block height + * @returns {number} + */ + getBlockHeight() { + return this.blockHeight; + } + + /** + * Get core chain-locked height + * @returns {number} + */ + getCoreChainLockedHeight() { + return this.coreChainLockedHeight; + } +} + +module.exports = Metadata; diff --git a/packages/js-dpp/lib/StateRepositoryInterface.js b/packages/js-dpp/lib/StateRepositoryInterface.js new file mode 100644 index 00000000000..4d133da7e59 --- /dev/null +++ b/packages/js-dpp/lib/StateRepositoryInterface.js @@ -0,0 +1,192 @@ +/** + * @interface StateRepository + * @classdesc StateRepository interface definition + */ + +/** + * Fetch Data Contract by ID + * + * @async + * @method + * @name StateRepository#fetchDataContract + * @param {Identifier} id + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Store Data Contract + * + * @async + * @method + * @name StateRepository#storeDataContract + * @param {DataContract} dataContract + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Fetch Documents by Data Contract ID and type + * + * @async + * @method + * @name StateRepository#fetchDocuments + * @param {Identifier} contractId + * @param {string} type + * @param {{ where: Object }} options + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Create document + * + * @async + * @method + * @name StateRepository#createDocument + * @param {Document} document + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Update document + * + * @async + * @method + * @name StateRepository#updateDocument + * @param {Document} document + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Remove document + * + * @async + * @method + * @name StateRepository#removeDocument + * @param {DataContract} dataContract + * @param {string} type + * @param {Identifier} id + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Fetch transaction by ID + * + * @async + * @method + * @name StateRepository#fetchTransaction + * @param {string} id + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Fetch identity by ID + * + * @async + * @method + * @name StateRepository#fetchIdentity + * @param {Identifier} id + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Store identity + * + * @async + * @method + * @name StateRepository#createIdentity + * @param {Identity} identity + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Store identity + * + * @async + * @method + * @name StateRepository#updateIdentity + * @param {Identity} identity + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Store public keys hashes and identity id pair + * + * @async + * @method + * @name StateRepository#storeIdentityPublicKeyHashes + * @param {Identifier} identityId + * @param {Buffer[]} publicKeyHashes + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Fetch identity ids by public keys hashes + * + * @async + * @method + * @name StateRepository#fetchIdentityIdsByPublicKeyHashes + * @param {Buffer[]} publicKeyHashes + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise>} + */ + +/** + * Fetch latest platform block header + * + * @async + * @method + * @name StateRepository#fetchLatestPlatformBlockHeader + * @returns {Promise} + */ + +/** + * Verify Instant Lock + * + * @async + * @method + * @name StateRepository#verifyInstantLock + * @param {InstantLock} instantLock + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Check if AssetLock Transaction outPoint exists in spent list + * + * @async + * @method + * @name StateRepository#isAssetLockTransactionOutPointAlreadyUsed + * @param {Buffer} outPointBuffer + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Store AssetLock Transaction outPoint in spent list + * + * @async + * @method + * @name StateRepository#markAssetLockTransactionOutPointAsUsed + * @param {Buffer} outPointBuffer + * @param {StateTransitionExecutionContext} [StateTransitionExecutionContext] + * @returns {Promise} + */ + +/** + * Fetch Simplified Masternode List Store + * + * @async + * @method + * @name StateRepository#fetchSMLStore + * @returns {Promise} + */ diff --git a/packages/js-dpp/lib/ajv/createAjv.js b/packages/js-dpp/lib/ajv/createAjv.js new file mode 100644 index 00000000000..6096530f55d --- /dev/null +++ b/packages/js-dpp/lib/ajv/createAjv.js @@ -0,0 +1,31 @@ +const { default: Ajv } = require('ajv/dist/2020'); + +const addFormats = require('ajv-formats'); + +const addByteArrayKeyword = require('./keywords/byteArray/addByteArrayKeyword'); + +const injectRE2 = require('./injectRE2'); + +/** + * @param {Function} RE2 + * @return {Ajv2020} + */ +function createAjv(RE2) { + injectRE2(RE2); + + const ajv = new Ajv({ + strictTypes: true, + strictTuples: true, + strictRequired: true, + addUsedSchema: false, + strict: true, + }); + + addFormats(ajv, { mode: 'fast' }); + + addByteArrayKeyword(ajv); + + return ajv; +} + +module.exports = createAjv; diff --git a/packages/js-dpp/lib/ajv/injectRE2.js b/packages/js-dpp/lib/ajv/injectRE2.js new file mode 100644 index 00000000000..3454106b213 --- /dev/null +++ b/packages/js-dpp/lib/ajv/injectRE2.js @@ -0,0 +1,19 @@ +const codegen = require('ajv/dist/compile/codegen'); +const code = require('ajv/dist/vocabularies/code'); + +/** + * @param {Function} RE2 + */ +function injectRE2(RE2) { + global.RE2 = RE2; + + code.usePattern = function usePattern({ gen }, pattern) { + return gen.scopeValue('pattern', { + key: pattern, + ref: new RE2(pattern, 'u'), + code: codegen._`new RE2(${pattern}, "u")`, + }); + }; +} + +module.exports = injectRE2; diff --git a/packages/js-dpp/lib/ajv/keywords/byteArray/addByteArrayKeyword.js b/packages/js-dpp/lib/ajv/keywords/byteArray/addByteArrayKeyword.js new file mode 100644 index 00000000000..40f1a7a22ac --- /dev/null +++ b/packages/js-dpp/lib/ajv/keywords/byteArray/addByteArrayKeyword.js @@ -0,0 +1,10 @@ +const byteArray = require('./byteArray'); + +/** + * @param {Ajv2020} ajv + */ +function addByteArrayKeyword(ajv) { + ajv.addKeyword(byteArray); +} + +module.exports = addByteArrayKeyword; diff --git a/packages/js-dpp/lib/ajv/keywords/byteArray/byteArray.js b/packages/js-dpp/lib/ajv/keywords/byteArray/byteArray.js new file mode 100644 index 00000000000..29c6192e25e --- /dev/null +++ b/packages/js-dpp/lib/ajv/keywords/byteArray/byteArray.js @@ -0,0 +1,29 @@ +const byteArray = { + keyword: 'byteArray', + type: ['array'], + schemaType: [], + macro(schema, parentSchema) { + if (parentSchema.items) { + throw new Error("'byteArray' should not be used with 'items'"); + } + + if (parentSchema.prefixItems) { + throw new Error("'byteArray' should not be used with 'prefixItems'"); + } + + return { + items: { + type: 'integer', + minimum: 0, + maximum: 255, + }, + }; + }, + errors: false, + metaSchema: { + type: 'boolean', + const: true, + }, +}; + +module.exports = byteArray; diff --git a/packages/js-dpp/lib/blockTimeWindow/ValidationResult.js b/packages/js-dpp/lib/blockTimeWindow/ValidationResult.js new file mode 100644 index 00000000000..651929a2367 --- /dev/null +++ b/packages/js-dpp/lib/blockTimeWindow/ValidationResult.js @@ -0,0 +1,39 @@ +class ValidationResult { + /** + * + * @param {boolean} isValid + * @param {Date} timeWindowStart + * @param {Date} timeWindowEnd + */ + constructor(isValid, timeWindowStart, timeWindowEnd) { + this.valid = isValid; + this.timeWindowStart = timeWindowStart; + this.timeWindowEnd = timeWindowEnd; + } + + /** + * + * @returns {Date} + */ + getTimeWindowStart() { + return this.timeWindowStart; + } + + /** + * + * @returns {Date} + */ + getTimeWindowEnd() { + return this.timeWindowEnd; + } + + /** + * + * @returns {boolean} + */ + isValid() { + return this.valid; + } +} + +module.exports = ValidationResult; diff --git a/packages/js-dpp/lib/blockTimeWindow/validateTimeInBlockTimeWindow.js b/packages/js-dpp/lib/blockTimeWindow/validateTimeInBlockTimeWindow.js new file mode 100644 index 00000000000..a21215fc68e --- /dev/null +++ b/packages/js-dpp/lib/blockTimeWindow/validateTimeInBlockTimeWindow.js @@ -0,0 +1,29 @@ +const ValidationResult = require('./ValidationResult'); + +const BLOCK_TIME_WINDOW_MINUTES = 5; + +/** + * + * @param {number} lastBlockHeaderTime + * @param {number} timeToCheck + * @returns {ValidationResult} + */ +function validateTimeInBlockTimeWindow(lastBlockHeaderTime, timeToCheck) { +// Define time window + const timeWindowStart = new Date(lastBlockHeaderTime); + timeWindowStart.setMinutes( + timeWindowStart.getMinutes() - BLOCK_TIME_WINDOW_MINUTES, + ); + + const timeWindowEnd = new Date(lastBlockHeaderTime); + timeWindowEnd.setMinutes( + timeWindowEnd.getMinutes() + BLOCK_TIME_WINDOW_MINUTES, + ); + + const isValid = timeToCheck >= timeWindowStart.getTime() + && timeToCheck <= timeWindowEnd.getTime(); + + return new ValidationResult(isValid, timeWindowStart, timeWindowEnd); +} + +module.exports = validateTimeInBlockTimeWindow; diff --git a/packages/js-dpp/lib/bls/bls.js b/packages/js-dpp/lib/bls/bls.js new file mode 100644 index 00000000000..e799ee5aacc --- /dev/null +++ b/packages/js-dpp/lib/bls/bls.js @@ -0,0 +1,48 @@ +const EventEmitter = require('events'); +const BlsSignatures = require('bls-signatures'); + +const eventNames = { + LOADED_EVENT: 'LOADED', +}; + +const events = new EventEmitter(); +let isLoading = false; +let instance = null; + +function compileWasmModule() { + isLoading = true; + return BlsSignatures().then((loadedInstance) => { + instance = loadedInstance; + isLoading = false; + events.emit(eventNames.LOADED_EVENT); + }); +} + +const bls = { + /** + * Compiles BlsSignature instance if it wasn't compiled yet + * and returns module instance + * @return {Promise} + */ + getInstance() { + return new Promise((resolve) => { + if (instance) { + resolve(instance); + + return; + } + + if (isLoading) { + events.once(eventNames.LOADED_EVENT, () => { + resolve(instance); + }); + } else { + compileWasmModule().then(() => { + resolve(instance); + }); + } + }); + }, +}; + +module.exports = bls; diff --git a/packages/js-dpp/lib/bls/blsPrivateKeyFactory.js b/packages/js-dpp/lib/bls/blsPrivateKeyFactory.js new file mode 100644 index 00000000000..d1976a5d860 --- /dev/null +++ b/packages/js-dpp/lib/bls/blsPrivateKeyFactory.js @@ -0,0 +1,29 @@ +const BlsSignatures = require('./bls'); + +/** + * Create an instance of BlsPrivateKey + * + * @param {string|Buffer|Uint8Array|PrivateKey} privateKey string must be hex + * @returns {Promise} + */ +async function blsPrivateKeyFactory(privateKey) { + const blsSignatures = await BlsSignatures.getInstance(); + const { PrivateKey: BlsPrivateKey } = blsSignatures; + + let bytes; + + if (typeof privateKey === 'string') { + const buf = Buffer.from(privateKey, 'hex'); + bytes = new Uint8Array(buf); + } else if (Buffer.isBuffer(privateKey)) { + bytes = new Uint8Array(privateKey); + } else if (privateKey instanceof BlsPrivateKey) { + return privateKey; + } else { + bytes = privateKey; + } + + return BlsPrivateKey.fromBytes(bytes, true); +} + +module.exports = blsPrivateKeyFactory; diff --git a/packages/js-dpp/lib/bls/blsPublicKeyFactory.js b/packages/js-dpp/lib/bls/blsPublicKeyFactory.js new file mode 100644 index 00000000000..7362b625671 --- /dev/null +++ b/packages/js-dpp/lib/bls/blsPublicKeyFactory.js @@ -0,0 +1,29 @@ +const BlsSignatures = require('./bls'); + +/** + * Create an instance of BlsPrivateKey + * + * @param {string|Buffer|Uint8Array|PrivateKey} publicKey string must be hex + * @returns {Promise} + */ +async function blsPublicKeyFactory(publicKey) { + const blsSignatures = await BlsSignatures.getInstance(); + const { PublicKey } = blsSignatures; + + let bytes; + + if (typeof publicKey === 'string') { + const buf = Buffer.from(publicKey, 'hex'); + bytes = new Uint8Array(buf); + } else if (Buffer.isBuffer(publicKey)) { + bytes = new Uint8Array(publicKey); + } else if (publicKey instanceof PublicKey) { + return publicKey; + } else { + bytes = publicKey; + } + + return PublicKey.fromBytes(bytes); +} + +module.exports = blsPublicKeyFactory; diff --git a/packages/js-dpp/lib/dataContract/DataContract.js b/packages/js-dpp/lib/dataContract/DataContract.js new file mode 100644 index 00000000000..c9eb36ef47d --- /dev/null +++ b/packages/js-dpp/lib/dataContract/DataContract.js @@ -0,0 +1,355 @@ +const hashModule = require('../util/hash'); +const serializer = require('../util/serializer'); + +const getBinaryPropertiesFromSchemaModule = require('./getBinaryPropertiesFromSchema'); + +const InvalidDocumentTypeError = require('../errors/InvalidDocumentTypeError'); +const Identifier = require('../identifier/Identifier'); + +class DataContract { + /** + * @param {RawDataContract} rawDataContract + */ + constructor(rawDataContract) { + this.protocolVersion = rawDataContract.protocolVersion; + + this.id = Identifier.from(rawDataContract.$id); + this.ownerId = Identifier.from(rawDataContract.ownerId); + + this.setVersion(rawDataContract.version); + this.setJsonMetaSchema(rawDataContract.$schema); + this.setDocuments(rawDataContract.documents); + this.setDefinitions(rawDataContract.$defs); + + this.binaryProperties = {}; + } + + /** + * Get Data Contract protocol version + * + * @returns {number} + */ + getProtocolVersion() { + return this.protocolVersion; + } + + /** + * Get ID + * + * @return {Identifier} + */ + getId() { + return this.id; + } + + /** + * Get owner id + * + * @return {Identifier} + */ + getOwnerId() { + return this.ownerId; + } + + /** + * Get version + * @returns {number} + */ + getVersion() { + return this.version; + } + + /** + * Set version + * @param {number} version + */ + setVersion(version) { + this.version = version; + } + + /** + * Increment version by 1 + */ + incrementVersion() { + this.version += 1; + } + + /** + * Get JSON Schema ID + * + * @return {string} + */ + getJsonSchemaId() { + return this.getId().toString(); + } + + /** + * + * @param {string} schema + */ + setJsonMetaSchema(schema) { + this.schema = schema; + + return this; + } + + /** + * + * @return {string} + */ + getJsonMetaSchema() { + return this.schema; + } + + /** + * + * @param {Object} documents + * @return {DataContract} + */ + setDocuments(documents) { + this.documents = documents; + + return this; + } + + /** + * + * @return {Object} + */ + getDocuments() { + return this.documents; + } + + /** + * Returns true if document type is defined + * + * @param {string} type + * @return {boolean} + */ + isDocumentDefined(type) { + return Object.prototype.hasOwnProperty.call(this.documents, type); + } + + /** + * + * @param {string} type + * @param {object} schema + * @return {DataContract} + */ + setDocumentSchema(type, schema) { + this.documents[type] = schema; + + return this; + } + + /** + * + * @param {string} type + * @return {Object} + */ + getDocumentSchema(type) { + if (!this.isDocumentDefined(type)) { + throw new InvalidDocumentTypeError(type, this); + } + + return this.documents[type]; + } + + /** + * @param {string} type + * @return {{$ref: string}} + */ + getDocumentSchemaRef(type) { + if (!this.isDocumentDefined(type)) { + throw new InvalidDocumentTypeError(type, this); + } + + return { $ref: `${this.getJsonSchemaId()}#/documents/${type}` }; + } + + /** + * @param {Object} $defs + * @return {DataContract} + */ + setDefinitions($defs) { + this.$defs = $defs; + + return this; + } + + /** + * @return {Object} + */ + getDefinitions() { + return this.$defs; + } + + /** + * Set Data Contract entropy + * + * @param {Buffer} entropy + * @return {DataContract} + */ + setEntropy(entropy) { + this.entropy = entropy; + + return this; + } + + /** + * Get Data Contract entropy + * + * @return {Buffer} + */ + getEntropy() { + return this.entropy; + } + + /** + * Get properties with `contentEncoding` constraint + * + * @param {string} type + * + * @return {Object} + */ + getBinaryProperties(type) { + if (!this.isDocumentDefined(type)) { + throw new InvalidDocumentTypeError(type, this); + } + + if (this.binaryProperties[type]) { + return this.binaryProperties[type]; + } + + const { getBinaryPropertiesFromSchema } = getBinaryPropertiesFromSchemaModule; + + this.binaryProperties[type] = getBinaryPropertiesFromSchema( + this.documents[type], + ); + + return this.binaryProperties[type]; + } + + /** + * Set metadata + * @param {Metadata} metadata + */ + setMetadata(metadata) { + this.metadata = metadata; + } + + /** + * Get metadata + * @returns {Metadata|null} + */ + getMetadata() { + return this.metadata; + } + + /** + * Return Data Contract as plain object + * + * @param {Object} [options] + * @param {boolean} [options.skipIdentifiersConversion=false] + * + * @return {RawDataContract} + */ + toObject(options = {}) { + Object.assign( + options, + { + skipIdentifiersConversion: false, + ...options, + }, + ); + + const rawDataContract = { + protocolVersion: this.getProtocolVersion(), + $id: this.getId(), + $schema: this.getJsonMetaSchema(), + version: this.getVersion(), + ownerId: this.getOwnerId(), + documents: this.getDocuments(), + }; + + if (!options.skipIdentifiersConversion) { + rawDataContract.$id = this.getId().toBuffer(); + rawDataContract.ownerId = this.getOwnerId().toBuffer(); + } + + const $defs = this.getDefinitions(); + + if ($defs && Object.getOwnPropertyNames($defs).length) { + rawDataContract.$defs = $defs; + } + + return rawDataContract; + } + + /** + * Return Data Contract as JSON object + * + * @return {JsonDataContract} + */ + toJSON() { + return { + ...this.toObject({ skipIdentifiersConversion: true }), + $id: this.getId().toString(), + ownerId: this.getOwnerId().toString(), + }; + } + + /** + * Return Data Contract as a Buffer + * + * @returns {Buffer} + */ + toBuffer() { + const serializedData = this.toObject(); + delete serializedData.protocolVersion; + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(this.getProtocolVersion(), 0); + + return Buffer.concat([protocolVersionUInt32, serializer.encode(serializedData)]); + } + + /** + * Returns hex string with Data Contract hash + * + * @return {Buffer} + */ + hash() { + const { hash } = hashModule; + + return hash(this.toBuffer()); + } +} + +/** + * @typedef {Object} RawDataContract + * @property {number} protocolVersion + * @property {Buffer} $id + * @property {string} $schema + * @property {number} version + * @property {Buffer} ownerId + * @property {Object} documents + * @property {Object} [$defs] + */ + +/** + * @typedef {Object} JsonDataContract + * @property {number} protocolVersion + * @property {string} $id + * @property {string} $schema + * @property {number} version + * @property {string} ownerId + * @property {Object} documents + * @property {Object} [$defs] + */ + +DataContract.DEFAULTS = { + SCHEMA: 'https://schema.dash.org/dpp-0-4-0/meta/data-contract', +}; + +module.exports = DataContract; diff --git a/packages/js-dpp/lib/dataContract/DataContractFacade.js b/packages/js-dpp/lib/dataContract/DataContractFacade.js new file mode 100644 index 00000000000..9811bd8ad7d --- /dev/null +++ b/packages/js-dpp/lib/dataContract/DataContractFacade.js @@ -0,0 +1,121 @@ +const $RefParser = require('@apidevtools/json-schema-ref-parser'); + +const DataContract = require('./DataContract'); +const DataContractFactory = require('./DataContractFactory'); +const validateDataContractFactory = require('./validation/validateDataContractFactory'); +const enrichDataContractWithBaseSchema = require('./enrichDataContractWithBaseSchema'); +const validateDataContractMaxDepthFactory = require('./validation/validateDataContractMaxDepthFactory'); +const validateDataContractPatternsFactory = require('./validation/validateDataContractPatternsFactory'); +const decodeProtocolEntityFactory = require('../decodeProtocolEntityFactory'); + +const protocolVersion = require('../version/protocolVersion'); +const validateProtocolVersionFactory = require('../version/validateProtocolVersionFactory'); +const getPropertyDefinitionByPath = require('./getPropertyDefinitionByPath'); + +class DataContractFacade { + /** + * @param {DashPlatformProtocol} dpp + * @param {RE2} RE2 + */ + constructor(dpp, RE2) { + const validateDataContractMaxDepth = validateDataContractMaxDepthFactory($RefParser); + + const validateDataContractPatterns = validateDataContractPatternsFactory(RE2); + + const validateProtocolVersion = validateProtocolVersionFactory( + dpp, + protocolVersion.compatibility, + ); + + this.validateDataContract = validateDataContractFactory( + dpp.getJsonSchemaValidator(), + validateDataContractMaxDepth, + enrichDataContractWithBaseSchema, + validateDataContractPatterns, + validateProtocolVersion, + getPropertyDefinitionByPath, + ); + + const decodeProtocolEntity = decodeProtocolEntityFactory(); + + this.factory = new DataContractFactory( + dpp, + this.validateDataContract, + decodeProtocolEntity, + ); + } + + /** + * Create Data Contract + * + * @param {Identifier|Buffer} ownerId + * @param {Object} documents + * @return {DataContract} + */ + create(ownerId, documents) { + return this.factory.create(ownerId, documents); + } + + /** + * Create Data Contract from plain object + * + * @param {RawDataContract} rawDataContract + * @param {Object} options + * @param {boolean} [options.skipValidation=false] + * @return {Promise} + */ + async createFromObject(rawDataContract, options = { }) { + return this.factory.createFromObject(rawDataContract, options); + } + + /** + * Create Data Contract from buffer + * + * @param {Buffer} buffer + * @param {Object} options + * @param {boolean} [options.skipValidation=false] + * @return {Promise} + */ + async createFromBuffer(buffer, options = { }) { + return this.factory.createFromBuffer(buffer, options); + } + + /** + * Create Data Contract Create State Transition + * + * @param {DataContract} dataContract + * @return {DataContractCreateTransition} + */ + createDataContractCreateTransition(dataContract) { + return this.factory.createDataContractCreateTransition(dataContract); + } + + /** + * Create Data Contract Update State Transition + * + * @param {DataContract} dataContract + * @return {DataContractUpdateTransition} + */ + createDataContractUpdateTransition(dataContract) { + return this.factory.createDataContractUpdateTransition(dataContract); + } + + /** + * Validate Data Contract + * + * @param {DataContract|RawDataContract} dataContract + * @return {Promise} + */ + async validate(dataContract) { + let rawDataContract; + if (dataContract instanceof DataContract) { + rawDataContract = dataContract.toObject(); + } else { + rawDataContract = dataContract; + } + + return this.validateDataContract(rawDataContract); + } +} + +module.exports = DataContractFacade; diff --git a/packages/js-dpp/lib/dataContract/DataContractFactory.js b/packages/js-dpp/lib/dataContract/DataContractFactory.js new file mode 100644 index 00000000000..5a0c1bc8bdd --- /dev/null +++ b/packages/js-dpp/lib/dataContract/DataContractFactory.js @@ -0,0 +1,131 @@ +const InvalidDataContractError = require('./errors/InvalidDataContractError'); + +const DataContract = require('./DataContract'); +const generateDataContractId = require('./generateDataContractId'); + +const DataContractCreateTransition = require('./stateTransition/DataContractCreateTransition/DataContractCreateTransition'); + +const entropyGenerator = require('../util/entropyGenerator'); +const AbstractConsensusError = require('../errors/consensus/AbstractConsensusError'); +const DataContractUpdateTransition = require('./stateTransition/DataContractUpdateTransition/DataContractUpdateTransition'); + +class DataContractFactory { + /** + * @param {DashPlatformProtocol} dpp + * @param {validateDataContract} validateDataContract + * @param {decodeProtocolEntity} decodeProtocolEntity + */ + constructor(dpp, validateDataContract, decodeProtocolEntity) { + this.dpp = dpp; + this.validateDataContract = validateDataContract; + this.decodeProtocolEntity = decodeProtocolEntity; + } + + /** + * Create Data Contract + * + * @param {Identifier|Buffer} ownerId + * @param {Object} documents + * @return {DataContract} + */ + create(ownerId, documents) { + const { generate } = entropyGenerator; + const dataContractEntropy = generate(); + + const dataContractId = generateDataContractId(ownerId, dataContractEntropy); + + const dataContract = new DataContract({ + protocolVersion: this.dpp.getProtocolVersion(), + $schema: DataContract.DEFAULTS.SCHEMA, + $id: dataContractId, + version: 1, + ownerId, + documents, + $defs: {}, + }); + + dataContract.setEntropy(dataContractEntropy); + + return dataContract; + } + + /** + * Create Data Contract from plain object + * + * @param {RawDataContract} rawDataContract + * @param {Object} options + * @param {boolean} [options.skipValidation=false] + * @return {Promise} + */ + async createFromObject(rawDataContract, options = { }) { + const opts = { skipValidation: false, ...options }; + + if (!opts.skipValidation) { + const result = await this.validateDataContract(rawDataContract); + + if (!result.isValid()) { + throw new InvalidDataContractError(result.getErrors(), rawDataContract); + } + } + + return new DataContract(rawDataContract); + } + + /** + * Create Data Contract from buffer + * + * @param {Buffer} buffer + * @param {Object} options + * @param {boolean} [options.skipValidation=false] + * @return {Promise} + */ + async createFromBuffer(buffer, options = { }) { + let rawDataContract; + let protocolVersion; + + try { + [protocolVersion, rawDataContract] = this.decodeProtocolEntity( + buffer, + ); + + rawDataContract.protocolVersion = protocolVersion; + } catch (error) { + if (error instanceof AbstractConsensusError) { + throw new InvalidDataContractError([error]); + } + + throw error; + } + + return this.createFromObject(rawDataContract, options); + } + + /** + * Create Data Contract Create State Transition + * + * @param {DataContract} dataContract + * @return {DataContractCreateTransition} + */ + createDataContractCreateTransition(dataContract) { + return new DataContractCreateTransition({ + protocolVersion: this.dpp.getProtocolVersion(), + dataContract: dataContract.toObject(), + entropy: dataContract.getEntropy(), + }); + } + + /** + * Create Data Contract Update State Transition + * + * @param {DataContract} dataContract + * @return {DataContractUpdateTransition} + */ + createDataContractUpdateTransition(dataContract) { + return new DataContractUpdateTransition({ + protocolVersion: this.dpp.getProtocolVersion(), + dataContract: dataContract.toObject(), + }); + } +} + +module.exports = DataContractFactory; diff --git a/packages/js-dpp/lib/dataContract/enrichDataContractWithBaseSchema.js b/packages/js-dpp/lib/dataContract/enrichDataContractWithBaseSchema.js new file mode 100644 index 00000000000..23c62ef744b --- /dev/null +++ b/packages/js-dpp/lib/dataContract/enrichDataContractWithBaseSchema.js @@ -0,0 +1,71 @@ +const lodashCloneDeep = require('lodash.clonedeep'); + +const DataContract = require('./DataContract'); + +/** + * @typedef {enrichDataContractWithBaseSchema} + * + * @param {DataContract} dataContract + * @param {Object} baseSchema + * @param {number} schemaIdBytePrefix + * @param {string[]} [excludeProperties] + * + * @return {DataContract} + */ +function enrichDataContractWithBaseSchema( + dataContract, + baseSchema, + schemaIdBytePrefix, + excludeProperties = [], +) { + const clonedDataContract = lodashCloneDeep(dataContract.toObject()); + + delete clonedDataContract.$schema; + + const { documents: clonedDocuments } = clonedDataContract; + + Object.keys(clonedDocuments).forEach((type) => { + const clonedDocument = clonedDocuments[type]; + + const { + properties: baseProperties, + required: baseRequired, + } = baseSchema; + + if (!clonedDocument.required) { + clonedDocument.required = []; + } + + Object.keys(baseProperties) + .forEach((name) => { + clonedDocument.properties[name] = baseProperties[name]; + }); + + baseRequired.forEach((name) => clonedDocument.required.push(name)); + + excludeProperties.forEach((property) => { + delete clonedDocument[property]; + }); + + clonedDocument.required = clonedDocument.required + .filter((property) => !excludeProperties.includes(property)); + }); + + // Ajv caches schemas using $id internally + // so we can't pass two different schemas with the same $id. + // Hacky solution for that is to replace first four bytes + // in $id with passed prefix byte + clonedDataContract.$id[0] = schemaIdBytePrefix; + clonedDataContract.$id[1] = schemaIdBytePrefix; + clonedDataContract.$id[2] = schemaIdBytePrefix; + clonedDataContract.$id[4] = schemaIdBytePrefix; + + return new DataContract(clonedDataContract); +} + +enrichDataContractWithBaseSchema.PREFIX_BYTE_0 = 0; +enrichDataContractWithBaseSchema.PREFIX_BYTE_1 = 1; +enrichDataContractWithBaseSchema.PREFIX_BYTE_2 = 2; +enrichDataContractWithBaseSchema.PREFIX_BYTE_3 = 3; + +module.exports = enrichDataContractWithBaseSchema; diff --git a/packages/js-dpp/lib/dataContract/errors/DataContractAlreadyExistsError.js b/packages/js-dpp/lib/dataContract/errors/DataContractAlreadyExistsError.js new file mode 100644 index 00000000000..07fd9ee72fb --- /dev/null +++ b/packages/js-dpp/lib/dataContract/errors/DataContractAlreadyExistsError.js @@ -0,0 +1,23 @@ +const DPPError = require('../../errors/DPPError'); + +class DataContractAlreadyExistsError extends DPPError { + /** + * @param {AbstractStateTransition} stateTransition + */ + constructor(stateTransition) { + super('Data contract already exists'); + + this.stateTransition = stateTransition; + } + + /** + * Get failed state transition + * + * @return {AbstractStateTransition} + */ + getStateTransition() { + return this.stateTransition; + } +} + +module.exports = DataContractAlreadyExistsError; diff --git a/packages/js-dpp/lib/dataContract/errors/InvalidDataContractError.js b/packages/js-dpp/lib/dataContract/errors/InvalidDataContractError.js new file mode 100644 index 00000000000..28f78e44bd5 --- /dev/null +++ b/packages/js-dpp/lib/dataContract/errors/InvalidDataContractError.js @@ -0,0 +1,39 @@ +const DPPError = require('../../errors/DPPError'); + +class InvalidDataContractError extends DPPError { + /** + * @param {AbstractConsensusError[]} errors + * @param {RawDataContract} rawDataContract + */ + constructor(errors, rawDataContract) { + let message = `Invalid Data Contract: "${errors[0].message}"`; + if (errors.length > 1) { + message = `${message} and ${errors.length - 1} more`; + } + + super(message); + + this.errors = errors; + this.rawDataContract = rawDataContract; + } + + /** + * Get validation errors + * + * @return {AbstractConsensusError[]} + */ + getErrors() { + return this.errors; + } + + /** + * Get raw Data Contract + * + * @return {RawDataContract} + */ + getRawDataContract() { + return this.rawDataContract; + } +} + +module.exports = InvalidDataContractError; diff --git a/packages/js-dpp/lib/dataContract/generateDataContractId.js b/packages/js-dpp/lib/dataContract/generateDataContractId.js new file mode 100644 index 00000000000..92d5d03df4a --- /dev/null +++ b/packages/js-dpp/lib/dataContract/generateDataContractId.js @@ -0,0 +1,22 @@ +const hashModule = require('../util/hash'); + +/** + * Generate data contract id based on owner id and entropy + * + * @param {Buffer} ownerId + * @param {Buffer} entropy + * + * @return {Buffer} + */ +function generateDataContractId(ownerId, entropy) { + const { hash } = hashModule; + + return hash( + Buffer.concat([ + ownerId, + entropy, + ]), + ); +} + +module.exports = generateDataContractId; diff --git a/packages/js-dpp/lib/dataContract/getBinaryPropertiesFromSchema.js b/packages/js-dpp/lib/dataContract/getBinaryPropertiesFromSchema.js new file mode 100644 index 00000000000..6a4f3c98cf3 --- /dev/null +++ b/packages/js-dpp/lib/dataContract/getBinaryPropertiesFromSchema.js @@ -0,0 +1,109 @@ +/** + * Recursively build properties map + * + * @param {Object} schema + * @param {string} [propertyName=undefined] + * + * @return {Object} + */ +function buildBinaryPropertiesMap(schema, propertyName = undefined) { + const propertyNames = Object.keys(schema.properties); + + // We iterate over every property defined in the document schema while + // building a flat map e.g. + // + // { + // "firstLevel.secondLevel": { ...property keywords }, + // "firstLevel.secondLevel.third[0].property": { ...property keywords }, + // } + // + // of every property that have `contentEncoding` keyword + return propertyNames.reduce((map, name) => { + const property = schema.properties[name]; + + const propertyPath = propertyName ? `${propertyName}.${name}` : name; + + if (property.type === 'object' + && Object.prototype.hasOwnProperty.call(property, 'properties')) { + // In case property is an object we recursively call build method + // passing property as schema and assigning property path to current property name, + // this will allow for property name chaining e.g. `first.second: value` + // then we flatten the result and add to our resulting map + + // eslint-disable-next-line no-param-reassign + map = { + ...map, + ...buildBinaryPropertiesMap(property, propertyPath), + }; + } + + if (property.type === 'array' && property.items && property.items.type === 'object' + && Object.prototype.hasOwnProperty.call(property.items, 'properties')) { + // In case property is an array of a single type we recursively call build method + // passing array `item` property as schema and assigning property path to current + // property name, this will allow for property name chaining e.g. `first.second: value` + // then we flatten the result and add to our resulting map + + // eslint-disable-next-line no-param-reassign + map = { + ...map, + ...buildBinaryPropertiesMap(property.items, propertyPath), + }; + } + + if (property.type === 'array' && Array.isArray(property.items)) { + // In case property is an array of arrays + // We build a schema by assigning every item in the array schema to an index property name e.g + // + // { + // "arrayPropertyName[0]": { ...item 0 object }, + // "arrayPropertyName[1]": { ...item 1 object }, + // ... + // } + // + // and recursively call build method passing resulting schema and empty property name + // to avoid duplication of the property name in the resulting object, + // this will allow for property name chaining e.g. `first.second: value` + // then we flatten the result and add to our resulting map + + const arraySchema = property.items.reduce((schemaObject, item, index) => { + // eslint-disable-next-line no-param-reassign + schemaObject.properties[`${propertyPath}[${index}]`] = item; + + return schemaObject; + }, { + properties: {}, + }); + + // eslint-disable-next-line no-param-reassign + map = { + ...map, + ...buildBinaryPropertiesMap(arraySchema), + }; + } + + if (Object.prototype.hasOwnProperty.call(property, 'byteArray')) { + // eslint-disable-next-line no-param-reassign + map[propertyPath] = property; + } + + return map; + }, {}); +} + +/** + * Construct and get all properties with `contentEncoding` keyword + * + * @param {Object} documentSchema + * + * @return {Object} + */ +function getBinaryPropertiesFromSchema(documentSchema) { + if (!documentSchema.properties) { + return {}; + } + + return buildBinaryPropertiesMap(documentSchema); +} + +module.exports = { getBinaryPropertiesFromSchema }; diff --git a/packages/js-dpp/lib/dataContract/getPropertyDefinitionByPath.js b/packages/js-dpp/lib/dataContract/getPropertyDefinitionByPath.js new file mode 100644 index 00000000000..edc369d49bb --- /dev/null +++ b/packages/js-dpp/lib/dataContract/getPropertyDefinitionByPath.js @@ -0,0 +1,54 @@ +const baseDocumentSchema = require('../../schema/document/documentBase.json'); + +/** + * Get user property definition + * + * @typeof {getPropertyDefinitionByPath} + * @param {Object} documentDefinition + * @param {string} path + * + * @return {Object} + */ +function getPropertyDefinitionByPath(documentDefinition, path) { + // Return system properties schema + if (path.startsWith('$')) { + return baseDocumentSchema.properties[path]; + } + + const [currentSegment, ...rest] = path.split('.'); + + const { [currentSegment]: propertyDefinition } = (documentDefinition.properties || {}); + + // nothing found return nothing + if (!propertyDefinition) { + return undefined; + } + + // if there is nothing to lookup for next + // return currently found property definition + if (rest.length === 0) { + return propertyDefinition; + } + + const { type } = propertyDefinition; + + if (type === 'array') { + const { items: itemsDefinition } = propertyDefinition; + + if (itemsDefinition.type === 'object') { + return getPropertyDefinitionByPath(itemsDefinition, rest.join('.')); + } + } + + if (type === 'object') { + // rince and repeat + return getPropertyDefinitionByPath(propertyDefinition, rest.join('.')); + } + + // the `rest` is not empty + // but definition is not an object nor array + // nothing to lookup for + return undefined; +} + +module.exports = getPropertyDefinitionByPath; diff --git a/packages/js-dpp/lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition.js b/packages/js-dpp/lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition.js new file mode 100644 index 00000000000..41d78ced94c --- /dev/null +++ b/packages/js-dpp/lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition.js @@ -0,0 +1,127 @@ +const AbstractStateTransitionIdentitySigned = require('../../../stateTransition/AbstractStateTransitionIdentitySigned'); +const stateTransitionTypes = require('../../../stateTransition/stateTransitionTypes'); +const DataContract = require('../../DataContract'); + +class DataContractCreateTransition extends AbstractStateTransitionIdentitySigned { + /** + * @param {RawDataContractCreateTransition} rawDataContractCreateTransition + */ + constructor(rawDataContractCreateTransition) { + super(rawDataContractCreateTransition); + + if (Object.prototype.hasOwnProperty.call(rawDataContractCreateTransition, 'entropy')) { + this.entropy = rawDataContractCreateTransition.entropy; + } + + const dataContract = new DataContract(rawDataContractCreateTransition.dataContract); + + this.setDataContract(dataContract); + } + + /** + * Get State Transition type + * + * @return {number} + */ + getType() { + return stateTransitionTypes.DATA_CONTRACT_CREATE; + } + + /** + * Get Data Contract + * + * @return {DataContract} + */ + getDataContract() { + return this.dataContract; + } + + /** + * Set Data Contract + * + * @param {DataContract} dataContract + * @return {DataContractCreateTransition} + */ + setDataContract(dataContract) { + this.dataContract = dataContract; + + return this; + } + + /** + * Get entropy + * + * @returns {Buffer} + */ + getEntropy() { + return this.entropy; + } + + /** + * Get state transition as plain object + * + * @param {Object} [options] + * @param {boolean} [options.skipSignature=false] + * @param {boolean} [options.skipIdentifiersConversion=false] + * @return {RawDataContractCreateTransition} + */ + toObject(options = {}) { + Object.assign( + options, + { + skipIdentifiersConversion: false, + ...options, + }, + ); + + return { + ...super.toObject(options), + dataContract: this.getDataContract().toObject(), + entropy: this.getEntropy(), + }; + } + + /** + * Get state transition as JSON + * + * @return {JsonDataContractCreateTransition} + */ + toJSON() { + return { + ...super.toJSON(), + dataContract: this.getDataContract().toJSON(), + entropy: this.getEntropy().toString('base64'), + }; + } + + /** + * Get owner ID + * @return {Identifier} + */ + getOwnerId() { + return this.getDataContract().getOwnerId(); + } + + /** + * Returns id of the created contract + * + * @return {Identifier[]} + */ + getModifiedDataIds() { + return [this.getDataContract().getId()]; + } +} + +/** + * @typedef {RawStateTransitionIdentitySigned & Object} RawDataContractCreateTransition + * @property {RawDataContract} dataContract + * @property {Buffer} entropy + */ + +/** + * @typedef {JsonStateTransitionIdentitySigned & Object} JsonDataContractCreateTransition + * @property {JsonDataContract} dataContract + * @property {string} entropy + */ + +module.exports = DataContractCreateTransition; diff --git a/packages/js-dpp/lib/dataContract/stateTransition/DataContractCreateTransition/applyDataContractCreateTransitionFactory.js b/packages/js-dpp/lib/dataContract/stateTransition/DataContractCreateTransition/applyDataContractCreateTransitionFactory.js new file mode 100644 index 00000000000..dd1e6967224 --- /dev/null +++ b/packages/js-dpp/lib/dataContract/stateTransition/DataContractCreateTransition/applyDataContractCreateTransitionFactory.js @@ -0,0 +1,30 @@ +/** + * Apply data contract state transition (factory) + * + * @param {StateRepository} stateRepository + * + * @returns {applyDataContractCreateTransition} + */ +function applyDataContractCreateTransitionFactory(stateRepository) { + /** + * Apply data contract state transition + * + * @typedef applyDataContractCreateTransition + * + * @param {DataContractCreateTransition} stateTransition + * + * @return {Promise} + */ + async function applyDataContractCreateTransition(stateTransition) { + const executionContext = stateTransition.getExecutionContext(); + + await stateRepository.storeDataContract( + stateTransition.getDataContract(), + executionContext, + ); + } + + return applyDataContractCreateTransition; +} + +module.exports = applyDataContractCreateTransitionFactory; diff --git a/packages/js-dpp/lib/dataContract/stateTransition/DataContractCreateTransition/validation/basic/validateDataContractCreateTransitionBasicFactory.js b/packages/js-dpp/lib/dataContract/stateTransition/DataContractCreateTransition/validation/basic/validateDataContractCreateTransitionBasicFactory.js new file mode 100644 index 00000000000..7e75f3931f2 --- /dev/null +++ b/packages/js-dpp/lib/dataContract/stateTransition/DataContractCreateTransition/validation/basic/validateDataContractCreateTransitionBasicFactory.js @@ -0,0 +1,74 @@ +const InvalidDataContractIdError = require('../../../../../errors/consensus/basic/dataContract/InvalidDataContractIdError'); + +const generateDataContractId = require('../../../../generateDataContractId'); + +const convertBuffersToArrays = require('../../../../../util/convertBuffersToArrays'); + +const dataContractCreateTransitionSchema = require('../../../../../../schema/dataContract/stateTransition/dataContractCreate.json'); + +/** + * @param {JsonSchemaValidator} jsonSchemaValidator + * @param {validateDataContract} validateDataContract + * @param {validateProtocolVersion} validateProtocolVersion + * + * @return {validateDataContractCreateTransitionBasic} + */ +function validateDataContractCreateTransitionBasicFactory( + jsonSchemaValidator, + validateDataContract, + validateProtocolVersion, +) { + /** + * @typedef validateDataContractCreateTransitionBasic + * @param {RawDataContractCreateTransition} rawStateTransition + * @param {StateTransitionExecutionContext} executionContext + * @return {ValidationResult} + */ + // eslint-disable-next-line no-unused-vars + async function validateDataContractCreateTransitionBasic(rawStateTransition, executionContext) { + const result = jsonSchemaValidator.validate( + dataContractCreateTransitionSchema, + convertBuffersToArrays(rawStateTransition), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + validateProtocolVersion(rawStateTransition.protocolVersion), + ); + + if (!result.isValid()) { + return result; + } + + // Validate Data Contract + const rawDataContract = rawStateTransition.dataContract; + + result.merge( + await validateDataContract(rawDataContract), + ); + + if (!result.isValid()) { + return result; + } + + // Validate Data Contract ID + const generatedId = generateDataContractId( + rawDataContract.ownerId, rawStateTransition.entropy, + ); + + if (!generatedId.equals(rawDataContract.$id)) { + result.addError( + new InvalidDataContractIdError(generatedId, rawDataContract.$id), + ); + } + + return result; + } + + return validateDataContractCreateTransitionBasic; +} + +module.exports = validateDataContractCreateTransitionBasicFactory; diff --git a/packages/js-dpp/lib/dataContract/stateTransition/DataContractCreateTransition/validation/state/validateDataContractCreateTransitionStateFactory.js b/packages/js-dpp/lib/dataContract/stateTransition/DataContractCreateTransition/validation/state/validateDataContractCreateTransitionStateFactory.js new file mode 100644 index 00000000000..46b298b0258 --- /dev/null +++ b/packages/js-dpp/lib/dataContract/stateTransition/DataContractCreateTransition/validation/state/validateDataContractCreateTransitionStateFactory.js @@ -0,0 +1,45 @@ +const ValidationResult = require('../../../../../validation/ValidationResult'); + +const DataContractAlreadyPresentError = require('../../../../../errors/consensus/state/dataContract/DataContractAlreadyPresentError'); + +/** + * + * @param {StateRepository} stateRepository + * @return {validateDataContractCreateTransitionState} + */ +function validateDataContractCreateTransitionStateFactory(stateRepository) { + /** + * @typedef validateDataContractCreateTransitionState + * @param {DataContractCreateTransition} stateTransition + * @return {ValidationResult} + */ + async function validateDataContractCreateTransitionState(stateTransition) { + const result = new ValidationResult(); + + const executionContext = stateTransition.getExecutionContext(); + const dataContract = stateTransition.getDataContract(); + const dataContractId = dataContract.getId(); + + // Data contract shouldn't exist + const existingDataContract = await stateRepository.fetchDataContract( + dataContractId, + executionContext, + ); + + if (executionContext.isDryRun()) { + return result; + } + + if (existingDataContract) { + result.addError( + new DataContractAlreadyPresentError(dataContractId.toBuffer()), + ); + } + + return result; + } + + return validateDataContractCreateTransitionState; +} + +module.exports = validateDataContractCreateTransitionStateFactory; diff --git a/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/DataContractUpdateTransition.js b/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/DataContractUpdateTransition.js new file mode 100644 index 00000000000..6297f9952cc --- /dev/null +++ b/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/DataContractUpdateTransition.js @@ -0,0 +1,110 @@ +const AbstractStateTransitionIdentitySigned = require('../../../stateTransition/AbstractStateTransitionIdentitySigned'); +const stateTransitionTypes = require('../../../stateTransition/stateTransitionTypes'); +const DataContract = require('../../DataContract'); + +class DataContractUpdateTransition extends AbstractStateTransitionIdentitySigned { + /** + * @param {RawDataContractUpdateTransition} rawDataContractUpdateTransition + */ + constructor(rawDataContractUpdateTransition) { + super(rawDataContractUpdateTransition); + + const dataContract = new DataContract(rawDataContractUpdateTransition.dataContract); + + this.setDataContract(dataContract); + } + + /** + * Get State Transition type + * + * @return {number} + */ + getType() { + return stateTransitionTypes.DATA_CONTRACT_UPDATE; + } + + /** + * Get Data Contract + * + * @return {DataContract} + */ + getDataContract() { + return this.dataContract; + } + + /** + * Set Data Contract + * + * @param {DataContract} dataContract + * @return {DataContractUpdateTransition} + */ + setDataContract(dataContract) { + this.dataContract = dataContract; + + return this; + } + + /** + * Get state transition as plain object + * + * @param {Object} [options] + * @param {boolean} [options.skipSignature=false] + * @param {boolean} [options.skipIdentifiersConversion=false] + * @return {RawDataContractUpdateTransition} + */ + toObject(options = {}) { + Object.assign( + options, + { + skipIdentifiersConversion: false, + ...options, + }, + ); + + return { + ...super.toObject(options), + dataContract: this.getDataContract().toObject(), + }; + } + + /** + * Get state transition as JSON + * + * @return {JsonDataContractUpdateTransition} + */ + toJSON() { + return { + ...super.toJSON(), + dataContract: this.getDataContract().toJSON(), + }; + } + + /** + * Get owner ID + * @return {Identifier} + */ + getOwnerId() { + return this.getDataContract().getOwnerId(); + } + + /** + * Returns id of the created contract + * + * @return {Identifier[]} + */ + getModifiedDataIds() { + return [this.getDataContract().getId()]; + } +} + +/** + * @typedef {RawStateTransitionIdentitySigned & Object} RawDataContractUpdateTransition + * @property {RawDataContract} dataContract + */ + +/** + * @typedef {JsonStateTransitionIdentitySigned & Object} JsonDataContractUpdateTransition + * @property {JsonDataContract} dataContract + */ + +module.exports = DataContractUpdateTransition; diff --git a/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/applyDataContractUpdateTransitionFactory.js b/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/applyDataContractUpdateTransitionFactory.js new file mode 100644 index 00000000000..1cb07f36278 --- /dev/null +++ b/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/applyDataContractUpdateTransitionFactory.js @@ -0,0 +1,30 @@ +/** + * Apply data contract state transition (factory) + * + * @param {StateRepository} stateRepository + * + * @returns {applyDataContractUpdateTransition} + */ +function applyDataContractUpdateTransitionFactory(stateRepository) { + /** + * Apply data contract state transition + * + * @typedef applyDataContractUpdateTransition + * + * @param {DataContractCreateTransition} stateTransition + * + * @return {Promise} + */ + async function applyDataContractUpdateTransition(stateTransition) { + const executionContext = stateTransition.getExecutionContext(); + + await stateRepository.storeDataContract( + stateTransition.getDataContract(), + executionContext, + ); + } + + return applyDataContractUpdateTransition; +} + +module.exports = applyDataContractUpdateTransitionFactory; diff --git a/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateDataContractUpdateTransitionBasicFactory.js b/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateDataContractUpdateTransitionBasicFactory.js new file mode 100644 index 00000000000..85d8ece44c9 --- /dev/null +++ b/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateDataContractUpdateTransitionBasicFactory.js @@ -0,0 +1,199 @@ +const lodashClone = require('lodash.clonedeep'); + +const convertBuffersToArrays = require('../../../../../util/convertBuffersToArrays'); + +const dataContractUpdateTransitionSchema = require('../../../../../../schema/dataContract/stateTransition/dataContractUpdate.json'); + +const IncompatibleDataContractSchemaError = require('../../../../../errors/consensus/basic/dataContract/IncompatibleDataContractSchemaError'); +const DataContractImmutablePropertiesUpdateError = require('../../../../../errors/consensus/basic/dataContract/DataContractImmutablePropertiesUpdateError'); +const InvalidDataContractVersionError = require('../../../../../errors/consensus/basic/dataContract/InvalidDataContractVersionError'); +const DataContractNotPresentError = require('../../../../../errors/consensus/basic/document/DataContractNotPresentError'); + +const Identifier = require('../../../../../identifier/Identifier'); + +/** + * @param {JsonSchemaValidator} jsonSchemaValidator + * @param {validateDataContract} validateDataContract + * @param {validateProtocolVersion} validateProtocolVersion + * @param {StateRepository} stateRepository + * @param {DiffValidator} diffValidator + * @param {validateIndicesAreBackwardCompatible} validateIndicesAreBackwardCompatible + * @param {JsonPatch} jsonPatch + * + * @return {validateDataContractUpdateTransitionBasic} + */ +function validateDataContractUpdateTransitionBasicFactory( + jsonSchemaValidator, + validateDataContract, + validateProtocolVersion, + stateRepository, + diffValidator, + validateIndicesAreBackwardCompatible, + jsonPatch, +) { + /** + * @typedef validateDataContractUpdateTransitionBasic + * @param {RawDataContractUpdateTransition} rawStateTransition + * @param {StateTransitionExecutionContext} executionContext + * @return {Promise} + */ + async function validateDataContractUpdateTransitionBasic(rawStateTransition, executionContext) { + const result = jsonSchemaValidator.validate( + dataContractUpdateTransitionSchema, + convertBuffersToArrays(rawStateTransition), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + validateProtocolVersion(rawStateTransition.protocolVersion), + ); + + if (!result.isValid()) { + return result; + } + + // Validate Data Contract + const rawDataContract = rawStateTransition.dataContract; + + result.merge( + await validateDataContract(rawDataContract), + ); + + if (!result.isValid()) { + return result; + } + + const dataContractId = Identifier.from(rawDataContract.$id); + + // Data contract should exist + const existingDataContract = await stateRepository.fetchDataContract( + dataContractId, + executionContext, + ); + + if (executionContext.isDryRun()) { + return result; + } + + if (!existingDataContract) { + result.addError( + new DataContractNotPresentError(dataContractId.toBuffer()), + ); + + return result; + } + + // Version difference should be exactly 1 + const oldVersion = existingDataContract.getVersion(); + const newVersion = rawDataContract.version; + const versionDiff = newVersion - oldVersion; + + if (versionDiff !== 1) { + result.addError( + new InvalidDataContractVersionError( + oldVersion + 1, + oldVersion + versionDiff, + ), + ); + } + + // check that only $defs, version and documents are changed + const oldBaseDataContract = lodashClone(existingDataContract.toObject()); + delete oldBaseDataContract.$defs; + delete oldBaseDataContract.documents; + delete oldBaseDataContract.version; + + oldBaseDataContract.$id = oldBaseDataContract.$id.toString('hex'); + oldBaseDataContract.ownerId = oldBaseDataContract.ownerId.toString('hex'); + + const newBaseDataContract = lodashClone(rawDataContract); + delete newBaseDataContract.$defs; + delete newBaseDataContract.documents; + delete newBaseDataContract.version; + + newBaseDataContract.$id = newBaseDataContract.$id.toString('hex'); + newBaseDataContract.ownerId = newBaseDataContract.ownerId.toString('hex'); + + const baseDataContractDiff = jsonPatch.compare( + oldBaseDataContract, + newBaseDataContract, + ); + + if (baseDataContractDiff.length > 0) { + const { op: operation, path: fieldPath } = baseDataContractDiff[0]; + + const error = new DataContractImmutablePropertiesUpdateError( + operation, fieldPath, + ); + error.setDiff(baseDataContractDiff); + + result.addError(error); + + return result; + } + + // check indices are not changed + result.merge( + validateIndicesAreBackwardCompatible( + existingDataContract.getDocuments(), + rawDataContract.documents, + ), + ); + + if (!result.isValid()) { + return result; + } + + // Schema should be backward compatible + const oldSchema = existingDataContract.getDocuments(); + const newSchema = rawDataContract.documents; + + Object.entries(oldSchema) + .forEach(([documentType, documentSchema]) => { + try { + diffValidator.validateSchemaCompatibility( + documentSchema, + newSchema[documentType] || {}, + ); + } catch (schemaValidationError) { + const regexp = /change = (.*?)$/; + + const match = schemaValidationError.message.match(regexp); + + const validationErrorOperations = JSON.parse(match[1]); + + const { op: operation, path: fieldPath } = validationErrorOperations[0]; + + const error = new IncompatibleDataContractSchemaError( + existingDataContract.getId().toBuffer(), + operation, + fieldPath, + ); + error.setOldSchema(documentSchema); + error.setNewSchema(newSchema[documentType]); + error.setValidationError(schemaValidationError); + + result.addError(error); + } + }); + + return result; + } + + return validateDataContractUpdateTransitionBasic; +} + +/** + * @typedef {Object} DiffValidator + * @property {function(Object, Object)} validateSchemaCompatibility + */ + +/** + * @typedef {Object} JsonPatch + * @property {function(Object, Object)} compare + */ + +module.exports = validateDataContractUpdateTransitionBasicFactory; diff --git a/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateIndicesAreBackwardCompatible.js b/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateIndicesAreBackwardCompatible.js new file mode 100644 index 00000000000..b140d6c52ba --- /dev/null +++ b/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateIndicesAreBackwardCompatible.js @@ -0,0 +1,262 @@ +const lodashGet = require('lodash.get'); + +const DataContractHaveNewUniqueIndexError = require('../../../../../errors/consensus/basic/dataContract/DataContractHaveNewUniqueIndexError'); +const DataContractUniqueIndicesChangedError = require('../../../../../errors/consensus/basic/dataContract/DataContractUniqueIndicesChangedError'); +const DataContractInvalidIndexDefinitionUpdateError = require('../../../../../errors/consensus/basic/dataContract/DataContractInvalidIndexDefinitionUpdateError'); + +const getPropertyDefinitionByPath = require('../../../../getPropertyDefinitionByPath'); + +const serializer = require('../../../../../util/serializer'); + +const ValidationResult = require('../../../../../validation/ValidationResult'); + +/** + * Get one of old unique indices that have been changed + * + * @param {Object} nameIndexMap + * @param {string} documentType + * @param {Object} existingSchema + * + * @returns {object|undefined} + */ +function getChangedOldUniqueIndex(nameIndexMap, documentType, existingSchema) { + // Checking every unique existing index if it has been altered + return (existingSchema.indices || []).find( + (indexDefinition) => ( + !serializer.encode(indexDefinition).equals( + serializer.encode(nameIndexMap[indexDefinition.name]), + ) && indexDefinition.unique === true + ), + ); +} + +/** + * Get one of the old non-unique indices that have been wrongly changed + * + * @param {Object} nameIndexMap + * @param {string} documentType + * @param {object} existingSchema + * + * @returns {object} + */ +function getWronglyUpdatedNonUniqueIndex(nameIndexMap, documentType, existingSchema) { + // Checking every existing non-unique index, and it's respective new index + // if they are changed per spec + return (existingSchema.indices || []).find((indexDefinition) => { + if (indexDefinition.unique === true) { + return false; + } + + const newIndexDefinition = nameIndexMap[indexDefinition.name]; + + // creating new index definition snapshot + // with the same amount of properties as old one + // if they are the same and in the same order + // later check will return nothing + const newIndexSnapshot = { + name: indexDefinition.name, + properties: newIndexDefinition.properties.slice( + 0, indexDefinition.properties.length, + ), + }; + + // In case `unique` is false it still must be in the new index snapshot + if (Object.prototype.hasOwnProperty.call(indexDefinition, 'unique')) { + newIndexSnapshot.unique = indexDefinition.unique; + } + + if (!serializer.encode(indexDefinition).equals( + serializer.encode(newIndexSnapshot), + )) { + return true; + } + + // check that rest of the properties are newly defined ones + const notNewProperty = newIndexDefinition.properties.slice( + indexDefinition.properties.length, + ).find((propertyWithOrder) => { + const propertyName = Object.keys(propertyWithOrder)[0]; + + return Boolean( + getPropertyDefinitionByPath(existingSchema, propertyName), + ); + }); + + return notNewProperty !== undefined; + }); +} + +/** + * Get one of the new indices that have unique flag + * + * @param {string} documentType + * @param {object} existingSchema + * @param {object} newSchema + * + * @returns {object} + */ +function getNewUniqueIndex(documentType, existingSchema, newSchema) { + const newSchemaIndices = lodashGet(newSchema, `${documentType}.indices`); + + const existingIndexNames = (existingSchema.indices || []).map( + (indexDefinition) => indexDefinition.name, + ); + + // Gather only newly defined indices + const newIndices = (newSchemaIndices || []).filter( + (indexDefinition) => !existingIndexNames.includes(indexDefinition.name), + ); + + return (newIndices || []).find((indexDefinition) => indexDefinition.unique === true); +} + +/** + * Get one of the new indices that have old properties in them in the wrong order + * + * @param {string} documentType + * @param {object} existingSchema + * @param {object[]} newDocumentDefinitions + * + * @returns {object} + */ +function getWronglyConstructedNewIndex(documentType, existingSchema, newDocumentDefinitions) { + const newSchemaIndices = lodashGet(newDocumentDefinitions, `${documentType}.indices`); + + const existingIndexNames = (existingSchema.indices || []).map( + (indexDefinition) => indexDefinition.name, + ); + + const existingIndexedProperties = new Set((existingSchema.indices || []).reduce( + (properties, indexDefinition) => [ + ...properties, + ...indexDefinition.properties.map((definition) => Object.keys(definition)[0]), + ], [], + )); + + // Build an index of all possible allowed combinations + // of old indices to check later + const existingIndexSnapshots = (existingSchema.indices || []).reduce( + (snapshots, indexDefinition) => [ + ...snapshots, + ...indexDefinition.properties.map((_, index) => ( + serializer.encode(indexDefinition.properties.slice(0, index + 1)).toString('hex') + )), + ], [], + ); + + // Gather only newly defined indices + const newIndices = (newSchemaIndices || []).filter( + (indexDefinition) => !existingIndexNames.includes(indexDefinition.name), + ); + + return (newIndices || []).find((indexDefinition) => { + const existingProperties = indexDefinition.properties.filter( + (prop) => existingIndexedProperties.has(Object.keys(prop)[0]), + ); + + // if no old properties being used - skip + if (existingProperties.length === 0) { + return false; + } + + // build a partial snapshot of the new index + // containing only first part of it with a + // length equal to number of old properties used + // sine they should be in the beginning of the + // index we can check it with previously built snapshot combinations + const partialNewIndexSnapshot = serializer.encode( + indexDefinition.properties.slice(0, existingProperties.length), + ).toString('hex'); + + return !existingIndexSnapshots.includes(partialNewIndexSnapshot); + }); +} + +/** + * Validate indices have not been changed + * + * @typedef {validateIndicesAreBackwardCompatible} + * @param {Object} existingDocumentDefinitions + * @param {Object} newDocumentDefinitions + * + * @returns {ValidationResult} + */ +function validateIndicesAreBackwardCompatible(existingDocumentDefinitions, newDocumentDefinitions) { + const result = new ValidationResult(); + + Object.entries(existingDocumentDefinitions) + .find(([documentType, existingSchema]) => { + // Building name - index map for easier search + const nameIndexMap = (newDocumentDefinitions[documentType].indices || []) + .reduce((map, indexDefinition) => ({ + ...map, + [indexDefinition.name]: indexDefinition, + }), {}); + + const changedUniqueExistingIndex = getChangedOldUniqueIndex( + nameIndexMap, + documentType, + existingSchema, + ); + + if (changedUniqueExistingIndex !== undefined) { + result.addError( + new DataContractUniqueIndicesChangedError( + documentType, changedUniqueExistingIndex.name, + ), + ); + + return true; + } + + const wronglyUpdatedIndex = getWronglyUpdatedNonUniqueIndex( + nameIndexMap, + documentType, + existingSchema, + ); + + if (wronglyUpdatedIndex !== undefined) { + result.addError( + new DataContractInvalidIndexDefinitionUpdateError( + documentType, wronglyUpdatedIndex.name, + ), + ); + + return true; + } + + const newUniqueIndex = getNewUniqueIndex( + documentType, existingSchema, newDocumentDefinitions, + ); + + if (newUniqueIndex !== undefined) { + result.addError( + new DataContractHaveNewUniqueIndexError( + documentType, newUniqueIndex.name, + ), + ); + + return true; + } + + const wronglyConstructedNewIndex = getWronglyConstructedNewIndex( + documentType, existingSchema, newDocumentDefinitions, + ); + + if (wronglyConstructedNewIndex !== undefined) { + result.addError( + new DataContractInvalidIndexDefinitionUpdateError( + documentType, wronglyConstructedNewIndex.name, + ), + ); + + return true; + } + + return false; + }); + + return result; +} + +module.exports = validateIndicesAreBackwardCompatible; diff --git a/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/validation/state/validateDataContractUpdateTransitionStateFactory.js b/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/validation/state/validateDataContractUpdateTransitionStateFactory.js new file mode 100644 index 00000000000..1587ace0ee8 --- /dev/null +++ b/packages/js-dpp/lib/dataContract/stateTransition/DataContractUpdateTransition/validation/state/validateDataContractUpdateTransitionStateFactory.js @@ -0,0 +1,64 @@ +const ValidationResult = require('../../../../../validation/ValidationResult'); + +const InvalidDataContractVersionError = require('../../../../../errors/consensus/basic/dataContract/InvalidDataContractVersionError'); +const DataContractNotPresentError = require('../../../../../errors/consensus/basic/document/DataContractNotPresentError'); + +/** + * + * @param {StateRepository} stateRepository + * @return {validateDataContractUpdateTransitionState} + */ +function validateDataContractUpdateTransitionStateFactory( + stateRepository, +) { + /** + * @typedef validateDataContractUpdateTransitionState + * @param {DataContractCreateTransition} stateTransition + * @return {ValidationResult} + */ + async function validateDataContractUpdateTransitionState(stateTransition) { + const result = new ValidationResult(); + + const executionContext = stateTransition.getExecutionContext(); + const dataContract = stateTransition.getDataContract(); + const dataContractId = dataContract.getId(); + + // Data contract should exist + const existingDataContract = await stateRepository.fetchDataContract( + dataContractId, + executionContext, + ); + + if (executionContext.isDryRun()) { + return result; + } + + if (!existingDataContract) { + result.addError( + new DataContractNotPresentError(dataContractId.toBuffer()), + ); + + return result; + } + + // Version difference should be exactly 1 + const oldVersion = existingDataContract.getVersion(); + const newVersion = dataContract.getVersion(); + const versionDiff = newVersion - oldVersion; + + if (versionDiff !== 1) { + result.addError( + new InvalidDataContractVersionError( + oldVersion + 1, + oldVersion + versionDiff, + ), + ); + } + + return result; + } + + return validateDataContractUpdateTransitionState; +} + +module.exports = validateDataContractUpdateTransitionStateFactory; diff --git a/packages/js-dpp/lib/dataContract/validation/validateDataContractFactory.js b/packages/js-dpp/lib/dataContract/validation/validateDataContractFactory.js new file mode 100644 index 00000000000..a92e4c89274 --- /dev/null +++ b/packages/js-dpp/lib/dataContract/validation/validateDataContractFactory.js @@ -0,0 +1,337 @@ +const JsonSchemaValidator = require('../../validation/JsonSchemaValidator'); +const ValidationResult = require('../../validation/ValidationResult'); + +const DataContract = require('../DataContract'); + +const baseDocumentSchema = require('../../../schema/document/documentBase.json'); + +const DuplicateIndexError = require('../../errors/consensus/basic/dataContract/DuplicateIndexError'); +const UndefinedIndexPropertyError = require('../../errors/consensus/basic/dataContract/UndefinedIndexPropertyError'); +const InvalidIndexPropertyTypeError = require('../../errors/consensus/basic/dataContract/InvalidIndexPropertyTypeError'); +const SystemPropertyIndexAlreadyPresentError = require('../../errors/consensus/basic/dataContract/SystemPropertyIndexAlreadyPresentError'); +const UniqueIndicesLimitReachedError = require('../../errors/consensus/basic/dataContract/UniqueIndicesLimitReachedError'); +const InvalidIndexedPropertyConstraintError = require('../../errors/consensus/basic/dataContract/InvalidIndexedPropertyConstraintError'); +const InvalidCompoundIndexError = require('../../errors/consensus/basic/dataContract/InvalidCompoundIndexError'); + +const convertBuffersToArrays = require('../../util/convertBuffersToArrays'); +const DuplicateIndexNameError = require('../../errors/consensus/basic/dataContract/DuplicateIndexNameError'); + +const allowedIndexSystemProperties = ['$ownerId', '$createdAt', '$updatedAt']; +const notAllowedIndexProperties = ['$id']; + +const MAX_INDEXED_STRING_PROPERTY_LENGTH = 63; +const MAX_INDEXED_BYTE_ARRAY_PROPERTY_LENGTH = 255; +const MAX_INDEXED_ARRAY_ITEMS = 1024; + +/** + * @param {JsonSchemaValidator} jsonSchemaValidator + * @param {validateDataContractMaxDepth} validateDataContractMaxDepth + * @param {enrichDataContractWithBaseSchema} enrichDataContractWithBaseSchema + * @param {validateDataContractPatterns} validateDataContractPatterns + * @param {validateProtocolVersion} validateProtocolVersion + * @param {getPropertyDefinitionByPath} getPropertyDefinitionByPath + * @return {validateDataContract} + */ +module.exports = function validateDataContractFactory( + jsonSchemaValidator, + validateDataContractMaxDepth, + enrichDataContractWithBaseSchema, + validateDataContractPatterns, + validateProtocolVersion, + getPropertyDefinitionByPath, +) { + /** + * @typedef validateDataContract + * @param {RawDataContract} rawDataContract + * @return {ValidationResult} + */ + async function validateDataContract(rawDataContract) { + const result = new ValidationResult(); + + // Validate Data Contract schema + result.merge( + jsonSchemaValidator.validate( + JsonSchemaValidator.SCHEMAS.META.DATA_CONTRACT, + convertBuffersToArrays(rawDataContract), + ), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + validateProtocolVersion(rawDataContract.protocolVersion), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + await validateDataContractMaxDepth(rawDataContract), + ); + + // Validate regexp patterns are compatible with Re2 + result.merge( + validateDataContractPatterns(rawDataContract), + ); + + if (!result.isValid()) { + return result; + } + + // Validate Document JSON Schemas + const enrichedDataContract = enrichDataContractWithBaseSchema( + new DataContract(rawDataContract), + baseDocumentSchema, + enrichDataContractWithBaseSchema.PREFIX_BYTE_0, + ); + + Object.keys(enrichedDataContract.getDocuments()).forEach((documentType) => { + const documentSchemaRef = enrichedDataContract.getDocumentSchemaRef( + documentType, + ); + + const additionalSchemas = { + [enrichedDataContract.getJsonSchemaId()]: enrichedDataContract.toJSON(), + }; + + result.merge( + jsonSchemaValidator.validateSchema( + documentSchemaRef, + additionalSchemas, + ), + ); + }); + + if (!result.isValid()) { + return result; + } + + // Validate indices + Object.entries(enrichedDataContract.documents).filter(([, documentSchema]) => ( + Object.prototype.hasOwnProperty.call(documentSchema, 'indices') + )) + .forEach(([documentType, documentSchema]) => { + const indicesFingerprints = []; + let uniqueIndexCount = 0; + let isUniqueIndexLimitReached = false; + + // Ensure index names are unique + const indexNames = documentSchema.indices.map((indexDefinition) => indexDefinition.name); + const [nonUniqueIndexName] = indexNames.filter( + (indexName, i) => indexNames.indexOf(indexName) !== i, + ); + + if (nonUniqueIndexName !== undefined) { + result.addError(new DuplicateIndexNameError( + documentType, + nonUniqueIndexName, + )); + } + + documentSchema.indices.forEach((indexDefinition) => { + const indexPropertyNames = indexDefinition.properties + .map((property) => Object.keys(property)[0]); + + // Ensure there are no more than 3 unique indices + if (!isUniqueIndexLimitReached && indexDefinition.unique) { + uniqueIndexCount++; + + if (uniqueIndexCount > UniqueIndicesLimitReachedError.UNIQUE_INDEX_LIMIT) { + isUniqueIndexLimitReached = true; + + result.addError(new UniqueIndicesLimitReachedError( + documentType, + )); + } + } + + // Ensure there are no duplicate system indices + notAllowedIndexProperties + .forEach((propertyName) => { + if (indexPropertyNames.includes(propertyName)) { + result.addError(new SystemPropertyIndexAlreadyPresentError( + documentType, + indexDefinition, + propertyName, + )); + } + }); + + // Ensure index properties are defined in the document + const userDefinedProperties = indexPropertyNames + .filter((name) => !allowedIndexSystemProperties.includes(name)); + + const propertyDefinitionEntities = userDefinedProperties + .map((propertyName) => ( + [ + propertyName, + getPropertyDefinitionByPath(documentSchema, propertyName), + ] + )); + + const undefinedProperties = propertyDefinitionEntities + .filter(([, propertyDefinition]) => !propertyDefinition) + .map(([propertyName]) => { + result.addError( + new UndefinedIndexPropertyError( + documentType, + indexDefinition, + propertyName, + ), + ); + + return propertyName; + }); + + // Skip further validation if there are undefined properties + if (undefinedProperties.length > 0) { + return; + } + + // Validate indexed property $defs + propertyDefinitionEntities.forEach(([propertyName, propertyDefinition]) => { + const { + type: propertyType, + byteArray: isByteArray, + } = propertyDefinition; + + let invalidPropertyType; + + if (propertyType === 'object') { + invalidPropertyType = 'object'; + } + + // const { items, prefixItems } = propertyDefinition; + + // Validate arrays contain scalar values or have the same types + if (propertyType === 'array' && !isByteArray) { + invalidPropertyType = 'array'; + + // const isInvalidPrefixItems = prefixItems + // && ( + // prefixItems.some((prefixItem) => + // prefixItem.type === 'object' || prefixItem.type === 'array') + // || !prefixItems.every((prefixItem) => prefixItem.type === prefixItems[0].type) + // ); + // + // const isInvalidItemTypes = items.type === 'object' || items.type === 'array'; + // + // if (isInvalidPrefixItems || isInvalidItemTypes) { + // invalidPropertyType = 'array'; + // } + } + + if (invalidPropertyType) { + result.addError(new InvalidIndexPropertyTypeError( + documentType, + indexDefinition, + propertyName, + invalidPropertyType, + )); + } + + // Validate sting length inside arrays + // if (!invalidPropertyType && propertyType === 'array' && !isByteArray) { + // const isInvalidPrefixItems = prefixItems && prefixItems.some((prefixItem) => ( + // prefixItem.type === 'string' + // && ( + // !prefixItem.maxLength || prefixItem.maxLength > MAX_INDEXED_STRING_PROPERTY_LENGTH + // ) + // )); + // + // const isInvalidItemTypes = items.type === 'string' && ( + // !items.maxLength || items.maxLength > MAX_INDEXED_STRING_PROPERTY_LENGTH + // ); + // + // if (isInvalidPrefixItems || isInvalidItemTypes) { + // result.addError( + // new InvalidIndexedPropertyConstraintError( + // documentType, + // indexDefinition, + // propertyName, + // 'maxLength', + // `should be less or equal ${MAX_INDEXED_STRING_PROPERTY_LENGTH}`, + // ), + // ); + // } + // } + // + if (!invalidPropertyType && propertyType === 'array') { + const { maxItems } = propertyDefinition; + + let maxLimit; + if (isByteArray) { + maxLimit = MAX_INDEXED_BYTE_ARRAY_PROPERTY_LENGTH; + } else { + maxLimit = MAX_INDEXED_ARRAY_ITEMS; + } + + if ((maxItems === undefined || maxItems > maxLimit)) { + result.addError( + new InvalidIndexedPropertyConstraintError( + documentType, + indexDefinition, + propertyName, + 'maxItems', + `should be less or equal ${maxLimit}`, + ), + ); + } + } + + if (propertyType === 'string') { + const { maxLength } = propertyDefinition; + + if (maxLength === undefined || maxLength > MAX_INDEXED_STRING_PROPERTY_LENGTH) { + result.addError( + new InvalidIndexedPropertyConstraintError( + documentType, + indexDefinition, + propertyName, + 'maxLength', + `should be less or equal ${MAX_INDEXED_STRING_PROPERTY_LENGTH}`, + ), + ); + } + } + }); + + // Make sure that compound unique indices contain all fields + if (indexPropertyNames.length > 1) { + const requiredFields = documentSchema.required || []; + const allAreRequired = indexPropertyNames + .every((propertyName) => requiredFields.includes(propertyName)); + const allAreNotRequired = indexPropertyNames + .every((propertyName) => !requiredFields.includes(propertyName)); + + if (!allAreRequired && !allAreNotRequired) { + result.addError( + new InvalidCompoundIndexError(documentType, indexDefinition), + ); + } + } + + // Ensure index definition uniqueness + const indicesFingerprint = JSON.stringify(indexDefinition.properties); + + if (indicesFingerprints.includes(indicesFingerprint)) { + result.addError( + new DuplicateIndexError( + documentType, + indexDefinition, + ), + ); + } + + indicesFingerprints.push(indicesFingerprint); + }); + }); + + return result; + } + + return validateDataContract; +}; diff --git a/packages/js-dpp/lib/dataContract/validation/validateDataContractMaxDepthFactory.js b/packages/js-dpp/lib/dataContract/validation/validateDataContractMaxDepthFactory.js new file mode 100644 index 00000000000..ceea4baf926 --- /dev/null +++ b/packages/js-dpp/lib/dataContract/validation/validateDataContractMaxDepthFactory.js @@ -0,0 +1,94 @@ +const lodashCloneDeep = require('lodash.clonedeep'); +const ValidationResult = require('../../validation/ValidationResult'); +const DataContractMaxDepthExceedError = require('../../errors/consensus/basic/dataContract/DataContractMaxDepthExceedError'); +const InvalidJsonSchemaRefError = require('../../errors/consensus/basic/dataContract/InvalidJsonSchemaRefError'); + +/** + * Check that JSON Schema max depth is less than max value + * @private + * @param {Object} json + * @returns {number} + */ +function checkMaxDepth(json) { + let depth = 1; + + const keys = Object.keys(json); + + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + + if (typeof json[key] === 'object') { + const tmpDepth = checkMaxDepth(json[key]) + 1; + depth = Math.max(depth, tmpDepth); + } + } + + if (depth > DataContractMaxDepthExceedError.MAX_DEPTH) { + throw new DataContractMaxDepthExceedError(); + } + + return depth; +} + +/** + * + * @param {$RefParser} refParser + * @returns {validateDataContractMaxDepth} + */ +function validateDataContractMaxDepthFactory(refParser) { + /** + * Dereference JSON Schema $ref pointers and check max depth + * @typedef validateDataContractMaxDepth + * @param {RawDataContract} rawDataContract + * @returns {ValidationResult} + */ + async function validateDataContractMaxDepth(rawDataContract) { + const result = new ValidationResult(); + let dereferencedDataContract; + + const clonedDataContract = lodashCloneDeep(rawDataContract); + + try { + dereferencedDataContract = await refParser.dereference(clonedDataContract, { + parse: { + yaml: false, + text: false, + binary: false, + }, + resolve: { + external: false, + http: false, + }, + dereference: { + circular: false, + }, + }); + } catch (error) { + const consensusError = new InvalidJsonSchemaRefError(error.message); + + consensusError.setRefError(error); + + result.addError(consensusError); + + return result; + } + + try { + checkMaxDepth(dereferencedDataContract); + } catch (error) { + if (error instanceof DataContractMaxDepthExceedError) { + result.addError(error); + + return result; + } + + throw error; + } + + return result; + } + + return validateDataContractMaxDepth; +} + +module.exports = validateDataContractMaxDepthFactory; diff --git a/packages/js-dpp/lib/dataContract/validation/validateDataContractPatternsFactory.js b/packages/js-dpp/lib/dataContract/validation/validateDataContractPatternsFactory.js new file mode 100644 index 00000000000..11db3c78d70 --- /dev/null +++ b/packages/js-dpp/lib/dataContract/validation/validateDataContractPatternsFactory.js @@ -0,0 +1,47 @@ +const traverse = require('json-schema-traverse'); +const ValidationResult = require('../../validation/ValidationResult'); +const IncompatibleRe2PatternError = require('../../errors/consensus/basic/dataContract/IncompatibleRe2PatternError'); + +/** + * + * @param {RE2} RE2 + * @return validateDataContractPatterns + */ +function validateDataContractPatternsFactory( + RE2, +) { + /** + * @typedef validateDataContractPatterns + * @param {RawDataContract} rawDataContract + * @returns {ValidationResult} + */ + function validateDataContractPatterns(rawDataContract) { + const result = new ValidationResult(); + + traverse(rawDataContract, { + allKeys: true, + cb: (item, path) => { + Object.entries(item).forEach(([key, value]) => { + if (key === 'pattern') { + try { + // eslint-disable-next-line no-new + new RE2(value, 'u'); + } catch (e) { + const consensusError = new IncompatibleRe2PatternError(value, path, e); + + consensusError.setPatternError(new Error(e)); + + result.addError(consensusError); + } + } + }); + }, + }); + + return result; + } + + return validateDataContractPatterns; +} + +module.exports = validateDataContractPatternsFactory; diff --git a/packages/js-dpp/lib/dataTrigger/DataTrigger.js b/packages/js-dpp/lib/dataTrigger/DataTrigger.js new file mode 100644 index 00000000000..c0932e06771 --- /dev/null +++ b/packages/js-dpp/lib/dataTrigger/DataTrigger.js @@ -0,0 +1,85 @@ +const DataTriggerExecutionResult = require('./DataTriggerExecutionResult'); +const DataTriggerExecutionError = require('../errors/consensus/state/dataContract/dataTrigger/DataTriggerExecutionError'); +const DataTriggerInvalidResultError = require('../errors/consensus/state/dataContract/dataTrigger/DataTriggerInvalidResultError'); + +class DataTrigger { + /** + * @param {Buffer|Identifier} dataContractId + * @param {string} documentType + * @param {number} transitionAction + * @param { + * function(DocumentCreateTransition[] + * |DocumentReplaceTransition[] + * |DocumentDeleteTransition[], DataTriggerExecutionContext, string):DataTriggerExecutionResult + * } trigger + * @param {Buffer|Identifier} topLevelIdentity + */ + constructor(dataContractId, documentType, transitionAction, trigger, topLevelIdentity) { + this.dataContractId = dataContractId; + this.documentType = documentType; + this.transitionAction = transitionAction; + this.trigger = trigger; + this.topLevelIdentity = topLevelIdentity; + } + + /** + * Check this trigger is matching for specified data + * + * @param {string} dataContractId + * @param {string} documentType + * @param {number} transitionAction + * + * @return {boolean} + */ + isMatchingTriggerForData(dataContractId, documentType, transitionAction) { + return this.dataContractId.equals(dataContractId) + && this.documentType === documentType + && this.transitionAction === transitionAction; + } + + /** + * Execute data trigger + * + * @param {DocumentCreateTransition[] + * |DocumentReplaceTransition[] + * |DocumentDeleteTransition[]} documentTransition + * @param {DataTriggerExecutionContext} context + * + * @returns {Promise} + */ + async execute(documentTransition, context) { + let result; + + try { + result = await this.trigger(documentTransition, context, this.topLevelIdentity); + } catch (e) { + result = new DataTriggerExecutionResult(); + + const consensusError = new DataTriggerExecutionError( + context.getDataContract().getId().toBuffer(), + documentTransition.getId().toBuffer(), + e.message, + ); + + consensusError.setExecutionError(e); + + result.addError(consensusError); + + return result; + } + + if (!(result instanceof DataTriggerExecutionResult)) { + result = new DataTriggerExecutionResult(); + result.addError( + new DataTriggerInvalidResultError( + context.getDataContract().getId().toBuffer(), + documentTransition.getId().toBuffer(), + ), + ); + } + + return result; + } +} + +module.exports = DataTrigger; diff --git a/packages/js-dpp/lib/dataTrigger/DataTriggerExecutionContext.js b/packages/js-dpp/lib/dataTrigger/DataTriggerExecutionContext.js new file mode 100644 index 00000000000..abdd2eeced6 --- /dev/null +++ b/packages/js-dpp/lib/dataTrigger/DataTriggerExecutionContext.js @@ -0,0 +1,47 @@ +class DataTriggerExecutionContext { + /** + * @param {StateRepository} stateRepository + * @param {Buffer|Identifier} ownerId + * @param {DataContract} dataContract + * @param {StateTransitionExecutionContext} stateTransitionExecutionContext + */ + constructor(stateRepository, ownerId, dataContract, stateTransitionExecutionContext) { + /** + * @type {StateRepository} + */ + this.stateRepository = stateRepository; + this.ownerId = ownerId; + this.dataContract = dataContract; + this.stateTransitionExecutionContext = stateTransitionExecutionContext; + } + + /** + * @returns {StateRepository} + */ + getStateRepository() { + return this.stateRepository; + } + + /** + * @returns {Buffer|Identifier} + */ + getOwnerId() { + return this.ownerId; + } + + /** + * @returns {DataContract} + */ + getDataContract() { + return this.dataContract; + } + + /** + * @return {StateTransitionExecutionContext} + */ + getStateTransitionExecutionContext() { + return this.stateTransitionExecutionContext; + } +} + +module.exports = DataTriggerExecutionContext; diff --git a/packages/js-dpp/lib/dataTrigger/DataTriggerExecutionResult.js b/packages/js-dpp/lib/dataTrigger/DataTriggerExecutionResult.js new file mode 100644 index 00000000000..229aedf10d5 --- /dev/null +++ b/packages/js-dpp/lib/dataTrigger/DataTriggerExecutionResult.js @@ -0,0 +1,37 @@ +class DataTriggerExecutionResult { + /** + * @param {AbstractDataTriggerError[]} errors + */ + constructor(errors = []) { + this.errors = errors; + } + + /** + * Add an error to result + * + * @param {AbstractDataTriggerError} error + */ + addError(...error) { + this.errors.push(...error); + } + + /** + * Get all data trigger execution errors + * + * @return {AbstractDataTriggerError[]} + */ + getErrors() { + return this.errors; + } + + /** + * Check if result have no errors + * + * @return {boolean} + */ + isOk() { + return this.errors.length === 0; + } +} + +module.exports = DataTriggerExecutionResult; diff --git a/packages/js-dpp/lib/dataTrigger/dashpayDataTriggers/createContactRequestDataTrigger.js b/packages/js-dpp/lib/dataTrigger/dashpayDataTriggers/createContactRequestDataTrigger.js new file mode 100644 index 00000000000..137b1b36b9e --- /dev/null +++ b/packages/js-dpp/lib/dataTrigger/dashpayDataTriggers/createContactRequestDataTrigger.js @@ -0,0 +1,54 @@ +const DataTriggerExecutionResult = require('../DataTriggerExecutionResult'); +const DataTriggerConditionError = require('../../errors/consensus/state/dataContract/dataTrigger/DataTriggerConditionError'); + +const BLOCKS_WINDOW_SIZE = 8; + +/** + * Data trigger for contract request creation process + * + * @param {DocumentCreateTransition} documentTransition + * @param {DataTriggerExecutionContext} context + * + * @return {Promise} + */ +async function createContactRequestDataTrigger(documentTransition, context) { + const result = new DataTriggerExecutionResult(); + + if (context.getStateTransitionExecutionContext().isDryRun()) { + return result; + } + + const { + coreHeightCreatedAt, + } = documentTransition.getData(); + + if (coreHeightCreatedAt === undefined) { + return result; + } + + const stateRepository = context.getStateRepository(); + + const latestPlatformBlockHeader = await stateRepository.fetchLatestPlatformBlockHeader(); + + const { coreChainLockedHeight } = latestPlatformBlockHeader; + + const heightWindowStart = coreChainLockedHeight - BLOCKS_WINDOW_SIZE; + const heightWindowEnd = coreChainLockedHeight + BLOCKS_WINDOW_SIZE; + + if (coreHeightCreatedAt < heightWindowStart || coreHeightCreatedAt > heightWindowEnd) { + const error = new DataTriggerConditionError( + context.getDataContract().getId().toBuffer(), + documentTransition.getId().toBuffer(), + `Core height ${coreHeightCreatedAt} is out of block height window from ${heightWindowStart} to ${heightWindowEnd}`, + ); + + error.setOwnerId(context.getOwnerId()); + error.setDocumentTransition(documentTransition); + + result.addError(error); + } + + return result; +} + +module.exports = createContactRequestDataTrigger; diff --git a/packages/js-dpp/lib/dataTrigger/dpnsTriggers/createDomainDataTrigger.js b/packages/js-dpp/lib/dataTrigger/dpnsTriggers/createDomainDataTrigger.js new file mode 100644 index 00000000000..958cbb43b00 --- /dev/null +++ b/packages/js-dpp/lib/dataTrigger/dpnsTriggers/createDomainDataTrigger.js @@ -0,0 +1,237 @@ +const hashModule = require('../../util/hash'); + +const DataTriggerExecutionResult = require('../DataTriggerExecutionResult'); +const DataTriggerConditionError = require('../../errors/consensus/state/dataContract/dataTrigger/DataTriggerConditionError'); + +const MAX_PRINTABLE_DOMAIN_NAME_LENGTH = 253; + +/** + * Data trigger for domain creation process + * + * @param {DocumentCreateTransition} documentTransition + * @param {DataTriggerExecutionContext} context + * @param {Identifier|Buffer} topLevelIdentity + * + * @return {Promise} + */ +async function createDomainDataTrigger(documentTransition, context, topLevelIdentity) { + const { + label, + normalizedLabel, + normalizedParentDomainName, + preorderSalt, + records, + subdomainRules, + } = documentTransition.getData(); + + const result = new DataTriggerExecutionResult(); + + const isDryRun = context.getStateTransitionExecutionContext().isDryRun(); + + let fullDomainName = normalizedLabel; + if (normalizedParentDomainName.length > 0) { + fullDomainName = `${normalizedLabel}.${normalizedParentDomainName}`; + } + + if (!isDryRun) { + if (fullDomainName.length > MAX_PRINTABLE_DOMAIN_NAME_LENGTH) { + const error = new DataTriggerConditionError( + context.getDataContract() + .getId() + .toBuffer(), + documentTransition.getId() + .toBuffer(), + `Full domain name length can not be more than ${MAX_PRINTABLE_DOMAIN_NAME_LENGTH} characters long but got ${fullDomainName.length}`, + ); + + error.setOwnerId(context.getOwnerId()); + error.setDocumentTransition(documentTransition); + + result.addError(error); + } + + if (normalizedLabel !== label.toLowerCase()) { + const error = new DataTriggerConditionError( + context.getDataContract() + .getId() + .toBuffer(), + documentTransition.getId() + .toBuffer(), + 'Normalized label doesn\'t match label', + ); + + error.setOwnerId(context.getOwnerId()); + error.setDocumentTransition(documentTransition); + + result.addError(error); + } + + if (records.dashUniqueIdentityId + && !context.getOwnerId() + .equals(records.dashUniqueIdentityId) + ) { + const error = new DataTriggerConditionError( + context.getDataContract() + .getId() + .toBuffer(), + documentTransition.getId() + .toBuffer(), + `ownerId ${context.getOwnerId()} doesn't match dashUniqueIdentityId ${records.dashUniqueIdentityId}`, + ); + + error.setOwnerId(context.getOwnerId()); + error.setDocumentTransition(documentTransition); + + result.addError(error); + } + + if (records.dashAliasIdentityId + && !context.getOwnerId() + .equals(records.dashAliasIdentityId)) { + const error = new DataTriggerConditionError( + context.getDataContract() + .getId() + .toBuffer(), + documentTransition.getId() + .toBuffer(), + `ownerId ${context.getOwnerId()} doesn't match dashAliasIdentityId ${records.dashAliasIdentityId}`, + ); + + error.setOwnerId(context.getOwnerId()); + error.setDocumentTransition(documentTransition); + + result.addError(error); + } + + if (normalizedParentDomainName.length === 0 + && !context.getOwnerId() + .equals(topLevelIdentity)) { + const error = new DataTriggerConditionError( + context.getDataContract() + .getId() + .toBuffer(), + documentTransition.getId() + .toBuffer(), + 'Can\'t create top level domain for this identity', + ); + + error.setOwnerId(context.getOwnerId()); + error.setDocumentTransition(documentTransition); + + result.addError(error); + } + } + + if (normalizedParentDomainName.length > 0) { + const parentDomainSegments = normalizedParentDomainName.split('.'); + + const parentDomainLabel = parentDomainSegments[0]; + const grandParentDomainName = parentDomainSegments.slice(1) + .join('.'); + + const [parentDomain] = await context.getStateRepository() + .fetchDocuments( + context.getDataContract() + .getId(), + documentTransition.getType(), + { + where: [ + ['normalizedParentDomainName', '==', grandParentDomainName], + ['normalizedLabel', '==', parentDomainLabel], + ], + }, + context.getStateTransitionExecutionContext(), + ); + + if (!isDryRun) { + if (!parentDomain) { + const error = new DataTriggerConditionError( + context.getDataContract() + .getId() + .toBuffer(), + documentTransition.getId() + .toBuffer(), + 'Parent domain is not present', + ); + + error.setOwnerId(context.getOwnerId()); + error.setDocumentTransition(documentTransition); + + result.addError(error); + + return result; + } + + if (subdomainRules.allowSubdomains === true) { + const error = new DataTriggerConditionError( + context.getDataContract() + .getId() + .toBuffer(), + documentTransition.getId() + .toBuffer(), + 'Allowing subdomains registration is forbidden for non top level domains', + ); + + error.setOwnerId(context.getOwnerId()); + error.setDocumentTransition(documentTransition); + + result.addError(error); + } + + if (parentDomain.getData().subdomainRules.allowSubdomains === false + && !context.getOwnerId() + .equals(parentDomain.getOwnerId())) { + const error = new DataTriggerConditionError( + context.getDataContract() + .getId() + .toBuffer(), + documentTransition.getId() + .toBuffer(), + 'The subdomain can be created only by the parent domain owner', + ); + + error.setOwnerId(context.getOwnerId()); + error.setDocumentTransition(documentTransition); + + result.addError(error); + } + } + } + + const saltedDomainBuffer = Buffer.concat([ + preorderSalt, + Buffer.from(fullDomainName), + ]); + + const { hash } = hashModule; + const saltedDomainHash = hash(saltedDomainBuffer); + + const [preorderDocument] = await context.getStateRepository() + .fetchDocuments( + context.getDataContract().getId(), + 'preorder', + { where: [['saltedDomainHash', '==', saltedDomainHash]] }, + context.getStateTransitionExecutionContext(), + ); + + if (isDryRun) { + return result; + } + + if (!preorderDocument) { + const error = new DataTriggerConditionError( + context.getDataContract().getId().toBuffer(), + documentTransition.getId().toBuffer(), + 'preorderDocument was not found', + ); + + error.setOwnerId(context.getOwnerId()); + error.setDocumentTransition(documentTransition); + + result.addError(error); + } + + return result; +} + +module.exports = createDomainDataTrigger; diff --git a/packages/js-dpp/lib/dataTrigger/featureFlagsDataTriggers/createFeatureFlagDataTrigger.js b/packages/js-dpp/lib/dataTrigger/featureFlagsDataTriggers/createFeatureFlagDataTrigger.js new file mode 100644 index 00000000000..52060f991fd --- /dev/null +++ b/packages/js-dpp/lib/dataTrigger/featureFlagsDataTriggers/createFeatureFlagDataTrigger.js @@ -0,0 +1,54 @@ +const Long = require('long'); + +const DataTriggerConditionError = require('../../errors/consensus/state/dataContract/dataTrigger/DataTriggerConditionError'); +const DataTriggerExecutionResult = require('../DataTriggerExecutionResult'); + +/** + * @param {DocumentCreateTransition} documentTransition + * @param {DataTriggerExecutionContext} context + * @param {Identifier} topLevelIdentityId + * @return {Promise} + */ +async function createFeatureFlagDataTrigger(documentTransition, context, topLevelIdentityId) { + const result = new DataTriggerExecutionResult(); + + if (context.getStateTransitionExecutionContext().isDryRun()) { + return result; + } + + const stateRepository = context.getStateRepository(); + + const { height: blockHeight } = await stateRepository.fetchLatestPlatformBlockHeader(); + + if (Long.fromNumber(documentTransition.get('enableAtHeight')).lt(blockHeight)) { + const error = new DataTriggerConditionError( + context.getDataContract().getId().toBuffer(), + documentTransition.getId().toBuffer(), + `Feature flag cannot be enabled in the past on block ${documentTransition.get('enableAtHeight')}. Current block height is ${blockHeight}`, + ); + + error.setOwnerId(context.getOwnerId()); + error.setDocumentTransition(documentTransition); + + result.addError(error); + + return result; + } + + if (!context.getOwnerId().equals(topLevelIdentityId)) { + const error = new DataTriggerConditionError( + context.getDataContract().getId().toBuffer(), + documentTransition.getId().toBuffer(), + 'This identity can\'t activate selected feature flag', + ); + + error.setOwnerId(context.getOwnerId()); + error.setDocumentTransition(documentTransition); + + result.addError(error); + } + + return result; +} + +module.exports = createFeatureFlagDataTrigger; diff --git a/packages/js-dpp/lib/dataTrigger/getDataTriggersFactory.js b/packages/js-dpp/lib/dataTrigger/getDataTriggersFactory.js new file mode 100644 index 00000000000..5036a6c435c --- /dev/null +++ b/packages/js-dpp/lib/dataTrigger/getDataTriggersFactory.js @@ -0,0 +1,147 @@ +const featureFlagTypes = require('@dashevo/feature-flags-contract/lib/featureFlagTypes'); +const featureFlagsSystemIds = require('@dashevo/feature-flags-contract/lib/systemIds'); + +const dpnsSystemIds = require('@dashevo/dpns-contract/lib/systemIds'); +const dashpaySystemIds = require('@dashevo/dashpay-contract/lib/systemIds'); +const masternodeRewardSharesSystemIds = require('@dashevo/masternode-reward-shares-contract/lib/systemIds'); + +const Identifier = require('../identifier/Identifier'); + +const AbstractDocumentTransition = require('../document/stateTransition/DocumentsBatchTransition/documentTransition/AbstractDocumentTransition'); + +const DataTrigger = require('./DataTrigger'); + +const rejectDataTrigger = require('./rejectDataTrigger'); +const createDomainDataTrigger = require('./dpnsTriggers/createDomainDataTrigger'); +const createContactRequestDataTrigger = require('./dashpayDataTriggers/createContactRequestDataTrigger'); +const createFeatureFlagDataTrigger = require('./featureFlagsDataTriggers/createFeatureFlagDataTrigger'); +const createMasternodeRewardSharesDataTrigger = require('./rewardShareDataTriggers/createMasternodeRewardSharesDataTrigger'); + +/** + * Get respective data triggers (factory) + * + * @return {getDataTriggers} + */ +function getDataTriggersFactory() { + const dpnsDataContractId = Identifier.from(dpnsSystemIds.contractId); + const dpnsOwnerId = Identifier.from(dpnsSystemIds.ownerId); + + const dashPayDataContractId = Identifier.from(dashpaySystemIds.contractId); + + const featureFlagsDataContractId = Identifier.from(featureFlagsSystemIds.contractId); + const featureFlagsOwnerId = Identifier.from( + featureFlagsSystemIds.ownerId, + ); + + const masternodeRewardSharesContractId = Identifier.from( + masternodeRewardSharesSystemIds.contractId, + ); + + const dataTriggers = [ + new DataTrigger( + dpnsDataContractId, + 'domain', + AbstractDocumentTransition.ACTIONS.CREATE, + createDomainDataTrigger, + dpnsOwnerId, + ), + new DataTrigger( + dpnsDataContractId, + 'domain', + AbstractDocumentTransition.ACTIONS.REPLACE, + rejectDataTrigger, + ), + new DataTrigger( + dpnsDataContractId, + 'domain', + AbstractDocumentTransition.ACTIONS.DELETE, + rejectDataTrigger, + ), + new DataTrigger( + dpnsDataContractId, + 'preorder', + AbstractDocumentTransition.ACTIONS.REPLACE, + rejectDataTrigger, + ), + new DataTrigger( + dpnsDataContractId, + 'preorder', + AbstractDocumentTransition.ACTIONS.DELETE, + rejectDataTrigger, + ), + new DataTrigger( + dashPayDataContractId, + 'contactRequest', + AbstractDocumentTransition.ACTIONS.CREATE, + createContactRequestDataTrigger, + ), + new DataTrigger( + dashPayDataContractId, + 'contactRequest', + AbstractDocumentTransition.ACTIONS.REPLACE, + rejectDataTrigger, + ), + new DataTrigger( + dashPayDataContractId, + 'contactRequest', + AbstractDocumentTransition.ACTIONS.DELETE, + rejectDataTrigger, + ), + new DataTrigger( + featureFlagsDataContractId, + featureFlagTypes.UPDATE_CONSENSUS_PARAMS, + AbstractDocumentTransition.ACTIONS.CREATE, + createFeatureFlagDataTrigger, + featureFlagsOwnerId, + ), + new DataTrigger( + featureFlagsDataContractId, + featureFlagTypes.UPDATE_CONSENSUS_PARAMS, + AbstractDocumentTransition.ACTIONS.REPLACE, + rejectDataTrigger, + ), + new DataTrigger( + featureFlagsDataContractId, + featureFlagTypes.UPDATE_CONSENSUS_PARAMS, + AbstractDocumentTransition.ACTIONS.DELETE, + rejectDataTrigger, + ), + new DataTrigger( + masternodeRewardSharesContractId, + 'rewardShare', + AbstractDocumentTransition.ACTIONS.CREATE, + createMasternodeRewardSharesDataTrigger, + ), + new DataTrigger( + masternodeRewardSharesContractId, + 'rewardShare', + AbstractDocumentTransition.ACTIONS.REPLACE, + createMasternodeRewardSharesDataTrigger, + ), + ]; + + /** + * Get respective data triggers + * + * @typedef getDataTriggers + * + * @param {Identifier|Buffer} dataContractId + * @param {string} documentType + * @param {number} transitionAction + * + * @returns {DataTrigger[]} + */ + function getDataTriggers(dataContractId, documentType, transitionAction) { + return dataTriggers.filter( + (dataTrigger) => dataTrigger.isMatchingTriggerForData( + dataContractId, + documentType, + transitionAction, + ), + ); + } + + return getDataTriggers; +} + +module.exports = getDataTriggersFactory; diff --git a/packages/js-dpp/lib/dataTrigger/rejectDataTrigger.js b/packages/js-dpp/lib/dataTrigger/rejectDataTrigger.js new file mode 100644 index 00000000000..fd5ae7119f0 --- /dev/null +++ b/packages/js-dpp/lib/dataTrigger/rejectDataTrigger.js @@ -0,0 +1,26 @@ +const DataTriggerExecutionResult = require('./DataTriggerExecutionResult'); +const DataTriggerConditionError = require('../errors/consensus/state/dataContract/dataTrigger/DataTriggerConditionError'); + +/** + * Data trigger for domain deletion process + * + * @param {DocumentDeleteTransition} documentTransition + * @param {DataTriggerExecutionContext} context + * + * @return {Promise} + */ +async function rejectDataTrigger(documentTransition, context) { + const result = new DataTriggerExecutionResult(); + + result.addError( + new DataTriggerConditionError( + documentTransition.getId().toBuffer(), + context.getDataContract().getId().toBuffer(), + 'Action is not allowed', + ), + ); + + return result; +} + +module.exports = rejectDataTrigger; diff --git a/packages/js-dpp/lib/dataTrigger/rewardShareDataTriggers/createMasternodeRewardSharesDataTrigger.js b/packages/js-dpp/lib/dataTrigger/rewardShareDataTriggers/createMasternodeRewardSharesDataTrigger.js new file mode 100644 index 00000000000..a82b5e71a7b --- /dev/null +++ b/packages/js-dpp/lib/dataTrigger/rewardShareDataTriggers/createMasternodeRewardSharesDataTrigger.js @@ -0,0 +1,129 @@ +const DataTriggerConditionError = require('../../errors/consensus/state/dataContract/dataTrigger/DataTriggerConditionError'); +const DataTriggerExecutionResult = require('../DataTriggerExecutionResult'); + +const MAX_PERCENTAGE = 10000; +const MAX_DOCUMENTS = 16; + +/** + * @param {DocumentCreateTransition} documentTransition + * @param {DataTriggerExecutionContext} context + * @return {Promise} + */ +async function createMasternodeRewardSharesDataTrigger( + documentTransition, + context, +) { + const { + payToId, + percentage, + } = documentTransition.getData(); + + const ownerId = context.getOwnerId(); + + const result = new DataTriggerExecutionResult(); + + const isDryRun = context.getStateTransitionExecutionContext().isDryRun(); + + if (!isDryRun) { + // Do not allow creating document if ownerId is not in SML + const smlStore = await context.getStateRepository() + .fetchSMLStore(); + const validMasternodesList = smlStore.getCurrentSML() + .getValidMasternodesList(); + + const ownerIdInSml = !!validMasternodesList.find( + (smlEntry) => Buffer.compare(ownerId, Buffer.from(smlEntry.proRegTxHash, 'hex')) === 0, + ); + + if (!ownerIdInSml) { + const error = new DataTriggerConditionError( + context.getDataContract() + .getId() + .toBuffer(), + documentTransition.getId() + .toBuffer(), + 'Only masternode identities can share rewards', + ); + + error.setOwnerId(ownerId); + error.setDocumentTransition(documentTransition); + + result.addError(error); + + return result; + } + } + + // payToId identity exists + const identity = await context.getStateRepository().fetchIdentity( + payToId, + context.getStateTransitionExecutionContext(), + ); + + if (!isDryRun) { + if (identity === null) { + const error = new DataTriggerConditionError( + context.getDataContract() + .getId() + .toBuffer(), + documentTransition.getId() + .toBuffer(), + `Identity ${payToId.toString()} doesn't exist`, + ); + + error.setOwnerId(ownerId); + error.setDocumentTransition(documentTransition); + + result.addError(error); + + return result; + } + } + + // The overall percentage for ownerId is not more than 10000 + const documents = await context.getStateRepository().fetchDocuments( + context.getDataContract().getId(), + documentTransition.getType(), + { + where: [ + ['$ownerId', '==', ownerId], + ], + }, + context.getStateTransitionExecutionContext(), + ); + + if (documents.length === MAX_DOCUMENTS) { + const error = new DataTriggerConditionError( + context.getDataContract().getId().toBuffer(), + documentTransition.getId().toBuffer(), + `Reward shares cannot contain more than ${MAX_DOCUMENTS} identities`, + ); + result.addError(error); + + return result; + } + + if (isDryRun) { + return result; + } + + const totalPercent = documents + .reduce((prevValue, document) => prevValue + document.data.percentage, percentage); + + if (totalPercent > MAX_PERCENTAGE) { + const error = new DataTriggerConditionError( + context.getDataContract().getId().toBuffer(), + documentTransition.getId().toBuffer(), + `Percentage can not be more than ${MAX_PERCENTAGE}`, + ); + + error.setOwnerId(ownerId); + error.setDocumentTransition(documentTransition); + + result.addError(error); + } + + return result; +} + +module.exports = createMasternodeRewardSharesDataTrigger; diff --git a/packages/js-dpp/lib/decodeProtocolEntityFactory.js b/packages/js-dpp/lib/decodeProtocolEntityFactory.js new file mode 100644 index 00000000000..57ce3d07119 --- /dev/null +++ b/packages/js-dpp/lib/decodeProtocolEntityFactory.js @@ -0,0 +1,44 @@ +const ProtocolVersionParsingError = require('./errors/consensus/basic/decode/ProtocolVersionParsingError'); +const SerializedObjectParsingError = require('./errors/consensus/basic/decode/SerializedObjectParsingError'); + +const { decode } = require('./util/serializer'); + +function decodeProtocolEntityFactory() { + /** + * @typedef {decodeProtocolEntity} + * @param {Buffer} buffer + * @return {[number, Object]} + */ + function decodeProtocolEntity(buffer) { + // Parse protocol version from the first 4 bytes + let protocolVersion; + try { + protocolVersion = buffer.slice(0, 4).readUInt32LE(0); + } catch (error) { + const consensusError = new ProtocolVersionParsingError(error.message); + + consensusError.setParsingError(error); + + throw consensusError; + } + + let rawEntity; + try { + rawEntity = decode( + buffer.slice(4, buffer.length), + ); + } catch (error) { + const consensusError = new SerializedObjectParsingError(error.message); + + consensusError.setParsingError(error); + + throw consensusError; + } + + return [protocolVersion, rawEntity]; + } + + return decodeProtocolEntity; +} + +module.exports = decodeProtocolEntityFactory; diff --git a/packages/js-dpp/lib/document/Document.js b/packages/js-dpp/lib/document/Document.js new file mode 100644 index 00000000000..5613abf778b --- /dev/null +++ b/packages/js-dpp/lib/document/Document.js @@ -0,0 +1,415 @@ +const lodashGet = require('lodash.get'); +const lodashSet = require('lodash.set'); +const lodashCloneDeepWith = require('lodash.clonedeepwith'); + +const cloneDeepWithIdentifiers = require('../util/cloneDeepWithIdentifiers'); + +const hashModule = require('../util/hash'); +const serializer = require('../util/serializer'); +const Identifier = require('../identifier/Identifier'); + +class Document { + /** + * @param {RawDocument} rawDocument + * @param {DataContract} dataContract + */ + constructor(rawDocument, dataContract) { + this.dataContract = dataContract; + + const data = { ...rawDocument }; + + this.entropy = undefined; + + if (Object.prototype.hasOwnProperty.call(rawDocument, '$protocolVersion')) { + this.protocolVersion = rawDocument.$protocolVersion; + delete data.$protocolVersion; + } + + if (Object.prototype.hasOwnProperty.call(rawDocument, '$id')) { + this.id = Identifier.from(rawDocument.$id); + delete data.$id; + } + + if (Object.prototype.hasOwnProperty.call(rawDocument, '$type')) { + this.type = rawDocument.$type; + delete data.$type; + } + + if (Object.prototype.hasOwnProperty.call(rawDocument, '$dataContractId')) { + this.dataContractId = Identifier.from(rawDocument.$dataContractId); + delete data.$dataContractId; + } + + if (Object.prototype.hasOwnProperty.call(rawDocument, '$ownerId')) { + this.ownerId = Identifier.from(rawDocument.$ownerId); + delete data.$ownerId; + } + + if (Object.prototype.hasOwnProperty.call(rawDocument, '$revision')) { + this.revision = rawDocument.$revision; + delete data.$revision; + } + + if (Object.prototype.hasOwnProperty.call(rawDocument, '$createdAt')) { + this.createdAt = new Date(rawDocument.$createdAt); + delete data.$createdAt; + } + + if (Object.prototype.hasOwnProperty.call(rawDocument, '$updatedAt')) { + this.updatedAt = new Date(rawDocument.$updatedAt); + delete data.$updatedAt; + } + + this.setData(data); + } + + /** + * Get Document protocol version + * + * @returns {number} + */ + getProtocolVersion() { + return this.protocolVersion; + } + + /** + * Get ID + * + * @return {Identifier} + */ + getId() { + return this.id; + } + + /** + * Get type + * + * @return {string} + */ + getType() { + return this.type; + } + + /** + * Get Data Contract ID + * + * @return {Identifier} + */ + getDataContractId() { + return this.dataContractId; + } + + /** + * Get Data Contract + * + * @return {DataContract} + */ + getDataContract() { + return this.dataContract; + } + + /** + * Get Owner ID + * + * @return {Identifier} + */ + getOwnerId() { + return this.ownerId; + } + + /** + * Set revision + * + * @param {number} revision + * @return Document + */ + setRevision(revision) { + this.revision = revision; + + return this; + } + + /** + * Get revision + * + * @return {number} + */ + getRevision() { + return this.revision; + } + + /** + * Set entropy + * + * @param {Buffer} entropy + */ + setEntropy(entropy) { + this.entropy = entropy; + } + + /** + * Get entropy + * + * @return {Buffer} + */ + getEntropy() { + return this.entropy; + } + + /** + * Set data + * + * @param {Object} data + * @return {Document} + */ + setData(data) { + this.data = {}; + + Object.entries(data) + .forEach(([name, value]) => this.set(name, value)); + + return this; + } + + /** + * Get data + * + * @return {Object} + */ + getData() { + return this.data; + } + + /** + * Retrieves the field specified by {path} + * + * @param {string} path + * @return {*} + */ + get(path) { + return lodashGet(this.data, path); + } + + /** + * Set the field specified by {path} + * + * @param {string} path + * @param {*} value + * @return {Document} + */ + set(path, value) { + let clonedValue = cloneDeepWithIdentifiers(value); + + const binaryProperties = this.dataContract.getBinaryProperties( + this.getType(), + ); + + Object.entries(binaryProperties) + .filter(([, property]) => property.contentMediaType === Identifier.MEDIA_TYPE) + .forEach(([propertyPath]) => { + if (path === propertyPath) { + clonedValue = Identifier.from(value); + } else if (propertyPath.includes(path)) { + // in case of object we need to remove + // first dot as we removed beginning of the path + // e.g. `1.2.3` and '1.2` in the result would be `.2` + const partialPath = propertyPath.substring(path.length + 1, propertyPath.length); + const buffer = lodashGet(clonedValue, partialPath); + + if (buffer !== undefined) { + lodashSet( + clonedValue, + partialPath, + Identifier.from(buffer), + ); + } + } + }); + + lodashSet(this.data, path, clonedValue); + + return this; + } + + /** + * Set document creation date + * + * @param {Date} date + * @return {Document} + */ + setCreatedAt(date) { + this.createdAt = date; + + return this; + } + + /** + * Get document creation date + * + * @return {Date} + */ + getCreatedAt() { + return this.createdAt; + } + + /** + * Set document updated date + * + * @param {Date} date + * @return {Document} + */ + setUpdatedAt(date) { + this.updatedAt = date; + + return this; + } + + /** + * Get document updated date + * + * @return {Date} + */ + getUpdatedAt() { + return this.updatedAt; + } + + /** + * Set metadata + * @param {Metadata} metadata + */ + setMetadata(metadata) { + this.metadata = metadata; + } + + /** + * Get metadata + * @returns {Metadata|null} + */ + getMetadata() { + return this.metadata; + } + + /** + * Return Document as plain object + * + * @param {Object} [options] + * @param {boolean} [options.skipIdentifiersConversion=false] + * @return {RawDocument} + */ + toObject(options = {}) { + Object.assign( + options, + { + skipIdentifiersConversion: false, + ...options, + }, + ); + + const rawDocument = { + $protocolVersion: this.getProtocolVersion(), + $id: this.getId(), + $type: this.getType(), + $dataContractId: this.getDataContractId(), + $ownerId: this.getOwnerId(), + $revision: this.getRevision(), + ...this.getData(), + }; + + if (this.createdAt) { + rawDocument.$createdAt = this.getCreatedAt().getTime(); + } + + if (this.updatedAt) { + rawDocument.$updatedAt = this.getUpdatedAt().getTime(); + } + + if (!options.skipIdentifiersConversion) { + rawDocument.$id = this.getId().toBuffer(); + rawDocument.$dataContractId = this.getDataContractId().toBuffer(); + rawDocument.$ownerId = this.getOwnerId().toBuffer(); + + // eslint-disable-next-line consistent-return + return lodashCloneDeepWith(rawDocument, (value) => { + if (value instanceof Identifier) { + return value.toBuffer(); + } + }); + } + + return rawDocument; + } + + /** + * Return Document as JSON object + * + * @return {JsonDocument} + */ + toJSON() { + const rawDocument = this.toObject({ skipIdentifiersConversion: true }); + + // eslint-disable-next-line consistent-return + return lodashCloneDeepWith(rawDocument, (value) => { + if (value instanceof Identifier) { + return value.toString(); + } + + if (Buffer.isBuffer(value)) { + return value.toString('base64'); + } + }); + } + + /** + * Return serialized Document + * + * @return {Buffer} + */ + toBuffer() { + const serializedData = this.toObject(); + delete serializedData.$protocolVersion; + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(this.getProtocolVersion(), 0); + + return Buffer.concat([protocolVersionUInt32, serializer.encode(serializedData)]); + } + + /** + * Returns hex string with object hash + * + * @return {Buffer} + */ + hash() { + const { hash } = hashModule; + + return hash(this.toBuffer()); + } +} + +/** + * @typedef {Object} RawDocument + * @property {number} $protocolVersion + * @property {Buffer} $id + * @property {string} $type + * @property {Buffer} $dataContractId + * @property {Buffer} $ownerId + * @property {number} $revision + * @property {number} [$createdAt] + * @property {number} [$updatedAt] + */ + +/** + * @typedef {Object} JsonDocument + * @property {number} $protocolVersion + * @property {string} $id + * @property {string} $type + * @property {string} $dataContractId + * @property {string} $ownerId + * @property {number} $revision + * @property {number} [$createdAt] + * @property {number} [$updatedAt] + */ + +Document.SYSTEM_PREFIX = '$'; + +module.exports = Document; diff --git a/packages/js-dpp/lib/document/DocumentFacade.js b/packages/js-dpp/lib/document/DocumentFacade.js new file mode 100644 index 00000000000..44f82395ebd --- /dev/null +++ b/packages/js-dpp/lib/document/DocumentFacade.js @@ -0,0 +1,149 @@ +const enrichDataContractWithBaseSchema = require('../dataContract/enrichDataContractWithBaseSchema'); +const validateDocumentFactory = require('./validation/validateDocumentFactory'); +const fetchAndValidateDataContractFactory = require('./fetchAndValidateDataContractFactory'); + +const Document = require('./Document'); +const DocumentFactory = require('./DocumentFactory'); + +const MissingOptionError = require('../errors/MissingOptionError'); +const decodeProtocolEntityFactory = require('../decodeProtocolEntityFactory'); + +const protocolVersion = require('../version/protocolVersion'); +const validateProtocolVersionFactory = require('../version/validateProtocolVersionFactory'); + +class DocumentFacade { + /** + * @param {DashPlatformProtocol} dpp + */ + constructor(dpp) { + this.stateRepository = dpp.getStateRepository(); + + const validateProtocolVersion = validateProtocolVersionFactory( + dpp, + protocolVersion.compatibility, + ); + + this.validateDocument = validateDocumentFactory( + dpp.getJsonSchemaValidator(), + enrichDataContractWithBaseSchema, + validateProtocolVersion, + ); + + this.fetchAndValidateDataContract = fetchAndValidateDataContractFactory( + this.stateRepository, + ); + + const decodeProtocolEntity = decodeProtocolEntityFactory(); + + this.factory = new DocumentFactory( + dpp, + this.validateDocument, + this.fetchAndValidateDataContract, + decodeProtocolEntity, + ); + } + + /** + * Create Document + * + * @param {DataContract} dataContract + * @param {Identifier|Buffer} ownerId + * @param {string} type + * @param {Object} [data] + * @return {Document} + */ + create(dataContract, ownerId, type, data = {}) { + return this.factory.create(dataContract, ownerId, type, data); + } + + /** + * Create Document from plain object + * + * @param {RawDocument} rawDocument + * @param {Object} options + * @param {boolean} [options.skipValidation=false] + * @param {boolean} [options.action] + * @return {Promise} + */ + async createFromObject(rawDocument, options = {}) { + if (!this.stateRepository && !options.skipValidation) { + throw new MissingOptionError( + 'stateRepository', + 'Can\'t create Document because State Repository is not set in' + + ' DashPlatformProtocol options', + ); + } + + return this.factory.createFromObject(rawDocument, options); + } + + /** + * Create Document from buffer + * + * @param {Buffer} buffer + * @param {Object} options + * @param {boolean} [options.skipValidation=false] + * @param {boolean} [options.action] + * @return {Promise} + */ + async createFromBuffer(buffer, options = { }) { + if (!this.stateRepository && !options.skipValidation) { + throw new MissingOptionError( + 'stateRepository', + 'Can\'t create Document because State Repository is not set in' + + ' DashPlatformProtocol options', + ); + } + + return this.factory.createFromBuffer(buffer, options); + } + + /** + * Create Documents State Transition + * + * @param {Object} documents + * @param {Document[]} [documents.create] + * @param {Document[]} [documents.replace] + * @param {Document[]} [documents.delete] + * + * @return {DocumentsBatchTransition} + */ + createStateTransition(documents) { + return this.factory.createStateTransition(documents); + } + + /** + * Validate document + * + * @param {Document|RawDocument} document + * @return {Promise} + */ + async validate(document) { + if (!this.stateRepository) { + throw new MissingOptionError( + 'stateRepository', + 'Can\'t validate Document because State Repository is not set in' + + ' DashPlatformProtocol options', + ); + } + + let rawDocument; + if (document instanceof Document) { + rawDocument = document.toObject(); + } else { + rawDocument = document; + } + + const result = await this.fetchAndValidateDataContract(rawDocument); + + if (!result.isValid()) { + return result; + } + + const dataContract = result.getData(); + + return this.validateDocument(rawDocument, dataContract); + } +} + +module.exports = DocumentFacade; diff --git a/packages/js-dpp/lib/document/DocumentFactory.js b/packages/js-dpp/lib/document/DocumentFactory.js new file mode 100644 index 00000000000..6c2ae48d4c1 --- /dev/null +++ b/packages/js-dpp/lib/document/DocumentFactory.js @@ -0,0 +1,326 @@ +const Document = require('./Document'); + +const entropyGenerator = require('../util/entropyGenerator'); +const generateDocumentId = require('./generateDocumentId'); + +const DocumentsBatchTransition = require('./stateTransition/DocumentsBatchTransition/DocumentsBatchTransition'); + +const AbstractDocumentTransition = require('./stateTransition/DocumentsBatchTransition/documentTransition/AbstractDocumentTransition'); +const DocumentCreateTransition = require('./stateTransition/DocumentsBatchTransition/documentTransition/DocumentCreateTransition'); + +const AbstractConsensusError = require('../errors/consensus/AbstractConsensusError'); +const InvalidActionNameError = require('./errors/InvalidActionNameError'); +const NoDocumentsSuppliedError = require('./errors/NoDocumentsSuppliedError'); +const MismatchOwnerIdsError = require('./errors/MismatchOwnerIdsError'); +const InvalidInitialRevisionError = require('./errors/InvalidInitialRevisionError'); +const InvalidDocumentError = require('./errors/InvalidDocumentError'); +const InvalidDocumentTypeError = require('../errors/InvalidDocumentTypeError'); + +class DocumentFactory { + /** + * @param {DashPlatformProtocol} dpp + * @param {validateDocument} validateDocument + * @param {fetchAndValidateDataContract} fetchAndValidateDataContract + * @param {decodeProtocolEntity} decodeProtocolEntity + */ + constructor( + dpp, + validateDocument, + fetchAndValidateDataContract, + decodeProtocolEntity, + ) { + this.dpp = dpp; + this.validateDocument = validateDocument; + this.fetchAndValidateDataContract = fetchAndValidateDataContract; + this.decodeProtocolEntity = decodeProtocolEntity; + } + + /** + * Create Document + * + * @param {DataContract} dataContract + * @param {Identifier|Buffer} ownerId + * @param {string} type + * @param {Object} [data] + * @return {Document} + */ + create(dataContract, ownerId, type, data = {}) { + if (!dataContract.isDocumentDefined(type)) { + throw new InvalidDocumentTypeError(type, dataContract); + } + + const documentEntropy = entropyGenerator.generate(); + const dataContractId = dataContract.getId(); + + const id = generateDocumentId( + dataContractId, + ownerId, + type, + documentEntropy, + ); + + const rawDocument = { + $protocolVersion: this.dpp.getProtocolVersion(), + $id: id, + $type: type, + $dataContractId: dataContractId, + $ownerId: ownerId, + $revision: DocumentCreateTransition.INITIAL_REVISION, + ...data, + }; + + // We should set timestamps + // Only if they are required by the contract + const { required: documentRequiredFields } = dataContract.getDocumentSchema(type); + + const creationTime = new Date().getTime(); + + if (documentRequiredFields + && documentRequiredFields.includes('$createdAt')) { + rawDocument.$createdAt = creationTime; + } + + if (documentRequiredFields + && documentRequiredFields.includes('$updatedAt')) { + rawDocument.$updatedAt = creationTime; + } + + const result = this.validateDocument( + rawDocument, + dataContract, + ); + + if (!result.isValid()) { + throw new InvalidDocumentError(result.getErrors(), rawDocument); + } + + const document = new Document(rawDocument, dataContract); + + document.setEntropy(documentEntropy); + + return document; + } + + /** + * Create Document from plain object + * + * @param {RawDocument} rawDocument + * @param {Object} options + * @param {boolean} [options.skipValidation=false] + * @param {boolean} [options.action] + * @return {Document} + */ + async createFromObject(rawDocument, options = {}) { + const dataContract = await this.validateDataContractForDocument(rawDocument, options); + + return new Document(rawDocument, dataContract); + } + + /** + * @private + * + * @param {RawDocument} rawDocument + * @param {Object} options + * @param {boolean} [options.skipValidation=false] + * @param {boolean} [options.action] + * + * @return {Promise} + */ + async validateDataContractForDocument(rawDocument, options = {}) { + const opts = { skipValidation: false, ...options }; + + const result = await this.fetchAndValidateDataContract(rawDocument); + + if (!result.isValid()) { + throw new InvalidDocumentError(result.getErrors(), rawDocument); + } + + const dataContract = result.getData(); + + if (!opts.skipValidation) { + result.merge( + this.validateDocument( + rawDocument, + dataContract, + ), + ); + + if (!result.isValid()) { + throw new InvalidDocumentError(result.getErrors(), rawDocument); + } + } + + return dataContract; + } + + /** + * Create Document from buffer + * + * @param {Buffer} buffer + * @param {Object} options + * @param {boolean} [options.skipValidation=false] + * @param {boolean} [options.action] + * @return {Promise} + */ + async createFromBuffer(buffer, options = { }) { + let rawDocument; + let protocolVersion; + + try { + [protocolVersion, rawDocument] = this.decodeProtocolEntity( + buffer, + ); + + rawDocument.$protocolVersion = protocolVersion; + } catch (error) { + if (error instanceof AbstractConsensusError) { + throw new InvalidDocumentError([error]); + } + + throw error; + } + + return this.createFromObject(rawDocument, options); + } + + /** + * Create Documents State Transition + * + * @param {Object} documents + * @param {Document[]} [documents.create] + * @param {Document[]} [documents.replace] + * @param {Document[]} [documents.delete] + * + * @return {DocumentsBatchTransition} + */ + createStateTransition(documents) { + // Check no wrong actions were supplied + const allowedKeys = Object.values(AbstractDocumentTransition.ACTION_NAMES); + + const actionKeys = Object.keys(documents); + const filteredKeys = actionKeys + .filter((key) => allowedKeys.indexOf(key) === -1); + + if (filteredKeys.length > 0) { + throw new InvalidActionNameError(filteredKeys); + } + + const documentsFlattened = actionKeys + .reduce((all, t) => all.concat(documents[t]), []); + + if (documentsFlattened.length === 0) { + throw new NoDocumentsSuppliedError(); + } + + // Check that documents are not mixed + const [aDocument] = documentsFlattened; + + const ownerId = aDocument.getOwnerId(); + + const mismatchedOwnerIdsLength = documentsFlattened + .reduce((result, document) => { + if (!document.getOwnerId().equals(ownerId)) { + // eslint-disable-next-line no-param-reassign + result += 1; + } + + return result; + }, 0); + + if (mismatchedOwnerIdsLength > 0) { + throw new MismatchOwnerIdsError(documentsFlattened); + } + + // Convert documents to action transitions + const { + [AbstractDocumentTransition.ACTION_NAMES.CREATE]: createDocuments, + [AbstractDocumentTransition.ACTION_NAMES.REPLACE]: replaceDocuments, + [AbstractDocumentTransition.ACTION_NAMES.DELETE]: deleteDocuments, + } = documents; + + const rawDocumentCreateTransitions = (createDocuments || []) + .map((document) => { + if (document.getRevision() !== DocumentCreateTransition.INITIAL_REVISION) { + throw new InvalidInitialRevisionError(document); + } + + const rawDocument = document.toObject(); + + const keysToStay = [ + '$id', + '$type', + '$dataContractId', + '$createdAt', + '$updatedAt', + ]; + + Object.keys(rawDocument).forEach((key) => { + if (key.startsWith('$') && !keysToStay.includes(key)) { + delete rawDocument[key]; + } + }); + + return { + ...rawDocument, + $action: AbstractDocumentTransition.ACTIONS.CREATE, + $entropy: document.getEntropy(), + }; + }); + + const rawDocumentReplaceTransitions = (replaceDocuments || []) + .map((document) => { + let rawDocument = document.toObject(); + + const keysToStay = [ + '$id', + '$type', + '$dataContractId', + '$revision', + '$updatedAt', + ]; + + Object.keys(rawDocument).forEach((key) => { + if (key.startsWith('$') && !keysToStay.includes(key)) { + delete rawDocument[key]; + } + }); + + rawDocument = { + ...rawDocument, + $action: AbstractDocumentTransition.ACTIONS.REPLACE, + $revision: rawDocument.$revision + 1, + }; + + // If document have an originally set `updatedAt` + // we should update it then + if (rawDocument.$updatedAt) { + rawDocument.$updatedAt = new Date().getTime(); + } + + return rawDocument; + }); + + const rawDocumentDeleteTransitions = (deleteDocuments || []) + .map((document) => ({ + $action: AbstractDocumentTransition.ACTIONS.DELETE, + $id: document.getId(), + $type: document.getType(), + $dataContractId: document.getDataContractId(), + })); + + const rawDocumentTransitions = rawDocumentCreateTransitions + .concat(rawDocumentReplaceTransitions) + .concat(rawDocumentDeleteTransitions); + + const dataContracts = documentsFlattened + .map((document) => document.getDataContract()); + + return new DocumentsBatchTransition({ + protocolVersion: this.dpp.getProtocolVersion(), + ownerId, + transitions: rawDocumentTransitions, + }, dataContracts); + } +} + +module.exports = DocumentFactory; diff --git a/packages/js-dpp/lib/document/errors/DocumentAlreadyExistsError.js b/packages/js-dpp/lib/document/errors/DocumentAlreadyExistsError.js new file mode 100644 index 00000000000..ac567daa061 --- /dev/null +++ b/packages/js-dpp/lib/document/errors/DocumentAlreadyExistsError.js @@ -0,0 +1,23 @@ +const DPPError = require('../../errors/DPPError'); + +class DocumentAlreadyExistsError extends DPPError { + /** + * @param {DocumentCreateTransition} documentTransition + */ + constructor(documentTransition) { + super('Document already exists'); + + this.documentTransition = documentTransition; + } + + /** + * Get document transition + * + * @return {DocumentCreateTransition} + */ + getDocumentTransition() { + return this.documentTransition; + } +} + +module.exports = DocumentAlreadyExistsError; diff --git a/packages/js-dpp/lib/document/errors/DocumentNotProvidedError.js b/packages/js-dpp/lib/document/errors/DocumentNotProvidedError.js new file mode 100644 index 00000000000..23e80dd4247 --- /dev/null +++ b/packages/js-dpp/lib/document/errors/DocumentNotProvidedError.js @@ -0,0 +1,23 @@ +const DPPError = require('../../errors/DPPError'); + +class DocumentNotProvidedError extends DPPError { + /** + * @param {DocumentCreateTransition} documentTransition + */ + constructor(documentTransition) { + super('Document was not provided for apply of state transition'); + + this.documentTransition = documentTransition; + } + + /** + * Get document transition + * + * @return {DocumentCreateTransition} + */ + getDocumentTransition() { + return this.documentTransition; + } +} + +module.exports = DocumentNotProvidedError; diff --git a/packages/js-dpp/lib/document/errors/InvalidActionNameError.js b/packages/js-dpp/lib/document/errors/InvalidActionNameError.js new file mode 100644 index 00000000000..a6e2c6cd727 --- /dev/null +++ b/packages/js-dpp/lib/document/errors/InvalidActionNameError.js @@ -0,0 +1,23 @@ +const DPPError = require('../../errors/DPPError'); + +class InvalidActionNameError extends DPPError { + /** + * @param {string[]} actions + */ + constructor(actions) { + super('Invalid document action submitted'); + + this.actions = actions; + } + + /** + * Get actions + * + * @returns {string[]} + */ + getActions() { + return this.actions; + } +} + +module.exports = InvalidActionNameError; diff --git a/packages/js-dpp/lib/document/errors/InvalidDocumentActionError.js b/packages/js-dpp/lib/document/errors/InvalidDocumentActionError.js new file mode 100644 index 00000000000..4e475004d1c --- /dev/null +++ b/packages/js-dpp/lib/document/errors/InvalidDocumentActionError.js @@ -0,0 +1,27 @@ +const DPPError = require('../../errors/DPPError'); + +class InvalidDocumentActionError extends DPPError { + /** + * @param { + * DocumentCreateTransition|DocumentReplaceTransition|DocumentDeleteTransition + * } documentTransition + */ + constructor(documentTransition) { + super(`Invalid Document action ${documentTransition.getAction()}`); + + this.documentTransition = documentTransition; + } + + /** + * Get Document transition + * + * @return { + * DocumentCreateTransition|DocumentReplaceTransition|DocumentDeleteTransition + * } + */ + getDocumentTransition() { + return this.documentTransition; + } +} + +module.exports = InvalidDocumentActionError; diff --git a/packages/js-dpp/lib/document/errors/InvalidDocumentError.js b/packages/js-dpp/lib/document/errors/InvalidDocumentError.js new file mode 100644 index 00000000000..5fd30eb6836 --- /dev/null +++ b/packages/js-dpp/lib/document/errors/InvalidDocumentError.js @@ -0,0 +1,39 @@ +const DPPError = require('../../errors/DPPError'); + +class InvalidDocumentError extends DPPError { + /** + * @param {AbstractConsensusError[]} errors + * @param {RawDocument} rawDocument + */ + constructor(errors, rawDocument) { + let message = `Invalid Document: "${errors[0].message}"`; + if (errors.length > 1) { + message = `${message} and ${errors.length - 1} more`; + } + + super(message); + + this.errors = errors; + this.rawDocument = rawDocument; + } + + /** + * Get validation errors + * + * @return {AbstractConsensusError[]} + */ + getErrors() { + return this.errors; + } + + /** + * Get raw Document + * + * @return {RawDocument} + */ + getRawDocument() { + return this.rawDocument; + } +} + +module.exports = InvalidDocumentError; diff --git a/packages/js-dpp/lib/document/errors/InvalidInitialRevisionError.js b/packages/js-dpp/lib/document/errors/InvalidInitialRevisionError.js new file mode 100644 index 00000000000..ba0aacae19b --- /dev/null +++ b/packages/js-dpp/lib/document/errors/InvalidInitialRevisionError.js @@ -0,0 +1,23 @@ +const DPPError = require('../../errors/DPPError'); + +class InvalidInitialRevisionError extends DPPError { + /** + * @param {Document} document + */ + constructor(document) { + super(`Invalid Document initial revision ${document.getRevision()}`); + + this.document = document; + } + + /** + * Get Document + * + * @return {Document} + */ + getDocument() { + return this.document; + } +} + +module.exports = InvalidInitialRevisionError; diff --git a/packages/js-dpp/lib/document/errors/MismatchOwnerIdsError.js b/packages/js-dpp/lib/document/errors/MismatchOwnerIdsError.js new file mode 100644 index 00000000000..6769e116ca7 --- /dev/null +++ b/packages/js-dpp/lib/document/errors/MismatchOwnerIdsError.js @@ -0,0 +1,23 @@ +const DPPError = require('../../errors/DPPError'); + +class MismatchOwnerIdsError extends DPPError { + /** + * @param {Document[]} documents + */ + constructor(documents) { + super('Documents have mixed owner ids'); + + this.documents = documents; + } + + /** + * Get documents + * + * @returns {Document[]} + */ + getDocuments() { + return this.documents; + } +} + +module.exports = MismatchOwnerIdsError; diff --git a/packages/js-dpp/lib/document/errors/NoDocumentsSuppliedError.js b/packages/js-dpp/lib/document/errors/NoDocumentsSuppliedError.js new file mode 100644 index 00000000000..328d7e5f030 --- /dev/null +++ b/packages/js-dpp/lib/document/errors/NoDocumentsSuppliedError.js @@ -0,0 +1,9 @@ +const DPPError = require('../../errors/DPPError'); + +class NoDocumentsSuppliedError extends DPPError { + constructor() { + super('No documents were supplied to state transition'); + } +} + +module.exports = NoDocumentsSuppliedError; diff --git a/packages/js-dpp/lib/document/fetchAndValidateDataContractFactory.js b/packages/js-dpp/lib/document/fetchAndValidateDataContractFactory.js new file mode 100644 index 00000000000..e00a4e1ce7d --- /dev/null +++ b/packages/js-dpp/lib/document/fetchAndValidateDataContractFactory.js @@ -0,0 +1,57 @@ +const MissingDataContractIdError = require('../errors/consensus/basic/document/MissingDataContractIdError'); +const DataContractNotPresentError = require('../errors/consensus/basic/document/DataContractNotPresentError'); + +const createAndValidateIdentifier = require('../identifier/createAndValidateIdentifier'); + +const ValidationResult = require('../validation/ValidationResult'); + +/** + * @param {StateRepository} stateRepository + * @return {fetchAndValidateDataContract} + */ +function fetchAndValidateDataContractFactory(stateRepository) { + /** + * @typedef fetchAndValidateDataContract + * @param {RawDocument} rawDocument + * @return {ValidationResult} + */ + async function fetchAndValidateDataContract(rawDocument) { + const result = new ValidationResult(); + + if (!Object.prototype.hasOwnProperty.call(rawDocument, '$dataContractId')) { + result.addError( + new MissingDataContractIdError(), + ); + } + + if (!result.isValid()) { + return result; + } + + const dataContractId = createAndValidateIdentifier( + '$dataContractId', + rawDocument.$dataContractId, + result, + ); + + if (!result.isValid()) { + return result; + } + + const dataContract = await stateRepository.fetchDataContract(dataContractId); + + if (!dataContract) { + result.addError( + new DataContractNotPresentError(dataContractId.toBuffer()), + ); + } + + result.setData(dataContract); + + return result; + } + + return fetchAndValidateDataContract; +} + +module.exports = fetchAndValidateDataContractFactory; diff --git a/packages/js-dpp/lib/document/generateDocumentId.js b/packages/js-dpp/lib/document/generateDocumentId.js new file mode 100644 index 00000000000..5f4e1f2ba4b --- /dev/null +++ b/packages/js-dpp/lib/document/generateDocumentId.js @@ -0,0 +1,23 @@ +const hashModule = require('../util/hash'); + +/** + * Generates document ID + * + * @param {Buffer} contractId + * @param {Buffer} ownerId + * @param {string} type + * @param {Buffer} entropy + * @returns {Buffer} + */ +function generateDocumentId(contractId, ownerId, type, entropy) { + const { hash } = hashModule; + + return hash(Buffer.concat([ + contractId, + ownerId, + Buffer.from(type), + entropy, + ])); +} + +module.exports = generateDocumentId; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/DocumentsBatchTransition.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/DocumentsBatchTransition.js new file mode 100644 index 00000000000..3ad5a1a8678 --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/DocumentsBatchTransition.js @@ -0,0 +1,183 @@ +const AbstractStateTransitionIdentitySigned = require('../../../stateTransition/AbstractStateTransitionIdentitySigned'); +const stateTransitionTypes = require('../../../stateTransition/stateTransitionTypes'); + +const AbstractDocumentTransition = require('./documentTransition/AbstractDocumentTransition'); +const DocumentCreateTransition = require('./documentTransition/DocumentCreateTransition'); +const DocumentReplaceTransition = require('./documentTransition/DocumentReplaceTransition'); +const DocumentDeleteTransition = require('./documentTransition/DocumentDeleteTransition'); + +const IdentityPublicKey = require('../../../identity/IdentityPublicKey'); + +const actionsToClasses = { + [AbstractDocumentTransition.ACTIONS.CREATE]: DocumentCreateTransition, + [AbstractDocumentTransition.ACTIONS.REPLACE]: DocumentReplaceTransition, + [AbstractDocumentTransition.ACTIONS.DELETE]: DocumentDeleteTransition, +}; + +const Identifier = require('../../../identifier/Identifier'); + +class DocumentsBatchTransition extends AbstractStateTransitionIdentitySigned { + /** + * @param {RawDocumentsBatchTransition} [rawStateTransition] + * @param {DataContract[]} dataContracts + */ + constructor(rawStateTransition = {}, dataContracts) { + super(rawStateTransition); + + if (Object.prototype.hasOwnProperty.call(rawStateTransition, 'ownerId')) { + this.ownerId = Identifier.from(rawStateTransition.ownerId); + } + + if (Object.prototype.hasOwnProperty.call(rawStateTransition, 'transitions')) { + const dataContractsMap = dataContracts.reduce((map, dataContract) => ({ + ...map, + [dataContract.getId().toString('hex')]: dataContract, + }), {}); + + this.transitions = rawStateTransition.transitions.map((rawDocumentTransition) => ( + new actionsToClasses[rawDocumentTransition.$action]( + rawDocumentTransition, + dataContractsMap[rawDocumentTransition.$dataContractId.toString('hex')], + ) + )); + } + } + + /** + * Get State Transition type + * + * @return {number} + */ + getType() { + return stateTransitionTypes.DOCUMENTS_BATCH; + } + + /** + * Get owner id + * + * @return {Identifier} + */ + getOwnerId() { + return this.ownerId; + } + + /** + * Get document action transitions + * + * @return {DocumentCreateTransition[]|DocumentReplaceTransition[]|DocumentDeleteTransition[]} + */ + getTransitions() { + return this.transitions; + } + + /** + * Get state transition as plain object + * + * @param {Object} [options] + * @param {boolean} [options.skipSignature=false] + * @param {boolean} [options.skipIdentifiersConversion=false] + * + * @return {RawDocumentsBatchTransition} + */ + toObject(options = {}) { + Object.assign( + options, + { + skipIdentifiersConversion: false, + ...options, + }, + ); + + const rawDocumentsBatchTransition = { + ...super.toObject(options), + ownerId: this.getOwnerId(), + transitions: this.getTransitions().map((t) => t.toObject()), + }; + + if (!options.skipIdentifiersConversion) { + rawDocumentsBatchTransition.ownerId = this.getOwnerId().toBuffer(); + } + + return rawDocumentsBatchTransition; + } + + /** + * Get state transition as JSON + * + * @return {JsonDocumentsBatchTransition} + */ + toJSON() { + const jsonStateTransition = { + ...super.toJSON(), + ownerId: this.getOwnerId().toString(), + }; + + // overwrite plain object transitions + jsonStateTransition.transitions = this.getTransitions().map((t) => t.toJSON()); + + return jsonStateTransition; + } + + /** + * Returns ids of all affected documents + * + * @return {Identifier[]} + */ + getModifiedDataIds() { + return this.getTransitions().map((documentTransition) => documentTransition.getId()); + } + + /** + * Returns minimal key security level that can be used to sign this ST + * + * @override + * @return {number} + */ + getKeySecurityLevelRequirement() { + const defaultSecurityLevel = IdentityPublicKey.SECURITY_LEVELS.HIGH; + + // Step 1: Get all document types for the ST + // Step 2: Get document schema for every type + // If schema has security level, use that, if not, use the default security level + // Find the highest level (lowest int value) of all documents - the ST's signature + // requirement is the highest level across all documents affected by the ST. + const documentTransitions = this.getTransitions(); + let highestSecurityLevel; + documentTransitions.forEach((documentTransition) => { + const documentType = documentTransition.getType(); + const dataContract = documentTransition.getDataContract(); + const documentSchema = dataContract.getDocumentSchema(documentType); + + const documentKeySecurityLevelRequirement = documentSchema + .signatureSecurityLevelRequirement == null + ? defaultSecurityLevel + : documentSchema.signatureSecurityLevelRequirement; + + if ( + highestSecurityLevel == null || highestSecurityLevel > documentKeySecurityLevelRequirement + ) { + highestSecurityLevel = documentKeySecurityLevelRequirement; + } + }); + + return highestSecurityLevel; + } +} + +/** + * @typedef {RawStateTransitionIdentitySigned & Object} RawDocumentsBatchTransition + * @property {Buffer} ownerId + * @property { + * Array. + * } transitions + */ + +/** + * @typedef {JsonStateTransitionIdentitySigned & Object} JsonDocumentsBatchTransition + * @property {string} ownerId + * @property { + * Array. + * } transitions + */ + +module.exports = DocumentsBatchTransition; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/applyDocumentsBatchTransitionFactory.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/applyDocumentsBatchTransitionFactory.js new file mode 100644 index 00000000000..f8be16c232e --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/applyDocumentsBatchTransitionFactory.js @@ -0,0 +1,135 @@ +/* eslint-disable no-await-in-loop */ +const AbstractDocumentTransition = require( + './documentTransition/AbstractDocumentTransition', +); + +const InvalidDocumentActionError = require('../../errors/InvalidDocumentActionError'); + +const Document = require('../../Document'); +const DocumentNotProvidedError = require('../../errors/DocumentNotProvidedError'); + +/** + * @param {StateRepository} stateRepository + * @param {fetchDocuments} fetchDocuments + * + * @returns {applyDocumentsBatchTransition} + */ +function applyDocumentsBatchTransitionFactory( + stateRepository, + fetchDocuments, +) { + /** + * Apply documents batch state transition + * + * @typedef applyDocumentsBatchTransition + * + * @param {DocumentsBatchTransition} stateTransition + * + * @return {Promise<*>} + */ + async function applyDocumentsBatchTransition(stateTransition) { + const executionContext = stateTransition.getExecutionContext(); + + // Fetch documents for replace transitions + const replaceTransitions = stateTransition.getTransitions() + .filter((dt) => dt.getAction() === AbstractDocumentTransition.ACTIONS.REPLACE); + + const fetchedDocuments = await fetchDocuments(replaceTransitions, executionContext); + + const fetchedDocumentsById = fetchedDocuments.reduce((result, document) => ( + { + ...result, + [document.getId()]: document, + } + ), {}); + + // since groveDB doesn't support parallel inserts, wee need to make them sequential + // we don't want to use regenerator-runtime, so we use `reduce` instead + // more info at https://jrsinclair.com/articles/2019/how-to-run-async-js-in-parallel-or-sequential/ + + const starterPromise = Promise.resolve(null); + + return stateTransition.getTransitions().reduce( + (previousPromise, documentTransition) => previousPromise.then(async () => { + switch (documentTransition.getAction()) { + case AbstractDocumentTransition.ACTIONS.CREATE: { + const newDocument = new Document({ + $protocolVersion: stateTransition.getProtocolVersion(), + $id: documentTransition.getId(), + $type: documentTransition.getType(), + $dataContractId: documentTransition.getDataContractId(), + $ownerId: stateTransition.getOwnerId(), + ...documentTransition.getData(), + }, documentTransition.getDataContract()); + + if (documentTransition.getCreatedAt()) { + newDocument.setCreatedAt(documentTransition.getCreatedAt()); + } + + if (documentTransition.getUpdatedAt()) { + newDocument.setUpdatedAt(documentTransition.getUpdatedAt()); + } + + newDocument.setEntropy(documentTransition.getEntropy()); + + newDocument.setRevision(documentTransition.getRevision()); + + return stateRepository.createDocument(newDocument, executionContext); + } + case AbstractDocumentTransition.ACTIONS.REPLACE: { + let document; + if (executionContext.isDryRun()) { + const { + time: { + seconds: lastBlockHeaderTimeSeconds, + }, + } = await stateRepository.fetchLatestPlatformBlockHeader(); + + const lastBlockHeaderTime = lastBlockHeaderTimeSeconds * 1000; + + document = new Document({ + $protocolVersion: stateTransition.getProtocolVersion(), + $id: documentTransition.getId(), + $type: documentTransition.getType(), + $dataContractId: documentTransition.getDataContractId(), + $ownerId: stateTransition.getOwnerId(), + $createdAt: lastBlockHeaderTime, + ...documentTransition.getData(), + }, documentTransition.getDataContract()); + } else { + document = fetchedDocumentsById[documentTransition.getId()]; + + if (!document) { + throw new DocumentNotProvidedError(documentTransition); + } + } + + document.setRevision(documentTransition.getRevision()); + document.setData(documentTransition.getData()); + + if (documentTransition.getUpdatedAt()) { + document.setUpdatedAt(documentTransition.getUpdatedAt()); + } + + return stateRepository.updateDocument(document, executionContext); + } + case AbstractDocumentTransition.ACTIONS.DELETE: { + return stateRepository.removeDocument( + documentTransition.getDataContract(), + documentTransition.getType(), + documentTransition.getId(), + executionContext, + ); + } + default: + throw new InvalidDocumentActionError(documentTransition); + } + }), + starterPromise, + ); + } + + return applyDocumentsBatchTransition; +} + +module.exports = applyDocumentsBatchTransitionFactory; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/AbstractDataDocumentTransition.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/AbstractDataDocumentTransition.js new file mode 100644 index 00000000000..b91cf611640 --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/AbstractDataDocumentTransition.js @@ -0,0 +1,116 @@ +const lodashCloneDeepWith = require('lodash.clonedeepwith'); +const lodashGet = require('lodash.get'); +const lodashSet = require('lodash.set'); + +const cloneDeepWithIdentifiers = require('../../../../util/cloneDeepWithIdentifiers'); + +const AbstractDocumentTransition = require('./AbstractDocumentTransition'); + +const Identifier = require('../../../../identifier/Identifier'); + +/** + * @abstract + */ +class AbstractDataDocumentTransition extends AbstractDocumentTransition { + /** + * @param {RawDocumentCreateTransition} rawTransition + * @param {DataContract} dataContract + */ + constructor(rawTransition, dataContract) { + super(rawTransition, dataContract); + + const binaryProperties = dataContract.getBinaryProperties( + this.getType(), + ); + + const identifierProperties = Object.fromEntries( + Object.entries(binaryProperties) + .filter(([, property]) => property.contentMediaType === Identifier.MEDIA_TYPE), + ); + + const data = Object.fromEntries( + Object.entries(rawTransition).filter(([propertyName]) => !propertyName.startsWith('$')), + ); + + this.data = cloneDeepWithIdentifiers(data); + + Object.keys(identifierProperties) + .forEach((propertyPath) => { + const value = lodashGet(this.data, propertyPath); + + if (value !== undefined) { + const clonedValue = Identifier.from(value); + + lodashSet( + this.data, + propertyPath, + clonedValue, + ); + } + }); + } + + /** + * Get data + * + * @returns {Object} + */ + getData() { + return this.data; + } + + /** + * Get plain object representation + * + * @param {Object} [options] + * @param {boolean} [options.skipIdentifiersConversion=false] + * @return {RawDocumentCreateTransition} + */ + toObject(options = {}) { + Object.assign( + options, + { + skipIdentifiersConversion: false, + ...options, + }, + ); + + const rawDocumentTransition = { + ...super.toObject(options), + ...this.getData(), + }; + + if (!options.skipIdentifiersConversion) { + // eslint-disable-next-line consistent-return + return lodashCloneDeepWith(rawDocumentTransition, (value) => { + if (value instanceof Identifier) { + return value.toBuffer(); + } + }); + } + + return rawDocumentTransition; + } + + /** + * Get JSON representation + * + * @return {JsonDocumentCreateTransition} + */ + toJSON() { + const jsonDocumentTransition = super.toJSON(); + + // eslint-disable-next-line consistent-return + return lodashCloneDeepWith(jsonDocumentTransition, (value) => { + if (value instanceof Identifier) { + return value.toString(); + } + + if (Buffer.isBuffer(value)) { + return value.toString('base64'); + } + }); + } +} + +module.exports = AbstractDataDocumentTransition; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/AbstractDocumentTransition.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/AbstractDocumentTransition.js new file mode 100644 index 00000000000..29b2d67551e --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/AbstractDocumentTransition.js @@ -0,0 +1,151 @@ +const lodashGet = require('lodash.get'); + +const Identifier = require('../../../../identifier/Identifier'); + +/** + * @abstract + */ +class AbstractDocumentTransition { + constructor(rawDocumentTransition, dataContract) { + this.type = rawDocumentTransition.$type; + + if (Object.prototype.hasOwnProperty.call(rawDocumentTransition, '$id')) { + this.id = Identifier.from(rawDocumentTransition.$id); + } + + if (Object.prototype.hasOwnProperty.call(rawDocumentTransition, '$dataContractId')) { + this.dataContractId = Identifier.from(rawDocumentTransition.$dataContractId); + } + + this.dataContract = dataContract; + } + + /** + * Get id + * + * @returns {Identifier} + */ + getId() { + return this.id; + } + + /** + * Get type + * + * @returns {*} + */ + getType() { + return this.type; + } + + /** + * @abstract + */ + getAction() { + throw new Error('Not implemented'); + } + + /** + * Get data contract associated with this transition + * + * @return {DataContract} + */ + getDataContract() { + return this.dataContract; + } + + /** + * Get Data Contract ID + * + * @return {Identifier} + */ + getDataContractId() { + return this.dataContractId; + } + + /** + * Get transition property by path + * + * @param {string} propertyPath + * + * @return {*} + */ + get(propertyPath) { + return lodashGet(this.getData(), propertyPath); + } + + /** + * Get plain object representation + * + * @param {Object} [options] + * @param {boolean} [options.skipIdentifiersConversion=false] + * @return {RawDocumentTransition} + */ + toObject(options = {}) { + Object.assign( + options, + { + skipIdentifiersConversion: false, + ...options, + }, + ); + + const rawDocumentTransition = { + $id: this.getId(), + $type: this.getType(), + $action: this.getAction(), + $dataContractId: this.getDataContractId(), + }; + + if (!options.skipIdentifiersConversion) { + rawDocumentTransition.$id = this.getId().toBuffer(); + rawDocumentTransition.$dataContractId = this.getDataContractId().toBuffer(); + } + + return rawDocumentTransition; + } + + /** + * Get JSON representation + * + * @return {JsonDocumentTransition} + */ + toJSON() { + return { + ...this.toObject({ skipIdentifiersConversion: true }), + $id: this.getId().toString(), + $dataContractId: this.getDataContractId().toString(), + }; + } +} + +/** + * @typedef {Object} RawDocumentTransition + * @property {Buffer} $id + * @property {string} $type + * @property {number} $action + * @property {Buffer} $dataContractId + */ + +/** + * @typedef {Object} JsonDocumentTransition + * @property {string} $id + * @property {string} $type + * @property {number} $action + * @property {string} $dataContractId + */ + +AbstractDocumentTransition.ACTIONS = { + CREATE: 0, + REPLACE: 1, + // 2 reserved for UPDATE + DELETE: 3, +}; + +AbstractDocumentTransition.ACTION_NAMES = { + CREATE: 'create', + REPLACE: 'replace', + DELETE: 'delete', +}; + +module.exports = AbstractDocumentTransition; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/DocumentCreateTransition.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/DocumentCreateTransition.js new file mode 100644 index 00000000000..239e51b0e7f --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/DocumentCreateTransition.js @@ -0,0 +1,119 @@ +const AbstractDocumentTransition = require('./AbstractDocumentTransition'); +const AbstractDataDocumentTransition = require('./AbstractDataDocumentTransition'); + +class DocumentCreateTransition extends AbstractDataDocumentTransition { + /** + * @param {RawDocumentCreateTransition} rawTransition + * @param {DataContract} dataContract + */ + constructor(rawTransition, dataContract) { + super(rawTransition, dataContract); + + if (Object.prototype.hasOwnProperty.call(rawTransition, '$entropy')) { + this.entropy = rawTransition.$entropy; + } + + if (Object.prototype.hasOwnProperty.call(rawTransition, '$createdAt')) { + this.createdAt = new Date(rawTransition.$createdAt); + } + + if (Object.prototype.hasOwnProperty.call(rawTransition, '$updatedAt')) { + this.updatedAt = new Date(rawTransition.$updatedAt); + } + } + + /** + * Get action + * + * @returns {number} + */ + getAction() { + return AbstractDocumentTransition.ACTIONS.CREATE; + } + + /** + * Get entropy + * + * @returns {Buffer} + */ + getEntropy() { + return this.entropy; + } + + /** + * Get creation date + * + * @return {Date} + */ + getCreatedAt() { + return this.createdAt; + } + + /** + * Get update date + * + * @return {Date} + */ + getUpdatedAt() { + return this.updatedAt; + } + + /** + * Get revision + * + * @return {number} + */ + getRevision() { + return DocumentCreateTransition.INITIAL_REVISION; + } + + /** + * Get plain object representation + * + * @param {Object} [options] + * @param {boolean} [options.skipIdentifiersConversion=false] + * @return {RawDocumentCreateTransition} + */ + toObject(options = {}) { + Object.assign( + options, + { + skipIdentifiersConversion: false, + ...options, + }, + ); + + const rawDocumentTransition = { + ...super.toObject(options), + $entropy: this.getEntropy(), + }; + + if (this.createdAt) { + rawDocumentTransition.$createdAt = this.getCreatedAt().getTime(); + } + + if (this.updatedAt) { + rawDocumentTransition.$updatedAt = this.getUpdatedAt().getTime(); + } + + return rawDocumentTransition; + } +} + +/** + * @typedef {RawDocumentTransition & Object} RawDocumentCreateTransition + * @property {Buffer} $entropy + * @property {number} [$createdAt] + * @property {number} [$updatedAt] + */ + +/** + * @typedef {JsonDocumentTransition & Object} JsonDocumentCreateTransition + * @property {string} $entropy + * @property {number} [$createdAt] + * @property {number} [$updatedAt] + */ + +DocumentCreateTransition.INITIAL_REVISION = 1; + +module.exports = DocumentCreateTransition; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/DocumentDeleteTransition.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/DocumentDeleteTransition.js new file mode 100644 index 00000000000..73525c1bc70 --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/DocumentDeleteTransition.js @@ -0,0 +1,22 @@ +const AbstractDocumentTransition = require('./AbstractDocumentTransition'); + +class DocumentDeleteTransition extends AbstractDocumentTransition { + /** + * Get action + * + * @returns {number} + */ + getAction() { + return AbstractDocumentTransition.ACTIONS.DELETE; + } +} + +/** + * @typedef {RawDocumentTransition & Object} RawDocumentDeleteTransition + */ + +/** + * @typedef {JsonDocumentTransition & Object} JsonDocumentDeleteTransition + */ + +module.exports = DocumentDeleteTransition; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/DocumentReplaceTransition.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/DocumentReplaceTransition.js new file mode 100644 index 00000000000..051cdbc8390 --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/DocumentReplaceTransition.js @@ -0,0 +1,89 @@ +const AbstractDocumentTransition = require('./AbstractDocumentTransition'); +const AbstractDataDocumentTransition = require('./AbstractDataDocumentTransition'); + +class DocumentReplaceTransition extends AbstractDataDocumentTransition { + /** + * @param {RawDocumentReplaceTransition} rawTransition + * @param {DataContract} dataContract + */ + constructor(rawTransition, dataContract) { + super(rawTransition, dataContract); + + if (Object.prototype.hasOwnProperty.call(rawTransition, '$revision')) { + this.revision = rawTransition.$revision; + } + + if (Object.prototype.hasOwnProperty.call(rawTransition, '$updatedAt')) { + this.updatedAt = new Date(rawTransition.$updatedAt); + } + } + + /** + * Get action + * + * @returns {number} + */ + getAction() { + return AbstractDocumentTransition.ACTIONS.REPLACE; + } + + /** + * Get next document revision + * + * @return {number} + */ + getRevision() { + return this.revision; + } + + /** + * Get update date + * + * @return {Date} + */ + getUpdatedAt() { + return this.updatedAt; + } + + /** + * Get plain object representation + * + * @param {Object} [options] + * @param {boolean} [options.skipIdentifiersConversion=false] + * @return {RawDocumentReplaceTransition} + */ + toObject(options = {}) { + Object.assign( + options, + { + skipIdentifiersConversion: false, + ...options, + }, + ); + + const rawDocumentTransition = { + ...super.toObject(options), + $revision: this.getRevision(), + }; + + if (this.getUpdatedAt()) { + rawDocumentTransition.$updatedAt = this.getUpdatedAt().getTime(); + } + + return rawDocumentTransition; + } +} + +/** + * @typedef {RawDocumentTransition & Object} RawDocumentReplaceTransition + * @property {number} $revision + * @property {number} [$updatedAt] + */ + +/** + * @typedef {JsonDocumentTransition & Object} JsonDocumentReplaceTransition + * @property {number} $revision + * @property {number} [$updatedAt] + */ + +module.exports = DocumentReplaceTransition; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/basic/findDuplicatesById.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/basic/findDuplicatesById.js new file mode 100644 index 00000000000..0573406cc96 --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/basic/findDuplicatesById.js @@ -0,0 +1,50 @@ +/** + * @param { + * RawDocumentCreateTransition|RawDocumentReplaceTransition|RawDocumentDeleteTransition + * } documentTransition + * + * @return {string} + */ +function createFingerPrint(documentTransition) { + return [ + documentTransition.$type, + documentTransition.$id, + ].join(':'); +} + +/** + * Find duplicates + * + * @typedef findDuplicatesById + * + * @param { + * Array. + * } documentTransitions + * + * @return { + * Array. + * } + */ +function findDuplicatesById(documentTransitions) { + const fingerprints = {}; + const duplicates = []; + + documentTransitions + .forEach((documentTransition) => { + const fingerprint = createFingerPrint(documentTransition); + + if (!fingerprints[fingerprint]) { + fingerprints[fingerprint] = []; + } + + fingerprints[fingerprint].push(documentTransition); + + if (fingerprints[fingerprint].length > 1) { + duplicates.push(...fingerprints[fingerprint]); + } + }); + + return duplicates; +} + +module.exports = findDuplicatesById; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/basic/findDuplicatesByIndices.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/basic/findDuplicatesByIndices.js new file mode 100644 index 00000000000..21296ea4c34 --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/basic/findDuplicatesByIndices.js @@ -0,0 +1,89 @@ +/** + * @param {RawDocumentCreateTransition|RawDocumentReplaceTransition} originalTransition + * @param {RawDocumentCreateTransition|RawDocumentReplaceTransition} transitionToCheck + * @param {Object} typeIndices + * + * @return {boolean} + */ +function isDuplicateByIndices(originalTransition, transitionToCheck, typeIndices) { + return typeIndices + // For every index definition check if hashes match + // accumulating overall boolean result + .reduce((accumulator, definition) => { + const [originalHash, hashToCheck] = definition.properties + .reduce(([originalAcc, toCheckAcc], property) => { + const propertyName = Object.keys(property)[0]; + return [ + `${originalAcc}:${originalTransition[propertyName]}`, + `${toCheckAcc}:${transitionToCheck[propertyName]}`, + ]; + }, ['', '']); + + return accumulator || (originalHash === hashToCheck); + }, false); +} + +/** + * Find duplicate objects by unique indices + * + * @typedef findDuplicatesByIndices + * + * @param { + * Array. + * } documentTransitions + * @param {DataContract} dataContract + * + * @return { + * Array. + * } + */ +function findDuplicatesByIndices(documentTransitions, dataContract) { + const groupsObject = documentTransitions + // Group documentTransitions by its type, enrich them by type's unique indices + .reduce((groups, documentTransition) => { + const { $type: type } = documentTransition; + const typeIndices = (dataContract.getDocumentSchema(type).indices || []); + + // Init empty group + if (!groups[type]) { + // eslint-disable-next-line no-param-reassign + groups[type] = { + items: [], + // init group with only it's unique indices + indices: typeIndices.filter((index) => index.unique), + }; + } + + groups[type].items.push(documentTransition); + + return groups; + }, {}); + + const duplicateArrays = Object.values(groupsObject) + // Filter out groups without unique indices + .filter((group) => group.indices.length > 0) + // Filter out groups with only one object + .filter((group) => group.items.length > 1) + .map((group) => group.items + // Flat map found duplicates in a group + .reduce((foundGroupDuplicates, transition) => { + // For every transition in a group make duplicate search + const duplicates = group.items + // Exclude current transition from search + .filter((o) => o.$id !== transition.$id) + .reduce((foundDuplicates, transitionsToCheck) => { + if (isDuplicateByIndices(transition, transitionsToCheck, group.indices)) { + foundDuplicates.push(transitionsToCheck); + } + return foundDuplicates; + }, []); + + return foundGroupDuplicates.concat(duplicates); + }, [])); + + // Flat map the results and return raw items + return duplicateArrays + .reduce((accumulator, items) => accumulator.concat(items), []); +} + +module.exports = findDuplicatesByIndices; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/basic/validateDocumentsBatchTransitionBasicFactory.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/basic/validateDocumentsBatchTransitionBasicFactory.js new file mode 100644 index 00000000000..eaaf1d74996 --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/basic/validateDocumentsBatchTransitionBasicFactory.js @@ -0,0 +1,333 @@ +const ValidationResult = require('../../../../../validation/ValidationResult'); + +const AbstractDocumentTransition = require('../../documentTransition/AbstractDocumentTransition'); + +const DataContractNotPresentError = require('../../../../../errors/consensus/basic/document/DataContractNotPresentError'); +const InvalidDocumentTransitionIdError = require('../../../../../errors/consensus/basic/document/InvalidDocumentTransitionIdError'); +const DuplicateDocumentTransitionsWithIdsError = require('../../../../../errors/consensus/basic/document/DuplicateDocumentTransitionsWithIdsError'); +const MissingDocumentTransitionTypeError = require('../../../../../errors/consensus/basic/document/MissingDocumentTransitionTypeError'); +const InvalidDocumentTypeError = require('../../../../../errors/consensus/basic/document/InvalidDocumentTypeError'); +const InvalidDocumentTransitionActionError = require('../../../../../errors/consensus/basic/document/InvalidDocumentTransitionActionError'); +const MissingDocumentTransitionActionError = require('../../../../../errors/consensus/basic/document/MissingDocumentTransitionActionError'); +const MissingDataContractIdError = require('../../../../../errors/consensus/basic/document/MissingDataContractIdError'); +const Identifier = require('../../../../../identifier/Identifier'); + +const baseTransitionSchema = require('../../../../../../schema/document/stateTransition/documentTransition/base.json'); +const createTransitionSchema = require('../../../../../../schema/document/stateTransition/documentTransition/create.json'); +const replaceTransitionSchema = require('../../../../../../schema/document/stateTransition/documentTransition/replace.json'); + +const generateDocumentId = require('../../../../generateDocumentId'); +const convertBuffersToArrays = require('../../../../../util/convertBuffersToArrays'); + +const documentsBatchTransitionSchema = require('../../../../../../schema/document/stateTransition/documentsBatch.json'); +const createAndValidateIdentifier = require('../../../../../identifier/createAndValidateIdentifier'); +const DuplicateDocumentTransitionsWithIndicesError = require('../../../../../errors/consensus/basic/document/DuplicateDocumentTransitionsWithIndicesError'); + +/** + * @param {findDuplicatesById} findDuplicatesById + * @param {findDuplicatesByIndices} findDuplicatesByIndices + * @param {StateRepository} stateRepository + * @param {JsonSchemaValidator} jsonSchemaValidator + * @param {enrichDataContractWithBaseSchema} enrichDataContractWithBaseSchema + * @param {validatePartialCompoundIndices} validatePartialCompoundIndices + * @param {validateProtocolVersion} validateProtocolVersion + * + * @return {validateDocumentsBatchTransitionBasic} + */ +function validateDocumentsBatchTransitionBasicFactory( + findDuplicatesById, + findDuplicatesByIndices, + stateRepository, + jsonSchemaValidator, + enrichDataContractWithBaseSchema, + validatePartialCompoundIndices, + validateProtocolVersion, +) { + const { ACTIONS } = AbstractDocumentTransition; + + /** + * + * @param {DataContract} dataContract + * @param {Buffer} ownerId + * @param {Array< + * RawDocumentCreateTransition| + * RawDocumentDeleteTransition| + * DocumentReplaceTransition + * >} rawDocumentTransitions + * @return {Promise} + */ + async function validateDocumentTransitions(dataContract, ownerId, rawDocumentTransitions) { + const result = new ValidationResult(); + + if (!result.isValid()) { + return result; + } + + const enrichedBaseDataContract = enrichDataContractWithBaseSchema( + dataContract, + baseTransitionSchema, + enrichDataContractWithBaseSchema.PREFIX_BYTE_1, + ); + + const enrichedDataContractsByActions = { + [ACTIONS.CREATE]: enrichDataContractWithBaseSchema( + enrichedBaseDataContract, + createTransitionSchema, + enrichDataContractWithBaseSchema.PREFIX_BYTE_2, + ), + [ACTIONS.REPLACE]: enrichDataContractWithBaseSchema( + enrichedBaseDataContract, + replaceTransitionSchema, + enrichDataContractWithBaseSchema.PREFIX_BYTE_3, + ['$createdAt'], + ), + }; + + rawDocumentTransitions.forEach((rawDocumentTransition) => { + // Validate $type + if (!Object.prototype.hasOwnProperty.call(rawDocumentTransition, '$type')) { + result.addError( + new MissingDocumentTransitionTypeError(), + ); + + return; + } + + if (!dataContract.isDocumentDefined(rawDocumentTransition.$type)) { + result.addError( + new InvalidDocumentTypeError( + rawDocumentTransition.$type, + dataContract.getId().toBuffer(), + ), + ); + + return; + } + + // Validate $action + if (!Object.prototype.hasOwnProperty.call(rawDocumentTransition, '$action')) { + result.addError( + new MissingDocumentTransitionActionError(), + ); + + return; + } + + // Validate document schema + switch (rawDocumentTransition.$action) { + case ACTIONS.CREATE: + case ACTIONS.REPLACE: { + // eslint-disable-next-line max-len + const enrichedDataContract = enrichedDataContractsByActions[rawDocumentTransition.$action]; + + const documentSchemaRef = enrichedDataContract.getDocumentSchemaRef( + rawDocumentTransition.$type, + ); + + const additionalSchemas = { + [enrichedDataContract.getJsonSchemaId()]: + enrichedDataContract.toJSON(), + }; + + const schemaResult = jsonSchemaValidator.validate( + documentSchemaRef, + convertBuffersToArrays(rawDocumentTransition), + additionalSchemas, + ); + + if (!schemaResult.isValid()) { + result.merge(schemaResult); + + break; + } + + // Additional checks for CREATE transitions + if (ACTIONS.CREATE === rawDocumentTransition.$action) { + // validate id generation + const documentId = generateDocumentId( + dataContract.getId(), + ownerId, + rawDocumentTransition.$type, + rawDocumentTransition.$entropy, + ); + + if (!rawDocumentTransition.$id.equals(documentId)) { + result.addError( + new InvalidDocumentTransitionIdError( + documentId, + rawDocumentTransition.$id, + ), + ); + } + } + + break; + } + case ACTIONS.DELETE: + result.merge( + jsonSchemaValidator.validate( + baseTransitionSchema, + convertBuffersToArrays(rawDocumentTransition), + ), + ); + + break; + default: + result.addError( + new InvalidDocumentTransitionActionError( + rawDocumentTransition.$action, + ), + ); + } + }); + + if (!result.isValid()) { + return result; + } + + // Find duplicate documents by type and ID + const duplicateTransitions = findDuplicatesById(rawDocumentTransitions); + if (duplicateTransitions.length > 0) { + result.addError( + new DuplicateDocumentTransitionsWithIdsError( + duplicateTransitions.map( + (documentTransition) => [documentTransition.$type, documentTransition.$id], + ), + ), + ); + } + + // Find duplicate transitions by unique indices + const duplicateTransitionsByIndices = findDuplicatesByIndices( + rawDocumentTransitions, + dataContract, + ); + + if (duplicateTransitionsByIndices.length > 0) { + result.addError( + new DuplicateDocumentTransitionsWithIndicesError( + duplicateTransitionsByIndices.map( + (documentTransition) => [documentTransition.$type, documentTransition.$id], + ), + ), + ); + } + + // Validate partial compound indices + const nonDeleteDocumentTransitions = rawDocumentTransitions + .filter((d) => d.$action !== AbstractDocumentTransition.ACTIONS.DELETE); + + if (nonDeleteDocumentTransitions.length > 0) { + result.merge( + validatePartialCompoundIndices( + ownerId, + nonDeleteDocumentTransitions, + dataContract, + ), + ); + } + + return result; + } + + /** + * @typedef validateDocumentsBatchTransitionBasic + * @param {RawDocumentsBatchTransition} rawStateTransition + * @param {StateTransitionExecutionContext} executionContext + * @return {ValidationResult} + */ + async function validateDocumentsBatchTransitionBasic(rawStateTransition, executionContext) { + const result = jsonSchemaValidator.validate( + documentsBatchTransitionSchema, + convertBuffersToArrays(rawStateTransition), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + validateProtocolVersion(rawStateTransition.protocolVersion), + ); + + if (!result.isValid()) { + return result; + } + + // Group document transitions by data contracts + const documentTransitionsByContracts = rawStateTransition.transitions + .reduce((obj, rawDocumentTransition) => { + if (!Object.prototype.hasOwnProperty.call(rawDocumentTransition, '$dataContractId')) { + result.addError( + new MissingDataContractIdError(), + ); + + return obj; + } + + const dataContractId = createAndValidateIdentifier( + '$dataContractId', + rawDocumentTransition.$dataContractId, + result, + ); + + if (!dataContractId) { + return obj; + } + + if (!obj[dataContractId]) { + // eslint-disable-next-line no-param-reassign + obj[dataContractId] = []; + } + + obj[dataContractId].push(rawDocumentTransition); + + return obj; + }, {}); + + const documentTransitionResultsPromises = Object.entries(documentTransitionsByContracts) + .map(async ([dataContractIdString, documentTransitions]) => { + const perDocumentResult = new ValidationResult(); + + const dataContractId = Identifier.from(dataContractIdString); + + const dataContract = await stateRepository.fetchDataContract( + dataContractId, + executionContext, + ); + + if (executionContext.isDryRun()) { + return perDocumentResult; + } + + if (!dataContract) { + perDocumentResult.addError( + new DataContractNotPresentError(dataContractId.toBuffer()), + ); + } + + if (!perDocumentResult.isValid()) { + return perDocumentResult; + } + + perDocumentResult.merge( + await validateDocumentTransitions( + dataContract, + rawStateTransition.ownerId, + documentTransitions, + ), + ); + + return perDocumentResult; + }); + + const documentTransitionResults = await Promise.all(documentTransitionResultsPromises); + documentTransitionResults.forEach(result.merge.bind(result)); + + return result; + } + + return validateDocumentsBatchTransitionBasic; +} + +module.exports = validateDocumentsBatchTransitionBasicFactory; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/basic/validatePartialCompoundIndices.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/basic/validatePartialCompoundIndices.js new file mode 100644 index 00000000000..4b50cd03404 --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/basic/validatePartialCompoundIndices.js @@ -0,0 +1,67 @@ +const lodashGet = require('lodash.get'); + +const ValidationResult = require('../../../../../validation/ValidationResult'); + +const InconsistentCompoundIndexDataError = require('../../../../../errors/consensus/basic/document/InconsistentCompoundIndexDataError'); + +/** + * @typedef validatePartialCompoundIndices + * @param {Buffer} ownerId + * @param {Array< + * RawDocumentCreateTransition| + * RawDocumentReplaceTransition + * >} rawDocumentTransitions + * @param {DataContract} dataContract + * @return {ValidationResult} + */ +function validatePartialCompoundIndices( + ownerId, + rawDocumentTransitions, + dataContract, +) { + const result = new ValidationResult(); + + rawDocumentTransitions.forEach((rawDocumentTransition) => { + const documentSchema = dataContract.getDocumentSchema(rawDocumentTransition.$type); + + if (!documentSchema.indices) { + return; + } + + documentSchema.indices + .filter((index) => index.unique && index.properties.length > 1) + .forEach((indexDefinition) => { + const data = indexDefinition.properties.map((property) => { + const [propertyPath] = Object.keys(property); + + if (propertyPath === '$ownerId') { + return ownerId; + } + + if (propertyPath.startsWith('$')) { + return rawDocumentTransition[propertyPath]; + } + + return lodashGet(rawDocumentTransition, propertyPath); + }); + + const allAreDefined = data.every((item) => item !== undefined); + const allAreUndefined = data.every((item) => item === undefined); + + const isOk = allAreDefined || allAreUndefined; + + if (!isOk) { + result.addError( + new InconsistentCompoundIndexDataError( + rawDocumentTransition.$type, + indexDefinition.properties.map((i) => Object.keys(i)[0]), + ), + ); + } + }); + }); + + return result; +} + +module.exports = validatePartialCompoundIndices; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/state/executeDataTriggersFactory.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/state/executeDataTriggersFactory.js new file mode 100644 index 00000000000..1a69a2c0ef9 --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/state/executeDataTriggersFactory.js @@ -0,0 +1,70 @@ +/** + * Execute data triggers for a document sequentially + * + * @param {DocumentCreateTransition[] + * |DocumentReplaceTransition[] + * |DocumentDeleteTransition[]} documentTransition + * @param {DataTrigger[]} dataTriggers + * @param {DataTriggerExecutionContext} context + * @param {DataTriggerExecutionResult[]} results + * + * @return {Promise} + */ +async function executeTriggersSequentially(documentTransition, dataTriggers, context, results) { + return dataTriggers.reduce(async (previousPromise, dataTrigger) => { + const result = await previousPromise; + if (result) { + results.push(result); + } + return dataTrigger.execute(documentTransition, context); + }, Promise.resolve()).then((lastResult) => results.push(lastResult)); +} + +/** + * Execute data trigger for a document with a context (factory) + * + * @param {getDataTriggers} getDataTriggers + * + * @return {executeDataTriggers} + */ +function executeDataTriggersFactory(getDataTriggers) { + /** + * Execute data trigger for a document with a context + * + * @typedef {executeDataTriggers} + * + * @param {DocumentCreateTransition[] + * |DocumentReplaceTransition[] + * |DocumentDeleteTransition[]} documentTransitions + * @param {DataTriggerExecutionContext} context + * + * @return {Promise} + */ + async function executeDataTriggers(documentTransitions, context) { + const dataContractId = context.getDataContract().getId(); + + const results = []; + + await documentTransitions.reduce(async (previousPromise, documentTransition) => { + await previousPromise; + + const dataTriggers = getDataTriggers( + dataContractId, + documentTransition.getType(), + documentTransition.getAction(), + ); + + if (dataTriggers.length === 0) { + return Promise.resolve(); + } + + return executeTriggersSequentially(documentTransition, dataTriggers, context, results); + }, Promise.resolve()); + + return results; + } + + return executeDataTriggers; +} + +module.exports = executeDataTriggersFactory; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/state/fetchDocumentsFactory.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/state/fetchDocumentsFactory.js new file mode 100644 index 00000000000..ff9459cc5c1 --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/state/fetchDocumentsFactory.js @@ -0,0 +1,53 @@ +/** + * @param {StateRepository} stateRepository + * @return {fetchDocuments} + */ +function fetchDocumentsFactory(stateRepository) { + /** + * @typedef fetchDocuments + * @param {DocumentCreateTransition[] + * |DocumentReplaceTransition[] + * |DocumentDeleteTransition[]} documentTransitions + * @param {StateTransitionExecutionContext} executionContext + * @return {Promise} + */ + async function fetchDocuments(documentTransitions, executionContext) { + // Group document transitions by contracts and types + const transitionsByContractsAndTypes = documentTransitions.reduce((obj, dt) => { + const uniqueKey = `${dt.getDataContractId()}${dt.getType()}`; + + if (!obj[uniqueKey]) { + // eslint-disable-next-line no-param-reassign + obj[uniqueKey] = []; + } + + obj[uniqueKey].push(dt); + + return obj; + }, {}); + + // Fetch Documents + const fetchedDocumentsPromises = Object.values(transitionsByContractsAndTypes) + .map((transitions) => { + const options = { + where: [['$id', 'in', transitions.map((t) => t.getId())]], + orderBy: [['$id', 'asc']], + }; + + return stateRepository.fetchDocuments( + transitions[0].getDataContractId(), + transitions[0].getType(), + options, + executionContext, + ); + }); + + const fetchedDocuments = await Promise.all(fetchedDocumentsPromises); + + return fetchedDocuments.reduce((array, docs) => array.concat(docs), []); + } + + return fetchDocuments; +} + +module.exports = fetchDocumentsFactory; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/state/validateDocumentsBatchTransitionStateFactory.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/state/validateDocumentsBatchTransitionStateFactory.js new file mode 100644 index 00000000000..40a38d228ea --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/state/validateDocumentsBatchTransitionStateFactory.js @@ -0,0 +1,325 @@ +const DataTriggerExecutionContext = require('../../../../../dataTrigger/DataTriggerExecutionContext'); + +const ValidationResult = require('../../../../../validation/ValidationResult'); + +const DocumentAlreadyPresentError = require('../../../../../errors/consensus/state/document/DocumentAlreadyPresentError'); +const DocumentNotFoundError = require('../../../../../errors/consensus/state/document/DocumentNotFoundError'); +const DocumentOwnerIdMismatchError = require('../../../../../errors/consensus/state/document/DocumentOwnerIdMismatchError'); +const InvalidDocumentRevisionError = require('../../../../../errors/consensus/state/document/InvalidDocumentRevisionError'); +const InvalidDocumentActionError = require('../../../../errors/InvalidDocumentActionError'); +const DataContractNotPresentError = require('../../../../../errors/DataContractNotPresentError'); +const DocumentTimestampWindowViolationError = require( + '../../../../../errors/consensus/state/document/DocumentTimestampWindowViolationError', +); +const DocumentTimestampsMismatchError = require( + '../../../../../errors/consensus/state/document/DocumentTimestampsMismatchError', +); + +const AbstractDocumentTransition = require('../../documentTransition/AbstractDocumentTransition'); + +const validateTimeInBlockTimeWindow = require('../../../../../blockTimeWindow/validateTimeInBlockTimeWindow'); +const StateTransitionExecutionContext = require('../../../../../stateTransition/StateTransitionExecutionContext'); + +/** + * + * @param {StateRepository} stateRepository + * @param {fetchDocuments} fetchDocuments + * @param {validateDocumentsUniquenessByIndices} validateDocumentsUniquenessByIndices + * @param {executeDataTriggers} executeDataTriggers + * @return {validateDocumentsBatchTransitionState} + */ +function validateDocumentsBatchTransitionStateFactory( + stateRepository, + fetchDocuments, + validateDocumentsUniquenessByIndices, + executeDataTriggers, +) { + /** + * + * @param {Identifier} dataContractId + * @param {Identifier} ownerId + * @param {DocumentCreateTransition[] + * |DocumentReplaceTransition[] + * |DocumentDeleteTransition[]} documentTransitions + * @param {StateTransitionExecutionContext} executionContext + * @return {Promise} + */ + async function validateDocumentTransitions( + dataContractId, + ownerId, + documentTransitions, + executionContext, + ) { + const result = new ValidationResult(); + + // We use temporary execution context without dry run, + // because despite the dryRun, we need to get the + // data contract to proceed with following logic + const tmpExecutionContext = new StateTransitionExecutionContext(); + + // Data contract must exist + const dataContract = await stateRepository.fetchDataContract( + dataContractId, + tmpExecutionContext, + ); + + // Collect operations back from temporary context + executionContext.addOperation(...tmpExecutionContext.getOperations()); + + if (!dataContract) { + throw new DataContractNotPresentError(dataContractId); + } + + if (!result.isValid()) { + return result; + } + + const fetchedDocuments = await fetchDocuments(documentTransitions, executionContext); + + if (!executionContext.isDryRun()) { + // Calculate time window for timestamps + const { + time: { + seconds: lastBlockHeaderTimeSeconds, + }, + } = await stateRepository.fetchLatestPlatformBlockHeader(); + + // Get last block header time in milliseconds + const lastBlockHeaderTime = lastBlockHeaderTimeSeconds * 1000; + + // Validate document action, ownerId, revision and timestamps + documentTransitions + .forEach((documentTransition) => { + const fetchedDocument = fetchedDocuments + .find((d) => documentTransition.getId() + .equals(d.getId())); + + switch (documentTransition.getAction()) { + case AbstractDocumentTransition.ACTIONS.CREATE: + // createdAt and updatedAt should be equal + if (documentTransition.getCreatedAt() !== undefined + && documentTransition.getUpdatedAt() !== undefined) { + const createdAtTime = documentTransition.getCreatedAt() + .getTime(); + const updatedAtTime = documentTransition.getUpdatedAt() + .getTime(); + + if (createdAtTime !== updatedAtTime) { + result.addError( + new DocumentTimestampsMismatchError( + documentTransition.getId() + .toBuffer(), + ), + ); + } + } + + // Check createdAt is within a block time window + if (documentTransition.getCreatedAt() !== undefined) { + const createdAtTime = documentTransition.getCreatedAt() + .getTime(); + + const validateTimeWindowResult = validateTimeInBlockTimeWindow( + lastBlockHeaderTime, + createdAtTime, + ); + if (!validateTimeWindowResult.isValid()) { + result.addError( + new DocumentTimestampWindowViolationError( + 'createdAt', + documentTransition.getId() + .toBuffer(), + documentTransition.getCreatedAt(), + validateTimeWindowResult.getTimeWindowStart(), + validateTimeWindowResult.getTimeWindowEnd(), + ), + ); + } + } + + // Check updatedAt is within a block time window + if (documentTransition.getUpdatedAt() !== undefined) { + const updatedAtTime = documentTransition.getUpdatedAt() + .getTime(); + const validateTimeWindowResult = validateTimeInBlockTimeWindow( + lastBlockHeaderTime, + updatedAtTime, + ); + + if (!validateTimeWindowResult.isValid()) { + result.addError( + new DocumentTimestampWindowViolationError( + 'updatedAt', + documentTransition.getId() + .toBuffer(), + documentTransition.getUpdatedAt(), + validateTimeWindowResult.getTimeWindowStart(), + validateTimeWindowResult.getTimeWindowEnd(), + ), + ); + } + } + + if (fetchedDocument) { + result.addError( + new DocumentAlreadyPresentError(documentTransition.getId() + .toBuffer()), + ); + } + break; + case AbstractDocumentTransition.ACTIONS.REPLACE: { + // Check updatedAt is within a block time window + if (documentTransition.getUpdatedAt() !== undefined) { + const updatedAtTime = documentTransition.getUpdatedAt() + .getTime(); + + const validateTimeWindowResult = validateTimeInBlockTimeWindow( + lastBlockHeaderTime, + updatedAtTime, + ); + + if (!validateTimeWindowResult.isValid()) { + result.addError( + new DocumentTimestampWindowViolationError( + 'updatedAt', + documentTransition.getId() + .toBuffer(), + documentTransition.getUpdatedAt(), + validateTimeWindowResult.getTimeWindowStart(), + validateTimeWindowResult.getTimeWindowEnd(), + ), + ); + } + } + + if ( + fetchedDocument + && documentTransition.getRevision() !== fetchedDocument.getRevision() + 1 + ) { + result.addError( + new InvalidDocumentRevisionError( + documentTransition.getId() + .toBuffer(), + fetchedDocument.getRevision(), + ), + ); + } + } + // eslint-disable-next-line no-fallthrough + case AbstractDocumentTransition.ACTIONS.DELETE: { + if (!fetchedDocument) { + result.addError( + new DocumentNotFoundError(documentTransition.getId() + .toBuffer()), + ); + + break; + } + + if (!fetchedDocument.getOwnerId() + .equals(ownerId)) { + result.addError( + new DocumentOwnerIdMismatchError( + documentTransition.getId() + .toBuffer(), + ownerId.toBuffer(), + fetchedDocument.getOwnerId() + .toBuffer(), + ), + ); + } + + break; + } + default: + throw new InvalidDocumentActionError(documentTransition); + } + }); + + if (!result.isValid()) { + return result; + } + } + + // Validate unique indices + const nonDeleteDocumentTransitions = documentTransitions + .filter((d) => d.getAction() !== AbstractDocumentTransition.ACTIONS.DELETE); + + if (nonDeleteDocumentTransitions.length > 0) { + result.merge( + await validateDocumentsUniquenessByIndices( + ownerId, + nonDeleteDocumentTransitions, + dataContract, + executionContext, + ), + ); + + if (!result.isValid()) { + return result; + } + } + + // Run Data Triggers + const dataTriggersExecutionContext = new DataTriggerExecutionContext( + stateRepository, + ownerId, + dataContract, + executionContext, + ); + + const dataTriggersExecutionResults = await executeDataTriggers( + documentTransitions, + dataTriggersExecutionContext, + ); + + dataTriggersExecutionResults.forEach((dataTriggerExecutionResult) => { + if (!dataTriggerExecutionResult.isOk()) { + result.addError(...dataTriggerExecutionResult.getErrors()); + } + }); + + return result; + } + /** + * @typedef validateDocumentsBatchTransitionState + * @param {DocumentsBatchTransition} stateTransition + * @return {ValidationResult} + */ + async function validateDocumentsBatchTransitionState(stateTransition) { + const result = new ValidationResult(); + + const executionContext = stateTransition.getExecutionContext(); + const ownerId = stateTransition.getOwnerId(); + + // Group document transitions by data contracts + const documentTransitionsByContracts = stateTransition.getTransitions() + .reduce((obj, documentTransition) => { + if (!obj[documentTransition.getDataContractId()]) { + // eslint-disable-next-line no-param-reassign + obj[documentTransition.getDataContractId()] = { + dataContractId: documentTransition.getDataContractId(), + documentTransitions: [], + }; + } + + obj[documentTransition.getDataContractId()].documentTransitions.push(documentTransition); + + return obj; + }, {}); + + const documentTransitionResultsPromises = Object.entries(documentTransitionsByContracts) + .map(([, { dataContractId, documentTransitions }]) => ( + validateDocumentTransitions(dataContractId, ownerId, documentTransitions, executionContext) + )); + + const documentTransitionResults = await Promise.all(documentTransitionResultsPromises); + documentTransitionResults.forEach(result.merge.bind(result)); + + return result; + } + + return validateDocumentsBatchTransitionState; +} + +module.exports = validateDocumentsBatchTransitionStateFactory; diff --git a/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/state/validateDocumentsUniquenessByIndicesFactory.js b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/state/validateDocumentsUniquenessByIndicesFactory.js new file mode 100644 index 00000000000..e15100c35b7 --- /dev/null +++ b/packages/js-dpp/lib/document/stateTransition/DocumentsBatchTransition/validation/state/validateDocumentsUniquenessByIndicesFactory.js @@ -0,0 +1,136 @@ +const ValidationResult = require('../../../../../validation/ValidationResult'); +const DuplicateUniqueIndexError = require('../../../../../errors/consensus/state/document/DuplicateUniqueIndexError'); +const AbstractDocumentTransition = require('../../documentTransition/AbstractDocumentTransition'); + +/** + * @param {StateRepository} stateRepository + * @return {validateDocumentsUniquenessByIndices} + */ +function validateDocumentsUniquenessByIndicesFactory(stateRepository) { + /** + * @typedef validateDocumentsUniquenessByIndices + * @param {Identifier} ownerId + * @param {DocumentCreateTransition[] + * |DocumentReplaceTransition[]} documentTransitions + * @param {DataContract} dataContract + * @param {StateTransitionExecutionContext} executionContext + * @return {ValidationResult} + */ + async function validateDocumentsUniquenessByIndices( + ownerId, + documentTransitions, + dataContract, + executionContext, + ) { + const result = new ValidationResult(); + + // 1. Prepare fetchDocuments queries from indexed properties + const documentIndexQueries = documentTransitions + .reduce((queries, documentTransition) => { + const documentSchema = dataContract.getDocumentSchema(documentTransition.getType()); + + if (!documentSchema.indices) { + return queries; + } + + documentSchema.indices + .filter((index) => index.unique) + .forEach((indexDefinition) => { + const where = []; + + indexDefinition.properties.forEach((property) => { + const propertyName = Object.keys(property)[0]; + let propertyValue; + + switch (propertyName) { + case '$ownerId': + propertyValue = ownerId; + break; + case '$createdAt': + if (documentTransition.getAction() + === AbstractDocumentTransition.ACTIONS.CREATE) { + const createdAt = documentTransition.getCreatedAt(); + if (createdAt) { + propertyValue = createdAt.getTime(); + } + } + break; + case '$updatedAt': { + const updatedAt = documentTransition.getUpdatedAt(); + if (updatedAt) { + propertyValue = updatedAt.getTime(); + } + } + break; + default: + propertyValue = documentTransition.get(propertyName); + } + + if (propertyValue !== undefined) { + where.push([propertyName, '==', propertyValue]); + } + }); + + queries.push({ + type: documentTransition.getType(), + indexDefinition, + documentTransition, + where, + }); + }); + + return queries; + }, []); + + // 2. Fetch Document by indexed properties + const fetchRawDocumentPromises = documentIndexQueries + .filter(({ where }) => where.length > 0) + .map(async ({ + type, + where, + indexDefinition, + documentTransition, + }) => { + const doc = await stateRepository.fetchDocuments( + dataContract.getId(), + type, + { where }, + executionContext, + ); + + return Object.assign(doc, { + indexDefinition, + documentTransition, + }); + }); + + const fetchedDocumentsByIndices = await Promise.all(fetchRawDocumentPromises); + + if (executionContext.isDryRun()) { + return result; + } + + // 3. Create errors if duplicates found + fetchedDocumentsByIndices + .filter((docs) => { + const isEmpty = docs.length === 0; + const onlyOriginDocument = docs.length === 1 + && docs[0].getId().equals(docs.documentTransition.getId()); + + return !isEmpty && !onlyOriginDocument; + }).forEach((rawDocuments) => { + result.addError( + new DuplicateUniqueIndexError( + rawDocuments.documentTransition.getId().toBuffer(), + rawDocuments.indexDefinition.properties.map((i) => Object.keys(i)[0]), + ), + ); + }); + + return result; + } + + return validateDocumentsUniquenessByIndices; +} + +module.exports = validateDocumentsUniquenessByIndicesFactory; diff --git a/packages/js-dpp/lib/document/validation/validateDocumentFactory.js b/packages/js-dpp/lib/document/validation/validateDocumentFactory.js new file mode 100644 index 00000000000..f875155748a --- /dev/null +++ b/packages/js-dpp/lib/document/validation/validateDocumentFactory.js @@ -0,0 +1,84 @@ +const baseDocumentSchema = require('../../../schema/document/documentBase.json'); + +const ValidationResult = require('../../validation/ValidationResult'); + +const convertBuffersToArrays = require('../../util/convertBuffersToArrays'); + +const InvalidDocumentTypeError = require('../../errors/consensus/basic/document/InvalidDocumentTypeError'); +const MissingDocumentTypeError = require('../../errors/consensus/basic/document/MissingDocumentTypeError'); + +/** + * @param {JsonSchemaValidator} validator + * @param {enrichDataContractWithBaseSchema} enrichDataContractWithBaseSchema + * @param {validateProtocolVersion} validateProtocolVersion + * + * @return {validateDocument} + */ +module.exports = function validateDocumentFactory( + validator, + enrichDataContractWithBaseSchema, + validateProtocolVersion, +) { + /** + * @typedef validateDocument + * @param {RawDocument} rawDocument + * @param {DataContract} dataContract + * @return {ValidationResult} + */ + function validateDocument(rawDocument, dataContract) { + const result = new ValidationResult(); + + if (!Object.prototype.hasOwnProperty.call(rawDocument, '$type')) { + result.addError( + new MissingDocumentTypeError(), + ); + + return result; + } + + if (!dataContract.isDocumentDefined(rawDocument.$type)) { + result.addError( + new InvalidDocumentTypeError( + rawDocument.$type, + dataContract.getId().toBuffer(), + ), + ); + + return result; + } + + const enrichedDataContract = enrichDataContractWithBaseSchema( + dataContract, + baseDocumentSchema, + enrichDataContractWithBaseSchema.PREFIX_BYTE_0, + ); + + const documentSchemaRef = enrichedDataContract.getDocumentSchemaRef( + rawDocument.$type, + ); + + const additionalSchemas = { + [enrichedDataContract.getJsonSchemaId()]: enrichedDataContract.toJSON(), + }; + + result.merge( + validator.validate( + documentSchemaRef, + convertBuffersToArrays(rawDocument), + additionalSchemas, + ), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + validateProtocolVersion(rawDocument.$protocolVersion), + ); + + return result; + } + + return validateDocument; +}; diff --git a/packages/js-dpp/lib/errors/CompatibleProtocolVersionIsNotDefinedError.js b/packages/js-dpp/lib/errors/CompatibleProtocolVersionIsNotDefinedError.js new file mode 100644 index 00000000000..4651ee8ce1d --- /dev/null +++ b/packages/js-dpp/lib/errors/CompatibleProtocolVersionIsNotDefinedError.js @@ -0,0 +1,21 @@ +const DPPError = require('./DPPError'); + +class CompatibleProtocolVersionIsNotDefinedError extends DPPError { + /** + * @param {number} currentProtocolVersion + */ + constructor(currentProtocolVersion) { + super(`Compatible version is not defined for protocol version ${currentProtocolVersion}`); + + this.currentProtocolVersion = currentProtocolVersion; + } + + /** + * @return {number} + */ + getCurrentProtocolVersion() { + return this.currentProtocolVersion; + } +} + +module.exports = CompatibleProtocolVersionIsNotDefinedError; diff --git a/packages/js-dpp/lib/errors/DPPError.js b/packages/js-dpp/lib/errors/DPPError.js new file mode 100644 index 00000000000..07b73e7387a --- /dev/null +++ b/packages/js-dpp/lib/errors/DPPError.js @@ -0,0 +1,17 @@ +class DPPError extends Error { + /** + * @param {string} message + */ + constructor(message) { + super(); + + this.name = this.constructor.name; + this.message = message; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + } +} + +module.exports = DPPError; diff --git a/packages/js-dpp/lib/errors/DataContractNotPresentError.js b/packages/js-dpp/lib/errors/DataContractNotPresentError.js new file mode 100644 index 00000000000..7e1dd51ef42 --- /dev/null +++ b/packages/js-dpp/lib/errors/DataContractNotPresentError.js @@ -0,0 +1,23 @@ +const DPPError = require('./DPPError'); + +class DataContractNotPresentError extends DPPError { + /** + * @param {Identifier} dataContractId + */ + constructor(dataContractId) { + super('Data Contract is not present'); + + this.dataContractId = dataContractId; + } + + /** + * Get Data Contract ID + * + * @return {Identifier} + */ + getDataContractId() { + return this.dataContractId; + } +} + +module.exports = DataContractNotPresentError; diff --git a/packages/js-dpp/lib/errors/InvalidDocumentTypeError.js b/packages/js-dpp/lib/errors/InvalidDocumentTypeError.js new file mode 100644 index 00000000000..38f6f104bcb --- /dev/null +++ b/packages/js-dpp/lib/errors/InvalidDocumentTypeError.js @@ -0,0 +1,34 @@ +const DPPError = require('./DPPError'); + +class InvalidDocumentTypeError extends DPPError { + /** + * @param {string} type + * @param {DataContract} dataContract + */ + constructor(type, dataContract) { + super(`Data Contract doesn't define document with type ${type}`); + + this.type = type; + this.dataContract = dataContract; + } + + /** + * Get type + * + * @return {string} + */ + getType() { + return this.type; + } + + /** + * Get Data Contract + * + * @return {DataContract} + */ + getDataContract() { + return this.dataContract; + } +} + +module.exports = InvalidDocumentTypeError; diff --git a/packages/js-dpp/lib/errors/MissingOptionError.js b/packages/js-dpp/lib/errors/MissingOptionError.js new file mode 100644 index 00000000000..a25b2885a3c --- /dev/null +++ b/packages/js-dpp/lib/errors/MissingOptionError.js @@ -0,0 +1,22 @@ +const DPPError = require('./DPPError'); + +class MissingOptionError extends DPPError { + /** + * @param {string} optionName + * @param {string} message + */ + constructor(optionName, message) { + super(message); + + this.optionName = optionName; + } + + /** + * @return {string} + */ + getOptionName() { + return this.optionName; + } +} + +module.exports = MissingOptionError; diff --git a/packages/js-dpp/lib/errors/consensus/AbstractConsensusError.js b/packages/js-dpp/lib/errors/consensus/AbstractConsensusError.js new file mode 100644 index 00000000000..c4da5d16fe5 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/AbstractConsensusError.js @@ -0,0 +1,58 @@ +const DPPError = require('../DPPError'); + +const CONSTRUCTOR_ARGUMENTS_SYMBOL = Symbol.for('constructorArguments'); + +/** + * @abstract + */ +class AbstractConsensusError extends DPPError { + /** + * @param {string} message + */ + constructor(message) { + super(message); + + this[CONSTRUCTOR_ARGUMENTS_SYMBOL] = []; + } + + /** + * @return {number} + */ + getCode() { + // Mitigate recursive dependency + + // eslint-disable-next-line global-require + const codes = require('./codes'); + + const code = Object.keys(codes) + .find((c) => this.constructor === codes[c]); + + if (!code) { + throw new Error('Error code is not defined'); + } + + return parseInt(code, 10); + } + + /** + * Get array of the error's arguments + * + * @returns {*[]} + */ + getConstructorArguments() { + return this[CONSTRUCTOR_ARGUMENTS_SYMBOL]; + } + + /** + * Set the error's arguments. + * Must be called from the constructor + * + * @protected + * @param {Object|Array} args + */ + setConstructorArguments(args) { + this[CONSTRUCTOR_ARGUMENTS_SYMBOL] = Array.from(args); + } +} + +module.exports = AbstractConsensusError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/AbstractBasicError.js b/packages/js-dpp/lib/errors/consensus/basic/AbstractBasicError.js new file mode 100644 index 00000000000..876c150d2db --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/AbstractBasicError.js @@ -0,0 +1,10 @@ +const AbstractConsensusError = require('../AbstractConsensusError'); + +/** + * @abstract + */ +class AbstractBasicError extends AbstractConsensusError { + +} + +module.exports = AbstractBasicError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/IncompatibleProtocolVersionError.js b/packages/js-dpp/lib/errors/consensus/basic/IncompatibleProtocolVersionError.js new file mode 100644 index 00000000000..c1619a4435e --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/IncompatibleProtocolVersionError.js @@ -0,0 +1,35 @@ +const AbstractConsensusError = require('../AbstractConsensusError'); + +class IncompatibleProtocolVersionError extends AbstractConsensusError { + /** + * @param {number} parsedProtocolVersion + * @param {number} minimalProtocolVersion + */ + constructor(parsedProtocolVersion, minimalProtocolVersion) { + super( + `Protocol version ${parsedProtocolVersion} is not supported. Minimal supported protocol version is ${minimalProtocolVersion}`, + ); + + this.parsedProtocolVersion = parsedProtocolVersion; + this.minimalProtocolVersion = minimalProtocolVersion; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @return {number} + */ + getParsedProtocolVersion() { + return this.parsedProtocolVersion; + } + + /** + * @return {number} + */ + getMinimalProtocolVersion() { + return this.minimalProtocolVersion; + } +} + +module.exports = IncompatibleProtocolVersionError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/InvalidIdentifierError.js b/packages/js-dpp/lib/errors/consensus/basic/InvalidIdentifierError.js new file mode 100644 index 00000000000..7e004f550bf --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/InvalidIdentifierError.js @@ -0,0 +1,45 @@ +const AbstractBasicError = require('./AbstractBasicError'); + +class InvalidIdentifierError extends AbstractBasicError { + /** + * @param {string} identifierName + * @param {string} message + */ + constructor(identifierName, message) { + super(`Invalid ${identifierName}: ${message}`); + + this.identifierName = identifierName; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get identifier name + * + * @return {string} + */ + getIdentifierName() { + return this.identifierName; + } + + /** + * Set identifier error + * + * @param {Error} error + */ + setIdentifierError(error) { + this.identifierError = error; + } + + /** + * Get identifier error + * + * @return {Error} + */ + getIdentifierError() { + return this.identifierError; + } +} + +module.exports = InvalidIdentifierError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/JsonSchemaCompilationError.js b/packages/js-dpp/lib/errors/consensus/basic/JsonSchemaCompilationError.js new file mode 100644 index 00000000000..ebf9699af1f --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/JsonSchemaCompilationError.js @@ -0,0 +1,12 @@ +const AbstractBasicError = require('./AbstractBasicError'); + +class JsonSchemaCompilationError extends AbstractBasicError { + constructor(message) { + super(message); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } +} + +module.exports = JsonSchemaCompilationError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/JsonSchemaError.js b/packages/js-dpp/lib/errors/consensus/basic/JsonSchemaError.js new file mode 100644 index 00000000000..ee5b4f9cc3e --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/JsonSchemaError.js @@ -0,0 +1,65 @@ +const AbstractBasicError = require('./AbstractBasicError'); + +class JsonSchemaError extends AbstractBasicError { + /** + * @param {string} message + * @param {string} keyword + * @param {string} instancePath + * @param {string} schemaPath + * @param {Object} params + * @param {string} [propertyName] + */ + constructor(message, keyword, instancePath, schemaPath, params, propertyName) { + super(message); + + this.keyword = keyword; + + this.instancePath = instancePath; + + this.schemaPath = schemaPath; + + this.params = params; + + this.propertyName = propertyName; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @return {string} + */ + getKeyword() { + return this.keyword; + } + + /** + * @return {string} + */ + getInstancePath() { + return this.instancePath; + } + + /** + * @return {string} + */ + getSchemaPath() { + return this.schemaPath; + } + + /** + * @return {Object} + */ + getParams() { + return this.params; + } + + /** + * @return {string} + */ + getPropertyName() { + return this.propertyName; + } +} + +module.exports = JsonSchemaError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/UnsupportedProtocolVersionError.js b/packages/js-dpp/lib/errors/consensus/basic/UnsupportedProtocolVersionError.js new file mode 100644 index 00000000000..a9664747c35 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/UnsupportedProtocolVersionError.js @@ -0,0 +1,35 @@ +const AbstractConsensusError = require('../AbstractConsensusError'); + +class UnsupportedProtocolVersionError extends AbstractConsensusError { + /** + * @param {number} parsedProtocolVersion + * @param {number} latestVersion + */ + constructor(parsedProtocolVersion, latestVersion) { + super( + `Protocol version ${parsedProtocolVersion} is not supported. Latest supported version is ${latestVersion}`, + ); + + this.parsedProtocolVersion = parsedProtocolVersion; + this.latestVersion = latestVersion; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @return {number} + */ + getParsedProtocolVersion() { + return this.parsedProtocolVersion; + } + + /** + * @return {number} + */ + getLatestVersion() { + return this.latestVersion; + } +} + +module.exports = UnsupportedProtocolVersionError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/AbstractIndexError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/AbstractIndexError.js new file mode 100644 index 00000000000..60799a6bdf8 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/AbstractIndexError.js @@ -0,0 +1,39 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +/** + * @class + * @abstract + */ +class AbstractIndexError extends AbstractBasicError { + /** + * @param {string} message + * @param {string} documentType + * @param {Object} indexDefinition + */ + constructor(message, documentType, indexDefinition) { + super(message); + + this.documentType = documentType; + this.indexDefintion = indexDefinition; + } + + /** + * Get Document type + * + * @return {string} + */ + getDocumentType() { + return this.documentType; + } + + /** + * Get index definition + * + * @return {Object} + */ + getIndexDefinition() { + return this.indexDefintion; + } +} + +module.exports = AbstractIndexError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractHaveNewUniqueIndexError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractHaveNewUniqueIndexError.js new file mode 100644 index 00000000000..23866c280f9 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractHaveNewUniqueIndexError.js @@ -0,0 +1,37 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class DataContractHaveNewUniqueIndexError extends AbstractBasicError { + /** + * @param {string} documentType + * @param {string} indexName + */ + constructor(documentType, indexName) { + super(`Document with type ${documentType} has a new unique index named "${indexName}". Adding unique indices during Data Contract update is not allowed.`); + + this.documentType = documentType; + this.indexName = indexName; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get document type with changed indices + * + * @returns {string} + */ + getDocumentType() { + return this.documentType; + } + + /** + * Get unique index name + * + * @returns {string} + */ + getIndexName() { + return this.indexName; + } +} + +module.exports = DataContractHaveNewUniqueIndexError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractImmutablePropertiesUpdateError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractImmutablePropertiesUpdateError.js new file mode 100644 index 00000000000..4cc8eeca481 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractImmutablePropertiesUpdateError.js @@ -0,0 +1,62 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class DataContractImmutablePropertiesUpdateError extends AbstractBasicError { + /** + * @param {string} operation + * @param {string} fieldPath + */ + constructor(operation, fieldPath) { + let message = 'Only $defs, version and documents fields are allowed to be updated.'; + + if (operation === 'remove') { + message = `${message} Immutable field '${fieldPath}' has been removed.`; + } + + if (operation === 'replace') { + message = `${message} Immutable field '${fieldPath}' has been changed.`; + } + + super(message); + + this.operation = operation; + this.fieldPath = fieldPath; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get operation + * @returns {string} + */ + getOperation() { + return this.operation; + } + + /** + * Get updated field path + * @returns {string} + */ + getFieldPath() { + return this.fieldPath; + } + + /** + * Set diff + * @param {{ op: string, path: string }[]} diff + */ + setDiff(diff) { + this.diff = diff; + } + + /** + * Get diff + * + * @returns {{ op: string, path: string }[]} + */ + getDiff() { + return this.diff; + } +} + +module.exports = DataContractImmutablePropertiesUpdateError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractInvalidIndexDefinitionUpdateError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractInvalidIndexDefinitionUpdateError.js new file mode 100644 index 00000000000..8369507f28b --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractInvalidIndexDefinitionUpdateError.js @@ -0,0 +1,37 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class DataContractInvalidIndexDefinitionUpdateError extends AbstractBasicError { + /** + * @param {string} documentType + * @param {string} indexName + */ + constructor(documentType, indexName) { + super(`Document with type ${documentType} has badly constructed index "${indexName}". Existing properties in the indices should be defined in the beginning of it.`); + + this.documentType = documentType; + this.indexName = indexName; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get document type with changed indices + * + * @returns {string} + */ + getDocumentType() { + return this.documentType; + } + + /** + * Get index name that have old properties + * + * @returns {string} + */ + getIndexName() { + return this.indexName; + } +} + +module.exports = DataContractInvalidIndexDefinitionUpdateError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractMaxDepthExceedError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractMaxDepthExceedError.js new file mode 100644 index 00000000000..e8874294cce --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractMaxDepthExceedError.js @@ -0,0 +1,11 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class DataContractMaxDepthExceedError extends AbstractBasicError { + constructor() { + super(`JSON Schema depth is greater than ${DataContractMaxDepthExceedError.MAX_DEPTH}`); + } +} + +DataContractMaxDepthExceedError.MAX_DEPTH = 500; + +module.exports = DataContractMaxDepthExceedError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractUniqueIndicesChangedError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractUniqueIndicesChangedError.js new file mode 100644 index 00000000000..94b76b16734 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DataContractUniqueIndicesChangedError.js @@ -0,0 +1,37 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class DataContractUniqueIndicesChangedError extends AbstractBasicError { + /** + * @param {string} documentType + * @param {string} indexName + */ + constructor(documentType, indexName) { + super(`Document with type ${documentType} has updated unique index named "${indexName}". Change of unique indices is not allowed.`); + + this.documentType = documentType; + this.indexName = indexName; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get document type with changed indices + * + * @returns {string} + */ + getDocumentType() { + return this.documentType; + } + + /** + * Get updated index name + * + * @returns {string} + */ + getIndexName() { + return this.indexName; + } +} + +module.exports = DataContractUniqueIndicesChangedError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/DuplicateIndexError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DuplicateIndexError.js new file mode 100644 index 00000000000..83910d06943 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DuplicateIndexError.js @@ -0,0 +1,22 @@ +const AbstractIndexError = require('./AbstractIndexError'); + +class DuplicateIndexError extends AbstractIndexError { + /** + * @param {string} documentType + * @param {Object} indexDefinition + */ + constructor(documentType, indexDefinition) { + const message = `Duplicate index definition for "${documentType}" document`; + + super( + message, + documentType, + indexDefinition, + ); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } +} + +module.exports = DuplicateIndexError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/DuplicateIndexNameError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DuplicateIndexNameError.js new file mode 100644 index 00000000000..3b877a3e107 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/DuplicateIndexNameError.js @@ -0,0 +1,39 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class DuplicateIndexNameError extends AbstractBasicError { + /** + * @param {string} documentType + * @param {string} duplicateIndexName + */ + constructor(documentType, duplicateIndexName) { + const message = `Duplicate index name "${duplicateIndexName}" defined in "${documentType}" document`; + + super(message); + + this.documentType = documentType; + this.duplicateIndexName = duplicateIndexName; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get Document type + * + * @return {string} + */ + getDocumentType() { + return this.documentType; + } + + /** + * Get duplicate index name + * + * @returns {string} + */ + getDuplicateIndexName() { + return this.duplicateIndexName; + } +} + +module.exports = DuplicateIndexNameError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/IncompatibleDataContractSchemaError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/IncompatibleDataContractSchemaError.js new file mode 100644 index 00000000000..dd2ef1eae68 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/IncompatibleDataContractSchemaError.js @@ -0,0 +1,105 @@ +const AbstractBasicError = require('../AbstractBasicError'); +const Identifier = require('../../../../identifier/Identifier'); + +class IncompatibleDataContractSchemaError extends AbstractBasicError { + /** + * @param {Buffer} dataContractId + * @param {string} operation + * @param {string} fieldPath + */ + constructor(dataContractId, operation, fieldPath) { + let message = `Data Contract updated schema is not backward compatible with one defined in Data Contract with id ${Identifier.from(dataContractId)}.`; + + if (operation === 'remove') { + message = `${message} Field '${fieldPath}' has been removed.`; + } + + if (operation === 'replace') { + message = `${message} Field '${fieldPath}' has been changed.`; + } + + super(message); + + this.dataContractId = dataContractId; + this.operation = operation; + this.fieldPath = fieldPath; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get operation + * @returns {string} + */ + getOperation() { + return this.operation; + } + + /** + * Get updated field path + * @returns {string} + */ + getFieldPath() { + return this.fieldPath; + } + + /** + * Set old data contract schema + * @param {Object} oldSchema + */ + setOldSchema(oldSchema) { + this.oldSchema = oldSchema; + } + + /** + * Get old schema + * @returns {Object} + */ + getOldSchema() { + return this.oldSchema; + } + + /** + * Set new schema + * @param {Object} newSchema + */ + setNewSchema(newSchema) { + this.newSchema = newSchema; + } + + /** + * Get new schema + * @returns {Object} + */ + getNewSchema() { + return this.newSchema; + } + + /** + * Set original validation error + * @param {Error} validationError + */ + setValidationError(validationError) { + this.validationError = validationError; + } + + /** + * Get orignal validation error + * @returns {Error} + */ + getValidationError() { + return this.validationError; + } + + /** + * Get Data Contract ID + * + * @return {Buffer} + */ + getDataContractId() { + return this.dataContractId; + } +} + +module.exports = IncompatibleDataContractSchemaError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/IncompatibleRe2PatternError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/IncompatibleRe2PatternError.js new file mode 100644 index 00000000000..2e1072584f1 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/IncompatibleRe2PatternError.js @@ -0,0 +1,48 @@ +const AbstractConsensusError = require('../../AbstractConsensusError'); + +class IncompatibleRe2PatternError extends AbstractConsensusError { + /** + * + * @param {string} pattern + * @param {string} path + * @param {string} message + */ + constructor(pattern, path, message) { + super(`Pattern ${pattern} at ${path} is not compatible with Re2: ${message}`); + + this.pattern = pattern; + this.path = path; + } + + /** + * + * @returns {string} + */ + getPattern() { + return this.pattern; + } + + /** + * + * @returns {string} + */ + getPath() { + return this.path; + } + + /** + * @param {Error} error + */ + setPatternError(error) { + this.patternError = error; + } + + /** + * @returns {Error} + */ + getPatternError() { + return this.patternError; + } +} + +module.exports = IncompatibleRe2PatternError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidCompoundIndexError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidCompoundIndexError.js new file mode 100644 index 00000000000..1bebb96a0e1 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidCompoundIndexError.js @@ -0,0 +1,21 @@ +const AbstractIndexError = require('./AbstractIndexError'); + +class InvalidCompoundIndexError extends AbstractIndexError { + /** + * + * @param {string} documentType + * @param {Object} indexDefinition + */ + constructor(documentType, indexDefinition) { + super( + `All or none of unique compound index properties must be set for "${documentType}" document`, + documentType, + indexDefinition, + ); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } +} + +module.exports = InvalidCompoundIndexError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidDataContractIdError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidDataContractIdError.js new file mode 100644 index 00000000000..46891dc7f0f --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidDataContractIdError.js @@ -0,0 +1,40 @@ +const bs58 = require('bs58'); + +const AbstractBasicError = require('../AbstractBasicError'); + +const Identifier = require('../../../../identifier/Identifier'); + +class InvalidDataContractIdError extends AbstractBasicError { + /** + * @param {Buffer} expectedId + * @param {Buffer} invalidId + */ + constructor(expectedId, invalidId) { + const expectedIdentifier = Identifier.from(expectedId); + const invalidIdentifier = bs58.encode(invalidId); + + super(`Data Contract ID must be ${expectedIdentifier}, got ${invalidIdentifier}`); + + this.expectedId = expectedId; + this.invalidId = invalidId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @return {Buffer} + */ + getExpectedId() { + return this.expectedId; + } + + /** + * @return {Buffer} + */ + getInvalidId() { + return this.invalidId; + } +} + +module.exports = InvalidDataContractIdError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidDataContractVersionError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidDataContractVersionError.js new file mode 100644 index 00000000000..218d1b14c65 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidDataContractVersionError.js @@ -0,0 +1,33 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidDataContractVersionError extends AbstractBasicError { + /** + * @param {number} expectedVersion + * @param {number} version + */ + constructor(expectedVersion, version) { + super(`Data Contract version must be ${expectedVersion}, got ${version}`); + + this.expectedVersion = expectedVersion; + this.version = version; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @return {number} + */ + getExpectedVersion() { + return this.expectedVersion; + } + + /** + * @return {number} + */ + getVersion() { + return this.version; + } +} + +module.exports = InvalidDataContractVersionError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidIndexPropertyTypeError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidIndexPropertyTypeError.js new file mode 100644 index 00000000000..f6f2c6aa610 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidIndexPropertyTypeError.js @@ -0,0 +1,45 @@ +const AbstractIndexError = require('./AbstractIndexError'); + +class InvalidIndexPropertyTypeError extends AbstractIndexError { + /** + * @param {string} documentType + * @param {Object} indexDefinition + * @param {string} propertyName + * @param {string} propertyType + */ + constructor(documentType, indexDefinition, propertyName, propertyType) { + const message = `'${propertyName}' property for ${documentType} document has an invalid type ${propertyType} and can not be used as index`; + + super( + message, + documentType, + indexDefinition, + ); + + this.propertyName = propertyName; + this.propertyType = propertyType; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get property name + * + * @return {string} + */ + getPropertyName() { + return this.propertyName; + } + + /** + * Get property type name + * + * @return {string} + */ + getPropertyType() { + return this.propertyType; + } +} + +module.exports = InvalidIndexPropertyTypeError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidIndexedPropertyConstraintError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidIndexedPropertyConstraintError.js new file mode 100644 index 00000000000..58acbb13f5e --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidIndexedPropertyConstraintError.js @@ -0,0 +1,63 @@ +const AbstractIndexError = require('./AbstractIndexError'); + +class InvalidIndexedPropertyConstraintError extends AbstractIndexError { + /** + * @param {string} documentType + * @param {Object} indexDefinition + * @param {string} propertyName + * @param {string} constraintName + * @param {string} reason + */ + constructor( + documentType, + indexDefinition, + propertyName, + constraintName, + reason, + ) { + const message = `Indexed property '${propertyName}' for ${documentType} document have invalid constraint '${constraintName}',` + + ` reason '${reason}'`; + + super( + message, + documentType, + indexDefinition, + ); + + this.propertyName = propertyName; + this.constraintName = constraintName; + this.reason = reason; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get property name + * + * @return {string} + */ + getPropertyName() { + return this.propertyName; + } + + /** + * Get property constraint name + * + * @return {string} + */ + getConstraintName() { + return this.constraintName; + } + + /** + * Get invalidity reason + * + * @return {string} + */ + getReason() { + return this.reason; + } +} + +module.exports = InvalidIndexedPropertyConstraintError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidJsonSchemaRefError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidJsonSchemaRefError.js new file mode 100644 index 00000000000..272479c9a13 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/InvalidJsonSchemaRefError.js @@ -0,0 +1,26 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidJsonSchemaRefError extends AbstractBasicError { + constructor(message) { + super(`Invalid JSON Schema $ref: ${message}`); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @param {Error} error + */ + setRefError(error) { + this.refError = error; + } + + /** + * @returns {Error} + */ + getRefError() { + return this.refError; + } +} + +module.exports = InvalidJsonSchemaRefError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/SystemPropertyIndexAlreadyPresentError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/SystemPropertyIndexAlreadyPresentError.js new file mode 100644 index 00000000000..0a379bbc648 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/SystemPropertyIndexAlreadyPresentError.js @@ -0,0 +1,34 @@ +const AbstractIndexError = require('./AbstractIndexError'); + +class SystemPropertyIndexAlreadyPresentError extends AbstractIndexError { + /** + * @param {string} documentType + * @param {Object} indexDefinition + * @param {string} propertyName + */ + constructor(documentType, indexDefinition, propertyName) { + const message = `System property ${propertyName} is already indexed and can't be used in other indices for ${documentType} document.`; + + super( + message, + documentType, + indexDefinition, + ); + + this.propertyName = propertyName; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get property name + * + * @return {string} + */ + getPropertyName() { + return this.propertyName; + } +} + +module.exports = SystemPropertyIndexAlreadyPresentError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/UndefinedIndexPropertyError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/UndefinedIndexPropertyError.js new file mode 100644 index 00000000000..c25ecb89c37 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/UndefinedIndexPropertyError.js @@ -0,0 +1,34 @@ +const AbstractIndexError = require('./AbstractIndexError'); + +class UndefinedIndexPropertyError extends AbstractIndexError { + /** + * @param {string} documentType + * @param {Object} indexDefinition + * @param {string} propertyName + */ + constructor(documentType, indexDefinition, propertyName) { + const message = `'${propertyName}' property is not defined in the ${documentType} document`; + + super( + message, + documentType, + indexDefinition, + ); + + this.propertyName = propertyName; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get property name + * + * @return {string} + */ + getPropertyName() { + return this.propertyName; + } +} + +module.exports = UndefinedIndexPropertyError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/dataContract/UniqueIndicesLimitReachedError.js b/packages/js-dpp/lib/errors/consensus/basic/dataContract/UniqueIndicesLimitReachedError.js new file mode 100644 index 00000000000..6ddf5c07689 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/dataContract/UniqueIndicesLimitReachedError.js @@ -0,0 +1,24 @@ +const AbstractIndexError = require('./AbstractIndexError'); + +class UniqueIndicesLimitReachedError extends AbstractIndexError { + /** + * @param {string} documentType + */ + constructor(documentType) { + const message = `'${documentType}' document has more ` + + `than ${UniqueIndicesLimitReachedError.UNIQUE_INDEX_LIMIT} unique indexes`; + + super( + message, + documentType, + {}, + ); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } +} + +UniqueIndicesLimitReachedError.UNIQUE_INDEX_LIMIT = 3; + +module.exports = UniqueIndicesLimitReachedError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/decode/ProtocolVersionParsingError.js b/packages/js-dpp/lib/errors/consensus/basic/decode/ProtocolVersionParsingError.js new file mode 100644 index 00000000000..3f1eac880f6 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/decode/ProtocolVersionParsingError.js @@ -0,0 +1,31 @@ +const AbstractConsensusError = require('../../AbstractConsensusError'); + +class ProtocolVersionParsingError extends AbstractConsensusError { + /** + * @param {string} message + */ + constructor(message) { + super(`Can't read protocol version from serialized object: ${message}`); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @param {Error} error + */ + setParsingError(error) { + this.parsingError = error; + } + + /** + * Get parsing error + * + * @return {Error} + */ + getParsingError() { + return this.parsingError; + } +} + +module.exports = ProtocolVersionParsingError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/decode/SerializedObjectParsingError.js b/packages/js-dpp/lib/errors/consensus/basic/decode/SerializedObjectParsingError.js new file mode 100644 index 00000000000..be0a7152beb --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/decode/SerializedObjectParsingError.js @@ -0,0 +1,31 @@ +const AbstractConsensusError = require('../../AbstractConsensusError'); + +class SerializedObjectParsingError extends AbstractConsensusError { + /** + * @param {string} message + */ + constructor(message) { + super(`Parsing of a serialized object failed due to: ${message}`); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @param {Error} error + */ + setParsingError(error) { + this.parsingError = error; + } + + /** + * Get parsing error + * + * @return {Error} + */ + getParsingError() { + return this.parsingError; + } +} + +module.exports = SerializedObjectParsingError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/document/DataContractNotPresentError.js b/packages/js-dpp/lib/errors/consensus/basic/document/DataContractNotPresentError.js new file mode 100644 index 00000000000..60bd93267c4 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/document/DataContractNotPresentError.js @@ -0,0 +1,29 @@ +const AbstractBasicError = require('../AbstractBasicError'); +const Identifier = require('../../../../identifier/Identifier'); + +class DataContractNotPresentError extends AbstractBasicError { + /** + * @param {Buffer} dataContractId + */ + constructor(dataContractId) { + const dataContractIdentifier = Identifier.from(dataContractId); + + super(`Data Contract ${dataContractIdentifier} is not present`); + + this.dataContractId = dataContractIdentifier; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get Data Contract ID + * + * @return {Identifier} + */ + getDataContractId() { + return this.dataContractId; + } +} + +module.exports = DataContractNotPresentError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/document/DuplicateDocumentTransitionsWithIdsError.js b/packages/js-dpp/lib/errors/consensus/basic/document/DuplicateDocumentTransitionsWithIdsError.js new file mode 100644 index 00000000000..4186ca89ab7 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/document/DuplicateDocumentTransitionsWithIdsError.js @@ -0,0 +1,34 @@ +const AbstractBasicError = require('../AbstractBasicError'); +const Identifier = require('../../../../identifier/Identifier'); + +class DuplicateDocumentTransitionsWithIdsError extends AbstractBasicError { + /** + * @param { + * [string, Buffer][] + * } documentTransitionReferences + */ + constructor(documentTransitionReferences) { + const references = documentTransitionReferences + .map(([type, id]) => `${type} ${Identifier.from(id)}`).join(', '); + + super(`Document transitions with duplicate IDs: ${references}`); + + this.documentTransitionReferences = documentTransitionReferences; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get duplicate transition references + * + * @return { + * [string, Buffer][] + * } + */ + getDocumentTransitionReferences() { + return this.documentTransitionReferences; + } +} + +module.exports = DuplicateDocumentTransitionsWithIdsError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/document/DuplicateDocumentTransitionsWithIndicesError.js b/packages/js-dpp/lib/errors/consensus/basic/document/DuplicateDocumentTransitionsWithIndicesError.js new file mode 100644 index 00000000000..50420ff52d6 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/document/DuplicateDocumentTransitionsWithIndicesError.js @@ -0,0 +1,34 @@ +const AbstractBasicError = require('../AbstractBasicError'); +const Identifier = require('../../../../identifier/Identifier'); + +class DuplicateDocumentTransitionsWithIndicesError extends AbstractBasicError { + /** + * @param { + * [string, Buffer][] + * } documentTransitionReferences + */ + constructor(documentTransitionReferences) { + const references = documentTransitionReferences + .map(([type, id]) => `${type} ${Identifier.from(id)}`).join(', '); + + super(`Document transitions with duplicate unique properties: ${references}`); + + this.documentTransitionReferences = documentTransitionReferences; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get duplicate raw transition references + * + * @return { + * [string, Buffer][] + * } + */ + getDocumentTransitionReferences() { + return this.documentTransitionReferences; + } +} + +module.exports = DuplicateDocumentTransitionsWithIndicesError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/document/InconsistentCompoundIndexDataError.js b/packages/js-dpp/lib/errors/consensus/basic/document/InconsistentCompoundIndexDataError.js new file mode 100644 index 00000000000..ac66dc529d2 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/document/InconsistentCompoundIndexDataError.js @@ -0,0 +1,35 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InconsistentCompoundIndexDataError extends AbstractBasicError { + /** + * + * @param {string} documentType + * @param {string[]} indexedProperties + */ + constructor(documentType, indexedProperties) { + super(`Unique compound index properties ${indexedProperties.join(', ')} are partially set for ${documentType} document`); + + this.documentType = documentType; + this.indexedProperties = indexedProperties; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @return {string[]} + */ + getIndexedProperties() { + return this.indexedProperties; + } + + /** + * + * @return {string} + */ + getDocumentType() { + return this.documentType; + } +} + +module.exports = InconsistentCompoundIndexDataError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/document/InvalidDocumentTransitionActionError.js b/packages/js-dpp/lib/errors/consensus/basic/document/InvalidDocumentTransitionActionError.js new file mode 100644 index 00000000000..5187a69338a --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/document/InvalidDocumentTransitionActionError.js @@ -0,0 +1,26 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidDocumentTransitionActionError extends AbstractBasicError { + /** + * @param {number} action + */ + constructor(action) { + super(`Document transition action ${action} is not supported`); + + this.action = action; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get action + * + * @return {*} + */ + getAction() { + return this.action; + } +} + +module.exports = InvalidDocumentTransitionActionError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/document/InvalidDocumentTransitionIdError.js b/packages/js-dpp/lib/errors/consensus/basic/document/InvalidDocumentTransitionIdError.js new file mode 100644 index 00000000000..5862f321c60 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/document/InvalidDocumentTransitionIdError.js @@ -0,0 +1,34 @@ +const AbstractBasicError = require('../AbstractBasicError'); +const Identifier = require('../../../../identifier/Identifier'); + +class InvalidDocumentTransitionIdError extends AbstractBasicError { + /** + * @param {Buffer} expectedId + * @param {Buffer} invalidId + */ + constructor(expectedId, invalidId) { + super(`Invalid document transition id ${Identifier.from(invalidId)}, expected ${Identifier.from(expectedId)}`); + + this.expectedId = expectedId; + this.invalidId = invalidId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @return {Buffer} + */ + getExpectedId() { + return this.expectedId; + } + + /** + * @return {Buffer} + */ + getInvalidId() { + return this.invalidId; + } +} + +module.exports = InvalidDocumentTransitionIdError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/document/InvalidDocumentTypeError.js b/packages/js-dpp/lib/errors/consensus/basic/document/InvalidDocumentTypeError.js new file mode 100644 index 00000000000..44810a219ba --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/document/InvalidDocumentTypeError.js @@ -0,0 +1,38 @@ +const AbstractBasicError = require('../AbstractBasicError'); +const Identifier = require('../../../../identifier/Identifier'); + +class InvalidDocumentTypeError extends AbstractBasicError { + /** + * @param {string} type + * @param {Buffer} dataContractId + */ + constructor(type, dataContractId) { + super(`Data Contract ${Identifier.from(dataContractId)} doesn't define document with type ${type}`); + + this.type = type; + this.dataContractId = dataContractId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get type + * + * @return {string} + */ + getType() { + return this.type; + } + + /** + * Get Data Contract ID + * + * @return {Identifier} + */ + getDataContractId() { + return this.dataContractId; + } +} + +module.exports = InvalidDocumentTypeError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/document/MissingDataContractIdError.js b/packages/js-dpp/lib/errors/consensus/basic/document/MissingDataContractIdError.js new file mode 100644 index 00000000000..20f580453f8 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/document/MissingDataContractIdError.js @@ -0,0 +1,9 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class MissingDataContractIdError extends AbstractBasicError { + constructor() { + super('$dataContractId is not present'); + } +} + +module.exports = MissingDataContractIdError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/document/MissingDocumentTransitionActionError.js b/packages/js-dpp/lib/errors/consensus/basic/document/MissingDocumentTransitionActionError.js new file mode 100644 index 00000000000..ad2721055fc --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/document/MissingDocumentTransitionActionError.js @@ -0,0 +1,9 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class MissingDocumentTransitionActionError extends AbstractBasicError { + constructor() { + super('$action is not present'); + } +} + +module.exports = MissingDocumentTransitionActionError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/document/MissingDocumentTransitionTypeError.js b/packages/js-dpp/lib/errors/consensus/basic/document/MissingDocumentTransitionTypeError.js new file mode 100644 index 00000000000..359259fea0b --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/document/MissingDocumentTransitionTypeError.js @@ -0,0 +1,9 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class MissingDocumentTransitionTypeError extends AbstractBasicError { + constructor() { + super('$type is not present'); + } +} + +module.exports = MissingDocumentTransitionTypeError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/document/MissingDocumentTypeError.js b/packages/js-dpp/lib/errors/consensus/basic/document/MissingDocumentTypeError.js new file mode 100644 index 00000000000..390a3350949 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/document/MissingDocumentTypeError.js @@ -0,0 +1,9 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class MissingDocumentTypeError extends AbstractBasicError { + constructor() { + super('$type is not present'); + } +} + +module.exports = MissingDocumentTypeError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/DuplicatedIdentityPublicKeyError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/DuplicatedIdentityPublicKeyError.js new file mode 100644 index 00000000000..125946f0be7 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/DuplicatedIdentityPublicKeyError.js @@ -0,0 +1,26 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class DuplicatedIdentityPublicKeyError extends AbstractBasicError { + /** + * @param {number[]} duplicatedPublicKeyIds + */ + constructor(duplicatedPublicKeyIds) { + super(`Duplicated public keys ${duplicatedPublicKeyIds.join(', ')} found`); + + this.duplicatedPublicKeyIds = duplicatedPublicKeyIds; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get duplicated public key ids + * + * @return {number[]} + */ + getDuplicatedPublicKeysIds() { + return this.duplicatedPublicKeyIds; + } +} + +module.exports = DuplicatedIdentityPublicKeyError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/DuplicatedIdentityPublicKeyIdError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/DuplicatedIdentityPublicKeyIdError.js new file mode 100644 index 00000000000..f78be27e2a0 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/DuplicatedIdentityPublicKeyIdError.js @@ -0,0 +1,26 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class DuplicatedIdentityPublicKeyIdError extends AbstractBasicError { + /** + * @param {number[]} duplicatedIds + */ + constructor(duplicatedIds) { + super(`Duplicated public key ids ${duplicatedIds.join(', ')} found`); + + this.duplicatedIds = duplicatedIds; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get duplicated public key ids + * + * @return {number[]} + */ + getDuplicatedIds() { + return this.duplicatedIds; + } +} + +module.exports = DuplicatedIdentityPublicKeyIdError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/IdentityAssetLockProofLockedTransactionMismatchError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/IdentityAssetLockProofLockedTransactionMismatchError.js new file mode 100644 index 00000000000..a1eca0cbee6 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/IdentityAssetLockProofLockedTransactionMismatchError.js @@ -0,0 +1,33 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class IdentityAssetLockProofLockedTransactionMismatchError extends AbstractBasicError { + /** + * @param {Buffer} instantLockTransactionId + * @param {Buffer} assetLockTransactionId + */ + constructor(instantLockTransactionId, assetLockTransactionId) { + super(`Instant Lock transaction ${instantLockTransactionId.toString('hex')} and Asset lock transaction ${assetLockTransactionId.toString('hex')} mismatch`); + + this.instantLockTransactionId = instantLockTransactionId; + this.assetLockTransactionId = assetLockTransactionId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @return {Buffer} + */ + getInstantLockTransactionId() { + return this.instantLockTransactionId; + } + + /** + * @return {Buffer} + */ + getAssetLockTransactionId() { + return this.assetLockTransactionId; + } +} + +module.exports = IdentityAssetLockProofLockedTransactionMismatchError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/IdentityAssetLockTransactionIsNotFoundError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/IdentityAssetLockTransactionIsNotFoundError.js new file mode 100644 index 00000000000..3b9cdfa1a85 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/IdentityAssetLockTransactionIsNotFoundError.js @@ -0,0 +1,25 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class IdentityAssetLockTransactionIsNotFoundError extends AbstractBasicError { + /** + * @param {Buffer} transactionId + */ + constructor(transactionId) { + super(`Asset Lock transaction ${transactionId.toString('hex')} is not found`); + + this.transactionId = transactionId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * + * @returns {Buffer} + */ + getTransactionId() { + return this.transactionId; + } +} + +module.exports = IdentityAssetLockTransactionIsNotFoundError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/IdentityAssetLockTransactionOutPointAlreadyExistsError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/IdentityAssetLockTransactionOutPointAlreadyExistsError.js new file mode 100644 index 00000000000..ee5afc920cb --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/IdentityAssetLockTransactionOutPointAlreadyExistsError.js @@ -0,0 +1,33 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class IdentityAssetLockTransactionOutPointAlreadyExistsError extends AbstractBasicError { + /** + * @param {Buffer} transactionId + * @param {number} outputIndex + */ + constructor(transactionId, outputIndex) { + super(`Asset lock transaction ${transactionId.toString('hex')} output ${outputIndex} already used`); + + this.transactionId = transactionId; + this.outputIndex = outputIndex; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @return {Buffer} + */ + getTransactionId() { + return this.transactionId; + } + + /** + * @return {number} + */ + getOutputIndex() { + return this.outputIndex; + } +} + +module.exports = IdentityAssetLockTransactionOutPointAlreadyExistsError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/IdentityAssetLockTransactionOutputNotFoundError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/IdentityAssetLockTransactionOutputNotFoundError.js new file mode 100644 index 00000000000..0611a78db70 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/IdentityAssetLockTransactionOutputNotFoundError.js @@ -0,0 +1,24 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class IdentityAssetLockTransactionOutputNotFoundError extends AbstractBasicError { + /** + * @param {number} outputIndex + */ + constructor(outputIndex) { + super(`Asset Lock Transaction Output with index ${outputIndex} not found`); + + this.outputIndex = outputIndex; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @return {number} + */ + getOutputIndex() { + return this.outputIndex; + } +} + +module.exports = IdentityAssetLockTransactionOutputNotFoundError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidAssetLockProofCoreChainHeightError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidAssetLockProofCoreChainHeightError.js new file mode 100644 index 00000000000..4a5368d6f28 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidAssetLockProofCoreChainHeightError.js @@ -0,0 +1,35 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidAssetLockProofCoreChainHeightError extends AbstractBasicError { + /** + * @param {number} proofCoreChainLockedHeight + * @param {number} currentCoreChainLockedHeight + */ + constructor(proofCoreChainLockedHeight, currentCoreChainLockedHeight) { + super(`Asset Lock proof core chain height ${proofCoreChainLockedHeight} is higher than the current consensus core height ${currentCoreChainLockedHeight}.`); + + this.proofCoreChainLockedHeight = proofCoreChainLockedHeight; + this.currentCoreChainLockedHeight = currentCoreChainLockedHeight; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * + * @returns {number} + */ + getProofCoreChainLockedHeight() { + return this.proofCoreChainLockedHeight; + } + + /** + * + * @returns {number} + */ + getCurrentCoreChainLockedHeight() { + return this.currentCoreChainLockedHeight; + } +} + +module.exports = InvalidAssetLockProofCoreChainHeightError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidAssetLockProofTransactionHeightError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidAssetLockProofTransactionHeightError.js new file mode 100644 index 00000000000..297b6d685c4 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidAssetLockProofTransactionHeightError.js @@ -0,0 +1,35 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidAssetLockProofTransactionHeightError extends AbstractBasicError { + /** + * @param {number} proofCoreChainLockedHeight + * @param {number} transactionHeight + */ + constructor(proofCoreChainLockedHeight, transactionHeight) { + super(`Core chain locked height ${proofCoreChainLockedHeight} must be higher than block ${transactionHeight || ''} with Asset Lock transaction`); + + this.proofCoreChainLockedHeight = proofCoreChainLockedHeight; + this.transactionHeight = transactionHeight; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * + * @returns {number} + */ + getProofCoreChainLockedHeight() { + return this.proofCoreChainLockedHeight; + } + + /** + * + * @returns {number} + */ + getTransactionHeight() { + return this.transactionHeight; + } +} + +module.exports = InvalidAssetLockProofTransactionHeightError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidAssetLockTransactionOutputReturnSizeError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidAssetLockTransactionOutputReturnSizeError.js new file mode 100644 index 00000000000..546141cd790 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidAssetLockTransactionOutputReturnSizeError.js @@ -0,0 +1,26 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidAssetLockTransactionOutputReturnSizeError extends AbstractBasicError { + /** + * @param {number} outputIndex + */ + constructor(outputIndex) { + super(`Asset Lock output ${outputIndex} has invalid public key hash. Must be 20 length bytes hash.`); + + this.outputIndex = outputIndex; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get asset lock transaction output index + * + * @return {number} + */ + getOutputIndex() { + return this.outputIndex; + } +} + +module.exports = InvalidAssetLockTransactionOutputReturnSizeError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionError.js new file mode 100644 index 00000000000..236dcbc6ce7 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionError.js @@ -0,0 +1,29 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidIdentityAssetLockTransactionError extends AbstractBasicError { + /** + * @param {string} message + */ + constructor(message) { + super(`Invalid asset lock transaction: ${message}`); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @param {Error} error + */ + setValidationError(error) { + this.validationError = error; + } + + /** + * @returns {Error} + */ + getValidationError() { + return this.validationError; + } +} + +module.exports = InvalidIdentityAssetLockTransactionError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionOutputError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionOutputError.js new file mode 100644 index 00000000000..88166b8d3cd --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionOutputError.js @@ -0,0 +1,26 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidIdentityAssetLockTransactionOutputError extends AbstractBasicError { + /** + * @param {number} outputIndex + */ + constructor(outputIndex) { + super(`Asset lock output ${outputIndex} is not a valid standard OP_RETURN output`); + + this.outputIndex = outputIndex; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get lock transaction output + * + * @return {number} + */ + getOutputIndex() { + return this.outputIndex; + } +} + +module.exports = InvalidIdentityAssetLockTransactionOutputError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityKeySignatureError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityKeySignatureError.js new file mode 100644 index 00000000000..0483b698ebb --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityKeySignatureError.js @@ -0,0 +1,26 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidIdentityKeySignatureError extends AbstractBasicError { + /** + * @param {number} publicKeyId + */ + constructor(publicKeyId) { + super(`Identity key ${publicKeyId} has invalid signature`); + + this.publicKeyId = publicKeyId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get id of public key with signature + * + * @return {number} + */ + getPublicKeyId() { + return this.publicKeyId; + } +} + +module.exports = InvalidIdentityKeySignatureError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityPublicKeyDataError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityPublicKeyDataError.js new file mode 100644 index 00000000000..bd558443a4d --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityPublicKeyDataError.js @@ -0,0 +1,41 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidIdentityPublicKeyDataError extends AbstractBasicError { + /** + * @param {number} publicKeyId + * @param {string} message + */ + constructor(publicKeyId, message) { + super(`Invalid identity public key ${publicKeyId} data: ${message}`); + + this.publicKeyId = publicKeyId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get identity public key ID + * + * @return {number} + */ + getPublicKeyId() { + return this.publicKeyId; + } + + /** + * @param {Error} error + */ + setValidationError(error) { + this.validationError = error; + } + + /** + * @return {Error} + */ + getValidationError() { + return this.validationError; + } +} + +module.exports = InvalidIdentityPublicKeyDataError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityPublicKeySecurityLevelError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityPublicKeySecurityLevelError.js new file mode 100644 index 00000000000..6dd346562c2 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidIdentityPublicKeySecurityLevelError.js @@ -0,0 +1,63 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidIdentityPublicKeySecurityLevelError extends AbstractBasicError { + /** + * @param {number} publicKeyId + * @param {number} purpose + * @param {number} securityLevel + * @param {number[]} allowedSecurityLevels + */ + constructor(publicKeyId, purpose, securityLevel, allowedSecurityLevels) { + super(`Invalid identity public key ${publicKeyId} security level: purpose ${purpose} allows only for ${allowedSecurityLevels.join(',')} security levels, but got ${securityLevel}`); + + this.publicKeyId = publicKeyId; + this.purpose = purpose; + this.securityLevel = securityLevel; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get identity public key ID + * + * @return {number} + */ + getPublicKeyId() { + return this.publicKeyId; + } + + /** + * Get identity public key purpose + * + * @return {number} + */ + getPublicKeyPurpose() { + return this.purpose; + } + + /** + * Get identity public key security level + * + * @return {number} + */ + getPublicKeySecurityLevel() { + return this.securityLevel; + } + + /** + * @param {Error} error + */ + setValidationError(error) { + this.validationError = error; + } + + /** + * @return {Error} + */ + getValidationError() { + return this.validationError; + } +} + +module.exports = InvalidIdentityPublicKeySecurityLevelError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidInstantAssetLockProofError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidInstantAssetLockProofError.js new file mode 100644 index 00000000000..9c5bced0998 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidInstantAssetLockProofError.js @@ -0,0 +1,29 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidInstantAssetLockProofError extends AbstractBasicError { + /** + * @param {string} message + */ + constructor(message) { + super(`Invalid instant lock proof: ${message}`); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @param {Error} error + */ + setValidationError(error) { + this.validationError = error; + } + + /** + * @return {Error} + */ + getValidationError() { + return this.validationError; + } +} + +module.exports = InvalidInstantAssetLockProofError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidInstantAssetLockProofSignatureError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidInstantAssetLockProofSignatureError.js new file mode 100644 index 00000000000..cb11509dc36 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/InvalidInstantAssetLockProofSignatureError.js @@ -0,0 +1,9 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidInstantAssetLockProofSignatureError extends AbstractBasicError { + constructor() { + super('Invalid instant lock proof signature'); + } +} + +module.exports = InvalidInstantAssetLockProofSignatureError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/identity/MissingMasterPublicKeyError.js b/packages/js-dpp/lib/errors/consensus/basic/identity/MissingMasterPublicKeyError.js new file mode 100644 index 00000000000..62cddb2acc6 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/identity/MissingMasterPublicKeyError.js @@ -0,0 +1,12 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class MissingMasterPublicKeyError extends AbstractBasicError { + constructor() { + super('Identity doesn\'t contain any master key, thus can not be updated. Please add a master key'); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } +} + +module.exports = MissingMasterPublicKeyError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/stateTransition/InvalidStateTransitionTypeError.js b/packages/js-dpp/lib/errors/consensus/basic/stateTransition/InvalidStateTransitionTypeError.js new file mode 100644 index 00000000000..020e32ac96d --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/stateTransition/InvalidStateTransitionTypeError.js @@ -0,0 +1,26 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class InvalidStateTransitionTypeError extends AbstractBasicError { + /** + * @param {number} type + */ + constructor(type) { + super(`Invalid State Transition type ${type}`); + + this.type = type; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get state transition type + * + * @return {number} + */ + getType() { + return this.type; + } +} + +module.exports = InvalidStateTransitionTypeError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/stateTransition/MissingStateTransitionTypeError.js b/packages/js-dpp/lib/errors/consensus/basic/stateTransition/MissingStateTransitionTypeError.js new file mode 100644 index 00000000000..e6d252478ff --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/stateTransition/MissingStateTransitionTypeError.js @@ -0,0 +1,9 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class MissingStateTransitionTypeError extends AbstractBasicError { + constructor() { + super('State Transition type is not present'); + } +} + +module.exports = MissingStateTransitionTypeError; diff --git a/packages/js-dpp/lib/errors/consensus/basic/stateTransition/StateTransitionMaxSizeExceededError.js b/packages/js-dpp/lib/errors/consensus/basic/stateTransition/StateTransitionMaxSizeExceededError.js new file mode 100644 index 00000000000..3ba0c9f4f14 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/basic/stateTransition/StateTransitionMaxSizeExceededError.js @@ -0,0 +1,37 @@ +const AbstractBasicError = require('../AbstractBasicError'); + +class StateTransitionMaxSizeExceededError extends AbstractBasicError { + /** + * @param {number} actualSizeKBytes + * @param {number} maxSizeKBytes + */ + constructor(actualSizeKBytes, maxSizeKBytes) { + super(`State transition size ${actualSizeKBytes} Kb is more than maximum ${maxSizeKBytes} Kb`); + + this.actualSizeKBytes = actualSizeKBytes; + this.maxSizeKBytes = maxSizeKBytes; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get actual state transition size in Kb + * + * @return {number} + */ + getActualSizeKBytes() { + return this.actualSizeKBytes; + } + + /** + * Get max state transition size in Kb + * + * @return {number} + */ + getMaxSizeKBytes() { + return this.maxSizeKBytes; + } +} + +module.exports = StateTransitionMaxSizeExceededError; diff --git a/packages/js-dpp/lib/errors/consensus/codes.js b/packages/js-dpp/lib/errors/consensus/codes.js new file mode 100644 index 00000000000..822dde62a2c --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/codes.js @@ -0,0 +1,208 @@ +const ProtocolVersionParsingError = require('./basic/decode/ProtocolVersionParsingError'); +const UnsupportedProtocolVersionError = require('./basic/UnsupportedProtocolVersionError'); +const IncompatibleProtocolVersionError = require('./basic/IncompatibleProtocolVersionError'); +const SerializedObjectParsingError = require('./basic/decode/SerializedObjectParsingError'); +const JsonSchemaError = require('./basic/JsonSchemaError'); +const InvalidIdentifierError = require('./basic/InvalidIdentifierError'); +const DataContractMaxDepthExceedError = require('./basic/dataContract/DataContractMaxDepthExceedError'); +const DuplicateIndexError = require('./basic/dataContract/DuplicateIndexError'); +const InvalidCompoundIndexError = require('./basic/dataContract/InvalidCompoundIndexError'); +const InvalidDataContractIdError = require('./basic/dataContract/InvalidDataContractIdError'); +const InvalidIndexedPropertyConstraintError = require('./basic/dataContract/InvalidIndexedPropertyConstraintError'); +const InvalidIndexPropertyTypeError = require('./basic/dataContract/InvalidIndexPropertyTypeError'); +const SystemPropertyIndexAlreadyPresentError = require('./basic/dataContract/SystemPropertyIndexAlreadyPresentError'); +const UndefinedIndexPropertyError = require('./basic/dataContract/UndefinedIndexPropertyError'); +const UniqueIndicesLimitReachedError = require('./basic/dataContract/UniqueIndicesLimitReachedError'); +const InconsistentCompoundIndexDataError = require('./basic/document/InconsistentCompoundIndexDataError'); +const InvalidDocumentTransitionActionError = require('./basic/document/InvalidDocumentTransitionActionError'); +const InvalidDocumentTransitionIdError = require('./basic/document/InvalidDocumentTransitionIdError'); +const DataContractNotPresentError = require('./basic/document/DataContractNotPresentError'); +const InvalidDocumentTypeError = require('./basic/document/InvalidDocumentTypeError'); +const MissingDataContractIdError = require('./basic/document/MissingDataContractIdError'); +const MissingDocumentTransitionActionError = require('./basic/document/MissingDocumentTransitionActionError'); +const MissingDocumentTransitionTypeError = require('./basic/document/MissingDocumentTransitionTypeError'); +const MissingDocumentTypeError = require('./basic/document/MissingDocumentTypeError'); +const DuplicatedIdentityPublicKeyError = require('./basic/identity/DuplicatedIdentityPublicKeyError'); +const DuplicatedIdentityPublicKeyIdError = require('./basic/identity/DuplicatedIdentityPublicKeyIdError'); +const MissingMasterPublicKeyError = require('./basic/identity/MissingMasterPublicKeyError'); +const IdentityAssetLockProofLockedTransactionMismatchError = require('./basic/identity/IdentityAssetLockProofLockedTransactionMismatchError'); +const IdentityAssetLockTransactionIsNotFoundError = require('./basic/identity/IdentityAssetLockTransactionIsNotFoundError'); +const IdentityAssetLockTransactionOutPointAlreadyExistsError = require('./basic/identity/IdentityAssetLockTransactionOutPointAlreadyExistsError'); +const IdentityAssetLockTransactionOutputNotFoundError = require('./basic/identity/IdentityAssetLockTransactionOutputNotFoundError'); +const InvalidAssetLockProofCoreChainHeightError = require('./basic/identity/InvalidAssetLockProofCoreChainHeightError'); +const InvalidAssetLockProofTransactionHeightError = require('./basic/identity/InvalidAssetLockProofTransactionHeightError'); +const InvalidIdentityAssetLockTransactionError = require('./basic/identity/InvalidIdentityAssetLockTransactionError'); +const InvalidIdentityAssetLockTransactionOutputError = require('./basic/identity/InvalidIdentityAssetLockTransactionOutputError'); +const InvalidIdentityPublicKeyDataError = require('./basic/identity/InvalidIdentityPublicKeyDataError'); +const InvalidIdentityPublicKeySecurityLevelError = require('./basic/identity/InvalidIdentityPublicKeySecurityLevelError'); +const InvalidStateTransitionTypeError = require('./basic/stateTransition/InvalidStateTransitionTypeError'); +const MissingStateTransitionTypeError = require('./basic/stateTransition/MissingStateTransitionTypeError'); +const StateTransitionMaxSizeExceededError = require('./basic/stateTransition/StateTransitionMaxSizeExceededError'); +const IdentityNotFoundError = require('./signature/IdentityNotFoundError'); +const InvalidIdentityPublicKeyTypeError = require('./signature/InvalidIdentityPublicKeyTypeError'); +const InvalidStateTransitionSignatureError = require('./signature/InvalidStateTransitionSignatureError'); +const MissingPublicKeyError = require('./signature/MissingPublicKeyError'); +const BalanceIsNotEnoughError = require('./fee/BalanceIsNotEnoughError'); +const DataContractAlreadyPresentError = require('./state/dataContract/DataContractAlreadyPresentError'); +const DataTriggerConditionError = require('./state/dataContract/dataTrigger/DataTriggerConditionError'); +const DataTriggerExecutionError = require('./state/dataContract/dataTrigger/DataTriggerExecutionError'); +const DataTriggerInvalidResultError = require('./state/dataContract/dataTrigger/DataTriggerInvalidResultError'); +const DocumentAlreadyPresentError = require('./state/document/DocumentAlreadyPresentError'); +const DocumentNotFoundError = require('./state/document/DocumentNotFoundError'); +const DocumentOwnerIdMismatchError = require('./state/document/DocumentOwnerIdMismatchError'); +const DocumentTimestampsMismatchError = require('./state/document/DocumentTimestampsMismatchError'); +const DocumentTimestampWindowViolationError = require('./state/document/DocumentTimestampWindowViolationError'); +const DuplicateUniqueIndexError = require('./state/document/DuplicateUniqueIndexError'); +const InvalidDocumentRevisionError = require('./state/document/InvalidDocumentRevisionError'); +const IdentityAlreadyExistsError = require('./state/identity/IdentityAlreadyExistsError'); +const InvalidJsonSchemaRefError = require('./basic/dataContract/InvalidJsonSchemaRefError'); +const JsonSchemaCompilationError = require('./basic/JsonSchemaCompilationError'); +const DuplicateDocumentTransitionsWithIdsError = require('./basic/document/DuplicateDocumentTransitionsWithIdsError'); +const DuplicateDocumentTransitionsWithIndicesError = require('./basic/document/DuplicateDocumentTransitionsWithIndicesError'); +const InvalidAssetLockTransactionOutputReturnSizeError = require('./basic/identity/InvalidAssetLockTransactionOutputReturnSizeError'); +const InvalidInstantAssetLockProofError = require('./basic/identity/InvalidInstantAssetLockProofError'); +const InvalidInstantAssetLockProofSignatureError = require('./basic/identity/InvalidInstantAssetLockProofSignatureError'); +const IncompatibleRe2PatternError = require('./basic/dataContract/IncompatibleRe2PatternError'); +const InvalidDataContractVersionError = require('./basic/dataContract/InvalidDataContractVersionError'); +const IncompatibleDataContractSchemaError = require('./basic/dataContract/IncompatibleDataContractSchemaError'); +const DataContractImmutablePropertiesUpdateError = require('./basic/dataContract/DataContractImmutablePropertiesUpdateError'); +const DataContractIndicesChangedError = require('./basic/dataContract/DataContractUniqueIndicesChangedError'); +const DuplicateIndexNameError = require('./basic/dataContract/DuplicateIndexNameError'); +const DataContractInvalidIndexDefinitionUpdateError = require('./basic/dataContract/DataContractInvalidIndexDefinitionUpdateError'); +const DataContractHaveNewUniqueIndexError = require('./basic/dataContract/DataContractHaveNewUniqueIndexError'); +const IdentityPublicKeyDisabledAtWindowViolationError = require('./state/identity/IdentityPublicKeyDisabledAtWindowViolationError'); +const IdentityPublicKeyIsReadOnlyError = require('./state/identity/IdentityPublicKeyIsReadOnlyError'); +const InvalidIdentityPublicKeyIdError = require('./state/identity/InvalidIdentityPublicKeyIdError'); +const InvalidIdentityRevisionError = require('./state/identity/InvalidIdentityRevisionError'); +const StateMaxIdentityPublicKeyLimitReachedError = require('./state/identity/MaxIdentityPublicKeyLimitReachedError'); +const DuplicatedIdentityPublicKeyStateError = require('./state/identity/DuplicatedIdentityPublicKeyError'); +const DuplicatedIdentityPublicKeyIdStateError = require('./state/identity/DuplicatedIdentityPublicKeyIdError'); +const InvalidIdentityKeySignatureError = require('./basic/identity/InvalidIdentityKeySignatureError'); +const InvalidSignaturePublicKeySecurityLevelError = require('./signature/InvalidSignaturePublicKeySecurityLevelError'); +const PublicKeyIsDisabledError = require('./signature/PublicKeyIsDisabledError'); +const PublicKeySecurityLevelNotMetError = require('./signature/PublicKeySecurityLevelNotMetError'); +const WrongPublicKeyPurposeError = require('./signature/WrongPublicKeyPurposeError'); + +const codes = { + /** + * Basic + */ + + // Decoding + 1000: ProtocolVersionParsingError, + 1001: SerializedObjectParsingError, + + // General + 1002: UnsupportedProtocolVersionError, + 1003: IncompatibleProtocolVersionError, + 1004: JsonSchemaCompilationError, + 1005: JsonSchemaError, + 1006: InvalidIdentifierError, + + // Data Contract + 1007: DataContractMaxDepthExceedError, + 1008: DuplicateIndexError, + 1009: IncompatibleRe2PatternError, + 1010: InvalidCompoundIndexError, + 1011: InvalidDataContractIdError, + 1012: InvalidIndexedPropertyConstraintError, + 1013: InvalidIndexPropertyTypeError, + 1014: InvalidJsonSchemaRefError, + 1015: SystemPropertyIndexAlreadyPresentError, + 1016: UndefinedIndexPropertyError, + 1017: UniqueIndicesLimitReachedError, + 1048: DuplicateIndexNameError, + 1050: InvalidDataContractVersionError, + 1051: IncompatibleDataContractSchemaError, + 1052: DataContractImmutablePropertiesUpdateError, + 1053: DataContractIndicesChangedError, + 1054: DataContractInvalidIndexDefinitionUpdateError, + 1055: DataContractHaveNewUniqueIndexError, + + // Document + 1018: DataContractNotPresentError, + 1019: DuplicateDocumentTransitionsWithIdsError, + 1020: DuplicateDocumentTransitionsWithIndicesError, + 1021: InconsistentCompoundIndexDataError, + 1022: InvalidDocumentTransitionActionError, + 1023: InvalidDocumentTransitionIdError, + 1024: InvalidDocumentTypeError, + 1025: MissingDataContractIdError, + 1026: MissingDocumentTransitionActionError, + 1027: MissingDocumentTransitionTypeError, + 1028: MissingDocumentTypeError, + + // Identity + 1029: DuplicatedIdentityPublicKeyError, + 1030: DuplicatedIdentityPublicKeyIdError, + 1031: IdentityAssetLockProofLockedTransactionMismatchError, + 1032: IdentityAssetLockTransactionIsNotFoundError, + 1033: IdentityAssetLockTransactionOutPointAlreadyExistsError, + 1034: IdentityAssetLockTransactionOutputNotFoundError, + 1035: InvalidAssetLockProofCoreChainHeightError, + 1036: InvalidAssetLockProofTransactionHeightError, + 1037: InvalidAssetLockTransactionOutputReturnSizeError, + 1038: InvalidIdentityAssetLockTransactionError, + 1039: InvalidIdentityAssetLockTransactionOutputError, + 1040: InvalidIdentityPublicKeyDataError, + 1041: InvalidInstantAssetLockProofError, + 1042: InvalidInstantAssetLockProofSignatureError, + 1046: MissingMasterPublicKeyError, + 1047: InvalidIdentityPublicKeySecurityLevelError, + 1056: InvalidIdentityKeySignatureError, + + // State Transition + 1043: InvalidStateTransitionTypeError, + 1044: MissingStateTransitionTypeError, + 1045: StateTransitionMaxSizeExceededError, + + /** + * Signature + */ + + 2000: IdentityNotFoundError, + 2001: InvalidIdentityPublicKeyTypeError, + 2002: InvalidStateTransitionSignatureError, + 2003: MissingPublicKeyError, + 2004: InvalidSignaturePublicKeySecurityLevelError, + 2005: WrongPublicKeyPurposeError, + 2006: PublicKeyIsDisabledError, + 2007: PublicKeySecurityLevelNotMetError, + + /** + * Fee + */ + + 3000: BalanceIsNotEnoughError, + + /** + * State + */ + + // Data Contract + 4000: DataContractAlreadyPresentError, + 4001: DataTriggerConditionError, + 4002: DataTriggerExecutionError, + 4003: DataTriggerInvalidResultError, + + // Document + 4004: DocumentAlreadyPresentError, + 4005: DocumentNotFoundError, + 4006: DocumentOwnerIdMismatchError, + 4007: DocumentTimestampsMismatchError, + 4008: DocumentTimestampWindowViolationError, + 4009: DuplicateUniqueIndexError, + 4010: InvalidDocumentRevisionError, + + // Identity + 4011: IdentityAlreadyExistsError, + 4012: IdentityPublicKeyDisabledAtWindowViolationError, + 4017: IdentityPublicKeyIsReadOnlyError, + 4018: InvalidIdentityPublicKeyIdError, + 4019: InvalidIdentityRevisionError, + 4020: StateMaxIdentityPublicKeyLimitReachedError, + 4021: DuplicatedIdentityPublicKeyStateError, + 4022: DuplicatedIdentityPublicKeyIdStateError, +}; + +module.exports = codes; diff --git a/packages/js-dpp/lib/errors/consensus/createConsensusError.js b/packages/js-dpp/lib/errors/consensus/createConsensusError.js new file mode 100644 index 00000000000..17e5f1699f7 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/createConsensusError.js @@ -0,0 +1,17 @@ +const codes = require('./codes'); + +/** + * + * @param {number} code + * @param {*[]} args + * @returns {*} + */ +function createConsensusError(code, args) { + if (!codes[code]) { + throw new Error(`Consensus error with code ${code} is not defined. Probably you need to update DPP`); + } + + return new codes[code](...args); +} + +module.exports = createConsensusError; diff --git a/packages/js-dpp/lib/errors/consensus/fee/AbstractFeeError.js b/packages/js-dpp/lib/errors/consensus/fee/AbstractFeeError.js new file mode 100644 index 00000000000..bf74fd1ed6c --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/fee/AbstractFeeError.js @@ -0,0 +1,10 @@ +const AbstractConsensusError = require('../AbstractConsensusError'); + +/** + * @abstract + */ +class AbstractFeeError extends AbstractConsensusError { + +} + +module.exports = AbstractFeeError; diff --git a/packages/js-dpp/lib/errors/consensus/fee/BalanceIsNotEnoughError.js b/packages/js-dpp/lib/errors/consensus/fee/BalanceIsNotEnoughError.js new file mode 100644 index 00000000000..817aab07233 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/fee/BalanceIsNotEnoughError.js @@ -0,0 +1,35 @@ +const AbstractFeeError = require('./AbstractFeeError'); + +class BalanceIsNotEnoughError extends AbstractFeeError { + /** + * @param {number} balance + * @param {number} fee + */ + constructor(balance, fee) { + super(`Current credits balance ${balance} is not enough to pay ${fee} fee`); + + this.balance = balance; + this.fee = fee; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @return {number} + */ + getFee() { + return this.fee; + } + + /** + * Get current balance + * + * @return {number} + */ + getBalance() { + return this.balance; + } +} + +module.exports = BalanceIsNotEnoughError; diff --git a/packages/js-dpp/lib/errors/consensus/signature/AbstractSignatureError.js b/packages/js-dpp/lib/errors/consensus/signature/AbstractSignatureError.js new file mode 100644 index 00000000000..0e179a2752d --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/signature/AbstractSignatureError.js @@ -0,0 +1,10 @@ +const AbstractConsensusError = require('../AbstractConsensusError'); + +/** + * @abstract + */ +class AbstractSignatureError extends AbstractConsensusError { + +} + +module.exports = AbstractSignatureError; diff --git a/packages/js-dpp/lib/errors/consensus/signature/IdentityNotFoundError.js b/packages/js-dpp/lib/errors/consensus/signature/IdentityNotFoundError.js new file mode 100644 index 00000000000..e7b5d7dae1e --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/signature/IdentityNotFoundError.js @@ -0,0 +1,27 @@ +const AbstractSignatureError = require('./AbstractSignatureError'); +const Identifier = require('../../../identifier/Identifier'); + +class IdentityNotFoundError extends AbstractSignatureError { + /** + * @param {Buffer} identityId + */ + constructor(identityId) { + super(`Identity ${Identifier.from(identityId)} not found`); + + this.identityId = identityId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get identity id + * + * @return {Buffer} + */ + getIdentityId() { + return this.identityId; + } +} + +module.exports = IdentityNotFoundError; diff --git a/packages/js-dpp/lib/errors/consensus/signature/InvalidIdentityPublicKeyTypeError.js b/packages/js-dpp/lib/errors/consensus/signature/InvalidIdentityPublicKeyTypeError.js new file mode 100644 index 00000000000..cbe7a1b31f1 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/signature/InvalidIdentityPublicKeyTypeError.js @@ -0,0 +1,22 @@ +const AbstractSignatureError = require('./AbstractSignatureError'); + +class InvalidIdentityPublicKeyTypeError extends AbstractSignatureError { + /** + * + * @param {number} publicKeyType + */ + constructor(publicKeyType) { + super(`Unsupported signature type ${publicKeyType}. Please use type ECDSA (0), BLS (1) or ECDSA HASH160 (2) keys to sign the state transition`); + + this.publicKeyType = publicKeyType; + } + + /** + * @returns {number} + */ + getPublicKeyType() { + return this.publicKeyType; + } +} + +module.exports = InvalidIdentityPublicKeyTypeError; diff --git a/packages/js-dpp/lib/errors/consensus/signature/InvalidSignaturePublicKeySecurityLevelError.js b/packages/js-dpp/lib/errors/consensus/signature/InvalidSignaturePublicKeySecurityLevelError.js new file mode 100644 index 00000000000..6c51d6cf85b --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/signature/InvalidSignaturePublicKeySecurityLevelError.js @@ -0,0 +1,35 @@ +const AbstractSignatureError = require('./AbstractSignatureError'); + +class InvalidSignaturePublicKeySecurityLevelError extends AbstractSignatureError { + /** + * + * @param {number} publicKeySecurityLevel + * @param {number} requiredSecurityLevel + */ + constructor(publicKeySecurityLevel, requiredSecurityLevel) { + super(`Invalid public key security level ${publicKeySecurityLevel}. This state transition requires ${requiredSecurityLevel}.`); + + this.publicKeySecurityLevel = publicKeySecurityLevel; + this.requiredSecurityLevel = requiredSecurityLevel; + } + + /** + * Get mismatched public key + * + * @return {number} + */ + getPublicKeySecurityLevel() { + return this.publicKeySecurityLevel; + } + + /** + * Get required key security level + * + * @returns {number} + */ + getKeySecurityLevelRequirement() { + return this.requiredSecurityLevel; + } +} + +module.exports = InvalidSignaturePublicKeySecurityLevelError; diff --git a/packages/js-dpp/lib/errors/consensus/signature/InvalidStateTransitionSignatureError.js b/packages/js-dpp/lib/errors/consensus/signature/InvalidStateTransitionSignatureError.js new file mode 100644 index 00000000000..daf1c8d9fb2 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/signature/InvalidStateTransitionSignatureError.js @@ -0,0 +1,9 @@ +const AbstractSignatureError = require('./AbstractSignatureError'); + +class InvalidStateTransitionSignatureError extends AbstractSignatureError { + constructor() { + super('Invalid State Transition signature'); + } +} + +module.exports = InvalidStateTransitionSignatureError; diff --git a/packages/js-dpp/lib/errors/consensus/signature/MissingPublicKeyError.js b/packages/js-dpp/lib/errors/consensus/signature/MissingPublicKeyError.js new file mode 100644 index 00000000000..d2adf105203 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/signature/MissingPublicKeyError.js @@ -0,0 +1,26 @@ +const AbstractSignatureError = require('./AbstractSignatureError'); + +class MissingPublicKeyError extends AbstractSignatureError { + /** + * @param {number} publicKeyId + */ + constructor(publicKeyId) { + super(`Public key ${publicKeyId} doesn't exist`); + + this.publicKeyId = publicKeyId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get public key id + * + * @return {number} + */ + getPublicKeyId() { + return this.publicKeyId; + } +} + +module.exports = MissingPublicKeyError; diff --git a/packages/js-dpp/lib/errors/consensus/signature/PublicKeyIsDisabledError.js b/packages/js-dpp/lib/errors/consensus/signature/PublicKeyIsDisabledError.js new file mode 100644 index 00000000000..2fec3a350ba --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/signature/PublicKeyIsDisabledError.js @@ -0,0 +1,24 @@ +const AbstractSignatureError = require('./AbstractSignatureError'); + +class PublicKeyIsDisabledError extends AbstractSignatureError { + /** + * + * @param {number} publicKeyId + */ + constructor(publicKeyId) { + super(`Identity key ${publicKeyId} is disabled`); + + this.publicKeyId = publicKeyId; + } + + /** + * Get disabled public key ID + * + * @return {number} + */ + getPublicKeyId() { + return this.publicKeyId; + } +} + +module.exports = PublicKeyIsDisabledError; diff --git a/packages/js-dpp/lib/errors/consensus/signature/PublicKeySecurityLevelNotMetError.js b/packages/js-dpp/lib/errors/consensus/signature/PublicKeySecurityLevelNotMetError.js new file mode 100644 index 00000000000..49f8affefec --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/signature/PublicKeySecurityLevelNotMetError.js @@ -0,0 +1,35 @@ +const AbstractSignatureError = require('./AbstractSignatureError'); + +class PublicKeySecurityLevelNotMetError extends AbstractSignatureError { + /** + * + * @param {number} publicKeySecurityLevel + * @param {number} requiredSecurityLevel + */ + constructor(publicKeySecurityLevel, requiredSecurityLevel) { + super(`Invalid key security level ${publicKeySecurityLevel}. This state transition requires at least ${requiredSecurityLevel}`); + + this.publicKeySecurityLevel = publicKeySecurityLevel; + this.requiredSecurityLevel = requiredSecurityLevel; + } + + /** + * Get mismatched public key + * + * @return {number} + */ + getPublicKeySecurityLevel() { + return this.publicKeySecurityLevel; + } + + /** + * Get minimal required key security level + * + * @returns {number} + */ + getKeySecurityLevelRequirement() { + return this.requiredSecurityLevel; + } +} + +module.exports = PublicKeySecurityLevelNotMetError; diff --git a/packages/js-dpp/lib/errors/consensus/signature/WrongPublicKeyPurposeError.js b/packages/js-dpp/lib/errors/consensus/signature/WrongPublicKeyPurposeError.js new file mode 100644 index 00000000000..a8902f19ccb --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/signature/WrongPublicKeyPurposeError.js @@ -0,0 +1,35 @@ +const AbstractSignatureError = require('./AbstractSignatureError'); + +class WrongPublicKeyPurposeError extends AbstractSignatureError { + /** + * + * @param {number} publicKeyPurpose + * @param {number} keyPurposeRequirement + */ + constructor(publicKeyPurpose, keyPurposeRequirement) { + super(`Invalid identity key purpose ${publicKeyPurpose}. This state transition requires ${keyPurposeRequirement}`); + + this.publicKeyPurpose = publicKeyPurpose; + this.keyPurposeRequirement = keyPurposeRequirement; + } + + /** + * Get mismatched public key + * + * @return {number} + */ + getPublicKeyPurpose() { + return this.publicKeyPurpose; + } + + /** + * Get required key purpose + * + * @returns {number} + */ + getKeyPurposeRequirement() { + return this.keyPurposeRequirement; + } +} + +module.exports = WrongPublicKeyPurposeError; diff --git a/packages/js-dpp/lib/errors/consensus/state/AbstractStateError.js b/packages/js-dpp/lib/errors/consensus/state/AbstractStateError.js new file mode 100644 index 00000000000..31685bc8948 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/AbstractStateError.js @@ -0,0 +1,7 @@ +const AbstractConsensusError = require('../AbstractConsensusError'); + +class AbstractStateError extends AbstractConsensusError { + +} + +module.exports = AbstractStateError; diff --git a/packages/js-dpp/lib/errors/consensus/state/dataContract/DataContractAlreadyPresentError.js b/packages/js-dpp/lib/errors/consensus/state/dataContract/DataContractAlreadyPresentError.js new file mode 100644 index 00000000000..625f529da92 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/dataContract/DataContractAlreadyPresentError.js @@ -0,0 +1,27 @@ +const AbstractStateError = require('../AbstractStateError'); +const Identifier = require('../../../../identifier/Identifier'); + +class DataContractAlreadyPresentError extends AbstractStateError { + /** + * @param {Buffer} dataContractId + */ + constructor(dataContractId) { + super(`Data Contract ${Identifier.from(dataContractId)} is already present`); + + this.dataContractId = dataContractId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get Data Contract ID + * + * @return {Buffer} + */ + getDataContractId() { + return this.dataContractId; + } +} + +module.exports = DataContractAlreadyPresentError; diff --git a/packages/js-dpp/lib/errors/consensus/state/dataContract/dataTrigger/AbstractDataTriggerError.js b/packages/js-dpp/lib/errors/consensus/state/dataContract/dataTrigger/AbstractDataTriggerError.js new file mode 100644 index 00000000000..b5e77aca94d --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/dataContract/dataTrigger/AbstractDataTriggerError.js @@ -0,0 +1,70 @@ +const AbstractStateError = require('../../AbstractStateError'); + +/** + * @abstract + */ +class AbstractDataTriggerError extends AbstractStateError { + /** + * @param {Buffer} dataContractId + * @param {Buffer} documentTransitionId + * @param {string} message + */ + constructor(dataContractId, documentTransitionId, message) { + super(message); + + this.dataContractId = dataContractId; + this.documentTransitionId = documentTransitionId; + } + + /** + * @returns {Buffer} + */ + getDocumentTransitionId() { + return this.documentTransitionId; + } + + /** + * @returns {Buffer} + */ + getDataContractId() { + return this.dataContractId; + } + + /** + * @param {Identifier} ownerId + */ + setOwnerId(ownerId) { + this.ownerId = ownerId; + } + + /** + * Get data trigger owner id + * + * @return {Identifier} + */ + getOwnerId() { + return this.ownerId; + } + + /** + * @param { + * DocumentCreateTransition|DocumentReplaceTransition|DocumentDeleteTransition + * } documentTransition + */ + setDocumentTransition(documentTransition) { + this.documentTransition = documentTransition; + } + + /** + * Get document transition + * + * @returns { + * DocumentCreateTransition|DocumentReplaceTransition|DocumentDeleteTransition + * } + */ + getDocumentTransition() { + return this.documentTransition; + } +} + +module.exports = AbstractDataTriggerError; diff --git a/packages/js-dpp/lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerConditionError.js b/packages/js-dpp/lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerConditionError.js new file mode 100644 index 00000000000..d1473ef9dcf --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerConditionError.js @@ -0,0 +1,17 @@ +const AbstractDataTriggerError = require('./AbstractDataTriggerError'); + +class DataTriggerConditionError extends AbstractDataTriggerError { + /** + * @param {Buffer} dataContractId + * @param {Buffer} documentTransitionId + * @param {string} message + */ + constructor(dataContractId, documentTransitionId, message) { + super(dataContractId, documentTransitionId, message); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } +} + +module.exports = DataTriggerConditionError; diff --git a/packages/js-dpp/lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerExecutionError.js b/packages/js-dpp/lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerExecutionError.js new file mode 100644 index 00000000000..1704bf965ed --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerExecutionError.js @@ -0,0 +1,35 @@ +const AbstractDataTriggerError = require('./AbstractDataTriggerError'); + +class DataTriggerExecutionError extends AbstractDataTriggerError { + /** + * @param {Buffer} dataContractId + * @param {Buffer} documentTransitionId + * @param {string} message + */ + constructor(dataContractId, documentTransitionId, message) { + super(dataContractId, documentTransitionId, message); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Set internal error + * + * @param {Error} error + */ + setExecutionError(error) { + this.executionError = error; + } + + /** + * Return internal error + * + * @return {Error} + */ + getExecutionError() { + return this.executionError; + } +} + +module.exports = DataTriggerExecutionError; diff --git a/packages/js-dpp/lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerInvalidResultError.js b/packages/js-dpp/lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerInvalidResultError.js new file mode 100644 index 00000000000..35bb9a37098 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerInvalidResultError.js @@ -0,0 +1,16 @@ +const AbstractDataTriggerError = require('./AbstractDataTriggerError'); + +class DataTriggerInvalidResultError extends AbstractDataTriggerError { + /** + * @param {Buffer} dataContractId + * @param {Buffer} documentTransitionId + */ + constructor(dataContractId, documentTransitionId) { + super(dataContractId, documentTransitionId, 'Data trigger have not returned any result'); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } +} + +module.exports = DataTriggerInvalidResultError; diff --git a/packages/js-dpp/lib/errors/consensus/state/document/DocumentAlreadyPresentError.js b/packages/js-dpp/lib/errors/consensus/state/document/DocumentAlreadyPresentError.js new file mode 100644 index 00000000000..33f9045a56b --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/document/DocumentAlreadyPresentError.js @@ -0,0 +1,27 @@ +const AbstractStateError = require('../AbstractStateError'); +const Identifier = require('../../../../identifier/Identifier'); + +class DocumentAlreadyPresentError extends AbstractStateError { + /** + * @param {Buffer} documentId + */ + constructor(documentId) { + super(`Document ${Identifier.from(documentId)} is already present`); + + this.documentId = documentId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get document ID + * + * @return {Buffer} + */ + getDocumentId() { + return this.documentId; + } +} + +module.exports = DocumentAlreadyPresentError; diff --git a/packages/js-dpp/lib/errors/consensus/state/document/DocumentNotFoundError.js b/packages/js-dpp/lib/errors/consensus/state/document/DocumentNotFoundError.js new file mode 100644 index 00000000000..c098156036c --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/document/DocumentNotFoundError.js @@ -0,0 +1,33 @@ +const AbstractStateError = require('../AbstractStateError'); + +const AbstractDocumentTransition = require('../../../../document/stateTransition/DocumentsBatchTransition/documentTransition/AbstractDocumentTransition'); + +class DocumentNotFoundError extends AbstractStateError { + /** + * @param {Buffer} documentId + */ + constructor(documentId) { + const noun = { + [AbstractDocumentTransition.ACTIONS.REPLACE]: 'Updated', + [AbstractDocumentTransition.ACTIONS.DELETE]: 'Deleted', + }; + + super(`${noun[documentId]} document not found`); + + this.documentId = documentId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get Document ID + * + * @return {Buffer} + */ + getDocumentId() { + return this.documentId; + } +} + +module.exports = DocumentNotFoundError; diff --git a/packages/js-dpp/lib/errors/consensus/state/document/DocumentOwnerIdMismatchError.js b/packages/js-dpp/lib/errors/consensus/state/document/DocumentOwnerIdMismatchError.js new file mode 100644 index 00000000000..fcd1ebeb88c --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/document/DocumentOwnerIdMismatchError.js @@ -0,0 +1,49 @@ +const AbstractStateError = require('../AbstractStateError'); +const Identifier = require('../../../../identifier/Identifier'); + +class DocumentOwnerIdMismatchError extends AbstractStateError { + /** + * @param {Buffer} documentId + * @param {Buffer} documentOwnerId + * @param {Buffer} existingDocumentOwnerId + */ + constructor(documentId, documentOwnerId, existingDocumentOwnerId) { + super(`Provided document ${Identifier.from(documentId)} owner ID ${Identifier.from(documentOwnerId)} mismatch with existing ${Identifier.from(existingDocumentOwnerId)}`); + + this.documentId = documentId; + this.documentOwnerId = documentOwnerId; + this.existingDocumentOwnerId = existingDocumentOwnerId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get document ID + * + * @returns {Buffer} + */ + getDocumentId() { + return this.documentId; + } + + /** + * Get document owner ID + * + * @return {Buffer} + */ + getDocumentOwnerId() { + return this.documentOwnerId; + } + + /** + * Get existing Document owner ID + * + * @return {Buffer} + */ + getExistingDocumentOwnerId() { + return this.existingDocumentOwnerId; + } +} + +module.exports = DocumentOwnerIdMismatchError; diff --git a/packages/js-dpp/lib/errors/consensus/state/document/DocumentTimestampWindowViolationError.js b/packages/js-dpp/lib/errors/consensus/state/document/DocumentTimestampWindowViolationError.js new file mode 100644 index 00000000000..0973acc7695 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/document/DocumentTimestampWindowViolationError.js @@ -0,0 +1,67 @@ +const AbstractStateError = require('../AbstractStateError'); +const Identifier = require('../../../../identifier/Identifier'); + +class DocumentTimestampWindowViolationError extends AbstractStateError { + /** + * @param {string} timestampName + * @param {Buffer} documentId + * @param {Date} timestamp + * @param {Date} timeWindowStart + * @param {Date} timeWindowEnd + */ + constructor(timestampName, documentId, timestamp, timeWindowStart, timeWindowEnd) { + super(`Document ${Identifier.from(documentId)} ${timestampName} timestamp (${timestamp}) are out of block time window from ${timeWindowStart} and ${timeWindowEnd}`); + + this.timestampName = timestampName; + this.documentId = documentId; + this.timestamp = timestamp; + this.timeWindowStart = timeWindowStart; + this.timeWindowEnd = timeWindowEnd; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get Document timestamp name + * + * @return {string} + */ + getTimestampName() { + return this.timestampName; + } + + /** + * Get Document ID + * + * @return {Buffer} + */ + getDocumentId() { + return this.documentId; + } + + /** + * Get timestamp + * + * @return {Date} + */ + getTimestamp() { + return this.timestamp; + } + + /** + * @returns {Date} + */ + getTimeWindowStart() { + return this.timeWindowStart; + } + + /** + * @returns {Date} + */ + getTimeWindowEnd() { + return this.timeWindowEnd; + } +} + +module.exports = DocumentTimestampWindowViolationError; diff --git a/packages/js-dpp/lib/errors/consensus/state/document/DocumentTimestampsMismatchError.js b/packages/js-dpp/lib/errors/consensus/state/document/DocumentTimestampsMismatchError.js new file mode 100644 index 00000000000..9e80abf4937 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/document/DocumentTimestampsMismatchError.js @@ -0,0 +1,27 @@ +const AbstractStateError = require('../AbstractStateError'); +const Identifier = require('../../../../identifier/Identifier'); + +class DocumentTimestampsMismatchError extends AbstractStateError { + /** + * @param {Buffer} documentId + */ + constructor(documentId) { + super(`Document ${Identifier.from(documentId)} createdAt and updatedAt timestamps are not equal`); + + this.documentId = documentId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get document create transition + * + * @return {Buffer} + */ + getDocumentId() { + return this.documentId; + } +} + +module.exports = DocumentTimestampsMismatchError; diff --git a/packages/js-dpp/lib/errors/consensus/state/document/DuplicateUniqueIndexError.js b/packages/js-dpp/lib/errors/consensus/state/document/DuplicateUniqueIndexError.js new file mode 100644 index 00000000000..3900887c21d --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/document/DuplicateUniqueIndexError.js @@ -0,0 +1,38 @@ +const AbstractStateError = require('../AbstractStateError'); +const Identifier = require('../../../../identifier/Identifier'); + +class DuplicateUniqueIndexError extends AbstractStateError { + /** + * @param {Buffer} documentId + * @param {string[]} duplicatingProperties + */ + constructor(documentId, duplicatingProperties) { + super(`Document ${Identifier.from(documentId)} has duplicate unique properties ${duplicatingProperties.join(', ')} with other documents`); + + this.documentId = documentId; + this.duplicatingProperties = duplicatingProperties; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get document id + * + * @return {Buffer} + */ + getDocumentId() { + return this.documentId; + } + + /** + * Get duplicating properties + * + * @return {string[]} + */ + getDuplicatingProperties() { + return this.duplicatingProperties; + } +} + +module.exports = DuplicateUniqueIndexError; diff --git a/packages/js-dpp/lib/errors/consensus/state/document/InvalidDocumentRevisionError.js b/packages/js-dpp/lib/errors/consensus/state/document/InvalidDocumentRevisionError.js new file mode 100644 index 00000000000..b59999b2b62 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/document/InvalidDocumentRevisionError.js @@ -0,0 +1,38 @@ +const AbstractStateError = require('../AbstractStateError'); +const Identifier = require('../../../../identifier/Identifier'); + +class InvalidDocumentRevisionError extends AbstractStateError { + /** + * @param {Buffer} documentId + * @param {number} currentRevision + */ + constructor(documentId, currentRevision) { + super(`Document ${Identifier.from(documentId)} has invalid revision. The current revision is ${currentRevision}`); + + this.documentId = documentId; + this.currentRevision = currentRevision; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get Document ID + * + * @return {Buffer} + */ + getDocumentId() { + return this.documentId; + } + + /** + * Get current revision + * + * @return {number} + */ + getCurrentRevision() { + return this.currentRevision; + } +} + +module.exports = InvalidDocumentRevisionError; diff --git a/packages/js-dpp/lib/errors/consensus/state/identity/DuplicatedIdentityPublicKeyError.js b/packages/js-dpp/lib/errors/consensus/state/identity/DuplicatedIdentityPublicKeyError.js new file mode 100644 index 00000000000..26f38925196 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/identity/DuplicatedIdentityPublicKeyError.js @@ -0,0 +1,26 @@ +const AbstractStateError = require('../AbstractStateError'); + +class DuplicatedIdentityPublicKeyError extends AbstractStateError { + /** + * @param {number[]} duplicatedPublicKeyIds + */ + constructor(duplicatedPublicKeyIds) { + super(`Duplicated public keys ${duplicatedPublicKeyIds.join(', ')} found`); + + this.duplicatedPublicKeyIds = duplicatedPublicKeyIds; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get duplicated public key ids + * + * @return {number[]} + */ + getDuplicatedPublicKeysIds() { + return this.duplicatedPublicKeyIds; + } +} + +module.exports = DuplicatedIdentityPublicKeyError; diff --git a/packages/js-dpp/lib/errors/consensus/state/identity/DuplicatedIdentityPublicKeyIdError.js b/packages/js-dpp/lib/errors/consensus/state/identity/DuplicatedIdentityPublicKeyIdError.js new file mode 100644 index 00000000000..dac4c1d275d --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/identity/DuplicatedIdentityPublicKeyIdError.js @@ -0,0 +1,26 @@ +const AbstractStateError = require('../AbstractStateError'); + +class DuplicatedIdentityPublicKeyIdError extends AbstractStateError { + /** + * @param {number[]} duplicatedIds + */ + constructor(duplicatedIds) { + super(`Duplicated public key ids ${duplicatedIds.join(', ')} found`); + + this.duplicatedIds = duplicatedIds; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get duplicated public key ids + * + * @return {number[]} + */ + getDuplicatedIds() { + return this.duplicatedIds; + } +} + +module.exports = DuplicatedIdentityPublicKeyIdError; diff --git a/packages/js-dpp/lib/errors/consensus/state/identity/IdentityAlreadyExistsError.js b/packages/js-dpp/lib/errors/consensus/state/identity/IdentityAlreadyExistsError.js new file mode 100644 index 00000000000..de45c76782f --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/identity/IdentityAlreadyExistsError.js @@ -0,0 +1,27 @@ +const AbstractStateError = require('../AbstractStateError'); +const Identifier = require('../../../../identifier/Identifier'); + +class IdentityAlreadyExistsError extends AbstractStateError { + /** + * @param {Buffer} identityId + */ + constructor(identityId) { + super(`Identity ${Identifier.from(identityId)} already exists`); + + this.identityId = identityId; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get identity id + * + * @return {Buffer} + */ + getIdentityId() { + return this.identityId; + } +} + +module.exports = IdentityAlreadyExistsError; diff --git a/packages/js-dpp/lib/errors/consensus/state/identity/IdentityPublicKeyDisabledAtWindowViolationError.js b/packages/js-dpp/lib/errors/consensus/state/identity/IdentityPublicKeyDisabledAtWindowViolationError.js new file mode 100644 index 00000000000..2e320e2d3ad --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/identity/IdentityPublicKeyDisabledAtWindowViolationError.js @@ -0,0 +1,44 @@ +const AbstractStateError = require('../AbstractStateError'); + +class IdentityPublicKeyDisabledAtWindowViolationError extends AbstractStateError { + /** + * @param {Date} disabledAt + * @param {Date} timeWindowStart + * @param {Date} timeWindowEnd + */ + constructor(disabledAt, timeWindowStart, timeWindowEnd) { + super(`Identity public keys disabled time (${disabledAt}) is out of block time window from ${timeWindowStart} and ${timeWindowEnd}`); + + this.disabledAt = disabledAt; + this.timeWindowStart = timeWindowStart; + this.timeWindowEnd = timeWindowEnd; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get disabledAt + * + * @return {Date} + */ + getDisabledAt() { + return this.disabledAt; + } + + /** + * @returns {Date} + */ + getTimeWindowStart() { + return this.timeWindowStart; + } + + /** + * @returns {Date} + */ + getTimeWindowEnd() { + return this.timeWindowEnd; + } +} + +module.exports = IdentityPublicKeyDisabledAtWindowViolationError; diff --git a/packages/js-dpp/lib/errors/consensus/state/identity/IdentityPublicKeyIsReadOnlyError.js b/packages/js-dpp/lib/errors/consensus/state/identity/IdentityPublicKeyIsReadOnlyError.js new file mode 100644 index 00000000000..60c14ad3bb6 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/identity/IdentityPublicKeyIsReadOnlyError.js @@ -0,0 +1,22 @@ +const AbstractStateError = require('../AbstractStateError'); + +class IdentityPublicKeyIsReadOnlyError extends AbstractStateError { + /** + * @param {number} publicKeyIndex + */ + constructor(publicKeyIndex) { + super(`Identity Public Key #${publicKeyIndex} is read only`); + + this.publicKeyIndex = publicKeyIndex; + } + + /** + * + * @returns {number} + */ + getPublicKeyIndex() { + return this.publicKeyIndex; + } +} + +module.exports = IdentityPublicKeyIsReadOnlyError; diff --git a/packages/js-dpp/lib/errors/consensus/state/identity/InvalidIdentityPublicKeyIdError.js b/packages/js-dpp/lib/errors/consensus/state/identity/InvalidIdentityPublicKeyIdError.js new file mode 100644 index 00000000000..e2a91f7ac61 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/identity/InvalidIdentityPublicKeyIdError.js @@ -0,0 +1,26 @@ +const AbstractStateError = require('../AbstractStateError'); + +class InvalidIdentityPublicKeyIdError extends AbstractStateError { + /** + * @param {number} id + */ + constructor(id) { + super(`Identity public key with ID ${id} does not exist`); + + this.id = id; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get ID + * + * @return {number} + */ + getId() { + return this.id; + } +} + +module.exports = InvalidIdentityPublicKeyIdError; diff --git a/packages/js-dpp/lib/errors/consensus/state/identity/InvalidIdentityRevisionError.js b/packages/js-dpp/lib/errors/consensus/state/identity/InvalidIdentityRevisionError.js new file mode 100644 index 00000000000..cb96b4f78d4 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/identity/InvalidIdentityRevisionError.js @@ -0,0 +1,38 @@ +const AbstractStateError = require('../AbstractStateError'); +const Identifier = require('../../../../identifier/Identifier'); + +class InvalidIdentityRevisionError extends AbstractStateError { + /** + * @param {Buffer} identityId + * @param {number} currentRevision + */ + constructor(identityId, currentRevision) { + super(`Identity ${Identifier.from(identityId)} has invalid revision. The current revision is ${currentRevision}`); + + this.identityId = identityId; + this.currentRevision = currentRevision; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * Get Identity ID + * + * @return {Buffer} + */ + getIdentityId() { + return this.identityId; + } + + /** + * Get current revision + * + * @return {number} + */ + getCurrentRevision() { + return this.currentRevision; + } +} + +module.exports = InvalidIdentityRevisionError; diff --git a/packages/js-dpp/lib/errors/consensus/state/identity/MaxIdentityPublicKeyLimitReachedError.js b/packages/js-dpp/lib/errors/consensus/state/identity/MaxIdentityPublicKeyLimitReachedError.js new file mode 100644 index 00000000000..7046ca19a93 --- /dev/null +++ b/packages/js-dpp/lib/errors/consensus/state/identity/MaxIdentityPublicKeyLimitReachedError.js @@ -0,0 +1,25 @@ +const AbstractStateError = require('../AbstractStateError'); + +class MaxIdentityPublicKeyLimitReachedError extends AbstractStateError { + /** + * @param {number} maxItems + */ + constructor(maxItems) { + super(`Identity cannot contain more than ${maxItems} public keys`); + + this.maxItems = maxItems; + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * + * @return {number} + */ + geMaxItems() { + return this.maxItems; + } +} + +module.exports = MaxIdentityPublicKeyLimitReachedError; diff --git a/packages/js-dpp/lib/identifier/Identifier.js b/packages/js-dpp/lib/identifier/Identifier.js new file mode 100644 index 00000000000..f229665106a --- /dev/null +++ b/packages/js-dpp/lib/identifier/Identifier.js @@ -0,0 +1,113 @@ +const bs58 = require('bs58'); + +const IdentifierError = require('./errors/IdentifierError'); + +// Buffer extending is not a trivial thing: +// * https://github.com/nodejs/node/commit/651a5b51eb838e8e23a5b94ba34e8e06630a004a +// * https://github.com/nodejs/node/issues/4701 +// * https://github.com/nodejs/help/issues/1300 +// * https://github.com/nodejs/node/issues/2882 + +/** + * @param {Buffer} buffer + * @returns {Identifier} + * @constructor + */ +function Identifier(buffer) { + if (!Buffer.isBuffer(buffer)) { + throw new IdentifierError('Identifier expects Buffer'); + } + + if (buffer.length !== 32) { + throw new IdentifierError('Identifier must be 32 long'); + } + + const patchedBuffer = Buffer.from(buffer); + + Object.setPrototypeOf(patchedBuffer, Identifier.prototype); + + // noinspection JSValidateTypes + return patchedBuffer; +} + +/** + * Convert to Buffer + * + * @return {Buffer} + */ +Identifier.prototype.toBuffer = function toBuffer() { + return Buffer.from(this); +}; + +/** + * Encode to CBOR + * + * @param {Encoder} encoder + * @return {boolean} + */ +Identifier.prototype.encodeCBOR = function encodeCBOR(encoder) { + encoder.pushAny(this.toBuffer()); + + return true; +}; + +/** + * Convert to JSON + * + * @return {string} + */ +Identifier.prototype.toJSON = function toJSON() { + return this.toString(); +}; + +/** + * Encode to string + * + * @param {string} [encoding=base58] + * @return {string} + */ +Identifier.prototype.toString = function toString(encoding = 'base58') { + if (encoding === 'base58') { + return bs58.encode(this); + } + + return this.toBuffer().toString(encoding); +}; + +/** + * Create Identifier from buffer or encoded string + * + * @param {string|Buffer} value + * @param {string} encoding + * @return {Identifier} + */ +Identifier.from = function from(value, encoding = undefined) { + let buffer; + + if (typeof value === 'string') { + if (encoding === undefined) { + // eslint-disable-next-line no-param-reassign + encoding = 'base58'; + } + + if (encoding === 'base58') { + buffer = bs58.decode(value); + } else { + buffer = Buffer.from(value, 'base64'); + } + } else { + if (encoding !== undefined) { + throw new IdentifierError('encoding accepted only with type string'); + } + + buffer = value; + } + + return new Identifier(buffer); +}; + +Object.setPrototypeOf(Identifier.prototype, Buffer.prototype); + +Identifier.MEDIA_TYPE = 'application/x.dash.dpp.identifier'; + +module.exports = Identifier; diff --git a/packages/js-dpp/lib/identifier/createAndValidateIdentifier.js b/packages/js-dpp/lib/identifier/createAndValidateIdentifier.js new file mode 100644 index 00000000000..bff9f56d6cc --- /dev/null +++ b/packages/js-dpp/lib/identifier/createAndValidateIdentifier.js @@ -0,0 +1,29 @@ +const Identifier = require('./Identifier'); +const InvalidIdentifierError = require('../errors/consensus/basic/InvalidIdentifierError'); +const IdentifierError = require('./errors/IdentifierError'); + +/** + * @param {string} name + * @param {Buffer} buffer + * @param {ValidationResult} result + * @return {Identifier} + */ +function createAndValidateIdentifier(name, buffer, result) { + try { + return new Identifier(buffer); + } catch (e) { + if (e instanceof IdentifierError) { + const consensusError = new InvalidIdentifierError(name, e.message); + + consensusError.setIdentifierError(e); + + result.addError(consensusError); + + return undefined; + } + + throw e; + } +} + +module.exports = createAndValidateIdentifier; diff --git a/packages/js-dpp/lib/identifier/errors/IdentifierError.js b/packages/js-dpp/lib/identifier/errors/IdentifierError.js new file mode 100644 index 00000000000..e539c0f21c4 --- /dev/null +++ b/packages/js-dpp/lib/identifier/errors/IdentifierError.js @@ -0,0 +1,7 @@ +const DPPError = require('../../errors/DPPError'); + +class IdentifierError extends DPPError { + +} + +module.exports = IdentifierError; diff --git a/packages/js-dpp/lib/identity/Identity.js b/packages/js-dpp/lib/identity/Identity.js new file mode 100644 index 00000000000..a4d583f8388 --- /dev/null +++ b/packages/js-dpp/lib/identity/Identity.js @@ -0,0 +1,260 @@ +const hashModule = require('../util/hash'); +const serializer = require('../util/serializer'); +const IdentityPublicKey = require('./IdentityPublicKey'); +const Identifier = require('../identifier/Identifier'); + +class Identity { + /** + * @param {RawIdentity} rawIdentity + */ + constructor(rawIdentity) { + if (Object.prototype.hasOwnProperty.call(rawIdentity, 'protocolVersion')) { + this.protocolVersion = rawIdentity.protocolVersion; + } + + if (Object.prototype.hasOwnProperty.call(rawIdentity, 'id')) { + this.id = Identifier.from(rawIdentity.id); + } + + if (Object.prototype.hasOwnProperty.call(rawIdentity, 'publicKeys')) { + this.publicKeys = rawIdentity.publicKeys.map((rawPublicKey) => ( + new IdentityPublicKey(rawPublicKey) + )); + } + + if (Object.prototype.hasOwnProperty.call(rawIdentity, 'balance')) { + this.balance = rawIdentity.balance; + } + + if (Object.prototype.hasOwnProperty.call(rawIdentity, 'revision')) { + this.revision = rawIdentity.revision; + } + } + + /** + * @returns {number} + */ + getProtocolVersion() { + return this.protocolVersion; + } + + /** + * @return {Identifier} + */ + getId() { + return this.id; + } + + /** + * @param {IdentityPublicKey[]} publicKeys + * @return {Identity} + */ + setPublicKeys(publicKeys) { + this.publicKeys = publicKeys; + + return this; + } + + /** + * @return {IdentityPublicKey[]} + */ + getPublicKeys() { + return this.publicKeys; + } + + /** + * Returns a public key for a given id + * + * @param {number} keyId + * @return {IdentityPublicKey} + */ + getPublicKeyById(keyId) { + return this.publicKeys.find((k) => k.getId() === keyId); + } + + /** + * Get plain object representation + * + * @return {RawIdentity} + */ + toObject() { + return { + protocolVersion: this.getProtocolVersion(), + id: this.getId().toBuffer(), + publicKeys: this.getPublicKeys() + .map((publicKey) => publicKey.toObject()), + balance: this.getBalance(), + revision: this.getRevision(), + }; + } + + /** + * Get JSON representation + * + * @return {JsonIdentity} + */ + toJSON() { + return { + protocolVersion: this.getProtocolVersion(), + id: this.getId().toString(), + publicKeys: this.getPublicKeys() + .map((publicKey) => publicKey.toJSON()), + balance: this.getBalance(), + revision: this.getRevision(), + }; + } + + /** + * @return {Buffer} + */ + toBuffer() { + const serializedData = this.toObject(); + delete serializedData.protocolVersion; + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(this.getProtocolVersion(), 0); + + return Buffer.concat([protocolVersionUInt32, serializer.encode(serializedData)]); + } + + /** + * @return {Buffer} + */ + hash() { + const { hash } = hashModule; + + return hash(this.toBuffer()); + } + + /** + * Returns balance + * @returns {number} + */ + getBalance() { + return this.balance; + } + + /** + * Set Identity balance + * + * @param {number} balance + * @return {Identity} + */ + setBalance(balance) { + this.balance = balance; + + return this; + } + + /** + * Increase balance + * + * @param {number} amount + * @return {number} + */ + increaseBalance(amount) { + this.balance += amount; + + return this.balance; + } + + /** + * Reduce balance + * + * @param {number} amount + * @return {number} + */ + reduceBalance(amount) { + this.balance -= amount; + + return this.balance; + } + + /** + * Set asset lock proof + * + * @param {InstantAssetLockProof|ChainAssetLockProof} assetLockProof + * @return {Identity} + */ + setAssetLockProof(assetLockProof) { + this.assetLockProof = assetLockProof; + + return this; + } + + /** + * Get asset lock proof + * + * @return {InstantAssetLockProof|ChainAssetLockProof} + */ + getAssetLockProof() { + return this.assetLockProof; + } + + /** + * Get Identity revision + * + * @return {number} + */ + getRevision() { + return this.revision; + } + + /** + * Set Identity revision + * + * @param {number} revision + * @return {Identity} + */ + setRevision(revision) { + this.revision = revision; + + return this; + } + + /** + * Set metadata + * @param {Metadata} metadata + */ + setMetadata(metadata) { + this.metadata = metadata; + } + + /** + * Get metadata + * @returns {Metadata|null} + */ + getMetadata() { + return this.metadata; + } + + /** + * Get the biggest public key ID + * @returns {number} + */ + getPublicKeyMaxId() { + return this.publicKeys.reduce( + (result, publicKey) => (result > publicKey.getId() ? result : publicKey.getId()), 0, + ); + } +} + +/** + * @typedef {Object} RawIdentity + * @property {number} protocolVersion + * @property {Buffer} id + * @property {RawIdentityPublicKey[]} publicKeys + * @property {number} balance + * @property {number} revision + */ + +/** + * @typedef {Object} JsonIdentity + * @property {number} protocolVersion + * @property {string} id + * @property {JsonIdentityPublicKey[]} publicKeys + * @property {number} balance + * @property {number} revision + */ + +module.exports = Identity; diff --git a/packages/js-dpp/lib/identity/IdentityFacade.js b/packages/js-dpp/lib/identity/IdentityFacade.js new file mode 100644 index 00000000000..d48f12fc2eb --- /dev/null +++ b/packages/js-dpp/lib/identity/IdentityFacade.js @@ -0,0 +1,163 @@ +const Identity = require('./Identity'); +const IdentityFactory = require('./IdentityFactory'); + +const validateIdentityFactory = require('./validation/validateIdentityFactory'); +const validatePublicKeysFactory = require('./validation/validatePublicKeysFactory'); +const decodeProtocolEntityFactory = require('../decodeProtocolEntityFactory'); + +const publicKeyJsonSchema = require('../../schema/identity/publicKey.json'); + +const protocolVersion = require('../version/protocolVersion'); +const validateProtocolVersionFactory = require('../version/validateProtocolVersionFactory'); + +/** + * @class IdentityFacade + * @property {validateIdentity} validateIdentity + */ +class IdentityFacade { + /** + * @param {DashPlatformProtocol} dpp + * @param {BlsSignatures} bls + */ + constructor(dpp, bls) { + const validatePublicKeys = validatePublicKeysFactory( + dpp.getJsonSchemaValidator(), + publicKeyJsonSchema, + bls, + ); + + const validateProtocolVersion = validateProtocolVersionFactory( + dpp, + protocolVersion.compatibility, + ); + + this.validateIdentity = validateIdentityFactory( + dpp.getJsonSchemaValidator(), + validatePublicKeys, + validateProtocolVersion, + ); + + const decodeProtocolEntity = decodeProtocolEntityFactory(); + + this.factory = new IdentityFactory( + dpp, + this.validateIdentity, + decodeProtocolEntity, + ); + } + + /** + * Create Identity + * + * @param {InstantAssetLockProof|ChainAssetLockProof} assetLockProof + * @param {PublicKeyConfig[]} publicKeyConfigs + * @return {Identity} + */ + create(assetLockProof, publicKeyConfigs) { + return this.factory.create( + assetLockProof, + publicKeyConfigs, + ); + } + + /** + * Create Identity from the plain object + * + * @param {RawIdentity} rawIdentity + * @param [options] + * @param {boolean} [options.skipValidation] + * @return {Identity} + */ + createFromObject(rawIdentity, options = {}) { + return this.factory.createFromObject(rawIdentity, options); + } + + /** + * Create identity from a Buffer + * + * @param {Buffer} buffer + * @param [options] + * @param {boolean} [options.skipValidation] + * @return {Identity} + */ + createFromBuffer(buffer, options = {}) { + return this.factory.createFromBuffer(buffer, options); + } + + /** + * Validate identity + * + * @param {Identity|RawIdentity} identity + * @return {ValidationResult} + */ + validate(identity) { + let rawIdentity; + if (identity instanceof Identity) { + rawIdentity = identity.toObject(); + } else { + rawIdentity = identity; + } + + return this.validateIdentity(rawIdentity); + } + + /** + * Create Instant Asset Lock proof + * + * @param {InstantLock} instantLock + * @param {Transaction} assetLockTransaction + * @param {number} outputIndex + * @returns {InstantAssetLockProof} + */ + createInstantAssetLockProof(instantLock, assetLockTransaction, outputIndex) { + return this.factory.createInstantAssetLockProof(instantLock, assetLockTransaction, outputIndex); + } + + /** + * Create Chain Asset Lock proof + * + * @param {number} coreChainLockedHeight + * @param {Buffer} outPoint + * @returns {InstantAssetLockProof|ChainAssetLockProof} + */ + createChainAssetLockProof(coreChainLockedHeight, outPoint) { + return this.factory.createChainAssetLockProof(coreChainLockedHeight, outPoint); + } + + /** + * Create identity create transition + * + * @param {Identity} identity + * @return {IdentityCreateTransition} + */ + createIdentityCreateTransition(identity) { + return this.factory.createIdentityCreateTransition(identity); + } + + /** + * Create identity top up transition + * + * @param {Identifier|Buffer|string} identityId - identity to top up + * @param {InstantAssetLockProof|ChainAssetLockProof} assetLockProof + * @return {IdentityTopUpTransition} + */ + createIdentityTopUpTransition(identityId, assetLockProof) { + return this.factory.createIdentityTopUpTransition( + identityId, + assetLockProof, + ); + } + + /** + * Create identity update transition + * + * @param {Identity} identity + * @param {{add: IdentityPublicKey[]; disable: IdentityPublicKey[]}} publicKeys + * @returns {IdentityUpdateTransition} + */ + createIdentityUpdateTransition(identity, publicKeys) { + return this.factory.createIdentityUpdateTransition(identity, publicKeys); + } +} + +module.exports = IdentityFacade; diff --git a/packages/js-dpp/lib/identity/IdentityFactory.js b/packages/js-dpp/lib/identity/IdentityFactory.js new file mode 100644 index 00000000000..b6c35275c30 --- /dev/null +++ b/packages/js-dpp/lib/identity/IdentityFactory.js @@ -0,0 +1,219 @@ +const Identity = require('./Identity'); +const IdentityPublicKey = require('./IdentityPublicKey'); + +const IdentityCreateTransition = require('./stateTransition/IdentityCreateTransition/IdentityCreateTransition'); +const IdentityTopUpTransition = require('./stateTransition/IdentityTopUpTransition/IdentityTopUpTransition'); +const IdentityUpdateTransition = require('./stateTransition/IdentityUpdateTransition/IdentityUpdateTransition'); + +const InvalidIdentityError = require('./errors/InvalidIdentityError'); +const InstantAssetLockProof = require('./stateTransition/assetLockProof/instant/InstantAssetLockProof'); +const ChainAssetLockProof = require('./stateTransition/assetLockProof/chain/ChainAssetLockProof'); +const AbstractConsensusError = require('../errors/consensus/AbstractConsensusError'); + +class IdentityFactory { + /** + * @param {DashPlatformProtocol} dpp + * @param {validateIdentity} validateIdentity + * @param {decodeProtocolEntity} decodeProtocolEntity + */ + constructor( + dpp, + validateIdentity, + decodeProtocolEntity, + ) { + this.dpp = dpp; + this.validateIdentity = validateIdentity; + this.decodeProtocolEntity = decodeProtocolEntity; + } + + /** + * Create Identity + * + * @param {InstantAssetLockProof} assetLockProof + * @param {PublicKeyConfig[]} publicKeyConfigs + * @return {Identity} + */ + create(assetLockProof, publicKeyConfigs) { + const identity = new Identity({ + protocolVersion: this.dpp.getProtocolVersion(), + id: assetLockProof.createIdentifier(), + balance: 0, + publicKeys: publicKeyConfigs.map((publicKey, i) => ({ + id: publicKey.id == null ? i : publicKey.id, + type: publicKey.type == null ? IdentityPublicKey.TYPES.ECDSA_SECP256K1 : publicKey.type, + purpose: publicKey.purpose == null ? IdentityPublicKey.PURPOSES.AUTHENTICATION + : publicKey.purpose, + securityLevel: publicKey.securityLevel == null + ? IdentityPublicKey.SECURITY_LEVELS.CRITICAL : publicKey.securityLevel, + // Copy data buffer + data: publicKey.key.toBuffer(), + readOnly: Boolean(publicKey.readOnly), + })), + revision: 0, + }); + + identity.setAssetLockProof(assetLockProof); + + return identity; + } + + /** + * Create identity from a plain object + * + * @param {RawIdentity} rawIdentity + * @param [options] + * @param {boolean} [options.skipValidation] + * @return {Identity} + */ + createFromObject(rawIdentity, options = {}) { + const opts = { skipValidation: false, ...options }; + + if (!opts.skipValidation) { + const result = this.validateIdentity(rawIdentity); + + if (!result.isValid()) { + throw new InvalidIdentityError(result.getErrors(), rawIdentity); + } + } + + return new Identity(rawIdentity); + } + + /** + * Create Identity from a Buffer + * + * @param {Buffer} buffer + * @param {Object} options + * @param {boolean} [options.skipValidation=false] + * @return {Identity} + */ + createFromBuffer(buffer, options = {}) { + let rawIdentity; + let protocolVersion; + + try { + [protocolVersion, rawIdentity] = this.decodeProtocolEntity( + buffer, + ); + + rawIdentity.protocolVersion = protocolVersion; + } catch (error) { + if (error instanceof AbstractConsensusError) { + throw new InvalidIdentityError([error]); + } + + throw error; + } + + return this.createFromObject(rawIdentity, options); + } + + /** + * Create Instant Asset Lock proof + * + * @param {InstantLock} instantLock + * @param {Transaction} assetLockTransaction + * @param {number} outputIndex + * @returns {InstantAssetLockProof} + */ + createInstantAssetLockProof(instantLock, assetLockTransaction, outputIndex) { + return new InstantAssetLockProof({ + instantLock: instantLock.toBuffer(), + transaction: assetLockTransaction.toBuffer(), + outputIndex, + }); + } + + /** + * Create Chain Asset Lock proof + * + * @param {number} coreChainLockedHeight + * @param {Buffer} outPoint + * @returns {ChainAssetLockProof} + */ + createChainAssetLockProof(coreChainLockedHeight, outPoint) { + return new ChainAssetLockProof({ + coreChainLockedHeight, + outPoint, + }); + } + + /** + * Create identity create transition + * + * @param {Identity} identity + * @return {IdentityCreateTransition} + */ + createIdentityCreateTransition(identity) { + // Copy public keys + const publicKeys = identity.getPublicKeys() + .map((publicKey) => publicKey.toObject()); + + return new IdentityCreateTransition({ + protocolVersion: this.dpp.getProtocolVersion(), + assetLockProof: identity.getAssetLockProof().toObject(), + publicKeys, + }); + } + + /** + * Create identity top up transition + * + * @param {Identifier|Buffer|string} identityId - identity to top up + * @param {InstantAssetLockProof} assetLockProof + * @return {IdentityTopUpTransition} + */ + createIdentityTopUpTransition(identityId, assetLockProof) { + return new IdentityTopUpTransition({ + protocolVersion: this.dpp.getProtocolVersion(), + identityId, + assetLockProof: assetLockProof.toObject(), + }); + } + + /** + * Create identity update transition + * + * @param {Identity} identity - identity to update + * @param {{add: IdentityPublicKey[]; disable: IdentityPublicKey[]}} publicKeys - public + * keys to add or delete + * @return {IdentityUpdateTransition} + */ + createIdentityUpdateTransition( + identity, + publicKeys = {}, + ) { + const rawStateTransition = { + protocolVersion: this.dpp.getProtocolVersion(), + identityId: identity.getId(), + revision: identity.getRevision() + 1, + }; + + if (publicKeys.add) { + // Copy public keys + rawStateTransition.addPublicKeys = publicKeys.add.map((publicKey) => ( + new IdentityPublicKey(publicKey.toObject()) + )); + } + + if (publicKeys.disable) { + const now = new Date().getTime(); + + rawStateTransition.disablePublicKeys = publicKeys.disable.map((pk) => pk.getId()); + rawStateTransition.publicKeysDisabledAt = now; + } + + return new IdentityUpdateTransition(rawStateTransition); + } +} + +/** + * @typedef {Object} PublicKeyConfig + * @property [number|undefined] id + * @property [number|undefined] type + * @property [number|undefined] purpose + * @property [number|undefined] securityLevel + * @property {PublicKey} key + */ + +module.exports = IdentityFactory; diff --git a/packages/js-dpp/lib/identity/IdentityPublicKey.js b/packages/js-dpp/lib/identity/IdentityPublicKey.js new file mode 100644 index 00000000000..b052227c981 --- /dev/null +++ b/packages/js-dpp/lib/identity/IdentityPublicKey.js @@ -0,0 +1,354 @@ +const { crypto: { Hash } } = require('@dashevo/dashcore-lib'); + +const EmptyPublicKeyDataError = require('./errors/EmptyPublicKeyDataError'); +const InvalidIdentityPublicKeyTypeError = require('../stateTransition/errors/InvalidIdentityPublicKeyTypeError'); + +class IdentityPublicKey { + /** + * @param {RawIdentityPublicKey} [rawIdentityPublicKey] + */ + constructor(rawIdentityPublicKey = { }) { + if (Object.prototype.hasOwnProperty.call(rawIdentityPublicKey, 'id')) { + this.setId(rawIdentityPublicKey.id); + } + + if (Object.prototype.hasOwnProperty.call(rawIdentityPublicKey, 'type')) { + this.setType(rawIdentityPublicKey.type); + } + + if (Object.prototype.hasOwnProperty.call(rawIdentityPublicKey, 'purpose')) { + this.setPurpose(rawIdentityPublicKey.purpose); + } + + if (Object.prototype.hasOwnProperty.call(rawIdentityPublicKey, 'securityLevel')) { + this.setSecurityLevel(rawIdentityPublicKey.securityLevel); + } + + if (Object.prototype.hasOwnProperty.call(rawIdentityPublicKey, 'data')) { + this.setData(rawIdentityPublicKey.data); + } + + if (Object.prototype.hasOwnProperty.call(rawIdentityPublicKey, 'readOnly')) { + this.setReadOnly(rawIdentityPublicKey.readOnly); + } + + if (Object.prototype.hasOwnProperty.call(rawIdentityPublicKey, 'disabledAt')) { + this.setDisabledAt(rawIdentityPublicKey.disabledAt); + } + + if (Object.prototype.hasOwnProperty.call(rawIdentityPublicKey, 'signature')) { + this.setSignature(rawIdentityPublicKey.signature); + } + } + + /** + * Get key ID + * + * @return {number} + */ + getId() { + return this.id; + } + + /** + * Set key ID + * + * @param {number} id + * @return {IdentityPublicKey} + */ + setId(id) { + this.id = id; + + return this; + } + + /** + * Get key type + * + * @return {number} + */ + getType() { + return this.type; + } + + /** + * Set key type + * + * @param {number} type + * @return {IdentityPublicKey} + */ + setType(type) { + this.type = type; + + return this; + } + + /** + * Set raw public key + * + * @param {Buffer} data + * @return {IdentityPublicKey} + */ + setData(data) { + this.data = data; + + return this; + } + + /** + * Get raw public key + * + * @return {Buffer} + */ + getData() { + return this.data; + } + + /** + * Set the raw purpose value. A uint8 number + * + * @param {number} purpose + * @return {IdentityPublicKey} + */ + setPurpose(purpose) { + this.purpose = purpose; + + return this; + } + + /** + * Get the raw purpose value. A uint8 number + * + * @return number + */ + getPurpose() { + return this.purpose; + } + + /** + * Set the raw security level. A uint8 number + * + * @param {number} securityLevel + * @return {IdentityPublicKey} + */ + setSecurityLevel(securityLevel) { + this.securityLevel = securityLevel; + + return this; + } + + /** + * Get the raw security level value. A uint8 number + * + * @return number + */ + getSecurityLevel() { + return this.securityLevel; + } + + /** + * Set readOnly flag + * + * @param {boolean} readOnly + * @return {IdentityPublicKey} + */ + setReadOnly(readOnly) { + this.readOnly = readOnly; + + return this; + } + + /** + * Get readOnly flag + * + * @return {boolean} + */ + isReadOnly() { + return this.readOnly; + } + + /** + * Set disabledAt timestamp + * + * @param {number} disabledAt + * @return {IdentityPublicKey} + */ + setDisabledAt(disabledAt) { + this.disabledAt = disabledAt; + + return this; + } + + /** + * Get disabledAt timestamp + * + * @return {number} + */ + getDisabledAt() { + return this.disabledAt; + } + + /** + * Set signature + * + * @param {Buffer} signature + * @returns {IdentityPublicKey} + */ + setSignature(signature) { + this.signature = signature; + + return this; + } + + /** + * Get signature + * + * @returns {Buffer} + */ + getSignature() { + return this.signature; + } + + /** + * Get the original public key hash + * + * @return {Buffer} + */ + hash() { + if (!this.getData()) { + throw new EmptyPublicKeyDataError(); + } + + switch (this.getType()) { + case IdentityPublicKey.TYPES.BLS12_381: + case IdentityPublicKey.TYPES.ECDSA_SECP256K1: { + return Hash.sha256ripemd160(this.getData()); + } + case IdentityPublicKey.TYPES.ECDSA_HASH160: + case IdentityPublicKey.TYPES.BIP13_SCRIPT_HASH: + return this.getData(); + default: + throw new InvalidIdentityPublicKeyTypeError(this.getType()); + } + } + + /** + * Get a plain object representation + * + * @param {Object} [options] + * @param {Object} [options.skipSignature=false] + * + * @return {RawIdentityPublicKey} + */ + toObject(options = {}) { + const result = { + id: this.getId(), + type: this.getType(), + purpose: this.getPurpose(), + securityLevel: this.getSecurityLevel(), + data: this.getData(), + readOnly: this.isReadOnly(), + }; + + if (this.getDisabledAt() !== undefined) { + result.disabledAt = this.getDisabledAt(); + } + + if (!options.skipSignature && this.signature !== undefined) { + result.signature = this.signature; + } + + return result; + } + + /** + * Get a JSON representation + * + * @return {JsonIdentityPublicKey} + */ + toJSON() { + const result = { + ...this.toObject(), + data: this.getData().toString('base64'), + }; + + if (this.signature) { + result.signature = this.signature.toString('base64'); + } + + return result; + } + + /** + * Check if public ket security level is MASTER + * + * @returns {boolean} + */ + isMaster() { + return this.getSecurityLevel() === IdentityPublicKey.SECURITY_LEVELS.MASTER; + } +} + +/** + * @typedef {Object} RawIdentityPublicKey + * @property {number} id + * @property {number} type + * @property {number} purpose + * @property {number} securityLevel + * @property {Buffer} data + * @property {boolean} readOnly + * @property {number} [disabledAt] + * @property {Buffer} [signature] + */ + +/** + * @typedef {Object} JsonIdentityPublicKey + * @property {number} id + * @property {number} purpose + * @property {number} securityLevel + * @property {number} type + * @property {string} data + * @property {boolean} readOnly + * @property {number} [disabledAt] + * @property {string} [signature] + */ + +IdentityPublicKey.TYPES = { + ECDSA_SECP256K1: 0, + BLS12_381: 1, + ECDSA_HASH160: 2, + BIP13_SCRIPT_HASH: 3, +}; + +IdentityPublicKey.PURPOSES = { + AUTHENTICATION: 0, + ENCRYPTION: 1, + DECRYPTION: 2, + WITHDRAW: 3, +}; + +IdentityPublicKey.SECURITY_LEVELS = { + MASTER: 0, + CRITICAL: 1, + HIGH: 2, + MEDIUM: 3, +}; + +IdentityPublicKey.ALLOWED_SECURITY_LEVELS = {}; +IdentityPublicKey.ALLOWED_SECURITY_LEVELS[IdentityPublicKey.PURPOSES.AUTHENTICATION] = [ + IdentityPublicKey.SECURITY_LEVELS.MASTER, + IdentityPublicKey.SECURITY_LEVELS.CRITICAL, + IdentityPublicKey.SECURITY_LEVELS.HIGH, + IdentityPublicKey.SECURITY_LEVELS.MEDIUM, +]; +IdentityPublicKey.ALLOWED_SECURITY_LEVELS[IdentityPublicKey.PURPOSES.ENCRYPTION] = [ + IdentityPublicKey.SECURITY_LEVELS.MEDIUM, +]; +IdentityPublicKey.ALLOWED_SECURITY_LEVELS[IdentityPublicKey.PURPOSES.DECRYPTION] = [ + IdentityPublicKey.SECURITY_LEVELS.MEDIUM, +]; +IdentityPublicKey.ALLOWED_SECURITY_LEVELS[IdentityPublicKey.PURPOSES.WITHDRAW] = [ + IdentityPublicKey.SECURITY_LEVELS.CRITICAL, +]; + +module.exports = IdentityPublicKey; diff --git a/packages/js-dpp/lib/identity/creditsConverter.js b/packages/js-dpp/lib/identity/creditsConverter.js new file mode 100644 index 00000000000..2ec069900fc --- /dev/null +++ b/packages/js-dpp/lib/identity/creditsConverter.js @@ -0,0 +1,15 @@ +const RATIO = 1000; + +function convertSatoshiToCredits(amount) { + return amount * RATIO; +} + +function convertCreditsToSatoshi(amount) { + return Math.floor(amount / RATIO); +} + +module.exports = { + convertSatoshiToCredits, + convertCreditsToSatoshi, + RATIO, +}; diff --git a/packages/js-dpp/lib/identity/errors/AssetLockOutputNotFoundError.js b/packages/js-dpp/lib/identity/errors/AssetLockOutputNotFoundError.js new file mode 100644 index 00000000000..a7f36e47502 --- /dev/null +++ b/packages/js-dpp/lib/identity/errors/AssetLockOutputNotFoundError.js @@ -0,0 +1,9 @@ +const DPPError = require('../../errors/DPPError'); + +class AssetLockOutputNotFoundError extends DPPError { + constructor() { + super('Asset Lock transaction output not found'); + } +} + +module.exports = AssetLockOutputNotFoundError; diff --git a/packages/js-dpp/lib/identity/errors/AssetLockTransactionIsNotFoundError.js b/packages/js-dpp/lib/identity/errors/AssetLockTransactionIsNotFoundError.js new file mode 100644 index 00000000000..8eefd16dd76 --- /dev/null +++ b/packages/js-dpp/lib/identity/errors/AssetLockTransactionIsNotFoundError.js @@ -0,0 +1,22 @@ +const DPPError = require('../../errors/DPPError'); + +class AssetLockTransactionIsNotFoundError extends DPPError { + /** + * @param {string} transactionId + */ + constructor(transactionId) { + super(`Asset Lock transaction ${transactionId} is not found`); + + this.transactionId = transactionId; + } + + /** + * + * @returns {string} + */ + getTransactionId() { + return this.transactionId; + } +} + +module.exports = AssetLockTransactionIsNotFoundError; diff --git a/packages/js-dpp/lib/identity/errors/EmptyPublicKeyDataError.js b/packages/js-dpp/lib/identity/errors/EmptyPublicKeyDataError.js new file mode 100644 index 00000000000..480689c6b6f --- /dev/null +++ b/packages/js-dpp/lib/identity/errors/EmptyPublicKeyDataError.js @@ -0,0 +1,9 @@ +const DPPError = require('../../errors/DPPError'); + +class EmptyPublicKeyDataError extends DPPError { + constructor() { + super('Public key data is not set'); + } +} + +module.exports = EmptyPublicKeyDataError; diff --git a/packages/js-dpp/lib/identity/errors/InvalidIdentityError.js b/packages/js-dpp/lib/identity/errors/InvalidIdentityError.js new file mode 100644 index 00000000000..ec8bfb83870 --- /dev/null +++ b/packages/js-dpp/lib/identity/errors/InvalidIdentityError.js @@ -0,0 +1,39 @@ +const DPPError = require('../../errors/DPPError'); + +class InvalidIdentityError extends DPPError { + /** + * @param {AbstractConsensusError[]} errors + * @param {RawIdentity} rawIdentity + */ + constructor(errors, rawIdentity) { + let message = `Invalid Identity: "${errors[0].message}"`; + if (errors.length > 1) { + message = `${message} and ${errors.length - 1} more`; + } + + super(message); + + this.errors = errors; + this.rawIdentity = rawIdentity; + } + + /** + * Get validation errors + * + * @return {AbstractConsensusError[]} + */ + getErrors() { + return this.errors; + } + + /** + * Get raw Identity + * + * @return {RawIdentity} + */ + getRawIdentity() { + return this.rawIdentity; + } +} + +module.exports = InvalidIdentityError; diff --git a/packages/js-dpp/lib/identity/errors/UnknownAssetLockProofTypeError.js b/packages/js-dpp/lib/identity/errors/UnknownAssetLockProofTypeError.js new file mode 100644 index 00000000000..758707c571f --- /dev/null +++ b/packages/js-dpp/lib/identity/errors/UnknownAssetLockProofTypeError.js @@ -0,0 +1,23 @@ +const DPPError = require('../../errors/DPPError'); + +class UnknownAssetLockProofTypeError extends DPPError { + /** + * + * @param {number} type + */ + constructor(type) { + super('Unknown Asset lock proof type'); + + this.type = type; + } + + /** + * + * @returns {number} + */ + getType() { + return this.type; + } +} + +module.exports = UnknownAssetLockProofTypeError; diff --git a/packages/js-dpp/lib/identity/getBiggestPossibleIdentity.js b/packages/js-dpp/lib/identity/getBiggestPossibleIdentity.js new file mode 100644 index 00000000000..e69e2777def --- /dev/null +++ b/packages/js-dpp/lib/identity/getBiggestPossibleIdentity.js @@ -0,0 +1,46 @@ +const identityCreateTransitionSchema = require('../../schema/identity/stateTransition/identityCreate.json'); + +const IdentityPublicKey = require('./IdentityPublicKey'); + +const Identity = require('./Identity'); +const generateRandomIdentifier = require('../test/utils/generateRandomIdentifier'); + +let identity; + +/** + * @return {Identity} + */ +function getBiggestPossibleIdentity() { + if (identity) { + return identity; + } + + const publicKeys = []; + + for (let i = 0; i < identityCreateTransitionSchema.properties.publicKeys.maxItems; i++) { + const securityLevel = i === 0 + ? IdentityPublicKey.SECURITY_LEVELS.MASTER + : IdentityPublicKey.SECURITY_LEVELS.HIGH; + + publicKeys.push({ + id: i, + type: IdentityPublicKey.TYPES.BLS12_381, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel, + readOnly: false, + data: Buffer.alloc(48).fill(255), + }); + } + + identity = new Identity({ + protocolVersion: 1, + id: generateRandomIdentifier().toBuffer(), + publicKeys, + balance: Number.MAX_VALUE, + revision: Number.MAX_VALUE, + }); + + return identity; +} + +module.exports = getBiggestPossibleIdentity; diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition.js b/packages/js-dpp/lib/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition.js new file mode 100644 index 00000000000..c5f87d9fcd7 --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition.js @@ -0,0 +1,166 @@ +const AbstractStateTransition = require('../../../stateTransition/AbstractStateTransition'); +const stateTransitionTypes = require('../../../stateTransition/stateTransitionTypes'); +const IdentityPublicKey = require('../../IdentityPublicKey'); +const createAssetLockProofInstance = require('../assetLockProof/createAssetLockProofInstance'); + +class IdentityCreateTransition extends AbstractStateTransition { + /** + * @param {RawIdentityCreateTransition} rawStateTransition + */ + constructor(rawStateTransition) { + super(rawStateTransition); + + this.publicKeys = []; + + if (Object.prototype.hasOwnProperty.call(rawStateTransition, 'publicKeys')) { + this.setPublicKeys( + rawStateTransition.publicKeys + .map((rawPublicKey) => new IdentityPublicKey(rawPublicKey)), + ); + } + + if (Object.prototype.hasOwnProperty.call(rawStateTransition, 'assetLockProof')) { + this.setAssetLockProof(createAssetLockProofInstance(rawStateTransition.assetLockProof)); + } + } + + /** + * Get State Transition type + * + * @return {number} + */ + getType() { + return stateTransitionTypes.IDENTITY_CREATE; + } + + /** + * Set asset lock + * + * @param {InstantAssetLockProof|ChainAssetLockProof} assetLockProof + * @return {IdentityCreateTransition} + */ + setAssetLockProof(assetLockProof) { + this.assetLockProof = assetLockProof; + + this.identityId = assetLockProof.createIdentifier(); + + return this; + } + + /** + * @return {InstantAssetLockProof|ChainAssetLockProof} + */ + getAssetLockProof() { + return this.assetLockProof; + } + + /** + * @return {IdentityPublicKey[]} + */ + getPublicKeys() { + return this.publicKeys; + } + + /** + * Replaces existing set of public keys with a new one + * @param {IdentityPublicKey[]} publicKeys + * @return {IdentityCreateTransition} + */ + setPublicKeys(publicKeys) { + this.publicKeys = publicKeys; + + return this; + } + + /** + * Adds public keys to the existing public keys array + * @param {IdentityPublicKey[]} publicKeys + * @return {IdentityCreateTransition} + */ + addPublicKeys(publicKeys) { + this.publicKeys.push(...publicKeys); + + return this; + } + + /** + * Returns identity id + * + * @return {Identifier} + */ + getIdentityId() { + return this.identityId; + } + + /** + * Returns Owner ID + * + * @return {Identifier} + */ + getOwnerId() { + return this.identityId; + } + + /** + * Get raw state transition + * + * @param {Object} [options] + * @param {boolean} [options.skipSignature=false] + * @param {boolean} [options.skipIdentifiersConversion=false] + * + * @return {RawIdentityCreateTransition} + */ + toObject(options = {}) { + Object.assign( + options, + { + skipIdentifiersConversion: false, + ...options, + }, + ); + + return { + ...super.toObject(options), + assetLockProof: this.getAssetLockProof().toObject(), + publicKeys: this.getPublicKeys() + .map((publicKey) => publicKey.toObject(options)), + }; + } + + /** + * Get state transition as JSON + * + * @return {JsonIdentityCreateTransition} + */ + // eslint-disable-next-line no-unused-vars + toJSON() { + return { + ...super.toJSON(), + assetLockProof: this.getAssetLockProof().toJSON(), + publicKeys: this.getPublicKeys().map((publicKey) => publicKey.toJSON()), + }; + } + + /** + * Returns ids of created identities + * + * @return {Identifier[]} + */ + getModifiedDataIds() { + return [this.getIdentityId()]; + } +} + +/** + * @typedef {RawStateTransition & Object} RawIdentityCreateTransition + * @property {RawInstantAssetLockProof|RawChainAssetLockProof} assetLockProof + * @property {RawIdentityPublicKey[]} publicKeys + */ + +/** + * @typedef {JsonStateTransition & Object} JsonIdentityCreateTransition + * @property {JsonInstantAssetLockProof|JsonChainAssetLockProof} assetLockProof + * @property {JsonIdentityPublicKey[]} publicKeys + */ + +module.exports = IdentityCreateTransition; diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityCreateTransition/applyIdentityCreateTransitionFactory.js b/packages/js-dpp/lib/identity/stateTransition/IdentityCreateTransition/applyIdentityCreateTransitionFactory.js new file mode 100644 index 00000000000..e45b4fb804b --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityCreateTransition/applyIdentityCreateTransitionFactory.js @@ -0,0 +1,63 @@ +const Identity = require('../../Identity'); + +const { convertSatoshiToCredits } = require('../../creditsConverter'); + +/** + * @param {StateRepository} stateRepository + * @param {fetchAssetLockTransactionOutput} fetchAssetLockTransactionOutput + * + * @returns {applyIdentityCreateTransition} + */ +function applyIdentityCreateTransitionFactory( + stateRepository, + fetchAssetLockTransactionOutput, +) { + /** + * Apply identity state transition + * + * @typedef applyIdentityCreateTransition + * + * @param {IdentityCreateTransition} stateTransition + * + * @return {Promise} + */ + async function applyIdentityCreateTransition(stateTransition) { + const executionContext = stateTransition.getExecutionContext(); + + const output = await fetchAssetLockTransactionOutput( + stateTransition.getAssetLockProof(), + executionContext, + ); + + const creditsAmount = convertSatoshiToCredits(output.satoshis); + + const identity = new Identity({ + protocolVersion: stateTransition.getProtocolVersion(), + id: stateTransition.getIdentityId().toBuffer(), + publicKeys: stateTransition.getPublicKeys() + .map((key) => key.toObject({ skipSignature: true })), + balance: creditsAmount, + revision: 0, + }); + + await stateRepository.createIdentity(identity, executionContext); + + const publicKeyHashes = identity + .getPublicKeys() + .map((publicKey) => publicKey.hash()); + + await stateRepository.storeIdentityPublicKeyHashes( + identity.getId(), + publicKeyHashes, + executionContext, + ); + + const outPoint = stateTransition.getAssetLockProof().getOutPoint(); + + await stateRepository.markAssetLockTransactionOutPointAsUsed(outPoint, executionContext); + } + + return applyIdentityCreateTransition; +} + +module.exports = applyIdentityCreateTransitionFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityCreateTransition/validation/basic/validateIdentityCreateTransitionBasicFactory.js b/packages/js-dpp/lib/identity/stateTransition/IdentityCreateTransition/validation/basic/validateIdentityCreateTransitionBasicFactory.js new file mode 100644 index 00000000000..04a2b4b6ceb --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityCreateTransition/validation/basic/validateIdentityCreateTransitionBasicFactory.js @@ -0,0 +1,98 @@ +const identityCreateTransitionSchema = require('../../../../../../schema/identity/stateTransition/identityCreate.json'); + +const convertBuffersToArrays = require('../../../../../util/convertBuffersToArrays'); + +/** + * @param {JsonSchemaValidator} jsonSchemaValidator + * @param {validatePublicKeys} validatePublicKeys + * @param { + * validateRequiredPurposeAndSecurityLevel + * } validateRequiredPurposeAndSecurityLevel + * @param {Object.} proofValidationFunctionsByType + * @param {validateProtocolVersion} validateProtocolVersion + * @param {validatePublicKeySignatures} validatePublicKeySignatures + * + * @return {validateIdentityCreateTransitionBasic} + */ +function validateIdentityCreateTransitionBasicFactory( + jsonSchemaValidator, + validatePublicKeys, + validateRequiredPurposeAndSecurityLevel, + proofValidationFunctionsByType, + validateProtocolVersion, + validatePublicKeySignatures, +) { + /** + * @typedef validateIdentityCreateTransitionBasic + * @param {RawIdentityCreateTransition} rawStateTransition + * @param {StateTransitionExecutionContext} executionContext + * @return {Promise} + */ + // eslint-disable-next-line no-unused-vars + async function validateIdentityCreateTransitionBasic(rawStateTransition, executionContext) { + // Validate state transition against JSON Schema + const result = jsonSchemaValidator.validate( + identityCreateTransitionSchema, + convertBuffersToArrays(rawStateTransition), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + validateProtocolVersion(rawStateTransition.protocolVersion), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + validatePublicKeys(rawStateTransition.publicKeys), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + await validatePublicKeySignatures( + rawStateTransition, + rawStateTransition.publicKeys, + executionContext, + ), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + validateRequiredPurposeAndSecurityLevel(rawStateTransition.publicKeys), + ); + + if (!result.isValid()) { + return result; + } + + const proofValidationFunction = proofValidationFunctionsByType[ + rawStateTransition.assetLockProof.type + ]; + + const assetLockProofValidationResult = await proofValidationFunction( + rawStateTransition.assetLockProof, + executionContext, + ); + + result.merge( + assetLockProofValidationResult, + ); + + return result; + } + + return validateIdentityCreateTransitionBasic; +} + +module.exports = validateIdentityCreateTransitionBasicFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityCreateTransition/validation/state/validateIdentityCreateTransitionStateFactory.js b/packages/js-dpp/lib/identity/stateTransition/IdentityCreateTransition/validation/state/validateIdentityCreateTransitionStateFactory.js new file mode 100644 index 00000000000..27cf6d5b412 --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityCreateTransition/validation/state/validateIdentityCreateTransitionStateFactory.js @@ -0,0 +1,50 @@ +const ValidationResult = require('../../../../../validation/ValidationResult'); + +const IdentityAlreadyExistsError = require('../../../../../errors/consensus/state/identity/IdentityAlreadyExistsError'); + +/** + * @param {StateRepository} stateRepository + * @return {validateIdentityCreateTransitionState} + */ +function validateIdentityCreateTransitionStateFactory( + stateRepository, +) { + /** + * + * Do we need to check that key ids are incremental? + * + * For later versions: + * 1. We need to check that outpoint exists (not now) + * 2. Verify ownership proof signature, as it requires special transaction to be implemented + */ + + /** + * @typedef {validateIdentityCreateTransitionState} + * @param {IdentityCreateTransition} stateTransition + * @return {ValidationResult} + */ + async function validateIdentityCreateTransitionState(stateTransition) { + const result = new ValidationResult(); + + // Check if identity with such id already exists + const executionContext = stateTransition.getExecutionContext(); + const identityId = stateTransition.getIdentityId(); + const identity = await stateRepository.fetchIdentity(identityId, executionContext); + + if (executionContext.isDryRun()) { + return result; + } + + if (identity) { + result.addError( + new IdentityAlreadyExistsError(identityId.toBuffer()), + ); + } + + return result; + } + + return validateIdentityCreateTransitionState; +} + +module.exports = validateIdentityCreateTransitionStateFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityTopUpTransition/IdentityTopUpTransition.js b/packages/js-dpp/lib/identity/stateTransition/IdentityTopUpTransition/IdentityTopUpTransition.js new file mode 100644 index 00000000000..219327b3e86 --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityTopUpTransition/IdentityTopUpTransition.js @@ -0,0 +1,146 @@ +const AbstractStateTransition = require('../../../stateTransition/AbstractStateTransition'); +const stateTransitionTypes = require('../../../stateTransition/stateTransitionTypes'); +const Identifier = require('../../../identifier/Identifier'); +const createAssetLockProofInstance = require('../assetLockProof/createAssetLockProofInstance'); + +class IdentityTopUpTransition extends AbstractStateTransition { + /** + * @param {RawIdentityTopUpTransition} rawStateTransition + */ + constructor(rawStateTransition) { + super(rawStateTransition); + + if (Object.prototype.hasOwnProperty.call(rawStateTransition, 'identityId')) { + this.setIdentityId(rawStateTransition.identityId); + } + + if (Object.prototype.hasOwnProperty.call(rawStateTransition, 'assetLockProof')) { + this.setAssetLockProof(createAssetLockProofInstance(rawStateTransition.assetLockProof)); + } + } + + /** + * Get State Transition type + * + * @return {number} + */ + getType() { + return stateTransitionTypes.IDENTITY_TOP_UP; + } + + /** + * Set Asset Lock + * + * @param {InstantAssetLockProof|ChainAssetLockProof} assetLockProof + * @return {IdentityTopUpTransition} + */ + setAssetLockProof(assetLockProof) { + this.assetLockProof = assetLockProof; + + return this; + } + + /** + * @return {InstantAssetLockProof|ChainAssetLockProof} + */ + getAssetLockProof() { + return this.assetLockProof; + } + + /** + * Returns base58 representation of the identity id top up + * + * @param {Buffer} identityId + * @return {IdentityTopUpTransition} + */ + setIdentityId(identityId) { + this.identityId = Identifier.from(identityId); + + return this; + } + + /** + * Returns identity id + * + * @return {Identifier} + */ + getIdentityId() { + return this.identityId; + } + + /** + * Returns Owner ID + * + * @return {Identifier} + */ + getOwnerId() { + return this.identityId; + } + + /** + * Get state transition as plain object + * + * @param {Object} [options] + * @param {boolean} [options.skipSignature=false] + * @param {boolean} [options.skipIdentifiersConversion=false] + * + * @return {RawIdentityTopUpTransition} + */ + toObject(options = {}) { + Object.assign( + options, + { + skipIdentifiersConversion: false, + ...options, + }, + ); + + const rawStateTransition = { + ...super.toObject(options), + identityId: this.getIdentityId(), + assetLockProof: this.getAssetLockProof().toObject(), + }; + + if (!options.skipIdentifiersConversion) { + rawStateTransition.identityId = this.getIdentityId().toBuffer(); + } + + return rawStateTransition; + } + + /** + * Get state transition as JSON + * + * @return {JsonIdentityTopUpTransition} + */ + toJSON() { + return { + ...super.toJSON(), + identityId: this.getIdentityId().toString(), + assetLockProof: this.getAssetLockProof().toJSON(), + }; + } + + /** + * Returns ids of topped up identities + * + * @return {Identifier[]} + */ + getModifiedDataIds() { + return [this.getIdentityId()]; + } +} + +/** + * @typedef {RawStateTransition & Object} RawIdentityTopUpTransition + * @property {RawInstantAssetLockProof|RawChainAssetLockProof} assetLockProof + * @property {Buffer} identityId + */ + +/** + * @typedef {JsonStateTransition & Object} JsonIdentityTopUpTransition + * @property {JsonInstantAssetLockProof|JsonChainAssetLockProof} assetLockProof + * @property {string} identityId + */ + +module.exports = IdentityTopUpTransition; diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityTopUpTransition/applyIdentityTopUpTransitionFactory.js b/packages/js-dpp/lib/identity/stateTransition/IdentityTopUpTransition/applyIdentityTopUpTransitionFactory.js new file mode 100644 index 00000000000..e52b04b86df --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityTopUpTransition/applyIdentityTopUpTransitionFactory.js @@ -0,0 +1,53 @@ +const { convertSatoshiToCredits } = require('../../creditsConverter'); +const getBiggestPossibleIdentity = require('../../getBiggestPossibleIdentity'); + +/** + * @param {StateRepository} stateRepository + * @param {fetchAssetLockTransactionOutput} fetchAssetLockTransactionOutput + * + * @returns {applyIdentityTopUpTransition} + */ +function applyIdentityTopUpTransitionFactory( + stateRepository, + fetchAssetLockTransactionOutput, +) { + /** + * Apply identity state transition + * + * @typedef applyIdentityTopUpTransition + * + * @param {IdentityTopUpTransition} stateTransition + * + * @return {Promise} + */ + async function applyIdentityTopUpTransition(stateTransition) { + const executionContext = stateTransition.getExecutionContext(); + + const output = await fetchAssetLockTransactionOutput( + stateTransition.getAssetLockProof(), + executionContext, + ); + + const outPoint = stateTransition.getAssetLockProof().getOutPoint(); + + const creditsAmount = convertSatoshiToCredits(output.satoshis); + + const identityId = stateTransition.getIdentityId(); + + let identity = await stateRepository.fetchIdentity(identityId, executionContext); + + if (executionContext.isDryRun()) { + identity = getBiggestPossibleIdentity(); + } + + identity.increaseBalance(creditsAmount); + + await stateRepository.updateIdentity(identity, executionContext); + + await stateRepository.markAssetLockTransactionOutPointAsUsed(outPoint, executionContext); + } + + return applyIdentityTopUpTransition; +} + +module.exports = applyIdentityTopUpTransitionFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityTopUpTransition/validation/basic/validateIdentityTopUpTransitionBasicFactory.js b/packages/js-dpp/lib/identity/stateTransition/IdentityTopUpTransition/validation/basic/validateIdentityTopUpTransitionBasicFactory.js new file mode 100644 index 00000000000..430bc40c314 --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityTopUpTransition/validation/basic/validateIdentityTopUpTransitionBasicFactory.js @@ -0,0 +1,60 @@ +const identityTopUpTransitionSchema = require('../../../../../../schema/identity/stateTransition/identityTopUp.json'); + +const convertBuffersToArrays = require('../../../../../util/convertBuffersToArrays'); + +/** + * @param {JsonSchemaValidator} jsonSchemaValidator + * @param {Object.} proofValidationFunctionsByType + * @param {validateProtocolVersion} validateProtocolVersion + * + * @return {validateIdentityTopUpTransitionBasic} + */ +function validateIdentityTopUpTransitionBasicFactory( + jsonSchemaValidator, + proofValidationFunctionsByType, + validateProtocolVersion, +) { + /** + * @typedef {validateIdentityTopUpTransitionBasic} + * @param {RawIdentityTopUpTransition} rawStateTransition + * @param {StateTransitionExecutionContext} executionContext + * @return {Promise} + */ + async function validateIdentityTopUpTransitionBasic(rawStateTransition, executionContext) { + const result = jsonSchemaValidator.validate( + identityTopUpTransitionSchema, + convertBuffersToArrays(rawStateTransition), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + validateProtocolVersion(rawStateTransition.protocolVersion), + ); + + if (!result.isValid()) { + return result; + } + + const proofValidationFunction = proofValidationFunctionsByType[ + rawStateTransition.assetLockProof.type + ]; + + const assetLockProofValidationResult = await proofValidationFunction( + rawStateTransition.assetLockProof, + executionContext, + ); + + result.merge( + assetLockProofValidationResult, + ); + + return result; + } + + return validateIdentityTopUpTransitionBasic; +} + +module.exports = validateIdentityTopUpTransitionBasicFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityTopUpTransition/validation/state/validateIdentityTopUpTransitionStateFactory.js b/packages/js-dpp/lib/identity/stateTransition/IdentityTopUpTransition/validation/state/validateIdentityTopUpTransitionStateFactory.js new file mode 100644 index 00000000000..6509d3d346d --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityTopUpTransition/validation/state/validateIdentityTopUpTransitionStateFactory.js @@ -0,0 +1,20 @@ +const ValidationResult = require('../../../../../validation/ValidationResult'); + +/** + * @return {validateIdentityTopUpTransitionState} + */ +function validateIdentityTopUpTransitionStateFactory() { + /** + * @typedef {validateIdentityTopUpTransitionState} + * @param {IdentityTopUpTransition} stateTransition + * @return {Promise} + */ + // eslint-disable-next-line no-unused-vars + async function validateIdentityTopUpTransitionState(stateTransition) { + return new ValidationResult(); + } + + return validateIdentityTopUpTransitionState; +} + +module.exports = validateIdentityTopUpTransitionStateFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/IdentityUpdateTransition.js b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/IdentityUpdateTransition.js new file mode 100644 index 00000000000..a5226729649 --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/IdentityUpdateTransition.js @@ -0,0 +1,274 @@ +const stateTransitionTypes = require('../../../stateTransition/stateTransitionTypes'); +const Identifier = require('../../../identifier/Identifier'); +const IdentityPublicKey = require('../../IdentityPublicKey'); +const AbstractStateTransitionIdentitySigned = require('../../../stateTransition/AbstractStateTransitionIdentitySigned'); + +class IdentityUpdateTransition extends AbstractStateTransitionIdentitySigned { + /** + * @param {RawIdentityUpdateTransition} rawStateTransition + */ + constructor(rawStateTransition) { + super(rawStateTransition); + + if (Object.prototype.hasOwnProperty.call(rawStateTransition, 'identityId')) { + this.setIdentityId(rawStateTransition.identityId); + } + + if (Object.prototype.hasOwnProperty.call(rawStateTransition, 'revision')) { + this.setRevision(rawStateTransition.revision); + } + + if (Object.prototype.hasOwnProperty.call(rawStateTransition, 'addPublicKeys')) { + this.setPublicKeysToAdd( + rawStateTransition.addPublicKeys + .map((rawPublicKey) => new IdentityPublicKey(rawPublicKey)), + ); + } + + if (Object.prototype.hasOwnProperty.call(rawStateTransition, 'disablePublicKeys')) { + this.setPublicKeyIdsToDisable( + rawStateTransition.disablePublicKeys, + ); + } + + if (Object.prototype.hasOwnProperty.call(rawStateTransition, 'publicKeysDisabledAt')) { + this.setPublicKeysDisabledAt(new Date(rawStateTransition.publicKeysDisabledAt)); + } + } + + /** + * Get State Transition type + * + * @return {number} + */ + getType() { + return stateTransitionTypes.IDENTITY_UPDATE; + } + + /** + * Returns base58 representation of the identity id top up + * + * @param {Buffer} identityId + * @return {IdentityUpdateTransition} + */ + setIdentityId(identityId) { + this.identityId = Identifier.from(identityId); + + return this; + } + + /** + * Returns identity id + * + * @return {Identifier} + */ + getIdentityId() { + return this.identityId; + } + + /** + * Get revision + * + * @return {number} + */ + getRevision() { + return this.revision; + } + + /** + * Set revision + * + * @param {number} revision + * @return {IdentityUpdateTransition} + */ + setRevision(revision) { + this.revision = revision; + + return this; + } + + /** + * Returns Owner ID + * + * @return {Identifier} + */ + getOwnerId() { + return this.identityId; + } + + /** + * Get public keys to add to the Identity. + * + * @returns {IdentityPublicKey[]} + */ + getPublicKeysToAdd() { + return this.addPublicKeys; + } + + /** + * Set public keys to add to the Identity. + * + * @param {IdentityPublicKey[]} publicKeys + * @returns {IdentityUpdateTransition} + */ + setPublicKeysToAdd(publicKeys) { + this.addPublicKeys = publicKeys; + + return this; + } + + /** + * + * Get Identity Public key IDs to disable for the Identity. + * + * @returns {number[]} + */ + getPublicKeyIdsToDisable() { + return this.disablePublicKeys; + } + + /** + * + * Set Identity Public key IDs to disable for the Identity. + * + * @param {number[]} publicKeyIds + * @returns {IdentityUpdateTransition} + */ + setPublicKeyIdsToDisable(publicKeyIds) { + this.disablePublicKeys = publicKeyIds; + + return this; + } + + /** + * Get timestamp when keys were disabled. + * + * @returns {Date} + */ + getPublicKeysDisabledAt() { + return this.publicKeysDisabledAt; + } + + /** + * Set timestamp when keys were disabled. + * + * @param {Date} publicKeysDisabledAt + * @returns {IdentityUpdateTransition} + */ + setPublicKeysDisabledAt(publicKeysDisabledAt) { + this.publicKeysDisabledAt = publicKeysDisabledAt; + + return this; + } + + /** + * Get state transition as plain object + * + * @param {Object} [options] + * @param {boolean} [options.skipSignature=false] + * @param {boolean} [options.skipIdentifiersConversion=false] + * + * @return {RawIdentityUpdateTransition} + */ + toObject(options = {}) { + Object.assign( + options, + { + skipIdentifiersConversion: false, + ...options, + }, + ); + + const rawStateTransition = { + ...super.toObject(options), + identityId: this.getIdentityId(), + revision: this.getRevision(), + }; + + if (this.getPublicKeysDisabledAt()) { + rawStateTransition.publicKeysDisabledAt = this.getPublicKeysDisabledAt().getTime(); + } + + if (this.getPublicKeysToAdd()) { + rawStateTransition.addPublicKeys = this.getPublicKeysToAdd() + .map((publicKey) => publicKey.toObject(options)); + } + + if (this.getPublicKeyIdsToDisable()) { + rawStateTransition.disablePublicKeys = this.getPublicKeyIdsToDisable(); + } + + if (!options.skipIdentifiersConversion) { + rawStateTransition.identityId = this.getIdentityId().toBuffer(); + } + + return rawStateTransition; + } + + /** + * Get state transition as JSON + * + * @return {JsonIdentityUpdateTransition} + */ + toJSON() { + const jsonStateTransition = { + ...super.toJSON(), + identityId: this.getIdentityId().toString(), + revision: this.getRevision(), + }; + + if (this.getPublicKeysDisabledAt()) { + jsonStateTransition.publicKeysDisabledAt = this.getPublicKeysDisabledAt().getTime(); + } + + if (this.getPublicKeysToAdd()) { + jsonStateTransition.addPublicKeys = this.getPublicKeysToAdd() + .map((publicKey) => publicKey.toJSON()); + } + + if (this.getPublicKeyIdsToDisable()) { + jsonStateTransition.disablePublicKeys = this.getPublicKeyIdsToDisable(); + } + + return jsonStateTransition; + } + + /** + * Returns ids of topped up identities + * + * @return {Identifier[]} + */ + getModifiedDataIds() { + return [this.getIdentityId()]; + } + + /** + * Returns minimal key security level that can be used to sign this ST + * + * @override + * @return {number} + */ + getKeySecurityLevelRequirement() { + return IdentityPublicKey.SECURITY_LEVELS.MASTER; + } +} + +/** + * @typedef {RawStateTransition & Object} RawIdentityUpdateTransition + * @property {Buffer} identityId + * @property {number} revision + * @property {IdentityPublicKey[]} [addPublicKeys] + * @property {number[]} [disablePublicKeys] + * @property {number} [publicKeysDisabledAt] + */ + +/** + * @typedef {JsonStateTransition & Object} JsonIdentityUpdateTransition + * @property {Buffer} identityId + * @property {number} revision + * @property {IdentityPublicKey[]} [addPublicKeys] + * @property {number[]} [disablePublicKeys] + * @property {number} [publicKeysDisabledAt] + */ + +module.exports = IdentityUpdateTransition; diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/applyIdentityUpdateTransitionFactory.js b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/applyIdentityUpdateTransitionFactory.js new file mode 100644 index 00000000000..dc9980c168b --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/applyIdentityUpdateTransitionFactory.js @@ -0,0 +1,75 @@ +/** + * @param {StateRepository} stateRepository + * + * @returns {applyIdentityUpdateTransition} + */ +const IdentityPublicKey = require('../../IdentityPublicKey'); +const getBiggestPossibleIdentity = require('../../getBiggestPossibleIdentity'); + +function applyIdentityUpdateTransitionFactory( + stateRepository, +) { + /** + * Apply identity state transition + * + * @typedef {applyIdentityUpdateTransition} + * @param {IdentityUpdateTransition} stateTransition + * @returns {Promise} + */ + async function applyIdentityUpdateTransition(stateTransition) { + const identityId = stateTransition.getIdentityId(); + const executionContext = stateTransition.getExecutionContext(); + + let identity = await stateRepository.fetchIdentity(identityId, executionContext); + + if (executionContext.isDryRun()) { + identity = getBiggestPossibleIdentity(); + } + + identity.setRevision(stateTransition.getRevision()); + + if (stateTransition.getPublicKeyIdsToDisable()) { + const identityPublicKeys = identity.getPublicKeys(); + + stateTransition.getPublicKeyIdsToDisable() + .forEach( + (id) => identity.getPublicKeyById(id) + .setDisabledAt(stateTransition.getPublicKeysDisabledAt().getTime()), + ); + + identity.setPublicKeys(identityPublicKeys); + } + + if (stateTransition.getPublicKeysToAdd()) { + const publicKeysToAdd = stateTransition.getPublicKeysToAdd() + .map((publicKey) => { + const rawPublicKey = publicKey.toObject({ skipSignature: true }); + + return new IdentityPublicKey(rawPublicKey); + }); + + // Add public keys to identity + const identityPublicKeys = identity + .getPublicKeys() + .concat(publicKeysToAdd); + + identity.setPublicKeys(identityPublicKeys); + + const publicKeyHashes = stateTransition + .getPublicKeysToAdd() + .map((publicKey) => publicKey.hash()); + + await stateRepository.storeIdentityPublicKeyHashes( + identity.getId(), + publicKeyHashes, + executionContext, + ); + } + + await stateRepository.updateIdentity(identity, executionContext); + } + + return applyIdentityUpdateTransition; +} + +module.exports = applyIdentityUpdateTransitionFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/basic/validateIdentityUpdateTransitionBasicFactory.js b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/basic/validateIdentityUpdateTransitionBasicFactory.js new file mode 100644 index 00000000000..4848de8788c --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/basic/validateIdentityUpdateTransitionBasicFactory.js @@ -0,0 +1,67 @@ +const identityUpdateTransitionSchema = require('../../../../../../schema/identity/stateTransition/identityUpdate.json'); +const convertBuffersToArrays = require('../../../../../util/convertBuffersToArrays'); + +/** + * @param {JsonSchemaValidator} jsonSchemaValidator + * @param {validateProtocolVersion} validateProtocolVersion + * @param {validatePublicKeys} validatePublicKeys + * @param {validatePublicKeySignatures} validatePublicKeySignatures + * + * @return {validateIdentityUpdateTransitionBasic} + */ +function validateIdentityUpdateTransitionBasicFactory( + jsonSchemaValidator, + validateProtocolVersion, + validatePublicKeys, + validatePublicKeySignatures, +) { + /** + * @typedef validateIdentityUpdateTransitionBasic + * @param {RawIdentityUpdateTransition} rawStateTransition + * @param {StateTransitionExecutionContext} executionContext + * @return {Promise} + */ + // eslint-disable-next-line no-unused-vars + async function validateIdentityUpdateTransitionBasic(rawStateTransition, executionContext) { + const result = jsonSchemaValidator.validate( + identityUpdateTransitionSchema, + convertBuffersToArrays(rawStateTransition), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + validateProtocolVersion(rawStateTransition.protocolVersion), + ); + + if (!result.isValid()) { + return result; + } + + if (rawStateTransition.addPublicKeys) { + result.merge( + validatePublicKeys(rawStateTransition.addPublicKeys), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + await validatePublicKeySignatures( + rawStateTransition, + rawStateTransition.addPublicKeys, + executionContext, + ), + ); + } + + return result; + } + + return validateIdentityUpdateTransitionBasic; +} + +module.exports = validateIdentityUpdateTransitionBasicFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validateIdentityUpdateTransitionStateFactory.js b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validateIdentityUpdateTransitionStateFactory.js new file mode 100644 index 00000000000..64e894786dd --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validateIdentityUpdateTransitionStateFactory.js @@ -0,0 +1,137 @@ +const ValidationResult = require('../../../../../validation/ValidationResult'); +const InvalidIdentityRevisionError = require('../../../../../errors/consensus/state/identity/InvalidIdentityRevisionError'); +const IdentityPublicKeyIsReadOnlyError = require('../../../../../errors/consensus/state/identity/IdentityPublicKeyIsReadOnlyError'); +const InvalidIdentityPublicKeyIdError = require('../../../../../errors/consensus/state/identity/InvalidIdentityPublicKeyIdError'); +const Identity = require('../../../../Identity'); +const IdentityPublicKeyDisabledAtWindowViolationError = require('../../../../../errors/consensus/state/identity/IdentityPublicKeyDisabledAtWindowViolationError'); +const validateTimeInBlockTimeWindow = require('../../../../../blockTimeWindow/validateTimeInBlockTimeWindow'); + +/** + * @param {StateRepository} stateRepository + * @param {validatePublicKeys} validatePublicKeys + * @param {validateRequiredPurposeAndSecurityLevel} validateRequiredPurposeAndSecurityLevel + * @return {validateIdentityUpdateTransitionState} + */ +function validateIdentityUpdateTransitionStateFactory( + stateRepository, + validatePublicKeys, + validateRequiredPurposeAndSecurityLevel, +) { + /** + * @typedef {validateIdentityUpdateTransitionState} + * @param {IdentityUpdateTransition} stateTransition + * @return {Promise} + */ + // eslint-disable-next-line no-unused-vars + async function validateIdentityUpdateTransitionState(stateTransition) { + const result = new ValidationResult(); + + const executionContext = stateTransition.getExecutionContext(); + const identityId = stateTransition.getIdentityId(); + + const storedIdentity = await stateRepository.fetchIdentity(identityId, executionContext); + + if (executionContext.isDryRun()) { + return result; + } + + // copy identity + const identity = new Identity(storedIdentity.toObject()); + + // Check revision + if (identity.getRevision() !== stateTransition.getRevision() - 1) { + result.addError( + new InvalidIdentityRevisionError(identityId.toBuffer(), identity.getRevision()), + ); + + return result; + } + + const publicKeyIdsToDisable = stateTransition.getPublicKeyIdsToDisable(); + + if (publicKeyIdsToDisable) { + publicKeyIdsToDisable.forEach((id) => { + if (!identity.getPublicKeyById(id)) { + result.addError( + new InvalidIdentityPublicKeyIdError(id), + ); + } else if (identity.getPublicKeyById(id).isReadOnly()) { + result.addError( + new IdentityPublicKeyIsReadOnlyError(id), + ); + } + }); + + if (!result.isValid()) { + return result; + } + + // Keys can only be disabled if another valid key is enabled in the same security level + publicKeyIdsToDisable.forEach( + (id) => identity.getPublicKeyById(id) + .setDisabledAt(stateTransition.getPublicKeysDisabledAt().getTime()), + ); + + // Calculate time window for timestamps + const { + time: { + seconds: lastBlockHeaderTimeSeconds, + }, + } = await stateRepository.fetchLatestPlatformBlockHeader(); + + // Get last block header time in milliseconds + const lastBlockHeaderTime = lastBlockHeaderTimeSeconds * 1000; + + const disabledAtTime = stateTransition.getPublicKeysDisabledAt(); + + const validateTimeWindowResult = validateTimeInBlockTimeWindow( + lastBlockHeaderTime, + disabledAtTime.getTime(), + ); + + if (!validateTimeWindowResult.isValid()) { + result.addError( + new IdentityPublicKeyDisabledAtWindowViolationError( + disabledAtTime, + validateTimeWindowResult.getTimeWindowStart(), + validateTimeWindowResult.getTimeWindowEnd(), + ), + ); + + return result; + } + } + + const publicKeysToAdd = stateTransition.getPublicKeysToAdd(); + if (publicKeysToAdd) { + const identityPublicKeys = identity.getPublicKeys(); + + publicKeysToAdd.forEach((pk) => identityPublicKeys.push(pk)); + + identity.setPublicKeys(identityPublicKeys); + + // validate new fields with existing once to make sure that keys are unique and so on + result.merge( + validatePublicKeys( + identity.getPublicKeys().map((pk) => pk.toObject()), + ), + ); + + if (!result.isValid()) { + return result; + } + } + + const rawPublicKeys = identity.getPublicKeys().map((pk) => pk.toObject()); + + result.merge( + validateRequiredPurposeAndSecurityLevel(rawPublicKeys), + ); + + return result; + } + + return validateIdentityUpdateTransitionState; +} + +module.exports = validateIdentityUpdateTransitionStateFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.js b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.js new file mode 100644 index 00000000000..3416355f2a9 --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.js @@ -0,0 +1,78 @@ +const ValidationResult = require('../../../../../validation/ValidationResult'); + +const identitySchema = require('../../../../../../schema/identity/identity.json'); + +const DuplicatedIdentityPublicKeyError = require( + '../../../../../errors/consensus/state/identity/DuplicatedIdentityPublicKeyError', +); +const DuplicatedIdentityPublicKeyIdError = require( + '../../../../../errors/consensus/state/identity/DuplicatedIdentityPublicKeyIdError', +); + +const MaxIdentityPublicKeyLimitReachedError = require( + '../../../../../errors/consensus/state/identity/MaxIdentityPublicKeyLimitReachedError', +); + +/** + * Validate public keys + * + * @typedef validatePublicKeysState + * + * @param {RawIdentityPublicKey[]} rawPublicKeys + * + * @return {ValidationResult} + */ +function validatePublicKeysState(rawPublicKeys) { + const result = new ValidationResult(); + + if (rawPublicKeys.length > identitySchema.properties.publicKeys.maxItems) { + result.addError( + new MaxIdentityPublicKeyLimitReachedError(identitySchema.properties.publicKeys.maxItems), + ); + + return result; + } + + // Check that there's no duplicated key ids in the state transition + const duplicatedIds = []; + const idsCount = {}; + + rawPublicKeys.forEach((rawPublicKey) => { + idsCount[rawPublicKey.id] = !idsCount[rawPublicKey.id] ? 1 : idsCount[rawPublicKey.id] + 1; + if (idsCount[rawPublicKey.id] > 1) { + duplicatedIds.push(rawPublicKey.id); + } + }); + + if (duplicatedIds.length > 0) { + result.addError( + new DuplicatedIdentityPublicKeyIdError(duplicatedIds), + ); + } + + // Check that there's no duplicated keys + const keysCount = {}; + const duplicatedKeyIds = []; + rawPublicKeys + .filter((rawPublicKey) => rawPublicKey.disabledAt === undefined) + .forEach((rawPublicKey) => { + const dataHex = rawPublicKey.data.toString('hex'); + + keysCount[dataHex] = !keysCount[dataHex] + ? 1 : keysCount[dataHex] + 1; + + if (keysCount[dataHex] > 1) { + duplicatedKeyIds.push(rawPublicKey.id); + } + }); + + if (duplicatedKeyIds.length > 0) { + result.addError( + new DuplicatedIdentityPublicKeyError(duplicatedKeyIds), + ); + } + + return result; +} + +module.exports = validatePublicKeysState; diff --git a/packages/js-dpp/lib/identity/stateTransition/assetLockProof/chain/ChainAssetLockProof.js b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/chain/ChainAssetLockProof.js new file mode 100644 index 00000000000..97e4dd3c663 --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/chain/ChainAssetLockProof.js @@ -0,0 +1,96 @@ +const Identifier = require('../../../../identifier/Identifier'); +const hashModule = require('../../../../util/hash'); + +class ChainAssetLockProof { + /** + * @param {RawChainAssetLockProof} rawAssetLockProof + */ + constructor(rawAssetLockProof) { + this.coreChainLockedHeight = rawAssetLockProof.coreChainLockedHeight; + this.outPoint = rawAssetLockProof.outPoint; + } + + /** + * Get proof type + * + * @returns {number} + */ + getType() { + return ChainAssetLockProof.type; + } + + /** + * Get Asset Lock proof core height + * + * @returns {number} + */ + getCoreChainLockedHeight() { + return this.coreChainLockedHeight; + } + + /** + * Get outPoint + * + * @return {Buffer} + */ + getOutPoint() { + return this.outPoint; + } + + /** + * Create identifier + * + * @returns {Identifier} + */ + createIdentifier() { + const { hash } = hashModule; + + return new Identifier( + hash(this.getOutPoint()), + ); + } + + /** + * Get plain object representation + * + * @returns {RawChainAssetLockProof} + */ + toObject() { + return { + type: this.getType(), + coreChainLockedHeight: this.getCoreChainLockedHeight(), + outPoint: this.getOutPoint(), + }; + } + + /** + * Get JSON representation + * + * @returns {JsonChainAssetLockProof} + */ + toJSON() { + return { + type: this.getType(), + coreChainLockedHeight: this.getCoreChainLockedHeight(), + outPoint: this.getOutPoint().toString('base64'), + }; + } +} + +/** + * @typedef {Object} RawChainAssetLockProof + * @property {number} type + * @property {number} coreChainLockedHeight + * @property {Buffer} outPoint + */ + +/** + * @typedef {Object} JsonChainAssetLockProof + * @property {number} type + * @property {number} coreChainLockedHeight + * @property {string} outPoint + */ + +ChainAssetLockProof.type = 1; + +module.exports = ChainAssetLockProof; diff --git a/packages/js-dpp/lib/identity/stateTransition/assetLockProof/chain/validateChainAssetLockProofStructureFactory.js b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/chain/validateChainAssetLockProofStructureFactory.js new file mode 100644 index 00000000000..a3ad697d453 --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/chain/validateChainAssetLockProofStructureFactory.js @@ -0,0 +1,110 @@ +const { Transaction } = require('@dashevo/dashcore-lib'); +const chainAssetLockProofSchema = require('../../../../../schema/identity/stateTransition/assetLockProof/chainAssetLockProof.json'); + +const convertBuffersToArrays = require('../../../../util/convertBuffersToArrays'); +const InvalidAssetLockProofCoreChainHeightError = require('../../../../errors/consensus/basic/identity/InvalidAssetLockProofCoreChainHeightError'); +const IdentityAssetLockTransactionIsNotFoundError = require('../../../../errors/consensus/basic/identity/IdentityAssetLockTransactionIsNotFoundError'); +const InvalidAssetLockProofTransactionHeightError = require('../../../../errors/consensus/basic/identity/InvalidAssetLockProofTransactionHeightError'); + +/** + * @param {JsonSchemaValidator} jsonSchemaValidator + * @param {StateRepository} stateRepository + * @param {validateAssetLockTransaction} validateAssetLockTransaction + * @returns {validateChainAssetLockProofStructure} + */ +function validateChainAssetLockProofStructureFactory( + jsonSchemaValidator, + stateRepository, + validateAssetLockTransaction, +) { + /** + * @typedef {validateChainAssetLockProofStructure} + * @param {RawChainAssetLockProof} rawAssetLockProof + * @param {StateTransitionExecutionContext} executionContext + * @returns {ValidationResult} + */ + async function validateChainAssetLockProofStructure( + rawAssetLockProof, + executionContext, + ) { + const result = jsonSchemaValidator.validate( + chainAssetLockProofSchema, + convertBuffersToArrays(rawAssetLockProof), + ); + + if (!result.isValid()) { + return result; + } + + const { + coreChainLockedHeight: proofCoreChainLockedHeight, + outPoint: outPointBuffer, + } = rawAssetLockProof; + + const latestPlatformBlockHeader = await stateRepository.fetchLatestPlatformBlockHeader(); + + const { coreChainLockedHeight: currentCoreChainLockedHeight } = latestPlatformBlockHeader; + + if (currentCoreChainLockedHeight < proofCoreChainLockedHeight) { + result.addError( + new InvalidAssetLockProofCoreChainHeightError( + proofCoreChainLockedHeight, + currentCoreChainLockedHeight, + ), + ); + + return result; + } + + const outPoint = Transaction.parseOutPointBuffer(outPointBuffer); + const { outputIndex, transactionHash } = outPoint; + + const rawTransaction = await stateRepository.fetchTransaction( + transactionHash, + executionContext, + ); + + if (rawTransaction === null) { + result.addError( + new IdentityAssetLockTransactionIsNotFoundError( + Buffer.from(transactionHash, 'hex'), + ), + ); + + return result; + } + + if (!rawTransaction.height || proofCoreChainLockedHeight < rawTransaction.height) { + result.addError( + new InvalidAssetLockProofTransactionHeightError( + proofCoreChainLockedHeight, + rawTransaction.height, + ), + ); + + return result; + } + + const validateAssetLockTransactionResult = await validateAssetLockTransaction( + rawTransaction.data, + outputIndex, + executionContext, + ); + + result.merge(validateAssetLockTransactionResult); + + if (!result.isValid()) { + return result; + } + + const { publicKeyHash } = validateAssetLockTransactionResult.getData(); + + result.setData(publicKeyHash); + + return result; + } + + return validateChainAssetLockProofStructure; +} + +module.exports = validateChainAssetLockProofStructureFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/assetLockProof/createAssetLockProofInstance.js b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/createAssetLockProofInstance.js new file mode 100644 index 00000000000..6978efc6635 --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/createAssetLockProofInstance.js @@ -0,0 +1,18 @@ +const InstantAssetLockProof = require('./instant/InstantAssetLockProof'); +const ChainAssetLockProof = require('./chain/ChainAssetLockProof'); + +/** + * + * @param {RawInstantAssetLockProof|RawChainAssetLockProof} rawAssetLockProof + * @returns {InstantAssetLockProof|ChainAssetLockProof} + */ +function createAssetLockProofInstance(rawAssetLockProof) { + const assetLockProofByTypes = { + [InstantAssetLockProof.type]: InstantAssetLockProof, + [ChainAssetLockProof.type]: ChainAssetLockProof, + }; + + return new assetLockProofByTypes[rawAssetLockProof.type](rawAssetLockProof); +} + +module.exports = createAssetLockProofInstance; diff --git a/packages/js-dpp/lib/identity/stateTransition/assetLockProof/fetchAssetLockPublicKeyHashFactory.js b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/fetchAssetLockPublicKeyHashFactory.js new file mode 100644 index 00000000000..793e3a1d6ad --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/fetchAssetLockPublicKeyHashFactory.js @@ -0,0 +1,27 @@ +const AssetLockOutputNotFoundError = require('../../errors/AssetLockOutputNotFoundError'); + +/** + * @param {fetchAssetLockTransactionOutput} fetchAssetLockTransactionOutput + * @return {fetchAssetLockPublicKeyHash} + */ +function fetchAssetLockPublicKeyHashFactory(fetchAssetLockTransactionOutput) { + /** + * @typedef {fetchAssetLockPublicKeyHash} + * @param {InstantAssetLockProof|ChainAssetLockProof} assetLockProof + * @param {StateTransitionExecutionContext} executionContext + * @return {Promise} + */ + async function fetchAssetLockPublicKeyHash(assetLockProof, executionContext) { + const output = await fetchAssetLockTransactionOutput(assetLockProof, executionContext); + + if (!output) { + throw new AssetLockOutputNotFoundError(); + } + + return output.script.getData(); + } + + return fetchAssetLockPublicKeyHash; +} + +module.exports = fetchAssetLockPublicKeyHashFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/assetLockProof/fetchAssetLockTransactionOutputFactory.js b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/fetchAssetLockTransactionOutputFactory.js new file mode 100644 index 00000000000..76d8a5b448e --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/fetchAssetLockTransactionOutputFactory.js @@ -0,0 +1,60 @@ +const { Transaction, Script } = require('@dashevo/dashcore-lib'); +const Output = require('@dashevo/dashcore-lib/lib/transaction/output'); +const InstantAssetLockProof = require('./instant/InstantAssetLockProof'); +const ChainAssetLockProof = require('./chain/ChainAssetLockProof'); +const AssetLockTransactionIsNotFoundError = require('../../errors/AssetLockTransactionIsNotFoundError'); +const UnknownAssetLockProofTypeError = require('../../errors/UnknownAssetLockProofTypeError'); + +/** + * @param {StateRepository} stateRepository + * + * @returns {fetchAssetLockTransactionOutput} + */ + +function fetchAssetLockTransactionOutputFactory( + stateRepository, +) { + /** + * + * @typedef fetchAssetLockTransactionOutput + * @param {InstantAssetLockProof|ChainAssetLockProof} assetLockProof + * @param {StateTransitionExecutionContext} executionContext + * @returns {Promise} + */ + async function fetchAssetLockTransactionOutput(assetLockProof, executionContext) { + if (assetLockProof.getType() === InstantAssetLockProof.type) { + return assetLockProof.getOutput(); + } + + if (assetLockProof.getType() === ChainAssetLockProof.type) { + const outPoint = Transaction.parseOutPointBuffer(assetLockProof.getOutPoint()); + + const { outputIndex, transactionHash } = outPoint; + + const rawTransaction = await stateRepository.fetchTransaction( + transactionHash, + executionContext, + ); + + if (executionContext.isDryRun()) { + return new Output({ + satoshis: 1000, + script: new Script(), + }); + } + + if (rawTransaction === null) { + throw new AssetLockTransactionIsNotFoundError(transactionHash); + } + + const transaction = new Transaction(rawTransaction.data); + return transaction.outputs[outputIndex]; + } + + throw new UnknownAssetLockProofTypeError(assetLockProof.getType()); + } + + return fetchAssetLockTransactionOutput; +} + +module.exports = fetchAssetLockTransactionOutputFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/assetLockProof/instant/InstantAssetLockProof.js b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/instant/InstantAssetLockProof.js new file mode 100644 index 00000000000..18a78f95171 --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/instant/InstantAssetLockProof.js @@ -0,0 +1,128 @@ +const { InstantLock, Transaction } = require('@dashevo/dashcore-lib'); +const hashModule = require('../../../../util/hash'); +const Identifier = require('../../../../identifier/Identifier'); + +class InstantAssetLockProof { + /** + * @param {RawInstantAssetLockProof} rawAssetLockProof + */ + constructor(rawAssetLockProof) { + this.instantLock = InstantLock.fromBuffer(rawAssetLockProof.instantLock); + this.transaction = new Transaction(rawAssetLockProof.transaction); + this.outputIndex = rawAssetLockProof.outputIndex; + } + + /** + * Get proof type + * + * @returns {number} + */ + getType() { + return InstantAssetLockProof.type; + } + + /** + * Get asset lock transaction output index + * + * @returns {number} + */ + getOutputIndex() { + return this.outputIndex; + } + + /** + * Get transaction outPoint + * @return {Buffer} + */ + getOutPoint() { + return this.transaction.getOutPointBuffer(this.getOutputIndex()); + } + + /** + * Get transaction output + * + * @returns {Output} + */ + getOutput() { + return this.transaction.outputs[this.getOutputIndex()]; + } + + /** + * Create identifier + * + * @returns {Identifier} + */ + createIdentifier() { + const { hash } = hashModule; + + return new Identifier( + hash(this.getTransaction().getOutPointBuffer(this.getOutputIndex())), + ); + } + + /** + * Get Instant Lock + * + * @returns {InstantLock} + */ + getInstantLock() { + return this.instantLock; + } + + /** + * Get asset lock transaction + * + * @returns {Transaction} + */ + getTransaction() { + return this.transaction; + } + + /** + * Get plain object representation + * + * @returns {RawInstantAssetLockProof} + */ + toObject() { + return { + type: this.getType(), + instantLock: this.getInstantLock().toBuffer(), + transaction: this.getTransaction().toBuffer(), + outputIndex: this.getOutputIndex(), + }; + } + + /** + * Get JSON representation + * + * @returns {JsonInstantAssetLockProof} + */ + toJSON() { + return { + type: this.getType(), + instantLock: this.getInstantLock().toBuffer().toString('base64'), + transaction: this.getTransaction().toString('base64'), + outputIndex: this.getOutputIndex(), + }; + } +} + +/** + * @typedef {Object} RawInstantAssetLockProof + * @property {number} type + * @property {Buffer} instantLock + * @property {Buffer} transaction + * @property {number} outputIndex + */ + +/** + * @typedef {Object} JsonInstantAssetLockProof + * @property {number} type + * @property {string} instantLock + * @property {string} transaction + * @property {number} outputIndex + */ + +InstantAssetLockProof.type = 0; + +module.exports = InstantAssetLockProof; diff --git a/packages/js-dpp/lib/identity/stateTransition/assetLockProof/instant/validateInstantAssetLockProofStructureFactory.js b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/instant/validateInstantAssetLockProofStructureFactory.js new file mode 100644 index 00000000000..0c20cf69b70 --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/instant/validateInstantAssetLockProofStructureFactory.js @@ -0,0 +1,101 @@ +const DashCoreLib = require('@dashevo/dashcore-lib'); + +const instantAssetLockProofSchema = require('../../../../../schema/identity/stateTransition/assetLockProof/instantAssetLockProof.json'); + +const convertBuffersToArrays = require('../../../../util/convertBuffersToArrays'); + +const InvalidInstantAssetLockProofError = require('../../../../errors/consensus/basic/identity/InvalidInstantAssetLockProofError'); +const IdentityAssetLockProofLockedTransactionMismatchError = require('../../../../errors/consensus/basic/identity/IdentityAssetLockProofLockedTransactionMismatchError'); +const InvalidInstantAssetLockProofSignatureError = require('../../../../errors/consensus/basic/identity/InvalidInstantAssetLockProofSignatureError'); + +/** + * @param {JsonSchemaValidator} jsonSchemaValidator + * @param {StateRepository} stateRepository + * @param {validateAssetLockTransaction} validateAssetLockTransaction + * @returns {validateInstantAssetLockProofStructure} + */ +function validateInstantAssetLockProofStructureFactory( + jsonSchemaValidator, + stateRepository, + validateAssetLockTransaction, +) { + /** + * @typedef {validateInstantAssetLockProofStructure} + * @param {RawInstantAssetLockProof} rawAssetLockProof + * @param {StateTransitionExecutionContext} executionContext + * @returns {Promise} + */ + async function validateInstantAssetLockProofStructure( + rawAssetLockProof, + executionContext, + ) { + const result = jsonSchemaValidator.validate( + instantAssetLockProofSchema, + convertBuffersToArrays(rawAssetLockProof), + ); + + if (!result.isValid()) { + return result; + } + + const { InstantLock } = DashCoreLib; + + let instantLock; + try { + instantLock = InstantLock.fromBuffer(rawAssetLockProof.instantLock); + } catch (e) { + const error = new InvalidInstantAssetLockProofError(e.message); + + error.setValidationError(e); + + result.addError(error); + + return result; + } + + const isValid = await stateRepository.verifyInstantLock(instantLock, executionContext); + + if (!isValid) { + result.addError(new InvalidInstantAssetLockProofSignatureError()); + + return result; + } + + const validateAssetLockTransactionResult = await validateAssetLockTransaction( + rawAssetLockProof.transaction, + rawAssetLockProof.outputIndex, + executionContext, + ); + + result.merge(validateAssetLockTransactionResult); + + if (!result.isValid()) { + return result; + } + + /** + * @typedef {Transaction} transaction + * @typedef {Buffer} publicKeyHash + */ + const { publicKeyHash, transaction } = validateAssetLockTransactionResult.getData(); + + if (instantLock.txid !== transaction.id) { + result.addError( + new IdentityAssetLockProofLockedTransactionMismatchError( + Buffer.from(instantLock.txid, 'hex'), + Buffer.from(transaction.id, 'hex'), + ), + ); + + return result; + } + + result.setData(publicKeyHash); + + return result; + } + + return validateInstantAssetLockProofStructure; +} + +module.exports = validateInstantAssetLockProofStructureFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/assetLockProof/validateAssetLockTransactionFactory.js b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/validateAssetLockTransactionFactory.js new file mode 100644 index 00000000000..463a83f3512 --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/assetLockProof/validateAssetLockTransactionFactory.js @@ -0,0 +1,99 @@ +const DashCoreLib = require('@dashevo/dashcore-lib'); + +const InvalidIdentityAssetLockTransactionError = require('../../../errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionError'); +const IdentityAssetLockTransactionOutputNotFoundError = require('../../../errors/consensus/basic/identity/IdentityAssetLockTransactionOutputNotFoundError'); +const InvalidIdentityAssetLockTransactionOutputError = require('../../../errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionOutputError'); +const ValidationResult = require('../../../validation/ValidationResult'); +const IdentityAssetLockTransactionOutPointAlreadyExistsError = require('../../../errors/consensus/basic/identity/IdentityAssetLockTransactionOutPointAlreadyExistsError'); +const InvalidAssetLockTransactionOutputReturnSizeError = require('../../../errors/consensus/basic/identity/InvalidAssetLockTransactionOutputReturnSizeError'); + +/** + * + * @param {StateRepository} stateRepository + * @returns {validateAssetLockTransaction} + */ +function validateAssetLockTransactionFactory(stateRepository) { + /** + * @typedef validateAssetLockTransaction + * @param {Buffer} rawTransaction + * @param {number} outputIndex + * @param {StateTransitionExecutionContext} executionContext + * @returns {Promise} + */ + async function validateAssetLockTransaction(rawTransaction, outputIndex, executionContext) { + const result = new ValidationResult(); + const { Transaction } = DashCoreLib; + + /** + * @type {Transaction} + */ + let transaction; + try { + transaction = new Transaction(rawTransaction); + } catch (error) { + const consensusError = new InvalidIdentityAssetLockTransactionError(error.message); + + consensusError.setValidationError(error); + + result.addError(consensusError); + + return result; + } + + const output = transaction.outputs[outputIndex]; + + if (!output) { + result.addError( + new IdentityAssetLockTransactionOutputNotFoundError(outputIndex), + ); + + return result; + } + + if (!output.script.isDataOut()) { + result.addError( + new InvalidIdentityAssetLockTransactionOutputError(outputIndex), + ); + + return result; + } + + const publicKeyHash = output.script.getData(); + + if (publicKeyHash.length !== 20) { + result.addError( + new InvalidAssetLockTransactionOutputReturnSizeError(outputIndex), + ); + + return result; + } + + const outPointBuffer = transaction.getOutPointBuffer(outputIndex); + const outPointIsUsed = await stateRepository.isAssetLockTransactionOutPointAlreadyUsed( + outPointBuffer, + executionContext, + ); + + if (outPointIsUsed) { + result.addError( + new IdentityAssetLockTransactionOutPointAlreadyExistsError( + Buffer.from(transaction.id, 'hex'), + outputIndex, + ), + ); + + return result; + } + + result.setData({ + publicKeyHash, + transaction, + }); + + return result; + } + + return validateAssetLockTransaction; +} + +module.exports = validateAssetLockTransactionFactory; diff --git a/packages/js-dpp/lib/identity/stateTransition/validatePublicKeySignaturesFactory.js b/packages/js-dpp/lib/identity/stateTransition/validatePublicKeySignaturesFactory.js new file mode 100644 index 00000000000..da529ae7d2d --- /dev/null +++ b/packages/js-dpp/lib/identity/stateTransition/validatePublicKeySignaturesFactory.js @@ -0,0 +1,79 @@ +/** + * + * @param {createStateTransition} createStateTransition + * @returns {validatePublicKeySignatures} + */ +const ValidationResult = require('../../validation/ValidationResult'); +const InvalidIdentityKeySignatureError = require('../../errors/consensus/basic/identity/InvalidIdentityKeySignatureError'); +const SignatureVerificationOperation = require('../../stateTransition/fee/operations/SignatureVerificationOperation'); + +/** + * @param {IdentityCreateTransition|IdentityUpdateTransition} stateTransition + * @param {RawIdentityPublicKey[]} rawPublicKeys + * @param {StateTransitionExecutionContext} executionContext + * @param {number} [i=0] + * @returns {Promise} + */ +async function verifyPublicKeysSequentially( + stateTransition, + rawPublicKeys, + executionContext, + i = 0, +) { + const rawPublicKey = rawPublicKeys[i]; + + stateTransition.setSignature(rawPublicKey.signature); + + const operation = new SignatureVerificationOperation(rawPublicKey.type); + + executionContext.addOperation(operation); + + const result = await stateTransition.verifyByPublicKey( + rawPublicKey.data, + rawPublicKey.type, + ); + + if (!result) { + return rawPublicKey; + } + + // eslint-disable-next-line no-param-reassign + if (rawPublicKeys.length > ++i) { + return verifyPublicKeysSequentially(stateTransition, rawPublicKeys, executionContext, i); + } + + return undefined; +} + +function validatePublicKeySignaturesFactory(createStateTransition) { + /** + * @typedef {validatePublicKeySignatures} + * @param {RawStateTransition} rawStateTransition + * @param {RawIdentityPublicKey[]} rawPublicKeys + * @param {StateTransitionExecutionContext} executionContext + * @returns {Promise} + */ + async function validatePublicKeySignatures(rawStateTransition, rawPublicKeys, executionContext) { + const stateTransition = await createStateTransition(rawStateTransition); + + const result = new ValidationResult(); + + const invalidRawPublicKey = await verifyPublicKeysSequentially( + stateTransition, + rawPublicKeys, + executionContext, + ); + + if (invalidRawPublicKey) { + result.addError( + new InvalidIdentityKeySignatureError(invalidRawPublicKey.id), + ); + } + + return result; + } + + return validatePublicKeySignatures; +} + +module.exports = validatePublicKeySignaturesFactory; diff --git a/packages/js-dpp/lib/identity/validation/validateIdentityFactory.js b/packages/js-dpp/lib/identity/validation/validateIdentityFactory.js new file mode 100644 index 00000000000..8223ecb8c18 --- /dev/null +++ b/packages/js-dpp/lib/identity/validation/validateIdentityFactory.js @@ -0,0 +1,52 @@ +const identitySchema = require('../../../schema/identity/identity.json'); + +const convertBuffersToArrays = require('../../util/convertBuffersToArrays'); + +/** + * @param {JsonSchemaValidator} validator + * @param {validatePublicKeys} validatePublicKeys + * @param {validateProtocolVersion} validateProtocolVersion + * + * @return {validateIdentity} + */ +function validateIdentityFactory( + validator, + validatePublicKeys, + validateProtocolVersion, +) { + /** + * Validates identity + * + * @typedef validateIdentity + * @param {RawIdentity} rawIdentity + * @return {ValidationResult} + */ + function validateIdentity(rawIdentity) { + const result = validator.validate( + identitySchema, + convertBuffersToArrays(rawIdentity), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + validateProtocolVersion(rawIdentity.protocolVersion), + ); + + if (!result.isValid()) { + return result; + } + + result.merge( + validatePublicKeys(rawIdentity.publicKeys), + ); + + return result; + } + + return validateIdentity; +} + +module.exports = validateIdentityFactory; diff --git a/packages/js-dpp/lib/identity/validation/validatePublicKeysFactory.js b/packages/js-dpp/lib/identity/validation/validatePublicKeysFactory.js new file mode 100644 index 00000000000..17b87ee3912 --- /dev/null +++ b/packages/js-dpp/lib/identity/validation/validatePublicKeysFactory.js @@ -0,0 +1,165 @@ +const { PublicKey } = require('@dashevo/dashcore-lib'); + +const ValidationResult = require('../../validation/ValidationResult'); + +const convertBuffersToArrays = require('../../util/convertBuffersToArrays'); + +const InvalidIdentityPublicKeyDataError = require( + '../../errors/consensus/basic/identity/InvalidIdentityPublicKeyDataError', +); + +const DuplicatedIdentityPublicKeyError = require( + '../../errors/consensus/basic/identity/DuplicatedIdentityPublicKeyError', +); +const DuplicatedIdentityPublicKeyIdError = require( + '../../errors/consensus/basic/identity/DuplicatedIdentityPublicKeyIdError', +); + +const InvalidIdentityPublicKeySecurityLevelError = require( + '../../errors/consensus/basic/identity/InvalidIdentityPublicKeySecurityLevelError', +); + +const IdentityPublicKey = require('../IdentityPublicKey'); + +/** + * Validate public keys (factory) + * + * @param {JsonSchemaValidator} validator + * @param {Object} jsonSchema + * @param {BlsSignatures} bls + * + * @return {validatePublicKeys} + */ +function validatePublicKeysFactory(validator, jsonSchema, bls) { + /** + * Validate public keys + * + * @typedef validatePublicKeys + * + * @param {RawIdentityPublicKey[]} rawPublicKeys + * + * @return {ValidationResult} + */ + function validatePublicKeys(rawPublicKeys) { + const result = new ValidationResult(); + + // Validate public key structure + rawPublicKeys.forEach((rawPublicKey) => { + result.merge( + validator.validate( + jsonSchema, + convertBuffersToArrays(rawPublicKey), + ), + ); + }); + + if (!result.isValid()) { + return result; + } + + // Check that there's no duplicated key ids in the state transition + const duplicatedIds = []; + const idsCount = {}; + + rawPublicKeys.forEach((rawPublicKey) => { + idsCount[rawPublicKey.id] = !idsCount[rawPublicKey.id] ? 1 : idsCount[rawPublicKey.id] + 1; + if (idsCount[rawPublicKey.id] > 1) { + duplicatedIds.push(rawPublicKey.id); + } + }); + + if (duplicatedIds.length > 0) { + result.addError( + new DuplicatedIdentityPublicKeyIdError(duplicatedIds), + ); + } + + // Check that there's no duplicated keys + const keysCount = {}; + const duplicatedKeyIds = []; + rawPublicKeys.forEach((rawPublicKey) => { + const dataHex = rawPublicKey.data.toString('hex'); + + keysCount[dataHex] = !keysCount[dataHex] + ? 1 : keysCount[dataHex] + 1; + + if (keysCount[dataHex] > 1) { + duplicatedKeyIds.push(rawPublicKey.id); + } + }); + + if (duplicatedKeyIds.length > 0) { + result.addError( + new DuplicatedIdentityPublicKeyError(duplicatedKeyIds), + ); + } + + // validate key data + rawPublicKeys + .forEach((rawPublicKey) => { + let validationError; + + switch (rawPublicKey.type) { + case IdentityPublicKey.TYPES.ECDSA_SECP256K1: { + const dataHex = rawPublicKey.data.toString('hex'); + + if (!PublicKey.isValid(dataHex)) { + validationError = PublicKey.getValidationError(dataHex); + } + break; + } + case IdentityPublicKey.TYPES.BLS12_381: { + try { + bls.PublicKey.fromBytes( + Uint8Array.from(rawPublicKey.data), + ); + } catch (e) { + validationError = new TypeError('Invalid public key'); + } + break; + } + case IdentityPublicKey.TYPES.ECDSA_HASH160: + case IdentityPublicKey.TYPES.BIP13_SCRIPT_HASH: + // Do nothing + break; + default: + throw new TypeError(`Unknown public key type: ${rawPublicKey.type}`); + } + + if (validationError !== undefined) { + const consensusError = new InvalidIdentityPublicKeyDataError( + rawPublicKey.id, + validationError.message, + ); + + consensusError.setValidationError(validationError); + + result.addError(consensusError); + } + }); + + // Validate that public keys have correct purpose and security level + rawPublicKeys + .forEach((rawPublicKey) => { + const keyPurpose = rawPublicKey.purpose; + const allowedSecurityLevels = IdentityPublicKey.ALLOWED_SECURITY_LEVELS[keyPurpose]; + + if (!allowedSecurityLevels || !allowedSecurityLevels.includes(rawPublicKey.securityLevel)) { + const error = new InvalidIdentityPublicKeySecurityLevelError( + rawPublicKey.id, + rawPublicKey.purpose, + rawPublicKey.securityLevel, + allowedSecurityLevels, + ); + + result.addError(error); + } + }); + + return result; + } + + return validatePublicKeys; +} + +module.exports = validatePublicKeysFactory; diff --git a/packages/js-dpp/lib/identity/validation/validateRequiredPurposeAndSecurityLevelFactory.js b/packages/js-dpp/lib/identity/validation/validateRequiredPurposeAndSecurityLevelFactory.js new file mode 100644 index 00000000000..21d1ab86117 --- /dev/null +++ b/packages/js-dpp/lib/identity/validation/validateRequiredPurposeAndSecurityLevelFactory.js @@ -0,0 +1,53 @@ +const ValidationResult = require('../../validation/ValidationResult'); + +const MissingMasterPublicKeyError = require('../../errors/consensus/basic/identity/MissingMasterPublicKeyError'); + +const IdentityPublicKey = require('../IdentityPublicKey'); + +const MASTER_PURPOSE = IdentityPublicKey.PURPOSES.AUTHENTICATION; +const MASTER_SECURITY_LEVEL = IdentityPublicKey.SECURITY_LEVELS.MASTER; + +/** + * Validate public keys for the identity create ST (factory) + * + * @return {validateRequiredPurposeAndSecurityLevel} + */ +function validateRequiredPurposeAndSecurityLevelFactory() { + /** + * Validate public keys for a create identity transaction + * + * @typedef validateRequiredPurposeAndSecurityLevel + * + * @param {RawIdentityPublicKey[]} rawPublicKeys + * + * @return {ValidationResult} + */ + function validateRequiredPurposeAndSecurityLevel(rawPublicKeys) { + const result = new ValidationResult(); + + // Count how many purpose/security key combinations are here + const keyPurposesAndLevelsCount = {}; + Object.entries(IdentityPublicKey.PURPOSES).forEach(([, purpose]) => { + keyPurposesAndLevelsCount[purpose] = {}; + Object.entries(IdentityPublicKey.SECURITY_LEVELS).forEach(([, securityLevel]) => { + keyPurposesAndLevelsCount[purpose][securityLevel] = 0; + }); + }); + + rawPublicKeys + .filter((rawPublicKey) => rawPublicKey.disabledAt === undefined) + .forEach((rawPublicKey) => { + keyPurposesAndLevelsCount[rawPublicKey.purpose][rawPublicKey.securityLevel] += 1; + }); + + if (keyPurposesAndLevelsCount[MASTER_PURPOSE][MASTER_SECURITY_LEVEL] === 0) { + result.addError(new MissingMasterPublicKeyError()); + } + + return result; + } + + return validateRequiredPurposeAndSecurityLevel; +} + +module.exports = validateRequiredPurposeAndSecurityLevelFactory; diff --git a/packages/js-dpp/lib/index.js b/packages/js-dpp/lib/index.js new file mode 100644 index 00000000000..3a1d77d34fa --- /dev/null +++ b/packages/js-dpp/lib/index.js @@ -0,0 +1,29 @@ +const DashPlatformProtocol = require('./DashPlatformProtocol'); + +const Identity = require('./identity/Identity'); +const IdentityPublicKey = require('./identity/IdentityPublicKey'); +const Identifier = require('./identifier/Identifier'); + +const DataContractFactory = require('./dataContract/DataContractFactory'); + +const consensusErrorCodes = require('./errors/consensus/codes'); + +const protocolVersion = require('./version/protocolVersion'); + +DashPlatformProtocol.DataContractFactory = DataContractFactory; + +DashPlatformProtocol.Identity = Identity; +DashPlatformProtocol.IdentityPublicKey = IdentityPublicKey; +DashPlatformProtocol.Identifier = Identifier; + +DashPlatformProtocol.version = protocolVersion.latestVersion; + +DashPlatformProtocol.ConsensusErrors = Object.values(consensusErrorCodes) + .reduce((obj, ConsensusErrorClass) => { + // eslint-disable-next-line no-param-reassign + obj[ConsensusErrorClass.name] = ConsensusErrorClass; + + return obj; + }, {}); + +module.exports = DashPlatformProtocol; diff --git a/packages/js-dpp/lib/stateTransition/AbstractStateTransition.js b/packages/js-dpp/lib/stateTransition/AbstractStateTransition.js new file mode 100644 index 00000000000..4fdc332dc98 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/AbstractStateTransition.js @@ -0,0 +1,379 @@ +const { + PublicKey, + PrivateKey, + Signer: { sign, verifySignature, verifyHashSignature }, +} = require('@dashevo/dashcore-lib'); + +const StateTransitionIsNotSignedError = require( + './errors/StateTransitionIsNotSignedError', +); + +const stateTransitionTypes = require('./stateTransitionTypes'); + +const hashModule = require('../util/hash'); +const serializer = require('../util/serializer'); + +const calculateStateTransitionFee = require('./fee/calculateStateTransitionFee'); +const IdentityPublicKey = require('../identity/IdentityPublicKey'); +const InvalidIdentityPublicKeyTypeError = require('./errors/InvalidIdentityPublicKeyTypeError'); +const blsPrivateKeyFactory = require('../bls/blsPrivateKeyFactory'); +const blsPublicKeyFactory = require('../bls/blsPublicKeyFactory'); +const BlsSignatures = require('../bls/bls'); +const StateTransitionExecutionContext = require('./StateTransitionExecutionContext'); + +/** + * @abstract + */ +class AbstractStateTransition { + /** + * @param { + * RawDataContractCreateTransition| + * RawDocumentsBatchTransition| + * RawIdentityCreateTransition| + * RawIdentityTopUpTransition + * } [rawStateTransition] + */ + constructor(rawStateTransition = {}) { + this.protocolVersion = rawStateTransition.protocolVersion; + + if (Object.prototype.hasOwnProperty.call(rawStateTransition, 'signature')) { + this.signature = rawStateTransition.signature; + } + + this.executionContext = new StateTransitionExecutionContext(); + } + + /** + * Get protocol version + * + * @return {number} + */ + getProtocolVersion() { + return this.protocolVersion; + } + + /** + * @abstract + * + * @return {number} + */ + getType() { + throw new Error('Not implemented'); + } + + /** + * Returns signature + * + * @return {Buffer} + */ + getSignature() { + return this.signature; + } + + /** + * Set signature + * @param {Buffer} signature + * @return {AbstractStateTransition} + */ + setSignature(signature) { + this.signature = signature; + + return this; + } + + /** + * Get state transition as plain object + * + * @param {Object} [options] + * @param {boolean} [options.skipSignature=false] + * @param {boolean} [options.skipIdentifiersConversion=false] + * + * @return {RawStateTransition} + */ + toObject(options = {}) { + Object.assign( + options, + { + skipIdentifiersConversion: false, + skipSignature: false, + ...options, + }, + ); + + const rawStateTransition = { + protocolVersion: this.getProtocolVersion(), + type: this.getType(), + }; + + if (!options.skipSignature) { + rawStateTransition.signature = this.getSignature(); + } + + return rawStateTransition; + } + + /** + * Get state transition as JSON + * + * @return {JsonStateTransition} + */ + toJSON() { + const jsonStateTransition = this.toObject({ skipIdentifiersConversion: true }); + + if (jsonStateTransition.signature) { + // noinspection JSValidateTypes + jsonStateTransition.signature = jsonStateTransition.signature.toString('base64'); + } + + // noinspection JSValidateTypes + return jsonStateTransition; + } + + /** + * Return serialized State Transition + * + * @param {Object} [options] + * @param {boolean} [options.skipSignature=false] + * @return {Buffer} + */ + toBuffer(options = {}) { + const serializedData = this.toObject(options); + delete serializedData.protocolVersion; + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(this.getProtocolVersion(), 0); + + return Buffer.concat([protocolVersionUInt32, serializer.encode(serializedData)]); + } + + /** + * Returns hex string with Data Contract hash + * + * @param {Object} [options] + * @param {boolean} [options.skipSignature=false] + * @return {Buffer} + */ + hash(options = {}) { + const { hash } = hashModule; + + return hash(this.toBuffer(options)); + } + + /** + * Sign data with private key + * @param {string|Buffer|Uint8Array|PrivateKey} privateKey string must be hex or base58 + * @param {number} keyType private key type + * @return {Promise} + */ + async signByPrivateKey(privateKey, keyType) { + const data = this.toBuffer({ skipSignature: true }); + + switch (keyType) { + case IdentityPublicKey.TYPES.ECDSA_SECP256K1: + case IdentityPublicKey.TYPES.ECDSA_HASH160: { + const privateKeyModel = new PrivateKey(privateKey); + + this.setSignature(sign(data, privateKeyModel)); + + break; + } + case IdentityPublicKey.TYPES.BLS12_381: { + const privateKeyModel = await blsPrivateKeyFactory(privateKey); + const blsSignature = privateKeyModel.sign(new Uint8Array(data)).serialize(); + + this.setSignature(Buffer.from(blsSignature)); + break; + } + default: + throw new InvalidIdentityPublicKeyTypeError(keyType); + } + + return this; + } + + /** + * Verify signature by public key + * + * @param {Buffer} publicKey + * @param publicKeyType + * + * @returns {Promise} + */ + async verifyByPublicKey(publicKey, publicKeyType) { + switch (publicKeyType) { + case IdentityPublicKey.TYPES.ECDSA_SECP256K1: + return this.verifyECDSASignatureByPublicKey(publicKey); + case IdentityPublicKey.TYPES.ECDSA_HASH160: + return this.verifyESDSAHash160SignatureByPublicKeyHash(publicKey); + case IdentityPublicKey.TYPES.BLS12_381: + return this.verifyBLSSignatureByPublicKey(publicKey); + default: + throw new InvalidIdentityPublicKeyTypeError(publicKeyType); + } + } + + /** + * @protected + * @param {Buffer} publicKeyHash + * @return {boolean} + */ + verifyESDSAHash160SignatureByPublicKeyHash(publicKeyHash) { + const signature = this.getSignature(); + if (!signature) { + throw new StateTransitionIsNotSignedError(this); + } + + const hash = this.hash({ skipSignature: true }); + + let isSignatureVerified; + try { + isSignatureVerified = verifyHashSignature(hash, signature, publicKeyHash); + } catch (e) { + isSignatureVerified = false; + } + + return isSignatureVerified; + } + + /** + * Verify signature with public key + * @protected + * @param {string|Buffer|Uint8Array|PublicKey} publicKey string must be hex or base58 + * @returns {boolean} + */ + verifyECDSASignatureByPublicKey(publicKey) { + const signature = this.getSignature(); + if (!signature) { + throw new StateTransitionIsNotSignedError(this); + } + + const data = this.toBuffer({ skipSignature: true }); + + const publicKeyModel = new PublicKey(publicKey, {}); + + let isSignatureVerified; + try { + isSignatureVerified = verifySignature(data, signature, publicKeyModel); + } catch (e) { + isSignatureVerified = false; + } + + return isSignatureVerified; + } + + /** + * Verify signature with public key + * @protected + * @param {string|Buffer|Uint8Array|PublicKey} publicKey string must be hex + * @returns {Promise} + */ + async verifyBLSSignatureByPublicKey(publicKey) { + const signature = this.getSignature(); + if (!signature) { + throw new StateTransitionIsNotSignedError(this); + } + + const data = this.toBuffer({ skipSignature: true }); + + const publicKeyModel = await blsPublicKeyFactory(publicKey); + + const { Signature: BlsSignature, AggregationInfo } = await BlsSignatures.getInstance(); + const aggregationInfo = AggregationInfo.fromMsg(publicKeyModel, new Uint8Array(data)); + const blsSignature = BlsSignature.fromBytesAndAggregationInfo(signature, aggregationInfo); + + return blsSignature.verify(); + } + + /** + * Calculate ST fee in credits + * + * @return {number} + */ + calculateFee() { + return calculateStateTransitionFee(this); + } + + /** + * Returns ids of entities affected by the state transition + * @abstract + * + * @return {Identifier[]} + */ + getModifiedDataIds() { + throw new Error('Not implemented'); + } + + /** + * Returns true if this state transition affects documents: create, update and delete transitions + * + * @return {boolean} + */ + isDocumentStateTransition() { + return AbstractStateTransition.documentTransitionTypes.includes(this.getType()); + } + + /** + * Returns true if this state transition affects data contracts + * + * @return {boolean} + */ + isDataContractStateTransition() { + return AbstractStateTransition.dataContractTransitionTypes.includes(this.getType()); + } + + /** + * Returns true if this state transition affects identities: create, update or top up. + * + * @return {boolean} + */ + isIdentityStateTransition() { + return AbstractStateTransition.identityTransitionTypes.includes(this.getType()); + } + + /** + * Set state transition execution context + * + * @param {StateTransitionExecutionContext} executionContext + */ + setExecutionContext(executionContext) { + this.executionContext = executionContext; + } + + /** + * Get state transition execution context + * + * @return {StateTransitionExecutionContext} + */ + getExecutionContext() { + return this.executionContext; + } +} + +/** + * @typedef RawStateTransition + * @property {number} protocolVersion + * @property {number} type + * @property {Buffer} [signature] + */ + +/** + * @typedef JsonStateTransition + * @property {number} protocolVersion + * @property {number} type + * @property {string} [signature] + */ + +AbstractStateTransition.documentTransitionTypes = [ + stateTransitionTypes.DOCUMENTS_BATCH, +]; +AbstractStateTransition.identityTransitionTypes = [ + stateTransitionTypes.IDENTITY_CREATE, + stateTransitionTypes.IDENTITY_TOP_UP, + stateTransitionTypes.IDENTITY_UPDATE, +]; +AbstractStateTransition.dataContractTransitionTypes = [ + stateTransitionTypes.DATA_CONTRACT_CREATE, + stateTransitionTypes.DATA_CONTRACT_UPDATE, +]; + +module.exports = AbstractStateTransition; diff --git a/packages/js-dpp/lib/stateTransition/AbstractStateTransitionIdentitySigned.js b/packages/js-dpp/lib/stateTransition/AbstractStateTransitionIdentitySigned.js new file mode 100644 index 00000000000..32d6173930e --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/AbstractStateTransitionIdentitySigned.js @@ -0,0 +1,250 @@ +const { + PublicKey, + PrivateKey, + crypto: { Hash }, +} = require('@dashevo/dashcore-lib'); + +const AbstractStateTransition = require('./AbstractStateTransition'); + +const IdentityPublicKey = require('../identity/IdentityPublicKey'); +const InvalidSignaturePublicKeyError = require('./errors/InvalidSignaturePublicKeyError'); +const StateTransitionIsNotSignedError = require('./errors/StateTransitionIsNotSignedError'); +const PublicKeyMismatchError = require('./errors/PublicKeyMismatchError'); +const PublicKeySecurityLevelNotMetError = require('./errors/PublicKeySecurityLevelNotMetError'); +const WrongPublicKeyPurposeError = require('./errors/WrongPublicKeyPurposeError'); +const InvalidIdentityPublicKeyTypeError = require('./errors/InvalidIdentityPublicKeyTypeError'); +const blsPrivateKeyFactory = require('../bls/blsPrivateKeyFactory'); +const blsPublicKeyFactory = require('../bls/blsPublicKeyFactory'); +const PublicKeyIsDisabledError = require('./errors/PublicKeyIsDisabledError'); +const InvalidSignaturePublicKeySecurityLevelError = require('./errors/InvalidSignaturePublicKeySecurityLevelError'); + +/** + * @abstract + */ +class AbstractStateTransitionIdentitySigned extends AbstractStateTransition { + /** + * @param { + * RawDataContractCreateTransition| + * RawDocumentsBatchTransition + * } [rawStateTransition] + */ + constructor(rawStateTransition = {}) { + super(rawStateTransition); + + if (Object.prototype.hasOwnProperty.call(rawStateTransition, 'signaturePublicKeyId')) { + this.signaturePublicKeyId = rawStateTransition.signaturePublicKeyId; + } + } + + /** + * Returns public key id + * + * @returns {number} + */ + getSignaturePublicKeyId() { + return this.signaturePublicKeyId; + } + + /** + * Sign data and check identityPublicKey + * + * @param {IdentityPublicKey} identityPublicKey + * @param {string|Buffer|Uint8Array|PrivateKey} privateKey string must be hex or base58 + * @return {Promise} + */ + async sign(identityPublicKey, privateKey) { + let privateKeyModel; + let pubKeyBase; + + this.verifyPublicKeyLevelAndPurpose(identityPublicKey); + this.verifyPublicKeyIsEnabled(identityPublicKey); + + switch (identityPublicKey.getType()) { + case IdentityPublicKey.TYPES.ECDSA_SECP256K1: + privateKeyModel = new PrivateKey(privateKey); + + /* We store compressed public key in the identity as a base64 string, + /* and here we compare the private key used to sign the state transition + /* with the compressed key stored in the identity */ + pubKeyBase = new PublicKey({ + ...privateKeyModel.toPublicKey().toObject(), + compressed: true, + }) + .toBuffer(); + + if (!pubKeyBase.equals(identityPublicKey.getData())) { + throw new InvalidSignaturePublicKeyError(identityPublicKey.getData()); + } + + await this.signByPrivateKey(privateKeyModel, identityPublicKey.getType()); + break; + case IdentityPublicKey.TYPES.ECDSA_HASH160: { + privateKeyModel = new PrivateKey(privateKey); + pubKeyBase = new PublicKey({ + ...privateKeyModel.toPublicKey().toObject(), + compressed: true, + }) + .toBuffer(); + + pubKeyBase = Hash.sha256ripemd160(pubKeyBase); + + if (!pubKeyBase.equals(identityPublicKey.getData())) { + throw new InvalidSignaturePublicKeyError(identityPublicKey.getData()); + } + + await this.signByPrivateKey(privateKeyModel, identityPublicKey.getType()); + break; + } + case IdentityPublicKey.TYPES.BLS12_381: + privateKeyModel = await blsPrivateKeyFactory(privateKey); + pubKeyBase = Buffer.from(privateKeyModel.getPublicKey().serialize()); + + if (!pubKeyBase.equals(identityPublicKey.getData())) { + throw new InvalidSignaturePublicKeyError(identityPublicKey.getData()); + } + + await this.signByPrivateKey(privateKeyModel, identityPublicKey.getType()); + break; + default: + throw new InvalidIdentityPublicKeyTypeError(identityPublicKey.getType()); + } + + this.signaturePublicKeyId = identityPublicKey.getId(); + + return this; + } + + /** + * @private + * @param {IdentityPublicKey} publicKey + * + * Verifies that the supplied public key has the correct security level + * and purpose to sign this state transition + */ + verifyPublicKeyLevelAndPurpose(publicKey) { + // If state transition requires MASTER security level it must be sign only with MASTER key + if ( + publicKey.isMaster() + && this.getKeySecurityLevelRequirement() !== IdentityPublicKey.SECURITY_LEVELS.MASTER + ) { + throw new InvalidSignaturePublicKeySecurityLevelError( + IdentityPublicKey.SECURITY_LEVELS.MASTER, + this.getKeySecurityLevelRequirement(), + ); + } + + // Otherwise, key security level should be less than MASTER but more or equal than required + if (this.getKeySecurityLevelRequirement() < publicKey.getSecurityLevel()) { + throw new PublicKeySecurityLevelNotMetError( + publicKey.getSecurityLevel(), + this.getKeySecurityLevelRequirement(), + ); + } + + if (publicKey.getPurpose() !== IdentityPublicKey.PURPOSES.AUTHENTICATION) { + throw new WrongPublicKeyPurposeError( + publicKey.getPurpose(), + IdentityPublicKey.PURPOSES.AUTHENTICATION, + ); + } + } + + /** + * @private + * @param {IdentityPublicKey} publicKey + */ + verifyPublicKeyIsEnabled(publicKey) { + if (publicKey.getDisabledAt()) { + throw new PublicKeyIsDisabledError(publicKey); + } + } + + /** + * Verify signature + * + * @param {IdentityPublicKey} publicKey + * @return {Promise} + */ + async verifySignature(publicKey) { + this.verifyPublicKeyLevelAndPurpose(publicKey); + this.verifyPublicKeyIsEnabled(publicKey); + + const signature = this.getSignature(); + if (!signature) { + throw new StateTransitionIsNotSignedError(this); + } + + if (this.getSignaturePublicKeyId() !== publicKey.getId()) { + throw new PublicKeyMismatchError(publicKey); + } + + const publicKeyBuffer = publicKey.getData(); + + switch (publicKey.getType()) { + case IdentityPublicKey.TYPES.ECDSA_HASH160: + return this.verifyESDSAHash160SignatureByPublicKeyHash(publicKeyBuffer); + case IdentityPublicKey.TYPES.ECDSA_SECP256K1: + return this.verifyECDSASignatureByPublicKey(PublicKey.fromBuffer(publicKeyBuffer)); + case IdentityPublicKey.TYPES.BLS12_381: { + const publicKeyModel = await blsPublicKeyFactory(new Uint8Array(publicKeyBuffer)); + + return this.verifyBLSSignatureByPublicKey(publicKeyModel); + } + default: + throw new InvalidIdentityPublicKeyTypeError(publicKey.getType()); + } + } + + /** + * Set signature public key id + * @param {number} signaturePublicKeyId + * @return {AbstractStateTransition} + */ + setSignaturePublicKeyId(signaturePublicKeyId) { + this.signaturePublicKeyId = signaturePublicKeyId; + + return this; + } + + /** + * Get state transition as plain object + * + * @param {Object} [options] + * @param {boolean} [options.skipSignature] + * + * @return {Object} + */ + toObject(options = {}) { + const skipSignature = !!options.skipSignature; + + const rawStateTransition = super.toObject(options); + + if (!skipSignature) { + rawStateTransition.signaturePublicKeyId = this.getSignaturePublicKeyId(); + } + + return rawStateTransition; + } + + /** + * Returns minimal key security level that can be used to sign this ST. + * Override this method if the ST requires a different security level. + * + * @return {number} + */ + getKeySecurityLevelRequirement() { + return IdentityPublicKey.SECURITY_LEVELS.HIGH; + } +} + +/** + * @typedef {RawStateTransition & Object} RawStateTransitionIdentitySigned + * @property {number} [signaturePublicKeyId] + */ + +/** + * @typedef {JsonStateTransition & Object} JsonStateTransitionIdentitySigned + * @property {number} [signaturePublicKeyId] + */ + +module.exports = AbstractStateTransitionIdentitySigned; diff --git a/packages/js-dpp/lib/stateTransition/StateTransitionExecutionContext.js b/packages/js-dpp/lib/stateTransition/StateTransitionExecutionContext.js new file mode 100644 index 00000000000..fd3bb3720b9 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/StateTransitionExecutionContext.js @@ -0,0 +1,69 @@ +class StateTransitionExecutionContext { + constructor() { + /** + * @type {AbstractOperation[]} + */ + this.actualOperations = []; + /** + * @type {AbstractOperation[]} + */ + this.dryOperations = []; + this.dryRun = false; + } + + /** + * Add operation into context + * + * @param {AbstractOperation} operation + */ + addOperation(...operation) { + if (this.isDryRun()) { + this.dryOperations.push(...operation); + } else { + this.actualOperations.push(...operation); + } + } + + /** + * Get operations + * + * @return {AbstractOperation[]} + */ + getOperations() { + return this.actualOperations.concat(this.dryOperations); + } + + /** + * Clear dry operations + */ + clearDryOperations() { + this.dryOperations = []; + } + + /** + * Enable dry run + * + * Count only operations + */ + enableDryRun() { + this.dryRun = true; + } + + /** + * Disable dry run + * + * Execute state transition + */ + disableDryRun() { + this.dryRun = false; + } + + /** + * @return {boolean} + */ + isDryRun() { + return this.dryRun; + } +} + +module.exports = StateTransitionExecutionContext; diff --git a/packages/js-dpp/lib/stateTransition/StateTransitionFacade.js b/packages/js-dpp/lib/stateTransition/StateTransitionFacade.js new file mode 100644 index 00000000000..d67de094b11 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/StateTransitionFacade.js @@ -0,0 +1,577 @@ +const $RefParser = require('@apidevtools/json-schema-ref-parser'); +const jsonPatch = require('fast-json-patch'); +const jsonSchemaDiffValidator = require('json-schema-diff-validator'); +const { Signer: { verifyHashSignature } } = require('@dashevo/dashcore-lib'); + +const MissingOptionError = require('../errors/MissingOptionError'); + +const StateTransitionFactory = require('./StateTransitionFactory'); + +const AbstractStateTransition = require('./AbstractStateTransition'); + +const stateTransitionTypes = require('./stateTransitionTypes'); + +const createStateTransitionFactory = require('./createStateTransitionFactory'); + +const validateDataContractFactory = require('../dataContract/validation/validateDataContractFactory'); +const validateDataContractPatternsFactory = require('../dataContract/validation/validateDataContractPatternsFactory'); +const validateDataContractCreateTransitionBasicFactory = require('../dataContract/stateTransition/DataContractCreateTransition/validation/basic/validateDataContractCreateTransitionBasicFactory'); +const validateStateTransitionBasicFactory = require('./validation/validateStateTransitionBasicFactory'); +const validateDataContractCreateTransitionStateFactory = require('../dataContract/stateTransition/DataContractCreateTransition/validation/state/validateDataContractCreateTransitionStateFactory'); +const validateStateTransitionStateFactory = require('./validation/validateStateTransitionStateFactory'); +const validateDocumentsBatchTransitionBasicFactory = require('../document/stateTransition/DocumentsBatchTransition/validation/basic/validateDocumentsBatchTransitionBasicFactory'); +const validateIdentityCreateTransitionStateFactory = require('../identity/stateTransition/IdentityCreateTransition/validation/state/validateIdentityCreateTransitionStateFactory'); +const validateIdentityTopUpTransitionStateFactory = require('../identity/stateTransition/IdentityTopUpTransition/validation/state/validateIdentityTopUpTransitionStateFactory'); +const validateIdentityUpdateTransitionStateFactory = require('../identity/stateTransition/IdentityUpdateTransition/validation/state/validateIdentityUpdateTransitionStateFactory'); +const validateIdentityCreateTransitionBasicFactory = require('../identity/stateTransition/IdentityCreateTransition/validation/basic/validateIdentityCreateTransitionBasicFactory'); +const validateIdentityTopUpTransitionBasicFactory = require('../identity/stateTransition/IdentityTopUpTransition/validation/basic/validateIdentityTopUpTransitionBasicFactory'); +const validateIdentityUpdateTransitionBasicFactory = require('../identity/stateTransition/IdentityUpdateTransition/validation/basic/validateIdentityUpdateTransitionBasicFactory'); +const validateStateTransitionIdentitySignatureFactory = require('./validation/validateStateTransitionIdentitySignatureFactory'); +const validateStateTransitionFeeFactory = require('./validation/validateStateTransitionFeeFactory'); + +const enrichDataContractWithBaseSchema = require('../dataContract/enrichDataContractWithBaseSchema'); +const findDuplicatesById = require('../document/stateTransition/DocumentsBatchTransition/validation/basic/findDuplicatesById'); +const findDuplicatesByIndices = require('../document/stateTransition/DocumentsBatchTransition/validation/basic/findDuplicatesByIndices'); + +const validateDocumentsBatchTransitionStateFactory = require('../document/stateTransition/DocumentsBatchTransition/validation/state/validateDocumentsBatchTransitionStateFactory'); +const fetchDocumentsFactory = require('../document/stateTransition/DocumentsBatchTransition/validation/state/fetchDocumentsFactory'); +const validateDocumentsUniquenessByIndicesFactory = require('../document/stateTransition/DocumentsBatchTransition/validation/state/validateDocumentsUniquenessByIndicesFactory'); +const validatePartialCompoundIndices = require('../document/stateTransition/DocumentsBatchTransition/validation/basic/validatePartialCompoundIndices'); +const getDataTriggersFactory = require('../dataTrigger/getDataTriggersFactory'); +const executeDataTriggersFactory = require('../document/stateTransition/DocumentsBatchTransition/validation/state/executeDataTriggersFactory'); +const validatePublicKeysFactory = require('../identity/validation/validatePublicKeysFactory'); +const validatePublicKeysState = require('../identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState'); +const validateRequiredPurposeAndSecurityLevelFactory = require('../identity/validation/validateRequiredPurposeAndSecurityLevelFactory'); +const validateDataContractMaxDepthFactory = require('../dataContract/validation/validateDataContractMaxDepthFactory'); + +const applyStateTransitionFactory = require('./applyStateTransitionFactory'); + +const applyDataContractCreateTransitionFactory = require( + '../dataContract/stateTransition/DataContractCreateTransition/applyDataContractCreateTransitionFactory', +); +const applyDataContractUpdateTransitionFactory = require( + '../dataContract/stateTransition/DataContractUpdateTransition/applyDataContractUpdateTransitionFactory', +); + +const applyDocumentsBatchTransitionFactory = require( + '../document/stateTransition/DocumentsBatchTransition/applyDocumentsBatchTransitionFactory', +); + +const applyIdentityCreateTransitionFactory = require( + '../identity/stateTransition/IdentityCreateTransition/applyIdentityCreateTransitionFactory', +); + +const applyIdentityTopUpTransitionFactory = require( + '../identity/stateTransition/IdentityTopUpTransition/applyIdentityTopUpTransitionFactory', +); + +const applyIdentityUpdateTransitionFactory = require( + '../identity/stateTransition/IdentityUpdateTransition/applyIdentityUpdateTransitionFactory', +); +const validateInstantAssetLockProofStructureFactory = require('../identity/stateTransition/assetLockProof/instant/validateInstantAssetLockProofStructureFactory'); +const calculateStateTransitionFee = require('./fee/calculateStateTransitionFee'); +const InstantAssetLockProof = require('../identity/stateTransition/assetLockProof/instant/InstantAssetLockProof'); +const ChainAssetLockProof = require('../identity/stateTransition/assetLockProof/chain/ChainAssetLockProof'); +const validateChainAssetLockProofStructureFactory = require('../identity/stateTransition/assetLockProof/chain/validateChainAssetLockProofStructureFactory'); +const fetchAssetLockTransactionOutputFactory = require('../identity/stateTransition/assetLockProof/fetchAssetLockTransactionOutputFactory'); +const validateAssetLockTransactionFactory = require('../identity/stateTransition/assetLockProof/validateAssetLockTransactionFactory'); + +const ValidationResult = require('../validation/ValidationResult'); +const AbstractStateTransitionIdentitySigned = require('./AbstractStateTransitionIdentitySigned'); +const validateStateTransitionKeySignatureFactory = require('./validation/validateStateTransitionKeySignatureFactory'); + +const fetchAssetLockPublicKeyHashFactory = require('../identity/stateTransition/assetLockProof/fetchAssetLockPublicKeyHashFactory'); + +const decodeProtocolEntityFactory = require('../decodeProtocolEntityFactory'); +const protocolVersion = require('../version/protocolVersion'); +const validateProtocolVersionFactory = require('../version/validateProtocolVersionFactory'); +const validateDataContractUpdateTransitionBasicFactory = require('../dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateDataContractUpdateTransitionBasicFactory'); +const validateDataContractUpdateTransitionStateFactory = require('../dataContract/stateTransition/DataContractUpdateTransition/validation/state/validateDataContractUpdateTransitionStateFactory'); +const validateIndicesAreBackwardCompatible = require('../dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateIndicesAreBackwardCompatible'); +const getPropertyDefinitionByPath = require('../dataContract/getPropertyDefinitionByPath'); + +const identityJsonSchema = require('../../schema/identity/stateTransition/publicKey.json'); +const validatePublicKeySignaturesFactory = require('../identity/stateTransition/validatePublicKeySignaturesFactory'); +const StateTransitionExecutionContext = require('./StateTransitionExecutionContext'); + +class StateTransitionFacade { + /** + * @param {DashPlatformProtocol} dpp + * @param {RE2} RE2 + * @param {BlsSignatures} bls + */ + constructor(dpp, RE2, bls) { + this.stateRepository = dpp.getStateRepository(); + + const validator = dpp.getJsonSchemaValidator(); + + const validateDataContractMaxDepth = validateDataContractMaxDepthFactory($RefParser); + const validateDataContractPatterns = validateDataContractPatternsFactory(RE2); + + const validateProtocolVersion = validateProtocolVersionFactory( + dpp, + protocolVersion.compatibility, + ); + const validateDataContract = validateDataContractFactory( + validator, + validateDataContractMaxDepth, + enrichDataContractWithBaseSchema, + validateDataContractPatterns, + validateProtocolVersion, + getPropertyDefinitionByPath, + ); + + this.validateStateTransitionIdentitySignature = validateStateTransitionIdentitySignatureFactory( + this.stateRepository, + ); + + const fetchAssetLockTransactionOutput = fetchAssetLockTransactionOutputFactory( + this.stateRepository, + ); + + const fetchAssetLockPublicKeyHash = fetchAssetLockPublicKeyHashFactory( + fetchAssetLockTransactionOutput, + ); + + this.validateStateTransitionKeySignature = validateStateTransitionKeySignatureFactory( + verifyHashSignature, + fetchAssetLockPublicKeyHash, + ); + + // eslint-disable-next-line max-len + const validateDataContractCreateTransitionBasic = validateDataContractCreateTransitionBasicFactory( + validator, + validateDataContract, + validateProtocolVersion, + ); + + // eslint-disable-next-line max-len + const validateDataContractUpdateTransitionBasic = validateDataContractUpdateTransitionBasicFactory( + validator, + validateDataContract, + validateProtocolVersion, + this.stateRepository, + jsonSchemaDiffValidator, + validateIndicesAreBackwardCompatible, + jsonPatch, + ); + + this.createStateTransition = createStateTransitionFactory(this.stateRepository); + + const validateDocumentsBatchTransitionBasic = ( + validateDocumentsBatchTransitionBasicFactory( + findDuplicatesById, + findDuplicatesByIndices, + this.stateRepository, + validator, + enrichDataContractWithBaseSchema, + validatePartialCompoundIndices, + validateProtocolVersion, + ) + ); + + const validateAssetLockTransaction = validateAssetLockTransactionFactory(this.stateRepository); + + const validateInstantAssetLockProofStructure = validateInstantAssetLockProofStructureFactory( + validator, + this.stateRepository, + validateAssetLockTransaction, + ); + + const validateChainAssetLockProofStructure = validateChainAssetLockProofStructureFactory( + validator, + this.stateRepository, + validateAssetLockTransaction, + ); + + const proofValidationFunctionsByType = { + [InstantAssetLockProof.type]: validateInstantAssetLockProofStructure, + [ChainAssetLockProof.type]: validateChainAssetLockProofStructure, + }; + + const validatePublicKeys = validatePublicKeysFactory( + validator, + identityJsonSchema, + bls, + ); + + const validateRequiredPurposeAndSecurityLevel = ( + validateRequiredPurposeAndSecurityLevelFactory() + ); + + const validatePublicKeySignatures = validatePublicKeySignaturesFactory( + this.createStateTransition, + ); + + const validateIdentityCreateTransitionBasic = ( + validateIdentityCreateTransitionBasicFactory( + validator, + validatePublicKeys, + validateRequiredPurposeAndSecurityLevel, + proofValidationFunctionsByType, + validateProtocolVersion, + validatePublicKeySignatures, + ) + ); + + const validateIdentityTopUpTransitionBasic = ( + validateIdentityTopUpTransitionBasicFactory( + validator, + proofValidationFunctionsByType, + validateProtocolVersion, + ) + ); + + const validateIdentityUpdateTransitionBasic = validateIdentityUpdateTransitionBasicFactory( + validator, + validateProtocolVersion, + validatePublicKeys, + validatePublicKeySignatures, + ); + + const validationFunctionsByType = { + [stateTransitionTypes.DATA_CONTRACT_CREATE]: validateDataContractCreateTransitionBasic, + [stateTransitionTypes.DATA_CONTRACT_UPDATE]: validateDataContractUpdateTransitionBasic, + [stateTransitionTypes.DOCUMENTS_BATCH]: validateDocumentsBatchTransitionBasic, + [stateTransitionTypes.IDENTITY_CREATE]: validateIdentityCreateTransitionBasic, + [stateTransitionTypes.IDENTITY_TOP_UP]: validateIdentityTopUpTransitionBasic, + [stateTransitionTypes.IDENTITY_UPDATE]: validateIdentityUpdateTransitionBasic, + }; + + this.validateStateTransitionBasic = validateStateTransitionBasicFactory( + validationFunctionsByType, + this.createStateTransition, + ); + + const validateDataContractCreateTransitionState = ( + validateDataContractCreateTransitionStateFactory( + this.stateRepository, + ) + ); + + const validateDataContractUpdateTransitionState = ( + validateDataContractUpdateTransitionStateFactory( + this.stateRepository, + ) + ); + + const validateIdentityCreateTransitionState = validateIdentityCreateTransitionStateFactory( + this.stateRepository, + ); + + const validateIdentityTopUpTransitionState = validateIdentityTopUpTransitionStateFactory(); + + const validateIdentityUpdateTransitionState = validateIdentityUpdateTransitionStateFactory( + this.stateRepository, + validatePublicKeysState, + validateRequiredPurposeAndSecurityLevel, + ); + + const fetchDocuments = fetchDocumentsFactory( + this.stateRepository, + ); + + const validateDocumentsUniquenessByIndices = validateDocumentsUniquenessByIndicesFactory( + this.stateRepository, + ); + + const getDataTriggers = getDataTriggersFactory(); + + const executeDataTriggers = executeDataTriggersFactory( + getDataTriggers, + ); + + const validateDocumentsBatchTransitionState = validateDocumentsBatchTransitionStateFactory( + this.stateRepository, + fetchDocuments, + validateDocumentsUniquenessByIndices, + executeDataTriggers, + ); + + this.validateStateTransitionState = validateStateTransitionStateFactory({ + [stateTransitionTypes.DATA_CONTRACT_CREATE]: validateDataContractCreateTransitionState, + [stateTransitionTypes.DATA_CONTRACT_UPDATE]: validateDataContractUpdateTransitionState, + [stateTransitionTypes.DOCUMENTS_BATCH]: validateDocumentsBatchTransitionState, + [stateTransitionTypes.IDENTITY_CREATE]: validateIdentityCreateTransitionState, + [stateTransitionTypes.IDENTITY_TOP_UP]: validateIdentityTopUpTransitionState, + [stateTransitionTypes.IDENTITY_UPDATE]: validateIdentityUpdateTransitionState, + }); + + this.validateStateTransitionFee = validateStateTransitionFeeFactory( + this.stateRepository, + calculateStateTransitionFee, + fetchAssetLockTransactionOutput, + ); + + const decodeProtocolEntity = decodeProtocolEntityFactory(); + + this.factory = new StateTransitionFactory( + this.validateStateTransitionBasic, + this.createStateTransition, + dpp, + decodeProtocolEntity, + ); + + const applyDataContractCreateTransition = applyDataContractCreateTransitionFactory( + this.stateRepository, + ); + + const applyDataContractUpdateTransition = applyDataContractUpdateTransitionFactory( + this.stateRepository, + ); + + const applyDocumentsBatchTransition = applyDocumentsBatchTransitionFactory( + this.stateRepository, + fetchDocuments, + ); + + const applyIdentityCreateTransition = applyIdentityCreateTransitionFactory( + this.stateRepository, + fetchAssetLockTransactionOutput, + ); + + const applyIdentityTopUpTransition = applyIdentityTopUpTransitionFactory( + this.stateRepository, + fetchAssetLockTransactionOutput, + ); + + const applyIdentityUpdateTransition = applyIdentityUpdateTransitionFactory( + this.stateRepository, + ); + + this.applyStateTransition = applyStateTransitionFactory( + applyDataContractCreateTransition, + applyDataContractUpdateTransition, + applyDocumentsBatchTransition, + applyIdentityCreateTransition, + applyIdentityTopUpTransition, + applyIdentityUpdateTransition, + ); + } + + /** + * Create State Transition from plain object + * + * @param {RawDataContractCreateTransition|RawDocumentsBatchTransition} rawStateTransition + * @param {Object} [options] + * @param {boolean} [options.skipValidation=false] + * @return {DataContractCreateTransition|DocumentsBatchTransition} + */ + async createFromObject(rawStateTransition, options = {}) { + if (!this.stateRepository && !options.skipValidation) { + throw new MissingOptionError( + 'stateRepository', + 'Can\'t create State Transition because State Repository is not set, use' + + ' setStateRepository method', + ); + } + + return this.factory.createFromObject(rawStateTransition, options); + } + + /** + * Create State Transition from buffer + * + * @param {Buffer} buffer + * @param {Object} [options] + * @param {boolean} [options.skipValidation=false] + * @return {DataContractCreateTransition|DocumentsBatchTransition} + */ + async createFromBuffer(buffer, options = {}) { + if (!this.stateRepository && !options.skipValidation) { + throw new MissingOptionError( + 'stateRepository', + 'Can\'t create State Transition because State Repository is not set, use' + + ' setStateRepository method', + ); + } + + return this.factory.createFromBuffer(buffer, options); + } + + /** + * Validate State Transition + * + * @param {RawStateTransition|AbstractStateTransition} stateTransition + * @param {Object} [options] + * @param {boolean} [options.basic=true] + * @param {boolean} [options.signature=true] + * @param {boolean} [options.fee=true] + * @param {boolean} [options.state=true] + * @return {Promise} + */ + async validate(stateTransition, options = {}) { + // eslint-disable-next-line no-param-reassign + options = { + basic: true, + signature: true, + fee: true, + state: true, + ...options, + }; + + if (!this.stateRepository) { + throw new MissingOptionError( + 'stateRepository', + 'Can\'t validate State Transition because State Repository is not set, use' + + ' setStateRepository method', + ); + } + + // Convert raw state transition to the model + let stateTransitionModel = stateTransition; + + if (!(stateTransition instanceof AbstractStateTransition)) { + stateTransitionModel = await this.createStateTransition(stateTransition); + } + + const result = new ValidationResult(); + + // Basic validation + if (options.basic) { + result.merge( + await this.validateBasic(stateTransition), + ); + } + + if (!result.isValid()) { + return result; + } + + // Signature validation + if (options.signature) { + result.merge( + await this.validateSignature(stateTransitionModel), + ); + } + + if (!result.isValid()) { + return result; + } + + // Fee validation + if (options.fee) { + result.merge( + await this.validateFee(stateTransitionModel), + ); + } + + if (!result.isValid()) { + return result; + } + + // Validate against existing state + if (options.state) { + result.merge( + await this.validateState(stateTransitionModel), + ); + } + + return result; + } + + /** + * Validate State Transition structure and data + * + * @param {AbstractStateTransition|RawStateTransition} stateTransition + * @return {Promise} + */ + async validateBasic(stateTransition) { + if (!this.stateRepository) { + throw new MissingOptionError( + 'stateRepository', + 'Can\'t validate State Transition because State Repository is not set, use' + + ' setStateRepository method', + ); + } + + let rawStateTransition; + let executionContext; + + if (stateTransition instanceof AbstractStateTransition) { + rawStateTransition = stateTransition.toObject(); + executionContext = stateTransition.getExecutionContext(); + } else { + rawStateTransition = stateTransition; + } + + return this.validateStateTransitionBasic( + rawStateTransition, + executionContext || new StateTransitionExecutionContext(), + ); + } + + /** + * Validate State Transition signature and ownership + * + * @param {AbstractStateTransition} stateTransition + * + * @return {Promise} + */ + async validateSignature(stateTransition) { + if (!this.stateRepository) { + throw new MissingOptionError( + 'stateRepository', + 'Can\'t validate State Transition because State Repository is not set, use' + + ' setStateRepository method', + ); + } + + if (stateTransition instanceof AbstractStateTransitionIdentitySigned) { + return this.validateStateTransitionIdentitySignature(stateTransition); + } + + return this.validateStateTransitionKeySignature(stateTransition); + } + + /** + * Validate State Transition fee + * + * @param {AbstractStateTransition} stateTransition + * + * @return {Promise} + */ + async validateFee(stateTransition) { + if (!this.stateRepository) { + throw new MissingOptionError( + 'stateRepository', + 'Can\'t validate State Transition because State Repository is not set, use' + + ' setStateRepository method', + ); + } + + return this.validateStateTransitionFee(stateTransition); + } + + /** + * Validate State Transition against existing state + * + * @param {AbstractStateTransition} stateTransition + * + * @return {Promise} + */ + async validateState(stateTransition) { + if (!this.stateRepository) { + throw new MissingOptionError( + 'stateRepository', + 'Can\'t validate State Transition because State Repository is not set, use' + + ' setStateRepository method', + ); + } + + return this.validateStateTransitionState(stateTransition); + } + + /** + * Apply state transition to the state + * + * @param {AbstractStateTransition} stateTransition + * + * @return {Promise} + */ + async apply(stateTransition) { + return this.applyStateTransition(stateTransition); + } +} + +module.exports = StateTransitionFacade; diff --git a/packages/js-dpp/lib/stateTransition/StateTransitionFactory.js b/packages/js-dpp/lib/stateTransition/StateTransitionFactory.js new file mode 100644 index 00000000000..60cc92a08fb --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/StateTransitionFactory.js @@ -0,0 +1,81 @@ +const InvalidStateTransitionError = require('./errors/InvalidStateTransitionError'); +const AbstractConsensusError = require('../errors/consensus/AbstractConsensusError'); +const StateTransitionExecutionContext = require('./StateTransitionExecutionContext'); + +class StateTransitionFactory { + /** + * @param {validateStateTransitionBasic} validateStateTransitionBasic + * @param {createStateTransition} createStateTransition + * @param {DashPlatformProtocol} dpp + * @param {decodeProtocolEntity} decodeProtocolEntity + */ + constructor( + validateStateTransitionBasic, + createStateTransition, + dpp, + decodeProtocolEntity, + ) { + this.validateStateTransitionBasic = validateStateTransitionBasic; + this.createStateTransition = createStateTransition; + this.dpp = dpp; + this.decodeProtocolEntity = decodeProtocolEntity; + } + + /** + * Create State Transition from plain object + * + * @param {RawStateTransition} rawStateTransition + * @param {Object} [options] + * @param {boolean} [options.skipValidation=false] + * @return {AbstractStateTransition} + */ + async createFromObject(rawStateTransition, options = {}) { + const opts = { skipValidation: false, ...options }; + + const executionContext = new StateTransitionExecutionContext(); + + if (!opts.skipValidation) { + const result = await this.validateStateTransitionBasic(rawStateTransition, executionContext); + + if (!result.isValid()) { + throw new InvalidStateTransitionError(result.getErrors(), rawStateTransition); + } + } + + // noinspection UnnecessaryLocalVariableJS + const stateTransition = await this.createStateTransition(rawStateTransition, executionContext); + + return stateTransition; + } + + /** + * Create State Transition from buffer + * + * @param {Buffer} buffer + * @param {Object} options + * @param {boolean} [options.skipValidation=false] + * @return {RawDataContractCreateTransition|DocumentsBatchTransition} + */ + async createFromBuffer(buffer, options = { }) { + let rawStateTransition; + let protocolVersion; + + try { + [protocolVersion, rawStateTransition] = this.decodeProtocolEntity( + buffer, + ); + + rawStateTransition.protocolVersion = protocolVersion; + } catch (error) { + if (error instanceof AbstractConsensusError) { + throw new InvalidStateTransitionError([error]); + } + + throw error; + } + + return this.createFromObject(rawStateTransition, options); + } +} + +module.exports = StateTransitionFactory; diff --git a/packages/js-dpp/lib/stateTransition/applyStateTransitionFactory.js b/packages/js-dpp/lib/stateTransition/applyStateTransitionFactory.js new file mode 100644 index 00000000000..e893ebe0270 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/applyStateTransitionFactory.js @@ -0,0 +1,49 @@ +const stateTransitionTypes = require('./stateTransitionTypes'); + +/** + * Update state by applying transition (factory) + * + * @param {applyDataContractCreateTransition} applyDataContractCreateTransition + * @param {applyDataContractUpdateTransition} applyDataContractUpdateTransition + * @param {applyDocumentsBatchTransition} applyDocumentsBatchTransition + * @param {applyIdentityCreateTransition} applyIdentityCreateTransition + * @param {applyIdentityTopUpTransition} applyIdentityTopUpTransition + * @param {applyIdentityUpdateTransition} applyIdentityUpdateTransition + * + * @returns {applyStateTransition} + */ +function applyStateTransitionFactory( + applyDataContractCreateTransition, + applyDataContractUpdateTransition, + applyDocumentsBatchTransition, + applyIdentityCreateTransition, + applyIdentityTopUpTransition, + applyIdentityUpdateTransition, +) { + /* map apply functions */ + const typesToFunction = { + [stateTransitionTypes.DATA_CONTRACT_CREATE]: applyDataContractCreateTransition, + [stateTransitionTypes.DATA_CONTRACT_UPDATE]: applyDataContractUpdateTransition, + [stateTransitionTypes.DOCUMENTS_BATCH]: applyDocumentsBatchTransition, + [stateTransitionTypes.IDENTITY_CREATE]: applyIdentityCreateTransition, + [stateTransitionTypes.IDENTITY_TOP_UP]: applyIdentityTopUpTransition, + [stateTransitionTypes.IDENTITY_UPDATE]: applyIdentityUpdateTransition, + }; + + /** + * Update state by applying transition + * + * @typedef applyStateTransition + * + * @param {AbstractStateTransition} stateTransition + * + * @returns {Promise} + */ + async function applyStateTransition(stateTransition) { + await typesToFunction[stateTransition.getType()](stateTransition); + } + + return applyStateTransition; +} + +module.exports = applyStateTransitionFactory; diff --git a/packages/js-dpp/lib/stateTransition/createStateTransitionFactory.js b/packages/js-dpp/lib/stateTransition/createStateTransitionFactory.js new file mode 100644 index 00000000000..3b9a2c03bf0 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/createStateTransitionFactory.js @@ -0,0 +1,90 @@ +const types = require('./stateTransitionTypes'); + +const DocumentsBatchTransition = require('../document/stateTransition/DocumentsBatchTransition/DocumentsBatchTransition'); +const DataContractCreateTransition = require('../dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition'); +const IdentityCreateTransition = require('../identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition'); +const IdentityTopUpTransition = require('../identity/stateTransition/IdentityTopUpTransition/IdentityTopUpTransition'); +const IdentityUpdateTransition = require('../identity/stateTransition/IdentityUpdateTransition/IdentityUpdateTransition'); + +const InvalidStateTransitionTypeError = require('./errors/InvalidStateTransitionTypeError'); +const DataContractNotPresentError = require('../errors/DataContractNotPresentError'); +const MissingDataContractIdError = require('./errors/MissingDataContractIdError'); + +const Identifier = require('../identifier/Identifier'); +const DataContractUpdateTransition = require('../dataContract/stateTransition/DataContractUpdateTransition/DataContractUpdateTransition'); +const StateTransitionExecutionContext = require('./StateTransitionExecutionContext'); + +const typesToClasses = { + [types.DATA_CONTRACT_CREATE]: DataContractCreateTransition, + [types.DATA_CONTRACT_UPDATE]: DataContractUpdateTransition, + [types.DOCUMENTS_BATCH]: DocumentsBatchTransition, + [types.IDENTITY_CREATE]: IdentityCreateTransition, + [types.IDENTITY_TOP_UP]: IdentityTopUpTransition, + [types.IDENTITY_UPDATE]: IdentityUpdateTransition, +}; + +/** + * @param {StateRepository} stateRepository + * @return {createStateTransition} + */ +function createStateTransitionFactory(stateRepository) { + /** + * @typedef {createStateTransition} + * @param {RawStateTransition} rawStateTransition + * @param {StateTransitionExecutionContext} [executionContext] + * @return {Promise} + */ + async function createStateTransition(rawStateTransition, executionContext) { + if (!typesToClasses[rawStateTransition.type]) { + throw new InvalidStateTransitionTypeError(rawStateTransition.type); + } + + if (!executionContext) { + // eslint-disable-next-line no-param-reassign + executionContext = new StateTransitionExecutionContext(); + } + + if (rawStateTransition.type === types.DOCUMENTS_BATCH) { + const dataContractPromises = rawStateTransition.transitions + .map(async (documentTransition) => { + if (!Object.prototype.hasOwnProperty.call(documentTransition, '$dataContractId')) { + throw new MissingDataContractIdError(documentTransition); + } + + const dataContractId = new Identifier(documentTransition.$dataContractId); + + const dataContract = await stateRepository.fetchDataContract( + dataContractId, + executionContext, + ); + + if (!dataContract) { + throw new DataContractNotPresentError(dataContractId); + } + + return dataContract; + }); + + const dataContracts = await Promise.all(dataContractPromises); + + const stateTransition = new typesToClasses[rawStateTransition.type]( + rawStateTransition, + dataContracts, + ); + + stateTransition.setExecutionContext(executionContext); + + return stateTransition; + } + + const stateTransition = new typesToClasses[rawStateTransition.type](rawStateTransition); + + stateTransition.setExecutionContext(executionContext); + + return stateTransition; + } + + return createStateTransition; +} + +module.exports = createStateTransitionFactory; diff --git a/packages/js-dpp/lib/stateTransition/errors/InvalidIdentityPublicKeyTypeError.js b/packages/js-dpp/lib/stateTransition/errors/InvalidIdentityPublicKeyTypeError.js new file mode 100644 index 00000000000..72b97f509f7 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/errors/InvalidIdentityPublicKeyTypeError.js @@ -0,0 +1,22 @@ +const DPPError = require('../../errors/DPPError'); + +class InvalidIdentityPublicKeyTypeError extends DPPError { + /** + * + * @param {number} publicKeyType + */ + constructor(publicKeyType) { + super('Invalid signature type'); + + this.publicKeyType = publicKeyType; + } + + /** + * @returns {number} + */ + getPublicKeyType() { + return this.publicKeyType; + } +} + +module.exports = InvalidIdentityPublicKeyTypeError; diff --git a/packages/js-dpp/lib/stateTransition/errors/InvalidSignaturePublicKeyError.js b/packages/js-dpp/lib/stateTransition/errors/InvalidSignaturePublicKeyError.js new file mode 100644 index 00000000000..4ff0885d857 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/errors/InvalidSignaturePublicKeyError.js @@ -0,0 +1,23 @@ +const DPPError = require('../../errors/DPPError'); + +class InvalidSignaturePublicKeyError extends DPPError { + /** + * + * @param {Buffer} signaturePublicKey + */ + constructor(signaturePublicKey) { + super('Invalid signature public key'); + + this.publicKey = signaturePublicKey; + } + + /** + * + * @returns {Buffer} + */ + getSignaturePublicKey() { + return this.publicKey; + } +} + +module.exports = InvalidSignaturePublicKeyError; diff --git a/packages/js-dpp/lib/stateTransition/errors/InvalidSignaturePublicKeySecurityLevelError.js b/packages/js-dpp/lib/stateTransition/errors/InvalidSignaturePublicKeySecurityLevelError.js new file mode 100644 index 00000000000..098f3861a4b --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/errors/InvalidSignaturePublicKeySecurityLevelError.js @@ -0,0 +1,35 @@ +const DPPError = require('../../errors/DPPError'); + +class InvalidSignaturePublicKeySecurityLevelError extends DPPError { + /** + * + * @param {number} publicKeySecurityLevel + * @param {number} requiredSecurityLevel + */ + constructor(publicKeySecurityLevel, requiredSecurityLevel) { + super(`Invalid public key security level ${publicKeySecurityLevel}. This state transition requires ${requiredSecurityLevel}.`); + + this.publicKeySecurityLevel = publicKeySecurityLevel; + this.requiredSecurityLevel = requiredSecurityLevel; + } + + /** + * Get mismatched public key + * + * @return {number} + */ + getPublicKeySecurityLevel() { + return this.publicKeySecurityLevel; + } + + /** + * Get required key security level + * + * @returns {number} + */ + getKeySecurityLevelRequirement() { + return this.requiredSecurityLevel; + } +} + +module.exports = InvalidSignaturePublicKeySecurityLevelError; diff --git a/packages/js-dpp/lib/stateTransition/errors/InvalidStateTransitionError.js b/packages/js-dpp/lib/stateTransition/errors/InvalidStateTransitionError.js new file mode 100644 index 00000000000..589e32fa988 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/errors/InvalidStateTransitionError.js @@ -0,0 +1,39 @@ +const DPPError = require('../../errors/DPPError'); + +class InvalidStateTransitionError extends DPPError { + /** + * @param {AbstractConsensusError[]} errors + * @param {RawStateTransition} [rawStateTransition] + */ + constructor(errors, rawStateTransition = undefined) { + let message = `Invalid State Transition: "${errors[0].message}"`; + if (errors.length > 1) { + message = `${message} and ${errors.length - 1} more`; + } + + super(message); + + this.errors = errors; + this.rawStateTransition = rawStateTransition; + } + + /** + * Get validation errors + * + * @return {AbstractConsensusError[]} + */ + getErrors() { + return this.errors; + } + + /** + * Get raw State Transition + * + * @return {RawStateTransition} + */ + getRawStateTransition() { + return this.rawStateTransition; + } +} + +module.exports = InvalidStateTransitionError; diff --git a/packages/js-dpp/lib/stateTransition/errors/InvalidStateTransitionTypeError.js b/packages/js-dpp/lib/stateTransition/errors/InvalidStateTransitionTypeError.js new file mode 100644 index 00000000000..1137d6fc153 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/errors/InvalidStateTransitionTypeError.js @@ -0,0 +1,23 @@ +const DPPError = require('../../errors/DPPError'); + +class InvalidStateTransitionTypeError extends DPPError { + /** + * @param {number} type + */ + constructor(type) { + super(`Invalid State Transition type ${type}`); + + this.type = type; + } + + /** + * Get State Transition type + * + * @return {number} + */ + getType() { + return this.type; + } +} + +module.exports = InvalidStateTransitionTypeError; diff --git a/packages/js-dpp/lib/stateTransition/errors/MissingDataContractIdError.js b/packages/js-dpp/lib/stateTransition/errors/MissingDataContractIdError.js new file mode 100644 index 00000000000..ff5a82d965a --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/errors/MissingDataContractIdError.js @@ -0,0 +1,31 @@ +const DPPError = require('../../errors/DPPError'); + +class MissingDataContractIdError extends DPPError { + /** + * @param { + * RawDocumentCreateTransition| + * RawDocumentReplaceTransition| + * RawDocumentDeleteTransition + * } rawDocumentTransition + */ + constructor(rawDocumentTransition) { + super('$dataContractId is not present'); + + this.rawDocumentTransition = rawDocumentTransition; + } + + /** + * Get Raw Document Transition + * + * @return { + * RawDocumentCreateTransition| + * RawDocumentReplaceTransition| + * RawDocumentDeleteTransition + * } + */ + getRawDocument() { + return this.rawDocument; + } +} + +module.exports = MissingDataContractIdError; diff --git a/packages/js-dpp/lib/stateTransition/errors/PublicKeyIsDisabledError.js b/packages/js-dpp/lib/stateTransition/errors/PublicKeyIsDisabledError.js new file mode 100644 index 00000000000..27ad2f182e5 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/errors/PublicKeyIsDisabledError.js @@ -0,0 +1,24 @@ +const DPPError = require('../../errors/DPPError'); + +class PublicKeyIsDisabledError extends DPPError { + /** + * + * @param {IdentityPublicKey} publicKey + */ + constructor(publicKey) { + super('Public key is disabled'); + + this.publicKey = publicKey; + } + + /** + * Get disabled public key + * + * @return {IdentityPublicKey} + */ + getPublicKey() { + return this.publicKey; + } +} + +module.exports = PublicKeyIsDisabledError; diff --git a/packages/js-dpp/lib/stateTransition/errors/PublicKeyMismatchError.js b/packages/js-dpp/lib/stateTransition/errors/PublicKeyMismatchError.js new file mode 100644 index 00000000000..eedf62b562f --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/errors/PublicKeyMismatchError.js @@ -0,0 +1,24 @@ +const DPPError = require('../../errors/DPPError'); + +class PublicKeyMismatchError extends DPPError { + /** + * + * @param {IdentityPublicKey} publicKey + */ + constructor(publicKey) { + super('Public key mismatched'); + + this.publicKey = publicKey; + } + + /** + * Get mismatched public key + * + * @return {IdentityPublicKey} + */ + getPublicKey() { + return this.publicKey; + } +} + +module.exports = PublicKeyMismatchError; diff --git a/packages/js-dpp/lib/stateTransition/errors/PublicKeySecurityLevelNotMetError.js b/packages/js-dpp/lib/stateTransition/errors/PublicKeySecurityLevelNotMetError.js new file mode 100644 index 00000000000..7e8ee65e7ce --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/errors/PublicKeySecurityLevelNotMetError.js @@ -0,0 +1,35 @@ +const DPPError = require('../../errors/DPPError'); + +class PublicKeySecurityLevelNotMetError extends DPPError { + /** + * + * @param {number} publicKeySecurityLevel + * @param {number} requiredSecurityLevel + */ + constructor(publicKeySecurityLevel, requiredSecurityLevel) { + super(`Invalid key security level ${publicKeySecurityLevel}. This state transition requires at least ${requiredSecurityLevel}`); + + this.publicKeySecurityLevel = publicKeySecurityLevel; + this.requiredSecurityLevel = requiredSecurityLevel; + } + + /** + * Get mismatched public key + * + * @return {number} + */ + getPublicKeySecurityLevel() { + return this.publicKeySecurityLevel; + } + + /** + * Get minimal required key security level + * + * @returns {number} + */ + getKeySecurityLevelRequirement() { + return this.requiredSecurityLevel; + } +} + +module.exports = PublicKeySecurityLevelNotMetError; diff --git a/packages/js-dpp/lib/stateTransition/errors/StateTransitionIsNotSignedError.js b/packages/js-dpp/lib/stateTransition/errors/StateTransitionIsNotSignedError.js new file mode 100644 index 00000000000..915f5bbd9c8 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/errors/StateTransitionIsNotSignedError.js @@ -0,0 +1,24 @@ +const DPPError = require('../../errors/DPPError'); + +class StateTransitionIsNotSignedError extends DPPError { + /** + * + * @param {AbstractStateTransition} stateTransition + */ + constructor(stateTransition) { + super('State Transition is not signed'); + + this.stateTransition = stateTransition; + } + + /** + * Get unsigned state transition + * + * @return {AbstractStateTransition} + */ + getStateTransition() { + return this.stateTransition; + } +} + +module.exports = StateTransitionIsNotSignedError; diff --git a/packages/js-dpp/lib/stateTransition/errors/WrongPublicKeyPurposeError.js b/packages/js-dpp/lib/stateTransition/errors/WrongPublicKeyPurposeError.js new file mode 100644 index 00000000000..cf94827ace5 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/errors/WrongPublicKeyPurposeError.js @@ -0,0 +1,35 @@ +const DPPError = require('../../errors/DPPError'); + +class WrongPublicKeyPurposeError extends DPPError { + /** + * + * @param {number} publicKeyPurpose + * @param {number} keyPurposeRequirement + */ + constructor(publicKeyPurpose, keyPurposeRequirement) { + super(`Invalid identity key purpose ${publicKeyPurpose}. This state transition requires ${keyPurposeRequirement}`); + + this.publicKeyPurpose = publicKeyPurpose; + this.keyPurposeRequirement = keyPurposeRequirement; + } + + /** + * Get mismatched public key + * + * @return {number} + */ + getPublicKeyPurpose() { + return this.publicKeyPurpose; + } + + /** + * Get required key purpose + * + * @returns {number} + */ + getKeyPurposeRequirement() { + return this.keyPurposeRequirement; + } +} + +module.exports = WrongPublicKeyPurposeError; diff --git a/packages/js-dpp/lib/stateTransition/errors/WrongStateTransitionTypeError.js b/packages/js-dpp/lib/stateTransition/errors/WrongStateTransitionTypeError.js new file mode 100644 index 00000000000..21040e54c67 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/errors/WrongStateTransitionTypeError.js @@ -0,0 +1,23 @@ +const DPPError = require('../../errors/DPPError'); + +class WrongStateTransitionTypeError extends DPPError { + /** + * @param {AbstractStateTransition} stateTransition + */ + constructor(stateTransition) { + super('Can\'t apply a state transition to a model, wrong state transition type'); + + this.stateTransition = stateTransition; + } + + /** + * Get failed state transition + * + * @return {AbstractStateTransition} + */ + getStateTransition() { + return this.stateTransition; + } +} + +module.exports = WrongStateTransitionTypeError; diff --git a/packages/js-dpp/lib/stateTransition/fee/calculateOperationFees.js b/packages/js-dpp/lib/stateTransition/fee/calculateOperationFees.js new file mode 100644 index 00000000000..698d9ba7af8 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/fee/calculateOperationFees.js @@ -0,0 +1,29 @@ +const { + FEE_MULTIPLIER, +} = require('./constants'); + +/** + * Calculate processing and storage fees based on operations + * + * @param {AbstractOperation[]} operations + * + * @returns {{ storageFee: number, processingFee: number }} + */ +function calculateOperationFees(operations) { + const fees = { + storageFee: 0, + processingFee: 0, + }; + + operations.forEach((operation) => { + fees.storageFee += operation.getProcessingCost(); + fees.processingFee += operation.getStorageCost(); + }); + + fees.storageFee *= FEE_MULTIPLIER; + fees.processingFee *= FEE_MULTIPLIER; + + return fees; +} + +module.exports = calculateOperationFees; diff --git a/packages/js-dpp/lib/stateTransition/fee/calculateStateTransitionFee.js b/packages/js-dpp/lib/stateTransition/fee/calculateStateTransitionFee.js new file mode 100644 index 00000000000..5ca7abe46cc --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/fee/calculateStateTransitionFee.js @@ -0,0 +1,25 @@ +const { + DEFAULT_USER_TIP, +} = require('./constants'); + +const calculateOperationFees = require('./calculateOperationFees'); + +/** + * @typedef {calculateStateTransitionFee} + * @param {AbstractStateTransition} stateTransition + * @return {number} + */ +function calculateStateTransitionFee(stateTransition) { + const executionContext = stateTransition.getExecutionContext(); + + const { storageFee, processingFee } = calculateOperationFees( + executionContext.getOperations(), + ); + + // Is not implemented yet + const storageRefund = 0; + + return (storageFee + processingFee) + DEFAULT_USER_TIP - storageRefund; +} + +module.exports = calculateStateTransitionFee; diff --git a/packages/js-dpp/lib/stateTransition/fee/constants.js b/packages/js-dpp/lib/stateTransition/fee/constants.js new file mode 100644 index 00000000000..37a82bc1443 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/fee/constants.js @@ -0,0 +1,18 @@ +const { TYPES: IDENTITY_KEY_TYPES } = require('../../identity/IdentityPublicKey'); + +module.exports = { + BASE_ST_PROCESSING_FEE: 10000, // 84000 + FEE_MULTIPLIER: 2, + DEFAULT_USER_TIP: 0, + STORAGE_CREDIT_PER_BYTE: 5000, + PROCESSING_CREDIT_PER_BYTE: 12, + DELETE_BASE_PROCESSING_COST: 2000, // 20000 + READ_BASE_PROCESSING_COST: 8400, // 8400 + WRITE_BASE_PROCESSING_COST: 6000, // 60000 + VERIFY_SIGNATURE_COSTS: { + [IDENTITY_KEY_TYPES.ECDSA_SECP256K1]: 3000, + [IDENTITY_KEY_TYPES.BLS12_381]: 6000, + [IDENTITY_KEY_TYPES.ECDSA_HASH160]: 3000, + [IDENTITY_KEY_TYPES.BIP13_SCRIPT_HASH]: 6000, + }, +}; diff --git a/packages/js-dpp/lib/stateTransition/fee/createOperationFromJSON.js b/packages/js-dpp/lib/stateTransition/fee/createOperationFromJSON.js new file mode 100644 index 00000000000..07b5569963c --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/fee/createOperationFromJSON.js @@ -0,0 +1,25 @@ +const ReadOperation = require('./operations/ReadOperation'); +const WriteOperation = require('./operations/WriteOperation'); +const DeleteOperation = require('./operations/DeleteOperation'); +const PreCalculatedOperation = require('./operations/PreCalculatedOperation'); +const SignatureVerificationOperation = require('./operations/SignatureVerificationOperation'); + +const OPERATIONS = { + read: ReadOperation, + write: WriteOperation, + delete: DeleteOperation, + preCalculated: PreCalculatedOperation, + signatureVerification: SignatureVerificationOperation, +}; + +function createOperationFromJSON(json) { + const OperationClass = OPERATIONS[json.type]; + + if (OperationClass) { + throw new Error(`Operation ${json.type} is not supported`); + } + + return OperationClass.fromJSON(json); +} + +module.exports = createOperationFromJSON; diff --git a/packages/js-dpp/lib/stateTransition/fee/operations/AbstractOperation.js b/packages/js-dpp/lib/stateTransition/fee/operations/AbstractOperation.js new file mode 100644 index 00000000000..ed291c36a9d --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/fee/operations/AbstractOperation.js @@ -0,0 +1,30 @@ +/** + * @abstract + */ +class AbstractOperation { + /** + * @abstract + * @returns {number} + */ + getProcessingCost() { + throw new Error('Not implemented'); + } + + /** + * @abstract + * @returns {number} + */ + getStorageCost() { + throw new Error('Not implemented'); + } + + /** + * @abstract + * @returns {Object} + */ + toJSON() { + throw new Error('Not implemented'); + } +} + +module.exports = AbstractOperation; diff --git a/packages/js-dpp/lib/stateTransition/fee/operations/DeleteOperation.js b/packages/js-dpp/lib/stateTransition/fee/operations/DeleteOperation.js new file mode 100644 index 00000000000..67633e0011f --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/fee/operations/DeleteOperation.js @@ -0,0 +1,60 @@ +const AbstractOperation = require('./AbstractOperation'); + +const { + DELETE_BASE_PROCESSING_COST, + PROCESSING_CREDIT_PER_BYTE, + STORAGE_CREDIT_PER_BYTE, +} = require('../constants'); + +class DeleteOperation extends AbstractOperation { + /** + * @param {number} keySize + * @param {number} valueSize + */ + constructor(keySize, valueSize) { + super(); + + this.keySize = keySize; + this.valueSize = valueSize; + } + + /** + * Get CPU cost of the operation + * + * @returns {number} + */ + getProcessingCost() { + return ((this.keySize + this.valueSize) * PROCESSING_CREDIT_PER_BYTE) + + DELETE_BASE_PROCESSING_COST; + } + + /** + * Get storage cost of the operation + * + * @returns {number} + */ + getStorageCost() { + return -((this.keySize + this.valueSize) * STORAGE_CREDIT_PER_BYTE); + } + + /** + * @return {{keySize: number, type: string, valueSize: number}} + */ + toJSON() { + return { + type: 'delete', + keySize: this.keySize, + valueSize: this.valueSize, + }; + } + + /** + * @param {{keySize: number, type: string, valueSize: number}} json + * @return {DeleteOperation} + */ + static fromJSON(json) { + return new DeleteOperation(json.keySize, json.valueSize); + } +} + +module.exports = DeleteOperation; diff --git a/packages/js-dpp/lib/stateTransition/fee/operations/PreCalculatedOperation.js b/packages/js-dpp/lib/stateTransition/fee/operations/PreCalculatedOperation.js new file mode 100644 index 00000000000..b910566f6cf --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/fee/operations/PreCalculatedOperation.js @@ -0,0 +1,53 @@ +const AbstractOperation = require('./AbstractOperation'); + +class PreCalculatedOperation extends AbstractOperation { + /** + * @param {number} storageCost + * @param {number} processingCost + */ + constructor(storageCost, processingCost) { + super(); + + this.storageCost = storageCost || 0; + this.processingCost = processingCost || 0; + } + + /** + * Get CPU cost of the operation + * + * @returns {number} + */ + getProcessingCost() { + return this.processingCost; + } + + /** + * Get storage cost of the operation + * + * @returns {number} + */ + getStorageCost() { + return this.storageCost; + } + + /** + * @return {{processingCost: number, type: string, storageCost: number}} + */ + toJSON() { + return { + type: 'preCalculated', + storageCost: this.getStorageCost(), + processingCost: this.getProcessingCost(), + }; + } + + /** + * @param {{storageCost: number, type: string, processingCost: number}} json + * @return {PreCalculatedOperation} + */ + static fromJSON(json) { + return new PreCalculatedOperation(json.storageCost, json.processingCost); + } +} + +module.exports = PreCalculatedOperation; diff --git a/packages/js-dpp/lib/stateTransition/fee/operations/ReadOperation.js b/packages/js-dpp/lib/stateTransition/fee/operations/ReadOperation.js new file mode 100644 index 00000000000..e9462861c9a --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/fee/operations/ReadOperation.js @@ -0,0 +1,55 @@ +const AbstractOperation = require('./AbstractOperation'); + +const { + READ_BASE_PROCESSING_COST, + PROCESSING_CREDIT_PER_BYTE, +} = require('../constants'); + +class ReadOperation extends AbstractOperation { + /** + * @param {number} valueSize + */ + constructor(valueSize) { + super(); + + this.valueSize = valueSize; + } + + /** + * Get CPU cost of the operation + * + * @returns {number} + */ + getProcessingCost() { + return READ_BASE_PROCESSING_COST + this.valueSize * PROCESSING_CREDIT_PER_BYTE; + } + + /** + * Get storage cost of the operation + * + * @returns {number} + */ + getStorageCost() { + return 0; + } + + /** + * @return {{valueSize: number, type: string}} + */ + toJSON() { + return { + type: 'read', + valueSize: this.valueSize, + }; + } + + /** + * @param {{type: string, valueSize: number}} json + * @return {ReadOperation} + */ + static fromJSON(json) { + return new ReadOperation(json.valueSize); + } +} + +module.exports = ReadOperation; diff --git a/packages/js-dpp/lib/stateTransition/fee/operations/SignatureVerificationOperation.js b/packages/js-dpp/lib/stateTransition/fee/operations/SignatureVerificationOperation.js new file mode 100644 index 00000000000..8f069ef0f2b --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/fee/operations/SignatureVerificationOperation.js @@ -0,0 +1,60 @@ +const AbstractOperation = require('./AbstractOperation'); + +const DPPError = require('../../../errors/DPPError'); + +const { + VERIFY_SIGNATURE_COSTS, +} = require('../constants'); + +class SignatureVerificationOperation extends AbstractOperation { + /** + * @param {number} signatureType + */ + constructor(signatureType) { + super(); + + if (!VERIFY_SIGNATURE_COSTS[signatureType]) { + throw new DPPError(`Operation cost for verification of identity key type ${signatureType} is not defined`); + } + + this.signatureType = signatureType; + } + + /** + * Get CPU cost of the operation + * + * @returns {number} + */ + getProcessingCost() { + return VERIFY_SIGNATURE_COSTS[this.signatureType]; + } + + /** + * Get storage cost of the operation + * + * @returns {number} + */ + getStorageCost() { + return 0; + } + + /** + * @return {{signatureType: number, type: string}} + */ + toJSON() { + return { + type: 'signatureVerification', + signatureType: this.signatureType, + }; + } + + /** + * @param {{signatureType: number, type: string}} json + * @return {SignatureVerificationOperation} + */ + static fromJSON(json) { + return new SignatureVerificationOperation(json.signatureType); + } +} + +module.exports = SignatureVerificationOperation; diff --git a/packages/js-dpp/lib/stateTransition/fee/operations/WriteOperation.js b/packages/js-dpp/lib/stateTransition/fee/operations/WriteOperation.js new file mode 100644 index 00000000000..d4006a2d7b2 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/fee/operations/WriteOperation.js @@ -0,0 +1,60 @@ +const AbstractOperation = require('./AbstractOperation'); + +const { + WRITE_BASE_PROCESSING_COST, + PROCESSING_CREDIT_PER_BYTE, + STORAGE_CREDIT_PER_BYTE, +} = require('../constants'); + +class WriteOperation extends AbstractOperation { + /** + * @param {number} keySize + * @param {number} valueSize + */ + constructor(keySize, valueSize) { + super(); + + this.keySize = keySize; + this.valueSize = valueSize; + } + + /** + * Get CPU cost of the operation + * + * @returns {number} + */ + getProcessingCost() { + return ((this.keySize + this.valueSize) * PROCESSING_CREDIT_PER_BYTE) + + WRITE_BASE_PROCESSING_COST; + } + + /** + * Get storage cost of the operation + * + * @returns {number} + */ + getStorageCost() { + return (this.keySize + this.valueSize) * STORAGE_CREDIT_PER_BYTE; + } + + /** + * @return {{valueSize: number, type: string, keySize: number}} + */ + toJSON() { + return { + type: 'write', + keySize: this.keySize, + valueSize: this.valueSize, + }; + } + + /** + * @param {{keySize: number, type: string, valueSize: number}} json + * @return {WriteOperation} + */ + static fromJSON(json) { + return new WriteOperation(json.keySize, json.valueSize); + } +} + +module.exports = WriteOperation; diff --git a/packages/js-dpp/lib/stateTransition/stateTransitionTypes.js b/packages/js-dpp/lib/stateTransition/stateTransitionTypes.js new file mode 100644 index 00000000000..35178a75bef --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/stateTransitionTypes.js @@ -0,0 +1,8 @@ +module.exports = { + DATA_CONTRACT_CREATE: 0, + DOCUMENTS_BATCH: 1, + IDENTITY_CREATE: 2, + IDENTITY_TOP_UP: 3, + DATA_CONTRACT_UPDATE: 4, + IDENTITY_UPDATE: 5, +}; diff --git a/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionBasicFactory.js b/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionBasicFactory.js new file mode 100644 index 00000000000..ce23cfc5141 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionBasicFactory.js @@ -0,0 +1,74 @@ +const ValidationResult = require('../../validation/ValidationResult'); + +const MissingStateTransitionTypeError = require('../../errors/consensus/basic/stateTransition/MissingStateTransitionTypeError'); +const InvalidStateTransitionTypeError = require('../../errors/consensus/basic/stateTransition/InvalidStateTransitionTypeError'); +const StateTransitionMaxSizeExceededError = require('../../errors/consensus/basic/stateTransition/StateTransitionMaxSizeExceededError'); +const MaxEncodedBytesReachedError = require('../../util/errors/MaxEncodedBytesReachedError'); + +/** + * @param {Object.} validationFunctionsByType + * @param {createStateTransition} createStateTransition + * @return {validateStateTransitionBasic} + */ +function validateStateTransitionBasicFactory( + validationFunctionsByType, + createStateTransition, +) { + /** + * @typedef validateStateTransitionBasic + * @param {RawStateTransition} rawStateTransition + * @param {StateTransitionExecutionContext} executionContext + */ + async function validateStateTransitionBasic(rawStateTransition, executionContext) { + const result = new ValidationResult(); + + if (!Object.prototype.hasOwnProperty.call(rawStateTransition, 'type')) { + result.addError( + new MissingStateTransitionTypeError(), + ); + + return result; + } + + if (!validationFunctionsByType[rawStateTransition.type]) { + result.addError( + new InvalidStateTransitionTypeError(rawStateTransition.type), + ); + + return result; + } + + const validationFunction = validationFunctionsByType[rawStateTransition.type]; + + result.merge( + await validationFunction(rawStateTransition, executionContext), + ); + + if (!result.isValid()) { + return result; + } + + const stateTransition = await createStateTransition(rawStateTransition, executionContext); + + try { + stateTransition.toBuffer(); + } catch (e) { + if (e instanceof MaxEncodedBytesReachedError) { + result.addError( + new StateTransitionMaxSizeExceededError( + Math.floor(e.getPayload().length / 1024), + e.getMaxSizeKBytes(), + ), + ); + } else { + throw e; + } + } + + return result; + } + + return validateStateTransitionBasic; +} + +module.exports = validateStateTransitionBasicFactory; diff --git a/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionFeeFactory.js b/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionFeeFactory.js new file mode 100644 index 00000000000..04d4e97ef91 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionFeeFactory.js @@ -0,0 +1,97 @@ +const ValidationResult = require('../../validation/ValidationResult'); + +const InvalidStateTransitionTypeError = require('../errors/InvalidStateTransitionTypeError'); +const BalanceIsNotEnoughError = require('../../errors/consensus/fee/BalanceIsNotEnoughError'); + +const stateTransitionTypes = require('../stateTransitionTypes'); +const { convertSatoshiToCredits } = require('../../identity/creditsConverter'); + +/** + * Validate state transition fee + * + * @param {StateRepository} stateRepository + * @param {calculateStateTransitionFee} calculateStateTransitionFee + * @param {fetchAssetLockTransactionOutput} fetchAssetLockTransactionOutput + * @return {validateStateTransitionFee} + */ +function validateStateTransitionFeeFactory( + stateRepository, + calculateStateTransitionFee, + fetchAssetLockTransactionOutput, +) { + /** + * @typedef validateStateTransitionFee + * @param {AbstractStateTransition} stateTransition + * @return {ValidationResult} + */ + async function validateStateTransitionFee(stateTransition) { + const result = new ValidationResult(); + + const executionContext = stateTransition.getExecutionContext(); + + let balance; + switch (stateTransition.getType()) { + case stateTransitionTypes.IDENTITY_TOP_UP: + case stateTransitionTypes.IDENTITY_CREATE: { + const output = await fetchAssetLockTransactionOutput( + stateTransition.getAssetLockProof(), + executionContext, + ); + + balance = convertSatoshiToCredits(output.satoshis); + + if (stateTransition.getType() === stateTransitionTypes.IDENTITY_TOP_UP) { + const identityId = stateTransition.getOwnerId(); + + const identity = await stateRepository.fetchIdentity(identityId, executionContext); + + if (executionContext.isDryRun()) { + return result; + } + + balance += identity.getBalance(); + } + + break; + } + case stateTransitionTypes.DATA_CONTRACT_CREATE: + case stateTransitionTypes.DATA_CONTRACT_UPDATE: + case stateTransitionTypes.DOCUMENTS_BATCH: + case stateTransitionTypes.IDENTITY_UPDATE: { + const identityId = stateTransition.getOwnerId(); + + const identity = await stateRepository.fetchIdentity(identityId, executionContext); + + if (executionContext.isDryRun()) { + return result; + } + + balance = identity.getBalance(); + + break; + } + default: + throw new InvalidStateTransitionTypeError(stateTransition.getType()); + } + + if (executionContext.isDryRun()) { + return result; + } + + // We could use `stateTransition.calculateFee()` but + // `calculateStateTransitionFee` is easier to mock in test + const fee = calculateStateTransitionFee(stateTransition); + + if (balance < fee) { + result.addError( + new BalanceIsNotEnoughError(balance, fee), + ); + } + + return result; + } + + return validateStateTransitionFee; +} + +module.exports = validateStateTransitionFeeFactory; diff --git a/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionIdentitySignatureFactory.js b/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionIdentitySignatureFactory.js new file mode 100644 index 00000000000..f2717482704 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionIdentitySignatureFactory.js @@ -0,0 +1,147 @@ +const IdentityPublicKey = require('../../identity/IdentityPublicKey'); +const InvalidIdentityPublicKeyTypeConsensusError = require('../../errors/consensus/signature/InvalidIdentityPublicKeyTypeError'); +const InvalidStateTransitionSignatureConsensusError = require('../../errors/consensus/signature/InvalidStateTransitionSignatureError'); +const MissingPublicKeyConsensusError = require('../../errors/consensus/signature/MissingPublicKeyError'); +const InvalidSignaturePublicKeySecurityLevelConsensusError = require('../../errors/consensus/signature/InvalidSignaturePublicKeySecurityLevelError'); +const PublicKeySecurityLevelNotMetConsensusError = require('../../errors/consensus/signature/PublicKeySecurityLevelNotMetError'); +const WrongPublicKeyPurposeConsensusError = require('../../errors/consensus/signature/WrongPublicKeyPurposeError'); +const PublicKeyIsDisabledConsensusError = require('../../errors/consensus/signature/PublicKeyIsDisabledError'); +const DPPError = require('../../errors/DPPError'); +const InvalidSignaturePublicKeySecurityLevelError = require('../errors/InvalidSignaturePublicKeySecurityLevelError'); +const PublicKeySecurityLevelNotMetError = require('../errors/PublicKeySecurityLevelNotMetError'); +const WrongPublicKeyPurposeError = require('../errors/WrongPublicKeyPurposeError'); +const PublicKeyIsDisabledError = require('../errors/PublicKeyIsDisabledError'); +const SignatureVerificationOperation = require('../fee/operations/SignatureVerificationOperation'); +const ValidationResult = require('../../validation/ValidationResult'); +const IdentityNotFoundError = require('../../errors/consensus/signature/IdentityNotFoundError'); +const StateTransitionExecutionContext = require('../StateTransitionExecutionContext'); +const InvalidIdentityPublicKeyTypeError = require('../errors/InvalidIdentityPublicKeyTypeError'); + +const supportedPublicKeyTypes = [ + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + IdentityPublicKey.TYPES.BLS12_381, + IdentityPublicKey.TYPES.ECDSA_HASH160, +]; + +/** + * Validate state transition signature + * + * @param {StateRepository} stateRepository + * @returns {validateStateTransitionIdentitySignature} + */ +function validateStateTransitionIdentitySignatureFactory( + stateRepository, +) { + /** + * @typedef validateStateTransitionIdentitySignature + * @param { + * DataContractCreateTransition| + * DocumentsBatchTransition + * } stateTransition + * @returns {Promise} + */ + async function validateStateTransitionIdentitySignature(stateTransition) { + const result = new ValidationResult(); + + const executionContext = stateTransition.getExecutionContext(); + + const ownerId = stateTransition.getOwnerId(); + + // We use temporary execution context without dry run, + // because despite the dryRun, we need to get the + // identity to proceed with following logic + const tmpExecutionContext = new StateTransitionExecutionContext(); + + // Owner must exist + const identity = await stateRepository.fetchIdentity(ownerId, tmpExecutionContext); + + // Collect operations back from temporary context + executionContext.addOperation(...tmpExecutionContext.getOperations()); + + if (!identity) { + result.addError(new IdentityNotFoundError(ownerId.toBuffer())); + + return result; + } + + // Signature must be valid + const publicKey = identity.getPublicKeyById(stateTransition.getSignaturePublicKeyId()); + + if (!publicKey) { + result.addError( + new MissingPublicKeyConsensusError(stateTransition.getSignaturePublicKeyId()), + ); + + return result; + } + + if (!supportedPublicKeyTypes.includes(publicKey.getType())) { + result.addError( + new InvalidIdentityPublicKeyTypeConsensusError(publicKey.getType()), + ); + + return result; + } + + const operation = new SignatureVerificationOperation(publicKey.getType()); + + executionContext.addOperation(operation); + + if (executionContext.isDryRun()) { + return result; + } + + try { + const signatureIsValid = await stateTransition.verifySignature(publicKey); + + if (!signatureIsValid) { + result.addError( + new InvalidStateTransitionSignatureConsensusError(stateTransition), + ); + } + } catch (e) { + if (e instanceof InvalidSignaturePublicKeySecurityLevelError) { + result.addError( + new InvalidSignaturePublicKeySecurityLevelConsensusError( + e.getPublicKeySecurityLevel(), + e.getKeySecurityLevelRequirement(), + ), + ); + } else if (e instanceof PublicKeySecurityLevelNotMetError) { + result.addError( + new PublicKeySecurityLevelNotMetConsensusError( + e.getPublicKeySecurityLevel(), + e.getKeySecurityLevelRequirement(), + ), + ); + } else if (e instanceof WrongPublicKeyPurposeError) { + result.addError( + new WrongPublicKeyPurposeConsensusError( + e.getPublicKeyPurpose(), + e.getKeyPurposeRequirement(), + ), + ); + } else if (e instanceof PublicKeyIsDisabledError) { + result.addError( + new PublicKeyIsDisabledConsensusError(e.getPublicKey().getId()), + ); + } else if (e instanceof InvalidIdentityPublicKeyTypeError) { + result.addError( + new InvalidIdentityPublicKeyTypeConsensusError(e.getPublicKeyType()), + ); + } else if (e instanceof DPPError) { + result.addError( + new InvalidStateTransitionSignatureConsensusError(), + ); + } else { + throw e; + } + } + + return result; + } + + return validateStateTransitionIdentitySignature; +} + +module.exports = validateStateTransitionIdentitySignatureFactory; diff --git a/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionKeySignatureFactory.js b/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionKeySignatureFactory.js new file mode 100644 index 00000000000..b14fb0f47e1 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionKeySignatureFactory.js @@ -0,0 +1,59 @@ +const InvalidStateTransitionSignatureError = require('../../errors/consensus/signature/InvalidStateTransitionSignatureError'); + +const ValidationResult = require('../../validation/ValidationResult'); +const SignatureVerificationOperation = require('../fee/operations/SignatureVerificationOperation'); +const { TYPES } = require('../../identity/IdentityPublicKey'); + +/** + * @param {Function} verifyHashSignature + * @param {fetchAssetLockPublicKeyHash} fetchAssetLockPublicKeyHash + * @returns {validateStateTransitionKeySignature} + */ +function validateStateTransitionKeySignatureFactory( + verifyHashSignature, + fetchAssetLockPublicKeyHash, +) { + /** + * @typedef {validateStateTransitionKeySignature} + * @param {IdentityCreateTransition|IdentityTopUpTransition} stateTransition + * @returns {Promise} + */ + async function validateStateTransitionKeySignature(stateTransition) { + const result = new ValidationResult(); + + const executionContext = stateTransition.getExecutionContext(); + + const stateTransitionHash = stateTransition.hash({ skipSignature: true }); + + const publicKeyHash = await fetchAssetLockPublicKeyHash( + stateTransition.getAssetLockProof(), + executionContext, + ); + + const operation = new SignatureVerificationOperation(TYPES.ECDSA_SECP256K1); + + executionContext.addOperation(operation); + + let signatureIsVerified; + + try { + signatureIsVerified = verifyHashSignature( + stateTransitionHash, + stateTransition.getSignature(), + publicKeyHash, + ); + } catch (e) { + signatureIsVerified = false; + } + + if (!signatureIsVerified) { + result.addError(new InvalidStateTransitionSignatureError()); + } + + return result; + } + + return validateStateTransitionKeySignature; +} + +module.exports = validateStateTransitionKeySignatureFactory; diff --git a/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionStateFactory.js b/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionStateFactory.js new file mode 100644 index 00000000000..9bfbedd02f3 --- /dev/null +++ b/packages/js-dpp/lib/stateTransition/validation/validateStateTransitionStateFactory.js @@ -0,0 +1,26 @@ +const InvalidStateTransitionTypeError = require('../errors/InvalidStateTransitionTypeError'); + +/** + * @param {Object} validationFunctions + * @return {validateStateTransitionState} + */ +function validateStateTransitionStateFactory(validationFunctions) { + /** + * @typedef {validateStateTransitionState} + * @param {AbstractStateTransition} stateTransition + * @return {ValidationResult} + */ + async function validateStateTransitionState(stateTransition) { + const validationFunction = validationFunctions[stateTransition.getType()]; + + if (!validationFunction) { + throw new InvalidStateTransitionTypeError(stateTransition.getType()); + } + + return validationFunction(stateTransition); + } + + return validateStateTransitionState; +} + +module.exports = validateStateTransitionStateFactory; diff --git a/packages/js-dpp/lib/test/.eslintrc b/packages/js-dpp/lib/test/.eslintrc new file mode 100644 index 00000000000..4c2b11fe817 --- /dev/null +++ b/packages/js-dpp/lib/test/.eslintrc @@ -0,0 +1,9 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + } +} diff --git a/packages/js-dpp/lib/test/bootstrap.js b/packages/js-dpp/lib/test/bootstrap.js new file mode 100644 index 00000000000..ea15af6ab9d --- /dev/null +++ b/packages/js-dpp/lib/test/bootstrap.js @@ -0,0 +1,27 @@ +const { expect, use } = require('chai'); +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); +const dirtyChai = require('dirty-chai'); +const chaiAsPromised = require('chai-as-promised'); +const chaiString = require('chai-string'); +const chaiExclude = require('chai-exclude'); + +use(sinonChai); +use(chaiAsPromised); +use(dirtyChai); +use(chaiString); +use(chaiExclude); + +beforeEach(function beforeEach() { + if (!this.sinonSandbox) { + this.sinonSandbox = sinon.createSandbox(); + } else { + this.sinonSandbox.restore(); + } +}); + +afterEach(function afterEach() { + this.sinonSandbox.restore(); +}); + +global.expect = expect; diff --git a/packages/js-dpp/lib/test/expect/expectError.js b/packages/js-dpp/lib/test/expect/expectError.js new file mode 100644 index 00000000000..b8ad446b1d5 --- /dev/null +++ b/packages/js-dpp/lib/test/expect/expectError.js @@ -0,0 +1,30 @@ +const { expect } = require('chai'); + +const ValidationResult = require('../../validation/ValidationResult'); +const AbstractConsensusError = require('../../errors/consensus/AbstractConsensusError'); +const JsonSchemaError = require('../../errors/consensus/basic/JsonSchemaError'); + +const expectError = { + /** + * @param {ValidationResult} result + * @param {AbstractConsensusError} [errorClass] + * @param {number} [count] + */ + expectValidationError(result, errorClass = AbstractConsensusError, count = 1) { + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.getErrors()).to.have.lengthOf(count); + + result.getErrors().forEach((error) => expect(error).to.be.an.instanceOf(errorClass)); + }, + + /** + * + * @param {ValidationResult} result + * @param [count] + */ + expectJsonSchemaError(result, count = 1) { + expectError.expectValidationError(result, JsonSchemaError, count); + }, +}; + +module.exports = expectError; diff --git a/packages/js-dpp/lib/test/fixtures/getChainAssetLockProofFixture.js b/packages/js-dpp/lib/test/fixtures/getChainAssetLockProofFixture.js new file mode 100644 index 00000000000..8d3f4ae9d0b --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getChainAssetLockProofFixture.js @@ -0,0 +1,21 @@ +const ChainAssetLockProof = require('../../identity/stateTransition/assetLockProof/chain/ChainAssetLockProof'); + +function getChainAssetLockProofFixture() { + const outPoint = { + outpointHash: '6e200d059fb567ba19e92f5c2dcd3dde522fd4e0a50af223752db16158dabb1d', + outpointIndex: 0, + }; + + const binaryTransactionHash = Buffer.from(outPoint.outpointHash, 'hex'); + const indexBuffer = Buffer.alloc(4); + + indexBuffer.writeUInt32LE(outPoint.outpointIndex, 0); + + return new ChainAssetLockProof({ + type: 1, + coreChainLockedHeight: 42, + outPoint: Buffer.concat([binaryTransactionHash, indexBuffer]), + }); +} + +module.exports = getChainAssetLockProofFixture; diff --git a/packages/js-dpp/lib/test/fixtures/getDashPayContractFixture.js b/packages/js-dpp/lib/test/fixtures/getDashPayContractFixture.js new file mode 100644 index 00000000000..f2153dfe4f6 --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getDashPayContractFixture.js @@ -0,0 +1,16 @@ +const dashPaySchema = require('@dashevo/dashpay-contract/schema/dashpay.schema.json'); +const DataContractFactory = require('../../dataContract/DataContractFactory'); + +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); + +const createDPPMock = require('../mocks/createDPPMock'); + +const ownerId = generateRandomIdentifier(); + +/** + * @return {DataContract} + */ +module.exports = function getDataContractFixture() { + const factory = new DataContractFactory(createDPPMock(), () => {}); + return factory.create(ownerId, dashPaySchema); +}; diff --git a/packages/js-dpp/lib/test/fixtures/getDashPayDocumentFixture.js b/packages/js-dpp/lib/test/fixtures/getDashPayDocumentFixture.js new file mode 100644 index 00000000000..c7036824fc2 --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getDashPayDocumentFixture.js @@ -0,0 +1,37 @@ +const getDashPayContractFixture = require('./getDashPayContractFixture'); +const DocumentFactory = require('../../document/DocumentFactory'); +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); +const createDPPMock = require('../mocks/createDPPMock'); + +const ownerId = generateRandomIdentifier(); +const dataContract = getDashPayContractFixture(); + +/** + * @return {Document} + */ +function getContactRequestDocumentFixture(options = {}) { + const factory = new DocumentFactory( + createDPPMock(), + () => ({ + isValid: () => true, + }), + () => {}, + ); + + const data = { + toUserId: Buffer.alloc(32), + encryptedPublicKey: Buffer.alloc(96), + senderKeyIndex: 0, + recipientKeyIndex: 0, + accountReference: 0, + ...options, + }; + + return factory.create(dataContract, ownerId, 'contactRequest', data); +} + +module.exports = { + getContactRequestDocumentFixture, +}; + +module.exports.dataContract = dataContract; diff --git a/packages/js-dpp/lib/test/fixtures/getDataContractFixture.js b/packages/js-dpp/lib/test/fixtures/getDataContractFixture.js new file mode 100644 index 00000000000..d6b61d1f5b9 --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getDataContractFixture.js @@ -0,0 +1,249 @@ +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); + +const DataContractFactory = require('../../dataContract/DataContractFactory'); + +const randomOwnerId = generateRandomIdentifier(); + +const Identifier = require('../../identifier/Identifier'); +const createDPPMock = require('../mocks/createDPPMock'); + +/** + * + * @param {Buffer} [ownerId] + * @return {DataContract} + */ +module.exports = function getDataContractFixture(ownerId = randomOwnerId) { + const documents = { + niceDocument: { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['$createdAt'], + additionalProperties: false, + }, + prettyDocument: { + type: 'object', + properties: { + lastName: { + type: 'string', + }, + }, + required: ['lastName', '$updatedAt'], + additionalProperties: false, + }, + indexedDocument: { + type: 'object', + indices: [ + { + name: 'index1', + properties: [ + { $ownerId: 'asc' }, + { firstName: 'asc' }, + ], + unique: true, + }, + { + name: 'index2', + properties: [ + { $ownerId: 'asc' }, + { lastName: 'asc' }, + ], + unique: true, + }, + { + name: 'index3', + properties: [ + { lastName: 'asc' }, + ], + unique: false, + }, + { + name: 'index4', + properties: [ + { $createdAt: 'asc' }, + { $updatedAt: 'asc' }, + ], + }, + { + name: 'index5', + properties: [ + { $updatedAt: 'asc' }, + ], + }, + { + name: 'index6', + properties: [ + { $createdAt: 'asc' }, + ], + }, + ], + properties: { + firstName: { + type: 'string', + maxLength: 63, + }, + lastName: { + type: 'string', + maxLength: 63, + }, + }, + required: ['firstName', '$createdAt', '$updatedAt', 'lastName'], + additionalProperties: false, + }, + // indexedArray: { + // type: 'object', + // indices: [ + // { + // name: 'index1', + // properties: [ + // { mentions: 'asc' }, + // ], + // }, + // ], + // properties: { + // mentions: { + // type: 'array', + // prefixItems: [ + // { + // type: 'string', + // maxLength: 100, + // }, + // ], + // minItems: 1, + // maxItems: 5, + // items: false, + // }, + // }, + // additionalProperties: false, + // }, + noTimeDocument: { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + additionalProperties: false, + }, + uniqueDates: { + type: 'object', + indices: [ + { + name: 'index1', + properties: [ + { $createdAt: 'asc' }, + { $updatedAt: 'asc' }, + ], + unique: true, + }, + { + name: 'index2', + properties: [ + { $updatedAt: 'asc' }, + ], + }, + ], + properties: { + firstName: { + type: 'string', + }, + lastName: { + type: 'string', + }, + }, + required: ['firstName', '$createdAt', '$updatedAt'], + additionalProperties: false, + }, + withByteArrays: { + type: 'object', + indices: [ + { + name: 'index1', + properties: [ + { byteArrayField: 'asc' }, + ], + }, + ], + properties: { + byteArrayField: { + type: 'array', + byteArray: true, + maxItems: 16, + }, + identifierField: { + type: 'array', + byteArray: true, + contentMediaType: Identifier.MEDIA_TYPE, + minItems: 32, + maxItems: 32, + }, + }, + required: ['byteArrayField'], + additionalProperties: false, + }, + optionalUniqueIndexedDocument: { + type: 'object', + properties: { + firstName: { + type: 'string', + maxLength: 63, + }, + lastName: { + type: 'string', + maxLength: 63, + }, + country: { + type: 'string', + maxLength: 63, + }, + city: { + type: 'string', + maxLength: 63, + }, + }, + indices: [ + { + name: 'index1', + properties: [ + { firstName: 'asc' }, + ], + unique: true, + }, + { + name: 'index2', + properties: [ + { $ownerId: 'asc' }, + { firstName: 'asc' }, + { lastName: 'asc' }, + ], + unique: true, + }, + { + name: 'index3', + properties: [ + { country: 'asc' }, + { city: 'asc' }, + ], + unique: true, + }, + ], + required: ['firstName', 'lastName'], + additionalProperties: false, + }, + }; + + const factory = new DataContractFactory(createDPPMock(), () => {}); + + const dataContract = factory.create(ownerId, documents); + + // dataContract.setDefinitions({ + // lastName: { + // type: 'string', + // }, + // }); + + return dataContract; +}; diff --git a/packages/js-dpp/lib/test/fixtures/getDocumentTransitionsFixture.js b/packages/js-dpp/lib/test/fixtures/getDocumentTransitionsFixture.js new file mode 100644 index 00000000000..4d08e0fae25 --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getDocumentTransitionsFixture.js @@ -0,0 +1,30 @@ +const DocumentFactory = require('../../document/DocumentFactory'); +const createDPPMock = require('../mocks/createDPPMock'); + +const getDocumentsFixture = require('./getDocumentsFixture'); + +function getDocumentTransitionsFixture(documents = {}) { + const { + create: createDocuments, + replace: replaceDocuments, + delete: deleteDocuments, + } = documents; + + const fixtureDocuments = getDocumentsFixture(); + + const factory = new DocumentFactory( + createDPPMock(), + () => {}, + () => {}, + ); + + const stateTransition = factory.createStateTransition({ + create: (createDocuments || fixtureDocuments), + replace: (replaceDocuments || []), + delete: (deleteDocuments || []), + }); + + return stateTransition.getTransitions(); +} + +module.exports = getDocumentTransitionsFixture; diff --git a/packages/js-dpp/lib/test/fixtures/getDocumentsFixture.js b/packages/js-dpp/lib/test/fixtures/getDocumentsFixture.js new file mode 100644 index 00000000000..db7b91df226 --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getDocumentsFixture.js @@ -0,0 +1,39 @@ +const crypto = require('crypto'); + +const getDataContractFixture = require('./getDataContractFixture'); + +const DocumentFactory = require('../../document/DocumentFactory'); + +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); +const createDPPMock = require('../mocks/createDPPMock'); + +const ownerId = generateRandomIdentifier(); + +/** + * @param {DataContract} [dataContract] + * @return {Document[]} + */ +module.exports = function getDocumentsFixture(dataContract = getDataContractFixture()) { + const factory = new DocumentFactory( + createDPPMock(), + () => ({ + isValid: () => true, + }), + () => {}, + ); + + return [ + factory.create(dataContract, ownerId, 'niceDocument', { name: 'Cutie' }), + factory.create(dataContract, ownerId, 'prettyDocument', { lastName: 'Shiny' }), + factory.create(dataContract, ownerId, 'prettyDocument', { lastName: 'Sweety' }), + factory.create(dataContract, ownerId, 'indexedDocument', { firstName: 'William', lastName: 'Birkin' }), + factory.create(dataContract, ownerId, 'indexedDocument', { firstName: 'Leon', lastName: 'Kennedy' }), + factory.create(dataContract, ownerId, 'noTimeDocument', { name: 'ImOutOfTime' }), + factory.create(dataContract, ownerId, 'uniqueDates', { firstName: 'John' }), + factory.create(dataContract, ownerId, 'indexedDocument', { firstName: 'Bill', lastName: 'Gates' }), + factory.create(dataContract, ownerId, 'withByteArrays', { byteArrayField: crypto.randomBytes(10), identifierField: generateRandomIdentifier().toBuffer() }), + factory.create(dataContract, ownerId, 'optionalUniqueIndexedDocument', { firstName: 'Jacques-Yves', lastName: 'Cousteau' }), + ]; +}; + +module.exports.ownerId = ownerId; diff --git a/packages/js-dpp/lib/test/fixtures/getDpnsContractFixture.js b/packages/js-dpp/lib/test/fixtures/getDpnsContractFixture.js new file mode 100644 index 00000000000..529e66fa306 --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getDpnsContractFixture.js @@ -0,0 +1,15 @@ +const dpnsDocuments = require('@dashevo/dpns-contract/schema/dpns-contract-documents.json'); +const DataContractFactory = require('../../dataContract/DataContractFactory'); +const createDPPMock = require('../mocks/createDPPMock'); + +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); + +const ownerId = generateRandomIdentifier(); + +/** + * @return {DataContract} + */ +module.exports = function getDataContractFixture() { + const factory = new DataContractFactory(createDPPMock(), () => {}); + return factory.create(ownerId, dpnsDocuments); +}; diff --git a/packages/js-dpp/lib/test/fixtures/getDpnsDocumentFixture.js b/packages/js-dpp/lib/test/fixtures/getDpnsDocumentFixture.js new file mode 100644 index 00000000000..6293e900b7f --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getDpnsDocumentFixture.js @@ -0,0 +1,111 @@ +const crypto = require('crypto'); +const getDpnsContractFixture = require('./getDpnsContractFixture'); +const DocumentFactory = require('../../document/DocumentFactory'); +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); +const createDPPMock = require('../mocks/createDPPMock'); + +const ownerId = generateRandomIdentifier(); +const dataContract = getDpnsContractFixture(); + +/** + * @return {Document} + */ +function getTopDocumentFixture(options = {}) { + const factory = new DocumentFactory( + createDPPMock(), + () => ({ + isValid: () => true, + }), + () => {}, + ); + + const label = options.label || 'grandparent'; + const normalizedLabel = options.normalizedLabel || label.toLowerCase(); + const data = { + label, + normalizedLabel, + normalizedParentDomainName: '', + preorderSalt: crypto.randomBytes(32), + records: { + dashUniqueIdentityId: ownerId, + }, + subdomainRules: { + allowSubdomains: true, + }, + ...options, + }; + + return factory.create(dataContract, ownerId, 'domain', data); +} + +/** + * @return {Document} + */ +function getParentDocumentFixture(options = {}) { + const factory = new DocumentFactory( + createDPPMock(), + () => ({ + isValid: () => true, + }), + () => {}, + ); + + const label = options.label || 'Parent'; + const normalizedLabel = options.normalizedLabel || label.toLowerCase(); + const data = { + label, + normalizedLabel, + normalizedParentDomainName: 'grandparent', + preorderSalt: crypto.randomBytes(32), + records: { + dashUniqueIdentityId: ownerId, + }, + subdomainRules: { + allowSubdomains: false, + }, + ...options, + }; + + return factory.create(dataContract, ownerId, 'domain', data); +} + +/** + * @return {Document} + */ +function getChildDocumentFixture(options = {}) { + const factory = new DocumentFactory( + createDPPMock(), + () => ({ + isValid: () => true, + }), + () => {}, + ); + + const label = options.label || 'Child'; + const normalizedLabel = options.normalizedLabel || label.toLowerCase(); + const parent = getParentDocumentFixture(); + const parentDomainName = `${parent.getData().normalizedLabel}.${parent.getData().normalizedParentDomainName}`; + const data = { + label, + normalizedLabel, + normalizedParentDomainName: parentDomainName, + preorderSalt: crypto.randomBytes(32), + records: { + dashUniqueIdentityId: ownerId, + }, + subdomainRules: { + allowSubdomains: false, + }, + ...options, + }; + + return factory.create(dataContract, ownerId, 'domain', data); +} + +module.exports = { + getTopDocumentFixture, + getParentDocumentFixture, + getChildDocumentFixture, +}; + +module.exports.dataContract = dataContract; diff --git a/packages/js-dpp/lib/test/fixtures/getFeatureFlagsContractFixture.js b/packages/js-dpp/lib/test/fixtures/getFeatureFlagsContractFixture.js new file mode 100644 index 00000000000..bcfe82a841c --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getFeatureFlagsContractFixture.js @@ -0,0 +1,15 @@ +const featureFlagDocuments = require('@dashevo/feature-flags-contract/schema/feature-flags-documents.json'); +const DataContractFactory = require('../../dataContract/DataContractFactory'); +const createDPPMock = require('../mocks/createDPPMock'); + +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); + +const ownerId = generateRandomIdentifier(); + +/** + * @return {DataContract} + */ +module.exports = function getFeatureFlagsContractFixture() { + const factory = new DataContractFactory(createDPPMock(), () => {}); + return factory.create(ownerId, featureFlagDocuments); +}; diff --git a/packages/js-dpp/lib/test/fixtures/getFeatureFlagsDocumentsFixture.js b/packages/js-dpp/lib/test/fixtures/getFeatureFlagsDocumentsFixture.js new file mode 100644 index 00000000000..b4763b2919d --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getFeatureFlagsDocumentsFixture.js @@ -0,0 +1,31 @@ +const getFeatureFlagsContractFixture = require('./getFeatureFlagsContractFixture'); +const DocumentFactory = require('../../document/DocumentFactory'); +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); +const createDPPMock = require('../mocks/createDPPMock'); + +const ownerId = generateRandomIdentifier(); +const dataContract = getFeatureFlagsContractFixture(); + +/** + * @return {Document} + */ +function getFeatureFlagsDocumentsFixture() { + const factory = new DocumentFactory( + createDPPMock(), + () => ({ + isValid: () => true, + }), + () => {}, + ); + + return [ + factory.create(dataContract, ownerId, 'updateConsensusParams', { + enabled: true, + enableAtHeight: 77, + }), + ]; +} + +module.exports = getFeatureFlagsDocumentsFixture; + +module.exports.dataContract = dataContract; diff --git a/packages/js-dpp/lib/test/fixtures/getIdentityCreateTransitionFixture.js b/packages/js-dpp/lib/test/fixtures/getIdentityCreateTransitionFixture.js new file mode 100644 index 00000000000..a289514d523 --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getIdentityCreateTransitionFixture.js @@ -0,0 +1,35 @@ +const PrivateKey = require('@dashevo/dashcore-lib/lib/privatekey'); +const IdentityCreateTransition = require('../../identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition'); + +const IdentityPublicKey = require('../../identity/IdentityPublicKey'); + +const stateTransitionTypes = require('../../stateTransition/stateTransitionTypes'); + +const getInstantAssetLockProofFixture = require('./getInstantAssetLockProofFixture'); + +const protocolVersion = require('../../version/protocolVersion'); + +/** + * @param {PrivateKey} oneTimePrivateKey + * + * @return {IdentityCreateTransition} + */ +module.exports = function getIdentityCreateTransitionFixture(oneTimePrivateKey = new PrivateKey()) { + const rawStateTransition = { + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.IDENTITY_CREATE, + assetLockProof: getInstantAssetLockProofFixture(oneTimePrivateKey).toObject(), + publicKeys: [ + { + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: Buffer.from('AuryIuMtRrl/VviQuyLD1l4nmxi9ogPzC9LT7tdpo0di', 'base64'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + }, + ], + }; + + return new IdentityCreateTransition(rawStateTransition); +}; diff --git a/packages/js-dpp/lib/test/fixtures/getIdentityFixture.js b/packages/js-dpp/lib/test/fixtures/getIdentityFixture.js new file mode 100644 index 00000000000..adcebc35ebd --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getIdentityFixture.js @@ -0,0 +1,40 @@ +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); + +const protocolVersion = require('../../version/protocolVersion'); + +const Identity = require('../../identity/Identity'); +const IdentityPublicKey = require('../../identity/IdentityPublicKey'); + +const id = generateRandomIdentifier(); + +/** + * @return {Identity} + */ +module.exports = function getIdentityFixture() { + const rawIdentity = { + protocolVersion: protocolVersion.latestVersion, + id: id.toBuffer(), + balance: 10, + revision: 0, + publicKeys: [ + { + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: Buffer.from('AuryIuMtRrl/VviQuyLD1l4nmxi9ogPzC9LT7tdpo0di', 'base64'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + }, + { + id: 1, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: Buffer.from('A8AK95PYMVX5VQKzOhcVQRCUbc9pyg3RiL7jttEMDU+L', 'base64'), + purpose: IdentityPublicKey.PURPOSES.ENCRYPTION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MEDIUM, + readOnly: false, + }, + ], + }; + + return new Identity(rawIdentity); +}; diff --git a/packages/js-dpp/lib/test/fixtures/getIdentityTopUpTransitionFixture.js b/packages/js-dpp/lib/test/fixtures/getIdentityTopUpTransitionFixture.js new file mode 100644 index 00000000000..d79589dcddb --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getIdentityTopUpTransitionFixture.js @@ -0,0 +1,24 @@ +const IdentityTopUpTransition = require('../../identity/stateTransition/IdentityTopUpTransition/IdentityTopUpTransition'); + +const stateTransitionTypes = require('../../stateTransition/stateTransitionTypes'); + +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); + +const getInstantAssetLockProofFixture = require('./getInstantAssetLockProofFixture'); + +const protocolVersion = require('../../version/protocolVersion'); + +/** + * + * @return {IdentityTopUpTransition} + */ +module.exports = function getIdentityTopUpTransitionFixture() { + const rawStateTransition = { + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.IDENTITY_CREATE, + assetLockProof: getInstantAssetLockProofFixture().toObject(), + identityId: generateRandomIdentifier(), + }; + + return new IdentityTopUpTransition(rawStateTransition); +}; diff --git a/packages/js-dpp/lib/test/fixtures/getIdentityUpdateTransitionFixture.js b/packages/js-dpp/lib/test/fixtures/getIdentityUpdateTransitionFixture.js new file mode 100644 index 00000000000..6f5a3eb6f36 --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getIdentityUpdateTransitionFixture.js @@ -0,0 +1,30 @@ +const protocolVersion = require('../../version/protocolVersion'); +const stateTransitionTypes = require('../../stateTransition/stateTransitionTypes'); +const getInstantAssetLockProofFixture = require('./getInstantAssetLockProofFixture'); +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); +const IdentityUpdateTransition = require('../../identity/stateTransition/IdentityUpdateTransition/IdentityUpdateTransition'); +const IdentityPublicKey = require('../../identity/IdentityPublicKey'); + +module.exports = function getIdentityUpdateTransitionFixture() { + const rawStateTransition = { + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.IDENTITY_UPDATE, + assetLockProof: getInstantAssetLockProofFixture().toObject(), + identityId: generateRandomIdentifier(), + revision: 0, + addPublicKeys: [ + { + id: 3, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: Buffer.from('AkVuTKyF3YgKLAQlLEtaUL2HTditwGILfWUVqjzYnIgH', 'base64'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + }, + ], + disablePublicKeys: [0], + publicKeysDisabledAt: 1234567, + }; + + return new IdentityUpdateTransition(rawStateTransition); +}; diff --git a/packages/js-dpp/lib/test/fixtures/getInstantAssetLockProofFixture.js b/packages/js-dpp/lib/test/fixtures/getInstantAssetLockProofFixture.js new file mode 100644 index 00000000000..af1d09f7bff --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getInstantAssetLockProofFixture.js @@ -0,0 +1,62 @@ +const { + Transaction, + InstantLock, + PrivateKey, + Script, + Opcode, +} = require('@dashevo/dashcore-lib'); + +const InstantAssetLockProof = require('../../identity/stateTransition/assetLockProof/instant/InstantAssetLockProof'); + +/** + * @param {PrivateKey} [oneTimePrivateKey] + */ +function getInstantAssetLockProofFixture(oneTimePrivateKey = new PrivateKey()) { + const privateKeyHex = 'cSBnVM4xvxarwGQuAfQFwqDg9k5tErHUHzgWsEfD4zdwUasvqRVY'; + const privateKey = new PrivateKey(privateKeyHex); + const fromAddress = privateKey.toAddress(); + + const oneTimePublicKey = oneTimePrivateKey.toPublicKey(); + + const transaction = new Transaction() + .from({ + address: fromAddress, + txId: 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458', + outputIndex: 0, + script: Script.buildPublicKeyHashOut(fromAddress) + .toString(), + satoshis: 100000, + }) + // eslint-disable-next-line no-underscore-dangle + .addBurnOutput(90000, oneTimePublicKey._getID()) + .to(fromAddress, 5000) + .addOutput(Transaction.Output({ + satoshis: 5000, + script: Script() + .add(Opcode.OP_RETURN) + .add(Buffer.from([1, 2, 3])), + })) + .sign(privateKey); + + const instantLock = new InstantLock({ + version: 1, + inputs: [ + { + outpointHash: '6e200d059fb567ba19e92f5c2dcd3dde522fd4e0a50af223752db16158dabb1d', + outpointIndex: 0, + }, + ], + txid: transaction.id, + cyclehash: '7c30826123d0f29fe4c4a8895d7ba4eb469b1fafa6ad7b23896a1a591766a536', + signature: '8967c46529a967b3822e1ba8a173066296d02593f0f59b3a78a30a7eef9c8a120847729e62e4a32954339286b79fe7590221331cd28d576887a263f45b595d499272f656c3f5176987c976239cac16f972d796ad82931d532102a4f95eec7d80', + }); + + return new InstantAssetLockProof({ + type: 0, + instantLock: instantLock.toBuffer(), + transaction: transaction.toBuffer(), + outputIndex: 0, + }); +} + +module.exports = getInstantAssetLockProofFixture; diff --git a/packages/js-dpp/lib/test/fixtures/getMasternodeRewardShareDocumentsFixture.js b/packages/js-dpp/lib/test/fixtures/getMasternodeRewardShareDocumentsFixture.js new file mode 100644 index 00000000000..df1eca7ad8f --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getMasternodeRewardShareDocumentsFixture.js @@ -0,0 +1,29 @@ +const DocumentFactory = require('../../document/DocumentFactory'); +const createDPPMock = require('../mocks/createDPPMock'); +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); +const getMasternodeRewardSharesContractFixture = require('./getMasternodeRewardSharesContractFixture'); + +const ownerId = generateRandomIdentifier(); +const payToId = generateRandomIdentifier(); +const dataContract = getMasternodeRewardSharesContractFixture(); + +function getMasternodeRewardShareDocumentsFixture() { + const factory = new DocumentFactory( + createDPPMock(), + () => ({ + isValid: () => true, + }), + () => {}, + ); + + return [ + factory.create(dataContract, ownerId, 'rewardShare', { + payToId, + percentage: 500, + }), + ]; +} + +module.exports = getMasternodeRewardShareDocumentsFixture; + +module.exports.dataContract = dataContract; diff --git a/packages/js-dpp/lib/test/fixtures/getMasternodeRewardSharesContractFixture.js b/packages/js-dpp/lib/test/fixtures/getMasternodeRewardSharesContractFixture.js new file mode 100644 index 00000000000..fdc602d519a --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getMasternodeRewardSharesContractFixture.js @@ -0,0 +1,15 @@ +const masternodeRewardSharesDocuments = require('@dashevo/masternode-reward-shares-contract/schema/masternode-reward-shares-documents.json'); +const DataContractFactory = require('../../dataContract/DataContractFactory'); +const createDPPMock = require('../mocks/createDPPMock'); +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); + +const ownerId = generateRandomIdentifier(); + +/** + * @return {DataContract} + */ +module.exports = function getMasternodeRewardSharesContractFixture() { + const factory = new DataContractFactory(createDPPMock(), () => {}); + + return factory.create(ownerId, masternodeRewardSharesDocuments); +}; diff --git a/packages/js-dpp/lib/test/fixtures/getPreorderDocumentFixture.js b/packages/js-dpp/lib/test/fixtures/getPreorderDocumentFixture.js new file mode 100644 index 00000000000..4714669b203 --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getPreorderDocumentFixture.js @@ -0,0 +1,40 @@ +const getDpnsContractFixture = require('./getDpnsContractFixture'); +const DocumentFactory = require('../../document/DocumentFactory'); +const { generate: generateEntropy } = require('../../util/entropyGenerator'); + +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); +const createDPPMock = require('../mocks/createDPPMock'); + +const ownerId = generateRandomIdentifier(); + +/** + * @return {Document} + */ +function getPreorderDocumentFixture(options = {}) { + const dataContract = getDpnsContractFixture(); + + const factory = new DocumentFactory( + createDPPMock(), + () => ({ + isValid: () => true, + }), + () => {}, + ); + + const label = options.label || 'Preorder'; + const normalizedLabel = options.normalizedLabel || label.toLowerCase(); + const data = { + label, + normalizedLabel, + parentDomainHash: '', + preorderSalt: generateEntropy(), + records: { + dashIdentity: ownerId, + }, + ...options, + }; + + return factory.create(dataContract, ownerId, 'preorder', data); +} + +module.exports = getPreorderDocumentFixture; diff --git a/packages/js-dpp/lib/test/fixtures/getRawTransactionFixture.js b/packages/js-dpp/lib/test/fixtures/getRawTransactionFixture.js new file mode 100644 index 00000000000..d5c7415c02c --- /dev/null +++ b/packages/js-dpp/lib/test/fixtures/getRawTransactionFixture.js @@ -0,0 +1,118 @@ +/** + * Verbose raw transaction fixture + * + * @return Object + */ +module.exports = function getRawTransactionFixture() { + return { + txid: 'f1c1cbc37b5d5543eeb126a53de7863ea2b9d5dbd03b981337bbda76cc6d771c', + version: 2, + type: 0, + size: 817, + locktime: 34623, + vin: [ + { + txid: '3b8c0ef5f91c330f236f869eba2904432f03d1a3ab969882dae6f9417e77c760', + vout: 1, + scriptSig: { + asm: '3045022100cab5638d1498930dcc88047ba0c44b952988c390cd66e643de7b975b62170be40220181ede94c41ded6d83b586ce90e195ed09ad4c6b9dfcb76bc07fbba7b5e89e51[ALL] 03a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1', + hex: '483045022100cab5638d1498930dcc88047ba0c44b952988c390cd66e643de7b975b62170be40220181ede94c41ded6d83b586ce90e195ed09ad4c6b9dfcb76bc07fbba7b5e89e51012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1', + }, + value: 100.00000788, + valueSat: 10000000788, + address: 'yNPbcFfabtNmmxKdGwhHomdYfVs6gikbPf', + sequence: 4294967294, + }, + { + txid: '8ba79ebf35b55eed79601b17ec1c32ee5af592554fc3b6e5ff5fdc4d39854546', + vout: 0, + scriptSig: { + asm: '3045022100ecda52ffb4500b7e593bf6a1d1f7a5b7303acf3a20b58c1cb0200b34a523d1d902203295bfb668e69b79275ed03335a8652d7687012227ac492a12cf9d29c1f7a7cc[ALL] 03a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1', + hex: '483045022100ecda52ffb4500b7e593bf6a1d1f7a5b7303acf3a20b58c1cb0200b34a523d1d902203295bfb668e69b79275ed03335a8652d7687012227ac492a12cf9d29c1f7a7cc012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1', + }, + value: 11.25005000, + valueSat: 1125005000, + address: 'yNPbcFfabtNmmxKdGwhHomdYfVs6gikbPf', + sequence: 4294967294, + }, + { + txid: '9b192afc29f4b85795e6bdf829d6b7aa763915dea10b2cb21f23aafc0b3fb042', + vout: 0, + scriptSig: { + asm: '3045022100ddb49494296712b6f2db5031512d11dd470d338af010088fec65ed398cec0afe0220285d4b7d8a2b3ea0a18fa9d6fe375b401e8f6ae98281a1c3f1a9bd3742840702[ALL] 03a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1', + hex: '483045022100ddb49494296712b6f2db5031512d11dd470d338af010088fec65ed398cec0afe0220285d4b7d8a2b3ea0a18fa9d6fe375b401e8f6ae98281a1c3f1a9bd3742840702012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1', + }, + value: 11.25005000, + valueSat: 1125005000, + address: 'yNPbcFfabtNmmxKdGwhHomdYfVs6gikbPf', + sequence: 4294967294, + }, + { + txid: 'f5b471fbdacd35de483d029f4e9b4237e9fb3244d184e530c381a6961cc047be', + vout: 0, + scriptSig: { + asm: '3045022100ccf54a31a262e5ed9e58e1fa6c23938b81fd79dd62a1057f841e255a64a0e06c0220138773e3f5dd77b0bc1d1f1d01110b1b93741f631c177e7936c335dbaa19686f[ALL] 03a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1', + hex: '483045022100ccf54a31a262e5ed9e58e1fa6c23938b81fd79dd62a1057f841e255a64a0e06c0220138773e3f5dd77b0bc1d1f1d01110b1b93741f631c177e7936c335dbaa19686f012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1', + }, + value: 11.25005000, + valueSat: 1125005000, + address: 'yNPbcFfabtNmmxKdGwhHomdYfVs6gikbPf', + sequence: 4294967294, + }, + { + txid: 'fa3759df9830aed2aa59d41f825eec5ba2aa58dc99d377b28488a92997a8cfb7', + vout: 0, + scriptSig: { + asm: '304402200b035f016087a5468cd27f9575fb496aaeaf2c454c8bb402c3624c20bdd4448f022015a23433ad620ea7c5ff92a8198262c66a20595902ddc4141722396ecadc07ca[ALL] 02a1dc26a61b5ed6fbecd4c0fe65b7dd8c637eee7cfce759cd0c55f83b6d9680b9', + hex: '47304402200b035f016087a5468cd27f9575fb496aaeaf2c454c8bb402c3624c20bdd4448f022015a23433ad620ea7c5ff92a8198262c66a20595902ddc4141722396ecadc07ca012102a1dc26a61b5ed6fbecd4c0fe65b7dd8c637eee7cfce759cd0c55f83b6d9680b9', + }, + value: 6.82471210, + valueSat: 682471210, + address: 'yc1C9TTSwXmN59rxpP5fKvtvrQa6MWVzaf', + sequence: 4294967294, + }, + ], + vout: [ + { + value: 3.55976180, + valueSat: 355976180, + n: 0, + scriptPubKey: { + asm: 'OP_DUP OP_HASH160 ad56adfd39caf20673de73b9eae51352e730a2f2 OP_EQUALVERIFY OP_CHECKSIG', + hex: '76a914ad56adfd39caf20673de73b9eae51352e730a2f288ac', + reqSigs: 1, + type: 'pubkeyhash', + addresses: [ + 'yc7yaznKps1rXWovLMXcMRQfc2AZUxoB3C', + ], + }, + spentTxId: 'b53f8b9a47e3af0491205c3d60689fed8d6d750bcfe9f1731a061f469c4f4e82', + spentIndex: 17, + spentHeight: 34899, + }, + { + value: 137.01510000, + valueSat: 13701510000, + n: 1, + scriptPubKey: { + asm: 'OP_DUP OP_HASH160 b9e06afc1400f95eaffce11e51e58b0a8390e88b OP_EQUALVERIFY OP_CHECKSIG', + hex: '76a914b9e06afc1400f95eaffce11e51e58b0a8390e88b88ac', + reqSigs: 1, + type: 'pubkeyhash', + addresses: [ + 'ydGGhXC6PXoBvUhG9rxs2LmriuEHdsjt4A', + ], + }, + }, + ], + hex: '020000000560c7777e41f9e6da829896aba3d1032f430429ba9e866f230f331cf9f50e8c3b010000006b483045022100cab5638d1498930dcc88047ba0c44b952988c390cd66e643de7b975b62170be40220181ede94c41ded6d83b586ce90e195ed09ad4c6b9dfcb76bc07fbba7b5e89e51012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1feffffff464585394ddc5fffe5b6c34f5592f55aee321cec171b6079ed5eb535bf9ea78b000000006b483045022100ecda52ffb4500b7e593bf6a1d1f7a5b7303acf3a20b58c1cb0200b34a523d1d902203295bfb668e69b79275ed03335a8652d7687012227ac492a12cf9d29c1f7a7cc012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1feffffff42b03f0bfcaa231fb22c0ba1de153976aab7d629f8bde69557b8f429fc2a199b000000006b483045022100ddb49494296712b6f2db5031512d11dd470d338af010088fec65ed398cec0afe0220285d4b7d8a2b3ea0a18fa9d6fe375b401e8f6ae98281a1c3f1a9bd3742840702012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1feffffffbe47c01c96a681c330e584d14432fbe937429b4e9f023d48de35cddafb71b4f5000000006b483045022100ccf54a31a262e5ed9e58e1fa6c23938b81fd79dd62a1057f841e255a64a0e06c0220138773e3f5dd77b0bc1d1f1d01110b1b93741f631c177e7936c335dbaa19686f012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1feffffffb7cfa89729a98884b277d399dc58aaa25bec5e821fd459aad2ae3098df5937fa000000006a47304402200b035f016087a5468cd27f9575fb496aaeaf2c454c8bb402c3624c20bdd4448f022015a23433ad620ea7c5ff92a8198262c66a20595902ddc4141722396ecadc07ca012102a1dc26a61b5ed6fbecd4c0fe65b7dd8c637eee7cfce759cd0c55f83b6d9680b9feffffff02f4c33715000000001976a914ad56adfd39caf20673de73b9eae51352e730a2f288ac7073ac30030000001976a914b9e06afc1400f95eaffce11e51e58b0a8390e88b88ac3f870000', + blockhash: '000000e256b0ea8983f076771f019ebf4a6112f5c85396eab7f3e0660e9685c3', + height: 34624, + confirmations: 4607, + time: 1588755748, + blocktime: 1588755748, + instantlock: true, + instantlock_internal: false, + chainlock: true, + }; +}; diff --git a/packages/js-dpp/lib/test/karma/loader.js b/packages/js-dpp/lib/test/karma/loader.js new file mode 100644 index 00000000000..c3cbdb3bcbe --- /dev/null +++ b/packages/js-dpp/lib/test/karma/loader.js @@ -0,0 +1,6 @@ +// This file is used for compiling tests with webpack into one file for using with karma +require('../bootstrap'); + +const testsContext = require.context('../../../test', true, /\.spec\.js$/); + +testsContext.keys().forEach(testsContext); diff --git a/packages/js-dpp/lib/test/mocks/SomeConsensusError.js b/packages/js-dpp/lib/test/mocks/SomeConsensusError.js new file mode 100644 index 00000000000..0cae2395a00 --- /dev/null +++ b/packages/js-dpp/lib/test/mocks/SomeConsensusError.js @@ -0,0 +1,19 @@ +const AbstractConsensusError = require('../../errors/consensus/AbstractConsensusError'); + +class SomeConsensusError extends AbstractConsensusError { + constructor(message) { + super(message); + + // eslint-disable-next-line prefer-rest-params + this.setConstructorArguments(arguments); + } + + /** + * @returns {number} + */ + getCode() { + return Number.MAX_SAFE_INTEGER; + } +} + +module.exports = SomeConsensusError; diff --git a/packages/js-dpp/lib/test/mocks/StateTransitionMock.js b/packages/js-dpp/lib/test/mocks/StateTransitionMock.js new file mode 100644 index 00000000000..107b2c7ab1a --- /dev/null +++ b/packages/js-dpp/lib/test/mocks/StateTransitionMock.js @@ -0,0 +1,14 @@ +const AbstractStateTransitionIdentitySigned = require('../../stateTransition/AbstractStateTransitionIdentitySigned'); +const stateTransitionTypes = require('../../stateTransition/stateTransitionTypes'); + +class StateTransitionMock extends AbstractStateTransitionIdentitySigned { + getType() { + return stateTransitionTypes.DATA_CONTRACT_CREATE; + } + + getModifiedDataIds() { + return []; + } +} + +module.exports = StateTransitionMock; diff --git a/packages/js-dpp/lib/test/mocks/createDPPMock.js b/packages/js-dpp/lib/test/mocks/createDPPMock.js new file mode 100644 index 00000000000..bbcada5bebb --- /dev/null +++ b/packages/js-dpp/lib/test/mocks/createDPPMock.js @@ -0,0 +1,61 @@ +const protocolVersion = require('../../version/protocolVersion'); + +/** + * @param {Sandbox} [sinonSandbox] + * + * @returns {DashPlatformProtocol} + */ +module.exports = function createDPPMock(sinonSandbox = undefined) { + // in simplier cases when you do not have acccess + // to Sinon sandbox return a simplified version of DPP + // with some predefined behaviour + if (!sinonSandbox) { + return { + getProtocolVersion: () => protocolVersion.latestVersion, + }; + } + + const dataContract = { + create: sinonSandbox.stub(), + createFromObject: sinonSandbox.stub(), + createFromBuffer: sinonSandbox.stub(), + validate: sinonSandbox.stub(), + }; + + const document = { + create: sinonSandbox.stub(), + createFromObject: sinonSandbox.stub(), + createFromBuffer: sinonSandbox.stub(), + validate: sinonSandbox.stub(), + createStateTransition: sinonSandbox.stub(), + }; + + const stateTransition = { + createFromObject: sinonSandbox.stub(), + createFromBuffer: sinonSandbox.stub(), + validate: sinonSandbox.stub(), + validateBasic: sinonSandbox.stub(), + validateState: sinonSandbox.stub(), + apply: sinonSandbox.stub(), + }; + + const identity = { + create: sinonSandbox.stub(), + createFromObject: sinonSandbox.stub(), + createFromBuffer: sinonSandbox.stub(), + validate: sinonSandbox.stub(), + }; + + return { + dataContract, + document, + stateTransition, + identity, + getOwnerId: sinonSandbox.stub(), + setOwnerId: sinonSandbox.stub(), + getDataContract: sinonSandbox.stub(), + setDataContract: sinonSandbox.stub(), + getStateRepository: sinonSandbox.stub(), + getProtocolVersion: sinonSandbox.stub().returns(protocolVersion), + }; +}; diff --git a/packages/js-dpp/lib/test/mocks/createStateRepositoryMock.js b/packages/js-dpp/lib/test/mocks/createStateRepositoryMock.js new file mode 100644 index 00000000000..50291653025 --- /dev/null +++ b/packages/js-dpp/lib/test/mocks/createStateRepositoryMock.js @@ -0,0 +1,39 @@ +/** + * @param sinonSandbox + * @return {{ + * fetchDataContract: *, + * storeDataContract: *, + * fetchDocuments: *, + * createDocument: *, + * updateDocument: *, + * removeDocument: *, + * fetchTransaction: *, + * fetchIdentity: *, + * createIdentity: *, + * updateIdentity: *, + * verifyInstantLock: *, + * fetchSMLStore: *, + * }} + */ +module.exports = function createStateRepositoryMock(sinonSandbox) { + return { + fetchDataContract: sinonSandbox.stub(), + storeDataContract: sinonSandbox.stub(), + fetchDocuments: sinonSandbox.stub(), + createDocument: sinonSandbox.stub(), + updateDocument: sinonSandbox.stub(), + removeDocument: sinonSandbox.stub(), + fetchTransaction: sinonSandbox.stub(), + fetchIdentity: sinonSandbox.stub(), + createIdentity: sinonSandbox.stub(), + updateIdentity: sinonSandbox.stub(), + fetchLatestPlatformBlockHeader: sinonSandbox.stub(), + storeIdentityPublicKeyHashes: sinonSandbox.stub(), + fetchIdentityIdsByPublicKeyHashes: sinonSandbox.stub(), + verifyInstantLock: sinonSandbox.stub(), + markAssetLockTransactionOutPointAsUsed: sinonSandbox.stub(), + verifyChainLockHeight: sinonSandbox.stub(), + isAssetLockTransactionOutPointAlreadyUsed: sinonSandbox.stub(), + fetchSMLStore: sinonSandbox.stub(), + }; +}; diff --git a/packages/js-dpp/lib/test/utils/generateDeepJson.js b/packages/js-dpp/lib/test/utils/generateDeepJson.js new file mode 100644 index 00000000000..439a4e857f7 --- /dev/null +++ b/packages/js-dpp/lib/test/utils/generateDeepJson.js @@ -0,0 +1,20 @@ +/** + * Generate JSON with big depth + * @param {number} depth + * @returns {object} + */ +function generateDeepJson(depth) { + const result = {}; + + if (depth === 1) { + return { + depth, + }; + } + + result[depth] = generateDeepJson(depth - 1); + + return result; +} + +module.exports = generateDeepJson; diff --git a/packages/js-dpp/lib/test/utils/generateRandomIdentifier.js b/packages/js-dpp/lib/test/utils/generateRandomIdentifier.js new file mode 100644 index 00000000000..f9aece4e190 --- /dev/null +++ b/packages/js-dpp/lib/test/utils/generateRandomIdentifier.js @@ -0,0 +1,13 @@ +const crypto = require('crypto'); +const Identifier = require('../../identifier/Identifier'); + +/** + * Generate random identity ID + * + * @return {Identifier} + */ +function generateRandomIdentifier() { + return new Identifier(crypto.randomBytes(32)); +} + +module.exports = generateRandomIdentifier; diff --git a/packages/js-dpp/lib/test/utils/wait.js b/packages/js-dpp/lib/test/utils/wait.js new file mode 100644 index 00000000000..05f2bf344b0 --- /dev/null +++ b/packages/js-dpp/lib/test/utils/wait.js @@ -0,0 +1,12 @@ +/** + * Asynchronously wait for a specified number of milliseconds. + * + * @param {number} ms - Number of milliseconds to wait. + * + * @return {Promise} The promise to await on. + */ +async function wait(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +module.exports = wait; diff --git a/packages/js-dpp/lib/util/cloneDeepWithIdentifiers.js b/packages/js-dpp/lib/util/cloneDeepWithIdentifiers.js new file mode 100644 index 00000000000..ca742581879 --- /dev/null +++ b/packages/js-dpp/lib/util/cloneDeepWithIdentifiers.js @@ -0,0 +1,19 @@ +const lodashCloneDeepWith = require('lodash.clonedeepwith'); +const Identifier = require('../identifier/Identifier'); + +/** + * Clone data which contains Identifiers + * + * @param {*} value + * @return {*} + */ +function cloneDeepWithIdentifiers(value) { + // eslint-disable-next-line consistent-return + return lodashCloneDeepWith(value, (item) => { + if (item instanceof Identifier) { + return new Identifier(item.toBuffer()); + } + }); +} + +module.exports = cloneDeepWithIdentifiers; diff --git a/packages/js-dpp/lib/util/convertBuffersToArrays.js b/packages/js-dpp/lib/util/convertBuffersToArrays.js new file mode 100644 index 00000000000..b17549ffab1 --- /dev/null +++ b/packages/js-dpp/lib/util/convertBuffersToArrays.js @@ -0,0 +1,18 @@ +const lodashCloneDeepWith = require('lodash.clonedeepwith'); + +/** + * Clone data which contains Identifiers + * + * @param {*} value + * @return {*} + */ +function convertBuffersToArrays(value) { + // eslint-disable-next-line consistent-return + return lodashCloneDeepWith(value, (item) => { + if (item instanceof Buffer) { + return [...item]; + } + }); +} + +module.exports = convertBuffersToArrays; diff --git a/packages/js-dpp/lib/util/entropyGenerator.js b/packages/js-dpp/lib/util/entropyGenerator.js new file mode 100644 index 00000000000..d087cc03974 --- /dev/null +++ b/packages/js-dpp/lib/util/entropyGenerator.js @@ -0,0 +1,12 @@ +const crypto = require('crypto'); + +/** + * Generate entropy + * + * @return {Buffer} + */ +function generate() { + return crypto.randomBytes(32); +} + +module.exports = { generate }; diff --git a/packages/js-dpp/lib/util/errors/MaxEncodedBytesReachedError.js b/packages/js-dpp/lib/util/errors/MaxEncodedBytesReachedError.js new file mode 100644 index 00000000000..43bdeccc343 --- /dev/null +++ b/packages/js-dpp/lib/util/errors/MaxEncodedBytesReachedError.js @@ -0,0 +1,31 @@ +const DPPError = require('../../errors/DPPError'); + +class MaxEncodedBytesReachedError extends DPPError { + /** + * @param {*} payload + * @param {number} maxSizeKBytes + */ + constructor(payload, maxSizeKBytes) { + super(`Payload reached a ${maxSizeKBytes}Kb limit`); + + this.payload = payload; + this.maxSizeKBytes = maxSizeKBytes; + } + + /** + * @return {*} + */ + getPayload() { + return this.payload; + } + + /** + * Get max payload size + * @returns {number} + */ + getMaxSizeKBytes() { + return this.maxSizeKBytes; + } +} + +module.exports = MaxEncodedBytesReachedError; diff --git a/packages/js-dpp/lib/util/getFunctionParams.js b/packages/js-dpp/lib/util/getFunctionParams.js new file mode 100644 index 00000000000..18ef78bedb0 --- /dev/null +++ b/packages/js-dpp/lib/util/getFunctionParams.js @@ -0,0 +1,59 @@ +const STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,)]*))/mg; +const ARGUMENT_NAMES = /([^\s,]+)/g; + +/** + * Get function params + * + * @param {Function} fn + * @param {number} skip Skip params + * @return {array} + */ +function getFunctionParams(fn, skip = 0) { + const functionString = fn.toString().replace(STRIP_COMMENTS, ''); + + let params = functionString.slice( + functionString.indexOf('(') + 1, + functionString.indexOf(')'), + ).match(ARGUMENT_NAMES); + + if (params === null) { + params = []; + } + + const filteredParams = []; + let openDestructors = 0; + let skippedCount = 0; + + for (let i = 0; i < params.length; i++) { + switch (params[i]) { + case '{': + openDestructors++; + + break; + case '}': + openDestructors--; + + if (openDestructors === 0 && skippedCount < skip) { + skippedCount++; + } + + break; + default: + if (openDestructors > 0) { + break; + } + + if (skippedCount < skip) { + skippedCount++; + + break; + } + + filteredParams.push(params[i]); + } + } + + return filteredParams; +} + +module.exports = getFunctionParams; diff --git a/packages/js-dpp/lib/util/hash.js b/packages/js-dpp/lib/util/hash.js new file mode 100644 index 00000000000..fb58965876f --- /dev/null +++ b/packages/js-dpp/lib/util/hash.js @@ -0,0 +1,18 @@ +const crypto = require('crypto'); + +function sha256(payload) { + return crypto.createHash('sha256') + .update(payload) + .digest(); +} +/** + * Serialize and hash payload using double sha256 + * + * @param {Buffer} buffer + * @return {Buffer} + */ +function hash(buffer) { + return sha256(sha256(buffer)); +} + +module.exports = { hash }; diff --git a/packages/js-dpp/lib/util/serializer.js b/packages/js-dpp/lib/util/serializer.js new file mode 100644 index 00000000000..68b658490e3 --- /dev/null +++ b/packages/js-dpp/lib/util/serializer.js @@ -0,0 +1,35 @@ +const cbor = require('cbor'); + +const MaxEncodedBytesReachedError = require('./errors/MaxEncodedBytesReachedError'); + +const MAX_ENCODED_KBYTE_LENGTH = 16; // 16Kb + +/** + * @typedef serializer + * @type {{encode(*): Buffer, decode((Buffer|string)): *}} + */ +module.exports = { + /** + * + * @param {*} payload + * @return {Buffer} + */ + encode(payload) { + const encodedData = cbor.encodeCanonical(payload); + const encodedDataByteLength = Buffer.byteLength(encodedData); + + if (encodedDataByteLength >= MAX_ENCODED_KBYTE_LENGTH * 1024) { + throw new MaxEncodedBytesReachedError(payload, MAX_ENCODED_KBYTE_LENGTH); + } + + return encodedData; + }, + + /** + * + * @param {Buffer|string} payload + */ + decode(payload) { + return cbor.decode(payload); + }, +}; diff --git a/packages/js-dpp/lib/validation/JsonSchemaValidator.js b/packages/js-dpp/lib/validation/JsonSchemaValidator.js new file mode 100644 index 00000000000..700e99843e7 --- /dev/null +++ b/packages/js-dpp/lib/validation/JsonSchemaValidator.js @@ -0,0 +1,108 @@ +const dataContractMetaSchema = require('../../schema/dataContract/dataContractMeta.json'); + +const ValidationResult = require('./ValidationResult'); + +const JsonSchemaError = require('../errors/consensus/basic/JsonSchemaError'); +const JsonSchemaCompilationError = require('../errors/consensus/basic/JsonSchemaCompilationError'); + +class JsonSchemaValidator { + constructor(ajv) { + this.ajv = ajv; + + // TODO Validator shouldn't know about schemas + this.ajv.addMetaSchema(dataContractMetaSchema); + this.ajv.addVocabulary([ + 'ownerId', + 'documents', + 'protocolVersion', + 'indices', + 'version', + ]); + } + + /** + * @param {object} schema + * @param {object} object + * @param {array|Object} additionalSchemas + * @return {ValidationResult} + */ + validate(schema, object, additionalSchemas = {}) { + // TODO Keep cached/compiled additional schemas + + Object.keys(additionalSchemas).forEach((schemaId) => { + this.ajv.addSchema(additionalSchemas[schemaId], schemaId); + }); + + this.ajv.validate(schema, object); + + Object.keys(additionalSchemas).forEach((schemaId) => { + this.ajv.removeSchema(schemaId); + }); + + return new ValidationResult( + (this.ajv.errors || []).map((error) => new JsonSchemaError( + error.message, + error.keyword, + error.instancePath, + error.schemaPath, + error.params, + error.propertyName, + )), + ); + } + + /** + * Validate JSON Schema + * + * @param {object} schema + * @param additionalSchemas + * @return {ValidationResult} + */ + validateSchema(schema, additionalSchemas = {}) { + const result = new ValidationResult(); + + Object.keys(additionalSchemas).forEach((schemaId) => { + this.ajv.addSchema(additionalSchemas[schemaId], schemaId); + }); + + try { + // TODO: Use validateSchema + // https://github.com/epoberezkin/ajv#validateschemaobject-schema---boolean + this.ajv.compile(schema); + } catch (e) { + result.addError( + new JsonSchemaCompilationError(e.message), + ); + } finally { + Object.keys(additionalSchemas).forEach((schemaId) => { + this.ajv.removeSchema(schemaId); + }); + } + + if (this.ajv.errors) { + result.addError( + this.ajv.errors.map((error) => new JsonSchemaError( + error.message, + error.keyword, + error.instancePath, + error.schemaPath, + error.params, + error.propertyName, + )), + ); + } + + return result; + } +} + +JsonSchemaValidator.SCHEMAS = { + META: { + DATA_CONTRACT: 'https://schema.dash.org/dpp-0-4-0/meta/data-contract', + }, + BASE: { + DP_OBJECT: 'https://schema.dash.org/dpp-0-4-0/base/document', + }, +}; + +module.exports = JsonSchemaValidator; diff --git a/packages/js-dpp/lib/validation/ValidationResult.js b/packages/js-dpp/lib/validation/ValidationResult.js new file mode 100644 index 00000000000..9ba88c59678 --- /dev/null +++ b/packages/js-dpp/lib/validation/ValidationResult.js @@ -0,0 +1,77 @@ +class ValidationResult { + /** + * @param {AbstractConsensusError[]} [errors] + */ + constructor(errors = []) { + this.errors = errors; + this.data = undefined; + } + + /** + * Add consensus error + * + * @param {...AbstractConsensusError} error + */ + addError(...error) { + this.errors.push(...error); + } + + /** + * Get consensus errors + * + * @return {AbstractConsensusError[]} + */ + getErrors() { + return this.errors; + } + + /** + * Get the first consensus error + * + * @returns {AbstractConsensusError} + */ + getFirstError() { + return this.errors[0]; + } + + /** + * Is data valid + * + * @return {boolean} + */ + isValid() { + return !this.errors.length; + } + + /** + * Merge Validation results + * + * @param {ValidationResult} result + */ + merge(result) { + if (!result.isValid()) { + this.addError(...result.getErrors()); + } + } + + /** + * + * @param {*} data + * @return {ValidationResult} + */ + setData(data) { + this.data = data; + + return this; + } + + /** + * + * @return {*} + */ + getData() { + return this.data; + } +} + +module.exports = ValidationResult; diff --git a/packages/js-dpp/lib/version/protocolVersion.js b/packages/js-dpp/lib/version/protocolVersion.js new file mode 100644 index 00000000000..7161d534898 --- /dev/null +++ b/packages/js-dpp/lib/version/protocolVersion.js @@ -0,0 +1,11 @@ +module.exports = { + latestVersion: 1, + // Even if we bumping protocol version, previous versions of entity structures + // can be still compatible, that allow to not update clients so often. + // + // Minimum compatible versions must be defined for all protocol versions: + // [protocolVersion]: [minimumCompatibleProtocolVersions] + compatibility: { + 1: 1, + }, +}; diff --git a/packages/js-dpp/lib/version/validateProtocolVersionFactory.js b/packages/js-dpp/lib/version/validateProtocolVersionFactory.js new file mode 100644 index 00000000000..2e769288d84 --- /dev/null +++ b/packages/js-dpp/lib/version/validateProtocolVersionFactory.js @@ -0,0 +1,66 @@ +const UnsupportedProtocolVersionError = require('../errors/consensus/basic/UnsupportedProtocolVersionError'); +const CompatibleProtocolVersionIsNotDefinedError = require('../errors/CompatibleProtocolVersionIsNotDefinedError'); +const ValidationResult = require('../validation/ValidationResult'); + +const IncompatibleProtocolVersionError = require('../errors/consensus/basic/IncompatibleProtocolVersionError'); +const { latestVersion } = require('./protocolVersion'); + +/** + * @param {DashPlatformProtocol} dpp + * @param versionCompatibilityMap + * @returns {validateProtocolVersion} + */ +function validateProtocolVersionFactory(dpp, versionCompatibilityMap) { + /** + * @typedef {validateProtocolVersion} + * @param {number} protocolVersion + * @returns {ValidationResult} + */ + function validateProtocolVersion(protocolVersion) { + const result = new ValidationResult(); + + // Parsed protocol version must be equal or lower than latest protocol version + if (protocolVersion > latestVersion) { + result.addError( + new UnsupportedProtocolVersionError( + protocolVersion, + latestVersion, + ), + ); + + return result; + } + + // The highest version should be used for the compatibility map + // to get minimal compatible version + const maxProtocolVersion = Math.max(protocolVersion, dpp.getProtocolVersion()); + + // The lowest version should be used to compare with the minimal compatible version + const minProtocolVersion = Math.min(protocolVersion, dpp.getProtocolVersion()); + + if (!Object.prototype.hasOwnProperty.call(versionCompatibilityMap, maxProtocolVersion)) { + throw new CompatibleProtocolVersionIsNotDefinedError(maxProtocolVersion); + } + + const minimalCompatibleProtocolVersion = versionCompatibilityMap[maxProtocolVersion]; + + // Parsed protocol version (or current network protocol version) must higher + // or equal to the minimum compatible version + if (minProtocolVersion < minimalCompatibleProtocolVersion) { + result.addError( + new IncompatibleProtocolVersionError( + protocolVersion, + minimalCompatibleProtocolVersion, + ), + ); + + return result; + } + + return result; + } + + return validateProtocolVersion; +} + +module.exports = validateProtocolVersionFactory; diff --git a/packages/js-dpp/package.json b/packages/js-dpp/package.json new file mode 100644 index 00000000000..97c6cfea517 --- /dev/null +++ b/packages/js-dpp/package.json @@ -0,0 +1,107 @@ +{ + "name": "@dashevo/dpp", + "version": "0.23.0-dev.4", + "description": "The JavaScript implementation of the Dash Platform Protocol", + "scripts": { + "lint": "eslint .", + "test": "yarn run test:coverage && yarn run test:browsers", + "build:web": "webpack --stats-error-details", + "test:node": "NODE_ENV=test mocha", + "test:browsers": "karma start ./karma.conf.js --single-run", + "test:coverage": "NODE_ENV=test nyc --check-coverage --stmts=98 --branch=94 --funcs=95 --lines=97 yarn run mocha 'test/unit/**/*.spec.js' 'test/integration/**/*.spec.js'", + "prepublishOnly": "yarn run build:web" + }, + "ultra": { + "concurrent": [ + "test" + ] + }, + "main": "lib/index.js", + "contributors": [ + { + "name": "Ivan Shumkov", + "email": "ivan@shumkov.ru", + "url": "https://github.com/shumkov" + }, + { + "name": "Djavid Gabibiyan", + "email": "djavid@dash.org", + "url": "https://github.com/jawid-h" + }, + { + "name": "Anton Suprunchuk", + "email": "anton.suprunchuk@dash.org", + "url": "https://github.com/antouhou" + }, + { + "name": "Konstantin Shuplenkov", + "email": "konstantin.shuplenkov@dash.org", + "url": "https://github.com/shuplenkov" + } + ], + "license": "MIT", + "devDependencies": { + "@babel/core": "^7.15.5", + "@babel/preset-env": "^7.15.4", + "acorn": "^8.5.0", + "assert": "^2.0.0", + "babel-loader": "^8.2.2", + "buffer": "^6.0.3", + "chai": "^4.3.4", + "chai-as-promised": "^7.1.1", + "chai-exclude": "^2.1.0", + "chai-string": "^1.5.0", + "core-js": "^3.17.2", + "crypto-browserify": "^3.12.0", + "dirty-chai": "^2.0.1", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2", + "events": "^3.3.0", + "https-browserify": "^1.0.0", + "karma": "^6.3.4", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^3.1.0", + "karma-firefox-launcher": "^2.1.1", + "karma-mocha": "^2.0.1", + "karma-mocha-reporter": "^2.2.5", + "karma-webpack": "^5.0.0", + "mocha": "^9.1.2", + "node-inspect-extracted": "^1.0.8", + "nyc": "^15.1.0", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "sinon": "^11.1.2", + "sinon-chai": "^3.7.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "string_decoder": "^1.3.0", + "url": "^0.11.0", + "util": "^0.12.4", + "webpack": "^5.59.1", + "webpack-cli": "^4.9.1" + }, + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^8.0.0", + "@dashevo/dashcore-lib": "~0.19.39", + "@dashevo/dashpay-contract": "workspace:~", + "@dashevo/dpns-contract": "workspace:~", + "@dashevo/feature-flags-contract": "workspace:~", + "@dashevo/masternode-reward-shares-contract": "workspace:~", + "@dashevo/wasm-re2": "~1.0.2", + "ajv": "^8.6.0", + "ajv-formats": "^2.1.1", + "bignumber.js": "^9.0.1", + "bls-signatures": "^0.2.5", + "bs58": "^4.0.1", + "cbor": "^8.0.0", + "fast-json-patch": "^3.1.0", + "json-schema-diff-validator": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "lodash.clonedeep": "^4.5.0", + "lodash.clonedeepwith": "^4.5.0", + "lodash.get": "^4.4.2", + "lodash.set": "^4.3.2", + "long": "^5.2.0" + } +} diff --git a/packages/js-dpp/schema/dataContract/dataContractMeta.json b/packages/js-dpp/schema/dataContract/dataContractMeta.json new file mode 100644 index 00000000000..ffa9e62f080 --- /dev/null +++ b/packages/js-dpp/schema/dataContract/dataContractMeta.json @@ -0,0 +1,442 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schema.dash.org/dpp-0-4-0/meta/data-contract", + "type": "object", + "$defs": { + "documentProperties": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9-_]{1,64}$": { + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/documentSchema" + } + ], + "unevaluatedProperties": false + } + }, + "propertyNames": { + "pattern": "^[a-zA-Z0-9-_]{1,64}$" + }, + "minProperties": 1, + "maxProperties": 100 + }, + "documentSchemaArray": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/documentSchema" + } + ], + "unevaluatedProperties": false + } + }, + "documentSchema": { + "type": "object", + "properties": { + "$id": { + "type": "string", + "pattern": "^#", + "minLength": 1 + }, + "$comment": { + "$ref": "https://json-schema.org/draft/2020-12/meta/core#/properties/$comment" + }, + "description": { + "$ref": "https://json-schema.org/draft/2020-12/meta/meta-data#/properties/description" + }, + "examples": { + "$ref": "https://json-schema.org/draft/2020-12/meta/meta-data#/properties/examples" + }, + "multipleOf": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/multipleOf" + }, + "maximum": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/maximum" + }, + "exclusiveMaximum": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/exclusiveMaximum" + }, + "minimum": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/minimum" + }, + "exclusiveMinimum": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/exclusiveMinimum" + }, + "maxLength": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/maxLength" + }, + "minLength": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/minLength" + }, + "pattern": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/pattern" + }, + "maxItems": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/maxItems" + }, + "minItems": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/minItems" + }, + "uniqueItems": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/uniqueItems" + }, + "contains": { + "$ref": "https://json-schema.org/draft/2020-12/meta/applicator#/properties/contains" + }, + "maxProperties": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/maxProperties" + }, + "minProperties": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/minProperties" + }, + "required": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/required" + }, + "additionalProperties": { + "type": "boolean", + "const": false + }, + "properties": { + "$ref": "#/$defs/documentProperties" + }, + "dependentSchemas": { + "type": "object", + "minProperties": 1, + "additionalProperties": { + "$ref": "#/$defs/documentSchema" + } + }, + "dependentRequired": { + "type": "object", + "minProperties": 1, + "additionalProperties": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/$defs/stringArray" + } + }, + "const": true, + "enum": { + "type": "array", + "items": true, + "minItems": 1, + "uniqueItems": true + }, + "type": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/type" + }, + "format": { + "$ref": "https://json-schema.org/draft/2020-12/meta/format-annotation#/properties/format" + }, + "contentMediaType": { + "$ref": "https://json-schema.org/draft/2020-12/meta/content#/properties/contentMediaType" + }, + "byteArray": { + "type": "boolean", + "const": true + }, + "prefixItems": { + "$ref": "#/$defs/documentSchemaArray" + }, + "items": true + }, + "dependentSchemas": { + "byteArray": { + "description": "should be used only with array type", + "properties": { + "type": { + "type": "string", + "const": "array" + } + }, + "not": { + "properties": { + "items": { + "type": "array" + } + }, + "required": ["items"] + } + }, + "contentMediaType": { + "if": { + "properties": { + "contentMediaType": { + "const": "application/x.dash.dpp.identifier" + } + } + }, + "then": { + "properties": { + "byteArray": { + "const": true + }, + "minItems": { + "const": 32 + }, + "maxItems": { + "const": 32 + } + }, + "required": ["byteArray", "minItems", "maxItems"] + } + }, + "uniqueItems": { + "description": "prevent slow validation of large non-scalar arrays", + "if": { + "properties": { + "uniqueItems": { + "const": true + }, + "items": { + "type": "object", + "properties": { + "type": { + "anyOf": [ + { + "type": "string", + "enum": ["object", "array"] + }, + { + "type": "array", + "contains": { + "enum": ["object", "array"] + } + } + ] + } + } + } + } + }, + "then": { + "properties": { + "maxItems": { + "type": "number", + "maximum": 100000 + } + }, + "required": ["maxItems"] + } + }, + "pattern": { + "description": "prevent slow pattern matching of large strings", + "properties": { + "maxLength": { + "type": "integer", + "minimum": 0, + "maximum": 50000 + } + }, + "required": [ + "maxLength" + ] + }, + "format": { + "description": "prevent slow format validation of large strings", + "properties": { + "maxLength": { + "type": "integer", + "minimum": 0, + "maximum": 50000 + } + }, + "required": ["maxLength"] + }, + "prefixItems": { + "$comment": "array must not contain undefined item sub schemas", + "properties": { + "items": { + "type": "boolean", + "const": false + } + }, + "required": ["items"] + } + }, + "allOf": [ + { + "$comment": "array must contain items", + "if": { + "properties": { + "type": { + "const": "array" + } + }, + "required": ["type"], + "not": { + "properties": { + "byteArray": true + }, + "required": ["byteArray"] + } + }, + "then": { + "properties": { + "items": true + }, + "required": ["items"] + } + }, + { + "$comment": "array without prefixItems must contain items sub schema", + "if": { + "not": { + "properties": { + "prefixItems": true + }, + "required": ["prefixItems"] + } + }, + "then": { + "properties": { + "items": { + "$ref": "#/$defs/documentSchema" + } + } + } + }, + { + "$comment": "all object properties must be defined", + "if": { + "properties": { + "type": { + "const": "object" + } + }, + "not": { + "properties": { + "$ref": true + }, + "required": ["$ref"] + } + }, + "then": { + "properties": { + "properties": { + "$ref": "#/$defs/documentProperties" + }, + "additionalProperties": { + "$ref": "#/$defs/documentSchema/properties/additionalProperties" + } + }, + "required": ["properties", "additionalProperties"] + } + } + ] + } + }, + "properties": { + "protocolVersion": { + "type": "integer", + "$comment": "Maximum is the latest protocol version" + }, + "$schema": { + "type": "string", + "const": "https://schema.dash.org/dpp-0-4-0/meta/data-contract" + }, + "$id":{ + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "version": { + "type": "integer", + "minimum": 1 + }, + "ownerId":{ + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "documents": { + "type": "object", + "propertyNames": { + "pattern": "^[a-zA-Z0-9-_]{1,64}$" + }, + "additionalProperties": { + "type": "object", + "allOf": [ + { + "properties": { + "indices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 32 + }, + "properties": { + "type": "array", + "items": { + "type": "object", + "propertyNames": { + "maxLength": 256 + }, + "additionalProperties": { + "type": "string", + "enum": ["asc"] + }, + "minProperties": 1, + "maxProperties": 1 + }, + "minItems": 1, + "maxItems": 10 + }, + "unique": { + "type": "boolean" + } + }, + "required": ["properties", "name"], + "additionalProperties": false + }, + "minItems": 1, + "maxItems": 10 + }, + "type": { + "const": "object" + }, + "signatureSecurityLevelRequirement": { + "type": "integer", + "enum": [ + 1, + 2, + 3 + ], + "description": "Public key security level. 1 - Critical, 2 - High, 3 - Medium. If none specified, High level is used" + } + } + }, + { + "$ref": "#/$defs/documentSchema" + } + ], + "unevaluatedProperties": false + }, + "minProperties": 1, + "maxProperties": 100 + }, + "$defs": { + "$ref": "#/$defs/documentProperties" + } + }, + "required": [ + "protocolVersion", + "$schema", + "$id", + "version", + "ownerId", + "documents" + ], + "additionalProperties": false +} diff --git a/packages/js-dpp/schema/dataContract/stateTransition/dataContractCreate.json b/packages/js-dpp/schema/dataContract/stateTransition/dataContractCreate.json new file mode 100644 index 00000000000..89071aae3aa --- /dev/null +++ b/packages/js-dpp/schema/dataContract/stateTransition/dataContractCreate.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "protocolVersion": { + "type": "integer", + "$comment": "Maximum is the latest protocol version" + }, + "type": { + "type": "integer", + "const": 0 + }, + "dataContract": { + "type": "object" + }, + "entropy": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32 + }, + "signaturePublicKeyId": { + "type": "integer", + "minimum": 0 + }, + "signature": { + "type": "array", + "byteArray": true, + "minItems": 65, + "maxItems": 96 + } + }, + "additionalProperties": false, + "required": [ + "protocolVersion", + "type", + "dataContract", + "entropy", + "signaturePublicKeyId", + "signature" + ] +} diff --git a/packages/js-dpp/schema/dataContract/stateTransition/dataContractUpdate.json b/packages/js-dpp/schema/dataContract/stateTransition/dataContractUpdate.json new file mode 100644 index 00000000000..8ca270a4c9a --- /dev/null +++ b/packages/js-dpp/schema/dataContract/stateTransition/dataContractUpdate.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "protocolVersion": { + "type": "integer", + "$comment": "Maximum is the latest protocol version" + }, + "type": { + "type": "integer", + "const": 4 + }, + "dataContract": { + "type": "object" + }, + "signaturePublicKeyId": { + "type": "integer", + "minimum": 0 + }, + "signature": { + "type": "array", + "byteArray": true, + "minItems": 65, + "maxItems": 96 + } + }, + "additionalProperties": false, + "required": [ + "protocolVersion", + "type", + "dataContract", + "signaturePublicKeyId", + "signature" + ] +} diff --git a/packages/js-dpp/schema/document/documentBase.json b/packages/js-dpp/schema/document/documentBase.json new file mode 100644 index 00000000000..d745088cd11 --- /dev/null +++ b/packages/js-dpp/schema/document/documentBase.json @@ -0,0 +1,55 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "$protocolVersion": { + "type": "integer", + "$comment": "Maximum is the latest protocol version" + }, + "$id": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "$type": { + "type": "string" + }, + "$revision": { + "type": "integer", + "minimum": 1 + }, + "$dataContractId": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "$ownerId": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "$createdAt": { + "type": "integer", + "minimum": 0 + }, + "$updatedAt": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "$protocolVersion", + "$id", + "$type", + "$revision", + "$dataContractId", + "$ownerId" + ], + "additionalProperties": false +} diff --git a/packages/js-dpp/schema/document/stateTransition/documentTransition/base.json b/packages/js-dpp/schema/document/stateTransition/documentTransition/base.json new file mode 100644 index 00000000000..7b74ddb4b87 --- /dev/null +++ b/packages/js-dpp/schema/document/stateTransition/documentTransition/base.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "$id": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "$type": { + "type": "string" + }, + "$action": { + "type": "integer", + "enum": [0, 1, 3] + }, + "$dataContractId": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier" + } + }, + "required": [ + "$id", + "$type", + "$action", + "$dataContractId" + ], + "additionalProperties": false +} diff --git a/packages/js-dpp/schema/document/stateTransition/documentTransition/create.json b/packages/js-dpp/schema/document/stateTransition/documentTransition/create.json new file mode 100644 index 00000000000..399cb5c4f7d --- /dev/null +++ b/packages/js-dpp/schema/document/stateTransition/documentTransition/create.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "$entropy": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32 + }, + "$createdAt": { + "type": "integer", + "minimum": 0 + }, + "$updatedAt": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "$entropy" + ], + "additionalProperties": false +} diff --git a/packages/js-dpp/schema/document/stateTransition/documentTransition/replace.json b/packages/js-dpp/schema/document/stateTransition/documentTransition/replace.json new file mode 100644 index 00000000000..1fde6adba6f --- /dev/null +++ b/packages/js-dpp/schema/document/stateTransition/documentTransition/replace.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "$revision": { + "type": "integer", + "minimum": 1 + }, + "$updatedAt": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "$revision" + ], + "additionalProperties": false +} diff --git a/packages/js-dpp/schema/document/stateTransition/documentsBatch.json b/packages/js-dpp/schema/document/stateTransition/documentsBatch.json new file mode 100644 index 00000000000..01e4d2bad61 --- /dev/null +++ b/packages/js-dpp/schema/document/stateTransition/documentsBatch.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "protocolVersion": { + "type": "integer", + "$comment": "Maximum is the latest protocol version" + }, + "type": { + "type": "integer", + "const": 1 + }, + "ownerId": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "transitions": { + "type": "array", + "items": { + "type": "object" + }, + "minItems": 1, + "maxItems": 10 + }, + "signaturePublicKeyId": { + "type": "integer", + "minimum": 0 + }, + "signature": { + "type": "array", + "byteArray": true, + "minItems": 65, + "maxItems": 96 + } + }, + "additionalProperties": false, + "required": [ + "protocolVersion", + "type", + "ownerId", + "transitions", + "signaturePublicKeyId", + "signature" + ] +} diff --git a/packages/js-dpp/schema/identity/identity.json b/packages/js-dpp/schema/identity/identity.json new file mode 100644 index 00000000000..1d940b4f389 --- /dev/null +++ b/packages/js-dpp/schema/identity/identity.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "protocolVersion": { + "type": "integer", + "$comment": "Maximum is the latest protocol version" + }, + "id": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "publicKeys": { + "type": "array", + "minItems": 1, + "maxItems": 32, + "uniqueItems": true + }, + "balance": { + "type": "integer", + "minimum": 0 + }, + "revision": { + "type": "integer", + "minimum": 0, + "description": "Identity update revision" + } +}, + "required": [ + "protocolVersion", + "id", + "publicKeys", + "balance", + "revision" + ] +} diff --git a/packages/js-dpp/schema/identity/publicKey.json b/packages/js-dpp/schema/identity/publicKey.json new file mode 100644 index 00000000000..449eb6fd0e5 --- /dev/null +++ b/packages/js-dpp/schema/identity/publicKey.json @@ -0,0 +1,150 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "id": { + "type": "integer", + "minimum": 0, + "description": "Public key ID", + "$comment": "Must be unique for the identity. It can’t be changed after adding a key. Included when signing state transitions to indicate which identity key was used to sign." + }, + "type": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3 + ], + "description": "Public key type. 0 - ECDSA Secp256k1, 1 - BLS 12-381, 2 - ECDSA Secp256k1 Hash160, 3 - BIP 13 Hash160", + "$comment": "It can't be changed after adding a key" + }, + "purpose": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3 + ], + "description": "Public key purpose. 0 - Authentication, 1 - Encryption, 2 - Decryption, 3 - Withdraw", + "$comment": "It can't be changed after adding a key" + }, + "securityLevel": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3 + ], + "description": "Public key security level. 0 - Master, 1 - Critical, 2 - High, 3 - Medium", + "$comment": "It can't be changed after adding a key" + }, + "data": true, + "readOnly": { + "type": "boolean", + "description": "Read only", + "$comment": "Identity public key can't be modified with readOnly set to true. It can’t be changed after adding a key" + }, + "disabledAt": { + "type": "integer", + "description": "Timestamp indicating that the key was disabled at a specified time", + "minimum": 0 + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "const": 0 + } + } + }, + "then": { + "properties": { + "data": { + "type": "array", + "byteArray": true, + "minItems": 33, + "maxItems": 33, + "description": "Raw ECDSA public key", + "$comment": "It must be a valid key of the specified type and unique for the identity. It can’t be changed after adding a key" + } + } + } + }, + { + "if": { + "properties": { + "type": { + "const": 1 + } + } + }, + "then": { + "properties": { + "data": { + "type": "array", + "byteArray": true, + "minItems": 48, + "maxItems": 48, + "description": "Raw BLS public key", + "$comment": "It must be a valid key of the specified type and unique for the identity. It can’t be changed after adding a key" + } + } + } + }, + { + "if": { + "properties": { + "type": { + "const": 2 + } + } + }, + "then": { + "properties": { + "data": { + "type": "array", + "byteArray": true, + "minItems": 20, + "maxItems": 20, + "description": "ECDSA Secp256k1 public key Hash160", + "$comment": "It must be a valid key hash of the specified type and unique for the identity. It can’t be changed after adding a key" + } + } + } + }, + { + "if": { + "properties": { + "type": { + "const": 3 + } + } + }, + "then": { + "properties": { + "data": { + "type": "array", + "byteArray": true, + "minItems": 20, + "maxItems": 20, + "description": "BIP13 script public key", + "$comment": "It must be a valid script hash of the specified type and unique for the identity" + } + } + } + } + ], + "required": [ + "id", + "type", + "data", + "purpose", + "securityLevel" + ], + "additionalProperties": false +} diff --git a/packages/js-dpp/schema/identity/stateTransition/assetLockProof/chainAssetLockProof.json b/packages/js-dpp/schema/identity/stateTransition/assetLockProof/chainAssetLockProof.json new file mode 100644 index 00000000000..e5da91cbc26 --- /dev/null +++ b/packages/js-dpp/schema/identity/stateTransition/assetLockProof/chainAssetLockProof.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "type": { + "type": "integer", + "const": 1 + }, + "coreChainLockedHeight": { + "type": "integer", + "minimum": 1, + "maximum": 4294967295 + }, + "outPoint": { + "type": "array", + "byteArray": true, + "minItems": 36, + "maxItems": 36 + } + }, + "additionalProperties": false, + "required": [ + "type", + "coreChainLockedHeight", + "outPoint" + ] +} diff --git a/packages/js-dpp/schema/identity/stateTransition/assetLockProof/instantAssetLockProof.json b/packages/js-dpp/schema/identity/stateTransition/assetLockProof/instantAssetLockProof.json new file mode 100644 index 00000000000..5a5441b432f --- /dev/null +++ b/packages/js-dpp/schema/identity/stateTransition/assetLockProof/instantAssetLockProof.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "type": { + "type": "integer", + "const": 0 + }, + "instantLock": { + "type": "array", + "byteArray": true, + "minItems": 165, + "maxItems": 100000 + }, + "transaction": { + "type": "array", + "byteArray": true, + "minItems": 1, + "maxItems": 100000 + }, + "outputIndex": { + "type": "integer", + "minimum": 0 + } + }, + "additionalProperties": false, + "required": [ + "type", + "instantLock", + "transaction", + "outputIndex" + ] +} diff --git a/packages/js-dpp/schema/identity/stateTransition/identityCreate.json b/packages/js-dpp/schema/identity/stateTransition/identityCreate.json new file mode 100644 index 00000000000..3eb2227a2f7 --- /dev/null +++ b/packages/js-dpp/schema/identity/stateTransition/identityCreate.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "protocolVersion": { + "type": "integer", + "$comment": "Maximum is the latest protocol version" + }, + "type": { + "type": "integer", + "const": 2 + }, + "assetLockProof": { + "type": "object" + }, + "publicKeys": { + "type": "array", + "minItems": 1, + "maxItems": 10, + "uniqueItems": true + }, + "signature": { + "type": "array", + "byteArray": true, + "minItems": 65, + "maxItems": 65, + "description": "Signature made by AssetLock one time ECDSA key" + } + }, + "additionalProperties": false, + "required": [ + "protocolVersion", + "type", + "assetLockProof", + "publicKeys", + "signature" + ] +} diff --git a/packages/js-dpp/schema/identity/stateTransition/identityTopUp.json b/packages/js-dpp/schema/identity/stateTransition/identityTopUp.json new file mode 100644 index 00000000000..73be31c5200 --- /dev/null +++ b/packages/js-dpp/schema/identity/stateTransition/identityTopUp.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "protocolVersion": { + "type": "integer", + "$comment": "Maximum is the latest protocol version" + }, + "type": { + "type": "integer", + "const": 3 + }, + "assetLockProof": { + "type": "object" + }, + "identityId": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "signature": { + "type": "array", + "byteArray": true, + "minItems": 65, + "maxItems": 65, + "description": "Signature made by AssetLock one time ECDSA key" + } + }, + "additionalProperties": false, + "required": [ + "protocolVersion", + "type", + "assetLockProof", + "identityId", + "signature" + ] +} diff --git a/packages/js-dpp/schema/identity/stateTransition/identityUpdate.json b/packages/js-dpp/schema/identity/stateTransition/identityUpdate.json new file mode 100644 index 00000000000..320195e5a3b --- /dev/null +++ b/packages/js-dpp/schema/identity/stateTransition/identityUpdate.json @@ -0,0 +1,85 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "protocolVersion": { + "type": "integer", + "$comment": "Maximum is the latest protocol version" + }, + "type": { + "type": "integer", + "const": 5 + }, + "identityId": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "signature": { + "type": "array", + "byteArray": true, + "minItems": 65, + "maxItems": 96 + }, + "revision": { + "type": "integer", + "minimum": 0, + "description": "Identity update revision" + }, + "publicKeysDisabledAt": { + "type": "integer", + "minimum": 0 + }, + "addPublicKeys": { + "type": "array", + "minItems": 1, + "maxItems": 10, + "uniqueItems": true + }, + "disablePublicKeys": { + "type": "array", + "minItems": 1, + "maxItems": 10, + "uniqueItems": true, + "items": { + "type": "integer", + "minimum": 0 + } + }, + "signaturePublicKeyId": { + "type": "integer", + "minimum": 0 + } + }, + "dependentRequired" : { + "disablePublicKeys": ["publicKeysDisabledAt"], + "publicKeysDisabledAt": ["disablePublicKeys"] + }, + "anyOf": [ + { + "type": "object", + "required": ["addPublicKeys"], + "properties": { + "addPublicKeys": true + } + }, + { + "type": "object", + "required": ["disablePublicKeys"], + "properties": { + "disablePublicKeys": true + } + } + ], + "additionalProperties": false, + "required": [ + "protocolVersion", + "type", + "identityId", + "signature", + "revision", + "signaturePublicKeyId" + ] +} diff --git a/packages/js-dpp/schema/identity/stateTransition/publicKey.json b/packages/js-dpp/schema/identity/stateTransition/publicKey.json new file mode 100644 index 00000000000..8316fe6e972 --- /dev/null +++ b/packages/js-dpp/schema/identity/stateTransition/publicKey.json @@ -0,0 +1,146 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "id": { + "type": "integer", + "minimum": 0, + "description": "Public key ID", + "$comment": "Must be unique for the identity. It can’t be changed after adding a key. Included when signing state transitions to indicate which identity key was used to sign." + }, + "type": { + "type": "integer", + "enum": [ + 0, + 1, + 2 + ], + "description": "Public key type. 0 - ECDSA Secp256k1, 1 - BLS 12-381, 2 - ECDSA Secp256k1 Hash160", + "$comment": "It can't be changed after adding a key" + }, + "purpose": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3 + ], + "description": "Public key purpose. 0 - Authentication, 1 - Encryption, 2 - Decryption, 3 - Withdraw", + "$comment": "It can't be changed after adding a key" + }, + "securityLevel": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3 + ], + "description": "Public key security level. 0 - Master, 1 - Critical, 2 - High, 3 - Medium", + "$comment": "It can't be changed after adding a key" + }, + "data": true, + "readOnly": { + "type": "boolean", + "description": "Read only", + "$comment": "Identity public key can't be modified with readOnly set to true. It can’t be changed after adding a key" + }, + "signature": true + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "const": 0 + } + } + }, + "then": { + "properties": { + "data": { + "type": "array", + "byteArray": true, + "minItems": 33, + "maxItems": 33, + "description": "Raw ECDSA Secp256k1 public key", + "$comment": "It must be a valid key of the specified type and unique for the identity. It can’t be changed after adding a key" + }, + "signature": { + "type": "array", + "byteArray": true, + "description": "ECDSA Secp256k1 signature to prove ownership of public key", + "minItems": 65, + "maxItems": 65 + } + } + } + }, + { + "if": { + "properties": { + "type": { + "const": 1 + } + } + }, + "then": { + "properties": { + "data": { + "type": "array", + "byteArray": true, + "minItems": 48, + "maxItems": 48, + "description": "Raw BLS public key", + "$comment": "It must be a valid key of the specified type and unique for the identity. It can’t be changed after adding a key" + }, + "signature": { + "type": "array", + "byteArray": true, + "description": "BLS signature to prove ownership of public key", + "minItems": 96, + "maxItems": 96 + } + } + } + }, + { + "if": { + "properties": { + "type": { + "const": 2 + } + } + }, + "then": { + "properties": { + "data": { + "type": "array", + "byteArray": true, + "minItems": 20, + "maxItems": 20, + "description": "ECDSA Secp256k1 public key Hash160", + "$comment": "It must be a valid key hash of the specified type and unique for the identity. It can’t be changed after adding a key" + }, + "signature": { + "type": "array", + "byteArray": true, + "description": "ECDSA signature to prove ownership of public key", + "minItems": 65, + "maxItems": 65 + } + } + } + } + ], + "required": [ + "id", + "type", + "data", + "purpose", + "securityLevel", + "signature" + ], + "additionalProperties": false +} diff --git a/packages/js-dpp/test/.eslintrc b/packages/js-dpp/test/.eslintrc new file mode 100644 index 00000000000..720ced73852 --- /dev/null +++ b/packages/js-dpp/test/.eslintrc @@ -0,0 +1,12 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + }, + "globals": { + "expect": true + } +} diff --git a/packages/js-dpp/test/integration/DashPlatformProtocol.spec.js b/packages/js-dpp/test/integration/DashPlatformProtocol.spec.js new file mode 100644 index 00000000000..bbdc521222a --- /dev/null +++ b/packages/js-dpp/test/integration/DashPlatformProtocol.spec.js @@ -0,0 +1,53 @@ +const DashPlatformProtocol = require('../../lib/DashPlatformProtocol'); +const protocolVersion = require('../../lib/version/protocolVersion'); +const getChainAssetLockProofFixture = require('../../lib/test/fixtures/getChainAssetLockProofFixture'); +const generateRandomIdentifier = require('../../lib/test/utils/generateRandomIdentifier'); + +describe('DashPlatformProtocol', () => { + let dpp; + + beforeEach(async () => { + dpp = new DashPlatformProtocol({}); + await dpp.initialize(); + }); + + it('should propagate protocol version to factories', async () => { + let dataContract = dpp.dataContract.create(generateRandomIdentifier(), { + niceDocument: { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['$createdAt'], + additionalProperties: false, + }, + }); + const document = dpp.document.create(dataContract, generateRandomIdentifier(), 'niceDocument', {}); + let identity = dpp.identity.create(getChainAssetLockProofFixture(), []); + + expect(dataContract.protocolVersion).to.equal(protocolVersion.latestVersion); + expect(document.protocolVersion).to.equal(protocolVersion.latestVersion); + expect(identity.protocolVersion).to.equal(protocolVersion.latestVersion); + + dpp.setProtocolVersion(42); + + dataContract = dpp.dataContract.create(generateRandomIdentifier(), { + niceDocument: { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['$createdAt'], + additionalProperties: false, + }, + }); + identity = dpp.identity.create(getChainAssetLockProofFixture(), []); + + expect(dataContract.protocolVersion).to.equal(42); + expect(identity.protocolVersion).to.equal(42); + }); +}); diff --git a/packages/js-dpp/test/integration/ajv/keywords/byteArray/addByteArrayKeyword.spec.js b/packages/js-dpp/test/integration/ajv/keywords/byteArray/addByteArrayKeyword.spec.js new file mode 100644 index 00000000000..a8bca3b6fc0 --- /dev/null +++ b/packages/js-dpp/test/integration/ajv/keywords/byteArray/addByteArrayKeyword.spec.js @@ -0,0 +1,148 @@ +const { default: Ajv } = require('ajv'); + +const addByteArrayKeyword = require('../../../../../lib/ajv/keywords/byteArray/addByteArrayKeyword'); +const byteArray = require('../../../../../lib/ajv/keywords/byteArray/byteArray'); + +describe('addByteArrayKeyword', () => { + let ajv; + + beforeEach(() => { + ajv = new Ajv(); + }); + + it('should add byteArray keyword', () => { + addByteArrayKeyword(ajv); + + expect( + ajv.getKeyword('byteArray'), + ).to.deep.equal(byteArray); + }); + + describe('byteArray', () => { + beforeEach(() => { + addByteArrayKeyword(ajv); + }); + + describe('compilation', () => { + it('should be used with array type', () => { + const schema = { + type: 'string', + byteArray: true, + }; + + ajv.validate(schema, Buffer.alloc(0)); + + expect(ajv.errors).to.have.lengthOf(1); + expect(ajv.errors[0].keyword).to.equal('type'); + expect(ajv.errors[0].params.type).to.equal('string'); + }); + + it('should not be used with `items` keyword', () => { + const schema = { + type: 'array', + byteArray: true, + items: { + type: 'string', + }, + }; + + try { + ajv.validate(schema, Buffer.alloc(0)); + + expect.fail('should fail with keyword schema error'); + } catch (e) { + expect(e.message).to.equal('\'byteArray\' should not be used with \'items\''); + } + }); + + it('should be boolean', () => { + const schema = { + type: 'array', + byteArray: 'something', + }; + + try { + ajv.validate(schema, Buffer.alloc(0)); + + expect.fail('should fail with keyword schema error'); + } catch (e) { + expect(e.message).to.equal('keyword "byteArray" value is invalid at path "#": data must be boolean'); + } + }); + + it('should have value of true', () => { + const schema = { + type: 'array', + byteArray: false, + }; + + try { + ajv.validate(schema, Buffer.alloc(0)); + + expect.fail('should fail with keyword schema error'); + } catch (e) { + expect(e.message).to.equal('keyword "byteArray" value is invalid at path "#": data must be equal to constant'); + } + }); + }); + + describe('validation', () => { + it('should accept array of integers', () => { + const schema = { + type: 'array', + byteArray: true, + }; + + ajv.validate(schema, ['string']); + + expect(ajv.errors).to.have.lengthOf(2); + + const [error, byteArrayError] = ajv.errors; + + expect(error.keyword).to.equal('type'); + expect(error.schemaPath).to.equal('#/byteArray/items/type'); + expect(error.message).to.equal('must be integer'); + + expect(byteArrayError.keyword).to.equal('byteArray'); + }); + + it('should accept array of integers not less than 0', () => { + const schema = { + type: 'array', + byteArray: true, + }; + + ajv.validate(schema, [-1]); + + expect(ajv.errors).to.have.lengthOf(2); + + const [error, byteArrayError] = ajv.errors; + + expect(error.keyword).to.equal('minimum'); + expect(error.schemaPath).to.equal('#/byteArray/items/minimum'); + expect(error.message).to.equal('must be >= 0'); + + expect(byteArrayError.keyword).to.equal('byteArray'); + }); + + it('should accept array of integers not greater than 255', () => { + const schema = { + type: 'array', + byteArray: true, + }; + + ajv.validate(schema, [0, 256]); + + expect(ajv.errors).to.have.lengthOf(2); + + const [error, byteArrayError] = ajv.errors; + + expect(error.keyword).to.equal('maximum'); + expect(error.schemaPath).to.equal('#/byteArray/items/maximum'); + expect(error.message).to.equal('must be <= 255'); + + expect(byteArrayError.keyword).to.equal('byteArray'); + }); + }); + }); +}); diff --git a/packages/js-dpp/test/integration/dataContract/DataContractFacade.spec.js b/packages/js-dpp/test/integration/dataContract/DataContractFacade.spec.js new file mode 100644 index 00000000000..01644c130de --- /dev/null +++ b/packages/js-dpp/test/integration/dataContract/DataContractFacade.spec.js @@ -0,0 +1,84 @@ +const DashPlatformProtocol = require('../../../lib/DashPlatformProtocol'); + +const DataContract = require('../../../lib/dataContract/DataContract'); + +const DataContractCreateTransition = require('../../../lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition'); + +const ValidationResult = require('../../../lib/validation/ValidationResult'); + +const getDataContractFixture = require('../../../lib/test/fixtures/getDataContractFixture'); + +const DataContractFactory = require('../../../lib/dataContract/DataContractFactory'); + +describe('DataContractFacade', () => { + let dpp; + let dataContract; + let dataContractFactory; + + beforeEach(async () => { + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + dataContract = getDataContractFixture(); + + dataContractFactory = new DataContractFactory( + dpp, + undefined, + undefined, + ); + }); + + describe('create', () => { + it('should create DataContract', () => { + const result = dpp.dataContract.create( + dataContract.getOwnerId(), + dataContract.getDocuments(), + ); + + expect(result).to.be.an.instanceOf(DataContract); + + expect(result.getOwnerId()).to.deep.equal(dataContract.getOwnerId()); + expect(result.getDocuments()).to.equal(dataContract.getDocuments()); + }); + }); + + describe('createFromObject', () => { + it('should create DataContract from plain object', async () => { + const result = await dpp.dataContract.createFromObject(dataContract.toObject()); + + expect(result).to.be.an.instanceOf(DataContract); + + expect(result.toObject()).to.deep.equal(dataContract.toObject()); + }); + }); + + describe('createFromBuffer', () => { + it('should create DataContract from string', async () => { + const result = await dpp.dataContract.createFromBuffer(dataContract.toBuffer()); + + expect(result).to.be.an.instanceOf(DataContract); + + expect(result.toObject()).to.deep.equal(dataContract.toObject()); + }); + }); + + describe('createDataContractCreateTransition', () => { + it('should create DataContractCreateTransition from DataContract', () => { + const stateTransition = dataContractFactory.createDataContractCreateTransition(dataContract); + + const result = dpp.dataContract.createDataContractCreateTransition(dataContract); + + expect(result).to.be.an.instanceOf(DataContractCreateTransition); + + expect(result.toObject()).to.deep.equal(stateTransition.toObject()); + }); + }); + + describe('validate', () => { + it('should validate DataContract', async () => { + const result = await dpp.dataContract.validate(dataContract); + + expect(result).to.be.an.instanceOf(ValidationResult); + }); + }); +}); diff --git a/packages/js-dpp/test/integration/dataContract/stateTransition/DataContractCreateTransition/validation/basic/validateDataContractCreateTransitionBasicFactory.spec.js b/packages/js-dpp/test/integration/dataContract/stateTransition/DataContractCreateTransition/validation/basic/validateDataContractCreateTransitionBasicFactory.spec.js new file mode 100644 index 00000000000..a2ea1cb9178 --- /dev/null +++ b/packages/js-dpp/test/integration/dataContract/stateTransition/DataContractCreateTransition/validation/basic/validateDataContractCreateTransitionBasicFactory.spec.js @@ -0,0 +1,357 @@ +const crypto = require('crypto'); + +const { getRE2Class } = require('@dashevo/wasm-re2'); + +const createAjv = require('../../../../../../../lib/ajv/createAjv'); + +const JsonSchemaValidator = require('../../../../../../../lib/validation/JsonSchemaValidator'); + +const protocolVersion = require('../../../../../../../lib/version/protocolVersion'); + +const validateDataContractCreateTransitionBasicFactory = require('../../../../../../../lib/dataContract/stateTransition/DataContractCreateTransition/validation/basic/validateDataContractCreateTransitionBasicFactory'); + +const DataContractCreateTransition = require('../../../../../../../lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition'); + +const getDataContractFixture = require('../../../../../../../lib/test/fixtures/getDataContractFixture'); + +const { + expectValidationError, + expectJsonSchemaError, +} = require('../../../../../../../lib/test/expect/expectError'); + +const ValidationResult = require('../../../../../../../lib/validation/ValidationResult'); + +const InvalidDataContractIdError = require('../../../../../../../lib/errors/consensus/basic/dataContract/InvalidDataContractIdError'); +const SomeConsensusError = require('../../../../../../../lib/test/mocks/SomeConsensusError'); + +describe('validateDataContractCreateTransitionBasicFactory', () => { + let validateDataContractMock; + let validateDataContractCreateTransitionBasic; + let stateTransition; + let rawStateTransition; + let dataContract; + let rawDataContract; + let validateProtocolVersionMock; + + beforeEach(async function beforeEach() { + validateDataContractMock = this.sinonSandbox.stub().returns(new ValidationResult()); + validateProtocolVersionMock = this.sinonSandbox.stub().returns(new ValidationResult()); + + dataContract = getDataContractFixture(); + rawDataContract = dataContract.toObject(); + + stateTransition = new DataContractCreateTransition({ + protocolVersion: protocolVersion.latestVersion, + dataContract: rawDataContract, + entropy: dataContract.getEntropy(), + signature: Buffer.alloc(65), + signaturePublicKeyId: 0, + }); + + rawStateTransition = stateTransition.toObject(); + + const RE2 = await getRE2Class(); + const ajv = createAjv(RE2); + + const jsonSchemaValidator = new JsonSchemaValidator(ajv); + + // eslint-disable-next-line max-len + validateDataContractCreateTransitionBasic = validateDataContractCreateTransitionBasicFactory( + jsonSchemaValidator, + validateDataContractMock, + validateProtocolVersionMock, + ); + }); + + describe('protocolVersion', () => { + it('should be present', async () => { + delete rawStateTransition.protocolVersion; + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('protocolVersion'); + }); + + it('should be an integer', async () => { + rawStateTransition.protocolVersion = '1'; + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/protocolVersion'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be valid', async () => { + rawStateTransition.protocolVersion = -1; + + const protocolVersionError = new SomeConsensusError('test'); + const protocolVersionResult = new ValidationResult([ + protocolVersionError, + ]); + + validateProtocolVersionMock.returns(protocolVersionResult); + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectValidationError(result, SomeConsensusError); + + const [error] = result.getErrors(); + + expect(error).to.equal(protocolVersionError); + + expect(validateProtocolVersionMock).to.be.calledOnceWith( + rawStateTransition.protocolVersion, + ); + }); + }); + + describe('type', () => { + it('should be present', async () => { + delete rawStateTransition.type; + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('type'); + }); + + it('should be equal to 0', async () => { + rawStateTransition.type = 666; + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/type'); + expect(error.getKeyword()).to.equal('const'); + expect(error.getParams().allowedValue).to.equal(0); + }); + }); + + describe('dataContract', () => { + it('should be present', async () => { + delete rawStateTransition.dataContract; + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('dataContract'); + }); + + it('should be valid', async () => { + const dataContractError = new SomeConsensusError('test'); + const dataContractResult = new ValidationResult([ + dataContractError, + ]); + + validateDataContractMock.returns(dataContractResult); + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.equal(dataContractError); + + expect(validateDataContractMock.getCall(0).args).to.have.deep.members([rawDataContract]); + }); + + it('should return invalid result on invalid Data Contract id', async () => { + const dataContractResult = new ValidationResult(); + + validateDataContractMock.returns(dataContractResult); + + const expectedId = rawStateTransition.dataContract.$id; + rawStateTransition.dataContract.$id = crypto.randomBytes(34); + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(InvalidDataContractIdError); + expect(error.getExpectedId()).to.deep.equal(expectedId); + expect(error.getInvalidId()).to.deep.equal(rawStateTransition.dataContract.$id); + expect(error.getCode()).to.equal(1011); + }); + }); + + describe('entropy', () => { + it('should be present', async () => { + delete rawStateTransition.entropy; + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('entropy'); + }); + + it('should be a byte array', async () => { + rawStateTransition.entropy = new Array(32).fill('string'); + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/entropy/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be no less than 32 bytes', async () => { + rawStateTransition.entropy = Buffer.alloc(31); + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/entropy'); + expect(error.getKeyword()).to.equal('minItems'); + expect(error.getParams().limit).to.equal(32); + }); + + it('should be no longer than 32 bytes', async () => { + rawStateTransition.entropy = Buffer.alloc(33); + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/entropy'); + expect(error.getKeyword()).to.equal('maxItems'); + expect(error.getParams().limit).to.equal(32); + }); + }); + + describe('signature', () => { + it('should be present', async () => { + delete rawStateTransition.signature; + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('signature'); + }); + + it('should be a byte array', async () => { + rawStateTransition.signature = new Array(65).fill('string'); + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/signature/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be not less than 65 bytes', async () => { + rawStateTransition.signature = Buffer.alloc(64); + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/signature'); + expect(error.getKeyword()).to.equal('minItems'); + expect(error.getParams().limit).to.equal(65); + }); + + it('should be not longer than 96 bytes', async () => { + rawStateTransition.signature = Buffer.alloc(97); + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/signature'); + expect(error.getKeyword()).to.equal('maxItems'); + expect(error.getParams().limit).to.equal(96); + }); + }); + + describe('signaturePublicKeyId', () => { + it('should be an integer', async () => { + rawStateTransition.signaturePublicKeyId = 1.4; + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result, 1); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signaturePublicKeyId'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should not be < 0', async () => { + rawStateTransition.signaturePublicKeyId = -1; + + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result, 1); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signaturePublicKeyId'); + expect(error.getKeyword()).to.equal('minimum'); + }); + }); + + it('should return valid result', async () => { + const result = await validateDataContractCreateTransitionBasic(rawStateTransition); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(validateDataContractMock).to.be.calledOnceWith(rawDataContract); + }); +}); diff --git a/packages/js-dpp/test/integration/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateDataContractUpdateTransitionBasicFactory.spec.js b/packages/js-dpp/test/integration/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateDataContractUpdateTransitionBasicFactory.spec.js new file mode 100644 index 00000000000..1bf347e6899 --- /dev/null +++ b/packages/js-dpp/test/integration/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateDataContractUpdateTransitionBasicFactory.spec.js @@ -0,0 +1,444 @@ +const lodashClone = require('lodash.clonedeep'); + +const jsonPatch = require('fast-json-patch'); +const jsonSchemaDiffValidator = require('json-schema-diff-validator'); + +const { getRE2Class } = require('@dashevo/wasm-re2'); + +const createAjv = require('../../../../../../../lib/ajv/createAjv'); + +const JsonSchemaValidator = require('../../../../../../../lib/validation/JsonSchemaValidator'); + +const protocolVersion = require('../../../../../../../lib/version/protocolVersion'); + +const createStateRepositoryMock = require('../../../../../../../lib/test/mocks/createStateRepositoryMock'); + +const validateDataContractUpdateTransitionBasicFactory = require('../../../../../../../lib/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateDataContractUpdateTransitionBasicFactory'); + +const DataContractUpdateTransition = require('../../../../../../../lib/dataContract/stateTransition/DataContractUpdateTransition/DataContractUpdateTransition'); + +const getDataContractFixture = require('../../../../../../../lib/test/fixtures/getDataContractFixture'); + +const { + expectValidationError, + expectJsonSchemaError, +} = require('../../../../../../../lib/test/expect/expectError'); + +const ValidationResult = require('../../../../../../../lib/validation/ValidationResult'); + +const SomeConsensusError = require('../../../../../../../lib/test/mocks/SomeConsensusError'); +const DataContractImmutablePropertiesUpdateError = require('../../../../../../../lib/errors/consensus/basic/dataContract/DataContractImmutablePropertiesUpdateError'); +const IncompatibleDataContractSchemaError = require('../../../../../../../lib/errors/consensus/basic/dataContract/IncompatibleDataContractSchemaError'); +const StateTransitionExecutionContext = require('../../../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('validateDataContractUpdateTransitionBasicFactory', () => { + let validateDataContractMock; + let validateDataContractUpdateTransitionBasic; + let stateTransition; + let rawStateTransition; + let dataContract; + let rawDataContract; + let validateProtocolVersionMock; + let validateIndicesAreNotChangedMock; + let stateRepositoryMock; + let executionContext; + + beforeEach(async function beforeEach() { + validateDataContractMock = this.sinonSandbox.stub().returns(new ValidationResult()); + validateProtocolVersionMock = this.sinonSandbox.stub().returns(new ValidationResult()); + + dataContract = getDataContractFixture(); + + rawDataContract = lodashClone(dataContract.toObject()); + rawDataContract.version += 1; + + stateTransition = new DataContractUpdateTransition({ + protocolVersion: protocolVersion.latestVersion, + dataContract: rawDataContract, + signature: Buffer.alloc(65), + signaturePublicKeyId: 0, + }); + + rawStateTransition = stateTransition.toObject(); + + const RE2 = await getRE2Class(); + const ajv = createAjv(RE2); + + const jsonSchemaValidator = new JsonSchemaValidator(ajv); + + validateIndicesAreNotChangedMock = this.sinonSandbox.stub(); + validateIndicesAreNotChangedMock.returns(new ValidationResult()); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchDataContract.resolves(dataContract); + + executionContext = new StateTransitionExecutionContext(); + + // eslint-disable-next-line max-len + validateDataContractUpdateTransitionBasic = validateDataContractUpdateTransitionBasicFactory( + jsonSchemaValidator, + validateDataContractMock, + validateProtocolVersionMock, + stateRepositoryMock, + jsonSchemaDiffValidator, + validateIndicesAreNotChangedMock, + jsonPatch, + ); + }); + + describe('protocolVersion', () => { + it('should be present', async () => { + delete rawStateTransition.protocolVersion; + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('protocolVersion'); + }); + + it('should be an integer', async () => { + rawStateTransition.protocolVersion = '1'; + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/protocolVersion'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be valid', async () => { + rawStateTransition.protocolVersion = -1; + + const protocolVersionError = new SomeConsensusError('test'); + const protocolVersionResult = new ValidationResult([ + protocolVersionError, + ]); + + validateProtocolVersionMock.returns(protocolVersionResult); + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result, SomeConsensusError); + + const [error] = result.getErrors(); + + expect(error).to.equal(protocolVersionError); + + expect(validateProtocolVersionMock).to.be.calledOnceWith( + rawStateTransition.protocolVersion, + ); + }); + }); + + describe('type', () => { + it('should be present', async () => { + delete rawStateTransition.type; + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('type'); + }); + + it('should be equal to 4', async () => { + rawStateTransition.type = 666; + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/type'); + expect(error.getKeyword()).to.equal('const'); + expect(error.getParams().allowedValue).to.equal(4); + }); + }); + + describe('dataContract', () => { + it('should be present', async () => { + delete rawStateTransition.dataContract; + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('dataContract'); + }); + + it('should have no existing documents removed', async () => { + rawStateTransition.dataContract.documents.indexedDocument = undefined; + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(IncompatibleDataContractSchemaError); + expect(error.getOperation()).to.equal('remove'); + expect(error.getFieldPath()).to.equal('/additionalProperties'); + expect(error.getNewSchema()).to.equal(undefined); + }); + + it('should allow making backward compatible changes to existing documents', async () => { + rawStateTransition.dataContract.documents.indexedDocument.properties.newProp = { + type: 'integer', + minimum: 0, + }; + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expect(result.isValid()).to.be.true(); + }); + + it('should have existing documents schema backward compatible', async () => { + rawStateTransition.dataContract.documents.indexedDocument.properties.firstName = undefined; + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(IncompatibleDataContractSchemaError); + expect(error.getOperation()).to.equal('remove'); + expect(error.getFieldPath()).to.equal('/properties/firstName'); + }); + + it('should allow defining new document', async () => { + rawStateTransition.dataContract.documents.myNewAwesomeDoc = { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['name'], + }; + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expect(result.isValid()).to.be.true(); + }); + + it('should not have root immutable properties changed', async () => { + rawStateTransition.dataContract.$schema = undefined; + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataContractImmutablePropertiesUpdateError); + expect(error.getOperation()).to.equal('remove'); + expect(error.getFieldPath()).to.equal('/$schema'); + }); + + it('should be valid', async () => { + const dataContractError = new SomeConsensusError('test'); + const dataContractResult = new ValidationResult([ + dataContractError, + ]); + + validateDataContractMock.returns(dataContractResult); + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.equal(dataContractError); + + expect(validateDataContractMock.getCall(0).args).to.have.deep.members([rawDataContract]); + }); + }); + + describe('signature', () => { + it('should be present', async () => { + delete rawStateTransition.signature; + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('signature'); + }); + + it('should be a byte array', async () => { + rawStateTransition.signature = new Array(65).fill('string'); + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/signature/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be not less than 65 bytes', async () => { + rawStateTransition.signature = Buffer.alloc(64); + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/signature'); + expect(error.getKeyword()).to.equal('minItems'); + expect(error.getParams().limit).to.equal(65); + }); + + it('should be not longer than 96 bytes', async () => { + rawStateTransition.signature = Buffer.alloc(97); + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/signature'); + expect(error.getKeyword()).to.equal('maxItems'); + expect(error.getParams().limit).to.equal(96); + }); + }); + + describe('signaturePublicKeyId', () => { + it('should be an integer', async () => { + rawStateTransition.signaturePublicKeyId = 1.4; + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result, 1); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signaturePublicKeyId'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should not be < 0', async () => { + rawStateTransition.signaturePublicKeyId = -1; + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result, 1); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signaturePublicKeyId'); + expect(error.getKeyword()).to.equal('minimum'); + }); + }); + + it('should return valid result', async () => { + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(validateDataContractMock).to.be.calledOnceWith(rawDataContract); + }); + + it('should not check Data Contract on dry run', async () => { + stateRepositoryMock.fetchDataContract.resolves(null); + + executionContext.enableDryRun(); + + const result = await validateDataContractUpdateTransitionBasic( + rawStateTransition, + executionContext, + ); + + executionContext.disableDryRun(); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/integration/dataContract/validation/validateDataContractFactory.spec.js b/packages/js-dpp/test/integration/dataContract/validation/validateDataContractFactory.spec.js new file mode 100644 index 00000000000..18f0691cf6b --- /dev/null +++ b/packages/js-dpp/test/integration/dataContract/validation/validateDataContractFactory.spec.js @@ -0,0 +1,2191 @@ +const { getRE2Class } = require('@dashevo/wasm-re2'); +const lodashCloneDeep = require('lodash.clonedeep'); + +const $RefParser = require('@apidevtools/json-schema-ref-parser'); + +const createAjv = require('../../../../lib/ajv/createAjv'); + +const JsonSchemaValidator = require('../../../../lib/validation/JsonSchemaValidator'); + +const ValidationResult = require('../../../../lib/validation/ValidationResult'); + +const validateDataContractFactory = require('../../../../lib/dataContract/validation/validateDataContractFactory'); +const validateDataContractMaxDepthFactory = require('../../../../lib/dataContract/validation/validateDataContractMaxDepthFactory'); +const enrichDataContractWithBaseSchema = require('../../../../lib/dataContract/enrichDataContractWithBaseSchema'); +const validateDataContractPatternsFactory = require('../../../../lib/dataContract/validation/validateDataContractPatternsFactory'); + +const getDataContractFixture = require('../../../../lib/test/fixtures/getDataContractFixture'); + +const { expectJsonSchemaError, expectValidationError } = require('../../../../lib/test/expect/expectError'); + +const DuplicateIndexError = require('../../../../lib/errors/consensus/basic/dataContract/DuplicateIndexError'); +const UndefinedIndexPropertyError = require('../../../../lib/errors/consensus/basic/dataContract/UndefinedIndexPropertyError'); +const InvalidIndexPropertyTypeError = require('../../../../lib/errors/consensus/basic/dataContract/InvalidIndexPropertyTypeError'); +const SystemPropertyIndexAlreadyPresentError = require('../../../../lib/errors/consensus/basic/dataContract/SystemPropertyIndexAlreadyPresentError'); +const UniqueIndicesLimitReachedError = require('../../../../lib/errors/consensus/basic/dataContract/UniqueIndicesLimitReachedError'); +const InvalidIndexedPropertyConstraintError = require('../../../../lib/errors/consensus/basic/dataContract/InvalidIndexedPropertyConstraintError'); +const InvalidCompoundIndexError = require('../../../../lib/errors/consensus/basic/dataContract/InvalidCompoundIndexError'); +const IncompatibleRe2PatternError = require('../../../../lib/errors/consensus/basic/dataContract/IncompatibleRe2PatternError'); +const InvalidJsonSchemaRefError = require('../../../../lib/errors/consensus/basic/dataContract/InvalidJsonSchemaRefError'); +const JsonSchemaCompilationError = require('../../../../lib/errors/consensus/basic/JsonSchemaCompilationError'); +const SomeConsensusError = require('../../../../lib/test/mocks/SomeConsensusError'); +const getPropertyDefinitionByPath = require('../../../../lib/dataContract/getPropertyDefinitionByPath'); + +describe('validateDataContractFactory', function main() { + this.timeout(15000); + + let dataContract; + let rawDataContract; + let validateDataContract; + let RE2; + let validateProtocolVersionMock; + + before(async () => { + RE2 = await getRE2Class(); + }); + + beforeEach(async function beforeEach() { + dataContract = getDataContractFixture(); + rawDataContract = dataContract.toObject(); + + const ajv = createAjv(RE2); + const jsonSchemaValidator = new JsonSchemaValidator(ajv); + + const validateDataContractMaxDepth = validateDataContractMaxDepthFactory($RefParser); + + const validateDataContractPatterns = validateDataContractPatternsFactory(RE2); + + validateProtocolVersionMock = this.sinonSandbox.stub().returns(new ValidationResult()); + + validateDataContract = validateDataContractFactory( + jsonSchemaValidator, + validateDataContractMaxDepth, + enrichDataContractWithBaseSchema, + validateDataContractPatterns, + validateProtocolVersionMock, + getPropertyDefinitionByPath, + ); + }); + + describe('protocolVersion', () => { + it('should be present', async () => { + rawDataContract = { + documents: { + someDocument: { + type: 'object', + }, + }, + }; + delete rawDataContract.protocolVersion; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('protocolVersion'); + }); + + it('should be an integer', async () => { + rawDataContract.protocolVersion = '1'; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/protocolVersion'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be valid', async () => { + rawDataContract.protocolVersion = -1; + + const protocolVersionError = new SomeConsensusError('test'); + const protocolVersionResult = new ValidationResult([ + protocolVersionError, + ]); + + validateProtocolVersionMock.returns(protocolVersionResult); + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, SomeConsensusError); + + const [error] = result.getErrors(); + + expect(error).to.equal(protocolVersionError); + + expect(validateProtocolVersionMock).to.be.calledOnceWith( + rawDataContract.protocolVersion, + ); + }); + }); + + describe('$schema', () => { + it('should be present', async () => { + delete rawDataContract.$schema; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('$schema'); + }); + + it('should be a string', async () => { + rawDataContract.$schema = 1; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$schema'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be a particular url', async () => { + rawDataContract.$schema = 'wrong'; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('const'); + expect(error.getInstancePath()).to.equal('/$schema'); + }); + }); + + describe('ownerId', () => { + it('should be present', async () => { + delete rawDataContract.ownerId; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('ownerId'); + }); + + it('should be a byte array', async () => { + rawDataContract.ownerId = new Array(32).fill('string'); + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/ownerId/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be no less than 32 bytes', async () => { + rawDataContract.ownerId = Buffer.alloc(31); + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/ownerId'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be no longer than 32 bytes', async () => { + rawDataContract.ownerId = Buffer.alloc(33); + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/ownerId'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + + describe('$id', () => { + it('should be present', async () => { + delete rawDataContract.$id; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('$id'); + }); + + it('should be a byte array', async () => { + rawDataContract.$id = new Array(32).fill('string'); + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$id/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be no less than 32 bytes', async () => { + rawDataContract.$id = Buffer.alloc(31); + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$id'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be no longer than 32 bytes', async () => { + rawDataContract.$id = Buffer.alloc(33); + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$id'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + + describe('$defs', () => { + it('may not be present', async () => { + delete rawDataContract.$defs; + delete rawDataContract.documents.prettyDocument; + + const result = await validateDataContract(rawDataContract); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + + it('should be an object', async () => { + rawDataContract.$defs = 1; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$defs'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should not be empty', async () => { + rawDataContract.$defs = {}; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$defs'); + expect(error.getKeyword()).to.equal('minProperties'); + }); + + it('should have no non-alphanumeric properties', async () => { + rawDataContract.$defs = { + $subSchema: {}, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [patternError, propertyNamesError] = result.getErrors(); + + expect(patternError.getInstancePath()).to.equal('/$defs'); + expect(patternError.getKeyword()).to.equal('pattern'); + + expect(propertyNamesError.getInstancePath()).to.equal('/$defs'); + expect(propertyNamesError.getKeyword()).to.equal('propertyNames'); + }); + + it('should have no more than 100 properties', async () => { + rawDataContract.$defs = {}; + + Array(101).fill({ type: 'string' }).forEach((item, i) => { + rawDataContract.$defs[i] = item; + }); + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$defs'); + expect(error.getKeyword()).to.equal('maxProperties'); + }); + + it('should have valid property names', async () => { + const validNames = ['validName', 'valid_name', 'valid-name', 'abc', 'ab12c', 'abc123', 'ValidName', + 'abcdefghigklmnopqrstuvwxyz01234567890abcdefghigklmnopqrstuvwxyz', 'abc_gbf_gdb', 'abc-gbf-gdb', + '-validname', '_validname', 'validname-', 'validname_', 'a', 'ab', '1', '123', '123_', '-123', '_123']; + + await Promise.all( + validNames.map(async (name) => { + const clonedDataContract = lodashCloneDeep(rawDataContract); + + clonedDataContract.$defs = {}; + clonedDataContract.$defs[name] = { + type: 'string', + }; + + clonedDataContract.$defs[name] = { + type: 'string', + }; + + const result = await validateDataContract(clonedDataContract); + + expectJsonSchemaError(result, 0); + }), + ); + }); + + it('should return an invalid result if a property has invalid format', async () => { + const invalidNames = ['*(*&^', '$test', '.', '.a']; + + await Promise.all( + invalidNames.map(async (name) => { + const clonedDataContract = lodashCloneDeep(rawDataContract); + + clonedDataContract.$defs = {}; + clonedDataContract.$defs[name] = { + type: 'string', + }; + + const result = await validateDataContract(clonedDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$defs'); + expect(error.getKeyword()).to.equal('pattern'); + }), + ); + }); + }); + + describe('documents', () => { + it('should be present', async () => { + delete rawDataContract.documents; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('documents'); + }); + + it('should be an object', async () => { + rawDataContract.documents = 1; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/documents'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should not be empty', async () => { + rawDataContract.documents = {}; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/documents'); + expect(error.getKeyword()).to.equal('minProperties'); + }); + + it('should have valid property names (document types)', async () => { + const validNames = ['validName', 'valid_name', 'valid-name', 'abc', 'a123123bc', 'ab123c', 'ValidName', 'validName', + 'abcdefghigklmnopqrstuvwxyz01234567890abcdefghigklmnopqrstuvwxyz', 'abc_gbf_gdb', 'abc-gbf-gdb']; + + await Promise.all( + validNames.map(async (name) => { + const clonedDataContract = lodashCloneDeep(rawDataContract); + + clonedDataContract.documents[name] = clonedDataContract.documents.niceDocument; + + const result = await validateDataContract(clonedDataContract); + + expectJsonSchemaError(result, 0); + }), + ); + }); + + it('should return an invalid result if a property (document type) has invalid format', async () => { + const invalidNames = ['*(*&^', '$test', '.', '.a']; + + await Promise.all( + invalidNames.map(async (name) => { + const clonedDataContract = lodashCloneDeep(rawDataContract); + + clonedDataContract.documents[name] = clonedDataContract.documents.niceDocument; + + const result = await validateDataContract(clonedDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/documents'); + expect(error.getKeyword()).to.equal('pattern'); + }), + ); + }); + + it('should have no more than 100 properties', async () => { + const niceDocumentDefinition = rawDataContract.documents.niceDocument; + + rawDataContract.documents = {}; + + Array(101).fill(niceDocumentDefinition).forEach((item, i) => { + rawDataContract.documents[i] = item; + }); + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/documents'); + expect(error.getKeyword()).to.equal('maxProperties'); + }); + + describe('Document schema', () => { + it('should not be empty', async () => { + rawDataContract.documents.niceDocument.properties = {}; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/niceDocument/properties'); + expect(error.getKeyword()).to.equal('minProperties'); + }); + + it('should have type "object"', async () => { + rawDataContract.documents.niceDocument.type = 'string'; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/niceDocument/type'); + expect(error.getKeyword()).to.equal('const'); + }); + + it('should have "properties"', async () => { + delete rawDataContract.documents.niceDocument.properties; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/niceDocument'); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('properties'); + }); + + it('should have nested "properties"', async () => { + rawDataContract.documents.niceDocument.properties.object = { + type: 'array', + prefixItems: [ + { + type: 'object', + properties: { + something: { + type: 'object', + }, + }, + additionalProperties: false, + }, + ], + items: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 3); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/niceDocument/properties/object/prefixItems/0/properties/something'); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('properties'); + }); + + it('should have valid property names', async () => { + const validNames = ['validName', 'valid_name', 'valid-name', 'abc', 'a123bc', 'abc123', 'ValidName', 'validName', + 'abcdefghigklmnopqrstuvwxyz01234567890abcdefghigklmnopqrstuvwxyz', 'abc_gbf_gdb', 'abc-gbf-gdb']; + + await Promise.all( + validNames.map(async (name) => { + const clonedDataContract = lodashCloneDeep(rawDataContract); + + clonedDataContract.documents.niceDocument.properties[name] = { + type: 'string', + }; + + const result = await validateDataContract(clonedDataContract); + + expectJsonSchemaError(result, 0); + }), + ); + }); + + it('should have valid nested property names', async () => { + const validNames = ['validName', 'valid_name', 'valid-name', 'abc', 'a123bc', 'abc123', 'ValidName', 'validName', + 'abcdefghigklmnopqrstuvwxyz01234567890abcdefghigklmnopqrstuvwxyz', 'abc_gbf_gdb', 'abc-gbf-gdb']; + + rawDataContract.documents.niceDocument.properties.something = { + type: 'object', + properties: {}, + additionalProperties: false, + }; + + await Promise.all( + validNames.map(async (name) => { + const clonedDataContract = lodashCloneDeep(rawDataContract); + + clonedDataContract.documents.niceDocument.properties.something.properties[name] = { + type: 'string', + }; + + const result = await validateDataContract(clonedDataContract); + + expectJsonSchemaError(result, 0); + }), + ); + }); + + it('should return an invalid result if a property has invalid format', async () => { + const invalidNames = ['*(*&^', '$test', '.', '.a']; + + await Promise.all( + invalidNames.map(async (name) => { + const clonedDataContract = lodashCloneDeep(rawDataContract); + + clonedDataContract.documents.niceDocument.properties[name] = {}; + + const result = await validateDataContract(clonedDataContract); + + expectJsonSchemaError(result, 3); + + const errors = result.getErrors(); + + expect(errors[0].instancePath).to.equal('/documents/niceDocument/properties'); + expect(errors[0].keyword).to.equal('pattern'); + expect(errors[1].instancePath).to.equal('/documents/niceDocument/properties'); + expect(errors[1].keyword).to.equal('propertyNames'); + }), + ); + }); + + it('should return an invalid result if a nested property has invalid format', async () => { + const invalidNames = ['*(*&^', '$test', '.', '.a']; + + rawDataContract.documents.niceDocument.properties.something = { + properties: {}, + additionalProperties: false, + }; + + await Promise.all( + invalidNames.map(async (name) => { + const clonedDataContract = lodashCloneDeep(rawDataContract); + + clonedDataContract.documents.niceDocument.properties.something.properties[name] = {}; + + const result = await validateDataContract(clonedDataContract); + + expectJsonSchemaError(result, 4); + + const errors = result.getErrors(); + + expect(errors[0].instancePath).to.equal( + '/documents/niceDocument/properties/something/properties', + ); + expect(errors[0].keyword).to.equal('pattern'); + expect(errors[1].instancePath).to.equal( + '/documents/niceDocument/properties/something/properties', + ); + expect(errors[1].keyword).to.equal('propertyNames'); + }), + ); + }); + + it('should have "additionalProperties" defined', async () => { + delete rawDataContract.documents.niceDocument.additionalProperties; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/niceDocument'); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('additionalProperties'); + }); + + it('should have "additionalProperties" defined to false', async () => { + rawDataContract.documents.niceDocument.additionalProperties = true; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/niceDocument/additionalProperties'); + expect(error.getKeyword()).to.equal('const'); + }); + + it('should have nested "additionalProperties" defined', async () => { + rawDataContract.documents.niceDocument.properties.object = { + type: 'array', + prefixItems: [ + { + type: 'object', + properties: { + something: { + type: 'string', + }, + }, + }, + ], + items: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/niceDocument/properties/object/prefixItems/0'); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('additionalProperties'); + }); + + it('should return invalid result if there are additional properties', async () => { + rawDataContract.additionalProperty = { }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal(''); + expect(error.getKeyword()).to.equal('additionalProperties'); + }); + + it('should have no more than 100 properties', async () => { + const propertyDefinition = { }; + + rawDataContract.documents.niceDocument.properties = {}; + + Array(101).fill(propertyDefinition).forEach((item, i) => { + rawDataContract.documents.niceDocument.properties[i] = item; + }); + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/niceDocument/properties'); + expect(error.getKeyword()).to.equal('maxProperties'); + }); + + it('should have defined items for arrays', async () => { + rawDataContract.documents.new = { + properties: { + something: { + type: 'array', + }, + }, + additionalProperties: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/new/properties/something'); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('items'); + }); + + it('should have sub schema in items for arrays', async () => { + rawDataContract.documents.new = { + properties: { + something: { + type: 'array', + items: [ + { + type: 'string', + }, + { + type: 'number', + }, + ], + }, + }, + additionalProperties: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 3); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/new/properties/something/items'); + expect(error.getKeyword()).to.equal('type'); + expect(error.getParams().type).to.equal('object'); + }); + + it('should have items if prefixItems is used for arrays', async () => { + rawDataContract.documents.new = { + type: 'object', + properties: { + something: { + type: 'array', + prefixItems: [ + { + type: 'string', + }, + { + type: 'number', + }, + ], + minItems: 2, + }, + }, + additionalProperties: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/new/properties/something'); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('items'); + }); + + it('should not have items disabled if prefixItems is used for arrays', async () => { + rawDataContract.documents.new = { + properties: { + something: { + type: 'array', + prefixItems: [ + { + type: 'string', + }, + { + type: 'number', + }, + ], + items: true, + }, + }, + additionalProperties: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/new/properties/something/items'); + expect(error.getKeyword()).to.equal('const'); + expect(error.getParams().allowedValue).to.equal(false); + }); + + it('should return invalid result if "default" keyword is used', async () => { + rawDataContract.documents.indexedDocument.properties.firstName.default = '1'; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/properties/firstName'); + expect(error.getKeyword()).to.equal('unevaluatedProperties'); + }); + + it.skip('should return invalid result if remote `$ref` is used', async () => { + rawDataContract.documents.indexedDocument = { + $ref: 'http://remote.com/schema#', + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/$ref'); + expect(error.getKeyword()).to.equal('pattern'); + }); + + it('should not have `propertyNames`', async () => { + rawDataContract.documents.indexedDocument = { + type: 'object', + properties: { + something: { + type: 'string', + }, + }, + propertyNames: { + pattern: 'abc', + }, + additionalProperties: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument'); + expect(error.getKeyword()).to.equal('unevaluatedProperties'); + expect(error.getParams().unevaluatedProperty).to.equal('propertyNames'); + }); + + it('should have `maxItems` if `uniqueItems` is used', async () => { + rawDataContract.documents.indexedDocument = { + type: 'object', + properties: { + something: { + type: 'array', + uniqueItems: true, + }, + }, + additionalProperties: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/properties/something'); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('items'); + }); + + it('should have `maxItems` no bigger than 100000 if `uniqueItems` is used', async () => { + rawDataContract.documents.indexedDocument = { + type: 'object', + properties: { + something: { + type: 'array', + uniqueItems: true, + maxItems: 200000, + items: { + type: 'object', + properties: { + property: { + type: 'string', + }, + }, + additionalProperties: false, + }, + }, + }, + additionalProperties: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/properties/something/maxItems'); + expect(error.getKeyword()).to.equal('maximum'); + }); + + it('should return invalid result if document JSON Schema is not valid', async () => { + rawDataContract.documents.indexedDocument = { + type: 'object', + properties: { + something: { + type: 'string', + format: 'lalala', + maxLength: 100, + }, + }, + additionalProperties: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, JsonSchemaCompilationError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1004); + + expect(error.message).to.be.a('string').and.satisfy((msg) => ( + msg.startsWith('unknown format "lalala" ignored in schema') + )); + }); + + it('should have `maxLength` if `pattern` is used', async () => { + rawDataContract.documents.indexedDocument = { + type: 'object', + properties: { + something: { + type: 'string', + pattern: 'a', + }, + }, + additionalProperties: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/properties/something'); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('maxLength'); + }); + + it('should have `maxLength` no bigger than 50000 if `pattern` is used', async () => { + rawDataContract.documents.indexedDocument = { + type: 'object', + properties: { + something: { + type: 'string', + pattern: 'a', + maxLength: 60000, + }, + }, + additionalProperties: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/properties/something/maxLength'); + expect(error.getKeyword()).to.equal('maximum'); + }); + + it('should have `maxLength` if `format` is used', async () => { + rawDataContract.documents.indexedDocument = { + type: 'object', + properties: { + something: { + type: 'string', + format: 'url', + }, + }, + additionalProperties: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/properties/something'); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('maxLength'); + }); + + it('should have `maxLength` no bigger than 50000 if `format` is used', async () => { + rawDataContract.documents.indexedDocument = { + type: 'object', + properties: { + something: { + type: 'string', + format: 'url', + maxLength: 60000, + }, + }, + additionalProperties: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/properties/something/maxLength'); + expect(error.getKeyword()).to.equal('maximum'); + }); + + it('should not have incompatible patterns', async () => { + rawDataContract.documents.indexedDocument = { + type: 'object', + properties: { + something: { + type: 'string', + maxLength: 100, + pattern: '^((?!-|_)[a-zA-Z0-9-_]{0,62}[a-zA-Z0-9])$', + }, + }, + additionalProperties: false, + }; + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, IncompatibleRe2PatternError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1009); + expect(error.getPattern()).to.equal('^((?!-|_)[a-zA-Z0-9-_]{0,62}[a-zA-Z0-9])$'); + expect(error.getPath()).to.equal('/documents/indexedDocument/properties/something'); + expect(error.getPatternError()).to.be.instanceOf(Error); + }); + + describe('byteArray', () => { + it('should be a boolean', async () => { + rawDataContract.documents.withByteArrays.properties.byteArrayField.byteArray = 1; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/withByteArrays/properties/byteArrayField/byteArray'); + expect(error.getKeyword()).to.equal('type'); + expect(error.getParams().type).to.equal('boolean'); + }); + + it('should equal to true', async () => { + rawDataContract.documents.withByteArrays.properties.byteArrayField.byteArray = false; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/withByteArrays/properties/byteArrayField/byteArray'); + expect(error.getKeyword()).to.equal('const'); + expect(error.getParams().allowedValue).to.equal(true); + }); + + it('should be used with type `array`', async () => { + rawDataContract.documents.withByteArrays.properties.byteArrayField.type = 'string'; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/withByteArrays/properties/byteArrayField/type'); + expect(error.getKeyword()).to.equal('const'); + }); + + it('should not be used with `items`', async () => { + rawDataContract.documents.withByteArrays.properties.byteArrayField.items = { + type: 'string', + }; + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, JsonSchemaCompilationError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1004); + expect(error.message).to.equal("'byteArray' should not be used with 'items'"); + }); + }); + + describe('contentMediaType', () => { + describe('application/x.dash.dpp.identifier', () => { + it('should be used with byte array only', async () => { + delete rawDataContract.documents.withByteArrays.properties.identifierField.byteArray; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/withByteArrays/properties/identifierField'); + expect(error.getKeyword()).to.equal('required'); + }); + + it('should be used with byte array not shorter than 32 bytes', async () => { + rawDataContract.documents.withByteArrays.properties.identifierField.minItems = 31; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/withByteArrays/properties/identifierField/minItems'); + expect(error.getKeyword()).to.equal('const'); + }); + + it('should be used with byte array not longer than 32 bytes', async () => { + rawDataContract.documents.withByteArrays.properties.identifierField.maxItems = 31; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result, 2); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/withByteArrays/properties/identifierField/maxItems'); + expect(error.getKeyword()).to.equal('const'); + }); + }); + }); + }); + }); + + describe('indices', () => { + it('should be an array', async () => { + rawDataContract.documents.indexedDocument.indices = 'definitely not an array'; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/indices'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should have at least one item', async () => { + rawDataContract.documents.indexedDocument.indices = []; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/indices'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should return invalid result if there are duplicated indices', async () => { + const indexDefinition = { + ...rawDataContract.documents.indexedDocument.indices[0], + name: 'otherIndexName', + }; + + rawDataContract.documents.indexedDocument.indices.push(indexDefinition); + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, DuplicateIndexError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1008); + expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + expect(error.getDocumentType()).to.deep.equal('indexedDocument'); + }); + + it('should return invalid result if there are duplicated index names', async () => { + const indexDefinition = { + ...rawDataContract.documents.indexedDocument.indices[0], + }; + + rawDataContract.documents.indexedDocument.indices.push(indexDefinition); + + const result = await validateDataContract(rawDataContract); + + expect(result.isValid()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1048); + expect(error.getDocumentType()).to.deep.equal('indexedDocument'); + expect(error.getDuplicateIndexName()).to.deep.equal('index1'); + }); + + describe('index', () => { + it('should be an object', async () => { + rawDataContract.documents.indexedDocument.indices = ['something else']; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/indices/0'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should have properties definition', async () => { + rawDataContract.documents.indexedDocument.indices = [{}]; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/indices/0'); + expect(error.getParams().missingProperty).to.equal('properties'); + expect(error.getKeyword()).to.equal('required'); + }); + + describe('properties definition', () => { + it('should be an array', async () => { + rawDataContract.documents.indexedDocument.indices[0] + .properties = 'something else'; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal( + '/documents/indexedDocument/indices/0/properties', + ); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should have at least one property defined', async () => { + rawDataContract.documents.indexedDocument.indices[0] + .properties = []; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal( + '/documents/indexedDocument/indices/0/properties', + ); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should have no more than 10 property $defs', async () => { + for (let i = 0; i < 10; i++) { + rawDataContract.documents.indexedDocument.indices[0] + .properties.push({ [`field${i}`]: 'asc' }); + } + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal( + '/documents/indexedDocument/indices/0/properties', + ); + expect(error.getKeyword()).to.equal('maxItems'); + }); + + describe('property definition', () => { + it('should be an object', async () => { + rawDataContract.documents.indexedDocument.indices[0] + .properties[0] = 'something else'; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal( + '/documents/indexedDocument/indices/0/properties/0', + ); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should have at least one property', async () => { + rawDataContract.documents.indexedDocument.indices[0] + .properties = []; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal( + '/documents/indexedDocument/indices/0/properties', + ); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should have no more than one property', async () => { + const property = rawDataContract.documents.indexedDocument.indices[0] + .properties[0]; + + property.anotherField = 'something'; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal( + '/documents/indexedDocument/indices/0/properties/0', + ); + expect(error.getKeyword()).to.equal('maxProperties'); + }); + + it('should have property values only "asc" or "desc"', async () => { + rawDataContract.documents.indexedDocument.indices[0] + .properties[0].$ownerId = 'wrong'; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal( + '/documents/indexedDocument/indices/0/properties/0/$ownerId', + ); + expect(error.getKeyword()).to.equal('enum'); + }); + }); + }); + + describe('property names', () => { + it('should have valid property names (indices)', async () => { + const validNames = ['validName', 'valid_name', 'valid-name', 'abc', 'a123123bc', 'ab123c', 'ValidName', 'validName', + 'abcdefghigklmnopqrstuvwxyz01234567890abcdefghigklmnopqrstuvwxyz', 'abc_gbf_gdb', 'abc-gbf-gdb']; + + await Promise.all( + validNames.map(async (name) => { + const clonedDataContract = lodashCloneDeep(rawDataContract); + + clonedDataContract.documents.indexedDocument.properties[name] = { type: 'string', maxLength: 63 }; + clonedDataContract.documents.indexedDocument.indices[0].properties.push({ [name]: 'asc' }); + clonedDataContract.documents.indexedDocument.required.push(name); + + const result = await validateDataContract(clonedDataContract); + + expectJsonSchemaError(result, 0); + }), + ); + }); + + it('should return an invalid result if a property (indices) has invalid format', async () => { + const invalidNames = ['a.', '.a']; + + rawDataContract.documents.indexedDocument = { + type: 'object', + properties: { + a: { + type: 'object', + properties: { + property: { + type: 'string', + maxLength: 63, + }, + }, + additionalProperties: false, + }, + }, + indices: [ + { + name: 'index1', + properties: [], + unique: true, + }, + ], + additionalProperties: false, + }; + + await Promise.all( + invalidNames.map(async (name) => { + const clonedDataContract = lodashCloneDeep(rawDataContract); + + clonedDataContract.documents.indexedDocument.indices[0].properties.push({ [name]: 'asc' }); + + const result = await validateDataContract(clonedDataContract); + + expectValidationError(result, UndefinedIndexPropertyError, 1); + }), + ); + }); + }); + + it('should have "unique" flag to be of a boolean type', async () => { + rawDataContract.documents.indexedDocument.indices[0].unique = 12; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/indices/0/unique'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should have no more than 10 indices', async () => { + for (let i = 0; i < 10; i++) { + const propertyName = `field${i}`; + + rawDataContract.documents.indexedDocument.properties[propertyName] = { type: 'string' }; + + rawDataContract.documents.indexedDocument.indices.push({ + properties: [{ [propertyName]: 'asc' }], + }); + } + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal( + '/documents/indexedDocument/indices', + ); + expect(error.getKeyword()).to.equal('maxItems'); + }); + + it('should have no more than 3 unique indices', async () => { + for (let i = 0; i < 4; i++) { + const propertyName = `field${i}`; + + rawDataContract.documents.indexedDocument.properties[propertyName] = { + type: 'string', + maxLength: 63, + }; + + rawDataContract.documents.indexedDocument.indices.push({ + name: `index_${i}`, + properties: [{ [propertyName]: 'asc' }], + unique: true, + }); + } + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, UniqueIndicesLimitReachedError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1017); + expect(error.getDocumentType()).to.equal('indexedDocument'); + }); + + it('should return invalid result if $id is specified as an indexed property', async () => { + const indexDefinition = { + name: 'index_1', + properties: [ + { $id: 'asc' }, + { firstName: 'asc' }, + ], + }; + + const indeciesDefinition = rawDataContract.documents.indexedDocument.indices; + + indeciesDefinition.push(indexDefinition); + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, SystemPropertyIndexAlreadyPresentError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1015); + expect(error.getPropertyName()).to.equal('$id'); + expect(error.getDocumentType()).to.deep.equal('indexedDocument'); + expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + }); + + it('should return invalid result if indices has undefined property', async () => { + const indexDefinition = rawDataContract.documents.indexedDocument.indices[0]; + + indexDefinition.properties.push({ + missingProperty: 'asc', + }); + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, UndefinedIndexPropertyError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1016); + expect(error.getPropertyName()).to.equal('missingProperty'); + expect(error.getDocumentType()).to.deep.equal('indexedDocument'); + expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + }); + + it('should return invalid result if index property is object', async () => { + const indexedDocumentDefinition = rawDataContract.documents.indexedDocument; + + indexedDocumentDefinition.properties.objectProperty = { + type: 'object', + properties: { + something: { + type: 'string', + }, + }, + additionalProperties: false, + }; + + indexedDocumentDefinition.required.push('objectProperty'); + + const indexDefinition = indexedDocumentDefinition.indices[0]; + + indexDefinition.properties.push({ + objectProperty: 'asc', + }); + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidIndexPropertyTypeError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1013); + expect(error.getPropertyName()).to.equal('objectProperty'); + expect(error.getPropertyType()).to.equal('object'); + expect(error.getDocumentType()).to.deep.equal('indexedDocument'); + expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + }); + + it('should return invalid result if index property is an array', async () => { + rawDataContract.documents.indexedArray = { + type: 'object', + indices: [ + { + name: 'index1', + properties: [ + { mentions: 'asc' }, + ], + }, + ], + properties: { + mentions: { + type: 'array', + prefixItems: [ + { + type: 'string', + maxLength: 100, + }, + ], + minItems: 1, + maxItems: 5, + items: false, + }, + }, + additionalProperties: false, + }; + + const indexDefinition = rawDataContract.documents.indexedArray.indices[0]; + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidIndexPropertyTypeError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1013); + expect(error.getPropertyName()).to.equal('mentions'); + expect(error.getPropertyType()).to.equal('array'); + expect(error.getDocumentType()).to.deep.equal('indexedArray'); + expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + }); + + // it('should return invalid result if index property is array of objects', async () => { + // const indexedDocumentDefinition = rawDataContract.documents.indexedDocument; + // + // indexedDocumentDefinition.properties.arrayProperty = { + // type: 'array', + // items: { + // type: 'object', + // properties: { + // something: { + // type: 'string', + // }, + // }, + // additionalProperties: false, + // }, + // }; + // + // indexedDocumentDefinition.required.push('arrayProperty'); + // + // const indexDefinition = indexedDocumentDefinition.indices[0]; + // + // indexDefinition.properties.push({ + // arrayProperty: 'asc', + // }); + // + // const result = await validateDataContract(rawDataContract); + // + // expectValidationError(result, InvalidIndexPropertyTypeError); + // + // const [error] = result.getErrors(); + // + // expect(error.getCode()).to.equal(1013); + // expect(error.getPropertyName()).to.equal('arrayProperty'); + // expect(error.getPropertyType()).to.equal('array'); + // expect(error.getDocumentType()).to.deep.equal('indexedDocument'); + // expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + // }); + + // it('should return invalid result if index property is an array of different types', + // async () => { + // const indexedDocumentDefinition = rawDataContract.documents.indexedArray; + // + // const indexDefinition = indexedDocumentDefinition.indices[0]; + // + // rawDataContract.documents.indexedArray.properties.mentions.prefixItems = [ + // { + // type: 'string', + // }, + // { + // type: 'number', + // }, + // ]; + // + // rawDataContract.documents.indexedArray.properties.mentions.minItems = 2; + // + // const result = await validateDataContract(rawDataContract); + // expectValidationError(result, InvalidIndexPropertyTypeError); + // + // const error = result.getFirstError(); + // + // expect(error.getCode()).to.equal(1013); + // expect(error.getPropertyName()).to.equal('mentions'); + // expect(error.getPropertyType()).to.equal('array'); + // expect(error.getDocumentType()).to.deep.equal('indexedArray'); + // expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + // }); + // + // it('should return invalid result if index property contained prefixItems array of arrays', + // async () => { + // const indexedDocumentDefinition = rawDataContract.documents.indexedArray; + // + // const indexDefinition = indexedDocumentDefinition.indices[0]; + // + // rawDataContract.documents.indexedArray.properties.mentions.prefixItems = [ + // { + // type: 'array', + // items: { + // type: 'string', + // }, + // }, + // ]; + // + // const result = await validateDataContract(rawDataContract); + // expectValidationError(result, InvalidIndexPropertyTypeError); + // + // const error = result.getFirstError(); + // + // expect(error.getCode()).to.equal(1013); + // expect(error.getPropertyName()).to.equal('mentions'); + // expect(error.getPropertyType()).to.equal('array'); + // expect(error.getDocumentType()).to.deep.equal('indexedArray'); + // expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + // }); + + // it('should return invalid result if index property contained prefixItems array of objects', + // async () => { + // const indexedDocumentDefinition = rawDataContract.documents.indexedArray; + // + // const indexDefinition = indexedDocumentDefinition.indices[0]; + // + // rawDataContract.documents.indexedArray.properties.mentions.prefixItems = [ + // { + // type: 'object', + // properties: { + // something: { + // type: 'string', + // }, + // }, + // additionalProperties: false, + // }, + // ]; + // + // const result = await validateDataContract(rawDataContract); + // expectValidationError(result, InvalidIndexPropertyTypeError); + // + // const error = result.getFirstError(); + // + // expect(error.getCode()).to.equal(1013); + // expect(error.getPropertyName()).to.equal('mentions'); + // expect(error.getPropertyType()).to.equal('array'); + // expect(error.getDocumentType()).to.deep.equal('indexedArray'); + // expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + // }); + // + // it('should return invalid result if index property is array of arrays', async () => { + // const indexedDocumentDefinition = rawDataContract.documents.indexedDocument; + // + // indexedDocumentDefinition.properties.arrayProperty = { + // type: 'array', + // items: { + // type: 'array', + // items: { + // type: 'string', + // }, + // }, + // }; + // + // indexedDocumentDefinition.required.push('arrayProperty'); + // + // const indexDefinition = indexedDocumentDefinition.indices[0]; + // + // indexDefinition.properties.push({ + // arrayProperty: 'asc', + // }); + // + // const result = await validateDataContract(rawDataContract); + // + // expectValidationError(result, InvalidIndexPropertyTypeError); + // + // const [error] = result.getErrors(); + // + // expect(error.getCode()).to.equal(1013); + // expect(error.getPropertyName()).to.equal('arrayProperty'); + // expect(error.getPropertyType()).to.equal('array'); + // expect(error.getDocumentType()).to.deep.equal('indexedDocument'); + // expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + // }); + + it('should return invalid result if index property is array with different item definitions', async () => { + const indexedDocumentDefinition = rawDataContract.documents.indexedDocument; + + indexedDocumentDefinition.properties.arrayProperty = { + type: 'array', + prefixItems: [ + { + type: 'string', + }, + { + type: 'number', + }, + ], + minItems: 2, + items: false, + }; + + indexedDocumentDefinition.required.push('arrayProperty'); + + const indexDefinition = indexedDocumentDefinition.indices[0]; + + indexDefinition.properties.push({ + arrayProperty: 'asc', + }); + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidIndexPropertyTypeError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1013); + expect(error.getPropertyName()).to.equal('arrayProperty'); + expect(error.getPropertyType()).to.equal('array'); + expect(error.getDocumentType()).to.deep.equal('indexedDocument'); + expect(error.getIndexDefinition()).to.deep.equal(indexDefinition); + }); + + it('should return invalid result if unique compound index contains both required and optional properties', async () => { + rawDataContract.documents.optionalUniqueIndexedDocument.required.splice(-1); + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidCompoundIndexError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1010); + expect(error.getIndexDefinition()).to.deep.equal( + rawDataContract.documents.optionalUniqueIndexedDocument.indices[1], + ); + expect(error.getDocumentType()).to.equal('optionalUniqueIndexedDocument'); + }); + }); + }); + + describe('signatureSecurityLevelRequirement', () => { + it('should be a number', async () => { + rawDataContract.documents.indexedDocument.signatureSecurityLevelRequirement = 'definitely not a number'; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/signatureSecurityLevelRequirement'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be one of the available values', async () => { + rawDataContract.documents.indexedDocument.signatureSecurityLevelRequirement = 199; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/documents/indexedDocument/signatureSecurityLevelRequirement'); + expect(error.getKeyword()).to.equal('enum'); + }); + }); + + describe('dependentSchemas', () => { + it('should be an object', async () => { + rawDataContract.documents.niceDocument = { + type: 'object', + properties: { + abc: { + type: 'string', + }, + }, + additionalProperties: false, + dependentSchemas: 'string', + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('type'); + expect(error.instancePath).to.equal('/documents/niceDocument/dependentSchemas'); + expect(error.message).to.equal('must be object'); + }); + }); + + describe('dependentRequired', () => { + it('should be an object', async () => { + rawDataContract.documents.niceDocument = { + type: 'object', + properties: { + abc: { + type: 'string', + }, + }, + additionalProperties: false, + dependentRequired: 'string', + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('type'); + expect(error.instancePath).to.equal('/documents/niceDocument/dependentRequired'); + expect(error.message).to.equal('must be object'); + }); + + it('should have an array value', async () => { + rawDataContract.documents.niceDocument = { + type: 'object', + properties: { + abc: { + type: 'string', + }, + }, + additionalProperties: false, + dependentRequired: { + zxy: { + type: 'number', + }, + }, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('type'); + expect(error.instancePath).to.equal('/documents/niceDocument/dependentRequired/zxy'); + expect(error.message).to.equal('must be array'); + }); + + it('should have an array of strings', async () => { + rawDataContract.documents.niceDocument = { + type: 'object', + properties: { + abc: { + type: 'string', + }, + }, + additionalProperties: false, + dependentRequired: { + zxy: [1, '2'], + }, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('type'); + expect(error.instancePath).to.equal('/documents/niceDocument/dependentRequired/zxy/0'); + expect(error.message).to.equal('must be string'); + }); + + it('should have an array of unique strings', async () => { + rawDataContract.documents.niceDocument = { + type: 'object', + properties: { + abc: { + type: 'string', + }, + }, + additionalProperties: false, + dependentRequired: { + zxy: ['1', '2', '2'], + }, + }; + + const result = await validateDataContract(rawDataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('uniqueItems'); + expect(error.instancePath).to.equal('/documents/niceDocument/dependentRequired/zxy'); + expect(error.message).to.equal('must NOT have duplicate items (items ## 2 and 1 are identical)'); + }); + }); + + it.skip('should return invalid result with circular $ref pointer', async () => { + rawDataContract.$defs.object = { $ref: '#/$defs/object' }; + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidJsonSchemaRefError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1014); + + expect(error.message).to.startsWith('Invalid JSON Schema $ref: Circular $ref pointer found'); + }); + + it('should return invalid result if indexed string property missing maxLength constraint', async () => { + delete rawDataContract.documents.indexedDocument.properties.firstName.maxLength; + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidIndexedPropertyConstraintError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1012); + expect(error.getPropertyName()).to.equal('firstName'); + expect(error.getConstraintName()).to.equal('maxLength'); + expect(error.getReason()).to.equal('should be less or equal 63'); + }); + + it('should return invalid result if indexed string property have to big maxLength', async () => { + rawDataContract.documents.indexedDocument.properties.firstName.maxLength = 2048; + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidIndexedPropertyConstraintError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1012); + expect(error.getPropertyName()).to.equal('firstName'); + expect(error.getConstraintName()).to.equal('maxLength'); + expect(error.getReason()).to.equal('should be less or equal 63'); + }); + + // it('should return invalid result if indexed array property missing maxItems constraint', + // async () => { + // delete rawDataContract.documents.indexedArray.properties.mentions.maxItems; + // + // const result = await validateDataContract(rawDataContract); + // + // expectValidationError(result, InvalidIndexedPropertyConstraintError); + // + // const [error] = result.getErrors(); + // + // expect(error.getCode()).to.equal(1012); + // expect(error.getPropertyName()).to.equal('mentions'); + // expect(error.getConstraintName()).to.equal('maxItems'); + // expect(error.getReason()).to.equal('should be less or equal 63'); + // }); + // + // it('should return invalid result if indexed array property have to big maxItems', async () => { + // rawDataContract.documents.indexedArray.properties.mentions.maxItems = 2048; + // + // const result = await validateDataContract(rawDataContract); + // + // expectValidationError(result, InvalidIndexedPropertyConstraintError); + // + // const [error] = result.getErrors(); + // + // expect(error.getCode()).to.equal(1012); + // expect(error.getPropertyName()).to.equal('mentions'); + // expect(error.getConstraintName()).to.equal('maxItems'); + // expect(error.getReason()).to.equal('should be less or equal 63'); + // }); + // + // it('should return invalid result if indexed array property + // have string item without maxItems constraint', async () => { + // delete rawDataContract.documents.indexedArray.properties.mentions.maxItems; + // + // const result = await validateDataContract(rawDataContract); + // + // expectValidationError(result, InvalidIndexedPropertyConstraintError); + // + // const [error] = result.getErrors(); + // + // expect(error.getCode()).to.equal(1012); + // expect(error.getPropertyName()).to.equal('mentions'); + // expect(error.getConstraintName()).to.equal('maxItems'); + // expect(error.getReason()).to.equal('should be less or equal 63'); + // }); + // + // it('should return invalid result if indexed array property have + // string item with maxItems bigger than 1024', async () => { + // rawDataContract.documents.indexedArray.properties.mentions.maxItems = 2048; + // + // const result = await validateDataContract(rawDataContract); + // + // expectValidationError(result, InvalidIndexedPropertyConstraintError); + // + // const [error] = result.getErrors(); + // + // expect(error.getCode()).to.equal(1012); + // expect(error.getPropertyName()).to.equal('mentions'); + // expect(error.getConstraintName()).to.equal('maxItems'); + // expect(error.getReason()).to.equal('should be less or equal 63'); + // }); + + it('should return invalid result if indexed byte array property missing maxItems constraint', async () => { + delete rawDataContract.documents.withByteArrays.properties.byteArrayField.maxItems; + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidIndexedPropertyConstraintError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1012); + expect(error.getPropertyName()).to.equal('byteArrayField'); + expect(error.getConstraintName()).to.equal('maxItems'); + expect(error.getReason()).to.equal('should be less or equal 255'); + }); + + it('should return invalid result if indexed byte array property have to big maxItems', async () => { + rawDataContract.documents.withByteArrays.properties.byteArrayField.maxItems = 8192; + + const result = await validateDataContract(rawDataContract); + + expectValidationError(result, InvalidIndexedPropertyConstraintError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1012); + expect(error.getPropertyName()).to.equal('byteArrayField'); + expect(error.getConstraintName()).to.equal('maxItems'); + expect(error.getReason()).to.equal('should be less or equal 255'); + }); + + it('should return valid result if Data Contract is valid', async () => { + const result = await validateDataContract(rawDataContract); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/integration/document/Document.spec.js b/packages/js-dpp/test/integration/document/Document.spec.js new file mode 100644 index 00000000000..3ce0a421c06 --- /dev/null +++ b/packages/js-dpp/test/integration/document/Document.spec.js @@ -0,0 +1,90 @@ +const Identifier = require('../../../lib/identifier/Identifier'); +const Metadata = require('../../../lib/Metadata'); + +const getDataContractFixture = require('../../../lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('../../../lib/test/fixtures/getDocumentsFixture'); + +describe('Document', () => { + let document; + let dataContract; + let metadataFixture; + + beforeEach(() => { + dataContract = getDataContractFixture(); + [document] = getDocumentsFixture(dataContract).slice(8); + + metadataFixture = new Metadata(42, 0); + + document.setMetadata(metadataFixture); + }); + + describe('#toJSON', () => { + it('should return json document', () => { + const result = document.toJSON(); + + expect(result).to.deep.equal({ + $protocolVersion: document.getProtocolVersion(), + $dataContractId: dataContract.getId().toString(), + $id: document.getId().toString(), + $ownerId: getDocumentsFixture.ownerId.toString(), + $revision: 1, + $type: 'withByteArrays', + byteArrayField: document.get('byteArrayField').toString('base64'), + identifierField: document.get('identifierField').toString(), + }); + }); + }); + + describe('#toObject', () => { + it('should return raw document', () => { + const result = document.toObject(); + + expect(result).to.deep.equal({ + $protocolVersion: document.getProtocolVersion(), + $dataContractId: dataContract.getId(), + $id: document.getId(), + $ownerId: getDocumentsFixture.ownerId, + $revision: 1, + $type: 'withByteArrays', + byteArrayField: document.get('byteArrayField'), + identifierField: document.get('identifierField'), + }); + }); + + it('should return raw document with Identifiers', () => { + const result = document.toObject({ skipIdentifiersConversion: true }); + + expect(result).to.deep.equal({ + $protocolVersion: document.getProtocolVersion(), + $dataContractId: dataContract.getId(), + $id: document.getId(), + $ownerId: getDocumentsFixture.ownerId, + $revision: 1, + $type: 'withByteArrays', + byteArrayField: document.get('byteArrayField'), + identifierField: document.get('identifierField'), + }); + + expect(result.$dataContractId).to.be.an.instanceOf(Identifier); + expect(result.$id).to.be.an.instanceOf(Identifier); + expect(result.$ownerId).to.be.an.instanceOf(Identifier); + expect(result.identifierField).to.be.an.instanceOf(Identifier); + }); + }); + + describe('#setMetadata', () => { + it('should set metadata', () => { + const otherMetadata = new Metadata(43, 1); + + document.setMetadata(otherMetadata); + + expect(document.metadata).to.deep.equal(otherMetadata); + }); + }); + + describe('#getMetadata', () => { + it('should get metadata', () => { + expect(document.getMetadata()).to.deep.equal(metadataFixture); + }); + }); +}); diff --git a/packages/js-dpp/test/integration/document/DocumentFacade.spec.js b/packages/js-dpp/test/integration/document/DocumentFacade.spec.js new file mode 100644 index 00000000000..fecf9b25a44 --- /dev/null +++ b/packages/js-dpp/test/integration/document/DocumentFacade.spec.js @@ -0,0 +1,155 @@ +const generateRandomIdentifier = require('../../../lib/test/utils/generateRandomIdentifier'); +const DashPlatformProtocol = require('../../../lib/DashPlatformProtocol'); + +const Document = require('../../../lib/document/Document'); +const DocumentsBatchTransition = require('../../../lib/document/stateTransition/DocumentsBatchTransition/DocumentsBatchTransition'); + +const ValidationResult = require('../../../lib/validation/ValidationResult'); + +const createStateRepositoryMock = require('../../../lib/test/mocks/createStateRepositoryMock'); + +const getDataContractFixture = require('../../../lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('../../../lib/test/fixtures/getDocumentsFixture'); +const getDocumentTransitionsFixture = require('../../../lib/test/fixtures/getDocumentTransitionsFixture'); + +const DataContractNotPresentError = require('../../../lib/errors/consensus/basic/document/DataContractNotPresentError'); +const MissingOptionError = require('../../../lib/errors/MissingOptionError'); + +describe('DocumentFacade', () => { + let dpp; + let document; + let documents; + let dataContract; + let ownerId; + let stateRepositoryMock; + + beforeEach(async function beforeEach() { + ownerId = generateRandomIdentifier(); + dataContract = getDataContractFixture(ownerId); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + + stateRepositoryMock.fetchDataContract.resolves(dataContract); + + dpp = new DashPlatformProtocol({ + stateRepository: stateRepositoryMock, + }); + await dpp.initialize(); + + documents = getDocumentsFixture(dataContract); + ([document] = documents); + }); + + describe('create', () => { + it('should create Document', () => { + const result = dpp.document.create( + dataContract, + ownerId, + document.getType(), + document.getData(), + ); + + expect(result).to.be.an.instanceOf(Document); + + expect(result.getType()).to.equal(document.getType()); + expect(result.getData()).to.deep.equal(document.getData()); + }); + }); + + describe('createFromObject', () => { + it('should throw MissingOption if stateRepository is not set', async () => { + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + try { + await dpp.document.createFromObject(document.toObject()); + + expect.fail('MissingOption should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(MissingOptionError); + expect(e.getOptionName()).to.equal('stateRepository'); + } + }); + + it('should create Document from plain object', async () => { + const result = await dpp.document.createFromObject(document.toObject()); + + expect(result).to.be.an.instanceOf(Document); + + expect(result.toObject()).to.deep.equal(document.toObject()); + }); + }); + + describe('createFromBuffer', () => { + it('should throw MissingOption if stateRepository is not set', async () => { + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + try { + await dpp.document.createFromBuffer(document.toObject()); + + expect.fail('MissingOption should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(MissingOptionError); + expect(e.getOptionName()).to.equal('stateRepository'); + } + }); + + it('should create Document from serialized', async () => { + const result = await dpp.document.createFromBuffer(document.toBuffer()); + + expect(result).to.be.an.instanceOf(Document); + + expect(result.toObject()).to.deep.equal(document.toObject()); + }); + }); + + describe('createStateTransition', () => { + it('should create DocumentsBatchTransition with passed documents', () => { + const result = dpp.document.createStateTransition({ + create: documents, + }); + + expect(result).to.be.instanceOf(DocumentsBatchTransition); + expect(result.getTransitions()).to.deep.equal(getDocumentTransitionsFixture({ + create: documents, + })); + }); + }); + + describe('validate', () => { + it('should throw MissingOption if stateRepository is not set', async () => { + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + try { + await dpp.document.validate(document); + + expect.fail('MissingOption should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(MissingOptionError); + expect(e.getOptionName()).to.equal('stateRepository'); + } + }); + + it('should validate Document', async () => { + const result = await dpp.document.validate(document); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + + it('should return invalid result if Data Contract is invalid', async () => { + stateRepositoryMock.fetchDataContract.returns(null); + + const result = await dpp.document.validate(document); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataContractNotPresentError); + }); + }); +}); diff --git a/packages/js-dpp/test/integration/document/stateTransition/DocumentsBatchTransition/DocumentBatchTransition.spec.js b/packages/js-dpp/test/integration/document/stateTransition/DocumentsBatchTransition/DocumentBatchTransition.spec.js new file mode 100644 index 00000000000..c3d14fce393 --- /dev/null +++ b/packages/js-dpp/test/integration/document/stateTransition/DocumentsBatchTransition/DocumentBatchTransition.spec.js @@ -0,0 +1,83 @@ +const IdentityPublicKey = require('../../../../../lib/identity/IdentityPublicKey'); + +const getDataContractFixture = require('../../../../../lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('../../../../../lib/test/fixtures/getDocumentsFixture'); +const DocumentFactory = require('../../../../../lib/document/DocumentFactory'); +const createDPPMock = require('../../../../../lib/test/mocks/createDPPMock'); + +describe('DocumentBatchTransition', () => { + let stateTransitionFixture; + let dataContractFixture; + let documentsFixture; + let documentFactory; + let mediumSecurityDocumentFixture; + let masterSecurityDocumentFixture; + let noSecurityLevelSpecifiedDocumentFixture; + + beforeEach(() => { + dataContractFixture = getDataContractFixture(); + + dataContractFixture.documents.niceDocument + .signatureSecurityLevelRequirement = IdentityPublicKey.SECURITY_LEVELS.MEDIUM; + dataContractFixture.documents.prettyDocument + .signatureSecurityLevelRequirement = IdentityPublicKey.SECURITY_LEVELS.MASTER; + + // 0 is niceDocument, + // 1 and 2 are pretty documents, + // 3 and 4 are indexed documents that do not have security level specified + documentsFixture = getDocumentsFixture(dataContractFixture); + [ + mediumSecurityDocumentFixture,, + masterSecurityDocumentFixture,, + noSecurityLevelSpecifiedDocumentFixture, + ] = documentsFixture; + + documentFactory = new DocumentFactory( + createDPPMock(), + () => {}, + () => {}, + ); + + stateTransitionFixture = documentFactory.createStateTransition({ + create: documentsFixture, + replace: [], + delete: [], + }); + }); + + describe('#getRequiredKeySecurityLevel', () => { + it('should return the highest security level of all transitions', () => { + stateTransitionFixture = documentFactory.createStateTransition({ + create: [mediumSecurityDocumentFixture], + replace: [], + delete: [], + }); + + // Nice document has medium security level + expect(stateTransitionFixture.getKeySecurityLevelRequirement()) + .to.be.equal(IdentityPublicKey.SECURITY_LEVELS.MEDIUM); + + stateTransitionFixture = documentFactory.createStateTransition({ + create: [mediumSecurityDocumentFixture, masterSecurityDocumentFixture], + replace: [], + delete: [], + }); + + // Should be the highest security level out of MEDIUM and MASTER + expect(stateTransitionFixture.getKeySecurityLevelRequirement()) + .to.be.equal(IdentityPublicKey.SECURITY_LEVELS.MASTER); + }); + + it('should return default security level if no document has a security level defined', () => { + stateTransitionFixture = documentFactory.createStateTransition({ + create: [noSecurityLevelSpecifiedDocumentFixture], + replace: [], + delete: [], + }); + + // Should be the default level, which is HIGH + expect(stateTransitionFixture.getKeySecurityLevelRequirement()) + .to.be.equal(IdentityPublicKey.SECURITY_LEVELS.HIGH); + }); + }); +}); diff --git a/packages/js-dpp/test/integration/document/stateTransition/DocumentsBatchTransition/validation/basic/validateDocumentsBatchTransitionBasicFactory.spec.js b/packages/js-dpp/test/integration/document/stateTransition/DocumentsBatchTransition/validation/basic/validateDocumentsBatchTransitionBasicFactory.spec.js new file mode 100644 index 00000000000..ce3242802b9 --- /dev/null +++ b/packages/js-dpp/test/integration/document/stateTransition/DocumentsBatchTransition/validation/basic/validateDocumentsBatchTransitionBasicFactory.spec.js @@ -0,0 +1,1146 @@ +const { getRE2Class } = require('@dashevo/wasm-re2'); + +const createAjv = require('../../../../../../../lib/ajv/createAjv'); + +const protocolVersion = require('../../../../../../../lib/version/protocolVersion'); + +const JsonSchemaValidator = require('../../../../../../../lib/validation/JsonSchemaValidator'); + +const generateRandomIdentifier = require('../../../../../../../lib/test/utils/generateRandomIdentifier'); + +const enrichDataContractWithBaseSchema = require('../../../../../../../lib/dataContract/enrichDataContractWithBaseSchema'); + +const DocumentsBatchTransition = require('../../../../../../../lib/document/stateTransition/DocumentsBatchTransition/DocumentsBatchTransition'); + +const getDocumentTransitionsFixture = require('../../../../../../../lib/test/fixtures/getDocumentTransitionsFixture'); +const getDocumentsFixture = require('../../../../../../../lib/test/fixtures/getDocumentsFixture'); +const getDataContractFixture = require('../../../../../../../lib/test/fixtures/getDataContractFixture'); + +const ValidationResult = require('../../../../../../../lib/validation/ValidationResult'); + +const validateDocumentsBatchTransitionBasicFactory = require('../../../../../../../lib/document/stateTransition/DocumentsBatchTransition/validation/basic/validateDocumentsBatchTransitionBasicFactory'); + +const { expectValidationError, expectJsonSchemaError } = require('../../../../../../../lib/test/expect/expectError'); + +const createStateRepositoryMock = require('../../../../../../../lib/test/mocks/createStateRepositoryMock'); + +const InvalidDocumentTransitionIdError = require('../../../../../../../lib/errors/consensus/basic/document/InvalidDocumentTransitionIdError'); +const DataContractNotPresentError = require('../../../../../../../lib/errors/consensus/basic/document/DataContractNotPresentError'); +const MissingDataContractIdError = require('../../../../../../../lib/errors/consensus/basic/document/MissingDataContractIdError'); +const MissingDocumentTransitionTypeError = require('../../../../../../../lib/errors/consensus/basic/document/MissingDocumentTransitionTypeError'); +const InvalidDocumentTypeError = require('../../../../../../../lib/errors/consensus/basic/document/InvalidDocumentTypeError'); +const MissingDocumentTransitionActionError = require('../../../../../../../lib/errors/consensus/basic/document/MissingDocumentTransitionActionError'); +const InvalidDocumentTransitionActionError = require('../../../../../../../lib/errors/consensus/basic/document/InvalidDocumentTransitionActionError'); +const InvalidIdentifierError = require('../../../../../../../lib/errors/consensus/basic/InvalidIdentifierError'); +const DuplicateDocumentTransitionsWithIndicesError = require('../../../../../../../lib/errors/consensus/basic/document/DuplicateDocumentTransitionsWithIndicesError'); +const DuplicateDocumentTransitionsWithIdsError = require('../../../../../../../lib/errors/consensus/basic/document/DuplicateDocumentTransitionsWithIdsError'); +const SomeConsensusError = require('../../../../../../../lib/test/mocks/SomeConsensusError'); +const StateTransitionExecutionContext = require('../../../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('validateDocumentsBatchTransitionBasicFactory', () => { + let dataContract; + let documents; + let rawStateTransition; + let findDuplicatesByIdMock; + let findDuplicatesByIndicesMock; + let validateDocumentsBatchTransitionBasic; + let stateTransition; + let ownerId; + let stateRepositoryMock; + let validator; + let enrichSpy; + let documentTransitions; + let validatePartialCompoundIndicesMock; + let validateProtocolVersionMock; + let executionContext; + + beforeEach(async function beforeEach() { + dataContract = getDataContractFixture(); + documents = getDocumentsFixture(dataContract); + + ownerId = getDocumentsFixture.ownerId; + + documentTransitions = getDocumentTransitionsFixture({ + create: documents, + }); + + executionContext = new StateTransitionExecutionContext(); + + stateTransition = new DocumentsBatchTransition({ + protocolVersion: protocolVersion.latestVersion, + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + signature: Buffer.alloc(65), + signaturePublicKeyId: 0, + }, [dataContract]); + + rawStateTransition = stateTransition.toObject(); + + findDuplicatesByIdMock = this.sinonSandbox.stub().returns([]); + findDuplicatesByIndicesMock = this.sinonSandbox.stub().returns([]); + + const dataContractValidationResult = new ValidationResult(); + dataContractValidationResult.setData(dataContract); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchDataContract.resolves(dataContract); + + const RE2 = await getRE2Class(); + const ajv = createAjv(RE2); + + validator = new JsonSchemaValidator(ajv); + + enrichSpy = this.sinonSandbox.spy(enrichDataContractWithBaseSchema); + + validatePartialCompoundIndicesMock = this.sinonSandbox.stub().returns( + new ValidationResult(), + ); + + validateProtocolVersionMock = this.sinonSandbox.stub().returns(new ValidationResult()); + + validateDocumentsBatchTransitionBasic = validateDocumentsBatchTransitionBasicFactory( + findDuplicatesByIdMock, + findDuplicatesByIndicesMock, + stateRepositoryMock, + validator, + enrichSpy, + validatePartialCompoundIndicesMock, + validateProtocolVersionMock, + ); + }); + + describe('protocolVersion', () => { + it('should be present', async () => { + delete rawStateTransition.protocolVersion; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('protocolVersion'); + }); + + it('should be an integer', async () => { + rawStateTransition.protocolVersion = '1'; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/protocolVersion'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be valid', async () => { + rawStateTransition.protocolVersion = -1; + + const protocolVersionError = new SomeConsensusError('test'); + const protocolVersionResult = new ValidationResult([ + protocolVersionError, + ]); + + validateProtocolVersionMock.returns(protocolVersionResult); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result, SomeConsensusError); + + const [error] = result.getErrors(); + + expect(error).to.equal(protocolVersionError); + + expect(validateProtocolVersionMock).to.be.calledOnceWith( + rawStateTransition.protocolVersion, + ); + }); + }); + + describe('type', () => { + it('should be present', async () => { + delete rawStateTransition.type; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('type'); + }); + + it('should be equal 1', async () => { + rawStateTransition.type = 666; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/type'); + expect(error.getKeyword()).to.equal('const'); + expect(error.getParams().allowedValue).to.equal(1); + }); + }); + + describe('ownerId', () => { + it('should be present', async () => { + delete rawStateTransition.ownerId; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('ownerId'); + }); + + it('should be a byte array', async () => { + rawStateTransition.ownerId = new Array(32).fill('string'); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/ownerId/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be no less than 32 bytes', async () => { + rawStateTransition.ownerId = Buffer.alloc(31); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/ownerId'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be no longer than 32 bytes', async () => { + rawStateTransition.ownerId = Buffer.alloc(33); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/ownerId'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + + describe('document transitions', () => { + it('should be present', async () => { + delete rawStateTransition.transitions; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('transitions'); + }); + + it('should be an array', async () => { + rawStateTransition.transitions = {}; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/transitions'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should have at least one element', async () => { + rawStateTransition.transitions = []; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/transitions'); + expect(error.getKeyword()).to.equal('minItems'); + expect(error.getParams().limit).to.equal(1); + }); + + it('should have no more than 10 elements', async () => { + rawStateTransition.transitions = Array(11).fill({}); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/transitions'); + expect(error.getKeyword()).to.equal('maxItems'); + expect(error.getParams().limit).to.equal(10); + }); + + it('should have objects as elements', async () => { + rawStateTransition.transitions = [1]; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/transitions/0'); + expect(error.getKeyword()).to.equal('type'); + }); + + describe('document transition', () => { + describe('$id', () => { + it('should be present', async () => { + const [documentTransition] = rawStateTransition.transitions; + + delete documentTransition.$id; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('$id'); + }); + + it('should be a byte array', async () => { + const [documentTransition] = rawStateTransition.transitions; + + documentTransition.$id = new Array(32).fill('string'); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.instancePath).to.equal('/$id/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be no less than 32 bytes', async () => { + const [documentTransition] = rawStateTransition.transitions; + + documentTransition.$id = Buffer.alloc(31); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/$id'); + expect(error.getKeyword()).to.equal('minItems'); + expect(error.getParams().limit).to.equal(32); + }); + + it('should be no longer than 32 bytes', async () => { + const [documentTransition] = rawStateTransition.transitions; + + documentTransition.$id = Buffer.alloc(33); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/$id'); + expect(error.getKeyword()).to.equal('maxItems'); + expect(error.getParams().limit).to.equal(32); + }); + + it('should no have duplicate IDs in the state transition', async () => { + const duplicates = [documentTransitions[0].toObject()]; + + findDuplicatesByIdMock.returns(duplicates); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result, DuplicateDocumentTransitionsWithIdsError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1019); + expect(error.getDocumentTransitionReferences()).to.deep.equal( + duplicates.map((d) => [d.$type, d.$id]), + ); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + expect(enrichSpy).to.have.been.calledThrice(); + expect(findDuplicatesByIdMock).to.have.been.calledOnceWithExactly( + rawStateTransition.transitions, + ); + expect(findDuplicatesByIndicesMock).to.have.been.calledOnceWithExactly( + rawStateTransition.transitions, dataContract, + ); + }); + }); + + describe('$dataContractId', () => { + it('should be present', async () => { + const [firstDocumentTransition] = rawStateTransition.transitions; + + delete firstDocumentTransition.$dataContractId; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result, MissingDataContractIdError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1025); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + + expect(enrichSpy).to.have.been.calledThrice(); + + expect(findDuplicatesByIdMock).to.have.been.calledOnceWithExactly( + rawStateTransition.transitions.slice(1), + ); + expect(findDuplicatesByIndicesMock).to.have.been.calledOnceWithExactly( + rawStateTransition.transitions.slice(1), dataContract, + ); + }); + + it('should be a byte array', async () => { + const [firstDocumentTransition] = rawStateTransition.transitions; + + firstDocumentTransition.$dataContractId = 'something'; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result, InvalidIdentifierError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1006); + + expect(error.getIdentifierName()).to.equal('$dataContractId'); + + expect(error.getIdentifierError()).to.be.instanceOf(Error); + expect(error.getIdentifierError().message).to.equal('Identifier expects Buffer'); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + + expect(enrichSpy).to.have.been.calledThrice(); + + expect(findDuplicatesByIdMock).to.have.been.calledOnceWithExactly( + rawStateTransition.transitions.slice(1), + ); + expect(findDuplicatesByIndicesMock).to.have.been.calledOnceWithExactly( + rawStateTransition.transitions.slice(1), dataContract, + ); + }); + + it('should exists in the state', async () => { + stateRepositoryMock.fetchDataContract.resolves(undefined); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result, DataContractNotPresentError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1018); + expect(error.getDataContractId()).to.deep.equal(dataContract.getId()); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + + expect(enrichSpy).to.have.not.been.called(); + expect(findDuplicatesByIdMock).to.have.not.been.called(); + expect(findDuplicatesByIndicesMock).to.have.not.been.called(); + }); + }); + + describe('$type', () => { + it('should be present', async () => { + const [firstDocumentTransition] = rawStateTransition.transitions; + + delete firstDocumentTransition.$type; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result, MissingDocumentTransitionTypeError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1027); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + + expect(enrichSpy).to.have.been.calledThrice(); + expect(findDuplicatesByIdMock).to.have.not.been.called(); + expect(findDuplicatesByIndicesMock).to.have.not.been.called(); + }); + + it('should be defined in Data Contract', async () => { + const [firstDocumentTransition] = rawStateTransition.transitions; + + firstDocumentTransition.$type = 'wrong'; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result, InvalidDocumentTypeError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1024); + expect(error.getType()).to.equal(firstDocumentTransition.$type); + + expect(Buffer.isBuffer(error.getDataContractId())).to.be.true(); + expect(error.getDataContractId()).to.deep.equal(dataContract.getId().toBuffer()); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + + expect(enrichSpy).to.have.been.calledThrice(); + expect(findDuplicatesByIdMock).to.have.not.been.called(); + expect(findDuplicatesByIndicesMock).to.have.not.been.called(); + }); + }); + + describe('$action', () => { + it('should be present', async () => { + const [firstDocumentTransition] = rawStateTransition.transitions; + + delete firstDocumentTransition.$action; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result, MissingDocumentTransitionActionError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1026); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + + expect(enrichSpy).to.have.been.calledThrice(); + expect(findDuplicatesByIdMock).to.have.not.been.called(); + expect(findDuplicatesByIndicesMock).to.have.not.been.called(); + }); + + it('should throw InvalidDocumentTransitionActionError if action is not valid', async () => { + const [firstDocumentTransition] = rawStateTransition.transitions; + + firstDocumentTransition.$action = 4; + + try { + await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + } catch (e) { + expect(e).to.be.instanceOf(InvalidDocumentTransitionActionError); + expect(e.getAction()).to.equal(firstDocumentTransition.$action); + expect(e.getRawDocumentTransition()).to.deep.equal(firstDocumentTransition); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + ); + + expect(enrichSpy).to.have.been.calledThrice(); + expect(findDuplicatesByIdMock).to.have.not.been.called(); + expect(findDuplicatesByIndicesMock).to.have.not.been.called(); + } + }); + }); + + describe('create', () => { + describe('$id', () => { + it('should be valid generated ID', async () => { + const [firstTransition] = rawStateTransition.transitions; + + const expectedId = firstTransition.$id; + firstTransition.$id = generateRandomIdentifier(); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result, InvalidDocumentTransitionIdError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1023); + + expect(Buffer.isBuffer(error.getExpectedId())).to.be.true(); + expect(error.getExpectedId()).to.deep.equal(expectedId); + + expect(Buffer.isBuffer(error.getInvalidId())).to.be.true(); + expect(error.getInvalidId()).to.deep.equal(firstTransition.$id); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + + expect(enrichSpy).to.have.been.calledThrice(); + + expect(findDuplicatesByIdMock).to.have.not.been.called(); + expect(findDuplicatesByIndicesMock).to.have.not.been.called(); + }); + }); + + describe('$entropy', () => { + it('should be present', async () => { + const [documentTransition] = rawStateTransition.transitions; + + delete documentTransition.$entropy; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('$entropy'); + }); + + it('should be a byte array', async () => { + const [documentTransition] = rawStateTransition.transitions; + + documentTransition.$entropy = new Array(32).fill('string'); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.instancePath).to.equal('/$entropy/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be no less than 32 bytes', async () => { + const [documentTransition] = rawStateTransition.transitions; + + documentTransition.$entropy = Buffer.alloc(31); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/$entropy'); + expect(error.getKeyword()).to.equal('minItems'); + expect(error.getParams().limit).to.equal(32); + }); + + it('should be no longer than 32 bytes', async () => { + const [documentTransition] = rawStateTransition.transitions; + + documentTransition.$entropy = Buffer.alloc(33); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/$entropy'); + expect(error.getKeyword()).to.equal('maxItems'); + expect(error.getParams().limit).to.equal(32); + }); + }); + }); + + describe('replace', () => { + beforeEach(() => { + documentTransitions = getDocumentTransitionsFixture({ + create: [], + replace: documents, + }); + + stateTransition = new DocumentsBatchTransition({ + protocolVersion: protocolVersion.latestVersion, + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + signature: Buffer.alloc(65), + signaturePublicKeyId: 0, + }, [dataContract]); + + rawStateTransition = stateTransition.toObject(); + }); + + describe('$revision', () => { + it('should be present', async () => { + const [documentTransition] = rawStateTransition.transitions; + + delete documentTransition.$revision; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getParams().missingProperty).to.equal('$revision'); + expect(error.getKeyword()).to.equal('required'); + }); + + it('should be a number', async () => { + const [documentTransition] = rawStateTransition.transitions; + + documentTransition.$revision = '1'; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/$revision'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be multiple of 1.0', async () => { + const [documentTransition] = rawStateTransition.transitions; + + documentTransition.$revision = 1.2; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/$revision'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should have a minimum value of 1', async () => { + const [documentTransition] = rawStateTransition.transitions; + + documentTransition.$revision = 0; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/$revision'); + expect(error.getKeyword()).to.equal('minimum'); + }); + }); + }); + + describe('delete', () => { + beforeEach(() => { + documentTransitions = getDocumentTransitionsFixture({ + create: [], + replace: [], + delete: documents, + }); + + stateTransition = new DocumentsBatchTransition({ + protocolVersion: protocolVersion.latestVersion, + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + signature: Buffer.alloc(65), + signaturePublicKeyId: 0, + }, [dataContract]); + + rawStateTransition = stateTransition.toObject(); + }); + + it('should return invalid result if delete transaction is not valid', async () => { + const [documentTransition] = rawStateTransition.transitions; + + delete documentTransition.$id; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getParams().missingProperty).to.equal('$id'); + expect(error.getKeyword()).to.equal('required'); + }); + }); + + it('should return invalid result if there are duplicate unique index values', async () => { + const duplicates = [documentTransitions[1].toObject()]; + + findDuplicatesByIndicesMock.returns(duplicates); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result, DuplicateDocumentTransitionsWithIndicesError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1020); + + expect(error.getDocumentTransitionReferences()).to.deep.equal( + duplicates.map((d) => [d.$type, d.$id]), + ); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + expect(enrichSpy).to.have.been.calledThrice(); + expect(findDuplicatesByIdMock).to.have.been.calledOnceWithExactly( + rawStateTransition.transitions, + ); + expect(findDuplicatesByIndicesMock).to.have.been.calledOnceWithExactly( + rawStateTransition.transitions, dataContract, + ); + }); + + it('should return invalid result if compound index doesn\'t contain all fields', async () => { + const consensusError = new SomeConsensusError('error'); + + validatePartialCompoundIndicesMock.returns( + new ValidationResult([consensusError]), + ); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.equal(consensusError); + + expect(validatePartialCompoundIndicesMock).to.be.calledOnceWithExactly( + ownerId.toBuffer(), + rawStateTransition.transitions, + dataContract, + ); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + expect(enrichSpy).to.have.been.calledThrice(); + }); + }); + }); + + describe('signature', () => { + it('should be present', async () => { + delete rawStateTransition.signature; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('signature'); + }); + + it('should be a byte array', async () => { + rawStateTransition.signature = new Array(65).fill('string'); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be not less than 65 bytes', async () => { + rawStateTransition.signature = Buffer.alloc(64); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature'); + expect(error.getKeyword()).to.equal('minItems'); + expect(error.getParams().limit).to.equal(65); + }); + + it('should be not longer than 96 bytes', async () => { + rawStateTransition.signature = Buffer.alloc(97); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature'); + expect(error.getKeyword()).to.equal('maxItems'); + expect(error.getParams().limit).to.equal(96); + }); + }); + + describe('signaturePublicKeyId', () => { + it('should be an integer', async () => { + rawStateTransition.signaturePublicKeyId = 1.4; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result, 1); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signaturePublicKeyId'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should not be < 0', async () => { + rawStateTransition.signaturePublicKeyId = -1; + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result, 1); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signaturePublicKeyId'); + expect(error.getKeyword()).to.equal('minimum'); + }); + }); + + it('should return valid result', async () => { + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + + expect(enrichSpy).to.have.been.calledThrice(); + + expect(findDuplicatesByIdMock).to.have.been.calledOnceWithExactly( + rawStateTransition.transitions, + ); + + expect(findDuplicatesByIndicesMock).to.have.been.calledOnceWithExactly( + rawStateTransition.transitions, dataContract, + ); + }); + + it('should not validate Document transitions on dry run', async () => { + stateRepositoryMock.fetchDataContract.resolves(null); + + executionContext.enableDryRun(); + + const result = await validateDocumentsBatchTransitionBasic( + rawStateTransition, + executionContext, + ); + + executionContext.disableDryRun(); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + }); +}); diff --git a/packages/js-dpp/test/integration/document/stateTransition/DocumentsBatchTransition/validation/state/executeDataTriggersFactory.spec.js b/packages/js-dpp/test/integration/document/stateTransition/DocumentsBatchTransition/validation/state/executeDataTriggersFactory.spec.js new file mode 100644 index 00000000000..5648670b908 --- /dev/null +++ b/packages/js-dpp/test/integration/document/stateTransition/DocumentsBatchTransition/validation/state/executeDataTriggersFactory.spec.js @@ -0,0 +1,294 @@ +const bs58 = require('bs58'); +const AbstractDocumentTransition = require('../../../../../../../lib/document/stateTransition/DocumentsBatchTransition/documentTransition/AbstractDocumentTransition'); + +const generateRandomIdentifier = require('../../../../../../../lib/test/utils/generateRandomIdentifier'); + +const DataTrigger = require('../../../../../../../lib/dataTrigger/DataTrigger'); +const DataTriggerExecutionResult = require('../../../../../../../lib/dataTrigger/DataTriggerExecutionResult'); +const DataTriggerExecutionContext = require('../../../../../../../lib/dataTrigger/DataTriggerExecutionContext'); +const getDpnsContractFixture = require('../../../../../../../lib/test/fixtures/getDpnsContractFixture'); +const dpnsDocumentFixture = require('../../../../../../../lib/test/fixtures/getDpnsDocumentFixture'); +const getDocumentsFixture = require('../../../../../../../lib/test/fixtures/getDocumentsFixture'); +const getDocumentTransitionsFixture = require('../../../../../../../lib/test/fixtures/getDocumentTransitionsFixture'); +const getDataContractFixture = require('../../../../../../../lib/test/fixtures/getDataContractFixture'); + +const dpnsCreateDomainDataTrigger = require('../../../../../../../lib/dataTrigger/dpnsTriggers/createDomainDataTrigger'); +const dpnsDeleteDomainDataTrigger = require('../../../../../../../lib/dataTrigger/dpnsTriggers/createDomainDataTrigger'); +const dpnsUpdateDomainDataTrigger = require('../../../../../../../lib/dataTrigger/dpnsTriggers/createDomainDataTrigger'); + +const executeDataTriggersFactory = require('../../../../../../../lib/document/stateTransition/DocumentsBatchTransition/validation/state/executeDataTriggersFactory'); + +const Identifier = require('../../../../../../../lib/identifier/Identifier'); + +describe('executeDataTriggersFactory', () => { + let childDocument; + let contractMock; + + let dpnsTriggers; + + let domainDocumentType; + + let stateTransitionHeaderMock; + let context; + let documentTransitions; + let dpnsCreateDomainDataTriggerMock; + let dpnsUpdateDomainDataTriggerMock; + let dpnsDeleteDomainDataTriggerMock; + let getDataTriggersMock; + + let executeDataTriggers; + let dataContract; + + beforeEach(function beforeEach() { + dataContract = getDataContractFixture(); + + domainDocumentType = 'domain'; + + dpnsTriggers = [ + dpnsCreateDomainDataTrigger, + dpnsDeleteDomainDataTrigger, + dpnsUpdateDomainDataTrigger, + ]; + + contractMock = getDpnsContractFixture(); + + childDocument = dpnsDocumentFixture.getChildDocumentFixture(); + + dpnsCreateDomainDataTriggerMock = { execute: this.sinonSandbox.stub() }; + dpnsUpdateDomainDataTriggerMock = { execute: this.sinonSandbox.stub() }; + dpnsDeleteDomainDataTriggerMock = { execute: this.sinonSandbox.stub() }; + + dpnsCreateDomainDataTriggerMock + .execute.resolves(new DataTriggerExecutionResult()); + + dpnsUpdateDomainDataTriggerMock + .execute.resolves(new DataTriggerExecutionResult()); + + dpnsDeleteDomainDataTriggerMock + .execute.resolves(new DataTriggerExecutionResult()); + + const ownerId = bs58.decode('5zcXZpTLWFwZjKjq3ME5KVavtZa9YUaZESVzrndehBhq'); + + context = new DataTriggerExecutionContext( + null, ownerId, contractMock, + ); + + documentTransitions = getDocumentTransitionsFixture({ + create: [childDocument], + }); + + getDataTriggersMock = this.sinonSandbox.stub(); + + getDataTriggersMock.returns([ + dpnsCreateDomainDataTriggerMock, + ]); + + executeDataTriggers = executeDataTriggersFactory(getDataTriggersMock); + }); + + it('should return an array of DataTriggerExecutionResult', async () => { + const dataTriggerExecutionResults = await executeDataTriggers( + documentTransitions, context, + ); + + expect(dataTriggerExecutionResults).to.have.a.lengthOf(1); + + const [result] = dataTriggerExecutionResults; + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.getErrors()).to.have.a.lengthOf(0); + expect(result.isOk()).to.be.true(); + }); + + it('should execute multiple data triggers if there is more than one data trigger for' + + ' the same document and action in the contract', async () => { + getDataTriggersMock.returns([ + dpnsCreateDomainDataTriggerMock, + dpnsCreateDomainDataTriggerMock, + dpnsCreateDomainDataTriggerMock, + ]); + + const expectedTriggersCount = 3; + expect(dpnsTriggers.length).to.equal(expectedTriggersCount); + + const dataTriggerExecutionResults = await executeDataTriggers( + documentTransitions, context, + ); + + expect(dataTriggerExecutionResults).to.have.a.lengthOf(expectedTriggersCount); + + dataTriggerExecutionResults.forEach((dataTriggerExecutionResult) => { + expect(dataTriggerExecutionResult.getErrors()).to.have.a.lengthOf(0); + }); + }); + + it('should return a result for each passed document with success or error', async function test() { + const doc1 = getDocumentsFixture(dataContract)[0]; + const doc2 = getDocumentsFixture(dataContract)[1]; + + documentTransitions = getDocumentTransitionsFixture({ + create: [doc1, doc1], + replace: [doc2], + }); + + const passingExecutionResult = new DataTriggerExecutionResult(); + const executionResultWithErrors = new DataTriggerExecutionResult(); + + executionResultWithErrors.addError(new Error('Trigger error')); + + const passingTriggerMockFunction = this.sinonSandbox.stub() + .resolves(passingExecutionResult); + const throwingTriggerMockFunction = this.sinonSandbox.stub() + .resolves(executionResultWithErrors); + + const passingDataTriggerMock = new DataTrigger( + contractMock.getId(), + doc1.getType(), + AbstractDocumentTransition.ACTIONS.CREATE, + passingTriggerMockFunction, + ); + + const throwingDataTriggerMock = new DataTrigger( + contractMock.getId(), + doc2.getType(), + AbstractDocumentTransition.ACTIONS.REPLACE, + throwingTriggerMockFunction, + ); + + getDataTriggersMock + .withArgs(contractMock.getId(), doc1.getType(), AbstractDocumentTransition.ACTIONS.CREATE) + .returns([passingDataTriggerMock]); + + getDataTriggersMock + .withArgs(contractMock.getId(), doc2.getType(), AbstractDocumentTransition.ACTIONS.REPLACE) + .returns([throwingDataTriggerMock]); + + context = new DataTriggerExecutionContext( + null, generateRandomIdentifier(), contractMock, stateTransitionHeaderMock, + ); + + const dataTriggerExecutionResults = await executeDataTriggers( + documentTransitions, context, + ); + + const expectedResultsCount = 3; + + expect(documentTransitions.length).to.equal(expectedResultsCount); + expect(dataTriggerExecutionResults.length).to.equal(expectedResultsCount); + + const passingResults = dataTriggerExecutionResults.filter((result) => result.isOk()); + const failingResults = dataTriggerExecutionResults.filter((result) => !result.isOk()); + + expect(passingResults).to.have.a.lengthOf(2); + expect(failingResults).to.have.a.lengthOf(1); + + expect(failingResults[0].getErrors()).to.have.a.lengthOf(1); + expect(failingResults[0].getErrors()[0].message).to + .equal('Trigger error'); + + expect(passingTriggerMockFunction.callCount).to.equal(2); + expect(throwingTriggerMockFunction.callCount).to.equal(1); + }); + + it("should not call any triggers if documents have no triggers associated with it's type or action", async () => { + getDataTriggersMock + .withArgs( + contractMock.getId(), + domainDocumentType, + AbstractDocumentTransition.ACTIONS.CREATE, + ) + .returns([]) + .withArgs( + contractMock.getId(), + domainDocumentType, + AbstractDocumentTransition.ACTIONS.DELETE, + ) + .returns([dpnsDeleteDomainDataTriggerMock]) + .withArgs( + contractMock.getId(), + domainDocumentType, + AbstractDocumentTransition.ACTIONS.REPLACE, + ) + .returns([dpnsUpdateDomainDataTriggerMock]); + + await executeDataTriggers(documentTransitions, context); + + expect(dpnsDeleteDomainDataTriggerMock.execute).not.to.be.called(); + expect(dpnsUpdateDomainDataTriggerMock.execute).not.to.be.called(); + }); + + it("should call only one trigger if there's one document with a trigger and one without", async () => { + const dataContractId = getDataContractFixture().getId(); + childDocument.dataContractId = dataContractId; + childDocument.dataContract.id = dataContractId; + childDocument.ownerId = Identifier.from( + getDocumentsFixture.ownerId, + ); + + documentTransitions = getDocumentTransitionsFixture({ + create: [childDocument].concat(getDocumentsFixture(dataContract)), + }); + + getDataTriggersMock.resetBehavior(); + getDataTriggersMock + .returns([]) + .withArgs( + contractMock.getId(), + domainDocumentType, + AbstractDocumentTransition.ACTIONS.CREATE, + ) + .returns([dpnsCreateDomainDataTriggerMock]) + .withArgs( + contractMock.getId(), + domainDocumentType, + AbstractDocumentTransition.ACTIONS.DELETE, + ) + .returns([dpnsDeleteDomainDataTriggerMock]) + .withArgs( + contractMock.getId(), + domainDocumentType, + AbstractDocumentTransition.ACTIONS.REPLACE, + ) + .returns([dpnsUpdateDomainDataTriggerMock]); + + await executeDataTriggers(documentTransitions, context); + + expect(dpnsCreateDomainDataTriggerMock.execute).to.be.calledOnce(); + expect(dpnsDeleteDomainDataTriggerMock.execute).not.to.be.called(); + expect(dpnsUpdateDomainDataTriggerMock.execute).not.to.be.called(); + }); + + it("should not call any triggers if there's no triggers in the contract", async () => { + documentTransitions = getDocumentTransitionsFixture({ + create: getDocumentsFixture(dataContract), + }); + + getDataTriggersMock.resetBehavior(); + getDataTriggersMock + .returns([]) + .withArgs( + contractMock.getId(), + domainDocumentType, + AbstractDocumentTransition.ACTIONS.CREATE, + ) + .returns([dpnsCreateDomainDataTriggerMock]) + .withArgs( + contractMock.getId(), + domainDocumentType, + AbstractDocumentTransition.ACTIONS.DELETE, + ) + .returns([dpnsDeleteDomainDataTriggerMock]) + .withArgs( + contractMock.getId(), + domainDocumentType, + AbstractDocumentTransition.ACTIONS.REPLACE, + ) + .returns([dpnsUpdateDomainDataTriggerMock]); + + await executeDataTriggers(documentTransitions, context); + + expect(dpnsCreateDomainDataTriggerMock.execute).not.to.be.called(); + expect(dpnsDeleteDomainDataTriggerMock.execute).not.to.be.called(); + expect(dpnsUpdateDomainDataTriggerMock.execute).not.to.be.called(); + }); +}); diff --git a/packages/js-dpp/test/integration/document/validation/validateDocumentFactory.spec.js b/packages/js-dpp/test/integration/document/validation/validateDocumentFactory.spec.js new file mode 100644 index 00000000000..78c17aecbcc --- /dev/null +++ b/packages/js-dpp/test/integration/document/validation/validateDocumentFactory.spec.js @@ -0,0 +1,447 @@ +const { getRE2Class } = require('@dashevo/wasm-re2'); + +const createAjv = require('../../../../lib/ajv/createAjv'); + +const JsonSchemaValidator = require('../../../../lib/validation/JsonSchemaValidator'); +const ValidationResult = require('../../../../lib/validation/ValidationResult'); + +const DataContract = require('../../../../lib/dataContract/DataContract'); + +const validateDocumentFactory = require('../../../../lib/document/validation/validateDocumentFactory'); +const enrichDataContractWithBaseSchema = require('../../../../lib/dataContract/enrichDataContractWithBaseSchema'); + +const getDataContractFixture = require('../../../../lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('../../../../lib/test/fixtures/getDocumentsFixture'); + +const MissingDocumentTypeError = require('../../../../lib/errors/consensus/basic/document/MissingDocumentTypeError'); +const InvalidDocumentTypeError = require('../../../../lib/errors/consensus/basic/document/InvalidDocumentTypeError'); + +const { + expectValidationError, + expectJsonSchemaError, +} = require('../../../../lib/test/expect/expectError'); +const SomeConsensusError = require('../../../../lib/test/mocks/SomeConsensusError'); + +describe('validateDocumentFactory', () => { + let dataContract; + let rawDocuments; + let rawDocument; + let documents; + let validateDocument; + let validator; + let validateProtocolVersionMock; + + beforeEach(async function beforeEach() { + const RE2 = await getRE2Class(); + const ajv = createAjv(RE2); + + validator = new JsonSchemaValidator(ajv); + + this.sinonSandbox.spy(validator, 'validate'); + + dataContract = getDataContractFixture(); + + validateProtocolVersionMock = this.sinonSandbox.stub().returns(new ValidationResult()); + + validateDocument = validateDocumentFactory( + validator, + enrichDataContractWithBaseSchema, + validateProtocolVersionMock, + ); + + documents = getDocumentsFixture(dataContract); + rawDocuments = documents.map((o) => o.toObject()); + [rawDocument] = rawDocuments; + }); + + describe('Base schema', () => { + describe('$protocolVersion', () => { + it('should be present', () => { + delete rawDocument.$protocolVersion; + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('$protocolVersion'); + }); + + it('should be an integer', () => { + rawDocument.$protocolVersion = '1'; + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$protocolVersion'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be valid', async () => { + rawDocument.$protocolVersion = -1; + + const protocolVersionError = new SomeConsensusError('test'); + const protocolVersionResult = new ValidationResult([ + protocolVersionError, + ]); + + validateProtocolVersionMock.returns(protocolVersionResult); + + const result = await validateDocument(rawDocument, dataContract); + + expectValidationError(result, SomeConsensusError); + + const [error] = result.getErrors(); + + expect(error).to.equal(protocolVersionError); + + expect(validateProtocolVersionMock).to.be.calledOnceWith( + rawDocument.$protocolVersion, + ); + }); + }); + + describe('$id', () => { + it('should be present', () => { + delete rawDocument.$id; + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('$id'); + }); + + it('should be a byte array', () => { + rawDocument.$id = new Array(32).fill('string'); + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$id/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be no less than 32 bytes', () => { + rawDocument.$id = Buffer.alloc(31); + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$id'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be no longer than 32 bytes', () => { + rawDocument.$id = Buffer.alloc(33); + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$id'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + + describe('$type', () => { + let DataContractMock; + + afterEach(() => { + if (DataContractMock) { + DataContractMock.restore(); + } + }); + + it('should be present', () => { + delete rawDocument.$type; + + const result = validateDocument(rawDocument, dataContract); + + expectValidationError( + result, + MissingDocumentTypeError, + ); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1028); + }); + + it('should be defined in Data Contract', () => { + rawDocument.$type = 'undefinedDocument'; + + const result = validateDocument(rawDocument, dataContract); + + expectValidationError( + result, + InvalidDocumentTypeError, + ); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1024); + expect(error.getType()).to.equal('undefinedDocument'); + }); + + it('should throw an error if getDocumentSchemaRef throws error', function it() { + const someError = new Error(); + + DataContractMock = this.sinonSandbox.stub(DataContract.prototype, 'getDocumentSchemaRef').throws(someError); + + let error; + try { + validateDocument(rawDocument, dataContract); + } catch (e) { + error = e; + } + + expect(error).to.equal(someError); + + expect(dataContract.getDocumentSchemaRef).to.have.been.calledOnce(); + }); + }); + + describe('$revision', () => { + it('should be present', () => { + delete rawDocument.$revision; + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('$revision'); + }); + + it('should be a number', () => { + rawDocument.$revision = 'string'; + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$revision'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be an integer', () => { + rawDocument.$revision = 1.1; + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$revision'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be greater or equal to one', () => { + rawDocument.$revision = -1; + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$revision'); + expect(error.getKeyword()).to.equal('minimum'); + }); + }); + + describe('$dataContractId', () => { + it('should be present', () => { + delete rawDocument.$dataContractId; + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('$dataContractId'); + }); + + it('should be a byte array', () => { + rawDocument.$dataContractId = new Array(32).fill('string'); + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$dataContractId/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be no less than 32 bytes', () => { + rawDocument.$dataContractId = Buffer.alloc(31); + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$dataContractId'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be no longer than 32 bytes', () => { + rawDocument.$dataContractId = Buffer.alloc(33); + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/$dataContractId'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + + describe('$ownerId', () => { + it('should be present', () => { + delete rawDocument.$ownerId; + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('$ownerId'); + }); + + it('should be a byte array', () => { + rawDocument.$ownerId = new Array(32).fill('string'); + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.instancePath).to.equal('/$ownerId/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be no less than 32 bytes', () => { + rawDocument.$ownerId = Buffer.alloc(31); + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/$ownerId'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be no longer than 32 bytes', () => { + rawDocument.$ownerId = Buffer.alloc(33); + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/$ownerId'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + }); + + describe('Data Contract schema', () => { + it('should return an error if the first document is not valid against Data Contract', () => { + rawDocument.name = 1; + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/name'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should return an error if the second document is not valid against Data Contract', () => { + // eslint-disable-next-line prefer-destructuring + rawDocument = rawDocuments[1]; + rawDocument.undefined = 1; + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal(''); + expect(error.getKeyword()).to.equal('additionalProperties'); + }); + }); + + it('return invalid result if a byte array exceeds `maxItems`', () => { + // eslint-disable-next-line prefer-destructuring + rawDocument = getDocumentsFixture(dataContract)[8].toObject(); + + rawDocument.byteArrayField = Buffer.alloc(32); + + const result = validateDocument(rawDocument, dataContract); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/byteArrayField'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + + it('should return valid result is a document is valid', () => { + const result = validateDocument(rawDocument, dataContract); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/integration/error/consensus/createConsensusError.spec.js b/packages/js-dpp/test/integration/error/consensus/createConsensusError.spec.js new file mode 100644 index 00000000000..a24895a748a --- /dev/null +++ b/packages/js-dpp/test/integration/error/consensus/createConsensusError.spec.js @@ -0,0 +1,21 @@ +const InvalidDataContractIdError = require('../../../../lib/errors/consensus/basic/dataContract/InvalidDataContractIdError'); +const generateRandomIdentifier = require('../../../../lib/test/utils/generateRandomIdentifier'); +const createConsensusError = require('../../../../lib/errors/consensus/createConsensusError'); + +describe('createConsensusError', () => { + it('should create an error instance from code and arguments', () => { + const expectedId = generateRandomIdentifier(); + const invalidId = Buffer.alloc(16).fill(1); + + const error = new InvalidDataContractIdError(expectedId.toBuffer(), invalidId); + + const restoredError = createConsensusError(error.getCode(), error.getConstructorArguments()); + + // Stack will be always different so we need to skip it for comparison + expect(restoredError.message).to.equal(error.message); + expect(restoredError.getExpectedId()).to.deep.equal(error.getExpectedId()); + expect(restoredError.getInvalidId()).to.deep.equal(error.getInvalidId()); + expect(restoredError.getConstructorArguments()).to.deep.equal(error.getConstructorArguments()); + expect(restoredError.getCode()).to.deep.equal(error.getCode()); + }); +}); diff --git a/packages/js-dpp/test/integration/identity/IdentityFacade.spec.js b/packages/js-dpp/test/integration/identity/IdentityFacade.spec.js new file mode 100644 index 00000000000..3dcaafae0e7 --- /dev/null +++ b/packages/js-dpp/test/integration/identity/IdentityFacade.spec.js @@ -0,0 +1,187 @@ +const { PublicKey } = require('@dashevo/dashcore-lib'); +const DashPlatformProtocol = require('../../../lib/DashPlatformProtocol'); + +const Identity = require('../../../lib/identity/Identity'); +const IdentityCreateTransition = require('../../../lib/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition'); +const IdentityTopUpTransition = require('../../../lib/identity/stateTransition/IdentityTopUpTransition/IdentityTopUpTransition'); + +const ValidationResult = require('../../../lib/validation/ValidationResult'); + +const getIdentityFixture = require('../../../lib/test/fixtures/getIdentityFixture'); + +const createStateRepositoryMock = require('../../../lib/test/mocks/createStateRepositoryMock'); +const InstantAssetLockProof = require('../../../lib/identity/stateTransition/assetLockProof/instant/InstantAssetLockProof'); +const getInstantAssetLockProofFixture = require('../../../lib/test/fixtures/getInstantAssetLockProofFixture'); +const getChainAssetLockProofFixture = require('../../../lib/test/fixtures/getChainAssetLockProofFixture'); +const ChainAssetLockProof = require('../../../lib/identity/stateTransition/assetLockProof/chain/ChainAssetLockProof'); +const IdentityUpdateTransition = require('../../../lib/identity/stateTransition/IdentityUpdateTransition/IdentityUpdateTransition'); +const IdentityPublicKey = require('../../../lib/identity/IdentityPublicKey'); + +describe('IdentityFacade', () => { + let dpp; + let identity; + let stateRepositoryMock; + let instantAssetLockProof; + let chainAssetLockProof; + + beforeEach(async function beforeEach() { + const rawTransaction = '030000000137feb5676d0851337ea3c9a992496aab7a0b3eee60aeeb9774000b7f4bababa5000000006b483045022100d91557de37645c641b948c6cd03b4ae3791a63a650db3e2fee1dcf5185d1b10402200e8bd410bf516ca61715867666d31e44495428ce5c1090bf2294a829ebcfa4ef0121025c3cc7fbfc52f710c941497fd01876c189171ea227458f501afcb38a297d65b4ffffffff021027000000000000166a14152073ca2300a86b510fa2f123d3ea7da3af68dcf77cb0090a0000001976a914152073ca2300a86b510fa2f123d3ea7da3af68dc88ac00000000'; + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchTransaction.resolves(rawTransaction); + + dpp = new DashPlatformProtocol({ + stateRepository: stateRepositoryMock, + }); + await dpp.initialize(); + + chainAssetLockProof = getChainAssetLockProofFixture(); + instantAssetLockProof = getInstantAssetLockProofFixture(); + identity = getIdentityFixture(); + identity.id = instantAssetLockProof.createIdentifier(); + identity.setAssetLockProof(instantAssetLockProof); + identity.setBalance(0); + }); + + describe('#create', () => { + it('should create Identity', () => { + const publicKeys = identity.getPublicKeys() + .map((identityPublicKey) => ({ + ...identityPublicKey.toObject(), + key: new PublicKey(identityPublicKey.getData()), + })); + + const result = dpp.identity.create( + instantAssetLockProof, + publicKeys, + ); + + expect(result).to.be.an.instanceOf(Identity); + expect(result.toObject()).to.deep.equal(identity.toObject()); + }); + }); + + describe('#createFromObject', () => { + it('should create Identity from plain object', () => { + const result = dpp.identity.createFromObject(identity.toObject()); + + expect(result).to.be.an.instanceOf(Identity); + + expect(result.toObject()).to.deep.equal(identity.toObject()); + }); + }); + + describe('#createFromBuffer', () => { + it('should create Identity from string', () => { + const result = dpp.identity.createFromBuffer(identity.toBuffer()); + + expect(result).to.be.an.instanceOf(Identity); + + expect(result.toObject()).to.deep.equal(identity.toObject()); + }); + }); + + describe('#validate', () => { + it('should validate Identity', async () => { + const result = await dpp.identity.validate(identity); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + }); + + describe('#createInstantAssetLockProof', () => { + it('should create instant asset lock proof', () => { + const instantLock = instantAssetLockProof.getInstantLock(); + const assetLockTransaction = instantAssetLockProof.getTransaction(); + const outputIndex = instantAssetLockProof.getOutputIndex(); + + const result = dpp.identity.createInstantAssetLockProof( + instantLock, + assetLockTransaction, + outputIndex, + ); + + expect(result).to.be.instanceOf(InstantAssetLockProof); + expect(result.getInstantLock()).to.deep.equal(instantLock); + expect(result.getTransaction().toObject()).to.deep.equal(assetLockTransaction.toObject()); + expect(result.getOutputIndex()).to.equal(outputIndex); + }); + }); + + describe('#createChainAssetLockProof', () => { + it('should create chain asset lock proof', () => { + const coreChainLockedHeight = chainAssetLockProof.getCoreChainLockedHeight(); + const outPoint = chainAssetLockProof.getOutPoint(); + + const result = dpp.identity.createChainAssetLockProof( + coreChainLockedHeight, + outPoint, + ); + + expect(result).to.be.instanceOf(ChainAssetLockProof); + expect(result.getCoreChainLockedHeight()).to.equal(coreChainLockedHeight); + expect(result.getOutPoint()).to.deep.equal(outPoint); + }); + }); + + describe('#createIdentityCreateTransition', () => { + it('should create IdentityCreateTransition from Identity model', () => { + const stateTransition = dpp.identity.createIdentityCreateTransition(identity); + + expect(stateTransition).to.be.instanceOf(IdentityCreateTransition); + expect(stateTransition.getPublicKeys()).to.deep.equal(identity.getPublicKeys()); + expect(stateTransition.getAssetLockProof().toObject()).to.deep.equal( + instantAssetLockProof.toObject(), + ); + }); + }); + + describe('#createIdentityTopUpTransition', () => { + it('should create IdentityTopUpTransition from identity id and outpoint', () => { + const stateTransition = dpp.identity + .createIdentityTopUpTransition( + identity.getId(), + instantAssetLockProof, + ); + + expect(stateTransition).to.be.instanceOf(IdentityTopUpTransition); + expect(stateTransition.getIdentityId()).to.be.deep.equal(identity.getId()); + expect(stateTransition.getAssetLockProof().toObject()).to.deep.equal( + instantAssetLockProof.toObject(), + ); + }); + }); + + describe('#createIdentityUpdateTransition', () => { + it('should create IdentityUpdateTransition from identity id and public keys', () => { + const publicKeys = { + add: [new IdentityPublicKey({ + id: 3, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: Buffer.from('AuryIuMtRrl/VviQuyLD1l4nmxi9ogPzC9LT7tdpo0di', 'base64'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.CRITICAL, + readOnly: false, + })], + }; + + const stateTransition = dpp.identity + .createIdentityUpdateTransition( + identity, + publicKeys, + ); + + expect(stateTransition).to.be.instanceOf(IdentityUpdateTransition); + expect(stateTransition.getIdentityId()).to.be.deep.equal(identity.getId()); + expect(stateTransition.getRevision()).to.equal( + identity.getRevision() + 1, + ); + expect( + stateTransition.getPublicKeysToAdd().map((pk) => pk.toObject()), + ).to.deep.equal(publicKeys.add); + expect(stateTransition.getPublicKeyIdsToDisable()).to.equal(undefined); + expect(stateTransition.getPublicKeysDisabledAt()).to.equal(undefined); + }); + }); +}); diff --git a/packages/js-dpp/test/integration/identity/stateTransition/IdentityCreateTransition/validation/basic/validateIdentityCreateTransitionBasicFactory.spec.js b/packages/js-dpp/test/integration/identity/stateTransition/IdentityCreateTransition/validation/basic/validateIdentityCreateTransitionBasicFactory.spec.js new file mode 100644 index 00000000000..485192fc72e --- /dev/null +++ b/packages/js-dpp/test/integration/identity/stateTransition/IdentityCreateTransition/validation/basic/validateIdentityCreateTransitionBasicFactory.spec.js @@ -0,0 +1,422 @@ +const { getRE2Class } = require('@dashevo/wasm-re2'); + +const createAjv = require('../../../../../../../lib/ajv/createAjv'); + +const JsonSchemaValidator = require('../../../../../../../lib/validation/JsonSchemaValidator'); + +const getIdentityCreateTransitionFixture = require('../../../../../../../lib/test/fixtures/getIdentityCreateTransitionFixture'); + +const validateIdentityCreateTransitionBasicFactory = require( + '../../../../../../../lib/identity/stateTransition/IdentityCreateTransition/validation/basic/validateIdentityCreateTransitionBasicFactory', +); + +const { + expectJsonSchemaError, + expectValidationError, +} = require('../../../../../../../lib/test/expect/expectError'); + +const ValidationResult = require('../../../../../../../lib/validation/ValidationResult'); +const InstantAssetLockProof = require('../../../../../../../lib/identity/stateTransition/assetLockProof/instant/InstantAssetLockProof'); +const ChainAssetLockProof = require('../../../../../../../lib/identity/stateTransition/assetLockProof/chain/ChainAssetLockProof'); +const SomeConsensusError = require('../../../../../../../lib/test/mocks/SomeConsensusError'); +const IdentityPublicKey = require('../../../../../../../lib/identity/IdentityPublicKey'); +const StateTransitionExecutionContext = require('../../../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('validateIdentityCreateTransitionBasicFactory', () => { + let validateIdentityCreateTransitionBasic; + let rawStateTransition; + let stateTransition; + let validatePublicKeysMock; + let validatePublicKeysInIdentityCreateTransition; + let assetLockPublicKeyHash; + let proofValidationFunctionsByTypeMock; + let validateProtocolVersionMock; + let validatePublicKeySignaturesMock; + + beforeEach(async function beforeEach() { + validatePublicKeysMock = this.sinonSandbox.stub() + .returns(new ValidationResult()); + + validatePublicKeysInIdentityCreateTransition = this.sinonSandbox.stub() + .returns(new ValidationResult()); + + assetLockPublicKeyHash = Buffer.alloc(20, 1); + + const assetLockValidationResult = new ValidationResult(); + + assetLockValidationResult.setData(assetLockPublicKeyHash); + + const RE2 = await getRE2Class(); + const ajv = createAjv(RE2); + + const jsonSchemaValidator = new JsonSchemaValidator(ajv); + + const proofValidationResult = new ValidationResult(); + proofValidationResult.setData(assetLockPublicKeyHash); + + proofValidationFunctionsByTypeMock = { + [InstantAssetLockProof.type]: this.sinonSandbox.stub().resolves(proofValidationResult), + [ChainAssetLockProof.type]: this.sinonSandbox.stub().resolves(proofValidationResult), + }; + + validateProtocolVersionMock = this.sinonSandbox.stub().returns(new ValidationResult()); + + validatePublicKeySignaturesMock = this.sinonSandbox.stub() + .returns(new ValidationResult()); + + validateIdentityCreateTransitionBasic = validateIdentityCreateTransitionBasicFactory( + jsonSchemaValidator, + validatePublicKeysMock, + validatePublicKeysInIdentityCreateTransition, + proofValidationFunctionsByTypeMock, + validateProtocolVersionMock, + validatePublicKeySignaturesMock, + ); + + stateTransition = getIdentityCreateTransitionFixture(); + + const privateKey = '9b67f852093bc61cea0eeca38599dbfba0de28574d2ed9b99d10d33dc1bde7b2'; + + await stateTransition.signByPrivateKey(privateKey, IdentityPublicKey.TYPES.ECDSA_SECP256K1); + + rawStateTransition = stateTransition.toObject(); + }); + + describe('protocolVersion', () => { + it('should be present', async () => { + delete rawStateTransition.protocolVersion; + + const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('protocolVersion'); + }); + + it('should be an integer', async () => { + rawStateTransition.protocolVersion = '1'; + + const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/protocolVersion'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be valid', async () => { + rawStateTransition.protocolVersion = -1; + + const protocolVersionError = new SomeConsensusError('test'); + const protocolVersionResult = new ValidationResult([ + protocolVersionError, + ]); + + validateProtocolVersionMock.returns(protocolVersionResult); + + const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + + expectValidationError(result, SomeConsensusError); + + const [error] = result.getErrors(); + + expect(error).to.equal(protocolVersionError); + + expect(validateProtocolVersionMock).to.be.calledOnceWith( + rawStateTransition.protocolVersion, + ); + }); + }); + + describe('type', () => { + it('should be present', async () => { + delete rawStateTransition.type; + + const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('type'); + }); + + it('should be equal to 2', async () => { + rawStateTransition.type = 666; + + const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/type'); + expect(error.getKeyword()).to.equal('const'); + expect(error.getParams().allowedValue).to.equal(2); + }); + }); + + describe('assetLockProof', () => { + it('should be present', async () => { + delete rawStateTransition.assetLockProof; + + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getParams().missingProperty).to.equal('assetLockProof'); + expect(error.getKeyword()).to.equal('required'); + }); + + it('should be an object', async () => { + rawStateTransition.assetLockProof = 1; + + const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result, 1); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/assetLockProof'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be valid', async () => { + const assetLockError = new SomeConsensusError('test'); + const assetLockResult = new ValidationResult([ + assetLockError, + ]); + + const executionContext = new StateTransitionExecutionContext(); + + proofValidationFunctionsByTypeMock[InstantAssetLockProof.type].resolves(assetLockResult); + + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.equal(assetLockError); + + expect(proofValidationFunctionsByTypeMock[InstantAssetLockProof.type]) + .to.be.calledOnceWithExactly( + rawStateTransition.assetLockProof, + executionContext, + ); + }); + }); + + describe('publicKeys', () => { + it('should be present', async () => { + rawStateTransition.publicKeys = undefined; + + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getParams().missingProperty).to.equal('publicKeys'); + expect(error.getKeyword()).to.equal('required'); + }); + + it('should not be empty', async () => { + rawStateTransition.publicKeys = []; + + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('minItems'); + expect(error.getInstancePath()).to.equal('/publicKeys'); + }); + + it('should not have more than 10 items', async () => { + const [key] = rawStateTransition.publicKeys; + + for (let i = 0; i < 10; i++) { + rawStateTransition.publicKeys.push(key); + } + + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('maxItems'); + expect(error.getInstancePath()).to.equal('/publicKeys'); + }); + + it('should be unique', async () => { + rawStateTransition.publicKeys.push(rawStateTransition.publicKeys[0]); + + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('uniqueItems'); + expect(error.getInstancePath()).to.equal('/publicKeys'); + }); + + it('should be valid', async () => { + const publicKeysError = new SomeConsensusError('test'); + const publicKeysResult = new ValidationResult([ + publicKeysError, + ]); + + validatePublicKeysMock.returns(publicKeysResult); + + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + ); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.equal(publicKeysError); + + expect(validatePublicKeysMock) + .to.be.calledOnceWithExactly(rawStateTransition.publicKeys); + }); + + it('should have at least 1 master key', async () => { + const publicKeysError = new SomeConsensusError('test'); + const publicKeysResult = new ValidationResult([ + publicKeysError, + ]); + + validatePublicKeysInIdentityCreateTransition.returns(publicKeysResult); + + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + ); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.equal(publicKeysError); + + expect(validatePublicKeysInIdentityCreateTransition) + .to.be.calledOnceWithExactly(rawStateTransition.publicKeys); + }); + + it('should have valid signatures', async () => { + const publicKeysError = new SomeConsensusError('test'); + const publicKeysResult = new ValidationResult([ + publicKeysError, + ]); + + validatePublicKeySignaturesMock.resolves(publicKeysResult); + + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + ); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.equal(publicKeysError); + }); + }); + + describe('signature', () => { + it('should be present', async () => { + delete rawStateTransition.signature; + + const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('signature'); + }); + + it('should be a byte array', async () => { + rawStateTransition.signature = new Array(65).fill('string'); + + const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be not shorter than 65 bytes', async () => { + rawStateTransition.signature = Buffer.alloc(64); + + const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be not longer than 65 bytes', async () => { + rawStateTransition.signature = Buffer.alloc(66); + + const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + + it('should return valid result', async () => { + const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + + expect(result.isValid()).to.be.true(); + + expect(validatePublicKeysMock).to.be.calledOnceWithExactly( + rawStateTransition.publicKeys, + ); + }); +}); diff --git a/packages/js-dpp/test/integration/identity/stateTransition/IdentityTopUpTransition/validation/basic/validateIdentityTopUpTransitionBasicFactory.spec.js b/packages/js-dpp/test/integration/identity/stateTransition/IdentityTopUpTransition/validation/basic/validateIdentityTopUpTransitionBasicFactory.spec.js new file mode 100644 index 00000000000..315055c0818 --- /dev/null +++ b/packages/js-dpp/test/integration/identity/stateTransition/IdentityTopUpTransition/validation/basic/validateIdentityTopUpTransitionBasicFactory.spec.js @@ -0,0 +1,361 @@ +const { getRE2Class } = require('@dashevo/wasm-re2'); + +const createAjv = require('../../../../../../../lib/ajv/createAjv'); + +const JsonSchemaValidator = require('../../../../../../../lib/validation/JsonSchemaValidator'); + +const getIdentityTopUpTransitionFixture = require('../../../../../../../lib/test/fixtures/getIdentityTopUpTransitionFixture'); + +const validateIdentityTopUpTransitionBasicFactory = require( + '../../../../../../../lib/identity/stateTransition/IdentityTopUpTransition/validation/basic/validateIdentityTopUpTransitionBasicFactory', +); + +const { + expectJsonSchemaError, + expectValidationError, +} = require('../../../../../../../lib/test/expect/expectError'); + +const ValidationResult = require('../../../../../../../lib/validation/ValidationResult'); + +const ChainAssetLockProof = require('../../../../../../../lib/identity/stateTransition/assetLockProof/chain/ChainAssetLockProof'); +const InstantAssetLockProof = require('../../../../../../../lib/identity/stateTransition/assetLockProof/instant/InstantAssetLockProof'); +const SomeConsensusError = require('../../../../../../../lib/test/mocks/SomeConsensusError'); +const IdentityPublicKey = require('../../../../../../../lib/identity/IdentityPublicKey'); +const StateTransitionExecutionContext = require('../../../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('validateIdentityTopUpTransitionBasicFactory', () => { + let rawStateTransition; + let stateTransition; + let assetLockPublicKeyHash; + let validateIdentityTopUpTransitionBasic; + let proofValidationFunctionsByTypeMock; + let validateProtocolVersionMock; + let executionContext; + + beforeEach(async function beforeEach() { + assetLockPublicKeyHash = Buffer.alloc(20, 1); + + const assetLockValidationResult = new ValidationResult(); + assetLockValidationResult.setData(assetLockPublicKeyHash); + + proofValidationFunctionsByTypeMock = { + [InstantAssetLockProof.type]: this.sinonSandbox.stub().resolves(assetLockValidationResult), + [ChainAssetLockProof.type]: this.sinonSandbox.stub().resolves(assetLockValidationResult), + }; + + const RE2 = await getRE2Class(); + const ajv = createAjv(RE2); + + const jsonSchemaValidator = new JsonSchemaValidator(ajv); + + validateProtocolVersionMock = this.sinonSandbox.stub().returns(new ValidationResult()); + + validateIdentityTopUpTransitionBasic = validateIdentityTopUpTransitionBasicFactory( + jsonSchemaValidator, + proofValidationFunctionsByTypeMock, + validateProtocolVersionMock, + ); + + executionContext = new StateTransitionExecutionContext(); + + stateTransition = getIdentityTopUpTransitionFixture(); + + const privateKey = '9b67f852093bc61cea0eeca38599dbfba0de28574d2ed9b99d10d33dc1bde7b2'; + + await stateTransition.signByPrivateKey(privateKey, IdentityPublicKey.TYPES.ECDSA_SECP256K1); + + rawStateTransition = stateTransition.toObject(); + }); + + describe('protocolVersion', () => { + it('should be present', async () => { + delete rawStateTransition.protocolVersion; + + const result = await validateIdentityTopUpTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('protocolVersion'); + }); + + it('should be an integer', async () => { + rawStateTransition.protocolVersion = '1'; + + const result = await validateIdentityTopUpTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/protocolVersion'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be valid', async () => { + rawStateTransition.protocolVersion = -1; + + const protocolVersionError = new SomeConsensusError('test'); + const protocolVersionResult = new ValidationResult([ + protocolVersionError, + ]); + + validateProtocolVersionMock.returns(protocolVersionResult); + + const result = await validateIdentityTopUpTransitionBasic(rawStateTransition); + + expectValidationError(result, SomeConsensusError); + + const [error] = result.getErrors(); + + expect(error).to.equal(protocolVersionError); + + expect(validateProtocolVersionMock).to.be.calledOnceWith( + rawStateTransition.protocolVersion, + ); + }); + }); + + describe('type', () => { + it('should be present', async () => { + delete rawStateTransition.type; + + const result = await validateIdentityTopUpTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('type'); + }); + + it('should be equal to 3', async () => { + rawStateTransition.type = 666; + + const result = await validateIdentityTopUpTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/type'); + expect(error.getKeyword()).to.equal('const'); + expect(error.getParams().allowedValue).to.equal(3); + }); + }); + + describe('assetLockProof', () => { + it('should be present', async () => { + delete rawStateTransition.assetLockProof; + + const result = await validateIdentityTopUpTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getParams().missingProperty).to.equal('assetLockProof'); + expect(error.getKeyword()).to.equal('required'); + }); + + it('should be an object', async () => { + rawStateTransition.assetLockProof = 1; + + const result = await validateIdentityTopUpTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result, 1); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/assetLockProof'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be valid', async () => { + const assetLockError = new SomeConsensusError('test'); + const assetLockResult = new ValidationResult([ + assetLockError, + ]); + + proofValidationFunctionsByTypeMock[InstantAssetLockProof.type].resolves(assetLockResult); + + const result = await validateIdentityTopUpTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.equal(assetLockError); + + expect(proofValidationFunctionsByTypeMock[InstantAssetLockProof.type]) + .to.be.calledOnceWithExactly( + rawStateTransition.assetLockProof, + executionContext, + ); + }); + }); + + describe('identityId', () => { + it('should be present', async () => { + delete rawStateTransition.identityId; + + const result = await validateIdentityTopUpTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('identityId'); + }); + + it('should be a byte array', async () => { + rawStateTransition.identityId = new Array(32).fill('string'); + + const result = await validateIdentityTopUpTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/identityId/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be no less than 32 bytes', async () => { + rawStateTransition.identityId = Buffer.alloc(31); + + const result = await validateIdentityTopUpTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/identityId'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be no longer than 32 bytes', async () => { + rawStateTransition.identityId = Buffer.alloc(33); + + const result = await validateIdentityTopUpTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/identityId'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + + describe('signature', () => { + it('should be present', async () => { + delete rawStateTransition.signature; + + const result = await validateIdentityTopUpTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('signature'); + }); + + it('should be a byte array', async () => { + rawStateTransition.signature = new Array(65).fill('string'); + + const result = await validateIdentityTopUpTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be not shorter than 65 bytes', async () => { + rawStateTransition.signature = Buffer.alloc(64); + + const result = await validateIdentityTopUpTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be not longer than 65 bytes', async () => { + rawStateTransition.signature = Buffer.alloc(66); + + const result = await validateIdentityTopUpTransitionBasic( + rawStateTransition, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + + it('should return valid result', async () => { + const result = await validateIdentityTopUpTransitionBasic( + rawStateTransition, + executionContext, + ); + + expect(result.isValid()).to.be.true(); + + expect(proofValidationFunctionsByTypeMock[InstantAssetLockProof.type]) + .to.be.calledOnceWithExactly( + rawStateTransition.assetLockProof, + executionContext, + ); + }); +}); diff --git a/packages/js-dpp/test/integration/identity/stateTransition/IdentityUpdateTransition/validation/basic/validateIdentityUpdateTransitionBasicFactory.spec.js b/packages/js-dpp/test/integration/identity/stateTransition/IdentityUpdateTransition/validation/basic/validateIdentityUpdateTransitionBasicFactory.spec.js new file mode 100644 index 00000000000..bac1345b686 --- /dev/null +++ b/packages/js-dpp/test/integration/identity/stateTransition/IdentityUpdateTransition/validation/basic/validateIdentityUpdateTransitionBasicFactory.spec.js @@ -0,0 +1,595 @@ +const { getRE2Class } = require('@dashevo/wasm-re2'); +const { PrivateKey } = require('@dashevo/dashcore-lib'); +const validateIdentityUpdateTransitionBasicFactory = require( + '../../../../../../../lib/identity/stateTransition/IdentityUpdateTransition/validation/basic/validateIdentityUpdateTransitionBasicFactory', +); +const JsonSchemaValidator = require('../../../../../../../lib/validation/JsonSchemaValidator'); +const createAjv = require('../../../../../../../lib/ajv/createAjv'); +const ValidationResult = require('../../../../../../../lib/validation/ValidationResult'); +const IdentityPublicKey = require('../../../../../../../lib/identity/IdentityPublicKey'); +const getIdentityUpdateTransitionFixture = require('../../../../../../../lib/test/fixtures/getIdentityUpdateTransitionFixture'); +const { expectJsonSchemaError, expectValidationError } = require('../../../../../../../lib/test/expect/expectError'); +const SomeConsensusError = require('../../../../../../../lib/test/mocks/SomeConsensusError'); + +describe('validateIdentityUpdateTransitionBasicFactory', () => { + let validateIdentityUpdateTransitionBasic; + let validateProtocolVersionMock; + let validatePublicKeysMock; + let rawStateTransition; + let stateTransition; + let publicKeyToAdd; + let validatePublicKeySignaturesMock; + + beforeEach(async function beforeEach() { + const RE2 = await getRE2Class(); + const ajv = createAjv(RE2); + const jsonSchemaValidator = new JsonSchemaValidator(ajv); + + validateProtocolVersionMock = this.sinonSandbox.stub().returns(new ValidationResult()); + + validatePublicKeysMock = this.sinonSandbox.stub() + .returns(new ValidationResult()); + + validatePublicKeySignaturesMock = this.sinonSandbox.stub() + .returns(new ValidationResult()); + + validateIdentityUpdateTransitionBasic = validateIdentityUpdateTransitionBasicFactory( + jsonSchemaValidator, + validateProtocolVersionMock, + validatePublicKeysMock, + validatePublicKeySignaturesMock, + ); + + stateTransition = getIdentityUpdateTransitionFixture(); + + const privateKeyModel = new PrivateKey(); + const privateKeyHex = privateKeyModel.toBuffer().toString('hex'); + const publicKey = privateKeyModel.toPublicKey().toBuffer(); + + const identityPublicKey = new IdentityPublicKey() + .setId(1) + .setType(IdentityPublicKey.TYPES.ECDSA_SECP256K1) + .setData(publicKey) + .setSecurityLevel(IdentityPublicKey.SECURITY_LEVELS.MASTER) + .setPurpose(IdentityPublicKey.PURPOSES.AUTHENTICATION); + + await stateTransition.sign(identityPublicKey, privateKeyHex); + + rawStateTransition = stateTransition.toObject(); + + publicKeyToAdd = { + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: Buffer.from('AuryIuMtRrl/VviQuyLD1l4nmxi9ogPzC9LT7tdpo0di', 'base64'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + }; + }); + + describe('protocolVersion', () => { + it('should be present', async () => { + delete rawStateTransition.protocolVersion; + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('protocolVersion'); + }); + + it('should be integer', async () => { + rawStateTransition.protocolVersion = '1'; + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/protocolVersion'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be valid', async () => { + rawStateTransition.protocolVersion = -1; + const protocolVersionError = new SomeConsensusError('test'); + const protocolVersionResult = new ValidationResult([ + protocolVersionError, + ]); + + validateProtocolVersionMock.returns(protocolVersionResult); + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectValidationError(result, SomeConsensusError); + + const [error] = result.getErrors(); + + expect(error).to.equal(protocolVersionError); + + expect(validateProtocolVersionMock).to.be.calledOnceWith( + rawStateTransition.protocolVersion, + ); + }); + }); + + describe('type', () => { + it('should be present', async () => { + delete rawStateTransition.type; + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('type'); + }); + + it('should be equal to 5', async () => { + rawStateTransition.type = 666; + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/type'); + expect(error.getKeyword()).to.equal('const'); + expect(error.getParams().allowedValue).to.equal(5); + }); + }); + + describe('identityId', () => { + it('should be present', async () => { + delete rawStateTransition.identityId; + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('identityId'); + }); + + it('should be a byte array', async () => { + rawStateTransition.identityId = new Array(32).fill('string'); + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/identityId/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be no less than 32 bytes', async () => { + rawStateTransition.identityId = Buffer.alloc(31); + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/identityId'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be no longer than 32 bytes', async () => { + rawStateTransition.identityId = Buffer.alloc(33); + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/identityId'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + + describe('signature', () => { + it('should be present', async () => { + delete rawStateTransition.signature; + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('signature'); + }); + + it('should be a byte array', async () => { + rawStateTransition.signature = new Array(65).fill('string'); + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be not shorter than 65 bytes', async () => { + rawStateTransition.signature = Buffer.alloc(64); + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be not longer than 96 bytes', async () => { + rawStateTransition.signature = Buffer.alloc(97); + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + + describe('revision', () => { + it('should be present', async () => { + delete rawStateTransition.revision; + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('revision'); + }); + + it('should be integer', async () => { + rawStateTransition.revision = '1'; + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/revision'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be greater or equal 0', async () => { + rawStateTransition.revision = -1; + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('minimum'); + expect(error.getInstancePath()).to.equal('/revision'); + }); + }); + + describe('addPublicKeys', async () => { + beforeEach(() => { + delete rawStateTransition.disablePublicKeys; + delete rawStateTransition.publicKeysDisabledAt; + }); + + it('should return valid result', async () => { + rawStateTransition.addPublicKeys = [publicKeyToAdd]; + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expect(result.isValid()).to.be.true(); + }); + + it('should not be empty', async () => { + rawStateTransition.addPublicKeys = []; + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + expect(error.getKeyword()).to.equal('minItems'); + expect(error.getInstancePath()).to.equal('/addPublicKeys'); + }); + + it('should not have more than 10 items', async () => { + rawStateTransition.addPublicKeys = []; + + for (let i = 0; i <= 10; i++) { + rawStateTransition.addPublicKeys.push(publicKeyToAdd); + } + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('maxItems'); + expect(error.getInstancePath()).to.equal('/addPublicKeys'); + }); + + it('should be unique', async () => { + rawStateTransition.addPublicKeys = [publicKeyToAdd, publicKeyToAdd]; + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('uniqueItems'); + expect(error.getInstancePath()).to.equal('/addPublicKeys'); + }); + + it('should be valid', async () => { + rawStateTransition.addPublicKeys = [publicKeyToAdd]; + + const publicKeysError = new SomeConsensusError('test'); + const publicKeysResult = new ValidationResult([ + publicKeysError, + ]); + + validatePublicKeysMock.returns(publicKeysResult); + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.equal(publicKeysError); + + expect(validatePublicKeysMock).to.be.calledOnceWithExactly( + rawStateTransition.addPublicKeys, + ); + }); + + it('should have valid signatures', async () => { + const publicKeysError = new SomeConsensusError('test'); + const publicKeysResult = new ValidationResult([ + publicKeysError, + ]); + + validatePublicKeySignaturesMock.resolves(publicKeysResult); + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.equal(publicKeysError); + }); + }); + + describe('disablePublicKeys', async () => { + beforeEach(() => { + delete rawStateTransition.addPublicKeys; + }); + + it('should be used only with publicKeysDisabledAt', async () => { + delete rawStateTransition.publicKeysDisabledAt; + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + expect(error.getKeyword()).to.equal('dependentRequired'); + expect(error.params.missingProperty).to.equal('publicKeysDisabledAt'); + }); + + it('should be valid', async () => { + rawStateTransition.disablePublicKeys = [0]; + rawStateTransition.publicKeysDisabledAt = 0; + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expect(result.isValid()).to.be.true(); + }); + + it('should contain numbers >= 0', async () => { + rawStateTransition.disablePublicKeys = [-1, 0]; + rawStateTransition.publicKeysDisabledAt = 0; + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/disablePublicKeys/0'); + expect(error.getKeyword()).to.equal('minimum'); + }); + + it('should contain integers', async () => { + rawStateTransition.publicKeysDisabledAt = 0; + rawStateTransition.disablePublicKeys = [1.1]; + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/disablePublicKeys/0'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should not have more than 10 items', async () => { + rawStateTransition.publicKeysDisabledAt = 0; + rawStateTransition.disablePublicKeys = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('maxItems'); + expect(error.getInstancePath()).to.equal('/disablePublicKeys'); + }); + + it('should be unique', async () => { + rawStateTransition.publicKeysDisabledAt = 0; + rawStateTransition.disablePublicKeys = [0, 0]; + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('uniqueItems'); + expect(error.getInstancePath()).to.equal('/disablePublicKeys'); + }); + }); + + describe('publicKeysDisabledAt', async () => { + it('should be used only with disablePublicKeys', async () => { + delete rawStateTransition.disablePublicKeys; + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + expect(error.getKeyword()).to.equal('dependentRequired'); + expect(error.params.missingProperty).to.equal('disablePublicKeys'); + }); + + it('should be integer', async () => { + rawStateTransition.publicKeysDisabledAt = 1.1; + rawStateTransition.disablePublicKeys = [0]; + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/publicKeysDisabledAt'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be >= 0', async () => { + rawStateTransition.publicKeysDisabledAt = -1; + rawStateTransition.disablePublicKeys = [0]; + + const result = await validateIdentityUpdateTransitionBasic( + rawStateTransition, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/publicKeysDisabledAt'); + expect(error.getKeyword()).to.equal('minimum'); + }); + }); + + it('should return valid result', async () => { + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expect(result.isValid()).to.be.true(); + + expect(validatePublicKeysMock) + .to.be.calledOnceWithExactly( + rawStateTransition.addPublicKeys, + ); + }); + + it('should have either addPublicKeys or disablePublicKeys', async () => { + delete rawStateTransition.disablePublicKeys; + delete rawStateTransition.addPublicKeys; + delete rawStateTransition.publicKeysDisabledAt; + + const result = await validateIdentityUpdateTransitionBasic(rawStateTransition); + + expectJsonSchemaError(result, 3); + + const [addPublicKeysError, disablePublicKeysError, error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('anyOf'); + + expect(disablePublicKeysError.schemaPath).to.equal('#/anyOf/1/required'); + expect(disablePublicKeysError.getKeyword()).to.equal('required'); + + expect(addPublicKeysError.schemaPath).to.equal('#/anyOf/0/required'); + expect(addPublicKeysError.getKeyword()).to.equal('required'); + }); +}); diff --git a/packages/js-dpp/test/integration/identity/stateTransition/IdentityUpdateTransition/validation/state/validateIdentityUpdateTransitionStateFactory.spec.js b/packages/js-dpp/test/integration/identity/stateTransition/IdentityUpdateTransition/validation/state/validateIdentityUpdateTransitionStateFactory.spec.js new file mode 100644 index 00000000000..89a2ac28023 --- /dev/null +++ b/packages/js-dpp/test/integration/identity/stateTransition/IdentityUpdateTransition/validation/state/validateIdentityUpdateTransitionStateFactory.spec.js @@ -0,0 +1,286 @@ +const createStateRepositoryMock = require('../../../../../../../lib/test/mocks/createStateRepositoryMock'); +const validateIdentityUpdateTransitionStateFactory = require('../../../../../../../lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validateIdentityUpdateTransitionStateFactory'); +const getIdentityUpdateTransitionFixture = require('../../../../../../../lib/test/fixtures/getIdentityUpdateTransitionFixture'); +const IdentityPublicKey = require('../../../../../../../lib/identity/IdentityPublicKey'); +const getIdentityFixture = require('../../../../../../../lib/test/fixtures/getIdentityFixture'); +const ValidationResult = require('../../../../../../../lib/validation/ValidationResult'); +const { expectValidationError } = require('../../../../../../../lib/test/expect/expectError'); +const InvalidIdentityRevisionError = require('../../../../../../../lib/errors/consensus/state/identity/InvalidIdentityRevisionError'); +const IdentityPublicKeyIsReadOnlyError = require('../../../../../../../lib/errors/consensus/state/identity/IdentityPublicKeyIsReadOnlyError'); +const IdentityPublicKeyDisabledAtWindowViolationError = require('../../../../../../../lib/errors/consensus/state/identity/IdentityPublicKeyDisabledAtWindowViolationError'); +const InvalidIdentityPublicKeyIdError = require('../../../../../../../lib/errors/consensus/state/identity/InvalidIdentityPublicKeyIdError'); +const SomeConsensusError = require('../../../../../../../lib/test/mocks/SomeConsensusError'); +const StateTransitionExecutionContext = require('../../../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('validateIdentityUpdateTransitionStateFactory', () => { + let validateIdentityUpdateTransitionState; + let stateRepositoryMock; + let stateTransition; + let identity; + let validatePublicKeysMock; + let fakeTime; + let blockTime; + let validateRequiredPurposeAndSecurityLevelMock; + let executionContext; + + beforeEach(async function beforeEach() { + identity = getIdentityFixture(); + validatePublicKeysMock = this.sinonSandbox.stub() + .returns(new ValidationResult()); + validateRequiredPurposeAndSecurityLevelMock = this.sinonSandbox.stub() + .returns(new ValidationResult()); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchIdentity.resolves(identity); + + blockTime = new Date().getTime() / 1000; + + const abciHeader = { + time: { + seconds: blockTime, + }, + }; + + stateRepositoryMock.fetchLatestPlatformBlockHeader.resolves(abciHeader); + + validateIdentityUpdateTransitionState = validateIdentityUpdateTransitionStateFactory( + stateRepositoryMock, + validatePublicKeysMock, + validateRequiredPurposeAndSecurityLevelMock, + ); + + stateTransition = getIdentityUpdateTransitionFixture(); + stateTransition.setRevision(identity.getRevision() + 1); + stateTransition.setPublicKeyIdsToDisable(undefined); + stateTransition.setPublicKeysDisabledAt(undefined); + + executionContext = new StateTransitionExecutionContext(); + + stateTransition.setExecutionContext(executionContext); + + const privateKey = '9b67f852093bc61cea0eeca38599dbfba0de28574d2ed9b99d10d33dc1bde7b2'; + + await stateTransition.signByPrivateKey(privateKey, IdentityPublicKey.TYPES.ECDSA_SECP256K1); + + fakeTime = this.sinonSandbox.useFakeTimers(new Date()); + }); + + afterEach(() => { + fakeTime.reset(); + }); + + it('should return InvalidIdentityRevisionError if new revision is not incremented by 1', async () => { + stateTransition.setRevision(identity.getRevision() + 2); + + const result = await validateIdentityUpdateTransitionState(stateTransition); + + expectValidationError(result, InvalidIdentityRevisionError); + + const [error] = result.getErrors(); + expect(error.getIdentityId()).to.deep.equal(stateTransition.getIdentityId()); + expect(error.getCurrentRevision()).to.equal(identity.getRevision()); + }); + + it('should return IdentityPublicKeyIsReadOnlyError if disabling public key is readOnly', async () => { + identity.getPublicKeyById(0).setReadOnly(true); + stateTransition.setPublicKeyIdsToDisable([0]); + + const result = await validateIdentityUpdateTransitionState(stateTransition); + + expectValidationError(result, IdentityPublicKeyIsReadOnlyError); + + const [error] = result.getErrors(); + expect(error.getPublicKeyIndex()).to.equal(0); + }); + + it('should return invalid result if disabledAt has violated time window', async () => { + stateTransition.setPublicKeyIdsToDisable([1]); + stateTransition.setPublicKeysDisabledAt(new Date()); + + const timeWindowStart = new Date(blockTime * 1000); + timeWindowStart.setMinutes( + timeWindowStart.getMinutes() - 5, + ); + + const timeWindowEnd = new Date(blockTime * 1000); + timeWindowEnd.setMinutes( + timeWindowEnd.getMinutes() + 5, + ); + + stateTransition.publicKeysDisabledAt.setMinutes( + stateTransition.publicKeysDisabledAt.getMinutes() - 6, + ); + + const result = await validateIdentityUpdateTransitionState(stateTransition); + + expectValidationError(result, IdentityPublicKeyDisabledAtWindowViolationError); + + const [error] = result.getErrors(); + expect(error.getDisabledAt()).to.deep.equal(stateTransition.publicKeysDisabledAt); + expect(error.getTimeWindowStart()).to.deep.equal(timeWindowStart); + expect(error.getTimeWindowEnd()).to.deep.equal(timeWindowEnd); + }); + + it('should throw InvalidIdentityPublicKeyIdError if identity does not contain public key with disabling ID', async () => { + stateTransition.setPublicKeyIdsToDisable([3]); + stateTransition.setPublicKeysDisabledAt(new Date()); + + const result = await validateIdentityUpdateTransitionState(stateTransition); + + expectValidationError(result, InvalidIdentityPublicKeyIdError); + + const [error] = result.getErrors(); + expect(error.getId()).to.equal(3); + }); + + it('should pass when disabling public key', async () => { + stateTransition.setPublicKeyIdsToDisable([1]); + stateTransition.setPublicKeysDisabledAt(new Date()); + stateTransition.setPublicKeysToAdd(undefined); + + const result = await validateIdentityUpdateTransitionState(stateTransition); + + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchIdentity) + .to.be.calledOnceWithExactly( + stateTransition.getIdentityId(), + executionContext, + ); + + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader) + .to.be.calledOnce(); + }); + + it('should pass when adding public key', async () => { + stateTransition.setPublicKeyIdsToDisable(undefined); + stateTransition.setPublicKeysDisabledAt(undefined); + + const result = await validateIdentityUpdateTransitionState(stateTransition); + + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchIdentity) + .to.be.calledOnceWithExactly( + stateTransition.getIdentityId(), + executionContext, + ); + + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader) + .to.not.be.called(); + + expect(validatePublicKeysMock).to.be.calledOnceWithExactly( + [...identity.getPublicKeys(), ...stateTransition.getPublicKeysToAdd()].map( + (pk) => pk.toObject(), + ), + ); + }); + + it('should pass when both adding and disabling public keys', async () => { + stateTransition.setPublicKeyIdsToDisable([1]); + stateTransition.setPublicKeysDisabledAt(new Date()); + + const result = await validateIdentityUpdateTransitionState(stateTransition); + + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchIdentity) + .to.be.calledOnceWithExactly( + stateTransition.getIdentityId(), + executionContext, + ); + + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader) + .to.be.calledOnce(); + + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader) + .to.be.calledOnce(); + }); + + it('should validate purpose and security level', async () => { + const now = new Date(); + + stateTransition.setPublicKeyIdsToDisable([1]); + stateTransition.setPublicKeysDisabledAt(now); + + const publicKeysError = new SomeConsensusError('test'); + + validateRequiredPurposeAndSecurityLevelMock.onCall(0) + .returns(new ValidationResult([publicKeysError])); + + const result = await validateIdentityUpdateTransitionState(stateTransition); + + expectValidationError(result, SomeConsensusError); + + expect(validateRequiredPurposeAndSecurityLevelMock).to.be.calledOnceWithExactly([ + identity.getPublicKeys()[0].toObject(), + { ...identity.getPublicKeys()[1].toObject(), disabledAt: now.getTime() }, + stateTransition.getPublicKeysToAdd()[0].toObject(), + ]); + }); + + it('should validate public keys to add', async () => { + const publicKeysError = new SomeConsensusError('test'); + + validatePublicKeysMock.onCall(0).returns(new ValidationResult([publicKeysError])); + + const result = await validateIdentityUpdateTransitionState(stateTransition); + + expectValidationError(result, SomeConsensusError); + + expect(validatePublicKeysMock).to.be.calledOnceWithExactly( + [...identity.getPublicKeys(), ...stateTransition.getPublicKeysToAdd()] + .map((pk) => pk.toObject()), + ); + }); + + it('should validate resulting identity public keys', async () => { + const publicKeysError = new SomeConsensusError('test'); + + validatePublicKeysMock.returns(new ValidationResult([publicKeysError])); + + const result = await validateIdentityUpdateTransitionState(stateTransition); + + expectValidationError(result, SomeConsensusError); + + expect(validatePublicKeysMock).to.be.calledOnce(); + + const publicKeys = [...identity.getPublicKeys(), ...stateTransition.getPublicKeysToAdd()]; + + expect(validatePublicKeysMock).to.be.calledWithExactly( + publicKeys.map((pk) => pk.toObject()), + ); + }); + + it('should return valid result on dry run', async () => { + stateTransition.setPublicKeyIdsToDisable([3]); + stateTransition.setPublicKeysDisabledAt(new Date()); + + const publicKeysError = new SomeConsensusError('test'); + + validateRequiredPurposeAndSecurityLevelMock.onCall(0) + .returns(new ValidationResult([publicKeysError])); + + stateTransition.getExecutionContext().enableDryRun(); + + const result = await validateIdentityUpdateTransitionState(stateTransition); + + stateTransition.getExecutionContext().disableDryRun(); + + expect(result.isValid()).to.be.true(); + + expect(validatePublicKeysMock).to.not.be.called(); + expect(validateRequiredPurposeAndSecurityLevelMock).to.not.be.called(); + expect(stateRepositoryMock.fetchIdentity) + .to.be.calledOnceWithExactly( + stateTransition.getIdentityId(), + executionContext, + ); + + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader) + .to.not.be.called(); + + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader) + .to.not.be.called(); + }); +}); diff --git a/packages/js-dpp/test/integration/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.spec.js b/packages/js-dpp/test/integration/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.spec.js new file mode 100644 index 00000000000..8aa62cbf0ad --- /dev/null +++ b/packages/js-dpp/test/integration/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.spec.js @@ -0,0 +1,66 @@ +const validatePublicKeys = require( + '../../../../../../../lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState', +); +const { expectValidationError } = require('../../../../../../../lib/test/expect/expectError'); +const DuplicatedIdentityPublicKeyIdError = require('../../../../../../../lib/errors/consensus/state/identity/DuplicatedIdentityPublicKeyIdError'); +const DuplicatedIdentityPublicKeyError = require('../../../../../../../lib/errors/consensus/state/identity/DuplicatedIdentityPublicKeyError'); +const identitySchema = require('../../../../../../../schema/identity/identity.json'); +const MaxIdentityPublicKeyLimitReachedError = require('../../../../../../../lib/errors/consensus/state/identity/MaxIdentityPublicKeyLimitReachedError'); +const getIdentityFixture = require('../../../../../../../lib/test/fixtures/getIdentityFixture'); + +describe('validatePublicKeysState', () => { + let rawPublicKeys; + + beforeEach(() => { + ({ publicKeys: rawPublicKeys } = getIdentityFixture().toObject()); + }); + + it('should return invalid result if there are duplicate key ids', () => { + rawPublicKeys[1].id = rawPublicKeys[0].id; + + const result = validatePublicKeys(rawPublicKeys); + + expectValidationError(result, DuplicatedIdentityPublicKeyIdError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4022); + expect(error.getDuplicatedIds()).to.deep.equal([rawPublicKeys[1].id]); + }); + + it('should return invalid result if there are duplicate keys', () => { + rawPublicKeys[1].data = rawPublicKeys[0].data; + + const result = validatePublicKeys(rawPublicKeys); + + expectValidationError(result, DuplicatedIdentityPublicKeyError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4021); + expect(error.getDuplicatedPublicKeysIds()).to.deep.equal([rawPublicKeys[1].id]); + }); + + it('should pass valid public keys', () => { + const result = validatePublicKeys(rawPublicKeys); + + expect(result.isValid()).to.be.true(); + }); + + it('should return invalid result if number of public keys is bigger than 32', () => { + const { maxItems } = identitySchema.properties.publicKeys; + const numToAdd = maxItems - rawPublicKeys.length; + + for (let i = 0; i <= numToAdd; ++i) { + rawPublicKeys.push(rawPublicKeys[0]); + } + + const result = validatePublicKeys(rawPublicKeys); + + expectValidationError(result, MaxIdentityPublicKeyLimitReachedError); + + const [error] = result.getErrors(); + expect(error.getCode()).to.equal(4020); + expect(error.geMaxItems()).to.equal(maxItems); + }); +}); diff --git a/packages/js-dpp/test/integration/identity/stateTransition/assetLockProof/chain/validateChainAssetLockProofStructureFactory.spec.js b/packages/js-dpp/test/integration/identity/stateTransition/assetLockProof/chain/validateChainAssetLockProofStructureFactory.spec.js new file mode 100644 index 00000000000..b908a2ff6f5 --- /dev/null +++ b/packages/js-dpp/test/integration/identity/stateTransition/assetLockProof/chain/validateChainAssetLockProofStructureFactory.spec.js @@ -0,0 +1,415 @@ +const { getRE2Class } = require('@dashevo/wasm-re2'); + +const createAjv = require('../../../../../../lib/ajv/createAjv'); + +const getChainAssetLockFixture = require('../../../../../../lib/test/fixtures/getChainAssetLockProofFixture'); +const JsonSchemaValidator = require('../../../../../../lib/validation/JsonSchemaValidator'); +const createStateRepositoryMock = require('../../../../../../lib/test/mocks/createStateRepositoryMock'); + +const { expectValidationError, expectJsonSchemaError } = require( + '../../../../../../lib/test/expect/expectError', +); + +const validateChainAssetLockProofStructureFactory = require('../../../../../../lib/identity/stateTransition/assetLockProof/chain/validateChainAssetLockProofStructureFactory'); +const ValidationResult = require('../../../../../../lib/validation/ValidationResult'); +const IdentityAssetLockTransactionIsNotFoundError = require('../../../../../../lib/errors/consensus/basic/identity/IdentityAssetLockTransactionIsNotFoundError'); +const InvalidAssetLockProofCoreChainHeightError = require('../../../../../../lib/errors/consensus/basic/identity/InvalidAssetLockProofCoreChainHeightError'); +const InvalidAssetLockProofTransactionHeightError = require('../../../../../../lib/errors/consensus/basic/identity/InvalidAssetLockProofTransactionHeightError'); +const SomeConsensusError = require('../../../../../../lib/test/mocks/SomeConsensusError'); +const StateTransitionExecutionContext = require('../../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('validateChainAssetLockProofStructureFactory', () => { + let rawProof; + let stateRepositoryMock; + let validateChainAssetLockProofStructure; + let jsonSchemaValidator; + let validateAssetLockTransactionMock; + let validateAssetLockTransactionResult; + let publicKeyHash; + let rawTransaction; + let transactionHash; + let executionContext; + + beforeEach(async function beforeEach() { + rawTransaction = '030000000137feb5676d0851337ea3c9a992496aab7a0b3eee60aeeb9774000b7f4bababa5000000006b483045022100d91557de37645c641b948c6cd03b4ae3791a63a650db3e2fee1dcf5185d1b10402200e8bd410bf516ca61715867666d31e44495428ce5c1090bf2294a829ebcfa4ef0121025c3cc7fbfc52f710c941497fd01876c189171ea227458f501afcb38a297d65b4ffffffff021027000000000000166a14152073ca2300a86b510fa2f123d3ea7da3af68dcf77cb0090a0000001976a914152073ca2300a86b510fa2f123d3ea7da3af68dc88ac00000000'; + transactionHash = '6e200d059fb567ba19e92f5c2dcd3dde522fd4e0a50af223752db16158dabb1d'; + + const assetLock = getChainAssetLockFixture(); + + rawProof = assetLock.toObject(); + + const RE2 = await getRE2Class(); + const ajv = createAjv(RE2); + + jsonSchemaValidator = new JsonSchemaValidator(ajv); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + + stateRepositoryMock.fetchLatestPlatformBlockHeader.resolves({ + coreChainLockedHeight: 42, + }); + + stateRepositoryMock.fetchTransaction.resolves({ + data: Buffer.from(rawTransaction, 'hex'), + height: 42, + }); + + executionContext = new StateTransitionExecutionContext(); + + publicKeyHash = Buffer.from('152073ca2300a86b510fa2f123d3ea7da3af68dc', 'hex'); + + validateAssetLockTransactionResult = new ValidationResult(); + validateAssetLockTransactionResult.setData({ + publicKeyHash, + }); + validateAssetLockTransactionMock = this.sinonSandbox.stub().resolves( + validateAssetLockTransactionResult, + ); + + validateChainAssetLockProofStructure = validateChainAssetLockProofStructureFactory( + jsonSchemaValidator, + stateRepositoryMock, + validateAssetLockTransactionMock, + ); + }); + + describe('type', () => { + it('should be present', async () => { + delete rawProof.type; + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('type'); + + expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.not.be.called(); + }); + + it('should be equal to 1', async () => { + rawProof.type = -1; + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/type'); + expect(error.getKeyword()).to.equal('const'); + + expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.not.be.called(); + }); + }); + + describe('coreChainLockedHeight', () => { + it('should be preset', async () => { + delete rawProof.coreChainLockedHeight; + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('coreChainLockedHeight'); + + expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.not.be.called(); + }); + + it('should be an integer', async () => { + rawProof.coreChainLockedHeight = 1.5; + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/coreChainLockedHeight'); + expect(error.getKeyword()).to.equal('type'); + + expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.not.be.called(); + }); + + it('should be a number', async () => { + rawProof.coreChainLockedHeight = '42'; + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/coreChainLockedHeight'); + expect(error.getKeyword()).to.equal('type'); + + expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.not.be.called(); + }); + + it('should be greater than 0', async () => { + rawProof.coreChainLockedHeight = 0; + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/coreChainLockedHeight'); + expect(error.getKeyword()).to.equal('minimum'); + + expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.not.be.called(); + }); + + it('should be less than 4294967296', async () => { + rawProof.coreChainLockedHeight = 4294967296; + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/coreChainLockedHeight'); + expect(error.getKeyword()).to.equal('maximum'); + + expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.not.be.called(); + }); + + it('should be less or equal to consensus core height', async () => { + stateRepositoryMock.fetchLatestPlatformBlockHeader.resolves({ + coreChainLockedHeight: 41, + }); + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectValidationError(result, InvalidAssetLockProofCoreChainHeightError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1035); + expect(error.getProofCoreChainLockedHeight()).to.equal(42); + expect(error.getCurrentCoreChainLockedHeight()).to.equal(41); + }); + }); + + describe('outPoint', () => { + it('should be present', async () => { + delete rawProof.outPoint; + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('outPoint'); + + expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.not.be.called(); + }); + + it('should be a byte array', async () => { + rawProof.outPoint = new Array(36).fill('string'); + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.instancePath).to.equal('/outPoint/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + + expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.not.be.called(); + }); + + it('should not be shorter than 36 bytes', async () => { + rawProof.outPoint = Buffer.alloc(35); + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/outPoint'); + expect(error.getKeyword()).to.equal('minItems'); + + expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.not.be.called(); + }); + + it('should not be longer than 36 bytes', async () => { + rawProof.outPoint = Buffer.alloc(37); + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/outPoint'); + expect(error.getKeyword()).to.equal('maxItems'); + + expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.not.be.called(); + }); + + it('should point to existing transaction', async () => { + stateRepositoryMock.fetchTransaction.resolves(null); + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectValidationError(result, IdentityAssetLockTransactionIsNotFoundError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1032); + expect(error.getTransactionId()).to.deep.equal( + Buffer.from(transactionHash, 'hex'), + ); + + expect(stateRepositoryMock.fetchTransaction).to.be.calledOnceWithExactly( + transactionHash, + executionContext, + ); + expect(validateAssetLockTransactionMock).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.be.calledOnce(); + }); + + it('should point to valid transaction', async () => { + const consensusError = new SomeConsensusError('test'); + + validateAssetLockTransactionResult.addError(consensusError); + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectValidationError(result, SomeConsensusError); + + const [error] = result.getErrors(); + + expect(error).to.deep.equal(consensusError); + }); + + it('should point to transaction from block lower than core chain locked height', async () => { + rawProof.coreChainLockedHeight = 41; + stateRepositoryMock.fetchLatestPlatformBlockHeader.resolves({ + coreChainLockedHeight: 41, + }); + + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expectValidationError(result, InvalidAssetLockProofTransactionHeightError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1036); + expect(error.getProofCoreChainLockedHeight()).to.equal(41); + expect(error.getTransactionHeight()).to.equal(42); + + expect(stateRepositoryMock.fetchTransaction).to.be.calledOnceWithExactly( + transactionHash, + executionContext, + ); + expect(validateAssetLockTransactionMock).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.be.calledOnce(); + }); + }); + + it('should return valid result', async () => { + const result = await validateChainAssetLockProofStructure( + rawProof, + executionContext, + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + expect(result.getData()).to.deep.equal(publicKeyHash); + + expect(stateRepositoryMock.fetchTransaction).to.be.calledOnceWithExactly( + transactionHash, + executionContext, + ); + expect(validateAssetLockTransactionMock).to.be.calledOnceWithExactly( + Buffer.from(rawTransaction, 'hex'), + 0, + executionContext, + ); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.be.calledOnce(); + }); +}); diff --git a/packages/js-dpp/test/integration/identity/stateTransition/assetLockProof/fetchAssetLockTransactionOutputFactory.spec.js b/packages/js-dpp/test/integration/identity/stateTransition/assetLockProof/fetchAssetLockTransactionOutputFactory.spec.js new file mode 100644 index 00000000000..a691cd5931a --- /dev/null +++ b/packages/js-dpp/test/integration/identity/stateTransition/assetLockProof/fetchAssetLockTransactionOutputFactory.spec.js @@ -0,0 +1,134 @@ +const { Transaction, Script } = require('@dashevo/dashcore-lib'); +const Output = require('@dashevo/dashcore-lib/lib/transaction/output'); + +const fetchAssetLockTransactionOutputFactory = require('../../../../../lib/identity/stateTransition/assetLockProof/fetchAssetLockTransactionOutputFactory'); +const getChainAssetLockFixture = require('../../../../../lib/test/fixtures/getChainAssetLockProofFixture'); +const getInstantAssetLockProofFixture = require('../../../../../lib/test/fixtures/getInstantAssetLockProofFixture'); +const createStateRepositoryMock = require('../../../../../lib/test/mocks/createStateRepositoryMock'); + +const UnknownAssetLockProofError = require('../../../../../lib/identity/errors/UnknownAssetLockProofTypeError'); +const AssetLockTransactionIsNotFoundError = require('../../../../../lib/identity/errors/AssetLockTransactionIsNotFoundError'); +const StateTransitionExecutionContext = require('../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('fetchAssetLockTransactionOutputFactory', () => { + let fetchAssetLockTransactionOutput; + let stateRepositoryMock; + let executionContext; + + beforeEach(function beforeEach() { + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + fetchAssetLockTransactionOutput = fetchAssetLockTransactionOutputFactory(stateRepositoryMock); + + executionContext = new StateTransitionExecutionContext(); + }); + + describe('InstantAssetLockProof', () => { + let assetLockProofFixture; + + beforeEach(() => { + assetLockProofFixture = getInstantAssetLockProofFixture(); + }); + + it('should return asset lock output', async () => { + const assetLockTransactionOutput = await fetchAssetLockTransactionOutput( + assetLockProofFixture, + executionContext, + ); + + expect(assetLockTransactionOutput).to.deep.equal(assetLockProofFixture.getOutput()); + expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); + }); + }); + + describe('ChainAssetLockProof', () => { + let assetLockProofFixture; + let output; + let transactionHash; + + beforeEach(() => { + const rawTransaction = '030000000137feb5676d0851337ea3c9a992496aab7a0b3eee60aeeb9774000b7f4bababa5000000006b483045022100d91557de37645c641b948c6cd03b4ae3791a63a650db3e2fee1dcf5185d1b10402200e8bd410bf516ca61715867666d31e44495428ce5c1090bf2294a829ebcfa4ef0121025c3cc7fbfc52f710c941497fd01876c189171ea227458f501afcb38a297d65b4ffffffff021027000000000000166a14152073ca2300a86b510fa2f123d3ea7da3af68dcf77cb0090a0000001976a914152073ca2300a86b510fa2f123d3ea7da3af68dc88ac00000000'; + assetLockProofFixture = getChainAssetLockFixture(); + stateRepositoryMock.fetchTransaction.resolves({ + data: Buffer.from(rawTransaction, 'hex'), + height: 42, + }); + + const transaction = new Transaction(rawTransaction); + ([output] = transaction.outputs); + + const outPoint = Transaction.parseOutPointBuffer(assetLockProofFixture.getOutPoint()); + ({ transactionHash } = outPoint); + }); + + it('should fetch output from state repository', async () => { + const assetLockTransactionOutput = await fetchAssetLockTransactionOutput( + assetLockProofFixture, + executionContext, + ); + + expect(assetLockTransactionOutput).to.deep.equal(output); + + expect(stateRepositoryMock.fetchTransaction).to.be.calledOnceWithExactly( + transactionHash, + executionContext, + ); + }); + + it('should throw IdentityAssetLockTransactionIsNotFoundError when transaction is not found', async () => { + stateRepositoryMock.fetchTransaction.resolves(null); + + try { + await fetchAssetLockTransactionOutput( + assetLockProofFixture, + executionContext, + ); + + expect.fail('should throw IdentityAssetLockTransactionIsNotFoundError'); + } catch (e) { + expect(e).to.be.an.instanceOf(AssetLockTransactionIsNotFoundError); + expect(e.getTransactionId()).to.deep.equal(transactionHash); + } + }); + + it('should return mocked output on dry run', async () => { + executionContext.enableDryRun(); + + const result = await fetchAssetLockTransactionOutput( + assetLockProofFixture, + executionContext, + ); + + executionContext.disableDryRun(); + + expect(result).to.deep.equal(new Output({ + satoshis: 1000, + script: new Script(), + })); + + expect(stateRepositoryMock.fetchTransaction).to.be.calledOnceWithExactly( + transactionHash, + executionContext, + ); + }); + }); + + it('should throw UnknownAssetLockProofError for unknown assetLockProof', async function it() { + const type = 666; + + const assetLockProofFixture = { + getType: this.sinonSandbox.stub().returns(type), + }; + + try { + await fetchAssetLockTransactionOutput( + assetLockProofFixture, + executionContext, + ); + + expect.fail('should throw UnknownAssetLockProofError'); + } catch (e) { + expect(e).to.be.an.instanceOf(UnknownAssetLockProofError); + expect(e.getType()).to.equal(type); + } + }); +}); diff --git a/packages/js-dpp/test/integration/identity/stateTransition/assetLockProof/instant/validateInstantAssetLockProofStructureFactory.spec.js b/packages/js-dpp/test/integration/identity/stateTransition/assetLockProof/instant/validateInstantAssetLockProofStructureFactory.spec.js new file mode 100644 index 00000000000..f513684f7f1 --- /dev/null +++ b/packages/js-dpp/test/integration/identity/stateTransition/assetLockProof/instant/validateInstantAssetLockProofStructureFactory.spec.js @@ -0,0 +1,401 @@ +const { getRE2Class } = require('@dashevo/wasm-re2'); + +const DashCoreLib = require('@dashevo/dashcore-lib'); + +const createAjv = require('../../../../../../lib/ajv/createAjv'); + +const getInstantAssetLockFixture = require('../../../../../../lib/test/fixtures/getInstantAssetLockProofFixture'); +const JsonSchemaValidator = require('../../../../../../lib/validation/JsonSchemaValidator'); +const createStateRepositoryMock = require('../../../../../../lib/test/mocks/createStateRepositoryMock'); +const InvalidIdentityAssetLockProofError = require('../../../../../../lib/errors/consensus/basic/identity/InvalidInstantAssetLockProofError'); +const IdentityAssetLockProofLockedTransactionMismatchError = require('../../../../../../lib/errors/consensus/basic/identity/IdentityAssetLockProofLockedTransactionMismatchError'); +const InvalidIdentityAssetLockProofSignatureError = require('../../../../../../lib/errors/consensus/basic/identity/InvalidInstantAssetLockProofSignatureError'); + +const { expectValidationError, expectJsonSchemaError } = require( + '../../../../../../lib/test/expect/expectError', +); + +const ValidationResult = require('../../../../../../lib/validation/ValidationResult'); +const InvalidIdentityAssetLockTransactionError = require('../../../../../../lib/errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionError'); +const validateInstantAssetLockProofStructureFactory = require('../../../../../../lib/identity/stateTransition/assetLockProof/instant/validateInstantAssetLockProofStructureFactory'); + +describe('validateInstantAssetLockProofStructureFactory', () => { + let rawProof; + let transaction; + let stateRepositoryMock; + let instantLockFromBufferMock; + let instantLockMock; + let validateInstantAssetLockProofStructure; + let jsonSchemaValidator; + let validateAssetLockTransactionResult; + let publicKeyHash; + let validateAssetLockTransactionMock; + + beforeEach(async function beforeEach() { + const assetLock = getInstantAssetLockFixture(); + transaction = assetLock.getTransaction(); + + rawProof = assetLock.toObject(); + + const RE2 = await getRE2Class(); + const ajv = createAjv(RE2); + + jsonSchemaValidator = new JsonSchemaValidator(ajv); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.verifyInstantLock.resolves(true); + + instantLockMock = { + txid: transaction.id, + verify: this.sinonSandbox.stub().resolves(true), + }; + + instantLockFromBufferMock = this.sinonSandbox.stub(DashCoreLib.InstantLock, 'fromBuffer').returns(instantLockMock); + + publicKeyHash = Buffer.from('152073ca2300a86b510fa2f123d3ea7da3af68dc', 'hex'); + + validateAssetLockTransactionResult = new ValidationResult(); + validateAssetLockTransactionResult.setData({ + publicKeyHash, + transaction, + }); + validateAssetLockTransactionMock = this.sinonSandbox.stub().resolves( + validateAssetLockTransactionResult, + ); + + validateInstantAssetLockProofStructure = validateInstantAssetLockProofStructureFactory( + jsonSchemaValidator, + stateRepositoryMock, + validateAssetLockTransactionMock, + ); + }); + + afterEach(() => { + instantLockFromBufferMock.restore(); + }); + + describe('type', () => { + it('should be present', async () => { + delete rawProof.type; + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('type'); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + }); + + it('should be equal to 0', async () => { + rawProof.type = -1; + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/type'); + expect(error.getKeyword()).to.equal('const'); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + }); + }); + + describe('instantLock', () => { + it('should be present', async () => { + delete rawProof.instantLock; + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('instantLock'); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + }); + + it('should be a byte array', async () => { + rawProof.instantLock = new Array(165).fill('string'); + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/instantLock/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + }); + + it('should not be shorter than 160 bytes', async () => { + rawProof.instantLock = Buffer.alloc(159); + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/instantLock'); + expect(error.getKeyword()).to.equal('minItems'); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + }); + + it('should not be longer than 100 Kb', async () => { + rawProof.instantLock = Buffer.alloc(100001); + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/instantLock'); + expect(error.getKeyword()).to.equal('maxItems'); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + }); + + it('should be valid', async () => { + const instantLockError = new Error('something is wrong'); + + instantLockFromBufferMock.throws(instantLockError); + + rawProof.instantLock = Buffer.alloc(200); + + const result = await validateInstantAssetLockProofStructure(rawProof); + expectValidationError(result, InvalidIdentityAssetLockProofError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1041); + expect(error.getValidationError()).to.equal(instantLockError); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + }); + + it('should lock the same transaction', async () => { + const txId = Buffer.alloc(32); + instantLockMock.txid = txId.toString('hex'); + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectValidationError(result, IdentityAssetLockProofLockedTransactionMismatchError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1031); + expect(error.getInstantLockTransactionId()).to.deep.equal(txId); + expect(error.getAssetLockTransactionId()).to.deep.equal(Buffer.from(transaction.id, 'hex')); + + // expect(stateRepositoryMock.verifyInstantLock).to.be.calledOnce(); + expect(instantLockMock.verify).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.be.calledOnce(); + }); + + it('should have valid signature', async () => { + stateRepositoryMock.verifyInstantLock.resolves(false); + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectValidationError(result, InvalidIdentityAssetLockProofSignatureError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1042); + + expect(stateRepositoryMock.verifyInstantLock).to.be.calledOnce(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + }); + }); + + describe('transaction', () => { + it('should be present', async () => { + delete rawProof.transaction; + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('transaction'); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + }); + + it('should be a byte array', async () => { + rawProof.transaction = new Array(65).fill('string'); + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/transaction/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + }); + + it('should not be shorter than 1 byte', async () => { + rawProof.transaction = Buffer.alloc(0); + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/transaction'); + expect(error.getKeyword()).to.equal('minItems'); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + }); + + it('should not be longer than 100 Kb', async () => { + rawProof.transaction = Buffer.alloc(100001); + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/transaction'); + expect(error.getKeyword()).to.equal('maxItems'); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.not.be.called(); + }); + + it('should should be valid', async () => { + const validationError = new Error('parsing failed'); + + const consensusError = new InvalidIdentityAssetLockTransactionError(validationError.message); + + consensusError.setValidationError(validationError); + + validateAssetLockTransactionResult.addError(consensusError); + validateAssetLockTransactionMock.resolves(validateAssetLockTransactionResult); + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectValidationError(result, InvalidIdentityAssetLockTransactionError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1038); + + expect(error).to.equal(consensusError); + + expect(error.getValidationError()).to.equal(validationError); + + // expect(stateRepositoryMock.verifyInstantLock).to.be.calledOnce(); + expect(instantLockMock.verify).to.not.be.called(); + expect(validateAssetLockTransactionMock).to.be.calledOnce(); + }); + }); + + describe('outputIndex', () => { + it('should be present', async () => { + delete rawProof.outputIndex; + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('outputIndex'); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + }); + + it('should be an integer', async () => { + rawProof.outputIndex = 1.1; + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectJsonSchemaError(result, 1); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/outputIndex'); + expect(error.getKeyword()).to.equal('type'); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + }); + + it('should not be less than 0', async () => { + rawProof.outputIndex = -1; + + const result = await validateInstantAssetLockProofStructure(rawProof); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/outputIndex'); + expect(error.getKeyword()).to.equal('minimum'); + + expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); + expect(instantLockMock.verify).to.not.be.called(); + }); + }); + + it('should return valid result', async () => { + const result = await validateInstantAssetLockProofStructure(rawProof); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + expect(result.getData()).to.deep.equal(publicKeyHash); + + // expect(stateRepositoryMock.verifyInstantLock).to.be.calledOnce(); + }); +}); diff --git a/packages/js-dpp/test/integration/identity/stateTransition/assetLockProof/validateAssetLockTransactionFactory.spec.js b/packages/js-dpp/test/integration/identity/stateTransition/assetLockProof/validateAssetLockTransactionFactory.spec.js new file mode 100644 index 00000000000..054401786d0 --- /dev/null +++ b/packages/js-dpp/test/integration/identity/stateTransition/assetLockProof/validateAssetLockTransactionFactory.spec.js @@ -0,0 +1,205 @@ +const { Transaction } = require('@dashevo/dashcore-lib'); + +const DashCoreLib = require('@dashevo/dashcore-lib'); +const validateAssetLockTransactionFactory = require('../../../../../lib/identity/stateTransition/assetLockProof/validateAssetLockTransactionFactory'); +const createStateRepositoryMock = require('../../../../../lib/test/mocks/createStateRepositoryMock'); + +const ValidationResult = require('../../../../../lib/validation/ValidationResult'); + +const { expectValidationError } = require('../../../../../lib/test/expect/expectError'); + +const InvalidIdentityAssetLockTransactionError = require('../../../../../lib/errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionError'); +const IdentityAssetLockTransactionOutputNotFoundError = require('../../../../../lib/errors/consensus/basic/identity/IdentityAssetLockTransactionOutputNotFoundError'); +const IdentityAssetLockTransactionOutPointAlreadyExistsError = require('../../../../../lib/errors/consensus/basic/identity/IdentityAssetLockTransactionOutPointAlreadyExistsError'); +const InvalidIdentityAssetLockTransactionOutputError = require('../../../../../lib/errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionOutputError'); +const InvalidAssetLockTransactionOutputReturnSizeError = require('../../../../../lib/errors/consensus/basic/identity/InvalidAssetLockTransactionOutputReturnSizeError'); +const StateTransitionExecutionContext = require('../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('validateAssetLockTransactionFactory', () => { + let stateRepositoryMock; + let validateAssetLockTransaction; + let rawTransaction; + let outputIndex; + let transactionInstance; + let transactionMock; + let executionContext; + + beforeEach(function beforeEach() { + rawTransaction = '030000000137feb5676d0851337ea3c9a992496aab7a0b3eee60aeeb9774000b7f4bababa5000000006b483045022100d91557de37645c641b948c6cd03b4ae3791a63a650db3e2fee1dcf5185d1b10402200e8bd410bf516ca61715867666d31e44495428ce5c1090bf2294a829ebcfa4ef0121025c3cc7fbfc52f710c941497fd01876c189171ea227458f501afcb38a297d65b4ffffffff021027000000000000166a14152073ca2300a86b510fa2f123d3ea7da3af68dcf77cb0090a0000001976a914152073ca2300a86b510fa2f123d3ea7da3af68dc88ac00000000'; + outputIndex = 0; + transactionInstance = new Transaction(rawTransaction); + + executionContext = new StateTransitionExecutionContext(); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + + stateRepositoryMock.isAssetLockTransactionOutPointAlreadyUsed.returns(false); + + validateAssetLockTransaction = validateAssetLockTransactionFactory(stateRepositoryMock); + }); + + afterEach(() => { + if (transactionMock) { + transactionMock.restore(); + } + }); + + it('should be valid transaction', async () => { + rawTransaction = '030000000137feb5676d085133'; + + validateAssetLockTransaction = validateAssetLockTransactionFactory(stateRepositoryMock); + + const result = await validateAssetLockTransaction( + rawTransaction, + outputIndex, + executionContext, + ); + + expectValidationError(result, InvalidIdentityAssetLockTransactionError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1038); + expect(error.getValidationError()).to.be.instanceOf(Error); + + expect(result.getData()).to.be.undefined(); + expect(stateRepositoryMock.isAssetLockTransactionOutPointAlreadyUsed).to.not.be.called(); + }); + + it('should return IdentityAssetLockTransactionOutputNotFoundError on invalid outputIndex', async () => { + outputIndex = 42; + + const result = await validateAssetLockTransaction( + rawTransaction, + outputIndex, + executionContext, + ); + + expectValidationError(result, IdentityAssetLockTransactionOutputNotFoundError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1034); + expect(error.getOutputIndex()).to.equal(outputIndex); + + expect(result.getData()).to.be.undefined(); + expect(stateRepositoryMock.isAssetLockTransactionOutPointAlreadyUsed).to.not.be.called(); + }); + + it('should point to output with OR_RETURN', async function it() { + const isDataOut = this.sinonSandbox.stub().returns(false); + + const stubInstance = this.sinonSandbox.createStubInstance(Transaction); + + stubInstance.outputs = [{ + script: { + isDataOut, + }, + }]; + + stubInstance.getOutPointBuffer.returns(transactionInstance.getOutPointBuffer(outputIndex)); + + transactionMock = this.sinonSandbox.stub(DashCoreLib, 'Transaction').returns( + stubInstance, + ); + + const result = await validateAssetLockTransaction( + rawTransaction, + outputIndex, + executionContext, + ); + + expectValidationError(result, InvalidIdentityAssetLockTransactionOutputError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1039); + expect(error.getOutputIndex()).to.equal(outputIndex); + }); + + it('should contain valid public key hash', async function it() { + const isDataOut = this.sinonSandbox.stub().returns(true); + const getData = this.sinonSandbox.stub().returns(Buffer.alloc(0)); + + const stubInstance = this.sinonSandbox.createStubInstance(Transaction); + stubInstance.outputs = [{ + script: { + isDataOut, + getData, + }, + }]; + stubInstance.getOutPointBuffer.returns(transactionInstance.getOutPointBuffer(outputIndex)); + + transactionMock = this.sinonSandbox.stub(DashCoreLib, 'Transaction').returns( + stubInstance, + ); + + const result = await validateAssetLockTransaction( + rawTransaction, + outputIndex, + executionContext, + ); + + expectValidationError(result, InvalidAssetLockTransactionOutputReturnSizeError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1037); + expect(error.getOutputIndex()).to.equal(outputIndex); + }); + + it('should return IdentityAssetLockTransactionOutPointAlreadyExistsError if outPoint was already used', async () => { + stateRepositoryMock.isAssetLockTransactionOutPointAlreadyUsed.returns(true); + + const result = await validateAssetLockTransaction( + rawTransaction, + outputIndex, + executionContext, + ); + + expectValidationError(result, IdentityAssetLockTransactionOutPointAlreadyExistsError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1033); + + const transaction = new Transaction(rawTransaction); + + expect(Buffer.isBuffer(error.getTransactionId())).to.be.true(); + expect(error.getTransactionId()).to.deep.equal(Buffer.from(transaction.id, 'hex')); + expect(error.getOutputIndex()).to.deep.equal(outputIndex); + + expect(result.getData()).to.be.undefined(); + expect(stateRepositoryMock.isAssetLockTransactionOutPointAlreadyUsed) + .to.be.calledOnceWithExactly( + transaction.getOutPointBuffer(outputIndex), + executionContext, + ); + }); + + it('should return valid result', async () => { + const result = await validateAssetLockTransaction( + rawTransaction, + outputIndex, + executionContext, + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + + expect(result.isValid()).to.be.true(); + + const initialTransaction = new Transaction(rawTransaction); + const initialPublicKeyHash = initialTransaction.outputs[outputIndex].script.getData(); + + expect(stateRepositoryMock.isAssetLockTransactionOutPointAlreadyUsed) + .to.be.calledOnceWithExactly( + initialTransaction.getOutPointBuffer(outputIndex), + executionContext, + ); + + const { transaction, publicKeyHash } = result.getData(); + expect(publicKeyHash).to.deep.equal(initialPublicKeyHash); + expect(transaction).to.be.an.instanceOf(Transaction); + expect(transaction.toJSON()).to.deep.equal(initialTransaction.toJSON()); + }); +}); diff --git a/packages/js-dpp/test/integration/identity/stateTransition/validatePublicKeySignaturesFactory.spec.js b/packages/js-dpp/test/integration/identity/stateTransition/validatePublicKeySignaturesFactory.spec.js new file mode 100644 index 00000000000..41d5caa98eb --- /dev/null +++ b/packages/js-dpp/test/integration/identity/stateTransition/validatePublicKeySignaturesFactory.spec.js @@ -0,0 +1,127 @@ +const { PrivateKey, crypto: { Hash } } = require('@dashevo/dashcore-lib'); + +const crypto = require('crypto'); + +const getIdentityCreateTransitionFixture = require('../../../../lib/test/fixtures/getIdentityCreateTransitionFixture'); +const IdentityPublicKey = require('../../../../lib/identity/IdentityPublicKey'); +const BlsSignatures = require('../../../../lib/bls/bls'); +const validatePublicKeySignaturesFactory = require('../../../../lib/identity/stateTransition/validatePublicKeySignaturesFactory'); +const ValidationResult = require('../../../../lib/validation/ValidationResult'); +const { expectValidationError } = require('../../../../lib/test/expect/expectError'); +const InvalidIdentityKeySignatureError = require('../../../../lib/errors/consensus/basic/identity/InvalidIdentityKeySignatureError'); + +describe('validatePublicKeySignaturesFactory', () => { + let identityCreateTransition; + let rawIdentityCreateTransition; + let validatePublicKeySignatures; + + beforeEach(async function beforeEach() { + identityCreateTransition = getIdentityCreateTransitionFixture(); + + const privateKey1 = new PrivateKey(); + const publicKey1 = privateKey1.toPublicKey(); + + const identityPublicKey1 = new IdentityPublicKey({ + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: publicKey1.toBuffer(), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + }); + + const privateKey2 = new PrivateKey(); + const publicKey2 = privateKey2.toPublicKey(); + + const identityPublicKey2 = new IdentityPublicKey({ + id: 1, + type: IdentityPublicKey.TYPES.ECDSA_HASH160, + data: Hash.sha256ripemd160(publicKey2.toBuffer()), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.CRITICAL, + readOnly: false, + }); + + const { PrivateKey: BlsPrivateKey } = await BlsSignatures.getInstance(); + + const randomBytes = new Uint8Array(crypto.randomBytes(256)); + const privateKey3 = BlsPrivateKey.fromBytes(randomBytes, true); + // blsPrivateKeyHex = Buffer.from(blsPrivateKey.serialize()).toString('hex'); + const publicKey3 = privateKey3.getPublicKey(); + + const identityPublicKey3 = new IdentityPublicKey({ + id: 2, + type: IdentityPublicKey.TYPES.BLS12_381, + data: Buffer.from(publicKey3.serialize()), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.CRITICAL, + readOnly: false, + }); + + identityCreateTransition.setPublicKeys([ + identityPublicKey1, + identityPublicKey2, + identityPublicKey3, + ]); + + await identityCreateTransition.signByPrivateKey( + privateKey1, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + const signature1 = identityCreateTransition.getSignature(); + + await identityCreateTransition.signByPrivateKey( + privateKey2, + IdentityPublicKey.TYPES.ECDSA_HASH160, + ); + + const signature2 = identityCreateTransition.getSignature(); + + await identityCreateTransition.signByPrivateKey( + Buffer.from(privateKey3.serialize()), + IdentityPublicKey.TYPES.BLS12_381, + ); + + const signature3 = identityCreateTransition.getSignature(); + + identityPublicKey1.setSignature(signature1); + identityPublicKey2.setSignature(signature2); + identityPublicKey3.setSignature(signature3); + + rawIdentityCreateTransition = identityCreateTransition.toObject(); + + const createStateTransitionMock = this.sinonSandbox.stub().resolves(identityCreateTransition); + + validatePublicKeySignatures = validatePublicKeySignaturesFactory(createStateTransitionMock); + }); + + it('should return InvalidIdentityKeySignatureError if signature is not valid', async () => { + const rawPublicKey2 = rawIdentityCreateTransition.publicKeys[1]; + + rawPublicKey2.signature = crypto.randomBytes(65); + + const result = await validatePublicKeySignatures( + rawIdentityCreateTransition, + rawIdentityCreateTransition.publicKeys, + identityCreateTransition.getExecutionContext(), + ); + + expectValidationError(result, InvalidIdentityKeySignatureError); + + const error = result.getFirstError(); + + expect(error.getPublicKeyId()).to.equals(rawPublicKey2.id); + }); + + it('should return valid result', async () => { + const result = await validatePublicKeySignatures( + rawIdentityCreateTransition, + rawIdentityCreateTransition.publicKeys, + identityCreateTransition.getExecutionContext(), + ); + + expect(result).to.be.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/integration/identity/validation/validateIdentityFactory.spec.js b/packages/js-dpp/test/integration/identity/validation/validateIdentityFactory.spec.js new file mode 100644 index 00000000000..0896f342788 --- /dev/null +++ b/packages/js-dpp/test/integration/identity/validation/validateIdentityFactory.spec.js @@ -0,0 +1,370 @@ +const { getRE2Class } = require('@dashevo/wasm-re2'); + +const createAjv = require('../../../../lib/ajv/createAjv'); + +const getIdentityFixture = require('../../../../lib/test/fixtures/getIdentityFixture'); + +const JsonSchemaValidator = require( + '../../../../lib/validation/JsonSchemaValidator', +); + +const { expectValidationError, expectJsonSchemaError } = require( + '../../../../lib/test/expect/expectError', +); + +const validateIdentityFactory = require( + '../../../../lib/identity/validation/validateIdentityFactory', +); + +const JsonSchemaError = require( + '../../../../lib/errors/consensus/basic/JsonSchemaError', +); + +const ValidationResult = require('../../../../lib/validation/ValidationResult'); +const SomeConsensusError = require('../../../../lib/test/mocks/SomeConsensusError'); + +describe('validateIdentityFactory', () => { + let rawIdentity; + let validateIdentity; + let identity; + let validatePublicKeysMock; + let validateProtocolVersionMock; + + beforeEach(async function beforeEach() { + const RE2 = await getRE2Class(); + const ajv = createAjv(RE2); + + const schemaValidator = new JsonSchemaValidator(ajv); + + validatePublicKeysMock = this.sinonSandbox.stub().returns(new ValidationResult()); + + validateProtocolVersionMock = this.sinonSandbox.stub().returns(new ValidationResult()); + + validateIdentity = validateIdentityFactory( + schemaValidator, + validatePublicKeysMock, + validateProtocolVersionMock, + ); + + identity = getIdentityFixture(); + + rawIdentity = identity.toObject(); + }); + + describe('protocolVersion', () => { + it('should be present', async () => { + delete rawIdentity.protocolVersion; + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('protocolVersion'); + }); + + it('should be an integer', async () => { + rawIdentity.protocolVersion = '1'; + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/protocolVersion'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be valid', async () => { + rawIdentity.protocolVersion = -1; + + const protocolVersionError = new SomeConsensusError('test'); + const protocolVersionResult = new ValidationResult([ + protocolVersionError, + ]); + + validateProtocolVersionMock.returns(protocolVersionResult); + + const result = validateIdentity(rawIdentity); + + expectValidationError(result, SomeConsensusError); + + const [error] = result.getErrors(); + + expect(error).to.equal(protocolVersionError); + + expect(validateProtocolVersionMock).to.be.calledOnceWith( + rawIdentity.protocolVersion, + ); + }); + }); + + describe('id', () => { + it('should be present', () => { + rawIdentity.id = undefined; + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getParams().missingProperty).to.equal('id'); + expect(error.getKeyword()).to.equal('required'); + + expect(validatePublicKeysMock).to.not.be.called(); + }); + + it('should be a byte array', () => { + rawIdentity.id = new Array(32).fill('string'); + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/id/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + + expect(validatePublicKeysMock).to.not.be.called(); + }); + + it('should not be less than 32 bytes', () => { + rawIdentity.id = Buffer.alloc(31); + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('minItems'); + expect(error.getInstancePath()).to.equal('/id'); + + expect(validatePublicKeysMock).to.not.be.called(); + }); + + it('should not be more than 32 bytes', () => { + rawIdentity.id = Buffer.alloc(33); + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('maxItems'); + expect(error.getInstancePath()).to.equal('/id'); + + expect(validatePublicKeysMock).to.not.be.called(); + }); + }); + + describe('balance', () => { + it('should be present', async () => { + rawIdentity.balance = undefined; + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getParams().missingProperty).to.equal('balance'); + expect(error.getKeyword()).to.equal('required'); + + expect(validatePublicKeysMock).to.not.be.called(); + }); + + it('should be an integer', async () => { + rawIdentity.balance = 1.2; + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('type'); + expect(error.getInstancePath()).to.equal('/balance'); + + expect(validatePublicKeysMock).to.not.be.called(); + }); + + it('should be greater or equal 0', async () => { + rawIdentity.balance = -1; + + let result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('minimum'); + expect(error.getInstancePath()).to.equal('/balance'); + + expect(validatePublicKeysMock).to.not.be.called(); + + rawIdentity.balance = 0; + + result = validateIdentity(rawIdentity); + + expect(result.isValid()).to.be.true(); + }); + }); + + describe('publicKeys', () => { + it('should be present', () => { + rawIdentity.publicKeys = undefined; + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getParams().missingProperty).to.equal('publicKeys'); + expect(error.getKeyword()).to.equal('required'); + + expect(validatePublicKeysMock).to.not.be.called(); + }); + + it('should be an array', () => { + rawIdentity.publicKeys = 1; + + const result = validateIdentity(rawIdentity); + + expectValidationError(result, JsonSchemaError, 1); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1005); + expect(error.getInstancePath()).to.equal('/publicKeys'); + expect(error.getKeyword()).to.equal('type'); + + expect(validatePublicKeysMock).to.not.be.called(); + }); + + it('should not be empty', () => { + rawIdentity.publicKeys = []; + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('minItems'); + expect(error.getInstancePath()).to.equal('/publicKeys'); + + expect(validatePublicKeysMock).to.not.be.called(); + }); + + it('should be unique', async () => { + rawIdentity.publicKeys.push(rawIdentity.publicKeys[0]); + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('uniqueItems'); + expect(error.getInstancePath()).to.equal('/publicKeys'); + + expect(validatePublicKeysMock).to.not.be.called(); + }); + + it('should throw an error if publicKeys have more than 100 keys', () => { + const [key] = rawIdentity.publicKeys; + + rawIdentity.publicKeys = []; + for (let i = 0; i < 101; i++) { + rawIdentity.publicKeys.push(key); + } + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('maxItems'); + expect(error.getInstancePath()).to.equal('/publicKeys'); + + expect(validatePublicKeysMock).to.not.be.called(); + }); + }); + + describe('revision', () => { + it('should be present', async () => { + rawIdentity.revision = undefined; + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getParams().missingProperty).to.equal('revision'); + expect(error.getKeyword()).to.equal('required'); + + expect(validatePublicKeysMock).to.not.be.called(); + }); + + it('should be an integer', async () => { + rawIdentity.revision = 1.2; + + const result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('type'); + expect(error.getInstancePath()).to.equal('/revision'); + + expect(validatePublicKeysMock).to.not.be.called(); + }); + + it('should be greater or equal 0', async () => { + rawIdentity.revision = -1; + + let result = validateIdentity(rawIdentity); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getKeyword()).to.equal('minimum'); + expect(error.getInstancePath()).to.equal('/revision'); + + expect(validatePublicKeysMock).to.not.be.called(); + + rawIdentity.revision = 0; + + result = validateIdentity(rawIdentity); + + expect(result.isValid()).to.be.true(); + }); + }); + + it('should return valid result if a raw identity is valid', () => { + const result = validateIdentity(rawIdentity); + + expect(validatePublicKeysMock).to.be.calledOnceWithExactly(rawIdentity.publicKeys); + + expect(result.isValid()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/integration/identity/validation/validatePublicKeysFactory.spec.js b/packages/js-dpp/test/integration/identity/validation/validatePublicKeysFactory.spec.js new file mode 100644 index 00000000000..eaf646bb6e9 --- /dev/null +++ b/packages/js-dpp/test/integration/identity/validation/validatePublicKeysFactory.spec.js @@ -0,0 +1,497 @@ +const { getRE2Class } = require('@dashevo/wasm-re2'); + +const crypto = require('crypto'); + +const createAjv = require('../../../../lib/ajv/createAjv'); + +const JsonSchemaValidator = require( + '../../../../lib/validation/JsonSchemaValidator', +); + +const validatePublicKeysFactory = require( + '../../../../lib/identity/validation/validatePublicKeysFactory', +); + +const getIdentityFixture = require('../../../../lib/test/fixtures/getIdentityFixture'); + +const { + expectValidationError, + expectJsonSchemaError, +} = require('../../../../lib/test/expect/expectError'); + +const DuplicatedIdentityPublicKeyError = require( + '../../../../lib/errors/consensus/basic/identity/DuplicatedIdentityPublicKeyError', +); +const DuplicatedIdentityPublicKeyIdError = require( + '../../../../lib/errors/consensus/basic/identity/DuplicatedIdentityPublicKeyIdError', +); + +const InvalidIdentityPublicKeyDataError = require( + '../../../../lib/errors/consensus/basic/identity/InvalidIdentityPublicKeyDataError', +); + +const InvalidIdentityPublicKeySecurityLevelError = require( + '../../../../lib/errors/consensus/basic/identity/InvalidIdentityPublicKeySecurityLevelError', +); + +const IdentityPublicKey = require( + '../../../../lib/identity/IdentityPublicKey', +); +const BlsSignatures = require('../../../../lib/bls/bls'); + +const identityPublicKeySchema = require('../../../../schema/identity/publicKey.json'); +const stateTransitionPublicKeySchema = require('../../../../schema/identity/stateTransition/publicKey.json'); + +describe('validatePublicKeysFactory', () => { + let rawPublicKeys; + let validatePublicKeys; + let validator; + let bls; + + beforeEach(async () => { + ({ publicKeys: rawPublicKeys } = getIdentityFixture().toObject()); + + const RE2 = await getRE2Class(); + const ajv = createAjv(RE2); + bls = await BlsSignatures.getInstance(); + + validator = new JsonSchemaValidator(ajv); + + validatePublicKeys = validatePublicKeysFactory( + validator, + identityPublicKeySchema, + bls, + ); + }); + + describe('id', () => { + it('should be present', () => { + delete rawPublicKeys[1].id; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('id'); + }); + + it('should be a number', () => { + rawPublicKeys[1].id = 'string'; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/id'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be an integer', () => { + rawPublicKeys[1].id = 1.1; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/id'); + expect(error.getKeyword()).to.equal('type'); + }); + + it('should be greater or equal to one', () => { + rawPublicKeys[1].id = -1; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/id'); + expect(error.getKeyword()).to.equal('minimum'); + }); + }); + + describe('type', () => { + it('should be present', () => { + delete rawPublicKeys[1].type; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/data'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be a number', () => { + rawPublicKeys[1].type = 'string'; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/type'); + expect(error.getKeyword()).to.equal('type'); + }); + }); + + describe('data', () => { + it('should be present', () => { + delete rawPublicKeys[1].data; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('data'); + }); + + it('should be a byte array', () => { + rawPublicKeys[1].data = new Array(33).fill('string'); + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/data/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + describe('ECDSA_SECP256K1', () => { + it('should be no less than 33 bytes', () => { + rawPublicKeys[1].data = Buffer.alloc(32); + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/data'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be no longer than 33 bytes', () => { + rawPublicKeys[1].data = Buffer.alloc(34); + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/data'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + + describe('BLS12_381', () => { + it('should be no less than 48 bytes', () => { + rawPublicKeys[1].data = Buffer.alloc(47); + rawPublicKeys[1].type = 1; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/data'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be no longer than 48 bytes', () => { + rawPublicKeys[1].data = Buffer.alloc(49); + rawPublicKeys[1].type = 1; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/data'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + + describe('ECDSA_HASH160', () => { + it('should be no less than 20 bytes', () => { + rawPublicKeys[1].data = Buffer.alloc(19); + rawPublicKeys[1].type = 2; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/data'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be no longer than 20 bytes', () => { + rawPublicKeys[1].data = Buffer.alloc(21); + rawPublicKeys[1].type = 2; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/data'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + + describe('BIP13_SCRIPT_HASH', () => { + it('should be no less than 20 bytes', () => { + rawPublicKeys[1].data = Buffer.alloc(19); + rawPublicKeys[1].type = 3; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/data'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be no longer than 20 bytes', () => { + rawPublicKeys[1].data = Buffer.alloc(21); + rawPublicKeys[1].type = 3; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.getInstancePath()).to.equal('/data'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + }); + + it('should return invalid result if there are duplicate key ids', () => { + rawPublicKeys[1].id = rawPublicKeys[0].id; + + const result = validatePublicKeys(rawPublicKeys); + + expectValidationError(result, DuplicatedIdentityPublicKeyIdError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1030); + expect(error.getDuplicatedIds()).to.deep.equal([rawPublicKeys[1].id]); + }); + + it('should return invalid result if there are duplicate keys', () => { + rawPublicKeys[1].data = rawPublicKeys[0].data; + + const result = validatePublicKeys(rawPublicKeys); + + expectValidationError(result, DuplicatedIdentityPublicKeyError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1029); + expect(error.getDuplicatedPublicKeysIds()).to.deep.equal([rawPublicKeys[1].id]); + }); + + it('should return invalid result if key data is not a valid DER', () => { + rawPublicKeys[1].data = Buffer.alloc(33); + + const result = validatePublicKeys(rawPublicKeys); + + expectValidationError(result, InvalidIdentityPublicKeyDataError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1040); + expect(error.getPublicKeyId()).to.deep.equal(rawPublicKeys[1].id); + expect(error.getValidationError()).to.be.instanceOf(TypeError); + expect(error.getValidationError().message).to.equal('Invalid DER format public key'); + }); + + it('should return invalid result if key has an invalid combination of purpose and security level', () => { + rawPublicKeys[1].purpose = IdentityPublicKey.PURPOSES.ENCRYPTION; + rawPublicKeys[1].securityLevel = IdentityPublicKey.SECURITY_LEVELS.MASTER; + + const result = validatePublicKeys(rawPublicKeys); + + expectValidationError(result, InvalidIdentityPublicKeySecurityLevelError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1047); + expect(error.getPublicKeyId()).to.deep.equal(rawPublicKeys[1].id); + expect(error.getPublicKeySecurityLevel()).to.be.equal(rawPublicKeys[1].securityLevel); + expect(error.getPublicKeyPurpose()).to.equal(rawPublicKeys[1].purpose); + }); + + it('should pass valid public keys', () => { + const result = validatePublicKeys(rawPublicKeys); + + expect(result.isValid()).to.be.true(); + }); + + it('should pass valid BLS12_381 public key', () => { + rawPublicKeys = [{ + id: 0, + type: IdentityPublicKey.TYPES.BLS12_381, + purpose: 0, + securityLevel: 0, + readOnly: true, + data: Buffer.from('01fac99ca2c8f39c286717c213e190aba4b7af76db320ec43f479b7d9a2012313a0ae59ca576edf801444bc694686694', 'hex'), + }]; + + const result = validatePublicKeys(rawPublicKeys); + + expect(result.isValid()).to.be.true(); + }); + + it('should pass valid ECDSA_HASH160 public key', () => { + rawPublicKeys = [{ + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_HASH160, + purpose: 0, + securityLevel: 0, + readOnly: true, + data: Buffer.from('6086389d3fa4773aa950b8de18c5bd6d8f2b73bc', 'hex'), + }]; + + const result = validatePublicKeys(rawPublicKeys); + + expect(result.isValid()).to.be.true(); + }); + + it('should return invalid result if BLS12_381 public key is invalid', () => { + rawPublicKeys = [{ + id: 0, + type: IdentityPublicKey.TYPES.BLS12_381, + purpose: 0, + securityLevel: 0, + readOnly: true, + data: Buffer.from('11fac99ca2c8f39c286717c213e190aba4b7af76db320ec43f479b7d9a2012313a0ae59ca576edf801444bc694686694', 'hex'), + }]; + + const result = validatePublicKeys(rawPublicKeys); + + expectValidationError(result, InvalidIdentityPublicKeyDataError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1040); + expect(error.getPublicKeyId()).to.deep.equal(rawPublicKeys[0].id); + expect(error.getValidationError()).to.be.instanceOf(TypeError); + expect(error.getValidationError().message).to.equal('Invalid public key'); + }); + + describe('Identity Schema', () => { + beforeEach(() => { + rawPublicKeys[0].disabledAt = new Date().getTime(); + }); + + describe('disabledAt', () => { + it('should be an integer'); + + it('should be greater than 0'); + }); + }); + + describe('State Transition Schema', () => { + beforeEach(() => { + validatePublicKeys = validatePublicKeysFactory( + validator, + stateTransitionPublicKeySchema, + bls, + ); + + rawPublicKeys.forEach((rawPublicKey) => { + // eslint-disable-next-line no-param-reassign + rawPublicKey.signature = crypto.randomBytes(65); + }); + }); + + describe('signature', () => { + it('should be present', () => { + delete rawPublicKeys[0].signature; + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal(''); + expect(error.getKeyword()).to.equal('required'); + expect(error.getParams().missingProperty).to.equal('signature'); + }); + + it('should be a byte array', async () => { + rawPublicKeys[0].signature = new Array(65).fill('string'); + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result, 2); + + const [error, byteArrayError] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature/0'); + expect(error.getKeyword()).to.equal('type'); + + expect(byteArrayError.getKeyword()).to.equal('byteArray'); + }); + + it('should be not shorter than 65 bytes', () => { + rawPublicKeys[0].signature = Buffer.alloc(64); + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature'); + expect(error.getKeyword()).to.equal('minItems'); + }); + + it('should be not longer than 65 bytes', () => { + rawPublicKeys[0].signature = Buffer.alloc(66); + + const result = validatePublicKeys(rawPublicKeys); + + expectJsonSchemaError(result); + + const [error] = result.getErrors(); + + expect(error.instancePath).to.equal('/signature'); + expect(error.getKeyword()).to.equal('maxItems'); + }); + }); + }); +}); diff --git a/packages/js-dpp/test/integration/stateTransition/AbstractStateTransitionIdentitySigned.spec.js b/packages/js-dpp/test/integration/stateTransition/AbstractStateTransitionIdentitySigned.spec.js new file mode 100644 index 00000000000..ec7d36b181f --- /dev/null +++ b/packages/js-dpp/test/integration/stateTransition/AbstractStateTransitionIdentitySigned.spec.js @@ -0,0 +1,507 @@ +const { PrivateKey, crypto: { Hash } } = require('@dashevo/dashcore-lib'); + +const crypto = require('crypto'); +const calculateStateTransitionFee = require('../../../lib/stateTransition/fee/calculateStateTransitionFee'); + +const StateTransitionMock = require('../../../lib/test/mocks/StateTransitionMock'); +const IdentityPublicKey = require('../../../lib/identity/IdentityPublicKey'); +const InvalidSignatureTypeError = require('../../../lib/stateTransition/errors/InvalidIdentityPublicKeyTypeError'); +const InvalidSignaturePublicKeyError = require('../../../lib/stateTransition/errors/InvalidSignaturePublicKeyError'); +const PublicKeySecurityLevelNotMetError = require('../../../lib/stateTransition/errors/PublicKeySecurityLevelNotMetError'); +const WrongPublicKeyPurposeError = require('../../../lib/stateTransition/errors/WrongPublicKeyPurposeError'); +const StateTransitionIsNotSignedError = require('../../../lib/stateTransition/errors/StateTransitionIsNotSignedError'); +const PublicKeyMismatchError = require('../../../lib/stateTransition/errors/PublicKeyMismatchError'); +const BlsSignatures = require('../../../lib/bls/bls'); +const PublicKeyIsDisabledError = require('../../../lib/stateTransition/errors/PublicKeyIsDisabledError'); +const InvalidSignaturePublicKeySecurityLevelError = require('../../../lib/stateTransition/errors/InvalidSignaturePublicKeySecurityLevelError'); +const stateTransitionTypes = require('../../../lib/stateTransition/stateTransitionTypes'); + +describe('AbstractStateTransitionIdentitySigned', () => { + let stateTransition; + let protocolVersion; + let privateKeyHex; + let privateKeyWIF; + let publicKeyId; + let identityPublicKey; + let blsPrivateKey; + let blsPrivateKeyHex; + let blsInstance; + + beforeEach(async () => { + const privateKeyModel = new PrivateKey(); + privateKeyWIF = privateKeyModel.toWIF(); + privateKeyHex = privateKeyModel.toBuffer().toString('hex'); + const publicKey = privateKeyModel.toPublicKey().toBuffer(); + publicKeyId = 1; + + protocolVersion = 1; + + stateTransition = new StateTransitionMock({ + protocolVersion, + }); + + blsInstance = await BlsSignatures.getInstance(); + const { + PrivateKey: BlsPrivateKey, + } = blsInstance; + + const randomBytes = new Uint8Array(crypto.randomBytes(256)); + blsPrivateKey = BlsPrivateKey.fromBytes(randomBytes, true); + blsPrivateKeyHex = Buffer.from(blsPrivateKey.serialize()).toString('hex'); + + identityPublicKey = new IdentityPublicKey() + .setId(publicKeyId) + .setType(IdentityPublicKey.TYPES.ECDSA_SECP256K1) + .setData(publicKey) + .setSecurityLevel(IdentityPublicKey.SECURITY_LEVELS.HIGH) + .setPurpose(IdentityPublicKey.PURPOSES.AUTHENTICATION); + }); + + describe('#toObject', () => { + it('should return raw state transition', () => { + const rawStateTransition = stateTransition.toObject(); + + expect(rawStateTransition).to.deep.equal({ + protocolVersion, + signature: undefined, + signaturePublicKeyId: undefined, + type: 0, + }); + }); + + it('should return raw state transition without signature ', () => { + const rawStateTransition = stateTransition.toObject({ skipSignature: true }); + + expect(rawStateTransition).to.deep.equal({ + protocolVersion, + type: 0, + }); + }); + }); + + describe('#toJSON', () => { + it('should return state transition as JSON', () => { + const jsonStateTransition = stateTransition.toJSON(); + + expect(jsonStateTransition).to.deep.equal({ + signaturePublicKeyId: undefined, + signature: undefined, + protocolVersion, + type: 0, + }); + }); + }); + + describe('#hash', () => { + it.skip('should return serialized hash', () => { + const hash = stateTransition.hash(); + + expect(hash).to.be.equal('9177fcb220cbab84abcb9ebd5c048facf47f455f6826bf37d97a9908e09fcafd'); + }); + }); + + describe('#toBuffer', () => { + it.skip('should return serialized data', () => { + const serializedData = stateTransition.toBuffer(); + + expect(serializedData.toString('hex')).to.be.equal('a4647479706500697369676e6174757265f66f70726f746f636f6c56657273696f6ef6747369676e61747572655075626c69634b65794964f6'); + }); + + it.skip('should return serialized data without signature data', () => { + const serializedData = stateTransition.toBuffer({ skipSignature: true }); + + expect(serializedData.toString('hex')).to.be.equal('a26474797065006f70726f746f636f6c56657273696f6ef6'); + }); + }); + + describe('#getSignaturePublicKeyId', () => { + it('should return public key ID', async () => { + await stateTransition.sign(identityPublicKey, privateKeyHex); + + const keyId = stateTransition.getSignaturePublicKeyId(); + expect(keyId).to.be.equal(publicKeyId); + }); + }); + + describe('#sign', () => { + it('should sign data and validate signature with private key in hex format', async () => { + await stateTransition.sign(identityPublicKey, privateKeyHex); + + expect(stateTransition.signature).to.be.an.instanceOf(Buffer); + + const isValid = await stateTransition.verifySignature(identityPublicKey); + + expect(isValid).to.be.true(); + }); + + it('should sign data and validate signature with private key in buffer format', async () => { + await stateTransition.sign(identityPublicKey, privateKeyWIF); + + expect(stateTransition.signature).to.be.an.instanceOf(Buffer); + + const isValid = await stateTransition.verifySignature(identityPublicKey); + + expect(isValid).to.be.true(); + }); + + it('should sign data and validate signature with ECDSA_HASH160 identityPublicKey', async () => { + identityPublicKey.setType(IdentityPublicKey.TYPES.ECDSA_HASH160); + identityPublicKey.setData( + Hash.sha256ripemd160(identityPublicKey.getData()), + ); + + await stateTransition.sign(identityPublicKey, privateKeyHex); + + expect(stateTransition.signature).to.be.an.instanceOf(Buffer); + + const isValid = await stateTransition.verifySignature(identityPublicKey); + + expect(isValid).to.be.true(); + }); + + it('should throw an error if we try to sign with wrong public key', async () => { + const publicKey = new PrivateKey() + .toPublicKey() + .toBuffer(); + + identityPublicKey.setData(publicKey); + + try { + await stateTransition.sign(identityPublicKey, privateKeyHex); + + expect.fail('Should throw InvalidSignaturePublicKeyError'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidSignaturePublicKeyError); + expect(e.getSignaturePublicKey()).to.deep.equal(identityPublicKey.getData()); + } + }); + + it('should throw InvalidSignatureTypeError if signature type is not equal ECDSA', async () => { + identityPublicKey.setType(30000); + + try { + await stateTransition.sign(identityPublicKey, privateKeyHex); + + expect.fail('Should throw InvalidSignatureTypeError'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidSignatureTypeError); + expect(e.getPublicKeyType()).to.be.equal(identityPublicKey.getType()); + } + }); + + it('should throw an error if the key security level is not met', async function it() { + stateTransition.getRequiredKeySecurityLevel = this.sinonSandbox + .stub() + .returns(IdentityPublicKey.SECURITY_LEVELS.MASTER); + + identityPublicKey.setSecurityLevel(IdentityPublicKey.SECURITY_LEVELS.MEDIUM); + + try { + await stateTransition.sign(identityPublicKey, privateKeyHex); + + expect.fail('Should throw PublicKeySecurityLevelNotMetError'); + } catch (e) { + expect(e).to.be.instanceOf(PublicKeySecurityLevelNotMetError); + expect(e.getPublicKeySecurityLevel()) + .to.be.deep.equal(identityPublicKey.getSecurityLevel()); + expect(e.getKeySecurityLevelRequirement()) + .to.be.deep.equal(IdentityPublicKey.SECURITY_LEVELS.HIGH); + } + }); + + it('should throw an error if the key purpose is not authentication', async () => { + identityPublicKey.setPurpose(IdentityPublicKey.PURPOSES.ENCRYPTION); + + try { + await stateTransition.sign(identityPublicKey, privateKeyHex); + + expect.fail('Should throw WrongPublicKeyPurposeError'); + } catch (e) { + expect(e).to.be.instanceOf(WrongPublicKeyPurposeError); + expect(e.getPublicKeyPurpose()).to.be.deep.equal(identityPublicKey.getPurpose()); + expect(e.getKeyPurposeRequirement()) + .to.be.deep.equal(IdentityPublicKey.PURPOSES.AUTHENTICATION); + } + }); + + it('should sign data and validate signature with BLS12_381 identityPublicKey', async () => { + identityPublicKey.setType(IdentityPublicKey.TYPES.BLS12_381); + identityPublicKey.setData(Buffer.from(blsPrivateKey.getPublicKey().serialize())); + + await stateTransition.sign(identityPublicKey, blsPrivateKeyHex); + + expect(stateTransition.signature).to.be.an.instanceOf(Buffer); + + const isValid = await stateTransition.verifySignature(identityPublicKey); + + expect(isValid).to.be.true(); + }); + }); + + describe('#signByPrivateKey', () => { + it('should sign and validate with private key', async () => { + privateKeyHex = '9b67f852093bc61cea0eeca38599dbfba0de28574d2ed9b99d10d33dc1bde7b2'; + + await stateTransition.signByPrivateKey( + privateKeyHex, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + expect(stateTransition.signature).to.be.an.instanceOf(Buffer); + }); + + it('should sign and validate with BLS private key', async () => { + identityPublicKey.setType(IdentityPublicKey.TYPES.BLS12_381); + identityPublicKey.setData(Buffer.from(blsPrivateKey.getPublicKey().serialize())); + + await stateTransition.signByPrivateKey(blsPrivateKeyHex, IdentityPublicKey.TYPES.BLS12_381); + + expect(stateTransition.signature).to.be.an.instanceOf(Buffer); + + const isValid = await stateTransition.verifyBLSSignatureByPublicKey( + blsPrivateKey.getPublicKey(), + ); + + expect(isValid).to.be.true(); + }); + }); + + describe('#verifySignature', () => { + it('should validate signature', async () => { + await stateTransition.sign(identityPublicKey, privateKeyHex); + + expect(stateTransition.signature).to.be.an.instanceOf(Buffer); + + const isValid = await stateTransition.verifySignature(identityPublicKey); + + expect(isValid).to.be.true(); + }); + + it('should throw an StateTransitionIsNotSignedError error if transition is not signed', async () => { + try { + await stateTransition.verifySignature(identityPublicKey); + + expect.fail('should throw StateTransitionIsNotSignedError'); + } catch (e) { + expect(e).to.be.instanceOf(StateTransitionIsNotSignedError); + expect(e.getStateTransition()).to.equal(stateTransition); + } + }); + + it('should throw an PublicKeyMismatchError error if public key id not equals public key id in state transition', async () => { + await stateTransition.sign(identityPublicKey, privateKeyHex); + + identityPublicKey.setId(identityPublicKey.getId() + 1); + + try { + await stateTransition.verifySignature(identityPublicKey); + + expect.fail('should throw PublicKeyMismatchError'); + } catch (e) { + expect(e).to.be.instanceOf(PublicKeyMismatchError); + expect(e.getPublicKey()).to.equal(identityPublicKey); + } + }); + + it('should not verify signature with wrong public key', async () => { + await stateTransition.sign(identityPublicKey, privateKeyHex); + const publicKey = new PrivateKey() + .toPublicKey() + .toBuffer(); + + identityPublicKey.setData(publicKey); + + const isValid = await stateTransition.verifySignature(identityPublicKey); + + expect(isValid).to.be.false(); + }); + + it('should throw an error if the key security level is not met', async () => { + await stateTransition.sign(identityPublicKey, privateKeyHex); + + // Set key security level after the signing, since otherwise .sign method won't work + identityPublicKey.setSecurityLevel(IdentityPublicKey.SECURITY_LEVELS.MEDIUM); + + try { + await stateTransition.verifySignature(identityPublicKey); + + expect.fail('Should throw PublicKeySecurityLevelNotMetError'); + } catch (e) { + expect(e).to.be.instanceOf(PublicKeySecurityLevelNotMetError); + expect(e.getPublicKeySecurityLevel()) + .to.be.deep.equal(identityPublicKey.getSecurityLevel()); + expect(e.getKeySecurityLevelRequirement()) + .to.be.deep.equal(IdentityPublicKey.SECURITY_LEVELS.HIGH); + } + }); + + it('should throw an error if the key purpose is not equal to authentication', async () => { + await stateTransition.sign(identityPublicKey, privateKeyHex); + + // Set key security level after the signing, since otherwise .sign method won't work + identityPublicKey.setPurpose(IdentityPublicKey.PURPOSES.ENCRYPTION); + + try { + await stateTransition.verifySignature(identityPublicKey); + + expect.fail('Should throw WrongPublicKeyPurposeError'); + } catch (e) { + expect(e).to.be.instanceOf(WrongPublicKeyPurposeError); + expect(e.getPublicKeyPurpose()).to.be.deep.equal(identityPublicKey.getPurpose()); + expect(e.getKeyPurposeRequirement()) + .to.be.deep.equal(IdentityPublicKey.PURPOSES.AUTHENTICATION); + } + }); + + it('should validate BLS signature', async () => { + identityPublicKey.setType(IdentityPublicKey.TYPES.BLS12_381); + identityPublicKey.setData(Buffer.from(blsPrivateKey.getPublicKey().serialize())); + + await stateTransition.sign(identityPublicKey, blsPrivateKeyHex); + + expect(stateTransition.signature).to.be.an.instanceOf(Buffer); + + const isValid = await stateTransition.verifySignature(identityPublicKey); + + expect(isValid).to.be.true(); + }); + + it('should throw PublicKeyIsDisabledError if public key is disabled', async () => { + identityPublicKey.setDisabledAt(new Date().getTime()); + + try { + await stateTransition.sign(identityPublicKey, privateKeyHex); + + expect.fail('Should throw PublicKeyIsDisabledError'); + } catch (e) { + expect(e).to.be.instanceOf(PublicKeyIsDisabledError); + expect(e.getPublicKey()).to.be.deep.equal(identityPublicKey); + } + }); + + it('should throw InvalidSignaturePublicKeySecurityLevelError if public key with master level is using to sign non update state transition', async () => { + stateTransition.type = stateTransitionTypes.DATA_CONTRACT_CREATE; + identityPublicKey.setSecurityLevel(IdentityPublicKey.SECURITY_LEVELS.MASTER); + + try { + await stateTransition.sign(identityPublicKey, blsPrivateKeyHex); + + expect.fail('Should throw PublicKeyIsDisabledError'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidSignaturePublicKeySecurityLevelError); + expect(e.getPublicKeySecurityLevel()).to.equal(IdentityPublicKey.SECURITY_LEVELS.MASTER); + expect(e.getKeySecurityLevelRequirement()).to.equal( + IdentityPublicKey.SECURITY_LEVELS.HIGH, + ); + } + }); + }); + + describe('#verifyESDSAHash160SignatureByPublicKeyHash', () => { + it('should validate sign by public key hash', async () => { + privateKeyHex = 'fdfa0d878967ac17ca3e6fa6ca7f647fea51cffac85e41424c6954fcbe97721c'; + const publicKey = 'dLfavDCp+ARA3O0AXsOFJ0W//mg='; + + await stateTransition.signByPrivateKey(privateKeyHex, IdentityPublicKey.TYPES.ECDSA_HASH160); + + const isValid = stateTransition.verifyESDSAHash160SignatureByPublicKeyHash(Buffer.from(publicKey, 'base64')); + + expect(isValid).to.be.true(); + }); + + it('should throw an StateTransitionIsNotSignedError error if transition is not signed', async () => { + const publicKey = 'dLfavDCp+ARA3O0AXsOFJ0W//mg='; + try { + stateTransition.verifyESDSAHash160SignatureByPublicKeyHash(Buffer.from(publicKey, 'base64')); + + expect.fail('should throw StateTransitionIsNotSignedError'); + } catch (e) { + expect(e).to.be.instanceOf(StateTransitionIsNotSignedError); + expect(e.getStateTransition()).to.equal(stateTransition); + } + }); + }); + + describe('#verifyECDSASignatureByPublicKey', () => { + it('should validate sign by public key', async () => { + privateKeyHex = '9b67f852093bc61cea0eeca38599dbfba0de28574d2ed9b99d10d33dc1bde7b2'; + const publicKey = 'A1eUrJ7lM6F1m6dbIyk+vXimKfzki+QRMHMwoAmggt6L'; + + await stateTransition.signByPrivateKey( + privateKeyHex, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + const isValid = stateTransition.verifyECDSASignatureByPublicKey(Buffer.from(publicKey, 'base64')); + + expect(isValid).to.be.true(); + }); + + it('should throw an StateTransitionIsNotSignedError error if transition is not signed', async () => { + const publicKey = 'A1eUrJ7lM6F1m6dbIyk+vXimKfzki+QRMHMwoAmggt6L'; + try { + stateTransition.verifyECDSASignatureByPublicKey(Buffer.from(publicKey, 'base64')); + + expect.fail('should throw StateTransitionIsNotSignedError'); + } catch (e) { + expect(e).to.be.instanceOf(StateTransitionIsNotSignedError); + expect(e.getStateTransition()).to.equal(stateTransition); + } + }); + }); + + describe('#verifyBLSSignatureByPublicKey', () => { + it('should validate sign by public key', async () => { + const publicKey = blsPrivateKey.getPublicKey(); + + identityPublicKey.setType(IdentityPublicKey.TYPES.BLS12_381); + identityPublicKey.setData(Buffer.from(publicKey.serialize())); + + await stateTransition.signByPrivateKey(blsPrivateKeyHex, IdentityPublicKey.TYPES.BLS12_381); + + const isValid = await stateTransition.verifyBLSSignatureByPublicKey(publicKey); + + expect(isValid).to.be.true(); + }); + + it('should throw an StateTransitionIsNotSignedError error if transition is not signed', async () => { + const publicKey = Buffer.from(blsPrivateKey.getPublicKey().serialize()); + try { + await stateTransition.verifyBLSSignatureByPublicKey(publicKey); + + expect.fail('should throw StateTransitionIsNotSignedError'); + } catch (e) { + expect(e).to.be.instanceOf(StateTransitionIsNotSignedError); + expect(e.getStateTransition()).to.equal(stateTransition); + } + }); + }); + + describe('#setSignature', () => { + it('should set signature', () => { + const signature = 'A1eUrA'; + stateTransition.setSignature(signature); + + expect(stateTransition.signature.toString()).to.equal(signature); + }); + }); + + describe('#setSignaturePublicKeyId', () => { + it('should set signature public key id', async () => { + const signaturePublicKeyId = 1; + stateTransition.setSignaturePublicKeyId(signaturePublicKeyId); + + expect(stateTransition.signaturePublicKeyId).to.equal(signaturePublicKeyId); + }); + }); + + describe('#calculateFee', () => { + it('should calculate fee', () => { + const result = stateTransition.calculateFee(); + + const fee = calculateStateTransitionFee(stateTransition); + + expect(result).to.equal(fee); + }); + }); +}); diff --git a/packages/js-dpp/test/integration/stateTransition/StateTransitionFacade.spec.js b/packages/js-dpp/test/integration/stateTransition/StateTransitionFacade.spec.js new file mode 100644 index 00000000000..dc7cfdc1621 --- /dev/null +++ b/packages/js-dpp/test/integration/stateTransition/StateTransitionFacade.spec.js @@ -0,0 +1,418 @@ +const { PrivateKey } = require('@dashevo/dashcore-lib'); + +const DashPlatformProtocol = require('../../../lib/DashPlatformProtocol'); + +const DataContractCreateTransition = require('../../../lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition'); + +const ValidationResult = require('../../../lib/validation/ValidationResult'); + +const getDataContractFixture = require('../../../lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('../../../lib/test/fixtures/getDocumentsFixture'); +const getIdentityCreateTransitionFixture = require('../../../lib/test/fixtures/getIdentityCreateTransitionFixture'); + +const createStateRepositoryMock = require('../../../lib/test/mocks/createStateRepositoryMock'); + +const DataContractFactory = require('../../../lib/dataContract/DataContractFactory'); +const DocumentFactory = require('../../../lib/document/DocumentFactory'); + +const IdentityPublicKey = require('../../../lib/identity/IdentityPublicKey'); + +const MissingOptionError = require('../../../lib/errors/MissingOptionError'); +const createDPPMock = require('../../../lib/test/mocks/createDPPMock'); +const SomeConsensusError = require('../../../lib/test/mocks/SomeConsensusError'); + +describe('StateTransitionFacade', () => { + let dpp; + let dataContractCreateTransition; + let documentsBatchTransition; + let stateRepositoryMock; + let dataContract; + let identityPublicKey; + + beforeEach(async function beforeEach() { + const privateKeyModel = new PrivateKey(); + const privateKey = privateKeyModel.toWIF(); + const publicKey = privateKeyModel.toPublicKey().toBuffer(); + const publicKeyId = 1; + + identityPublicKey = new IdentityPublicKey() + .setId(publicKeyId) + .setType(IdentityPublicKey.TYPES.ECDSA_SECP256K1) + .setData(publicKey) + .setPurpose(IdentityPublicKey.PURPOSES.AUTHENTICATION) + .setSecurityLevel(IdentityPublicKey.PURPOSES.MASTER); + + dataContract = getDataContractFixture(); + + const dataContractFactory = new DataContractFactory(createDPPMock(), undefined); + + dataContractCreateTransition = dataContractFactory.createDataContractCreateTransition( + dataContract, + ); + await dataContractCreateTransition.sign(identityPublicKey, privateKey); + + const documentFactory = new DocumentFactory(createDPPMock(), undefined, undefined); + + documentsBatchTransition = documentFactory.createStateTransition({ + create: getDocumentsFixture(dataContract), + }); + await documentsBatchTransition.sign(identityPublicKey, privateKey); + + const getPublicKeyById = this.sinonSandbox.stub().returns(identityPublicKey); + const getBalance = this.sinonSandbox.stub().returns(10000); + + const identity = { + getPublicKeyById, + type: 2, + getBalance, + }; + + const timeInSeconds = Math.ceil(new Date().getTime() / 1000); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchIdentity.resolves(identity); + stateRepositoryMock.fetchLatestPlatformBlockHeader.resolves({ + time: { + seconds: timeInSeconds, + }, + }); + + dpp = new DashPlatformProtocol({ + stateRepository: stateRepositoryMock, + }); + + await dpp.initialize(); + }); + + describe('createFromObject', () => { + it('should throw MissingOption if stateRepository is not set', async () => { + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + try { + await dpp.stateTransition.createFromObject( + dataContractCreateTransition.toObject(), + ); + + expect.fail('MissingOption should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(MissingOptionError); + expect(e.getOptionName()).to.equal('stateRepository'); + } + }); + + it('should skip checking for state repository if skipValidation is set', async () => { + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + await dpp.stateTransition.createFromObject( + dataContractCreateTransition.toObject(), + { skipValidation: true }, + ); + }); + + it('should create State Transition from plain object', async () => { + const result = await dpp.stateTransition.createFromObject( + dataContractCreateTransition.toObject(), + ); + + expect(result).to.be.an.instanceOf(DataContractCreateTransition); + + expect(result.toObject()).to.deep.equal(dataContractCreateTransition.toObject()); + }); + }); + + describe('createFromBuffer', () => { + it('should throw MissingOption if stateRepository is not set', async () => { + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + try { + await dpp.stateTransition.createFromBuffer( + dataContractCreateTransition.toBuffer(), + ); + + expect.fail('MissingOption should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(MissingOptionError); + expect(e.getOptionName()).to.equal('stateRepository'); + } + }); + + it('should skip checking for state repository if skipValidation is set', async () => { + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + await dpp.stateTransition.createFromBuffer( + dataContractCreateTransition.toBuffer(), + { skipValidation: true }, + ); + }); + + it('should create State Transition from string', async () => { + const result = await dpp.stateTransition.createFromBuffer( + dataContractCreateTransition.toBuffer(), + ); + + expect(result).to.be.an.instanceOf(DataContractCreateTransition); + + expect(result.toObject()).to.deep.equal(dataContractCreateTransition.toObject()); + }); + }); + + describe('validate', () => { + let validateBasicSpy; + let validateSignatureSpy; + let validateFeeSpy; + let validateStateSpy; + + beforeEach(function beforeEach() { + validateBasicSpy = this.sinonSandbox.spy( + dpp.stateTransition, + 'validateBasic', + ); + + validateSignatureSpy = this.sinonSandbox.spy( + dpp.stateTransition, + 'validateSignature', + ); + + validateFeeSpy = this.sinonSandbox.spy( + dpp.stateTransition, + 'validateFee', + ); + + validateStateSpy = this.sinonSandbox.spy( + dpp.stateTransition, + 'validateState', + ); + }); + + it('should return invalid result if State Transition structure is invalid', async () => { + const rawStateTransition = dataContractCreateTransition.toObject(); + delete rawStateTransition.protocolVersion; + + const result = await dpp.stateTransition.validate(rawStateTransition); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.false(); + + expect(validateBasicSpy).to.be.calledOnceWithExactly(rawStateTransition); + expect(validateSignatureSpy).to.not.be.called(); + expect(validateFeeSpy).to.not.be.called(); + expect(validateStateSpy).to.not.be.called(); + }); + + it('should return invalid result if State Transition signature is invalid', async () => { + dataContractCreateTransition.signature = Buffer.alloc(65).fill(1); + + const result = await dpp.stateTransition.validate(dataContractCreateTransition); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.false(); + + expect(validateBasicSpy).to.be.calledOnceWithExactly(dataContractCreateTransition); + expect(validateSignatureSpy).to.be.calledOnceWithExactly(dataContractCreateTransition); + expect(validateFeeSpy).to.not.be.called(); + expect(validateStateSpy).to.not.be.called(); + }); + + it('should return invalid result if not enough balance to pay fee for State Transition', async () => { + const consensusError = new SomeConsensusError('error'); + + dpp.stateTransition.validateStateTransitionFee = () => new ValidationResult([consensusError]); + + const result = await dpp.stateTransition.validate(dataContractCreateTransition); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.false(); + + expect(validateBasicSpy).to.be.calledOnceWithExactly(dataContractCreateTransition); + expect(validateSignatureSpy).to.be.calledOnceWithExactly(dataContractCreateTransition); + expect(validateFeeSpy).to.be.calledOnceWithExactly(dataContractCreateTransition); + expect(validateStateSpy).to.not.be.called(); + }); + + it('should return invalid result if State Transition is invalid against state', async () => { + const consensusError = new SomeConsensusError('error'); + + dpp.stateTransition.validateStateTransitionState = () => ( + new ValidationResult([consensusError]) + ); + + const result = await dpp.stateTransition.validate(dataContractCreateTransition); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.false(); + + expect(validateBasicSpy).to.be.calledOnceWithExactly(dataContractCreateTransition); + expect(validateSignatureSpy).to.be.calledOnceWithExactly(dataContractCreateTransition); + expect(validateFeeSpy).to.be.calledOnceWithExactly(dataContractCreateTransition); + expect(validateStateSpy).to.be.calledOnceWithExactly(dataContractCreateTransition); + }); + + it('should validate DataContractCreateTransition', async () => { + const result = await dpp.stateTransition.validate( + dataContractCreateTransition, + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(validateBasicSpy).to.be.calledOnceWithExactly(dataContractCreateTransition); + expect(validateSignatureSpy).to.be.calledOnceWithExactly(dataContractCreateTransition); + expect(validateFeeSpy).to.be.calledOnceWithExactly(dataContractCreateTransition); + expect(validateStateSpy).to.be.calledOnceWithExactly(dataContractCreateTransition); + }); + + it('should validate DocumentsBatchTransition', async function it() { + stateRepositoryMock.fetchDocuments.resolves([]); + + stateRepositoryMock.fetchDataContract.resolves(dataContract); + stateRepositoryMock.fetchIdentity.resolves({ + getPublicKeyById: this.sinonSandbox.stub().returns(identityPublicKey), + getBalance: this.sinonSandbox.stub().returns(10000), + }); + + const result = await dpp.stateTransition.validate( + documentsBatchTransition, + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(validateBasicSpy).to.be.calledOnceWithExactly(documentsBatchTransition); + expect(validateSignatureSpy).to.be.calledOnceWithExactly(documentsBatchTransition); + expect(validateFeeSpy).to.be.calledOnceWithExactly(documentsBatchTransition); + expect(validateStateSpy).to.be.calledOnceWithExactly(documentsBatchTransition); + }); + }); + + describe('validateBasic', () => { + it('should throw MissingOption if stateRepository is not set', async () => { + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + try { + await dpp.stateTransition.validateBasic( + dataContractCreateTransition.toObject(), + ); + + expect.fail('MissingOption should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(MissingOptionError); + expect(e.getOptionName()).to.equal('stateRepository'); + } + }); + + it('should validate State Transition', async () => { + const result = await dpp.stateTransition.validateBasic( + dataContractCreateTransition.toObject(), + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + }); + + describe('validateSignature', () => { + it('should throw MissingOption if stateRepository is not set', async () => { + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + try { + await dpp.stateTransition.validateSignature( + dataContractCreateTransition, + ); + + expect.fail('MissingOption should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(MissingOptionError); + expect(e.getOptionName()).to.equal('stateRepository'); + } + }); + + it('should validate identity signed State Transition', async () => { + const result = await dpp.stateTransition.validateSignature( + dataContractCreateTransition, + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + + it('should validate key signed State Transition', async () => { + const oneTimePrivateKey = new PrivateKey( + 'af432c476f65211f45f48f1d42c9c0b497e56696aa1736b40544ef1a496af837', + ); + + const identityCreateTransition = getIdentityCreateTransitionFixture(oneTimePrivateKey); + + await identityCreateTransition.signByPrivateKey( + oneTimePrivateKey, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + const result = await dpp.stateTransition.validateSignature( + identityCreateTransition, + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + }); + + describe('validateFee', () => { + it('should throw MissingOption if stateRepository is not set', async () => { + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + try { + await dpp.stateTransition.validateFee( + dataContractCreateTransition, + ); + + expect.fail('MissingOption should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(MissingOptionError); + expect(e.getOptionName()).to.equal('stateRepository'); + } + }); + + it('should validate State Transition', async () => { + const result = await dpp.stateTransition.validateFee( + dataContractCreateTransition, + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + }); + + describe('validateState', () => { + it('should throw MissingOption if stateRepository is not set', async () => { + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + try { + await dpp.stateTransition.validateState( + dataContractCreateTransition, + ); + + expect.fail('MissingOption should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(MissingOptionError); + expect(e.getOptionName()).to.equal('stateRepository'); + } + }); + + it('should validate State Transition', async () => { + const result = await dpp.stateTransition.validateState( + dataContractCreateTransition, + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + }); +}); diff --git a/packages/js-dpp/test/integration/stateTransition/calculateStateTransitionFee.spec.js b/packages/js-dpp/test/integration/stateTransition/calculateStateTransitionFee.spec.js new file mode 100644 index 00000000000..fab641bb416 --- /dev/null +++ b/packages/js-dpp/test/integration/stateTransition/calculateStateTransitionFee.spec.js @@ -0,0 +1,35 @@ +const calculateStateTransitionFee = require('../../../lib/stateTransition/fee/calculateStateTransitionFee'); + +const getIdentityCreateTransitionFixture = require('../../../lib/test/fixtures/getIdentityCreateTransitionFixture'); +const IdentityPublicKey = require('../../../lib/identity/IdentityPublicKey'); +const ReadOperation = require('../../../lib/stateTransition/fee/operations/ReadOperation'); +const WriteOperation = require('../../../lib/stateTransition/fee/operations/WriteOperation'); +const DeleteOperation = require('../../../lib/stateTransition/fee/operations/DeleteOperation'); +const PreCalculatedOperation = require('../../../lib/stateTransition/fee/operations/PreCalculatedOperation'); + +describe('calculateStateTransitionFee', () => { + let stateTransition; + + beforeEach(async () => { + const privateKey = 'af432c476f65211f45f48f1d42c9c0b497e56696aa1736b40544ef1a496af837'; + + stateTransition = getIdentityCreateTransitionFixture(); + await stateTransition.signByPrivateKey(privateKey, IdentityPublicKey.TYPES.ECDSA_SECP256K1); + }); + + // TODO: Must be more comprehensive. After we settle all factors and formula. + it('should calculate fee based on executed operations', () => { + const executionContext = stateTransition.getExecutionContext(); + + executionContext.addOperation( + new ReadOperation(10), + new WriteOperation(5, 5), + new DeleteOperation(6, 6), + new PreCalculatedOperation(12, 12), + ); + + const result = calculateStateTransitionFee(stateTransition); + + expect(result).to.equal(13616); + }); +}); diff --git a/packages/js-dpp/test/integration/util/generateEntropy.spec.js b/packages/js-dpp/test/integration/util/generateEntropy.spec.js new file mode 100644 index 00000000000..2fb0a3ecf00 --- /dev/null +++ b/packages/js-dpp/test/integration/util/generateEntropy.spec.js @@ -0,0 +1,17 @@ +const { generate: generateEntropy } = require('../../../lib/util/entropyGenerator'); + +describe('generateEntropy', () => { + it('should generate a byte array of length 32', () => { + const entropy = generateEntropy(); + + expect(Buffer.isBuffer(entropy)).to.be.true(); + expect(entropy.byteLength).to.be.equal(32); + }); + + it('should generate random byte array', () => { + const randomBuffer = generateEntropy(); + const secondRandomBuffer = generateEntropy(); + + expect(randomBuffer).to.not.deep.equal(secondRandomBuffer); + }); +}); diff --git a/packages/js-dpp/test/integration/util/serializer.spec.js b/packages/js-dpp/test/integration/util/serializer.spec.js new file mode 100644 index 00000000000..fc73dd341b3 --- /dev/null +++ b/packages/js-dpp/test/integration/util/serializer.spec.js @@ -0,0 +1,24 @@ +const { encode } = require('../../../lib/util/serializer'); + +const DataSerializationError = require('../../../lib/util/errors/MaxEncodedBytesReachedError'); + +describe('serializer', function main() { + this.timeout(10000); + + describe('#encode', () => { + it('should throw an error if payload is larger that 16 Kb', () => { + const payload = {}; + for (let i = 0; i < 10000; i++) { + payload[i] = i; + } + + try { + encode(payload); + expect.fail('Error was not thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(DataSerializationError); + expect(e.getPayload()).to.deep.equal(payload); + } + }); + }); +}); diff --git a/packages/js-dpp/test/unit/DashPlatformProtocol.spec.js b/packages/js-dpp/test/unit/DashPlatformProtocol.spec.js new file mode 100644 index 00000000000..17aec64a63a --- /dev/null +++ b/packages/js-dpp/test/unit/DashPlatformProtocol.spec.js @@ -0,0 +1,79 @@ +const { default: Ajv } = require('ajv/dist/2020'); + +const protocolVersion = require('../../lib/version/protocolVersion'); + +const DashPlatformProtocol = require('../../lib/DashPlatformProtocol'); +const JsonSchemaValidator = require('../../lib/validation/JsonSchemaValidator'); + +const createStateRepositoryMock = require('../../lib/test/mocks/createStateRepositoryMock'); + +describe('DashPlatformProtocol', () => { + let dpp; + let stateRepositoryMock; + let jsonSchemaValidatorMock; + + beforeEach(async function beforeEach() { + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + jsonSchemaValidatorMock = {}; + + dpp = new DashPlatformProtocol({ + stateRepository: stateRepositoryMock, + jsonSchemaValidator: jsonSchemaValidatorMock, + }); + await dpp.initialize(); + }); + + describe('constructor', () => { + it('should create JsonSchemaValidator if not passed in options', async () => { + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + const jsonSchemaValidator = dpp.getJsonSchemaValidator(); + + expect(jsonSchemaValidator).to.be.instanceOf(JsonSchemaValidator); + expect(jsonSchemaValidator.ajv).to.be.instanceOf(Ajv); + }); + + it('should set default protocol version', () => { + dpp = new DashPlatformProtocol(); + + expect(dpp.protocolVersion).to.equal(protocolVersion.latestVersion); + }); + }); + + describe('getStateRepository', () => { + it('should return StateRepository', () => { + const result = dpp.getStateRepository(); + + expect(result).to.equal(stateRepositoryMock); + }); + }); + + describe('getJsonSchemaValidator', () => { + it('should return JsonSchemaValidator', () => { + const result = dpp.getJsonSchemaValidator(); + + expect(result).to.equal(jsonSchemaValidatorMock); + }); + }); + + describe('setProtocolVersion', () => { + it('should set protocol version', () => { + expect(dpp.protocolVersion).to.equal(protocolVersion.latestVersion); + + dpp.setProtocolVersion(42); + + expect(dpp.protocolVersion).to.equal(42); + }); + }); + + describe('getProtocolVersion', () => { + it('should get protocol version', () => { + expect(dpp.getProtocolVersion()).to.equal(protocolVersion.latestVersion); + + dpp.setProtocolVersion(42); + + expect(dpp.getProtocolVersion()).to.equal(42); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/Identifier.spec.js b/packages/js-dpp/test/unit/Identifier.spec.js new file mode 100644 index 00000000000..5ab44678dc6 --- /dev/null +++ b/packages/js-dpp/test/unit/Identifier.spec.js @@ -0,0 +1,123 @@ +const crypto = require('crypto'); +const bs58 = require('bs58'); +const Identifier = require('../../lib/identifier/Identifier'); +const IdentifierError = require('../../lib/identifier/errors/IdentifierError'); + +describe('Identifier', () => { + let buffer; + + beforeEach(() => { + buffer = crypto.randomBytes(32); + }); + + describe('#constructor', () => { + it('should accept Buffer', () => { + const identifier = new Identifier(buffer); + + expect(identifier).to.be.deep.equal(buffer); + expect(identifier).to.be.an.instanceOf(Identifier); + }); + + it('should throw error if first argument is not Buffer', () => { + expect( + () => new Identifier(1), + ).to.throw(IdentifierError, 'Identifier expects Buffer'); + }); + + it('should throw error if buffer is not 32 bytes long', () => { + expect( + () => new Identifier(Buffer.alloc(30)), + ).to.throw(IdentifierError, 'Identifier must be 32 long'); + }); + }); + + describe('#toBuffer', () => { + it('should return a new normal Buffer', () => { + const identifier = new Identifier(buffer); + + expect(identifier.toBuffer()).to.deep.equal(buffer); + }); + }); + + describe('#encodeCBOR', () => { + let encoderMock; + + beforeEach(function before() { + encoderMock = { + pushAny: this.sinonSandbox.stub(), + }; + }); + + it('should encode using cbor encoder', () => { + const identifier = new Identifier(buffer); + + const result = identifier.encodeCBOR(encoderMock); + + expect(result).to.be.true(); + expect(encoderMock.pushAny).to.be.calledOnceWithExactly(buffer); + }); + }); + + describe('#toJSON', () => { + it('should return a base58 encoded string', () => { + const identifier = new Identifier(buffer); + + const string = identifier.toJSON(); + + expect(string).to.equal(bs58.encode(buffer)); + }); + }); + + describe('#toString', () => { + it('should return a base58 encoded string by default', () => { + const base58string = bs58.encode(buffer); + + const identifier = new Identifier(buffer); + + const string = identifier.toString(); + + expect(string).to.equal(base58string); + }); + + it('should return a string encoded with specified encoding', () => { + const identifier = new Identifier(buffer); + + const string = identifier.toString('base64'); + + expect(string).to.equal(buffer.toString('base64')); + }); + }); + + describe('#from', () => { + it('should create an instance from Buffer', async () => { + const identifier = Identifier.from(buffer); + + expect(identifier).to.be.an.instanceOf(Identifier); + expect(identifier).to.deep.equal(buffer); + }); + + it('should throw error if buffer is passed among with encoding', async () => { + expect( + () => Identifier.from(buffer, 'base64'), + ).to.throw(IdentifierError, 'encoding accepted only with type string'); + }); + + it('should create an instance with a base58 string', () => { + const string = bs58.encode(buffer); + + const identifier = Identifier.from(string); + + expect(identifier).to.be.an.instanceOf(Identifier); + expect(identifier).to.deep.equal(buffer); + }); + + it('should create an instance with a base64 string', () => { + const string = buffer.toString('base64'); + + const identifier = Identifier.from(string, 'base64'); + + expect(identifier).to.be.an.instanceOf(Identifier); + expect(identifier).to.deep.equal(buffer); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/Metadata.spec.js b/packages/js-dpp/test/unit/Metadata.spec.js new file mode 100644 index 00000000000..c7c5815f7f7 --- /dev/null +++ b/packages/js-dpp/test/unit/Metadata.spec.js @@ -0,0 +1,37 @@ +const Metadata = require('../../lib/Metadata'); + +describe('Metadata', () => { + describe('#constructor', () => { + it('should set height and core chain-locked height', () => { + const result = new Metadata({ + blockHeight: 42, + coreChainLockedHeight: 1, + }); + + expect(result.blockHeight).to.equal(42); + expect(result.coreChainLockedHeight).to.equal(1); + }); + }); + + describe('#getBlockHeight', () => { + it('should get block height', () => { + const result = new Metadata({ + blockHeight: 42, + coreChainLockedHeight: 1, + }); + + expect(result.getBlockHeight()).to.equal(42); + }); + }); + + describe('#getCoreChainLockedHeight', () => { + it('should get core chain-locked height', () => { + const result = new Metadata({ + blockHeight: 1, + coreChainLockedHeight: 42, + }); + + expect(result.getCoreChainLockedHeight()).to.equal(42); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/DataContract.spec.js b/packages/js-dpp/test/unit/dataContract/DataContract.spec.js new file mode 100644 index 00000000000..01640128214 --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/DataContract.spec.js @@ -0,0 +1,407 @@ +const bs58 = require('bs58'); + +const Identifier = require('../../../lib/identifier/Identifier'); + +const InvalidDocumentTypeError = require('../../../lib/errors/InvalidDocumentTypeError'); + +const generateRandomIdentifier = require('../../../lib/test/utils/generateRandomIdentifier'); +const Metadata = require('../../../lib/Metadata'); +const DataContract = require('../../../lib/dataContract/DataContract'); + +const hash = require('../../../lib/util/hash'); +const serializer = require('../../../lib/util/serializer'); +const getBinaryPropertiesFromSchema = require('../../../lib/dataContract/getBinaryPropertiesFromSchema'); + +describe('DataContract', () => { + let hashMock; + let encodeMock; + let documentType; + let documentSchema; + let documents; + let dataContract; + let ownerId; + let entropy; + let contractId; + let getBinaryPropertiesFromSchemaMock; + let metadataFixture; + + beforeEach(function beforeEach() { + encodeMock = this.sinonSandbox.stub(serializer, 'encode'); + hashMock = this.sinonSandbox.stub(hash, 'hash'); + getBinaryPropertiesFromSchemaMock = this.sinonSandbox.stub(getBinaryPropertiesFromSchema, 'getBinaryPropertiesFromSchema'); + + documentType = 'niceDocument'; + + documentSchema = { + properties: { + nice: { + type: 'boolean', + }, + }, + }; + + documents = { + [documentType]: documentSchema, + }; + + getBinaryPropertiesFromSchemaMock.withArgs(documentSchema) + .returns({ + 'firstLevel.secondLevel': { + type: 'array', + byteArray: true, + }, + }); + + ownerId = generateRandomIdentifier(); + entropy = Buffer.alloc(32); + contractId = generateRandomIdentifier(); + + dataContract = new DataContract({ + $schema: DataContract.DEFAULTS.SCHEMA, + $id: contractId, + version: 1, + ownerId, + documents, + $defs: {}, + }); + + metadataFixture = new Metadata(42, 0); + + dataContract.setMetadata(metadataFixture); + }); + + afterEach(() => { + encodeMock.restore(); + hashMock.restore(); + getBinaryPropertiesFromSchemaMock.restore(); + }); + + describe('constructor', () => { + it('should create new DataContract', () => { + const id = generateRandomIdentifier(); + + dataContract = new DataContract({ + $schema: DataContract.DEFAULTS.SCHEMA, + $id: id, + ownerId, + documents, + $defs: {}, + }); + + expect(dataContract.id).to.deep.equal(id); + expect(dataContract.ownerId).to.deep.equal(ownerId); + expect(dataContract.schema).to.equal(DataContract.DEFAULTS.SCHEMA); + expect(dataContract.documents).to.equal(documents); + expect(dataContract.$defs).to.deep.equal({}); + }); + }); + + describe('#getId', () => { + it('should return DataContract Identifier', () => { + const result = dataContract.getId(); + + expect(result).to.deep.equal(contractId); + expect(result).to.be.instanceof(Identifier); + }); + }); + + describe('#getJsonSchemaId', () => { + it('should return JSON Schema ID', () => { + const result = dataContract.getJsonSchemaId(); + + expect(result).to.equal(dataContract.getId().toString()); + }); + }); + + describe('#setJsonMetaSchema', () => { + it('should set meta schema', () => { + const metaSchema = 'http://test.com/schema'; + + const result = dataContract.setJsonMetaSchema(metaSchema); + + expect(result).to.equal(dataContract); + expect(dataContract.schema).to.equal(metaSchema); + }); + }); + + describe('#getJsonMetaSchema', () => { + it('should return meta schema', () => { + const result = dataContract.getJsonMetaSchema(); + + expect(result).to.equal(dataContract.schema); + }); + }); + + describe('#setDocuments', () => { + it('should set Documents definition', () => { + const anotherDocuments = { + anotherDocument: { + properties: { + name: { type: 'string' }, + }, + }, + }; + + const result = dataContract.setDocuments(anotherDocuments); + + expect(result).to.equal(dataContract); + expect(dataContract.documents).to.equal(anotherDocuments); + }); + }); + + describe('#getDocuments', () => { + it('should return Documents definition', () => { + const result = dataContract.getDocuments(); + + expect(result).to.equal(dataContract.documents); + }); + }); + + describe('#isDocumentDefined', () => { + it('should return true if Document schema is defined', () => { + const result = dataContract.isDocumentDefined('niceDocument'); + + expect(result).to.equal(true); + }); + + it('should return false if Document schema is not defined', () => { + const result = dataContract.isDocumentDefined('undefinedDocument'); + + expect(result).to.equal(false); + }); + }); + + describe('#setDocumentSchema', () => { + it('should set Document schema', () => { + const anotherType = 'prettyDocument'; + const anotherDefinition = { + properties: { + name: { type: 'string' }, + }, + }; + + const result = dataContract.setDocumentSchema(anotherType, anotherDefinition); + + expect(result).to.equal(dataContract); + + expect(dataContract.documents).to.have.property(anotherType); + expect(dataContract.documents[anotherType]).to.equal(anotherDefinition); + }); + }); + + describe('#getDocumentSchema', () => { + it('should throw error if Document is not defined', () => { + let error; + try { + dataContract.getDocumentSchema('undefinedObject'); + } catch (e) { + error = e; + } + + expect(error).to.be.an.instanceOf(InvalidDocumentTypeError); + }); + + it('should return Document Schema', () => { + const result = dataContract.getDocumentSchema(documentType); + + expect(result).to.equal(documentSchema); + }); + }); + + describe('#getDocumentSchemaRef', () => { + it('should throw error if Document is not defined', () => { + let error; + try { + dataContract.getDocumentSchemaRef('undefinedObject'); + } catch (e) { + error = e; + } + + expect(error).to.be.an.instanceOf(InvalidDocumentTypeError); + }); + + it('should return schema with $ref to Document schema', () => { + const hashed = Buffer.from(ownerId + entropy); + hashMock.returns(hashed); + + const result = dataContract.getDocumentSchemaRef(documentType); + + expect(result).to.deep.equal({ + $ref: `${dataContract.getJsonSchemaId()}#/documents/niceDocument`, + }); + }); + }); + + describe('#setDefinitions', () => { + it('should set $defs', () => { + const $defs = {}; + + const result = dataContract.setDefinitions($defs); + + expect(result).to.equal(dataContract); + expect(dataContract.$defs).to.equal($defs); + }); + }); + + describe('#getDefinitions', () => { + it('should return $defs', () => { + const result = dataContract.getDefinitions(); + + expect(result).to.equal(dataContract.$defs); + }); + }); + + describe('#toJSON', () => { + it('should return DataContract as plain object', () => { + const result = dataContract.toJSON(); + + expect(result).to.deep.equal({ + protocolVersion: dataContract.getProtocolVersion(), + $id: bs58.encode(contractId), + $schema: DataContract.DEFAULTS.SCHEMA, + version: 1, + ownerId: bs58.encode(ownerId), + documents, + }); + }); + + it('should return plain object with "$defs" if present', () => { + const $defs = { + subSchema: { type: 'object' }, + }; + + dataContract.setDefinitions($defs); + + const result = dataContract.toJSON(); + + expect(result).to.deep.equal({ + protocolVersion: dataContract.getProtocolVersion(), + $schema: DataContract.DEFAULTS.SCHEMA, + $id: bs58.encode(contractId), + version: 1, + ownerId: bs58.encode(ownerId), + documents, + $defs, + }); + }); + }); + + describe('#toBuffer', () => { + it('should return DataContract as a Buffer', () => { + const serializedDataContract = Buffer.from('123'); + + encodeMock.returns(serializedDataContract); + + const result = dataContract.toBuffer(); + + const dataContractToEncode = dataContract.toObject(); + delete dataContractToEncode.protocolVersion; + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(dataContract.getProtocolVersion(), 0); + + expect(encodeMock).to.have.been.calledOnceWith(dataContractToEncode); + expect(result).to.deep.equal(Buffer.concat([protocolVersionUInt32, serializedDataContract])); + }); + }); + + describe('#hash', () => { + let toBufferMock; + + beforeEach(function beforeEach() { + toBufferMock = this.sinonSandbox.stub(DataContract.prototype, 'toBuffer'); + }); + + afterEach(() => { + toBufferMock.restore(); + }); + + it('should return DataContract hash', () => { + const serializedDataContract = '123'; + const hashedDocument = '456'; + + toBufferMock.returns(serializedDataContract); + + hashMock.returns(hashedDocument); + + const result = dataContract.hash(); + + expect(result).to.equal(hashedDocument); + + expect(toBufferMock).to.have.been.calledOnce(); + + expect(hashMock).to.have.been.calledOnceWith(serializedDataContract); + }); + }); + + describe('#setEntropy', () => { + it('should set entropy', () => { + const result = dataContract.setEntropy(entropy); + + expect(result).to.equal(dataContract); + expect(dataContract.entropy).to.deep.equal(entropy); + }); + }); + + describe('#getEntropy', () => { + it('should return entropy', () => { + dataContract.entropy = entropy; + + const result = dataContract.getEntropy(); + + expect(result).to.equal(dataContract.entropy); + }); + }); + + describe('#getBinaryProperties', () => { + it('should return flat map of properties with `contentEncoding` keywords', () => { + const result = dataContract.getBinaryProperties(documentType); + expect(result).to.deep.equal({ + 'firstLevel.secondLevel': { + type: 'array', + byteArray: true, + }, + }); + }); + + it('should return cached flat map of properties with `contentEncoding` keywords', () => { + dataContract.getBinaryProperties(documentType); + + const result = dataContract.getBinaryProperties(documentType); + + expect(result).to.deep.equal({ + 'firstLevel.secondLevel': { + type: 'array', + byteArray: true, + }, + }); + + expect(getBinaryPropertiesFromSchemaMock).to.have.been.calledOnceWith(documentSchema); + }); + + it('should throw an error if document type is not found', () => { + try { + dataContract.getBinaryProperties('unknown'); + expect.fail('Error was not thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidDocumentTypeError); + } + }); + }); + + describe('#setMetadata', () => { + it('should set metadata', () => { + const otherMetadata = new Metadata(43, 1); + + dataContract.setMetadata(otherMetadata); + + expect(dataContract.metadata).to.deep.equal(otherMetadata); + }); + }); + + describe('#getMetadata', () => { + it('should get metadata', () => { + expect(dataContract.getMetadata()).to.deep.equal(metadataFixture); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/DataContractFactory.spec.js b/packages/js-dpp/test/unit/dataContract/DataContractFactory.spec.js new file mode 100644 index 00000000000..c615be139d1 --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/DataContractFactory.spec.js @@ -0,0 +1,178 @@ +const getDataContractFixture = require('../../../lib/test/fixtures/getDataContractFixture'); + +const protocolVersion = require('../../../lib/version/protocolVersion'); + +const DataContractCreateTransition = require('../../../lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition'); + +const ValidationResult = require('../../../lib/validation/ValidationResult'); + +const InvalidDataContractError = require('../../../lib/dataContract/errors/InvalidDataContractError'); +const SerializedObjectParsingError = require('../../../lib/errors/consensus/basic/decode/SerializedObjectParsingError'); +const createDPPMock = require('../../../lib/test/mocks/createDPPMock'); +const SomeConsensusError = require('../../../lib/test/mocks/SomeConsensusError'); + +const DataContractFactory = require('../../../lib/dataContract/DataContractFactory'); +const entropyGenerator = require('../../../lib/util/entropyGenerator'); + +describe('DataContractFactory', () => { + let decodeProtocolEntityMock; + let validateDataContractMock; + let factory; + let dataContract; + let rawDataContract; + let generateEntropyMock; + let dppMock; + + beforeEach(function beforeEach() { + dataContract = getDataContractFixture(); + dppMock = createDPPMock(); + + rawDataContract = dataContract.toObject(); + + decodeProtocolEntityMock = this.sinonSandbox.stub(); + validateDataContractMock = this.sinonSandbox.stub(); + generateEntropyMock = this.sinonSandbox.stub(entropyGenerator, 'generate'); + + factory = new DataContractFactory( + dppMock, + validateDataContractMock, + decodeProtocolEntityMock, + ); + }); + + afterEach(() => { + generateEntropyMock.restore(); + }); + + describe('create', () => { + it('should return new Data Contract with specified name and documents definition', () => { + generateEntropyMock.returns(dataContract.getEntropy()); + const result = factory.create( + dataContract.ownerId.toBuffer(), + rawDataContract.documents, + ); + + expect(result).excluding('$defs').to.deep.equal(dataContract); + }); + }); + + describe('createFromObject', () => { + it('should return new Data Contract with data from passed object', async () => { + validateDataContractMock.returns(new ValidationResult()); + + const result = await factory.createFromObject(rawDataContract); + + expect(result.toObject()).excluding('entropy').to.deep.equal(rawDataContract); + + expect(validateDataContractMock).to.have.been.calledOnceWith(rawDataContract); + }); + + it('should return new Data Contract without validation if "skipValidation" option is passed', async () => { + const result = await factory.createFromObject(rawDataContract, { skipValidation: true }); + + expect(result.toObject()).excluding('entropy').to.deep.equal(rawDataContract); + + expect(validateDataContractMock).to.have.not.been.called(); + }); + + it('should throw an error if passed object is not valid', async () => { + const validationError = new SomeConsensusError('test'); + + validateDataContractMock.returns(new ValidationResult([validationError])); + + let error; + try { + await factory.createFromObject(rawDataContract); + } catch (e) { + error = e; + } + + expect(error).to.be.an.instanceOf(InvalidDataContractError); + expect(error.getRawDataContract()).to.equal(rawDataContract); + + expect(error.getErrors()).to.have.length(1); + + const [consensusError] = error.getErrors(); + + expect(consensusError).to.equal(validationError); + + expect(validateDataContractMock).to.have.been.calledOnceWith(rawDataContract); + }); + }); + + describe('createFromBuffer', () => { + let serializedDataContract; + + beforeEach(function beforeEach() { + this.sinonSandbox.stub(factory, 'createFromObject'); + + serializedDataContract = dataContract.toBuffer(); + }); + + afterEach(() => { + factory.createFromObject.restore(); + }); + + it('should return new Data Contract from serialized contract', async () => { + decodeProtocolEntityMock.returns([rawDataContract.protocolVersion, rawDataContract]); + + factory.createFromObject.returns(dataContract); + + const result = await factory.createFromBuffer(serializedDataContract); + + expect(result).to.equal(dataContract); + + expect(factory.createFromObject).to.have.been.calledOnceWith(rawDataContract); + + expect(decodeProtocolEntityMock).to.have.been.calledOnceWithExactly( + serializedDataContract, + ); + }); + + it('should throw InvalidDataContractError if the decoding fails with consensus error', async () => { + const parsingError = new SerializedObjectParsingError( + serializedDataContract, + new Error(), + ); + + decodeProtocolEntityMock.throws(parsingError); + + try { + await factory.createFromBuffer(serializedDataContract); + + expect.fail('should throw InvalidDataContractError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidDataContractError); + + const [innerError] = e.getErrors(); + expect(innerError).to.equal(parsingError); + } + }); + + it('should throw an error if decoding fails with any other error', async () => { + const parsingError = new Error('Something failed during parsing'); + + decodeProtocolEntityMock.throws(parsingError); + + try { + await factory.createFromBuffer(serializedDataContract); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.equal(parsingError); + } + }); + }); + + describe('createDataContractCreateTransition', () => { + it('should return new DataContractCreateTransition with passed DataContract', () => { + const result = factory.createDataContractCreateTransition(dataContract); + + expect(result).to.be.an.instanceOf(DataContractCreateTransition); + + expect(result.getProtocolVersion()).to.equal(protocolVersion.latestVersion); + expect(result.getEntropy()).to.deep.equal(dataContract.getEntropy()); + expect(result.getDataContract().toObject()).to.deep.equal(dataContract.toObject()); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/createDataContract.spec.js b/packages/js-dpp/test/unit/dataContract/createDataContract.spec.js new file mode 100644 index 00000000000..095de2b12f1 --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/createDataContract.spec.js @@ -0,0 +1,57 @@ +const DataContract = require('../../../lib/dataContract/DataContract'); + +const generateRandomIdentifier = require('../../../lib/test/utils/generateRandomIdentifier'); + +describe('createDataContract', () => { + let rawDataContract; + + beforeEach(() => { + rawDataContract = { + $id: generateRandomIdentifier().toBuffer(), + ownerId: generateRandomIdentifier().toBuffer(), + contractId: generateRandomIdentifier().toBuffer(), + documents: { + niceDocument: { + name: { type: 'string' }, + }, + }, + }; + }); + + it('should return new DataContract with "dataContractId" and documents', () => { + const dataContract = new DataContract(rawDataContract); + + expect(dataContract).to.be.an.instanceOf(DataContract); + + expect(dataContract.getOwnerId()).to.deep.equal(rawDataContract.ownerId); + expect(dataContract.getDocuments()).to.equal(rawDataContract.documents); + }); + + it('should return new DataContract with "$schema" if present', () => { + rawDataContract.$schema = 'http://test.com/schema'; + + const dataContract = new DataContract(rawDataContract); + + expect(dataContract).to.be.an.instanceOf(DataContract); + + expect(dataContract.getJsonMetaSchema()).to.equal(rawDataContract.$schema); + + expect(dataContract.getOwnerId()).to.deep.equal(rawDataContract.ownerId); + expect(dataContract.getDocuments()).to.equal(rawDataContract.documents); + }); + + it('should return new DataContract with "$defs" if present', () => { + rawDataContract.$defs = { + subSchema: { type: 'object' }, + }; + + const dataContract = new DataContract(rawDataContract); + + expect(dataContract).to.be.an.instanceOf(DataContract); + + expect(dataContract.getDefinitions()).to.equal(rawDataContract.$defs); + + expect(dataContract.getOwnerId()).to.deep.equal(rawDataContract.ownerId); + expect(dataContract.getDocuments()).to.equal(rawDataContract.documents); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/errors/InvalidDataContractError.spec.js b/packages/js-dpp/test/unit/dataContract/errors/InvalidDataContractError.spec.js new file mode 100644 index 00000000000..a05f43d210a --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/errors/InvalidDataContractError.spec.js @@ -0,0 +1,46 @@ +const InvalidDataContractError = require('../../../../lib/dataContract/errors/InvalidDataContractError'); +const getDataContractFixture = require('../../../../lib/test/fixtures/getDataContractFixture'); + +describe('InvalidDataContractError', () => { + let rawDataContract; + let error; + + beforeEach(() => { + error = new Error('Some error'); + + const dataContract = getDataContractFixture(); + rawDataContract = dataContract.toObject(); + }); + + it('should return errors', () => { + const errors = [error]; + + const invalidDataContractError = new InvalidDataContractError(errors, rawDataContract); + + expect(invalidDataContractError.getErrors()).to.deep.equal(errors); + }); + + it('should return Data Contract', async () => { + const errors = [error]; + + const invalidDataContractError = new InvalidDataContractError(errors, rawDataContract); + + expect(invalidDataContractError.getRawDataContract()).to.deep.equal(rawDataContract); + }); + + it('should contain message for 1 error', async () => { + const errors = [error]; + + const invalidDataContractError = new InvalidDataContractError(errors, rawDataContract); + + expect(invalidDataContractError.message).to.equal(`Invalid Data Contract: "${error.message}"`); + }); + + it('should contain message for multiple errors', async () => { + const errors = [error, error]; + + const invalidDataContractError = new InvalidDataContractError(errors, rawDataContract); + + expect(invalidDataContractError.message).to.equal(`Invalid Data Contract: "${error.message}" and 1 more`); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/generateDataContractId.spec.js b/packages/js-dpp/test/unit/dataContract/generateDataContractId.spec.js new file mode 100644 index 00000000000..d2a77250561 --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/generateDataContractId.spec.js @@ -0,0 +1,19 @@ +const bs58 = require('bs58'); +const generateDataContractId = require('../../../lib/dataContract/generateDataContractId'); + +describe('generateDataContractId', () => { + let ownerId; + let entropy; + + beforeEach(() => { + ownerId = bs58.decode('23wdhodag'); + entropy = bs58.decode('5dz916pTe1'); + }); + + it('should generate bs58 id based on ', () => { + const id = bs58.decode('CnS7cz4z1qoPsNfEgpgyVnKdtH2u7bgzZXHLcCQt24US'); + const generatedId = generateDataContractId(ownerId, entropy); + + expect(Buffer.compare(id, generatedId)).to.equal(0); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/getBinaryPropertiesFromSchema.spec.js b/packages/js-dpp/test/unit/dataContract/getBinaryPropertiesFromSchema.spec.js new file mode 100644 index 00000000000..869c8b9c4fa --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/getBinaryPropertiesFromSchema.spec.js @@ -0,0 +1,103 @@ +const { getBinaryPropertiesFromSchema } = require( + '../../../lib/dataContract/getBinaryPropertiesFromSchema', +); + +describe('getBinaryPropertiesFromSchema', () => { + let documentSchema; + + beforeEach(() => { + documentSchema = { + properties: { + simple: { + type: 'string', + }, + withByteArray: { + type: 'object', + byteArray: true, + }, + nestedObject: { + type: 'object', + properties: { + simple: { + type: 'string', + }, + withByteArray: { + type: 'object', + byteArray: true, + }, + }, + }, + arrayOfObject: { + type: 'array', + items: { + type: 'object', + properties: { + simple: { + type: 'string', + }, + withByteArray: { + type: 'object', + byteArray: true, + }, + }, + }, + }, + arrayOfObjects: { + type: 'array', + items: [ + { + type: 'object', + properties: { + simple: { + type: 'string', + }, + withByteArray: { + type: 'object', + byteArray: true, + }, + }, + }, + { + type: 'string', + }, + { + type: 'array', + items: [ + { + type: 'object', + properties: { + simple: { + type: 'string', + }, + withByteArray: { + type: 'object', + byteArray: true, + }, + }, + }, + ], + }, + ], + }, + }, + }; + }); + + it('should return an empty object if not `properties` property found', () => { + const result = getBinaryPropertiesFromSchema({}); + + expect(result).to.deep.equal({}); + }); + + it('should return flat object with properties having contentEncoding keyword', () => { + const result = getBinaryPropertiesFromSchema(documentSchema); + + expect(result).to.deep.equal({ + withByteArray: { type: 'object', byteArray: true }, + 'nestedObject.withByteArray': { type: 'object', byteArray: true }, + 'arrayOfObject.withByteArray': { type: 'object', byteArray: true }, + 'arrayOfObjects[0].withByteArray': { type: 'object', byteArray: true }, + 'arrayOfObjects[2][0].withByteArray': { type: 'object', byteArray: true }, + }); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/getPropertyDefinitionByPath.spec.js b/packages/js-dpp/test/unit/dataContract/getPropertyDefinitionByPath.spec.js new file mode 100644 index 00000000000..ce2108acc8a --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/getPropertyDefinitionByPath.spec.js @@ -0,0 +1,84 @@ +const getPropertyDefinitionByPath = require( + '../../../lib/dataContract/getPropertyDefinitionByPath', +); + +describe('getPropertyDefinitionByPath', () => { + let schema; + + beforeEach(() => { + schema = { + properties: { + a: { + type: 'string', + }, + b: { + type: 'array', + items: { + type: 'object', + properties: { + inner: { + type: 'object', + properties: { + abc: { + type: 'string', + }, + }, + }, + }, + }, + }, + c: { + type: 'object', + properties: { + inner: { + type: 'object', + patternProperties: { + '[a-z]': { + type: 'string', + }, + }, + }, + }, + }, + }, + }; + }); + + it('should return `undefined` if property not found', () => { + const definition = getPropertyDefinitionByPath(schema, 'nope'); + + expect(definition).to.be.undefined(); + }); + + it('should return definition immediately if path is one-level', () => { + const definition = getPropertyDefinitionByPath(schema, 'a'); + + expect(definition).to.deep.equal({ + type: 'string', + }); + }); + + it('should return nested definition from an array', () => { + const definition = getPropertyDefinitionByPath(schema, 'b.inner'); + + expect(definition).to.deep.equal(schema.properties.b.items.properties.inner); + }); + + it('should return nested definition from object', () => { + const definition = getPropertyDefinitionByPath(schema, 'c.inner'); + + expect(definition).to.deep.equal(schema.properties.c.properties.inner); + }); + + it('should return `undefined` if path does not match a pattern', () => { + const definition = getPropertyDefinitionByPath(schema, 'c.inner.NOPE'); + + expect(definition).to.be.undefined(); + }); + + it('should return `undefined` if first item in a path is not an object or array', () => { + const definition = getPropertyDefinitionByPath(schema, 'a.someOther'); + + expect(definition).to.be.undefined(); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition.spec.js b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition.spec.js new file mode 100644 index 00000000000..bfbdc5fd90b --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition.spec.js @@ -0,0 +1,160 @@ +const getDataContractFixture = require('../../../../../lib/test/fixtures/getDataContractFixture'); +const stateTransitionTypes = require('../../../../../lib/stateTransition/stateTransitionTypes'); + +const Identifier = require('../../../../../lib/identifier/Identifier'); +const protocolVersion = require('../../../../../lib/version/protocolVersion'); +const DataContractCreateTransition = require('../../../../../lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition'); +const hash = require('../../../../../lib/util/hash'); +const serializer = require('../../../../../lib/util/serializer'); + +describe('DataContractCreateTransition', () => { + let stateTransition; + let dataContract; + let hashMock; + let encodeMock; + + beforeEach(function beforeEach() { + dataContract = getDataContractFixture(); + + encodeMock = this.sinonSandbox.stub(serializer, 'encode'); + hashMock = this.sinonSandbox.stub(hash, 'hash'); + + stateTransition = new DataContractCreateTransition({ + protocolVersion: protocolVersion.latestVersion, + dataContract: dataContract.toObject(), + entropy: dataContract.getEntropy(), + }); + }); + + afterEach(() => { + encodeMock.restore(); + hashMock.restore(); + }); + + describe('#getProtocolVersion', () => { + it('should return the current protocol version', () => { + const result = stateTransition.getProtocolVersion(); + + expect(result).to.equal(protocolVersion.latestVersion); + }); + }); + + describe('#getType', () => { + it('should return State Transition type', () => { + const result = stateTransition.getType(); + + expect(result).to.equal(stateTransitionTypes.DATA_CONTRACT_CREATE); + }); + }); + + describe('#getDataContract', () => { + it('should return Data Contract', () => { + const result = stateTransition.getDataContract(); + + expect(result.toObject()).to.deep.equal(dataContract.toObject()); + }); + }); + + describe('#toJSON', () => { + it('should return State Transition as plain JS object', () => { + expect(stateTransition.toJSON()).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.DATA_CONTRACT_CREATE, + dataContract: dataContract.toJSON(), + signaturePublicKeyId: undefined, + signature: undefined, + entropy: dataContract.getEntropy().toString('base64'), + }); + }); + }); + + describe('#toBuffer', () => { + it('should return serialized State Transition', () => { + const serializedStateTransition = Buffer.from('123'); + + encodeMock.returns(serializedStateTransition); + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(stateTransition.protocolVersion, 0); + + const result = stateTransition.toBuffer(); + + expect(result).to.deep.equal( + Buffer.concat([protocolVersionUInt32, serializedStateTransition]), + ); + + const dataToEncode = stateTransition.toObject(); + delete dataToEncode.protocolVersion; + + expect(encodeMock.getCall(0).args).to.have.deep.members([ + dataToEncode, + ]); + }); + }); + + describe('#hash', () => { + it('should return State Transition hash as hex', () => { + const serializedDocument = Buffer.from('123'); + const hashedDocument = '456'; + + encodeMock.returns(serializedDocument); + hashMock.returns(hashedDocument); + + const result = stateTransition.hash(); + + expect(result).to.equal(hashedDocument); + + const dataToEncode = stateTransition.toObject(); + delete dataToEncode.protocolVersion; + + expect(encodeMock.getCall(0).args).to.have.deep.members([ + dataToEncode, + ]); + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(stateTransition.protocolVersion, 0); + + expect(hashMock).to.have.been.calledOnceWith( + Buffer.concat([protocolVersionUInt32, serializedDocument]), + ); + }); + }); + + describe('#getOwnerId', () => { + it('should return owner id', async () => { + const result = stateTransition.getOwnerId(); + + expect(result).to.equal(stateTransition.getDataContract().getOwnerId()); + }); + }); + + describe('#getModifiedDataIds', () => { + it('should return ids of affected data contracts', () => { + const result = stateTransition.getModifiedDataIds(); + + expect(result.length).to.be.equal(1); + const contractId = result[0]; + + expect(contractId).to.be.an.instanceOf(Identifier); + expect(contractId).to.be.deep.equal(dataContract.getId()); + }); + }); + + describe('#isDataContractStateTransition', () => { + it('should return true', () => { + expect(stateTransition.isDataContractStateTransition()).to.be.true(); + }); + }); + + describe('#isDocumentStateTransition', () => { + it('should return false', () => { + expect(stateTransition.isDocumentStateTransition()).to.be.false(); + }); + }); + + describe('#isIdentityStateTransition', () => { + it('should return false', () => { + expect(stateTransition.isIdentityStateTransition()).to.be.false(); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractCreateTransition/applyDataContractCreateTransitionFactory.spec.js b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractCreateTransition/applyDataContractCreateTransitionFactory.spec.js new file mode 100644 index 00000000000..2e8994659b4 --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractCreateTransition/applyDataContractCreateTransitionFactory.spec.js @@ -0,0 +1,48 @@ +const DataContractCreateTransition = require( + '../../../../../lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition', +); + +const getDataContractFixture = require('../../../../../lib/test/fixtures/getDataContractFixture'); + +const applyDataContractCreateTransitionFactory = require( + '../../../../../lib/dataContract/stateTransition/DataContractCreateTransition/applyDataContractCreateTransitionFactory', +); + +const createStateRepositoryMock = require('../../../../../lib/test/mocks/createStateRepositoryMock'); +const StateTransitionExecutionContext = require('../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('applyDataContractCreateTransitionFactory', () => { + let stateTransition; + let dataContract; + let stateRepositoryMock; + let applyDataContractCreateTransition; + let executionContext; + + beforeEach(function beforeEach() { + dataContract = getDataContractFixture(); + + stateTransition = new DataContractCreateTransition({ + dataContract: dataContract.toObject(), + entropy: Buffer.alloc(32), + }); + + executionContext = new StateTransitionExecutionContext(); + + stateTransition.setExecutionContext(executionContext); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + + applyDataContractCreateTransition = applyDataContractCreateTransitionFactory( + stateRepositoryMock, + ); + }); + + it('should store a data contract from state transition in the repository', async () => { + await applyDataContractCreateTransition(stateTransition); + + expect(stateRepositoryMock.storeDataContract).to.have.been.calledOnceWithExactly( + stateTransition.getDataContract(), + executionContext, + ); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractCreateTransition/validation/state/validateDataContractCreateTransitionStateFactory.spec.js b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractCreateTransition/validation/state/validateDataContractCreateTransitionStateFactory.spec.js new file mode 100644 index 00000000000..1a7e6d429c4 --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractCreateTransition/validation/state/validateDataContractCreateTransitionStateFactory.spec.js @@ -0,0 +1,85 @@ +const validateDataContractCreateTransitionStateFactory = require('../../../../../../../lib/dataContract/stateTransition/DataContractCreateTransition/validation/state/validateDataContractCreateTransitionStateFactory'); +const DataContractCreateTransition = require('../../../../../../../lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition'); + +const createStateRepositoryMock = require('../../../../../../../lib/test/mocks/createStateRepositoryMock'); +const getDataContractFixture = require('../../../../../../../lib/test/fixtures/getDataContractFixture'); + +const { expectValidationError } = require('../../../../../../../lib/test/expect/expectError'); + +const ValidationResult = require('../../../../../../../lib/validation/ValidationResult'); + +const DataContractAlreadyPresentError = require('../../../../../../../lib/errors/consensus/state/dataContract/DataContractAlreadyPresentError'); +const StateTransitionExecutionContext = require('../../../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('validateDataContractCreateTransitionStateFactory', () => { + let validateDataContractCreateTransitionState; + let dataContract; + let stateTransition; + let stateRepositoryMock; + let executionContext; + + beforeEach(function beforeEach() { + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + + dataContract = getDataContractFixture(); + stateTransition = new DataContractCreateTransition({ + dataContract: dataContract.toObject(), + entropy: dataContract.getEntropy(), + }); + + executionContext = new StateTransitionExecutionContext(); + + stateTransition.setExecutionContext(executionContext); + + validateDataContractCreateTransitionState = validateDataContractCreateTransitionStateFactory( + stateRepositoryMock, + ); + }); + + it('should return invalid result if Data Contract with specified contractId is already exist', async () => { + stateRepositoryMock.fetchDataContract.resolves(dataContract); + + const result = await validateDataContractCreateTransitionState(stateTransition); + + expectValidationError(result, DataContractAlreadyPresentError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4000); + expect(Buffer.isBuffer(error.getDataContractId())).to.be.true(); + expect(error.getDataContractId()).to.deep.equal(dataContract.getId().toBuffer()); + + expect(stateRepositoryMock.fetchDataContract).to.be.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + }); + + it('should return valid result', async () => { + const result = await validateDataContractCreateTransitionState(stateTransition); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchDataContract).to.be.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + }); + + it('should return valid result on dry run', async () => { + stateRepositoryMock.fetchDataContract.resolves(dataContract); + + executionContext.enableDryRun(); + const result = await validateDataContractCreateTransitionState(stateTransition); + executionContext.disableDryRun(); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchDataContract).to.be.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractUpdateTransition/DataContractUpdateTransition.spec.js b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractUpdateTransition/DataContractUpdateTransition.spec.js new file mode 100644 index 00000000000..15d96d50e35 --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractUpdateTransition/DataContractUpdateTransition.spec.js @@ -0,0 +1,158 @@ +const getDataContractFixture = require('../../../../../lib/test/fixtures/getDataContractFixture'); +const stateTransitionTypes = require('../../../../../lib/stateTransition/stateTransitionTypes'); + +const Identifier = require('../../../../../lib/identifier/Identifier'); +const protocolVersion = require('../../../../../lib/version/protocolVersion'); +const DataContractUpdateTransition = require('../../../../../lib/dataContract/stateTransition/DataContractUpdateTransition/DataContractUpdateTransition'); +const hash = require('../../../../../lib/util/hash'); +const serializer = require('../../../../../lib/util/serializer'); + +describe('DataContractUpdateTransition', () => { + let stateTransition; + let dataContract; + let hashMock; + let encodeMock; + + beforeEach(function beforeEach() { + dataContract = getDataContractFixture(); + + encodeMock = this.sinonSandbox.stub(serializer, 'encode'); + hashMock = this.sinonSandbox.stub(hash, 'hash'); + + stateTransition = new DataContractUpdateTransition({ + protocolVersion: protocolVersion.latestVersion, + dataContract: dataContract.toObject(), + }); + }); + + afterEach(() => { + encodeMock.restore(); + hashMock.restore(); + }); + + describe('#getProtocolVersion', () => { + it('should return the current protocol version', () => { + const result = stateTransition.getProtocolVersion(); + + expect(result).to.equal(protocolVersion.latestVersion); + }); + }); + + describe('#getType', () => { + it('should return State Transition type', () => { + const result = stateTransition.getType(); + + expect(result).to.equal(stateTransitionTypes.DATA_CONTRACT_UPDATE); + }); + }); + + describe('#getDataContract', () => { + it('should return Data Contract', () => { + const result = stateTransition.getDataContract(); + + expect(result.toObject()).to.deep.equal(dataContract.toObject()); + }); + }); + + describe('#toJSON', () => { + it('should return State Transition as plain JS object', () => { + expect(stateTransition.toJSON()).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.DATA_CONTRACT_UPDATE, + dataContract: dataContract.toJSON(), + signaturePublicKeyId: undefined, + signature: undefined, + }); + }); + }); + + describe('#toBuffer', () => { + it('should return serialized State Transition', () => { + const serializedStateTransition = Buffer.from('123'); + + encodeMock.returns(serializedStateTransition); + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(stateTransition.protocolVersion, 0); + + const result = stateTransition.toBuffer(); + + expect(result).to.deep.equal( + Buffer.concat([protocolVersionUInt32, serializedStateTransition]), + ); + + const dataToEncode = stateTransition.toObject(); + delete dataToEncode.protocolVersion; + + expect(encodeMock.getCall(0).args).to.have.deep.members([ + dataToEncode, + ]); + }); + }); + + describe('#hash', () => { + it('should return State Transition hash as hex', () => { + const serializedDocument = Buffer.from('123'); + const hashedDocument = '456'; + + encodeMock.returns(serializedDocument); + hashMock.returns(hashedDocument); + + const result = stateTransition.hash(); + + expect(result).to.equal(hashedDocument); + + const dataToEncode = stateTransition.toObject(); + delete dataToEncode.protocolVersion; + + expect(encodeMock.getCall(0).args).to.have.deep.members([ + dataToEncode, + ]); + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(stateTransition.protocolVersion, 0); + + expect(hashMock).to.have.been.calledOnceWith( + Buffer.concat([protocolVersionUInt32, serializedDocument]), + ); + }); + }); + + describe('#getOwnerId', () => { + it('should return owner id', async () => { + const result = stateTransition.getOwnerId(); + + expect(result).to.equal(stateTransition.getDataContract().getOwnerId()); + }); + }); + + describe('#getModifiedDataIds', () => { + it('should return ids of affected data contracts', () => { + const result = stateTransition.getModifiedDataIds(); + + expect(result.length).to.be.equal(1); + const contractId = result[0]; + + expect(contractId).to.be.an.instanceOf(Identifier); + expect(contractId).to.be.deep.equal(dataContract.getId()); + }); + }); + + describe('#isDataContractStateTransition', () => { + it('should return true', () => { + expect(stateTransition.isDataContractStateTransition()).to.be.true(); + }); + }); + + describe('#isDocumentStateTransition', () => { + it('should return false', () => { + expect(stateTransition.isDocumentStateTransition()).to.be.false(); + }); + }); + + describe('#isIdentityStateTransition', () => { + it('should return false', () => { + expect(stateTransition.isIdentityStateTransition()).to.be.false(); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractUpdateTransition/applyDataContractUpdateTransitionFactory.spec.js b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractUpdateTransition/applyDataContractUpdateTransitionFactory.spec.js new file mode 100644 index 00000000000..3f0f6c652db --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractUpdateTransition/applyDataContractUpdateTransitionFactory.spec.js @@ -0,0 +1,47 @@ +const DataContractUpdateTransition = require( + '../../../../../lib/dataContract/stateTransition/DataContractUpdateTransition/DataContractUpdateTransition', +); + +const getDataContractFixture = require('../../../../../lib/test/fixtures/getDataContractFixture'); + +const applyDataContractUpdateTransitionFactory = require( + '../../../../../lib/dataContract/stateTransition/DataContractUpdateTransition/applyDataContractUpdateTransitionFactory', +); + +const createStateRepositoryMock = require('../../../../../lib/test/mocks/createStateRepositoryMock'); +const StateTransitionExecutionContext = require('../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('applyDataContractUpdateTransitionFactory', () => { + let stateTransition; + let dataContract; + let stateRepositoryMock; + let applyDataContractUpdateTransition; + let executionContext; + + beforeEach(function beforeEach() { + dataContract = getDataContractFixture(); + + stateTransition = new DataContractUpdateTransition({ + dataContract: dataContract.toObject(), + }); + + executionContext = new StateTransitionExecutionContext(); + + stateTransition.setExecutionContext(executionContext); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + + applyDataContractUpdateTransition = applyDataContractUpdateTransitionFactory( + stateRepositoryMock, + ); + }); + + it('should store a data contract from state transition in the repository', async () => { + await applyDataContractUpdateTransition(stateTransition); + + expect(stateRepositoryMock.storeDataContract).to.have.been.calledOnceWithExactly( + stateTransition.getDataContract(), + executionContext, + ); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateIndicesAreBackwardCompatible.spec.js b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateIndicesAreBackwardCompatible.spec.js new file mode 100644 index 00000000000..38ed06619b1 --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateIndicesAreBackwardCompatible.spec.js @@ -0,0 +1,121 @@ +const validateIndicesAreBackwardCompatible = require('../../../../../../../lib/dataContract/stateTransition/DataContractUpdateTransition/validation/basic/validateIndicesAreBackwardCompatible'); +const DataContractHaveNewIndexWithOldPropertiesError = require('../../../../../../../lib/errors/consensus/basic/dataContract/DataContractInvalidIndexDefinitionUpdateError'); +const DataContractHaveNewUniqueIndexError = require('../../../../../../../lib/errors/consensus/basic/dataContract/DataContractHaveNewUniqueIndexError'); +const DataContractIndicesChangedError = require('../../../../../../../lib/errors/consensus/basic/dataContract/DataContractUniqueIndicesChangedError'); +const getDataContractFixture = require('../../../../../../../lib/test/fixtures/getDataContractFixture'); +const DataContractInvalidIndexDefinitionUpdateError = require('../../../../../../../lib/errors/consensus/basic/dataContract/DataContractInvalidIndexDefinitionUpdateError'); + +describe('validateIndicesAreBackwardCompatible', () => { + let oldDocumentsSchema; + let newDocumentsSchema; + + beforeEach(() => { + const oldDataContract = getDataContractFixture(); + const newDataContract = getDataContractFixture(); + + newDataContract.getDocumentSchema('indexedDocument').properties.otherName = { + type: 'string', + }; + + newDataContract.getDocumentSchema('indexedDocument').indices.push({ + name: 'index42', + unique: false, + properties: [ + { otherName: 'asc' }, + ], + }); + + newDataContract.getDocumentSchema('indexedDocument').indices.push({ + name: 'index42', + properties: [ + { otherName: 'asc' }, + ], + }); + + oldDocumentsSchema = oldDataContract.getDocuments(); + newDocumentsSchema = newDataContract.getDocuments(); + }); + + it('should return invalid result if some of unique indices have changed', async () => { + newDocumentsSchema.indexedDocument.indices[0].properties[0].lastName = 'asc'; + + const result = validateIndicesAreBackwardCompatible(oldDocumentsSchema, newDocumentsSchema); + + expect(result.isValid()).to.be.false(); + + const error = result.getErrors()[0]; + + expect(error).to.be.an.instanceOf(DataContractIndicesChangedError); + expect(error.getIndexName()).to.equal(newDocumentsSchema.indexedDocument.indices[0].name); + }); + + it('should return invalid result if non-unique index update failed due to changed old properties', async () => { + newDocumentsSchema.indexedDocument.indices[2].properties[0].$id = 'asc'; + + const result = validateIndicesAreBackwardCompatible(oldDocumentsSchema, newDocumentsSchema); + + expect(result.isValid()).to.be.false(); + + const error = result.getErrors()[0]; + + expect(error).to.be.an.instanceOf(DataContractInvalidIndexDefinitionUpdateError); + expect(error.getIndexName()).to.equal(newDocumentsSchema.indexedDocument.indices[2].name); + }); + + it('should return invalid result if non-unique index update failed due old properties used', async () => { + newDocumentsSchema.indexedDocument.indices[2].properties.push({ firstName: 'asc' }); + + const result = validateIndicesAreBackwardCompatible(oldDocumentsSchema, newDocumentsSchema); + + expect(result.isValid()).to.be.false(); + + const error = result.getErrors()[0]; + + expect(error).to.be.an.instanceOf(DataContractInvalidIndexDefinitionUpdateError); + expect(error.getIndexName()).to.equal(newDocumentsSchema.indexedDocument.indices[2].name); + }); + + it('should return invalid result if one of new indices contains old properties in the wrong order', async () => { + newDocumentsSchema.indexedDocument.indices.push({ + name: 'index_other', + properties: [ + { firstName: 'asc' }, + { $ownerId: 'asc' }, + ], + }); + + const result = validateIndicesAreBackwardCompatible(oldDocumentsSchema, newDocumentsSchema); + + expect(result.isValid()).to.be.false(); + + const error = result.getErrors()[0]; + + expect(error).to.be.an.instanceOf(DataContractHaveNewIndexWithOldPropertiesError); + expect(error.getIndexName()).to.equal('index_other'); + }); + + it('should return invalid result if one of new indices is unique', async () => { + newDocumentsSchema.indexedDocument.indices.push({ + name: 'index_other', + properties: [ + { otherName: 'asc' }, + ], + unique: true, + }); + + const result = validateIndicesAreBackwardCompatible(oldDocumentsSchema, newDocumentsSchema); + + expect(result.isValid()).to.be.false(); + + const error = result.getErrors()[0]; + + expect(error).to.be.an.instanceOf(DataContractHaveNewUniqueIndexError); + expect(error.getIndexName()).to.equal('index_other'); + }); + + it('should return valid result if indices are not changed', async () => { + const result = validateIndicesAreBackwardCompatible(oldDocumentsSchema, newDocumentsSchema); + + expect(result.isValid()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractUpdateTransition/validation/state/validateDataContractUpdateTransitionStateFactory.spec.js b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractUpdateTransition/validation/state/validateDataContractUpdateTransitionStateFactory.spec.js new file mode 100644 index 00000000000..438dfb16bf1 --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/stateTransition/DataContractUpdateTransition/validation/state/validateDataContractUpdateTransitionStateFactory.spec.js @@ -0,0 +1,111 @@ +const validateDataContractUpdateTransitionStateFactory = require('../../../../../../../lib/dataContract/stateTransition/DataContractUpdateTransition/validation/state/validateDataContractUpdateTransitionStateFactory'); +const DataContractUpdateTransition = require('../../../../../../../lib/dataContract/stateTransition/DataContractUpdateTransition/DataContractUpdateTransition'); + +const createStateRepositoryMock = require('../../../../../../../lib/test/mocks/createStateRepositoryMock'); +const getDataContractFixture = require('../../../../../../../lib/test/fixtures/getDataContractFixture'); + +const { expectValidationError } = require('../../../../../../../lib/test/expect/expectError'); + +const ValidationResult = require('../../../../../../../lib/validation/ValidationResult'); + +const DataContractNotPresentError = require('../../../../../../../lib/errors/consensus/basic/document/DataContractNotPresentError'); +const InvalidDataContractVersionError = require('../../../../../../../lib/errors/consensus/basic/dataContract/InvalidDataContractVersionError'); +const StateTransitionExecutionContext = require('../../../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('validateDataContractUpdateTransitionStateFactory', () => { + let validateDataContractUpdateTransitionState; + let dataContract; + let stateTransition; + let stateRepositoryMock; + let executionContext; + + beforeEach(function beforeEach() { + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + + dataContract = getDataContractFixture(); + + const updatedRawDataContract = dataContract.toObject(); + + updatedRawDataContract.version += 1; + + stateTransition = new DataContractUpdateTransition({ + dataContract: updatedRawDataContract, + }); + + executionContext = new StateTransitionExecutionContext(); + + stateTransition.setExecutionContext(executionContext); + + stateRepositoryMock.fetchDataContract.resolves(dataContract); + + validateDataContractUpdateTransitionState = validateDataContractUpdateTransitionStateFactory( + stateRepositoryMock, + ); + }); + + it('should return invalid result if Data Contract with specified contractId was not found', async () => { + stateRepositoryMock.fetchDataContract.resolves(undefined); + + const result = await validateDataContractUpdateTransitionState(stateTransition); + + expectValidationError(result, DataContractNotPresentError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1018); + expect(Buffer.isBuffer(error.getDataContractId())).to.be.true(); + expect(error.getDataContractId()).to.deep.equal(dataContract.getId().toBuffer()); + + expect(stateRepositoryMock.fetchDataContract).to.be.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + }); + + it('should return invalid result if Data Contract version is not larger by 1', async () => { + dataContract.version -= 1; + + const result = await validateDataContractUpdateTransitionState(stateTransition); + + expectValidationError(result, InvalidDataContractVersionError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1050); + + expect(stateRepositoryMock.fetchDataContract).to.be.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + }); + + it('should return valid result', async () => { + const result = await validateDataContractUpdateTransitionState(stateTransition); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchDataContract).to.be.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + }); + + it('should return valid result on dry run', async () => { + stateRepositoryMock.fetchDataContract.resolves(undefined); + + executionContext.enableDryRun(); + + const result = await validateDataContractUpdateTransitionState(stateTransition); + + executionContext.disableDryRun(); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchDataContract).to.be.calledOnceWithExactly( + dataContract.getId(), + executionContext, + ); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/validation/validateDataContractMaxDepthFactory.spec.js b/packages/js-dpp/test/unit/dataContract/validation/validateDataContractMaxDepthFactory.spec.js new file mode 100644 index 00000000000..da999dc6f38 --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/validation/validateDataContractMaxDepthFactory.spec.js @@ -0,0 +1,93 @@ +const validateDataContractMaxDepthFactory = require('../../../../lib/dataContract/validation/validateDataContractMaxDepthFactory'); +const ValidationResult = require('../../../../lib/validation/ValidationResult'); +const { expectValidationError } = require('../../../../lib/test/expect/expectError'); +const DataContractMaxDepthExceedError = require('../../../../lib/errors/consensus/basic/dataContract/DataContractMaxDepthExceedError'); +const generateDeepJson = require('../../../../lib/test/utils/generateDeepJson'); +const InvalidJsonSchemaRefError = require('../../../../lib/errors/consensus/basic/dataContract/InvalidJsonSchemaRefError'); + +describe('validateDataContractMaxDepthFactory', () => { + let refParserMock; + let validateDataContractMaxDepth; + let dataContractFixture; + + beforeEach(function beforeEach() { + dataContractFixture = {}; + + refParserMock = { + dereference: this.sinonSandbox.stub(), + }; + + validateDataContractMaxDepth = validateDataContractMaxDepthFactory(refParserMock); + }); + + it('should throw error if depth > MAX_DEPTH', async () => { + dataContractFixture = generateDeepJson(DataContractMaxDepthExceedError.MAX_DEPTH + 1); + + refParserMock.dereference.resolves(dataContractFixture); + + const result = await validateDataContractMaxDepth(dataContractFixture); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataContractMaxDepthExceedError); + expect(error.getCode()).to.equal(1007); + }); + + it('should return valid result if depth = MAX_DEPTH', async () => { + dataContractFixture = generateDeepJson(DataContractMaxDepthExceedError.MAX_DEPTH); + + refParserMock.dereference.resolves(dataContractFixture); + + const result = await validateDataContractMaxDepth(dataContractFixture); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + + it('should throw error if contract contains array with depth > MAX_DEPTH', async () => { + const deepJson = generateDeepJson(DataContractMaxDepthExceedError.MAX_DEPTH + 1); + + dataContractFixture = { + array: [ + 0, deepJson, + ], + }; + + refParserMock.dereference.resolves(dataContractFixture); + + const result = await validateDataContractMaxDepth(dataContractFixture); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataContractMaxDepthExceedError); + expect(error.getCode()).to.equal(1007); + }); + + it('should return error if refParser throws an error', async () => { + const refParserError = new Error('refParser error'); + + refParserMock.dereference.throws(refParserError); + + const result = await validateDataContractMaxDepth(dataContractFixture); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(InvalidJsonSchemaRefError); + expect(error.message).to.equal(`Invalid JSON Schema $ref: ${refParserError.message}`); + }); + + it('should return valid result', async () => { + refParserMock.dereference.resolves(dataContractFixture); + + const result = await validateDataContractMaxDepth(dataContractFixture); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/unit/dataContract/validation/validateDataContractPatternsFactory.spec.js b/packages/js-dpp/test/unit/dataContract/validation/validateDataContractPatternsFactory.spec.js new file mode 100644 index 00000000000..b86eb93fb24 --- /dev/null +++ b/packages/js-dpp/test/unit/dataContract/validation/validateDataContractPatternsFactory.spec.js @@ -0,0 +1,64 @@ +const { getRE2Class } = require('@dashevo/wasm-re2'); + +const validateDataContractPatternsFactory = require('../../../../lib/dataContract/validation/validateDataContractPatternsFactory'); +const { expectValidationError } = require( + '../../../../lib/test/expect/expectError', +); +const IncompatibleRe2PatternError = require('../../../../lib/errors/consensus/basic/dataContract/IncompatibleRe2PatternError'); + +describe('validateDataContractPatternsFactory', () => { + let validateDataContractPatterns; + let RE2; + + before(async () => { + RE2 = await getRE2Class(); + }); + + beforeEach(() => { + validateDataContractPatterns = validateDataContractPatternsFactory(RE2); + }); + + it('should return valid result', () => { + const schema = { + type: 'object', + properties: { + foo: { type: 'integer' }, + bar: { + type: 'string', + pattern: '([a-z]+)+$', + }, + }, + required: ['foo'], + additionalProperties: false, + }; + + const result = validateDataContractPatterns(schema); + + expect(result.isValid()).to.be.true(); + }); + + it('should return invalid result on incompatible pattern', () => { + const schema = { + type: 'object', + properties: { + foo: { type: 'integer' }, + bar: { + type: 'string', + pattern: '^((?!-|_)[a-zA-Z0-9-_]{0,62}[a-zA-Z0-9])$', + }, + }, + required: ['foo'], + additionalProperties: false, + }; + + const result = validateDataContractPatterns(schema); + + expectValidationError(result, IncompatibleRe2PatternError); + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1009); + expect(error.getPattern()).to.equal('^((?!-|_)[a-zA-Z0-9-_]{0,62}[a-zA-Z0-9])$'); + expect(error.getPath()).to.equal('/properties/bar'); + expect(error.getPatternError()).to.be.instanceOf(Error); + }); +}); diff --git a/packages/js-dpp/test/unit/dataTrigger/DataTrigger.spec.js b/packages/js-dpp/test/unit/dataTrigger/DataTrigger.spec.js new file mode 100644 index 00000000000..8cbcf225c45 --- /dev/null +++ b/packages/js-dpp/test/unit/dataTrigger/DataTrigger.spec.js @@ -0,0 +1,132 @@ +const bs58 = require('bs58'); +const AbstractDocumentTransition = require('../../../lib/document/stateTransition/DocumentsBatchTransition/documentTransition/AbstractDocumentTransition'); +const DataTrigger = require('../../../lib/dataTrigger/DataTrigger'); +const DataTriggerExecutionContext = require('../../../lib/dataTrigger/DataTriggerExecutionContext'); +const getDpnsContractFixture = require('../../../lib/test/fixtures/getDpnsContractFixture'); +const DataTriggerExecutionResult = require('../../../lib/dataTrigger/DataTriggerExecutionResult'); +const getDocumentsFixture = require('../../../lib/test/fixtures/getDocumentsFixture'); +const DataTriggerExecutionError = require('../../../lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerExecutionError'); +const DataTriggerInvalidResultError = require('../../../lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerInvalidResultError'); + +describe('DataTrigger', () => { + let dataContractMock; + let context; + let triggerStub; + let document; + let topLevelIdentity; + + beforeEach(function beforeEach() { + triggerStub = this.sinonSandbox.stub().resolves(new DataTriggerExecutionResult()); + dataContractMock = getDpnsContractFixture(); + + ([document] = getDocumentsFixture()); + + context = new DataTriggerExecutionContext( + null, + bs58.decode('5zcXZpTLWFwZjKjq3ME5KVavtZa9YUaZESVzrndehBhq'), + dataContractMock, + ); + + topLevelIdentity = context.getOwnerId(); + }); + + it('should check trigger fields', () => { + const trigger = new DataTrigger( + dataContractMock.getId(), + document.getType(), + AbstractDocumentTransition.ACTIONS.CREATE, + triggerStub, + topLevelIdentity, + ); + + expect(trigger.dataContractId).to.equal(dataContractMock.getId()); + expect(trigger.documentType).to.equal(document.getType()); + expect(trigger.transitionAction).to.equal(AbstractDocumentTransition.ACTIONS.CREATE); + expect(trigger.trigger).to.equal(triggerStub); + expect(trigger.topLevelIdentity).to.equal(topLevelIdentity); + }); + + describe('#execute', () => { + it('should check trigger execution', async () => { + const trigger = new DataTrigger( + dataContractMock.getId(), + document.getType(), + AbstractDocumentTransition.ACTIONS.CREATE, + triggerStub, + topLevelIdentity, + ); + + const result = await trigger.execute(context); + + expect(result).to.be.instanceOf(DataTriggerExecutionResult); + }); + + it('should pass through the result of the trigger function', async () => { + const functionResult = new DataTriggerExecutionResult(); + + const triggerError = new Error('Trigger error'); + + functionResult.addError(triggerError); + + triggerStub.resolves(functionResult); + + const trigger = new DataTrigger( + dataContractMock.getId(), + document.getType(), + AbstractDocumentTransition.ACTIONS.CREATE, + triggerStub, + topLevelIdentity, + ); + + const result = await trigger.execute(document, context); + + expect(result).to.deep.equal(functionResult); + expect(result.getErrors()[0]).to.deep.equal(triggerError); + }); + + it('should return a result with execution error if trigger function have thrown an error', async () => { + const triggerError = new Error('Trigger error'); + + triggerStub.throws(triggerError); + + const trigger = new DataTrigger( + dataContractMock.getId(), + document.getType(), + AbstractDocumentTransition.ACTIONS.CREATE, + triggerStub, + topLevelIdentity, + ); + + const result = await trigger.execute(document, context); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerExecutionError); + + expect(error.getExecutionError()).to.equal(triggerError); + }); + + it('should return a result with invalid result error if trigger function have not returned any result', async () => { + triggerStub.resolves(null); + + const trigger = new DataTrigger( + dataContractMock.getId(), + document.getType(), + AbstractDocumentTransition.ACTIONS.CREATE, + triggerStub, + topLevelIdentity, + ); + + const result = await trigger.execute(document, context); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerInvalidResultError); + expect(error.message).to.equal('Data trigger have not returned any result'); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/dataTrigger/DataTriggerExecutionContext.spec.js b/packages/js-dpp/test/unit/dataTrigger/DataTriggerExecutionContext.spec.js new file mode 100644 index 00000000000..c271862bafd --- /dev/null +++ b/packages/js-dpp/test/unit/dataTrigger/DataTriggerExecutionContext.spec.js @@ -0,0 +1,29 @@ +const bs58 = require('bs58'); +const DataTriggerExecutionContext = require('../../../lib/dataTrigger/DataTriggerExecutionContext'); +const createStateRepositoryMock = require('../../../lib/test/mocks/createStateRepositoryMock'); +const getDpnsContractFixture = require('../../../lib/test/fixtures/getDpnsContractFixture'); +const StateTransitionExecutionContext = require('../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('DataTriggerExecutionContext', () => { + let dataContractMock; + let stateRepositoryMock; + + beforeEach(function beforeEach() { + dataContractMock = getDpnsContractFixture(); + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + }); + + it('should have all getters working', () => { + const ownerId = bs58.decode('5zcXZpTLWFwZjKjq3ME5KVavtZa9YUaZESVzrndehBhq'); + const context = new DataTriggerExecutionContext( + stateRepositoryMock, + ownerId, + dataContractMock, + new StateTransitionExecutionContext(), + ); + + expect(context.getDataContract()).to.be.deep.equal(dataContractMock); + expect(context.getStateRepository()).to.be.deep.equal(stateRepositoryMock); + expect(context.getOwnerId()).to.be.deep.equal(ownerId); + }); +}); diff --git a/packages/js-dpp/test/unit/dataTrigger/dashpayDataTriggers/createContactRequestDataTrigger.spec.js b/packages/js-dpp/test/unit/dataTrigger/dashpayDataTriggers/createContactRequestDataTrigger.spec.js new file mode 100644 index 00000000000..00366315494 --- /dev/null +++ b/packages/js-dpp/test/unit/dataTrigger/dashpayDataTriggers/createContactRequestDataTrigger.spec.js @@ -0,0 +1,109 @@ +const createContactRequestDataTrigger = require('../../../../lib/dataTrigger/dashpayDataTriggers/createContactRequestDataTrigger'); + +const DataTriggerExecutionContext = require('../../../../lib/dataTrigger/DataTriggerExecutionContext'); +const DataTriggerExecutionResult = require('../../../../lib/dataTrigger/DataTriggerExecutionResult'); +const DataTriggerConditionError = require('../../../../lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerConditionError'); + +const createStateRepositoryMock = require('../../../../lib/test/mocks/createStateRepositoryMock'); +const getDocumentTransitionFixture = require('../../../../lib/test/fixtures/getDocumentTransitionsFixture'); + +const getDashPayContractFixture = require('../../../../lib/test/fixtures/getDashPayContractFixture'); +const { getContactRequestDocumentFixture } = require('../../../../lib/test/fixtures/getDashPayDocumentFixture'); +const StateTransitionExecutionContext = require('../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('createContactRequestDataTrigger', () => { + let context; + let dashPayIdentity; + let stateRepositoryMock; + let dataContract; + let contactRequestDocument; + let documentTransition; + + beforeEach(function beforeEach() { + contactRequestDocument = getContactRequestDocumentFixture(); + dataContract = getDashPayContractFixture(); + + [documentTransition] = getDocumentTransitionFixture({ + create: [contactRequestDocument], + }); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchLatestPlatformBlockHeader.resolves({ + coreChainLockedHeight: 42, + }); + + context = new DataTriggerExecutionContext( + stateRepositoryMock, + contactRequestDocument.getOwnerId(), + dataContract, + new StateTransitionExecutionContext(), + ); + + dashPayIdentity = context.getOwnerId(); + }); + + it('should successfully execute if document is valid', async () => { + contactRequestDocument.data.coreHeightCreatedAt = 40; + + [documentTransition] = getDocumentTransitionFixture({ + create: [contactRequestDocument], + }); + + const result = await createContactRequestDataTrigger( + documentTransition, context, dashPayIdentity, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.be.calledOnce(); + expect(result.isOk()).to.be.true(); + }); + + it('should successfully execute if document has no `coreHeightCreatedAt` field', async () => { + const result = await createContactRequestDataTrigger( + documentTransition, context, dashPayIdentity, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.be.not.called(); + expect(result.isOk()).to.be.true(); + }); + + it('should fail with out of window error', async () => { + contactRequestDocument.data.coreHeightCreatedAt = 10; + + [documentTransition] = getDocumentTransitionFixture({ + create: [contactRequestDocument], + }); + + const result = await createContactRequestDataTrigger( + documentTransition, context, dashPayIdentity, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal('Core height 10 is out of block height window from 34 to 50'); + }); + + it('should successfully execute on dry run', async () => { + contactRequestDocument.data.coreHeightCreatedAt = 10; + [documentTransition] = getDocumentTransitionFixture({ + create: [contactRequestDocument], + }); + + context.getStateTransitionExecutionContext().enableDryRun(); + + const result = await createContactRequestDataTrigger( + documentTransition, context, dashPayIdentity, + ); + + context.getStateTransitionExecutionContext().disableDryRun(); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.be.not.called(); + expect(result.isOk()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/unit/dataTrigger/dpnsTriggers/createDomainDataTrigger.spec.js b/packages/js-dpp/test/unit/dataTrigger/dpnsTriggers/createDomainDataTrigger.spec.js new file mode 100644 index 00000000000..53fc8f2047d --- /dev/null +++ b/packages/js-dpp/test/unit/dataTrigger/dpnsTriggers/createDomainDataTrigger.spec.js @@ -0,0 +1,382 @@ +const createDomainDataTrigger = require('../../../../lib/dataTrigger/dpnsTriggers/createDomainDataTrigger'); + +const DataTriggerExecutionContext = require('../../../../lib/dataTrigger/DataTriggerExecutionContext'); +const DataTriggerExecutionResult = require('../../../../lib/dataTrigger/DataTriggerExecutionResult'); + +const { getParentDocumentFixture, getChildDocumentFixture, getTopDocumentFixture } = require('../../../../lib/test/fixtures/getDpnsDocumentFixture'); +const getPreorderDocumentFixture = require('../../../../lib/test/fixtures/getPreorderDocumentFixture'); +const getDpnsContractFixture = require('../../../../lib/test/fixtures/getDpnsContractFixture'); +const getDocumentTransitionFixture = require('../../../../lib/test/fixtures/getDocumentTransitionsFixture'); +const createStateRepositoryMock = require('../../../../lib/test/mocks/createStateRepositoryMock'); + +const { hash } = require('../../../../lib/util/hash'); + +const DataTriggerConditionError = require('../../../../lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerConditionError'); +const Identifier = require('../../../../lib/identifier/Identifier'); +const StateTransitionExecutionContext = require('../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('createDomainDataTrigger', () => { + let parentDocumentTransition; + let childDocumentTransition; + let childDocument; + let parentDocument; + let topDocument; + let context; + let stateRepositoryMock; + let dataContract; + let topLevelIdentity; + let executionContext; + + beforeEach(function beforeEach() { + dataContract = getDpnsContractFixture(); + + topDocument = getTopDocumentFixture(); + parentDocument = getParentDocumentFixture(); + childDocument = getChildDocumentFixture(); + const preorderDocument = getPreorderDocumentFixture(); + + [parentDocumentTransition] = getDocumentTransitionFixture({ + create: [parentDocument], + }); + + [childDocumentTransition] = getDocumentTransitionFixture({ + create: [childDocument], + }); + + const { + preorderSalt, records, normalizedParentDomainName, normalizedLabel, + } = childDocument.getData(); + + let fullDomainName = normalizedLabel; + if (normalizedParentDomainName.length > 0) { + fullDomainName = `${normalizedLabel}.${normalizedParentDomainName}`; + } + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchDocuments.resolves([]); + + const [normalizedParentLabel] = normalizedParentDomainName.split('.'); + const normalizedGrandParentDomainName = normalizedParentDomainName.split('.') + .slice(1) + .join('.'); + + stateRepositoryMock.fetchDocuments + .withArgs( + dataContract.getId(), + childDocument.getType(), + { + where: [ + ['normalizedParentDomainName', '==', normalizedGrandParentDomainName], + ['normalizedLabel', '==', normalizedParentLabel], + ], + }, + ) + .resolves([parentDocument]); + + const saltedDomainHashBuffer = Buffer.concat([ + preorderSalt, + Buffer.from(fullDomainName), + ]); + + const saltedDomainHash = hash(saltedDomainHashBuffer); + + stateRepositoryMock.fetchDocuments + .withArgs( + dataContract.getId(), + 'preorder', + { where: [['saltedDomainHash', '==', saltedDomainHash]] }, + ) + .resolves([preorderDocument.toObject()]); + + stateRepositoryMock.fetchTransaction.resolves(null); + + stateRepositoryMock.fetchTransaction + .withArgs( + records.dashUniqueIdentityId, + ) + .resolves({ confirmations: 10 }); + + executionContext = new StateTransitionExecutionContext(); + + context = new DataTriggerExecutionContext( + stateRepositoryMock, + records.dashUniqueIdentityId, + dataContract, + executionContext, + ); + + topLevelIdentity = context.getOwnerId(); + }); + + it('should successfully execute if document is valid', async () => { + const result = await createDomainDataTrigger( + childDocumentTransition, context, topLevelIdentity, + ); + + expect(result.isOk()).to.be.true(); + }); + + it('should fail with invalid normalizedLabel', async () => { + childDocument = getChildDocumentFixture({ normalizedLabel: childDocument.getData().label }); + stateRepositoryMock.fetchTransaction + .withArgs( + childDocument.getData().records.dashUniqueIdentityId, + ) + .resolves({ confirmations: 10 }); + + [childDocumentTransition] = getDocumentTransitionFixture({ + create: [childDocument], + }); + + const result = await createDomainDataTrigger( + childDocumentTransition, context, topLevelIdentity, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal('Normalized label doesn\'t match label'); + }); + + it('should fail with invalid parent domain', async () => { + childDocument = getChildDocumentFixture({ + label: 'label', + normalizedLabel: 'label', + normalizedParentDomainName: 'parent.invalidname', + }); + + stateRepositoryMock.fetchTransaction + .withArgs( + childDocument.getData().records.dashUniqueIdentityId, + ) + .resolves({ confirmations: 10 }); + + [childDocumentTransition] = getDocumentTransitionFixture({ + create: [childDocument], + }); + + const result = await createDomainDataTrigger( + childDocumentTransition, context, topLevelIdentity, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal('Parent domain is not present'); + + expect(stateRepositoryMock.fetchDocuments).to.have.been.calledOnceWithExactly( + context.getDataContract().getId(), + 'domain', + { + where: [ + ['normalizedParentDomainName', '==', 'invalidname'], + ['normalizedLabel', '==', 'parent'], + ], + }, + executionContext, + ); + }); + + it('should fail with invalid dashUniqueIdentityId', async () => { + const dashUniqueIdentityId = Identifier.from( + Buffer.alloc(32, 5), + ); + + childDocument = getChildDocumentFixture({ + records: { + dashUniqueIdentityId: dashUniqueIdentityId.toBuffer(), + }, + }); + + [childDocumentTransition] = getDocumentTransitionFixture({ + create: [childDocument], + }); + + const result = await createDomainDataTrigger( + childDocumentTransition, context, topLevelIdentity, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal(`ownerId ${childDocument.getOwnerId()} doesn't match dashUniqueIdentityId ${dashUniqueIdentityId}`); + }); + + it('should fail with invalid dashAliasIdentityId', async () => { + const dashUniqueIdentityId = Identifier.from( + Buffer.alloc(32, 2), + ); + + childDocument = getChildDocumentFixture({ + records: { + dashAliasIdentityId: dashUniqueIdentityId.toBuffer(), + }, + }); + + [childDocumentTransition] = getDocumentTransitionFixture({ + create: [childDocument], + }); + + const result = await createDomainDataTrigger( + childDocumentTransition, context, topLevelIdentity, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal(`ownerId ${childDocument.getOwnerId()} doesn't match dashAliasIdentityId ${dashUniqueIdentityId}`); + }); + + it('should fail with preorder document was not found', async () => { + childDocument = getChildDocumentFixture({ + preorderSalt: Buffer.alloc(256, '012fd'), + }); + + [childDocumentTransition] = getDocumentTransitionFixture({ + create: [childDocument], + }); + + const result = await createDomainDataTrigger( + childDocumentTransition, context, topLevelIdentity, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal('preorderDocument was not found'); + }); + + it('should fail with invalid full domain name length', async () => { + childDocument = getChildDocumentFixture({ + normalizedParentDomainName: 'a'.repeat(512), + }); + + [childDocumentTransition] = getDocumentTransitionFixture({ + create: [childDocument], + }); + + const result = await createDomainDataTrigger( + childDocumentTransition, context, topLevelIdentity, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal( + 'Full domain name length can not be more than 253 characters long but got 518', + ); + }); + + it('should fail with identity can\'t create top level domain', async () => { + parentDocumentTransition.data.normalizedParentDomainName = ''; + + topLevelIdentity = Buffer.from('someIdentity'); + + const result = await createDomainDataTrigger( + parentDocumentTransition, context, topLevelIdentity, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal( + 'Can\'t create top level domain for this identity', + ); + }); + + it('should fail with disallowed domain creation', async () => { + parentDocument.ownerId = Buffer.from('newId'); + + const result = await createDomainDataTrigger( + childDocumentTransition, context, topLevelIdentity, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal( + 'The subdomain can be created only by the parent domain owner', + ); + }); + + it('should fail with allowing subdomains for non top level domain', async () => { + childDocument = getChildDocumentFixture({ subdomainRules: { allowSubdomains: true } }); + + [childDocumentTransition] = getDocumentTransitionFixture({ + create: [childDocument], + }); + + const result = await createDomainDataTrigger( + childDocumentTransition, context, topLevelIdentity, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal( + 'Allowing subdomains registration is forbidden for non top level domains', + ); + }); + + it('should allow creating a second level domain by any identity', async () => { + topDocument.ownerId = 'anotherId'; + + stateRepositoryMock.fetchDocuments.resolves([topDocument]); + + const result = await createDomainDataTrigger( + parentDocumentTransition, context, topLevelIdentity, + ); + + expect(result.isOk()).to.be.true(); + }); + + it('should return DataTriggerExecutionResult om dry run', async () => { + context.getStateTransitionExecutionContext().enableDryRun(); + + childDocument = getChildDocumentFixture({ normalizedLabel: childDocument.getData().label }); + stateRepositoryMock.fetchTransaction + .withArgs( + childDocument.getData().records.dashUniqueIdentityId, + ) + .resolves({ confirmations: 10 }); + + [childDocumentTransition] = getDocumentTransitionFixture({ + create: [childDocument], + }); + + const result = await createDomainDataTrigger( + childDocumentTransition, context, topLevelIdentity, + ); + + context.getStateTransitionExecutionContext().disableDryRun(); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/unit/dataTrigger/featureFlagDataTriggers/createFeatureFlagDataTrigger.spec.js b/packages/js-dpp/test/unit/dataTrigger/featureFlagDataTriggers/createFeatureFlagDataTrigger.spec.js new file mode 100644 index 00000000000..283fda9e552 --- /dev/null +++ b/packages/js-dpp/test/unit/dataTrigger/featureFlagDataTriggers/createFeatureFlagDataTrigger.spec.js @@ -0,0 +1,101 @@ +const Long = require('long'); + +const createFeatureFlagDataTrigger = require('../../../../lib/dataTrigger/featureFlagsDataTriggers/createFeatureFlagDataTrigger'); + +const getIdentityFixture = require('../../../../lib/test/fixtures/getIdentityFixture'); +const getFeatureFlagsDocumentsFixture = require('../../../../lib/test/fixtures/getFeatureFlagsDocumentsFixture'); +const getDocumentTransitionsFixture = require('../../../../lib/test/fixtures/getDocumentTransitionsFixture'); +const createStateRepositoryMock = require('../../../../lib/test/mocks/createStateRepositoryMock'); +const DataTriggerExecutionResult = require('../../../../lib/dataTrigger/DataTriggerExecutionResult'); +const DataTriggerConditionError = require('../../../../lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerConditionError'); +const Identifier = require('../../../../lib/identifier/Identifier'); +const StateTransitionExecutionContext = require('../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('createFeatureFlagDataTrigger', () => { + let contextMock; + let stateRepositoryMock; + let documentTransition; + let topLevelIdentityId; + + beforeEach(function beforeEach() { + topLevelIdentityId = getIdentityFixture().getId(); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchLatestPlatformBlockHeader.resolves({ + height: new Long(42), + }); + + const [document] = getFeatureFlagsDocumentsFixture(); + + [documentTransition] = getDocumentTransitionsFixture({ + create: [document], + }); + + const context = new StateTransitionExecutionContext(); + + contextMock = { + getStateRepository: () => stateRepositoryMock, + getOwnerId: this.sinonSandbox.stub(), + getDataContract: () => getFeatureFlagsDocumentsFixture.dataContract, + getStateTransitionExecutionContext: () => context, + }; + contextMock.getOwnerId.returns(topLevelIdentityId); + }); + + it('should return an error if height is lower than block height', async () => { + documentTransition.data.enableAtHeight = 1; + + const result = await createFeatureFlagDataTrigger( + documentTransition, contextMock, topLevelIdentityId, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal('Feature flag cannot be enabled in the past on block 1. Current block height is 42'); + }); + + it('should return an error if owner id is not equal to top level identity id', async () => { + contextMock.getOwnerId.returns(Identifier.from(Buffer.alloc(32, 1))); + + const result = await createFeatureFlagDataTrigger( + documentTransition, contextMock, topLevelIdentityId, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal('This identity can\'t activate selected feature flag'); + }); + + it('should pass', async () => { + const result = await createFeatureFlagDataTrigger( + documentTransition, contextMock, topLevelIdentityId, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.true(); + }); + + it('should pass on dry run', async () => { + contextMock.getStateTransitionExecutionContext().enableDryRun(); + + const result = await createFeatureFlagDataTrigger( + documentTransition, contextMock, topLevelIdentityId, + ); + + contextMock.getStateTransitionExecutionContext().disableDryRun(); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.true(); + + expect(contextMock.getOwnerId).to.not.be.called(); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.not.be.called(); + }); +}); diff --git a/packages/js-dpp/test/unit/dataTrigger/getDataTriggersFactory.spec.js b/packages/js-dpp/test/unit/dataTrigger/getDataTriggersFactory.spec.js new file mode 100644 index 00000000000..9b8442a4031 --- /dev/null +++ b/packages/js-dpp/test/unit/dataTrigger/getDataTriggersFactory.spec.js @@ -0,0 +1,113 @@ +const { + contractId: dpnsContractId, + ownerId: dpnsOwnerId, +} = require('@dashevo/dpns-contract/lib/systemIds'); + +const AbstractDocumentTransition = require('../../../lib/document/stateTransition/DocumentsBatchTransition/documentTransition/AbstractDocumentTransition'); + +const getDataTriggersFactory = require('../../../lib/dataTrigger/getDataTriggersFactory'); + +const getDpnsDocumentFixture = require('../../../lib/test/fixtures/getDpnsDocumentFixture'); + +const DataTrigger = require('../../../lib/dataTrigger/DataTrigger'); + +const createDomainDataTrigger = require('../../../lib/dataTrigger/dpnsTriggers/createDomainDataTrigger'); +const rejectDataTrigger = require('../../../lib/dataTrigger/rejectDataTrigger'); +const Identifier = require('../../../lib/identifier/Identifier'); + +describe('getDataTriggers', () => { + let getDataTriggers; + + let createDocument; + let updateDocument; + let deleteDocument; + + let createTrigger; + let updateTrigger; + let deleteTrigger; + + let updatePreorderTrigger; + let deletePreorderTrigger; + + let dataContractId; + let topLevelIdentity; + + let processMock; + + beforeEach(function beforeEach() { + createDocument = getDpnsDocumentFixture.getChildDocumentFixture(); + updateDocument = getDpnsDocumentFixture.getChildDocumentFixture(); + deleteDocument = getDpnsDocumentFixture.getChildDocumentFixture(); + deleteDocument.data = {}; + + dataContractId = Identifier.from(dpnsContractId); + topLevelIdentity = Identifier.from(dpnsOwnerId); + + createTrigger = new DataTrigger( + dataContractId, 'domain', AbstractDocumentTransition.ACTIONS.CREATE, createDomainDataTrigger, topLevelIdentity, + ); + updateTrigger = new DataTrigger( + dataContractId, 'domain', AbstractDocumentTransition.ACTIONS.REPLACE, rejectDataTrigger, + ); + deleteTrigger = new DataTrigger( + dataContractId, 'domain', AbstractDocumentTransition.ACTIONS.DELETE, rejectDataTrigger, + ); + updatePreorderTrigger = new DataTrigger( + dataContractId, 'preorder', AbstractDocumentTransition.ACTIONS.REPLACE, rejectDataTrigger, + ); + deletePreorderTrigger = new DataTrigger( + dataContractId, 'preorder', AbstractDocumentTransition.ACTIONS.DELETE, rejectDataTrigger, + ); + + processMock = this.sinonSandbox.stub(process, 'env').value({ + DPNS_CONTRACT_ID: dataContractId, + DPNS_TOP_LEVEL_IDENTITY: topLevelIdentity, + }); + + getDataTriggers = getDataTriggersFactory(); + }); + + afterEach(() => { + processMock.restore(); + }); + + it('should return matching triggers', () => { + let result = getDataTriggers( + dataContractId, createDocument.getType(), AbstractDocumentTransition.ACTIONS.CREATE, + ); + + expect(result).to.deep.equal([createTrigger]); + + result = getDataTriggers( + dataContractId, updateDocument.getType(), AbstractDocumentTransition.ACTIONS.REPLACE, + ); + + expect(result).to.deep.equal([updateTrigger]); + + result = getDataTriggers( + dataContractId, deleteDocument.getType(), AbstractDocumentTransition.ACTIONS.DELETE, + ); + + expect(result).to.deep.equal([deleteTrigger]); + + result = getDataTriggers( + dataContractId, 'preorder', AbstractDocumentTransition.ACTIONS.REPLACE, + ); + + expect(result).to.deep.equal([updatePreorderTrigger]); + + result = getDataTriggers( + dataContractId, 'preorder', AbstractDocumentTransition.ACTIONS.DELETE, + ); + + expect(result).to.deep.equal([deletePreorderTrigger]); + }); + + it('should return empty trigger array for any other type except `domain`', () => { + const result = getDataTriggers( + dataContractId, 'otherType', AbstractDocumentTransition.ACTIONS.CREATE, + ); + + expect(result).to.deep.equal([]); + }); +}); diff --git a/packages/js-dpp/test/unit/dataTrigger/rejectDataTrigger.spec.js b/packages/js-dpp/test/unit/dataTrigger/rejectDataTrigger.spec.js new file mode 100644 index 00000000000..8d6bdb25833 --- /dev/null +++ b/packages/js-dpp/test/unit/dataTrigger/rejectDataTrigger.spec.js @@ -0,0 +1,51 @@ +const bs58 = require('bs58'); +const rejectDataTrigger = require('../../../lib/dataTrigger/rejectDataTrigger'); + +const DataTriggerExecutionContext = require('../../../lib/dataTrigger/DataTriggerExecutionContext'); + +const { getChildDocumentFixture } = require('../../../lib/test/fixtures/getDpnsDocumentFixture'); + +const createStateRepositoryMock = require('../../../lib/test/mocks/createStateRepositoryMock'); + +const getDpnsContractFixture = require('../../../lib/test/fixtures/getDpnsContractFixture'); +const getDocumentTransitionFixture = require('../../../lib/test/fixtures/getDocumentTransitionsFixture'); + +const DataTriggerExecutionResult = require('../../../lib/dataTrigger/DataTriggerExecutionResult'); +const StateTransitionExecutionContext = require('../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('rejectDataTrigger', () => { + let documentTransition; + let context; + let stateRepositoryMock; + let dataContract; + + beforeEach(function beforeEach() { + dataContract = getDpnsContractFixture(); + const document = getChildDocumentFixture(); + + [documentTransition] = getDocumentTransitionFixture({ + create: [], + delete: [document], + }); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + context = new DataTriggerExecutionContext( + stateRepositoryMock, + bs58.decode('5zcXZpTLWFwZjKjq3ME5KVavtZa9YUaZESVzrndehBhq'), + dataContract, + new StateTransitionExecutionContext(), + ); + }); + + it('should always fail', async () => { + const result = await rejectDataTrigger(documentTransition, context); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error.message).to.equal('Action is not allowed'); + }); +}); diff --git a/packages/js-dpp/test/unit/dataTrigger/rewardShareDataTriggers/createMasternodeRewardSharesDataTrigger.spec.js b/packages/js-dpp/test/unit/dataTrigger/rewardShareDataTriggers/createMasternodeRewardSharesDataTrigger.spec.js new file mode 100644 index 00000000000..729b223dd41 --- /dev/null +++ b/packages/js-dpp/test/unit/dataTrigger/rewardShareDataTriggers/createMasternodeRewardSharesDataTrigger.spec.js @@ -0,0 +1,197 @@ +const SimplifiedMNListEntry = require('@dashevo/dashcore-lib/lib/deterministicmnlist/SimplifiedMNListEntry'); +const getIdentityFixture = require('../../../../lib/test/fixtures/getIdentityFixture'); +const createStateRepositoryMock = require('../../../../lib/test/mocks/createStateRepositoryMock'); +const getMasternodeRewardShareDocumentsFixture = require('../../../../lib/test/fixtures/getMasternodeRewardShareDocumentsFixture'); +const getDocumentTransitionsFixture = require('../../../../lib/test/fixtures/getDocumentTransitionsFixture'); +const createRewardShareDataTrigger = require('../../../../lib/dataTrigger/rewardShareDataTriggers/createMasternodeRewardSharesDataTrigger'); +const DataTriggerExecutionResult = require('../../../../lib/dataTrigger/DataTriggerExecutionResult'); +const DataTriggerConditionError = require('../../../../lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerConditionError'); +const StateTransitionExecutionContext = require('../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('createMasternodeRewardSharesDataTrigger', () => { + let contextMock; + let stateRepositoryMock; + let documentTransition; + let topLevelIdentityId; + let smlStoreMock; + let smlMock; + let documentsFixture; + let executionContext; + + beforeEach(function beforeEach() { + topLevelIdentityId = Buffer.from('c286807d463b06c7aba3b9a60acf64c1fc03da8c1422005cd9b4293f08cf0562', 'hex'); + + smlMock = { + getQuorum: this.sinonSandbox.stub(), + toSimplifiedMNListDiff: this.sinonSandbox.stub(), + getQuorumsOfType: this.sinonSandbox.stub(), + getValidMasternodesList: this.sinonSandbox.stub().returns([ + new SimplifiedMNListEntry({ + proRegTxHash: 'c286807d463b06c7aba3b9a60acf64c1fc03da8c1422005cd9b4293f08cf0562', + confirmedHash: '4eb56228c535db3b234907113fd41d57bcc7cdcb8e0e00e57590af27ee88c119', + service: '192.168.65.2:20101', + pubKeyOperator: '809519c5f6f3be1c08782ac42ae9a83b6c7205eba43f9a96a4f032ec7a73f1a7c25fa78cce0d6d9c135f7e2c28527179', + votingAddress: 'yXmprXYP51uzfMyndtWwxz96MnkCKkFc9x', + isValid: true, + }), + new SimplifiedMNListEntry({ + proRegTxHash: 'a3e1edc6bd352eeaf0ae58e30781ef4b127854241a3fe7fddf36d5b7e1dc2b3f', + confirmedHash: '27a0b637b56af038c45e2fd1f06c2401c8dadfa28ca5e0d19ca836cc984a8378', + service: '192.168.65.2:20201', + pubKeyOperator: '987a4873caba62cd45a2f7d4aa6d94519ee6753e9bef777c927cb94ade768a542b0ff34a93231d3a92b4e75ffdaa366e', + votingAddress: 'ycL7L4mhYoaZdm9TH85svvpfeKtdfo249u', + isValid: true, + }), + ]), + }; + + documentsFixture = getMasternodeRewardShareDocumentsFixture(); + + smlStoreMock = { + getSMLbyHeight: this.sinonSandbox.stub().returns(smlMock), + getCurrentSML: this.sinonSandbox.stub().returns(smlMock), + }; + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchSMLStore.resolves(smlStoreMock); + stateRepositoryMock.fetchIdentity.resolves(getIdentityFixture()); + stateRepositoryMock.fetchDocuments.resolves([]); + + const [document] = getMasternodeRewardShareDocumentsFixture(); + + [documentTransition] = getDocumentTransitionsFixture({ + create: [document], + }); + + executionContext = new StateTransitionExecutionContext(); + + contextMock = { + getStateRepository: () => stateRepositoryMock, + getOwnerId: this.sinonSandbox.stub(), + getDataContract: () => getMasternodeRewardShareDocumentsFixture.dataContract, + getStateTransitionExecutionContext: () => executionContext, + }; + contextMock.getOwnerId.returns(topLevelIdentityId); + }); + + it('should return an error if percentage > 10000', async () => { + stateRepositoryMock.fetchDocuments.resolves(documentsFixture); + // documentsFixture contains percentage = 500 + documentTransition.data.percentage = 9501; + + const result = await createRewardShareDataTrigger( + documentTransition, contextMock, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal('Percentage can not be more than 10000'); + + expect(stateRepositoryMock.fetchSMLStore).to.be.calledOnce(); + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + documentTransition.data.payToId, + executionContext, + ); + }); + + it('should return an error if payToId does not exist', async () => { + stateRepositoryMock.fetchIdentity.resolves(null); + + const result = await createRewardShareDataTrigger( + documentTransition, contextMock, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal(`Identity ${documentTransition.data.payToId} doesn't exist`); + + expect(stateRepositoryMock.fetchSMLStore).to.be.calledOnce(); + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + documentTransition.data.payToId, + executionContext, + ); + }); + + it('should return an error if ownerId is not a masternode identity', async () => { + contextMock.getOwnerId.returns(getIdentityFixture().getId()); + + const result = await createRewardShareDataTrigger( + documentTransition, contextMock, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal('Only masternode identities can share rewards'); + + expect(stateRepositoryMock.fetchSMLStore).to.be.calledOnce(); + expect(stateRepositoryMock.fetchIdentity).to.be.not.called(); + }); + + it('should pass', async () => { + const result = await createRewardShareDataTrigger( + documentTransition, contextMock, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.true(); + + expect(stateRepositoryMock.fetchSMLStore).to.be.calledOnce(); + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + documentTransition.data.payToId, + executionContext, + ); + }); + + it('should pass on dry run', async () => { + stateRepositoryMock.fetchIdentity.resolves(null); + + executionContext.enableDryRun(); + + const result = await createRewardShareDataTrigger( + documentTransition, contextMock, + ); + executionContext.disableDryRun(); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.true(); + expect(stateRepositoryMock.fetchSMLStore).to.not.be.called(); + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + documentTransition.data.payToId, + executionContext, + ); + }); + + it('should return an error if there are 16 stored shares', async () => { + stateRepositoryMock.fetchDocuments.resolves(new Array(16).fill(0)); + + const result = await createRewardShareDataTrigger( + documentTransition, contextMock, + ); + + expect(result).to.be.an.instanceOf(DataTriggerExecutionResult); + expect(result.isOk()).to.be.false(); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(DataTriggerConditionError); + expect(error.message).to.equal('Reward shares cannot contain more than 16 identities'); + + expect(stateRepositoryMock.fetchSMLStore).to.be.calledOnce(); + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + documentTransition.data.payToId, + executionContext, + ); + }); +}); diff --git a/packages/js-dpp/test/unit/decodeProtocolEntityFactory.spec.js b/packages/js-dpp/test/unit/decodeProtocolEntityFactory.spec.js new file mode 100644 index 00000000000..6e5a1f6fd52 --- /dev/null +++ b/packages/js-dpp/test/unit/decodeProtocolEntityFactory.spec.js @@ -0,0 +1,75 @@ +const decodeProtocolEntityFactory = require('../../lib/decodeProtocolEntityFactory'); +const ProtocolVersionParsingError = require('../../lib/errors/consensus/basic/decode/ProtocolVersionParsingError'); +const SerializedObjectParsingError = require('../../lib/errors/consensus/basic/decode/SerializedObjectParsingError'); + +const { encode } = require('../../lib/util/serializer'); + +describe('decodeProtocolEntityFactory', () => { + let decodeProtocolEntity; + let versionCompatibilityMap; + let parsedProtocolVersion; + let entityBuffer; + let protocolVersionBuffer; + let rawEntity; + let buffer; + + beforeEach(() => { + parsedProtocolVersion = 0; + + protocolVersionBuffer = Buffer.alloc(4); + protocolVersionBuffer.writeUInt32LE(parsedProtocolVersion, 0); + + rawEntity = { test: 'successful' }; + entityBuffer = encode(rawEntity); + + buffer = Buffer.concat([protocolVersionBuffer, entityBuffer]); + + versionCompatibilityMap = { + 0: 0, + 1: 0, + }; + + decodeProtocolEntity = decodeProtocolEntityFactory( + versionCompatibilityMap, + ); + }); + + it('should throw ProtocolVersionParsingError if can\'t parse protocol version', () => { + buffer = Buffer.alloc(0); + + try { + decodeProtocolEntity(buffer); + + expect.fail('should throw ProtocolVersionParsingError'); + } catch (e) { + expect(e).to.be.an.instanceOf(ProtocolVersionParsingError); + + expect(e.getParsingError()).to.be.instanceOf(Error); + expect(e.getCode()).to.equal(1000); + } + }); + + it('should throw SerializedObjectParsingError if entity decoding fails', () => { + entityBuffer = Buffer.alloc(5).fill(1); + + buffer = Buffer.concat([protocolVersionBuffer, entityBuffer]); + + try { + decodeProtocolEntity(buffer); + + expect.fail('should throw SerializedObjectParsingError'); + } catch (e) { + expect(e).to.be.an.instanceOf(SerializedObjectParsingError); + + expect(e.getParsingError()).to.be.an.instanceOf(Error); + expect(e.getCode()).to.equal(1001); + } + }); + + it('should decode protocol version and entity successfully', () => { + const [protocolVersion, actualRawEntity] = decodeProtocolEntity(buffer); + + expect(protocolVersion).to.equal(parsedProtocolVersion); + expect(rawEntity).to.deep.equal(actualRawEntity); + }); +}); diff --git a/packages/js-dpp/test/unit/document/Document.spec.js b/packages/js-dpp/test/unit/document/Document.spec.js new file mode 100644 index 00000000000..1db514c2224 --- /dev/null +++ b/packages/js-dpp/test/unit/document/Document.spec.js @@ -0,0 +1,444 @@ +const DataContractFactory = require('../../../lib/dataContract/DataContractFactory'); + +const generateRandomIdentifier = require('../../../lib/test/utils/generateRandomIdentifier'); + +const DocumentCreateTransition = require( + '../../../lib/document/stateTransition/DocumentsBatchTransition/documentTransition/DocumentCreateTransition', +); + +const Identifier = require('../../../lib/identifier/Identifier'); + +const protocolVersion = require('../../../lib/version/protocolVersion'); +const createDPPMock = require('../../../lib/test/mocks/createDPPMock'); + +const Document = require('../../../lib/document/Document'); + +const hash = require('../../../lib/util/hash'); +const serializer = require('../../../lib/util/serializer'); + +describe('Document', () => { + let hashMock; + let encodeMock; + let rawDocument; + let document; + let dataContract; + + beforeEach(function beforeEach() { + const now = new Date().getTime(); + + const ownerId = generateRandomIdentifier().toBuffer(); + + const dataContractFactory = new DataContractFactory(createDPPMock(), () => {}); + + dataContract = dataContractFactory.create(ownerId, { + test: { + properties: { + name: { + type: 'string', + }, + dataObject: { + type: 'object', + properties: { + binaryObject: { + type: 'object', + properties: { + identifier: { + type: 'array', + byteArray: true, + contentMediaType: Identifier.MEDIA_TYPE, + minItems: 32, + maxItems: 32, + }, + }, + }, + }, + }, + }, + }, + }); + + rawDocument = { + $protocolVersion: protocolVersion.latestVersion, + $id: generateRandomIdentifier(), + $type: 'test', + $dataContractId: dataContract.getId(), + $ownerId: ownerId, + $revision: DocumentCreateTransition.INITIAL_REVISION, + $createdAt: now, + $updatedAt: now, + }; + + document = new Document(rawDocument, dataContract); + + encodeMock = this.sinonSandbox.stub(serializer, 'encode'); + hashMock = this.sinonSandbox.stub(hash, 'hash'); + }); + + afterEach(() => { + encodeMock.restore(); + hashMock.restore(); + }); + + describe('constructor', () => { + it('should create Document with $id and data if present', () => { + const data = { + test: 1, + }; + + rawDocument = { + $id: Buffer.alloc(32), + $type: 'test', + ...data, + }; + + document = new Document(rawDocument, dataContract); + + expect(document.id).to.deep.equal(rawDocument.$id); + }); + + it('should create Document with $type and data if present', () => { + const data = { + test: 1, + }; + + rawDocument = { + $type: 'test', + ...data, + }; + + document = new Document(rawDocument, dataContract); + + expect(document.type).to.equal(rawDocument.$type); + }); + + it('should create Document with $dataContractId and data if present', () => { + const data = { + test: 1, + }; + + rawDocument = { + $dataContractId: generateRandomIdentifier().toBuffer(), + $type: 'test', + ...data, + }; + + document = new Document(rawDocument, dataContract); + + expect(document.dataContractId).to.deep.equal(rawDocument.$dataContractId); + }); + + it('should create Document with $ownerId and data if present', () => { + const data = { + test: 1, + }; + + rawDocument = { + $ownerId: generateRandomIdentifier().toBuffer(), + $type: 'test', + ...data, + }; + + document = new Document(rawDocument, dataContract); + + expect(document.ownerId.toBuffer()).to.deep.equal(rawDocument.$ownerId); + }); + + it('should create Document with undefined action and data if present', () => { + const data = { + test: 1, + }; + + rawDocument = { + $type: 'test', + ...data, + }; + + document = new Document(rawDocument, dataContract); + + expect(document.action).to.equal(undefined); + }); + + it('should create Document with $revision and data if present', () => { + const data = { + test: 1, + }; + + rawDocument = { + $revision: 'test', + $type: 'test', + ...data, + }; + + document = new Document(rawDocument, dataContract); + + expect(document.revision).to.equal(rawDocument.$revision); + }); + + it('should create Document with $createdAt and data if present', async () => { + const data = { + test: 1, + }; + + const createdAt = new Date().getTime(); + + rawDocument = { + $createdAt: createdAt, + $type: 'test', + ...data, + }; + + document = new Document(rawDocument, dataContract); + + expect(document.getCreatedAt().getTime()).to.equal(rawDocument.$createdAt); + }); + + it('should create Document with $updatedAt and data if present', async () => { + const data = { + test: 1, + }; + + const updatedAt = new Date().getTime(); + + rawDocument = { + $updatedAt: updatedAt, + $type: 'test', + ...data, + }; + + document = new Document(rawDocument, dataContract); + + expect(document.getUpdatedAt().getTime()).to.equal(rawDocument.$updatedAt); + }); + }); + + describe('#getId', () => { + it('should return ID', () => { + const id = '123'; + + document.id = id; + + const actualId = document.getId(); + + expect(hashMock).to.have.not.been.called(); + + expect(id).to.equal(actualId); + }); + }); + + describe('#getType', () => { + it('should return $type', () => { + expect(document.getType()).to.equal(rawDocument.$type); + }); + }); + + describe('#getOwnerId', () => { + it('should return $ownerId', () => { + expect(document.getOwnerId()).to.deep.equal(rawDocument.$ownerId); + }); + }); + + describe('#getDataContractId', () => { + it('should return $dataContractId', () => { + expect(document.getOwnerId()).to.deep.equal(rawDocument.$ownerId); + }); + }); + + describe('#setRevision', () => { + it('should set $revision', () => { + const revision = 5; + + const result = document.setRevision(revision); + + expect(result).to.equal(document); + + expect(document.revision).to.equal(revision); + }); + }); + + describe('#getRevision', () => { + it('should return $revision', () => { + const revision = 5; + + document.revision = revision; + + expect(document.getRevision()).to.equal(revision); + }); + }); + + describe('#setData', () => { + it('should call set for each document property', () => { + const data = { + test1: 1, + test2: 2, + }; + + const result = document.setData(data); + + expect(result).to.equal(document); + }); + }); + + describe('#getData', () => { + it('should return all data', () => { + const data = { + test1: 1, + test2: 2, + }; + + document.data = data; + + expect(document.getData()).to.equal(data); + }); + }); + + describe('#set', () => { + it('should set value for specified property name', () => { + const path = 'test[0].$my'; + const value = 2; + + const result = document.set(path, value); + + expect(result).to.equal(document); + }); + + it('should set identifier', () => { + const path = 'dataObject.binaryObject.identifier'; + const buffer = Buffer.alloc(32); + + const result = document.set(path, buffer); + + expect(result).to.equal(document); + }); + + it('should set identifier as part of object', () => { + const buffer = Buffer.alloc(32, 'a'); + const path = 'dataObject.binaryObject'; + const value = { identifier: buffer }; + + const result = document.set(path, value); + + expect(result).to.equal(document); + }); + }); + + describe('#get', () => { + it('should return value for specified property name', () => { + const path = 'dataObject.binaryObject.identifier'; + const buffer = Buffer.alloc(32); + + document.set(path, buffer); + + const result = document.get(path); + + expect(result).to.deep.equal(buffer); + }); + }); + + describe('#toJSON', () => { + it('should return Document as plain JS object', () => { + const jsonDocument = { + ...rawDocument, + $dataContractId: document.dataContractId.toString(), + $id: document.id.toString(), + $ownerId: document.ownerId.toString(), + }; + + expect(document.toJSON()).to.deep.equal(jsonDocument); + }); + }); + + describe('#toBuffer', () => { + it('should return serialized Document', () => { + const serializedDocument = Buffer.from('123'); + + encodeMock.returns(serializedDocument); + + const result = document.toBuffer(); + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(rawDocument.$protocolVersion, 0); + + expect(result).to.deep.equal(Buffer.concat([protocolVersionUInt32, serializedDocument])); + + const documentToEncode = { ...rawDocument }; + delete documentToEncode.$protocolVersion; + + expect(encodeMock.getCall(0).args).to.have.deep.members([ + documentToEncode, + ]); + }); + }); + + describe('#hash', () => { + let toBufferMock; + + beforeEach(function beforeEach() { + toBufferMock = this.sinonSandbox.stub(Document.prototype, 'toBuffer'); + }); + + afterEach(() => { + toBufferMock.restore(); + }); + + it('should return Document hash', () => { + const serializedDocument = '123'; + const hashedDocument = '456'; + + toBufferMock.returns(serializedDocument); + + hashMock.returns(hashedDocument); + + const result = document.hash(); + + expect(result).to.equal(hashedDocument); + + expect(toBufferMock).to.have.been.calledOnce(); + + expect(hashMock).to.have.been.calledOnceWith(serializedDocument); + }); + }); + + describe('#setCreatedAt', () => { + it('should set $createdAt', () => { + const time = new Date(); + + const result = document.setCreatedAt(time); + + expect(result).to.equal(document); + + expect(document.createdAt).to.equal(time); + }); + }); + + describe('#getCreatedAt', () => { + it('should return $createdAt', () => { + const time = new Date(); + + document.createdAt = time; + + expect(document.getCreatedAt()).to.equal(time); + }); + }); + + describe('#setUpdatedAt', () => { + it('should set $updatedAt', () => { + const time = new Date(); + + const result = document.setUpdatedAt(time); + + expect(result).to.equal(document); + + expect(document.updatedAt).to.equal(time); + }); + }); + + describe('#getUpdatedAt', () => { + it('should return $updatedAt', () => { + const time = new Date(); + + document.updatedAt = time; + + expect(document.getUpdatedAt()).to.equal(time); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/document/DocumentFactory.spec.js b/packages/js-dpp/test/unit/document/DocumentFactory.spec.js new file mode 100644 index 00000000000..830ae24e4da --- /dev/null +++ b/packages/js-dpp/test/unit/document/DocumentFactory.spec.js @@ -0,0 +1,376 @@ +const bs58 = require('bs58'); + +const Document = require('../../../lib/document/Document'); + +const DocumentCreateTransition = require('../../../lib/document/stateTransition/DocumentsBatchTransition/documentTransition/DocumentCreateTransition'); + +const getDocumentsFixture = require('../../../lib/test/fixtures/getDocumentsFixture'); +const getDataContractFixture = require('../../../lib/test/fixtures/getDataContractFixture'); +const getDocumentTransitionsFixture = require('../../../lib/test/fixtures/getDocumentTransitionsFixture'); + +const ValidationResult = require('../../../lib/validation/ValidationResult'); + +const Identifier = require('../../../lib/identifier/Identifier'); + +const InvalidDocumentTypeError = require('../../../lib/errors/InvalidDocumentTypeError'); +const InvalidDocumentError = require('../../../lib/document/errors/InvalidDocumentError'); +const InvalidActionNameError = require('../../../lib/document/errors/InvalidActionNameError'); +const NoDocumentsSuppliedError = require('../../../lib/document/errors/NoDocumentsSuppliedError'); +const MismatchOwnerIdsError = require('../../../lib/document/errors/MismatchOwnerIdsError'); +const InvalidInitialRevisionError = require('../../../lib/document/errors/InvalidInitialRevisionError'); +const SerializedObjectParsingError = require('../../../lib/errors/consensus/basic/decode/SerializedObjectParsingError'); + +const generateRandomIdentifier = require('../../../lib/test/utils/generateRandomIdentifier'); +const createDPPMock = require('../../../lib/test/mocks/createDPPMock'); +const SomeConsensusError = require('../../../lib/test/mocks/SomeConsensusError'); +const entropyGenerator = require('../../../lib/util/entropyGenerator'); +const DocumentFactory = require('../../../lib/document/DocumentFactory'); + +describe('DocumentFactory', () => { + let decodeProtocolEntityMock; + let generateEntropyMock; + let validateDocumentMock; + let fetchAndValidateDataContractMock; + let ownerId; + let dataContract; + let document; + let documents; + let rawDocument; + let factory; + let fakeTime; + let fakeTimeDate; + let entropy; + let dppMock; + + beforeEach(function beforeEach() { + ({ ownerId } = getDocumentsFixture); + dataContract = getDataContractFixture(); + + documents = getDocumentsFixture(dataContract); + ([,,, document] = documents); + rawDocument = document.toObject(); + + decodeProtocolEntityMock = this.sinonSandbox.stub(); + generateEntropyMock = this.sinonSandbox.stub(entropyGenerator, 'generate'); + validateDocumentMock = this.sinonSandbox.stub(); + + validateDocumentMock.returns(new ValidationResult()); + + entropy = bs58.decode('789'); + + generateEntropyMock.returns(entropy); + + const fetchContractResult = new ValidationResult(); + fetchContractResult.setData(dataContract); + + fetchAndValidateDataContractMock = this.sinonSandbox.stub().returns(fetchContractResult); + + dppMock = createDPPMock(); + + factory = new DocumentFactory( + dppMock, + validateDocumentMock, + fetchAndValidateDataContractMock, + decodeProtocolEntityMock, + ); + + fakeTimeDate = new Date(); + fakeTime = this.sinonSandbox.useFakeTimers(fakeTimeDate); + }); + + afterEach(() => { + fakeTime.reset(); + generateEntropyMock.restore(); + }); + + describe('create', () => { + it('should return new Document with specified type and data', () => { + const contractId = bs58.decode('FQco85WbwNgb5ix8QQAH6wurMcgEC5ENSCv5ixG9cj12'); + const name = 'Cutie'; + + ownerId = bs58.decode('5zcXZpTLWFwZjKjq3ME5KVavtZa9YUaZESVzrndehBhq'); + dataContract.id = Identifier.from(contractId); + + const newDocument = factory.create( + dataContract, + ownerId, + rawDocument.$type, + { name }, + ); + + expect(newDocument).to.be.an.instanceOf(Document); + + expect(newDocument.getType()).to.equal(rawDocument.$type); + + expect(newDocument.get('name')).to.equal(name); + + expect(newDocument.getDataContractId().toBuffer()).to.deep.equal(contractId); + expect(newDocument.getOwnerId().toBuffer()).to.deep.equal(ownerId); + + expect(generateEntropyMock).to.have.been.calledOnce(); + expect(newDocument.getEntropy()).to.deep.equal(entropy); + + expect(newDocument.getRevision()).to.equal(DocumentCreateTransition.INITIAL_REVISION); + + expect(newDocument.getId()).to.deep.equal(bs58.decode('B99gjrjq6R1FXwGUQnoP7VrmCDDT1PbKprUNzjVbxXfz')); + + expect(newDocument.getCreatedAt().getTime()).to.be.equal(fakeTimeDate.getTime()); + expect(newDocument.getCreatedAt().getTime()).to.equal(newDocument.getUpdatedAt().getTime()); + }); + + it('should throw an error if type is not defined', () => { + const type = 'wrong'; + + try { + factory.create(dataContract, ownerId, type); + + expect.fail('InvalidDocumentTypeError should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidDocumentTypeError); + expect(e.getType()).to.equal(type); + expect(e.getDataContract()).to.equal(dataContract); + } + }); + + it('should throw an error if validation faled', () => { + const error = new Error('validation failed'); + const validationResult = new ValidationResult(); + validationResult.addError(error); + + validateDocumentMock.returns(validationResult); + + try { + factory.create(dataContract, ownerId, rawDocument.$type); + + expect.fail('InvalidDocumentError should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidDocumentError); + } + }); + }); + + describe('createFromObject', () => { + it('should return new Data Contract with data from passed object', async () => { + validateDocumentMock.returns(new ValidationResult()); + + const result = await factory.createFromObject(rawDocument); + + expect(result).to.be.an.instanceOf(Document); + expect(result.toObject()).to.deep.equal(document.toObject()); + + expect(fetchAndValidateDataContractMock).to.have.been.calledOnceWithExactly(rawDocument); + + expect(validateDocumentMock).to.have.been.calledOnceWithExactly( + rawDocument, dataContract, + ); + }); + + it('should return new Document without validation if "skipValidation" option is passed', async function it() { + const resultMock = { + isValid: () => true, + merge: this.sinonSandbox.stub(), + getData: () => getDataContractFixture(), + }; + + fetchAndValidateDataContractMock.resolves(resultMock); + + const result = await factory.createFromObject(rawDocument, { skipValidation: true }); + + expect(result).to.be.an.instanceOf(Document); + expect(result.toObject()).to.deep.equal(document.toObject()); + + expect(fetchAndValidateDataContractMock).to.have.been.calledOnceWithExactly(rawDocument); + expect(validateDocumentMock).to.have.not.been.called(); + expect(resultMock.merge).to.have.not.been.called(); + }); + + it('should throw InvalidDocumentError if passed object is not valid', async () => { + const validationError = new SomeConsensusError('test'); + + validateDocumentMock.returns( + new ValidationResult([validationError]), + ); + + try { + await factory.createFromObject(rawDocument); + + expect.fail('InvalidDocumentError should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidDocumentError); + + expect(e.getErrors()).to.have.length(1); + expect(e.getRawDocument()).to.equal(rawDocument); + + const [consensusError] = e.getErrors(); + expect(consensusError).to.equal(validationError); + + expect(fetchAndValidateDataContractMock).to.have.been.calledOnceWithExactly(rawDocument); + expect(validateDocumentMock).to.have.been.calledOnceWithExactly(rawDocument, dataContract); + } + }); + + it('should throw InvalidDocumentError if Data Contract is not valid', async () => { + const fetchContractError = new SomeConsensusError('error'); + + fetchAndValidateDataContractMock.returns( + new ValidationResult([fetchContractError]), + ); + + try { + await factory.createFromObject(rawDocument); + + expect.fail('InvalidDocumentError should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidDocumentError); + + expect(e.getErrors()).to.have.length(1); + expect(e.getRawDocument()).to.equal(rawDocument); + + const [consensusError] = e.getErrors(); + + expect(consensusError).to.equal(fetchContractError); + + expect(fetchAndValidateDataContractMock).to.have.been.calledOnceWith(rawDocument); + expect(validateDocumentMock).to.have.not.been.called(); + } + }); + }); + + describe('createFromBuffer', () => { + let serializedDocument; + + beforeEach(function beforeEach() { + this.sinonSandbox.stub(factory, 'createFromObject'); + // eslint-disable-next-line prefer-destructuring + document = documents[8]; // document with binary fields + + serializedDocument = document.toBuffer(); + rawDocument = document.toObject(); + }); + + afterEach(() => { + factory.createFromObject.restore(); + }); + + it('should return new Document from serialized one', async () => { + decodeProtocolEntityMock.returns([rawDocument.$protocolVersion, rawDocument]); + + factory.createFromObject.returns(document); + + const result = await factory.createFromBuffer(serializedDocument); + + expect(result).to.equal(document); + + expect(factory.createFromObject).to.have.been.calledOnceWith(rawDocument); + + expect(decodeProtocolEntityMock).to.have.been.calledOnceWith( + serializedDocument, + ); + }); + + it('should throw InvalidDocumentError if the decoding fails with consensus error', async () => { + const parsingError = new SerializedObjectParsingError( + serializedDocument, + new Error(), + ); + + decodeProtocolEntityMock.throws(parsingError); + + try { + await factory.createFromBuffer(serializedDocument); + + expect.fail('should throw InvalidDocumentError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidDocumentError); + + const [innerError] = e.getErrors(); + expect(innerError).to.equal(parsingError); + } + }); + + it('should throw an error if decoding fails with any other error', async () => { + const parsingError = new Error('Something failed during parsing'); + + decodeProtocolEntityMock.throws(parsingError); + + try { + await factory.createFromBuffer(serializedDocument); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.equal(parsingError); + } + }); + }); + + describe('createStateTransition', () => { + it('should throw and error if documents have unknown action', () => { + try { + factory.createStateTransition({ + unknown: documents, + }); + expect.fail('Error was not thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidActionNameError); + expect(e.getActions()).to.have.deep.members(['unknown']); + } + }); + + it('should throw and error if no documents were supplied', () => { + try { + factory.createStateTransition({}); + expect.fail('Error was not thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(NoDocumentsSuppliedError); + } + }); + + it('should throw and error if documents have mixed owner ids', () => { + documents[0].ownerId = generateRandomIdentifier().toBuffer(); + try { + factory.createStateTransition({ + create: documents, + }); + expect.fail('Error was not thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(MismatchOwnerIdsError); + expect(e.getDocuments()).to.have.deep.members(documents); + } + }); + + it('should throw and error if create documents have invalid initial version', () => { + documents[0].setRevision(3); + try { + factory.createStateTransition({ + create: documents, + }); + expect.fail('Error was not thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidInitialRevisionError); + expect(e.getDocument()).to.deep.equal(documents[0]); + } + }); + + it('should create DocumentsBatchTransition with passed documents', () => { + const [newDocument] = getDocumentsFixture(dataContract); + + fakeTime.tick(1000); + + const stateTransition = factory.createStateTransition({ + create: documents, + replace: [newDocument], + }); + + const expectedTransitions = getDocumentTransitionsFixture({ + create: documents, + replace: [newDocument], + }); + + expectedTransitions.slice(-1).updatedAt = new Date(); + + expect(stateTransition.getTransitions()).to.deep.equal( + expectedTransitions, + ); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/document/errors/InvalidDocumentError.spec.js b/packages/js-dpp/test/unit/document/errors/InvalidDocumentError.spec.js new file mode 100644 index 00000000000..c3d65e9d0dd --- /dev/null +++ b/packages/js-dpp/test/unit/document/errors/InvalidDocumentError.spec.js @@ -0,0 +1,46 @@ +const InvalidDocumentError = require('../../../../lib/document/errors/InvalidDocumentError'); +const getDocumentsFixture = require('../../../../lib/test/fixtures/getDocumentsFixture'); + +describe('InvalidDocumentError', () => { + let rawDocument; + let error; + + beforeEach(() => { + error = new Error('Some error'); + + const [document] = getDocumentsFixture(); + rawDocument = document.toObject(); + }); + + it('should return errors', () => { + const errors = [error]; + + const invalidDocumentError = new InvalidDocumentError(errors, rawDocument); + + expect(invalidDocumentError.getErrors()).to.deep.equal(errors); + }); + + it('should return Document', async () => { + const errors = [error]; + + const invalidDocumentError = new InvalidDocumentError(errors, rawDocument); + + expect(invalidDocumentError.getRawDocument()).to.deep.equal(rawDocument); + }); + + it('should contain message for 1 error', async () => { + const errors = [error]; + + const invalidDocumentError = new InvalidDocumentError(errors, rawDocument); + + expect(invalidDocumentError.message).to.equal(`Invalid Document: "${error.message}"`); + }); + + it('should contain message for multiple errors', async () => { + const errors = [error, error]; + + const invalidDocumentError = new InvalidDocumentError(errors, rawDocument); + + expect(invalidDocumentError.message).to.equal(`Invalid Document: "${error.message}" and 1 more`); + }); +}); diff --git a/packages/js-dpp/test/unit/document/fetchAndValidateDataContractFactory.spec.js b/packages/js-dpp/test/unit/document/fetchAndValidateDataContractFactory.spec.js new file mode 100644 index 00000000000..7cb112f34ac --- /dev/null +++ b/packages/js-dpp/test/unit/document/fetchAndValidateDataContractFactory.spec.js @@ -0,0 +1,66 @@ +const fetchAndValidateDataContractFactory = require('../../../lib/document/fetchAndValidateDataContractFactory'); + +const createStateRepositoryMock = require('../../../lib/test/mocks/createStateRepositoryMock'); + +const getDocumentsFixture = require('../../../lib/test/fixtures/getDocumentsFixture'); +const getDataContractFixture = require('../../../lib/test/fixtures/getDataContractFixture'); + +const ValidationResult = require('../../../lib/validation/ValidationResult'); + +const MissingDataContractIdError = require('../../../lib/errors/consensus/basic/document/MissingDataContractIdError'); +const DataContractNotPresentError = require('../../../lib/errors/consensus/basic/document/DataContractNotPresentError'); + +const { expectValidationError } = require('../../../lib/test/expect/expectError'); + +describe('fetchAndValidateDataContractFactory', () => { + let stateRepositoryMock; + let fetchAndValidateDataContract; + let rawDocument; + + beforeEach(function beforeEach() { + const dataContract = getDataContractFixture(); + + const [document] = getDocumentsFixture(dataContract); + rawDocument = document.toObject(); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + + stateRepositoryMock.fetchDataContract.resolves(dataContract); + + fetchAndValidateDataContract = fetchAndValidateDataContractFactory( + stateRepositoryMock, + ); + }); + + it('should return with invalid result if $dataContractId is not present', async () => { + delete rawDocument.$dataContractId; + + const result = await fetchAndValidateDataContract(rawDocument); + + expectValidationError(result, MissingDataContractIdError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1025); + }); + + it('should return with invalid result if Data Contract is not present', async () => { + stateRepositoryMock.fetchDataContract.resolves(null); + + const result = await fetchAndValidateDataContract(rawDocument); + + expectValidationError(result, DataContractNotPresentError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1018); + expect(error.getDataContractId()).to.deep.equal(rawDocument.$dataContractId); + }); + + it('should return valid result', async () => { + const result = await fetchAndValidateDataContract(rawDocument); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/DocumentsBatchTransition.spec.js b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/DocumentsBatchTransition.spec.js new file mode 100644 index 00000000000..dcd0e1c36b1 --- /dev/null +++ b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/DocumentsBatchTransition.spec.js @@ -0,0 +1,168 @@ +const getDataContractFixture = require('../../../../../lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('../../../../../lib/test/fixtures/getDocumentsFixture'); +const stateTransitionTypes = require('../../../../../lib/stateTransition/stateTransitionTypes'); +const createDPPMock = require('../../../../../lib/test/mocks/createDPPMock'); +const protocolVersion = require('../../../../../lib/version/protocolVersion'); +const DocumentFactory = require('../../../../../lib/document/DocumentFactory'); +const serializer = require('../../../../../lib/util/serializer'); +const hash = require('../../../../../lib/util/hash'); + +describe('DocumentsBatchTransition', () => { + let stateTransition; + let documents; + let hashMock; + let encodeMock; + let dataContract; + + beforeEach(function beforeEach() { + dataContract = getDataContractFixture(); + documents = getDocumentsFixture(dataContract); + + encodeMock = this.sinonSandbox.stub(serializer, 'encode'); + hashMock = this.sinonSandbox.stub(hash, 'hash'); + + const factory = new DocumentFactory(createDPPMock(), undefined, undefined); + stateTransition = factory.createStateTransition({ + create: documents, + }); + }); + + afterEach(() => { + encodeMock.restore(); + hashMock.restore(); + }); + + describe('#getProtocolVersion', () => { + it('should return the current protocol version', () => { + const result = stateTransition.getProtocolVersion(); + + expect(result).to.equal(protocolVersion.latestVersion); + }); + }); + + describe('#getType', () => { + it('should return State Transition type', () => { + const result = stateTransition.getType(); + + expect(result).to.equal(stateTransitionTypes.DOCUMENTS_BATCH); + }); + }); + + describe('#getTransitions', () => { + it('should return document transitions', () => { + const result = stateTransition.getTransitions(); + + expect(result).to.equal(stateTransition.transitions); + }); + }); + + describe('#toJSON', () => { + it('should return State Transition as JSON', () => { + expect(stateTransition.toJSON()).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.DOCUMENTS_BATCH, + ownerId: documents[0].getOwnerId().toString(), + transitions: stateTransition.getTransitions().map((d) => d.toJSON()), + signaturePublicKeyId: undefined, + signature: undefined, + }); + }); + }); + + describe('#toObject', () => { + it('should return State Transition as plain object', () => { + expect(stateTransition.toObject()).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.DOCUMENTS_BATCH, + ownerId: documents[0].getOwnerId(), + transitions: stateTransition.getTransitions().map((d) => d.toObject()), + signaturePublicKeyId: undefined, + signature: undefined, + }); + }); + }); + + describe('#toBuffer', () => { + it('should return serialized Documents State Transition', () => { + const serializedStateTransition = Buffer.from('123'); + + encodeMock.returns(serializedStateTransition); + + const result = stateTransition.toBuffer(); + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(stateTransition.protocolVersion, 0); + + expect(result).to.deep.equal( + Buffer.concat([protocolVersionUInt32, serializedStateTransition]), + ); + + const dataToEncode = stateTransition.toObject(); + delete dataToEncode.protocolVersion; + + expect(encodeMock).to.have.been.calledOnceWith(dataToEncode); + }); + }); + + describe('#hash', () => { + it('should return Documents State Transition hash as hex', () => { + const serializedDocument = Buffer.from('123'); + const hashedDocument = '456'; + + encodeMock.returns(serializedDocument); + hashMock.returns(hashedDocument); + + const result = stateTransition.hash(); + + expect(result).to.equal(hashedDocument); + + const dataToEncode = stateTransition.toObject(); + delete dataToEncode.protocolVersion; + + expect(encodeMock).to.have.been.calledOnceWith(dataToEncode); + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(stateTransition.protocolVersion, 0); + + expect(hashMock).to.have.been.calledOnceWith( + Buffer.concat([protocolVersionUInt32, serializedDocument]), + ); + }); + }); + + describe('#getOwnerId', () => { + it('should return owner id', async () => { + const result = stateTransition.getOwnerId(); + + expect(result).to.deep.equal(getDocumentsFixture.ownerId); + }); + }); + + describe('#getModifiedDataIds', () => { + it('should return ids of affected documents', () => { + const expectedIds = documents.map((doc) => doc.getId()); + const result = stateTransition.getModifiedDataIds(); + + expect(result.length).to.be.equal(10); + expect(result).to.be.deep.equal(expectedIds); + }); + }); + + describe('#isDataContractStateTransition', () => { + it('should return false', () => { + expect(stateTransition.isDataContractStateTransition()).to.be.false(); + }); + }); + + describe('#isDocumentStateTransition', () => { + it('should return true', () => { + expect(stateTransition.isDocumentStateTransition()).to.be.true(); + }); + }); + + describe('#isIdentityStateTransition', () => { + it('should return false', () => { + expect(stateTransition.isIdentityStateTransition()).to.be.false(); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/applyDocumentsBatchTransitionFactory.spec.js b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/applyDocumentsBatchTransitionFactory.spec.js new file mode 100644 index 00000000000..a35e1f2039b --- /dev/null +++ b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/applyDocumentsBatchTransitionFactory.spec.js @@ -0,0 +1,171 @@ +const Document = require('../../../../../lib/document/Document'); +const DocumentsBatchTransition = require( + '../../../../../lib/document/stateTransition/DocumentsBatchTransition/DocumentsBatchTransition', +); + +const applyDocumentsBatchTransitionFactory = require( + '../../../../../lib/document/stateTransition/DocumentsBatchTransition/applyDocumentsBatchTransitionFactory', +); + +const getDataContractFixture = require('../../../../../lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('../../../../../lib/test/fixtures/getDocumentsFixture'); +const getDocumentTransitionsFixture = require( + '../../../../../lib/test/fixtures/getDocumentTransitionsFixture', +); + +const createStateRepositoryMock = require('../../../../../lib/test/mocks/createStateRepositoryMock'); + +const protocolVersion = require('../../../../../lib/version/protocolVersion'); +const StateTransitionExecutionContext = require('../../../../../lib/stateTransition/StateTransitionExecutionContext'); +const DocumentNotProvidedError = require('../../../../../lib/document/errors/DocumentNotProvidedError'); + +describe('applyDocumentsBatchTransitionFactory', () => { + let documents; + let dataContract; + let documentTransitions; + let ownerId; + let replaceDocument; + let stateTransition; + let documentsFixture; + let applyDocumentsBatchTransition; + let stateRepositoryMock; + let fetchDocumentsMock; + let executionContext; + + beforeEach(function beforeEach() { + dataContract = getDataContractFixture(); + documentsFixture = getDocumentsFixture(dataContract); + + ownerId = getDocumentsFixture.ownerId; + + replaceDocument = new Document({ + ...documentsFixture[1].toObject(), + lastName: 'NotSoShiny', + }, dataContract); + + documents = [replaceDocument, documentsFixture[2]]; + + documentTransitions = getDocumentTransitionsFixture({ + create: [documentsFixture[0]], + replace: [documents[0]], + delete: [documents[1]], + }); + + stateTransition = new DocumentsBatchTransition({ + protocolVersion: protocolVersion.latestVersion, + ownerId, + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + executionContext = new StateTransitionExecutionContext(); + + stateTransition.setExecutionContext(executionContext); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchDataContract.resolves(dataContract); + stateRepositoryMock.fetchLatestPlatformBlockHeader.resolves({ + time: { + seconds: 86400, + }, + }); + + fetchDocumentsMock = this.sinonSandbox.stub(); + fetchDocumentsMock.resolves([ + replaceDocument, + ]); + + applyDocumentsBatchTransition = applyDocumentsBatchTransitionFactory( + stateRepositoryMock, + fetchDocumentsMock, + ); + }); + + it('should call `store`, `replace` and `remove` functions for specific type of transitions', async () => { + await applyDocumentsBatchTransition(stateTransition); + + const replaceDocumentTransition = documentTransitions[1]; + + expect(fetchDocumentsMock).to.have.been.calledOnceWithExactly( + [replaceDocumentTransition], + executionContext, + ); + + expect(stateRepositoryMock.createDocument).to.have.been.calledOnce(); + expect(stateRepositoryMock.updateDocument).to.have.been.calledOnce(); + + const callsArgs = [ + ...stateRepositoryMock.createDocument.getCall(0).args, + ...stateRepositoryMock.updateDocument.getCall(0).args, + ]; + + expect(callsArgs).to.have.deep.members([ + documentsFixture[0], + documents[0], + executionContext, + executionContext, + ]); + + expect(stateRepositoryMock.removeDocument).to.have.been.calledOnceWithExactly( + documentTransitions[2].getDataContract(), + documentTransitions[2].getType(), + documentTransitions[2].getId(), + executionContext, + ); + }); + + it('should throw an error if document was not provided for a replacement', async () => { + fetchDocumentsMock.resolves([]); + + const replaceDocumentTransition = documentTransitions[1]; + + try { + await applyDocumentsBatchTransition(stateTransition); + expect.fail('Error was not thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(DocumentNotProvidedError); + expect(e.getDocumentTransition()).to.deep.equal(replaceDocumentTransition); + } + }); + + it('should call `replace` functions on dry run', async () => { + documentTransitions = getDocumentTransitionsFixture({ + create: [], + replace: [documents[0]], + delete: [], + }); + + stateTransition = new DocumentsBatchTransition({ + protocolVersion: protocolVersion.latestVersion, + ownerId, + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + stateTransition.getExecutionContext().enableDryRun(); + + await applyDocumentsBatchTransition(stateTransition); + + stateTransition.getExecutionContext().disableDryRun(); + + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.have.been.calledOnceWith(); + + const [documentTransition] = stateTransition.getTransitions(); + const newDocument = new Document({ + $protocolVersion: stateTransition.getProtocolVersion(), + $id: documentTransition.getId(), + $type: documentTransition.getType(), + $dataContractId: documentTransition.getDataContractId(), + $ownerId: stateTransition.getOwnerId(), + $createdAt: 86400 * 1000, + ...documentTransition.getData(), + }, documentTransition.getDataContract()); + + newDocument.setRevision(documentTransition.getRevision()); + newDocument.setData(documentTransition.getData()); + newDocument.setUpdatedAt(documentTransition.getUpdatedAt()); + + expect(stateRepositoryMock.updateDocument).to.have.been.calledOnceWithExactly( + newDocument, + executionContext, + ); + }); +}); diff --git a/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/documentTransition/DocumentCreateTransition.spec.js b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/documentTransition/DocumentCreateTransition.spec.js new file mode 100644 index 00000000000..4eba297f35c --- /dev/null +++ b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/documentTransition/DocumentCreateTransition.spec.js @@ -0,0 +1,25 @@ +const getDocumentTransitionsFixture = require('../../../../../../lib/test/fixtures/getDocumentTransitionsFixture'); + +describe('DocumentCreateTransition', () => { + let documentTransition; + + beforeEach(() => { + [documentTransition] = getDocumentTransitionsFixture(); + }); + + describe('toJSON', () => { + it('should return json representation', () => { + const jsonDocumentTransition = documentTransition.toJSON(); + + expect(jsonDocumentTransition).to.deep.equal({ + $id: documentTransition.getId().toString(), + $type: documentTransition.getType(), + $action: documentTransition.getAction(), + $dataContractId: documentTransition.getDataContractId().toString(), + $entropy: documentTransition.getEntropy().toString('base64'), + $createdAt: documentTransition.getCreatedAt().getTime(), + name: documentTransition.getData().name, + }); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/basic/findDuplicatesById.spec.js b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/basic/findDuplicatesById.spec.js new file mode 100644 index 00000000000..4ffaacb5fa0 --- /dev/null +++ b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/basic/findDuplicatesById.spec.js @@ -0,0 +1,31 @@ +const findDuplicateDocuments = require('../../../../../../../lib/document/stateTransition/DocumentsBatchTransition/validation/basic/findDuplicatesById'); + +const getDocumentTransitionsFixture = require('../../../../../../../lib/test/fixtures/getDocumentTransitionsFixture'); + +describe('findDuplicatesById', () => { + let rawDocumentTransitions; + + beforeEach(() => { + rawDocumentTransitions = getDocumentTransitionsFixture().map((t) => t.toObject()); + }); + + it('should return empty array if there are no duplicated Documents', () => { + const result = findDuplicateDocuments(rawDocumentTransitions); + + expect(result).to.be.an('array'); + expect(result).to.have.lengthOf(0); + }); + + it('should return duplicated Documents', () => { + rawDocumentTransitions.push(rawDocumentTransitions[0]); + + const result = findDuplicateDocuments(rawDocumentTransitions); + + expect(result).to.be.an('array'); + expect(result).to.have.lengthOf(2); + expect(result).to.have.deep.members([ + rawDocumentTransitions[0], + rawDocumentTransitions[0], + ]); + }); +}); diff --git a/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/basic/findDuplicatesByIndices.spec.js b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/basic/findDuplicatesByIndices.spec.js new file mode 100644 index 00000000000..49ba8d4ed42 --- /dev/null +++ b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/basic/findDuplicatesByIndices.spec.js @@ -0,0 +1,122 @@ +const Document = require('../../../../../../../lib/document/Document'); + +const findDuplicateDocumentsByIndices = require('../../../../../../../lib/document/stateTransition/DocumentsBatchTransition/validation/basic/findDuplicatesByIndices'); + +const getDataContractFixture = require('../../../../../../../lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('../../../../../../../lib/test/fixtures/getDocumentsFixture'); +const getDocumentTransitionsFixture = require('../../../../../../../lib/test/fixtures/getDocumentTransitionsFixture'); + +const { generate: generateEntropy } = require('../../../../../../../lib/util/entropyGenerator'); + +describe('findDuplicatesByIndices', () => { + let documents; + let contract; + let documentTransitions; + + beforeEach(() => { + contract = getDataContractFixture(); + contract.setDocumentSchema('nonUniqueIndexDocument', { + indices: [ + { + name: 'ownerIdLastName', + properties: [ + { $ownerId: 'asc' }, + { lastName: 'asc' }, + ], + unique: false, + }, + ], + properties: { + firstName: { + type: 'string', + }, + lastName: { + type: 'string', + }, + }, + required: ['lastName'], + additionalProperties: false, + }); + + contract.setDocumentSchema('singleDocument', { + indices: [ + { + name: 'ownerIdLastName', + properties: [ + { $ownerId: 'asc' }, + { lastName: 'asc' }, + ], + unique: true, + }, + ], + properties: { + firstName: { + type: 'string', + }, + lastName: { + type: 'string', + }, + }, + required: ['lastName'], + additionalProperties: false, + }); + + documents = getDocumentsFixture(contract); + documents.forEach((doc) => { + // eslint-disable-next-line no-param-reassign + doc.dataContract = contract; + // eslint-disable-next-line no-param-reassign + doc.dataContractId = contract.getId(); + }); + + const [, , , william] = documents; + + let document = new Document({ + ...william.toObject(), + $type: 'nonUniqueIndexDocument', + $entropy: generateEntropy(), + }, contract); + + document.setEntropy(generateEntropy()); + + documents.push(document); + + document = new Document({ + ...william.toObject(), + $type: 'singleDocument', + $entropy: generateEntropy(), + }, contract); + + document.setEntropy(generateEntropy()); + + documents.push(document); + + documentTransitions = getDocumentTransitionsFixture({ + create: documents, + }).map((t) => t.toObject()); + }); + + it('should return duplicate documents if they are present', () => { + const [, , , , leon] = documents; + + leon.set('lastName', 'Birkin'); + + documentTransitions = getDocumentTransitionsFixture({ + create: documents, + }).map((t) => t.toObject()); + + const duplicates = findDuplicateDocumentsByIndices(documentTransitions, contract); + expect(duplicates).to.have.deep.members( + [ + documentTransitions[3], + documentTransitions[4], + ], + ); + }); + + it('should return an empty array of there are no duplicates', () => { + const duplicates = findDuplicateDocumentsByIndices(documentTransitions, contract); + + expect(duplicates.length).to.equal(0); + }); +}); diff --git a/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/basic/validatePartialCompoundIndices.spec.js b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/basic/validatePartialCompoundIndices.spec.js new file mode 100644 index 00000000000..de02c49c627 --- /dev/null +++ b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/basic/validatePartialCompoundIndices.spec.js @@ -0,0 +1,75 @@ +const validatePartialCompoundIndices = require('../../../../../../../lib/document/stateTransition/DocumentsBatchTransition/validation/basic/validatePartialCompoundIndices'); +const InconsistentCompoundIndexDataError = require('../../../../../../../lib/errors/consensus/basic/document/InconsistentCompoundIndexDataError'); + +const getDocumentsFixture = require('../../../../../../../lib/test/fixtures/getDocumentsFixture'); +const getContractFixture = require('../../../../../../../lib/test/fixtures/getDataContractFixture'); +const getDocumentTransitionsFixture = require('../../../../../../../lib/test/fixtures/getDocumentTransitionsFixture'); + +const ValidationResult = require('../../../../../../../lib/validation/ValidationResult'); +const { expectValidationError } = require('../../../../../../../lib/test/expect/expectError'); + +describe('validatePartialCompoundIndices', () => { + let documents; + let rawDocumentTransitions; + let dataContract; + let ownerId; + + beforeEach(() => { + dataContract = getContractFixture(); + ownerId = dataContract.getOwnerId(); + }); + + it('should return invalid result if compound index contains not all fields', () => { + const document = getDocumentsFixture(dataContract)[9]; + document.set('lastName', undefined); + + documents = [document]; + rawDocumentTransitions = getDocumentTransitionsFixture({ + create: documents, + }).map((documentTransition) => documentTransition.toObject()); + + const result = validatePartialCompoundIndices(ownerId, rawDocumentTransitions, dataContract); + + expectValidationError(result, InconsistentCompoundIndexDataError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1021); + + const { optionalUniqueIndexedDocument } = dataContract.getDocuments(); + + expect(error.getIndexedProperties()).to.deep.equal( + optionalUniqueIndexedDocument.indices[1].properties.map((i) => Object.keys(i)[0]), + ); + + expect(error.getDocumentType()).to.equal('optionalUniqueIndexedDocument'); + }); + + it('should return valid result if compound index contains no fields', () => { + const document = getDocumentsFixture(dataContract)[8]; + document.setData({ }); + + documents = [document]; + + rawDocumentTransitions = getDocumentTransitionsFixture({ + create: documents, + }).map((documentTransition) => documentTransition.toObject()); + + const result = validatePartialCompoundIndices(ownerId, rawDocumentTransitions, dataContract); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + + it('should return valid result if compound index contains all fields', () => { + documents = [getDocumentsFixture(dataContract)[8]]; + rawDocumentTransitions = getDocumentTransitionsFixture({ + create: documents, + }).map((documentTransition) => documentTransition.toObject()); + + const result = validatePartialCompoundIndices(ownerId, rawDocumentTransitions, dataContract); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/state/fetchDocumentsFactory.spec.js b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/state/fetchDocumentsFactory.spec.js new file mode 100644 index 00000000000..3b1c247c9a9 --- /dev/null +++ b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/state/fetchDocumentsFactory.spec.js @@ -0,0 +1,128 @@ +const getDocumentsFixture = require('../../../../../../../lib/test/fixtures/getDocumentsFixture'); +const getDocumentTransitionsFixture = require('../../../../../../../lib/test/fixtures/getDocumentTransitionsFixture'); + +const fetchDocumentsFactory = require('../../../../../../../lib/document/stateTransition/DocumentsBatchTransition/validation/state/fetchDocumentsFactory'); + +const createStateRepositoryMock = require('../../../../../../../lib/test/mocks/createStateRepositoryMock'); + +const generateRandomIdentifier = require('../../../../../../../lib/test/utils/generateRandomIdentifier'); +const StateTransitionExecutionContext = require('../../../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('fetchDocumentsFactory', () => { + let fetchDocuments; + let stateRepositoryMock; + let documentTransitions; + let documents; + let executionContext; + + beforeEach(function beforeEach() { + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + + fetchDocuments = fetchDocumentsFactory(stateRepositoryMock); + + executionContext = new StateTransitionExecutionContext(); + + documents = getDocumentsFixture().slice(0, 5); + + documentTransitions = getDocumentTransitionsFixture({ + create: documents, + }); + }); + + it('should fetch specified Documents using StateRepository', async () => { + const firstDocumentDataContractId = generateRandomIdentifier().toBuffer(); + + documentTransitions[0].dataContractId = firstDocumentDataContractId; + documents[0].dataContractId = firstDocumentDataContractId; + + stateRepositoryMock.fetchDocuments.withArgs( + documentTransitions[0].getDataContractId(), + documentTransitions[0].getType(), + ).resolves([documents[0]]); + + stateRepositoryMock.fetchDocuments.withArgs( + documentTransitions[1].getDataContractId(), + documentTransitions[1].getType(), + ).resolves([documents[1], documents[2]]); + + stateRepositoryMock.fetchDocuments.withArgs( + documentTransitions[3].getDataContractId(), + documentTransitions[3].getType(), + ).resolves([documents[3], documents[4]]); + + const fetchedDocuments = await fetchDocuments(documentTransitions, executionContext); + + expect(stateRepositoryMock.fetchDocuments).to.have.been.calledThrice(); + + const callArgsOne = [ + documents[0].getDataContractId(), + documents[0].getType(), + { + where: [ + ['$id', 'in', [documents[0].getId()]], + ], + orderBy: [ + [ + '$id', + 'asc', + ], + ], + }, + executionContext, + ]; + + const callArgsTwo = [ + documents[1].getDataContractId(), + documents[1].getType(), + { + where: [ + ['$id', 'in', [ + documents[1].getId(), + documents[2].getId(), + ]], + ], + orderBy: [ + [ + '$id', + 'asc', + ], + ], + }, + executionContext, + ]; + + const callArgsThree = [ + documents[3].getDataContractId(), + documents[3].getType(), + { + where: [ + ['$id', 'in', [ + documents[3].getId(), + documents[4].getId(), + ]], + ], + orderBy: [ + [ + '$id', + 'asc', + ], + ], + }, + executionContext, + ]; + + const callsArgs = []; + for (let i = 0; i < stateRepositoryMock.fetchDocuments.callCount; i++) { + const call = stateRepositoryMock.fetchDocuments.getCall(i); + callsArgs.push(call.args); + } + + expect(callsArgs).to.have.deep.members([ + callArgsOne, + callArgsTwo, + callArgsThree, + ]); + + expect(fetchedDocuments).to.deep.equal(documents); + }); +}); diff --git a/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/state/validateDocumentsBatchTransitionStateFactory.spec.js b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/state/validateDocumentsBatchTransitionStateFactory.spec.js new file mode 100644 index 00000000000..79b451d1481 --- /dev/null +++ b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/state/validateDocumentsBatchTransitionStateFactory.spec.js @@ -0,0 +1,811 @@ +const validateDocumentsBatchTransitionStateFactory = require('../../../../../../../lib/document/stateTransition/DocumentsBatchTransition/validation/state/validateDocumentsBatchTransitionStateFactory'); + +const Document = require('../../../../../../../lib/document/Document'); +const DocumentsBatchTransition = require('../../../../../../../lib/document/stateTransition/DocumentsBatchTransition/DocumentsBatchTransition'); + +const DataTriggerExecutionContext = require('../../../../../../../lib/dataTrigger/DataTriggerExecutionContext'); +const DataTriggerExecutionError = require('../../../../../../../lib/errors/consensus/state/dataContract/dataTrigger/DataTriggerExecutionError'); +const DataTriggerExecutionResult = require('../../../../../../../lib/dataTrigger/DataTriggerExecutionResult'); + +const getDataContractFixture = require('../../../../../../../lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('../../../../../../../lib/test/fixtures/getDocumentsFixture'); +const getDocumentTransitionsFixture = require('../../../../../../../lib/test/fixtures/getDocumentTransitionsFixture'); +const createStateRepositoryMock = require('../../../../../../../lib/test/mocks/createStateRepositoryMock'); + +const ValidationResult = require('../../../../../../../lib/validation/ValidationResult'); + +const { expectValidationError } = require('../../../../../../../lib/test/expect/expectError'); + +const DataContractNotPresentError = require('../../../../../../../lib/errors/DataContractNotPresentError'); + +const DocumentAlreadyPresentError = require('../../../../../../../lib/errors/consensus/state/document/DocumentAlreadyPresentError'); +const DocumentNotFoundError = require('../../../../../../../lib/errors/consensus/state/document/DocumentNotFoundError'); +const InvalidDocumentRevisionError = require('../../../../../../../lib/errors/consensus/state/document/InvalidDocumentRevisionError'); +const InvalidDocumentActionError = require('../../../../../../../lib/document/errors/InvalidDocumentActionError'); +const DocumentOwnerIdMismatchError = require('../../../../../../../lib/errors/consensus/state/document/DocumentOwnerIdMismatchError'); +const DocumentTimestampsMismatchError = require('../../../../../../../lib/errors/consensus/state/document/DocumentTimestampsMismatchError'); +const DocumentTimestampWindowViolationError = require('../../../../../../../lib/errors/consensus/state/document/DocumentTimestampWindowViolationError'); + +const generateRandomIdentifier = require('../../../../../../../lib/test/utils/generateRandomIdentifier'); +const SomeConsensusError = require('../../../../../../../lib/test/mocks/SomeConsensusError'); +const StateTransitionExecutionContext = require('../../../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('validateDocumentsBatchTransitionStateFactory', () => { + let validateDocumentsBatchTransitionState; + let fetchDocumentsMock; + let stateTransition; + let documents; + let dataContract; + let ownerId; + let validateDocumentsUniquenessByIndicesMock; + let stateRepositoryMock; + let executeDataTriggersMock; + let documentTransitions; + let abciHeader; + let fakeTime; + let blockTime; + let executionContext; + + beforeEach(function beforeEach() { + dataContract = getDataContractFixture(); + documents = getDocumentsFixture(dataContract); + ownerId = getDocumentsFixture.ownerId; + + documentTransitions = getDocumentTransitionsFixture({ + create: documents, + }); + + stateTransition = new DocumentsBatchTransition({ + ownerId, + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + executionContext = new StateTransitionExecutionContext(); + + stateTransition.setExecutionContext(executionContext); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchDataContract.resolves(dataContract); + + blockTime = new Date().getTime() / 1000; + + abciHeader = { + time: { + seconds: blockTime, + }, + }; + + stateRepositoryMock.fetchLatestPlatformBlockHeader.resolves(abciHeader); + + fetchDocumentsMock = this.sinonSandbox.stub().resolves([]); + + executeDataTriggersMock = this.sinonSandbox.stub(); + + validateDocumentsUniquenessByIndicesMock = this.sinonSandbox.stub(); + validateDocumentsUniquenessByIndicesMock.resolves(new ValidationResult()); + + validateDocumentsBatchTransitionState = validateDocumentsBatchTransitionStateFactory( + stateRepositoryMock, + fetchDocumentsMock, + validateDocumentsUniquenessByIndicesMock, + executeDataTriggersMock, + ); + + fakeTime = this.sinonSandbox.useFakeTimers(new Date()); + }); + + afterEach(() => { + fakeTime.reset(); + }); + + it('should throw DataContractNotPresentError if data contract was not found', async () => { + stateRepositoryMock.fetchDataContract.resolves(null); + + try { + await validateDocumentsBatchTransitionState(stateTransition); + + expect.fail('should throw DataContractNotPresentError'); + } catch (e) { + expect(e).to.be.instanceOf(DataContractNotPresentError); + + expect(e.getDataContractId()).to.deep.equal(dataContract.getId()); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + new StateTransitionExecutionContext(), + ); + + expect(fetchDocumentsMock).to.have.not.been.called(); + expect(validateDocumentsUniquenessByIndicesMock).to.have.not.been.called(); + expect(executeDataTriggersMock).to.have.not.been.called(); + } + }); + + it('should return invalid result if document transition with action "create" is already present', async () => { + fetchDocumentsMock.resolves([documents[0]]); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + expectValidationError(result, DocumentAlreadyPresentError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4004); + expect(Buffer.isBuffer(error.getDocumentId())).to.be.true(); + expect(error.getDocumentId()).to.deep.equal(documentTransitions[0].getId().toBuffer()); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + new StateTransitionExecutionContext(), + ); + + expect(fetchDocumentsMock.getCall(0).args[0].map((t) => t.toObject())).to.have.deep.members( + documentTransitions.map((t) => t.toObject()), + ); + + expect(validateDocumentsUniquenessByIndicesMock).to.have.not.been.called(); + expect(executeDataTriggersMock).to.have.not.been.called(); + }); + + it('should return invalid result if document transition with action "replace" is not present', async () => { + documentTransitions = getDocumentTransitionsFixture({ + create: [], + replace: [documents[0]], + }); + + stateTransition = new DocumentsBatchTransition({ + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + stateTransition.setExecutionContext(executionContext); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + expectValidationError(result, DocumentNotFoundError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4005); + expect(Buffer.isBuffer(error.getDocumentId())).to.be.true(); + expect(error.getDocumentId()).to.deep.equal(documentTransitions[0].getId().toBuffer()); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + new StateTransitionExecutionContext(), + ); + + expect(fetchDocumentsMock).to.have.been.calledOnceWithExactly( + documentTransitions, + executionContext, + ); + + expect(validateDocumentsUniquenessByIndicesMock).to.have.not.been.called(); + expect(executeDataTriggersMock).to.have.not.been.called(); + }); + + it('should return invalid result if document transition with action "delete" is not present', async () => { + documentTransitions = getDocumentTransitionsFixture({ + create: [], + delete: [documents[0]], + }); + + stateTransition = new DocumentsBatchTransition({ + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + stateTransition.setExecutionContext(executionContext); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + expectValidationError(result, DocumentNotFoundError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4005); + expect(Buffer.isBuffer(error.getDocumentId())).to.be.true(); + expect(error.getDocumentId()).to.deep.equal(documentTransitions[0].getId().toBuffer()); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + new StateTransitionExecutionContext(), + ); + + expect(fetchDocumentsMock).to.have.been.calledOnceWithExactly( + documentTransitions, + executionContext, + ); + + expect(validateDocumentsUniquenessByIndicesMock).to.have.not.been.called(); + expect(executeDataTriggersMock).to.have.not.been.called(); + }); + + it('should return invalid result if document transition with action "replace" has wrong revision', async () => { + const replaceDocument = new Document(documents[0].toObject(), dataContract); + replaceDocument.setRevision(3); + + documentTransitions = getDocumentTransitionsFixture({ + create: [], + replace: [replaceDocument], + }); + + stateTransition = new DocumentsBatchTransition({ + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + stateTransition.setExecutionContext(executionContext); + + documents[0].setCreatedAt(replaceDocument.getCreatedAt()); + fetchDocumentsMock.resolves([documents[0]]); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + expectValidationError(result, InvalidDocumentRevisionError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4010); + expect(Buffer.isBuffer(error.getDocumentId())).to.be.true(); + expect(error.getDocumentId()).to.deep.equal(documentTransitions[0].getId().toBuffer()); + expect(error.getCurrentRevision()).to.deep.equal(documents[0].getRevision()); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + new StateTransitionExecutionContext(), + ); + + expect(fetchDocumentsMock).to.have.been.calledOnceWithExactly( + documentTransitions, + executionContext, + ); + + expect(validateDocumentsUniquenessByIndicesMock).to.have.not.been.called(); + expect(executeDataTriggersMock).to.have.not.been.called(); + }); + + it('should return invalid result if document transition with action "replace" has mismatch of ownerId with previous revision', async () => { + const replaceDocument = new Document(documents[0].toObject(), dataContract); + replaceDocument.setRevision(1); + + const fetchedDocument = new Document(documents[0].toObject(), dataContract); + fetchedDocument.ownerId = generateRandomIdentifier(); + + documentTransitions = getDocumentTransitionsFixture({ + create: [], + replace: [replaceDocument], + }); + + stateTransition = new DocumentsBatchTransition({ + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + stateTransition.setExecutionContext(executionContext); + + fetchDocumentsMock.resolves([fetchedDocument]); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + expectValidationError(result, DocumentOwnerIdMismatchError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4006); + expect(Buffer.isBuffer(error.getDocumentId())).to.be.true(); + expect(error.getDocumentId()).to.deep.equal(documentTransitions[0].getId().toBuffer()); + + expect(Buffer.isBuffer(error.getDocumentOwnerId())).to.be.true(); + expect(error.getDocumentOwnerId()).to.deep.equal( + replaceDocument.getOwnerId().toBuffer(), + ); + + expect(Buffer.isBuffer(error.getExistingDocumentOwnerId())).to.be.true(); + expect(error.getExistingDocumentOwnerId()).to.deep.equal( + fetchedDocument.getOwnerId().toBuffer(), + ); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + new StateTransitionExecutionContext(), + ); + + expect(fetchDocumentsMock).to.have.been.calledOnceWithExactly( + documentTransitions, + executionContext, + ); + + expect(validateDocumentsUniquenessByIndicesMock).to.have.not.been.called(); + expect(executeDataTriggersMock).to.have.not.been.called(); + }); + + it('should throw an error if document transition has invalid action', async () => { + stateTransition = new DocumentsBatchTransition({ + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + stateTransition.setExecutionContext(executionContext); + + stateTransition.transitions[0].getAction = () => 5; + + fetchDocumentsMock.resolves([documents[0]]); + + try { + await validateDocumentsBatchTransitionState(stateTransition); + + expect.fail('InvalidDocumentActionError should be thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidDocumentActionError); + expect(e.getDocumentTransition().toObject()).to.deep.equal( + stateTransition.transitions[0].toObject(), + ); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + new StateTransitionExecutionContext(), + ); + + expect(fetchDocumentsMock).to.have.been.calledOnceWithExactly( + stateTransition.transitions, + executionContext, + ); + + expect(validateDocumentsUniquenessByIndicesMock).to.have.not.been.called(); + expect(executeDataTriggersMock).to.have.not.been.called(); + } + }); + + it('should return invalid result if there are duplicate document transitions according to unique indices', async () => { + const duplicateDocumentsError = new SomeConsensusError('error'); + + validateDocumentsUniquenessByIndicesMock.resolves( + new ValidationResult([duplicateDocumentsError]), + ); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.equal(duplicateDocumentsError); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + new StateTransitionExecutionContext(), + ); + + expect(fetchDocumentsMock).to.have.been.calledOnceWithExactly( + stateTransition.transitions, + executionContext, + ); + + const [callOwnerId, callDocumentTransitions, callDataContract] = ( + validateDocumentsUniquenessByIndicesMock.getCall(0).args + ); + + const callArgs = [ + callOwnerId, + callDocumentTransitions.map((t) => t.toObject()), + callDataContract, + ]; + + expect(callArgs).to.have.deep.members([ + ownerId, + documentTransitions.map((t) => t.toObject()), + dataContract, + ]); + expect(executeDataTriggersMock).to.have.not.been.called(); + }); + + it('should return invalid result if data triggers execution failed', async () => { + const dataTriggersExecutionContext = new DataTriggerExecutionContext( + stateRepositoryMock, + ownerId, + dataContract, + executionContext, + ); + + const dataTriggerExecutionError = new DataTriggerExecutionError( + documentTransitions[0], + dataTriggersExecutionContext.getDataContract(), + dataTriggersExecutionContext.getOwnerId(), + new Error('error'), + ); + + executeDataTriggersMock.resolves([ + new DataTriggerExecutionResult([dataTriggerExecutionError]), + ]); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + expectValidationError(result, DataTriggerExecutionError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4002); + expect(error).to.equal(dataTriggerExecutionError); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + new StateTransitionExecutionContext(), + ); + + expect(fetchDocumentsMock).to.have.been.calledOnceWithExactly( + stateTransition.transitions, + executionContext, + ); + + const [callOwnerId, callDocumentTransitions, callDataContract, callExecutionContext] = ( + validateDocumentsUniquenessByIndicesMock.getCall(0).args + ); + + const callArgs = [ + callOwnerId, + callDocumentTransitions.map((t) => t.toObject()), + callDataContract, + callExecutionContext, + ]; + + expect(callArgs).to.have.deep.members([ + ownerId, + documentTransitions.map((t) => t.toObject()), + dataContract, + executionContext, + ]); + + const [triggerCallDocumentTransitions, triggerCallDataTriggersExecutionContext] = ( + executeDataTriggersMock.getCall(0).args + ); + + const triggerCallArgs = [ + triggerCallDocumentTransitions.map((t) => t.toObject()), + triggerCallDataTriggersExecutionContext, + ]; + + expect(triggerCallArgs).to.have.deep.members([ + documentTransitions.map((t) => t.toObject()), + dataTriggersExecutionContext, + ]); + }); + + describe('Timestamps', () => { + let timeWindowStart; + let timeWindowEnd; + + beforeEach(() => { + timeWindowStart = new Date(blockTime * 1000); + timeWindowStart.setMinutes( + timeWindowStart.getMinutes() - 5, + ); + + timeWindowEnd = new Date(blockTime * 1000); + timeWindowEnd.setMinutes( + timeWindowEnd.getMinutes() + 5, + ); + }); + + describe('CREATE transition', () => { + it('should return invalid result if timestamps mismatch', async () => { + documentTransitions = getDocumentTransitionsFixture({ + create: [documents[0]], + }); + + stateTransition = new DocumentsBatchTransition({ + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + stateTransition.transitions.forEach((t) => { + // eslint-disable-next-line no-param-reassign + t.updatedAt = new Date(); + }); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + expectValidationError(result, DocumentTimestampsMismatchError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4007); + + documentTransitions[0].updatedAt = new Date(); + + expect(Buffer.isBuffer(error.getDocumentId())).to.be.true(); + expect(error.getDocumentId()).to.deep.equal(documentTransitions[0].getId().toBuffer()); + }); + + it('should return invalid result if "$createdAt" have violated time window', async () => { + documentTransitions = getDocumentTransitionsFixture({ + create: [documents[0]], + }); + + stateTransition = new DocumentsBatchTransition({ + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + stateTransition.transitions.forEach((t) => { + // eslint-disable-next-line no-param-reassign + t.createdAt.setMinutes(t.createdAt.getMinutes() - 6); + // eslint-disable-next-line no-param-reassign + t.updatedAt = undefined; + }); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + expectValidationError(result, DocumentTimestampWindowViolationError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4008); + + documentTransitions[0].createdAt.setMinutes( + documentTransitions[0].createdAt.getMinutes() - 6, + ); + documentTransitions[0].updatedAt = undefined; + + expect(Buffer.isBuffer(error.getDocumentId())).to.be.true(); + expect(error.getDocumentId()).to.deep.equal(documentTransitions[0].getId().toBuffer()); + expect(error.getTimestampName()).to.equal('createdAt'); + expect(error.getTimestamp()).to.deep.equal(documentTransitions[0].createdAt); + expect(error.getTimeWindowStart()).to.deep.equal(timeWindowStart); + expect(error.getTimeWindowEnd()).to.deep.equal(timeWindowEnd); + }); + + it('should return invalid result if "$updatedAt" have violated time window', async () => { + documentTransitions = getDocumentTransitionsFixture({ + create: [documents[1]], + }); + + stateTransition = new DocumentsBatchTransition({ + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + stateTransition.transitions.forEach((t) => { + // eslint-disable-next-line no-param-reassign + t.updatedAt.setMinutes(t.updatedAt.getMinutes() - 6); + // eslint-disable-next-line no-param-reassign + t.createdAt = undefined; + }); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + expectValidationError(result, DocumentTimestampWindowViolationError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4008); + + documentTransitions[0].updatedAt.setMinutes( + documentTransitions[0].updatedAt.getMinutes() - 6, + ); + documentTransitions[0].createdAt = undefined; + + expect(Buffer.isBuffer(error.getDocumentId())).to.be.true(); + expect(error.getDocumentId()).to.deep.equal(documentTransitions[0].getId().toBuffer()); + expect(error.getTimestampName()).to.equal('updatedAt'); + expect(error.getTimestamp()).to.deep.equal(documentTransitions[0].updatedAt); + expect(error.getTimeWindowStart()).to.deep.equal(timeWindowStart); + expect(error.getTimeWindowEnd()).to.deep.equal(timeWindowEnd); + }); + + it('should not validate time in block window on dry run', async () => { + documentTransitions = getDocumentTransitionsFixture({ + create: [documents[1]], + }); + + executeDataTriggersMock.resolves([ + new DataTriggerExecutionResult(), + ]); + + stateTransition = new DocumentsBatchTransition({ + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + stateTransition.transitions.forEach((t) => { + // eslint-disable-next-line no-param-reassign + t.updatedAt.setMinutes(t.updatedAt.getMinutes() - 6); + }); + + stateTransition.getExecutionContext().enableDryRun(); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + stateTransition.getExecutionContext().disableDryRun(); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + + it('should return valid result if timestamps mismatch on dry run', async () => { + documentTransitions = getDocumentTransitionsFixture({ + create: [documents[0]], + }); + + executeDataTriggersMock.resolves([ + new DataTriggerExecutionResult(), + ]); + + stateTransition = new DocumentsBatchTransition({ + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + stateTransition.transitions.forEach((t) => { + // eslint-disable-next-line no-param-reassign + t.updatedAt = new Date(); + }); + + stateTransition.getExecutionContext().enableDryRun(); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + stateTransition.getExecutionContext().disableDryRun(); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + }); + + describe('REPLACE transition', () => { + it('should return invalid result if documents with action "replace" have violated time window', async () => { + documentTransitions = getDocumentTransitionsFixture({ + create: [], + replace: [documents[1]], + }); + + stateTransition = new DocumentsBatchTransition({ + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + documents[1].updatedAt.setMinutes( + documents[1].updatedAt.getMinutes() - 6, + ); + + fetchDocumentsMock.resolves([documents[1]]); + + stateTransition.transitions.forEach((t) => { + // eslint-disable-next-line no-param-reassign + t.updatedAt.setMinutes(t.updatedAt.getMinutes() - 6); + }); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + expectValidationError(result, DocumentTimestampWindowViolationError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4008); + + documentTransitions[0].updatedAt.setMinutes( + documentTransitions[0].updatedAt.getMinutes() - 6, + ); + + expect(Buffer.isBuffer(error.getDocumentId())).to.be.true(); + expect(error.getDocumentId()).to.deep.equal(documentTransitions[0].getId().toBuffer()); + expect(error.getTimestampName()).to.equal('updatedAt'); + expect(error.getTimestamp()).to.deep.equal(documentTransitions[0].updatedAt); + expect(error.getTimeWindowStart()).to.deep.equal(timeWindowStart); + expect(error.getTimeWindowEnd()).to.deep.equal(timeWindowEnd); + }); + + it('should return valid result if documents with action "replace" have violated time window on dry run', async () => { + executeDataTriggersMock.resolves([ + new DataTriggerExecutionResult(), + ]); + + documentTransitions = getDocumentTransitionsFixture({ + create: [], + replace: [documents[1]], + }); + + stateTransition = new DocumentsBatchTransition({ + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + documents[1].updatedAt.setMinutes( + documents[1].updatedAt.getMinutes() - 6, + ); + + fetchDocumentsMock.resolves([documents[1]]); + + stateTransition.transitions.forEach((t) => { + // eslint-disable-next-line no-param-reassign + t.updatedAt.setMinutes(t.updatedAt.getMinutes() - 6); + }); + + stateTransition.getExecutionContext().enableDryRun(); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + stateTransition.getExecutionContext().disableDryRun(); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + }); + }); + + it('should return valid result if document transitions are valid', async () => { + const fetchedDocuments = [ + new Document(documents[1].toObject(), dataContract), + new Document(documents[2].toObject(), dataContract), + ]; + + fetchDocumentsMock.resolves(fetchedDocuments); + + documents[1].setRevision(1); + documents[2].setRevision(1); + + documentTransitions = getDocumentTransitionsFixture({ + create: [], + replace: [documents[1]], + delete: [documents[2]], + }); + + stateTransition = new DocumentsBatchTransition({ + ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + stateTransition.setExecutionContext(executionContext); + + const dataTriggersExecutionContext = new DataTriggerExecutionContext( + stateRepositoryMock, + ownerId, + dataContract, + executionContext, + ); + + executeDataTriggersMock.resolves([ + new DataTriggerExecutionResult(), + ]); + + const result = await validateDocumentsBatchTransitionState(stateTransition); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchDataContract).to.have.been.calledOnceWithExactly( + dataContract.getId(), + new StateTransitionExecutionContext(), + ); + + expect(fetchDocumentsMock).to.have.been.calledOnceWithExactly( + stateTransition.transitions, + executionContext, + ); + + expect(validateDocumentsUniquenessByIndicesMock).to.have.been.calledOnceWithExactly( + ownerId, + [documentTransitions[0]], + dataContract, + executionContext, + ); + + expect(executeDataTriggersMock).to.have.been.calledOnceWithExactly( + documentTransitions, + dataTriggersExecutionContext, + ); + }); +}); diff --git a/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/state/validateDocumentsUniquenessByIndicesFactory.spec.js b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/state/validateDocumentsUniquenessByIndicesFactory.spec.js new file mode 100644 index 00000000000..22ba2493878 --- /dev/null +++ b/packages/js-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/validation/state/validateDocumentsUniquenessByIndicesFactory.spec.js @@ -0,0 +1,279 @@ +const verifyDocumentsUniquenessByIndicesFactory = require('../../../../../../../lib/document/stateTransition/DocumentsBatchTransition/validation/state/validateDocumentsUniquenessByIndicesFactory'); + +const getDocumentsFixture = require('../../../../../../../lib/test/fixtures/getDocumentsFixture'); +const getContractFixture = require('../../../../../../../lib/test/fixtures/getDataContractFixture'); +const getDocumentTransitionsFixture = require('../../../../../../../lib/test/fixtures/getDocumentTransitionsFixture'); + +const { expectValidationError } = require('../../../../../../../lib/test/expect/expectError'); +const createStateRepositoryMock = require('../../../../../../../lib/test/mocks/createStateRepositoryMock'); + +const ValidationResult = require('../../../../../../../lib/validation/ValidationResult'); + +const DuplicateUniqueIndexError = require('../../../../../../../lib/errors/consensus/state/document/DuplicateUniqueIndexError'); +const StateTransitionExecutionContext = require('../../../../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('validateDocumentsUniquenessByIndices', () => { + let stateRepositoryMock; + let validateDocumentsUniquenessByIndices; + let documents; + let documentTransitions; + let dataContract; + let ownerId; + let executionContext; + + beforeEach(function beforeEach() { + ({ ownerId } = getDocumentsFixture); + + documents = getDocumentsFixture(dataContract); + documentTransitions = getDocumentTransitionsFixture({ + create: documents, + }); + dataContract = getContractFixture(); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchDocuments.resolves([]); + + executionContext = new StateTransitionExecutionContext(); + + validateDocumentsUniquenessByIndices = verifyDocumentsUniquenessByIndicesFactory( + stateRepositoryMock, + ); + }); + + it('should return valid result if Documents have no unique indices', async () => { + const [niceDocument] = documents; + const noIndexDocumentTransitions = getDocumentTransitionsFixture({ + create: [niceDocument], + }); + + const result = await validateDocumentsUniquenessByIndices( + ownerId, + noIndexDocumentTransitions, + dataContract, + executionContext, + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + expect(stateRepositoryMock.fetchDocuments).to.have.not.been.called(); + }); + + it('should return valid result if Document has unique indices and there are no duplicates', async () => { + const [, , , william] = documents; + + stateRepositoryMock.fetchDocuments + .withArgs( + dataContract.getId().toBuffer(), + william.getType(), + { + where: [ + ['$ownerId', '==', ownerId], + ['firstName', '==', william.get('firstName')], + ], + }, + ) + .resolves([william]); + + stateRepositoryMock.fetchDocuments + .withArgs( + dataContract.getId().toBuffer(), + william.getType(), + { + where: [ + ['$ownerId', '==', ownerId], + ['lastName', '==', william.get('lastName')], + ], + }, + ) + .resolves([william]); + + const result = await validateDocumentsUniquenessByIndices( + ownerId, + documentTransitions, + dataContract, + executionContext, + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + + it('should return invalid result if Document has unique indices and there are duplicates', async () => { + const [, , , william, leon] = documents; + + const indicesDefinition = dataContract.getDocumentSchema(william.getType()).indices; + + stateRepositoryMock.fetchDocuments + .withArgs( + dataContract.getId(), + william.getType(), + { + where: [ + ['$ownerId', '==', ownerId], + ['firstName', '==', william.get('firstName')], + ], + }, + ) + .resolves([leon]); + + stateRepositoryMock.fetchDocuments + .withArgs( + dataContract.getId(), + william.getType(), + { + where: [ + ['$ownerId', '==', ownerId], + ['lastName', '==', william.get('lastName')], + ], + }, + ) + .resolves([leon]); + + stateRepositoryMock.fetchDocuments + .withArgs( + dataContract.getId(), + leon.getType(), + { + where: [ + ['$ownerId', '==', ownerId], + ['firstName', '==', leon.get('firstName')], + ], + }, + ) + .resolves([william]); + + stateRepositoryMock.fetchDocuments + .withArgs( + dataContract.getId(), + leon.getType(), + { + where: [ + ['$ownerId', '==', ownerId], + ['lastName', '==', leon.get('lastName')], + ], + }, + ) + .resolves([william]); + + const result = await validateDocumentsUniquenessByIndices( + ownerId, + documentTransitions, + dataContract, + executionContext, + ); + + expectValidationError(result, DuplicateUniqueIndexError, 4); + + const errors = result.getErrors(); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4009); + + expect(errors.map((e) => e.getDocumentId())).to.have.deep.members([ + documentTransitions[3].getId().toBuffer(), + documentTransitions[3].getId().toBuffer(), + documentTransitions[4].getId().toBuffer(), + documentTransitions[4].getId().toBuffer(), + ]); + + expect(errors.map((e) => e.getDuplicatingProperties())).to.have.deep.members([ + indicesDefinition[0].properties.map((i) => Object.keys(i)[0]), + indicesDefinition[1].properties.map((i) => Object.keys(i)[0]), + indicesDefinition[0].properties.map((i) => Object.keys(i)[0]), + indicesDefinition[1].properties.map((i) => Object.keys(i)[0]), + ]); + }); + + it('should return valid result if Document has undefined field from index', async () => { + const indexedDocument = documents[7]; + const indexedDocumentTransitions = getDocumentTransitionsFixture({ + create: [indexedDocument], + }); + + stateRepositoryMock.fetchDocuments + .withArgs( + dataContract.getId().toBuffer(), + indexedDocument.getType(), + { + where: [ + ['$ownerId', '==', ownerId], + ['firstName', '==', indexedDocument.get('firstName')], + ], + }, + ) + .resolves([indexedDocument]); + + stateRepositoryMock.fetchDocuments + .withArgs( + dataContract.getId(), + indexedDocument.getType(), + { + where: [ + ['$ownerId', '==', ownerId], + ], + }, + ) + .resolves([indexedDocument]); + + const result = await validateDocumentsUniquenessByIndices( + ownerId, + indexedDocumentTransitions, + dataContract, + executionContext, + ); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); + + it('should return valid result if Document being created and has createdAt and updatedAt indices', async () => { + const [, , , , , , uniqueDatesDocument] = documents; + + const uniqueDatesDocumentTransitions = getDocumentTransitionsFixture({ + create: [uniqueDatesDocument], + }); + stateRepositoryMock.fetchDocuments + .withArgs( + dataContract.getId().toBuffer(), + uniqueDatesDocument.getType(), + { + where: [ + ['$createdAt', '==', uniqueDatesDocument.getCreatedAt().getTime()], + ['$updatedAt', '==', uniqueDatesDocument.getUpdatedAt().getTime()], + ], + }, + ) + .resolves([uniqueDatesDocument]); + + const result = await validateDocumentsUniquenessByIndices( + ownerId, + uniqueDatesDocumentTransitions, + dataContract, + executionContext, + ); + + expect(result.isValid()).to.be.true(); + }); + + it('should return invalid result on dry run', async () => { + const [niceDocument] = documents; + const noIndexDocumentTransitions = getDocumentTransitionsFixture({ + create: [niceDocument], + }); + + executionContext.enableDryRun(); + + const result = await validateDocumentsUniquenessByIndices( + ownerId, + noIndexDocumentTransitions, + dataContract, + executionContext, + ); + executionContext.disableDryRun(); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + expect(stateRepositoryMock.fetchDocuments).to.have.not.been.called(); + }); +}); diff --git a/packages/js-dpp/test/unit/errors/consensus/codes.spec.js b/packages/js-dpp/test/unit/errors/consensus/codes.spec.js new file mode 100644 index 00000000000..d3d6bf2e1dc --- /dev/null +++ b/packages/js-dpp/test/unit/errors/consensus/codes.spec.js @@ -0,0 +1,102 @@ +const path = require('path'); +const fs = require('fs'); +const codes = require('../../../../lib/errors/consensus/codes'); +const AbstractConsensusError = require('../../../../lib/errors/consensus/AbstractConsensusError'); +const AbstractBasicError = require('../../../../lib/errors/consensus/basic/AbstractBasicError'); +const AbstractSignatureError = require('../../../../lib/errors/consensus/signature/AbstractSignatureError'); +const AbstractFeeError = require('../../../../lib/errors/consensus/fee/AbstractFeeError'); +const AbstractStateError = require('../../../../lib/errors/consensus/state/AbstractStateError'); + +const getAllFiles = (dirPath, arrayOfFiles) => { + const files = fs.readdirSync(dirPath); + + // eslint-disable-next-line no-param-reassign + arrayOfFiles = arrayOfFiles || []; + + files.forEach((file) => { + if (fs.statSync(`${dirPath}/${file}`).isDirectory()) { + // eslint-disable-next-line no-param-reassign + arrayOfFiles = getAllFiles(`${dirPath}/${file}`, arrayOfFiles); + } else if (file.slice(-3) === '.js') { + arrayOfFiles.push(path.join(dirPath, '/', file)); + } + }); + + return arrayOfFiles; +}; + +function isChildOf(classToCheck, parentClass) { + if (!classToCheck || !classToCheck.prototype) { + return false; + } + + if (classToCheck.prototype instanceof parentClass) { + return true; + } + + return isChildOf(classToCheck.prototype, parentClass); +} + +const errorClasses = Object.values(codes).map((ErrorClass) => ErrorClass); +const errorClassDuplicates = errorClasses.filter((item, index) => ( + errorClasses.indexOf(item) !== index +)); + +describe('Consensus error codes', () => { + // Skip the tests for browsers + if (global.window !== undefined) { + return; + } + + const normalizedPath = path.join(__dirname, '../../../../lib/errors/'); + const allFiles = getAllFiles(normalizedPath); + + allFiles.forEach((fileName) => { + // eslint-disable-next-line global-require,import/no-dynamic-require + const ErrorClass = require(fileName); + + if ( + (isChildOf(ErrorClass, AbstractConsensusError)) && !ErrorClass.name.startsWith('Abstract') + ) { + context(ErrorClass.name, () => { + let code; + let AssignedErrorClass; + + beforeEach(() => { + const result = Object.entries(codes) + .find(([, ErrorClassWithCode]) => ErrorClassWithCode === ErrorClass); + + if (result) { + code = Number(result[0]); + // eslint-disable-next-line prefer-destructuring + AssignedErrorClass = result[1]; + } + }); + + it('should have error code defined', () => { + expect(AssignedErrorClass).to.exist(); + }); + + it('should have been define in the correct code range', () => { + if (isChildOf(ErrorClass, AbstractBasicError)) { + expect(code).to.be.above(999); + expect(code).to.be.below(2000); + } else if (isChildOf(ErrorClass, AbstractSignatureError)) { + expect(code).to.be.above(1999); + expect(code).to.be.below(3000); + } else if (isChildOf(ErrorClass, AbstractFeeError)) { + expect(code).to.be.above(2999); + expect(code).to.be.below(4000); + } else if (isChildOf(ErrorClass, AbstractStateError)) { + expect(code).to.be.above(3999); + expect(code).to.be.below(5000); + } + }); + + it('should not have duplicates', () => { + expect(errorClassDuplicates).to.not.include(ErrorClass); + }); + }); + } + }); +}); diff --git a/packages/js-dpp/test/unit/identity/Identity.spec.js b/packages/js-dpp/test/unit/identity/Identity.spec.js new file mode 100644 index 00000000000..795e5882f5e --- /dev/null +++ b/packages/js-dpp/test/unit/identity/Identity.spec.js @@ -0,0 +1,243 @@ +const generateRandomIdentifier = require('../../../lib/test/utils/generateRandomIdentifier'); + +const IdentityPublicKey = require('../../../lib/identity/IdentityPublicKey'); +const Metadata = require('../../../lib/Metadata'); +const protocolVersion = require('../../../lib/version/protocolVersion'); + +const Identity = require('../../../lib/identity/Identity'); +const serializer = require('../../../lib/util/serializer'); +const hash = require('../../../lib/util/hash'); + +describe('Identity', () => { + let rawIdentity; + let identity; + let hashMock; + let encodeMock; + let metadataFixture; + + beforeEach(function beforeEach() { + rawIdentity = { + protocolVersion: protocolVersion.latestVersion, + id: generateRandomIdentifier(), + publicKeys: [ + { + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: Buffer.alloc(36).fill('a'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + }, + ], + balance: 0, + revision: 0, + }; + + identity = new Identity(rawIdentity); + + metadataFixture = new Metadata(42, 0); + + identity.setMetadata(metadataFixture); + + encodeMock = this.sinonSandbox.stub(serializer, 'encode'); + hashMock = this.sinonSandbox.stub(hash, 'hash'); + }); + + afterEach(() => { + encodeMock.restore(); + hashMock.restore(); + }); + + describe('#constructor', () => { + it('should set variables from raw model', () => { + const instance = new Identity(rawIdentity); + + expect(instance.id).to.deep.equal(rawIdentity.id); + expect(instance.type).to.equal(rawIdentity.type); + expect(instance.publicKeys).to.deep.equal( + rawIdentity.publicKeys.map((rawPublicKey) => new IdentityPublicKey(rawPublicKey)), + ); + }); + }); + + describe('#getId', () => { + it('should return set id', () => { + expect(identity.getId()).to.deep.equal(rawIdentity.id); + }); + }); + + describe('#getPublicKeys', () => { + it('should return set public keys', () => { + expect(identity.getPublicKeys()).to.deep.equal( + rawIdentity.publicKeys.map((rawPublicKey) => new IdentityPublicKey(rawPublicKey)), + ); + }); + }); + + describe('#setPublicKeys', () => { + it('should set public keys', () => { + identity.setPublicKeys(42); + expect(identity.publicKeys).to.equal(42); + }); + }); + + describe('#getPublicKeyById', () => { + it('should return a public key for a given id', () => { + const key = identity.getPublicKeyById(0); + + expect(key).to.be.deep.equal(new IdentityPublicKey(rawIdentity.publicKeys[0])); + }); + + it("should return undefined if there's no key with such id", () => { + const key = identity.getPublicKeyById(3); + expect(key).to.be.undefined(); + }); + }); + + describe('#toBuffer', () => { + it('should return serialized Identity', () => { + const encodeMockData = Buffer.from('42'); + encodeMock.returns(encodeMockData); // for example + + const result = identity.toBuffer(); + + const identityDataToEncode = identity.toObject(); + delete identityDataToEncode.protocolVersion; + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(identity.getProtocolVersion(), 0); + + expect(encodeMock).to.have.been.calledOnceWith(identityDataToEncode); + expect(result).to.deep.equal(Buffer.concat([protocolVersionUInt32, encodeMockData])); + }); + }); + + describe('#hash', () => { + it('should return hex string of a buffer return by serialize', () => { + const buffer = Buffer.from('someString'); + + encodeMock.returns(buffer); + hashMock.returns(buffer); + + const result = identity.hash(); + + const identityDataToEncode = identity.toObject(); + delete identityDataToEncode.protocolVersion; + + const protocolVersionUInt32 = Buffer.alloc(4); + protocolVersionUInt32.writeUInt32LE(identity.getProtocolVersion(), 0); + + expect(encodeMock).to.have.been.calledOnceWith(identityDataToEncode); + expect(hashMock).to.have.been.calledOnceWith(Buffer.concat([protocolVersionUInt32, buffer])); + expect(result).to.equal(buffer); + }); + }); + + describe('#toObject', () => { + it('should return plain object representation', () => { + expect(identity.toObject()).to.deep.equal(rawIdentity); + }); + }); + + describe('#toJSON', () => { + it('should return json representation', () => { + const jsonIdentity = identity.toJSON(); + + expect(jsonIdentity).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + id: rawIdentity.id.toString(), + publicKeys: [ + { + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: rawIdentity.publicKeys[0].data.toString('base64'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + }, + ], + balance: 0, + revision: 0, + }); + }); + }); + + describe('#getBalance', () => { + it('should return set identity balance', () => { + identity.balance = 42; + expect(identity.getBalance()).to.equal(42); + }); + }); + + describe('#setBalance', () => { + it('should set identity balance', () => { + identity.setBalance(42); + expect(identity.balance).to.equal(42); + }); + }); + + describe('#increaseBalance', () => { + it('should increase identity balance', () => { + const result = identity.increaseBalance(42); + + expect(result).to.equal(42); + expect(identity.balance).to.equal(42); + }); + }); + + describe('#reduceBalance', () => { + it('should reduce identity balance', () => { + identity.balance = 42; + + const result = identity.reduceBalance(2); + + expect(result).to.equal(40); + expect(identity.balance).to.equal(40); + }); + }); + + describe('#setMetadata', () => { + it('should set metadata', () => { + const otherMetadata = new Metadata(43, 1); + + identity.setMetadata(otherMetadata); + + expect(identity.metadata).to.deep.equal(otherMetadata); + }); + }); + + describe('#getMetadata', () => { + it('should get metadata', () => { + expect(identity.getMetadata()).to.deep.equal(metadataFixture); + }); + }); + + describe('#getPublicKeyMaxId', () => { + it('should get the biggest public key ID', () => { + identity.publicKeys.push( + new IdentityPublicKey({ + id: 99, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: Buffer.alloc(36).fill('a'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + }), + new IdentityPublicKey({ + id: 50, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: Buffer.alloc(36).fill('a'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + }), + ); + + const maxId = identity.getPublicKeyMaxId(); + + const publicKeyIds = identity.getPublicKeys().map((publicKey) => publicKey.getId()); + + expect(Math.max(...publicKeyIds)).to.equal(maxId); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/IdentityFactory.spec.js b/packages/js-dpp/test/unit/identity/IdentityFactory.spec.js new file mode 100644 index 00000000000..25a1a1f835b --- /dev/null +++ b/packages/js-dpp/test/unit/identity/IdentityFactory.spec.js @@ -0,0 +1,274 @@ +const { PublicKey } = require('@dashevo/dashcore-lib'); +const Identity = require('../../../lib/identity/Identity'); +const IdentityCreateTransition = require('../../../lib/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition'); +const IdentityTopUpTransition = require('../../../lib/identity/stateTransition/IdentityTopUpTransition/IdentityTopUpTransition'); + +const getIdentityFixture = require('../../../lib/test/fixtures/getIdentityFixture'); + +const ValidationResult = require('../../../lib/validation/ValidationResult'); +const SerializedObjectParsingError = require('../../../lib/errors/consensus/basic/decode/SerializedObjectParsingError'); + +const InvalidIdentityError = require( + '../../../lib/identity/errors/InvalidIdentityError', +); +const getInstantAssetLockProofFixture = require('../../../lib/test/fixtures/getInstantAssetLockProofFixture'); +const InstantAssetLockProof = require('../../../lib/identity/stateTransition/assetLockProof/instant/InstantAssetLockProof'); +const getChainAssetLockProofFixture = require('../../../lib/test/fixtures/getChainAssetLockProofFixture'); +const createDPPMock = require('../../../lib/test/mocks/createDPPMock'); +const SomeConsensusError = require('../../../lib/test/mocks/SomeConsensusError'); +const IdentityFactory = require('../../../lib/identity/IdentityFactory'); +const IdentityUpdateTransition = require('../../../lib/identity/stateTransition/IdentityUpdateTransition/IdentityUpdateTransition'); +const IdentityPublicKey = require('../../../lib/identity/IdentityPublicKey'); + +describe('IdentityFactory', () => { + let factory; + let validateIdentityMock; + let decodeProtocolEntityMock; + let identity; + let instantAssetLockProof; + let chainAssetLockProof; + let dppMock; + let fakeTime; + + beforeEach(function beforeEach() { + validateIdentityMock = this.sinonSandbox.stub(); + decodeProtocolEntityMock = this.sinonSandbox.stub(); + + instantAssetLockProof = getInstantAssetLockProofFixture(); + chainAssetLockProof = getChainAssetLockProofFixture(); + + dppMock = createDPPMock(); + + factory = new IdentityFactory( + dppMock, + validateIdentityMock, + decodeProtocolEntityMock, + ); + + identity = getIdentityFixture(); + identity.id = instantAssetLockProof.createIdentifier(); + identity.setAssetLockProof(instantAssetLockProof); + identity.setBalance(0); + + fakeTime = this.sinonSandbox.useFakeTimers(new Date()); + }); + + afterEach(() => { + fakeTime.reset(); + }); + + describe('#constructor', () => { + it('should set validator', () => { + expect(factory.validateIdentity).to.equal(validateIdentityMock); + }); + }); + + describe('#create', () => { + it('should create Identity from asset lock transaction, output index, proof and public keys', () => { + const publicKeys = identity + .getPublicKeys() + .map((identityPublicKey) => ({ + ...identityPublicKey.toObject(), + key: new PublicKey(identityPublicKey.getData()), + readonly: true, + })); + + const result = factory.create( + instantAssetLockProof, + publicKeys, + ); + + expect(result).to.be.an.instanceOf(Identity); + expect(result.toObject()).to.deep.equal(identity.toObject()); + }); + }); + + describe('#createFromObject', () => { + it('should skip validation if options is set', () => { + factory.createFromObject({}, { skipValidation: true }); + + expect(validateIdentityMock).to.have.not.been.called(); + }); + + it('should throw an error if validation have failed', () => { + const errors = [new SomeConsensusError('error')]; + + validateIdentityMock.returns(new ValidationResult(errors)); + + try { + factory.createFromObject(identity.toObject()); + + expect.fail('error was not thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidIdentityError); + expect(e.getErrors()).to.have.deep.members(errors); + expect(e.getRawIdentity()).to.deep.equal(identity.toObject()); + } + }); + + it('should create an identity if validation passed', () => { + validateIdentityMock.returns(new ValidationResult()); + + const result = factory.createFromObject(identity.toObject()); + + expect(result).to.be.an.instanceOf(Identity); + expect(result.toObject()).to.deep.equal(identity.toObject()); + }); + }); + + describe('#createFromBuffer', () => { + let serializedIdentity; + let rawIdentity; + + beforeEach(function beforeEach() { + this.sinonSandbox.stub(factory, 'createFromObject'); + + serializedIdentity = identity.toBuffer(); + rawIdentity = identity.toObject(); + }); + + afterEach(() => { + factory.createFromObject.restore(); + }); + + it('should return new Identity from serialized one', () => { + decodeProtocolEntityMock.returns([rawIdentity.protocolVersion, rawIdentity]); + + factory.createFromObject.returns(identity); + + const result = factory.createFromBuffer(serializedIdentity); + + expect(result).to.equal(identity); + + expect(factory.createFromObject).to.have.been.calledOnceWith(rawIdentity); + + expect(decodeProtocolEntityMock).to.have.been.calledOnceWithExactly( + serializedIdentity, + ); + }); + + it('should throw InvalidIdentityError if the decoding fails with consensus error', () => { + const parsingError = new SerializedObjectParsingError( + serializedIdentity, + new Error(), + ); + + decodeProtocolEntityMock.throws(parsingError); + + try { + factory.createFromBuffer(serializedIdentity); + + expect.fail('should throw InvalidIdentityError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidIdentityError); + + const [innerError] = e.getErrors(); + expect(innerError).to.equal(parsingError); + } + }); + + it('should throw an error if decoding fails with any other error', () => { + const parsingError = new Error('Something failed during parsing'); + + decodeProtocolEntityMock.throws(parsingError); + + try { + factory.createFromBuffer(serializedIdentity); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.equal(parsingError); + } + }); + }); + + describe('#createInstantAssetLockProof', () => { + it('should create instant asset lock proof from InstantLock', () => { + const instantLock = instantAssetLockProof.getInstantLock(); + const assetLockTransaction = instantAssetLockProof.getTransaction(); + const outputIndex = instantAssetLockProof.getOutputIndex(); + + const result = factory.createInstantAssetLockProof( + instantLock, + assetLockTransaction, + outputIndex, + ); + + expect(result).to.be.instanceOf(InstantAssetLockProof); + expect(result.getInstantLock()).to.deep.equal(instantLock); + }); + }); + + describe('#createIdentityCreateTransition', () => { + it('should create IdentityCreateTransition from Identity model', () => { + const stateTransition = factory.createIdentityCreateTransition(identity); + + expect(stateTransition).to.be.instanceOf(IdentityCreateTransition); + expect(stateTransition.getPublicKeys()).to.deep.equal(identity.getPublicKeys()); + expect(stateTransition.getAssetLockProof().toObject()) + .to.deep.equal(instantAssetLockProof.toObject()); + }); + }); + + describe('createChainAssetLockProof', () => { + it('should create IdentityCreateTransition from Identity model', () => { + identity = getIdentityFixture(); + identity.id = chainAssetLockProof.createIdentifier(); + identity.setAssetLockProof(chainAssetLockProof); + identity.setBalance(0); + + const stateTransition = factory.createIdentityCreateTransition(identity); + + expect(stateTransition).to.be.instanceOf(IdentityCreateTransition); + expect(stateTransition.getPublicKeys()).deep.to.equal(identity.getPublicKeys()); + expect(stateTransition.getAssetLockProof().toObject()) + .to.deep.equal(chainAssetLockProof.toObject()); + }); + }); + + describe('#createIdentityTopUpTransition', () => { + it('should create IdentityTopUpTransition from identity id and outpoint', () => { + const stateTransition = factory + .createIdentityTopUpTransition( + identity.getId(), + instantAssetLockProof, + ); + + expect(stateTransition).to.be.instanceOf(IdentityTopUpTransition); + expect(stateTransition.getIdentityId()).to.deep.equal(identity.getId()); + expect(stateTransition.getAssetLockProof().toObject()) + .to.deep.equal(instantAssetLockProof.toObject()); + }); + }); + + describe('createIdentityUpdateTransition', () => { + it('should create IdentityUpdateTransition', () => { + const revision = 1; + const disablePublicKeys = [identity.getPublicKeyById(0)]; + const addPublicKeys = [new IdentityPublicKey({ + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: Buffer.from('AuryIuMtRrl/VviQuyLD1l4nmxi9ogPzC9LT7tdpo0di', 'base64'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + })]; + + const stateTransition = factory + .createIdentityUpdateTransition( + identity, + { + add: addPublicKeys, + disable: disablePublicKeys, + }, + ); + + expect(stateTransition).to.be.instanceOf(IdentityUpdateTransition); + expect(stateTransition.getIdentityId()).to.deep.equal(identity.getId()); + expect(stateTransition.getRevision()).to.deep.equal(revision); + expect(stateTransition.getPublicKeysToAdd()).to.deep.equal(addPublicKeys); + expect(stateTransition.getPublicKeyIdsToDisable()).to.deep.equal([0]); + expect(stateTransition.getPublicKeysDisabledAt()).to.deep.equal(new Date()); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/IdentityPublicKey.spec.js b/packages/js-dpp/test/unit/identity/IdentityPublicKey.spec.js new file mode 100644 index 00000000000..a8a232ded81 --- /dev/null +++ b/packages/js-dpp/test/unit/identity/IdentityPublicKey.spec.js @@ -0,0 +1,273 @@ +const IdentityPublicKey = require('../../../lib/identity/IdentityPublicKey'); +const EmptyPublicKeyDataError = require('../../../lib/identity/errors/EmptyPublicKeyDataError'); + +describe('IdentityPublicKey', () => { + let rawPublicKey; + let publicKey; + + beforeEach(() => { + rawPublicKey = { + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: Buffer.from('AkVuTKyF3YgKLAQlLEtaUL2HTditwGILfWUVqjzYnIgH', 'base64'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + }; + + publicKey = new IdentityPublicKey(rawPublicKey); + }); + + describe('#constructor', () => { + it('should not set anything if nothing passed', () => { + const instance = new IdentityPublicKey(); + + expect(instance.id).to.be.undefined(); + expect(instance.type).to.be.undefined(); + expect(instance.data).to.be.undefined(); + }); + + it('should set variables from raw model', () => { + const instance = new IdentityPublicKey(rawPublicKey); + + expect(instance.id).to.equal(rawPublicKey.id); + expect(instance.type).to.equal(rawPublicKey.type); + expect(instance.data).to.equal(rawPublicKey.data); + }); + }); + + describe('#getId', () => { + it('should return set id', () => { + expect(publicKey.getId()).to.equal(rawPublicKey.id); + }); + }); + + describe('#setId', () => { + it('should set id', () => { + publicKey.setId(42); + + expect(publicKey.id).to.equal(42); + }); + }); + + describe('#getType', () => { + it('should return set type', () => { + publicKey.type = 42; + + expect(publicKey.getType()).to.equal(42); + }); + }); + + describe('#setType', () => { + it('should set type', () => { + publicKey.setType(42); + + expect(publicKey.type).to.equal(42); + }); + }); + + describe('#getData', () => { + it('should return set data', () => { + expect(publicKey.getData()).to.equal(rawPublicKey.data); + }); + }); + + describe('#setData', () => { + it('should set data', () => { + const buffer = Buffer.alloc(36); + + publicKey.setData(buffer); + + expect(publicKey.data).to.equal(buffer); + }); + }); + + describe('#getPurpose', () => { + it('should return set data', () => { + expect(publicKey.getPurpose()).to.equal(rawPublicKey.purpose); + }); + }); + + describe('#setPurpose', () => { + it('should set data', () => { + publicKey.setPurpose(IdentityPublicKey.PURPOSES.DECRYPTION); + + expect(publicKey.purpose).to.equal(IdentityPublicKey.PURPOSES.DECRYPTION); + }); + }); + + describe('#getSecurityLevel', () => { + it('should return set data', () => { + expect(publicKey.getSecurityLevel()).to.equal(rawPublicKey.securityLevel); + }); + }); + + describe('#setSecurityLevel', () => { + it('should set data', () => { + publicKey.setSecurityLevel(IdentityPublicKey.SECURITY_LEVELS.MEDIUM); + + expect(publicKey.securityLevel).to.equal(IdentityPublicKey.SECURITY_LEVELS.MEDIUM); + }); + }); + + describe('#isReadOnly', () => { + it('should return readOnly', () => { + expect(publicKey.isReadOnly()).to.equal(rawPublicKey.readOnly); + }); + }); + + describe('#setReadOnly', () => { + it('should set readOnly', () => { + publicKey.setReadOnly(true); + + expect(publicKey.readOnly).to.equal(true); + }); + }); + + describe('#setDisabledAt', () => { + it('should set disabledAt', () => { + publicKey.setDisabledAt(123); + + expect(publicKey.disabledAt).to.equal(123); + }); + }); + + describe('#getDisabledAt', () => { + it('should return disabledAt', () => { + publicKey.disabledAt = 42; + + expect(publicKey.getDisabledAt()).to.equal(42); + }); + }); + + describe('#hash', () => { + it('should return original public key hash', () => { + const result = publicKey.hash(); + + const expectedHash = Buffer.from('Q/5mfilFPdZt+Fr5JWC1+tg0cPs=', 'base64'); + + expect(result).to.deep.equal(expectedHash); + }); + + it('should return data in case ECDSA_HASH160', () => { + rawPublicKey = { + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_HASH160, + data: Buffer.from('AkVuTKyF3YgKLAQlLEtaUL2HTditwGILfWUVqjzYnIgH', 'base64'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + }; + + publicKey = new IdentityPublicKey(rawPublicKey); + + const result = publicKey.hash(); + + const expectedHash = Buffer.from('AkVuTKyF3YgKLAQlLEtaUL2HTditwGILfWUVqjzYnIgH', 'base64'); + + expect(result).to.deep.equal(expectedHash); + }); + + it('should return original public key hash in case BLS12_381', () => { + rawPublicKey = { + id: 0, + type: IdentityPublicKey.TYPES.BLS12_381, + data: Buffer.from('01fac99ca2c8f39c286717c213e190aba4b7af76db320ec43f479b7d9a2012313a0ae59ca576edf801444bc694686694', 'hex'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + }; + + publicKey = new IdentityPublicKey(rawPublicKey); + + const result = publicKey.hash(); + + const expectedHash = Buffer.from('1de31a0a328e8822f9cb2c25141d7d80baee26ef', 'hex'); + + expect(result).to.deep.equal(expectedHash); + }); + + it('should return data in case BIP13_SCRIPT_HASH', () => { + rawPublicKey = { + id: 0, + type: IdentityPublicKey.TYPES.BIP13_SCRIPT_HASH, + data: Buffer.from('54c557e07dde5bb6cb791c7a540e0a4796f5e97e', 'hex'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + }; + + publicKey = new IdentityPublicKey(rawPublicKey); + + const result = publicKey.hash(); + + const expectedHash = Buffer.from('54c557e07dde5bb6cb791c7a540e0a4796f5e97e', 'hex'); + + expect(result).to.deep.equal(expectedHash); + }); + + it('should throw invalid argument error if data was not originally provided', async () => { + publicKey = new IdentityPublicKey({ + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + }); + + try { + publicKey.hash(); + + expect.fail('Error was not thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(EmptyPublicKeyDataError); + expect(e.message).to.equal( + 'Public key data is not set', + ); + } + }); + }); + + describe('#toJSON', () => { + it('should return JSON representation', () => { + const jsonPublicKey = publicKey.toJSON(); + + expect(jsonPublicKey).to.deep.equal({ + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: 'AkVuTKyF3YgKLAQlLEtaUL2HTditwGILfWUVqjzYnIgH', + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + }); + }); + + it('should return JSON representation with optional properties', () => { + publicKey.setDisabledAt(42); + + const jsonPublicKey = publicKey.toJSON(); + + expect(jsonPublicKey).to.deep.equal({ + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: 'AkVuTKyF3YgKLAQlLEtaUL2HTditwGILfWUVqjzYnIgH', + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + disabledAt: 42, + }); + }); + }); + + describe('#isMaster', () => { + it('should return true when public key has MASTER security level', () => { + publicKey.securityLevel = IdentityPublicKey.SECURITY_LEVELS.MASTER; + + const result = publicKey.isMaster(); + + expect(result).to.be.true(); + }); + + it('should return false when public key doesn\'t have MASTER security level', () => { + publicKey.securityLevel = IdentityPublicKey.SECURITY_LEVELS.HIGH; + + const result = publicKey.isMaster(); + + expect(result).to.be.false(); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/creditsConverter.spec.js b/packages/js-dpp/test/unit/identity/creditsConverter.spec.js new file mode 100644 index 00000000000..7d7cbaf797d --- /dev/null +++ b/packages/js-dpp/test/unit/identity/creditsConverter.spec.js @@ -0,0 +1,34 @@ +const { + convertSatoshiToCredits, + convertCreditsToSatoshi, + RATIO, +} = require('../../../lib/identity/creditsConverter'); + +describe('creditsConverter', () => { + describe('convertSatoshiToCredits', () => { + it('should convert satoshi to credits', () => { + const amount = 42; + + const convertedAmount = convertSatoshiToCredits(amount); + + expect(convertedAmount).to.equal(amount * RATIO); + }); + }); + describe('convertCreditsToSatoshi', () => { + it('should convert credits to satoshi', () => { + const amount = 10000; + + const convertedAmount = convertCreditsToSatoshi(amount); + + expect(convertedAmount).to.equal(Math.floor(amount / RATIO)); + }); + + it('should convert credits to 0 satoshi if amount < RATIO', () => { + const amount = RATIO - 1; + + const convertedAmount = convertCreditsToSatoshi(amount); + + expect(convertedAmount).to.equal(0); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/errors/InvalidIdentityError.spec.js b/packages/js-dpp/test/unit/identity/errors/InvalidIdentityError.spec.js new file mode 100644 index 00000000000..33bb6f5c89a --- /dev/null +++ b/packages/js-dpp/test/unit/identity/errors/InvalidIdentityError.spec.js @@ -0,0 +1,46 @@ +const InvalidIdentityError = require('../../../../lib/identity/errors/InvalidIdentityError'); +const getIdentityFixture = require('../../../../lib/test/fixtures/getIdentityFixture'); + +describe('InvalidIdentityError', () => { + let rawIdentity; + let error; + + beforeEach(() => { + error = new Error('Some error'); + + const identity = getIdentityFixture(); + rawIdentity = identity.toObject(); + }); + + it('should return errors', () => { + const errors = [error]; + + const invalidIdentityError = new InvalidIdentityError(errors, rawIdentity); + + expect(invalidIdentityError.getErrors()).to.deep.equal(errors); + }); + + it('should return Identity', async () => { + const errors = [error]; + + const invalidIdentityError = new InvalidIdentityError(errors, rawIdentity); + + expect(invalidIdentityError.getRawIdentity()).to.deep.equal(rawIdentity); + }); + + it('should contain message for 1 error', async () => { + const errors = [error]; + + const invalidIdentityError = new InvalidIdentityError(errors, rawIdentity); + + expect(invalidIdentityError.message).to.equal(`Invalid Identity: "${error.message}"`); + }); + + it('should contain message for multiple errors', async () => { + const errors = [error, error]; + + const invalidIdentityError = new InvalidIdentityError(errors, rawIdentity); + + expect(invalidIdentityError.message).to.equal(`Invalid Identity: "${error.message}" and 1 more`); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition.spec.js b/packages/js-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition.spec.js new file mode 100644 index 00000000000..554f942e3b0 --- /dev/null +++ b/packages/js-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition.spec.js @@ -0,0 +1,184 @@ +const IdentityPublicKey = require('../../../../../lib/identity/IdentityPublicKey'); + +const stateTransitionTypes = require( + '../../../../../lib/stateTransition/stateTransitionTypes', +); + +const protocolVersion = require('../../../../../lib/version/protocolVersion'); +const IdentityCreateTransition = require('../../../../../lib/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition'); +const Identifier = require('../../../../../lib/identifier/Identifier'); + +const getIdentityCreateTransitionFixture = require('../../../../../lib/test/fixtures/getIdentityCreateTransitionFixture'); +const InstantAssetLockProof = require('../../../../../lib/identity/stateTransition/assetLockProof/instant/InstantAssetLockProof'); + +describe('IdentityCreateTransition', () => { + let rawStateTransition; + let stateTransition; + + beforeEach(() => { + stateTransition = getIdentityCreateTransitionFixture(); + rawStateTransition = stateTransition.toObject(); + }); + + describe('#constructor', () => { + it('should create an instance with specified data', () => { + expect(stateTransition.getAssetLockProof().toObject()).to.deep.equal( + rawStateTransition.assetLockProof, + ); + + expect(stateTransition.publicKeys).to.deep.equal([ + new IdentityPublicKey(rawStateTransition.publicKeys[0]), + ]); + }); + }); + + describe('#getType', () => { + it('should return IDENTITY_CREATE type', () => { + expect(stateTransition.getType()).to.equal(stateTransitionTypes.IDENTITY_CREATE); + }); + }); + + describe('#setAssetLockProof', () => { + it('should set asset lock proof', () => { + stateTransition.setAssetLockProof( + new InstantAssetLockProof(rawStateTransition.assetLockProof), + ); + + expect(stateTransition.assetLockProof.toObject()) + .to.deep.equal(rawStateTransition.assetLockProof); + }); + + it('should set `identityId`', () => { + stateTransition.setAssetLockProof( + new InstantAssetLockProof(rawStateTransition.assetLockProof), + ); + + expect(stateTransition.identityId).to.deep.equal( + stateTransition.getAssetLockProof().createIdentifier(), + ); + }); + }); + + describe('#getAssetLockProof', () => { + it('should return currently set locked OutPoint', () => { + expect(stateTransition.getAssetLockProof().toObject()).to.deep.equal( + rawStateTransition.assetLockProof, + ); + }); + }); + + describe('#setPublicKeys', () => { + it('should set public keys', () => { + const publicKeys = [new IdentityPublicKey(), new IdentityPublicKey()]; + + stateTransition.setPublicKeys(publicKeys); + + expect(stateTransition.publicKeys).to.have.deep.members(publicKeys); + }); + }); + + describe('#getPublicKeys', () => { + it('should return set public keys', () => { + expect(stateTransition.getPublicKeys()).to.deep.equal( + rawStateTransition.publicKeys.map((rawPublicKey) => new IdentityPublicKey(rawPublicKey)), + ); + }); + }); + + describe('#addPublicKeys', () => { + it('should add more public keys', () => { + const publicKeys = [new IdentityPublicKey(), new IdentityPublicKey()]; + + stateTransition.publicKeys = []; + stateTransition.addPublicKeys(publicKeys); + expect(stateTransition.getPublicKeys()).to.have.deep.members(publicKeys); + }); + }); + + describe('#getIdentityId', () => { + it('should return identity id', () => { + expect(stateTransition.getIdentityId()).to.deep.equal( + stateTransition.getAssetLockProof().createIdentifier(), + ); + }); + }); + + describe('#getOwnerId', () => { + it('should return owner id', () => { + expect(stateTransition.getOwnerId()).to.equal( + stateTransition.getIdentityId(), + ); + }); + }); + + describe('#toObject', () => { + it('should return raw state transition', () => { + rawStateTransition = stateTransition.toObject(); + + expect(rawStateTransition).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.IDENTITY_CREATE, + assetLockProof: rawStateTransition.assetLockProof, + publicKeys: rawStateTransition.publicKeys, + signature: undefined, + }); + }); + + it('should return raw state transition without signature', () => { + rawStateTransition = stateTransition.toObject({ skipSignature: true }); + + expect(rawStateTransition).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.IDENTITY_CREATE, + assetLockProof: rawStateTransition.assetLockProof, + publicKeys: rawStateTransition.publicKeys, + }); + }); + }); + + describe('#toJSON', () => { + it('should return JSON representation of state transition', () => { + const jsonStateTransition = stateTransition.toJSON(); + + expect(jsonStateTransition).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.IDENTITY_CREATE, + assetLockProof: stateTransition.getAssetLockProof().toJSON(), + publicKeys: stateTransition.getPublicKeys().map((k) => k.toJSON()), + signature: undefined, + }); + }); + }); + + describe('#getModifiedDataIds', () => { + it('should return ids of created identities', () => { + const result = stateTransition.getModifiedDataIds(); + + expect(result.length).to.be.equal(1); + const identityId = result[0]; + + expect(identityId).to.be.an.instanceOf(Identifier); + expect(identityId).to.be.deep.equal( + new IdentityCreateTransition(rawStateTransition).getIdentityId(), + ); + }); + }); + + describe('#isDataContractStateTransition', () => { + it('should return false', () => { + expect(stateTransition.isDataContractStateTransition()).to.be.false(); + }); + }); + + describe('#isDocumentStateTransition', () => { + it('should return false', () => { + expect(stateTransition.isDocumentStateTransition()).to.be.false(); + }); + }); + + describe('#isIdentityStateTransition', () => { + it('should return true', () => { + expect(stateTransition.isIdentityStateTransition()).to.be.true(); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/applyIdentityCreateTransitionFactory.spec.js b/packages/js-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/applyIdentityCreateTransitionFactory.spec.js new file mode 100644 index 00000000000..7d47e140a8a --- /dev/null +++ b/packages/js-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/applyIdentityCreateTransitionFactory.spec.js @@ -0,0 +1,90 @@ +const Identity = require('../../../../../lib/identity/Identity'); + +const applyIdentityCreateTransitionFactory = require( + '../../../../../lib/identity/stateTransition/IdentityCreateTransition/applyIdentityCreateTransitionFactory', +); + +const getIdentityCreateTransitionFixture = require('../../../../../lib/test/fixtures/getIdentityCreateTransitionFixture'); + +const { convertSatoshiToCredits } = require('../../../../../lib/identity/creditsConverter'); + +const createStateRepositoryMock = require('../../../../../lib/test/mocks/createStateRepositoryMock'); + +const protocolVersion = require('../../../../../lib/version/protocolVersion'); +const StateTransitionExecutionContext = require('../../../../../lib/stateTransition/StateTransitionExecutionContext'); +const ReadOperation = require('../../../../../lib/stateTransition/fee/operations/ReadOperation'); + +describe('applyIdentityCreateTransitionFactory', () => { + let stateTransition; + let applyIdentityCreateTransition; + let stateRepositoryMock; + let fetchAssetLockTransactionOutputMock; + let output; + let executionContext; + + beforeEach(function beforeEach() { + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + + stateTransition = getIdentityCreateTransitionFixture(); + + executionContext = new StateTransitionExecutionContext(); + + stateTransition.setExecutionContext(executionContext); + + output = stateTransition.getAssetLockProof().getOutput(); + + fetchAssetLockTransactionOutputMock = this.sinonSandbox.stub().resolves(output); + + applyIdentityCreateTransition = applyIdentityCreateTransitionFactory( + stateRepositoryMock, + fetchAssetLockTransactionOutputMock, + ); + }); + + it('should store identity created from state transition', async () => { + executionContext.addOperation( + new ReadOperation(1), + ); + + await applyIdentityCreateTransition(stateTransition); + + const balance = convertSatoshiToCredits( + output.satoshis, + ); + + const identity = new Identity({ + protocolVersion: protocolVersion.latestVersion, + id: stateTransition.getIdentityId(), + publicKeys: stateTransition.getPublicKeys().map((key) => key.toObject()), + balance, + revision: 0, + }); + + expect(stateRepositoryMock.createIdentity).to.have.been.calledOnceWithExactly( + identity, + executionContext, + ); + + const publicKeyHashes = identity + .getPublicKeys() + .map((publicKey) => publicKey.hash()); + + expect(stateRepositoryMock.storeIdentityPublicKeyHashes).to.have.been.calledOnceWithExactly( + identity.getId(), + publicKeyHashes, + executionContext, + ); + + expect(stateRepositoryMock.markAssetLockTransactionOutPointAsUsed).to.have.been + .calledOnceWithExactly( + stateTransition.getAssetLockProof().getOutPoint(), + executionContext, + ); + + expect(fetchAssetLockTransactionOutputMock) + .to.be.calledOnceWithExactly( + stateTransition.getAssetLockProof(), + executionContext, + ); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/validation/state/validateIdentityCreateTransitionStateFactory.spec.js b/packages/js-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/validation/state/validateIdentityCreateTransitionStateFactory.spec.js new file mode 100644 index 00000000000..53ba59a32ea --- /dev/null +++ b/packages/js-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/validation/state/validateIdentityCreateTransitionStateFactory.spec.js @@ -0,0 +1,71 @@ +const { expectValidationError } = require( + '../../../../../../../lib/test/expect/expectError', +); + +const validateIdentityCreateTransitionStateFactory = require( + '../../../../../../../lib/identity/stateTransition/IdentityCreateTransition/validation/state/validateIdentityCreateTransitionStateFactory', +); + +const getIdentityCreateTransitionFixture = require('../../../../../../../lib/test/fixtures/getIdentityCreateTransitionFixture'); + +const IdentityAlreadyExistsError = require( + '../../../../../../../lib/errors/consensus/state/identity/IdentityAlreadyExistsError', +); + +const createStateRepositoryMock = require('../../../../../../../lib/test/mocks/createStateRepositoryMock'); +const IdentityPublicKey = require('../../../../../../../lib/identity/IdentityPublicKey'); + +describe('validateIdentityCreateTransitionStateFactory', () => { + let validateIdentityCreateTransitionState; + let stateTransition; + let stateRepositoryMock; + + beforeEach(async function beforeEach() { + const privateKey = 'af432c476f65211f45f48f1d42c9c0b497e56696aa1736b40544ef1a496af837'; + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + + validateIdentityCreateTransitionState = validateIdentityCreateTransitionStateFactory( + stateRepositoryMock, + ); + + stateTransition = getIdentityCreateTransitionFixture(); + + await stateTransition.signByPrivateKey(privateKey, IdentityPublicKey.TYPES.ECDSA_SECP256K1); + + const rawTransaction = '030000000137feb5676d0851337ea3c9a992496aab7a0b3eee60aeeb9774000b7f4bababa5000000006b483045022100d91557de37645c641b948c6cd03b4ae3791a63a650db3e2fee1dcf5185d1b10402200e8bd410bf516ca61715867666d31e44495428ce5c1090bf2294a829ebcfa4ef0121025c3cc7fbfc52f710c941497fd01876c189171ea227458f501afcb38a297d65b4ffffffff021027000000000000166a14152073ca2300a86b510fa2f123d3ea7da3af68dcf77cb0090a0000001976a914152073ca2300a86b510fa2f123d3ea7da3af68dc88ac00000000'; + + stateRepositoryMock.fetchTransaction.resolves(rawTransaction); + }); + + it('should return invalid result if identity already exists', async () => { + stateRepositoryMock.fetchIdentity.resolves({}); + + const result = await validateIdentityCreateTransitionState(stateTransition); + + expectValidationError(result, IdentityAlreadyExistsError, 1); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(4011); + expect(Buffer.isBuffer(error.getIdentityId())).to.be.true(); + expect(error.getIdentityId()).to.deep.equal(stateTransition.getIdentityId()); + }); + + it('should return valid result if state transition is valid', async () => { + const result = await validateIdentityCreateTransitionState(stateTransition); + + expect(result.isValid()).to.be.true(); + }); + + it('should return valid result on dry run', async () => { + stateRepositoryMock.fetchIdentity.resolves({}); + + stateTransition.getExecutionContext().enableDryRun(); + + const result = await validateIdentityCreateTransitionState(stateTransition); + + stateTransition.getExecutionContext().disableDryRun(); + + expect(result.isValid()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/stateTransition/IdentityTopUpTransition/IdentityTopUpTransition.spec.js b/packages/js-dpp/test/unit/identity/stateTransition/IdentityTopUpTransition/IdentityTopUpTransition.spec.js new file mode 100644 index 00000000000..74245a50c25 --- /dev/null +++ b/packages/js-dpp/test/unit/identity/stateTransition/IdentityTopUpTransition/IdentityTopUpTransition.spec.js @@ -0,0 +1,137 @@ +const stateTransitionTypes = require( + '../../../../../lib/stateTransition/stateTransitionTypes', +); + +const Identifier = require('../../../../../lib/identifier/Identifier'); + +const getIdentityTopUpTransitionFixture = require('../../../../../lib/test/fixtures/getIdentityTopUpTransitionFixture'); + +const protocolVersion = require('../../../../../lib/version/protocolVersion'); + +describe('IdentityTopUpTransition', () => { + let rawStateTransition; + let stateTransition; + + beforeEach(() => { + stateTransition = getIdentityTopUpTransitionFixture(); + rawStateTransition = stateTransition.toObject(); + }); + + describe('#constructor', () => { + it('should create an instance with specified data from specified raw transition', () => { + expect(stateTransition.getAssetLockProof().toObject()).to.be.deep.equal( + rawStateTransition.assetLockProof, + ); + expect(stateTransition.getIdentityId()).to.be.deep.equal( + rawStateTransition.identityId, + ); + }); + }); + + describe('#getType', () => { + it('should return IDENTITY_TOP_UP type', () => { + expect(stateTransition.getType()).to.equal(stateTransitionTypes.IDENTITY_TOP_UP); + }); + }); + + describe('#setAssetLockProof', () => { + it('should set asset lock proof', () => { + stateTransition.setAssetLockProof(rawStateTransition.assetLockProof); + + expect(stateTransition.assetLockProof).to.deep.equal(rawStateTransition.assetLockProof); + }); + }); + + describe('#getAssetLock', () => { + it('should return currently set asset lock proof', () => { + expect(stateTransition.getAssetLockProof().toObject()).to.deep.equal( + rawStateTransition.assetLockProof, + ); + }); + }); + + describe('#getIdentityId', () => { + it('should return identity id', () => { + expect(stateTransition.getIdentityId()).to.deep.equal( + rawStateTransition.identityId, + ); + }); + }); + + describe('#getOwnerId', () => { + it('should return owner id', () => { + expect(stateTransition.getOwnerId()).to.deep.equal( + rawStateTransition.identityId, + ); + }); + }); + + describe('#toObject', () => { + it('should return raw state transition', () => { + rawStateTransition = stateTransition.toObject(); + + expect(rawStateTransition).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.IDENTITY_TOP_UP, + assetLockProof: rawStateTransition.assetLockProof, + identityId: rawStateTransition.identityId, + signature: undefined, + }); + }); + + it('should return raw state transition', () => { + rawStateTransition = stateTransition.toObject({ skipSignature: true }); + + expect(rawStateTransition).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.IDENTITY_TOP_UP, + assetLockProof: rawStateTransition.assetLockProof, + identityId: rawStateTransition.identityId, + }); + }); + }); + + describe('#toJSON', () => { + it('should return JSON representation of state transition', () => { + const jsonStateTransition = stateTransition.toJSON(); + + expect(jsonStateTransition).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.IDENTITY_TOP_UP, + assetLockProof: stateTransition.getAssetLockProof().toJSON(), + identityId: Identifier(rawStateTransition.identityId).toString(), + signature: undefined, + }); + }); + }); + + describe('#getModifiedDataIds', () => { + it('should return ids of topped up identity', () => { + const result = stateTransition.getModifiedDataIds(); + + expect(result.length).to.be.equal(1); + const identityId = result[0]; + + expect(identityId).to.be.an.instanceOf(Identifier); + expect(identityId).to.be.deep.equal(rawStateTransition.identityId); + }); + }); + + describe('#isDataContractStateTransition', () => { + it('should return false', () => { + expect(stateTransition.isDataContractStateTransition()).to.be.false(); + }); + }); + + describe('#isDocumentStateTransition', () => { + it('should return false', () => { + expect(stateTransition.isDocumentStateTransition()).to.be.false(); + }); + }); + + describe('#isIdentityStateTransition', () => { + it('should return true', () => { + expect(stateTransition.isIdentityStateTransition()).to.be.true(); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/stateTransition/IdentityTopUpTransition/applyIdentityTopUpTransitionFactory.spec.js b/packages/js-dpp/test/unit/identity/stateTransition/IdentityTopUpTransition/applyIdentityTopUpTransitionFactory.spec.js new file mode 100644 index 00000000000..2fedd027d48 --- /dev/null +++ b/packages/js-dpp/test/unit/identity/stateTransition/IdentityTopUpTransition/applyIdentityTopUpTransitionFactory.spec.js @@ -0,0 +1,108 @@ +const applyIdentityTopUpTransitionFactory = require( + '../../../../../lib/identity/stateTransition/IdentityTopUpTransition/applyIdentityTopUpTransitionFactory', +); + +const getIdentityFixture = require('../../../../../lib/test/fixtures/getIdentityFixture'); +const getIdentityTopUpTransitionFixture = require('../../../../../lib/test/fixtures/getIdentityTopUpTransitionFixture'); + +const { convertSatoshiToCredits } = require('../../../../../lib/identity/creditsConverter'); + +const createStateRepositoryMock = require('../../../../../lib/test/mocks/createStateRepositoryMock'); +const StateTransitionExecutionContext = require('../../../../../lib/stateTransition/StateTransitionExecutionContext'); +const getBiggestPossibleIdentity = require('../../../../../lib/identity/getBiggestPossibleIdentity'); + +describe('applyIdentityTopUpTransitionFactory', () => { + let stateTransition; + let applyIdentityTopUpTransition; + let stateRepositoryMock; + let identity; + let fetchAssetLockTransactionOutputMock; + let executionContext; + + beforeEach(function beforeEach() { + identity = getIdentityFixture(); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchIdentity.resolves(identity); + + stateTransition = getIdentityTopUpTransitionFixture(); + + executionContext = new StateTransitionExecutionContext(); + + stateTransition.setExecutionContext(executionContext); + + const output = stateTransition.getAssetLockProof().getOutput(); + + fetchAssetLockTransactionOutputMock = this.sinonSandbox.stub().resolves(output); + + applyIdentityTopUpTransition = applyIdentityTopUpTransitionFactory( + stateRepositoryMock, + fetchAssetLockTransactionOutputMock, + ); + }); + + it('should store identity created from state transition', async () => { + const balanceBeforeTopUp = identity.getBalance(); + + const balanceToTopUp = convertSatoshiToCredits( + stateTransition.getAssetLockProof().getOutput().satoshis, + ); + + await applyIdentityTopUpTransition(stateTransition); + + expect(identity.getBalance()).to.be.equal(balanceBeforeTopUp + balanceToTopUp); + expect(identity.getBalance()).to.be.greaterThan(balanceBeforeTopUp); + + expect(stateRepositoryMock.updateIdentity).to.have.been.calledOnceWithExactly( + identity, + executionContext, + ); + + expect(stateRepositoryMock.markAssetLockTransactionOutPointAsUsed).to.have.been + .calledOnceWithExactly( + stateTransition.getAssetLockProof().getOutPoint(), + executionContext, + ); + + expect(fetchAssetLockTransactionOutputMock) + .to.be.calledOnceWithExactly( + stateTransition.getAssetLockProof(), + executionContext, + ); + }); + + it('should store biggest possible identity on dry run', async () => { + const biggestPossibleIdentity = getBiggestPossibleIdentity(); + + const balanceBeforeTopUp = biggestPossibleIdentity.getBalance(); + + const balanceToTopUp = convertSatoshiToCredits( + stateTransition.getAssetLockProof().getOutput().satoshis, + ); + + executionContext.enableDryRun(); + + await applyIdentityTopUpTransition(stateTransition); + + executionContext.disableDryRun(); + + expect(biggestPossibleIdentity.getBalance()).to.be.equal(balanceBeforeTopUp + balanceToTopUp); + + expect(stateRepositoryMock.updateIdentity).to.have.been.calledOnceWithExactly( + biggestPossibleIdentity, + executionContext, + ); + + expect(stateRepositoryMock.markAssetLockTransactionOutPointAsUsed).to.have.been + .calledOnceWithExactly( + stateTransition.getAssetLockProof().getOutPoint(), + executionContext, + ); + + expect(fetchAssetLockTransactionOutputMock) + .to.be.calledOnceWithExactly( + stateTransition.getAssetLockProof(), + executionContext, + ); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/stateTransition/IdentityTopUpTransition/validation/state/validateIdentityTopUpTransitionStateFactory.spec.js b/packages/js-dpp/test/unit/identity/stateTransition/IdentityTopUpTransition/validation/state/validateIdentityTopUpTransitionStateFactory.spec.js new file mode 100644 index 00000000000..954e7ff4b8b --- /dev/null +++ b/packages/js-dpp/test/unit/identity/stateTransition/IdentityTopUpTransition/validation/state/validateIdentityTopUpTransitionStateFactory.spec.js @@ -0,0 +1,22 @@ +const getIdentityTopUpTransitionFixture = require('../../../../../../../lib/test/fixtures/getIdentityTopUpTransitionFixture'); + +const validateIdentityTopUpTransitionStateFactory = require( + '../../../../../../../lib/identity/stateTransition/IdentityTopUpTransition/validation/state/validateIdentityTopUpTransitionStateFactory', +); + +describe('validateIdentityTopUpTransitionStateFactory', () => { + let validateIdentityTopUpTransitionState; + let stateTransition; + + beforeEach(() => { + validateIdentityTopUpTransitionState = validateIdentityTopUpTransitionStateFactory(); + + stateTransition = getIdentityTopUpTransitionFixture(); + }); + + it('should return valid result', async () => { + const result = await validateIdentityTopUpTransitionState(stateTransition); + + expect(result.isValid()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/stateTransition/IdentityUpdateTransition/IdentityUpdateTransition.spec.js b/packages/js-dpp/test/unit/identity/stateTransition/IdentityUpdateTransition/IdentityUpdateTransition.spec.js new file mode 100644 index 00000000000..344ecc5e7fd --- /dev/null +++ b/packages/js-dpp/test/unit/identity/stateTransition/IdentityUpdateTransition/IdentityUpdateTransition.spec.js @@ -0,0 +1,219 @@ +const IdentityPublicKey = require('../../../../../lib/identity/IdentityPublicKey'); + +const stateTransitionTypes = require( + '../../../../../lib/stateTransition/stateTransitionTypes', +); + +const protocolVersion = require('../../../../../lib/version/protocolVersion'); +const Identifier = require('../../../../../lib/identifier/Identifier'); + +const getIdentityUpdateTransitionFixture = require('../../../../../lib/test/fixtures/getIdentityUpdateTransitionFixture'); +const generateRandomIdentifier = require('../../../../../lib/test/utils/generateRandomIdentifier'); + +describe('IdentityUpdateTransition', () => { + let rawStateTransition; + let stateTransition; + + beforeEach(() => { + stateTransition = getIdentityUpdateTransitionFixture(); + rawStateTransition = stateTransition.toObject(); + }); + + describe('#getType', () => { + it('should return IDENTITY_UPDATE type', () => { + expect(stateTransition.getType()).to.equal(stateTransitionTypes.IDENTITY_UPDATE); + }); + }); + + describe('#setIdentityId', () => { + it('should set identityId', () => { + const id = generateRandomIdentifier(); + + stateTransition.setIdentityId(id); + + expect(stateTransition.identityId).to.deep.equal(id); + }); + }); + + describe('#getIdentityId', () => { + it('should return identityId', () => { + expect(stateTransition.getIdentityId()).to.deep.equal(rawStateTransition.identityId); + }); + }); + + describe('#getRevision', () => { + it('should return revision', () => { + expect(stateTransition.getRevision()).to.equal(rawStateTransition.revision); + }); + }); + + describe('#setRevision', () => { + it('should set revision', () => { + stateTransition.setRevision(42); + + expect(stateTransition.revision).to.equal(42); + }); + }); + + describe('#getOwnerId', () => { + it('should return owner id', () => { + expect(stateTransition.getOwnerId()).to.deep.equal( + rawStateTransition.identityId, + ); + }); + }); + + describe('#getPublicKeysToAdd', () => { + it('should return public keys to add', () => { + expect(stateTransition.getPublicKeysToAdd()).to.deep.equal( + rawStateTransition.addPublicKeys.map((rawPublicKey) => new IdentityPublicKey(rawPublicKey)), + ); + }); + }); + + describe('#setPublicKeysToAdd', () => { + it('should set public keys to add', () => { + const publicKeys = [new IdentityPublicKey({ + id: 0, + type: IdentityPublicKey.TYPES.BLS12_381, + purpose: 0, + securityLevel: 0, + readOnly: true, + data: Buffer.from('01fac99ca2c8f39c286717c213e190aba4b7af76db320ec43f479b7d9a2012313a0ae59ca576edf801444bc694686694', 'hex'), + })]; + + stateTransition.setPublicKeysToAdd(publicKeys); + + expect(stateTransition.addPublicKeys).to.have.deep.members(publicKeys); + }); + }); + + describe('#getPublicKeyIdsToDisable', () => { + it('should return public key ids to disable', () => { + expect(stateTransition.getPublicKeyIdsToDisable()) + .to.deep.equal(stateTransition.disablePublicKeys); + }); + }); + + describe('#setPublicKeyIdsToDisable', () => { + it('should set public key ids to disable', () => { + stateTransition.setPublicKeyIdsToDisable([1, 2]); + + expect(stateTransition.disablePublicKeys).to.deep.equal([1, 2]); + }); + }); + + describe('#getPublicKeysDisabledAt', () => { + it('should return time to disable public keys', () => { + expect(stateTransition.getPublicKeysDisabledAt()) + .to.deep.equal(new Date(stateTransition.publicKeysDisabledAt)); + }); + }); + + describe('#setPublicKeysDisabledAt', () => { + it('should set time to disable public keys', () => { + const now = new Date(); + + stateTransition.setPublicKeysDisabledAt(now); + + expect(stateTransition.publicKeysDisabledAt).to.deep.equal(new Date(now)); + }); + }); + + describe('#toObject', () => { + it('should return raw state transition', () => { + rawStateTransition = stateTransition.toObject(); + + expect(rawStateTransition).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.IDENTITY_UPDATE, + signature: undefined, + identityId: rawStateTransition.identityId, + revision: rawStateTransition.revision, + publicKeysDisabledAt: rawStateTransition.publicKeysDisabledAt, + addPublicKeys: rawStateTransition.addPublicKeys, + disablePublicKeys: rawStateTransition.disablePublicKeys, + signaturePublicKeyId: undefined, + }); + }); + + it('should return raw state transition without signature', () => { + rawStateTransition = stateTransition.toObject({ skipSignature: true }); + + expect(rawStateTransition).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.IDENTITY_UPDATE, + identityId: rawStateTransition.identityId, + revision: rawStateTransition.revision, + publicKeysDisabledAt: rawStateTransition.publicKeysDisabledAt, + addPublicKeys: rawStateTransition.addPublicKeys, + disablePublicKeys: rawStateTransition.disablePublicKeys, + }); + }); + + it('should return raw state transition without optional properties', () => { + stateTransition.setPublicKeyIdsToDisable(undefined); + stateTransition.setPublicKeysDisabledAt(undefined); + stateTransition.setPublicKeysToAdd(undefined); + + rawStateTransition = stateTransition.toObject(); + + expect(rawStateTransition).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.IDENTITY_UPDATE, + signature: undefined, + identityId: rawStateTransition.identityId, + revision: rawStateTransition.revision, + signaturePublicKeyId: undefined, + }); + }); + }); + + describe('#toJSON ', () => { + it('should return JSON representation of state transition', () => { + const jsonStateTransition = stateTransition.toJSON(); + + expect(jsonStateTransition).to.deep.equal({ + protocolVersion: protocolVersion.latestVersion, + type: stateTransitionTypes.IDENTITY_UPDATE, + signature: undefined, + identityId: stateTransition.getIdentityId().toString(), + revision: rawStateTransition.revision, + publicKeysDisabledAt: rawStateTransition.publicKeysDisabledAt, + addPublicKeys: stateTransition.getPublicKeysToAdd().map((k) => k.toJSON()), + disablePublicKeys: rawStateTransition.disablePublicKeys, + signaturePublicKeyId: undefined, + }); + }); + }); + + describe('#getModifiedDataIds', () => { + it('should return ids of topped up identity', () => { + const result = stateTransition.getModifiedDataIds(); + + expect(result.length).to.be.equal(1); + const identityId = result[0]; + + expect(identityId).to.be.an.instanceOf(Identifier); + expect(identityId).to.be.deep.equal(rawStateTransition.identityId); + }); + }); + + describe('#isDataContractStateTransition', () => { + it('should return false', () => { + expect(stateTransition.isDataContractStateTransition()).to.be.false(); + }); + }); + + describe('#isDocumentStateTransition', () => { + it('should return false', () => { + expect(stateTransition.isDocumentStateTransition()).to.be.false(); + }); + }); + + describe('#isIdentityStateTransition', () => { + it('should return true', () => { + expect(stateTransition.isIdentityStateTransition()).to.be.true(); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/stateTransition/IdentityUpdateTransition/applyIdentityUpdateTransitionFactory.spec.js b/packages/js-dpp/test/unit/identity/stateTransition/IdentityUpdateTransition/applyIdentityUpdateTransitionFactory.spec.js new file mode 100644 index 00000000000..407e078b89a --- /dev/null +++ b/packages/js-dpp/test/unit/identity/stateTransition/IdentityUpdateTransition/applyIdentityUpdateTransitionFactory.spec.js @@ -0,0 +1,149 @@ +const applyIdentityUpdateTransitionFactory = require('../../../../../lib/identity/stateTransition/IdentityUpdateTransition/applyIdentityUpdateTransitionFactory'); +const createStateRepositoryMock = require('../../../../../lib/test/mocks/createStateRepositoryMock'); +const getIdentityUpdateTransitionFixture = require('../../../../../lib/test/fixtures/getIdentityUpdateTransitionFixture'); +const getIdentityFixture = require('../../../../../lib/test/fixtures/getIdentityFixture'); +const StateTransitionExecutionContext = require('../../../../../lib/stateTransition/StateTransitionExecutionContext'); +const getBiggestPossibleIdentity = require('../../../../../lib/identity/getBiggestPossibleIdentity'); + +describe('applyIdentityUpdateTransition', () => { + let applyIdentityUpdateTransition; + let stateRepositoryMock; + let stateTransition; + let identity; + let executionContext; + + beforeEach(function beforeEach() { + stateTransition = getIdentityUpdateTransitionFixture(); + stateTransition.setRevision(stateTransition.getRevision() + 1); + identity = getIdentityFixture(); + + executionContext = new StateTransitionExecutionContext(); + + stateTransition.setExecutionContext(executionContext); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchIdentity.resolves(identity); + + applyIdentityUpdateTransition = applyIdentityUpdateTransitionFactory( + stateRepositoryMock, + ); + }); + + it('should add public keys', async () => { + stateTransition.setPublicKeysDisabledAt(undefined); + stateTransition.setPublicKeyIdsToDisable(undefined); + + await applyIdentityUpdateTransition(stateTransition); + + expect(identity.getPublicKeys()).to.have.lengthOf(3); + + expect(identity.getPublicKeyById(3).toObject()) + .to.deep.equal(stateTransition.getPublicKeysToAdd()[0]); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + stateTransition.getIdentityId(), + executionContext, + ); + + expect(stateRepositoryMock.updateIdentity).to.be.calledOnceWithExactly( + identity, + executionContext, + ); + + const publicKeyHashes = stateTransition.getPublicKeysToAdd() + .map((publicKey) => publicKey.hash()); + + expect(stateRepositoryMock.storeIdentityPublicKeyHashes).to.be.calledOnceWithExactly( + identity.getId(), + publicKeyHashes, + executionContext, + ); + + expect(identity.getRevision()).to.equal(stateTransition.getRevision()); + }); + + it('should disable public key', async () => { + stateTransition.setPublicKeysToAdd(undefined); + + await applyIdentityUpdateTransition(stateTransition); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + stateTransition.getIdentityId(), + executionContext, + ); + + expect(stateRepositoryMock.storeIdentityPublicKeyHashes).to.not.be.called(); + + expect(stateRepositoryMock.updateIdentity).to.be.calledOnceWithExactly( + identity, + executionContext, + ); + + const [id] = stateTransition.getPublicKeyIdsToDisable(); + + expect(identity.getPublicKeyById(id).getDisabledAt()) + .to.equal(stateTransition.getPublicKeysDisabledAt().getTime()); + + expect(identity.getRevision()).to.equal(stateTransition.getRevision()); + }); + + it('should not add public keys on dry run', async () => { + const biggestPossibleIdentity = getBiggestPossibleIdentity(); + + stateTransition.setPublicKeysDisabledAt(undefined); + stateTransition.setPublicKeyIdsToDisable(undefined); + + stateTransition.getExecutionContext().enableDryRun(); + + await applyIdentityUpdateTransition(stateTransition); + + stateTransition.getExecutionContext().disableDryRun(); + + expect(identity.getPublicKeys()).to.have.lengthOf(2); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + stateTransition.getIdentityId(), + executionContext, + ); + + expect(stateRepositoryMock.updateIdentity).to.be.calledOnceWithExactly( + biggestPossibleIdentity, + executionContext, + ); + + const publicKeyHashes = stateTransition.getPublicKeysToAdd() + .map((publicKey) => publicKey.hash()); + + expect(stateRepositoryMock.storeIdentityPublicKeyHashes).to.be.calledOnceWithExactly( + biggestPossibleIdentity.getId(), + publicKeyHashes, + executionContext, + ); + + expect(biggestPossibleIdentity.getRevision()).to.equal(stateTransition.getRevision()); + }); + + it('should use biggestPossibleIdentity on dry run', async () => { + const biggestPossibleIdentity = getBiggestPossibleIdentity(); + + stateTransition.setPublicKeysToAdd(undefined); + + stateTransition.getExecutionContext().enableDryRun(); + + await applyIdentityUpdateTransition(stateTransition); + + stateTransition.getExecutionContext().disableDryRun(); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + stateTransition.getIdentityId(), + executionContext, + ); + + expect(stateRepositoryMock.storeIdentityPublicKeyHashes).to.not.be.called(); + + expect(stateRepositoryMock.updateIdentity).to.be.calledOnceWithExactly( + biggestPossibleIdentity, + executionContext, + ); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/stateTransition/assetLockProof/createAssetLockProofInstance.spec.js b/packages/js-dpp/test/unit/identity/stateTransition/assetLockProof/createAssetLockProofInstance.spec.js new file mode 100644 index 00000000000..ddeba170cf5 --- /dev/null +++ b/packages/js-dpp/test/unit/identity/stateTransition/assetLockProof/createAssetLockProofInstance.spec.js @@ -0,0 +1,21 @@ +const createAssetLockProofInstance = require('../../../../../lib/identity/stateTransition/assetLockProof/createAssetLockProofInstance'); +const getChainAssetLockFixture = require('../../../../../lib/test/fixtures/getChainAssetLockProofFixture'); +const getInstantAssetLockProofFixture = require('../../../../../lib/test/fixtures/getInstantAssetLockProofFixture'); +const ChainAssetLockProof = require('../../../../../lib/identity/stateTransition/assetLockProof/chain/ChainAssetLockProof'); +const InstantAssetLockProof = require('../../../../../lib/identity/stateTransition/assetLockProof/instant/InstantAssetLockProof'); + +describe('createAssetLockProofInstance', () => { + it('should create an instance of InstantAssetLockProof', () => { + const assetLockProofFixture = getInstantAssetLockProofFixture(); + const instance = createAssetLockProofInstance(assetLockProofFixture.toObject()); + + expect(instance).to.be.an.instanceOf(InstantAssetLockProof); + }); + + it('should create an instance of ChainAssetLockProof', () => { + const assetLockProofFixture = getChainAssetLockFixture(); + const instance = createAssetLockProofInstance(assetLockProofFixture.toObject()); + + expect(instance).to.be.an.instanceOf(ChainAssetLockProof); + }); +}); diff --git a/packages/js-dpp/test/unit/identity/stateTransition/assetLockProof/fetchAssetLockPublicKeyHashFactory.spec.js b/packages/js-dpp/test/unit/identity/stateTransition/assetLockProof/fetchAssetLockPublicKeyHashFactory.spec.js new file mode 100644 index 00000000000..d364916c5a1 --- /dev/null +++ b/packages/js-dpp/test/unit/identity/stateTransition/assetLockProof/fetchAssetLockPublicKeyHashFactory.spec.js @@ -0,0 +1,36 @@ +const fetchAssetLockPublicKeyHashFactory = require('../../../../../lib/identity/stateTransition/assetLockProof/fetchAssetLockPublicKeyHashFactory'); +const getInstantAssetLockProofFixture = require('../../../../../lib/test/fixtures/getInstantAssetLockProofFixture'); +const AssetLockOutputNotFoundError = require('../../../../../lib/identity/errors/AssetLockOutputNotFoundError'); + +describe('fetchAssetLockPublicKeyHashFactory', () => { + let fetchAssetLockPublicKeyHash; + let fetchAssetLockTransactionOutputMock; + let assetLockProof; + + beforeEach(function beforeEach() { + fetchAssetLockTransactionOutputMock = this.sinonSandbox.stub(); + + fetchAssetLockPublicKeyHash = fetchAssetLockPublicKeyHashFactory( + fetchAssetLockTransactionOutputMock, + ); + + assetLockProof = getInstantAssetLockProofFixture(); + }); + + it('should return public key hash for specified asset lock proof', async () => { + fetchAssetLockTransactionOutputMock.resolves(assetLockProof.getOutput()); + + const result = await fetchAssetLockPublicKeyHash(assetLockProof); + + expect(result).to.deep.equal(assetLockProof.getOutput().script.getData()); + }); + + it('should throw AssetLockOutputNotFoundError if output is not found', async () => { + try { + await fetchAssetLockPublicKeyHash(assetLockProof); + expect.fail('should throw AssetLockOutputNotFoundError'); + } catch (e) { + expect(e).to.be.an.instanceOf(AssetLockOutputNotFoundError); + } + }); +}); diff --git a/packages/js-dpp/test/unit/identity/validation/validateRequiredPurposeAndSecurityLevelFactory.spec.js b/packages/js-dpp/test/unit/identity/validation/validateRequiredPurposeAndSecurityLevelFactory.spec.js new file mode 100644 index 00000000000..6780c7d5fa7 --- /dev/null +++ b/packages/js-dpp/test/unit/identity/validation/validateRequiredPurposeAndSecurityLevelFactory.spec.js @@ -0,0 +1,47 @@ +const IdentityPublicKey = require('../../../../lib/identity/IdentityPublicKey'); + +const validateRequiredPurposeAndSecurityLevelFactory = require('../../../../lib/identity/validation/validateRequiredPurposeAndSecurityLevelFactory'); + +const { expectValidationError } = require('../../../../lib/test/expect/expectError'); + +const ValidationResult = require('../../../../lib/validation/ValidationResult'); + +const MissingMasterPublicKeyError = require('../../../../lib/errors/consensus/basic/identity/MissingMasterPublicKeyError'); + +describe('validateRequiredPurposeAndSecurityLevel', () => { + let validateRequiredPurposeAndSecurityLevel; + + beforeEach(() => { + validateRequiredPurposeAndSecurityLevel = ( + validateRequiredPurposeAndSecurityLevelFactory() + ); + }); + + it('should return invalid result if the state transition does not contain master key', async () => { + const result = await validateRequiredPurposeAndSecurityLevel([{ + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.CRITICAL, + }, { + // this key must be filtered out + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + disabledAt: 42, + }]); + + expectValidationError(result, MissingMasterPublicKeyError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1046); + }); + + it('should return valid result', async () => { + const result = await validateRequiredPurposeAndSecurityLevel([{ + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + }]); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + }); +}); diff --git a/packages/js-dpp/test/unit/stateTransition/StateTransitionFactory.spec.js b/packages/js-dpp/test/unit/stateTransition/StateTransitionFactory.spec.js new file mode 100644 index 00000000000..543ca334d2a --- /dev/null +++ b/packages/js-dpp/test/unit/stateTransition/StateTransitionFactory.spec.js @@ -0,0 +1,160 @@ +const getDataContractFixture = require('../../../lib/test/fixtures/getDataContractFixture'); + +const DataContractCreateTransition = require('../../../lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition'); + +const ValidationResult = require('../../../lib/validation/ValidationResult'); + +const InvalidStateTransitionError = require('../../../lib/stateTransition/errors/InvalidStateTransitionError'); + +const SerializedObjectParsingError = require('../../../lib/errors/consensus/basic/decode/SerializedObjectParsingError'); + +const createDPPMock = require('../../../lib/test/mocks/createDPPMock'); +const StateTransitionFactory = require('../../../lib/stateTransition/StateTransitionFactory'); +const SomeConsensusError = require('../../../lib/test/mocks/SomeConsensusError'); + +describe('StateTransitionFactory', () => { + let validateStateTransitionBasicMock; + let createStateTransitionMock; + let factory; + let stateTransition; + let rawStateTransition; + let decodeProtocolEntityMock; + let dppMock; + + beforeEach(function beforeEach() { + const dataContract = getDataContractFixture(); + + stateTransition = new DataContractCreateTransition({ + dataContract: dataContract.toObject(), + entropy: dataContract.getEntropy(), + }); + rawStateTransition = stateTransition.toObject(); + + decodeProtocolEntityMock = this.sinonSandbox.stub(); + + validateStateTransitionBasicMock = this.sinonSandbox.stub(); + createStateTransitionMock = this.sinonSandbox.stub().returns(stateTransition); + + dppMock = createDPPMock(); + + factory = new StateTransitionFactory( + validateStateTransitionBasicMock, + createStateTransitionMock, + dppMock, + decodeProtocolEntityMock, + ); + }); + + describe('createFromObject', () => { + it('should return new State Transition with data from passed object', async () => { + validateStateTransitionBasicMock.returns(new ValidationResult()); + + const result = await factory.createFromObject(rawStateTransition); + + expect(result).to.equal(stateTransition); + + expect(validateStateTransitionBasicMock).to.have.been.calledOnceWith(rawStateTransition); + + expect(createStateTransitionMock).to.have.been.calledOnceWith(rawStateTransition); + }); + + it('should return new State Transition without validation if "skipValidation" option is passed', async () => { + const result = await factory.createFromObject(rawStateTransition, { skipValidation: true }); + + expect(result).to.equal(stateTransition); + + expect(validateStateTransitionBasicMock).to.have.not.been.called(); + + expect(createStateTransitionMock).to.have.been.calledOnceWith(rawStateTransition); + }); + + it('should throw InvalidStateTransitionError if passed object is not valid', async () => { + const validationError = new SomeConsensusError('test'); + + validateStateTransitionBasicMock.returns(new ValidationResult([validationError])); + + try { + await factory.createFromObject(rawStateTransition); + + expect.fail('InvalidStateTransitionError is not thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidStateTransitionError); + expect(e.getRawStateTransition()).to.equal(rawStateTransition); + + expect(e.getErrors()).to.have.length(1); + + const [consensusError] = e.getErrors(); + + expect(consensusError).to.equal(validationError); + + expect(validateStateTransitionBasicMock).to.have.been.calledOnceWith( + rawStateTransition, + ); + } + }); + }); + + describe('createFromBuffer', () => { + let serializedStateTransition; + + beforeEach(function beforeEach() { + this.sinonSandbox.stub(factory, 'createFromObject'); + + serializedStateTransition = stateTransition.toBuffer(); + }); + + afterEach(() => { + factory.createFromObject.restore(); + }); + + it('should return new State Transition from serialized contract', async () => { + decodeProtocolEntityMock.returns([rawStateTransition.protocolVersion, rawStateTransition]); + + factory.createFromObject.resolves(stateTransition); + + const result = await factory.createFromBuffer(serializedStateTransition); + + expect(result).to.equal(stateTransition); + + expect(factory.createFromObject).to.have.been.calledOnceWith(rawStateTransition); + + expect(decodeProtocolEntityMock).to.have.been.calledOnceWith( + serializedStateTransition, + ); + }); + + it('should throw InvalidStateTransitionError if the decoding fails with consensus error', async () => { + const parsingError = new SerializedObjectParsingError( + serializedStateTransition, + new Error(), + ); + + decodeProtocolEntityMock.throws(parsingError); + + try { + await factory.createFromBuffer(serializedStateTransition); + + expect.fail('should throw InvalidStateTransitionError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidStateTransitionError); + + const [innerError] = e.getErrors(); + expect(innerError).to.be.equal(parsingError); + } + }); + + it('should throw an error if decoding fails with any other error', async () => { + const otherParsingError = new Error(); + + decodeProtocolEntityMock.throws(otherParsingError); + + try { + await factory.createFromBuffer(serializedStateTransition); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.equal(otherParsingError); + } + }); + }); +}); diff --git a/packages/js-dpp/test/unit/stateTransition/createStateTransitionFactory.spec.js b/packages/js-dpp/test/unit/stateTransition/createStateTransitionFactory.spec.js new file mode 100644 index 00000000000..b13b086734d --- /dev/null +++ b/packages/js-dpp/test/unit/stateTransition/createStateTransitionFactory.spec.js @@ -0,0 +1,72 @@ +const createStateTransitionFactory = require('../../../lib/stateTransition/createStateTransitionFactory'); + +const DataContractCreateTransition = require('../../../lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition'); +const DocumentsBatchTransition = require('../../../lib/document/stateTransition/DocumentsBatchTransition/DocumentsBatchTransition'); + +const getDataContractFixture = require('../../../lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('../../../lib/test/fixtures/getDocumentsFixture'); +const getDocumentTranstionsFixture = require('../../../lib/test/fixtures/getDocumentTransitionsFixture'); + +const createStateRepositoryMock = require('../../../lib/test/mocks/createStateRepositoryMock'); + +const InvalidStateTransitionTypeError = require('../../../lib/stateTransition/errors/InvalidStateTransitionTypeError'); + +describe('createStateTransitionFactory', () => { + let createStateTransition; + let stateRepositoryMock; + let dataContract; + + beforeEach(function beforeEach() { + dataContract = getDataContractFixture(); + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchDataContract.resolves(dataContract); + createStateTransition = createStateTransitionFactory(stateRepositoryMock); + }); + + it('should return DataContractCreateTransition if type is DATA_CONTRACT_CREATE', async () => { + const stateTransition = new DataContractCreateTransition({ + dataContract: dataContract.toObject(), + entropy: dataContract.getEntropy(), + }); + + const result = await createStateTransition(stateTransition.toObject()); + + expect(result).to.be.instanceOf(DataContractCreateTransition); + expect(result.getDataContract().toObject()).to.deep.equal(dataContract.toObject()); + }); + + it('should return DocumentsBatchTransition if type is DOCUMENTS', async () => { + const documents = getDocumentsFixture(dataContract); + const documentTransitions = getDocumentTranstionsFixture({ + create: documents, + }); + + const stateTransition = new DocumentsBatchTransition({ + ownerId: getDocumentsFixture.ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + + const result = await createStateTransition(stateTransition.toObject()); + + expect(result).to.be.instanceOf(DocumentsBatchTransition); + expect(result.getTransitions().map((t) => t.toObject())).to.have.deep.members( + documentTransitions.map((t) => t.toObject()), + ); + }); + + it('should throw InvalidStateTransitionTypeError if type is invalid', async () => { + const rawStateTransition = { + type: 666, + }; + + try { + await createStateTransition(rawStateTransition); + + expect.fail('InvalidStateTransitionTypeError is not thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidStateTransitionTypeError); + expect(e.getType()).to.equal(rawStateTransition.type); + } + }); +}); diff --git a/packages/js-dpp/test/unit/stateTransition/errors/InvalidStateTransitionError.spec.js b/packages/js-dpp/test/unit/stateTransition/errors/InvalidStateTransitionError.spec.js new file mode 100644 index 00000000000..6099aac354c --- /dev/null +++ b/packages/js-dpp/test/unit/stateTransition/errors/InvalidStateTransitionError.spec.js @@ -0,0 +1,51 @@ +const InvalidStateTransitionError = require('../../../../lib/stateTransition/errors/InvalidStateTransitionError'); +const DataContractCreateTransition = require('../../../../lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition'); +const getDataContractFixture = require('../../../../lib/test/fixtures/getDataContractFixture'); + +describe('InvalidStateTransitionError', () => { + let rawStateTransition; + let error; + + beforeEach(() => { + error = new Error('Some error'); + + const dataContract = getDataContractFixture(); + const dataContractCreateTransition = new DataContractCreateTransition({ + dataContract: dataContract.toObject(), + entropy: dataContract.getEntropy(), + }); + rawStateTransition = dataContractCreateTransition.toObject(); + }); + + it('should return errors', () => { + const errors = [error]; + + const invalidStateTransitionError = new InvalidStateTransitionError(errors, rawStateTransition); + + expect(invalidStateTransitionError.getErrors()).to.deep.equal(errors); + }); + + it('should return State Transition', async () => { + const errors = [error]; + + const invalidStateTransitionError = new InvalidStateTransitionError(errors, rawStateTransition); + + expect(invalidStateTransitionError.getRawStateTransition()).to.deep.equal(rawStateTransition); + }); + + it('should contain message for 1 error', async () => { + const errors = [error]; + + const invalidStateTransitionError = new InvalidStateTransitionError(errors, rawStateTransition); + + expect(invalidStateTransitionError.message).to.equal(`Invalid State Transition: "${error.message}"`); + }); + + it('should contain message for multiple errors', async () => { + const errors = [error, error]; + + const invalidStateTransitionError = new InvalidStateTransitionError(errors, rawStateTransition); + + expect(invalidStateTransitionError.message).to.equal(`Invalid State Transition: "${error.message}" and 1 more`); + }); +}); diff --git a/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionBasicFactory.spec.js b/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionBasicFactory.spec.js new file mode 100644 index 00000000000..3a5eef70e22 --- /dev/null +++ b/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionBasicFactory.spec.js @@ -0,0 +1,138 @@ +const validateStateTransitionBasicFactory = require('../../../../lib/stateTransition/validation/validateStateTransitionBasicFactory'); + +const DataContractFactory = require('../../../../lib/dataContract/DataContractFactory'); + +const stateTransitionTypes = require('../../../../lib/stateTransition/stateTransitionTypes'); + +const getDataContractFixture = require('../../../../lib/test/fixtures/getDataContractFixture'); + +const StateTransitionMaxSizeExceededError = require('../../../../lib/errors/consensus/basic/stateTransition/StateTransitionMaxSizeExceededError'); + +const { + expectValidationError, +} = require('../../../../lib/test/expect/expectError'); + +const ValidationResult = require('../../../../lib/validation/ValidationResult'); + +const MissingStateTransitionTypeError = require('../../../../lib/errors/consensus/basic/stateTransition/MissingStateTransitionTypeError'); +const InvalidStateTransitionTypeError = require('../../../../lib/errors/consensus/basic/stateTransition/InvalidStateTransitionTypeError'); +const createDPPMock = require('../../../../lib/test/mocks/createDPPMock'); +const SomeConsensusError = require('../../../../lib/test/mocks/SomeConsensusError'); +const IdentityPublicKey = require('../../../../lib/identity/IdentityPublicKey'); + +describe('validateStateTransitionBasicFactory', () => { + let validateStateTransitionBasic; + let validationFunctionMock; + let rawStateTransition; + let dataContract; + let dataContractFactory; + let createStateTransitionMock; + let stateTransition; + + beforeEach(async function beforeEach() { + validationFunctionMock = this.sinonSandbox.stub(); + + const validationFunctionsByType = { + [stateTransitionTypes.DATA_CONTRACT_CREATE]: validationFunctionMock, + }; + + dataContract = getDataContractFixture(); + + const privateKey = '9b67f852093bc61cea0eeca38599dbfba0de28574d2ed9b99d10d33dc1bde7b2'; + + dataContractFactory = new DataContractFactory(createDPPMock(), undefined); + + stateTransition = dataContractFactory.createDataContractCreateTransition(dataContract); + await stateTransition.signByPrivateKey(privateKey, IdentityPublicKey.TYPES.ECDSA_SECP256K1); + + rawStateTransition = stateTransition.toObject(); + + createStateTransitionMock = this.sinonSandbox.stub().resolves(stateTransition); + + validateStateTransitionBasic = validateStateTransitionBasicFactory( + validationFunctionsByType, + createStateTransitionMock, + ); + }); + + it('should return invalid result if ST type is missing', async () => { + delete rawStateTransition.type; + + const result = await validateStateTransitionBasic(rawStateTransition); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.be.instanceof(MissingStateTransitionTypeError); + + expect(validationFunctionMock).to.not.be.called(); + }); + + it('should return invalid result if ST type is not valid', async () => { + rawStateTransition.type = 666; + + const result = await validateStateTransitionBasic(rawStateTransition); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.be.instanceof(InvalidStateTransitionTypeError); + + expect(validationFunctionMock).to.not.be.called(); + }); + + it('should return invalid result if ST is invalid against validation function', async () => { + const extensionError = new SomeConsensusError('test'); + const extensionResult = new ValidationResult([ + extensionError, + ]); + + validationFunctionMock.returns(extensionResult); + + const result = await validateStateTransitionBasic(rawStateTransition); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.equal(extensionError); + + expect(validationFunctionMock).to.be.calledOnceWith(rawStateTransition); + }); + + it('should return invalid result if ST size is more than 16 kb', async () => { + const validationFunctionResult = new ValidationResult(); + + validationFunctionMock.returns(validationFunctionResult); + + // generate big state transition + for (let i = 0; i < 500; i++) { + stateTransition.dataContract.documents[`anotherContract${i}`] = rawStateTransition.dataContract.documents.niceDocument; + } + + const result = await validateStateTransitionBasic( + rawStateTransition, + ); + + expectValidationError(result, StateTransitionMaxSizeExceededError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1045); + }); + + it('should return valid result', async () => { + const extensionResult = new ValidationResult(); + + validationFunctionMock.returns(extensionResult); + + const result = await validateStateTransitionBasic(rawStateTransition); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(validationFunctionMock).to.be.calledOnceWith(rawStateTransition); + }); +}); diff --git a/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionFeeFactory.spec.js b/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionFeeFactory.spec.js new file mode 100644 index 00000000000..1fbda5f1511 --- /dev/null +++ b/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionFeeFactory.spec.js @@ -0,0 +1,372 @@ +const validateStateTransitionFeeFactory = require('../../../../lib/stateTransition/validation/validateStateTransitionFeeFactory'); + +const createStateRepositoryMock = require('../../../../lib/test/mocks/createStateRepositoryMock'); + +const getIdentityFixture = require('../../../../lib/test/fixtures/getIdentityFixture'); +const getDocumentsFixture = require('../../../../lib/test/fixtures/getDocumentsFixture'); +const getDataContractFixture = require('../../../../lib/test/fixtures/getDataContractFixture'); +const getIdentityCreateTransitionFixture = require('../../../../lib/test/fixtures/getIdentityCreateTransitionFixture'); +const getDocumentTransitionsFixture = require('../../../../lib/test/fixtures/getDocumentTransitionsFixture'); +const getIdentityTopUpTransitionFixture = require('../../../../lib/test/fixtures/getIdentityTopUpTransitionFixture'); + +const DataContractCreateTransition = require('../../../../lib/dataContract/stateTransition/DataContractCreateTransition/DataContractCreateTransition'); +const DocumentsBatchTransition = require('../../../../lib/document/stateTransition/DocumentsBatchTransition/DocumentsBatchTransition'); + +const { expectValidationError } = require('../../../../lib/test/expect/expectError'); + +const IdentityBalanceIsNotEnoughError = require('../../../../lib/errors/consensus/fee/BalanceIsNotEnoughError'); +const InvalidStateTransitionTypeError = require('../../../../lib/stateTransition/errors/InvalidStateTransitionTypeError'); + +const { RATIO } = require('../../../../lib/identity/creditsConverter'); +const StateTransitionExecutionContext = require('../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('validateStateTransitionFeeFactory', () => { + let stateRepositoryMock; + let validateStateTransitionFee; + let identity; + let dataContract; + let calculateStateTransitionFeeMock; + let fetchAssetLockTransactionOutputMock; + + beforeEach(function beforeEach() { + identity = getIdentityFixture(); + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchIdentity.resolves(identity); + + const output = getIdentityCreateTransitionFixture().getAssetLockProof().getOutput(); + + calculateStateTransitionFeeMock = this.sinonSandbox.stub().returns(42); + fetchAssetLockTransactionOutputMock = this.sinonSandbox.stub().resolves(output); + + validateStateTransitionFee = validateStateTransitionFeeFactory( + stateRepositoryMock, + calculateStateTransitionFeeMock, + fetchAssetLockTransactionOutputMock, + ); + + dataContract = getDataContractFixture(); + }); + + describe('DataContractCreateTransition', () => { + let dataContractCreateTransition; + + beforeEach(() => { + dataContractCreateTransition = new DataContractCreateTransition({ + dataContract: dataContract.toObject(), + entropy: dataContract.getEntropy(), + }); + }); + + it('should return invalid result if balance is not enough', async () => { + identity.balance = 1; + + const result = await validateStateTransitionFee(dataContractCreateTransition); + + expectValidationError(result, IdentityBalanceIsNotEnoughError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(3000); + expect(error.getBalance()).to.equal(identity.balance); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + dataContract.getOwnerId(), + dataContractCreateTransition.getExecutionContext(), + ); + + expect(calculateStateTransitionFeeMock).to.be.calledOnceWithExactly( + dataContractCreateTransition, + ); + + expect(fetchAssetLockTransactionOutputMock).to.not.be.called(); + }); + + it('should return valid result', async () => { + identity.balance = 42; + + const result = await validateStateTransitionFee(dataContractCreateTransition); + + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + dataContract.getOwnerId(), + dataContractCreateTransition.getExecutionContext(), + ); + + expect(calculateStateTransitionFeeMock).to.be.calledOnceWithExactly( + dataContractCreateTransition, + ); + + expect(fetchAssetLockTransactionOutputMock).to.not.be.called(); + }); + }); + + describe('DocumentsBatchTransition', () => { + let documentsBatchTransition; + + beforeEach(() => { + const documents = getDocumentsFixture(dataContract); + + const documentTransitions = getDocumentTransitionsFixture({ + create: documents, + }); + + documentsBatchTransition = new DocumentsBatchTransition({ + ownerId: getDocumentsFixture.ownerId, + contractId: dataContract.getId(), + transitions: documentTransitions.map((t) => t.toObject()), + }, [dataContract]); + }); + + it('should return invalid result if balance is not enough', async () => { + identity.balance = 1; + + const result = await validateStateTransitionFee(documentsBatchTransition); + + expectValidationError(result, IdentityBalanceIsNotEnoughError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(3000); + expect(error.getBalance()).to.equal(identity.balance); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + getDocumentsFixture.ownerId, + documentsBatchTransition.getExecutionContext(), + ); + + expect(calculateStateTransitionFeeMock).to.be.calledOnceWithExactly( + documentsBatchTransition, + ); + + expect(fetchAssetLockTransactionOutputMock).to.not.be.called(); + }); + + it('should return valid result', async () => { + identity.balance = 42; + + const result = await validateStateTransitionFee(documentsBatchTransition); + + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + getDocumentsFixture.ownerId, + documentsBatchTransition.getExecutionContext(), + ); + + expect(calculateStateTransitionFeeMock).to.be.calledOnceWithExactly( + documentsBatchTransition, + ); + + expect(fetchAssetLockTransactionOutputMock).to.not.be.called(); + }); + + it('should not increase balance on dry run', async () => { + documentsBatchTransition.getExecutionContext().enableDryRun(); + + const result = await validateStateTransitionFee(documentsBatchTransition); + + documentsBatchTransition.getExecutionContext().disableDryRun(); + + expect(result.isValid()).to.be.true(); + + expect(calculateStateTransitionFeeMock).to.be.not.called(); + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + getDocumentsFixture.ownerId, + documentsBatchTransition.getExecutionContext(), + ); + expect(fetchAssetLockTransactionOutputMock).to.not.be.called(); + }); + }); + + describe('IdentityCreateStateTransition', () => { + let identityCreateTransition; + let outputAmount; + + beforeEach(() => { + identityCreateTransition = getIdentityCreateTransitionFixture(); + + const { satoshis } = identityCreateTransition + .getAssetLockProof() + .getOutput(); + + outputAmount = satoshis * RATIO; + }); + + it('should return invalid result if asset lock output amount is not enough', async () => { + calculateStateTransitionFeeMock.returns(outputAmount + 1); + + const result = await validateStateTransitionFee(identityCreateTransition); + + expectValidationError(result, IdentityBalanceIsNotEnoughError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(3000); + expect(error.getBalance()).to.equal(outputAmount); + + expect(stateRepositoryMock.fetchIdentity).to.be.not.called(); + + expect(calculateStateTransitionFeeMock).to.be.calledOnceWithExactly( + identityCreateTransition, + ); + + expect(fetchAssetLockTransactionOutputMock).to.be.calledOnceWithExactly( + identityCreateTransition.getAssetLockProof(), + identityCreateTransition.getExecutionContext(), + ); + }); + + it('should return valid result', async () => { + calculateStateTransitionFeeMock.returns(outputAmount); + + const result = await validateStateTransitionFee(identityCreateTransition); + + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchIdentity).to.be.not.called(); + + expect(calculateStateTransitionFeeMock).to.be.calledOnceWithExactly( + identityCreateTransition, + ); + + expect(fetchAssetLockTransactionOutputMock).to.be.calledOnceWithExactly( + identityCreateTransition.getAssetLockProof(), + identityCreateTransition.getExecutionContext(), + ); + }); + + it('should not increase balance on dry run', async () => { + identityCreateTransition.getExecutionContext().enableDryRun(); + + const result = await validateStateTransitionFee(identityCreateTransition); + + identityCreateTransition.getExecutionContext().disableDryRun(); + + expect(result.isValid()).to.be.true(); + + expect(calculateStateTransitionFeeMock).to.be.not.called(); + expect(fetchAssetLockTransactionOutputMock).to.be.calledOnceWithExactly( + identityCreateTransition.getAssetLockProof(), + identityCreateTransition.getExecutionContext(), + ); + }); + }); + + describe('IdentityTopUpTransition', () => { + let identityTopUpTransition; + let outputAmount; + + beforeEach(() => { + identityTopUpTransition = getIdentityTopUpTransitionFixture(); + + const { satoshis } = identityTopUpTransition + .getAssetLockProof() + .getOutput(); + + outputAmount = satoshis * RATIO; + }); + + it('should return invalid result if sum of balance and asset lock output amount is not enough', async () => { + identity.balance = 1; + + calculateStateTransitionFeeMock.returns(outputAmount + 2); + + const result = await validateStateTransitionFee(identityTopUpTransition); + + expectValidationError(result, IdentityBalanceIsNotEnoughError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(3000); + expect(error.getBalance()).to.equal(outputAmount + identity.balance); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + identityTopUpTransition.getIdentityId(), + identityTopUpTransition.getExecutionContext(), + ); + + expect(calculateStateTransitionFeeMock).to.be.calledOnceWithExactly( + identityTopUpTransition, + ); + + expect(fetchAssetLockTransactionOutputMock).to.be.calledOnceWithExactly( + identityTopUpTransition.getAssetLockProof(), + identityTopUpTransition.getExecutionContext(), + ); + }); + + it('should return valid result', async () => { + identity.balance = 41; + + calculateStateTransitionFeeMock.returns(outputAmount - 1); + + const result = await validateStateTransitionFee(identityTopUpTransition); + + expect(result.isValid()).to.be.true(); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + identityTopUpTransition.getIdentityId(), + identityTopUpTransition.getExecutionContext(), + ); + + expect(calculateStateTransitionFeeMock).to.be.calledOnceWithExactly( + identityTopUpTransition, + ); + + expect(fetchAssetLockTransactionOutputMock).to.be.calledOnceWithExactly( + identityTopUpTransition.getAssetLockProof(), + identityTopUpTransition.getExecutionContext(), + ); + }); + + it('should not increase balance on dry run', async () => { + identityTopUpTransition.getExecutionContext().enableDryRun(); + + const result = await validateStateTransitionFee(identityTopUpTransition); + + identityTopUpTransition.getExecutionContext().disableDryRun(); + + expect(result.isValid()).to.be.true(); + + expect(calculateStateTransitionFeeMock).to.be.not.called(); + expect(fetchAssetLockTransactionOutputMock).to.be.calledOnceWithExactly( + identityTopUpTransition.getAssetLockProof(), + identityTopUpTransition.getExecutionContext(), + ); + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + identityTopUpTransition.getIdentityId(), + identityTopUpTransition.getExecutionContext(), + ); + }); + }); + + it('should throw InvalidStateTransitionTypeError on invalid State Transition', async function it() { + const rawStateTransitionMock = { + data: 'sample data', + type: -1, + }; + + const stateTransitionMock = { + getType: this.sinonSandbox.stub().returns(rawStateTransitionMock.type), + toBuffer: this.sinonSandbox.stub().returns(Buffer.alloc(0)), + toObject: this.sinonSandbox.stub().returns(rawStateTransitionMock), + getExecutionContext: this.sinonSandbox.stub().returns(new StateTransitionExecutionContext()), + }; + + try { + await validateStateTransitionFee(stateTransitionMock); + + expect.fail('should throw InvalidStateTransitionTypeError'); + } catch (error) { + expect(error).to.be.an.instanceOf(InvalidStateTransitionTypeError); + expect(error.getType()).to.equal(rawStateTransitionMock.type); + + expect(calculateStateTransitionFeeMock).to.not.be.called(); + expect(stateRepositoryMock.fetchIdentity).to.not.be.called(); + + expect(fetchAssetLockTransactionOutputMock).to.not.be.called(); + } + }); +}); diff --git a/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionIdentitySignatureFactory.spec.js b/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionIdentitySignatureFactory.spec.js new file mode 100644 index 00000000000..c67b9471c02 --- /dev/null +++ b/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionIdentitySignatureFactory.spec.js @@ -0,0 +1,339 @@ +const validateStateTransitionSignatureFactory = require('../../../../lib/stateTransition/validation/validateStateTransitionIdentitySignatureFactory'); +const ValidationResult = require('../../../../lib/validation/ValidationResult'); +const IdentityPublicKey = require('../../../../lib/identity/IdentityPublicKey'); +const InvalidStateTransitionSignatureError = require('../../../../lib/errors/consensus/signature/InvalidStateTransitionSignatureError'); +const MissingPublicKeyError = require('../../../../lib/errors/consensus/signature/MissingPublicKeyError'); +const generateRandomIdentifier = require('../../../../lib/test/utils/generateRandomIdentifier'); + +const { expectValidationError } = require('../../../../lib/test/expect/expectError'); +const stateTransitionTypes = require('../../../../lib/stateTransition/stateTransitionTypes'); +const StateTransitionExecutionContext = require('../../../../lib/stateTransition/StateTransitionExecutionContext'); +const PublicKeyIsDisabledConsensusError = require('../../../../lib/errors/consensus/signature/PublicKeyIsDisabledError'); +const WrongPublicKeyPurposeConsensusError = require('../../../../lib/errors/consensus/signature/WrongPublicKeyPurposeError'); +const PublicKeySecurityLevelNotMetConsensusError = require('../../../../lib/errors/consensus/signature/PublicKeySecurityLevelNotMetError'); +const InvalidSignaturePublicKeySecurityLevelConsensusError = require('../../../../lib/errors/consensus/signature/InvalidSignaturePublicKeySecurityLevelError'); +const InvalidIdentityPublicKeyTypeConsensusError = require('../../../../lib/errors/consensus/signature/InvalidIdentityPublicKeyTypeError'); +const InvalidSignaturePublicKeySecurityLevelError = require('../../../../lib/stateTransition/errors/InvalidSignaturePublicKeySecurityLevelError'); +const PublicKeySecurityLevelNotMetError = require('../../../../lib/stateTransition/errors/PublicKeySecurityLevelNotMetError'); +const WrongPublicKeyPurposeError = require('../../../../lib/stateTransition/errors/WrongPublicKeyPurposeError'); +const PublicKeyIsDisabledError = require('../../../../lib/stateTransition/errors/PublicKeyIsDisabledError'); +const DPPError = require('../../../../lib/errors/DPPError'); +const createStateRepositoryMock = require('../../../../lib/test/mocks/createStateRepositoryMock'); +const IdentityNotFoundError = require('../../../../lib/errors/consensus/signature/IdentityNotFoundError'); + +describe('validateStateTransitionIdentitySignatureFactory', () => { + let validateStateTransitionIdentitySignature; + let stateTransition; + let ownerId; + let identity; + let identityPublicKey; + let publicKeyId; + let executionContext; + let stateRepositoryMock; + + beforeEach(function beforeEach() { + executionContext = new StateTransitionExecutionContext(); + + ownerId = generateRandomIdentifier(); + publicKeyId = 1; + stateTransition = { + verifySignature: this.sinonSandbox.stub().returns(true), + getSignaturePublicKeyId: this.sinonSandbox.stub().returns(publicKeyId), + getSignature: this.sinonSandbox.stub(), + getOwnerId: this.sinonSandbox.stub().returns(ownerId), + getType: this.sinonSandbox.stub().returns(stateTransitionTypes.IDENTITY_CREATE), + getExecutionContext: this.sinonSandbox.stub().returns(executionContext), + }; + + identityPublicKey = { + getType: this.sinonSandbox.stub().returns(IdentityPublicKey.TYPES.ECDSA_SECP256K1), + getSecurityLevel: this.sinonSandbox.stub(), + getId: this.sinonSandbox.stub().returns(publicKeyId), + }; + + const getPublicKeyById = this.sinonSandbox.stub().returns(identityPublicKey); + + identity = { + getPublicKeyById, + getId: this.sinonSandbox.stub().returns(ownerId), + }; + + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchIdentity.resolves(identity); + + validateStateTransitionIdentitySignature = validateStateTransitionSignatureFactory( + stateRepositoryMock, + ); + }); + + it('should pass properly signed state transition', async () => { + const result = await validateStateTransitionIdentitySignature( + stateTransition, + ); + + expect(result).to.be.instanceOf(ValidationResult); + + expect(result.isValid()).to.be.true(); + expect(result.getErrors()).to.be.an('array'); + expect(result.getErrors()).to.be.empty(); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + ownerId, + new StateTransitionExecutionContext(), + ); + expect(identity.getPublicKeyById).to.be.calledOnceWithExactly(publicKeyId); + expect(identityPublicKey.getType).to.be.calledTwice(); + expect(stateTransition.getSignaturePublicKeyId).to.be.calledOnce(); + expect(stateTransition.verifySignature).to.be.calledOnceWithExactly(identityPublicKey); + expect(stateTransition.getOwnerId).to.be.calledOnceWithExactly(); + }); + + it('should return invalid result if owner id doesn\'t exist', async () => { + stateRepositoryMock.fetchIdentity.resolves(null); + + const result = await validateStateTransitionIdentitySignature( + stateTransition, + ); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.be.an.instanceOf(IdentityNotFoundError); + expect(error.getCode()).to.equal(2000); + expect(Buffer.isBuffer(error.getIdentityId())).to.be.true(); + expect(error.getIdentityId()).to.deep.equal(identity.getId().toBuffer()); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + ownerId, + new StateTransitionExecutionContext(), + ); + expect(identity.getPublicKeyById).to.not.be.called(); + expect(identityPublicKey.getType).to.not.be.called(); + expect(stateTransition.getSignaturePublicKeyId).to.not.be.called(); + expect(stateTransition.verifySignature).to.not.be.called(); + expect(stateTransition.getOwnerId).to.be.calledOnceWithExactly(); + }); + + it("should return MissingPublicKeyError if the identity doesn't have a matching public key", async () => { + const type = IdentityPublicKey.TYPES.ECDSA_SECP256K1 + 1; + identityPublicKey.getType.returns(type); + identity.getPublicKeyById.returns(undefined); + + const result = await validateStateTransitionIdentitySignature( + stateTransition, + ); + + expect(result).to.be.instanceOf(ValidationResult); + expect(result.isValid()).to.be.false(); + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + ownerId, + new StateTransitionExecutionContext(), + ); + expect(identity.getPublicKeyById).to.be.calledOnceWithExactly(publicKeyId); + expect(stateTransition.getSignaturePublicKeyId).to.be.calledTwice(); + expect(stateTransition.verifySignature).to.not.be.called(); + + expect(result.getErrors()).to.be.an('array'); + expect(result.getErrors()).to.have.lengthOf(1); + + const [error] = result.getErrors(); + + expect(error).to.be.instanceOf(MissingPublicKeyError); + expect(error.getPublicKeyId()).to.equal(publicKeyId); + }); + + it('should return InvalidIdentityPublicKeyTypeError if type is not exist', async () => { + const type = Math.max(...Object.values(IdentityPublicKey.TYPES)) + 1; + identityPublicKey.getType.returns(type); + + const result = await validateStateTransitionIdentitySignature( + stateTransition, + ); + + expect(result).to.be.instanceOf(ValidationResult); + expect(result.isValid()).to.be.false(); + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + ownerId, + new StateTransitionExecutionContext(), + ); + expect(identity.getPublicKeyById).to.be.calledOnceWithExactly(publicKeyId); + expect(identityPublicKey.getType).to.be.calledTwice(); + expect(stateTransition.getSignaturePublicKeyId).to.be.calledOnce(); + expect(stateTransition.verifySignature).to.not.be.called(); + + expect(result.getErrors()).to.be.an('array'); + expect(result.getErrors()).to.have.lengthOf(1); + + const [error] = result.getErrors(); + + expect(error).to.be.instanceOf(InvalidIdentityPublicKeyTypeConsensusError); + expect(error.getPublicKeyType()).to.equal(type); + }); + + it('should return InvalidStateTransitionSignatureError if signature is invalid', async () => { + stateTransition.verifySignature.resolves(false); + + const result = await validateStateTransitionIdentitySignature( + stateTransition, + ); + + expect(result).to.be.instanceOf(ValidationResult); + + expect(result.isValid()).to.be.false(); + expect(result.getErrors()).to.be.an('array'); + expect(result.getErrors()).to.have.lengthOf(1); + + const [error] = result.getErrors(); + + expect(error).to.be.instanceOf(InvalidStateTransitionSignatureError); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWithExactly( + ownerId, + new StateTransitionExecutionContext(), + ); + expect(identity.getPublicKeyById).to.be.calledOnceWithExactly(publicKeyId); + expect(identityPublicKey.getType).to.be.calledTwice(); + expect(stateTransition.getSignaturePublicKeyId).to.be.calledOnce(); + expect(stateTransition.verifySignature).to.be.calledOnceWithExactly(identityPublicKey); + }); + + describe('Consensus errors', () => { + it('should return InvalidSignaturePublicKeySecurityLevelConsensusError if InvalidSignaturePublicKeySecurityLevelError was thrown', async () => { + const e = new InvalidSignaturePublicKeySecurityLevelError(1, 0); + + stateTransition.verifySignature.throws(e); + + const result = await validateStateTransitionIdentitySignature( + stateTransition, + ); + + expect(result).to.be.instanceOf(ValidationResult); + + expect(result.isValid()).to.be.false(); + expect(result.getErrors()).to.be.an('array'); + expect(result.getErrors()).to.have.lengthOf(1); + + const [error] = result.getErrors(); + expect(error).to.be.instanceOf(InvalidSignaturePublicKeySecurityLevelConsensusError); + expect(error.getPublicKeySecurityLevel()).to.equal(1); + expect(error.getKeySecurityLevelRequirement()).to.equal(0); + }); + + it('should return PublicKeySecurityLevelNotMetConsensusError if PublicKeySecurityLevelNotMetError was thrown', async () => { + const e = new PublicKeySecurityLevelNotMetError(1, 2); + + stateTransition.verifySignature.throws(e); + + const result = await validateStateTransitionIdentitySignature( + stateTransition, + ); + + expect(result).to.be.instanceOf(ValidationResult); + + expect(result.isValid()).to.be.false(); + expect(result.getErrors()).to.be.an('array'); + expect(result.getErrors()).to.have.lengthOf(1); + + const [error] = result.getErrors(); + expect(error).to.be.instanceOf(PublicKeySecurityLevelNotMetConsensusError); + expect(error.getPublicKeySecurityLevel()).to.equal(1); + expect(error.getKeySecurityLevelRequirement()).to.equal(2); + }); + + it('should return WrongPublicKeyPurposeConsensusError if WrongPublicKeyPurposeError was thrown', async () => { + const e = new WrongPublicKeyPurposeError(4, 2); + + stateTransition.verifySignature.throws(e); + + const result = await validateStateTransitionIdentitySignature( + stateTransition, + ); + + expect(result).to.be.instanceOf(ValidationResult); + + expect(result.isValid()).to.be.false(); + expect(result.getErrors()).to.be.an('array'); + expect(result.getErrors()).to.have.lengthOf(1); + + const [error] = result.getErrors(); + expect(error).to.be.instanceOf(WrongPublicKeyPurposeConsensusError); + + expect(error.getPublicKeyPurpose()).to.equal(4); + expect(error.getKeyPurposeRequirement()).to.equal(2); + }); + + it('should return PublicKeyIsDisabledConsensusError if PublicKeyIsDisabledError was thrown', async () => { + const e = new PublicKeyIsDisabledError(identityPublicKey); + + stateTransition.verifySignature.throws(e); + + const result = await validateStateTransitionIdentitySignature( + stateTransition, + ); + + expect(result).to.be.instanceOf(ValidationResult); + + expect(result.isValid()).to.be.false(); + expect(result.getErrors()).to.be.an('array'); + expect(result.getErrors()).to.have.lengthOf(1); + + const [error] = result.getErrors(); + expect(error).to.be.instanceOf(PublicKeyIsDisabledConsensusError); + expect(error.getPublicKeyId()).to.deep.equal(publicKeyId); + }); + + it('should return InvalidStateTransitionSignatureError if DPPError was thrown', async () => { + const e = new DPPError('Dpp error'); + + stateTransition.verifySignature.throws(e); + + const result = await validateStateTransitionIdentitySignature( + stateTransition, + ); + + expect(result).to.be.instanceOf(ValidationResult); + + expect(result.isValid()).to.be.false(); + expect(result.getErrors()).to.be.an('array'); + expect(result.getErrors()).to.have.lengthOf(1); + + const [error] = result.getErrors(); + expect(error).to.be.instanceOf(InvalidStateTransitionSignatureError); + }); + + it('should throw unknown error', async () => { + const e = new Error('unknown error'); + + stateTransition.verifySignature.throws(e); + + try { + await validateStateTransitionIdentitySignature( + stateTransition, + ); + + expect.fail('should throw an error'); + } catch (error) { + expect(error).to.equal(e); + } + }); + + it('should not verify signature on dry run', async () => { + const e = new DPPError('Dpp error'); + + stateTransition.verifySignature.throws(e); + + executionContext.enableDryRun(); + + const result = await validateStateTransitionIdentitySignature( + stateTransition, + ); + + executionContext.disableDryRun(); + + expect(result.isValid()).to.be.true(); + expect(result.getErrors()).to.be.an('array'); + expect(result.getErrors()).to.be.empty(); + }); + }); +}); diff --git a/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionKeySignatureFactory.spec.js b/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionKeySignatureFactory.spec.js new file mode 100644 index 00000000000..18c323111e6 --- /dev/null +++ b/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionKeySignatureFactory.spec.js @@ -0,0 +1,86 @@ +const validateStateTransitionKeySignatureFactory = require('../../../../lib/stateTransition/validation/validateStateTransitionKeySignatureFactory'); + +const getIdentityCreateTransitionFixture = require('../../../../lib/test/fixtures/getIdentityCreateTransitionFixture'); +const InvalidStateTransitionSignatureError = require('../../../../lib/errors/consensus/signature/InvalidStateTransitionSignatureError'); + +const { expectValidationError } = require('../../../../lib/test/expect/expectError'); + +const ValidationResult = require('../../../../lib/validation/ValidationResult'); +const StateTransitionExecutionContext = require('../../../../lib/stateTransition/StateTransitionExecutionContext'); + +describe('validateStateTransitionKeySignatureFactory', () => { + let publicKeyHash; + let stateTransition; + let stateTransitionHash; + let verifyHashSignatureMock; + let validateStateTransitionKeySignature; + let fetchAssetLockPublicKeyHashMock; + let executionContext; + + beforeEach(function beforeEach() { + publicKeyHash = Buffer.alloc(20).fill(1); + + stateTransition = getIdentityCreateTransitionFixture(); + stateTransitionHash = stateTransition.hash({ skipSignature: true }); + + executionContext = new StateTransitionExecutionContext(); + + stateTransition.setExecutionContext(executionContext); + + verifyHashSignatureMock = this.sinonSandbox.stub(); + + fetchAssetLockPublicKeyHashMock = this.sinonSandbox.stub().resolves(publicKeyHash); + + validateStateTransitionKeySignature = validateStateTransitionKeySignatureFactory( + verifyHashSignatureMock, + fetchAssetLockPublicKeyHashMock, + ); + }); + + it('should return invalid result if signature is not valid', async () => { + verifyHashSignatureMock.returns(false); + + const result = await validateStateTransitionKeySignature( + stateTransition, + ); + + expectValidationError(result, InvalidStateTransitionSignatureError); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(2002); + + expect(fetchAssetLockPublicKeyHashMock).to.be.calledOnceWithExactly( + stateTransition.getAssetLockProof(), + executionContext, + ); + + expect(verifyHashSignatureMock).to.be.calledOnceWithExactly( + stateTransitionHash, + stateTransition.getSignature(), + publicKeyHash, + ); + }); + + it('should return valid result if signature is valid', async () => { + verifyHashSignatureMock.returns(true); + + const result = await validateStateTransitionKeySignature( + stateTransition, + ); + + expect(result).to.be.instanceof(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(fetchAssetLockPublicKeyHashMock).to.be.calledOnceWithExactly( + stateTransition.getAssetLockProof(), + executionContext, + ); + + expect(verifyHashSignatureMock).to.be.calledOnceWithExactly( + stateTransitionHash, + stateTransition.getSignature(), + publicKeyHash, + ); + }); +}); diff --git a/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionStateFactory.spec.js b/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionStateFactory.spec.js new file mode 100644 index 00000000000..f6c35f52c38 --- /dev/null +++ b/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionStateFactory.spec.js @@ -0,0 +1,89 @@ +const stateTransitionTypes = require('../../../../lib/stateTransition/stateTransitionTypes'); + +const validateStateTransitionStateFactory = require('../../../../lib/stateTransition/validation/validateStateTransitionStateFactory'); + +const { expectValidationError } = require('../../../../lib/test/expect/expectError'); + +const getDataContractFixture = require('../../../../lib/test/fixtures/getDataContractFixture'); + +const ValidationResult = require('../../../../lib/validation/ValidationResult'); + +const DataContractFactory = require('../../../../lib/dataContract/DataContractFactory'); + +const InvalidStateTransitionTypeError = require('../../../../lib/stateTransition/errors/InvalidStateTransitionTypeError'); +const createDPPMock = require('../../../../lib/test/mocks/createDPPMock'); +const SomeConsensusError = require('../../../../lib/test/mocks/SomeConsensusError'); + +describe('validateStateTransitionStateFactory', () => { + let validateDataContractSTDataMock; + let validateStateTransitionState; + let stateTransition; + + beforeEach(function beforeEach() { + validateDataContractSTDataMock = this.sinonSandbox.stub(); + + const dataContractFactory = new DataContractFactory(createDPPMock(), undefined); + + const dataContract = getDataContractFixture(); + stateTransition = dataContractFactory.createDataContractCreateTransition(dataContract); + + validateStateTransitionState = validateStateTransitionStateFactory({ + [stateTransitionTypes.DATA_CONTRACT_CREATE]: validateDataContractSTDataMock, + }); + }); + + it('should return invalid result if State Transition type is invalid', async () => { + const rawStateTransition = {}; + stateTransition = { + getType() { + return 4343; + }, + toObject() { + return rawStateTransition; + }, + }; + + try { + await validateStateTransitionState(stateTransition); + + expect.fail('should throw InvalidStateTransitionTypeError'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidStateTransitionTypeError); + expect(e.getType()).to.equal(stateTransition.getType()); + + expect(validateDataContractSTDataMock).to.not.be.called(); + } + }); + + it('should return invalid result if Data Contract State Transition is not valid', async () => { + const dataContractError = new SomeConsensusError('test'); + const dataContractResult = new ValidationResult([ + dataContractError, + ]); + + validateDataContractSTDataMock.resolves(dataContractResult); + + const result = await validateStateTransitionState(stateTransition); + + expectValidationError(result); + + const [error] = result.getErrors(); + + expect(error).to.equal(dataContractError); + + expect(validateDataContractSTDataMock).to.be.calledOnceWith(stateTransition); + }); + + it('should return valid result', async () => { + const dataContractResult = new ValidationResult(); + + validateDataContractSTDataMock.resolves(dataContractResult); + + const result = await validateStateTransitionState(stateTransition); + + expect(result).to.be.an.instanceOf(ValidationResult); + expect(result.isValid()).to.be.true(); + + expect(validateDataContractSTDataMock).to.be.calledOnceWith(stateTransition); + }); +}); diff --git a/packages/js-dpp/test/unit/version/validateProtocolVersionFactory.spec.js b/packages/js-dpp/test/unit/version/validateProtocolVersionFactory.spec.js new file mode 100644 index 00000000000..85640b27f39 --- /dev/null +++ b/packages/js-dpp/test/unit/version/validateProtocolVersionFactory.spec.js @@ -0,0 +1,80 @@ +const UnsupportedProtocolVersionError = require('../../../lib/errors/consensus/basic/UnsupportedProtocolVersionError'); +const CompatibleProtocolVersionIsNotDefinedError = require('../../../lib/errors/CompatibleProtocolVersionIsNotDefinedError'); +const IncompatibleProtocolVersionError = require('../../../lib/errors/consensus/basic/IncompatibleProtocolVersionError'); + +const createDPPMock = require('../../../lib/test/mocks/createDPPMock'); +const validateProtocolVersionFactory = require('../../../lib/version/validateProtocolVersionFactory'); + +const { expectValidationError } = require('../../../lib/test/expect/expectError'); +const { latestVersion } = require('../../../lib/version/protocolVersion'); + +describe('validateProtocolVersionFactory', () => { + let validateProtocolVersion; + let dppMock; + let versionCompatibilityMap; + let currentProtocolVersion; + let protocolVersion; + + beforeEach(function beforeEach() { + protocolVersion = 1; + currentProtocolVersion = 1; + + dppMock = createDPPMock(this.sinonSandbox); + dppMock.getProtocolVersion.returns(currentProtocolVersion); + + versionCompatibilityMap = { + 1: 1, + }; + + validateProtocolVersion = validateProtocolVersionFactory( + dppMock, + versionCompatibilityMap, + ); + }); + + it('should throw UnsupportedProtocolVersionError if protocolVersion is higher than latestVersion', () => { + protocolVersion = latestVersion + 1; + + const result = validateProtocolVersion(protocolVersion); + + expectValidationError(result, UnsupportedProtocolVersionError); + + const error = result.getFirstError(); + + expect(error.getParsedProtocolVersion()).to.equal(protocolVersion); + expect(error.getLatestVersion()).to.equal(latestVersion); + expect(error.getCode()).to.equal(1002); + }); + + it('should throw CompatibleProtocolVersionIsNotDefinedError if compatible version is not' + + ' defined for the current protocol version', () => { + delete versionCompatibilityMap[currentProtocolVersion.toString()]; + + try { + validateProtocolVersion(protocolVersion); + + expect.fail('should throw CompatibleProtocolVersionIsNotDefinedError'); + } catch (e) { + expect(e).to.be.an.instanceOf(CompatibleProtocolVersionIsNotDefinedError); + } + }); + + it('should throw IncompatibleProtocolVersionError if parsed version is lower than compatible one', () => { + const minimalProtocolVersion = 1; + + protocolVersion = 0; + currentProtocolVersion = 5; + + versionCompatibilityMap[currentProtocolVersion.toString()] = minimalProtocolVersion; + + const result = validateProtocolVersion(protocolVersion); + + expectValidationError(result, IncompatibleProtocolVersionError); + + const error = result.getFirstError(); + + expect(error.getParsedProtocolVersion()).to.equal(protocolVersion); + expect(error.getMinimalProtocolVersion()).to.equal(minimalProtocolVersion); + expect(error.getCode()).to.equal(1003); + }); +}); diff --git a/packages/js-dpp/webpack.config.js b/packages/js-dpp/webpack.config.js new file mode 100644 index 00000000000..2d8373b22c6 --- /dev/null +++ b/packages/js-dpp/webpack.config.js @@ -0,0 +1,49 @@ +const path = require('path'); +const webpack = require('webpack'); + +const commonJSConfig = { + entry: ['core-js/stable', './lib/DashPlatformProtocol.js'], + mode: 'production', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'DashPlatformProtocol.min.js', + library: 'DashPlatformProtocol', + libraryTarget: 'umd', + }, + resolve: { + fallback: { + fs: false, + ws: false, + crypto: require.resolve('crypto-browserify'), + http: require.resolve('stream-http'), + https: require.resolve('https-browserify'), + stream: require.resolve('stream-browserify'), + path: require.resolve('path-browserify'), + url: require.resolve('url/'), + util: require.resolve('util/'), + buffer: require.resolve('buffer/'), + events: require.resolve('events/'), + assert: require.resolve('assert/'), + string_decoder: require.resolve('string_decoder/'), + }, + }, + plugins: [ + new webpack.ProvidePlugin({ + Buffer: [require.resolve('buffer/'), 'Buffer'], + process: require.resolve('process/browser'), + }), + ], + module: { + rules: [ + { + test: /\.js$/, + exclude: /(node_modules)/, + use: { + loader: 'babel-loader', + }, + }, + ], + }, +}; + +module.exports = [commonJSConfig]; diff --git a/packages/js-drive/.env.example b/packages/js-drive/.env.example new file mode 100644 index 00000000000..45ba854e880 --- /dev/null +++ b/packages/js-drive/.env.example @@ -0,0 +1,65 @@ +# ABCI host and port to listen +ABCI_HOST=0.0.0.0 +ABCI_PORT=26658 + +DB_PATH=./db + +# Common store MerkDB file +GROVEDB_LATEST_FILE=${DB_PATH}/latest_state + +# Cache size for Data Contracts +DATA_CONTRACT_CACHE_SIZE=500 + +# DashCore JSON-RPC host, port and credentials +# Read more: https://dashcore.readme.io/docs/core-api-ref-remote-procedure-calls +CORE_JSON_RPC_HOST=127.0.0.1 +CORE_JSON_RPC_PORT=9998 +CORE_JSON_RPC_USERNAME=dashrpc +CORE_JSON_RPC_PASSWORD=password + +# DashCore ZMQ host and port +CORE_ZMQ_HOST=127.0.0.1 +CORE_ZMQ_PORT=29998 +CORE_ZMQ_CONNECTION_RETRIES=16 + +NETWORK=testnet + +INITIAL_CORE_CHAINLOCKED_HEIGHT= + +# https://github.com/dashevo/dashcore-lib/blob/286c33a9d29d33f05d874c47a9b33764a0be0cf1/lib/constants/index.js#L42-L57 +VALIDATOR_SET_LLMQ_TYPE=100 + +# DPNS Contract + +DPNS_MASTER_PUBLIC_KEY= +DPNS_SECOND_PUBLIC_KEY= + +# Dashpay Contract + +DASHPAY_MASTER_PUBLIC_KEY= +DASHPAY_SECOND_PUBLIC_KEY= + +# Feature flags contract + +FEATURE_FLAGS_MASTER_PUBLIC_KEY= +FEATURE_FLAGS_SECOND_PUBLIC_KEY= + +# Masternode reward shares contract + +MASTERNODE_REWARD_SHARES_MASTER_PUBLIC_KEY= +MASTERNODE_REWARD_SHARES_SECOND_PUBLIC_KEY= + +# logging +LOG_STDOUT_LEVEL=info + +LOG_PRETTY_FILE_LEVEL=silent +LOG_PRETTY_FILE_PATH=/tmp/drive-pretty.log + +LOG_JSON_FILE_LEVEL=silent +LOG_JSON_FILE_PATH=/tmp/drive-json.log + +LOG_STATE_REPOSITORY=false + +NODE_ENV=production + +TENDERDASH_P2P_PORT=26656 diff --git a/packages/js-drive/.eslintrc b/packages/js-drive/.eslintrc new file mode 100644 index 00000000000..53708855109 --- /dev/null +++ b/packages/js-drive/.eslintrc @@ -0,0 +1,36 @@ +{ + "extends": "airbnb-base", + "env": { + "es2021": true, + "node": true + }, + "parser": "babel-eslint", + "rules": { + "no-plusplus": 0, + "eol-last": [ + "error", + "always" + ], + "no-continue": "off", + "class-methods-use-this": "off", + "no-await-in-loop": "off", + "no-restricted-syntax": [ + "error", + { + "selector": "LabeledStatement", + "message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand." + }, + { + "selector": "WithStatement", + "message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize." + } + ], + "curly": [ + "error", + "all" + ] + }, + "globals": { + "BigInt": true + } +} diff --git a/packages/js-drive/.mocharc.yml b/packages/js-drive/.mocharc.yml new file mode 100644 index 00000000000..4b2c6eac541 --- /dev/null +++ b/packages/js-drive/.mocharc.yml @@ -0,0 +1,4 @@ +exit: true +timeout: 3000 +file: + - ./lib/test/bootstrap.js diff --git a/packages/js-drive/CHANGELOG.md b/packages/js-drive/CHANGELOG.md new file mode 100644 index 00000000000..e0e0e75d9aa --- /dev/null +++ b/packages/js-drive/CHANGELOG.md @@ -0,0 +1,509 @@ +## [0.21.1](https://github.com/dashevo/js-drive/compare/v0.21.0...v0.21.1) (2021-10-28) + + +### Bug Fixes + +* getFeatureFlagForHeight must not try to fetch feature flags before they were created ([#575](https://github.com/dashevo/js-drive/issues/575)) + + + +# [0.21.0](https://github.com/dashevo/js-drive/compare/v0.20.0...v0.21.0) (2021-10-14) + + +### Features + +* support higher protocol version ([#571](https://github.com/dashevo/js-drive/issues/571)) +* set protocol version on `begin block` ([#558](https://github.com/dashevo/js-drive/issues/558)) +* comprehensive error codes ([#564](https://github.com/dashevo/js-drive/issues/564), [#572](https://github.com/dashevo/js-drive/issues/572)) +* multiproof for the identity non inclusion proof root tree ([#560](https://github.com/dashevo/js-drive/issues/560)) + + +### Bug Fixes + +* consensus logger wasn't set on error ([#567](https://github.com/dashevo/js-drive/issues/567)) +* previousRootTree not rebuilt on commit, resulting in a wrong proof ([#563](https://github.com/dashevo/js-drive/issues/563)) + + + +# [0.20.0](https://github.com/dashevo/js-drive/compare/v0.19.3...v0.20.0) (2021-07-22) + + +### Features + +* use latest version of Merk storage ([#546](https://github.com/dashevo/js-drive/issues/546)) +* remove chainlock SML verification in favor of more robust core verification ([#536](https://github.com/dashevo/js-drive/issues/536)) +* remove SML instant lock verification in favor of more robust core verification [#533](https://github.com/dashevo/js-drive/issues/533)) +* make compatible with Tenderdash v0.5 ([#527](https://github.com/dashevo/js-drive/issues/527)) +* add additional info to proofs ([#518](https://github.com/dashevo/js-drive/issues/518), [#523](https://github.com/dashevo/js-drive/issues/523), [#525](https://github.com/dashevo/js-drive/issues/525), [#540](https://github.com/dashevo/js-drive/issues/540), [#542](https://github.com/dashevo/js-drive/issues/542)) +* validator set rotation ([#446](https://github.com/dashevo/js-drive/issues/446), [#515](https://github.com/dashevo/js-drive/issues/515), [#517](https://github.com/dashevo/js-drive/issues/517), [#530](https://github.com/dashevo/js-drive/issues/530), [#531](https://github.com/dashevo/js-drive/issues/531), ) + + +### Bug Fixes + +* invalid instant lock if no blocks are produced ([#513](https://github.com/dashevo/js-drive/issues/513)) +* `getProofs` method was missing from `PublicKeyToIdentityIdStoreRootTreeLeaf` class ([#544](https://github.com/dashevo/js-drive/issues/544)) +* typo in trace output ([#516](https://github.com/dashevo/js-drive/issues/516)) + + +### BREAKING CHANGES + +* `document`, `dataContract`, `identity`, `identityIdsByPublicKeyHashes` and `identitiesByPublicKeyHashes` query handlers now returns Protobuf messages instead of cbor'ed data. data is not sent if a proof is requested +* `VALIDATOR_SET_LLMQ_TYPE` env is required +* due to changes in hashing algorithm `appHash` is no longer same and not reproducible for old blocks hence new nodes would not be able to sync +* removing SML IS lock verification make some previously invalid transactions valid +* not compatible with Tenderdash v0.4 +* new ABCI messages and types not compatible with previous ones + + + +## [0.19.3](https://github.com/dashevo/js-drive/compare/v0.19.2...v0.19.3) (2021-06-04) + + +### Bug Fixes + +* documents were deleted using wrong id ([#514](https://github.com/dashevo/js-drive/issues/514)) + + + +## [0.19.2](https://github.com/dashevo/js-drive/compare/v0.19.1...v0.19.2) (2021-05-25) + + +### Bug Fixes + +* InvalidQuery error due to feature flags ([#512](https://github.com/dashevo/js-drive/issues/512)) + + + +## [0.19.1](https://github.com/dashevo/js-drive/compare/v0.19.0...v0.19.1) (2021-05-13) + + +### Bug Fixes + +* feature flags contract height variable has had an invalid name ([#509](https://github.com/dashevo/js-drive/issues/509)) + + + +# [0.19.0](https://github.com/dashevo/js-drive/compare/v0.18.1...v0.19.0) (2021-05-05) + + +### Features + +* use Dash Core to verify chain locks ([#503](https://github.com/dashevo/js-drive/issues/503), [#505](https://github.com/dashevo/js-drive/issues/505), [#506](https://github.com/dashevo/js-drive/issues/506)) +* verify instant locks using Dash Core ([#499](https://github.com/dashevo/js-drive/issues/499), [#501](https://github.com/dashevo/js-drive/issues/501), [#492](https://github.com/dashevo/js-drive/issues/492), [#498](https://github.com/dashevo/js-drive/issues/498)) +* feature flags ([#491](https://github.com/dashevo/js-drive/issues/491), [#504](https://github.com/dashevo/js-drive/issues/504), [#485](https://github.com/dashevo/js-drive/issues/485)) +* output Core network on start ([#490](https://github.com/dashevo/js-drive/issues/490)) +* update js-dp-services-ctl to 0.19-dev ([#486](https://github.com/dashevo/js-drive/issues/486)) +* enable docker build npm cache ([#478](https://github.com/dashevo/js-drive/issues/478)) +* do not setup node if SKIP_TEST_SUITE option is set ([#480](https://github.com/dashevo/js-drive/issues/480)) +* remove regtest fallbacks ([#477](https://github.com/dashevo/js-drive/issues/477)) +* add `verifyInstantLock` in favor of `getSMLStore` method ([#474](https://github.com/dashevo/js-drive/issues/474)) + + +### Bug Fixes + +* error loading shared library libzmq.so.5 ([#483](https://github.com/dashevo/js-drive/issues/483)) +* blockExecutionContext header might be null ([#481](https://github.com/dashevo/js-drive/issues/481)) + + +### BREAKING CHANGES + +* running in standalone regtest mode is not supported anymore +* `fetchSMLStore` method has been removed +* See [DPP v0.19 breaking changes](https://github.com/dashevo/js-dpp/releases/tag/v0.19.0) + + +# [0.18.1](https://github.com/dashevo/js-drive/compare/v0.18.0...v0.18.1) (2021-03-08) + + +### Documentation + +* polish changelog ([ac434e](https://github.com/dashevo/dapi/commit/ac434eea9e1588077445ac13a7f4c066a710a3ec)) + + + +# [0.18.0](https://github.com/dashevo/js-drive/compare/v0.17.14...v0.18.0) (2021-03-03) + + +### Bug Fixes + +* ABCI request length error still not parsing properly ([#476](https://github.com/dashevo/js-drive/issues/476)) + + +### Features + +* output ABCI connection error message ([ff3660a](https://github.com/dashevo/js-drive/commit/ff3660a638f0f4170ff3f7242f84c256e75fa4c6)) +* getProofs ABCI query endpoint ([#451](https://github.com/dashevo/js-drive/issues/451), [#462](https://github.com/dashevo/js-drive/issues/462)) + + + +## [0.17.14](https://github.com/dashevo/js-drive/compare/v0.18.0-dev.5...v0.17.14) (2021-02-20) + + +### Bug Fixes + +* can't parse ABCI request length error ([f55fac7](https://github.com/dashevo/js-drive/commit/f55fac7aced9334cf26e930cfaf23258cec66a9d)) + + + +## [0.17.13](https://github.com/dashevo/js-drive/compare/v0.17.12...v0.17.13) (2021-02-19) + + +### Features + +* reimplemented ABCI server for better reliability ([#475](https://github.com/dashevo/js-drive/issues/475)) + + + +## [0.17.12](https://github.com/dashevo/js-drive/compare/v0.17.11...v0.17.12) (2021-02-16) + + +### Features + +* better handle abci connection errors ([f4348e9](https://github.com/dashevo/js-drive/commit/f4348e944825dc9b554eec8dcf7752e972081b2a)) + + + +## [0.17.11](https://github.com/dashevo/js-drive/compare/v0.17.8...v0.17.11) (2021-02-16) + + +### Bug Fixes + +* stack overflow due to write on write error ([cb3e0ac](https://github.com/dashevo/js-drive/commit/cb3e0ac4212d95372c2b402496125afdf5e69cea)) + + + +## [0.17.10](https://github.com/dashevo/js-drive/compare/v0.17.8...v0.17.10) (2021-02-16) + + +### Bug Fixes + +* abci connection error writes to closed stream ([41a891a](https://github.com/dashevo/js-drive/commit/41a891a922bf2f924c543410dd6d19b3a3ba03d0)) + + + +## [0.17.9](https://github.com/dashevo/js-drive/compare/v0.17.8...v0.17.9) (2021-02-15) + + +### Features + +* robust error handling ([#473](https://github.com/dashevo/js-drive/issues/473)) +* use a different handler for ABCI connection error ([#465](https://github.com/dashevo/js-drive/issues/465), [b9d452a](https://github.com/dashevo/js-drive/commit/b9d452a20bdf75699fa532eb69af7500fc985045)) + + + +## [0.17.8](https://github.com/dashevo/js-drive/compare/v0.17.7...v0.17.8) (2021-02-11) + + +### Bug Fixes + +* could not resolve `previousBlockExecutionStoreTransactions` on query ([#470](https://github.com/dashevo/js-drive/issues/470)) + + +### Features + +* add `driveVersion` to every log output ([#469](https://github.com/dashevo/js-drive/issues/469)) +* await Node logger stream to be ended ([#471](https://github.com/dashevo/js-drive/issues/471)) +* distinguishing log data ([#472](https://github.com/dashevo/js-drive/issues/472)) + + + +## [0.17.7](https://github.com/dashevo/js-drive/compare/v0.17.6...v0.17.7) (2021-02-04) + + +### Features + +* disable state repository and merk logging by default ([#467](https://github.com/dashevo/js-drive/issues/467)) + + + +## [0.17.6](https://github.com/dashevo/js-drive/compare/v0.17.5...v0.17.6) (2021-01-26) + + +### Bug Fixes + +* only info log level is present in log streams ([#463](https://github.com/dashevo/js-drive/issues/463)) + + + +## [0.17.5](https://github.com/dashevo/js-drive/compare/v0.17.4...v0.17.5) (2021-01-21) + + +### Features + +* different logging levels ([#461](https://github.com/dashevo/js-drive/issues/461)) + + +### BREAKING CHANGES + +* `LOGGING_LEVEL` is ignored. Use `LOG_STDOUT_LEVEL`. + + + +## [0.17.4](https://github.com/dashevo/js-drive/compare/v0.17.3...v0.17.4) (2021-01-20) + + +### Bug Fixes + +* logger with context is not used in some cases ([#458](https://github.com/dashevo/js-drive/issues/458)) +* tx counters and logger were not reset ([#460](https://github.com/dashevo/js-drive/issues/460)) + + +### Features + +* log to human-readable and json files ([#459](https://github.com/dashevo/js-drive/issues/459)) + + + +## [0.17.3](https://github.com/dashevo/js-drive/compare/v0.17.2...v0.17.3) (2021-01-20) + + +### Features + +* better logging ([#456](https://github.com/dashevo/js-drive/issues/456)) + + + +## [0.17.2](https://github.com/dashevo/js-drive/compare/v0.17.1...v0.17.2) (2021-01-19) + + +### Bug Fixes + +* could not resolve 'previousBlockExecutionStoreTransactions' ([5a9dbff](https://github.com/dashevo/js-drive/commit/5a9dbffb05cfb85e6e394ed79538d979eb4a73a7)) +* ST isolation leads to non-deterministic results ([#455](https://github.com/dashevo/js-drive/issues/455)) +* handle rawChainLockMessage parsing errors ([#454](https://github.com/dashevo/js-drive/issues/454)) + + + +## [0.17.1](https://github.com/dashevo/js-drive/compare/v0.17.0...v0.17.1) (2021-01-12) + + +### Bug Fixes + +* duplicate MongoDB index name ([#453](https://github.com/dashevo/js-drive/issues/453)) + + + +# [0.17.0](https://github.com/dashevo/js-drive/compare/v0.16.1...v0.17.0) (2020-12-30) + + +### Features + +* introduce `DriveStateRepository#fetchSMLStore` ([#444](https://github.com/dashevo/js-drive/issues/444), [#445](https://github.com/dashevo/js-drive/issues/445)) +* update `dashcore-lib` ([#411](https://github.com/dashevo/js-drive/issues/411), [#442](https://github.com/dashevo/js-drive/issues/442), [#443](https://github.com/dashevo/js-drive/issues/443)) +* add old zmq client from DAPI ([#439](https://github.com/dashevo/js-drive/issues/439)) +* dashpay contract support ([#441](https://github.com/dashevo/js-drive/issues/441)) +* change merk to @dashevo/merk +* gracefull shutdown on SIGINT, SIGTERM, SIGQUIT and unhandled errors ([#427](https://github.com/dashevo/js-drive/issues/427)) +* handle core chain locked height ([#428](https://github.com/dashevo/js-drive/issues/428)) +* implement verify chainlock query handler ([#402](https://github.com/dashevo/js-drive/issues/402)) +* intermediate merk tree for the current block ([#429](https://github.com/dashevo/js-drive/issues/429)) +* pass latestCoreChainLock on block end ([#434](https://github.com/dashevo/js-drive/issues/434)) +* provide proofs for getIdentitiesByPublicKeyHashes endpoint ([#422](https://github.com/dashevo/js-drive/issues/422)) +* provide proofs for getIdentitiyIdsByPublicKeyHashes endpoint ([#419](https://github.com/dashevo/js-drive/issues/419)) +* provide proofs in ABCI query and DAPI getIdentity ([#415](https://github.com/dashevo/js-drive/issues/415)) +* set IDENTITY_SKIP_ASSET_LOCK_CONFIRMATION_VALIDATION to false ([#437](https://github.com/dashevo/js-drive/issues/437)) +* sort keys for MerkDB ([#413](https://github.com/dashevo/js-drive/issues/413)) +* store ChainInfo in MerkDb ([#404](https://github.com/dashevo/js-drive/issues/404)) +* store Data Contracts in merk tree ([#405](https://github.com/dashevo/js-drive/issues/405)) +* store documents in MerkDb ([#410](https://github.com/dashevo/js-drive/issues/410)) +* store height in externalStorage instead of merkDB ([#433](https://github.com/dashevo/js-drive/issues/433)) +* store identities in merk tree ([#400](https://github.com/dashevo/js-drive/issues/400)) +* store Public Key to Identity ID in MerkDb ([#409](https://github.com/dashevo/js-drive/issues/409)) +* update `dpp` to include asset lock verification logic ([#432](https://github.com/dashevo/js-drive/issues/432)) +* introduce merkle forest ([#401](https://github.com/dashevo/js-drive/issues/401)) +* move block execution context out of blockchain state ([#403](https://github.com/dashevo/js-drive/issues/403)) +* add abstraction for MerkDb ([#407](https://github.com/dashevo/js-drive/issues/407)) + + +### Bug Fixes + +* hash was used as a Buffer where it should be hex string ([#440](https://github.com/dashevo/js-drive/issues/440)) +* documents DB transaction is already started error ([#417](https://github.com/dashevo/js-drive/issues/417)) +* e.getErrors is not a function error ([#418](https://github.com/dashevo/js-drive/issues/418)) +* missing nested indexed fields and transaction ([#426](https://github.com/dashevo/js-drive/issues/426)) + + +### BREAKING CHANGES + +* AppHash is not equal to nils anymore. +* data created with 0.16 and lower versions of Drive is not compatible anymore +* ABCI query responses are changed + + + +## [0.16.1](https://github.com/dashevo/js-drive/compare/v0.16.0...v0.16.1) (2020-10-29) + + +### Bug Fixes + +* `header` is not present in `RequestEndBlock` ([#399](https://github.com/dashevo/js-drive/issues/399)) + + + +# [0.16.0](https://github.com/dashevo/js-drive/compare/v0.15.0...v0.16.0) (2020-10-28) + + +### Bug Fixes + +* incorrect deliver state transition hash logging ([#396](https://github.com/dashevo/js-drive/issues/396)) + + +### Features + +* verify DPNS contract existence ([#397](https://github.com/dashevo/js-drive/issues/397)) +* add `LoggedStateRepositoryDecorator` ([#393](https://github.com/dashevo/js-drive/issues/393)) +* debug mode to respond internal error with message and stack ([#383](https://github.com/dashevo/js-drive/issues/383)) +* implement `fetchIdentityIdsByPublicKeys` method ([#385](https://github.com/dashevo/js-drive/issues/385)) +* implement `storeIdentityPublicKeyHashes` method ([#387](https://github.com/dashevo/js-drive/issues/387)) +* implement getting identities by multiple public keys hashes ([#388](https://github.com/dashevo/js-drive/issues/388), [#395](https://github.com/dashevo/js-drive/issues/395), [#386](https://github.com/dashevo/js-drive/issues/386)) +* update DPP to 0.16.0 ([#392](https://github.com/dashevo/js-drive/issues/392)) + + +### Refactoring + +* remove unnecessary InvalidDocumentTypeError handling ([#384](https://github.com/dashevo/js-drive/issues/384)) + + +### BREAKING CHANGES + +* If `DPNS_CONTRACT_ID` is set it requires `DPNS_CONTRACT_BLOCK_HEIGHT` to be set too. +* See [DPP v0.16 breaking changes](https://github.com/dashevo/js-dpp/releases/tag/v0.16.0) + + + +# [0.15.0](https://github.com/dashevo/js-drive/compare/v0.14.0...v0.15.0) (2020-09-04) + + +### Bug Fixes + +* internal errors are not logged ([#380](https://github.com/dashevo/js-drive/issues/380)) +* unique index throws duplicate key error (#378) + + +### Features + +* handle protocol and software versions ([#377](https://github.com/dashevo/js-drive/issues/377)) +* handle user-defined binary fields ([#373](https://github.com/dashevo/js-drive/issues/373), [#381](https://github.com/dashevo/js-drive/issues/381)) + + +### BREAKING CHANGES + +* protocol version (`AppVersion`) is required in a Tendermint block header +* the previous state is not compatible due to new DPP serialization format +* See [DPP breaking changes](https://github.com/dashevo/js-dpp/releases/tag/v0.15.0) + + + +# [0.14.0](https://github.com/dashevo/drive/compare/v0.13.2...v0.14.0) (2020-07-23) + + +### Features + +* increase MongoDB query allowed field length ([#366](https://github.com/dashevo/drive/issues/366)) +* logging of block execution process ([#365](https://github.com/dashevo/drive/issues/365)) +* use test suite to run functional and e2e tests ([#362](https://github.com/dashevo/drive/issues/362)) +* update to DPP v0.14 with timestamps ([#363](https://github.com/dashevo/drive/issues/363)) + + +### BREAKING CHANGES + +* See [DPP v0.14 breaking changes](https://github.com/dashevo/js-dpp/releases/tag/v0.14.0) + + + +## [0.13.2](https://github.com/dashevo/drive/compare/v0.13.0-dev.2...v0.13.2) (2020-06-12) + + +### Bug Fixes + +* internal errors lead to inability to fix bugs as it leads to a state inconsistency ([#360](https://github.com/dashevo/drive/issues/360)) + + + +## [0.13.1](https://github.com/dashevo/drive/compare/v0.13.0...v0.13.1) (2020-06-12) + + +### Bug Fixes + +* document repository not created properly due to missing `await` ([#358](https://github.com/dashevo/drive/issues/358)) + + + +# [0.13.0](https://github.com/dashevo/drive/compare/v0.12.1...v0.13.0) (2020-06-08) + + +### Features + +* update to DPP 0.13 ([#336](https://github.com/dashevo/drive/issues/336), [#338](https://github.com/dashevo/drive/issues/338), [#340](https://github.com/dashevo/drive/issues/340), [#344](https://github.com/dashevo/drive/issues/344), [#346](https://github.com/dashevo/drive/issues/346), [#348](https://github.com/dashevo/drive/issues/348), [#354](https://github.com/dashevo/drive/issues/354), [#357](https://github.com/dashevo/drive/issues/357)) +* wait mongoDB replica set initialization ([#349](https://github.com/dashevo/drive/issues/349)) +* wait for Core to be synced before starting ([#345](https://github.com/dashevo/drive/issues/345), [#353](https://github.com/dashevo/drive/issues/353), [#356](https://github.com/dashevo/drive/issues/356)) +* get identity by public key endpoints ([#341](https://github.com/dashevo/drive/issues/341)) +* store identity id with identity's public key as a DB key ([#337](https://github.com/dashevo/drive/issues/337), [#339](https://github.com/dashevo/drive/issues/339)) + + +### Code Refactoring + +* use async function with cache to connect and get `MongoClient` ([#350](https://github.com/dashevo/drive/issues/350)) + + +### BREAKING CHANGES + +* see [DPP breaking changes](https://github.com/dashevo/js-dpp/releases/tag/v0.13.0) + + + +## [0.12.2](https://github.com/dashevo/drive/compare/v0.12.1...v0.12.2) (2020-05-21) + + +### Bug Fixes + +* validateFee error handling expects only BalanceIsNotEnoughError ([#343](https://github.com/dashevo/drive/issues/343)) + + + +## [0.12.1](https://github.com/dashevo/drive/compare/v0.12.0...v0.12.1) (2020-04-22) + + +### Features + +* update `dpp` version to `0.12.1` ([#335](https://github.com/dashevo/drive/issues/335)) + + +# [0.12.0](https://github.com/dashevo/drive/compare/v0.11.1...v0.12.0) (2020-04-18) + +### Features + +* publish docker image with tag for every Semver segment ([#332](https://github.com/dashevo/drive/issues/332)) +* introduce ABCI and Machine logic, remove API and upgrade to DPP 0.12 ([#328](https://github.com/dashevo/drive/issues/328)) +* validate fee, reduce balance and move fees to distribution pool ([#329](https://github.com/dashevo/drive/issues/329)) + +### BREAKING CHANGES + +* JSON RPC and gRPC endpoints are removed. Use Tendermint ABCI query endpoint in order to fetch data +* see [DPP breaking changes](https://github.com/dashevo/js-dpp/releases/tag/v0.12.0) + + +## [0.11.1](https://github.com/dashevo/drive/compare/v0.11.0...v0.11.1) (2020-03-17) + +### Bug Fixes + +* do not validate ST second time in `applyStateTransition` ([d296608](https://github.com/dashevo/drive/commit/d29660886deb7e5556c5346da54506aebc005bfa)) +* check for MongoDb replica set on start ([286074f](https://github.com/dashevo/drive/commit/286074fe297bb693ffe7492523e560aeb2512330)) + +# [0.11.0](https://github.com/dashevo/drive/compare/v0.7.0...v0.11.0) (2020-03-09) + +### Bug Fixes + +* prevent to update dependencies with major version `0` to minor versions ([9f1dd95](https://github.com/dashevo/drive/commit/9f1dd95fe2294de2d0a3157807eec9598d0f0db7)) + +### Features + +* upgrade DPP to v0.11 ([9797e51](https://github.com/dashevo/drive/commit/9797e51bee6899c07aabcf733fa54650037c42cd)) + +### Chore + +* update gRPC errors ([1d31326](https://github.com/dashevo/drive/commit/1d31326977b2b5f1537426d9d31d89f459aaace6)) + +### BREAKING CHANGES + +* see [DPP breaking changes](https://github.com/dashevo/js-dpp/releases/tag/v0.11.0) diff --git a/packages/js-drive/Dockerfile b/packages/js-drive/Dockerfile new file mode 100644 index 00000000000..60572d69733 --- /dev/null +++ b/packages/js-drive/Dockerfile @@ -0,0 +1,66 @@ +# syntax = docker/dockerfile:1.3 +FROM node:16-alpine as builder + +ARG NODE_ENV=production +ENV NODE_ENV ${NODE_ENV} + +RUN apk update && \ + apk --no-cache upgrade && \ + apk add --no-cache git \ + openssh-client \ + linux-headers \ + python3 \ + alpine-sdk \ + cmake \ + zeromq-dev + +# Enable corepack https://github.com/nodejs/corepack +RUN corepack enable + +WORKDIR /platform + +# Copy yarn files +COPY .yarn /platform/.yarn +COPY package.json yarn.lock .yarnrc.yml .pnp.* ./ + +# Copy only necessary packages from monorepo +COPY packages/js-drive packages/js-drive +COPY packages/dapi-grpc packages/dapi-grpc +COPY packages/feature-flags-contract packages/feature-flags-contract +COPY packages/js-dpp packages/js-dpp +COPY packages/js-grpc-common packages/js-grpc-common +COPY packages/masternode-reward-shares-contract packages/masternode-reward-shares-contract +COPY packages/dpns-contract packages/dpns-contract +COPY packages/dashpay-contract packages/dashpay-contract + +# Print build output +RUN yarn config set enableInlineBuilds true + +# Install Drive-specific dependencies using previous +# node_modules directory to reuse built binaries +RUN --mount=type=cache,target=/tmp/unplugged \ + cp -R /tmp/unplugged /platform/.yarn/ && \ + yarn workspaces focus --production @dashevo/drive && \ + cp -R /platform/.yarn/unplugged /tmp/ + +FROM node:16-alpine + +ARG NODE_ENV=production +ENV NODE_ENV ${NODE_ENV} + +LABEL maintainer="Dash Developers " +LABEL description="Drive Node.JS" + +# Install ZMQ shared library +RUN apk update && apk add --no-cache zeromq-dev + +# Install latest yarn +RUN yarn set version 3.1.0 + +WORKDIR /platform + +COPY --from=builder /platform /platform + +RUN cp /platform/packages/js-drive/.env.example /platform/packages/js-drive/.env + +EXPOSE 26658 diff --git a/packages/js-drive/LICENSE b/packages/js-drive/LICENSE new file mode 100644 index 00000000000..f735c60619c --- /dev/null +++ b/packages/js-drive/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017-2019 Dash Core Group, Inc. + +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/packages/js-drive/README.md b/packages/js-drive/README.md new file mode 100644 index 00000000000..16f196fe9c3 --- /dev/null +++ b/packages/js-drive/README.md @@ -0,0 +1,55 @@ +# Drive + +[![Latest Release](https://img.shields.io/github/v/release/dashevo/platform)](https://github.com/dashevo/platform/releases/latest) +[![Build Status](https://github.com/dashevo/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashevo/platform/actions/workflows/release.yml) +[![Release Date](https://img.shields.io/github/release-date/dashevo/platform)](https://github.com/dashevo/platform/releases/latest) +[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen)](https://github.com/RichardLitt/standard-readme) + +Replicated state machine for Dash Platform + +Drive is the storage component of Dash Platform, allowing developers to store and secure their application data through Dash's masternode network. Application data structures are defined by a data contract, which is stored on Drive and used to verify/validate updates to your application data. + +## Table of Contents +- [Install](#install) +- [Usage](#usage) +- [Configuration](#configuration) +- [Tests](#tests) +- [Maintainer](#maintainer) +- [Contributing](#contributing) +- [License](#license) + +## Install + +1. [Install Node.JS 12 or higher](https://nodejs.org/en/download/) +2. Copy `.env.example` to `.env` file +3. Install npm dependencies: `npm install` + +## Usage + +```bash +npm run abci +``` + +## Configuration + +Drive uses environment variables for configuration. +Variables are read from `.env` file and can be overwritten by variables +defined in env or directly passed to the process. + +See all available settings in [.env.example](.env.example). + +## Tests + +[Read](test/) about tests in `test/` folder. + +## Maintainer + +[@shumkov](https://github.com/shumkov) + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs. + +## License + +[MIT](LICENSE) © Dash Core Group, Inc. diff --git a/packages/js-drive/db/.gitignore b/packages/js-drive/db/.gitignore new file mode 100644 index 00000000000..593bcf0e80e --- /dev/null +++ b/packages/js-drive/db/.gitignore @@ -0,0 +1,2 @@ +!.gitignore +* diff --git a/packages/js-drive/lib/abci/closeAbciServerFactory.js b/packages/js-drive/lib/abci/closeAbciServerFactory.js new file mode 100644 index 00000000000..141cdeb9b3b --- /dev/null +++ b/packages/js-drive/lib/abci/closeAbciServerFactory.js @@ -0,0 +1,25 @@ +const { promisify } = require('util'); + +/** + * + * @param {net.Server} abciServer + * @return {closeAbciServer} + */ +function closeAbciServerFactory(abciServer) { + /** + * @typedef {closeAbciServer} + * @return {Promise} + */ + async function closeAbciServer() { + if (!abciServer.listening) { + return; + } + + const close = promisify(abciServer.close.bind(abciServer)); + await close(); + } + + return closeAbciServer; +} + +module.exports = closeAbciServerFactory; diff --git a/packages/js-drive/lib/abci/errors/AbstractAbciError.js b/packages/js-drive/lib/abci/errors/AbstractAbciError.js new file mode 100644 index 00000000000..ae054bc477d --- /dev/null +++ b/packages/js-drive/lib/abci/errors/AbstractAbciError.js @@ -0,0 +1,68 @@ +const cbor = require('cbor'); + +const DriveError = require('../../errors/DriveError'); + +/** + * @abstract + */ +class AbstractAbciError extends DriveError { + /** + * + * @param {number} code + * @param {string} message + * @param {Object} data + */ + constructor(code, message, data) { + super(message); + + this.code = code; + this.data = data; + } + + /** + * @returns {string} + */ + getMessage() { + return this.message; + } + + /** + * Get error code + * + * @returns {number} + */ + getCode() { + return this.code; + } + + /** + * Get error data + * + * @returns {Object} + */ + getData() { + return this.data; + } + + /** + * @returns {{code: number, info: string}} + */ + getAbciResponse() { + const info = { + message: this.getMessage(), + }; + + const data = this.getData(); + + if (Object.keys(data).length > 0) { + info.data = data; + } + + return { + code: this.getCode(), + info: cbor.encode(info).toString('base64'), + }; + } +} + +module.exports = AbstractAbciError; diff --git a/packages/js-drive/lib/abci/errors/DPPValidationAbciError.js b/packages/js-drive/lib/abci/errors/DPPValidationAbciError.js new file mode 100644 index 00000000000..7fcf434a5c0 --- /dev/null +++ b/packages/js-drive/lib/abci/errors/DPPValidationAbciError.js @@ -0,0 +1,44 @@ +const cbor = require('cbor'); + +const AbstractAbciError = require('./AbstractAbciError'); + +class DPPValidationAbciError extends AbstractAbciError { + /** + * + * @param {string} message + * @param {AbstractConsensusError} consensusError + */ + constructor(message, consensusError) { + const args = consensusError.getConstructorArguments(); + + const data = { }; + if (args.length > 0) { + data.arguments = args; + } + + super(consensusError.getCode(), message, data); + } + + /** + * @returns {{code: number, info: string}} + */ + getAbciResponse() { + const info = { }; + + const data = this.getData(); + + let encodedInfo; + if (Object.keys(data).length > 0) { + info.data = data; + + encodedInfo = cbor.encode(info).toString('base64'); + } + + return { + code: this.getCode(), + info: encodedInfo, + }; + } +} + +module.exports = DPPValidationAbciError; diff --git a/packages/js-drive/lib/abci/errors/InternalAbciError.js b/packages/js-drive/lib/abci/errors/InternalAbciError.js new file mode 100644 index 00000000000..bc5ac65b1c4 --- /dev/null +++ b/packages/js-drive/lib/abci/errors/InternalAbciError.js @@ -0,0 +1,25 @@ +const grpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); + +const AbstractAbciError = require('./AbstractAbciError'); + +class InternalAbciError extends AbstractAbciError { + /** + * + * @param {Error} error + * @param {Object} [data] + */ + constructor(error, data = {}) { + super(grpcErrorCodes.INTERNAL, 'Internal error', data); + + this.error = error; + } + + /** + * @returns {Error} + */ + getError() { + return this.error; + } +} + +module.exports = InternalAbciError; diff --git a/packages/js-drive/lib/abci/errors/InvalidArgumentAbciError.js b/packages/js-drive/lib/abci/errors/InvalidArgumentAbciError.js new file mode 100644 index 00000000000..dc66740da1f --- /dev/null +++ b/packages/js-drive/lib/abci/errors/InvalidArgumentAbciError.js @@ -0,0 +1,16 @@ +const grpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); + +const AbstractAbciError = require('./AbstractAbciError'); + +class InvalidArgumentAbciError extends AbstractAbciError { + /** + * + * @param {string} message + * @param {Object} [data] + */ + constructor(message, data = {}) { + super(grpcErrorCodes.INVALID_ARGUMENT, message, data); + } +} + +module.exports = InvalidArgumentAbciError; diff --git a/packages/js-drive/lib/abci/errors/NotFoundAbciError.js b/packages/js-drive/lib/abci/errors/NotFoundAbciError.js new file mode 100644 index 00000000000..1daeb9325c1 --- /dev/null +++ b/packages/js-drive/lib/abci/errors/NotFoundAbciError.js @@ -0,0 +1,16 @@ +const grpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); + +const AbstractAbciError = require('./AbstractAbciError'); + +class NotFoundAbciError extends AbstractAbciError { + /** + * + * @param {string} message + * @param {Object} [data] + */ + constructor(message, data = {}) { + super(grpcErrorCodes.NOT_FOUND, message, data); + } +} + +module.exports = NotFoundAbciError; diff --git a/packages/js-drive/lib/abci/errors/UnavailableAbciError.js b/packages/js-drive/lib/abci/errors/UnavailableAbciError.js new file mode 100644 index 00000000000..227aff45fe4 --- /dev/null +++ b/packages/js-drive/lib/abci/errors/UnavailableAbciError.js @@ -0,0 +1,16 @@ +const grpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); + +const AbstractAbciError = require('./AbstractAbciError'); + +class UnavailableAbciError extends AbstractAbciError { + /** + * + * @param {string} message + * @param {Object} [data] + */ + constructor(message, data = {}) { + super(grpcErrorCodes.UNAVAILABLE, message, data); + } +} + +module.exports = UnavailableAbciError; diff --git a/packages/js-drive/lib/abci/errors/UnimplementedAbciError.js b/packages/js-drive/lib/abci/errors/UnimplementedAbciError.js new file mode 100644 index 00000000000..81e279f2039 --- /dev/null +++ b/packages/js-drive/lib/abci/errors/UnimplementedAbciError.js @@ -0,0 +1,16 @@ +const grpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); + +const AbstractAbciError = require('./AbstractAbciError'); + +class UnimplementedAbciError extends AbstractAbciError { + /** + * + * @param {string} message + * @param {Object} [data] + */ + constructor(message, data = {}) { + super(grpcErrorCodes.UNIMPLEMENTED, message, data); + } +} + +module.exports = UnimplementedAbciError; diff --git a/packages/js-drive/lib/abci/errors/VerboseInternalAbciError.js b/packages/js-drive/lib/abci/errors/VerboseInternalAbciError.js new file mode 100644 index 00000000000..fe13d71c83c --- /dev/null +++ b/packages/js-drive/lib/abci/errors/VerboseInternalAbciError.js @@ -0,0 +1,30 @@ +const InternalAbciError = require('./InternalAbciError'); + +class VerboseInternalAbciError extends InternalAbciError { + /** + * + * @param {InternalAbciError} error + */ + constructor(error) { + const originalError = error.getError(); + let [, errorPath] = originalError.stack.toString().split(/\r\n|\n/); + + if (!errorPath) { + errorPath = originalError.stack; + } + + const message = `${originalError.message} ${errorPath.trim()}`; + + const data = error.getData() || {}; + data.stack = originalError.stack; + + super( + originalError, + data, + ); + + this.message = message; + } +} + +module.exports = VerboseInternalAbciError; diff --git a/packages/js-drive/lib/abci/errors/enrichErrorWithConsensusLoggerFactory.js b/packages/js-drive/lib/abci/errors/enrichErrorWithConsensusLoggerFactory.js new file mode 100644 index 00000000000..2aedaa0efcb --- /dev/null +++ b/packages/js-drive/lib/abci/errors/enrichErrorWithConsensusLoggerFactory.js @@ -0,0 +1,42 @@ +/** + * Add consensus logger to an error (factory) + * + * @param {blockExecutionContext} blockExecutionContext + * + * @return {enrichErrorWithConsensusLogger} + */ +function enrichErrorWithConsensusLoggerFactory(blockExecutionContext) { + /** + * Add consensus logger to an error + * + * @typedef enrichErrorWithConsensusLogger + * + * @param {Function} method + * + * @return {Function} + */ + function enrichErrorWithConsensusLogger(method) { + /** + * @param {*} request + */ + async function methodHandler(request) { + try { + return await method(request); + } catch (e) { + const { consensusLogger } = blockExecutionContext; + + if (consensusLogger) { + e.consensusLogger = consensusLogger; + } + + throw e; + } + } + + return methodHandler; + } + + return enrichErrorWithConsensusLogger; +} + +module.exports = enrichErrorWithConsensusLoggerFactory; diff --git a/packages/js-drive/lib/abci/errors/wrapInErrorHandlerFactory.js b/packages/js-drive/lib/abci/errors/wrapInErrorHandlerFactory.js new file mode 100644 index 00000000000..248792e7bde --- /dev/null +++ b/packages/js-drive/lib/abci/errors/wrapInErrorHandlerFactory.js @@ -0,0 +1,77 @@ +const AbstractAbciError = require('./AbstractAbciError'); +const InternalAbciError = require('./InternalAbciError'); +const VerboseInternalAbciError = require('./VerboseInternalAbciError'); + +/** + * @param {BaseLogger} logger + * @param {boolean} isProductionEnvironment + * + * @return wrapInErrorHandler + */ +function wrapInErrorHandlerFactory(logger, isProductionEnvironment) { + /** + * Wrap ABCI methods in error handler + * + * @typedef wrapInErrorHandler + * + * @param {Function} method + * @param {Object} [options={}] + * @param {boolean} [options.respondWithInternalError=false] + * + * @return {Function} + */ + function wrapInErrorHandler(method, options = {}) { + // eslint-disable-next-line no-param-reassign + options = { + respondWithInternalError: false, + ...options, + }; + + /** + * @param request + */ + async function methodErrorHandler(request) { + try { + return await method(request); + } catch (e) { + let error = e; + + // Wrap all non ABCI errors to an internal ABCI error + if (!(e instanceof AbstractAbciError)) { + error = new InternalAbciError(e); + } + + // Log only internal ABCI errors + if (error instanceof InternalAbciError) { + // in consensus ABCI handlers (blockBegin, deliverTx, blockEnd, commit) + // we should propagate the error upwards + // to halt the Drive + // in order cases like query and checkTx + // we need to respond with internal errors + if (!options.respondWithInternalError) { + throw error.getError(); + } + + const originalError = error.getError(); + + (originalError.consensusLogger || logger).error( + { err: originalError }, + originalError.message, + ); + + if (!isProductionEnvironment) { + error = new VerboseInternalAbciError(error); + } + } + + return error.getAbciResponse(); + } + } + + return methodErrorHandler; + } + + return wrapInErrorHandler; +} + +module.exports = wrapInErrorHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/beginBlockHandlerFactory.js b/packages/js-drive/lib/abci/handlers/beginBlockHandlerFactory.js new file mode 100644 index 00000000000..b16d4225259 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/beginBlockHandlerFactory.js @@ -0,0 +1,159 @@ +const { + tendermint: { + abci: { + ResponseBeginBlock, + }, + }, +} = require('@dashevo/abci/types'); + +const NotSupportedNetworkProtocolVersionError = require('./errors/NotSupportedNetworkProtocolVersionError'); +const NetworkProtocolVersionIsNotSetError = require('./errors/NetworkProtocolVersionIsNotSetError'); + +/** + * Begin Block ABCI Handler + * + * @param {GroveDBStore} groveDBStore + * @param {BlockExecutionContext} blockExecutionContext + * @param {BlockExecutionContextStack} blockExecutionContextStack + * @param {Long} latestProtocolVersion + * @param {DashPlatformProtocol} dpp + * @param {DashPlatformProtocol} transactionalDpp + * @param {updateSimplifiedMasternodeList} updateSimplifiedMasternodeList + * @param {waitForChainLockedHeight} waitForChainLockedHeight + * @param {synchronizeMasternodeIdentities} synchronizeMasternodeIdentities + * @param {BaseLogger} logger + * @param {ExecutionTimer} executionTimer + * + * @return {beginBlockHandler} + */ +function beginBlockHandlerFactory( + groveDBStore, + blockExecutionContext, + blockExecutionContextStack, + latestProtocolVersion, + dpp, + transactionalDpp, + updateSimplifiedMasternodeList, + waitForChainLockedHeight, + synchronizeMasternodeIdentities, + logger, + executionTimer, +) { + /** + * @typedef beginBlockHandler + * + * @param {abci.RequestBeginBlock} request + * @return {Promise} + */ + async function beginBlockHandler(request) { + const { header, lastCommitInfo } = request; + + const { + coreChainLockedHeight, + height, + version, + } = header; + + // Start block execution timer + executionTimer.clearTimer('blockExecution'); + + executionTimer.startTimer('blockExecution'); + + const consensusLogger = logger.child({ + height: height.toString(), + abciMethod: 'beginBlock', + }); + + consensusLogger.debug('BeginBlock ABCI method requested'); + consensusLogger.trace({ abciRequest: request }); + + // Validate protocol version + + if (version.app.eq(0)) { + throw new NetworkProtocolVersionIsNotSetError(); + } + + if (version.app.gt(latestProtocolVersion)) { + throw new NotSupportedNetworkProtocolVersionError( + version.app, + latestProtocolVersion, + ); + } + + // Make sure Core has the same height as the network + + await waitForChainLockedHeight(coreChainLockedHeight); + + // Set block execution context + + // in case previous block execution failed in process + // and not committed. We need to make sure + // previous context properly reset. + const contextHeader = blockExecutionContext.getHeader(); + if (contextHeader && contextHeader.height.equals(height)) { + // Remove failed block context from the stack + const latestContext = blockExecutionContextStack.getLatest(); + const latestContextHeader = latestContext.getHeader(); + + if (latestContextHeader.height.equals(height)) { + blockExecutionContextStack.removeLatest(); + } + } + + blockExecutionContext.reset(); + + blockExecutionContext.setConsensusLogger(consensusLogger); + + blockExecutionContext.setHeader(header); + + blockExecutionContext.setLastCommitInfo(lastCommitInfo); + + // Set protocol version to DPP + dpp.setProtocolVersion(version.app.toNumber()); + transactionalDpp.setProtocolVersion(version.app.toNumber()); + + if (await groveDBStore.isTransactionStarted()) { + await groveDBStore.abortTransaction(); + } + + // Start db transaction for the block + await groveDBStore.startTransaction(); + + const isSimplifiedMasternodeListUpdated = await updateSimplifiedMasternodeList( + coreChainLockedHeight, { + logger: consensusLogger, + }, + ); + + if (isSimplifiedMasternodeListUpdated) { + const synchronizeMasternodeIdentitiesResult = await synchronizeMasternodeIdentities( + coreChainLockedHeight, + ); + + const { + createdEntities, updatedEntities, removedEntities, fromHeight, toHeight, + } = synchronizeMasternodeIdentitiesResult; + + consensusLogger.info( + `Masternode identities are synced for heights from ${fromHeight} to ${toHeight}: ${createdEntities.length} created, ${updatedEntities.length} updated, ${removedEntities.length} removed`, + ); + + consensusLogger.trace( + { + createdEntities: createdEntities.map((item) => item.toJSON()), + updatedEntities: updatedEntities.map((item) => item.toJSON()), + removedEntities: removedEntities.map((item) => item.toJSON()), + }, + 'Synchronized masternode identities', + ); + } + + consensusLogger.info(`Block begin #${height}`); + + return new ResponseBeginBlock(); + } + + return beginBlockHandler; +} + +module.exports = beginBlockHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/checkTxHandlerFactory.js b/packages/js-drive/lib/abci/handlers/checkTxHandlerFactory.js new file mode 100644 index 00000000000..1cf6707185c --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/checkTxHandlerFactory.js @@ -0,0 +1,35 @@ +const { + tendermint: { + abci: { + ResponseCheckTx, + }, + }, +} = require('@dashevo/abci/types'); + +/** + * @param {unserializeStateTransition} unserializeStateTransition + * + * @returns {checkTxHandler} + */ +function checkTxHandlerFactory( + unserializeStateTransition, +) { + /** + * CheckTx ABCI Handler + * + * @typedef checkTxHandler + * + * @param {abci.RequestCheckTx} request + * + * @returns {Promise} + */ + async function checkTxHandler({ tx: stateTransitionByteArray }) { + await unserializeStateTransition(stateTransitionByteArray); + + return new ResponseCheckTx(); + } + + return checkTxHandler; +} + +module.exports = checkTxHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/commitHandlerFactory.js b/packages/js-drive/lib/abci/handlers/commitHandlerFactory.js new file mode 100644 index 00000000000..f4896924927 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/commitHandlerFactory.js @@ -0,0 +1,123 @@ +const { + tendermint: { + abci: { + ResponseCommit, + }, + }, +} = require('@dashevo/abci/types'); +const ReadOperation = require('@dashevo/dpp/lib/stateTransition/fee/operations/ReadOperation'); +const DataContractCacheItem = require('../../dataContract/DataContractCacheItem'); + +/** + * @param {CreditsDistributionPool} creditsDistributionPool + * @param {CreditsDistributionPoolRepository} creditsDistributionPoolRepository + * @param {BlockExecutionContext} blockExecutionContext + * @param {BlockExecutionContextStack} blockExecutionContextStack + * @param {BlockExecutionContextStackRepository} blockExecutionContextStackRepository + * @param {rotateSignedStore} rotateSignedStore + * @param {BaseLogger} logger + * @param {LRUCache} dataContractCache + * @param {GroveDBStore} groveDBStore + * @param {ExecutionTimer} executionTimer + * + * @return {commitHandler} + */ +function commitHandlerFactory( + creditsDistributionPool, + creditsDistributionPoolRepository, + blockExecutionContext, + blockExecutionContextStack, + blockExecutionContextStackRepository, + rotateSignedStore, + logger, + dataContractCache, + groveDBStore, + executionTimer, +) { + /** + * Commit ABCI Handler + * + * @typedef commitHandler + * + * @return {Promise} + */ + async function commitHandler() { + const { height: blockHeight } = blockExecutionContext.getHeader(); + + const consensusLogger = logger.child({ + height: blockHeight.toString(), + abciMethod: 'commit', + }); + + blockExecutionContext.setConsensusLogger(consensusLogger); + + consensusLogger.debug('Commit ABCI method requested'); + + // Store ST fees from the block to distribution pool + creditsDistributionPool.incrementAmount( + blockExecutionContext.getCumulativeFees(), + ); + + await creditsDistributionPoolRepository.store( + creditsDistributionPool, + { + useTransaction: true, + }, + ); + + // Store block execution context + blockExecutionContextStack.add(blockExecutionContext); + blockExecutionContextStackRepository.store( + blockExecutionContextStack, + { + useTransaction: true, + }, + ); + + // Commit the current block db transactions + await groveDBStore.commitTransaction(); + + // Update data contract cache with new version of + // committed data contract + for (const dataContract of blockExecutionContext.getDataContracts()) { + const operations = [new ReadOperation(dataContract.toBuffer().length)]; + + const cacheItem = new DataContractCacheItem(dataContract, operations); + + if (dataContractCache.has(cacheItem.getKey())) { + dataContractCache.set(cacheItem.getKey(), cacheItem); + } + } + + // Rotate signed store + // Create a new GroveDB checkpoint and remove the old one + // TODO: We do not rotate signed state for now + // await rotateSignedStore(blockHeight); + + const appHash = await groveDBStore.getRootHash(); + + consensusLogger.info( + { + appHash: appHash.toString('hex').toUpperCase(), + }, + `Block commit #${blockHeight} with appHash ${appHash.toString('hex').toUpperCase()}`, + ); + + const blockExecutionTimings = executionTimer.stopTimer('blockExecution'); + + consensusLogger.trace( + { + timings: blockExecutionTimings, + }, + `Block #${blockHeight} execution took ${blockExecutionTimings} seconds`, + ); + + return new ResponseCommit({ + data: appHash, + }); + } + + return commitHandler; +} + +module.exports = commitHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/deliverTxHandlerFactory.js b/packages/js-drive/lib/abci/handlers/deliverTxHandlerFactory.js new file mode 100644 index 00000000000..7665bda57fd --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/deliverTxHandlerFactory.js @@ -0,0 +1,291 @@ +const { + tendermint: { + abci: { + ResponseDeliverTx, + }, + }, +} = require('@dashevo/abci/types'); + +const crypto = require('crypto'); + +const stateTransitionTypes = require('@dashevo/dpp/lib/stateTransition/stateTransitionTypes'); +const AbstractDocumentTransition = require( + '@dashevo/dpp/lib/document/stateTransition/DocumentsBatchTransition/documentTransition/AbstractDocumentTransition', +); + +const calculateOperationFees = require('@dashevo/dpp/lib/stateTransition/fee/calculateOperationFees'); + +const DPPValidationAbciError = require('../errors/DPPValidationAbciError'); + +const NegativeBalanceError = require('./errors/NegativeBalanceError'); +const PredictedFeeLowerThanActualError = require('./errors/PredictedFeeLowerThanActualError'); + +const DOCUMENT_ACTION_DESCRIPTIONS = { + [AbstractDocumentTransition.ACTIONS.CREATE]: 'created', + [AbstractDocumentTransition.ACTIONS.REPLACE]: 'replaced', + [AbstractDocumentTransition.ACTIONS.DELETE]: 'deleted', +}; + +const DATA_CONTRACT_ACTION_DESCRIPTIONS = { + [stateTransitionTypes.DATA_CONTRACT_CREATE]: 'created', + [stateTransitionTypes.DATA_CONTRACT_UPDATE]: 'updated', +}; + +const TIMERS = require('./timers'); + +/** + * @param {unserializeStateTransition} transactionalUnserializeStateTransition + * @param {DashPlatformProtocol} transactionalDpp + * @param {BlockExecutionContext} blockExecutionContext + * @param {BaseLogger} logger + * @param {ExecutionTimer} executionTimer + * + * @return {deliverTxHandler} + */ +function deliverTxHandlerFactory( + transactionalUnserializeStateTransition, + transactionalDpp, + blockExecutionContext, + logger, + executionTimer, +) { + /** + * DeliverTx ABCI Handler + * + * @typedef deliverTxHandler + * + * @param {abci.RequestDeliverTx} request + * @return {Promise} + */ + async function deliverTxHandler({ tx: stateTransitionByteArray }) { + const { height: blockHeight } = blockExecutionContext.getHeader(); + + // Start execution timer + + executionTimer.clearTimer(TIMERS.DELIVER_TX.OVERALL); + executionTimer.clearTimer(TIMERS.DELIVER_TX.VALIDATE_BASIC); + executionTimer.clearTimer(TIMERS.DELIVER_TX.VALIDATE_FEE); + executionTimer.clearTimer(TIMERS.DELIVER_TX.VALIDATE_SIGNATURE); + executionTimer.clearTimer(TIMERS.DELIVER_TX.VALIDATE_STATE); + executionTimer.clearTimer(TIMERS.DELIVER_TX.APPLY); + + executionTimer.startTimer(TIMERS.DELIVER_TX.OVERALL); + + const stHash = crypto + .createHash('sha256') + .update(stateTransitionByteArray) + .digest() + .toString('hex') + .toUpperCase(); + + const consensusLogger = logger.child({ + height: blockHeight.toString(), + txId: stHash, + abciMethod: 'deliverTx', + }); + + blockExecutionContext.setConsensusLogger(consensusLogger); + + consensusLogger.info(`Deliver state transition ${stHash} from block #${blockHeight}`); + + let stateTransition; + try { + stateTransition = await transactionalUnserializeStateTransition( + stateTransitionByteArray, + { + logger: consensusLogger, + executionTimer, + }, + ); + } catch (e) { + blockExecutionContext.incrementInvalidTxCount(); + + throw e; + } + + // Keep only actual operations + const stateTransitionExecutionContext = stateTransition.getExecutionContext(); + + const predictedStateTransitionFee = stateTransition.calculateFee(); + const predictedStateTransitionOperations = stateTransitionExecutionContext.getOperations(); + + stateTransitionExecutionContext.clearDryOperations(); + + executionTimer.startTimer(TIMERS.DELIVER_TX.VALIDATE_STATE); + + const result = await transactionalDpp.stateTransition.validateState(stateTransition); + + if (!result.isValid()) { + const consensusError = result.getFirstError(); + const message = 'State transition is invalid against the state'; + + consensusLogger.info(message); + consensusLogger.debug({ + consensusError, + }); + + blockExecutionContext.incrementInvalidTxCount(); + + throw new DPPValidationAbciError(message, result.getFirstError()); + } + + executionTimer.stopTimer(TIMERS.DELIVER_TX.VALIDATE_STATE, true); + + executionTimer.startTimer(TIMERS.DELIVER_TX.APPLY); + + // Apply state transition to the state + await transactionalDpp.stateTransition.apply(stateTransition); + + executionTimer.stopTimer(TIMERS.DELIVER_TX.APPLY, true); + + blockExecutionContext.incrementValidTxCount(); + + // Reduce an identity balance and accumulate fees for all STs in the block + // in order to store them in credits distribution pool + const actualStateTransitionFee = stateTransition.calculateFee(); + + if (actualStateTransitionFee > predictedStateTransitionFee) { + throw new PredictedFeeLowerThanActualError( + predictedStateTransitionFee, + actualStateTransitionFee, + stateTransition, + ); + } + + const identity = await transactionalDpp.getStateRepository().fetchIdentity( + stateTransition.getOwnerId(), + ); + + const updatedBalance = identity.reduceBalance(actualStateTransitionFee); + + if (updatedBalance < 0) { + throw new NegativeBalanceError(identity); + } + + await transactionalDpp.getStateRepository().updateIdentity(identity); + + blockExecutionContext.incrementCumulativeFees(actualStateTransitionFee); + + // Logging + switch (stateTransition.getType()) { + case stateTransitionTypes.DATA_CONTRACT_UPDATE: + case stateTransitionTypes.DATA_CONTRACT_CREATE: { + const dataContract = stateTransition.getDataContract(); + + // Save data contracts in order to create databases for documents on block commit + blockExecutionContext.addDataContract(dataContract); + + const description = DATA_CONTRACT_ACTION_DESCRIPTIONS[stateTransition.getType()]; + + consensusLogger.info( + { + dataContractId: dataContract.getId().toString(), + }, + `Data contract ${description} with id: ${dataContract.getId()}`, + ); + + break; + } + case stateTransitionTypes.IDENTITY_CREATE: { + const identityId = stateTransition.getIdentityId(); + + consensusLogger.info( + { + identityId: identityId.toString(), + }, + `Identity created with id: ${identityId}`, + ); + + break; + } + case stateTransitionTypes.IDENTITY_TOP_UP: { + const identityId = stateTransition.getIdentityId(); + + consensusLogger.info( + { + identityId: identityId.toString(), + }, + `Identity topped up with id: ${identityId}`, + ); + + break; + } + case stateTransitionTypes.IDENTITY_UPDATE: { + const identityId = stateTransition.getIdentityId(); + + consensusLogger.info( + { + identityId: identityId.toString(), + }, + `Identity updated with id: ${identityId}`, + ); + break; + } + case stateTransitionTypes.DOCUMENTS_BATCH: { + stateTransition.getTransitions().forEach((transition) => { + const description = DOCUMENT_ACTION_DESCRIPTIONS[transition.getAction()]; + + consensusLogger.info( + { + documentId: transition.getId().toString(), + }, + `Document ${description} with id: ${transition.getId()}`, + ); + }); + + break; + } + default: + break; + } + + const deliverTxTiming = executionTimer.stopTimer(TIMERS.DELIVER_TX.OVERALL); + + const actualStateTransitionOperations = stateTransition.getExecutionContext().getOperations(); + + const { + storageFee: actualStorageFee, + processingFee: actualProcessingFee, + } = calculateOperationFees(actualStateTransitionOperations); + + const { + storageFee: predictedStorageFee, + processingFee: predictedProcessingFee, + } = calculateOperationFees(predictedStateTransitionOperations); + + consensusLogger.trace( + { + timings: { + overall: deliverTxTiming, + validateBasic: executionTimer.getTimer(TIMERS.DELIVER_TX.VALIDATE_BASIC, true), + validateFee: executionTimer.getTimer(TIMERS.DELIVER_TX.VALIDATE_FEE, true), + validateSignature: executionTimer.getTimer(TIMERS.DELIVER_TX.VALIDATE_SIGNATURE, true), + validateState: executionTimer.getTimer(TIMERS.DELIVER_TX.VALIDATE_STATE, true), + apply: executionTimer.getTimer(TIMERS.DELIVER_TX.APPLY, true), + }, + fees: { + predicted: { + storage: predictedStorageFee, + processing: predictedProcessingFee, + final: predictedStateTransitionFee, + operations: predictedStateTransitionOperations.map((operation) => operation.toJSON()), + }, + actual: { + storage: actualStorageFee, + processing: actualProcessingFee, + final: actualStateTransitionFee, + operations: actualStateTransitionOperations.map((operation) => operation.toJSON()), + }, + }, + txType: stateTransition.getType(), + }, + `${stateTransition.constructor.name} execution took ${deliverTxTiming} seconds and cost ${actualStateTransitionFee} credits`, + ); + + return new ResponseDeliverTx(); + } + + return deliverTxHandler; +} + +module.exports = deliverTxHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/endBlockHandlerFactory.js b/packages/js-drive/lib/abci/handlers/endBlockHandlerFactory.js new file mode 100644 index 00000000000..1fb353bde02 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/endBlockHandlerFactory.js @@ -0,0 +1,146 @@ +const { + tendermint: { + abci: { + ResponseEndBlock, + }, + types: { + CoreChainLock, + ConsensusParams, + }, + }, +} = require('@dashevo/abci/types'); + +const featureFlagTypes = require('@dashevo/feature-flags-contract/lib/featureFlagTypes'); + +/** + * Begin block ABCI handler + * + * @param {BlockExecutionContext} blockExecutionContext + * @param {LatestCoreChainLock} latestCoreChainLock + * @param {ValidatorSet} validatorSet + * @param {createValidatorSetUpdate} createValidatorSetUpdate + * @param {BaseLogger} logger + * @param {getFeatureFlagForHeight} getFeatureFlagForHeight + * + * @return {endBlockHandler} + */ +function endBlockHandlerFactory( + blockExecutionContext, + latestCoreChainLock, + validatorSet, + createValidatorSetUpdate, + logger, + getFeatureFlagForHeight, +) { + /** + * @typedef endBlockHandler + * + * @param {abci.RequestEndBlock} request + * @return {Promise} + */ + async function endBlockHandler(request) { + const { height } = request; + + const consensusLogger = logger.child({ + height: height.toString(), + abciMethod: 'endBlock', + }); + + consensusLogger.debug('EndBlock ABCI method requested'); + + blockExecutionContext.setConsensusLogger(consensusLogger); + + const header = blockExecutionContext.getHeader(); + const lastCommitInfo = blockExecutionContext.getLastCommitInfo(); + const coreChainLock = latestCoreChainLock.getChainLock(); + + // Rotate validators + + let validatorSetUpdate; + const rotationEntropy = Buffer.from(lastCommitInfo.stateSignature); + if (await validatorSet.rotate(height, coreChainLock.height, rotationEntropy)) { + validatorSetUpdate = createValidatorSetUpdate(validatorSet); + + const { quorumHash } = validatorSet.getQuorum(); + + consensusLogger.debug( + { + quorumHash, + }, + `Validator set switched to ${quorumHash} quorum`, + ); + } + + // Update Core Chain Locks + + let nextCoreChainLockUpdate; + if (coreChainLock && coreChainLock.height > header.coreChainLockedHeight) { + nextCoreChainLockUpdate = new CoreChainLock({ + coreBlockHeight: coreChainLock.height, + coreBlockHash: coreChainLock.blockHash, + signature: coreChainLock.signature, + }); + + consensusLogger.trace( + { + nextCoreChainLockHeight: coreChainLock.height, + }, + `Provide next chain lock for Core height ${coreChainLock.height}`, + ); + } + + // Update consensus params feature flag + + const updateConsensusParamsFeatureFlag = await getFeatureFlagForHeight( + featureFlagTypes.UPDATE_CONSENSUS_PARAMS, + height, + true, + ); + + let consensusParamUpdates; + if (updateConsensusParamsFeatureFlag) { + // Use previous version if we aren't going to update it + let version = { + appVersion: header.version.app, + }; + + if (updateConsensusParamsFeatureFlag.get('version')) { + version = updateConsensusParamsFeatureFlag.get('version'); + } + + consensusParamUpdates = new ConsensusParams({ + block: updateConsensusParamsFeatureFlag.get('block'), + evidence: updateConsensusParamsFeatureFlag.get('evidence'), + version, + }); + + consensusLogger.info( + { + consensusParamUpdates, + }, + 'Update consensus params', + ); + } + + const validTxCount = blockExecutionContext.getValidTxCount(); + const invalidTxCount = blockExecutionContext.getInvalidTxCount(); + + consensusLogger.info( + { + validTxCount, + invalidTxCount, + }, + `Block end #${height} (valid txs = ${validTxCount}, invalid txs = ${invalidTxCount})`, + ); + + return new ResponseEndBlock({ + consensusParamUpdates, + validatorSetUpdate, + nextCoreChainLockUpdate, + }); + } + + return endBlockHandler; +} + +module.exports = endBlockHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/errors/NegativeBalanceError.js b/packages/js-drive/lib/abci/handlers/errors/NegativeBalanceError.js new file mode 100644 index 00000000000..bfe0e0a399a --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/errors/NegativeBalanceError.js @@ -0,0 +1,12 @@ +const DriveError = require('../../../errors/DriveError'); + +class NegativeBalanceError extends DriveError { + /** + * @param {Identity} identity + */ + constructor(identity) { + super(`Identity ${identity.getId()} has negative balance ${identity.getBalance()}`); + } +} + +module.exports = NegativeBalanceError; diff --git a/packages/js-drive/lib/abci/handlers/errors/NetworkProtocolVersionIsNotSetError.js b/packages/js-drive/lib/abci/handlers/errors/NetworkProtocolVersionIsNotSetError.js new file mode 100644 index 00000000000..39a8ca62828 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/errors/NetworkProtocolVersionIsNotSetError.js @@ -0,0 +1,9 @@ +const DriveError = require('../../../errors/DriveError'); + +class NetworkProtocolVersionIsNotSetError extends DriveError { + constructor() { + super('Network protocol version is not set'); + } +} + +module.exports = NetworkProtocolVersionIsNotSetError; diff --git a/packages/js-drive/lib/abci/handlers/errors/NotSupportedNetworkProtocolVersionError.js b/packages/js-drive/lib/abci/handlers/errors/NotSupportedNetworkProtocolVersionError.js new file mode 100644 index 00000000000..5422637b6b2 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/errors/NotSupportedNetworkProtocolVersionError.js @@ -0,0 +1,30 @@ +const DriveError = require('../../../errors/DriveError'); + +class NotSupportedNetworkProtocolVersionError extends DriveError { + /** + * @param {Long} networkProtocolVersion + * @param {Long} latestProtocolVersion + */ + constructor(networkProtocolVersion, latestProtocolVersion) { + super(`Block protocol version ${networkProtocolVersion} not supported. Expected to be less or equal to ${latestProtocolVersion}.`); + + this.networkProtocolVersion = networkProtocolVersion; + this.latestProtocolVersion = latestProtocolVersion; + } + + /** + * @returns {Long} + */ + getNetworkProtocolVersion() { + return this.networkProtocolVersion; + } + + /** + * @returns {Long} + */ + getLatestProtocolVersion() { + return this.latestProtocolVersion; + } +} + +module.exports = NotSupportedNetworkProtocolVersionError; diff --git a/packages/js-drive/lib/abci/handlers/errors/PredictedFeeLowerThanActualError.js b/packages/js-drive/lib/abci/handlers/errors/PredictedFeeLowerThanActualError.js new file mode 100644 index 00000000000..af8e08262d2 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/errors/PredictedFeeLowerThanActualError.js @@ -0,0 +1,20 @@ +const DriveError = require('../../../errors/DriveError'); + +class PredictedFeeLowerThanActualError extends DriveError { + /** + * @param {number} predictedFee + * @param {number} actualFee + * @param {AbstractStateTransition} stateTransition + */ + constructor(predictedFee, actualFee, stateTransition) { + super(`Predicted fee ${predictedFee} is lower than actual fee ${actualFee}`); + + this.stateTransition = stateTransition; + } + + getStateTransition() { + return this.stateTransition; + } +} + +module.exports = PredictedFeeLowerThanActualError; diff --git a/packages/js-drive/lib/abci/handlers/infoHandlerFactory.js b/packages/js-drive/lib/abci/handlers/infoHandlerFactory.js new file mode 100644 index 00000000000..7ed8ee7f4de --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/infoHandlerFactory.js @@ -0,0 +1,121 @@ +const { + tendermint: { + abci: { + ResponseInfo, + }, + }, +} = require('@dashevo/abci/types'); + +const Long = require('long'); + +const { version: driveVersion } = require('../../../package.json'); + +/** + * @param {BlockExecutionContextStack} blockExecutionContextStack + * @param {BlockExecutionContextStackRepository} blockExecutionContextStackRepository + * @param {BlockExecutionContext} blockExecutionContext + * @param {Long} latestProtocolVersion + * @param {updateSimplifiedMasternodeList} updateSimplifiedMasternodeList + * @param {BaseLogger} logger + * @param {GroveDBStore} groveDBStore + * @param {CreditsDistributionPoolRepository} creditsDistributionPoolRepository + * @param {CreditsDistributionPool} creditsDistributionPool + * @param {BlockExecutionContextStackRepository} blockExecutionContextStackRepository + * @return {infoHandler} + */ +function infoHandlerFactory( + blockExecutionContextStack, + blockExecutionContextStackRepository, + blockExecutionContext, + latestProtocolVersion, + updateSimplifiedMasternodeList, + logger, + groveDBStore, + creditsDistributionPoolRepository, + creditsDistributionPool, +) { + /** + * Info ABCI handler + * + * @typedef infoHandler + * + * @param {abci.RequestInfo} request + * @return {Promise} + */ + async function infoHandler(request) { + let contextLogger = logger.child({ + abciMethod: 'info', + }); + + contextLogger.debug('Info ABCI method requested'); + contextLogger.trace({ abciRequest: request }); + + // Initialize Block Execution Contexts + + const persistedBlockExecutionContextStack = await blockExecutionContextStackRepository.fetch(); + + blockExecutionContextStack.setContexts(persistedBlockExecutionContextStack.getContexts()); + + const latestContext = blockExecutionContextStack.getLatest(); + + if (latestContext) { + blockExecutionContext.populate(blockExecutionContextStack.getLatest()); + } + + // Initialize Credits Distribution Pool + + if (latestContext) { + const fetchedCreditsDistributionPoolResult = await creditsDistributionPoolRepository.fetch(); + + const fetchedCreditsDistributionPool = fetchedCreditsDistributionPoolResult.getValue(); + + creditsDistributionPool.populate(fetchedCreditsDistributionPool.toJSON()); + } + + // Initialize current heights + + let lastHeight = Long.fromNumber(0); + let lastCoreChainLockedHeight = 0; + + if (latestContext) { + const lastHeader = blockExecutionContext.getHeader(); + + lastHeight = lastHeader.height; + lastCoreChainLockedHeight = lastHeader.coreChainLockedHeight; + } + + contextLogger = contextLogger.child({ + height: lastHeight.toString(), + }); + + // Update SML store to latest saved core chain lock to make sure + // that verify chain lock handler has updated SML Store to verify signatures + if (latestContext) { + await updateSimplifiedMasternodeList(lastCoreChainLockedHeight, { + logger: contextLogger, + }); + } + + const appHash = await groveDBStore.getRootHash(); + + contextLogger.info( + { + lastHeight: lastHeight.toString(), + appHash: appHash.toString('hex').toUpperCase(), + latestProtocolVersion: latestProtocolVersion.toString(), + }, + `Start processing from block #${lastHeight} with appHash ${appHash.toString('hex').toUpperCase()}`, + ); + + return new ResponseInfo({ + version: driveVersion, + appVersion: latestProtocolVersion, + lastBlockHeight: lastHeight, + lastBlockAppHash: appHash, + }); + } + + return infoHandler; +} + +module.exports = infoHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/initChainHandlerFactory.js b/packages/js-drive/lib/abci/handlers/initChainHandlerFactory.js new file mode 100644 index 00000000000..a6f1438e640 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/initChainHandlerFactory.js @@ -0,0 +1,121 @@ +const { + tendermint: { + abci: { + ResponseInitChain, + }, + }, +} = require('@dashevo/abci/types'); + +/** + * Init Chain ABCI handler + * + * @param {updateSimplifiedMasternodeList} updateSimplifiedMasternodeList + * @param {number} initialCoreChainLockedHeight + * @param {ValidatorSet} validatorSet + * @param {createValidatorSetUpdate} createValidatorSetUpdate + * @param {synchronizeMasternodeIdentities} synchronizeMasternodeIdentities + * @param {BaseLogger} logger + * @param {createInitialStateStructure} createInitialStateStructure + * @param {registerSystemDataContracts} registerSystemDataContracts + * @param {GroveDBStore} groveDBStore + * + * @return {initChainHandler} + */ +function initChainHandlerFactory( + updateSimplifiedMasternodeList, + initialCoreChainLockedHeight, + validatorSet, + createValidatorSetUpdate, + synchronizeMasternodeIdentities, + logger, + createInitialStateStructure, + registerSystemDataContracts, + groveDBStore, +) { + /** + * @typedef initChainHandler + * + * @param {abci.RequestInitChain} request + * @return {Promise} + */ + async function initChainHandler(request) { + const { time } = request; + + const consensusLogger = logger.child({ + height: request.initialHeight.toString(), + abciMethod: 'initChain', + }); + + consensusLogger.debug('InitChain ABCI method requested'); + consensusLogger.trace({ abciRequest: request }); + + await updateSimplifiedMasternodeList( + initialCoreChainLockedHeight, { + logger: consensusLogger, + }, + ); + + // Create initial state + + await groveDBStore.startTransaction(); + + await createInitialStateStructure(); + + await registerSystemDataContracts(consensusLogger, time); + + const synchronizeMasternodeIdentitiesResult = await synchronizeMasternodeIdentities( + initialCoreChainLockedHeight, + ); + + const { + createdEntities, updatedEntities, removedEntities, fromHeight, toHeight, + } = synchronizeMasternodeIdentitiesResult; + + consensusLogger.info( + `Masternode identities are synced for heights from ${fromHeight} to ${toHeight}: ${createdEntities.length} created, ${updatedEntities.length} updated, ${removedEntities.length} removed`, + ); + + consensusLogger.trace( + { + createdEntities: createdEntities.map((item) => item.toJSON()), + updatedEntities: updatedEntities.map((item) => item.toJSON()), + removedEntities: removedEntities.map((item) => item.toJSON()), + }, + 'Synchronized masternode identities', + ); + + await groveDBStore.commitTransaction(); + + const appHash = await groveDBStore.getRootHash(); + + // Set initial validator set + + await validatorSet.initialize(initialCoreChainLockedHeight); + + const { quorumHash } = validatorSet.getQuorum(); + + const validatorSetUpdate = createValidatorSetUpdate(validatorSet); + + consensusLogger.trace(validatorSetUpdate, `Validator set initialized with ${quorumHash} quorum`); + + consensusLogger.info( + { + chainId: request.chainId, + appHash: appHash.toString('hex').toUpperCase(), + initialHeight: request.initialHeight.toString(), + initialCoreHeight: initialCoreChainLockedHeight, + }, + `Init ${request.chainId} chain on block #${request.initialHeight.toString()} with app hash ${appHash.toString('hex').toUpperCase()}`, + ); + + return new ResponseInitChain({ + appHash, + validatorSetUpdate, + initialCoreHeight: initialCoreChainLockedHeight, + }); + } + + return initChainHandler; +} + +module.exports = initChainHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/query/dataContractQueryHandlerFactory.js b/packages/js-drive/lib/abci/handlers/query/dataContractQueryHandlerFactory.js new file mode 100644 index 00000000000..0e61a1e2517 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/query/dataContractQueryHandlerFactory.js @@ -0,0 +1,81 @@ +const { + tendermint: { + abci: { + ResponseQuery, + }, + }, +} = require('@dashevo/abci/types'); + +const { + v0: { + GetDataContractResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); +const IdentifierError = require('@dashevo/dpp/lib/identifier/errors/IdentifierError'); + +const NotFoundAbciError = require('../../errors/NotFoundAbciError'); +const InvalidArgumentAbciError = require('../../errors/InvalidArgumentAbciError'); + +/** + * + * @param {DataContractStoreRepository} signedDataContractRepository + * @param {createQueryResponse} createQueryResponse + * @param {BlockExecutionContextStack} blockExecutionContextStack + * @return {dataContractQueryHandler} + */ +function dataContractQueryHandlerFactory( + signedDataContractRepository, + createQueryResponse, + blockExecutionContextStack, +) { + /** + * @typedef dataContractQueryHandler + * @param {Object} params + * @param {Object} data + * @param {Buffer} data.id + * @param {RequestQuery} request + * @return {Promise} + */ + async function dataContractQueryHandler(params, { id }, request) { + // There is no signed state (current committed block height less than 3) + if (!blockExecutionContextStack.getLast()) { + throw new NotFoundAbciError('Data Contract not found'); + } + + let contractIdIdentifier; + try { + contractIdIdentifier = new Identifier(id); + } catch (e) { + if (e instanceof IdentifierError) { + throw new InvalidArgumentAbciError('id must be a valid identifier (32 bytes long)'); + } + + throw e; + } + + const response = createQueryResponse(GetDataContractResponse, request.prove); + + if (request.prove) { + const proof = await signedDataContractRepository.prove(contractIdIdentifier); + + response.getProof().setMerkleProof(proof.getValue()); + } else { + const dataContract = await signedDataContractRepository.fetch(contractIdIdentifier); + if (dataContract.isNull()) { + throw new NotFoundAbciError('Data Contract not found'); + } + + response.setDataContract(dataContract.getValue().toBuffer()); + } + + return new ResponseQuery({ + value: response.serializeBinary(), + }); + } + + return dataContractQueryHandler; +} + +module.exports = dataContractQueryHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/query/documentQueryHandlerFactory.js b/packages/js-drive/lib/abci/handlers/query/documentQueryHandlerFactory.js new file mode 100644 index 00000000000..c0df0344cf3 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/query/documentQueryHandlerFactory.js @@ -0,0 +1,111 @@ +const { + tendermint: { + abci: { + ResponseQuery, + }, + }, +} = require('@dashevo/abci/types'); + +const { + v0: { + GetDocumentsResponse, + ResponseMetadata, + }, +} = require('@dashevo/dapi-grpc'); + +const InvalidArgumentAbciError = require('../../errors/InvalidArgumentAbciError'); +const InvalidQueryError = require('../../../document/errors/InvalidQueryError'); + +/** + * + * @param {fetchDocuments} fetchSignedDocuments + * @param {proveDocuments} proveSignedDocuments + * @param {createQueryResponse} createQueryResponse + * @param {BlockExecutionContextStack} blockExecutionContextStack + * @return {documentQueryHandler} + */ +function documentQueryHandlerFactory( + fetchSignedDocuments, + proveSignedDocuments, + createQueryResponse, + blockExecutionContextStack, +) { + /** + * @typedef {documentQueryHandler} + * @param {Object} params + * @param {Object} data + * @param {Buffer} data.contractId + * @param {string} data.type + * @param {string} [data.where] + * @param {string} [data.orderBy] + * @param {string} [data.limit] + * @param {Buffer} [data.startAfter] + * @param {Buffer} [data.startAt] + * @param {RequestQuery} request + * @return {Promise} + */ + async function documentQueryHandler( + params, + { + contractId, + type, + where, + orderBy, + limit, + startAfter, + startAt, + }, + request, + ) { + // There is no signed state (current committed block height less than 3) + if (!blockExecutionContextStack.getLast()) { + const response = new GetDocumentsResponse(); + + response.setMetadata(new ResponseMetadata()); + + return new ResponseQuery({ + value: response.serializeBinary(), + }); + } + + const response = createQueryResponse(GetDocumentsResponse, request.prove); + + const options = { + where, + orderBy, + limit, + startAfter: startAfter ? Buffer.from(startAfter) : startAfter, + startAt: startAt ? Buffer.from(startAt) : startAt, + }; + + try { + if (request.prove) { + const proof = await proveSignedDocuments(contractId, type, options); + + response.getProof().setMerkleProof(proof.getValue()); + } else { + const documentsResult = await fetchSignedDocuments(contractId, type, options); + + const documents = documentsResult.getValue(); + + response.setDocumentsList( + documents.map((document) => document.toBuffer()), + ); + } + } catch (e) { + if (e instanceof InvalidQueryError) { + throw new InvalidArgumentAbciError(`Invalid query: ${e.message}`); + } + + throw e; + } + + return new ResponseQuery({ + value: response.serializeBinary(), + }); + } + + return documentQueryHandler; +} + +module.exports = documentQueryHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/query/getProofsQueryHandlerFactory.js b/packages/js-drive/lib/abci/handlers/query/getProofsQueryHandlerFactory.js new file mode 100644 index 00000000000..a7392338753 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/query/getProofsQueryHandlerFactory.js @@ -0,0 +1,123 @@ +const { + tendermint: { + abci: { + ResponseQuery, + }, + }, +} = require('@dashevo/abci/types'); + +const cbor = require('cbor'); +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); + +/** + * + * @param {BlockExecutionContextStack} blockExecutionContextStack + * @param {IdentityStoreRepository} signedIdentityRepository + * @param {DataContractStoreRepository} signedDataContractRepository + * @param {DocumentRepository} signedDocumentRepository + * @return {getProofsQueryHandler} + */ +function getProofsQueryHandlerFactory( + blockExecutionContextStack, + signedIdentityRepository, + signedDataContractRepository, + signedDocumentRepository, +) { + /** + * @typedef getProofsQueryHandler + * @param params + * @param callArguments + * @param {Buffer[]} callArguments.identityIds + * @param {Buffer[]} callArguments.dataContractIds + * @param {{dataContractId: Buffer, documentId: Buffer, type: string}[]} documents + * @return {Promise} + */ + async function getProofsQueryHandler(params, { + identityIds, + dataContractIds, + documents, + }) { + // There is no signed state (current committed block height less than 3) + if (!blockExecutionContextStack.getLast()) { + return new ResponseQuery({ + value: await cbor.encodeAsync({ + documentsProof: null, + identitiesProof: null, + dataContractsProof: null, + metadata: { + height: 0, + coreChainLockedHeight: 0, + }, + }), + }); + } + + const blockExecutionContext = blockExecutionContextStack.getFirst(); + const signedBlockExecutionContext = blockExecutionContextStack.getLast(); + + const { + height: signedBlockHeight, + coreChainLockedHeight: signedCoreChainLockedHeight, + } = signedBlockExecutionContext.getHeader(); + + const { + quorumHash: signatureLlmqHash, + stateSignature: signature, + } = blockExecutionContext.getLastCommitInfo(); + + const response = { + documentsProof: null, + identitiesProof: null, + dataContractsProof: null, + metadata: { + height: signedBlockHeight.toNumber(), + coreChainLockedHeight: signedCoreChainLockedHeight, + }, + }; + + if (documents && documents.length) { + const documentsProof = await signedDocumentRepository + .proveManyDocumentsFromDifferentContracts(documents); + + response.documentsProof = { + signatureLlmqHash, + signature, + merkleProof: documentsProof.getValue(), + }; + } + + if (identityIds && identityIds.length) { + const identitiesProof = await signedIdentityRepository.proveMany( + identityIds.map((identityId) => Identifier.from(identityId)), + ); + + response.identitiesProof = { + signatureLlmqHash, + signature, + merkleProof: identitiesProof.getValue(), + }; + } + + if (dataContractIds && dataContractIds.length) { + const dataContractsProof = await signedDataContractRepository.proveMany( + dataContractIds.map((dataContractId) => Identifier.from(dataContractId)), + ); + + response.dataContractsProof = { + signatureLlmqHash, + signature, + merkleProof: dataContractsProof.getValue(), + }; + } + + return new ResponseQuery({ + value: await cbor.encodeAsync( + response, + ), + }); + } + + return getProofsQueryHandler; +} + +module.exports = getProofsQueryHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/query/identitiesByPublicKeyHashesQueryHandlerFactory.js b/packages/js-drive/lib/abci/handlers/query/identitiesByPublicKeyHashesQueryHandlerFactory.js new file mode 100644 index 00000000000..0f85a1f8724 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/query/identitiesByPublicKeyHashesQueryHandlerFactory.js @@ -0,0 +1,83 @@ +const { + tendermint: { + abci: { + ResponseQuery, + }, + }, +} = require('@dashevo/abci/types'); + +const { + v0: { + GetIdentitiesByPublicKeyHashesResponse, + ResponseMetadata, + }, +} = require('@dashevo/dapi-grpc'); + +const InvalidArgumentAbciError = require('../../errors/InvalidArgumentAbciError'); + +/** + * + * @param {PublicKeyToIdentitiesStoreRepository} signedPublicKeyToIdentitiesRepository + * @param {number} maxIdentitiesPerRequest + * @param {createQueryResponse} createQueryResponse + * @param {BlockExecutionContextStack} blockExecutionContextStack + * @return {identitiesByPublicKeyHashesQueryHandler} + */ +function identitiesByPublicKeyHashesQueryHandlerFactory( + signedPublicKeyToIdentitiesRepository, + maxIdentitiesPerRequest, + createQueryResponse, + blockExecutionContextStack, +) { + /** + * @typedef identitiesByPublicKeyHashesQueryHandler + * @param {Object} params + * @param {Object} data + * @param {Buffer[]} data.publicKeyHashes + * @param {RequestQuery} request + * @return {Promise} + */ + async function identitiesByPublicKeyHashesQueryHandler(params, { publicKeyHashes }, request) { + if (publicKeyHashes && publicKeyHashes.length > maxIdentitiesPerRequest) { + throw new InvalidArgumentAbciError( + `Maximum number of ${maxIdentitiesPerRequest} requested items exceeded.`, { + maxIdentitiesPerRequest, + }, + ); + } + + // There is no signed state (current committed block height less than 3) + if (!blockExecutionContextStack.getLast()) { + const response = new GetIdentitiesByPublicKeyHashesResponse(); + + response.setIdentitiesList([]); + response.setMetadata(new ResponseMetadata()); + + return new ResponseQuery({ + value: response.serializeBinary(), + }); + } + + const response = createQueryResponse(GetIdentitiesByPublicKeyHashesResponse, request.prove); + + if (request.prove) { + const proof = await signedPublicKeyToIdentitiesRepository.proveMany(publicKeyHashes); + + response.getProof().setMerkleProof(proof.getValue()); + } else { + const identitiesListResult = await signedPublicKeyToIdentitiesRepository.fetchManyBuffers( + publicKeyHashes, + ); + + response.setIdentitiesList(identitiesListResult.getValue()); + } + + return new ResponseQuery({ + value: response.serializeBinary(), + }); + } + + return identitiesByPublicKeyHashesQueryHandler; +} + +module.exports = identitiesByPublicKeyHashesQueryHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/query/identityQueryHandlerFactory.js b/packages/js-drive/lib/abci/handlers/query/identityQueryHandlerFactory.js new file mode 100644 index 00000000000..8bdaf5ee18f --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/query/identityQueryHandlerFactory.js @@ -0,0 +1,84 @@ +const { + tendermint: { + abci: { + ResponseQuery, + }, + }, +} = require('@dashevo/abci/types'); + +const { + v0: { + GetIdentityResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); +const IdentifierError = require('@dashevo/dpp/lib/identifier/errors/IdentifierError'); + +const NotFoundAbciError = require('../../errors/NotFoundAbciError'); +const InvalidArgumentAbciError = require('../../errors/InvalidArgumentAbciError'); + +/** + * + * @param {IdentityStoreRepository} signedIdentityRepository + * @param {createQueryResponse} createQueryResponse + * @param {BlockExecutionContext} blockExecutionContext + * @param {BlockExecutionContextStack} blockExecutionContextStack + * @return {identityQueryHandler} + */ +function identityQueryHandlerFactory( + signedIdentityRepository, + createQueryResponse, + blockExecutionContext, + blockExecutionContextStack, +) { + /** + * @typedef identityQueryHandler + * @param {Object} params + * @param {Object} options + * @param {Buffer} options.id + * @param {RequestQuery} request + * @return {Promise} + */ + async function identityQueryHandler(params, { id }, request) { + // There is no signed state (current committed block height less than 3) + if (!blockExecutionContextStack.getLast()) { + throw new NotFoundAbciError('Identity not found'); + } + + let identifier; + try { + identifier = new Identifier(id); + } catch (e) { + if (e instanceof IdentifierError) { + throw new InvalidArgumentAbciError('id must be a valid identifier (32 bytes long)'); + } + + throw e; + } + + const response = createQueryResponse(GetIdentityResponse, request.prove); + + if (request.prove) { + const proof = await signedIdentityRepository.prove(identifier); + + response.getProof().setMerkleProof(proof.getValue()); + } else { + const identityResult = await signedIdentityRepository.fetch(identifier); + + if (identityResult.isNull()) { + throw new NotFoundAbciError('Identity not found'); + } + + response.setIdentity(identityResult.getValue().toBuffer()); + } + + return new ResponseQuery({ + value: response.serializeBinary(), + }); + } + + return identityQueryHandler; +} + +module.exports = identityQueryHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/query/response/createQueryResponseFactory.js b/packages/js-drive/lib/abci/handlers/query/response/createQueryResponseFactory.js new file mode 100644 index 00000000000..3ef501fdbfc --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/query/response/createQueryResponseFactory.js @@ -0,0 +1,57 @@ +const { + v0: { + Proof, + ResponseMetadata, + }, +} = require('@dashevo/dapi-grpc'); + +/** + * @param {BlockExecutionContextStack} blockExecutionContextStack + * @return {createQueryResponse} + */ +function createQueryResponseFactory( + blockExecutionContextStack, +) { + /** + * @typedef {createQueryResponse} + * @param {Function} ResponseClass + * @param {boolean} [prove=false] + */ + function createQueryResponse(ResponseClass, prove = false) { + const blockExecutionContext = blockExecutionContextStack.getFirst(); + const signedBlockExecutionContext = blockExecutionContextStack.getLast(); + + const { + height: signedBlockHeight, + coreChainLockedHeight: signedCoreChainLockedHeight, + } = signedBlockExecutionContext.getHeader(); + + const response = new ResponseClass(); + + const metadata = new ResponseMetadata(); + metadata.setHeight(signedBlockHeight); + metadata.setCoreChainLockedHeight(signedCoreChainLockedHeight); + + response.setMetadata(metadata); + + if (prove) { + const { + quorumHash: signatureLlmqHash, + stateSignature: signature, + } = blockExecutionContext.getLastCommitInfo(); + + const proof = new Proof(); + + proof.setSignatureLlmqHash(signatureLlmqHash); + proof.setSignature(signature); + + response.setProof(proof); + } + + return response; + } + + return createQueryResponse; +} + +module.exports = createQueryResponseFactory; diff --git a/packages/js-drive/lib/abci/handlers/query/verifyChainLockQueryHandlerFactory.js b/packages/js-drive/lib/abci/handlers/query/verifyChainLockQueryHandlerFactory.js new file mode 100644 index 00000000000..a4af384800a --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/query/verifyChainLockQueryHandlerFactory.js @@ -0,0 +1,96 @@ +const { + tendermint: { + abci: { + ResponseQuery, + }, + }, +} = require('@dashevo/abci/types'); + +const InvalidArgumentAbciError = require('../../errors/InvalidArgumentAbciError'); + +/** + * + * @param {SimplifiedMasternodeList} simplifiedMasternodeList + * @param {decodeChainLock} decodeChainLock + * @param {getLatestFeatureFlag} getLatestFeatureFlag + * @param {BlockExecutionContext} blockExecutionContext + * @param {RpcClient} coreRpcClient + * @param {BaseLogger} logger + * @return {verifyChainLockQueryHandler} + */ +function verifyChainLockQueryHandlerFactory( + simplifiedMasternodeList, + decodeChainLock, + getLatestFeatureFlag, + blockExecutionContext, + coreRpcClient, + logger, +) { + /** + * @typedef verifyChainLockQueryHandler + * @param {Object} params + * @param {Buffer} data + * @return {Promise} + */ + async function verifyChainLockQueryHandler(params, data) { + let chainLock; + try { + chainLock = decodeChainLock(data); + } catch (e) { + logger.debug( + { + chainLock: data.toString('hex'), + }, + 'Invalid chainLock format', + ); + + throw new InvalidArgumentAbciError( + 'Invalid ChainLock format', { chainLock: data.toString('hex') }, + ); + } + + let isVerified; + try { + ({ result: isVerified } = await coreRpcClient.verifyChainLock( + chainLock.blockHash.toString('hex'), + chainLock.signature.toString('hex'), + chainLock.height, + )); + } catch (e) { + // Invalid signature format + // Parse error + if ([-8, -32700].includes(e.code)) { + logger.debug( + { + err: e, + chainLock: data.toString('hex'), + }, + `Chainlock verification failed using verifyChainLock method: ${e.message}`, + ); + + return new ResponseQuery({ + code: e.code, + log: `Chainlock verification failed using verifyChainLock method: ${e.message}`, + }); + } + + throw e; + } + + if (!isVerified) { + logger.debug(`Invalid chainLock for height ${chainLock.height}`); + + throw new InvalidArgumentAbciError( + 'ChainLock verification failed', chainLock.toJSON(), + ); + } + + logger.debug(`ChainLock is valid for height ${chainLock.height}`); + + return new ResponseQuery(); + } + + return verifyChainLockQueryHandler; +} + +module.exports = verifyChainLockQueryHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/queryHandlerFactory.js b/packages/js-drive/lib/abci/handlers/queryHandlerFactory.js new file mode 100644 index 00000000000..77fd405435f --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/queryHandlerFactory.js @@ -0,0 +1,52 @@ +const cbor = require('cbor'); + +const InvalidArgumentAbciError = require('../errors/InvalidArgumentAbciError'); + +/** + * @param {Object} queryHandlerRouter + * @param {Function} sanitizeUrl + * @return {queryHandler} + */ +function queryHandlerFactory(queryHandlerRouter, sanitizeUrl) { + /** + * Query ABCI Handler + * + * @typedef queryHandler + * + * @param {RequestQuery} request + * @return {Promise} + */ + async function queryHandler(request) { + const { path, data } = request; + + const route = queryHandlerRouter.find('GET', sanitizeUrl(path)); + + if (!route) { + throw new InvalidArgumentAbciError('Invalid path', { path }); + } + + const invalidDataMessage = 'Invalid data format: it should be cbor encoded object.'; + + let encodedData = {}; + + const decodeData = route.store && route.store.rawData === true; + + if (data.length > 0) { + try { + encodedData = decodeData ? Buffer.from(data) : cbor.decode(Buffer.from(data)); + } catch (e) { + throw new InvalidArgumentAbciError(invalidDataMessage); + } + + if (encodedData === null || typeof encodedData !== 'object') { + throw new InvalidArgumentAbciError(invalidDataMessage); + } + } + + return route.handler(route.params, encodedData, request); + } + + return queryHandler; +} + +module.exports = queryHandlerFactory; diff --git a/packages/js-drive/lib/abci/handlers/state/registerSystemDataContractsFactory.js b/packages/js-drive/lib/abci/handlers/state/registerSystemDataContractsFactory.js new file mode 100644 index 00000000000..7b1a57e4d7e --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/state/registerSystemDataContractsFactory.js @@ -0,0 +1,139 @@ +/** + * + * @param {registerSystemDataContract} registerSystemDataContract + * @param {registerTopLevelDomain} registerTopLevelDomain + * @param {Identifier} dpnsContractId + * @param {Identifier} dpnsOwnerId + * @param {PublicKey} dpnsOwnerMasterPublicKey + * @param {PublicKey} dpnsOwnerSecondPublicKey + * @param {Object} dpnsDocuments + * @param {Identifier} featureFlagsContractId + * @param {Identifier} featureFlagsOwnerId + * @param {PublicKey} featureFlagsOwnerMasterPublicKey + * @param {PublicKey} featureFlagsOwnerSecondPublicKey + * @param {Object} featureFlagsDocuments + * @param {Identifier} masternodeRewardSharesContractId + * @param {Identifier} masternodeRewardSharesOwnerId + * @param {PublicKey} masternodeRewardSharesOwnerMasterPublicKey + * @param {PublicKey} masternodeRewardSharesOwnerSecondPublicKey + * @param {Object} masternodeRewardSharesDocuments + * @param {Identifier} dashpayContractId + * @param {Identifier} dashpayOwnerId + * @param {PublicKey} dashpayOwnerMasterPublicKey + * @param {PublicKey} dashpayOwnerSecondPublicKey + * @param {Object} dashpayDocuments + * + * @return {registerSystemDataContracts} + */ +function registerSystemDataContractsFactory( + registerSystemDataContract, + registerTopLevelDomain, + dpnsContractId, + dpnsOwnerId, + dpnsOwnerMasterPublicKey, + dpnsOwnerSecondPublicKey, + dpnsDocuments, + featureFlagsContractId, + featureFlagsOwnerId, + featureFlagsOwnerMasterPublicKey, + featureFlagsOwnerSecondPublicKey, + featureFlagsDocuments, + masternodeRewardSharesContractId, + masternodeRewardSharesOwnerId, + masternodeRewardSharesOwnerMasterPublicKey, + masternodeRewardSharesOwnerSecondPublicKey, + masternodeRewardSharesDocuments, + dashpayContractId, + dashpayOwnerId, + dashpayOwnerMasterPublicKey, + dashpayOwnerSecondPublicKey, + dashpayDocuments, +) { + /** + * @typedef {registerSystemDataContracts} + * + * @param {BaseLogger} contextLogger + * @param {{ seconds: Long }} genesisTime + * + * @return {Promise} + */ + async function registerSystemDataContracts(contextLogger, genesisTime) { + contextLogger.debug('Registering Feature Flags data contract'); + contextLogger.trace({ + ownerId: featureFlagsOwnerId, + contractId: featureFlagsContractId, + masterPublicKey: featureFlagsOwnerMasterPublicKey, + secondPublicKey: featureFlagsOwnerSecondPublicKey, + }); + + // Registering feature flags data contract + await registerSystemDataContract( + featureFlagsOwnerId, + featureFlagsContractId, + featureFlagsOwnerMasterPublicKey, + featureFlagsOwnerSecondPublicKey, + featureFlagsDocuments, + ); + + contextLogger.debug('Registering DPNS data contract'); + contextLogger.trace({ + ownerId: dpnsOwnerId, + contractId: dpnsContractId, + masterPublicKey: dpnsOwnerMasterPublicKey, + secondPublicKey: dpnsOwnerSecondPublicKey, + }); + + // Registering DPNS data contract + const dpnsContract = await registerSystemDataContract( + dpnsOwnerId, + dpnsContractId, + dpnsOwnerMasterPublicKey, + dpnsOwnerSecondPublicKey, + dpnsDocuments, + ); + + const genesisDate = new Date( + genesisTime.seconds.toNumber() * 1000, + ); + + await registerTopLevelDomain('dash', dpnsContract, dpnsOwnerId, genesisDate); + + contextLogger.debug('Registering Masternode Rewards data contract'); + contextLogger.trace({ + ownerId: masternodeRewardSharesOwnerId, + contractId: masternodeRewardSharesContractId, + masterPublicKey: masternodeRewardSharesOwnerMasterPublicKey, + secondPublicKey: masternodeRewardSharesOwnerSecondPublicKey, + }); + + // Registering masternode reward sharing data contract + await registerSystemDataContract( + masternodeRewardSharesOwnerId, + masternodeRewardSharesContractId, + masternodeRewardSharesOwnerMasterPublicKey, + masternodeRewardSharesOwnerSecondPublicKey, + masternodeRewardSharesDocuments, + ); + + contextLogger.debug('Registering Dashpay data contract'); + contextLogger.trace({ + ownerId: dashpayOwnerId, + contractId: dashpayContractId, + masterPublicKey: dashpayOwnerMasterPublicKey, + secondPublicKey: dashpayOwnerSecondPublicKey, + }); + + // Registering masternode reward sharing data contract + await registerSystemDataContract( + dashpayOwnerId, + dashpayContractId, + dashpayOwnerMasterPublicKey, + dashpayOwnerSecondPublicKey, + dashpayDocuments, + ); + } + + return registerSystemDataContracts; +} + +module.exports = registerSystemDataContractsFactory; diff --git a/packages/js-drive/lib/abci/handlers/stateTransition/unserializeStateTransitionFactory.js b/packages/js-drive/lib/abci/handlers/stateTransition/unserializeStateTransitionFactory.js new file mode 100644 index 00000000000..252fccf29d9 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/stateTransition/unserializeStateTransitionFactory.js @@ -0,0 +1,120 @@ +const InvalidStateTransitionError = require('@dashevo/dpp/lib/stateTransition/errors/InvalidStateTransitionError'); +const InvalidArgumentAbciError = require('../../errors/InvalidArgumentAbciError'); + +const DPPValidationAbciError = require('../../errors/DPPValidationAbciError'); + +const TIMERS = require('../timers'); + +/** + * @param {DashPlatformProtocol} dpp + * @param {Object} noopLogger + * @return {unserializeStateTransition} + */ +function unserializeStateTransitionFactory(dpp, noopLogger) { + /** + * @typedef unserializeStateTransition + * @param {Uint8Array} stateTransitionByteArray + * @param {Object} [options] + * @param {BaseLogger} [options.logger] + * @param {ExecutionTimer} [options.executionTimer] + * @return {DocumentsBatchTransition|DataContractCreateTransition|IdentityCreateTransition} + */ + async function unserializeStateTransition(stateTransitionByteArray, options = {}) { + // either use a logger passed or use noop logger + const logger = (options.logger || noopLogger); + + // measure timing if timer is passed + const executionTimer = (options.executionTimer || { + startTimer: () => {}, + stopTimer: () => {}, + }); + + if (!stateTransitionByteArray) { + logger.info('State transition is not specified'); + + throw new InvalidArgumentAbciError('State Transition is not specified'); + } + + const stateTransitionSerialized = Buffer.from(stateTransitionByteArray); + + executionTimer.startTimer(TIMERS.DELIVER_TX.VALIDATE_BASIC); + + let stateTransition; + try { + stateTransition = await dpp + .stateTransition + .createFromBuffer(stateTransitionSerialized); + } catch (e) { + if (e instanceof InvalidStateTransitionError) { + const consensusError = e.getErrors()[0]; + const message = 'Invalid state transition'; + + logger.info(message); + logger.debug({ + consensusError, + }); + + throw new DPPValidationAbciError(message, consensusError); + } + + throw e; + } + + executionTimer.stopTimer(TIMERS.DELIVER_TX.VALIDATE_BASIC, true); + + executionTimer.startTimer(TIMERS.DELIVER_TX.VALIDATE_SIGNATURE); + + let result = await dpp.stateTransition.validateSignature(stateTransition); + + if (!result.isValid()) { + const consensusError = result.getFirstError(); + const message = 'Invalid state transition signature'; + + logger.info(message); + + logger.debug({ + consensusError, + }); + + throw new DPPValidationAbciError(message, consensusError); + } + + executionTimer.stopTimer(TIMERS.DELIVER_TX.VALIDATE_SIGNATURE, true); + + executionTimer.startTimer(TIMERS.DELIVER_TX.VALIDATE_FEE); + + const executionContext = stateTransition.getExecutionContext(); + + // Pre-calculate fee for validateState and state transition apply + // with worst case costs to validate the whole state transition execution cost + executionContext.enableDryRun(); + + await dpp.stateTransition.validateState(stateTransition); + await dpp.stateTransition.apply(stateTransition); + + executionContext.disableDryRun(); + + result = await dpp.stateTransition.validateFee(stateTransition); + + if (!result.isValid()) { + const consensusError = result.getFirstError(); + const message = 'Insufficient funds to process state transition'; + + logger.info(message); + + logger.debug({ + consensusError, + }); + + throw new DPPValidationAbciError(message, consensusError); + } + + executionTimer.stopTimer(TIMERS.DELIVER_TX.VALIDATE_FEE, true); + + return stateTransition; + } + + return unserializeStateTransition; +} + +module.exports = unserializeStateTransitionFactory; diff --git a/packages/js-drive/lib/abci/handlers/timers.js b/packages/js-drive/lib/abci/handlers/timers.js new file mode 100644 index 00000000000..8aa29e8c4cf --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/timers.js @@ -0,0 +1,10 @@ +module.exports = { + DELIVER_TX: { + OVERALL: 'deliverTx:overall', + VALIDATE_BASIC: 'deliverTx:validate:basic', + VALIDATE_FEE: 'deliverTx:validate:fee', + VALIDATE_SIGNATURE: 'deliverTx:validate:signature', + VALIDATE_STATE: 'deliverTx:validate:state', + APPLY: 'deliverTx:apply', + }, +}; diff --git a/packages/js-drive/lib/abci/handlers/validator/createValidatorSetUpdate.js b/packages/js-drive/lib/abci/handlers/validator/createValidatorSetUpdate.js new file mode 100644 index 00000000000..60287f54b5e --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/validator/createValidatorSetUpdate.js @@ -0,0 +1,49 @@ +const { + tendermint: { + abci: { + ValidatorUpdate, + ValidatorSetUpdate, + }, + crypto: { + PublicKey, + }, + }, +} = require('@dashevo/abci/types'); + +/** + * @typedef {createValidatorSetUpdate} + * @param {ValidatorSet} validatorSet + * @return {ValidatorSetUpdate} + */ +function createValidatorSetUpdate(validatorSet) { + const validatorUpdates = validatorSet.getValidators() + .map((validator) => { + const networkInfo = validator.getNetworkInfo(); + + const validatorUpdate = new ValidatorUpdate({ + power: validator.getVotingPower(), + proTxHash: validator.getProTxHash(), + nodeAddress: `tcp://${networkInfo.getHost()}:${networkInfo.getPort()}`, + }); + + if (validator.getPublicKeyShare()) { + validatorUpdate.pubKey = new PublicKey({ + bls12381: validator.getPublicKeyShare(), + }); + } + + return validatorUpdate; + }); + + const { quorumPublicKey, quorumHash } = validatorSet.getQuorum(); + + return new ValidatorSetUpdate({ + validatorUpdates, + thresholdPublicKey: new PublicKey({ + bls12381: Buffer.from(quorumPublicKey, 'hex'), + }), + quorumHash: Buffer.from(quorumHash, 'hex'), + }); +} + +module.exports = createValidatorSetUpdate; diff --git a/packages/js-drive/lib/blockExecution/BlockExecutionContext.js b/packages/js-drive/lib/blockExecution/BlockExecutionContext.js new file mode 100644 index 00000000000..d8f11083a92 --- /dev/null +++ b/packages/js-drive/lib/blockExecution/BlockExecutionContext.js @@ -0,0 +1,268 @@ +const DataContract = require('@dashevo/dpp/lib/dataContract/DataContract'); + +const { + tendermint: { + abci: { + LastCommitInfo, + }, + types: { + Header, + }, + }, +} = require('@dashevo/abci/types'); + +const Long = require('long'); + +class BlockExecutionContext { + constructor() { + this.dataContracts = []; + this.cumulativeFees = 0; + this.header = null; + this.lastCommitInfo = null; + this.validTxs = 0; + this.invalidTxs = 0; + this.consensusLogger = null; + } + + /** + * Add Data Contract + * + * @param {DataContract|null} dataContract + */ + addDataContract(dataContract) { + this.dataContracts.push(dataContract); + } + + /** + * Check is data contract with specific ID is persistent in the context + * + * @param {Identifier} dataContractId + * @return {boolean} + */ + hasDataContract(dataContractId) { + const index = this.dataContracts + .findIndex((dataContract) => dataContractId.equals(dataContract.getId())); + + return index !== -1; + } + + /** + * Get Data Contracts + * + * @returns {DataContract[]} + */ + getDataContracts() { + return this.dataContracts; + } + + /** + * @return {number} + */ + getCumulativeFees() { + return this.cumulativeFees; + } + + /** + * Increment cumulative fees + * + * @param {number} fee + */ + incrementCumulativeFees(fee) { + this.cumulativeFees += fee; + + return this; + } + + /** + * Set current block header + * @param {IHeader} header + * @return {BlockExecutionContext} + */ + setHeader(header) { + this.header = header; + + return this; + } + + /** + * Get block header + * + * @return {IHeader|null} + */ + getHeader() { + return this.header; + } + + /** + * Set current block lastCommitInfo + * @param {ILastCommitInfo} lastCommitInfo + * @return {BlockExecutionContext} + */ + setLastCommitInfo(lastCommitInfo) { + this.lastCommitInfo = lastCommitInfo; + + return this; + } + + /** + * Get block lastCommitInfo + * + * @return {ILastCommitInfo|null} + */ + getLastCommitInfo() { + return this.lastCommitInfo; + } + + /** + * Increment number of valid txs processed + * + * @return {BlockExecutionContext} + */ + incrementValidTxCount() { + this.validTxs += 1; + + return this; + } + + /** + * Increment number of invalid txs processed + * + * @return {BlockExecutionContext} + */ + incrementInvalidTxCount() { + this.invalidTxs += 1; + + return this; + } + + /** + * Get number of valid txs processed + * + * @return {number} + */ + getValidTxCount() { + return this.validTxs; + } + + /** + * Get number of invalid txs processed + * + * @return {number} + */ + getInvalidTxCount() { + return this.invalidTxs; + } + + /** + * Set consensus logger + * + * @param {BaseLogger} logger + */ + setConsensusLogger(logger) { + this.consensusLogger = logger; + } + + /** + * Get consensus logger + * + * @return {BaseLogger} + */ + getConsensusLogger() { + if (!this.consensusLogger) { + throw new Error('Consensus logger has not been set'); + } + + return this.consensusLogger; + } + + /** + * Reset state + */ + reset() { + this.dataContracts = []; + this.cumulativeFees = 0; + this.header = null; + this.lastCommitInfo = null; + this.validTxs = 0; + this.invalidTxs = 0; + this.consensusLogger = null; + } + + /** + * Check is the context is not set + * + * @return {boolean} + */ + isEmpty() { + return !this.header; + } + + /** + * Populate the current instance with data from another instance + * + * @param {BlockExecutionContext} blockExecutionContext + */ + populate(blockExecutionContext) { + this.dataContracts = blockExecutionContext.dataContracts; + this.lastCommitInfo = blockExecutionContext.lastCommitInfo; + this.cumulativeFees = blockExecutionContext.cumulativeFees; + this.header = blockExecutionContext.header; + this.validTxs = blockExecutionContext.validTxs; + this.invalidTxs = blockExecutionContext.invalidTxs; + this.consensusLogger = blockExecutionContext.consensusLogger; + } + + /** + * Populate the current instance with data + * + * @param object + */ + fromObject(object) { + this.dataContracts = object.dataContracts + .map((rawDataContract) => new DataContract(rawDataContract)); + this.lastCommitInfo = LastCommitInfo.fromObject(object.lastCommitInfo); + this.cumulativeFees = object.cumulativeFees; + this.header = Header.fromObject(object.header); + this.validTxs = object.validTxs; + this.invalidTxs = object.invalidTxs; + this.consensusLogger = object.consensusLogger; + + this.header.time.seconds = Long.fromNumber(this.header.time.seconds); + this.header.height = Long.fromNumber(this.header.height); + } + + /** + * @param {Object} options + * @param {boolean} [options.skipConsensusLogger=false] + * @return {{ + * dataContracts: Object[], + * invalidTxs: number, + * header: null, + * validTxs: number, + * cumulativeFees: number + * }} + */ + toObject(options = {}) { + const header = Header.toObject(this.header); + + header.time.seconds = header.time.seconds.toNumber(); + header.height = header.height.toNumber(); + + const object = { + dataContracts: this.dataContracts.map((dataContract) => dataContract.toObject()), + cumulativeFees: this.cumulativeFees, + header, + lastCommitInfo: LastCommitInfo.toObject(this.lastCommitInfo), + validTxs: this.validTxs, + invalidTxs: this.invalidTxs, + }; + + if (!options.skipConsensusLogger) { + object.consensusLogger = this.consensusLogger; + } + + return object; + } +} + +module.exports = BlockExecutionContext; diff --git a/packages/js-drive/lib/blockExecution/BlockExecutionContextStack.js b/packages/js-drive/lib/blockExecution/BlockExecutionContextStack.js new file mode 100644 index 00000000000..c3b63727e80 --- /dev/null +++ b/packages/js-drive/lib/blockExecution/BlockExecutionContextStack.js @@ -0,0 +1,89 @@ +const ContextsAreMoreThanStackMaxSizeError = require('./errors/ContextsAreMoreThanStackMaxSizeError'); + +class BlockExecutionContextStack { + /** + * @type {BlockExecutionContext[]} + */ + #contexts = []; + + /** + * @type {number} + */ + #maxSize = 3; + + /** + * + * @param {BlockExecutionContext[]} contexts + */ + setContexts(contexts) { + if (contexts.length > this.#maxSize) { + throw new ContextsAreMoreThanStackMaxSizeError(); + } + + this.#contexts = contexts; + } + + /** + * + * @return {BlockExecutionContext[]} + */ + getContexts() { + return this.#contexts; + } + + /** + * @returns {BlockExecutionContext} + */ + getFirst() { + return this.#contexts[0]; + } + + /** + * Get last context according to stack maximum size + * + * @returns {BlockExecutionContext} + */ + getLast() { + return this.#contexts[this.#maxSize - 1]; + } + + /** + * Get the latest context from the stack + * + * @return {BlockExecutionContext} + */ + getLatest() { + return this.#contexts[this.#contexts.length - 1]; + } + + /** + * Remove the last context from the stack + * + * @return {BlockExecutionContextStack} + */ + removeLatest() { + this.#contexts.pop(); + + return this; + } + + /** + * @param {BlockExecutionContext} context + */ + add(context) { + this.#contexts.unshift(context); + + if (this.#contexts.length > this.#maxSize) { + this.#contexts.pop(); + } + } + + /** + * @return {number} + */ + getSize() { + return this.#contexts.length; + } +} + +module.exports = BlockExecutionContextStack; diff --git a/packages/js-drive/lib/blockExecution/BlockExecutionContextStackRepository.js b/packages/js-drive/lib/blockExecution/BlockExecutionContextStackRepository.js new file mode 100644 index 00000000000..3eaee160b2f --- /dev/null +++ b/packages/js-drive/lib/blockExecution/BlockExecutionContextStackRepository.js @@ -0,0 +1,78 @@ +const cbor = require('cbor'); + +const BlockExecutionContextStack = require('./BlockExecutionContextStack'); +const BlockExecutionContext = require('./BlockExecutionContext'); + +class BlockExecutionContextStackRepository { + /** + * + * @param {GroveDBStore} groveDBStore + */ + constructor(groveDBStore) { + this.db = groveDBStore; + } + + /** + * Store block execution context + * + * @param {BlockExecutionContextStack} blockExecutionContextStack + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @return {this} + */ + async store(blockExecutionContextStack, options = {}) { + const contexts = blockExecutionContextStack.getContexts() + .map((context) => context.toObject({ + skipConsensusLogger: true, + })); + + await this.db.putAux( + BlockExecutionContextStackRepository.EXTERNAL_STORE_KEY_NAME, + await cbor.encodeAsync(contexts), + options, + ); + + return this; + } + + /** + * Fetch block execution stack + * + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * + * @return {BlockExecutionContextStack} + */ + async fetch(options = {}) { + const blockExecutionContextsEncodedResult = await this.db.getAux( + BlockExecutionContextStackRepository.EXTERNAL_STORE_KEY_NAME, + options, + ); + + const blockExecutionContextsEncoded = blockExecutionContextsEncodedResult.getValue(); + + const blockExecutionContextStack = new BlockExecutionContextStack(); + + if (!blockExecutionContextsEncoded) { + return blockExecutionContextStack; + } + + const rawBlockExecutionContexts = cbor.decode(blockExecutionContextsEncoded); + + const blockExecutionContexts = rawBlockExecutionContexts.map((rawContext) => { + const context = new BlockExecutionContext(); + + context.fromObject(rawContext); + + return context; + }); + + blockExecutionContextStack.setContexts(blockExecutionContexts); + + return blockExecutionContextStack; + } +} + +BlockExecutionContextStackRepository.EXTERNAL_STORE_KEY_NAME = Buffer.from('blockExecutionContext'); + +module.exports = BlockExecutionContextStackRepository; diff --git a/packages/js-drive/lib/blockExecution/errors/ContextsAreMoreThanStackMaxSizeError.js b/packages/js-drive/lib/blockExecution/errors/ContextsAreMoreThanStackMaxSizeError.js new file mode 100644 index 00000000000..a5594fd4e5b --- /dev/null +++ b/packages/js-drive/lib/blockExecution/errors/ContextsAreMoreThanStackMaxSizeError.js @@ -0,0 +1,9 @@ +const DriveError = require('../../errors/DriveError'); + +class ContextsAreMoreThanStackMaxSizeError extends DriveError { + constructor() { + super('Number of contexts is more than stack max size'); + } +} + +module.exports = ContextsAreMoreThanStackMaxSizeError; diff --git a/packages/js-drive/lib/core/LatestCoreChainLock.js b/packages/js-drive/lib/core/LatestCoreChainLock.js new file mode 100644 index 00000000000..1dee1a2a642 --- /dev/null +++ b/packages/js-drive/lib/core/LatestCoreChainLock.js @@ -0,0 +1,42 @@ +const EventEmitter = require('events'); + +class LatestCoreChainLock extends EventEmitter { + /** + * + * @param {ChainLock} [chainLock] + */ + constructor(chainLock = undefined) { + super(); + + this.chainLock = chainLock; + } + + /** + * Update latest chainlock + * + * @param {ChainLock} chainLock + * @return {LatestCoreChainLock} + */ + update(chainLock) { + this.chainLock = chainLock; + + this.emit(LatestCoreChainLock.EVENTS.update, this.chainLock); + this.emit(`${LatestCoreChainLock.EVENTS.update}:${this.chainLock.height}`, this.chainLock); + + return this; + } + + /** + * + * @return {ChainLock} + */ + getChainLock() { + return this.chainLock; + } +} + +LatestCoreChainLock.EVENTS = { + update: 'update', +}; + +module.exports = LatestCoreChainLock; diff --git a/packages/js-drive/lib/core/SimplifiedMasternodeList.js b/packages/js-drive/lib/core/SimplifiedMasternodeList.js new file mode 100644 index 00000000000..804d55707b8 --- /dev/null +++ b/packages/js-drive/lib/core/SimplifiedMasternodeList.js @@ -0,0 +1,47 @@ +const SimplifiedMNListStore = require('@dashevo/dashcore-lib/lib/deterministicmnlist/SimplifiedMNListStore'); + +class SimplifiedMasternodeList { + constructor(options) { + this.options = { + maxListsLimit: options.smlMaxListsLimit, + }; + + this.store = undefined; + } + + /** + * @param {SimplifiedMNListDiff[]} smlDiffs + * + * @return SimplifiedMasternodeList + */ + applyDiffs(smlDiffs) { + if (!this.store) { + this.store = new SimplifiedMNListStore([...smlDiffs], this.options); + } else { + smlDiffs.forEach((diff) => { + this.store.addDiff(diff); + }); + } + + return this; + } + + /** + * + * @return {SimplifiedMNListStore|undefined} + */ + getStore() { + return this.store; + } + + /** + * Reset the SML store + * + * @return {void} + */ + reset() { + this.store = undefined; + } +} + +module.exports = SimplifiedMasternodeList; diff --git a/packages/js-drive/lib/core/ZmqClient.js b/packages/js-drive/lib/core/ZmqClient.js new file mode 100644 index 00000000000..e3450af613d --- /dev/null +++ b/packages/js-drive/lib/core/ZmqClient.js @@ -0,0 +1,129 @@ +const { EventEmitter } = require('events'); +const zeromq = require('zeromq'); + +const ZMQ_TOPICS = { + hashtx: 'hashtx', + hashtxlock: 'hashtxlock', + hashblock: 'hashblock', + rawblock: 'rawblock', + rawtx: 'rawtx', + rawtxlock: 'rawtxlock', + rawtxlocksig: 'rawtxlocksig', + rawchainlock: 'rawchainlock', + rawchainlocksig: 'rawchainlocksig', +}; + +const defaultOptions = { topics: ZMQ_TOPICS, maxRetryCount: 20 }; + +class ZmqClient extends EventEmitter { + constructor(host, port, options = defaultOptions) { + super(); + this.subscriberSocket = zeromq.socket('sub'); + this.connectionString = `tcp://${host}:${port}`; + this.topics = options.topics || []; + this.maxRetryCount = options.maxRetryCount; + this.isConnected = false; + this.resetConnectionFailuresCount(); + } + + resetConnectionFailuresCount() { + this.connectionFailuresCount = 0; + } + + /** + * Starts listening to zmq messages + * @returns {Promise} + */ + start() { + return new Promise((resolve) => { + this.subscriberSocket.once('connect', () => resolve()); + this.subscriberSocket.once('connect', () => { + this.emit(ZmqClient.events.CONNECTED); + }); + this.subscriberSocket.on('connect', () => { + this.resetConnectionFailuresCount(); + }); + + this.initErrorHandlers(); + this.initMessageHandlers(); + this.startMonitor(); + this.subscriberSocket.connect(this.connectionString); + this.isConnected = true; + }); + } + + /** + * @private + * Starts connection monitor to monitor connection status + */ + startMonitor() { + this.subscriberSocket.monitor(500, 0); + } + + /** + * @private + */ + incrementErrorCount() { + this.connectionFailuresCount += 1; + if (this.connectionFailuresCount >= this.maxRetryCount) { + this.emit(ZmqClient.events.MAX_RETRIES_REACHED, `Failed to connect to ZMQ after ${this.maxRetryCount} tries`); + } + } + + /** + * Init connection error handlers. Requires connection monitor to be started + */ + initErrorHandlers() { + this.subscriberSocket.on('connect_delay', () => { + this.emit(ZmqClient.events.CONNECTION_DELAY, 'Dashcore ZMQ connection delay'); + this.incrementErrorCount(); + }); + this.subscriberSocket.on('disconnect', () => { + this.emit(ZmqClient.events.DISCONNECTED, 'Dashcore ZMQ connection is lost'); + this.incrementErrorCount(); + }); + this.subscriberSocket.on('monitor_error', (error) => { + this.emit(ZmqClient.events.MONITOR_ERROR, error); + this.incrementErrorCount(); + setTimeout(() => this.startMonitor(), 1000); + }); + } + + /** + * Subscribes to zmq messages + */ + initMessageHandlers() { + Object.keys(this.topics).forEach((key) => this.subscriberSocket.subscribe(this.topics[key])); + this.subscriberSocket.on('message', this.emit.bind(this)); + } + + subscribe(topicName, callback) { + const isAlreadySubscribed = Object.keys(this.topics).includes(topicName); + + if (!this.isConnected) { + throw new Error('Socket not connected. Wait until .start() resolves'); + } + + if (!isAlreadySubscribed) { + this.topics[topicName] = topicName; + this.subscriberSocket.subscribe(topicName); + } + + if (callback) { + this.on(topicName, callback); + } + } +} + +ZmqClient.events = { + CONNECTION_DELAY: 'CONNECTION_DELAY', + DISCONNECTED: 'DISCONNECTED', + MONITOR_ERROR: 'MONITOR_ERROR', + ERROR: 'ERROR', + MAX_RETRIES_REACHED: 'MAX_RETRIES_REACHED', + CONNECTED: 'CONNECTED', +}; + +ZmqClient.TOPICS = ZMQ_TOPICS; + +module.exports = ZmqClient; diff --git a/packages/js-drive/lib/core/decodeChainLock.js b/packages/js-drive/lib/core/decodeChainLock.js new file mode 100644 index 00000000000..6179aa53eb6 --- /dev/null +++ b/packages/js-drive/lib/core/decodeChainLock.js @@ -0,0 +1,25 @@ +const { ChainLock } = require('@dashevo/dashcore-lib'); +const { + tendermint: { + types: { + CoreChainLock, + }, + }, +} = require('@dashevo/abci/types'); + +/** + * @typedef decodeChainLock + * @param {Buffer} buffer - serialized chainLock as buffer + * @return {ChainLock} + */ +function decodeChainLock(buffer) { + const coreChainLock = CoreChainLock.decode(buffer); + + return ChainLock.fromObject({ + height: coreChainLock.coreBlockHeight, + blockHash: coreChainLock.coreBlockHash, + signature: coreChainLock.signature, + }); +} + +module.exports = decodeChainLock; diff --git a/packages/js-drive/lib/core/ensureBlock.js b/packages/js-drive/lib/core/ensureBlock.js new file mode 100644 index 00000000000..4b3091dcde1 --- /dev/null +++ b/packages/js-drive/lib/core/ensureBlock.js @@ -0,0 +1,35 @@ +const ZMQClient = require('./ZmqClient'); + +/** + * + * @param {ZMQClient} zmqClient + * @param {RpcClient} rpcClient + * @param {string} hash + * @return {Promise} + */ +async function ensureBlock(zmqClient, rpcClient, hash) { + const eventPromise = new Promise((resolve) => { + const onHashBlock = (response) => { + if (hash.toString('hex') === response.toString('hex')) { + zmqClient.removeListener(ZMQClient.TOPICS.hashblock, onHashBlock); + + resolve(response); + } + }; + + zmqClient.on(ZMQClient.TOPICS.hashblock, onHashBlock); + }); + + try { + await rpcClient.getBlock(hash.toString('hex')); + } catch (e) { + // Block not found + if (e.code === -5) { + await eventPromise; + } else { + throw e; + } + } +} + +module.exports = ensureBlock; diff --git a/packages/js-drive/lib/core/errors/MissingChainLockError.js b/packages/js-drive/lib/core/errors/MissingChainLockError.js new file mode 100644 index 00000000000..ab3a2960e45 --- /dev/null +++ b/packages/js-drive/lib/core/errors/MissingChainLockError.js @@ -0,0 +1,11 @@ +class MissingChainLockError extends Error { + constructor() { + super('ChainLock is required to obtain SML'); + + this.name = this.constructor.name; + + Error.captureStackTrace(this, this.constructor); + } +} + +module.exports = MissingChainLockError; diff --git a/packages/js-drive/lib/core/errors/NotEnoughBlocksForValidSMLError.js b/packages/js-drive/lib/core/errors/NotEnoughBlocksForValidSMLError.js new file mode 100644 index 00000000000..9021506a9ef --- /dev/null +++ b/packages/js-drive/lib/core/errors/NotEnoughBlocksForValidSMLError.js @@ -0,0 +1,23 @@ +const DriveError = require('../../errors/DriveError'); + +class NotEnoughBlocksForValidSMLError extends DriveError { + /** + * @param {number} blockHeight + */ + constructor(blockHeight) { + super(`${blockHeight} blocks are not enough to obtain comprehensive SML. Needs 16 block diffs minimum`); + + this.blockHeight = blockHeight; + } + + /** + * Get block height + * + * @return {number} + */ + getBlockHeight() { + return this.blockHeight; + } +} + +module.exports = NotEnoughBlocksForValidSMLError; diff --git a/packages/js-drive/lib/core/errors/QuorumsNotFoundError.js b/packages/js-drive/lib/core/errors/QuorumsNotFoundError.js new file mode 100644 index 00000000000..6b3e12d0ead --- /dev/null +++ b/packages/js-drive/lib/core/errors/QuorumsNotFoundError.js @@ -0,0 +1,31 @@ +const DriveError = require('../../errors/DriveError'); + +class QuorumsNotFoundError extends DriveError { + /** + * @param {SimplifiedMNList} simplifiedMNList + * @param {number} quorumType + */ + constructor(simplifiedMNList, quorumType) { + let message; + if (simplifiedMNList.quorumList.length === 0) { + message = `SML at block ${simplifiedMNList.blockHash} contains no quorums of any type`; + } else { + const otherQuorumTypes = [...new Set(simplifiedMNList.quorumList.map((quorumEntry) => quorumEntry.llmqType))].join(','); + message = `SML at block ${simplifiedMNList.blockHash} contains no quorums of type ${quorumType}, but contains entries for types ${otherQuorumTypes}. Please check the Drive configuration`; + } + super(message); + + this.simplifiedMNList = simplifiedMNList; + } + + /** + * Get block height + * + * @return {SimplifiedMNList} + */ + getSimplifiedMNList() { + return this.simplifiedMNList; + } +} + +module.exports = QuorumsNotFoundError; diff --git a/packages/js-drive/lib/core/fetchQuorumMembersFactory.js b/packages/js-drive/lib/core/fetchQuorumMembersFactory.js new file mode 100644 index 00000000000..bd175746c5d --- /dev/null +++ b/packages/js-drive/lib/core/fetchQuorumMembersFactory.js @@ -0,0 +1,38 @@ +/** + * @param {RpcClient} coreRpcClient + * @return {fetchQuorumMembers} + */ +function fetchQuorumMembersFactory(coreRpcClient) { + /** + * @typedef {fetchQuorumMembers} + * @param {number} quorumType + * @param {string} quorumHash + * @return {Promise} + */ + async function fetchQuorumMembers(quorumType, quorumHash) { + try { + const { + result: { + members: validators, + }, + } = await coreRpcClient.quorum( + 'info', + quorumType, + quorumHash, + ); + + return validators; + } catch (e) { + // RPC_INVALID_PARAMETER: quorum not found + if (e.code === -8) { + throw new Error(`The quorum of type ${quorumType} and quorumHash ${quorumHash} doesn't exist`); + } + + throw e; + } + } + + return fetchQuorumMembers; +} + +module.exports = fetchQuorumMembersFactory; diff --git a/packages/js-drive/lib/core/fetchTransactionFactory.js b/packages/js-drive/lib/core/fetchTransactionFactory.js new file mode 100644 index 00000000000..2a770150429 --- /dev/null +++ b/packages/js-drive/lib/core/fetchTransactionFactory.js @@ -0,0 +1,33 @@ +const { Transaction } = require('@dashevo/dashcore-lib'); + +/** + * @param {RpcClient} coreRpcClient + * @returns {fetchTransaction} + */ +function fetchTransactionFactory(coreRpcClient) { + /** + * @typedef {fetchTransaction} + * @param {string} id + * @returns {Transaction} + */ + async function fetchTransaction(id) { + let rawTransaction; + + try { + ({ result: rawTransaction } = await coreRpcClient.getRawTransaction(id, 1)); + } catch (e) { + // Invalid address or key error + if (e.code === -5) { + return null; + } + + throw e; + } + + return new Transaction(rawTransaction.hex); + } + + return fetchTransaction; +} + +module.exports = fetchTransactionFactory; diff --git a/packages/js-drive/lib/core/getRandomQuorum.js b/packages/js-drive/lib/core/getRandomQuorum.js new file mode 100644 index 00000000000..20f706ffb77 --- /dev/null +++ b/packages/js-drive/lib/core/getRandomQuorum.js @@ -0,0 +1,53 @@ +const BufferWriter = require('@dashevo/dashcore-lib/lib/encoding/bufferwriter'); +const Hash = require('@dashevo/dashcore-lib/lib/crypto/hash'); +const QuorumsNotFoundError = require('./errors/QuorumsNotFoundError'); + +/** + * Calculates scores for validator quorum selection + * it calculates sha256(hash, modifier) per quorumHash + * Please note that this is not a double-sha256 but a single-sha256 + * + * @param {Buffer[]} quorumHashes + * @param {Buffer} modifier + * @return {Object[]} scores + */ +function calculateQuorumHashScores(quorumHashes, modifier) { + return quorumHashes.map((hash) => { + const bufferWriter = new BufferWriter(); + + bufferWriter.write(hash); + bufferWriter.write(modifier); + + return { score: Hash.sha256(bufferWriter.toBuffer()), hash }; + }); +} + +/** + * Gets the current validator set quorum hash for a particular core height + * + * @typedef {getRandomQuorum} + * @param {SimplifiedMNList} sml + * @param {number} quorumType + * @param {Buffer} entropy - the entropy to select the quorum + * @return {QuorumEntry} - the current validator set's quorumHash + */ +function getRandomQuorum(sml, quorumType, entropy) { + const validatorQuorums = sml.getQuorumsOfType(quorumType); + + if (validatorQuorums.length === 0) { + throw new QuorumsNotFoundError(sml, quorumType); + } + + const validatorQuorumHashes = validatorQuorums + .map((quorum) => Buffer.from(quorum.quorumHash, 'hex')); + + const scoredHashes = calculateQuorumHashScores(validatorQuorumHashes, entropy); + + scoredHashes.sort((a, b) => Buffer.compare(a.score, b.score)); + + const quorumHash = scoredHashes[0].hash.toString('hex'); + + return sml.getQuorum(quorumType, quorumHash); +} + +module.exports = getRandomQuorum; diff --git a/packages/js-drive/lib/core/updateSimplifiedMasternodeListFactory.js b/packages/js-drive/lib/core/updateSimplifiedMasternodeListFactory.js new file mode 100644 index 00000000000..5f46e530903 --- /dev/null +++ b/packages/js-drive/lib/core/updateSimplifiedMasternodeListFactory.js @@ -0,0 +1,113 @@ +const SimplifiedMNListDiff = require('@dashevo/dashcore-lib/lib/deterministicmnlist/SimplifiedMNListDiff'); + +const NotEnoughBlocksForValidSMLError = require('./errors/NotEnoughBlocksForValidSMLError'); + +/** + * Check that core is synced (factory) + * + * @param {RpcClient} coreRpcClient + * @param {SimplifiedMasternodeList} simplifiedMasternodeList + * @param {number} smlMaxListsLimit + * @param {string} network + * @param {BaseLogger} logger + * + * @returns {updateSimplifiedMasternodeList} + */ +function updateSimplifiedMasternodeListFactory( + coreRpcClient, + simplifiedMasternodeList, + smlMaxListsLimit, + network, + logger, +) { + // 1 means first block + let latestRequestedHeight = 1; + + /** + * @param {number} fromHeight + * @param {number} toHeight + * @return {Promise} + */ + async function fetchDiffsPerBlock(fromHeight, toHeight) { + const diffs = []; + + for (let height = fromHeight; height < toHeight; height += 1) { + const { result: rawDiff } = await coreRpcClient.protx('diff', height, height + 1); + + const diff = new SimplifiedMNListDiff(rawDiff, network); + + diffs.push(diff); + } + + return diffs; + } + + /** + * Check that core is synced + * + * @typedef updateSimplifiedMasternodeList + * @param {number} coreHeight + * @param {Object} [options] + * @param {BaseLogger} [options.logger] + * + * @returns {Promise} + */ + async function updateSimplifiedMasternodeList(coreHeight, options = {}) { + // either use a logger passed or use standard logger + const contextLogger = (options.logger || logger); + + // Should be enough to get 16 diffs + if (coreHeight < smlMaxListsLimit + 1) { + throw new NotEnoughBlocksForValidSMLError(coreHeight); + } + + // When we got more than 16 blocks of difference between last requested height + // and core height, we take only last 16 of them. + if (coreHeight - latestRequestedHeight > smlMaxListsLimit) { + latestRequestedHeight = 1; + simplifiedMasternodeList.reset(); + } + + if (latestRequestedHeight === 1) { + // Initialize SML with 16 diffs to have enough quorum information + // to be able to verify signatures + + const startHeight = coreHeight - smlMaxListsLimit; + + const { result: rawDiff } = await coreRpcClient.protx('diff', latestRequestedHeight, startHeight); + + const initialSmlDiffs = [ + new SimplifiedMNListDiff(rawDiff, network), + ...await fetchDiffsPerBlock(startHeight, coreHeight), + ]; + + simplifiedMasternodeList.applyDiffs(initialSmlDiffs); + + latestRequestedHeight = coreHeight; + + contextLogger.debug(`SML is initialized for core heights ${startHeight} to ${coreHeight}`); + + return true; + } + + if (latestRequestedHeight < coreHeight) { + // Update SML + + const smlDiffs = await fetchDiffsPerBlock(latestRequestedHeight, coreHeight); + + simplifiedMasternodeList.applyDiffs(smlDiffs); + + contextLogger.debug(`SML is updated for core heights ${latestRequestedHeight} to ${coreHeight}`); + + latestRequestedHeight = coreHeight; + + return true; + } + + return false; + } + + return updateSimplifiedMasternodeList; +} + +module.exports = updateSimplifiedMasternodeListFactory; diff --git a/packages/js-drive/lib/core/waitForChainLockedHeightFactory.js b/packages/js-drive/lib/core/waitForChainLockedHeightFactory.js new file mode 100644 index 00000000000..8d01c2fdfcc --- /dev/null +++ b/packages/js-drive/lib/core/waitForChainLockedHeightFactory.js @@ -0,0 +1,49 @@ +const MissingChainLockError = require('./errors/MissingChainLockError'); +const LatestCoreChainLock = require('./LatestCoreChainLock'); + +/** + * + * @param {LatestCoreChainLock} latestCoreChainLock + + * @return {waitForChainLockedHeight} + */ +function waitForChainLockedHeightFactory( + latestCoreChainLock, +) { + /** + * @typedef waitForChainLockedHeight + * @param {number} coreHeight + * + * @return {Promise} + */ + async function waitForChainLockedHeight(coreHeight) { + // ChainLock is required to get finalized SML that won't be reorged + const existingChainLock = latestCoreChainLock.getChainLock(); + + if (!existingChainLock) { + throw new MissingChainLockError(); + } + + // Wait for core to be synced up to coreHeight + if (coreHeight > existingChainLock.height) { + await new Promise((resolve) => { + const listener = (chainLock) => { + // Skip if core height still not reached + if (coreHeight > chainLock.height) { + return; + } + + latestCoreChainLock.removeListener(LatestCoreChainLock.EVENTS.update, listener); + + resolve(); + }; + + latestCoreChainLock.on(LatestCoreChainLock.EVENTS.update, listener); + }); + } + } + + return waitForChainLockedHeight; +} + +module.exports = waitForChainLockedHeightFactory; diff --git a/packages/js-drive/lib/core/waitForCoreChainLockSyncFactory.js b/packages/js-drive/lib/core/waitForCoreChainLockSyncFactory.js new file mode 100644 index 00000000000..75acdced6ed --- /dev/null +++ b/packages/js-drive/lib/core/waitForCoreChainLockSyncFactory.js @@ -0,0 +1,100 @@ +const { ChainLock } = require('@dashevo/dashcore-lib'); + +const ChainLockSigMessage = require('@dashevo/dashcore-lib/lib/zmqMessages/ChainLockSigMessage'); +const ZMQClient = require('./ZmqClient'); + +const ensureBlock = require('./ensureBlock'); + +/** + * Wait and ensure that core chain lock stays synced (factory) + * + * @param {ZMQClient} coreZMQClient + * @param {RpcClient} coreRpcClient + * @param {LatestCoreChainLock} latestCoreChainLock + * @param {BaseLogger} logger + * + * @returns {waitForCoreSync} + */ +function waitForCoreChainLockSyncFactory( + coreZMQClient, + coreRpcClient, + latestCoreChainLock, + logger, +) { + /** + * Wait and ensure that core chain lock stays synced. + * On new ChainLock received, will also ensure that its block has been processed. + * + * @typedef waitForCoreChainLockSync + * + * @returns {Promise} + */ + async function waitForCoreChainLockSync() { + coreZMQClient.subscribe(ZMQClient.TOPICS.rawchainlocksig); + + let resolveFirstChainLockFromZMQPromise; + const firstChainLockFromZMQPromise = new Promise((resolve) => { + resolveFirstChainLockFromZMQPromise = resolve; + }); + + coreZMQClient.on(ZMQClient.TOPICS.rawchainlocksig, async (rawChainLockMessage) => { + let chainLock; + + try { + ({ chainLock } = new ChainLockSigMessage(rawChainLockMessage)); + } catch (e) { + logger.error({ err: e }, 'Error on creating ChainLockSigMessage'); + logger.debug({ + rawChainLockMessage: rawChainLockMessage.toString('hex'), + }); + + return; + } + + latestCoreChainLock.update(chainLock); + + logger.trace( + { + rawChainLockMessage: rawChainLockMessage.toString('hex'), + }, + `Updated latestCoreChainLock for core height ${chainLock.height}`, + ); + + if (resolveFirstChainLockFromZMQPromise) { + resolveFirstChainLockFromZMQPromise(); + resolveFirstChainLockFromZMQPromise = null; + } + }); + + // Because a ChainLock may happen before its block, we also subscribe to rawblock + coreZMQClient.subscribe(ZMQClient.TOPICS.hashblock); + + // We need to retrieve latest ChainLock from our fully synced Core instance + let rpcBestChainLockResponse; + try { + rpcBestChainLockResponse = await coreRpcClient.getBestChainLock(); + } catch (e) { + // Unable to find any ChainLock + if (e.code === -32603) { + logger.debug('There is no chain locks currently. Waiting for a first one...'); + + // We need to wait for a new ChainLock from ZMQ socket + await firstChainLockFromZMQPromise; + } else { + throw e; + } + } + + if (rpcBestChainLockResponse) { + const chainLock = new ChainLock(rpcBestChainLockResponse.result); + + await ensureBlock(coreZMQClient, coreRpcClient, chainLock.blockHash); + + latestCoreChainLock.update(chainLock); + } + } + + return waitForCoreChainLockSync; +} + +module.exports = waitForCoreChainLockSyncFactory; diff --git a/packages/js-drive/lib/core/waitForCoreSyncFactory.js b/packages/js-drive/lib/core/waitForCoreSyncFactory.js new file mode 100644 index 00000000000..9a4b612101a --- /dev/null +++ b/packages/js-drive/lib/core/waitForCoreSyncFactory.js @@ -0,0 +1,47 @@ +const wait = require('../util/wait'); + +/** + * Check that core is synced (factory) + * + * @param {RpcClient} coreRpcClient + * + * @returns {waitForCoreSync} + */ +function waitForCoreSyncFactory(coreRpcClient) { + /** + * Check that core is synced + * + * @typedef waitForCoreSync + * + * @param {function(number, number)} progressCallback + * + * @returns {Promise} + */ + async function waitForCoreSync(progressCallback) { + let isBlockchainSynced = false; + while (!isBlockchainSynced) { + ({ + result: { + IsBlockchainSynced: isBlockchainSynced, + }, + } = await coreRpcClient.mnsync('status')); + + if (!isBlockchainSynced) { + const { + result: { + blocks: currentBlockHeight, + headers: currentHeadersNumber, + }, + } = await coreRpcClient.getBlockchainInfo(); + + progressCallback(currentBlockHeight, currentHeadersNumber); + + await wait(10000); + } + } + } + + return waitForCoreSync; +} + +module.exports = waitForCoreSyncFactory; diff --git a/packages/js-drive/lib/createDIContainer.js b/packages/js-drive/lib/createDIContainer.js new file mode 100644 index 00000000000..6a6c9c4f961 --- /dev/null +++ b/packages/js-drive/lib/createDIContainer.js @@ -0,0 +1,826 @@ +const { + createContainer: createAwilixContainer, + InjectionMode, + asClass, + asFunction, + asValue, +} = require('awilix'); + +const fs = require('fs'); + +const Long = require('long'); + +const RSDrive = require('@dashevo/rs-drive'); + +const LRUCache = require('lru-cache'); +const RpcClient = require('@dashevo/dashd-rpc/promise'); + +const { PublicKey } = require('@dashevo/dashcore-lib'); + +const DashPlatformProtocol = require('@dashevo/dpp'); + +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); + +const findMyWay = require('find-my-way'); + +const pino = require('pino'); +const pinoMultistream = require('pino-multi-stream'); + +const createABCIServer = require('@dashevo/abci'); + +const protocolVersion = require('@dashevo/dpp/lib/version/protocolVersion'); + +const decodeProtocolEntityFactory = require('@dashevo/dpp/lib/decodeProtocolEntityFactory'); + +const featureFlagsSystemIds = require('@dashevo/feature-flags-contract/lib/systemIds'); +const featureFlagsDocuments = require('@dashevo/feature-flags-contract/schema/feature-flags-documents.json'); + +const dpnsSystemIds = require('@dashevo/dpns-contract/lib/systemIds'); +const dpnsDocuments = require('@dashevo/dpns-contract/schema/dpns-contract-documents.json'); + +const masternodeRewardsSystemIds = require('@dashevo/masternode-reward-shares-contract/lib/systemIds'); +const masternodeRewardsDocuments = require('@dashevo/masternode-reward-shares-contract/schema/masternode-reward-shares-documents.json'); + +const dashpaySystemIds = require('@dashevo/dashpay-contract/lib/systemIds'); +const dashpayDocuments = require('@dashevo/dashpay-contract/schema/dashpay.schema.json'); + +const packageJSON = require('../package.json'); + +const ZMQClient = require('./core/ZmqClient'); + +const sanitizeUrl = require('./util/sanitizeUrl'); + +const LatestCoreChainLock = require('./core/LatestCoreChainLock'); + +const GroveDBStore = require('./storage/GroveDBStore'); +const IdentityStoreRepository = require('./identity/IdentityStoreRepository'); + +const PublicKeyToIdentitiesStoreRepository = require( + './identity/PublicKeyToIdentitiesStoreRepository', +); + +const DataContractStoreRepository = require('./dataContract/DataContractStoreRepository'); + +const fetchDocumentsFactory = require('./document/fetchDocumentsFactory'); +const proveDocumentsFactory = require('./document/proveDocumentsFactory'); +const fetchDataContractFactory = require('./document/fetchDataContractFactory'); +const BlockExecutionContext = require('./blockExecution/BlockExecutionContext'); + +const CreditsDistributionPoolRepository = require('./creditsDistributionPool/CreditsDistributionPoolRepository'); +const unserializeStateTransitionFactory = require( + './abci/handlers/stateTransition/unserializeStateTransitionFactory', +); +const DriveStateRepository = require('./dpp/DriveStateRepository'); + +const CachedStateRepositoryDecorator = require('./dpp/CachedStateRepositoryDecorator'); +const LoggedStateRepositoryDecorator = require('./dpp/LoggedStateRepositoryDecorator'); +const dataContractQueryHandlerFactory = require('./abci/handlers/query/dataContractQueryHandlerFactory'); +const identityQueryHandlerFactory = require('./abci/handlers/query/identityQueryHandlerFactory'); +const documentQueryHandlerFactory = require('./abci/handlers/query/documentQueryHandlerFactory'); +const identitiesByPublicKeyHashesQueryHandlerFactory = require('./abci/handlers/query/identitiesByPublicKeyHashesQueryHandlerFactory'); + +const getProofsQueryHandlerFactory = require('./abci/handlers/query/getProofsQueryHandlerFactory'); + +const verifyChainLockQueryHandlerFactory = require('./abci/handlers/query/verifyChainLockQueryHandlerFactory'); + +const wrapInErrorHandlerFactory = require('./abci/errors/wrapInErrorHandlerFactory'); +const errorHandlerFactory = require('./errorHandlerFactory'); +const checkTxHandlerFactory = require('./abci/handlers/checkTxHandlerFactory'); +const commitHandlerFactory = require('./abci/handlers/commitHandlerFactory'); +const deliverTxHandlerFactory = require('./abci/handlers/deliverTxHandlerFactory'); +const initChainHandlerFactory = require('./abci/handlers/initChainHandlerFactory'); +const infoHandlerFactory = require('./abci/handlers/infoHandlerFactory'); +const beginBlockHandlerFactory = require('./abci/handlers/beginBlockHandlerFactory'); + +const endBlockHandlerFactory = require('./abci/handlers/endBlockHandlerFactory'); +const queryHandlerFactory = require('./abci/handlers/queryHandlerFactory'); +const waitForCoreSyncFactory = require('./core/waitForCoreSyncFactory'); +const waitForCoreChainLockSyncFactory = require('./core/waitForCoreChainLockSyncFactory'); +const updateSimplifiedMasternodeListFactory = require('./core/updateSimplifiedMasternodeListFactory'); +const waitForChainLockedHeightFactory = require('./core/waitForChainLockedHeightFactory'); +const SimplifiedMasternodeList = require('./core/SimplifiedMasternodeList'); + +const decodeChainLock = require('./core/decodeChainLock'); +const SpentAssetLockTransactionsRepository = require('./identity/SpentAssetLockTransactionsRepository'); +const enrichErrorWithConsensusErrorFactory = require('./abci/errors/enrichErrorWithConsensusLoggerFactory'); +const CreditsDistributionPool = require('./creditsDistributionPool/CreditsDistributionPool'); +const closeAbciServerFactory = require('./abci/closeAbciServerFactory'); +const getLatestFeatureFlagFactory = require('./featureFlag/getLatestFeatureFlagFactory'); +const getFeatureFlagForHeightFactory = require('./featureFlag/getFeatureFlagForHeightFactory'); +const ValidatorSet = require('./validator/ValidatorSet'); +const createValidatorSetUpdate = require('./abci/handlers/validator/createValidatorSetUpdate'); +const fetchQuorumMembersFactory = require('./core/fetchQuorumMembersFactory'); +const getRandomQuorum = require('./core/getRandomQuorum'); +const createQueryResponseFactory = require('./abci/handlers/query/response/createQueryResponseFactory'); +const BlockExecutionContextStackRepository = require('./blockExecution/BlockExecutionContextStackRepository'); +const rotateSignedStoreFactory = require('./storage/rotateSignedStoreFactory'); +const BlockExecutionContextStack = require('./blockExecution/BlockExecutionContextStack'); +const createInitialStateStructureFactory = require('./state/createInitialStateStructureFactory'); + +const registerSystemDataContractFactory = require('./state/registerSystemDataContractFactory'); +const registerTopLevelDomainFactory = require('./state/registerTopLevelDomainFactory'); +const synchronizeMasternodeIdentitiesFactory = require('./identity/masternode/synchronizeMasternodeIdentitiesFactory'); +const createMasternodeIdentityFactory = require('./identity/masternode/createMasternodeIdentityFactory'); +const handleNewMasternodeFactory = require('./identity/masternode/handleNewMasternodeFactory'); +const handleUpdatedPubKeyOperatorFactory = require('./identity/masternode/handleUpdatedPubKeyOperatorFactory'); +const registerSystemDataContractsFactory = require('./abci/handlers/state/registerSystemDataContractsFactory'); +const createRewardShareDocumentFactory = require('./identity/masternode/createRewardShareDocumentFactory'); +const handleRemovedMasternodeFactory = require('./identity/masternode/handleRemovedMasternodeFactory'); +const handleUpdatedScriptPayoutFactory = require('./identity/masternode/handleUpdatedScriptPayoutFactory'); +const getWithdrawPubKeyTypeFromPayoutScriptFactory = require('./identity/masternode/getWithdrawPubKeyTypeFromPayoutScriptFactory'); +const getPublicKeyFromPayoutScript = require('./identity/masternode/getPublicKeyFromPayoutScript'); + +const DocumentRepository = require('./document/DocumentRepository'); +const ExecutionTimer = require('./util/ExecutionTimer'); +const noopLoggerInstance = require('./util/noopLogger'); +const fetchTransactionFactory = require('./core/fetchTransactionFactory'); + +/** + * + * @param {Object} options + * @param {string} options.ABCI_HOST + * @param {string} options.ABCI_PORT + * @param {string} options.DB_PATH + * @param {string} options.GROVEDB_LATEST_FILE + * @param {string} options.DATA_CONTRACT_CACHE_SIZE + * @param {string} options.CORE_JSON_RPC_HOST + * @param {string} options.CORE_JSON_RPC_PORT + * @param {string} options.CORE_JSON_RPC_USERNAME + * @param {string} options.CORE_JSON_RPC_PASSWORD + * @param {string} options.CORE_ZMQ_HOST + * @param {string} options.CORE_ZMQ_PORT + * @param {string} options.CORE_ZMQ_CONNECTION_RETRIES + * @param {string} options.NETWORK + * @param {string} options.DPNS_MASTER_PUBLIC_KEY + * @param {string} options.DPNS_SECOND_PUBLIC_KEY + * @param {string} options.DASHPAY_MASTER_PUBLIC_KEY + * @param {string} options.DASHPAY_SECOND_PUBLIC_KEY + * @param {string} options.FEATURE_FLAGS_MASTER_PUBLIC_KEY + * @param {string} options.FEATURE_FLAGS_SECOND_PUBLIC_KEY + * @param {string} options.MASTERNODE_REWARD_SHARES_MASTER_PUBLIC_KEY + * @param {string} options.MASTERNODE_REWARD_SHARES_SECOND_PUBLIC_KEY + * @param {string} options.INITIAL_CORE_CHAINLOCKED_HEIGHT + * @param {string} options.VALIDATOR_SET_LLMQ_TYPE + * @param {string} options.TENDERDASH_P2P_PORT + * @param {string} options.LOG_STDOUT_LEVEL + * @param {string} options.LOG_PRETTY_FILE_LEVEL + * @param {string} options.LOG_PRETTY_FILE_PATH + * @param {string} options.LOG_JSON_FILE_LEVEL + * @param {string} options.LOG_JSON_FILE_PATH + * @param {string} options.LOG_STATE_REPOSITORY + * @param {string} options.NODE_ENV + * + * @return {AwilixContainer} + */ +function createDIContainer(options) { + if (!options.DPNS_MASTER_PUBLIC_KEY) { + throw new Error('DPNS_MASTER_PUBLIC_KEY must be set'); + } + if (!options.DPNS_SECOND_PUBLIC_KEY) { + throw new Error('DPNS_SECOND_PUBLIC_KEY must be set'); + } + + if (!options.DASHPAY_MASTER_PUBLIC_KEY) { + throw new Error('DASHPAY_MASTER_PUBLIC_KEY must be set'); + } + + if (!options.DASHPAY_SECOND_PUBLIC_KEY) { + throw new Error('DASHPAY_SECOND_PUBLIC_KEY must be set'); + } + + if (!options.FEATURE_FLAGS_MASTER_PUBLIC_KEY) { + throw new Error('FEATURE_FLAGS_MASTER_PUBLIC_KEY must be set'); + } + + if (!options.FEATURE_FLAGS_SECOND_PUBLIC_KEY) { + throw new Error('FEATURE_FLAGS_SECOND_PUBLIC_KEY must be set'); + } + + if (!options.MASTERNODE_REWARD_SHARES_MASTER_PUBLIC_KEY) { + throw new Error('MASTERNODE_REWARD_SHARES_MASTER_PUBLIC_KEY must be set'); + } + + if (!options.MASTERNODE_REWARD_SHARES_SECOND_PUBLIC_KEY) { + throw new Error('MASTERNODE_REWARD_SHARES_SECOND_PUBLIC_KEY must be set'); + } + + const container = createAwilixContainer({ + injectionMode: InjectionMode.CLASSIC, + }); + + /** + * Register itself (usually to solve recursive dependencies) + */ + container.register({ + container: asValue(container), + }); + + /** + * Register latest protocol version + * Define highest supported protocol version + */ + container.register({ + latestProtocolVersion: asValue(Long.fromInt(protocolVersion.latestVersion)), + }); + + /** + * Register environment variables + */ + container.register({ + abciHost: asValue(options.ABCI_HOST), + abciPort: asValue(options.ABCI_PORT), + + dbPath: asValue(options.DB_PATH), + + groveDBLatestFile: asValue(options.GROVEDB_LATEST_FILE), + dataContractCacheSize: asValue(options.DATA_CONTRACT_CACHE_SIZE), + + coreJsonRpcHost: asValue(options.CORE_JSON_RPC_HOST), + coreJsonRpcPort: asValue(options.CORE_JSON_RPC_PORT), + coreJsonRpcUsername: asValue(options.CORE_JSON_RPC_USERNAME), + coreJsonRpcPassword: asValue(options.CORE_JSON_RPC_PASSWORD), + coreZMQHost: asValue(options.CORE_ZMQ_HOST), + coreZMQPort: asValue(options.CORE_ZMQ_PORT), + coreZMQConnectionRetries: asValue( + parseInt(options.CORE_ZMQ_CONNECTION_RETRIES, 10), + ), + network: asValue(options.NETWORK), + logStdoutLevel: asValue(options.LOG_STDOUT_LEVEL), + logPrettyFileLevel: asValue(options.LOG_PRETTY_FILE_LEVEL), + logPrettyFilePath: asValue(options.LOG_PRETTY_FILE_PATH), + logJsonFileLevel: asValue(options.LOG_JSON_FILE_LEVEL), + logJsonFilePath: asValue(options.LOG_JSON_FILE_PATH), + logStateRepository: asValue(options.LOG_STATE_REPOSITORY === 'true'), + isProductionEnvironment: asValue(options.NODE_ENV === 'production'), + maxIdentitiesPerRequest: asValue(25), + smlMaxListsLimit: asValue(16), + initialCoreChainLockedHeight: asValue( + parseInt(options.INITIAL_CORE_CHAINLOCKED_HEIGHT, 10), + ), + validatorSetLLMQType: asValue( + parseInt(options.VALIDATOR_SET_LLMQ_TYPE, 10), + ), + masternodeRewardSharesContractId: asValue( + Identifier.from(masternodeRewardsSystemIds.contractId), + ), + masternodeRewardSharesOwnerId: asValue( + Identifier.from(masternodeRewardsSystemIds.ownerId), + ), + masternodeRewardSharesOwnerMasterPublicKey: asValue( + PublicKey.fromString( + options.MASTERNODE_REWARD_SHARES_MASTER_PUBLIC_KEY, + ), + ), + masternodeRewardSharesOwnerSecondPublicKey: asValue( + PublicKey.fromString( + options.MASTERNODE_REWARD_SHARES_SECOND_PUBLIC_KEY, + ), + ), + masternodeRewardSharesDocuments: asValue( + masternodeRewardsDocuments, + ), + featureFlagsContractId: asValue( + Identifier.from(featureFlagsSystemIds.contractId), + ), + featureFlagsOwnerId: asValue( + Identifier.from(featureFlagsSystemIds.ownerId), + ), + featureFlagsOwnerMasterPublicKey: asValue( + PublicKey.fromString( + options.FEATURE_FLAGS_MASTER_PUBLIC_KEY, + ), + ), + featureFlagsOwnerSecondPublicKey: asValue( + PublicKey.fromString( + options.FEATURE_FLAGS_SECOND_PUBLIC_KEY, + ), + ), + featureFlagsDocuments: asValue(featureFlagsDocuments), + dpnsContractId: asValue(Identifier.from(dpnsSystemIds.contractId)), + dpnsOwnerId: asValue(Identifier.from(dpnsSystemIds.ownerId)), + dpnsOwnerMasterPublicKey: asValue( + PublicKey.fromString( + options.DPNS_MASTER_PUBLIC_KEY, + ), + ), + dpnsOwnerSecondPublicKey: asValue( + PublicKey.fromString( + options.DPNS_SECOND_PUBLIC_KEY, + ), + ), + dpnsDocuments: asValue(dpnsDocuments), + dashpayContractId: asValue(Identifier.from(dashpaySystemIds.contractId)), + dashpayOwnerId: asValue(Identifier.from(dashpaySystemIds.ownerId)), + dashpayOwnerMasterPublicKey: asValue( + PublicKey.fromString( + options.DASHPAY_MASTER_PUBLIC_KEY, + ), + ), + dashpayOwnerSecondPublicKey: asValue( + PublicKey.fromString( + options.DASHPAY_SECOND_PUBLIC_KEY, + ), + ), + dashpayDocuments: asValue(dashpayDocuments), + tenderdashP2pPort: asValue(options.TENDERDASH_P2P_PORT), + }); + + /** + * Register global DPP options + */ + container.register({ + dppOptions: asValue({}), + }); + + /** + * Register Core related + */ + container.register({ + latestCoreChainLock: asValue(new LatestCoreChainLock()), + simplifiedMasternodeList: asClass(SimplifiedMasternodeList).proxy().singleton(), + decodeChainLock: asValue(decodeChainLock), + fetchQuorumMembers: asFunction(fetchQuorumMembersFactory), + getRandomQuorum: asValue(getRandomQuorum), + coreZMQClient: asFunction(( + coreZMQHost, + coreZMQPort, + coreZMQConnectionRetries, + ) => ( + new ZMQClient(coreZMQHost, coreZMQPort, { + maxRetryCount: coreZMQConnectionRetries, + }) + )).singleton(), + + coreRpcClient: asFunction(( + coreJsonRpcHost, + coreJsonRpcPort, + coreJsonRpcUsername, + coreJsonRpcPassword, + ) => ( + new RpcClient({ + protocol: 'http', + host: coreJsonRpcHost, + port: coreJsonRpcPort, + user: coreJsonRpcUsername, + pass: coreJsonRpcPassword, + }) + )).singleton(), + }); + + /** + * Register common services + */ + container.register({ + loggerPrettyfierOptions: asValue({ + translateTime: true, + }), + + logStdoutStream: asFunction((loggerPrettyfierOptions) => pinoMultistream.prettyStream({ + prettyPrint: loggerPrettyfierOptions, + })).singleton(), + + logPrettyFileStream: asFunction(( + logPrettyFilePath, + loggerPrettyfierOptions, + ) => pinoMultistream.prettyStream({ + prettyPrint: loggerPrettyfierOptions, + dest: fs.createWriteStream(logPrettyFilePath, { flags: 'a' }), + })).singleton(), + + logJsonFileStream: asFunction((logJsonFilePath) => fs.createWriteStream(logJsonFilePath, { flags: 'a' })) + .disposer(async (stream) => new Promise((resolve) => stream.end(resolve))).singleton(), + + loggerStreams: asFunction(( + logStdoutLevel, + logStdoutStream, + logPrettyFileLevel, + logPrettyFileStream, + logJsonFileLevel, + logJsonFileStream, + ) => [ + { + level: logStdoutLevel, + stream: logStdoutStream, + }, + { + level: logPrettyFileLevel, + stream: logPrettyFileStream, + }, + { + level: logJsonFileLevel, + stream: logJsonFileStream, + }, + ]), + + logger: asFunction( + (loggerStreams) => pino({ + level: 'trace', + }, pinoMultistream.multistream(loggerStreams)) + .child({ driveVersion: packageJSON.version }), + ).singleton(), + + noopLogger: asValue(noopLoggerInstance), + + sanitizeUrl: asValue(sanitizeUrl), + + executionTimer: asClass(ExecutionTimer).singleton(), + }); + + /** + * RS Drive and GroveDB + */ + + container.register({ + rsDrive: asFunction((groveDBLatestFile) => new RSDrive(groveDBLatestFile)) + // TODO: With signed state rotation we need to dispose each groveDB store. + .disposer(async (rsDrive) => { + // Flush data on disk + await rsDrive.getGroveDB().flush(); + + await rsDrive.close(); + + if (process.env.NODE_ENV === 'test') { + fs.rmSync(options.GROVEDB_LATEST_FILE, { recursive: true }); + } + }).singleton(), + groveDB: asFunction((rsDrive) => rsDrive.getGroveDB()).singleton(), + + groveDBStore: asFunction((rsDrive) => new GroveDBStore(rsDrive)).singleton(), + + signedGroveDBStore: asFunction((rsDrive) => new GroveDBStore(rsDrive)).singleton(), + + rotateSignedStore: asFunction(rotateSignedStoreFactory).singleton(), + }); + + /** + * Register Identity + */ + container.register({ + identityRepository: asClass(IdentityStoreRepository).singleton(), + + signedIdentityRepository: asFunction(( + signedGroveDBStore, + decodeProtocolEntity, + ) => (new IdentityStoreRepository(signedGroveDBStore, decodeProtocolEntity))).singleton(), + + publicKeyToIdentitiesRepository: asClass(PublicKeyToIdentitiesStoreRepository).singleton(), + + signedPublicKeyToIdentitiesRepository: asFunction(( + signedGroveDBStore, + ) => ( + new PublicKeyToIdentitiesStoreRepository(signedGroveDBStore) + )).singleton(), + + synchronizeMasternodeIdentities: asFunction(synchronizeMasternodeIdentitiesFactory).singleton(), + + createMasternodeIdentity: asFunction(createMasternodeIdentityFactory).singleton(), + + createRewardShareDocument: asFunction(createRewardShareDocumentFactory).singleton(), + + handleNewMasternode: asFunction(handleNewMasternodeFactory).singleton(), + + handleUpdatedPubKeyOperator: asFunction(handleUpdatedPubKeyOperatorFactory).singleton(), + + handleRemovedMasternode: asFunction(handleRemovedMasternodeFactory).singleton(), + + handleUpdatedScriptPayout: asFunction(handleUpdatedScriptPayoutFactory).singleton(), + + getWithdrawPubKeyTypeFromPayoutScript: asFunction(getWithdrawPubKeyTypeFromPayoutScriptFactory) + .singleton(), + + getPublicKeyFromPayoutScript: asValue(getPublicKeyFromPayoutScript), + }); + + /** + * Register asset lock transactions + */ + container.register({ + spentAssetLockTransactionsRepository: asClass(SpentAssetLockTransactionsRepository).singleton(), + + signedSpentAssetLockTransactionsRepository: asFunction(( + signedGroveDBStore, + ) => ( + new SpentAssetLockTransactionsRepository(signedGroveDBStore) + )).singleton(), + }); + + /** + * Register Data Contract + */ + container.register({ + dataContractRepository: asFunction(( + groveDBStore, + decodeProtocolEntity, + ) => new DataContractStoreRepository(groveDBStore, decodeProtocolEntity)).singleton(), + + signedDataContractRepository: asFunction(( + signedGroveDBStore, + decodeProtocolEntity, + ) => (new DataContractStoreRepository(signedGroveDBStore, decodeProtocolEntity))).singleton(), + + dataContractCache: asFunction((dataContractCacheSize) => ( + new LRUCache(dataContractCacheSize) + )).singleton(), + + signedDataContractCache: asFunction((dataContractCacheSize) => ( + new LRUCache(dataContractCacheSize) + )).singleton(), + }); + + /** + * Register Document + */ + container.register({ + documentRepository: asFunction(( + groveDBStore, + ) => new DocumentRepository(groveDBStore)).singleton(), + + signedDocumentRepository: asFunction(( + signedGroveDBStore, + ) => (new DocumentRepository( + signedGroveDBStore, + ))).singleton(), + + fetchDocuments: asFunction(fetchDocumentsFactory).singleton(), + fetchDataContract: asFunction(fetchDataContractFactory).singleton(), + proveDocuments: asFunction(proveDocumentsFactory).singleton(), + fetchSignedDataContract: asFunction(( + signedDataContractRepository, + signedDataContractCache, + ) => ( + fetchDataContractFactory( + signedDataContractRepository, + signedDataContractCache, + ) + )).singleton(), + fetchSignedDocuments: asFunction(( + signedDocumentRepository, + fetchSignedDataContract, + ) => ( + fetchDocumentsFactory( + signedDocumentRepository, + fetchSignedDataContract, + ) + )).singleton(), + proveSignedDocuments: asFunction(( + signedDocumentRepository, + ) => ( + proveDocumentsFactory( + signedDocumentRepository, + ) + )).singleton(), + }); + + /** + * Register credits distribution pool + */ + container.register({ + creditsDistributionPoolRepository: asClass(CreditsDistributionPoolRepository) + .singleton(), + + signedCreditsDistributionPoolRepository: asFunction(( + signedGroveDBStore, + ) => (new CreditsDistributionPoolRepository(signedGroveDBStore))).singleton(), + + creditsDistributionPool: asValue(new CreditsDistributionPool()), + }); + + /** + * Register block execution context + */ + container.register({ + blockExecutionContext: asClass(BlockExecutionContext).singleton(), + blockExecutionContextStack: asClass(BlockExecutionContextStack).singleton(), + blockExecutionContextStackRepository: asClass(BlockExecutionContextStackRepository).singleton(), + }); + + /** + * Register DPP + */ + container.register({ + decodeProtocolEntity: asFunction(decodeProtocolEntityFactory), + + stateRepository: asFunction(( + identityRepository, + publicKeyToIdentitiesRepository, + dataContractRepository, + fetchDocuments, + documentRepository, + spentAssetLockTransactionsRepository, + coreRpcClient, + dataContractCache, + blockExecutionContext, + simplifiedMasternodeList, + ) => { + const stateRepository = new DriveStateRepository( + identityRepository, + publicKeyToIdentitiesRepository, + dataContractRepository, + fetchDocuments, + documentRepository, + spentAssetLockTransactionsRepository, + coreRpcClient, + blockExecutionContext, + simplifiedMasternodeList, + ); + + return new CachedStateRepositoryDecorator( + stateRepository, + dataContractCache, + ); + }).singleton(), + + transactionalStateRepository: asFunction(( + identityRepository, + publicKeyToIdentitiesRepository, + dataContractRepository, + fetchDocuments, + documentRepository, + spentAssetLockTransactionsRepository, + coreRpcClient, + dataContractCache, + blockExecutionContext, + simplifiedMasternodeList, + logStateRepository, + ) => { + const stateRepository = new DriveStateRepository( + identityRepository, + publicKeyToIdentitiesRepository, + dataContractRepository, + fetchDocuments, + documentRepository, + spentAssetLockTransactionsRepository, + coreRpcClient, + blockExecutionContext, + simplifiedMasternodeList, + { + useTransaction: true, + }, + ); + + const cachedRepository = new CachedStateRepositoryDecorator( + stateRepository, dataContractCache, + ); + + if (!logStateRepository) { + return cachedRepository; + } + + return new LoggedStateRepositoryDecorator( + cachedRepository, + blockExecutionContext, + ); + }).singleton(), + + unserializeStateTransition: asFunction(( + dpp, + noopLogger, + ) => unserializeStateTransitionFactory(dpp, noopLogger)).singleton(), + + transactionalUnserializeStateTransition: asFunction(( + transactionalDpp, + noopLogger, + ) => unserializeStateTransitionFactory(transactionalDpp, noopLogger)).singleton(), + + dpp: asFunction((stateRepository, dppOptions) => ( + new DashPlatformProtocol({ + ...dppOptions, + stateRepository, + }) + )).singleton(), + + transactionalDpp: asFunction((transactionalStateRepository, dppOptions) => ( + new DashPlatformProtocol({ + ...dppOptions, + stateRepository: transactionalStateRepository, + }) + )).singleton(), + }); + + /** + * Register validator quorums + */ + container.register({ + validatorSet: asClass(ValidatorSet), + }); + + /** + * Register feature flags stuff + */ + container.register({ + getLatestFeatureFlag: asFunction(getLatestFeatureFlagFactory), + getFeatureFlagForHeight: asFunction(getFeatureFlagForHeightFactory), + }); + + /** + * Register Core stuff + */ + container.register({ + waitForCoreSync: asFunction(waitForCoreSyncFactory).singleton(), + + updateSimplifiedMasternodeList: asFunction(updateSimplifiedMasternodeListFactory).singleton(), + + waitForChainLockedHeight: asFunction(waitForChainLockedHeightFactory).singleton(), + + waitForCoreChainLockSync: asFunction(waitForCoreChainLockSyncFactory).singleton(), + + fetchTransaction: asFunction(fetchTransactionFactory).singleton(), + }); + + /** + * State + */ + container.register({ + createInitialStateStructure: asFunction(createInitialStateStructureFactory).singleton(), + registerSystemDataContract: asFunction(registerSystemDataContractFactory).singleton(), + registerSystemDataContracts: asFunction(registerSystemDataContractsFactory).singleton(), + registerTopLevelDomain: asFunction(registerTopLevelDomainFactory).singleton(), + dashDomainDocumentId: asValue( + Identifier.from('FXyN2NZAdRFADgBQfb1XM1Qq7pWoEcgSWj1GaiQJqcrS'), + ), + dashPreorderSalt: asValue( + Buffer.from('e0b508c5a36825a206693a1f414aa13edbecf43c41e3c799ea9e737b4f9aa226', 'hex'), + ), + }); + + /** + * Register ABCI handlers + */ + container.register({ + createQueryResponse: asFunction(createQueryResponseFactory).singleton(), + createValidatorSetUpdate: asValue(createValidatorSetUpdate), + identityQueryHandler: asFunction(identityQueryHandlerFactory).singleton(), + dataContractQueryHandler: asFunction(dataContractQueryHandlerFactory).singleton(), + documentQueryHandler: asFunction(documentQueryHandlerFactory).singleton(), + getProofsQueryHandler: asFunction(getProofsQueryHandlerFactory).singleton(), + identitiesByPublicKeyHashesQueryHandler: + asFunction(identitiesByPublicKeyHashesQueryHandlerFactory).singleton(), + verifyChainLockQueryHandler: asFunction(verifyChainLockQueryHandlerFactory).singleton(), + + queryHandlerRouter: asFunction(( + identityQueryHandler, + dataContractQueryHandler, + documentQueryHandler, + identitiesByPublicKeyHashesQueryHandler, + verifyChainLockQueryHandler, + getProofsQueryHandler, + ) => { + const router = findMyWay({ + ignoreTrailingSlash: true, + }); + + router.on('GET', '/identities', identityQueryHandler); + router.on('GET', '/dataContracts', dataContractQueryHandler); + router.on('GET', '/dataContracts/documents', documentQueryHandler); + router.on('GET', '/proofs', getProofsQueryHandler); + router.on('GET', '/identities/by-public-key-hash', identitiesByPublicKeyHashesQueryHandler); + router.on('GET', '/verify-chainlock', verifyChainLockQueryHandler, { rawData: true }); + + return router; + }).singleton(), + + infoHandler: asFunction(infoHandlerFactory).singleton(), + checkTxHandler: asFunction(checkTxHandlerFactory).singleton(), + beginBlockHandler: asFunction(beginBlockHandlerFactory).singleton(), + deliverTxHandler: asFunction(deliverTxHandlerFactory).singleton(), + initChainHandler: asFunction(initChainHandlerFactory).singleton(), + endBlockHandler: asFunction(endBlockHandlerFactory).singleton(), + commitHandler: asFunction(commitHandlerFactory).singleton(), + queryHandler: asFunction(queryHandlerFactory).singleton(), + + wrapInErrorHandler: asFunction(wrapInErrorHandlerFactory).singleton(), + enrichErrorWithConsensusError: asFunction(enrichErrorWithConsensusErrorFactory).singleton(), + errorHandler: asFunction(errorHandlerFactory).singleton(), + + abciHandlers: asFunction(( + infoHandler, + checkTxHandler, + beginBlockHandler, + deliverTxHandler, + initChainHandler, + endBlockHandler, + commitHandler, + wrapInErrorHandler, + enrichErrorWithConsensusError, + queryHandler, + ) => ({ + info: infoHandler, + checkTx: wrapInErrorHandler(checkTxHandler, { respondWithInternalError: true }), + beginBlock: enrichErrorWithConsensusError(beginBlockHandler), + deliverTx: wrapInErrorHandler(enrichErrorWithConsensusError(deliverTxHandler)), + initChain: initChainHandler, + endBlock: enrichErrorWithConsensusError(endBlockHandler), + commit: enrichErrorWithConsensusError(commitHandler), + query: wrapInErrorHandler(queryHandler, { respondWithInternalError: true }), + })).singleton(), + + closeAbciServer: asFunction(closeAbciServerFactory).singleton(), + + abciServer: asFunction((abciHandlers) => createABCIServer(abciHandlers)) + .singleton(), + }); + + return container; +} + +module.exports = createDIContainer; diff --git a/packages/js-drive/lib/creditsDistributionPool/CreditsDistributionPool.js b/packages/js-drive/lib/creditsDistributionPool/CreditsDistributionPool.js new file mode 100644 index 00000000000..513c017ac20 --- /dev/null +++ b/packages/js-drive/lib/creditsDistributionPool/CreditsDistributionPool.js @@ -0,0 +1,73 @@ +class CreditsDistributionPool { + /** + * + * @param {number} [amount] + */ + constructor( + amount = 0, + ) { + this.amount = amount; + } + + /** + * Set credits distribution pool + * + * @param {number} amount + * @return {CreditsDistributionPool} + */ + setAmount(amount) { + this.amount = amount; + + return this; + } + + /** + * Increment credits distribution pool + * + * @param {number} amount + * @return {CreditsDistributionPool} + */ + incrementAmount(amount) { + this.amount += amount; + + return this; + } + + /** + * Get credits distribution pool + * + * @return {number} + */ + getAmount() { + return this.amount; + } + + /** + * Populate with data + * + * @param {{ + * amount: number, + * }} object + * @return {CreditsDistributionPool} + */ + populate(object) { + this.amount = object.amount; + + return this; + } + + /** + * Get plain JS object + * + * @return {{ + * amount: number, + * }} + */ + toJSON() { + return { + amount: this.getAmount(), + }; + } +} + +module.exports = CreditsDistributionPool; diff --git a/packages/js-drive/lib/creditsDistributionPool/CreditsDistributionPoolRepository.js b/packages/js-drive/lib/creditsDistributionPool/CreditsDistributionPoolRepository.js new file mode 100644 index 00000000000..bb4d0a5cb94 --- /dev/null +++ b/packages/js-drive/lib/creditsDistributionPool/CreditsDistributionPoolRepository.js @@ -0,0 +1,75 @@ +const cbor = require('cbor'); + +const CreditsDistributionPool = require('./CreditsDistributionPool'); +const StorageResult = require('../storage/StorageResult'); + +class CreditsDistributionPoolRepository { + /** + * + * @param {GroveDBStore} groveDBStore + */ + constructor(groveDBStore) { + this.storage = groveDBStore; + } + + /** + * Store Credits Distribution Pool + * + * @param {CreditsDistributionPool} creditsDistributionPool + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * + * @return {Promise>} + */ + async store(creditsDistributionPool, options = {}) { + const encodedCreditsDistributionPool = cbor.encodeCanonical( + creditsDistributionPool.toJSON(), + ); + + const result = await this.storage.put( + CreditsDistributionPoolRepository.PATH, + CreditsDistributionPoolRepository.KEY, + encodedCreditsDistributionPool, + options, + ); + + result.setValue(undefined); + + return result; + } + + /** + * Fetch Credits Distribution Pool + * + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * + * @return {Promise>} + */ + async fetch(options = {}) { + const result = await this.storage.get( + CreditsDistributionPoolRepository.PATH, + CreditsDistributionPoolRepository.KEY, + options, + ); + + if (result.isEmpty()) { + return new StorageResult( + new CreditsDistributionPool(), + result.getOperations(), + ); + } + + const { amount } = cbor.decode(result.getValue()); + + return new StorageResult( + new CreditsDistributionPool(amount), + result.getOperations(), + ); + } +} + +CreditsDistributionPoolRepository.PATH = [Buffer.from([3])]; +CreditsDistributionPoolRepository.KEY = Buffer.from([1]); + +module.exports = CreditsDistributionPoolRepository; diff --git a/packages/js-drive/lib/dataContract/DataContractCacheItem.js b/packages/js-drive/lib/dataContract/DataContractCacheItem.js new file mode 100644 index 00000000000..99cea496f8b --- /dev/null +++ b/packages/js-drive/lib/dataContract/DataContractCacheItem.js @@ -0,0 +1,44 @@ +class DataContractCacheItem { + /** + * @type {DataContract} + */ + #dataContract; + + /** + * @type {AbstractOperation[]} + */ + #operations; + + /** + * + * @param {DataContract} dataContract + * @param {AbstractOperation[]} operations + */ + constructor(dataContract, operations) { + this.#dataContract = dataContract; + this.#operations = operations; + } + + /** + * @return {DataContract} + */ + getDataContract() { + return this.#dataContract; + } + + /** + * @return {AbstractOperation[]} + */ + getOperations() { + return this.#operations; + } + + /** + * @return {string} + */ + getKey() { + return this.#dataContract.getId().toString(); + } +} + +module.exports = DataContractCacheItem; diff --git a/packages/js-drive/lib/dataContract/DataContractStoreRepository.js b/packages/js-drive/lib/dataContract/DataContractStoreRepository.js new file mode 100644 index 00000000000..910c14a197a --- /dev/null +++ b/packages/js-drive/lib/dataContract/DataContractStoreRepository.js @@ -0,0 +1,157 @@ +const DataContract = require('@dashevo/dpp/lib/dataContract/DataContract'); +const { createHash } = require('crypto'); + +const PreCalculatedOperation = require('@dashevo/dpp/lib/stateTransition/fee/operations/PreCalculatedOperation'); +const StorageResult = require('../storage/StorageResult'); + +class DataContractStoreRepository { + /** + * + * @param {GroveDBStore} groveDBStore + * @param {decodeProtocolEntity} decodeProtocolEntity + * @param {BaseLogger} [logger] + */ + constructor(groveDBStore, decodeProtocolEntity, logger = undefined) { + this.storage = groveDBStore; + this.decodeProtocolEntity = decodeProtocolEntity; + this.logger = logger; + } + + /** + * Store Data Contract into database + * + * @param {DataContract} dataContract + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async store(dataContract, options = {}) { + try { + const [storageCost, processingCost] = await this.storage.getDrive().applyContract( + dataContract, + new Date('2022-03-17T15:08:26.132Z'), + Boolean(options.useTransaction), + Boolean(options.dryRun), // TODO rs-drive doesn't support this + ); + + return new StorageResult( + undefined, + [ + new PreCalculatedOperation( + storageCost, + processingCost, + ), + ], + ); + } finally { + if (this.logger) { + this.logger.trace({ + dataContract: dataContract.toBuffer().toString('hex'), + dataContractHash: createHash('sha256') + .update( + dataContract.toBuffer(), + ).digest('hex'), + useTransaction: Boolean(options.useTransaction), + appHash: (await this.storage.getRootHash(options)).toString('hex'), + }, 'applyContract'); + } + } + } + + /** + * Fetch Data Contract by ID from database + * + * @param {Identifier} id + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async fetch(id, options = {}) { + const result = await this.storage.get( + DataContractStoreRepository.TREE_PATH.concat([id.toBuffer()]), + DataContractStoreRepository.DATA_CONTRACT_KEY, + { + ...options, + predictedValueSize: 16 * 1024, // Max size of State Transition + }, + ); + + if (result.isNull()) { + return result; + } + + const [protocolVersion, rawDataContract] = this.decodeProtocolEntity( + result.getValue(), + ); + + rawDataContract.protocolVersion = protocolVersion; + + return new StorageResult( + new DataContract(rawDataContract), + result.getOperations(), + ); + } + + /** + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.skipIfExists] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async createTree(options = {}) { + return this.storage.createTree( + [], + DataContractStoreRepository.TREE_PATH[0], + options, + ); + } + + /** + * Prove Data Contract by ID from database + * + * @param {Identifier} id + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @return {Promise>} + * */ + async prove(id, options) { + return this.proveMany([id], options); + } + + /** + * Prove Data Contract by IDs from database + * + * @param {Identifier[]} ids + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @return {Promise>} + * */ + async proveMany(ids, options) { + const items = ids.map((id) => ({ + type: 'key', + key: id.toBuffer(), + })); + + return this.storage.proveQuery({ + path: DataContractStoreRepository.TREE_PATH, + query: { + query: { + items, + subqueryKey: DataContractStoreRepository.DATA_CONTRACT_KEY, + }, + }, + }, options); + } +} + +DataContractStoreRepository.TREE_PATH = [Buffer.from([1])]; +DataContractStoreRepository.DATA_CONTRACT_KEY = Buffer.from([0]); +DataContractStoreRepository.DOCUMENTS_TREE_KEY = Buffer.from([0]); + +module.exports = DataContractStoreRepository; diff --git a/packages/js-drive/lib/document/DocumentRepository.js b/packages/js-drive/lib/document/DocumentRepository.js new file mode 100644 index 00000000000..574c508ec71 --- /dev/null +++ b/packages/js-drive/lib/document/DocumentRepository.js @@ -0,0 +1,348 @@ +const { createHash } = require('crypto'); + +const lodashCloneDeep = require('lodash.clonedeep'); + +const PreCalculatedOperation = require('@dashevo/dpp/lib/stateTransition/fee/operations/PreCalculatedOperation'); +const createDocumentTypeTreePath = require('./groveDB/createDocumentTreePath'); +const InvalidQueryError = require('./errors/InvalidQueryError'); +const StorageResult = require('../storage/StorageResult'); +const DataContractStoreRepository = require('../dataContract/DataContractStoreRepository'); + +class DocumentRepository { + /** + * + * @param {GroveDBStore} groveDBStore + * @param {BaseLogger} [logger] + */ + constructor( + groveDBStore, + logger = undefined, + ) { + this.storage = groveDBStore; + this.logger = logger; + } + + /** + * Create document + * + * @param {Document} document + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async create(document, options = {}) { + let processingCost; + let storageCost; + + try { + ([storageCost, processingCost] = await this.storage.getDrive() + .createDocument( + document, + new Date('2022-03-17T15:08:26.132Z'), + Boolean(options.useTransaction), + Boolean(options.dryRun), + )); + } finally { + if (this.logger) { + this.logger.info({ + document: document.toBuffer().toString('hex'), + documentHash: createHash('sha256') + .update( + document.toBuffer(), + ).digest('hex'), + useTransaction: Boolean(options.useTransaction), + dryRun: Boolean(options.dryRun), + appHash: (await this.storage.getRootHash(options)).toString('hex'), + }, 'createDocument'); + } + } + + return new StorageResult( + undefined, + [ + new PreCalculatedOperation(storageCost, processingCost), + ], + ); + } + + /** + * Update document + * + * @param {Document} document + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async update(document, options = {}) { + let processingCost; + let storageCost; + + try { + ([storageCost, processingCost] = await this.storage.getDrive() + .updateDocument( + document, + new Date('2022-03-17T15:08:26.132Z'), + Boolean(options.useTransaction), + Boolean(options.dryRun), + )); + } finally { + if (this.logger) { + this.logger.info({ + document: document.toBuffer().toString('hex'), + documentHash: createHash('sha256') + .update( + document.toBuffer(), + ).digest('hex'), + useTransaction: Boolean(options.useTransaction), + dryRun: Boolean(options.dryRun), + appHash: (await this.storage.getRootHash(options)).toString('hex'), + }, 'updateDocument'); + } + } + + return new StorageResult( + undefined, + [ + new PreCalculatedOperation(storageCost, processingCost), + ], + ); + } + + /** + * @param {Document} document + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async isExist(document, options = { }) { + const documentTypeTreePath = createDocumentTypeTreePath( + document.getDataContract(), + document.getType(), + ); + + const documentTreePath = documentTypeTreePath.concat( + [Buffer.from([0])], + ); + + const result = await this.storage.get( + documentTreePath, + document.getId().toBuffer(), + { + useTransaction: Boolean(options.useTransaction), + dryRun: Boolean(options.dryRun), + }, + ); + + return new StorageResult( + Boolean(result.getValue()), + result.getOperations(), + ); + } + + /** + * Find documents with query + * + * @param {DataContract} dataContract + * @param {string} documentType + * @param {Object} [options] + * @param {Array} [options.where] + * @param {number} [options.limit] + * @param {Buffer} [options.startAt] + * @param {Buffer} [options.startAfter] + * @param {Array} [options.orderBy] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * + * @throws InvalidQueryError + * + * @returns {Promise>} + */ + async find(dataContract, documentType, options = {}) { + const query = lodashCloneDeep(options); + let useTransaction = false; + + if (typeof query === 'object' && !Array.isArray(query) && query !== null) { + ({ useTransaction } = query); + delete query.useTransaction; + delete query.dryRun; + + // Remove undefined options before we pass them to RS Drive + Object.keys(query) + .forEach((queryOption) => { + if (query[queryOption] === undefined) { + // eslint-disable-next-line no-param-reassign + delete query[queryOption]; + } + }); + } + + try { + const [documents, , processingCost] = await this.storage.getDrive() + .queryDocuments( + dataContract, + documentType, + query, + useTransaction, + ); + + return new StorageResult( + documents, + [ + new PreCalculatedOperation(0, processingCost), + ], + ); + } catch (e) { + if (e.message.startsWith('query: ')) { + throw new InvalidQueryError(e.message.substring(7, e.message.length)); + } + + if (e.message.startsWith('structure: ')) { + throw new InvalidQueryError(e.message.substring(11, e.message.length)); + } + + if (e.message.startsWith('contract: ')) { + throw new InvalidQueryError(e.message.substring(10, e.message.length)); + } + + throw e; + } + } + + /** + * @param {DataContract} dataContract + * @param {string} documentType + * @param {Identifier} id + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * @return {Promise>} + */ + async delete(dataContract, documentType, id, options = { }) { + try { + const [storageCost, processingCost] = await this.storage.getDrive() + .deleteDocument( + dataContract, + documentType, + id, + Boolean(options.useTransaction), + Boolean(options.dryRun), + ); + + return new StorageResult( + undefined, + [ + new PreCalculatedOperation(storageCost, processingCost), + ], + ); + } finally { + if (this.logger) { + this.logger.info({ + dataContractId: dataContract.getId().toString(), + documentType, + id: id.toString(), + useTransaction: Boolean(options.useTransaction), + appHash: (await this.storage.getRootHash(options)).toString('hex'), + }, 'deleteDocument'); + } + } + } + + /** + * @param {DataContract} dataContract + * @param {string} documentType + * @param {Object} options + * @param {boolean} [options.useTransaction=false] + * @return {Promise} + */ + async prove(dataContract, documentType, options = {}) { + const query = lodashCloneDeep(options); + let useTransaction = false; + + if (typeof query === 'object' && !Array.isArray(query) && query !== null) { + ({ useTransaction } = query); + delete query.useTransaction; + delete query.dryRun; + + // Remove undefined options before we pass them to RS Drive + Object.keys(query) + .forEach((queryOption) => { + if (query[queryOption] === undefined) { + // eslint-disable-next-line no-param-reassign + delete query[queryOption]; + } + }); + } + + try { + const prove = await this.storage.getDrive() + .proveQueryDocuments( + dataContract, + documentType, + query, + useTransaction, + ); + + return new StorageResult( + prove, + [ + new PreCalculatedOperation(0, 0), + ], + ); + } catch (e) { + if (e.message.startsWith('query: ')) { + throw new InvalidQueryError(e.message.substring(7, e.message.length)); + } + + if (e.message.startsWith('structure: ')) { + throw new InvalidQueryError(e.message.substring(11, e.message.length)); + } + + if (e.message.startsWith('contract: ')) { + throw new InvalidQueryError(e.message.substring(10, e.message.length)); + } + + throw e; + } + } + + /** + * Prove documents from different contracts + * + * @param {{ dataContractId: Buffer, documentId: Buffer, type: string }[]} documents + * @return {Promise>} + */ + async proveManyDocumentsFromDifferentContracts(documents) { + const queries = documents.map(({ dataContractId, documentId, type }) => { + const dataContractsDocumentsPath = [ + dataContractId, + Buffer.from([1]), + Buffer.from(type), + Buffer.from([0]), + ]; + + return { + path: DataContractStoreRepository.TREE_PATH.concat(dataContractsDocumentsPath), + query: { + query: { + items: [ + { + type: 'key', + key: documentId, + }, + ], + }, + }, + }; + }); + + return this.storage.proveQueryMany(queries); + } +} + +module.exports = DocumentRepository; diff --git a/packages/js-drive/lib/document/errors/InvalidQueryError.js b/packages/js-drive/lib/document/errors/InvalidQueryError.js new file mode 100644 index 00000000000..899eae1a1f6 --- /dev/null +++ b/packages/js-drive/lib/document/errors/InvalidQueryError.js @@ -0,0 +1,7 @@ +const DriveError = require('../../errors/DriveError'); + +class InvalidQueryError extends DriveError { + +} + +module.exports = InvalidQueryError; diff --git a/packages/js-drive/lib/document/fetchDataContractFactory.js b/packages/js-drive/lib/document/fetchDataContractFactory.js new file mode 100644 index 00000000000..4c9d5a30cf5 --- /dev/null +++ b/packages/js-drive/lib/document/fetchDataContractFactory.js @@ -0,0 +1,71 @@ +const IdentifierError = require('@dashevo/dpp/lib/identifier/errors/IdentifierError'); +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); + +const InvalidQueryError = require('./errors/InvalidQueryError'); +const StorageResult = require('../storage/StorageResult'); +const DataContractCacheItem = require('../dataContract/DataContractCacheItem'); + +/** + * @param {DataContractStoreRepository} dataContractRepository + * @param {LRUCache} dataContractCache + * @returns {fetchDocuments} + */ +function fetchDataContractFactory( + dataContractRepository, + dataContractCache, +) { + /** + * Fetch Data Contract by Contract ID and type + * + * @typedef {Promise} fetchDataContract + * @param {Buffer|Identifier} contractId + * @returns {Promise>} + */ + async function fetchDataContract(contractId) { + let contractIdIdentifier; + try { + contractIdIdentifier = new Identifier(contractId); + } catch (e) { + if (e instanceof IdentifierError) { + throw new InvalidQueryError(`invalid data contract ID: ${e.message}`); + } + + throw e; + } + + const contractIdString = contractIdIdentifier.toString(); + + /** + * @type {DataContractCacheItem} + */ + let cacheItem = dataContractCache.get(contractIdString); + + let dataContractResult; + + if (cacheItem) { + dataContractResult = new StorageResult( + cacheItem.getDataContract(), + cacheItem.getOperations(), + ); + } else { + dataContractResult = await dataContractRepository.fetch(contractIdIdentifier); + + if (dataContractResult.isNull()) { + throw new InvalidQueryError(`data contract ${contractIdIdentifier} not found`); + } + + cacheItem = new DataContractCacheItem( + dataContractResult.getValue(), + dataContractResult.getOperations(), + ); + + dataContractCache.set(contractIdString, cacheItem); + } + + return dataContractResult; + } + + return fetchDataContract; +} + +module.exports = fetchDataContractFactory; diff --git a/packages/js-drive/lib/document/fetchDocumentsFactory.js b/packages/js-drive/lib/document/fetchDocumentsFactory.js new file mode 100644 index 00000000000..31f4cda35d3 --- /dev/null +++ b/packages/js-drive/lib/document/fetchDocumentsFactory.js @@ -0,0 +1,46 @@ +const InvalidQueryError = require('./errors/InvalidQueryError'); + +/** + * @param {DocumentRepository} documentRepository + * @param {fetchDataContract} fetchDataContract + * @returns {fetchDocuments} + */ +function fetchDocumentsFactory( + documentRepository, + fetchDataContract, +) { + /** + * Fetch original Documents by Contract ID and type + * + * @typedef {Promise} fetchDocuments + * @param {Buffer|Identifier} dataContractId + * @param {string} type + * @param {Object} [options] options + * @param {boolean} [options.useTransaction=false] + * @returns {Promise} + */ + async function fetchDocuments(dataContractId, type, options) { + const dataContractResult = await fetchDataContract(dataContractId); + + const dataContract = dataContractResult.getValue(); + const operations = dataContractResult.getOperations(); + + if (!dataContract.isDocumentDefined(type)) { + throw new InvalidQueryError(`document type ${type} is not defined in the data contract`); + } + + const result = await documentRepository.find( + dataContract, + type, + options, + ); + + result.addOperation(...operations); + + return result; + } + + return fetchDocuments; +} + +module.exports = fetchDocumentsFactory; diff --git a/packages/js-drive/lib/document/groveDB/createDocumentTreePath.js b/packages/js-drive/lib/document/groveDB/createDocumentTreePath.js new file mode 100644 index 00000000000..b2dc1625d57 --- /dev/null +++ b/packages/js-drive/lib/document/groveDB/createDocumentTreePath.js @@ -0,0 +1,15 @@ +const DataContractStoreRepository = require('../../dataContract/DataContractStoreRepository'); +/** + * @param {DataContract} dataContract + * @param {string} documentType + * @return {Buffer[]} + */ +function createDocumentTypeTreePath(dataContract, documentType) { + return DataContractStoreRepository.TREE_PATH.concat([ + dataContract.getId().toBuffer(), + Buffer.from([1]), + Buffer.from(documentType), + ]); +} + +module.exports = createDocumentTypeTreePath; diff --git a/packages/js-drive/lib/document/proveDocumentsFactory.js b/packages/js-drive/lib/document/proveDocumentsFactory.js new file mode 100644 index 00000000000..3da870652a0 --- /dev/null +++ b/packages/js-drive/lib/document/proveDocumentsFactory.js @@ -0,0 +1,39 @@ +/** + * @param {DocumentRepository} documentRepository + * @param {fetchDataContract} fetchDataContract + * @returns {fetchDocuments} + */ +function proveDocumentsFactory( + documentRepository, + fetchDataContract, +) { + /** + * + * @typedef {Promise} proveDocuments + * @param {Buffer|Identifier} dataContractId + * @param {string} type + * @param {Object} [options] options + * @param {boolean} [options.useTransaction=false] + * @returns {Promise} + */ + async function proveDocuments(dataContractId, type, options) { + const dataContractResult = await fetchDataContract(dataContractId); + + const dataContract = dataContractResult.getValue(); + const operations = dataContractResult.getOperations(); + + const result = await documentRepository.prove( + dataContract, + type, + options, + ); + + result.addOperation(...operations); + + return result; + } + + return proveDocuments; +} + +module.exports = proveDocumentsFactory; diff --git a/packages/js-drive/lib/dpp/CachedStateRepositoryDecorator.js b/packages/js-drive/lib/dpp/CachedStateRepositoryDecorator.js new file mode 100644 index 00000000000..21ab205e850 --- /dev/null +++ b/packages/js-drive/lib/dpp/CachedStateRepositoryDecorator.js @@ -0,0 +1,268 @@ +const StateTransitionExecutionContext = require('@dashevo/dpp/lib/stateTransition/StateTransitionExecutionContext'); + +const DataContractCacheItem = require('../dataContract/DataContractCacheItem'); + +/** + * @implements StateRepository + */ +class CachedStateRepositoryDecorator { + /** + * @param {DriveStateRepository} stateRepository + * @param {LRUCache} dataContractCache + */ + constructor( + stateRepository, + dataContractCache, + ) { + this.stateRepository = stateRepository; + this.contractCache = dataContractCache; + } + + /** + * Fetch Identity by ID + * + * @param {Identifier} id + * @param {StateTransitionExecutionContext} [executionContext] + * + * @return {Promise} + */ + async fetchIdentity(id, executionContext = undefined) { + return this.stateRepository.fetchIdentity(id, executionContext); + } + + /** + * Create identity + * + * @param {Identity} identity + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async createIdentity(identity, executionContext = undefined) { + return this.stateRepository.createIdentity(identity, executionContext); + } + + /** + * Update identity + * + * @param {Identity} identity + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async updateIdentity(identity, executionContext = undefined) { + return this.stateRepository.updateIdentity(identity, executionContext); + } + + /** + * Store public key hashes for an identity id + * + * @param {Identifier} identityId + * @param {Buffer[]} publicKeyHashes + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async storeIdentityPublicKeyHashes(identityId, publicKeyHashes, executionContext = undefined) { + return this.stateRepository.storeIdentityPublicKeyHashes( + identityId, + publicKeyHashes, + executionContext, + ); + } + + /** + * Fetch identity ids mapped by related public keys + * using public key hashes + * + * @param {Buffer[]} publicKeyHashes + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise>} + */ + async fetchIdentityIdsByPublicKeyHashes(publicKeyHashes, executionContext = undefined) { + return this.stateRepository.fetchIdentityIdsByPublicKeyHashes( + publicKeyHashes, + executionContext, + ); + } + + /** + * Store spent asset lock transaction + * + * @param {Buffer} outPointBuffer + * @param {StateTransitionExecutionContext} [executionContext] + * + * @return {Promise} + */ + async markAssetLockTransactionOutPointAsUsed(outPointBuffer, executionContext = undefined) { + return this.stateRepository.markAssetLockTransactionOutPointAsUsed( + outPointBuffer, + executionContext, + ); + } + + /** + * Check if spent asset lock transaction is stored + * + * @param {Buffer} outPointBuffer + * @param {StateTransitionExecutionContext} [executionContext] + * + * @return {Promise} + */ + async isAssetLockTransactionOutPointAlreadyUsed(outPointBuffer, executionContext = undefined) { + return this.stateRepository.isAssetLockTransactionOutPointAlreadyUsed( + outPointBuffer, + executionContext, + ); + } + + /** + * Fetch Data Contract by ID + * + * @param {Identifier} id + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async fetchDataContract(id, executionContext = undefined) { + const idString = id.toString(); + + let cacheItem = this.contractCache.get(idString); + + if (cacheItem) { + if (executionContext) { + executionContext.addOperation(...cacheItem.getOperations()); + } + + return cacheItem.getDataContract(); + } + + const isolatedExecutionContext = new StateTransitionExecutionContext(); + + const dataContract = await this.stateRepository.fetchDataContract(id, isolatedExecutionContext); + + if (executionContext) { + executionContext.addOperation(...isolatedExecutionContext.getOperations()); + } + + if (dataContract !== null) { + cacheItem = new DataContractCacheItem( + dataContract, + isolatedExecutionContext.getOperations(), + ); + + this.contractCache.set(idString, cacheItem); + } + + return dataContract; + } + + /** + * Store Data Contract + * + * @param {DataContract} dataContract + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async storeDataContract(dataContract, executionContext = undefined) { + return this.stateRepository.storeDataContract(dataContract, executionContext); + } + + /** + * Fetch Documents by contract ID and type + * + * @param {Identifier} contractId + * @param {string} type + * @param {{ where: Object }} [options] + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async fetchDocuments(contractId, type, options = {}, executionContext = undefined) { + return this.stateRepository.fetchDocuments(contractId, type, options, executionContext); + } + + /** + * Create document + * + * @param {Document} document + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async createDocument(document, executionContext = undefined) { + return this.stateRepository.createDocument(document, executionContext); + } + + /** + * Update document + * + * @param {Document} document + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async updateDocument(document, executionContext = undefined) { + return this.stateRepository.updateDocument(document, executionContext); + } + + /** + * Remove document + * + * @param {DataContract} dataContract + * @param {string} type + * @param {Identifier} id + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async removeDocument(dataContract, type, id, executionContext = undefined) { + return this.stateRepository.removeDocument(dataContract, type, id, executionContext); + } + + /** + * Fetch transaction by ID + * + * @param {string} id + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async fetchTransaction(id, executionContext = undefined) { + return this.stateRepository.fetchTransaction(id, executionContext); + } + + /** + * Fetch the latest platform block header + * + * @return {Promise} + */ + async fetchLatestPlatformBlockHeader() { + return this.stateRepository.fetchLatestPlatformBlockHeader(); + } + + /** + * Verify instant lock + * + * @param {InstantLock} instantLock + * @param {StateTransitionExecutionContext} [executionContext] + * + * @return {Promise} + */ + async verifyInstantLock(instantLock, executionContext = undefined) { + return this.stateRepository.verifyInstantLock(instantLock, executionContext); + } + + /** + * Fetch Simplified Masternode List Store + * + * @return {Promise} + */ + async fetchSMLStore() { + return this.stateRepository.fetchSMLStore(); + } +} + +module.exports = CachedStateRepositoryDecorator; diff --git a/packages/js-drive/lib/dpp/DriveStateRepository.js b/packages/js-drive/lib/dpp/DriveStateRepository.js new file mode 100644 index 00000000000..cfaccb8bcf5 --- /dev/null +++ b/packages/js-drive/lib/dpp/DriveStateRepository.js @@ -0,0 +1,460 @@ +const { TYPES } = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); + +const ReadOperation = require('@dashevo/dpp/lib/stateTransition/fee/operations/ReadOperation'); +const SignatureVerificationOperation = require('@dashevo/dpp/lib/stateTransition/fee/operations/SignatureVerificationOperation'); + +/** + * @implements StateRepository + */ +class DriveStateRepository { + #options = {}; + + /** + * @param {IdentityStoreRepository} identityRepository + * @param {PublicKeyToIdentitiesStoreRepository} publicKeyToToIdentitiesRepository + * @param {DataContractStoreRepository} dataContractRepository + * @param {fetchDocuments} fetchDocuments + * @param {DocumentRepository} documentRepository + * @param {SpentAssetLockTransactionsRepository} spentAssetLockTransactionsRepository + * @param {RpcClient} coreRpcClient + * @param {BlockExecutionContext} blockExecutionContext + * @param {SimplifiedMasternodeList} simplifiedMasternodeList + * @param {Object} [options] + * @param {Object} [options.useTransaction=false] + */ + constructor( + identityRepository, + publicKeyToToIdentitiesRepository, + dataContractRepository, + fetchDocuments, + documentRepository, + spentAssetLockTransactionsRepository, + coreRpcClient, + blockExecutionContext, + simplifiedMasternodeList, + options = {}, + ) { + this.identityRepository = identityRepository; + this.publicKeyToIdentitiesRepository = publicKeyToToIdentitiesRepository; + this.dataContractRepository = dataContractRepository; + this.fetchDocumentsFunction = fetchDocuments; + this.documentRepository = documentRepository; + this.spentAssetLockTransactionsRepository = spentAssetLockTransactionsRepository; + this.coreRpcClient = coreRpcClient; + this.blockExecutionContext = blockExecutionContext; + this.simplifiedMasternodeList = simplifiedMasternodeList; + this.#options = options; + } + + /** + * Fetch Identity by ID + * + * @param {Identifier} id + * @param {StateTransitionExecutionContext} [executionContext] + * + * @return {Promise} + */ + async fetchIdentity(id, executionContext = undefined) { + const result = await this.identityRepository.fetch( + id, + this.#createRepositoryOptions(executionContext), + ); + + if (executionContext) { + executionContext.addOperation(...result.getOperations()); + } + + return result.getValue(); + } + + /** + * Create identity + * + * @param {Identity} identity + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async createIdentity(identity, executionContext = undefined) { + const result = await this.identityRepository.create( + identity, + this.#createRepositoryOptions(executionContext), + ); + + if (executionContext) { + executionContext.addOperation(...result.getOperations()); + } + } + + /** + * Update identity + * + * @param {Identity} identity + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async updateIdentity(identity, executionContext = undefined) { + const result = await this.identityRepository.update( + identity, + this.#createRepositoryOptions(executionContext), + ); + + if (executionContext) { + executionContext.addOperation(...result.getOperations()); + } + } + + /** + * Store public key hashes for an identity id + * + * @param {Identifier} identityId + * @param {Buffer[]} publicKeyHashes + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async storeIdentityPublicKeyHashes(identityId, publicKeyHashes, executionContext = undefined) { + for (const publicKeyHash of publicKeyHashes) { + const result = await this.publicKeyToIdentitiesRepository.store( + publicKeyHash, + identityId, + this.#createRepositoryOptions(executionContext), + ); + + if (executionContext) { + executionContext.addOperation(...result.getOperations()); + } + } + } + + /** + * Store spent asset lock transaction + * + * @param {Buffer} outPointBuffer + * @param {StateTransitionExecutionContext} [executionContext] + * + * @return {Promise} + */ + async markAssetLockTransactionOutPointAsUsed(outPointBuffer, executionContext = undefined) { + const result = await this.spentAssetLockTransactionsRepository.store( + outPointBuffer, + this.#createRepositoryOptions(executionContext), + ); + + if (executionContext) { + executionContext.addOperation(...result.getOperations()); + } + } + + /** + * Check if spent asset lock transaction is stored + * + * @param {Buffer} outPointBuffer + * @param {StateTransitionExecutionContext} [executionContext] + * + * @return {Promise} + */ + async isAssetLockTransactionOutPointAlreadyUsed(outPointBuffer, executionContext = undefined) { + const result = await this.spentAssetLockTransactionsRepository.fetch( + outPointBuffer, + this.#createRepositoryOptions(executionContext), + ); + + if (executionContext) { + executionContext.addOperation(...result.getOperations()); + } + + return !result.isNull(); + } + + /** + * Fetch identity ids by related public key hashes + * + * @param {Buffer[]} publicKeyHashes + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise>} + */ + async fetchIdentityIdsByPublicKeyHashes(publicKeyHashes, executionContext = undefined) { + // Keep await here. + // noinspection UnnecessaryLocalVariableJS + const results = await Promise.all( + publicKeyHashes.map(async (publicKeyHash) => ( + this.publicKeyToIdentitiesRepository.fetch( + publicKeyHash, + this.#createRepositoryOptions(executionContext), + ) + )), + ); + + return results.map((result) => { + if (executionContext) { + executionContext.addOperation(...result.getOperations()); + } + + return result.getValue(); + }); + } + + /** + * Fetch Data Contract by ID + * + * @param {Identifier} id + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async fetchDataContract(id, executionContext = undefined) { + const result = await this.dataContractRepository.fetch( + id, + { + dryRun: executionContext ? executionContext.isDryRun() : false, + // Transaction is not using since Data Contract + // should be always committed to use + useTransaction: false, + }, + ); + + if (executionContext) { + executionContext.addOperation(...result.getOperations()); + } + + return result.getValue(); + } + + /** + * Store Data Contract + * + * @param {DataContract} dataContract + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async storeDataContract(dataContract, executionContext = undefined) { + const result = await this.dataContractRepository.store( + dataContract, + this.#createRepositoryOptions(executionContext), + ); + + if (executionContext) { + executionContext.addOperation(...result.getOperations()); + } + } + + /** + * Fetch Documents by contract ID and type + * + * @param {Identifier} contractId + * @param {string} type + * @param {{ where: Object }} [options] + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async fetchDocuments(contractId, type, options = {}, executionContext = undefined) { + const result = await this.fetchDocumentsFunction( + contractId, + type, + { + ...options, + ...this.#createRepositoryOptions(executionContext), + }, + ); + + if (executionContext) { + executionContext.addOperation(...result.getOperations()); + } + + return result.getValue(); + } + + /** + * Create document + * + * @param {Document} document + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async createDocument(document, executionContext = undefined) { + const result = await this.documentRepository.create( + document, + this.#createRepositoryOptions(executionContext), + ); + + if (executionContext) { + executionContext.addOperation(...result.getOperations()); + } + } + + /** + * Update document + * + * @param {Document} document + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async updateDocument(document, executionContext = undefined) { + const result = await this.documentRepository.update( + document, + this.#createRepositoryOptions(executionContext), + ); + + if (executionContext) { + executionContext.addOperation(...result.getOperations()); + } + } + + /** + * Remove document + * + * @param {DataContract} dataContract + * @param {string} type + * @param {Identifier} id + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async removeDocument(dataContract, type, id, executionContext = undefined) { + const result = await this.documentRepository.delete( + dataContract, + type, + id, + this.#createRepositoryOptions(executionContext), + ); + + if (executionContext) { + executionContext.addOperation(...result.getOperations()); + } + } + + /** + * Fetch Core transaction by ID + * + * @param {string} id - Transaction ID hex + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async fetchTransaction(id, executionContext = undefined) { + if (executionContext && executionContext.isDryRun()) { + executionContext.addOperation( + new ReadOperation(512), + ); + + return { + data: Buffer.alloc(0), + height: 1, + }; + } + + try { + const { result: transaction } = await this.coreRpcClient.getRawTransaction(id, 1); + + const data = Buffer.from(transaction.hex, 'hex'); + + if (executionContext) { + executionContext.addOperation( + new ReadOperation(data.length), + ); + } + + return { + data, + height: transaction.height, + }; + } catch (e) { + // Invalid address or key error + if (e.code === -5) { + return null; + } + + throw e; + } + } + + /** + * Fetch latest platform block header + * + * @return {Promise} + */ + async fetchLatestPlatformBlockHeader() { + return this.blockExecutionContext.getHeader(); + } + + /** + * Verify instant lock + * + * @param {InstantLock} instantLock + * @param {StateTransitionExecutionContext} [executionContext] + * + * @return {Promise} + */ + // eslint-disable-next-line no-unused-vars + async verifyInstantLock(instantLock, executionContext = undefined) { + const header = this.blockExecutionContext.getHeader(); + + if (header === null) { + return false; + } + + if (executionContext) { + executionContext.addOperation( + new SignatureVerificationOperation(TYPES.ECDSA_SECP256K1), + ); + + if (executionContext.isDryRun()) { + return true; + } + } + + const { + coreChainLockedHeight, + } = header; + + try { + const { result: isVerified } = await this.coreRpcClient.verifyIsLock( + instantLock.getRequestId().toString('hex'), + instantLock.txid, + instantLock.signature, + coreChainLockedHeight, + ); + + return isVerified; + } catch (e) { + // Invalid address or key error or + // Invalid, missing or duplicate parameter + // Parse error + if ([-8, -5, -32700].includes(e.code)) { + return false; + } + + throw e; + } + } + + /** + * Fetch Simplified Masternode List Store + * + * @return {Promise} + */ + async fetchSMLStore() { + return this.simplifiedMasternodeList.getStore(); + } + + /** + * @private + * @param {StateTransitionExecutionContext} [executionContext] + * @return {{dryRun: boolean, useTransaction: boolean}} + */ + #createRepositoryOptions(executionContext) { + return { + useTransaction: this.#options.useTransaction || false, + dryRun: executionContext ? executionContext.isDryRun() : false, + }; + } +} + +module.exports = DriveStateRepository; diff --git a/packages/js-drive/lib/dpp/LoggedStateRepositoryDecorator.js b/packages/js-drive/lib/dpp/LoggedStateRepositoryDecorator.js new file mode 100644 index 00000000000..d183c9b4a28 --- /dev/null +++ b/packages/js-drive/lib/dpp/LoggedStateRepositoryDecorator.js @@ -0,0 +1,462 @@ +/** + * @implements StateRepository + */ +class LoggedStateRepositoryDecorator { + /** + * @param {DriveStateRepository|CachedStateRepositoryDecorator} stateRepository + * @param {BlockExecutionContext} blockExecutionContext + */ + constructor( + stateRepository, + blockExecutionContext, + ) { + this.stateRepository = stateRepository; + this.blockExecutionContext = blockExecutionContext; + } + + /** + * @private + * @param {string} method - state repository method name + * @param {object} parameters - parameters of the state repository call + * @param {object} response - response of the state repository call + */ + log(method, parameters, response) { + const logger = this.blockExecutionContext.getConsensusLogger(); + + logger.trace({ + stateRepository: { + method, + parameters, + response, + }, + }, `StateRepository#${method}`); + } + + /** + * Fetch Identity by ID + * + * @param {Identifier} id + * @param {StateTransitionExecutionContext} [executionContext] + * + * @return {Promise} + */ + async fetchIdentity(id, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.fetchIdentity(id, executionContext); + } finally { + this.log( + 'fetchIdentity', + { + id, + }, + response, + ); + } + + return response; + } + + /** + * Create identity + * + * @param {Identity} identity + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async createIdentity(identity, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.createIdentity(identity, executionContext); + } finally { + this.log( + 'createIdentity', + { + identity, + }, + response, + ); + } + + return response; + } + + /** + * Update identity + * + * @param {Identity} identity + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async updateIdentity(identity, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.updateIdentity(identity, executionContext); + } finally { + this.log( + 'updateIdentity', + { + identity, + }, + response, + ); + } + + return response; + } + + /** + * Store public key hashes for an identity id + * + * @param {Identifier} identityId + * @param {Buffer[]} publicKeyHashes + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async storeIdentityPublicKeyHashes(identityId, publicKeyHashes, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository + .storeIdentityPublicKeyHashes(identityId, publicKeyHashes, executionContext); + } finally { + this.log( + 'storeIdentityPublicKeyHashes', + { + identityId, + publicKeyHashes: publicKeyHashes.map((hash) => hash.toString('base64')), + }, + response, + ); + } + + return response; + } + + /** + * Fetch identity ids mapped by related public keys + * using public key hashes + * + * @param {Buffer[]} publicKeyHashes + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise>} + */ + async fetchIdentityIdsByPublicKeyHashes(publicKeyHashes, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.fetchIdentityIdsByPublicKeyHashes( + publicKeyHashes, + executionContext, + ); + } finally { + this.log( + 'fetchIdentityIdsByPublicKeyHashes', + { + publicKeyHashes: publicKeyHashes.map((hash) => hash.toString('base64')), + }, + response, + ); + } + + return response; + } + + /** + * Store spent asset lock transaction + * + * @param {Buffer} outPointBuffer + * @param {StateTransitionExecutionContext} [executionContext] + * + * @return {Promise} + */ + async markAssetLockTransactionOutPointAsUsed(outPointBuffer, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.markAssetLockTransactionOutPointAsUsed( + outPointBuffer, + executionContext, + ); + } finally { + this.log( + 'markAssetLockTransactionOutPointAsUsed', + { + outPointBuffer: outPointBuffer.toString('base64'), + }, + response, + ); + } + + return response; + } + + /** + * Check if spent asset lock transaction is stored + * + * @param {Buffer} outPointBuffer + * @param {StateTransitionExecutionContext} [executionContext] + * + * @return {Promise} + */ + async isAssetLockTransactionOutPointAlreadyUsed(outPointBuffer, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.isAssetLockTransactionOutPointAlreadyUsed( + outPointBuffer, + executionContext, + ); + } finally { + this.log( + 'isAssetLockTransactionOutPointAlreadyUsed', + { + outPointBuffer: outPointBuffer.toString('base64'), + }, + response, + ); + } + + return response; + } + + /** + * Fetch Data Contract by ID + * + * @param {Identifier} id + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async fetchDataContract(id, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.fetchDataContract(id, executionContext); + } finally { + this.log( + 'fetchDataContract', + { + id, + }, + response, + ); + } + + return response; + } + + /** + * Store Data Contract + * + * @param {DataContract} dataContract + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async storeDataContract(dataContract, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.storeDataContract(dataContract, executionContext); + } finally { + this.log('storeDataContract', { dataContract }, response); + } + + return response; + } + + /** + * Fetch Documents by contract ID and type + * + * @param {Identifier} contractId + * @param {string} type + * @param {{ where: Object }} [options] + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async fetchDocuments(contractId, type, options = {}, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.fetchDocuments( + contractId, + type, + options, + executionContext, + ); + } finally { + this.log( + 'fetchDocuments', + { + contractId, + type, + options, + }, + response, + ); + } + + return response; + } + + /** + * Create document + * + * @param {Document} document + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async createDocument(document, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.createDocument(document, executionContext); + } finally { + this.log( + 'createDocument', + { + document, + }, + response, + ); + } + + return response; + } + + /** + * Update document + * + * @param {Document} document + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async updateDocument(document, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.updateDocument(document, executionContext); + } finally { + this.log( + 'updateDocument', + { + document, + }, + response, + ); + } + + return response; + } + + /** + * Remove document + * + * @param {DataContract} dataContract + * @param {string} type + * @param {Identifier} id + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async removeDocument(dataContract, type, id, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.removeDocument( + dataContract, + type, + id, + executionContext, + ); + } finally { + this.log( + 'removeDocument', + { + dataContract, + type, + id, + }, + response, + ); + } + + return response; + } + + /** + * Fetch transaction by ID + * + * @param {string} id + * @param {StateTransitionExecutionContext} [executionContext] + * + * @returns {Promise} + */ + async fetchTransaction(id, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.fetchTransaction(id, executionContext); + } finally { + this.log( + 'fetchTransaction', + { + id, + }, + response, + ); + } + + return response; + } + + /** + * Fetch latest platform block header + * + * @return {Promise} + */ + async fetchLatestPlatformBlockHeader() { + let response; + + try { + response = await this.stateRepository.fetchLatestPlatformBlockHeader(); + } finally { + this.log('fetchLatestPlatformBlockHeader', { }, response); + } + + return response; + } + + /** + * Verify instant lock + * + * @param {InstantLock} instantLock + * @param {StateTransitionExecutionContext} [executionContext] + * + * @return {Promise} + */ + async verifyInstantLock(instantLock, executionContext = undefined) { + let response; + + try { + response = await this.stateRepository.verifyInstantLock(instantLock, executionContext); + } finally { + this.log('verifyInstantLock', { instantLock }, response); + } + + return response; + } +} + +module.exports = LoggedStateRepositoryDecorator; diff --git a/packages/js-drive/lib/errorHandlerFactory.js b/packages/js-drive/lib/errorHandlerFactory.js new file mode 100644 index 00000000000..cf06daf200b --- /dev/null +++ b/packages/js-drive/lib/errorHandlerFactory.js @@ -0,0 +1,56 @@ +const printErrorFace = require('./util/printErrorFace'); + +/** + * @param {BaseLogger} logger + * @param {AwilixContainer} container + * @param {closeAbciServer} closeAbciServer + */ +function errorHandlerFactory(logger, container, closeAbciServer) { + let isCalledAlready = false; + const errors = []; + + /** + * Error handler + * + * @param {Error} error + */ + async function errorHandler(error) { + // Collect all thrown errors + errors.push(error); + + // Gracefully shutdown only once + if (isCalledAlready) { + return; + } + + isCalledAlready = true; + + try { + try { + // Close all ABCI server connections + await closeAbciServer(); + + // Add further code to the end of event loop (the same as process.nextTick) + await Promise.resolve(); + + // eslint-disable-next-line no-console + console.log(printErrorFace()); + + errors.forEach((e) => { + (error.consensusLogger || logger).fatal({ err: e }, e.message); + }); + } finally { + await container.dispose(); + } + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + } finally { + process.exit(1); + } + } + + return errorHandler; +} + +module.exports = errorHandlerFactory; diff --git a/packages/js-drive/lib/errors/DriveError.js b/packages/js-drive/lib/errors/DriveError.js new file mode 100644 index 00000000000..ba5dc360532 --- /dev/null +++ b/packages/js-drive/lib/errors/DriveError.js @@ -0,0 +1,23 @@ +class DriveError extends Error { + /** + * @param {string} message + */ + constructor(message) { + super(message); + + this.name = this.constructor.name; + + Error.captureStackTrace(this, this.constructor); + } + + /** + * Get message + * + * @return {string} + */ + getMessage() { + return this.message; + } +} + +module.exports = DriveError; diff --git a/packages/js-drive/lib/featureFlag/getFeatureFlagForHeightFactory.js b/packages/js-drive/lib/featureFlag/getFeatureFlagForHeightFactory.js new file mode 100644 index 00000000000..c15175c48da --- /dev/null +++ b/packages/js-drive/lib/featureFlag/getFeatureFlagForHeightFactory.js @@ -0,0 +1,48 @@ +/** + * @param {Identifier} featureFlagsContractId + * @param {fetchDocuments} fetchDocuments + * + * @return {getFeatureFlagForHeight} + */ +function getFeatureFlagForHeightFactory( + featureFlagsContractId, + fetchDocuments, +) { + /** + * @typedef getFeatureFlagForHeight + * + * @param {string} flagType + * @param {Long} blockHeight + * @param {boolean} [useTransaction=false] + * + * @return {Promise} + */ + async function getFeatureFlagForHeight(flagType, blockHeight, useTransaction = false) { + if (!featureFlagsContractId) { + return null; + } + + const query = { + where: [ + ['enableAtHeight', '==', blockHeight.toNumber()], + ], + }; + + const result = await fetchDocuments( + featureFlagsContractId, + flagType, + { + ...query, + useTransaction, + }, + ); + + const [document] = result.getValue(); + + return document; + } + + return getFeatureFlagForHeight; +} + +module.exports = getFeatureFlagForHeightFactory; diff --git a/packages/js-drive/lib/featureFlag/getLatestFeatureFlagFactory.js b/packages/js-drive/lib/featureFlag/getLatestFeatureFlagFactory.js new file mode 100644 index 00000000000..56e1dfcd885 --- /dev/null +++ b/packages/js-drive/lib/featureFlag/getLatestFeatureFlagFactory.js @@ -0,0 +1,52 @@ +/** + * @param {Identifier} featureFlagsContractId + * @param {fetchDocuments} fetchDocuments + * + * @return {getLatestFeatureFlag} + */ +function getLatestFeatureFlagFactory( + featureFlagsContractId, + fetchDocuments, +) { + /** + * @typedef getLatestFeatureFlag + * + * @param {string} flagType + * @param {Long} blockHeight + * @param {boolean} [useTransaction=false] + * + * @return {Promise} + */ + async function getLatestFeatureFlag(flagType, blockHeight, useTransaction = false) { + if (!featureFlagsContractId) { + return null; + } + + const query = { + where: [ + ['enableAtHeight', '<=', blockHeight.toNumber()], + ], + orderBy: [ + ['enableAtHeight', 'desc'], + ], + limit: 1, + }; + + const result = await fetchDocuments( + featureFlagsContractId, + flagType, + { + ...query, + useTransaction, + }, + ); + + const [document] = result.getValue(); + + return document; + } + + return getLatestFeatureFlag; +} + +module.exports = getLatestFeatureFlagFactory; diff --git a/packages/js-drive/lib/identity/IdentityStoreRepository.js b/packages/js-drive/lib/identity/IdentityStoreRepository.js new file mode 100644 index 00000000000..cf475a10b6d --- /dev/null +++ b/packages/js-drive/lib/identity/IdentityStoreRepository.js @@ -0,0 +1,172 @@ +const Identity = require('@dashevo/dpp/lib/identity/Identity'); + +const getBiggestPossibleIdentity = require('@dashevo/dpp/lib/identity/getBiggestPossibleIdentity'); + +const StorageResult = require('../storage/StorageResult'); + +const MAX_IDENTITY_SIZE = getBiggestPossibleIdentity().toBuffer().length; + +class IdentityStoreRepository { + /** + * + * @param {GroveDBStore} groveDBStore + * @param {decodeProtocolEntity} decodeProtocolEntity + */ + constructor(groveDBStore, decodeProtocolEntity) { + this.storage = groveDBStore; + this.decodeProtocolEntity = decodeProtocolEntity; + } + + /** + * Store identity into database + * + * @param {Identity} identity + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * @return {Promise>} + */ + async create(identity, options = {}) { + const key = identity.getId().toBuffer(); + const value = identity.toBuffer(); + + const treeResult = await this.storage.createTree( + IdentityStoreRepository.TREE_PATH, + key, + options, + ); + + const identityResult = await this.storage.put( + IdentityStoreRepository.TREE_PATH.concat([key]), + IdentityStoreRepository.IDENTITY_KEY, + value, + options, + ); + + return new StorageResult( + undefined, + treeResult.getOperations().concat(identityResult.getOperations()), + ); + } + + /** + * Store identity into database + * + * @param {Identity} identity + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * @return {Promise>} + */ + async update(identity, options = {}) { + const key = identity.getId().toBuffer(); + const value = identity.toBuffer(); + + const result = await this.storage.put( + IdentityStoreRepository.TREE_PATH.concat([key]), + IdentityStoreRepository.IDENTITY_KEY, + value, + options, + ); + + result.setValue(undefined); + + return result; + } + + /** + * Fetch identity by id from database + * + * @param {Identifier} id + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * @return {Promise>} + */ + async fetch(id, options = { }) { + const encodedIdentityResult = await this.storage.get( + IdentityStoreRepository.TREE_PATH.concat([id.toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + { + ...options, + predictedValueSize: MAX_IDENTITY_SIZE, + }, + ); + + if (encodedIdentityResult.isNull()) { + return encodedIdentityResult; + } + + const [protocolVersion, rawIdentity] = this.decodeProtocolEntity( + encodedIdentityResult.getValue(), + ); + + rawIdentity.protocolVersion = protocolVersion; + + return new StorageResult( + new Identity(rawIdentity), + encodedIdentityResult.getOperations(), + ); + } + + /** + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.skipIfExists=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async createTree(options = {}) { + return this.storage.createTree( + [], + IdentityStoreRepository.TREE_PATH[0], + options, + ); + } + + /** + * Prove identity by id + * + * @param {Identifier} id + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * + * @return {Promise>} + * */ + async prove(id, options) { + return this.proveMany([id], options); + } + + /** + * Prove identity by ids + * + * @param {Identifier[]} ids + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * + * @return {Promise>} + * */ + async proveMany(ids, options) { + const items = ids.map((id) => ({ + type: 'key', + key: id.toBuffer(), + })); + + return this.storage.proveQuery({ + path: IdentityStoreRepository.TREE_PATH, + query: { + query: { + items, + subqueryKey: IdentityStoreRepository.IDENTITY_KEY, + }, + }, + }, options); + } +} + +IdentityStoreRepository.TREE_PATH = [Buffer.from([0])]; + +IdentityStoreRepository.IDENTITY_KEY = Buffer.from([0]); + +module.exports = IdentityStoreRepository; diff --git a/packages/js-drive/lib/identity/PublicKeyToIdentitiesStoreRepository.js b/packages/js-drive/lib/identity/PublicKeyToIdentitiesStoreRepository.js new file mode 100644 index 00000000000..47368d65408 --- /dev/null +++ b/packages/js-drive/lib/identity/PublicKeyToIdentitiesStoreRepository.js @@ -0,0 +1,201 @@ +const Identity = require('@dashevo/dpp/lib/identity/Identity'); +const StorageResult = require('../storage/StorageResult'); +const IdentityStoreRepository = require('./IdentityStoreRepository'); + +class PublicKeyToIdentitiesStoreRepository { + /** + * + * @param {GroveDBStore} groveDBStore + * @param {decodeProtocolEntity} decodeProtocolEntity + */ + constructor(groveDBStore, decodeProtocolEntity) { + this.storage = groveDBStore; + this.decodeProtocolEntity = decodeProtocolEntity; + } + + /** + * Store public key to identity ids map into database + * + * @param {Buffer} publicKeyHash + * @param {Identifier} identityId + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async store(publicKeyHash, identityId, options = {}) { + const treeResult = await this.storage.createTree( + PublicKeyToIdentitiesStoreRepository.TREE_PATH, + publicKeyHash, + { + ...options, + skipIfExists: true, + }, + ); + + const key = identityId.toBuffer(); + + const referenceResult = await this.storage.putReference( + PublicKeyToIdentitiesStoreRepository.TREE_PATH.concat([publicKeyHash]), + key, + IdentityStoreRepository.TREE_PATH.concat([key, IdentityStoreRepository.IDENTITY_KEY]), + options, + ); + + return new StorageResult( + undefined, + treeResult.getOperations().concat(referenceResult.getOperations()), + ); + } + + /** + * Fetch deserialized identities by public key hash + * + * @param {Buffer} publicKeyHash + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async fetch(publicKeyHash, options = {}) { + const result = await this.storage.query({ + path: PublicKeyToIdentitiesStoreRepository.TREE_PATH.concat([publicKeyHash]), + query: { + query: { + items: [ + { + type: 'rangeFull', + }, + ], + }, + }, + }, options); + + return new StorageResult( + result.getValue().map((serializedIdentity) => { + const [protocolVersion, rawIdentity] = this.decodeProtocolEntity( + serializedIdentity, + ); + + rawIdentity.protocolVersion = protocolVersion; + + return new Identity(rawIdentity); + }), + result.getOperations(), + ); + } + + /** + * Fetch deserialized identities by multiple public key hashes + * + * @param {Buffer[]} publicKeyHashes + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async fetchMany(publicKeyHashes, options = {}) { + const result = await this.fetchManyBuffers(publicKeyHashes, options); + + return new StorageResult( + result.getValue().map((serializedIdentity) => { + const [protocolVersion, rawIdentity] = this.decodeProtocolEntity( + serializedIdentity, + ); + + rawIdentity.protocolVersion = protocolVersion; + + return new Identity(rawIdentity); + }), + result.getOperations(), + ); + } + + /** + * Fetch serialized identities by multiple public key hashes + * + * @param {Buffer[]} publicKeyHashes + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async fetchManyBuffers(publicKeyHashes, options = {}) { + const items = publicKeyHashes.map((publicKeyHash) => ({ + type: 'key', + key: publicKeyHash, + })); + + return this.storage.query({ + path: PublicKeyToIdentitiesStoreRepository.TREE_PATH, + query: { + query: { + items, + subquery: { + items: [ + { + type: 'rangeFull', + }, + ], + }, + }, + }, + }, options); + } + + /** + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.skipIfExists=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async createTree(options = {}) { + return this.storage.createTree( + [], + PublicKeyToIdentitiesStoreRepository.TREE_PATH[0], + options, + ); + } + + /** + * Prove identities by multiple public key hashes + * + * @param {Buffer[]} publicKeyHashes + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * + * @return {Promise>} + */ + async proveMany(publicKeyHashes, options = {}) { + const items = publicKeyHashes.map((publicKeyHash) => ({ + type: 'key', + key: publicKeyHash, + })); + + return this.storage.proveQuery({ + path: PublicKeyToIdentitiesStoreRepository.TREE_PATH, + query: { + query: { + items, + subquery: { + items: [ + { + type: 'rangeFull', + }, + ], + }, + }, + }, + }, options); + } +} + +PublicKeyToIdentitiesStoreRepository.TREE_PATH = [Buffer.from([2])]; + +module.exports = PublicKeyToIdentitiesStoreRepository; diff --git a/packages/js-drive/lib/identity/SpentAssetLockTransactionsRepository.js b/packages/js-drive/lib/identity/SpentAssetLockTransactionsRepository.js new file mode 100644 index 00000000000..c84d172d090 --- /dev/null +++ b/packages/js-drive/lib/identity/SpentAssetLockTransactionsRepository.js @@ -0,0 +1,90 @@ +const StorageResult = require('../storage/StorageResult'); + +class SpentAssetLockTransactionsRepository { + /** + * @param {GroveDBStore} groveDBStore + */ + constructor(groveDBStore) { + this.storage = groveDBStore; + } + + /** + * Store the outPoint + * + * @param {Buffer} outPointBuffer + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async store(outPointBuffer, options = {}) { + const emptyValue = Buffer.from([0]); + + const result = await this.storage.put( + SpentAssetLockTransactionsRepository.TREE_PATH, + outPointBuffer, + emptyValue, + options, + ); + + return new StorageResult( + undefined, + result.getOperations(), + ); + } + + /** + * Fetch the outPoint + * + * @param {Buffer} outPointBuffer + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryTun=false] + * + * @return {Promise>} + */ + async fetch(outPointBuffer, options = {}) { + const result = await this.storage.get( + SpentAssetLockTransactionsRepository.TREE_PATH, + outPointBuffer, + options, + ); + + return new StorageResult( + result.getValue(), + result.getOperations(), + ); + } + + /** + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.skipIfExists=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async createTree(options = {}) { + const rootTreePath = [SpentAssetLockTransactionsRepository.TREE_PATH[0]]; + const treePath = SpentAssetLockTransactionsRepository.TREE_PATH[1]; + + const result = await this.storage.createTree( + rootTreePath, + treePath, + options, + ); + + return new StorageResult( + undefined, + result.getOperations(), + ); + } +} + +SpentAssetLockTransactionsRepository.TREE_PATH = [ + Buffer.from([3]), + Buffer.from([0]), +]; + +module.exports = SpentAssetLockTransactionsRepository; diff --git a/packages/js-drive/lib/identity/masternode/createMasternodeIdentityFactory.js b/packages/js-drive/lib/identity/masternode/createMasternodeIdentityFactory.js new file mode 100644 index 00000000000..881f81ce289 --- /dev/null +++ b/packages/js-drive/lib/identity/masternode/createMasternodeIdentityFactory.js @@ -0,0 +1,87 @@ +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); +const Identity = require('@dashevo/dpp/lib/identity/Identity'); +const InvalidMasternodeIdentityError = require('./errors/InvalidMasternodeIdentityError'); + +/** + * @param {DashPlatformProtocol} dpp + * @param {DriveStateRepository|CachedStateRepositoryDecorator} transactionalStateRepository + * @param {getWithdrawPubKeyTypeFromPayoutScript} getWithdrawPubKeyTypeFromPayoutScript + * @param {getPublicKeyFromPayoutScript} getPublicKeyFromPayoutScript + * @return {createMasternodeIdentity} + */ +function createMasternodeIdentityFactory( + dpp, + transactionalStateRepository, + getWithdrawPubKeyTypeFromPayoutScript, + getPublicKeyFromPayoutScript, +) { + /** + * @typedef createMasternodeIdentity + * @param {Identifier} identifier + * @param {Buffer} pubKeyData + * @param {number} pubKeyType + * @param {Script} [payoutScript] + * @return {Promise} + */ + async function createMasternodeIdentity( + identifier, + pubKeyData, + pubKeyType, + payoutScript, + ) { + const publicKeys = [{ + id: 0, + type: pubKeyType, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: true, + // Copy data buffer + data: Buffer.from(pubKeyData), + }]; + + if (payoutScript) { + const withdrawPubKeyType = getWithdrawPubKeyTypeFromPayoutScript(payoutScript); + + publicKeys.push({ + id: 1, + type: withdrawPubKeyType, + purpose: IdentityPublicKey.PURPOSES.WITHDRAW, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.CRITICAL, + readOnly: false, + data: getPublicKeyFromPayoutScript(payoutScript, withdrawPubKeyType), + }); + } + + const identity = new Identity({ + protocolVersion: dpp.getProtocolVersion(), + id: identifier.toBuffer(), + publicKeys, + balance: 0, + revision: 0, + }); + + const validationResult = await dpp.identity.validate(identity); + if (!validationResult.isValid()) { + const validationError = validationResult.getFirstError(); + + throw new InvalidMasternodeIdentityError(validationError); + } + + await transactionalStateRepository.createIdentity(identity); + + const publicKeyHashes = identity + .getPublicKeys() + .map((publicKey) => publicKey.hash()); + + await transactionalStateRepository.storeIdentityPublicKeyHashes( + identity.getId(), + publicKeyHashes, + ); + + return identity; + } + + return createMasternodeIdentity; +} + +module.exports = createMasternodeIdentityFactory; diff --git a/packages/js-drive/lib/identity/masternode/createOperatorIdentifier.js b/packages/js-drive/lib/identity/masternode/createOperatorIdentifier.js new file mode 100644 index 00000000000..4effefbf931 --- /dev/null +++ b/packages/js-drive/lib/identity/masternode/createOperatorIdentifier.js @@ -0,0 +1,20 @@ +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); +const { hash } = require('@dashevo/dpp/lib/util/hash'); + +/** + * @param {SimplifiedMNListEntry} smlEntry + */ +function createOperatorIdentifier(smlEntry) { + const operatorPubKey = Buffer.from(smlEntry.pubKeyOperator, 'hex'); + + return Identifier.from( + hash( + Buffer.concat([ + Buffer.from(smlEntry.proRegTxHash, 'hex'), + operatorPubKey, + ]), + ), + ); +} + +module.exports = createOperatorIdentifier; diff --git a/packages/js-drive/lib/identity/masternode/createRewardShareDocumentFactory.js b/packages/js-drive/lib/identity/masternode/createRewardShareDocumentFactory.js new file mode 100644 index 00000000000..87e524b0487 --- /dev/null +++ b/packages/js-drive/lib/identity/masternode/createRewardShareDocumentFactory.js @@ -0,0 +1,86 @@ +const { hash } = require('@dashevo/dpp/lib/util/hash'); +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); + +const MAX_DOCUMENTS = 16; + +/** + * @param {DashPlatformProtocol} dpp + * @param {DocumentRepository} documentRepository + * @return {createRewardShareDocument} + */ +function createRewardShareDocumentFactory( + dpp, + documentRepository, +) { + /** + * @typedef {createRewardShareDocument} + * @param {DataContract} dataContract + * @param {Identifier} masternodeIdentifier + * @param {Identifier} operatorIdentifier + * @param {number} percentage + * @returns {Promise} + */ + async function createRewardShareDocument( + dataContract, + masternodeIdentifier, + operatorIdentifier, + percentage, + ) { + const documentsResult = await documentRepository.find( + dataContract, + 'rewardShare', + { + where: [ + ['$ownerId', '==', masternodeIdentifier.toBuffer()], + ], + useTransaction: true, + }, + ); + + // Do not create a share if it's exist already + // or max shares limit is reached + if (!documentsResult.isEmpty()) { + if (documentsResult.getValue().length > MAX_DOCUMENTS) { + return null; + } + + const operatorShare = documentsResult.getValue().find((shareDocument) => ( + shareDocument.get('payToId').equals(operatorIdentifier) + )); + + if (operatorShare) { + return null; + } + } + + const rewardShareDocument = dpp.document.create( + dataContract, + masternodeIdentifier, + 'rewardShare', + { + payToId: operatorIdentifier, + percentage, + }, + ); + + // Create an identity for operator + const rewardShareDocumentIdSeed = hash( + Buffer.concat([ + masternodeIdentifier.toBuffer(), + operatorIdentifier.toBuffer(), + ]), + ); + + rewardShareDocument.id = Identifier.from(rewardShareDocumentIdSeed); + + await documentRepository.create(rewardShareDocument, { + useTransaction: true, + }); + + return rewardShareDocument; + } + + return createRewardShareDocument; +} + +module.exports = createRewardShareDocumentFactory; diff --git a/packages/js-drive/lib/identity/masternode/errors/InvalidIdentityPublicKeyTypeError.js b/packages/js-drive/lib/identity/masternode/errors/InvalidIdentityPublicKeyTypeError.js new file mode 100644 index 00000000000..59b2a671f58 --- /dev/null +++ b/packages/js-drive/lib/identity/masternode/errors/InvalidIdentityPublicKeyTypeError.js @@ -0,0 +1,22 @@ +const DriveError = require('../../../errors/DriveError'); + +class InvalidIdentityPublicKeyTypeError extends DriveError { + /** + * @param {number} type + */ + constructor(type) { + super('Invalid Identity Public Key type'); + + this.type = type; + } + + /** + * + * @return {number} + */ + getType() { + return this.type; + } +} + +module.exports = InvalidIdentityPublicKeyTypeError; diff --git a/packages/js-drive/lib/identity/masternode/errors/InvalidMasternodeIdentityError.js b/packages/js-drive/lib/identity/masternode/errors/InvalidMasternodeIdentityError.js new file mode 100644 index 00000000000..ad1a8fe7ba7 --- /dev/null +++ b/packages/js-drive/lib/identity/masternode/errors/InvalidMasternodeIdentityError.js @@ -0,0 +1,23 @@ +const DriveError = require('../../../errors/DriveError'); + +class InvalidMasternodeIdentityError extends DriveError { + /** + * @param {Error} validationError + */ + constructor(validationError) { + super('Invalid masternode identity'); + + this.validationError = validationError; + } + + /** + * Get validation error + * + * @return {Error} + */ + getValidationError() { + return this.validationError; + } +} + +module.exports = InvalidMasternodeIdentityError; diff --git a/packages/js-drive/lib/identity/masternode/errors/InvalidPayoutScriptError.js b/packages/js-drive/lib/identity/masternode/errors/InvalidPayoutScriptError.js new file mode 100644 index 00000000000..2a224ff0661 --- /dev/null +++ b/packages/js-drive/lib/identity/masternode/errors/InvalidPayoutScriptError.js @@ -0,0 +1,22 @@ +const DriveError = require('../../../errors/DriveError'); + +class InvalidPayoutScriptError extends DriveError { + /** + * @param {Buffer} payoutScript + */ + constructor(payoutScript) { + super('Invalid payout script'); + + this.payoutScript = payoutScript; + } + + /** + * + * @return {Buffer} + */ + getPayoutScript() { + return this.payoutScript; + } +} + +module.exports = InvalidPayoutScriptError; diff --git a/packages/js-drive/lib/identity/masternode/getPublicKeyFromPayoutScript.js b/packages/js-drive/lib/identity/masternode/getPublicKeyFromPayoutScript.js new file mode 100644 index 00000000000..e6dc34d4bd1 --- /dev/null +++ b/packages/js-drive/lib/identity/masternode/getPublicKeyFromPayoutScript.js @@ -0,0 +1,21 @@ +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); +const InvalidIdentityPublicKeyTypeError = require('@dashevo/dpp/lib/stateTransition/errors/InvalidIdentityPublicKeyTypeError'); + +/** + * @typedef getPublicKeyFromPayoutScript + * @param {Script} payoutScript + * @param {number} type + * @returns {Buffer} + */ +function getPublicKeyFromPayoutScript(payoutScript, type) { + switch (type) { + case IdentityPublicKey.TYPES.ECDSA_HASH160: + return payoutScript.toBuffer().slice(3, 23); + case IdentityPublicKey.TYPES.BIP13_SCRIPT_HASH: + return payoutScript.toBuffer().slice(2, 22); + default: + throw new InvalidIdentityPublicKeyTypeError(type); + } +} + +module.exports = getPublicKeyFromPayoutScript; diff --git a/packages/js-drive/lib/identity/masternode/getWithdrawPubKeyTypeFromPayoutScriptFactory.js b/packages/js-drive/lib/identity/masternode/getWithdrawPubKeyTypeFromPayoutScriptFactory.js new file mode 100644 index 00000000000..402fd45f45f --- /dev/null +++ b/packages/js-drive/lib/identity/masternode/getWithdrawPubKeyTypeFromPayoutScriptFactory.js @@ -0,0 +1,38 @@ +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); + +const InvalidPayoutScriptError = require('./errors/InvalidPayoutScriptError'); + +/** + * + * @param {string} network + * @returns {getWithdrawPubKeyTypeFromPayoutScript} + */ +function getWithdrawPubKeyTypeFromPayoutScriptFactory(network) { + /** + * @typedef getWithdrawPubKeyTypeFromPayoutScript + * @param {Script} payoutScript + * @returns {number} + */ + function getWithdrawPubKeyTypeFromPayoutScript(payoutScript) { + const address = payoutScript.toAddress(network); + + if (address === false) { + throw new InvalidPayoutScriptError(payoutScript); + } + + let withdrawPubKeyType; + if (address.isPayToScriptHash()) { + withdrawPubKeyType = IdentityPublicKey.TYPES.BIP13_SCRIPT_HASH; + } else if (address.isPayToPublicKeyHash()) { + withdrawPubKeyType = IdentityPublicKey.TYPES.ECDSA_HASH160; + } else { + throw new InvalidPayoutScriptError(payoutScript); + } + + return withdrawPubKeyType; + } + + return getWithdrawPubKeyTypeFromPayoutScript; +} + +module.exports = getWithdrawPubKeyTypeFromPayoutScriptFactory; diff --git a/packages/js-drive/lib/identity/masternode/handleNewMasternodeFactory.js b/packages/js-drive/lib/identity/masternode/handleNewMasternodeFactory.js new file mode 100644 index 00000000000..eab925d058d --- /dev/null +++ b/packages/js-drive/lib/identity/masternode/handleNewMasternodeFactory.js @@ -0,0 +1,100 @@ +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); +const Address = require('@dashevo/dashcore-lib/lib/address'); +const Script = require('@dashevo/dashcore-lib/lib/script'); +const createOperatorIdentifier = require('./createOperatorIdentifier'); + +/** + * + * @param {DashPlatformProtocol} transactionalDpp + * @param {DriveStateRepository|CachedStateRepositoryDecorator} transactionalStateRepository + * @param {createMasternodeIdentity} createMasternodeIdentity + * @param {createRewardShareDocument} createRewardShareDocument + * @param {fetchTransaction} fetchTransaction + * @return {handleNewMasternode} + */ +function handleNewMasternodeFactory( + transactionalDpp, + transactionalStateRepository, + createMasternodeIdentity, + createRewardShareDocument, + fetchTransaction, +) { + /** + * @typedef handleNewMasternode + * @param {SimplifiedMNListEntry} masternodeEntry + * @param {DataContract} dataContract + * @return Promise> + */ + async function handleNewMasternode(masternodeEntry, dataContract) { + const result = []; + + const { extraPayload: proRegTxPayload } = await fetchTransaction(masternodeEntry.proRegTxHash); + + const proRegTxHash = Buffer.from(masternodeEntry.proRegTxHash, 'hex'); + + let payoutScript; + + if (masternodeEntry.payoutAddress) { + const payoutAddress = Address.fromString(masternodeEntry.payoutAddress); + payoutScript = new Script(payoutAddress); + } + + // Create a masternode identity + const masternodeIdentifier = Identifier.from( + proRegTxHash, + ); + + const publicKey = Buffer.from(proRegTxPayload.keyIDOwner, 'hex').reverse(); + + result.push( + await createMasternodeIdentity( + masternodeIdentifier, + publicKey, + IdentityPublicKey.TYPES.ECDSA_HASH160, + payoutScript, + ), + ); + + // we need to crate reward shares only if it's enabled in proRegTx + + if (proRegTxPayload.operatorReward > 0) { + const operatorPubKey = Buffer.from(masternodeEntry.pubKeyOperator, 'hex'); + + let operatorPayoutScript; + if (masternodeEntry.operatorPayoutAddress) { + const operatorPayoutAddress = Address.fromString(masternodeEntry.operatorPayoutAddress); + operatorPayoutScript = new Script(operatorPayoutAddress); + } + + const operatorIdentifier = createOperatorIdentifier(masternodeEntry); + + result.push( + await createMasternodeIdentity( + operatorIdentifier, + operatorPubKey, + IdentityPublicKey.TYPES.BLS12_381, + operatorPayoutScript, + ), + ); + + // Create a document in rewards data contract with percentage + const rewardShareDocument = await createRewardShareDocument( + dataContract, + masternodeIdentifier, + operatorIdentifier, + proRegTxPayload.operatorReward, + ); + + if (rewardShareDocument) { + result.push(rewardShareDocument); + } + } + + return result; + } + + return handleNewMasternode; +} + +module.exports = handleNewMasternodeFactory; diff --git a/packages/js-drive/lib/identity/masternode/handleRemovedMasternodeFactory.js b/packages/js-drive/lib/identity/masternode/handleRemovedMasternodeFactory.js new file mode 100644 index 00000000000..c4a48c6832d --- /dev/null +++ b/packages/js-drive/lib/identity/masternode/handleRemovedMasternodeFactory.js @@ -0,0 +1,48 @@ +/** + * + * @returns {handleRemovedMasternode} + */ +function handleRemovedMasternodeFactory( + documentRepository, +) { + /** + * @typedef {handleRemovedMasternode} + */ + async function handleRemovedMasternode(masternodeIdentifier, dataContract) { + // Delete documents belongs to masternode identity (ownerId) from rewards contract + // since max amount is 16, we can fetch all of them in one request + const result = []; + + const fetchedDocumentsResult = await documentRepository.find( + dataContract, + 'rewardShare', + { + where: [ + ['$ownerId', '==', masternodeIdentifier], + ], + useTransaction: true, + }, + ); + + const documentsToDelete = fetchedDocumentsResult.getValue(); + + for (const document of documentsToDelete) { + await documentRepository.delete( + dataContract, + 'rewardShare', + document.getId(), + true, + ); + + result.push( + document, + ); + } + + return result; + } + + return handleRemovedMasternode; +} + +module.exports = handleRemovedMasternodeFactory; diff --git a/packages/js-drive/lib/identity/masternode/handleUpdatedPubKeyOperatorFactory.js b/packages/js-drive/lib/identity/masternode/handleUpdatedPubKeyOperatorFactory.js new file mode 100644 index 00000000000..d99de0043b9 --- /dev/null +++ b/packages/js-drive/lib/identity/masternode/handleUpdatedPubKeyOperatorFactory.js @@ -0,0 +1,127 @@ +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); +const Address = require('@dashevo/dashcore-lib/lib/address'); +const Script = require('@dashevo/dashcore-lib/lib/script'); +const createOperatorIdentifier = require('./createOperatorIdentifier'); + +/** + * + * @param {DashPlatformProtocol} transactionalDpp + * @param {DriveStateRepository|CachedStateRepositoryDecorator} transactionalStateRepository + * @param {createMasternodeIdentity} createMasternodeIdentity + * @param {Identifier} masternodeRewardSharesContractId + * @param {createRewardShareDocument} createRewardShareDocument + * @param {DocumentRepository} documentRepository + * @param {fetchTransaction} fetchTransaction + * @return {handleUpdatedPubKeyOperator} + */ +function handleUpdatedPubKeyOperatorFactory( + transactionalDpp, + transactionalStateRepository, + createMasternodeIdentity, + masternodeRewardSharesContractId, + createRewardShareDocument, + documentRepository, + fetchTransaction, +) { + /** + * @typedef handleUpdatedPubKeyOperator + * @param {SimplifiedMNListEntry} masternodeEntry + * @param {SimplifiedMNListEntry} previousMasternodeEntry + * @param {DataContract} dataContract + * @return Promise> + */ + async function handleUpdatedPubKeyOperator( + masternodeEntry, + previousMasternodeEntry, + dataContract, + ) { + const result = []; + + const { extraPayload: proRegTxPayload } = await fetchTransaction(masternodeEntry.proRegTxHash); + + // we need to crate reward shares only if it's enabled in proRegTx + if (proRegTxPayload.operatorReward === 0) { + return result; + } + + const proRegTxHash = Buffer.from(masternodeEntry.proRegTxHash, 'hex'); + const operatorPublicKey = Buffer.from(masternodeEntry.pubKeyOperator, 'hex'); + + const operatorIdentifier = createOperatorIdentifier(masternodeEntry); + + const operatorIdentity = await transactionalStateRepository.fetchIdentity(operatorIdentifier); + + let operatorPayoutPubKey; + if (masternodeEntry.operatorPayoutAddress) { + const operatorPayoutAddress = Address.fromString(masternodeEntry.operatorPayoutAddress); + operatorPayoutPubKey = new Script(operatorPayoutAddress); + } + + // Create an identity for operator if there is no identity exist with the same ID + if (operatorIdentity === null) { + result.push( + await createMasternodeIdentity( + operatorIdentifier, + operatorPublicKey, + IdentityPublicKey.TYPES.BLS12_381, + operatorPayoutPubKey, + ), + ); + } + + // Create a document in rewards data contract with percentage defined + // in corresponding ProRegTx + + const masternodeIdentifier = Identifier.from( + proRegTxHash, + ); + + const rewardShareDocument = await createRewardShareDocument( + dataContract, + masternodeIdentifier, + operatorIdentifier, + proRegTxPayload.operatorReward, + ); + + if (rewardShareDocument) { + result.push(rewardShareDocument); + } + + // Delete document from reward shares data contract with ID corresponding to the + // masternode identity (ownerId) and previous operator identity (payToId) + + const previousOperatorIdentifier = createOperatorIdentifier(previousMasternodeEntry); + + const previousDocumentsResult = await documentRepository.find( + dataContract, + 'rewardShare', + { + where: [ + ['$ownerId', '==', masternodeIdentifier], + ['payToId', '==', previousOperatorIdentifier], + ], + useTransaction: true, + }, + ); + + if (!previousDocumentsResult.isEmpty()) { + const [previousDocument] = previousDocumentsResult.getValue(); + + await documentRepository.delete( + dataContract, + 'rewardShare', + previousDocument.getId(), + { useTransaction: true }, + ); + + result.push(previousDocument); + } + + return result; + } + + return handleUpdatedPubKeyOperator; +} + +module.exports = handleUpdatedPubKeyOperatorFactory; diff --git a/packages/js-drive/lib/identity/masternode/handleUpdatedScriptPayoutFactory.js b/packages/js-drive/lib/identity/masternode/handleUpdatedScriptPayoutFactory.js new file mode 100644 index 00000000000..fafee672c51 --- /dev/null +++ b/packages/js-drive/lib/identity/masternode/handleUpdatedScriptPayoutFactory.js @@ -0,0 +1,90 @@ +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); +const identitySchema = require('@dashevo/dpp/schema/identity/identity.json'); + +/** + * + * @param {DriveStateRepository|CachedStateRepositoryDecorator} transactionalStateRepository + * @param {BlockExecutionContext} blockExecutionContext + * @param {getWithdrawPubKeyTypeFromPayoutScript} getWithdrawPubKeyTypeFromPayoutScript + * @param {getPublicKeyFromPayoutScript} getPublicKeyFromPayoutScript + * @returns {handleUpdatedScriptPayout} + */ +function handleUpdatedScriptPayoutFactory( + transactionalStateRepository, + blockExecutionContext, + getWithdrawPubKeyTypeFromPayoutScript, + getPublicKeyFromPayoutScript, +) { + /** + * @typedef handleUpdatedScriptPayout + * @param {Identifier} identityId + * @param {Script} newPayoutScript + * @param {Script} [previousPayoutScript] + * @returns {Promise} + */ + async function handleUpdatedScriptPayout( + identityId, + newPayoutScript, + previousPayoutScript, + ) { + const identity = await transactionalStateRepository.fetchIdentity(identityId); + identity.setRevision(identity.getRevision() + 1); + let identityPublicKeys = identity + .getPublicKeys(); + + if (identityPublicKeys.length === identitySchema.properties.publicKeys.maxItems) { + // do not add new public key + return; + } + + // disable previous + if (previousPayoutScript) { + const previousPubKeyType = getWithdrawPubKeyTypeFromPayoutScript(previousPayoutScript); + const previousPubKeyData = getPublicKeyFromPayoutScript( + previousPayoutScript, + previousPubKeyType, + ); + const { time } = blockExecutionContext.getHeader(); + + identityPublicKeys = identityPublicKeys.map((pk) => { + if (pk.getData().equals(previousPubKeyData)) { + pk.setDisabledAt( + time.seconds * 1000, + ); + } + + return pk; + }); + } + + // add new + const withdrawPubKeyType = getWithdrawPubKeyTypeFromPayoutScript(newPayoutScript); + const pubKeyData = getPublicKeyFromPayoutScript(newPayoutScript, withdrawPubKeyType); + + const newWithdrawalIdentityPublicKey = new IdentityPublicKey() + .setId(identity.getPublicKeyMaxId() + 1) + .setType(withdrawPubKeyType) + .setData(pubKeyData) + .setPurpose(IdentityPublicKey.PURPOSES.WITHDRAW) + .setSecurityLevel(IdentityPublicKey.SECURITY_LEVELS.MASTER); + + identityPublicKeys.push( + newWithdrawalIdentityPublicKey, + ); + + identity.setPublicKeys(identityPublicKeys); + + await transactionalStateRepository.updateIdentity(identity); + + const publicKeyHash = newWithdrawalIdentityPublicKey.hash(); + + await transactionalStateRepository.storeIdentityPublicKeyHashes( + identity.getId(), + [publicKeyHash], + ); + } + + return handleUpdatedScriptPayout; +} + +module.exports = handleUpdatedScriptPayoutFactory; diff --git a/packages/js-drive/lib/identity/masternode/synchronizeMasternodeIdentitiesFactory.js b/packages/js-drive/lib/identity/masternode/synchronizeMasternodeIdentitiesFactory.js new file mode 100644 index 00000000000..56b62457ca1 --- /dev/null +++ b/packages/js-drive/lib/identity/masternode/synchronizeMasternodeIdentitiesFactory.js @@ -0,0 +1,198 @@ +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); +const SimplifiedMNList = require('@dashevo/dashcore-lib/lib/deterministicmnlist/SimplifiedMNList'); +// const Address = require('@dashevo/dashcore-lib/lib/address'); +// const Script = require('@dashevo/dashcore-lib/lib/script'); +// const createOperatorIdentifier = require('./createOperatorIdentifier'); + +/** + * + * @param {DataContractStoreRepository} dataContractRepository + * @param {SimplifiedMasternodeList} simplifiedMasternodeList + * @param {Identifier} masternodeRewardSharesContractId + * @param {handleNewMasternode} handleNewMasternode + * @param {handleUpdatedPubKeyOperator} handleUpdatedPubKeyOperator + * @param {handleRemovedMasternode} handleRemovedMasternode + * @param {handleUpdatedScriptPayout} handleUpdatedScriptPayout + * @param {number} smlMaxListsLimit + * @param {RpcClient} coreRpcClient + * @return {synchronizeMasternodeIdentities} + */ +function synchronizeMasternodeIdentitiesFactory( + dataContractRepository, + simplifiedMasternodeList, + masternodeRewardSharesContractId, + handleNewMasternode, + handleUpdatedPubKeyOperator, + handleRemovedMasternode, + handleUpdatedScriptPayout, + smlMaxListsLimit, + coreRpcClient, +) { + let lastSyncedCoreHeight = 0; + + /** + * @typedef synchronizeMasternodeIdentities + * @param {number} coreHeight + * @return {Promise<{ + * created: Array, + * updated: Array, + * removed: Array, + * fromHeight: number, + * toHeight: number, + * }>} + */ + async function synchronizeMasternodeIdentities(coreHeight) { + let newMasternodes = []; + + let previousMNList = []; + + let updatedEntities = []; + + const currentMNList = simplifiedMasternodeList.getStore() + .getSMLbyHeight(coreHeight) + .mnList; + + const dataContractResult = await dataContractRepository.fetch( + masternodeRewardSharesContractId, + { + useTransaction: true, + }, + ); + + const dataContract = dataContractResult.getValue(); + + if (lastSyncedCoreHeight === 0) { + // Create identities for all masternodes on the first sync + newMasternodes = currentMNList; + } else { + // simplifiedMasternodeList contains sml only for the last `smlMaxListsLimit` number of blocks + if (coreHeight - lastSyncedCoreHeight >= smlMaxListsLimit) { + // get diff directly from core + const { result: rawDiff } = await coreRpcClient.protx('diff', 1, lastSyncedCoreHeight); + + previousMNList = new SimplifiedMNList(rawDiff).mnList; + } else { + previousMNList = simplifiedMasternodeList.getStore() + .getSMLbyHeight(lastSyncedCoreHeight) + .mnList; + } + + // Get the difference between last sync and requested core height + newMasternodes = currentMNList.filter((currentMnListEntry) => ( + !previousMNList.find((previousMnListEntry) => ( + previousMnListEntry.proRegTxHash === currentMnListEntry.proRegTxHash + )) + )); + + // Update operator identities (PubKeyOperator is changed) + for (const mnEntry of currentMNList) { + const previousMnEntry = previousMNList.find((previousMnListEntry) => ( + previousMnListEntry.proRegTxHash === mnEntry.proRegTxHash + && previousMnListEntry.pubKeyOperator !== mnEntry.pubKeyOperator + )); + + if (previousMnEntry) { + updatedEntities = updatedEntities.concat( + await handleUpdatedPubKeyOperator( + mnEntry, + previousMnEntry, + dataContract, + ), + ); + } + + // if (mnEntry.payoutAddress) { + // const mnEntryWithChangedPayoutAddress = previousMNList.find((previousMnListEntry) => ( + // previousMnListEntry.proRegTxHash === mnEntry.proRegTxHash + // && previousMnListEntry.payoutAddress !== mnEntry.payoutAddress + // )); + // + // if (mnEntryWithChangedPayoutAddress) { + // const newPayoutScript = new Script(Address.fromString(mnEntry.payoutAddress)); + // const previousPayoutScript = mnEntryWithChangedPayoutAddress.payoutAddress + // ? new Script(Address.fromString(mnEntryWithChangedPayoutAddress.payoutAddress)) + // : undefined; + // + // await handleUpdatedScriptPayout( + // Identifier.from(Buffer.from(mnEntry.proRegTxHash, 'hex')), + // newPayoutScript, + // previousPayoutScript, + // ); + // } + // } + + // if (mnEntry.operatorPayoutAddress) { + // const mnEntryWithChangedOperatorPayoutAddress = previousMNList + // .find((previousMnListEntry) => ( + // previousMnListEntry.proRegTxHash === mnEntry.proRegTxHash + // && previousMnListEntry.operatorPayoutAddress !== mnEntry.operatorPayoutAddress + // )); + // + // if (mnEntryWithChangedOperatorPayoutAddress) { + // const newOperatorPayoutAddress = Address.fromString(mnEntry.operatorPayoutAddress); + // + // const { operatorPayoutAddress } = mnEntryWithChangedOperatorPayoutAddress; + // + // const previousOperatorPayoutScript = operatorPayoutAddress + // ? new Script(Address.fromString(operatorPayoutAddress)) + // : undefined; + // + // await handleUpdatedScriptPayout( + // createOperatorIdentifier(mnEntry), + // new Script(newOperatorPayoutAddress), + // previousOperatorPayoutScript, + // ); + // } + // } + } + } + + // Create identities and shares for new masternodes + let createdEntities = []; + + for (const newMasternodeEntry of newMasternodes) { + createdEntities = createdEntities.concat( + await handleNewMasternode(newMasternodeEntry, dataContract), + ); + } + + // Remove masternode reward shares for invalid/removed masternodes + + let removedEntities = []; + + const disappearedOrInvalidMasterNodes = previousMNList + .filter((previousMnListEntry) => + // eslint-disable-next-line max-len,implicit-arrow-linebreak + (!currentMNList.find((currentMnListEntry) => currentMnListEntry.proRegTxHash === previousMnListEntry.proRegTxHash))) + .concat(currentMNList.filter((currentMnListEntry) => !currentMnListEntry.isValid)); + + for (const masternodeEntry of disappearedOrInvalidMasterNodes) { + const masternodeIdentifier = Identifier.from( + Buffer.from(masternodeEntry.proRegTxHash, 'hex'), + ); + + removedEntities = removedEntities.concat( + await handleRemovedMasternode( + masternodeIdentifier, + dataContract, + ), + ); + } + + const fromHeight = lastSyncedCoreHeight; + + lastSyncedCoreHeight = coreHeight; + + return { + fromHeight, + toHeight: coreHeight, + createdEntities, + updatedEntities, + removedEntities, + }; + } + + return synchronizeMasternodeIdentities; +} + +module.exports = synchronizeMasternodeIdentitiesFactory; diff --git a/packages/js-drive/lib/state/createInitialStateStructureFactory.js b/packages/js-drive/lib/state/createInitialStateStructureFactory.js new file mode 100644 index 00000000000..b4fa269d3ba --- /dev/null +++ b/packages/js-drive/lib/state/createInitialStateStructureFactory.js @@ -0,0 +1,44 @@ +const SpentAssetLockTransactionsRepository = require('../identity/SpentAssetLockTransactionsRepository'); + +/** + * + * @param {IdentityStoreRepository} identityRepository + * @param {PublicKeyToIdentitiesStoreRepository} publicKeyToIdentitiesRepository + * @param {SpentAssetLockTransactionsRepository} spentAssetLockTransactionsRepository + * @param {DataContractStoreRepository} dataContractRepository + * @param {GroveDBStore} groveDBStore + * @return {createInitialStateStructure} + */ +function createInitialStateStructureFactory( + identityRepository, + publicKeyToIdentitiesRepository, + spentAssetLockTransactionsRepository, + dataContractRepository, + groveDBStore, +) { + /** + * @typedef {createInitialStateStructure} + * @return {Promise} + */ + async function createInitialStateStructure() { + await identityRepository.createTree({ useTransaction: true }); + + await publicKeyToIdentitiesRepository.createTree({ useTransaction: true }); + + await dataContractRepository.createTree({ useTransaction: true }); + + // Create Misc tree + await groveDBStore.createTree( + [], + SpentAssetLockTransactionsRepository.TREE_PATH[0], + { useTransaction: true }, + ); + + // Add spent asset lock tree + await spentAssetLockTransactionsRepository.createTree({ useTransaction: true }); + } + + return createInitialStateStructure; +} + +module.exports = createInitialStateStructureFactory; diff --git a/packages/js-drive/lib/state/registerSystemDataContractFactory.js b/packages/js-drive/lib/state/registerSystemDataContractFactory.js new file mode 100644 index 00000000000..d56a649556b --- /dev/null +++ b/packages/js-drive/lib/state/registerSystemDataContractFactory.js @@ -0,0 +1,88 @@ +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); +const ReadOperation = require('@dashevo/dpp/lib/stateTransition/fee/operations/ReadOperation'); +const DataContractCacheItem = require('../dataContract/DataContractCacheItem'); + +/** + * @param {DashPlatformProtocol} dpp + * @param {IdentityStoreRepository} identityRepository + * @param {DataContractStoreRepository} dataContractRepository + * @param {PublicKeyToIdentitiesStoreRepository} publicKeyToIdentitiesRepository + * @param {BlockExecutionContext} blockExecutionContext + * @param {LRUCache} dataContractCache + * + * @return {registerSystemDataContract} + */ +function registerSystemDataContractFactory( + dpp, + identityRepository, + dataContractRepository, + publicKeyToIdentitiesRepository, + blockExecutionContext, + dataContractCache, +) { + /** + * @typedef registerSystemDataContract + * + * @param {Identifier} ownerId + * @param {Identifier} contractId + * @param {PublicKey} masterPublicKey + * @param {PublicKey} secondPublicKey + * @param {Object} documentDefinitions + * + * @returns {Promise} + */ + async function registerSystemDataContract( + ownerId, + contractId, + masterPublicKey, + secondPublicKey, + documentDefinitions, + ) { + const ownerIdentity = dpp.identity.create( + { + createIdentifier: () => ownerId, + }, + [{ + key: masterPublicKey, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + }, { + key: secondPublicKey, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.HIGH, + }], + ); + + await identityRepository.create(ownerIdentity, { + useTransaction: true, + }); + + await publicKeyToIdentitiesRepository.store(masterPublicKey.hash, ownerId, { + useTransaction: true, + }); + + const dataContract = dpp.dataContract.create( + ownerIdentity.getId(), + documentDefinitions, + ); + + dataContract.id = contractId; + + await dataContractRepository.store(dataContract, { + useTransaction: true, + }); + + // Store data contract in the cache + const cacheItem = new DataContractCacheItem(dataContract, [ + new ReadOperation(dataContract.toBuffer().length), + ]); + + dataContractCache.set(cacheItem.getKey(), cacheItem); + + return dataContract; + } + + return registerSystemDataContract; +} + +module.exports = registerSystemDataContractFactory; diff --git a/packages/js-drive/lib/state/registerTopLevelDomainFactory.js b/packages/js-drive/lib/state/registerTopLevelDomainFactory.js new file mode 100644 index 00000000000..7a11080fe14 --- /dev/null +++ b/packages/js-drive/lib/state/registerTopLevelDomainFactory.js @@ -0,0 +1,59 @@ +/** + * @param {DashPlatformProtocol} dpp + * @param {DocumentRepository} documentRepository + * @param {Identifier} dashDomainDocumentId + * @param {Buffer} dashPreorderSalt + * + * @return {registerTopLevelDomain} + */ +function registerTopLevelDomainFactory( + dpp, + documentRepository, + // dashPreorderDocumentId, + dashDomainDocumentId, + dashPreorderSalt, +) { + /** + * @typedef registerTopLevelDomain + * + * @param {string} name + * @param {DataContract} dataContract + * @param {Identifier} ownerId + * @param {Date} genesisDate + * + * @return {Promise} + */ + async function registerTopLevelDomain(name, dataContract, ownerId, genesisDate) { + const normalizedParentDomainName = ''; + const normalizedLabel = name.toLowerCase(); + + const domainDocument = await dpp.document.create( + dataContract, + ownerId, + 'domain', + { + label: name, + normalizedLabel, + normalizedParentDomainName, + preorderSalt: dashPreorderSalt, + records: { + dashAliasIdentityId: ownerId, + }, + subdomainRules: { + allowSubdomains: true, + }, + }, + ); + + domainDocument.id = dashDomainDocumentId; + domainDocument.createdAt = genesisDate; + + await documentRepository.create(domainDocument, { + useTransaction: true, + }); + } + + return registerTopLevelDomain; +} + +module.exports = registerTopLevelDomainFactory; diff --git a/packages/js-drive/lib/storage/GroveDBStore.js b/packages/js-drive/lib/storage/GroveDBStore.js new file mode 100644 index 00000000000..8e505e6862a --- /dev/null +++ b/packages/js-drive/lib/storage/GroveDBStore.js @@ -0,0 +1,572 @@ +const { createHash } = require('crypto'); +const WriteOperation = require('@dashevo/dpp/lib/stateTransition/fee/operations/WriteOperation'); +const ReadOperation = require('@dashevo/dpp/lib/stateTransition/fee/operations/ReadOperation'); +const DeleteOperation = require('@dashevo/dpp/lib/stateTransition/fee/operations/DeleteOperation'); +const StorageResult = require('./StorageResult'); + +class GroveDBStore { + /** + * @param {Drive} rsDrive + * @param {Object} [logger] + */ + constructor(rsDrive, logger = undefined) { + this.rsDrive = rsDrive; + this.db = rsDrive.getGroveDB(); + this.logger = logger; + } + + /** + * Store a key + * + * @param {Buffer[]} path + * @param {Buffer} key + * @param {Buffer} value + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.skipIfExists=false] + * @param {boolean} [options.dryRun=false] + * + * @return {Promise>} + */ + async put(path, key, value, options = {}) { + const method = options.skipIfExists ? 'insertIfNotExists' : 'insert'; + + try { + if (!options.dryRun) { + await this.db[method]( + path, + key, + { + type: 'item', + epoch: 0, + value, + }, + options.useTransaction || false, + ); + } + } finally { + if (this.logger) { + this.logger.info({ + path: path.map((segment) => segment.toString('hex')), + pathHash: createHash('sha256') + .update( + path.reduce((segment, buffer) => Buffer.concat([segment, buffer]), Buffer.alloc(0)), + ).digest('hex'), + key: key.toString('hex'), + value: value.toString('hex'), + valueHash: createHash('sha256') + .update(value) + .digest('hex'), + useTransaction: Boolean(options.useTransaction), + type: 'item', + method, + appHash: (await this.getRootHash(options)).toString('hex'), + }, 'put'); + } + } + + return new StorageResult( + undefined, + [new WriteOperation(key.length, value.length)], + ); + } + + /** + * Store a reference to the specified key + * + * @param {Buffer[]} path + * @param {Buffer} key + * @param {Buffer[]} referencePath + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.skipIfExists=false] + * @param {boolean} [options.dryRun=false] + * @return {Promise>} + */ + async putReference(path, key, referencePath, options = {}) { + const method = options.skipIfExists ? 'insertIfNotExists' : 'insert'; + + try { + if (!options.dryRun) { + await this.db[method]( + path, + key, + { + type: 'reference', + epoch: 0, + value: referencePath, + }, + options.useTransaction || false, + ); + } + } finally { + if (this.logger) { + this.logger.info({ + path: path.map((segment) => segment.toString('hex')), + pathHash: createHash('sha256') + .update( + path.reduce((segment, buffer) => Buffer.concat([segment, buffer]), Buffer.alloc(0)), + ) + .digest('hex'), + key: key.toString('hex'), + value: referencePath.map((segment) => segment.toString('hex')), + valueHash: createHash('sha256') + .update( + referencePath.reduce((segment, buffer) => ( + Buffer.concat([segment, buffer]) + ), Buffer.alloc(0)), + ) + .digest('hex'), + useTransaction: Boolean(options.useTransaction), + type: 'reference', + method, + appHash: (await this.getRootHash(options)).toString('hex'), + }, 'putReference'); + } + } + + return new StorageResult( + undefined, + [ + new WriteOperation( + key.length, + referencePath.reduce((size, pathItem) => size + pathItem.length, 0), + ), + ], + ); + } + + /** + * Create empty key + * + * @param {Buffer[]} path + * @param {Buffer} key + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.skipIfExists=false] + * @param {boolean} [options.dryRun=false] + * @return {Promise>} + */ + async createTree(path, key, options = { }) { + const method = options.skipIfExists ? 'insertIfNotExists' : 'insert'; + + try { + if (!options.dryRun) { + await this.db[method]( + path, + key, + { + type: 'tree', + epoch: 0, + value: Buffer.alloc(32), + }, + options.useTransaction || false, + ); + } + } finally { + if (this.logger) { + this.logger.info({ + path: path.map((segment) => segment.toString('hex')), + pathHash: createHash('sha256') + .update( + path.reduce((segment, buffer) => Buffer.concat([segment, buffer]), Buffer.alloc(0)), + ).digest('hex'), + key: key.toString('hex'), + value: Buffer.alloc(32).toString('hex'), + valueHash: createHash('sha256') + .update(Buffer.alloc(32)) + .digest('hex'), + useTransaction: Boolean(options.useTransaction), + type: 'tree', + method, + appHash: (await this.getRootHash(options)).toString('hex'), + }, 'createTree'); + } + } + + return new StorageResult( + undefined, + [ + new WriteOperation( + key.length, + 32, + ), + ], + ); + } + + /** + * Get a value by key + * + * @param {Buffer[]} path + * @param {Buffer} key + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * @param {number} [options.predictedValueSize] + * @return {Promise>} + */ + async get(path, key, options = { }) { + let type; + let value; + + try { + if (!options.dryRun) { + ({ + type, + value, + } = await this.db.get( + path, + key, + options.useTransaction || false, + )); + } + } catch (e) { + if ( + e.message.startsWith('path key not found') + || e.message.startsWith('path not found') + ) { + return new StorageResult( + null, + [new ReadOperation(0)], + ); + } + + throw e; + } + + if (type === undefined) { + const valueSize = options.dryRun ? (options.predictedValueSize || 0) : 0; + + return new StorageResult( + null, + [new ReadOperation(valueSize)], + ); + } + + if (type !== 'item') { + throw new Error('Key should point to item element type'); + } + + return new StorageResult( + value, + [new ReadOperation(value.length)], + ); + } + + /** + * Query keys and values + * + * @param {PathQuery} query + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * @return {Promise>} + */ + async query(query, options = { }) { + let items; + + try { + if (!options.dryRun) { + [items] = await this.db.query( + query, + options.useTransaction || false, + ); + } + } catch (e) { + if ( + e.message.startsWith('path key not found') + || e.message.startsWith('path not found') + ) { + return new StorageResult( + null, + [new ReadOperation(0)], + ); + } + + throw e; + } + + return new StorageResult( + items, + [new ReadOperation(0)], + ); + } + + /** + * Prove query + * + * @param {PathQuery} query + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @return {Promise>} + * */ + async proveQuery(query, options = {}) { + const proof = await this.db.proveQuery( + query, + options.useTransaction || false, + ); + + return new StorageResult( + proof, + [new ReadOperation(0)], + ); + } + + /** + * Prove many queries + * + * @param {PathQuery[]} queries + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @return {Promise>} + * */ + async proveQueryMany(queries, options = {}) { + const proof = await this.db.proveQueryMany( + queries, + options.useTransaction || false, + ); + + return new StorageResult( + proof, + [new ReadOperation(0)], + ); + } + + /** + * Delete value by key + * + * @param {Buffer[]} path + * @param {Buffer} key + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * @return {Promise>} + */ + async delete(path, key, options = {}) { + try { + if (!options.dryRun) { + await this.db.delete( + path, + key, + options.useTransaction || false, + ); + } + } finally { + if (this.logger) { + this.logger.info({ + path: path.map((segment) => segment.toString('hex')), + pathHash: createHash('sha256') + .update( + path.reduce((segment, buffer) => Buffer.concat([segment, buffer]), Buffer.alloc(0)), + ).digest('hex'), + key: key.toString('hex'), + useTransaction: Boolean(options.useTransaction), + method: 'delete', + appHash: (await this.getRootHash(options)).toString('hex'), + }, 'delete'); + } + } + + return new StorageResult( + undefined, + [new DeleteOperation(key.length, 0)], + ); + } + + /** + * Get auxiliary value by key + * + * @param {Buffer} key + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * @param {boolean} [options.predictedValueSize] + * @return {Promise>} + */ + async getAux(key, options = {}) { + let result = null; + + try { + if (!options.dryRun) { + result = await this.db.getAux( + key, + options.useTransaction || false, + ); + } + } catch (e) { + if (e.message.startsWith('path key not found')) { + return new StorageResult( + null, + [ + new ReadOperation(result ? result.length : 0), + ], + ); + } + + throw e; + } + + let valueSize = result ? result.length : 0; + + if (options.dryRun) { + valueSize = options.predictedValueSize; + } + + return new StorageResult( + result, + [ + new ReadOperation(valueSize), + ], + ); + } + + /** + * Store auxiliary value by key + * + * @param {Buffer} key + * @param {Buffer} value + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * @return {Promise>} + */ + async putAux(key, value, options = {}) { + try { + if (!options.dryRun) { + await this.db.putAux( + key, + value, + options.useTransaction || false, + ); + } + } finally { + if (this.logger) { + this.logger.info({ + key: key.toString('hex'), + value: value.toString('hex'), + valueHash: createHash('sha256') + .update(value) + .digest('hex'), + useTransaction: Boolean(options.useTransaction), + method: 'putAux', + appHash: (await this.getRootHash(options)).toString('hex'), + }, 'putAux'); + } + } + + return new StorageResult( + undefined, + [ + new WriteOperation(key.length, value.length), + ], + ); + } + + /** + * Delete auxiliary value by key + * + * @param {Buffer} key + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @param {boolean} [options.dryRun=false] + * @return {Promise>} + */ + async deleteAux(key, options = {}) { + try { + if (!options.dryRun) { + await this.db.deleteAux( + key, + options.useTransaction || false, + ); + } + } finally { + if (this.logger) { + this.logger.info({ + key: key.toString('hex'), + useTransaction: Boolean(options.useTransaction), + method: 'deleteAux', + appHash: (await this.getRootHash(options)).toString('hex'), + }, 'deleteAux'); + } + } + + return new StorageResult( + undefined, + [ + new DeleteOperation(key.length, 0), + ], + ); + } + + /** + * Get tree root hash + * + * @param {Object} [options] + * @param {boolean} [options.useTransaction=false] + * @return {Buffer} + */ + async getRootHash(options = {}) { + return this.db.getRootHash(options.useTransaction || false); + } + + /** + * @return {Promise} + */ + async startTransaction() { + return this.db.startTransaction(); + } + + /** + * @return {Promise} + */ + async isTransactionStarted() { + return this.db.isTransactionStarted(); + } + + /** + * Rollback transaction to this initial state when it was created + * + * @returns {Promise} + */ + async rollbackTransaction() { + return this.db.rollbackTransaction(); + } + + /** + * @return {Promise} + */ + async commitTransaction() { + return this.db.commitTransaction(); + } + + /** + * @return {Promise} + */ + async abortTransaction() { + return this.db.abortTransaction(); + } + + /** + * @return {Drive} + */ + getDrive() { + return this.rsDrive; + } + + /** + * @returns {GroveDB} + */ + getDB() { + return this.db; + } + + /** + * @param {GroveDB} db + */ + setDB(db) { + this.db = db; + } +} + +module.exports = GroveDBStore; diff --git a/packages/js-drive/lib/storage/StorageResult.js b/packages/js-drive/lib/storage/StorageResult.js new file mode 100644 index 00000000000..fb548802ef5 --- /dev/null +++ b/packages/js-drive/lib/storage/StorageResult.js @@ -0,0 +1,68 @@ +/** + * @template T + */ +class StorageResult { + /** + * @type {T} + */ + #value; + + /** + * @type {AbstractOperation[]} + */ + #operations; + + /** + * @template T + * @param {T} value + * @param {AbstractOperation[]} operations + */ + constructor(value, operations = []) { + this.#value = value; + this.#operations = operations; + } + + /** + * @return {T} + */ + getValue() { + return this.#value; + } + + /** + * @param {T} value + */ + setValue(value) { + this.#value = value; + } + + /** + * @return {AbstractOperation[]} + */ + getOperations() { + return this.#operations; + } + + /** + * @return {boolean} + */ + isNull() { + return this.#value === null || this.#value === undefined; + } + + /** + * @return {boolean} + */ + isEmpty() { + return this.isNull() || (Array.isArray(this.#value) && this.#value.length === 0); + } + + /** + * @param {AbstractOperation} operation + */ + addOperation(...operation) { + this.#operations.push(...operation); + } +} + +module.exports = StorageResult; diff --git a/packages/js-drive/lib/storage/rotateSignedStoreFactory.js b/packages/js-drive/lib/storage/rotateSignedStoreFactory.js new file mode 100644 index 00000000000..6e7699759e6 --- /dev/null +++ b/packages/js-drive/lib/storage/rotateSignedStoreFactory.js @@ -0,0 +1,43 @@ +const fs = require('fs'); + +/** + * @param {GroveDBStore} groveDBStore + * @param {GroveDBStore} signedGroveDBStore + * @param {string} dbPath + * @return {rotateSignedStore} + */ +function rotateSignedStoreFactory(groveDBStore, signedGroveDBStore, dbPath) { + /** + * @typedef {rotateSignedStore} + * @param {Long} height + * @returns {Promise} + */ + async function rotateSignedStore(height) { + if (height.lessThanOrEqual(2)) { + return false; + } + + const signedStateHeight = height.subtract(2); + const signedStatePath = `${dbPath}/signed_state_${signedStateHeight}`; + + const previousSignedStateHeight = signedStateHeight.subtract(1); + const previousSignedStatePath = `${dbPath}/signed_state_${previousSignedStateHeight}`; + + const newSignedGroveDB = groveDBStore.checkpoint(signedStatePath); + const previousSignedGroveDB = signedGroveDBStore.getDB(); + + if (previousSignedStateHeight.greaterThan(0)) { + await previousSignedGroveDB.close(); + + fs.rmSync(previousSignedStatePath, { recursive: true }); + } + + signedGroveDBStore.setDB(newSignedGroveDB); + + return true; + } + + module.exports = rotateSignedStore; +} + +module.exports = rotateSignedStoreFactory; diff --git a/packages/js-drive/lib/test/.eslintrc b/packages/js-drive/lib/test/.eslintrc new file mode 100644 index 00000000000..4c2b11fe817 --- /dev/null +++ b/packages/js-drive/lib/test/.eslintrc @@ -0,0 +1,9 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + } +} diff --git a/packages/js-drive/lib/test/bootstrap.js b/packages/js-drive/lib/test/bootstrap.js new file mode 100644 index 00000000000..069c7bd80b5 --- /dev/null +++ b/packages/js-drive/lib/test/bootstrap.js @@ -0,0 +1,66 @@ +const path = require('path'); +const dotenvSafe = require('dotenv-safe'); +const dotenvExpand = require('dotenv-expand'); +const { expect, use } = require('chai'); +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); +const dirtyChai = require('dirty-chai'); +const chaiAsPromised = require('chai-as-promised'); +const chaiString = require('chai-string'); + +use(sinonChai); +use(chaiAsPromised); +use(chaiString); +use(dirtyChai); + +process.env.NODE_ENV = 'test'; + +const testPublicKey = '029470f30d543c500558080bf953f96f4beda8ce3c7e00965891913e586f682bb4'; + +// Workaround for dotenv-safe +if (process.env.INITIAL_CORE_CHAINLOCKED_HEIGHT === undefined) { + process.env.INITIAL_CORE_CHAINLOCKED_HEIGHT = testPublicKey; +} +if (process.env.DPNS_MASTER_PUBLIC_KEY === undefined) { + process.env.DPNS_MASTER_PUBLIC_KEY = testPublicKey; +} +if (process.env.DPNS_SECOND_PUBLIC_KEY === undefined) { + process.env.DPNS_SECOND_PUBLIC_KEY = testPublicKey; +} +if (process.env.DASHPAY_MASTER_PUBLIC_KEY === undefined) { + process.env.DASHPAY_MASTER_PUBLIC_KEY = testPublicKey; +} +if (process.env.DASHPAY_SECOND_PUBLIC_KEY === undefined) { + process.env.DASHPAY_SECOND_PUBLIC_KEY = testPublicKey; +} +if (process.env.FEATURE_FLAGS_MASTER_PUBLIC_KEY === undefined) { + process.env.FEATURE_FLAGS_MASTER_PUBLIC_KEY = testPublicKey; +} +if (process.env.FEATURE_FLAGS_SECOND_PUBLIC_KEY === undefined) { + process.env.FEATURE_FLAGS_SECOND_PUBLIC_KEY = testPublicKey; +} +if (process.env.MASTERNODE_REWARD_SHARES_MASTER_PUBLIC_KEY === undefined) { + process.env.MASTERNODE_REWARD_SHARES_MASTER_PUBLIC_KEY = testPublicKey; +} +if (process.env.MASTERNODE_REWARD_SHARES_SECOND_PUBLIC_KEY === undefined) { + process.env.MASTERNODE_REWARD_SHARES_SECOND_PUBLIC_KEY = testPublicKey; +} + +const dotenvConfig = dotenvSafe.config({ + path: path.resolve(__dirname, '..', '..', '.env'), +}); +dotenvExpand(dotenvConfig); + +beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + +afterEach(function afterEach() { + this.sinon.restore(); +}); + +global.expect = expect; diff --git a/packages/js-drive/lib/test/createTestDIContainer.js b/packages/js-drive/lib/test/createTestDIContainer.js new file mode 100644 index 00000000000..8a768500898 --- /dev/null +++ b/packages/js-drive/lib/test/createTestDIContainer.js @@ -0,0 +1,31 @@ +const createDIContainer = require('../createDIContainer'); + +async function createTestDIContainer(dashCore = undefined) { + let coreOptions = {}; + if (dashCore) { + coreOptions = { + CORE_JSON_RPC_HOST: '127.0.0.1', + CORE_JSON_RPC_PORT: dashCore.options.getRpcPort(), + CORE_JSON_RPC_USERNAME: dashCore.options.getRpcUser(), + CORE_JSON_RPC_PASSWORD: dashCore.options.getRpcPassword(), + }; + } + + const container = createDIContainer({ + ...process.env, + GROVEDB_LATEST_FILE: './db/latest_state_test', + EXTERNAL_STORE_LEVEL_DB_FILE: './db/external_leveldb_test', + SIGNED_EXTERNAL_STORE_LEVEL_DB_FILE: './db/external_leveldb_signed', + ...coreOptions, + }); + + const dpp = container.resolve('dpp'); + const transactionalDpp = container.resolve('transactionalDpp'); + + await dpp.initialize(); + await transactionalDpp.initialize(); + + return container; +} + +module.exports = createTestDIContainer; diff --git a/packages/js-drive/lib/test/fixtures/createDataContractDocuments.js b/packages/js-drive/lib/test/fixtures/createDataContractDocuments.js new file mode 100644 index 00000000000..42aa2c7976c --- /dev/null +++ b/packages/js-drive/lib/test/fixtures/createDataContractDocuments.js @@ -0,0 +1,28 @@ +const dataContractMetaSchema = require('@dashevo/dpp/schema/dataContract/dataContractMeta.json'); +const createIndices = require('./createIndices'); +const createProperties = require('./createProperties'); + +/** + * + * @param {number} count + * @returns {Object} + */ +function createDataContractDocuments(count = 2) { + const documents = {}; + + for (let i = 0; i < count; i++) { + documents[`doc${i}`] = { + type: 'object', + indices: createIndices(dataContractMetaSchema.$defs.documentProperties.maxProperties, true), + properties: createProperties(dataContractMetaSchema.$defs.documentProperties.maxProperties, { + type: 'string', + maxLength: 63, + }), + additionalProperties: false, + }; + } + + return documents; +} + +module.exports = createDataContractDocuments; diff --git a/packages/js-drive/lib/test/fixtures/createIndices.js b/packages/js-drive/lib/test/fixtures/createIndices.js new file mode 100644 index 00000000000..9cca689b728 --- /dev/null +++ b/packages/js-drive/lib/test/fixtures/createIndices.js @@ -0,0 +1,36 @@ +/** + * @param {number} count + * @param {boolean} [unique=false] + */ +function createIndices(count, unique = false) { + const indices = []; + + const indexCount = (count < 10 ? count : 10); + + let propertyIndex = 0; + + const basePropertyCount = Math.floor(count / indexCount); + const propertyLeftovers = count % indexCount; + + for (let i = 0; i < indexCount; i++) { + const properties = []; + + for (let x = 0; x < basePropertyCount + ((i < propertyLeftovers) ? 1 : 0); x++) { + const name = `property${propertyIndex}`; + + propertyIndex++; + + properties.push({ [name]: 'asc' }); + } + + indices.push({ + name: `index${i}`, + properties, + unique: unique && i < 3, + }); + } + + return indices; +} + +module.exports = createIndices; diff --git a/packages/js-drive/lib/test/fixtures/createProperties.js b/packages/js-drive/lib/test/fixtures/createProperties.js new file mode 100644 index 00000000000..11ba4876908 --- /dev/null +++ b/packages/js-drive/lib/test/fixtures/createProperties.js @@ -0,0 +1,17 @@ +/** + * @param {number} count + * @param {Object} subSchema + */ +function createProperties(count, subSchema) { + const properties = {}; + + for (let i = 0; i < count; i++) { + const name = `property${i}`; + + properties[name] = subSchema; + } + + return properties; +} + +module.exports = createProperties; diff --git a/packages/js-drive/lib/test/fixtures/getBlockExecutionContextObjectFixture.js b/packages/js-drive/lib/test/fixtures/getBlockExecutionContextObjectFixture.js new file mode 100644 index 00000000000..868661fe966 --- /dev/null +++ b/packages/js-drive/lib/test/fixtures/getBlockExecutionContextObjectFixture.js @@ -0,0 +1,66 @@ +const { + tendermint: { + abci: { + LastCommitInfo, + }, + types: { + Header, + }, + }, + google: { + protobuf: { + Timestamp, + }, + }, +} = require('@dashevo/abci/types'); + +const pino = require('pino'); + +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); + +/** + * @param {DataContract} [dataContract] + * @return {{ + * dataContracts: Object[], + * lastCommitInfo, + * invalidTxs: number, + * header: Object, + * validTxs: number, + * cumulativeFees: number, + * consensusLogger: Logger, + * }} + */ +function getBlockExecutionContextObjectFixture(dataContract = getDataContractFixture()) { + const lastCommitInfo = new LastCommitInfo({ + quorumHash: Buffer.from('000003c60ecd9576a05a7e15d93baae18729cb4477d44246093bd2cf8d4f53d8', 'hex'), + blockSignature: Buffer.from('003657bb44d74c371d14485117de43313ca5c2848f3622d691c2b1bf3576a64bdc2538efab24854eb82ae7db38482dbd15a1cb3bc98e55173817c9d05c86e47a5d67614a501414aae6dd1565e59422d1d77c41ae9b38de34ecf1e9f778b2a97b', 'hex'), + stateSignature: Buffer.from('09c3e46f5bc1abcb7c130b8c36a168e1fbc471fa86445dfce49e151086a277216e7a5618a7554b823d995c5606d0642f18f9c4caa249605d2ab156e14728c82f58f9008d4bcc6e21e0a561e3185e2ae654605613e86af507ca49079595872532', 'hex'), + }); + + const header = new Header({ + height: 10, + time: new Timestamp({ + seconds: Math.ceil(new Date().getTime() / 1000), + nanos: 0, + }), + }); + + const cumulativeFees = 10; + + const logger = pino(); + + const validTxs = 2; + const invalidTxs = 1; + + return { + dataContracts: [dataContract.toObject()], + lastCommitInfo: LastCommitInfo.toObject(lastCommitInfo), + cumulativeFees, + header: Header.toObject(header), + validTxs, + invalidTxs, + consensusLogger: logger, + }; +} + +module.exports = getBlockExecutionContextObjectFixture; diff --git a/packages/js-drive/lib/test/fixtures/getSmlFixture.js b/packages/js-drive/lib/test/fixtures/getSmlFixture.js new file mode 100644 index 00000000000..6083ed8eb3d --- /dev/null +++ b/packages/js-drive/lib/test/fixtures/getSmlFixture.js @@ -0,0 +1,3374 @@ +module.exports = function getSmlFixture() { + return [ + { + baseBlockHash: '0000047d24635e347be3aaaeb66c26be94901a2f962feccd4f95090191f208c1', + blockHash: '00000ac05a06682172d8b49be7c9ddc4189126d7200ebf0fc074c433ae74b596', + cbTxMerkleTree: '01000000013ea54aa6ca7985a23943b7b1ac2cfb168516cfb1f5fe0f2f876a470dc7f526fc0101', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603d803060108ffffffff02eefccf31000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac65fbb74a000000001976a9140424c6fa140348b7a72fad83400a8b08f5bffaca88ac00000000460200d803060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff0f5943a9c52d6ff829b170022a954b952a4542d8bebd94dc2a025f1d1969a27c', + deletedMNs: [ + ], + mnList: [ + { + proRegTxHash: '5557273f5922d9925e2327908ddb128bcf8e055a04d86e23431809bedd077060', + confirmedHash: '0000003da09fd100c60ad5743c44257bb9220ad8162a9b6cae9d005c8e465dba', + service: '95.222.25.60:19997', + pubKeyOperator: '08b66151b81bd6a08bad2e68810ea07014012d6d804859219958a7fbc293689aa902bd0cd6db7a4699c9e88a4ae8c2c0', + votingAddress: 'yZRteAQ51BoeD3sJL1iGdt6HJLgkWGurw5', + isValid: false, + }, + { + proRegTxHash: '85412e8586e7e2015db1d2e9b4dd380e89251ed812e40bf8d5e220ee40bc18a0', + confirmedHash: '00000000051d7e2ea0d1021a75861d968ac553c592bd003e125fce8dfda6cac5', + service: '167.71.223.212:19998', + pubKeyOperator: '80b7defb6341399f9e9b4ed7c2d627fc828d0eff9c168165b75b24e5fc6c3f5bc8a9eeaee2bc655fdaa58c0d2f3b1b94', + votingAddress: 'yTCALGQTFNsA4pMPLTKAWdaLRmxfGpbujY', + isValid: false, + }, + { + proRegTxHash: '7a46d4cbb10b3834c9bbde18829db32c206d8b123c27604a139034597102dd60', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26021', + pubKeyOperator: '1503175cf5df6348af9f6f9c397aed76bd1bc1a1486ae5440721db5a8a6e802f45d8a2aaa2fa30960d1402df7802809b', + votingAddress: 'ycemHo8hrgkQEuKgJbYDfVw5CvvWcgAwzK', + isValid: true, + }, + { + proRegTxHash: '34f19e4ac7e1b2abbded7fe0d19991cde34eb7797d8e81fe01d6e73db2097180', + confirmedHash: '000000002e15489f059ee6bd649f906ebeebb1d4291afb5cb24a3f87edefd69b', + service: '3.20.70.18:10003', + pubKeyOperator: '0fb7164d86058e2b22c4a6f6917714dfa4a2cb4d54bebbf3c9300ebfe1759b33d15b0b68e32999aae19bf0dd92341e40', + votingAddress: 'yP2swcUzQ7MHtaubyg3uKrRcM7oWER3X9Q', + isValid: false, + }, + { + proRegTxHash: 'ab51b2ba4dca27658e13fea81c0764167c1466aa2d92050c67e4490ce7623da0', + confirmedHash: '00000d8e4cadff81c56eaaa7d5afe4a582a5023a683a9cae59945bf654504bd9', + service: '167.99.164.60:19999', + pubKeyOperator: '8072ac9a55d1cf5bf9c4262d49e2ef1ffcd716b8983ffdc62b940fec6cb4179d6275f8b68316f29c6c2ad540db329258', + votingAddress: 'yVpKfQgjkRkezFS5SpZvAEVFsbv9zJedf4', + isValid: false, + }, + { + proRegTxHash: '79e51449f47f3501d7dd466ebfa0fd3d686d75f672f2eb5cbb321db92f67d200', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26080', + pubKeyOperator: '8a20e40690e9f804806d7685b84f16d3ac312fc50e5730fb5b7b234b4992f59f3e05fbc4f58dd5fdd82bc934b6f982e0', + votingAddress: 'yh4LrSGw7taLrTLsbYKyzx2Sgru5kYR8Ci', + isValid: true, + }, + { + proRegTxHash: '5cd86ed16f87819dca7b6e4e3d24947b1a6328ed8cc4c9aec7af35fa2b162220', + confirmedHash: '0000052c9685b51f2143792ed6b2f5cda84e5b1bdb7783268729114e8d0d08f6', + service: '68.183.167.16:19999', + pubKeyOperator: '18af4d035eed23d30eb02808af0c133d9879c0fb82c72329ab2ed208ebc1631641ca42bbf462239d151f4e84d8dcde7b', + votingAddress: 'yLvTNLDLHa3pDMbFDRBX5mVMjCshzrDD1X', + isValid: false, + }, + { + proRegTxHash: '9b2e4d4a2fd4dad4b5a5b7d38b0af9a09f71c680822fa88bbc2cb086e5ffd300', + confirmedHash: '0000000000962d68dfab6a7cc716753923efdd35f5f422a7dfd2ee7a34d026db', + service: '3.213.168.240:19999', + pubKeyOperator: '1301934c2416b61edcf2c37a4b09fe6916aa77b79d2d41e532e9e02dbfde8ce492110de4e762c0a78f70194a435d199c', + votingAddress: 'yZJbXf7fVjKyarvYwyD7wmehS3tdSUo2SY', + isValid: false, + }, + { + proRegTxHash: '39a1339d9bf26de701345beecc5de75a690bc9533741a3dbe90f2fd88b8ed461', + confirmedHash: '0000030c37a946836029eeca338604c652e3c6cd368eb54bdfa8553213954f74', + service: '198.199.74.241:19999', + pubKeyOperator: '0efda51589f86e30cc2305e7388c01ce0309c19a182cf37bced97c7da72236f660c0a395e765e6e06962ecff5a69d7de', + votingAddress: 'yRCunhZVjbMxDr1C6fD6Pf37sTwH6wG7Uu', + isValid: false, + }, + { + proRegTxHash: 'c6eee81fd38e6db24cb5e847794cefca7f3f8f95a066028bb8dfd6f36fb92921', + confirmedHash: '0000000000d9ba93b25a135dc9ccba58d94ec50c7bdd62c20bb800a9c5486690', + service: '104.248.242.126:19999', + pubKeyOperator: '050f3a743867bf78d2e9a3906d15d8400d8d58255771d12828922386e8685f8aeccb8d9d81153f9c2d7da0436a71fe55', + votingAddress: 'yRRwW957BJwL6SVVh3s8ASQYa2qXnduyfx', + isValid: false, + }, + { + proRegTxHash: '0569ce8b1a5fddf85850b5415b0435c46e198a8f146b1344bd618c8fc6e9e541', + confirmedHash: '0000000001483699d4d1fde497302840c8d3e7b2ae5c0c78be452f5b5c816436', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yUYKo97qTRw25frwj8FmYdQE53hbCM7MhG', + isValid: false, + }, + { + proRegTxHash: '19eca6afa8467e8ff25054473a7e97a0f9198c0209e9b97f32984a536a1b6d81', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26092', + pubKeyOperator: '0d0c8bd6f610d824f206ba7b89f6759bc046c64b45935001f0ff2de957fa27a9f57a82ad48eb1c1537a84c8600cbda1f', + votingAddress: 'yYF9NYF9jrEZXURuDUFnZ5vERWj7P21PPj', + isValid: true, + }, + { + proRegTxHash: 'c95f78ad5dab4ceaa98ff2d9d60d6d69e741f554b3ff876998dd832b2255a5c1', + confirmedHash: '00000000105d464362d37247fb09e946cc91b33dc909e690abf3f0ce80c12829', + service: '45.32.86.231:19999', + pubKeyOperator: '128e20333b8b51fb8c72c2d5acafa049758b53279bf78f10a1f32995bd05d4f6313b2bd67fbc48379455d89ef869fa6e', + votingAddress: 'yVbDZi6bank9eBLRr1X7JXybSNKnziiPfM', + isValid: false, + }, + { + proRegTxHash: 'df337c675939dcef69332161ef1d90a7b6f75f42d70fa017b3d6dbc1b85611e1', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26016', + pubKeyOperator: '16f66304e30d61cb86427d9f0d3387c6221afb5fa94cabe0f5c3479bcf184a6193a95cd271210d68d435fe4b0c1cf95b', + votingAddress: 'yPUdNBzsPaBugQwx5i9VcXUFNcafTzBk1A', + isValid: true, + }, + { + proRegTxHash: '9fe2f8d43c11c61b5a545f451d5f9ffb89bed5bd91f43988eb97ce9a33692281', + confirmedHash: '0000000001f20727768a67e1e27826bd844d79b5d9869439b3f533f45c9b05a5', + service: '78.46.161.22:19999', + pubKeyOperator: '80174252e0f66a71b7e53f8b32dea5f97a6b39dfc1479c6e355daa415b1ff7733a4bea6bbe3fdf412fed0fb60e5b71d7', + votingAddress: 'yXshqW8dY2BirzasuA7paj6pCS7xd6oR7A', + isValid: false, + }, + { + proRegTxHash: '0d5c9fd099033cc89b3d0c93e71b9bb703c7d5ae7b14cd23cd82c38cf2e25b21', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26093', + pubKeyOperator: '94e6a117d2af7958dac4cf005999078bff9cb2ea8c946ccbca04a9be3ba0e4969ab7f90020a7e271a4e06a9d05c73d3e', + votingAddress: 'yT7uzfZEhfY9SRZrQVn94msh4kQroyosQA', + isValid: true, + }, + { + proRegTxHash: 'b4ba4ab73c3757ba9cc6b6fb98020b854228be7de4704ceb7da02e7c6a2ad741', + confirmedHash: '0000000009b1ca289f613565a9fd852b2a795821c4bdc0ee381ac2ffa5d258c0', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yRwm9ZgrJ5YMY2aQhhK7R5HZqptpd5KwUb', + isValid: false, + }, + { + proRegTxHash: 'f129a2035414a54881224bb0926390bef90b8bfcc63fd2757ae95f07fc9cb381', + confirmedHash: '00000000031be3080f65deb5f857d6502852ca5525dfc855bb31031c2e68417f', + service: '167.71.223.212:19999', + pubKeyOperator: '84657bff1dbf81b2aa50e385d01549f9c3683994ab0b16d5b7e3ede8efe95992bb621ec221c5003d2f9f26fa190ffb2a', + votingAddress: 'ygw2ahuKPBhCH8tRZQ9ShEe826vT6Re8Fd', + isValid: false, + }, + { + proRegTxHash: 'e558c21609f13196f38a0e135c8a56ee4632ea1681a9dedd5d65ae8031b34be1', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26006', + pubKeyOperator: '005d1f334fabccd08847756effff3116eece973c077e3acd1aa936f4e51293fa8753de661dc7a03edde24714eb7acdcd', + votingAddress: 'yNbb9wQzp14iidj6JgCzfGmRmFXv4fRNUo', + isValid: true, + }, + { + proRegTxHash: 'fef106ff6420f9c6638c9676988a8fc655750caafb506c98cb5ff3d4fea99a41', + confirmedHash: '0000000005d5635228f113b50fb5ad66995a7476ed20374e6e159f1f9e62347b', + service: '45.48.168.16:19999', + pubKeyOperator: '842476e8d82327adfb9b617a7ac3f62868946c0c4b6b0e365747cfb8825b8b79ba0eb1fa62e8583ae7102f59bf70c7c7', + votingAddress: 'yf7QHemCfbmKEncwZxroTj8JtShXsC28V6', + isValid: false, + }, + { + proRegTxHash: '86b8061fb7fe866b492b84e85aa0548f68ff376c4cbc5893e46ae361a5e57241', + confirmedHash: '00000012b002b15f3b0e003502f37b181f158efa3392de3139cfffa4f79fafbb', + service: '109.235.71.56:19999', + pubKeyOperator: '8d1412ff39045ef39c2e19a75cb3ad986afc14c3139ed0a3392b41d471558676029a8137f95b0ba0e7315bf11c497f0f', + votingAddress: 'yeZknaGXQ3Sf7o22MxByRzeYdbRK2JKPDu', + isValid: false, + }, + { + proRegTxHash: '1345ef115b88dcfe8f6735b26c1aec1faa85474004fe1dcc8263ded2393bf062', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26057', + pubKeyOperator: '0eec71c1fdfdda9e5c31a27cb459d548284f805bb5f0fb97004e5310f86b97badc1b20a617aa379f7082d65f31e6e3aa', + votingAddress: 'yaghiw5rzx7cDNKAWz277n7WL597M15Z3x', + isValid: true, + }, + { + proRegTxHash: '188b4f3700c029d93de43a0e865b3e2e800c3bc67718f8d213dd111a9e401cc2', + confirmedHash: '0000000018652555e9d31570fdb12f77d09bc16698796815c6e0be973d786ec6', + service: '118.31.35.13:20001', + pubKeyOperator: '831193814d5cddb0268d276281ff7356b9cbe560bbcb6c9c55f12a53b0dfdb60ed5570c9c4bdd39d8a0728dfb2b0596f', + votingAddress: 'ybatJBzsLivsAiRJTcNHd6bGuz7wX9xRDC', + isValid: false, + }, + { + proRegTxHash: 'f735ca801b3ed2a87a0fe2838a38a56d72239fd0c4e3877e80cc280090c6f8e2', + confirmedHash: '0000000003acf2fb24d6fdf167410d63ec85e376554077c67ac9d41addadc125', + service: '34.218.129.98:19999', + pubKeyOperator: '8cf9b3235f77637f144728584ca13d1d3fd47450ad392a510beb2425e0d88f6a3354f0cbdd26d4e6152d38899c025aa3', + votingAddress: 'yVa8ezmKKG1RdGCH6cnYhbmi59fegKf32t', + isValid: false, + }, + { + proRegTxHash: '302ba9134d9d734e0a76599c9cddfdd1ea2231ff6c152fd5a95c9ec38aa66d02', + confirmedHash: '00000d8e4cadff81c56eaaa7d5afe4a582a5023a683a9cae59945bf654504bd9', + service: '165.227.63.223:19999', + pubKeyOperator: '083997ad0a7d12c5038242eb54f0aa3952ede09814c57b7392adc4db58f4070dc0b44431c20be0636a21ec238436fafb', + votingAddress: 'yfA2kapYFt41mB3UvgjtEis3Jj8i8Nst4R', + isValid: false, + }, + { + proRegTxHash: 'c286807d463b06c7aba3b9a60acf64c1fc03da8c1422005cd9b4293f08cf0562', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26044', + pubKeyOperator: '06abc1c890c9da4e513d52f20da1882228bfa2db4bb29cbd064e1b2a61d9dcdadcf0784fd1371338c8ad1bf323d87ae6', + votingAddress: 'yX4nvGZnMuF7ZMmoop6nyPeCzVJTs11U9x', + isValid: true, + }, + { + proRegTxHash: '7880f9f018c3d9f13503d2830a1c62387f4fb00230fee33bd0badc5228cc89c2', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26083', + pubKeyOperator: '95f73c3aafca2a25b27a8e9c3dc8578f1387ee77b9d31de706573d610131554c33d6ed17ab19afb3a8e4d03f1d971a78', + votingAddress: 'yeJqijWBvFWrQLJ4gCZFCvMTXG6HKEiCMd', + isValid: true, + }, + { + proRegTxHash: 'f5ff9fbf1daf5db3539c7e307d9d50b12bb58a491b2f684c123256fd8193aa22', + confirmedHash: '0000000001c39daba81f375dcb34d9ec38755675aa6e7930a920508bb6b26443', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yUh6buQPiHPnGbUEkKH8mM8hBuVRS1miGb', + isValid: false, + }, + { + proRegTxHash: '3fe25a5d51edc1942b3e68170fd693bf8068968ca6e1be3a1721bfe5ac841642', + confirmedHash: '000000000a38c31bafc093f1963dd5912505588b2c5f35ac0eee5ea4d7b0bff3', + service: '134.209.90.112:19999', + pubKeyOperator: '91386d3acee0bf9044cce40a07515289589b68fc9b8c8e5d184471ed7982106b1e11587af4c9e983883baec00b67e473', + votingAddress: 'yfvsooGJYKa4gx3N2VJ6YtawZ64Q4skAUE', + isValid: false, + }, + { + proRegTxHash: 'bac184b9f1e4eb098d1f8df0b07af6af8919c60c1f60e31ba29c2f39f395ff22', + confirmedHash: '0000000002b7a2319e0946e7dd0656cb82d14e92652206c7ce708b5633502baa', + service: '3.221.29.23:19999', + pubKeyOperator: '8bf25d66d63197e3144f6fa17ad92ae38cc11b143027fb91dcf5c20fde6e52bde7f46f2789e6fa84573197d8085389dd', + votingAddress: 'yU5R4bX8G2h9rzn2e4CMFJQXXCFP977HDV', + isValid: false, + }, + { + proRegTxHash: '17013c34733cdafc4feb7f317587cbb891b1027ded099e2dc2e8e1da05ad0f42', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26042', + pubKeyOperator: '836747d419d09200e404aa3500fc5d51f044fc01fd1a9f452324c8c14ab90ccd75887b6bae1599cdd458e738a53587f2', + votingAddress: 'yUmWuj82pCJpccWwAowyK7tp9R1r8yyhaP', + isValid: true, + }, + { + proRegTxHash: 'be8d0533c692cf23e3d3eeb3957422b5a98acd82aadbf7baef255edb2f491b82', + confirmedHash: '0000000007f50b53ea1666aa886b1880d12a0976848c23bd6d0801e59d7a5d36', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yZbriMPqB9YLPbqemLWkaybUuLNYHGoSpX', + isValid: false, + }, + { + proRegTxHash: 'fbb1a1aa283faeb8082a7331c5010f13272f7ce6cb24845b3d1f260f7cb75423', + confirmedHash: '0000002910988f956b7d9199e4fddfd9462d79aa1c6e43205dce157d208e4ab0', + service: '173.61.30.231:19012', + pubKeyOperator: '96a9d730b5800ad10d2fb52b0067b5145d763b227fccb90f37f14f94afd9a9927776f9af8cfcd271f9ce9d06b97af01a', + votingAddress: 'yc8Ji5CgQujxfxcP8eCqu82WoBD16tcGnt', + isValid: false, + }, + { + proRegTxHash: 'b037d52073d1e445bc2fd41e35be1c52426e00152e49ffa55b6fc20f33b28483', + confirmedHash: '000000000ba46588ff753b61eb5ec499c4c0e504feaad39ca116b31758afb13f', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '09a5e37e3b9e556a7d7fe7cda1b54682351d4d1f6ecd331d98816db0696a5389c4a09bfc57f66f0a27120302ceadb078', + votingAddress: 'ydneMgTtTgkt7HbFNBomGkaey2FQ3JNJxq', + isValid: false, + }, + { + proRegTxHash: 'e60be9b19bee9ec1f8246128d0caf27d064b8eaf690005cb3d22789f20d65d03', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26037', + pubKeyOperator: '004b447b181a990584c38462629f212d8f753a8b9051daf1fafad468cbdbc2dda04c93418ef6726b7994b664b28e544a', + votingAddress: 'yNta5cjuBfHYM8Jg9VhDTyxv2FeJA1AteV', + isValid: true, + }, + { + proRegTxHash: '0867e1018f1de184690af3586fc3b4e17cf61614cd926532cc13f9ecf1244523', + confirmedHash: '0000000000691141a416f78b3f93d5ac371bd8e362990648d96027e3ab018dde', + service: '165.22.213.149:19999', + pubKeyOperator: '881cec3eab18eaeb916d3f234fae24363b68faa705f51a6efb06f83adef70f73fe28ee70bf4c8300ec4f77f017dbf7f0', + votingAddress: 'yWLY85qvzXnwWqtfWk1BiSyYC1j1CW6mpU', + isValid: false, + }, + { + proRegTxHash: '4636ed7acbacbc76aba60aa7a1011688fe9ad5fd701d0bf8fc42a502ea3e6543', + confirmedHash: '0000063950f0cde94633aa231b42e384de998f9b33c11b5ec532f366157cd4b7', + service: '134.209.5.148:19999', + pubKeyOperator: '83a6548569b0c410d7e1dec3f4f5a18a0790723a991d3b9477a9e062c660959bdbe5b3c1d231195801b9072ae9427966', + votingAddress: 'ybeRrDqpAvcy1zv8xLizjgKGRWUPLmtA77', + isValid: false, + }, + { + proRegTxHash: '7551fe264f2ccc4e714195d2ffb79eea7ebd47517a7164c69653569b10f51fc3', + confirmedHash: '0000000000f43b1bd227258a4a3d86ac175553b984feb12e656b23b00e681389', + service: '161.189.67.25:19999', + pubKeyOperator: '0becd48c0d44ca6fbff3825f55c35a6f70024f2b8f4f939260d40b5b51c11cdfff85f7d0444a1a9cb8fc45bacd237b31', + votingAddress: 'ybziaDozC5ZVSR4aQPgz1qixqXGSorVUzq', + isValid: false, + }, + { + proRegTxHash: '1124b37d6b4f90ccc8280c85e6e5978fabf9b5e6116c42c493aa7a462737d1a3', + confirmedHash: '000000000c03b07f56811017b7bea0e2d4fc5ae0cb857a8ea46d43d6cc87cb83', + service: '3.222.94.210:19999', + pubKeyOperator: '0b27835d169ae7e229770b0eed8115b23c0aec942443c97bfd4fd56fd445fc6ef0eed60e694990bebb59a2adf4fa537b', + votingAddress: 'yTP5pwZLjBCRoqRJ5nsP7ss8drpGrDxBbe', + isValid: false, + }, + { + proRegTxHash: 'b4f9de65ae676b63f84f2865317b8b512a12516c4459f2f59ca2626c71f7dda3', + confirmedHash: '0000000dbf8512e58203072f0ee85d0f6bce6675d65d6f49c3186fdd2a9fee69', + service: '1.1.1.1:19999', + pubKeyOperator: '016a16472319f62f71bb60e38038aa8cb93a301ff6c3727f75f4d770428d71d032fdbd27c5d03dc56ef1d658fefe7954', + votingAddress: 'yVvctToMgz3GNkgCFh4SqXmFzEZNfmXANX', + isValid: false, + }, + { + proRegTxHash: '1f1443d9f38273f6437fe37eb34c30033fcd51c7e7f563504c8809906e711de3', + confirmedHash: '000000000806b74def47b236cf053667c25c8891205758afbdc3f6c71735f870', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yTNJXd6fEk1ZbxiDPk4Qfa4S5ZBHWCkFUv', + isValid: false, + }, + { + proRegTxHash: '3a6f022ad2829da589046ab87db1d157b143693214af06bf9e22e0fdfce2f5e3', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26061', + pubKeyOperator: '0ccca2500463fe0b8be9d97a31dc51d7b774d1733b322bf4802ad1f2f1255726ab0f5099c4726e7634614c43f9285180', + votingAddress: 'yMnZNjY9eKCf3pkbLQcrv5JK2SQAuBNQEp', + isValid: true, + }, + { + proRegTxHash: 'b00179bd307619645399361abddcb07287ecd406301fdf405d9a82c8e333aa03', + confirmedHash: '000000001c2ec7f5efdd4a8c496632ad203f277f6b181406094e8b37a89affca', + service: '195.141.143.49:19999', + pubKeyOperator: '878386a8d07cf79e1dc6963d4cdcd7a4af6ef7e350cad3e1373e45fb86fdd9390a77366ccffda6ebe1470c45b0f75910', + votingAddress: 'yQjrj7ksc6Yyv1Ppmuxc9GDReFC1eRVjfe', + isValid: false, + }, + { + proRegTxHash: '3a80d01eee0c4b79f9de8393f0260fe859677b6bda207a21fc3217a9ae4b5a03', + confirmedHash: '000000000130049bb4a0ee120b3518c8ebfb07a8c43ad9ac1e1e6811af5fbc85', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yh6o2qtskpuhSwhBz66tULaj2UXgennDQA', + isValid: false, + }, + { + proRegTxHash: '682b3e58e283081c51f2e8e7a7de5c7312a2e8074affaf389fafcc39c4805404', + confirmedHash: '00000018c824355520c6a850076c041b533d05cbe481f8187e541d7e2f856def', + service: '64.193.62.206:19999', + pubKeyOperator: '05f2269374676476f00068b7cb168d124b7b780a92e8564e18edf45d77497abd9debf186ee98001a0c9a6dfccbab7a0a', + votingAddress: 'yid7uAsVJzvSLrEekHuGNuY3KWCqJopyJ8', + isValid: false, + }, + { + proRegTxHash: '7abe11022a30fb9e614725880e035fb48a8438d3885a3762cc53b2c3cffa3824', + confirmedHash: '0000000006ce300f29bf8dd4e241c7f0741aca652410a4131d85cfe840da9bff', + service: '18.136.145.165:19998', + pubKeyOperator: '8edd5cbdd7b381c92ac7de638440bb1ad417af0e82fece69432f36930a6defd3faa0d53d79bc3347ef684eb1e470abbc', + votingAddress: 'yfv472J2XNVZAN28vYkCS7naWmBxA2Woyc', + isValid: false, + }, + { + proRegTxHash: 'd3a0e645c1830de00ca370761d0db7a75a408b9322ca571fe26b7f8cc5a0ecc4', + confirmedHash: '0000000000fad7c5f9c2fa286f412774f5582bb925ab93adc54958427e09fb32', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '05b7d7aee629c25efc5604104a3a9af1e23663464e0505a057e68cf12317834160597fcf80287a94e98f171a8c79a2a9', + votingAddress: 'yP6tX7mBmuJsXyUW1oYDN846hom4k5gREx', + isValid: false, + }, + { + proRegTxHash: 'f02c8e9a3deaad566c707a23c4ecdd77c9615cf4934c27d43d1a46315fe28d24', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26069', + pubKeyOperator: '0eba486a0e6448ca193584c77f6a3b8b8774470e27c67a364034de794b97db9c1fbe8b617625d37861195cd54dddf41b', + votingAddress: 'yawUPMQE12RyRuCKU71gUtbxvnz7145mYa', + isValid: true, + }, + { + proRegTxHash: '7f771a55bf8d18d1ec8c60e61494546e5b9ea1d0639369aa5d09cb3ec7d53144', + confirmedHash: '00000000061775933cea9418de2f874c958760e026445ae2633d1fe8372a6487', + service: '47.75.68.154:19999', + pubKeyOperator: '842b8e5b5cc0841de193f440d5fa3e0b4a34df7fffa798fb8c3df46fa31187162cc3b3ecf929689ae35e04cbac6e069f', + votingAddress: 'yiFsin3TXNE6acmvChCAbjHaNTZ5jsmppf', + isValid: false, + }, + { + proRegTxHash: 'eeb8cb773673c77f664501bc68b813206e9cd0920a11cb74cc918897804bee24', + confirmedHash: '0000000012d9a625380c4490ffb21605825a8758374a7eabd6c68e29a5041ada', + service: '3.13.34.147:10001', + pubKeyOperator: '04cab5bc1d73f5f8299feeecc0bee2d76f27c3b2a56a7e2fc1f927e495ac9b2a0560b7d82fd06fa8fce4af69d0fcb10b', + votingAddress: 'yYhrMPHjoQ5QXJVmbbvBDwQgiKoC1XjYkk', + isValid: false, + }, + { + proRegTxHash: 'e3735f9bb5a8886a070ca9c6fff68961a4481fcad5eb6be35e564816cde94244', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26030', + pubKeyOperator: '8ff934db7c40ff9dfc1e707f92fa20f0a39ac60f88c928ce1de99519c85c4589852a9a8dc5decdc05c379771628ef8f3', + votingAddress: 'yasBevczzYB3etihrspkYU5REbnwjCknTK', + isValid: true, + }, + { + proRegTxHash: '7cbb7c6b65f9360c3ef908ccf93ef438a449938acf05c743b7b92647ab3ad264', + confirmedHash: '00000006ddc2d61122c00951c15516368ac0bfd7fa0a8bbf13258e59f1fa54df', + service: '104.248.92.98:19999', + pubKeyOperator: '136de56a265eb21c006bd312a0353c7c3eed46f4f63c301c348fb5d5de8f965c9b60ac6a4ae805d0d241e4942821ed9a', + votingAddress: 'yibp4BpABm6Cy4u29cV9ErxidYDoe5tCsd', + isValid: false, + }, + { + proRegTxHash: '4da18e72d162548eeb79781d97a2629cdf02a004cf90287378dc5c7ab8ae3504', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26052', + pubKeyOperator: '05256adc53a9a55a82d4d91cccadc5ed706899dcadf4f1bbf964abac55d37e6d358c1bf3856ee5bff6a72b1cb68a89f6', + votingAddress: 'yVH8CDTRsDnqG8RfEEj2rvjSP1Vcnt1c7z', + isValid: true, + }, + { + proRegTxHash: '344ca8af7d689645fa041a707750b206f58dc5dbb9da72cd46b0b9fd319e5104', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26071', + pubKeyOperator: '1040609a801ddb9ff262789df007e7274f99bad2499beecc17a1256439dff7cf8850127e14497b5c072a0bcc1cd809af', + votingAddress: 'yQRPBC3cy39AaLqeERqn56XzkC4qiPzyem', + isValid: true, + }, + { + proRegTxHash: 'd746303dd230f9c5ac57b663da6eb7766e3f983da03fc433fc2f1ac6a5f39485', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26002', + pubKeyOperator: '15625a4f150d7c749597a586cab4f035e0ec3052a6e3d2fc0525c3572c049798eaa2063f01fe96076cf3cf81c9cf750e', + votingAddress: 'yR24tSEXsDHdpEVHFrNSw7re9F9DbWwM8J', + isValid: true, + }, + { + proRegTxHash: '766c3edf3c134fc0b5ede4fb57b15564819caad310b1929cb5b57251114d64e5', + confirmedHash: '00000000106f12ba06bb94c891e5a8d7376f40ae04d2037108f41ec86846fa0d', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yZRpazSYy42d2VSKSA1YnhRcHAZvU8sijR', + isValid: false, + }, + { + proRegTxHash: 'b5d5c145d244a98b81cc9b5fa2f841cb6689e7cab76566fdc66ba16a82db95a5', + confirmedHash: '00000000003573878a486d9f966d5da410300b4680a4f2dbbc7135f9c9a97f38', + service: '51.107.4.38:19999', + pubKeyOperator: '8f4fed7576bd1e31d45225788c1c96836dbc85b3ece3b77fbe4ede0f5f784f138ef6b32884e3345915a758364d1e5823', + votingAddress: 'yQX6bNQmmkGQ7xnUbLuCSGtJJ8GmGtRHMT', + isValid: false, + }, + { + proRegTxHash: 'de57b4825d55ff101e6b4924b0724b04a3d0b8da9c913ba9987ee76eb11555e5', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26087', + pubKeyOperator: '81ba5be7b11bc6f248187a33782ccaf3b0245d1fec38f1ae2d0a2d845041652f371a7a4c365173a0958929161e7c33ae', + votingAddress: 'yMzt3QUgng1k1zEPgWfzUv63xkUX49U5T1', + isValid: true, + }, + { + proRegTxHash: '85d82a069e7c89a1da85a0043ef7bf06c32ad0dc1dd3c9518e997b66da222705', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26010', + pubKeyOperator: '8273d35e1f33d76b384cba83a58aa7bc82eef781012b144f2bcb287627541c6dc713ff1cf730e9dd7d14edf914bbf331', + votingAddress: 'yj4jPPFwp4t1m7oeQoW7j4f3XV2xHAc5rL', + isValid: true, + }, + { + proRegTxHash: '300715758c4dd712ab80899630ff0963d4ce8e824778fafae02c82272a420725', + confirmedHash: '00000000d03fe6c22a10c02e93a8c095954c2e0be0720479dcf779c3e7fc4566', + service: '173.61.30.231:19017', + pubKeyOperator: '8bb67827af87431673e737c49312c5a16fd284daf1c4050e530b604ec4f85f217080503f978a6bec89d1ad4bca089c32', + votingAddress: 'yPjodPW2v1VMJBteJedW38qtdbeUamfCyr', + isValid: false, + }, + { + proRegTxHash: 'c26cc8b3a3b6b08e5fac0f234f3bf5d98bdc447780360ba4763652fec5b9e725', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26005', + pubKeyOperator: '00a7f8ed313583f6085d77d0ab2f9d425ac62246ed28e2f8f095caa61ff0651087e65cc15494dfe8806cb26b03040407', + votingAddress: 'yWbGH4iUhbUSNQnwKQp4yR7QThKRGJewqR', + isValid: true, + }, + { + proRegTxHash: '7320ef8a6733a02e5621d460be2e1880c1081f948e301c35e8b9c3039710ac86', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26049', + pubKeyOperator: '932139b3b5b6ee65d18d75a24c639b83ed1ac8fe05138251b422044484d161694fe31a7f41a065d5f52cb5e456368bf5', + votingAddress: 'yPiC2TtJ2n4eZ99436xqTRuvX5FRRUfE45', + isValid: true, + }, + { + proRegTxHash: 'a565c913052a586db0d5a000007ff981ff3b59982e662e02ac6d45e37bf8f0e6', + confirmedHash: '000000000044408872129eae0afaec3af24e6c708b7b39a579c7ba173af36077', + service: '145.239.235.16:19999', + pubKeyOperator: '01e584b5723fec78495744b68b971fd654f16b016d676ffdbac01b2c64f319675eda577f5eaa5cf5379e95418c61ab10', + votingAddress: 'yfXCHmtQ7S4TN4rEBusxfEJThAzoZaAtE9', + isValid: false, + }, + { + proRegTxHash: '69f156c10220991da1f4e8d692a582ea686a028d532b037f29684610fdb60d26', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26086', + pubKeyOperator: '8e72ce4ecb7e37c0ba5376c77ec364606b796eccd05d80583a36da42d57421c21d3ccc3b3105ab18f87901e03ce09a00', + votingAddress: 'yX8gWHmevBaseFpeVRk6HWxRRSyoeZNurv', + isValid: true, + }, + { + proRegTxHash: '2b17e66d746cfdd146c8e14042143ef1525bc68df95cffc5d7e3100edfe3d1e6', + confirmedHash: '000000000b013b111e69e4dc8f11b539bd1e9908eb64afd698cb60586bdd661f', + service: '95.183.53.17:10006', + pubKeyOperator: '0dee44e338280a8e534c9e8bea9cb9d73163070d90d511e5c83859c384790e12da189e791404126eb2fe080593ad9a73', + votingAddress: 'yi9GtcFVyD2GbXYKW2GMgtNCxiaJcCTPzB', + isValid: false, + }, + { + proRegTxHash: '7161d8618826c76aad36d8b59bc4c1fabb1d8299115f8314e74d7854fc3ef666', + confirmedHash: '0000010f227be0c01e195a477638557e8b2e92a909d026070b95c09a20984f9e', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'ya323cSJ9oFeNEPfBo13FkFP6ocknw2MGD', + isValid: false, + }, + { + proRegTxHash: 'b3c65139e739ac5664f1c835e215fe48eb3e9b3c5dea336465fd1c4bb6159286', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26098', + pubKeyOperator: '0f835d7e21c94878c4f8a9ff8add97ec686a26c4c7f6bc6cefd6a0117a7c0d3bf0200c733b96e189760c774003ee5703', + votingAddress: 'ydm48vHQ9KJBXKwoiQYrvJJPiatsgtPYxV', + isValid: true, + }, + { + proRegTxHash: 'c5ac594a4d00199db59c178104effff54bcd082d9be4e7625196817719730426', + confirmedHash: '000000924de3117d8092fa95c92b0ecb39470d6d87975286c2978c07ffe24e1d', + service: '185.50.195.156:20004', + pubKeyOperator: '82b97953757c5674467c5d85115dbbcb55e4670f69b3279c6e9b6e8f10d28c571473a2fa3b97e8f78859c31cd9dccc71', + votingAddress: 'ybFjFRMuGB4KG4svcUhio1e9TiZffiXKpa', + isValid: true, + }, + { + proRegTxHash: '5670b9b6afbec79192133e01ec9dcd16fb9e90bb6f0afc7ae92ee35bb3a94c26', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26027', + pubKeyOperator: '920e2db8d3f6a448ce77125ec7bef179d52019c6d50c3cf5a044bce83d518b676f20e2d6d9a887a1138d306e4c574166', + votingAddress: 'yXQ5UZRRHBMQtbCXLss9aRQ1Avdc176t9d', + isValid: true, + }, + { + proRegTxHash: '39ec834a6c7ac5ebf5fd885a271e2149099e87e89a9ea30f573b4b699b9399c6', + confirmedHash: '00000d4f6fedf394689147afe04c95c2c68f94a68334b9c4e1e613ecebfa2b95', + service: '134.209.2.128:19999', + pubKeyOperator: '0617fffba2681e4712782d97b84cb41b722d56089c3f3b3978b8724cb02baf0f67a57e8d1e2f8227d21700f7b10230c4', + votingAddress: 'yZxXNKWM5D2GnSceh68gPfdywb561j1kMJ', + isValid: false, + }, + { + proRegTxHash: '0b53cea62f79dbd8b43ef803b9eb1d47a1dec3611f460dba83ba9dc32483f9c6', + confirmedHash: '000000beaf5223da101ebdf37d26e2568f7b2e5d0b454115a5179969e410c3d1', + service: '95.179.251.182:19999', + pubKeyOperator: '90403255a5c2aef92a899cf01080a78446f07e0a25fe391a81791c37eddba6e82aee9b8b86b7aa4f44129637146221c9', + votingAddress: 'yhiD4tNFgaCkXuRsxJzjC66UuuDq4QWi4H', + isValid: false, + }, + { + proRegTxHash: 'e48789320db46a122458408c6ea314d5c8a6ace3608ab614b090c742ce174e46', + confirmedHash: '000001e09fd871c98ed84c47f26cdfe57af91f580066f40b25d59e821947d137', + service: '168.119.172.100:19999', + pubKeyOperator: '040afddea4bb79d915f799462b5cbbe991602410d041503b9cb2bf6a6e73e04d85518e612a16a28b797d7978ab915057', + votingAddress: 'yekDQwZyBaDTtBBXsGu3MfdoxFdSeZoxRx', + isValid: true, + }, + { + proRegTxHash: 'ce4311bda3ac4ebeae085f82eca70cbb8115b5aede3e3efed7bcd413c73ff246', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26017', + pubKeyOperator: '1689af166e3b2008425588df6b90de2c5c17848028ff4c54338a884ce442074ea8656e18cfac939b78c12a731f5237b9', + votingAddress: 'ySa2vCfBVqpdQMgtmbuZpzUpTtb2xEA5DZ', + isValid: true, + }, + { + proRegTxHash: '9be1c90d8bfe3b8e02da278855c6091451df5045f57a42b8ba8015770d3c9087', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26066', + pubKeyOperator: '19e6d1dc06c14b711a4ecd5aeee0208df5b06ad6f012f6ead59bc57e32b2f77f83a56bd42080145de0bdebcfc6d4ec44', + votingAddress: 'yQBhH23RytM1EsdX4eLscxELdkTum6RX8u', + isValid: true, + }, + { + proRegTxHash: '88ce5bbf28b3bcf5c3cba3bada3b76ffceafe4e93d1303674b4555dcb2a09d27', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26088', + pubKeyOperator: '0bbc35d75b9cf82398781cb1b7b45abb8bc0d50b1a39d55d8961405c9b602d6cf4fd2fa7ec1cad5bcb0a5378f10ec1d7', + votingAddress: 'ydNp7KgVDjPb27ZkKsozQNaVaazKy8H1D8', + isValid: true, + }, + { + proRegTxHash: '0fbea7792604319890cf39e6afed9e0866f33c38bb56424cba1cc27ff462f947', + confirmedHash: '000000000485da28d65527dff74c16b81f525edbc9f98c8abe525f02fbae11c0', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'ygXqVGMMwdvc4hQW8n2S2dXgmSAfcR1uKm', + isValid: false, + }, + { + proRegTxHash: '444d4d1e9850cf19adcf3aa6e01e8a198779eecc8d55fe8bc9715726efc58987', + confirmedHash: '000000001238e57dcb6c1e2ccebb501e652e060a998824f9168c7d84f02810e6', + service: '207.154.242.157:19999', + pubKeyOperator: '958adbe9f954ec23983c4be5788e86e0df30fed1d6852136376b49c1a24e0fe1da1178b23dec4ea098b9e355aba8de0b', + votingAddress: 'yRrhnfBVp6wdkMfnx6tokRbBXqoTMeVA8B', + isValid: false, + }, + { + proRegTxHash: '5592cf425cc40337e733be4acc48cef4d5a39e9148967b8ac094336923eac1e7', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26026', + pubKeyOperator: '07b3deeab02edd452fbed7d745a73d4860159659f8fd92752e59e9f02054b794b0b371a8960cd77bc7edcd6eece14136', + votingAddress: 'ycJ6TwoqXDdBfMd8b9DLz1ntVM89GbpGrs', + isValid: true, + }, + { + proRegTxHash: 'dfab7fd7e6f141d1ad7ff9fcaf8dafaf85b05dafc9058b376a33c6f4ee1da607', + confirmedHash: '00000000007eef00be22896c39686efebde62cc88be4a30b4ebd41f8d5e1adf8', + service: '145.239.235.18:19999', + pubKeyOperator: '0b4282cdfe1cd639e60b6c58b2f210bfe6b57f8f247cc5b55673d188ef458270c7314f7128b286a3326b9ab6109bd2ff', + votingAddress: 'yTJHgkiEMAev8eycCXQ1nQemwx2McAxaGu', + isValid: false, + }, + { + proRegTxHash: '8ba8c6867b46bb40408022696bab30719990806d6e5eeebebe8e5377228b3ac7', + confirmedHash: '00000000164ca3ad61abb2925f718243372fe54e86b900eb65f18e02621e4173', + service: '34.210.246.185:19999', + pubKeyOperator: '8b5d53516c0c7134efabc77f5f7d19b6e289b5e8befc35ca5d77626a252e659888fdd09a7c9bb286dd9fc4d73025bcd7', + votingAddress: 'ycdU6EyVggw4RaW3EKPHCMBeT6vzRDXgbJ', + isValid: false, + }, + { + proRegTxHash: '05f876be752ae6461ff137383280810a4f2f1a6c28c70316b4723d1db0ea3367', + confirmedHash: '000000000d8fafe0cb68fd608a02c0cbf25518aa5ebd3956183d457a9f398ce9', + service: '173.61.30.231:19024', + pubKeyOperator: '02bc82de6e1c7ba14576b504d412b2bd06f7e3cec15da7a94fc8df136c13d581e0e9973ca62106e67fe7a22ca816ac55', + votingAddress: 'ySBU7oXuuTSJqtmUArMRFsKefJPtEDkESG', + isValid: false, + }, + { + proRegTxHash: '2707b054eb4d005cc4c6c8e91408e705a3afac1cd13324a1c494180d40ae93c7', + confirmedHash: '0000000d285cd29574b7e58e247701d8f7de8153a1fedf5b663efc85594e1e9b', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '089bb561944d0a8d5f6150a54733f81dc7110c0b8cb0a4c847a800caf9c4a347959fcf2d813e0ca8a81b3dc0cbceb9d2', + votingAddress: 'yLoEXN2fev6GmRjJuMfA6TeH2wy2Mitt8q', + isValid: false, + }, + { + proRegTxHash: 'bf574b70af705880fc8aaba47f3285ac998456f3885d7a559ed8c256810b0427', + confirmedHash: '000001ecd02eadcfe7fa5747f00789142ba3ebd4f5513e59810293da411ccb5a', + service: '149.28.127.8:22999', + pubKeyOperator: '14cd442dc85a0037b86dea18e05cd909d8ec06fa7390b32ad2f0341ceb69a45c69f40d526865de790d34cd66d4e65ec6', + votingAddress: 'yW8GyYsFDkuTpDbCoz5N1BSPcP4d2x8FN2', + isValid: true, + }, + { + proRegTxHash: '83886e9f5d20ae160d94ac66ee73f522684c7aa8de5a9746ffe5cdd2257c8427', + confirmedHash: '0000001960431ec5a566e69f28ae0f6fa3199bd99ec527cccd02f7541d77300c', + service: '95.183.51.146:29999', + pubKeyOperator: '9426621a0df5cd8a4432c4050f39163a76ab39b2682aa3ea2064993265d66324be3d45ab22d5f9910c8ad09b96bbc952', + votingAddress: 'yg5fiNeT9MLoKpHNnxxpHL6Uaxs5pLzQYh', + isValid: false, + }, + { + proRegTxHash: '71cf5017c4c5f69db5c17a8cfb4c28ffc14ad1715dba2a83f0c30e534291f828', + confirmedHash: '0000001d265e101abb16f78133ea20f57cb2651108b24b506eaf41ff282865f1', + service: '95.183.53.17:10007', + pubKeyOperator: '10d647e3107b77440e2e9957092aeadbba86d02eb95ec23e490c023936bbd4eda6cf8850f98d01bddf4db0a405bc6a37', + votingAddress: 'yS7M1mbiyT7jk2aTUwMcBA8hHd1jDSjgUh', + isValid: false, + }, + { + proRegTxHash: '886b718447cf6f73b942a24a924211bcbafb3fad6fb9237302db0371a0a1e948', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26028', + pubKeyOperator: '99a5d0292eb120db363bc9f133afa72ea08af66664bfc55b9bc831905480413e655ae61d72a717d5d3e7d184cc8b7298', + votingAddress: 'yTM4MNUjnZYNEh5JNq6cWkGiD9F8wkFuq5', + isValid: true, + }, + { + proRegTxHash: '998840732beb3f2fe63563eab8986785134428b74aee4c00217610ec79fd7568', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26050', + pubKeyOperator: '0e55ea95825e6bea050b9a8bc97616fcc81e6ad8762c00fd39d3098acd58b650ec763af29ba7849b0b97a3ac5d23e80c', + votingAddress: 'yiN8QQL4XmH7wMVaGA4x9nJ4tMAW7CYxFg', + isValid: true, + }, + { + proRegTxHash: '68fa216e06d6eded3afecfe2c4b1320a20652972a006f85bc024d8f46dbe8d88', + confirmedHash: '000002a00fe699be8d6a6ec04fc72f4632817372f81e7b47e9cd96944daa6167', + service: '159.69.72.12:19999', + pubKeyOperator: '039715a9bc06634ab10b432e3a9d446d436b4584f65c19aef93c69d07802690df0b51d81da6ff9a8de1542c40edb0b1a', + votingAddress: 'yiEzt1onJsFBwBxwcN9vibquPpajQHuRRD', + isValid: false, + }, + { + proRegTxHash: '15d1b166ff3603d1a583f028e9a0b5334a48db7f3272ea2e4d101306e353f748', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26022', + pubKeyOperator: '18e4c1013002f690ead979628cecef981034bf9277813035637c376ce9dd04b1b379c4abd45bc5ea969a894fa0d0ebc2', + votingAddress: 'yW7hvWCYfD8DNPRRAepgU3dtSP5BeVuzvn', + isValid: true, + }, + { + proRegTxHash: 'b9eeb35f00d10ebde45a23db94875bed007b94eb03cd0317ff721e24dd5363c8', + confirmedHash: '000000000163ae96784593e522d7717eb47acc6b6e6866df39cf2bbf1cd1c238', + service: '157.230.40.102:19996', + pubKeyOperator: '8f420a082c56b30c9bd8492394d83066a8d03628a7c8e3eac27486377e2648bdb3cebcc4fdd16cb4cab1341e480fa439', + votingAddress: 'ygE5WkubdrrYafFUvPNz3xQuJT5XF9jxry', + isValid: false, + }, + { + proRegTxHash: 'a4d877cee62f82868034fb678436d87afbb13330d2b66a24ae1d357f0de55c68', + confirmedHash: '00000000069c41d7444a7da5d67f222224e9e37590c474f102ee1ae0da998f39', + service: '83.80.229.213:19999', + pubKeyOperator: '16415af54406658be9ea44d82b6b502bb90d93e32997484533a8a71a4ed98d12cea3709d84a5835b6ad8ed48d3101633', + votingAddress: 'yfKNLE5v4QTnMvj7y3JVoWEfQanD4qHWGk', + isValid: false, + }, + { + proRegTxHash: 'c2a54c1ad133acbf3366aba2534ed6c1f01728553a7e877ac2a22c98be085c68', + confirmedHash: '000000001024eaa42e10e7690a4fd014e58e91cfa9e00ca1e722124e13e18023', + service: '157.230.110.86:19999', + pubKeyOperator: '85f01c97f8e6d601ed269d4fd1d33b456c5c940aecc45b084c0f8d9ddac26d6fb7c5cc1eb817a41bca401e5c9c4ff856', + votingAddress: 'yM2BrdCajmovvGsox55mkZHemnECwBeRxC', + isValid: false, + }, + { + proRegTxHash: '0d20e20e6a7ed1999b25f2dee0a53893a462e92170bc13a8b321f25c87d99728', + confirmedHash: '000000000e6301d8f5a3e6030526db5639e343f82045180d58cd725766570574', + service: '159.65.69.245:19999', + pubKeyOperator: '14bf71456476fa02cfe3de9735eaa10513e943db4576667128051f34692ce042c90cbb9dbb268d3ebf89205e5c8e2afc', + votingAddress: 'yS1MkoYQqSdeH16AXuZoAk2snvMX6xsHM3', + isValid: false, + }, + { + proRegTxHash: 'e2c750039cd378dd87853062fc93ceb9cbb14338c8679bc03cfe79550f7c6328', + confirmedHash: '000000dd237910211e5f44889bcba3ae038c5111a80ca1d5158b68952a703222', + service: '185.213.37.2:19999', + pubKeyOperator: '868ad323ed59996a80a6bc8365d2a0939c40f29688d619edb7fd4ab63e4788dea07b31bb3e04a848ea75ad3c827e6209', + votingAddress: 'yd6MJ6ohWCcAKPKawafuKv7qdVYSuvWQ4Y', + isValid: true, + }, + { + proRegTxHash: 'f311a4630250c2c2fe0f6121d7214b1e962d2e7385e78cdc3ff694c9cfc0cbe8', + confirmedHash: '000000002a82d47cac977529605e9b39715c6cd4137a0afd5e5f7dc8a2a60c08', + service: '106.51.78.70:19998', + pubKeyOperator: '01841a16adc73f0224e6544d0cc57057ee2508c906706307ef8561908bd476594ff1e825798faac54c8f8a66583a3dba', + votingAddress: 'yULZpTMc3fcGRtMc9EdhftZP4c5Y4khPKm', + isValid: false, + }, + { + proRegTxHash: 'e6218b98482d5533f37cb384b9403ad482163bc76c783ac290d78a5fb54573e8', + confirmedHash: '000000001a4cfc8b64b92e78b3c3145ba9003dffdc0beadd36d1d4b45184800d', + service: '173.61.30.231:19020', + pubKeyOperator: '8a0ede82d78a0a8f4c2332d431c7be496c3aa09349ed3b2db30f7eb7dcc7b6e580a9d71f7d76bdaca1b3670e0cf4cd3c', + votingAddress: 'ySBU7oXuuTSJqtmUArMRFsKefJPtEDkESG', + isValid: false, + }, + { + proRegTxHash: 'ec6e4e052c3b28d77c13ccc5072b5f5c185e1a53a6ffaedbb1de9739c0d31489', + confirmedHash: '000001ce92de12813e3d6e61dfa01676caabea27f0f9294fd818af3537b4ff38', + service: '195.128.102.75:19999', + pubKeyOperator: '1926b68942b544b4e17347c5e0d28ba91453984294c7679965f3a1d3cdcd9f5bf80f2c28a48b503f301bada544798968', + votingAddress: 'yfAZHJ52VWG82Cupk5RuQFaAg6vX62N1yW', + isValid: false, + }, + { + proRegTxHash: 'f7f3a36e13bd406d5b9c9a19b6c67c5051f7a29e6596c1413326b98c00cad909', + confirmedHash: '000000000f053ceea06bca709a19c9990c6b84f62258bcb7dd4c735bdc7b91bf', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yZvrn7it42q6MVGhJRbNSANjZB2QBBhWDW', + isValid: false, + }, + { + proRegTxHash: '3f960fd8d414906a260cd07db16f743f65306823355b61b5d3ad4bdcf9184549', + confirmedHash: '00000000066cfe7531eea3f7a2b6d43a3292092c963e4bd8dae7a9d130231bb1', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yP2bVNryY5q8x8CcXyzjRUp8wWha2kJfgT', + isValid: false, + }, + { + proRegTxHash: '446395517d8dc7a2fe06ffc2dcb5300c248a324b9bd5bd91532acd77eabb5d69', + confirmedHash: '00000000017ba736774fa1588f1843fddb7f694fb1f4d4a7b1accca69da01bdb', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yW52P2j5UE1S6L5h3jBczbWMPB1njNoFjH', + isValid: false, + }, + { + proRegTxHash: '5f0407d926660f7688f5a22f6ea17b39ae4d2df36aeac42099ed173bc7b48a09', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26045', + pubKeyOperator: '0b235e789d9895d6c0fccd31c8e93109cb92633b16ec21e5f8fcca9cd562cf07ee1e5c75446dd32843cfe2676dd5f9b9', + votingAddress: 'ydsBcVJpfE4b9g2PX82jCJ7a6gtsKH5Swn', + isValid: true, + }, + { + proRegTxHash: '3dbb7de94e219e8f7eaea4f3c01cf97d77372e10152734c1959f17302369aa49', + confirmedHash: '0000043c0d46d3e6b84c1b420b77b65e962207fb6427361f74d243b9b1fd51cb', + service: '52.36.64.148:19999', + pubKeyOperator: '139b654f0b1c031e1cf2b934c2d895178875cfe7c6a4f6758f02bc66eea7fc292d0040701acbe31f5e14a911cb061a2f', + votingAddress: 'yWEZQmGADmdSk6xCai7TPcmiSZuY65hBmo', + isValid: false, + }, + { + proRegTxHash: '3a541549d161a0e134f54db0afaee615530bb6d84e353b82afc9af76a9a39329', + confirmedHash: '0000000e179a7c0f8b53e3af8fc2cbe13d36dbf5cc1ccd3675d77c541423b43e', + service: '139.59.35.20:19999', + pubKeyOperator: '0a97f109f81f15cc0b6af0a57ec93cf9f201789fd28494baf1840594d4bb233cb790f4ba434c49ecb6a1ebba61beec03', + votingAddress: 'yQ2xMRbx1nz7hGNsh1hZdeYCCDjXq6W5MM', + isValid: false, + }, + { + proRegTxHash: 'eaaf0220c44e4e049b5899f162e7adf2da1e7946a2272489f304fe3df5247349', + confirmedHash: '000000000927161a5292421aae4045606f4f92fb734b564434897bd36ba07430', + service: '159.203.34.99:19999', + pubKeyOperator: '0452f32ac367f352d6ef53f984667db9ad658bf940292eae2440024e5af9445f7a7a618d536c4743c9de4c3e07b6a5f9', + votingAddress: 'yNPto4mNDk6CkwcWeqYzq5dKnBWLkgq5XD', + isValid: false, + }, + { + proRegTxHash: 'c6b291a26712b3f789a6f9379d55029d78f895fe3372c701dd5ef0a597be3b69', + confirmedHash: '000000aaae9720cbf93a950d4743ca62a8ec200558c4cb4860674d374507d393', + service: '46.101.243.84:19999', + pubKeyOperator: '83152e6489f210094f5dd558373a1ce9abf36bb8001d4f35b3dea74f7ae74baf859abd22238c682ac94cab82eeb58aab', + votingAddress: 'yUoKYVFD6P18FywDN1QYnnyxjbCwW4EWyQ', + isValid: false, + }, + { + proRegTxHash: 'f0567069d4f2a2e536e46173a097b318daf03edef989f6875ca06f5c4d49abc9', + confirmedHash: '0000001511beddb0eda1c353a019eab5569433e7088be2799f85c953d86871f8', + service: '95.183.53.17:10009', + pubKeyOperator: '865d6f26ed3f5309e4aed19583cf179bc779e21c967485f355b214ffb6ba461a01b575a9c62b3a02d08a37d01817af83', + votingAddress: 'yQYRfZvQxaQnjR28AkCtmvHFHP22p2PLKR', + isValid: false, + }, + { + proRegTxHash: 'ee870538e2c265e7c53af7f94934fdef16cc8016c2f36a1f266541cba96a1049', + confirmedHash: '000000000ca732b4a97c3ef8a8d567c96d0385e2f80b9f2268e8a0bd271b84f9', + service: '43.229.77.46:19999', + pubKeyOperator: '8de69524dd60930aacf252a19e34e5928dbb20144d1f336a45dd4248acdcbcafa929619913980156defa1113d1481139', + votingAddress: 'yUVxd8VafRftExWmz12oHUxrfB1kmZuaMe', + isValid: false, + }, + { + proRegTxHash: '7e1d6bdfbe135910f32160c96a38469c52e0c8c3af6c489dfbbca6b187e97849', + confirmedHash: '000000000196e26c6161bd43f1508e5c2723d414f219f1c8e6e39258aaa8e5da', + service: '45.77.176.16:19999', + pubKeyOperator: '0d5a850d41302b179b9009a4969537c5cbf7f0145c94de4306a4e09115ec00248cb1aa76cf04249e2a5104b5cfa86879', + votingAddress: 'yPS1XeEPzHY5rcvPVzQetfTF3X5q5TycyC', + isValid: false, + }, + { + proRegTxHash: 'e341b4207f799d7b216593303c5705c97825331805f32cf54188dd05a7e9940a', + confirmedHash: '0000000035b53f53c072c9f56be6c07cabb6a31a765975e018a6a264a456468a', + service: '54.157.8.145:19999', + pubKeyOperator: '029d0298b3ab58f541f566ba5ddd40e8e1e711dca26b1757fd1b707baea16ce77aaf8a836232809a5e1d301a36f20458', + votingAddress: 'yahTCc45Gu6M51s2qiUNZHpXuqSVSnfDCC', + isValid: false, + }, + { + proRegTxHash: '485c145d2cd83c298bdd692bd7b8b944c6664a409d85babf1934f4a9cbe3702a', + confirmedHash: '000001bc6a26c4bad8be750a040a4a80a65c3b91a36381bfefac669b60e55456', + service: '149.28.127.8:24999', + pubKeyOperator: '951a2e2db5451c398d6e22330dc786cb647e33e6c3eb878d1d9c1f3b8e5cd1dd11d1dde10ad7cd27fdb073fe7ba06ef6', + votingAddress: 'yjKL1dEcj2NfiAGmYsMyTT5brKQVSjMyX2', + isValid: true, + }, + { + proRegTxHash: '999d7bdf3c9247c61681148dafe7406c8407f0d07c4d699a1f501adc075248ca', + confirmedHash: '00000000d03fe6c22a10c02e93a8c095954c2e0be0720479dcf779c3e7fc4566', + service: '173.61.30.231:19016', + pubKeyOperator: '101d302d6c69d9ecb9e13e755947f3af22f63ed4ecbf466ff64bd35c3d86bf2e4d8455ab736715d8f064c8c8e4d3c585', + votingAddress: 'ycyDoK4GK4GR2qdNZ5UXjjS17VnJbQ8wm1', + isValid: false, + }, + { + proRegTxHash: '3ecdbedf3d9a13822f437a1f0c5ea44f290ab90f7c3bb42c1b5fd785b5f9596a', + confirmedHash: '000000376edfdde23aa3f08bc83fa4d347759fbca92bfb8adee2bc8426c3b9d2', + service: '108.61.192.47:19999', + pubKeyOperator: '0634f8b926631cb2b14c81720c6130b3f6f5429da1c9dc9c33918b2474b7ffff239caa9b59c7b1a782565052232d052a', + votingAddress: 'yNr4BzdbZy5kGGeuhoFThj2XjhaVyFQTxS', + isValid: false, + }, + { + proRegTxHash: '037db07b953e2196d659075376e7ba9d85baebed5c49577a898c0ace2515c1ca', + confirmedHash: '000000000769e2816c932194906ff8923fab5adb7dadb92efe8140d950c25fe1', + service: '128.199.99.191:19999', + pubKeyOperator: '8f3afb0dbbfec8610efdb4089f1b163e7f55325f6c0503470e8d49ecf439c848ff9448749e0a383980824994aa5dc50d', + votingAddress: 'yZR51hLrxHBpx3riRoUZt84RPCYBiUqwcj', + isValid: false, + }, + { + proRegTxHash: '0d862fc048631f81cabda9446c5f94c8c9a559d2107db383379697381816d66a', + confirmedHash: '00000b692814c552e626d4c7ccd98e6141b2bf0957098f6668bc75c3cfcd0928', + service: '116.203.204.120:19999', + pubKeyOperator: '028f3d9ff027351da67047d78254333d5430c022f800ce5530835d1f048721a82d87bfe959b74aa447908e3c3cacb63a', + votingAddress: 'ybfmFeetvfZHqRuFfpucdqTeSPRDyDrzZM', + isValid: false, + }, + { + proRegTxHash: 'e157a38e02aad5da99fa7792d07eb8b773ccfef9084d892580b5a920741d72ea', + confirmedHash: '0000000bf6f060f5ad57947c355f8ffc9df1563ac2698a5ddea6c2c605cef576', + service: '173.61.30.231:19014', + pubKeyOperator: '983ca9ab507b3eb4e7b0d31ccef3f4553493ee5334116a3f79689f9b808a201ead332a26f7052fd17123cf142f96d85f', + votingAddress: 'yeLNezEUhMaBEp4Y3qiGwhphJbz244UQsT', + isValid: false, + }, + { + proRegTxHash: '9adf02049b965817cf6fb5b675c17dbc00df7e2fbf68cf3377adfe30e3ed0bca', + confirmedHash: '0000036e3c2522e8308920e40d5b4841c320fffdc7f12bc45526fabe826bcd6f', + service: '78.46.185.94:19999', + pubKeyOperator: '0df074095e5498c6f1b76508c3ae700484d8b0d5cd12a990f3dd54e35b47d6fc943af4223d390ddaf8989d883c84b284', + votingAddress: 'ybbHMUmNCPg3F4GWxi1HksFgRNgXG3k1ye', + isValid: false, + }, + { + proRegTxHash: 'bc36e6c0d0c69173ea0c8a9a821548468e7713ab9bf748c117d5404b4450f86b', + confirmedHash: '00000024498196b428e004ddfad17ea89f7f3c9650701d17656efa8dd9c3d68c', + service: '95.183.53.128:10001', + pubKeyOperator: '0077eb37d4559f880e21dbc3840a1a8ec8c32787fab07bd12e7fde1ad5f94ae95d6e4694f3533799d14e18c683249742', + votingAddress: 'yZQf2eCE9mRL9Fi4ADof7PfkjazVQu1nSU', + isValid: false, + }, + { + proRegTxHash: 'a2ff1f7f998cf0b6745973d4c450a083cd3b17b78814bea849761d109546f8ab', + confirmedHash: '0000000003ed603a8494444732ea2e1e06a091f79578421c5ed5c2f84494cc9c', + service: '3.220.176.35:19999', + pubKeyOperator: '86b437535c51f06dc3064937acb7d49cbf79fa5c628ff7cbc784717de1fb3fd121fab0e2c0b2888053bb0a6b76444805', + votingAddress: 'yUoUuJxUdiHsMX1NPhcb841GtgSbqLygFz', + isValid: false, + }, + { + proRegTxHash: '85541d4fb839cd1019d8f074d130ea6e2fb17dc556250ee7ba509309bfaf60eb', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26043', + pubKeyOperator: '8d08326b410021f9fa896cf73b5d8dba5b05d630aa9c9cb6ac1ec8c7bdde32a67a3a6418d30afa05e6e6d95b15629de5', + votingAddress: 'ygH8LxcbTyT5vGkgtFWH292V57SmaNgaVn', + isValid: true, + }, + { + proRegTxHash: 'e9d5e03a9c68ca226ba4b11c81e38e44bfec6df0b9f9ca19866e479a2558f9cb', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26058', + pubKeyOperator: '86f774a0cc60b899182e96855a9798d447031b70f7ee1753daa45a00cb0252e058c93c9c6292f71e3935ede70af6e2ac', + votingAddress: 'yRFBUW4hvb8LSEdhQH5F5u6Fm8ZkiyFjak', + isValid: true, + }, + { + proRegTxHash: '48eb1b545d87e712edb1382d8bee300aa0bae80bfd0c347920f4afcd0ea34b0b', + confirmedHash: '000000000809ffe583272d5bef8288ac60a564ac8e1cc85b30c991ecac66fb49', + service: '35.168.78.191:19999', + pubKeyOperator: '051b1a638ba22cba300ba0836304586ba5572a525622c4dd49e7178214985277eebad66a371253163e35e93d3b44081b', + votingAddress: 'yPwCX1AfjYmfeDY2m4oDMRwE4XjkSqtcCa', + isValid: true, + }, + { + proRegTxHash: 'a19202b42318e5b2ae7057cf045a29cc2f10e2cd3906e487c4946d5e501fe32b', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26073', + pubKeyOperator: '01ac0e55cb271408d6304b41b1d0dff84ee957829d3c8f7385e76dbbc1b67ba91b70b04dca50d3d7f515c6cacc8bb908', + votingAddress: 'yWNu2uwY3YgethASL8fGgi4xNRdtEo1G2u', + isValid: true, + }, + { + proRegTxHash: '78c43475cc270d075a38bf9959c590492dc8682a6feb46157444d29de4a13b8b', + confirmedHash: '0000002f23cb3daea22301ce75043b100d78d4f58532aa9db486889b595f274e', + service: '173.61.30.231:19008', + pubKeyOperator: '1828028671209b5196d2204d5bc3ce3ecd554dee9ff231883f04e67bea856fcae19d7a6154039140e9e3a6c6cf3fad4d', + votingAddress: 'yS3ybhnzXDryoVqqxcK7YXfLyGy1pSctYr', + isValid: false, + }, + { + proRegTxHash: 'f39722e1e9d02ddb49512b16674868a865bae2a912401bb6b006b09a74186beb', + confirmedHash: '00000000093cbe98651a7dcb1d9df8f716949648d30131ebdd98fa4478ce9537', + service: '3.13.34.147:10007', + pubKeyOperator: '96395d8ca159e5ca66eae7685beb6766a6c0ae50b4569809c4ecca3e101a1f210bc35637473b5afd5e71bfbbc976277d', + votingAddress: 'ydWdu2QwsmGBzkrozcrp7GcEkiJ4GxZ6ek', + isValid: false, + }, + { + proRegTxHash: 'f8293d83dfb38fa7a6c34928e9171fe6a112d5a5b1d07592d59f37a23ed0a00c', + confirmedHash: '00000000017be48320860de22051bebce194b64dabc76b0eab1cf4c1c321210a', + service: '52.52.139.186:20001', + pubKeyOperator: '803d3e3a2593dfd56111203f3f7c562d1df639d57376d1994aff17260cfbfa576bfed870eedf234bec169e2f8e6c44da', + votingAddress: 'yZHnhkJQn5gQTgi8ED41qDMT67S7yhYPmR', + isValid: false, + }, + { + proRegTxHash: '8d0099a82609e4c9c0ae42c5c98aefa8bed3393cf9e25d138d14141fddf50c4c', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26029', + pubKeyOperator: '8a596212592302df7247e9b29031a6f1ae4555e6b177b7c5810c9221d1cb7ee84fb3d2d6b604e0af1c59676efe784766', + votingAddress: 'yRE2ruPswgXQSXdMq9MLk1rm7K7gSM5uSZ', + isValid: true, + }, + { + proRegTxHash: 'ad9ae35caf7548cf3df6343dede0e585702eb5cf80306e76b65db2c603baa0ac', + confirmedHash: '0000010f227be0c01e195a477638557e8b2e92a909d026070b95c09a20984f9e', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yYvFHSBwpv6WzKwVK95wP5euS8wUaejxS8', + isValid: false, + }, + { + proRegTxHash: '48c29275ad2ba66954b3ed4d58a29c799da0abcfbbd38da9646079e94610c8cc', + confirmedHash: '0000004b78f5f2dfe557481764bb7bfdf86eca6245567d49e9bb4ccd1c1684cf', + service: '136.244.89.226:19999', + pubKeyOperator: '90dfa669abbc6504966bf8cc2b4971db18a5052b70475fdd5e6f427349635ccd6b9c7869b52ff3133c5661412ea5ec13', + votingAddress: 'ySHx2HYftzNkcsYv3BGgbUBBNB3wTvXMLB', + isValid: false, + }, + { + proRegTxHash: '84bb939170f4714be54d6217cffa5a3818a1c521115d45141b4df642b1dbc5ac', + confirmedHash: '000000000037b5b46625cb58a7b4ed74c62752d496cba421b79179bcc9a51364', + service: '109.97.214.43:19999', + pubKeyOperator: '0f4002936319c495d9557ac1bd514bc760cb8db72dd99d5d20af93dd5a7570974d75e5761fc494de28127ae02413819c', + votingAddress: 'ygVha4ZHSZExvWXaKQxhycdp7aQzYMJWJj', + isValid: false, + }, + { + proRegTxHash: '84cb17f8193558315fbb5acb6b285f80c3727489f3f167380189c73751ee99ec', + confirmedHash: '0000010f227be0c01e195a477638557e8b2e92a909d026070b95c09a20984f9e', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'ya8DyA7Xpo37rXAnk1DLjUvgs6bGXbhEQQ', + isValid: false, + }, + { + proRegTxHash: 'ed8575335b7e0b420b09b4b8c530711b98aa472504d91bbca9745873a106cb0c', + confirmedHash: '0000000006de39a4945dcab34a2221311710875eda3105b47b1f8e08d34c13c6', + service: '139.59.81.170:19999', + pubKeyOperator: '8dd3b8d006c8ea260bc6158daf0680c5cc7cf4936458024b51ea2036a800ec6563d75135004055d94743b8341b701358', + votingAddress: 'yasGgaGhf4UKwhnZHuJjb5hVWW6Wu1DfzV', + isValid: false, + }, + { + proRegTxHash: '2695fed97527f712995a207d278a1cb7fea614effd3f6d3cacf58052af020b8c', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26018', + pubKeyOperator: '91c5a9a2513c46543acad374e11f69e63df9583e74e511e619355f2cb7c1b9cd7b4c1ddf3f33c28a8c046658958c3a10', + votingAddress: 'ye7n6CRqa28tBJLRqbseDiKW76xrgUaDYh', + isValid: true, + }, + { + proRegTxHash: 'a5acd6561beac5bf61f9d6c084a288ed763f8fa891ac4c762ca3983203d14bcc', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26047', + pubKeyOperator: '9969c5e9e10673d15a23bce8cd61f83321570f8d5e95f79a640e4f2cccfe0e81d80c9a5a3c6f3cc2e7b9b68e83d6d8a2', + votingAddress: 'yYqtxWz7CF6SLvMprS95owMt7HD7MEH7EG', + isValid: true, + }, + { + proRegTxHash: '9767a4cafa9d1057b48de795ea834a15664b58a79d75a4f826299ce1ba11842c', + confirmedHash: '00000000006fb3a7ca777f44ad0880e6fc0c1eb4e96187d2e489af7c3ca51e20', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yPmhSriYQKX1cD815hC5gNK1LXnzv5Udnf', + isValid: false, + }, + { + proRegTxHash: '306c038de5c583febcb09b55f527eaffe177acd9d118e8076f61349a305e902c', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26008', + pubKeyOperator: '052abb8468545b0593010c326d358cec47a3079a36c6f5002b2c36fb45aeaa6ef00746d0e73697fef01704d51d870494', + votingAddress: 'yXF7nWfRxi8pyZeCbz42gv6V9HALZYNYrU', + isValid: true, + }, + { + proRegTxHash: 'd304e1e869e1a9aee1952f06bfd8dd43f8a4d12231c5d43641a31f89a53eca2c', + confirmedHash: '0000000001595028dde2b7eb972583b4d3f575c88c56bc1145b8befe529591b2', + service: '144.76.66.20:19999', + pubKeyOperator: '09591bf26ea6f179457440d73ebc70c44310ea56dad7539a93be903e28788cac013b646e3ed4198ca74a8325d8a721c2', + votingAddress: 'yWTZFfywm6HB5RtbZxV7xaqVT5W7hB9WcS', + isValid: false, + }, + { + proRegTxHash: '714874684cfabe0cca907ff0e61bde28c2fc1a8840c485fa14ba5660bfad5e2c', + confirmedHash: '00000000011fa90e02fdf218fbc8e712d45645ac4fc939d2f8d46d03e7054dc7', + service: '182.50.125.85:19999', + pubKeyOperator: '11b8a3cdbf872f868b08b211878ef11a0f6f7a7ebd55533864aa98e53e194faa159ae2d13a7625384a3fd1572f68deb4', + votingAddress: 'yS5ZfPiQ2hHd8LtwcKK7wjjkZxWSyZUZdo', + isValid: false, + }, + { + proRegTxHash: '672830015f3330a96d5aa74d43b6dd2f6896821d8caed7fdad6427c74b7a7e2c', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26099', + pubKeyOperator: '151436dee05a55afb36dfcc21afcd193ec0852d2f2b27d86f3ce8c05e7e4d4af4e0023d0089b0ef3d41bcdaf4556b1fe', + votingAddress: 'yMkQeRpNkwA431mgrmvzFjoFVE6M571D68', + isValid: true, + }, + { + proRegTxHash: 'cfcddb737317b86698faef84734e9e655fcc2899ee449d13ff70b014419b6c4d', + confirmedHash: '0000000004e5ceefa44e7f0fe7a25a27188ddd24bfa4503f7e7f355e8b851e72', + service: '52.21.8.124:19999', + pubKeyOperator: '009a876325699d6979757ac10d6b37eb7f6690a40447f6473779eb9130975998d3a9fd6e9ada19559730bc017843ce12', + votingAddress: 'yZ33XKzMFKpFgtbjuWisRRCxQPZTB4f5ry', + isValid: false, + }, + { + proRegTxHash: '31a74f4cfe217fd8504e6b210fcdf12243828a67191448b649b648f7397da08d', + confirmedHash: '000000bfad696395b22512fdd61376874caefcd5dcdb4382b38fcc118a56198a', + service: '139.59.249.65:19999', + pubKeyOperator: '82f48df5b39fac4fe299c0741cdf675eb53aa0b936ea147d4883b650596887142e4fecf91087799de85312aa47c6d601', + votingAddress: 'yeHoJW4Pu8xMGhe4wKAmZEzdWR7aPRwXKZ', + isValid: false, + }, + { + proRegTxHash: 'cf774e2a4bcab3c7e7d2d934cd2977b090fc1e414d26dce53e16cf2cc5971ccd', + confirmedHash: '0000052c9685b51f2143792ed6b2f5cda84e5b1bdb7783268729114e8d0d08f6', + service: '159.65.105.41:19999', + pubKeyOperator: '002decf89a99afc7bfddbac08c6c25a028e58f0c7863b7fa6811ff84afcefb933b510b78df6551f844eb2221b0c0bb53', + votingAddress: 'yfSaH3f2Aybz6GnTJPHbt6rHjbz2vrAdMD', + isValid: false, + }, + { + proRegTxHash: '87c5a82f46522a809f60943985bdbbe6ab131f49bc4b35602c0b2ed34dab354d', + confirmedHash: '0000000007a6b62eed2e73d3fa9816fa237289df6ba87cd843e98c374bf78bde', + service: '157.230.40.102:19997', + pubKeyOperator: '84fb8f4119d367a2336982fecdcf326c56b7c09c0911994720ebe2a657d5d95252be1871889b13f81cdd16d49e15a7d3', + votingAddress: 'yQyDLSJ5EsZhpeAEDZHnrH2EEx7cJdJwgT', + isValid: false, + }, + { + proRegTxHash: 'ffd0d1e2a321312a66de9eec7107ddf6df24db56366d9e5095c20996ee54196d', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26036', + pubKeyOperator: '0267a4b0aa2305e081e19b8efd65de037862b305f0c5c9871c56ea46145e125a77db6323155ca8c37abb147641806fac', + votingAddress: 'yLte1V24YadioUp83Rm5KJtj8kumTneBqF', + isValid: true, + }, + { + proRegTxHash: '75adf981e3a77630507882a9a41d551ee1e5b8ed570e61a855008ca293e615ad', + confirmedHash: '00000000001387caa35200203c654a06b8b565f3a79b8596a0621f6df24a5895', + service: '167.99.110.59:19999', + pubKeyOperator: '0fd87b62bf91162008451c1f00a1d7bd65ef581e88c153d105970ab30e451378966b6e4141e68024b3976461605e8402', + votingAddress: 'yYBa2QWVSPp4jDCLDP4caVNtA3EFdVDFMq', + isValid: false, + }, + { + proRegTxHash: '0022afbe93054ca11ce9b67892661af4558597bacff0ab82bff05a2b4a89ca2d', + confirmedHash: '0000000bf6f060f5ad57947c355f8ffc9df1563ac2698a5ddea6c2c605cef576', + service: '173.61.30.231:19015', + pubKeyOperator: '94b7723262031b6cd2e79b07f36a794d3e684c538a6f2418fff01c027fab1ca4663ab0b92670ee1797fa71d8676362a0', + votingAddress: 'yhyMruXFX6waubpxGZx37FBF5r5DRb1QAZ', + isValid: false, + }, + { + proRegTxHash: '0631c61e2ebf3d2f3b5022022b304492e935dfa25f9f52d13a45b448a61dea4d', + confirmedHash: '0000000001619c5be84cae531b07ca3069e6dad3d1fe71d945a902b77929662f', + service: '116.203.197.7:19999', + pubKeyOperator: '186053ffd90c84db8fb369e1178492b3a0a3941d33c43cd84b839d92668203b6501c786486083eff2b229cec3e0a190d', + votingAddress: 'yRSrEfHrzgzDfABrZ4AsAXagrJkvipfwdT', + isValid: false, + }, + { + proRegTxHash: '2cc0a073e9fadb5cea5cc3303b8f85ed603dc6de043f0ec06edab72d886c57cd', + confirmedHash: '00000000022ae73001d449650701ad35953fdfdd0dcf545bf9d3b446c39d592b', + service: '95.216.174.152:19999', + pubKeyOperator: '8dd3ff4dfb358ca5c5c58f5c163d73482caf44427b62751b18255a456b8edb175f887db87b3b214753045f631db58475', + votingAddress: 'yeKsmtKwGhSaYmSUexnLsbGkt3q9SNPJqx', + isValid: false, + }, + { + proRegTxHash: '274ae6ab38ea0f3b8fe726b3e52d998443ba0d77e85d88c20d179d4fecd0b96e', + confirmedHash: '000001ccaea749abb1b3253d8b076279fe9334759fdea0171df8bae9d8623af7', + service: '134.209.231.79:19999', + pubKeyOperator: '0db6da5d8ee9fb8925f0818df7553062bf35ec9d62114144bc395980c29fcd06b738beca63faf265d7480106fc6cceea', + votingAddress: 'yXuFGzX412qTAYopYkge6RQgjtXsc6c61o', + isValid: false, + }, + { + proRegTxHash: 'c60e7942e3ddacea393c597f56632b90936ba1411a0666eae5913d2f4ca575ee', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26096', + pubKeyOperator: '07785a51a1061d596e135cd585e221269ab19b24d1a14f3ed9c2910eef5eb91b1676c8e5a1b02fe9abffb9cb13e85e0b', + votingAddress: 'yUb3oNMM9SRd3CMVKJj8AwwXapj2mYpVSg', + isValid: true, + }, + { + proRegTxHash: '438a51e16b84584f2328da338ce87277991fa6b83cb8d39919dc08049f128a2e', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26019', + pubKeyOperator: '8df88c3300207f57363057baf4b5a09d13605608d7a621575148d6ff21609fdbb0e10096bf66c11a39adc0f30d4eb655', + votingAddress: 'yiQNdUMdi9CeYGL1djDg8br8RswTzmV5SH', + isValid: true, + }, + { + proRegTxHash: '8eecbc2ec317e3f05bc6daa16f800a20f9c6a311b9c5e870848a4e418a294aae', + confirmedHash: '00000024f9d5cd5a0b26d87d0eb18d41801e08d2676a2c07ce833c35fa9dc084', + service: '95.183.51.146:49999', + pubKeyOperator: '92f5e861ac88ddd95e3829afc45f9358ea0973e19da8e42eabbfe8f2d9e5fa32204e7f1de5e20c7e45ee51ab262cf7dd', + votingAddress: 'yWYP75RuRAQ37BGzUKASxcLHDP4b8AGxHY', + isValid: false, + }, + { + proRegTxHash: '6802ed5074a42b84a99b5fb6da29d04c2c80e6c9dc437203acd698ade36c6eee', + confirmedHash: '00000000007db17d5fc45199e1b6a2e3e267a30a2a43d53165e3f97081ca51af', + service: '34.224.152.100:19999', + pubKeyOperator: '8d6bea256f36d8b92071b66fa64f4023737cbd1b5dac7c1c9bf514cae400c332a1091df0ba8cd007d641a92507d9cbbf', + votingAddress: 'yMSs73kBNfVceVQWxQYerbDNdBpmWAkdti', + isValid: false, + }, + { + proRegTxHash: 'd5e56587681a42c22b0f9d0c3599e0482f44a1fe774990e6e48446a6dc9647ae', + confirmedHash: '0000018d54d1c79cc69bd2f96cfc1ba33047d0aba07e3ca837a483f612d007e6', + service: '149.28.127.8:23999', + pubKeyOperator: '191d269ab03adbd6c74c4784b0edee94af4c37396a1fa525e21cfd8ff2818fc7e3548a493ffbae3d409e434bdd004811', + votingAddress: 'yVhmKQPDjQMeZzvnjDZLzQU6jUEySVsEMd', + isValid: true, + }, + { + proRegTxHash: '0df89123845c267278fa7db47a8350181575fb6d388c0718b398845f29bd4f4e', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26014', + pubKeyOperator: '0018772d45be3b36be43727f549ca2b3e5abcceecb207ac538d5ac318054d801f9d81f25ee4e127fe408bd79872428b3', + votingAddress: 'yYeR67HLZVujP52GiTgMzrLmYQU6c5wQuF', + isValid: true, + }, + { + proRegTxHash: 'ccc3668156451b3e10ac5c97d60e2c20fadf88c6266e3b2a9afb0e33e658734e', + confirmedHash: '0000004049f489e97d0facd650e8a0eeceac26d29ecc638ef14ef1b6d0f84b83', + service: '155.138.226.83:19999', + pubKeyOperator: '057b3b0190261b1ded22b9c58550f7bf17a150de6a755a5478988b58e32bb7a53e7e6f9981bdbf416324e75ddd9853b8', + votingAddress: 'yfPx6hHX4CzRTfdfTGDimHWUnK7fYpyUWH', + isValid: false, + }, + { + proRegTxHash: '712c0edad9b39de087a2422aae59cbee77b63aef06de6db44b2b8287303620ef', + confirmedHash: '00000236f23ce424517a82b07318ccf1bb9a4f296a46c2e8c62cfc4f29ab7679', + service: '201.16.2.5:1998', + pubKeyOperator: '0ec57774146e447ac6cf131a40fb37664ed5f9ff45b59d22cc68f2aa9f4659cef42235b63c3f2c3ed36f8b2344399d2f', + votingAddress: 'yUK263HAwotnvFMhCVyozcQAZJmTuTKNS5', + isValid: false, + }, + { + proRegTxHash: 'c48a44a9493eae641bea36992bc8c27eaaa33adb1884960f55cd259608d26d2f', + confirmedHash: '000000237725f8fe7d78153ae9c11193ee0cda18f8b48141acff8e1ac713da5b', + service: '173.61.30.231:19013', + pubKeyOperator: '8700add55a28ef22ec042a2f28e25fb4ef04b3024a7c56ad7eed4aebc736f312d18f355370dfb6a5fec9258f464b227e', + votingAddress: 'yTMDce5yEpiPqmgPrPmTj7yAmQPJERUSVy', + isValid: false, + }, + { + proRegTxHash: 'e0ef260e49c9f2139825cc98504c536397595e05813cc1de5dff2eb793aeb5ef', + confirmedHash: '0000035bd78b69e7895629c0132439b0449af9c9e62e3ceb4a701ccc27cd2632', + service: '142.93.163.66:19999', + pubKeyOperator: '0dda9adbc22cdf89c04e8ee714da7d80dd5620c1d14e30780668d3b782b2f0acac9f00a556be1548733c1ad1abdd96ff', + votingAddress: 'yUpPpuLw6KTqzRJhuu7cS75khDgHyBgrMR', + isValid: false, + }, + { + proRegTxHash: '1659e06c825212c9b11325760a18f6ea06194ec4efd603f03d8704f23d818a6f', + confirmedHash: '000000000ca93e850827b361743c25c8508e6e42efaaa331cc1b54326d9fd179', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yUTy9Fb2ULXdgyqYtMMbuUWpFLaDgUqT3f', + isValid: false, + }, + { + proRegTxHash: '7893b72d36e71a7b83a1fff61e4fbbe1400b11f12bfb349923397a2df3a9ff4f', + confirmedHash: '0000000024048798dd72076e5512cd8eafa7695a07b596ea57dc84393dcd9bbc', + service: '54.236.214.8:19999', + pubKeyOperator: '92427cdb8c9694e0c6ba086ed7c00dd9f52ca18e335f65de8a839a378b1e040b279c05e3822e7c2fbd57fa8852d04cf4', + votingAddress: 'ySTEAjUkZcv3N2MeieGcHPPKSXBw5a84pa', + isValid: true, + }, + { + proRegTxHash: '52716cf4f2abfd9068500ddb6dde92c95a16d23be90d57e74a7efde13ecb6f6f', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26003', + pubKeyOperator: '0e1071aab685e8e41128eacd9d189c04b6c1f8827784c9bcebb34f43ac2f82fc6514552fc8085958784be4fbe57b8699', + votingAddress: 'yh8uygxVWDBcUF6BTc6coY7Tnxrnv8yAas', + isValid: true, + }, + { + proRegTxHash: '1a3b5e4d4e06cc89b26ef6e4b962831ede8e3e6c7ceb0ea5de58f6c392aff3cf', + confirmedHash: '000000000be653cd1fbc213239cfec83ca68da657f24cc05305d0be75d34e392', + service: '173.61.30.231:19022', + pubKeyOperator: '0d7a51dc63be4cc12a7307e8505b6a737d2bca66af51faec6c448d2588aa347e90ac4deb3a4af294614093ece40a5fc7', + votingAddress: 'ySBU7oXuuTSJqtmUArMRFsKefJPtEDkESG', + isValid: false, + }, + { + proRegTxHash: '4008a18371798ba28da7c9a581daf0aa92c4ac0b980c3438936823274f64dfef', + confirmedHash: '000000000033140b252436f13e4be668c28e3e9948f9d410281be397f4788c7d', + service: '165.227.10.68:19999', + pubKeyOperator: '121af4d4c49a65e1439a27ce0f39a2eca5ff751e9998c0fea7a3c2b13731cfa47fc6a56a313a38b448f3792fb60dc117', + votingAddress: 'ySioPN6smXqGc2d9vrTV3TkXTgtdrFevbG', + isValid: false, + }, + { + proRegTxHash: '2a0a24107934da0bb8fbe78d776724753bc8e8eb2ead3ec2c51cbe07392a8a0f', + confirmedHash: '000000b5217824002903c8f12a10efbf3aed5f35d304ae6c97ecc82d914feb61', + service: '149.28.127.8:19999', + pubKeyOperator: '111c6da658f21af129c37abea6777df93c768fd3e5aff681de06f78ac01f24803e286d5b754ba1122db32ae29e0ea401', + votingAddress: 'yS6yCeyT6nKg5CHC2m1GW1UPJq5DbuLvX5', + isValid: true, + }, + { + proRegTxHash: 'f2c73787f74e58aa7857f9872f7602fa20c1c50e465d2ce7d433525f7fb6460f', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26078', + pubKeyOperator: '10a2ed4c4111c5b10cf207af3f924e8401db776498f08768e75e0d412f73a79d45d771c1eb2f00cc9f6c90be02cfee72', + votingAddress: 'yeEYPuTC2x8QbCMTfQ8HbLSNmgxgDTVZE6', + isValid: true, + }, + { + proRegTxHash: '74f428a70f701afc33b26dd89dba3e25a3ad42168284e64c79572cdc69b7bbaf', + confirmedHash: '000000373f1b228238e7a411bc2ad3719cbd9a475e880ea55e24fb5ea24aa3f5', + service: '173.61.30.231:19009', + pubKeyOperator: '925d20af1a6d0ccd3890f0aead4a05a59be22e005b6d732f855311915b351a9153b2c83d84611b2c9958f806c93f7b5f', + votingAddress: 'ybc3AmPjvoGD2b2gfd6iEsZiv4h4KAtS8S', + isValid: false, + }, + { + proRegTxHash: '67e84c0e7dfc04e2e996c0953a74bc5d61fed1faa7a3e3b6d377c3926749bfaf', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26024', + pubKeyOperator: '96fb79395865f2c2210608b388dea41c88d35952485d34c8e566f706193a6080d36e0c645027917e8491c5ae35c65b29', + votingAddress: 'ye3LZSdK8iAKvSWfXDUh4euBpNaH8fgtCi', + isValid: true, + }, + { + proRegTxHash: '77fcca4a0e43f6e0b96687a87b4272eb8523315c8f2a176d0a2df549a869f3af', + confirmedHash: '0000000004b27f77bbc9fc8288357690c9777350ba5b0686d0fbfdd572fa1e86', + service: '139.159.206.76:19999', + pubKeyOperator: '914aa95d1c7d7c39e0a3b213b6497f5c8624d4f476b8043b22c4f30cd05bc037c80d02b42625d743c6a18d0562aeb579', + votingAddress: 'yiTNq8NMuYdDKgzfuY6JCZUT3DLjqJ8taK', + isValid: false, + }, + { + proRegTxHash: 'e220b3b30879a4e489a99f265f00aeafaab0fbedd0ec3fa194befc03fe93ac30', + confirmedHash: '00000076d69bc315d8aefb708b5b09343653c948ee5307878548e67eb21edb85', + service: '149.28.127.8:20999', + pubKeyOperator: '127147f0cd5bc3ec7c8c5704924e855d46dac21de72e2de112f7dee7c9c3f9c40d4474c0d9ae36a56b800659af62c16d', + votingAddress: 'yPwDJdyhYViy3MybjC3kbDvK23krs2i4RT', + isValid: false, + }, + { + proRegTxHash: 'a3b3e4b3d98c934f056a2e76ec8ff07ef8473b87f559295ece2254b3820e54f0', + confirmedHash: '00000074bbc8417236c769851d0209eaf867e97e396f1bace85b7ffae4df0e9a', + service: '210.90.210.90:19199', + pubKeyOperator: '81bc001c31c71b0d4d4f9ecfb205e914fd9cdaab4e7dbaf0b320d40f0bd5b193d1be809ca34eb4979d661f487b21124b', + votingAddress: 'yfrLG1VEHeBgCMnLsxwnhs7U7qsTHov8yG', + isValid: false, + }, + { + proRegTxHash: '94044c070f9ce6bdd05c2b655ad2383c8402a74c10e0a9a3099d759b33cb7630', + confirmedHash: '000000000121df4660f5cafa24c6662d24cf28563f664e00bdddf835a8df5759', + service: '108.61.189.144:19999', + pubKeyOperator: '996f5888a81b9668c16a12c87134536e3616c929a7b67b37aa06d3eb7d7e405e3d3148ce7a072128c9063e1a8042eccd', + votingAddress: 'ySytGYbYw7rhmuNTvDapSCaFgMxAuKZRXn', + isValid: false, + }, + { + proRegTxHash: 'a671f057d9937b97c9d256e4eca70318cf51f7259fed49b9ab441f13cc1b12d0', + confirmedHash: '0000063950f0cde94633aa231b42e384de998f9b33c11b5ec532f366157cd4b7', + service: '104.248.218.23:19999', + pubKeyOperator: '898839eeb51c078a7785efeed45b73db7e97138eb950b84302f2f13d6b33e6f8e58eb14e52c4a9c168edf50f35d0a4cc', + votingAddress: 'yVAgnd9R6zDigtxURRc2YLbgiNtnkggvtk', + isValid: false, + }, + { + proRegTxHash: '4b29fd3f568db741930c47f3cbdc1502cdd98f8bd84fd654a369ef2af9993b50', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26074', + pubKeyOperator: '8d23e61e3d839113300bcce34319e5d766a0877ebbd3f9785da2b5c755b74662040b2c80f6aa02e0fd2b0907adc45aa9', + votingAddress: 'yLqFbiMseHELxJvjfVN6G5cKuaLmVDaSBS', + isValid: true, + }, + { + proRegTxHash: 'd2148afa283037e255d65a3acc82428d6a712215003963a60c6e015aaaa4bff0', + confirmedHash: '0000000025f2f0ee8753d65d4e98a103e1b37564f2c6aec3e9c71395df5e3557', + service: '3.20.14.143:10002', + pubKeyOperator: '8de1a5d67b291f75e87e20eb4b9fa7246dff5bcf4030ae26c321b1845609d50d04b240cc51862b4a7b3dc9be4aff050f', + votingAddress: 'yTUYhXxt9F8YWV5MWWYEaSNxbi6D4vRhd9', + isValid: false, + }, + { + proRegTxHash: '77e85d6d4d7d4bd4183968b61ef0d5b3a7d15df9b249a11da6776188754f5590', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26089', + pubKeyOperator: '0fea9a101cd0ada9f5002c531a7578eb3dcf550fb6715f70a572955565fb7e6e4fc7e99062323dabd1ff5fa963d9041d', + votingAddress: 'yWvkJMQcAzg1L2cJDKSaBrjb7jBFSGc5ow', + isValid: true, + }, + { + proRegTxHash: '0bb4178501da3646c2ac908c25dc6d890bcee788bde6b8391ccde7c648607590', + confirmedHash: '0000000000bda4445ca0755b54b311ae43dddbf2a5a2ef7c2ed923bd4fd4117c', + service: '116.202.68.142:19999', + pubKeyOperator: '8ef072018a444bc1f30885a199087d2e2200ea31acc4659eb0c05cca30f83e4bd940ed27873458fb605c92556883933b', + votingAddress: 'ySCpVr9PC3TG5Tr9pbd6CsgUpUxnXuQbwR', + isValid: false, + }, + { + proRegTxHash: '25d0e1d65de8604437fa2fa7ac530258c86e7019aea8366f72ff9cfc8e7e9b10', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26012', + pubKeyOperator: '87589365c1690252507c01b234e7af371cbf96b3ab661a48bd5ba9b7a082f0299746f9244047cb799d5a4086fd25470e', + votingAddress: 'yXZQFeUp8ykWEkGWn9svqbPXBNJUxj3dfK', + isValid: true, + }, + { + proRegTxHash: '11df45fae69d6ab445e87f787f1e90f3aca14d3c624ad0713f4e9938dc98bb10', + confirmedHash: '0000000dae9c800220bec038f0c45cbb1006d0ee60c38f01dc1e89bc38f3063f', + service: '185.50.195.156:20003', + pubKeyOperator: '0e8d315e57e559d5239fb15cc3f2d167a57fad2a23c5d90f20e40798f6690b783e1c525cb1ecdbd1261e3938f3d805cb', + votingAddress: 'yN3DoZ8QDedfPzR82MYivpyMZ1Waz4RYKq', + isValid: true, + }, + { + proRegTxHash: '422456a81d1601f5aab4494d935919058905ffe2dff342e8be1345f5e5b46c51', + confirmedHash: '0000007f118f2c886e4a67c1496887c77cc51af327229dd0510eb626d8563b27', + service: '155.138.239.217:19999', + pubKeyOperator: '14f9192c3986e589f919f428c43770c3eca5c4ff3722d967d8f0d4b69ec3ec02fd876737fb06880a54566f5639389972', + votingAddress: 'yRvp52x7q6c1aqn4C3FDDToASaSVnhPDfy', + isValid: false, + }, + { + proRegTxHash: 'b2d240e9669e8a31a13bd3d51087f2b52c8f7b5e0561f9a595c64cc454e268b1', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26046', + pubKeyOperator: '0e2f9d4a400a2a6995fcbcc5e5d651575518a2f92aaec556b0a0fe8699133ba527dee3e1a17a5576027a5378cbb96649', + votingAddress: 'yMNRfb43JomUWWCRLmPraBYUTfqEyjwvqT', + isValid: true, + }, + { + proRegTxHash: 'd2467a06e67cfe57ce8723f565c296f97abb663380ce87bb8d5e72989e3301d1', + confirmedHash: '0000000d3dd0394176164ff9357a3306ac84ad2364078df5027fba633e7d036b', + service: '104.238.156.109:19999', + pubKeyOperator: '145a212f37b991a6c050cfaaad7e83f1347486984174e8f446e59fa6c225691ea679f70613d829f536bb1a311d812cb8', + votingAddress: 'yUqFFFHVwuMW51u1VAwmWynhk7ABP3P1nj', + isValid: false, + }, + { + proRegTxHash: 'a690051e69de6e36eeba664bff34e017f973d27ce91c1f2247120e8ce586b1f1', + confirmedHash: '0000000019091441469a98f9a8889d94e54723286fe1cd13703aa6b652fc4863', + service: '149.248.55.77:19999', + pubKeyOperator: '8b165f653a3970a17f432f6c3abb8b681c71a3775f998fff322341d2994767c167c8a43b1b4661b9c01ef637763d4d81', + votingAddress: 'yTMbtGvG722zFbkpAnBrQvJ8WXH2g2kosL', + isValid: true, + }, + { + proRegTxHash: '7ad29bc543761bfa9ee6a8be1f32bf5bbbc4f979d036676835b4717f8abb9211', + confirmedHash: '0000000000b12022bb12e33de5b2e03187cb5c0702c9b2122c411de42a72957c', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yPSonWf7LPBGKtJf1VJ2PEG8n6ekmpZomU', + isValid: false, + }, + { + proRegTxHash: '5fb5d9a4d16ead76d127e6829423c9e3d12f4d3c49f7ef6b3387886f3244ca71', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26090', + pubKeyOperator: '032dc187d6097df361f4d0ea7825d02e1bf38636c4883156d08032d0afc010c737bbc6eb59977774a2691681c77c97e2', + votingAddress: 'yfBpEhxNCDwropPqkytUtexb3L21tnVoau', + isValid: true, + }, + { + proRegTxHash: '11eabc1e72394af02bbe86815975d054816fe69006fdc64c6d7a06b585e5c311', + confirmedHash: '0000001fd4305c32af36cbdf651cf1585e1a2a5b93a871186c63cfbe67c8eb8e', + service: '95.183.53.17:10004', + pubKeyOperator: '14926e7ba179612df5cb1cc4ebbe311cfa9679e41f14ed7b35d12cc33d419073f013bf751be85f2b50e28910df332463', + votingAddress: 'yT2owpXECuYHnZ8HuHuHWD9anynSQrfcDL', + isValid: false, + }, + { + proRegTxHash: 'c36584a1242574644c1a1620703c55200cc0158de276dc388ffa9815ec328c31', + confirmedHash: '000000000f344db8af9362bc9e4f3c14e73f9be6bd5f36a523b2a37195782d9b', + service: '35.163.226.32:19999', + pubKeyOperator: '08b4c1a8b9c1402ea84afe7c47f7e98d657df873b9747a0e4a497120ec62c81f314ad91a6f3384648e7e60f2734554f7', + votingAddress: 'yidavU3B2BUNzaUv3gW6nmV4ojLNwPeazt', + isValid: false, + }, + { + proRegTxHash: 'dd64a93eed7d3bec470466e8bcd0f107c8137ff7342d878dd04c0d6427ff9c31', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26053', + pubKeyOperator: '8a8f81687d1e01de856cddda83db3b3f9adb0a269583ed1a32bd7979c61fa1662f4e66821b28f406e3f1ba4e4a4ee0a0', + votingAddress: 'yRy9wiGXJK9RH9foyjsSTaVPZRyfuzrQVC', + isValid: true, + }, + { + proRegTxHash: '72a6a2a5c2fb260fe3d41913ae019feb1d2489867e85f57cd1fa994bbe3458f1', + confirmedHash: '0000002666729a05b9de9021413132d9998be62719fb9f4c4aaac6f6a33e1318', + service: '95.183.51.146:19999', + pubKeyOperator: '987d8b49e8aca918aead0d50b28fd0f61ed166f28b6365acef6a9aaee144a692f5b3cce00a40719917a042d16d1849b8', + votingAddress: 'yMr6eatVutuPXxqBM5c2N8F2r1GKcfoe17', + isValid: false, + }, + { + proRegTxHash: '3aa33cbad1659ba0bdf6530b5ba543592e2f30c5a35cd89fac77604317cff0f1', + confirmedHash: '000000000bb2ac2d297fb8eef2cdac4b13764c0b9fd161f3eaab47bbc38e44a0', + service: '116.203.200.139:19999', + pubKeyOperator: '8130957c5939cc5aa59a9d7ebb88a03c8e60175230f87927f9485d452f6c844454148d8efdeb9e3216600b7f6645ecb8', + votingAddress: 'yhFqQoH3mPX4vEcP7rqUPFMxxaJ6qiR5i6', + isValid: false, + }, + { + proRegTxHash: '8bf18698fd403d18f976fc5f89d79db263fb354a63781a512e2d48faa17190f1', + confirmedHash: '000000000a4a691e8ab28702727608eb72937bba1d621e4f3c5cb88eb6d3b788', + service: '23.240.232.195:19999', + pubKeyOperator: '8041404bfd1cd4b71416116af92b7a17f42c47bf3dbc2294369fe1691eccb9ba851183a0e85a4fd728c946a582d006a7', + votingAddress: 'yLm93fnYyxrBWupEhctkeU1zgvs5zU8ZTV', + isValid: false, + }, + { + proRegTxHash: '87fb91d219ad0ad8f972f6f6e2b434bcd34d375dc23679b4f3800863140790f1', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26085', + pubKeyOperator: '8f0b832b114fd402c7f49b6d7c590ee0744df4544d2fc6f4479af01792f761d275611a4ba0b770ffd2bd7e32d353a5c4', + votingAddress: 'yf9BsxRrHDQFAVSSzfjMhnPLBmprpeaS7d', + isValid: true, + }, + { + proRegTxHash: '4e1769c46b15671930aa28aac75993a266e9592083c7a0090d8bc4dec9809d11', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26007', + pubKeyOperator: '0e18aee6d97e6a4f316265b770c8901d7eafd89e0acf1ae85a4e19d410c9fcad60389915deef181e804cc6b5c269c228', + votingAddress: 'yWLxxC1khfy839GzwoVcgR8FrFho9hhANN', + isValid: true, + }, + { + proRegTxHash: 'f175c7de73165d9fd47d4e2c038d68ef6a4e509937bf05400e87aa00f565b511', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26079', + pubKeyOperator: '1866424b4f2d994d0ba5cc693a502446f923a174ae5553cd0c00236c5b0247f0bbddedefa7a303af064185ca44b26806', + votingAddress: 'yRCXnJ2BnRGLXQBSUXMn9u9yEq476raNyQ', + isValid: true, + }, + { + proRegTxHash: '9212f5312730c7881b882b9fb7864dc686fa5a585b7a93253ccf1ce87ee59331', + confirmedHash: '000000000c7128d2757b098362bbee1d8693c5e1f6218a6a807c137f5804d6be', + service: '100.24.239.64:19999', + pubKeyOperator: '1931bdfa94f15b64ed9d09d210db9998dfa068332fee19d8e1ba4872c0acc3efc723e2fd04a64ef2da473caa4471c69e', + votingAddress: 'yZyqhSGtwMBxWh4how7UwWBmyXii1CrCKD', + isValid: false, + }, + { + proRegTxHash: 'c32e9e14c81665699b121e886146c2fa4b3b933ff3b71a534755a3431634af31', + confirmedHash: '0000000ca88728d3a57bbf3b80f3d73af03078e6b6ffb5a259343f2e3a3f1dfc', + service: '95.183.53.17:10002', + pubKeyOperator: '09f87f98c0ad49811131a31e94d875bb6c88f64226727a508094ea8e5f25f8f6cba8d2fb27f0f7e662233c565c1cf114', + votingAddress: 'yhmDRodXKq7kHLokEdCYfnGYvuuiBL9rGc', + isValid: false, + }, + { + proRegTxHash: 'fe480695451d3b256cadfcce5796555d03ed0e023b00cb543003652391b94052', + confirmedHash: '0000000036ea545d41db49ea080e9a5cfa57f82be036440813b2043ac2465a30', + service: '185.62.150.195:19999', + pubKeyOperator: '926fe5fb0d2c6685a10d9cbbdfd6529baa54f808903efbb9336bd01e3e576d831a225cd2031cfa9448fd9c130bb68c25', + votingAddress: 'ygnM34NrSQKX4XGiim2K2i3Npckm68LQ2d', + isValid: false, + }, + { + proRegTxHash: 'cbd2cf7555b23d719bfe1346bbb29f893ff8fc90bd5ded2950c8ad89b8c48d72', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26001', + pubKeyOperator: '078d81aa6da6215eed09f2c9830668ce263bfa3e863cafebd256ba3947291c50c5eeb03729cb4ce0552922fba197b5cc', + votingAddress: 'yVXn5rcyTLYaXrka6aRYHcqNv6yZmrV4y2', + isValid: true, + }, + { + proRegTxHash: '89a6dc42063e4a792ec225db64dd9426742a5d1738e8821625d2ab920a6187b2', + confirmedHash: '000000380f38c5a7dc5165cfd6d8ceb922fb7f601c4cdb6e8c34970400e50fb9', + service: '173.61.30.231:19011', + pubKeyOperator: '07ffa44583c9908f4aaca8dd97990c56043e475723f90940ef5fd7d493152540f25f58fb8c965ee5e1be4f850a661476', + votingAddress: 'ydyWnUXrJAUEW3sr56yX8zvpV7xPWexMf7', + isValid: false, + }, + { + proRegTxHash: 'f2382c75e2009f5ce32df63933aa700a05239dde4f2df94a40ba2234b8e777f2', + confirmedHash: '000000000aa79ee5c5b74013000633242f2cf14856713ef090dc74a6e6a51f50', + service: '52.42.213.147:19999', + pubKeyOperator: '16d49c42cf506d5687c4035fc8ea37c2bc293761412b8c28a73f674df9d3983581f53a8eeb7f1c7b6382bb0485df3814', + votingAddress: 'yeARCinqiurM9oni3VQ2Grm3Z6tXYxfKAR', + isValid: false, + }, + { + proRegTxHash: 'fac81f18b3a968f5f881324d8eb38983f3f892c4999c2f46809c4de620b784d2', + confirmedHash: '000000000048f73e016ef8d7b6c4d097a692058dbdba39709fa694f67dc4043d', + service: '178.62.203.249:19999', + pubKeyOperator: '905caab51ff07a2f8d69972fd6ec09f6f9893cf6dfc49775f5a2db2ea7a8a525bbaf4e7e369d06590f6f2e8e4658d4dc', + votingAddress: 'yN5GKRn9zTKgaVTo1uJxihub6sbD6bFMG6', + isValid: true, + }, + { + proRegTxHash: 'f620edd6e3eec8afbd13959e52b38474a5268520b033d9c618d2fbf69caf84d2', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26048', + pubKeyOperator: '90c5efbc90b1257da43f9388d6e2bd386ed0e3317729ba94c27c98cee11efb15566c9fc7da288c7233478b5b5c82ffc5', + votingAddress: 'yhXpBVesXNiez6ZHUJyuz5pMr6HeG81VDd', + isValid: true, + }, + { + proRegTxHash: 'e6595f88f935fef934a6d51dc0a1fd43e65de1adfacc2c99851a69d80cd26493', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26072', + pubKeyOperator: '8f72e69ac2373a62f14b7b1d99fb24eafdc87b74247af42a591aef0989c9a3e152197736dbc266b2535c4b4b53d8ec4a', + votingAddress: 'yenp91Y6Xce31AFTELJHiG1kSxZRL4xLVZ', + isValid: true, + }, + { + proRegTxHash: 'e6986bb24b729ab531ba778bc7292a0a8abbf66b5996f45ca6a1dcbd5e46e0b3', + confirmedHash: '0000000004995804891a4c2c54dad4f684135f6b626979777839de454c8610bb', + service: '173.61.30.231:19021', + pubKeyOperator: '88afefbabbc35d16594697bdde87717229aa8946dc781baec59e9ece3855ee31ddad469386b23a7e61805785ec827f50', + votingAddress: 'ySBU7oXuuTSJqtmUArMRFsKefJPtEDkESG', + isValid: false, + }, + { + proRegTxHash: '93ae9471fd9d4b487c487de920d73c595d388e136c52f576298c7fab7a6f5933', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26076', + pubKeyOperator: '16a4c1768bcd40a254b1097ecc93f24149f96fc0e291927dfa446e3fcf90c54850b2e773ff8e03b6928b99621b689d8b', + votingAddress: 'yjTJxd9XCzxc2CW5RjJPCY59W2uhexe9qH', + isValid: true, + }, + { + proRegTxHash: '2356f75f3f35322a81e5b8a37bf6ec59388408f8faa6468075ee5f4026ee0f93', + confirmedHash: '00000000234c1d83d657cdb5220d0372f3d46b9c4438cbe9238fce3ed417f249', + service: '35.172.52.88:19999', + pubKeyOperator: '05d35fb7ea96c707d0bb6a9185f575596285d3578af73d43d2940e6ba7a39a60ad5d3a6f5df16449e4f5ba1cbd8306f6', + votingAddress: 'yh4FmWKA6KyT3aX4KtNx8EQFshQdXjKMTY', + isValid: false, + }, + { + proRegTxHash: 'b09c47d39537078986e4639420ce87b32039b17d80ff4eb88238c27fcfa1abd3', + confirmedHash: '00000000096cdf3347ebcda58d182e9cf47e9f6babc0c0ec09d9fd0cb7aadfed', + service: '35.169.113.136:19999', + pubKeyOperator: '1848a0024f4a1b85e18552105a7d397714bb9d16a392a29b5d6d18bba91fc880a6b20be09f1400dfe58de3ea87f919ba', + votingAddress: 'ygREfRit6M5PtGzU4J12CnupR47KAD9XZs', + isValid: false, + }, + { + proRegTxHash: '0ae71d42a6b2956f22a11d20e12dbe309a20fec575aa6023b983fe1b8976ac53', + confirmedHash: '000000002f86ca3787d263bbf1cc817d7b2b70549b656f7b738ad4a213e44542', + service: '13.59.231.197:10005', + pubKeyOperator: '1438959163472114ebd0f4e72e984527894a871063cefbc8cc492593a7afbf4214538c0618ff8477590f40a3b2155aee', + votingAddress: 'ycRxtkXT6qtMv3yZfWeauhKFW6Fj3tYwRo', + isValid: false, + }, + { + proRegTxHash: '5d40e68f65e7263d91e114b644ff7f8c9c376db63550d5ef9bc4228870c4f053', + confirmedHash: '00000000077bd01073605cc5956a9ee883f8c47c8c7a337ecbd14ec5aa91e294', + service: '173.61.30.231:19002', + pubKeyOperator: '98b26368c5f73198500cae0d7e1108833489e7f8bc5d7fa507014fdd0ad2b6a082012883a8acdbcf688423419bff7e24', + votingAddress: 'yecoEzHhCDtFmqFx6UTbAk8kTWZDGxmXBb', + isValid: false, + }, + { + proRegTxHash: '8b79f76bfc55f978fbc011757b32050209116f5046b73fe111f07491a403ec54', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26041', + pubKeyOperator: '0c13e65f580d9784f3e11210b6f32253d1528610e945a4f36995033957aa99a7e43ddc0859b819ee2150f6cf0178eebd', + votingAddress: 'yWwTse3VniDujMUAFx3qQAnrHz7toYg3Tp', + isValid: true, + }, + { + proRegTxHash: 'e60bbbf832ad14b0635ff4a9e0a21d1f1567fa435e92ca754399088afb20e154', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26040', + pubKeyOperator: '1235b2d950a3938a352c6649f8049e80a38f7123ae63829eb255896447e04ca57fb8f1b73fe2cbfece02b7cd98bad4f1', + votingAddress: 'yZvXiPjfdCnQN6hGFdpfoqeyc1hY6aESdt', + isValid: true, + }, + { + proRegTxHash: 'c4090fa171f9ffd59c8a808a34eba9b9b5e94ca179bb7c71a0e44f565f076194', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26033', + pubKeyOperator: '09829901b1670826d6bf9746c0ae974b05a1ac4e7efc0388c21d261858b3f31516ee394aa1c2cb3d25a8e2aab44fde3f', + votingAddress: 'yU9dgM8W39QehqCYbMgh5x4NoJ1a9hq37y', + isValid: true, + }, + { + proRegTxHash: '0b73d89eea3f90572ccdb26585f3ac32b70e3b4ab0784966e35df736ac6d0214', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26067', + pubKeyOperator: '10b58493e7596034ede84fc3b769327e7be3334d4339bcf3b64481309fb2d58341430f7ea121aa7e581f062957081bd3', + votingAddress: 'ygZ3PxTS3mwbqh3NBtegp9EuNzbkhsaYVr', + isValid: true, + }, + { + proRegTxHash: 'f7e9a302a452ba42559ea3a9193737aa18884ea7137b9dfe4ee89242cb80e2f4', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26011', + pubKeyOperator: '0f6baddad80e0985cdb3a583270424b87ed71b499c0ef0ed6c030f61ba829361e66f47f485e2af1934e21ca60362f875', + votingAddress: 'ycggDjAUdgdf7FTsrRLvypzPEc9tZCziqx', + isValid: true, + }, + { + proRegTxHash: '14f2f481ca295a5bdb3e3d7f50ff87f205230609c53989da809420b874a17f34', + confirmedHash: '0000052c9685b51f2143792ed6b2f5cda84e5b1bdb7783268729114e8d0d08f6', + service: '159.89.137.143:19999', + pubKeyOperator: '0871c91beabf5c3b98cdb1009763d03f62550e676d20b54c3fde7e50ba97e54b1cd7bf83909932697fba7627a8e583e5', + votingAddress: 'yb9p11CpZCzVi8LwDQUuunR4xRy6E5iGmj', + isValid: false, + }, + { + proRegTxHash: '393936246926976b4135b6dca4295f45dcf95c875422b70be451f2d51293c7d4', + confirmedHash: '0000037dd80e31a3432246d991942a17c5d9732b3a2037c825bd6cf870824455', + service: '95.179.164.87:19999', + pubKeyOperator: '847383710ac1786f020769809abad8f93018338eb855c103f5239d75dc2767770eb45709c895c99c0c86d375ddbe478d', + votingAddress: 'yhYKgmkhdxvZcaiLh2FhwX2gFxEihP4jub', + isValid: false, + }, + { + proRegTxHash: 'f5d2276a70a9a4c7f0e9de32e533ce15602ae1ba3d60d3f6eb67e52c7c488074', + confirmedHash: '00000024f9d5cd5a0b26d87d0eb18d41801e08d2676a2c07ce833c35fa9dc084', + service: '95.183.51.146:59999', + pubKeyOperator: '95d5badff945693fd24158932b41e311e6fb3cca1e1e551eeed72cddba2e3b04abe86547a265fb7ee958875f9c33134d', + votingAddress: 'ycanojRxMxeW3R9kVN9Tck4NGsSNusrQBo', + isValid: false, + }, + { + proRegTxHash: '385499428f83b0ef164b8b21d631301aa67a4338f6861ec9f8de8d54cf8d5074', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26068', + pubKeyOperator: '927ee1530d566bf541d2cd52d0ff60520ca6bdf27af0dfc783692e613d2281e5014f70a4927d6c0f550e04da03186205', + votingAddress: 'ybpRp5oYYrYzYDzTMxyQj5HxoCVnh9rKMZ', + isValid: true, + }, + { + proRegTxHash: 'f773def21e01af33f508b4e978631b99405fd1ad3947984d3bbca5b41b221175', + confirmedHash: '0000000e079707c52a8f35ead698f59b70967c006577c631e3ecff7b0bd26e4c', + service: '35.185.202.219:19999', + pubKeyOperator: '04f1a3407bf953809815243d539d316d2b055a57ff6c5412f31d98f0d5ea84f54511fa9f02ddd6d7f8751505c560eaec', + votingAddress: 'yYuTtXsaTD69dyxtVLVCYw4LExXn2ma753', + isValid: false, + }, + { + proRegTxHash: 'fa3b3b0d3522becb02ddd15dd075f3d6ecc6a5a50b43c6c9f6d4703a9a8509d5', + confirmedHash: '000000380f38c5a7dc5165cfd6d8ceb922fb7f601c4cdb6e8c34970400e50fb9', + service: '173.61.30.231:19010', + pubKeyOperator: '89e308c9d2d8a3cb35f9d7bb7220b1eca82c952b82111119670dacae18a509628c775287e4e796128cd6379b80dffd7d', + votingAddress: 'yZC4fLDV1enraYJeXbPGskMfgHVSAADPyg', + isValid: false, + }, + { + proRegTxHash: '5dbd3e1adcef3a9e61ea1c8d0d3bed643ab3a36872ad78905b009cd6dbde6df5', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26023', + pubKeyOperator: '119d599048331efb5bd38ea0506ac51765eaddf396114dcf14fbbbab70b7a929c9227f8ec980851ad79f502d2fd1750a', + votingAddress: 'yfLiU6Y2xZoRro6PzyGBfQgZyt7pYik7vs', + isValid: true, + }, + { + proRegTxHash: 'afc870bbb09307d8cf66c4758310afeb3b67b8f5e38df4152baeb85573f5ca55', + confirmedHash: '00000e63fcc656fa72d28be7e12c0903768787ab76b36db55a4957c71d64de65', + service: '144.217.86.47:19999', + pubKeyOperator: '0422c49dd9a3f6397d4be6fd69b7055d84137215e85685ea4eae89be0c9e93d61d1be0f615c26a43277a704fe186328e', + votingAddress: 'yiNS7qgZudHyC2bGSSnP5qrs5A39a4rLXM', + isValid: false, + }, + { + proRegTxHash: '76cdee4c76afa2b79041284fcdb4f7b1f55279c00cd538bf49515728ee052ed5', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26013', + pubKeyOperator: '8686945cd3325cf5ce0903782d5135128baf2417dfa1c2d0926994df657c0e4772d567b5bcdfe9c413a773afebdc17e6', + votingAddress: 'yfRgjjeuqmd41WffqfYT2sizjxSM1B8Y8R', + isValid: true, + }, + { + proRegTxHash: 'f443dd87ec7981e8630ae957f295d9d226d4bd3895f59dbd80b30137a92b3735', + confirmedHash: '0000001d265e101abb16f78133ea20f57cb2651108b24b506eaf41ff282865f1', + service: '95.183.53.17:10008', + pubKeyOperator: '9809c680a8b7852279f00438526b2d940e65a0e746725adf2bf00ffc054ad2601b9011cf1edbd391426afd1b204d696f', + votingAddress: 'yWtU7dWwTo6G5DTZxY1rorAbNjoycYARnT', + isValid: false, + }, + { + proRegTxHash: 'f94177aabacd11c12c92b1d5ec28b8ee9f1c07b220ab783cbf8a1a21cf6a5f55', + confirmedHash: '0000003eaccfc65510d12a454b8927f844bb7c96001334b0b906f173cc7586ac', + service: '157.230.247.219:19999', + pubKeyOperator: '9993c900fc49b020d4050981a45281cc71274196c57c9405f7ea8d82823b2cb36c04a2aa363111d74e383bdf9fdfe254', + votingAddress: 'yRGqkX9VanksXQCtGNAyx7e4RrBGiij8Lh', + isValid: false, + }, + { + proRegTxHash: '486b68e49ba8508a5b9a725a885a51f01c23320abe9f45c9c8d5c3521b2e2bf5', + confirmedHash: '0000000000a490eeb60e0fb86d5e61fb45c30f090f34d4fe463e080737ed6aeb', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yPLT4e6L5wVMhG1egoJ3W9MyYYccSdvqdA', + isValid: false, + }, + { + proRegTxHash: '541b6f00e629e52310a87f88a2cfc31529ea3eac3b4c357eea5894ee3b8a9775', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26095', + pubKeyOperator: '0bbb5f1771615f2dad2b3bace289c25a6491d321b8e5feb58aeb0c5e0c55bc4e15fae818ab71aa9e88875b2edc4d4ff5', + votingAddress: 'yU3ZwoVKgGmUC81pkdscZJLLfNDTbZhBNT', + isValid: true, + }, + { + proRegTxHash: '0e920042fd29aed13e2b60bdc2a254c00f2ba4a7be7f2d1a9cfc52fb4569a775', + confirmedHash: '0000029d80c81aee5539c61b84fea7be5018182fe8a9dab3b368cf6dae9004ec', + service: '185.50.195.156:20005', + pubKeyOperator: '8325cd0990a4f3fe11ac2d3ca245773a01424ff2a2f23456ba3fdc57964f605ccc08e32978e4d5679f6ab730de75ad43', + votingAddress: 'yLdyqrzbq2BVdbDNuv2mKHUN6w1MUCGH8V', + isValid: true, + }, + { + proRegTxHash: '63115def57591ee9abad23b796cff8cf63b7c1e9878ab77ce8e354c388035016', + confirmedHash: '00000000024c1b2e4183beaa99c68b36667f3995eb76100e4822a26d6268bf93', + service: '45.77.176.16:20000', + pubKeyOperator: '02ede7bba4f6330aa85b22f2d20167cb529ac1334125ec439f873c0cd7d54e7c07b65bca725799f8292564af4296f060', + votingAddress: 'yXRUcK8Xqhygg8wWjhvB29q1t43t32Ksni', + isValid: false, + }, + { + proRegTxHash: 'ced895c3027330288a0081b29f6a180230c25380fb57b4ec019159e12c369836', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26064', + pubKeyOperator: '92a27c1c66e4614049109b43a26dcf486748a7e1f558d269663c39524314147319e07b504b244d013c1233c2b62378e3', + votingAddress: 'ydGa6gSBRdNosRWTGBkWcx8ZMYzL7ujrED', + isValid: true, + }, + { + proRegTxHash: '71d1eee72379edada11d464bcee475b37371e4d907db5848c3f50e0bed00a456', + confirmedHash: '0000000000f7e6d3aca6fbf8bc5a8efc04606765c4ba95e86e1be955971051c3', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yUVi8VFcoYZXqnDuRHQtgrn3mtei4owTVJ', + isValid: false, + }, + { + proRegTxHash: '27a6ff2f188c6190d44b657f54bd831f57228f918cbb7fd6026f5cf5c443d496', + confirmedHash: '00000000077bd01073605cc5956a9ee883f8c47c8c7a337ecbd14ec5aa91e294', + service: '173.61.30.231:19003', + pubKeyOperator: '110bdff9037c3e3926082ff9e9e9de9cd0a0dd416ac6d60a61781f1b3832a4bd068e92343be400fc31db6eb4404d0701', + votingAddress: 'yjW7bQrKBsMV8Wh19LgT8Z1uLkWY8P2EBd', + isValid: false, + }, + { + proRegTxHash: 'b5706630cf1c631b5f95edf39d0fa1fcdaba9d34459b0b392e3f28eb59ae90f6', + confirmedHash: '000000000143ff1bbead342c77289171f387d8486c9ac9d53bd42d974ed301ad', + service: '157.230.19.127:19999', + pubKeyOperator: '874b17058e37c39f770188dfe8e699959654d723e62e28b2760900e5284f63f6b70e077a6ea9803714bdf62d083b1d9e', + votingAddress: 'yfQM6j2bPK9oPJQjkCT9yYPn3cq7SzmXM8', + isValid: false, + }, + { + proRegTxHash: '46dbd118b9d9b138a5be20446ae3448e8c41acc8c28411848fc85f563090b196', + confirmedHash: '0000000007c0702a4e7c785f96b2ec6d9b5480628a7ac056c961d45bfee5502f', + service: '165.22.233.59:19999', + pubKeyOperator: '97adf3867ae5155b18345e44e277ab26b9a497c7d0cf9b53bdc42362dff3642c922d1d10e277ce6bd407f48bfabac68b', + votingAddress: 'yh5bCo62e45TxCx2AQSaCewMzTvT9H5txe', + isValid: false, + }, + { + proRegTxHash: '0cb486a3f478e2baccb3bc755f87b241e9ffe05dd693ba92e4777dd2175b5a16', + confirmedHash: '000002331a26a373fbb35fff5616fed193a8c1d551428cab4cbf460708db1204', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yWiEmf2vwG1yD6xUf4T2XZ5P1wBwYLWNxc', + isValid: false, + }, + { + proRegTxHash: '16dbc9ed051c6cb601af553affc6d3bd9de229e4f56ee936ea5ef3a0cd5612b6', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26051', + pubKeyOperator: '1515e397da32f0d63f0730bf751fb1ce4a2cfb57d3a26bfd547033c0e502ed86c29985bd42b110bd9cf0010b664a6654', + votingAddress: 'ySycDAguLUCTGPjBy9UaJ3KWP4SYnCf5YP', + isValid: true, + }, + { + proRegTxHash: '87d21608895b8148fdb2c846d5401158720c3721dc07c4fa0981f2bb25ae52d6', + confirmedHash: '00000000001e4a5b3033b52f45f573e52765f0e87bb1aa33df1bd4c40ea5f269', + service: '68.183.165.54:10004', + pubKeyOperator: '974b7b4e608007f22ece8fb933fc18d66cf35cc0e5a7977279a092976b501786d4ab9108c7fda681e23978bf54b7709a', + votingAddress: 'ySJCghqoc9muzubw6XDa7pzgC2xXQr8upz', + isValid: false, + }, + { + proRegTxHash: '37f38ca1a8881828a70ee052493565882a551a48e413afb57861356b925c0356', + confirmedHash: '0000029474ae51345262eab93a25478c7a5e3221e0b2d4e6bbe3c1b2e0223727', + service: '149.28.127.8:21999', + pubKeyOperator: '838e54677f3393ed4378377ad723cc8ac15b54b1441ef5edf5f97d4fff3ebe15bbacece2aba8103de48e2b5e16ac850c', + votingAddress: 'yfBLbt3xc32CjmArrhEVxtWadc7BTQ7mYh', + isValid: true, + }, + { + proRegTxHash: '36c99a74a4cfec77b2a7438558a8ec53ee09c11833597c1b601c5b00c93e37d6', + confirmedHash: '0000000013b5219a9afed259a4e50a9de5a3a8218ef42ef8f4dd03c3ba134b52', + service: '35.175.62.106:19999', + pubKeyOperator: '8f2caf4cf1e01130aa6bcf27784bb36a2b4daea4ada3be553c9b709afcc752d0f34a0c35e3301e8f6a2fb3ca44656be2', + votingAddress: 'yWzHk3cgMS6f7Hjh7wwRRoTk7wKuL8jPsU', + isValid: false, + }, + { + proRegTxHash: 'c7a1e0341bb079e2402eb8955b720c93cf90ce65cdf20856220cd52bb7478116', + confirmedHash: '0000001ffaa9dcfa755922d7715dc62dab33950dca2d62781b9d8f27bfa141dd', + service: '95.183.53.17:10011', + pubKeyOperator: '802913fa3cc02a35fb8e1b26b644f8a2395078818f9bb3be8ad08fc8cb175f16c43e2b0aa2fc12a7f8dda3914946f702', + votingAddress: 'yf7n6338bCMiQXm1gfCSkQM9qLRajEvB9K', + isValid: false, + }, + { + proRegTxHash: 'ebcac87f687f834ac9f17c408bec456a36345ca276cd7f9d699d7518efd06d16', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26059', + pubKeyOperator: '8f97da757051c9d15ae50df6cb0471ca3756e2ae527d823d1033670f2f1b3423026b8e130a4416ee1c11cdc0adbb9b75', + votingAddress: 'yMoq4Lp6x8v6YA5amQhSr1S27wpoh5GPBf', + isValid: true, + }, + { + proRegTxHash: 'fcc330b0afdf27d07997b93277a3942e28f7cf4fd043b7ee64b6b5c16173c936', + confirmedHash: '0000006dffe973ce0549de2861b0f3aeb1566c12fedc1f520b28487d41e8699b', + service: '3.129.25.142:19999', + pubKeyOperator: '89aaf743d70a26ecd18aae71d8e2ff0bfc98a51511f83c8acda856e3f5d7c61c21ff4e19a56f02ecfb6ee014097945e3', + votingAddress: 'yWiEmf2vwG1yD6xUf4T2XZ5P1wBwYLWNxc', + isValid: false, + }, + { + proRegTxHash: '8d4d1bfc7e6667a370e072079dc70b3e3268f71a32a54371487339429aa47536', + confirmedHash: '000000001712da1292a63fafb2c0996842081680b8931ceee542418ce90a4f6d', + service: '45.63.104.104:19999', + pubKeyOperator: '05e588704a6f6d703617081d8328c006b1173d60aa26cfe44b954f1279a1ba9a042bddc5b3a00cbc8180676d12060d62', + votingAddress: 'yUG1j9KMztBz5JkL9P1R5nxwqPfvZXPLwz', + isValid: false, + }, + { + proRegTxHash: 'aea2c0ad3c65b374731f81c1c3b9d08ada064798f788cd8346315eab076f6057', + confirmedHash: '000008a5ce3ff6310d1f71ade22d6cd322613ab62c6a9dff16e616b15a03f9e5', + service: '116.203.87.12:19999', + pubKeyOperator: '95234e6e7d476318b4811f1daaa7e887fad24b1499b3472a3a7decdd88e8bdd14551b7b67b22ab896adb298600aee96f', + votingAddress: 'yb37kTs4ct6JPCYVfWwviJGLoZ3dUepLR7', + isValid: false, + }, + { + proRegTxHash: '77788d49e5e03f7611805181678d1041bbfaa37c887d798a04d37d49ff9a45b7', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26055', + pubKeyOperator: '87e5f16add346cca4d02770f67168e486e980e89c30e44d5eeb7ed709edebee947c02769862b4065a99aeccff77689c2', + votingAddress: 'yLej5wqGYCFPmJRnBJbETkkCEmkxb1NtrB', + isValid: true, + }, + { + proRegTxHash: 'accec7a4773ff470c1f3d592f35b0a2a0ff9ed77b0559351ab0481a67a77f657', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26035', + pubKeyOperator: '088407dfe6ec87795ea9386ac3f52ff183ca41e8c05b95af0b0f763d413445870022850dd7b59735b59fbbdda099d6d9', + votingAddress: 'yMj6vmtc87ph5Q7Ljwiozs2rnuGZHerGSo', + isValid: true, + }, + { + proRegTxHash: '371e682d3b93d8459db0e58d1e260bed60fea4e5edff230e83a61cc37253b677', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26065', + pubKeyOperator: '99ea7b2960131f06fe48e47d02ce4dfa85c088e457c754ffdb4fec897879dfa8c0fb9210b45bb31e3310186026b2dd0d', + votingAddress: 'yQDmDDSD6awsUvMLiopgQPYJBV9dEAcgfw', + isValid: true, + }, + { + proRegTxHash: '0c807e5e4c96d56008c2e3de266027c7232d070710868e6751c2ff907a4dba97', + confirmedHash: '0000000004e6879ed6e696b157b7a28bd61e068c91b8c2d5c269d096d9b37600', + service: '107.150.121.217:19999', + pubKeyOperator: '088f0bea4590c29e0a8657faea9d5f2e0f79cbe8f1cae3cf9111e84ecace1443ed8dfe136c539019684d9511d1bba807', + votingAddress: 'yMbGA7fswtM2gchMvRFHSPG9agNeZkAqmV', + isValid: false, + }, + { + proRegTxHash: '4f4cdd55a71a68183cd6ceb8da6e95c9526e259ce0d243e297ecbeaae0f93ef7', + confirmedHash: '000000000ece0b3e58a4fc2208bc4e48bcb40a8b06a71016b2066b0241c019af', + service: '178.62.93.226:19999', + pubKeyOperator: '93a5eb4a6fb84c13bba4d597a6f9d37f565048a384c94d3b81630c6965a023eb3748b6fed0ebc224f051eb23f50d9ff3', + votingAddress: 'ygp36RFYTnVHY2xRTeFqcNkHPKXMvRDQEB', + isValid: false, + }, + { + proRegTxHash: 'c865b48a09801c61dce5804f28fe994c72577254ea1859cf1c37fe92b428e757', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26009', + pubKeyOperator: '0a3f1d06fbd8e7664a628c679296f29d20b1907719a487148d856374b3ec4903e47a1a65295188981e46d0978bd24378', + votingAddress: 'yhJ1ewzV64KUYWbkVFjidDa5do5QqVEa8N', + isValid: true, + }, + { + proRegTxHash: '7504ff244e65de04c91640380c0c996f1f5b09073a8eb387ceba1a3c1ba18ff7', + confirmedHash: '00000000061771e19a1adc5b3f48507cc92b65257c0f6fb6e918c0336b261456', + service: '173.61.30.231:19004', + pubKeyOperator: '1249d9527e8ccf8d237e828500cf7f8946963d45264460586ffd8fb1b76e16a541c54695089fbcf4b1b8e1ec79e93a70', + votingAddress: 'yYaSqRGYcWdkv2UCBAE4W4wV4rTFTmwz7p', + isValid: false, + }, + { + proRegTxHash: 'd1ce9c61c04501fdade45632d83ba14b76b8cd89de369e7ba9594731e21a8c97', + confirmedHash: '00000017ce7c1d85ad9502dffd83ef55400a816a5f5759868eb83c5d33bf9dcd', + service: '68.183.196.93:19999', + pubKeyOperator: '0c49037992160cb8d7f6ad7e13d778fbbfb5d10230b456bb3aca1c044e79fb15c3b1fcef7efac59899eccbd190bdc40e', + votingAddress: 'yPTkskWwjg7UkXdUregmfbNnPkTCfNRNaa', + isValid: false, + }, + { + proRegTxHash: '76476a2678d5c1e9ea4951cdd00babd50f6c53f91427ba8dc8fe49f5dc1f5c97', + confirmedHash: '000000001087611a48b9237c0db4a849c5afcdc3aa7009a1cbc6058a1b4520bc', + service: '52.220.61.88:19999', + pubKeyOperator: '10142d44041c90621d111283fe46fd8b2450d4b9bebad194290fce09ba080679c748b1ba70e3959623f127af0d2bc9c4', + votingAddress: 'yWLN8dwGS8SxndBEW7Hwvn2yAD7hULTojP', + isValid: false, + }, + { + proRegTxHash: 'a1e5ad1ad55d663a413b69bf9c879fc2b0c78219ea8d44f3f4f4e3943dd1e897', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26070', + pubKeyOperator: '99f59fdb3a093e6254637c97056489f438e696f3a229e0fa041cd624cf0e21ff7e89bea17ab07b8d48618a9df7c05266', + votingAddress: 'yQpZRkoz1K8VeAQGxVJ5vHN3vSQWG7gxkC', + isValid: true, + }, + { + proRegTxHash: '4d13a8912d3119f1a9eea95d70a546bc449307af3521dd532c0ecb1ee5a494d7', + confirmedHash: '0000000006f3a417b9a06ac46f5f1e354d14b3e3059e8db385c646d50edf317b', + service: '54.200.200.228:19999', + pubKeyOperator: '09ca23af93ce00a95bfefe790ffca791e093a8c0e79675b103b2a4d06f930433b3f6b15c83f4e2c4b5118fe0c27ca13a', + votingAddress: 'yhVunEPt1uPX6Xg7CmDH3nuUe9fXfK2QUK', + isValid: false, + }, + { + proRegTxHash: 'c24aea30305d539887223fd923df775644b1d86db0aac8c654026e823b549cd7', + confirmedHash: '00000012b002b15f3b0e003502f37b181f158efa3392de3139cfffa4f79fafbb', + service: '95.183.53.17:10001', + pubKeyOperator: '845e9bf2879d98ece4aa8b78ca074e32f968bd93bac973a1abafd61f900b70e7178b6352d830d0fecc2653d0f04a9151', + votingAddress: 'yYrZkUYznVBbTsRPZ6yvaisT7Vv6EthwKM', + isValid: false, + }, + { + proRegTxHash: '904132db5c8718123233252283268bd908f1585a7a8db92f997c03694914f0d7', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26081', + pubKeyOperator: '962c65927aa1616e3783ae7cdf8c3d19b4c26b477686a9f146cd9ae40eb7c0e01a1580d5df8c32d1f4c43a52f62ff5e0', + votingAddress: 'yWSEqnFncdvieSWzt35uqG5DzdyajRphXd', + isValid: true, + }, + { + proRegTxHash: '71e6b3fc43cbb7a04eac2799d8f98f76f3b0ac867a8b8c82caf876cd0737ac98', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26097', + pubKeyOperator: '975c482384c7bd4cfd5930fabac11646121d420e31883673dfb6e6e3bfa273da73a2a91b4b69cc108eff9619fdbb4cf4', + votingAddress: 'yM6TrvNTXVFPqyR4DWi1kHkoviVbVukWZt', + isValid: true, + }, + { + proRegTxHash: 'd01146e83d8037c01eef6e790d0ae86459f9457663463019e02ab1cd26eff938', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26091', + pubKeyOperator: '11a2bc871712d5b4eba48e75525b5b4067288773c26e2cddec1bd90b0ede995a5da66c0c4d8310e8b7c16b61c7e7f260', + votingAddress: 'yNKjayHNuEJtGufWrduPS191d4eT14rARP', + isValid: true, + }, + { + proRegTxHash: 'f487b2b02c554816bb44cfc35fed083951ff94a3ecb5ccacb578986615cbfdd8', + confirmedHash: '00000000041f86bfb8c2e5c4f166686f73f4930e4b6f1b9a8feb8480890ba724', + service: '173.61.30.231:19007', + pubKeyOperator: '07f818e5c2330ac4e7f0ef820f337addf8ab28b07c9d451304d807feda1d764c7074bccbbd941284b0d0276a96cf5e7f', + votingAddress: 'ySXL8BpEMVjFR6sNEbR1LGPuHfCbaWYmBJ', + isValid: false, + }, + { + proRegTxHash: '84d181ca2e1afd3fb416c71f62c2f5370a1e7f54c3400faadde30563e62f7318', + confirmedHash: '0000000008e28f4f86520b232c34de8266994898d54f8c09190106839a2e9735', + service: '142.93.40.79:19999', + pubKeyOperator: '05b69b964d581a7659f5fcf2cf4a50a75e9cacccebc4e18d27364225eb3f9886de5472cfffbf9cf029f81b49037e27a2', + votingAddress: 'ybMGwdeScTbxL28qxKAsxknzB27nrrFfVm', + isValid: false, + }, + { + proRegTxHash: 'b149c50e97a1411b76b2e26ca20b9a6a317d0afe19df2b78b967f0b94aef1f38', + confirmedHash: '00000000102b0cc0c494db1eadc2110a2d268217edc9f1fd079c5781638db7d8', + service: '52.35.83.81:19999', + pubKeyOperator: '8f60f80538f335ba0f9f5452f02d7f5527652671da80c3d1c10e31e040f9b901a53b476a9ce02b507958bc8a65acd7b0', + votingAddress: 'yQE4NXok7VTyPaBjNhKHefxsGLwYJaTgTn', + isValid: false, + }, + { + proRegTxHash: '6fc2e949a1bb5bff22ac494c3434d2db20a61bc91c9f8a3e57048292abd33f78', + confirmedHash: '000001adc393a4ef65ba6cd7d5306389a5813fe31ea1a3d41f587363adf6b5b2', + service: '194.16.2.5:1998', + pubKeyOperator: '032bfab78f78c968f4a1e7fb87d9b3bd75dd2a49e18b7592e4274322660c27f213b898442eb41f5db42291a2172508b6', + votingAddress: 'yecMRvQ7g8yfUzvnmrCF14hTn3xG5Df7iv', + isValid: false, + }, + { + proRegTxHash: '54ba308edee77a4dec9f9cf4c589144cb49b44e7b475816fa8e3617aa9573f98', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26082', + pubKeyOperator: '143f4a617b40aba6192800c4022f132a7cc805142ddc731d3832b8f44001e5c88e56e536f825f97ea5998941ceb6f87d', + votingAddress: 'yV3chHipkovWZPvsQMgt6WRah3KAZf3T9E', + isValid: true, + }, + { + proRegTxHash: 'c81c26dda720ccca323cca9a675257c9ea50c4aaf40cb1a4c2e931435f160fb8', + confirmedHash: '000001d767bfe5ca9b3d6d2780168945514d070e3de1f8f52133e360edc248fb', + service: '54.190.173.23:19999', + pubKeyOperator: '10d9901c0aa8f9b3e789a1413e731ff07b9b58d4f53925f53a1502f00e6ccf056dc86ffa5595a1ca5c02cbdfb38b1cb8', + votingAddress: 'yhJ1GR5xSeL4NDdq1genQYczFeUuCh5apG', + isValid: false, + }, + { + proRegTxHash: '2c2e638a830ed2844997569b6173632d5bc28f3c9d96402e73374248d5420ff8', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26015', + pubKeyOperator: '18c08d1799441888955f7abd005e1e5cc91ee59e4359cb01232900537fc90bdb2dbc8123806d5cc5260bde93543ead69', + votingAddress: 'yQNPLGqfp9VQf7QSqx2g7CR7g68ZsWJHA8', + isValid: true, + }, + { + proRegTxHash: '261d21be8fd7ac4883ba89d9f65ca5e2827cec249b81443f6ec02ef41c63f119', + confirmedHash: '00000089a6dd8a2f116d8561444ead4efab8b829e4ad6e537f9ee1f94aa1e1e9', + service: '35.161.101.35:19999', + pubKeyOperator: '8b10b741296213c9220adbc6f061c1e3f4ee3c3e692f003ac5a3dc56c4433066d3c9b8e8f1883f7fc928bd059cd78c49', + votingAddress: 'yUC1HZyDczQ7MwqDjnEuvJh6cfJJB65qtf', + isValid: false, + }, + { + proRegTxHash: '500bde7398a2ef46b93994f34d3607875321076ee3ef975d5477c3d06b0c1659', + confirmedHash: '0000016756fd794df2fccbd4a74b8467327ec3cc488283cc1e8b03d763cfcb3e', + service: '167.99.183.55:19999', + pubKeyOperator: '822967c827427a4ea722459a6a5d007c5a14e1da4b6fd52417914cdac7dfbc9233dd046cda0c2980d1936cfa8b229200', + votingAddress: 'yR97bk92FrDQc87ohE5GkeF4uxnScVbvGN', + isValid: false, + }, + { + proRegTxHash: '5aa7b0778c53e048abacecf9e63558fea80ea270ffb13ed12cb71f9b5ea08739', + confirmedHash: '0000001fd4305c32af36cbdf651cf1585e1a2a5b93a871186c63cfbe67c8eb8e', + service: '95.183.53.17:10003', + pubKeyOperator: '940c2271fbfbe83cd9dadaf03da32e840466cd4eb0e358749d5f22da2ca22610c6cdcb664b1c082b84cd4516d73ce5d5', + votingAddress: 'yW9zeJrPv5yJf71qvNfyPrJg8Me9UdaDP3', + isValid: false, + }, + { + proRegTxHash: 'f2b2cda32fe9ab9a29ad463e878bc4061e3fd74bd30508c53a214333f8b58839', + confirmedHash: '0000001056f8616640db4d31c63a4a56efea5fd2e3795599e52a644fbbd6ea81', + service: '34.209.211.134:19999', + pubKeyOperator: '195b44e1d553d160abfcf70b8ccfaf24480ad34fa7917fb87675f712f0795a23dd0107f5f3e39c07474697e95b15170d', + votingAddress: 'ydKVa83W3hffWLKnRVggx1aBGxqefS317r', + isValid: false, + }, + { + proRegTxHash: '6721137dd75158b749d24db954a79809129fecb605cabe9d3303073c03e81439', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26077', + pubKeyOperator: '0a5f192c6a67f027d6a90a9eaf142bd4c44f08f9cbe9416622ad76663a5b76c60efabc7db4317cb8968e2e655c6f376f', + votingAddress: 'yLpVwkPxiChGPa39ZQ6AxyKSeCTFyc6Evi', + isValid: true, + }, + { + proRegTxHash: '2d3394ece5ebfb9e2f369fae0c663b01b978f946dfa06fe938f2c292661c9499', + confirmedHash: '0000016da30f478bb48e01dca4a04f763e4561dd256124344173f08435187815', + service: '80.240.23.199:19999', + pubKeyOperator: '1260b9b40d6a39c14c5f52763ef6e98094a6d41ed35660ba24334c50b60cbf18a524aec8bd4d0203c3257e70e193fd30', + votingAddress: 'yZ7fp3FCN7mkoFCYvSN1egT5NLbNWaHssc', + isValid: false, + }, + { + proRegTxHash: '41fb85bb67981f4e1fe41e0f7d520bf6df2167c9ced5e51fae33343f98d9cc99', + confirmedHash: '00000000008ef5ceb0281bf322f3483762496f709bed1c3db2abec492037c743', + service: '1.159.143.235:19999', + pubKeyOperator: '8e53b8ab39fcc259aa22b93d1ab4e333353e6d56b9bd4d194985a59e0de5060c1225588a256569848ea421725223711b', + votingAddress: 'yM3P8YfvczXWotVeXW8xQawodtzLwjTEvH', + isValid: false, + }, + { + proRegTxHash: '869b6700423da629920dc2101ec88e894f450f66aa751879dce0468945e04179', + confirmedHash: '000000000254b4aa8b2f12c4068c0dc2e8cd325a039b01f7dadb7b878bfb07fc', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yLRHza4VBgV8LEBbyWqq7EEtZg9iPWr5YL', + isValid: false, + }, + { + proRegTxHash: '113f86cd2940e9638cf59e9e06e9a73aa132b73c78b8b39c28ae2544ab765979', + confirmedHash: '000000003b72f73e793d2020185d48affa2e6c01c75ce696d80849b425136091', + service: '18.231.111.219:19999', + pubKeyOperator: '9273016bb92b9101798bdbaf656bb14f47120241ff9c76d2650da9e399edb4f7bde8238b260a3bd935609e15e2a7c479', + votingAddress: 'yeoi2KgiCdkbqJXXm7yyRrg6pL1omfpB8Y', + isValid: false, + }, + { + proRegTxHash: '67491f0cb0874d179d8ece6f3ff25f721b2eb016ab5768bfabdc5e6ca614aaf9', + confirmedHash: '000000001f25c0f6c1535ab47211b21185409c6af85df7a82e798e1ca00ed742', + service: '91.190.125.133:6667', + pubKeyOperator: '91e633b72726091f58e3bd1ede3a21de66abb2456c2f669be8bdcf76f3ab76aa2d75f7d03cf2f7d5761ab15e62e00613', + votingAddress: 'ygbXcRv8sqYJ3DcEkyRwTmZuFaKwmHTTEo', + isValid: false, + }, + { + proRegTxHash: '8d2f4505922cb82f7ec601deeba318ca7ed2f47b89274792dc9001ab62112ef9', + confirmedHash: '0000001662beffb7d48834d209f84394edee5601408439ca8e646b0d88cfd2ec', + service: '95.183.53.17:10010', + pubKeyOperator: '8c01a1351c0f42892d6b68c106ba584f91dcc2869f384830c968688d09becfd0f7468e7ac7f02983724a6e95a887a148', + votingAddress: 'yRsuGQ1q9Cw6PbYuHx9GdLV89e5qMXDYnM', + isValid: false, + }, + { + proRegTxHash: '2da32791d877b4dd542825055418cf7e70f08e6e32a6921f4164066a8d8bc359', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '173.61.30.231:19019', + pubKeyOperator: '932f6fc90c9dcaacdf9d836a2a7e60d090fe5e55b0b02f5a4f608a4b8235ba5aa7abc4e05f9387d1d942adc57c87f5b7', + votingAddress: 'yejV1vWQXEwYwH9gT9hXqAAg81opUvqe88', + isValid: false, + }, + { + proRegTxHash: '5a5b3a5da96d5ee8ec30e9cfe76cf0c407ef040836d2dcedd94c4315c9f07b59', + confirmedHash: '000000dc808e75afb244393520c93c16870dd85b9aaa79f282d2713959a8cd7a', + service: '18.222.111.70:19999', + pubKeyOperator: '80f8dfef131ac329d428504e7cb89974f188f07caef0668df1daa4ca8fc5f50f6c5945f020271292dc220ce313c00f16', + votingAddress: 'yiGZiy9oCfq4zNpAzNt9C8nB5XxefeoQCb', + isValid: false, + }, + { + proRegTxHash: '66392f74233df2c8ce7c022d8ff992b925f0a32852a28c8b95da560cb860b41a', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26084', + pubKeyOperator: '0bcc3cb60387ff0a646cfac70b5c727ac553e360e519f7598afc72e73bf4a63d48ec8a1651a715c3bf454bea0963b656', + votingAddress: 'yUKdbDrdejZkD9QroRFGpM7GSXyd3so3qx', + isValid: true, + }, + { + proRegTxHash: '2ab9d8594ed6b96aad7e0d89e739e15f43540da076265f22f1f2be892b9af9ba', + confirmedHash: '0000000043a6ec7c09f02be40ec2072be99e94fe63b2b73d59fd52b140326c60', + service: '58.218.60.42:20001', + pubKeyOperator: '04eda2a8c31489e17463ea27c0c39473afe2c9153641028de360eee8ce213d36a14ef8f8b4f85fb2cd70815a9c1f56db', + votingAddress: 'yXKJuKaExdoc5r771Dwqp7Xfo6C6ojbUPQ', + isValid: false, + }, + { + proRegTxHash: 'be32ec53dbbfb64e5ba29e25e3716f6f4024291914ce4c858cd69f0b4e371dda', + confirmedHash: '0000000015717296254a7c6139a50c34ad481dc8fdf7b0ea4c8320dc3fff2759', + service: '173.61.30.231:19025', + pubKeyOperator: '86ce02e551a46f1ca9a734104b4e387984d733ba99930eb677aae126fa142f201049842422ab2f105e3c9805f1bd54e8', + votingAddress: 'ySBU7oXuuTSJqtmUArMRFsKefJPtEDkESG', + isValid: false, + }, + { + proRegTxHash: 'f8b10b1d14931a44c0e7b65d531db89b62d7789a09f0a88c7c07a48552fe9eba', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26020', + pubKeyOperator: '05ed065bdd0a7745aa1165e7a00f05e11108235d6cbb4cb7f1c751df8aa117707b686800c7c63f526c431659f2f2b5f2', + votingAddress: 'yXvKDpQvD9WuMJRN9zirfcGEZTreLWynrT', + isValid: true, + }, + { + proRegTxHash: 'db3158d303d9634fc0a4772452707e4f6154aabedcce40d60e7932137ca52efa', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26032', + pubKeyOperator: '989f584df6e5a359bc469a4975baccda2bc6a3fc3e89721c639f5567db7abef79f31ddeb4832b95418d49322419d3eb0', + votingAddress: 'yiUhmN59P6ht4GdTFW1EjRaZiRRo1Tg4go', + isValid: true, + }, + { + proRegTxHash: '9c95baca0dd01e44752732b425cc57e0d40b8f926bb41c6e04d260900ce9433a', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26060', + pubKeyOperator: '11de7db9ca698de02848b9bbdae959be3ad4c1e5b3e12be8d2350a68d5d2213f9e0c88410c4ede8f9e0c0678f2e94c3d', + votingAddress: 'yMCL9xz69ZfECrQsEHT5zCiTdHgEz7mKLg', + isValid: true, + }, + { + proRegTxHash: '4a0e85206a1359af84a75f4b210eadf62455bb18a5be99260f0ad97c5b307f7a', + confirmedHash: '00000235f472a20859e86ef9f254e3980fc682b573b765a8d3e3c04c2202cc1f', + service: '195.201.37.255:19999', + pubKeyOperator: '870cbb1e25299c79046e99f6a47ab87ffcc97f92eddeb83e93e11b5c483268e781062243daa14a20ef1f2c78a4e5ffa8', + votingAddress: 'yZvpoUTwyoF2pFyWDUjszNLtx2dKaMamQB', + isValid: false, + }, + { + proRegTxHash: 'ab0d65e0f36ab63ed4c93a4bb25ceb2847f8b3796fd5a6b8b8fcf532c75f7bfa', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26004', + pubKeyOperator: '94f54a21c9348d0cd1ad3d14953b2b633b7a1b9035985da4d7c583babbaf214c1c602c96811fb2b66b821e3da63a7512', + votingAddress: 'yhMEPxuRfdXoCJeECvhoMd7RAnSwWpe4cj', + isValid: true, + }, + { + proRegTxHash: '6d6eb7a108fef471947d245e9189e47284d9a720f95aab0127adb9bc6459557a', + confirmedHash: '000001d335f35a8a288696fa998b1ca70242f4c90be10675cf58dc93b76df649', + service: '95.217.26.135:18888', + pubKeyOperator: '955368e9fb5cce100a0ce6df64bcf624355222e19032cff0c80cbc75140173c2eb47863b189d2423b64af6544226bb50', + votingAddress: 'yRU6YMBo8jyiQReCcBSwuuTzkyamchW1wc', + isValid: false, + }, + { + proRegTxHash: 'f02741ae9716d858719beb673e81353a94964043d170d83501f85fe3b3fbf17a', + confirmedHash: '000000000677339533b8886a180ee5c5bce9b0b69fcb9914817c54da204edf2e', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'ybqnRicfu7NtYvTh8aMEhmg9CTHkGrti2Z', + isValid: false, + }, + { + proRegTxHash: '400c7f8990e6f8a3993b7d5900ea0b58e18bf86ba9b147bdefcd0df4cda1887b', + confirmedHash: '000000000613ea19d2c5a0d6bbf861eebcba6c56b2e32c25306c30589906e8f3', + service: '89.17.41.106:19999', + pubKeyOperator: '848bfbe1bf50debe1322e14c9115adb3b96e5b8a3ae96beb7e2161281d9e56c30e43478d6f39835e3533a1c54377258b', + votingAddress: 'yWjnrJQzvgfVPPQJkRu4NUPue2CiKe8kSD', + isValid: false, + }, + { + proRegTxHash: '6fbe7935a362d6c08e5d10af09398ac4ebd2edcd1f5d657816c4f0982da6999b', + confirmedHash: '0000063950f0cde94633aa231b42e384de998f9b33c11b5ec532f366157cd4b7', + service: '165.227.20.111:19999', + pubKeyOperator: '0dc936ac5a2e0e0e81a682afbf1d5a4b6c761d265c944b7065cde7c0009b103b6e163441eab78460b0aa6951477123a0', + votingAddress: 'yMiN8ESAhQTm6uBd14vgxhM5c6Nu7AaBJ7', + isValid: false, + }, + { + proRegTxHash: 'e626083b8b4fc5007fdd06001a3e73cef8428da97177363be739cec0a9d17ddb', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26062', + pubKeyOperator: '050b7589e6e7a2320fed065cc89483d5f913a07f5381ef56b7f47103a5cdd1249820fbbdef8f4fce898e894d6807b3dc', + votingAddress: 'ydRaCRcpeJCTR8rDvgoPypEuWq5oB1hmUM', + isValid: true, + }, + { + proRegTxHash: '585674740dac63692bbb0ea4ee899c575f9883e91fb5ba5ebf26b5b2fb66f21b', + confirmedHash: '0000000012bc881cfca8226618342e02fb582ca11d92ec9eb0168d0a309da791', + service: '52.220.133.88:19999', + pubKeyOperator: '93dac0f5d028eeddeaf4257919511991872523675ab24d1d971af3ab1900f27fc617d1d53a846c32abe1ef52a2cb26ee', + votingAddress: 'yMbYh5KUeFrePfCcEhce4GGXPF21vs6YW4', + isValid: false, + }, + { + proRegTxHash: '3667fe83d6c334eae930252ca9bdd22d3eed1aee1c3b5b40d7244b98bea2c77b', + confirmedHash: '000000000882cafe55ba050f6d84cb7095ceea8056d5dc0c004b2997cc02d605', + service: '173.61.30.231:19006', + pubKeyOperator: '8b6159beec3c3c1ba223fa988b5806a02edebcd16869a2e053b41b7db3e28f12136636974f5333317fc67a22d2b9b3db', + votingAddress: 'yYmWHHP4i812Lyj8PWT6FsuL6yikkH7hYC', + isValid: false, + }, + { + proRegTxHash: 'c0dc1876eca746f08e401c7873260e277baf0096a0b19e519e6298b649dd23bb', + confirmedHash: '000000113e255c63df05c6481e88c787d43defb34ac5958f79f7c7faaf50eac4', + service: '178.128.87.111:19999', + pubKeyOperator: '03f959fdcb3eefebe409ee7044748f71ec8cba18a7a73df9d55d118e170d7ec2540d5c08a4cadc4bdeff3f7886265ac1', + votingAddress: 'yjYPS6w8S6KrAMu3bj5haPvHKSKQvAhRoi', + isValid: false, + }, + { + proRegTxHash: '5346dd62f6d0d846ca8b37cad7e4438d1effa1a61a63f8d55ba93069f560949b', + confirmedHash: '00000000085669fc9a48193ebea36ceec0d38be3ce4537ff0213dddba86424d0', + service: '52.52.139.186:19999', + pubKeyOperator: '847178ed08f0f5728dfe39ba9e3a43555b4c5e8100d825d91bf452bb7dad7bce7e8224fb665abc59cfc74d3bd1e040e1', + votingAddress: 'yVuX3X4i4pZhXZkqcDGWkxRuW3RbpaQZev', + isValid: false, + }, + { + proRegTxHash: 'c98c6303af03f7f3b2673ceece962134088e5dcc3c69a0977069c6201b26dc9b', + confirmedHash: '0000001fd4305c32af36cbdf651cf1585e1a2a5b93a871186c63cfbe67c8eb8e', + service: '95.183.53.17:10005', + pubKeyOperator: '8a209b5083c2b601ea18a04f0e92ee5befecf765486deb9643dc3b3fd193080c2659bba166f3873364964d5e8f7e4b93', + votingAddress: 'ySaqwxVfdvWnw8yMPBqoHaRQZc9YHvBk2c', + isValid: false, + }, + { + proRegTxHash: '94228ffa91da894b9a3a9e1db307aa48794d9bb84bc58ce3850c10f7efac1afb', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26075', + pubKeyOperator: '0d37f37c85b5d2a0e3727710d30eb88fa1344bdc9a6b4eb60499406dfdeb3958e952dc1bc13aa669bc47e1b061c24d28', + votingAddress: 'yjQ9DCRzgpfbDWLvvJf7qaCa9vZbE8eA2h', + isValid: true, + }, + { + proRegTxHash: 'abdbc34bf06c8e29cc84a5f827b4e90d9b49e10ac872ed1a55a84a761f5d46fb', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26031', + pubKeyOperator: '93a4daa7a44e7ccba28f407742ef9627a982d19590bd261ae5f0f8b44cd66e6aff524ca5713e184109fdcca30b1c2a5e', + votingAddress: 'yTC8Qv1DfCEtkM1kbhyDwSnPxHddqKsBh4', + isValid: true, + }, + { + proRegTxHash: '995d7facdd36d2db5a0e3621ea50678ce494149b4d2dece73d4a7fa2e095ac1c', + confirmedHash: '000000000a945eae95651b125c5f6188b6c4271ed3e5c0169cd0ceb45827cb5b', + service: '207.246.97.105:19999', + pubKeyOperator: '8c26812d38faf159f811a09c1462e07e40d6b44881114358cb5390b65778ee437018c5879fbf935fd78955899b67633e', + votingAddress: 'yRMZ6Fa84AewYEmWpGvoqEUTgWerfHrn8a', + isValid: false, + }, + { + proRegTxHash: '41e41c6b6e1b1c43e73c7644ea36eb622bee149ab05693ea487e784614e524fc', + confirmedHash: '000000000512f0cbe444e1047340df99ef6455276c5efd130cc814aef126e7fc', + service: '149.248.51.30:19999', + pubKeyOperator: '99567cfb20c6bed5d20638c31e7e512aedda02649e82f2b955ecd3e34f73c2229b350069f6e74a4304acedddd87997fe', + votingAddress: 'yXturAgedijBdGt33CNMAa4pdQybvgdC4E', + isValid: false, + }, + { + proRegTxHash: 'cfa6f7b58c78f827c15e8f1b6a5a2a3a92140101719006d8226a363e2c0c8e5c', + confirmedHash: '00000f2ba5df23ffb093cc6fd6f8d1418276d582a1ad61606d0322feff447876', + service: '138.68.45.118:19999', + pubKeyOperator: '8ff05fb385c08528b762683c2ab6864ab1ac031146d9be0df597961625c9538e0bb03ae6a759d66e1717e879ebaad41c', + votingAddress: 'yhks7vBpE2Q7AAF6SoQXjNE3ToAphAiV1q', + isValid: false, + }, + { + proRegTxHash: 'd811a1902c29708fee81311dba76418fa86a69fb09aa4784734e410cb223badc', + confirmedHash: '0000000e0a615dc913a1cac6f823a330f4325f4e6a94d95e3df3fa6eae24cd41', + service: '178.151.192.107:19999', + pubKeyOperator: '08118ce26460c9e0bfe6e80cc36c276f7caab9cfedf9f55d72be064eec0ad6ec2ac3502d972818ff5364276de8cd7ed1', + votingAddress: 'yaKMAfyXXFE33L7BDo6n5Kga57eiM51Aiz', + isValid: false, + }, + { + proRegTxHash: '4106bef7acd1243652495260325ec3baf5bba47bb6e5d934c67b96bb24e3af1c', + confirmedHash: '0000075f6979fb4507953801cec9154ec4373c10634c2856ef6687c0896869c6', + service: '195.201.19.40:19999', + pubKeyOperator: '98e7dca1b8dbcfdc54faff65b94f81f2e3fce6440bb10848d434a96ebe30ccbb33aa586a2d0ddce112e38cb09bbf13c7', + votingAddress: 'yQwe5Y5Xgsgtuz2ahHkP634BATFrsjb5Mz', + isValid: false, + }, + { + proRegTxHash: '95e048c6e09dd0367006df0dfe9737d69800526869590dc8acbc96fb94332c9c', + confirmedHash: '000000000882cafe55ba050f6d84cb7095ceea8056d5dc0c004b2997cc02d605', + service: '173.61.30.231:19005', + pubKeyOperator: '182ece65d7aef6b0d0a92c0e3451609607717f9cdb6d11cc6e31a2d625c7f40a8cace522b036481daf4e4425c41880a5', + votingAddress: 'ySfnordUG2758rcRfMz1328rmvzSUStEbe', + isValid: false, + }, + { + proRegTxHash: 'f57414829fed903bbe928ca2ab17450ac3ac9ccd6a9c7b5de86e549f9e6fdc9c', + confirmedHash: '0000000005612af1071fec3c0824d8427a1a79f9d01773c7358b08fed34fd3ae', + service: '185.213.37.1:19999', + pubKeyOperator: '90e39ea01f37a796795ca51a0d583defe2db3c8d7b1ce97b73776caaf31d8546232a831ce458b4ff9599f05da49a8724', + votingAddress: 'yRspEa8kVZtAy5Z32RFDa9B3JFTJQnLhAP', + isValid: true, + }, + { + proRegTxHash: '30eded4041e2c494c3e5ae391331b4ea1dc464d50a34d76178d20fe9904d041d', + confirmedHash: '00000000074d4e8d532a24773ae307e221f274d0e9cffb7bd28d0d3fabc9823a', + service: '167.71.223.212:1999', + pubKeyOperator: '0d731903ca090050801af45465c96d1248532819959a5a97eacb1ce518dcd5c5a21f20676d7c893f81ba672fcfb0f805', + votingAddress: 'yc1Sk81GBaLh3gH9pjpRwHqgP1t3jGbeAG', + isValid: false, + }, + { + proRegTxHash: '9d3664f872028a8ac0fe867129f4027e96ee9747a4690a29cae3d6e84311b47d', + confirmedHash: '0000001afddbda372e2f7bfd6080552eb548c8954d0d1aff3c33ffbb45b95435', + service: '173.61.30.231:19018', + pubKeyOperator: '862599b105fae8d252fef9707d02988e9f302ce6ffa7d1566908979816af6752e1470dab2f6bbed45ca65e64e4b74a3f', + votingAddress: 'yhrgd6foTdjHsmWeg9bQKghS9xUYXchz5E', + isValid: false, + }, + { + proRegTxHash: '9f4f9f83ecbcd5739d7f1479ee14b508f2414d044a717acba0960566c4e6091d', + confirmedHash: '0000002c1c2e8842db3fdc3594dce9febe6d862cbd832b1995d756a466a9f483', + service: '45.32.211.155:19999', + pubKeyOperator: '08e37b3fcba972fe0c2c0ea15f8285c8bfb262ad4d8a6741a530154f1abc4edd367a22abd0cb1934647f033913cca58a', + votingAddress: 'ybAZoZ6iybhEwoCfb6utGfU753R1wcQSZT', + isValid: false, + }, + { + proRegTxHash: 'b9e4c7189d01f8da6eb8bb5f4b8f8c2a0a24293d0f6e900aa0371bc32ec6021d', + confirmedHash: '000000000f2554f7a529d3d76e67678e5a2dce6025d1b8da6c118aabbced6908', + service: '139.59.86.146:19999', + pubKeyOperator: '98bae0f71cbb77fff1560f45680ada9492ec4c9f779df777754b54bfb3474729c269399bd3cdfc736866364d3fb011d1', + votingAddress: 'yjLeYNhxvH5exLJJcMyFgqW3Adp47QoVjr', + isValid: false, + }, + { + proRegTxHash: 'accd3915d6756cd44d0334a6f753082cd62e723408f02c52ecd7b74280cd3fbd', + confirmedHash: '0000010f227be0c01e195a477638557e8b2e92a909d026070b95c09a20984f9e', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yU4Qrh2Vpfzd199kgzrLJ3YDJp8gk9ZcAX', + isValid: false, + }, + { + proRegTxHash: '9998fa60315f3130aec798d922b3f81702329d1214c05e4a7c9d002f3e71dffd', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26034', + pubKeyOperator: '050c44ccabc5fe859e89dc52a52220d85e8117964f6720ec39fb965b65d340126bfab0b95b573c3052052e77ff506b40', + votingAddress: 'yaXTGpWrjwJjw3hAmNuBgwfRXZ2upJLNon', + isValid: true, + }, + { + proRegTxHash: 'dcbcf8311e414aaafac3650f3f61326dce386eee3d1a53da86e4c9925af48d9d', + confirmedHash: '000000000b303cb8c7cb2ae4fecc59ff4377e4139fac224ba2a2ff24b6fc9112', + service: '178.62.68.10:19999', + pubKeyOperator: '89277d2620e48dcf8456cc8815aa18ad3587bbf40cf0d1718696bd126e19791bb600b22f1063d4e5e8efe85fab8f90c8', + votingAddress: 'yg7qyMQrdRYTzo5hj6bVdD3tcY6QSLn1bx', + isValid: false, + }, + { + proRegTxHash: '7228951470758be7eecda8126c7a23fe8ad019e67f3fdd5507003bf0d2d4159d', + confirmedHash: '0000001676b838480d59c928be8bb5cef048b5010f09d807233729a558a8020d', + service: '63.238.229.186:9998', + pubKeyOperator: '88d719278eef605d9c19037366910b59bc28d437de4a8db4d76fda6d6985dbdf10404fb9bb5cd0e8c22f4a914a6c5566', + votingAddress: 'yV3WubWTpyuQUvucZ22apW8Gh14v4nCPic', + isValid: false, + }, + { + proRegTxHash: '5fc5f8808c9c0ca9ccf9615e3cc29fc4cf25d98e65c6288f11d1c42c4fa4503e', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26000', + pubKeyOperator: '0ae60c37954708db5c220727682e74489c5c5a1d623c3987ad698f26758000c218d7b9e27a1082e7b48c8ef6964d5f31', + votingAddress: 'yWeYx3JUXRHdD7FyjXRzJ6pVPw3RXZN6h7', + isValid: true, + }, + { + proRegTxHash: '50a5733b8430461139765ed886b998258bcca5a9df528e069d313e289df6a05e', + confirmedHash: '00000000077bd01073605cc5956a9ee883f8c47c8c7a337ecbd14ec5aa91e294', + service: '173.61.30.231:19001', + pubKeyOperator: '0418bfc9d8225bae5a889f1f74d47d539e9e7a8d441cb2b743b176e9d3a7ea4915fb40844cdb53a6faebdb4e826f9f78', + votingAddress: 'yPJh4D1sLdbXZG6Qu1X66FdNsu2qoBQ7Mz', + isValid: false, + }, + { + proRegTxHash: '6068b9cad85e9b3b23c88328801a02fbcabf338dbc327481769e06a82776c89e', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26056', + pubKeyOperator: '8335456428142ced12628374716c2dd9c4d3b89fc2dadc549ca4b682ee4760cdab3f24705801f0f4f81ae62fed9ea71e', + votingAddress: 'ybuCgKrNZL8d2MRP21FiAWffy6pKuWvc3g', + isValid: true, + }, + { + proRegTxHash: 'e9126eafb8f62f5a4e8b4d4f2419f4377a8dd14635fc749f9ca2636ffa93815e', + confirmedHash: '000000000088524172d60794604a0d28bbb125f6c3c649ba5eab8b6ac46f7658', + service: '54.149.207.193:19999', + pubKeyOperator: '8ad9500ef26ae510e0dd8cf0568b2a89d1234697873db2fcdd11674a73caba91cd416f9ac701f4f7807d8db102bc4a39', + votingAddress: 'ycdU6EyVggw4RaW3EKPHCMBeT6vzRDXgbJ', + isValid: false, + }, + { + proRegTxHash: '6f0bdd7034ce8d3a6976a15e4b4442c274b5c1739fb63fc0a50f01425580e17e', + confirmedHash: '000000000be653cd1fbc213239cfec83ca68da657f24cc05305d0be75d34e392', + service: '173.61.30.231:19023', + pubKeyOperator: '963692dfe42c25fc6ba4f3ec7f11181d1d8e97910eb6349b20dc9f79cc3cfa6adbd402a1a808898c4086690fc029d27e', + votingAddress: 'ySBU7oXuuTSJqtmUArMRFsKefJPtEDkESG', + isValid: false, + }, + { + proRegTxHash: '563743fc0e5109dade1f4ccd2f997c9821dcdac58414ae3877ae5bbf901041be', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26039', + pubKeyOperator: '98022f5042c242d5789f5ab73f713f2e34d211b47960d23e8a34a65e225a117a60cd6f6251cfe7b189b65a7cb81a9e5e', + votingAddress: 'yjQ6rZoi85csmJr3dQHTTn2Dfo5tjJNhwB', + isValid: true, + }, + { + proRegTxHash: '0b4b33aadb8095af383a8c9c5e63750b8ef4abb5d9c091360d788cf18267fe9e', + confirmedHash: '00000000003b6a01f6477284844732f6cc5d55ad393a06abddd178472b142d0e', + service: '145.239.235.17:19999', + pubKeyOperator: '11d05fff5f406fd207bc8984188894b6bbd32098e58244136519a51c183c70db3d713a33c9a55f8d6993f644fb34ff2d', + votingAddress: 'yajR2F8Qqv59dMpLny63xKeCSJHJKY6ZKr', + isValid: false, + }, + { + proRegTxHash: '6e84dcf6f2ddcf4444bec6dc070d9cbc52c3ef6681a14238b2e1390a77a6435e', + confirmedHash: '00000000157b35eb1479c67365202a899cd93cd84244215ba4a35b83fd92885b', + service: '23.91.97.211:19999', + pubKeyOperator: '0064583f3f5dbb756708aa405572d2eaf3349ec2d9048c93f21a2d1e5a0da7ae1675d27d626035ce0754de1898d5cc30', + votingAddress: 'yZDi6dkNKHYxqjMfbvrwgvf5HFas9vE2WX', + isValid: false, + }, + { + proRegTxHash: 'b9b63c1edfe991b4f45bce60de002807d2179f55eec8892ad23b838e4385d3fe', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26038', + pubKeyOperator: '029fb155b6b5beae540545516c660e72a1fc4297d012b17b5df6fe016aa0ae36b253aff7dba4780182259559d1726185', + votingAddress: 'yU4AkmhKBFPq3iFkyKaCNSJnfVmGFawHU6', + isValid: true, + }, + { + proRegTxHash: '4ac3d41c3b2fb88c4e4c33b465ec1150b43522985fe221f18c4b91b5f7b43cdf', + confirmedHash: '00000000000b3f46f23f8139fe17759e1410b02655bb070905ff04fceb0829a4', + service: '185.62.150.195:10001', + pubKeyOperator: '8ebffc014c8b97d9da8841464d3c7cd09b9f679471a068666c217ec13524ad7ccafa50eb18126c99edcb43fb74e290b9', + votingAddress: 'yPHa8A915DsehQmKK7MRQQedVEwjHZwnDL', + isValid: false, + }, + { + proRegTxHash: '87c23375674218932c768502d4ed00794fa327b0a95f3fd07e3366021284a8ff', + confirmedHash: '0000000008e21facf8a6acf9025c223b242c9bc32c404d093c625a96de57756e', + service: '3.83.240.220:1998', + pubKeyOperator: '8074793934715bde7630f4f267a9647955ac45400792369bd3e5f88e2b9d6c809251b79428e3a8ec07bdbb7364e3c299', + votingAddress: 'yiSiKosaJxTEZ9JFSvUg8XhMLxFAHp34cy', + isValid: false, + }, + { + proRegTxHash: 'c940d40d2849fe70baeffa8e343024d01dc80380f38b2a015798f503cba26d3f', + confirmedHash: '0000000ce19e75bfab825e97269f7065ace279e378516f62f0c34658e2ba3df3', + service: '207.154.250.175:19999', + pubKeyOperator: '069dd3113d6320397e9674ba3595f46dacd562e013e9e80a2e7d1095525f35134d4a8c19f4cd4a19d3886edf60328755', + votingAddress: 'yZSNyTWZNydmZUkFAVty3FUZbydAof8NQN', + isValid: false, + }, + { + proRegTxHash: '32e5ad5cf9a06eb13e0f65cb7ecde1a93ef24995d07355fac2ff05ebd5b9ddbf', + confirmedHash: '0000001960431ec5a566e69f28ae0f6fa3199bd99ec527cccd02f7541d77300c', + service: '95.183.51.146:39999', + pubKeyOperator: '1326ddac1044e0219dba7dccf6b43d1deed3e897717ca06757243b02516cfa67e24026f7a317cf575b40c10e7f6bf7f0', + votingAddress: 'yYhmQPak2w5L8KSwVw9R5wpqzPbAJ1fK7v', + isValid: false, + }, + { + proRegTxHash: '92b5bbdfcd2d46938c23f5d48ac6dbc2fb041172766455fbd8863cf81e8bc9df', + confirmedHash: '000000000bb44e3519fc1f1b43ee4982c329e4e0f898821cde114185263fd69c', + service: '106.12.73.74:19999', + pubKeyOperator: '07cff9e4c50da82722bf41fa5da01ca4bdb238d8d53fef085a56c34a432f6994c79e5bf754898499ec4dbe91eec0d00a', + votingAddress: 'ydRRskBneJ6sY4eP9DEc2FhvEAa7xRFJdA', + isValid: false, + }, + { + proRegTxHash: 'b5c0d248eac5f19d665159412f357073359d0d643930adee1d071f02e9ad0a1f', + confirmedHash: '000000000d6e349660e83e6925295f92beefdb06756104e7e7e6e83e11814fb6', + service: '[0:0:0:0:0:0:0:0]:0', + pubKeyOperator: '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + votingAddress: 'yYpaVuojgzDNiMnBNHH87E9NTT3BpFDgMT', + isValid: false, + }, + { + proRegTxHash: '2ab90c655fce7462791fd57049fd3477460cf45aec3483dd8f92ac76cbb5b65f', + confirmedHash: '000000000a38c31bafc093f1963dd5912505588b2c5f35ac0eee5ea4d7b0bff3', + service: '159.203.21.20:19999', + pubKeyOperator: '06b2e598c2a16e7394cff63bb9939e8bec49849f2520f97d8de70ed9336625ea0582889be68007e93663685d03d6996b', + votingAddress: 'yZp9BeBaGwnQEZMbe327hvsKfi64x4h5Ko', + isValid: false, + }, + { + proRegTxHash: '5a6d674367f4fad9883595d74d6eb628c59495a3af0732d24db983359cb7127f', + confirmedHash: '0000000000a32c92001fb2641d1f22653e459d0f1543457f3365491f2d03117a', + service: '185.195.19.212:19999', + pubKeyOperator: '8f53fb19c3be85ce00e96d634221f20a06a3a50942998193004264075a70422a3305f57c0c478a70ad69f1112e2f9993', + votingAddress: 'ybXEeMPyU81hzu2c6bv2VZY3xMbZgjQkgz', + isValid: false, + }, + { + proRegTxHash: 'a3e1edc6bd352eeaf0ae58e30781ef4b127854241a3fe7fddf36d5b7e1dc2b3f', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26094', + pubKeyOperator: '04d748ba0efeb7a8f8548e0c22b4c188c293a19837a1c5440649279ba73ead0c62ac1e840050a10a35e0ae05659d2a8d', + votingAddress: 'yMfroSZRBxVeU7owng1YRv1jePMzYxiVji', + isValid: true, + }, + { + proRegTxHash: '24882d9422a58849ab7c37617aeaddaa163ed5f600ab5e26db177947ef51779f', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26025', + pubKeyOperator: '98ab084c5aec382215721f37dc4c8a3f2ba450e8d4cd85aabd8b751b96f0cc9a45454c1e3319bbfa1d0853e098f60f19', + votingAddress: 'yhhYuCyawTbyVnmCgsDueWUrWrXug5Pt9z', + isValid: true, + }, + { + proRegTxHash: '1f1b266459465c15ac66e646f115f7a9a851f65655b458e1128657bbbfbee41f', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '54.154.211.242:26054', + pubKeyOperator: '09d302e2437e5724c8eaa006664e1ac6a7f595515186763bed95f17d12c99e221609da3c09f5d6550a7dec35c3f099c4', + votingAddress: 'yQtSC4e4t9RpxzqoFyFAk1NR75vEcEL2bU', + isValid: true, + }, + { + proRegTxHash: '946cfa76691437835e78fb94e96cf526f8aa9aafbf2d58181f40903efc33781f', + confirmedHash: '0000065656ebd4f2b8b3f3a6fc0e5c8b95834ad432099c81ff37c16e8c570bae', + service: '51.68.175.79:29999', + pubKeyOperator: '191651dfa8aa0cf65486799bf708a6e706b699878ab2d7d3eac248212ef5fe2adb02094d1bab670ab7817436a25bcfda', + votingAddress: 'yXspQ9mdY3m2xPV7JiRefngZ34SxZBvMG6', + isValid: true, + }, + { + proRegTxHash: 'b0aee43d5964ae06a7ce63c03332d9f1af46386b91738cbbfdf42f67db488c5f', + confirmedHash: '0000000010cc490ede341e5ed1d6576d96a06731ff672f9df1ae09d5a9c028ef', + service: '34.233.155.236:19999', + pubKeyOperator: '90ea99287802a44836309be934ed63933b423580626adeb428026acde6bdb283f370ff19bb37f81b0e4775187ad006a3', + votingAddress: 'yNTh6hhKDn1D8d5C3q4t81vuRnBZA6Fi9A', + isValid: true, + }, + { + proRegTxHash: '77b259c1e3c2d6ff06a764b88e2050f1362dafe0b8c8e2a5c27addffcaedcc5f', + confirmedHash: '00000036bb5f7f8055eab3eb95f933203f2ea4bc74ab474a3743c6d2472cc86b', + service: '52.212.19.71:26063', + pubKeyOperator: '8da90866fdd35d834783a7fffe0036085d3c0908d3a9326b358f5b2192b4b3bad0ed02736597f8c603ecdd13adcc0f6b', + votingAddress: 'ycSxYNPfrncJcGDKudU6yqurLXbmRWtybE', + isValid: true, + }, + { + proRegTxHash: 'f718902044925ab8ba5089667a4c2a1e45b855eb4388d21c1b14e1d05bc1991f', + confirmedHash: '000000438d3d6cee854b7d30177f5dcb37db12c3207b72d883c8f39a31981440', + service: '46.101.52.138:19999', + pubKeyOperator: '0d2be4cbd0faf7a27695a4f11690ba772a32c9df368f0558998681d697e60888b7127314dfa8495096050638d8507c92', + votingAddress: 'ybZWBtGJGQkRR1F32XmCJu7MgzJq6t1ona', + isValid: false, + }, + { + proRegTxHash: 'b43dadbd485e4d1e1d202ea5180f0ad4e8e7f05e97a7e566a764ed714356bd1f', + confirmedHash: '0000000007e96484872936d5783fdd5e12d11093051a4c4d73a4a1de5d01cfbf', + service: '47.111.181.207:20001', + pubKeyOperator: '90c0e9ec9dc5f08b1d4d0211920fe5d96a225c555a4ba7dd7f6cb14e271c925f2fc72316a01282973f9ad9cf1e39e038', + votingAddress: 'yQ8oETtF1pRQfBP4iake2e5zyCCm85CAET', + isValid: false, + }, + ], + deletedQuorums: [ + ], + newQuorums: [ + { + version: 1, + llmqType: 1, + quorumHash: '000003c9f4d1d56c1805832efcffea8d3338f79457138d953d82e48f4015a220', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '92c9283ec9ff66571278e9c59bc121a60b0cf48fb28f7b8812c95b8c9d88ff3acef031ca43e5704e276b0b2e497e6535', + quorumVvecHash: 'd31fa3b0741389753fac3bcc5b301e8ade6f4378f6592c8ed743ec265a6a5b91', + quorumSig: '014bd484e67037e4456eb7798767ca74cf11768852c34a6dbeb5669e04d3b8a5058e10da87a12d3a69e7922edf2ab88617c9dc7ffede9ca78ac08d4e04be5aedfadd02a8cdcf4d74b4b8b856ccf6a74fb80995652182e0b405fca39099ed4ac0', + membersSig: '0b48b48a99b12d4f2c868dcd322d9f10064c513733b8b784c80a2995d9e1536fb48933100d17ffd066b7fb241fb35ae00f949b2b46eacc3b9b79c38e115e69a876bdc185ea79a7ab08f5440ad44d27b6bf60aaa15161fc805ddb49f14861d0d0', + }, + { + version: 1, + llmqType: 1, + quorumHash: '000002df62636a47af1046c0f5ece8e1d5fde9b18d572059fe00621668727a26', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '80de8564cacea4ce889a897a8075fc3783cf5157e0badc7e73a2811343f9a24ec9068ccabc9568339ba8526586427dad', + quorumVvecHash: '9bd9d7f7bd24474df5ade8cacc0688f759102fe366bff6605b2b079746ff4521', + quorumSig: '0c5902d8f342b6e72ad5c5c37032cccf998567d7c101e9a2c526bdb108f2bebfac7eb2babea7995884164b5bfbf83e1d11ba8678fefb8d1bea670d68bcb00f55234f135d0602926a7b2f27f97930370ea5c6b2d3f4dd72ebbd99b00a59bce0cf', + membersSig: '1972b1ea85ab624400b7accfbf04f2efc47dafd1db14ff6cbbb67a2ab754d885703c6668f70cbaf69c60a413aa606d2e1023a2a9534f827795c013eddfdad6a289902d6aee0890f4fcc7058a45478a363e6f582db842362efb5e5d0c3c76e6eb', + }, + { + version: 1, + llmqType: 1, + quorumHash: '000006e75a0f3551e79dc306eb6f47aae80e408e23b006e62e82a9c6a6f7c32b', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '11a3455ecf0577b213a056aa0d313a0081eca46f0bebac5fe991fd09d04b211f71ee337f10257a4f31b8047ac58b556f', + quorumVvecHash: '7a5d7069006cec12587033dce9744a289b3924d538d529d492181050693d3b34', + quorumSig: '92cc8cec5af871bfe208e464ecac26d64db5843c3511d66be541e485854faf3e83814b9eef32c84b779e17ca6d2af34f161f90cd5e8e070ac975dca8b84b189a14b54510e0cd8c6f75cb7175541d050a2069602155aa65e063b64aef9824615b', + membersSig: '892e85b71523e3547dbe4b4ac33b960d455e19f4bca3d37f5c39c8b581cf6e0385a94f3de4bef410ef828b7e2518cd550eec1de0ca131e4f07afee176ea877263373212e5f70dabe2fa129707cd96eef0c32f1bad41a9a6c83a6ca6a3d1808c2', + }, + { + version: 1, + llmqType: 1, + quorumHash: '00000479c5fbb0c1b42bbefd4c0794cabfc92f0ca14dffa5161350d2774f0c48', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '06852ec7b53ec4a977b8e528923d28d6f386486487bc30dda55d104cc6c6307a9d54fc76b77c7d3075de886b7ba0dd3a', + quorumVvecHash: '032146614efc60947bd34f790b03c26d74318795d184e583032ac2756b218588', + quorumSig: '94452ec4a55e5b5cc3e9faa3ca1024dcc9e2ebe1cee7d1a655508715cb0c9329ac95c6692e3e535ce12e63ce99c688c21738c9c729cc8e188034ea7cbccd4a9cb794b6681954a95a2af410c39b7197bdaf2ad1cc9d301b46db22d738e9002f23', + membersSig: '81314e280379e095396756f6a0c47ecdc80e718b83231a5ba38c6ac3272368ba5d247d7bffbe10cd97a2d58b8cf6d81d02310000c1c5019f6f807dd8e3418e63d832b1957b83c05821740f21dda57a9c644ca5a32bd529c02ad24e3ab83d35f2', + }, + { + version: 1, + llmqType: 1, + quorumHash: '00000b43a95013caf801325f53fdca7ee6419b9426661e09a3af81cd52854050', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '17c28d5d10bfd1fa48931bc01ccd7391f33c0675dab97218e3910162aceeff9d5e1d43670e4c3e9fc9ce0dacd82f8343', + quorumVvecHash: '560c63d429234a1acdbf573dfc11f5e7f3d1f68045d0d5e40dba4c32bbee0586', + quorumSig: '05b6947123659a357c724ea89a18f48d03e1815a4fa4c210a164b911d6fb9487245aaabb7edb0a95786a1147e909f57a05d9a713647047c19391789f03ff46c4c92598de1fad3258eca2fc75495d2767ac0b6b18937667d4950a6ecb96bc38dc', + membersSig: '0d27b6df540da7997d99dbec0084bce96aa30f9e01a5ec50b826861e2e9501557010ce56063d20de2227a895df54ef9517a3155934b33069162b59de4152b14a2dbd933f0bdab87b0bd7e99848fd0aa51326e20109adcf1b2294bb702d8d955d', + }, + { + version: 1, + llmqType: 1, + quorumHash: '000004a0c7c29636970e1190639e6eda2e7bc75c18701085bfa67ff13041df59', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '8307be29ccb61ee5a7da6ca8dd855213602892024c80d0200454269e480c20cd12c840a66e1609647f8ee12873ad51a6', + quorumVvecHash: '8d461a70d33d0cc64b27dab85add827da39858eabda90f99ccf0ea5c7f9eda67', + quorumSig: '0bd5fc9edfb8957c1b6ffe442f53075653e4c7dd82f30c2f025c8c202ea115e7f5b60e0ec93a9772900770aad74124a00a0e666a6c45cd7cd6722d0e943018da4890a6886450351e63e36832a59e99ac427ff1e16ecdedaadaa8c7ce6c6941c3', + membersSig: '033b4857494910bec9a578279eb6f76ac3f2a28ba2e907c0d603835c57a0fdf54be868a8ed3019e1bad2fcbed9935e5c1965ebace1ec914ed4cefca1ad3f4ba04b45983649c1718ad27fa233fe6b4832c6c52c9b16d312f6e25f1c4bdda2ac56', + }, + { + version: 1, + llmqType: 1, + quorumHash: '00000dbe513158d366e37df666c618055552432b5932e160a4845367ed48c663', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '85ccb27a3b657183bceeda06c7c850e306a12b090467f8efc0c9a66b26e624b486de922c7b5ae9eae941ba1e3bad639b', + quorumVvecHash: 'bd668a8e058dc581733a5b88fbd0a08b1731219bf203a79568c33beed5d71c50', + quorumSig: '0f6697ca4a99df93ba5e497825c25721b355b6821143de82618fb7c094ad5583077be509efca816c3231161517a9c1c80080736bf5399c063dccfa18c8b44a7fb14e5dacdcd05bb7ecf17f35ff5942a527f2ff694dcbe3da64c79d73bdb8f414', + membersSig: '92f1495a98f279afd8d3fa68ec0c0e4d9c9fc926248d10289a2b3ab2df1fa6eef3455e1eceea9e48a2cc9be32e46b2220af89707e966fa0e73bcb229e666dae50745229a026833fdba311dfb58f406ddec41f578684b752078c287cb99c2b435', + }, + { + version: 1, + llmqType: 1, + quorumHash: '00000fc816bc9ab8c7ae3107501bbf71c23c4fccd58ecb2ba185ccbb8ff6d863', + signersCount: 49, + signers: 'ffffffbfffff03', + validMembersCount: 49, + validMembers: 'ffffffbfffff03', + quorumPublicKey: '92878497b80fe3ac55287bbd27e42bc4a7e6a42a8cb56860af1a75992b8caea2ec0fb8e9761b5b11474650b16494fd6e', + quorumVvecHash: '4b9a5020763f48981ed1bfe0e5b72f93ca405b2b03b61c20235d31839561eef1', + quorumSig: '10ffb77d1430dc766b6bb19fed060b3eb20185de89b220199449df8de505a89bb277245f869ad997c914fed291287a140b7090480b43905bc7283bc2b5a082ea948205175017693a8439cee3c6c6ca9d2af03359d446ac6079798107bf09ba37', + membersSig: '83ff5fb747eaf5b85884ab29a6a06989fc5effc4709438df9ae77fcdcc4549ce1237aaee36cdb935fd4668d1d5e1ad12157e5467df67132c3762898fdb877bdcb8201beee4ee4d783ec0029bd76c5b793e11d19db89e6b1cc7e3577fd086c7ed', + }, + { + version: 1, + llmqType: 1, + quorumHash: '0000042262dbe883b9165f44a8ce676d84b4e34629cd853e3ae0945ffb83636d', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '0255ef1dfa571a356b3dc493c538c6554119b37f0f0dabd9ed0465b603438724b29f4040791e85775efe54f8debc527c', + quorumVvecHash: 'e446318136e498f8e42d0386d9ff40ada98facc44d75abe0753ad0cdc0530b3e', + quorumSig: '127a428120097cdfb7ce732dc9579bd23058203aaacbfc14ee764bcb720b61dce26c0e2b04cdb43b1f575b20c4b734c010b6d59d9d1b38ca4fbca11836d2825b639b75afe2b2df9ace9358664f54257995c7eba2f43db6e7f02ee232d041f2a0', + membersSig: '130a05fc5ee5956e8d6b58c0051fd100941d0a51426444ceb47e51724bfc726230557d3c666f7fae726e57f65ba59b5711508c59ba9271ab88212e4f5323e8d9e67b01e6089523525df683aa9728cade58de9e9fa277a221835cdbb502e44b00', + }, + { + version: 1, + llmqType: 1, + quorumHash: '00000647134b03ecee71bf63e8f2b8f4755b6332ddf8df3e572cff42d1998479', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '1220bb580a690f7becb10cde87b5c386fd29f67e54beacb40d5c233c9d957fd9f7e502afda38c1eb2e75a6f95bf2cdc4', + quorumVvecHash: '4351783adb967db4bec47eacd65b29634f6662af51d2f2637375fc04a5b4a299', + quorumSig: '0afe29b3c969b3ceeadc1b6224b27b2696d24f0afd0fee5e7135854c6e0789fd5abc15e7108eb9ac8a512954c8851163032c424c610a357e5acc712c459f3924e529f3d9476977417bdb64e1cabf978086bdf455c727c4012ef85ef793793839', + membersSig: '0e65bf7b6635326d6e55ea8f4ce41d57fa1fdb0168c222adde704458001483fc2f4c94d3d2e6e7450ce23b1c725f3f570667d615cd0421158cd59e30b8b9bebbfe926b3e51460353344b2359765e365820e778eb2126319a32016aaee679e1ff', + }, + { + version: 1, + llmqType: 1, + quorumHash: '000000d58a8195fd79bc105a8a4058808f786a4e21957f630a4569900129337a', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '0a5d32f7d3d896f8c05d1f6862db3a33bc47c49298eeabcbc6d171dfc1822a9279af54d34696f4209e58b6a128ee61eb', + quorumVvecHash: '9ae3c82119e4872f564cdc7d0bb82110ac97251b1b2767d4050ec6b6fc41deb1', + quorumSig: '82769438b4440cfe47394363e49da4f8972060d025e18d31416fca9d19fe304aae49b2d096836ab126e5ed88efc5f30b07889b2023dd2aa84bb83b03dbbc291895e7c11b8b37162a035d2efb37c5cef7955da48b2402a7f561cd44eae3bf2432', + membersSig: '00f7be17804126dbf4102cc26698f6865ceec0b3df8922a7d12a15f7c5aa849f21045a0afced0071da76da01c80b12fe063694b9a4b83f54eca2b148f594508509ceb53fa49cfc6ff14d6da7f08228a3fa76dab84a3f214ae568b1d877ac7f75', + }, + { + version: 1, + llmqType: 1, + quorumHash: '000007c9129bd7c90b761b1a6f4120bee08a15cf75bbfc1979dc49a30cff627c', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '0883007c61213a62638583df78ed5f4abe1bd9e0e2d106fe9a38ede6f4eeade9417d22c6e25c5aca55e94a3134eb20b0', + quorumVvecHash: 'd3b67c9ed966e33534ea0557a85baf44ab27ca83467a75d9cc8e824271606a2f', + quorumSig: '8739b82a522a2bd709245a52043b433545a937ece65226e863d4c5d57826386a32bb48118e694d6f77bf14e04e8adbaa0330fd0283a95d53de386089b395fa27a86c14e32aea4368705178e2f89f1529da5b003484f0ddc9ec53c303b11f7cfc', + membersSig: '9199675e9ad28780390b70ee6d399c30dc7d7c4ab93d6069964bde38d1eb6370eb4945b238de2fad942cafbdca2433f7047d28a135b60fac86709cb393022b4c024ba7d73f8f5d6a4bda2a4636fc172df63857eae1beaa00c37ff01056e863cd', + }, + { + version: 1, + llmqType: 1, + quorumHash: '000003e5de501ab991bf9c95673cd12ca8f4943cf772405c0a009d49ac381782', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '86a7e8163123c634c3ef6c4a0f9755bd24e6a1b240a876f53f77d9427a5c396672c1130ea881b71192e5e4f1bbbfb7b0', + quorumVvecHash: '6478eed9434c9f7b640e2c3e1644bfce1f55e6aa00a2fa5e2d9c25f32c181289', + quorumSig: '055bea03f4a1d74617416297a9267d903a7e0a6b136b395ff1e0535cbcbfaffdbad5ed057f927488d31b2ed3869fc89a018361cd788ee8aa41d583ed0f42fe609905745d5b986e93c70f9d6379437065c9ac1b79089e6307ec30ef226d46c185', + membersSig: '96d5b356dc1bd2c366976dbdaa50ed7a42b102227a90b658c7356799e78707780a83edd808fcc2785ec3d76f2c4f25bf06707ac2be00cbcb652ad1587a94ca20b5574e25a1f5ce050eeb7a31cb78614346040e5c5e5b7f0c5ad3791c9809e20e', + }, + { + version: 1, + llmqType: 1, + quorumHash: '00000dda49bdd9065eebceeefc74a841a09f62953fbb795997afa9cfd2d13b86', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '823c926dd162a40a4c871521d599dfc4af7cbb546329607c3863aa1be311cc3dd699c708cf7c208fdb9ff00783ee0be5', + quorumVvecHash: '34fad1325318dbfeb932e42cced579094a5f7d5e438eb618506001c55f587daa', + quorumSig: '19ceb88cedb9f92ad5e55098243a1df7ec2f8e826be11fd3f33b71dad39af1b2001173d8cefb9377864bfab5536db3be03d34ebf6e10b3796074fa9096a6f6df6229376656305bd97cd67ec71af02f4972a5174a4591b7633815fa25b4823642', + membersSig: '92f318ed0564176135aca65b155c1b57ee5ed5e3a64fd8cab613576eacfbdf69ff8c4b274b28f1eaa508608bd4ab81e109bd61dcf803c6ff498de109dbfc1c9f95e99114d89a87fa5c312c82d3b65df6ec64121c0752422115e5620568ff9cb0', + }, + { + version: 1, + llmqType: 1, + quorumHash: '000003255f1bbb83cd6cec99e270e857e5f48f449e7b0b698244e6568a5108a1', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '122d6d6798da8a3904865f6c28533b660a5cf8a90cde006b8cef01f814be5e73984fc546d889a3c042c110fbcce1f414', + quorumVvecHash: '271144d39c4f08379af5eb8fc1504aa6deff5e4bc84f0e0a8d91412495edaa80', + quorumSig: '06140c865001babbbc639d7502c5790a12199da27288a148c9427ae3acd5c9f5736bbf560a5f9a6197c53aba834bf0b918435f770a9cbeb6ff25af1b68d1c309da5ec5f4b9c74e3c8402f5303f2cb8b2926758e81f7cca7c85e66239fd70e480', + membersSig: '0cd42a9a82fe59bc7a9f5ee291afcc495038a7bc728058ca364d76580c58b654d71657bbdd8ebfd8a5902b1a3c3aa85d1697b28c5fc10d68c751eaf4411d07a2e2eb22694f6435a3b7d8a1269d21665c6f8f834b509e1f2b6bdce58e47a945aa', + }, + { + version: 1, + llmqType: 1, + quorumHash: '0000008d3d35c02fab8cc631d85d968c1e09cff14c78d517821851956805b7ad', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '0404550c74ffcb2a85ed56adbe6b7f8740063348ec1a8b35854d7059b029086b4224f9485fc2d7f31615938fb06a221b', + quorumVvecHash: 'd62ffb14b97196051d1cbc1327606bd9965ce03fa349a7a6679f5ee515aef2bb', + quorumSig: '0af28f9a60e13a47e7c8610e4805892e6bea5de7f38d6611a60a074e3723b52618fe486f9130824d463594c2766bcf0104620059a3a9db8f9caf632b60b1c89ce90981d890f0927ccf2ddd05dc603c9f65f7bee8a9f671de94458c891db56969', + membersSig: '97423ef771e7b1c6ea55c938ca7164552ff878956f183c70f761e0beee4187010f0b16c2d5fe5adb948d80bb614bff9b1648fd10c76918631ed98d90460081bffdb1717c441f7cdb2e0a48301c743e271d75728822e0edb81b6d567a7b71536e', + }, + { + version: 1, + llmqType: 1, + quorumHash: '00000a95d081a06e2ec67932b14b70b9d8ef3a586cd27ba288afe66d0fc069c2', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '86d0992f5c73b8f57101c34a0c4ebb17d962bb935a738c1ef1e2bb1c25034d8e4a0a2cc96e0ebc69a7bf3b8b67b2de5f', + quorumVvecHash: '66db73de07442a06de20a171828abbd81589f8c6dc099cdc191d22f40aab1096', + quorumSig: '1604a01eb78aa70fb28d12ab01fb9a3632036ff19fa249e5809e425ea09bda515a3d03d3c04901f8cb9ce35ef17cac4208dd21f3ffa4847a26c03357e5c2db2d0cd1b406e75389dc61effa4a8e30d287d4349cdb94d801ae3fe542c36460f2b8', + membersSig: '140f5a4db1a3330b7dfdda8fe181137b2644577efd843a60401f0dbc7b0856782578bc9d6ab1a0b133596bcc158d781d02ed4db881cb4cc3260273dc90a53c1d1ce37930fa106c47db4cf7702b2e956dcafb7b180bea7aae2d662b7a6c217f27', + }, + { + version: 1, + llmqType: 1, + quorumHash: '00000c84bf68a4ef66f5f558586a346e605fad75759078c53bf2f01013b17dd4', + signersCount: 49, + signers: 'ffffff7fffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '94aee5dc783ad01f282fc70c08a58c3daef9a9f91892540e8c8a462b45f31b615799cc0e6bfb1754fbab9aae9b5db197', + quorumVvecHash: 'c24099479936dc0e2776cce5fe2d55e4ed920daca470b4a0f839eda35784e1c0', + quorumSig: '90810b96fbba398595b1f48b56aab25a41aef18b1931e87747166b401f6c59fdebd961552f548c7bdcc94ab86fdea4930575d581a7889f1e843280a3e9d511885082dda9cc2d1db9ce37d04b72208a5fcc3339e880703fd67c566202ed9f6dbc', + membersSig: '999cc066a9b2b59c0efc02216b534af8e16ad8d42ba2f98ac5522cebc96345fe876bf82592edcb2307dbf5e6e06d0c7b05e26e45f843407c67a8dfc870dcc2b21b347730c28eabb78987ce5910ddda6b42f3918db741652e80322fe2f898c967', + }, + { + version: 1, + llmqType: 1, + quorumHash: '000003c60ecd9576a05a7e15d93baae18729cb4477d44246093bd2cf8d4f53d8', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '00d7bb8d6753865c367824691610dcc313b661b7e024e36e82f8af33f5701caddb2668dadd1e647d8d7d5b30e37ebbcf', + quorumVvecHash: '8868aa2aa8e58c3771ad1ad4a3e90cfc54e4667a750b83fac1ece93624cccdb0', + quorumSig: '003657bb44d74c371d14485117de43313ca5c2848f3622d691c2b1bf3576a64bdc2538efab24854eb82ae7db38482dbd15a1cb3bc98e55173817c9d05c86e47a5d67614a501414aae6dd1565e59422d1d77c41ae9b38de34ecf1e9f778b2a97b', + membersSig: '09c3e46f5bc1abcb7c130b8c36a168e1fbc471fa86445dfce49e151086a277216e7a5618a7554b823d995c5606d0642f18f9c4caa249605d2ab156e14728c82f58f9008d4bcc6e21e0a561e3185e2ae654605613e86af507ca49079595872532', + }, + { + version: 1, + llmqType: 1, + quorumHash: '000007720c0e03437ee3c49c2819e8c2975affa46bd413f2aea746fbccb757db', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '97060b7adc21b2a0af28c7b73573ed38d10918f507e324b62373f633a634ecdaade8d5692f1909672b92895195cf6486', + quorumVvecHash: 'ae1a27c825d093d0e3bd9d1eaafae127cc6e6fa6a61c9b3deb6c8d4ceabbba80', + quorumSig: '08c1126f1bb0134281d0fc9eaf1ea3ea777520ffe60c1b1420ab1ec196098fa72d489acfef3be6b45094e8e089bb7e6c15164f9ef85f44cd20fb94e20f147c84d1032394d098a1a66bb21d322a83abec60be6409942250f8243d0b47d66db11e', + membersSig: '902e75727a13044d030d04e07a3ece2e25de7534a565e1f6d9406f9659d96233616c8b2d9c7f028becdd1dc7f3d9997f0cec84f8c84c8da57fa6364b0eb4fcec59b0761836381baec57f8e3819caa1a032812e454b98fe0733db6f8dcfc3e5fc', + }, + { + version: 1, + llmqType: 1, + quorumHash: '0000055cc3271edb256ae4f8bf1837b7accef516aca3e450546fb0598efef7e2', + signersCount: 49, + signers: 'fffffffff7ff03', + validMembersCount: 49, + validMembers: 'fffffffff7ff03', + quorumPublicKey: '8b061142afe2dad1d45c0fa61575196389bb201bc77b5b6f25828439bb6fc47d46ab229feb348ffeac33aa1017ba0cda', + quorumVvecHash: 'f7324d613bc76b387dd963389accc47ce427b171c23de8ba79c878d3db46f88c', + quorumSig: '87edc7aa86948cd72c927751e32c6be86c2583b81cd9c21c7e6e0f4ceb4f21f45636d9718ba65431fe1c675da0f67c4816bb86cf7428c8db1191f02fb27d228e5bc1458f927ffcf09266a4050615ead9198c839ea180ee484de3c700c7a67476', + membersSig: '112806c23c54e461b178522687ea2ec935212301d799a10ad7805b54b0538c05bba860b238dcdfa262ba91fcc45614d310c4cf0ff9f423ff672286fa33c44a25f5aa75a0d0b5272c20776787bf92ec73f84eeb5e0cd41402a48a4d3f5671daa7', + }, + { + version: 1, + llmqType: 1, + quorumHash: '00000ba15a36058b650463a0f6037f14626b959ec58d41ebd893c3d76cf80bea', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '9633b264f5a6c88295f7f6d5020036e431d540dfb469ee54da3fa7c60d56036ddf107c89afb0e2e1f6d8804c521386af', + quorumVvecHash: '19b93927936b7f17441fe8f6d6edba24f0061e290f1c065f70757de4aab0efa2', + quorumSig: '0329f30efb0651d7290d41bc993cb3960a95439198a2fa6aa26a86e66b920872fbde12bc4fbc31e62e3e2d340ac372f0039f399df43b7ce9b55916fda2f1da88bdfc05171e1c4037312490c71696a38de60a85c2e5b1945d20b07864c965e136', + membersSig: '820d78935fbf4730a9f9454c098bc66da22fb29bf57cd7a8f60de642665fa2777c8b0b9b1e4dd978e2f709ece07477ae119a2442e27f47246fe2ec15e76db06499a87c7f0995106bc01bb10d3b4f6940c3c4407301152f802d8652c5f189cbde', + }, + { + version: 1, + llmqType: 1, + quorumHash: '00000abadcc671159b19c7f68bf77795ed0ca3d33aa50fd8ebe533fb2a9028ed', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '04190972b9d0452d56d67ff0332eb1485446e251091afaf9796b437cea66f455d8c155f25a4ae2267749b410652b1353', + quorumVvecHash: '8a80690b36e0b9c56fd867b200a795d011d6a248634ea10aa703475bd4b2e1ab', + quorumSig: '98b3676f278cd17ce1e406dfe08b2441422202a6dd9bcce90abc788f86d24926743f0a311638e53c5e88a9bcdbf4a1080e45c41ac3ba51c76beff32ec82fe626dbe2e06dd3c60a4793beb3266e01568d144b2a33a27c47a57a7e8ee60781de83', + membersSig: '061f9674e759cdde503e324e7c03ae06830147885ab00b0843a32482759b6652c75c7d740401f4abaeb4db3332242ef4067acd7132f81a3bd12518d47f50408d881515ddf5af481a8fcb4261e2227a6a05a9ac1003f1f42165ba2bf2504657d4', + }, + { + version: 1, + llmqType: 1, + quorumHash: '000006f93f411e1a0e48f38d66108b6778e0342bf8e1031012e713654d74bbf0', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 49, + validMembers: 'fffdffffffff03', + quorumPublicKey: '08237346bb5fcf51d7d8083a77e5dcf6c016fdc400a81c4aacd66a2c8b9ed9034e68286613dbea0e616c4ece103821be', + quorumVvecHash: '6fb0866848fdba762caf7528dee0a7762d84c974d44b6fcc29e0feca36c94cb2', + quorumSig: '946b21908a5d37e46632b8f9c73eed33eed7871419572fe876740780f0ac9b265f7625767d82e1ec0a5bdbda681489610e5fb7e51a5c0abdaac483a90760b1eb1badf0b93afb30518448b505f4f69fedcd09257144c16d5f19021708b95acf9c', + membersSig: '10db84e57fba926d64518613d4a005574c337bed40c208287fbe157f3ea3393a538bff6a9b34b6d3bc0646e20866cd140e95855c691d84ee5136a4ef55c757a34e6e8bfa3134abd217fbade0dd9ba0bf41f524678910ff367c57821c38057007', + }, + { + version: 1, + llmqType: 2, + quorumHash: '00000093d7e01511d38aff53a407d1e983a7266c25696af197fdf72a851a6a5d', + signersCount: 301, + signers: 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1f000000000000000000000000', + validMembersCount: 301, + validMembers: 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1f000000000000000000000000', + quorumPublicKey: '16b914701462a4e0c9401b544810924a1847405d087b60a90bf1a8c6dc98cd31a7b0cd8427257549acb5f0fd0de8b01f', + quorumVvecHash: '5402c443f74a5e9c609e0e401c429a6fd80a11eb73051a6b18a7dda76e1d9715', + quorumSig: '82fb28075b9f8f48c519cba8ed386e3a12a3b35edb1ea4ca0f7ba66d079ebf4a259a8205bca6c0ef58ecbccb9a3161121442aba88de0c8bda23ca813a47fef40c763dd9c8eb2ecd22635e927715844f2c2c2d91e8ec07fa7c8ab0b6139833796', + membersSig: '89d071088f793ba0c8225d01fd37245addf48c0063eab7c3a34387c629aacae8700f046c6fcc234a39056e3bb8928ad00d763f39c63b5f095d50c57db41a798eaf0c2b95b43a171e109e3308037a03aa61fd9c0886dfe37301dde1fbe6ed2b4b', + }, + { + version: 1, + llmqType: 2, + quorumHash: '0000019513bca4bc47171fe43a8c840dd15d2bbfc1018c5059fd36e25e66e971', + signersCount: 300, + signers: 'fffffffffffffffffffffdffffffffffffffffffffffffffffffffffffffffffffffffffff1f000000000000000000000000', + validMembersCount: 300, + validMembers: 'fffffffffffffffffffffdffffffffffffffffffffffffffffffffffffffffffffffffffff1f000000000000000000000000', + quorumPublicKey: '917e619100c56d635bb40b89f04418b5b8146f7f5c8f5565c3a96d148f2668e19dcd7f323ce8053fa6623eee449f27bd', + quorumVvecHash: '1dc20bc8166a474b8fd74e0bd9dc8ca650d482974be14130e725662b5438f6a1', + quorumSig: '11fa234f23293a013a4d934b5089c3adba225ffaf40e0f1315fad152eb7ccf7e23623ebc859fae74400f35522d1226770d8a898b52f002a46c2b6a0d65a4e6afc4a956602df5b1facbf8cac8d6c9037de5703a592bbd12b12f43260a9d499939', + membersSig: '99b5b401aa9ce15cf035250db1bb3dfcd36b18a9048fb65e27d92586649dd4b900c57c81a47ea2836523ce857754c2a607125a2ed20ccab308aae9ceb70f9cf7c534b0b3024b412062fb2b15e301364bd2b8525789536bf485c8ab75de4bb498', + }, + { + version: 1, + llmqType: 2, + quorumHash: '000001702422af778c9d1e16a891f58fbaabb6ff82dea8fc1910ab80552bdf9c', + signersCount: 301, + signers: 'ffffffffffffffffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f000000000000000000000000', + validMembersCount: 301, + validMembers: 'ffffffffffffffffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f000000000000000000000000', + quorumPublicKey: '9408850ed5f61f4ecd7cad7928574729283b14e8f127577b4a537cfd0f00ddcd174a529b0104e9f0f99701b6e209b05d', + quorumVvecHash: '9efbeeda8f7e00a587567924f1f7cbedecdda93170e52a0b9d312a21c3767196', + quorumSig: '803f9373798afddf5225a441b711e928f09dff63efe2e02a5fd26a7ff493081916729cc4474c8f40e195f51bf400125608eddfbb62392c5058ffa7185041eb7405c37fa64de5a9c945a4400d70a52ef48cc38115007016dcd721886705906a7f', + membersSig: '97760cd173301036761cd273167a5301c6df30e11a8647725450e05ce07c904a63d86d96688ef7112ec76bbdccfac5150d0c4e813bef0b5f46870093e096face161d764373c91ec2d7afa8afaae72f6b10da50660cc4d858c6d74987b69bf051', + }, + { + version: 1, + llmqType: 2, + quorumHash: '00000117df5733ef3c624b76fe65b0117b15f8e7c9287e2a198c0f30e14dcae2', + signersCount: 301, + signers: 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1f000000000000000000000000', + validMembersCount: 301, + validMembers: 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1f000000000000000000000000', + quorumPublicKey: '97ea8dc2c66d374f0cd1393396258a1ab593ef453ca2efcd48df4280c2beb86129c695e46d58bfd4c3e1a6c9231e920b', + quorumVvecHash: '24a531e5cd509cd532ff4545ad4c66d567e0d58b038d5a90f9248c75446f00f0', + quorumSig: '0835890e6a36372b6e05d1e2ff9dee83c4785cb6db0c7ecf54b50e9bb6feaf791f86fe49ddd44f4701190b915f1a283c08ea91dd8f97c40554085a54b21da838bc56e95a22a2fcc926feb169e13b38ae4fbe46a7fb7f4de7c5080169ec0cdf4b', + membersSig: '81ed136546e8d9ae08c010612317360f056844f7ad1ea9bd6dd3b837c12399d84aa55cdd57e398ced5e69214023c0114060601315b053c0fea41508350bf8da583dd760c804f7201a46fb74c4f8d473eaca7db6126a48a669628265fc6b8aa55', + }, + { + version: 1, + llmqType: 3, + quorumHash: '000000339cd97d45ee18cd0cba0fd590fb9c64e127d3c30885e5b7376af94fdf', + signersCount: 389, + signers: 'fffffffdfffff7fffffffffffdffdfbffffffffff7fff7fffffffffffffffffffffffffffffffffffffffffffffff7ffff1f', + validMembersCount: 396, + validMembers: 'fffffffdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1f', + quorumPublicKey: '002a5dfd00f393a181688b0b81cb6dee658939fee0d1163f34fbe50bf3d9b7da721e721dd2c505490730462ec0b7bb05', + quorumVvecHash: '6924ce47fa307780918f7b7a0bd1ccefaab211d80019b4e87365a4a7756353b0', + quorumSig: '0a76386291014fe1591564a9dfad552562d68e2291876b4e9032d3a512b98004fbcaa4ebd541a6b9ca0425feb4fc29d90b7fa748d08ab5c2c57596d9a69d61b1015082e8eb6be7e713bd421c96d43e5c0ff570463a3cc2c4b62be7774fddfdac', + membersSig: '975134cdfffcfcdd4d349b4b5f70bfc06f04de05f70b5bd9c7447342a2ea048bf160b1cc7a54aa12e8a100516ded0c7e06b04211fb1433c153d27d6755974ebdad61329e9345e8530f51a0c80f4246ef35951a05331b10a4204b1157eb817431', + }, + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: '7ca269191d5f022adc94bdbed842452a954b952a0270b129f86f2dc5a943590f', + }, + { + baseBlockHash: '00000ac05a06682172d8b49be7c9ddc4189126d7200ebf0fc074c433ae74b596', + blockHash: '00000167c955d38f2dd08951df73cc95873d550a024168dca77a7667914a624c', + cbTxMerkleTree: '01000000015163cac4fb1087525db72d3a1ce8284e354287ac1f5f72ea90a234b4a0d706490101', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603d90306010cffffffff02eefccf31000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac65fbb74a000000001976a914970ee41ee261dbf2c421764c2d2df654a57a91be88ac00000000460200d903060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff0f5943a9c52d6ff829b170022a954b952a4542d8bebd94dc2a025f1d1969a27c', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: '7ca269191d5f022adc94bdbed842452a954b952a0270b129f86f2dc5a943590f', + }, + { + baseBlockHash: '00000167c955d38f2dd08951df73cc95873d550a024168dca77a7667914a624c', + blockHash: '00000b0f913fdfa0f5cebcf2d31b3acf14650b5c3ca11041ec08760af786f2aa', + cbTxMerkleTree: '0300000003691d2a2b5e18a01ca66c8e01bb70a558a015f0d11b0550ec4e6f41ddfe24b2088850a773f73862ff6e339fdd802db1ad7a8a08c2d7da93e39817020d2c7273bd647077761e96fe000f4d5655929f685bbd406b07034b1725bd3e2de9e80bbdde0107', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603da03060109ffffffff027c00d031000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088acba00b84a000000001976a9145615f5efe0555e81f7799ff3e1017eed0f5d816088ac00000000460200da03060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff0f5943a9c52d6ff829b170022a954b952a4542d8bebd94dc2a025f1d1969a27c', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: '7ca269191d5f022adc94bdbed842452a954b952a0270b129f86f2dc5a943590f', + }, + { + baseBlockHash: '00000b0f913fdfa0f5cebcf2d31b3acf14650b5c3ca11041ec08760af786f2aa', + blockHash: '000001e91e9f80460aea8911aaf2d67875de215556f320eaf042c85ed46e238b', + cbTxMerkleTree: '0200000002c36f7014dc07d788eed3c31c91733bffdb62fe8df53e94add889d192898ff888813917b7b1e3106b38d05534109db40f4e96cc9b0da9a2b6b8e7d9cf368bac310103', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603db03060106ffffffff03aefecf31000000001976a9140ddfb51fd90bb8449574852c471fa838c16d275788ac5bb6f849000000001976a9140d5bcbeeb459af40f97fcb4a98e9d1ed13e904c888aca847bf00000000001976a914c623e2d926af5eb52dcc2b931993d3e52714511888ac00000000460200db03060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff0f5943a9c52d6ff829b170022a954b952a4542d8bebd94dc2a025f1d1969a27c', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: '7ca269191d5f022adc94bdbed842452a954b952a0270b129f86f2dc5a943590f', + }, + { + baseBlockHash: '000001e91e9f80460aea8911aaf2d67875de215556f320eaf042c85ed46e238b', + blockHash: '000006efc67c6cd75552cfb24fcc9bfe2d91eb669e17f70268e3ababcc96a4d2', + cbTxMerkleTree: '010000000106bd16a9555fbb7b58b8671243a0b6a3a496a3eeab105d5c98e16621741b3d1c0101', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603dc03060108ffffffff02eefccf31000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac65fbb74a000000001976a914379812a735584751c27ae8d47205e7048ad8afb788ac00000000460200dc03060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff0f5943a9c52d6ff829b170022a954b952a4542d8bebd94dc2a025f1d1969a27c', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: '7ca269191d5f022adc94bdbed842452a954b952a0270b129f86f2dc5a943590f', + }, + { + baseBlockHash: '000006efc67c6cd75552cfb24fcc9bfe2d91eb669e17f70268e3ababcc96a4d2', + blockHash: '00000e32f3ab6259ca5bb79fbfdc42d6fb7c6ffb2de0a82f6698a7ee1f5f6589', + cbTxMerkleTree: '0200000002f4027ce1b98ef4c88b37963edcbe7d4bfcef2f8fe9bbdd37f60452501b2dec3fef683084b2f24e3f5023abaf870b44871d73d0620dd5f82fa6ab3864072a319c0103', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603dd03060102ffffffff023312d031000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac4c1bb84a000000001976a9147770d9914a34f8d7b8d2eaa9fa20699e79b1c82188ac00000000460200dd03060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff0f5943a9c52d6ff829b170022a954b952a4542d8bebd94dc2a025f1d1969a27c', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: '7ca269191d5f022adc94bdbed842452a954b952a0270b129f86f2dc5a943590f', + }, + { + baseBlockHash: '00000e32f3ab6259ca5bb79fbfdc42d6fb7c6ffb2de0a82f6698a7ee1f5f6589', + blockHash: '00000a4cba0496c348de03c53c3a396add2b0db3d1b845dd6ca258e4c46cfc4d', + cbTxMerkleTree: '0c0000000542059a9791d05526b82c0de465da6683b4ad7f73e3713362ef8571fcace7d8300c267f8580aa00608eb7b38109f85ff1d0644ac7be6ea2750c9155e7b8a22184ab767ae473e6bc9294e0dc993738890dcf427cd1e4e684c77a33ff86addb24ba34d6a9352bcef6ad8e5d3efaf02bb71cc9003e9b5e19673ef693dba7b8cd30fa6efff530fa9a08fc084aa14c1a0bde489ce9ff0e7775d8c8c34ce2033e0d6de5021f00', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603de03060107ffffffff02d100d031000000001976a9140ddfb51fd90bb8449574852c471fa838c16d275788ac3801b84a000000001976a914703c4c06da3612cc2309e93d40d4e5e739f1333a88ac00000000460200de03060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff0f5943a9c52d6ff829b170022a954b952a4542d8bebd94dc2a025f1d1969a27c', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: '7ca269191d5f022adc94bdbed842452a954b952a0270b129f86f2dc5a943590f', + }, + { + baseBlockHash: '00000a4cba0496c348de03c53c3a396add2b0db3d1b845dd6ca258e4c46cfc4d', + blockHash: '0000078d232cfdd02334828b4f9f9daadcae3525e05612c26e0b5d3980d0eddb', + cbTxMerkleTree: '05000000049796d61a3b99b7ce47c01fdf22f36d05d868078cb0e989a30b0394f1798d63f94c1db4c620384dfc752d27caf28ee40ee2ffe3fea32e0f9b1062a6d9c3319ae27ba0e140e1c0c3cbe67513742e142830904e5e86d2be173972fa9fc6a92f24a14777a1c955aa48fb68f58235d64d8f517f21cfb52fdd895247bee8d8c8228865010f', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603df03060101ffffffff0258fecf31000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac83fdb74a000000001976a9142b2fa71a36eac313219822b700ee2f11f48d7ca688ac00000000460200df03060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff0f5943a9c52d6ff829b170022a954b952a4542d8bebd94dc2a025f1d1969a27c', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: '7ca269191d5f022adc94bdbed842452a954b952a0270b129f86f2dc5a943590f', + }, + { + baseBlockHash: '0000078d232cfdd02334828b4f9f9daadcae3525e05612c26e0b5d3980d0eddb', + blockHash: '000005f462b40eb3db5e9c1fa7de7258366922ba52f30482ad24bbf9518222dd', + cbTxMerkleTree: '0a00000005b80e262c9958870882c4076430d0aa935d9e4f1d1a8fec2c6061d0f40d0c2260bcada7828d61588d357bd25514232b161b15123a9fb880aca76f5d52f6591e9da512eccd56a4cff0577c979b05bf9a378a97fb29bee597f310eeffb300e0d4a23f7828bb4f1f192153c6f45103de6163117b69c9e7a04474792bdc99f969c0e0d13df433a7aa95bf8c2c9f8a4c63b0e7e68b69b5428f703afe27b8a99c78ba4e021f00', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e003060109ffffffff021c00d031000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac2900b84a000000001976a914b13fea72c981e82080e3016efb9345aea570803d88ac00000000460200e003060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff0f5943a9c52d6ff829b170022a954b952a4542d8bebd94dc2a025f1d1969a27c', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: '7ca269191d5f022adc94bdbed842452a954b952a0270b129f86f2dc5a943590f', + }, + { + baseBlockHash: '000005f462b40eb3db5e9c1fa7de7258366922ba52f30482ad24bbf9518222dd', + blockHash: '000000615db85d7b2bac04b9c852d6ca9ffec16d24c2ea816e28ec1acb5dcf22', + cbTxMerkleTree: '050000000406667e560db292cfed37a306d108713939145c66c1547757a539d0fb4ddf5d374084b87a357aa9d92bd59143239dbea73399bc19c2ca046f7fb00b5d3e2d7aa272b2695673919541d892f1ac9850f71d15402cc570bdc3ab7b0e5095ad80bb4d1cdf1bedc39cc78bee2a44e66aebaacf3312330ee713ecf907db491ce4539290010f', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e103060106ffffffff0258fecf31000000001976a9140ddfb51fd90bb8449574852c471fa838c16d275788ac83fdb74a000000001976a9141f72c30a78380bd3494f87188578b8faf116e38c88ac00000000460200e103060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff0f5943a9c52d6ff829b170022a954b952a4542d8bebd94dc2a025f1d1969a27c', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: '7ca269191d5f022adc94bdbed842452a954b952a0270b129f86f2dc5a943590f', + }, + { + baseBlockHash: '000000615db85d7b2bac04b9c852d6ca9ffec16d24c2ea816e28ec1acb5dcf22', + blockHash: '000005d7bead3943423f8f58afd6994d93384d9ebcd2bc8840b6c633750fb67b', + cbTxMerkleTree: '0300000003dc151704d8057576356882243288ef1abeb4a5510a46dba43d434cae27f1d285e5109c5e639227d7fb9d4368f42e82963df649a7079b6bfac528a4d5424c152d8878bb5f4b1ec9606d0087331974fbfa477f1639d2ec384b24b960b74468ca690107', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e203060102ffffffff0249fdcf31000000001976a9140ddfb51fd90bb8449574852c471fa838c16d275788acecfbb74a000000001976a9141f72c30a78380bd3494f87188578b8faf116e38c88ac00000000460200e203060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff0f5943a9c52d6ff829b170022a954b952a4542d8bebd94dc2a025f1d1969a27c', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: '7ca269191d5f022adc94bdbed842452a954b952a0270b129f86f2dc5a943590f', + }, + { + baseBlockHash: '000005d7bead3943423f8f58afd6994d93384d9ebcd2bc8840b6c633750fb67b', + blockHash: '00000317ef5e5b10cec94d864c40222b5c946a101ac88324b780de84b4b600a6', + cbTxMerkleTree: '0200000002edb89a1f21bdf55fe2fd4dca0bee62d1a7979437ead7c267cb6e2fa8af21b00016265ecd36ae5d07092f494826f929947f7771e72d474e7d9a373bf17daedb810103', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e30306010cffffffff02eefccf31000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac65fbb74a000000001976a914374a35e55d7d04519fe282556ebbd649b384656888ac00000000460200e303060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff53fc1cc04336bdfe9210627b13395bf49fc1b3c641b2ba5cad81b4cb2d167fd9', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + { + llmqType: 1, + quorumHash: '00000479c5fbb0c1b42bbefd4c0794cabfc92f0ca14dffa5161350d2774f0c48', + }, + ], + newQuorums: [ + { + version: 1, + llmqType: 1, + quorumHash: '00000ac05a06682172d8b49be7c9ddc4189126d7200ebf0fc074c433ae74b596', + signersCount: 50, + signers: 'ffffffffffff03', + validMembersCount: 50, + validMembers: 'ffffffffffff03', + quorumPublicKey: '18993589ad25864ac8bc6fb529079e42ad115e95310bb24da64c077ba12c019e7e32dadb2fc097732ced84a483eb3249', + quorumVvecHash: 'e83ac06f18fc3fa69e50b295f6b269fcc030a258b6ce69a1ffacb7e4b40419e6', + quorumSig: '172da42164ac45a3220837f99d58a9c26acf345d9b69cdbf39f37b6fe378045804e45b8047b6165c59b92681b16a040d0f69a6288276e5e11df8065f9f148bb0b1c2ba79a178b29f7da49a700eeae2e058a8f6f7d3a2067c654fbe919f8365db', + membersSig: '90113a8851adead9266e9ee4fccde54067e1c6c4dea05e67e32fa438f0d3e45756fb6576ccd1aa968fc3133f0b276c1d0bcb44f0fe2e9da92e952827af1c196e303e99bae8eeae0fcada1578b89599ac6eae5e8b144d40b9a9346ff12e9e226a', + }, + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: 'd97f162dcbb481ad5cbab241c6b3c19ff45b39137b621092febd3643c01cfc53', + }, + { + baseBlockHash: '00000317ef5e5b10cec94d864c40222b5c946a101ac88324b780de84b4b600a6', + blockHash: '00000c22544f8d3d460a76c62f76ad6482d087d03e60412c7bdcf42cc1153505', + cbTxMerkleTree: '0200000002eafbbaa300ec1af7f0c019178966609ea093baaaf263a64d9e76aec2e7975d1c72a7f74a70d5602fbc67a8c27639c29d3cedef7535b57c852bcf69c6c87e4c570103', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e403060107ffffffff0249fdcf31000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088acecfbb74a000000001976a914f32bb66d6fdcd07b1268d21a3e021c9c9544529388ac00000000460200e403060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff53fc1cc04336bdfe9210627b13395bf49fc1b3c641b2ba5cad81b4cb2d167fd9', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: 'd97f162dcbb481ad5cbab241c6b3c19ff45b39137b621092febd3643c01cfc53', + }, + { + baseBlockHash: '00000c22544f8d3d460a76c62f76ad6482d087d03e60412c7bdcf42cc1153505', + blockHash: '000006675beb0bbf5e399c933cebc589dffe93712b20af63ef0bceef8818fc77', + cbTxMerkleTree: '01000000010df7d1c2a5b4b1be63abab5d74dadb4e7237725e13b84d3095c4ec151dfb71160101', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e50306010affffffff02eefccf31000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac65fbb74a000000001976a914f32bb66d6fdcd07b1268d21a3e021c9c9544529388ac00000000460200e503060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff53fc1cc04336bdfe9210627b13395bf49fc1b3c641b2ba5cad81b4cb2d167fd9', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: 'd97f162dcbb481ad5cbab241c6b3c19ff45b39137b621092febd3643c01cfc53', + }, + { + baseBlockHash: '000006675beb0bbf5e399c933cebc589dffe93712b20af63ef0bceef8818fc77', + blockHash: '00000e2fd05526d6c535664db8e4bb9dcacbd6fe383a47e402914d5a109d25a9', + cbTxMerkleTree: '010000000106c6600e6f28bf7ee5617a47ee2000c38f7bd2ae146f929f0d47391e3b6916930101', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e603060104ffffffff02eefccf31000000001976a9140ddfb51fd90bb8449574852c471fa838c16d275788ac65fbb74a000000001976a9145a375814e9caf5b8575a8221be246457e5c5c28d88ac00000000460200e603060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff53fc1cc04336bdfe9210627b13395bf49fc1b3c641b2ba5cad81b4cb2d167fd9', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: 'd97f162dcbb481ad5cbab241c6b3c19ff45b39137b621092febd3643c01cfc53', + }, + { + baseBlockHash: '00000e2fd05526d6c535664db8e4bb9dcacbd6fe383a47e402914d5a109d25a9', + blockHash: '00000fd124de645de51fb535fd52aaea58e871dc4511cae344ae8b3b7dd63b33', + cbTxMerkleTree: '010000000185e84cd8b25052843319a95d34d78b0381c7f20f707c6ca0b2df719520426f580101', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e703060102ffffffff02eefccf31000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac65fbb74a000000001976a914288e0340106fb37e4cba12e21c2af87e965ebb8b88ac00000000460200e703060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff53fc1cc04336bdfe9210627b13395bf49fc1b3c641b2ba5cad81b4cb2d167fd9', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: 'd97f162dcbb481ad5cbab241c6b3c19ff45b39137b621092febd3643c01cfc53', + }, + { + baseBlockHash: '00000fd124de645de51fb535fd52aaea58e871dc4511cae344ae8b3b7dd63b33', + blockHash: '000008cc02119a783921e214f358c72eb42941d1f972e0111da5037f5007270b', + cbTxMerkleTree: '01000000016b19957f4fcdf44574c3cff3f1bcc4e62f2f75a96f20dbace2600d52a601e0ec0101', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e803060101ffffffff02eefccf31000000001976a9140ddfb51fd90bb8449574852c471fa838c16d275788ac65fbb74a000000001976a914253cae727106faa4bf76b0b4f8375091889f671588ac00000000460200e803060076f0fa94a217a4b87c23a4db952c583472964b6a2e894e8305a67f9ea575f8ff53fc1cc04336bdfe9210627b13395bf49fc1b3c641b2ba5cad81b4cb2d167fd9', + deletedMNs: [ + ], + mnList: [ + ], + deletedQuorums: [ + ], + newQuorums: [ + ], + merkleRootMNList: 'fff875a59e7fa605834e892e6a4b967234582c95dba4237cb8a417a294faf076', + merkleRootQuorums: 'd97f162dcbb481ad5cbab241c6b3c19ff45b39137b621092febd3643c01cfc53', + }, + ]; +}; diff --git a/packages/js-drive/lib/test/mock/BlockExecutionContextMock.js b/packages/js-drive/lib/test/mock/BlockExecutionContextMock.js new file mode 100644 index 00000000000..f4ce3d01c1b --- /dev/null +++ b/packages/js-drive/lib/test/mock/BlockExecutionContextMock.js @@ -0,0 +1,45 @@ +/** + * @method addDataContract + * @method hasDataContract + * @method getDataContracts + * @method getCumulativeFees + * @method incrementCumulativeFees + * @method reset + * @method setHeader + * @method getHeader + * @method setLastCommitInfo + * @method getLastCommitInfo + * @method getValidTxCount + * @method getInvalidTxCount + * @method incrementValidTxCount + * @method incrementInvalidTxCount + * @method setConsensusLogger + * @method getConsensusLogger + */ +class BlockExecutionContextMock { + /** + * @param {SinonSandbox} sinon + */ + constructor(sinon) { + this.addDataContract = sinon.stub(); + this.hasDataContract = sinon.stub(); + this.getDataContracts = sinon.stub(); + this.getCumulativeFees = sinon.stub(); + this.incrementCumulativeFees = sinon.stub(); + this.reset = sinon.stub(); + this.setHeader = sinon.stub(); + this.getHeader = sinon.stub(); + this.setLastCommitInfo = sinon.stub(); + this.getLastCommitInfo = sinon.stub(); + this.getValidTxCount = sinon.stub(); + this.getInvalidTxCount = sinon.stub(); + this.incrementValidTxCount = sinon.stub(); + this.incrementInvalidTxCount = sinon.stub(); + this.setConsensusLogger = sinon.stub(); + this.getConsensusLogger = sinon.stub(); + this.populate = sinon.stub(); + this.isEmpty = sinon.stub(); + } +} + +module.exports = BlockExecutionContextMock; diff --git a/packages/js-drive/lib/test/mock/BlockExecutionContextStackMock.js b/packages/js-drive/lib/test/mock/BlockExecutionContextStackMock.js new file mode 100644 index 00000000000..1c8ee5b1e83 --- /dev/null +++ b/packages/js-drive/lib/test/mock/BlockExecutionContextStackMock.js @@ -0,0 +1,27 @@ +/** + * @method setContexts + * @method getContexts + * @method getFirst + * @method getLast + * @method getLatest + * @method removeLatest + * @method add + * @method getSize + */ +class BlockExecutionContextStackMock { + /** + * @param {SinonSandbox} sinon + */ + constructor(sinon) { + this.setContexts = sinon.stub(); + this.getContexts = sinon.stub(); + this.getFirst = sinon.stub(); + this.getLast = sinon.stub(); + this.getLatest = sinon.stub(); + this.removeLatest = sinon.stub(); + this.add = sinon.stub(); + this.getSize = sinon.stub(); + } +} + +module.exports = BlockExecutionContextStackMock; diff --git a/packages/js-drive/lib/test/mock/BlockExecutionContextStackRepositoryMock.js b/packages/js-drive/lib/test/mock/BlockExecutionContextStackRepositoryMock.js new file mode 100644 index 00000000000..e528c0b13a4 --- /dev/null +++ b/packages/js-drive/lib/test/mock/BlockExecutionContextStackRepositoryMock.js @@ -0,0 +1,15 @@ +/** + * @method store + * @method fetch + */ +class BlockExecutionContextStackRepositoryMock { + /** + * @param {SinonSandbox} sinon + */ + constructor(sinon) { + this.store = sinon.stub(); + this.fetch = sinon.stub(); + } +} + +module.exports = BlockExecutionContextStackRepositoryMock; diff --git a/packages/js-drive/lib/test/mock/BlockExecutionStoreTransactionsMock.js b/packages/js-drive/lib/test/mock/BlockExecutionStoreTransactionsMock.js new file mode 100644 index 00000000000..44b2f87f360 --- /dev/null +++ b/packages/js-drive/lib/test/mock/BlockExecutionStoreTransactionsMock.js @@ -0,0 +1,21 @@ +/** + * @method start + * @method commit + * @method abort + * @method getTransaction + */ +class BlockExecutionStoreTransactionsMock { + /** + * @param {SinonSandbox} sinon + */ + constructor(sinon) { + this.start = sinon.stub(); + this.commit = sinon.stub(); + this.abort = sinon.stub(); + this.getTransaction = sinon.stub(); + this.clone = sinon.stub(); + this.isStarted = sinon.stub(); + } +} + +module.exports = BlockExecutionStoreTransactionsMock; diff --git a/packages/js-drive/lib/test/mock/CreditsDistributionPoolMock.js b/packages/js-drive/lib/test/mock/CreditsDistributionPoolMock.js new file mode 100644 index 00000000000..becb80ff46f --- /dev/null +++ b/packages/js-drive/lib/test/mock/CreditsDistributionPoolMock.js @@ -0,0 +1,21 @@ +/** + * @method setAmount + * @method incrementAmount + * @method getAmount + * @method populate + * @method toJSON + */ +class CreditsDistributionPoolMock { + /** + * @param {SinonSandbox} sinon + */ + constructor(sinon) { + this.setAmount = sinon.stub(); + this.incrementAmount = sinon.stub(); + this.getAmount = sinon.stub(); + this.populate = sinon.stub(); + this.toJSON = sinon.stub(); + } +} + +module.exports = CreditsDistributionPoolMock; diff --git a/packages/js-drive/lib/test/mock/CreditsDistributionPoolRepositoryMock.js b/packages/js-drive/lib/test/mock/CreditsDistributionPoolRepositoryMock.js new file mode 100644 index 00000000000..6fb9df0f867 --- /dev/null +++ b/packages/js-drive/lib/test/mock/CreditsDistributionPoolRepositoryMock.js @@ -0,0 +1,15 @@ +/** + * @method store + * @method fetch + */ +class CreditsDistributionPoolRepositoryMock { + /** + * @param {SinonSandbox} sinon + */ + constructor(sinon) { + this.store = sinon.stub(); + this.fetch = sinon.stub(); + } +} + +module.exports = CreditsDistributionPoolRepositoryMock; diff --git a/packages/js-drive/lib/test/mock/DriveMock.js b/packages/js-drive/lib/test/mock/DriveMock.js new file mode 100644 index 00000000000..0a032d38544 --- /dev/null +++ b/packages/js-drive/lib/test/mock/DriveMock.js @@ -0,0 +1,25 @@ +class DriveMock { + /** + * @param {Sandbox} sinon + * @method getGroveDB + * @method close + * @method createRootTree + * @method applyContract + * @method createDocument + * @method updateDocument + * @method deleteDocument + * @method queryDocuments + */ + constructor(sinon) { + this.getGroveDB = sinon.stub(); + this.close = sinon.stub(); + this.createRootTree = sinon.stub(); + this.applyContract = sinon.stub(); + this.createDocument = sinon.stub(); + this.updateDocument = sinon.stub(); + this.deleteDocument = sinon.stub(); + this.queryDocuments = sinon.stub(); + } +} + +module.exports = DriveMock; diff --git a/packages/js-drive/lib/test/mock/GroveDBStoreMock.js b/packages/js-drive/lib/test/mock/GroveDBStoreMock.js new file mode 100644 index 00000000000..795a3ea6ece --- /dev/null +++ b/packages/js-drive/lib/test/mock/GroveDBStoreMock.js @@ -0,0 +1,43 @@ +class GroveDBStoreMock { + /** + * @param {Sandbox} sinon + * @method put + * @method putReference + * @method createTree + * @method get + * @method delete + * @method getAux + * @method putAux + * @method deleteAux + * @method getRootHash + * @method startTransaction + * @method isTransactionStarted + * @method rollbackTransaction + * @method commitTransaction + * @method abortTransaction + * @method getDrive + * @method getDB + * @method setDB + */ + constructor(sinon) { + this.put = sinon.stub(); + this.putReference = sinon.stub(); + this.createTree = sinon.stub(); + this.get = sinon.stub(); + this.delete = sinon.stub(); + this.getAux = sinon.stub(); + this.putAux = sinon.stub(); + this.deleteAux = sinon.stub(); + this.getRootHash = sinon.stub(); + this.startTransaction = sinon.stub(); + this.isTransactionStarted = sinon.stub(); + this.rollbackTransaction = sinon.stub(); + this.commitTransaction = sinon.stub(); + this.abortTransaction = sinon.stub(); + this.getDrive = sinon.stub(); + this.getDB = sinon.stub(); + this.setDB = sinon.stub(); + } +} + +module.exports = GroveDBStoreMock; diff --git a/packages/js-drive/lib/test/mock/LoggerMock.js b/packages/js-drive/lib/test/mock/LoggerMock.js new file mode 100644 index 00000000000..ad45d080d92 --- /dev/null +++ b/packages/js-drive/lib/test/mock/LoggerMock.js @@ -0,0 +1,25 @@ +/** + * @method trace + * @method debug + * @method info + * @method warn + * @method error + * @method fatal + * @method child + */ +class LoggerMock { + /** + * @param {SinonSandbox} sinon + */ + constructor(sinon) { + this.trace = sinon.stub(); + this.debug = sinon.stub(); + this.info = sinon.stub(); + this.warn = sinon.stub(); + this.error = sinon.stub(); + this.fatal = sinon.stub(); + this.child = () => this; + } +} + +module.exports = LoggerMock; diff --git a/packages/js-drive/lib/test/mock/RootTreeMock.js b/packages/js-drive/lib/test/mock/RootTreeMock.js new file mode 100644 index 00000000000..2347b53ba25 --- /dev/null +++ b/packages/js-drive/lib/test/mock/RootTreeMock.js @@ -0,0 +1,11 @@ +class RootTreeMock { + /** + * @param {Sandbox} sinon + */ + constructor(sinon) { + this.getRootHash = sinon.stub(); + this.rebuild = sinon.stub(); + } +} + +module.exports = RootTreeMock; diff --git a/packages/js-drive/lib/test/mock/StateViewTransactionMock.js b/packages/js-drive/lib/test/mock/StateViewTransactionMock.js new file mode 100644 index 00000000000..4c8b767ba04 --- /dev/null +++ b/packages/js-drive/lib/test/mock/StateViewTransactionMock.js @@ -0,0 +1,20 @@ +/** + * @method start + * @method commit + * @method abort + * @property {boolean} isTransactionStarted + */ +class StateViewTransactionMock { + /** + * @param {Sandbox} sinon + */ + constructor(sinon) { + this.start = sinon.stub(); + this.commit = sinon.stub(); + this.abort = sinon.stub(); + + this.isTransactionStarted = false; + } +} + +module.exports = StateViewTransactionMock; diff --git a/packages/js-drive/lib/test/mock/StoreMock.js b/packages/js-drive/lib/test/mock/StoreMock.js new file mode 100644 index 00000000000..6a5c77d74b3 --- /dev/null +++ b/packages/js-drive/lib/test/mock/StoreMock.js @@ -0,0 +1,13 @@ +class StoreMock { + /** + * @param {Sandbox} sinon + */ + constructor(sinon) { + this.put = sinon.stub(); + this.get = sinon.stub(); + this.delete = sinon.stub(); + this.createTransaction = sinon.stub(); + } +} + +module.exports = StoreMock; diff --git a/packages/js-drive/lib/test/mock/StoreRepositoryMock.js b/packages/js-drive/lib/test/mock/StoreRepositoryMock.js new file mode 100644 index 00000000000..d37a99001ee --- /dev/null +++ b/packages/js-drive/lib/test/mock/StoreRepositoryMock.js @@ -0,0 +1,17 @@ +class StoreRepositoryMock { + /** + * @param {Sandbox} sinon + * @method store + * @method fetch + * @method createTree + */ + constructor(sinon) { + this.store = sinon.stub(); + this.fetch = sinon.stub(); + this.prove = sinon.stub(); + this.proveMany = sinon.stub(); + this.createTree = sinon.stub(); + } +} + +module.exports = StoreRepositoryMock; diff --git a/packages/js-drive/lib/test/util/allocateRandomMemory.js b/packages/js-drive/lib/test/util/allocateRandomMemory.js new file mode 100644 index 00000000000..4fda2848440 --- /dev/null +++ b/packages/js-drive/lib/test/util/allocateRandomMemory.js @@ -0,0 +1,16 @@ +/** + * + * @param {number} sizeInBytes - size of memory to allocate + * @returns {number[]} - result of the allocation - array filled with random doubles + */ +/* istanbul ignore next */ +module.exports = function allocateRandomMemory(sizeInBytes) { + // This constant is inside of this function because + // it's easier to pass the whole function to the isolate in this case + const NUMBER_SIZE_IN_BYTES = 64 / 8; + const storage = []; + while ((storage.length * NUMBER_SIZE_IN_BYTES) < sizeInBytes) { + storage.push(Math.random()); + } + return storage; +}; diff --git a/packages/js-drive/lib/test/util/setTimeoutShim.js b/packages/js-drive/lib/test/util/setTimeoutShim.js new file mode 100644 index 00000000000..976a0d47fe1 --- /dev/null +++ b/packages/js-drive/lib/test/util/setTimeoutShim.js @@ -0,0 +1,14 @@ +/* istanbul ignore next */ +async function wait(timeout) { + const timeStarted = Date.now(); + let finished = false; + + while (!finished) { + if (Date.now() > timeStarted + timeout) { + finished = true; + } + await Promise.resolve(); + } +} + +module.exports = wait; diff --git a/packages/js-drive/lib/util/ExecutionTimer.js b/packages/js-drive/lib/util/ExecutionTimer.js new file mode 100644 index 00000000000..baead9caa20 --- /dev/null +++ b/packages/js-drive/lib/util/ExecutionTimer.js @@ -0,0 +1,95 @@ +const process = require('process'); + +class ExecutionTimer { + /** + * @type {Object.} + */ + #started = {}; + + /** + * @type {Object.} + */ + #stopped = {}; + + /** + * Start named timer + * + * @param {string} name + * + * @return {void} + */ + startTimer(name) { + if (this.isStarted(name)) { + throw new Error(`${name} timer is already started`); + } + + this.#started[name] = process.hrtime(); + } + + /** + * Clear timer + * + * @param {string} name + */ + clearTimer(name) { + delete this.#started[name]; + delete this.#stopped[name]; + } + + /** + * Get timer + * + * @param {string} name + * @param {boolean} clear - clear timer after getting + * @returns {string} + */ + getTimer(name, clear = false) { + if (!this.#stopped[name]) { + throw new Error(`${name} timer is not stopped`); + } + + const timing = this.#stopped[name]; + + if (clear) { + this.clearTimer(name); + } + + return timing; + } + + /** + * Stop named timer and get timings + * + * @param {string} name + * @param {boolean} keep - do not delete timer + * + * @return {string} + */ + stopTimer(name, keep = false) { + if (!this.isStarted(name)) { + throw new Error(`${name} timer is not started`); + } + + const timings = process.hrtime(this.#started[name]); + + const result = ( + parseFloat(timings[0].toString()) + timings[1] / 1000000000 + ).toFixed(3); + + if (keep) { + this.#stopped[name] = result; + } + + return result; + } + + /** + * @param {string} name + * @return {boolean} + */ + isStarted(name) { + return this.#started[name] !== undefined; + } +} + +module.exports = ExecutionTimer; diff --git a/packages/js-drive/lib/util/noopLogger.js b/packages/js-drive/lib/util/noopLogger.js new file mode 100644 index 00000000000..a1593e9d71c --- /dev/null +++ b/packages/js-drive/lib/util/noopLogger.js @@ -0,0 +1,8 @@ +const pino = require('pino'); + +const noopLogger = Object.keys(pino.levels.values).reduce((logger, functionName) => ({ + ...logger, + [functionName]: () => {}, +}), {}); + +module.exports = noopLogger; diff --git a/packages/js-drive/lib/util/printErrorFace.js b/packages/js-drive/lib/util/printErrorFace.js new file mode 100644 index 00000000000..b6e745d647a --- /dev/null +++ b/packages/js-drive/lib/util/printErrorFace.js @@ -0,0 +1,35 @@ +const chalk = require('chalk'); + +// Faces https://github.com/maxogden/cool-ascii-faces +const faces = [ + '\\_(ʘ_ʘ)_/', + '(•̀o•́)ง', + 'ヽ༼° ͟ل͜ ͡°༽ノ', + 'ノ( ゜-゜ノ)', + '༼ ºل͟º ༽', + '(ಥ﹏ಥ)', + '¯\\_(ツ)_/¯', + '(╯°□°)╯︵ ┻━┻', // https://looks.wtf/flipping-tables +]; + +/** + * @return {string} + */ +function printErrorFace() { + let face = ''; + + // top padding + face += '\n\n'; + + // face + face += chalk.red( + faces[Math.floor(Math.random() * faces.length)], + ); + + // bottom padding + face += '\n\n'; + + return face; +} + +module.exports = printErrorFace; diff --git a/packages/js-drive/lib/util/rejectAfter.js b/packages/js-drive/lib/util/rejectAfter.js new file mode 100644 index 00000000000..4701716d38f --- /dev/null +++ b/packages/js-drive/lib/util/rejectAfter.js @@ -0,0 +1,25 @@ +/** + * Reject with @param error if @param promise is not resolved in @param ms + * + * @param {Promise} promise + * @param {Error} error + * @param {number} ms + * @return {Promise} + */ +module.exports = async function rejectAfter(promise, error, ms) { + let timeout; + let res; + try { + res = await Promise.race([ + promise, + new Promise((resolve, reject) => { + timeout = setTimeout(() => reject(error), ms); + }), + ]); + } finally { + // noinspection JSUnusedAssignment + clearTimeout(timeout); + } + + return res; +}; diff --git a/packages/js-drive/lib/util/sanitizeUrl.js b/packages/js-drive/lib/util/sanitizeUrl.js new file mode 100644 index 00000000000..d3fedd42249 --- /dev/null +++ b/packages/js-drive/lib/util/sanitizeUrl.js @@ -0,0 +1,15 @@ +function sanitizeUrl(url) { + for (let i = 0, len = url.length; i < len; i++) { + const charCode = url.charCodeAt(i); + // Some systems do not follow RFC and separate the path and query + // string with a `;` character (code 59), e.g. `/foo;jsessionid=123456`. + // Thus, we need to split on `;` as well as `?` and `#`. + if (charCode === 63 || charCode === 59 || charCode === 35) { + return url.slice(0, i); + } + } + + return url; +} + +module.exports = sanitizeUrl; diff --git a/packages/js-drive/lib/util/shuffleArray.js b/packages/js-drive/lib/util/shuffleArray.js new file mode 100644 index 00000000000..1e6c97f9102 --- /dev/null +++ b/packages/js-drive/lib/util/shuffleArray.js @@ -0,0 +1,14 @@ +/* eslint-disable no-param-reassign */ +/** + * Shuffle the given array in place + * + * @param {Array} array + * @returns {Array} + */ +module.exports = function shuffleArray(array) { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + return array; +}; diff --git a/packages/js-drive/lib/util/wait.js b/packages/js-drive/lib/util/wait.js new file mode 100644 index 00000000000..b0d45b4c5ce --- /dev/null +++ b/packages/js-drive/lib/util/wait.js @@ -0,0 +1,10 @@ +/** + * Asynchronously wait for a specified number of milliseconds. + * @param {Number} ms - Number of milliseconds to wait. + * @return {Promise} The promise to await on. + */ +async function wait(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +module.exports = wait; diff --git a/packages/js-drive/lib/validator/Validator.js b/packages/js-drive/lib/validator/Validator.js new file mode 100644 index 00000000000..daefdb63af4 --- /dev/null +++ b/packages/js-drive/lib/validator/Validator.js @@ -0,0 +1,72 @@ +const PublicKeyShareIsNotPresentError = require('./errors/PublicKeyShareIsNotPresentError'); + +class Validator { + /** + * @param {Buffer} proTxHash + * @param {ValidatorNetworkInfo} networkInfo + * @param {Buffer} [pubKeyShare] + */ + constructor(proTxHash, networkInfo, pubKeyShare = undefined) { + this.proTxHash = proTxHash; + this.networkInfo = networkInfo; + this.pubKeyShare = pubKeyShare; + } + + /** + * Get validator pro tx hash + * + * @return {Buffer} + */ + getProTxHash() { + return this.proTxHash; + } + + /** + * Get validator public key share + * @return {Buffer} + */ + getPublicKeyShare() { + return this.pubKeyShare; + } + + /** + * Get validator voting power + * + * @return {number} + */ + getVotingPower() { + return Validator.DEFAULT_DASH_VOTING_POWER; + } + + /** + * Get network info + * + * @returns {ValidatorNetworkInfo} + */ + getNetworkInfo() { + return this.networkInfo; + } + + /** + * @param {Object} member + * @param {ValidatorNetworkInfo} networkInfo + * @param {boolean} [pubKeyShareRequired=false] + * @return {Validator} + */ + static createFromQuorumMember(member, networkInfo, pubKeyShareRequired = false) { + const proTxHash = Buffer.from(member.proTxHash, 'hex'); + + let pubKeyShare; + if (member.pubKeyShare) { + pubKeyShare = Buffer.from(member.pubKeyShare, 'hex'); + } else if (pubKeyShareRequired) { + throw new PublicKeyShareIsNotPresentError(member); + } + + return new Validator(proTxHash, networkInfo, pubKeyShare); + } +} + +Validator.DEFAULT_DASH_VOTING_POWER = 100; + +module.exports = Validator; diff --git a/packages/js-drive/lib/validator/ValidatorNetworkInfo.js b/packages/js-drive/lib/validator/ValidatorNetworkInfo.js new file mode 100644 index 00000000000..91593bd4f8f --- /dev/null +++ b/packages/js-drive/lib/validator/ValidatorNetworkInfo.js @@ -0,0 +1,29 @@ +class ValidatorNetworkInfo { + /** + * + * @param {string} host + * @param {number} port + */ + constructor(host, port) { + this.host = host; + this.port = port; + } + + /** + * Get validator host + * @returns {string} + */ + getHost() { + return this.host; + } + + /** + * Get validator port + * @returns {number} + */ + getPort() { + return this.port; + } +} + +module.exports = ValidatorNetworkInfo; diff --git a/packages/js-drive/lib/validator/ValidatorSet.js b/packages/js-drive/lib/validator/ValidatorSet.js new file mode 100644 index 00000000000..e8bad8edbe3 --- /dev/null +++ b/packages/js-drive/lib/validator/ValidatorSet.js @@ -0,0 +1,172 @@ +const Validator = require('./Validator'); +const ValidatorSetIsNotInitializedError = require('./errors/ValidatorSetIsNotInitializedError'); +const ValidatorNetworkInfo = require('./ValidatorNetworkInfo'); + +class ValidatorSet { + /** + * @param {SimplifiedMasternodeList} simplifiedMasternodeList + * @param {getRandomQuorum} getRandomQuorum + * @param {fetchQuorumMembers} fetchQuorumMembers + * @param {number} validatorSetLLMQType + * @param {RpcClient} coreRpcClient + * @param {number} tenderdashP2pPort + */ + constructor( + simplifiedMasternodeList, + getRandomQuorum, + fetchQuorumMembers, + validatorSetLLMQType, + coreRpcClient, + tenderdashP2pPort, + ) { + this.simplifiedMasternodeList = simplifiedMasternodeList; + this.getRandomQuorum = getRandomQuorum; + this.fetchQuorumMembers = fetchQuorumMembers; + this.validatorSetLLMQType = validatorSetLLMQType; + this.coreRpcClient = coreRpcClient; + this.tenderdashP2pPort = tenderdashP2pPort; + + this.quorum = null; + this.validators = []; + } + + /** + * Chooses an active validator set from among all active validator quorums for the first time + * + * @param {number} coreHeight + */ + async initialize(coreHeight) { + const sml = this.simplifiedMasternodeList.getStore().getSMLbyHeight(coreHeight); + + // using the block hash at the first core height as entropy + const rotationEntropy = Buffer.from(sml.toSimplifiedMNListDiff().blockHash, 'hex'); + + await this.switchToRandomQuorum( + sml, + coreHeight, + rotationEntropy, + ); + } + + /** + * Rotates to a new active validator set from among all active validator quorums + * + * @param {Long} height + * @param {number} coreHeight + * @param {Buffer} rotationEntropy + */ + async rotate(height, coreHeight, rotationEntropy) { + const sml = this.simplifiedMasternodeList.getStore().getSMLbyHeight(coreHeight); + + // validator set is rotated every ROTATION_BLOCK_INTERVAL blocks + if (height.toNumber() % ValidatorSet.ROTATION_BLOCK_INTERVAL !== 0) { + return false; + } + + await this.switchToRandomQuorum( + sml, + coreHeight, + rotationEntropy, + ); + + return true; + } + + /** + * Get Validator Set Quorum + * + * @return {QuorumEntry} + */ + getQuorum() { + if (!this.quorum) { + throw new ValidatorSetIsNotInitializedError(); + } + + return this.quorum; + } + + /** + * Get validators + * + * @return {Validator[]} + */ + getValidators() { + if (this.validators.length === 0) { + throw new ValidatorSetIsNotInitializedError(); + } + + return this.validators; + } + + /** + * @private + * @param {SimplifiedMNList} sml + * @param {number} coreHeight + * @param {Buffer} rotationEntropy + * @return {Promise} + */ + async switchToRandomQuorum(sml, coreHeight, rotationEntropy) { + this.quorum = await this.getRandomQuorum( + sml, + this.validatorSetLLMQType, + rotationEntropy, + ); + + const quorumMembers = await this.fetchQuorumMembers( + this.validatorSetLLMQType, + this.quorum.quorumHash, + ); + + // If the node is a quorum member and doesn't receive public key share for members + // it should throw an error + let proTxHash; + + try { + ({ + result: { + proTxHash, + }, + } = await this.coreRpcClient.masternode('status')); + } catch (e) { + // This node is not a masternode + if (e.code !== -32603) { + throw e; + } + } + + const isThisNodeMember = !!quorumMembers + .find((member) => member.valid && member.proTxHash === proTxHash); + + const validMasternodesList = this.simplifiedMasternodeList + .getStore() + .getCurrentSML() + .getValidMasternodesList(); + + const masternodes = {}; + + this.validators = await Promise.all( + quorumMembers.filter((member) => { + // Ignore invalid quorum members + if (!member.valid) { + return false; + } + + // Ignore members which are not part of SML + masternodes[member.proTxHash] = validMasternodesList + .find((mnEntry) => mnEntry.proRegTxHash === member.proTxHash); + + return Boolean(masternodes[member.proTxHash]); + }).map(async (member) => { + const masternode = masternodes[member.proTxHash]; + + const networkInfo = new ValidatorNetworkInfo(masternode.getIp(), this.tenderdashP2pPort); + + return Validator.createFromQuorumMember(member, networkInfo, isThisNodeMember); + }), + ); + } +} + +ValidatorSet.ROTATION_BLOCK_INTERVAL = 15; + +module.exports = ValidatorSet; diff --git a/packages/js-drive/lib/validator/errors/PublicKeyShareIsNotPresentError.js b/packages/js-drive/lib/validator/errors/PublicKeyShareIsNotPresentError.js new file mode 100644 index 00000000000..aebcec4b46b --- /dev/null +++ b/packages/js-drive/lib/validator/errors/PublicKeyShareIsNotPresentError.js @@ -0,0 +1,23 @@ +const DriveError = require('../../errors/DriveError'); + +class PublicKeyShareIsNotPresentError extends DriveError { + /** + * @param {Object} member + */ + constructor(member) { + super('Public key share is not present for validator'); + + this.member = member; + } + + /** + * Get quorum member info + * + * @return {Object} + */ + getMember() { + return this.member; + } +} + +module.exports = PublicKeyShareIsNotPresentError; diff --git a/packages/js-drive/lib/validator/errors/ValidatorSetIsNotInitializedError.js b/packages/js-drive/lib/validator/errors/ValidatorSetIsNotInitializedError.js new file mode 100644 index 00000000000..c6a2b1c0ea7 --- /dev/null +++ b/packages/js-drive/lib/validator/errors/ValidatorSetIsNotInitializedError.js @@ -0,0 +1,9 @@ +const DriveError = require('../../errors/DriveError'); + +class ValidatorSetIsNotInitializedError extends DriveError { + constructor() { + super('Validator Set is not initialized'); + } +} + +module.exports = ValidatorSetIsNotInitializedError; diff --git a/packages/js-drive/package.json b/packages/js-drive/package.json new file mode 100644 index 00000000000..fbd52e587ff --- /dev/null +++ b/packages/js-drive/package.json @@ -0,0 +1,105 @@ +{ + "name": "@dashevo/drive", + "private": true, + "version": "0.23.0-dev.4", + "description": "Replicated state machine for Dash Platform", + "engines": { + "node": ">=12" + }, + "contributors": [ + { + "name": "Ivan Shumkov", + "email": "ivan@shumkov.ru", + "url": "https://github.com/shumkov" + }, + { + "name": "Djavid Gabibiyan", + "email": "djavid@dash.org", + "url": "https://github.com/jawid-h" + }, + { + "name": "Anton Suprunchuk", + "email": "anton.suprunchuk@dash.org", + "url": "https://github.com/antouhou" + }, + { + "name": "Konstantin Shuplenkov", + "email": "konstantin.shuplenkov@dash.org", + "url": "https://github.com/shuplenkov" + } + ], + "scripts": { + "abci": "node scripts/abci", + "lint": "eslint .", + "test": "yarn run test:coverage", + "test:coverage": "nyc --check-coverage --stmts=93 --branch=85 --funcs=90 --lines=90 yarn run mocha './test/unit/**/*.spec.js' './test/integration/**/*.spec.js'", + "test:unit": "mocha './test/unit/**/*.spec.js'", + "test:integration": "mocha './test/integration/**/*.spec.js'" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/dashevo/js-drive.git" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/dashevo/js-drive/issues" + }, + "homepage": "https://github.com/dashevo/js-drive", + "devDependencies": { + "@dashevo/dp-services-ctl": "github:dashevo/js-dp-services-ctl#v0.19-dev", + "@types/pino": "^6.3.0", + "babel-eslint": "^10.1.0", + "chai": "^4.3.4", + "chai-as-promised": "^7.1.1", + "chai-string": "^1.5.0", + "dirty-chai": "^2.0.1", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2", + "levelup": "^4.4.0", + "memdown": "^5.1.0", + "mocha": "^9.1.2", + "nyc": "^15.1.0", + "rimraf": "^3.0.2", + "sinon": "^11.1.2", + "sinon-chai": "^3.7.0" + }, + "dependencies": { + "@dashevo/abci": "~0.23.0-dev.1", + "@dashevo/dapi-grpc": "workspace:~", + "@dashevo/dashcore-lib": "~0.19.39", + "@dashevo/dashd-rpc": "^2.3.2", + "@dashevo/dashpay-contract": "workspace:~", + "@dashevo/dpns-contract": "workspace:~", + "@dashevo/dpp": "workspace:~", + "@dashevo/feature-flags-contract": "workspace:~", + "@dashevo/grpc-common": "workspace:~", + "@dashevo/masternode-reward-shares-contract": "workspace:~", + "@dashevo/rs-drive": "0.23.0-dev.5.pr.114.5", + "ajv": "^8.6.0", + "ajv-keywords": "^5.0.0", + "awilix": "^4.2.6", + "blake3": "^2.1.4", + "browserify": "^16.5.1", + "bs58": "^4.0.1", + "cbor": "^8.0.0", + "chalk": "^4.1.0", + "dotenv-expand": "^5.1.0", + "dotenv-safe": "^8.2.0", + "find-my-way": "^2.2.2", + "js-merkle": "^0.1.5", + "lodash.clonedeep": "^4.5.0", + "lodash.get": "^4.4.2", + "lodash.set": "^4.3.2", + "long": "^5.2.0", + "lru-cache": "^5.1.1", + "node-graceful": "^3.0.1", + "pino": "^6.4.0", + "pino-multi-stream": "^5.2.0", + "pino-pretty": "^4.0.3", + "rimraf": "^3.0.2", + "setimmediate": "^1.0.5", + "through2": "^3.0.1", + "zeromq": "^5.2.8" + } +} diff --git a/packages/js-drive/scripts/abci.js b/packages/js-drive/scripts/abci.js new file mode 100644 index 00000000000..4d577e27e22 --- /dev/null +++ b/packages/js-drive/scripts/abci.js @@ -0,0 +1,181 @@ +require('dotenv-expand')(require('dotenv-safe').config()); + +const graceful = require('node-graceful'); + +const chalk = require('chalk'); + +const ZMQClient = require('../lib/core/ZmqClient'); + +const createDIContainer = require('../lib/createDIContainer'); + +const { version: driveVersion } = require('../package.json'); + +const banner = '\n ____ ______ ____ __ __ ____ ____ ______ __ __ ____ \n' ++ '/\\ _`\\ /\\ _ \\ /\\ _`\\ /\\ \\/\\ \\ /\\ _`\\ /\\ _`\\ /\\__ _\\ /\\ \\/\\ \\ /\\ _`\\ \n' ++ '\\ \\ \\/\\ \\ \\ \\ \\L\\ \\ \\ \\,\\L\\_\\ \\ \\ \\_\\ \\ \\ \\ \\/\\ \\ \\ \\ \\L\\ \\ \\/_/\\ \\/ \\ \\ \\ \\ \\ \\ \\ \\L\\_\\ \n' ++ ' \\ \\ \\ \\ \\ \\ \\ __ \\ \\/_\\__ \\ \\ \\ _ \\ \\ \\ \\ \\ \\ \\ \\ , / \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ _\\L \n' ++ ' \\ \\ \\_\\ \\ \\ \\ \\/\\ \\ /\\ \\L\\ \\ \\ \\ \\ \\ \\ \\ \\ \\_\\ \\ \\ \\ \\\\ \\ \\_\\ \\__ \\ \\ \\_/ \\ \\ \\ \\L\\ \\\n' ++ ' \\ \\____/ \\ \\_\\ \\_\\ \\ `\\____\\ \\ \\_\\ \\_\\ \\ \\____/ \\ \\_\\ \\_\\ /\\_____\\ \\ `\\___/ \\ \\____/\n' ++ ' \\/___/ \\/_/\\/_/ \\/_____/ \\/_/\\/_/ \\/___/ \\/_/\\/ / \\/_____/ `\\/__/ \\/___/\n\n\n'; + +// eslint-disable-next-line no-console +console.log(chalk.hex('#008de4')(banner)); + +(async function main() { + const container = createDIContainer(process.env); + const logger = container.resolve('logger'); + const dpp = container.resolve('dpp'); + const transactionalDpp = container.resolve('transactionalDpp'); + const errorHandler = container.resolve('errorHandler'); + const latestProtocolVersion = container.resolve('latestProtocolVersion'); + const closeAbciServer = container.resolve('closeAbciServer'); + + logger.info(`Starting Drive ABCI application v${driveVersion} (latest protocol v${latestProtocolVersion})`); + + /** + * Ensure graceful shutdown + */ + + process + .on('unhandledRejection', errorHandler) + .on('uncaughtException', errorHandler); + + graceful.DEADLY_SIGNALS.push('SIGQUIT'); + + graceful.on('exit', async (signal) => { + logger.info({ signal }, `Received ${signal}. Stopping Drive ABCI application...`); + + await closeAbciServer(); + + await container.dispose(); + }); + + /** + * Initialize DPP + */ + + await dpp.initialize(); + await transactionalDpp.initialize(); + + /** + * Make sure Core is synced + */ + + const network = container.resolve('network'); + + logger.info(`Connecting to Core in ${network} network...`); + + const waitForCoreSync = container.resolve('waitForCoreSync'); + await waitForCoreSync((currentBlockHeight, currentHeaderNumber) => { + let message = `waiting for core to finish sync ${currentBlockHeight}/${currentHeaderNumber}...`; + + if (currentBlockHeight === 0 && currentHeaderNumber === 0) { + message = 'waiting for core to connect to peers...'; + } + + logger.info(message); + }); + + /** + * Connect to Core ZMQ socket + */ + + const coreZMQClient = container.resolve('coreZMQClient'); + + coreZMQClient.on(ZMQClient.events.CONNECTED, () => { + logger.debug('Connected to core ZMQ socket'); + }); + + coreZMQClient.on(ZMQClient.events.DISCONNECTED, () => { + logger.debug('Disconnected from core ZMQ socket'); + }); + + coreZMQClient.on(ZMQClient.events.MAX_RETRIES_REACHED, async () => { + const error = new Error('Can\'t connect to core ZMQ'); + + await errorHandler(error); + }); + + try { + await coreZMQClient.start(); + } catch (e) { + const error = new Error(`Can't connect to core ZMQ socket: ${e.message}`); + + await errorHandler(error); + } + + /** + * Obtain chain lock + */ + + logger.info('Obtaining the latest chain lock...'); + + const waitForCoreChainLockSync = container.resolve('waitForCoreChainLockSync'); + await waitForCoreChainLockSync(); + + /** + * Wait for initial core chain locked height + */ + const initialCoreChainLockedHeight = container.resolve('initialCoreChainLockedHeight'); + + logger.info(`Waiting for initial core chain locked height #${initialCoreChainLockedHeight}...`); + + const waitForChainLockedHeight = container.resolve('waitForChainLockedHeight'); + await waitForChainLockedHeight(initialCoreChainLockedHeight); + + /** + * Start ABCI server + */ + + const abciServer = container.resolve('abciServer'); + + abciServer.on('connection', (socket) => { + logger.debug( + { + abciConnectionId: socket.connection.id, + }, + `Accepted new ABCI connection #${socket.connection.id} from ${socket.remoteAddress}:${socket.remotePort}`, + ); + + socket.on('error', (e) => { + logger.error( + { + err: e, + abciConnectionId: socket.connection.id, + }, + `ABCI connection #${socket.connection.id} error: ${e.message}`, + ); + }); + + socket.once('close', (hasError) => { + let message = `ABCI connection #${socket.connection.id} is closed`; + if (hasError) { + message += ' with error'; + } + + logger.debug( + { + abciConnectionId: socket.connection.id, + }, + message, + ); + }); + }); + + abciServer.once('close', () => { + logger.info('ABCI server and all connections are closed'); + }); + + abciServer.on('error', async (e) => { + await errorHandler(e); + }); + + abciServer.on('listening', () => { + logger.info(`ABCI server is waiting for connection on port ${container.resolve('abciPort')}`); + }); + + abciServer.listen( + container.resolve('abciPort'), + container.resolve('abciHost'), + ); +}()); diff --git a/packages/js-drive/test/.eslintrc b/packages/js-drive/test/.eslintrc new file mode 100644 index 00000000000..720ced73852 --- /dev/null +++ b/packages/js-drive/test/.eslintrc @@ -0,0 +1,12 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + }, + "globals": { + "expect": true + } +} diff --git a/packages/js-drive/test/README.md b/packages/js-drive/test/README.md new file mode 100644 index 00000000000..ce3a86a76af --- /dev/null +++ b/packages/js-drive/test/README.md @@ -0,0 +1,54 @@ +# Drive Tests + +We believe in [Test Pyramid](http://verraes.net/2015/01/economy-of-tests/). + +## Structure + + - `integration/` - [Integration tests](https://en.wikipedia.org/wiki/Integration_testing) + - `unit/` - [Unit tests](https://en.wikipedia.org/wiki/Unit_testing) + +A subsequent paths the same as the structure of the code in the [lib/](../lib) directory. + +## How to run tests + +Run all tests: + +```bash +npm test +``` + +Run unit tests: + +```bash +npm run test:unit +``` + +Run integration tests: + +```bash +npm run test:integration +``` + +## How to write tests + +We use: + - [Mocha](https://mochajs.org) as testing framework + - [Sinon.JS](http://sinonjs.org/) for stubs and spies + - [Chai](http://chaijs.com/) with several plugins for assertions: + - [Sinon Chai](https://github.com/domenic/sinon-chai) for Sinon.JS assertions + - [Chai as promised](https://github.com/domenic/chai-as-promised) for assertions about promises + - [Dirty Chai](https://github.com/prodatakey/dirty-chai) for lint-friendly terminating assertions + +We prefer `expect` assertions syntax instead of `should`. + +All tools are [bootstrapped](../lib/test/bootstrap.js) before tests: + - `expect` function is available in global context + - Sinon sandbox is created before each test and available as `this.sinon` property in the test's context + - Envs from `.env` are loaded before all tests + +## Evolution helpers +We use [js-evo-services-ctl](https://github.com/dashevo/js-evo-services-ctl) library to manipulate Evolution's services. + +## Other tools + +You may find other useful tools for testing in [lib/test](../lib/test) directory. diff --git a/packages/js-drive/test/integration/abci/handlers/queryHandlerFactory.spec.js b/packages/js-drive/test/integration/abci/handlers/queryHandlerFactory.spec.js new file mode 100644 index 00000000000..a5f55f9405d --- /dev/null +++ b/packages/js-drive/test/integration/abci/handlers/queryHandlerFactory.spec.js @@ -0,0 +1,149 @@ +const cbor = require('cbor'); + +const { + asValue, +} = require('awilix'); + +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); + +const createTestDIContainer = require('../../../../lib/test/createTestDIContainer'); +const InvalidArgumentAbciError = require('../../../../lib/abci/errors/InvalidArgumentAbciError'); + +describe('queryHandlerFactory', function main() { + this.timeout(90000); + + let container; + let queryHandler; + let identityQueryHandlerMock; + let dataContractQueryHandlerMock; + let documentQueryHandlerMock; + let dataContract; + let documents; + let identity; + let proof; + + beforeEach(async function beforeEach() { + proof = Buffer.from('GbYYWuLCU6u7nb4pdnMM1uzAeURhE7ZPxGqAbUARBsb3', 'hex'); + + container = await createTestDIContainer(); + + dataContract = getDataContractFixture(); + documents = getDocumentsFixture(dataContract); + identity = getIdentityFixture(); + + identityQueryHandlerMock = this.sinon.stub(); + identityQueryHandlerMock.resolves({ + value: identity, + proof, + }); + + dataContractQueryHandlerMock = this.sinon.stub(); + dataContractQueryHandlerMock.resolves(dataContract); + + documentQueryHandlerMock = this.sinon.stub(); + documentQueryHandlerMock.resolves(documents); + + container.register('identityQueryHandler', asValue(identityQueryHandlerMock)); + container.register('dataContractQueryHandler', asValue(dataContractQueryHandlerMock)); + container.register('documentQueryHandler', asValue(documentQueryHandlerMock)); + + queryHandler = container.resolve('queryHandler'); + }); + + afterEach(async () => { + if (container) { + await container.dispose(); + } + }); + + describe('/identities', () => { + it('should call identity handler and return an identity with proof', async () => { + const result = await queryHandler({ + path: '/identities', + data: cbor.encode({ + id: 1, + }), + prove: 'true', + }); + + expect(identityQueryHandlerMock).to.have.been.calledOnceWithExactly( + {}, + { id: 1 }, + { + path: '/identities', + data: cbor.encode({ + id: 1, + }), + prove: 'true', + }, + ); + + expect(result).to.deep.equal({ + value: identity, + proof, + }); + }); + }); + + describe('/dataContracts', () => { + it('should call data contract handler and return data contract', async () => { + const result = await queryHandler({ + path: '/dataContracts', + data: cbor.encode({ + id: 1, + }), + }); + + expect(dataContractQueryHandlerMock).to.have.been.calledOnceWithExactly( + {}, + { id: 1 }, + { + path: '/dataContracts', + data: cbor.encode({ + id: 1, + }), + }, + ); + expect(result).to.deep.equal(dataContract); + }); + }); + + describe('/dataContracts/documents', () => { + it('should call documents handler and return documents', async () => { + const result = await queryHandler({ + path: '/dataContracts/documents', + data: cbor.encode({ + contractId: 1, + type: 'someType', + }), + }); + + expect(documentQueryHandlerMock).to.have.been.calledOnceWithExactly( + {}, + { contractId: 1, type: 'someType' }, + { + path: '/dataContracts/documents', + data: cbor.encode({ + contractId: 1, + type: 'someType', + }), + }, + ); + expect(result).to.deep.equal(documents); + }); + }); + + it('should throw an error if invalid path is submitted', async () => { + try { + await queryHandler({ + path: '/unknownPath', + data: Buffer.alloc(0), + }); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidArgumentAbciError); + expect(e.getMessage()).to.equal('Invalid path'); + } + }); +}); diff --git a/packages/js-drive/test/integration/blockExecution/BlockExecutionContextStackRepository.spec.js b/packages/js-drive/test/integration/blockExecution/BlockExecutionContextStackRepository.spec.js new file mode 100644 index 00000000000..9de7efb9c9e --- /dev/null +++ b/packages/js-drive/test/integration/blockExecution/BlockExecutionContextStackRepository.spec.js @@ -0,0 +1,218 @@ +const rimraf = require('rimraf'); + +const cbor = require('cbor'); + +const Drive = require('@dashevo/rs-drive'); +const BlockExecutionContextStackRepository = require('../../../lib/blockExecution/BlockExecutionContextStackRepository'); +const BlockExecutionContext = require('../../../lib/blockExecution/BlockExecutionContext'); + +const getBlockExecutionContextObjectFixture = require('../../../lib/test/fixtures/getBlockExecutionContextObjectFixture'); +const BlockExecutionContextStack = require('../../../lib/blockExecution/BlockExecutionContextStack'); +const GroveDBStore = require('../../../lib/storage/GroveDBStore'); +const logger = require('../../../lib/util/noopLogger'); + +function removeConsensusLogger(blockExecutionContextStack) { + const plainContexts = blockExecutionContextStack.getContexts() + .map((context) => context.toObject({ + skipConsensusLogger: true, + })); + + return plainContexts.map((rawContext) => { + const context = new BlockExecutionContext(); + + context.fromObject(rawContext); + + return context; + }); +} + +describe('BlockExecutionContextStackRepository', () => { + let rsDrive; + let store; + let repository; + let blockExecutionContextObject; + let blockExecutionContext; + let blockExecutionContextStack; + + beforeEach(async () => { + rsDrive = new Drive('./db/grovedb_test'); + store = new GroveDBStore(rsDrive, logger, 'blockchainStateTestStore'); + + repository = new BlockExecutionContextStackRepository(store); + + blockExecutionContextObject = getBlockExecutionContextObjectFixture(); + + blockExecutionContext = new BlockExecutionContext(); + blockExecutionContext.fromObject(blockExecutionContextObject); + + blockExecutionContextStack = new BlockExecutionContextStack(); + blockExecutionContextStack.setContexts([ + blockExecutionContext, + blockExecutionContext, + ]); + }); + + afterEach(async () => { + await rsDrive.close(); + rimraf.sync('./db/grovedb_test'); + }); + + describe('#store', () => { + it('should store block execution context stack', async () => { + const result = await repository.store( + blockExecutionContextStack, + ); + + expect(result).to.equal(repository); + + const storedContextsBufferResult = await store.getAux( + BlockExecutionContextStackRepository.EXTERNAL_STORE_KEY_NAME, + ); + + const storedContextsBuffer = storedContextsBufferResult.getValue(); + + expect(storedContextsBuffer).to.be.instanceOf(Buffer); + + const storedContexts = cbor.decode(storedContextsBuffer); + + expect(storedContexts).to.deep.equals( + blockExecutionContextStack.getContexts() + .map((context) => context.toObject({ + skipDBTransaction: true, + skipConsensusLogger: true, + })), + ); + }); + + it('should store block execution context stack using transaction', async () => { + await store.startTransaction(); + + await repository.store( + blockExecutionContextStack, + { + useTransaction: true, + }, + ); + + const notFoundDataResult = await store.getAux( + BlockExecutionContextStackRepository.EXTERNAL_STORE_KEY_NAME, + { useTransaction: false }, + ); + + const notFoundData = notFoundDataResult.getValue(); + + expect(notFoundData).to.be.null(); + + const dataFromTransactionResult = await store.getAux( + BlockExecutionContextStackRepository.EXTERNAL_STORE_KEY_NAME, + { useTransaction: true }, + ); + + const dataFromTransaction = dataFromTransactionResult.getValue(); + + expect(dataFromTransaction).to.be.instanceOf(Buffer); + + let storedContexts = cbor.decode(dataFromTransaction); + + expect(storedContexts).to.deep.equals( + blockExecutionContextStack.getContexts() + .map((context) => context.toObject({ + skipDBTransaction: true, + skipConsensusLogger: true, + })), + ); + + await store.commitTransaction(); + + const committedDataResult = await store.getAux( + BlockExecutionContextStackRepository.EXTERNAL_STORE_KEY_NAME, + ); + + const committedData = committedDataResult.getValue(); + + expect(committedData).to.be.instanceOf(Buffer); + + storedContexts = cbor.decode(committedData); + + expect(storedContexts).to.deep.equals( + blockExecutionContextStack.getContexts() + .map((context) => context.toObject({ + skipDBTransaction: true, + skipConsensusLogger: true, + })), + ); + }); + }); + + describe('#fetch', () => { + it('should return empty block execution context stack if it is not stored', async () => { + const storedContext = await repository.fetch(); + + expect(storedContext).to.be.instanceOf(BlockExecutionContextStack); + expect(storedContext.getSize()).to.equals(0); + }); + + it('should return stored block execution context', async () => { + const plainContexts = blockExecutionContextStack.getContexts() + .map((context) => context.toObject({ + skipConsensusLogger: true, + })); + + const storedStackBuffer = cbor.encode(plainContexts); + + await store.putAux( + BlockExecutionContextStackRepository.EXTERNAL_STORE_KEY_NAME, + storedStackBuffer, + ); + + const storedStack = await repository.fetch(); + + expect(storedStack).to.be.instanceOf(BlockExecutionContextStack); + + const blockExecutionContexts = removeConsensusLogger(blockExecutionContextStack); + + expect(storedStack.getContexts()).to.deep.equal(blockExecutionContexts); + }); + + it('should return stored block execution context using transaction', async () => { + await store.startTransaction(); + + const plainContexts = blockExecutionContextStack.getContexts() + .map((context) => context.toObject({ + skipConsensusLogger: true, + })); + + const storedStackBuffer = cbor.encode(plainContexts); + + await store.putAux( + BlockExecutionContextStackRepository.EXTERNAL_STORE_KEY_NAME, + storedStackBuffer, + { useTransaction: true }, + ); + + let storedStack = await repository.fetch({ + useTransaction: false, + }); + + expect(storedStack.getContexts()).to.deep.equal([]); + + storedStack = await repository.fetch({ + useTransaction: true, + }); + + let blockExecutionContexts = removeConsensusLogger(blockExecutionContextStack); + + expect(storedStack.getContexts()).to.deep.equal(blockExecutionContexts); + + await store.commitTransaction(); + + storedStack = await repository.fetch({ + useTransaction: true, + }); + + blockExecutionContexts = removeConsensusLogger(blockExecutionContextStack); + + expect(storedStack.getContexts()).to.deep.equal(blockExecutionContexts); + }); + }); +}); diff --git a/packages/js-drive/test/integration/core/SimplifiedMasternodeList.spec.js b/packages/js-drive/test/integration/core/SimplifiedMasternodeList.spec.js new file mode 100644 index 00000000000..b9794d449e4 --- /dev/null +++ b/packages/js-drive/test/integration/core/SimplifiedMasternodeList.spec.js @@ -0,0 +1,103 @@ +const getSmlFixture = require('../../../lib/test/fixtures/getSmlFixture'); +const SimplifiedMasternodeList = require('../../../lib/core/SimplifiedMasternodeList'); + +describe('SimplifiedMasternodeList', function SimplifiedMasternodeListTest() { + let simplifiedMasternodeList; + let smlMaxListsLimit; + let initialSmlDiffs; + let updatedSmlDiffs; + + this.timeout(10000); + + beforeEach(() => { + simplifiedMasternodeList = new SimplifiedMasternodeList({ + smlMaxListsLimit, + }); + + initialSmlDiffs = getSmlFixture().slice(0, 16); + updatedSmlDiffs = getSmlFixture().slice(16, 17); + }); + + it('should set options', async () => { + expect(simplifiedMasternodeList.options).to.deep.equal({ maxListsLimit: smlMaxListsLimit }); + }); + + describe('#applyDiffs', () => { + it('should create simplifiedMNList', async () => { + let simplifiedMNList = simplifiedMasternodeList.getStore(); + + expect(simplifiedMNList).to.deep.equal(undefined); + + simplifiedMasternodeList.applyDiffs(initialSmlDiffs); + + simplifiedMNList = simplifiedMasternodeList.getStore(); + + expect(simplifiedMNList.baseSimplifiedMNList.baseBlockHash).to.equal( + initialSmlDiffs[0].baseBlockHash, + ); + expect(simplifiedMNList.baseSimplifiedMNList.blockHash).to.equal( + initialSmlDiffs[0].blockHash, + ); + expect(simplifiedMNList.currentSML.baseBlockHash).to.equal( + initialSmlDiffs[0].baseBlockHash, + ); + expect(simplifiedMNList.currentSML.blockHash).to.equal( + initialSmlDiffs[initialSmlDiffs.length - 1].blockHash, + ); + }); + + it('should add diff to simplifiedMNList', async () => { + let simplifiedMNList = simplifiedMasternodeList.getStore(); + + expect(simplifiedMNList).to.deep.equal(undefined); + + simplifiedMasternodeList.applyDiffs(initialSmlDiffs); + + simplifiedMNList = simplifiedMasternodeList.getStore(); + + expect(simplifiedMNList.baseSimplifiedMNList.baseBlockHash).to.equal( + initialSmlDiffs[0].baseBlockHash, + ); + expect(simplifiedMNList.baseSimplifiedMNList.blockHash).to.equal( + initialSmlDiffs[0].blockHash, + ); + expect(simplifiedMNList.currentSML.baseBlockHash).to.equal( + initialSmlDiffs[0].baseBlockHash, + ); + expect(simplifiedMNList.currentSML.blockHash).to.equal( + initialSmlDiffs[initialSmlDiffs.length - 1].blockHash, + ); + + simplifiedMasternodeList.applyDiffs(updatedSmlDiffs); + + simplifiedMNList = simplifiedMasternodeList.getStore(); + + expect(simplifiedMNList.baseSimplifiedMNList.baseBlockHash).to.equal( + initialSmlDiffs[0].baseBlockHash, + ); + expect(simplifiedMNList.baseSimplifiedMNList.blockHash).to.equal( + initialSmlDiffs[0].blockHash, + ); + expect(simplifiedMNList.currentSML.baseBlockHash).to.equal( + initialSmlDiffs[0].baseBlockHash, + ); + expect(simplifiedMNList.currentSML.blockHash).to.equal( + updatedSmlDiffs[0].blockHash, + ); + }); + }); + + describe('#getStore', () => { + it('should return simplifiedMNList', async () => { + let simplifiedMNList = simplifiedMasternodeList.getStore(); + + expect(simplifiedMNList).to.deep.equal(undefined); + + simplifiedMasternodeList.applyDiffs(initialSmlDiffs); + + simplifiedMNList = simplifiedMasternodeList.getStore(); + + expect(simplifiedMNList).to.deep.equal(simplifiedMasternodeList.store); + }); + }); +}); diff --git a/packages/js-drive/test/integration/core/updateSimplifiedMasternodeListFactory.spec.js b/packages/js-drive/test/integration/core/updateSimplifiedMasternodeListFactory.spec.js new file mode 100644 index 00000000000..d6ede8ea854 --- /dev/null +++ b/packages/js-drive/test/integration/core/updateSimplifiedMasternodeListFactory.spec.js @@ -0,0 +1,84 @@ +const { startDashCore } = require('@dashevo/dp-services-ctl'); +const SimplifiedMNListStore = require('@dashevo/dashcore-lib/lib/deterministicmnlist/SimplifiedMNListStore'); + +const createTestDIContainer = require('../../../lib/test/createTestDIContainer'); + +describe('updateSimplifiedMasternodeListFactory', function main() { + this.timeout(190000); + + let container; + let dashCore; + + after(async () => { + if (dashCore) { + await dashCore.remove(); + } + }); + + afterEach(async () => { + if (container) { + await container.dispose(); + } + }); + + it('should wait until SML will be retrieved', async () => { + dashCore = await startDashCore(); + + container = await createTestDIContainer(dashCore); + + const simplifiedMasternodeList = container.resolve('simplifiedMasternodeList'); + + expect(simplifiedMasternodeList.getStore()).to.equal(undefined); + + const { result: randomAddress } = await dashCore.getApi().getNewAddress(); + + await dashCore.getApi().generateToAddress(1000, randomAddress); + + const updateSimplifiedMasternodeList = container.resolve('updateSimplifiedMasternodeList'); + + await updateSimplifiedMasternodeList(1000); + + expect(simplifiedMasternodeList.getStore()) + .to.be.an.instanceOf(SimplifiedMNListStore); + }); + + it('should synchronizeMasternodeIdentities by smlMaxListsLimit number of blocks', async () => { + dashCore = await startDashCore(); + + container = await createTestDIContainer(dashCore); + + const simplifiedMasternodeList = container.resolve('simplifiedMasternodeList'); + const updateSimplifiedMasternodeList = container.resolve('updateSimplifiedMasternodeList'); + const synchronizeMasternodeIdentities = container.resolve('synchronizeMasternodeIdentities'); + const smlMaxListsLimit = container.resolve('smlMaxListsLimit'); + + const api = dashCore.getApi(); + const { result: randomAddress } = await api.getNewAddress(); + + await api.generateToAddress(600, randomAddress); + + let blockNumber = 500; + + await updateSimplifiedMasternodeList(blockNumber); + await synchronizeMasternodeIdentities(blockNumber); + + expect(simplifiedMasternodeList.getStore()) + .to.be.an.instanceOf(SimplifiedMNListStore); + + blockNumber += smlMaxListsLimit; + + await updateSimplifiedMasternodeList(blockNumber); + await synchronizeMasternodeIdentities(blockNumber); + + expect(simplifiedMasternodeList.getStore()) + .to.be.an.instanceOf(SimplifiedMNListStore); + + blockNumber += smlMaxListsLimit; + + await updateSimplifiedMasternodeList(blockNumber); + await synchronizeMasternodeIdentities(blockNumber); + + expect(simplifiedMasternodeList.getStore()) + .to.be.an.instanceOf(SimplifiedMNListStore); + }); +}); diff --git a/packages/js-drive/test/integration/core/waitForCoreSyncFactory.spec.js b/packages/js-drive/test/integration/core/waitForCoreSyncFactory.spec.js new file mode 100644 index 00000000000..a7a94ef64af --- /dev/null +++ b/packages/js-drive/test/integration/core/waitForCoreSyncFactory.spec.js @@ -0,0 +1,54 @@ +const { startDashCore } = require('@dashevo/dp-services-ctl'); + +const createTestDIContainer = require('../../../lib/test/createTestDIContainer'); + +describe('waitForCoreSyncFactory', function main() { + this.timeout(90000); + + let firstDashCore; + let secondDashCore; + let container; + let waitForCoreSync; + + after(async () => { + if (firstDashCore) { + await firstDashCore.remove(); + } + + if (secondDashCore) { + await secondDashCore.remove(); + } + }); + + afterEach(async () => { + if (container) { + await container.dispose(); + } + }); + + it('should wait until Dash Core in regtest mode with peers is synced', async () => { + firstDashCore = await startDashCore(); + const { result: randomAddress } = await firstDashCore.getApi().getNewAddress(); + await firstDashCore.getApi().generateToAddress(1000, randomAddress); + + secondDashCore = await startDashCore(); + await secondDashCore.connect(firstDashCore); + + container = await createTestDIContainer(secondDashCore); + waitForCoreSync = container.resolve('waitForCoreSync'); + + await waitForCoreSync(() => {}); + + const secondApi = secondDashCore.getApi(); + + const { + result: { + blocks: currentBlockHeight, + headers: currentHeadersNumber, + }, + } = await secondApi.getBlockchainInfo(); + + expect(currentHeadersNumber).to.equal(1000); + expect(currentBlockHeight).to.equal(1000); + }); +}); diff --git a/packages/js-drive/test/integration/createDIContainer.spec.js b/packages/js-drive/test/integration/createDIContainer.spec.js new file mode 100644 index 00000000000..f2c744d1626 --- /dev/null +++ b/packages/js-drive/test/integration/createDIContainer.spec.js @@ -0,0 +1,37 @@ +const { expect } = require('chai'); + +const createTestDIContainer = require('../../lib/test/createTestDIContainer'); + +describe('createDIContainer', function describeContainer() { + this.timeout(25000); + + let container; + + beforeEach(async () => { + container = await createTestDIContainer(); + }); + + afterEach(async () => { + if (container) { + await container.dispose(); + } + }); + + it('should create DI container', async () => { + expect(container).to.respondTo('register'); + expect(container).to.respondTo('resolve'); + }); + + describe('container', () => { + it('should resolve abciHandlers', () => { + const abciHandlers = container.resolve('abciHandlers'); + + expect(abciHandlers).to.have.property('info'); + expect(abciHandlers).to.have.property('checkTx'); + expect(abciHandlers).to.have.property('beginBlock'); + expect(abciHandlers).to.have.property('deliverTx'); + expect(abciHandlers).to.have.property('commit'); + expect(abciHandlers).to.have.property('query'); + }); + }); +}); diff --git a/packages/js-drive/test/integration/creditsDistributionPool/CreditsDistributionPoolRepository.spec.js b/packages/js-drive/test/integration/creditsDistributionPool/CreditsDistributionPoolRepository.spec.js new file mode 100644 index 00000000000..b6c7d50dce1 --- /dev/null +++ b/packages/js-drive/test/integration/creditsDistributionPool/CreditsDistributionPoolRepository.spec.js @@ -0,0 +1,189 @@ +const fs = require('fs'); +const cbor = require('cbor'); +const Drive = require('@dashevo/rs-drive'); +const GroveDBStore = require('../../../lib/storage/GroveDBStore'); +const CreditsDistributionPoolRepository = require('../../../lib/creditsDistributionPool/CreditsDistributionPoolRepository'); +const CreditsDistributionPool = require('../../../lib/creditsDistributionPool/CreditsDistributionPool'); +const logger = require('../../../lib/util/noopLogger'); +const StorageResult = require('../../../lib/storage/StorageResult'); + +describe('CreditsDistributionPoolRepository', () => { + let rsDrive; + let store; + let repository; + let creditsDistributionPool; + let amount; + + beforeEach(async () => { + rsDrive = new Drive('./db/grovedb_test'); + store = new GroveDBStore(rsDrive, logger, 'creditsDistributionPoolTestStore'); + + await store.createTree([], CreditsDistributionPoolRepository.PATH[0]); + + repository = new CreditsDistributionPoolRepository(store); + + amount = 42; + + creditsDistributionPool = new CreditsDistributionPool(amount); + }); + + afterEach(async () => { + await rsDrive.close(); + + fs.rmSync('./db/grovedb_test', { recursive: true }); + }); + + describe('#store', () => { + it('should store creditsDistributionPool', async () => { + const result = await repository.store(creditsDistributionPool); + + expect(result).to.be.instanceOf(StorageResult); + + expect(result.getOperations().length).to.be.greaterThan(0); + + const storedCreditsDistributionPoolBufferResult = await store.get( + CreditsDistributionPoolRepository.PATH, + CreditsDistributionPoolRepository.KEY, + ); + + const storedCreditsDistributionPoolBuffer = storedCreditsDistributionPoolBufferResult + .getValue(); + + expect(storedCreditsDistributionPoolBuffer).to.be.instanceOf(Buffer); + + const storedCreditsDistributionPool = cbor.decode( + storedCreditsDistributionPoolBuffer, + ); + + expect(storedCreditsDistributionPool.amount).to.equal(amount); + }); + + it('should store creditsDistributionPool using transaction', async () => { + await store.startTransaction(); + + const result = await repository.store(creditsDistributionPool, { + useTransaction: true, + }); + + expect(result).to.be.instanceOf(StorageResult); + + expect(result.getOperations().length).to.be.greaterThan(0); + + const notFoundDataResult = await store.get( + CreditsDistributionPoolRepository.PATH, + CreditsDistributionPoolRepository.KEY, + ); + + expect(notFoundDataResult.getValue()).to.be.null(); + + const dataFromTransactionResult = await store.get( + CreditsDistributionPoolRepository.PATH, + CreditsDistributionPoolRepository.KEY, + { useTransaction: true }, + ); + + const dataFromTransaction = cbor.decode(dataFromTransactionResult.getValue()); + + expect(dataFromTransaction.amount).to.equal(amount); + + await store.commitTransaction(); + + const committedDataResult = await store.get( + CreditsDistributionPoolRepository.PATH, + CreditsDistributionPoolRepository.KEY, + ); + + const committedData = cbor.decode(committedDataResult.getValue()); + + expect(committedData.amount).to.equal(amount); + }); + }); + + describe('#fetch', () => { + it('should fetch empty CreditsDistributionPool', async () => { + const result = await repository.fetch(); + + expect(result).to.be.instanceOf(StorageResult); + + expect(result.getOperations().length).to.be.greaterThan(0); + + const fetchedCreditsDistributionPool = result.getValue(); + + expect(fetchedCreditsDistributionPool).to.be.instanceOf( + CreditsDistributionPool, + ); + + expect(fetchedCreditsDistributionPool.getAmount()).to.equals(0); + }); + + it('should fetch stored CreditsDistributionPool', async () => { + await store.put( + CreditsDistributionPoolRepository.PATH, + CreditsDistributionPoolRepository.KEY, + cbor.encodeCanonical( + creditsDistributionPool.toJSON(), + ), + ); + + const result = await repository.fetch(); + + expect(result).to.be.instanceOf(StorageResult); + + expect(result.getOperations().length).to.be.greaterThan(0); + + const storedCreditsDistributionPool = result.getValue(); + + expect(storedCreditsDistributionPool).to.be.instanceOf(CreditsDistributionPool); + expect(storedCreditsDistributionPool.getAmount()).to.equals(amount); + }); + + it('should fetch stored CreditsDistributionPool using transaction', async () => { + await store.startTransaction(); + + await store.put( + CreditsDistributionPoolRepository.PATH, + CreditsDistributionPoolRepository.KEY, + cbor.encodeCanonical( + creditsDistributionPool.toJSON(), + ), + { useTransaction: true }, + ); + + // Nothing without transaction + const emptyResult = await repository.fetch({ + useTransaction: false, + }); + + expect(emptyResult).to.be.instanceOf(StorageResult); + + expect(emptyResult.getOperations().length).to.be.greaterThan(0); + + const emptyPool = emptyResult.getValue(); + + expect(emptyPool).to.be.instanceOf(CreditsDistributionPool); + expect(emptyPool.getAmount()).to.equals(0); + + // Actual amount in transactions + const transactionalResult = await repository.fetch({ + useTransaction: true, + }); + + const transactionalPool = transactionalResult.getValue(); + + expect(transactionalPool).to.be.instanceOf(CreditsDistributionPool); + expect(transactionalPool.getAmount()).to.equals(amount); + + await store.commitTransaction(); + + // Actual amount without transaction + const committedResults = await repository.fetch({ + useTransaction: false, + }); + + const committedPool = committedResults.getValue(); + + expect(committedPool).to.be.instanceOf(CreditsDistributionPool); + expect(committedPool.getAmount()).to.equals(amount); + }); + }); +}); diff --git a/packages/js-drive/test/integration/dataContract/DataContractStoreRepository.spec.js b/packages/js-drive/test/integration/dataContract/DataContractStoreRepository.spec.js new file mode 100644 index 00000000000..9e9bcf537c4 --- /dev/null +++ b/packages/js-drive/test/integration/dataContract/DataContractStoreRepository.spec.js @@ -0,0 +1,368 @@ +const rimraf = require('rimraf'); +const Drive = require('@dashevo/rs-drive'); +const decodeProtocolEntityFactory = require('@dashevo/dpp/lib/decodeProtocolEntityFactory'); +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const DataContract = require('@dashevo/dpp/lib/dataContract/DataContract'); +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); +const GroveDBStore = require('../../../lib/storage/GroveDBStore'); +const DataContractStoreRepository = require('../../../lib/dataContract/DataContractStoreRepository'); +const noopLogger = require('../../../lib/util/noopLogger'); +const StorageResult = require('../../../lib/storage/StorageResult'); + +describe('DataContractStoreRepository', () => { + let rsDrive; + let store; + let repository; + let decodeProtocolEntity; + let dataContract; + + beforeEach(async () => { + rsDrive = new Drive('./db/grovedb_test'); + store = new GroveDBStore(rsDrive, noopLogger); + + decodeProtocolEntity = decodeProtocolEntityFactory(); + + repository = new DataContractStoreRepository(store, decodeProtocolEntity, noopLogger); + + dataContract = getDataContractFixture(); + }); + + afterEach(async () => { + await rsDrive.close(); + rimraf.sync('./db/grovedb_test'); + }); + + describe('#store', () => { + beforeEach(async () => { + await store.createTree([], DataContractStoreRepository.TREE_PATH[0]); + }); + + it('should store Data Contract', async () => { + const result = await repository.store( + dataContract, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const encodedDataContractResult = await store.get( + DataContractStoreRepository.TREE_PATH.concat([dataContract.getId().toBuffer()]), + DataContractStoreRepository.DATA_CONTRACT_KEY, + ); + + const [protocolVersion, rawDataContract] = decodeProtocolEntity( + encodedDataContractResult.getValue(), + ); + + rawDataContract.protocolVersion = protocolVersion; + + const fetchedDataContract = new DataContract(rawDataContract); + + expect(dataContract.toObject()).to.deep.equal(fetchedDataContract.toObject()); + }); + + it('should store Data Contract using transaction', async () => { + await store.startTransaction(); + + const result = await repository.store( + dataContract, + { useTransaction: true }, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const notFoundDataContractResult = await store.get( + DataContractStoreRepository.TREE_PATH.concat([dataContract.getId().toBuffer()]), + DataContractStoreRepository.DATA_CONTRACT_KEY, + { useTransaction: false }, + ); + + expect(notFoundDataContractResult.getValue()).to.be.null(); + + const dataFromTransactionResult = await store.get( + DataContractStoreRepository.TREE_PATH.concat([dataContract.getId().toBuffer()]), + DataContractStoreRepository.DATA_CONTRACT_KEY, + { useTransaction: true }, + ); + + let [protocolVersion, rawDataContract] = decodeProtocolEntity( + dataFromTransactionResult.getValue(), + ); + + rawDataContract.protocolVersion = protocolVersion; + + const fetchedDataContract = new DataContract(rawDataContract); + + expect(dataContract.toObject()).to.deep.equal(fetchedDataContract.toObject()); + + await store.commitTransaction(); + + const committedDataResult = await store.get( + DataContractStoreRepository.TREE_PATH.concat([dataContract.getId().toBuffer()]), + DataContractStoreRepository.DATA_CONTRACT_KEY, + { useTransaction: true }, + ); + + [protocolVersion, rawDataContract] = decodeProtocolEntity(committedDataResult.getValue()); + + rawDataContract.protocolVersion = protocolVersion; + + const fetchedOneMoreDataContract = new DataContract(rawDataContract); + + expect(dataContract.toObject()).to.deep.equal(fetchedOneMoreDataContract.toObject()); + }); + + it('should not store Data Contract with dry run', async () => { + const result = await repository.store( + dataContract, + { dryRun: true }, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const encodedDataContractResult = await store.get( + DataContractStoreRepository.TREE_PATH.concat([dataContract.getId().toBuffer()]), + DataContractStoreRepository.DATA_CONTRACT_KEY, + ); + + expect(encodedDataContractResult.getValue()).to.be.null(); + }); + }); + + describe('#fetch', () => { + beforeEach(async () => { + await store.createTree([], DataContractStoreRepository.TREE_PATH[0]); + }); + + it('should should fetch null if Data Contract not found', async () => { + const result = await repository.fetch(dataContract.getId()); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.be.null(); + }); + + it('should fetch Data Contract', async () => { + await store.getDrive().applyContract(dataContract, new Date(), false); + + const result = await repository.fetch(dataContract.getId()); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const storedDataContract = result.getValue(); + + expect(storedDataContract).to.be.an.instanceof(DataContract); + expect(storedDataContract.toObject()).to.deep.equal(storedDataContract.toObject()); + }); + + it('should fetch Data Contract using transaction', async () => { + await store.startTransaction(); + + await store.getDrive().applyContract(dataContract, new Date(), true); + + const notFoundDataContractResult = await repository.fetch(dataContract.getId(), { + useTransaction: false, + }); + + expect(notFoundDataContractResult.getValue()).to.be.null(); + + const transactionalDataContractResult = await repository.fetch(dataContract.getId(), { + useTransaction: true, + }); + + const transactionalDataContract = transactionalDataContractResult.getValue(); + + expect(transactionalDataContract).to.be.an.instanceof(DataContract); + expect(transactionalDataContract.toObject()).to.deep.equal(dataContract.toObject()); + + await store.commitTransaction(); + + const storedDataContractResult = await repository.fetch(dataContract.getId()); + + const storedDataContract = storedDataContractResult.getValue(); + + expect(storedDataContract).to.be.an.instanceof(DataContract); + expect(storedDataContract.toObject()).to.deep.equal(dataContract.toObject()); + }); + + it('should fetch null on dry run', async () => { + await store.getDrive().applyContract(dataContract, new Date(), false); + + const result = await repository.fetch(dataContract.getId(), { dryRun: true }); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.be.null(); + }); + }); + + describe('#createTree', () => { + it('should create a tree', async () => { + const result = await repository.createTree(); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const data = await store.db.get( + [], + DataContractStoreRepository.TREE_PATH[0], + ); + + expect(data).to.deep.equal({ + type: 'tree', + value: Buffer.alloc(32), + }); + }); + }); + + describe('#prove', () => { + beforeEach(async () => { + await store.createTree([], DataContractStoreRepository.TREE_PATH[0]); + }); + + it('should should return proof if Data Contract not found', async () => { + const result = await repository.prove(dataContract.getId()); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceof(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + it('should return proof', async () => { + await store.getDrive().applyContract(dataContract, new Date(), false); + + const result = await repository.prove(dataContract.getId()); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceof(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + // TODO enable this test when we support transactions + it.skip('should return proof using transaction', async () => { + await store.startTransaction(); + + await store.getDrive().applyContract(dataContract, new Date(), true); + + const notFoundDataContractResult = await repository.prove(dataContract.getId(), { + useTransaction: false, + }); + + expect(notFoundDataContractResult.getValue()).to.be.null(); + + const transactionalDataContractResult = await repository.prove(dataContract.getId(), { + useTransaction: true, + }); + + const transactionalDataContract = transactionalDataContractResult.getValue(); + + expect(transactionalDataContract).to.be.an.instanceof(Buffer); + + await store.commitTransaction(); + + const storedDataContractResult = await repository.prove(dataContract.getId()); + + const storedDataContract = storedDataContractResult.getValue(); + + expect(storedDataContract).to.be.an.instanceof(Buffer); + }); + }); + + describe('#proveMany', () => { + let dataContract2; + + beforeEach(async () => { + dataContract2 = new DataContract({ + $id: generateRandomIdentifier().toBuffer(), + ownerId: generateRandomIdentifier().toBuffer(), + contractId: generateRandomIdentifier().toBuffer(), + documents: { + niceDocument: { + properties: { + nice: { + type: 'boolean', + }, + }, + }, + }, + }); + + await store.createTree([], DataContractStoreRepository.TREE_PATH[0]); + }); + + it('should should return proof if Data Contract not found', async () => { + const result = await repository.proveMany([dataContract.getId(), dataContract2.getId()]); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceof(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + it('should return proof', async () => { + await store.getDrive().applyContract(dataContract, new Date(), false); + await store.getDrive().applyContract(dataContract2, new Date(), false); + + const result = await repository.proveMany([dataContract.getId(), dataContract2.getId()]); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceof(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + // TODO enable this test when we support transactions + it.skip('should return proof using transaction', async () => { + await store.startTransaction(); + + await store.getDrive().applyContract(dataContract, new Date(), true); + await store.getDrive().applyContract(dataContract2, new Date(), true); + + const notFoundDataContractResult = await repository.prove( + [dataContract.getId(), dataContract2.getId()], { + useTransaction: false, + }, + ); + + expect(notFoundDataContractResult.getValue()).to.be.null(); + + const transactionalDataContractResult = await repository.proveMany( + [dataContract.getId(), dataContract2.getId()], + { useTransaction: true }, + ); + + const transactionalDataContract = transactionalDataContractResult.getValue(); + + expect(transactionalDataContract).to.be.an.instanceof(Buffer); + + await store.commitTransaction(); + + const storedDataContractResult = await repository.proveMany( + [dataContract.getId(), dataContract2.getId()], + ); + + const storedDataContract = storedDataContractResult.getValue(); + + expect(storedDataContract).to.be.an.instanceof(Buffer); + }); + }); +}); diff --git a/packages/js-drive/test/integration/document/DocumentRepository.spec.js b/packages/js-drive/test/integration/document/DocumentRepository.spec.js new file mode 100644 index 00000000000..9241199f607 --- /dev/null +++ b/packages/js-drive/test/integration/document/DocumentRepository.spec.js @@ -0,0 +1,3355 @@ +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); +const Document = require('@dashevo/dpp/lib/document/Document'); +const DataContractFactory = require('@dashevo/dpp/lib/dataContract/DataContractFactory'); +const createDPPMock = require('@dashevo/dpp/lib/test/mocks/createDPPMock'); +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); +const createTestDIContainer = require('../../../lib/test/createTestDIContainer'); +const createDocumentTypeTreePath = require('../../../lib/document/groveDB/createDocumentTreePath'); +const InvalidQueryError = require('../../../lib/document/errors/InvalidQueryError'); +const StorageResult = require('../../../lib/storage/StorageResult'); + +function ucFirst(string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + +const typesTestCases = { + number: { + type: 'number', + value: 1, + }, + boolean: { + type: 'boolean', + value: true, + }, + string: { + type: 'string', + value: 'test', + }, + null: { + type: 'null', + value: null, + }, + undefined: { + type: 'undefined', + value: undefined, + }, + object: { + type: 'object', + value: {}, + }, + buffer: { + type: 'buffer', + value: Buffer.alloc(32), + }, +}; + +const notObjectTestCases = [ + typesTestCases.number, + typesTestCases.boolean, + typesTestCases.string, + typesTestCases.null, +]; + +const notArrayTestCases = [ + typesTestCases.number, + typesTestCases.boolean, + typesTestCases.string, + typesTestCases.null, + typesTestCases.object, + typesTestCases.buffer, +]; + +const nonScalarTestCases = [ + typesTestCases.null, + typesTestCases.undefined, + typesTestCases.object, +]; + +const scalarTestCases = [ + typesTestCases.number, + typesTestCases.string, + typesTestCases.boolean, + typesTestCases.buffer, +]; + +const nonNumberTestCases = [ + typesTestCases.string, + typesTestCases.boolean, + typesTestCases.null, + typesTestCases.undefined, + typesTestCases.object, + typesTestCases.buffer, +]; + +const nonNumberAndUndefinedTestCases = [ + typesTestCases.string, + typesTestCases.boolean, + typesTestCases.null, + typesTestCases.object, + typesTestCases.buffer, +]; + +const nonNumberNullAndUndefinedTestCases = [ + typesTestCases.string, + typesTestCases.boolean, + typesTestCases.object, + typesTestCases.buffer, +]; + +const validFieldNameTestCases = [ + 'a', + 'a.b', + 'a.b.c', + 'array.element', + 'a.0', + 'a.0.b', + 'a_._b', + 'a-b.c_', + '$id', + '$ownerId', + '$createdAt', + '$updatedAt', +]; + +const invalidFieldNameTestCases = [ + '$a', + '$#1321', + 'a...', + '.a', + 'a.b.c.', +]; + +const validOrderByOperators = [ + { + operator: '>', + value: 42, + documentType: 'documentNumber', + }, + { + operator: '<', + value: 42, + documentType: 'documentNumber', + }, + { + operator: 'startsWith', + value: 'rt-', + documentType: 'documentString', + }, + { + operator: 'in', + value: [1, 2], + documentType: 'documentNumber', + }, +]; + +const queryDocumentSchema = { + testDocument: { + type: 'object', + properties: { + firstName: { + type: 'string', + }, + lastName: { + type: 'string', + }, + a: { + type: 'integer', + }, + b: { + type: 'integer', + }, + c: { + type: 'integer', + }, + d: { + type: 'integer', + }, + e: { + type: 'integer', + }, + }, + required: ['$createdAt'], + additionalProperties: false, + indices: [ + { + name: 'one', + properties: [ + { firstName: 'asc' }, + ], + }, + { + name: 'two', + properties: [ + { a: 'asc' }, + { b: 'asc' }, + { c: 'asc' }, + { d: 'asc' }, + { e: 'asc' }, + ], + }, + { + name: 'three', + properties: [ + { firstName: 'asc' }, + { lastName: 'asc' }, + ], + }, + ], + }, + documentA: { + type: 'object', + properties: { + firstName: { + type: 'string', + }, + }, + additionalProperties: false, + indices: [ + { + name: 'one', + properties: [{ $id: 'asc' }], + }, + ], + }, + documentB: { + type: 'object', + additionalProperties: false, + properties: { + firstName: { + type: 'string', + }, + }, + indices: [ + { + properties: [{ $id: 'asc' }], + unique: true, + }, + ], + }, + documentC: { + type: 'object', + additionalProperties: false, + properties: { + a: { + type: 'integer', + }, + b: { + type: 'integer', + }, + }, + indices: [ + { + properties: [{ a: 'asc' }, { b: 'asc' }], + }, + ], + }, + documentD: { + // no index + type: 'object', + additionalProperties: false, + properties: { + firstName: { + type: 'string', + }, + }, + }, + documentE: { + type: 'object', + additionalProperties: false, + properties: { + a: { + type: 'string', + }, + b: { + type: 'string', + }, + }, + indices: [ + { + properties: [{ a: 'asc' }, { b: 'asc' }], + }, + ], + }, + documentF: { + type: 'object', + additionalProperties: false, + properties: { + a: { + type: 'integer', + }, + b: { + type: 'integer', + }, + c: { + type: 'integer', + }, + }, + indices: [ + { + properties: [{ a: 'asc' }, { b: 'asc' }, { c: 'asc' }], + }, + ], + }, + documentG: { + type: 'object', + additionalProperties: false, + properties: { + a: { + type: 'integer', + }, + b: { + type: 'integer', + }, + }, + indices: [ + { + properties: [{ b: 'asc' }, { a: 'asc' }], + }, + { + properties: [{ a: 'asc' }, { b: 'asc' }], + }, + ], + }, + documentH: { + type: 'object', + additionalProperties: false, + properties: { + firstName: { + type: 'string', + }, + }, + indices: [ + { + properties: [{ $updatedAt: 'asc' }], + }, + ], + }, + documentI: { + type: 'object', + additionalProperties: false, + properties: { + firstName: { + type: 'string', + }, + }, + indices: [ + { + properties: [{ $createdAt: 'asc' }], + }, + ], + }, + documentJ: { + type: 'object', + additionalProperties: false, + properties: { + a: { + type: 'integer', + }, + b: { + type: 'integer', + }, + c: { + type: 'integer', + }, + d: { + type: 'integer', + }, + e: { + type: 'integer', + }, + }, + indices: [ + { + name: 'index1', + properties: [ + { a: 'asc' }, + { b: 'asc' }, + { c: 'asc' }, + { d: 'asc' }, + { e: 'asc' }, + ], + unique: true, + }, + ], + }, + documentK: { + type: 'object', + additionalProperties: false, + properties: { + a: { + type: 'string', + }, + b: { + type: 'string', + }, + }, + indices: [ + { + properties: [{ b: 'asc' }], + }, + ], + }, + documentL: { + type: 'object', + additionalProperties: false, + properties: { + a: { + type: 'integer', + }, + b: { + type: 'integer', + }, + c: { + type: 'integer', + }, + d: { + type: 'integer', + }, + }, + indices: [ + { + name: 'index1', + properties: [ + { a: 'asc' }, + { b: 'asc' }, + { c: 'asc' }, + { d: 'asc' }, + ], + unique: true, + }, + ], + }, +}; + +for (const fieldName of validFieldNameTestCases) { + queryDocumentSchema[`document${fieldName}`] = { + type: 'object', + properties: { + [fieldName]: { + type: 'integer', + }, + }, + additionalProperties: false, + indices: [ + { + name: 'one', + properties: [{ [fieldName]: 'asc' }], + }, + ], + }; +} + +for (const type of ['number', 'string', 'boolean', 'buffer']) { + const properties = { + a: { + type, + }, + }; + + if (type === 'buffer') { + properties.a.type = 'array'; + properties.a.byteArray = true; + } + + queryDocumentSchema[`document${ucFirst(type)}`] = { + type: 'object', + properties, + additionalProperties: false, + indices: [ + { + name: 'one', + properties: [{ a: 'asc' }], + }, + ], + }; +} + +queryDocumentSchema.documentBig = { + type: 'object', + properties: Array(256).fill().map((v, i) => `a${i}`).reduce((res, key) => { + res[key] = { + type: 'integer', + }; + + return res; + }, {}), + additionalProperties: false, + indices: Array(256).fill().map((v, i) => ({ + properties: [{ [`a${i}`]: 'asc' }], + })), +}; + +const validQueries = [ + {}, + { + where: [['$id', 'in', [ + generateRandomIdentifier(), + generateRandomIdentifier(), + generateRandomIdentifier(), + ]]], + orderBy: [['$id', 'asc']], + }, + { + where: [ + ['a', '==', 1], + ['b', '==', 2], + ['c', '==', 3], + ['d', 'in', [1, 2]], + ], + orderBy: [ + ['d', 'desc'], + ['e', 'asc'], + ], + }, + { + where: [ + ['a', '==', 1], + ['b', '==', 2], + ['c', '==', 3], + ['d', 'in', [1, 2]], + ['e', '>', 3], + ], + orderBy: [ + ['d', 'desc'], + ['e', 'asc'], + ], + }, + { + where: [ + ['firstName', '>', 'Chris'], + ['firstName', '<=', 'Noellyn'], + ], + orderBy: [ + ['firstName', 'asc'], + ], + }, + { + where: [ + ['firstName', '==', '1'], + ['lastName', '==', '2'], + ], + limit: 1, + }, +]; + +const invalidQueries = [ + { + query: { + where: [ + ['a', '==', 1], + ['b', '==', 2], + ], + }, + error: 'query is too far from index: query must better match an existing index', + }, + { + query: { + where: [ + ['a', '==', 1], + ['b', '==', 2], + ['c', 'in', [1, 2]], + ], + orderBy: [ + ['c', 'desc'], + ], + }, + error: 'where clause on non indexed property error: query must be for valid indexes', + }, + { + query: { + where: [ + ['a', '==', 1], + ['b', '==', 2], + ['b', 'in', [1, 2]], + ], + orderBy: [ + ['b', 'desc'], + ], + }, + error: 'duplicate non groupable clause on same field error: in clause has same field as an equality clause', + }, + { + query: { + where: [ + ['z', '==', 1], + ], + }, + error: 'where clause on non indexed property error: query must be for valid indexes', + }, + { + query: { + where: [ + ['a', '==', 1], + ['b', '==', 2], + ['c', '>', 3], + ['d', 'in', [1, 2]], + ['e', '>', 3], + ], + }, + error: 'multiple range clauses error: all ranges must be on same field', + }, + { + query: { + where: [ + ['a', '==', 1], + ['b', '==', 2], + ['c', '>', 3], + ['d', '>', 3], + ], + orderBy: [ + ['c', 'asc'], + ['d', 'desc'], + ], + }, + error: 'multiple range clauses error: all ranges must be on same field', + }, + { + query: { + where: [ + ['a', '==', 3], + ['b', '==', 2], + ['c', '>', 1], + ], + }, + error: 'missing order by for range error: query must have an orderBy field for each range element', + }, + { + query: { + where: [ + ['a', '==', 3], + ['b', '==', 2], + ['c', '==', 3], + ['d', 'in', [1, 2]], + ['e', '<', 1], + ], + orderBy: [ + ['e', 'asc'], + ['d', 'asc'], + ], + }, + error: 'where clause on non indexed property error: query must be for valid indexes', + }, + { + query: 'abc', + error: 'invalid cbor error: unable to decode query', + }, + { + query: [], + error: 'invalid cbor error: unable to decode query', + }, + { + query: { where: [1, 2, 3] }, + error: 'query invalid format for where clause error: where clause must be an array', + }, + { + query: { invalid: 'query' }, + error: 'unsupported error: unsupported syntax in where clause', + }, +]; + +const invalidOperators = ['<<', '<==', '===', '!>', '>>=']; + +async function createDocuments(documentRepository, documents) { + return Promise.all( + documents.map(async (o) => { + const result = await documentRepository.create(o); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + }), + ); +} + +describe('DocumentRepository', function main() { + this.timeout(30000); + + let documentRepository; + let dataContractRepository; + let container; + let dataContract; + let queryDataContract; + let documents; + let document; + let documentSchema; + + beforeEach(async () => { + const now = 86400; + container = await createTestDIContainer(); + + dataContract = getDataContractFixture(); + documents = getDocumentsFixture(dataContract).slice(0, 5); + + [document] = documents; + + // Modify documents for the test cases + documents = documents.map((doc, i) => { + const currentDocument = doc; + // const arrayItem = { item: i + 1, flag: true }; + + currentDocument.set('order', i); + currentDocument.set('$createdAt', now); + // currentDocument.set('arrayWithScalar', Array(i + 1) + // .fill(1) + // .map((item, index) => i + index)); + // currentDocument.set('arrayWithObjects', Array(i + 1).fill(arrayItem)); + currentDocument.type = document.getType(); + + return currentDocument; + }); + + [document] = documents; + + dataContract.documents[document.getType()].properties = { + ...dataContract.documents[document.getType()].properties, + name: { + type: 'string', + maxLength: 255, + }, + order: { + type: 'number', + }, + lastName: { + type: 'string', + maxLength: 255, + }, + // arrayWithScalar: { + // type: 'array', + // items: [ + // { type: 'string' }, + // ], + // }, + // arrayWithObjects: { + // type: 'array', + // items: { + // type: 'object', + // properties: { + // flag: { + // type: 'string', + // }, + // }, + // }, + // }, + }; + // + const documentsSchema = dataContract.getDocuments(); + + documentSchema = documentsSchema[document.getType()]; + + // redeclare indices + const indices = documentSchema.indices || []; + documentSchema.indices = indices.concat([ + { + name: 'index1', + properties: [{ name: 'asc' }], + }, + // { + // name: 'index2', + // + // properties: [{ name: 'asc' }, { 'arrayWithObjects.item': 'asc' }], + // }, + { + name: 'index3', + properties: [{ order: 'asc' }], + }, + { + name: 'index4', + properties: [{ lastName: 'asc' }], + }, + // { + // name: 'index5', + // properties: [{ arrayWithScalar: 'asc' }], + // }, + // { + // name: 'index6', + // properties: [{ arrayWithObjects: 'asc' }], + // }, + // { + // name: 'index7', + // properties: [{ 'arrayWithObjects.item': 'asc' }], + // }, + // { + // name: 'index8', + // properties: [{ 'arrayWithObjects.flag': 'asc' }], + // }, + // { + // name: 'index9', + // properties: [{ primaryOrder: 'asc' }, { order: 'desc' }], + // }, + { + name: 'index10', + properties: [{ $ownerId: 'asc' }], + }, + ]); + + const dpp = container.resolve('dpp'); + queryDataContract = dpp.dataContract.create(generateRandomIdentifier(), queryDocumentSchema); + + documentRepository = container.resolve('documentRepository'); + + const createInitialStateStructure = container.resolve('createInitialStateStructure'); + await createInitialStateStructure(); + + dataContractRepository = container.resolve('dataContractRepository'); + + await dataContractRepository.store(dataContract); + await dataContractRepository.store(queryDataContract); + }); + + afterEach(async () => { + if (container) { + await container.dispose(); + } + }); + + describe('#create', () => { + beforeEach(async () => { + await createDocuments(documentRepository, documents); + }); + + it('should create Document', async () => { + const documentTypeTreePath = createDocumentTypeTreePath( + document.getDataContract(), + document.getType(), + ); + + const documentTreePath = documentTypeTreePath.concat( + [Buffer.from([0])], + ); + + const result = await documentRepository + .storage + .db + .get(documentTreePath, document.getId().toBuffer(), false); + + expect(document.toBuffer()).to.deep.equal(result.value); + }); + + it('should create Document in transaction', async () => { + await documentRepository.delete(dataContract, document.getType(), document.getId()); + + await documentRepository + .storage + .startTransaction(); + + await documentRepository.create(document, { + useTransaction: true, + }); + + const documentTypeTreePath = createDocumentTypeTreePath( + document.getDataContract(), + document.getType(), + ); + + const documentTreePath = documentTypeTreePath.concat( + [Buffer.from([0])], + ); + + const transactionDocument = await documentRepository + .storage + .db + .get(documentTreePath, document.getId().toBuffer(), true); + + try { + await documentRepository + .storage + .db + .get(documentTreePath, document.getId().toBuffer(), false); + + expect.fail('should fail with NotFoundError error'); + } catch (e) { + expect(e.message.startsWith('path key not found: key not found in Merk')).to.be.true(); + } + + await documentRepository.storage.commitTransaction(); + + const createdDocument = await documentRepository + .storage + .db + .get(documentTreePath, document.getId().toBuffer(), false); + + expect(document.toBuffer()).to.deep.equal(transactionDocument.value); + expect(document.toBuffer()).to.deep.equal(createdDocument.value); + }); + + it('should not create Document on dry run', async () => { + await documentRepository.delete(dataContract, document.getType(), document.getId()); + + await documentRepository.create(document, { + dryRun: true, + }); + + const documentTypeTreePath = createDocumentTypeTreePath( + document.getDataContract(), + document.getType(), + ); + + const documentTreePath = documentTypeTreePath.concat( + [Buffer.from([0])], + ); + + try { + await documentRepository + .storage + .db + .get(documentTreePath, document.getId().toBuffer()); + + expect.fail('should fail with NotFoundError error'); + } catch (e) { + expect(e.message.startsWith('path key not found: key not found in Merk')).to.be.true(); + } + }); + }); + + describe('#update', () => { + let replaceDocument; + + beforeEach(async () => { + await createDocuments(documentRepository, documents); + + replaceDocument = new Document({ + ...documents[1].toObject(), + lastName: 'NotSoShiny', + }, dataContract); + }); + + it('should update Document', async () => { + const updateResult = await documentRepository.update(replaceDocument); + + expect(updateResult).to.be.instanceOf(StorageResult); + expect(updateResult.getOperations().length).to.be.greaterThan(0); + + const documentTypeTreePath = createDocumentTypeTreePath( + replaceDocument.getDataContract(), + replaceDocument.getType(), + ); + + const documentTreePath = documentTypeTreePath.concat( + [Buffer.from([0])], + ); + + const result = await documentRepository + .storage + .db + .get(documentTreePath, replaceDocument.getId().toBuffer(), false); + + expect(replaceDocument.toBuffer()).to.deep.equal(result.value); + }); + + it('should store Document in transaction', async () => { + await documentRepository + .storage + .startTransaction(); + + const updateResult = await documentRepository.update(replaceDocument, { + useTransaction: true, + }); + + expect(updateResult).to.be.instanceOf(StorageResult); + expect(updateResult.getOperations().length).to.be.greaterThan(0); + + const documentTypeTreePath = createDocumentTypeTreePath( + replaceDocument.getDataContract(), + replaceDocument.getType(), + ); + + const documentTreePath = documentTypeTreePath.concat( + [Buffer.from([0])], + ); + + const transactionDocument = await documentRepository + .storage + .db + .get(documentTreePath, replaceDocument.getId().toBuffer(), true); + + const notUpdatedDocument = await documentRepository + .storage + .db + .get(documentTreePath, replaceDocument.getId().toBuffer(), false); + + await documentRepository.storage.commitTransaction(); + + const createdDocument = await documentRepository + .storage + .db + .get(documentTreePath, replaceDocument.getId().toBuffer(), false); + + expect(replaceDocument.toBuffer()).to.deep.equal(transactionDocument.value); + expect(replaceDocument.toBuffer()).to.deep.equal(createdDocument.value); + expect(documents[1].toBuffer()).to.deep.equal(notUpdatedDocument.value); + }); + + it('should not update Document on dry run', async () => { + const updateResult = await documentRepository.update(replaceDocument, { + dryRun: true, + }); + + expect(updateResult).to.be.instanceOf(StorageResult); + expect(updateResult.getOperations().length).to.be.greaterThan(0); + + const documentTypeTreePath = createDocumentTypeTreePath( + replaceDocument.getDataContract(), + replaceDocument.getType(), + ); + + const documentTreePath = documentTypeTreePath.concat( + [Buffer.from([0])], + ); + + const notUpdatedDocument = await documentRepository + .storage + .db + .get(documentTreePath, replaceDocument.getId().toBuffer(), false); + + expect(documents[1].toBuffer()).to.deep.equal(notUpdatedDocument.value); + }); + }); + + describe('#find', () => { + beforeEach(async () => { + await createDocuments(documentRepository, documents); + }); + + it('should find all existing documents', async () => { + const result = await documentRepository.find(dataContract, document.getType()); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.have.lengthOf(documents.length); + + const foundDocumentsBuffers = foundDocuments.map((doc) => doc.toBuffer()); + + expect(foundDocumentsBuffers).to.have.deep.members(documents.map((doc) => doc.toBuffer())); + }); + + it('should find all existing documents in transaction', async () => { + await documentRepository + .storage + .startTransaction(); + + const foundDocumentsResult = await documentRepository + .find(dataContract, document.getType(), { + useTransaction: true, + }); + + expect(foundDocumentsResult).to.be.instanceOf(StorageResult); + expect(foundDocumentsResult.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = foundDocumentsResult.getValue(); + + await documentRepository.storage.commitTransaction(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.have.lengthOf(documents.length); + + const foundDocumentsBuffers = foundDocuments.map((doc) => doc.toBuffer()); + + expect(foundDocumentsBuffers).to.have.deep.members(documents.map((doc) => doc.toBuffer())); + }); + + it('should fetch Documents with dry run', async () => { + await documentRepository + .storage + .startTransaction(); + + const foundDocumentsResult = await documentRepository + .find(dataContract, document.getType(), { + dryRun: true, + }); + + expect(foundDocumentsResult).to.be.instanceOf(StorageResult); + expect(foundDocumentsResult.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = foundDocumentsResult.getValue(); + + await documentRepository.storage.commitTransaction(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.have.lengthOf(documents.length); + + const foundDocumentsBuffers = foundDocuments.map((doc) => doc.toBuffer()); + + expect(foundDocumentsBuffers).to.have.deep.members(documents.map((doc) => doc.toBuffer())); + }); + + describe('queries', () => { + describe('valid queries', () => { + validQueries.forEach((query) => { + it(`should return valid result for query "${JSON.stringify(query)}"`, async () => { + const result = await documentRepository.find(queryDataContract, 'testDocument', query); + + expect(result).to.be.instanceOf(StorageResult); + }); + }); + + it('should return valid result if data contract has only system properties', async () => { + const schema = { + chat: { + type: 'object', + indices: [ + { + name: 'ownerAndCreatedAt', + properties: [ + { + $ownerId: 'asc', + }, + { + $createdAt: 'asc', + }, + ], + }, + ], + properties: { + test: { + type: 'string', + }, + }, + required: ['$createdAt'], + additionalProperties: false, + }, + }; + + const factory = new DataContractFactory(createDPPMock(), () => {}); + const ownerId = generateRandomIdentifier(); + const myDataContract = factory.create(ownerId, schema); + await dataContractRepository.store(myDataContract); + + const result = await documentRepository.find(myDataContract, 'chat', { + where: [ + ['$ownerId', '==', ownerId], + ['$createdAt', '>', new Date().getTime()], + ], + orderBy: [['$createdAt', 'asc']], + }); + + expect(result).to.be.instanceOf(StorageResult); + }); + + it('should return valid result for DPNS contract', async () => { + const schema = { + label: { + type: 'object', + properties: { + normalizedLabel: { + type: 'string', + }, + normalizedParentDomainName: { + type: 'string', + }, + }, + indices: [ + { + name: 'index1', + properties: [ + { + normalizedParentDomainName: 'asc', + }, + { + normalizedLabel: 'asc', + }, + ], + unique: true, + }, + ], + }, + }; + + const factory = new DataContractFactory(createDPPMock(), () => {}); + const ownerId = generateRandomIdentifier(); + const myDataContract = factory.create(ownerId, schema); + await dataContractRepository.store(myDataContract); + + const result = await documentRepository.find(myDataContract, 'label', { + where: [ + ['normalizedParentDomainName', '==', 'dash'], + ], + orderBy: [['normalizedLabel', 'asc']], + }); + + expect(result).to.be.instanceOf(StorageResult); + }); + }); + + describe('invalid queries', () => { + invalidQueries.forEach(({ query, error }) => { + it(`should throw InvalidQueryError for query "${JSON.stringify(query)}"`, async () => { + try { + await documentRepository.find(queryDataContract, 'testDocument', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(error); + } + }); + }); + + notObjectTestCases.forEach(({ type, value: query }) => { + it(`should return invalid result if query is a ${type}`, async () => { + try { + await documentRepository.find(queryDataContract, 'documentA', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('invalid cbor error: unable to decode query'); + } + }); + }); + }); + + describe('where', () => { + it('should return empty array if where clause conditions do not match', async () => { + const query = { + where: [['name', '==', 'Dash enthusiast']], + }; + + const result = await documentRepository.find(dataContract, document.getType(), query); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.have.lengthOf(0); + }); + + it.skip('should find documents by nested object fields', async () => { + const query = { + where: [ + ['arrayWithObjects.item', '==', 2], + ], + }; + + const result = await documentRepository.find(dataContract, document.getType(), query); + + expect(result).to.be.an('array'); + expect(result).to.be.lengthOf(1); + + const [expectedDocument] = result; + + expect(expectedDocument.toBuffer()).to.deep.equal(documents[1].toBuffer()); + }); + + it.skip('should return documents by several conditions', async () => { + const query = { + where: [ + ['name', '==', 'Cutie'], + ['arrayWithObjects.item', '==', 1], + ], + }; + + const result = await documentRepository.find(dataContract, document.getType(), query); + + expect(result).to.be.an('array'); + expect(result).to.be.lengthOf(1); + + const [expectedDocument] = result; + + expect(expectedDocument.toBuffer()).to.deep.equal(documents[0].toBuffer()); + }); + + notArrayTestCases.forEach(({ type, value: query }) => { + it(`should return invalid result if "where" is not an array, but ${type}`, async () => { + try { + await documentRepository.find(queryDataContract, 'documentA', { where: query }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('query invalid format for where clause error: where clause must be an array'); + } + }); + }); + + it('should return invalid result if "where" contains more than 10 conditions', async () => { + const where = Array(11).fill(['a', '<', 1]); + try { + await documentRepository.find(queryDataContract, 'documentA', { where }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('multiple range clauses error: there can only be at most 2 range clauses that must be on the same field'); + } + }); + + it('should return invalid result if "where" contains conflicting conditions', async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { + where: [ + ['a', '<', 1], + ['a', '>', 1], + ], + orderBy: [['a', 'asc']], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + } + }); + + it('should return invalid result if number of properties queried does not match number of indexed ones minus 2', async () => { + try { + await documentRepository.find(queryDataContract, 'documentL', { + where: [ + ['a', '==', 1], + ], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('query is too far from index: query must better match an existing index'); + } + }); + + describe('condition', () => { + describe('property', () => { + it('should return valid result if condition contains "$id" field', async () => { + const result = await documentRepository.find(queryDataContract, 'documentB', { + where: + [['$id', '==', generateRandomIdentifier()]], + }); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.isEmpty()).to.be.true(); + }); + + it('should return valid result if condition contains top-level field', async () => { + const result = await documentRepository.find(queryDataContract, 'documentE', { + where: [ + ['a', '==', '1'], + ], + }); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.isEmpty()).to.be.true(); + }); + + it.skip('should return valid result if condition contains nested path field', async () => { + const result = await documentRepository.find(queryDataContract, 'documentD', { + where: + [['a.b', '==', '1']], + }); + + expect(result).to.be.instanceOf(StorageResult); + }); + + it('should return invalid result if property is not specified in document indices', async () => { + try { + await documentRepository.find(queryDataContract, 'documentD', { + where: [ + ['a', '==', '1'], + ], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('where clause on non indexed property error: query must be for valid indexes'); + } + }); + }); + + it('should return invalid result if condition array has less than 3 elements (field, operator, value)', async () => { + try { + await documentRepository.find(queryDataContract, 'documentA', { + where: + [['a', '==']], + + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('invalid where clause components error: where clauses should have at most 3 components'); + } + }); + + it('should return invalid result if condition array has more than 3 elements (field, operator, value)', async () => { + try { + await documentRepository.find(queryDataContract, 'documentA', { + where: [ + [['a', '==', '1', '2']], + ], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('invalid where clause components error: where clauses should have at most 3 components'); + } + }); + + describe('operators', () => { + describe('comparisons', () => { + invalidOperators.forEach((operator) => { + it('should return invalid result if condition contains invalid comparison operator', async () => { + const query = { where: [['a', operator, '1']] }; + if (operator !== '===') { + query.orderBy = [['a', 'asc']]; + } + + try { + await documentRepository.find(queryDataContract, 'documentE', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('invalid where clause components error: second field of where component should be a known operator'); + } + }); + }); + + describe('<', () => { + it('should find documents with "<" operator', async () => { + const query = { + where: [['order', '<', documents[1].get('order')]], + orderBy: [['order', 'asc']], + }; + + const result = await documentRepository.find( + dataContract, + document.getType(), + query, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.be.lengthOf(1); + + const [expectedDocument] = foundDocuments; + + expect(expectedDocument.toBuffer()).to.deep.equal(documents[0].toBuffer()); + }); + + it('should return invalid result if "<" operator used with a string value longer than 255 bytes', async () => { + const longString = 't'.repeat(255); + + const result = await documentRepository.find( + queryDataContract, + 'documentString', + { + where: [['a', '<', longString]], + orderBy: [['a', 'asc']], + }, + ); + + expect(result).to.be.instanceOf(StorageResult); + + const veryLongString = 't'.repeat(256); + + try { + await documentRepository.find( + queryDataContract, + 'documentString', + { + where: [['a', '<', veryLongString]], + orderBy: [['a', 'asc']], + }, + ); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('field requirement unmet: value must be less than 256 bytes long'); + } + }); + + nonScalarTestCases.forEach(({ type, value }) => { + it(`should return invalid result if "<" operator used with a not scalar value, but ${type}`, async function it() { + if ((typeof value === 'object' && value === null) || typeof value === 'undefined') { + this.skip('will be implemented later'); + } + + try { + await documentRepository.find(queryDataContract, 'documentNumber', { where: [['a', '<', value]], orderBy: [['a', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('value wrong type error: document field type doesn\'t match document value'); + } + }); + }); + + scalarTestCases.forEach(({ type, value }) => { + it(`should return valid result if "<" operator used with a scalar value ${type}`, async () => { + const docType = `document${ucFirst(type)}`; + + const result = await documentRepository.find(queryDataContract, docType, { where: [['a', '<', value]], orderBy: [['a', 'asc']] }); + + expect(result).to.be.instanceOf(StorageResult); + }); + }); + }); + + describe('<=', () => { + scalarTestCases.forEach(({ type, value }) => { + it(`should return valid result if "<=" operator used with a scalar value ${type}`, async () => { + const result = await documentRepository.find(queryDataContract, `document${ucFirst(type)}`, { where: [['a', '<=', value]], orderBy: [['a', 'asc']] }); + + expect(result).to.be.instanceOf(StorageResult); + }); + }); + + it('should find Documents using "<=" operator', async () => { + const query = { + where: [['order', '<=', documents[1].get('order')]], + orderBy: [['order', 'asc']], + }; + + const result = await documentRepository.find( + dataContract, + document.getType(), + query, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.be.lengthOf(2); + + const expectedDocuments = documents.slice(0, 2).map((doc) => doc.toBuffer()); + + expect(foundDocuments.map((doc) => doc.toBuffer())).to.deep.members( + expectedDocuments, + ); + }); + }); + + describe('==', () => { + it('should find existing documents using "==" operator', async () => { + const query = { + where: [['name', '==', document.get('name')]], + }; + + const result = await documentRepository.find( + dataContract, + document.getType(), + query, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.be.lengthOf(1); + + const [expectedDocument] = foundDocuments; + + expect(expectedDocument.toBuffer()).to.deep.equal(document.toBuffer()); + }); + + scalarTestCases.forEach(({ type, value }) => { + it(`should return valid result if "==" operator used with a scalar value ${type}`, async () => { + const result = await documentRepository.find(queryDataContract, `document${ucFirst(type)}`, { where: [['a', '==', value]] }); + + expect(result).to.be.instanceOf(StorageResult); + }); + }); + }); + + describe('=>', () => { + it('should find existing documents using ">=" operator', async () => { + const query = { + where: [['order', '>=', documents[1].get('order')]], + orderBy: [['order', 'asc']], + }; + + const result = await documentRepository.find( + dataContract, + document.getType(), + query, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.be.lengthOf(documents.length - 1); + + documents.shift(); + const expectedDocuments = documents + .map((doc) => doc.toBuffer()); + + expect(foundDocuments.map((doc) => doc.toBuffer())).to.deep.members( + expectedDocuments, + ); + }); + + scalarTestCases.forEach(({ type, value }) => { + it(`should return valid result if ">=" operator used with a scalar value ${type}`, async () => { + const result = await documentRepository.find(queryDataContract, `document${ucFirst(type)}`, { where: [['a', '>=', value]], orderBy: [['a', 'asc']] }); + + expect(result).to.be.instanceOf(StorageResult); + }); + }); + }); + + describe('>', () => { + it('should find existing documents using ">" operator', async () => { + const query = { + where: [['order', '>', documents[1].get('order')]], + orderBy: [['order', 'asc']], + }; + + const result = await documentRepository.find( + dataContract, + document.getType(), + query, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.be.lengthOf(documents.length - 2); + + const expectedDocuments = documents + .splice(2, documents.length) + .map((doc) => doc.toBuffer()); + + expect(foundDocuments.map((doc) => doc.toBuffer())).to.deep.members( + expectedDocuments, + ); + }); + + scalarTestCases.forEach(({ type, value }) => { + it(`should return valid result if ">" operator used with a scalar value ${type}`, async () => { + const result = await documentRepository.find(queryDataContract, `document${ucFirst(type)}`, { where: [['a', '>', value]], orderBy: [['a', 'asc']] }); + + expect(result).to.be.instanceOf(StorageResult); + }); + }); + }); + + ['>', '<', '<=', '>='].forEach((operator) => { + it(`should return invalid results if "${operator}" used not in the last 2 where conditions`, async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { + where: [ + ['a', operator, 1], + ['a', 'startsWith', 'rt-'], + ['a', 'startsWith', 'r-'], + ], + orderBy: [['a', 'asc']], + }); + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('range clauses not groupable error: clauses are not groupable'); + } + }); + }); + + describe('ranges', () => { + describe('multiple ranges', () => { + ['>', '<', '<=', '>='].forEach((firstOperator) => { + ['>', '<', '>=', '<='].forEach((secondOperator) => { + it(`should return invalid result if ${firstOperator} operator used with ${secondOperator} operator`, async () => { + const query = { where: [['a', firstOperator, '1'], ['b', secondOperator, 'a']] }; + + try { + await documentRepository.find(queryDataContract, 'documentE', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('multiple range clauses error: all ranges must be on same field'); + } + }); + }); + }); + + ['>', '<', '<=', '>='].forEach((firstOperator) => { + it(`should return invalid result if ${firstOperator} operator used with startsWith operator`, async () => { + const query = { where: [['a', firstOperator, '1'], ['b', 'startsWith', 'a']] }; + + try { + await documentRepository.find(queryDataContract, 'documentE', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('range clauses not groupable error: clauses are not groupable'); + } + }); + }); + + it('should return invalid result if startsWith operator used with startsWith operator', async () => { + const query = { where: [['a', 'startsWith', '1'], ['b', 'startsWith', 'a']] }; + + try { + await documentRepository.find(queryDataContract, 'documentE', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('multiple range clauses error: there can not be more than 1 non groupable range clause'); + } + }); + }); + + describe('conflicting operators', () => { + const conflictingOperators = [ + { + operators: ['>', '>'], + errorMessage: 'multiple range clauses error: there can only at most one range clause with a lower bound', + }, + { + operators: ['>', '=>'], + errorMessage: 'invalid where clause components error: second field of where component should be a known operator', + }, + { + operators: ['<', '<'], + errorMessage: 'range clauses not groupable error: lower and upper bounds must be passed if providing 2 ranges', + }, + { + operators: ['<', '<='], + errorMessage: 'range clauses not groupable error: lower and upper bounds must be passed if providing 2 ranges', + }, + ]; + + conflictingOperators.forEach(({ errorMessage, operators }) => { + it(`should return invalid result if ${operators[0]} operator used with ${operators[1]} operator`, async () => { + const query = { + where: [['a', operators[0], '1'], ['a', operators[1], 'a']], + orderBy: [['a', 'asc']], + }; + + try { + await documentRepository.find(queryDataContract, 'documentE', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(errorMessage); + } + }); + }); + }); + + it('should return invalid result if "in" operator is used before last two indexed conditions', async () => { + const query = { where: [['a', 'in', [1, 2]]] }; + + try { + await documentRepository.find(queryDataContract, 'documentF', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + // TODO is it correct ?????? + expect(e.message).to.equal('where clause on non indexed property error: query must be for valid indexes'); + } + }); + + ['>', '<', '>=', '<='].forEach((operator) => { + it(`should return invalid result if ${operator} operator is used before "=="`, async () => { + const query = { where: [['a', operator, 2], ['b', '==', 1]], orderBy: [['a', 'asc']] }; + + try { + await documentRepository.find(queryDataContract, 'documentF', query); + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + // TODO is it correct? + expect(e.message).to.equal('where clause on non indexed property error: query must be for valid indexes'); + } + }); + }); + + ['>', '<', '>=', '<='].forEach((operator) => { + it(`should return valid result if ${operator} operator is used before "in"`, async () => { + const query = { where: [['a', operator, 2], ['b', 'in', [1, 2]]], orderBy: [['a', 'asc'], ['b', 'asc']] }; + + const result = await documentRepository.find(queryDataContract, 'documentG', query); + + expect(result).to.be.instanceOf(StorageResult); + }); + }); + + it('should return invalid result if "in" or range operators are not in orderBy', async () => { + const query = { + where: [ + ['a', '==', 1], + ['b', '>', 1], + ], + orderBy: [['b', 'asc']], + }; + + delete query.orderBy; + + try { + await documentRepository.find(queryDataContract, 'documentF', query); + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('missing order by for range error: query must have an orderBy field for each range element'); + } + }); + }); + }); + + describe('timestamps', () => { + nonNumberNullAndUndefinedTestCases.forEach(({ type, value }) => { + it(`should return invalid result if $createdAt timestamp used with ${type} value`, async () => { + try { + await documentRepository.find(queryDataContract, 'documentI', { where: [['$createdAt', '>', value]], orderBy: [['$createdAt', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('value wrong type error: document field type doesn\'t match document value'); + } + }); + }); + + nonNumberNullAndUndefinedTestCases.forEach(({ type, value }) => { + it(`should return invalid result if $updatedAt timestamp used with ${type} value`, async () => { + try { + await documentRepository.find(queryDataContract, 'documentH', { where: [['$updatedAt', '>', value]], orderBy: [['$updatedAt', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal( + 'value wrong type error: document field type doesn\'t match document value', + ); + } + }); + }); + + it('should return valid result if condition contains "$createdAt" field', async () => { + const result = await documentRepository.find(queryDataContract, 'documentI', { where: [['$createdAt', '==', Date.now()]] }); + + expect(result).to.be.instanceOf(StorageResult); + }); + + it('should return valid result if condition contains "$updatedAt" field', async () => { + const result = await documentRepository.find(queryDataContract, 'documentH', { where: [['$updatedAt', '==', Date.now()]] }); + + expect(result).to.be.instanceOf(StorageResult); + }); + }); + + describe('in', () => { + it('should return valid result if "in" operator used with an array value', async () => { + const query = { + where: [ + ['$id', 'in', [ + documents[0].getId(), + documents[1].getId(), + ]], + ], + orderBy: [['$id', 'asc']], + }; + + const result = await documentRepository.find( + dataContract, + document.getType(), + query, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.be.lengthOf(2); + + const expectedDocuments = documents.slice(0, 2).map((doc) => doc.toBuffer()); + + expect(foundDocuments.map((doc) => doc.toBuffer())).to.deep.members( + expectedDocuments, + ); + }); + + notArrayTestCases.forEach(({ type, value }) => { + it(`should return invalid result if "in" operator used with not an array value, but ${type}`, async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { where: [['a', 'in', value]], orderBy: [['a', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('invalid IN clause error: when using in operator you must provide an array of values'); + } + }); + }); + + it('should return invalid result if "in" operator used with an empty array value', async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { where: [['a', 'in', []]], orderBy: [['a', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('invalid IN clause error: in clause must at least 1 value'); + } + }); + + it('should return invalid result if "in" operator used with an array value which contains more than 100 elements', async () => { + const arr = []; + + for (let i = 0; i < 100; i++) { + arr.push(i); + } + + const result = await documentRepository.find(queryDataContract, 'documentNumber', { where: [['a', 'in', arr]], orderBy: [['a', 'asc']] }); + + expect(result).to.be.instanceOf(StorageResult); + + arr.push(101); + + try { + await documentRepository.find(queryDataContract, 'documentNumber', { where: [['a', 'in', arr]], orderBy: [['a', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('invalid IN clause error: in clause must at most 100 values'); + } + }); + + it('should return invalid result if "in" operator used with an array which contains not unique elements', async () => { + const arr = [1, 1]; + try { + await documentRepository.find(queryDataContract, 'documentNumber', { where: [['a', 'in', arr]], orderBy: [['a', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('invalid IN clause error: there should be no duplicates values for In query'); + } + }); + + it('should return invalid results if "in" condition contains an array as an element', async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { where: [['a', 'in', [[]]]], orderBy: [['a', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('value wrong type error: document field type doesn\'t match document value'); + } + }); + }); + + describe('startsWith', () => { + it('should return valid result if "startsWith" operator used with a string value', async () => { + const query = { + where: [['lastName', 'startsWith', 'Swe']], + orderBy: [['lastName', 'asc']], + }; + + const result = await documentRepository.find( + dataContract, + document.getType(), + query, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.be.lengthOf(1); + + const [expectedDocument] = foundDocuments; + + expect(expectedDocument.toBuffer()).to.deep.equal(documents[2].toBuffer()); + }); + + it('should return invalid result if "startsWith" operator used with an empty string value', async () => { + try { + await documentRepository.find(queryDataContract, 'documentString', { where: [['a', 'startsWith', '']], orderBy: [['a', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('starts with illegal string error: starts with can not start with an empty string'); + } + }); + + it('should return invalid result if "startsWith" operator used with a string value which is more than 255 bytes long', async () => { + const value = 'b'.repeat(256); + try { + await documentRepository.find(queryDataContract, 'documentString', { where: [['a', 'startsWith', value]], orderBy: [['a', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('field requirement unmet: value must be less than 256 bytes long'); + } + }); + + [ + typesTestCases.number, + typesTestCases.boolean, + typesTestCases.object, + typesTestCases.buffer, + ].forEach(({ type, value }) => { + it(`should return invalid result if "startWith" operator used with a not string value, but ${type}`, async () => { + try { + await documentRepository.find(queryDataContract, 'documentString', { where: [['a', 'startsWith', value]], orderBy: [['a', 'asc']] }); + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('value wrong type error: document field type doesn\'t match document value'); + } + }); + }); + + [ + typesTestCases.null, + typesTestCases.undefined, + ].forEach(({ type, value }) => { + it(`should return invalid result if "startWith" operator used with a not string value, but ${type}`, async () => { + try { + await documentRepository.find(queryDataContract, 'documentString', { where: [['a', 'startsWith', value]], orderBy: [['a', 'asc']] }); + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('invalid STARTSWITH clause error: starts with must have at least one character'); + } + }); + }); + }); + + describe.skip('elementMatch', () => { + it('should return valid result if "elementMatch" operator used with "where" conditions', async () => { + const query = { + where: [ + ['arrayWithObjects', 'elementMatch', [ + ['item', '==', 2], ['flag', '==', true], + ]], + ], + }; + + const result = await documentRepository.find( + dataContract, + document.getType(), + query, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.be.lengthOf(1); + + const [expectedDocument] = foundDocuments; + + expect(expectedDocument.toBuffer()).to.deep.equal(documents[1].toBuffer()); + }); + + it('should return invalid result if "elementMatch" operator used with invalid "where" conditions', async () => { + const query = { + where: [ + ['arr', 'elementMatch', + [['elem', 'startsWith', 1], ['elem', '<', 3]], + ], + ], + }; + + try { + await documentRepository.find(queryDataContract, 'document', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + + it('should return invalid result if "elementMatch" operator used with less than 2 "where" conditions', async () => { + const query = { + where: [ + ['arr', 'elementMatch', + [['elem', '>', 1]], + ], + ], + }; + + try { + await documentRepository.find(queryDataContract, 'document', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + + it('should return invalid result if value contains conflicting conditions', async () => { + const query = { + where: [ + ['arr', 'elementMatch', + [['elem', '>', 1], ['elem', '>', 1]], + ], + ], + }; + + try { + await documentRepository.find(queryDataContract, 'document', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + + it('should return invalid result if $id field is specified', async () => { + const query = { + where: [ + ['arr', 'elementMatch', + [['$id', '>', 1], ['$id', '<', 3]], + ], + ], + }; + + try { + await documentRepository.find(queryDataContract, 'document', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + + it('should return invalid result if $ownerId field is specified', async () => { + const query = { + where: [ + ['arr', 'elementMatch', + [['$ownerId', '>', 1], ['$ownerId', '<', 3]], + ], + ], + }; + + try { + await documentRepository.find(queryDataContract, 'document', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + + it('should return invalid result if value contains nested "elementMatch" operator', async () => { + const query = { + where: [ + ['arr', 'elementMatch', + [['subArr', 'elementMatch', [ + ['subArrElem', '>', 1], ['subArrElem', '<', 3], + ]], ['subArr', '<', 3]], + ], + ], + }; + + try { + await documentRepository.find(queryDataContract, 'document', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + }); + + describe.skip('length', () => { + it('should return valid result if "length" operator used with a positive numeric value', async () => { + const query = { + where: [['arrayWithObjects', 'length', 2]], + }; + + const result = await documentRepository.find( + dataContract, + document.getType(), + query, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.be.lengthOf(1); + + const [expectedDocument] = foundDocuments; + + expect(expectedDocument.toBuffer()).to.deep.equal(documents[1].toBuffer()); + }); + + it('should return valid result if "length" operator used with zero', async () => { + const result = await documentRepository.find(queryDataContract, 'document', { + where: [ + ['arr', 'length', 0], + ], + }); + + expect(result).to.be.instanceOf(StorageResult); + }); + + it('should return invalid result if "length" operator used with a float numeric value', async () => { + try { + await documentRepository.find(queryDataContract, 'document', { + where: [ + ['arr', 'length', 1.2], + ], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + + it('should return invalid result if "length" operator used with a NaN', async () => { + try { + await documentRepository.find(queryDataContract, 'document', { + where: [ + ['arr', 'length', NaN], + ], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + + it('should return invalid result if "length" operator used with a numeric value which is less than 0', async () => { + try { + await documentRepository.find(queryDataContract, 'document', { + where: [ + ['arr', 'length', -1], + ], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + + nonNumberTestCases.forEach(({ type, value }) => { + it(`should return invalid result if "length" operator used with a ${type} instead of numeric value`, async () => { + try { + await documentRepository.find(queryDataContract, 'document', { + where: [ + ['arr', 'length', value], + ], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + }); + }); + + describe.skip('contains', () => { + it('should find Documents using "contains" operator and array value', async () => { + const query = { + where: [ + ['arrayWithScalar', 'contains', [2, 3]], + ], + }; + + const result = await documentRepository.find( + dataContract, + document.getType(), + query, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.be.lengthOf(1); + + const [expectedDocument] = foundDocuments; + + expect(expectedDocument.toBuffer()).to.deep.equal(documents[2].toBuffer()); + }); + + it('should find Documents using "contains" operator and scalar value', async () => { + const query = { + where: [ + ['arrayWithScalar', 'contains', 2], + ], + }; + + const result = await documentRepository.find( + dataContract, + document.getType(), + query, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.be.lengthOf(2); + + const expectedDocuments = documents.slice(1, 3).map((doc) => doc.toBuffer()); + + expect(foundDocuments.map((doc) => doc.toBuffer())).to.deep.members( + expectedDocuments, + ); + }); + + scalarTestCases.forEach(({ type, value }) => { + it(`should return valid result if "contains" operator used with a scalar value ${type}`, async () => { + const result = await documentRepository.find(queryDataContract, 'document', { + where: [ + ['arr', 'contains', value], + ], + }); + + expect(result).to.be.instanceOf(StorageResult); + }); + }); + + scalarTestCases.forEach(({ type, value }) => { + it(`should return valid result if "contains" operator used with an array of scalar values ${type}`, async () => { + const result = await documentRepository.find(queryDataContract, 'document', { + where: [ + ['arr', 'contains', [value]], + ], + }); + + expect(result).to.be.instanceOf(StorageResult); + }); + }); + + it('should return invalid result if "contains" operator used with an array which has ' + + ' more than 100 elements', async () => { + const arr = []; + for (let i = 0; i < 100; i++) { + arr.push(i); + } + + const result = await documentRepository.find(queryDataContract, 'document', { + where: [ + ['arr', 'contains', arr], + ], + }); + + expect(result).to.be.instanceOf(StorageResult); + + arr.push(101); + + try { + await documentRepository.find(queryDataContract, 'document', { + where: [ + ['arr', 'contains', arr], + ], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + + it('should return invalid result if "contains" operator used with an empty array', async () => { + try { + await documentRepository.find(queryDataContract, 'document', { + where: [ + ['arr', 'contains', []], + ], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + + it('should return invalid result if "contains" operator used with an array which contains not unique' + + ' elements', async () => { + try { + await documentRepository.find(queryDataContract, 'document', { + where: [ + ['arr', 'contains', [1, 1]], + ], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + + nonScalarTestCases.forEach(({ type, value }) => { + it(`should return invalid result if used with non-scalar value ${type}`, async () => { + try { + await documentRepository.find(queryDataContract, 'document', { + where: [ + ['arr', 'contains', value], + ], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + }); + + nonScalarTestCases.forEach(({ type, value }) => { + it(`should return invalid result if used with an array of non-scalar values ${type}`, async () => { + try { + await documentRepository.find(queryDataContract, 'document', { + where: [ + ['arr', 'contains', [value]], + + ], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(''); + } + }); + }); + }); + }); + }); + }); + + describe('limit', () => { + it('should limit return to 1 Document if limit is set', async () => { + const options = { + limit: 1, + }; + + const result = await documentRepository.find(dataContract, document.getType(), options); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.have.lengthOf(1); + }); + + it('should limit result to 100 Documents if limit is not set', async () => { + // Store 101 document + for (let i = 0; i < 101; i++) { + const svDoc = document; + + svDoc.id = Identifier.from(Buffer.alloc(32, i + 1)); + await documentRepository.create(svDoc); + } + + const result = await documentRepository.find(dataContract, document.getType()); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.have.lengthOf(100); + }); + + it('should return valid result if "limit" is a number', async () => { + const result = await documentRepository.find(queryDataContract, 'documentNumber', { + where: [ + ['a', '>', 1], + ], + orderBy: [['a', 'asc']], + limit: 1, + }); + + expect(result).to.be.instanceOf(StorageResult); + }); + + it('should return invalid result if "limit" is less than 0', async () => { + const where = [ + ['a', '>', 1], + ]; + + try { + await documentRepository.find(queryDataContract, 'documentNumber', { where, limit: -1, orderBy: [['a', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('query invalid limit error: limit should be a integer from 1 to 100'); + } + }); + + it('should return invalid result if "limit" is 0', async () => { + const where = [ + ['a', '>', 1], + ]; + + try { + await documentRepository.find(queryDataContract, 'documentNumber', { where, limit: 0, orderBy: [['a', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('query invalid limit error: limit should be a integer from 1 to 100'); + } + }); + + it('should return invalid result if "limit" is bigger than 100', async () => { + const where = [ + ['a', '>', 1], + ]; + + const result = await documentRepository.find(queryDataContract, 'documentNumber', { where, limit: 100, orderBy: [['a', 'asc']] }); + + expect(result).to.be.instanceOf(StorageResult); + + try { + await documentRepository.find(queryDataContract, 'documentNumber', { where, limit: 101, orderBy: [['a', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('query invalid limit error: limit should be a integer from 1 to 100'); + } + }); + + it('should return invalid result if "limit" is a float number', async () => { + const where = [ + ['a', '>', 1], + ]; + + try { + await documentRepository.find(queryDataContract, 'documentNumber', { where, limit: 1.5, orderBy: [['a', 'asc']] }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('query invalid limit error: limit should be a integer from 1 to 100'); + } + }); + + nonNumberAndUndefinedTestCases.forEach(({ type, value }) => { + it(`should return invalid result if "limit" is not a number, but ${type}`, async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { + where: [ + ['a', '>', 1], + ], + limit: value, + orderBy: [['a', 'asc']], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('query invalid limit error: limit should be a integer from 1 to 100'); + } + }); + }); + }); + + describe('startAt', () => { + it('should return the second document using identifier', async () => { + const query = { + where: [ + ['order', '>=', 0], + ], + orderBy: [ + ['order', 'asc'], + ], + startAt: documents[1].getId(), + }; + + const result = await documentRepository.find( + dataContract, + document.getType(), + query, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + + const expectedDocuments = documents.splice(1).map((doc) => doc.toBuffer()); + + expect(foundDocuments.map((doc) => doc.toBuffer())).to.deep.members(expectedDocuments); + }); + + it('should return the second document using base58', async () => { + const query = { + where: [ + ['order', '>=', 0], + ], + orderBy: [ + ['order', 'asc'], + ], + startAt: documents[1].getId().toString(), + }; + + const result = await documentRepository.find( + dataContract, + document.getType(), + query, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + + const expectedDocuments = documents.splice(1).map((doc) => doc.toBuffer()); + + expect(foundDocuments.map((doc) => doc.toBuffer())).to.deep.members(expectedDocuments); + }); + + it('should throw InvalidQuery if document not found', async () => { + const options = { + startAt: Buffer.alloc(0), + }; + + try { + await documentRepository.find(dataContract, document.getType(), options); + + expect.fail('should throw InvalidQueryError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidQueryError); + expect(e.message).to.equal('start document not found error: startAt document not found'); + } + }); + + [ + typesTestCases.boolean, + typesTestCases.null, + typesTestCases.object, + typesTestCases.number, + ].forEach(({ type, value }) => { + it(`should return invalid result if "startAt" is not a buffer, but ${type}`, async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { + startAt: value, + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + } + }); + }); + }); + + describe('startAfter', () => { + it('should return Documents after 1 document', async () => { + const options = { + where: [ + ['order', '>=', 0], + ], + orderBy: [ + ['order', 'asc'], + ], + startAfter: documents[0].id, + }; + + const result = await documentRepository.find(dataContract, document.getType(), options); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + + const expectedDocuments = documents.splice(1).map((doc) => doc.toBuffer()); + + expect(foundDocuments.map((doc) => doc.toBuffer())).to.deep.members(expectedDocuments); + }); + + it('should throw InvalidQuery if document not found', async () => { + const options = { + startAfter: Buffer.alloc(0), + }; + + try { + await documentRepository.find(dataContract, document.getType(), options); + + expect.fail('should throw InvalidQueryError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidQueryError); + expect(e.message).to.equal('start document not found error: startAfter document not found'); + } + }); + + it('should return invalid result if both "startAt" and "startAfter" are present', async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { + startAfter: documents[1].getId(), + startAt: documents[1].getId(), + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('duplicate start conditions error: only one of startAt or startAfter should be provided'); + } + }); + + [ + typesTestCases.boolean, + typesTestCases.null, + typesTestCases.object, + typesTestCases.number, + ].forEach(({ type, value }) => { + it(`should return invalid result if "startAfter" is not a buffer, but ${type}`, async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { + startAfter: value, + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('value wrong type error: system value is incorrect type'); + } + }); + }); + }); + + describe('orderBy', () => { + it('should sort Documents in descending order', async () => { + const query = { + where: [ + ['order', '>=', 0], + ], + orderBy: [ + ['order', 'desc'], + ], + }; + + const result = await documentRepository.find(dataContract, document.getType(), query); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + + const expectedDocuments = documents.reverse().map((doc) => doc.toBuffer()); + + expect(foundDocuments.map((doc) => doc.toBuffer())).to.deep.equal(expectedDocuments); + }); + + it('should sort Documents in ascending order', async () => { + const query = { + where: [ + ['order', '>=', 0], + ], + orderBy: [ + ['order', 'asc'], + ], + }; + + const result = await documentRepository.find(dataContract, document.getType(), query); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + + const expectedDocuments = documents.map((doc) => doc.toBuffer()); + + expect(foundDocuments.map((doc) => doc.toBuffer())).to.deep.equal(expectedDocuments); + }); + + it('should sort Documents by $id', async () => { + await Promise.all( + documents.map((d) => documentRepository + .delete(dataContract, document.getType(), d.getId())), + ); + + const createdIds = []; + let i = 0; + for (const svDoc of documents) { + svDoc.id = Identifier.from(Buffer.alloc(32, i + 1)); + await documentRepository.create(svDoc); + i++; + createdIds.push(svDoc.id); + } + + const query = { + where: [ + ['$id', 'in', createdIds], + ], + orderBy: [ + ['$id', 'desc'], + ], + }; + + const result = await documentRepository.find(dataContract, document.getType(), query); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.be.lengthOf(documents.length); + + expect(foundDocuments[0].getId()).to.deep.equal(createdIds[4]); + expect(foundDocuments[1].getId()).to.deep.equal(createdIds[3]); + expect(foundDocuments[2].getId()).to.deep.equal(createdIds[2]); + expect(foundDocuments[3].getId()).to.deep.equal(createdIds[1]); + expect(foundDocuments[4].getId()).to.deep.equal(createdIds[0]); + }); + + it('should return valid result if "orderBy" contains 1 sorting field', async () => { + const result = await documentRepository.find(queryDataContract, 'documentNumber', { + where: [ + ['a', '>', 1], + ], + orderBy: [['a', 'asc']], + }); + + expect(result).to.be.instanceOf(StorageResult); + }); + + it('should return valid result if "orderBy" contains a second fields not used in where clause', async () => { + const result = await documentRepository.find(queryDataContract, 'documentC', { + where: [ + ['a', '>', 1], + ], + orderBy: [['a', 'asc'], ['b', 'desc']], + }); + + expect(result).to.be.an.instanceOf(StorageResult); + }); + + it('should return invalid result if "orderBy" is an empty array', async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { + where: [ + ['a', '>', 1], + ], + orderBy: [], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('missing order by for range error: query must have an orderBy field for each range element'); + } + }); + + it('should return invalid result if sorting applied to not range condition', async function it() { + this.skip('will be implemented later'); + + try { + await documentRepository.find(queryDataContract, 'documentString', { + where: [['a', '==', 'b']], + orderBy: [['a', 'asc']], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + } + }); + + it('should return valid result if there is no where conditions', async () => { + const result = await documentRepository.find(queryDataContract, 'documentNumber', { + orderBy: [['a', 'asc']], + }); + + expect(result).to.be.instanceOf(StorageResult); + }); + + it('should return invalid result if the field inside an "orderBy" is an empty array', async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { + where: [ + ['a', '>', 1], + ], + orderBy: [[]], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('missing order by for range error: query must have an orderBy field for each range element'); + } + }); + + it('should return invalid result if order of three of two properties after indexed one is not preserved', async () => { + try { + await documentRepository.find(queryDataContract, 'documentL', { + where: [ + ['b', '>', 1], + ], + orderBy: [['b', 'desc'], ['e', 'asc']], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('where clause on non indexed property error: query must be for valid indexes'); + } + }); + + it('should return invalid result if order of properties does not match index', async () => { + try { + await documentRepository.find(queryDataContract, 'documentJ', { + where: [ + ['b', '>', 1], + ], + orderBy: [['b', 'desc'], ['d', 'asc']], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('where clause on non indexed property error: query must be for valid indexes'); + } + }); + + validFieldNameTestCases.forEach((fieldName) => { + it(`should return valid result if "orderBy" has valid field format, ${fieldName}`, async () => { + const result = await documentRepository.find(queryDataContract, `document${fieldName}`, { + where: [ + [fieldName, '>', fieldName.startsWith('$') && !fieldName.endsWith('At') ? generateRandomIdentifier() : 1], + ], + orderBy: [[fieldName, 'asc']], + }); + + expect(result).to.be.instanceOf(StorageResult); + }); + }); + + invalidFieldNameTestCases.forEach((fieldName) => { + it(`should return invalid result if "orderBy" has invalid field format, ${fieldName}`, async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { + where: [ + ['a', '>', 1], + ], + orderBy: [['$a', 'asc']], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('where clause on non indexed property error: query must be for valid indexes'); + } + }); + }); + + it('should return invalid result if "orderBy" has wrong direction', async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { + where: [ + ['a', '>', 1], + ], + orderBy: [['a', 'a']], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('missing order by for range error: query must have an orderBy field for each range element'); + } + }); + + it('should return invalid result if "orderBy" field array has less than 2 elements (field, direction)', async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { + where: [ + ['a', '>', 1], + ], + orderBy: [['a']], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('missing order by for range error: query must have an orderBy field for each range element'); + } + }); + + it('should return invalid result if "orderBy" field array has more than 2 elements (field, direction)', async () => { + try { + await documentRepository.find(queryDataContract, 'documentNumber', { + where: [ + ['a', '>', 1], + ], + orderBy: [['a', 'asc', 'desc']], + }); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('missing order by for range error: query must have an orderBy field for each range element'); + } + }); + + validOrderByOperators.forEach(({ operator, value, documentType }) => { + it(`should return valid result if "orderBy" has valid field with valid operator (${operator}) and value (${value})" in "where" clause`, async () => { + const result = await documentRepository.find(queryDataContract, documentType, { + where: [ + ['a', operator, value], + ], + orderBy: [['a', 'asc']], + }); + + expect(result).to.be.instanceOf(StorageResult); + }); + }); + + it('should return invalid result if "orderBy" was not used with range operator', async () => { + const query = { + where: [['a', '==', 1]], + orderBy: [['b', 'asc']], + }; + + try { + await documentRepository.find(queryDataContract, 'documentK', query); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('where clause on non indexed property error: query must be for valid indexes'); + } + }); + }); + }); + }); + + describe('#delete', () => { + beforeEach(async () => { + await createDocuments(documentRepository, documents); + }); + + it('should delete Document', async () => { + let result = await documentRepository.delete( + dataContract, + document.getType(), + document.getId(), + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + result = await documentRepository.find(dataContract, document.getType(), { + where: [['$id', '==', document.getId()]], + }); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.have.lengthOf(0); + }); + + it('should delete Document in transaction', async () => { + await documentRepository + .storage + .startTransaction(); + + const result = await documentRepository.delete( + dataContract, + document.getType(), + document.getId(), + { + useTransaction: true, + }, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const query = { + where: [['$id', '==', document.getId()]], + }; + + const removedDocumentResult = await documentRepository + .find( + dataContract, + document.getType(), + { + ...query, + useTransaction: true, + }, + ); + + const removedDocument = removedDocumentResult.getValue(); + + const notRemovedDocumentsResult = await documentRepository + .find(dataContract, document.getType(), query); + + const notRemovedDocuments = notRemovedDocumentsResult.getValue(); + + await documentRepository + .storage.commitTransaction(); + + const completelyRemovedDocumentResult = await documentRepository + .find(dataContract, document.getType(), query); + + const completelyRemovedDocument = completelyRemovedDocumentResult.getValue(); + + expect(removedDocument).to.have.lengthOf(0); + expect(notRemovedDocuments).to.be.not.null(); + expect(notRemovedDocuments[0].toBuffer()).to.deep.equal(document.toBuffer()); + expect(completelyRemovedDocument).to.have.lengthOf(0); + }); + + it('should restore document if transaction aborted', async () => { + await documentRepository + .storage + .startTransaction(); + + await documentRepository.delete( + dataContract, + document.getType(), + document.getId(), + { + useTransaction: true, + }, + ); + + const query = { + where: [['$id', '==', document.getId()]], + }; + + // Document should be removed in transaction + + const removedDocumentsResult = await documentRepository.find( + dataContract, + document.getType(), + { + ...query, + useTransaction: true, + }, + ); + + const removedDocuments = removedDocumentsResult.getValue(); + + expect(removedDocuments).to.have.lengthOf(0); + + // But still exists in main database + + const removedDocumentsWithoutTransactionResult = await documentRepository + .find(dataContract, document.getType(), query); + + const removedDocumentsWithoutTransaction = removedDocumentsWithoutTransactionResult + .getValue(); + + expect(removedDocumentsWithoutTransaction).to.not.have.lengthOf(0); + expect(removedDocumentsWithoutTransaction[0].toBuffer()).to.deep.equal(document.toBuffer()); + + await documentRepository + .storage + .abortTransaction(); + + const restoredDocumentsResult = await documentRepository + .find(dataContract, document.getType(), query); + + const restoredDocuments = restoredDocumentsResult.getValue(); + + expect(restoredDocuments).to.not.have.lengthOf(0); + expect(restoredDocuments[0].toBuffer()).to.deep.equal(document.toBuffer()); + }); + + it('should not delete Document on dry run', async () => { + const result = await documentRepository.delete( + dataContract, + document.getType(), + document.getId(), + { + dryRun: true, + }, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const query = { + where: [['$id', '==', document.getId()]], + }; + + const removedDocumentsResult = await documentRepository + .find(dataContract, document.getType(), query); + + const removedDocuments = removedDocumentsResult + .getValue(); + + expect(removedDocuments).to.not.have.lengthOf(0); + expect(removedDocuments[0].toBuffer()).to.deep.equal(document.toBuffer()); + }); + }); + + describe('#prove', () => { + // TODO do we need to check prove result with every single find test request? + + beforeEach(async () => { + await createDocuments(documentRepository, documents); + }); + + it('should return proof for all existing documents', async () => { + const result = await documentRepository.prove(dataContract, document.getType()); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceOf(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + // TODO enable this test when we support transactions + it.skip('should return proof for all existing documents in transaction', async () => { + await documentRepository + .storage + .startTransaction(); + + const result = await documentRepository + .prove(dataContract, document.getType(), { + useTransaction: true, + }); + + await documentRepository + .storage + .stopTransaction(); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceOf(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + }); + + describe('#proveManyDocumentsFromDifferentContracts', () => { + beforeEach(async () => { + await createDocuments(documentRepository, documents); + }); + + it('should return proof for all existing documents', async () => { + const documentsToProve = documents.map((doc) => ({ + dataContractId: doc.getDataContractId().toBuffer(), + documentId: doc.getId().toBuffer(), + type: doc.getType(), + })); + + const result = await documentRepository.proveManyDocumentsFromDifferentContracts( + documentsToProve, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceOf(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + it('should return proof non existing documents', async () => { + const documentsToProve = [{ + dataContractId: generateRandomIdentifier().toBuffer(), + documentId: generateRandomIdentifier().toBuffer(), + type: 'unknownType', + }]; + + const result = await documentRepository.proveManyDocumentsFromDifferentContracts( + documentsToProve, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceOf(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + it('should return proof for existing and non existing documents', async () => { + const documentsToProve = documents.map((doc) => ({ + dataContractId: doc.getDataContractId().toBuffer(), + documentId: doc.getId().toBuffer(), + type: doc.getType(), + })); + + documentsToProve.push({ + dataContractId: generateRandomIdentifier().toBuffer(), + documentId: generateRandomIdentifier().toBuffer(), + type: 'unknownType', + }); + + const result = await documentRepository.proveManyDocumentsFromDifferentContracts( + documentsToProve, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceOf(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + }); +}); diff --git a/packages/js-drive/test/integration/document/fetchDataContractFactory.spec.js b/packages/js-drive/test/integration/document/fetchDataContractFactory.spec.js new file mode 100644 index 00000000000..840150f97cf --- /dev/null +++ b/packages/js-drive/test/integration/document/fetchDataContractFactory.spec.js @@ -0,0 +1,75 @@ +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); + +const InvalidQueryError = require('../../../lib/document/errors/InvalidQueryError'); + +const createTestDIContainer = require('../../../lib/test/createTestDIContainer'); +const StorageResult = require('../../../lib/storage/StorageResult'); + +describe('fetchDataContractFactory', () => { + let fetchDataContract; + let contractId; + let dataContractRepository; + let dataContract; + let container; + + beforeEach(async () => { + container = await createTestDIContainer(); + + dataContractRepository = container.resolve('dataContractRepository'); + + dataContract = getDataContractFixture(); + + contractId = dataContract.getId(); + + const createInitialStateStructure = container.resolve('createInitialStateStructure'); + await createInitialStateStructure(); + + await dataContractRepository.store(dataContract); + + fetchDataContract = container.resolve('fetchDataContract'); + }); + + afterEach(async () => { + if (container) { + await container.dispose(); + } + }); + + it('should fetch DataContract for specified contract ID and document type', async () => { + const result = await fetchDataContract(contractId); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDataContract = result.getValue(); + + expect(foundDataContract.toObject()).to.deep.equal(dataContract.toObject()); + }); + + it('should throw InvalidQueryError if contract ID is not valid', async () => { + contractId = 'something'; + + try { + await fetchDataContract(contractId); + + expect.fail('should throw InvalidQueryError'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('invalid data contract ID: Identifier expects Buffer'); + } + }); + + it('should throw InvalidQueryError if contract ID does not exist', async () => { + contractId = generateRandomIdentifier(); + + try { + await fetchDataContract(contractId); + + expect.fail('should throw InvalidQueryError'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal(`data contract ${contractId} not found`); + } + }); +}); diff --git a/packages/js-drive/test/integration/document/fetchDocumentsFactory.spec.js b/packages/js-drive/test/integration/document/fetchDocumentsFactory.spec.js new file mode 100644 index 00000000000..41cec5090d0 --- /dev/null +++ b/packages/js-drive/test/integration/document/fetchDocumentsFactory.spec.js @@ -0,0 +1,214 @@ +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); + +const InvalidQueryError = require('../../../lib/document/errors/InvalidQueryError'); + +const createTestDIContainer = require('../../../lib/test/createTestDIContainer'); +const StorageResult = require('../../../lib/storage/StorageResult'); + +describe('fetchDocumentsFactory', () => { + let fetchDocuments; + let documentType; + let contractId; + let document; + let dataContractRepository; + let documentRepository; + let dataContract; + let container; + + beforeEach(async () => { + container = await createTestDIContainer(); + + dataContractRepository = container.resolve('dataContractRepository'); + documentRepository = container.resolve('documentRepository'); + + dataContract = getDataContractFixture(); + + contractId = dataContract.getId(); + + [document] = getDocumentsFixture(dataContract); + + documentType = document.getType(); + + dataContract.documents[documentType].indices = [ + { + properties: [ + { name: 'asc' }, + ], + }, + ]; + + const createInitialStateStructure = container.resolve('createInitialStateStructure'); + await createInitialStateStructure(); + + await dataContractRepository.store(dataContract); + + fetchDocuments = container.resolve('fetchDocuments'); + }); + + afterEach(async () => { + if (container) { + await container.dispose(); + } + }); + + it('should fetch Documents for specified contract ID and document type', async () => { + await documentRepository.create(document); + + const result = await fetchDocuments(contractId, documentType); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.have.lengthOf(1); + + const [actualDocument] = foundDocuments; + + expect(actualDocument.toObject()).to.deep.equal(document.toObject()); + }); + + it('should fetch Documents for specified contract id, document type and name', async () => { + await documentRepository.create(document); + + const query = { where: [['name', '==', document.get('name')]] }; + + const result = await fetchDocuments(contractId, documentType, query); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.be.an('array'); + expect(foundDocuments).to.have.lengthOf(1); + + const [actualDocument] = foundDocuments; + + expect(actualDocument.toObject()).to.deep.equal(document.toObject()); + }); + + it('should return empty array for specified contract ID, document type and name not exist', async () => { + await documentRepository.create(document); + + const query = { where: [['name', '==', 'unknown']] }; + + const result = await fetchDocuments(contractId, documentType, query); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.deep.equal([]); + }); + + it('should fetch documents by an equal date', async () => { + const indexedDocument = getDocumentsFixture(dataContract)[3]; + + await documentRepository.create(indexedDocument); + + const query = { + where: [ + ['$createdAt', '==', indexedDocument.getCreatedAt().getTime()], + ], + }; + + const result = await fetchDocuments(contractId, 'indexedDocument', query); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments[0].toObject()).to.deep.equal( + indexedDocument.toObject(), + ); + }); + + it('should fetch documents by a date range', async () => { + const [, , , indexedDocument] = getDocumentsFixture(dataContract); + + await documentRepository.create(indexedDocument); + + const startDate = new Date(); + startDate.setSeconds(startDate.getSeconds() - 10); + + const endDate = new Date(); + endDate.setSeconds(endDate.getSeconds() + 10); + + const query = { + where: [ + ['$createdAt', '>', startDate.getTime()], + ['$createdAt', '<=', endDate.getTime()], + ], + orderBy: [['$createdAt', 'asc']], + }; + + const result = await fetchDocuments(contractId, 'indexedDocument', query); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments[0].toObject()).to.deep.equal( + indexedDocument.toObject(), + ); + }); + + it('should fetch empty array in case date is out of range', async () => { + const [, , , indexedDocument] = getDocumentsFixture(dataContract); + + await documentRepository.create(indexedDocument); + + const startDate = new Date(); + startDate.setSeconds(startDate.getSeconds() + 10); + + const endDate = new Date(); + endDate.setSeconds(endDate.getSeconds() + 20); + + const query = { + where: [ + ['$createdAt', '>', startDate.getTime()], + ['$createdAt', '<=', endDate.getTime()], + ], + orderBy: [['$createdAt', 'asc']], + }; + + const result = await fetchDocuments(contractId, 'indexedDocument', query); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const foundDocuments = result.getValue(); + + expect(foundDocuments).to.have.length(0); + }); + + it('should throw InvalidQueryError if searching by non indexed fields', async () => { + await documentRepository.create(document); + + const query = { where: [['lastName', '==', 'unknown']] }; + + try { + await fetchDocuments(contractId, documentType, query); + + expect.fail('should throw InvalidQueryError'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + } + }); + + it('should throw InvalidQueryError if type does not exist', async () => { + documentType = 'Unknown'; + + try { + await fetchDocuments(contractId, documentType); + + expect.fail('should throw InvalidQueryError'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + expect(e.message).to.equal('document type Unknown is not defined in the data contract'); + } + }); +}); diff --git a/packages/js-drive/test/integration/document/proveDocumentsFactory.spec.js b/packages/js-drive/test/integration/document/proveDocumentsFactory.spec.js new file mode 100644 index 00000000000..a545b57bbd8 --- /dev/null +++ b/packages/js-drive/test/integration/document/proveDocumentsFactory.spec.js @@ -0,0 +1,191 @@ +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); +const createTestDIContainer = require('../../../lib/test/createTestDIContainer'); +const StorageResult = require('../../../lib/storage/StorageResult'); +const InvalidQueryError = require('../../../lib/document/errors/InvalidQueryError'); + +describe('proveDocumentsFactory', () => { + let proveDocuments; + let documentType; + let contractId; + let document; + let dataContractRepository; + let documentRepository; + let dataContract; + let container; + + beforeEach(async () => { + container = await createTestDIContainer(); + + dataContractRepository = container.resolve('dataContractRepository'); + documentRepository = container.resolve('documentRepository'); + + dataContract = getDataContractFixture(); + + contractId = dataContract.getId(); + + [document] = getDocumentsFixture(dataContract); + + documentType = document.getType(); + + dataContract.documents[documentType].indices = [ + { + properties: [ + { name: 'asc' }, + ], + }, + ]; + + const createInitialStateStructure = container.resolve('createInitialStateStructure'); + await createInitialStateStructure(); + + await dataContractRepository.store(dataContract); + + proveDocuments = container.resolve('proveDocuments'); + }); + + afterEach(async () => { + if (container) { + await container.dispose(); + } + }); + + it('should return proof for specified contract ID and document type', async () => { + await documentRepository.create(document); + + const result = await proveDocuments(contractId, documentType); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceOf(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + it('should return proof for specified contract id, document type and name', async () => { + await documentRepository.create(document); + + const query = { where: [['name', '==', document.get('name')]] }; + + const result = await proveDocuments(contractId, documentType, query); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceOf(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + it('should return proof for specified contract ID, document type and name not exist', async () => { + await documentRepository.create(document); + + const query = { where: [['name', '==', 'unknown']] }; + + const result = await proveDocuments(contractId, documentType, query); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceOf(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + it('should return proof by an equal date', async () => { + const indexedDocument = getDocumentsFixture(dataContract)[3]; + + await documentRepository.create(indexedDocument); + + const query = { + where: [ + ['$createdAt', '==', indexedDocument.getCreatedAt().getTime()], + ], + }; + + const result = await proveDocuments(contractId, 'indexedDocument', query); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceOf(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + it('should return proof by a date range', async () => { + const [, , , indexedDocument] = getDocumentsFixture(dataContract); + + await documentRepository.create(indexedDocument); + + const startDate = new Date(); + startDate.setSeconds(startDate.getSeconds() - 10); + + const endDate = new Date(); + endDate.setSeconds(endDate.getSeconds() + 10); + + const query = { + where: [ + ['$createdAt', '>', startDate.getTime()], + ['$createdAt', '<=', endDate.getTime()], + ], + orderBy: [['$createdAt', 'asc']], + }; + + const result = await proveDocuments(contractId, 'indexedDocument', query); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceOf(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + it('should fetch empty array in case date is out of range', async () => { + const [, , , indexedDocument] = getDocumentsFixture(dataContract); + + await documentRepository.create(indexedDocument); + + const startDate = new Date(); + startDate.setSeconds(startDate.getSeconds() + 10); + + const endDate = new Date(); + endDate.setSeconds(endDate.getSeconds() + 20); + + const query = { + where: [ + ['$createdAt', '>', startDate.getTime()], + ['$createdAt', '<=', endDate.getTime()], + ], + orderBy: [['$createdAt', 'asc']], + }; + + const result = await proveDocuments(contractId, 'indexedDocument', query); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceOf(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + it('should throw InvalidQueryError if searching by non indexed fields', async () => { + await documentRepository.create(document); + + const query = { where: [['lastName', '==', 'unknown']] }; + + try { + await proveDocuments(contractId, documentType, query); + + expect.fail('should throw InvalidQueryError'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidQueryError); + } + }); +}); diff --git a/packages/js-drive/test/integration/fee/feesPrediction.spec.js b/packages/js-drive/test/integration/fee/feesPrediction.spec.js new file mode 100644 index 00000000000..4c8e76f0e7f --- /dev/null +++ b/packages/js-drive/test/integration/fee/feesPrediction.spec.js @@ -0,0 +1,527 @@ +const crypto = require('crypto'); + +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); +const Identity = require('@dashevo/dpp/lib/identity/Identity'); +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); + +const getBiggestPossibleIdentity = require('@dashevo/dpp/lib/identity/getBiggestPossibleIdentity'); +const getInstantAssetLockProofFixture = require('@dashevo/dpp/lib/test/fixtures/getInstantAssetLockProofFixture'); +const identityUpdateTransitionSchema = require('@dashevo/dpp/schema/identity/stateTransition/identityUpdate.json'); +const StateTransitionExecutionContext = require('@dashevo/dpp/lib/stateTransition/StateTransitionExecutionContext'); +const calculateStateTransitionFee = require('@dashevo/dpp/lib/stateTransition/fee/calculateStateTransitionFee'); + +const PrivateKey = require('@dashevo/dashcore-lib/lib/privatekey'); +const BlsSignatures = require('@dashevo/dpp/lib/bls/bls'); + +const createTestDIContainer = require('../../../lib/test/createTestDIContainer'); +const createDataContractDocuments = require('../../../lib/test/fixtures/createDataContractDocuments'); + +/** + * @param {DashPlatformProtocol} dpp + * @param {AbstractStateTransition} stateTransition + * @return {Promise} + */ +async function validateStateTransition(dpp, stateTransition) { + const validateBasicResult = await dpp.stateTransition.validateBasic(stateTransition); + expect(validateBasicResult.isValid()).to.be.true(); + + const validateSignatureResult = await dpp.stateTransition.validateSignature(stateTransition); + expect(validateSignatureResult.isValid()).to.be.true(); + + const validateFeeResult = await dpp.stateTransition.validateFee(stateTransition); + expect(validateFeeResult.isValid()).to.be.true(); + + const validateStateResult = await dpp.stateTransition.validateState(stateTransition); + expect(validateStateResult.isValid()).to.be.true(); + + const applyResult = await dpp.stateTransition.validateState(stateTransition); + expect(applyResult.isValid()).to.be.true(); +} + +/** + * @param {DashPlatformProtocol} dpp + * @param {GroveDBStore} groveDBStore + * @param {AbstractStateTransition} stateTransition + * @return {Promise} + */ +async function expectPredictedFeeHigherOrEqualThanActual(dpp, groveDBStore, stateTransition) { + // Execute state transition without dry run + + const actualExecutionContext = new StateTransitionExecutionContext(); + + stateTransition.setExecutionContext(actualExecutionContext); + + await validateStateTransition(dpp, stateTransition); + + // Execute state transition with dry run enabled + + const predictedExecutionContext = new StateTransitionExecutionContext(); + + predictedExecutionContext.enableDryRun(); + + stateTransition.setExecutionContext(predictedExecutionContext); + + const initialAppHash = await groveDBStore.getRootHash(); + + await validateStateTransition(dpp, stateTransition); + + // AppHash shouldn't be changed after dry run + const appHashAfterDryRun = await groveDBStore.getRootHash(); + + expect(appHashAfterDryRun).to.deep.equal(initialAppHash); + + // Compare operations + + const actualOperations = actualExecutionContext.getOperations(); + const predictedOperations = predictedExecutionContext.getOperations(); + + expect(predictedOperations).to.have.lengthOf(actualOperations.length); + + // Compare fees + + stateTransition.setExecutionContext(actualExecutionContext); + const actualFees = calculateStateTransitionFee(stateTransition); + + stateTransition.setExecutionContext(predictedExecutionContext); + const predictedFees = calculateStateTransitionFee(stateTransition); + + expect(predictedFees).to.be.greaterThanOrEqual(actualFees); + + predictedOperations.forEach((predictedOperation, i) => { + expect(predictedOperation.getStorageCost()).to.be.greaterThanOrEqual( + actualOperations[i].getStorageCost(), + ); + + expect(predictedOperation.getProcessingCost()).to.be.greaterThanOrEqual( + actualOperations[i].getProcessingCost(), + ); + }); +} + +describe('feesPrediction', () => { + let dpp; + let container; + let stateRepository; + let identity; + let groveDBStore; + + beforeEach(async function beforeEach() { + container = await createTestDIContainer(); + + const blockExecutionContext = container.resolve('blockExecutionContext'); + blockExecutionContext.getHeader = this.sinon.stub().returns( + { time: { seconds: new Date().getTime() / 1000 } }, + ); + + dpp = container.resolve('dpp'); + + stateRepository = container.resolve('stateRepository'); + groveDBStore = container.resolve('groveDBStore'); + + const createInitialStateStructure = container.resolve('createInitialStateStructure'); + await createInitialStateStructure(); + }); + + afterEach(async () => { + if (container) { + await container.dispose(); + } + }); + + describe('Identity', () => { + let assetLockPrivateKey; + let instantAssetLockProof; + let privateKeys; + + beforeEach(async function beforeEachFunction() { + assetLockPrivateKey = new PrivateKey(); + + instantAssetLockProof = getInstantAssetLockProofFixture(assetLockPrivateKey); + + identity = getBiggestPossibleIdentity(); + identity.id = instantAssetLockProof.createIdentifier(); + identity.setAssetLockProof(instantAssetLockProof); + + // Generate real keys + const { PrivateKey: BlsPrivateKey } = await BlsSignatures.getInstance(); + + privateKeys = identity.getPublicKeys().map((identityPublicKey) => { + const randomBytes = new Uint8Array(crypto.randomBytes(256)); + const privateKey = BlsPrivateKey.fromBytes(randomBytes, true); + const publicKey = privateKey.getPublicKey(); + const publicKeyBuffer = Buffer.from(publicKey.serialize()); + + identityPublicKey.setData(publicKeyBuffer); + + return Buffer.from(privateKey.serialize()); + }); + + stateRepository.verifyInstantLock = this.sinon.stub().resolves(true); + }); + + describe('IdentityCreateTransition', () => { + it('should have predicted fee more than actual fee', async () => { + const stateTransition = dpp.identity.createIdentityCreateTransition(identity); + + // Sign public keys + const publicKeys = stateTransition.getPublicKeys(); + + for (let i = 0; i < publicKeys.length; i++) { + await stateTransition.signByPrivateKey( + privateKeys[i], + IdentityPublicKey.TYPES.BLS12_381, + ); + + publicKeys[i].setSignature(stateTransition.getSignature()); + + stateTransition.setSignature(undefined); + } + + // Sign state transition + await stateTransition.signByPrivateKey( + assetLockPrivateKey, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + await expectPredictedFeeHigherOrEqualThanActual(dpp, groveDBStore, stateTransition); + }); + }); + + describe('IdentityTopUpTransition', () => { + it('should have predicted fee more than actual fee', async () => { + await stateRepository.createIdentity(identity); + + const stateTransition = dpp.identity.createIdentityTopUpTransition( + identity.getId(), + instantAssetLockProof, + ); + + await stateTransition.signByPrivateKey( + assetLockPrivateKey, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + await expectPredictedFeeHigherOrEqualThanActual(dpp, groveDBStore, stateTransition); + }); + }); + + describe('IdentityUpdateTransition', () => { + it('should have predicted fee more than actual fee', async () => { + await stateRepository.createIdentity(identity); + + const newIdentityPublicKeys = []; + const disableIdentityPublicKeys = []; + + const { PrivateKey: BlsPrivateKey } = await BlsSignatures.getInstance(); + + const newPrivateKeys = []; + for (let i = 0; i < identityUpdateTransitionSchema.properties.addPublicKeys.maxItems; i++) { + const randomBytes = new Uint8Array(crypto.randomBytes(256)); + const privateKey = BlsPrivateKey.fromBytes(randomBytes, true); + const publicKey = privateKey.getPublicKey(); + const publicKeyBuffer = Buffer.from(publicKey.serialize()); + + newPrivateKeys.push(privateKey); + + newIdentityPublicKeys.push( + new IdentityPublicKey({ + id: i + identity.getPublicKeys().length, + type: IdentityPublicKey.TYPES.BLS12_381, + data: publicKeyBuffer, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: i === 0 + ? IdentityPublicKey.SECURITY_LEVELS.MASTER : IdentityPublicKey.SECURITY_LEVELS.HIGH, + readOnly: false, + }), + ); + + disableIdentityPublicKeys.push(identity.getPublicKeyById(i)); + } + + const stateTransition = dpp.identity.createIdentityUpdateTransition( + identity, + { + add: newIdentityPublicKeys, + disable: disableIdentityPublicKeys, + }, + ); + + const [signerKey] = identity.getPublicKeys(); + + const starterPromise = Promise.resolve(null); + + await stateTransition.getPublicKeysToAdd().reduce( + (previousPromise, publicKey) => previousPromise.then(async () => { + const privateKey = newPrivateKeys[publicKey.getId() - identity.getPublicKeys().length]; + + if (!privateKey) { + throw new Error(`Private key for key ${publicKey.getId()} not found`); + } + + stateTransition.setSignaturePublicKeyId(signerKey.getId()); + + await stateTransition.signByPrivateKey(privateKey, publicKey.getType()); + + publicKey.setSignature(stateTransition.getSignature()); + + stateTransition.setSignature(undefined); + stateTransition.setSignaturePublicKeyId(undefined); + }), + starterPromise, + ); + + await stateTransition.sign( + identity.getPublicKeyById(0), + privateKeys[0], + ); + + await expectPredictedFeeHigherOrEqualThanActual(dpp, groveDBStore, stateTransition); + }); + }); + }); + + describe('DataContract', () => { + let dataContract; + let privateKey; + + beforeEach(async () => { + // Create identity + + privateKey = new PrivateKey(); + + identity = new Identity({ + protocolVersion: 1, + id: generateRandomIdentifier().toBuffer(), + publicKeys: [ + { + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + data: Buffer.alloc(48).fill(255), + }, + { + id: 1, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.HEIGHT, + readOnly: false, + data: privateKey.toPublicKey().toBuffer(), + }, + ], + balance: Number.MAX_VALUE, + revision: 0, + }); + + await stateRepository.createIdentity(identity); + + // Generate Data Contract + + const documents = createDataContractDocuments(); + + dataContract = dpp.dataContract.create(identity.getId(), documents); + }); + + describe('DataContractCreate', () => { + it('should have predicted fee more than actual fee', async () => { + const stateTransition = dpp.dataContract.createDataContractCreateTransition(dataContract); + + await stateTransition.sign( + identity.getPublicKeyById(1), + privateKey, + ); + + await expectPredictedFeeHigherOrEqualThanActual(dpp, groveDBStore, stateTransition); + }); + }); + + describe('DataContractUpdate', () => { + it('should have predicted fee more than actual fee', async () => { + await stateRepository.storeDataContract(dataContract); + + dataContract.setVersion(2); + + const documents = dataContract.getDocuments(); + + documents.newDoc = { + type: 'object', + indices: [ + { + name: 'onwerIdToUser', + properties: [ + { $ownerId: 'asc' }, + { user: 'asc' }, + ], + unique: true, + }, + ], + properties: { + user: { + type: 'string', + maxLength: 63, + }, + publicKey: { + type: 'array', + byteArray: true, + maxItems: 33, + }, + }, + required: ['user', 'publicKey'], + additionalProperties: false, + }; + + dataContract.setDocuments(documents); + + const stateTransition = dpp.dataContract.createDataContractUpdateTransition(dataContract); + + await stateTransition.sign( + identity.getPublicKeyById(1), + privateKey, + ); + + await expectPredictedFeeHigherOrEqualThanActual(dpp, groveDBStore, stateTransition); + }); + }); + }); + + describe('Document', () => { + let documents; + let dataContract; + let privateKey; + + beforeEach(async () => { + // Create Identity + + privateKey = new PrivateKey(); + + identity = new Identity({ + protocolVersion: 1, + id: generateRandomIdentifier().toBuffer(), + publicKeys: [ + { + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + data: Buffer.alloc(48).fill(255), + }, + { + id: 1, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.HEIGHT, + readOnly: false, + data: privateKey.toPublicKey().toBuffer(), + }, + ], + balance: Number.MAX_VALUE, + revision: 0, + }); + + await stateRepository.createIdentity(identity); + + // Create Data Contract + + const documentTypes = createDataContractDocuments(); + + dataContract = dpp.dataContract.create(identity.getId(), documentTypes); + + await stateRepository.storeDataContract(dataContract); + + // Create documents + + documents = []; + + let i = 0; + for (const documentType of Object.keys(documentTypes)) { + const data = {}; + + for (const propertyName of Object.keys(documentTypes[documentType].properties)) { + data[propertyName] = `${crypto.randomBytes(31).toString('hex')}a`; + } + + const document = dpp.document.create( + dataContract, + identity.getId(), + documentType, + data, + ); + + documents.push(document); + + i += 1; + + if (i === 10) { + break; + } + } + }); + + describe('DocumentsBatchTransition', () => { + context('create', () => { + it('should have predicted fee more than actual fee', async () => { + const stateTransition = dpp.document.createStateTransition({ + create: documents, + }); + + await stateTransition.sign( + identity.getPublicKeyById(1), + privateKey, + ); + + await expectPredictedFeeHigherOrEqualThanActual(dpp, groveDBStore, stateTransition); + }); + }); + + context('replace', () => { + it('should have predicted fee more than actual fee', async () => { + for (const document of documents) { + await stateRepository.createDocument(document); + } + + for (const document of documents) { + const data = document.getData(); + + for (const propertyName of Object.keys(data)) { + data[propertyName] = `${crypto.randomBytes(31).toString('hex')}b`; + } + + document.setData(data); + } + + const stateTransition = dpp.document.createStateTransition({ + replace: documents, + }); + + await stateTransition.sign( + identity.getPublicKeyById(1), + privateKey, + ); + + await expectPredictedFeeHigherOrEqualThanActual(dpp, groveDBStore, stateTransition); + }); + }); + + context('delete', () => { + it('should have predicted fee more than actual fee', async () => { + for (const document of documents) { + await stateRepository.createDocument(document); + } + + const stateTransition = dpp.document.createStateTransition({ + delete: documents, + }); + + await stateTransition.sign( + identity.getPublicKeyById(1), + privateKey, + ); + + await expectPredictedFeeHigherOrEqualThanActual(dpp, groveDBStore, stateTransition); + }); + }); + }); + }); +}); diff --git a/packages/js-drive/test/integration/groveDB/GroveDBStore.spec.js b/packages/js-drive/test/integration/groveDB/GroveDBStore.spec.js new file mode 100644 index 00000000000..843210e7bf9 --- /dev/null +++ b/packages/js-drive/test/integration/groveDB/GroveDBStore.spec.js @@ -0,0 +1,499 @@ +const rimraf = require('rimraf'); + +const Drive = require('@dashevo/rs-drive/node/Drive'); +const GroveDBStore = require('../../../lib/storage/GroveDBStore'); +const logger = require('../../../lib/util/noopLogger'); +const StorageResult = require('../../../lib/storage/StorageResult'); + +describe('GroveDBStore', () => { + let rsDrive; + let store; + let key; + let value; + let testTreePath; + let otherTreePath; + + beforeEach(async () => { + rsDrive = new Drive('./db/grovedb_test'); + + store = new GroveDBStore(rsDrive, logger); + + testTreePath = [Buffer.from('testTree')]; + otherTreePath = [Buffer.from('otherTree')]; + + await store.createTree([], testTreePath[0]); + await store.createTree([], otherTreePath[0]); + + key = Buffer.alloc(32).fill(1); + value = Buffer.alloc(32).fill(2); + }); + + afterEach(async () => { + await rsDrive.close(); + rimraf.sync('./db/grovedb_test'); + }); + + describe('#put', () => { + it('should store value', async () => { + const result = await store.put(testTreePath, key, value); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const actualValue = await rsDrive.getGroveDB().get(testTreePath, key); + + expect(actualValue).to.be.deep.equal({ + type: 'item', + value, + }); + }); + + it('should store value in transaction', async () => { + await store.startTransaction(); + + // store data in transaction + await store.put(testTreePath, key, value, { + useTransaction: true, + }); + + // check we don't have data in db before commit + try { + await rsDrive.getGroveDB().get(testTreePath, key); + + expect.fail('Should fail with NotFoundError error'); + } catch (e) { + expect(e.message.startsWith('path key not found: key not found in Merk')).to.be.true(); + } + + // check we can't fetch data without transaction + const notFoundValueResult = await store.get(testTreePath, key); + + expect(notFoundValueResult.getValue()).to.be.null(); + + // check we can fetch data inside transaction + const valueFromTransactionResult = await store.get(testTreePath, key, { + useTransaction: true, + }); + + expect(valueFromTransactionResult).to.be.instanceOf(StorageResult); + expect(valueFromTransactionResult.getOperations().length).to.be.greaterThan(0); + + expect(valueFromTransactionResult.getValue()).to.deep.equal(value); + + await store.commitTransaction(); + + // check we have data in db after commit + const storedValue = await rsDrive.getGroveDB().get(testTreePath, key); + + expect(storedValue).to.deep.equal({ + type: 'item', + value, + }); + }); + + it('should not store value on dry run', async () => { + const result = await store.put(testTreePath, key, value, { dryRun: true }); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + try { + await rsDrive.getGroveDB().get(testTreePath, key); + + expect.fail('should with NotFoundError error'); + } catch (e) { + expect(e.message.startsWith('path key not found: key not found in Merk')).to.be.true(); + } + }); + }); + + describe('#get', () => { + it('should return null if key was not found', async () => { + const result = await store.get(testTreePath, key); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.be.null(); + }); + + it('should return stored value', async () => { + await rsDrive.getGroveDB().insert( + testTreePath, + key, + { type: 'item', epoch: 0, value }, + false, + ); + + const result = await store.get(testTreePath, key); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.deep.equal(value); + }); + + it('should return stored value with transaction', async () => { + await store.put(testTreePath, key, value); + + await store.startTransaction(); + + const result = await store.get(testTreePath, key, { + useTransaction: true, + }); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.deep.equal(value); + }); + + it('should return null on dry run', async () => { + await rsDrive.getGroveDB().insert( + testTreePath, + key, + { type: 'item', epoch: 0, value }, + false, + ); + + const result = await store.get(testTreePath, key, { dryRun: true }); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.be.null(); + }); + }); + + describe('#putReference', () => { + it('should put an item by reference', async () => { + await store.put(otherTreePath, key, value); + + const result = await store.putReference(testTreePath, key, [otherTreePath[0], key]); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const getResult = await store.get(testTreePath, key); + + expect(getResult.getValue()).to.deep.equal(value); + }); + + it('should put an item by reference in transaction', async () => { + await store.put(otherTreePath, key, value); + + await store.startTransaction(); + + const result = await store.putReference(testTreePath, key, [otherTreePath[0], key], { + useTransaction: true, + }); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const nonTxResult = await store.get(testTreePath, key); + + expect(nonTxResult.getValue()).to.be.null(); + + const txResult = await store.get(testTreePath, key, { + useTransaction: true, + }); + + expect(txResult.getValue()).to.deep.equal(value); + }); + + it('should not put an item by reference on dry run', async () => { + await store.put(otherTreePath, key, value); + + const result = await store.putReference( + testTreePath, + key, + [otherTreePath[0], key], + { dryRun: true }, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const getResult = await store.get(testTreePath, key); + + expect(getResult.getValue()).to.be.null(); + }); + }); + + describe('#query', () => { + it('should return results', async () => { + await store.put(testTreePath, key, value); + + const result = await store.query({ + path: testTreePath, + query: { + query: { + items: [ + { + type: 'rangeFull', + }, + ], + }, + }, + }); + + expect(result).to.have.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.have.lengthOf(1); + + const [item] = result.getValue(); + + expect(item).to.deep.equal(value); + }); + }); + + describe('#proveQuery', () => { + it('should return proof', async () => { + await store.put(testTreePath, key, value); + + const result = await store.proveQuery({ + path: testTreePath, + query: { + query: { + items: [ + { + type: 'rangeFull', + }, + ], + }, + }, + }); + + expect(result).to.have.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.be.an.instanceOf(Buffer); + expect(result.getValue().length).to.be.greaterThan(0); + }); + }); + + describe('#delete', () => { + it('should delete value', async () => { + await store.put(testTreePath, key, value); + + const result = await store.delete(testTreePath, key); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + try { + await rsDrive.getGroveDB().get(testTreePath, key); + + expect.fail('should throw no value found for key error'); + } catch (e) { + expect(e.message.startsWith('path key not found: key not found in Merk')).to.be.true(); + } + }); + + it('should delete value in transaction', async () => { + await store.put(testTreePath, key, value); + + await store.startTransaction(); + + // Delete a value from transaction + const result = await store.delete(testTreePath, key, { + useTransaction: true, + }); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + // Now it should be absent there + const valueFromTransactionResult = await store.get(testTreePath, key, { + useTransaction: true, + }); + + expect(valueFromTransactionResult.getValue()).to.be.null(); + + // But should be still present in store + const valueFromStoreResult = await store.get(testTreePath, key); + expect(valueFromStoreResult.getValue()).to.deep.equal(value); + + await store.commitTransaction(); + + // When we commit transaction this key should disappear from store too + const valueFromStoreAfterCommitResult = await store.get(testTreePath, key); + expect(valueFromStoreAfterCommitResult.getValue()).to.be.null(); + }); + + it('should not delete value on dry run', async () => { + await store.put(testTreePath, key, value); + + const result = await store.delete(testTreePath, key, { dryRun: true }); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const storedValue = await rsDrive.getGroveDB().get(testTreePath, key); + + expect(storedValue).to.deep.equal({ + type: 'item', + value, + }); + }); + }); + + describe('#getAux', () => { + it('should get an auxiliary data from db', async () => { + await rsDrive.getGroveDB().putAux(key, value); + + const result = await store.getAux(key); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.deep.equal(value); + }); + + it('should get an auxiliary data from db with transaction', async () => { + await rsDrive.getGroveDB().putAux(key, value); + + await store.startTransaction(); + + const result = await store.getAux(key, { + useTransaction: true, + }); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.deep.equal(value); + }); + + it('should return null on dry run', async () => { + await rsDrive.getGroveDB().putAux(key, value); + + const result = await store.getAux(key, { dryRun: true }); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.be.null(value); + }); + }); + + describe('#putAux', () => { + it('should put an auxiliary data', async () => { + await store.putAux(key, value); + + const result = await rsDrive.getGroveDB().getAux(key); + + expect(result).to.deep.equal(value); + }); + + it('should put an auxiliary data using transaction', async () => { + await store.startTransaction(); + + await store.putAux(key, value, { + useTransaction: true, + }); + + const nonTxResult = await rsDrive.getGroveDB().getAux(key); + + expect(nonTxResult).to.be.null(); + + const txResult = await rsDrive.getGroveDB().getAux(key, true); + + expect(txResult).to.deep.equal(value); + }); + + it('should not put an auxiliary data on dry run', async () => { + await store.putAux(key, value, { dryRun: true }); + + const result = await rsDrive.getGroveDB().getAux(key); + + expect(result).to.be.null(); + }); + }); + + describe('#deleteAux', () => { + it('should delete an auxiliary data', async () => { + await store.putAux(key, value); + + const getResult = await store.getAux(key); + + expect(getResult.getValue()).to.deep.equal(value); + + const deleteResult = await store.deleteAux(key); + + expect(deleteResult).to.be.instanceOf(StorageResult); + expect(deleteResult.getOperations().length).to.be.greaterThan(0); + + const deletedValue = await rsDrive.getGroveDB().getAux(key); + + expect(deletedValue).to.be.null(); + }); + + it('should delete an auxiliary data within transaction', async () => { + await store.putAux(key, value); + + await store.startTransaction(); + + const deleteResult = await store.deleteAux(key, { + useTransaction: true, + }); + + expect(deleteResult).to.be.instanceOf(StorageResult); + expect(deleteResult.getOperations().length).to.be.greaterThan(0); + + const nonTxResult = await store.getAux(key); + + expect(nonTxResult.getValue()).to.deep.equal(value); + + const txResult = await store.getAux(key, { + useTransaction: true, + }); + + expect(txResult.getValue()).to.be.null(); + }); + + it('should not delete an auxiliary data on dry run', async () => { + await store.putAux(key, value); + + const getResult = await store.getAux(key); + + expect(getResult.getValue()).to.deep.equal(value); + + const deleteResult = await store.deleteAux(key, { dryRun: true }); + + expect(deleteResult).to.be.instanceOf(StorageResult); + expect(deleteResult.getOperations().length).to.be.greaterThan(0); + + const deletedValue = await rsDrive.getGroveDB().getAux(key); + + expect(deletedValue).to.deep.equal(value); + }); + }); + + describe('#getRootHash', () => { + it('should return a null hash for empty store', async () => { + await rsDrive.close(); + + rimraf.sync('./db/grovedb_test'); + + rsDrive = new Drive('./db/grovedb_test'); + store = new GroveDBStore(rsDrive, logger, 'testStore'); + + const result = await store.getRootHash(); + + expect(result).to.deep.equal(Buffer.alloc(32).fill(0)); + }); + + it('should return a root hash for store with value', async () => { + await store.put(testTreePath, key, value); + + const valueHash = Buffer.from('4761772ecb332ab96912b384fe8934c85d447e04d151932d385516a88e3dd098', 'hex'); + + const result = await store.getRootHash(); + + expect(result).to.deep.equal(valueHash); + }); + }); +}); diff --git a/packages/js-drive/test/integration/identity/IdentityStoreRepository.spec.js b/packages/js-drive/test/integration/identity/IdentityStoreRepository.spec.js new file mode 100644 index 00000000000..5682c4982e5 --- /dev/null +++ b/packages/js-drive/test/integration/identity/IdentityStoreRepository.spec.js @@ -0,0 +1,505 @@ +const fs = require('fs'); +const Drive = require('@dashevo/rs-drive'); +const decodeProtocolEntityFactory = require('@dashevo/dpp/lib/decodeProtocolEntityFactory'); +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); +const Identity = require('@dashevo/dpp/lib/identity/Identity'); +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); +const GroveDBStore = require('../../../lib/storage/GroveDBStore'); +const IdentityStoreRepository = require('../../../lib/identity/IdentityStoreRepository'); +const logger = require('../../../lib/util/noopLogger'); +const StorageResult = require('../../../lib/storage/StorageResult'); + +describe('IdentityStoreRepository', () => { + let rsDrive; + let store; + let repository; + let decodeProtocolEntity; + let identity; + + beforeEach(async () => { + rsDrive = new Drive('./db/grovedb_test'); + store = new GroveDBStore(rsDrive, logger, 'blockchainStateTestStore'); + + decodeProtocolEntity = decodeProtocolEntityFactory(); + + repository = new IdentityStoreRepository(store, decodeProtocolEntity); + identity = getIdentityFixture(); + }); + + afterEach(async () => { + await rsDrive.close(); + + fs.rmSync('./db/grovedb_test', { recursive: true, force: true }); + }); + + describe('#create', () => { + beforeEach(async () => { + await store.createTree([], IdentityStoreRepository.TREE_PATH[0]); + }); + + it('should create an identity', async () => { + const result = await repository.create( + identity, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const encodedIdentityResult = await store.get( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + ); + + const [protocolVersion, rawIdentity] = decodeProtocolEntity( + encodedIdentityResult.getValue(), + ); + + rawIdentity.protocolVersion = protocolVersion; + + const fetchedIdentity = new Identity(rawIdentity); + + expect(fetchedIdentity.toObject()).to.deep.equal(identity.toObject()); + }); + + it('should store identity using transaction', async () => { + await store.startTransaction(); + + await repository.create( + identity, + { useTransaction: true }, + ); + + const notFoundIdentityResult = await store.get( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + { useTransaction: false }, + ); + + expect(notFoundIdentityResult.isNull()).to.be.true(); + + const identityTransactionResult = await store.get( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + { useTransaction: true }, + ); + + let [protocolVersion, rawIdentity] = decodeProtocolEntity( + identityTransactionResult.getValue(), + ); + + rawIdentity.protocolVersion = protocolVersion; + + let fetchedIdentity = new Identity(rawIdentity); + + expect(fetchedIdentity.toObject()).to.deep.equal(identity.toObject()); + + await store.commitTransaction(); + + const committedIdentityResult = await store.get( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + { useTransaction: true }, + ); + + [protocolVersion, rawIdentity] = decodeProtocolEntity(committedIdentityResult.getValue()); + + rawIdentity.protocolVersion = protocolVersion; + + fetchedIdentity = new Identity(rawIdentity); + + expect(fetchedIdentity.toObject()).to.deep.equal(identity.toObject()); + }); + }); + + describe('#update', () => { + beforeEach(async () => { + await store.createTree([], IdentityStoreRepository.TREE_PATH[0]); + }); + + it('should update identity', async () => { + await repository.create( + identity, + ); + + const [, publicKey] = identity.getPublicKeys(); + + publicKey.setReadOnly(true); + + const result = await repository.update( + identity, + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const encodedIdentityResult = await store.get( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + ); + + const [protocolVersion, rawIdentity] = decodeProtocolEntity( + encodedIdentityResult.getValue(), + ); + + rawIdentity.protocolVersion = protocolVersion; + + const fetchedIdentity = new Identity(rawIdentity); + + expect(fetchedIdentity.toObject()).to.deep.equal(identity.toObject()); + }); + + it('should store identity using transaction', async () => { + // Create identity + await repository.create( + identity, + ); + + await store.startTransaction(); + + // Update identity + const updatedIdentity = new Identity(identity.toObject()); + + const [, publicKey] = updatedIdentity.getPublicKeys(); + + publicKey.setReadOnly(true); + + await repository.update( + updatedIdentity, + { useTransaction: true }, + ); + + const previousIdentityResult = await store.get( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + { useTransaction: false }, + ); + + let [protocolVersion, rawIdentity] = decodeProtocolEntity(previousIdentityResult.getValue()); + + rawIdentity.protocolVersion = protocolVersion; + + let fetchedIdentity = new Identity(rawIdentity); + + expect(fetchedIdentity.toObject()).to.deep.equal(identity.toObject()); + + const identityTransactionResult = await store.get( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + { useTransaction: true }, + ); + + [protocolVersion, rawIdentity] = decodeProtocolEntity( + identityTransactionResult.getValue(), + ); + + rawIdentity.protocolVersion = protocolVersion; + + fetchedIdentity = new Identity(rawIdentity); + + expect(fetchedIdentity.toObject()).to.deep.equal(updatedIdentity.toObject()); + + await store.commitTransaction(); + + const committedIdentityResult = await store.get( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + { useTransaction: true }, + ); + + [protocolVersion, rawIdentity] = decodeProtocolEntity(committedIdentityResult.getValue()); + + rawIdentity.protocolVersion = protocolVersion; + + fetchedIdentity = new Identity(rawIdentity); + + expect(fetchedIdentity.toObject()).to.deep.equal(updatedIdentity.toObject()); + }); + }); + + describe('#fetch', () => { + beforeEach(async () => { + await store.createTree([], IdentityStoreRepository.TREE_PATH[0]); + }); + + it('should fetch null if identity not found', async () => { + const result = await repository.fetch(identity.getId()); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.be.null(); + }); + + it('should fetch an identity', async () => { + await store.createTree(IdentityStoreRepository.TREE_PATH, identity.getId().toBuffer()); + + await store.put( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + identity.toBuffer(), + ); + + const result = await repository.fetch(identity.getId()); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const storedIdentity = result.getValue(); + + expect(storedIdentity).to.be.an.instanceof(Identity); + expect(storedIdentity.toObject()).to.deep.equal(identity.toObject()); + }); + + it('should fetch an identity using transaction', async () => { + await store.startTransaction(); + + await store.createTree( + IdentityStoreRepository.TREE_PATH, + identity.getId().toBuffer(), + { useTransaction: true }, + ); + + await store.put( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + identity.toBuffer(), + { useTransaction: true }, + ); + + const notFoundIdentityResult = await repository.fetch(identity.getId(), { + useTransaction: false, + }); + + expect(notFoundIdentityResult.getValue()).to.be.null(); + + const transactionalIdentityResult = await repository.fetch(identity.getId(), { + useTransaction: true, + }); + + const transactionalIdentity = transactionalIdentityResult.getValue(); + + expect(transactionalIdentity).to.be.an.instanceof(Identity); + expect(transactionalIdentity.toObject()).to.deep.equal(identity.toObject()); + + await store.commitTransaction(); + + const storedIdentityResult = await repository.fetch(identity.getId()); + + const storedIdentity = storedIdentityResult.getValue(); + + expect(storedIdentity).to.be.an.instanceof(Identity); + expect(storedIdentity.toObject()).to.deep.equal(identity.toObject()); + }); + }); + + describe('#createTree', () => { + it('should create a tree', async () => { + const result = await repository.createTree(); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const data = await store.db.get( + [], + IdentityStoreRepository.TREE_PATH[0], + ); + + expect(data).to.deep.equal({ + type: 'tree', + value: Buffer.alloc(32), + }); + }); + }); + + describe('#prove', () => { + beforeEach(async () => { + await store.createTree([], IdentityStoreRepository.TREE_PATH[0]); + }); + + it('should return prove if identity does not exist', async () => { + const result = await repository.prove(identity.getId()); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceof(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + it('should return proof', async () => { + await store.createTree(IdentityStoreRepository.TREE_PATH, identity.getId().toBuffer()); + + await store.put( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + identity.toBuffer(), + ); + + const result = await repository.prove(identity.getId()); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceof(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + // TODO enable this test when we support transactions + it.skip('should return proof using transaction', async () => { + await store.startTransaction(); + + await store.createTree( + IdentityStoreRepository.TREE_PATH, + identity.getId().toBuffer(), + { useTransaction: true }, + ); + + await store.put( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + identity.toBuffer(), + { useTransaction: true }, + ); + + const notFoundProof = await repository.prove(identity.getId(), { + useTransaction: false, + }); + + expect(notFoundProof.getValue()).to.be.null(); + + const transactionalIdentityResult = await repository.prove(identity.getId(), { + useTransaction: true, + }); + + const transactionalProof = transactionalIdentityResult.getValue(); + + expect(transactionalProof).to.be.an.instanceof(Buffer); + expect(transactionalProof.length).to.be.greaterThan(0); + + await store.commitTransaction(); + + const storedIdentityResult = await repository.prove(identity.getId()); + + const storedProof = storedIdentityResult.getValue(); + + expect(storedProof).to.be.an.instanceof(Buffer); + expect(storedProof.length).to.be.greaterThan(0); + }); + }); + + describe('#proveMany', () => { + let identity2; + + beforeEach(async () => { + identity2 = new Identity({ + protocolVersion: 1, + id: generateRandomIdentifier().toBuffer(), + publicKeys: [ + { + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + data: Buffer.alloc(48).fill(255), + }, + ], + balance: 10, + revision: 0, + }); + + await store.createTree([], IdentityStoreRepository.TREE_PATH[0]); + }); + + it('should return proof if identity does not exist', async () => { + // Create only first identity + await store.createTree(IdentityStoreRepository.TREE_PATH, identity.getId().toBuffer()); + + await store.put( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + identity.toBuffer(), + ); + + const result = await repository.proveMany([identity.getId(), identity2.getId()]); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceof(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + it('should return proof', async () => { + await store.createTree(IdentityStoreRepository.TREE_PATH, identity.getId().toBuffer()); + + await store.put( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + identity.toBuffer(), + ); + + const result = await repository.proveMany([identity.getId(), identity2.getId()]); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const proof = result.getValue(); + + expect(proof).to.be.an.instanceof(Buffer); + expect(proof.length).to.be.greaterThan(0); + }); + + // TODO enable this test when we support transactions + it.skip('should return proof using transaction', async () => { + await store.startTransaction(); + + await store.createTree( + IdentityStoreRepository.TREE_PATH, + identity.getId().toBuffer(), + { useTransaction: true }, + ); + + await store.put( + IdentityStoreRepository.TREE_PATH.concat([identity.getId().toBuffer()]), + IdentityStoreRepository.IDENTITY_KEY, + identity.toBuffer(), + { useTransaction: true }, + ); + + const notFoundProof = await repository.proveMany([identity.getId(), identity2.getId()], { + useTransaction: false, + }); + + expect(notFoundProof.getValue()).to.be.null(); + + const transactionalIdentityResult = await repository.proveMany( + [identity.getId(), identity2.getId()], + { useTransaction: true }, + ); + + const transactionalProof = transactionalIdentityResult.getValue(); + + expect(transactionalProof).to.be.an.instanceof(Buffer); + expect(transactionalProof.length).to.be.greaterThan(0); + + await store.commitTransaction(); + + const storedIdentityResult = await repository.proveMany( + [identity.getId(), identity2.getId()], + ); + + const storedProof = storedIdentityResult.getValue(); + + expect(storedProof).to.be.an.instanceof(Buffer); + expect(storedProof.length).to.be.greaterThan(0); + }); + }); +}); diff --git a/packages/js-drive/test/integration/identity/PublicKeyToIdentitiesStoreRepository.spec.js b/packages/js-drive/test/integration/identity/PublicKeyToIdentitiesStoreRepository.spec.js new file mode 100644 index 00000000000..373db5be3ca --- /dev/null +++ b/packages/js-drive/test/integration/identity/PublicKeyToIdentitiesStoreRepository.spec.js @@ -0,0 +1,366 @@ +const fs = require('fs'); +const Drive = require('@dashevo/rs-drive'); +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); +const Identity = require('@dashevo/dpp/lib/identity/Identity'); +const decodeProtocolEntityFactory = require('@dashevo/dpp/lib/decodeProtocolEntityFactory'); +const PublicKeyToIdentitiesStoreRepository = require('../../../lib/identity/PublicKeyToIdentitiesStoreRepository'); +const GroveDBStore = require('../../../lib/storage/GroveDBStore'); +const logger = require('../../../lib/util/noopLogger'); +const StorageResult = require('../../../lib/storage/StorageResult'); +const IdentityStoreRepository = require('../../../lib/identity/IdentityStoreRepository'); + +describe('PublicKeyToIdentitiesStoreRepository', () => { + let rsDrive; + let store; + let publicKeyRepository; + let identityRepository; + let publicKeyHash; + let identity; + + beforeEach(async () => { + rsDrive = new Drive('./db/grovedb_test'); + store = new GroveDBStore(rsDrive, logger, 'blockchainStateTestStore'); + + const decodeProtocolEntity = decodeProtocolEntityFactory(); + + identityRepository = new IdentityStoreRepository(store, decodeProtocolEntity); + + publicKeyRepository = new PublicKeyToIdentitiesStoreRepository(store, decodeProtocolEntity); + + publicKeyHash = Buffer.alloc(20).fill(1); + identity = getIdentityFixture(); + }); + + afterEach(async () => { + await rsDrive.close(); + + fs.rmSync('./db/grovedb_test', { recursive: true, force: true }); + }); + + describe('#store', () => { + beforeEach(async () => { + await store.createTree([], PublicKeyToIdentitiesStoreRepository.TREE_PATH[0]); + await identityRepository.createTree(); + }); + + it('should store public key to identities', async () => { + await identityRepository.create(identity); + + const result = await publicKeyRepository.store( + publicKeyHash, + identity.getId(), + ); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const fetchedIdentityResult = await store.get( + PublicKeyToIdentitiesStoreRepository.TREE_PATH.concat([publicKeyHash]), + identity.getId().toBuffer(), + ); + + expect(fetchedIdentityResult).to.be.instanceOf(StorageResult); + expect(fetchedIdentityResult.getValue()).to.be.deep.equal(identity.toBuffer()); + }); + + it('should store public key to identities using transaction', async () => { + await identityRepository.create(identity); + + await store.startTransaction(); + + await publicKeyRepository.store( + publicKeyHash, + identity.getId(), + { useTransaction: true }, + ); + + const emptyIdentitiesResult = await store.get( + PublicKeyToIdentitiesStoreRepository.TREE_PATH.concat([publicKeyHash]), + identity.getId().toBuffer(), + ); + + expect(emptyIdentitiesResult).to.be.instanceOf(StorageResult); + expect(emptyIdentitiesResult.isNull()).to.be.true(); + + const transactionalIdentitiesResult = await store.get( + PublicKeyToIdentitiesStoreRepository.TREE_PATH.concat([publicKeyHash]), + identity.getId().toBuffer(), + { useTransaction: true }, + ); + + expect(transactionalIdentitiesResult).to.be.instanceOf(StorageResult); + expect(transactionalIdentitiesResult.getValue()).to.be.deep.equal(identity.toBuffer()); + + await store.commitTransaction(); + + const committedIdentitiesResult = await store.get( + PublicKeyToIdentitiesStoreRepository.TREE_PATH.concat([publicKeyHash]), + identity.getId().toBuffer(), + ); + + expect(committedIdentitiesResult).to.be.instanceOf(StorageResult); + expect(committedIdentitiesResult.getValue()).to.be.deep.equal(identity.toBuffer()); + }); + }); + + describe('#fetch', () => { + beforeEach(async () => { + await store.createTree([], PublicKeyToIdentitiesStoreRepository.TREE_PATH[0]); + await identityRepository.createTree(); + }); + + it('should fetch empty array if public key to identities not found', async () => { + const result = await publicKeyRepository.fetch(publicKeyHash); + + expect(result).to.be.empty(); + }); + + it('should fetch an public key to identity ids map', async () => { + await identityRepository.create(identity); + + await publicKeyRepository.store( + publicKeyHash, + identity.getId(), + ); + + const result = await publicKeyRepository.fetch(publicKeyHash); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.deep.have.lengthOf(1); + + const [fetchedIdentity] = result.getValue(); + + expect(fetchedIdentity).to.be.instanceOf(Identity); + expect(fetchedIdentity).to.deep.equal(identity.toObject()); + }); + + it('should fetch an public key to identities using transaction', async () => { + await store.startTransaction(); + + await identityRepository.create(identity, { useTransaction: true }); + + await publicKeyRepository.store( + publicKeyHash, + identity.getId(), + { useTransaction: true }, + ); + + const emptyIdentitiesResult = await publicKeyRepository.fetch(publicKeyHash, { + useTransaction: false, + }); + + expect(emptyIdentitiesResult.isEmpty()).to.be.true(); + + const transactionalIdentitiesResult = await publicKeyRepository.fetch(publicKeyHash, { + useTransaction: true, + }); + + expect(transactionalIdentitiesResult.getValue()).to.deep.equal([identity]); + + await store.commitTransaction(); + + const storedIdentitiesResult = await publicKeyRepository.fetch(publicKeyHash); + + expect(storedIdentitiesResult.getValue()).to.deep.equal([identity]); + }); + }); + + describe('#fetchMany', () => { + let publicKeyHash2; + + beforeEach(async () => { + await store.createTree([], PublicKeyToIdentitiesStoreRepository.TREE_PATH[0]); + await identityRepository.createTree(); + + publicKeyHash2 = Buffer.alloc(20).fill(2); + }); + + it('should fetch empty array if public key to identities map not found', async () => { + const result = await publicKeyRepository.fetchMany([publicKeyHash, publicKeyHash2]); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getValue()).to.be.empty(); + }); + + it('should fetch an public key to identities', async () => { + await identityRepository.create(identity); + + await publicKeyRepository.store( + publicKeyHash, + identity.getId(), + ); + + await publicKeyRepository.store( + publicKeyHash2, + identity.getId(), + ); + + const result = await publicKeyRepository.fetchMany([publicKeyHash, publicKeyHash2]); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.deep.equal([identity, identity]); + }); + + it('should fetch an public key to identities map using transaction', async () => { + await store.startTransaction(); + + await identityRepository.create(identity, { useTransaction: true }); + + await publicKeyRepository.store( + publicKeyHash, + identity.getId(), + { useTransaction: true }, + ); + + await publicKeyRepository.store( + publicKeyHash2, + identity.getId(), + { useTransaction: true }, + ); + + const emptyIdentitiesResult = await publicKeyRepository.fetchMany( + [publicKeyHash, publicKeyHash2], + { + useTransaction: false, + }, + ); + + expect(emptyIdentitiesResult.isEmpty()).to.be.true(); + + const transactionalIdentitiesResult = await publicKeyRepository.fetchMany( + [publicKeyHash, publicKeyHash2], + { + useTransaction: true, + }, + ); + + expect(transactionalIdentitiesResult.getValue()).to.deep.equal([identity, identity]); + + await store.commitTransaction(); + + const storedIdentitiesResult = await publicKeyRepository.fetchMany( + [publicKeyHash, publicKeyHash2], + ); + + expect(storedIdentitiesResult.getValue()).to.deep.equal([identity, identity]); + }); + }); + + describe('#createTree', () => { + it('should create a tree', async () => { + const result = await publicKeyRepository.createTree(); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const data = await store.db.get( + [], + PublicKeyToIdentitiesStoreRepository.TREE_PATH[0], + ); + + expect(data).to.deep.equal({ + type: 'tree', + value: Buffer.alloc(32), + }); + }); + }); + + describe('#prove', () => { + let publicKeyHash2; + + beforeEach(async () => { + await store.createTree([], PublicKeyToIdentitiesStoreRepository.TREE_PATH[0]); + await identityRepository.createTree(); + + publicKeyHash2 = Buffer.alloc(20).fill(2); + }); + + it('should fetch proof if public key to identities map not found', async () => { + const result = await publicKeyRepository.proveMany([publicKeyHash, publicKeyHash2]); + + expect(result).to.be.instanceOf(StorageResult); + + expect(result.getValue()).to.be.an.instanceOf(Buffer); + expect(result.getValue().length).to.be.greaterThan(0); + }); + + it('should return proof', async () => { + await identityRepository.create(identity); + + await publicKeyRepository.store( + publicKeyHash, + identity.getId(), + ); + + await publicKeyRepository.store( + publicKeyHash2, + identity.getId(), + ); + + const result = await publicKeyRepository.proveMany([publicKeyHash, publicKeyHash2]); + + expect(result).to.be.instanceOf(StorageResult); + + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.be.an.instanceOf(Buffer); + expect(result.getValue().length).to.be.greaterThan(0); + }); + + // TODO: Enable when transactions will be supported for queries with proofs + it.skip('should return proof map using transaction', async () => { + await store.startTransaction(); + + await identityRepository.create(identity, { useTransaction: true }); + + await publicKeyRepository.store( + publicKeyHash, + identity.getId(), + { useTransaction: true }, + ); + + await publicKeyRepository.store( + publicKeyHash2, + identity.getId(), + { useTransaction: true }, + ); + + // Should return proof of non-existence + let result = await publicKeyRepository.proveMany([publicKeyHash, publicKeyHash2]); + + expect(result).to.be.instanceOf(StorageResult); + + expect(result.getValue()).to.be.an.instanceOf(Buffer); + expect(result.getValue().length).to.be.greaterThan(0); + + // Should return proof of existence + result = await publicKeyRepository.proveMany( + [publicKeyHash, publicKeyHash2], + { useTransaction: true }, + ); + + expect(result).to.be.instanceOf(StorageResult); + + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.be.an.instanceOf(Buffer); + expect(result.getValue().length).to.be.greaterThan(0); + + await store.commitTransaction(); + + // Should return proof of existence + result = await publicKeyRepository.proveMany([publicKeyHash, publicKeyHash2]); + + expect(result).to.be.instanceOf(StorageResult); + + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.be.an.instanceOf(Buffer); + expect(result.getValue().length).to.be.greaterThan(0); + }); + }); +}); diff --git a/packages/js-drive/test/integration/identity/SpentAssetLockTransactionsRepository.spec.js b/packages/js-drive/test/integration/identity/SpentAssetLockTransactionsRepository.spec.js new file mode 100644 index 00000000000..eff6fac674a --- /dev/null +++ b/packages/js-drive/test/integration/identity/SpentAssetLockTransactionsRepository.spec.js @@ -0,0 +1,78 @@ +const Drive = require('@dashevo/rs-drive'); +const fs = require('fs'); + +const SpentAssetLockTransactionsRepository = require('../../../lib/identity/SpentAssetLockTransactionsRepository'); +const StorageResult = require('../../../lib/storage/StorageResult'); +const GroveDBStore = require('../../../lib/storage/GroveDBStore'); +const logger = require('../../../lib/util/noopLogger'); + +describe('SpentAssetLockTransactionsRepository', () => { + let outPointBuffer; + let repository; + let store; + let rsDrive; + + beforeEach(async () => { + outPointBuffer = Buffer.from([42]); + + rsDrive = new Drive('./db/grovedb_test'); + store = new GroveDBStore(rsDrive, logger); + + repository = new SpentAssetLockTransactionsRepository(store); + + await store.createTree([], SpentAssetLockTransactionsRepository.TREE_PATH[0]); + await store.createTree( + [SpentAssetLockTransactionsRepository.TREE_PATH[0]], + SpentAssetLockTransactionsRepository.TREE_PATH[1], + ); + }); + + afterEach(async () => { + await rsDrive.close(); + fs.rmSync('./db/grovedb_test', { recursive: true }); + }); + + describe('#store', () => { + it('should store outpoint', async () => { + const result = await repository.store(outPointBuffer, { + useTransaction: true, + }); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + const placeholderResult = await store.get( + SpentAssetLockTransactionsRepository.TREE_PATH, + outPointBuffer, + ); + + expect(placeholderResult.getValue()).to.deep.equal(Buffer.from([0])); + }); + }); + + describe('#fetch', () => { + it('should return null if outpoint is not present', async () => { + const result = await repository.fetch(outPointBuffer); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.be.null(); + }); + + it('should return buffer containing [0]', async () => { + await store.put( + SpentAssetLockTransactionsRepository.TREE_PATH, + outPointBuffer, + Buffer.from([0]), + ); + + const result = await repository.fetch(outPointBuffer); + + expect(result).to.be.instanceOf(StorageResult); + expect(result.getOperations().length).to.be.greaterThan(0); + + expect(result.getValue()).to.be.deep.equal(Buffer.from([0])); + }); + }); +}); diff --git a/packages/js-drive/test/integration/identity/masternode/synchronizeMasternodeIdentitiesFactory.spec.js b/packages/js-drive/test/integration/identity/masternode/synchronizeMasternodeIdentitiesFactory.spec.js new file mode 100644 index 00000000000..48d0f69d574 --- /dev/null +++ b/packages/js-drive/test/integration/identity/masternode/synchronizeMasternodeIdentitiesFactory.spec.js @@ -0,0 +1,913 @@ +const { + asValue, +} = require('awilix'); + +const SimplifiedMNListEntry = require('@dashevo/dashcore-lib/lib/deterministicmnlist/SimplifiedMNListEntry'); +const { hash } = require('@dashevo/dpp/lib/util/hash'); +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); + +const Address = require('@dashevo/dashcore-lib/lib/address'); +const Script = require('@dashevo/dashcore-lib/lib/script'); +const createTestDIContainer = require('../../../../lib/test/createTestDIContainer'); +const createOperatorIdentifier = require('../../../../lib/identity/masternode/createOperatorIdentifier'); + +/** + * @param {IdentityStoreRepository} identityRepository + * @param {PublicKeyToIdentitiesStoreRepository} publicKeyToIdentitiesRepository + * @param {getWithdrawPubKeyTypeFromPayoutScript} getWithdrawPubKeyTypeFromPayoutScript + * @param {getPublicKeyFromPayoutScript} getPublicKeyFromPayoutScript + * @returns {expectOperatorIdentity} + */ +function expectOperatorIdentityFactory( + identityRepository, + publicKeyToIdentitiesRepository, + getWithdrawPubKeyTypeFromPayoutScript, + getPublicKeyFromPayoutScript, +) { + /** + * @typedef {expectOperatorIdentity} + * @param {SimplifiedMNListEntry} smlEntry + * @param {Address} [previousPayoutAddress] + * @param {Address} [payoutAddress] + * @returns {Promise} + */ + async function expectOperatorIdentity( + smlEntry, + previousPayoutAddress, + payoutAddress, + ) { + // Validate operator identity + + const operatorIdentifier = createOperatorIdentifier(smlEntry); + + const operatorIdentityResult = await identityRepository.fetch(operatorIdentifier); + + const operatorIdentity = operatorIdentityResult.getValue(); + + expect(operatorIdentity) + .to + .exist(); + + // Validate operator public keys + + const operatorPubKey = Buffer.from(smlEntry.pubKeyOperator, 'hex'); + + let publicKeysNum = 1; + if (payoutAddress) { + publicKeysNum += 1; + } + if (previousPayoutAddress) { + publicKeysNum += 1; + } + + expect(operatorIdentity.getPublicKeys()) + .to + .have + .lengthOf(publicKeysNum); + + const firstOperatorMasternodePublicKey = operatorIdentity.getPublicKeyById(0); + expect(firstOperatorMasternodePublicKey.getType()) + .to + .equal(IdentityPublicKey.TYPES.BLS12_381); + expect(firstOperatorMasternodePublicKey.getData()) + .to + .deep + .equal(operatorPubKey); + + const firstOperatorIdentityByPublicKeyHashResult = await publicKeyToIdentitiesRepository + .fetch(firstOperatorMasternodePublicKey.hash()); + + const firstOperatorIdentityByPublicKeyHash = firstOperatorIdentityByPublicKeyHashResult + .getValue(); + + expect(firstOperatorIdentityByPublicKeyHash) + .to + .have + .lengthOf(1); + expect(firstOperatorIdentityByPublicKeyHash[0].getId()) + .to + .deep + .equal(operatorIdentifier); + + let i = 0; + + if (previousPayoutAddress) { + i += 1; + const payoutScript = new Script(previousPayoutAddress); + const publicKeyType = getWithdrawPubKeyTypeFromPayoutScript(payoutScript); + + const payoutPublicKey = operatorIdentity.getPublicKeyById(i); + expect(payoutPublicKey.getType()).to.equal(publicKeyType); + expect(payoutPublicKey.getData()).to.deep.equal( + getPublicKeyFromPayoutScript(payoutScript, publicKeyType), + ); + + const masternodeIdentityByPayoutPublicKeyHashResult = await publicKeyToIdentitiesRepository + .fetch(payoutPublicKey.hash()); + + const masternodeIdentityByPayoutPublicKeyHash = masternodeIdentityByPayoutPublicKeyHashResult + .getValue(); + + expect(masternodeIdentityByPayoutPublicKeyHash).to.have.lengthOf(1); + expect(masternodeIdentityByPayoutPublicKeyHash[0].toBuffer()) + .to.deep.equal(operatorIdentifier); + } + + if (payoutAddress) { + i += 1; + const payoutScript = new Script(payoutAddress); + const publicKeyType = getWithdrawPubKeyTypeFromPayoutScript(payoutScript); + + const payoutPublicKey = operatorIdentity.getPublicKeyById(i); + expect(payoutPublicKey.getType()).to.equal(publicKeyType); + expect(payoutPublicKey.getData()).to.deep.equal( + getPublicKeyFromPayoutScript(payoutScript, publicKeyType), + ); + + const masternodeIdentityByPayoutPublicKeyHashResult = await publicKeyToIdentitiesRepository + .fetch(payoutPublicKey.hash()); + + const masternodeIdentityByPayoutPublicKeyHash = masternodeIdentityByPayoutPublicKeyHashResult + .getValue(); + + expect(masternodeIdentityByPayoutPublicKeyHash).to.have.lengthOf(1); + expect(masternodeIdentityByPayoutPublicKeyHash[0].getId()) + .to.deep.equal(operatorIdentifier); + } + } + + return expectOperatorIdentity; +} + +/** + * @param {IdentityStoreRepository} identityRepository + * @param {PublicKeyToIdentitiesStoreRepository} publicKeyToIdentitiesRepository + * @param {getWithdrawPubKeyTypeFromPayoutScript} getWithdrawPubKeyTypeFromPayoutScript + * @param {getPublicKeyFromPayoutScript} getPublicKeyFromPayoutScript + * @returns {expectMasternodeIdentity} + */ +function expectMasternodeIdentityFactory( + identityRepository, + publicKeyToIdentitiesRepository, + getWithdrawPubKeyTypeFromPayoutScript, + getPublicKeyFromPayoutScript, +) { + /** + * @typedef {expectMasternodeIdentity} + * @param {SimplifiedMNListEntry} smlEntry + * @param {Object} proRegTx + * @param {Address} [previousPayoutAddress] + * @param {Address} [payoutAddress] + * @returns {Promise} + */ + async function expectMasternodeIdentity( + smlEntry, + proRegTx, + previousPayoutAddress, + payoutAddress, + ) { + const masternodeIdentifier = Identifier.from( + Buffer.from(smlEntry.proRegTxHash, 'hex'), + ); + + const masternodeIdentityResult = await identityRepository.fetch(masternodeIdentifier); + + const masternodeIdentity = masternodeIdentityResult.getValue(); + + expect(masternodeIdentity).to.be.not.null(); + + // Validate masternode identity public keys + let publicKeysNum = 1; + if (payoutAddress) { + publicKeysNum += 1; + } + if (previousPayoutAddress) { + publicKeysNum += 1; + } + + expect(masternodeIdentity.getPublicKeys()).to.have.lengthOf(publicKeysNum); + + const masternodePublicKey = masternodeIdentity.getPublicKeyById(0); + expect(masternodePublicKey.getType()).to.equal(IdentityPublicKey.TYPES.ECDSA_HASH160); + expect(masternodePublicKey.getData()).to.deep.equal( + Buffer.from(proRegTx.extraPayload.keyIDOwner, 'hex').reverse(), + ); + + const masternodeIdentityByPublicKeyHashResult = await publicKeyToIdentitiesRepository + .fetch(masternodePublicKey.hash()); + + const masternodeIdentityByPublicKeyHash = masternodeIdentityByPublicKeyHashResult.getValue(); + + expect(masternodeIdentityByPublicKeyHash).to.have.lengthOf(1); + expect(masternodeIdentityByPublicKeyHash[0].getId()) + .to.deep.equal(masternodeIdentifier); + + let i = 0; + + if (previousPayoutAddress) { + i += 1; + const payoutScript = new Script(previousPayoutAddress); + const publicKeyType = getWithdrawPubKeyTypeFromPayoutScript(payoutScript); + + const payoutPublicKey = masternodeIdentity.getPublicKeyById(i); + expect(payoutPublicKey.getType()).to.equal(publicKeyType); + expect(payoutPublicKey.getData()).to.deep.equal( + getPublicKeyFromPayoutScript(payoutScript, publicKeyType), + ); + + const masternodeIdentityByPayoutPublicKeyHashResult = await publicKeyToIdentitiesRepository + .fetch(payoutPublicKey.hash()); + + const masternodeIdentityByPayoutPublicKeyHash = masternodeIdentityByPayoutPublicKeyHashResult + .getValue(); + + expect(masternodeIdentityByPayoutPublicKeyHash).to.have.lengthOf(1); + expect(masternodeIdentityByPayoutPublicKeyHash[0].getId()) + .to.deep.equal(masternodeIdentifier); + } + + if (payoutAddress) { + i += 1; + const payoutScript = new Script(payoutAddress); + const publicKeyType = getWithdrawPubKeyTypeFromPayoutScript(payoutScript); + + const payoutPublicKey = masternodeIdentity.getPublicKeyById(i); + expect(payoutPublicKey.getType()).to.equal(publicKeyType); + expect(payoutPublicKey.getData()).to.deep.equal( + getPublicKeyFromPayoutScript(payoutScript, publicKeyType), + ); + + const masternodeIdentityByPayoutPublicKeyHashResult = await publicKeyToIdentitiesRepository + .fetch(payoutPublicKey.hash()); + + const masternodeIdentityByPayoutPublicKeyHash = masternodeIdentityByPayoutPublicKeyHashResult + .getValue(); + + expect(masternodeIdentityByPayoutPublicKeyHash).to.have.lengthOf(1); + expect(masternodeIdentityByPayoutPublicKeyHash[0].getId()) + .to.deep.equal(masternodeIdentifier); + } + } + + return expectMasternodeIdentity; +} + +/** + * @param {GroveDBStore} groveDBStore + * @returns {expectDeterministicAppHash} + */ +function expectDeterministicAppHashFactory(groveDBStore) { + /** + * @typedef {expectDeterministicAppHash} + * @param {string} appHash + * @returns {Promise} + */ + async function expectDeterministicAppHash(appHash) { + const actualAppHash = await groveDBStore.getRootHash({ useTransaction: true }); + + const actualAppHashHex = actualAppHash.toString('hex'); + + expect(actualAppHashHex).to.deep.equal(appHash); + } + + return expectDeterministicAppHash; +} + +describe('synchronizeMasternodeIdentitiesFactory', () => { + let container; + let coreHeight; + let rawDiff; + let fetchTransactionMock; + let smlStoreMock; + let smlFixture; + let transaction1; + let transaction2; + let synchronizeMasternodeIdentities; + let rewardsDataContract; + let identityRepository; + let documentRepository; + let publicKeyToIdentitiesRepository; + let coreRpcClientMock; + let expectOperatorIdentity; + let expectMasternodeIdentity; + let expectDeterministicAppHash; + let firstSyncAppHash; + + beforeEach(async function beforeEach() { + coreHeight = 3; + firstSyncAppHash = 'c05324cb829d952453e12ba1ea737110d6efacce8a3224f74ef03bab029f0869'; + + container = await createTestDIContainer(); + + const blockExecutionContext = container.resolve('blockExecutionContext'); + blockExecutionContext.getHeader = this.sinon.stub().returns( + { time: { seconds: 1651585250 } }, + ); + + // Mock fetchTransaction + + fetchTransactionMock = this.sinon.stub(); + + transaction1 = { + extraPayload: { + operatorReward: 100, + keyIDOwner: Buffer.alloc(20).fill('a').toString('hex'), + }, + }; + + transaction2 = { + extraPayload: { + operatorReward: 0, + keyIDOwner: Buffer.alloc(20).fill('b').toString('hex'), + }, + }; + + fetchTransactionMock.withArgs('954112bb018895896cfa3c3d00761a045fc16b22f2170c1fbb029a2936c68f16').resolves(transaction1); + fetchTransactionMock.withArgs('9673b21f45b216dce2b4ffb4a85e1471d57aed6bf8e34d961a48296fe9b7f51a').resolves(transaction2); + + container.register('fetchTransaction', asValue(fetchTransactionMock)); + + // Mock Core RPC + + coreRpcClientMock = { + protx: this.sinon.stub().resolves({ + result: rawDiff, + }), + }; + + container.register('coreRpcClient', asValue(coreRpcClientMock)); + + // Mock SML + + smlFixture = [ + new SimplifiedMNListEntry({ + proRegTxHash: '954112bb018895896cfa3c3d00761a045fc16b22f2170c1fbb029a2936c68f16', + confirmedHash: '1de71625dbc973e2377ebd7da4fe6f8a8eb8af8c5a99373e36151a4fbe9947cc', + service: '192.168.65.2:20101', + pubKeyOperator: '8e4c8c144bd6c62640fe3ae295973d512f83f7f541525a5da3c91e77ec02ff4dcd214e7431b7d2cc28e420ebfeb612ee', + votingAddress: 'yfLLjdEynGQBdoPcCDUNAxu6pksYGzXKA4', + isValid: true, + payoutAddress: 'yR843jN58m5dubmQjfUmKDDJMJzNatFV9M', + payoutOperatorAddress: 'yNjsnYM16J5NZPA2P8BKJG3MKfUD7XHAFE', + }), + new SimplifiedMNListEntry({ + proRegTxHash: '9673b21f45b216dce2b4ffb4a85e1471d57aed6bf8e34d961a48296fe9b7f51a', + confirmedHash: '25e1884e4251cbf42a0f9f42666443c62d89b3bc1aae73fb1e9d753e0b2732f4', + service: '192.168.65.2:20201', + pubKeyOperator: '06a9789fab00deae1464ed80bda281fc833f85959b04201645e5fc25635e3e7ecda30d13d328b721af0809fca3bf3b63', + votingAddress: 'yVRXh9Tgf9qt9tCbXmeX9FQsEYa526FMxR', + isValid: true, + payoutAddress: 'ycL7L4mhYoaZdm9TH85svvpfeKtdfo249u', + }), + ]; + + smlStoreMock = { + getSMLbyHeight: this.sinon.stub().returns({ mnList: smlFixture }), + }; + + const simplifiedMasternodeListMock = { + getStore: this.sinon.stub().returns(smlStoreMock), + }; + + container.register('simplifiedMasternodeList', asValue(simplifiedMasternodeListMock)); + + const createInitialStateStructure = container.resolve('createInitialStateStructure'); + await createInitialStateStructure(); + + const registerSystemDataContract = container.resolve('registerSystemDataContract'); + const masternodeRewardSharesContractId = container.resolve('masternodeRewardSharesContractId'); + const masternodeRewardSharesOwnerId = container.resolve('masternodeRewardSharesOwnerId'); + const masternodeRewardSharesOwnerMasterPublicKey = container.resolve('masternodeRewardSharesOwnerMasterPublicKey'); + const masternodeRewardSharesOwnerSecondPublicKey = container.resolve('masternodeRewardSharesOwnerSecondPublicKey'); + const masternodeRewardSharesDocuments = container.resolve('masternodeRewardSharesDocuments'); + + rewardsDataContract = await registerSystemDataContract( + masternodeRewardSharesOwnerId, + masternodeRewardSharesContractId, + masternodeRewardSharesOwnerMasterPublicKey, + masternodeRewardSharesOwnerSecondPublicKey, + masternodeRewardSharesDocuments, + ); + + synchronizeMasternodeIdentities = container.resolve('synchronizeMasternodeIdentities'); + + identityRepository = container.resolve('identityRepository'); + documentRepository = container.resolve('documentRepository'); + publicKeyToIdentitiesRepository = container.resolve('publicKeyToIdentitiesRepository'); + const getWithdrawPubKeyTypeFromPayoutScript = container.resolve('getWithdrawPubKeyTypeFromPayoutScript'); + const getPublicKeyFromPayoutScript = container.resolve('getPublicKeyFromPayoutScript'); + + expectOperatorIdentity = expectOperatorIdentityFactory( + identityRepository, + publicKeyToIdentitiesRepository, + getWithdrawPubKeyTypeFromPayoutScript, + getPublicKeyFromPayoutScript, + ); + + expectMasternodeIdentity = expectMasternodeIdentityFactory( + identityRepository, + publicKeyToIdentitiesRepository, + getWithdrawPubKeyTypeFromPayoutScript, + getPublicKeyFromPayoutScript, + ); + + expectDeterministicAppHash = expectDeterministicAppHashFactory( + container.resolve('groveDBStore'), + ); + }); + + afterEach(async () => { + if (container) { + await container.dispose(); + } + }); + + it('should create identities for all masternodes on the first sync', async () => { + const result = await synchronizeMasternodeIdentities(coreHeight); + + expect(result.fromHeight).to.be.equal(0); + expect(result.toHeight).to.be.equal(3); + expect(result.createdEntities).to.have.lengthOf(4); + expect(result.updatedEntities).to.have.lengthOf(0); + expect(result.removedEntities).to.have.lengthOf(0); + + await expectDeterministicAppHash(firstSyncAppHash); + + /** + * Validate first masternode + */ + + // Masternode identity should be created + + await expectMasternodeIdentity( + smlFixture[0], + transaction1, + ); + + // Operator identity should be created + + await expectOperatorIdentity(smlFixture[0]); + + // Masternode reward shares should be created + + const firstMasternodeIdentifier = Identifier.from( + Buffer.from(smlFixture[0].proRegTxHash, 'hex'), + ); + + const firstOperatorIdentifier = createOperatorIdentifier(smlFixture[0]); + + let documentsResult = await documentRepository.find( + rewardsDataContract, + 'rewardShare', + { + where: [ + ['$ownerId', '==', firstMasternodeIdentifier], + ['payToId', '==', firstOperatorIdentifier], + ], + }, + ); + + let documents = documentsResult.getValue(); + + expect(documents).to.have.lengthOf(1); + + const expectedDocumentId = Identifier.from( + hash( + Buffer.concat([ + firstMasternodeIdentifier, + firstOperatorIdentifier, + ]), + ), + ); + + expect(documents[0].getId()).to.deep.equal(expectedDocumentId); + expect(documents[0].getOwnerId()).to.deep.equal(firstMasternodeIdentifier); + expect(documents[0].get('percentage')).to.equal(100); + expect(documents[0].get('payToId')).to.deep.equal(firstOperatorIdentifier); + + /** + * Validate second masternode + */ + + // Masternode identity should be created + + await expectMasternodeIdentity( + smlFixture[1], + transaction2, + ); + + // Operator identity shouldn't be created + + const secondOperatorPubKey = Buffer.from(smlFixture[1].pubKeyOperator, 'hex'); + + const secondOperatorIdentifier = Identifier.from( + hash( + Buffer.concat([ + Buffer.from(smlFixture[1].proRegTxHash, 'hex'), + secondOperatorPubKey, + ]), + ), + ); + + const secondOperatorIdentityResult = await identityRepository.fetch(secondOperatorIdentifier); + + const secondOperatorIdentity = secondOperatorIdentityResult.getValue(); + + expect(secondOperatorIdentity).to.be.null(); + + // Masternode reward shares shouldn't be created + + const secondMasternodeIdentifier = Identifier.from( + Buffer.from(smlFixture[1].proRegTxHash, 'hex'), + ); + + documentsResult = await documentRepository.find( + rewardsDataContract, + 'rewardShare', + { + where: [ + ['$ownerId', '==', secondMasternodeIdentifier], + ['payToId', '==', secondOperatorIdentifier], + ], + }, + ); + + documents = documentsResult.getValue(); + + expect(documents).to.have.lengthOf(0); + }); + + it('should sync identities if the gap between coreHeight and lastSyncedCoreHeight > smlMaxListsLimit', async () => { + // Sync initial list + + await synchronizeMasternodeIdentities(coreHeight); + + await expectDeterministicAppHash(firstSyncAppHash); + + // Second call + + const result = await synchronizeMasternodeIdentities(coreHeight + 42); + + expect(result.fromHeight).to.be.equal(3); + expect(result.toHeight).to.be.equal(45); + expect(result.createdEntities).to.have.lengthOf(3); + expect(result.updatedEntities).to.have.lengthOf(0); + expect(result.removedEntities).to.have.lengthOf(0); + + // Nothing happened + + await expectDeterministicAppHash(firstSyncAppHash); + + // Core RPC should be called + + expect(coreRpcClientMock.protx).to.have.been.calledOnceWithExactly('diff', 1, 3); + }); + + it('should create masternode identities if new masternode appeared', async () => { + // Sync initial list + + await synchronizeMasternodeIdentities(coreHeight); + + await expectDeterministicAppHash(firstSyncAppHash); + + // Mock SML + + const newSmlFixture = [ + new SimplifiedMNListEntry({ + proRegTxHash: '3b73b21f45b216dce2b4ffb4a85e1471d57aed6bf8e34d961a48296fe9b7f53b', + confirmedHash: '3be1884e4251cbf42a0f9f42666443c62d89b3bc1aae73fb1e9d753e0b27323b', + service: '192.168.65.3:20201', + pubKeyOperator: '3ba9789fab00deae1464ed80bda281fc833f85959b04201645e5fc25635e3e7ecda30d13d328b721af0809fca3bf3b3b', + votingAddress: 'yVey9g4fsN3RY3ZjQ7HqiKEH2zEVAG95EN', + isValid: true, + payoutAddress: '7UkJidhNjEPJCQnCTXeaJKbJmL4JuyV66w', + payoutOperatorAddress: 'yPDBTHAjPwJfZSSQYczccA78XRS2tZ5fZF', + }), + ]; + + smlStoreMock.getSMLbyHeight.withArgs(coreHeight + 1).returns( + { mnList: smlFixture.concat(newSmlFixture) }, + ); + + // Mock fetchTransaction + + const transaction3 = { + extraPayload: { + operatorReward: 200, + keyIDOwner: Buffer.alloc(20).fill('c').toString('hex'), + }, + }; + + fetchTransactionMock.withArgs('3b73b21f45b216dce2b4ffb4a85e1471d57aed6bf8e34d961a48296fe9b7f53b').resolves(transaction3); + + // Second call + + const result = await synchronizeMasternodeIdentities(coreHeight + 1); + + expect(result.fromHeight).to.be.equal(3); + expect(result.toHeight).to.be.equal(4); + expect(result.createdEntities).to.have.lengthOf(3); + expect(result.updatedEntities).to.have.lengthOf(0); + expect(result.removedEntities).to.have.lengthOf(0); + + await expectDeterministicAppHash('9785da4ccade9e014da4138cd0b81d06fbfb058bd27d378ac710fdd0f3dd0812'); + + // New masternode identity should be created + + await expectMasternodeIdentity( + newSmlFixture[0], + transaction3, + ); + + // New operator should be created + + await expectOperatorIdentity(newSmlFixture[0]); + + // Masternode reward shares should be created + + const newMasternodeIdentifier = Identifier.from( + Buffer.from(newSmlFixture[0].proRegTxHash, 'hex'), + ); + + const newOperatorIdentifier = createOperatorIdentifier(newSmlFixture[0]); + + const documentsResult = await documentRepository.find( + rewardsDataContract, + 'rewardShare', + { + where: [ + ['$ownerId', '==', newMasternodeIdentifier], + ['payToId', '==', newOperatorIdentifier], + ], + }, + ); + + const documents = documentsResult.getValue(); + + expect(documents).to.have.lengthOf(1); + + const expectedDocumentId = Identifier.from( + hash( + Buffer.concat([ + newMasternodeIdentifier, + newOperatorIdentifier, + ]), + ), + ); + + expect(documents[0].getId()).to.deep.equal(expectedDocumentId); + expect(documents[0].getOwnerId()).to.deep.equal(newMasternodeIdentifier); + expect(documents[0].get('percentage')).to.equal(200); + expect(documents[0].get('payToId')).to.deep.equal(newOperatorIdentifier); + }); + + it('should remove reward shares if masternode disappeared', async () => { + // Sync initial list + + await synchronizeMasternodeIdentities(coreHeight); + + await expectDeterministicAppHash(firstSyncAppHash); + + // Mock SML + + smlStoreMock.getSMLbyHeight.withArgs(coreHeight + 1).returns( + { mnList: [smlFixture[1]] }, + ); + + // Second call + + const result = await synchronizeMasternodeIdentities(coreHeight + 1); + + expect(result.fromHeight).to.be.equal(3); + expect(result.toHeight).to.be.equal(4); + expect(result.createdEntities).to.have.lengthOf(0); + expect(result.updatedEntities).to.have.lengthOf(0); + expect(result.removedEntities).to.have.lengthOf(1); + + await expectDeterministicAppHash('20969f374525ed989a9eac95deaa21f6222395e6369c5e9e8f8ae8740a0fb22d'); + + // Masternode identity should stay + + await expectMasternodeIdentity( + smlFixture[0], + transaction1, + ); + + // Operator identity should stay + + await expectOperatorIdentity(smlFixture[0]); + + // Masternode reward shares should be removed + + const removedMasternodeIdentifier = Buffer.from(smlFixture[0].proRegTxHash, 'hex'); + + const documentsResult = await documentRepository.find( + rewardsDataContract, + 'rewardShare', + { + where: [ + ['$ownerId', '==', removedMasternodeIdentifier], + ], + }, + ); + + const documents = documentsResult.getValue(); + + expect(documents).to.have.lengthOf(0); + }); + + it('should remove reward shares if masternode is not valid', async () => { + // Sync initial list + + await synchronizeMasternodeIdentities(coreHeight); + + await expectDeterministicAppHash(firstSyncAppHash); + + // Mock SML + + const invalidSmlEntry = smlFixture[0].copy(); + invalidSmlEntry.isValid = false; + + smlStoreMock.getSMLbyHeight.withArgs(coreHeight + 1).returns( + { mnList: [smlFixture[1], invalidSmlEntry] }, + ); + + // Second call + + const result = await synchronizeMasternodeIdentities(coreHeight + 1); + + expect(result.fromHeight).to.be.equal(3); + expect(result.toHeight).to.be.equal(4); + expect(result.createdEntities).to.have.lengthOf(0); + expect(result.updatedEntities).to.have.lengthOf(0); + expect(result.removedEntities).to.have.lengthOf(1); + + await expectDeterministicAppHash('20969f374525ed989a9eac95deaa21f6222395e6369c5e9e8f8ae8740a0fb22d'); + + const invalidMasternodeIdentifier = Identifier.from( + Buffer.from(invalidSmlEntry.proRegTxHash, 'hex'), + ); + + // Masternode reward shares should be removed + + const documentsResult = await documentRepository.find( + rewardsDataContract, + 'rewardShare', + { + where: [ + ['$ownerId', '==', invalidMasternodeIdentifier], + ], + }, + ); + + const documents = documentsResult.getValue(); + + expect(documents).to.have.lengthOf(0); + }); + + it('should create operator identity and reward shares if PubKeyOperator was changed', async () => { + // Initial sync + + await synchronizeMasternodeIdentities(coreHeight); + + await expectDeterministicAppHash(firstSyncAppHash); + + // Mock SML + + const changedSmlEntry = smlFixture[0].copy(); + changedSmlEntry.pubKeyOperator = '3ba9789fab00deae1464ed80bda281fc833f85959b04201645e5fc25635e3e7ecda30d13d328b721af0809fca3bf3b3b'; + + smlStoreMock.getSMLbyHeight.withArgs(coreHeight + 1).returns( + { mnList: [smlFixture[1], changedSmlEntry] }, + ); + + // Second call + + const result = await synchronizeMasternodeIdentities(coreHeight + 1); + + expect(result.fromHeight).to.be.equal(3); + expect(result.toHeight).to.be.equal(4); + expect(result.createdEntities).to.have.lengthOf(0); + expect(result.updatedEntities).to.have.lengthOf(3); + expect(result.removedEntities).to.have.lengthOf(0); + + await expectDeterministicAppHash('eefb5ee17a1bac487b466ab5176091740762f69c566640d5ede7cb56b7fcca49'); + + // Masternode identity should stay + + await expectMasternodeIdentity( + smlFixture[0], + transaction1, + ); + + // Previous operator identity should stay + + await expectOperatorIdentity(smlFixture[0]); + + // New operator identity should be created + + await expectOperatorIdentity(changedSmlEntry); + + // Only new masternode reward shares should exist + + const changedMasternodeIdentifier = Identifier.from( + Buffer.from(changedSmlEntry.proRegTxHash, 'hex'), + ); + + const documentsResult = await documentRepository.find( + rewardsDataContract, + 'rewardShare', + { + where: [ + ['$ownerId', '==', changedMasternodeIdentifier], + ], + }, + ); + + const documents = documentsResult.getValue(); + + expect(documents).to.have.lengthOf(1); + + const [document] = documents; + + const newOperatorIdentifier = createOperatorIdentifier(changedSmlEntry); + + expect(document.get('payToId')).to.deep.equal(newOperatorIdentifier); + }); + + it.skip('should handle changed payout and operator payout addresses', async () => { + // Sync initial list + + await synchronizeMasternodeIdentities(coreHeight); + + await expectDeterministicAppHash(firstSyncAppHash); + + // Mock SML + + const changedSmlEntry = smlFixture[0].copy(); + changedSmlEntry.payoutAddress = 'yMLrhooXyJtpV3R2ncsxvkrh6wRennNPoG'; + changedSmlEntry.operatorPayoutAddress = 'yT8DDY5NkX4ZtBkUVz7y1RgzbakCnMPogh'; + + smlStoreMock.getSMLbyHeight.withArgs(coreHeight + 1).returns( + { mnList: [smlFixture[1], changedSmlEntry] }, + ); + + // Second call + + await synchronizeMasternodeIdentities(coreHeight + 1); + + await expectDeterministicAppHash('7d0248dbad9d0109a9d215158a5196991d6c4650bfd4e043a36bb8517c2068a9'); + + // Masternode identity should contain new public key + + await expectMasternodeIdentity( + smlFixture[0], + transaction1, + Address.fromString(smlFixture[0].payoutAddress), + Address.fromString(changedSmlEntry.payoutAddress), + ); + + // Previous operator identity should stay + + await expectOperatorIdentity( + smlFixture[0], + undefined, + Address.fromString(changedSmlEntry.operatorPayoutAddress), + ); + + // New operator identity should be created + + await expectOperatorIdentity( + changedSmlEntry, + undefined, + Address.fromString(changedSmlEntry.operatorPayoutAddress), + ); + + // Only new masternode reward shares should exist + + const changedMasternodeIdentifier = Identifier.from( + Buffer.from(changedSmlEntry.proRegTxHash, 'hex'), + ); + + const documentsResult = await documentRepository.find( + rewardsDataContract, + 'rewardShare', + { + where: [ + ['$ownerId', '==', changedMasternodeIdentifier], + ], + }, + ); + + const documents = documentsResult.getValue(); + + expect(documents).to.have.lengthOf(1); + + const [document] = documents; + + const newOperatorIdentifier = createOperatorIdentifier(changedSmlEntry); + + expect(document.get('payToId')).to.deep.equal(newOperatorIdentifier); + }); +}); diff --git a/packages/js-drive/test/unit/abci/closeAbciServerFactory.spec.js b/packages/js-drive/test/unit/abci/closeAbciServerFactory.spec.js new file mode 100644 index 00000000000..37f0f6b4d0e --- /dev/null +++ b/packages/js-drive/test/unit/abci/closeAbciServerFactory.spec.js @@ -0,0 +1,29 @@ +const closeAbciServerFactory = require('../../../lib/abci/closeAbciServerFactory'); + +describe('closeAbciServerFactory', () => { + let closeAbciServer; + let abciServerMock; + + beforeEach(function beforeEach() { + abciServerMock = { + close: this.sinon.spy((resolve) => { + resolve(); + }), + listening: true, + }; + + closeAbciServer = closeAbciServerFactory(abciServerMock); + }); + + it('should close server if it\'s listening', async () => { + await closeAbciServer(); + + expect(abciServerMock.close).to.be.calledOnce(); + }); + + it('should not close server if not listening', async () => { + abciServerMock.listening = false; + + expect(abciServerMock.close).to.not.be.called(); + }); +}); diff --git a/packages/js-drive/test/unit/abci/errors/enrichErrorWithConsensusLoggerFactory.spec.js b/packages/js-drive/test/unit/abci/errors/enrichErrorWithConsensusLoggerFactory.spec.js new file mode 100644 index 00000000000..7ed0c86195a --- /dev/null +++ b/packages/js-drive/test/unit/abci/errors/enrichErrorWithConsensusLoggerFactory.spec.js @@ -0,0 +1,39 @@ +const enrichErrorWithConsensusLoggerFactory = require('../../../../lib/abci/errors/enrichErrorWithConsensusLoggerFactory'); +const BlockExecutionContextMock = require('../../../../lib/test/mock/BlockExecutionContextMock'); +const LoggerMock = require('../../../../lib/test/mock/LoggerMock'); + +describe('enrichErrorWithConsensusLoggerFactory', () => { + let blockExecutionContextMock; + let enrichErrorWithConsensusLogger; + let loggerMock; + + beforeEach(function beforeEach() { + loggerMock = new LoggerMock(this.sinon); + + blockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + blockExecutionContextMock.consensusLogger = loggerMock; + + enrichErrorWithConsensusLogger = enrichErrorWithConsensusLoggerFactory( + blockExecutionContextMock, + ); + }); + + it('should add consensusLogger from BlockExecutionContext to thrown error', async () => { + const error = new Error(); + + const method = () => { + throw error; + }; + + const methodHandler = enrichErrorWithConsensusLogger(method); + + try { + await methodHandler(); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.equal(error); + expect(e.consensusLogger).to.equal(loggerMock); + } + }); +}); diff --git a/packages/js-drive/test/unit/abci/errors/wrapInErrorHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/errors/wrapInErrorHandlerFactory.spec.js new file mode 100644 index 00000000000..fb51a98d179 --- /dev/null +++ b/packages/js-drive/test/unit/abci/errors/wrapInErrorHandlerFactory.spec.js @@ -0,0 +1,139 @@ +const SomeConsensusError = require('@dashevo/dpp/lib/test/mocks/SomeConsensusError'); +const wrapInErrorHandlerFactory = require('../../../../lib/abci/errors/wrapInErrorHandlerFactory'); +const LoggerMock = require('../../../../lib/test/mock/LoggerMock'); +const InternalAbciError = require('../../../../lib/abci/errors/InternalAbciError'); +const InvalidArgumentAbciError = require('../../../../lib/abci/errors/InvalidArgumentAbciError'); +const VerboseInternalAbciError = require('../../../../lib/abci/errors/VerboseInternalAbciError'); +const DPPValidationAbciError = require('../../../../lib/abci/errors/DPPValidationAbciError'); + +describe('wrapInErrorHandlerFactory', () => { + let loggerMock; + let methodMock; + let request; + let handler; + let wrapInErrorHandler; + + beforeEach(function beforeEach() { + request = { + tx: Buffer.alloc(0), + }; + + loggerMock = new LoggerMock(this.sinon); + + wrapInErrorHandler = wrapInErrorHandlerFactory(loggerMock, true); + methodMock = this.sinon.stub(); + + handler = wrapInErrorHandler( + methodMock, + ); + }); + + it('should throw an internal error if any Error is thrown in handler', async () => { + const error = new Error('Custom error'); + + methodMock.throws(error); + + try { + await handler(request); + + expect.fail('Internal error must be thrown'); + } catch (e) { + expect(e).to.equal(error); + } + }); + + it('should throw en internal error if an InternalAbciError is thrown in handler', async () => { + const originError = new Error(); + const metadata = { sample: 'data' }; + const error = new InternalAbciError(originError, metadata); + + methodMock.throws(error); + + try { + await handler(request); + + expect.fail('Internal error must be thrown'); + } catch (e) { + expect(e).to.equal(originError); + } + }); + + it('should respond with internal error code if any Error is thrown in handler and respondWithInternalError enabled', async () => { + handler = wrapInErrorHandler( + methodMock, { respondWithInternalError: true }, + ); + + const error = new Error('Custom error'); + + methodMock.throws(error); + + const response = await handler(request); + + const expectedError = new InternalAbciError(error); + + expect(response).to.deep.equal(expectedError.getAbciResponse()); + }); + + it('should respond with internal error code if an InternalAbciError is thrown in handler and respondWithInternalError enabled', async () => { + handler = wrapInErrorHandler( + methodMock, { respondWithInternalError: true }, + ); + + const data = { sample: 'data' }; + const error = new InternalAbciError(new Error(), data); + + methodMock.throws(error); + + const response = await handler(request); + + expect(response).to.deep.equal(error.getAbciResponse()); + }); + + it('should respond with invalid argument error if it is thrown in handler', async () => { + const data = { sample: 'data' }; + const error = new InvalidArgumentAbciError('test', data); + + methodMock.throws(error); + + const response = await handler(request); + + expect(response).to.deep.equal(error.getAbciResponse()); + }); + + it('should respond with verbose error containing message and stack in debug mode', async () => { + wrapInErrorHandler = wrapInErrorHandlerFactory(loggerMock, false); + + const error = new Error('Custom error'); + + methodMock.throws(error); + + handler = wrapInErrorHandler( + methodMock, { respondWithInternalError: true }, + ); + + const response = await handler(request); + + const expectedError = new VerboseInternalAbciError( + new InternalAbciError(error), + ); + + expect(response).to.deep.equal(expectedError.getAbciResponse()); + }); + + it('should respond with error if method throws DPPValidationAbciError', async () => { + const dppValidationError = new DPPValidationAbciError( + 'Some error', + new SomeConsensusError('Consensus error'), + ); + + methodMock.throws(dppValidationError); + + handler = wrapInErrorHandler( + methodMock, { respondWithInternalError: true }, + ); + + const response = await handler(request); + + expect(response).to.deep.equal(dppValidationError.getAbciResponse()); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/beginBlockHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/beginBlockHandlerFactory.spec.js new file mode 100644 index 00000000000..a5e459cf2e7 --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/beginBlockHandlerFactory.spec.js @@ -0,0 +1,220 @@ +const Long = require('long'); + +const { + tendermint: { + abci: { + ResponseBeginBlock, + }, + }, +} = require('@dashevo/abci/types'); + +const beginBlockHandlerFactory = require('../../../../lib/abci/handlers/beginBlockHandlerFactory'); + +const BlockExecutionContextMock = require('../../../../lib/test/mock/BlockExecutionContextMock'); +const LoggerMock = require('../../../../lib/test/mock/LoggerMock'); +const NotSupportedNetworkProtocolVersionError = require('../../../../lib/abci/handlers/errors/NotSupportedNetworkProtocolVersionError'); +const NetworkProtocolVersionIsNotSetError = require('../../../../lib/abci/handlers/errors/NetworkProtocolVersionIsNotSetError'); +const GroveDBStoreMock = require('../../../../lib/test/mock/GroveDBStoreMock'); +const BlockExecutionContextStackMock = require('../../../../lib/test/mock/BlockExecutionContextStackMock'); + +describe('beginBlockHandlerFactory', () => { + let protocolVersion; + let beginBlockHandler; + let request; + let blockHeight; + let coreChainLockedHeight; + let blockExecutionContextMock; + let header; + let updateSimplifiedMasternodeListMock; + let waitForChainLockedHeightMock; + let loggerMock; + let lastCommitInfo; + let dppMock; + let transactionalDppMock; + let synchronizeMasternodeIdentitiesMock; + let groveDBStoreMock; + let blockExecutionContextStackMock; + let executionTimerMock; + + beforeEach(function beforeEach() { + protocolVersion = Long.fromInt(1); + + blockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + + loggerMock = new LoggerMock(this.sinon); + + dppMock = { + setProtocolVersion: this.sinon.stub(), + }; + transactionalDppMock = { + setProtocolVersion: this.sinon.stub(), + }; + + updateSimplifiedMasternodeListMock = this.sinon.stub().resolves(false); + waitForChainLockedHeightMock = this.sinon.stub(); + synchronizeMasternodeIdentitiesMock = this.sinon.stub().resolves({ + createdEntities: [], + updatedEntities: [], + removedEntities: [], + fromHeight: 1, + toHeight: 42, + }); + + groveDBStoreMock = new GroveDBStoreMock(this.sinon); + blockExecutionContextStackMock = new BlockExecutionContextStackMock(this.sinon); + + blockExecutionContextStackMock.getLatest.returns({ + getHeader: this.sinon.stub(), + }); + + executionTimerMock = { + clearTimer: this.sinon.stub(), + startTimer: this.sinon.stub(), + stopTimer: this.sinon.stub(), + }; + + beginBlockHandler = beginBlockHandlerFactory( + groveDBStoreMock, + blockExecutionContextMock, + blockExecutionContextStackMock, + protocolVersion, + dppMock, + transactionalDppMock, + updateSimplifiedMasternodeListMock, + waitForChainLockedHeightMock, + synchronizeMasternodeIdentitiesMock, + loggerMock, + executionTimerMock, + ); + + blockHeight = 2; + blockHeight = 1; + + header = { + version: { + app: protocolVersion, + }, + height: blockHeight, + time: { + seconds: Math.ceil(new Date().getTime() / 1000), + }, + coreChainLockedHeight, + }; + + lastCommitInfo = {}; + + request = { + header, + lastCommitInfo, + }; + }); + + it('should reset previous block state and prepare everything for for a next one', async () => { + const response = await beginBlockHandler(request); + + expect(response).to.be.an.instanceOf(ResponseBeginBlock); + + // Wait for chain locked core block height + expect(waitForChainLockedHeightMock).to.be.calledOnceWithExactly(coreChainLockedHeight); + + // Reset block execution context + expect(blockExecutionContextMock.getHeader).to.be.calledOnceWithExactly(); + expect(blockExecutionContextMock.reset).to.be.calledOnceWithExactly(); + expect(blockExecutionContextMock.setHeader).to.be.calledOnceWithExactly(header); + expect(blockExecutionContextMock.setLastCommitInfo).to.be.calledOnceWithExactly(lastCommitInfo); + + // Set current protocol version + expect(dppMock.setProtocolVersion).to.have.been.calledOnceWithExactly( + protocolVersion.toNumber(), + ); + expect(transactionalDppMock.setProtocolVersion).to.have.been.calledOnceWithExactly( + protocolVersion.toNumber(), + ); + + // Start new transaction + expect(groveDBStoreMock.startTransaction).to.be.calledOnceWithExactly(); + + // Update SML + expect(updateSimplifiedMasternodeListMock).to.be.calledOnceWithExactly( + coreChainLockedHeight, { logger: loggerMock }, + ); + + expect(synchronizeMasternodeIdentitiesMock).to.not.been.called(); + }); + + it('should synchronize masternode identities if SML is updated', async () => { + updateSimplifiedMasternodeListMock.resolves(true); + + const response = await beginBlockHandler(request); + + expect(response).to.be.an.instanceOf(ResponseBeginBlock); + + expect(synchronizeMasternodeIdentitiesMock).to.have.been.calledOnceWithExactly( + coreChainLockedHeight, + ); + }); + + it('should throw NotSupportedNetworkProtocolVersionError if protocol version is not supported', async () => { + request.header.version.app = Long.fromInt(42); + + try { + await beginBlockHandler(request); + + expect.fail('should throw NotSupportedNetworkProtocolVersionError'); + } catch (e) { + expect(e).to.be.instanceOf(NotSupportedNetworkProtocolVersionError); + expect(e.getNetworkProtocolVersion()).to.equal(request.header.version.app); + expect(e.getLatestProtocolVersion()).to.equal(protocolVersion); + } + }); + + it('should throw an NetworkProtocolVersionIsNotSetError if network protocol version is not set', async () => { + request.header.version.app = Long.fromInt(0); + + try { + await beginBlockHandler(request); + + expect.fail('should throw NetworkProtocolVersionIsNotSetError'); + } catch (err) { + expect(err).to.be.an.instanceOf(NetworkProtocolVersionIsNotSetError); + } + }); + + it('should abort db transaction and reset previous execution context if previous block failed', async function it() { + blockExecutionContextMock.getHeader.returns({ + height: { + equals: this.sinon.stub().returns(true), + }, + }); + + blockExecutionContextStackMock.getLatest.returns({ + getHeader: this.sinon.stub().returns( + { + height: { + equals: this.sinon.stub().returns(true), + }, + }, + ), + }); + + groveDBStoreMock.isTransactionStarted.resolves(true); + + const response = await beginBlockHandler(request); + + expect(response).to.be.an.instanceOf(ResponseBeginBlock); + + expect(groveDBStoreMock.abortTransaction).to.be.calledOnceWithExactly(); + expect(blockExecutionContextStackMock.removeLatest).to.be.calledOnceWithExactly(); + + expect(blockExecutionContextMock.reset).to.be.calledOnceWithExactly(); + expect(blockExecutionContextMock.setHeader).to.be.calledOnceWithExactly(header); + expect(blockExecutionContextMock.setLastCommitInfo).to.be.calledOnceWithExactly(lastCommitInfo); + + expect(updateSimplifiedMasternodeListMock).to.be.calledOnceWithExactly( + coreChainLockedHeight, { logger: loggerMock }, + ); + + expect(waitForChainLockedHeightMock).to.be.calledOnceWithExactly(coreChainLockedHeight); + expect(synchronizeMasternodeIdentitiesMock).to.have.not.been.called(); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/checkTxHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/checkTxHandlerFactory.spec.js new file mode 100644 index 00000000000..00f48adde14 --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/checkTxHandlerFactory.spec.js @@ -0,0 +1,42 @@ +const { + tendermint: { + abci: { + ResponseCheckTx, + }, + }, +} = require('@dashevo/abci/types'); + +const getIdentityCreateTransitionFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityCreateTransitionFixture'); + +const checkTxHandlerFactory = require('../../../../lib/abci/handlers/checkTxHandlerFactory'); + +describe('checkTxHandlerFactory', () => { + let checkTxHandler; + let request; + let stateTransitionFixture; + let unserializeStateTransitionMock; + + beforeEach(function beforeEach() { + stateTransitionFixture = getIdentityCreateTransitionFixture(); + + request = { + tx: stateTransitionFixture.toBuffer(), + }; + + unserializeStateTransitionMock = this.sinon.stub() + .resolves(stateTransitionFixture); + + checkTxHandler = checkTxHandlerFactory( + unserializeStateTransitionMock, + ); + }); + + it('should validate a State Transition and return response', async () => { + const response = await checkTxHandler(request); + + expect(response).to.be.an.instanceOf(ResponseCheckTx); + expect(response.code).to.equal(0); + + expect(unserializeStateTransitionMock).to.be.calledOnceWith(request.tx); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/commitHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/commitHandlerFactory.spec.js new file mode 100644 index 00000000000..705ccb93e59 --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/commitHandlerFactory.spec.js @@ -0,0 +1,144 @@ +const { + tendermint: { + abci: { + ResponseCommit, + }, + }, +} = require('@dashevo/abci/types'); + +const Long = require('long'); + +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const createDPPMock = require('@dashevo/dpp/lib/test/mocks/createDPPMock'); + +const commitHandlerFactory = require('../../../../lib/abci/handlers/commitHandlerFactory'); + +const RootTreeMock = require('../../../../lib/test/mock/RootTreeMock'); + +const BlockExecutionContextMock = require('../../../../lib/test/mock/BlockExecutionContextMock'); +const LoggerMock = require('../../../../lib/test/mock/LoggerMock'); +const GroveDBStoreMock = require('../../../../lib/test/mock/GroveDBStoreMock'); +const BlockExecutionContextStackMock = require('../../../../lib/test/mock/BlockExecutionContextStackMock'); +const BlockExecutionContextStackRepositoryMock = require('../../../../lib/test/mock/BlockExecutionContextStackRepositoryMock'); + +describe('commitHandlerFactory', () => { + let commitHandler; + let appHash; + let creditsDistributionPoolMock; + let creditsDistributionPoolRepositoryMock; + let blockExecutionContextMock; + let dataContract; + let accumulativeFees; + let rootTreeMock; + let dppMock; + let header; + let dataContractCacheMock; + let blockExecutionContextStackMock; + let blockExecutionContextStackRepositoryMock; + let groveDBStoreMock; + let rotateSignedStoreMock; + let executionTimerMock; + + beforeEach(function beforeEach() { + appHash = Buffer.alloc(0); + + creditsDistributionPoolMock = { + incrementAmount: this.sinon.stub(), + setAmount: this.sinon.stub(), + }; + + dataContract = getDataContractFixture(); + + creditsDistributionPoolRepositoryMock = { + store: this.sinon.stub(), + }; + + blockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + + blockExecutionContextMock.getDataContracts.returns([dataContract]); + blockExecutionContextMock.getCumulativeFees.returns(accumulativeFees); + + header = { + height: Long.fromInt(1), + }; + + blockExecutionContextMock.getHeader.returns(header); + + rootTreeMock = new RootTreeMock(this.sinon); + rootTreeMock.getRootHash.returns(appHash); + + dppMock = createDPPMock(this.sinon); + dppMock.dataContract.createFromBuffer.resolves(dataContract); + + const loggerMock = new LoggerMock(this.sinon); + + dataContractCacheMock = { + set: this.sinon.stub(), + get: this.sinon.stub(), + has: this.sinon.stub(), + }; + + blockExecutionContextStackMock = new BlockExecutionContextStackMock(this.sinon); + blockExecutionContextStackRepositoryMock = new BlockExecutionContextStackRepositoryMock( + this.sinon, + ); + + groveDBStoreMock = new GroveDBStoreMock(this.sinon); + groveDBStoreMock.getRootHash.resolves(appHash); + + executionTimerMock = { + startTimer: this.sinon.stub(), + stopTimer: this.sinon.stub(), + }; + + commitHandler = commitHandlerFactory( + creditsDistributionPoolMock, + creditsDistributionPoolRepositoryMock, + blockExecutionContextMock, + blockExecutionContextStackMock, + blockExecutionContextStackRepositoryMock, + rotateSignedStoreMock, + loggerMock, + dataContractCacheMock, + groveDBStoreMock, + executionTimerMock, + ); + }); + + it('should commit db transactions, create document dbs and return ResponseCommit', async () => { + const response = await commitHandler(); + + expect(response).to.be.an.instanceOf(ResponseCommit); + expect(response.data).to.deep.equal(appHash); + + expect(blockExecutionContextMock.getHeader).to.be.calledOnce(); + + expect(creditsDistributionPoolMock.incrementAmount).to.be.calledOnceWith( + accumulativeFees, + ); + + expect(creditsDistributionPoolRepositoryMock.store).to.be.calledOnceWith( + creditsDistributionPoolMock, + { + useTransaction: true, + }, + ); + + expect(blockExecutionContextStackMock.add).to.be.calledOnceWith( + blockExecutionContextMock, + ); + + expect(blockExecutionContextStackRepositoryMock.store).to.be.calledOnceWith( + blockExecutionContextStackMock, + { + useTransaction: true, + }, + ); + + expect(groveDBStoreMock.commitTransaction).to.be.calledOnce(); + + expect(blockExecutionContextMock.getDataContracts).to.be.calledOnce(); + + expect(groveDBStoreMock.getRootHash).to.be.calledOnce(); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/deliverTxHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/deliverTxHandlerFactory.spec.js new file mode 100644 index 00000000000..0529cd83a3b --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/deliverTxHandlerFactory.spec.js @@ -0,0 +1,249 @@ +const { + tendermint: { + abci: { + ResponseDeliverTx, + }, + }, +} = require('@dashevo/abci/types'); + +const DashPlatformProtocol = require('@dashevo/dpp'); + +const ValidationResult = require('@dashevo/dpp/lib/validation/ValidationResult'); + +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); + +const createDPPMock = require('@dashevo/dpp/lib/test/mocks/createDPPMock'); +const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const getDocumentFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const SomeConsensusError = require('@dashevo/dpp/lib/test/mocks/SomeConsensusError'); +const BlockExecutionContextMock = require('../../../../lib/test/mock/BlockExecutionContextMock'); + +const deliverTxHandlerFactory = require('../../../../lib/abci/handlers/deliverTxHandlerFactory'); + +const LoggerMock = require('../../../../lib/test/mock/LoggerMock'); +const DPPValidationAbciError = require('../../../../lib/abci/errors/DPPValidationAbciError'); + +const InvalidArgumentAbciError = require('../../../../lib/abci/errors/InvalidArgumentAbciError'); +const PredictedFeeLowerThanActualError = require('../../../../lib/abci/handlers/errors/PredictedFeeLowerThanActualError'); +const NegativeBalanceError = require('../../../../lib/abci/handlers/errors/NegativeBalanceError'); + +describe('deliverTxHandlerFactory', () => { + let deliverTxHandler; + let dataContractRequest; + let documentRequest; + let identity; + let dppMock; + let stateRepositoryMock; + let documentsBatchTransitionFixture; + let dataContractCreateTransitionFixture; + let dpp; + let unserializeStateTransitionMock; + let blockExecutionContextMock; + let validationResult; + let executionTimerMock; + + beforeEach(async function beforeEach() { + const dataContractFixture = getDataContractFixture(); + const documentFixture = getDocumentFixture(); + + dpp = new DashPlatformProtocol(); + await dpp.initialize(); + + documentsBatchTransitionFixture = dpp.document.createStateTransition({ + create: documentFixture, + }); + + dataContractCreateTransitionFixture = dpp + .dataContract.createDataContractCreateTransition(dataContractFixture); + + documentRequest = { + tx: documentsBatchTransitionFixture.toBuffer(), + }; + + dataContractRequest = { + tx: dataContractCreateTransitionFixture.toBuffer(), + }; + + dppMock = createDPPMock(this.sinon); + + validationResult = new ValidationResult(); + + dppMock + .stateTransition + .validateState + .resolves(validationResult); + + stateRepositoryMock = createStateRepositoryMock(this.sinon); + + identity = getIdentityFixture(); + + stateRepositoryMock.fetchIdentity.resolves(identity); + + dppMock.getStateRepository.returns(stateRepositoryMock); + + unserializeStateTransitionMock = this.sinon.stub(); + + blockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + blockExecutionContextMock.getHeader.returns({ + height: 42, + }); + + const loggerMock = new LoggerMock(this.sinon); + + executionTimerMock = { + clearTimer: this.sinon.stub(), + getTimer: this.sinon.stub(), + startTimer: this.sinon.stub(), + stopTimer: this.sinon.stub(), + isStarted: this.sinon.stub(), + }; + + deliverTxHandler = deliverTxHandlerFactory( + unserializeStateTransitionMock, + dppMock, + blockExecutionContextMock, + loggerMock, + executionTimerMock, + ); + }); + + it('should apply a DocumentsBatchTransition and return ResponseDeliverTx', async () => { + unserializeStateTransitionMock.resolves(documentsBatchTransitionFixture); + + const response = await deliverTxHandler(documentRequest); + + expect(response).to.be.an.instanceOf(ResponseDeliverTx); + expect(response.code).to.equal(0); + + expect(unserializeStateTransitionMock).to.be.calledOnceWith( + documentsBatchTransitionFixture.toBuffer(), + ); + expect(dppMock.stateTransition.validateState).to.be.calledOnceWith( + documentsBatchTransitionFixture, + ); + expect(dppMock.stateTransition.apply).to.be.calledOnceWith( + documentsBatchTransitionFixture, + ); + expect(blockExecutionContextMock.addDataContract).to.not.be.called(); + + const stateTransitionFee = documentsBatchTransitionFixture.calculateFee(); + + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWith( + documentsBatchTransitionFixture.getOwnerId(), + ); + + identity.reduceBalance(stateTransitionFee); + + expect(stateRepositoryMock.updateIdentity).to.be.calledOnceWith(identity); + + expect(blockExecutionContextMock.incrementCumulativeFees).to.be.calledOnceWith( + stateTransitionFee, + ); + }); + + it('should apply a DataContractCreateTransition, add it to block execution state and return ResponseDeliverTx', async () => { + unserializeStateTransitionMock.resolves(dataContractCreateTransitionFixture); + + const response = await deliverTxHandler(dataContractRequest); + + expect(response).to.be.an.instanceOf(ResponseDeliverTx); + expect(response.code).to.equal(0); + + expect(unserializeStateTransitionMock).to.be.calledOnceWith( + dataContractCreateTransitionFixture.toBuffer(), + ); + expect(dppMock.stateTransition.validateState).to.be.calledOnceWith( + dataContractCreateTransitionFixture, + ); + expect(dppMock.stateTransition.apply).to.be.calledOnceWith( + dataContractCreateTransitionFixture, + ); + expect(blockExecutionContextMock.addDataContract).to.be.calledOnceWith( + dataContractCreateTransitionFixture.getDataContract(), + ); + + expect(blockExecutionContextMock.incrementCumulativeFees).to.be.calledOnceWith( + dataContractCreateTransitionFixture.calculateFee(), + ); + + expect( + dataContractCreateTransitionFixture.getExecutionContext().dryOperations, + ).to.have.length(0); + }); + + it('should throw DPPValidationAbciError if a state transition is invalid against state', async () => { + unserializeStateTransitionMock.resolves(dataContractCreateTransitionFixture); + + const error = new SomeConsensusError('Consensus error'); + + validationResult.addError(error); + + try { + await deliverTxHandler(documentRequest); + + expect.fail('should throw InvalidArgumentAbciError error'); + } catch (e) { + expect(e).to.be.instanceOf(DPPValidationAbciError); + expect(e.getCode()).to.equal(error.getCode()); + expect(e.getData()).to.deep.equal({ + arguments: ['Consensus error'], + }); + expect(blockExecutionContextMock.incrementCumulativeFees).to.not.be.called(); + } + }); + + it('should throw DPPValidationAbciError if a state transition is not valid', async () => { + const errorMessage = 'Invalid structure'; + const error = new InvalidArgumentAbciError(errorMessage); + + unserializeStateTransitionMock.throws(error); + + try { + await deliverTxHandler(documentRequest); + + expect.fail('should throw InvalidArgumentAbciError error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentAbciError); + expect(e.getMessage()).to.equal(errorMessage); + expect(e.getCode()).to.equal(GrpcErrorCodes.INVALID_ARGUMENT); + expect(blockExecutionContextMock.incrementCumulativeFees).to.not.be.called(); + expect(dppMock.stateTransition.validate).to.not.be.called(); + } + }); + + it('should throw PredictedFeeLowerThanActualError if actual fee > predicted fee', async function it() { + dataContractCreateTransitionFixture.calculateFee = this.sinon.stub().returns(0); + + dataContractCreateTransitionFixture.calculateFee.onCall(1).returns(10); + + unserializeStateTransitionMock.resolves(dataContractCreateTransitionFixture); + + try { + await deliverTxHandler(documentRequest); + + expect.fail('should throw InvalidArgumentAbciError error'); + } catch (e) { + expect(e).to.be.instanceOf(PredictedFeeLowerThanActualError); + expect(e.getStateTransition().toBuffer()) + .to.deep.equal(dataContractCreateTransitionFixture.toBuffer()); + } + }); + + it('should throw NegativeBalanceError if balance < fee', async function it() { + dataContractCreateTransitionFixture.calculateFee = this.sinon.stub().returns(0); + + dataContractCreateTransitionFixture.calculateFee.returns(100); + + unserializeStateTransitionMock.resolves(dataContractCreateTransitionFixture); + + try { + await deliverTxHandler(documentRequest); + + expect.fail('should throw InvalidArgumentAbciError error'); + } catch (e) { + expect(e).to.be.instanceOf(NegativeBalanceError); + } + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/endBlockHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/endBlockHandlerFactory.spec.js new file mode 100644 index 00000000000..13e0863cf0b --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/endBlockHandlerFactory.spec.js @@ -0,0 +1,196 @@ +const { + tendermint: { + abci: { + ResponseEndBlock, + ValidatorSetUpdate, + }, + types: { + CoreChainLock, + }, + }, +} = require('@dashevo/abci/types'); + +const Long = require('long'); + +const endBlockHandlerFactory = require('../../../../lib/abci/handlers/endBlockHandlerFactory'); + +const BlockExecutionContextMock = require('../../../../lib/test/mock/BlockExecutionContextMock'); +const LoggerMock = require('../../../../lib/test/mock/LoggerMock'); + +describe('endBlockHandlerFactory', () => { + let endBlockHandler; + let requestMock; + let headerMock; + let lastCommitInfoMock; + let blockExecutionContextMock; + let dpnsContractBlockHeight; + let latestCoreChainLockMock; + let loggerMock; + let createValidatorSetUpdateMock; + let chainLockMock; + let validatorSetMock; + let getFeatureFlagForHeightMock; + + beforeEach(function beforeEach() { + headerMock = { + coreChainLockedHeight: 2, + version: { + app: Long.fromInt(1), + }, + }; + + lastCommitInfoMock = { + stateSignature: Uint8Array.from('003657bb44d74c371d14485117de43313ca5c2848f3622d691c2b1bf3576a64bdc2538efab24854eb82ae7db38482dbd15a1cb3bc98e55173817c9d05c86e47a5d67614a501414aae6dd1565e59422d1d77c41ae9b38de34ecf1e9f778b2a97b'), + }; + + blockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + + blockExecutionContextMock.hasDataContract.returns(true); + blockExecutionContextMock.getHeader.returns(headerMock); + blockExecutionContextMock.getLastCommitInfo.returns(lastCommitInfoMock); + + chainLockMock = { + height: 1, + blockHash: Buffer.alloc(0), + signature: Buffer.alloc(0), + }; + + latestCoreChainLockMock = { + getChainLock: this.sinon.stub().returns(chainLockMock), + }; + + loggerMock = new LoggerMock(this.sinon); + + dpnsContractBlockHeight = 2; + + validatorSetMock = { + rotate: this.sinon.stub(), + getQuorum: this.sinon.stub(), + }; + + createValidatorSetUpdateMock = this.sinon.stub(); + + getFeatureFlagForHeightMock = this.sinon.stub().resolves(null); + + endBlockHandler = endBlockHandlerFactory( + blockExecutionContextMock, + latestCoreChainLockMock, + validatorSetMock, + createValidatorSetUpdateMock, + loggerMock, + getFeatureFlagForHeightMock, + ); + + requestMock = { + height: Long.fromInt(dpnsContractBlockHeight), + }; + }); + + it('should finalize a block', async () => { + endBlockHandler = endBlockHandlerFactory( + blockExecutionContextMock, + latestCoreChainLockMock, + validatorSetMock, + createValidatorSetUpdateMock, + loggerMock, + getFeatureFlagForHeightMock, + ); + + const response = await endBlockHandler(requestMock); + + expect(response).to.be.an.instanceOf(ResponseEndBlock); + expect(response.toJSON()).to.be.empty(); + + expect(blockExecutionContextMock.hasDataContract).to.not.have.been.called(); + }); + + it('should return nextCoreChainLockUpdate if latestCoreChainLock above header height', async () => { + chainLockMock.height = 3; + + const response = await endBlockHandler(requestMock); + + expect(latestCoreChainLockMock.getChainLock).to.have.been.calledOnceWithExactly(); + + const expectedCoreChainLock = new CoreChainLock({ + coreBlockHeight: chainLockMock.height, + coreBlockHash: chainLockMock.blockHash, + signature: chainLockMock.signature, + }); + + expect(response.nextCoreChainLockUpdate).to.deep.equal(expectedCoreChainLock); + expect(response.validatorSetUpdate).to.be.null(); + }); + + it('should rotate validator set and return ValidatorSetUpdate if height is divisible by ROTATION_BLOCK_INTERVAL', async () => { + requestMock = { + height: Long.fromInt(15), + }; + + validatorSetMock.rotate.resolves(true); + + const quorumHash = Buffer.alloc(64).fill(1).toString('hex'); + validatorSetMock.getQuorum.returns({ + quorumHash, + }); + + const validatorSetUpdate = new ValidatorSetUpdate(); + + createValidatorSetUpdateMock.returns(validatorSetUpdate); + + const response = await endBlockHandler(requestMock); + + expect(response).to.be.an.instanceOf(ResponseEndBlock); + + expect(validatorSetMock.rotate).to.be.calledOnceWithExactly( + requestMock.height, + chainLockMock.height, + Buffer.from(lastCommitInfoMock.stateSignature), + ); + + expect(createValidatorSetUpdateMock).to.be.calledOnceWithExactly(validatorSetMock); + + expect(response.validatorSetUpdate).to.be.equal(validatorSetUpdate); + }); + + it('should return consensusParamUpdates if request contains update consensus features flag', async function it() { + const getLatestFeatureFlagGetMock = this.sinon.stub(); + getLatestFeatureFlagGetMock.withArgs('block').returns({ + maxBytes: 1, + maxGas: 2, + }); + getLatestFeatureFlagGetMock.withArgs('evidence').returns({ + maxAgeNumBlocks: 1, + maxAgeDuration: null, + maxBytes: 2, + }); + getLatestFeatureFlagGetMock.withArgs('version').returns({ + appVersion: 1, + }); + + getFeatureFlagForHeightMock.resolves({ + get: getLatestFeatureFlagGetMock, + }); + + const response = await endBlockHandler(requestMock); + + expect(response).to.be.an.instanceOf(ResponseEndBlock); + + expect(response.toJSON()).to.deep.equal({ + consensusParamUpdates: { + block: { + maxBytes: '1', + maxGas: '2', + }, + evidence: { + maxAgeNumBlocks: '1', + maxBytes: '2', + }, + version: { + appVersion: '1', + }, + }, + }); + + expect(getFeatureFlagForHeightMock).to.be.calledOnce(); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/infoHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/infoHandlerFactory.spec.js new file mode 100644 index 00000000000..9b50fb2dc41 --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/infoHandlerFactory.spec.js @@ -0,0 +1,137 @@ +const Long = require('long'); + +const { + tendermint: { + abci: { + ResponseInfo, + }, + }, +} = require('@dashevo/abci/types'); + +const infoHandlerFactory = require('../../../../lib/abci/handlers/infoHandlerFactory'); + +const packageJson = require('../../../../package.json'); +const LoggerMock = require('../../../../lib/test/mock/LoggerMock'); + +const BlockExecutionContextMock = require('../../../../lib/test/mock/BlockExecutionContextMock'); +const GroveDBStoreMock = require('../../../../lib/test/mock/GroveDBStoreMock'); +const BlockExecutionContextStackMock = require('../../../../lib/test/mock/BlockExecutionContextStackMock'); +const BlockExecutionContextStackRepositoryMock = require('../../../../lib/test/mock/BlockExecutionContextStackRepositoryMock'); +const CreditsDistributionPoolRepositoryMock = require('../../../../lib/test/mock/CreditsDistributionPoolRepositoryMock'); +const CreditsDistributionPoolMock = require('../../../../lib/test/mock/CreditsDistributionPoolMock'); +const StorageResult = require('../../../../lib/storage/StorageResult'); + +describe('infoHandlerFactory', () => { + let protocolVersion; + let lastBlockHeight; + let lastBlockAppHash; + let infoHandler; + let updateSimplifiedMasternodeListMock; + let lastCoreChainLockedHeight; + let loggerMock; + let blockExecutionContextMock; + let blockExecutionContextStackMock; + let blockExecutionContextStackRepositoryMock; + let groveDBStoreMock; + let creditsDistributionPoolRepositoryMock; + let creditsDistributionPoolMock; + + beforeEach(function beforeEach() { + lastBlockHeight = Long.fromInt(0); + lastBlockAppHash = Buffer.alloc(0); + protocolVersion = Long.fromInt(1); + lastCoreChainLockedHeight = 0; + + updateSimplifiedMasternodeListMock = this.sinon.stub(); + + loggerMock = new LoggerMock(this.sinon); + + blockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + blockExecutionContextStackMock = new BlockExecutionContextStackMock(this.sinon); + blockExecutionContextStackRepositoryMock = new BlockExecutionContextStackRepositoryMock( + this.sinon, + ); + creditsDistributionPoolRepositoryMock = new CreditsDistributionPoolRepositoryMock(this.sinon); + creditsDistributionPoolMock = new CreditsDistributionPoolMock(this.sinon); + groveDBStoreMock = new GroveDBStoreMock(this.sinon); + + blockExecutionContextStackRepositoryMock.fetch.resolves({ + getContexts: this.sinon.stub(), + }); + + creditsDistributionPoolRepositoryMock.fetch.resolves( + new StorageResult({ + toJSON: this.sinon.stub().returns('json'), + }), + ); + + groveDBStoreMock.getRootHash.resolves(lastBlockAppHash); + + infoHandler = infoHandlerFactory( + blockExecutionContextStackMock, + blockExecutionContextStackRepositoryMock, + blockExecutionContextMock, + protocolVersion, + updateSimplifiedMasternodeListMock, + loggerMock, + groveDBStoreMock, + creditsDistributionPoolRepositoryMock, + creditsDistributionPoolMock, + ); + }); + + it('should return respond with genesis heights and app hash on the first run', async () => { + const response = await infoHandler(); + + expect(response).to.be.an.instanceOf(ResponseInfo); + + expect(response).to.deep.include({ + version: packageJson.version, + appVersion: protocolVersion, + lastBlockHeight, + lastBlockAppHash, + }); + + expect(blockExecutionContextStackRepositoryMock.fetch).to.be.calledOnce(); + expect(blockExecutionContextStackMock.getLatest).to.be.calledOnce(); + expect(blockExecutionContextMock.populate).to.not.be.called(); + expect(creditsDistributionPoolRepositoryMock.fetch).to.not.be.called(); + expect(blockExecutionContextMock.getHeader).to.not.be.called(); + expect(updateSimplifiedMasternodeListMock).to.not.be.called(); + expect(groveDBStoreMock.getRootHash).to.be.calledOnce(); + }); + + it('should populate context, initialize Credits Distribution Pool and update SML on subsequent runs', async () => { + blockExecutionContextStackMock.getLatest.returns(blockExecutionContextMock); + + lastBlockHeight = Long.fromInt(1); + lastCoreChainLockedHeight = 2; + + blockExecutionContextMock.getHeader.returns({ + height: lastBlockHeight, + coreChainLockedHeight: lastCoreChainLockedHeight, + }); + + const response = await infoHandler(); + + expect(response).to.be.an.instanceOf(ResponseInfo); + + expect(ResponseInfo.toObject(response)).to.deep.equal({ + version: packageJson.version, + appVersion: protocolVersion, + lastBlockHeight, + lastBlockAppHash, + }); + + expect(creditsDistributionPoolRepositoryMock.fetch).to.be.calledOnce(); + expect(creditsDistributionPoolMock.populate).to.be.calledOnceWithExactly('json'); + expect(blockExecutionContextMock.getHeader).to.be.calledOnce(); + + expect(updateSimplifiedMasternodeListMock).to.be.calledOnceWithExactly( + lastCoreChainLockedHeight, + { + logger: loggerMock, + }, + ); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/initChainHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/initChainHandlerFactory.spec.js new file mode 100644 index 00000000000..5c4523a9638 --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/initChainHandlerFactory.spec.js @@ -0,0 +1,128 @@ +const Long = require('long'); + +const { + tendermint: { + abci: { + ResponseInitChain, + ValidatorSetUpdate, + }, + }, +} = require('@dashevo/abci/types'); + +const initChainHandlerFactory = require('../../../../lib/abci/handlers/initChainHandlerFactory'); +const LoggerMock = require('../../../../lib/test/mock/LoggerMock'); +const GroveDBStoreMock = require('../../../../lib/test/mock/GroveDBStoreMock'); + +describe('initChainHandlerFactory', () => { + let initChainHandler; + let updateSimplifiedMasternodeListMock; + let initialCoreChainLockedHeight; + let validatorSetMock; + let createValidatorSetUpdateMock; + let loggerMock; + let validatorSetUpdate; + let synchronizeMasternodeIdentitiesMock; + let registerSystemDataContractsMock; + let createInitialStateStructureMock; + let groveDBStoreMock; + let appHashFixture; + + beforeEach(function beforeEach() { + initialCoreChainLockedHeight = 1; + + appHashFixture = Buffer.alloc(0); + + updateSimplifiedMasternodeListMock = this.sinon.stub(); + + const quorumHash = Buffer.alloc(64).fill(1).toString('hex'); + validatorSetMock = { + initialize: this.sinon.stub(), + getQuorum: this.sinon.stub().returns({ + quorumHash, + }), + }; + + validatorSetUpdate = new ValidatorSetUpdate(); + + createValidatorSetUpdateMock = this.sinon.stub().returns(validatorSetUpdate); + synchronizeMasternodeIdentitiesMock = this.sinon.stub().resolves({ + createdEntities: [], + updatedEntities: [], + removedEntities: [], + fromHeight: 1, + toHeight: 42, + }); + + loggerMock = new LoggerMock(this.sinon); + + registerSystemDataContractsMock = this.sinon.stub(); + createInitialStateStructureMock = this.sinon.stub(); + + groveDBStoreMock = new GroveDBStoreMock(this.sinon); + groveDBStoreMock.getRootHash.resolves(appHashFixture); + + initChainHandler = initChainHandlerFactory( + updateSimplifiedMasternodeListMock, + initialCoreChainLockedHeight, + validatorSetMock, + createValidatorSetUpdateMock, + synchronizeMasternodeIdentitiesMock, + loggerMock, + createInitialStateStructureMock, + registerSystemDataContractsMock, + groveDBStoreMock, + ); + }); + + it('should initialize the chain', async () => { + const request = { + initialHeight: Long.fromInt(1), + chainId: 'test', + time: { + seconds: Long.fromInt((new Date()).getTime() / 1000), + }, + }; + + const response = await initChainHandler(request); + + expect(response).to.be.an.instanceOf(ResponseInitChain); + expect(response.validatorSetUpdate).to.be.equal(validatorSetUpdate); + expect(response.initialCoreHeight).to.be.equal(initialCoreChainLockedHeight); + expect(response.appHash).to.deep.equal(appHashFixture); + + // Update SML + + expect(updateSimplifiedMasternodeListMock).to.be.calledOnceWithExactly( + initialCoreChainLockedHeight, + { + logger: loggerMock, + }, + ); + + // Create initial state + + expect(groveDBStoreMock.startTransaction).to.be.calledOnce(); + + expect(createInitialStateStructureMock).to.be.calledOnce(); + + expect(registerSystemDataContractsMock).to.be.calledOnceWithExactly(loggerMock, request.time); + + expect(synchronizeMasternodeIdentitiesMock).to.be.calledOnceWithExactly( + initialCoreChainLockedHeight, + ); + + expect(groveDBStoreMock.commitTransaction).to.be.calledOnce(); + + expect(groveDBStoreMock.getRootHash).to.be.calledOnce(); + + // Initialize VS + + expect(validatorSetMock.initialize).to.be.calledOnceWithExactly( + initialCoreChainLockedHeight, + ); + + expect(validatorSetMock.getQuorum).to.be.calledOnce(); + + expect(createValidatorSetUpdateMock).to.be.calledOnceWithExactly(validatorSetMock); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/query/dataContractQueryHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/query/dataContractQueryHandlerFactory.spec.js new file mode 100644 index 00000000000..203f97fafb2 --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/query/dataContractQueryHandlerFactory.spec.js @@ -0,0 +1,147 @@ +const { + tendermint: { + abci: { + ResponseQuery, + }, + }, +} = require('@dashevo/abci/types'); + +const { + v0: { + GetDataContractResponse, + Proof, + }, +} = require('@dashevo/dapi-grpc'); + +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); + +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); +const dataContractQueryHandlerFactory = require('../../../../../lib/abci/handlers/query/dataContractQueryHandlerFactory'); + +const NotFoundAbciError = require('../../../../../lib/abci/errors/NotFoundAbciError'); +const StoreRepositoryMock = require('../../../../../lib/test/mock/StoreRepositoryMock'); +const BlockExecutionContextStackMock = require('../../../../../lib/test/mock/BlockExecutionContextStackMock'); +const InvalidArgumentAbciError = require('../../../../../lib/abci/errors/InvalidArgumentAbciError'); +const StorageResult = require('../../../../../lib/storage/StorageResult'); + +describe('dataContractQueryHandlerFactory', () => { + let dataContractQueryHandler; + let dataContract; + let params; + let data; + let createQueryResponseMock; + let responseMock; + let blockExecutionContextStackMock; + let signedDataContractRepositoryMock; + + beforeEach(function beforeEach() { + dataContract = getDataContractFixture(); + + createQueryResponseMock = this.sinon.stub(); + + responseMock = new GetDataContractResponse(); + responseMock.setProof(new Proof()); + + createQueryResponseMock.returns(responseMock); + + blockExecutionContextStackMock = new BlockExecutionContextStackMock(this.sinon); + blockExecutionContextStackMock.getLast.returns(true); + + signedDataContractRepositoryMock = new StoreRepositoryMock(this.sinon); + + dataContractQueryHandler = dataContractQueryHandlerFactory( + signedDataContractRepositoryMock, + createQueryResponseMock, + blockExecutionContextStackMock, + ); + + blockExecutionContextStackMock.getLast.returns(true); + + params = { }; + data = { + id: dataContract.getId(), + }; + }); + + it('should throw NotFoundAbciError if there is no signed state', async () => { + blockExecutionContextStackMock.getLast.returns(null); + + try { + await dataContractQueryHandler(params, data, {}); + + expect.fail('should throw NotFoundAbciError'); + } catch (e) { + expect(e).to.be.an.instanceOf(NotFoundAbciError); + expect(blockExecutionContextStackMock.getLast).to.be.calledOnce(); + expect(signedDataContractRepositoryMock.fetch).to.be.not.called(); + } + }); + + it('should throw NotFoundAbciError if Data Contract not found', async () => { + signedDataContractRepositoryMock.fetch.resolves( + new StorageResult(null), + ); + + try { + await dataContractQueryHandler(params, data, {}); + + expect.fail('should throw NotFoundAbciError'); + } catch (e) { + expect(e).to.be.an.instanceOf(NotFoundAbciError); + expect(blockExecutionContextStackMock.getLast).to.be.calledOnce(); + expect(signedDataContractRepositoryMock.fetch).to.be.calledOnce(); + } + }); + + it('should return data contract', async () => { + signedDataContractRepositoryMock.fetch.resolves( + new StorageResult(dataContract), + ); + + const result = await dataContractQueryHandler(params, data, {}); + + expect(result).to.be.an.instanceof(ResponseQuery); + expect(result.code).to.equal(0); + expect(result.value).to.deep.equal(responseMock.serializeBinary()); + }); + + it('should InvalidArgumentAbciError on wrong Id', async () => { + data.id = Buffer.alloc(0); + + try { + await dataContractQueryHandler(params, data, {}); + + expect.fail('should throw InvalidArgumentAbciError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidArgumentAbciError); + } + }); + + it('should return proof if it was requested', async () => { + // const proof = { + // rootTreeProof: Buffer.from('0100000001f0faf5f55674905a68eba1be2f946e667c1cb5010101', 'hex'), + // storeTreeProof: Buffer.from('03046b657931060076616c75653103046b657932060076616c75653210', + // 'hex'), + // }; + + const proof = Buffer.alloc(20, 255); + + signedDataContractRepositoryMock.fetch.resolves( + new StorageResult(dataContract), + ); + + signedDataContractRepositoryMock.prove.resolves( + new StorageResult(proof), + ); + + const result = await dataContractQueryHandler(params, data, { prove: true }); + + expect(signedDataContractRepositoryMock.prove).to.be.calledOnceWithExactly( + new Identifier(data.id), + ); + + expect(result).to.be.an.instanceof(ResponseQuery); + expect(result.code).to.equal(0); + expect(result.value).to.deep.equal(responseMock.serializeBinary()); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/query/documentQueryHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/query/documentQueryHandlerFactory.spec.js new file mode 100644 index 00000000000..1a12c07eade --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/query/documentQueryHandlerFactory.spec.js @@ -0,0 +1,188 @@ +const { + tendermint: { + abci: { + ResponseQuery, + }, + }, +} = require('@dashevo/abci/types'); + +const { + v0: { + GetDocumentsResponse, + ResponseMetadata, + Proof, + }, +} = require('@dashevo/dapi-grpc'); + +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); + +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); + +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const documentQueryHandlerFactory = require('../../../../../lib/abci/handlers/query/documentQueryHandlerFactory'); +const InvalidQueryError = require('../../../../../lib/document/errors/InvalidQueryError'); + +const UnavailableAbciError = require('../../../../../lib/abci/errors/UnavailableAbciError'); +const InvalidArgumentAbciError = require('../../../../../lib/abci/errors/InvalidArgumentAbciError'); +const BlockExecutionContextStackMock = require('../../../../../lib/test/mock/BlockExecutionContextStackMock'); +const StorageResult = require('../../../../../lib/storage/StorageResult'); + +describe('documentQueryHandlerFactory', () => { + let documentQueryHandler; + let fetchSignedDocumentsMock; + let proveSignedDocumentsMock; + let documents; + let params; + let data; + let options; + let createQueryResponseMock; + let responseMock; + let blockExecutionContextStackMock; + + beforeEach(function beforeEach() { + documents = getDocumentsFixture(); + + fetchSignedDocumentsMock = this.sinon.stub(); + proveSignedDocumentsMock = this.sinon.stub(); + createQueryResponseMock = this.sinon.stub(); + + responseMock = new GetDocumentsResponse(); + responseMock.setProof(new Proof()); + + createQueryResponseMock.returns(responseMock); + + blockExecutionContextStackMock = new BlockExecutionContextStackMock(this.sinon); + blockExecutionContextStackMock.getLast.returns(true); + + documentQueryHandler = documentQueryHandlerFactory( + fetchSignedDocumentsMock, + proveSignedDocumentsMock, + createQueryResponseMock, + blockExecutionContextStackMock, + ); + + params = {}; + data = { + contractId: generateRandomIdentifier(), + type: 'documentType', + orderBy: [{ sort: 'asc' }], + limit: 2, + startAt: undefined, + startAfter: undefined, + where: [['field', '==', 'value']], + }; + options = { + orderBy: data.orderBy, + limit: data.limit, + startAt: data.startAt, + startAfter: data.startAfter, + where: data.where, + }; + }); + + it('should return empty response if there is no signed state', async () => { + blockExecutionContextStackMock.getLast.returns(null); + + responseMock = new GetDocumentsResponse(); + + responseMock.setMetadata(new ResponseMetadata()); + + const result = await documentQueryHandler(params, data, {}); + + expect(createQueryResponseMock).to.have.not.been.called(); + expect(fetchSignedDocumentsMock).to.have.not.been.called(); + expect(proveSignedDocumentsMock).to.have.not.been.called(); + expect(result).to.be.an.instanceof(ResponseQuery); + expect(result.code).to.equal(0); + + expect(result.value).to.deep.equal(responseMock.serializeBinary()); + }); + + it('should return serialized documents', async () => { + fetchSignedDocumentsMock.resolves( + new StorageResult(documents), + ); + + const result = await documentQueryHandler(params, data, {}); + + expect(createQueryResponseMock).to.be.calledOnceWith(GetDocumentsResponse, undefined); + expect(fetchSignedDocumentsMock).to.be.calledOnceWith(data.contractId, data.type, options); + expect(proveSignedDocumentsMock).to.not.be.called(); + expect(result).to.be.an.instanceof(ResponseQuery); + expect(result.code).to.equal(0); + + expect(result.value).to.deep.equal(responseMock.serializeBinary()); + }); + + it('should return proof if it was requested', async () => { + // const proof = { + // rootTreeProof: Buffer.from('0100000001f0faf5f55674905a68eba1be2f946e667c1cb5010101', + // 'hex'), + // storeTreeProof: Buffer.from('03046b657931060076616c75653103046b657932060076616c75653210', + // 'hex'), + // }; + + const proof = Buffer.alloc(20, 255); + + fetchSignedDocumentsMock.resolves(new StorageResult(documents)); + proveSignedDocumentsMock.resolves( + new StorageResult(proof), + ); + + const result = await documentQueryHandler(params, data, { prove: true }); + + expect(createQueryResponseMock).to.be.calledOnceWith(GetDocumentsResponse, true); + expect(fetchSignedDocumentsMock).to.not.be.called(); + expect(proveSignedDocumentsMock).to.be.calledOnceWith(data.contractId, data.type, options); + + expect(result).to.be.an.instanceof(ResponseQuery); + expect(result.code).to.equal(0); + + expect(result.value).to.deep.equal(responseMock.serializeBinary()); + }); + + it('should throw InvalidArgumentAbciError on invalid query', async () => { + fetchSignedDocumentsMock.throws(new InvalidQueryError('invalid')); + + try { + await documentQueryHandler(params, data, {}); + + expect.fail('should throw UnavailableAbciError'); + } catch (e) { + expect(e).to.be.an.instanceof(InvalidArgumentAbciError); + expect(e.getCode()).to.equal(GrpcErrorCodes.INVALID_ARGUMENT); + expect(e.getMessage()).to.equal('Invalid query: invalid'); + expect(fetchSignedDocumentsMock).to.be.calledOnceWith(data.contractId, data.type); + } + }); + + it('should not proceed forward if createQueryResponse throws UnavailableAbciError', async () => { + createQueryResponseMock.throws(new UnavailableAbciError('message')); + + try { + await documentQueryHandler(params, data, {}); + + expect.fail('should throw UnavailableAbciError'); + } catch (e) { + expect(e).to.be.an.instanceof(UnavailableAbciError); + expect(e.getCode()).to.equal(GrpcErrorCodes.UNAVAILABLE); + expect(e.getMessage()).to.equal('message'); + expect(fetchSignedDocumentsMock).to.not.be.called(); + } + }); + + it('should throw error if fetchSignedDocuments throws unknown error', async () => { + const error = new Error('Some error'); + + fetchSignedDocumentsMock.throws(error); + + try { + await documentQueryHandler(params, data, {}); + + expect.fail('should throw any error'); + } catch (e) { + expect(e).to.deep.equal(error); + expect(fetchSignedDocumentsMock).to.be.calledOnceWith(data.contractId, data.type); + } + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/query/getProofsQueryHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/query/getProofsQueryHandlerFactory.spec.js new file mode 100644 index 00000000000..a083106b2bf --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/query/getProofsQueryHandlerFactory.spec.js @@ -0,0 +1,147 @@ +const { + tendermint: { + abci: { + ResponseQuery, + }, + }, +} = require('@dashevo/abci/types'); + +const Long = require('long'); +const cbor = require('cbor'); + +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); + +const getProofsQueryHandlerFactory = require('../../../../../lib/abci/handlers/query/getProofsQueryHandlerFactory'); +const BlockExecutionContextMock = require('../../../../../lib/test/mock/BlockExecutionContextMock'); +const BlockExecutionContextStackMock = require('../../../../../lib/test/mock/BlockExecutionContextStackMock'); +const StorageResult = require('../../../../../lib/storage/StorageResult'); + +describe('getProofsQueryHandlerFactory', () => { + let getProofsQueryHandler; + let dataContract; + let identity; + let documents; + let dataContractData; + let documentsData; + let identityData; + let blockExecutionContextStackMock; + let signedBlockExecutionContextMock; + let blockExecutionContextMock; + let signedIdentityRepositoryMock; + let signedDataContractRepositoryMock; + let signedDocumentRepository; + + beforeEach(function beforeEach() { + dataContract = getDataContractFixture(); + identity = getIdentityFixture(); + documents = getDocumentsFixture(); + + signedBlockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + signedBlockExecutionContextMock.getHeader.returns({ + height: new Long(42), + coreChainLockedHeight: 41, + }); + + blockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + + blockExecutionContextMock.getLastCommitInfo.returns({ + quorumHash: Buffer.alloc(32, 1), + stateSignature: Buffer.alloc(32, 1), + }); + + blockExecutionContextStackMock = new BlockExecutionContextStackMock(this.sinon); + blockExecutionContextStackMock.getLast.returns(signedBlockExecutionContextMock); + blockExecutionContextStackMock.getFirst.returns(blockExecutionContextMock); + + signedIdentityRepositoryMock = { + proveMany: this.sinon.stub().resolves(new StorageResult(Buffer.from([1]))), + }; + signedDataContractRepositoryMock = { + proveMany: this.sinon.stub().resolves(new StorageResult(Buffer.from([1]))), + }; + + signedDocumentRepository = { + proveManyDocumentsFromDifferentContracts: this.sinon.stub().resolves( + new StorageResult(Buffer.from([1])), + ), + }; + + getProofsQueryHandler = getProofsQueryHandlerFactory( + blockExecutionContextStackMock, + signedIdentityRepositoryMock, + signedDataContractRepositoryMock, + signedDocumentRepository, + ); + + dataContractData = { + id: dataContract.getId(), + }; + identityData = { + id: identity.getId(), + }; + documentsData = documents.map((doc) => ({ + documentId: doc.getId(), + dataContractId: doc.getDataContractId(), + type: doc.getType(), + })); + }); + + it('should return empty response if there is no signed state', async () => { + blockExecutionContextStackMock.getLast.returns(null); + + const result = await getProofsQueryHandler({}, {}, {}); + + expect(result).to.be.an.instanceof(ResponseQuery); + expect(result.code).to.equal(0); + + const emptyValue = cbor.encode( + { + documentsProof: null, + identitiesProof: null, + dataContractsProof: null, + metadata: { + height: 0, + coreChainLockedHeight: 0, + }, + }, + ); + + expect(result.value).to.deep.equal(emptyValue); + }); + + it('should return proof for passed data contract ids', async () => { + const expectedProof = { + signatureLlmqHash: Buffer.alloc(32, 1), + signature: Buffer.alloc(32, 1), + merkleProof: Buffer.from([1]), + // rootTreeProof: Buffer.from('0100000001f0faf5f55674905a68eba1be2f946e667c1cb5010101', + // 'hex'), + // storeTreeProof: Buffer.from('03046b657931060076616c75653103046b657932060076616c75653210', + // 'hex'), + }; + + const result = await getProofsQueryHandler({}, { + dataContractIds: [dataContractData.id], + identityIds: [identityData.id], + documents: documentsData, + }); + + const expectedResult = new ResponseQuery({ + value: cbor.encode( + { + documentsProof: expectedProof, + identitiesProof: expectedProof, + dataContractsProof: expectedProof, + metadata: { + height: 42, + coreChainLockedHeight: 41, + }, + }, + ), + }); + + expect(result).to.be.deep.equal(expectedResult); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/query/identitiesByPublicKeyHashesQueryHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/query/identitiesByPublicKeyHashesQueryHandlerFactory.spec.js new file mode 100644 index 00000000000..4f5c6404103 --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/query/identitiesByPublicKeyHashesQueryHandlerFactory.spec.js @@ -0,0 +1,161 @@ +const { + tendermint: { + abci: { + ResponseQuery, + }, + }, +} = require('@dashevo/abci/types'); + +const { + v0: { + GetIdentitiesByPublicKeyHashesResponse, + Proof, + ResponseMetadata, + }, +} = require('@dashevo/dapi-grpc'); + +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); + +const identitiesByPublicKeyHashesQueryHandlerFactory = require( + '../../../../../lib/abci/handlers/query/identitiesByPublicKeyHashesQueryHandlerFactory', +); +const InvalidArgumentAbciError = require('../../../../../lib/abci/errors/InvalidArgumentAbciError'); +const BlockExecutionContextStackMock = require('../../../../../lib/test/mock/BlockExecutionContextStackMock'); +const StorageResult = require('../../../../../lib/storage/StorageResult'); + +describe('identitiesByPublicKeyHashesQueryHandlerFactory', () => { + let identitiesByPublicKeyHashesQueryHandler; + let signedPublicKeyToIdentitiesRepositoryMock; + let publicKeyHashes; + let identities; + let maxIdentitiesPerRequest; + let createQueryResponseMock; + let responseMock; + let blockExecutionContextStackMock; + let params; + let data; + + beforeEach(function beforeEach() { + signedPublicKeyToIdentitiesRepositoryMock = { + fetchManyBuffers: this.sinon.stub(), + proveMany: this.sinon.stub(), + }; + + maxIdentitiesPerRequest = 5; + + createQueryResponseMock = this.sinon.stub(); + + responseMock = new GetIdentitiesByPublicKeyHashesResponse(); + responseMock.setProof(new Proof()); + + createQueryResponseMock.returns(responseMock); + + blockExecutionContextStackMock = new BlockExecutionContextStackMock(this.sinon); + + blockExecutionContextStackMock.getLast.returns(true); + + identitiesByPublicKeyHashesQueryHandler = identitiesByPublicKeyHashesQueryHandlerFactory( + signedPublicKeyToIdentitiesRepositoryMock, + maxIdentitiesPerRequest, + createQueryResponseMock, + blockExecutionContextStackMock, + ); + + publicKeyHashes = [ + Buffer.from('784ca12495d2e61f992db9e55d1f9599b0cf1328', 'hex'), + Buffer.from('784ca12495d2e61f992db9e55d1f9599b0cf1329', 'hex'), + Buffer.from('784ca12495d2e61f992db9e55d1f9599b0cf1330', 'hex'), + ]; + + identities = [ + getIdentityFixture(), + getIdentityFixture(), + ]; + + signedPublicKeyToIdentitiesRepositoryMock + .fetchManyBuffers.resolves( + new StorageResult([identities[0].toBuffer(), identities[1].toBuffer()]), + ); + + params = {}; + data = { publicKeyHashes }; + }); + + it('should return empty response if there is no signed state', async () => { + blockExecutionContextStackMock.getLast.returns(null); + + responseMock = new GetIdentitiesByPublicKeyHashesResponse(); + responseMock.setIdentitiesList([]); + responseMock.setMetadata(new ResponseMetadata()); + + const result = await identitiesByPublicKeyHashesQueryHandler(params, data, {}); + + expect(result).to.be.an.instanceof(ResponseQuery); + expect(result.code).to.equal(0); + + expect(result.value).to.deep.equal(responseMock.serializeBinary()); + + expect(signedPublicKeyToIdentitiesRepositoryMock.fetchManyBuffers).to.have.not.been.called(); + }); + + it('should throw an error if maximum requested items exceeded', async () => { + maxIdentitiesPerRequest = 1; + + identitiesByPublicKeyHashesQueryHandler = identitiesByPublicKeyHashesQueryHandlerFactory( + signedPublicKeyToIdentitiesRepositoryMock, + maxIdentitiesPerRequest, + createQueryResponseMock, + blockExecutionContextStackMock, + ); + + try { + await identitiesByPublicKeyHashesQueryHandler(params, data, {}); + + expect.fail('Error was not thrown'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidArgumentAbciError); + expect(e.getData()).to.deep.equal({ + maxIdentitiesPerRequest, + }); + } + }); + + it('should return identities', async () => { + params = publicKeyHashes; + + const result = await identitiesByPublicKeyHashesQueryHandler(params, data, {}); + + expect(signedPublicKeyToIdentitiesRepositoryMock.fetchManyBuffers).to.be.calledOnceWithExactly( + publicKeyHashes, + ); + + expect(result).to.be.an.instanceof(ResponseQuery); + expect(result.code).to.equal(0); + expect(result.value).to.deep.equal(responseMock.serializeBinary()); + }); + + it('should return proof if it was requested', async () => { + // const proof = { + // rootTreeProof: Buffer.from('0100000001f0faf5f55674905a68eba1be2f946e667c1cb5010101', + // 'hex'), + // storeTreeProof: Buffer.from('03046b657931060076616c75653103046b657932060076616c75653210', + // 'hex'), + // }; + + const proof = Buffer.alloc(20, 1); + + signedPublicKeyToIdentitiesRepositoryMock.proveMany.resolves( + new StorageResult(proof), + ); + + const result = await identitiesByPublicKeyHashesQueryHandler(params, data, { prove: true }); + + expect(result).to.be.an.instanceof(ResponseQuery); + expect(result.code).to.equal(0); + expect(result.value).to.deep.equal(responseMock.serializeBinary()); + + expect(signedPublicKeyToIdentitiesRepositoryMock.proveMany).to.be.calledOnceWithExactly( + data.publicKeyHashes, + ); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/query/identityQueryHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/query/identityQueryHandlerFactory.spec.js new file mode 100644 index 00000000000..e54c1d568b7 --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/query/identityQueryHandlerFactory.spec.js @@ -0,0 +1,134 @@ +const { + tendermint: { + abci: { + ResponseQuery, + }, + }, +} = require('@dashevo/abci/types'); + +const { + v0: { + GetIdentityResponse, + Proof, + }, +} = require('@dashevo/dapi-grpc'); + +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); + +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const identityQueryHandlerFactory = require('../../../../../lib/abci/handlers/query/identityQueryHandlerFactory'); +const BlockExecutionContextMock = require('../../../../../lib/test/mock/BlockExecutionContextMock'); +const NotFoundAbciError = require('../../../../../lib/abci/errors/NotFoundAbciError'); +const BlockExecutionContextStackMock = require('../../../../../lib/test/mock/BlockExecutionContextStackMock'); +const StorageResult = require('../../../../../lib/storage/StorageResult'); + +describe('identityQueryHandlerFactory', () => { + let identityQueryHandler; + let signedIdentityRepositoryMock; + let identity; + let params; + let data; + let createQueryResponseMock; + let responseMock; + let blockExecutionContextMock; + let blockExecutionContextStackMock; + + beforeEach(function beforeEach() { + signedIdentityRepositoryMock = { + fetch: this.sinon.stub(), + prove: this.sinon.stub(), + }; + + createQueryResponseMock = this.sinon.stub(); + + responseMock = new GetIdentityResponse(); + responseMock.setProof(new Proof()); + + createQueryResponseMock.returns(responseMock); + + blockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + blockExecutionContextStackMock = new BlockExecutionContextStackMock(this.sinon); + blockExecutionContextStackMock.getLast.returns(true); + + identityQueryHandler = identityQueryHandlerFactory( + signedIdentityRepositoryMock, + createQueryResponseMock, + blockExecutionContextMock, + blockExecutionContextStackMock, + ); + + identity = getIdentityFixture(); + + params = {}; + data = { + id: identity.getId(), + }; + }); + + it('should throw NotFoundAbciError if there is no signed state', async () => { + blockExecutionContextStackMock.getLast.returns(null); + + try { + await identityQueryHandler(params, data, {}); + + expect.fail('should throw NotFoundAbciError'); + } catch (e) { + expect(e).to.be.an.instanceOf(NotFoundAbciError); + } + }); + + it('should return serialized identity', async () => { + signedIdentityRepositoryMock.fetch.resolves( + new StorageResult(identity), + ); + + const result = await identityQueryHandler(params, data, {}); + + expect(signedIdentityRepositoryMock.fetch).to.be.calledOnceWith(data.id); + expect(result).to.be.an.instanceof(ResponseQuery); + expect(result.code).to.equal(0); + expect(result.value).to.deep.equal(responseMock.serializeBinary()); + }); + + it('should throw NotFoundAbciError if identity not found', async () => { + signedIdentityRepositoryMock.fetch.resolves( + new StorageResult(null), + ); + + try { + await identityQueryHandler(params, data, {}); + + expect.fail('should throw NotFoundAbciError'); + } catch (e) { + expect(e).to.be.an.instanceof(NotFoundAbciError); + expect(e.getCode()).to.equal(GrpcErrorCodes.NOT_FOUND); + expect(e.message).to.equal('Identity not found'); + expect(signedIdentityRepositoryMock.fetch).to.be.calledOnceWith(data.id); + } + }); + + it('should return proof if it was requested', async () => { + // const proof = { + // rootTreeProof: Buffer.from('0100000001f0faf5f55674905a68eba1be2f946e667c1cb5010101', + // 'hex'), + // storeTreeProof: Buffer.from('03046b657931060076616c75653103046b657932060076616c75653210', + // 'hex'), + // }; + const proof = Buffer.alloc(20, 1); + + signedIdentityRepositoryMock.fetch.resolves( + new StorageResult(null), + ); + signedIdentityRepositoryMock.prove.resolves( + new StorageResult(proof), + ); + + const result = await identityQueryHandler(params, data, { prove: true }); + + expect(signedIdentityRepositoryMock.fetch).to.not.be.called(); + expect(signedIdentityRepositoryMock.prove).to.be.calledOnceWith(data.id); + expect(result).to.be.an.instanceof(ResponseQuery); + expect(result.code).to.equal(0); + expect(result.value).to.deep.equal(responseMock.serializeBinary()); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/query/response/createQueryResponseFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/query/response/createQueryResponseFactory.spec.js new file mode 100644 index 00000000000..f0cf4402e2d --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/query/response/createQueryResponseFactory.spec.js @@ -0,0 +1,72 @@ +const { + v0: { + GetDataContractResponse, + }, +} = require('@dashevo/dapi-grpc'); + +const BlockExecutionContextMock = require('../../../../../../lib/test/mock/BlockExecutionContextMock'); +const createQueryResponseFactory = require('../../../../../../lib/abci/handlers/query/response/createQueryResponseFactory'); +const BlockExecutionContextStackMock = require('../../../../../../lib/test/mock/BlockExecutionContextStackMock'); + +describe('createQueryResponseFactory', () => { + let blockExecutionContextStackMock; + let createQueryResponse; + let metadata; + let lastCommitInfo; + let signedBlockExecutionContext; + let blockExecutionContextMock; + + beforeEach(function beforeEach() { + signedBlockExecutionContext = new BlockExecutionContextMock(this.sinon); + blockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + blockExecutionContextStackMock = new BlockExecutionContextStackMock(this.sinon); + + blockExecutionContextStackMock.getLast.returns(signedBlockExecutionContext); + blockExecutionContextStackMock.getFirst.returns(blockExecutionContextMock); + + metadata = { + height: 1, + coreChainLockedHeight: 1, + }; + + signedBlockExecutionContext.getHeader.returns(metadata); + + lastCommitInfo = { + quorumHash: Buffer.alloc(12).fill(1), + stateSignature: Buffer.alloc(12).fill(2), + }; + + blockExecutionContextMock.getLastCommitInfo.returns(lastCommitInfo); + + createQueryResponse = createQueryResponseFactory( + blockExecutionContextStackMock, + ); + }); + + it('should create a response', () => { + const response = createQueryResponse(GetDataContractResponse); + + response.serializeBinary(); + + expect(response).to.be.instanceOf(GetDataContractResponse); + + expect(response.getMetadata().toObject()).to.deep.equal(metadata); + expect(response.getProof()).to.undefined(); + }); + + it('should create a response with proof if requested', () => { + const response = createQueryResponse(GetDataContractResponse, true); + + response.serializeBinary(); + + expect(response).to.be.instanceOf(GetDataContractResponse); + + expect(response.getMetadata().toObject()).to.deep.equal(metadata); + + expect(response.getProof().toObject()).to.deep.equal({ + signatureLlmqHash: lastCommitInfo.quorumHash.toString('base64'), + signature: lastCommitInfo.stateSignature.toString('base64'), + merkleProof: '', + }); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/query/verifyChainLockQueryHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/query/verifyChainLockQueryHandlerFactory.spec.js new file mode 100644 index 00000000000..9f8dbd4e3c5 --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/query/verifyChainLockQueryHandlerFactory.spec.js @@ -0,0 +1,178 @@ +const { + tendermint: { + abci: { + ResponseQuery, + }, + }, +} = require('@dashevo/abci/types'); + +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const verifyChainLockQueryHandlerFactory = require('../../../../../lib/abci/handlers/query/verifyChainLockQueryHandlerFactory'); + +const LoggerMock = require('../../../../../lib/test/mock/LoggerMock'); +const BlockExecutionContextMock = require('../../../../../lib/test/mock/BlockExecutionContextMock'); +const InvalidArgumentAbciError = require('../../../../../lib/abci/errors/InvalidArgumentAbciError'); + +describe('verifyChainLockQueryHandlerFactory', () => { + let simplifiedMasternodeListMock; + let verifyChainLockQueryHandler; + let params; + let decodeChainLockMock; + let encodedChainLock; + let chainLockMock; + let loggerMock; + let getLatestFeatureFlagMock; + let blockExecutionContextMock; + let coreRpcClientMock; + + beforeEach(function beforeEach() { + params = {}; + + simplifiedMasternodeListMock = { + getStore: this.sinon.stub(), + }; + + simplifiedMasternodeListMock.getStore.returns({ + tipHeight: 42, + }); + + chainLockMock = { + verify: this.sinon.stub(), + toJSON: this.sinon.stub(), + }; + chainLockMock.blockHash = Buffer.alloc(0); + chainLockMock.signature = Buffer.alloc(0); + chainLockMock.height = 42; + + loggerMock = new LoggerMock(this.sinon); + + decodeChainLockMock = this.sinon.stub().returns(chainLockMock); + + encodedChainLock = Buffer.alloc(0); + + getLatestFeatureFlagMock = this.sinon.stub(); + getLatestFeatureFlagMock.resolves(null); + + blockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + blockExecutionContextMock.getHeader.returns({ + height: 42, + coreChainLockedHeight: 43, + }); + + coreRpcClientMock = { + verifyChainLock: this.sinon.stub(), + }; + coreRpcClientMock.verifyChainLock.resolves({ result: true }); + + verifyChainLockQueryHandler = verifyChainLockQueryHandlerFactory( + simplifiedMasternodeListMock, + decodeChainLockMock, + getLatestFeatureFlagMock, + blockExecutionContextMock, + coreRpcClientMock, + loggerMock, + ); + }); + + it('should validate a valid chainLock', async () => { + chainLockMock.verify.returns(true); + + const result = await verifyChainLockQueryHandler(params, encodedChainLock); + + expect(result).to.be.an.instanceof(ResponseQuery); + expect(result.code).to.equal(0); + + expect(decodeChainLockMock).to.be.calledOnceWithExactly(encodedChainLock); + }); + + it('should throw InvalidArgumentAbciError if chainLock is not valid', async () => { + coreRpcClientMock.verifyChainLock.returns(false); + + try { + await verifyChainLockQueryHandler(params, encodedChainLock); + + expect.fail('should throw InvalidArgumentAbciError'); + } catch (e) { + expect(e).to.be.an.instanceof(InvalidArgumentAbciError); + expect(e.getCode()).to.equal(GrpcErrorCodes.INVALID_ARGUMENT); + expect(e.message).to.equal('ChainLock verification failed'); + } + }); + + it('should verify chain lock though Core', async () => { + const result = await verifyChainLockQueryHandler(params, encodedChainLock); + + expect(result).to.be.an.instanceof(ResponseQuery); + expect(result.code).to.equal(0); + + expect(decodeChainLockMock).to.be.calledOnceWithExactly(encodedChainLock); + expect(coreRpcClientMock.verifyChainLock).to.be.calledOnceWithExactly( + chainLockMock.blockHash.toString('hex'), + chainLockMock.signature.toString('hex'), + chainLockMock.height, + ); + }); + + it('should return false if Core returns parse error', async () => { + const error = new Error(); + error.code = -32700; + + coreRpcClientMock.verifyChainLock.throws(error); + + const result = await verifyChainLockQueryHandler(params, encodedChainLock); + + expect(result).to.deep.equal(new ResponseQuery({ + code: -32700, + log: 'Chainlock verification failed using verifyChainLock method: ', + })); + + expect(decodeChainLockMock).to.be.calledOnceWithExactly(encodedChainLock); + expect(coreRpcClientMock.verifyChainLock).to.be.calledOnceWithExactly( + chainLockMock.blockHash.toString('hex'), + chainLockMock.signature.toString('hex'), + chainLockMock.height, + ); + }); + + it('should return false if Core returns invalid signature format error', async () => { + const error = new Error(); + error.code = -8; + + coreRpcClientMock.verifyChainLock.throws(error); + + const result = await verifyChainLockQueryHandler(params, encodedChainLock); + + expect(result).to.deep.equal(new ResponseQuery({ + code: -8, + log: 'Chainlock verification failed using verifyChainLock method: ', + })); + + expect(decodeChainLockMock).to.be.calledOnceWithExactly(encodedChainLock); + expect(coreRpcClientMock.verifyChainLock).to.be.calledOnceWithExactly( + chainLockMock.blockHash.toString('hex'), + chainLockMock.signature.toString('hex'), + chainLockMock.height, + ); + }); + + it('should throw an error if Core throws error', async () => { + const error = new Error(); + + coreRpcClientMock.verifyChainLock.throws(error); + + try { + await verifyChainLockQueryHandler(params, encodedChainLock); + + expect.fail('error was not thrown'); + } catch (e) { + expect(e).to.deep.equal(error); + } + + expect(decodeChainLockMock).to.be.calledOnceWithExactly(encodedChainLock); + expect(coreRpcClientMock.verifyChainLock).to.be.calledOnceWithExactly( + chainLockMock.blockHash.toString('hex'), + chainLockMock.signature.toString('hex'), + chainLockMock.height, + ); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/queryHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/queryHandlerFactory.spec.js new file mode 100644 index 00000000000..d7027723173 --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/queryHandlerFactory.spec.js @@ -0,0 +1,122 @@ +const cbor = require('cbor'); + +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const queryHandlerFactory = require('../../../../lib/abci/handlers/queryHandlerFactory'); +const LoggerMock = require('../../../../lib/test/mock/LoggerMock'); +const InvalidArgumentAbciError = require('../../../../lib/abci/errors/InvalidArgumentAbciError'); + +describe('queryHandlerFactory', () => { + let queryHandler; + let queryHandlerRouterMock; + let sanitizeUrlMock; + let request; + let routeMock; + let loggerMock; + + beforeEach(function beforeEach() { + request = { + path: '/identity', + data: cbor.encode(Buffer.from('data')), + }; + + loggerMock = new LoggerMock(this.sinon); + + sanitizeUrlMock = this.sinon.stub(); + + routeMock = { + handler: this.sinon.stub(), + params: 'params', + }; + + queryHandlerRouterMock = { + find: this.sinon.stub().returns(routeMock), + }; + + queryHandler = queryHandlerFactory( + queryHandlerRouterMock, + sanitizeUrlMock, + loggerMock, + ); + }); + + it('should throw InvalidArgumentAbciError if route was not found', async () => { + const sanitizedUrl = 'sanitizedUrl'; + + sanitizeUrlMock.returns(sanitizedUrl); + queryHandlerRouterMock.find.returns(false); + + try { + await queryHandler(request); + + expect.fail('should throw InvalidArgumentAbciError'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentAbciError); + expect(e.getCode()).to.equal(GrpcErrorCodes.INVALID_ARGUMENT); + + expect(sanitizeUrlMock).to.be.calledOnceWith(request.path); + expect(queryHandlerRouterMock.find).to.be.calledOnceWith('GET', sanitizedUrl); + expect(routeMock.handler).to.be.not.called(); + } + }); + + it('should throw InvalidArgumentAbciError if fail to decode request data', async () => { + const sanitizedUrl = 'sanitizedUrl'; + + sanitizeUrlMock.returns(sanitizedUrl); + + request.data = Buffer.from('bb'); + + try { + await queryHandler(request); + + expect.fail('should throw InvalidArgumentAbciError'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentAbciError); + expect(e.getCode()).to.equal(GrpcErrorCodes.INVALID_ARGUMENT); + + expect(sanitizeUrlMock).to.be.calledOnceWith(request.path); + expect(queryHandlerRouterMock.find).to.be.calledOnceWith('GET', sanitizedUrl); + expect(routeMock.handler).to.be.not.called(); + } + }); + + it('should throw InvalidArgumentAbciError on invalid request data', async () => { + const sanitizedUrl = 'sanitizedUrl'; + + sanitizeUrlMock.returns(sanitizedUrl); + + request.data = cbor.encode(null); + + try { + await queryHandler(request); + + expect.fail('should throw InvalidArgumentAbciError'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentAbciError); + expect(e.getCode()).to.equal(GrpcErrorCodes.INVALID_ARGUMENT); + + expect(sanitizeUrlMock).to.be.calledOnceWith(request.path); + expect(queryHandlerRouterMock.find).to.be.calledOnceWith('GET', sanitizedUrl); + expect(routeMock.handler).to.be.not.called(); + } + }); + + it('should call route handler without data'); + + it('should call route handler and return response', async () => { + const data = 'some data'; + const encodedData = cbor.decode(Buffer.from(request.data)); + const sanitizedUrl = 'sanitizedUrl'; + + sanitizeUrlMock.returns(sanitizedUrl); + routeMock.handler.resolves(data); + queryHandlerRouterMock.find.returns(routeMock); + + const result = await queryHandler(request); + + expect(sanitizeUrlMock).to.be.calledOnceWith(request.path); + expect(queryHandlerRouterMock.find).to.be.calledOnceWith('GET', sanitizedUrl); + expect(routeMock.handler).to.be.calledOnceWith(routeMock.params, encodedData, request); + expect(result).to.equal(data); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/stateTransition/unserializeStateTransitionFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/stateTransition/unserializeStateTransitionFactory.spec.js new file mode 100644 index 00000000000..29c54743eb5 --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/stateTransition/unserializeStateTransitionFactory.spec.js @@ -0,0 +1,203 @@ +const getIdentityCreateTransitionFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityCreateTransitionFixture'); + +const InvalidStateTransitionTypeError = require('@dashevo/dpp/lib/errors/consensus/basic/stateTransition/InvalidStateTransitionTypeError'); +const InvalidStateTransitionError = require('@dashevo/dpp/lib/stateTransition/errors/InvalidStateTransitionError'); +const BalanceNotEnoughError = require('@dashevo/dpp/lib/errors/consensus/fee/BalanceIsNotEnoughError'); +const ValidatorResult = require('@dashevo/dpp/lib/validation/ValidationResult'); + +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const IdentityNotFoundError = require('@dashevo/dpp/lib/errors/consensus/signature/IdentityNotFoundError'); +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); +const unserializeStateTransitionFactory = require('../../../../../lib/abci/handlers/stateTransition/unserializeStateTransitionFactory'); +const LoggerMock = require('../../../../../lib/test/mock/LoggerMock'); +const DPPValidationAbciError = require('../../../../../lib/abci/errors/DPPValidationAbciError'); +const InvalidArgumentAbciError = require('../../../../../lib/abci/errors/InvalidArgumentAbciError'); + +describe('unserializeStateTransitionFactory', () => { + let unserializeStateTransition; + let stateTransitionFixture; + let dppMock; + let noopLoggerMock; + let stateTransition; + + beforeEach(function beforeEach() { + stateTransition = getIdentityCreateTransitionFixture(); + stateTransitionFixture = stateTransition.toBuffer(); + + dppMock = { + dispose: this.sinon.stub(), + stateTransition: { + createFromBuffer: this.sinon.stub(), + validateFee: this.sinon.stub(), + validateSignature: this.sinon.stub(), + validateState: this.sinon.stub(), + apply: this.sinon.stub(), + }, + }; + + dppMock.stateTransition.validateSignature.resolves(new ValidatorResult()); + + noopLoggerMock = new LoggerMock(this.sinon); + + unserializeStateTransition = unserializeStateTransitionFactory(dppMock, noopLoggerMock); + }); + + it('should throw InvalidArgumentAbciError if State Transition is not specified', async () => { + try { + await unserializeStateTransition(); + + expect.fail('should throw InvalidArgumentAbciError error'); + } catch (e) { + expect(e).to.be.instanceOf(InvalidArgumentAbciError); + expect(e.getMessage()).to.equal('State Transition is not specified'); + expect(e.getCode()).to.equal(GrpcErrorCodes.INVALID_ARGUMENT); + + expect(dppMock.stateTransition.validateFee).to.not.be.called(); + } + }); + + it('should throw InvalidArgumentAbciError if State Transition is invalid', async () => { + const dppError = new InvalidStateTransitionTypeError(-1); + const error = new InvalidStateTransitionError( + [dppError], + stateTransitionFixture, + ); + + dppMock.stateTransition.createFromBuffer.throws(error); + + try { + await unserializeStateTransition(stateTransitionFixture); + + expect.fail('should throw InvalidArgumentAbciError error'); + } catch (e) { + expect(e).to.be.instanceOf(DPPValidationAbciError); + expect(e.getCode()).to.equal(dppError.getCode()); + expect(e.getData()).to.deep.equal({ + arguments: [-1], + }); + + expect(dppMock.stateTransition.createFromBuffer).to.be.calledOnce(); + expect(dppMock.stateTransition.validateFee).to.not.be.called(); + } + }); + + it('should throw the error from createFromBuffer if throws not InvalidStateTransitionError', async () => { + const error = new Error('Custom error'); + dppMock.stateTransition.createFromBuffer.throws(error); + + try { + await unserializeStateTransition(stateTransitionFixture); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.equal(error); + + expect(dppMock.stateTransition.createFromBuffer).to.be.calledOnce(); + expect(dppMock.stateTransition.validateFee).to.not.be.called(); + } + }); + + it('should throw InsufficientFundsError in case if identity has not enough credits', async () => { + const balance = 1000; + const fee = 1; + const error = new BalanceNotEnoughError(balance, fee); + + dppMock.stateTransition.validateFee.resolves( + new ValidatorResult([error]), + ); + + dppMock.stateTransition.createFromBuffer.resolves(stateTransition); + + try { + await unserializeStateTransition(stateTransitionFixture); + + expect.fail('should throw an InsufficientFundsError'); + } catch (e) { + expect(e).to.be.instanceOf(DPPValidationAbciError); + expect(e.getCode()).to.equal(error.getCode()); + expect(e.getData()).to.deep.equal({ + arguments: [balance, fee], + }); + + expect(dppMock.stateTransition.createFromBuffer).to.be.calledOnce(); + expect(dppMock.stateTransition.validateFee).to.be.calledOnce(); + } + }); + + it('should return invalid result if validateSignature failed', async () => { + const identity = getIdentityFixture(); + const error = new IdentityNotFoundError(identity.getId()); + + dppMock.stateTransition.validateSignature.resolves( + new ValidatorResult([error]), + ); + + try { + await unserializeStateTransition(stateTransitionFixture); + + expect.fail('should throw an InsufficientFundsError'); + } catch (e) { + expect(e).to.be.instanceOf(DPPValidationAbciError); + expect(e.getCode()).to.equal(error.getCode()); + expect(e.getData()).to.deep.equal({ + arguments: [identity.getId()], + }); + + expect(dppMock.stateTransition.createFromBuffer).to.be.calledOnce(); + expect(dppMock.stateTransition.validateFee).to.have.not.been.called(); + } + }); + + it('should return stateTransition', async () => { + dppMock.stateTransition.createFromBuffer.resolves(stateTransition); + + dppMock.stateTransition.validateFee.resolves(new ValidatorResult()); + + const result = await unserializeStateTransition(stateTransitionFixture); + + expect(result).to.deep.equal(stateTransition); + + expect(dppMock.stateTransition.validateFee).to.be.calledOnceWith(stateTransition); + expect(dppMock.stateTransition.validateState).to.be.calledOnceWithExactly(stateTransition); + expect(dppMock.stateTransition.apply).to.be.calledOnceWithExactly(stateTransition); + }); + + it('should use provided logger', async function it() { + const loggerMock = new LoggerMock(this.sinon); + + const balance = 1000; + const fee = 1000; + const error = new BalanceNotEnoughError(balance, fee); + + dppMock.stateTransition.createFromBuffer.resolves(stateTransition); + + dppMock.stateTransition.validateFee.resolves( + new ValidatorResult([error]), + ); + + try { + await unserializeStateTransition(stateTransitionFixture, { logger: loggerMock }); + + expect.fail('should throw an InsufficientFundsError'); + } catch (e) { + expect(e).to.be.instanceOf(DPPValidationAbciError); + expect(e.getCode()).to.equal(error.getCode()); + expect(e.getData()).to.deep.equal({ + arguments: [balance, fee], + }); + + expect(dppMock.stateTransition.createFromBuffer).to.be.calledOnce(); + expect(dppMock.stateTransition.validateFee).to.be.calledOnce(); + + expect(noopLoggerMock.info).to.not.have.been.called(); + expect(noopLoggerMock.debug).to.not.have.been.called(); + + expect(loggerMock.info).to.have.been.calledOnceWithExactly( + 'Insufficient funds to process state transition', + ); + expect(loggerMock.debug).to.have.been.calledOnceWithExactly({ + consensusError: error, + }); + } + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/validator/createValidatorSetUpdate.spec.js b/packages/js-drive/test/unit/abci/handlers/validator/createValidatorSetUpdate.spec.js new file mode 100644 index 00000000000..30c170d277f --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/validator/createValidatorSetUpdate.spec.js @@ -0,0 +1,57 @@ +const { + tendermint: { + abci: { + ValidatorSetUpdate, + }, + }, +} = require('@dashevo/abci/types'); +const { expect } = require('chai'); + +const createValidatorSetUpdate = require('../../../../../lib/abci/handlers/validator/createValidatorSetUpdate'); +const ValidatorNetworkInfo = require('../../../../../lib/validator/ValidatorNetworkInfo'); + +describe('createValidatorSetUpdate', () => { + let validatorSetMock; + let validatorMock; + let quorumHash; + let quorumPublicKey; + + beforeEach(function beforeEach() { + validatorMock = { + getPublicKeyShare: this.sinon.stub(), + getVotingPower: this.sinon.stub(), + getProTxHash: this.sinon.stub(), + getNetworkInfo: this.sinon.stub(), + }; + + validatorMock.getVotingPower.returns(Buffer.alloc(2, 32)); + validatorMock.getProTxHash.returns(Buffer.alloc(3, 32)); + validatorMock.getNetworkInfo.returns(new ValidatorNetworkInfo('192.168.65.2', 26656)); + + validatorSetMock = { + getValidators: this.sinon.stub(), + getQuorum: this.sinon.stub(), + }; + + quorumHash = Buffer.alloc(1, 32).toString('hex'); + quorumPublicKey = 'a7e75af9dd4d868a41ad2f5a5b021d653e31084261724fb40ae2f1b1c31c778d3b9464502d599cf6720723ec5c68b59d'; + + validatorMock.getPublicKeyShare.returns( + Buffer.from(quorumPublicKey, 'hex'), + ); + + validatorSetMock.getValidators.returns([validatorMock]); + validatorSetMock.getQuorum.returns({ + quorumHash, + quorumPublicKey, + }); + }); + + it('should create ValidatorSetUpdate object from specified ValidatorSet instance', () => { + const result = createValidatorSetUpdate(validatorSetMock); + + expect(result).to.be.an.instanceOf(ValidatorSetUpdate); + + // TODO: check something else? + }); +}); diff --git a/packages/js-drive/test/unit/blockExecution/BlockExecutionContext.spec.js b/packages/js-drive/test/unit/blockExecution/BlockExecutionContext.spec.js new file mode 100644 index 00000000000..30bf271eb10 --- /dev/null +++ b/packages/js-drive/test/unit/blockExecution/BlockExecutionContext.spec.js @@ -0,0 +1,250 @@ +const { + tendermint: { + abci: { + LastCommitInfo, + }, + types: { + Header, + }, + }, +} = require('@dashevo/abci/types'); + +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const BlockExecutionContext = require('../../../lib/blockExecution/BlockExecutionContext'); +const getBlockExecutionContextObjectFixture = require('../../../lib/test/fixtures/getBlockExecutionContextObjectFixture'); + +describe('BlockExecutionContext', () => { + let blockExecutionContext; + let dataContract; + let lastCommitInfo; + let header; + let logger; + let cumulativeFees; + let plainObject; + let validTxs; + let invalidTxs; + + beforeEach(() => { + blockExecutionContext = new BlockExecutionContext(); + dataContract = getDataContractFixture(); + delete dataContract.entropy; + + plainObject = getBlockExecutionContextObjectFixture(dataContract); + + lastCommitInfo = LastCommitInfo.fromObject(plainObject.lastCommitInfo); + + header = Header.fromObject(plainObject.header); + + logger = plainObject.consensusLogger; + cumulativeFees = plainObject.cumulativeFees; + validTxs = plainObject.validTxs; + invalidTxs = plainObject.invalidTxs; + }); + + describe('#addDataContract', () => { + it('should add a Data Contract', async () => { + expect(blockExecutionContext.getDataContracts()).to.have.lengthOf(0); + + blockExecutionContext.addDataContract(dataContract); + const contracts = blockExecutionContext.getDataContracts(); + + expect(contracts).to.have.lengthOf(1); + expect(contracts[0]).to.deep.equal(dataContract); + }); + }); + + describe('#hasDataContract', () => { + it('should respond with false if data contract with specified ID is not present', async () => { + const result = blockExecutionContext.hasDataContract(dataContract.getId()); + + expect(result).to.be.false(); + }); + + it('should respond with true if data contract with specified ID is present', async () => { + blockExecutionContext.addDataContract(dataContract); + + const result = blockExecutionContext.hasDataContract(dataContract.getId()); + + expect(result).to.be.true(); + }); + }); + + describe('#getDataContracts', () => { + it('should get data contracts', async () => { + blockExecutionContext.addDataContract(dataContract); + blockExecutionContext.addDataContract(dataContract); + + const contracts = blockExecutionContext.getDataContracts(); + + expect(contracts).to.have.lengthOf(2); + expect(contracts[0]).to.deep.equal(dataContract); + expect(contracts[1]).to.deep.equal(dataContract); + }); + }); + + describe('#getCumulativeFees', () => { + it('should get cumulative fees', async () => { + let result = blockExecutionContext.getCumulativeFees(); + + expect(result).to.equal(0); + + blockExecutionContext.cumulativeFees = cumulativeFees; + + result = blockExecutionContext.getCumulativeFees(); + + expect(result).to.equal(cumulativeFees); + }); + }); + + describe('#incrementCumulativeFees', () => { + it('should increment cumulative fees', async () => { + let result = blockExecutionContext.getCumulativeFees(); + + expect(result).to.equal(0); + + blockExecutionContext.incrementCumulativeFees(15); + + result = blockExecutionContext.getCumulativeFees(); + + expect(result).to.equal(15); + }); + }); + + describe('#reset', () => { + it('should reset state', () => { + blockExecutionContext.addDataContract(dataContract); + + expect(blockExecutionContext.getDataContracts()).to.have.lengthOf(1); + + blockExecutionContext.reset(); + + expect(blockExecutionContext.getDataContracts()).to.have.lengthOf(0); + + expect(blockExecutionContext.getHeader()).to.be.null(); + }); + }); + + describe('#setHeader', () => { + it('should set header', async () => { + const result = blockExecutionContext.setHeader(header); + + expect(result).to.equal(blockExecutionContext); + + expect(blockExecutionContext.header).to.deep.equal(header); + }); + }); + + describe('#getHeader', () => { + it('should get header', async () => { + blockExecutionContext.header = header; + + expect(blockExecutionContext.getHeader()).to.deep.equal(header); + }); + }); + + describe('#setLastCommitInfo', () => { + it('should set lastCommitInfo', async () => { + const result = blockExecutionContext.setLastCommitInfo(lastCommitInfo); + + expect(result).to.equal(blockExecutionContext); + + expect(blockExecutionContext.lastCommitInfo).to.deep.equal(lastCommitInfo); + }); + }); + + describe('#getLastCommitInfo', () => { + it('should get lastCommitInfo', async () => { + blockExecutionContext.lastCommitInfo = lastCommitInfo; + + expect(blockExecutionContext.getLastCommitInfo()).to.deep.equal(lastCommitInfo); + }); + }); + + describe('#populate', () => { + it('should populate instance from another instance', () => { + const anotherBlockExecutionContext = new BlockExecutionContext(); + + anotherBlockExecutionContext.dataContracts = [dataContract]; + anotherBlockExecutionContext.lastCommitInfo = lastCommitInfo; + anotherBlockExecutionContext.cumulativeFees = cumulativeFees; + anotherBlockExecutionContext.header = header; + anotherBlockExecutionContext.validTxs = validTxs; + anotherBlockExecutionContext.invalidTxs = invalidTxs; + anotherBlockExecutionContext.consensusLogger = logger; + + blockExecutionContext.populate(anotherBlockExecutionContext); + + expect(blockExecutionContext.dataContracts).to.equal( + anotherBlockExecutionContext.dataContracts, + ); + expect(blockExecutionContext.lastCommitInfo).to.equal( + anotherBlockExecutionContext.lastCommitInfo, + ); + expect(blockExecutionContext.cumulativeFees).to.equal( + anotherBlockExecutionContext.cumulativeFees, + ); + expect(blockExecutionContext.header).to.equal( + anotherBlockExecutionContext.header, + ); + expect(blockExecutionContext.validTxs).to.equal( + anotherBlockExecutionContext.validTxs, + ); + expect(blockExecutionContext.invalidTxs).to.equal( + anotherBlockExecutionContext.invalidTxs, + ); + expect(blockExecutionContext.consensusLogger).to.equal( + anotherBlockExecutionContext.consensusLogger, + ); + }); + }); + + describe('#toObject', () => { + it('should return a plain object', () => { + blockExecutionContext.dataContracts = [dataContract]; + blockExecutionContext.lastCommitInfo = lastCommitInfo; + blockExecutionContext.cumulativeFees = cumulativeFees; + blockExecutionContext.header = header; + blockExecutionContext.validTxs = validTxs; + blockExecutionContext.invalidTxs = invalidTxs; + blockExecutionContext.consensusLogger = logger; + + expect(blockExecutionContext.toObject()).to.deep.equal(plainObject); + }); + + it('should skipConsensusLogger if the option passed', () => { + blockExecutionContext.dataContracts = [dataContract]; + blockExecutionContext.lastCommitInfo = lastCommitInfo; + blockExecutionContext.cumulativeFees = cumulativeFees; + blockExecutionContext.header = header; + blockExecutionContext.validTxs = validTxs; + blockExecutionContext.invalidTxs = invalidTxs; + blockExecutionContext.consensusLogger = logger; + + const result = blockExecutionContext.toObject({ skipConsensusLogger: true }); + + delete plainObject.consensusLogger; + + expect(result).to.deep.equal(plainObject); + }); + }); + + describe('#fromObject', () => { + it('should populate instance from a plain object', () => { + blockExecutionContext.fromObject(plainObject); + + if (blockExecutionContext.dataContracts[0].$defs === undefined) { + blockExecutionContext.dataContracts[0].$defs = {}; + } + + expect(blockExecutionContext.dataContracts).to.have.deep.members( + [dataContract], + ); + expect(blockExecutionContext.lastCommitInfo).to.deep.equal(lastCommitInfo); + expect(blockExecutionContext.cumulativeFees).to.equal(cumulativeFees); + expect(blockExecutionContext.header).to.deep.equal(header); + expect(blockExecutionContext.validTxs).to.equal(validTxs); + expect(blockExecutionContext.invalidTxs).to.equal(invalidTxs); + expect(blockExecutionContext.consensusLogger).to.equal(logger); + }); + }); +}); diff --git a/packages/js-drive/test/unit/blockExecution/BlockExecutionContextStack.spec.js b/packages/js-drive/test/unit/blockExecution/BlockExecutionContextStack.spec.js new file mode 100644 index 00000000000..96bb23ddb4a --- /dev/null +++ b/packages/js-drive/test/unit/blockExecution/BlockExecutionContextStack.spec.js @@ -0,0 +1,145 @@ +const getBlockExecutionContextObjectFixture = require('../../../lib/test/fixtures/getBlockExecutionContextObjectFixture'); +const BlockExecutionContextStack = require('../../../lib/blockExecution/BlockExecutionContextStack'); +const BlockExecutionContext = require('../../../lib/blockExecution/BlockExecutionContext'); +const ContextsAreMoreThanStackMaxSizeError = require('../../../lib/blockExecution/errors/ContextsAreMoreThanStackMaxSizeError'); + +describe('BlockExecutionContextStack', () => { + let blockExecutionContextStack; + let blockExecutionContext; + + beforeEach(() => { + blockExecutionContextStack = new BlockExecutionContextStack(); + + blockExecutionContext = new BlockExecutionContext(); + blockExecutionContext.fromObject( + getBlockExecutionContextObjectFixture(), + ); + }); + + describe('#setContexts and #getContexts', () => { + it('should set contexts', () => { + blockExecutionContextStack.setContexts([ + blockExecutionContext, + ]); + + expect(blockExecutionContextStack.getContexts()).to.have.members([ + blockExecutionContext, + ]); + }); + + it('should throw ContextsAreMoreThanStackMaxSizeError error if contexts are more than stack max size', () => { + try { + blockExecutionContextStack.setContexts([ + blockExecutionContext, + blockExecutionContext, + blockExecutionContext, + blockExecutionContext, + ]); + + expect.fail('should throw ContextsAreMoreThanStackMaxSizeError'); + } catch (e) { + expect(e).to.be.an.instanceOf(ContextsAreMoreThanStackMaxSizeError); + } + }); + }); + + describe('#getFirst', () => { + it('should return the first context from the stack', () => { + let result = blockExecutionContextStack.getFirst(); + + expect(result).to.be.undefined(); + + blockExecutionContextStack.setContexts([ + blockExecutionContext, + ]); + + result = blockExecutionContextStack.getFirst(); + + expect(result).to.equals(blockExecutionContext); + }); + }); + + describe('#getLast', () => { + it('should return the last context from the stack', () => { + let result = blockExecutionContextStack.getLast(); + + expect(result).to.be.undefined(); + + const lastContext = new BlockExecutionContext(); + + blockExecutionContextStack.setContexts([ + blockExecutionContext, + blockExecutionContext, + lastContext, + ]); + + result = blockExecutionContextStack.getLast(); + + expect(result).to.equals(lastContext); + }); + }); + + describe('#removeLatest', () => { + it('should return remove the last context from the stack', () => { + const lastContext = new BlockExecutionContext(); + + blockExecutionContextStack.setContexts([ + blockExecutionContext, + lastContext, + ]); + + blockExecutionContextStack.removeLatest(); + + const result = blockExecutionContextStack.getContexts(); + + expect(result).to.deep.equals([ + blockExecutionContext, + ]); + }); + }); + + describe('add', () => { + it('should append a context to the stack and remove the last one', () => { + const firstContext = new BlockExecutionContext(); + const secondContext = new BlockExecutionContext(); + const thirdContext = new BlockExecutionContext(); + const forthContext = new BlockExecutionContext(); + + blockExecutionContextStack.add(firstContext); + + expect(blockExecutionContextStack.getContexts()).to.have.ordered.members([ + firstContext, + ]); + + blockExecutionContextStack.add(secondContext); + + expect(blockExecutionContextStack.getContexts()).to.have.ordered.members([ + secondContext, firstContext, + ]); + + blockExecutionContextStack.add(thirdContext); + + expect(blockExecutionContextStack.getContexts()).to.have.ordered.members([ + thirdContext, secondContext, firstContext, + ]); + + blockExecutionContextStack.add(forthContext); + + expect(blockExecutionContextStack.getContexts()).to.have.ordered.members([ + forthContext, thirdContext, secondContext, + ]); + }); + }); + + describe('getSize', () => { + it('should return the current size of the stack', () => { + blockExecutionContextStack.setContexts([ + blockExecutionContext, + blockExecutionContext, + ]); + + const result = blockExecutionContextStack.getSize(); + expect(result).to.equals(2); + }); + }); +}); diff --git a/packages/js-drive/test/unit/core/LatestCoreChainLock.spec.js b/packages/js-drive/test/unit/core/LatestCoreChainLock.spec.js new file mode 100644 index 00000000000..2ab24a6db1e --- /dev/null +++ b/packages/js-drive/test/unit/core/LatestCoreChainLock.spec.js @@ -0,0 +1,44 @@ +const LatestCoreChainLock = require('../../../lib/core/LatestCoreChainLock'); + +describe('LatestCoreChainLock', () => { + describe('#constructor', () => { + it('should instantiate', () => { + const latestCoreChainLock = new LatestCoreChainLock(); + expect(latestCoreChainLock.chainLock).to.equal(undefined); + const latestCoreChainLockWithValue = new LatestCoreChainLock('someValue'); + expect(latestCoreChainLockWithValue.chainLock).to.equal('someValue'); + }); + }); + + describe('#update', () => { + it('should update', () => { + const latestCoreChainLock = new LatestCoreChainLock(); + latestCoreChainLock.update('someValue'); + expect(latestCoreChainLock.chainLock).to.equal('someValue'); + }); + + it('should emit updated chainLock', (done) => { + const chainLock = 'someValue'; + const latestCoreChainLock = new LatestCoreChainLock(); + + latestCoreChainLock.on(LatestCoreChainLock.EVENTS.update, (data) => { + expect(data).to.equal(chainLock); + + done(); + }); + + latestCoreChainLock.update(chainLock); + }); + }); + + describe('#getChainLock', () => { + it('should return chainLock', async () => { + const chainLock = 'someValue'; + + const latestCoreChainLock = new LatestCoreChainLock(); + latestCoreChainLock.update(chainLock); + + expect(latestCoreChainLock.getChainLock()).to.equal(chainLock); + }); + }); +}); diff --git a/packages/js-drive/test/unit/core/decodeChainLock.spec.js b/packages/js-drive/test/unit/core/decodeChainLock.spec.js new file mode 100644 index 00000000000..676a28e641a --- /dev/null +++ b/packages/js-drive/test/unit/core/decodeChainLock.spec.js @@ -0,0 +1,19 @@ +const decodeChainLock = require('../../../lib/core/decodeChainLock'); + +describe('decodeChainLock', () => { + let chaiLockBuffer; + + beforeEach(() => { + chaiLockBuffer = Buffer.from('08ea07122036252dfdf79b1b8a95141d32a4c66353a88e439506f036867d7949a5ca7d8a371a60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 'hex'); + }); + + it('should decode chainLock', async () => { + const decodedChainLock = decodeChainLock(chaiLockBuffer); + + expect(decodedChainLock).to.deep.equal({ + height: 1002, + blockHash: Buffer.from('36252dfdf79b1b8a95141d32a4c66353a88e439506f036867d7949a5ca7d8a37', 'hex'), + signature: Buffer.from('000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 'hex'), + }); + }); +}); diff --git a/packages/js-drive/test/unit/core/ensureBlock.spec.js b/packages/js-drive/test/unit/core/ensureBlock.spec.js new file mode 100644 index 00000000000..a9b86156f94 --- /dev/null +++ b/packages/js-drive/test/unit/core/ensureBlock.spec.js @@ -0,0 +1,66 @@ +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); + +chai.use(chaiAsPromised); +chai.should(); + +const EventEmitter = require('events'); +const ZMQClient = require('../../../lib/core/ZmqClient'); +const ensureBlock = require('../../../lib/core/ensureBlock'); + +describe('ensureBlock', () => { + const hash = '00000'; + const otherHash = '00001'; + const socketClient = new EventEmitter(); + let rpcClient; + + beforeEach(function beforeEach() { + socketClient.subscribe = this.sinon.stub(); + + rpcClient = { + getBlock: this.sinon.stub().resolves(true), + }; + }); + + it('should ensure a block exist before returning promise', async () => { + await ensureBlock(socketClient, rpcClient, hash); + + expect(rpcClient.getBlock).to.be.calledOnceWithExactly(hash); + }); + + it('should wait for block if not found before returning promise', (done) => { + const err = new Error(); + err.code = -5; + err.message = 'Block not found'; + + rpcClient.getBlock.throws(err); + + ensureBlock(socketClient, rpcClient, hash).then(done); + + setImmediate(() => { + socketClient.emit(ZMQClient.TOPICS.hashblock, otherHash); + }); + + setImmediate(() => { + socketClient.emit(ZMQClient.TOPICS.hashblock, hash); + }); + + expect(rpcClient.getBlock).to.be.calledOnceWithExactly(hash); + }); + + it('should throw on unexpected error', async () => { + const err = new Error(); + err.code = -6; + err.message = 'Another error'; + + rpcClient.getBlock.throws(err); + + try { + await ensureBlock(socketClient, rpcClient, hash); + expect.fail('Internal error must be thrown'); + } catch (e) { + expect(e).to.equal(err); + expect(rpcClient.getBlock).to.be.calledOnceWithExactly(hash); + } + }); +}); diff --git a/packages/js-drive/test/unit/core/getRandomQuorum.spec.js b/packages/js-drive/test/unit/core/getRandomQuorum.spec.js new file mode 100644 index 00000000000..98426745b83 --- /dev/null +++ b/packages/js-drive/test/unit/core/getRandomQuorum.spec.js @@ -0,0 +1,57 @@ +const { QuorumEntry } = require('@dashevo/dashcore-lib'); +const { expect } = require('chai'); +const getRandomQuorum = require('../../../lib/core/getRandomQuorum'); + +describe('getRandomQuorum', () => { + let smlMock; + let quorumType; + let randomQuorum; + + beforeEach(function beforeEach() { + smlMock = { + getQuorumsOfType: this.sinon.stub(), + getQuorum: this.sinon.stub(), + quorumList: [], + blockHash: '0'.repeat(32), + }; + + quorumType = 1; + + smlMock.getQuorumsOfType.returns([ + { + quorumHash: Buffer.alloc(1, 32).toString('hex'), + }, + ]); + + randomQuorum = new QuorumEntry(); + + smlMock.getQuorum.returns(randomQuorum); + }); + + it('should return random quorum based on entropy', () => { + const result = getRandomQuorum(smlMock, quorumType, Buffer.alloc(1)); + + expect(smlMock.getQuorumsOfType).to.have.been.calledOnceWithExactly(quorumType); + expect(smlMock.getQuorum).to.have.been.calledOnceWithExactly( + quorumType, '20', + ); + expect(result).to.equals(randomQuorum); + }); + + it('should throw an error if SML does not contain any quorums', () => { + smlMock.getQuorumsOfType.returns([]); + + expect(() => { + getRandomQuorum(smlMock, quorumType, Buffer.alloc(1)); + }).to.throw(`SML at block ${'0'.repeat(32)} contains no quorums of any type`); + }); + + it('should throw an error if SML contains quorums that differ from the specified quorum type', () => { + smlMock.getQuorumsOfType.returns([]); + smlMock.quorumList = [{ llmqType: 999 }]; + + expect(() => { + getRandomQuorum(smlMock, quorumType, Buffer.alloc(1)); + }).to.throw(`SML at block ${'0'.repeat(32)} contains no quorums of type 1, but contains entries for types 999. Please check the Drive configuration`); + }); +}); diff --git a/packages/js-drive/test/unit/core/updateSimplifiedMasternodeListFactory.spec.js b/packages/js-drive/test/unit/core/updateSimplifiedMasternodeListFactory.spec.js new file mode 100644 index 00000000000..3d9daddb9eb --- /dev/null +++ b/packages/js-drive/test/unit/core/updateSimplifiedMasternodeListFactory.spec.js @@ -0,0 +1,190 @@ +const SimplifiedMNListDiff = require('@dashevo/dashcore-lib/lib/deterministicmnlist/SimplifiedMNListDiff'); +const { expect } = require('chai'); +const updateSimplifiedMasternodeListFactory = require('../../../lib/core/updateSimplifiedMasternodeListFactory'); +const NotEnoughBlocksForValidSMLError = require('../../../lib/core/errors/NotEnoughBlocksForValidSMLError'); +const LoggerMock = require('../../../lib/test/mock/LoggerMock'); + +describe('updateSimplifiedMasternodeListFactory', () => { + let updateSimplifiedMasternodeList; + let coreRpcClientMock; + let network; + let smlMaxListsLimit; + let simplifiedMasternodeListMock; + let rawDiff; + let coreHeight; + + beforeEach(function beforeEach() { + network = 'regtest'; + + rawDiff = { + baseBlockHash: '644bd9dcbc0537026af6d31181570f934d868f121c55513009bb36f509ec816e', + blockHash: '23beac1b700c4a49855a9653e036219384ac2fab7eeba2ec45b3e2d0063d1285', + cbTxMerkleTree: '03000000032f7f142e19bee0c595dac9f900695d1e428a4db70a805fda6c834cfec0de506a0d39baea39dbbaf9827a1f3b8f381a65ebcf4c2ef415025bc4d20afd372e680d12c226f084a6e28e421fbedff22b13aa1191d6a80744d104fa75ede12332467d0107', + cbTx: '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502e9030101ffffffff01a2567a76070000001976a914f713c2fa5ef0e7c48f0d1b3ad2a79150037c72d788ac00000000460200e90300003fdbe53b9a4cd0b62284195cbd4f4c1655ebdd70e9117ed3c0e49c37bfce46060000000000000000000000000000000000000000000000000000000000000000', + deletedMNs: [], + mnList: [ + { + proRegTxHash: 'e57402007ca10454d77437d9c1156b1c4ff8af86d699c08e9a31dbd1dfe3c991', + confirmedHash: '0000000000000000000000000000000000000000000000000000000000000000', + service: '127.0.0.1:20001', + pubKeyOperator: '906d84cb88f532145d8838414f777b971c976ffcf8ccfc57413a13cf2f8a7750a92f9b997a5a741f1afa34d989f4312b', + votingAddress: 'ydC3Qkhq6qc1qgHD8PVSHyAB6t3NYa7aw4', + isValid: true, + }, + ], + deletedQuorums: [], + newQuorums: [], + merkleRootMNList: '0646cebf379ce4c0d37e11e970ddeb55164c4fbd5c198422b6d04c9a3be5db3f', + merkleRootQuorums: '0000000000000000000000000000000000000000000000000000000000000000', + }; + + coreHeight = 84202; + + coreRpcClientMock = { + protx: this.sinon.stub(), + }; + + coreRpcClientMock.protx.resolves({ + result: rawDiff, + }); + + simplifiedMasternodeListMock = { + applyDiffs: this.sinon.stub(), + reset: this.sinon.stub(), + }; + + smlMaxListsLimit = 2; + + const loggerMock = new LoggerMock(this.sinon); + + updateSimplifiedMasternodeList = updateSimplifiedMasternodeListFactory( + coreRpcClientMock, + simplifiedMasternodeListMock, + smlMaxListsLimit, + network, + loggerMock, + ); + }); + + it('should throw error if not enough blocks for valid SML', async () => { + try { + await updateSimplifiedMasternodeList(smlMaxListsLimit); + + expect.fail('should throw NotEnoughBlocksForValidSMLError'); + } catch (e) { + expect(e).to.be.instanceOf(NotEnoughBlocksForValidSMLError); + expect(e.getBlockHeight()).to.be.equal(smlMaxListsLimit); + } + }); + + it('should obtain 16 latest diffs according to core height on first call', async () => { + const isUpdated = await updateSimplifiedMasternodeList(coreHeight); + + expect(isUpdated).to.be.true(); + + const proTxCallCount = coreHeight - (coreHeight - smlMaxListsLimit) + 1; + + expect(coreRpcClientMock.protx.callCount).to.equal(proTxCallCount); + + expect(coreRpcClientMock.protx.getCall(0).args).to.have.deep.members( + [ + 'diff', + 1, + (coreHeight - smlMaxListsLimit), + ], + ); + + for (let i = 1; i < proTxCallCount; i++) { + expect(coreRpcClientMock.protx.getCall(i).args).to.have.deep.members( + [ + 'diff', + (coreHeight - smlMaxListsLimit) + (i - 1), + (coreHeight - smlMaxListsLimit) + (i - 1) + 1, + ], + ); + } + + const smlDiffs = []; + for (let i = 0; i < proTxCallCount; i++) { + smlDiffs.push(new SimplifiedMNListDiff(rawDiff, network)); + } + + const argsDiffBuffers = simplifiedMasternodeListMock.applyDiffs.getCall(0).args[0].map( + (item) => item.toBuffer(), + ); + + const smlDiffBuffers = smlDiffs.map((item) => item.toBuffer()); + + expect(argsDiffBuffers).to.deep.equal(smlDiffBuffers); + }); + + it('should update diffs since last call and up to passed core height', async () => { + let isUpdated = await updateSimplifiedMasternodeList(coreHeight); + + expect(isUpdated).to.be.true(); + + isUpdated = await updateSimplifiedMasternodeList(coreHeight + 1); + + expect(isUpdated).to.be.true(); + + const proTxCallCount = smlMaxListsLimit + 2; + + expect(coreRpcClientMock.protx.callCount).to.equal(proTxCallCount); + + expect(coreRpcClientMock.protx.getCall(0).args).to.have.deep.members( + [ + 'diff', + 1, + (coreHeight - smlMaxListsLimit), + ], + ); + + for (let i = 1; i < proTxCallCount; i++) { + expect(coreRpcClientMock.protx.getCall(i).args).to.have.deep.members( + [ + 'diff', + (coreHeight - smlMaxListsLimit) + (i - 1), + (coreHeight - smlMaxListsLimit) + (i - 1) + 1, + ], + ); + } + + const simplifiedMNListDiffArray = []; + + for (let i = 0; i < proTxCallCount - 1; i++) { + simplifiedMNListDiffArray.push(new SimplifiedMNListDiff(rawDiff, network)); + } + + const argsDiffsBuffers = simplifiedMasternodeListMock.applyDiffs.getCall(0).args[0].map( + (item) => item.toBuffer(), + ); + + const smlDiffBuffers = simplifiedMNListDiffArray.map((item) => item.toBuffer()); + + expect(argsDiffsBuffers).to.deep.equal(smlDiffBuffers); + }); + + it('should not update more than 16 diffs', async () => { + let isUpdated = await updateSimplifiedMasternodeList(coreHeight); // 3 + + expect(isUpdated).to.be.true(); + + isUpdated = await updateSimplifiedMasternodeList(coreHeight + 10); // 3 + + expect(isUpdated).to.be.true(); + + const proTxCallCount = 3 + 3; + + expect(coreRpcClientMock.protx.callCount).to.equal(proTxCallCount); + }); + + it('should return false if SML was not updated', async () => { + let isUpdated = await updateSimplifiedMasternodeList(coreHeight); + + expect(isUpdated).to.be.true(); + + isUpdated = await updateSimplifiedMasternodeList(coreHeight); + + expect(isUpdated).to.be.false(); + }); +}); diff --git a/packages/js-drive/test/unit/core/waitForChainLockedHeightFactory.spec.js b/packages/js-drive/test/unit/core/waitForChainLockedHeightFactory.spec.js new file mode 100644 index 00000000000..6226680bf53 --- /dev/null +++ b/packages/js-drive/test/unit/core/waitForChainLockedHeightFactory.spec.js @@ -0,0 +1,63 @@ +const EventEmitter = require('events'); +const waitForChainLockedHeightFactory = require('../../../lib/core/waitForChainLockedHeightFactory'); +const MissingChainlockError = require('../../../lib/core/errors/MissingChainLockError'); +const LatestCoreChainLock = require('../../../lib/core/LatestCoreChainLock'); + +describe('waitForChainLockedHeightFactory', () => { + let waitForChainLockedHeight; + let latestCoreChainLockMock; + let chainLock; + let coreHeight; + + beforeEach(function beforeEach() { + coreHeight = 84202; + + chainLock = { + height: coreHeight, + signature: '0a43f1c3e5b3e8dbd670bca8d437dc25572f72d8e1e9be673e9ebbb606570307c3e5f5d073f7beb209dd7e0b8f96c751060ab3a7fb69a71d5ccab697b8cfa5a91038a6fecf76b7a827d75d17f01496302942aa5e2c7f4a48246efc8d3941bf6c', + }; + + latestCoreChainLockMock = new EventEmitter(); + latestCoreChainLockMock.getChainLock = this.sinon.stub().returns(chainLock); + + waitForChainLockedHeight = waitForChainLockedHeightFactory( + latestCoreChainLockMock, + ); + }); + + it('should throw MissingChainlockError if chainlock is empty', async () => { + latestCoreChainLockMock.getChainLock.returns(null); + + try { + await waitForChainLockedHeight(coreHeight); + + expect.fail(); + } catch (e) { + expect(e).to.be.an.instanceOf(MissingChainlockError); + } + }); + + it('should resolve promise if existing chainlock on the same height or higher', async () => { + latestCoreChainLockMock.getChainLock.returns(chainLock); + + await waitForChainLockedHeight(coreHeight); + }); + + it('should resolve when chainLock height to be equal or higher', (done) => { + coreHeight = chainLock.height + 1; + + waitForChainLockedHeight(coreHeight) + .then(() => { + expect(latestCoreChainLockMock.getChainLock).to.have.been.calledOnce(); + + done(); + }); + + setImmediate(() => { + latestCoreChainLockMock.emit(LatestCoreChainLock.EVENTS.update, { + ...chainLock, + height: chainLock.height + 1, + }); + }); + }); +}); diff --git a/packages/js-drive/test/unit/core/waitForCoreChainLockSyncFactory.spec.js b/packages/js-drive/test/unit/core/waitForCoreChainLockSyncFactory.spec.js new file mode 100644 index 00000000000..d88d7801873 --- /dev/null +++ b/packages/js-drive/test/unit/core/waitForCoreChainLockSyncFactory.spec.js @@ -0,0 +1,86 @@ +const EventEmitter = require('events'); +const LatestCoreChainLock = require('../../../lib/core/LatestCoreChainLock'); +const ZMQClient = require('../../../lib/core/ZmqClient'); +const waitForCoreChainLockSyncFactory = require('../../../lib/core/waitForCoreChainLockSyncFactory'); +const LoggerMock = require('../../../lib/test/mock/LoggerMock'); + +describe('waitForCoreChainLockSyncFactory', () => { + let waitForCoreChainLockHandler; + let coreRpcClientMock; + let coreZMQClientMock; + let latestCoreChainLock; + let chainLock; + let rawChainLockSigMessage; + + beforeEach(function beforeEach() { + chainLock = { + blockHash: '0000003df90e1cec3fea6bd17508f653cea093c536199e9d50a05bd69ee23b5d', + height: 3887, + signature: '1770e35c281ebfcf14b8a62071f76146eb0a5ede6fb43543a9c0ccddf3cf87fcdd0a96eea867595bb980dcea13e6283f16744631df895404434c7840f9b3d9c1069790a0459a0d35b7ae353519f5d437ded547f8d65f6c4916e988c842488e7a', + }; + + rawChainLockSigMessage = Buffer.from('00000020fd0ab0fc0fb0cbecb62cf7555aee6a8ce18564a9bbed8b22585d9f8563000000ee131c25019aaee0f1bdde2a5d6eb99ec0b4497e68776f18916951e8ddb6b922dd3be45f62f6011ed18800000103000500010000000000000000000000000000000000000000000000000000000000000000ffffffff05024c0f010bffffffff0200c817a8040000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88ac00ac23fc060000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88ac000000004602004c0f00003d8e273bf286d48ccba5a87b5adf332ed070a15e4e2d81eeb9ff685373be5656961e0b73ea855fdac9cc530782a7f0a22d25d1eaab4b2068efa647e9da0915d02f0f00005d3be29ed65ba0509d9e1936c593a0ce53f60875d16bea3fec1c0ef93d0000001770e35c281ebfcf14b8a62071f76146eb0a5ede6fb43543a9c0ccddf3cf87fcdd0a96eea867595bb980dcea13e6283f16744631df895404434c7840f9b3d9c1069790a0459a0d35b7ae353519f5d437ded547f8d65f6c4916e988c842488e7a', 'hex'); + + latestCoreChainLock = new LatestCoreChainLock(); + coreRpcClientMock = { + getBestChainLock: this.sinon.stub().resolves({ + result: chainLock, + error: null, + id: 5, + }), + getBlock: this.sinon.stub(), + }; + coreZMQClientMock = new EventEmitter(); + coreZMQClientMock.subscribe = this.sinon.stub(); + + const loggerMock = new LoggerMock(this.sinon); + + waitForCoreChainLockHandler = waitForCoreChainLockSyncFactory( + coreZMQClientMock, + coreRpcClientMock, + latestCoreChainLock, + loggerMock, + ); + }); + + it('should wait for chainlock to be synced', async () => { + expect(latestCoreChainLock.chainLock).to.equal(undefined); + + await waitForCoreChainLockHandler(); + + expect(latestCoreChainLock.chainLock.toJSON()).to.deep.equal(chainLock); + + expect(coreZMQClientMock.subscribe).to.be.calledTwice(); + expect(coreZMQClientMock.subscribe).to.be.calledWith(ZMQClient.TOPICS.rawchainlocksig); + expect(coreZMQClientMock.subscribe).to.be.calledWith(ZMQClient.TOPICS.hashblock); + expect(coreRpcClientMock.getBestChainLock).to.be.calledOnce(); + }); + + it('should handle when no chainlock is found via RPC', (done) => { + expect(latestCoreChainLock.chainLock).to.equal(undefined); + + const err = new Error(); + err.code = -32603; + err.message = 'Chainlock not found'; + + coreRpcClientMock.getBestChainLock.throws(err); + + waitForCoreChainLockHandler() + .then(() => { + expect(latestCoreChainLock.chainLock.toJSON()).to.deep.equal(chainLock); + + expect(coreZMQClientMock.subscribe).to.be.calledTwice(); + expect(coreZMQClientMock.subscribe).to.be.calledWith(ZMQClient.TOPICS.rawchainlocksig); + expect(coreZMQClientMock.subscribe).to.be.calledWith(ZMQClient.TOPICS.hashblock); + expect(coreRpcClientMock.getBestChainLock).to.be.calledOnce(); + done(); + }); + + setImmediate(() => { + coreZMQClientMock.emit( + ZMQClient.TOPICS.rawchainlocksig, + rawChainLockSigMessage, + ); + }); + }); +}); diff --git a/packages/js-drive/test/unit/dpp/CachedStateRepositoryDecorator.spec.js b/packages/js-drive/test/unit/dpp/CachedStateRepositoryDecorator.spec.js new file mode 100644 index 00000000000..7b2ba47d46e --- /dev/null +++ b/packages/js-drive/test/unit/dpp/CachedStateRepositoryDecorator.spec.js @@ -0,0 +1,215 @@ +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); + +const CachedStateRepositoryDecorator = require('../../../lib/dpp/CachedStateRepositoryDecorator'); +const DataContractCacheItem = require('../../../lib/dataContract/DataContractCacheItem'); + +describe('CachedStateRepositoryDecorator', () => { + let stateRepositoryMock; + let cachedStateRepository; + let dataContractCacheMock; + let id; + let identity; + let documents; + let dataContract; + + beforeEach(function beforeEach() { + id = 'id'; + identity = getIdentityFixture(); + documents = getDocumentsFixture(); + dataContract = getDataContractFixture(); + + dataContractCacheMock = { + set: this.sinon.stub(), + get: this.sinon.stub(), + }; + + stateRepositoryMock = createStateRepositoryMock(this.sinon); + + cachedStateRepository = new CachedStateRepositoryDecorator( + stateRepositoryMock, + dataContractCacheMock, + ); + }); + + describe('#fetchIdentity', () => { + it('should fetch identity from state repository', async () => { + stateRepositoryMock.fetchIdentity.resolves(identity); + + const result = await cachedStateRepository.fetchIdentity(id); + + expect(result).to.deep.equal(identity); + expect(stateRepositoryMock.fetchIdentity).to.be.calledOnceWith(id); + }); + }); + + describe('#createIdentity', () => { + it('should store identity to repository', async () => { + await cachedStateRepository.createIdentity(identity); + + expect(stateRepositoryMock.createIdentity).to.be.calledOnceWith(identity); + }); + }); + + describe('#updateIdentity', () => { + it('should store identity to repository', async () => { + await cachedStateRepository.updateIdentity(identity); + + expect(stateRepositoryMock.updateIdentity).to.be.calledOnceWith(identity); + }); + }); + + describe('#storeIdentityPublicKeyHashes', () => { + it('should store identity id and public key hashes to repository', async () => { + const publicKeyHashes = identity.getPublicKeys().map((pk) => pk.hash()); + + await cachedStateRepository.storeIdentityPublicKeyHashes( + identity.getId(), publicKeyHashes, + ); + + expect(stateRepositoryMock.storeIdentityPublicKeyHashes).to.be.calledOnceWithExactly( + identity.getId(), publicKeyHashes, undefined, + ); + }); + }); + + describe('#fetchIdentityIdsByPublicKeyHashes', () => { + it('should fetch identity id and public key hash pairs map from repository', async () => { + const publicKeys = identity.getPublicKeys(); + + stateRepositoryMock.fetchIdentityIdsByPublicKeyHashes.resolves({ + [publicKeys[0].hash()]: identity.getId(), + [publicKeys[1].hash()]: identity.getId(), + }); + + const result = await cachedStateRepository.fetchIdentityIdsByPublicKeyHashes( + publicKeys.map((pk) => pk.hash()), + ); + + expect(stateRepositoryMock.fetchIdentityIdsByPublicKeyHashes).to.be.calledOnceWithExactly( + publicKeys.map((pk) => pk.hash()), + undefined, + ); + expect(result).to.deep.equal({ + [publicKeys[0].hash()]: identity.getId(), + [publicKeys[1].hash()]: identity.getId(), + }); + }); + }); + + describe('#fetchDocuments', () => { + it('should fetch documents from state repository', async () => { + const contractId = 'contractId'; + const type = 'documentType'; + const options = {}; + + stateRepositoryMock.fetchDocuments.resolves(documents); + + const result = await cachedStateRepository.fetchDocuments(contractId, type, options); + + expect(result).to.equal(documents); + expect(stateRepositoryMock.fetchDocuments).to.be.calledOnceWith(contractId, type, options); + }); + }); + + describe('#createDocument', () => { + it('should create document in repository', async () => { + const [document] = documents; + + await cachedStateRepository.createDocument(document); + + expect(stateRepositoryMock.createDocument).to.be.calledOnceWith(document); + }); + }); + + describe('#updateDocument', () => { + it('should update document in repository', async () => { + const [document] = documents; + + await cachedStateRepository.updateDocument(document); + + expect(stateRepositoryMock.updateDocument).to.be.calledOnceWith(document); + }); + }); + + describe('#removeDocument', () => { + it('should delete document from repository', async () => { + const type = 'documentType'; + + await cachedStateRepository.removeDocument(dataContract, type, id); + + expect(stateRepositoryMock.removeDocument).to.be.calledOnceWith(dataContract, type, id); + }); + }); + + describe('fetchTransaction', () => { + it('should fetch transaction from state repository', async () => { + stateRepositoryMock.fetchTransaction.resolves(dataContract); + + const result = await cachedStateRepository.fetchTransaction(id); + + expect(result).to.equal(dataContract); + expect(stateRepositoryMock.fetchTransaction).to.be.calledOnceWith(id); + }); + }); + + describe('#fetchDataContract', () => { + it('should fetch data contract from cache', async () => { + const cacheItem = new DataContractCacheItem(dataContract, []); + + dataContractCacheMock.get.returns(cacheItem); + + const result = await cachedStateRepository.fetchDataContract(id); + + expect(result).to.equal(dataContract); + expect(stateRepositoryMock.fetchDataContract).to.be.not.called(); + expect(dataContractCacheMock.get).to.be.calledOnceWith(id); + }); + + it('should fetch data contract from state repository if it is not present in cache', async () => { + dataContractCacheMock.get.returns(undefined); + stateRepositoryMock.fetchDataContract.resolves(dataContract); + + const result = await cachedStateRepository.fetchDataContract(id); + + const cacheItem = new DataContractCacheItem(dataContract, []); + + expect(result).to.equal(dataContract); + expect(dataContractCacheMock.get).to.be.calledOnceWith(id); + expect(dataContractCacheMock.set).to.be.calledOnceWith(id, cacheItem); + expect(stateRepositoryMock.fetchDataContract).to.be.calledOnceWith(id); + }); + + it('should not store null in cache if data contract is not present in state repository', async () => { + stateRepositoryMock.fetchDataContract.resolves(null); + + const result = await cachedStateRepository.fetchDataContract(id); + + expect(result).to.be.null(); + + expect(dataContractCacheMock.get).to.be.calledOnceWith(id); + expect(dataContractCacheMock.set).to.not.be.called(); + expect(stateRepositoryMock.fetchDataContract).to.be.calledOnceWith(id); + }); + }); + + describe('#fetchLatestPlatformBlockHeader', () => { + it('should fetch latest platform block header from state repository', async () => { + const header = { + height: 10, + time: { + seconds: Math.ceil(new Date().getTime() / 1000), + }, + }; + + stateRepositoryMock.fetchLatestPlatformBlockHeader.resolves(header); + + const result = await cachedStateRepository.fetchLatestPlatformBlockHeader(id); + + expect(result).to.deep.equal(header); + expect(stateRepositoryMock.fetchLatestPlatformBlockHeader).to.be.calledOnce(); + }); + }); +}); diff --git a/packages/js-drive/test/unit/dpp/DriveStateRepository.spec.js b/packages/js-drive/test/unit/dpp/DriveStateRepository.spec.js new file mode 100644 index 00000000000..a0bba412f30 --- /dev/null +++ b/packages/js-drive/test/unit/dpp/DriveStateRepository.spec.js @@ -0,0 +1,591 @@ +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); + +const ReadOperation = require('@dashevo/dpp/lib/stateTransition/fee/operations/ReadOperation'); +const StateTransitionExecutionContext = require('@dashevo/dpp/lib/stateTransition/StateTransitionExecutionContext'); + +const DriveStateRepository = require('../../../lib/dpp/DriveStateRepository'); +const StorageResult = require('../../../lib/storage/StorageResult'); + +describe('DriveStateRepository', () => { + let stateRepository; + let identityRepositoryMock; + let publicKeyIdentityIdRepositoryMock; + let dataContractRepositoryMock; + let fetchDocumentsMock; + let documentsRepositoryMock; + let spentAssetLockTransactionsRepositoryMock; + let coreRpcClientMock; + let id; + let identity; + let documents; + let dataContract; + let blockExecutionContextMock; + let simplifiedMasternodeListMock; + let instantLockMock; + let repositoryOptions; + let executionContext; + let operations; + + beforeEach(function beforeEach() { + identity = getIdentityFixture(); + documents = getDocumentsFixture(); + dataContract = getDataContractFixture(); + id = generateRandomIdentifier(); + + coreRpcClientMock = { + getRawTransaction: this.sinon.stub(), + verifyIsLock: this.sinon.stub(), + }; + + dataContractRepositoryMock = { + fetch: this.sinon.stub(), + store: this.sinon.stub(), + }; + + identityRepositoryMock = { + fetch: this.sinon.stub(), + create: this.sinon.stub(), + update: this.sinon.stub(), + }; + + publicKeyIdentityIdRepositoryMock = { + fetch: this.sinon.stub(), + store: this.sinon.stub(), + }; + + fetchDocumentsMock = this.sinon.stub(); + + documentsRepositoryMock = { + create: this.sinon.stub(), + update: this.sinon.stub(), + find: this.sinon.stub(), + delete: this.sinon.stub(), + }; + + spentAssetLockTransactionsRepositoryMock = { + store: this.sinon.stub(), + find: this.sinon.stub(), + delete: this.sinon.stub(), + }; + + blockExecutionContextMock = { + getHeader: this.sinon.stub(), + }; + + simplifiedMasternodeListMock = { + getStore: this.sinon.stub(), + }; + + repositoryOptions = { useTransaction: true }; + + stateRepository = new DriveStateRepository( + identityRepositoryMock, + publicKeyIdentityIdRepositoryMock, + dataContractRepositoryMock, + fetchDocumentsMock, + documentsRepositoryMock, + spentAssetLockTransactionsRepositoryMock, + coreRpcClientMock, + blockExecutionContextMock, + simplifiedMasternodeListMock, + repositoryOptions, + ); + + instantLockMock = { + getRequestId: () => 'someRequestId', + txid: 'someTxId', + signature: 'signature', + verify: this.sinon.stub(), + }; + + executionContext = new StateTransitionExecutionContext(); + operations = [new ReadOperation(1)]; + }); + + describe('#fetchIdentity', () => { + it('should fetch identity from repository', async () => { + identityRepositoryMock.fetch.resolves( + new StorageResult(identity, operations), + ); + + const result = await stateRepository.fetchIdentity(id, executionContext); + + expect(result).to.equal(identity); + expect(identityRepositoryMock.fetch).to.be.calledOnceWith( + id, + { + useTransaction: repositoryOptions.useTransaction, + dryRun: false, + }, + ); + + expect(executionContext.getOperations()).to.deep.equals(operations); + }); + }); + + describe('#createIdentity', () => { + it('should create identity', async () => { + identityRepositoryMock.create.resolves( + new StorageResult(undefined, operations), + ); + + await stateRepository.createIdentity(identity, executionContext); + + expect(identityRepositoryMock.create).to.be.calledOnceWith( + identity, + { + useTransaction: repositoryOptions.useTransaction, + dryRun: false, + }, + ); + + expect(executionContext.getOperations()).to.deep.equals(operations); + }); + }); + + describe('#updateIdentity', () => { + it('should update identity', async () => { + identityRepositoryMock.update.resolves( + new StorageResult(undefined, operations), + ); + + await stateRepository.updateIdentity(identity, executionContext); + + expect(identityRepositoryMock.update).to.be.calledOnceWith( + identity, + { + useTransaction: repositoryOptions.useTransaction, + dryRun: false, + }, + ); + + expect(executionContext.getOperations()).to.deep.equals(operations); + }); + }); + + describe('#storeIdentityPublicKeyHashes', () => { + it('should store public key hashes for an identity id to repository', async () => { + publicKeyIdentityIdRepositoryMock.store.resolves( + new StorageResult(undefined, operations), + ); + + await stateRepository.storeIdentityPublicKeyHashes( + identity.getId(), + [ + identity.getPublicKeyById(0).hash(), + identity.getPublicKeyById(1).hash(), + ], + executionContext, + ); + + expect(publicKeyIdentityIdRepositoryMock.store).to.have.been.calledTwice(); + expect(publicKeyIdentityIdRepositoryMock.store.getCall(0).args).to.have.deep.members([ + identity.getPublicKeyById(0).hash(), + identity.getId(), + { + useTransaction: repositoryOptions.useTransaction, + dryRun: false, + }, + ]); + expect(publicKeyIdentityIdRepositoryMock.store.getCall(1).args).to.have.deep.members([ + identity.getPublicKeyById(1).hash(), + identity.getId(), + { + useTransaction: repositoryOptions.useTransaction, + dryRun: false, + }, + ]); + + expect(executionContext.getOperations()).to.deep.equals(operations.concat(operations)); + }); + }); + + describe('#fetchIdentityIdsByPublicKeyHashes', () => { + it('should fetch map of previously stored public key hash and identity id pairs', async () => { + const publicKeyHashes = [ + identity.getPublicKeyById(0).hash(), + identity.getPublicKeyById(1).hash(), + ]; + + publicKeyIdentityIdRepositoryMock + .fetch + .withArgs(publicKeyHashes[0]) + .resolves(new StorageResult(identity.getId(), operations)); + + publicKeyIdentityIdRepositoryMock + .fetch + .withArgs(publicKeyHashes[1]) + .resolves(new StorageResult(identity.getId(), operations)); + + const result = await stateRepository.fetchIdentityIdsByPublicKeyHashes( + publicKeyHashes, + executionContext, + ); + + expect(result).to.have.deep.members([ + identity.getId(), + identity.getId(), + ]); + + expect(executionContext.getOperations()).to.deep.equals(operations.concat(operations)); + }); + + it('should have null as value if pair was not found', async () => { + const publicKeyHashes = [ + identity.getPublicKeyById(0).hash(), + identity.getPublicKeyById(1).hash(), + ]; + + publicKeyIdentityIdRepositoryMock + .fetch + .withArgs(publicKeyHashes[0]) + .resolves(new StorageResult(identity.getId(), operations)); + + publicKeyIdentityIdRepositoryMock + .fetch + .withArgs(publicKeyHashes[1]) + .resolves(new StorageResult(null, operations)); + + const result = await stateRepository.fetchIdentityIdsByPublicKeyHashes( + publicKeyHashes, + executionContext, + ); + + expect(result).to.have.deep.members([ + identity.getId(), + null, + ]); + + expect(executionContext.getOperations()).to.deep.equals(operations.concat(operations)); + }); + }); + + describe('#fetchDataContract', () => { + it('should fetch data contract from repository', async () => { + dataContractRepositoryMock.fetch.resolves( + new StorageResult(dataContract, operations), + ); + + const result = await stateRepository.fetchDataContract(id, executionContext); + + expect(result).to.equal(dataContract); + expect(dataContractRepositoryMock.fetch).to.be.calledOnceWithExactly( + id, + { + dryRun: false, + useTransaction: false, + }, + ); + + expect(executionContext.getOperations()).to.deep.equals(operations); + }); + }); + + describe('#storeDataContract', () => { + it('should store data contract to repository', async () => { + dataContractRepositoryMock.store.resolves( + new StorageResult(undefined, operations), + ); + + await stateRepository.storeDataContract(dataContract, executionContext); + + expect(dataContractRepositoryMock.store).to.be.calledOnceWith( + dataContract, + { + useTransaction: repositoryOptions.useTransaction, + dryRun: false, + }, + ); + + expect(executionContext.getOperations()).to.deep.equals(operations); + }); + }); + + describe('#fetchDocuments', () => { + it('should fetch documents from repository', async () => { + const type = 'documentType'; + const options = {}; + + fetchDocumentsMock.resolves( + new StorageResult(documents, operations), + ); + + const result = await stateRepository.fetchDocuments( + id, + type, + options, + executionContext, + ); + + expect(result).to.equal(documents); + expect(fetchDocumentsMock).to.be.calledOnceWith( + id, + type, + { + ...options, + useTransaction: repositoryOptions.useTransaction, + dryRun: false, + }, + ); + + expect(executionContext.getOperations()).to.deep.equals(operations); + }); + }); + + describe('#createDocument', () => { + it('should create document in repository', async () => { + documentsRepositoryMock.create.resolves( + new StorageResult(undefined, operations), + ); + + const [document] = documents; + + await stateRepository.createDocument(document, executionContext); + + expect(documentsRepositoryMock.create).to.be.calledOnceWith( + document, + { + useTransaction: repositoryOptions.useTransaction, + dryRun: false, + }, + ); + + expect(executionContext.getOperations()).to.deep.equals(operations); + }); + }); + + describe('#updateDocument', () => { + it('should store document in repository', async () => { + documentsRepositoryMock.update.resolves( + new StorageResult(undefined, operations), + ); + + const [document] = documents; + + await stateRepository.updateDocument(document, executionContext); + + expect(documentsRepositoryMock.update).to.be.calledOnceWith( + document, + { + useTransaction: repositoryOptions.useTransaction, + dryRun: false, + }, + ); + + expect(executionContext.getOperations()).to.deep.equals(operations); + }); + }); + + describe('#removeDocument', () => { + it('should delete document from repository', async () => { + documentsRepositoryMock.delete.resolves( + new StorageResult(undefined, operations), + ); + + const type = 'documentType'; + + await stateRepository.removeDocument(dataContract, type, id, executionContext); + + expect(documentsRepositoryMock.delete).to.be.calledOnceWith( + dataContract, + type, + id, + { + useTransaction: repositoryOptions.useTransaction, + dryRun: false, + }, + ); + + expect(executionContext.getOperations()).to.deep.equals(operations); + }); + }); + + describe('#fetchTransaction', () => { + it('should fetch transaction from core', async () => { + const rawTransaction = { + hex: 'some result', + height: 1, + }; + + coreRpcClientMock.getRawTransaction.resolves({ result: rawTransaction }); + + const result = await stateRepository.fetchTransaction(id, executionContext); + + expect(result).to.deep.equal({ + data: Buffer.from(rawTransaction.hex, 'hex'), + height: rawTransaction.height, + }); + + expect(coreRpcClientMock.getRawTransaction).to.be.calledOnceWithExactly(id, 1); + + const operation = new ReadOperation(Buffer.from(rawTransaction.hex, 'hex').length); + + expect(executionContext.getOperations()).to.deep.equals([operation]); + }); + + it('should return null if core throws Invalid address or key error', async () => { + const error = new Error('Some error'); + error.code = -5; + + coreRpcClientMock.getRawTransaction.throws(error); + + const result = await stateRepository.fetchTransaction(id); + + expect(result).to.equal(null); + expect(coreRpcClientMock.getRawTransaction).to.be.calledOnceWith(id); + }); + + it('should throw an error if core throws an unknown error', async () => { + const error = new Error('Some error'); + + coreRpcClientMock.getRawTransaction.throws(error); + + try { + await stateRepository.fetchTransaction(id); + + expect.fail('should throw error'); + } catch (e) { + expect(e).to.equal(error); + expect(coreRpcClientMock.getRawTransaction).to.be.calledOnceWith(id); + } + }); + + it('should return mocked transaction on dry run', async () => { + executionContext.enableDryRun(); + + const result = await stateRepository.fetchTransaction(id, executionContext); + + executionContext.disableDryRun(); + + expect(result).to.deep.equal({ + data: Buffer.alloc(0), + height: 1, + }); + + expect(coreRpcClientMock.getRawTransaction).to.not.be.called(id); + }); + }); + + describe('#fetchLatestPlatformBlockHeader', () => { + it('should fetch latest platform block header', async () => { + const header = { + height: 10, + time: { + seconds: Math.ceil(new Date().getTime() / 1000), + }, + }; + + blockExecutionContextMock.getHeader.resolves(header); + + const result = await stateRepository.fetchLatestPlatformBlockHeader(); + + expect(result).to.deep.equal(header); + expect(blockExecutionContextMock.getHeader).to.be.calledOnce(); + }); + }); + + describe('#verifyInstantLock', () => { + let smlStore; + + beforeEach(() => { + blockExecutionContextMock.getHeader.returns({ + header: 41, + coreChainLockedHeight: 42, + }); + + smlStore = {}; + + simplifiedMasternodeListMock.getStore.returns(smlStore); + }); + + it('it should verify instant lock using Core', async () => { + coreRpcClientMock.verifyIsLock.resolves({ result: true }); + + const result = await stateRepository.verifyInstantLock(instantLockMock); + + expect(result).to.equal(true); + expect(coreRpcClientMock.verifyIsLock).to.have.been.calledOnceWithExactly( + 'someRequestId', + 'someTxId', + 'signature', + 42, + ); + expect(instantLockMock.verify).to.have.not.been.called(); + }); + + it('should return false if core throws Invalid address or key error', async () => { + const error = new Error('Some error'); + error.code = -5; + + coreRpcClientMock.verifyIsLock.throws(error); + + const result = await stateRepository.verifyInstantLock(instantLockMock); + + expect(result).to.equal(false); + expect(coreRpcClientMock.verifyIsLock).to.have.been.calledOnceWithExactly( + 'someRequestId', + 'someTxId', + 'signature', + 42, + ); + expect(instantLockMock.verify).to.have.not.been.called(); + }); + + it('should return false if core throws Invalid parameter', async () => { + const error = new Error('Some error'); + error.code = -8; + + coreRpcClientMock.verifyIsLock.throws(error); + + const result = await stateRepository.verifyInstantLock(instantLockMock); + + expect(result).to.equal(false); + expect(coreRpcClientMock.verifyIsLock).to.have.been.calledOnceWithExactly( + 'someRequestId', + 'someTxId', + 'signature', + 42, + ); + expect(instantLockMock.verify).to.have.not.been.called(); + }); + + it('should return false if header is null', async () => { + blockExecutionContextMock.getHeader.returns(null); + + const result = await stateRepository.verifyInstantLock(instantLockMock); + + expect(result).to.be.false(); + }); + + it('should return true on dry run', async () => { + const error = new Error('Some error'); + error.code = -5; + + coreRpcClientMock.verifyIsLock.throws(error); + + executionContext.enableDryRun(); + + const result = await stateRepository.verifyInstantLock(instantLockMock, executionContext); + + executionContext.disableDryRun(); + + expect(result).to.be.true(); + expect(instantLockMock.verify).to.have.not.been.called(); + expect(coreRpcClientMock.verifyIsLock).to.have.not.been.called(); + }); + }); + + describe('#fetchSMLStore', () => { + it('should fetch SML store', async () => { + simplifiedMasternodeListMock.getStore.resolves('store'); + + const result = await stateRepository.fetchSMLStore(); + + expect(result).to.equal('store'); + expect(simplifiedMasternodeListMock.getStore).to.be.calledOnce(); + }); + }); +}); diff --git a/packages/js-drive/test/unit/dpp/LoggedStateRepositoryDecorator.spec.js b/packages/js-drive/test/unit/dpp/LoggedStateRepositoryDecorator.spec.js new file mode 100644 index 00000000000..d53206299bf --- /dev/null +++ b/packages/js-drive/test/unit/dpp/LoggedStateRepositoryDecorator.spec.js @@ -0,0 +1,645 @@ +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); +const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); + +const LoggedStateRepositoryDecorator = require('../../../lib/dpp/LoggedStateRepositoryDecorator'); +const LoggerMock = require('../../../lib/test/mock/LoggerMock'); +const BlockExecutionContextMock = require('../../../lib/test/mock/BlockExecutionContextMock'); + +describe('LoggedStateRepositoryDecorator', () => { + let loggedStateRepositoryDecorator; + let stateRepositoryMock; + let loggerMock; + let blockExecutionContextMock; + + beforeEach(function beforeEach() { + stateRepositoryMock = createStateRepositoryMock(this.sinon); + loggerMock = new LoggerMock(this.sinon); + + blockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + blockExecutionContextMock.getConsensusLogger.returns(loggerMock); + + loggedStateRepositoryDecorator = new LoggedStateRepositoryDecorator( + stateRepositoryMock, + blockExecutionContextMock, + ); + }); + + describe('#fetchIdentity', () => { + let id; + + beforeEach(() => { + id = generateRandomIdentifier(); + }); + + it('should call logger with proper params', async () => { + const response = getIdentityFixture(); + + stateRepositoryMock.fetchIdentity.resolves(response); + + await loggedStateRepositoryDecorator.fetchIdentity(id); + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'fetchIdentity', + parameters: { id }, + response, + }, + }, 'StateRepository#fetchIdentity'); + }); + + it('should call logger in case of error', async () => { + const error = new Error('unknown error'); + + stateRepositoryMock.fetchIdentity.throws(error); + + try { + await loggedStateRepositoryDecorator.fetchIdentity(id); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).equals(error); + } + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'fetchIdentity', + parameters: { id }, + response: undefined, + }, + }, 'StateRepository#fetchIdentity'); + }); + }); + + describe('#storeIdentity', () => { + let identity; + + beforeEach(() => { + identity = getIdentityFixture(); + }); + + it('should call logger with proper params', async () => { + const response = undefined; + + stateRepositoryMock.createIdentity.resolves(response); + + await loggedStateRepositoryDecorator.createIdentity(identity); + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'createIdentity', + parameters: { identity }, + response, + }, + }, 'StateRepository#createIdentity'); + }); + + it('should call logger in case of error', async () => { + const error = new Error('unknown error'); + + stateRepositoryMock.createIdentity.throws(error); + + try { + await loggedStateRepositoryDecorator.createIdentity(identity); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).equals(error); + } + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'createIdentity', + parameters: { identity }, + response: undefined, + }, + }, 'StateRepository#createIdentity'); + }); + }); + + describe('#updateIdentity', () => { + let identity; + + beforeEach(() => { + identity = getIdentityFixture(); + }); + + it('should call logger with proper params', async () => { + const response = undefined; + + stateRepositoryMock.updateIdentity.resolves(response); + + await loggedStateRepositoryDecorator.updateIdentity(identity); + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'updateIdentity', + parameters: { identity }, + response, + }, + }, 'StateRepository#updateIdentity'); + }); + + it('should call logger in case of error', async () => { + const error = new Error('unknown error'); + + stateRepositoryMock.updateIdentity.throws(error); + + try { + await loggedStateRepositoryDecorator.updateIdentity(identity); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).equals(error); + } + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'updateIdentity', + parameters: { identity }, + response: undefined, + }, + }, 'StateRepository#updateIdentity'); + }); + }); + + describe('#storeIdentityPublicKeyHashes', () => { + let identityId; + let publicKeyHashes; + + beforeEach(() => { + identityId = generateRandomIdentifier(); + publicKeyHashes = [Buffer.alloc(36), Buffer.alloc(36)]; + }); + + it('should call logger with proper params', async () => { + const response = undefined; + + stateRepositoryMock.storeIdentityPublicKeyHashes.resolves(response); + + await loggedStateRepositoryDecorator + .storeIdentityPublicKeyHashes(identityId, publicKeyHashes); + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'storeIdentityPublicKeyHashes', + parameters: { + identityId, + publicKeyHashes: publicKeyHashes.map((hash) => hash.toString('base64')), + }, + response, + }, + }, 'StateRepository#storeIdentityPublicKeyHashes'); + }); + + it('should call logger in case of error', async () => { + const error = new Error('unknown error'); + + stateRepositoryMock.storeIdentityPublicKeyHashes.throws(error); + + try { + await loggedStateRepositoryDecorator + .storeIdentityPublicKeyHashes(identityId, publicKeyHashes); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).equals(error); + } + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'storeIdentityPublicKeyHashes', + parameters: { + identityId, + publicKeyHashes: publicKeyHashes.map((hash) => hash.toString('base64')), + }, + response: undefined, + }, + }, 'StateRepository#storeIdentityPublicKeyHashes'); + }); + }); + + describe('#fetchIdentityIdsByPublicKeyHashes', () => { + let publicKeyHashes; + + beforeEach(() => { + publicKeyHashes = [Buffer.alloc(36), Buffer.alloc(36)]; + }); + + it('should call logger with proper params', async () => { + const response = [null, generateRandomIdentifier()]; + + stateRepositoryMock.fetchIdentityIdsByPublicKeyHashes.resolves(response); + + await loggedStateRepositoryDecorator.fetchIdentityIdsByPublicKeyHashes(publicKeyHashes); + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'fetchIdentityIdsByPublicKeyHashes', + parameters: { + publicKeyHashes: publicKeyHashes.map((hash) => hash.toString('base64')), + }, + response, + }, + }, 'StateRepository#fetchIdentityIdsByPublicKeyHashes'); + }); + + it('should call logger in case of error', async () => { + const error = new Error('unknown error'); + + stateRepositoryMock.fetchIdentityIdsByPublicKeyHashes.throws(error); + + try { + await loggedStateRepositoryDecorator.fetchIdentityIdsByPublicKeyHashes(publicKeyHashes); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).equals(error); + } + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'fetchIdentityIdsByPublicKeyHashes', + parameters: { + publicKeyHashes: publicKeyHashes.map((hash) => hash.toString('base64')), + }, + response: undefined, + }, + }, 'StateRepository#fetchIdentityIdsByPublicKeyHashes'); + }); + }); + + describe('#fetchDataContract', () => { + let id; + + beforeEach(() => { + id = generateRandomIdentifier(); + }); + + it('should call logger with proper params', async () => { + const response = getDataContractFixture(); + + stateRepositoryMock.fetchDataContract.resolves(response); + + await loggedStateRepositoryDecorator.fetchDataContract(id); + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'fetchDataContract', + parameters: { id }, + response, + }, + }, 'StateRepository#fetchDataContract'); + }); + + it('should call logger in case of error', async () => { + const error = new Error('unknown error'); + + stateRepositoryMock.fetchDataContract.throws(error); + + try { + await loggedStateRepositoryDecorator.fetchDataContract(id); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).equals(error); + } + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'fetchDataContract', + parameters: { id }, + response: undefined, + }, + }, 'StateRepository#fetchDataContract'); + }); + }); + + describe('#storeDataContract', () => { + let dataContract; + + beforeEach(() => { + dataContract = getDataContractFixture(); + }); + + it('should call logger with proper params', async () => { + const response = undefined; + + stateRepositoryMock.storeDataContract.resolves(response); + + await loggedStateRepositoryDecorator.storeDataContract(dataContract); + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'storeDataContract', + parameters: { dataContract }, + response, + }, + }, 'StateRepository#storeDataContract'); + }); + + it('should call logger in case of error', async () => { + const error = new Error('unknown error'); + + stateRepositoryMock.storeDataContract.throws(error); + + try { + await loggedStateRepositoryDecorator.storeDataContract(dataContract); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).equals(error); + } + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'storeDataContract', + parameters: { dataContract }, + response: undefined, + }, + }, 'StateRepository#storeDataContract'); + }); + }); + + describe('#fetchDocuments', () => { + let contractId; + let type; + let options; + + beforeEach(() => { + contractId = generateRandomIdentifier(); + type = 'type'; + options = { + where: [['field', '==', 'value']], + }; + }); + + it('should call logger with proper params', async () => { + const response = getDocumentsFixture(); + + stateRepositoryMock.fetchDocuments.resolves(response); + + await loggedStateRepositoryDecorator.fetchDocuments(contractId, type, options); + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'fetchDocuments', + parameters: { contractId, type, options }, + response, + }, + }, 'StateRepository#fetchDocuments'); + }); + + it('should call logger in case of error', async () => { + const error = new Error('unknown error'); + + stateRepositoryMock.fetchDocuments.throws(error); + + try { + await loggedStateRepositoryDecorator.fetchDocuments(contractId, type, options); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).equals(error); + } + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'fetchDocuments', + parameters: { contractId, type, options }, + response: undefined, + }, + }, 'StateRepository#fetchDocuments'); + }); + }); + + describe('#createDocument', () => { + let document; + + beforeEach(() => { + [document] = getDocumentsFixture(); + }); + + it('should call logger with proper params', async () => { + const response = undefined; + + stateRepositoryMock.createDocument.resolves(response); + + await loggedStateRepositoryDecorator.createDocument(document); + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'createDocument', + parameters: { document }, + response, + }, + }, 'StateRepository#createDocument'); + }); + + it('should call logger in case of error', async () => { + const error = new Error('unknown error'); + + stateRepositoryMock.createDocument.throws(error); + + try { + await loggedStateRepositoryDecorator.createDocument(document); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).equals(error); + } + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'createDocument', + parameters: { document }, + response: undefined, + }, + }, 'StateRepository#createDocument'); + }); + }); + + describe('#updateDocument', () => { + let document; + + beforeEach(() => { + [document] = getDocumentsFixture(); + }); + + it('should call logger with proper params', async () => { + const response = undefined; + + stateRepositoryMock.updateDocument.resolves(response); + + await loggedStateRepositoryDecorator.updateDocument(document); + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'updateDocument', + parameters: { document }, + response, + }, + }, 'StateRepository#updateDocument'); + }); + + it('should call logger in case of error', async () => { + const error = new Error('unknown error'); + + stateRepositoryMock.updateDocument.throws(error); + + try { + await loggedStateRepositoryDecorator.updateDocument(document); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).equals(error); + } + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'updateDocument', + parameters: { document }, + response: undefined, + }, + }, 'StateRepository#updateDocument'); + }); + }); + + describe('#removeDocument', () => { + let dataContract; + let type; + let id; + + beforeEach(() => { + dataContract = getDataContractFixture(); + type = 'type'; + id = generateRandomIdentifier(); + }); + + it('should call logger with proper params', async () => { + const response = undefined; + + stateRepositoryMock.removeDocument.resolves(response); + + await loggedStateRepositoryDecorator.removeDocument(dataContract, type, id); + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'removeDocument', + parameters: { dataContract, type, id }, + response, + }, + }, 'StateRepository#removeDocument'); + }); + + it('should call logger in case of error', async () => { + const error = new Error('unknown error'); + + stateRepositoryMock.removeDocument.throws(error); + + try { + await loggedStateRepositoryDecorator.removeDocument(dataContract, type, id); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).equals(error); + } + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'removeDocument', + parameters: { dataContract, type, id }, + response: undefined, + }, + }, 'StateRepository#removeDocument'); + }); + }); + + describe('#fetchTransaction', () => { + let id; + + beforeEach(() => { + id = 'id'; + }); + + it('should call logger with proper params', async () => { + const response = { hex: '123' }; + + stateRepositoryMock.fetchTransaction.resolves(response); + + await loggedStateRepositoryDecorator.fetchTransaction(id); + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'fetchTransaction', + parameters: { id }, + response, + }, + }, 'StateRepository#fetchTransaction'); + }); + + it('should call logger in case of error', async () => { + const error = new Error('unknown error'); + + stateRepositoryMock.fetchTransaction.throws(error); + + try { + await loggedStateRepositoryDecorator.fetchTransaction(id); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).equals(error); + } + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'fetchTransaction', + parameters: { id }, + response: undefined, + }, + }, 'StateRepository#fetchTransaction'); + }); + }); + + describe('#fetchLatestPlatformBlockHeader', () => { + it('should call logger with proper params', async () => { + const response = { }; + + stateRepositoryMock.fetchLatestPlatformBlockHeader.resolves(response); + + await loggedStateRepositoryDecorator.fetchLatestPlatformBlockHeader(); + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'fetchLatestPlatformBlockHeader', + parameters: { }, + response, + }, + }, 'StateRepository#fetchLatestPlatformBlockHeader'); + }); + + it('should call logger in case of error', async () => { + const error = new Error('unknown error'); + + stateRepositoryMock.fetchLatestPlatformBlockHeader.throws(error); + + try { + await loggedStateRepositoryDecorator.fetchLatestPlatformBlockHeader(); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).equals(error); + } + + expect(loggerMock.trace).to.be.calledOnceWithExactly({ + stateRepository: { + method: 'fetchLatestPlatformBlockHeader', + parameters: { }, + response: undefined, + }, + }, 'StateRepository#fetchLatestPlatformBlockHeader'); + }); + }); +}); diff --git a/packages/js-drive/test/unit/featureFlag/getFeatureFlagForHeightFactory.spec.js b/packages/js-drive/test/unit/featureFlag/getFeatureFlagForHeightFactory.spec.js new file mode 100644 index 00000000000..508fa99878d --- /dev/null +++ b/packages/js-drive/test/unit/featureFlag/getFeatureFlagForHeightFactory.spec.js @@ -0,0 +1,65 @@ +const Long = require('long'); + +const Identifier = require('@dashevo/dpp/lib/Identifier'); +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); + +const getFeatureFlagForHeightFactory = require('../../../lib/featureFlag/getFeatureFlagForHeightFactory'); +const StorageResult = require('../../../lib/storage/StorageResult'); + +describe('getFeatureFlagForHeightFactory', () => { + let featureFlagDataContractId; + let fetchDocumentsMock; + let getFeatureFlagForHeight; + let document; + let featureFlagDataContractBlockHeight; + + beforeEach(function beforeEach() { + featureFlagDataContractId = Identifier.from(Buffer.alloc(32, 1)); + + ([document] = getDocumentsFixture()); + + fetchDocumentsMock = this.sinon.stub().resolves( + new StorageResult([document]), + ); + + featureFlagDataContractBlockHeight = 42; + + getFeatureFlagForHeight = getFeatureFlagForHeightFactory( + featureFlagDataContractId, + fetchDocumentsMock, + ); + }); + + it('should call `fetchDocuments` and return first item from the result', async () => { + const result = await getFeatureFlagForHeight('someType', new Long(43)); + + const query = { + where: [ + ['enableAtHeight', '==', 43], + ], + }; + + expect(fetchDocumentsMock).to.have.been.calledOnceWithExactly( + featureFlagDataContractId, + 'someType', + { + ...query, + useTransaction: false, + }, + ); + expect(result).to.deep.equal(document); + }); + + it('should return null if featureFlagDataContractId is undefined', async () => { + getFeatureFlagForHeight = getFeatureFlagForHeightFactory( + undefined, + featureFlagDataContractBlockHeight, + fetchDocumentsMock, + ); + + const result = await getFeatureFlagForHeight('someType', new Long(42)); + + expect(result).to.equal(null); + expect(fetchDocumentsMock).to.not.be.called(); + }); +}); diff --git a/packages/js-drive/test/unit/featureFlag/getLatestFeatureFlagFactory.spec.js b/packages/js-drive/test/unit/featureFlag/getLatestFeatureFlagFactory.spec.js new file mode 100644 index 00000000000..d0db783d48f --- /dev/null +++ b/packages/js-drive/test/unit/featureFlag/getLatestFeatureFlagFactory.spec.js @@ -0,0 +1,53 @@ +const Identifier = require('@dashevo/dpp/lib/Identifier'); +const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); +const { expect } = require('chai'); + +const Long = require('long'); + +const getLatestFeatureFlagFactory = require('../../../lib/featureFlag/getLatestFeatureFlagFactory'); +const StorageResult = require('../../../lib/storage/StorageResult'); + +describe('getLatestFeatureFlagFactory', () => { + let featureFlagDataContractId; + let fetchDocumentsMock; + let getLatestFeatureFlag; + let document; + + beforeEach(function beforeEach() { + featureFlagDataContractId = Identifier.from(Buffer.alloc(32, 1)); + + ([document] = getDocumentsFixture()); + + fetchDocumentsMock = this.sinon.stub(); + fetchDocumentsMock.resolves( + new StorageResult([document]), + ); + + getLatestFeatureFlag = getLatestFeatureFlagFactory( + featureFlagDataContractId, + fetchDocumentsMock, + ); + }); + + it('should call `fetchDocuments` and return first item from the result', async () => { + const result = await getLatestFeatureFlag('someType', new Long(42)); + + const query = { + where: [ + ['enableAtHeight', '<=', 42], + ], + orderBy: [ + ['enableAtHeight', 'desc'], + ], + limit: 1, + useTransaction: false, + }; + + expect(fetchDocumentsMock).to.have.been.calledOnceWithExactly( + featureFlagDataContractId, + 'someType', + query, + ); + expect(result).to.deep.equal(document); + }); +}); diff --git a/packages/js-drive/test/unit/identity/masternode/createMasternodeIdentityFactory.spec.js b/packages/js-drive/test/unit/identity/masternode/createMasternodeIdentityFactory.spec.js new file mode 100644 index 00000000000..953c030b7cf --- /dev/null +++ b/packages/js-drive/test/unit/identity/masternode/createMasternodeIdentityFactory.spec.js @@ -0,0 +1,190 @@ +const createDPPMock = require('@dashevo/dpp/lib/test/mocks/createDPPMock'); +const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); +const Identity = require('@dashevo/dpp/lib/identity/Identity'); +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); +const ValidationResult = require('@dashevo/dpp/lib/validation/ValidationResult'); +const Address = require('@dashevo/dashcore-lib/lib/address'); +const Script = require('@dashevo/dashcore-lib/lib/script'); +const createMasternodeIdentityFactory = require('../../../../lib/identity/masternode/createMasternodeIdentityFactory'); +const InvalidMasternodeIdentityError = require('../../../../lib/identity/masternode/errors/InvalidMasternodeIdentityError'); + +describe('createMasternodeIdentityFactory', () => { + let createMasternodeIdentity; + let dppMock; + let stateRepositoryMock; + let validationResult; + let getWithdrawPubKeyTypeFromPayoutScriptMock; + let getPublicKeyFromPayoutScriptMock; + + beforeEach(function beforeEach() { + dppMock = createDPPMock(this.sinon); + stateRepositoryMock = createStateRepositoryMock(this.sinon); + getWithdrawPubKeyTypeFromPayoutScriptMock = this.sinon.stub().returns( + IdentityPublicKey.TYPES.BIP13_SCRIPT_HASH, + ); + + getPublicKeyFromPayoutScriptMock = this.sinon.stub().returns( + Buffer.alloc(20, 1), + ); + + validationResult = new ValidationResult(); + + dppMock.identity.validate.resolves(validationResult); + + createMasternodeIdentity = createMasternodeIdentityFactory( + dppMock, + stateRepositoryMock, + getWithdrawPubKeyTypeFromPayoutScriptMock, + getPublicKeyFromPayoutScriptMock, + ); + }); + + it('should create masternode identity', async () => { + const identityId = generateRandomIdentifier(); + const pubKeyData = Buffer.from([0]); + const pubKeyType = IdentityPublicKey.TYPES.ECDSA_HASH160; + + const result = await createMasternodeIdentity(identityId, pubKeyData, pubKeyType); + + const identity = new Identity({ + protocolVersion: dppMock.getProtocolVersion(), + id: identityId, + publicKeys: [{ + id: 0, + type: pubKeyType, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: true, + // Copy data buffer + data: Buffer.from([0]), + }], + balance: 0, + revision: 0, + }); + + expect(result).to.deep.equal(identity); + + expect(stateRepositoryMock.createIdentity).to.have.been.calledOnceWithExactly(identity); + expect(getWithdrawPubKeyTypeFromPayoutScriptMock).to.not.be.called(); + expect(getPublicKeyFromPayoutScriptMock).to.not.be.called(); + + const publicKeyHashes = identity + .getPublicKeys() + .map((publicKey) => publicKey.hash()); + + expect(stateRepositoryMock.storeIdentityPublicKeyHashes).to.have.been.calledOnceWithExactly( + identity.getId(), + publicKeyHashes, + ); + + expect(dppMock.identity.validate).to.be.calledOnceWithExactly(identity); + }); + + it('should store identity and public key hashed to the previous store', async () => { + const identityId = generateRandomIdentifier(); + const pubKeyData = Buffer.from([0]); + const pubKeyType = IdentityPublicKey.TYPES.ECDSA_HASH160; + + const result = await createMasternodeIdentity(identityId, pubKeyData, pubKeyType); + + const identity = new Identity({ + protocolVersion: dppMock.getProtocolVersion(), + id: identityId, + publicKeys: [{ + id: 0, + type: pubKeyType, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: true, + // Copy data buffer + data: Buffer.from([0]), + }], + balance: 0, + revision: 0, + }); + + expect(result).to.deep.equal(identity); + + expect(stateRepositoryMock.createIdentity).to.have.been.calledOnceWithExactly(identity); + + const publicKeyHashes = identity + .getPublicKeys() + .map((publicKey) => publicKey.hash()); + + expect(stateRepositoryMock.storeIdentityPublicKeyHashes).to.have.been.calledOnceWithExactly( + identity.getId(), + publicKeyHashes, + ); + + expect(dppMock.identity.validate).to.be.calledOnceWithExactly(identity); + }); + + it('should throw DPPValidationAbciError if identity is not valid', async () => { + const validationError = new Error('Validation error'); + + validationResult.addError(validationError); + + const identityId = generateRandomIdentifier(); + const pubKeyData = Buffer.from([0]); + const pubKeyType = IdentityPublicKey.TYPES.ECDSA_HASH160; + + try { + await createMasternodeIdentity(identityId, pubKeyData, pubKeyType); + + expect.fail('should fail with an error'); + } catch (e) { + expect(e).to.be.an.instanceof(InvalidMasternodeIdentityError); + expect(e.message).to.be.equal('Invalid masternode identity'); + expect(e.getValidationError()).to.be.deep.equal(validationError); + } + }); + + it('should create masternode identity with payoutScript public key', async () => { + const identityId = generateRandomIdentifier(); + const pubKeyData = Buffer.from([0]); + const pubKeyType = IdentityPublicKey.TYPES.ECDSA_HASH160; + const payoutScript = new Script(Address.fromString('7UkJidhNjEPJCQnCTXeaJKbJmL4JuyV66w')); + + const result = await createMasternodeIdentity(identityId, pubKeyData, pubKeyType, payoutScript); + + const identity = new Identity({ + protocolVersion: dppMock.getProtocolVersion(), + id: identityId, + publicKeys: [{ + id: 0, + type: pubKeyType, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: true, + data: Buffer.from([0]), + }, { + id: 1, + type: IdentityPublicKey.TYPES.BIP13_SCRIPT_HASH, + purpose: IdentityPublicKey.PURPOSES.WITHDRAW, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.CRITICAL, + readOnly: false, + data: Buffer.alloc(20, 1), + }], + balance: 0, + revision: 0, + }); + + expect(result).to.deep.equal(identity); + + expect(stateRepositoryMock.createIdentity).to.have.been.calledOnceWithExactly(identity); + expect(getWithdrawPubKeyTypeFromPayoutScriptMock).to.be.calledOnce(); + expect(getPublicKeyFromPayoutScriptMock).to.be.calledOnce(); + + const publicKeyHashes = identity + .getPublicKeys() + .map((publicKey) => publicKey.hash()); + + expect(stateRepositoryMock.storeIdentityPublicKeyHashes).to.have.been.calledOnceWithExactly( + identity.getId(), + publicKeyHashes, + ); + + expect(dppMock.identity.validate).to.be.calledOnceWithExactly(identity); + }); +}); diff --git a/packages/js-drive/test/unit/identity/masternode/createOperatorIdentifier.spec.js b/packages/js-drive/test/unit/identity/masternode/createOperatorIdentifier.spec.js new file mode 100644 index 00000000000..59815f056d5 --- /dev/null +++ b/packages/js-drive/test/unit/identity/masternode/createOperatorIdentifier.spec.js @@ -0,0 +1,23 @@ +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); +const createOperatorIdentifier = require('../../../../lib/identity/masternode/createOperatorIdentifier'); + +describe('createOperatorIdentifier', () => { + let smlEntry; + + beforeEach(() => { + smlEntry = { + proRegTxHash: '5557273f5922d9925e2327908ddb128bcf8e055a04d86e23431809bedd077060', + confirmedHash: '0000003da09fd100c60ad5743c44257bb9220ad8162a9b6cae9d005c8e465dba', + service: '95.222.25.60:19997', + pubKeyOperator: '08b66151b81bd6a08bad2e68810ea07014012d6d804859219958a7fbc293689aa902bd0cd6db7a4699c9e88a4ae8c2c0', + votingAddress: 'yZRteAQ51BoeD3sJL1iGdt6HJLgkWGurw5', + isValid: false, + }; + }); + + it('should return operator identifier from smlEntry', () => { + const identifier = createOperatorIdentifier(smlEntry); + + expect(identifier).to.deep.equal(Identifier.from('EwLi1FgGwvmLQ9nkfnttpXzv4SfC7XGBvs61QBCtnHEL')); + }); +}); diff --git a/packages/js-drive/test/unit/identity/masternode/getPublicKeyFromPayoutScript.spec.js b/packages/js-drive/test/unit/identity/masternode/getPublicKeyFromPayoutScript.spec.js new file mode 100644 index 00000000000..9239624e377 --- /dev/null +++ b/packages/js-drive/test/unit/identity/masternode/getPublicKeyFromPayoutScript.spec.js @@ -0,0 +1,43 @@ +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); +const Address = require('@dashevo/dashcore-lib/lib/address'); +const Script = require('@dashevo/dashcore-lib/lib/script'); +const InvalidIdentityPublicKeyTypeError = require('@dashevo/dpp/lib/stateTransition/errors/InvalidIdentityPublicKeyTypeError'); +const getPublicKeyFromPayoutScript = require('../../../../lib/identity/masternode/getPublicKeyFromPayoutScript'); + +describe('getPublicKeyFromPayoutScript', () => { + it('should return public key for ECDSA_HASH160 script', () => { + const payoutAddress = Address.fromString('yLceJztHVZFbeqE9v86sLD9bDKFBmNqHQD'); + const scriptBuffer = new Script(payoutAddress); + + const type = IdentityPublicKey.TYPES.ECDSA_HASH160; + + const result = getPublicKeyFromPayoutScript(scriptBuffer, type); + + expect(result).to.deep.equal(Buffer.from('0340a3abf7e6eccf42b4dd71ef8c20ed53a78d1f', 'hex')); + }); + + it('should return public key for BIP13_SCRIPT_HASH script', () => { + const payoutAddress = Address.fromString('7UkJidhNjEPJCQnCTXeaJKbJmL4JuyV66w'); + const scriptBuffer = new Script(payoutAddress); + + const type = IdentityPublicKey.TYPES.BIP13_SCRIPT_HASH; + + const result = getPublicKeyFromPayoutScript(scriptBuffer, type); + + expect(result).to.deep.equal(Buffer.from('19a7d869032368fd1f1e26e5e73a4ad0e474960e', 'hex')); + }); + + it('should throw InvalidIdentityPublicKeyTypeError if type is unknown', () => { + const payoutAddress = Address.fromString('7UkJidhNjEPJCQnCTXeaJKbJmL4JuyV66w'); + const scriptBuffer = new Script(payoutAddress); + + try { + getPublicKeyFromPayoutScript(scriptBuffer, -1); + + expect.fail('should throw InvalidIdentityPublicKeyTypeError'); + } catch (e) { + expect(e).to.be.an.instanceof(InvalidIdentityPublicKeyTypeError); + expect(e.getPublicKeyType()).to.equal(-1); + } + }); +}); diff --git a/packages/js-drive/test/unit/identity/masternode/getWithdrawPubKeyTypeFromPayoutScriptFactory.spec.js b/packages/js-drive/test/unit/identity/masternode/getWithdrawPubKeyTypeFromPayoutScriptFactory.spec.js new file mode 100644 index 00000000000..5b2950fec34 --- /dev/null +++ b/packages/js-drive/test/unit/identity/masternode/getWithdrawPubKeyTypeFromPayoutScriptFactory.spec.js @@ -0,0 +1,44 @@ +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); +const Address = require('@dashevo/dashcore-lib/lib/address'); +const Script = require('@dashevo/dashcore-lib/lib/script'); +const getWithdrawPubKeyTypeFromPayoutScriptFactory = require('../../../../lib/identity/masternode/getWithdrawPubKeyTypeFromPayoutScriptFactory'); +const InvalidPayoutScriptError = require('../../../../lib/identity/masternode/errors/InvalidPayoutScriptError'); + +describe('getWithdrawPubKeyTypeFromPayoutScriptFactory', () => { + let getWithdrawPubKeyTypeFromPayoutScript; + let network; + + beforeEach(() => { + network = 'testnet'; + getWithdrawPubKeyTypeFromPayoutScript = getWithdrawPubKeyTypeFromPayoutScriptFactory( + network, + ); + }); + + it('should return ECDSA_HASH160 if address has p2pkh type', () => { + const payoutScript = Script(Address.fromString('yTsGq4wV8WF5GKLaYV2C43zrkr2sfTtysT')); + const type = getWithdrawPubKeyTypeFromPayoutScript(payoutScript); + + expect(type).to.be.equal(IdentityPublicKey.TYPES.ECDSA_HASH160); + }); + + it('should return BIP13_SCRIPT_HASH if address has p2sh type', () => { + const payoutScript = Script(Address.fromString('7UkJidhNjEPJCQnCTXeaJKbJmL4JuyV66w')); + const type = getWithdrawPubKeyTypeFromPayoutScript(payoutScript); + + expect(type).to.be.equal(IdentityPublicKey.TYPES.BIP13_SCRIPT_HASH); + }); + + it('should throw InvalidPayoutScriptError if address is not p2sh or p2pkh', () => { + const payoutScript = new Script(); + + try { + getWithdrawPubKeyTypeFromPayoutScript(payoutScript); + + expect.fail('should throw InvalidPayoutScriptError'); + } catch (e) { + expect(e).to.be.an.instanceOf(InvalidPayoutScriptError); + expect(e.getPayoutScript()).to.deep.equal(payoutScript); + } + }); +}); diff --git a/packages/js-drive/test/unit/identity/masternode/handleNewMasternodeFactory.spec.js b/packages/js-drive/test/unit/identity/masternode/handleNewMasternodeFactory.spec.js new file mode 100644 index 00000000000..819b9db1c88 --- /dev/null +++ b/packages/js-drive/test/unit/identity/masternode/handleNewMasternodeFactory.spec.js @@ -0,0 +1,103 @@ +const createDPPMock = require('@dashevo/dpp/lib/test/mocks/createDPPMock'); +const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); +const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); +const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); +const Address = require('@dashevo/dashcore-lib/lib/address'); +const Script = require('@dashevo/dashcore-lib/lib/script'); +const handleNewMasternodeFactory = require('../../../../lib/identity/masternode/handleNewMasternodeFactory'); +const getSmlFixture = require('../../../../lib/test/fixtures/getSmlFixture'); +const createOperatorIdentifier = require('../../../../lib/identity/masternode/createOperatorIdentifier'); + +describe('handleNewMasternodeFactory', () => { + let handleNewMasternode; + let dppMock; + let stateRepositoryMock; + let createMasternodeIdentityMock; + let createRewardShareDocumentMock; + let fetchTransactionMock; + let transactionFixture; + let masternodeEntry; + let dataContract; + + beforeEach(function beforeEach() { + const smlFixture = getSmlFixture(); + [masternodeEntry] = smlFixture[0].mnList; + masternodeEntry.operatorPayoutAddress = 'yTCALGQTFNsA4pMPLTKAWdaLRmxfGpbujY'; + + dataContract = getDataContractFixture(); + + dppMock = createDPPMock(this.sinon); + stateRepositoryMock = createStateRepositoryMock(this.sinon); + + createMasternodeIdentityMock = this.sinon.stub(); + createRewardShareDocumentMock = this.sinon.stub(); + + transactionFixture = { + extraPayload: { + operatorReward: 0, + keyIDOwner: Buffer.alloc(20).fill('a').toString('hex'), + }, + }; + + fetchTransactionMock = this.sinon.stub().resolves(transactionFixture); + + handleNewMasternode = handleNewMasternodeFactory( + dppMock, + stateRepositoryMock, + createMasternodeIdentityMock, + createRewardShareDocumentMock, + fetchTransactionMock, + ); + }); + + it('should create masternode identity', async () => { + masternodeEntry.payoutAddress = 'yRRwW957BJwL6SVVh3s8ASQYa2qXnduyfx'; + + const payoutAddress = Address.fromString(masternodeEntry.payoutAddress); + const payoutScript = new Script(payoutAddress); + + await handleNewMasternode(masternodeEntry, dataContract); + + expect(fetchTransactionMock).to.be.calledOnceWithExactly(masternodeEntry.proRegTxHash); + expect(createMasternodeIdentityMock).to.be.calledOnceWithExactly( + Identifier.from('6k8jXHFuno3vqpfrQ36CaxrGi4SupdTJcGNeZLPioxQo'), + Buffer.from('6161616161616161616161616161616161616161', 'hex'), + IdentityPublicKey.TYPES.ECDSA_HASH160, + payoutScript, + ); + expect(createRewardShareDocumentMock).to.not.be.called(); + }); + + it('should create masternode identity and a document in rewards data contract with percentage', async () => { + transactionFixture.extraPayload.operatorReward = 10; + + await handleNewMasternode(masternodeEntry, dataContract); + + const operatorIdentifier = createOperatorIdentifier(masternodeEntry); + const operatorPayoutAddress = Address.fromString(masternodeEntry.operatorPayoutAddress); + const operatorPayoutScript = new Script(operatorPayoutAddress); + + expect(fetchTransactionMock).to.be.calledOnceWithExactly(masternodeEntry.proRegTxHash); + expect(createMasternodeIdentityMock).to.be.calledTwice(); + expect(createMasternodeIdentityMock.getCall(0)).to.be.calledWith( + Identifier.from('6k8jXHFuno3vqpfrQ36CaxrGi4SupdTJcGNeZLPioxQo'), + Buffer.from('6161616161616161616161616161616161616161', 'hex'), + IdentityPublicKey.TYPES.ECDSA_HASH160, + undefined, + ); + expect(createMasternodeIdentityMock.getCall(1)).to.be.calledWith( + operatorIdentifier, + Buffer.from('08b66151b81bd6a08bad2e68810ea07014012d6d804859219958a7fbc293689aa902bd0cd6db7a4699c9e88a4ae8c2c0', 'hex'), + IdentityPublicKey.TYPES.BLS12_381, + operatorPayoutScript, + ); + + expect(createRewardShareDocumentMock).to.be.calledOnceWithExactly( + dataContract, + Identifier.from('6k8jXHFuno3vqpfrQ36CaxrGi4SupdTJcGNeZLPioxQo'), + Identifier.from('EwLi1FgGwvmLQ9nkfnttpXzv4SfC7XGBvs61QBCtnHEL'), + 10, + ); + }); +}); diff --git a/packages/js-drive/test/unit/identity/masternode/handleUpdatedScriptPayoutFactory.spec.js b/packages/js-drive/test/unit/identity/masternode/handleUpdatedScriptPayoutFactory.spec.js new file mode 100644 index 00000000000..d5b0c5e51a4 --- /dev/null +++ b/packages/js-drive/test/unit/identity/masternode/handleUpdatedScriptPayoutFactory.spec.js @@ -0,0 +1,125 @@ +const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); +const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); +const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFixture'); +const Identity = require('@dashevo/dpp/lib/identity/Identity'); +const Script = require('@dashevo/dashcore-lib/lib/script'); +const identitySchema = require('@dashevo/dpp/schema/identity/identity.json'); +const handleUpdatedScriptPayoutFactory = require('../../../../lib/identity/masternode/handleUpdatedScriptPayoutFactory'); +const BlockExecutionContextMock = require('../../../../lib/test/mock/BlockExecutionContextMock'); + +describe('handleUpdatedScriptPayoutFactory', () => { + let handleUpdatedScriptPayout; + let stateRepositoryMock; + let getWithdrawPubKeyTypeFromPayoutScriptMock; + let getPublicKeyFromPayoutScriptMock; + let blockExecutionContextMock; + let identity; + let time; + + beforeEach(function beforeEach() { + identity = getIdentityFixture(); + + time = new Date().getTime(); + + blockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + blockExecutionContextMock.getHeader.returns({ time: { seconds: Math.ceil(time / 1000) } }); + + stateRepositoryMock = createStateRepositoryMock(this.sinon); + stateRepositoryMock.fetchIdentity.resolves( + identity, + ); + + getWithdrawPubKeyTypeFromPayoutScriptMock = this.sinon.stub().returns( + IdentityPublicKey.TYPES.ECDSA_HASH160, + ); + + getPublicKeyFromPayoutScriptMock = this.sinon.stub().returns(Buffer.alloc(20, '0')); + + handleUpdatedScriptPayout = handleUpdatedScriptPayoutFactory( + stateRepositoryMock, + blockExecutionContextMock, + getWithdrawPubKeyTypeFromPayoutScriptMock, + getPublicKeyFromPayoutScriptMock, + ); + }); + + it('should not update identity if identityPublicKeys max length was reached', async () => { + const { maxItems } = identitySchema.properties.publicKeys; + for (let i = identity.getPublicKeys().length; i < maxItems; ++i) { + identity.publicKeys.push({ + data: 'fakePublicKey', + }); + } + + const newPubKeyData = Buffer.alloc(20, '0'); + + await handleUpdatedScriptPayout( + identity.getId(), + newPubKeyData, + identity.publicKeys[0].getData(), + ); + + expect(stateRepositoryMock.updateIdentity).to.not.be.called(); + expect(stateRepositoryMock.storeIdentityPublicKeyHashes).to.not.be.called(); + }); + + it('should store updated identity with updated public keys', async () => { + const newPubKeyData = Buffer.alloc(20, '0'); + const identityPublicKeys = identity.getPublicKeys(); + + await handleUpdatedScriptPayout( + identity.getId(), + newPubKeyData, + identity.publicKeys[0].getData(), + ); + + const identityToStore = new Identity(identity.toObject()); + + identityPublicKeys[0].disabledAt = time; + + const newWithdrawalIdentityPublicKey = new IdentityPublicKey() + .setId(2) + .setType(IdentityPublicKey.TYPES.ECDSA_HASH160) + .setData(Buffer.from(newPubKeyData)) + .setPurpose(IdentityPublicKey.PURPOSES.WITHDRAW) + .setSecurityLevel(IdentityPublicKey.SECURITY_LEVELS.MASTER); + + identityPublicKeys.push(newWithdrawalIdentityPublicKey); + identityToStore.setPublicKeys(identityPublicKeys); + + expect(stateRepositoryMock.updateIdentity).to.be.calledOnceWithExactly(identityToStore); + expect(stateRepositoryMock.storeIdentityPublicKeyHashes).to.be.calledOnceWithExactly( + identity.getId(), + [newPubKeyData], + ); + }); + + it('should store add public keys to the stored identity', async () => { + const newPubKeyData = Buffer.alloc(20, '0'); + const identityPublicKeys = identity.getPublicKeys(); + + await handleUpdatedScriptPayout( + identity.getId(), + newPubKeyData, + new Script(), + ); + + const identityToStore = new Identity(identity.toObject()); + + const newWithdrawalIdentityPublicKey = new IdentityPublicKey() + .setId(2) + .setType(IdentityPublicKey.TYPES.ECDSA_HASH160) + .setData(Buffer.from(newPubKeyData)) + .setPurpose(IdentityPublicKey.PURPOSES.WITHDRAW) + .setSecurityLevel(IdentityPublicKey.SECURITY_LEVELS.MASTER); + + identityPublicKeys.push(newWithdrawalIdentityPublicKey); + identityToStore.setPublicKeys(identityPublicKeys); + + expect(stateRepositoryMock.updateIdentity).to.be.calledOnceWithExactly(identityToStore); + expect(stateRepositoryMock.storeIdentityPublicKeyHashes).to.be.calledOnceWithExactly( + identity.getId(), + [newPubKeyData], + ); + }); +}); diff --git a/packages/js-drive/test/unit/state/createInitialStateStructureFactory.spec.js b/packages/js-drive/test/unit/state/createInitialStateStructureFactory.spec.js new file mode 100644 index 00000000000..7e709e6b2f8 --- /dev/null +++ b/packages/js-drive/test/unit/state/createInitialStateStructureFactory.spec.js @@ -0,0 +1,62 @@ +const createInitialStateStructureFactory = require('../../../lib/state/createInitialStateStructureFactory'); +const SpentAssetLockTransactionsRepository = require('../../../lib/identity/SpentAssetLockTransactionsRepository'); + +describe('createInitialStateStructureFactory', () => { + let createInitialStateStructure; + let identityRepositoryMock; + let publicKeyToIdentitiesRepositoryMock; + let groveDBStoreMock; + let dataContractRepositoryMock; + let spentAssetLockTransactionsRepositoryMock; + + beforeEach(function beforeEach() { + identityRepositoryMock = { + createTree: this.sinon.stub(), + }; + + publicKeyToIdentitiesRepositoryMock = { + createTree: this.sinon.stub(), + }; + + dataContractRepositoryMock = { + createTree: this.sinon.stub(), + }; + + groveDBStoreMock = { + createTree: this.sinon.stub(), + }; + + spentAssetLockTransactionsRepositoryMock = { + createTree: this.sinon.stub(), + }; + + createInitialStateStructure = createInitialStateStructureFactory( + identityRepositoryMock, + publicKeyToIdentitiesRepositoryMock, + spentAssetLockTransactionsRepositoryMock, + dataContractRepositoryMock, + groveDBStoreMock, + ); + }); + + it('should create initial state structure', async () => { + await createInitialStateStructure(); + + expect(identityRepositoryMock.createTree) + .to.be.calledOnceWithExactly({ useTransaction: true }); + expect(publicKeyToIdentitiesRepositoryMock.createTree) + .to.be.calledOnceWithExactly({ useTransaction: true }); + expect(dataContractRepositoryMock.createTree) + .to.be.calledOnceWithExactly({ useTransaction: true }); + + expect(groveDBStoreMock.createTree) + .to.be.calledOnceWithExactly( + [], + SpentAssetLockTransactionsRepository.TREE_PATH[0], + { useTransaction: true }, + ); + + expect(spentAssetLockTransactionsRepositoryMock.createTree) + .to.be.calledOnceWithExactly({ useTransaction: true }); + }); +}); diff --git a/packages/js-drive/test/unit/util/ExecutionTimer.spec.js b/packages/js-drive/test/unit/util/ExecutionTimer.spec.js new file mode 100644 index 00000000000..b1f3aff7a98 --- /dev/null +++ b/packages/js-drive/test/unit/util/ExecutionTimer.spec.js @@ -0,0 +1,43 @@ +const ExecutionTimer = require('../../../lib/util/ExecutionTimer'); +const wait = require('../../../lib/util/wait'); + +describe('ExecutionTimer', () => { + let timer; + + beforeEach(() => { + timer = new ExecutionTimer(); + }); + + describe('#startTimer', () => { + it('should throw an error if timer already started', () => { + timer.startTimer('some'); + + try { + timer.startTimer('some'); + expect.fail('An error was not thrown'); + } catch (e) { + expect(e.message).to.equal('some timer is already started'); + } + }); + }); + + describe('#stopTimer', () => { + it('should throw an error if timer has not been started', () => { + try { + timer.stopTimer('some'); + expect.fail('An error was not thrown'); + } catch (e) { + expect(e.message).to.equal('some timer is not started'); + } + }); + }); + + it('should measure function execution time', async () => { + // TODO: maybe there should be a better way to do it + timer.startTimer('some'); + await wait(1500); + const timings = timer.stopTimer('some'); + + expect(parseInt(timings, 10)).to.equal(1); + }); +}); diff --git a/packages/js-drive/test/unit/util/errorHandlerFactory.spec.js b/packages/js-drive/test/unit/util/errorHandlerFactory.spec.js new file mode 100644 index 00000000000..eed07ae33d8 --- /dev/null +++ b/packages/js-drive/test/unit/util/errorHandlerFactory.spec.js @@ -0,0 +1,140 @@ +const errorHandlerFactory = require('../../../lib/errorHandlerFactory'); +const LoggerMock = require('../../../lib/test/mock/LoggerMock'); + +describe('errorHandlerFactory', () => { + let errorHandler; + let containerMock; + let loggerMock; + let closeAbciServerMock; + + beforeEach(function beforeEach() { + this.sinon.stub(console, 'log'); + this.sinon.stub(console, 'error'); + this.sinon.stub(process, 'exit'); + + containerMock = { + dispose: this.sinon.stub(), + }; + + closeAbciServerMock = this.sinon.stub(); + + loggerMock = new LoggerMock(this.sinon); + + errorHandler = errorHandlerFactory( + loggerMock, + containerMock, + closeAbciServerMock, + ); + }); + + it('should close server, log error, dispose container and exit process on first call', async () => { + const error = new Error('message'); + + await errorHandler(error); + + expect(closeAbciServerMock).to.be.calledOnceWithExactly(); + + // Error face is printed + // eslint-disable-next-line no-console + expect(console.log).to.be.calledOnce(); + + expect(loggerMock.fatal).to.be.calledOnceWithExactly({ err: error }, error.message); + + expect(containerMock.dispose).to.be.calledOnceWithExactly(); + + expect(process.exit).to.be.calledOnceWithExactly(1); + }); + + it('should use consensus logger if it\'s present', async function it() { + const error = new Error('message'); + + error.consensusLogger = new LoggerMock(this.sinon); + + await errorHandler(error); + + expect(loggerMock.fatal).to.not.be.called(); + expect(error.consensusLogger.fatal).to.be.calledOnceWithExactly({ err: error }, error.message); + + expect(containerMock.dispose).to.be.calledOnceWithExactly(); + + expect(process.exit).to.be.calledOnceWithExactly(1); + + // eslint-disable-next-line no-console + expect(console.log).to.be.calledOnce(); + }); + + it('should collect an error on second call', async () => { + const error1 = new Error('error1'); + const error2 = new Error('error2'); + + await Promise.all([ + errorHandler(error1), + errorHandler(error2), + ]); + + expect(closeAbciServerMock).to.be.calledOnceWithExactly(); + + // Error face is printed + // eslint-disable-next-line no-console + expect(console.log).to.be.calledOnce(); + + expect(loggerMock.fatal).to.be.calledTwice(); + + expect(loggerMock.fatal.getCall(0)).to.be.calledWithExactly({ err: error1 }, error1.message); + expect(loggerMock.fatal.getCall(1)).to.be.calledWithExactly({ err: error2 }, error2.message); + + expect(containerMock.dispose).to.be.calledOnceWithExactly(); + + expect(process.exit).to.be.calledOnceWithExactly(1); + }); + + it('should dispose container and output error in console if it was thrown during error handling', async () => { + const closeError = new Error('close server error'); + + closeAbciServerMock.throws(closeError); + + const error = new Error('message'); + + await errorHandler(error); + + expect(closeAbciServerMock).to.be.calledOnceWithExactly(); + + // Error face is printed + // eslint-disable-next-line no-console + expect(console.log).to.not.be.called(); + + expect(loggerMock.fatal).to.not.be.called(); + + expect(containerMock.dispose).to.be.calledOnceWithExactly(); + + // eslint-disable-next-line no-console + expect(console.error).to.be.calledOnceWithExactly(closeError); + + expect(process.exit).to.be.calledOnceWithExactly(1); + }); + + it('should output error in console if it was thrown during dispose', async () => { + const disposeError = new Error('dispose error'); + + containerMock.dispose.throws(disposeError); + + const error = new Error('message'); + + await errorHandler(error); + + expect(closeAbciServerMock).to.be.calledOnceWithExactly(); + + // Error face is printed + // eslint-disable-next-line no-console + expect(console.log).to.be.calledOnce(); + + expect(loggerMock.fatal).to.be.calledOnceWithExactly({ err: error }, error.message); + + expect(containerMock.dispose).to.be.calledOnceWithExactly(); + + // eslint-disable-next-line no-console + expect(console.error).to.be.calledOnceWithExactly(disposeError); + + expect(process.exit).to.be.calledOnceWithExactly(1); + }); +}); diff --git a/packages/js-drive/test/unit/util/rejectAfter.spec.js b/packages/js-drive/test/unit/util/rejectAfter.spec.js new file mode 100644 index 00000000000..31a281a6fa6 --- /dev/null +++ b/packages/js-drive/test/unit/util/rejectAfter.spec.js @@ -0,0 +1,34 @@ +const rejectAfter = require('../../../lib/util/rejectAfter'); + +describe('rejectAfter', () => { + it('should return resolved promise', async () => { + const resolvedValue = 1; + const promise = Promise.resolve(resolvedValue); + + const actualValue = await rejectAfter(promise, new Error(), 1000); + + expect(actualValue).to.equal(resolvedValue); + }); + + it('should return rejected promise', (done) => { + const error = new Error(); + const promise = Promise.reject(error); + + const actualPromise = rejectAfter(promise, new Error(), 1000); + + expect(actualPromise).to.be.rejectedWith(error).and.notify(done); + }); + + it('should reject unresolved promise after specified time', function it(done) { + const promise = new Promise(() => {}); + const error = new Error(); + + const clock = this.sinon.useFakeTimers(); + + const rejectedPromise = rejectAfter(promise, error, 1000); + + clock.next(); + + expect(rejectedPromise).to.be.rejectedWith(error).and.notify(done); + }); +}); diff --git a/packages/js-drive/test/unit/util/sanitizeUrl.spec.js b/packages/js-drive/test/unit/util/sanitizeUrl.spec.js new file mode 100644 index 00000000000..73c40d47682 --- /dev/null +++ b/packages/js-drive/test/unit/util/sanitizeUrl.spec.js @@ -0,0 +1,13 @@ +const { expect } = require('chai'); +const sanitizeUrl = require('../../../lib/util/sanitizeUrl'); + +describe('sanitizeUrl', () => { + it('should sanitize an url', () => { + const sanitized = sanitizeUrl('https://www.dash.org?something=true'); + expect(sanitized).to.equal('https://www.dash.org'); + }); + it('should handle non RFC path', () => { + const sanitized = sanitizeUrl('/foo;jsessionid=123456'); + expect(sanitized).to.equal('/foo'); + }); +}); diff --git a/packages/js-drive/test/unit/util/wait.spec.js b/packages/js-drive/test/unit/util/wait.spec.js new file mode 100644 index 00000000000..353cf3606b0 --- /dev/null +++ b/packages/js-drive/test/unit/util/wait.spec.js @@ -0,0 +1,30 @@ +const wait = require('../../../lib/util/wait'); + +describe('wait', () => { + let clock; + let executeWithWait; + + beforeEach(function beforeEach() { + clock = this.sinon.useFakeTimers(); + + executeWithWait = async (f) => { + await wait(1200); + f(); + }; + }); + + it('should delay execution of a flow for a specified amount of milliseconds', function it(done) { + const callback = this.sinon.stub(); + + executeWithWait(callback).then(() => { + expect(callback).to.have.been.calledOnce(); + done(); + }).catch(done); + + clock.tick(1199); + + expect(callback).to.have.not.been.called(); + + clock.tick(1); + }); +}); diff --git a/packages/js-drive/test/unit/validator/Validator.spec.js b/packages/js-drive/test/unit/validator/Validator.spec.js new file mode 100644 index 00000000000..c3544e8140b --- /dev/null +++ b/packages/js-drive/test/unit/validator/Validator.spec.js @@ -0,0 +1,37 @@ +const { expect } = require('chai'); +const Validator = require('../../../lib/validator/Validator'); +const ValidatorNetworkInfo = require('../../../lib/validator/ValidatorNetworkInfo'); + +describe('Validator', () => { + let networkInfo; + let host; + let port; + + beforeEach(() => { + host = '192.168.65.2'; + port = 26656; + networkInfo = new ValidatorNetworkInfo(host, port); + }); + + describe('#createFromQuorumMember', () => { + it('should create an instance from quorum member info', () => { + const memberInfo = { + proTxHash: Buffer.alloc(0, 32).toString('hex'), + pubKeyShare: Buffer.alloc(1, 32).toString('hex'), + }; + + const instance = Validator.createFromQuorumMember(memberInfo, networkInfo); + + expect(instance).to.be.an.instanceOf(Validator); + expect(instance.getProTxHash()).to.deep.equal( + Buffer.from(memberInfo.proTxHash, 'hex'), + ); + expect(instance.getPublicKeyShare()).to.deep.equal( + Buffer.from(memberInfo.pubKeyShare, 'hex'), + ); + expect(instance.getNetworkInfo()).to.be.an.instanceOf(ValidatorNetworkInfo); + expect(instance.getNetworkInfo().getHost()).to.equal(host); + expect(instance.getNetworkInfo().getPort()).to.equal(port); + }); + }); +}); diff --git a/packages/js-drive/test/unit/validator/ValidatorNetworkInfo.spec.js b/packages/js-drive/test/unit/validator/ValidatorNetworkInfo.spec.js new file mode 100644 index 00000000000..92ba6b25946 --- /dev/null +++ b/packages/js-drive/test/unit/validator/ValidatorNetworkInfo.spec.js @@ -0,0 +1,24 @@ +const ValidatorNetworkInfo = require('../../../lib/validator/ValidatorNetworkInfo'); + +describe('ValidatorNetworkInfo', () => { + let validatorNetworkInfo; + let host; + let port; + + beforeEach(() => { + host = '192.168.65.2'; + port = 26656; + }); + + it('should return host', () => { + validatorNetworkInfo = new ValidatorNetworkInfo(host, port); + + expect(validatorNetworkInfo.getHost()).to.equal(host); + }); + + it('should return port', () => { + validatorNetworkInfo = new ValidatorNetworkInfo(host, port); + + expect(validatorNetworkInfo.getPort()).to.equal(port); + }); +}); diff --git a/packages/js-drive/test/unit/validator/ValidatorSet.spec.js b/packages/js-drive/test/unit/validator/ValidatorSet.spec.js new file mode 100644 index 00000000000..82885a699d3 --- /dev/null +++ b/packages/js-drive/test/unit/validator/ValidatorSet.spec.js @@ -0,0 +1,249 @@ +const Long = require('long'); + +const QuorumEntry = require('@dashevo/dashcore-lib/lib/deterministicmnlist/QuorumEntry'); + +const SimplifiedMNListEntry = require('@dashevo/dashcore-lib/lib/deterministicmnlist/SimplifiedMNListEntry'); +const ValidatorSet = require('../../../lib/validator/ValidatorSet'); +const getSmlFixture = require('../../../lib/test/fixtures/getSmlFixture'); +const ValidatorSetIsNotInitializedError = require('../../../lib/validator/errors/ValidatorSetIsNotInitializedError'); +const Validator = require('../../../lib/validator/Validator'); +const PublicKeyShareIsNotPresentError = require('../../../lib/validator/errors/PublicKeyShareIsNotPresentError'); + +describe('ValidatorSet', () => { + let smlStoreMock; + let simplifiedMasternodeListMock; + let smlDiffMock; + let smlMock; + let quorumMembers; + let rotationEntropy; + let quorumEntry; + let coreHeight; + let coreRpcClientMock; + let validatorNetworkPort; + + let validatorSetLLMQType; + + let validatorSet; + let getRandomQuorumMock; + let fetchQuorumMembersMock; + + beforeEach(function beforeEach() { + coreHeight = 42; + + validatorSetLLMQType = 4; + + quorumEntry = new QuorumEntry(getSmlFixture()[0].newQuorums[0]); + + smlDiffMock = { + blockHash: 'some block hash', + }; + + smlMock = { + getQuorum: this.sinon.stub().returns(quorumEntry), + toSimplifiedMNListDiff: this.sinon.stub().returns(smlDiffMock), + getQuorumsOfType: this.sinon.stub().returns( + getSmlFixture()[0].newQuorums.filter((quorum) => quorum.llmqType === 1), + ), + getValidMasternodesList: this.sinon.stub().returns([ + new SimplifiedMNListEntry({ + proRegTxHash: 'c286807d463b06c7aba3b9a60acf64c1fc03da8c1422005cd9b4293f08cf0562', + confirmedHash: '4eb56228c535db3b234907113fd41d57bcc7cdcb8e0e00e57590af27ee88c119', + service: '192.168.65.2:20101', + pubKeyOperator: '809519c5f6f3be1c08782ac42ae9a83b6c7205eba43f9a96a4f032ec7a73f1a7c25fa78cce0d6d9c135f7e2c28527179', + votingAddress: 'yXmprXYP51uzfMyndtWwxz96MnkCKkFc9x', + isValid: true, + }), + new SimplifiedMNListEntry({ + proRegTxHash: 'a3e1edc6bd352eeaf0ae58e30781ef4b127854241a3fe7fddf36d5b7e1dc2b3f', + confirmedHash: '27a0b637b56af038c45e2fd1f06c2401c8dadfa28ca5e0d19ca836cc984a8378', + service: '192.168.65.2:20201', + pubKeyOperator: '987a4873caba62cd45a2f7d4aa6d94519ee6753e9bef777c927cb94ade768a542b0ff34a93231d3a92b4e75ffdaa366e', + votingAddress: 'ycL7L4mhYoaZdm9TH85svvpfeKtdfo249u', + isValid: true, + }), + ]), + }; + + smlStoreMock = { + getSMLbyHeight: this.sinon.stub().returns(smlMock), + getCurrentSML: this.sinon.stub().returns(smlMock), + }; + + simplifiedMasternodeListMock = { + getStore: this.sinon.stub().returns(smlStoreMock), + }; + + rotationEntropy = Buffer.from('00000ac05a06682172d8b49be7c9ddc4189126d7200ebf0fc074c433ae74b596', 'hex'); + + quorumMembers = [ + { + proTxHash: 'c286807d463b06c7aba3b9a60acf64c1fc03da8c1422005cd9b4293f08cf0562', + pubKeyOperator: '06abc1c890c9da4e513d52f20da1882228bfa2db4bb29cbd064e1b2a61d9dcdadcf0784fd1371338c8ad1bf323d87ae6', + valid: true, + pubKeyShare: '00d7bb8d6753865c367824691610dcc313b661b7e024e36e82f8af33f5701caddb2668dadd1e647d8d7d5b30e37ebbcf', + }, + { + proTxHash: 'a3e1edc6bd352eeaf0ae58e30781ef4b127854241a3fe7fddf36d5b7e1dc2b3f', + pubKeyOperator: '04d748ba0efeb7a8f8548e0c22b4c188c293a19837a1c5440649279ba73ead0c62ac1e840050a10a35e0ae05659d2a8d', + valid: true, + pubKeyShare: '86d0992f5c73b8f57101c34a0c4ebb17d962bb935a738c1ef1e2bb1c25034d8e4a0a2cc96e0ebc69a7bf3b8b67b2de5f', + }, + { + proTxHash: 'a3e1edc6bd352eeaf0ae58e30781ef4b127854241a3fe7fddf36d5b7e1dc2b3f', + pubKeyOperator: '04d748ba0efeb7a8f8548e0c22b4c188c293a19837a1c5440649279ba73ead0c62ac1e840050a10a35e0ae05659d2a8d', + valid: false, + }, + ]; + + getRandomQuorumMock = this.sinon.stub().resolves(quorumEntry); + + fetchQuorumMembersMock = this.sinon.stub().resolves(quorumMembers); + + const notMasternodeError = new Error(); + notMasternodeError.code = -32603; + + coreRpcClientMock = { + masternode: this.sinon.stub().throws(notMasternodeError), + }; + + validatorNetworkPort = 26656; + + validatorSet = new ValidatorSet( + simplifiedMasternodeListMock, + getRandomQuorumMock, + fetchQuorumMembersMock, + validatorSetLLMQType, + coreRpcClientMock, + validatorNetworkPort, + ); + }); + + describe('initialize', () => { + it('should initialize with specified core height', async () => { + await validatorSet.initialize(coreHeight); + + expect(smlStoreMock.getSMLbyHeight).to.be.calledOnceWithExactly(coreHeight); + + expect(getRandomQuorumMock).to.be.calledOnceWithExactly( + smlMock, + validatorSetLLMQType, + Buffer.from(smlDiffMock.blockHash, 'hex'), + ); + + expect(fetchQuorumMembersMock).to.be.calledOnceWithExactly( + validatorSetLLMQType, + quorumEntry.quorumHash, + ); + + expect(smlStoreMock.getCurrentSML().getValidMasternodesList).to.be.calledOnce(); + }); + + it('should throw an error if the node is a quorum member and doesn\'t receive public key shares', async () => { + coreRpcClientMock.masternode.resolves({ + result: { + proTxHash: quorumMembers[0].proTxHash, + }, + }); + + quorumMembers[2].valid = true; + + try { + await validatorSet.initialize(coreHeight); + + expect.fail('should throw PublicKeyShareIsNotPresentError'); + } catch (e) { + expect(e).to.be.instanceOf(PublicKeyShareIsNotPresentError); + expect(e.getMember()).to.be.equal(quorumMembers[2]); + } + }); + }); + + describe('rotate', () => { + it('should rotate validator set with specified core height and entropy if height divisible by ROTATION_BLOCK_INTERVAL', async () => { + const height = Long.fromInt(ValidatorSet.ROTATION_BLOCK_INTERVAL); + + const result = await validatorSet.rotate( + height, + coreHeight, + rotationEntropy, + ); + + expect(result).to.be.true(); + + expect(smlStoreMock.getSMLbyHeight).to.be.calledOnceWithExactly(coreHeight); + + expect(getRandomQuorumMock).to.be.calledOnceWithExactly( + smlMock, + validatorSetLLMQType, + rotationEntropy, + ); + + expect(fetchQuorumMembersMock).to.be.calledOnceWithExactly( + validatorSetLLMQType, + quorumEntry.quorumHash, + ); + + expect(smlStoreMock.getCurrentSML().getValidMasternodesList).to.be.calledOnce(); + }); + + it('should not rotate validator set if height not divisible by ROTATION_BLOCK_INTERVAL', async () => { + const height = Long.fromInt(42); + + const result = await validatorSet.rotate( + height, + coreHeight, + rotationEntropy, + ); + + expect(result).to.be.false(); + + expect(smlStoreMock.getSMLbyHeight).to.be.calledOnceWithExactly(coreHeight); + + expect(getRandomQuorumMock).to.not.be.called(); + + expect(fetchQuorumMembersMock).to.not.be.called(); + }); + }); + + describe('getQuorum', () => { + it('should return QuorumEntry', async () => { + await validatorSet.initialize(coreHeight); + + const result = validatorSet.getQuorum(); + + expect(result).to.equals(quorumEntry); + }); + + it('should thrown an error if ValidatorSet is not initialized', () => { + try { + validatorSet.getQuorum(); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(ValidatorSetIsNotInitializedError); + } + }); + }); + + describe('getValidators', () => { + it('should return array of validators', async () => { + await validatorSet.initialize(coreHeight); + + const result = validatorSet.getValidators(); + + expect(result).to.have.lengthOf(2); + expect(result[0]).to.be.instanceOf(Validator); + expect(result[1]).to.be.instanceOf(Validator); + }); + + it('should thrown an error if ValidatorSet is not initialized', () => { + try { + validatorSet.getValidators(); + + expect.fail('should throw an error'); + } catch (e) { + expect(e).to.be.instanceOf(ValidatorSetIsNotInitializedError); + } + }); + }); +}); diff --git a/packages/js-grpc-common/.eslintrc b/packages/js-grpc-common/.eslintrc new file mode 100644 index 00000000000..d94c110f931 --- /dev/null +++ b/packages/js-grpc-common/.eslintrc @@ -0,0 +1,17 @@ +{ + "extends": "airbnb-base", + "rules": { + "import/no-extraneous-dependencies": ["error", { "packageDir": "." }], + "no-plusplus": 0, + "eol-last": [ + "error", + "always" + ], + "class-methods-use-this": "off", + "curly": [ + "error", + "all" + ] + } + } + \ No newline at end of file diff --git a/packages/js-grpc-common/.mocharc.yml b/packages/js-grpc-common/.mocharc.yml new file mode 100644 index 00000000000..1f6e57d579e --- /dev/null +++ b/packages/js-grpc-common/.mocharc.yml @@ -0,0 +1,3 @@ +file: + - lib/test/bootstrap.js +recursive: true diff --git a/packages/js-grpc-common/LICENSE b/packages/js-grpc-common/LICENSE new file mode 100644 index 00000000000..f735c60619c --- /dev/null +++ b/packages/js-grpc-common/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017-2019 Dash Core Group, Inc. + +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/packages/js-grpc-common/README.md b/packages/js-grpc-common/README.md new file mode 100644 index 00000000000..19fed15e689 --- /dev/null +++ b/packages/js-grpc-common/README.md @@ -0,0 +1,3 @@ +# js-grpc-common + +Common JavaScript GRPC code and utils for platform projects. \ No newline at end of file diff --git a/packages/js-grpc-common/index.js b/packages/js-grpc-common/index.js new file mode 100644 index 00000000000..ed85e5354ca --- /dev/null +++ b/packages/js-grpc-common/index.js @@ -0,0 +1,70 @@ +const jsonToProtobufFactory = require('./lib/client/converters/jsonToProtobufFactory'); +const protobufToJsonFactory = require('./lib/client/converters/protobufToJsonFactory'); +const jsonToProtobufInterceptorFactory = require( + './lib/client/interceptors/jsonToProtobufInterceptorFactory', +); +const protocolVersionInterceptorFactory = require( + './lib/client/interceptors/protocolVersionInterceptorFactory', +); + +const createServer = require('./lib/server/createServer'); +const jsonToProtobufHandlerWrapper = require( + './lib/server/jsonToProtobufHandlerWrapper', +); +const checkVersionWrapperFactory = require('./lib/server/checks/checkVersionWrapperFactory'); +const AcknowledgingWritable = require('./lib/server/stream/AcknowledgingWritable'); +const wrapInErrorHandlerFactory = require('./lib/server/error/wrapInErrorHandlerFactory'); + +const FailedPreconditionGrpcError = require('./lib/server/error/FailedPreconditionGrpcError'); +const InvalidArgumentGrpcError = require('./lib/server/error/InvalidArgumentGrpcError'); +const InternalGrpcError = require('./lib/server/error/InternalGrpcError'); +const ResourceExhaustedGrpcError = require('./lib/server/error/ResourceExhaustedGrpcError'); +const DeadlineExceededGrpcError = require('./lib/server/error/DeadlineExceededGrpcError'); +const NotFoundGrpcError = require('./lib/server/error/NotFoundGrpcError'); +const UnavailableGrpcError = require('./lib/server/error/UnavailableGrpcError'); +const AlreadyExistsGrpcError = require('./lib/server/error/AlreadyExistsGrpcError'); +const GrpcError = require('./lib/server/error/GrpcError'); + +const isObject = require('./lib/utils/isObject'); +const convertObjectToMetadata = require('./lib/convertObjectToMetadata'); +const loadPackageDefinition = require('./lib/loadPackageDefinition'); + +module.exports = { + loadPackageDefinition, + convertObjectToMetadata, + client: { + converters: { + jsonToProtobufFactory, + protobufToJsonFactory, + }, + interceptors: { + jsonToProtobufInterceptorFactory, + protocolVersionInterceptorFactory, + }, + }, + server: { + createServer, + jsonToProtobufHandlerWrapper, + stream: { + AcknowledgingWritable, + }, + error: { + wrapInErrorHandlerFactory, + GrpcError, + InternalGrpcError, + InvalidArgumentGrpcError, + FailedPreconditionGrpcError, + ResourceExhaustedGrpcError, + DeadlineExceededGrpcError, + NotFoundGrpcError, + UnavailableGrpcError, + AlreadyExistsGrpcError, + }, + checks: { + checkVersionWrapperFactory, + }, + }, + utils: { + isObject, + }, +}; diff --git a/packages/js-grpc-common/lib/client/converters/jsonToProtobufFactory.js b/packages/js-grpc-common/lib/client/converters/jsonToProtobufFactory.js new file mode 100644 index 00000000000..98b041e79e3 --- /dev/null +++ b/packages/js-grpc-common/lib/client/converters/jsonToProtobufFactory.js @@ -0,0 +1,31 @@ +/** + * Convert snake cased json object to protobuf message (factory) + * + * @param ProtocMessageClass + * @param PBJSMessageClass + * + * @returns {jsonToProtobuf} + */ +function jsonToProtobufFactory(ProtocMessageClass, PBJSMessageClass) { + /** + * Convert snake cased json object to protobuf message + * + * @typedef jsonToProtobuf + * + * @param {Object} object + * + * @returns {*} + */ + function jsonToProtobuf(object) { + const grpcMessage = PBJSMessageClass.fromObject(object); + const grpcMessageBinary = PBJSMessageClass + .encode(grpcMessage) + .finish(); + + return ProtocMessageClass.deserializeBinary(grpcMessageBinary); + } + + return jsonToProtobuf; +} + +module.exports = jsonToProtobufFactory; diff --git a/packages/js-grpc-common/lib/client/converters/protobufToJsonFactory.js b/packages/js-grpc-common/lib/client/converters/protobufToJsonFactory.js new file mode 100644 index 00000000000..85762096e4a --- /dev/null +++ b/packages/js-grpc-common/lib/client/converters/protobufToJsonFactory.js @@ -0,0 +1,29 @@ +/** + * Converts protobuf message to a JSON (factory) + * + * @param PBJSMessageClass + * + * @returns {Object} + */ +function protobufToJsonFactory(PBJSMessageClass) { + /** + * Converts protobuf message to a JSON + * + * @typedef protobufToJson + * + * @param message + * + * @returns {Object} + */ + function protobufToJson(message) { + const messageBinary = message.serializeBinary(); + const grpcMessage = PBJSMessageClass + .decode(messageBinary); + + return PBJSMessageClass.toObject(grpcMessage); + } + + return protobufToJson; +} + +module.exports = protobufToJsonFactory; diff --git a/packages/js-grpc-common/lib/client/interceptors/jsonToProtobufInterceptorFactory.js b/packages/js-grpc-common/lib/client/interceptors/jsonToProtobufInterceptorFactory.js new file mode 100644 index 00000000000..f8911a3aed7 --- /dev/null +++ b/packages/js-grpc-common/lib/client/interceptors/jsonToProtobufInterceptorFactory.js @@ -0,0 +1,45 @@ +const grpc = require('@grpc/grpc-js'); + +const { InterceptingCall } = grpc; + +/** + * Client-side JSON -> protobuf -> JSON interceptor (factory) + * + * @param {jsonToProtobuf} jsonToProtobuf + * @param {protobufToJson} protobufToJson + * + * @returns {conversionInterceptor} + */ +function jsonToProtobufInterceptorFactory(jsonToProtobuf, protobufToJson) { + /** + * Client-side JSON -> protobuf -> JSON interceptor + * + * @param {Object} options + * @param {module:grpc.InterceptingCall} nextCall + * + * @returns {module:grpc.InterceptingCall} + */ + function conversionInterceptor(options, nextCall) { + const methods = { + start(metadata, listener, nextStart) { + nextStart(metadata, { + onReceiveMessage(jsonResponse, next) { + if (!jsonResponse) { + return next(); + } + + return next(jsonToProtobuf(jsonResponse)); + }, + }); + }, + sendMessage(message, next) { + next(protobufToJson(message)); + }, + }; + return new InterceptingCall(nextCall(options), methods); + } + + return conversionInterceptor; +} + +module.exports = jsonToProtobufInterceptorFactory; diff --git a/packages/js-grpc-common/lib/client/interceptors/protocolVersionInterceptorFactory.js b/packages/js-grpc-common/lib/client/interceptors/protocolVersionInterceptorFactory.js new file mode 100644 index 00000000000..759986835b7 --- /dev/null +++ b/packages/js-grpc-common/lib/client/interceptors/protocolVersionInterceptorFactory.js @@ -0,0 +1,81 @@ +const semver = require('semver'); + +const { InterceptingCall } = require('@grpc/grpc-js'); + +const VersionMismatchGrpcError = require('../../server/error/VersionMismatchGrpcError'); + +const { + convertVersionToInt32, + convertInt32VersionToString, +} = require('../../utils/semanticVersioningConversion'); + +/** + * Client-side `add protocol version` interceptor (factory) + * + * @param {string} clientProtocolVersionString + * + * @returns {protocolVersionInterceptor} + */ +function protocolVersionInterceptorFactory(clientProtocolVersionString) { + /** + * Client-side `add protocol version` interceptor + * + * @typedef protocolVersionInterceptor + * + * @param {Object} options + * @param {module:grpc.InterceptingCall} nextCall + * + * @return {module:grpc.InterceptingCall} + */ + function protocolVersionInterceptor(options, nextCall) { + const methods = { + start(metadata, listener, next) { + const clientProtocolVersionNumber = convertVersionToInt32( + clientProtocolVersionString, + ); + + metadata.set( + 'protocolVersion', clientProtocolVersionNumber, + ); + + next(metadata, { + onReceiveMetadata: (receivedMetadata, onReceiveMetadataNext) => { + const [serverProtocolVersionFromMeta] = receivedMetadata.get('protocolVersion'); + + if (!serverProtocolVersionFromMeta) { + throw new VersionMismatchGrpcError({ + clientVersion: clientProtocolVersionNumber, + serverVersion: null, + }); + } + + const serverProtocolVersionNumber = parseInt(serverProtocolVersionFromMeta, 10); + const serverProtocolVersionString = convertInt32VersionToString( + serverProtocolVersionNumber, + ); + + const clientVersion = semver.coerce(clientProtocolVersionString); + const serverVersion = semver.coerce(serverProtocolVersionString); + + const majorMismatch = clientVersion.major !== serverVersion.major; + const minorMismatch = clientVersion.minor !== serverVersion.minor; + + if (majorMismatch || minorMismatch) { + throw new VersionMismatchGrpcError({ + clientVersion: clientProtocolVersionNumber, + serverVersion: serverProtocolVersionNumber, + }); + } + + onReceiveMetadataNext(receivedMetadata); + }, + }); + }, + }; + return new InterceptingCall(nextCall(options), methods); + } + + return protocolVersionInterceptor; +} + +module.exports = protocolVersionInterceptorFactory; diff --git a/packages/js-grpc-common/lib/convertObjectToMetadata.js b/packages/js-grpc-common/lib/convertObjectToMetadata.js new file mode 100644 index 00000000000..900edbe9051 --- /dev/null +++ b/packages/js-grpc-common/lib/convertObjectToMetadata.js @@ -0,0 +1,21 @@ +// Import metadata directly to do not import Node.JS server logic in browsers +const { Metadata } = require('@grpc/grpc-js/build/src/metadata'); + +/** + * Converts any JavaScript object to grpc metadata + * + * @param {Object} obj + * + * @return {module:grpc.Metadata} + */ +function convertObjectToMetadata(obj) { + const metadata = new Metadata(); + + Object.keys(obj).forEach((key) => { + metadata.set(key, obj[key]); + }); + + return metadata; +} + +module.exports = convertObjectToMetadata; diff --git a/packages/js-grpc-common/lib/loadPackageDefinition.js b/packages/js-grpc-common/lib/loadPackageDefinition.js new file mode 100644 index 00000000000..c90c34b18bb --- /dev/null +++ b/packages/js-grpc-common/lib/loadPackageDefinition.js @@ -0,0 +1,32 @@ +const grpc = require('@grpc/grpc-js'); +const protoLoader = require('@grpc/proto-loader'); + +const lodashGet = require('lodash.get'); + +/** + * Load GRPC package definition + * + * @param {string} protoPath + * @param {string} [namespace] + * + * @return {*} + */ +function loadPackageDefinition(protoPath, namespace = undefined) { + const definition = protoLoader.loadSync(protoPath, { + keepCase: false, + longs: String, + enums: String, + bytes: Uint8Array, + defaults: true, + }); + + const packageDefinition = grpc.loadPackageDefinition(definition); + + if (namespace) { + return lodashGet(packageDefinition, namespace); + } + + return packageDefinition; +} + +module.exports = loadPackageDefinition; diff --git a/packages/js-grpc-common/lib/server/checks/checkVersionWrapperFactory.js b/packages/js-grpc-common/lib/server/checks/checkVersionWrapperFactory.js new file mode 100644 index 00000000000..3560910fcd0 --- /dev/null +++ b/packages/js-grpc-common/lib/server/checks/checkVersionWrapperFactory.js @@ -0,0 +1,88 @@ +const semver = require('semver'); + +const { Metadata } = require('@grpc/grpc-js'); + +const VersionMismatchGrpcError = require('../error/VersionMismatchGrpcError'); + +const { + convertVersionToInt32, + convertInt32VersionToString, +} = require('../../utils/semanticVersioningConversion'); + +/** + * Check version wrapper hanldler (factory) + * + * @param {string} serverProtocolVersionString + * + * @returns {checkVersionWrapper} + */ +function checkVersionWrapperFactory(serverProtocolVersionString) { + /* Prepare server metadata on factory call */ + const serverProtocolVersionNumber = convertVersionToInt32( + serverProtocolVersionString, + ); + + const serverMetadata = new Metadata(); + serverMetadata.set( + 'protocolVersion', serverProtocolVersionNumber, + ); + + /** + * + * @typedef checkVersionWrapper + * + * @param {Function(grpc.ServerWriteableStream, Function)} method + * + * @returns {internalHandler} + */ + function checkVersionWrapper(method) { + /** + * @typedef internalHandler + * + * @param {grpc.ServerWriteableStream} call + * @param {Function(Error|null, *|null)} [callback=undefined] + * + * @returns {Promise} + */ + async function handler(call, callback = undefined) { + await call.sendMetadata(serverMetadata); + + const { metadata } = call; + + if (!metadata || metadata.get('protocolVersion').length === 0) { + throw new VersionMismatchGrpcError({ + clientVersion: null, + serverVersion: serverProtocolVersionNumber, + }); + } + + const [clientProtocolVersionFromMeta] = metadata.get('protocolVersion'); + + const clientProtocolVersionNumber = parseInt(clientProtocolVersionFromMeta, 10); + const clientProtocolVersionString = convertInt32VersionToString( + clientProtocolVersionNumber, + ); + + const clientVersion = semver.coerce(clientProtocolVersionString); + const serverVersion = semver.coerce(serverProtocolVersionString); + + const majorMismatch = clientVersion.major !== serverVersion.major; + const minorMismatch = clientVersion.minor !== serverVersion.minor; + + if (majorMismatch || minorMismatch) { + throw new VersionMismatchGrpcError({ + clientVersion: clientProtocolVersionNumber, + serverVersion: serverProtocolVersionNumber, + }); + } + + return method(call, callback); + } + + return handler; + } + + return checkVersionWrapper; +} + +module.exports = checkVersionWrapperFactory; diff --git a/packages/js-grpc-common/lib/server/createServer.js b/packages/js-grpc-common/lib/server/createServer.js new file mode 100644 index 00000000000..699ed9819cc --- /dev/null +++ b/packages/js-grpc-common/lib/server/createServer.js @@ -0,0 +1,20 @@ +const grpc = require('@grpc/grpc-js'); + +/** + * Create GRPC server + * + * @typedef createServer + * + * @param {Object} serviceDefinition + * @param {Object} handlers + * + * @return {module:grpc.Server} + */ +function createServer(serviceDefinition, handlers) { + const server = new grpc.Server(); + server.addService(serviceDefinition.service, handlers); + + return server; +} + +module.exports = createServer; diff --git a/packages/js-grpc-common/lib/server/error/AlreadyExistsGrpcError.js b/packages/js-grpc-common/lib/server/error/AlreadyExistsGrpcError.js new file mode 100644 index 00000000000..6ee1b84e038 --- /dev/null +++ b/packages/js-grpc-common/lib/server/error/AlreadyExistsGrpcError.js @@ -0,0 +1,14 @@ +const GrpcError = require('./GrpcError'); +const GrpcErrorCodes = require('./GrpcErrorCodes'); + +class AlreadyExistsGrpcError extends GrpcError { + /** + * @param {string} message + * @param {Object} [metadata] + */ + constructor(message, metadata = undefined) { + super(GrpcErrorCodes.ALREADY_EXISTS, message, metadata); + } +} + +module.exports = AlreadyExistsGrpcError; diff --git a/packages/js-grpc-common/lib/server/error/DeadlineExceededGrpcError.js b/packages/js-grpc-common/lib/server/error/DeadlineExceededGrpcError.js new file mode 100644 index 00000000000..a29d5a9f032 --- /dev/null +++ b/packages/js-grpc-common/lib/server/error/DeadlineExceededGrpcError.js @@ -0,0 +1,14 @@ +const GrpcError = require('./GrpcError'); +const GrpcErrorCodes = require('./GrpcErrorCodes'); + +class DeadlineExceededGrpcError extends GrpcError { + /** + * @param {string} message + * @param {Object} [metadata] + */ + constructor(message, metadata = undefined) { + super(GrpcErrorCodes.DEADLINE_EXCEEDED, message, metadata); + } +} + +module.exports = DeadlineExceededGrpcError; diff --git a/packages/js-grpc-common/lib/server/error/FailedPreconditionGrpcError.js b/packages/js-grpc-common/lib/server/error/FailedPreconditionGrpcError.js new file mode 100644 index 00000000000..bc2a17f93fa --- /dev/null +++ b/packages/js-grpc-common/lib/server/error/FailedPreconditionGrpcError.js @@ -0,0 +1,14 @@ +const GrpcError = require('./GrpcError'); +const GrpcErrorCodes = require('./GrpcErrorCodes'); + +class FailedPreconditionGrpcError extends GrpcError { + /** + * @param {string} message + * @param {Object} [metadata] + */ + constructor(message, metadata = undefined) { + super(GrpcErrorCodes.FAILED_PRECONDITION, message, metadata); + } +} + +module.exports = FailedPreconditionGrpcError; diff --git a/packages/js-grpc-common/lib/server/error/GrpcError.js b/packages/js-grpc-common/lib/server/error/GrpcError.js new file mode 100644 index 00000000000..d9a7e228516 --- /dev/null +++ b/packages/js-grpc-common/lib/server/error/GrpcError.js @@ -0,0 +1,73 @@ +const convertObjectToMetadata = require('../../convertObjectToMetadata'); + +class GrpcError extends Error { + /** + * @param {string} message + * @param {number} code + * @param {Object} [rawMetadata] + */ + constructor(code, message, rawMetadata = undefined) { + super(message); + + this.code = code; + + if (rawMetadata) { + this.metadata = convertObjectToMetadata(rawMetadata); + } + + this.rawMetadata = rawMetadata; + } + + /** + * Get message + * + * @return {string} + */ + getMessage() { + return this.message; + } + + /** + * Get error code + * + * @return {number} + */ + getCode() { + return this.code; + } + + /** + * Get metadata + * + * @return {Object} + */ + getRawMetadata() { + return this.rawMetadata; + } + + /** + * + * @param {Object} rawMetadata + * @return {GrpcError} + */ + setRawMetadata(rawMetadata) { + this.metadata = convertObjectToMetadata(rawMetadata); + + this.rawMetadata = rawMetadata; + + return this; + } + + /** + * + * @param {string} message + * @return {GrpcError} + */ + setMessage(message) { + this.message = message; + + return this; + } +} + +module.exports = GrpcError; diff --git a/packages/js-grpc-common/lib/server/error/GrpcErrorCodes.js b/packages/js-grpc-common/lib/server/error/GrpcErrorCodes.js new file mode 100644 index 00000000000..2baa63eaf19 --- /dev/null +++ b/packages/js-grpc-common/lib/server/error/GrpcErrorCodes.js @@ -0,0 +1,19 @@ +module.exports = { + CANCELLED: 1, + UNKNOWN: 2, + INVALID_ARGUMENT: 3, + DEADLINE_EXCEEDED: 4, + NOT_FOUND: 5, + ALREADY_EXISTS: 6, + PERMISSION_DENIED: 7, + RESOURCE_EXHAUSTED: 8, + FAILED_PRECONDITION: 9, + ABORTED: 10, + OUT_OF_RANGE: 11, + UNIMPLEMENTED: 12, + INTERNAL: 13, + UNAVAILABLE: 14, + DATA_LOSS: 15, + UNAUTHENTICATED: 16, + VERSION_MISMATCH: 100, +}; diff --git a/packages/js-grpc-common/lib/server/error/InternalGrpcError.js b/packages/js-grpc-common/lib/server/error/InternalGrpcError.js new file mode 100644 index 00000000000..9c69ce51221 --- /dev/null +++ b/packages/js-grpc-common/lib/server/error/InternalGrpcError.js @@ -0,0 +1,25 @@ +const GrpcError = require('./GrpcError'); +const GrpcErrorCodes = require('./GrpcErrorCodes'); + +class InternalGrpcError extends GrpcError { + /** + * @param {Error} error + * @param {Object} [metadata] + */ + constructor(error, metadata = undefined) { + super(GrpcErrorCodes.INTERNAL, 'Internal error', metadata); + + this.error = error; + } + + /** + * Get error + * + * @return {Error} + */ + getError() { + return this.error; + } +} + +module.exports = InternalGrpcError; diff --git a/packages/js-grpc-common/lib/server/error/InvalidArgumentGrpcError.js b/packages/js-grpc-common/lib/server/error/InvalidArgumentGrpcError.js new file mode 100644 index 00000000000..8a05956f82e --- /dev/null +++ b/packages/js-grpc-common/lib/server/error/InvalidArgumentGrpcError.js @@ -0,0 +1,14 @@ +const GrpcError = require('./GrpcError'); +const GrpcErrorCodes = require('./GrpcErrorCodes'); + +class InvalidArgumentGrpcError extends GrpcError { + /** + * @param {string} message + * @param {Object} [metadata] + */ + constructor(message, metadata = undefined) { + super(GrpcErrorCodes.INVALID_ARGUMENT, message, metadata); + } +} + +module.exports = InvalidArgumentGrpcError; diff --git a/packages/js-grpc-common/lib/server/error/NotFoundGrpcError.js b/packages/js-grpc-common/lib/server/error/NotFoundGrpcError.js new file mode 100644 index 00000000000..06250afe0df --- /dev/null +++ b/packages/js-grpc-common/lib/server/error/NotFoundGrpcError.js @@ -0,0 +1,14 @@ +const GrpcError = require('./GrpcError'); +const GrpcErrorCodes = require('./GrpcErrorCodes'); + +class NotFoundGrpcError extends GrpcError { + /** + * @param {string} message + * @param {Object} [metadata] + */ + constructor(message, metadata = undefined) { + super(GrpcErrorCodes.NOT_FOUND, message, metadata); + } +} + +module.exports = NotFoundGrpcError; diff --git a/packages/js-grpc-common/lib/server/error/ResourceExhaustedGrpcError.js b/packages/js-grpc-common/lib/server/error/ResourceExhaustedGrpcError.js new file mode 100644 index 00000000000..d2bb41e7614 --- /dev/null +++ b/packages/js-grpc-common/lib/server/error/ResourceExhaustedGrpcError.js @@ -0,0 +1,14 @@ +const GrpcError = require('./GrpcError'); +const GrpcErrorCodes = require('./GrpcErrorCodes'); + +class ResourceExhaustedGrpcError extends GrpcError { + /** + * @param {string} message + * @param {Object} [metadata] + */ + constructor(message, metadata = undefined) { + super(GrpcErrorCodes.RESOURCE_EXHAUSTED, message, metadata); + } +} + +module.exports = ResourceExhaustedGrpcError; diff --git a/packages/js-grpc-common/lib/server/error/UnavailableGrpcError.js b/packages/js-grpc-common/lib/server/error/UnavailableGrpcError.js new file mode 100644 index 00000000000..e4d6c65c0f4 --- /dev/null +++ b/packages/js-grpc-common/lib/server/error/UnavailableGrpcError.js @@ -0,0 +1,14 @@ +const GrpcError = require('./GrpcError'); +const GrpcErrorCodes = require('./GrpcErrorCodes'); + +class UnavailableGrpcError extends GrpcError { + /** + * @param {string} message + * @param {Object} [metadata] + */ + constructor(message, metadata = undefined) { + super(GrpcErrorCodes.UNAVAILABLE, message, metadata); + } +} + +module.exports = UnavailableGrpcError; diff --git a/packages/js-grpc-common/lib/server/error/VerboseInternalGrpcError.js b/packages/js-grpc-common/lib/server/error/VerboseInternalGrpcError.js new file mode 100644 index 00000000000..02e247de018 --- /dev/null +++ b/packages/js-grpc-common/lib/server/error/VerboseInternalGrpcError.js @@ -0,0 +1,32 @@ +const cbor = require('cbor'); + +const InternalGrpcError = require('./InternalGrpcError'); + +class VerboseInternalGrpcError extends InternalGrpcError { + /** + * + * @param {InternalGrpcError} error + */ + constructor(error) { + const originalError = error.getError(); + let [, errorPath] = originalError.stack.toString().split(/\r\n|\n/); + + if (!errorPath) { + errorPath = originalError.stack; + } + + const message = `${originalError.message} ${errorPath.trim()}`; + + const rawMetadata = error.getRawMetadata() || {}; + rawMetadata['stack-bin'] = cbor.encode(originalError.stack); + + super( + originalError, + rawMetadata, + ); + + this.setMessage(message); + } +} + +module.exports = VerboseInternalGrpcError; diff --git a/packages/js-grpc-common/lib/server/error/VersionMismatchGrpcError.js b/packages/js-grpc-common/lib/server/error/VersionMismatchGrpcError.js new file mode 100644 index 00000000000..eb4533b6921 --- /dev/null +++ b/packages/js-grpc-common/lib/server/error/VersionMismatchGrpcError.js @@ -0,0 +1,17 @@ +const GrpcError = require('./GrpcError'); +const GrpcErrorCodes = require('./GrpcErrorCodes'); + +class VersionMismatchGrpcError extends GrpcError { + /** + * @param {Object} [metadata] + */ + constructor(metadata = undefined) { + super( + GrpcErrorCodes.VERSION_MISMATCH, + 'client and server versions mismatch', + metadata, + ); + } +} + +module.exports = VersionMismatchGrpcError; diff --git a/packages/js-grpc-common/lib/server/error/wrapInErrorHandlerFactory.js b/packages/js-grpc-common/lib/server/error/wrapInErrorHandlerFactory.js new file mode 100644 index 00000000000..5ef7b9bc69c --- /dev/null +++ b/packages/js-grpc-common/lib/server/error/wrapInErrorHandlerFactory.js @@ -0,0 +1,58 @@ +const GrpcError = require('./GrpcError'); +const InternalGrpcError = require('./InternalGrpcError'); +const VerboseInternalGrpcError = require('./VerboseInternalGrpcError'); + +/** + * @param {Object} logger + * @param {boolean=true} isProductionEnvironment + * @return wrapInErrorHandler + */ +module.exports = function wrapInErrorHandlerFactory(logger, isProductionEnvironment) { + /** + * Wrap RPC method in error handler + * + * @typedef wrapInErrorHandler + * @param {Function} method RPC method + * @return {Function} + */ + function wrapInErrorHandler(method) { + /** + * @param {grpc.ServerWriteableStream} call + * @param {function(Error, *)} [callback] + */ + async function rpcMethodErrorHandler(call, callback = undefined) { + try { + const result = await method(call); + + if (callback) { + callback(null, result); + } + } catch (e) { + let error = e; + + // Wrap all non GRPC errors to an internal GRPC error + if (!(e instanceof GrpcError)) { + error = new InternalGrpcError(e); + } + + // Log only internal GRPC errors + if (error instanceof InternalGrpcError) { + logger.error(error.getError()); + + if (!isProductionEnvironment) { + error = new VerboseInternalGrpcError(error); + } + } + + if (callback) { + callback(error, null); + } else { + call.destroy(error); + } + } + } + return rpcMethodErrorHandler; + } + + return wrapInErrorHandler; +}; diff --git a/packages/js-grpc-common/lib/server/jsonToProtobufHandlerWrapper.js b/packages/js-grpc-common/lib/server/jsonToProtobufHandlerWrapper.js new file mode 100644 index 00000000000..1cc46283b64 --- /dev/null +++ b/packages/js-grpc-common/lib/server/jsonToProtobufHandlerWrapper.js @@ -0,0 +1,70 @@ +/** + * Server-side JSON -> protobuf -> JSON handler wrapper (factory) + * + * @param {jsonToProtobuf} jsonToProtobuf + * @param {protobufToJson} protobufToJson + * @param {function(grpc.Call, function(Error|null, jspb.Message|null))} rpcMethod + * + * @returns {wrappedMethodHandler} + */ +function jsonToProtobufHandlerWrapper(jsonToProtobuf, protobufToJson, rpcMethod) { + /** + * Decorate `request` and `write` + * + * @param {grpc.Call} call + * + * @returns {grpc.Call} + */ + function decorateCall(call) { + return new Proxy(call, { + get(target, propKey) { + if (propKey === 'request') { + return jsonToProtobuf(target[propKey]); + } + + if (propKey === 'write') { + return (message, flags, writeCallback) => { + let convertedMessage = null; + if (message) { + convertedMessage = protobufToJson(message); + } + return call.write(convertedMessage, flags, writeCallback); + }; + } + + return target[propKey]; + }, + }); + } + + /** + * Server-side JSON -> protobuf -> JSON handler wrapper + * + * @typedef wrappedMethodHandler + * + * @param {grpc.Call} call + * @param {function(Error|null, jspb.Message|null, grpc.Metadata|null)} callback + * + * @returns {*} + */ + function methodHandler(call, callback = undefined) { + const proxyCall = decorateCall(call); + + let interceptedCallback; + if (callback) { + interceptedCallback = (err, message, metadata) => { + let convertedMessage = null; + if (message) { + convertedMessage = protobufToJson(message); + } + callback(err, convertedMessage, metadata); + }; + } + + return rpcMethod(proxyCall, interceptedCallback); + } + + return methodHandler; +} + +module.exports = jsonToProtobufHandlerWrapper; diff --git a/packages/js-grpc-common/lib/server/stream/AcknowledgingWritable.js b/packages/js-grpc-common/lib/server/stream/AcknowledgingWritable.js new file mode 100644 index 00000000000..ff3a1608a18 --- /dev/null +++ b/packages/js-grpc-common/lib/server/stream/AcknowledgingWritable.js @@ -0,0 +1,63 @@ +class AcknowledgingWritable { + /** + * @param {stream.Writable} writable + */ + constructor(writable) { + this.writable = writable; + } + + /** + * @param data + * @return {Promise} + */ + write(data) { + let handler; + return new Promise((resolve, reject) => { + const callback = (error) => { + if (error) { + return reject(error); + } + return resolve(true); + }; + handler = this.attachHandler(callback); + this.writable.write(data, handler); + }).finally(() => { + this.detachHandler(handler); + }); + } + + /** + * @private + * @param callback + * @return {handler} + */ + createHandler(callback) { + const handler = (error) => { + callback(error); + }; + return handler; + } + + /** + * @private + * @param {function} handler + */ + detachHandler(handler) { + this.writable.off('error', handler); + this.writable.off('drain', handler); + } + + /** + * @private + * @param {function} callback + * @return {handler} + */ + attachHandler(callback) { + const handler = this.createHandler(callback); + this.writable.once('error', handler); + this.writable.once('drain', handler); + return handler; + } +} + +module.exports = AcknowledgingWritable; diff --git a/packages/js-grpc-common/lib/test/.eslintrc b/packages/js-grpc-common/lib/test/.eslintrc new file mode 100644 index 00000000000..4c2b11fe817 --- /dev/null +++ b/packages/js-grpc-common/lib/test/.eslintrc @@ -0,0 +1,9 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + } +} diff --git a/packages/js-grpc-common/lib/test/bootstrap.js b/packages/js-grpc-common/lib/test/bootstrap.js new file mode 100644 index 00000000000..a7449ee8b4d --- /dev/null +++ b/packages/js-grpc-common/lib/test/bootstrap.js @@ -0,0 +1,25 @@ +const sinon = require('sinon'); + +const { use, expect } = require('chai'); + +const dirtyChai = require('dirty-chai'); +const sinonChai = require('sinon-chai'); +const chaiAsPromised = require('chai-as-promised'); + +use(dirtyChai); +use(sinonChai); +use(chaiAsPromised); + +beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + +afterEach(function afterEach() { + this.sinon.restore(); +}); + +global.expect = expect; diff --git a/packages/js-grpc-common/lib/test/fixture/example.proto b/packages/js-grpc-common/lib/test/fixture/example.proto new file mode 100644 index 00000000000..f3fa5aa0a1f --- /dev/null +++ b/packages/js-grpc-common/lib/test/fixture/example.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package org.dash.platform.example.v0; + +service Example { + rpc callExample (ExampleRequest) returns (ExampleResponse); +} + +message ExampleRequest { + uint32 value = 1; +} + +message ExampleResponse { + uint32 value = 1; +} + diff --git a/packages/js-grpc-common/lib/test/mock/WritableMock.js b/packages/js-grpc-common/lib/test/mock/WritableMock.js new file mode 100644 index 00000000000..738c5b9ca21 --- /dev/null +++ b/packages/js-grpc-common/lib/test/mock/WritableMock.js @@ -0,0 +1,38 @@ +const { Writable } = require('stream'); + +class WritableMock extends Writable { + constructor({ + throwInWrite, requireDrain, callWriteCallbackWithAnError, + fireOnErrorWithoutCallback, callCallback, + }) { + super(); + this.throwInWrite = throwInWrite; + this.requireDrain = requireDrain; + this.callWriteCallbackWithAnError = callWriteCallbackWithAnError; + this.fireOnErrorWithoutCallback = fireOnErrorWithoutCallback; + this.callCallback = callCallback; + } + + // eslint-disable-next-line no-underscore-dangle + _write(chunk, encoding, callback) { + if (this.fireOnErrorWithoutCallback) { + this.emit('error', new Error('Error event')); + return; + } + if (this.callWriteCallbackWithAnError) { + callback(new Error('Error from callback')); + return; + } + if (this.throwInWrite) { + throw new Error('Thrown error'); + } + if (this.callCallback) { + callback(); + } + if (this.requireDrain) { + this.emit('drain'); + } + } +} + +module.exports = WritableMock; diff --git a/packages/js-grpc-common/lib/utils/isObject.js b/packages/js-grpc-common/lib/utils/isObject.js new file mode 100644 index 00000000000..01f08dfde63 --- /dev/null +++ b/packages/js-grpc-common/lib/utils/isObject.js @@ -0,0 +1,12 @@ +/** + * Checks if argument is actually an object + * + * @param {*} object + * + * @return {boolean} + */ +function isObject(object) { + return (typeof object === 'object') && (object !== null); +} + +module.exports = isObject; diff --git a/packages/js-grpc-common/lib/utils/semanticVersioningConversion.js b/packages/js-grpc-common/lib/utils/semanticVersioningConversion.js new file mode 100644 index 00000000000..e6eaaee1070 --- /dev/null +++ b/packages/js-grpc-common/lib/utils/semanticVersioningConversion.js @@ -0,0 +1,56 @@ +/** + * Convert a sematic versioning string into an 32-bit integer. + * + * Make sure the input string is compatible with the standard found + * at semver.org. Since this only uses 10-bit per major/minor/patch version, + * the highest possible SemVer string would be 1023.1023.1023. + * + * @param {string} version SemVer string + * @return {number} Numeric version + */ +function convertVersionToInt32(version) { + // Split a given version string into three parts. + const parts = version.split('.'); + + // Check if we got exactly three parts, otherwise throw an error. + if (parts.length !== 3) { + throw new Error('Received invalid version string'); + } + + // Make sure that no part is larger than 1023 or else it + // won't fit into a 32-bit integer. + parts.forEach((part) => { + if (part >= 1024) { + throw new Error(`Version string invalid, ${part} is too large`); + } + }); + + // Let's create a new number which we will return later on + let numericVersion = 0; + // Shift all parts either 0, 10 or 20 bits to the left. + for (let i = 0; i < 3; i++) { + // eslint-disable-next-line no-bitwise + numericVersion |= parts[i] << i * 10; + } + + return numericVersion; +} + +/** + * Converts a 32-bit integer into a semantic versioning (SemVer) compatible string. + * + * @param {number} v Numeric version + * @return {string} SemVer string + */ +function convertInt32VersionToString(v) { + // Works by shifting the numeric version to the right and then masking it + // with 0b1111111111 (or 1023 in decimal). + + // eslint-disable-next-line no-bitwise, no-mixed-operators + return `${v & 1023}.${v >> 10 & 1023}.${v >> 20 & 1023}`; +} + +module.exports = { + convertVersionToInt32, + convertInt32VersionToString, +}; diff --git a/packages/js-grpc-common/package.json b/packages/js-grpc-common/package.json new file mode 100644 index 00000000000..8f0b3644960 --- /dev/null +++ b/packages/js-grpc-common/package.json @@ -0,0 +1,37 @@ +{ + "name": "@dashevo/grpc-common", + "version": "0.23.0-dev.4", + "description": "Common GRPC library", + "main": "index.js", + "scripts": { + "build": "", + "lint": "eslint .", + "test": "yarn run test:coverage", + "test:coverage": "nyc --check-coverage --stmts=95 --branch=95 --funcs=95 --lines=95 yarn run mocha 'test/unit/**/*.spec.js' 'test/integration/**/*.spec.js'", + "test:unit": "mocha './test/unit/**/*.spec.js'", + "test:integration": "mocha './test/integration/**/*.spec.js'" + }, + "license": "MIT", + "devDependencies": { + "chai": "^4.3.4", + "chai-as-promised": "^7.1.1", + "dirty-chai": "^2.0.1", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2", + "mocha": "^9.1.2", + "mocha-sinon": "^2.1.2", + "nyc": "^15.1.0", + "sinon": "^11.1.2", + "sinon-chai": "^3.7.0" + }, + "dependencies": { + "@dashevo/protobufjs": "6.10.5", + "@grpc/grpc-js": "^1.3.7", + "@grpc/proto-loader": "^0.5.2", + "cbor": "^8.0.0", + "lodash.get": "^4.4.2", + "long": "^5.2.0", + "semver": "^7.3.2" + } +} diff --git a/packages/js-grpc-common/test/.eslintrc b/packages/js-grpc-common/test/.eslintrc new file mode 100644 index 00000000000..720ced73852 --- /dev/null +++ b/packages/js-grpc-common/test/.eslintrc @@ -0,0 +1,12 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + }, + "globals": { + "expect": true + } +} diff --git a/packages/js-grpc-common/test/integration/convertObjectToMetadata.spec.js b/packages/js-grpc-common/test/integration/convertObjectToMetadata.spec.js new file mode 100644 index 00000000000..1ce9982fcc7 --- /dev/null +++ b/packages/js-grpc-common/test/integration/convertObjectToMetadata.spec.js @@ -0,0 +1,24 @@ +// Import metadata directly to do not import Node.JS server logic in browsers +const { Metadata } = require('@grpc/grpc-js/build/src/metadata'); + +const convertObjectToMetadata = require('../../lib/convertObjectToMetadata'); + +describe('convertObjectToMetadata', () => { + it('should successfully convert an object to Metadata', () => { + const object = { + some: 42, + string: 'someString', + 'buffer-bin': Buffer.from('some'), + }; + + const result = convertObjectToMetadata(object); + + expect(result).to.be.an.instanceOf(Metadata); + // eslint-disable-next-line no-underscore-dangle + expect(result.internalRepr.get('some')).to.deep.equal([42]); + // eslint-disable-next-line no-underscore-dangle + expect(result.internalRepr.get('string')).to.deep.equal(['someString']); + // eslint-disable-next-line no-underscore-dangle + expect(result.internalRepr.get('buffer-bin')).to.deep.equal([Buffer.from('some')]); + }); +}); diff --git a/packages/js-grpc-common/test/integration/loadPackageDefinition.spec.js b/packages/js-grpc-common/test/integration/loadPackageDefinition.spec.js new file mode 100644 index 00000000000..d2bd822ed94 --- /dev/null +++ b/packages/js-grpc-common/test/integration/loadPackageDefinition.spec.js @@ -0,0 +1,18 @@ +const path = require('path'); + +const loadPackageDefinition = require('../../lib/loadPackageDefinition'); + +describe('loadPackageDefinition', () => { + let protoPath; + + beforeEach(() => { + protoPath = path.join(__dirname, '../../lib/test/fixture/example.proto'); + }); + + it('should successfuly load package definition', () => { + const definition = loadPackageDefinition(protoPath, 'org.dash.platform.example.v0'); + + expect(definition.Example).to.be.an.instanceOf(Function); + expect(definition.Example).to.have.a.property('service'); + }); +}); diff --git a/packages/js-grpc-common/test/unit/client/converters/jsonToProtobufFactory.spec.js b/packages/js-grpc-common/test/unit/client/converters/jsonToProtobufFactory.spec.js new file mode 100644 index 00000000000..5e3cc7c6db0 --- /dev/null +++ b/packages/js-grpc-common/test/unit/client/converters/jsonToProtobufFactory.spec.js @@ -0,0 +1,59 @@ +const jsonToProtobufFactory = require( + '../../../../lib/client/converters/jsonToProtobufFactory', +); + +describe('jsonToProtobufFactory', () => { + let protocClassMock; + let pbjsClassMock; + let finishMock; + let jsonToProtobuf; + + beforeEach(function beforeEach() { + protocClassMock = { + deserializeBinary: this.sinon.stub(), + }; + + finishMock = this.sinon.stub(); + + pbjsClassMock = { + fromObject: this.sinon.stub(), + encode: this.sinon.spy(() => ({ + finish: finishMock, + })), + }; + + jsonToProtobuf = jsonToProtobufFactory(protocClassMock, pbjsClassMock); + }); + + it('should call methods of the provided classes', () => { + const message = { + value: 'message', + }; + + const grpcMessage = { + value: 'grpcMessage', + }; + + const grpcMessageBinary = { + value: 'grpcMessageBinary', + }; + + const converted = { + value: 'result', + }; + + protocClassMock.deserializeBinary.returns(converted); + pbjsClassMock.fromObject.returns(grpcMessage); + finishMock.returns(grpcMessageBinary); + + const result = jsonToProtobuf(message); + + expect(result).to.deep.equal(converted); + + expect(pbjsClassMock.fromObject).to.have.been.calledOnceWith(message); + expect(pbjsClassMock.encode).to.have.been.calledOnceWith(grpcMessage); + expect(finishMock).to.have.been.calledOnce(); + + expect(protocClassMock.deserializeBinary).to.have.been.calledOnceWith(grpcMessageBinary); + }); +}); diff --git a/packages/js-grpc-common/test/unit/client/converters/protobufToJsonFactory.spec.js b/packages/js-grpc-common/test/unit/client/converters/protobufToJsonFactory.spec.js new file mode 100644 index 00000000000..2f59c2647f7 --- /dev/null +++ b/packages/js-grpc-common/test/unit/client/converters/protobufToJsonFactory.spec.js @@ -0,0 +1,47 @@ +const protobufToJsonFactory = require( + '../../../../lib/client/converters/protobufToJsonFactory', +); + +describe('protobufToJsonFactory', () => { + let pbjsClassMock; + let protobufToJson; + + beforeEach(function beforeEach() { + pbjsClassMock = { + decode: this.sinon.stub(), + toObject: this.sinon.stub(), + }; + + protobufToJson = protobufToJsonFactory(pbjsClassMock); + }); + + it('should call PBJS class methods', function it() { + const serializedBinary = { + value: 'serializedBinary', + }; + + const message = { + value: 'message', + serializeBinary: this.sinon.spy(() => serializedBinary), + }; + + const grpcMessage = { + value: 'grpcMessage', + }; + + const converted = { + value: 'converted', + }; + + pbjsClassMock.decode.returns(grpcMessage); + pbjsClassMock.toObject.returns(converted); + + const result = protobufToJson(message); + + expect(result).to.deep.equal(converted); + + expect(message.serializeBinary).to.have.been.called(); + expect(pbjsClassMock.decode).to.have.been.calledOnceWith(serializedBinary); + expect(pbjsClassMock.toObject).to.have.been.calledOnceWith(grpcMessage); + }); +}); diff --git a/packages/js-grpc-common/test/unit/server/error/FailedPreconditionGrpcError.spec.js b/packages/js-grpc-common/test/unit/server/error/FailedPreconditionGrpcError.spec.js new file mode 100644 index 00000000000..a8caad6723d --- /dev/null +++ b/packages/js-grpc-common/test/unit/server/error/FailedPreconditionGrpcError.spec.js @@ -0,0 +1,40 @@ +const GrpcErrorCodes = require('../../../../lib/server/error/GrpcErrorCodes'); + +const FailedPreconditionGrpcError = require('../../../../lib/server/error/FailedPreconditionGrpcError'); + +describe('FailedPreconditionGrpcError', () => { + let message; + let metadata; + let error; + + beforeEach(() => { + message = 'Message'; + metadata = {}; + + error = new FailedPreconditionGrpcError(message, metadata); + }); + + describe('#getMessage', () => { + it('should return message', () => { + const result = error.getMessage(); + + expect(result).to.equal(message); + }); + }); + + describe('#getCode', () => { + it('should return FAILED_PRECONDITION error code', () => { + const result = error.getCode(); + + expect(result).to.equal(GrpcErrorCodes.FAILED_PRECONDITION); + }); + }); + + describe('#getMetadata', () => { + it('should return metadata', () => { + const result = error.getRawMetadata(); + + expect(result).to.equal(metadata); + }); + }); +}); diff --git a/packages/js-grpc-common/test/unit/server/error/GrpcError.spec.js b/packages/js-grpc-common/test/unit/server/error/GrpcError.spec.js new file mode 100644 index 00000000000..5cfd220697b --- /dev/null +++ b/packages/js-grpc-common/test/unit/server/error/GrpcError.spec.js @@ -0,0 +1,61 @@ +const GrpcError = require('../../../../lib/server/error/GrpcError'); + +describe('GrpcError', () => { + let code; + let message; + let metadata; + let error; + + beforeEach(() => { + code = 1; + message = 'Message'; + metadata = {}; + + error = new GrpcError(code, message, metadata); + }); + + describe('#getMessage', () => { + it('should return message', () => { + const result = error.getMessage(); + + expect(result).to.equal(message); + }); + }); + + describe('#getCode', () => { + it('should return code', () => { + const result = error.getCode(); + + expect(result).to.equal(code); + }); + }); + + describe('#getRawMetadata', () => { + it('should return metadata', () => { + const result = error.getRawMetadata(); + + expect(result).to.equal(metadata); + }); + }); + + describe('#setMessage', () => { + it('should set message', async () => { + message = 'error message'; + error.setMessage(message); + + expect(error.getMessage()).to.equal(message); + }); + }); + + describe('#setRawMetadata', () => { + it('should set metadata', async () => { + metadata = { + stack: 'stack info', + }; + + error.setRawMetadata(metadata); + + expect(error.getRawMetadata()).to.deep.equal(metadata); + }); + }); +}); diff --git a/packages/js-grpc-common/test/unit/server/error/InternalGrpcError.spec.js b/packages/js-grpc-common/test/unit/server/error/InternalGrpcError.spec.js new file mode 100644 index 00000000000..cc448ede5eb --- /dev/null +++ b/packages/js-grpc-common/test/unit/server/error/InternalGrpcError.spec.js @@ -0,0 +1,29 @@ +const GrpcErrorCodes = require('../../../../lib/server/error/GrpcErrorCodes'); +const InternalGrpcError = require('../../../../lib/server/error/InternalGrpcError'); + +describe('InternalGrpcError', () => { + let error; + let internalError; + + beforeEach(() => { + error = new Error(); + + internalError = new InternalGrpcError(error); + }); + + describe('#getError', () => { + it('should return error', () => { + const result = internalError.getError(); + + expect(result).to.equal(error); + }); + }); + + describe('#getCode', () => { + it('should return INTERNAL error code', () => { + const result = internalError.getCode(); + + expect(result).to.equal(GrpcErrorCodes.INTERNAL); + }); + }); +}); diff --git a/packages/js-grpc-common/test/unit/server/error/InvalidArgumentGrpcError.spec.js b/packages/js-grpc-common/test/unit/server/error/InvalidArgumentGrpcError.spec.js new file mode 100644 index 00000000000..6d68acb70db --- /dev/null +++ b/packages/js-grpc-common/test/unit/server/error/InvalidArgumentGrpcError.spec.js @@ -0,0 +1,39 @@ +const GrpcErrorCodes = require('../../../../lib/server/error/GrpcErrorCodes'); +const InvalidArgumentGrpcError = require('../../../../lib/server/error/InvalidArgumentGrpcError'); + +describe('InvalidArgumentGrpcError', () => { + let message; + let metadata; + let error; + + beforeEach(() => { + message = 'Message'; + metadata = {}; + + error = new InvalidArgumentGrpcError(message, metadata); + }); + + describe('#getMessage', () => { + it('should return message', () => { + const result = error.getMessage(); + + expect(result).to.equal(message); + }); + }); + + describe('#getCode', () => { + it('should return INVALID_ARGUMENT error code', () => { + const result = error.getCode(); + + expect(result).to.equal(GrpcErrorCodes.INVALID_ARGUMENT); + }); + }); + + describe('#getMetadata', () => { + it('should return metadata', () => { + const result = error.getRawMetadata(); + + expect(result).to.equal(metadata); + }); + }); +}); diff --git a/packages/js-grpc-common/test/unit/server/error/VerboseInternalGrpcError.spec.js b/packages/js-grpc-common/test/unit/server/error/VerboseInternalGrpcError.spec.js new file mode 100644 index 00000000000..cc8cf09c7f3 --- /dev/null +++ b/packages/js-grpc-common/test/unit/server/error/VerboseInternalGrpcError.spec.js @@ -0,0 +1,34 @@ +const InternalGrpcError = require('../../../../lib/server/error/InternalGrpcError'); +const VerboseInternalGrpcError = require('../../../../lib/server/error/VerboseInternalGrpcError'); + +describe('InvalidArgumentGrpcError', () => { + let message; + let metadata; + let error; + let internalError; + + beforeEach(() => { + message = 'VerboseInternalGrpcError Test Message'; + metadata = {}; + + error = new Error(message); + internalError = new InternalGrpcError(error, metadata); + }); + + describe('constructor', () => { + it('should attach full stack if errorPath can not be extracted from original stack', () => { + error.stack = 'anonymous'; + internalError = new InternalGrpcError(error, metadata); + const err = new VerboseInternalGrpcError(internalError); + + expect(err.getMessage()).to.be.equal(`${message} ${error.stack}`); + }); + + it('should attach last line of stack if it can be extracted from original stack', () => { + const err = new VerboseInternalGrpcError(internalError); + const [, errorPath] = error.stack.toString().split(/\r\n|\n/); + + expect(err.getMessage()).to.be.equal(`${message} ${errorPath.trim()}`); + }); + }); +}); diff --git a/packages/js-grpc-common/test/unit/server/error/wrapInErrorHandlerFactory.spec.js b/packages/js-grpc-common/test/unit/server/error/wrapInErrorHandlerFactory.spec.js new file mode 100644 index 00000000000..3def975e166 --- /dev/null +++ b/packages/js-grpc-common/test/unit/server/error/wrapInErrorHandlerFactory.spec.js @@ -0,0 +1,119 @@ +const cbor = require('cbor'); + +const wrapInErrorHandlerFactory = require('../../../../lib/server/error/wrapInErrorHandlerFactory'); +const InternalGrpcError = require('../../../../lib/server/error/InternalGrpcError'); +const VerboseInternalGrpcError = require('../../../../lib/server/error/VerboseInternalGrpcError'); +const InvalidArgumentGrpcError = require('../../../../lib/server/error/InvalidArgumentGrpcError'); + +describe('wrapInErrorHandlerFactory', () => { + let loggerMock; + let wrapInErrorHandler; + let rpcMethod; + let callback; + let call; + + beforeEach(function beforeEach() { + loggerMock = { + error: this.sinon.stub(), + }; + + wrapInErrorHandler = wrapInErrorHandlerFactory(loggerMock, false); + + rpcMethod = this.sinon.stub(); + callback = this.sinon.stub(); + call = {}; + }); + + it('should return wrapped RPC method', () => { + const wrappedRpcMethod = wrapInErrorHandler(rpcMethod); + + expect(wrappedRpcMethod).to.be.a('function'); + expect(rpcMethod).to.not.be.called(); + }); + + describe('wrapped RPC method', () => { + it('should call a method', async () => { + const result = 42; + + rpcMethod.resolves(result); + + const wrappedRpcMethod = wrapInErrorHandler(rpcMethod); + + await wrappedRpcMethod(call, callback); + + expect(rpcMethod).to.be.calledOnceWith(call); + expect(callback).to.be.calledOnceWith(null, result); + expect(loggerMock.error).to.not.be.called(); + }); + + it('should call callback with GrpcError if it was thrown from the method', async () => { + const wrappedRpcMethod = wrapInErrorHandler(rpcMethod); + + const grpcError = new InvalidArgumentGrpcError('Something wrong'); + + rpcMethod.throws(grpcError); + + await wrappedRpcMethod(call, callback); + + expect(rpcMethod).to.be.calledOnceWith(call); + expect(callback).to.be.calledOnceWith(grpcError, null); + expect(loggerMock.error).to.not.be.called(); + }); + + it('should log and call callback with InternalGrpcError if some error except GrpcError was thrown from the method', async () => { + const wrappedRpcMethod = wrapInErrorHandler(rpcMethod); + + const someError = new Error(); + + rpcMethod.throws(someError); + + await wrappedRpcMethod(call, callback); + + expect(rpcMethod).to.be.calledOnceWith(call); + + expect(callback).to.be.calledOnce(); + expect(callback.getCall(0).args).to.have.lengthOf(2); + + const [grpcError] = callback.getCall(0).args; + + expect(grpcError).to.be.instanceOf(InternalGrpcError); + expect(grpcError.getError()).to.equal(someError); + + expect(loggerMock.error).to.be.calledOnceWith(someError); + }); + + it('should return VerboseInternalGrpcError in development environment', async () => { + wrapInErrorHandler = wrapInErrorHandlerFactory(loggerMock, false); + + const wrappedRpcMethod = wrapInErrorHandler(rpcMethod); + + const someError = new Error('error'); + + const [, errorPath] = someError.stack.toString().split(/\r\n|\n/); + + const errorMessage = `${someError.message} ${errorPath.trim()}`; + + rpcMethod.throws(someError); + + await wrappedRpcMethod(call, callback); + + expect(rpcMethod).to.be.calledOnceWith(call); + + expect(rpcMethod).to.be.calledOnceWith(call); + + expect(callback).to.be.calledOnce(); + expect(callback.getCall(0).args).to.have.lengthOf(2); + + const [grpcError] = callback.getCall(0).args; + + expect(grpcError).to.be.instanceOf(VerboseInternalGrpcError); + expect(grpcError.getError()).to.equal(someError); + expect(grpcError.getMessage()).to.equal(errorMessage); + expect(grpcError.getRawMetadata()).to.deep.equal({ + 'stack-bin': cbor.encode(someError.stack), + }); + + expect(loggerMock.error).to.be.calledOnceWith(someError); + }); + }); +}); diff --git a/packages/js-grpc-common/test/unit/server/jsonToProtobufHandlerWrapper.spec.js b/packages/js-grpc-common/test/unit/server/jsonToProtobufHandlerWrapper.spec.js new file mode 100644 index 00000000000..65e2fb93606 --- /dev/null +++ b/packages/js-grpc-common/test/unit/server/jsonToProtobufHandlerWrapper.spec.js @@ -0,0 +1,70 @@ +const jsonToProtobufHandlerWrapper = require( + '../../../lib/server/jsonToProtobufHandlerWrapper', +); + +describe('jsonToProtobufHandlerWrapper', () => { + let jsonToProtobufMock; + let protobufToJsonMock; + let rpcMethodMock; + + beforeEach(function beforeEach() { + jsonToProtobufMock = this.sinon.stub(); + protobufToJsonMock = this.sinon.stub(); + rpcMethodMock = this.sinon.stub(); + }); + + it('should proxy call\'s request and write', function it() { + const message = 12; + + const call = { + request: 41, + write: this.sinon.stub(), + }; + + jsonToProtobufMock.returns(call.request + 1); + protobufToJsonMock = this.sinon.spy((value) => value + 1); + + let modifiedRequest; + rpcMethodMock = this.sinon.spy((rpcCall) => { + modifiedRequest = rpcCall.request; + rpcCall.write(message); + }); + + const wrappedMethod = jsonToProtobufHandlerWrapper( + jsonToProtobufMock, + protobufToJsonMock, + rpcMethodMock, + ); + + wrappedMethod(call); + + expect(jsonToProtobufMock).to.have.been.calledOnceWith(call.request); + expect(protobufToJsonMock).to.have.been.calledOnceWith(message); + + expect(call.write).to.have.been.calledOnceWith(message + 1, undefined, undefined); + expect(modifiedRequest).to.equal(call.request + 1); + }); + + it('should proxy callback and it\'s message', function it() { + const message = 12; + + protobufToJsonMock = this.sinon.spy((value) => value + 1); + + rpcMethodMock = this.sinon.spy((_, callback) => { + callback(null, message); + }); + + const wrappedMethod = jsonToProtobufHandlerWrapper( + jsonToProtobufMock, + protobufToJsonMock, + rpcMethodMock, + ); + + const callback = this.sinon.stub(); + + wrappedMethod({}, callback); + + expect(protobufToJsonMock).to.have.been.calledOnceWith(message); + expect(callback).to.have.been.calledOnceWith(null, message + 1); + }); +}); diff --git a/packages/js-grpc-common/test/unit/server/stream/AcknowledgingWritable.spec.js b/packages/js-grpc-common/test/unit/server/stream/AcknowledgingWritable.spec.js new file mode 100644 index 00000000000..8533a878094 --- /dev/null +++ b/packages/js-grpc-common/test/unit/server/stream/AcknowledgingWritable.spec.js @@ -0,0 +1,77 @@ +const WritableMock = require('../../../../lib/test/mock/WritableMock'); +const AcknowledgingWritable = require('../../../../lib/server/stream/AcknowledgingWritable'); + +describe('AcknowledgingWritable', () => { + describe('#write', () => { + it('should throw when wrapped stream emits error', () => { + const writable = new WritableMock({ fireOnErrorWithoutCallback: true }); + const wrapper = new AcknowledgingWritable(writable); + + expect(wrapper.write('123')).to.be.rejectedWith('Error event'); + }); + + it('should throw when wrapped stream calls callback with an error', () => { + const writable = new WritableMock({ callWriteCallbackWithAnError: true }); + const wrapper = new AcknowledgingWritable(writable); + + expect(wrapper.write('123')).to.be.rejectedWith('Error from callback'); + }); + + it('should throw an error if .write method of the wrapped stream throws an error', () => { + const writable = new WritableMock({ throwInWrite: true }); + const wrapper = new AcknowledgingWritable(writable); + + expect(wrapper.write('123')).to.be.rejectedWith('Thrown error'); + }); + + it('should return true when wrapped ._write callback called', async () => { + const writable = new WritableMock({ callCallback: true }); + const wrapper = new AcknowledgingWritable(writable); + + const result = await wrapper.write('123'); + + expect(result).to.be.true(); + }); + + it("should attach handlers when write is called and detach when it's finished", async () => { + const writable = new WritableMock({ callCallback: true }); + const wrapper = new AcknowledgingWritable(writable); + + // eslint-disable-next-line no-underscore-dangle + expect(wrapper.writable._eventsCount).to.be.equal(0); + + const promise = wrapper.write('123'); + + // eslint-disable-next-line no-underscore-dangle + expect(wrapper.writable._eventsCount).to.be.equal(2); + + const result = await promise; + + expect(result).to.be.true(); + + // eslint-disable-next-line no-underscore-dangle + expect(wrapper.writable._eventsCount).to.be.equal(0); + }); + + it('should return true when instead of calling callback drain is required', async () => { + const writable = new WritableMock({ requireDrain: true }); + const wrapper = new AcknowledgingWritable(writable); + + // eslint-disable-next-line no-underscore-dangle + expect(wrapper.writable._eventsCount).to.be.equal(0); + + const promise = wrapper.write('123'); + + // event error is still not detached + // eslint-disable-next-line no-underscore-dangle + expect(wrapper.writable._eventsCount).to.be.equal(1); + + const result = await promise; + + expect(result).to.be.true(); + + // eslint-disable-next-line no-underscore-dangle + expect(wrapper.writable._eventsCount).to.be.equal(0); + }); + }); +}); diff --git a/packages/js-grpc-common/test/unit/utils/isObject.spec.js b/packages/js-grpc-common/test/unit/utils/isObject.spec.js new file mode 100644 index 00000000000..ea859ede3f5 --- /dev/null +++ b/packages/js-grpc-common/test/unit/utils/isObject.spec.js @@ -0,0 +1,13 @@ +const isObject = require('../../../lib/utils/isObject'); + +describe('isObject', () => { + it('should return true if argument is Object', () => { + const result = isObject({ some: 42 }); + expect(result).to.be.true(); + }); + + it('should return false if argument is not Object', () => { + const result = isObject('asdasd'); + expect(result).to.be.false(); + }); +}); diff --git a/packages/masternode-reward-shares-contract/.eslintrc b/packages/masternode-reward-shares-contract/.eslintrc new file mode 100644 index 00000000000..333860e2f93 --- /dev/null +++ b/packages/masternode-reward-shares-contract/.eslintrc @@ -0,0 +1,15 @@ +{ + "extends": "airbnb-base", + "rules": { + "no-plusplus": 0, + "eol-last": [ + "error", + "always" + ], + "class-methods-use-this": "off", + "curly": [ + "error", + "all" + ] + } +} diff --git a/packages/masternode-reward-shares-contract/.mocharc.yml b/packages/masternode-reward-shares-contract/.mocharc.yml new file mode 100644 index 00000000000..96eed0105b5 --- /dev/null +++ b/packages/masternode-reward-shares-contract/.mocharc.yml @@ -0,0 +1,3 @@ +file: + - test/bootstrap.js +recursive: true diff --git a/packages/masternode-reward-shares-contract/LICENSE b/packages/masternode-reward-shares-contract/LICENSE new file mode 100644 index 00000000000..3be95833750 --- /dev/null +++ b/packages/masternode-reward-shares-contract/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2019 Dash Core Group, Inc. + +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/packages/masternode-reward-shares-contract/README.md b/packages/masternode-reward-shares-contract/README.md new file mode 100644 index 00000000000..8e080a05e92 --- /dev/null +++ b/packages/masternode-reward-shares-contract/README.md @@ -0,0 +1,33 @@ +# Reward sharing Contract + +[![Build Status](https://github.com/dashevo/dpns-contract/actions/workflows/test_and_release.yml/badge.svg)](https://github.com/dashevo/dpns-contract/actions/workflows/test_and_release.yml) +[![NPM version](https://img.shields.io/npm/v/@dashevo/dpns-contract.svg?style=flat-square)](https://npmjs.org/package/@dashevo/masternode-reward-shares-contract) + +JSON Contracts for Dash Platform Name Service + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contributing](#contributing) +- [License](#license) + +## Install + +```sh +npm install @dashevo/dpns-contract +``` + +## Usage + +```sh +# TODO ... +``` + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/masternode-reward-shares-contract/issues/new) or submit PRs. + +## License + +[MIT](LICENSE) © Dash Core Group, Inc. diff --git a/packages/masternode-reward-shares-contract/lib/systemIds.js b/packages/masternode-reward-shares-contract/lib/systemIds.js new file mode 100644 index 00000000000..caeaa27c46e --- /dev/null +++ b/packages/masternode-reward-shares-contract/lib/systemIds.js @@ -0,0 +1,4 @@ +module.exports = { + ownerId: 'BjDiho3ahEBT6w45YungawKrUcqCZ7q7p46FXwnoakXR', + contractId: 'rUnsWrFu3PKyRMGk2mxmZVBPbQuZx2qtHeFjURoQevX', +}; diff --git a/packages/masternode-reward-shares-contract/package.json b/packages/masternode-reward-shares-contract/package.json new file mode 100644 index 00000000000..d326d46a449 --- /dev/null +++ b/packages/masternode-reward-shares-contract/package.json @@ -0,0 +1,44 @@ +{ + "name": "@dashevo/masternode-reward-shares-contract", + "version": "0.23.0-dev.4", + "description": "A contract and helper scripts for reward sharing", + "scripts": { + "lint": "eslint .", + "test": "yarn run test:unit", + "test:unit": "mocha 'test/unit/**/*.spec.js'" + }, + "contributors": [ + { + "name": "Ivan Shumkov", + "email": "ivan@shumkov.ru", + "url": "https://github.com/shumkov" + }, + { + "name": "Djavid Gabibiyan", + "email": "djavid@dash.org", + "url": "https://github.com/jawid-h" + }, + { + "name": "Anton Suprunchuk", + "email": "anton.suprunchuk@dash.org", + "url": "https://github.com/antouhou" + }, + { + "name": "Konstantin Shuplenkov", + "email": "konstantin.shuplenkov@dash.org", + "url": "https://github.com/shuplenkov" + } + ], + "license": "MIT", + "devDependencies": { + "@dashevo/dpp": "workspace:~", + "chai": "^4.3.4", + "dirty-chai": "^2.0.1", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2", + "mocha": "^9.1.2", + "sinon": "^11.1.2", + "sinon-chai": "^3.7.0" + } +} diff --git a/packages/masternode-reward-shares-contract/schema/masternode-reward-shares-documents.json b/packages/masternode-reward-shares-contract/schema/masternode-reward-shares-documents.json new file mode 100644 index 00000000000..977ab1cd655 --- /dev/null +++ b/packages/masternode-reward-shares-contract/schema/masternode-reward-shares-documents.json @@ -0,0 +1,50 @@ +{ + "rewardShare": { + "description": "Share specified percentage of masternode rewards with identities", + "type": "object", + "indices": [ + { + "name": "ownerIdAndPayToId", + "properties": [ + { + "$ownerId": "asc" + }, + { + "payToId": "asc" + } + ], + "unique": true + }, + { + "name": "ownerId", + "properties": [ + { + "$ownerId": "asc" + } + ] + } + ], + "properties": { + "payToId": { + "description": "Identifier to share reward with", + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "percentage": { + "description": "Reward percentage to share", + "type": "integer", + "minimum": 1, + "maximum": 10000 + } + }, + "required": [ + "payToId", + "percentage" + ], + "additionalProperties": false + } +} + diff --git a/packages/masternode-reward-shares-contract/test/.eslintrc b/packages/masternode-reward-shares-contract/test/.eslintrc new file mode 100644 index 00000000000..720ced73852 --- /dev/null +++ b/packages/masternode-reward-shares-contract/test/.eslintrc @@ -0,0 +1,12 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + }, + "globals": { + "expect": true + } +} diff --git a/packages/masternode-reward-shares-contract/test/bootstrap.js b/packages/masternode-reward-shares-contract/test/bootstrap.js new file mode 100644 index 00000000000..461846fa5e7 --- /dev/null +++ b/packages/masternode-reward-shares-contract/test/bootstrap.js @@ -0,0 +1,22 @@ +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); + +const { expect, use } = require('chai'); +const dirtyChai = require('dirty-chai'); + +use(dirtyChai); +use(sinonChai); + +beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + +afterEach(function afterEach() { + this.sinon.restore(); +}); + +global.expect = expect; diff --git a/packages/masternode-reward-shares-contract/test/unit/masternodeRewardSharesContract.spec.js b/packages/masternode-reward-shares-contract/test/unit/masternodeRewardSharesContract.spec.js new file mode 100644 index 00000000000..ecad7403d4c --- /dev/null +++ b/packages/masternode-reward-shares-contract/test/unit/masternodeRewardSharesContract.spec.js @@ -0,0 +1,193 @@ +const DashPlatformProtocol = require('@dashevo/dpp'); +const generateRandomIdentifier = require('@dashevo/dpp/lib/test/utils/generateRandomIdentifier'); + +const rewardSharingContractSchema = require('../../schema/masternode-reward-shares-documents.json'); + +describe('Masternode reward shares contract', () => { + let dpp; + let contract; + let identityId; + let rewardShare; + + beforeEach(async function beforeEach() { + const rewardSharingContractStub = this.sinon.stub(); + + dpp = new DashPlatformProtocol({ + stateRepository: { + rewardSharingDataContract: rewardSharingContractStub, + }, + }); + + await dpp.initialize(); + + identityId = generateRandomIdentifier(); + + contract = dpp.dataContract.create(identityId, rewardSharingContractSchema); + + rewardSharingContractStub.resolves(contract); + + rewardShare = { + payToId: generateRandomIdentifier(), + percentage: 500, + }; + }); + + it('should have a valid contract definition', async function shouldHaveValidContract() { + this.timeout(5000); + + const validationResult = await dpp.dataContract.validate(contract); + + expect(validationResult.isValid()).to.be.true(); + }); + + describe('payToId', () => { + it('should be defined', () => { + delete rewardShare.payToId; + + try { + dpp.document.create(contract, identityId, 'rewardShare', rewardShare); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('payToId'); + } + }); + + it('should have no less than 32 bytes', () => { + rewardShare.payToId = Buffer.alloc(31); + + try { + dpp.document.create(contract, identityId, 'rewardShare', rewardShare); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minItems'); + expect(error.instancePath).to.equal('/payToId'); + } + }); + + it('should have no more than 32 bytes', async () => { + rewardShare.payToId = Buffer.alloc(33); + + try { + dpp.document.create(contract, identityId, 'rewardShare', rewardShare); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.getErrors()).to.have.a.lengthOf(1); + + const [error] = e.getErrors(); + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maxItems'); + expect(error.instancePath).to.equal('/payToId'); + } + }); + }); + + describe('percentage', () => { + it('should be defined', () => { + delete rewardShare.percentage; + + try { + dpp.document.create(contract, identityId, 'rewardShare', rewardShare); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('required'); + expect(error.params.missingProperty).to.equal('percentage'); + } + }); + + it('should not be less than 1', () => { + rewardShare.percentage = 0; + + try { + dpp.document.create(contract, identityId, 'rewardShare', rewardShare); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('minimum'); + expect(error.instancePath).to.equal('/percentage'); + } + }); + + it('should not be more than 10000', () => { + rewardShare.percentage = 10001; + + try { + dpp.document.create(contract, identityId, 'rewardShare', rewardShare); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('maximum'); + expect(error.instancePath).to.equal('/percentage'); + } + }); + + it('should be a number', () => { + rewardShare.percentage = '10'; + + try { + dpp.document.create(contract, identityId, 'rewardShare', rewardShare); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + const [error] = e.errors; + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('type'); + expect(error.instancePath).to.equal('/percentage'); + expect(error.params.type).to.equal('integer'); + } + }); + }); + + it('should should not have additional properties', () => { + rewardShare.someOtherProperty = 42; + + try { + dpp.document.create(contract, identityId, 'rewardShare', rewardShare); + + expect.fail('should throw error'); + } catch (e) { + expect(e.name).to.equal('InvalidDocumentError'); + expect(e.errors).to.have.a.lengthOf(1); + + const [error] = e.errors; + + expect(error.name).to.equal('JsonSchemaError'); + expect(error.keyword).to.equal('additionalProperties'); + expect(error.params.additionalProperty).to.equal('someOtherProperty'); + } + }); +}); diff --git a/packages/platform-test-suite/.env.example b/packages/platform-test-suite/.env.example new file mode 100644 index 00000000000..d57399d57a7 --- /dev/null +++ b/packages/platform-test-suite/.env.example @@ -0,0 +1,22 @@ +# DAPI seed ("ip:port") +DAPI_SEED= + +# Private key to fund wallets used in tests +FAUCET_ADDRESS= +FAUCET_PRIVATE_KEY= + +NETWORK= + +# Start to sync wallet from specific height to speed up the sync process +# SKIP_SYNC_BEFORE_HEIGHT= + +# System Data Contract info required for some tests +DPNS_OWNER_PRIVATE_KEY= +FEATURE_FLAGS_OWNER_PRIVATE_KEY= +DASHPAY_OWNER_PRIVATE_KEY= +# MASTERNODE_REWARD_SHARES_OWNER_PRO_REG_TX_HASH= +# MASTERNODE_REWARD_SHARES_OWNER_PRIVATE_KEY= +# MASTERNODE_REWARD_SHARES_MN_OWNER_PRIVATE_KEY= + +# Enable storage for faucet wallet +# FAUCET_WALLET_USE_STORAGE=true diff --git a/packages/platform-test-suite/.eslintrc b/packages/platform-test-suite/.eslintrc new file mode 100644 index 00000000000..c4cfd773546 --- /dev/null +++ b/packages/platform-test-suite/.eslintrc @@ -0,0 +1,35 @@ +{ + "extends": "airbnb-base", + "env": { + "node": true, + "mocha": true + }, + "rules": { + "no-plusplus": 0, + "eol-last": [ + "error", + "always" + ], + "no-await-in-loop": "off", + "import/no-extraneous-dependencies": "off", + "no-restricted-syntax": [ + "error", + { + "selector": "LabeledStatement", + "message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand." + }, + { + "selector": "WithStatement", + "message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize." + } + ], + "curly": [ + "error", + "all" + ], + "require-await": "error" + }, + "globals": { + "expect": true + } +} diff --git a/packages/platform-test-suite/.gitignore b/packages/platform-test-suite/.gitignore new file mode 100644 index 00000000000..0a731ec6c8a --- /dev/null +++ b/packages/platform-test-suite/.gitignore @@ -0,0 +1,7 @@ +/.env + +# Node.JS +node_modules + +# Jet Brains +.idea diff --git a/packages/platform-test-suite/.mocharc.yml b/packages/platform-test-suite/.mocharc.yml new file mode 100644 index 00000000000..c8c54c18400 --- /dev/null +++ b/packages/platform-test-suite/.mocharc.yml @@ -0,0 +1,4 @@ +exit: true +timeout: 650000 +file: + - ./lib/test/bootstrap.js diff --git a/packages/platform-test-suite/CHANGELOG.md b/packages/platform-test-suite/CHANGELOG.md new file mode 100644 index 00000000000..609a2750eb4 --- /dev/null +++ b/packages/platform-test-suite/CHANGELOG.md @@ -0,0 +1,157 @@ +# [0.21.0](https://github.com/dashevo/platform-test-suite/compare/v0.20.0...v0.21.0) (2021-10-26) + + + +### Features + +* `skip-sync-before-height` option to speedup tests ([#180](https://github.com/dashevo/platform-test-suite/issues/180)) +* run tests in browser by demand ([#172](https://github.com/dashevo/platform-test-suite/issues/172)) +* add a test for parsing waitForStateTransitionResult proof ([#173](https://github.com/dashevo/platform-test-suite/issues/173)) +* add proof test for all endpoints and appHash extraction ([#163](https://github.com/dashevo/platform-test-suite/issues/163)) +* use DPP error classes ([#164](https://github.com/dashevo/platform-test-suite/issues/164)) +* use new Drive's error codes ([#162](https://github.com/dashevo/platform-test-suite/issues/162)) +* add feature flags e2e test ([#155](https://github.com/dashevo/platform-test-suite/issues/155)) +* proof verification tests ([#159](https://github.com/dashevo/platform-test-suite/issues/159)) +* update tests to reflect lastest dapi changes ([#149](https://github.com/dashevo/platform-test-suite/issues/149)) + + +### Bug Fixes + +* don't upgrade evidence maxBytes value ([#177](https://github.com/dashevo/platform-test-suite/issues/177)) +* transaction queried to soon propagation ([#176](https://github.com/dashevo/platform-test-suite/issues/176)) +* testnet latency problems ([#182](https://github.com/dashevo/platform-test-suite/issues/182)) + + +# [0.20.0](https://github.com/dashevo/platform-test-suite/compare/v0.19.1...v0.20.0) (2021-07-21) + + +### Features + +* check the proof format for `waitForStateTrasitionResult` ([#141](https://github.com/dashevo/platform-test-suite/issues/141)) +* improve chainlock tests ([#138](https://github.com/dashevo/platform-test-suite/issues/138)) +* change an order of error check to provide better and faster debug info ([#129](https://github.com/dashevo/platform-test-suite/issues/129)) + + +### Bug Fixes + +* get non existing transaction test was not failing properly ([#140](https://github.com/dashevo/platform-test-suite/issues/140)) + + +### BREAKING CHANGES + +* not compatible with Dash Platform v0.19 and lower + + + +## [0.19.1](https://github.com/dashevo/platform-test-suite/compare/v0.19.0...v0.19.1) (2021-06-04) + + +### Bug Fixes + +* should fail to create an identity ([#132](https://github.com/dashevo/platform-test-suite/issues/132)) + + + +# [0.19.0](https://github.com/dashevo/platform-test-suite/compare/v0.18.0...v0.19.0) (2021-05-05) + + +### Features + +* integrate Chain Asset Lock Proofs ([#115](https://github.com/dashevo/platform-test-suite/issues/115), [#120](https://github.com/dashevo/platform-test-suite/issues/120), [#122](https://github.com/dashevo/platform-test-suite/issues/122)) +* update to new `getStatus` endpoint ([#111](https://github.com/dashevo/platform-test-suite/issues/111), [#119](https://github.com/dashevo/platform-test-suite/issues/119), [#114](https://github.com/dashevo/platform-test-suite/issues/114)) +* remove fallbacks from regtest mode ([#103](https://github.com/dashevo/platform-test-suite/issues/103)) +* CI with Github Actions ([#108](https://github.com/dashevo/platform-test-suite/issues/108), [#117](https://github.com/dashevo/platform-test-suite/issues/117)) + + +### Bug Fixes + +* bash script could not run mocha in github actions ([#105](https://github.com/dashevo/platform-test-suite/issues/105)) + + + +# [0.18.0](https://github.com/dashevo/platform-test-suite/compare/v0.17.0...v0.18.0) (2021-03-03) + + +### Features + +* use SDK with new ST acknowledgment ([#96](https://github.com/dashevo/platform-test-suite/pull/96)) + + +### Bug Fixes + +* identity was used in a wrong way ([f115468](https://github.com/dashevo/platform-test-suite/commit/f1154689e5a9c451a625a77c5b8c929e118a7fc6)) +* removed unused identity variable ([81f4839](https://github.com/dashevo/platform-test-suite/commit/81f4839bc67a8fdcb0df6283dae3276a72c579d7)) + + + +# [0.17.0](https://github.com/dashevo/platform-test-suite/compare/v0.16.0...v0.17.0) (2020-12-30) + + +### Features + +* make test works without fallback ([#91](https://github.com/dashevo/platform-test-suite/issues/91)) +* update `dashcore-lib`, `dpp`, `wallet-lib`, `dashjs` ([#81](https://github.com/dashevo/platform-test-suite/issues/81), [#83](https://github.com/dashevo/platform-test-suite/issues/83), [#88](https://github.com/dashevo/platform-test-suite/issues/88)) +* identity funding double-spend tests ([#86](https://github.com/dashevo/platform-test-suite/issues/86)) + + +### Bug Fixes + +* fake asset lock must be passed only for regtest ([#90](https://github.com/dashevo/platform-test-suite/issues/90)) +* invalid assertions in `Identity` functional test ([#84](https://github.com/dashevo/platform-test-suite/issues/84)) + + + +# [0.16.0](https://github.com/dashevo/platform-test-suite/compare/v0.15.0...v0.16.0) (2020-10-28) + + +### Chore + +* update to SDK 0.16 ([#77](https://github.com/dashevo/platform-test-suite/issues/77)) + + +### BREAKING CHANGES + +* Nodes with Dash Platform 0.15 are not supported + + + +# [0.15.0](https://github.com/dashevo/platform-test-suite/compare/v0.14.0...v0.15.0) (2020-09-04) + + +### Bug Fixes + +* faucet client singleton ([#70](https://github.com/dashevo/platform-test-suite/issues/70)) +* core tests were using `serialize` instead of `toBuffer` ([#66](https://github.com/dashevo/platform-test-suite/issues/66)) +* npm is not running `prepare` script as root ([#63](https://github.com/dashevo/platform-test-suite/issues/63)) + + +### Features + +* `wallet` e2e test ([#59](https://github.com/dashevo/platform-test-suite/issues/59)) +* new test timeout option ([#71](https://github.com/dashevo/platform-test-suite/issues/71)) +* remove pending `subscribeToTransactionsWithProofs` functional tests ([#72](https://github.com/dashevo/platform-test-suite/issues/72)) +* remove getAddressSummary tests ([#67](https://github.com/dashevo/platform-test-suite/issues/67)) +* update Wallet, DPP, DPNS and SDK deps ([#50](https://github.com/dashevo/platform-test-suite/issues/50), [#64](https://github.com/dashevo/platform-test-suite/issues/64), [#68](https://github.com/dashevo/platform-test-suite/issues/68), [#60](https://github.com/dashevo/platform-test-suite/issues/60)) +* support for installation node module from git on docker start ([#55](https://github.com/dashevo/platform-test-suite/issues/55)) ([adb1e16](https://github.com/dashevo/platform-test-suite/commit/adb1e1672a0288672b2eaef0bf9effc9212b50ad)) +* new topup identity test ([#53](https://github.com/dashevo/platform-test-suite/issues/53)) ([075f09c](https://github.com/dashevo/platform-test-suite/commit/075f09cb211fcda45aff2c75a2222e735f9eab49)) + + +### Code Refactoring + +* use Wallet lib instead of getUTXO ([#62](https://github.com/dashevo/platform-test-suite/issues/62)) + + + +# 0.14.0 (2020-07-23) + + +### Features + +* use external Travis scripts ([#47](https://github.com/dashevo/platform-test-suite/issues/47)) +* update SDK to 3.14.0 ([#45](https://github.com/dashevo/platform-test-suite/issues/45)) +* update DPP to 0.14.0 ([#42](https://github.com/dashevo/platform-test-suite/issues/42)) +* add document timestamp tests ([#40](https://github.com/dashevo/platform-test-suite/issues/40)) +* define npm test scopes ([#31](https://github.com/dashevo/platform-test-suite/issues/31)) +* dockerize test suite ([#28](https://github.com/dashevo/platform-test-suite/issues/28)) +* functional core tests ([#33](https://github.com/dashevo/platform-test-suite/issues/33)) +* implement functional tests ([#38](https://github.com/dashevo/platform-test-suite/issues/38)) diff --git a/packages/platform-test-suite/Dockerfile b/packages/platform-test-suite/Dockerfile new file mode 100644 index 00000000000..6fe594cd1a8 --- /dev/null +++ b/packages/platform-test-suite/Dockerfile @@ -0,0 +1,67 @@ +FROM node:16-alpine as builder + +ARG NODE_ENV=production +ENV NODE_ENV ${NODE_ENV} + +RUN apk update && \ + apk --no-cache upgrade && \ + apk add --no-cache git \ + openssh-client \ + python3 \ + alpine-sdk + +# Enable corepack https://github.com/nodejs/corepack +RUN corepack enable + +WORKDIR /platform + +# Copy yarn files +COPY .yarn ./.yarn +COPY package.json yarn.lock .yarnrc.yml .pnp.* ./ + +# Copy only necessary packages from monorepo +COPY packages/dapi-grpc packages/dapi-grpc +COPY packages/dash-spv packages/dash-spv +COPY packages/dpns-contract packages/dpns-contract +COPY packages/dashpay-contract packages/dashpay-contract +COPY packages/feature-flags-contract packages/feature-flags-contract +COPY packages/js-dapi-client packages/js-dapi-client +COPY packages/js-dash-sdk packages/js-dash-sdk +COPY packages/js-dpp packages/js-dpp +COPY packages/wallet-lib packages/wallet-lib +COPY packages/js-grpc-common packages/js-grpc-common +COPY packages/platform-test-suite packages/platform-test-suite +COPY packages/masternode-reward-shares-contract packages/masternode-reward-shares-contract + +# Install Test Suite specific dependencies using previous +# node_modules directory to reuse built binaries +RUN --mount=type=cache,target=/tmp/unplugged \ + cp -R /tmp/unplugged /platform/.yarn/ && \ + yarn workspaces focus --production @dashevo/platform-test-suite && \ + cp -R /platform/.yarn/unplugged /tmp/ + +FROM node:16-alpine + +ARG NODE_ENV=production +ENV NODE_ENV ${NODE_ENV} + +LABEL maintainer="Dash Developers " +LABEL description="DAPI Node.JS" + +# Install required deps +RUN apk add --no-cache bash + +# Install latest yarn +RUN yarn set version 3.1.0 + +ENV PATH /platform/node_modules/.bin:$PATH + +WORKDIR /platform + +COPY --from=builder /platform /platform + +RUN cp /platform/packages/platform-test-suite/.env.example /platform/packages/platform-test-suite/.env + +EXPOSE 2500 2501 2510 + +ENTRYPOINT ["/platform/packages/platform-test-suite/bin/test.sh"] diff --git a/packages/platform-test-suite/LICENSE b/packages/platform-test-suite/LICENSE new file mode 100644 index 00000000000..3cdb7111ae2 --- /dev/null +++ b/packages/platform-test-suite/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2018-2019 Dash Core Group, Inc. + +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/packages/platform-test-suite/README.md b/packages/platform-test-suite/README.md new file mode 100644 index 00000000000..0256ed2c8d3 --- /dev/null +++ b/packages/platform-test-suite/README.md @@ -0,0 +1,114 @@ +# Dash Platform Test Suite + +[![Latest Release](https://img.shields.io/github/v/release/dashevo/platform-test-suite)](https://github.com/dashevo/platform-test-suite/releases/latest) +[![Build Status](https://github.com/dashevo/platform-test-suite/actions/workflows/test_and_release.yml/badge.svg)](https://github.com/dashevo/platform-test-suite/actions/workflows/test_and_release.yml) +[![Release Date](https://img.shields.io/github/release-date/dashevo/platform-test-suite)](https://img.shields.io/github/release-date/dashevo/platform-test-suite) +[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen)](https://github.com/RichardLitt/standard-readme) + +The test suite for end-to-end and functional testing the Dash Platform by running some real-life scenarios against a Dash Network + +## Table of Contents +- [Pre-Requisites](#pre-requisites) +- [Usage](#usage) +- [Contributing](#contributing) +- [License](#license) + +## Pre-requisites + +You may run test-suite against any platform compatible network, or even [local node](https://github.com/dashevo/platform/tree/master/packages/dashmate). +To run locally make sure you have [Node.js](https://nodejs.org/) installed. +To run using [Docker](https://www.docker.com/), make sure you have it installed. + +## Usage + +### Running locally + +Install all the necessary dependencies: + +```sh +$ yarn +``` + +Use `./bin/test.sh` script to run tests: + +```sh +$ ./bin/test.sh + +Run test suite + +Usage: test [options] + + can be IP or IP:port (or pass via DAPI_SEED env) + + Options: + -s=a,b,c --scope=a,b,c - test scope to run + -k=key --faucet-key=key - faucet private key string + -n=network --network=network - use regtest, devnet or testnet + --skip-sync-before-height=H - start sync funding wallet from specific height + --dpns-tld-identity-private-key=private_key - top level identity private key + --dpns-tld-identity-id=tld_identity_id - top level identity id + --dpns-contract-id=tld_contract_id - dpns contract id + --feature-flags-identity-id=ff_identity_id - feature-flags contract id + --feature-flags-contract-id=ff_contract_id - feature-flags contract id + --faucet-wallet-use-storage=true - use persistent wallet storage for faucet + --faucet-wallet-storage-dir=absolute_dir - specify directory where faucet wallet persistent storage will be stored + -t --timeout - test timeout in milliseconds + -h --help - show help + + Possible scopes: + e2e + functional + core + platform + e2e:dpns + e2e:contacts + functional:core + functional:platform +``` + +### Running using Docker + +Just run pre-built image using the same arguments as [running locally](#running-locally): + +```sh +$ docker run --network=host --env ./.env dashpay/platform-test-suite + +Run test suite + +Usage: test [options] + + can be IP or IP:port (or pass via DAPI_SEED env) + + Options: + -s=a,b,c --scope=a,b,c - test scope to run + -k=key --faucet-key=key - faucet private key string + -n=network --network=network - use regtest, devnet or testnet + --skip-sync-before-height=H - start sync funding wallet from specific height + --dpns-tld-identity-private-key=private_key - top level identity private key + --dpns-tld-identity-id=tld_identity_id - top level identity id + --dpns-contract-id=tld_contract_id - dpns contract id + --feature-flags-identity-id=ff_identity_id - feature-flags contract id + --feature-flags-contract-id=ff_contract_id - feature-flags contract id + --faucet-wallet-use-storage=true - use persistent wallet storage for faucet + --faucet-wallet-storage-dir=absolute_dir - specify directory where faucet wallet persistent storage will be stored + -t --timeout - test timeout in milliseconds + -h --help - show help + + Possible scopes: + e2e + functional + core + platform + e2e:dpns + e2e:contacts + functional:core + functional:platform +``` + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs. + +## License + +[MIT](LICENSE) © Dash Core Group, Inc. diff --git a/packages/platform-test-suite/bin/test.sh b/packages/platform-test-suite/bin/test.sh new file mode 100755 index 00000000000..718f803d9c8 --- /dev/null +++ b/packages/platform-test-suite/bin/test.sh @@ -0,0 +1,212 @@ +#!/usr/bin/env bash + +set -ea + +cmd_usage="Run test suite + +Usage: test [options] + + can be IP or IP:port (or pass via DAPI_SEED env) + + Options: + -s=a,b,c --scope=a,b,c - test scope to run + -k=key --faucet-key=key - faucet private key string + -n=network --network=network - use regtest, devnet or testnet + --skip-sync-before-height=H - start sync funding wallet from specific height + --dpns-tld-identity-private-key=private_key - top level identity private key + --dpns-tld-identity-id=tld_identity_id - top level identity id + --dpns-contract-id=tld_contract_id - dpns contract id + --feature-flags-identity-id=ff_identity_id - feature-flags contract id + --feature-flags-contract-id=ff_contract_id - feature-flags contract id + --faucet-wallet-use-storage=true - use persistent wallet storage for faucet + --faucet-wallet-storage-dir=absolute_dir - specify directory where faucet wallet persistent storage will be stored + -t --timeout - test timeout in milliseconds + -h --help - show help + + Possible scopes: + e2e + functional + core + platform + e2e:dpns + e2e:contacts + functional:core + functional:platform" + +FIRST_ARG="$1" +DAPI_SEED="${DAPI_SEED:=$FIRST_ARG}" +network="testnet" + +DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +cd "${DIR}/.." + +for i in "$@" +do +case ${i} in + -h|--help) + echo "$cmd_usage" + exit 0 + ;; + -s=*|--scope=*) + scope="${i#*=}" + ;; + -k=*|--faucet-key=*) + faucet_key="${i#*=}" + ;; + -n=*|--network=*) + network="${i#*=}" + ;; + --skip-sync-before-height=*) + skip_sync_before_height="${i#*=}" + ;; + --dpns-tld-identity-private-key=*) + identity_private_key="${i#*=}" + ;; + --dpns-tld-identity-id=*) + tld_identity_id="${i#*=}" + ;; + --dpns-contract-id=*) + tld_contract_id="${i#*=}" + ;; + --feature-flags-identity-id=*) + ff_identity_id="${i#*=}" + ;; + --feature-flags-contract-id=*) + ff_contract_id="${i#*=}" + ;; + -t=*|--timeout=*) + timeout="${i#*=}" + ;; + --faucet-wallet-storage-dir=*) + faucet_wallet_storage_dir="${i#*=}" + ;; + --faucet-wallet-use-storage=*) + faucet_wallet_use_storage="${i#*=}" + ;; +esac +done + +if [ -z "$DAPI_SEED" ] || [[ $DAPI_SEED == -* ]] +then + echo "Seed is not specified" + echo "" + echo "$cmd_usage" + exit 1 +fi + +if [ -n "$timeout" ] && ! [[ $timeout =~ ^[0-9]+$ ]] +then + echo "Timeout must be an integer" + exit 1 +fi + +if [ -n "$scope" ] +then + scope_dirs="" + + IFS=', ' read -r -a scopes <<< "$scope" + + for scope in "${scopes[@]}" + do + case $scope in + e2e) + scope_dirs="${scope_dirs} test/e2e/**/*.spec.js" + ;; + functional) + scope_dirs="${scope_dirs} test/functional/**/*.spec.js" + ;; + core) + scope_dirs="${scope_dirs} test/functional/core/**/*.spec.js test/e2e/**/*.spec.js" + ;; + platform) + scope_dirs="${scope_dirs} test/functional/platform/**/*.spec.js test/e2e/**/*.spec.js" + ;; + e2e:dpns) + scope_dirs="${scope_dirs} test/e2e/dpns.spec.js" + ;; + e2e:contacts) + scope_dirs="${scope_dirs} test/e2e/contacts.spec.js" + ;; + functional:core) + scope_dirs="${scope_dirs} test/functional/core/**/*.spec.js" + ;; + functional:platform) + scope_dirs="${scope_dirs} test/functional/platform/**/*.spec.js" + ;; + *) + echo "Unknown scope $scope" + exit 1 + ;; + esac + done +else + scope_dirs="test/functional/**/*.spec.js test/e2e/**/*.spec.js" +fi + +cmd="DAPI_SEED=${DAPI_SEED}" + +if [ -n "$faucet_key" ] +then + cmd="${cmd} FAUCET_PRIVATE_KEY=${faucet_key}" +fi + +if [ -n "$network" ] +then + cmd="${cmd} NETWORK=${network}" +fi + +if [ -n "$skip_sync_before_height" ] +then + cmd="${cmd} SKIP_SYNC_BEFORE_HEIGHT=${skip_sync_before_height}" +fi + +if [ -n "$tld_contract_id" ] +then + cmd="${cmd} DPNS_CONTRACT_ID=${tld_contract_id}" +fi + +if [ -n "$tld_identity_id" ] +then + cmd="${cmd} DPNS_TOP_LEVEL_IDENTITY_ID=${tld_identity_id}" +fi + +if [ -n "$ff_identity_id" ] +then + cmd="${cmd} FEATURE_FLAGS_IDENTITY_ID=${ff_identity_id}" +fi + +if [ -n "$ff_contract_id" ] +then + cmd="${cmd} FEATURE_FLAGS_CONTRACT_ID=${ff_contract_id}" +fi + +if [ -n "$identity_private_key" ] +then + cmd="${cmd} DPNS_TOP_LEVEL_IDENTITY_PRIVATE_KEY=${identity_private_key}" +fi + +if [ -n "$faucet_wallet_use_storage" ] +then + cmd="${cmd} FAUCET_WALLET_USE_STORAGE=${faucet_wallet_use_storage}" +fi + +if [ -n "$faucet_wallet_storage_dir" ] +then + cmd="${cmd} FAUCET_WALLET_STORAGE_DIR=${faucet_wallet_storage_dir}" +fi + +if [ -n "$GITHUB_ACTIONS" ] +then + cmd="${cmd} NODE_ENV=test node_modules/.bin/mocha -b ${scope_dirs}" +else + echo $cmd + cmd="${cmd} NODE_ENV=test yarn mocha --inspect-brk -b ${scope_dirs}" +fi + +if [ -n "$timeout" ] +then + cmd="${cmd} --timeout ${timeout}" +fi + +eval $cmd diff --git a/packages/platform-test-suite/karma.conf.js b/packages/platform-test-suite/karma.conf.js new file mode 100644 index 00000000000..9816591b803 --- /dev/null +++ b/packages/platform-test-suite/karma.conf.js @@ -0,0 +1,82 @@ +const webpack = require('webpack'); +const dotenvResult = require('dotenv-safe').config(); + +const karmaMocha = require('karma-mocha'); +const karmaMochaReporter = require('karma-mocha-reporter'); +const karmaChai = require('karma-chai'); +const karmaChromeLauncher = require('karma-chrome-launcher'); +const karmaSourcemapLoader = require('karma-sourcemap-loader'); +const karmaWebpack = require('karma-webpack'); + +if (dotenvResult.error) { + throw dotenvResult.error; +} + +module.exports = (config) => { + config.set({ + client: { + mocha: { + timeout: 650000, + bail: true, + }, + }, + browserNoActivityTimeout: 900000, + browserDisconnectTimeout: 900000, + frameworks: ['mocha', 'chai', 'webpack'], + files: [ + 'lib/test/karma/loader.js', + './test/**/!(proofs|waitForStateTransitionResult).spec.js', + ], + preprocessors: { + 'lib/test/karma/loader.js': ['webpack', 'sourcemap'], + './test/**/!(proofs|waitForStateTransitionResult).spec.js': ['webpack', 'sourcemap'], + }, + webpack: { + mode: 'development', + devtool: 'inline-source-map', + plugins: [ + new webpack.ProvidePlugin({ + Buffer: [require.resolve('buffer/'), 'Buffer'], + process: require.resolve('process/browser'), + }), + new webpack.EnvironmentPlugin( + dotenvResult.parsed, + ), + ], + resolve: { + fallback: { + fs: false, + path: false, + net: false, + os: false, + http: false, + https: false, + assert: require.resolve('assert/'), + url: require.resolve('url/'), + string_decoder: require.resolve('string_decoder/'), + stream: require.resolve('stream-browserify'), + buffer: require.resolve('buffer/'), + crypto: require.resolve('crypto-browserify'), + events: require.resolve('events/'), + util: require.resolve('util/'), + }, + extensions: ['.ts', '.js', '.json'], + }, + }, + reporters: ['mocha'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + browsers: ['ChromeHeadless'], + singleRun: true, + concurrency: Infinity, + plugins: [ + karmaMocha, + karmaMochaReporter, + karmaChai, + karmaChromeLauncher, + karmaSourcemapLoader, + karmaWebpack, + ], + }); +}; diff --git a/packages/platform-test-suite/lib/parseRootTreeProof.js b/packages/platform-test-suite/lib/parseRootTreeProof.js new file mode 100644 index 00000000000..2863b4734a3 --- /dev/null +++ b/packages/platform-test-suite/lib/parseRootTreeProof.js @@ -0,0 +1,22 @@ +const BufferReader = require('@dashevo/dashcore-lib/lib/encoding/bufferreader'); + +module.exports = function parseRootTreeBuffer(rootTreeProofBuffer) { + /* + amount of subtrees to proof + It is equal to 1 because at the moment our proof won't be a simultaneous proof for more + than 1 tree, i.e. it's always identities OR documents OR contracts, not an AND in any case, + so there's always only 1 leaf to prove. The libraries that perform verification need to know + this, as this is equal to 1 in this particular case, usually it's not equal to 1. + */ + const bufferReader = new BufferReader(rootTreeProofBuffer); + + // const totalObjectsCount = bufferReader.readUInt32LE(); + const rootTreeProofHashesCount = bufferReader.readVarintNum(); + + const hashes = []; + for (let i = 0; i < rootTreeProofHashesCount; i++) { + hashes.push({ data: bufferReader.read(32) }); + } + + return hashes; +}; diff --git a/packages/platform-test-suite/lib/parseStoreTreeProof.js b/packages/platform-test-suite/lib/parseStoreTreeProof.js new file mode 100644 index 00000000000..f6b8a48048c --- /dev/null +++ b/packages/platform-test-suite/lib/parseStoreTreeProof.js @@ -0,0 +1,61 @@ +const hashLength = 32; + +module.exports = function getStoreProofData(storeProof) { + const buf = storeProof; + const hashes = []; + const keyValueHashes = []; + const values = []; + const keyValues = {}; + + let x = 0; + while (x < buf.length) { + const type = buf.readUInt8(x); + x += 1; + + switch (type) { + case 0x01: { // Hash + hashes.push(buf.slice(x, x + hashLength)); + x += hashLength; + break; + } + + case 0x02: { // Key/value hash + keyValueHashes.push(buf.slice(x, x + hashLength)); + x += hashLength; + break; + } + + case 0x03: { // Key / Value + const keySize = buf.readUInt8(x); + x += 1; + const key = buf.toString('hex', x, x + keySize); + x += keySize; + + const valueSize = buf.readUInt16BE(x); + x += 2; + + // Value + const valueHex = buf.toString('hex', x, x + valueSize); + const valueBuffer = Buffer.from(valueHex, 'hex'); + x += valueSize; + + keyValues[key] = valueBuffer; + values.push(valueBuffer); + break; + } + + case 0x10: // Parent + break; + + case 0x11: // Child + break; + + default: + throw new Error(`Unknown type: ${type.toString(16)}`); + } + } + + return { + hashes, keyValueHashes, values, keyValues, + }; +}; diff --git a/packages/platform-test-suite/lib/proofHashFunction.js b/packages/platform-test-suite/lib/proofHashFunction.js new file mode 100644 index 00000000000..12f5ef4dcfb --- /dev/null +++ b/packages/platform-test-suite/lib/proofHashFunction.js @@ -0,0 +1,18 @@ +const blake3Promise = require('blake3/dist/node'); + +// Including this file in the same file as merk segfaults the test, +// so webasm used instead +let blake3; +async function init() { + blake3 = await blake3Promise; +} + +/** + * @param {Buffer} data + * @return {Buffer} + */ +function hashFunction(data) { + return blake3.hash(data); +} + +module.exports = { init, hashFunction }; diff --git a/packages/platform-test-suite/lib/test/bootstrap.js b/packages/platform-test-suite/lib/test/bootstrap.js new file mode 100644 index 00000000000..c5856326049 --- /dev/null +++ b/packages/platform-test-suite/lib/test/bootstrap.js @@ -0,0 +1,31 @@ +const path = require('path'); +const dotenvSafe = require('dotenv-safe'); +const { expect, use } = require('chai'); +const dirtyChai = require('dirty-chai'); +const chaiAsPromised = require('chai-as-promised'); +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); + +use(chaiAsPromised); +use(dirtyChai); +use(sinonChai); + +process.env.NODE_ENV = 'test'; + +dotenvSafe.config({ + path: path.resolve(__dirname, '..', '..', '.env'), +}); + +beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + +afterEach(function afterEach() { + this.sinon.restore(); +}); + +global.expect = expect; diff --git a/packages/platform-test-suite/lib/test/createClientWithFundedWallet.js b/packages/platform-test-suite/lib/test/createClientWithFundedWallet.js new file mode 100644 index 00000000000..8ec34bb12ca --- /dev/null +++ b/packages/platform-test-suite/lib/test/createClientWithFundedWallet.js @@ -0,0 +1,69 @@ +const Dash = require('dash'); + +const fundWallet = require('@dashevo/wallet-lib/src/utils/fundWallet'); + +const { + contractId: dpnsContractId, +} = require('@dashevo/dpns-contract/lib/systemIds'); + +const getDAPISeeds = require('./getDAPISeeds'); + +const createFaucetClient = require('./createFaucetClient'); + +let faucetClient; + +/** + * Create and fund DashJS client + * @param {string} [HDPrivateKey] + * @param {number} [amount] - amount of Duffs to fund wallet with + * @returns {Promise} + */ +async function createClientWithFundedWallet(HDPrivateKey = undefined, amount = 100000) { + const useFaucetWalletStorage = process.env.FAUCET_WALLET_USE_STORAGE === 'true'; + const seeds = getDAPISeeds(); + + const clientOpts = { + seeds, + network: process.env.NETWORK, + apps: { + dpns: { + contractId: dpnsContractId, + }, + }, + }; + + if (!faucetClient || (faucetClient && useFaucetWalletStorage)) { + faucetClient = createFaucetClient(); + } + + const walletOptions = { + waitForInstantLockTimeout: 120000, + }; + + if (process.env.SKIP_SYNC_BEFORE_HEIGHT && HDPrivateKey) { + walletOptions.unsafeOptions = { + skipSynchronizationBeforeHeight: process.env.SKIP_SYNC_BEFORE_HEIGHT, + }; + } + + if (HDPrivateKey) { + walletOptions.HDPrivateKey = HDPrivateKey; + } else { + walletOptions.mnemonic = null; + } + + const client = new Dash.Client({ + ...clientOpts, + wallet: walletOptions, + }); + + await fundWallet(faucetClient.wallet, client.wallet, amount); + + if (useFaucetWalletStorage) { + await faucetClient.wallet.disconnect(); + } + + return client; +} + +module.exports = createClientWithFundedWallet; diff --git a/packages/platform-test-suite/lib/test/createClientWithoutWallet.js b/packages/platform-test-suite/lib/test/createClientWithoutWallet.js new file mode 100644 index 00000000000..186ce71509a --- /dev/null +++ b/packages/platform-test-suite/lib/test/createClientWithoutWallet.js @@ -0,0 +1,19 @@ +const Dash = require('dash'); + +const { contractId } = require('@dashevo/dpns-contract/lib/systemIds'); + +const getDAPISeeds = require('./getDAPISeeds'); + +function createClientWithoutWallet() { + return new Dash.Client({ + seeds: getDAPISeeds(), + network: process.env.NETWORK, + apps: { + dpns: { + contractId, + }, + }, + }); +} + +module.exports = createClientWithoutWallet; diff --git a/packages/platform-test-suite/lib/test/createFaucetClient.js b/packages/platform-test-suite/lib/test/createFaucetClient.js new file mode 100644 index 00000000000..99d94e70952 --- /dev/null +++ b/packages/platform-test-suite/lib/test/createFaucetClient.js @@ -0,0 +1,58 @@ +const Dash = require('dash'); + +let storageAdapter; + +if (typeof window === 'undefined') { + // eslint-disable-next-line global-require + const { NodeForage } = require('nodeforage'); + storageAdapter = new NodeForage({ + dir: process.env.FAUCET_WALLET_STORAGE_DIR || process.cwd(), + name: `faucet-wallet-${process.env.FAUCET_ADDRESS}`, + }); +} else { + // eslint-disable-next-line global-require + storageAdapter = require('localforage'); +} + +const { contractId } = require('@dashevo/dpns-contract/lib/systemIds'); + +const getDAPISeeds = require('./getDAPISeeds'); + +let faucetClient; + +function createFaucetClient() { + const seeds = getDAPISeeds(); + + const clientOpts = { + seeds, + network: process.env.NETWORK, + apps: { + dpns: { + contractId, + }, + }, + }; + + const walletOptions = { + privateKey: process.env.FAUCET_PRIVATE_KEY, + }; + + if (process.env.FAUCET_WALLET_USE_STORAGE === 'true') { + walletOptions.adapter = storageAdapter; + } + + if (process.env.SKIP_SYNC_BEFORE_HEIGHT) { + walletOptions.unsafeOptions = { + skipSynchronizationBeforeHeight: process.env.SKIP_SYNC_BEFORE_HEIGHT, + }; + } + + faucetClient = new Dash.Client({ + ...clientOpts, + wallet: walletOptions, + }); + + return faucetClient; +} + +module.exports = createFaucetClient; diff --git a/packages/platform-test-suite/lib/test/fixtures/getDataContractFixture.js b/packages/platform-test-suite/lib/test/fixtures/getDataContractFixture.js new file mode 100644 index 00000000000..550de7f9490 --- /dev/null +++ b/packages/platform-test-suite/lib/test/fixtures/getDataContractFixture.js @@ -0,0 +1,128 @@ +const Dash = require('dash'); + +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); + +const { + PlatformProtocol: { + DataContractFactory, + Identifier, + version, + }, +} = Dash; + +const randomOwnerId = generateRandomIdentifier(); + +/** + * + * @param {Identifier} [ownerId] + * @return {DataContract} + */ +module.exports = function getDataContractFixture( + ownerId = randomOwnerId, +) { + const documents = { + niceDocument: { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['$createdAt'], + additionalProperties: false, + }, + withByteArrays: { + type: 'object', + indices: [ + { + name: 'index1', + properties: [ + { byteArrayField: 'asc' }, + ], + }, + ], + properties: { + byteArrayField: { + type: 'array', + byteArray: true, + maxItems: 16, + }, + identifierField: { + type: 'array', + byteArray: true, + contentMediaType: Identifier.MEDIA_TYPE, + minItems: 32, + maxItems: 32, + }, + }, + required: ['byteArrayField'], + additionalProperties: false, + }, + indexedDocument: { + type: 'object', + indices: [ + { + name: 'index1', + properties: [ + { $ownerId: 'asc' }, + { firstName: 'asc' }, + ], + unique: true, + }, + { + name: 'index2', + properties: [ + { $ownerId: 'asc' }, + { lastName: 'asc' }, + ], + unique: true, + }, + { + name: 'index3', + properties: [ + { lastName: 'asc' }, + ], + }, + { + name: 'index4', + properties: [ + { $createdAt: 'asc' }, + { $updatedAt: 'asc' }, + ], + }, + { + name: 'index5', + properties: [ + { $updatedAt: 'asc' }, + ], + }, + { + name: 'index6', + properties: [ + { $createdAt: 'asc' }, + ], + }, + ], + properties: { + firstName: { + type: 'string', + maxLength: 63, + }, + lastName: { + type: 'string', + maxLength: 63, + }, + }, + required: ['firstName', '$createdAt', '$updatedAt', 'lastName'], + additionalProperties: false, + }, + }; + + const dpp = { + getProtocolVersion: () => version, + }; + + const factory = new DataContractFactory(dpp, () => {}); + + return factory.create(ownerId, documents); +}; diff --git a/packages/platform-test-suite/lib/test/fixtures/getIdentityFixture.js b/packages/platform-test-suite/lib/test/fixtures/getIdentityFixture.js new file mode 100644 index 00000000000..51fdd61155c --- /dev/null +++ b/packages/platform-test-suite/lib/test/fixtures/getIdentityFixture.js @@ -0,0 +1,45 @@ +const Dash = require('dash'); + +const generateRandomIdentifier = require('../utils/generateRandomIdentifier'); + +const { + PlatformProtocol: { + Identity, + IdentityPublicKey, + version, + }, +} = Dash; + +const id = generateRandomIdentifier(); + +/** + * @return {Identity} + */ +module.exports = function getIdentityFixture() { + const rawIdentity = { + protocolVersion: version, + id: id.toBuffer(), + balance: 10, + revision: 0, + publicKeys: [ + { + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: Buffer.from('AuryIuMtRrl/VviQuyLD1l4nmxi9ogPzC9LT7tdpo0di', 'base64'), + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + }, + { + id: 1, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: Buffer.from('A8AK95PYMVX5VQKzOhcVQRCUbc9pyg3RiL7jttEMDU+L', 'base64'), + purpose: IdentityPublicKey.PURPOSES.ENCRYPTION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MEDIUM, + readOnly: false, + }, + ], + }; + + return new Identity(rawIdentity); +}; diff --git a/packages/platform-test-suite/lib/test/getDAPISeeds.js b/packages/platform-test-suite/lib/test/getDAPISeeds.js new file mode 100644 index 00000000000..7fe23ddaa67 --- /dev/null +++ b/packages/platform-test-suite/lib/test/getDAPISeeds.js @@ -0,0 +1,15 @@ +function getDAPISeeds() { + return process.env.DAPI_SEED + .split(',') + .map((seed) => { + const [host, httpPort, grpcPort] = seed.split(':'); + + return { + host, + httpPort, + grpcPort, + }; + }); +} + +module.exports = getDAPISeeds; diff --git a/packages/platform-test-suite/lib/test/karma/bootstrap.js b/packages/platform-test-suite/lib/test/karma/bootstrap.js new file mode 100644 index 00000000000..6c9c8349707 --- /dev/null +++ b/packages/platform-test-suite/lib/test/karma/bootstrap.js @@ -0,0 +1,23 @@ +const { expect, use } = require('chai'); +const dirtyChai = require('dirty-chai'); +const chaiAsPromised = require('chai-as-promised'); +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); + +use(chaiAsPromised); +use(dirtyChai); +use(sinonChai); + +beforeEach(function beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } +}); + +afterEach(function afterEach() { + this.sinon.restore(); +}); + +global.expect = expect; diff --git a/packages/platform-test-suite/lib/test/karma/loader.js b/packages/platform-test-suite/lib/test/karma/loader.js new file mode 100644 index 00000000000..96bd0e240b0 --- /dev/null +++ b/packages/platform-test-suite/lib/test/karma/loader.js @@ -0,0 +1,11 @@ +// This file is used for compiling tests with webpack into one file for using with karma +require('./bootstrap'); + +// noinspection JSUnresolvedFunction +const testsContext = require.context('../../../test', true, /^.+\.spec\.js$/); + +testsContext.keys() + // Ignore proofs.spec.js because it uses Merk native Node.JS module + .filter((path) => !path.includes('proofs.spec.js')) + .filter((path) => !path.includes('waitForStateTransitionResult.spec.js')) + .forEach(testsContext); diff --git a/packages/platform-test-suite/lib/test/testProofStructure.js b/packages/platform-test-suite/lib/test/testProofStructure.js new file mode 100644 index 00000000000..33bc0fa2762 --- /dev/null +++ b/packages/platform-test-suite/lib/test/testProofStructure.js @@ -0,0 +1,19 @@ +function testProofStructure(expect, proof, proofExist = true) { + expect(proof).to.exist(); + + expect(proof.merkleProof).to.be.an.instanceof(Buffer); + + if (proofExist) { + expect(proof.merkleProof.length).to.be.greaterThan(0); + } else { + expect(proof.merkleProof.length).to.be.equal(0); + } + + expect(proof.signatureLLMQHash).to.be.an.instanceof(Buffer); + expect(proof.signatureLLMQHash.length).to.be.equal(32); + + expect(proof.signature).to.be.an.instanceof(Buffer); + expect(proof.signature.length).to.be.equal(96); +} + +module.exports = testProofStructure; diff --git a/packages/platform-test-suite/lib/test/throwGrpcErrorWithMetadata.js b/packages/platform-test-suite/lib/test/throwGrpcErrorWithMetadata.js new file mode 100644 index 00000000000..a04af511902 --- /dev/null +++ b/packages/platform-test-suite/lib/test/throwGrpcErrorWithMetadata.js @@ -0,0 +1,14 @@ +const { inspect } = require('util'); + +/** + * @param {Error} e + */ +function throwGrpcErrorWithMetadata(e) { + if (e.metadata) { + e.message = `${e.message}\n\nMetadata:\n${inspect(e.metadata.getMap())}`; + } + + throw e; +} + +module.exports = throwGrpcErrorWithMetadata; diff --git a/packages/platform-test-suite/lib/test/utils/generateRandomIdentifier.js b/packages/platform-test-suite/lib/test/utils/generateRandomIdentifier.js new file mode 100644 index 00000000000..84ea8336965 --- /dev/null +++ b/packages/platform-test-suite/lib/test/utils/generateRandomIdentifier.js @@ -0,0 +1,15 @@ +const crypto = require('crypto'); +const Dash = require('dash'); + +const { PlatformProtocol: { Identifier } } = Dash; + +/** + * Generate random identity ID + * + * @return {Identifier} + */ +function generateRandomIdentifier() { + return new Identifier(crypto.randomBytes(32)); +} + +module.exports = generateRandomIdentifier; diff --git a/packages/platform-test-suite/lib/test/waitForBalanceToChange.js b/packages/platform-test-suite/lib/test/waitForBalanceToChange.js new file mode 100644 index 00000000000..db8a733e8a1 --- /dev/null +++ b/packages/platform-test-suite/lib/test/waitForBalanceToChange.js @@ -0,0 +1,23 @@ +const wait = require('../wait'); + +const MAX_TIME_TO_WAIT_MS = 20000; +const ITERATION_TIME_MS = 500; +const NUMBER_OF_ITERATIONS = MAX_TIME_TO_WAIT_MS / ITERATION_TIME_MS; + +/** + * Wait for account balance to change + * + * @param {Account} walletAccount + */ +async function waitForBalanceToChange(walletAccount) { + const originalBalance = walletAccount.getTotalBalance(); + + let currentIteration = 0; + while (walletAccount.getTotalBalance() === originalBalance + && currentIteration <= NUMBER_OF_ITERATIONS) { + await wait(ITERATION_TIME_MS); + currentIteration++; + } +} + +module.exports = waitForBalanceToChange; diff --git a/packages/platform-test-suite/lib/wait.js b/packages/platform-test-suite/lib/wait.js new file mode 100644 index 00000000000..eed4896890a --- /dev/null +++ b/packages/platform-test-suite/lib/wait.js @@ -0,0 +1,3 @@ +module.exports = function wait(ms) { + return new Promise((res) => setTimeout(res, ms)); +}; diff --git a/packages/platform-test-suite/lib/waitForBlocks.js b/packages/platform-test-suite/lib/waitForBlocks.js new file mode 100644 index 00000000000..4f0be6d4bf0 --- /dev/null +++ b/packages/platform-test-suite/lib/waitForBlocks.js @@ -0,0 +1,20 @@ +const wait = require('./wait'); + +/** + * + * @param {DAPIClient} dapiClient + * @param {number} numberOfBlocks + * @return {Promise} + */ +module.exports = async function waitForBlocks(dapiClient, numberOfBlocks) { + let { chain: { blocksCount: currentBlockHeight } } = await dapiClient.core.getStatus(); + + const desiredBlockHeight = currentBlockHeight + numberOfBlocks; + do { + ({ chain: { blocksCount: currentBlockHeight } } = await dapiClient.core.getStatus()); + + if (currentBlockHeight < desiredBlockHeight) { + await wait(5000); + } + } while (currentBlockHeight < desiredBlockHeight); +}; diff --git a/packages/platform-test-suite/package.json b/packages/platform-test-suite/package.json new file mode 100644 index 00000000000..3848b1c5442 --- /dev/null +++ b/packages/platform-test-suite/package.json @@ -0,0 +1,81 @@ +{ + "name": "@dashevo/platform-test-suite", + "private": true, + "version": "0.23.0-dev.4", + "description": "Dash Network end-to-end tests", + "scripts": { + "test": "mocha -b './test/**/*.spec.js'", + "lint": "eslint .", + "test:e2e": "NODE_ENV=test mocha 'test/e2e/**/*.spec.js'", + "test:functional": "NODE_ENV=test mocha 'test/functional/**/*.spec.js'", + "test:browsers": "karma start ./karma.conf.js" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/dashevo/platform-test-suite.git" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/dashevo/platform-test-suite/issues" + }, + "homepage": "https://github.com/dashevo/platform-test-suite#readme", + "dependencies": { + "@dashevo/dapi-client": "workspace:~", + "@dashevo/dashcore-lib": "~0.19.39", + "@dashevo/dpns-contract": "workspace:~", + "@dashevo/dpp": "workspace:~", + "@dashevo/feature-flags-contract": "workspace:~", + "@dashevo/grpc-common": "workspace:~", + "@dashevo/masternode-reward-shares-contract": "workspace:~", + "@dashevo/merk": "github:dashevo/node-merk#eb37003300d22c6c04604463bcd7e861dd07000f", + "@dashevo/wallet-lib": "workspace:~", + "assert": "^2.0.0", + "assert-browserify": "^2.0.0", + "blake3": "^2.1.4", + "browserify-zlib": "^0.2.0", + "buffer": "^6.0.3", + "bufferutil": "^4.0.6", + "chai": "^4.3.4", + "chai-as-promised": "^7.1.1", + "crypto-browserify": "^3.12.0", + "dash": "workspace:~", + "dirty-chai": "^2.0.1", + "dotenv-safe": "^8.2.0", + "events": "^3.3.0", + "github-api": "^3.3.0", + "https-browserify": "^1.0.0", + "js-merkle": "^0.1.5", + "karma": "^6.3.4", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^3.1.0", + "karma-firefox-launcher": "^2.1.1", + "karma-mocha": "^2.0.1", + "karma-mocha-reporter": "^2.2.5", + "karma-sourcemap-loader": "^0.3.7", + "karma-webpack": "^5.0.0", + "localforage": "^1.10.0", + "mocha": "^9.1.2", + "net": "^1.0.2", + "nodeforage": "^1.1.2", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "semver": "^7.3.2", + "sinon": "^11.1.2", + "sinon-chai": "^3.7.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "string_decoder": "^1.3.0", + "tls": "^0.0.1", + "url": "^0.11.0", + "utf-8-validate": "^5.0.9", + "util": "^0.12.4", + "webpack": "^5.59.1", + "ws": "^7.5.3" + }, + "devDependencies": { + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2" + } +} diff --git a/packages/platform-test-suite/test/.eslintrc b/packages/platform-test-suite/test/.eslintrc new file mode 100644 index 00000000000..720ced73852 --- /dev/null +++ b/packages/platform-test-suite/test/.eslintrc @@ -0,0 +1,12 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + }, + "globals": { + "expect": true + } +} diff --git a/packages/platform-test-suite/test/e2e/contacts.spec.js b/packages/platform-test-suite/test/e2e/contacts.spec.js new file mode 100644 index 00000000000..17f04e8c382 --- /dev/null +++ b/packages/platform-test-suite/test/e2e/contacts.spec.js @@ -0,0 +1,307 @@ +const Identifier = require('@dashevo/dpp/lib/Identifier'); + +const createClientWithFundedWallet = require('../../lib/test/createClientWithFundedWallet'); +const wait = require('../../lib/wait'); + +describe('e2e', () => { + describe('Contacts', function contacts() { + this.timeout(950000); + + let dataContract; + + let bobClient; + let aliceClient; + + let bobIdentity; + let bobContactRequest; + let aliceIdentity; + let aliceProfile; + let aliceContactAcceptance; + + let dataContractDocumentSchemas; + + before(() => { + dataContractDocumentSchemas = { + profile: { + type: 'object', + indices: [ + { + name: 'ownerId', + properties: [{ $ownerId: 'asc' }], + unique: true, + }, + ], + properties: { + avatarUrl: { + type: 'string', + format: 'url', + maxLength: 255, + }, + about: { + type: 'string', + maxLength: 255, + }, + }, + required: ['avatarUrl', 'about'], + additionalProperties: false, + }, + contact: { + type: 'object', + indices: [ + { + name: 'onwerIdToUserId', + properties: [ + { $ownerId: 'asc' }, + { toUserId: 'asc' }, + ], + unique: true, + }, + ], + properties: { + toUserId: { + type: 'array', + byteArray: true, + contentMediaType: Identifier.MEDIA_TYPE, + minItems: 32, + maxItems: 32, + }, + publicKey: { + type: 'array', + byteArray: true, + maxItems: 33, + }, + }, + required: ['toUserId', 'publicKey'], + additionalProperties: false, + }, + }; + }); + + after(async () => { + if (bobClient) { + await bobClient.disconnect(); + } + + if (aliceClient) { + await aliceClient.disconnect(); + } + }); + + describe('Bob', () => { + it('should create user wallet and identity', async () => { + // Create Bob wallet + bobClient = await createClientWithFundedWallet(); + + bobIdentity = await bobClient.platform.identities.register(40000); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + expect(bobIdentity.constructor.name).to.be.equal('Identity'); + }); + + it('should publish "Contacts" data contract', async () => { + // 1. Create and broadcast data contract + dataContract = await bobClient.platform.contracts.create( + dataContractDocumentSchemas, bobIdentity, + ); + + await bobClient.platform.contracts.publish(dataContract, bobIdentity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + bobClient.getApps().set('contacts', { + contractId: dataContract.getId(), + contract: dataContract, + }); + + // 2. Fetch and check data contract + const fetchedDataContract = await bobClient.platform.contracts.get( + dataContract.getId(), + ); + + expect(fetchedDataContract.toJSON()).to.be.deep.equal(dataContract.toJSON()); + }); + + it('should create profile in "Contacts" app', async () => { + // 1. Create and broadcast profile + const profile = await bobClient.platform.documents.create('contacts.profile', bobIdentity, { + avatarUrl: 'http://test.com/bob.jpg', + about: 'This is story about me', + }); + + await bobClient.platform.documents.broadcast({ + create: [profile], + }, bobIdentity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + // 2. Fetch and compare profiles + const [fetchedProfile] = await bobClient.platform.documents.get( + 'contacts.profile', + { where: [['$id', '==', profile.getId()]] }, + ); + + expect(fetchedProfile.toJSON()).to.be.deep.equal(profile.toJSON()); + }); + }); + + describe('Alice', () => { + it('should create user wallet and identity', async () => { + // Create Alice wallet + aliceClient = await createClientWithFundedWallet(); + + aliceClient.getApps().set('contacts', { + contractId: dataContract.getId(), + contract: dataContract, + }); + + aliceIdentity = await aliceClient.platform.identities.register(40000); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + expect(aliceIdentity.constructor.name).to.be.equal('Identity'); + }); + + it('should create profile in "Contacts" app', async () => { + // 1. Create and broadcast profile + aliceProfile = await aliceClient.platform.documents.create('contacts.profile', aliceIdentity, { + avatarUrl: 'http://test.com/alice.jpg', + about: 'I am Alice', + }); + + await aliceClient.platform.documents.broadcast({ + create: [aliceProfile], + }, aliceIdentity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + // 2. Fetch and compare profile + const [fetchedProfile] = await aliceClient.platform.documents.get( + 'contacts.profile', + { where: [['$id', '==', aliceProfile.getId()]] }, + ); + + expect(fetchedProfile.toJSON()).to.be.deep.equal(aliceProfile.toJSON()); + }); + + it('should be able to update her profile', async () => { + // 1. Update profile document + aliceProfile.set('avatarUrl', 'http://test.com/alice2.jpg'); + + // 2. Broadcast change + await aliceClient.platform.documents.broadcast({ + replace: [aliceProfile], + }, aliceIdentity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + // 3. Fetch and compare profile + const [fetchedProfile] = await aliceClient.platform.documents.get( + 'contacts.profile', + { where: [['$id', '==', aliceProfile.getId()]] }, + ); + + expect(fetchedProfile.toJSON()).to.be.deep.equal({ + ...aliceProfile.toJSON(), + $revision: 2, + }); + }); + }); + + describe('Bob', () => { + it('should be able to send contact request', async () => { + // 1. Create and broadcast contact document + bobContactRequest = await bobClient.platform.documents.create('contacts.contact', bobIdentity, { + toUserId: aliceIdentity.getId(), + publicKey: bobIdentity.getPublicKeyById(0).getData(), + }); + + await bobClient.platform.documents.broadcast({ + create: [bobContactRequest], + }, bobIdentity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + // 2. Fetch and compare contacts + const [fetchedContactRequest] = await bobClient.platform.documents.get( + 'contacts.contact', + { where: [['$id', '==', bobContactRequest.getId()]] }, + ); + + expect(fetchedContactRequest.toJSON()).to.be.deep.equal(bobContactRequest.toJSON()); + }); + }); + + describe('Alice', () => { + it('should be able to approve contact request', async () => { + // 1. Create and broadcast contact approval document + aliceContactAcceptance = await aliceClient.platform.documents.create( + 'contacts.contact', aliceIdentity, { + toUserId: bobIdentity.getId(), + publicKey: aliceIdentity.getPublicKeyById(0).getData(), + }, + ); + + await aliceClient.platform.documents.broadcast({ + create: [aliceContactAcceptance], + }, aliceIdentity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + // 2. Fetch and compare contacts + const [fetchedAliceContactAcceptance] = await aliceClient.platform.documents.get( + 'contacts.contact', + { where: [['$id', '==', aliceContactAcceptance.getId()]] }, + ); + + expect(fetchedAliceContactAcceptance.toJSON()).to.be.deep.equal( + aliceContactAcceptance.toJSON(), + ); + }); + + it('should be able to remove contact approval', async () => { + // 1. Broadcast document deletion + await aliceClient.platform.documents.broadcast({ + delete: [aliceContactAcceptance], + }, aliceIdentity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + // 2. Fetch contact documents and check it does not exists + const [fetchedAliceContactAcceptance] = await aliceClient.platform.documents.get( + 'contacts.contact', + { where: [['$id', '==', aliceContactAcceptance.getId()]] }, + ); + + expect(fetchedAliceContactAcceptance).to.not.exist(); + }); + }); + }); +}); diff --git a/packages/platform-test-suite/test/e2e/dpns.spec.js b/packages/platform-test-suite/test/e2e/dpns.spec.js new file mode 100644 index 00000000000..bacf561812b --- /dev/null +++ b/packages/platform-test-suite/test/e2e/dpns.spec.js @@ -0,0 +1,257 @@ +const crypto = require('crypto'); + +const { + contractId: dpnsContractId, + ownerId: dpnsOwnerId, +} = require('@dashevo/dpns-contract/lib/systemIds'); + +const createClientWithFundedWallet = require('../../lib/test/createClientWithFundedWallet'); + +const wait = require('../../lib/wait'); + +const getRandomDomain = () => crypto.randomBytes(10).toString('hex'); + +describe('DPNS', () => { + let failed = false; + let client; + let identity; + let topLevelDomain; + let secondLevelDomain; + let registeredDomain; + + // Skip test if any prior test in this describe failed + beforeEach(function beforeEach() { + if (failed) { + this.skip(); + } + }); + + afterEach(function afterEach() { + failed = this.currentTest.state === 'failed'; + }); + + before(async () => { + topLevelDomain = 'dash'; + secondLevelDomain = getRandomDomain(); + client = await createClientWithFundedWallet(undefined, 1000000); + + await client.platform.identities.topUp(dpnsOwnerId, 200000); + }); + + after(async () => { + await client.disconnect(); + }); + + describe('Data contract', () => { + it('should exists', async () => { + const createdDataContract = await client.platform.contracts.get(dpnsContractId); + + expect(createdDataContract).to.exist(); + expect(createdDataContract.getId().toString()).to.equal(dpnsContractId); + }); + }); + + describe('DPNS owner', () => { + let createdTLD; + let newTopLevelDomain; + let ownerClient; + + before(async () => { + ownerClient = await createClientWithFundedWallet( + process.env.DPNS_OWNER_PRIVATE_KEY, + ); + + newTopLevelDomain = getRandomDomain(); + identity = await ownerClient.platform.identities.get(dpnsOwnerId); + + expect(identity).to.exist(); + await ownerClient.platform.identities.topUp(dpnsOwnerId, 5); + }); + + after(async () => { + await ownerClient.disconnect(); + }); + + // generate a random one which will be used in tests above + // skip if DPNS owner private key is not passed and use `dash` in tests above + it('should be able to register a TLD', async () => { + createdTLD = await ownerClient.platform.names.register(newTopLevelDomain, { + dashAliasIdentityId: identity.getId(), + }, identity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + expect(createdTLD).to.exist(); + expect(createdTLD.getType()).to.equal('domain'); + expect(createdTLD.getData().label).to.equal(newTopLevelDomain); + expect(createdTLD.getData().normalizedParentDomainName).to.equal(''); + }); + + it('should not be able to update domain', async () => { + createdTLD.set('label', 'anotherlabel'); + + let broadcastError; + + try { + await ownerClient.platform.documents.broadcast({ + replace: [createdTLD], + }, identity); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.exist(); + expect(broadcastError.message).to.be.equal('Action is not allowed'); + expect(broadcastError.code).to.equal(4001); + }); + + it('should not be able to delete domain', async () => { + let broadcastError; + + try { + await ownerClient.platform.documents.broadcast({ + delete: [createdTLD], + }, identity); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.exist(); + expect(broadcastError.message).to.be.equal('Action is not allowed'); + expect(broadcastError.code).to.equal(4001); + }); + }); + + describe('Any Identity', () => { + before(async () => { + identity = await client.platform.identities.register(200000); + }); + + after(async () => { + await client.disconnect(); + }); + + it('should not be able to register TLD', async () => { + let broadcastError; + + try { + await client.platform.names.register(getRandomDomain(), { + dashAliasIdentityId: identity.getId(), + }, identity); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.exist(); + expect(broadcastError.message).to.be.equal('Can\'t create top level domain for this identity'); + expect(broadcastError.code).to.equal(4001); + }); + + it('should be able to register a second level domain', async () => { + registeredDomain = await client.platform.names.register(`${secondLevelDomain}.${topLevelDomain}`, { + dashUniqueIdentityId: identity.getId(), + }, identity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + expect(registeredDomain.getType()).to.equal('domain'); + expect(registeredDomain.getData().label).to.equal(secondLevelDomain); + expect(registeredDomain.getData().normalizedParentDomainName).to.equal(topLevelDomain); + }); + + it('should not be able to register a subdomain for parent domain which is not exist', async () => { + let broadcastError; + + try { + const domain = `${getRandomDomain()}.${getRandomDomain()}.${topLevelDomain}`; + + await client.platform.names.register(domain, { + dashAliasIdentityId: identity.getId(), + }, identity); + + expect.fail('should throw error'); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.exist(); + expect(broadcastError.message).to.be.equal('Parent domain is not present'); + expect(broadcastError.code).to.equal(4001); + }); + + it('should be able to search a domain', async () => { + const documents = await client.platform.names.search(secondLevelDomain, topLevelDomain); + + expect(documents).to.have.lengthOf(1); + + const [document] = documents; + + expect(document.toJSON()).to.deep.equal(registeredDomain.toJSON()); + }); + + it('should be able to resolve domain by it\'s name', async () => { + const document = await client.platform.names.resolve(`${secondLevelDomain}.${topLevelDomain}`); + + expect(document.toJSON()).to.deep.equal(registeredDomain.toJSON()); + }); + + it('should be able to resolve domain by it\'s record', async () => { + const [document] = await client.platform.names.resolveByRecord( + 'dashUniqueIdentityId', + registeredDomain.getData().records.dashUniqueIdentityId, + ); + + expect(document.toJSON()).to.deep.equal(registeredDomain.toJSON()); + }); + + it('should not be able to update domain', async () => { + registeredDomain.set('label', 'newlabel'); + + let broadcastError; + + try { + await client.platform.documents.broadcast({ + replace: [registeredDomain], + }, identity); + + expect.fail('should throw an error'); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.exist(); + expect(broadcastError.message).to.be.equal('Action is not allowed'); + expect(broadcastError.code).to.equal(4001); + }); + + it('should not be able to delete domain', async () => { + let broadcastError; + + try { + await client.platform.documents.broadcast({ + delete: [registeredDomain], + }, identity); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.exist(); + expect(broadcastError.message).to.be.equal('Action is not allowed'); + expect(broadcastError.code).to.equal(4001); + }); + + it('should not be able to register two domains with same `dashAliasIdentityId` record'); + + it('should be able to register many domains with same `dashAliasIdentityId` record'); + + it('should not be able to update preorder'); + + it('should not be able to domain preorder'); + }); +}); diff --git a/packages/platform-test-suite/test/e2e/masternodeRewardShares.spec.js b/packages/platform-test-suite/test/e2e/masternodeRewardShares.spec.js new file mode 100644 index 00000000000..4b4061ad93f --- /dev/null +++ b/packages/platform-test-suite/test/e2e/masternodeRewardShares.spec.js @@ -0,0 +1,398 @@ +const Dash = require('dash'); + +const { + contractId: masternodeRewardSharesContractId, + ownerId: masternodeRewardSharesOwnerId, +} = require('@dashevo/masternode-reward-shares-contract/lib/systemIds'); + +const generateRandomIdentifier = require('../../lib/test/utils/generateRandomIdentifier'); + +const createClientWithFundedWallet = require('../../lib/test/createClientWithFundedWallet'); +const wait = require('../../lib/wait'); + +const { PlatformProtocol: { IdentityPublicKey } } = Dash; + +describe('Masternode Reward Shares', () => { + let failed = false; + let client; + let dpp; + + before(async () => { + dpp = new Dash.PlatformProtocol(); + await dpp.initialize(); + + client = await createClientWithFundedWallet( + process.env.MASTERNODE_REWARD_SHARES_OWNER_PRIVATE_KEY, + 200000, + ); + + await client.platform.identities.topUp(masternodeRewardSharesOwnerId, 50000); + + const masternodeRewardSharesContract = await client.platform.contracts.get( + masternodeRewardSharesContractId, + ); + + client.getApps().set('masternodeRewardShares', { + contractId: masternodeRewardSharesContractId, + contract: masternodeRewardSharesContract, + }); + }); + + // Skip test if any prior test in this describe failed + beforeEach(function beforeEach() { + if (failed) { + this.skip(); + } + }); + + afterEach(function afterEach() { + failed = this.currentTest.state === 'failed'; + }); + + after(async () => { + if (client) { + await client.disconnect(); + } + }); + + describe('Data Contract', () => { + it('should exists', async () => { + const createdDataContract = await client.platform.contracts.get( + masternodeRewardSharesContractId, + ); + + expect(createdDataContract).to.exist(); + + expect(createdDataContract.getId().toString()).to.equal( + masternodeRewardSharesContractId, + ); + }); + }); + + describe('Masternode owner', () => { + let anotherIdentity; + let rewardShare; + let anotherRewardShare; + let ownerPrivateKey; + let masternodeIdentity; + let derivedPrivateKey; + let signaturePublicKeyId; + + before(async function before() { + if (!process.env.MASTERNODE_REWARD_SHARES_OWNER_PRIVATE_KEY + || !process.env.MASTERNODE_REWARD_SHARES_OWNER_PRO_REG_TX_HASH + || !process.env.MASTERNODE_REWARD_SHARES_MN_OWNER_PRIVATE_KEY) { + this.skip('masternode owner credentials are not set'); + } + + const ownerIdentifier = Buffer.from(process.env.MASTERNODE_REWARD_SHARES_OWNER_PRO_REG_TX_HASH, 'hex'); + + masternodeIdentity = await client.platform.identities.get(ownerIdentifier); + + ownerPrivateKey = process.env.MASTERNODE_REWARD_SHARES_MN_OWNER_PRIVATE_KEY; + + // Masternode identity should exist + expect(masternodeIdentity).to.exist(); + + await client.platform.identities.topUp(masternodeIdentity.getId(), 50000); + + // Since we cannot create "High" level key for masternode Identities automatically, + // (this key is used to sign state transitions, other than "update") + // we add this key here + const account = await client.platform.client.getWalletAccount(); + + const identityIndex = await account.getUnusedIdentityIndex(); + + ({ privateKey: derivedPrivateKey } = account + .identities + .getIdentityHDKeyByIndex(identityIndex, 1)); + + const identityPublicKey = derivedPrivateKey.toPublicKey().toBuffer(); + + signaturePublicKeyId = masternodeIdentity.getPublicKeyMaxId() + 1; + + const newPublicKey = new IdentityPublicKey( + { + id: signaturePublicKeyId, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.HIGH, + data: identityPublicKey, + readOnly: false, + }, + ); + + const update = { + add: [newPublicKey], + }; + + const stateTransition = dpp.identity.createIdentityUpdateTransition( + masternodeIdentity, + update, + ); + + const signerKey = masternodeIdentity.getPublicKeys()[0]; + + const promises = stateTransition.getPublicKeysToAdd().map(async (publicKey) => { + // const privateKey = privateKeys[publicKey.getId()]; + + stateTransition.setSignaturePublicKeyId(signerKey.getId()); + + await stateTransition.signByPrivateKey(derivedPrivateKey, publicKey.getType()); + + publicKey.setSignature(stateTransition.getSignature()); + + stateTransition.setSignature(undefined); + stateTransition.setSignaturePublicKeyId(undefined); + }); + + await Promise.all(promises); + + stateTransition.setSignaturePublicKeyId(0); + + await stateTransition.signByPrivateKey( + ownerPrivateKey, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + await client.platform.broadcastStateTransition( + stateTransition, + ); + }); + + it('should be able to create reward shares with existing identity', async () => { + anotherIdentity = await client.platform.identities.register(7000); + + rewardShare = await client.platform.documents.create( + 'masternodeRewardShares.rewardShare', + masternodeIdentity, + { + payToId: anotherIdentity.getId(), + percentage: 1, + }, + ); + + const stateTransition = dpp.document.createStateTransition({ + create: [rewardShare], + }); + + stateTransition.setSignaturePublicKeyId(signaturePublicKeyId); + + await stateTransition.signByPrivateKey( + derivedPrivateKey, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + await client.platform.broadcastStateTransition( + stateTransition, + ); + }); + + it('should not be able to create reward shares with non-existing identity', async () => { + const payToId = generateRandomIdentifier(); + + const invalidRewardShare = await client.platform.documents.create( + 'masternodeRewardShares.rewardShare', + masternodeIdentity, + { + payToId, + percentage: 1, + }, + ); + + const stateTransition = dpp.document.createStateTransition({ + create: [invalidRewardShare], + }); + + stateTransition.setSignaturePublicKeyId(signaturePublicKeyId); + + await stateTransition.signByPrivateKey( + derivedPrivateKey, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + try { + await client.platform.broadcastStateTransition( + stateTransition, + ); + + expect.fail('should throw broadcast error'); + } catch (e) { + expect(e.message).to.be.equal(`Identity ${payToId} doesn't exist`); + expect(e.code).to.equal(4001); + } + }); + + it('should be able to update reward shares with existing identity', async () => { + rewardShare.set('percentage', 2); + + const stateTransition = dpp.document.createStateTransition({ + replace: [rewardShare], + }); + + stateTransition.setSignaturePublicKeyId(signaturePublicKeyId); + + await stateTransition.signByPrivateKey( + derivedPrivateKey, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + await client.platform.broadcastStateTransition( + stateTransition, + ); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + const [updatedRewardShare] = await client.platform.documents.get('masternodeRewardShares.rewardShare', { + where: [['$id', '==', rewardShare.getId()]], + }); + + expect(updatedRewardShare).to.exists(); + + expect(updatedRewardShare.get('percentage')).equals(2); + }); + + it('should not be able to update reward shares with non-existing identity', async () => { + const payToId = generateRandomIdentifier(); + + [rewardShare] = await client.platform.documents.get( + 'masternodeRewardShares.rewardShare', + { where: [['$id', '==', rewardShare.getId()]] }, + ); + + rewardShare.set('payToId', payToId); + + const stateTransition = dpp.document.createStateTransition({ + replace: [rewardShare], + }); + + stateTransition.setSignaturePublicKeyId(signaturePublicKeyId); + + await stateTransition.signByPrivateKey( + derivedPrivateKey, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + try { + await client.platform.broadcastStateTransition( + stateTransition, + ); + + expect.fail('should throw broadcast error'); + } catch (e) { + expect(e.message).to.be.equal(`Identity ${payToId} doesn't exist`); + expect(e.code).to.equal(4001); + } + }); + + it('should not be able to share more than 100% of rewards', async () => { + anotherIdentity = await client.platform.identities.register(7000); + + anotherRewardShare = await client.platform.documents.create( + 'masternodeRewardShares.rewardShare', + masternodeIdentity, + { + payToId: anotherIdentity.getId(), + percentage: 9999, // it will be 10001 in summary + }, + ); + + const stateTransition = dpp.document.createStateTransition({ + create: [anotherRewardShare], + }); + + stateTransition.setSignaturePublicKeyId(signaturePublicKeyId); + + await stateTransition.signByPrivateKey( + derivedPrivateKey, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + try { + await client.platform.broadcastStateTransition( + stateTransition, + ); + + expect.fail('should throw broadcast error'); + } catch (e) { + expect(e.message).to.be.equal('Percentage can not be more than 10000'); + expect(e.code).to.equal(4001); + } + }); + + it('should be able to remove reward shares', async () => { + const stateTransition = dpp.document.createStateTransition({ + delete: [rewardShare], + }); + + stateTransition.setSignaturePublicKeyId(signaturePublicKeyId); + + await stateTransition.signByPrivateKey( + derivedPrivateKey, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + await client.platform.broadcastStateTransition( + stateTransition, + ); + + const [storedDocument] = await client.platform.documents.get( + 'masternodeRewardShares.rewardShare', + { where: [['$id', '==', rewardShare.getId()]] }, + ); + + expect(storedDocument).to.not.exist(); + }); + }); + + describe('Any Identity', () => { + let identity; + + before(async () => { + identity = await client.platform.identities.register(40000); + }); + + it('should not be able to share rewards', async () => { + const rewardShare = await client.platform.documents.create( + 'masternodeRewardShares.rewardShare', + identity, + { + payToId: generateRandomIdentifier(), + percentage: 1, + }, + ); + + const stateTransition = dpp.document.createStateTransition({ + create: [rewardShare], + }); + + stateTransition.setSignaturePublicKeyId(1); + + const account = await client.getWalletAccount(); + + const { privateKey } = account.identities.getIdentityHDKeyById( + identity.getId().toString(), + 1, + ); + + await stateTransition.signByPrivateKey( + privateKey, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + try { + await client.platform.documents.broadcast({ + create: [rewardShare], + }, identity); + + expect.fail('should throw broadcast error'); + } catch (e) { + expect(e.message).to.be.equal('Only masternode identities can share rewards'); + expect(e.code).to.equal(4001); + } + }); + }); +}); diff --git a/packages/platform-test-suite/test/e2e/wallet.spec.js b/packages/platform-test-suite/test/e2e/wallet.spec.js new file mode 100644 index 00000000000..e138c8467ba --- /dev/null +++ b/packages/platform-test-suite/test/e2e/wallet.spec.js @@ -0,0 +1,169 @@ +const Dash = require('dash'); + +const getDAPISeeds = require('../../lib/test/getDAPISeeds'); + +const createClientWithFundedWallet = require('../../lib/test/createClientWithFundedWallet'); +const waitForBalanceToChange = require('../../lib/test/waitForBalanceToChange'); + +const { EVENTS } = Dash.WalletLib; + +describe('e2e', () => { + describe('Wallet', function main() { + this.timeout(950000); + + let failed = false; + let fundedWallet; + let fundedAccount; + let emptyWallet; + let emptyWalletHeight; + let emptyAccount; + let restoredWallet; + let restoredAccount; + let mnemonic; + let firstTransaction; + let secondTransaction; + + before(async () => { + fundedWallet = await createClientWithFundedWallet(); + const network = process.env.NETWORK; + emptyWallet = new Dash.Client({ + seeds: getDAPISeeds(), + network, + wallet: { + waitForInstantLockTimeout: 120000, + }, + }); + + mnemonic = emptyWallet.wallet.exportWallet(); + const { storage } = fundedWallet.wallet; + emptyWalletHeight = storage.getChainStore(storage.application.network).state.blockHeight; + }); + + // Skip test if any prior test in this describe failed + beforeEach(function beforeEach() { + if (failed) { + this.skip(); + } + }); + + afterEach(function afterEach() { + failed = this.currentTest.state === 'failed'; + }); + + after(async () => { + if (fundedWallet) { + await fundedWallet.disconnect(); + } + + if (emptyWallet) { + await emptyWallet.disconnect(); + } + + if (restoredWallet) { + await restoredWallet.disconnect(); + } + }); + + describe('empty wallet', () => { + it('should have no transaction at first', async () => { + emptyAccount = await emptyWallet.getWalletAccount(); + + expect(emptyAccount.getTransactions()).to.be.empty(); + }); + + it('should receive a transaction when as it has been sent', async () => { + fundedAccount = await fundedWallet.getWalletAccount(); + + firstTransaction = await fundedAccount.createTransaction({ + recipient: emptyAccount.getUnusedAddress().address, + satoshis: 1000, + }); + + await Promise.all([ + fundedAccount.broadcastTransaction(firstTransaction), + waitForBalanceToChange(emptyAccount), + ]); + + const transactionIds = Object.keys(emptyAccount.getTransactions()); + + expect(transactionIds).to.have.lengthOf(1); + + expect(transactionIds[0]).to.equal(firstTransaction.id); + }); + }); + + describe('restored wallet', () => { + it('should have all transaction from before at first', async () => { + restoredWallet = new Dash.Client({ + wallet: { + mnemonic, + waitForInstantLockTimeout: 120000, + unsafeOptions: { + skipSynchronizationBeforeHeight: emptyWalletHeight, + }, + }, + seeds: getDAPISeeds(), + network: process.env.NETWORK, + }); + + restoredAccount = await restoredWallet.getWalletAccount(); + + let transactions = restoredAccount.getTransactions(); + + // Wait for new block if transaction has not been propagated yet + if (Object.keys(transactions).length === 0) { + await new Promise((resolve) => restoredAccount.once(EVENTS.BLOCKHEADER, resolve)); + transactions = restoredAccount.getTransactions(); + } + + await waitForBalanceToChange(restoredAccount); + + const transactionIds = Object.keys(transactions); + + expect(transactionIds).to.have.lengthOf(1); + + expect(transactionIds[0]).to.equal(firstTransaction.id); + }); + + it('should receive a transaction when as it has been sent', async () => { + secondTransaction = await fundedAccount.createTransaction({ + recipient: restoredAccount.getUnusedAddress().address, + satoshis: 1000, + }); + + await Promise.all([ + fundedAccount.broadcastTransaction(secondTransaction), + waitForBalanceToChange(restoredAccount), + ]); + + const transactionIds = Object.keys(restoredAccount.getTransactions()); + + expect(transactionIds).to.have.lengthOf(2); + + expect(transactionIds).to.have.members([ + secondTransaction.id, + firstTransaction.id, + ]); + }); + }); + + describe('empty wallet', () => { + it('should receive a transaction when as it has been sent to restored wallet', async () => { + let transactionIds = Object.keys(emptyAccount.getTransactions()); + + if (transactionIds.length < 2) { + await waitForBalanceToChange(emptyAccount); + } + + transactionIds = Object.keys(emptyAccount.getTransactions()); + + expect(transactionIds).to.have.lengthOf(2); + + expect(transactionIds).to.have.members([ + firstTransaction.id, + secondTransaction.id, + ]); + }); + }); + }); +}); diff --git a/packages/platform-test-suite/test/functional/core/broadcastTransaction.spec.js b/packages/platform-test-suite/test/functional/core/broadcastTransaction.spec.js new file mode 100644 index 00000000000..d407155b9bf --- /dev/null +++ b/packages/platform-test-suite/test/functional/core/broadcastTransaction.spec.js @@ -0,0 +1,34 @@ +const Dash = require('dash'); + +const createClientWithFundedWallet = require('../../../lib/test/createClientWithFundedWallet'); + +const { Core: { PrivateKey } } = Dash; + +describe('Core', () => { + describe('broadcastTransaction', () => { + let client; + + before(async () => { + client = await createClientWithFundedWallet(); + }); + + after(async () => { + await client.disconnect(); + }); + + it('should sent transaction and return transaction ID', async () => { + const account = await client.getWalletAccount(); + + const transaction = account.createTransaction({ + recipient: new PrivateKey().toAddress(process.env.NETWORK), + satoshis: 10000, + }); + + const dapiClient = client.getDAPIClient(); + + const transactionId = await dapiClient.core.broadcastTransaction(transaction.toBuffer()); + + expect(transactionId).to.be.a('string'); + }); + }); +}); diff --git a/packages/platform-test-suite/test/functional/core/getBlock.spec.js b/packages/platform-test-suite/test/functional/core/getBlock.spec.js new file mode 100644 index 00000000000..ee61cc14567 --- /dev/null +++ b/packages/platform-test-suite/test/functional/core/getBlock.spec.js @@ -0,0 +1,67 @@ +const Dash = require('dash'); + +const createClientWithoutWallet = require('../../../lib/test/createClientWithoutWallet'); + +const { Core: { Block }, Essentials: { Buffer } } = Dash; + +describe('Core', () => { + describe('getBlock', () => { + let client; + + before(() => { + client = createClientWithoutWallet(); + }); + + after(async () => { + if (client) { + await client.disconnect(); + } + }); + + it('should get block by hash', async () => { + const blockHash = await client.getDAPIClient().core.getBestBlockHash(); + + const blockBinary = await client.getDAPIClient().core.getBlockByHash(blockHash); + expect(blockBinary).to.be.an.instanceof(Buffer); + + const block = new Block(blockBinary); + expect(block.hash).to.equal(blockHash); + }); + + it('should get block by height', async () => { + const { chain: { blocksCount: bestBlockHeight } } = await client + .getDAPIClient().core.getStatus(); + + const blockBinary = await client.getDAPIClient().core.getBlockByHeight(bestBlockHeight); + + expect(blockBinary).to.be.an.instanceof(Buffer); + + const block = new Block(blockBinary); + expect(block).to.be.an.instanceOf(Block); + }); + + it('should throw NotFound error when the block by height was not found', async () => { + try { + await client.getDAPIClient() + .core + .getBlockByHeight(1000000000); + + expect.fail('should throw NotFound error'); + } catch (e) { + expect(e.message).to.equal('Invalid block height'); + expect(e.code).to.equal(5); + } + }); + + it('should throw NotFound error when the block by hash was not found', async () => { + try { + await client.getDAPIClient().core.getBlockByHash('hash'); + + expect.fail('should throw NotFound error'); + } catch (e) { + expect(e.message).to.equal('Block not found'); + expect(e.code).to.equal(5); + } + }); + }); +}); diff --git a/packages/platform-test-suite/test/functional/core/getBlockHash.spec.js b/packages/platform-test-suite/test/functional/core/getBlockHash.spec.js new file mode 100644 index 00000000000..2c1bbdd61a6 --- /dev/null +++ b/packages/platform-test-suite/test/functional/core/getBlockHash.spec.js @@ -0,0 +1,45 @@ +const createClientWithoutWallet = require('../../../lib/test/createClientWithoutWallet'); + +describe('Core', () => { + describe('getBlockHash', () => { + let client; + let lastBlockHeight; + + before(async () => { + client = createClientWithoutWallet(); + + ({ chain: { blocksCount: lastBlockHeight } } = await client + .getDAPIClient().core.getStatus()); + }); + + after(async () => { + if (client) { + await client.disconnect(); + } + }); + + it('should get block hash by height', async () => { + const height = lastBlockHeight - 10; + const hash = await client.getDAPIClient().core.getBlockHash(height); + + expect(hash).to.be.a('string'); + }); + + it('should return RPC error if hash not found', async () => { + const height = lastBlockHeight * 2; + + let broadcastError; + + try { + await client.getDAPIClient().core.getBlockHash(height); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.exist(); + expect(broadcastError.name).to.equal('ResponseError'); + expect(broadcastError.message).contains('Block height out of range'); + expect(broadcastError.code).to.equal(-32602); + }); + }); +}); diff --git a/packages/platform-test-suite/test/functional/core/getStatus.spec.js b/packages/platform-test-suite/test/functional/core/getStatus.spec.js new file mode 100644 index 00000000000..ae8c56aa5b6 --- /dev/null +++ b/packages/platform-test-suite/test/functional/core/getStatus.spec.js @@ -0,0 +1,60 @@ +const { Essentials: { Buffer } } = require('dash'); + +const createClientWithoutWallet = require('../../../lib/test/createClientWithoutWallet'); + +describe('Core', () => { + describe('getStatus', function main() { + let client; + + this.timeout(160000); + + before(() => { + client = createClientWithoutWallet(); + }); + + after(async () => { + if (client) { + await client.disconnect(); + } + }); + + it('should return status', async () => { + const result = await client.getDAPIClient().core.getStatus(); + + const { + version, time, status, syncProgress, chain, masternode, network, + } = result; + + expect(version.protocol).to.be.a('number'); + expect(version.software).to.be.a('number'); + expect(version.agent).to.be.a('string'); + + expect(time.now).to.be.a('number'); + expect(time.offset).to.be.a('number'); + expect(time.median).to.be.a('number'); + + expect(status).to.be.a('string'); + + expect(syncProgress).to.be.a('number'); + + expect(chain.name).to.be.a('string'); + expect(chain.headersCount).to.be.a('number'); + expect(chain.blocksCount).to.be.a('number'); + expect(chain.bestBlockHash).to.be.an.instanceOf(Buffer); + expect(chain.difficulty).to.be.a('number'); + expect(chain.chainWork).to.be.an.instanceOf(Buffer); + expect(chain.isSynced).to.be.a('boolean'); + expect(chain.syncProgress).to.be.a('number'); + + expect(masternode.status).to.be.a('string'); + expect(masternode.proTxHash).to.be.an.instanceOf(Buffer); + expect(masternode.posePenalty).to.be.a('number'); + expect(masternode.isSynced).to.be.a('boolean'); + expect(masternode.syncProgress).to.be.a('number'); + + expect(network.peersCount).to.be.a('number'); + expect(network.fee.relay).to.be.a('number'); + expect(network.fee.incremental).to.be.a('number'); + }); + }); +}); diff --git a/packages/platform-test-suite/test/functional/core/getTransaction.spec.js b/packages/platform-test-suite/test/functional/core/getTransaction.spec.js new file mode 100644 index 00000000000..22d1b60b54b --- /dev/null +++ b/packages/platform-test-suite/test/functional/core/getTransaction.spec.js @@ -0,0 +1,58 @@ +const Dash = require('dash'); + +const wait = require('../../../lib/wait'); +const createClientWithFundedWallet = require('../../../lib/test/createClientWithFundedWallet'); + +const { + Core: { Transaction, PrivateKey }, DAPIClient: { + Errors: { + NotFoundError, + }, + }, +} = Dash; + +describe('Core', () => { + describe('getTransaction', () => { + let client; + + before(async () => { + client = await createClientWithFundedWallet(); + }); + + after(async () => { + await client.disconnect(); + }); + + it('should respond with a transaction by it\'s ID', async () => { + const account = await client.getWalletAccount(); + + await wait(5000); + + const transaction = account.createTransaction({ + recipient: new PrivateKey().toAddress(process.env.NETWORK), + satoshis: 10000, + }); + + await account.broadcastTransaction(transaction); + + await wait(5000); + + const result = await client.getDAPIClient().core.getTransaction(transaction.id); + const receivedTx = new Transaction(result.getTransaction()); + + expect(receivedTx.hash).to.deep.equal(transaction.id); + }); + + it('should throw NotFound error if transaction was not found', async () => { + const nonExistentId = Buffer.alloc(32).toString('hex'); + + try { + await client.getDAPIClient().core.getTransaction(nonExistentId); + + expect.fail('should throw NotFound'); + } catch (e) { + expect(e).to.be.an.instanceOf(NotFoundError); + } + }); + }); +}); diff --git a/packages/platform-test-suite/test/functional/dapi/subscribeToBlockHeadersWithChainLocksHandlerFactory.spec.js b/packages/platform-test-suite/test/functional/dapi/subscribeToBlockHeadersWithChainLocksHandlerFactory.spec.js new file mode 100644 index 00000000000..7c83478a1d4 --- /dev/null +++ b/packages/platform-test-suite/test/functional/dapi/subscribeToBlockHeadersWithChainLocksHandlerFactory.spec.js @@ -0,0 +1,244 @@ +const EventEmitter = require('events'); +const Dash = require('dash'); + +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const getDAPISeeds = require('../../../lib/test/getDAPISeeds'); +const createClientWithFundedWallet = require('../../../lib/test/createClientWithFundedWallet'); + +const { + Core: { + Block, + BlockHeader, + ChainLock, + }, + DAPIClient, +} = Dash; + +const wait = (ms) => new Promise((resolve) => { + setTimeout(resolve, ms); +}); + +const createRetryableStream = (dapiClient) => { + const streamMediator = new EventEmitter(); + + const maxRetries = 10; + let currentRetries = 0; + + const createStream = async (fromBlockHeight, count = 0) => { + let streamError; + const stream = await dapiClient.core.subscribeToBlockHeadersWithChainLocks( + { + fromBlockHeight, + count, + }, + ); + + streamMediator.cancel = stream.cancel.bind(stream); + + stream.on('data', (data) => { + streamMediator.emit('data', data); + }); + + stream.on('error', (e) => { + if (e.code === GrpcErrorCodes.CANCELLED) { + streamMediator.emit('end'); + return; + } + + streamError = e; + if (currentRetries === maxRetries) { + streamMediator.emit('error', e); + return; + } + + createStream(fromBlockHeight, count) + .then(() => { + currentRetries++; + }) + .catch((createStreamError) => { + streamMediator.emit('error', createStreamError); + }); + }); + + stream.on('end', () => { + if (!streamError) { + streamMediator.emit('end'); + } + }); + }; + streamMediator.init = createStream; + + return streamMediator; +}; + +describe('subscribeToBlockHeadersWithChainLocksHandlerFactory', () => { + let dapiClient; + let sdkClient; + const network = process.env.NETWORK; + + let bestBlock; + let bestBlockHeight; + + beforeEach(async () => { + dapiClient = new DAPIClient({ + network, + seeds: getDAPISeeds(), + }); + + const bestBlockHash = await dapiClient.core.getBestBlockHash(); + bestBlock = new Block( + await dapiClient.core.getBlockByHash(bestBlockHash), + ); + bestBlockHeight = bestBlock.transactions[0].extraPayload.height; + }); + + after(async () => { + await sdkClient.disconnect(); + }); + + it('should respond with only historical data', async () => { + const headersAmount = 10; + const historicalBlockHeaders = []; + let bestChainLock = null; + + const stream = createRetryableStream(dapiClient); + await stream.init(1, headersAmount); + + stream.on('data', (data) => { + const blockHeaders = data.getBlockHeaders(); + + if (blockHeaders) { + blockHeaders.getHeadersList().forEach((header) => { + historicalBlockHeaders.push(new BlockHeader(Buffer.from(header, 'hex'))); + }); + } + + const rawChainLock = data.getChainLock(); + + if (rawChainLock) { + bestChainLock = new ChainLock(Buffer.from(rawChainLock)); + } + }); + + let streamEnded = false; + + stream.on('end', () => { + streamEnded = true; + }); + + let streamError; + stream.on('error', (e) => { + streamError = e; + }); + + while (!streamEnded) { + if (streamError) { + throw streamError; + } + await wait(1000); + } + expect(streamError).to.not.exist(); + expect(streamEnded).to.be.true(); + + // TODO: fetching blocks one by one takes too long. Implement getBlockHeaders in dapi-client + const fetchedBlocks = []; + + for (let i = 1; i <= headersAmount; i++) { + const rawBlock = await dapiClient.core.getBlockByHeight(i); + const block = new Block(rawBlock); + + fetchedBlocks.push(block); + } + + expect(historicalBlockHeaders.map((header) => header.hash)) + .to.deep.equal(fetchedBlocks.map((block) => block.header.hash)); + expect(bestChainLock.height).to.exist(); + }); + + it('should respond with both new and historical data', async () => { + let latestChainLock = null; + + const historicalBlocksToGet = 10; + const blockHeadersHashesFromStream = new Set(); + + let obtainedFreshBlock = false; + + sdkClient = await createClientWithFundedWallet(); + const account = await sdkClient.getWalletAccount(); + // Connect to the stream + const stream = createRetryableStream(dapiClient); + await stream.init(bestBlockHeight - historicalBlocksToGet + 1); + + let streamEnded = false; + stream.on('data', (data) => { + const blockHeaders = data.getBlockHeaders(); + + if (blockHeaders) { + const list = blockHeaders.getHeadersList(); + list.forEach((headerBytes) => { + const header = new BlockHeader(Buffer.from(headerBytes)); + blockHeadersHashesFromStream.add(header.hash); + // Once we've obtained a required amount of historical blocks, + // we can consider the rest arriving as newly generated + if (blockHeadersHashesFromStream.size > historicalBlocksToGet) { + obtainedFreshBlock = true; + } + }); + } + + const rawChainLock = data.getChainLock(); + if (rawChainLock) { + latestChainLock = new ChainLock(Buffer.from(rawChainLock)); + } + + if (obtainedFreshBlock && latestChainLock) { + stream.cancel(); + streamEnded = true; + } + }); + + let streamError; + stream.on('error', (e) => { + streamError = e; + }); + + stream.on('end', () => { + streamEnded = true; + }); + + // Create and broadcast transaction to produce fresh block + const transaction = account.createTransaction({ + recipient: account.getUnusedAddress().address, + satoshis: 1000, + }); + + await dapiClient.core.broadcastTransaction(transaction.toBuffer()); + // Wait for stream ending + while (!streamEnded) { + if (streamError) { + throw streamError; + } + + await wait(1000); + } + + expect(streamError).to.not.exist(); + + // TODO: fetching blocks one by one takes too long. Implement getBlockHeaders in dapi-client + const fetchedHistoricalBlocks = []; + + for (let i = bestBlockHeight - historicalBlocksToGet + 1; i <= bestBlockHeight; i++) { + const rawBlock = await dapiClient.core.getBlockByHeight(i); + const block = new Block(rawBlock); + + fetchedHistoricalBlocks.push(block); + } + + for (let i = 0; i < historicalBlocksToGet; i++) { + expect(fetchedHistoricalBlocks[i].header.hash).to.equal([...blockHeadersHashesFromStream][i]); + } + + expect(obtainedFreshBlock).to.be.true(); + expect(latestChainLock).to.exist(); + }); +}); diff --git a/packages/platform-test-suite/test/functional/platform/DataContract.spec.js b/packages/platform-test-suite/test/functional/platform/DataContract.spec.js new file mode 100644 index 00000000000..301593094bf --- /dev/null +++ b/packages/platform-test-suite/test/functional/platform/DataContract.spec.js @@ -0,0 +1,216 @@ +const Dash = require('dash'); + +const getDataContractFixture = require('../../../lib/test/fixtures/getDataContractFixture'); + +const wait = require('../../../lib/wait'); + +const createClientWithFundedWallet = require('../../../lib/test/createClientWithFundedWallet'); + +const { + Errors: { + StateTransitionBroadcastError, + }, + PlatformProtocol: { + ConsensusErrors: { + IdentityNotFoundError, + InvalidDataContractVersionError, + IncompatibleDataContractSchemaError, + }, + }, +} = Dash; + +describe('Platform', () => { + describe('Data Contract', function main() { + this.timeout(700000); + + let client; + let dataContractFixture; + let identity; + + before(async () => { + client = await createClientWithFundedWallet(); + + identity = await client.platform.identities.register(90000); + }); + + after(async () => { + if (client) { + await client.disconnect(); + } + }); + + it('should fail to create new data contract with unknown owner', async () => { + // if no identity is specified + // random is generated within the function + dataContractFixture = getDataContractFixture(); + + let broadcastError; + + try { + await client.platform.contracts.publish(dataContractFixture, identity); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.be.an.instanceOf(StateTransitionBroadcastError); + expect(broadcastError.getCause()).to.be.an.instanceOf(IdentityNotFoundError); + }); + + it('should create new data contract with previously created identity as an owner', async () => { + dataContractFixture = getDataContractFixture(identity.getId()); + + await client.platform.contracts.publish(dataContractFixture, identity); + }); + + it('should be able to get newly created data contract', async () => { + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + const fetchedDataContract = await client.platform.contracts.get( + dataContractFixture.getId(), + ); + + expect(fetchedDataContract).to.be.not.null(); + expect(dataContractFixture.toJSON()).to.deep.equal(fetchedDataContract.toJSON()); + }); + + it('should not be able to update an existing data contract if version is incorrect', async () => { + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + const fetchedDataContract = await client.platform.contracts.get( + dataContractFixture.getId(), + ); + + fetchedDataContract.setVersion(fetchedDataContract.getVersion() + 2); + + let broadcastError; + + try { + await client.platform.contracts.update(fetchedDataContract, identity); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.be.an.instanceOf(StateTransitionBroadcastError); + expect(broadcastError.getCause()).to.be.an.instanceOf(InvalidDataContractVersionError); + }); + + it('should not be able to update an existing data contract if schema is not backward compatible', async () => { + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + const fetchedDataContract = await client.platform.contracts.get( + dataContractFixture.getId(), + ); + + const documentSchema = fetchedDataContract.getDocumentSchema('withByteArrays'); + delete documentSchema.properties.identifierField; + + let broadcastError; + + try { + await client.platform.contracts.update(fetchedDataContract, identity); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.be.an.instanceOf(StateTransitionBroadcastError); + expect(broadcastError.getCause()).to.be.an.instanceOf(IncompatibleDataContractSchemaError); + }); + + it('should be able to update an existing data contract', async () => { + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + const fetchedDataContract = await client.platform.contracts.get( + dataContractFixture.getId(), + ); + + const newDocumentType = 'myAwesomeDocument'; + + fetchedDataContract.setDocumentSchema(newDocumentType, { + type: 'object', + indices: [ + { + name: 'firstName', + properties: [ + { firstName: 'asc' }, + ], + unique: true, + }, + { + name: 'firstNameLastName', + properties: [ + { firstName: 'asc' }, + { lastName: 'asc' }, + ], + unique: true, + }, + ], + properties: { + firstName: { + type: 'string', + maxLength: 63, + }, + lastName: { + type: 'string', + maxLength: 63, + }, + }, + required: ['firstName', '$createdAt', '$updatedAt', 'lastName'], + additionalProperties: false, + }); + + await client.platform.contracts.update(fetchedDataContract, identity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + client.getApps().set('customContract', { + contractId: fetchedDataContract.getId(), + contract: fetchedDataContract, + }); + + const document = await client.platform.documents.create( + `customContract.${newDocumentType}`, + identity, + { + firstName: 'myName', + lastName: 'myLastName', + }, + ); + + await client.platform.documents.broadcast({ + create: [document], + }, identity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + const [fetchedDocument] = await client.platform.documents.get( + `customContract.${newDocumentType}`, + { where: [['firstName', '==', 'myName']] }, + ); + + expect(fetchedDocument.getData()).to.deep.equal( + { + firstName: 'myName', + lastName: 'myLastName', + }, + ); + }); + }); +}); diff --git a/packages/platform-test-suite/test/functional/platform/Document.spec.js b/packages/platform-test-suite/test/functional/platform/Document.spec.js new file mode 100644 index 00000000000..92d06d3337d --- /dev/null +++ b/packages/platform-test-suite/test/functional/platform/Document.spec.js @@ -0,0 +1,378 @@ +const Dash = require('dash'); +const { expect } = require('chai'); + +const { signStateTransition } = require('dash/build/src/SDK/Client/Platform/signStateTransition'); + +const getIdentityFixture = require('../../../lib/test/fixtures/getIdentityFixture'); +const getDataContractFixture = require('../../../lib/test/fixtures/getDataContractFixture'); +const wait = require('../../../lib/wait'); + +const createClientWithFundedWallet = require('../../../lib/test/createClientWithFundedWallet'); + +const { + Errors: { + StateTransitionBroadcastError, + }, + PlatformProtocol: { + ConsensusErrors: { + InvalidDocumentTypeError, + }, + }, +} = Dash; + +describe('Platform', () => { + describe('Document', function main() { + this.timeout(700000); + + let client; + let dataContractFixture; + let identity; + let document; + + before(async () => { + client = await createClientWithFundedWallet(undefined, 200000); + + identity = await client.platform.identities.register(160000); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + dataContractFixture = getDataContractFixture(identity.getId()); + + await client.platform.contracts.publish(dataContractFixture, identity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + client.getApps().set('customContracts', { + contractId: dataContractFixture.getId(), + contract: dataContractFixture, + }); + }); + + beforeEach(() => { + dataContractFixture = getDataContractFixture(identity.getId()); + }); + + after(async () => { + if (client) { + await client.disconnect(); + } + }); + + it('should fail to create new document with an unknown type', async function it() { + // Add undefined document type for + client.getApps().get('customContracts').contract.documents.undefinedType = { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + additionalProperties: false, + }; + + const newDocument = await client.platform.documents.create( + 'customContracts.undefinedType', + identity, + { + name: 'anotherName', + }, + ); + + // mock validateBasic to skip validation in SDK + this.sinon.stub(client.platform.dpp.stateTransition, 'validateBasic'); + + client.platform.dpp.stateTransition.validateBasic.returns({ + isValid: () => true, + }); + + let broadcastError; + + try { + await client.platform.documents.broadcast({ + create: [newDocument], + }, identity); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.be.an.instanceOf(StateTransitionBroadcastError); + expect(broadcastError.getCause()).to.be.an.instanceOf(InvalidDocumentTypeError); + }); + + it('should fail to create a new document with an unknown owner', async () => { + const unknownIdentity = getIdentityFixture(); + + document = await client.platform.documents.create( + 'customContracts.niceDocument', + unknownIdentity, + { + name: 'myName', + }, + ); + + let broadcastError; + + try { + await client.platform.documents.broadcast({ + create: [document], + }, unknownIdentity); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.exist(); + expect(broadcastError.message).to.equal( + `Identity with ID ${unknownIdentity.getId()} is not associated with wallet, or it's not synced`, + ); + }); + + it('should fail to create a document that violates unique index constraint', async () => { + const sharedDocumentData = { + firstName: 'Some First Name', + }; + + const firstDocument = await client.platform.documents.create( + 'customContracts.indexedDocument', + identity, + { + ...sharedDocumentData, + lastName: 'Some Last Name', + }, + ); + + await client.platform.documents.broadcast({ + create: [firstDocument], + }, identity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + const secondDocument = await client.platform.documents.create( + 'customContracts.indexedDocument', + identity, + { + ...sharedDocumentData, + lastName: 'Other Last Name', + }, + ); + + let broadcastError; + + try { + await client.platform.documents.broadcast({ + create: [secondDocument], + }, identity); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.exist(); + expect(broadcastError.code).to.be.equal(4009); + expect(broadcastError.message).to.match(/Document \w* has duplicate unique properties \$ownerId, firstName with other documents/); + }); + + it('should be able to create new document', async () => { + document = await client.platform.documents.create( + 'customContracts.indexedDocument', + identity, + { + firstName: 'myName', + lastName: 'lastName', + }, + ); + + await client.platform.documents.broadcast({ + create: [document], + }, identity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + }); + + it('should fetch created document', async () => { + const [fetchedDocument] = await client.platform.documents.get( + 'customContracts.indexedDocument', + { where: [['$id', '==', document.getId()]] }, + ); + + expect(fetchedDocument).to.exist(); + expect(document.toObject()).to.deep.equal(fetchedDocument.toObject()); + expect(fetchedDocument.getUpdatedAt().getTime()) + .to.be.equal(fetchedDocument.getCreatedAt().getTime()); + }); + + it('should be able to fetch created document by created timestamp', async () => { + const [fetchedDocument] = await client.platform.documents.get( + 'customContracts.indexedDocument', + { where: [['$createdAt', '==', document.getCreatedAt().getTime()]] }, + ); + + expect(fetchedDocument).to.exist(); + expect(document.toObject()).to.deep.equal(fetchedDocument.toObject()); + }); + + it('should be able to update document', async () => { + const [storedDocument] = await client.platform.documents.get( + 'customContracts.indexedDocument', + { where: [['$id', '==', document.getId()]] }, + ); + + storedDocument.set('firstName', 'updatedName'); + + await client.platform.documents.broadcast({ + replace: [storedDocument], + }, identity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + const [fetchedDocument] = await client.platform.documents.get( + 'customContracts.indexedDocument', + { where: [['$id', '==', document.getId()]] }, + ); + + expect(fetchedDocument.get('firstName')).to.equal('updatedName'); + expect(fetchedDocument.getUpdatedAt().getTime()) + .to.be.greaterThan(fetchedDocument.getCreatedAt().getTime()); + }); + + it.skip('should be able to prove that a document was updated', async () => { + const [storedDocument] = await client.platform.documents.get( + 'customContracts.indexedDocument', + { where: [['$id', '==', document.getId()]] }, + ); + + storedDocument.set('firstName', 'updatedName'); + + const documentsBatchTransition = await client.platform.documents.broadcast({ + replace: [storedDocument], + }, identity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + documentsBatchTransition.transitions[0].data.firstName = 'nameToProve'; + documentsBatchTransition.transitions[0].updatedAt = new Date(); + documentsBatchTransition.transitions[0].revision += 1; + const signedTransition = await signStateTransition( + client.platform, documentsBatchTransition, identity, 1, + ); + + const proof = await client.platform.broadcastStateTransition(signedTransition); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + expect(proof.rootTreeProof).to.be.an.instanceof(Uint8Array); + expect(proof.rootTreeProof.length).to.be.greaterThan(0); + + expect(proof.storeTreeProofs).to.exist(); + expect(proof.storeTreeProofs.documentsProof).to.be.an.instanceof(Uint8Array); + expect(proof.storeTreeProofs.documentsProof.length).to.be.greaterThan(0); + + expect(proof.signatureLLMQHash).to.be.an.instanceof(Uint8Array); + expect(proof.signatureLLMQHash.length).to.be.equal(32); + + expect(proof.signature).to.be.an.instanceof(Uint8Array); + expect(proof.signature.length).to.be.equal(96); + }); + + it('should fail to update document with timestamp in violated time frame', async () => { + const [storedDocument] = await client.platform.documents.get( + 'customContracts.indexedDocument', + { where: [['$id', '==', document.getId()]] }, + ); + + const updatedAt = storedDocument.getUpdatedAt(); + + updatedAt.setMinutes(updatedAt.getMinutes() - 10); + + let broadcastError; + + const documentsBatchTransition = await client.platform.documents.broadcast({ + replace: [storedDocument], + }, identity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + documentsBatchTransition.transitions[0].updatedAt = updatedAt; + documentsBatchTransition.transitions[0].revision += 1; + const signedTransition = await signStateTransition( + client.platform, documentsBatchTransition, identity, 1, + ); + + try { + await client.platform.broadcastStateTransition(signedTransition); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.exist(); + expect(broadcastError.code).to.be.equal(4008); + expect(broadcastError.message).to.match(/Document \w* updatedAt timestamp .* are out of block time window from .* and .*/); + }); + + it('should be able to delete a document', async () => { + await client.platform.documents.broadcast({ + delete: [document], + }, identity); + + const [storedDocument] = await client.platform.documents.get( + 'customContracts.indexedDocument', + { where: [['$id', '==', document.getId()]] }, + ); + + expect(storedDocument).to.not.exist(); + }); + + it('should fail to create a new document with timestamp in violated time frame', async () => { + document = await client.platform.documents.create( + 'customContracts.indexedDocument', + identity, + { + firstName: 'myName', + lastName: 'lastName', + }, + ); + + const createdAt = document.getCreatedAt(); + + createdAt.setMinutes(createdAt.getMinutes() - 10); + + document.setUpdatedAt(createdAt); + + let broadcastError; + + try { + await client.platform.documents.broadcast({ + create: [document], + }, identity); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.exist(); + expect(broadcastError.message).to.match(/Document \w* createdAt timestamp .* are out of block time window from .* and .*/); + expect(broadcastError.code).to.be.equal(4008); + }); + }); +}); diff --git a/packages/platform-test-suite/test/functional/platform/Identity.spec.js b/packages/platform-test-suite/test/functional/platform/Identity.spec.js new file mode 100644 index 00000000000..03c94f99e75 --- /dev/null +++ b/packages/platform-test-suite/test/functional/platform/Identity.spec.js @@ -0,0 +1,614 @@ +const Dash = require('dash'); + +const { createFakeInstantLock } = require('dash/build/src/utils/createFakeIntantLock'); + +const { hash } = require('@dashevo/dpp/lib/util/hash'); +const getDataContractFixture = require('../../../lib/test/fixtures/getDataContractFixture'); +const createClientWithFundedWallet = require('../../../lib/test/createClientWithFundedWallet'); +const wait = require('../../../lib/wait'); +const getDAPISeeds = require('../../../lib/test/getDAPISeeds'); + +const { + Essentials: { + Buffer, + }, + Core: { + Transaction, + }, + Errors: { + StateTransitionBroadcastError, + }, + PlatformProtocol: { + Identity, + Identifier, + IdentityPublicKey, + ConsensusErrors: { + InvalidInstantAssetLockProofSignatureError, + IdentityAssetLockTransactionOutPointAlreadyExistsError, + BalanceIsNotEnoughError, + InvalidIdentityKeySignatureError, + }, + }, +} = Dash; + +describe('Platform', () => { + describe('Identity', () => { + let dpp; + let client; + let identity; + let walletAccount; + + before(async () => { + dpp = new Dash.PlatformProtocol(); + await dpp.initialize(); + + client = await createClientWithFundedWallet(undefined, 200000); + + walletAccount = await client.getWalletAccount(); + }); + + after(async () => { + if (client) { + await client.disconnect(); + } + }); + + it('should create an identity', async () => { + identity = await client.platform.identities.register(140000); + + expect(identity).to.exist(); + }); + + it('should fail to create an identity if instantLock is not valid', async () => { + const { + transaction, + privateKey, + outputIndex, + } = await client.platform.identities.utils.createAssetLockTransaction(1); + + const invalidInstantLock = createFakeInstantLock(transaction.hash); + const assetLockProof = await dpp.identity.createInstantAssetLockProof( + invalidInstantLock, + transaction, + outputIndex, + ); + + const { + identityCreateTransition: invalidIdentityCreateTransition, + } = await client.platform.identities.utils.createIdentityCreateTransition( + assetLockProof, privateKey, + ); + + let broadcastError; + + try { + await client.platform.broadcastStateTransition( + invalidIdentityCreateTransition, + ); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.be.an.instanceOf(StateTransitionBroadcastError); + expect(broadcastError.getCause()).to.be.an.instanceOf( + InvalidInstantAssetLockProofSignatureError, + ); + }); + + it('should fail to create an identity with already used asset lock output', async () => { + const { + transaction, + privateKey, + outputIndex, + } = await client.platform.identities.utils.createAssetLockTransaction(7000); + + await client.getDAPIClient().core.broadcastTransaction(transaction.toBuffer()); + + const assetLockProof = await client.platform.identities.utils + .createAssetLockProof(transaction, outputIndex); + + // Creating normal transition + const { + identity: identityOne, + identityCreateTransition: identityCreateTransitionOne, + identityIndex: identityOneIndex, + } = await client.platform.identities.utils + .createIdentityCreateTransition(assetLockProof, privateKey); + + await client.platform.broadcastStateTransition( + identityCreateTransitionOne, + ); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + walletAccount.storage + .getWalletStore(walletAccount.walletId) + .insertIdentityIdAtIndex( + identityOne.getId().toString(), + identityOneIndex, + ); + + // Creating transition that tries to spend the same transaction + const { + identityCreateTransition: identityCreateDoubleSpendTransition, + } = await client.platform.identities.utils + .createIdentityCreateTransition(assetLockProof, privateKey); + + let broadcastError; + + try { + await client.platform.broadcastStateTransition( + identityCreateDoubleSpendTransition, + ); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.be.an.instanceOf(StateTransitionBroadcastError); + expect(broadcastError.getCause()).to.be.an.instanceOf( + IdentityAssetLockTransactionOutPointAlreadyExistsError, + ); + }); + + it('should not be able to create an identity without key proof', async () => { + const { + transaction, + privateKey, + outputIndex, + } = await client.platform.identities.utils.createAssetLockTransaction(15); + + await client.getDAPIClient().core.broadcastTransaction(transaction.toBuffer()); + + const assetLockProof = await client.platform.identities.utils.createAssetLockProof( + transaction, + outputIndex, + ); + + // Creating normal transition + const { + identityCreateTransition, + } = await client.platform.identities.utils.createIdentityCreateTransition( + assetLockProof, privateKey, + ); + + // Remove signature + + const [masterKey] = identityCreateTransition.getPublicKeys(); + masterKey.setSignature(Buffer.alloc(65)); + + // Broadcast + + let broadcastError; + + try { + await client.platform.broadcastStateTransition( + identityCreateTransition, + { skipValidation: true }, + ); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.be.an.instanceOf(StateTransitionBroadcastError); + expect(broadcastError.getCause()).to.be.an.instanceOf( + InvalidIdentityKeySignatureError, + ); + }); + + it('should be able to get newly created identity', async () => { + const fetchedIdentity = await client.platform.identities.get( + identity.getId(), + ); + + expect(fetchedIdentity).to.be.not.null(); + + const fetchedIdentityWithoutBalance = fetchedIdentity.toObject(); + delete fetchedIdentityWithoutBalance.balance; + + const localIdentityWithoutBalance = identity.toObject(); + delete localIdentityWithoutBalance.balance; + + expect(fetchedIdentityWithoutBalance).to.deep.equal(localIdentityWithoutBalance); + + expect(fetchedIdentity.getBalance()).to.be.greaterThan(0); + }); + + it('should be able to get newly created identity by it\'s public key', async () => { + const response = await client.getDAPIClient().platform.getIdentitiesByPublicKeyHashes( + [identity.getPublicKeyById(0).hash()], + ); + + const [fetchedIdentity] = response.getIdentities(); + + expect(fetchedIdentity).to.be.not.null(); + expect(fetchedIdentity).to.deep.equal(identity.toBuffer()); + }); + + describe('chainLock', function describe() { + let chainLockIdentity; + + this.timeout(850000); + + it('should create identity using chainLock', async () => { + const { + transaction, + privateKey, + outputIndex, + } = await client.platform.identities.utils.createAssetLockTransaction(7000); + + // Broadcast Asset Lock transaction + await client.getDAPIClient().core.broadcastTransaction(transaction.toBuffer()); + + // Wait for transaction to be mined and chain locked + const { promise: metadataPromise } = walletAccount.waitForTxMetadata(transaction.id); + + const { height: transactionHeight } = await metadataPromise; + + const outPoint = transaction.getOutPointBuffer(outputIndex); + const assetLockProof = await dpp.identity.createChainAssetLockProof( + transactionHeight, + outPoint, + ); + + // Wait for platform chain to sync core height up to transaction height + const { + promise: coreHeightPromise, + } = await client.platform.identities.utils + .waitForCoreChainLockedHeight(transactionHeight); + + await coreHeightPromise; + + const identityCreateTransitionData = await client.platform.identities.utils + .createIdentityCreateTransition(assetLockProof, privateKey); + + const { + identityCreateTransition, + } = identityCreateTransitionData; + + ({ identity: chainLockIdentity } = identityCreateTransitionData); + + await client.platform.broadcastStateTransition( + identityCreateTransition, + ); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + }); + + it('should be able to get newly created identity', async () => { + const fetchedIdentity = await client.platform.identities.get( + chainLockIdentity.getId(), + ); + + expect(fetchedIdentity).to.be.not.null(); + + const fetchedIdentityWithoutBalance = fetchedIdentity.toObject(); + delete fetchedIdentityWithoutBalance.balance; + + const localIdentityWithoutBalance = chainLockIdentity.toObject(); + delete localIdentityWithoutBalance.balance; + + expect(fetchedIdentityWithoutBalance).to.deep.equal(localIdentityWithoutBalance); + + expect(fetchedIdentity.getBalance()).to.be.greaterThan(0); + }); + }); + + describe('Credits', () => { + let dataContractFixture; + + before(async () => { + dataContractFixture = getDataContractFixture(identity.getId()); + + await client.platform.contracts.publish(dataContractFixture, identity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + client.getApps().set('customContracts', { + contractId: dataContractFixture.getId(), + contract: dataContractFixture, + }); + }); + + it('should fail to create more documents if there are no more credits', async () => { + const lowBalanceIdentity = await client.platform.identities.register(7000); + + const document = await client.platform.documents.create( + 'customContracts.niceDocument', + lowBalanceIdentity, + { + name: 'Some Very Long Long Long Name'.repeat(100), + }, + ); + + let broadcastError; + + try { + await client.platform.documents.broadcast({ + create: [document], + }, lowBalanceIdentity); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.be.an.instanceOf(StateTransitionBroadcastError); + expect(broadcastError.getCause()).to.be.an.instanceOf( + BalanceIsNotEnoughError, + ); + }); + + it.skip('should fail top-up if instant lock is not valid', async () => { + const { + transaction, + privateKey, + outputIndex, + } = await client.platform.identity.utils.createAssetLockTransaction(15); + + const instantLock = createFakeInstantLock(transaction.hash); + const assetLockProof = await dpp.identity.createInstantAssetLockProof(instantLock); + + const identityTopUpTransition = dpp.identity.createIdentityTopUpTransition( + identity.getId(), + transaction, + outputIndex, + assetLockProof, + ); + await identityTopUpTransition.signByPrivateKey( + privateKey, + IdentityPublicKey.TYPES.ECDSA_SECP256K1, + ); + + let broadcastError; + + try { + await client.platform.broadcastStateTransition( + identityTopUpTransition, + ); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.exist(); + expect(broadcastError.message).to.be.equal('State Transition is invalid: InvalidIdentityAssetLockProofSignatureError: Invalid Asset lock proof signature'); + expect(broadcastError.code).to.be.equal(3); + const [error] = broadcastError.data.errors; + expect(error.name).to.equal('IdentityAssetLockTransactionNotFoundError'); + }); + + it('should be able to top-up credit balance', async () => { + const identityBeforeTopUp = await client.platform.identities.get( + identity.getId(), + ); + const balanceBeforeTopUp = identityBeforeTopUp.getBalance(); + const topUpAmount = 20000; + const topUpCredits = topUpAmount * 1000; + + await client.platform.identities.topUp(identity.getId(), topUpAmount); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + const identityAfterTopUp = await client.platform.identities.get( + identity.getId(), + ); + + expect(identityAfterTopUp.getBalance()).to.be.greaterThan(balanceBeforeTopUp); + + expect(identityAfterTopUp.getBalance()).to.be + .lessThan(balanceBeforeTopUp + topUpCredits); + }); + + it('should be able to create more documents after the top-up', async () => { + const document = await client.platform.documents.create( + 'customContracts.niceDocument', + identity, + { + name: 'Some Very Long Long Long Name', + }, + ); + + await client.platform.documents.broadcast({ + create: [document], + }, identity); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + }); + + it('should fail to top up an identity with already used asset lock output', async () => { + const { + transaction, + privateKey, + outputIndex, + } = await client.platform.identities.utils.createAssetLockTransaction(1); + + await client.getDAPIClient().core.broadcastTransaction(transaction.toBuffer()); + + const assetLockProof = await client.platform.identities.utils.createAssetLockProof( + transaction, + outputIndex, + ); + + // Creating normal transition + const identityTopUpTransitionOne = await client.platform.identities.utils + .createIdentityTopUpTransition(assetLockProof, privateKey, identity.getId()); + // Creating ST that tries to spend the same output + const conflictingTopUpStateTransition = await client.platform.identities.utils + .createIdentityTopUpTransition(assetLockProof, privateKey, identity.getId()); + + await client.platform.broadcastStateTransition( + identityTopUpTransitionOne, + ); + + // Additional wait time to mitigate testnet latency + if (process.env.NETWORK === 'testnet') { + await wait(5000); + } + + let broadcastError; + + try { + await client.platform.broadcastStateTransition( + conflictingTopUpStateTransition, + ); + } catch (e) { + broadcastError = e; + } + + expect(broadcastError).to.be.an.instanceOf(StateTransitionBroadcastError); + expect(broadcastError.getCause()).to.be.an.instanceOf( + IdentityAssetLockTransactionOutPointAlreadyExistsError, + ); + }); + }); + + describe('Update', () => { + it('should be able to add public key to the identity', async () => { + const identityBeforeUpdate = new Identity(identity.toObject()); + + expect(identityBeforeUpdate.getPublicKeyById(2)).to.not.exist(); + + const account = await client.platform.client.getWalletAccount(); + const identityIndex = await account.getUnusedIdentityIndex(); + + const { privateKey: identityPrivateKey } = account + .identities + .getIdentityHDKeyByIndex(identityIndex, 1); + + const identityPublicKey = identityPrivateKey.toPublicKey().toBuffer(); + + const newPublicKey = new IdentityPublicKey( + { + id: 2, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.HIGH, + data: identityPublicKey, + readOnly: false, + }, + ); + + const update = { + add: [newPublicKey], + }; + + await client.platform.identities.update( + identity, + update, + { + [newPublicKey.getId()]: identityPrivateKey, + }, + ); + + identity = await client.platform.identities.get( + identity.getId(), + ); + + expect(identity.getRevision()).to.equal(identityBeforeUpdate.getRevision() + 1); + expect(identity.getPublicKeyById(2)).to.exist(); + + expect(identity.getPublicKeyById(2).toObject()).to.deep.equal( + newPublicKey.toObject(), + ); + }); + + it('should be able to disable public key of the identity', async () => { + const now = new Date().getTime(); + + const identityBeforeUpdate = new Identity(identity.toObject()); + + const publicKeyToDisable = identityBeforeUpdate.getPublicKeyById(2); + const update = { + disable: [publicKeyToDisable], + }; + + await client.platform.identities.update( + identity, + update, + ); + + identity = await client.platform.identities.get( + identity.getId(), + ); + + expect(identity.getRevision()).to.equal(identityBeforeUpdate.getRevision() + 1); + expect(identity.getPublicKeyById(2)).to.exist(); + expect(identity.getPublicKeyById(2).getDisabledAt()).to.be.at.least(now); + + expect(identity.getPublicKeyById(0)).to.exist(); + expect(identity.getPublicKeyById(0).getDisabledAt()).to.be.undefined(); + }); + }); + + describe('Masternodes', () => { + let dapiClient; + const network = process.env.NETWORK; + + beforeEach(() => { + dapiClient = new Dash.DAPIClient({ + network, + seeds: getDAPISeeds(), + }); + }); + + it('should receive masternode identities', async () => { + const bestBlockHash = await dapiClient.core.getBestBlockHash(); + const baseBlockHash = await dapiClient.core.getBlockHash(1); + + const { mnList } = await dapiClient.core.getMnListDiff( + baseBlockHash, + bestBlockHash, + ); + + for (const masternodeEntry of mnList) { + const masternodeIdentityId = Identifier.from( + Buffer.from(masternodeEntry.proRegTxHash, 'hex'), + ); + + let fetchedIdentity = await client.platform.identities.get( + masternodeIdentityId, + ); + + expect(fetchedIdentity).to.be.not.null(); + + const { transaction: transactionBuffer } = await client.dapiClient.core.getTransaction( + masternodeEntry.proRegTxHash, + ); + + const transaction = new Transaction(transactionBuffer); + + if (transaction.extraPayload.operatorReward > 0) { + const operatorPubKey = Buffer.from(masternodeEntry.pubKeyOperator, 'hex'); + + const operatorIdentityHash = hash( + Buffer.concat([ + Buffer.from(masternodeEntry.proRegTxHash, 'hex'), + operatorPubKey, + ]), + ); + + const operatorIdentityId = Identifier.from(operatorIdentityHash); + + fetchedIdentity = await client.platform.identities.get( + operatorIdentityId, + ); + + expect(fetchedIdentity).to.be.not.null(); + } + } + }); + }); + }); +}); diff --git a/packages/platform-test-suite/test/functional/platform/featureFlags.spec.js b/packages/platform-test-suite/test/functional/platform/featureFlags.spec.js new file mode 100644 index 00000000000..b9d4b13ac20 --- /dev/null +++ b/packages/platform-test-suite/test/functional/platform/featureFlags.spec.js @@ -0,0 +1,157 @@ +const featureFlagsSystemIds = require('@dashevo/feature-flags-contract/lib/systemIds'); + +const createClientWithFundedWallet = require('../../../lib/test/createClientWithFundedWallet'); + +describe('Platform', () => { + describe('Feature flags', function main() { + this.timeout(900000); + + describe('updateConsensusParams', () => { + let oldConsensusParams; + let ownerClient; + let updateConsensusParamsFeatureFlag; + let revertConsensusParamsFeatureFlag; + let identity; + + let contractId; + let ownerId; + + before(async () => { + ownerClient = await createClientWithFundedWallet( + process.env.FEATURE_FLAGS_OWNER_PRIVATE_KEY, + ); + + await ownerClient.platform.identities.topUp(featureFlagsSystemIds.ownerId, 50000); + + ({ contractId, ownerId } = featureFlagsSystemIds); + + const featureFlagContract = await ownerClient.platform.contracts.get( + contractId, + ); + + ownerClient.getApps().set('featureFlags', { + contractId, + contract: featureFlagContract, + }); + + identity = await ownerClient.platform.identities.get( + ownerId, + ); + + const { blockHeight: lastBlockHeight } = identity.getMetadata(); + + oldConsensusParams = await ownerClient.getDAPIClient().platform.getConsensusParams(); + + const block = oldConsensusParams.getBlock(); + const evidence = oldConsensusParams.getEvidence(); + + updateConsensusParamsFeatureFlag = { + enableAtHeight: lastBlockHeight + 2, + block: { + maxBytes: +block.maxBytes + 1, + }, + evidence: { + maxAgeNumBlocks: +evidence.maxAgeNumBlocks + 1, + maxAgeDuration: { + seconds: Math.trunc(evidence.maxAgeDuration / 1000000000) + 1, + nanos: (evidence.maxAgeDuration % 1000000000) + 1, + }, + // on schnaps, maxBytes default value is empty, and we can't revert it back + // maxBytes: +evidence.maxBytes + 1, + }, + }; + + revertConsensusParamsFeatureFlag = { + enableAtHeight: lastBlockHeight + 4, + block: { + maxBytes: +block.maxBytes, + }, + evidence: { + maxAgeNumBlocks: +evidence.maxAgeNumBlocks, + maxAgeDuration: { + seconds: Math.trunc(evidence.maxAgeDuration / 1000000000), + nanos: (evidence.maxAgeDuration % 1000000000), + }, + // maxBytes: +evidence.maxBytes, + }, + }; + }); + + it('should update consensus params', async function it() { + if (process.env.NETWORK === 'mainnet') { + this.skip('it\'s dangerous to run this test on mainnet'); + } + + const documentUpdate = await ownerClient.platform.documents.create( + 'featureFlags.updateConsensusParams', + identity, + updateConsensusParamsFeatureFlag, + ); + + const documentRevert = await ownerClient.platform.documents.create( + 'featureFlags.updateConsensusParams', + identity, + revertConsensusParamsFeatureFlag, + ); + + await ownerClient.platform.documents.broadcast({ + create: [documentUpdate, documentRevert], + }, identity); + + // wait for block and check consensus params were changed + let height; + do { + const someIdentity = await ownerClient.platform.identities.get( + ownerId, + ); + + ({ blockHeight: height } = someIdentity.getMetadata()); + } while (height <= updateConsensusParamsFeatureFlag.enableAtHeight); + + let newConsensusParams = await ownerClient.getDAPIClient().platform.getConsensusParams( + updateConsensusParamsFeatureFlag.enableAtHeight + 1, + ); + + const { block, evidence } = updateConsensusParamsFeatureFlag; + + let updatedBlock = newConsensusParams.getBlock(); + + expect(updatedBlock.getMaxBytes()).to.equal(`${block.maxBytes}`); + + const { seconds } = evidence.maxAgeDuration; + const nanos = `${evidence.maxAgeDuration.nanos}`.padStart(9, '0'); + + let updatedEvidence = newConsensusParams.getEvidence(); + + expect(updatedEvidence.getMaxAgeNumBlocks()).to.equal(`${evidence.maxAgeNumBlocks}`); + expect(updatedEvidence.getMaxAgeDuration()).to.equal(`${seconds}${nanos}`); + // expect(updatedEvidence.getMaxBytes()).to.equal(`${evidence.maxBytes}`); + + // wait for block and check consensus params were reverted + do { + const someIdentity = await ownerClient.platform.identities.get( + ownerId, + ); + + ({ blockHeight: height } = someIdentity.getMetadata()); + } while (height <= revertConsensusParamsFeatureFlag.enableAtHeight); + + newConsensusParams = await ownerClient.getDAPIClient().platform.getConsensusParams( + revertConsensusParamsFeatureFlag.enableAtHeight + 1, + ); + + updatedBlock = newConsensusParams.getBlock(); + const oldBlock = oldConsensusParams.getBlock(); + + expect(updatedBlock.getMaxBytes()).to.equal(`${oldBlock.maxBytes}`); + + updatedEvidence = newConsensusParams.getEvidence(); + const oldEvidence = oldConsensusParams.getEvidence(); + + expect(updatedEvidence.getMaxAgeNumBlocks()).to.equal(`${oldEvidence.maxAgeNumBlocks}`); + expect(updatedEvidence.getMaxAgeDuration()).to.equal(`${oldEvidence.maxAgeDuration}`); + // expect(updatedEvidence.getMaxBytes()).to.equal(`${oldEvidence.maxBytes}`); + }); + }); + }); +}); diff --git a/packages/platform-test-suite/test/functional/platform/proofs.spec.js b/packages/platform-test-suite/test/functional/platform/proofs.spec.js new file mode 100644 index 00000000000..f36201538f0 --- /dev/null +++ b/packages/platform-test-suite/test/functional/platform/proofs.spec.js @@ -0,0 +1,477 @@ +const Dash = require('dash'); +const { MerkleProof, MerkleTree } = require('js-merkle'); +const { executeProof/* , verifyProof */ } = require('@dashevo/merk'); +const { + contractId: dpnsContractId, + ownerId: dpnsOwnerId, +} = require('@dashevo/dpns-contract/lib/systemIds'); + +const generateRandomIdentifier = require('../../../lib/test/utils/generateRandomIdentifier'); +const hashFunction = require('../../../lib/proofHashFunction'); +const testProofStructure = require('../../../lib/test/testProofStructure'); +// const parseStoreTreeProof = require('../../../lib/parseStoreTreeProof'); +const createClientWithFundedWallet = require('../../../lib/test/createClientWithFundedWallet'); + +const { + Core: { + PrivateKey, + }, + PlatformProtocol: { + Identifier, + }, +} = Dash; + +describe('Platform', () => { + describe('Proofs', () => { + let blake3; + let dashClient; + let contractId; + + before(async () => { + await hashFunction.init(); + blake3 = hashFunction.hashFunction; + + dashClient = await createClientWithFundedWallet(); + + await dashClient.platform.initialize(); + + contractId = Identifier.from(dpnsContractId); + }); + + after(() => { + dashClient.disconnect(); + }); + + describe('Merkle Proofs', () => { + describe('Data Contract', () => { + it('should be able to get and verify proof that data contract exists with getIdentity', async () => { + const dataContractResponseWithProof = await dashClient.getDAPIClient() + .platform.getDataContract( + contractId, { prove: true }, + ); + + // const dataContractResponse = await dashClient.getDAPIClient().platform.getDataContract( + // contractId, + // ); + + // const dataContract = await dashClient.platform.dpp + // .dataContract.createFromBuffer(dataContractResponse.getDataContract()); + + const fullProof = dataContractResponseWithProof.getProof(); + + testProofStructure(expect, fullProof); + + // const dataContractsProofBuffer = fullProof.storeTreeProofs.getDataContractsProof(); + // + // const parsedStoreTreeProof = parseStoreTreeProof(dataContractsProofBuffer); + // + // expect(parsedStoreTreeProof.values.length).to.be.equal(1); + // + // const restoredDataContract = await dashClient.platform.dpp + // .dataContract.createFromBuffer(parsedStoreTreeProof.values[0]); + // + // expect(restoredDataContract.toObject()).to.be.deep.equal(dataContract.toObject()); + // + // const { rootHash: dataContractsLeafRoot } = executeProof(dataContractsProofBuffer); + // + // const verificationResult = verifyProof( + // dataContractsProofBuffer, + // [contractId], + // dataContractsLeafRoot, + // ); + // + // // We pass one key + // expect(verificationResult.length).to.be.equal(1); + // + // const recoveredDataContractBuffer = verificationResult[0]; + // expect(recoveredDataContractBuffer).to.be.an.instanceof(Uint8Array); + // + // const recoveredDataContract = await dashClient.platform.dpp + // .dataContract.createFromBuffer(recoveredDataContractBuffer); + // + // expect(recoveredDataContract.toObject()).to.be.deep.equal(dataContract.toObject()); + }); + + it('should be able to verify proof that data contract does not exist', async () => { + // The same as above, but for an identity id that doesn't exist + + const dataContractId = generateRandomIdentifier(); + + const dataContractWithProof = await dashClient.getDAPIClient().platform.getDataContract( + dataContractId, { prove: true }, + ); + + const fullProof = dataContractWithProof.proof; + + testProofStructure(expect, fullProof); + + // const dataContractsProofBuffer = fullProof.storeTreeProofs.getDataContractsProof(); + // + // const { rootHash: dataContractsLeafRoot } = executeProof(dataContractsProofBuffer); + // + // const verificationResult = verifyProof( + // dataContractsProofBuffer, + // [dataContractId], + // dataContractsLeafRoot, + // ); + // + // // We pass one key + // expect(verificationResult.length).to.be.equal(1); + // // Data contract doesn't exist, so result is null + // expect(verificationResult[0]).to.be.null(); + }); + }); + + describe('Identities', () => { + describe('Proofs', () => { + let identity; + let identityAtKey5; + let identityAtKey6; + let identityAtKey8; + let nonIncludedIdentityPubKeyHash; + let identity6PublicKeyHash; + let identity8PublicKeyHash; + + before(async () => { + identityAtKey5 = await dashClient.platform.identities.register(10000); + + identityAtKey6 = await dashClient.platform.identities.register(10000); + + identityAtKey8 = await dashClient.platform.identities.register(10000); + + // await waitForBalanceToChange(walletAccount); + + nonIncludedIdentityPubKeyHash = new PrivateKey().toPublicKey().hash; + + // Public key hashes + identity6PublicKeyHash = identityAtKey6.getPublicKeyById(0).hash(); + identity8PublicKeyHash = identityAtKey8.getPublicKeyById(0).hash(); + }); + + it('should be able to get and verify proof that identity exists with getIdentity', async () => { + identity = identityAtKey5; + + const identityProof = await dashClient.getDAPIClient().platform.getIdentity( + identity.getId(), { prove: true }, + ); + + const fullProof = identityProof.proof; + + testProofStructure(expect, fullProof); + + // const identitiesProofBuffer = fullProof.storeTreeProofs.getIdentitiesProof(); + // + // const parsedStoreTreeProof = parseStoreTreeProof(identitiesProofBuffer); + // + // const parsedIdentity = dashClient.platform.dpp + // .identity.createFromBuffer(parsedStoreTreeProof.values[0]); + // expect(identity.getId()).to.be.deep.equal(parsedIdentity.getId()); + // + // const { rootHash: identityLeafRoot } = executeProof(identitiesProofBuffer); + // + // const verificationResult = verifyProof( + // identitiesProofBuffer, + // [identity.getId()], + // identityLeafRoot, + // ); + // + // // We pass one key + // expect(verificationResult.length).to.be.equal(1); + // // Identity with id at index 0 doesn't exist + // const recoveredIdentityBuffer = verificationResult[0]; + // expect(recoveredIdentityBuffer).to.be.an.instanceof(Uint8Array); + // + // const recoveredIdentity = dashClient.platform.dpp + // .identity.createFromBuffer(recoveredIdentityBuffer); + // + // // Deep equal won't work in this case, because identity returned by the register + // const actualIdentity = identity.toObject(); + // // Because the actual identity state is before the registration, and the + // // balance wasn't added to it yet + // actualIdentity.balance = recoveredIdentity.toObject().balance; + // expect(recoveredIdentity.toObject()).to.be.deep.equal(actualIdentity); + }); + + it('should be able to verify proof that identity does not exist', async () => { + // The same as above, but for an identity id that doesn't exist + const fakeIdentityId = generateRandomIdentifier(); + + const identityProof = await dashClient.getDAPIClient().platform.getIdentity( + fakeIdentityId, { prove: true }, + ); + + const fullProof = identityProof.proof; + + testProofStructure(expect, fullProof); + + // const identitiesProofBuffer = fullProof.storeTreeProofs.getIdentitiesProof(); + // + // // const rootTreeProof = parseRootTreeProof(fullProof.rootTreeProof); + // const parsedStoreTreeProof = parseStoreTreeProof(identitiesProofBuffer); + // + // const identitiesFromProof = parsedStoreTreeProof.values; + // + // const valueIds = identitiesFromProof.map((identityValue) => dashClient.platform.dpp + // .identity.createFromBuffer(identityValue).getId().toString('hex')); + // + // // The proof will contain left and right values to the empty place + // expect(valueIds.indexOf(fakeIdentityId.toString('hex'))).to.be.equal(-1); + // + // const { rootHash: identityLeafRoot } = executeProof(identitiesProofBuffer); + // + // const identityIdsToProve = [fakeIdentityId]; + // + // const verificationResult = verifyProof( + // identitiesProofBuffer, + // identityIdsToProve, + // identityLeafRoot, + // ); + // + // // We pass one key + // expect(verificationResult.length).to.be.equal(1); + // // Identity with id at index 0 doesn't exist + // expect(verificationResult[0]).to.be.null(); + }); + + it('should be able to verify that multiple identities exist with getIdentitiesByPublicKeyHashes', async () => { + const publicKeyHashes = [ + identity6PublicKeyHash, + nonIncludedIdentityPubKeyHash, + identity8PublicKeyHash, + ]; + + /* Requesting identities by public key hashes and verifying the structure */ + + const identityProof = await dashClient.getDAPIClient().platform + .getIdentitiesByPublicKeyHashes( + publicKeyHashes, { prove: true }, + ); + + const fullProof = identityProof.proof; + + testProofStructure(expect, fullProof); + + // const identitiesProofBuffer = fullProof.storeTreeProofs.getIdentitiesProof(); + // const publicKeyHashesProofBuffer = fullProof.storeTreeProofs + // .getPublicKeyHashesToIdentityIdsProof(); + // + // /* Parsing values from the proof */ + // + // const parsedIdentitiesStoreTreeProof = parseStoreTreeProof(identitiesProofBuffer); + // + // // Existing identities should be in the identitiesProof, as it also serves + // // as an inclusion proof + // const restoredIdentities = parsedIdentitiesStoreTreeProof.values.map( + // (identityBuffer) => dashClient.platform.dpp.identity.createFromBuffer( + // identityBuffer, + // ), + // ); + // + // /* Figuring out what was found */ + // + // const foundIdentityIds = []; + // const notFoundPublicKeyHashes = []; + // + // // Scanning through public keys to figure out what identities were found + // for (const publicKeyHash of publicKeyHashes) { + // const foundIdentity = restoredIdentities + // .find( + // (restoredIdentity) => restoredIdentity.getPublicKeyById(0) + // .hash().toString('hex') === publicKeyHash.toString('hex'), + // ); + // if (foundIdentity) { + // foundIdentityIds.push(foundIdentity.getId()); + // } else { + // notFoundPublicKeyHashes.push(publicKeyHash); + // } + // } + // + // // We expect to find 2 identities out of 3 keys + // expect(foundIdentityIds.length).to.be.equal(2); + // expect(notFoundPublicKeyHashes.length).to.be.equal(1); + // + // // Note that identities in the proof won't necessary preserve the order in which they + // // were requested. This happens due to the proof structure: sorting values in the + // // proof would result in a different root hash. + // expect(foundIdentityIds.findIndex( + // (identityId) => identityId.toString('hex') === + // identityAtKey6.getId().toString('hex'), + // )).to.be.greaterThan(-1); + // expect(foundIdentityIds.findIndex( + // (identityId) => identityId.toString('hex') === + // identityAtKey8.getId().toString('hex'), + // )).to.be.greaterThan(-1); + // + // expect(notFoundPublicKeyHashes[0]).to.be.deep.equal(nonIncludedIdentityPubKeyHash); + // + // // Non-existing public key hash should be included into the identityIdsProof, + // // as it serves as a non-inclusion proof for the public keys + // + // /* Extracting root */ + // + // // While extracting the root isn't specifically useful for this test, + // // it is needed to fit those roots into the root tree later. + // const { rootHash: identityLeafRoot } = executeProof(identitiesProofBuffer); + // const { rootHash: identityIdsLeafRoot } = executeProof(publicKeyHashesProofBuffer); + // + // /* Inclusion proof */ + // + // // Note that you first has to parse values from the + // // proof and find identity ids you were looking for + // const inclusionVerificationResult = verifyProof( + // identitiesProofBuffer, + // foundIdentityIds, + // identityLeafRoot, + // ); + // + // expect(inclusionVerificationResult.length).to.be.equal(2); + // + // const firstRecoveredIdentityBuffer = inclusionVerificationResult[0]; + // const secondRecoveredIdentityBuffer = inclusionVerificationResult[1]; + // expect(firstRecoveredIdentityBuffer).to.be.an.instanceof(Uint8Array); + // expect(secondRecoveredIdentityBuffer).to.be.an.instanceof(Uint8Array); + // + // const firstRecoveredIdentity = dashClient.platform.dpp + // .identity.createFromBuffer(firstRecoveredIdentityBuffer); + // + // const secondRecoveredIdentity = dashClient.platform.dpp + // .identity.createFromBuffer(secondRecoveredIdentityBuffer); + // + // // Deep equal won't work in this case, because identity returned by the register + // const actualIdentityAtKey6 = identityAtKey6.toObject(); + // const actualIdentityAtKey8 = identityAtKey8.toObject(); + // // Because the actual identity state is before the registration, and the + // // balance wasn't added to it yet + // actualIdentityAtKey6.balance = firstRecoveredIdentity.toObject().balance; + // actualIdentityAtKey8.balance = secondRecoveredIdentity.toObject().balance; + // + // expect(firstRecoveredIdentity.toObject()).to.be.deep.equal(actualIdentityAtKey6); + // expect(secondRecoveredIdentity.toObject()).to.be.deep.equal(actualIdentityAtKey8); + // + // /* Non-inclusion proof */ + // + // const nonInclusionVerificationResult = verifyProof( + // publicKeyHashesProofBuffer, + // notFoundPublicKeyHashes, + // identityIdsLeafRoot, + // ); + // + // expect(nonInclusionVerificationResult.length).to.be.equal(1); + // + // const nonIncludedIdentityId = nonInclusionVerificationResult[0]; + // expect(nonIncludedIdentityId).to.be.null(); + }); + }); + }); + }); + + describe.skip('Root Tree Proof', () => { + it('should be correct for all endpoints', async () => { + // This test requests all endpoints instead of having multiple test for each endpoint + // on purpose. + // + // The reason being is that when verifying merkle proof, you usually need some value to + // compare it to, and platform doesn't provide one. There are two ways to verify that + // the root tree proof is working: either by knowing its root in advance, or by + // verifying it's signature that is also included in the response. + // Verifying signature requires verifying the header chain, which is not + // currently implemented in the JS SDK (Although it is implemented in Java and iOS SDK). + // So we left with only one option: to know the proof in advance. + // Platform doesn't give it directly, but we can reconstruct it from + // store tree leaves. This if fine in this case because this test doesn't test + // store tree proofs (every endpoint has its own separate store tree proof test). + // By making requests to all endpoints we can recover all leaves hashes, and construct + // the original root tree from it. Then we can get the root from that tree and use it + // as a reference root when verifying the root tree proof. + + const dapiClient = await dashClient.getDAPIClient(); + const identityId = Identifier.from(dpnsOwnerId); + const identity = await dashClient.platform.identities.get(identityId); + + const [ + identityResponse, + contractsResponse, + documentsResponse, + identitiesByPublicKeyHashesResponse, + ] = await Promise.all([ + dapiClient.platform.getIdentity(identityId, { prove: true }), + dapiClient.platform.getDataContract(contractId, { prove: true }), + dapiClient.platform.getDocuments(contractId, 'preorder', { + where: [['$id', '==', identityId]], + prove: true, + }), + dapiClient.platform.getIdentitiesByPublicKeyHashes( + [identity.getPublicKeyById(0).getData()], { prove: true }, + ), + ]); + + const identityProof = MerkleProof.fromBuffer( + identityResponse.proof.rootTreeProof, blake3, + ); + const contractsProof = MerkleProof.fromBuffer( + contractsResponse.proof.rootTreeProof, blake3, + ); + const documentsProof = MerkleProof.fromBuffer( + documentsResponse.proof.rootTreeProof, blake3, + ); + const identitiesByPublicKeyHashesProof = MerkleProof.fromBuffer( + identitiesByPublicKeyHashesResponse.proof.rootTreeProof, blake3, + ); + + const { rootHash: identityLeaf } = executeProof( + identityResponse.proof.storeTreeProofs.getIdentitiesProof(), + ); + + const { rootHash: contractsLeaf } = executeProof( + contractsResponse.proof.storeTreeProofs.getDataContractsProof(), + ); + const { rootHash: documentsLeaf } = executeProof( + documentsResponse.proof.storeTreeProofs.getDocumentsProof(), + ); + + const reconstructedLeaves = [ + identityProof.getProofHashes()[0], + identityLeaf, + contractsLeaf, + documentsLeaf, + documentsProof.getProofHashes()[0], + ]; + + const reconstructedTree = new MerkleTree(reconstructedLeaves, blake3); + const treeLayers = reconstructedTree.getHexLayers(); + const reconstructedAppHash = Buffer.from(reconstructedTree.getRoot()).toString('hex'); + + const identityProofRoot = Buffer.from(identityProof.calculateRoot([1], [identityLeaf], 6)).toString('hex'); + const contractsProofRoot = Buffer.from(contractsProof.calculateRoot([3], [contractsLeaf], 6)).toString('hex'); + const documentsProofRoot = Buffer.from(documentsProof.calculateRoot([4], [documentsLeaf], 6)).toString('hex'); + + expect(identityProof.getHexProofHashes()).to.be.deep.equal([ + treeLayers[0][0], + treeLayers[1][1], + treeLayers[1][2], + ]); + + expect(contractsProof.getHexProofHashes()).to.be.deep.equal([ + treeLayers[0][2], + treeLayers[1][0], + treeLayers[1][2], + ]); + + expect(documentsProof.getHexProofHashes()).to.be.deep.equal([ + treeLayers[0][5], + treeLayers[2][0], + ]); + + expect(identitiesByPublicKeyHashesProof.getHexProofHashes()).to.be.deep.equal([ + treeLayers[0][0], + treeLayers[0][3], + treeLayers[1][2], + ]); + + expect(identityProofRoot).to.be.equal(reconstructedAppHash); + expect(contractsProofRoot).to.be.equal(reconstructedAppHash); + expect(documentsProofRoot).to.be.equal(reconstructedAppHash); + }); + }); + }); +}); diff --git a/packages/platform-test-suite/test/functional/platform/waitForStateTransitionResult.spec.js b/packages/platform-test-suite/test/functional/platform/waitForStateTransitionResult.spec.js new file mode 100644 index 00000000000..51b2ac9fa6d --- /dev/null +++ b/packages/platform-test-suite/test/functional/platform/waitForStateTransitionResult.spec.js @@ -0,0 +1,89 @@ +const Dash = require('dash'); +const crypto = require('crypto'); + +const { MerkleProof } = require('js-merkle'); +const { executeProof } = require('@dashevo/merk'); + +const createClientWithFundedWallet = require('../../../lib/test/createClientWithFundedWallet'); + +const parseStoreTreeProof = require('../../../lib/parseStoreTreeProof'); +const hashFunction = require('../../../lib/proofHashFunction'); + +describe.skip('Platform', () => { + describe('waitForStateTransitionResult', () => { + let dpp; + let client; + let blake3; + + before(async () => { + dpp = new Dash.PlatformProtocol(); + await dpp.initialize(); + + await hashFunction.init(); + blake3 = hashFunction.hashFunction; + + client = await createClientWithFundedWallet(); + }); + + after(async () => { + if (client) { + await client.disconnect(); + } + }); + + it('should return a correct proof with a value', async () => { + const account = await client.getWalletAccount(); + + const { + transaction: assetLockTransaction, + privateKey: assetLockPrivateKey, + outputIndex: assetLockOutputIndex, + } = await client.platform.identities.utils + .createAssetLockTransaction(10000); + + // Broadcast Asset Lock transaction + await account.broadcastTransaction(assetLockTransaction); + const assetLockProof = await client.platform.identities.utils + .createAssetLockProof(assetLockTransaction, assetLockOutputIndex); + + const { + identity, identityCreateTransition, + } = await client.platform.identities.utils + .createIdentityCreateTransition(assetLockProof, assetLockPrivateKey); + + const hash = crypto.createHash('sha256') + .update(identityCreateTransition.toBuffer()) + .digest(); + + await client.platform.broadcastStateTransition( + identityCreateTransition, + ); + + /* Waiting for the result and parse the proof */ + + const result = await client.getDAPIClient() + .platform + .waitForStateTransitionResult(hash, { prove: true }); + + const { rootTreeProof } = result.proof; + const identitiesProofBuffer = result.proof.storeTreeProofs.identitiesProof; + + const parsedStoreTreeProof = parseStoreTreeProof(identitiesProofBuffer); + + const { rootHash: identityLeafRoot } = executeProof(identitiesProofBuffer); + + const identityProof = MerkleProof.fromBuffer( + rootTreeProof, blake3, + ); + Buffer + .from( + identityProof.calculateRoot([1], [identityLeafRoot], 6), + ) + .toString('hex'); + const parsedIdentity = client.platform.dpp + .identity.createFromBuffer(parsedStoreTreeProof.values[0]); + + expect(identity.getId()).to.be.deep.equal(parsedIdentity.getId()); + }); + }); +}); diff --git a/packages/wallet-lib/.env.example b/packages/wallet-lib/.env.example new file mode 100644 index 00000000000..433998c1d55 --- /dev/null +++ b/packages/wallet-lib/.env.example @@ -0,0 +1,3 @@ +DAPI_SEED= +FAUCET_PRIVATE_KEY= +NETWORK= diff --git a/packages/wallet-lib/.eslintignore b/packages/wallet-lib/.eslintignore new file mode 100644 index 00000000000..05e1ee023de --- /dev/null +++ b/packages/wallet-lib/.eslintignore @@ -0,0 +1,3 @@ +fixtures +dist +tests diff --git a/packages/wallet-lib/.eslintrc b/packages/wallet-lib/.eslintrc new file mode 100644 index 00000000000..c61970caa3a --- /dev/null +++ b/packages/wallet-lib/.eslintrc @@ -0,0 +1,8 @@ +{ + "extends": "airbnb-base", + "env": { + "node": true, + "mocha": true + }, + "ignorePatterns": ["**/*.spec.js"] +} diff --git a/packages/wallet-lib/.gitignore b/packages/wallet-lib/.gitignore new file mode 100644 index 00000000000..1521c8b7652 --- /dev/null +++ b/packages/wallet-lib/.gitignore @@ -0,0 +1 @@ +dist diff --git a/packages/wallet-lib/.mocharc.yml b/packages/wallet-lib/.mocharc.yml new file mode 100644 index 00000000000..22ec2faa74f --- /dev/null +++ b/packages/wallet-lib/.mocharc.yml @@ -0,0 +1,4 @@ +exit: true +timeout: 3000 +file: + - ./src/test/bootstrap.js diff --git a/packages/wallet-lib/.nycrc.yml b/packages/wallet-lib/.nycrc.yml new file mode 100644 index 00000000000..cd9cb4384dd --- /dev/null +++ b/packages/wallet-lib/.nycrc.yml @@ -0,0 +1,9 @@ +nyc: + check-coverage: false + watermarks: + lines: [80, 95] + functions: [80, 95] + branches: [80, 95] + statements: [80, 95] +reporter: + - text diff --git a/packages/wallet-lib/CHANGELOG.md b/packages/wallet-lib/CHANGELOG.md new file mode 100644 index 00000000000..042aa9e837e --- /dev/null +++ b/packages/wallet-lib/CHANGELOG.md @@ -0,0 +1,444 @@ +# [7.21.0](https://github.com/dashevo/wallet-lib/compare/v7.21.0...v7.21.0) (2021-10-21) + + +### Features + +* provide plugin dependencies sorting ([#281](https://github.com/dashevo/wallet-lib/issues/281)) +* **Keychain:** `getHardenedDIP15AccountKey`, `getDIP15ExtendedPrivateKey` ([#282](https://github.com/dashevo/wallet-lib/issues/282)) +* overridable coinbase maturity value ([#322](https://github.com/dashevo/wallet-lib/issues/322)) +* retry policy of unconfirmed transaction from stream ([#304](https://github.com/dashevo/wallet-lib/issues/304)), closes [#303](https://github.com/dashevo/wallet-lib/issues/303) +* implement transaction metadata handling ([#291](https://github.com/dashevo/wallet-lib/issues/291), [#303](https://github.com/dashevo/wallet-lib/issues/303)) +* transaction history ([#295](https://github.com/dashevo/wallet-lib/issues/295)), closes [#303](https://github.com/dashevo/wallet-lib/issues/303) +* **TransactionSyncWorker:** improve stream response handling ([#323](https://github.com/dashevo/wallet-lib/issues/323), [#339](https://github.com/dashevo/wallet-lib/issues/339), [#338](https://github.com/dashevo/wallet-lib/issues/338), [#336](https://github.com/dashevo/wallet-lib/issues/336)) +* prevent broadcast and throw error on transaction below min relay fee ([#305](https://github.com/dashevo/wallet-lib/issues/305)) +* provide watch-only mode for public key and address based wallet ([#290](https://github.com/dashevo/wallet-lib/issues/290)) + + +### Bug Fixes + +* logger doesn't work in the browser ([#330](https://github.com/dashevo/wallet-lib/issues/330)) +* typings fix for `waitForInstantLock` ([#289](https://github.com/dashevo/wallet-lib/issues/289)) +* correct logging of identity fetched ([#310](https://github.com/dashevo/wallet-lib/issues/310)) +* correctly announced when a plugin is initialized ([#321](https://github.com/dashevo/wallet-lib/issues/321)) +* `stream.cancel` were causing double-free and segfault ([#328](https://github.com/dashevo/wallet-lib/issues/328), [#329](https://github.com/dashevo/wallet-lib/issues/329)) +* grpc-web doesn't throw cancel error ([#332](https://github.com/dashevo/wallet-lib/issues/332)) + + +### Refactoring + +* **Keychain**: proper naming for BIP44 and DIP9 get keys methods ([#288](https://github.com/dashevo/wallet-lib/issues/288)) + + +### BREAKING CHANGES + +* `getHardenedBIP44Path` renamed to `getHardenedBIP44HDKey` +* `getHardenedDIP9FeaturePath` renamed to `getHardenedDIP9FeatureHDKey` + + + +## [7.20.1](https://github.com/dashevo/wallet-lib/compare/v7.20.0...v7.20.1) (2021-07-28) + + +### Bug Fixes + +* `InvalidResponse` error when connecting to older networks with newer client ([#284](https://github.com/dashevo/wallet-lib/issues/284)) + + + +# [7.20.0](https://github.com/dashevo/wallet-lib/compare/v7.19.2...v7.20.0) (2021-07-09) + + +### Features + +* add `waitForInstantLockTimeout` Wallet Option ([#270](https://github.com/dashevo/wallet-lib/issues/270)) + + +### Bug Fixes + +* `waitForInstantLock` timeouts stuck in event loop ([#272](https://github.com/dashevo/wallet-lib/issues/272)) + + + +## [7.19.2](https://github.com/dashevo/wallet-lib/compare/v7.19.1...v7.19.2) (2021-05-28) + + +### Bugfixes + +* platform queries with binary fields not serialized properly ([#264](https://github.com/dashevo/wallet-lib/pull/264)) + +## [7.19.1](https://github.com/dashevo/wallet-lib/compare/v7.19.0...v7.19.1) (2021-05-20) + + +### Chores + +* update dpp and dapi-client to 0.19.2 ([#258](https://github.com/dashevo/wallet-lib/pull/258)) + + +# [7.19.0](https://github.com/dashevo/wallet-lib/compare/v7.18.1...v7.19.0) (2021-05-03) + + +### Features + +* update dpp with verifyInstantLock method ([#234](https://github.com/dashevo/wallet-lib/issues/234)) + + +### BREAKING CHANGES + +* `Account#fetchStatus` response format is changed and not compatible with the previous version +* `Transport#getStatus` responded format is changed and not compatible with the previous version + + + +## [7.18.1](https://github.com/dashevo/wallet-lib/compare/v7.18.0...v7.18.1) (2021-04-28) + + +### Bug Fixes + +* transaction.isConbase is not a function ([#246](https://github.com/dashevo/wallet-lib/issues/246)) + + + +# [7.18.0](https://github.com/dashevo/wallet-lib/compare/v7.17.2...v7.18.0) (2021-03-03) + + +### Bug Fixes + +* UTXO being mixed up with multiple account in parallel ([#233](https://github.com/dashevo/wallet-lib/issues/233)) + + +### Features + +* workers and plugins error handling using events ([#221](https://github.com/dashevo/wallet-lib/issues/221)) +* aligning identityIndex as defined in DIP13 as hardened ([#222](https://github.com/dashevo/wallet-lib/issues/222)) +* add `skipSynchronizationBeforeHeight` unsafe option ([#217](https://github.com/dashevo/wallet-lib/issues/217)) + + +### Refactoring + +* Identities class ([#227](https://github.com/dashevo/wallet-lib/issues/227)) + + +### BREAKING CHANGES + +* to access identities from account use `account.identities` property +* previous identity created with DashJS / Wallet-lib would not be resolvable anymore + + + +## [7.17.2](https://github.com/dashevo/wallet-lib/compare/v7.17.1...v7.17.2) (2020-12-30) + + +### Bug Fixes + +* broadcastStateTransition is timing out on testnet ([#214](https://github.com/dashevo/wallet-lib/issues/214)) + + + +## [7.17.1](https://github.com/dashevo/wallet-lib/compare/v7.17.0...v7.17.1) (2020-12-30) + + +### Bug Fixes + +* merkleRootQuorums from the diff doesn’t match calculated quorum root after diff is applied ([#212](https://github.com/dashevo/wallet-lib/issues/212)) + + + +# [7.17.0](https://github.com/dashevo/wallet-lib/compare/v7.16.1...v7.17.0) (2020-12-29) + + +### Features + +* connect to testnet by default ([#210](https://github.com/dashevo/wallet-lib/issues/210)) +* update `dpp`, `dapi-client`, `dashcore-lib` ([#202](https://github.com/dashevo/wallet-lib/issues/202)) +* handle instant locks ([#206](https://github.com/dashevo/wallet-lib/issues/206)) + + +### BREAKING CHANGES + +* wallet is now connecting to a testnet by default + + + +## [7.16.1](https://github.com/dashevo/wallet-lib/compare/v7.16.0...v7.16.1) (2020-10-28) + + +### Bug Fixes + +* buggy version of protobufjs is used ([#200](https://github.com/dashevo/wallet-lib/issues/200)) + + + +# [7.16.0](https://github.com/dashevo/wallet-lib/compare/v7.15.1...v7.16.0) (2020-10-27) + + +### Bug Fixes + +* transaction sign and OP_RETURN + import ([#188](https://github.com/dashevo/wallet-lib/issues/188)) + + +### Chore + +* update to DAPI Client 0.16 ([#196](https://github.com/dashevo/wallet-lib/issues/196), [#197](https://github.com/dashevo/wallet-lib/issues/197)) + + +### BREAKING CHANGES + +* replaced `Transport#getIdentityIdByFirstPublicKey(string):` string with `Transport#getIdentityIdsByPublicKeyHashes(Buffer[]): Buffer[]` + + + +# [7.15.1](https://github.com/dashevo/wallet-lib/compare/v7.15.0...v7.15.1) (2020-09-11) + + +### Bug Fixes + +* false positive merkle blocks shouldn't be imported into the storage ([#185](https://github.com/dashevo/wallet-lib/issues/185)) + +# [7.15.0](https://github.com/dashevo/wallet-lib/compare/v7.14.0...v7.15.0) (2020-09-04) + + +### Bug Fixes + +* confirmation might come before broadcast ACK ([#183](https://github.com/dashevo/wallet-lib/issues/183)) +* outdated create transaction typing ([#180](https://github.com/dashevo/wallet-lib/issues/180)) + + +### Code Refactoring + +* switch from getUTXO to subscribeToTransactions ([#119](https://github.com/dashevo/wallet-lib/issues/119)) + + +### BREAKING CHANGES + +* removed `subscribeToAddressesTransactions`, `getUTXO` and `getAddressSummary` from the transport +* removed `Account#fetchAddressInfo` method + + + +# [7.14.0](https://github.com/dashevo/wallet-lib/compare/v7.13.4...v7.14.0) (2020-07-23) + + +### Bug Fixes + +* merge conflict artefact issue ([#170](https://github.com/dashevo/wallet-lib/issues/170)) +* outdated network option values ([#167](https://github.com/dashevo/wallet-lib/issues/167)) + + +### Features + +* run tests against mn-bootstrap instead of devnet ([#168](https://github.com/dashevo/wallet-lib/issues/168)) +* update to DAPI Client 0.14 and refactor transport layer ([#163](https://github.com/dashevo/wallet-lib/issues/163)) + + +### Documentation + +* readme standard updates ([#165](https://github.com/dashevo/wallet-lib/issues/165)) +* update documentation and definitions files ([#154](https://github.com/dashevo/wallet-lib/issues/154)) + + +### BREAKING CHANGES + +* `transporter` option is replaced with `transport` that accepts [DAPI Client options](https://github.com/dashevo/dapi-client/blob/1ec21652f1615ba95ea537c38632692f81deefa3/lib/DAPIClient.js#L42-L51) or a Transport instance. + + + +## [7.13.4](https://github.com/dashevo/wallet-lib/compare/v7.13.3...v7.13.4) (2020-07-01) + + +### Bug Fixes + +* simple transaction do not have any 4 inputs limitation ([#158](https://github.com/dashevo/wallet-lib/issues/158)) ([11d8d01](https://github.com/dashevo/wallet-lib/commit/11d8d011a15e9000dfd8dc4bd22c449334835767)) +* **account:** forward all storage events ([#159](https://github.com/dashevo/wallet-lib/issues/159)) ([e5c807e](https://github.com/dashevo/wallet-lib/commit/e5c807e1d0132d6fe0538e05f04e760ff0c0b1f3)) + + +### Features + +* update dashcore-lib and DAPI Client ([#161](https://github.com/dashevo/wallet-lib/issues/161)) ([81536d2](https://github.com/dashevo/wallet-lib/commit/81536d2235e335fed5fa53752b77260a4a7fa367)) + + + +## [7.13.4](https://github.com/dashevo/wallet-lib/compare/v7.13.3...v7.13.4) (2020-07-01) + + +### Bug Fixes + +* simple transaction do not have any 4 inputs limitation ([#158](https://github.com/dashevo/wallet-lib/issues/158)) +* **account:** forward all storage events ([#159](https://github.com/dashevo/wallet-lib/issues/159)) + + +### Features + +* update dashcore-lib and DAPI Client ([#161](https://github.com/dashevo/wallet-lib/issues/161)) + + + +# [7.13.3](https://github.com/dashevo/wallet-lib/compare/v7.13.2...v7.13.3) (2020-06-16) + +- **Fixes:** + * fix!: createTransaction should be checking for 'recipient' instead of 'address' in 'txOpts.recipients' ([#152](https://github.com/dashevo/wallet-lib/pull/152)) + * fix: transaction hash not present on address ([#151](https://github.com/dashevo/wallet-lib/pull/151)) + +- **Breaking changes:** + * Previously, the documentation stated a usage on `createTransaction()` with multiples recipients as such: `recipients:[{recipient,satoshis}]`. + However, the code where still referring and expecting recipients `recipients:[{address,satoshis}]`. + This version fixes that inconsistency. + +# [7.13.2](https://github.com/dashevo/wallet-lib/compare/v7.13.1...v7.13.2) (2020-06-15) + +- **Features:** + * feature: Worker will now have ability to return a value on onStart and onExecute ([#149](https://github.com/dashevo/wallet-lib/pull/149)) + +- **Fixes:** + * fix: comportement on new address with existing transaction in store ([#147](https://github.com/dashevo/wallet-lib/pull/147)) + * fix: SyncUp plugin not awaiting long enough ([#149](https://github.com/dashevo/wallet-lib/pull/149)) + +# [7.13.1](https://github.com/dashevo/wallet-lib/compare/v7.13.0...v7.13.1) (2020-06-15) + +- **Fixes:** + * fix(Storage): identityIds being restate to empty array ([#143](https://github.com/dashevo/wallet-lib/pull/143)) + +# [7.13.0](https://github.com/dashevo/wallet-lib/compare/v7.1.4...v7.13.0) (2020-06-13) + +- **Feat:** + * sync of identities associated with wallet ([#142](https://github.com/dashevo/wallet-lib/pull/142)) + +- **Breaking changes:** + * `Account#getIdentityHDKey` is removed in favor of `Account#getIdentityHDKeyByIndex(identityIndex, keyIndex)` + * `debug` option temporary disabled + +# [7.1.4](https://github.com/dashevo/wallet-lib/compare/v7.1.3...v7.1.4) (2020-06-11) + +- **Builds, Tests:** + - test: create a new wallet in functional tests (#140) + - build: simplify distributive and Travis CI builds (#139) + +# [7.1.3](https://github.com/dashevo/wallet-lib/compare/v7.1.2...v7.1.3) (2020-06-10) + +- **Chore:** + - chore: Update dashcore-lib version (#138) + +# [7.1.2](https://github.com/dashevo/wallet-lib/compare/v7.1.1...v7.1.2) (2020-06-10) + +- **Feat:** + - feat: TransactionOrderer (#136) + +# [7.1.1](https://github.com/dashevo/wallet-lib/compare/v7.1.0...v7.1.1) (2020-06-03) + +- **Fixes:** + - fix: broadcastTransaction not throwing an error when a transaction wasn't broadcasted (#133) + - fix: internal UTXO on Output format and getUTXO returning UnspentOutput + refactor initial sync up (#135) + +# [7.1.0](https://github.com/dashevo/wallet-lib/compare/v7.0.0...v7.1.0) (2020-06-03) + +- **Fixes:** + - fix: unavailable previous transactions history (#131) + - fix: transporter.resolve to extend passed options (#130) + +# [7.0.0](https://github.com/dashevo/wallet-lib/compare/v6.1.2...v7.0.0) (2020-06-01) + +- **Impr:** + - impr!: removed eventemitter2 (#128) + +- **Fixes:** + - fix!: handling errors on account init (#127) + +- **Chore, Docs & Tests:** + - tests: replace browser.js to wallet.js in karma.conf (#126) + +# [6.1.2](https://github.com/dashevo/wallet-lib/compare/v6.1.1...v6.1.2) (2020-05-22) + +- **Fixes:** + - fix: update evonet seeds (#120) + +- **Chore, Docs & Tests:** + - tests: added karma and functional browser test (#121) + - style: removed logger.error & improved error message (#118) + +# [6.1.1](https://github.com/dashevo/wallet-lib/compare/v6.1.0...v6.1.1) (2020-05-22) + +- **Fixes:** + - fix: update evonet seeds (#120) + +# [6.1.0](https://github.com/dashevo/wallet-lib/compare/v6.0.0...v6.1.0) (2020-04-23) + +- **Features:** + - Feat(Transporter): added .getBestBlock / .getBestBlockHeader (#110 ) + +- **Fixes:** + - Fix : Support for DAPIClient.getUTXO with more than 1000 utxos (#111 ) + - Fix: Empty confirmed balance (#109) + - Refact: Removed Identity Types + dpp (#114) + - Fix: Removed palinka, updated seeds (#117) + +- **Chore, Docs & Tests:** + - Doc: fixed link and duplicates (#113) + - Tests: refactorate + fakenet (#115) + +# [6.0.0](https://github.com/dashevo/wallet-lib/compare/v5.0.3...v6.0.0) (2020-03-10) + + +- **breaking:** + - Wallet: + - Wallet({transport}) is now Wallet({transporter}) (#102) + - Account: + - account.transport is now account.transporter (#102) + - account.transport.transport is now account.transporter.client (#102) + - fetchTransactionInfo() is removed. Use getTransaction() instead. (#102) + - .getTransactionHistory() removed (#102, 01d5b31) + - Transporter: + - new Transporter() is now invalid, use Transporters.resolve(arg) instead. (#102) + - Storage: + - Storage cannot be assigned an events anymore (storage.parentEvents now). (#102) + - ChainWorker: + - ChainWorker became a ChainPlugin using subscribeToBlock() (#102) + - misc: + - all events payload will now be returned under form {type, payload} (#102) + - all events are now accessed via .on() instead of .events.on() (#102) + - all events are to be emmited using .emit() instead of .events.emit() (#102) + - format of transactions internally has changed (returns a proper Dashcore Transaction object) (#102) + - internal reference to blockheight changed to blockHeight (#102) + - format of blocks internally has changed (returns a proper Dashcore Block object) (#102) + - format of utxo internally has changed (returns a proper Dashcore UTXO object) (#102) + +- **Feat**: + - Wallet: + - Sweep paper wallet (#83) + - Allow to generate a new privateKey (4e120f6) + - Account: + - added debug parameters (#102) + - Added account.getBlockHeader(identifier) method (#102) + - account.cacheBlockHeaders is now a available option (def: true) + - Storage: + - added Storage.importBlockHeader (#102) + - added Storage.getBlockHeader (#102) + - added Storage.searchBlockHeader (#102) + - Transporter: + - Transporter arg can take devnetName when type is DAPI (connects to palinka instead of evonet). (#102) + - subscribeToAddressesTransaction() (#102) + - subscribeToBlocks() (#102) + - subscribeToBlockHeaders() - temporary for BloomFilters (#102) + - Workers: + - Workers support onStart() method. (#102) + - Plugins: + - Plugins support onStart() method and send a PLUGIN/pluginName/STARTED event. (#102) +- **Impr**: + - moved from('event') to EventEmitter2 + wildcard support (5241ce1, 4db66d6, d20df76) +- **Fix**: + - KeyChain: + - .getKeyForPath when SINGLE_ADDRESS mode is now returned as PrivateKey (#102) + - Account: + - sequential account index + transporter missing method reporting #103 +- **Perf**: + - removed localforage from default adapter. #104 +- **Test**: + - Sweep wallet test + integration (ebbd0f8, +6bd24a3) + - FakeDevnet class (db46b05) + +# [5.0.3](https://github.com/dashevo/wallet-lib/compare/v5.0.2...v5.0.3) (2020-02-01) + +- **Feat**: + - Account: + - getIdentityHDKey (#99) +- **Fix**: + - typos (#98) diff --git a/packages/wallet-lib/LICENSE b/packages/wallet-lib/LICENSE new file mode 100644 index 00000000000..4f23e14a3ad --- /dev/null +++ b/packages/wallet-lib/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-2019 Dash Core Group, Inc. + +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/packages/wallet-lib/README.md b/packages/wallet-lib/README.md new file mode 100644 index 00000000000..42695e91e64 --- /dev/null +++ b/packages/wallet-lib/README.md @@ -0,0 +1,98 @@ +# Wallet Library + +[![NPM Version](https://img.shields.io/npm/v/@dashevo/wallet-lib)](https://www.npmjs.com/package/@dashevo/wallet-lib) +[![Build Status](https://github.com/dashevo/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashevo/platform/actions/workflows/release.yml) +[![Release Date](https://img.shields.io/github/release-date/dashevo/platform)](https://github.com/dashevo/platform/releases/latest) +[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen)](https://github.com/RichardLitt/standard-readme) + +A pure and extensible JavaScript Wallet Library for Dash + +## Table of Contents +- [Install](#install) +- [Usage](#usage) +- [Documentation](#documentation) +- [Maintainers](#maintainers) +- [Contributing](#contributing) +- [License](#license) + + +## Background + +[Dash](https://www.dash.org) is a powerful new peer-to-peer platform for the next generation of financial technology. The decentralized nature of the Dash network allows for highly resilient Dash infrastructure, and the developer community needs reliable, open-source tools to implement Dash apps and services. + +## Install + +### Node + +In order to use this library, you will need to add it to your project as a dependency. + +Having [NodeJS](https://nodejs.org/) installed, just type in your terminal : + +```sh +npm install @dashevo/wallet-lib +``` + +### CDN Standalone + +For browser usage, you can also directly rely on unpkg. Below, we also assume you use [localForage](https://github.com/localForage/localForage) as your persistence adapter. + +``` + + +const wallet = new Wallet({adapter: localforage}); +``` + +## Usage + +In your file, where you want to execute it : + +```js +const { Wallet, EVENTS } = require('@dashevo/wallet-lib'); + +const wallet = new Wallet(); + +// We can dump our initialization parameters +const mnemonic = wallet.exportWallet(); + +wallet.getAccount().then((account) => { + // At this point, account has fetch all UTXOs if they exists + const balance = account.getTotalBalance(); + console.log(`Balance: ${balance}`); + + // We easily can get a new address to fund + const { address } = account.getUnusedAddress(); +}); +``` + +Wallet will by default connects to DAPI and use either localforage (browser based device) or a InMem adapter. +Account will by default be on expected BIP44 path (...0/0). + +### Transports: + +Insight-Client has been removed from MVP and is not working since Wallet-lib v3.0. + +- [DAPI-Client](https://github.com/dashevo/platform/tree/master/packages/js-dapi-client) + +### Adapters : + +- [LocalForage](https://github.com/localForage/localForage) +- [ReactNative AsyncStorage](https://facebook.github.io/react-native/docs/asyncstorage) + +## Documentation + +You can see some [examples here](docs/usage/examples.md). + +More extensive documentation is available at https://dashevo.github.io/platform/Wallet-library/ along with additional [examples & snippets](https://dashevo.github.io/platform/Wallet-library/usage/examples/). + +## Maintainers + +Wallet-Lib is maintained by the [Dash Core Developers](https://www.github.com/dashevo). +We want to thank all members of the community that have submitted suggestions, issues and pull requests. + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/dashevo/platform/issues/new/choose) or submit PRs. + +## License + +[MIT](LICENSE) © Dash Core Group, Inc. diff --git a/packages/wallet-lib/docs/.nojekyll b/packages/wallet-lib/docs/.nojekyll new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/wallet-lib/docs/README.md b/packages/wallet-lib/docs/README.md new file mode 100644 index 00000000000..6a017f9246f --- /dev/null +++ b/packages/wallet-lib/docs/README.md @@ -0,0 +1,64 @@ +## Wallet-lib + +[![NPM Version](https://img.shields.io/npm/v/@dashevo/wallet-lib)](https://www.npmjs.com/package/@dashevo/wallet-lib) +[![Build Status](https://github.com/dashevo/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashevo/platform/actions/workflows/release.yml) +[![Release Date](https://img.shields.io/github/release-date/dashevo/platform)](https://github.com/dashevo/platform/releases/latest) +[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen)](https://github.com/RichardLitt/standard-readme) + +A pure and extensible JavaScript Wallet Library for Dash + +### What it is + +Wallet-lib provides all the wallet features needed for node and browser usage. +From being able to display an account balance, to paying to another address, passing by the need to automate back-end task related to a cold-storage. + +Wallet-lib allows you to easily work with Wallets/Accounts for HDWallets, or from just a single private key. +It also allows you to monitor public keys and HDPubKey. +You might also wish to create your own set of plugins or your own coin selection logic. + +### Install + +### ES5/ES6 via NPM + +In order to use this library in Node, you will need to add it to your project as a dependency. + +Having [NodeJS](https://nodejs.org/) installed, just type in your terminal : + +```sh +npm install @dashevo/wallet-lib +``` + +### CDN Standalone + +For browser usage, you can also directly rely on unpkg : + +``` + +``` + +## Usage + +In your file, where you want to execute it : + +```js +const { Wallet, EVENTS } = require('@dashevo/wallet-lib'); + +const wallet = new Wallet(); + +// We can dump our initialization parameters +const mnemonic = wallet.exportWallet(); + +wallet.getAccount().then((account) => { + // At this point, account has fetch all UTXOs if they exists + const balance = account.getTotalBalance(); + console.log(`Balance: ${balance}`); + + // We easily can get a new address to fund + const { address } = account.getUnusedAddress(); +}); +``` + +## Licence + +[MIT](https://github.com/dashevo/wallet-lib/blob/master/LICENCE.md) © Dash Core Group, Inc. + diff --git a/packages/wallet-lib/docs/_sidebar.md b/packages/wallet-lib/docs/_sidebar.md new file mode 100644 index 00000000000..d44aa0ea626 --- /dev/null +++ b/packages/wallet-lib/docs/_sidebar.md @@ -0,0 +1,119 @@ +- Getting started + - [Quick start](getting-started/quickstart.md) + - [Quick introduction to core concepts](getting-started/core-concepts.md) +- Usage + - [DAPI](usage/dapi.md) + - [Examples](usage/examples.md) + - [Coin Selection](usage/coinSelection.md) + - Account + - [`new Account()`](account/Account.md) + - [`.broadcastTransaction()`](account/broadcastTransaction.md) + - [`.connect()`](account/connect.md) + - [`.createTransaction()`](account/createTransaction.md) + - [`.decode()`](account/decode.md) + - [`.decrypt()`](account/decrypt.md) + - [`.disconnect()`](account/disconnect.md) + - [`.encode()`](account/encode.md) + - [`.encrypt()`](account/encrypt.md) + - [`.fetchAddressInfo()`](account/fetchAddressInfo.md) + - [`.fetchStatus()`](account/fetchStatus.md) + - [`.fetchTransactionInfo()`](account/fetchTransactionInfo.md) + - [`.forceRefreshAccount()`](account/forceRefreshAccount.md) + - [`.generateAddress()`](account/generateAddress.md) + - [`.getAddress()`](account/getAddress.md) + - [`.getConfirmedBalance()`](account/getConfirmedBalance.md) + - [`.getPrivateKeys()`](account/getPrivateKeys.md) + - [`.getTotalBalance()`](account/getTotalBalance.md) + - [`.getTransaction()`](account/getTransaction.md) + - [`.getTransactionHistory()`](account/getTransactionHistory.md) + - [`.getTransactions()`](account/getTransactions.md) + - [`.getUnconfirmedBalance()`](account/getUnconfirmedBalance.md) + - [`.getUTXOS()`](account/getUTXOS.md) + - [`.sign()`](account/sign.md) + - Wallet + - [`new Wallet()`](wallet/Wallet.md) + - [`.createAccount()`](wallet/createAccount.md) + - [`.disconnect()`](wallet/disconnect.md) + - [`.exportWallet()`](wallet/exportWallet.md) + - [`.fromHDPrivateKey()`](wallet/fromHDPrivateKey.md) + - [`.fromHDPublicKey()`](wallet/fromHDPublicKey.md) + - [`.fromMnemonic()`](wallet/fromMnemonic.md) + - [`.fromPrivateKey()`](wallet/fromPrivateKey.md) + - [`.fromSeed()`](wallet/fromSeed.md) + - [`.generateNewWalletId()`](wallet/generateNewWalletId.md) + - [`.getAccount()`](wallet/getAccount.md) + - [`.dumpStorage()`](wallet/dumpStorage.md) + - Identities + - [`new Identities()`](identities/Identities.md) + - [`.getIdentityHDKeyByIndex()`](identities/getIdentityHDKeyByIndex.md) + - KeyChain + - [`new KeyChain()`](keychain/KeyChain.md) + - [`.generateKeyForChild()`](keychain/generateKeyForChild.md) + - [`.generateKeyForPath()`](keychain/generateKeyForPath.md) + - [`.getDIPExtendedKey()`](keychain/getDIPExtendedKey.md) + - [`.getHardenedBIP44HDKey()`](keychain/getHardenedBIP44HDKey.md) + - [`.getHardenedDIP9FeatureHDKey()`](keychain/getHardenedDIP9FeatureHDKey.md) + - [`.getHardenedDIP15AccountKey()`](keychain/getHardenedDIP15AccountKey.md) + - [`.getKeyForChild()`](keychain/getKeyForChild.md) + - [`.getKeyForPath()`](keychain/getKeyForPath.md) + - [`.getPrivateKey()`](keychain/getPrivateKey.md) + - [`.sign()`](keychain/sign.md) + - Storage + - [`new KeyChain()`](storage/Storage.md) + - [`.addNewTxToAddress()`](storage/addNewTxToAddress.md) + - [`.addUTXOToAddress()`](storage/addUTXOToAddress.md) + - [`.announce()`](storage/announce.md) + - [`.calculateDuffBalance()`](storage/calculateDuffBalance.md) + - [`.clearAll()`](storage/clearAll.md) + - [`.configure()`](storage/configure.md) + - [`.createChain()`](storage/createChain.md) + - [`.createWallet()`](storage/createWallet.md) + - [`.getStore()`](storage/getStore.md) + - [`.getTransaction()`](storage/getTransaction.md) + - [`.getTransactionMetadata()`](storage/getTransactionMetadata.md) + - [`.importTransaction()`](storage/importTransaction.md) + - [`.importTransactions()`](storage/importTransactions.md) + - [`.rehydrateState()`](storage/rehydrateState.md) + - [`.saveState()`](storage/saveState.md) + - [`.searchAddress()`](storage/searchAddress.md) + - [`.searchAddressesWithTx()`](storage/searchAddressesWithTx.md) + - [`.searchBlockHeader()`](storage/searchBlockHeader.md) + - [`.searchTransaction()`](storage/searchTransaction.md) + - [`.searchTransactionMetadata()`](storage/searchTransactionMetadata.md) + - [`.searchWallet()`](storage/searchWallet.md) + - [`.startWorker()`](storage/startWorker.md) + - [`.stopWorker()`](storage/stopWorker.md) + - [`.stopWorker()`](storage/stopWorker.md) + - [`.updateTransaction()`](storage/updateTransaction.md) + - Utils + - [`calculateTransactionFees()`](utils/calculateTransactionFees.md) + - [`categorizeTransactions()`](utils/categorizeTransactions.md) + - [`classifyAddresses()`](utils/classifyAddresses.md) + - [`coinSelection()`](utils/coinSelection.md) + - [`dashToDuffs()`](utils/dashToDuffs.md) + - [`duffsToDash()`](utils/duffsToDash.md) + - [`extendTransactionsWithMetadata()`](utils/extendTransactionsWithMetadata.md) + - [`filterTransactions()`](utils/filterTransactions.md) + - [`getBytesOf()`](utils/getBytesOf.md) + - Mnemonic + - [`generateNewMnemonic()`](utils/mnemonic/generateNewMnemonic.md) + - [`mnemonicToHDPrivateKey()`](utils/mnemonic/mnemonicToHDPrivateKey.md) + - [`mnemonicToSeed()`](utils/mnemonic/mnemonicToSeed.md) + - [`mnemonicToWalletId()`](utils/mnemonic/mnemonicToWalletId.md) + - [`seedToHDPrivateKey()`](utils/mnemonic/seedToHDPrivateKey.md) + - Events + - [`FETCHED/UNCONFIRMED_TRANSACTION`](events/fetched_unconfirmed_transaction.md) + - [`FETCHED/CONFIRMED_TRANSACTION`](events/fetched_confirmed_transaction.md) + - [`CONFIRMED_BALANCE_CHANGED`](events/confirmed_balance_changed.md) + - [`UNCONFIRMED_BALANCE_CHANGED`](events/unconfirmed_balance_changed.md) + - [`BLOCKHEIGHT_CHANGED`](events/blockheight_changed.md) + +- Plugins + - [Using a plugin](plugins/using-a-plugin.md) + - [Writing a new plugin](plugins/writing-a-new-plugin.md) + - [Wallet workers](plugins/wallet-workers.md) + - [Community plugins](plugins/community-plugins.md) +- Develop + - [Logging](develop/logging.md) + - [Persistence](develop/persistence.md) +- [License](https://github.com/dashevo/wallet-lib/blob/master/LICENSE) diff --git a/packages/wallet-lib/docs/account/Account.md b/packages/wallet-lib/docs/account/Account.md new file mode 100644 index 00000000000..ae4e32e4d7d --- /dev/null +++ b/packages/wallet-lib/docs/account/Account.md @@ -0,0 +1,33 @@ +**Usage**: `new Account(wallet, accountOpts)` +**Description**: This method creates a new Account associated to the given wallet. +**Notes**: As it is directly linked to a wallet, you might want to rely on `Wallet.getAccount({index})` instead. +When `wallet.offlineMode:true`, you can manage utxos / addresses via a cache options (or after init via the Storage controller). + +Parameters: + +| parameters | type | required | Description | +|-------------------------------------------|-----------------|--------------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **wallet** | Wallet | yes | A valid [wallet](../wallet/Wallet.md) instance | +| **accountOpts.index** | number | no | The BIP44 account index; by default use the next one (n+1) of the biggest account index already created in wallet | +| **accountOpts.strategy** | string/function | no | A valid strategy string identifier (amongst "simpleAscendingAccumulator", "simpleDescendingAccumulator", simpleTransactionOptimizedAccumulator") or your own strategy function | +| **accountOpts.label** | string | no (def: null) | If you want to be able to reference to an account per label | +| **accountOpts.injectDefaultPlugins** | boolean | no (def: true) | Use to inject default plugins on loadup (BIP44Worker, ChainWorker and SyncWorker) | +| **accountOpts.allowSensitiveOperations** | boolean | no (def: false) | If you want a special plugin to access the keychain or other sensitive operation, set this to true. | +| **accountOpts.cacheTx** | boolean | no (def: true) | If you want to cache the transaction internally (for faster sync-up) | +| **accountOpts.cache.addresses** | object | no | If you have your addresses state somewhere else (fs) you can fetch and pass it along for faster sync-up | +| **accountOpts.cache.transactions** | object | no | If you have your tx state somewhere else (fs) you can fetch and pass it along for faster sync-up | + +Returns : Account instance. + +Examples (assuming a Wallet instance created) : + +```js +const { Account, Wallet } = require('@dashevo/wallet-lib'); +const wallet = new Wallet(); +const account = new Account(wallet, {index: 42}); +await account.init(); +``` + +**Reminder**: Because many parameters are inherited from the wallet object (network, plugins, transporter, storage, keychain...), initializing an Account without a Wallet will require mocking all those properties. + +**Identities**: Identities are accessible from an account via the identities interface : `account.identities`. See [Identities](../identities/Identities.md) diff --git a/packages/wallet-lib/docs/account/broadcastTransaction.md b/packages/wallet-lib/docs/account/broadcastTransaction.md new file mode 100644 index 00000000000..9104d10cc52 --- /dev/null +++ b/packages/wallet-lib/docs/account/broadcastTransaction.md @@ -0,0 +1,15 @@ +**Usage**: `account.broadcastTransaction(transaction)` +**Description**: Allow to broadcast a valid **signed** transaction to the network. +**Notes**: Requires a signed transaction, use [`account.sign(transaction)`](../account/sign.md) for that. + +Parameters: + +| parameters | type | required | Description | +|---------------------------------------|--------------------|----------|-------------------------------------------------------------------------------------------------------| +| **transaction** | Transaction/String | yes | A valid [created transaction](../account/createTransaction.md) or it's hexadecimal raw representation | +| **options** | Object | no | | +| **options.skipFeeValidation** | Boolean | no | When set to true, and min relay fee is not met, will still try to broadcast a transaction | +| **options.mempoolPropagationTimeout** | Number | no | The amount of milliseconds to wait for transaction mempool propagation | +Returns : transactionId (string). + +N.B : The TransactionID provided is subject to [transaction malleability](https://dashcore.readme.io/docs/core-guide-transactions-transaction-malleability), and is not a source of truth (the transaction might be included in a block with a different txid). diff --git a/packages/wallet-lib/docs/account/connect.md b/packages/wallet-lib/docs/account/connect.md new file mode 100644 index 00000000000..8eedce2b8f4 --- /dev/null +++ b/packages/wallet-lib/docs/account/connect.md @@ -0,0 +1,9 @@ +**Usage**: `account.connect()` +**Description**: This method can be used to reconnect after having used `.disconnect()`. It will connect to all streams, bloomfilters and will start workers. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-----------|----------------| ------------------------------------------------------------------------------- | + +Returns : Boolean (true). diff --git a/packages/wallet-lib/docs/account/createTransaction.md b/packages/wallet-lib/docs/account/createTransaction.md new file mode 100644 index 00000000000..7fec479c0fb --- /dev/null +++ b/packages/wallet-lib/docs/account/createTransaction.md @@ -0,0 +1,83 @@ +**Usage**: `account.createTransaction(txOpts)` +**Description**: Allow to create a transaction to one or multiple recipients. + +Parameters: + +| parameters | type | required | Description | +|-------------------------------|-------------------------------|------------------------------| ----------------------------------------------------------------------------------------------------------------------------------- | +| **txOpts.recipient** | string | yes (if no `recipients`) | The external address recipient of this transaction | +| **txOpts.satoshis** | string | yes (if no `recipients` set) | The value amount to transfer to the recipient address | +| **txOpts.recipients** | Array[{recipient, satoshis}] | no | Alternatively, you can use this to send to multiple address/amount. Array arra of {recipient, satoshis} | +| **txOpts.utxos** | Array[utxos] | no | Can be specified to use specific utxo to use, or other utxos own by other private keys (you will need to pass the privateKeys along | +| **txOpts.privateKeys** | Array[PrivateKey/HDPrivateKey]| no | Overwrite the default behaviour (searching locally for keys) and uses these to sign instead. | +| **txOpts.strategy** | string/Function | no | Overwrite the default strategy used (using account default or specified strategy) | +| **txOpts.deductFee** | boolean | no | Defaults: true. When set at false, will not deduct fee on the Transaction object | +| **txOpts.change** | string | no | Defaults: `account.getUnusedAddress(internal)`. When set, will use that address as a change address on remaining fund | + + +Returns : [Transaction](https://dashevo.github.io/platform/SDK/usage/dashcorelib-primitives/#transaction) +Notes: This transaction will be need to be signed [`account.sign(transaction)`](../account/sign.md) and then, if wanted, broadcasted to the network for execution `account.broadcastTransaction()`. + +Example : +```js +const recipients = [{recipient:"yereyozxENB9jbhqpbg1coE5c39ExqLSaG", satoshis:10e8},{recipient: "yMN2w8NiwcmY3zvJLeeBxpaExFV1aN23pg", satoshis: 1e8}]; +const change = "yaVrJ5dgELFkYwv6AydDyGPAJQ5kTJXyAN"; +const tx = account.createTransaction({recipients, change}); +``` + +**Strategy:** + +By default, wallet-lib is shipped with two different strategies : + +- **simpleDescendingStrategy** : Will maximize the use of big inputs to meet the amount required. + Allows the fee to be optimized for the smallest size at the cost of breaking big inputs. +- **simpleAscendingStrategy** : Will try to use as many small inputs as possible to meet the amount required. + Allows using many small inputs at the cost of a potentially bigger fee. + +You can also pass your own strategy (as a function) to allow you to create your own strategy for how you will want to spend the UTXO. + +```js +const recipient = "yereyozxENB9jbhqpbg1coE5c39ExqLSaG"; +const satoshis = 10e8; +const specialStrategy = (utxosList, outputsList, deductFee = false, feeCategory = 'normal')=> { +//... +}; +const txOpts1 = { + recipient, satoshis, + strategy: 'simpleAscendingStrategy', +}; +const txOpts2 = { + recipient, satoshis, + strategy: specialStrategy, +}; +const tx1 = account.createTransaction(txOpts1); +const tx2 = account.createTransaction(txOpts2); +``` + +See more information about [coinSelection](../usage/coinSelection.md). + +## Deduct Fee + +In order to broadcast a transaction, a minimal relay fee is required for a node to accept to broadcast the transaction. + +Such fee are used as a spam mechanism protection as a standard transaction would require slightly more than 0.0000012 Dash (varies per transaction and per node) as relay fee. + +The deduct fee property, when set at true allows to automatically estimate the size and deduct from outputs the corresponding amount. + +In case one user would want to not see that, he will be required to select an input to pay a fee by himself. + +Expected minimal relay fee for your transaction can be estimated this way : + +```js +const { storage, network } = account; +const { chains } = storage.getStore(); +const txOpts = { +deductFee: false, +} +const transaction = account.createTransaction(txOpts); + +const { minRelay: minRelayFeeRate } = chains[network.toString()].fees; + +const estimateKbSize = transaction._estimateSize() / 1000; +const minFeeToPay = estimateKbSize * minRelayFeeRate; +``` diff --git a/packages/wallet-lib/docs/account/decode.md b/packages/wallet-lib/docs/account/decode.md new file mode 100644 index 00000000000..23649d560f4 --- /dev/null +++ b/packages/wallet-lib/docs/account/decode.md @@ -0,0 +1,12 @@ +**Usage**: `account.decode(method, encodedValue)` +**Description**: Allow to decode an encoded value. +**Notes**: Method allowed right now limited to cbor (used by platform protocol). + +Parameters: + +| parameters | type | required | Description | +|-------------------|--------|----------------| -------------------------------------------------| +| **method** | String | yes | Enter a valid decoding method (one of: ['cbor']) | +| **encodedValue** | Buffer | yes | An encoded buffer value | + +Returns : decoded value (string). diff --git a/packages/wallet-lib/docs/account/decrypt.md b/packages/wallet-lib/docs/account/decrypt.md new file mode 100644 index 00000000000..73c0136be6c --- /dev/null +++ b/packages/wallet-lib/docs/account/decrypt.md @@ -0,0 +1,18 @@ +**Usage**: `account.decrypt(method, encryptedData, secret, encoding)` +**Description**: Allow to decrypt an encrypted message + +Parameters: + +| parameters | type | required | Description | +|-------------------|----------------|----------------| -----------------------------------------------------------| +| **method** | String | yes | Enter a valid decrypt method (one of: ['aes']) | +| **encryptedData** | String | yes | An encrypted value | +| **secret** | String | yes | The secret used for encrypting the data in first place | +| **encoding** | ['hex','utf8'] | no (def: utf8) | The secret used for encrypting the data in first place | + +Returns : decoded value (string). + +```js +const decrypted = account.decrypt('aes','U2FsdGVkX18+7ixRbZ7DzC8P8X/4ewNHSp2R6pZDmsI=', 'secret') +console.log(decrypted);// coucou +``` diff --git a/packages/wallet-lib/docs/account/disconnect.md b/packages/wallet-lib/docs/account/disconnect.md new file mode 100644 index 00000000000..ea8a0530ebc --- /dev/null +++ b/packages/wallet-lib/docs/account/disconnect.md @@ -0,0 +1,9 @@ +**Usage**: `account.disconnect()` +**Description**: This method will disconnect all plugins and other workers (Storage, SyncWorker). Useful to release all worker when doing integration testing with Wallet/Account + +Parameters: + +| parameters | type | required | Description | +|------------------------|-----------|----------------| ------------------------------------------------------------------------------- | + +Returns : void. diff --git a/packages/wallet-lib/docs/account/encode.md b/packages/wallet-lib/docs/account/encode.md new file mode 100644 index 00000000000..c22b860f7c2 --- /dev/null +++ b/packages/wallet-lib/docs/account/encode.md @@ -0,0 +1,28 @@ +**Usage**: `account.encode(method, data)` +**Description**: Allow to encode any raw data +**Notes**: Methods allowed right now limited to cbor (used by platform protocol). + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **method** | String | yes | Enter a valid encoding method (one of: ['cbor']) | +| **data** | Object/String | yes | A value to encode | + +Returns : encoded value (Buffer) + +Example : +```js +const jsonObject = { + string: 'string', + list: ['a', 'b', 'c', 'd'], + obj: { + int: 1, + boolean: true, + theNull: null, + }, + }; + +const encodedJSON = account.encode('cbor', jsonObject) +console.log(Buffer.from(encodedJSON).toString('hex')); +``` diff --git a/packages/wallet-lib/docs/account/encrypt.md b/packages/wallet-lib/docs/account/encrypt.md new file mode 100644 index 00000000000..9a233a5593f --- /dev/null +++ b/packages/wallet-lib/docs/account/encrypt.md @@ -0,0 +1,16 @@ +**Usage**: `account.encrypt(method, data, secret)` +**Description**: Allow to encrypt a value using a specific secret + +Parameters: + +| parameters | type | required | Description | +|-------------------|--------|----------------| -------------------------------------------------| +| **method** | String | yes | Enter a valid encryption method (one of: ['aes'])| +| **data** | String | yes | The value to encrypt | +| **secret** | String | yes | The secret used in order to encrypt the data | + +Returns : encrypted value (string). + +```js +const encrypted = account.encrypt('aes','coucou', 'secret'); +console.log(encrypted);// U2FsdGVkX18+7ixRbZ7DzC8P8X/4ewNHSp2R6pZDmsI= diff --git a/packages/wallet-lib/docs/account/fetchAddressInfo.md b/packages/wallet-lib/docs/account/fetchAddressInfo.md new file mode 100644 index 00000000000..7793da9d117 --- /dev/null +++ b/packages/wallet-lib/docs/account/fetchAddressInfo.md @@ -0,0 +1,14 @@ +**Usage**: `account.fetchAddressInfo(addressObj, fetchUtxo)` +**Description**: Fetch a specific address from the transport layer +**Notes**: This method will have breaking changes with SPV implementation. We encourage you with using `Storage` or `DAPI-Client`. + +Parameters: + +| parameters | type | required | Description | +|-------------------|--------|----------------| -----------------------------------------------------| +| **addressObj** | String | yes | Enter a valid encryption method (one of: ['aes']) | +| **fetchUtxo** | String | yes | The value to encrypt (default: true) | + +Returns : addrInfo (object representation of an address metadata) + +N.B: An AddressObject is an intern representation consisting of a `{path, address, index}` diff --git a/packages/wallet-lib/docs/account/fetchStatus.md b/packages/wallet-lib/docs/account/fetchStatus.md new file mode 100644 index 00000000000..dba6c02fe1e --- /dev/null +++ b/packages/wallet-lib/docs/account/fetchStatus.md @@ -0,0 +1,9 @@ +**Usage**: `account.fetchStatus()` +**Description**: Allow to retrieve status of the current chain (blockheight). + +Parameters: + +| parameters | type | required | Description | +|-------------------|--------|----------------| -----------------------------------------------------| + +Returns : status object diff --git a/packages/wallet-lib/docs/account/fetchTransactionInfo.md b/packages/wallet-lib/docs/account/fetchTransactionInfo.md new file mode 100644 index 00000000000..77a651ef46b --- /dev/null +++ b/packages/wallet-lib/docs/account/fetchTransactionInfo.md @@ -0,0 +1,11 @@ +**Usage**: `account.fetchTransactionInfo()` +**Description**: Fetch a specific transaction from the transport layer +**Notes**: This method will have breaking changes with SPV implementation. We encourage you with using `Storage` or `DAPI-Client`. + +Parameters: + +| parameters | type | required | Description | +|-------------------|--------|----------------| -----------------------------------------------------| +| **transactionId** | String | yes | identifier of the Transaction to retrieve | + +Returns : transaction object (metadata : `txid, blockhash, blockheight, blocktime, fees, size, vout, vin, txlock`). diff --git a/packages/wallet-lib/docs/account/forceRefreshAccount.md b/packages/wallet-lib/docs/account/forceRefreshAccount.md new file mode 100644 index 00000000000..fdd56067c92 --- /dev/null +++ b/packages/wallet-lib/docs/account/forceRefreshAccount.md @@ -0,0 +1,10 @@ +**Usage**: `account.forceRefreshAccount()` +**Description**: Force a refresh of all the addresses informations (utxo, balance, txs...) by invalidating previous. +**Important**: This is used for experience developer. If you have an issue or need using it, please do not hesitate to post an issue on GitHub. + +Parameters: + +| parameters | type | required | Description | +|-------------------|--------|----------------| -------------------------------------------------| + +Returns : boolean. diff --git a/packages/wallet-lib/docs/account/generateAddress.md b/packages/wallet-lib/docs/account/generateAddress.md new file mode 100644 index 00000000000..f885e858868 --- /dev/null +++ b/packages/wallet-lib/docs/account/generateAddress.md @@ -0,0 +1,11 @@ +**Usage**: `account.generateAddress(path)` +**Description**: Generate an address from a path and import it to the store +**Notes**: Usage of generate is discouraged, used `account.getAddress()` instead. + +Parameters: + +| parameters | type | required | Description | +|-------------------|--------|----------------| -----------------------------------------------------| +| **path** | String | yes | BIP44 path of the address to generate | + +Returns : address object (metadata : `path, index, address, transactions, balanceSat, unconfirmedBalanceSat, utxos, fetchedLast, used`). diff --git a/packages/wallet-lib/docs/account/getAddress.md b/packages/wallet-lib/docs/account/getAddress.md new file mode 100644 index 00000000000..a754ec56abb --- /dev/null +++ b/packages/wallet-lib/docs/account/getAddress.md @@ -0,0 +1,11 @@ +**Usage**: `account.getAddress(index, type)` +**Description**: Get a specific address based on the index and type of address. + +Parameters: + +| parameters | type | required | Description | +|-------------------|--------|----------------| --------------------------------------------------------------------------------------------| +| **index** | number | no | Index of the address (starting at 0) - default:0 | +| **type** | String | no | Type of the address, one of ['external','internal','misc']. - default: external | + +Returns : address object (metadata : `path, index, address, transactions, balanceSat, unconfirmedBalanceSat, utxos, fetchedLast, used`). diff --git a/packages/wallet-lib/docs/account/getConfirmedBalance.md b/packages/wallet-lib/docs/account/getConfirmedBalance.md new file mode 100644 index 00000000000..04b5cf025b0 --- /dev/null +++ b/packages/wallet-lib/docs/account/getConfirmedBalance.md @@ -0,0 +1,10 @@ +**Usage**: `account.getConfirmedBalance(displayDuffs)` +**Description**: This method will return the confirmed balance (included in a block) of an account + +Parameters: + +| parameters | type | required | Description | +|--------------------|-----------|---------------------| ------------------------------------------------------------------------------- | +| **displayDuffs** | boolean | no (default: true) | When set at true return in Duffs (satoshis), if false, returns in Dash | + +Return : number diff --git a/packages/wallet-lib/docs/account/getPrivateKeys.md b/packages/wallet-lib/docs/account/getPrivateKeys.md new file mode 100644 index 00000000000..9b10c76b5b7 --- /dev/null +++ b/packages/wallet-lib/docs/account/getPrivateKeys.md @@ -0,0 +1,10 @@ +**Usage**: `account.getPrivateKeys(addressList)` +**Description**: This method return the private keys list matching the passed address list of public keys + +Parameters: + +| parameters | type | required | Description | +|---------------------|-----------------|----------------| ------------------------------------------------------------------------------- | +| **addressList** | Array[String ]| yes | The list of public address to match private key | + +Returns : Array[PrivateKey] diff --git a/packages/wallet-lib/docs/account/getTotalBalance.md b/packages/wallet-lib/docs/account/getTotalBalance.md new file mode 100644 index 00000000000..059d9ae38d2 --- /dev/null +++ b/packages/wallet-lib/docs/account/getTotalBalance.md @@ -0,0 +1,10 @@ +**Usage**: `account.getTotalBalance(displayDuffs)` +**Description**: This method will return the total (unconfirmed + confirmed) balance of an account + +Parameters: + +| parameters | type | required | Description | +|--------------------|-----------|---------------------| ------------------------------------------------------------------------------- | +| **displayDuffs** | boolean | no (default: true) | When set at true return in Duffs (satoshis), if false, returns in Dash | + +Return : number diff --git a/packages/wallet-lib/docs/account/getTransaction.md b/packages/wallet-lib/docs/account/getTransaction.md new file mode 100644 index 00000000000..0a07eaab27a --- /dev/null +++ b/packages/wallet-lib/docs/account/getTransaction.md @@ -0,0 +1,26 @@ +**Usage**: `account.getTransaction(txid)` +**Description**: This method will return the transaction of a specific id + +Parameters: + +| parameters | type | required | Description | +|-------------|-----------|----------------| ------------------------------------------------------------------------------- | +| **txid** | string | yes | TxId of the transaction to fetch. | + +Return : transaction with metadata + +```js +account.getTransaction('1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6'); +/* + returns : + { + transaction: Transaction, + metadata: { + blockHash: '0000007a84abfe1d2b4201f4844bb1e59f24daf965c928281589269f281abc01', + height: 551438, + instantLocked: true, + chainLocked: true + } + } + */ +``` diff --git a/packages/wallet-lib/docs/account/getTransactionHistory.md b/packages/wallet-lib/docs/account/getTransactionHistory.md new file mode 100644 index 00000000000..2f4ebff80b6 --- /dev/null +++ b/packages/wallet-lib/docs/account/getTransactionHistory.md @@ -0,0 +1,43 @@ +**Usage**: `account.getTransactionHistory()` +**Description**: Allow to get the transaction history of an account + +Parameters: + +| parameters | type | required | Description | +|-------------------|--------|----------------| -------------------------------------------------| + +Returns : sorted and classified transaction history + +```js +const transactionHistory = await account.getTransactionHistory(); +``` + +Results in + +```js +[{ + from: [ { address: 'yNCqctyQaq51WU1hN5aNwsgMsZ5fRiB7GY', addressType: 'external' } ], + to: [ + { + address: 'yiXh4Yo5djG6QH8WzXkKm5EFzqLRJWakXz', + satoshis: 1150000000, + addressType: 'otherAccount' + }, + { + address: 'yh6Hcyipdvp6WJpQxjNbaXP4kzPQUJpY3n', + satoshis: 49999753, + addressType: 'internal' + } + ], + type: 'account_transfer', + time: Date('2021-08-17T21:35:58.000Z'), + txId: '6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135', + blockHash: '000000444b3f2f02085f8befe72da5442c865c290658766cf935e1a71a4f4ba7', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: -1150000000, + feeImpact: 247 +}] +``` + +Where `addressType=external|internal|otherAccount|unknown` diff --git a/packages/wallet-lib/docs/account/getTransactions.md b/packages/wallet-lib/docs/account/getTransactions.md new file mode 100644 index 00000000000..59325cb755b --- /dev/null +++ b/packages/wallet-lib/docs/account/getTransactions.md @@ -0,0 +1,9 @@ +**Usage**: `account.getTransactions()` +**Description**: This method will return the transactions for this account + +Parameters: + +| parameters | type | required | Description | +| ---------- | ---- | -------- | ----------- | + +Returns : Array[transaction]. diff --git a/packages/wallet-lib/docs/account/getUTXOS.md b/packages/wallet-lib/docs/account/getUTXOS.md new file mode 100644 index 00000000000..ee2d860f58b --- /dev/null +++ b/packages/wallet-lib/docs/account/getUTXOS.md @@ -0,0 +1,10 @@ +**Usage**: `account.getUTXOS(options)` +**Description**: This method will return the list of all available UTXOS for this account. + +Parameters: + +| parameters | type | required | Description | +|----------------------|-----------|----------------| ----------------------------------------------------------------------------------------| +| **options.coinbaseMaturity** | Number | no (def: 100) | Allow to override coinbase maturity | + +Returns : Array[utxos]. diff --git a/packages/wallet-lib/docs/account/getUnconfirmedBalance.md b/packages/wallet-lib/docs/account/getUnconfirmedBalance.md new file mode 100644 index 00000000000..565ee72b0ba --- /dev/null +++ b/packages/wallet-lib/docs/account/getUnconfirmedBalance.md @@ -0,0 +1,10 @@ +**Usage**: `account.getUnconfirmedBalance(displayDuffs)` +**Description**: This method will return the unconfirmed balance (missing inclusion in a block) of an account + +Parameters: + +| parameters | type | required | Description | +|--------------------|-----------|---------------------| ------------------------------------------------------------------------------- | +| **displayDuffs** | boolean | no (default: true) | When set at true return in Duffs (satoshis), if false, returns in Dash | + +Return : number diff --git a/packages/wallet-lib/docs/account/sign.md b/packages/wallet-lib/docs/account/sign.md new file mode 100644 index 00000000000..fba6ee91c54 --- /dev/null +++ b/packages/wallet-lib/docs/account/sign.md @@ -0,0 +1,32 @@ +**Usage**: `account.sign(transaction, privateKeys, sigType)` +**Description**: Allow to sign a transaction with private keys +**Notes**: A Signable Object is of type : Transaction or Message (exported by DashJS). + +Parameters: + +| parameters | type | required | Description | +|-------------------|-------------|----------------| -------------------------------------------------| +| **object** | Signable | yes | Enter a valid encryption method (one of: ['aes'])| +| **privateKeys** | PrivateKey | yes | The private keys used to sign | +| **sigtype** | String | no | Default: crypto.Signature.SIGHASH_ALL | + +Returns : Signed Signable Object. + +## Examples + +### Signing a transaction +```js +const tx = account.createTransaction(); +const signedTx = account.sign(tx); // Will find the privateKey from keychain for you. +``` + +### Signing a message +```js +const {Message} = require('dash'); +const message = new Message('hello, world'); + +const idPrivateKey = account.getIdentityHDKeyByIndex(0, 0).privateKey; + +const signed = account.sign(message, idPrivateKey); +const verify = message.verify(idPrivateKey.toAddress().toString(), signed.toString()); // true +``` diff --git a/packages/wallet-lib/docs/develop/logging.md b/packages/wallet-lib/docs/develop/logging.md new file mode 100644 index 00000000000..3bb1eb88aa9 --- /dev/null +++ b/packages/wallet-lib/docs/develop/logging.md @@ -0,0 +1,21 @@ +# Logging + +Wallet-lib will log multiple events happening which might help you to debug, or get a better understanding at what is happening internally. + +## Log levels + +These are the different levels used internally sorted from the least verbose to the most. + +- `error` - Warn about issues that are near critical (but are not straight thrown errors). +- `warn` - Warn about issues that aren't so critical. +- `info` - Log level used by default. Inform about some internal high value steps. +- `debug` - Inform about basic steps (plugin initialisations, ...) +- `silly` - Inform about everything going on (each transporter call, storage and worker execution,...) + +## Set a log level + +In order to control the granularity of the logger, simply put the environment variable `LOG_LEVEL` at the desired level. + +- Windows: `set LOG_LEVEL=silly node index.js` +- MacOS: `LOG_LEVEL=silly node index.js` +- Linux: `export LOG_LEVEL=silly node index.js` diff --git a/packages/wallet-lib/docs/develop/persistence.md b/packages/wallet-lib/docs/develop/persistence.md new file mode 100644 index 00000000000..a1c381f4fde --- /dev/null +++ b/packages/wallet-lib/docs/develop/persistence.md @@ -0,0 +1,23 @@ +# Persistence + +Wallet-lib allows the use of a persistence adapter in order to store information fetched via the transporter in order to provide faster loading on later uses. + +This adapter can be useful for multiple cases, for instance: + +- A degraded connectivity: Having stored information on a persistence layer (localStorage, secureStorage,...) would allow a user to still be able to consult his transaction history, UTXO set, balance, prepare and sign a transaction (intended to be broadcasted later, when connectivity is back). +- Offline Mode: In some conditions, using the wallet-lib on a non-connected device might be a desired feature. The persistence adapter would allow such usage be still providing most of it's feature from it's cache, and therefore, in the example of a transaction signing, the signing would be done on the offline device, while the broadcast would happen on another, connected device. + +When no persistence is set, Wallet-lib will use by default, an In Memory adapter, which won't persist information except in local RAM. +A message will warn you about this on starting up, and won't be displayed with a properly set adapter. + +## Create your own persistence adapter + +By just providing a class or instance of a class containing a certain minimal set of methods, one can provide an adapter for various databases, remote services or file storage. + +- `config(props)` - async / optional - When provided, before any execution, this method would be called passing with the following property `name: 'dashevo-wallet-lib'`. + +This method intends to allow the preparation of your persistence layer to be ready for further uses (for instance, in a case where your adapter is a database, this would allow to set indexes, and prepare the connection pool). + +- `setItem(key, item)` - async / mandatory - This is the method that will be used to set any item to the persistence layer. + +- `getItem(key)` - async / mandatory - This will be called in order to retrieve any item from the persistence layer. diff --git a/packages/wallet-lib/docs/events/blockheight_changed.md b/packages/wallet-lib/docs/events/blockheight_changed.md new file mode 100644 index 00000000000..521cc4dae85 --- /dev/null +++ b/packages/wallet-lib/docs/events/blockheight_changed.md @@ -0,0 +1,12 @@ +**Usage**: `account.events.on('blockheight_changed', fn)` +**Description**: An event is thrown each time Wallet-lib is being made aware of a new block validated by the protocol. + +Example: +```js +const {EVENTS} = require('@dashevo/wallet-lib'); + +account.events.on(EVENTS.BLOCKHEIGHT_CHANGED, ({payload: blockHeight})=>{ + console.log(`Blockheight changed to ${blockHeight}`); +}); +``` + diff --git a/packages/wallet-lib/docs/events/confirmed_balance_changed.md b/packages/wallet-lib/docs/events/confirmed_balance_changed.md new file mode 100644 index 00000000000..ee486e8b3d4 --- /dev/null +++ b/packages/wallet-lib/docs/events/confirmed_balance_changed.md @@ -0,0 +1,13 @@ +**Usage**: `account.events.on('confirmed_balance_changed', fn)` +**Description**: Wallet-lib, when finished to perform it's internal tasks (blockheight, SPV, utxos sync...), will throw this event. +**Important**: Standardization on event might happen soon, to avoid breaking change, use the EVENTS constant as described below. + +Example: +```js +const {EVENTS} = require('@dashevo/wallet-lib'); +const onConfirmedBalanceChange = ()=>{ + console.log('Balance changed'); +} +account.events.on(EVENTS.CONFIRMED_BALANCE_CHANGED, onConfirmedBalanceChange); +``` + diff --git a/packages/wallet-lib/docs/events/fetched_confirmed_transaction.md b/packages/wallet-lib/docs/events/fetched_confirmed_transaction.md new file mode 100644 index 00000000000..edf7054f88d --- /dev/null +++ b/packages/wallet-lib/docs/events/fetched_confirmed_transaction.md @@ -0,0 +1,13 @@ +**Usage**: `account.events.on('FETCHED/CONFIRMED_TRANSACTION', fn)` +**Description**: Every time a new confirmed transaction is fetched from the network, an event in thrown. + +Returns : {Transaction} + +Example: +```js +const {EVENTS} = require('@dashevo/wallet-lib'); +const onNewConfirmedTx = (tx)=>{ + console.log('Confirmed tx', tx); +} +account.events.on(EVENTS.FETCHED_CONFIRMED_TRANSACTION, onNewConfirmedTx); +``` diff --git a/packages/wallet-lib/docs/events/fetched_unconfirmed_transaction.md b/packages/wallet-lib/docs/events/fetched_unconfirmed_transaction.md new file mode 100644 index 00000000000..23753ece0a1 --- /dev/null +++ b/packages/wallet-lib/docs/events/fetched_unconfirmed_transaction.md @@ -0,0 +1,14 @@ +**Usage**: `account.events.on('FETCHED/UNCONFIRMED_TRANSACTION', fn)` +**Description**: Every time a new unconfirmed transaction is fetched from the network, an event in thrown. + +Returns : {Transaction} + +Example: +```js +const {EVENTS} = require('@dashevo/wallet-lib'); +const onNewUnconfirmedTx = (tx)=>{ + console.log('Unconfirmed tx', tx); +} +account.events.on(EVENTS.FETCHED_UNCONFIRMED_TRANSACTION, onNewUnconfirmedTx); +``` + diff --git a/packages/wallet-lib/docs/events/ready.md b/packages/wallet-lib/docs/events/ready.md new file mode 100644 index 00000000000..49705a32532 --- /dev/null +++ b/packages/wallet-lib/docs/events/ready.md @@ -0,0 +1,12 @@ +**Usage**: `account.events.on('ready', fn)` +**Description**: Wallet-lib, when finished to perform it's internal tasks (blockheight, SPV, utxos sync...), will throw this event. + +Example: +```js +const {EVENTS} = require('@dashevo/wallet-lib'); +const onReady = ()=>{ + console.log('Wallet-lib is ready to perform action'); +} +account.events.on(EVENTS.READY, onReady); +``` + diff --git a/packages/wallet-lib/docs/events/unconfirmed_balance_changed.md b/packages/wallet-lib/docs/events/unconfirmed_balance_changed.md new file mode 100644 index 00000000000..70fe2bbc83d --- /dev/null +++ b/packages/wallet-lib/docs/events/unconfirmed_balance_changed.md @@ -0,0 +1,13 @@ +**Usage**: `account.events.on('unconfirmed_balance_changed', fn)` +**Description**: When not offline, the wallet will keep track of new transaction incoming or outgoing, these cause balance modification that this method warn about +**Important**: Standardization on event might happen soon, to avoid breaking change, use the EVENTS constant as described below. + +Example: +```js +const {EVENTS} = require('@dashevo/wallet-lib'); +const onUnconfirmedBalanceChange = ()=>{ + console.log('Unconfirmed Balance changed'); +} +account.events.on(EVENTS.UNCONFIRMED_BALANCE_CHANGED, onUnconfirmedBalanceChange); +``` + diff --git a/packages/wallet-lib/docs/getting-started/core-concepts.md b/packages/wallet-lib/docs/getting-started/core-concepts.md new file mode 100644 index 00000000000..ba133d38150 --- /dev/null +++ b/packages/wallet-lib/docs/getting-started/core-concepts.md @@ -0,0 +1,28 @@ +# Core concepts + +The [Dash Core Developer Guide](https://dashcore.readme.io/docs/core-guide-introduction) will answer most of the questions about the fundamentals of Dash. + +However, some elements provided by the SDK need to be grasped, so we will quickly cover some of those. + +## Wallet + +At the core of Dash is the Payment Chain, in order to be able to transact on it, one needs to have a set of [UTXO](https://dashcore.readme.io/docs/core-guide-block-chain-transaction-data) that is controlled by a Wallet instance. + +In order to access your UTXO, you will have to provide a valid mnemonic that will unlock the Wallet and automatically fetch the associated UTXOs. + +## Wallet accounts + +Since the introduction of [deterministic wallet](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki), a Wallet is actually composed of multiple account. + +For manipulating multiple accounts, `Wallet.getAccount()` takes optional [options](../wallet/getAccount.md) where index allows either accessing or creating a specific account index. + +## Instantiation types + +A Wallet instance can be created from multiples types, which impact how much the Wallet can do. +In general, we expect you to initialize from a `mnemonic` or an `seed` (HD seed) or an `HDPrivateKey`, which allows wallet-lib to deal with HD Wallet (deterministic wallet). + +In some other cases, you might want to instantiate Wallet from another input such as : +- `privateKey`: This allows managing a single privateKey/publicKey set. Therefore, you will only have a single unique address to receive money. +- `HDPublicKey`: This allows a "watch-only" mode. You won't be able to spend anything, but this will allow you to track and monitor in real-time the address set of this public key. This allows you to derive unique unused addresses from a single key (shared by another user, third-party merchant). + +[Learn more about instantiation documentation](../wallet/Wallet.md) diff --git a/packages/wallet-lib/docs/getting-started/quickstart.md b/packages/wallet-lib/docs/getting-started/quickstart.md new file mode 100644 index 00000000000..24606982f9b --- /dev/null +++ b/packages/wallet-lib/docs/getting-started/quickstart.md @@ -0,0 +1,81 @@ +# Quick start + +## ES5/ES6 via NPM + +In order to use this library in Node, you will need to add it to your project as a dependency. + +Having [NodeJS](https://nodejs.org/) installed, just type in your terminal : + +```sh +npm install @dashevo/wallet-lib +``` + +## CDN Standalone + +For browser usage, you can also directly rely on unpkg for wallet-lib, and [localForage](https://github.com/localForage/localForage) as adapter for persistence. + +``` + + + + +``` + +## Initialization + +Let's load our Wallet by creating a new Wallet instance specifying our mnemonic. + +```js +const { Wallet } = require('@dashevo/wallet-lib'); + +const opts = { + network: 'testnet', + mnemonic: "arena light cheap control apple buffalo indicate rare motor valid accident isolate", +}; +const wallet = new Wallet(opts); +wallet.getAccount().then((account) => { + // At this point, account has fetched all UTXOs if they exist + const balance = account.getTotalBalance(); + console.log(`Balance: ${balance}`); + + // We easily can get a new address to fund + const { address } = account.getUnusedAddress(); +}); +``` + +In above code, we did not specify any `transport` instance, as by default, wallet-lib is using DAPI as a transporter; The `adapter` not being set, we will use by default an in-memory (without persistence) adapter. +One can set any adapter that contains a valid adapter syntax (getItem, setItem), such as [localForage](https://www.npmjs.com/package/localforage), you can learn more about [creating your own persistence adapter](develop/persistence.md). + +Quick note : + +- If no mnemonic is provided (nor any privatekey, HDPubKey,...), or if mnemonic is `null`, a mnemonic will be created for you automatically. +- **By default, if not provided, network value will be `evonet`**. +- If no adapter specified, Wallet-lib will use an in-memory store (and warn you about it). +- If no transport specified, Wallet-lib will connect to DAPI. +- `wallet.getAccount()` is by default equivalent to `wallet.getAccount({ index:0 })`, where 0 correspond of the account index as per [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki). + +## Make a payment to an address + +```js +const options = { + recipient:'yLptqWxjgTxtwKJuLHoGY222NnoeqYuN8h', + satoshis:100000 +}; +const transaction = account.createTransaction(options) +``` + +## Broadcast the transaction + +```js +const txid = await account.broadcastTransaction(transaction); +``` + +## Some rules of thumb + +- There are multiple event listeners (socket sync,...), running intervals (service worker,...), +therefore a good way to quit an instance would be to call `account.disconnect()` which will care to +call `clearWorker(), closeSocket()` of the different elements. You can still decide to remove them by hand if you want. +- Some classic examples of usage can be seen here : [Examples](../usage/examples.md) diff --git a/packages/wallet-lib/docs/identities/Identities.md b/packages/wallet-lib/docs/identities/Identities.md new file mode 100644 index 00000000000..0c0edad679c --- /dev/null +++ b/packages/wallet-lib/docs/identities/Identities.md @@ -0,0 +1,19 @@ +**Usage**: `new Identities(wallet)` +**Description**: This method creates a new Identities instance associated to the given wallet. + +Parameters: + +| parameters | type | required | Description | +|-------------------------------------------|-----------------|--------------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **wallet** | Wallet | yes | A valid [wallet](../wallet/Wallet.md) instance | + +Returns : Identities instance. + +Examples (assuming a Wallet instance created) : + +```js +const { Identities, Wallet } = require('@dashevo/wallet-lib'); +const wallet = new Wallet(); +const identities = new Identities(wallet); +identities.getIdentityHDKeyByIndex(0, 0); +``` diff --git a/packages/wallet-lib/docs/identities/getIdentityHDKeyByIndex.md b/packages/wallet-lib/docs/identities/getIdentityHDKeyByIndex.md new file mode 100644 index 00000000000..ff4b91a451d --- /dev/null +++ b/packages/wallet-lib/docs/identities/getIdentityHDKeyByIndex.md @@ -0,0 +1,11 @@ +**Usage**: `identities.getIdentityHDKeyByIndex(identityIndex, keyIndex)` +**Description**: This method returns the identity HDKey of `identityIndex` for the specified `keyIndex` + +Parameters: + +| parameters | type | required | Description | +|---------------------|-----------|----------------| --------------------------------------------------------------------------------| +| **identityIndex** | number | yes | To derive the key for a specific identityIndex (default: 0) | +| **keyIndex** | number | yes | To derive the key for a specific keyIndex (default: 0) | + +Returns : HDKeys (private, public). diff --git a/packages/wallet-lib/docs/index.html b/packages/wallet-lib/docs/index.html new file mode 100644 index 00000000000..564bb899c7a --- /dev/null +++ b/packages/wallet-lib/docs/index.html @@ -0,0 +1,41 @@ + + + + + Wallet-lib - A pure and extensible JavaScript Wallet Library for Dash + + + + + + + +
+ + + + + + diff --git a/packages/wallet-lib/docs/keychain/KeyChain.md b/packages/wallet-lib/docs/keychain/KeyChain.md new file mode 100644 index 00000000000..9c883869fbe --- /dev/null +++ b/packages/wallet-lib/docs/keychain/KeyChain.md @@ -0,0 +1,18 @@ +**Usage**: `new KeyChain(opts)` +**Description**: This method create a new KeyChain. Which handle handle the derivation and handling of the HDRootKey (when init from an HDPrivKey). + +While both the seed and the mnemonic would allow to generate other coins private keys, a HDRootKey is specific to a coin, which is why it's the value used in store.. + +Parameters: + +| parameters | type | required | Description | +|------------------------------------|-----------------|----------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **opts.network** | Network|String | no (testnet) | The network to use for the KeyChain address derivation | +| **opts.type** | string | yes | The type of the KeyChain (HDPrivateKey, HDPublicKey or privateKey) | +| **opts.HDPrivateKey** | object | yes (if type) | If type is HDPrivateKey, the root HDPrivateKey to allow KeyChain to generate new address | +| **opts.HDPublicKey** | object | yes (if type) | If type is HDPublicKey, the root HDPublicKey to allow KeyChain to generate new public address | +| **opts.privateKey** | object | yes (if type) | If type is a PrivateKey, the PrivKey to allow KeyChain to manage public address | +| **opts.keys** | object | no | If required, allow to create KeyChain by passing it a set of keys | + +Returns : Keychain instance. + diff --git a/packages/wallet-lib/docs/keychain/generateKeyForChild.md b/packages/wallet-lib/docs/keychain/generateKeyForChild.md new file mode 100644 index 00000000000..b09d9539f59 --- /dev/null +++ b/packages/wallet-lib/docs/keychain/generateKeyForChild.md @@ -0,0 +1,20 @@ +**Usage**: `keychain.generateKeyForChild(index,type)` +**Description**: Generate key for a specific index without storing the result in keychain. +**Important**: For cache/perf reason, this method is discouraged in favor of getKeyForPath. + +Parameters: + +| parameters | type | required | Description | +|-------------------|-------------|---------------------------| -------------------------------------------------| +| **index** | number | yes | Enter a valid index to derivate to | +| **type** | string | no (default:HDPrivateKey) | Enter a valid type (one of: ['HDPrivateKey','HDPublicKey']) | + +Returns : {HDPrivateKey|HDPublicKey} + +Example: +```js +const { privateKey } = keychain.generateKeyForChild(0); +``` + + +VERIFY THAT IT ACTUALLY WORKS AS EXPECTED LOL. diff --git a/packages/wallet-lib/docs/keychain/generateKeyForPath.md b/packages/wallet-lib/docs/keychain/generateKeyForPath.md new file mode 100644 index 00000000000..9836de1f85c --- /dev/null +++ b/packages/wallet-lib/docs/keychain/generateKeyForPath.md @@ -0,0 +1,19 @@ +**Usage**: `keychain.generateKeyForPath(path, type)` +**Description**: Generate key for a specific path without storing the result in keychain. +**Important**: For cache/perf reason, this method is discouraged in favor of getKeyForPath. + +Parameters: + +| parameters | type | required | Description | +|-------------------|-------------|---------------------------| ------------------------------------------------------------| +| **path** | string | yes | Enter a valid path | +| **type** | string | no (default:HDPrivateKey) | Enter a valid type (one of: ['HDPrivateKey','HDPublicKey']) | + +Returns : {HDPrivateKey|HDPublicKey} + +Example: +```js +const { privateKey } = keychain.generateKeyForPath(0); +``` + +ADD TESTS! PLUS PATH diff --git a/packages/wallet-lib/docs/keychain/getDIP15ExtendedKey.md b/packages/wallet-lib/docs/keychain/getDIP15ExtendedKey.md new file mode 100644 index 00000000000..2b999577f32 --- /dev/null +++ b/packages/wallet-lib/docs/keychain/getDIP15ExtendedKey.md @@ -0,0 +1,26 @@ +**Usage**: `keychain.getDIP15ExtendedKey(userUniqueId, contactUniqueId, index, accountIndex = 0, type = 'HDPrivateKey')` + +**Description**: Return a DIP15 Extended Key of a 2-contacts relationship + +Parameters: + +| parameters | type | required | Description | +|---------------------|-------------|---------------------------| ---------------------------------------------------------------| +| **userUniqueId** | string | yes | Current DashPay unique UserID | +| **contactUniqueId** | string | yes | Contact DashPay unique UserID | +| **index** | number | no(=0) | the key index to derivate to | +| **accountIndex** | number | no(=0) | the wallet account index from which to derivate | +| **type** | string | no (default:HDPrivateKey) | type of returned keys. one of: ['HDPrivateKey','HDPublicKey']. | + +Returns : {HDPrivateKey|HDPublicKey} (of path: `m/9'/5'/15'/accountIndex'/userId'/contactID'/index` on mainnet or `m/9'/1'/15'/...` on testnet) + +Example: +```js +// m/9'/5'/15'/0'/0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'/0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'/0 + +const userUniqueId = '0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'; +const contactUniqueId = '0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'; + +const DIP15ExtPrivKey_0 = keychain2.getDIP15ExtendedKey(userUniqueId, contactUniqueId, 0, 0, 'HDPrivateKey'); +const { privateKey } = DIP15ExtPrivKey_0; //fac40790776d171ee1db90899b5eb2df2f7d2aaf35ad56f07ffb8ed2c57f8e60 +``` diff --git a/packages/wallet-lib/docs/keychain/getHardenedBIP44HDKey.md b/packages/wallet-lib/docs/keychain/getHardenedBIP44HDKey.md new file mode 100644 index 00000000000..764b7a20751 --- /dev/null +++ b/packages/wallet-lib/docs/keychain/getHardenedBIP44HDKey.md @@ -0,0 +1,16 @@ +**Usage**: `keychain.getHardenedBIP44HDKey(type)` +**Description**: Return a safier root key to derivate from + +Parameters: + +| parameters | type | required | Description | +|-------------------|-------------|---------------------------| -------------------------------------------------| +| **type** | string | no (default:HDPrivateKey) | Enter a valid type (one of: ['HDPrivateKey','HDPublicKey']) | + +Returns : HDPrivateKey + +Example: +```js +const hdPrivateKey = keychain.getHardenedBIP44HDKey(); +const { privateKey } = hdPrivateKey +``` diff --git a/packages/wallet-lib/docs/keychain/getHardenedDIP15AccountKey.md b/packages/wallet-lib/docs/keychain/getHardenedDIP15AccountKey.md new file mode 100644 index 00000000000..41ba855d091 --- /dev/null +++ b/packages/wallet-lib/docs/keychain/getHardenedDIP15AccountKey.md @@ -0,0 +1,19 @@ +**Usage**: `keychain.getHardenedDIP15AccountKey(accountIndex = 0, type = 'HDPrivateKey')` +**Description**: Return a safier root path to derivate from + +Parameters: + +| parameters | type | required | Description | +|-------------------|-------------|---------------------------| ------------------------------------------------------------| +| **accountIndex** | number | no (default:0) | set the account index | +| **type** | string | no (default:HDPrivateKey) | Enter a valid type (one of: ['HDPrivateKey','HDPublicKey']) | + +Returns : HDPrivateKey (of path: `m/9'/1'/15'/accountIndex'` on testnet or `m/9'/5'/15'/accountIndex'` on livenet) + +Example: +```js + +const hdPrivateKey = keychain.getHardenedDIP15AccountKey(); +const { privateKey } = hdPrivateKey; + +``` diff --git a/packages/wallet-lib/docs/keychain/getHardenedDIP9FeatureHDKey.md b/packages/wallet-lib/docs/keychain/getHardenedDIP9FeatureHDKey.md new file mode 100644 index 00000000000..5e0e845f44e --- /dev/null +++ b/packages/wallet-lib/docs/keychain/getHardenedDIP9FeatureHDKey.md @@ -0,0 +1,18 @@ +**Usage**: `keychain.getHardenedDIP9FeatureHDKey(type)` +**Description**: Return a safier root key to derivate from + +Parameters: + +| parameters | type | required | Description | +|-------------------|-------------|---------------------------| -------------------------------------------------| +| **type** | string | no (default:HDPrivateKey) | Enter a valid type (one of: ['HDPrivateKey','HDPublicKey']) | + +Returns : HDPrivateKey (of path: `m/9'/1'` on testnet or `m/9'/5'` on livenet) + +Example: +```js + +const hdPrivateKey = keychain.getHardenedDIP9FeatureHDKey(); +const { privateKey } = hdPrivateKey; + +``` diff --git a/packages/wallet-lib/docs/keychain/getKeyForChild.md b/packages/wallet-lib/docs/keychain/getKeyForChild.md new file mode 100644 index 00000000000..42101cae30a --- /dev/null +++ b/packages/wallet-lib/docs/keychain/getKeyForChild.md @@ -0,0 +1,16 @@ +**Usage**: `keychain.getKeyForChild(index,type)` +**Description**: Use to derivate the root key to a specific child. (useful when Wallet is initialized from a HDPublicKey) + +Parameters: + +| parameters | type | required | Description | +|-------------------|-------------|---------------------------| -------------------------------------------------| +| **index** | number | yes | Enter a valid index | +| **type** | string | no (default:HDPublicKey) | Enter a valid type (one of: ['HDPrivateKey','HDPublicKey']) | + +Returns : HDPrivateKey + +Example: +```js +const { privateKey } = keychain.getKeyForChild(0); +``` diff --git a/packages/wallet-lib/docs/keychain/getKeyForPath.md b/packages/wallet-lib/docs/keychain/getKeyForPath.md new file mode 100644 index 00000000000..5d26ae04bc9 --- /dev/null +++ b/packages/wallet-lib/docs/keychain/getKeyForPath.md @@ -0,0 +1,16 @@ +**Usage**: `keychain.getKeyForPath(path,type)` +**Description**: Get a key from the keychain cache or generate if not yet existing + +Parameters: + +| parameters | type | required | Description | +|-------------------|-------------|---------------------------| -------------------------------------------------| +| **path** | string | yes | Enter a valid derivation path | +| **type** | string | no (default:HDPrivateKey) | Enter a valid type (one of: ['HDPrivateKey','HDPublicKey']) | + +Returns : HDPrivateKey + +Example: +```js +const { privateKey } = keychain.getKeyForPath(`m/44'/1'/0'/0'/0`); +``` diff --git a/packages/wallet-lib/docs/keychain/getPrivateKey.md b/packages/wallet-lib/docs/keychain/getPrivateKey.md new file mode 100644 index 00000000000..af3e300389f --- /dev/null +++ b/packages/wallet-lib/docs/keychain/getPrivateKey.md @@ -0,0 +1,9 @@ +**Usage**: `keychain.getPrivateKey()` +**Description**: Return the privateKey of the HDPrivateKey of a KeyChain initialized from a mnemonic (alternatively, if Keychain is init from a pk, it just returns it.) + +Parameters: + +| parameters | type | required | Description | +|-------------------|-------------|----------------| -------------------------------------------------| + +Returns : PrivateKey diff --git a/packages/wallet-lib/docs/keychain/sign.md b/packages/wallet-lib/docs/keychain/sign.md new file mode 100644 index 00000000000..d766d347ffe --- /dev/null +++ b/packages/wallet-lib/docs/keychain/sign.md @@ -0,0 +1,12 @@ +**Usage**: `keychain.sign(transaction, privateKeys, sigType)` +**Description**: Allow to sign a transaction with private keys + +Parameters: + +| parameters | type | required | Description | +|-------------------|-------------|----------------| -------------------------------------------------| +| **object** | Transaction | yes | Enter a valid encryption method (one of: ['aes']) | +| **privateKeys** | PrivateKey | yes | The private keys used to sign | +| **sigtype** | String | no | Default: crypto.Signature.SIGHASH_ALL | + +Returns : Signed Transaction. diff --git a/packages/wallet-lib/docs/plugins/community-plugins.md b/packages/wallet-lib/docs/plugins/community-plugins.md new file mode 100644 index 00000000000..4431a38b955 --- /dev/null +++ b/packages/wallet-lib/docs/plugins/community-plugins.md @@ -0,0 +1,5 @@ +# Community plugins + +## Submit your own plugin + +You may propose your own plugin by submitting a P.R to this file with the GitHub or NPM repository for your plugin with a small description. diff --git a/packages/wallet-lib/docs/plugins/using-a-plugin.md b/packages/wallet-lib/docs/plugins/using-a-plugin.md new file mode 100644 index 00000000000..2387120f42e --- /dev/null +++ b/packages/wallet-lib/docs/plugins/using-a-plugin.md @@ -0,0 +1,47 @@ +# Using a plugin + +## About plugins + +In order to add features and logic to the Wallet-library and be able to share independant module and request them together. +Wallet-lib can be passed some plugins at his instantiation. +Plugins are particular shaped class that can perform action on your wallet. + +By default, three plugins are injected : BIP44Worker, SyncWorker and ChainWorker. + +They handle respectively with maintaining your address pool, getting you in sync with the blockchain and maintaining some knowledge about the chain (blockheight). +You can disable them by adding `injectDefaultPlugins:false` at the initialization parameter of your wallet object. + +For more granularity, you could do it as a parameter of `getAccount(accOpts)`. + +## Type of plugins + +There are three different types of plugins that can be used in the wallet-library: + +- Workers : A worker plugins is a plugin that inherits from Worker class. It distinguish itself by having a execute method that will be executed each `workerIntervalTime`. +- Standard : These are mostly enhancers of the wallet library functionalities. + +## Dependencies + +In order for a plugin to have the ability to access wallet data, you have to add a dependency in the constructor. + +``` +class MyPlugin extends StandardPlugin { + constructor(){ + this.dependencies = ['walletId'] + } + doStruff(){ + return this.walletId.substr(0); + } +} +``` + +This will allow to access the walletId property; the same thing is doable with the account function. + +## Accessing a plugin + + +```js +wallet.getAccount({index:0}).then((account)=>{ + const plugin = account.getPlugin('pluginName'); + }); +``` diff --git a/packages/wallet-lib/docs/plugins/wallet-workers.md b/packages/wallet-lib/docs/plugins/wallet-workers.md new file mode 100644 index 00000000000..3711fba2a30 --- /dev/null +++ b/packages/wallet-lib/docs/plugins/wallet-workers.md @@ -0,0 +1,47 @@ +# Wallet workers + +In order to perform it's duty of being in-sync with the network and to always keep a pre-generated set of unused addresses, wallet-lib uses internally two workers : +- Sync Worker : Used to keep in sync with the network (utxo, received transactions,...) +- Chain Worker : Used to keep track of the current chain (best block height,...) +- BIP44 Worker : Used to always have a set of 20 unused addresses as per BIP44. + +Theses default workers can be deactivated by adding the options `injectDefaultPlugins` to `false` while initializing your Wallet instance. + + +## Start a worker + +``` +worker.startWorker(); +``` + +## Stop a worker + +``` +worker.stopWorker(); +``` + +## Sync Worker + +### Events + +- WORKER/SYNC/STARTED - Triggered when the worker is started. +- WORKER/SYNC/EXECUTED - Triggered each time the worker get executed. + +## BIP 44 Worker + +### Create a BIP44 worker + +``` +const {events, storage, getAddress} = account; +const opts = { + events, + storage, + getAddress +} +const worker = new BIP44Worker(opts); +``` + +### Events + +- WORKER/BIP44/STARTED - Triggered when the worker is started. +- WORKER/BIP44/EXECUTED - Triggered each time the worker get executed. diff --git a/packages/wallet-lib/docs/plugins/writing-a-new-plugin.md b/packages/wallet-lib/docs/plugins/writing-a-new-plugin.md new file mode 100644 index 00000000000..dfe73b3d0ff --- /dev/null +++ b/packages/wallet-lib/docs/plugins/writing-a-new-plugin.md @@ -0,0 +1,140 @@ +# Writing a new plugin + +There is no control nor monitoring over third-party plugin. So anyone can write it's own plugin. + +In order for a plugin to have the ability to access wallet data, you have to add a dependency in the constructor. + +Below, we create a Standard Plugin, see [Using a plugin](plugins/using-a-plugin.md) for more information about the different plugin types. + +```js +const { StandardPlugin } = require('@dashevo/wallet-lib').plugins; + +class MyWalletConsolidatorPlugin extends StandardPlugin { + constructor() { + super({ + // When true, the wallet instance will only fire "ready" when a first execution of the plugin has happen. + firstExecutionRequired: false, + // Describe if we want to automatically execute it on starting up an account. + executeOnStart: false, + // Methods and function that we would want to use + dependencies: [ + 'getUTXOS', + 'getUnusedAddress', + 'getConfirmedBalance', + 'createTransactionFromUTXOS', + 'broadcastTransaction', + ], + }); + } + + consolidateWallet(address = this.getUnusedAddress().address, utxos = this.getUTXOS()) { + return { + prepareTransaction: () => { + if (!utxos || utxos.length === 0) { + throw new Error('There is nothing to consolidate'); + } + const opts = { + utxos, + recipient: address, + }; + + const rawtx = this.createTransactionFromUTXOS(opts); + return { + toString: () => rawtx, + broadcast: async () => { + console.log(`BROADCASTING ${rawtx}`); + return self.broadcastTransaction(rawtx); + }, + }; + }, + }; + } +} +``` + +## Using my created plugin + +When you plugin is created, including it in your Wallet is as easy as referencing up the class in the `plugins` array. + +```js +const wallet = new Wallet({ + plugins:[MyWalletConsolidatorPlugin] +}) +``` + +When some parameters are required first for your plugin to work, you might also decide to initialize first your plugin like this : + +```js +const wallet = new Wallet({ + plugins:[new MyWalletConsolidatorPlugin({someOptions:true})] +}); +``` + +## Accessing secure dependencies + +Due to the risk from running a plugin that have access to your keychain, these are, by default, not accessible. +One would need to initialize a Wallet with the option `allowSensitiveOperations` set to `true`. + +You can see the list of thoses [sensitive functions and properties](https://github.com/dashevo/platform/blob/master/packages/wallet-lib/src/CONSTANTS.js#L67), anything under `UNSAFE_*` will require this option to be set to true in order to be use from within a plugin. + +## Injection order + +While system plugins will by default be first injected in the system, in the case of a need for specific injection order. +Plugin can be sorted in such a way that in got injected before or after another set of plugins. +For this, use injectionOrder properties before and/or after. + + +In below example, this worker will be dependent on the methods getUTXOS to be internally available, and will be expected to be injected before TransactionSyncStreamWorker and after ChainPlugin. + +```js + class WithInjectBeforeDependenciesWorker extends Worker { + constructor() { + super({ + name: 'withInjectBeforeDependenciesWorker', + dependencies: [ + 'getUTXOS', + ], + injectionOrder: { + after: [ + 'ChainPlugin' + ], + before: [ + 'TransactionSyncStreamWorker' + ] + } + }); + } + } + ``` + +## Accessing events + +From a plugin, you have the ability to listen to account's emitted events. + +```js +const { EVENT, plugins: { Worker } } = require('@dashevo/wallet-lib'); +class NewBlockWorker extends Worker { + constructor(options) { + super({ + name: 'NewBlockWorker', + executeOnStart: true, + firstExecutionRequired: true, + workerIntervalTime: 60 * 1000, + gapLimit: 10, + dependencies: [ + 'storage', + 'transport', + 'walletId', + 'identities', + ], + ...options, + }); + } + + async onStart() { + this.parentEvents.on(EVENT.BLOCKHEIGHT_CHANGED, ({payload: blockHeight}) => { + // on new blockheight do something. + }); + } +} +``` diff --git a/packages/wallet-lib/docs/storage/Storage.md b/packages/wallet-lib/docs/storage/Storage.md new file mode 100644 index 00000000000..0f3e169d179 --- /dev/null +++ b/packages/wallet-lib/docs/storage/Storage.md @@ -0,0 +1,17 @@ +**Usage**: `new Storage(opts)` +**Description**: This method create a new Storage instance which provide various helper with interacting with atomic elements (tx, address). It connects with the adapter to perform load/save operations. + + +Parameters: + +- opts : + +| parameters | type | required | Description | +|------------------------------------|-----------------|----------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **opts.network** | Network\|String | no (testnet) | The network to use for the Storage instance | +| **opts.rehydrate** | Boolean | no (true) | If data should be autoloaded from the adapter | +| **opts.autosave** | Boolean | no (true) | If set at true, will autosave the storage to adapter at an autosaveIntervalTime | +| **opts.autosaveIntervalTime** | Number | no (10sec) | If millisecond, the interval time at which the adapter should persist the data | + +Returns : Storage instance. + diff --git a/packages/wallet-lib/docs/storage/addNewTxToAddress.md b/packages/wallet-lib/docs/storage/addNewTxToAddress.md new file mode 100644 index 00000000000..61398559732 --- /dev/null +++ b/packages/wallet-lib/docs/storage/addNewTxToAddress.md @@ -0,0 +1,13 @@ +**Usage**: `storage.addNewTxToAddress(tx, address)` +**Description**: Add a specific transaction to the related address. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------------------------------|------------------| ------------------------------------------------------------------------| +| **tx** | Transaction Object | yes | The Transaction to link to the B58 address | +| **address** | String (b58 address) | yes | A valid B58 address | + + +Returns: Boolean + diff --git a/packages/wallet-lib/docs/storage/addUTXOToAddress.md b/packages/wallet-lib/docs/storage/addUTXOToAddress.md new file mode 100644 index 00000000000..83f2613db44 --- /dev/null +++ b/packages/wallet-lib/docs/storage/addUTXOToAddress.md @@ -0,0 +1,13 @@ +**Usage**: `storage.addUTXOToAddress(utxo, address)` +**Description**: Link the specified utxo to the related address. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------------------------------|------------------| ------------------------------------------------------------------------| +| **utxo** | UTXO Object | yes | A valid UTXO | +| **address** | String (b58 address) | yes | A valid B58 address | + + +Returns: Boolean + diff --git a/packages/wallet-lib/docs/storage/announce.md b/packages/wallet-lib/docs/storage/announce.md new file mode 100644 index 00000000000..e7f3a9c4f5a --- /dev/null +++ b/packages/wallet-lib/docs/storage/announce.md @@ -0,0 +1,14 @@ +**Usage**: `storage.announce(type, el)` +**Description**: Internal method helper to announce event to the Wallet and Account class. +**Notes**: Listening to `accounts.events` will transmit those event. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------------------------------|------------------| ------------------------------------------------------------------------| +| **type** | String (event) | yes | | +| **el** | Object | yes | | + + +Returns: Boolean + diff --git a/packages/wallet-lib/docs/storage/calculateDuffBalance.md b/packages/wallet-lib/docs/storage/calculateDuffBalance.md new file mode 100644 index 00000000000..07d320db94c --- /dev/null +++ b/packages/wallet-lib/docs/storage/calculateDuffBalance.md @@ -0,0 +1,14 @@ +**Usage**: `storage.calculateDuffBalance(walletId, accountIndex, type)` +**Description**: Perform a full calculation of the balance of a wallet and account set. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------------------------------|------------------| ------------------------------------------------------------------------| +| **walletId** | String | yes | The wallet identifier in which we the account is | +| **accountIndex** | Number | yes | The account index from which we want to perform the calculation | +| **type** | Enum['total', 'confirmed', 'unconfirmed'] | no (def: total) | Depending of UTXO status, will calculate accordingly | + + +Returns: Number (duff - aka satoshis - value of the balance). + diff --git a/packages/wallet-lib/docs/storage/clearAll.md b/packages/wallet-lib/docs/storage/clearAll.md new file mode 100644 index 00000000000..84e560555e0 --- /dev/null +++ b/packages/wallet-lib/docs/storage/clearAll.md @@ -0,0 +1,11 @@ +**Usage**: `storage.clearAll()` +**Description**: Clear all data from the store and ask the adapter to store the empty state. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|------------------| ------------------------------------------------------------------------| + + +Returns: Boolean (saveState). + diff --git a/packages/wallet-lib/docs/storage/configure.md b/packages/wallet-lib/docs/storage/configure.md new file mode 100644 index 00000000000..14bafb76ec0 --- /dev/null +++ b/packages/wallet-lib/docs/storage/configure.md @@ -0,0 +1,16 @@ +**Usage**: `storage.configure(opts)` +**Description**: After Storage creation, this method is called to ensure Adapter contains expected method. +**Notes**: This is an internal advanced function called on the startup of a Storage. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|------------------| ------------------------------------------------------------------------| +| **opts.rehydrate** | Boolean | no | Set if the Storage will autoload from the adapter | +| **opts.autosave** | Boolean | no | Set if the Storage will autosave to the adapter | +| **opts.adapter** | Adapter | no | The adapter to test and use. | + + +Returns: void. +Emits: `CONFIGURED` + diff --git a/packages/wallet-lib/docs/storage/createChain.md b/packages/wallet-lib/docs/storage/createChain.md new file mode 100644 index 00000000000..c538e4bb20b --- /dev/null +++ b/packages/wallet-lib/docs/storage/createChain.md @@ -0,0 +1,13 @@ +**Usage**: `storage.createChain(network)` +**Description**: Create, if not already existing, a chain in the store. +**Notes**: This is an internal advanced function called on the creation of a Wallet. Also, at current state, both testnet and evonet uses the same "Testnet" object. Which might cause support issue when using both chain. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|------------------| ------------------------------------------------------------------------| +| **network** | Network/String | yes | The network of the chain to create | + + +Returns: Boolean + diff --git a/packages/wallet-lib/docs/storage/createWallet.md b/packages/wallet-lib/docs/storage/createWallet.md new file mode 100644 index 00000000000..03f521807a9 --- /dev/null +++ b/packages/wallet-lib/docs/storage/createWallet.md @@ -0,0 +1,16 @@ +**Usage**: `storage.createWallet(walletId, network, mnemonic, type)` +**Description**: Create a wallet in store based on the specified params. +**Notes**: This is an internal advanced function called on the creation of a Wallet. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|-------------------| ------------------------------------------------------------------------| +| **walletId** | String | yes | The wallet id to create | +| **network** | Network/String | no (Def: testnet) | The network for the wallet | +| **mnemonic** | Mnemonic/String | no (Def: null) | When applicable, the mnemonic used to generate the wallet | +| **type** | String | no (Def: null) | The wallet type to create | + + +Returns: Boolean + diff --git a/packages/wallet-lib/docs/storage/getStore.md b/packages/wallet-lib/docs/storage/getStore.md new file mode 100644 index 00000000000..9ccfeccf1c3 --- /dev/null +++ b/packages/wallet-lib/docs/storage/getStore.md @@ -0,0 +1,11 @@ +**Usage**: `storage.getStore()` +**Description**: Used to get the whole store. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ------------------------------------------------------------------------| + + +Returns: Full store data + diff --git a/packages/wallet-lib/docs/storage/getTransaction.md b/packages/wallet-lib/docs/storage/getTransaction.md new file mode 100644 index 00000000000..10175a70eed --- /dev/null +++ b/packages/wallet-lib/docs/storage/getTransaction.md @@ -0,0 +1,17 @@ +**Usage**: `storage.getTransaction(transactionId)` +**Description**: Return the transaction from the store matching the txId. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ------------------------------------------------------------------------| +| **transactionId** | String | yes | The transaction id to fetch from the state | + + +Returns: TransactionObject + +Example: + +```js +storage.getTransaction('4f71db0c4bf3e2769a3ebd2162753b54b33028e3287e45f93c5c7df8bac5ec7e') +``` diff --git a/packages/wallet-lib/docs/storage/getTransactionMetadata.md b/packages/wallet-lib/docs/storage/getTransactionMetadata.md new file mode 100644 index 00000000000..45b00eac239 --- /dev/null +++ b/packages/wallet-lib/docs/storage/getTransactionMetadata.md @@ -0,0 +1,17 @@ +**Usage**: `storage.getTransactionMetadata(transactionId)` +**Description**: Return the transaction metadata from the store matching the txId. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ------------------------------------------------------------------------| +| **transactionId** | String | yes | The transaction id to fetch from the state | + + +Returns: TransactionMetadata + +Example: + +```js +storage.getTransactionMetadata('4f71db0c4bf3e2769a3ebd2162753b54b33028e3287e45f93c5c7df8bac5ec7e') +``` diff --git a/packages/wallet-lib/docs/storage/importAccounts.md b/packages/wallet-lib/docs/storage/importAccounts.md new file mode 100644 index 00000000000..c13fa9b2087 --- /dev/null +++ b/packages/wallet-lib/docs/storage/importAccounts.md @@ -0,0 +1,12 @@ +**Usage**: `storage.importAccounts(accounts, walletId)` +**Description**: Allow to import one or multiple accounts (Account store representation) to the wallet store based on it's walletId. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ------------------------------------------------------------------------| +| **accounts** | Array[AccountObj] | yes | The array of account object to import to the store | +| **walletId** | String | yes | The wallet id to attach the account to | + + +Returns: Boolean diff --git a/packages/wallet-lib/docs/storage/importAddress.md b/packages/wallet-lib/docs/storage/importAddress.md new file mode 100644 index 00000000000..16bb2b9b52e --- /dev/null +++ b/packages/wallet-lib/docs/storage/importAddress.md @@ -0,0 +1,12 @@ +**Usage**: `storage.importAddress(addressObj, walletId)` +**Description**: Allow to import one address to the wallet store + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ------------------------------------------------------------------------| +| **addressObj** | AddressObj | yes | The address object to import to the store | +| **walletId** | String | yes | The wallet id to attach the address to | + + +Returns: Boolean diff --git a/packages/wallet-lib/docs/storage/importAddresses.md b/packages/wallet-lib/docs/storage/importAddresses.md new file mode 100644 index 00000000000..3537004b46a --- /dev/null +++ b/packages/wallet-lib/docs/storage/importAddresses.md @@ -0,0 +1,12 @@ +**Usage**: `storage.importAddresses(addresses, walletId)` +**Description**: Allow to import one or multiple addresses to the wallet + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ------------------------------------------------------------------------| +| **addresses** | Array(addressObj) | yes | The address object set to import to the store | +| **walletId** | String | yes | The wallet id to attach the address to | + + +Returns: Boolean diff --git a/packages/wallet-lib/docs/storage/importSingleAddress.md b/packages/wallet-lib/docs/storage/importSingleAddress.md new file mode 100644 index 00000000000..97836f561bf --- /dev/null +++ b/packages/wallet-lib/docs/storage/importSingleAddress.md @@ -0,0 +1,13 @@ +**Usage**: `storage.importSingleAddress(singleAddress, walletId)` +**Description**: Allow to import a single address to the wallet +**Notes**: Not useful when managing a HDWallet. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ------------------------------------------------------------------------| +| **singleAddress** | Object | yes | The single address object representation to import to the store | +| **walletId** | String | yes | The wallet id to attach the address to | + + +Returns: Boolean diff --git a/packages/wallet-lib/docs/storage/importTransaction.md b/packages/wallet-lib/docs/storage/importTransaction.md new file mode 100644 index 00000000000..d4223230ddc --- /dev/null +++ b/packages/wallet-lib/docs/storage/importTransaction.md @@ -0,0 +1,14 @@ +**Usage**: `storage.importTransaction(transaction, metadata?)` +**Description**: Allow to import a transaction to the store. +**Notes**: TransactionObject needs to contains basic vin/vout information. + +Parameters: + +| parameters | type | required | Description | +|------------------------|--------------------|----------------| ------------------------------------------------------------------------| +| **transaction** | Object/Transaction | yes | The transaction to import to the store | +| **metadata** | TransactionMetaData| no | The transaction metadata | + + +Returns: Boolean +Emits: `FETCHED_CONFIRMED_TRANSACTION`/`FETCHED_UNCONFIRMED_TRANSACTION` diff --git a/packages/wallet-lib/docs/storage/importTransactions.md b/packages/wallet-lib/docs/storage/importTransactions.md new file mode 100644 index 00000000000..101b0f6a58d --- /dev/null +++ b/packages/wallet-lib/docs/storage/importTransactions.md @@ -0,0 +1,12 @@ +**Usage**: `storage.importTransactions(transactions)` +**Description**: Allow to import a set (array) of transactions to the store. +**Notes**: Uses `storage.importTransaction` + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ------------------------------------------------------------------------| +| **transactions** | Array[TransactionWithMetadata]/Array[Transaction/Object]/Object | yes | The set of transactions to import to the store (can be a single element)| + + +Returns: Boolean diff --git a/packages/wallet-lib/docs/storage/rehydrateState.md b/packages/wallet-lib/docs/storage/rehydrateState.md new file mode 100644 index 00000000000..c8e155ca8f9 --- /dev/null +++ b/packages/wallet-lib/docs/storage/rehydrateState.md @@ -0,0 +1,12 @@ +**Usage**: async `storage.rehydrateState()` +**Description**: Used to fetch the state from the persistence adapter +**Notes**: Three items are fetch (`adapter.getItem`) : transactions, wallets and chains data. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| -----------------------------------------------------------------------| + + +Returns: Void +Emit: `REHYDRATE_STATE_SUCCESS/REHYDRATE_STATE_FAILED` event diff --git a/packages/wallet-lib/docs/storage/saveState.md b/packages/wallet-lib/docs/storage/saveState.md new file mode 100644 index 00000000000..2cb6f3db93b --- /dev/null +++ b/packages/wallet-lib/docs/storage/saveState.md @@ -0,0 +1,11 @@ +**Usage**: async `storage.saveState()` +**Description**: Used to force persistence of the state to the adapter +**Notes**: Three items are set (`adapter.setItem`) : transactions, wallets and chains data. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| -----------------------------------------------------------------------| + + +Returns : Boolean diff --git a/packages/wallet-lib/docs/storage/searchAddress.md b/packages/wallet-lib/docs/storage/searchAddress.md new file mode 100644 index 00000000000..16c57888431 --- /dev/null +++ b/packages/wallet-lib/docs/storage/searchAddress.md @@ -0,0 +1,12 @@ +**Usage**: `storage.searchAddress(address, forceLoop)` +**Description**: Returns a specific address information from the store +**Notes**: We maintain mapped (cache) address for easy look-up, forceLoop value allow to outpass that cache and force a slow lookup in the store. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| -----------------------------------------------------------------------| +| **address** | String | yes | The Address identifier (Base 58 hash representation) to search for | +| **forceLoop** | Boolean | no (def: false)| Used to bypass the cache and force looping over the addresses | + +Returns : Object ({found, result, type, address, walletId}). diff --git a/packages/wallet-lib/docs/storage/searchAddressesWithTx.md b/packages/wallet-lib/docs/storage/searchAddressesWithTx.md new file mode 100644 index 00000000000..8bd864f7ef5 --- /dev/null +++ b/packages/wallet-lib/docs/storage/searchAddressesWithTx.md @@ -0,0 +1,10 @@ +**Usage**: `storage.searchAddressesWithTx(transactionId)` +**Description**: Returns the list of addresses objects that has a relation with the provided TxId. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ----------------------------------------------------------| +| **transactionId** | String | yes | The Transaction identifier to search for | + +Returns : Object ({found, results, txid}). diff --git a/packages/wallet-lib/docs/storage/searchBlockHeader.md b/packages/wallet-lib/docs/storage/searchBlockHeader.md new file mode 100644 index 00000000000..f63fa22e2ef --- /dev/null +++ b/packages/wallet-lib/docs/storage/searchBlockHeader.md @@ -0,0 +1,10 @@ +**Usage**: `storage.searchBlockHeader(identifier)` +**Description**: Returns the blockHeader matching the provided identifier if it is stored. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ----------------------------------------------------------| +| **identifier** | String/Number | yes | The BlockHeader identifier (hash or height) to search for | + +Returns : [BlockHeader](https://github.com/dashevo/dashcore-lib/blob/master/docs/block.md#block-header). diff --git a/packages/wallet-lib/docs/storage/searchTransaction.md b/packages/wallet-lib/docs/storage/searchTransaction.md new file mode 100644 index 00000000000..096627eabf9 --- /dev/null +++ b/packages/wallet-lib/docs/storage/searchTransaction.md @@ -0,0 +1,10 @@ +**Usage**: `storage.searchTransaction(transactionId)` +**Description**: Returns the transaction information from the provided TxId if exists. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ----------------------------------------------------------| +| **transactionId** | String | yes | The Transaction identifier to search for | + +Returns : Object ({found, result, hash}). diff --git a/packages/wallet-lib/docs/storage/searchTransactionMetadata.md b/packages/wallet-lib/docs/storage/searchTransactionMetadata.md new file mode 100644 index 00000000000..c668be862ab --- /dev/null +++ b/packages/wallet-lib/docs/storage/searchTransactionMetadata.md @@ -0,0 +1,10 @@ +**Usage**: `storage.searchTransactionMetadata(transactionId)` +**Description**: Returns the transaction metadata from the provided TxId if exists. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ----------------------------------------------------------| +| **transactionId** | String | yes | The Transaction identifier to search for | + +Returns : Object ({found, result, hash}). diff --git a/packages/wallet-lib/docs/storage/searchWallet.md b/packages/wallet-lib/docs/storage/searchWallet.md new file mode 100644 index 00000000000..cfc6cc45e56 --- /dev/null +++ b/packages/wallet-lib/docs/storage/searchWallet.md @@ -0,0 +1,10 @@ +**Usage**: `storage.searchWallet(walletId)` +**Description**: Returns the wallet information from the provided walletId if exists. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ----------------------------------------------------------| +| **walletId** | String | yes | The Wallet identifier of the wallet containing the address to update | + +Returns : Object ({found, result, walletId}). diff --git a/packages/wallet-lib/docs/storage/startWorker.md b/packages/wallet-lib/docs/storage/startWorker.md new file mode 100644 index 00000000000..894d53d4fa2 --- /dev/null +++ b/packages/wallet-lib/docs/storage/startWorker.md @@ -0,0 +1,11 @@ +**Usage**: `storage.startWorker()` +**Description**: Allow to start the storage worker (uses interval), will save the state when needed. +**Notes**: Use `storage.stopWorker()` to stop. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ----------------------------------------------------------| + +Returns : void. + diff --git a/packages/wallet-lib/docs/storage/stopWorker.md b/packages/wallet-lib/docs/storage/stopWorker.md new file mode 100644 index 00000000000..c05a8e2b4bf --- /dev/null +++ b/packages/wallet-lib/docs/storage/stopWorker.md @@ -0,0 +1,10 @@ +**Usage**: `storage.stopWorker()` +**Description**: When the storage worker is running (interval), will clear it stopping further interval to be executed. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ----------------------------------------------------------| + +Returns : Boolean. + diff --git a/packages/wallet-lib/docs/storage/updateAddress.md b/packages/wallet-lib/docs/storage/updateAddress.md new file mode 100644 index 00000000000..454a1da0302 --- /dev/null +++ b/packages/wallet-lib/docs/storage/updateAddress.md @@ -0,0 +1,27 @@ +**Usage**: async `storage.updateAddress(addressObj, walletId)` +**Description**: Used to update a specific address of a wallet identified by it's walletId. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ----------------------------------------------------------| +| **addressObj** | AddressObject | yes | The AddressObject to update (uses address.path as primary key) | +| **walletId** | String | yes | The Wallet identifier of the wallet containing the address to update | + +Returns : Boolean. + +Example: +```js +storage.updateAddress({ + path: "m/44'/1'/0'/0/0", + index: '0', + address: 'yLhsYLXW5sFHLDPLj2EHgrmQRhP712ANda', + transactions: [], + balanceSat: 0, + unconfirmedBalanceSat: 0, + utxos: {}, + fetchedLast: 0, + used: true, + }, "a3771aaf93"); +``` + diff --git a/packages/wallet-lib/docs/storage/updateTransaction.md b/packages/wallet-lib/docs/storage/updateTransaction.md new file mode 100644 index 00000000000..84f64cdd21a --- /dev/null +++ b/packages/wallet-lib/docs/storage/updateTransaction.md @@ -0,0 +1,38 @@ +**Usage**: `storage.updateTransaction(transaction)` +**Description**: Internally, this is mostly called to update the information of a transaction in the store. Works mostly more as an replace than an update. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-------------------|----------------| ----------------------------------------------------------| +| **transaction** | Transaction | yes | The Transaction to update (uses tx.hash as key) | + +Returns : Boolean. + +Example: +```js +const { Transaction } = require('@dashevo/dashcore-lib'); +const transaction = new Transaction({ + hash: '9b4a34096f2270f70d8e0ba91094eb37535349f80874f8440e74c0567ef82680', + version: 3, + inputs: [ + { + prevTxId: '9f398515b6fc898ebf4e7b49bbfc4359b8c89f508c6cd677e53946bd86064b28', + outputIndex: 0, + sequenceNumber: 4294967295, + script: '47304402205bb4f7880fb0fc13218940ba341c30e817363e5590343d28639af921b2a5f1d40220010920ae4b00bbb657f8653cb44172b8cb13447bb5105ddaf32a2845ea0666b90121025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b', + scriptString: '71 0x304402205bb4f7880fb0fc13218940ba341c30e817363e5590343d28639af921b2a5f1d40220010920ae4b00bbb657f8653cb44172b8cb13447bb5105ddaf32a2845ea0666b901 33 0x025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b' + } + ], + outputs: [ + { + satoshis: 4294967000, + script: '76a9143ec33076ba72b36b66b7ec571dd7417abdeb76f888ac' + } + ], + nLockTime: 0 +}) + +storage.updateTransaction(transaction); +``` + diff --git a/packages/wallet-lib/docs/usage/coinSelection.md b/packages/wallet-lib/docs/usage/coinSelection.md new file mode 100644 index 00000000000..2697b23ec3e --- /dev/null +++ b/packages/wallet-lib/docs/usage/coinSelection.md @@ -0,0 +1,51 @@ +# Coin Selection + +## Purpose + +In order to decide which set of input to use for making payments, wallet-lib use the coin selection helper which will decide which unspent transaction output (UTXO) to select. + +## Strategies + +There are multiples strategy algorithms provided with Wallet-lib, that you can chose from. + +| Strategy | Description | +|-------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| simpleDescendingAccumulator | Will maximize the uses of big inputs to meet the amount required. Allows the fee to be optimized for the smallest size at the cost of breaking big inputs. | +| simpleAscendingAccumulator | Will try to use as many small inputs as possible to meet the amount required. Allows using many small inputs at the cost of a potentially bigger fee. | + +By default, the algorithm that is being used is `simpleDescendingAccumulator`. + +See [Account.createTransaction()](../account/createTransaction.md) for more information about how to select one during transaction creation. + +Additionally, you can also require the utility function `const coinSelection = require('@dashevo/wallet-lib/src/utils/coinSelection.js')` for your own usage. + + +``` +const utxosList = account.getUTXOS(); +const outputsList = [{ + address:'XmjeE...', + satoshis:1200000 +}] +const coinSelection = coinSelection(utxosList, outputsList); +const selectedUTXO = coinSelection(utxosList, outputsList); +``` + +## Implement your own algorithm + +By creating a simple function algorithm that you pass to the createTransaction parameter, you can provide your own algorithm that will be used to the coinSelection. + +To implements your own algorithm, you might want to take example on the [already existing one](https://github.com/dashevo/platform/tree/master/packages/wallet-lib/src/utils/coinSelections/strategies). +You will need your algorithm to handle multiples parameter : + +- `utxosList` - An array consisting of multiple [unspent output](https://github.com/dashevo/dashcore-lib/blob/master/docs/unspentoutput.md). +- `outputsList` - An array consisting of multiple [Output](https://github.com/dashevo/dashcore-lib/blob/master/docs/transaction.md#handling-outputs). +- `deductFee` - A simple boolean that indicates if we want to deduct fee from our outputs. (Can be useful for a control on how much we wish to spend at maximum). +- `feeCategory` - A simple enum of the fee category (normal, slow, fast,...). + +Your algorithm will be required to return the following object structure : + +- `utxos`: An array consisting of the final selection of UTXOs. +- `outputs`: An array consisting of the final outputs (which might have been modified in case of deductFee being `true`). +- `estimatedFee`: A duff value of the fee estimated for such transaction. +- `utxosValue`: The total accumulated duffs value of the used UTXOs. +- `feeCategory` diff --git a/packages/wallet-lib/docs/usage/dapi.md b/packages/wallet-lib/docs/usage/dapi.md new file mode 100644 index 00000000000..4628092e241 --- /dev/null +++ b/packages/wallet-lib/docs/usage/dapi.md @@ -0,0 +1,32 @@ +## About DAPI + +DAPI (Decentralized API) is a distributed and decentralized endpoints provided by the Masternode Network. +You can learn more about DAPI on the [DAPI-Client documentation](https://dashevo.github.io/platform/DAPI-Client/). + +## Get the DAPI-Client instance + +When the Wallet-lib is initialized without any transporter, Wallet-lib will by default use DAPI-Client as a transporter. +You can fetch the current instance of DAPI directly from the wallet : + +```js + const wallet = new Wallet(); + const client = wallet.transport; +``` + +## Modify the seeds + +By using your own DAPI-Client instance and passing it to the Wallet constructor (using `transport` argument). You can specify your own seeds to connect to. + +```js +const DAPIClient = require('@dashevo/dapi-client'); +const { Wallet } = require('./src'); +const DAPIClientTransport = require('./src/transport/DAPIClientTransport/DAPIClientTransport.js'); + +const client = new DAPIClient({ + seeds: [{ service: '18.236.131.253:3000' }], + timeout: 20000, + retries: 5, +}); +const transport = new DAPIClientTransport(client); +const wallet = new Wallet({ transport }); +``` diff --git a/packages/wallet-lib/docs/usage/events.md b/packages/wallet-lib/docs/usage/events.md new file mode 100644 index 00000000000..782ec48c3dc --- /dev/null +++ b/packages/wallet-lib/docs/usage/events.md @@ -0,0 +1,46 @@ +## Events + +```javascript +const {EVENTS} = require('@dashevo/wallet-lib'); +const {FETCHED_CONFIRMED_TRANSACTION} = EVENTS; +const doSomethingConfirmedTransactionFetched = (tx) => {...} +account.on(FETCHED_CONFIRMED_TRANSACTION, doSomethingConfirmedTransactionFetched); +``` + +Events types : + + +### Storage + +| Event Name | Description | +| -------------------------- |:-------------------------------------------------------:| +| CONFIGURED | throwed when Storage has configured the adapter. | +| REHYDRATE_STATE_FAILED | onFailedRehydrateState | +| REHYDRATE_STATE_SUCCESS | throwed when Storage has succesfully rehydrated the data| + +### General + +| Event Name | Description | +| -------------------------- |:------------------------------------:| +| READY | throwed when ready to be used | + + + +### Sync Info + +| Event Name | Description | +| -------------------------------- |:--------------------------------------------------------------------:| +| BLOCKHEIGHT_CHANGED | When the chain has moved from one block forward | +| FETCHED_UNCONFIRMED_TRANSACTION | When we got to fetch an unconfirmed transaction, we throw this event | +| FETCHED_CONFIRMED_TRANSACTION | This one is if the transaction is confirmed | +| FETCHED_TRANSACTIONS | In both case, we throw that event | + + +### Balance + +| Event Name | Description | +| ---------------------------- |:----------------------------------------------------------------------:| +| UNCONFIRMED_BALANCE_CHANGED | When unconfirmed balance change, we gives the delta + totalValue | +| BALANCE_CHANGED | When the balance change, we gives the delta + totalValue | + + diff --git a/packages/wallet-lib/docs/usage/examples.md b/packages/wallet-lib/docs/usage/examples.md new file mode 100644 index 00000000000..72d70d1c7a9 --- /dev/null +++ b/packages/wallet-lib/docs/usage/examples.md @@ -0,0 +1,21 @@ +# Examples + +## Offline cold-wallet usage : + +The wallet can handle a lack of connectivity. This could allow the use of the wallet library in an offline way. +Such uses case would be to generate a bunch of addresses that you could manually import into a database (for rolling incoming addresses) + +You also have ways to import a known address, transaction, account or any type of data that you could wish to have via the network, +thus allowing you to use the wallet-library without connectivity. + +See here [sample code](https://github.com/dashevo/platform/blob/master/packages/wallet-lib/examples/offline-wallet.js) + +## Offline message signing : + +See here [sample code](https://github.com/dashevo/platform/blob/master/packages/wallet-lib/examples/offline-wallet-signing-message.js) + +## Client usage + +- From Mnemonic: [sample code](https://github.com/dashevo/platform/blob/master/packages/wallet-lib/examples/client-usage.js) +- From PrivateKey: [sample code](https://github.com/dashevo/platform/blob/master/packages/wallet-lib/examples/client-usage-single-privateKey.js) + diff --git a/packages/wallet-lib/docs/utils/calculateTransactionFees.md b/packages/wallet-lib/docs/utils/calculateTransactionFees.md new file mode 100644 index 00000000000..4c62a42efaa --- /dev/null +++ b/packages/wallet-lib/docs/utils/calculateTransactionFees.md @@ -0,0 +1,11 @@ +**Usage**: `calculateTransactionFees(transaction)` +**Description**: Return for a transaction, the fee value that were used +**Notes**: To calculate the fee, provided transaction's input require output knowledge to be supplied + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **transaction** | Transaction | yes | A transaction instance | + +Returns : {number} - fee value used for this transaction \ No newline at end of file diff --git a/packages/wallet-lib/docs/utils/categorizeTransactions.md b/packages/wallet-lib/docs/utils/categorizeTransactions.md new file mode 100644 index 00000000000..c6f0ea01aa7 --- /dev/null +++ b/packages/wallet-lib/docs/utils/categorizeTransactions.md @@ -0,0 +1,28 @@ +**Usage**: `categorizeTransactions()` +**Description**: Return for a transaction, the fee value that were used + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **transactionsWithMetadata** | [TransactionMetadata] | yes | Transaction with their metadata | +| **accountStore** | Object | yes | Account store with addresses | +| **accountIndex** | Number | yes | The account index | +| **walletType** | WALLET_TYPES | yes | The wallet type | +| **network** | Network/String | no (def: testnet) | Wallet network | + +Returns : {[CategorizedTransaction]} - Array of categorized transactions + +```js +const categorizedTransactions = categorizeTransaction(transactionsWithMetadata, accountstore, 0, WALLET_TYPES.HDWALLET); +[{ + transaction: Transaction(), + type: 'received', + from: [{}], + to: [{}], + blockHash: '00001' + height: 42, + isInstantLocked: true, + isChainLocked: true +}] +``` \ No newline at end of file diff --git a/packages/wallet-lib/docs/utils/classifyAddresses.md b/packages/wallet-lib/docs/utils/classifyAddresses.md new file mode 100644 index 00000000000..2649e9bccc8 --- /dev/null +++ b/packages/wallet-lib/docs/utils/classifyAddresses.md @@ -0,0 +1,28 @@ +**Usage**: `classifyAddresses(addressStore, accountIndex, walletType)` +**Description**: Return for an addressStore, accountIndex and wallet type, the array classified set of addresses + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **addressStore** | Object | yes | Account store with addresses | +| **accountIndex** | Number | yes | The account index | +| **walletType** | WALLET_TYPES | yes | The wallet type | + +Returns : {[ClassifiedAddresses]} - Array of classified addresses + +```js +const classifiedAddresses = classifyAddresses(addressStore, 0, WALLET_TYPES.HDWALLET); + +{ + externalAddressList: [ + 'yd1ohc12LgCYp56CDuckTEHwoa6LbPghMd', + '...' + ], + internalAddressList: [ + 'yaLhoAZ4iex2zKmfvS9rvEmxXmRiPrjHdD', + '...' + ], + otherAccountAddressList: [] +}; +``` \ No newline at end of file diff --git a/packages/wallet-lib/docs/utils/coinSelection.md b/packages/wallet-lib/docs/utils/coinSelection.md new file mode 100644 index 00000000000..08079e35f1c --- /dev/null +++ b/packages/wallet-lib/docs/utils/coinSelection.md @@ -0,0 +1,26 @@ +**Usage**: `coinSelection(utxosList, outputsList, deductFee, feeCategory, strategy)` +**Description**: For a provided outputsList will select the best utxos from utxosList matching the fees and strategy requirements + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **utxosList** | [UTXO] | yes | Account store with addresses | +| **outputsList** | [Output] | yes | The account index | +| **deductFee** | Boolean | no (def: false) | The wallet type | +| **feeCategory** | FeeCategory | no (def: normal) | The wallet type | +| **strategy** | Strategy | no (def: simpleDescendingAccumulator) | The wallet type | + +Returns : {[ClassifiedAddresses]} - Array of classified addresses + +```js +coinSelection(utxosList, outputsList, true); + +{ + utxos, + outputs, + feeCategory, + estimatedFee, + utxosValue, + } +``` \ No newline at end of file diff --git a/packages/wallet-lib/docs/utils/dashToDuffs.md b/packages/wallet-lib/docs/utils/dashToDuffs.md new file mode 100644 index 00000000000..1427c89e872 --- /dev/null +++ b/packages/wallet-lib/docs/utils/dashToDuffs.md @@ -0,0 +1,14 @@ +**Usage**: `dashToDuffs(dash)` +**Description**: For a dash value, returns value in duffs + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **dash** | Number | yes | Value input in dash | + +Returns : {Number} - Value in duffs (satoshis) + +```js +dashToDuffs(1); //returns 100000000 +``` \ No newline at end of file diff --git a/packages/wallet-lib/docs/utils/duffsToDash.md b/packages/wallet-lib/docs/utils/duffsToDash.md new file mode 100644 index 00000000000..d9cbd034115 --- /dev/null +++ b/packages/wallet-lib/docs/utils/duffsToDash.md @@ -0,0 +1,14 @@ +**Usage**: `duffsToDash(duffs)` +**Description**: For a duff value, returns value in Dash + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **duffs** | Number | yes | Value input in duff | + +Returns : {Number} - Value in dash + +```js +duffsToDash(100000000); //returns 1 +``` \ No newline at end of file diff --git a/packages/wallet-lib/docs/utils/extendTransactionsWithMetadata.md b/packages/wallet-lib/docs/utils/extendTransactionsWithMetadata.md new file mode 100644 index 00000000000..99de07e6916 --- /dev/null +++ b/packages/wallet-lib/docs/utils/extendTransactionsWithMetadata.md @@ -0,0 +1,27 @@ +**Usage**: `extendTransactionsWithMetadata(addressStore, accountIndex, walletType)` +**Description**: Return for an addressStore, accountIndex and wallet type, the array classified set of addresses + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **addressStore** | Object | yes | Account store with addresses | +| **accountIndex** | Number | yes | The account index | +| **walletType** | WALLET_TYPES | yes | The wallet type | + +Returns : {[TransactionsWithMetadata]} - Array of transactions with metadata addresses + +```js +extendTransactionsWithMetadata(transactions, transactionsMetadata); +[ + [ + Transaction, + { + blockHash: '0000012cf6377c6cf2b317a4deed46573c09f04f6880dca731cc9ccea6691e19', + height: 555508, + instantLocked: true, + chainLocked: true + } + ] +]; +``` \ No newline at end of file diff --git a/packages/wallet-lib/docs/utils/filterTransactions.md b/packages/wallet-lib/docs/utils/filterTransactions.md new file mode 100644 index 00000000000..8983ea35e2d --- /dev/null +++ b/packages/wallet-lib/docs/utils/filterTransactions.md @@ -0,0 +1,17 @@ +**Usage**: `filterTransactions(accountStore, walletType, accountIndex, transactions)` +**Description**: + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **addressStore** | Object | yes | Account store with addresses | +| **accountIndex** | Number | yes | The account index | +| **walletType** | WALLET_TYPES | yes | The wallet type | + +Returns : {[Transaction]} - Array of transaction filtered + +```js +filterTransactions(accountStore, walletType, accountIndex, transactions); +[Transaction,...]; +``` \ No newline at end of file diff --git a/packages/wallet-lib/docs/utils/getBytesOf.md b/packages/wallet-lib/docs/utils/getBytesOf.md new file mode 100644 index 00000000000..bc460a16058 --- /dev/null +++ b/packages/wallet-lib/docs/utils/getBytesOf.md @@ -0,0 +1,12 @@ +**Usage**: `getBytesOf(value, type)` +**Description**: For a provided type and value, returns the bytes value of it +**Notes**: Used for transaction size calculation for fee estimation + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **value** | Output/UTXO | yes | Value input in dash | +| **type** | Output/UTXO | yes | Value input in dash | + +Returns : {Number} - Bytes value diff --git a/packages/wallet-lib/docs/utils/mnemonic/generateNewMnemonic.md b/packages/wallet-lib/docs/utils/mnemonic/generateNewMnemonic.md new file mode 100644 index 00000000000..9806fad0198 --- /dev/null +++ b/packages/wallet-lib/docs/utils/mnemonic/generateNewMnemonic.md @@ -0,0 +1,9 @@ +**Usage**: `generateNewMnemonic()` +**Description**: Generate a new random mnemonic + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| + +Returns : {Mnemonic} diff --git a/packages/wallet-lib/docs/utils/mnemonic/mnemonicToHDPrivateKey.md b/packages/wallet-lib/docs/utils/mnemonic/mnemonicToHDPrivateKey.md new file mode 100644 index 00000000000..fcce455180e --- /dev/null +++ b/packages/wallet-lib/docs/utils/mnemonic/mnemonicToHDPrivateKey.md @@ -0,0 +1,10 @@ +**Usage**: `mnemonicToHDPrivateKey(mnemonic)` +**Description**: + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **mnemonic** | Mnemonic | yes | A mnemonic | + +Returns : {HDPrivateKey} diff --git a/packages/wallet-lib/docs/utils/mnemonic/mnemonicToSeed.md b/packages/wallet-lib/docs/utils/mnemonic/mnemonicToSeed.md new file mode 100644 index 00000000000..4b51fdd3795 --- /dev/null +++ b/packages/wallet-lib/docs/utils/mnemonic/mnemonicToSeed.md @@ -0,0 +1,11 @@ +**Usage**: `mnemonicToSeed(mnemonic, password)` +**Description**: Generate the seed from mnemonic + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **mnemonic** | Mnemonic | yes | A mnemonic | +| **password** | string | no | | + +Returns : {String} hex seed diff --git a/packages/wallet-lib/docs/utils/mnemonic/mnemonicToWalletId.md b/packages/wallet-lib/docs/utils/mnemonic/mnemonicToWalletId.md new file mode 100644 index 00000000000..ab84a7b54bf --- /dev/null +++ b/packages/wallet-lib/docs/utils/mnemonic/mnemonicToWalletId.md @@ -0,0 +1,10 @@ +**Usage**: `mnemonicToWalletId(mnemonic)` +**Description**: Generate the deterministic wallet id based on mnemonic + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **mnemonic** | Mnemonic | yes | A mnemonic | + +Returns : {String} hex wallet id diff --git a/packages/wallet-lib/docs/utils/mnemonic/seedToHDPrivateKey.md b/packages/wallet-lib/docs/utils/mnemonic/seedToHDPrivateKey.md new file mode 100644 index 00000000000..4c38db3b3ec --- /dev/null +++ b/packages/wallet-lib/docs/utils/mnemonic/seedToHDPrivateKey.md @@ -0,0 +1,10 @@ +**Usage**: `seedToHDPrivateKey(seed)` +**Description**: + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------------|----------------| -------------------------------------------------| +| **seed** | Mnemonic | yes | A mnemonic | + +Returns : {HDPrivateKey} diff --git a/packages/wallet-lib/docs/wallet/Wallet.md b/packages/wallet-lib/docs/wallet/Wallet.md new file mode 100644 index 00000000000..c6845323f3c --- /dev/null +++ b/packages/wallet-lib/docs/wallet/Wallet.md @@ -0,0 +1,102 @@ +**Usage**: `new Wallet(walletOpts)` +**Description**: This method creates a new Wallet. +In Wallet-Lib, a Wallet is a manager that is tied to a passphrase/seed or privateKey and manage one or multiples Account from that. +It's purpose is mainly to create or get an account, allowing multiple account to be tracked and tied from a single manager. + +Parameters: + +| parameters | type | required | Description | +|------------------------------------------|--------------------|---------------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **walletOpts.network** | string/Network | no (def:'testnet') | Use either a string reference to Networks ('livenet', 'testnet') or it's Networks representation | +| **walletOpts.mnemonic** | string/Mnemonic | no | If sets at null, generate a new mnemonic. If sets to a valid value, create wallet from mnemonic | +| **walletOpts.passphrase** | string | no | If sets at null, generate a new privateKey. It sets to a valid privateKey, uses it (with the passphrase if provided) to unlock the seed | +| **walletOpts.offlineMode** | boolean | no (def: false) | Set to true to not perform any request to the network | +| **walletOpts.injectDefaultPlugins** | boolean | no (def: true) | Use to inject default plugins on loadup (BIP44Worker, ChainWorker and SyncWorker) | +| **walletOpts.allowSensitiveOperations** | boolean | no (def: false) | If you want a special plugin to access the keychain or other sensitive operation, set this to true. | +| **walletOpts.cache.addresses** | object | no | If you have your cache state somewhere else (fs) you can fetch and pass it along for faster sync-up | +| **walletOpts.cache.transactions** | object | no | If you have your cache state somewhere else (fs) you can fetch and pass it along for faster sync-up | +| **walletOpts.plugins** | Array | no | It you have some plugins, worker you want to pass to wallet-lib. You can pass them as constructor or initialized object | +| **walletOpts.seed** | string | no | If you only have a seed representation, you can pass it instead of mnemonic to init the wallet from it | +| **walletOpts.HDPrivateKey** | string/HDPrivateKey| no | If you only have a HDPrivateKey representation, you can pass it instead of mnemonic to init the wallet from it | +| **walletOpts.HDPublicKey** | string/HDPublicKey | no | If you only have a HDPublicKey representation, you can pass it instead of mnemonic to init the wallet from it | +| **walletOpts.privateKey** | string/PrivateKey | no | If you only have a PrivateKey representation, you can pass it instead of mnemonic to init the wallet from it | +| **walletOpts.publicKey** | string/PublicKey | no | If you only have a PublicKey representation, you can pass it instead of mnemonic to init the wallet from it | + + +N.B 1 : If both mnemonic, seed and privateKey are filled, only mnemonic will be used. If none is entered, the wallet will create a mnemonic. +N.B 2 : When initialized from a `privateKey`, `publicKey` or an `HDPublicKey`, comportment of Wallet-lib differs slightly. + +- PrivateKey : There is no path in this mode. It's a unique public address. +- PrivateKey : There is no path in this mode. It's a unique public address. Watch-only. +- HDPublicKey : There is no signing in this mode. Watch-only. + +Returns : Wallet instance. + +**Examples** : + +### Creation without a mnemonic (gets one generated) +```js +const wallet = new Wallet(); +``` +or +```js +const wallet = new Wallet({ + mnemonic: null +}); +console.log(wallet.exportWallet()); +``` + +In the case where you will want to have stronger entropy (have 24 words generated instead of 12), this snippet will allow to do that : + +```js +const { Mnemonic } = require('@dashevo/dashcore-lib'); +const mnemonic = new Mnemonic(256).toString(); +``` + +### Creation from Mnemonic + +```js +const wallet = new Wallet({ + mnemonic: 'hole lesson insane entire dolphin scissors game dwarf polar ethics drip math' +}) +``` + +### Creation from HDPrivateKey + +```js +const wallet = new Wallet({ + HDPrivateKey: 'tprv8ZgxMBicQKsPeWisxgPVWiXho8ozsAUqc3uvpAhBuoGvSTxqkxPZbTeG43mvgXn3iNfL3cBL1NmR4DaVoDBPMUXe1xeiLoc39jU9gRTVBd2' +}) +``` + +### Creation from HDPublicKey + +```js +const wallet = new Wallet({ + HDPublicKey: 'tpubDEB6BgW9JvZRWVbFmwwGuJ2vifakABuxQWdY9yXbFC2rc3zagie1RkhwUEnahb1dzaapchEVeKqKcx99TzkjNvjXcmoQkLJwsYnA1J5bGNj' +}) +``` + +### Creation from Seed + +```js +const wallet = new Wallet({ + seed: '436905e6756c24551bffaebe97d0ebd51b2fa027e838c18d45767bd833b02a80a1dd55728635b54f2b1dbed5963f4155e160ee1e96e2d67f7e8ac28557d87d96' +}) +``` + +### Creation from privateKey + +```js +const wallet = new Wallet({ + privateKey: 'cR4t6evwVZoCp1JsLk4wURK4UmBCZzZotNzn9T1mhBT19SH9JtNt' +}) +``` + +### Creation from publicKey + +```js +const wallet = new Wallet({ + HDPublicKey: 'tpubDEB6BgW9JvZRWVbFmwwGuJ2vifakABuxQWdY9yXbFC2rc3zagie1RkhwUEnahb1dzaapchEVeKqKcx99TzkjNvjXcmoQkLJwsYnA1J5bGNj' +}) +``` diff --git a/packages/wallet-lib/docs/wallet/createAccount.md b/packages/wallet-lib/docs/wallet/createAccount.md new file mode 100644 index 00000000000..a5a3d91adc2 --- /dev/null +++ b/packages/wallet-lib/docs/wallet/createAccount.md @@ -0,0 +1,10 @@ +**Usage**: `await wallet.createAccount(accountOpts)` +**Description**: This method, equivalent of a `new Account(wallet, accountOpts)` will create a new account having the specified options. + +Parameters: +See the [Account](../account/Account.md) constructor parameters. + +N.B : You also probably mean to use [`.getAccount()`](../wallet/getAccount.md) instead. This is designed mostly to be Private as get an account deal with it. + +Returns : void. + diff --git a/packages/wallet-lib/docs/wallet/disconnect.md b/packages/wallet-lib/docs/wallet/disconnect.md new file mode 100644 index 00000000000..9b32fce821d --- /dev/null +++ b/packages/wallet-lib/docs/wallet/disconnect.md @@ -0,0 +1,9 @@ +**Usage**: `wallet.disconnect()` +**Description**: This method will disconnect all accounts, plugins and other workers (Storage). Useful to release all worker when doing integration testing with Wallet/Account + +Parameters: + +| parameters | type | required | Description | +|------------------------|-----------|----------------| ------------------------------------------------------------------------------- | + +Returns : void. diff --git a/packages/wallet-lib/docs/wallet/dumpStorage.md b/packages/wallet-lib/docs/wallet/dumpStorage.md new file mode 100644 index 00000000000..8b1952b4bf6 --- /dev/null +++ b/packages/wallet-lib/docs/wallet/dumpStorage.md @@ -0,0 +1,15 @@ +**Usage**: `wallet.dumpStorage([opts])` +**Description**: Method dumps the state of the storage in JSON format + +**Warning**: Storage dump may contain sensitive data. +Please, do not share the output of this function for `mainnet` wallets. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-----------|----------------| ---------------------------------------------------------- | +| **opts.log** | Boolean | no | Indicates whether storage should be logged in the console | + +Returns : {String} + + diff --git a/packages/wallet-lib/docs/wallet/exportWallet.md b/packages/wallet-lib/docs/wallet/exportWallet.md new file mode 100644 index 00000000000..5a045e35249 --- /dev/null +++ b/packages/wallet-lib/docs/wallet/exportWallet.md @@ -0,0 +1,17 @@ +**Usage**: `wallet.exportWallet([outputType])` +**Description**: This method will export the wallet to the default outputType (depending on initializated params : mnemonic. HDPubKey,...). + +This method varies depending from which type of wallet is this. +- When init from a mnemonic, by default return mnemonic but support 'HDPrivateKey' +- When init from a seed, by default returns and only support HDPrivateKey +- When init from a private key, by default returns and only support PrivateKey. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-----------|----------------| ---------------------------------------------------------- | +| **outputType** | String | no | The required output type of the exported wallet | + +Returns : {Mnemonic|HDPrivateKey|HDPublicKey|PrivateKey} + + diff --git a/packages/wallet-lib/docs/wallet/fromHDPrivateKey.md b/packages/wallet-lib/docs/wallet/fromHDPrivateKey.md new file mode 100644 index 00000000000..6efd132ede3 --- /dev/null +++ b/packages/wallet-lib/docs/wallet/fromHDPrivateKey.md @@ -0,0 +1,19 @@ +**Usage**: `wallet.fromHDPrivateKey(HDPrivateKey)` +**Description**: Initialize a Wallet from a HDPrivateKey representation. +**Notes**: This is an internal method, in the future, when TC39 proposal pass, we will use the private markup. + +Parameters: + +| parameters | type | required | Description | +|------------------------|------------------------|----------------| --------------------------------------------------------------------| +| **HDPrivateKey** | HDPrivateKey|String | yes | The HDPrivateKey from which you want to initialize the wallet. | + +Returns : void (set a HD Wallet). + +Examples : + +```js +wallet.fromHDPrivateKey('xprv9s21ZrQH143K37d2j9YW7snYGbAJJX9vzEZRwU7QEc4yP39t1Yc7t2Aw79aBBQWfLNqnpo9bFRnWoDv7xCPyBpLFHZvvrtVYfRv2zEBtnT5') +``` + + diff --git a/packages/wallet-lib/docs/wallet/fromHDPublicKey.md b/packages/wallet-lib/docs/wallet/fromHDPublicKey.md new file mode 100644 index 00000000000..5b88cc203fa --- /dev/null +++ b/packages/wallet-lib/docs/wallet/fromHDPublicKey.md @@ -0,0 +1,19 @@ +**Usage**: `wallet.fromHDPublicKey(HDPublicKey)` +**Description**: Initialize a Wallet from a HDPublicKey representation. +**Notes**: This is an internal method, in the future, when TC39 proposal pass, we will use the private markup. + +Parameters: + +| parameters | type | required | Description | +|------------------------|------------------------|----------------| --------------------------------------------------------------------| +| **HDPublicKey** | HDPublicKey|String | yes | The HDPublicKey from which you want to initialize the wallet. | + +Returns : void (set a HD Wallet in watch mode). + +Examples : + +```js +wallet.fromHDPrivateKey('tpubDEB6BgW9JvZRWVbFmwwGuJ2vifakABuxQWdY9yXbFC2rc3zagie1RkhwUEnahb1dzaapchEVeKqKcx99TzkjNvjXcmoQkLJwsYnA1J5bGNj') +``` + + diff --git a/packages/wallet-lib/docs/wallet/fromMnemonic.md b/packages/wallet-lib/docs/wallet/fromMnemonic.md new file mode 100644 index 00000000000..9d16dfcbece --- /dev/null +++ b/packages/wallet-lib/docs/wallet/fromMnemonic.md @@ -0,0 +1,19 @@ +**Usage**: `wallet.fromMnemonic(mnemonic)` +**Description**: Initialize a Wallet from a Mnemonic representation. +**Notes**: This is an internal method, in the future, when TC39 proposal pass, we will use the private markup. Mnemonic initialized wallet works a little differently as they store the mnemonic in the wallet object. + +Parameters: + +| parameters | type | required | Description | +|------------------------|------------------------|----------------| --------------------------------------------------------------------| +| **mnemonic** | Mnemonic|String | yes | The Mnemonic from which you want to initialize the wallet. | + +Returns : void (set a HD Wallet). + +Examples : + +```js +wallet.fromMnemonic('knife easily prosper input concert merge prepare autumn pen blood glance toilet') +``` + + diff --git a/packages/wallet-lib/docs/wallet/fromPrivateKey.md b/packages/wallet-lib/docs/wallet/fromPrivateKey.md new file mode 100644 index 00000000000..e3a738438f2 --- /dev/null +++ b/packages/wallet-lib/docs/wallet/fromPrivateKey.md @@ -0,0 +1,19 @@ +**Usage**: `wallet.fromPrivateKey(privateKey)` +**Description**: Initialize a Wallet from a PrivateKey representation. +**Notes**: This is an internal method, in the future, when TC39 proposal pass, we will use the private markup. + +Parameters: + +| parameters | type | required | Description | +|------------------------|------------------------|----------------| --------------------------------------------------------------------| +| **PrivateKey** | PrivateKey|String | yes | The PrivateKey from which you want to initialize the wallet. | + +Returns : void (set a single address wallet). + +Examples : + +```js +wallet.fromPrivateKey('cR4t6evwVZoCp1JsLk4wURK4UmBCZzZotNzn9T1mhBT19SH9JtNt') +``` + + diff --git a/packages/wallet-lib/docs/wallet/fromSeed.md b/packages/wallet-lib/docs/wallet/fromSeed.md new file mode 100644 index 00000000000..a76d3f1e2c5 --- /dev/null +++ b/packages/wallet-lib/docs/wallet/fromSeed.md @@ -0,0 +1,20 @@ +**Usage**: `wallet.fromSeed(seed)` +**Description**: Initialize a Wallet from a seed. +**Notes**: This is an internal method, in the future, when TC39 proposal pass, we will use the private markup. +**Notes 2**: This actually transform seed in HDPrivateKey and uses `wallet.fromHDPrivateKey()`. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-----------|----------------| --------------------------------------------------------------------| +| **seed** | String | yes | The seed from which you want to initialize the wallet. | + +Returns : void (set a HD wallet). + +Examples : + +```js +wallet.fromSeed('9e55e5cb0e2fe273600cf5af7d7760fe569121c320395f0233202b97445e54f577d5a706c49aa1f3f0993d2ff97e2e6d63e4ccd0b7e9d4c4f115ae58957a9114') +``` + + diff --git a/packages/wallet-lib/docs/wallet/generateNewWalletId.md b/packages/wallet-lib/docs/wallet/generateNewWalletId.md new file mode 100644 index 00000000000..461fbbfeada --- /dev/null +++ b/packages/wallet-lib/docs/wallet/generateNewWalletId.md @@ -0,0 +1,11 @@ +**Usage**: `wallet.generateNewWalletId()` +**Description**: Internally, each wallet has a WalletId attached. This tries to be deterministic by actually just be a substring of a double sha256 hash from the input. +**Notes**: This is an internal method, in the future, when TC39 proposal pass, we will use the private markup. Also, mutates Wallet.walletId. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-----------|----------------| ------------------------------------------------------------------------------- | + +Returns : String (wallet.walletId). + diff --git a/packages/wallet-lib/docs/wallet/getAccount.md b/packages/wallet-lib/docs/wallet/getAccount.md new file mode 100644 index 00000000000..8298260f1a4 --- /dev/null +++ b/packages/wallet-lib/docs/wallet/getAccount.md @@ -0,0 +1,13 @@ +**Usage**: `await wallet.getAccount([opts])` +**Description**: This method will get you the account specified by it's index. + +Parameters: + +| parameters | type | required | Description | +|------------------------|-----------|----------------| ------------------------------------------------------------------------------- | +| **opts.index** | number | no (default: 0)| The [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) index | + +Returns : Account. + +N.B: When `getAccount` is called on a never initialized account, you can pass-it any of [Account options](../account/Account.md), and the wallet will initiate it (.createAccount) for you with those passed params and returns you the account. + diff --git a/packages/wallet-lib/examples/adapters/JSONStore.js b/packages/wallet-lib/examples/adapters/JSONStore.js new file mode 100644 index 00000000000..89277c3c7ca --- /dev/null +++ b/packages/wallet-lib/examples/adapters/JSONStore.js @@ -0,0 +1,9 @@ +const logger = require('../../src/logger'); + +const JSONStorage = { + createInstance: () => ({ + setItem: (key, item) => logger.info('JSONStorage#setItem', { key, item }), + getItem: (key) => logger.info('JSONStorage#getItem', key), + }), +}; +module.exports = JSONStorage; diff --git a/packages/wallet-lib/examples/client-usage-single-privateKey.js b/packages/wallet-lib/examples/client-usage-single-privateKey.js new file mode 100644 index 00000000000..fe440157718 --- /dev/null +++ b/packages/wallet-lib/examples/client-usage-single-privateKey.js @@ -0,0 +1,21 @@ +const Wallet = require('../src/types/Wallet/Wallet'); + +const wallet = new Wallet({ + network: 'testnet', + privateKey: '9488797ee0018994d5ae22640e25210f65b1a450a0adcfa428cb5ffb29faa24b', +}); + +wallet + .getAccount() + .then(async (account) => { + const address = account.getUnusedAddress(); + + const balance = account.getTotalBalance(); + if (balance > 0) { + const rawtx = account.createTransaction({ + recipient: address, + satoshis: parseInt(balance / 4, 10), + }); + await account.broadcastTransaction(rawtx); + } + }); diff --git a/packages/wallet-lib/examples/client-usage.js b/packages/wallet-lib/examples/client-usage.js new file mode 100644 index 00000000000..9479b6ad64a --- /dev/null +++ b/packages/wallet-lib/examples/client-usage.js @@ -0,0 +1,30 @@ +/* eslint-disable no-console */ +const logger = require('../src/logger'); +const { Wallet, EVENTS } = require('../src'); + +const wallet = new Wallet({ + mnemonic: 'protect cave garden achieve hand vacant clarify atom finish outer waste sword', + network: 'testnet', +}); + +wallet + .getAccount() + .then(async (account) => { + logger.info('Balance Confirmed', await account.getConfirmedBalance(false)); + logger.info('Balance Unconfirmed', await account.getUnconfirmedBalance(false)); + logger.info('New Address', await account.getUnusedAddress().address); + + const transaction = account.createTransaction({ satoshis: 1000, recipient: 'ycyFFyWCPSWbXLZBeYppJqgvBF7bnu8BWQ' }); + const transactionID = await account.broadcastTransaction(transaction); + + logger.info(`Transaction ${transactionID} broadcast`); + + account.on(EVENTS.GENERATED_ADDRESS, () => logger.info('GENERATED_ADDRESS')); + account.on(EVENTS.CONFIRMED_BALANCE_CHANGED, (info) => logger.info('CONFIRMED_BALANCE_CHANGED', info)); + account.on(EVENTS.UNCONFIRMED_BALANCE_CHANGED, (info) => logger.info('UNCONFIRMED_BALANCE_CHANGED', info)); + account.on(EVENTS.BLOCKHEIGHT_CHANGED, (info) => logger.info('BLOCKHEIGHT_CHANGED:', info)); + account.on(EVENTS.PREFETCHED, () => logger.info('PREFETCHED', EVENTS.PREFETCHED)); + account.on(EVENTS.DISCOVERY_STARTED, () => logger.info(EVENTS.PREFETCHED)); + }).catch((e) => { + console.log('Failed with error', e); + }); diff --git a/packages/wallet-lib/examples/offline-wallet-signing-message.js b/packages/wallet-lib/examples/offline-wallet-signing-message.js new file mode 100644 index 00000000000..dd22c488356 --- /dev/null +++ b/packages/wallet-lib/examples/offline-wallet-signing-message.js @@ -0,0 +1,22 @@ +const { Message } = require('@dashevo/dashcore-lib'); +const { Wallet } = require('../src/index'); + +const mnemonic = 'never citizen worry shrimp used wild color snack undo armed scout chief'; +const walletOpts = { + offlineMode: true, + network: 'testnet', + mnemonic, +}; +const wallet = new Wallet(walletOpts); +wallet.getAccount() + .then((account) => { + const startSigningMessage = () => { + const message = new Message('Hello world!'); + + const idPrivateKey = account.identities.getIdentityHDKeyByIndex(0, 0).privateKey; + + const signed = account.sign(message, idPrivateKey); + message.verify(idPrivateKey.toAddress().toString(), signed.toString()); // true + }; + startSigningMessage(); + }); diff --git a/packages/wallet-lib/examples/offline-wallet.js b/packages/wallet-lib/examples/offline-wallet.js new file mode 100644 index 00000000000..4f381e8e740 --- /dev/null +++ b/packages/wallet-lib/examples/offline-wallet.js @@ -0,0 +1,43 @@ +const { Wallet } = require('../src/index'); +const logger = require('../src/logger'); + +const mnemonic = 'never citizen worry shrimp used wild color snack undo armed scout chief'; +const walletOpts = { + offlineMode: true, + network: 'testnet', + mnemonic, + // HDPublicKey: +}; +const wallet = new Wallet(walletOpts); +wallet.getAccount({ index: 0 }).then((account) => { + /** + * Simple offline service that simulates an offline usage + * which generates new addresses from an ExtendedPubKey. + */ + const startService = () => { + // Import a tx that happened in the network + // See for the format + const addresses = {}; + account.storage.importAddresses(addresses, account.walletId); + + // Get any specific address + const specific = account.getAddress(100); + logger.info('Specific', specific); + + // Generate a batch of all 200 first addreses + const poolAddresses = []; + for (let i = 0; i <= 200; i += 1) { + poolAddresses.push(account.getAddress(i).address); + } + logger.info('Pregenerated pool of addr', poolAddresses); + + const addrPool = []; + // get 10 unused address + for (let i = 0; i < 10; i += 1) { + const skip = i; + addrPool.push(account.getUnusedAddress('external', skip)); + } + logger.info('Pool of unused addr', addrPool); + }; + startService(); +}); diff --git a/packages/wallet-lib/examples/stdPlugins/WalletConsolidator.js b/packages/wallet-lib/examples/stdPlugins/WalletConsolidator.js new file mode 100644 index 00000000000..41f77301eb0 --- /dev/null +++ b/packages/wallet-lib/examples/stdPlugins/WalletConsolidator.js @@ -0,0 +1,46 @@ +const { StandardPlugin } = require('../../src/plugins/index'); +const logger = require('../../src/logger'); + +class WalletConsolidator extends StandardPlugin { + constructor() { + super({ + // When true, the wallet object will only fire "ready" + firstExecutionRequired: false, + // Describe if we execute it first on startup of an account. + executeOnStart: false, + // Methods and function that we would want to use + dependencies: [ + 'getUTXOS', + 'getUnusedAddress', + 'getConfirmedBalance', + 'createTransactionFromUTXOS', + 'broadcastTransaction', + ], + }); + } + + consolidateWallet(address = this.getUnusedAddress().address, utxos = this.getUTXOS()) { + return { + prepareTransaction: () => { + if (!utxos || utxos.length === 0) { + throw new Error('There is nothing to consolidate'); + } + const opts = { + utxos, + recipient: address, + }; + + const rawtx = this.createTransactionFromUTXOS(opts); + return { + toString: () => rawtx, + broadcast: async () => { + logger.info('TRIED TO BROADCAST', rawtx); + return rawtx; + // return self.broadcastTransaction(rawtx); + }, + }; + }, + }; + } +} +module.exports = WalletConsolidator; diff --git a/packages/wallet-lib/examples/wallet-fromHDPubKey.js b/packages/wallet-lib/examples/wallet-fromHDPubKey.js new file mode 100644 index 00000000000..73bb6e1359a --- /dev/null +++ b/packages/wallet-lib/examples/wallet-fromHDPubKey.js @@ -0,0 +1,21 @@ +const logger = require('../src/logger'); +const { Wallet, EVENTS } = require('../src'); + +const wallet = new Wallet({ + HDPublicKey: 'tpubDFLd6pf4VJ72YFdAVp5sXWiszJhGM4yhXnAYkXagFf2fS3NiTU6rwQsxkMiVPKqeGBTWC2DZ8ZicuT49jnKwMEr6gAT4f83YqB3dnujarD3', + network: 'testnet', +}); + +wallet.getAccount() + .then(async (account) => { + logger.info('Balance Confirmed', await account.getConfirmedBalance(false)); + logger.info('Balance Unconfirmed', await account.getUnconfirmedBalance(false)); + logger.info('New Address', await account.getUnusedAddress().address); + + account.on(EVENTS.GENERATED_ADDRESS, () => logger.info('GENERATED_ADDRESS')); + account.on(EVENTS.CONFIRMED_BALANCE_CHANGED, (info) => logger.info('CONFIRMED_BALANCE_CHANGED', info)); + account.on(EVENTS.UNCONFIRMED_BALANCE_CHANGED, (info) => logger.info('UNCONFIRMED_BALANCE_CHANGED', info)); + account.on(EVENTS.BLOCKHEIGHT_CHANGED, (info) => logger.info('BLOCKHEIGHT_CHANGED:', info)); + account.on(EVENTS.PREFETCHED, () => logger.info('EVENTS_PREFETCHED', EVENTS.PREFETCHED)); + account.on(EVENTS.DISCOVERY_STARTED, () => logger.info('EVENTS_DISCOVERY_STARTED', EVENTS.PREFETCHED)); + }); diff --git a/packages/wallet-lib/examples/wallet-plugins.js b/packages/wallet-lib/examples/wallet-plugins.js new file mode 100644 index 00000000000..5e17efe12ad --- /dev/null +++ b/packages/wallet-lib/examples/wallet-plugins.js @@ -0,0 +1,38 @@ +const { Wallet } = require('../src'); +const logger = require('../src/logger'); +// This is a ColdStorage worker. It ran each X, verify a condition (execute function), and +const ColdStorageWorker = require('./workers/ColdStorageWorker'); + +// Wallet Consolidator is a standard plugin, when added it will offer new +// functionalities to the account. Such as 'consolidateWallet' method. +const WalletConsolidator = require('./stdPlugins/WalletConsolidator'); + +// This will be used by the coldStorageWorker which is responsible for performing cold-storage on +// this address. +const coldStorageAddress = 'yb67GKjkk4AMrJcqoedCjeemFGo9bDovNS'; + +const wallet = new Wallet({ + mode: 'light', + injectDefaultPlugins: false, // Will not inject default plugins (BIP44, SyncWorker) + // Will add these plugin instead, one is already init to show that both are fine to used. + // The order has it's importance, here ColdStorageWorker will use WalletConsolidator as a depts. + plugins: [WalletConsolidator, new ColdStorageWorker({ address: coldStorageAddress })], +}); + +wallet.getAccount({ index: 0 }) + .then((account) => { + const showcasePlugin = async () => { + const walletConsolidator = account.getPlugin('walletConsolidator'); + const consolidate = await walletConsolidator.consolidateWallet(); + + const preparedTransaction = consolidate.prepareTransaction(); + + logger.info('RawTx', preparedTransaction.toString()); + logger.info('Broadcast', await preparedTransaction.broadcast()); + }; + + logger.info('Balance', account.getTotalBalance()); + logger.info('Funding address', account.getUnusedAddress().address); + + return showcasePlugin(); + }); diff --git a/packages/wallet-lib/examples/web/usage.web.html b/packages/wallet-lib/examples/web/usage.web.html new file mode 100644 index 00000000000..daf978b4219 --- /dev/null +++ b/packages/wallet-lib/examples/web/usage.web.html @@ -0,0 +1,11 @@ + + + + + Title + + + + + + diff --git a/packages/wallet-lib/examples/workers/ColdStorageWorker.js b/packages/wallet-lib/examples/workers/ColdStorageWorker.js new file mode 100644 index 00000000000..95cb253a1da --- /dev/null +++ b/packages/wallet-lib/examples/workers/ColdStorageWorker.js @@ -0,0 +1,41 @@ +const { Worker } = require('../../src/plugins'); +const logger = require('../../src/logger'); + +class ColdStorageWorker extends Worker { + constructor(props) { + super({ + executeOnStart: true, + workerIntervalTime: 6 * 60 * 60 * 1000, + dependencies: [ + 'walletConsolidator', + 'getUTXOS', + 'getConfirmedBalance', + ], + }); + if (!props.address) { + return new Error('ColdStorageWorker expect an address'); + } + this.address = props.address; + } + + execute() { + const { walletConsolidator } = this; + const utxos = this.getUTXOS(); + if (utxos.length === 0) { + throw new Error('ColdStorageWorker : We did not found any utxos. Doing nothing'); + } else { + const balance = this.getConfirmedBalance(); + logger.info('Found inputs to move'); + const consolidate = walletConsolidator.consolidateWallet(this.address, utxos); + const preparedTransaction = consolidate.prepareTransaction(); + const rawTx = preparedTransaction.toString(); + preparedTransaction + .broadcast() + .then((txid) => { + logger.info('Worker has moved ', balance, 'txid:', txid, 'rawTx:', rawTx); + }); + } + logger.info('Next execution in 6 hours.'); + } +} +module.exports = ColdStorageWorker; diff --git a/packages/wallet-lib/examples/workers/HelloWorldWorker.js b/packages/wallet-lib/examples/workers/HelloWorldWorker.js new file mode 100644 index 00000000000..ee34fc266b7 --- /dev/null +++ b/packages/wallet-lib/examples/workers/HelloWorldWorker.js @@ -0,0 +1,19 @@ +/* eslint-disable no-console */ +const { Worker } = require('../../src/plugins'); + +class HelloWorldWorker extends Worker { + constructor() { + // noinspection PointlessArithmeticExpressionJS + super({ + executeOnStart: true, + firstExecutionRequired: true, + workerIntervalTime: 1 * 60 * 1000, + }); + } + + // eslint-disable-next-line class-methods-use-this + execute() { + console.log('HELLO WORLD'); + } +} +module.exports = HelloWorldWorker; diff --git a/packages/wallet-lib/fixtures/DummyWorker.js b/packages/wallet-lib/fixtures/DummyWorker.js new file mode 100644 index 00000000000..e5c4c054892 --- /dev/null +++ b/packages/wallet-lib/fixtures/DummyWorker.js @@ -0,0 +1,18 @@ +const Worker = require('../src/plugins/Worker'); + +class DummyWorker extends Worker { + constructor() { + super({ + name: 'DummyWorker', + dependencies: [], + executeOnStart: true, + workerIntervalTime: 50 * 1000, + }); + } + + // eslint-disable-next-line class-methods-use-this + execute() { + console.log('Dummy worker successfully did nothing'); + } +} +module.exports = DummyWorker; diff --git a/packages/wallet-lib/fixtures/addresses.json b/packages/wallet-lib/fixtures/addresses.json new file mode 100644 index 00000000000..4fc85dfed84 --- /dev/null +++ b/packages/wallet-lib/fixtures/addresses.json @@ -0,0 +1,9 @@ +{ + "testnet": { + "valid": { + "yereyozxENB9jbhqpbg1coE5c39ExqLSaG":{ + "addr": "yereyozxENB9jbhqpbg1coE5c39ExqLSaG" + } + } + } +} \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/cR4t6e_pk.json b/packages/wallet-lib/fixtures/cR4t6e_pk.json new file mode 100644 index 00000000000..40286a726b3 --- /dev/null +++ b/packages/wallet-lib/fixtures/cR4t6e_pk.json @@ -0,0 +1,4 @@ +{ + "privateKey": "cR4t6evwVZoCp1JsLk4wURK4UmBCZzZotNzn9T1mhBT19SH9JtNt", + "walletIdTestnet":"bd0858f420" +} \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/chains/testnet/blockheaders.json b/packages/wallet-lib/fixtures/chains/testnet/blockheaders.json new file mode 100644 index 00000000000..d58e0885f60 --- /dev/null +++ b/packages/wallet-lib/fixtures/chains/testnet/blockheaders.json @@ -0,0 +1,12 @@ +[ + { + "height": 600000, + "blockheader": "00000020a69faca490012f4371e4bd40a40167809e6fe1b03c9c5dcb7df247f2fa000000b4b8c2d4a78fb35e50a4eda0bd3a5b9b32f4d739b28a4266f24775b3b79d5b61c73275610e04021ea74d0000", + "hash":"000000de786e659950e0f27681faf1a91871d15de264d0b769cb5941c1d807c3" + }, + { + "height": 610000, + "blockheader": "00000020af5155e1f8d5b60fb247ac9bb8badbdce1fa72302336c7daab53585caf0000006d495b9629989ac3699d528956b1f617fb51a199ef4277c3f45f4606aba8e461d1c78a61210b011e94ef0000", + "hash":"00000094d124cfb68d6d59ffaec9f7d63965cb894855684e23a586274b49708f" + } +] \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/chains/testnet/transactions.json b/packages/wallet-lib/fixtures/chains/testnet/transactions.json new file mode 100644 index 00000000000..5fd376a7a61 --- /dev/null +++ b/packages/wallet-lib/fixtures/chains/testnet/transactions.json @@ -0,0 +1,14 @@ +[ + { + "blockHash": "000000b2537d9b3468e02dc6f49fb8bbb5c9d8b77895df1e76816fbad5555d7d", + "transaction": "02000000019583d226737edfad682cc1f0297f688bfca4c0046062ed3e88fa47dd47eab2b2010000006a473044022021550232cc659fa412deb2ea98956faf9ff0efea3a04029bb63022f6809105ce0220230a3f955a3ee7563f5b006342d8db940f3b29f4e312701bf51720575c7a7a8a012103f3d2b2ddfe6ffad2b8fc1708f2eafadfed36bf31a05dfa04b1fc176526475b0ffeffffff02a003d806000000001976a91464220a1c12690ec26d837b3be0a2e3588bb4b79188ac4c5bc92b250000001976a9143e89746b9aa52703ab784bc0df467b160406ffb988acb56b0900", + "height": 617398, + "isInstantLocked": true, + "isChainLocked": true + }, + { + "transaction": "0200000001c7d66bb85e0069c221b44b07f49f52cc4f2e54f70e14430b94888327763a66a9010000006b483045022100da5b319f73e6adfee751f33308f5a8c1fceeab2683e15e132d79053b3118639602204262022fb85f88d9802649a289a1134b678efcf708faaeae8f101e8eab785054012102bc626898b49f31f5194de7bc68004401639a20cfa82e4c2eac9684a91fc47a57feffffff0270f2f605000000001976a91464220a1c12690ec26d837b3be0a2e3588bb4b79188ac912e250c250000001976a91415e1edb5c5d9e67d0e36f94343b3eff26bb76d1088ac266e0900", + "blockHash":"0000005c81a683007e86e75c76b4b2feca229f806702ca92953562f2ae628ce7", + "height": 618023 + } +] \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/crackspice.json b/packages/wallet-lib/fixtures/crackspice.json new file mode 100644 index 00000000000..6ef004df029 --- /dev/null +++ b/packages/wallet-lib/fixtures/crackspice.json @@ -0,0 +1,293 @@ +{ + "mnemonic": "crack spice venue ticket vacant steak next stomach amateur review okay curtain", + "utxosList": [ + { + "address": "yNgqjoW69ouSivtBMNFRCG5zSG85nyxW3d", + "txid": "36820d7268090d6f315eef03b28b7b2b2097c8b067608f652612a2c4612a6697", + "outputIndex": 1, + "script": "76a91419fc1815a04c42a849a7a6dda826c67478514fed88ac", + "amount": 9.9999, + "satoshis": 999990000, + "height": 203208 + }, + { + "address": "yPWVEG3mW8pFdPCXcE53gN1fSTM8dkV7kF", + "txid": "2911362650f08df1ea16e03973bb41e1ee33680cce2ec6ce864e2daf35431e08", + "outputIndex": 1, + "script": "76a91422fef09d745700a159553dd42227895053d33e6888ac", + "amount": 8.4999, + "satoshis": 849990000, + "height": 203251 + }, + { + "address": "yPn5VvPk7ioN9emDv3MkCKovpjNqSLwW1p", + "txid": "96eb6c951d69a3b8703673ca0d588cf6cee528f866fc598e84205ddcc34ea100", + "outputIndex": 1, + "script": "76a91425f1c9581cd2a9976e6ace867f8e895663e6825a88ac", + "amount": 6.9998, + "satoshis": 699980000, + "height": 201738 + }, + { + "address": "yb34xdJMT2mCrJdkawEti7ZnoYXZ5rpBUJ", + "txid": "e092395f069fbc62e4e88df6a962833a26ffb6f8f6fe984c70e23a47d406ac89", + "outputIndex": 1, + "script": "76a914a170f58a73fd56a8cf3e36015df98078d37e842488ac", + "amount": 5, + "satoshis": 500000000, + "height": 201382 + }, + { + "address": "yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42", + "txid": "5b14eea2e1e07f94fbce22b50b6cda6b748a66c1119524a623c6820b75bbc7ca", + "outputIndex": 0, + "script": "76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac", + "amount": 5, + "satoshis": 500000000, + "height": 203286 + }, + { + "address": "yYNZYgZrCVHQkJ4sPbmegb768zLaoAtREb", + "txid": "deceb521c45d78cfd85bfb2462595a39e10da232768fb61295229923bf265c2a", + "outputIndex": 1, + "script": "76a914843859336f31e96025afc658bf152fb0b0bb751188ac", + "amount": 4, + "satoshis": 400000000, + "height": 203313 + }, + { + "address": "yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42", + "txid": "2bf25390be738308827348711da2700918b73096bfaff99de6c9c60121fa5d8e", + "outputIndex": 0, + "script": "76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac", + "amount": 2, + "satoshis": 200000000, + "height": 203268 + }, + { + "address": "yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42", + "txid": "dd02316f28e6d04f1f6f998c30f367dee4dc820309a6cd3cdfc436dc63254c50", + "outputIndex": 1, + "script": "76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac", + "amount": 2, + "satoshis": 200000000, + "height": 203276 + }, + { + "address": "yMfDnWF6piqNA7mbSeEeAP4LiiqgxkJvNL", + "txid": "96eb6c951d69a3b8703673ca0d588cf6cee528f866fc598e84205ddcc34ea100", + "outputIndex": 0, + "script": "76a9140eb58a39a96968c19411568752ecdecf55dabb8588ac", + "amount": 2, + "satoshis": 200000000, + "height": 201738 + }, + { + "address": "yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX", + "txid": "5c462466bea61ff28e7805d20b482d83a139ea300a76052921038a22705e6937", + "outputIndex": 0, + "script": "76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac", + "amount": 2, + "satoshis": 200000000, + "height": 203198 + }, + { + "address": "yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42", + "txid": "8053a30d671b62e56a4a61d1fe2f899917cd20278e474a433e8d88d140757e0e", + "outputIndex": 1, + "script": "76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac", + "amount": 2, + "satoshis": 200000000, + "height": 203288 + }, + { + "address": "ygewiYb7ZJxU4uuNGEVzbbA3wZEpEQJKhr", + "txid": "9ab39713e9ce713d41ca6974db83e57bced02402e9516b8a662ed60d5c08f6d1", + "outputIndex": 0, + "script": "76a914df128447b46f9c81edbf13494d12aabca066b65688ac", + "amount": 2, + "satoshis": 200000000, + "height": 201436 + }, + { + "address": "yYNZYgZrCVHQkJ4sPbmegb768zLaoAtREb", + "txid": "eef0ebeaa4d934e2b68a32f3240c5a4dbc02f9a0d7f75e6a51bd74410bdc093c", + "outputIndex": 1, + "script": "76a914843859336f31e96025afc658bf152fb0b0bb751188ac", + "amount": 1.5, + "satoshis": 150000000, + "height": 203291 + }, + { + "address": "yZruigeCbPHVRnJG9JcSyG9AhX7PSF9oi7", + "txid": "2911362650f08df1ea16e03973bb41e1ee33680cce2ec6ce864e2daf35431e08", + "outputIndex": 0, + "script": "76a914948cf5d360500a04d0a9080eac8514b79c1297b288ac", + "amount": 1.5, + "satoshis": 150000000, + "height": 203251 + }, + { + "address": "yb34xdJMT2mCrJdkawEti7ZnoYXZ5rpBUJ", + "txid": "e4524e918977b70ab47160d8e3b87a5fa9f88f22e43f0eec2abbee2cf364c93b", + "outputIndex": 0, + "script": "76a914a170f58a73fd56a8cf3e36015df98078d37e842488ac", + "amount": 1, + "satoshis": 100000000, + "height": 201425 + }, + { + "address": "yYNZYgZrCVHQkJ4sPbmegb768zLaoAtREb", + "txid": "b42c5052d7d31a422e711d50d3754217b0b16b6dfa29cf497b3dd75afa4febcb", + "outputIndex": 0, + "script": "76a914843859336f31e96025afc658bf152fb0b0bb751188ac", + "amount": 1, + "satoshis": 100000000, + "height": 203313 + }, + { + "address": "yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX", + "txid": "0c98713b9895cf6c48f15aa717561f78339b9701f927c057758cb617f671cbfd", + "outputIndex": 0, + "script": "76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac", + "amount": 1, + "satoshis": 100000000, + "height": 203265 + }, + { + "address": "yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX", + "txid": "bdd9a949b13d67b41fc9895325977c058f472a29aea6b084ae6f6504d17e02cf", + "outputIndex": 0, + "script": "76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac", + "amount": 1, + "satoshis": 100000000, + "height": 203207 + }, + { + "address": "yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42", + "txid": "b452f2d7762b5cd94a0d375e60547c93035b97978a37bcaeed186d27e31feb3a", + "outputIndex": 0, + "script": "76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac", + "amount": 1, + "satoshis": 100000000, + "height": 203290 + }, + { + "address": "yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX", + "txid": "7a6578995dd6eb11f0ec08e61135363fab55c0732ac05f563088b864d62f8cd4", + "outputIndex": 1, + "script": "76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac", + "amount": 1, + "satoshis": 100000000, + "height": 203266 + }, + { + "address": "yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX", + "txid": "071502a8b211e08f575641f3345b687a86c922108b5fd608822bffe0151aaf09", + "outputIndex": 1, + "script": "76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac", + "amount": 1, + "satoshis": 100000000, + "height": 203268 + }, + { + "address": "yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42", + "txid": "f28ccc69a1411e0bc36802f2c70aba617f2483483a85542220fdce2eaf0d5dcb", + "outputIndex": 0, + "script": "76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac", + "amount": 1, + "satoshis": 100000000, + "height": 203281 + }, + { + "address": "yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX", + "txid": "5b6efaffbcf24b613ce29e18263203e05406f3fc130377eac02d579964672d67", + "outputIndex": 1, + "script": "76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac", + "amount": 1, + "satoshis": 100000000, + "height": 203207 + }, + { + "address": "yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42", + "txid": "6c42619dd84a02577458ba4f880fe8cfaced9ed518ee7c360c5b107d6ff5b62d", + "outputIndex": 0, + "script": "76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac", + "amount": 1, + "satoshis": 100000000, + "height": 203277 + }, + { + "address": "yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42", + "txid": "157a4869ac5de33f40812f1e50e50395b472f991a72e59170037671914e72b0d", + "outputIndex": 1, + "script": "76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac", + "amount": 1, + "satoshis": 100000000, + "height": 203277 + }, + { + "address": "yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42", + "txid": "1fe685297c8c188a440affdda538ef5c757399051965352157c7e1495e6038f0", + "outputIndex": 1, + "script": "76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac", + "amount": 1, + "satoshis": 100000000, + "height": 203276 + }, + { + "address": "yN4UthSmkxHL8LJJjKCj6YauPmxfQWLtXS", + "txid": "fd58a07200542a3170e05dbacb3ae80f9899e3a2a0a88308cbda81579f84ed0d", + "outputIndex": 0, + "script": "76a914131bb7b273f0c1256bf0fb96272caefed2e56b7588ac", + "amount": 1, + "satoshis": 100000000, + "height": 201637 + }, + { + "address": "yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42", + "txid": "1d90ba700b8fa18c8d9a6d3eaa505dde99a4a459c0d1e73bf40ba4b2cc2461cc", + "outputIndex": 0, + "script": "76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac", + "amount": 1, + "satoshis": 100000000, + "height": 203268 + }, + { + "address": "yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42", + "txid": "d928aedc4ecc6c251cabee0672c19308573e5b4898c32779f3fd211dd8a1fbd8", + "outputIndex": 1, + "script": "76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac", + "amount": 1, + "satoshis": 100000000, + "height": 203279 + }, + { + "address": "yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX", + "txid": "1240c9e3bba3f143ec354bd37e4b860609b944dee2e426e9868e5c3244e47f04", + "outputIndex": 1, + "script": "76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac", + "amount": 0.8, + "satoshis": 80000000, + "height": 203207 + }, + { + "address": "yMfDnWF6piqNA7mbSeEeAP4LiiqgxkJvNL", + "txid": "22c368e09ad8b36553b383c6a4ae989f91d1f66622b2b685262580c8a45175a4", + "outputIndex": 1, + "script": "76a9140eb58a39a96968c19411568752ecdecf55dabb8588ac", + "amount": 0.5, + "satoshis": 50000000, + "height": 203155 + }, + { + "address": "yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX", + "txid": "c00b418305222973330bb60750a9d948a02f3dd480e82fadc390382aa4939218", + "outputIndex": 1, + "script": "76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac", + "amount": 0.2, + "satoshis": 20000000, + "height": 203199 + } + ] +} diff --git a/packages/wallet-lib/fixtures/da07-fullstore-snapshot-1548533266.json b/packages/wallet-lib/fixtures/da07-fullstore-snapshot-1548533266.json new file mode 100644 index 00000000000..8b1ad2b8163 --- /dev/null +++ b/packages/wallet-lib/fixtures/da07-fullstore-snapshot-1548533266.json @@ -0,0 +1,300 @@ +{ + "wallets": { + "f60f493200": { + "accounts": { + "0": { + "label": null, + "path": "0", + "network": { + "name": "testnet", + "alias": "regtest", + "pubkeyhash": 140, + "privatekey": 239, + "scripthash": 19, + "xpubkey": 70617039, + "xprivkey": 70615956, + "port": 19999, + "networkMagic": { + "type": "Buffer", + "data": [ + 206, + 226, + 202, + 255 + ] + }, + "dnsSeeds": [ + "testnet-seed.darkcoin.io", + "testnet-seed.dashdot.io", + "test.dnsseed.masternode.io" + ] + } + } + }, + "network": { + "name": "testnet", + "alias": "regtest", + "pubkeyhash": 140, + "privatekey": 239, + "scripthash": 19, + "xpubkey": 70617039, + "xprivkey": 70615956, + "port": 19999, + "networkMagic": { + "type": "Buffer", + "data": [ + 206, + 226, + 202, + 255 + ] + }, + "dnsSeeds": [ + "testnet-seed.darkcoin.io", + "testnet-seed.dashdot.io", + "test.dnsseed.masternode.io" + ] + }, + "mnemonic": null, + "type": null, + "blockheight": 0, + "addresses": { + "external": {}, + "internal": {}, + "misc": { + "0": { + "address": "ygpAb9QawEwL6kej4u3r94gC4tfoLZpaLZ", + "path": "0", + "balanceSat": 49999999506, + "unconfirmedBalanceSat": 0, + "transactions": [ + "2d1cce77517e8411c9c9548884029edabbbd11ca3d13d6e11acdd90a79bb4408", + "4493e8a39bb97d6709ca69d391b0b99f573d3aed5ce50da5f8c7626fc1cb1a7d", + "f59ea94b2edf9b42e97027cc528b10d4874ce9ff604f095072e924611463053e" + ], + "fetchedLast": 1548533508688, + "used": true, + "utxos": { + "2d1cce77517e8411c9c9548884029edabbbd11ca3d13d6e11acdd90a79bb4408": { + "txid": "2d1cce77517e8411c9c9548884029edabbbd11ca3d13d6e11acdd90a79bb4408", + "outputIndex": 0, + "satoshis": 49900000000, + "scriptPubKey": "76a914e0d1017780c580d7beb2e06d93a95f77c105ef1a88ac" + } + } + } + } + } + } + }, + "transactions": { + "2d1cce77517e8411c9c9548884029edabbbd11ca3d13d6e11acdd90a79bb4408": { + "txid": "2d1cce77517e8411c9c9548884029edabbbd11ca3d13d6e11acdd90a79bb4408", + "blockhash": "000000dd9a35951470124d88a0c87a5c9f2c0f2a1f31b00643b4a228624189c5", + "blockheight": 6310, + "blocktime": 1548533266, + "fees": 247, + "size": 225, + "vout": [ + { + "value": "499.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914e0d1017780c580d7beb2e06d93a95f77c105ef1a88ac", + "asm": "OP_DUP OP_HASH160 e0d1017780c580d7beb2e06d93a95f77c105ef1a OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ygpAb9QawEwL6kej4u3r94gC4tfoLZpaLZ" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.99999506", + "n": 1, + "scriptPubKey": { + "hex": "76a914e0d1017780c580d7beb2e06d93a95f77c105ef1a88ac", + "asm": "OP_DUP OP_HASH160 e0d1017780c580d7beb2e06d93a95f77c105ef1a OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ygpAb9QawEwL6kej4u3r94gC4tfoLZpaLZ" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "txid": "4493e8a39bb97d6709ca69d391b0b99f573d3aed5ce50da5f8c7626fc1cb1a7d", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "473044022021b301971a3dee4dbc9ba3a73313ec260c847457b2988d6c0e24063c6017eff702205851b27349c9910efc4639278a879ae2a61908a10a7a9666009cf274c95272c001210247ea333d3bda2261442e8a5851b4d96a1c9bc029712d52f575fa8aa31fa76f10", + "asm": "3044022021b301971a3dee4dbc9ba3a73313ec260c847457b2988d6c0e24063c6017eff702205851b27349c9910efc4639278a879ae2a61908a10a7a9666009cf274c95272c0[ALL] 0247ea333d3bda2261442e8a5851b4d96a1c9bc029712d52f575fa8aa31fa76f10" + }, + "addr": "ygpAb9QawEwL6kej4u3r94gC4tfoLZpaLZ", + "valueSat": 49999999753, + "value": 499.99999753, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "4493e8a39bb97d6709ca69d391b0b99f573d3aed5ce50da5f8c7626fc1cb1a7d": { + "txid": "4493e8a39bb97d6709ca69d391b0b99f573d3aed5ce50da5f8c7626fc1cb1a7d", + "blockhash": "000002c283d7d55eb6a7d1939b9c428242d09869d6b456f82e1d0df3fdd83dec", + "blockheight": 6121, + "blocktime": 1548506741, + "fees": 247, + "size": 226, + "vout": [ + { + "value": "500.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a91419ba12c566e1e21deafa60f2a6de42d15f85dbbc88ac", + "asm": "OP_DUP OP_HASH160 19ba12c566e1e21deafa60f2a6de42d15f85dbbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "499.99999753", + "n": 1, + "scriptPubKey": { + "hex": "76a914e0d1017780c580d7beb2e06d93a95f77c105ef1a88ac", + "asm": "OP_DUP OP_HASH160 e0d1017780c580d7beb2e06d93a95f77c105ef1a OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ygpAb9QawEwL6kej4u3r94gC4tfoLZpaLZ" + ], + "type": "pubkeyhash" + }, + "spentTxId": "2d1cce77517e8411c9c9548884029edabbbd11ca3d13d6e11acdd90a79bb4408", + "spentIndex": 0, + "spentHeight": 6310 + } + ], + "vin": [ + { + "txid": "f59ea94b2edf9b42e97027cc528b10d4874ce9ff604f095072e924611463053e", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "4830450221008078d0922828a868040c9576ea6c58b166764d1a4195a6e9f14b8b489baab8d002201a2a3554a76d42c4f0a922228a90945a07af26a121373130926e4face562d6e901210247ea333d3bda2261442e8a5851b4d96a1c9bc029712d52f575fa8aa31fa76f10", + "asm": "30450221008078d0922828a868040c9576ea6c58b166764d1a4195a6e9f14b8b489baab8d002201a2a3554a76d42c4f0a922228a90945a07af26a121373130926e4face562d6e9[ALL] 0247ea333d3bda2261442e8a5851b4d96a1c9bc029712d52f575fa8aa31fa76f10" + }, + "addr": "ygpAb9QawEwL6kej4u3r94gC4tfoLZpaLZ", + "valueSat": 100000000000, + "value": 1000, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "f59ea94b2edf9b42e97027cc528b10d4874ce9ff604f095072e924611463053e": { + "txid": "f59ea94b2edf9b42e97027cc528b10d4874ce9ff604f095072e924611463053e", + "blockhash": "000000e0d3c67f9aceb3d91f812dbdc879d0c6ee70e86b67fa3e283af34b6f89", + "blockheight": 6119, + "blocktime": 1548505964, + "fees": 522, + "size": 519, + "vout": [ + { + "value": "2.99998060", + "n": 0, + "scriptPubKey": { + "hex": "76a914e4a02c346887cbf8d7f821588fe1b41e80eff8d288ac", + "asm": "OP_DUP OP_HASH160 e4a02c346887cbf8d7f821588fe1b41e80eff8d2 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yhAJnw4eDwJWBJR7CKwmwrysGWxyVPFPEz" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "1000.00000000", + "n": 1, + "scriptPubKey": { + "hex": "76a914e0d1017780c580d7beb2e06d93a95f77c105ef1a88ac", + "asm": "OP_DUP OP_HASH160 e0d1017780c580d7beb2e06d93a95f77c105ef1a OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ygpAb9QawEwL6kej4u3r94gC4tfoLZpaLZ" + ], + "type": "pubkeyhash" + }, + "spentTxId": "4493e8a39bb97d6709ca69d391b0b99f573d3aed5ce50da5f8c7626fc1cb1a7d", + "spentIndex": 0, + "spentHeight": 6121 + } + ], + "vin": [ + { + "txid": "467fd032162f50a8354255e272490828eef8b6c2c1ad190b09452ff3b7d1a03f", + "vout": 1, + "sequence": 4294967294, + "n": 0, + "scriptSig": { + "hex": "473044022057a7ed74505767eafcb7ad24616dc26d6c39133493e703527a9bc38300b7d3eb02206cbd82e1975b605c0bb1a22112a90d129a0ef1b5844584f5c3e0e39043ec9976012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844", + "asm": "3044022057a7ed74505767eafcb7ad24616dc26d6c39133493e703527a9bc38300b7d3eb02206cbd82e1975b605c0bb1a22112a90d129a0ef1b5844584f5c3e0e39043ec9976[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844" + }, + "addr": "yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5", + "valueSat": 90000000000, + "value": 900, + "doubleSpentTxID": null + }, + { + "txid": "7d869d991b1c973e1626cdc4a8cf56b37f18d7f1a0c6b929f7333533c1cce4e1", + "vout": 0, + "sequence": 4294967294, + "n": 1, + "scriptSig": { + "hex": "47304402202f37d22beccdb74df3e48c87a09b0f54365fe4a4586efa050db4bbf7497c4fa302202cf666507d238d652cc56dd6ec88bf1e7b2664d1b6796960bbb734dcfb5e880f0121030b992a821076dc5dee6b00e135178f71ae7047dfa136862a4d274713f4eb0177", + "asm": "304402202f37d22beccdb74df3e48c87a09b0f54365fe4a4586efa050db4bbf7497c4fa302202cf666507d238d652cc56dd6ec88bf1e7b2664d1b6796960bbb734dcfb5e880f[ALL] 030b992a821076dc5dee6b00e135178f71ae7047dfa136862a4d274713f4eb0177" + }, + "addr": "yifmFokBdParjkfZp3Bu5oR9gTtHtPEU3b", + "valueSat": 299998582, + "value": 2.99998582, + "doubleSpentTxID": null + }, + { + "txid": "baaa48cea28652dbadab9ec056a75f79bdc5e9f10df25a979a8dab6a3f415682", + "vout": 1, + "sequence": 4294967294, + "n": 2, + "scriptSig": { + "hex": "47304402202e7a9c79a72f9e2c037138e2fc1d6e44e683dedd14dbdf1049e9ceb73668f3b802202db60a1aed9c06bc413c8e2d6bfd63cc928a2989cf30c5f1e5f08d9ec1c581be012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844", + "asm": "304402202e7a9c79a72f9e2c037138e2fc1d6e44e683dedd14dbdf1049e9ceb73668f3b802202db60a1aed9c06bc413c8e2d6bfd63cc928a2989cf30c5f1e5f08d9ec1c581be[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844" + }, + "addr": "yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5", + "valueSat": 10000000000, + "value": 100, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + } + }, + "chains": { + "testnet": { + "name": "testnet", + "blockheight": 6310 + } + } +} diff --git a/packages/wallet-lib/fixtures/duringdevelop-fullstore-snapshot-1548538361.json b/packages/wallet-lib/fixtures/duringdevelop-fullstore-snapshot-1548538361.json new file mode 100644 index 00000000000..66a9729ab1f --- /dev/null +++ b/packages/wallet-lib/fixtures/duringdevelop-fullstore-snapshot-1548538361.json @@ -0,0 +1,1649 @@ +{ + "wallets": { + "5061b8276c": { + "accounts": { + "m/44'/1'/0'": { + "label": null, + "path": "m/44'/1'/0'", + "network": { + "name": "testnet", + "alias": "regtest", + "pubkeyhash": 140, + "privatekey": 239, + "scripthash": 19, + "xpubkey": 70617039, + "xprivkey": 70615956, + "port": 19999, + "networkMagic": { + "type": "Buffer", + "data": [ + 206, + 226, + 202, + 255 + ] + }, + "dnsSeeds": [ + "testnet-seed.darkcoin.io", + "testnet-seed.dashdot.io", + "test.dnsseed.masternode.io" + ] + } + }, + "m/44'/1'/1'": { + "label": null, + "path": "m/44'/1'/1'", + "network": { + "name": "testnet", + "alias": "regtest", + "pubkeyhash": 140, + "privatekey": 239, + "scripthash": 19, + "xpubkey": 70617039, + "xprivkey": 70615956, + "port": 19999, + "networkMagic": { + "type": "Buffer", + "data": [ + 206, + 226, + 202, + 255 + ] + }, + "dnsSeeds": [ + "testnet-seed.darkcoin.io", + "testnet-seed.dashdot.io", + "test.dnsseed.masternode.io" + ] + } + } + }, + "network": { + "name": "testnet", + "alias": "regtest", + "pubkeyhash": 140, + "privatekey": 239, + "scripthash": 19, + "xpubkey": 70617039, + "xprivkey": 70615956, + "port": 19999, + "networkMagic": { + "type": "Buffer", + "data": [ + 206, + 226, + 202, + 255 + ] + }, + "dnsSeeds": [ + "testnet-seed.darkcoin.io", + "testnet-seed.dashdot.io", + "test.dnsseed.masternode.io" + ] + }, + "mnemonic": null, + "type": null, + "blockheight": 0, + "addresses": { + "external": { + "m/44'/1'/0'/0/0": { + "address": "yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT", + "path": "m/44'/1'/0'/0/0", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "dd44373d55e6e8f3a0a0cf038de6a2c750f98a2088e074f3b6de249dad704abf", + "4493e8a39bb97d6709ca69d391b0b99f573d3aed5ce50da5f8c7626fc1cb1a7d", + "cdcf81b69629c3157f09878076bc4f544aa01477cf59915461343476772a4a84", + "507e56181d03ba75b133f93cd073703c5c514f623f30e4cc32144c62b5a697c4" + ], + "fetchedLast": 1548538385051, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/0/1": { + "address": "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe", + "path": "m/44'/1'/0'/0/1", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf", + "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059" + ], + "fetchedLast": 1548538385070, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/0/2": { + "address": "yQSVFizTKcPLz2V7zoZ3HkkJ7sQmb5jXAs", + "path": "m/44'/1'/0'/0/2", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "bb0c341e970418422bb94eb20d3ddb00a350907e2ef9d6247324665f78467872", + "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb" + ], + "fetchedLast": 1548538385060, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/0/3": { + "address": "yhnTNo6tkmr8tA4SAL8gcci1z5rPHuaoxA", + "path": "m/44'/1'/0'/0/3", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "bb0c341e970418422bb94eb20d3ddb00a350907e2ef9d6247324665f78467872", + "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf" + ], + "fetchedLast": 1548538385063, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/0/4": { + "address": "yNY6spErvvm9C8at2KQpvAfd6TPumgyETh", + "path": "m/44'/1'/0'/0/4", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "bb0c341e970418422bb94eb20d3ddb00a350907e2ef9d6247324665f78467872", + "5fc934fc42534dca5bea8d4f5cc5afa721dc1ce092e854050b761e3d4b757cc7", + "f093df5d83371c2f2f167399b2b27bc79d3387c7fd41575ba44881bace228bbe" + ], + "fetchedLast": 1548538385003, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/0/5": { + "address": "yaVrJ5dgELFkYwv6AydDyGPAJQ5kTJXyAN", + "path": "m/44'/1'/0'/0/5", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385006, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/6": { + "address": "yMN2w8NiwcmY3zvJLeeBxpaExFV1aN23pg", + "path": "m/44'/1'/0'/0/6", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385008, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/7": { + "address": "yQamgF8LvZ9nV1xrbZ1hUiHjmttznbdVnS", + "path": "m/44'/1'/0'/0/7", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538384995, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/8": { + "address": "ya2aCFRiEQ6SHiB9yETaEGEcs6gNSqJt9d", + "path": "m/44'/1'/0'/0/8", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385104, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/9": { + "address": "ygZJwpqD7y9SP7t1jk4reytSCW6M16aEfC", + "path": "m/44'/1'/0'/0/9", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385094, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/10": { + "address": "ybHHUqRWE35R7gohkXHQVJELoGkduywroY", + "path": "m/44'/1'/0'/0/10", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385102, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/11": { + "address": "yVe8ftxg2aubVa5gppnz4WZJ7Umgu1UEjh", + "path": "m/44'/1'/0'/0/11", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385015, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/12": { + "address": "ydhUVrSYdRKTm1bztWCgFSgSqF1HKKL25i", + "path": "m/44'/1'/0'/0/12", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385022, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/13": { + "address": "yaUfQiKzL29LkXYZiEGM5DmMuMBn5e81MV", + "path": "m/44'/1'/0'/0/13", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385013, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/14": { + "address": "yTZfTfqgv5r9CW4jyMsr3PnhYhGh7scFDj", + "path": "m/44'/1'/0'/0/14", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385010, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/15": { + "address": "yhxUAk6St4CdhYifJqEAaY9jJVd4mZyEmy", + "path": "m/44'/1'/0'/0/15", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385068, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/16": { + "address": "yRs2aA9UjfEaCpNF3wf8L2m62EfGkQdD8u", + "path": "m/44'/1'/0'/0/16", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385049, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/17": { + "address": "yVPAnRANHXZnrL5P5SwJCWKRru7PgvMhYg", + "path": "m/44'/1'/0'/0/17", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385064, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/18": { + "address": "yhUPdqWmmJPT4wE95QzmTvq8vkUJE6LswZ", + "path": "m/44'/1'/0'/0/18", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385013, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/19": { + "address": "yLwNeRjtHHqW1d7KVR5AXnELfB8vp9iuvk", + "path": "m/44'/1'/0'/0/19", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385102, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/20": { + "address": "yeEjYLeiHMhABkG2RUtTdXgZKfz7L5uohT", + "path": "m/44'/1'/0'/0/20", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385058, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/0": { + "address": "yd9o9jYkwB2Ba9aMtvm56YeHfCsTXyphhD", + "path": "m/44'/1'/1'/0/0", + "balanceSat": 15000000000, + "unconfirmedBalanceSat": 0, + "transactions": [ + "dd44373d55e6e8f3a0a0cf038de6a2c750f98a2088e074f3b6de249dad704abf" + ], + "fetchedLast": 1548538385098, + "used": true, + "utxos": { + "dd44373d55e6e8f3a0a0cf038de6a2c750f98a2088e074f3b6de249dad704abf": { + "txid": "dd44373d55e6e8f3a0a0cf038de6a2c750f98a2088e074f3b6de249dad704abf", + "outputIndex": 0, + "satoshis": 15000000000, + "scriptPubKey": "76a914b8a6ed1a5810dc15e6f35ba7c7e4cb97673c11f488ac" + } + } + }, + "m/44'/1'/1'/0/1": { + "address": "yVj8CJt6Jmh2AESjKQ3M1a8RvogibXgE4R", + "path": "m/44'/1'/1'/0/1", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385007, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/2": { + "address": "yUDcocLLM2TZYpRU4eAuHSHLZzsNXbYoCj", + "path": "m/44'/1'/1'/0/2", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385096, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/3": { + "address": "yWyhYnRyQTaQsPVwpX47ijNt8C3PDxm88x", + "path": "m/44'/1'/1'/0/3", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385093, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/4": { + "address": "yYqGjLtPffxzXo2bwBf8qAtycsjy4DfdQa", + "path": "m/44'/1'/1'/0/4", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385093, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/5": { + "address": "yikFoRg16VSuj7dakfhgX5iyWvRv2sVqCX", + "path": "m/44'/1'/1'/0/5", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385139, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/6": { + "address": "yWZfsn3zb3e87NMyjXLSe7KaQQQ28xHNSG", + "path": "m/44'/1'/1'/0/6", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385142, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/7": { + "address": "ycECoKam9BybfhH2VkNCaxeHcKxMrwVLvV", + "path": "m/44'/1'/1'/0/7", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385092, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/8": { + "address": "yPnVae7umcZGGJrdzfu9oJN7utGjVyrcgD", + "path": "m/44'/1'/1'/0/8", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385132, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/9": { + "address": "yjP4MqPguk72i7uHdvvAtWhJ5u478zdw9q", + "path": "m/44'/1'/1'/0/9", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385120, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/10": { + "address": "yaDdG2d39RseQkdE7VrMy4edpjSv5CCviy", + "path": "m/44'/1'/1'/0/10", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385090, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/11": { + "address": "yWWfjQ3R8KnUroKpvgNmdi5MgfPRibs8Wk", + "path": "m/44'/1'/1'/0/11", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385095, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/12": { + "address": "yQe7iJtxvmLcygBGnFd7J9MNHsnnKJQtob", + "path": "m/44'/1'/1'/0/12", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385149, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/13": { + "address": "yTUuA2NEfnZgS56LiD1eubwREEeBpwaHXU", + "path": "m/44'/1'/1'/0/13", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385138, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/14": { + "address": "yNN8GPRRa7fFZjDRMQRsNSkkJnVVT3oRUB", + "path": "m/44'/1'/1'/0/14", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385153, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/15": { + "address": "ydRonTPa2QuoS6idyKp4SwNZqvKZNbCq74", + "path": "m/44'/1'/1'/0/15", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385089, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/16": { + "address": "ygb1ynp132f8ASoKAZF1ww4gzYgMH5wD6b", + "path": "m/44'/1'/1'/0/16", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385150, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/17": { + "address": "yWzhNDEM8Fe9fhSBMUjnteP4ChNpFwK5Ji", + "path": "m/44'/1'/1'/0/17", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385147, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/18": { + "address": "yQBLPW4w4GhFyn8LKmQuVQYavwui7u92e9", + "path": "m/44'/1'/1'/0/18", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385158, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/19": { + "address": "yezAszavEFj1AUduRYHaoAVck3KpXLe9Ae", + "path": "m/44'/1'/1'/0/19", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385135, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/0/20": { + "address": "yLcz2qaeRAyxU36CYmTeroBMVqkaykk3Qx", + "path": "m/44'/1'/1'/0/20", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385105, + "used": false, + "utxos": {} + } + }, + "internal": { + "m/44'/1'/0'/1/0": { + "address": "yTa2L2ZJr48sbJCnYP96RwW1D4ceeCdyHS", + "path": "m/44'/1'/0'/1/0", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385123, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/1/1": { + "address": "ybTg1Xema7wsGHGxSMQUSNoxyYRkTMUWJd", + "path": "m/44'/1'/0'/1/1", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059", + "cdcf81b69629c3157f09878076bc4f544aa01477cf59915461343476772a4a84" + ], + "fetchedLast": 1548538385198, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/1/2": { + "address": "yLVQ9bZBLZmmvNQk7pPCUAGaXADQ6Rhkqt", + "path": "m/44'/1'/0'/1/2", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb", + "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059" + ], + "fetchedLast": 1548538385129, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/1/3": { + "address": "yU7hmdDdi9RWem64hMz3GV3i9UWHNNK2FS", + "path": "m/44'/1'/0'/1/3", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "5fc934fc42534dca5bea8d4f5cc5afa721dc1ce092e854050b761e3d4b757cc7", + "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb" + ], + "fetchedLast": 1548538385148, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/1/4": { + "address": "yfyTKf2PaxFvND6V5pEFWpnrbcSdy3igZQ", + "path": "m/44'/1'/0'/1/4", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "f093df5d83371c2f2f167399b2b27bc79d3387c7fd41575ba44881bace228bbe", + "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf" + ], + "fetchedLast": 1548538385128, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/1/5": { + "address": "yeLbU1At3Cp4RD7Gunic6iy6orgnoNDhEb", + "path": "m/44'/1'/0'/1/5", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "bb0c341e970418422bb94eb20d3ddb00a350907e2ef9d6247324665f78467872", + "f093df5d83371c2f2f167399b2b27bc79d3387c7fd41575ba44881bace228bbe" + ], + "fetchedLast": 1548538385180, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/1/6": { + "address": "ySVpgHLkgrrrsbaWJhW5GMHZjeSkADrsTJ", + "path": "m/44'/1'/0'/1/6", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "bb0c341e970418422bb94eb20d3ddb00a350907e2ef9d6247324665f78467872", + "5fc934fc42534dca5bea8d4f5cc5afa721dc1ce092e854050b761e3d4b757cc7" + ], + "fetchedLast": 1548538385167, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/1/7": { + "address": "yPuyPUdk32KP6iE9359TuqWMv8azrqtHrk", + "path": "m/44'/1'/0'/1/7", + "balanceSat": 34999999753, + "unconfirmedBalanceSat": 0, + "transactions": [ + "dd44373d55e6e8f3a0a0cf038de6a2c750f98a2088e074f3b6de249dad704abf" + ], + "fetchedLast": 1548538385190, + "used": true, + "utxos": { + "dd44373d55e6e8f3a0a0cf038de6a2c750f98a2088e074f3b6de249dad704abf": { + "txid": "dd44373d55e6e8f3a0a0cf038de6a2c750f98a2088e074f3b6de249dad704abf", + "outputIndex": 1, + "satoshis": 34999999753, + "scriptPubKey": "76a9142770034d7d5601c2cac955eee6bae8cfe9f0272988ac" + } + } + }, + "m/44'/1'/0'/1/8": { + "address": "yaZFt1VnAbi72mtyjDNV4AwTECqdg5Bv95", + "path": "m/44'/1'/0'/1/8", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385164, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/9": { + "address": "yR7bqPdf8UT5ZFpCiqwAiz69xHco7QJqyp", + "path": "m/44'/1'/0'/1/9", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385157, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/10": { + "address": "yRDW3KVi5vdnooaJxfXW2x9XKe8mz5XgRC", + "path": "m/44'/1'/0'/1/10", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385124, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/11": { + "address": "yfTs2aiRuWNdgnAW9gcf7sv2A3qpATRzHJ", + "path": "m/44'/1'/0'/1/11", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385168, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/12": { + "address": "ybqMAktAf9mktXHVN1p9BmfgUZzUnELkrw", + "path": "m/44'/1'/0'/1/12", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385137, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/13": { + "address": "ySBGFjj74zmCqmcMxCBr7JvgvYqjzsgZJp", + "path": "m/44'/1'/0'/1/13", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385183, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/14": { + "address": "yZdS1zoVDbGVRYgV3tnwGhhQMX7vw3rpBj", + "path": "m/44'/1'/0'/1/14", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385134, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/15": { + "address": "yYoAZ8hHN48usWRBuiMpbZ6aVifAw1E7TV", + "path": "m/44'/1'/0'/1/15", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385166, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/16": { + "address": "yhjj9bfgZrem1ytWby8dcTiJ9pA8Q4tC6J", + "path": "m/44'/1'/0'/1/16", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385136, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/17": { + "address": "yVycuCgivTbSpMfn9yDnjrCX3QzLgZRXDr", + "path": "m/44'/1'/0'/1/17", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385125, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/18": { + "address": "ydJsVhE6houFRWiNvFUYqhMUK5RHnuoqK9", + "path": "m/44'/1'/0'/1/18", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385244, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/19": { + "address": "yck7wS32ydQ8AxRw7NZJUtiRStbMSYGi6W", + "path": "m/44'/1'/0'/1/19", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385174, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/20": { + "address": "yU7sNM4j6fzKtbah24gCXdN636piQN8F2f", + "path": "m/44'/1'/0'/1/20", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385134, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/0": { + "address": "yNQjBXuY1anZvPgBnXLDUDx5yqQR5dT9V2", + "path": "m/44'/1'/1'/1/0", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385125, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/1": { + "address": "yaVLdwd2CwDMQeuxPg6JcTVwKsESfKhYM3", + "path": "m/44'/1'/1'/1/1", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385162, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/2": { + "address": "ySFdYXG7qhus6oyo9uczKga3FyBTqNPWDt", + "path": "m/44'/1'/1'/1/2", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385244, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/3": { + "address": "yR93AewAsL8Xkn4dNCowSfKQ1MZmibpvD1", + "path": "m/44'/1'/1'/1/3", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385124, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/4": { + "address": "yZHLVcr4tK7XHj3VdSzCcX6GJTJijQfALi", + "path": "m/44'/1'/1'/1/4", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385151, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/5": { + "address": "yLsbXTAtiiCLtRWmXEKtEfwGTumnSBfYnf", + "path": "m/44'/1'/1'/1/5", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385156, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/6": { + "address": "yZ1oG2SiCv1Rub9B1HkuT2CtH6pAnqSrDD", + "path": "m/44'/1'/1'/1/6", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385153, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/7": { + "address": "yLsrhZyCYBqwbZkzhcLF7ZjhXoksaVVo6v", + "path": "m/44'/1'/1'/1/7", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385135, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/8": { + "address": "yNnxzeyoSLcBC8r331Vn5yJ8uuwopSwizk", + "path": "m/44'/1'/1'/1/8", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385175, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/9": { + "address": "yNgiWL46aouoJNq9DAWYjn3nXSx6ksdLQN", + "path": "m/44'/1'/1'/1/9", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385231, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/10": { + "address": "yau4KZ2s6ra65UQtx1AiFReHj5ybDZLema", + "path": "m/44'/1'/1'/1/10", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385247, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/11": { + "address": "yTQpSiKZBrkrktQT4BZJ9Kjf3hCg5iAdQ3", + "path": "m/44'/1'/1'/1/11", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385249, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/12": { + "address": "yeCivTRyrkHRW2kQ8Nnp3BUTRCB51ohaMp", + "path": "m/44'/1'/1'/1/12", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385237, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/13": { + "address": "ybbexhYxeqFhihWxk5HtJqMqubcrn5Doo5", + "path": "m/44'/1'/1'/1/13", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385252, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/14": { + "address": "ySXuuqcE2smntL15nreTtRkwoPi2gjzij9", + "path": "m/44'/1'/1'/1/14", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385231, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/15": { + "address": "ySDpqsqsSKz7scf3FEbYZyfKM6NBW1eKaA", + "path": "m/44'/1'/1'/1/15", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385243, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/16": { + "address": "yNhERPDZcuTK6DkbcfQmqiN9agoxGnSqxY", + "path": "m/44'/1'/1'/1/16", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385230, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/17": { + "address": "yWdydR6A8bJomuYtg8Q6fdKf4LdVtxLnYv", + "path": "m/44'/1'/1'/1/17", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385245, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/18": { + "address": "yjPHdSvFgA8R7SjHsLJB9bvJKtFce9Jtc9", + "path": "m/44'/1'/1'/1/18", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385251, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/19": { + "address": "ydfpy6SLkhaDR2X4YhGfyQwKXStuxzk57V", + "path": "m/44'/1'/1'/1/19", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385241, + "used": false, + "utxos": {} + }, + "m/44'/1'/1'/1/20": { + "address": "yWdndssNjefUiDwK6drFVSvoXfFwZwDzwf", + "path": "m/44'/1'/1'/1/20", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548538385229, + "used": false, + "utxos": {} + } + }, + "misc": {} + } + } + }, + "transactions": { + "5fc934fc42534dca5bea8d4f5cc5afa721dc1ce092e854050b761e3d4b757cc7": { + "txid": "5fc934fc42534dca5bea8d4f5cc5afa721dc1ce092e854050b761e3d4b757cc7", + "blockhash": "000001d0ec348cbd873d033123c0c9ea58d58974933cedcd697b04bfaeb708e1", + "blockheight": 5409, + "blocktime": 1548409108, + "fees": 247, + "size": 225, + "vout": [ + { + "value": "139.99997456", + "n": 0, + "scriptPubKey": { + "hex": "76a9141854fcb39739af45f10a644280a056c06b978ed888ac", + "asm": "OP_DUP OP_HASH160 1854fcb39739af45f10a644280a056c06b978ed8 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yNY6spErvvm9C8at2KQpvAfd6TPumgyETh" + ], + "type": "pubkeyhash" + }, + "spentTxId": "bb0c341e970418422bb94eb20d3ddb00a350907e2ef9d6247324665f78467872", + "spentIndex": 2, + "spentHeight": 5418 + }, + { + "value": "49.99999753", + "n": 1, + "scriptPubKey": { + "hex": "76a91443c722df0e025bd2f8a8ae14accc35773d9145db88ac", + "asm": "OP_DUP OP_HASH160 43c722df0e025bd2f8a8ae14accc35773d9145db OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ySVpgHLkgrrrsbaWJhW5GMHZjeSkADrsTJ" + ], + "type": "pubkeyhash" + }, + "spentTxId": "bb0c341e970418422bb94eb20d3ddb00a350907e2ef9d6247324665f78467872", + "spentIndex": 4, + "spentHeight": 5418 + } + ], + "vin": [ + { + "txid": "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "473044022003ccc91adcbb7c7b0bf95bacfca9088a67a60086b4284a28eede127d5001de2e02205567836335fa12582278a91c8d203ae75b62f0c78fffc09146e8333ec942f06f0121033c66424e2ebcde8c06c42fefc7008028fa9e3864a0bc562663a3d30aae258d02", + "asm": "3044022003ccc91adcbb7c7b0bf95bacfca9088a67a60086b4284a28eede127d5001de2e02205567836335fa12582278a91c8d203ae75b62f0c78fffc09146e8333ec942f06f[ALL] 033c66424e2ebcde8c06c42fefc7008028fa9e3864a0bc562663a3d30aae258d02" + }, + "addr": "yU7hmdDdi9RWem64hMz3GV3i9UWHNNK2FS", + "valueSat": 18999997456, + "value": 189.99997456, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "f093df5d83371c2f2f167399b2b27bc79d3387c7fd41575ba44881bace228bbe": { + "txid": "f093df5d83371c2f2f167399b2b27bc79d3387c7fd41575ba44881bace228bbe", + "blockhash": "000001d0ec348cbd873d033123c0c9ea58d58974933cedcd697b04bfaeb708e1", + "blockheight": 5409, + "blocktime": 1548409108, + "fees": 247, + "size": 225, + "vout": [ + { + "value": "300.99999753", + "n": 0, + "scriptPubKey": { + "hex": "76a9141854fcb39739af45f10a644280a056c06b978ed888ac", + "asm": "OP_DUP OP_HASH160 1854fcb39739af45f10a644280a056c06b978ed8 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yNY6spErvvm9C8at2KQpvAfd6TPumgyETh" + ], + "type": "pubkeyhash" + }, + "spentTxId": "bb0c341e970418422bb94eb20d3ddb00a350907e2ef9d6247324665f78467872", + "spentIndex": 1, + "spentHeight": 5418 + }, + { + "value": "23.99999753", + "n": 1, + "scriptPubKey": { + "hex": "76a914c5a9df9687f4da65d1ee34e4cd0fcb26e6b64b2088ac", + "asm": "OP_DUP OP_HASH160 c5a9df9687f4da65d1ee34e4cd0fcb26e6b64b20 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yeLbU1At3Cp4RD7Gunic6iy6orgnoNDhEb" + ], + "type": "pubkeyhash" + }, + "spentTxId": "bb0c341e970418422bb94eb20d3ddb00a350907e2ef9d6247324665f78467872", + "spentIndex": 5, + "spentHeight": 5418 + } + ], + "vin": [ + { + "txid": "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "4730440220245994743918e527183ac6c88d46b3ba3db559da17670af44aadb0743026158f022036ed5d83710afa93e555ddfd85059cba2b108542d1f6c5a9e714b4923163ec99012103d026a1bf9086bd08ddf419cd3bedc89561a398aba8ad914c836b3854cd32e847", + "asm": "30440220245994743918e527183ac6c88d46b3ba3db559da17670af44aadb0743026158f022036ed5d83710afa93e555ddfd85059cba2b108542d1f6c5a9e714b4923163ec99[ALL] 03d026a1bf9086bd08ddf419cd3bedc89561a398aba8ad914c836b3854cd32e847" + }, + "addr": "yfyTKf2PaxFvND6V5pEFWpnrbcSdy3igZQ", + "valueSat": 32499999753, + "value": 324.99999753, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "bb0c341e970418422bb94eb20d3ddb00a350907e2ef9d6247324665f78467872": { + "txid": "bb0c341e970418422bb94eb20d3ddb00a350907e2ef9d6247324665f78467872", + "blockhash": "00000342589e047e102204962c69d53ae3f5bf282e1050183fca1d36ee95976b", + "blockheight": 5418, + "blocktime": 1548410546, + "fees": 930, + "size": 930, + "vout": [ + { + "value": "989.99995785", + "n": 0, + "scriptPubKey": { + "hex": "76a914b6a824e590c462d58539934486c582087ba7220488ac", + "asm": "OP_DUP OP_HASH160 b6a824e590c462d58539934486c582087ba72204 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ycyFFyWCPSWbXLZBeYppJqgvBF7bnu8BWQ" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "txid": "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb", + "vout": 0, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100f5486704f8935a04ef278644d88b3752bdbd4ad59498b3eacebd912e6de2d03202202b6d797e919f6bbf71fa92467ee5305155a79e226b8da561ba8a3a13ed671b9e0121021ecf25f36e4b18b9ea8aa1b69bc0aeeab552f026a22a8dfeda25b0bb1991227f", + "asm": "3045022100f5486704f8935a04ef278644d88b3752bdbd4ad59498b3eacebd912e6de2d03202202b6d797e919f6bbf71fa92467ee5305155a79e226b8da561ba8a3a13ed671b9e[ALL] 021ecf25f36e4b18b9ea8aa1b69bc0aeeab552f026a22a8dfeda25b0bb1991227f" + }, + "addr": "yQSVFizTKcPLz2V7zoZ3HkkJ7sQmb5jXAs", + "valueSat": 35000000000, + "value": 350, + "doubleSpentTxID": null + }, + { + "txid": "f093df5d83371c2f2f167399b2b27bc79d3387c7fd41575ba44881bace228bbe", + "vout": 0, + "sequence": 4294967295, + "n": 1, + "scriptSig": { + "hex": "47304402207d1849f9424fa8199a40492f51da00783397c9bc8cb7e354a7d661715afc44a702202faebd7411e3997741730ab6d95ea64e44cdb9b956220981792f891f5db09df301210356f70d089a69cc01e4495add072005e2430ecd9e636c6b54f7f0f5b07381df3b", + "asm": "304402207d1849f9424fa8199a40492f51da00783397c9bc8cb7e354a7d661715afc44a702202faebd7411e3997741730ab6d95ea64e44cdb9b956220981792f891f5db09df3[ALL] 0356f70d089a69cc01e4495add072005e2430ecd9e636c6b54f7f0f5b07381df3b" + }, + "addr": "yNY6spErvvm9C8at2KQpvAfd6TPumgyETh", + "valueSat": 30099999753, + "value": 300.99999753, + "doubleSpentTxID": null + }, + { + "txid": "5fc934fc42534dca5bea8d4f5cc5afa721dc1ce092e854050b761e3d4b757cc7", + "vout": 0, + "sequence": 4294967295, + "n": 2, + "scriptSig": { + "hex": "483045022100bf0fe5160058af9f1b8c1aa50499f48f13fe82bc8a80b9d94671c6dff56c09970220279d210e14906e340c5f91ecb47fe68220f0e09c65635c731a358fbf03bf843d01210356f70d089a69cc01e4495add072005e2430ecd9e636c6b54f7f0f5b07381df3b", + "asm": "3045022100bf0fe5160058af9f1b8c1aa50499f48f13fe82bc8a80b9d94671c6dff56c09970220279d210e14906e340c5f91ecb47fe68220f0e09c65635c731a358fbf03bf843d[ALL] 0356f70d089a69cc01e4495add072005e2430ecd9e636c6b54f7f0f5b07381df3b" + }, + "addr": "yNY6spErvvm9C8at2KQpvAfd6TPumgyETh", + "valueSat": 13999997456, + "value": 139.99997456, + "doubleSpentTxID": null + }, + { + "txid": "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf", + "vout": 0, + "sequence": 4294967295, + "n": 3, + "scriptSig": { + "hex": "483045022100e8c01736ea38a361dc352f7475482ae9a38877cea99afd8404af6062978da71202200877068eaa6e43de0b63189a94dcc32cb74a2a1c7084b11cbc289fdab458c359012103fc53f92624801deb3ae67cf6335629aa554c3858f944e7e903dd79145a1525ca", + "asm": "3045022100e8c01736ea38a361dc352f7475482ae9a38877cea99afd8404af6062978da71202200877068eaa6e43de0b63189a94dcc32cb74a2a1c7084b11cbc289fdab458c359[ALL] 03fc53f92624801deb3ae67cf6335629aa554c3858f944e7e903dd79145a1525ca" + }, + "addr": "yhnTNo6tkmr8tA4SAL8gcci1z5rPHuaoxA", + "valueSat": 12500000000, + "value": 125, + "doubleSpentTxID": null + }, + { + "txid": "5fc934fc42534dca5bea8d4f5cc5afa721dc1ce092e854050b761e3d4b757cc7", + "vout": 1, + "sequence": 4294967295, + "n": 4, + "scriptSig": { + "hex": "483045022100a9e60cf14d00dda487d3193e9dc83305d9ef626984d4d4d7fb482daa520af7760220752fd890d3ccb546fe1037254c1e0ecfc74046df76df222f8b02c1df52d0b1b201210222da1189f410214894e09898e67feff6fb2d52c47f9ddc8c3420b673ce19a45d", + "asm": "3045022100a9e60cf14d00dda487d3193e9dc83305d9ef626984d4d4d7fb482daa520af7760220752fd890d3ccb546fe1037254c1e0ecfc74046df76df222f8b02c1df52d0b1b2[ALL] 0222da1189f410214894e09898e67feff6fb2d52c47f9ddc8c3420b673ce19a45d" + }, + "addr": "ySVpgHLkgrrrsbaWJhW5GMHZjeSkADrsTJ", + "valueSat": 4999999753, + "value": 49.99999753, + "doubleSpentTxID": null + }, + { + "txid": "f093df5d83371c2f2f167399b2b27bc79d3387c7fd41575ba44881bace228bbe", + "vout": 1, + "sequence": 4294967295, + "n": 5, + "scriptSig": { + "hex": "47304402206a9bad2e1c32b51a8833f254d4d7f8aac419bc169d48da0156383c648a3eee8d02204064fb21acff7af6f8338fb93c99e4b2f610b7cae7b53e9911be5d12acab6aab012103e78530639ada8716fb7ff389ba3ba00d9cc8c6fb569c6aa5d1d22f27a896ac33", + "asm": "304402206a9bad2e1c32b51a8833f254d4d7f8aac419bc169d48da0156383c648a3eee8d02204064fb21acff7af6f8338fb93c99e4b2f610b7cae7b53e9911be5d12acab6aab[ALL] 03e78530639ada8716fb7ff389ba3ba00d9cc8c6fb569c6aa5d1d22f27a896ac33" + }, + "addr": "yeLbU1At3Cp4RD7Gunic6iy6orgnoNDhEb", + "valueSat": 2399999753, + "value": 23.99999753, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf": { + "txid": "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf", + "blockhash": "0000011ab17c26bb70dde073303dce57d94dbb4bb99da12d1383d09125055d6d", + "blockheight": 3546, + "blocktime": 1548153589, + "fees": 247, + "size": 225, + "vout": [ + { + "value": "125.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914eb76500ef39cb2a4f166eafd6ba4270b6ebae71988ac", + "asm": "OP_DUP OP_HASH160 eb76500ef39cb2a4f166eafd6ba4270b6ebae719 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yhnTNo6tkmr8tA4SAL8gcci1z5rPHuaoxA" + ], + "type": "pubkeyhash" + }, + "spentTxId": "bb0c341e970418422bb94eb20d3ddb00a350907e2ef9d6247324665f78467872", + "spentIndex": 3, + "spentHeight": 5418 + }, + { + "value": "324.99999753", + "n": 1, + "scriptPubKey": { + "hex": "76a914d79a97706450058b85aaf535e2c5853f181d5fac88ac", + "asm": "OP_DUP OP_HASH160 d79a97706450058b85aaf535e2c5853f181d5fac OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yfyTKf2PaxFvND6V5pEFWpnrbcSdy3igZQ" + ], + "type": "pubkeyhash" + }, + "spentTxId": "f093df5d83371c2f2f167399b2b27bc79d3387c7fd41575ba44881bace228bbe", + "spentIndex": 0, + "spentHeight": 5409 + } + ], + "vin": [ + { + "txid": "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059", + "vout": 0, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "473044022061d43a5beadb582842736407db70094a2d4bce2f162b6fd94760423f21c29a80022076878f295a64877b93d745fc089bd5acd6927b2966cbea5a5314925371893b7701210367b58178e4db0f50f2c7e18dbdaac0d7ed585a08c02fbf090d3fe2ecd04b2091", + "asm": "3044022061d43a5beadb582842736407db70094a2d4bce2f162b6fd94760423f21c29a80022076878f295a64877b93d745fc089bd5acd6927b2966cbea5a5314925371893b77[ALL] 0367b58178e4db0f50f2c7e18dbdaac0d7ed585a08c02fbf090d3fe2ecd04b2091" + }, + "addr": "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe", + "valueSat": 45000000000, + "value": 450, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059": { + "txid": "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059", + "blockhash": "3532c95f230458dfa5275c8e71bd074987de85e5c9f5ec28ce17c66973c167d8", + "blockheight": 3537, + "blocktime": 1548152219, + "fees": 247, + "size": 225, + "vout": [ + { + "value": "450.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf", + "spentIndex": 0, + "spentHeight": 3546 + }, + { + "value": "539.99997703", + "n": 1, + "scriptPubKey": { + "hex": "76a91401e1e7da88b5f2005a2b710fcf6b172ca2a221b488ac", + "asm": "OP_DUP OP_HASH160 01e1e7da88b5f2005a2b710fcf6b172ca2a221b4 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yLVQ9bZBLZmmvNQk7pPCUAGaXADQ6Rhkqt" + ], + "type": "pubkeyhash" + }, + "spentTxId": "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb", + "spentIndex": 0, + "spentHeight": 3544 + } + ], + "vin": [ + { + "txid": "cdcf81b69629c3157f09878076bc4f544aa01477cf59915461343476772a4a84", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "473044022013592c02394a0b12d0b0f0e0062388ec2806b5a4e9fcc51a8647f12c6bf1e2b002202813b2521c933dcf8efecc6b488defeec6ef85cfb34f6fbb86f32af7fcfdbf62012102420b7af29c43e5d2da5f23a30e0ce994b99e76ebcd2ad7605bd32418aa0c829f", + "asm": "3044022013592c02394a0b12d0b0f0e0062388ec2806b5a4e9fcc51a8647f12c6bf1e2b002202813b2521c933dcf8efecc6b488defeec6ef85cfb34f6fbb86f32af7fcfdbf62[ALL] 02420b7af29c43e5d2da5f23a30e0ce994b99e76ebcd2ad7605bd32418aa0c829f" + }, + "addr": "ybTg1Xema7wsGHGxSMQUSNoxyYRkTMUWJd", + "valueSat": 98999997950, + "value": 989.9999795, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb": { + "txid": "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb", + "blockhash": "1468d15a14ffa2937f87c26a9a25dd8dcfc62263482cb2af034065f117522944", + "blockheight": 3544, + "blocktime": 1548153208, + "fees": 247, + "size": 226, + "vout": [ + { + "value": "350.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a9142d356c444eaf779f274ebf440be834c62b9564ec88ac", + "asm": "OP_DUP OP_HASH160 2d356c444eaf779f274ebf440be834c62b9564ec OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yQSVFizTKcPLz2V7zoZ3HkkJ7sQmb5jXAs" + ], + "type": "pubkeyhash" + }, + "spentTxId": "bb0c341e970418422bb94eb20d3ddb00a350907e2ef9d6247324665f78467872", + "spentIndex": 0, + "spentHeight": 5418 + }, + { + "value": "189.99997456", + "n": 1, + "scriptPubKey": { + "hex": "76a9145588785fde163d906a3a816383656c56f73f11a988ac", + "asm": "OP_DUP OP_HASH160 5588785fde163d906a3a816383656c56f73f11a9 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yU7hmdDdi9RWem64hMz3GV3i9UWHNNK2FS" + ], + "type": "pubkeyhash" + }, + "spentTxId": "5fc934fc42534dca5bea8d4f5cc5afa721dc1ce092e854050b761e3d4b757cc7", + "spentIndex": 0, + "spentHeight": 5409 + } + ], + "vin": [ + { + "txid": "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100e91be951c634f8977516fc5240d39b11691841ea6a0d5e912966c9f9ce22070b02206a4d83d8a960a3c193687031d4133309ac69f32e7cb27aff5d6501648310227b0121031ea66e44bdd37dd6e3151e4f88ef3e6c4cde5d7614fd537e71fe9e1bb59d4abf", + "asm": "3045022100e91be951c634f8977516fc5240d39b11691841ea6a0d5e912966c9f9ce22070b02206a4d83d8a960a3c193687031d4133309ac69f32e7cb27aff5d6501648310227b[ALL] 031ea66e44bdd37dd6e3151e4f88ef3e6c4cde5d7614fd537e71fe9e1bb59d4abf" + }, + "addr": "yLVQ9bZBLZmmvNQk7pPCUAGaXADQ6Rhkqt", + "valueSat": 53999997703, + "value": 539.99997703, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "4493e8a39bb97d6709ca69d391b0b99f573d3aed5ce50da5f8c7626fc1cb1a7d": { + "txid": "4493e8a39bb97d6709ca69d391b0b99f573d3aed5ce50da5f8c7626fc1cb1a7d", + "blockhash": "000002c283d7d55eb6a7d1939b9c428242d09869d6b456f82e1d0df3fdd83dec", + "blockheight": 6121, + "blocktime": 1548506741, + "fees": 247, + "size": 226, + "vout": [ + { + "value": "500.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a91419ba12c566e1e21deafa60f2a6de42d15f85dbbc88ac", + "asm": "OP_DUP OP_HASH160 19ba12c566e1e21deafa60f2a6de42d15f85dbbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT" + ], + "type": "pubkeyhash" + }, + "spentTxId": "dd44373d55e6e8f3a0a0cf038de6a2c750f98a2088e074f3b6de249dad704abf", + "spentIndex": 0, + "spentHeight": 6353 + }, + { + "value": "499.99999753", + "n": 1, + "scriptPubKey": { + "hex": "76a914e0d1017780c580d7beb2e06d93a95f77c105ef1a88ac", + "asm": "OP_DUP OP_HASH160 e0d1017780c580d7beb2e06d93a95f77c105ef1a OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ygpAb9QawEwL6kej4u3r94gC4tfoLZpaLZ" + ], + "type": "pubkeyhash" + }, + "spentTxId": "2d1cce77517e8411c9c9548884029edabbbd11ca3d13d6e11acdd90a79bb4408", + "spentIndex": 0, + "spentHeight": 6310 + } + ], + "vin": [ + { + "txid": "f59ea94b2edf9b42e97027cc528b10d4874ce9ff604f095072e924611463053e", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "4830450221008078d0922828a868040c9576ea6c58b166764d1a4195a6e9f14b8b489baab8d002201a2a3554a76d42c4f0a922228a90945a07af26a121373130926e4face562d6e901210247ea333d3bda2261442e8a5851b4d96a1c9bc029712d52f575fa8aa31fa76f10", + "asm": "30450221008078d0922828a868040c9576ea6c58b166764d1a4195a6e9f14b8b489baab8d002201a2a3554a76d42c4f0a922228a90945a07af26a121373130926e4face562d6e9[ALL] 0247ea333d3bda2261442e8a5851b4d96a1c9bc029712d52f575fa8aa31fa76f10" + }, + "addr": "ygpAb9QawEwL6kej4u3r94gC4tfoLZpaLZ", + "valueSat": 100000000000, + "value": 1000, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "dd44373d55e6e8f3a0a0cf038de6a2c750f98a2088e074f3b6de249dad704abf": { + "txid": "dd44373d55e6e8f3a0a0cf038de6a2c750f98a2088e074f3b6de249dad704abf", + "blockhash": "000001151855f9d8babd4b1fffff59a5877ac191360b97d37dd6c6e18612ef32", + "blockheight": 6353, + "blocktime": 1548538361, + "fees": 247, + "size": 226, + "vout": [ + { + "value": "150.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914b8a6ed1a5810dc15e6f35ba7c7e4cb97673c11f488ac", + "asm": "OP_DUP OP_HASH160 b8a6ed1a5810dc15e6f35ba7c7e4cb97673c11f4 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yd9o9jYkwB2Ba9aMtvm56YeHfCsTXyphhD" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "349.99999753", + "n": 1, + "scriptPubKey": { + "hex": "76a9142770034d7d5601c2cac955eee6bae8cfe9f0272988ac", + "asm": "OP_DUP OP_HASH160 2770034d7d5601c2cac955eee6bae8cfe9f02729 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yPuyPUdk32KP6iE9359TuqWMv8azrqtHrk" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "txid": "4493e8a39bb97d6709ca69d391b0b99f573d3aed5ce50da5f8c7626fc1cb1a7d", + "vout": 0, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "48304502210087a769554e706308e111025d1fff60bdcaaa265a498217fa19d9381dd7f04aad022054de8122d9d84c50e7dcea1fe1fe13cffd4d328878734abb08f0a5e0564990100121037bc909c83d0571bca2589a12af9963a77d375c39d5bd39eb33495c09c60f917d", + "asm": "304502210087a769554e706308e111025d1fff60bdcaaa265a498217fa19d9381dd7f04aad022054de8122d9d84c50e7dcea1fe1fe13cffd4d328878734abb08f0a5e056499010[ALL] 037bc909c83d0571bca2589a12af9963a77d375c39d5bd39eb33495c09c60f917d" + }, + "addr": "yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT", + "valueSat": 50000000000, + "value": 500, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "507e56181d03ba75b133f93cd073703c5c514f623f30e4cc32144c62b5a697c4": { + "txid": "507e56181d03ba75b133f93cd073703c5c514f623f30e4cc32144c62b5a697c4", + "blockhash": "0000002d2cf12cb5dc7e2391c6393e2098d7f201d6f2388353d30485eab0803a", + "blockheight": 3463, + "blocktime": 1548141724, + "fees": 374, + "size": 372, + "vout": [ + { + "value": "99.99999626", + "n": 0, + "scriptPubKey": { + "hex": "76a914387d9e15cfc45b52f74743d44274e7794563bec388ac", + "asm": "OP_DUP OP_HASH160 387d9e15cfc45b52f74743d44274e7794563bec3 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRU9B1BKdGAaGLdZNXWM8CBXCf2CuYCSEv" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "1000.00000000", + "n": 1, + "scriptPubKey": { + "hex": "76a91419ba12c566e1e21deafa60f2a6de42d15f85dbbc88ac", + "asm": "OP_DUP OP_HASH160 19ba12c566e1e21deafa60f2a6de42d15f85dbbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT" + ], + "type": "pubkeyhash" + }, + "spentTxId": "cdcf81b69629c3157f09878076bc4f544aa01477cf59915461343476772a4a84", + "spentIndex": 0, + "spentHeight": 3479 + } + ], + "vin": [ + { + "txid": "03ff87bc72670742305ceb6f90911a0484bb2542459616db081e54e47cf0d012", + "vout": 1, + "sequence": 4294967294, + "n": 0, + "scriptSig": { + "hex": "4730440220549bb6f5882f92d62229890ef5b88588b4671b57dc88e95f8ba8142b049ff43d02205e97b05e44bf41935f399a8ded59e62ef605959b458e7b5e1ec34d39672cea9e012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844", + "asm": "30440220549bb6f5882f92d62229890ef5b88588b4671b57dc88e95f8ba8142b049ff43d02205e97b05e44bf41935f399a8ded59e62ef605959b458e7b5e1ec34d39672cea9e[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844" + }, + "addr": "yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5", + "valueSat": 100000000000, + "value": 1000, + "doubleSpentTxID": null + }, + { + "txid": "5fe820d678e30b5f2fd49a0f67f8382469c36e8b5cbd25cd06dd9ed049823dd6", + "vout": 1, + "sequence": 4294967294, + "n": 1, + "scriptSig": { + "hex": "473044022057122ae2361a059213e362c3da275efe2efd3273396e8e7353238fe140011a9d02206379d6a1eb8463bfaf0cf35f94581cc13c554761c94089fa0d9f704d448de6b9012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844", + "asm": "3044022057122ae2361a059213e362c3da275efe2efd3273396e8e7353238fe140011a9d02206379d6a1eb8463bfaf0cf35f94581cc13c554761c94089fa0d9f704d448de6b9[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844" + }, + "addr": "yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5", + "valueSat": 10000000000, + "value": 100, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "cdcf81b69629c3157f09878076bc4f544aa01477cf59915461343476772a4a84": { + "txid": "cdcf81b69629c3157f09878076bc4f544aa01477cf59915461343476772a4a84", + "blockhash": "000001c112eca9b3ee6b52a6aca271e1fe848951f1f666e87783f806ad445e87", + "blockheight": 3479, + "blocktime": 1548144723, + "fees": 2050, + "size": 226, + "vout": [ + { + "value": "10.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a9146e55f6575a5a735e028d28e6904b1d1759c5948d88ac", + "asm": "OP_DUP OP_HASH160 6e55f6575a5a735e028d28e6904b1d1759c5948d OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yWNrA4srrAjC9DT6UCu8NgpcqwQWa35dFX" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "989.99997950", + "n": 1, + "scriptPubKey": { + "hex": "76a914a6183356019635d8b4401d5c32b5defaf7b4b63c88ac", + "asm": "OP_DUP OP_HASH160 a6183356019635d8b4401d5c32b5defaf7b4b63c OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ybTg1Xema7wsGHGxSMQUSNoxyYRkTMUWJd" + ], + "type": "pubkeyhash" + }, + "spentTxId": "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059", + "spentIndex": 0, + "spentHeight": 3537 + } + ], + "vin": [ + { + "txid": "507e56181d03ba75b133f93cd073703c5c514f623f30e4cc32144c62b5a697c4", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100a8d824fe69662b5aba42dbcf139ad08a1212785734502b9b06244b7e55933c670220780667bcc9a798fe925c00c2d20f2fdaa93e86580c123b6deec8007fa7b1b3d30121037bc909c83d0571bca2589a12af9963a77d375c39d5bd39eb33495c09c60f917d", + "asm": "3045022100a8d824fe69662b5aba42dbcf139ad08a1212785734502b9b06244b7e55933c670220780667bcc9a798fe925c00c2d20f2fdaa93e86580c123b6deec8007fa7b1b3d3[ALL] 037bc909c83d0571bca2589a12af9963a77d375c39d5bd39eb33495c09c60f917d" + }, + "addr": "yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT", + "valueSat": 100000000000, + "value": 1000, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + } + }, + "chains": { + "testnet": { + "name": "testnet", + "blockheight": 6353 + } + } +} \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/duringdevelop-fullstore-snapshot-1549310417.json b/packages/wallet-lib/fixtures/duringdevelop-fullstore-snapshot-1549310417.json new file mode 100644 index 00000000000..ee7d690e17e --- /dev/null +++ b/packages/wallet-lib/fixtures/duringdevelop-fullstore-snapshot-1549310417.json @@ -0,0 +1,1248 @@ +{ + "transactions": { + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "blockhash": "000001fbcad67dbbb30523fa63fb0f260e77a79e6afa30680ed2aa5c12974abb", + "blockheight": 4790, + "blocktime": 1549299045, + "fees": 1125, + "size": 1110, + "vout": [ + { + "value": "100.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "100.00000000", + "n": 1, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "100.00000000", + "n": 2, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "100.00000000", + "n": 3, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "100.00000000", + "n": 4, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "100.00000000", + "n": 5, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "100.00000000", + "n": 6, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "100.00000000", + "n": 7, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "50.00000000", + "n": 8, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "10.00000000", + "n": 9, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "10.00000000", + "n": 10, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "10.00000000", + "n": 11, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "1.00000000", + "n": 12, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "1.00000000", + "n": 13, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "1.00000000", + "n": 14, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "1.00000000", + "n": 15, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "1.00000000", + "n": 16, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "1.00000000", + "n": 17, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "1.00000000", + "n": 18, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "1.00000000", + "n": 19, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "1.00000000", + "n": 20, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.10000000", + "n": 21, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.01000000", + "n": 22, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.00100000", + "n": 23, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.00010000", + "n": 24, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.00001000", + "n": 25, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.00000100", + "n": 26, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "10.88887528", + "n": 27, + "scriptPubKey": { + "hex": "76a9144f8aa6c3e302911b8c6b0ecb0538d209c144f84988ac", + "asm": "OP_DUP OP_HASH160 4f8aa6c3e302911b8c6b0ecb0538d209c144f849 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yTa2L2ZJr48sbJCnYP96RwW1D4ceeCdyHS" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "txid": "9cec6df6996accf80be685732f06040ceda23c488ec33404da3b07bbf06dd244", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100e592667fd0c6fc2eaf56c5b56cf9835a3327d890f2dc3638e8554556e09321bd02207af646205cb145eff6fadc4279f600f58c1ae01d45bf1e2598bb1030bbbd06ed012102420b7af29c43e5d2da5f23a30e0ce994b99e76ebcd2ad7605bd32418aa0c829f", + "asm": "3045022100e592667fd0c6fc2eaf56c5b56cf9835a3327d890f2dc3638e8554556e09321bd02207af646205cb145eff6fadc4279f600f58c1ae01d45bf1e2598bb1030bbbd06ed[ALL] 02420b7af29c43e5d2da5f23a30e0ce994b99e76ebcd2ad7605bd32418aa0c829f" + }, + "addr": "ybTg1Xema7wsGHGxSMQUSNoxyYRkTMUWJd", + "valueSat": 89999999753, + "value": 899.99999753, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "9cec6df6996accf80be685732f06040ceda23c488ec33404da3b07bbf06dd244": { + "txid": "9cec6df6996accf80be685732f06040ceda23c488ec33404da3b07bbf06dd244", + "blockhash": "0000010f1623651132401f8c888d25b478f76a5c7be318c16561bba18a72b2d3", + "blockheight": 4661, + "blocktime": 1549280939, + "fees": 247, + "size": 225, + "vout": [ + { + "value": "100.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "899.99999753", + "n": 1, + "scriptPubKey": { + "hex": "76a914a6183356019635d8b4401d5c32b5defaf7b4b63c88ac", + "asm": "OP_DUP OP_HASH160 a6183356019635d8b4401d5c32b5defaf7b4b63c OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ybTg1Xema7wsGHGxSMQUSNoxyYRkTMUWJd" + ], + "type": "pubkeyhash" + }, + "spentTxId": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "spentIndex": 0, + "spentHeight": 4790 + } + ], + "vin": [ + { + "txid": "1f5e5498cbe7de7635cd95e7baf7288687f34c79311cf62097bf35c1051cd163", + "vout": 0, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "47304402207fb7b8c0dd5c732af91c2edda873cd45c189efbee31383e82bf4bfe43e3d291302205acdf4bc18b1fd29a9247e8882f32b218c7773b299e0d5633f3d8413e16408540121037bc909c83d0571bca2589a12af9963a77d375c39d5bd39eb33495c09c60f917d", + "asm": "304402207fb7b8c0dd5c732af91c2edda873cd45c189efbee31383e82bf4bfe43e3d291302205acdf4bc18b1fd29a9247e8882f32b218c7773b299e0d5633f3d8413e1640854[ALL] 037bc909c83d0571bca2589a12af9963a77d375c39d5bd39eb33495c09c60f917d" + }, + "addr": "yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT", + "valueSat": 100000000000, + "value": 1000, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "1f5e5498cbe7de7635cd95e7baf7288687f34c79311cf62097bf35c1051cd163": { + "txid": "1f5e5498cbe7de7635cd95e7baf7288687f34c79311cf62097bf35c1051cd163", + "blockhash": "00000082cf918c30bff71f556d2b6580018b5fa954fff265f3e482672c6fe441", + "blockheight": 4645, + "blocktime": 1549278195, + "fees": 340, + "size": 338, + "vout": [ + { + "value": "1000.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a91419ba12c566e1e21deafa60f2a6de42d15f85dbbc88ac", + "asm": "OP_DUP OP_HASH160 19ba12c566e1e21deafa60f2a6de42d15f85dbbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT" + ], + "type": "pubkeyhash" + }, + "spentTxId": "9cec6df6996accf80be685732f06040ceda23c488ec33404da3b07bbf06dd244", + "spentIndex": 0, + "spentHeight": 4661 + } + ], + "vin": [ + { + "txid": "0f48a1e475ba275fc91bce70e76979acaf58c2fe7869327b1feaffeebb369577", + "vout": 1, + "sequence": 4294967294, + "n": 0, + "scriptSig": { + "hex": "47304402200b53f39e8c79376c58dae2147322a6cebf465d584144b9d126b27bfa0b3ecf0c0220335e1ee8cdc9e5e45f56ea85c6fe409083a31059ac893f901d9981847b1f2e47012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844", + "asm": "304402200b53f39e8c79376c58dae2147322a6cebf465d584144b9d126b27bfa0b3ecf0c0220335e1ee8cdc9e5e45f56ea85c6fe409083a31059ac893f901d9981847b1f2e47[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844" + }, + "addr": "yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5", + "valueSat": 67500000000, + "value": 675, + "doubleSpentTxID": null + }, + { + "txid": "a6c492c68bd689fdd7fc19338ca1c1a198822585ac233f96d2247a0f6e6b0ca5", + "vout": 0, + "sequence": 4294967294, + "n": 1, + "scriptSig": { + "hex": "4730440220524d6633a02101b07dd9b23ab0b26a278847e314e8d069c1804e97d90593f16c02202c42b6dfa8cc988f0952283b64f8cc06d29bcb78c4f10721ad90f414a6878f66012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844", + "asm": "30440220524d6633a02101b07dd9b23ab0b26a278847e314e8d069c1804e97d90593f16c02202c42b6dfa8cc988f0952283b64f8cc06d29bcb78c4f10721ad90f414a6878f66[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844" + }, + "addr": "yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5", + "valueSat": 32500000340, + "value": 325.0000034, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + } + }, + "wallets": { + "5061b8276c": { + "accounts": { + "m/44'/1'/0'": { + "label": null, + "path": "m/44'/1'/0'", + "network": { + "name": "testnet", + "alias": "regtest", + "pubkeyhash": 140, + "privatekey": 239, + "scripthash": 19, + "xpubkey": 70617039, + "xprivkey": 70615956, + "port": 19999, + "networkMagic": { + "type": "Buffer", + "data": [ + 206, + 226, + 202, + 255 + ] + }, + "dnsSeeds": [ + "testnet-seed.darkcoin.io", + "testnet-seed.dashdot.io", + "test.dnsseed.masternode.io" + ] + } + } + }, + "network": { + "name": "testnet", + "alias": "regtest", + "pubkeyhash": 140, + "privatekey": 239, + "scripthash": 19, + "xpubkey": 70617039, + "xprivkey": 70615956, + "port": 19999, + "networkMagic": { + "type": "Buffer", + "data": [ + 206, + 226, + 202, + 255 + ] + }, + "dnsSeeds": [ + "testnet-seed.darkcoin.io", + "testnet-seed.dashdot.io", + "test.dnsseed.masternode.io" + ] + }, + "mnemonic": null, + "type": null, + "blockheight": 0, + "addresses": { + "external": { + "m/44'/1'/0'/0/0": { + "address": "yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT", + "path": "m/44'/1'/0'/0/0", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "9cec6df6996accf80be685732f06040ceda23c488ec33404da3b07bbf06dd244", + "1f5e5498cbe7de7635cd95e7baf7288687f34c79311cf62097bf35c1051cd163" + ], + "fetchedLast": 1549310216188, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/0/1": { + "address": "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe", + "path": "m/44'/1'/0'/0/1", + "balanceSat": 98911111100, + "unconfirmedBalanceSat": 0, + "transactions": [ + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "9cec6df6996accf80be685732f06040ceda23c488ec33404da3b07bbf06dd244" + ], + "fetchedLast": 1549310216134, + "used": true, + "utxos": { + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-0": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 0, + "satoshis": 10000000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-1": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 1, + "satoshis": 10000000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-2": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 2, + "satoshis": 10000000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-3": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 3, + "satoshis": 10000000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-4": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 4, + "satoshis": 10000000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-5": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 5, + "satoshis": 10000000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-6": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 6, + "satoshis": 10000000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-7": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 7, + "satoshis": 10000000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-8": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 8, + "satoshis": 5000000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-9": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 9, + "satoshis": 1000000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-10": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 10, + "satoshis": 1000000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-11": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 11, + "satoshis": 1000000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-12": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 12, + "satoshis": 100000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-13": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 13, + "satoshis": 100000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-14": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 14, + "satoshis": 100000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-15": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 15, + "satoshis": 100000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-16": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 16, + "satoshis": 100000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-17": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 17, + "satoshis": 100000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-18": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 18, + "satoshis": 100000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-19": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 19, + "satoshis": 100000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-20": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 20, + "satoshis": 100000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-21": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 21, + "satoshis": 10000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-22": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 22, + "satoshis": 1000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-23": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 23, + "satoshis": 100000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-24": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 24, + "satoshis": 10000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-25": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 25, + "satoshis": 1000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-26": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 26, + "satoshis": 100, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + }, + "9cec6df6996accf80be685732f06040ceda23c488ec33404da3b07bbf06dd244-0": { + "txid": "9cec6df6996accf80be685732f06040ceda23c488ec33404da3b07bbf06dd244", + "outputIndex": 0, + "satoshis": 10000000000, + "scriptPubKey": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac" + } + } + }, + "m/44'/1'/0'/0/2": { + "address": "yQSVFizTKcPLz2V7zoZ3HkkJ7sQmb5jXAs", + "path": "m/44'/1'/0'/0/2", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216144, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/3": { + "address": "yhnTNo6tkmr8tA4SAL8gcci1z5rPHuaoxA", + "path": "m/44'/1'/0'/0/3", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216122, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/4": { + "address": "yNY6spErvvm9C8at2KQpvAfd6TPumgyETh", + "path": "m/44'/1'/0'/0/4", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216104, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/5": { + "address": "yaVrJ5dgELFkYwv6AydDyGPAJQ5kTJXyAN", + "path": "m/44'/1'/0'/0/5", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216193, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/6": { + "address": "yMN2w8NiwcmY3zvJLeeBxpaExFV1aN23pg", + "path": "m/44'/1'/0'/0/6", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216102, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/7": { + "address": "yQamgF8LvZ9nV1xrbZ1hUiHjmttznbdVnS", + "path": "m/44'/1'/0'/0/7", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216152, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/8": { + "address": "ya2aCFRiEQ6SHiB9yETaEGEcs6gNSqJt9d", + "path": "m/44'/1'/0'/0/8", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216145, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/9": { + "address": "ygZJwpqD7y9SP7t1jk4reytSCW6M16aEfC", + "path": "m/44'/1'/0'/0/9", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216172, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/10": { + "address": "ybHHUqRWE35R7gohkXHQVJELoGkduywroY", + "path": "m/44'/1'/0'/0/10", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216140, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/11": { + "address": "yVe8ftxg2aubVa5gppnz4WZJ7Umgu1UEjh", + "path": "m/44'/1'/0'/0/11", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216121, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/12": { + "address": "ydhUVrSYdRKTm1bztWCgFSgSqF1HKKL25i", + "path": "m/44'/1'/0'/0/12", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216144, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/13": { + "address": "yaUfQiKzL29LkXYZiEGM5DmMuMBn5e81MV", + "path": "m/44'/1'/0'/0/13", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216154, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/14": { + "address": "yTZfTfqgv5r9CW4jyMsr3PnhYhGh7scFDj", + "path": "m/44'/1'/0'/0/14", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216114, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/15": { + "address": "yhxUAk6St4CdhYifJqEAaY9jJVd4mZyEmy", + "path": "m/44'/1'/0'/0/15", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216149, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/16": { + "address": "yRs2aA9UjfEaCpNF3wf8L2m62EfGkQdD8u", + "path": "m/44'/1'/0'/0/16", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216148, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/17": { + "address": "yVPAnRANHXZnrL5P5SwJCWKRru7PgvMhYg", + "path": "m/44'/1'/0'/0/17", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216104, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/18": { + "address": "yhUPdqWmmJPT4wE95QzmTvq8vkUJE6LswZ", + "path": "m/44'/1'/0'/0/18", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216155, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/19": { + "address": "yLwNeRjtHHqW1d7KVR5AXnELfB8vp9iuvk", + "path": "m/44'/1'/0'/0/19", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216139, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/20": { + "address": "yeEjYLeiHMhABkG2RUtTdXgZKfz7L5uohT", + "path": "m/44'/1'/0'/0/20", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216142, + "used": false, + "utxos": {} + } + }, + "internal": { + "m/44'/1'/0'/1/0": { + "address": "yTa2L2ZJr48sbJCnYP96RwW1D4ceeCdyHS", + "path": "m/44'/1'/0'/1/0", + "balanceSat": 1088887528, + "unconfirmedBalanceSat": 0, + "transactions": [ + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94" + ], + "fetchedLast": 1549310216205, + "used": true, + "utxos": { + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94-27": { + "txid": "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "outputIndex": 27, + "satoshis": 1088887528, + "scriptPubKey": "76a9144f8aa6c3e302911b8c6b0ecb0538d209c144f84988ac" + } + } + }, + "m/44'/1'/0'/1/1": { + "address": "ybTg1Xema7wsGHGxSMQUSNoxyYRkTMUWJd", + "path": "m/44'/1'/0'/1/1", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "0de8c045009815ca8e7be0f461dc1569c89d8822cded87a6cecacbff2e8c6a94", + "9cec6df6996accf80be685732f06040ceda23c488ec33404da3b07bbf06dd244" + ], + "fetchedLast": 1549310216181, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/1/2": { + "address": "yLVQ9bZBLZmmvNQk7pPCUAGaXADQ6Rhkqt", + "path": "m/44'/1'/0'/1/2", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216175, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/3": { + "address": "yU7hmdDdi9RWem64hMz3GV3i9UWHNNK2FS", + "path": "m/44'/1'/0'/1/3", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216181, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/4": { + "address": "yfyTKf2PaxFvND6V5pEFWpnrbcSdy3igZQ", + "path": "m/44'/1'/0'/1/4", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216202, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/5": { + "address": "yeLbU1At3Cp4RD7Gunic6iy6orgnoNDhEb", + "path": "m/44'/1'/0'/1/5", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216174, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/6": { + "address": "ySVpgHLkgrrrsbaWJhW5GMHZjeSkADrsTJ", + "path": "m/44'/1'/0'/1/6", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216191, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/7": { + "address": "yPuyPUdk32KP6iE9359TuqWMv8azrqtHrk", + "path": "m/44'/1'/0'/1/7", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216196, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/8": { + "address": "yaZFt1VnAbi72mtyjDNV4AwTECqdg5Bv95", + "path": "m/44'/1'/0'/1/8", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216197, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/9": { + "address": "yR7bqPdf8UT5ZFpCiqwAiz69xHco7QJqyp", + "path": "m/44'/1'/0'/1/9", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216185, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/10": { + "address": "yRDW3KVi5vdnooaJxfXW2x9XKe8mz5XgRC", + "path": "m/44'/1'/0'/1/10", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216186, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/11": { + "address": "yfTs2aiRuWNdgnAW9gcf7sv2A3qpATRzHJ", + "path": "m/44'/1'/0'/1/11", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216197, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/12": { + "address": "ybqMAktAf9mktXHVN1p9BmfgUZzUnELkrw", + "path": "m/44'/1'/0'/1/12", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216178, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/13": { + "address": "ySBGFjj74zmCqmcMxCBr7JvgvYqjzsgZJp", + "path": "m/44'/1'/0'/1/13", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216183, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/14": { + "address": "yZdS1zoVDbGVRYgV3tnwGhhQMX7vw3rpBj", + "path": "m/44'/1'/0'/1/14", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216200, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/15": { + "address": "yYoAZ8hHN48usWRBuiMpbZ6aVifAw1E7TV", + "path": "m/44'/1'/0'/1/15", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216184, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/16": { + "address": "yhjj9bfgZrem1ytWby8dcTiJ9pA8Q4tC6J", + "path": "m/44'/1'/0'/1/16", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216175, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/17": { + "address": "yVycuCgivTbSpMfn9yDnjrCX3QzLgZRXDr", + "path": "m/44'/1'/0'/1/17", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216186, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/18": { + "address": "ydJsVhE6houFRWiNvFUYqhMUK5RHnuoqK9", + "path": "m/44'/1'/0'/1/18", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216196, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/19": { + "address": "yck7wS32ydQ8AxRw7NZJUtiRStbMSYGi6W", + "path": "m/44'/1'/0'/1/19", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216178, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/20": { + "address": "yU7sNM4j6fzKtbah24gCXdN636piQN8F2f", + "path": "m/44'/1'/0'/1/20", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1549310216197, + "used": false, + "utxos": {} + } + }, + "misc": {} + } + } + }, + "chains": { + "testnet": { + "name": "testnet", + "blockheight": 4878 + } + } +} diff --git a/packages/wallet-lib/fixtures/figurebridge.json b/packages/wallet-lib/fixtures/figurebridge.json new file mode 100644 index 00000000000..dad835be061 --- /dev/null +++ b/packages/wallet-lib/fixtures/figurebridge.json @@ -0,0 +1,1225 @@ +{ + "mnemonic": "figure bridge cupboard reduce note fatal idea agent uphold media almost announce", + "transactions": { + "3428f0c29370d1293b4706ffd0f8b0c84a5b7c1c217d319e5ef4722354000c6e": { + "txid": "3428f0c29370d1293b4706ffd0f8b0c84a5b7c1c217d319e5ef4722354000c6e", + "blockhash": "0000000002c51fbd8d68917dc34ed68716a5f5c956616b5dc9caf5a79dee16b2", + "blockheight": 255974, + "blocktime": 1541085563, + "fees": 7182, + "size": 7156, + "vout": [ + { + "value": "0.42849618", + "n": 0, + "scriptPubKey": { + "hex": "76a914ede7b57b7b7be7a143a88d0c44a60837a6d9293c88ac", + "asm": "OP_DUP OP_HASH160 ede7b57b7b7be7a143a88d0c44a60837a6d9293c OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yi1Na5rgrwwR9mv167xRgmNzqJvtHEt1eT" + ], + "type": "pubkeyhash" + }, + "spentTxId": "3cb7228af225c2a360c1ff8243536e8dd98113a8fc6e9f4f7ef38da4ee1bfeca", + "spentIndex": 29, + "spentHeight": 256388 + }, + { + "value": "501.00000000", + "n": 1, + "scriptPubKey": { + "hex": "76a9144127fb3bed3a27544198e62d456bdf3cae7f537988ac", + "asm": "OP_DUP OP_HASH160 4127fb3bed3a27544198e62d456bdf3cae7f5379 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ySFxfdLGzwSM5pcr5gFF4DJ6uuNTXrKaFP" + ], + "type": "pubkeyhash" + }, + "spentTxId": "56150e17895255d178eb4d3da0ccd580fdf50233a3767e1f562e05f00b48cf79", + "spentIndex": 0, + "spentHeight": 255976 + } + ], + "vin": [ + { + "txid": "03593ded7ee5bd5ef117735cd2eeef25081c7fe9154de8d81964f10a0a1b85a8", + "vout": 1, + "sequence": 4294967294, + "n": 0, + "scriptSig": { + "hex": "47304402205795935c4b229ef57f28d186deaa847661f69b630e8aca68583710821b3fb6d60220266816e40149028be508ce2fd5494532456e54c00324baa783d0469bf19a40c90121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304402205795935c4b229ef57f28d186deaa847661f69b630e8aca68583710821b3fb6d60220266816e40149028be508ce2fd5494532456e54c00324baa783d0469bf19a40c9[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "0b229900906d0b0ecd431296d2336fb90b0810e218b9817eb6c1a203647ccdca", + "vout": 1, + "sequence": 4294967294, + "n": 1, + "scriptSig": { + "hex": "483045022100bec5a46fddfb28582bb7f83c5d21276fa6e77a70c2faa8ee728d05ee12f8ae8d02207251dbac62e6e4d9f2471fee17b336eab688555591e8e356d52792d30e5aacda0121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "3045022100bec5a46fddfb28582bb7f83c5d21276fa6e77a70c2faa8ee728d05ee12f8ae8d02207251dbac62e6e4d9f2471fee17b336eab688555591e8e356d52792d30e5aacda[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "0cdab20b3b07ed46685c100c7ac7044dcdcdd7e2bbb75f8d4de692a3d6e3bd23", + "vout": 1, + "sequence": 4294967294, + "n": 2, + "scriptSig": { + "hex": "47304402204159a6279277b1a0652181ac8223cc6f201d0d9f34ce20eee194f18c4f1ac09902202a99969b152a73c04dcc3c1326ef55d7f7a5742741d9cb6a4f61d92caf6e83060121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304402204159a6279277b1a0652181ac8223cc6f201d0d9f34ce20eee194f18c4f1ac09902202a99969b152a73c04dcc3c1326ef55d7f7a5742741d9cb6a4f61d92caf6e8306[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "14ab9e366a66a51378c9b5c337cbfcef64557aa12be4734dd8ce9bdd7a00bb92", + "vout": 1, + "sequence": 4294967294, + "n": 3, + "scriptSig": { + "hex": "483045022100a7d902897be8263e67384fce44ad6c6357e85a59b17bbeceb7db33c5b6c9cc7d02201e6bae9d14f65fa0ac9b4f0fe0b72c289e81e7faca41a7fe33b9dde938e2d7530121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "3045022100a7d902897be8263e67384fce44ad6c6357e85a59b17bbeceb7db33c5b6c9cc7d02201e6bae9d14f65fa0ac9b4f0fe0b72c289e81e7faca41a7fe33b9dde938e2d753[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "1712008fa12e77bd867242f28a76c32d372fedfa2462730ede72cb29e63aff45", + "vout": 1, + "sequence": 4294967294, + "n": 4, + "scriptSig": { + "hex": "4730440220565db3639ab838b46d0a7a367aade7492a5c17f52cf24de3713a42b5886041b70220582051d1389bafbd1713f0f005174f28e3ac7d27ecf958ad92dbf62d6c2f17e20121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "30440220565db3639ab838b46d0a7a367aade7492a5c17f52cf24de3713a42b5886041b70220582051d1389bafbd1713f0f005174f28e3ac7d27ecf958ad92dbf62d6c2f17e2[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "24008ea93304c1779bf39e3469ec632cebeddaac5414a8d363f3b9868ce07517", + "vout": 1, + "sequence": 4294967294, + "n": 5, + "scriptSig": { + "hex": "47304402201234ec15bf227b81d3957bb4ed6d642809d188f4335002f799a53aec77cf20ac02206dede569d24deefc25659eb3adda15b5884ad8d6ddbeca5d3e2079c17e6b5cd00121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304402201234ec15bf227b81d3957bb4ed6d642809d188f4335002f799a53aec77cf20ac02206dede569d24deefc25659eb3adda15b5884ad8d6ddbeca5d3e2079c17e6b5cd0[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "2829ab9bf711dab7348f205d0830ded311b642a26f3253a1dd55013d2ec6e8e0", + "vout": 1, + "sequence": 4294967294, + "n": 6, + "scriptSig": { + "hex": "4730440220314cb79c5a54483907bb2cf8164d604e60ac59d04cc5fd1d7f1a5667aea375d70220611eaf1d0e9e6f5677f4dcacc6aa28fbf20a3a7732c2f1845fe6fd94a47c3b3b0121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "30440220314cb79c5a54483907bb2cf8164d604e60ac59d04cc5fd1d7f1a5667aea375d70220611eaf1d0e9e6f5677f4dcacc6aa28fbf20a3a7732c2f1845fe6fd94a47c3b3b[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "29bd813ec3404318cde2080234e973709a33fdc0ecf2492886f1ba09e547f482", + "vout": 1, + "sequence": 4294967294, + "n": 7, + "scriptSig": { + "hex": "483045022100c2428b032308139399be4beaf1b7a85a9ccd28bfe6651525fbfdb57690b1760a0220092eb427221ed91ca109ab7501ce12424949f3c26b88f462ecd54d64402f40bf0121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "3045022100c2428b032308139399be4beaf1b7a85a9ccd28bfe6651525fbfdb57690b1760a0220092eb427221ed91ca109ab7501ce12424949f3c26b88f462ecd54d64402f40bf[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "2ae72cbe982e3c37cc2a2e2880c5515b83a8b474556ccbca5935052e34925859", + "vout": 1, + "sequence": 4294967294, + "n": 8, + "scriptSig": { + "hex": "483045022100c4a4cafee589e93c210bce61f498fe83945cb65bb9f913dc34111a8aedd703e2022078b6a0f5f0537611975600dcd4bb9b75800bded514f2cdbf5f53f6da05ea02cf012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "3045022100c4a4cafee589e93c210bce61f498fe83945cb65bb9f913dc34111a8aedd703e2022078b6a0f5f0537611975600dcd4bb9b75800bded514f2cdbf5f53f6da05ea02cf[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "3308ac3fa66756c16d6107c5ae17915ee06ed77da4f7b5ec3ec5ff3696c9c5fc", + "vout": 1, + "sequence": 4294967294, + "n": 9, + "scriptSig": { + "hex": "47304402206b318d5f96157b4f63a0f5bd897d58b14d811a3cd1ddfc4e6b98c910f4bbd3b50220678eb1b0b3edf8846cb1e2d3f9bdb9fa143f6bcf24106aafa875322b7180892e0121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304402206b318d5f96157b4f63a0f5bd897d58b14d811a3cd1ddfc4e6b98c910f4bbd3b50220678eb1b0b3edf8846cb1e2d3f9bdb9fa143f6bcf24106aafa875322b7180892e[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "3463b3b2f37c6dcdf23ae486a3d426230dbc0c32c12d9811e91414eca9f997a2", + "vout": 1, + "sequence": 4294967294, + "n": 10, + "scriptSig": { + "hex": "47304402201d1fc53affe59ca06f4f0a9348e3cfaa8c5e26b22a1125cf7f674eb50d53f72b02206eb1f9a8a4873a82423a3a727288620a8e1c27a9e1b9d20a4f90c882b7b94a710121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304402201d1fc53affe59ca06f4f0a9348e3cfaa8c5e26b22a1125cf7f674eb50d53f72b02206eb1f9a8a4873a82423a3a727288620a8e1c27a9e1b9d20a4f90c882b7b94a71[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "41af033ecf660d9064510fbaf0fee12defee2632ad9942a05904e7383480f4a1", + "vout": 1, + "sequence": 4294967294, + "n": 11, + "scriptSig": { + "hex": "47304402200712c6a0cfa28ba0ae8068b0a8f43a870ab72af1a35fc2147d0b4bb5d657f19a02206fef32ee5f0f0419287c22cadba4d7aa3873bf0575660d9fadfa1fc8882477c9012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "304402200712c6a0cfa28ba0ae8068b0a8f43a870ab72af1a35fc2147d0b4bb5d657f19a02206fef32ee5f0f0419287c22cadba4d7aa3873bf0575660d9fadfa1fc8882477c9[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "451e53fbd3efd192e31a7edd7ce49590cb2be379c6fe59ec92df77d8e1a5fc76", + "vout": 1, + "sequence": 4294967294, + "n": 12, + "scriptSig": { + "hex": "483045022100c1fae39ee4a5b8f48f3d8417f46464d69fc264af0529747790ca6d473fbc65be022021aad59db0ff38c0c001871c384d1e2c13d0022f6ced0bb4f6ee3f8615c2fdf3012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "3045022100c1fae39ee4a5b8f48f3d8417f46464d69fc264af0529747790ca6d473fbc65be022021aad59db0ff38c0c001871c384d1e2c13d0022f6ced0bb4f6ee3f8615c2fdf3[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "5073b56733aef01072137362de8494c3e544f06803a9759ce2d4d006fe951092", + "vout": 1, + "sequence": 4294967294, + "n": 13, + "scriptSig": { + "hex": "473044022071b7b700546bc440b1b3ef2ee877a0423cb12666800661c6f15dc191c5dadd34022072e153be8178bd9bbf22cebf4c342d96c789cbc1186feef959facd0854b4b598012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "3044022071b7b700546bc440b1b3ef2ee877a0423cb12666800661c6f15dc191c5dadd34022072e153be8178bd9bbf22cebf4c342d96c789cbc1186feef959facd0854b4b598[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "51f445a230ebc8e47db4a0f27777529cf1dc7a390abdc792be5467e85f133e06", + "vout": 1, + "sequence": 4294967294, + "n": 14, + "scriptSig": { + "hex": "483045022100c1dc6475f95f7810ebbea7a7164f878606d7e70ed868e2150bfff2c6c3299bb302204fd7d26ec5f34fce50bd0e16e96a2c82a7f450b6b8904dc406bc9ef4ea9a4aae012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "3045022100c1dc6475f95f7810ebbea7a7164f878606d7e70ed868e2150bfff2c6c3299bb302204fd7d26ec5f34fce50bd0e16e96a2c82a7f450b6b8904dc406bc9ef4ea9a4aae[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "54e7370ace3af310f41270c463b9ec5d14220651d121bbd133c997de1061e6d4", + "vout": 1, + "sequence": 4294967294, + "n": 15, + "scriptSig": { + "hex": "483045022100be5296927414fbd1fe96eaa54b4e169002b337be199f13ebd1a1aaa11a4cf8e502201fe74cb75235624941556b52159a81f04941ed9a0878e57de00c7cc7dde096e20121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "3045022100be5296927414fbd1fe96eaa54b4e169002b337be199f13ebd1a1aaa11a4cf8e502201fe74cb75235624941556b52159a81f04941ed9a0878e57de00c7cc7dde096e2[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "58b8f5f375f9ad147db831badd0f81b6122f6dc1bea0189571cd2e436058c65e", + "vout": 1, + "sequence": 4294967294, + "n": 16, + "scriptSig": { + "hex": "47304402207729bcc04adcfbe182e205893ff6c0bbf7bea1332367f2e1594e89463754861402207d09279f660cc43e5a17f2861a3de3ebe4d545a5603485c153763c5c5daec6c0012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "304402207729bcc04adcfbe182e205893ff6c0bbf7bea1332367f2e1594e89463754861402207d09279f660cc43e5a17f2861a3de3ebe4d545a5603485c153763c5c5daec6c0[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "5bbc980a18f8feb8366ad28db39a44061d4889b3ec42ff7cc470b897e681fdd5", + "vout": 1, + "sequence": 4294967294, + "n": 17, + "scriptSig": { + "hex": "4730440220496fe50fe78bc04247701ed6598adc510364dd8f8d0812635e84d68982d9449702202c9d98ca03b4705a787f97c4c6cd5137ac9c88bcaa402e826e0a1873c51a78f90121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "30440220496fe50fe78bc04247701ed6598adc510364dd8f8d0812635e84d68982d9449702202c9d98ca03b4705a787f97c4c6cd5137ac9c88bcaa402e826e0a1873c51a78f9[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "621157e4f463c94193d031241a007d023689be23d94d96e5d614ea3a3d93ce43", + "vout": 1, + "sequence": 4294967294, + "n": 18, + "scriptSig": { + "hex": "47304402207329f0d8fd2101a8d87d4d7b0333ee28142163a6cd014a350f6a3cc0cf2ffc1502205c00e9b2a7dd301ab8147e5e65c55b7592dfbc557876b009ef4629caa4e85ce1012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "304402207329f0d8fd2101a8d87d4d7b0333ee28142163a6cd014a350f6a3cc0cf2ffc1502205c00e9b2a7dd301ab8147e5e65c55b7592dfbc557876b009ef4629caa4e85ce1[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "6ba04c1ac39582a0c3818dee0ffc5c6b986f4d9f362d2ed288734ad8908f3768", + "vout": 1, + "sequence": 4294967294, + "n": 19, + "scriptSig": { + "hex": "473044022006cd97c4a62775c14ae96e03ea660baf660f4798df9f6d18efac306d39c60e20022029b3f135d937878f24d3ed5eeea17b5eb421e204aa067d9c99fe453ba040d18b012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "3044022006cd97c4a62775c14ae96e03ea660baf660f4798df9f6d18efac306d39c60e20022029b3f135d937878f24d3ed5eeea17b5eb421e204aa067d9c99fe453ba040d18b[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "6c94abc46ce087563651f4d6f72bd685ed17e53a9fa1d15bdb9fa272d7d3517e", + "vout": 1, + "sequence": 4294967294, + "n": 20, + "scriptSig": { + "hex": "483045022100eb6e4e6d4d3e19460cd6408dce591ae7dab358cd2ff47f80ea15371e49d55eb502202ae70cdc9817ae4b8a54e8ad5dac6a9ee6fda4cd0814d9ca3b1d17b694830d2b012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "3045022100eb6e4e6d4d3e19460cd6408dce591ae7dab358cd2ff47f80ea15371e49d55eb502202ae70cdc9817ae4b8a54e8ad5dac6a9ee6fda4cd0814d9ca3b1d17b694830d2b[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "6f2d70cda306eaa0e186708d8033a8429636bbafc75f4f476f332585550a5299", + "vout": 1, + "sequence": 4294967294, + "n": 21, + "scriptSig": { + "hex": "47304402201ce336df4ee1518926212444f62ee652d335965eb3e5762d680bb58143f079fe022032c3770ff5d0196ac09271ae6ec7f00f149df3b6c67de61ef4f277b73a1feb1f0121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304402201ce336df4ee1518926212444f62ee652d335965eb3e5762d680bb58143f079fe022032c3770ff5d0196ac09271ae6ec7f00f149df3b6c67de61ef4f277b73a1feb1f[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "7031eafcc5147a27c556a6960706e9b89adad81b7309c99df960e5d421e9656a", + "vout": 1, + "sequence": 4294967294, + "n": 22, + "scriptSig": { + "hex": "473044022050e19a9f303784ba30bfc01236bc77e20f7687e2ac3c8aeef8bf6ef49ce08e0802202b663f771f8708d392931fcd901cdffdcce95788fdc02374e6d05fda435502b8012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "3044022050e19a9f303784ba30bfc01236bc77e20f7687e2ac3c8aeef8bf6ef49ce08e0802202b663f771f8708d392931fcd901cdffdcce95788fdc02374e6d05fda435502b8[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "71a69d3bd9aca167fae9adc8adba55be41f6a739eb2d38f204825729db3e97a4", + "vout": 1, + "sequence": 4294967294, + "n": 23, + "scriptSig": { + "hex": "47304402203c03041d34b40240a3d2fc141484c5791470af3f7435ef7a7390dfecb0db9de0022067aaafa643df9e0eda20c540356b7fc94160b15bb8b7585e87b69c5e3fa85f5a0121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304402203c03041d34b40240a3d2fc141484c5791470af3f7435ef7a7390dfecb0db9de0022067aaafa643df9e0eda20c540356b7fc94160b15bb8b7585e87b69c5e3fa85f5a[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "798f841d7284e2d897536e3384eb982e21e3e89a0252166cdb92de130012050c", + "vout": 1, + "sequence": 4294967294, + "n": 24, + "scriptSig": { + "hex": "47304402201f94f4557908fad807c87c7d78b16bc0a4de6d17e7067c54d72325a1ca4ddb1002206e03f7063e1d22dda91e96cf2758dd7ce9efc30934fa9a9d245c134f0395b1060121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304402201f94f4557908fad807c87c7d78b16bc0a4de6d17e7067c54d72325a1ca4ddb1002206e03f7063e1d22dda91e96cf2758dd7ce9efc30934fa9a9d245c134f0395b106[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "8a083b850a6766d86b04eeb6436fc15489b98f5d08dc461521022bb0a4ded83c", + "vout": 1, + "sequence": 4294967294, + "n": 25, + "scriptSig": { + "hex": "483045022100d9a26f47283b7f8ffa476ed1df372d807acd7b90b8532bc0f7d314c2e9b3249a02201c624d35e343afd4c5e0fb7ac43bd3362ed78745cb0daac750b42809866b95fd012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "3045022100d9a26f47283b7f8ffa476ed1df372d807acd7b90b8532bc0f7d314c2e9b3249a02201c624d35e343afd4c5e0fb7ac43bd3362ed78745cb0daac750b42809866b95fd[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "8dff1165b86f7145fa7b3c40ead1b16dcf514e6f1b0974d6ae2d100247d41026", + "vout": 1, + "sequence": 4294967294, + "n": 26, + "scriptSig": { + "hex": "47304402205f969350b583c339ec448f66cf83af11a8c0d6c20f5acf3b98a8aa06098aa553022066c5bebbdd484eda8eb6fbdf71eff0e6c118a6bb8e09d269b5ea11d799cf2ea0012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "304402205f969350b583c339ec448f66cf83af11a8c0d6c20f5acf3b98a8aa06098aa553022066c5bebbdd484eda8eb6fbdf71eff0e6c118a6bb8e09d269b5ea11d799cf2ea0[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "9153e98f3cff35de3766d71e6d3c0fee5f14ed2aaf27f63a8b76f15dd55d46ff", + "vout": 1, + "sequence": 4294967294, + "n": 27, + "scriptSig": { + "hex": "4830450221008c2269184f0dba6e6bc4bb1db47140a4d80aa2df31a54b1cd0201ba7db85fff0022050af8267cb2aa77045a5c42f34275b2f6f7e1ab313694ec8d352df51d871959e0121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "30450221008c2269184f0dba6e6bc4bb1db47140a4d80aa2df31a54b1cd0201ba7db85fff0022050af8267cb2aa77045a5c42f34275b2f6f7e1ab313694ec8d352df51d871959e[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "917e420d335c67b372322d13b466d828f677a1d4b264dd2b56077d93b84c556a", + "vout": 1, + "sequence": 4294967294, + "n": 28, + "scriptSig": { + "hex": "473044022040f57676eaa16dfc86b272a57928af8fcd1e2a27b379d265d8eeba842be7a5f90220447d3f0c7b34572d7c97a34775f791901564a7a43ffe5d6d8a44631f69d96b67012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "3044022040f57676eaa16dfc86b272a57928af8fcd1e2a27b379d265d8eeba842be7a5f90220447d3f0c7b34572d7c97a34775f791901564a7a43ffe5d6d8a44631f69d96b67[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "955fc97beabda54a7e42ea2632c2a80e2b9ec3ed5bf881c4f0192c1baf6ee4e0", + "vout": 1, + "sequence": 4294967294, + "n": 29, + "scriptSig": { + "hex": "47304402201a77c341303777f8599ceefed830e6627c7c8f37dc9210ec17788bc7bd8d0aad0220329765ace413ac657bc3862891bb16565fdd69b1b1f20e79682c3c0a7f2493970121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304402201a77c341303777f8599ceefed830e6627c7c8f37dc9210ec17788bc7bd8d0aad0220329765ace413ac657bc3862891bb16565fdd69b1b1f20e79682c3c0a7f249397[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "95b30cc33a9c1e3f6b9b8938a92d4faffb810ef6c287cf282329e34672dcec76", + "vout": 1, + "sequence": 4294967294, + "n": 30, + "scriptSig": { + "hex": "483045022100b175b76a51416fe6651eed6ac433bac41918cad7d7465895dc366e3eb44ddeaa02204dbcda4d63c7eecacb8d9337d145816c4612178cc31b03e1d2145b32ced57a940121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "3045022100b175b76a51416fe6651eed6ac433bac41918cad7d7465895dc366e3eb44ddeaa02204dbcda4d63c7eecacb8d9337d145816c4612178cc31b03e1d2145b32ced57a94[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "99fac8df2a846511a3638fabbbd697d28f126d9fa41091fcb87ee3a15cf939ce", + "vout": 1, + "sequence": 4294967294, + "n": 31, + "scriptSig": { + "hex": "47304402202f353cf5ddf75a912044d56f7afa7c756eadb42a3c22c6a3ecbd1732815511a9022054ad31f31b7b377c01ceb71a5f3b0bb956e0f9df74e06f2bfc6c68cf25f90db40121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304402202f353cf5ddf75a912044d56f7afa7c756eadb42a3c22c6a3ecbd1732815511a9022054ad31f31b7b377c01ceb71a5f3b0bb956e0f9df74e06f2bfc6c68cf25f90db4[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "9ebdde102a058b956c0d9697944890d960c89cafd72e2d33e36ecacfaa9b440d", + "vout": 1, + "sequence": 4294967294, + "n": 32, + "scriptSig": { + "hex": "483045022100c81db183e08800ead7321953a0191edc643a35f24915d24063dbaca9f2795e9e02207907ae49277ae7a5a12a523408112ce52f3540200697c08d88a2a4816742bb1b0121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "3045022100c81db183e08800ead7321953a0191edc643a35f24915d24063dbaca9f2795e9e02207907ae49277ae7a5a12a523408112ce52f3540200697c08d88a2a4816742bb1b[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "a2e04fd56836da298895acae4e44fa2303bee471ae2de3a25c5ad8cd3b596205", + "vout": 1, + "sequence": 4294967294, + "n": 33, + "scriptSig": { + "hex": "483045022100ca71e9d6aae2b6d029d9c2dbbe1da3629c205cf80accd73a8ac28901d323234d02204238d5979d5d018c5c158d9b86b0d64df82057fb056dd678593d11e40f204a4e012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "3045022100ca71e9d6aae2b6d029d9c2dbbe1da3629c205cf80accd73a8ac28901d323234d02204238d5979d5d018c5c158d9b86b0d64df82057fb056dd678593d11e40f204a4e[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "a34f1dbd5590b1da7f790ac9f5906df6efd8ff7d95dcade29bc739ea96a6c11e", + "vout": 1, + "sequence": 4294967294, + "n": 34, + "scriptSig": { + "hex": "47304402200a02335316436b5073823c144524d37eaa1e0a936cd3768e2c59462d320c08b00220416b526f46fd57e1e2548031605d6d681474cfc5269c18ada980f325e958d1c80121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304402200a02335316436b5073823c144524d37eaa1e0a936cd3768e2c59462d320c08b00220416b526f46fd57e1e2548031605d6d681474cfc5269c18ada980f325e958d1c8[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "a61537d0ae656dfe383d75745cb59c8bc06d3a18f83d742191e04079bb0d96ce", + "vout": 1, + "sequence": 4294967294, + "n": 35, + "scriptSig": { + "hex": "483045022100bed8af442b2a1ef3ce4ad0053bf25c69dbea99552b6524cca2d4b2c04759f7090220667751e5d34802167015365129872ef1fa5c3ca30eae0536771298b8d0a693e10121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "3045022100bed8af442b2a1ef3ce4ad0053bf25c69dbea99552b6524cca2d4b2c04759f7090220667751e5d34802167015365129872ef1fa5c3ca30eae0536771298b8d0a693e1[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "a7d5d36bd6d34a98faeba37f1bfdbc3dd60c98004021b19ef5aa7a2748ba7af9", + "vout": 1, + "sequence": 4294967294, + "n": 36, + "scriptSig": { + "hex": "483045022100c3ec09e97c70259f74d25255051b6af6807510b3f4f2485b4e9472ec274e04ba022050e70bbc0d9e299e488ae6f2f52e0ef66978b79c84974fba290074831cdee5c20121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "3045022100c3ec09e97c70259f74d25255051b6af6807510b3f4f2485b4e9472ec274e04ba022050e70bbc0d9e299e488ae6f2f52e0ef66978b79c84974fba290074831cdee5c2[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "ac7e5cd99dd22796adf5563b1999f68c2d455108baf4d380e307389d43a3451a", + "vout": 1, + "sequence": 4294967294, + "n": 37, + "scriptSig": { + "hex": "48304502210089bfda143f477fd9fc695c1d36a7d3894e97ece07ae85901cb73c08dd29e768c02202550dc3744c111d3d705edc8d68351c521ebdc7be5d20b8d260b90b774b71bd1012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "304502210089bfda143f477fd9fc695c1d36a7d3894e97ece07ae85901cb73c08dd29e768c02202550dc3744c111d3d705edc8d68351c521ebdc7be5d20b8d260b90b774b71bd1[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "bf497e584b0fdebc9ca47a6e749375f79e2054436ef54c12be5d2de3ab70ded8", + "vout": 1, + "sequence": 4294967294, + "n": 38, + "scriptSig": { + "hex": "4730440220392e907e766579f577668cc784365ee526440ea9d840de9119619dd23891107602201a19f030e5dbc05261597bdc9fb9a8ebddefb263f4ac94279faa040e653f9b1d0121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "30440220392e907e766579f577668cc784365ee526440ea9d840de9119619dd23891107602201a19f030e5dbc05261597bdc9fb9a8ebddefb263f4ac94279faa040e653f9b1d[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "ce5c048878f7c49220eb8f2f0eac36a31e193800269723d4a8a1b4df93f41f78", + "vout": 1, + "sequence": 4294967294, + "n": 39, + "scriptSig": { + "hex": "4730440220157844e5523198c9fcc6d4c1221fab66efd5a99391b22cae9f9dd531bef811ac02203abc99f2e0f8c38b3cd9c3bea4f4246fd434454714022248c69654071337a3090121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "30440220157844e5523198c9fcc6d4c1221fab66efd5a99391b22cae9f9dd531bef811ac02203abc99f2e0f8c38b3cd9c3bea4f4246fd434454714022248c69654071337a309[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "d13746c6a4c80c182323b5388ff5f7af7f86240e36aeb219f909b4aea61fd51f", + "vout": 1, + "sequence": 4294967294, + "n": 40, + "scriptSig": { + "hex": "47304402200353c93cb64342765878c9ecf4bb8e66af951a86b6e2b5fce03aa198531be5260220595c9d7713f4e52b97daaf68a043fff39d977c8de79b2353160d5be24e037cac0121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304402200353c93cb64342765878c9ecf4bb8e66af951a86b6e2b5fce03aa198531be5260220595c9d7713f4e52b97daaf68a043fff39d977c8de79b2353160d5be24e037cac[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "d24a2d7d16193552e8804bb2f5c72f016c264051e6fc9e127f683d34e369e25f", + "vout": 1, + "sequence": 4294967294, + "n": 41, + "scriptSig": { + "hex": "48304502210097f5aa52f4acd326c31f3b107bb8f47449d88ce56a91700be83f7afb791333d10220266b613445ce6054d37b5ad720596332b9e207e35a8aa5c6f4cb58a49aeaf0c60121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304502210097f5aa52f4acd326c31f3b107bb8f47449d88ce56a91700be83f7afb791333d10220266b613445ce6054d37b5ad720596332b9e207e35a8aa5c6f4cb58a49aeaf0c6[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "d4654a5a8d8fd4fe72af9955c3f245e1e916d2ca4e3e35945d242dd985ec20e9", + "vout": 1, + "sequence": 4294967294, + "n": 42, + "scriptSig": { + "hex": "483045022100807ae6f8402a55e48abc2a79626b3e2429d66d29b772829b45ec1d84cd0fee84022074a44ec78cc5976b0330e1080008cb3b8c7662aeafea5e1ba13b54cf8076f39b0121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "3045022100807ae6f8402a55e48abc2a79626b3e2429d66d29b772829b45ec1d84cd0fee84022074a44ec78cc5976b0330e1080008cb3b8c7662aeafea5e1ba13b54cf8076f39b[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "d4ba7e06f56dcbaa1503556f41210d239ca5d97def831f29733a75e8606ff736", + "vout": 1, + "sequence": 4294967294, + "n": 43, + "scriptSig": { + "hex": "4830450221009c794f55d7875d29e53193648e00437913ac72db5ba40fca3a0b5ee9c8f633de022016cc27b991f942bc89169ee8e35220df227d01a4b59618a681a56b9748577de7012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "30450221009c794f55d7875d29e53193648e00437913ac72db5ba40fca3a0b5ee9c8f633de022016cc27b991f942bc89169ee8e35220df227d01a4b59618a681a56b9748577de7[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "d62e9dbfbf67bb8a42076992480ca0c97f4973e09e4076dd8cd95a8b6679bb5e", + "vout": 1, + "sequence": 4294967294, + "n": 44, + "scriptSig": { + "hex": "4830450221008959e14b3f0d2366ba59e82ea8432c44fa8ea8ca58de87666022389aecead72402200f76a7fa26cd3e6f264533c7b479a319b5666d4cdee75b610185bfbd6cfaf0ac012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "30450221008959e14b3f0d2366ba59e82ea8432c44fa8ea8ca58de87666022389aecead72402200f76a7fa26cd3e6f264533c7b479a319b5666d4cdee75b610185bfbd6cfaf0ac[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "dbcea053983a2f1161754dea5dac8816e1bfa4e64a049e0735005122304d1bda", + "vout": 1, + "sequence": 4294967294, + "n": 45, + "scriptSig": { + "hex": "47304402201b733f7566d16faea501a220c7bf768f5b2c20aee4fbd2b934f375463a2273c802207f9b6280a536a99c94fc5029ebe4d43619cbb44d9386700c267b1b7abce296c90121038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362", + "asm": "304402201b733f7566d16faea501a220c7bf768f5b2c20aee4fbd2b934f375463a2273c802207f9b6280a536a99c94fc5029ebe4d43619cbb44d9386700c267b1b7abce296c9[ALL] 038ffe213b30d981fa88b13e29605f257523731de42a04c39d2626f1d184243362" + }, + "addr": "yLMwMw9mcmH5r3C4jxxJsdbcsoaoVdDJYz", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "df981b16893221f098d2c8a0349678967ef8d6b65b75b153929419ad5c3f9e47", + "vout": 1, + "sequence": 4294967294, + "n": 46, + "scriptSig": { + "hex": "483045022100beca07e5a93975e4d8cdda3a7f72962d885425092af25f133ec03a1e9d66cd510220228f4ebbb52c9170329cbe40c4152752b2c6f996e2387c343daf62b2b9536e2d012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "3045022100beca07e5a93975e4d8cdda3a7f72962d885425092af25f133ec03a1e9d66cd510220228f4ebbb52c9170329cbe40c4152752b2c6f996e2387c343daf62b2b9536e2d[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + }, + { + "txid": "e36c8e654ba3b3c67f4d0d61b6275c6545ebcad2d0f99047f56995e60d65e74c", + "vout": 1, + "sequence": 4294967294, + "n": 47, + "scriptSig": { + "hex": "483045022100d50370212d00ad4d06797e8b2fa3d424f2676711e9ab20c09d37f4a56fb2fc570220413396acbc715d7e43adf4c98326fc68430f6dbb15edc9affa101d8b682bca2e012102d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116", + "asm": "3045022100d50370212d00ad4d06797e8b2fa3d424f2676711e9ab20c09d37f4a56fb2fc570220413396acbc715d7e43adf4c98326fc68430f6dbb15edc9affa101d8b682bca2e[ALL] 02d7d6a16ba363cafb0abc4fc184fb1c363dddce258879c8cb47bae2c84139e116" + }, + "addr": "yLTcumsTwPT2yQpzKq2w7HvbALWCLDTBHj", + "valueSat": 1044642850, + "value": 10.4464285, + "doubleSpentTxID": null + } + ], + "txlock": false + }, + "56150e17895255d178eb4d3da0ccd580fdf50233a3767e1f562e05f00b48cf79": { + "txid": "56150e17895255d178eb4d3da0ccd580fdf50233a3767e1f562e05f00b48cf79", + "blockhash": "00000000080667e6f1d42af30d0c061261a5ed287bb4365fbf8b2e24c5d0954c", + "blockheight": 255976, + "blocktime": 1541086066, + "fees": 19440, + "size": 226, + "vout": [ + { + "value": "500.90183751", + "n": 0, + "scriptPubKey": { + "hex": "76a9142c6f2c3d8d6bc68f633e4ce4faebec44c941fd2b88ac", + "asm": "OP_DUP OP_HASH160 2c6f2c3d8d6bc68f633e4ce4faebec44c941fd2b OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yQNPm4nmJT7voxyK8W8XcNUid1hr6vWHHK" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.09796809", + "n": 1, + "scriptPubKey": { + "hex": "76a91440adb9f4260a3ab61a9c039c8a3da33d3ee0cb8488ac", + "asm": "OP_DUP OP_HASH160 40adb9f4260a3ab61a9c039c8a3da33d3ee0cb84 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ySDSDCbo6H5eNn3dKKdy9ahxsE1Jb4pW2W" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "txid": "3428f0c29370d1293b4706ffd0f8b0c84a5b7c1c217d319e5ef4722354000c6e", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100967b07e0a941a5af99d495a56bafa9d04865058c759306112903437364686db402201ba6dbebcd3023c1a141a543d8623557d8f94701513f398021a4993a0424f464012102b60057df450e39241c7a9f09cc2c51877eb06f589591cc9ce8c189b90f1cf9f7", + "asm": "3045022100967b07e0a941a5af99d495a56bafa9d04865058c759306112903437364686db402201ba6dbebcd3023c1a141a543d8623557d8f94701513f398021a4993a0424f464[ALL] 02b60057df450e39241c7a9f09cc2c51877eb06f589591cc9ce8c189b90f1cf9f7" + }, + "addr": "ySFxfdLGzwSM5pcr5gFF4DJ6uuNTXrKaFP", + "valueSat": 50100000000, + "value": 501, + "doubleSpentTxID": null + } + ], + "txlock": false + } + }, + "walletId":"5a6cea7411", + "addresses": { + "external": { + "m/44'/1'/0'/0/0": { + "path": "m/44'/1'/0'/0/0", + "index": "0", + "address": "ySFxfdLGzwSM5pcr5gFF4DJ6uuNTXrKaFP", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/1": { + "path": "m/44'/1'/0'/0/1", + "index": "1", + "address": "yUHiNcFa5CJhpTBVppxnCRr4AX8icnpx23", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/2": { + "path": "m/44'/1'/0'/0/2", + "index": "2", + "address": "ySwMKsNpDkHs2dZF9MGvJs2M8kwj9cwkeB", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/3": { + "path": "m/44'/1'/0'/0/3", + "index": "3", + "address": "yRnsDphcw8bbgTvQsmjA1Jx89bZMrnaYeG", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/4": { + "path": "m/44'/1'/0'/0/4", + "index": "4", + "address": "yWJE7zLBubfcqmNAL3aPg7zg2mZ9gaULpk", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/5": { + "path": "m/44'/1'/0'/0/5", + "index": "5", + "address": "yQLESvHfiTNQCEMYh3eTi8WMgtsrRCs8p9", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/6": { + "path": "m/44'/1'/0'/0/6", + "index": "6", + "address": "yM5JyViuExuF4iwsra9w6zhiHtuKPaCvzj", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/7": { + "path": "m/44'/1'/0'/0/7", + "index": "7", + "address": "yVQkAdhnbFtjZd1yGF3zYFvvZjryhgbThi", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/8": { + "path": "m/44'/1'/0'/0/8", + "index": "8", + "address": "yV257dr54NePCtLNC9DLWCLjQ1ek2kFcUc", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/9": { + "path": "m/44'/1'/0'/0/9", + "index": "9", + "address": "yiaDXBHDgKGdB9SsZD3AzZSyMNiXbELJpA", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/10": { + "path": "m/44'/1'/0'/0/10", + "index": "10", + "address": "yQALFvPbtFW4mLwBd4bmnJr81Rv8eDiexx", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/11": { + "path": "m/44'/1'/0'/0/11", + "index": "11", + "address": "yQ7UheRGhTptRDzL8eQ2iYQkKBfzmmvnvS", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/12": { + "path": "m/44'/1'/0'/0/12", + "index": "12", + "address": "yUPwcojKUr93i1dfk2CP958yKtU4w57eZ1", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/13": { + "path": "m/44'/1'/0'/0/13", + "index": "13", + "address": "ygMDQBoY2PB2dFqYAM8XgXbE4VW5jag9Pn", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/14": { + "path": "m/44'/1'/0'/0/14", + "index": "14", + "address": "yiMRx5AfiPBjNijPVmcjgzeQhnQr6hXcja", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/15": { + "path": "m/44'/1'/0'/0/15", + "index": "15", + "address": "yQi5MHp7ALs1fxz43TEngW4oTTtsDgjKcb", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/16": { + "path": "m/44'/1'/0'/0/16", + "index": "16", + "address": "ydWvJi5wA13AaxpaovExd12RkQbAD6zJ5u", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/17": { + "path": "m/44'/1'/0'/0/17", + "index": "17", + "address": "yVgqhk1uUJPGtaQPRQ7FzbKqzRQmEUDDqT", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/18": { + "path": "m/44'/1'/0'/0/18", + "index": "18", + "address": "yNDhTYz2Fr1hNgtKAinb97e6pxDRnCGgTS", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/19": { + "path": "m/44'/1'/0'/0/19", + "index": "19", + "address": "ybwMbYFtJdDqwexojuQmhy1vMuyr9A1o6L", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + } + }, + "internal": { + "m/44'/1'/0'/1/0": { + "path": "m/44'/1'/0'/1/0", + "index": "0", + "address": "yQNPm4nmJT7voxyK8W8XcNUid1hr6vWHHK", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/1": { + "path": "m/44'/1'/0'/1/1", + "index": "1", + "address": "yc5pKoBsBx9JTw5LMAysaJATAaNz7qS3bm", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/2": { + "path": "m/44'/1'/0'/1/2", + "index": "2", + "address": "ydUtknccGVWDgjaPfB36LSnLfWRvy9rquG", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/3": { + "path": "m/44'/1'/0'/1/3", + "index": "3", + "address": "yhR9SGaVqLU4RmURue6e6sejK1j6KN927o", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/4": { + "path": "m/44'/1'/0'/1/4", + "index": "4", + "address": "yZN6PTxRbp7nYbhEYEECvV7XDkicFA9g8w", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/5": { + "path": "m/44'/1'/0'/1/5", + "index": "5", + "address": "yWwK2zK9F7ktU4af24SWKfGJn3oMdkyCEi", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/6": { + "path": "m/44'/1'/0'/1/6", + "index": "6", + "address": "yPcmFEQ16wDGfeSoNceXsZKgZnsy9odasA", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/7": { + "path": "m/44'/1'/0'/1/7", + "index": "7", + "address": "yhcPBwkNEbkxoH3osTu7eYQ2LfKxdZBKeY", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/8": { + "path": "m/44'/1'/0'/1/8", + "index": "8", + "address": "ybu5ksK3FWfJS7AT5pPzYQqRCq5XQQ3hf6", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/9": { + "path": "m/44'/1'/0'/1/9", + "index": "9", + "address": "yfuZfF8FMz2bWD661cEtSjys1enewAuEcf", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/10": { + "path": "m/44'/1'/0'/1/10", + "index": "10", + "address": "yjXRPAqWKafGWRCqs2kw7hLdPNy1tYa7y3", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/11": { + "path": "m/44'/1'/0'/1/11", + "index": "11", + "address": "ycg2im4novAXkUtBqDJCvHQaVK213YteZ2", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/12": { + "path": "m/44'/1'/0'/1/12", + "index": "12", + "address": "ySy3HWcKiCkobz6xdPX925e8H1RaEgQGkW", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/13": { + "path": "m/44'/1'/0'/1/13", + "index": "13", + "address": "yXR5WLqqpZBLcgGr5GXUQKkpUk3TMhrwYM", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/14": { + "path": "m/44'/1'/0'/1/14", + "index": "14", + "address": "yaNTMgehByDwez1AxPeJTXqaVPAmjz9QBE", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/15": { + "path": "m/44'/1'/0'/1/15", + "index": "15", + "address": "yM2NyP8fsnCFdS4nqBoZgavtHunwr4k3uw", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/16": { + "path": "m/44'/1'/0'/1/16", + "index": "16", + "address": "yPdXGLkwhMEgzJoYEwvfJDP4zJ6HLL4YqL", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/17": { + "path": "m/44'/1'/0'/1/17", + "index": "17", + "address": "yd87DbyGA3fusm1xZyVhxF8jXPK9FgKiqY", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/18": { + "path": "m/44'/1'/0'/1/18", + "index": "18", + "address": "yirHnhzesUMTFvUYpQHP5nh7QeXMESugwq", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/19": { + "path": "m/44'/1'/0'/1/19", + "index": "19", + "address": "yRNtMCzgbg8jcX5ToLpePiFdtoVXX5YSbB", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + } + }, + "misc": {} + } +} \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/fixtures.json b/packages/wallet-lib/fixtures/fixtures.json new file mode 100644 index 00000000000..d89022a8cfc --- /dev/null +++ b/packages/wallet-lib/fixtures/fixtures.json @@ -0,0 +1,7 @@ +{ + "mnemonicString1":"knife easily prosper input concert merge prepare autumn pen blood glance toilet", + "mnemonicString2":"increase table banana fiscal innocent wool sport mercy motion stable prize promote", + "invalidMnemonicString1":"knife easily prosper input concert merge prepare autumn pen blood glance chair", + "HDPrivateKey1Mainnet":"xprv9s21ZrQH143K3oPRkzFha3zbXZYAhd3gLaGi93wCpnqBSv9JMDPFpZapDJHq2bN6EAd5Pqms4JfGQ1MYvT6ZsoeDaa8gQBKvzqrwYUFTUJT", + "HDPrivateKey1Testnet":"tprv8ZgxMBicQKsPdvrZPiQ1HXQXaiaWY3BwKnUYotXriaZTAdtxzuwsPmYP2KjqBmtyhpNZptmMQnNKG5Ts5QjuzsbqpD9EXFDbaXfTRxbTNsr" +} \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/fluidDepth.json b/packages/wallet-lib/fixtures/fluidDepth.json new file mode 100644 index 00000000000..a1498b81ca8 --- /dev/null +++ b/packages/wallet-lib/fixtures/fluidDepth.json @@ -0,0 +1,5 @@ +{ + "mnemonic":"fluid depth harvest front buffalo science item side type cook suit jaguar", + "walletIdTestnet":"6fbc7ebd65", + "HDRootKeyTestnet":"tprv8ZgxMBicQKsPeGi4CikhacVPz6UmErenu1PoD3S4XcEDSPP8auRaS8hG3DQtsQ2i9HACgohHwF5sgMVJNksoKqYoZbis8o75Pp1koCme2Yo" +} \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/gathersail.json b/packages/wallet-lib/fixtures/gathersail.json new file mode 100644 index 00000000000..aaf582ded14 --- /dev/null +++ b/packages/wallet-lib/fixtures/gathersail.json @@ -0,0 +1,35 @@ +{ + "mnemonic": "gather sail face invite together focus waste barely excuse slide harbor hint", + "testnet": { + "coinTypeRoothardened": { + "path": "m/44'/1'", + "walletId": "a5f693bd42", + "hdprivkey": "tprv8dofJF3bpqxuajWyTwnM7PyxCP28ubYAcZ9FmAUsK8mGWGtDsnVGqotKPoPYnqCrqHd5B5PkE6sX2ZX7HXrQLpKGw9EKsfN97LKQ1LMHXK2", + "hdpubkey": "tpubDAVhSf5qyDeaUCYmMbSwWoe4mQY54vj5Brk33gXAjQZfLm8zWBJs2JWBZy7ZZaDi2wDQDaBbtC9QAnDrHJSGBUCE9pBhdEZSbD7KqsNAewG" + }, + "accountRootUnhardened": { + "walletId": "c8bfa6aece", + "path": "m/44'/1'/0", + "hdprivkey": "tprv8fPaucpz8koz1x6d1EjuBXaDJqyUknWb2PwPFWXzoL3LUCDbWtw451fCxMBvpYcawck4ER6GJPRyhMapoYD3p5p6gY71uLsYiTJ4Lq7NBSf", + "hdpubkey": "tpubDC5d42sEH8VeuR8QttQVawEKssVQv7hVbhYAY2aJDbqjJgUN9HkeFWH58VX5xc5QUW2JDm4ActaFS6NVZUZu6pfijnvPVmUwF84ryPkBSi5" + }, + "accountRootHardened": { + "walletId": "84191f1b0d", + "path": "m/44'/1'/0'", + "hdprivkey": "tprv8fPaucq8URLxAntBgc3dJQ7A9gSZmGCEkfDjiUKqrfxb5hvLhMH2xE9FPxhBmpEdSYbZ7WgV1mVP6Q6CzFbiz3qfRs7pR3QT2ALCWGeVoWn", + "hdpubkey": "tpubDC5d42sNco2d4FuyaFiDhomGihxVvbP9KxpWzzN9GwkyvCB7Kk6d8im7a86EzYetBJ3fVywD3TtkD676HEtHEYNZhjRfbEuDPeUQwgxddFZ" + }, + "external": { + "walletId": "58a78501c1", + "path": "m/44'/1'/0'/0", + "hdprivkey": "tprv8hV43GTuAYskd2ZTtJGgVtNp9e4ozrj3qD2ksTVHpvETmZjp4KpRFG65J7wK6WhB3z8ixcGgFowtdrXtAorza6LT4aRMUK3NdPHW5bqFUVt", + "hdpubkey": "tpubDEB6BgW9JvZRWVbFmwwGuJ2vifakABuxQWdY9yXbFC2rc3zagie1RkhwUEnahb1dzaapchEVeKqKcx99TzkjNvjXcmoQkLJwsYnA1J5bGNj" + }, + "internal": { + "walletId": "e1ae985d2d", + "path": "m/44'/1'/0'/1", + "hdprivkey": "tprv8hV43GTuAYskeXZaJuuZw5kJS8whKashCoDAyr7wNJfC5LM73mfgUsJno7Uy7Db148R9bCgKGPPqWMyU8HhzVi73uwR1RsYF4ietJXpVAvW", + "hdpubkey": "tpubDEB6BgW9JvZRXzbNCZaALVQR1ATdUv4bn6oxGNAEnaTaupbsgAVGfMveyF8jkr6MzEWaUFDwUgKy41LaQbPofVpzCxpuwGMze4dvg2KU5f6" + } + } +} diff --git a/packages/wallet-lib/fixtures/getTransactionHistory.json b/packages/wallet-lib/fixtures/getTransactionHistory.json new file mode 100644 index 00000000000..a275b97e623 --- /dev/null +++ b/packages/wallet-lib/fixtures/getTransactionHistory.json @@ -0,0 +1,851 @@ +{ + "walletId": "5061b8276c", + "wallets": { + "5061b8276c": { + "accounts": { + "m/44'/1'/0'": { + "label": null, + "path": "m/44'/1'/0'", + "network": { + "name": "testnet", + "alias": "regtest", + "pubkeyhash": 140, + "privatekey": 239, + "scripthash": 19, + "xpubkey": 70617039, + "xprivkey": 70615956, + "port": 19999, + "networkMagic": { + "type": "Buffer", + "data": [ + 206, + 226, + 202, + 255 + ] + }, + "dnsSeeds": [ + "testnet-seed.darkcoin.io", + "testnet-seed.dashdot.io", + "test.dnsseed.masternode.io" + ] + } + } + }, + "network": { + "name": "testnet", + "alias": "regtest", + "pubkeyhash": 140, + "privatekey": 239, + "scripthash": 19, + "xpubkey": 70617039, + "xprivkey": 70615956, + "port": 19999, + "networkMagic": { + "type": "Buffer", + "data": [ + 206, + 226, + 202, + 255 + ] + }, + "dnsSeeds": [ + "testnet-seed.darkcoin.io", + "testnet-seed.dashdot.io", + "test.dnsseed.masternode.io" + ] + }, + "mnemonic": null, + "type": null, + "blockheight": 0, + "addresses": { + "external": { + "m/44'/1'/0'/0/0": { + "address": "yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT", + "path": "m/44'/1'/0'/0/0", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "cdcf81b69629c3157f09878076bc4f544aa01477cf59915461343476772a4a84", + "507e56181d03ba75b133f93cd073703c5c514f623f30e4cc32144c62b5a697c4" + ], + "fetchedLast": 1548153626921, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/0/1": { + "address": "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe", + "path": "m/44'/1'/0'/0/1", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf", + "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059" + ], + "fetchedLast": 1548153626888, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/0/2": { + "address": "yQSVFizTKcPLz2V7zoZ3HkkJ7sQmb5jXAs", + "path": "m/44'/1'/0'/0/2", + "balanceSat": 35000000000, + "unconfirmedBalanceSat": 0, + "transactions": [ + "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb" + ], + "fetchedLast": 1548153626921, + "used": true, + "utxos": { + "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb": { + "txid": "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb", + "outputIndex": 0, + "satoshis": 35000000000, + "scriptPubKey": "76a9142d356c444eaf779f274ebf440be834c62b9564ec88ac" + } + } + }, + "m/44'/1'/0'/0/3": { + "address": "yhnTNo6tkmr8tA4SAL8gcci1z5rPHuaoxA", + "path": "m/44'/1'/0'/0/3", + "balanceSat": 12500000000, + "unconfirmedBalanceSat": 0, + "transactions": [ + "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf" + ], + "fetchedLast": 1548153626955, + "used": true, + "utxos": { + "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf": { + "txid": "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf", + "outputIndex": 0, + "satoshis": 12500000000, + "scriptPubKey": "76a914eb76500ef39cb2a4f166eafd6ba4270b6ebae71988ac" + } + } + }, + "m/44'/1'/0'/0/4": { + "address": "yNY6spErvvm9C8at2KQpvAfd6TPumgyETh", + "path": "m/44'/1'/0'/0/4", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626962, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/5": { + "address": "yaVrJ5dgELFkYwv6AydDyGPAJQ5kTJXyAN", + "path": "m/44'/1'/0'/0/5", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626934, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/6": { + "address": "yMN2w8NiwcmY3zvJLeeBxpaExFV1aN23pg", + "path": "m/44'/1'/0'/0/6", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626917, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/7": { + "address": "yQamgF8LvZ9nV1xrbZ1hUiHjmttznbdVnS", + "path": "m/44'/1'/0'/0/7", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626919, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/8": { + "address": "ya2aCFRiEQ6SHiB9yETaEGEcs6gNSqJt9d", + "path": "m/44'/1'/0'/0/8", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626890, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/9": { + "address": "ygZJwpqD7y9SP7t1jk4reytSCW6M16aEfC", + "path": "m/44'/1'/0'/0/9", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626978, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/10": { + "address": "ybHHUqRWE35R7gohkXHQVJELoGkduywroY", + "path": "m/44'/1'/0'/0/10", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626971, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/11": { + "address": "yVe8ftxg2aubVa5gppnz4WZJ7Umgu1UEjh", + "path": "m/44'/1'/0'/0/11", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626965, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/12": { + "address": "ydhUVrSYdRKTm1bztWCgFSgSqF1HKKL25i", + "path": "m/44'/1'/0'/0/12", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626963, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/13": { + "address": "yaUfQiKzL29LkXYZiEGM5DmMuMBn5e81MV", + "path": "m/44'/1'/0'/0/13", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626923, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/14": { + "address": "yTZfTfqgv5r9CW4jyMsr3PnhYhGh7scFDj", + "path": "m/44'/1'/0'/0/14", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626890, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/15": { + "address": "yhxUAk6St4CdhYifJqEAaY9jJVd4mZyEmy", + "path": "m/44'/1'/0'/0/15", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626923, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/16": { + "address": "yRs2aA9UjfEaCpNF3wf8L2m62EfGkQdD8u", + "path": "m/44'/1'/0'/0/16", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626920, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/17": { + "address": "yVPAnRANHXZnrL5P5SwJCWKRru7PgvMhYg", + "path": "m/44'/1'/0'/0/17", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626887, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/18": { + "address": "yhUPdqWmmJPT4wE95QzmTvq8vkUJE6LswZ", + "path": "m/44'/1'/0'/0/18", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626924, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/19": { + "address": "yLwNeRjtHHqW1d7KVR5AXnELfB8vp9iuvk", + "path": "m/44'/1'/0'/0/19", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626957, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/20": { + "address": "yeEjYLeiHMhABkG2RUtTdXgZKfz7L5uohT", + "path": "m/44'/1'/0'/0/20", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626960, + "used": false, + "utxos": {} + } + }, + "internal": { + "m/44'/1'/0'/1/0": { + "address": "yTa2L2ZJr48sbJCnYP96RwW1D4ceeCdyHS", + "path": "m/44'/1'/0'/1/0", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626926, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/1": { + "address": "ybTg1Xema7wsGHGxSMQUSNoxyYRkTMUWJd", + "path": "m/44'/1'/0'/1/1", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059", + "cdcf81b69629c3157f09878076bc4f544aa01477cf59915461343476772a4a84" + ], + "fetchedLast": 1548153626927, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/1/2": { + "address": "yLVQ9bZBLZmmvNQk7pPCUAGaXADQ6Rhkqt", + "path": "m/44'/1'/0'/1/2", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb", + "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059" + ], + "fetchedLast": 1548153626964, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/1/3": { + "address": "yU7hmdDdi9RWem64hMz3GV3i9UWHNNK2FS", + "path": "m/44'/1'/0'/1/3", + "balanceSat": 18999997456, + "unconfirmedBalanceSat": 0, + "transactions": [ + "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb" + ], + "fetchedLast": 1548153627030, + "used": true, + "utxos": { + "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb": { + "txid": "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb", + "outputIndex": 1, + "satoshis": 18999997456, + "scriptPubKey": "76a9145588785fde163d906a3a816383656c56f73f11a988ac" + } + } + }, + "m/44'/1'/0'/1/4": { + "address": "yfyTKf2PaxFvND6V5pEFWpnrbcSdy3igZQ", + "path": "m/44'/1'/0'/1/4", + "balanceSat": 32499999753, + "unconfirmedBalanceSat": 0, + "transactions": [ + "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf" + ], + "fetchedLast": 1548153627019, + "used": true, + "utxos": { + "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf": { + "txid": "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf", + "outputIndex": 1, + "satoshis": 32499999753, + "scriptPubKey": "76a914d79a97706450058b85aaf535e2c5853f181d5fac88ac" + } + } + }, + "m/44'/1'/0'/1/5": { + "address": "yeLbU1At3Cp4RD7Gunic6iy6orgnoNDhEb", + "path": "m/44'/1'/0'/1/5", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626939, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/6": { + "address": "ySVpgHLkgrrrsbaWJhW5GMHZjeSkADrsTJ", + "path": "m/44'/1'/0'/1/6", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626980, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/7": { + "address": "yPuyPUdk32KP6iE9359TuqWMv8azrqtHrk", + "path": "m/44'/1'/0'/1/7", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626976, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/8": { + "address": "yaZFt1VnAbi72mtyjDNV4AwTECqdg5Bv95", + "path": "m/44'/1'/0'/1/8", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626974, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/9": { + "address": "yR7bqPdf8UT5ZFpCiqwAiz69xHco7QJqyp", + "path": "m/44'/1'/0'/1/9", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626947, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/10": { + "address": "yRDW3KVi5vdnooaJxfXW2x9XKe8mz5XgRC", + "path": "m/44'/1'/0'/1/10", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626970, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/11": { + "address": "yfTs2aiRuWNdgnAW9gcf7sv2A3qpATRzHJ", + "path": "m/44'/1'/0'/1/11", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153627029, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/12": { + "address": "ybqMAktAf9mktXHVN1p9BmfgUZzUnELkrw", + "path": "m/44'/1'/0'/1/12", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626982, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/13": { + "address": "ySBGFjj74zmCqmcMxCBr7JvgvYqjzsgZJp", + "path": "m/44'/1'/0'/1/13", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626934, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/14": { + "address": "yZdS1zoVDbGVRYgV3tnwGhhQMX7vw3rpBj", + "path": "m/44'/1'/0'/1/14", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626962, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/15": { + "address": "yYoAZ8hHN48usWRBuiMpbZ6aVifAw1E7TV", + "path": "m/44'/1'/0'/1/15", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626975, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/16": { + "address": "yhjj9bfgZrem1ytWby8dcTiJ9pA8Q4tC6J", + "path": "m/44'/1'/0'/1/16", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626930, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/17": { + "address": "yVycuCgivTbSpMfn9yDnjrCX3QzLgZRXDr", + "path": "m/44'/1'/0'/1/17", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626954, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/18": { + "address": "ydJsVhE6houFRWiNvFUYqhMUK5RHnuoqK9", + "path": "m/44'/1'/0'/1/18", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626941, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/19": { + "address": "yck7wS32ydQ8AxRw7NZJUtiRStbMSYGi6W", + "path": "m/44'/1'/0'/1/19", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153626967, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/20": { + "address": "yU7sNM4j6fzKtbah24gCXdN636piQN8F2f", + "path": "m/44'/1'/0'/1/20", + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1548153627027, + "used": false, + "utxos": {} + } + }, + "misc": {} + } + } + }, + "transactions": { + "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf": { + "txid": "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf", + "blockhash": "0000011ab17c26bb70dde073303dce57d94dbb4bb99da12d1383d09125055d6d", + "blockheight": 3546, + "blocktime": 1548153589, + "fees": 247, + "size": 225, + "vout": [ + { + "value": "125.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914eb76500ef39cb2a4f166eafd6ba4270b6ebae71988ac", + "asm": "OP_DUP OP_HASH160 eb76500ef39cb2a4f166eafd6ba4270b6ebae719 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yhnTNo6tkmr8tA4SAL8gcci1z5rPHuaoxA" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "324.99999753", + "n": 1, + "scriptPubKey": { + "hex": "76a914d79a97706450058b85aaf535e2c5853f181d5fac88ac", + "asm": "OP_DUP OP_HASH160 d79a97706450058b85aaf535e2c5853f181d5fac OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yfyTKf2PaxFvND6V5pEFWpnrbcSdy3igZQ" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "txid": "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059", + "vout": 0, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "473044022061d43a5beadb582842736407db70094a2d4bce2f162b6fd94760423f21c29a80022076878f295a64877b93d745fc089bd5acd6927b2966cbea5a5314925371893b7701210367b58178e4db0f50f2c7e18dbdaac0d7ed585a08c02fbf090d3fe2ecd04b2091", + "asm": "3044022061d43a5beadb582842736407db70094a2d4bce2f162b6fd94760423f21c29a80022076878f295a64877b93d745fc089bd5acd6927b2966cbea5a5314925371893b77[ALL] 0367b58178e4db0f50f2c7e18dbdaac0d7ed585a08c02fbf090d3fe2ecd04b2091" + }, + "addr": "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe", + "valueSat": 45000000000, + "value": 450, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059": { + "txid": "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059", + "blockhash": "3532c95f230458dfa5275c8e71bd074987de85e5c9f5ec28ce17c66973c167d8", + "blockheight": 3537, + "blocktime": 1548152219, + "fees": 247, + "size": 225, + "vout": [ + { + "value": "450.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac", + "asm": "OP_DUP OP_HASH160 3a9202121ee9ef906e567101326f2ecf8ad4ecbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRf8x9bov39e2vHtibjeG35ZNF4BCpSZGe" + ], + "type": "pubkeyhash" + }, + "spentTxId": "9a606bc71c4c87aa7735d55dc7f01047289b77945b9617615e9afc4643e14fdf", + "spentIndex": 0, + "spentHeight": 3546 + }, + { + "value": "539.99997703", + "n": 1, + "scriptPubKey": { + "hex": "76a91401e1e7da88b5f2005a2b710fcf6b172ca2a221b488ac", + "asm": "OP_DUP OP_HASH160 01e1e7da88b5f2005a2b710fcf6b172ca2a221b4 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yLVQ9bZBLZmmvNQk7pPCUAGaXADQ6Rhkqt" + ], + "type": "pubkeyhash" + }, + "spentTxId": "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb", + "spentIndex": 0, + "spentHeight": 3544 + } + ], + "vin": [ + { + "txid": "cdcf81b69629c3157f09878076bc4f544aa01477cf59915461343476772a4a84", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "473044022013592c02394a0b12d0b0f0e0062388ec2806b5a4e9fcc51a8647f12c6bf1e2b002202813b2521c933dcf8efecc6b488defeec6ef85cfb34f6fbb86f32af7fcfdbf62012102420b7af29c43e5d2da5f23a30e0ce994b99e76ebcd2ad7605bd32418aa0c829f", + "asm": "3044022013592c02394a0b12d0b0f0e0062388ec2806b5a4e9fcc51a8647f12c6bf1e2b002202813b2521c933dcf8efecc6b488defeec6ef85cfb34f6fbb86f32af7fcfdbf62[ALL] 02420b7af29c43e5d2da5f23a30e0ce994b99e76ebcd2ad7605bd32418aa0c829f" + }, + "addr": "ybTg1Xema7wsGHGxSMQUSNoxyYRkTMUWJd", + "valueSat": 98999997950, + "value": 989.9999795, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "507e56181d03ba75b133f93cd073703c5c514f623f30e4cc32144c62b5a697c4": { + "txid": "507e56181d03ba75b133f93cd073703c5c514f623f30e4cc32144c62b5a697c4", + "blockhash": "0000002d2cf12cb5dc7e2391c6393e2098d7f201d6f2388353d30485eab0803a", + "blockheight": 3463, + "blocktime": 1548141724, + "fees": 374, + "size": 372, + "vout": [ + { + "value": "99.99999626", + "n": 0, + "scriptPubKey": { + "hex": "76a914387d9e15cfc45b52f74743d44274e7794563bec388ac", + "asm": "OP_DUP OP_HASH160 387d9e15cfc45b52f74743d44274e7794563bec3 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRU9B1BKdGAaGLdZNXWM8CBXCf2CuYCSEv" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "1000.00000000", + "n": 1, + "scriptPubKey": { + "hex": "76a91419ba12c566e1e21deafa60f2a6de42d15f85dbbc88ac", + "asm": "OP_DUP OP_HASH160 19ba12c566e1e21deafa60f2a6de42d15f85dbbc OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT" + ], + "type": "pubkeyhash" + }, + "spentTxId": "cdcf81b69629c3157f09878076bc4f544aa01477cf59915461343476772a4a84", + "spentIndex": 0, + "spentHeight": 3479 + } + ], + "vin": [ + { + "txid": "03ff87bc72670742305ceb6f90911a0484bb2542459616db081e54e47cf0d012", + "vout": 1, + "sequence": 4294967294, + "n": 0, + "scriptSig": { + "hex": "4730440220549bb6f5882f92d62229890ef5b88588b4671b57dc88e95f8ba8142b049ff43d02205e97b05e44bf41935f399a8ded59e62ef605959b458e7b5e1ec34d39672cea9e012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844", + "asm": "30440220549bb6f5882f92d62229890ef5b88588b4671b57dc88e95f8ba8142b049ff43d02205e97b05e44bf41935f399a8ded59e62ef605959b458e7b5e1ec34d39672cea9e[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844" + }, + "addr": "yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5", + "valueSat": 100000000000, + "value": 1000, + "doubleSpentTxID": null + }, + { + "txid": "5fe820d678e30b5f2fd49a0f67f8382469c36e8b5cbd25cd06dd9ed049823dd6", + "vout": 1, + "sequence": 4294967294, + "n": 1, + "scriptSig": { + "hex": "473044022057122ae2361a059213e362c3da275efe2efd3273396e8e7353238fe140011a9d02206379d6a1eb8463bfaf0cf35f94581cc13c554761c94089fa0d9f704d448de6b9012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844", + "asm": "3044022057122ae2361a059213e362c3da275efe2efd3273396e8e7353238fe140011a9d02206379d6a1eb8463bfaf0cf35f94581cc13c554761c94089fa0d9f704d448de6b9[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844" + }, + "addr": "yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5", + "valueSat": 10000000000, + "value": 100, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "cdcf81b69629c3157f09878076bc4f544aa01477cf59915461343476772a4a84": { + "txid": "cdcf81b69629c3157f09878076bc4f544aa01477cf59915461343476772a4a84", + "blockhash": "000001c112eca9b3ee6b52a6aca271e1fe848951f1f666e87783f806ad445e87", + "blockheight": 3479, + "blocktime": 1548144723, + "fees": 2050, + "size": 226, + "vout": [ + { + "value": "10.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a9146e55f6575a5a735e028d28e6904b1d1759c5948d88ac", + "asm": "OP_DUP OP_HASH160 6e55f6575a5a735e028d28e6904b1d1759c5948d OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yWNrA4srrAjC9DT6UCu8NgpcqwQWa35dFX" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "989.99997950", + "n": 1, + "scriptPubKey": { + "hex": "76a914a6183356019635d8b4401d5c32b5defaf7b4b63c88ac", + "asm": "OP_DUP OP_HASH160 a6183356019635d8b4401d5c32b5defaf7b4b63c OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ybTg1Xema7wsGHGxSMQUSNoxyYRkTMUWJd" + ], + "type": "pubkeyhash" + }, + "spentTxId": "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059", + "spentIndex": 0, + "spentHeight": 3537 + } + ], + "vin": [ + { + "txid": "507e56181d03ba75b133f93cd073703c5c514f623f30e4cc32144c62b5a697c4", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100a8d824fe69662b5aba42dbcf139ad08a1212785734502b9b06244b7e55933c670220780667bcc9a798fe925c00c2d20f2fdaa93e86580c123b6deec8007fa7b1b3d30121037bc909c83d0571bca2589a12af9963a77d375c39d5bd39eb33495c09c60f917d", + "asm": "3045022100a8d824fe69662b5aba42dbcf139ad08a1212785734502b9b06244b7e55933c670220780667bcc9a798fe925c00c2d20f2fdaa93e86580c123b6deec8007fa7b1b3d3[ALL] 037bc909c83d0571bca2589a12af9963a77d375c39d5bd39eb33495c09c60f917d" + }, + "addr": "yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT", + "valueSat": 100000000000, + "value": 1000, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb": { + "txid": "00131c6c3ab8fca20380c6766f414a78f05b2e1783ce2632c9469d7357305dcb", + "blockhash": "1468d15a14ffa2937f87c26a9a25dd8dcfc62263482cb2af034065f117522944", + "blockheight": 3544, + "blocktime": 1548153208, + "fees": 247, + "size": 226, + "vout": [ + { + "value": "350.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a9142d356c444eaf779f274ebf440be834c62b9564ec88ac", + "asm": "OP_DUP OP_HASH160 2d356c444eaf779f274ebf440be834c62b9564ec OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yQSVFizTKcPLz2V7zoZ3HkkJ7sQmb5jXAs" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "189.99997456", + "n": 1, + "scriptPubKey": { + "hex": "76a9145588785fde163d906a3a816383656c56f73f11a988ac", + "asm": "OP_DUP OP_HASH160 5588785fde163d906a3a816383656c56f73f11a9 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yU7hmdDdi9RWem64hMz3GV3i9UWHNNK2FS" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "txid": "f4b0c5df91ce3bbbcf471cfbd4b024083ad66048126bd5d6732459a07e266059", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100e91be951c634f8977516fc5240d39b11691841ea6a0d5e912966c9f9ce22070b02206a4d83d8a960a3c193687031d4133309ac69f32e7cb27aff5d6501648310227b0121031ea66e44bdd37dd6e3151e4f88ef3e6c4cde5d7614fd537e71fe9e1bb59d4abf", + "asm": "3045022100e91be951c634f8977516fc5240d39b11691841ea6a0d5e912966c9f9ce22070b02206a4d83d8a960a3c193687031d4133309ac69f32e7cb27aff5d6501648310227b[ALL] 031ea66e44bdd37dd6e3151e4f88ef3e6c4cde5d7614fd537e71fe9e1bb59d4abf" + }, + "addr": "yLVQ9bZBLZmmvNQk7pPCUAGaXADQ6Rhkqt", + "valueSat": 53999997703, + "value": 539.99997703, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + } + }, + "chains": { + "testnet": { + "name": "testnet", + "blockheight": 3546 + } + } +} diff --git a/packages/wallet-lib/fixtures/increasetable.json b/packages/wallet-lib/fixtures/increasetable.json new file mode 100644 index 00000000000..3d450954578 --- /dev/null +++ b/packages/wallet-lib/fixtures/increasetable.json @@ -0,0 +1,714 @@ +{ + "mnemonic": "increase table banana fiscal innocent wool sport mercy motion stable prize promote", + "addresses": { + "external": { + "m/44'/1'/0'/0/0": { + "address": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "balanceSat": 100000000, + "fetchedLast": 0, + "path": "m/44'/1'/0'/0/0", + "transactions": [ + "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc", + "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35", + "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6" + ], + "index":0, + "unconfirmedBalanceSat": 0, + "used": true, + "utxos": [ + { + "address": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "txid": "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc", + "outputIndex": 0, + "scriptPubKey": "76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac", + "satoshis": 100000000 + } + ] + } + } + }, + "invalidTransactions": { + "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc": { + "txid": "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc", + "blockhash": "0000000007a3b51cccc3d8516917d3e8386dd82ec49322c790a0a873ee4e54d2", + "blockheight": 204344, + "blocktime": 0, + "fees": 245, + "size": 225, + "txlock": false + } + }, + "transactions": { + "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc": { + "txid": "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc", + "blockhash": "0000000007a3b51cccc3d8516917d3e8386dd82ec49322c790a0a873ee4e54d2", + "blockheight": 204344, + "blocktime": 0, + "fees": 245, + "size": 225, + "txlock": false, + "vin": [ + { + "addr": "yYC5x9QkcKcRyYVdzaAVAArSCD39byLRcm", + "txid": "1a855e19b90ca52851a94c0e520ee6a3eaa91bdc2bb84cdda1969b5b5b76201a", + "valueSat": 8966014640, + "vout": 0, + "scriptSig":{"hex":"47304402205c4d90dd2187eb069e546b28b6552617c098d0d0a2fb73e03e5dbd1afcd37bea02204ff1ce09e975294cfef0b64126cea2cf7957972cb4bc84194c55f6856f2e1fcf01210287d90f447cdbf4d9c5557276702ce320c0026a7f3aba6add8a94b97eb5503c17"} + } + ], + "vout": [ + { + "value": "1.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac", + "asm": "OP_DUP OP_HASH160 f8c2652847720ab6d401291e5a48e2c8fe5d3c9f OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "88.66014395", + "n": 1, + "scriptPubKey": { + "hex": "76a914891b30309bb70c129735bb48e520bc85e21cf70388ac", + "asm": "OP_DUP OP_HASH160 891b30309bb70c129735bb48e520bc85e21cf703 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yYpPzZCZ5BrU5RDaU8V2kBDbLzyP1xhTDj" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ] + }, + "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35": { + "txid": "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35", + "blockhash": "0000000002922d7d0a69ce3e29908dc26aba6565af760d208dac77a8b520fbf3", + "blockheight": 204161, + "blocktime": 1533900199, + "fees": 1000, + "size": 226, + "txlock": false, + "vout": [ + { + "value": "0.00100000", + "n": 0, + "scriptPubKey": { + "hex": "76a914f3145d42d98196ca439eee48da05af56c2d7c4a688ac", + "asm": "OP_DUP OP_HASH160 f3145d42d98196ca439eee48da05af56c2d7c4a6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yiUjSkhkAfaHfYYmTMhc27NCmogJ3iRBaS" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.99890000", + "n": 1, + "scriptPubKey": { + "hex": "76a9144d19cc13106cac13b386c89b003b02992ae8c5e688ac", + "asm": "OP_DUP OP_HASH160 4d19cc13106cac13b386c89b003b02992ae8c5e6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yTM7nPiekjMBkMCU6cPmFD2KReeFUeVwCp" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "txid": "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6", + "vout": 0, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100ad3c7a5eae5ccb491c0e2cc3521df2cc410bf403340d6587848dc5ceb2b6831f022016b622ee329a4ceb1364d3e7295832af30e4fbc63420257b5b4971185048ac410121022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536", + "asm": "3045022100ad3c7a5eae5ccb491c0e2cc3521df2cc410bf403340d6587848dc5ceb2b6831f022016b622ee329a4ceb1364d3e7295832af30e4fbc63420257b5b4971185048ac41[ALL] 022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536" + }, + "addr": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "valueSat": 100000000, + "value": 1, + "doubleSpentTxID": null + } + ] + }, + "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6": { + "txid": "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6", + "blockhash": "000000000388f8a1de91702e25209aad802d28fcd2710ac2e2acd12e9738dbe2", + "blockheight": 203633, + "blocktime": 1533827521, + "fees": 1000, + "size": 225, + "txlock": true, + "vout": [ + { + "value": "1.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac", + "asm": "OP_DUP OP_HASH160 f8c2652847720ab6d401291e5a48e2c8fe5d3c9f OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8" + ], + "type": "pubkeyhash" + }, + "spentTxId": "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35", + "spentIndex": 0, + "spentHeight": 204161 + }, + { + "value": "18.99980000", + "n": 1, + "scriptPubKey": { + "hex": "76a9147d39e5ff6ea7b82c6dc69994f1075f157bee9ef688ac", + "asm": "OP_DUP OP_HASH160 7d39e5ff6ea7b82c6dc69994f1075f157bee9ef6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yXjag2zjTwA5Ya2sSV5KoFzTvE6uKdrnKa" + ], + "type": "pubkeyhash" + }, + "spentTxId": "6ca8795f2534972e1371249c3d7b6c5095e1513bc8cc351eeaa2f364020dbc01", + "spentIndex": 1, + "spentHeight": 203674 + } + ], + "vin": [ + { + "txid": "4ae8d1960c9a4ed83dbeaf1ad94b4a82f11c8574207144beda87113d94a31da1", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "47304402205483f0b26a04876fe8eceb9a5d86b1da93012be27b5482a3d53443cda5ee7a13022027410cef1e19c2620ad0da1b800252b620e3e8dc576efb069e95eb1d2222ccfb01210221d2f6bd1b101dc4eb40f570af2a87221e3ffa166b6c5eac2faf496b0c53cbdf", + "asm": "304402205483f0b26a04876fe8eceb9a5d86b1da93012be27b5482a3d53443cda5ee7a13022027410cef1e19c2620ad0da1b800252b620e3e8dc576efb069e95eb1d2222ccfb[ALL] 0221d2f6bd1b101dc4eb40f570af2a87221e3ffa166b6c5eac2faf496b0c53cbdf" + }, + "addr": "yUvr9AxuFx3ifp8HHFdYnVGbvK8Qqz25SQ", + "valueSat": 1999990000, + "value": 19.9999, + "doubleSpentTxID": null + } + ] + } + }, + "rawtx1": "0300000001bc1496ab23372f8476f8d92b15df97c8aa20851c3fe3c6ce22f0b5edadfa7add000000006b483045022100b1c119442de0d039a25d6ad667699d63b5524d80836c5e255329be2bbbd4eefb0220667946d3cc68f5dcf468083ca3b588fdc9280a5033e9573e167467d815806e960121022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536ffffffff0240420f00000000001976a914ba66c4e2cbf2f8579e3971eea8ea3b6f5823ab8388acb077e605000000001976a9147fc9561b310aec538ef8c83112f32d2be6e1f08088ac00000000", + "getHistory": [ + { + "type": "receive", + "txid": "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc", + "from": [ + "yYC5x9QkcKcRyYVdzaAVAArSCD39byLRcm" + ], + "time": 0, + "to": { + "address": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "amount": "1.00000000" + } + }, + { + "type": "sent", + "txid": "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35", + "from": [ + "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8" + ], + "time": 1533900199, + "to": { + "address": "yiUjSkhkAfaHfYYmTMhc27NCmogJ3iRBaS", + "amount": "0.00100000" + } + }, + { + "type": "receive", + "txid": "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6", + "from": [ + "yUvr9AxuFx3ifp8HHFdYnVGbvK8Qqz25SQ" + ], + "time": 1533827521, + "to": { + "address": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "amount": "1.00000000" + } + } + ], + "testnet": { + "transactions": { + "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35": { + "txid": "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35", + "blockhash": "0000000002922d7d0a69ce3e29908dc26aba6565af760d208dac77a8b520fbf3", + "blockheight": 204161, + "blocktime": 1533900199, + "fees": 1000, + "size": 226, + "txlock": false, + "vout": [ + { + "value": "0.00100000", + "n": 0, + "scriptPubKey": { + "hex": "76a914f3145d42d98196ca439eee48da05af56c2d7c4a688ac", + "asm": "OP_DUP OP_HASH160 f3145d42d98196ca439eee48da05af56c2d7c4a6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yiUjSkhkAfaHfYYmTMhc27NCmogJ3iRBaS" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.99890000", + "n": 1, + "scriptPubKey": { + "hex": "76a9144d19cc13106cac13b386c89b003b02992ae8c5e688ac", + "asm": "OP_DUP OP_HASH160 4d19cc13106cac13b386c89b003b02992ae8c5e6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yTM7nPiekjMBkMCU6cPmFD2KReeFUeVwCp" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "txid": "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6", + "vout": 0, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100ad3c7a5eae5ccb491c0e2cc3521df2cc410bf403340d6587848dc5ceb2b6831f022016b622ee329a4ceb1364d3e7295832af30e4fbc63420257b5b4971185048ac410121022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536", + "asm": "3045022100ad3c7a5eae5ccb491c0e2cc3521df2cc410bf403340d6587848dc5ceb2b6831f022016b622ee329a4ceb1364d3e7295832af30e4fbc63420257b5b4971185048ac41[ALL] 022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536" + }, + "addr": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "valueSat": 100000000, + "value": 1, + "doubleSpentTxID": null + } + ] + }, + "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6": { + "txid": "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6", + "blockhash": "000000000388f8a1de91702e25209aad802d28fcd2710ac2e2acd12e9738dbe2", + "blockheight": 203633, + "blocktime": 1533827521, + "fees": 10000, + "size": 225, + "txlock": true, + "vout": [ + { + "value": "1.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac", + "asm": "OP_DUP OP_HASH160 f8c2652847720ab6d401291e5a48e2c8fe5d3c9f OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8" + ], + "type": "pubkeyhash" + }, + "spentTxId": "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35", + "spentIndex": 0, + "spentHeight": 204161 + }, + { + "value": "18.99980000", + "n": 1, + "scriptPubKey": { + "hex": "76a9147d39e5ff6ea7b82c6dc69994f1075f157bee9ef688ac", + "asm": "OP_DUP OP_HASH160 7d39e5ff6ea7b82c6dc69994f1075f157bee9ef6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yXjag2zjTwA5Ya2sSV5KoFzTvE6uKdrnKa" + ], + "type": "pubkeyhash" + }, + "spentTxId": "6ca8795f2534972e1371249c3d7b6c5095e1513bc8cc351eeaa2f364020dbc01", + "spentIndex": 1, + "spentHeight": 203674 + } + ], + "vin": [ + { + "txid": "4ae8d1960c9a4ed83dbeaf1ad94b4a82f11c8574207144beda87113d94a31da1", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "47304402205483f0b26a04876fe8eceb9a5d86b1da93012be27b5482a3d53443cda5ee7a13022027410cef1e19c2620ad0da1b800252b620e3e8dc576efb069e95eb1d2222ccfb01210221d2f6bd1b101dc4eb40f570af2a87221e3ffa166b6c5eac2faf496b0c53cbdf", + "asm": "304402205483f0b26a04876fe8eceb9a5d86b1da93012be27b5482a3d53443cda5ee7a13022027410cef1e19c2620ad0da1b800252b620e3e8dc576efb069e95eb1d2222ccfb[ALL] 0221d2f6bd1b101dc4eb40f570af2a87221e3ffa166b6c5eac2faf496b0c53cbdf" + }, + "addr": "yUvr9AxuFx3ifp8HHFdYnVGbvK8Qqz25SQ", + "valueSat": 1999990000, + "value": 19.9999, + "doubleSpentTxID": null + } + ] + }, + "688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23": { + "txid": "688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23", + "blockhash": "00000000220536db0f4c0afc5d3ec80b36a5a253390359b3e3bb7aaf7664edc6", + "blockheight": 206745, + "blocktime": 1534255937, + "fees": 1944, + "size": 22, + "txlock": false, + "vout": [ + { + "n": 0, + "value":"0.98760000", + "scriptPubKey": { + "hex": "76a9143a17e49041b36ac60ab8e494b0c52055ed48cfd388ac", + "asm":"OP_DUP OP_HASH160 3a17e49041b36ac60ab8e494b0c52055ed48cfd3 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRccfS185LFxLYndspnM3CpWVokXmV69GN" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.01220560", + "n": 1, + "scriptPubKey": { + "hex": "76a914fdab39799641911dd9fa518b016f41583c2e197888ac", + "asm":"OP_DUP OP_HASH160 fdab39799641911dd9fa518b016f41583c2e1978 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yjSivd8eWH1vVywaeePiHBLXqMbHFXxxXE" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "txid": "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc", + "vout": 0, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100ebd945fe363ba0da355f4e77000c45cd967374f763b84332bdef2b236aa7b41602200e4600f026f2f0dc6ab003002b30bdd57c50d5d1adb6e776ab20b448d00961e70121022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536", + "asm":"3045022100ebd945fe363ba0da355f4e77000c45cd967374f763b84332bdef2b236aa7b41602200e4600f026f2f0dc6ab003002b30bdd57c50d5d1adb6e776ab20b448d00961e7[ALL] 022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536" + }, + "addr": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "valueSat": 100000000, + "value": 1, + "doubleSpentTxID": null + } + ] + }, + "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc": { + "txid": "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc", + "blockhash": "0000000007a3b51cccc3d8516917d3e8386dd82ec49322c790a0a873ee4e54d2", + "blockheight": 204344, + "blocktime": 1533926635, + "fees": 245, + "size": 225, + "txlock": false, + "vout": [ + { + "n": 0, + "value": "1.00000000", + "scriptPubKey": { + "hex": "76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac", + "asm": "OP_DUP OP_HASH160 f8c2652847720ab6d401291e5a48e2c8fe5d3c9f OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8" + ], + "type": "pubkeyhash" + }, + "spentTxId": "688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23", + "spentIndex": 0, + "spentHeight": 206745 + }, + { + "value": "88.66014395", + "n": 1, + "scriptPubKey": { + "hex": "76a914891b30309bb70c129735bb48e520bc85e21cf70388ac", + "asm": "OP_DUP OP_HASH160 891b30309bb70c129735bb48e520bc85e21cf703 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yYpPzZCZ5BrU5RDaU8V2kBDbLzyP1xhTDj" + ], + "type": "pubkeyhash" + }, + "spentTxId": "ede87c04a7bbf3084d5f6118928742759cc49d1e61d033e56c5d2bd1d6e0c053", + "spentIndex": 0, + "spentHeight": 205767 + } + ], + "vin": [ + { + "txid": "1a855e19b90ca52851a94c0e520ee6a3eaa91bdc2bb84cdda1969b5b5b76201a", + "vout": 0, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "47304402205c4d90dd2187eb069e546b28b6552617c098d0d0a2fb73e03e5dbd1afcd37bea02204ff1ce09e975294cfef0b64126cea2cf7957972cb4bc84194c55f6856f2e1fcf01210287d90f447cdbf4d9c5557276702ce320c0026a7f3aba6add8a94b97eb5503c17", + "asm":"304402205c4d90dd2187eb069e546b28b6552617c098d0d0a2fb73e03e5dbd1afcd37bea02204ff1ce09e975294cfef0b64126cea2cf7957972cb4bc84194c55f6856f2e1fcf[ALL] 0287d90f447cdbf4d9c5557276702ce320c0026a7f3aba6add8a94b97eb5503c17" + }, + "addr": "yYC5x9QkcKcRyYVdzaAVAArSCD39byLRcm", + "valueSat": 8966014640, + "value": 89.6601464, + "doubleSpentTxID": null + } + ] + } + }, + "addresses": { + "external": { + "m/44'/1'/0'/0/0": { + "address": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "balanceSat": 0, + "fetchedLast": 1534867407075, + "path": "m/44'/1'/0'/0/0", + "transactions": [ + "688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23", + "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc", + "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35", + "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6" + ], + "unconfirmedBalanceSat": 0, + "used": true, + "utxos": [] + }, + "m/44'/1'/0'/0/1": { + "address": "yhLGmtf5Jmdb3DUvsaNJUHyCjjxTcBJEry", + "balanceSat": 0, + "fetchedLast": 1534867406705, + "path": "m/44'/1'/0'/0/1", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/0/2": { + "address": "yfX4zBhV6syJTbRJLwxhCNdXkDnE7Mj1N7", + "balanceSat": 0, + "fetchedLast": 1534867406711, + "path": "m/44'/1'/0'/0/2", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/0/3": { + "address": "ySqLBsyTsyAJ9ee3tamaVPrHyN4tpYPH4b", + "balanceSat": 0, + "fetchedLast": 1534867406727, + "path": "m/44'/1'/0'/0/3", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/0/4": { + "address": "yjageEcheCFsHiVJTonpJ7MuXqox7skPuz", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/0/4", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/0/5": { + "address": "yYTWoV19dxRrsBxGjGt8aRjSPviqTPK6Yv", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/0/5", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/0/6": { + "address": "yUf3iBng8e5xYu3HjgPHeZ5TeT3n3DrDEx", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/0/6", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/0/7": { + "address": "yeDeNn3xUtRdQJDrreqq739krZ4Gq6Cr3W", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/0/7", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/0/8": { + "address": "yVXJVuzQys4WdPXn1bThSvQNb6Y7ni9dav", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/0/8", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/0/9": { + "address": "yN53BceF98DsnmMy6CFWvWAUsVE6LYm35X", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/0/9", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/0/10": { + "address": "yPetUJo1WeAjLpNYACntNSgEvHuUu3p1a8", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/0/10", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + } + }, + "internal": { + "m/44'/1'/0'/1/0": { + "address": "yjSivd8eWH1vVywaeePiHBLXqMbHFXxxXE", + "balanceSat": 1220560, + "fetchedLast": 1534867407092, + "path": "m/44'/1'/0'/1/0", + "transactions": [ + "688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23" + ], + "unconfirmedBalanceSat": 0, + "utxos": [ + "688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23" + ], + "used": true + }, + "m/44'/1'/0'/1/1": { + "address": "yTM7nPiekjMBkMCU6cPmFD2KReeFUeVwCp", + "balanceSat": 99890000, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/1/1", + "transactions": [ + "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35" + ], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": true + }, + "m/44'/1'/0'/1/2": { + "address": "yXy7rfGH5tZJs9GmFbdk6hPg2mzb8MRdfC", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/1/2", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/1/3": { + "address": "yZgVHYQb63az5Fex8Xs8K6T2ZgR5RbuawE", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/1/3", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/1/4": { + "address": "yc9ZmPczW1D6mFQneTAo3KAwGmpddV2zMc", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/1/4", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/1/5": { + "address": "yf1Znbd7eUTgFpaFqMSs6uu3s5cixF3rWG", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/1/5", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/1/6": { + "address": "yaRW7DRpAEXLfKBps5yUVmJnBaAyc6q7iq", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/1/6", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/1/7": { + "address": "yXVuvMZRiAdSBni27ERcx6R2pLuaaDKdsZ", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/1/7", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/1/8": { + "address": "yPpC6Tjo3occEXHBvLWt3q8UD5KS75pKGb", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/1/8", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/1/9": { + "address": "yM471igq2M2Drf6tXa8dcWk8BQ6NfxaTtf", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/1/9", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + } + }, + "misc": {} + } + }, + "livenet":{ + "transactions":{}, + "addresses":{ + "internal":{}, + "external":{}, + "misc":{} + } + } +} \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/knifeeasily.json b/packages/wallet-lib/fixtures/knifeeasily.json new file mode 100644 index 00000000000..42706c3769d --- /dev/null +++ b/packages/wallet-lib/fixtures/knifeeasily.json @@ -0,0 +1,15 @@ +{ + "mnemonic":"knife easily prosper input concert merge prepare autumn pen blood glance toilet", + "walletIdTestnet":"906469385b", + "walletIdMainnet":"419829f73c", + "seed": "9e55e5cb0e2fe273600cf5af7d7760fe569121c320395f0233202b97445e54f577d5a706c49aa1f3f0993d2ff97e2e6d63e4ccd0b7e9d4c4f115ae58957a9114", + "passphrase": "myPassphrase", + "HDRootEncryptedPrivateKeyMainnet":"xprv9s21ZrQH143K2Gcg8Ey7FA7xrA2x8B8cnod787pVLgQjaNH2t4cM2TjB8dU5LSp5YbhckFRSYTdaKwzBdUavjFsnrKauwW23ytHYesCoCmG", + "HDRootEncryptedPrivateKeyTestnet":"tprv8ZgxMBicQKsPd5rCnopcQojxAHTAMhAd8MYDzYEwpeuDMy27sRx6YD6d3odjLpCPv3EPkM3ChpDNnoXvkgvsYK9PNxoDbrk6tz2y6WGXK5R", + "HDRootPrivateKeyMainnet":"xprv9s21ZrQH143K37d2j9YW7snYGbAJJX9vzEZRwU7QEc4yP39t1Yc7t2Aw79aBBQWfLNqnpo9bFRnWoDv7xCPyBpLFHZvvrtVYfRv2zEBtnT5", + "HDRootPrivateKeyTestnet":"tprv8ZgxMBicQKsPdvrZPiQ1HXQXaiaWY3BwKnUYotXriaZTAdtxzuwsPmYP2KjqBmtyhpNZptmMQnNKG5Ts5QjuzsbqpD9EXFDbaXfTRxbTNsr", + "HDRootPublicKeyMainnet":"xpub6DpxeMAsZivtkMfFg82pHMuwXaCcwTNZUdmUecLqZ8gwbgSLCqqeBRDEDAtMYxDCNRDTEpvvYrXETjibBaY2pDMmFqqwqgkgWbQtUerQTVT", + "HDRootPublicKeyTestnet":"tpubDECbAazZGztM29r78K1uVHFJrhJGvqsd2GMn1APyu3SqLy73H4gjKrt4TCy95TASAKkMrQSo5xMH9vDCaSPGhep4KSbFXDQv6Z1JEgt3m1J", + "HDPrivateKeyMainnetWalletId":"419829f73c", + "HDPrivateKeyTestnetWalletId":"906469385b" +} diff --git a/packages/wallet-lib/fixtures/misc.json b/packages/wallet-lib/fixtures/misc.json new file mode 100644 index 00000000000..f3af119e6a3 --- /dev/null +++ b/packages/wallet-lib/fixtures/misc.json @@ -0,0 +1,189 @@ +{ + "9ab39713e9ce713d41ca6974db83e57bced02402e9516b8a662ed60d5c08f6d1": { + "blockhash": "000000000a84c4703da7a69cfa65837251e4aac80e1621f2a2cc9504e0c149ba", + "blockheight": 201436, + "blocktime": 1533525448, + "fees": 1000, + "size": 225, + "txid": "9ab39713e9ce713d41ca6974db83e57bced02402e9516b8a662ed60d5c08f6d1", + "txlock": true, + "vin": [ + { + "txid": "e4524e918977b70ab47160d8e3b87a5fa9f88f22e43f0eec2abbee2cf364c93b", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "4730440220154e37879e70784daff6cf04993cc88e8cf7e5357f82e98df9c117941cd5b3f702200d02f583085fbfd28c77d31c6b9a69f641a8fef5e99aaec8b8aedd4a3326e4100121025deea4fcd79eb876daa0f5829659c76f00f6b3fe6bf12e3ea83ecc763219bf88", + "asm": "30440220154e37879e70784daff6cf04993cc88e8cf7e5357f82e98df9c117941cd5b3f702200d02f583085fbfd28c77d31c6b9a69f641a8fef5e99aaec8b8aedd4a3326e410[ALL] 025deea4fcd79eb876daa0f5829659c76f00f6b3fe6bf12e3ea83ecc763219bf88" + }, + "addr": "yhzoBe1aCTTganFBzFb3ErF4ufwMqonK5a", + "valueSat": 81246619083, + "value": 812.46619083, + "doubleSpentTxID": null + } + ], + "vout": [ + { + "value": "2.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914df128447b46f9c81edbf13494d12aabca066b65688ac", + "asm": "OP_DUP OP_HASH160 df128447b46f9c81edbf13494d12aabca066b656 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ygewiYb7ZJxU4uuNGEVzbbA3wZEpEQJKhr" + ], + "type": "pubkeyhash" + }, + "spentTxId": "6b90bf01b10a0c6cac018d376823f6b330edf2cbb783cc3d02004f8706bbc311", + "spentIndex": 7, + "spentHeight": 203517 + }, + { + "value": "810.46609083", + "n": 1, + "scriptPubKey": { + "hex": "76a914f67b2c4f47ea0a2bae829d3816a01cc486463d7988ac", + "asm": "OP_DUP OP_HASH160 f67b2c4f47ea0a2bae829d3816a01cc486463d79 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yinidcHwrfzb4bEJDSq3wtQyxRAgQxsQia" + ], + "type": "pubkeyhash" + }, + "spentTxId": "22c368e09ad8b36553b383c6a4ae989f91d1f66622b2b685262580c8a45175a4", + "spentIndex": 0, + "spentHeight": 203155 + } + ] + }, + "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6": { + "txid": "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6", + "blockhash": "000000000388f8a1de91702e25209aad802d28fcd2710ac2e2acd12e9738dbe2", + "blockheight": 203633, + "blocktime": 1533827521, + "fees": 1000, + "size": 225, + "txlock": true, + "vout": [ + { + "value": "1.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac", + "asm": "OP_DUP OP_HASH160 f8c2652847720ab6d401291e5a48e2c8fe5d3c9f OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8" + ], + "type": "pubkeyhash" + }, + "spentTxId": "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35", + "spentIndex": 0, + "spentHeight": 204161 + }, + { + "value": "18.99980000", + "n": 1, + "scriptPubKey": { + "hex": "76a9147d39e5ff6ea7b82c6dc69994f1075f157bee9ef688ac", + "asm": "OP_DUP OP_HASH160 7d39e5ff6ea7b82c6dc69994f1075f157bee9ef6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yXjag2zjTwA5Ya2sSV5KoFzTvE6uKdrnKa" + ], + "type": "pubkeyhash" + }, + "spentTxId": "6ca8795f2534972e1371249c3d7b6c5095e1513bc8cc351eeaa2f364020dbc01", + "spentIndex": 1, + "spentHeight": 203674 + } + ], + "vin": [ + { + "txid": "4ae8d1960c9a4ed83dbeaf1ad94b4a82f11c8574207144beda87113d94a31da1", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "47304402205483f0b26a04876fe8eceb9a5d86b1da93012be27b5482a3d53443cda5ee7a13022027410cef1e19c2620ad0da1b800252b620e3e8dc576efb069e95eb1d2222ccfb01210221d2f6bd1b101dc4eb40f570af2a87221e3ffa166b6c5eac2faf496b0c53cbdf", + "asm": "304402205483f0b26a04876fe8eceb9a5d86b1da93012be27b5482a3d53443cda5ee7a13022027410cef1e19c2620ad0da1b800252b620e3e8dc576efb069e95eb1d2222ccfb[ALL] 0221d2f6bd1b101dc4eb40f570af2a87221e3ffa166b6c5eac2faf496b0c53cbdf" + }, + "addr": "yUvr9AxuFx3ifp8HHFdYnVGbvK8Qqz25SQ", + "valueSat": 1999990000, + "value": 19.9999, + "doubleSpentTxID": null + } + ] + }, + "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35": { + "txid": "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35", + "blockhash": "0000000002922d7d0a69ce3e29908dc26aba6565af760d208dac77a8b520fbf3", + "blockheight": 204161, + "blocktime": 1533900199, + "fees": 1000, + "size": 226, + "txlock": false, + "vout": [ + { + "value": "0.00100000", + "n": 0, + "scriptPubKey": { + "hex": "76a914f3145d42d98196ca439eee48da05af56c2d7c4a688ac", + "asm": "OP_DUP OP_HASH160 f3145d42d98196ca439eee48da05af56c2d7c4a6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yiUjSkhkAfaHfYYmTMhc27NCmogJ3iRBaS" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.99890000", + "n": 1, + "scriptPubKey": { + "hex": "76a9144d19cc13106cac13b386c89b003b02992ae8c5e688ac", + "asm": "OP_DUP OP_HASH160 4d19cc13106cac13b386c89b003b02992ae8c5e6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yTM7nPiekjMBkMCU6cPmFD2KReeFUeVwCp" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "txid": "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6", + "vout": 0, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100ad3c7a5eae5ccb491c0e2cc3521df2cc410bf403340d6587848dc5ceb2b6831f022016b622ee329a4ceb1364d3e7295832af30e4fbc63420257b5b4971185048ac410121022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536", + "asm": "3045022100ad3c7a5eae5ccb491c0e2cc3521df2cc410bf403340d6587848dc5ceb2b6831f022016b622ee329a4ceb1364d3e7295832af30e4fbc63420257b5b4971185048ac41[ALL] 022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536" + }, + "addr": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "valueSat": 100000000, + "value": 1, + "doubleSpentTxID": null + } + ] + }, + "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8":{ + "address": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "balanceSat": 0, + "fetchedLast": 1534867407075, + "index":0, + "path": "m/44'/1'/0'/0/0", + "transactions": [ + "688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23", + "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc", + "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35", + "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6" + ], + "unconfirmedBalanceSat": 0, + "used": true, + "utxos": [] + } +} \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/mockedStore1.json b/packages/wallet-lib/fixtures/mockedStore1.json new file mode 100644 index 00000000000..96174542ec5 --- /dev/null +++ b/packages/wallet-lib/fixtures/mockedStore1.json @@ -0,0 +1,374 @@ +{ + "wallets": { + "123456789": { + "addresses": { + "external": { + "m/44'/1'/0'/0/0": { + "address": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "balanceSat": 100000000, + "fetchedLast": 0, + "path": "m/44'/1'/0'/0/0", + "transactions": [ + "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc", + "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35", + "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6" + ], + "index": 0, + "unconfirmedBalanceSat": 0, + "used": true, + "utxos": [ + { + "address": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "txid": "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc", + "outputIndex": 0, + "scriptPubKey": "76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac", + "satoshis": 100000000 + } + ] + }, + "m/44'/1'/0'/0/1": { + "address": "yhLGmtf5Jmdb3DUvsaNJUHyCjjxTcBJEry", + "balanceSat": 0, + "fetchedLast": 1534867406705, + "path": "m/44'/1'/0'/0/1", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + }, + "m/44'/1'/0'/0/2": { + "address": "yfX4zBhV6syJTbRJLwxhCNdXkDnE7Mj1N7", + "balanceSat": 0, + "fetchedLast": 1534867406711, + "path": "m/44'/1'/0'/0/2", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + } + }, + "internal": { + "m/44'/1'/0'/1/0": { + "address": "yjSivd8eWH1vVywaeePiHBLXqMbHFXxxXE", + "balanceSat": 1220560, + "fetchedLast": 1534867407092, + "path": "m/44'/1'/0'/1/0", + "transactions": [ + "688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23" + ], + "unconfirmedBalanceSat": 0, + "utxos": [ + "688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23" + ], + "used": true + }, + "m/44'/1'/0'/1/1": { + "address": "yTM7nPiekjMBkMCU6cPmFD2KReeFUeVwCp", + "balanceSat": 99890000, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/1/1", + "transactions": [ + "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35" + ], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": true + }, + "m/44'/1'/0'/1/2": { + "address": "yXy7rfGH5tZJs9GmFbdk6hPg2mzb8MRdfC", + "balanceSat": 0, + "fetchedLast": 1534867407080, + "path": "m/44'/1'/0'/1/2", + "transactions": [], + "unconfirmedBalanceSat": 0, + "utxos": [], + "used": false + } + }, + "misc": {} + } + } + }, + "transactions": { + "9ab39713e9ce713d41ca6974db83e57bced02402e9516b8a662ed60d5c08f6d1": { + "blockhash": "000000000a84c4703da7a69cfa65837251e4aac80e1621f2a2cc9504e0c149ba", + "blockheight": 201436, + "blocktime": 1533525448, + "fees": 1000, + "size": 225, + "txid": "9ab39713e9ce713d41ca6974db83e57bced02402e9516b8a662ed60d5c08f6d1", + "txlock": true, + "vin": [ + { + "txid": "e4524e918977b70ab47160d8e3b87a5fa9f88f22e43f0eec2abbee2cf364c93b", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "4730440220154e37879e70784daff6cf04993cc88e8cf7e5357f82e98df9c117941cd5b3f702200d02f583085fbfd28c77d31c6b9a69f641a8fef5e99aaec8b8aedd4a3326e4100121025deea4fcd79eb876daa0f5829659c76f00f6b3fe6bf12e3ea83ecc763219bf88", + "asm": "30440220154e37879e70784daff6cf04993cc88e8cf7e5357f82e98df9c117941cd5b3f702200d02f583085fbfd28c77d31c6b9a69f641a8fef5e99aaec8b8aedd4a3326e410[ALL] 025deea4fcd79eb876daa0f5829659c76f00f6b3fe6bf12e3ea83ecc763219bf88" + }, + "addr": "yhzoBe1aCTTganFBzFb3ErF4ufwMqonK5a", + "valueSat": 81246619083, + "value": 812.46619083, + "doubleSpentTxID": null + } + ], + "vout": [ + { + "value": "2.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914df128447b46f9c81edbf13494d12aabca066b65688ac", + "asm": "OP_DUP OP_HASH160 df128447b46f9c81edbf13494d12aabca066b656 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ygewiYb7ZJxU4uuNGEVzbbA3wZEpEQJKhr" + ], + "type": "pubkeyhash" + }, + "spentTxId": "6b90bf01b10a0c6cac018d376823f6b330edf2cbb783cc3d02004f8706bbc311", + "spentIndex": 7, + "spentHeight": 203517 + }, + { + "value": "810.46609083", + "n": 1, + "scriptPubKey": { + "hex": "76a914f67b2c4f47ea0a2bae829d3816a01cc486463d7988ac", + "asm": "OP_DUP OP_HASH160 f67b2c4f47ea0a2bae829d3816a01cc486463d79 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yinidcHwrfzb4bEJDSq3wtQyxRAgQxsQia" + ], + "type": "pubkeyhash" + }, + "spentTxId": "22c368e09ad8b36553b383c6a4ae989f91d1f66622b2b685262580c8a45175a4", + "spentIndex": 0, + "spentHeight": 203155 + } + ] + }, + "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6": { + "txid": "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6", + "blockhash": "000000000388f8a1de91702e25209aad802d28fcd2710ac2e2acd12e9738dbe2", + "blockheight": 203633, + "blocktime": 1533827521, + "fees": 1000, + "size": 225, + "txlock": true, + "vout": [ + { + "value": "1.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac", + "asm": "OP_DUP OP_HASH160 f8c2652847720ab6d401291e5a48e2c8fe5d3c9f OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8" + ], + "type": "pubkeyhash" + }, + "spentTxId": "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35", + "spentIndex": 0, + "spentHeight": 204161 + }, + { + "value": "18.99980000", + "n": 1, + "scriptPubKey": { + "hex": "76a9147d39e5ff6ea7b82c6dc69994f1075f157bee9ef688ac", + "asm": "OP_DUP OP_HASH160 7d39e5ff6ea7b82c6dc69994f1075f157bee9ef6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yXjag2zjTwA5Ya2sSV5KoFzTvE6uKdrnKa" + ], + "type": "pubkeyhash" + }, + "spentTxId": "6ca8795f2534972e1371249c3d7b6c5095e1513bc8cc351eeaa2f364020dbc01", + "spentIndex": 1, + "spentHeight": 203674 + } + ], + "vin": [ + { + "txid": "4ae8d1960c9a4ed83dbeaf1ad94b4a82f11c8574207144beda87113d94a31da1", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "47304402205483f0b26a04876fe8eceb9a5d86b1da93012be27b5482a3d53443cda5ee7a13022027410cef1e19c2620ad0da1b800252b620e3e8dc576efb069e95eb1d2222ccfb01210221d2f6bd1b101dc4eb40f570af2a87221e3ffa166b6c5eac2faf496b0c53cbdf", + "asm": "304402205483f0b26a04876fe8eceb9a5d86b1da93012be27b5482a3d53443cda5ee7a13022027410cef1e19c2620ad0da1b800252b620e3e8dc576efb069e95eb1d2222ccfb[ALL] 0221d2f6bd1b101dc4eb40f570af2a87221e3ffa166b6c5eac2faf496b0c53cbdf" + }, + "addr": "yUvr9AxuFx3ifp8HHFdYnVGbvK8Qqz25SQ", + "valueSat": 1999990000, + "value": 19.9999, + "doubleSpentTxID": null + } + ] + }, + "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35": { + "txid": "1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35", + "blockhash": "0000000002922d7d0a69ce3e29908dc26aba6565af760d208dac77a8b520fbf3", + "blockheight": 204161, + "blocktime": 1533900199, + "fees": 1000, + "size": 226, + "txlock": false, + "vout": [ + { + "value": "0.00100000", + "n": 0, + "scriptPubKey": { + "hex": "76a914f3145d42d98196ca439eee48da05af56c2d7c4a688ac", + "asm": "OP_DUP OP_HASH160 f3145d42d98196ca439eee48da05af56c2d7c4a6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yiUjSkhkAfaHfYYmTMhc27NCmogJ3iRBaS" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.99890000", + "n": 1, + "scriptPubKey": { + "hex": "76a9144d19cc13106cac13b386c89b003b02992ae8c5e688ac", + "asm": "OP_DUP OP_HASH160 4d19cc13106cac13b386c89b003b02992ae8c5e6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yTM7nPiekjMBkMCU6cPmFD2KReeFUeVwCp" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "txid": "7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6", + "vout": 0, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100ad3c7a5eae5ccb491c0e2cc3521df2cc410bf403340d6587848dc5ceb2b6831f022016b622ee329a4ceb1364d3e7295832af30e4fbc63420257b5b4971185048ac410121022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536", + "asm": "3045022100ad3c7a5eae5ccb491c0e2cc3521df2cc410bf403340d6587848dc5ceb2b6831f022016b622ee329a4ceb1364d3e7295832af30e4fbc63420257b5b4971185048ac41[ALL] 022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536" + }, + "addr": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "valueSat": 100000000, + "value": 1, + "doubleSpentTxID": null + } + ] + }, + "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc": { + "txid": "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc", + "blockhash": "0000000007a3b51cccc3d8516917d3e8386dd82ec49322c790a0a873ee4e54d2", + "blockheight": 204344, + "blocktime": 0, + "fees": 245, + "size": 225, + "txlock": false, + "vin": [ + { + "addr": "yYC5x9QkcKcRyYVdzaAVAArSCD39byLRcm", + "txid": "1a855e19b90ca52851a94c0e520ee6a3eaa91bdc2bb84cdda1969b5b5b76201a", + "valueSat": 8966014640, + "vout": 0, + "scriptSig": { + "hex": "47304402205c4d90dd2187eb069e546b28b6552617c098d0d0a2fb73e03e5dbd1afcd37bea02204ff1ce09e975294cfef0b64126cea2cf7957972cb4bc84194c55f6856f2e1fcf01210287d90f447cdbf4d9c5557276702ce320c0026a7f3aba6add8a94b97eb5503c17" + } + } + ], + "vout": [ + { + "value": "1.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac", + "asm": "OP_DUP OP_HASH160 f8c2652847720ab6d401291e5a48e2c8fe5d3c9f OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "88.66014395", + "n": 1, + "scriptPubKey": { + "hex": "76a914891b30309bb70c129735bb48e520bc85e21cf70388ac", + "asm": "OP_DUP OP_HASH160 891b30309bb70c129735bb48e520bc85e21cf703 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yYpPzZCZ5BrU5RDaU8V2kBDbLzyP1xhTDj" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ] + }, + "688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23": { + "txid": "688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23", + "blockhash": "00000000220536db0f4c0afc5d3ec80b36a5a253390359b3e3bb7aaf7664edc6", + "blockheight": 206745, + "blocktime": 1534255937, + "fees": 1944, + "size": 22, + "txlock": false, + "vout": [ + { + "n": 0, + "value": "0.98760000", + "scriptPubKey": { + "hex": "76a9143a17e49041b36ac60ab8e494b0c52055ed48cfd388ac", + "asm": "OP_DUP OP_HASH160 3a17e49041b36ac60ab8e494b0c52055ed48cfd3 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yRccfS185LFxLYndspnM3CpWVokXmV69GN" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.01220560", + "n": 1, + "scriptPubKey": { + "hex": "76a914fdab39799641911dd9fa518b016f41583c2e197888ac", + "asm": "OP_DUP OP_HASH160 fdab39799641911dd9fa518b016f41583c2e1978 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yjSivd8eWH1vVywaeePiHBLXqMbHFXxxXE" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "txid": "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc", + "vout": 0, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100ebd945fe363ba0da355f4e77000c45cd967374f763b84332bdef2b236aa7b41602200e4600f026f2f0dc6ab003002b30bdd57c50d5d1adb6e776ab20b448d00961e70121022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536", + "asm": "3045022100ebd945fe363ba0da355f4e77000c45cd967374f763b84332bdef2b236aa7b41602200e4600f026f2f0dc6ab003002b30bdd57c50d5d1adb6e776ab20b448d00961e7[ALL] 022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536" + }, + "addr": "yizmJb63ygipuJaRgYtpWCV2erQodmaZt8", + "valueSat": 100000000, + "value": 1, + "doubleSpentTxID": null + } + ] + } + } +} \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/plugins/FaultyWorker.js b/packages/wallet-lib/fixtures/plugins/FaultyWorker.js new file mode 100644 index 00000000000..cd3c604ba55 --- /dev/null +++ b/packages/wallet-lib/fixtures/plugins/FaultyWorker.js @@ -0,0 +1,21 @@ +const Worker = require('../../src/plugins/Worker'); + +class FaultyWorker extends Worker { + constructor() { + super({ + name: 'FaultyWorker', + firstExecutionRequired: true, + executeOnStart: true, + dependencies: [ + 'storage', 'walletId', + ], + }); + } + + // eslint-disable-next-line class-methods-use-this + execute() { + throw new Error('Some reason.'); + } +} + +module.exports = FaultyWorker; diff --git a/packages/wallet-lib/fixtures/plugins/SyncWorker/transactions.set.0.json b/packages/wallet-lib/fixtures/plugins/SyncWorker/transactions.set.0.json new file mode 100644 index 00000000000..7d0cbcde6f2 --- /dev/null +++ b/packages/wallet-lib/fixtures/plugins/SyncWorker/transactions.set.0.json @@ -0,0 +1,67 @@ +[ + { + "hex": "03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502c80f0101ffffffff0200972cbb080000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88ac00dd0ee9020000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88ac00000000260100c80f00002355b4fc221f47853e91247fa87ccd092362aa0bb50876822e6c950e8f6763f1", + "description": "Funding tx from coinbase to faucet of firstTx", + "txid": "263cef79694535b94b2a49595df127e8b1e19cd87e3a76f6589263530196a1fb" + }, + { + "hex": "0200000002fba1960153639258f6763a7ed89ce1b1e827f15d59492a4bb935456979ef3c26010000006b483045022100f153677c41e8201abbdfbb970bee97710fc4bcd78dd76c1f0f2a48b20f92b4c802204b4d515b77bca4d864b5a2be18ce7c2e5eafe1a07a313d1ed0cc3baf5327cc7d012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1feffffff498159ea46e263b05ce956dd2aa2b6561b8af354c5cf17a7053f4be5507b9182000000006a47304402200f41e7f33940407cbbfbc5ed4941909af24a8e2c40d5730cfcaa9ef05ffe65bf022001337acaf85f216d4fe205fc2f45e2b808e608d196c4148c96df415db438ac3b0121030c199868fe7cd96b9bca582c07217b20afd62da6fcf3dffe98d06ed85a41e4bafeffffff02e835442f000000001976a91410ab140da0f7ad02e0a2d35f5b9591773ae3593388ace085871e030000001976a914e0a72dd6643cf7bf8f7c5ce94800152c231ca6c488ac0c1f0000", + "description": "First tx - Faucet to external 0", + "txid": "4bddd3b1575e6f4ae4f5bbfe7058fa8353fcf5260c3976ae372870918f5502cf" + }, + { + "hex": "0200000001b9700e825bdeec45376c13857546193ce5c0654ef56e4fdcbc9163eb8c174fe7010000006a473044022001f6c2aaadf3143fb8be53c458a7e24d17f83bc582dec0897b1833c95c42c045022016a75e29f1a930eb96fdb8a1a361dd9e181e55260e033d261bef854937585de2012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1feffffff028eb24b04000000001976a914a48edba370d7b127a064a096a3f08c2c0a2392d788ac9022c679030000001976a914e0a72dd6643cf7bf8f7c5ce94800152c231ca6c488ac111f0000", + "description": "second tx - Faucet to external 0", + "txid": "0f817d1255aaad3467bf928a3aad2b177a9dba0cbbf4fe4cbc5ac225ae3cccdd" + }, + { + "hex": "0200000001213e6473e280e54e4f316345bfe40dfcd7a3c420a7f7036ec3345b096582c4cc010000006a47304402205c38bef4b3901d1b14c9b3b18c08edc61301b1227f0d111e02a8f1ba1c1e1f580220650de45c40bf3fbbb070b0b7b529d97bfc24401e31f39563328b6b2f9daa9ca6012102cb03bdf195993c5c0f02d1dd24e645655fc6d36c535435bda8300bfa184122d2feffffff028cea515d000000001976a91433807504ebba453042e1643470dde61721dad63688ac506bfc8d000000001976a9141659ffaf75ea3913d610f33984a234b5732b989f88ac121f0000", + "description": "third tx - faucet to external 1", + "txid": "c1b696be90520195db2bf3dd04ce25e23f9bc06db193b95ffc8f0062c640f21f" + }, + { + "hex": "020000000189e9aa5e6576730ad90dc5e0d859e2883a16d48a7f8713c238934e9a5a683460010000006b483045022100a17681d5c272291351590adab2dac614f0979ee12d64691695723232dae33b3d02207096977286b3996cba9058e7fda0d7d982e92a36df7242a4ce74ac572abeed44012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1feffffff0252600c33000000001976a9147ce64fb944442c280e9f7c657809462c655986c888ac1032ff20020000001976a9149100a247a0807ede78c14d930042797b513af67088acba1e0000", + "description": "fourth tx - faucet to external 2", + "txid": "15500731ef06c431e21ea63d5a5d51f1b62e3c13defcb4f26888e29d712bf5cf" + }, + { + "hex": "0300000001cff52b719de28868f2b4fcde133c2eb6f1515d5a3da61ee231c406ef31075015010000006a47304402207bfa79c5e283547aac17ea54a84534127845baa3dbf777bba3b6e38a008ffcec0220196bd5158c1e5bf092a83cd50962593371428b6b0b693732ebe2236c15705611012103b740a16f4436913186fb40b99869c8af00ef8ac218389dd6781343a0b341e9eaffffffff0200ea56fa000000001976a914186f67c6d4c9989d95eee66d203deef2a2b08e3988ac1947a826010000001976a9149ccf7f564d442febb1ea715bd1546491dddefeeb88ac00000000", + "txid": "235f0cabc45dae2aaecfab732b0b0b1a81ca8120d43674769f7316190a1fed7d", + "description": "fourth tx outputs -> external 3" + }, + { + "hex": "03000000011ff240c662008ffc5fb993b16dc09b3fe225ce04ddf32bdb95015290be96b6c1010000006a47304402202515fad021aae7df407f683a3f827edf96804b7ba8805f6101f6c37c956266b70220324c10c7ab7bed991828ae6855444e68f17c51ece9f5c696ce44061cd4ae335c012103b58e782f1276890ceaa6621d492f5a793023ecb7aeec2b10b43f12bc6eb5937cffffffff0200371789000000001976a914186f67c6d4c9989d95eee66d203deef2a2b08e3988ac5933e504000000001976a914787daf9c23b9e0d6a649556f3f970bed2611e72b88ac00000000", + "txid": "1ae63ccb996eeb7f81ed19883742a6ece40ebdb2f67fa9a7da28bc1c9faddf2a", + "description": "third tx outputs -> external 3" + }, + { + "hex": "0300000002ddcc3cae25c25abc4cfef4bb0cba9d7a172bad3a8a92bf6734adaa55127d810f010000006b483045022100d21b18a560ac333bf06ac36766a98a54cf09cb6928ee75858a17c499279f10ac02206c601d03152a4b3e412e11bfc99427a80cb3430bbad37eed1008a5fc627b98c1012102dada4f582424e8ffa113abc68927f048ee5c2cc6c70e1a5f72a4f714f1ad38e9ffffffffcf02558f91702837ae76390c26f5fc5383fa5870febbf5e44a6f5e57b1d3dd4b010000006b483045022100b0e3cb268373edbd780c870202c2230e46be37f617a5092120f68ba8540fe69802207e4699713bf111533d14fc227c6d772c78af7d9b99865dce8d8c643f597b2588012102dada4f582424e8ffa113abc68927f048ee5c2cc6c70e1a5f72a4f714f1ad38e9ffffffff0240334d98060000001976a91486e9f0e8820389426ae3f2ed5563151e12a33fbd88aca6730000000000001976a914c5b792243d998137f40e0663cb1f3b158dfc46d388ac00000000", + "txid": "4848a0a7aa7883363929e19fac92c6e86157d3bd843f5c5d4688524eaf7d507f", + "description": "first and second outputs -> external 4" + }, + { + "hex": "0200000004ddcc3cae25c25abc4cfef4bb0cba9d7a172bad3a8a92bf6734adaa55127d810f000000006b483045022100dc9a7054a6896c07b3c8f40a322be811ec8dd048b58d27ebe97eec94659efed2022071243718d89de816b5dacf965342940308299f91ab42a144a44e3fe0b0627cc901210314cc82d9f3d6fcb8449eb343fe463f753e2a8847f3c6b3a23bcab1b2dd60fd91feffffff491f8c1099cfc1fbcb22acff94399251c02d08073de3b6b5920957c9724d2a37000000006b4830450221009cbd2041722089577c845a4feeb0a5071184a5c01e4b21e972a1dffee79a5da402206741265bfb022f7627d46742f038b5f6d86eb771c500784fdd2d58a8d22bbe4f01210331f390de785e172e0ee34aaf6edb405ea2bb6db81cd0ef054cb005b1820c1cc3feffffffcf02558f91702837ae76390c26f5fc5383fa5870febbf5e44a6f5e57b1d3dd4b000000006a4730440220043c0f170d8ebc3c5ec0cc7314584aa938dabb5aa624ea82c374de6765e4a3710220421e3de0a112311ff8669c1566520fee626393cb0d247729bcae0a4ef5273350012103ad99a115c10ac5524eef04ac62396d4458f62e9d8b0a91fd342767061b8f8b3bfeffffff15b082de493858dbc5a719a0447dcb3d6f33e0b1e64e5f5ffd4d485370b25b50000000006a4730440220124735b19eb44ce8e22eef8df65c525be0c17ac3c3df7babf32bbe34bc4539cc02202de68c9f181a455bb68abdad86258bc4ee4795d7ed5e08584617513e1404223d012103ead09988d4cf7bfe8a057156d3f59df5f1405aaf9a5a9f810e68596583dd8d78feffffff020ee42400000000001976a914ab5b23b2983d8fee1c34036e1216ef6344569a6e88ac7018b846000000001976a914c41be50a9bab414ba8cf773f2f291b1f15f5faae88ac251f0000", + "txid": "7ee3756a8d95edc4e552b670cd08bc08f5f2787c925b2046d153bdde0b9b8468", + "description": "Faucet to external 5" + }, + { + "hex": "03000000047ded1f0a1916739f767436d42081ca811a0b0b2b73abcfae2aae5dc4ab0c5f23010000006a4730440220299fa5bc4e71c6f06b80c15dc5b44abf24546843da74e1b0ed7573bf2ca625e1022026672c3b54150442bcb8f8260ea0a9454e36262a6ee068d551a0af0aeaecf1bc0121021f2c59dcc5a2668cc5d0cfe674b73a0070daa902baefffcfc2a8db4cb4fe692affffffff68849b0bdebd53d146205b927c78f2f508bc08cd70b652e5c4ed958d6a75e37e010000006b483045022100bb11e2d0a03d280fcf5133228f343ac9106651681dc3d74292947ea14c9be62002203ba8aeb5c502d0bc414a085ab34acf64145791f577bba06e3cacce275a260cf8012102a5cd0d1313f158a85999df0e73e0958c77a0db91935131e2ddd86503ac43e73affffffff2adfad9f1cbc28daa7a97ff6b2bd0ee4eca642378819ed817feb6e99cb3ce61a010000006a473044022023972e6a3716a65796ebf63e52fa8bd923abadc8080c662af4341fdb8f99254e022056d0386032030c76b3e655a0560c5a0d20d1be8f9ce1850292c2079bb5490b050121022bc3131124befab2ace7377112dcdc817db613ec7d1801a504d1fc52dde58258ffffffff7f507daf4e5288465d5c3f84bdd35761e8c692ac9fe12939368378aaa7a04848010000006b483045022100cc76c10dcbb736f51057a79d817ab05310c8a6940c3d0f5d5d84dd6b68b89e1a02202d2810a6d7f5c8c925611d5ab9e2c41fcd7edd4e6e6341edd3b905a9900eba16012102d97b3931f2a539e5498cfd4290c4ca1cb0ecabb5022912a5bd9219b61191b82cffffffff0108044672010000001976a914c41be50a9bab414ba8cf773f2f291b1f15f5faae88ac00000000", + "txid": "cd14ecc5864f23ff0bb2860acf1f291bf63be458371dcbff17fd8109230ae64a", + "description": "All outputs to external 5" + }, + { + "hex": "0200000004278ab6cae799de63b271607daf0f5aab6e9dc1c83004bccc9bd3b9d7bfcf3b6a000000006a47304402200cbe38de7a914c9a1cee7f574e96e7266e725d70fa2c41e1c38d147608cdf853022064cfc04ca799507aff7da583d262bdbd59c2753046aa296833c312f753fe8128012102e450ddd756a2d27d6daac6f0f54c6ddd2c09113ef6f27ba5d830f9f622914f66feffffff68849b0bdebd53d146205b927c78f2f508bc08cd70b652e5c4ed958d6a75e37e000000006b48304502210089fb392352c3f91b5c9fa4b5d89584a814faa5c35d5f6c5b10899a1c911670f602203d1f0eb9a3f2975444912cec91608e5271e4708eb2ab3ef7ac1af53883705fe8012103789b66f4cdde920b444e3dd275e8ce2eefc588ccad9fcd4f4af026a3321d5567feffffff2ed7cf20708e1a27908b50247d1d830356abdb155468653871c5d899686f97c8010000006a47304402200618a1c0b48d133b8f8a74c024212de71bddb8551838aeabc40746a1fa50bf3b022052ce06ed7217de64e0351d35d982d45101be1d8c5bf429deeb7926bf8c8bb874012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1feffffff680f8f2893f5e0d32ae88139301f0cef227e32e42208059218bbe688ff8950ca000000006a47304402201a95e47a8bd9fe86ec9b881b2bb42e309260dbd1bd761bd648a4a4ce5ab9671102205e021e3d4021fc27eff0978a27fe680a3eb645bc51b056fb55f5ada566d0fa620121038bde7562c092dc9904669285da7da48ee8c7a6fbb2a7ebebfccf83fc18c164c6feffffff02f6641900000000001976a914180447553b1d7605bf153a7a1c8af32fc36342ce88ac00749c2c040000001976a914c41be50a9bab414ba8cf773f2f291b1f15f5faae88ac2e1f0000", + "txid": "1cf58f61dc315fab99d5b483aac9d76b2f7e205dbc8e8f46faecfbc70ea2ec0b", + "description": "Faucet to external 5" + }, + { + "hex": "03000000057f507daf4e5288465d5c3f84bdd35761e8c692ac9fe12939368378aaa7a04848000000006b4830450221009dc832aa7462983402026024610ab41fc843305ff17e06ba55cdbb531f222e0e0220636612e3752c0c3243301e78b85e2e65ab5c19cf56ab547755811f9f7598c1540121031cb4f758aaf25921ee44385b498b1fab258a432f7eb2b6e44f6a50aa22b782edffffffff4ae60a230981fd17ffcb1d3758e43bf61b291fcf0a86b20bff234f86c5ec14cd000000006b483045022100e0d95af44766381f4e51e94f30eff570161d6d026877d597236c14490c1acd7c0220723c6ea040d3d8eee2645164748abc08471c3abac61cc6b474ac38aa8efce4c3012102a5cd0d1313f158a85999df0e73e0958c77a0db91935131e2ddd86503ac43e73affffffff7ded1f0a1916739f767436d42081ca811a0b0b2b73abcfae2aae5dc4ab0c5f23000000006a47304402202c778a828d772722bcf506c26b63a866aa51ff1edfc1d3c042d6a05d8aa9631f0220721d5b57ca6e54694b36983c99cd38e48553c2638af0908cc8d7950161eeb458012103a2c415c6b0a0abfb9b473d406242ae40fa549b90e40d9a8ce05c6644e36e8840ffffffff2adfad9f1cbc28daa7a97ff6b2bd0ee4eca642378819ed817feb6e99cb3ce61a000000006a473044022051fb12bad50db94adcbba4f0d1d9718f6c11f740e7a7a1ee3400797c7525b7c00220182fb3b0c94f737fb48322e024e9699210f884b4b07129dd438882a58093ae8e012103a2c415c6b0a0abfb9b473d406242ae40fa549b90e40d9a8ce05c6644e36e8840ffffffff0beca20ec7fbecfa468f8ebc5d207e2f6bd7c9aa83b4d599ab5f31dc618ff51c010000006a473044022057c1bb77492f8332a98c9b7b8ad832d7162c24a8baee03c8888b15d3c8395c9302206a3503f2f22e535bbf97362fb47bd7bbaf26e49ab1552947b0f0a698ca50fc10012102a5cd0d1313f158a85999df0e73e0958c77a0db91935131e2ddd86503ac43e73affffffff031273a76e030000001976a9140f8e48573065d5724a872dcea2a7eb2d8733eb5e88ac1273a76e030000001976a9149b9d4885b76ffdb96f71d4e5f35cda77d0676f0188accfe24edd060000001976a914b02e68ab5175e2976b2523189583caa83586d4f788ac00000000", + "txid": "6f3816cc894f88c311fd0ed92dd7c3580d0c8709493ba8f00e74418944fa1cc9", + "description": "Use all outputs with 1/4 to internal 6 and 1/4 external 7, half rest to internal" + }, + { + "hex": "0300000003c91cfa448941740ef0a83b4909870c0d58c3d72dd90efd11c3884f89cc16386f000000006a4730440220674ad7191abe3f68b237643a2b76ced4427b462f8cba194d8d4e9fea94dfdb250220253deff3becb3c9a4df8f7a2d913e867a59cca409b05a74e45a1b89ce9bc00af012103823eadb47338f9f07adcceb3680ffc0adb244dc23d369200629653a1c82bd35bffffffffc91cfa448941740ef0a83b4909870c0d58c3d72dd90efd11c3884f89cc16386f010000006b4830450221008fb4c115143851348d55cb61cda409f5a8d3477e7266db02e1a95527e84e784a02207eb5dd268eef75efa404d8d18578108f781b0fbbe76238502f42aa500103681b012102615f7b2ee4f8ee514f5acbe1e5b86d70b75f0e11fd39ff88efb8a0fd2ce8adf9ffffffffc91cfa448941740ef0a83b4909870c0d58c3d72dd90efd11c3884f89cc16386f020000006b483045022100d8406feb58c9e53d74f6710194c12070ad7c0e3259f584fff7087b5b93d687fd022064830dbfc523279da370f41c06eb4a8ab0e68a52b3e8224ef8bf959402b9178c01210365bccc3586d80e420726ec31c392558a0e4378968d1a30a49f95da2bdb2159deffffffff0600e1f505000000001976a91466b8708bc732bcd33efe8c0dec1fae5d29eb4ff588ac00e1f505000000001976a914472bcc0baf96fa4d32e5c4536bdb9da07dfe673e88ac00e1f505000000001976a91499af5448c3bb2f3f01461cc3f0a7c0877e907b6a88ac00e1f505000000001976a914308d7e975ee63c2821584799dda045a7f8afd84b88ac0029df9e0d0000001976a914b17dea4d281f200f6ccc21182692b8178c46126b88ac5219e703000000001976a914aed18ac8d433969e49828f7532ade1dfa3580ad888ac00000000", + "txid": "7422665b2b1b99fe37ee35a63cd71414515c3e17365ed986f0c5ef510695ed5b", + "description": "Send 1D to external 6, external 8, external 9 and external 10, 585 to int 5, rest to internal" + } +] diff --git a/packages/wallet-lib/fixtures/plugins/SyncWorker/transactions.set.1.json b/packages/wallet-lib/fixtures/plugins/SyncWorker/transactions.set.1.json new file mode 100644 index 00000000000..28cca8d6211 --- /dev/null +++ b/packages/wallet-lib/fixtures/plugins/SyncWorker/transactions.set.1.json @@ -0,0 +1,1298 @@ +[ + { + "hash": "bfec828ed8ed562f53921e9580e847670044e870dda0e67b8f8d0c8d77962f7f", + "version": 2, + "inputs": [ + { + "prevTxId": "1a678b6c50add6e5655f2f3a3a3a04e3b57c2ea60c238506e66f5f0f0b356eaf", + "outputIndex": 1, + "sequenceNumber": 4294967294, + "script": "483045022100e793672a2edd9b5fbaf9e8a5eeed86adf73811349624316e5673d7b2beb187e802203ab8916ed96d376622a05b4784e09c3b028908ade6961236e9860be123aaef94012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1", + "scriptString": "72 0x3045022100e793672a2edd9b5fbaf9e8a5eeed86adf73811349624316e5673d7b2beb187e802203ab8916ed96d376622a05b4784e09c3b028908ade6961236e9860be123aaef9401 33 0x03a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1" + } + ], + "outputs": [ + { + "satoshis": 1120559774, + "script": "76a914cbd2a5cdfe0686f69ac5e05fc0a87ba49822d95f88ac" + }, + { + "satoshis": 13879440000, + "script": "76a914fa4b2bb85ad9b4075addb6d0eb50fa8b60c746c588ac" + } + ], + "nLockTime": 5545 + }, + { + "hash": "d16bed019e13edb0ff2fd67a7336829a0db98344eaa3ff1d3ccd4a1dd6a942de", + "version": 3, + "inputs": [ + { + "prevTxId": "bfec828ed8ed562f53921e9580e847670044e870dda0e67b8f8d0c8d77962f7f", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100898cfd76279fb767697323660cc4b4f8354f4ca79aa217d7c9b255388f85fe48022009eef899c1b99b24cf98ef682baae569af6b72ee88144fab27b17b94ff9bbef50121032c3f05819b3d3792411582889ed6ccf4020d3b88c4498bcd251581029744f4d6", + "scriptString": "72 0x3045022100898cfd76279fb767697323660cc4b4f8354f4ca79aa217d7c9b255388f85fe48022009eef899c1b99b24cf98ef682baae569af6b72ee88144fab27b17b94ff9bbef501 33 0x032c3f05819b3d3792411582889ed6ccf4020d3b88c4498bcd251581029744f4d6" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 13879429320, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "b11a68555032cd0ae44e68208d4d7421b33052b50f7fb0ba994d92d92f0e3c83", + "version": 2, + "inputs": [ + { + "prevTxId": "6dda9362c757dfcbaf92a02bdc6d0ae270270d6bcadd9daad09c771ecf4ba674", + "outputIndex": 1, + "sequenceNumber": 4294967294, + "script": "483045022100f7c17adea632e01b0c19ad60b2ba1f92234b4788bc770cdcdbb0b84e2da05a8402204595ce1d8164a2edf6a671bf9009b91b6361983d6eb492e18866f9491d9a3d8e012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1", + "scriptString": "72 0x3045022100f7c17adea632e01b0c19ad60b2ba1f92234b4788bc770cdcdbb0b84e2da05a8402204595ce1d8164a2edf6a671bf9009b91b6361983d6eb492e18866f9491d9a3d8e01 33 0x03a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1" + } + ], + "outputs": [ + { + "satoshis": 1160579106, + "script": "76a914bb6472bcb572f8d43d40a84eeced7298cfc75b1888ac" + }, + { + "satoshis": 8839400000, + "script": "76a91460f52f2f4a3e085a9e1a24e7af625fc1570712e288ac" + } + ], + "nLockTime": 4906 + }, + { + "hash": "b5e8749958fca8441530c2c2403dafba13be7b90a303e96eb7ba65d6ac2ea207", + "version": 3, + "inputs": [ + { + "prevTxId": "b11a68555032cd0ae44e68208d4d7421b33052b50f7fb0ba994d92d92f0e3c83", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402203a36450035825232639b08159cbdd4881ad59fa9fd3567d9f34c38857187d66d022040de956c14f648ae117ee0eda7aed5d7f822e0acc4e514e7eb964400f64a16f1012103e68f91513a0a19306b9c763305784b402a7329700202e25781a4315e304bcd40", + "scriptString": "71 0x304402203a36450035825232639b08159cbdd4881ad59fa9fd3567d9f34c38857187d66d022040de956c14f648ae117ee0eda7aed5d7f822e0acc4e514e7eb964400f64a16f101 33 0x03e68f91513a0a19306b9c763305784b402a7329700202e25781a4315e304bcd40" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839389320, + "script": "76a914fce828578a2e305a000b7f59f06bf0c0241208a788ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "b5e8749958fca8441530c2c2403dafba13be7b90a303e96eb7ba65d6ac2ea207", + "version": 3, + "inputs": [ + { + "prevTxId": "b11a68555032cd0ae44e68208d4d7421b33052b50f7fb0ba994d92d92f0e3c83", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402203a36450035825232639b08159cbdd4881ad59fa9fd3567d9f34c38857187d66d022040de956c14f648ae117ee0eda7aed5d7f822e0acc4e514e7eb964400f64a16f1012103e68f91513a0a19306b9c763305784b402a7329700202e25781a4315e304bcd40", + "scriptString": "71 0x304402203a36450035825232639b08159cbdd4881ad59fa9fd3567d9f34c38857187d66d022040de956c14f648ae117ee0eda7aed5d7f822e0acc4e514e7eb964400f64a16f101 33 0x03e68f91513a0a19306b9c763305784b402a7329700202e25781a4315e304bcd40" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839389320, + "script": "76a914fce828578a2e305a000b7f59f06bf0c0241208a788ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "5667567858d01259c44da2a77c69ec5ecd6b8d3c671b8d57f558b3a89231d92a", + "version": 3, + "inputs": [ + { + "prevTxId": "b5e8749958fca8441530c2c2403dafba13be7b90a303e96eb7ba65d6ac2ea207", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "4830450221009f53f0bde663a9b80e672229d7afa46de1b82271636d46112a709a0b9c13bbe0022014e8b3baa9af26898881ee2ccd4082254a784acf3483aca41b479e81481fbb3e0121020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc", + "scriptString": "72 0x30450221009f53f0bde663a9b80e672229d7afa46de1b82271636d46112a709a0b9c13bbe0022014e8b3baa9af26898881ee2ccd4082254a784acf3483aca41b479e81481fbb3e01 33 0x020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839378640, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "b565408f9072947788003e28fcacff5d9d772982ab618c4e8807c7b6cbad9ca0", + "version": 3, + "inputs": [ + { + "prevTxId": "246f9d53cc1b6c3635593c3ede6a732a57d89b5e6fc71c982931290ef16cdc78", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100a3c255ef6ec16a030ae42764069928046005045b558cd1c75a6367a1e23c8f2602207d72665b56ea7d840e3758ef07aae5517c5bbe55bfde66e6bfbee7bb78d3058d012103f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e", + "scriptString": "72 0x3045022100a3c255ef6ec16a030ae42764069928046005045b558cd1c75a6367a1e23c8f2602207d72665b56ea7d840e3758ef07aae5517c5bbe55bfde66e6bfbee7bb78d3058d01 33 0x03f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839346600, + "script": "76a914fce828578a2e305a000b7f59f06bf0c0241208a788ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "5360b1090fb80e5509329e112888b3e7ad88ff6938623026184431f5ba6bc6ed", + "version": 3, + "inputs": [ + { + "prevTxId": "b565408f9072947788003e28fcacff5d9d772982ab618c4e8807c7b6cbad9ca0", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100a805986816524972e6ce5b7d09b94620ec5beb72204ef6f6b2c8991dc057c935022039d756065de07ef84c2aaef5f47d77a979916ef4bf2cc811d54e7ded692e66b80121020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc", + "scriptString": "72 0x3045022100a805986816524972e6ce5b7d09b94620ec5beb72204ef6f6b2c8991dc057c935022039d756065de07ef84c2aaef5f47d77a979916ef4bf2cc811d54e7ded692e66b801 33 0x020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839335920, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "5afb61bbbd7a017bc43d6b3b44bf6a0095d34af37cd9d17eb1d615c97513d8ff", + "version": 3, + "inputs": [ + { + "prevTxId": "3109915709e6456b328b6093f643ed26dea1d58ced530367acb02dd75a8eb3ed", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402200944a6c0ccbbe31316aa0c11b582cf4e6a81af8709391af16398c3497648574902201d71cd0a0e612b9c78742d02fbd0e1fadec70573ac6a87ffba09a1c7428abaf1012103293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000", + "scriptString": "71 0x304402200944a6c0ccbbe31316aa0c11b582cf4e6a81af8709391af16398c3497648574902201d71cd0a0e612b9c78742d02fbd0e1fadec70573ac6a87ffba09a1c7428abaf101 33 0x03293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839314560, + "script": "76a914fce828578a2e305a000b7f59f06bf0c0241208a788ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "ae508932dbc8a41c77eef719e4032a83378e01ad585b9ef1b966b11b0f113eaa", + "version": 3, + "inputs": [ + { + "prevTxId": "5afb61bbbd7a017bc43d6b3b44bf6a0095d34af37cd9d17eb1d615c97513d8ff", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100a250ba0ce837f6f5a76c6f45fde48959a8fc7facf8bb08e2e3a5790e1bbdcfe202206a41b2dcf55bc24b613637d4dd7aea3796df46e883b5d0832f806a387571b3ad0121020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc", + "scriptString": "72 0x3045022100a250ba0ce837f6f5a76c6f45fde48959a8fc7facf8bb08e2e3a5790e1bbdcfe202206a41b2dcf55bc24b613637d4dd7aea3796df46e883b5d0832f806a387571b3ad01 33 0x020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839303880, + "script": "76a914d13a4888259b83ebe6de955a44bedafda97aebad88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "45e5489bccf92d1ae31cb9e859b8169d0d7affd42086f0642bd2b8678c89df3f", + "version": 3, + "inputs": [ + { + "prevTxId": "42549885756c60918de991d99cca7b3b95cdbf8c0af4173444dc926d1b3a3825", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100bb86b03e8e8368c68d164a0409390291937cc60432406f75ad77c90236347f5102205d12e77b5de2bbb29b137b4a003a7ad14620f94fb46abed6ff6ca11e37f1b714012103f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e", + "scriptString": "72 0x3045022100bb86b03e8e8368c68d164a0409390291937cc60432406f75ad77c90236347f5102205d12e77b5de2bbb29b137b4a003a7ad14620f94fb46abed6ff6ca11e37f1b71401 33 0x03f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a142508b374f64149cdb3ee10aee631a89724e9cb3e" + }, + { + "satoshis": 13879365240, + "script": "76a914fce828578a2e305a000b7f59f06bf0c0241208a788ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "fce62466c7770b0d0895fd900361f3361e928a9d0d992f84724fc9fa9490f4bb", + "version": 3, + "inputs": [ + { + "prevTxId": "217742bd93f8629edc12ae86b1820126436febfc518bec4d1ac438e8c4f5d788", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100ad9794e095669912028b3c7498e28ed10b6677e9aeeb597bf946221f9d7caa26022049d76f4601c519942ea653915f0ea2484bd4309243ef581290de70cea040d81b012103f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e", + "scriptString": "72 0x3045022100ad9794e095669912028b3c7498e28ed10b6677e9aeeb597bf946221f9d7caa26022049d76f4601c519942ea653915f0ea2484bd4309243ef581290de70cea040d81b01 33 0x03f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a142508b374f64149cdb3ee10aee631a89724e9cb3e" + }, + { + "satoshis": 8839282520, + "script": "76a914fce828578a2e305a000b7f59f06bf0c0241208a788ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "6f20648bd585ee01fe63e59f2a7ade38c1d4193ca314ee278955e465afca9c8e", + "version": 3, + "inputs": [ + { + "prevTxId": "45e5489bccf92d1ae31cb9e859b8169d0d7affd42086f0642bd2b8678c89df3f", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402202293552e5fc91bdef13fa2c5d61319f5704d0588b62c82546645b3190371e1db02206566ea4be21c0e4e74ee008a4518d9878a3858634112b0b77ce281e361a0b7140121020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc", + "scriptString": "71 0x304402202293552e5fc91bdef13fa2c5d61319f5704d0588b62c82546645b3190371e1db02206566ea4be21c0e4e74ee008a4518d9878a3858634112b0b77ce281e361a0b71401 33 0x020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a142508b374f64149cdb3ee10aee631a89724e9cb3e" + }, + { + "satoshis": 13879354560, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "b5c887b11fe99f1184c6346cb598e86e431108f623e45c0b0b7c3be91b01ee3c", + "version": 3, + "inputs": [ + { + "prevTxId": "fce62466c7770b0d0895fd900361f3361e928a9d0d992f84724fc9fa9490f4bb", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402201538a1cb7597c501b25e1696070073ab80df1397bcb02ad3319743dade91daf502205558fa2c90765b233427aa7b5390fa51c0798bc7faec0595e0eeed09ea68812f0121020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc", + "scriptString": "71 0x304402201538a1cb7597c501b25e1696070073ab80df1397bcb02ad3319743dade91daf502205558fa2c90765b233427aa7b5390fa51c0798bc7faec0595e0eeed09ea68812f01 33 0x020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a144346274857710673785ac728ed63ceaf49ed1f37" + }, + { + "satoshis": 8839271840, + "script": "76a9146069423367341195f6ccc390d0662b29500f0bbb88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "7af610476d85021c0d5d44832adac299b501c27810224d61741408c104792836", + "version": 3, + "inputs": [ + { + "prevTxId": "3e00dd0160bb3da12ee4c044d6430d5ec3864934975e405a01bb676c819f3ee9", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100fd10071c2cd285278c4f95d7455ee111aae3cbe850ffcf9a5a3b56300cf18569022075823c99431f9975791e8b5e0a01738cdbef06856658b04d81c7d76c4f482846012103f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e", + "scriptString": "72 0x3045022100fd10071c2cd285278c4f95d7455ee111aae3cbe850ffcf9a5a3b56300cf18569022075823c99431f9975791e8b5e0a01738cdbef06856658b04d81c7d76c4f48284601 33 0x03f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a1495cdc2cb2391dff7a39617d79401d779f5f87cca" + }, + { + "satoshis": 13879322520, + "script": "76a914fce828578a2e305a000b7f59f06bf0c0241208a788ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "962355e97889aaf32d541f4f1de2c71297b92799f302a91605d0ea4d363a3fc8", + "version": 3, + "inputs": [ + { + "prevTxId": "7af610476d85021c0d5d44832adac299b501c27810224d61741408c104792836", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402207691db1039e2a94e2cb1900a3dd9bfd7c143886207a027cee223c037331fb959022052cddce9c29b1200d3423ca641352cfc736366c9504236d9cfe7cf5927e1daa10121020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc", + "scriptString": "71 0x304402207691db1039e2a94e2cb1900a3dd9bfd7c143886207a027cee223c037331fb959022052cddce9c29b1200d3423ca641352cfc736366c9504236d9cfe7cf5927e1daa101 33 0x020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14cbdd46beddea16298e87d352bcad4e624d99b741" + }, + { + "satoshis": 13879311840, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "5667567858d01259c44da2a77c69ec5ecd6b8d3c671b8d57f558b3a89231d92a", + "version": 3, + "inputs": [ + { + "prevTxId": "b5e8749958fca8441530c2c2403dafba13be7b90a303e96eb7ba65d6ac2ea207", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "4830450221009f53f0bde663a9b80e672229d7afa46de1b82271636d46112a709a0b9c13bbe0022014e8b3baa9af26898881ee2ccd4082254a784acf3483aca41b479e81481fbb3e0121020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc", + "scriptString": "72 0x30450221009f53f0bde663a9b80e672229d7afa46de1b82271636d46112a709a0b9c13bbe0022014e8b3baa9af26898881ee2ccd4082254a784acf3483aca41b479e81481fbb3e01 33 0x020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839378640, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "0c83ceac2da9d638a0a8b8de99a1de8a4bae4dad9ba45ebdd47d8ebf139d4b82", + "version": 3, + "inputs": [ + { + "prevTxId": "5667567858d01259c44da2a77c69ec5ecd6b8d3c671b8d57f558b3a89231d92a", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100f7415c0d149df127173ace2aa06d11d07cf8ca3bf1e4f449e1b1d42eb0df5c65022050e743eef83b1515745f94db94e7b96f46b8f8f704bdce2b450da983d549a203012102e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7", + "scriptString": "72 0x3045022100f7415c0d149df127173ace2aa06d11d07cf8ca3bf1e4f449e1b1d42eb0df5c65022050e743eef83b1515745f94db94e7b96f46b8f8f704bdce2b450da983d549a20301 33 0x02e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839367960, + "script": "76a9146069423367341195f6ccc390d0662b29500f0bbb88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "5360b1090fb80e5509329e112888b3e7ad88ff6938623026184431f5ba6bc6ed", + "version": 3, + "inputs": [ + { + "prevTxId": "b565408f9072947788003e28fcacff5d9d772982ab618c4e8807c7b6cbad9ca0", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100a805986816524972e6ce5b7d09b94620ec5beb72204ef6f6b2c8991dc057c935022039d756065de07ef84c2aaef5f47d77a979916ef4bf2cc811d54e7ded692e66b80121020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc", + "scriptString": "72 0x3045022100a805986816524972e6ce5b7d09b94620ec5beb72204ef6f6b2c8991dc057c935022039d756065de07ef84c2aaef5f47d77a979916ef4bf2cc811d54e7ded692e66b801 33 0x020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839335920, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "3109915709e6456b328b6093f643ed26dea1d58ced530367acb02dd75a8eb3ed", + "version": 3, + "inputs": [ + { + "prevTxId": "5360b1090fb80e5509329e112888b3e7ad88ff6938623026184431f5ba6bc6ed", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "473044022079f0dbd3fd65193c2606434eb93b65509310f841fdda6420b3bf32a93430946a02206ab44af8f25964c1df0b3bd04ee0ea35edca992667c2d515f33a381db1333826012102e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7", + "scriptString": "71 0x3044022079f0dbd3fd65193c2606434eb93b65509310f841fdda6420b3bf32a93430946a02206ab44af8f25964c1df0b3bd04ee0ea35edca992667c2d515f33a381db133382601 33 0x02e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839325240, + "script": "76a9146069423367341195f6ccc390d0662b29500f0bbb88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "d16bed019e13edb0ff2fd67a7336829a0db98344eaa3ff1d3ccd4a1dd6a942de", + "version": 3, + "inputs": [ + { + "prevTxId": "bfec828ed8ed562f53921e9580e847670044e870dda0e67b8f8d0c8d77962f7f", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100898cfd76279fb767697323660cc4b4f8354f4ca79aa217d7c9b255388f85fe48022009eef899c1b99b24cf98ef682baae569af6b72ee88144fab27b17b94ff9bbef50121032c3f05819b3d3792411582889ed6ccf4020d3b88c4498bcd251581029744f4d6", + "scriptString": "72 0x3045022100898cfd76279fb767697323660cc4b4f8354f4ca79aa217d7c9b255388f85fe48022009eef899c1b99b24cf98ef682baae569af6b72ee88144fab27b17b94ff9bbef501 33 0x032c3f05819b3d3792411582889ed6ccf4020d3b88c4498bcd251581029744f4d6" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 13879429320, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "e242d404e8e25089071e46a40f310e1a816e1ce8555fd8a5f9c43a0a478e78a5", + "version": 3, + "inputs": [ + { + "prevTxId": "d16bed019e13edb0ff2fd67a7336829a0db98344eaa3ff1d3ccd4a1dd6a942de", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100f7dd04ec6b3a3572157aeb7a902e4fcf383bf724efb0b33668f7ad8ca39e498e0220645ab3617eab04f661e5393999f064db10e661a18eeb4c60448c76c12e715369012102e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7", + "scriptString": "72 0x3045022100f7dd04ec6b3a3572157aeb7a902e4fcf383bf724efb0b33668f7ad8ca39e498e0220645ab3617eab04f661e5393999f064db10e661a18eeb4c60448c76c12e71536901 33 0x02e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 13879418640, + "script": "76a91404c662a51e5ad7d2391d72819d44b49324fee07688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "2db901d6d0913661aec5d36d6bcf07df2f850942de2520930ce6ed34f7558cf9", + "version": 3, + "inputs": [ + { + "prevTxId": "d050e4ddde27360847cb6c17f7120fbd9eba988e3d71f5c0975d770c297f889a", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "4830450221009dd5913275829e411ff5fc5bd58e237318c31235818950f1705169de9da00a20022050833183c0f1074698fc85a344bf0e46e27df30c0adc2ace311ad665e76728560121026df9906f5052b29690a82ad0d5f24b2b58093edfdf5a7aff0d8bc7da4fe13a10", + "scriptString": "72 0x30450221009dd5913275829e411ff5fc5bd58e237318c31235818950f1705169de9da00a20022050833183c0f1074698fc85a344bf0e46e27df30c0adc2ace311ad665e767285601 33 0x026df9906f5052b29690a82ad0d5f24b2b58093edfdf5a7aff0d8bc7da4fe13a10" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a1400e5a94f745e8e028a5625e2a5dc48e8eae2ae60" + }, + { + "satoshis": 13879397280, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "3896c2f17f3c91d5f474316a2210d46084b05fdf94d5a02baa5c0d9c2e67e666", + "version": 3, + "inputs": [ + { + "prevTxId": "2db901d6d0913661aec5d36d6bcf07df2f850942de2520930ce6ed34f7558cf9", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100ef76da91a36006f9399c7daabf68a26d11e311716c3e365d191502a129d0bbe402206a2d4a3f92548e36541c7548497457cffbee4ec25046d8237067ab7e2a50600e012102e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7", + "scriptString": "72 0x3045022100ef76da91a36006f9399c7daabf68a26d11e311716c3e365d191502a129d0bbe402206a2d4a3f92548e36541c7548497457cffbee4ec25046d8237067ab7e2a50600e01 33 0x02e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a1400e5a94f745e8e028a5625e2a5dc48e8eae2ae60" + }, + { + "satoshis": 13879386600, + "script": "76a9146069423367341195f6ccc390d0662b29500f0bbb88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "6f20648bd585ee01fe63e59f2a7ade38c1d4193ca314ee278955e465afca9c8e", + "version": 3, + "inputs": [ + { + "prevTxId": "45e5489bccf92d1ae31cb9e859b8169d0d7affd42086f0642bd2b8678c89df3f", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402202293552e5fc91bdef13fa2c5d61319f5704d0588b62c82546645b3190371e1db02206566ea4be21c0e4e74ee008a4518d9878a3858634112b0b77ce281e361a0b7140121020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc", + "scriptString": "71 0x304402202293552e5fc91bdef13fa2c5d61319f5704d0588b62c82546645b3190371e1db02206566ea4be21c0e4e74ee008a4518d9878a3858634112b0b77ce281e361a0b71401 33 0x020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a142508b374f64149cdb3ee10aee631a89724e9cb3e" + }, + { + "satoshis": 13879354560, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "0c27cc9fc4c177c0a216dbc8bde141cb45108ebf88748f58a912ea37bb04eda5", + "version": 3, + "inputs": [ + { + "prevTxId": "6f20648bd585ee01fe63e59f2a7ade38c1d4193ca314ee278955e465afca9c8e", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "4730440220548574a9fe8320fa32e61d4a471115b47dcd2e8864c69708ef1395753237a0a502204e8ef83ef17b39426626bf12758a4439996916094b1052f09c065cf859eaa5b9012102e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7", + "scriptString": "71 0x30440220548574a9fe8320fa32e61d4a471115b47dcd2e8864c69708ef1395753237a0a502204e8ef83ef17b39426626bf12758a4439996916094b1052f09c065cf859eaa5b901 33 0x02e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a144346274857710673785ac728ed63ceaf49ed1f37" + }, + { + "satoshis": 13879343880, + "script": "76a9146069423367341195f6ccc390d0662b29500f0bbb88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "78860e25a2b316450ae78d37ae6f939c87fd62c2ebdcdc3af682844528c52080", + "version": 3, + "inputs": [ + { + "prevTxId": "ed5ba135b726ebd50905dd050b1dcaa85b5810f41141cb1346baabe294e95b96", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "473044022003f7e1d38b04bb2ed0c8808575391698909d2e5b7c26bba5ebeab1a6351ae78102200eafb81e9439c50aca4c1b6725b9fc137d9ee0fd11cf406a5b99ec586c3ae3b6012103f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e", + "scriptString": "71 0x3044022003f7e1d38b04bb2ed0c8808575391698909d2e5b7c26bba5ebeab1a6351ae78102200eafb81e9439c50aca4c1b6725b9fc137d9ee0fd11cf406a5b99ec586c3ae3b601 33 0x03f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14cbdd46beddea16298e87d352bcad4e624d99b741" + }, + { + "satoshis": 8839250480, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "962355e97889aaf32d541f4f1de2c71297b92799f302a91605d0ea4d363a3fc8", + "version": 3, + "inputs": [ + { + "prevTxId": "7af610476d85021c0d5d44832adac299b501c27810224d61741408c104792836", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402207691db1039e2a94e2cb1900a3dd9bfd7c143886207a027cee223c037331fb959022052cddce9c29b1200d3423ca641352cfc736366c9504236d9cfe7cf5927e1daa10121020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc", + "scriptString": "71 0x304402207691db1039e2a94e2cb1900a3dd9bfd7c143886207a027cee223c037331fb959022052cddce9c29b1200d3423ca641352cfc736366c9504236d9cfe7cf5927e1daa101 33 0x020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14cbdd46beddea16298e87d352bcad4e624d99b741" + }, + { + "satoshis": 13879311840, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "0c83ceac2da9d638a0a8b8de99a1de8a4bae4dad9ba45ebdd47d8ebf139d4b82", + "version": 3, + "inputs": [ + { + "prevTxId": "5667567858d01259c44da2a77c69ec5ecd6b8d3c671b8d57f558b3a89231d92a", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100f7415c0d149df127173ace2aa06d11d07cf8ca3bf1e4f449e1b1d42eb0df5c65022050e743eef83b1515745f94db94e7b96f46b8f8f704bdce2b450da983d549a203012102e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7", + "scriptString": "72 0x3045022100f7415c0d149df127173ace2aa06d11d07cf8ca3bf1e4f449e1b1d42eb0df5c65022050e743eef83b1515745f94db94e7b96f46b8f8f704bdce2b450da983d549a20301 33 0x02e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839367960, + "script": "76a9146069423367341195f6ccc390d0662b29500f0bbb88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "246f9d53cc1b6c3635593c3ede6a732a57d89b5e6fc71c982931290ef16cdc78", + "version": 3, + "inputs": [ + { + "prevTxId": "0c83ceac2da9d638a0a8b8de99a1de8a4bae4dad9ba45ebdd47d8ebf139d4b82", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100a734e5d4297adf9b9db517be04c912fc4282b8ba3b373569fa58d16696c5ab9a022054afc14044099ec86950a9c565f5fea8ad614517deded2896d737c23ca1cafc7012103293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000", + "scriptString": "72 0x3045022100a734e5d4297adf9b9db517be04c912fc4282b8ba3b373569fa58d16696c5ab9a022054afc14044099ec86950a9c565f5fea8ad614517deded2896d737c23ca1cafc701 33 0x03293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839357280, + "script": "76a91404c662a51e5ad7d2391d72819d44b49324fee07688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "3109915709e6456b328b6093f643ed26dea1d58ced530367acb02dd75a8eb3ed", + "version": 3, + "inputs": [ + { + "prevTxId": "5360b1090fb80e5509329e112888b3e7ad88ff6938623026184431f5ba6bc6ed", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "473044022079f0dbd3fd65193c2606434eb93b65509310f841fdda6420b3bf32a93430946a02206ab44af8f25964c1df0b3bd04ee0ea35edca992667c2d515f33a381db1333826012102e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7", + "scriptString": "71 0x3044022079f0dbd3fd65193c2606434eb93b65509310f841fdda6420b3bf32a93430946a02206ab44af8f25964c1df0b3bd04ee0ea35edca992667c2d515f33a381db133382601 33 0x02e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839325240, + "script": "76a9146069423367341195f6ccc390d0662b29500f0bbb88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "5afb61bbbd7a017bc43d6b3b44bf6a0095d34af37cd9d17eb1d615c97513d8ff", + "version": 3, + "inputs": [ + { + "prevTxId": "3109915709e6456b328b6093f643ed26dea1d58ced530367acb02dd75a8eb3ed", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402200944a6c0ccbbe31316aa0c11b582cf4e6a81af8709391af16398c3497648574902201d71cd0a0e612b9c78742d02fbd0e1fadec70573ac6a87ffba09a1c7428abaf1012103293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000", + "scriptString": "71 0x304402200944a6c0ccbbe31316aa0c11b582cf4e6a81af8709391af16398c3497648574902201d71cd0a0e612b9c78742d02fbd0e1fadec70573ac6a87ffba09a1c7428abaf101 33 0x03293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839314560, + "script": "76a914fce828578a2e305a000b7f59f06bf0c0241208a788ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "3896c2f17f3c91d5f474316a2210d46084b05fdf94d5a02baa5c0d9c2e67e666", + "version": 3, + "inputs": [ + { + "prevTxId": "2db901d6d0913661aec5d36d6bcf07df2f850942de2520930ce6ed34f7558cf9", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100ef76da91a36006f9399c7daabf68a26d11e311716c3e365d191502a129d0bbe402206a2d4a3f92548e36541c7548497457cffbee4ec25046d8237067ab7e2a50600e012102e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7", + "scriptString": "72 0x3045022100ef76da91a36006f9399c7daabf68a26d11e311716c3e365d191502a129d0bbe402206a2d4a3f92548e36541c7548497457cffbee4ec25046d8237067ab7e2a50600e01 33 0x02e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a1400e5a94f745e8e028a5625e2a5dc48e8eae2ae60" + }, + { + "satoshis": 13879386600, + "script": "76a9146069423367341195f6ccc390d0662b29500f0bbb88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "42549885756c60918de991d99cca7b3b95cdbf8c0af4173444dc926d1b3a3825", + "version": 3, + "inputs": [ + { + "prevTxId": "3896c2f17f3c91d5f474316a2210d46084b05fdf94d5a02baa5c0d9c2e67e666", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "473044022077063b2017d8ff37bea124b3ecb910078a5fb2127c46ae664beec4dcf8e2db06022020658d14c6719bc0ae2bfe9501bf295e6ed6d3106329e10ea8aae3347f3b0e5e012103293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000", + "scriptString": "71 0x3044022077063b2017d8ff37bea124b3ecb910078a5fb2127c46ae664beec4dcf8e2db06022020658d14c6719bc0ae2bfe9501bf295e6ed6d3106329e10ea8aae3347f3b0e5e01 33 0x03293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a142e895994e80b35376ab3f836d8127cebe3557d64" + }, + { + "satoshis": 13879375920, + "script": "76a91404c662a51e5ad7d2391d72819d44b49324fee07688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "b5c887b11fe99f1184c6346cb598e86e431108f623e45c0b0b7c3be91b01ee3c", + "version": 3, + "inputs": [ + { + "prevTxId": "fce62466c7770b0d0895fd900361f3361e928a9d0d992f84724fc9fa9490f4bb", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402201538a1cb7597c501b25e1696070073ab80df1397bcb02ad3319743dade91daf502205558fa2c90765b233427aa7b5390fa51c0798bc7faec0595e0eeed09ea68812f0121020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc", + "scriptString": "71 0x304402201538a1cb7597c501b25e1696070073ab80df1397bcb02ad3319743dade91daf502205558fa2c90765b233427aa7b5390fa51c0798bc7faec0595e0eeed09ea68812f01 33 0x020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a144346274857710673785ac728ed63ceaf49ed1f37" + }, + { + "satoshis": 8839271840, + "script": "76a9146069423367341195f6ccc390d0662b29500f0bbb88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "0c27cc9fc4c177c0a216dbc8bde141cb45108ebf88748f58a912ea37bb04eda5", + "version": 3, + "inputs": [ + { + "prevTxId": "6f20648bd585ee01fe63e59f2a7ade38c1d4193ca314ee278955e465afca9c8e", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "4730440220548574a9fe8320fa32e61d4a471115b47dcd2e8864c69708ef1395753237a0a502204e8ef83ef17b39426626bf12758a4439996916094b1052f09c065cf859eaa5b9012102e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7", + "scriptString": "71 0x30440220548574a9fe8320fa32e61d4a471115b47dcd2e8864c69708ef1395753237a0a502204e8ef83ef17b39426626bf12758a4439996916094b1052f09c065cf859eaa5b901 33 0x02e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a144346274857710673785ac728ed63ceaf49ed1f37" + }, + { + "satoshis": 13879343880, + "script": "76a9146069423367341195f6ccc390d0662b29500f0bbb88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "ed5ba135b726ebd50905dd050b1dcaa85b5810f41141cb1346baabe294e95b96", + "version": 3, + "inputs": [ + { + "prevTxId": "b5c887b11fe99f1184c6346cb598e86e431108f623e45c0b0b7c3be91b01ee3c", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402203ed000c40233799813dccad9b0c7b0e762ffd0f9d17cf584a62e0c6b6fdcf43b0220372fe7c46c0a7ddfd9e7f506db6f5dd6e54d12b47fb400c3eaa3e40ca9319822012103293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000", + "scriptString": "71 0x304402203ed000c40233799813dccad9b0c7b0e762ffd0f9d17cf584a62e0c6b6fdcf43b0220372fe7c46c0a7ddfd9e7f506db6f5dd6e54d12b47fb400c3eaa3e40ca931982201 33 0x03293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a1495cdc2cb2391dff7a39617d79401d779f5f87cca" + }, + { + "satoshis": 8839261160, + "script": "76a91404c662a51e5ad7d2391d72819d44b49324fee07688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "3e00dd0160bb3da12ee4c044d6430d5ec3864934975e405a01bb676c819f3ee9", + "version": 3, + "inputs": [ + { + "prevTxId": "0c27cc9fc4c177c0a216dbc8bde141cb45108ebf88748f58a912ea37bb04eda5", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100d791414f774903290d9f0e107e0ea91c1a2bf046ecc3946037cc8cb893cb01ca02207a04f425ea8c687c82391803a1361a571243fc3d0db8f6aaf7b2809410a55cca012103293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000", + "scriptString": "72 0x3045022100d791414f774903290d9f0e107e0ea91c1a2bf046ecc3946037cc8cb893cb01ca02207a04f425ea8c687c82391803a1361a571243fc3d0db8f6aaf7b2809410a55cca01 33 0x03293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a1495cdc2cb2391dff7a39617d79401d779f5f87cca" + }, + { + "satoshis": 13879333200, + "script": "76a91404c662a51e5ad7d2391d72819d44b49324fee07688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "246f9d53cc1b6c3635593c3ede6a732a57d89b5e6fc71c982931290ef16cdc78", + "version": 3, + "inputs": [ + { + "prevTxId": "0c83ceac2da9d638a0a8b8de99a1de8a4bae4dad9ba45ebdd47d8ebf139d4b82", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100a734e5d4297adf9b9db517be04c912fc4282b8ba3b373569fa58d16696c5ab9a022054afc14044099ec86950a9c565f5fea8ad614517deded2896d737c23ca1cafc7012103293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000", + "scriptString": "72 0x3045022100a734e5d4297adf9b9db517be04c912fc4282b8ba3b373569fa58d16696c5ab9a022054afc14044099ec86950a9c565f5fea8ad614517deded2896d737c23ca1cafc701 33 0x03293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839357280, + "script": "76a91404c662a51e5ad7d2391d72819d44b49324fee07688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "b565408f9072947788003e28fcacff5d9d772982ab618c4e8807c7b6cbad9ca0", + "version": 3, + "inputs": [ + { + "prevTxId": "246f9d53cc1b6c3635593c3ede6a732a57d89b5e6fc71c982931290ef16cdc78", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100a3c255ef6ec16a030ae42764069928046005045b558cd1c75a6367a1e23c8f2602207d72665b56ea7d840e3758ef07aae5517c5bbe55bfde66e6bfbee7bb78d3058d012103f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e", + "scriptString": "72 0x3045022100a3c255ef6ec16a030ae42764069928046005045b558cd1c75a6367a1e23c8f2602207d72665b56ea7d840e3758ef07aae5517c5bbe55bfde66e6bfbee7bb78d3058d01 33 0x03f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839346600, + "script": "76a914fce828578a2e305a000b7f59f06bf0c0241208a788ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "e242d404e8e25089071e46a40f310e1a816e1ce8555fd8a5f9c43a0a478e78a5", + "version": 3, + "inputs": [ + { + "prevTxId": "d16bed019e13edb0ff2fd67a7336829a0db98344eaa3ff1d3ccd4a1dd6a942de", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100f7dd04ec6b3a3572157aeb7a902e4fcf383bf724efb0b33668f7ad8ca39e498e0220645ab3617eab04f661e5393999f064db10e661a18eeb4c60448c76c12e715369012102e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7", + "scriptString": "72 0x3045022100f7dd04ec6b3a3572157aeb7a902e4fcf383bf724efb0b33668f7ad8ca39e498e0220645ab3617eab04f661e5393999f064db10e661a18eeb4c60448c76c12e71536901 33 0x02e06c0a310439ef49638f04cc6b9e0f054f604a9632de8befda09dd4954e59cb7" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 13879418640, + "script": "76a91404c662a51e5ad7d2391d72819d44b49324fee07688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "d050e4ddde27360847cb6c17f7120fbd9eba988e3d71f5c0975d770c297f889a", + "version": 3, + "inputs": [ + { + "prevTxId": "e242d404e8e25089071e46a40f310e1a816e1ce8555fd8a5f9c43a0a478e78a5", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "473044022023b2de331758db39297a5ef25f50a7c06353dec17a58234858eec5e380be85750220662fd85921a69728c4ef4713523fd5a0affcb43c63479b2b8bca2d9964403dae012103f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e", + "scriptString": "71 0x3044022023b2de331758db39297a5ef25f50a7c06353dec17a58234858eec5e380be85750220662fd85921a69728c4ef4713523fd5a0affcb43c63479b2b8bca2d9964403dae01 33 0x03f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a1400e5a94f745e8e028a5625e2a5dc48e8eae2ae60" + }, + { + "satoshis": 13879407960, + "script": "76a914d13a4888259b83ebe6de955a44bedafda97aebad88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "42549885756c60918de991d99cca7b3b95cdbf8c0af4173444dc926d1b3a3825", + "version": 3, + "inputs": [ + { + "prevTxId": "3896c2f17f3c91d5f474316a2210d46084b05fdf94d5a02baa5c0d9c2e67e666", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "473044022077063b2017d8ff37bea124b3ecb910078a5fb2127c46ae664beec4dcf8e2db06022020658d14c6719bc0ae2bfe9501bf295e6ed6d3106329e10ea8aae3347f3b0e5e012103293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000", + "scriptString": "71 0x3044022077063b2017d8ff37bea124b3ecb910078a5fb2127c46ae664beec4dcf8e2db06022020658d14c6719bc0ae2bfe9501bf295e6ed6d3106329e10ea8aae3347f3b0e5e01 33 0x03293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a142e895994e80b35376ab3f836d8127cebe3557d64" + }, + { + "satoshis": 13879375920, + "script": "76a91404c662a51e5ad7d2391d72819d44b49324fee07688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "217742bd93f8629edc12ae86b1820126436febfc518bec4d1ac438e8c4f5d788", + "version": 3, + "inputs": [ + { + "prevTxId": "ae508932dbc8a41c77eef719e4032a83378e01ad585b9ef1b966b11b0f113eaa", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402202c9df64f60f3d07e78bc48318b63b8be483703379c508045e40ede2f5bf37afc02207d2b6cd1ee7e38dfa5b8bf58ad5b69e35cec61e09eb0c5ca379f7ecc9e5406f20121026df9906f5052b29690a82ad0d5f24b2b58093edfdf5a7aff0d8bc7da4fe13a10", + "scriptString": "71 0x304402202c9df64f60f3d07e78bc48318b63b8be483703379c508045e40ede2f5bf37afc02207d2b6cd1ee7e38dfa5b8bf58ad5b69e35cec61e09eb0c5ca379f7ecc9e5406f201 33 0x026df9906f5052b29690a82ad0d5f24b2b58093edfdf5a7aff0d8bc7da4fe13a10" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a142e895994e80b35376ab3f836d8127cebe3557d64" + }, + { + "satoshis": 8839293200, + "script": "76a91404c662a51e5ad7d2391d72819d44b49324fee07688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "45e5489bccf92d1ae31cb9e859b8169d0d7affd42086f0642bd2b8678c89df3f", + "version": 3, + "inputs": [ + { + "prevTxId": "42549885756c60918de991d99cca7b3b95cdbf8c0af4173444dc926d1b3a3825", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100bb86b03e8e8368c68d164a0409390291937cc60432406f75ad77c90236347f5102205d12e77b5de2bbb29b137b4a003a7ad14620f94fb46abed6ff6ca11e37f1b714012103f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e", + "scriptString": "72 0x3045022100bb86b03e8e8368c68d164a0409390291937cc60432406f75ad77c90236347f5102205d12e77b5de2bbb29b137b4a003a7ad14620f94fb46abed6ff6ca11e37f1b71401 33 0x03f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a142508b374f64149cdb3ee10aee631a89724e9cb3e" + }, + { + "satoshis": 13879365240, + "script": "76a914fce828578a2e305a000b7f59f06bf0c0241208a788ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "fce62466c7770b0d0895fd900361f3361e928a9d0d992f84724fc9fa9490f4bb", + "version": 3, + "inputs": [ + { + "prevTxId": "217742bd93f8629edc12ae86b1820126436febfc518bec4d1ac438e8c4f5d788", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100ad9794e095669912028b3c7498e28ed10b6677e9aeeb597bf946221f9d7caa26022049d76f4601c519942ea653915f0ea2484bd4309243ef581290de70cea040d81b012103f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e", + "scriptString": "72 0x3045022100ad9794e095669912028b3c7498e28ed10b6677e9aeeb597bf946221f9d7caa26022049d76f4601c519942ea653915f0ea2484bd4309243ef581290de70cea040d81b01 33 0x03f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a142508b374f64149cdb3ee10aee631a89724e9cb3e" + }, + { + "satoshis": 8839282520, + "script": "76a914fce828578a2e305a000b7f59f06bf0c0241208a788ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "ed5ba135b726ebd50905dd050b1dcaa85b5810f41141cb1346baabe294e95b96", + "version": 3, + "inputs": [ + { + "prevTxId": "b5c887b11fe99f1184c6346cb598e86e431108f623e45c0b0b7c3be91b01ee3c", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402203ed000c40233799813dccad9b0c7b0e762ffd0f9d17cf584a62e0c6b6fdcf43b0220372fe7c46c0a7ddfd9e7f506db6f5dd6e54d12b47fb400c3eaa3e40ca9319822012103293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000", + "scriptString": "71 0x304402203ed000c40233799813dccad9b0c7b0e762ffd0f9d17cf584a62e0c6b6fdcf43b0220372fe7c46c0a7ddfd9e7f506db6f5dd6e54d12b47fb400c3eaa3e40ca931982201 33 0x03293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a1495cdc2cb2391dff7a39617d79401d779f5f87cca" + }, + { + "satoshis": 8839261160, + "script": "76a91404c662a51e5ad7d2391d72819d44b49324fee07688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "3e00dd0160bb3da12ee4c044d6430d5ec3864934975e405a01bb676c819f3ee9", + "version": 3, + "inputs": [ + { + "prevTxId": "0c27cc9fc4c177c0a216dbc8bde141cb45108ebf88748f58a912ea37bb04eda5", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100d791414f774903290d9f0e107e0ea91c1a2bf046ecc3946037cc8cb893cb01ca02207a04f425ea8c687c82391803a1361a571243fc3d0db8f6aaf7b2809410a55cca012103293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000", + "scriptString": "72 0x3045022100d791414f774903290d9f0e107e0ea91c1a2bf046ecc3946037cc8cb893cb01ca02207a04f425ea8c687c82391803a1361a571243fc3d0db8f6aaf7b2809410a55cca01 33 0x03293caf1b6155eb1f520548517d0b0d13611d2008bc5df8a1d98fa2be45ec8000" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a1495cdc2cb2391dff7a39617d79401d779f5f87cca" + }, + { + "satoshis": 13879333200, + "script": "76a91404c662a51e5ad7d2391d72819d44b49324fee07688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "7af610476d85021c0d5d44832adac299b501c27810224d61741408c104792836", + "version": 3, + "inputs": [ + { + "prevTxId": "3e00dd0160bb3da12ee4c044d6430d5ec3864934975e405a01bb676c819f3ee9", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100fd10071c2cd285278c4f95d7455ee111aae3cbe850ffcf9a5a3b56300cf18569022075823c99431f9975791e8b5e0a01738cdbef06856658b04d81c7d76c4f482846012103f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e", + "scriptString": "72 0x3045022100fd10071c2cd285278c4f95d7455ee111aae3cbe850ffcf9a5a3b56300cf18569022075823c99431f9975791e8b5e0a01738cdbef06856658b04d81c7d76c4f48284601 33 0x03f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a1495cdc2cb2391dff7a39617d79401d779f5f87cca" + }, + { + "satoshis": 13879322520, + "script": "76a914fce828578a2e305a000b7f59f06bf0c0241208a788ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "78860e25a2b316450ae78d37ae6f939c87fd62c2ebdcdc3af682844528c52080", + "version": 3, + "inputs": [ + { + "prevTxId": "ed5ba135b726ebd50905dd050b1dcaa85b5810f41141cb1346baabe294e95b96", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "473044022003f7e1d38b04bb2ed0c8808575391698909d2e5b7c26bba5ebeab1a6351ae78102200eafb81e9439c50aca4c1b6725b9fc137d9ee0fd11cf406a5b99ec586c3ae3b6012103f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e", + "scriptString": "71 0x3044022003f7e1d38b04bb2ed0c8808575391698909d2e5b7c26bba5ebeab1a6351ae78102200eafb81e9439c50aca4c1b6725b9fc137d9ee0fd11cf406a5b99ec586c3ae3b601 33 0x03f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14cbdd46beddea16298e87d352bcad4e624d99b741" + }, + { + "satoshis": 8839250480, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "d050e4ddde27360847cb6c17f7120fbd9eba988e3d71f5c0975d770c297f889a", + "version": 3, + "inputs": [ + { + "prevTxId": "e242d404e8e25089071e46a40f310e1a816e1ce8555fd8a5f9c43a0a478e78a5", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "473044022023b2de331758db39297a5ef25f50a7c06353dec17a58234858eec5e380be85750220662fd85921a69728c4ef4713523fd5a0affcb43c63479b2b8bca2d9964403dae012103f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e", + "scriptString": "71 0x3044022023b2de331758db39297a5ef25f50a7c06353dec17a58234858eec5e380be85750220662fd85921a69728c4ef4713523fd5a0affcb43c63479b2b8bca2d9964403dae01 33 0x03f7110af12e94e21440b8050ef4b7e2bfbdccf99eb148402a2a06ce2d6973876e" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a1400e5a94f745e8e028a5625e2a5dc48e8eae2ae60" + }, + { + "satoshis": 13879407960, + "script": "76a914d13a4888259b83ebe6de955a44bedafda97aebad88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "ae508932dbc8a41c77eef719e4032a83378e01ad585b9ef1b966b11b0f113eaa", + "version": 3, + "inputs": [ + { + "prevTxId": "5afb61bbbd7a017bc43d6b3b44bf6a0095d34af37cd9d17eb1d615c97513d8ff", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "483045022100a250ba0ce837f6f5a76c6f45fde48959a8fc7facf8bb08e2e3a5790e1bbdcfe202206a41b2dcf55bc24b613637d4dd7aea3796df46e883b5d0832f806a387571b3ad0121020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc", + "scriptString": "72 0x3045022100a250ba0ce837f6f5a76c6f45fde48959a8fc7facf8bb08e2e3a5790e1bbdcfe202206a41b2dcf55bc24b613637d4dd7aea3796df46e883b5d0832f806a387571b3ad01 33 0x020499f3f506e9e1f86d5d6767fe3bbd3dbc44901be1391ae3381d902c9e12d9bc" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a14d792b7f83ad8e88758304f757a426cf414d7656c" + }, + { + "satoshis": 8839303880, + "script": "76a914d13a4888259b83ebe6de955a44bedafda97aebad88ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "2db901d6d0913661aec5d36d6bcf07df2f850942de2520930ce6ed34f7558cf9", + "version": 3, + "inputs": [ + { + "prevTxId": "d050e4ddde27360847cb6c17f7120fbd9eba988e3d71f5c0975d770c297f889a", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "4830450221009dd5913275829e411ff5fc5bd58e237318c31235818950f1705169de9da00a20022050833183c0f1074698fc85a344bf0e46e27df30c0adc2ace311ad665e76728560121026df9906f5052b29690a82ad0d5f24b2b58093edfdf5a7aff0d8bc7da4fe13a10", + "scriptString": "72 0x30450221009dd5913275829e411ff5fc5bd58e237318c31235818950f1705169de9da00a20022050833183c0f1074698fc85a344bf0e46e27df30c0adc2ace311ad665e767285601 33 0x026df9906f5052b29690a82ad0d5f24b2b58093edfdf5a7aff0d8bc7da4fe13a10" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a1400e5a94f745e8e028a5625e2a5dc48e8eae2ae60" + }, + { + "satoshis": 13879397280, + "script": "76a9143acabd25b1f1e87a32f104553405350b84703da688ac" + } + ], + "nLockTime": 0 + }, + { + "hash": "217742bd93f8629edc12ae86b1820126436febfc518bec4d1ac438e8c4f5d788", + "version": 3, + "inputs": [ + { + "prevTxId": "ae508932dbc8a41c77eef719e4032a83378e01ad585b9ef1b966b11b0f113eaa", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402202c9df64f60f3d07e78bc48318b63b8be483703379c508045e40ede2f5bf37afc02207d2b6cd1ee7e38dfa5b8bf58ad5b69e35cec61e09eb0c5ca379f7ecc9e5406f20121026df9906f5052b29690a82ad0d5f24b2b58093edfdf5a7aff0d8bc7da4fe13a10", + "scriptString": "71 0x304402202c9df64f60f3d07e78bc48318b63b8be483703379c508045e40ede2f5bf37afc02207d2b6cd1ee7e38dfa5b8bf58ad5b69e35cec61e09eb0c5ca379f7ecc9e5406f201 33 0x026df9906f5052b29690a82ad0d5f24b2b58093edfdf5a7aff0d8bc7da4fe13a10" + } + ], + "outputs": [ + { + "satoshis": 10000, + "script": "6a142e895994e80b35376ab3f836d8127cebe3557d64" + }, + { + "satoshis": 8839293200, + "script": "76a91404c662a51e5ad7d2391d72819d44b49324fee07688ac" + } + ], + "nLockTime": 0 + } +] diff --git a/packages/wallet-lib/fixtures/plugins/WorkingWorker.js b/packages/wallet-lib/fixtures/plugins/WorkingWorker.js new file mode 100644 index 00000000000..d9c4a435fb8 --- /dev/null +++ b/packages/wallet-lib/fixtures/plugins/WorkingWorker.js @@ -0,0 +1,24 @@ +const Worker = require('../../src/plugins/Worker'); + +class WorkingWorker extends Worker { + constructor() { + super({ + name: 'WorkingWorker', + firstExecutionRequired: true, + executeOnStart: true, + dependencies: [ + 'storage', 'walletId', + ], + }); + } + + execute() { + const { storage } = this; + if (storage.workingWorkerPass === undefined) { + storage.workingWorkerPass = 0; + } + + storage.workingWorkerPass += 1; + } +} +module.exports = WorkingWorker; diff --git a/packages/wallet-lib/fixtures/rawtx.json b/packages/wallet-lib/fixtures/rawtx.json new file mode 100644 index 00000000000..23741f300ea --- /dev/null +++ b/packages/wallet-lib/fixtures/rawtx.json @@ -0,0 +1,15 @@ +{ + "valid": { + "coinbaseMainnet": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1703f22b0f12d70a7088b1ec96060209000000002f4e614effffffff02b38ffa09000000001976a9142832b5c571b5686c4a08dae4091d856c4f9b190a88acaa8ffa09000000001976a914193e1e93826c9e3edebedadff6f45b8805e0eee188ac00000000", + "coinbaseTestnet": "03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff4b0214330424c0245c08fabe6d6d6d6dbefa085c24b707043304024bffffffff0000000000000000000000000000010000000000000010000006010000000d2f6e6f64655374726174756d2f00000000028096c531010000001976a914cb594917ad4e5849688ec63f29a0f7f3badb5da688ac8096c531010000001976a914a3c5284d3cd896815ac815f2dd76a3a71cb3d8e688ac00000000260100143300002b9eee2b2c4932966de2e85ecf3a3aedd440000c4b13b45511ced941e1a223c3", + "tx2to2Testnet": "0200000005e11ee373385c91a3cb2019df473dc06b06fd809474a9d551957da93c80bb795a000000006a47304402205fd2ae76264cafd127e51631de84278eb911518f60535d570fdc5f8310ac90f50220503609793486414c504bb1ebebcce2108c474dbd358c211f06983997c6616c840121034fec45bcb69c80eadb62d4baa023663647ff946ed814bfc88c7a7441291cf21afeffffff392731772cc7880f1707580af4ac508b4652e6474151f4246274f14286f52181000000006a473044022075b5994a49f75932ed45c582b884f0b0994005e5bf9570506008d755fd760a920220771c310f19705034e85ac879e78ed8f6475e59905255b145fc8aaafed4ccf20c0121034fec45bcb69c80eadb62d4baa023663647ff946ed814bfc88c7a7441291cf21afeffffff50b608f8c59cf0944ef7b39429b6807636cdbc92224d4135624838ce1f301bca000000006a47304402202e467d625c0b1bf22a86ce72efa4729a94b0a2ff4264b1ff115c3bb983a11d6e0220232642fd0925d605751b66bdebbc9a61408b076010804c445fafe57ea2a1c19d0121034fec45bcb69c80eadb62d4baa023663647ff946ed814bfc88c7a7441291cf21afeffffff41efb034dfca6db9e473cf4e5fc6dc8ed83611d16d5ba4d5b6a11520e1c121d1000000006b483045022100b6082019950a5c6ce1e72ea8e9ce7f165b59d439b60ec5a7ad3615a7cac666cb022048505573803bd1b09e1684cd8aa6412c125c793de5c7820acf8777abb96ae0460121034fec45bcb69c80eadb62d4baa023663647ff946ed814bfc88c7a7441291cf21afeffffff7fa9899c0f245f6957377fef83437f27961e5f70a23fb923a8ba5032e97300de000000006a4730440220300a34acded66b3939c253297ed589cc7c671fd2406cfe57934556800c9786f2022013cefe22fdded7aa92c8d8904fdf2035cbf8d926b9312f0e3cc28f4f9221821a0121020dc5e55a4c179d911bdda5207911143f7ecb730631ba08e09f39be9e515a01e0feffffff021e281800000000001976a91444d3b909711a8c5fe44db4b75ee5fc888a271d7288acf4292fb8030000001976a91408a0b66e7786428a50344c1f5d7f4905508e2bca88ac11330000", + "tx1to1Mainnet" : "01000000015caa47ab19db22ee5d684c80da3e748f465a06247626a4be38f93255744c7337010000006b483045022100941f723aa4738d41d83cddf7289e28f305fb3401c4e3b2dd4d2116bfc842c1f602204e065aeccb2431ac5e5a71974fe947d604c0940f33233bb62a786ce431334e6c0121030675e7a57d2781a28b88cd63861dc8d602a89b2cd8eab12a70e825b99e7942f7ffffffff01851c3200000000001976a9140098095df8cfbe069f967c1b35d02f16a2c85ba588ac00000000", + "tx2to2Mainnet" : "02000000021c9cf00f743c011cc314b16be78d4f11c47016b6bfe062381f1a6a276f17b7ba000000006a473044022049ad922076aba122d9bfe7b5220486223c0f54fdfeb111acd7033a4627677fba0220654e85e097d575dfd3bf5c1626231bd4264ad8b11b65704b92e94d6bac3a53960121020a599de2789dd3dc5a938d28ab3a95f3ed3dd229f5b7030efdabe8e2025dcb26feffffff2ac5235d44ee3638fc85011e790a2d4f35380e4925e14579c9ddf257a5173be5000000006b48304502210096036108f859c34033df28ec2b5de39484423c4648834d8fce1ffa519a30f75f02206675cff745928354836eb4fcb5038e86f8c42e8635bfff1aba64db62cc89545e012103f7e656e52bd77939f0fe23191bba974c929e390464c61f5cd7aa8400ffe63fa3feffffff02ca400f00000000001976a91427cb94511ff5ca6e5d9fcdf522edfc538d32178b88ac2f31ea02000000001976a91462ab79397f39ee48eec1c4775ff5cfc101a5d94688acf12b0f00", + "txMixingMainnet": "02000000117f7391e3d2639a6911b7eaa703218fa64d07666dcb8ffc3c56260d87efb49556010000006b483045022100ce41464ddc6002044ab72d82c6f7469bafc4dc4e6108fc6a3dd081313549cfae02205119d4ec728195042ab5a269b957464272d36fe77e9293c034e0a8f65f4bdce5812102c4aefca3eb4932931419a3dc67d901f31166c5c939fc63752dea7356d9ce3e48ffffffff7f7391e3d2639a6911b7eaa703218fa64d07666dcb8ffc3c56260d87efb49556040000006a4730440220397f86014d0b0c38c655fa207bda9f7899570b27b8ac4db9bbf80c537e0627610220314f49ac5aac4921c6b7723a3ddbf956e6948e304e022eebc8b1778963f7bd9681210267fa88f690fcb177be8677846fe0d52cea2752d8dca744dc47fc0b397bd20543ffffffff7f7391e3d2639a6911b7eaa703218fa64d07666dcb8ffc3c56260d87efb49556060000006a473044022032ea08cc1801847b28f57eae4152666921cb0c296bf0e24eb09a7df5fa134aa002201adab3702c4e3585003cea37656d09499d5fc32591063d78dcc1b55660045724812103ef12b70ddcaa28005abe2b4cc6b0386a6b5ebc2fc15ab818eecb65f93e2909daffffffff7f7391e3d2639a6911b7eaa703218fa64d07666dcb8ffc3c56260d87efb49556080000006a473044022020c9d8cdb3332cf7c15323503fb2847196919ffc3b43d950b728ab2981fc1811022050104e41adfbb806538fc12a7ddbd1a099249c3aaffd2c85e11035357496970a812103a1f20eac3b98b9378bf3c460111c5009f23ad05cae8d35e147db7eb13ab0fa5dffffffff7f7391e3d2639a6911b7eaa703218fa64d07666dcb8ffc3c56260d87efb495560a0000006b483045022100e42ed2bda08a814b09f1745fc89b259d3c172aee42fee06047de3271bf488a6c022009cd430e0fc5718085b5550084575363e528e1aab6623ea170b4e9140ee1db238121030e669a27e2082996251abfaa103edf9989f6356826b16dca10a53e710e77406affffffff7f7391e3d2639a6911b7eaa703218fa64d07666dcb8ffc3c56260d87efb495560b0000006a473044022064a2527dfdfa437e4cc4d88f2e7a8ddfdafa2fe5d79c01fd1ebf8be827d860f50220691630566e0e0260d7e3012cda7b779bf9d9a3d58c39be6ca8bed7c000efa7488121033e9516a6b3f6929a9a7714a4657424b10420264c75731dbd23b8f67988bc36d1ffffffff7cb2a64a590b7099adfff3263bf09b2e5d5b7cab440bda960305bce3048e4a660c0000006a47304402204f36543cdedbe5a28211001de57952c5b89e6060404fc5a95d241aa85dbbe6fe02205aeff1dac2aca2268658363928830458d4de9e69985e441a8d807e042332f101812103390f1f2a6c065f3ddeff6ab7277b0968aef03541dd11708b193251ba538fe8b4ffffffff7cb2a64a590b7099adfff3263bf09b2e5d5b7cab440bda960305bce3048e4a660e0000006a4730440220583a2ef86395ac5e9e3ba6df3ad959e2c7ba550b169a8fab8ed998497e4dd4fc02201ce658bf36bd3d73d460f1336843a038a671e70c5a89d4d435b125470875c3d281210254a66e17acb2041c75e099e62b9c5abff78515974d0504b8c010493e0cf89ef4ffffffff8f722471f79f4b336006a506a3e71b606f6f98b232223999585ce5cdcf5434990d0000006a4730440220461aa18c8ef6d8b35504618d9989cc4b9490e8e7b5c4399eeb86ccadf207df0502201b6609f1b4ecf23d636b1af4cfbd8e1a84f2348e8152b2bbd7dfe5b6af4dcd1c81210324c7e3362b7234a62cda8827eec6a90e61161e4b1a7663a7b274adc2711a0647ffffffff8f722471f79f4b336006a506a3e71b606f6f98b232223999585ce5cdcf543499110000006b483045022100ad6f7b71656398a93e43cdf954384b1eefa383df6fc2de0d6833956076eb5535022035903212c3fc94e4bbfcc21a3db2ecc72d9c88910557bad706713fae48509f5281210399d2a5ae1a3bfb5b56f879ebe770e39ecc702fccdb162333fe0814322cf012a5ffffffff8f722471f79f4b336006a506a3e71b606f6f98b232223999585ce5cdcf543499120000006b4830450221009ba959ba25c2dff60146f647958d8a7a1f7946fab83d8917c63ebff19086a1dc02206fd30bccd71e2e110d07e572458c490adfc499d51f4b08553eb50f0fb149826f8121028a72433dacc7707bdadfc553b80050b18301bd7b222661d5f585930f3c627a95ffffffff8f722471f79f4b336006a506a3e71b606f6f98b232223999585ce5cdcf543499130000006b483045022100d6fe27448103df7e23c2e58a1eb1fc96438557a8b532596c70e9c4ab3e3c96ab022065fd984033bd2db618759e07bb8df9e3402d606fb76568b878fc1d204a2cf2aa812102efcd929a04361dc8083df71df2d11e37b621f019054ae972458d2408de60d64dffffffff2f77643d4d83bbb06ad4dc5c56a23b38f91daaa293daffa8eac8d4ce21ec14af050000006b483045022100e7e7603d3e29420124db8aeab63b0dbd999fe2d89afd7d2277039bb2190d22d902200111a0c5512cb8ed512ab7c3457ecce50bc2fa69480288e7392425beefa450b1812102b27617161b1efab9412c2c74f45b59cb6f95ddfe4877838cc9f94f7b4255ca60ffffffff2f77643d4d83bbb06ad4dc5c56a23b38f91daaa293daffa8eac8d4ce21ec14af070000006b483045022100b5bf57b71f3987b4a3cec3c2f1563dfa0cdf846b1473b91b7fd2b6e1021078f00220036d99045a2ae08e631465c579c5044100308fbf88b93ff2d1697e3fa657fd818121026aaf5085140e8b8388e9eab50908c2cd9b52db83e1e8a05a307bcb8de5efe430ffffffff2f77643d4d83bbb06ad4dc5c56a23b38f91daaa293daffa8eac8d4ce21ec14af0a0000006a473044022075b79e3588a967942bc38f520b406a89a76633a82d34a48599d74f8b45e1b2820220370d7b9fd12e06ff14255cdb924e033224159c13e1a19c81e2ef9283e9caf0f58121033b8f901d4626d7643f562689723a082172a289bdd93c31ff902d08f495300a85ffffffff2f77643d4d83bbb06ad4dc5c56a23b38f91daaa293daffa8eac8d4ce21ec14af0e0000006b483045022100b3566875579961d131ee4b3c5bb9b5b3d6413cc42980e473165ad873f27c4c9102200d6bf828b642614ddb8af851cbc68bdb72d182d2ec9cdf42bbd5316c94ff5f85812103d334523deb00f6900e4bb897efc3bbb261cfc78a46f029da00c8e1a0dcb64866ffffffff2f77643d4d83bbb06ad4dc5c56a23b38f91daaa293daffa8eac8d4ce21ec14af0f0000006b483045022100def24d04a595c2d0e966c81cd2f054faa81105845becdd8f32c48e4d829bfa1802200e21417c6089d6fc5aba716b543d3184a63d79ff9ed64886576aac7d376c48a081210239eeec2aab9117e03db07efe26d3a2a67cf91296188005684b26944d1b11cfadffffffff11e4969800000000001976a9140032011793d21386a3dac58a2c766639bb3fd9bc88ace4969800000000001976a9142b71e4025498dd29e348b6531f521638d954b1c888ace4969800000000001976a9142d5c440296a1ad4a7ac1322018451520e9b376b788ace4969800000000001976a9142eed607611f089be1c53005058dcf891652ff19888ace4969800000000001976a91439c5ff3570e9a606369163c3e164b476058a53ee88ace4969800000000001976a91445dae80e75215c55fce3f422f6e7df69cccd484a88ace4969800000000001976a9144ecec34eebb352047947fae5930a0393042390db88ace4969800000000001976a914506384377aac1d3060916749ea323ff221a9032f88ace4969800000000001976a9146ed1c9a19bec9d1c5a00f3b669d7da3380aaa41c88ace4969800000000001976a9148b4af90cbc37880dd54ce730c746197a4881f57188ace4969800000000001976a914bb8b0309df93d391c53246984181d507edbfe69a88ace4969800000000001976a914c8afe5ebfb76ad55a13c828daead130adafb1a7888ace4969800000000001976a914c94ea3a0b3e247a5bb8cb66805ad08495db65b9c88ace4969800000000001976a914d70754e4d607e65d8d79aaeafcc328f6eb103f0588ace4969800000000001976a914de42391a9d89eaacfffafcf1d6f5e9a2d32f74ed88ace4969800000000001976a914e83fafdaa3ae10ef74474021b9a2ef82a0ca624188ace4969800000000001976a914fc573e1c5e14417d5a0005f5dbd12c4da82fe19d88ac00000000" + }, + "invalid": { + "true": true, + "notRelatedString": "SANDWICH, holding short 27L, request permission to cross. SANDWICH, cleared to cross 27L. Cleared to cross 27L, SANDWICH.", + "truncatedRawTx": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1703f22b0f12d70" + } +} \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/sirentonight-fullstore-snapshot-1562711703.json b/packages/wallet-lib/fixtures/sirentonight-fullstore-snapshot-1562711703.json new file mode 100644 index 00000000000..6f901fd10b3 --- /dev/null +++ b/packages/wallet-lib/fixtures/sirentonight-fullstore-snapshot-1562711703.json @@ -0,0 +1,910 @@ +{ + "wallets": { + "8ed0c6c8e5": { + "accounts": { + "m/44'/1'/0'": { + "label": null, + "path": "m/44'/1'/0'", + "network": { + "name": "testnet", + "alias": "regtest", + "pubkeyhash": 140, + "privatekey": 239, + "scripthash": 19, + "xpubkey": 70617039, + "xprivkey": 70615956, + "port": 19999, + "networkMagic": { + "type": "Buffer", + "data": [ + 206, + 226, + 202, + 255 + ] + }, + "dnsSeeds": [ + "testnet-seed.darkcoin.io", + "testnet-seed.dashdot.io", + "test.dnsseed.masternode.io" + ] + } + } + }, + "network": { + "name": "testnet", + "alias": "regtest", + "pubkeyhash": 140, + "privatekey": 239, + "scripthash": 19, + "xpubkey": 70617039, + "xprivkey": 70615956, + "port": 19999, + "networkMagic": { + "type": "Buffer", + "data": [ + 206, + 226, + 202, + 255 + ] + }, + "dnsSeeds": [ + "testnet-seed.darkcoin.io", + "testnet-seed.dashdot.io", + "test.dnsseed.masternode.io" + ] + }, + "mnemonic": null, + "type": null, + "blockheight": 0, + "addresses": { + "external": { + "m/44'/1'/0'/0/0": { + "address": "yYWGjtb7XJqbXsUPfkaTWQKzcPYfmMp1Co", + "path": "m/44'/1'/0'/0/0", + "index": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "5a5626c59f3830d5d9e7261bed5ced2694a343100c18d4a730b26639f5832944", + "92150f239013c961db15bc91d904404d2ae0520929969b59b69b17493569d0d5" + ], + "fetchedLast": 1562711668630, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/0/1": { + "address": "yMxLgpotfN1LSs6Yegu4kdnquSMsa2HBfc", + "path": "m/44'/1'/0'/0/1", + "index": 1, + "balanceSat": 100000000000, + "unconfirmedBalanceSat": 0, + "transactions": [ + "9d177e2e09c8b95ccec3363d4f530614e4643a06bc5b4efbc30a0eb3d69f64e1" + ], + "fetchedLast": 1562711668625, + "used": true, + "utxos": { + "9d177e2e09c8b95ccec3363d4f530614e4643a06bc5b4efbc30a0eb3d69f64e1-1": { + "hash": "9d177e2e09c8b95ccec3363d4f530614e4643a06bc5b4efbc30a0eb3d69f64e1", + "outputIndex": 1, + "satoshis": 100000000000, + "scriptPubKey": "76a91411f25f732bb72520124055a0402858165221dc4c88ac" + } + } + }, + "m/44'/1'/0'/0/2": { + "address": "yTAPrH7mE1VXjzD7z7VcPix3puUmn2m8Ed", + "path": "m/44'/1'/0'/0/2", + "index": 2, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711670754, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/3": { + "address": "yihk3HxPUzbAy9Gjc9zTUmxonhHW2CBDss", + "path": "m/44'/1'/0'/0/3", + "index": 3, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668654, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/4": { + "address": "ybkWmfmdXWGtpd32cVGTAKTidMyMkkUTHz", + "path": "m/44'/1'/0'/0/4", + "index": 4, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668635, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/5": { + "address": "ydhhWSMdYA2bZ1by5zRJdUSsRRaBduPoAH", + "path": "m/44'/1'/0'/0/5", + "index": 5, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668625, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/6": { + "address": "yQ4zMvTcMyZ1QtLCNk6tdvF5asGwQHK6fZ", + "path": "m/44'/1'/0'/0/6", + "index": 6, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668737, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/7": { + "address": "yXADjJoKh1DSzqEZfQk4ABHqEf8EYDH1RQ", + "path": "m/44'/1'/0'/0/7", + "index": 7, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668653, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/8": { + "address": "yVZ7wyw6wGqesZVikX3kPzVEBMy7masYr1", + "path": "m/44'/1'/0'/0/8", + "index": 8, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668691, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/9": { + "address": "yRN3vsC2yHst3jbaNBS8DcYJFwXgDb3QqN", + "path": "m/44'/1'/0'/0/9", + "index": 9, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668628, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/10": { + "address": "yY7Wqu8FMVjqN5aV2BMFAetkXzx7QJKLvm", + "path": "m/44'/1'/0'/0/10", + "index": 10, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668772, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/11": { + "address": "yWBgMq2bT8wpqT9pijqYUH7apHR2TkJd7e", + "path": "m/44'/1'/0'/0/11", + "index": 11, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668763, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/12": { + "address": "yQAbbTMRDS2yiPrxQtZWQki7YwgstwrCbE", + "path": "m/44'/1'/0'/0/12", + "index": 12, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668758, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/13": { + "address": "ySbqA9KNvnBaBBnYKJtRdYN1Zrem6h5t5d", + "path": "m/44'/1'/0'/0/13", + "index": 13, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668686, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/14": { + "address": "yZGwXaxvBFfiHP9wg5vzPc7ztNnKF97tze", + "path": "m/44'/1'/0'/0/14", + "index": 14, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668796, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/15": { + "address": "ygc63K48zFv25XJoQtKyenHKB6jRgL1D3n", + "path": "m/44'/1'/0'/0/15", + "index": 15, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668727, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/16": { + "address": "ydowNWbovrpF61RvsReZsW5WV45MiK9TPs", + "path": "m/44'/1'/0'/0/16", + "index": 16, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668723, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/17": { + "address": "ybV6yxWUrNmA2eCwhaMpwYahhKNwwv7pd7", + "path": "m/44'/1'/0'/0/17", + "index": 17, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668781, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/18": { + "address": "yiKhStdZ5yM4sPiSNxwD94hDxM5NzjorNT", + "path": "m/44'/1'/0'/0/18", + "index": 18, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668634, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/19": { + "address": "yRyYzs6hxM9U4hFHKm8Zup3428G4x2Rk4W", + "path": "m/44'/1'/0'/0/19", + "index": 19, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668631, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/20": { + "address": "yccDpwrib4HHy4t1GCybCtTFYMnEtzhHku", + "path": "m/44'/1'/0'/0/20", + "index": 20, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668671, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/21": { + "address": "yRzcUU3ZwxpxQNT2hvkNVisvcFDzba8FmW", + "path": "m/44'/1'/0'/0/21", + "index": 21, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711670757, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/0/22": { + "address": "yXxMFaHyiXo4MiqJjAr6wcPGUxdGjdP1uH", + "path": "m/44'/1'/0'/0/22", + "index": 22, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711670735, + "used": false, + "utxos": {} + } + }, + "internal": { + "m/44'/1'/0'/1/0": { + "address": "yUyAVoBdt7jUJybbRU3ihM6Masdv6Vx7RQ", + "path": "m/44'/1'/0'/1/0", + "index": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [ + "29f030226dfbd3777a32646957e4d51bc5b6014ba31156f1730b260f98b94d11", + "5a5626c59f3830d5d9e7261bed5ced2694a343100c18d4a730b26639f5832944" + ], + "fetchedLast": 1562711668784, + "used": true, + "utxos": {} + }, + "m/44'/1'/0'/1/1": { + "address": "yU3r5H3EdLp7cdwCWPyPHQytnL2KP6hcPg", + "path": "m/44'/1'/0'/1/1", + "index": 1, + "balanceSat": 84499999506, + "unconfirmedBalanceSat": 0, + "transactions": [ + "29f030226dfbd3777a32646957e4d51bc5b6014ba31156f1730b260f98b94d11" + ], + "fetchedLast": 1562711668731, + "used": true, + "utxos": { + "29f030226dfbd3777a32646957e4d51bc5b6014ba31156f1730b260f98b94d11-1": { + "hash": "29f030226dfbd3777a32646957e4d51bc5b6014ba31156f1730b260f98b94d11", + "outputIndex": 1, + "satoshis": 84499999506, + "scriptPubKey": "76a91454cdbd0d5703a04f123275bfea67b3ba772543ed88ac" + } + } + }, + "m/44'/1'/0'/1/2": { + "address": "yc2gSzXsAMAxpHSwnemWXgphrwM7iAv7eZ", + "path": "m/44'/1'/0'/1/2", + "index": 2, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711670751, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/3": { + "address": "yhgrnXezQgRuNWWExCjfP5T8acokd9bNWc", + "path": "m/44'/1'/0'/1/3", + "index": 3, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668706, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/4": { + "address": "yPm3h2LFVAFc1oSpoXALYqYz4BdWkeF9Cx", + "path": "m/44'/1'/0'/1/4", + "index": 4, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668717, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/5": { + "address": "ybGUeinRZrd4aK8E5A7YvkdioNrTRfki5c", + "path": "m/44'/1'/0'/1/5", + "index": 5, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668745, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/6": { + "address": "yQre4BVu4EXacPHWxVEUED5qqzaT5Zk9ws", + "path": "m/44'/1'/0'/1/6", + "index": 6, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668764, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/7": { + "address": "yjbJLmtmWC4EzNAawHkfjTQLEmNPd7KteK", + "path": "m/44'/1'/0'/1/7", + "index": 7, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668791, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/8": { + "address": "yPHZboYWBVLZEfS3YPc5zdDmoqfnQUDHVF", + "path": "m/44'/1'/0'/1/8", + "index": 8, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668683, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/9": { + "address": "ydSN97f1tHFP87oXHiMYPXqpMEnHjmhruD", + "path": "m/44'/1'/0'/1/9", + "index": 9, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668733, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/10": { + "address": "yPsDgf1TRPZ55RpMyUw2rN15jd3JdzXidW", + "path": "m/44'/1'/0'/1/10", + "index": 10, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668770, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/11": { + "address": "yNoHBczcytbxwkKQZuCDHFA65yUbsqqkpm", + "path": "m/44'/1'/0'/1/11", + "index": 11, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668655, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/12": { + "address": "yWBv4JAxkgaAY4hgaporJ2SUA2pdZXfkPX", + "path": "m/44'/1'/0'/1/12", + "index": 12, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668795, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/13": { + "address": "ybucPZaub5UqrK6Hc5HiQa4jeWZxUanqtP", + "path": "m/44'/1'/0'/1/13", + "index": 13, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668626, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/14": { + "address": "yibw1FQ3eCPsJSGJrSD5eVpoocz69ijD3g", + "path": "m/44'/1'/0'/1/14", + "index": 14, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668789, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/15": { + "address": "yZBiMmQzBCqtj71cheD7k6R92Yx9AFCbzP", + "path": "m/44'/1'/0'/1/15", + "index": 15, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668772, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/16": { + "address": "yNi7Vw4nDQaE4YHECM792eg25NsZocpSgg", + "path": "m/44'/1'/0'/1/16", + "index": 16, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668797, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/17": { + "address": "yNxykPdo9tVY5huvFQsLTNwYnXJsUbvwQP", + "path": "m/44'/1'/0'/1/17", + "index": 17, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668658, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/18": { + "address": "yb62nXyjqbcbHWE7CLzRjQ2qztF5Cf2sr2", + "path": "m/44'/1'/0'/1/18", + "index": 18, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668755, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/19": { + "address": "yaP9eEJvGB2gZwPzWvTahGkLoz1HYT449X", + "path": "m/44'/1'/0'/1/19", + "index": 19, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668660, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/20": { + "address": "yjLQ53apN8VCoYwJqFzLQMRoLBLJtjMxX9", + "path": "m/44'/1'/0'/1/20", + "index": 20, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711668753, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/21": { + "address": "yZHdbuiA1bzmuPKGyXJewRSVqfp8e5PrMQ", + "path": "m/44'/1'/0'/1/21", + "index": 21, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711670745, + "used": false, + "utxos": {} + }, + "m/44'/1'/0'/1/22": { + "address": "ya3MbuFyu1abVpRQfAuNEX4RDt2MiVPgwc", + "path": "m/44'/1'/0'/1/22", + "index": 22, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "transactions": [], + "fetchedLast": 1562711670738, + "used": false, + "utxos": {} + } + }, + "misc": {} + }, + "identityIds": [ + "9Gk9T5mJY9j3dDX1D1tG5WYaV8g6zQTS2ocFFXe6NCrq", + null, + "HZJywfYZ87fdJFLkp7wtnTfS29zpvR63f21gqaajLYx6" + ] + } + }, + "transactions": { + "5a5626c59f3830d5d9e7261bed5ced2694a343100c18d4a730b26639f5832944": { + "hash": "5a5626c59f3830d5d9e7261bed5ced2694a343100c18d4a730b26639f5832944", + "blockhash": "000000c5d6ca463ebbfddffe9a0a135312b6d8fc4eae2787b82b0fca9de7a554", + "blockheight": 29197, + "blocktime": 1562060795, + "fees": 247, + "size": 226, + "vout": [ + { + "value": "1.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914ea3e99211c6180caf77c4d1c86967b639d72b60788ac", + "asm": "OP_DUP OP_HASH160 ea3e99211c6180caf77c4d1c86967b639d72b607 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yhg1xVFVkzwFiZQfGNjfrqNwFvwiNNUgiE" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "998.99999753", + "n": 1, + "scriptPubKey": { + "hex": "76a9145ee329ba6366c411babfe296eefaaaf02c811c1b88ac", + "asm": "OP_DUP OP_HASH160 5ee329ba6366c411babfe296eefaaaf02c811c1b OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yUyAVoBdt7jUJybbRU3ihM6Masdv6Vx7RQ" + ], + "type": "pubkeyhash" + }, + "spentTxId": "29f030226dfbd3777a32646957e4d51bc5b6014ba31156f1730b260f98b94d11", + "spentIndex": 0, + "spentHeight": 33881 + } + ], + "vin": [ + { + "hash": "92150f239013c961db15bc91d904404d2ae0520929969b59b69b17493569d0d5", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100f21c68b35348692a174b560dbcb0c080806f8f3274bcc457c69a2837ada2d1bc02207f6e16537fe4fab20132e45d15e2e0a7951053c89d3734e7c7dce11b68108d5f012102fefb5d4c9510f420cc1cda4af685c5da1e1309e2661dc0ecb1a9afbc29a4fedf", + "asm": "3045022100f21c68b35348692a174b560dbcb0c080806f8f3274bcc457c69a2837ada2d1bc02207f6e16537fe4fab20132e45d15e2e0a7951053c89d3734e7c7dce11b68108d5f[ALL] 02fefb5d4c9510f420cc1cda4af685c5da1e1309e2661dc0ecb1a9afbc29a4fedf" + }, + "addr": "yYWGjtb7XJqbXsUPfkaTWQKzcPYfmMp1Co", + "valueSat": 100000000000, + "value": 1000, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "9d177e2e09c8b95ccec3363d4f530614e4643a06bc5b4efbc30a0eb3d69f64e1": { + "hash": "9d177e2e09c8b95ccec3363d4f530614e4643a06bc5b4efbc30a0eb3d69f64e1", + "blockhash": "00000043821167fc319288ea65980d0faed3b192da01c3d5ad662727ed7133e5", + "blockheight": 33880, + "blocktime": 1562711111, + "fees": 522, + "size": 520, + "vout": [ + { + "value": "0.99989946", + "n": 0, + "scriptPubKey": { + "hex": "76a914e3e16d1ebad0882f32691a085643eab6fd4f1ea688ac", + "asm": "OP_DUP OP_HASH160 e3e16d1ebad0882f32691a085643eab6fd4f1ea6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yh6NHe1tMeRdPPG7DYnKXtBgtBJovPBK6T" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "1000.00000000", + "n": 1, + "scriptPubKey": { + "hex": "76a91411f25f732bb72520124055a0402858165221dc4c88ac", + "asm": "OP_DUP OP_HASH160 11f25f732bb72520124055a0402858165221dc4c OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yMxLgpotfN1LSs6Yegu4kdnquSMsa2HBfc" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "hash": "5ee8cbf0e426659ce5e1b6268ed28666f9552adc57cf44808afaa099fbf9e752", + "vout": 0, + "sequence": 4294967294, + "n": 0, + "scriptSig": { + "hex": "47304402204157f0645c665f29bfcd4947e91ed5cf5289013e9c1e40866ab15019f3495c0f02205362b47c3936349d6f0824e8981171c77ed6bab7c8497bce0680f5cb458b27bd012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844", + "asm": "304402204157f0645c665f29bfcd4947e91ed5cf5289013e9c1e40866ab15019f3495c0f02205362b47c3936349d6f0824e8981171c77ed6bab7c8497bce0680f5cb458b27bd[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844" + }, + "addr": "yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5", + "valueSat": 50000000000, + "value": 500, + "doubleSpentTxID": null + }, + { + "hash": "eabe39ada39b58d70c03e0e79b7d2c767ed1239dda436bbc5a58954285421acc", + "vout": 0, + "sequence": 4294967294, + "n": 1, + "scriptSig": { + "hex": "483045022100f92ecf0a0b8cdb7481712e7a3fa7eae3401e048ca951913d0bc753f83ff65539022027da1d5e6f3537621125ec614fd4523d4a02360aa8a8253ae2f7b18c1fbdd6c9012102268159c2e670fe42e2f1ad060b143ab52bbe850fc6994ffe9263d82154685f7f", + "asm": "3045022100f92ecf0a0b8cdb7481712e7a3fa7eae3401e048ca951913d0bc753f83ff65539022027da1d5e6f3537621125ec614fd4523d4a02360aa8a8253ae2f7b18c1fbdd6c9[ALL] 02268159c2e670fe42e2f1ad060b143ab52bbe850fc6994ffe9263d82154685f7f" + }, + "addr": "yMPcLJD8BGaFnkT9onNtK6Fb48jetrvcjn", + "valueSat": 99990468, + "value": 0.99990468, + "doubleSpentTxID": null + }, + { + "hash": "f407dd031661d64384b2cfd45e6d3abf3e1f47bf286c78cc630ca5d35752024d", + "vout": 0, + "sequence": 4294967294, + "n": 2, + "scriptSig": { + "hex": "47304402207ef09faedbc783924d939fa550a8f552365b3968aa63ef4fa874b26077bc0a210220036f3dbc5b216174e2adcfb89328aee775a4e8ccb899a73f192906c0c9f61ce9012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844", + "asm": "304402207ef09faedbc783924d939fa550a8f552365b3968aa63ef4fa874b26077bc0a210220036f3dbc5b216174e2adcfb89328aee775a4e8ccb899a73f192906c0c9f61ce9[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844" + }, + "addr": "yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5", + "valueSat": 50000000000, + "value": 500, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "92150f239013c961db15bc91d904404d2ae0520929969b59b69b17493569d0d5": { + "hash": "92150f239013c961db15bc91d904404d2ae0520929969b59b69b17493569d0d5", + "blockhash": "000000c5d6ca463ebbfddffe9a0a135312b6d8fc4eae2787b82b0fca9de7a554", + "blockheight": 29197, + "blocktime": 1562060795, + "fees": 522, + "size": 521, + "vout": [ + { + "value": "0.99990990", + "n": 0, + "scriptPubKey": { + "hex": "76a914ba84943e63925288d2972cd5d0c2e1e06873c7c688ac", + "asm": "OP_DUP OP_HASH160 ba84943e63925288d2972cd5d0c2e1e06873c7c6 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "ydKfMe2n4vWsrzvgfSieQsFFxM9XMoWBff" + ], + "type": "pubkeyhash" + }, + "spentTxId": "eabe39ada39b58d70c03e0e79b7d2c767ed1239dda436bbc5a58954285421acc", + "spentIndex": 1, + "spentHeight": 30969 + }, + { + "value": "1000.00000000", + "n": 1, + "scriptPubKey": { + "hex": "76a91485ada58442067249829d52ddd6c99c97a112749188ac", + "asm": "OP_DUP OP_HASH160 85ada58442067249829d52ddd6c99c97a1127491 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yYWGjtb7XJqbXsUPfkaTWQKzcPYfmMp1Co" + ], + "type": "pubkeyhash" + }, + "spentTxId": "5a5626c59f3830d5d9e7261bed5ced2694a343100c18d4a730b26639f5832944", + "spentIndex": 0, + "spentHeight": 29197 + } + ], + "vin": [ + { + "hash": "0c25c534aeef8a151e8ce325882f80af647621b9f0a54f995f75c0d2994966ad", + "vout": 0, + "sequence": 4294967294, + "n": 0, + "scriptSig": { + "hex": "483045022100b24c95914f666ecb3ac41048110d7732b890b0d3fac9a9ff05560913e530430a022006660d72df91158f4d4710b75b9b05502a1332b5afca1ab5f98d012119f62553012103a6592040a30bf9254306a9d1086803cd450ae817ed5b4ba34e3e1b43d48bb783", + "asm": "3045022100b24c95914f666ecb3ac41048110d7732b890b0d3fac9a9ff05560913e530430a022006660d72df91158f4d4710b75b9b05502a1332b5afca1ab5f98d012119f62553[ALL] 03a6592040a30bf9254306a9d1086803cd450ae817ed5b4ba34e3e1b43d48bb783" + }, + "addr": "yXzZsVfpPxjewfVd7oa2D6tBMHW7JbonBr", + "valueSat": 99991512, + "value": 0.99991512, + "doubleSpentTxID": null + }, + { + "hash": "92056b727a3e37f5946dc18aa4f497ba9c0e3a328105e743175629bf7c8f3d37", + "vout": 0, + "sequence": 4294967294, + "n": 1, + "scriptSig": { + "hex": "483045022100fe69fdb70c0550b900960e9fbfd7254726a237c8b5688e5c9a7fba15947638fa02206ba0463b51922b56d0064c06b55c21328a39aa81312cf25ec982fa5c1eed9214012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844", + "asm": "3045022100fe69fdb70c0550b900960e9fbfd7254726a237c8b5688e5c9a7fba15947638fa02206ba0463b51922b56d0064c06b55c21328a39aa81312cf25ec982fa5c1eed9214[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844" + }, + "addr": "yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5", + "valueSat": 50000000000, + "value": 500, + "doubleSpentTxID": null + }, + { + "hash": "be27a3dae2742aaca103fea0967edd9a6d0ef5cf90159af39f80ad5a7a50b7d6", + "vout": 0, + "sequence": 4294967294, + "n": 2, + "scriptSig": { + "hex": "47304402205d30afd97e5efbec984faae5be922a487d7adce1518a3214966198a5423c150d02204ea0e15a5fcf5b8034294c9d2b9d6fc638f75506fcac3530c91b960eaf2e6859012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844", + "asm": "304402205d30afd97e5efbec984faae5be922a487d7adce1518a3214966198a5423c150d02204ea0e15a5fcf5b8034294c9d2b9d6fc638f75506fcac3530c91b960eaf2e6859[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844" + }, + "addr": "yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5", + "valueSat": 50000000000, + "value": 500, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + }, + "29f030226dfbd3777a32646957e4d51bc5b6014ba31156f1730b260f98b94d11": { + "hash": "29f030226dfbd3777a32646957e4d51bc5b6014ba31156f1730b260f98b94d11", + "blockhash": "000000371c94fbd16fd327cb00bba378c4902275a80871792a7d0753b87dd036", + "blockheight": 33881, + "blocktime": 1562711224, + "fees": 247, + "size": 226, + "vout": [ + { + "value": "154.00000000", + "n": 0, + "scriptPubKey": { + "hex": "76a914b879ad27d46cf02c1097bd3cf463a4ddd7252eb488ac", + "asm": "OP_DUP OP_HASH160 b879ad27d46cf02c1097bd3cf463a4ddd7252eb4 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yd8rwivMf5YviJRXaKqU5NL2JfJ417mMkN" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "844.99999506", + "n": 1, + "scriptPubKey": { + "hex": "76a91454cdbd0d5703a04f123275bfea67b3ba772543ed88ac", + "asm": "OP_DUP OP_HASH160 54cdbd0d5703a04f123275bfea67b3ba772543ed OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "yU3r5H3EdLp7cdwCWPyPHQytnL2KP6hcPg" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "vin": [ + { + "hash": "5a5626c59f3830d5d9e7261bed5ced2694a343100c18d4a730b26639f5832944", + "vout": 1, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100d3bbdea768d40482e2c1cc501d79a5b64ce6cbb4650fd4b034288adc777e54a00220063d60f289b057429f3afa360a867fe34e4c3b1fb284c9c3560f8ee4268cd3d0012102a0f24c955a0119b9ae22a5ca512330f77c3a9e5b722cbdbc071a73a5f23b7e84", + "asm": "3045022100d3bbdea768d40482e2c1cc501d79a5b64ce6cbb4650fd4b034288adc777e54a00220063d60f289b057429f3afa360a867fe34e4c3b1fb284c9c3560f8ee4268cd3d0[ALL] 02a0f24c955a0119b9ae22a5ca512330f77c3a9e5b722cbdbc071a73a5f23b7e84" + }, + "addr": "yUyAVoBdt7jUJybbRU3ihM6Masdv6Vx7RQ", + "valueSat": 99899999753, + "value": 998.99999753, + "doubleSpentTxID": null + } + ], + "txlock": false, + "spendable": false + } + }, + "transactionsMetadata": { + "92150f239013c961db15bc91d904404d2ae0520929969b59b69b17493569d0d5": { + "hash": "92150f239013c961db15bc91d904404d2ae0520929969b59b69b17493569d0d5", + "blockHash": "000000c5d6ca463ebbfddffe9a0a135312b6d8fc4eae2787b82b0fca9de7a554", + "height": 29197, + "instantLocked": false, + "chainLocked": false + } + }, + "chains": { + "testnet": { + "name": "testnet", + "blockheight": 33885 + } + } +} diff --git a/packages/wallet-lib/fixtures/strategies/craftedGenerousMinerStrategy.js b/packages/wallet-lib/fixtures/strategies/craftedGenerousMinerStrategy.js new file mode 100644 index 00000000000..e7823cdaa8a --- /dev/null +++ b/packages/wallet-lib/fixtures/strategies/craftedGenerousMinerStrategy.js @@ -0,0 +1,51 @@ +const TransactionEstimator = require('../../src/utils/coinSelections/TransactionEstimator.js'); +const { sortAndVerifyUTXOS } = require('../../src/utils/coinSelections/helpers'); + +module.exports = function craftedGenerousMinerStrategy(utxosList, outputsList, deductFee = false, feeCategory = 'normal') { + const txEstimator = new TransactionEstimator(feeCategory); + + // We add our outputs, theses will change only in case deductfee being true + txEstimator.addOutputs(outputsList); + + const sort = [{ sortBy: 'satoshis', direction: 'descending' }]; + const sortedUtxosList = sortAndVerifyUTXOS(utxosList, sort); + + const totalOutputValue = txEstimator.getTotalOutputValue(); + + let pendingSatoshis = 0; + const simplyAccumulatedUtxos = sortedUtxosList.filter((utxo) => { + if (pendingSatoshis < totalOutputValue) { + pendingSatoshis += utxo.satoshis; + return utxo; + } + return false; + }); + if (pendingSatoshis < totalOutputValue) { + throw new Error('Unsufficient utxo amount'); + } + + // We add the expected inputs, which should match the requested amount + // TODO : handle case when we do not match it. + txEstimator.addInputs(simplyAccumulatedUtxos); + + const estimatedFee = txEstimator.getFeeEstimate() + 10; + if (deductFee === true) { + // Then we check that we will be able to do it + const inValue = txEstimator.getInValue(); + const outValue = txEstimator.getOutValue(); + if (inValue < outValue + estimatedFee) { + // We don't have enough change for fee, so we remove from outValue + txEstimator.reduceFeeFromOutput((outValue + estimatedFee) - inValue); + } else { + // TODO : Here we can add some process to check up that we clearly have enough to deduct fee + } + } + + return { + utxos: txEstimator.getInputs(), + outputs: txEstimator.getOutputs(), + feeCategory, + estimatedFee, + utxosValue: txEstimator.getInValue(), + }; +}; diff --git a/packages/wallet-lib/fixtures/sunnysoccer.json b/packages/wallet-lib/fixtures/sunnysoccer.json new file mode 100644 index 00000000000..c43ad0babf7 --- /dev/null +++ b/packages/wallet-lib/fixtures/sunnysoccer.json @@ -0,0 +1,520 @@ +{ + "mnemonic": "sunny soccer know title act build split soccer leaf tomato symbol name", + "addresses": { + "external": { + "m/44'/1'/0'/0/19": { + "address": "yPWxUxcGmeT8ZMMSVS9kBVk9drz151ZPx4", + "balance": 0.1, + "balanceSat": 10000000, + "fetchedLast": 0, + "path": "m/44'/1'/0'/0/19", + "transactions": [ + "1a855e19b90ca52851a94c0e520ee6a3eaa91bdc2bb84cdda1969b5b5b76201a" + ], + "unconfirmedBalanceSat": 0, + "used": true, + "utxos": [ + { + "address": "yPWxUxcGmeT8ZMMSVS9kBVk9drz151ZPx4", + "txid": "1a855e19b90ca52851a94c0e520ee6a3eaa91bdc2bb84cdda1969b5b5b76201a", + "outputIndex": 1, + "script": "76a9142315b09ef82bf97cc06cd9b6ae2caf0a664c89f188ac", + "satoshis": 10000000 + } + ] + } + } + }, + "getAddresses": { + "m/44'/1'/0'/0/19": { + "address": "yPWxUxcGmeT8ZMMSVS9kBVk9drz151ZPx4", + "balance": 0.1, + "balanceSat": 10000000, + "fetchedLast": 0, + "path": "m/44'/1'/0'/0/19", + "transactions": [ + "1a855e19b90ca52851a94c0e520ee6a3eaa91bdc2bb84cdda1969b5b5b76201a" + ], + "unconfirmedBalanceSat": 0, + "used": true, + "utxos": [ + { + "address": "yPWxUxcGmeT8ZMMSVS9kBVk9drz151ZPx4", + "txid": "1a855e19b90ca52851a94c0e520ee6a3eaa91bdc2bb84cdda1969b5b5b76201a", + "outputIndex": 1, + "script": "76a9142315b09ef82bf97cc06cd9b6ae2caf0a664c89f188ac", + "satoshis": 10000000 + } + ], + "index": "19" + }, + "m/44'/1'/0'/0/20": { + "path": "m/44'/1'/0'/0/20", + "index": "20", + "address": "yNZmFzX98cQNr5qPjj5fuxVWtaCJyFKeca", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/21": { + "path": "m/44'/1'/0'/0/21", + "index": "21", + "address": "yRkm8KyDUbo6QnXtAp48gbP35xhvWXGJJo", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/22": { + "path": "m/44'/1'/0'/0/22", + "index": "22", + "address": "yNwHT8TJFfwtL38JXoRx6rWsCa6XimRjby", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/23": { + "path": "m/44'/1'/0'/0/23", + "index": "23", + "address": "yQZaNRgV46iCpE2j7u5B19SfGZded2wa27", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/24": { + "path": "m/44'/1'/0'/0/24", + "index": "24", + "address": "yTeMgrGYdotXrFrk6CoYMSLJnyTBR3TQHE", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/25": { + "path": "m/44'/1'/0'/0/25", + "index": "25", + "address": "yMpRq2RFvYkLW2n3NjuqhaYhRzGGGxcBqy", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/26": { + "path": "m/44'/1'/0'/0/26", + "index": "26", + "address": "yggLHYjVfMZB7UipTn3HPTiTDK51UDwdH7", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/27": { + "path": "m/44'/1'/0'/0/27", + "index": "27", + "address": "yY5FM7nHmNwWMALhJE1vvfrJZapVTrsVsW", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/28": { + "path": "m/44'/1'/0'/0/28", + "index": "28", + "address": "yV8WFjnuqBh1u9dPK7rnis5JxRfBkxhJfE", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/29": { + "path": "m/44'/1'/0'/0/29", + "index": "29", + "address": "yawyYNjn8J3gFi8zFUsuqggoRiL1rBzhuq", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/30": { + "path": "m/44'/1'/0'/0/30", + "index": "30", + "address": "yNo3vRCxPX2ntJ3hPMtPN9wfxkzbx12zzH", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/31": { + "path": "m/44'/1'/0'/0/31", + "index": "31", + "address": "ydgwWMN9UxkABZrCkCfK2RGd1NEsmwf4Ej", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/32": { + "path": "m/44'/1'/0'/0/32", + "index": "32", + "address": "yjcq4ddJbEGvrTQjhdqW7Wz2vaLK5XTv6E", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/33": { + "path": "m/44'/1'/0'/0/33", + "index": "33", + "address": "yTZuNXTsuXoN5cSFakkSkxdZpUMtMnF85N", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/34": { + "path": "m/44'/1'/0'/0/34", + "index": "34", + "address": "yPm4WAoovHyk2uw8ShKjUZVyyatiF3Xphy", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/35": { + "path": "m/44'/1'/0'/0/35", + "index": "35", + "address": "ySdn3FNoj4WnTGUwn4WvZ8mfysAMW8ZdY9", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/36": { + "path": "m/44'/1'/0'/0/36", + "index": "36", + "address": "yVAmUUSmVSQbjmmXGYp7PS82f1Q9jAfoaq", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/37": { + "path": "m/44'/1'/0'/0/37", + "index": "37", + "address": "yWeU751srakQdYd1gBgreVB5tgSauz4Y8c", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/38": { + "path": "m/44'/1'/0'/0/38", + "index": "38", + "address": "yMwWLNFGEqu3jHdAmn4w3nmhd6i9uFCSJp", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/39": { + "path": "m/44'/1'/0'/0/39", + "index": "39", + "address": "ygAr3LkmcztBnNjncChiFUWgYCHDidB7y3", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/0": { + "path": "m/44'/1'/0'/0/0", + "index": "0", + "address": "ydK3eQRtVVJbY1gKL6yerGWFS58AsV6iU6", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/1": { + "path": "m/44'/1'/0'/0/1", + "index": "1", + "address": "yYFQRyUZ38euLKNiQqExe7Kcb2sa8wexMW", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/2": { + "path": "m/44'/1'/0'/0/2", + "index": "2", + "address": "ySvompZzpcQg5WRf1FcEHModSzfeQ2RxEh", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/3": { + "path": "m/44'/1'/0'/0/3", + "index": "3", + "address": "yfF2UCNbEu9s3hWrccLkitS7ABXoJfdVYr", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/4": { + "path": "m/44'/1'/0'/0/4", + "index": "4", + "address": "yPP1Wy342TPDbFk2DUjAbwjzuPfXDmddQX", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/5": { + "path": "m/44'/1'/0'/0/5", + "index": "5", + "address": "yYvEaCd2i5ALarYgYDxhsWUm3S4KC7biKU", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/6": { + "path": "m/44'/1'/0'/0/6", + "index": "6", + "address": "yVQtaXRTYt6ejrfdFPmJnBwiJB3e5rdbFw", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/7": { + "path": "m/44'/1'/0'/0/7", + "index": "7", + "address": "yRAPBtxHwxPVF6d4p3yqtNCh9K7fXvtJLE", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/8": { + "path": "m/44'/1'/0'/0/8", + "index": "8", + "address": "yUD8NSgdEFfjS2xbWafcd1RqqMJQunwx3u", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/9": { + "path": "m/44'/1'/0'/0/9", + "index": "9", + "address": "yVXjahDkx3sJuKtqnesnjHcffFkjgwJxCa", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/10": { + "path": "m/44'/1'/0'/0/10", + "index": "10", + "address": "yaq7qmQTbLxFDoVZYA49DP63ZpQi19nLyM", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/11": { + "path": "m/44'/1'/0'/0/11", + "index": "11", + "address": "yiUfs5KPDgwSnzuwo4t2ceFBjqnzP6K8Vp", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/12": { + "path": "m/44'/1'/0'/0/12", + "index": "12", + "address": "yP2aUKe5g9AJbRYXW3BWJT8jDXn2F3NPjG", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/13": { + "path": "m/44'/1'/0'/0/13", + "index": "13", + "address": "yQD2RoxN4TA39EL5AA4N3HvVNhHFPD97KF", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/14": { + "path": "m/44'/1'/0'/0/14", + "index": "14", + "address": "yXjVLzHJPtBwvjpDKsQAUbhAj1obd1NsQy", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/15": { + "path": "m/44'/1'/0'/0/15", + "index": "15", + "address": "yYSdAasMm6ywtrPt1GErjPYw5PrMSLyFAx", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/16": { + "path": "m/44'/1'/0'/0/16", + "index": "16", + "address": "yRiq59KVdRyaBooYyb5acEyrHbESRkNapE", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/17": { + "path": "m/44'/1'/0'/0/17", + "index": "17", + "address": "yeBaz8ZXhGh4M6wRf5mXUVFmEc238WpQFv", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/18": { + "path": "m/44'/1'/0'/0/18", + "index": "18", + "address": "yRbpoqpxTdaSSuaJME3Fm2J1YSmLnRfkPU", + "transactions": [], + "balance": 0, + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": [], + "fetchedLast": 0, + "used": false + } + } +} \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/transactions.json b/packages/wallet-lib/fixtures/transactions.json new file mode 100644 index 00000000000..9f437274d71 --- /dev/null +++ b/packages/wallet-lib/fixtures/transactions.json @@ -0,0 +1,218 @@ +{ + "valid": { + "mainnet": { + "fd7c727155ef67fd5c1d54b73dea869e9690c439570063d6e96fec1d3bba450e": { + "hash": "fd7c727155ef67fd5c1d54b73dea869e9690c439570063d6e96fec1d3bba450e", + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "a83b0dec5df0b837d945af2e2c0350e256edd64cbfddb6ebc04d54b2b39d9df0", + "vout": 0, + "sequence": 4294967295, + "n": 0, + "scriptSig": { + "hex": "483045022100c21ec47c02a61a21e235c19f7577e544a5aee5bad604af37bcd7c42314f63c3e0220135c386fcd05ad82a40674b9fd77424c469d742cff339408996d47f8e4be381f01210357cb9fea5e9ac1b4bc129ee22a5b3676a2fe92ccc2dd58dd8d68a3141a758a1e", + "asm": "3045022100c21ec47c02a61a21e235c19f7577e544a5aee5bad604af37bcd7c42314f63c3e0220135c386fcd05ad82a40674b9fd77424c469d742cff339408996d47f8e4be381f[ALL] 0357cb9fea5e9ac1b4bc129ee22a5b3676a2fe92ccc2dd58dd8d68a3141a758a1e" + }, + "addr": "XkeCYYwpNPDXZXr7e8h5e4RzBEdU6A3zNp", + "valueSat": 2699731, + "value": 0.02699731, + "doubleSpentTxID": null + }, + { + "txid": "4955b81d8a4a11d5a18de0413ca8d712b79974909ebc18b93926798a1df996f9", + "vout": 1, + "sequence": 4294967295, + "n": 1, + "scriptSig": { + "hex": "48304502210092147222da0234c88a1f1456db71ee1e6afc2603877c6f7ca6e5417c9078017602206ae3ceeb1435647073b055a65d0d9e5ea516495c5184f868df8fb1bc09c9913f01210357cb9fea5e9ac1b4bc129ee22a5b3676a2fe92ccc2dd58dd8d68a3141a758a1e", + "asm": "304502210092147222da0234c88a1f1456db71ee1e6afc2603877c6f7ca6e5417c9078017602206ae3ceeb1435647073b055a65d0d9e5ea516495c5184f868df8fb1bc09c9913f[ALL] 0357cb9fea5e9ac1b4bc129ee22a5b3676a2fe92ccc2dd58dd8d68a3141a758a1e" + }, + "addr": "XkeCYYwpNPDXZXr7e8h5e4RzBEdU6A3zNp", + "valueSat": 1960090, + "value": 0.0196009, + "doubleSpentTxID": null + } + ], + "vout": [ + { + "value": "0.04654821", + "n": 0, + "scriptPubKey": { + "hex": "76a91493bdb39b8b388debe16dc13250b9edb80bcdd58488ac", + "asm": "OP_DUP OP_HASH160 93bdb39b8b388debe16dc13250b9edb80bcdd584 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "XpA2R9fdBv4X28sexLv5EGzZMJPZFcgYxs" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "blockhash": "000000000000001bd15a926388d9edf8c921015bbc55311c88431f5db3730389", + "blockheight": 992830, + "confirmations": 2, + "time": 1545682391, + "blocktime": 1545682391, + "valueOut": 0.04654821, + "size": 340, + "valueIn": 0.04659821, + "fees": 0.00005, + "txlock": false + }, + "faa430b0fe84a074d981e6fa3995a13363478415ca029a12f6432bf3d90dfa60": { + "hash": "faa430b0fe84a074d981e6fa3995a13363478415ca029a12f6432bf3d90dfa60", + "version": 2, + "locktime": 992824, + "vin": [ + { + "txid": "87e79448846b3f18419391e115da2a0e7221261a2e00da451632eab0082b7043", + "vout": 1, + "sequence": 4294967294, + "n": 0, + "scriptSig": { + "hex": "483045022100b0c7fe88d531f3c5ec1725a1b252dad66f7eb2596a32c7a7d85677848435684e0220108da5b290c0aa2b7c68c25edfc918495b3b9d7849a857dff7ba2e1c1aa048ce0121023a25941180e4b763f90960eec840126b3f911facd7d02d04a714a23923bac10b", + "asm": "3045022100b0c7fe88d531f3c5ec1725a1b252dad66f7eb2596a32c7a7d85677848435684e0220108da5b290c0aa2b7c68c25edfc918495b3b9d7849a857dff7ba2e1c1aa048ce[ALL] 023a25941180e4b763f90960eec840126b3f911facd7d02d04a714a23923bac10b" + }, + "addr": "XcFXrmk6ZEbgFNMVCdKXpkDgyeExPpJARY", + "valueSat": 1270465, + "value": 0.01270465, + "doubleSpentTxID": null + } + ], + "vout": [ + { + "value": "0.00065000", + "n": 0, + "scriptPubKey": { + "hex": "76a914dc0ff706aca57dee74eae4c2f45b59cb1d58a9bb88ac", + "asm": "OP_DUP OP_HASH160 dc0ff706aca57dee74eae4c2f45b59cb1d58a9bb OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "XvkRciiQByhRfp5mniqUucbwEP1hDHnzR4" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + }, + { + "value": "0.01205239", + "n": 1, + "scriptPubKey": { + "hex": "76a914643893eab14b1e48a95eb97e0274f451d274593288ac", + "asm": "OP_DUP OP_HASH160 643893eab14b1e48a95eb97e0274f451d2745932 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "XjpmBXkPMiRkzezSPgCy79DgFM7B62BCZH" + ], + "type": "pubkeyhash" + }, + "spentTxId": "a7791a78d873bef03337bed69aa669a765bc13f4cfe37537292b3fb21853b561", + "spentIndex": 0, + "spentHeight": 992827 + } + ], + "blockhash": "0000000000000015d2fda513a139bdcfbe05047bb5db23e59f89bc068a5b0037", + "blockheight": 992826, + "confirmations": 3, + "time": 1545681464, + "blocktime": 1545681464, + "valueOut": 0.01270239, + "size": 226, + "valueIn": 0.01270465, + "fees": 0.00000226, + "txlock": false + }, + "4f71db0c4bf3e2769a3ebd2162753b54b33028e3287e45f93c5c7df8bac5ec7e": { + "txid": "4f71db0c4bf3e2769a3ebd2162753b54b33028e3287e45f93c5c7df8bac5ec7e", + "version": 2, + "locktime": 996859, + "vin": [ + { + "txid": "61fe7ec7ede1eab63e57408beca19c8525cd254704350bfbf454324dcac8a171", + "vout": 1, + "sequence": 4294967294, + "n": 0, + "scriptSig": { + "hex": "47304402206429b5a08a4cf0829ca98af53d89a40575b9a7754697a5f607755cd57d5dd52f022000f6502b0b269dd1315ccd2a1ed87b0aefa4b4db2c4e81fa3d961b3e227c0ac401210371990f9a619b67c1118cc418a03dd30cd62f35c4bf942164eb25bc21ed86ec15", + "asm": "304402206429b5a08a4cf0829ca98af53d89a40575b9a7754697a5f607755cd57d5dd52f022000f6502b0b269dd1315ccd2a1ed87b0aefa4b4db2c4e81fa3d961b3e227c0ac4[ALL] 0371990f9a619b67c1118cc418a03dd30cd62f35c4bf942164eb25bc21ed86ec15" + }, + "addr": "XhrKBHCwCgxNZkmNpvbpCSG5UksPV1FkHr", + "valueSat": 167294128, + "value": 1.67294128, + "doubleSpentTxID": null + }, + { + "txid": "753ce44c31cbfb76e3db79614d0a41e75dba3c402b91eadb919282f39d2591f3", + "vout": 1, + "sequence": 4294967294, + "n": 1, + "scriptSig": { + "hex": "483045022100c570f6f748bf087d00886cef18ec553084e53d0cdbe00ac4f0ac94c6bdb213fa022043e33a610e048c9b7a804f9be739dce9cb5937e19705c8be0b01bb63af42826901210371990f9a619b67c1118cc418a03dd30cd62f35c4bf942164eb25bc21ed86ec15", + "asm": "3045022100c570f6f748bf087d00886cef18ec553084e53d0cdbe00ac4f0ac94c6bdb213fa022043e33a610e048c9b7a804f9be739dce9cb5937e19705c8be0b01bb63af428269[ALL] 0371990f9a619b67c1118cc418a03dd30cd62f35c4bf942164eb25bc21ed86ec15" + }, + "addr": "XhrKBHCwCgxNZkmNpvbpCSG5UksPV1FkHr", + "valueSat": 167393360, + "value": 1.6739336, + "doubleSpentTxID": null + }, + { + "txid": "d0c13654bf42f882fdf823fbcbe0f337cd6bdaa3471b33479d29ad0d65767f4e", + "vout": 1, + "sequence": 4294967294, + "n": 2, + "scriptSig": { + "hex": "47304402206b03454a1c25022f18402114a5c83d4c4d72dfd14f51f6ffdf90041bb1c3777502202d25c465fe046e706b5dd7d769b7e2da25ddd2979fcb6eb3263911bcb26ef1b701210371990f9a619b67c1118cc418a03dd30cd62f35c4bf942164eb25bc21ed86ec15", + "asm": "304402206b03454a1c25022f18402114a5c83d4c4d72dfd14f51f6ffdf90041bb1c3777502202d25c465fe046e706b5dd7d769b7e2da25ddd2979fcb6eb3263911bcb26ef1b7[ALL] 0371990f9a619b67c1118cc418a03dd30cd62f35c4bf942164eb25bc21ed86ec15" + }, + "addr": "XhrKBHCwCgxNZkmNpvbpCSG5UksPV1FkHr", + "valueSat": 167282907, + "value": 1.67282907, + "doubleSpentTxID": null + } + ], + "vout": [ + { + "value": "5.01969873", + "n": 0, + "scriptPubKey": { + "hex": "76a914ffdcc5eb3241cb0f2f37ed3e1f156d82431265a788ac", + "asm": "OP_DUP OP_HASH160 ffdcc5eb3241cb0f2f37ed3e1f156d82431265a7 OP_EQUALVERIFY OP_CHECKSIG", + "addresses": [ + "Xz1ickmprsPiZAMQPkJzJd2h6AZLHkX8At" + ], + "type": "pubkeyhash" + }, + "spentTxId": null, + "spentIndex": null, + "spentHeight": null + } + ], + "blockhash": "000000000000001f7616220f600e282b3d3f95dc7288e122353bb3744c7bc399", + "blockheight": 996860, + "confirmations": 1, + "time": 1546316931, + "blocktime": 1546316931, + "valueOut": 5.01969873, + "size": 486, + "valueIn": 5.01970395, + "fees": 0.00000522, + "txlock": false + } + }, + "testnet": { + "metadata": { + "1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6": { + "blockHash": "0000007a84abfe1d2b4201f4844bb1e59f24daf965c928281589269f281abc01", + "height": 551438, + "instantLocked": true, + "chainLocked": true + } + }, + "1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6": "020000000142bb40d403a55a70e1601f8f34ef42c7fa1171dfd9477648a2b02e414f4f9488000000006a47304402206c0a1f90457439095660682dccc3f39cdd0dfeb2b573e2454ccace219e6cb87502204ce55165e01f51a0fb92e98a248dba55957c9441953d0a546977ecfdc9e75ac601210200669c7e5dd728b676c2c1163ddcfa88e7cd4f01d12f01188b6b32c399c008ccfeffffff024011f307000000001976a9149b41890df761a9b6e1fb8588e3e1c13390be914488ac900f4e26000000001976a914b7240ea2d7287d73ad41c05a48af34d4ed071da188ac0d6a0800" + } + } +} diff --git a/packages/wallet-lib/fixtures/walletStore.json b/packages/wallet-lib/fixtures/walletStore.json new file mode 100644 index 00000000000..e6b7ecefb32 --- /dev/null +++ b/packages/wallet-lib/fixtures/walletStore.json @@ -0,0 +1,498 @@ +{ + "valid": { + "orange": { + "store": { + "transactions": {}, + "wallets": { + "a3771aaf93": { + "accounts": { + "m/44'/1'/0'": { + "label": null, + "path": "m/44'/1'/0'", + "network": "testnet" + } + }, + "network":"testnet", + "mnemonic": "orange endorse vintage grant brother regular miss hobby hand update recall orient", + "type": "hdwallet", + "blockheight": 0, + "addresses": { + "external": { + "m/44'/1'/0'/0/0": { + "path": "m/44'/1'/0'/0/0", + "index": "0", + "address": "yLhsYLXW5sFHLDPLj2EHgrmQRhP712ANda", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/1": { + "path": "m/44'/1'/0'/0/1", + "index": "1", + "address": "ybTNYvmCuXyrcvfei6aTvXJPXKEWEAyQXm", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/2": { + "path": "m/44'/1'/0'/0/2", + "index": "2", + "address": "yUAkcTybkBFPbcWuj2e7jqn5k2BACzBRSL", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/3": { + "path": "m/44'/1'/0'/0/3", + "index": "3", + "address": "yZvMPsXqxDHq19pAW1Egxzy2WGEq9TXpie", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/4": { + "path": "m/44'/1'/0'/0/4", + "index": "4", + "address": "yPNQNVHquuGGLkkYqQpLTQovJWQYoFfsa7", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/5": { + "path": "m/44'/1'/0'/0/5", + "index": "5", + "address": "yZjhhVkGnSWBNCm6nQzn4tir4kMwXXdsQm", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/6": { + "path": "m/44'/1'/0'/0/6", + "index": "6", + "address": "yhp9Es2Sm4JUiwQs9vttbB2J8r3s24hdX7", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/7": { + "path": "m/44'/1'/0'/0/7", + "index": "7", + "address": "yaUAoXDWu63j8s86jH9WsppQXGuUAvevT6", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/8": { + "path": "m/44'/1'/0'/0/8", + "index": "8", + "address": "yQSAfxqVoQroj7LZVTn2FzpR3i9SvUX4KB", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/9": { + "path": "m/44'/1'/0'/0/9", + "index": "9", + "address": "yTefpkMfqTZuiaRBoNZkV8NTMKqChA7Yw3", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/10": { + "path": "m/44'/1'/0'/0/10", + "index": "10", + "address": "yVpCNH3djsRgnHTefjtoXR4wztXp6nNVW9", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/11": { + "path": "m/44'/1'/0'/0/11", + "index": "11", + "address": "yV4hxD467yh5KwQ37u92Xsi9P2RNbzSDJr", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/12": { + "path": "m/44'/1'/0'/0/12", + "index": "12", + "address": "yXrFGc2Rza3eSQD4wUy4B1fVu9Cup9sLUh", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/13": { + "path": "m/44'/1'/0'/0/13", + "index": "13", + "address": "yiGrpJHm32jZcdBzSSJqVUsiopQTBom3Yc", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/14": { + "path": "m/44'/1'/0'/0/14", + "index": "14", + "address": "ySxsHNgN3yLQd3MS7wpvyFufvHNNDUmJsj", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/15": { + "path": "m/44'/1'/0'/0/15", + "index": "15", + "address": "yU9PHmKJPjUXwgpehLmG9PG61yLpjxH2UF", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/16": { + "path": "m/44'/1'/0'/0/16", + "index": "16", + "address": "yRZrPo2JxBQiiaknDguqh7tNTTSn66oyj9", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/17": { + "path": "m/44'/1'/0'/0/17", + "index": "17", + "address": "yeRpWMeFFxHwYJBBpLnmUnLUJshEG3fqrk", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/18": { + "path": "m/44'/1'/0'/0/18", + "index": "18", + "address": "ycJqQ5pR8d7fbCjbG74zmTr2JWgyFJNViv", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/19": { + "path": "m/44'/1'/0'/0/19", + "index": "19", + "address": "ySKWUC9za8m3qyiPDy9S3HcD8KsMaYfwjy", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/20": { + "path": "m/44'/1'/0'/0/20", + "index": "20", + "address": "ydtMaURu17927Eac7DAgfWd7hezuX2Z4nU", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + } + }, + "internal": { + "m/44'/1'/0'/1/0": { + "path": "m/44'/1'/0'/1/0", + "index": "0", + "address": "yc385SReqKR93wEZoUn9sc9n1UXvWVnfeR", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/1": { + "path": "m/44'/1'/0'/1/1", + "index": "1", + "address": "yfSorbpPYa7DhXsvKYP5EMbeThvETzveqo", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/2": { + "path": "m/44'/1'/0'/1/2", + "index": "2", + "address": "yZc911vgE1V9VsuxfmSfM9HitpfWHGdaPi", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/3": { + "path": "m/44'/1'/0'/1/3", + "index": "3", + "address": "yc6WW8cxnHQycLbyZ3DD3Qu7T8LtQ5w2i8", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/4": { + "path": "m/44'/1'/0'/1/4", + "index": "4", + "address": "yZoJ95da1fYBqzxoPyhXYkADxgvxMmJLki", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/5": { + "path": "m/44'/1'/0'/1/5", + "index": "5", + "address": "yZtgPSgzLxRQRupyQjN4fej1vq2moNgD1Q", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/6": { + "path": "m/44'/1'/0'/1/6", + "index": "6", + "address": "yYRZuPAPLM7FQKsXYTqaBBijknAnWt1ySy", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/7": { + "path": "m/44'/1'/0'/1/7", + "index": "7", + "address": "yfrBtBF5r2PnaKBKDjSU2DpAekHRCCHWZM", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/8": { + "path": "m/44'/1'/0'/1/8", + "index": "8", + "address": "yS4Y6pPe5DXtGfGxmregszPYNuhtxmsSUP", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/9": { + "path": "m/44'/1'/0'/1/9", + "index": "9", + "address": "yLceV5NSyM9h5hbLBtipUpigtsG1uigGeB", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/10": { + "path": "m/44'/1'/0'/1/10", + "index": "10", + "address": "yhYYDqMRGxajKWNKdFWoz6o1a4KoeiB4jS", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/11": { + "path": "m/44'/1'/0'/1/11", + "index": "11", + "address": "yWe9dyXPZx1SnAF4iCaYRSBDeVo3CiNY8k", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/12": { + "path": "m/44'/1'/0'/1/12", + "index": "12", + "address": "yLW4rYyF42XhSx5Tsob1cyktQEmYkbzPKB", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/13": { + "path": "m/44'/1'/0'/1/13", + "index": "13", + "address": "yesjrsSqjQ38efozW1H47DFpeKLqZVLDzB", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/14": { + "path": "m/44'/1'/0'/1/14", + "index": "14", + "address": "ySQ5oXLLSJwGHTivpBFzsnd5oA2b5uyKuF", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/15": { + "path": "m/44'/1'/0'/1/15", + "index": "15", + "address": "yRucp7qZ6jSjaesMqugN3ADet94CEmr7W2", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/16": { + "path": "m/44'/1'/0'/1/16", + "index": "16", + "address": "yen8UFU1WGDMwgJ8vuoWTdRHLx8wUbjqbP", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/17": { + "path": "m/44'/1'/0'/1/17", + "index": "17", + "address": "yh2J7UzTGrBYUrqDF3MqWd3oXJeYsSbwzx", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/18": { + "path": "m/44'/1'/0'/1/18", + "index": "18", + "address": "yhtqA8nQhST5twp64E2XgoLmeKcWFLkvVP", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/19": { + "path": "m/44'/1'/0'/1/19", + "index": "19", + "address": "yQkb2LK8q4wjcUpW8w3n2jRKkMZRHNdC8g", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/20": { + "path": "m/44'/1'/0'/1/20", + "index": "20", + "address": "yjTxB6RZdmbMpW5LdtPrQJWxSAuLwrKSUw", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + } + }, + "misc": {} + } + } + }, + "chains":{ + "testnet":{ + "blockheight": 10000 + } + } + } + } + } +} diff --git a/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/chain-store.json b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/chain-store.json new file mode 100644 index 00000000000..84fd8599a97 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/chain-store.json @@ -0,0 +1,36 @@ +{ + "blockHeaders": { + "0000012464fba1e3c66e678de79e4003bf17c36d5caa689e80fd4711fe620ec1": "00000020dc238f49a4671c66a42eb8312996f69801c4867b50658bf1588e4c402e00000088249fb4facf62de5b7706fb41d25cf7ae6404e387b25bce43d5ca69c621a5a05c2acc6108ca011e20420000", + "000000299efeefa87dc15474fd0423c136798975b779a2bb8aa5bb2f50509afb": "0000002016d86d54b0fb10d74d56687f14f1ff451d1e8e1fd9078afee6c3847eec000000a4c3c2e1b7b03b06ab32eb36b59bbe1ef9bba5b6ba6c18cab1ec64f38b49b7aad2402061bcfb011e7c3a0000", + "0000018b88fe43d07c3d63050aa82271698dc406dd08388529205dd837bf92dc": "00000020a78bb103f356ad73a2da83cfa9138054d170eb86259c6d01ebc7cfa15a01000051066136f040a39786459a111d453331ced2997354230d23d0c171df7c154bb460562061cee0011e07810000", + "0000007b7356e715b43ed7d5b7135fb9a2bf403e079bbcf7faec0f0da5c40117": "0000002011ec1243498c7b866199ea055fc7c899e2efe11585fd8c8099e343331801000025b944e894131cde8e07aaca3a750d7ec707f8ae05b74198391e972f5a8625cbcc59206123e4011eebcb0000" + }, + "transactions": { + "0dcdaa9bf5b3596be1bcf22113e39026fd49d24b47190e2c7423be936cb116a7": "0200000002e56082c04785e77de2dbbfded439dbfc0dde2201fd32e4a32aa425f5769c9e33010000006a4730440220737fb319650cd5c5ff17a1ef0667ec4a2c9d337c25463b8eaa5018ec4ff41cf202201a3f82c7790e0fb07ceddbee7c2ffa1e6de999f5b42b9d3eb64dab2697625a830121038f7857ad3a707c2cb4fa5d7dc270b6e04050dff58613c3a0198a463923c89512fefffffff9c814dd01da4b5b59e06cd1dc3ad8bbfb4af9ddb07d4a3ec4748e91b58d0478000000006a473044022054b284413497f3d8a5909916c298a6585a27e0f20039dbe393634869896293d7022079d252b13434e9bb2bb347f7cb45750500439b652ab28e85573acc5e8c33a60c0121032996947342cd793585d9adc2dc007b89135c764c0c9d528723ad7031fdf2c5aafeffffff029adb4900000000001976a9149eaf940724ad809abdc1f8e32ad7fbd221c742af88ac1059492a000000001976a914ae6994ac03281f1137b91a7e0f050dece040455388ac078c0800", + "d48f415f08fb795d43b216cf56e9ef10e059d4009cfc8fc90edfc0d3850813af": "0300000001a716b16c93be23742c0e19474bd249fd2690e31321f2bce16b59b3f59baacd0d010000006a47304402202c72746fafa9db3ef4ce3b16f2a05bccf2f72b5b2fb18208a10ac563b0e1edc602203371d81765942104bb05acacfe141f204223a1fa426052b55b780e804eb563780121021feafc962128b08d7ef46ce6e4507193d53d3d101d7ad7e6d889fea9b1d4faaaffffffff0210329000000000001976a914ae6994ac03281f1137b91a7e0f050dece040455388ac0926b929000000001976a914ae6994ac03281f1137b91a7e0f050dece040455388ac00000000", + "47d13f7f713f4258953292c2298c1d91e2d6dee309d689f3c8b44ccf457bab52": "0300000001af130885d3c0df0ec98ffc9c00d459e010efe956cf16b2435d79fb085f418fd4000000006b4830450221008d7a45a192df8e1b7d39e1465ddbed40f34793ecfc4c3bbc9c9116fd6d91e3cb0220304abf8405aee39ed7e66fae73f713db5868068990ea48b3865c1bba0ed569fe0121021feafc962128b08d7ef46ce6e4507193d53d3d101d7ad7e6d889fea9b1d4faaaffffffff02d0dd0600000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac49538900000000001976a914ae6994ac03281f1137b91a7e0f050dece040455388ac00000000" + }, + "txMetadata": { + "0dcdaa9bf5b3596be1bcf22113e39026fd49d24b47190e2c7423be936cb116a7": { + "blockHash": "000000299efeefa87dc15474fd0423c136798975b779a2bb8aa5bb2f50509afb", + "height": 560136, + "isInstantLocked": true, + "isChainLocked": true + }, + "d48f415f08fb795d43b216cf56e9ef10e059d4009cfc8fc90edfc0d3850813af": { + "blockHash": "0000018b88fe43d07c3d63050aa82271698dc406dd08388529205dd837bf92dc", + "height": 560169, + "isInstantLocked": true, + "isChainLocked": true + }, + "47d13f7f713f4258953292c2298c1d91e2d6dee309d689f3c8b44ccf457bab52": { + "blockHash": "0000007b7356e715b43ed7d5b7135fb9a2bf403e079bbcf7faec0f0da5c40117", + "height": 560179, + "isInstantLocked": true, + "isChainLocked": true + } + }, + "fees": { + "minRelay": -1 + } +} diff --git a/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/getFixtureAccountWithStorage.js b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/getFixtureAccountWithStorage.js new file mode 100644 index 00000000000..56ab9ae1c14 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/getFixtureAccountWithStorage.js @@ -0,0 +1,49 @@ +const walletStoreMock = require('./wallet-store.json'); +const chainStoreMock = require('./chain-store.json'); +const Storage = require('../../../src/types/Storage/Storage'); +const { KeyChainStore, DerivableKeyChain } = require('../../../src/index'); +const createPathsForTransactions = require("../../../src/types/Account/methods/createPathsForTransactions"); +const addPathsToStore = require("../../../src/types/Account/methods/addPathsToStore"); +const generateNewPaths = require("../../../src/types/Account/methods/generateNewPaths"); +const addDefaultPaths = require("../../../src/types/Account/methods/addDefaultPaths"); + +module.exports = (opts = {}) => { + const { walletId } = walletStoreMock; + + const mockedAccount = { + walletId, + index: 0, + storage: new Storage(), + accountPath: 'm/0', + network: 'testnet', + walletType: 'privateKey', + createPathsForTransactions, + addPathsToStore, + generateNewPaths, + addDefaultPaths, + ...opts, + }; + mockedAccount.storage.createWalletStore(walletId); + mockedAccount.storage.createChainStore('testnet'); + + const walletStore = mockedAccount.storage.getWalletStore(walletId); + walletStore.importState(walletStoreMock); + walletStore.createPathState(mockedAccount.accountPath); + + mockedAccount.storage.getChainStore('testnet').importState(chainStoreMock); + + mockedAccount.keyChainStore = new KeyChainStore(); + mockedAccount.keyChainStore.addKeyChain(new DerivableKeyChain({ + address: 'ycDeuTfs4U77bTb5cq17dame28zdWHVYfk', + lookAheadOpts: { + 'm/0': 1, + }, + }), { isMasterKeyChain: true }); + + mockedAccount.keyChainStore + .getMasterKeyChain() + .getForPath('0', { isWatched: true }); + mockedAccount.addDefaultPaths() + + return mockedAccount; +}; diff --git a/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/store.json b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/store.json new file mode 100644 index 00000000000..bef8bf408de --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/store.json @@ -0,0 +1,198 @@ +{ + "wallets": { + "6101b44d50": { + "accounts": { + "0": { + "label": null, + "network": "testnet", + "blockHeight": 560182, + "blockHash": "0000007b7356e715b43ed7d5b7135fb9a2bf403e079bbcf7faec0f0da5c40117" + } + }, + "network": "testnet", + "mnemonic": null, + "type": null, + "identityIds": [], + "addresses": { + "external": {}, + "internal": {}, + "misc": { + "0": { + "path": "0", + "index": 0, + "address": "ycDeuTfs4U77bTb5cq17dame28zdWHVYfk", + "transactions": [ + "0dcdaa9bf5b3596be1bcf22113e39026fd49d24b47190e2c7423be936cb116a7", + "d48f415f08fb795d43b216cf56e9ef10e059d4009cfc8fc90edfc0d3850813af", + "47d13f7f713f4258953292c2298c1d91e2d6dee309d689f3c8b44ccf457bab52" + ], + "balanceSat": 708999506, + "unconfirmedBalanceSat": 0, + "utxos": { + "d48f415f08fb795d43b216cf56e9ef10e059d4009cfc8fc90edfc0d3850813af-1": { + "satoshis": 699999753, + "script": "76a914ae6994ac03281f1137b91a7e0f050dece040455388ac" + }, + "47d13f7f713f4258953292c2298c1d91e2d6dee309d689f3c8b44ccf457bab52-1": { + "satoshis": 8999753, + "script": "76a914ae6994ac03281f1137b91a7e0f050dece040455388ac" + } + }, + "fetchedLast": 0, + "used": true + } + } + } + } + }, + "transactions": { + "0dcdaa9bf5b3596be1bcf22113e39026fd49d24b47190e2c7423be936cb116a7": { + "hash": "0dcdaa9bf5b3596be1bcf22113e39026fd49d24b47190e2c7423be936cb116a7", + "version": 2, + "inputs": [ + { + "prevTxId": "339e9c76f525a42aa3e432fd0122de0dfcdb39d4debfdbe27de78547c08260e5", + "outputIndex": 1, + "sequenceNumber": 4294967294, + "script": "4730440220737fb319650cd5c5ff17a1ef0667ec4a2c9d337c25463b8eaa5018ec4ff41cf202201a3f82c7790e0fb07ceddbee7c2ffa1e6de999f5b42b9d3eb64dab2697625a830121038f7857ad3a707c2cb4fa5d7dc270b6e04050dff58613c3a0198a463923c89512", + "scriptString": "71 0x30440220737fb319650cd5c5ff17a1ef0667ec4a2c9d337c25463b8eaa5018ec4ff41cf202201a3f82c7790e0fb07ceddbee7c2ffa1e6de999f5b42b9d3eb64dab2697625a8301 33 0x038f7857ad3a707c2cb4fa5d7dc270b6e04050dff58613c3a0198a463923c89512" + }, + { + "prevTxId": "78048db5918e74c43e4a7db0ddf94afbbbd83adcd16ce0595b4bda01dd14c8f9", + "outputIndex": 0, + "sequenceNumber": 4294967294, + "script": "473044022054b284413497f3d8a5909916c298a6585a27e0f20039dbe393634869896293d7022079d252b13434e9bb2bb347f7cb45750500439b652ab28e85573acc5e8c33a60c0121032996947342cd793585d9adc2dc007b89135c764c0c9d528723ad7031fdf2c5aa", + "scriptString": "71 0x3044022054b284413497f3d8a5909916c298a6585a27e0f20039dbe393634869896293d7022079d252b13434e9bb2bb347f7cb45750500439b652ab28e85573acc5e8c33a60c01 33 0x032996947342cd793585d9adc2dc007b89135c764c0c9d528723ad7031fdf2c5aa" + } + ], + "outputs": [ + { + "satoshis": 4840346, + "script": "76a9149eaf940724ad809abdc1f8e32ad7fbd221c742af88ac" + }, + { + "satoshis": 709450000, + "script": "76a914ae6994ac03281f1137b91a7e0f050dece040455388ac" + } + ], + "nLockTime": 560135 + }, + "d48f415f08fb795d43b216cf56e9ef10e059d4009cfc8fc90edfc0d3850813af": { + "hash": "d48f415f08fb795d43b216cf56e9ef10e059d4009cfc8fc90edfc0d3850813af", + "version": 3, + "inputs": [ + { + "prevTxId": "0dcdaa9bf5b3596be1bcf22113e39026fd49d24b47190e2c7423be936cb116a7", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402202c72746fafa9db3ef4ce3b16f2a05bccf2f72b5b2fb18208a10ac563b0e1edc602203371d81765942104bb05acacfe141f204223a1fa426052b55b780e804eb563780121021feafc962128b08d7ef46ce6e4507193d53d3d101d7ad7e6d889fea9b1d4faaa", + "scriptString": "71 0x304402202c72746fafa9db3ef4ce3b16f2a05bccf2f72b5b2fb18208a10ac563b0e1edc602203371d81765942104bb05acacfe141f204223a1fa426052b55b780e804eb5637801 33 0x021feafc962128b08d7ef46ce6e4507193d53d3d101d7ad7e6d889fea9b1d4faaa" + } + ], + "outputs": [ + { + "satoshis": 9450000, + "script": "76a914ae6994ac03281f1137b91a7e0f050dece040455388ac" + }, + { + "satoshis": 699999753, + "script": "76a914ae6994ac03281f1137b91a7e0f050dece040455388ac" + } + ], + "nLockTime": 0 + }, + "47d13f7f713f4258953292c2298c1d91e2d6dee309d689f3c8b44ccf457bab52": { + "hash": "47d13f7f713f4258953292c2298c1d91e2d6dee309d689f3c8b44ccf457bab52", + "version": 3, + "inputs": [ + { + "prevTxId": "d48f415f08fb795d43b216cf56e9ef10e059d4009cfc8fc90edfc0d3850813af", + "outputIndex": 0, + "sequenceNumber": 4294967295, + "script": "4830450221008d7a45a192df8e1b7d39e1465ddbed40f34793ecfc4c3bbc9c9116fd6d91e3cb0220304abf8405aee39ed7e66fae73f713db5868068990ea48b3865c1bba0ed569fe0121021feafc962128b08d7ef46ce6e4507193d53d3d101d7ad7e6d889fea9b1d4faaa", + "scriptString": "72 0x30450221008d7a45a192df8e1b7d39e1465ddbed40f34793ecfc4c3bbc9c9116fd6d91e3cb0220304abf8405aee39ed7e66fae73f713db5868068990ea48b3865c1bba0ed569fe01 33 0x021feafc962128b08d7ef46ce6e4507193d53d3d101d7ad7e6d889fea9b1d4faaa" + } + ], + "outputs": [ + { + "satoshis": 450000, + "script": "76a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac" + }, + { + "satoshis": 8999753, + "script": "76a914ae6994ac03281f1137b91a7e0f050dece040455388ac" + } + ], + "nLockTime": 0 + } + }, + "transactionsMetadata": { + "0dcdaa9bf5b3596be1bcf22113e39026fd49d24b47190e2c7423be936cb116a7": { + "blockHash": "000000299efeefa87dc15474fd0423c136798975b779a2bb8aa5bb2f50509afb", + "height": 560136, + "instantLocked": true, + "chainLocked": true + }, + "d48f415f08fb795d43b216cf56e9ef10e059d4009cfc8fc90edfc0d3850813af": { + "blockHash": "0000018b88fe43d07c3d63050aa82271698dc406dd08388529205dd837bf92dc", + "height": 560169, + "instantLocked": true, + "chainLocked": true + }, + "47d13f7f713f4258953292c2298c1d91e2d6dee309d689f3c8b44ccf457bab52": { + "blockHash": "0000007b7356e715b43ed7d5b7135fb9a2bf403e079bbcf7faec0f0da5c40117", + "height": 560179, + "instantLocked": true, + "chainLocked": true + } + }, + "chains": { + "testnet": { + "name": "testnet", + "blockHeaders": { + "000000757a2488ae789e1e63786341f54790c1f4113e489a3458706bc4787212": { + "hash": "000000757a2488ae789e1e63786341f54790c1f4113e489a3458706bc4787212", + "version": 536870912, + "prevHash": "000000720499313fa45303fbe80436497579ba7e11458fe006cd9fb668e46869", + "merkleRoot": "a71f5b32c1380a6951a2566494bbbd19e8285cb44afe6d3f89d43716c49da281", + "time": 1629510646, + "bits": 503452142, + "nonce": 46932 + }, + "000000299efeefa87dc15474fd0423c136798975b779a2bb8aa5bb2f50509afb": { + "hash": "000000299efeefa87dc15474fd0423c136798975b779a2bb8aa5bb2f50509afb", + "version": 536870912, + "prevHash": "000000ec7e84c3e6fe8a07d91f8e1e1d45fff1147f68564dd710fbb0546dd816", + "merkleRoot": "aab7498bf364ecb1ca186cbab6a5bbf91ebe9bb536eb32ab063bb0b7e1c2c3a4", + "time": 1629503698, + "bits": 503446460, + "nonce": 14972 + }, + "0000018b88fe43d07c3d63050aa82271698dc406dd08388529205dd837bf92dc": { + "hash": "0000018b88fe43d07c3d63050aa82271698dc406dd08388529205dd837bf92dc", + "version": 536870912, + "prevHash": "0000015aa1cfc7eb016d9c2586eb70d1548013a9cf83daa273ad56f303b18ba7", + "merkleRoot": "b44b157cdf71c1d0230d23547399d2ce3133451d119a458697a340f036610651", + "time": 1629509216, + "bits": 503439566, + "nonce": 33031 + }, + "0000007b7356e715b43ed7d5b7135fb9a2bf403e079bbcf7faec0f0da5c40117": { + "hash": "0000007b7356e715b43ed7d5b7135fb9a2bf403e079bbcf7faec0f0da5c40117", + "version": 536870912, + "prevHash": "000001183343e399808cfd8515e1efe299c8c75f05ea9961867b8c494312ec11", + "merkleRoot": "cb25865a2f971e399841b705aef807c77e0d753acaaa078ede1c1394e844b925", + "time": 1629510092, + "bits": 503440419, + "nonce": 52203 + } + }, + "mappedBlockHeaderHeights": { + "560182": "0000007b7356e715b43ed7d5b7135fb9a2bf403e079bbcf7faec0f0da5c40117" + }, + "blockHeight": 560182 + } + }, + "instantLocks": {}, + "syncOptions": { "skipSynchronizationBeforeHeight": 558030 } +} diff --git a/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/wallet-store.json b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/wallet-store.json new file mode 100644 index 00000000000..23eb12010a7 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/wallet-store.json @@ -0,0 +1,6 @@ +{ + "walletId": "6101b44d50", + "lastKnownBlock": { + "height": 11703 + } +} diff --git a/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/wallet.json b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/wallet.json new file mode 100644 index 00000000000..facd8b39702 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/wallet.json @@ -0,0 +1,16 @@ +{ + "privateKey": "2a331817b9d6bf85100ef05503d16f9f57c8855dbf13766b2f26c382b716d396", + "network": "testnet", + "store": { + "accounts": { + "0": { + "label": null, + "network": "testnet", + "blockHeight": 560178, + "blockHash": "000000299efeefa87dc15474fd0423c136798975b779a2bb8aa5bb2f50509afb" + } + }, + "identityIds": [], + "type": "single_address" + } +} diff --git a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/addresses.json b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/addresses.json new file mode 100644 index 00000000000..08d6955a855 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/addresses.json @@ -0,0 +1,1195 @@ +{ + "external": { + "m/44'/1'/0'/0/0": { + "path": "m/44'/1'/0'/0/0", + "index": 0, + "address": "yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7", + "transactions": [ + "a43845e580ad01f31bc06ce47ab39674e40316c4c6b765b6e54d6d35777ef456", + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8" + ], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/1": { + "path": "m/44'/1'/0'/0/1", + "index": 1, + "address": "yercyhdN9oEkZcB9BsW5ktFaDxFEuK6qXN", + "transactions": [ + "d37b6c7dd449d605bea9997af8bbeed2f3fbbcb23a4068b1f1ad694db801912d", + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8" + ], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/2": { + "path": "m/44'/1'/0'/0/2", + "index": 2, + "address": "ygk3GCSba2J3L9G665Snozhj9HSkh5ByVE", + "transactions": [ + "7d1b78157f9f2238669f260d95af03aeefc99577ff0cddb91b3e518ee557a2fd", + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8" + ], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/3": { + "path": "m/44'/1'/0'/0/3", + "index": 3, + "address": "ybuL6rM6dgrKzCg8s99f3jxGuv5oz5JcDA", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/4": { + "path": "m/44'/1'/0'/0/4", + "index": 4, + "address": "ygHAVkMtYSqoTWHebDv7qkhMV6dHyuRsp2", + "transactions": [ + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8", + "1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b" + ], + "balanceSat": 729210000, + "unconfirmedBalanceSat": 0, + "utxos": { + "1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b-1": { + "satoshis": 729210000, + "script": "76a914daf40881fda36848da6cc430dcbec6da3ea421b088ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/5": { + "path": "m/44'/1'/0'/0/5", + "index": 5, + "address": "yMLhEsiP2ajSh8STmXnNmkWXtoHsmawZxd", + "transactions": [ + "eb1a7fc8e3b43d3021653b1176f8f9b41e9667d05b65ee225d14c149a5b14f77", + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8" + ], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/6": { + "path": "m/44'/1'/0'/0/6", + "index": 6, + "address": "yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv", + "transactions": [ + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5", + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8", + "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533" + ], + "balanceSat": 1777100000, + "unconfirmedBalanceSat": 0, + "utxos": { + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8-0": { + "satoshis": 1777100000, + "script": "76a914fa49fe511c437a0d4ec01050184bd2d6538b3f0888ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/7": { + "path": "m/44'/1'/0'/0/7", + "index": 7, + "address": "yhaAB6e8m3F8zmGX7WAVYa6eEfmSrrnY8x", + "transactions": [ + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5" + ], + "balanceSat": 400000000, + "unconfirmedBalanceSat": 0, + "utxos": { + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5-1": { + "satoshis": 400000000, + "script": "76a914e922f6420544f1be0cb593c10535cc3469198bc888ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/8": { + "path": "m/44'/1'/0'/0/8", + "index": 8, + "address": "yiXh4Yo5djG6QH8WzXkKm5EFzqLRJWakXz", + "transactions": [ + "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841" + ], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/9": { + "path": "m/44'/1'/0'/0/9", + "index": 9, + "address": "yQYv3Um6DsdtANo1ZPTUte75wAGMstLRex", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/10": { + "path": "m/44'/1'/0'/0/10", + "index": 10, + "address": "yiYPJmu7eEm1cXUNumQRdjv1fvPhsfgMS4", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/11": { + "path": "m/44'/1'/0'/0/11", + "index": 11, + "address": "yii4aUZhNfL6EWN9KAgAFrJzGJmqHnF4wx", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/12": { + "path": "m/44'/1'/0'/0/12", + "index": 12, + "address": "yLpTquSct2SGz2Ka45uTPDd81Kzro2Jt2k", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/13": { + "path": "m/44'/1'/0'/0/13", + "index": 13, + "address": "yMiJtpzb1Qthy9TGnavsf5NZ6EZZa4j9q3", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/14": { + "path": "m/44'/1'/0'/0/14", + "index": 14, + "address": "yacgSfW7RkwWakEZPg8USAVdzCypiG3vxS", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/15": { + "path": "m/44'/1'/0'/0/15", + "index": 15, + "address": "yVvrmoRPFLy6nUpCQBT8ZExxF5wF3DhiGU", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/16": { + "path": "m/44'/1'/0'/0/16", + "index": 16, + "address": "yaJf2aG6cFUtfv4o6TuEKsh5kr4xq5iAY4", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/17": { + "path": "m/44'/1'/0'/0/17", + "index": 17, + "address": "yfardJQ4ucgWLKQPaRHGMRMbSGm5H4ExJR", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/18": { + "path": "m/44'/1'/0'/0/18", + "index": 18, + "address": "yLSCqx7dcM5JKR2fG7vHbF2axMvuYqomaw", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/19": { + "path": "m/44'/1'/0'/0/19", + "index": 19, + "address": "yVij8XpJ78LM5hepSV1KF7T8vRpUEXCpK5", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/20": { + "path": "m/44'/1'/0'/0/20", + "index": 20, + "address": "ydJpjuJGossAZR7S5oS7cWvjygEwoj8Xwp", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/21": { + "path": "m/44'/1'/0'/0/21", + "index": 21, + "address": "yW3TmWnmhvpxRbgFcQ8oXqDRkn3RhRH6jj", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/22": { + "path": "m/44'/1'/0'/0/22", + "index": 22, + "address": "yRegVX85DThKRkH8C61TtRacfzrkiBfNy5", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/23": { + "path": "m/44'/1'/0'/0/23", + "index": 23, + "address": "yPtDCqDFRe1JuDp8pvdiEMQMz2erGwS3VG", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/24": { + "path": "m/44'/1'/0'/0/24", + "index": 24, + "address": "yM9pSw3L4oBfG7uQL5o522Hu3WTvy9awgZ", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/25": { + "path": "m/44'/1'/0'/0/25", + "index": 25, + "address": "yNC6qYJYungzuk5XUynDFKCn54Dy8ngox4", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/26": { + "path": "m/44'/1'/0'/0/26", + "index": 26, + "address": "yR5KcLr1bceLT4teTk2qoJx6pFLik1zyzL", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/27": { + "path": "m/44'/1'/0'/0/27", + "index": 27, + "address": "yRrKLGJa9JmdjBWvrHtedKjHTao6CRDTKf", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/0": { + "path": "m/44'/1'/1'/0/0", + "index": 0, + "address": "yYJmzWey5kNecAThet5BFxAga1F4b4DKQ2", + "transactions": [ + "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533", + "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840" + ], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/1'/0/1": { + "path": "m/44'/1'/1'/0/1", + "index": 1, + "address": "yNCqctyQaq51WU1hN5aNwsgMsZ5fRiB7GY", + "transactions": [ + "6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135", + "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840" + ], + "balanceSat": 1200000000, + "unconfirmedBalanceSat": 0, + "utxos": { + "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840-0": { + "satoshis": 1200000000, + "script": "76a91414b05906daab037707927bc6c83900d5dbf2849688ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/1'/0/2": { + "path": "m/44'/1'/1'/0/2", + "index": 2, + "address": "yNPbYz5cZKw2EwxtkL3VSVzPi2FYp9VKjQ", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/3": { + "path": "m/44'/1'/1'/0/3", + "index": 3, + "address": "ybsGWzsnSCAZufgSeUjScVxqEdved99UM2", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/4": { + "path": "m/44'/1'/1'/0/4", + "index": 4, + "address": "yfNHuPojk8XKWP5nuueDptX4nM7qToudgx", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/5": { + "path": "m/44'/1'/1'/0/5", + "index": 5, + "address": "yXxLnDkk6s8h1PSnYaFM6MAyRarc1Kc1rY", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/6": { + "path": "m/44'/1'/1'/0/6", + "index": 6, + "address": "yipResSzN2zUvL7UYkmptKKmQTv7sNssRn", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/7": { + "path": "m/44'/1'/1'/0/7", + "index": 7, + "address": "yZPtNwimHdRiKYbNQW49qezw1Kc1YwUJeT", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/8": { + "path": "m/44'/1'/1'/0/8", + "index": 8, + "address": "yPMjYYfQbga2nBiuqqfUyX41U1vwRZ8fG8", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/9": { + "path": "m/44'/1'/1'/0/9", + "index": 9, + "address": "yLueLWWcLQsaXQ8D5o9tcyo8tfTxMWXvG4", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/10": { + "path": "m/44'/1'/1'/0/10", + "index": 10, + "address": "yN8gzgsc1RVjXThMQT5qZH2jjpnMymz6zP", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/11": { + "path": "m/44'/1'/1'/0/11", + "index": 11, + "address": "yPQLWBNwMdLxUW2oUwHGwQtfyYxD41BARJ", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/12": { + "path": "m/44'/1'/1'/0/12", + "index": 12, + "address": "yg5g2AfWFdwWexWGfbSXYbUHf1y5WWrFPs", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/13": { + "path": "m/44'/1'/1'/0/13", + "index": 13, + "address": "yWyABu4naV1Jzw7w9sn1gqhebPRSkCndsS", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/14": { + "path": "m/44'/1'/1'/0/14", + "index": 14, + "address": "ycuUPzUBjhKyUjezQR1LNot79a6C4aRLaR", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/15": { + "path": "m/44'/1'/1'/0/15", + "index": 15, + "address": "yQ7YjvAXgDAUCekveHVjr6NBveXrUemVno", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/16": { + "path": "m/44'/1'/1'/0/16", + "index": 16, + "address": "yi8bghcw627cMGpuH4bJqH6bqR5ywv1NLH", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/17": { + "path": "m/44'/1'/1'/0/17", + "index": 17, + "address": "yizHu8i2rfwzwBgnJ62s2WUe6wLoDjne6N", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/18": { + "path": "m/44'/1'/1'/0/18", + "index": 18, + "address": "yW1u3tySeUKAKJsz7sjZFyjUiTyKLB6xBv", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/19": { + "path": "m/44'/1'/1'/0/19", + "index": 19, + "address": "yNaSkdy1Q8JNubUdbLMGsGf7sTRofEJYZq", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/20": { + "path": "m/44'/1'/1'/0/20", + "index": 20, + "address": "yjG4jiCMNbx3MyFCAsaNaU29CBVmgPf8hS", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/21": { + "path": "m/44'/1'/1'/0/21", + "index": 21, + "address": "yWrEYTtydaZCyJoKE1vzX4e4RXDNPGdnw9", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + } + + }, + "internal": { + "m/44'/1'/0'/1/0": { + "path": "m/44'/1'/0'/1/0", + "index": 0, + "address": "yNDpPsJqXKM36zHSNEW7c1zSvNnrZ699FY", + "transactions": [ + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8" + ], + "balanceSat": 99170, + "unconfirmedBalanceSat": 0, + "utxos": { + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8-1": { + "satoshis": 99170, + "script": "76a91414dfbdcfb48babe7127fa0ee90339c33a46aeda288ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/1/1": { + "path": "m/44'/1'/0'/1/1", + "index": 1, + "address": "yLk4Hw3w4zDudrDVP6W8J9TggkY57zQUki", + "transactions": [ + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5" + ], + "balanceSat": 107099720, + "unconfirmedBalanceSat": 0, + "utxos": { + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5-2": { + "satoshis": 107099720, + "script": "76a91404a791e67467246c3c0a003007793160387de54288ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/1/2": { + "path": "m/44'/1'/0'/1/2", + "index": 2, + "address": "yirJaK8KCE5YAmwvLadizqFw3TCXqBuZXL", + "transactions": [ + "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533", + "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841" + ], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/1/3": { + "path": "m/44'/1'/0'/1/3", + "index": 3, + "address": "yhdRfg5gNr587dtEC4YYMcSHmLVEGqqtHc", + "transactions": [ + "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841" + ], + "balanceSat": 159999359, + "unconfirmedBalanceSat": 0, + "utxos": { + "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841-1": { + "satoshis": 159999359, + "script": "76a914e9c12479daba9d989cedba69adb56a5a50fe500288ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/1/4": { + "path": "m/44'/1'/0'/1/4", + "index": 4, + "address": "yYwKP1FQae5kbjXkmuirGx6Xzf8NzHpLqW", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/5": { + "path": "m/44'/1'/0'/1/5", + "index": 5, + "address": "yX9gmsm8aSxZZjYhq4w35aidT7qbhcpNjU", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/6": { + "path": "m/44'/1'/0'/1/6", + "index": 6, + "address": "ybgXCTGMHEBbQeUib8c3xAjtGAc12XtWiU", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/7": { + "path": "m/44'/1'/0'/1/7", + "index": 7, + "address": "yS31WpdMT2b34uL9C37fbUoACHhiupHCyP", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/8": { + "path": "m/44'/1'/0'/1/8", + "index": 8, + "address": "yTSpFqRoX3vyN286AUtKKhgmX5Xb41YKQe", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/9": { + "path": "m/44'/1'/0'/1/9", + "index": 9, + "address": "yQU5YsqN7psTTASuYbcMi7N5nNZGaxXb2X", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/10": { + "path": "m/44'/1'/0'/1/10", + "index": 10, + "address": "yVGGFj9BLgEab5rucSGLC6UGVLQKB4U1wJ", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/11": { + "path": "m/44'/1'/0'/1/11", + "index": 11, + "address": "yQCh5yYCHEbJzgSJE9rdHiqXHidKm3kwr5", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/12": { + "path": "m/44'/1'/0'/1/12", + "index": 12, + "address": "yX7T3Ac3yaLk5CTC5UaR93Fc7SjYkeT5hn", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/13": { + "path": "m/44'/1'/0'/1/13", + "index": 13, + "address": "yXx3WXq8kYNPbYEg5U6bL8Xfih4g5LCYVo", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/14": { + "path": "m/44'/1'/0'/1/14", + "index": 14, + "address": "yYnLMTz3jCi2KKKNuo3TVkEAGyUFg8tgkJ", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/15": { + "path": "m/44'/1'/0'/1/15", + "index": 15, + "address": "yiKa1dA6B4tSTNJqJP9Y5pQfQEffnQQDTL", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/16": { + "path": "m/44'/1'/0'/1/16", + "index": 16, + "address": "yf7vcuDnE9DVhXdMfBMQQTEi43otYQzkWE", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/17": { + "path": "m/44'/1'/0'/1/17", + "index": 17, + "address": "yTmSmocwERCeRHqNNG5SbpYKUra1HTmj8m", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/18": { + "path": "m/44'/1'/0'/1/18", + "index": 18, + "address": "yivUe5NeJsGsREwPQZUGYaTSwWB3E1oLcz", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/19": { + "path": "m/44'/1'/0'/1/19", + "index": 19, + "address": "ygfsZojdfW9UjCRU4ra95Aq6YgCC7UqZFx", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/20": { + "path": "m/44'/1'/0'/1/20", + "index": 20, + "address": "yU9fdXaUVtefwDZvxjJAr9xj1z2MtYi34A", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/21": { + "path": "m/44'/1'/0'/1/21", + "index": 21, + "address": "yXgMN6FgrgZCnTN1vhoZMh8afKMBmi3JC4", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/22": { + "path": "m/44'/1'/0'/1/22", + "index": 22, + "address": "yiqaCbXscvR8y3VFYMzdaKCaAGuDuZxMzt", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/23": { + "path": "m/44'/1'/0'/1/23", + "index": 23, + "address": "ydcgWDxheSxrLAqDBP4JXBndMCzUNf77gq", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/24": { + "path": "m/44'/1'/0'/1/24", + "index": 24, + "address": "yYccLAwvYUDkjSp8VXvEyZ1t2i799pGrde", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/25": { + "path": "m/44'/1'/0'/1/25", + "index": 25, + "address": "yMRfbbqFZvojgYZCshdJNWJHruQb3DuCSC", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/26": { + "path": "m/44'/1'/0'/1/26", + "index": 26, + "address": "ydR4BHZTKYWjVhDS2gGapVhaZYP4QDSa1o", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/27": { + "path": "m/44'/1'/0'/1/27", + "index": 27, + "address": "yjDuyaft6sW1pLobQ2o9RxnshquuNnfjEH", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/0": { + "path": "m/44'/1'/1'/1/0", + "index": 0, + "address": "yXMrw79LPgu78EJsfGGYpm6fXKc1EMnQ49", + "transactions": [ + "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840" + ], + "balanceSat": 59999753, + "unconfirmedBalanceSat": 0, + "utxos": { + "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840-1": { + "satoshis": 59999753, + "script": "76a914791e51fff6554c18216c83d9ca81cf30cc66aff388ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/1'/1/1": { + "path": "m/44'/1'/1'/1/1", + "index": 1, + "address": "yh6Hcyipdvp6WJpQxjNbaXP4kzPQUJpY3n", + "transactions": [ + "6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135" + ], + "balanceSat": 49999753, + "unconfirmedBalanceSat": 0, + "utxos": { + "6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135-1": { + "satoshis": 49999753, + "script": "76a914e3dd87e2dd2080c854d0c90abae96d985ae8902288ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/1'/1/2": { + "path": "m/44'/1'/1'/1/2", + "index": 2, + "address": "yNphpXuaTZRpU9FBh2W7NkUYcr3kBDE8me", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/3": { + "path": "m/44'/1'/1'/1/3", + "index": 3, + "address": "yXFppDT59xYD41mT2pmAdnvr7aZEFdgdrN", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/4": { + "path": "m/44'/1'/1'/1/4", + "index": 4, + "address": "yeKGAiiEHBGRujvLoYewA77jDDpeDamxvF", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/5": { + "path": "m/44'/1'/1'/1/5", + "index": 5, + "address": "yaxTG66CVzKgHhHZXojRHC9ztLTvz3fwdT", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/6": { + "path": "m/44'/1'/1'/1/6", + "index": 6, + "address": "yYw6qU7dwGoELZkSTj3oSKRpM4U8qTMc1U", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/7": { + "path": "m/44'/1'/1'/1/7", + "index": 7, + "address": "yQE2MksEnSfbeNre19oja9Jj8tvpj64C5a", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/8": { + "path": "m/44'/1'/1'/1/8", + "index": 8, + "address": "yaRnvHo8oLvVmv46vMj5XPbDJouQSnmcLT", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/9": { + "path": "m/44'/1'/1'/1/9", + "index": 9, + "address": "yj5ofWf2uYQQkSavYm2WXgu1QkaZCyP3Cm", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/10": { + "path": "m/44'/1'/1'/1/10", + "index": 10, + "address": "yUCjGmEwrHJwNDrE1o2rMre6MkSbiE6yz7", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/11": { + "path": "m/44'/1'/1'/1/11", + "index": 11, + "address": "yfJzd1nE2rEqz5XEurD6vs4ykizwmw9xTv", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/12": { + "path": "m/44'/1'/1'/1/12", + "index": 12, + "address": "yUk8U3jRZMHKVTa1eFDEtZpa1G4E13FP4d", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/13": { + "path": "m/44'/1'/1'/1/13", + "index": 13, + "address": "yMr59YWQFCADq4FbWrtxDUtMwwshSrmAyK", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/14": { + "path": "m/44'/1'/1'/1/14", + "index": 14, + "address": "yetSehBupzGS9yps5ogqARUGmTMAs2xVcQ", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/15": { + "path": "m/44'/1'/1'/1/15", + "index": 15, + "address": "yNcESKLwriNrhM6EyoSpZEXrzdY3uht92T", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/16": { + "path": "m/44'/1'/1'/1/16", + "index": 16, + "address": "yN2FihGU7KdaEspp39bKrhsHypeyeYzoM2", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/17": { + "path": "m/44'/1'/1'/1/17", + "index": 17, + "address": "yirpWLxHuhwFzA6LfUPKUh1Ke9RB9BUjit", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/18": { + "path": "m/44'/1'/1'/1/18", + "index": 18, + "address": "yVDN66vvdshWdNzhUaQNB6xExAHkzs1zj8", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/19": { + "path": "m/44'/1'/1'/1/19", + "index": 19, + "address": "yPzofnEhRVfDisL2nCUJtAoSHkuyMirHZS", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/20": { + "path": "m/44'/1'/1'/1/20", + "index": 20, + "address": "yM7Smr8HEhbRW4CXga898brATrJSsk1QUh", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/21": { + "path": "m/44'/1'/1'/1/21", + "index": 21, + "address": "yUprvaE5KrKVPgABmGtxba9MmZqDzKQHom", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + } + }, + "misc": {} +} diff --git a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/categorizeTransactions.expectedResults.js b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/categorizeTransactions.expectedResults.js new file mode 100644 index 00000000000..10b6c73b1ca --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/categorizeTransactions.expectedResults.js @@ -0,0 +1,295 @@ +const transactionsWithMetadataFixtures = require("./transactions-with-metadata.json"); +const expectedResultTx1 = { + from: [ + {address: 'ygrRyPRf9vSHnP1ieoRRvY9THtFbTMc66e', addressType: "unknown"}, + {address: 'yhDaDMNRUAB93S2ZcprNLuEGHPG4VT8kYL', addressType: "unknown"}, + {address: 'ygZ5fgrtGQDtwsN8K7sftSNPXN4Srhz99s', addressType: "unknown"}, + {address: 'yb39TanhfUKeqaBtzqDvAE3ad9UsDuj3Fd', addressType: "unknown"}, + {address: 'yToX9gDE6tn2Sv1zhq88WNfJSomeHee3rR', addressType: "unknown"}, + {address: 'yViAv63brJ5kB7Gyc7yX2c7rJ9NuykCzRh', addressType: "unknown"}, + {address: 'yfnJMvdE32izNQP68PhMPiHAeJKYo2PBdH', addressType: "unknown"}, + ], + to: [ + { + address: 'ySE2UYPf7PWMJ5oYikSscVifzQEoGiGRmd', + satoshis: 1823313, + addressType: "unknown", + }, + { + address: 'yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7', + satoshis: 187980000, + addressType: "external", + } + ], + transaction: transactionsWithMetadataFixtures[2][0], + type: 'received', + blockHash: '000001deee9f99e8219a9abcaaea135dbaae8a9b0f1ea214e6b6a37a5c5b115d', + height: 555506, + isInstantLocked: true, + isChainLocked: true, + satoshisBalanceImpact: 187980000, + feeImpact: 0 +} +const expectedResultTx2 = { + from: [{address: 'yaLhoAZ4iex2zKmfvS9rvEmxXmRiPrjHdD', addressType: "unknown"}], + to: [ + { + address: 'yercyhdN9oEkZcB9BsW5ktFaDxFEuK6qXN', + satoshis: 10000000, + addressType: "external", + }, + { + address: 'yTcjWB7v7opDzpfYKpFdFEtEvSKFsh3bW3', + satoshis: 532649506, + addressType: "unknown", + } + ], + + transaction: transactionsWithMetadataFixtures[4][0], + type: 'received', + blockHash: '000000b6006c758eda23ec7e2a640a0bf2c6a0c44827be216faff6bf4fd388e8', + height: 555507, + isInstantLocked: true, + isChainLocked: true, + satoshisBalanceImpact: 10000000, + feeImpact: 0 +} +const expectedResultTx3 = { + from: [ { address: 'yTcjWB7v7opDzpfYKpFdFEtEvSKFsh3bW3', addressType: "unknown" } ], + to: [ + { + address: 'ygk3GCSba2J3L9G665Snozhj9HSkh5ByVE', + satoshis: 10000000, + addressType: "external" + }, + { + address: 'yiDVYtUZ2mKV4teSJzKBArqY4BRsZoFLYs', + satoshis: 522649259, + addressType: "unknown" + } + ], + transaction: transactionsWithMetadataFixtures[5][0], + type: 'received', + blockHash: '0000012cf6377c6cf2b317a4deed46573c09f04f6880dca731cc9ccea6691e19', + height: 555508, + isInstantLocked: true, + isChainLocked: true, + satoshisBalanceImpact: 10000000, + feeImpact: 0 +} +const expectedResultTx4 = { + from: [ + { address: 'yXxUiAnB31voBDPqnwxkffcPnUvwJz6a2k', addressType: 'unknown'}, + { address: 'yNh6Xzw4rs1kenAo8VWCswdyUnkdYXDZsg', addressType: 'unknown' } + ], + to: [ + { + address: 'yXiTNo71QQAqiw2u1i6vkEEj3m6y4sEGae', + satoshis: 1768694, + addressType: "unknown" + }, + { + address: 'yMLhEsiP2ajSh8STmXnNmkWXtoHsmawZxd', + satoshis: 840010000, + addressType: "external" + } + ], + transaction: transactionsWithMetadataFixtures[1][0], + type: 'received', + blockHash: '00000221952c2a60adcb929de837f659308cb5c6bb7783016479381fb550fbad', + height: 557481, + isInstantLocked: true, + isChainLocked: true, + satoshisBalanceImpact: 840010000, + feeImpact: 0 +} +const expectedResultTx5 = { + from: [{address: 'yP8A3cbdxRtLRduy5mXDsBnJtMzHWs6ZXr', addressType: 'unknown'}], + to: [ + { + address: 'yY16qMW4TSiYGWUyANYWMSwgwGe36KUQsR', + satoshis: 46810176, + addressType: "unknown" + }, + { + address: 'ygHAVkMtYSqoTWHebDv7qkhMV6dHyuRsp2', + satoshis: 729210000, + addressType: "external" + } + ], + transaction: transactionsWithMetadataFixtures[0][0], + type: 'received', + blockHash: '00000c1e4556add15119392ed36ec6af2640569409abfa23a9972bc3be1b3717', + height: 558036, + isInstantLocked: true, + isChainLocked: true, + satoshisBalanceImpact: 729210000, + feeImpact: 0 +}; +const expectedResultTx6 = { + from: [ + { address: 'ygHAVkMtYSqoTWHebDv7qkhMV6dHyuRsp2', addressType: "external" }, + { address: 'ygk3GCSba2J3L9G665Snozhj9HSkh5ByVE', addressType: "external" }, + { address: 'yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7', addressType: "external" }, + { address: 'yercyhdN9oEkZcB9BsW5ktFaDxFEuK6qXN', addressType: "external" }, + { address: 'yMLhEsiP2ajSh8STmXnNmkWXtoHsmawZxd', addressType: "external" } + ], + to: [ + { + address: 'yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv', + satoshis: 1777100000, + addressType: "external", + }, + { + address: 'yNDpPsJqXKM36zHSNEW7c1zSvNnrZ699FY', + satoshis: 99170, + addressType: "internal", + } + ], + transaction: transactionsWithMetadataFixtures[3][0], + type: 'address_transfer', + blockHash: '00000084b4d9e887a6ad3f37c576a17d79c35ec9301e55210eded519e8cdcd3a', + height: 558102, + isInstantLocked: true, + isChainLocked: true, + satoshisBalanceImpact: 0, + feeImpact: 830 +}; +const expectedResultTx7 = { + from: [ { address: 'yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv', addressType: "external" } ], + to: [ + { + address: 'yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv', + satoshis: 1270000000, + addressType: "external", + }, + { + address: 'yhaAB6e8m3F8zmGX7WAVYa6eEfmSrrnY8x', + satoshis: 400000000, + addressType: "external", + }, + { + address: 'yLk4Hw3w4zDudrDVP6W8J9TggkY57zQUki', + satoshis: 107099720, + addressType: "internal", + } + ], + transaction: transactionsWithMetadataFixtures[6][0], + type: 'address_transfer', + blockHash: '000001953ea0bbb8ad04a9a1a2a707fef207ad22a712d7d3c619f0f9b63fa98c', + height: 558229, + isInstantLocked: true, + isChainLocked: true, + satoshisBalanceImpact: 0, + feeImpact: 280 +}; +const expectedResultTx8 = { + from: [ { address: 'yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv', addressType: 'external' } ], + to: [ + { + address: 'yYJmzWey5kNecAThet5BFxAga1F4b4DKQ2', + satoshis: 1260000000, + addressType: "otherAccount", + }, + { + address: 'yirJaK8KCE5YAmwvLadizqFw3TCXqBuZXL', + satoshis: 9999753, + addressType: "internal", + } + ], + transaction: transactionsWithMetadataFixtures[7][0], + type: 'account_transfer', + blockHash: '000000dffb05c071a8c05082a475b7ce9c1e403f3b89895a6c448fe08535a5f5', + height: 558230, + isInstantLocked: true, + isChainLocked: true, + satoshisBalanceImpact: -1260000000, + feeImpact: 247 +}; +const expectedResultTx9 = { + from: [ { address: 'yYJmzWey5kNecAThet5BFxAga1F4b4DKQ2', addressType: 'otherAccount' } ], + to: [ + { + address: 'yNCqctyQaq51WU1hN5aNwsgMsZ5fRiB7GY', + satoshis: 1200000000, + addressType: "otherAccount", + }, + { + address: 'yXMrw79LPgu78EJsfGGYpm6fXKc1EMnQ49', + satoshis: 59999753, + addressType: "otherAccount", + } + ], + transaction: transactionsWithMetadataFixtures[10][0], + type: 'account_transfer', + blockHash: '0000016fb685b4b1efed743d2263de34a9f8323ed75e732654b1b951c5cb4dde', + height: 558236, + isInstantLocked: true, + isChainLocked: true, + satoshisBalanceImpact: 0, + feeImpact: 0 +}; +const expectedResultTx10 = { + from: [ { address: 'yNCqctyQaq51WU1hN5aNwsgMsZ5fRiB7GY', addressType: 'otherAccount' } ], + to: [ + { + address: 'yiXh4Yo5djG6QH8WzXkKm5EFzqLRJWakXz', + satoshis: 1150000000, + addressType: "external", + }, + { + address: 'yh6Hcyipdvp6WJpQxjNbaXP4kzPQUJpY3n', + satoshis: 49999753, + addressType: "otherAccount", + } + ], + transaction: transactionsWithMetadataFixtures[8][0], + type: 'account_transfer', + blockHash: '000000444b3f2f02085f8befe72da5442c865c290658766cf935e1a71a4f4ba7', + height: 558242, + isInstantLocked: true, + isChainLocked: true, + satoshisBalanceImpact: 1150000000, + feeImpact: 0 +}; + +const expectedResultTx11 = { + from: [ + { address: 'yirJaK8KCE5YAmwvLadizqFw3TCXqBuZXL', addressType: 'internal' }, + { address: 'yiXh4Yo5djG6QH8WzXkKm5EFzqLRJWakXz', addressType: 'external' } + ], + to: [ + { + address: 'yMX3ycrLVF2k6YxWQbMoYgs39aeTfY4wrB', + satoshis: 1000000000, + addressType: "unknown", + }, + { + address: 'yhdRfg5gNr587dtEC4YYMcSHmLVEGqqtHc', + satoshis: 159999359, + addressType: "internal", + } + ], + transaction: transactionsWithMetadataFixtures[9][0], + type: 'sent', + blockHash: '000001f9c5de4d2b258a975bfbf7b9a3346890af6389512bea3cb6926b9be330', + height: 558246, + isInstantLocked: true, + isChainLocked: true, + satoshisBalanceImpact: -1000000000, + feeImpact: 394 +}; + +module.exports = [ + expectedResultTx1, + expectedResultTx2, + expectedResultTx3, + expectedResultTx4, + expectedResultTx5, + expectedResultTx6, + expectedResultTx7, + expectedResultTx8, + expectedResultTx9, + expectedResultTx10, + expectedResultTx11, +] diff --git a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/chain-store.json b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/chain-store.json new file mode 100644 index 00000000000..41fd6246621 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/chain-store.json @@ -0,0 +1,100 @@ +{ + "blockHeaders": { + "0000022113844aa456c439bf1f7d720c5ee013cd945acad41b6522ddb12102ee": "00000020e621284508549461adaf248278d67709f3df2315a76be01175f7137872010000dd31aa7cb665279529260b154cac878c1af59e81d03209221bcec08a0889da3a7d1ccc616682021efd0d0a00", + "0000009d9bb006df83378ba3b22f6a17e3902658401668fed0b7d4971db91293": "00000020ee0221b1dd22651bd4ca5a94cd13e05e0c727d1fbf39c456a44a8413210200008c3ca551f529a105e132841a22d560d00002e9b2d1fed25bcd75fd8fa222bd44c21ccc61083e021e2cbe0900", + "000001deee9f99e8219a9abcaaea135dbaae8a9b0f1ea214e6b6a37a5c5b115d": "0000002084813d1a5019ed9079e8c7a0d579b685f16e2e0dcaae54d9144aa4a730010000adcba982cc7bea98a786106769a9341a4d3866834d58105be0630b890cba5882553a16613554021ef2800000", + "000000b6006c758eda23ec7e2a640a0bf2c6a0c44827be216faff6bf4fd388e8": "000000205d115b5c7aa3b6e614a21e0f9b8aaeba5d13eaaabc9a9a21e8999feede010000709f82baa9c1f24fec82712e0599152d7e4d077853cf7cba91861ec3eaea22eab03a1661113a021e01da0000", + "0000012cf6377c6cf2b317a4deed46573c09f04f6880dca731cc9ccea6691e19": "00000020e888d34fbff6af6f21be2748c4a0c6f20b0a642a7eec23da8e756c00b6000000766a990ddb0e1be5191eae3ae973e4558a20d9fcdcc5309145ac8edfab8f9966963b1661b82d021e450b0000", + "00000221952c2a60adcb929de837f659308cb5c6bb7783016479381fb550fbad": "0000002031f1bb8d975bf830b0ac702b534e2f66f0edd90fcd99770c8dd4794bd8020000bcaccb7f4cdf155fe1e08ac473530f92fbb4f51d62376f34ca1d882bd810b677c57f1a61e7f5021e53b30000", + "00000c1e4556add15119392ed36ec6af2640569409abfa23a9972bc3be1b3717": "00000020638feafd82aca4e6b2ac1f77876f880e06775d2e6f79a61725f5011c6e0100005ecf6c29dcff20133c5399ef08ea13299fec7788bc03dfb7e05b0695535aa738f7bb1b6174f50e1e95660000", + "00000084b4d9e887a6ad3f37c576a17d79c35ec9301e55210eded519e8cdcd3a": "00000020b0c8a51730dfa39cd12b1f80f55784fd167bf97392d7e8caf5f93053e20000009b43512fc810155df3d5f7d73e7031812845784a6b6ce68f1649dfd8db44d8f660df1b616c8a011e4aab0000", + "000001953ea0bbb8ad04a9a1a2a707fef207ad22a712d7d3c619f0f9b63fa98c": "000000207dca199148d151d71bc35127b31205090d8f242b8a05ad2adea07bc9fe000000b37f61034e6dedf382051227c736ced4b786eb0c6495e6c23526630e38bf6d732a251c61ee09021e6eeb0000", + "000000dffb05c071a8c05082a475b7ce9c1e403f3b89895a6c448fe08535a5f5": "000000208ca93fb6f9f019c6d3d712a722ad07f2fe07a7a2a1a904adb8bba03e95010000d1db1a1802320ab91ddb72525338b3a46059b0d3f3cbed01f550a72577db773cb9261c614d02021eb36b0000", + "000000444b3f2f02085f8befe72da5442c865c290658766cf935e1a71a4f4ba7": "0000002031b3986a9c0814a212264200e2a276c551e5df433c5e147276eab87b0c02000090f859396304db0dd94b9214222d0dd1dfd16d79e4bc610f68ffd18c377f5ef7be2b1c615321021e1d240000", + "000001f9c5de4d2b258a975bfbf7b9a3346890af6389512bea3cb6926b9be330": "00000020478e3710c6030a6d6e9c8588789f530b498db6f8e18b20e9ab23b48bd60100003ffa42f2dc18080e764a4cdd37fccf46533b74287ed7579200f5c937268ab9a9542f1c61803e021efbd80000" + }, + "transactions": { + "a43845e580ad01f31bc06ce47ab39674e40316c4c6b765b6e54d6d35777ef456": "0200000007fd1ef99bc1edb4ab93ba74309da788e4ac460975733f02936a6321620d2a8011000000006b483045022100dfb220a840d597179abdf49692ad64c1c0da785041975b00aee03c9625639cf202204d06eade5cca19fab1e10b1d6e1b67c77626a0e88bb4d5f61bd57293b4b64217012102295ecb812ccf52deaf304bebfe3a59a644f05bac81241ea1e3a2f8750064cbf6feffffff694226ee65ea29ba7ec3e5448464c16f11d5b7564f6b3b5d0425a4c751389519000000006b483045022100beff3263b7c99720e99af9ec146c818701efb0130603f1570f427b74aef8521802202e660bb9f7ea156f91addd5fe47cbd2c2bf388cc6e1eff3a39adffd89d26d346012102c33942799f7cbf4a7d12f1b3e52cb80cc4de083b997d3e63915df9973d5bce2afeffffff963e9964a8394a07dde1dbb1e8b34af0f194def0db77fa69229030fbade2c82d000000006b483045022100ff67776932e7a32520aa131f76bdfd6737650ad3b11edbdf466cca83f691b0e60220633bcbedebacffd53ceb7e9cdbd47928d7c2849f49ac1f8efb9f384c1a4ee46301210371c0bc42e08de059a8829730abb16f3d40cff87e5ad85d65c4a0a949d9c4b524feffffff1fe16550125c3398c25d97308d7ab89bdd3d27a1589c78e97c4823c92723cf40000000006b483045022100cca348c7ab16fac28b3bba502be54a9e3766b7da9821a90605f370b75840569702207d082510aa493988e09da046355b018781718208f8a954e14ea33d608ae59625012103699b9402e109ed9d0c67c6a45be5cf5f1236c44bb9fc4b07a2f3392ba0b64172feffffffce251fb3d87d7df03b0fbd720ce5425ab0ea86d96a4ec658463d9507928bb34b000000006a47304402203ae564ff74b08b1f96bf857f51448434418d747a02039ec1ee109a4f5d8e8106022072f8769bd175416d22f44011f7e67aec301f08573c9937be7e4a09c394c7396601210311bae874933a4503a61d1c8c2e5b57b1a278d28d4892af4bd79ab8a731495265feffffffaf2dfef80d4a1f77c75dfc74e3769d8526c66c467c859b16b3439ab213851eb2000000006a47304402200b49b7059064efb57df453dc2d20002f09b5266bc825760ef81624771f13920802200782616b8c4fb7b5eff94fdf865e6ddc4530d3932b97fdc3a747e8c451f0314c012103a94131f28f8efd67f47f2496ff6e8d9069a3a7df97202a33e90e16f257d03729feffffff344fef05224cca98a16f87515041df61cbca948518761021a286d1a76e2bfdd6000000006a47304402202a24d1123775641269c6f748d3e4dad08a682e4e334a9b73c7df84f6c22e8e7d022022c0cc2225d3f14cb58a6fb3e4bf23c0f33252d9040a9ca9ef66eb17742a476f01210347301de4c9ba7f46b0f27cb82ae70a73749821e2951d3c87c2f0d56648635d1cfeffffff0251d21b00000000001976a91440ca54360086cc0fbd69d862db58ab2b6d22805888ace058340b000000001976a914538da44e7136cc994023d89a7b4b3d02ac0e573988acf1790800", + "d37b6c7dd449d605bea9997af8bbeed2f3fbbcb23a4068b1f1ad694db801912d": "03000000014e74eb53f4b1fa08e1fa02788005b8ef9f70929b3f0668fcb9dc4093b7385af3010000006a473044022071fcd620db9f245fe91c44e9b298ee5a718c2be728958f9e707dd1f785fb05e002207b05d45e4ab78c83f9117032a109d6afa37883ca5b479d2b3110fb0d773d23df0121033883dff35aeac917a26d2cbfb59a365c7ff83256a49c4d6aed8f1c0684605c40ffffffff0280969800000000001976a914cb579d4aa777c3583f61f28425ae3fea5b60d39088ac2296bf1f000000001976a914500ddabedf00296b40842cea428951d57331d5e088ac00000000", + "7d1b78157f9f2238669f260d95af03aeefc99577ff0cddb91b3e518ee557a2fd": "03000000012d9101b84d69adf1b168403ab2bcfbf3d2eebbf87a99a9be05d649d47d6c7bd3010000006a47304402201cc3d6887d5161eba36a5e6fb1ccd8e8f9eeda7fe95b4fb0a1accb99eeba0223022040d0df81fde8f59c807e541ca5bcfc9d7450f76657aeb44c708fa7d65b7d58410121038cdae47fceb5b117cd3ef5bdf8c9f2a83679a9105d012095762067bdb2351ceaffffffff0280969800000000001976a914e00939d2ec2f885f5e7dc7b9f5b06dcf868d0c4b88acabfe261f000000001976a914f03286cbb7954ea6affa9654af6cfe1210dd0c6288ac00000000", + "eb1a7fc8e3b43d3021653b1176f8f9b41e9667d05b65ee225d14c149a5b14f77": "02000000024f5ccb10d1762b155b25a32c1242f72e506ead7dcdffebbb9a57c450eff24306000000006a473044022006d0f91fbc789475f4bc545901b069a5baf657b36914638b7847e61c6ce508a0022061cc6488a5e8e7d142acf015d9447ea2c37b2c44f7ce9822b2b3421e6f200111012103f7626b79771dc4e1928ffbb407aa32f7089bd8deebb7b7393d28397b504f3002feffffff07f289648f6f097dd46857c15d64576658b9f2c693a1472eb8beaa21b21ec020010000006a47304402207ac2b0c2ea3c073db24d893b533d1d5753f35a85c00971e8d0a1d7fdd77c1ffb0220024b43bdd62ee96189e3da40c5725d771c6cb8df0909826538f558fb32c46cf901210308b60306848cbb551d800192ddecfc07bd6cb34eb23df4c53d840a07d1db6e0cfeffffff02f6fc1a00000000001976a9147d03641b70a1883b600f9646081c8bf626504ac288ac10891132000000001976a9140b348dfe637f57295943cfdc2b5c65c79c0da6ba88ac72810800", + "1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b": "0200000001194d876938fc6a69f418367e81425640963a2f2569e44b8652fbf98deb49746a000000006b483045022100fc497d765125566b303738841fb04c953fa286e4f531f5fca21df8c2bbdb0b0f02207a57b073f934b47bb1d1b83bb7fedf7a2d52061ea8107d0362d7c2bedfe4b7d401210200669c7e5dd728b676c2c1163ddcfa88e7cd4f01d12f01188b6b32c399c008ccfeffffff024044ca02000000001976a914802950915c17f11b1a677dd7cae5101e376fb34888ac90dc762b000000001976a914daf40881fda36848da6cc430dcbec6da3ea421b088acd3830800", + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8": "03000000051bacf8f5091d903e647e46672bba1f3a6f2d120f573868958b9105c1ed35bb1c010000006b4830450221008f3e0a1b59bd4072242fafbe994fb264ca3751480628755585de32e7a828b13902205ed37762b260f9ad8ef641084da938b8bd6dd2df0f7d8dca91e7c0ac0d31f4610121039ac6241be1c1f328522f0cd183ccbe78c10ad74d4535bec40f5e8fe082633d3cfffffffffda257e58e513e1bb9dd0cff7795c9efae03af950d269f6638229f7f15781b7d000000006a47304402203773f77a278f4039caa7024373bd5ac8002c12f88ac20b1ac1480ed616c1b561022054811f33ba1403e1fa38839994e97bb4c6540b78b1a2a2d102cccd2f218cd99e012103d59b76bb58143cf8de0ff52007242f7af15f1e6943c40d4d5d5fc5e0aa5141aeffffffff56f47e77356d4de5b665b7c6c41603e47496b37ae46cc01bf301ad80e54538a4010000006a4730440220762743c1cb769a51f6f5e600da17c35cf6ed9d45315e96c96fb203d743118b15022062d5c03716c6a6a9a1bbe9eb139cc500778ef3bf00aa21724e83391edcfcf20b012103e98d3cc012bb72006634b098d678afac500a1b3d3a430ac972075b5fb7153d87ffffffff2d9101b84d69adf1b168403ab2bcfbf3d2eebbf87a99a9be05d649d47d6c7bd3000000006b483045022100d31090741004486f2cb473eb046d058e63f12ff9e101a2f2028d5f0e3371499d022057f7b3f0375590d3961cf8b51bde61a830bb4f29b07d7e48132de7d292306193012103f4991317c773fbb37756fb222909c8bbd44e53f3c4e4f3060edb3df7d8549b24ffffffff774fb1a549c1145d22ee655bd067961eb4f9f876113b6521303db4e3c87f1aeb010000006a473044022064f3b9869041239074dadae73c2d0e78abf100d89f88d4ba33b7b3b83eaf87010220608079322bd051ac7900a3bb96d3bc694f2b8d65850bcfc043ce51e6367c654801210332d0d14a1a28c90c1149b691a675e26f4c8b485b8dcc3624280a9fdc954ef081ffffffff02e064ec69000000001976a914fa49fe511c437a0d4ec01050184bd2d6538b3f0888ac62830100000000001976a91414dfbdcfb48babe7127fa0ee90339c33a46aeda288ac00000000", + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5": "0300000001f8eea18afa702989208916a4fb8ce7ed49b5d915257f6f3dd977f54b41a930f2000000006a4730440220442af7402fad5756cb4bd1890023369c9b4f63129cc2b6020b3e610cfe0f05f502206d90f315d3467d86b71871b484ab1dfdfdc7fb91cc190f35eb84c34d40490d6b012102ba0588ffd3c838b715d7c79bcf1cff2ba69befd5ea52aa3474d66f094536cac0ffffffff0380a9b24b000000001976a914fa49fe511c437a0d4ec01050184bd2d6538b3f0888ac0084d717000000001976a914e922f6420544f1be0cb593c10535cc3469198bc888ac48366206000000001976a91404a791e67467246c3c0a003007793160387de54288ac00000000", + "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533": "0300000001b51d5a6f5c7a680bce489e6f5a9b176ac85c49f10db4798867c7d1eb2036fbc3000000006a4730440220283fd42353767188532db4a4f1c3d0a9e96e313196ae1310af6d3006c7aa64ff022027fa50cf065c096f146e00516cb3e28a9bb387a6cf1103aae0592d5c882d25e5012102ba0588ffd3c838b715d7c79bcf1cff2ba69befd5ea52aa3474d66f094536cac0ffffffff0200131a4b000000001976a914838112cc6c85e074aa7f373e942c9f5240c3e13a88ac89959800000000001976a914f728c15b9a5fe4e6d7b6ed74b323e23f5c6e303f88ac00000000", + "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840" : "0300000001338540c64b794f73913f39f2d42d9139ce7c9d1c0ec5317c62ab4a28d6b0376f000000006b483045022100b996d726d224a762acf8ab3e37c085e796b44960b8e9933571ac57750e8ed05102201c6a36d72f16140d6a152be40add102d95a4ac5b177d300b10c277859690a859012103b5614f077d750a1eaffb23ca188dbcc7e267f4b8ffdedf81cdf970643027191bffffffff02008c8647000000001976a91414b05906daab037707927bc6c83900d5dbf2849688ac09869303000000001976a914791e51fff6554c18216c83d9ca81cf30cc66aff388ac00000000", + "6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135": "030000000140d85c0bec59ac6ba3892164ef9816b45f5d105769bcae339af9a7874ad4d39c000000006b483045022100eef13b38a771924b1429b119ef27494fe764cf1a7b12962462c4934ba15dd426022037097eaf74e3f3b547bf5e67451c7bf1e2e2a4b7b205850b1315bc4ab983fdf2012103f376b41c9e9ebc3131e33d4de127c57c1bf3ca88f81845595c44f9ac46122677ffffffff02809b8b44000000001976a914f3a39f8266812baa084890d02fc489f2aee8075a88ac89effa02000000001976a914e3dd87e2dd2080c854d0c90abae96d985ae8902288ac00000000", + "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841": "0300000002338540c64b794f73913f39f2d42d9139ce7c9d1c0ec5317c62ab4a28d6b0376f010000006a47304402204de0c38c97e07cddaa0da91563b9ae7620c593c18fe146dde8429b662e542b4902203eeb557d6553dbac0d4f813c07d5ad6ecdd9c27e2e228aaba8c727943676f62b0121038ada8b4de6d21a29ab12401e70d7f44566dbd224a056c857f273b48adf8b0cd2ffffffff351180e1d85feb4f2ae13b224a8e630ddcaf9806f8bb3b371bcbc63880ca766f000000006b483045022100b9a1ff2866f2795fead698f7b16f26c42cc91ef4efda6203e147d8fe910b31cb02203c9c846cea5efa369f2f0e015952bd5992a884a0d79b2c237bcaa842a83f0efa0121033f532214f69c414bc1742367df5cd1195c64a5ee08455d0aad17f6de72e9eaadffffffff0200ca9a3b000000001976a9140d2a064dc57ccd2270a436a871f277bbb7b9ca2088ac7f658909000000001976a914e9c12479daba9d989cedba69adb56a5a50fe500288ac00000000" + }, + "txMetadata": { + "a43845e580ad01f31bc06ce47ab39674e40316c4c6b765b6e54d6d35777ef456": { + "blockHash": "000001deee9f99e8219a9abcaaea135dbaae8a9b0f1ea214e6b6a37a5c5b115d", + "height": 555506, + "isInstantLocked": true, + "isChainLocked": true + }, + "d37b6c7dd449d605bea9997af8bbeed2f3fbbcb23a4068b1f1ad694db801912d": { + "blockHash": "000000b6006c758eda23ec7e2a640a0bf2c6a0c44827be216faff6bf4fd388e8", + "height": 555507, + "isInstantLocked": true, + "isChainLocked": true + }, + "7d1b78157f9f2238669f260d95af03aeefc99577ff0cddb91b3e518ee557a2fd": { + "blockHash": "0000012cf6377c6cf2b317a4deed46573c09f04f6880dca731cc9ccea6691e19", + "height": 555508, + "isInstantLocked": true, + "isChainLocked": true + }, + "eb1a7fc8e3b43d3021653b1176f8f9b41e9667d05b65ee225d14c149a5b14f77": { + "blockHash": "00000221952c2a60adcb929de837f659308cb5c6bb7783016479381fb550fbad", + "height": 557481, + "isInstantLocked": true, + "isChainLocked": true + }, + "1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b": { + "blockHash": "00000c1e4556add15119392ed36ec6af2640569409abfa23a9972bc3be1b3717", + "height": 558036, + "isInstantLocked": true, + "isChainLocked": true + }, + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8": { + "blockHash": "00000084b4d9e887a6ad3f37c576a17d79c35ec9301e55210eded519e8cdcd3a", + "height": 558102, + "isInstantLocked": true, + "isChainLocked": true + }, + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5": { + "blockHash": "000001953ea0bbb8ad04a9a1a2a707fef207ad22a712d7d3c619f0f9b63fa98c", + "height": 558229, + "isInstantLocked": true, + "isChainLocked": true + }, + "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533": { + "blockHash": "000000dffb05c071a8c05082a475b7ce9c1e403f3b89895a6c448fe08535a5f5", + "height": 558230, + "isInstantLocked": true, + "isChainLocked": true + }, + "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840": { + "blockHash": "0000016fb685b4b1efed743d2263de34a9f8323ed75e732654b1b951c5cb4dde", + "height": 558236, + "isInstantLocked": true, + "isChainLocked": true + }, + "6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135": { + "blockHash": "000000444b3f2f02085f8befe72da5442c865c290658766cf935e1a71a4f4ba7", + "height": 558242, + "isInstantLocked": true, + "isChainLocked": true + }, + "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841": { + "blockHash": "000001f9c5de4d2b258a975bfbf7b9a3346890af6389512bea3cb6926b9be330", + "height": 558246, + "isInstantLocked": true, + "isChainLocked": true + } + }, + "fees": { + "minRelay": -1 + } +} diff --git a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage.js b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage.js new file mode 100644 index 00000000000..9f678c9cc9a --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage.js @@ -0,0 +1,89 @@ +const walletStoreMock = require('./wallet-store.json'); +const chainStoreMock = require('./chain-store.json'); +const Storage = require('../../../src/types/Storage/Storage'); +const { KeyChainStore, DerivableKeyChain } = require('../../../src/index'); +const createPathsForTransactions = require('../../../src/types/Account/methods/createPathsForTransactions'); +const addPathsToStore = require("../../../src/types/Account/methods/addPathsToStore"); +const generateNewPaths = require("../../../src/types/Account/methods/generateNewPaths"); +const addDefaultPaths = require("../../../src/types/Account/methods/addDefaultPaths"); + +module.exports = (opts = {}) => { + const { walletId } = walletStoreMock; + + const mockedWallet = { + walletId, + storage: new Storage(), + keyChainStore: null + } + + mockedWallet.storage.createWalletStore(walletId); + mockedWallet.storage.createChainStore('testnet'); + + mockedWallet.keyChainStore = new KeyChainStore(); + mockedWallet.keyChainStore.addKeyChain(new DerivableKeyChain({ + mnemonic: 'apart trip dignity try point rocket damp reflect raw ten normal young', + }), { isMasterKeyChain: true }); + + const walletStore = mockedWallet.storage.getWalletStore(walletId); + walletStore.importState(walletStoreMock); + const chainStore = mockedWallet.storage.getChainStore('testnet'); + chainStore.importState(chainStoreMock); + + const mockedAccount0 = { + walletId, + index: 0, + storage: mockedWallet.storage, + accountPath: "m/44'/1'/0'", + network: 'testnet', + walletType: 'hdwallet', + ...opts, + addDefaultPaths, + createPathsForTransactions, + generateNewPaths, + addPathsToStore, + keyChainStore: null + }; + + // This account is not participating directly in the mock. + // However, we must take it into consideration having in mind that it participates in account_transfer actions + const mockedAccount1 = { + walletId, + network: 'testnet', + index: 1, + accountPath: "m/44'/1'/1'", + keyChainStore: null, + storage: mockedWallet.storage, + addDefaultPaths, + addPathsToStore, + }; + + const accounts = [mockedAccount0, mockedAccount1]; + /** + * Fill path states for both accounts in wallet store + */ + accounts.forEach(account => { + walletStore.createPathState(account.accountPath); + }) + + /** + * Initialize key chain stores and default derivation paths for accounts + */ + accounts.forEach(account => { + account.keyChainStore = mockedWallet.keyChainStore + .makeChildKeyChainStore(account.accountPath, { + lookAheadOpts: { + paths: { + 'm/0': 20, + 'm/1': 20, + } + } + }); + + account.addDefaultPaths() + }) + + + mockedAccount0.createPathsForTransactions() + + return mockedAccount0; +}; diff --git a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/store.json b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/store.json new file mode 100644 index 00000000000..3ebad673683 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/store.json @@ -0,0 +1,1582 @@ +{ + "wallets": { + "d6143ef4e6": { + "accounts": { + "m/44'/1'/0'": { + "label": null, + "path": "m/44'/1'/0'", + "network": "testnet", + "blockHeight": 560133, + "blockHash": "000001953ea0bbb8ad04a9a1a2a707fef207ad22a712d7d3c619f0f9b63fa98c" + }, + "m/44'/1'/1'": { + "label": null, + "path": "m/44'/1'/1'", + "network": "testnet", + "blockHeight": 560133, + "blockHash": "000000dffb05c071a8c05082a475b7ce9c1e403f3b89895a6c448fe08535a5f5" + } + }, + "network": "testnet", + "mnemonic": null, + "type": null, + "identityIds": [], + "addresses": { + "external": { + "m/44'/1'/0'/0/0": { + "path": "m/44'/1'/0'/0/0", + "index": 0, + "address": "yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7", + "transactions": [ + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8" + ], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/1": { + "path": "m/44'/1'/0'/0/1", + "index": 1, + "address": "yercyhdN9oEkZcB9BsW5ktFaDxFEuK6qXN", + "transactions": [ + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8" + ], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/2": { + "path": "m/44'/1'/0'/0/2", + "index": 2, + "address": "ygk3GCSba2J3L9G665Snozhj9HSkh5ByVE", + "transactions": [ + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8" + ], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/3": { + "path": "m/44'/1'/0'/0/3", + "index": 3, + "address": "ybuL6rM6dgrKzCg8s99f3jxGuv5oz5JcDA", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/4": { + "path": "m/44'/1'/0'/0/4", + "index": 4, + "address": "ygHAVkMtYSqoTWHebDv7qkhMV6dHyuRsp2", + "transactions": [ + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8", + "1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b" + ], + "balanceSat": 729210000, + "unconfirmedBalanceSat": 0, + "utxos": { + "1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b-1": { + "satoshis": 729210000, + "script": "76a914daf40881fda36848da6cc430dcbec6da3ea421b088ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/5": { + "path": "m/44'/1'/0'/0/5", + "index": 5, + "address": "yMLhEsiP2ajSh8STmXnNmkWXtoHsmawZxd", + "transactions": [ + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8" + ], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/6": { + "path": "m/44'/1'/0'/0/6", + "index": 6, + "address": "yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv", + "transactions": [ + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8", + "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533", + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5" + ], + "balanceSat": 1270000000, + "unconfirmedBalanceSat": 0, + "utxos": { + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5-0": { + "satoshis": 1270000000, + "script": "76a914fa49fe511c437a0d4ec01050184bd2d6538b3f0888ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/7": { + "path": "m/44'/1'/0'/0/7", + "index": 7, + "address": "yhaAB6e8m3F8zmGX7WAVYa6eEfmSrrnY8x", + "transactions": [ + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5" + ], + "balanceSat": 400000000, + "unconfirmedBalanceSat": 0, + "utxos": { + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5-1": { + "satoshis": 400000000, + "script": "76a914e922f6420544f1be0cb593c10535cc3469198bc888ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/8": { + "path": "m/44'/1'/0'/0/8", + "index": 8, + "address": "yiXh4Yo5djG6QH8WzXkKm5EFzqLRJWakXz", + "transactions": [ + "6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135", + "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841" + ], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/0/9": { + "path": "m/44'/1'/0'/0/9", + "index": 9, + "address": "yQYv3Um6DsdtANo1ZPTUte75wAGMstLRex", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/10": { + "path": "m/44'/1'/0'/0/10", + "index": 10, + "address": "yiYPJmu7eEm1cXUNumQRdjv1fvPhsfgMS4", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/11": { + "path": "m/44'/1'/0'/0/11", + "index": 11, + "address": "yii4aUZhNfL6EWN9KAgAFrJzGJmqHnF4wx", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/12": { + "path": "m/44'/1'/0'/0/12", + "index": 12, + "address": "yLpTquSct2SGz2Ka45uTPDd81Kzro2Jt2k", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/13": { + "path": "m/44'/1'/0'/0/13", + "index": 13, + "address": "yMiJtpzb1Qthy9TGnavsf5NZ6EZZa4j9q3", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/14": { + "path": "m/44'/1'/0'/0/14", + "index": 14, + "address": "yacgSfW7RkwWakEZPg8USAVdzCypiG3vxS", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/15": { + "path": "m/44'/1'/0'/0/15", + "index": 15, + "address": "yVvrmoRPFLy6nUpCQBT8ZExxF5wF3DhiGU", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/16": { + "path": "m/44'/1'/0'/0/16", + "index": 16, + "address": "yaJf2aG6cFUtfv4o6TuEKsh5kr4xq5iAY4", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/17": { + "path": "m/44'/1'/0'/0/17", + "index": 17, + "address": "yfardJQ4ucgWLKQPaRHGMRMbSGm5H4ExJR", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/18": { + "path": "m/44'/1'/0'/0/18", + "index": 18, + "address": "yLSCqx7dcM5JKR2fG7vHbF2axMvuYqomaw", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/19": { + "path": "m/44'/1'/0'/0/19", + "index": 19, + "address": "yVij8XpJ78LM5hepSV1KF7T8vRpUEXCpK5", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/20": { + "path": "m/44'/1'/0'/0/20", + "index": 20, + "address": "ydJpjuJGossAZR7S5oS7cWvjygEwoj8Xwp", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/21": { + "path": "m/44'/1'/0'/0/21", + "index": 21, + "address": "yW3TmWnmhvpxRbgFcQ8oXqDRkn3RhRH6jj", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/22": { + "path": "m/44'/1'/0'/0/22", + "index": 22, + "address": "yRegVX85DThKRkH8C61TtRacfzrkiBfNy5", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/23": { + "path": "m/44'/1'/0'/0/23", + "index": 23, + "address": "yPtDCqDFRe1JuDp8pvdiEMQMz2erGwS3VG", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/24": { + "path": "m/44'/1'/0'/0/24", + "index": 24, + "address": "yM9pSw3L4oBfG7uQL5o522Hu3WTvy9awgZ", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/25": { + "path": "m/44'/1'/0'/0/25", + "index": 25, + "address": "yNC6qYJYungzuk5XUynDFKCn54Dy8ngox4", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/26": { + "path": "m/44'/1'/0'/0/26", + "index": 26, + "address": "yR5KcLr1bceLT4teTk2qoJx6pFLik1zyzL", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/27": { + "path": "m/44'/1'/0'/0/27", + "index": 27, + "address": "yRrKLGJa9JmdjBWvrHtedKjHTao6CRDTKf", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/0/28": { + "path": "m/44'/1'/0'/0/28", + "index": 28, + "address": "yP5dShZBydpbEzgGoXL6kcjv2KzervRrYB", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/0": { + "path": "m/44'/1'/1'/0/0", + "index": 0, + "address": "yYJmzWey5kNecAThet5BFxAga1F4b4DKQ2", + "transactions": [ + "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840", + "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533" + ], + "balanceSat": 1260000000, + "unconfirmedBalanceSat": 0, + "utxos": { + "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533-0": { + "satoshis": 1260000000, + "script": "76a914838112cc6c85e074aa7f373e942c9f5240c3e13a88ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/1'/0/1": { + "path": "m/44'/1'/1'/0/1", + "index": 1, + "address": "yNCqctyQaq51WU1hN5aNwsgMsZ5fRiB7GY", + "transactions": [ + "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840", + "6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135" + ], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/1'/0/2": { + "path": "m/44'/1'/1'/0/2", + "index": 2, + "address": "yNPbYz5cZKw2EwxtkL3VSVzPi2FYp9VKjQ", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/3": { + "path": "m/44'/1'/1'/0/3", + "index": 3, + "address": "ybsGWzsnSCAZufgSeUjScVxqEdved99UM2", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/4": { + "path": "m/44'/1'/1'/0/4", + "index": 4, + "address": "yfNHuPojk8XKWP5nuueDptX4nM7qToudgx", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/5": { + "path": "m/44'/1'/1'/0/5", + "index": 5, + "address": "yXxLnDkk6s8h1PSnYaFM6MAyRarc1Kc1rY", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/6": { + "path": "m/44'/1'/1'/0/6", + "index": 6, + "address": "yipResSzN2zUvL7UYkmptKKmQTv7sNssRn", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/7": { + "path": "m/44'/1'/1'/0/7", + "index": 7, + "address": "yZPtNwimHdRiKYbNQW49qezw1Kc1YwUJeT", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/8": { + "path": "m/44'/1'/1'/0/8", + "index": 8, + "address": "yPMjYYfQbga2nBiuqqfUyX41U1vwRZ8fG8", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/9": { + "path": "m/44'/1'/1'/0/9", + "index": 9, + "address": "yLueLWWcLQsaXQ8D5o9tcyo8tfTxMWXvG4", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/10": { + "path": "m/44'/1'/1'/0/10", + "index": 10, + "address": "yN8gzgsc1RVjXThMQT5qZH2jjpnMymz6zP", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/11": { + "path": "m/44'/1'/1'/0/11", + "index": 11, + "address": "yPQLWBNwMdLxUW2oUwHGwQtfyYxD41BARJ", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/12": { + "path": "m/44'/1'/1'/0/12", + "index": 12, + "address": "yg5g2AfWFdwWexWGfbSXYbUHf1y5WWrFPs", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/13": { + "path": "m/44'/1'/1'/0/13", + "index": 13, + "address": "yWyABu4naV1Jzw7w9sn1gqhebPRSkCndsS", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/14": { + "path": "m/44'/1'/1'/0/14", + "index": 14, + "address": "ycuUPzUBjhKyUjezQR1LNot79a6C4aRLaR", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/15": { + "path": "m/44'/1'/1'/0/15", + "index": 15, + "address": "yQ7YjvAXgDAUCekveHVjr6NBveXrUemVno", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/16": { + "path": "m/44'/1'/1'/0/16", + "index": 16, + "address": "yi8bghcw627cMGpuH4bJqH6bqR5ywv1NLH", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/17": { + "path": "m/44'/1'/1'/0/17", + "index": 17, + "address": "yizHu8i2rfwzwBgnJ62s2WUe6wLoDjne6N", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/18": { + "path": "m/44'/1'/1'/0/18", + "index": 18, + "address": "yW1u3tySeUKAKJsz7sjZFyjUiTyKLB6xBv", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/19": { + "path": "m/44'/1'/1'/0/19", + "index": 19, + "address": "yNaSkdy1Q8JNubUdbLMGsGf7sTRofEJYZq", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/20": { + "path": "m/44'/1'/1'/0/20", + "index": 20, + "address": "yjG4jiCMNbx3MyFCAsaNaU29CBVmgPf8hS", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/0/21": { + "path": "m/44'/1'/1'/0/21", + "index": 21, + "address": "yWrEYTtydaZCyJoKE1vzX4e4RXDNPGdnw9", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + } + }, + "internal": { + "m/44'/1'/0'/1/0": { + "path": "m/44'/1'/0'/1/0", + "index": 0, + "address": "yNDpPsJqXKM36zHSNEW7c1zSvNnrZ699FY", + "transactions": [ + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8" + ], + "balanceSat": 99170, + "unconfirmedBalanceSat": 0, + "utxos": { + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8-1": { + "satoshis": 99170, + "script": "76a91414dfbdcfb48babe7127fa0ee90339c33a46aeda288ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/1/1": { + "path": "m/44'/1'/0'/1/1", + "index": 1, + "address": "yLk4Hw3w4zDudrDVP6W8J9TggkY57zQUki", + "transactions": [ + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5" + ], + "balanceSat": 107099720, + "unconfirmedBalanceSat": 0, + "utxos": { + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5-2": { + "satoshis": 107099720, + "script": "76a91404a791e67467246c3c0a003007793160387de54288ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/1/2": { + "path": "m/44'/1'/0'/1/2", + "index": 2, + "address": "yirJaK8KCE5YAmwvLadizqFw3TCXqBuZXL", + "transactions": [ + "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841", + "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533" + ], + "balanceSat": 9999753, + "unconfirmedBalanceSat": 0, + "utxos": { + "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533-1": { + "satoshis": 9999753, + "script": "76a914f728c15b9a5fe4e6d7b6ed74b323e23f5c6e303f88ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/1/3": { + "path": "m/44'/1'/0'/1/3", + "index": 3, + "address": "yhdRfg5gNr587dtEC4YYMcSHmLVEGqqtHc", + "transactions": [ + "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841" + ], + "balanceSat": 159999359, + "unconfirmedBalanceSat": 0, + "utxos": { + "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841-1": { + "satoshis": 159999359, + "script": "76a914e9c12479daba9d989cedba69adb56a5a50fe500288ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/0'/1/4": { + "path": "m/44'/1'/0'/1/4", + "index": 4, + "address": "yYwKP1FQae5kbjXkmuirGx6Xzf8NzHpLqW", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/5": { + "path": "m/44'/1'/0'/1/5", + "index": 5, + "address": "yX9gmsm8aSxZZjYhq4w35aidT7qbhcpNjU", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/6": { + "path": "m/44'/1'/0'/1/6", + "index": 6, + "address": "ybgXCTGMHEBbQeUib8c3xAjtGAc12XtWiU", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/7": { + "path": "m/44'/1'/0'/1/7", + "index": 7, + "address": "yS31WpdMT2b34uL9C37fbUoACHhiupHCyP", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/8": { + "path": "m/44'/1'/0'/1/8", + "index": 8, + "address": "yTSpFqRoX3vyN286AUtKKhgmX5Xb41YKQe", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/9": { + "path": "m/44'/1'/0'/1/9", + "index": 9, + "address": "yQU5YsqN7psTTASuYbcMi7N5nNZGaxXb2X", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/10": { + "path": "m/44'/1'/0'/1/10", + "index": 10, + "address": "yVGGFj9BLgEab5rucSGLC6UGVLQKB4U1wJ", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/11": { + "path": "m/44'/1'/0'/1/11", + "index": 11, + "address": "yQCh5yYCHEbJzgSJE9rdHiqXHidKm3kwr5", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/12": { + "path": "m/44'/1'/0'/1/12", + "index": 12, + "address": "yX7T3Ac3yaLk5CTC5UaR93Fc7SjYkeT5hn", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/13": { + "path": "m/44'/1'/0'/1/13", + "index": 13, + "address": "yXx3WXq8kYNPbYEg5U6bL8Xfih4g5LCYVo", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/14": { + "path": "m/44'/1'/0'/1/14", + "index": 14, + "address": "yYnLMTz3jCi2KKKNuo3TVkEAGyUFg8tgkJ", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/15": { + "path": "m/44'/1'/0'/1/15", + "index": 15, + "address": "yiKa1dA6B4tSTNJqJP9Y5pQfQEffnQQDTL", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/16": { + "path": "m/44'/1'/0'/1/16", + "index": 16, + "address": "yf7vcuDnE9DVhXdMfBMQQTEi43otYQzkWE", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/17": { + "path": "m/44'/1'/0'/1/17", + "index": 17, + "address": "yTmSmocwERCeRHqNNG5SbpYKUra1HTmj8m", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/18": { + "path": "m/44'/1'/0'/1/18", + "index": 18, + "address": "yivUe5NeJsGsREwPQZUGYaTSwWB3E1oLcz", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/19": { + "path": "m/44'/1'/0'/1/19", + "index": 19, + "address": "ygfsZojdfW9UjCRU4ra95Aq6YgCC7UqZFx", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/20": { + "path": "m/44'/1'/0'/1/20", + "index": 20, + "address": "yU9fdXaUVtefwDZvxjJAr9xj1z2MtYi34A", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/21": { + "path": "m/44'/1'/0'/1/21", + "index": 21, + "address": "yXgMN6FgrgZCnTN1vhoZMh8afKMBmi3JC4", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/22": { + "path": "m/44'/1'/0'/1/22", + "index": 22, + "address": "yiqaCbXscvR8y3VFYMzdaKCaAGuDuZxMzt", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/23": { + "path": "m/44'/1'/0'/1/23", + "index": 23, + "address": "ydcgWDxheSxrLAqDBP4JXBndMCzUNf77gq", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/24": { + "path": "m/44'/1'/0'/1/24", + "index": 24, + "address": "yYccLAwvYUDkjSp8VXvEyZ1t2i799pGrde", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/25": { + "path": "m/44'/1'/0'/1/25", + "index": 25, + "address": "yMRfbbqFZvojgYZCshdJNWJHruQb3DuCSC", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/26": { + "path": "m/44'/1'/0'/1/26", + "index": 26, + "address": "ydR4BHZTKYWjVhDS2gGapVhaZYP4QDSa1o", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/27": { + "path": "m/44'/1'/0'/1/27", + "index": 27, + "address": "yjDuyaft6sW1pLobQ2o9RxnshquuNnfjEH", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/0'/1/28": { + "path": "m/44'/1'/0'/1/28", + "index": 28, + "address": "yZRX4G1iMyoYE1jRh1UUbSrgSX9DYvrPUF", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/0": { + "path": "m/44'/1'/1'/1/0", + "index": 0, + "address": "yXMrw79LPgu78EJsfGGYpm6fXKc1EMnQ49", + "transactions": [ + "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840" + ], + "balanceSat": 59999753, + "unconfirmedBalanceSat": 0, + "utxos": { + "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840-1": { + "satoshis": 59999753, + "script": "76a914791e51fff6554c18216c83d9ca81cf30cc66aff388ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/1'/1/1": { + "path": "m/44'/1'/1'/1/1", + "index": 1, + "address": "yh6Hcyipdvp6WJpQxjNbaXP4kzPQUJpY3n", + "transactions": [ + "6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135" + ], + "balanceSat": 49999753, + "unconfirmedBalanceSat": 0, + "utxos": { + "6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135-1": { + "satoshis": 49999753, + "script": "76a914e3dd87e2dd2080c854d0c90abae96d985ae8902288ac" + } + }, + "fetchedLast": 0, + "used": true + }, + "m/44'/1'/1'/1/2": { + "path": "m/44'/1'/1'/1/2", + "index": 2, + "address": "yNphpXuaTZRpU9FBh2W7NkUYcr3kBDE8me", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/3": { + "path": "m/44'/1'/1'/1/3", + "index": 3, + "address": "yXFppDT59xYD41mT2pmAdnvr7aZEFdgdrN", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/4": { + "path": "m/44'/1'/1'/1/4", + "index": 4, + "address": "yeKGAiiEHBGRujvLoYewA77jDDpeDamxvF", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/5": { + "path": "m/44'/1'/1'/1/5", + "index": 5, + "address": "yaxTG66CVzKgHhHZXojRHC9ztLTvz3fwdT", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/6": { + "path": "m/44'/1'/1'/1/6", + "index": 6, + "address": "yYw6qU7dwGoELZkSTj3oSKRpM4U8qTMc1U", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/7": { + "path": "m/44'/1'/1'/1/7", + "index": 7, + "address": "yQE2MksEnSfbeNre19oja9Jj8tvpj64C5a", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/8": { + "path": "m/44'/1'/1'/1/8", + "index": 8, + "address": "yaRnvHo8oLvVmv46vMj5XPbDJouQSnmcLT", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/9": { + "path": "m/44'/1'/1'/1/9", + "index": 9, + "address": "yj5ofWf2uYQQkSavYm2WXgu1QkaZCyP3Cm", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/10": { + "path": "m/44'/1'/1'/1/10", + "index": 10, + "address": "yUCjGmEwrHJwNDrE1o2rMre6MkSbiE6yz7", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/11": { + "path": "m/44'/1'/1'/1/11", + "index": 11, + "address": "yfJzd1nE2rEqz5XEurD6vs4ykizwmw9xTv", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/12": { + "path": "m/44'/1'/1'/1/12", + "index": 12, + "address": "yUk8U3jRZMHKVTa1eFDEtZpa1G4E13FP4d", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/13": { + "path": "m/44'/1'/1'/1/13", + "index": 13, + "address": "yMr59YWQFCADq4FbWrtxDUtMwwshSrmAyK", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/14": { + "path": "m/44'/1'/1'/1/14", + "index": 14, + "address": "yetSehBupzGS9yps5ogqARUGmTMAs2xVcQ", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/15": { + "path": "m/44'/1'/1'/1/15", + "index": 15, + "address": "yNcESKLwriNrhM6EyoSpZEXrzdY3uht92T", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/16": { + "path": "m/44'/1'/1'/1/16", + "index": 16, + "address": "yN2FihGU7KdaEspp39bKrhsHypeyeYzoM2", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/17": { + "path": "m/44'/1'/1'/1/17", + "index": 17, + "address": "yirpWLxHuhwFzA6LfUPKUh1Ke9RB9BUjit", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/18": { + "path": "m/44'/1'/1'/1/18", + "index": 18, + "address": "yVDN66vvdshWdNzhUaQNB6xExAHkzs1zj8", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/19": { + "path": "m/44'/1'/1'/1/19", + "index": 19, + "address": "yPzofnEhRVfDisL2nCUJtAoSHkuyMirHZS", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/20": { + "path": "m/44'/1'/1'/1/20", + "index": 20, + "address": "yM7Smr8HEhbRW4CXga898brATrJSsk1QUh", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + }, + "m/44'/1'/1'/1/21": { + "path": "m/44'/1'/1'/1/21", + "index": 21, + "address": "yUprvaE5KrKVPgABmGtxba9MmZqDzKQHom", + "transactions": [], + "balanceSat": 0, + "unconfirmedBalanceSat": 0, + "utxos": {}, + "fetchedLast": 0, + "used": false + } + }, + "misc": {} + } + } + }, + "transactions": { + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8": { + "hash": "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8", + "version": 3, + "inputs": [ + { + "prevTxId": "1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "4830450221008f3e0a1b59bd4072242fafbe994fb264ca3751480628755585de32e7a828b13902205ed37762b260f9ad8ef641084da938b8bd6dd2df0f7d8dca91e7c0ac0d31f4610121039ac6241be1c1f328522f0cd183ccbe78c10ad74d4535bec40f5e8fe082633d3c", + "scriptString": "72 0x30450221008f3e0a1b59bd4072242fafbe994fb264ca3751480628755585de32e7a828b13902205ed37762b260f9ad8ef641084da938b8bd6dd2df0f7d8dca91e7c0ac0d31f46101 33 0x039ac6241be1c1f328522f0cd183ccbe78c10ad74d4535bec40f5e8fe082633d3c" + }, + { + "prevTxId": "7d1b78157f9f2238669f260d95af03aeefc99577ff0cddb91b3e518ee557a2fd", + "outputIndex": 0, + "sequenceNumber": 4294967295, + "script": "47304402203773f77a278f4039caa7024373bd5ac8002c12f88ac20b1ac1480ed616c1b561022054811f33ba1403e1fa38839994e97bb4c6540b78b1a2a2d102cccd2f218cd99e012103d59b76bb58143cf8de0ff52007242f7af15f1e6943c40d4d5d5fc5e0aa5141ae", + "scriptString": "71 0x304402203773f77a278f4039caa7024373bd5ac8002c12f88ac20b1ac1480ed616c1b561022054811f33ba1403e1fa38839994e97bb4c6540b78b1a2a2d102cccd2f218cd99e01 33 0x03d59b76bb58143cf8de0ff52007242f7af15f1e6943c40d4d5d5fc5e0aa5141ae" + }, + { + "prevTxId": "a43845e580ad01f31bc06ce47ab39674e40316c4c6b765b6e54d6d35777ef456", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "4730440220762743c1cb769a51f6f5e600da17c35cf6ed9d45315e96c96fb203d743118b15022062d5c03716c6a6a9a1bbe9eb139cc500778ef3bf00aa21724e83391edcfcf20b012103e98d3cc012bb72006634b098d678afac500a1b3d3a430ac972075b5fb7153d87", + "scriptString": "71 0x30440220762743c1cb769a51f6f5e600da17c35cf6ed9d45315e96c96fb203d743118b15022062d5c03716c6a6a9a1bbe9eb139cc500778ef3bf00aa21724e83391edcfcf20b01 33 0x03e98d3cc012bb72006634b098d678afac500a1b3d3a430ac972075b5fb7153d87" + }, + { + "prevTxId": "d37b6c7dd449d605bea9997af8bbeed2f3fbbcb23a4068b1f1ad694db801912d", + "outputIndex": 0, + "sequenceNumber": 4294967295, + "script": "483045022100d31090741004486f2cb473eb046d058e63f12ff9e101a2f2028d5f0e3371499d022057f7b3f0375590d3961cf8b51bde61a830bb4f29b07d7e48132de7d292306193012103f4991317c773fbb37756fb222909c8bbd44e53f3c4e4f3060edb3df7d8549b24", + "scriptString": "72 0x3045022100d31090741004486f2cb473eb046d058e63f12ff9e101a2f2028d5f0e3371499d022057f7b3f0375590d3961cf8b51bde61a830bb4f29b07d7e48132de7d29230619301 33 0x03f4991317c773fbb37756fb222909c8bbd44e53f3c4e4f3060edb3df7d8549b24" + }, + { + "prevTxId": "eb1a7fc8e3b43d3021653b1176f8f9b41e9667d05b65ee225d14c149a5b14f77", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "473044022064f3b9869041239074dadae73c2d0e78abf100d89f88d4ba33b7b3b83eaf87010220608079322bd051ac7900a3bb96d3bc694f2b8d65850bcfc043ce51e6367c654801210332d0d14a1a28c90c1149b691a675e26f4c8b485b8dcc3624280a9fdc954ef081", + "scriptString": "71 0x3044022064f3b9869041239074dadae73c2d0e78abf100d89f88d4ba33b7b3b83eaf87010220608079322bd051ac7900a3bb96d3bc694f2b8d65850bcfc043ce51e6367c654801 33 0x0332d0d14a1a28c90c1149b691a675e26f4c8b485b8dcc3624280a9fdc954ef081" + } + ], + "outputs": [ + { + "satoshis": 1777100000, + "script": "76a914fa49fe511c437a0d4ec01050184bd2d6538b3f0888ac" + }, + { + "satoshis": 99170, + "script": "76a91414dfbdcfb48babe7127fa0ee90339c33a46aeda288ac" + } + ], + "nLockTime": 0 + }, + "1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b": { + "hash": "1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b", + "version": 2, + "inputs": [ + { + "prevTxId": "6a7449eb8df9fb52864be469252f3a96405642817e3618f4696afc3869874d19", + "outputIndex": 0, + "sequenceNumber": 4294967294, + "script": "483045022100fc497d765125566b303738841fb04c953fa286e4f531f5fca21df8c2bbdb0b0f02207a57b073f934b47bb1d1b83bb7fedf7a2d52061ea8107d0362d7c2bedfe4b7d401210200669c7e5dd728b676c2c1163ddcfa88e7cd4f01d12f01188b6b32c399c008cc", + "scriptString": "72 0x3045022100fc497d765125566b303738841fb04c953fa286e4f531f5fca21df8c2bbdb0b0f02207a57b073f934b47bb1d1b83bb7fedf7a2d52061ea8107d0362d7c2bedfe4b7d401 33 0x0200669c7e5dd728b676c2c1163ddcfa88e7cd4f01d12f01188b6b32c399c008cc" + } + ], + "outputs": [ + { + "satoshis": 46810176, + "script": "76a914802950915c17f11b1a677dd7cae5101e376fb34888ac" + }, + { + "satoshis": 729210000, + "script": "76a914daf40881fda36848da6cc430dcbec6da3ea421b088ac" + } + ], + "nLockTime": 558035 + }, + "6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135": { + "hash": "6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135", + "version": 3, + "inputs": [ + { + "prevTxId": "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840", + "outputIndex": 0, + "sequenceNumber": 4294967295, + "script": "483045022100eef13b38a771924b1429b119ef27494fe764cf1a7b12962462c4934ba15dd426022037097eaf74e3f3b547bf5e67451c7bf1e2e2a4b7b205850b1315bc4ab983fdf2012103f376b41c9e9ebc3131e33d4de127c57c1bf3ca88f81845595c44f9ac46122677", + "scriptString": "72 0x3045022100eef13b38a771924b1429b119ef27494fe764cf1a7b12962462c4934ba15dd426022037097eaf74e3f3b547bf5e67451c7bf1e2e2a4b7b205850b1315bc4ab983fdf201 33 0x03f376b41c9e9ebc3131e33d4de127c57c1bf3ca88f81845595c44f9ac46122677" + } + ], + "outputs": [ + { + "satoshis": 1150000000, + "script": "76a914f3a39f8266812baa084890d02fc489f2aee8075a88ac" + }, + { + "satoshis": 49999753, + "script": "76a914e3dd87e2dd2080c854d0c90abae96d985ae8902288ac" + } + ], + "nLockTime": 0 + }, + "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841": { + "hash": "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841", + "version": 3, + "inputs": [ + { + "prevTxId": "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533", + "outputIndex": 1, + "sequenceNumber": 4294967295, + "script": "47304402204de0c38c97e07cddaa0da91563b9ae7620c593c18fe146dde8429b662e542b4902203eeb557d6553dbac0d4f813c07d5ad6ecdd9c27e2e228aaba8c727943676f62b0121038ada8b4de6d21a29ab12401e70d7f44566dbd224a056c857f273b48adf8b0cd2", + "scriptString": "71 0x304402204de0c38c97e07cddaa0da91563b9ae7620c593c18fe146dde8429b662e542b4902203eeb557d6553dbac0d4f813c07d5ad6ecdd9c27e2e228aaba8c727943676f62b01 33 0x038ada8b4de6d21a29ab12401e70d7f44566dbd224a056c857f273b48adf8b0cd2" + }, + { + "prevTxId": "6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135", + "outputIndex": 0, + "sequenceNumber": 4294967295, + "script": "483045022100b9a1ff2866f2795fead698f7b16f26c42cc91ef4efda6203e147d8fe910b31cb02203c9c846cea5efa369f2f0e015952bd5992a884a0d79b2c237bcaa842a83f0efa0121033f532214f69c414bc1742367df5cd1195c64a5ee08455d0aad17f6de72e9eaad", + "scriptString": "72 0x3045022100b9a1ff2866f2795fead698f7b16f26c42cc91ef4efda6203e147d8fe910b31cb02203c9c846cea5efa369f2f0e015952bd5992a884a0d79b2c237bcaa842a83f0efa01 33 0x033f532214f69c414bc1742367df5cd1195c64a5ee08455d0aad17f6de72e9eaad" + } + ], + "outputs": [ + { + "satoshis": 1000000000, + "script": "76a9140d2a064dc57ccd2270a436a871f277bbb7b9ca2088ac" + }, + { + "satoshis": 159999359, + "script": "76a914e9c12479daba9d989cedba69adb56a5a50fe500288ac" + } + ], + "nLockTime": 0 + }, + "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533": { + "hash": "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533", + "version": 3, + "inputs": [ + { + "prevTxId": "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5", + "outputIndex": 0, + "sequenceNumber": 4294967295, + "script": "4730440220283fd42353767188532db4a4f1c3d0a9e96e313196ae1310af6d3006c7aa64ff022027fa50cf065c096f146e00516cb3e28a9bb387a6cf1103aae0592d5c882d25e5012102ba0588ffd3c838b715d7c79bcf1cff2ba69befd5ea52aa3474d66f094536cac0", + "scriptString": "71 0x30440220283fd42353767188532db4a4f1c3d0a9e96e313196ae1310af6d3006c7aa64ff022027fa50cf065c096f146e00516cb3e28a9bb387a6cf1103aae0592d5c882d25e501 33 0x02ba0588ffd3c838b715d7c79bcf1cff2ba69befd5ea52aa3474d66f094536cac0" + } + ], + "outputs": [ + { + "satoshis": 1260000000, + "script": "76a914838112cc6c85e074aa7f373e942c9f5240c3e13a88ac" + }, + { + "satoshis": 9999753, + "script": "76a914f728c15b9a5fe4e6d7b6ed74b323e23f5c6e303f88ac" + } + ], + "nLockTime": 0 + }, + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5": { + "hash": "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5", + "version": 3, + "inputs": [ + { + "prevTxId": "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8", + "outputIndex": 0, + "sequenceNumber": 4294967295, + "script": "4730440220442af7402fad5756cb4bd1890023369c9b4f63129cc2b6020b3e610cfe0f05f502206d90f315d3467d86b71871b484ab1dfdfdc7fb91cc190f35eb84c34d40490d6b012102ba0588ffd3c838b715d7c79bcf1cff2ba69befd5ea52aa3474d66f094536cac0", + "scriptString": "71 0x30440220442af7402fad5756cb4bd1890023369c9b4f63129cc2b6020b3e610cfe0f05f502206d90f315d3467d86b71871b484ab1dfdfdc7fb91cc190f35eb84c34d40490d6b01 33 0x02ba0588ffd3c838b715d7c79bcf1cff2ba69befd5ea52aa3474d66f094536cac0" + } + ], + "outputs": [ + { + "satoshis": 1270000000, + "script": "76a914fa49fe511c437a0d4ec01050184bd2d6538b3f0888ac" + }, + { + "satoshis": 400000000, + "script": "76a914e922f6420544f1be0cb593c10535cc3469198bc888ac" + }, + { + "satoshis": 107099720, + "script": "76a91404a791e67467246c3c0a003007793160387de54288ac" + } + ], + "nLockTime": 0 + }, + "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840": { + "hash": "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840", + "version": 3, + "inputs": [ + { + "prevTxId": "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533", + "outputIndex": 0, + "sequenceNumber": 4294967295, + "script": "483045022100b996d726d224a762acf8ab3e37c085e796b44960b8e9933571ac57750e8ed05102201c6a36d72f16140d6a152be40add102d95a4ac5b177d300b10c277859690a859012103b5614f077d750a1eaffb23ca188dbcc7e267f4b8ffdedf81cdf970643027191b", + "scriptString": "72 0x3045022100b996d726d224a762acf8ab3e37c085e796b44960b8e9933571ac57750e8ed05102201c6a36d72f16140d6a152be40add102d95a4ac5b177d300b10c277859690a85901 33 0x03b5614f077d750a1eaffb23ca188dbcc7e267f4b8ffdedf81cdf970643027191b" + } + ], + "outputs": [ + { + "satoshis": 1200000000, + "script": "76a91414b05906daab037707927bc6c83900d5dbf2849688ac" + }, + { + "satoshis": 59999753, + "script": "76a914791e51fff6554c18216c83d9ca81cf30cc66aff388ac" + } + ], + "nLockTime": 0 + } + }, + "transactionsMetadata": { + "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8": { + "blockHash": "00000084b4d9e887a6ad3f37c576a17d79c35ec9301e55210eded519e8cdcd3a", + "height": 558102, + "instantLocked": true, + "chainLocked": true + }, + "1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b": { + "blockHash": "00000c1e4556add15119392ed36ec6af2640569409abfa23a9972bc3be1b3717", + "height": 558036, + "instantLocked": true, + "chainLocked": true + }, + "6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135": { + "blockHash": "000000444b3f2f02085f8befe72da5442c865c290658766cf935e1a71a4f4ba7", + "height": 558242, + "instantLocked": true, + "chainLocked": true + }, + "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841": { + "blockHash": "000001f9c5de4d2b258a975bfbf7b9a3346890af6389512bea3cb6926b9be330", + "height": 558246, + "instantLocked": true, + "chainLocked": true + }, + "6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533": { + "blockHash": "000000dffb05c071a8c05082a475b7ce9c1e403f3b89895a6c448fe08535a5f5", + "height": 558230, + "instantLocked": true, + "chainLocked": true + }, + "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5": { + "blockHash": "000001953ea0bbb8ad04a9a1a2a707fef207ad22a712d7d3c619f0f9b63fa98c", + "height": 558229, + "instantLocked": true, + "chainLocked": true + }, + "9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840": { + "blockHash": "0000016fb685b4b1efed743d2263de34a9f8323ed75e732654b1b951c5cb4dde", + "height": 558236, + "instantLocked": true, + "chainLocked": true + } + }, + "chains": { + "testnet": { + "name": "testnet", + "blockHeaders": { + "0000005acb5b0e367b6d85401f02635d29f508eda382c5e512464c40538ae267": { + "hash": "0000005acb5b0e367b6d85401f02635d29f508eda382c5e512464c40538ae267", + "version": 536870912, + "prevHash": "000001283c0c89e9ddacfb1ccb2dcb0ac7e003d1522a7a56c1c15b5c84d62b3d", + "merkleRoot": "cb51759ad1f65ebcaa105faf46db31f67a451d3509f18611a70920b9170eeb7a", + "time": 1629503032, + "bits": 503439293, + "nonce": 34652 + }, + "00000084b4d9e887a6ad3f37c576a17d79c35ec9301e55210eded519e8cdcd3a": { + "hash": "00000084b4d9e887a6ad3f37c576a17d79c35ec9301e55210eded519e8cdcd3a", + "version": 536870912, + "prevHash": "000000e25330f9f5cae8d79273f97b16fd8457f5801f2bd19ca3df3017a5c8b0", + "merkleRoot": "f6d844dbd8df49168fe66c6b4a7845288131703ed7f7d5f35d1510c82f51439b", + "time": 1629216608, + "bits": 503417452, + "nonce": 43850 + }, + "00000c1e4556add15119392ed36ec6af2640569409abfa23a9972bc3be1b3717": { + "hash": "00000c1e4556add15119392ed36ec6af2640569409abfa23a9972bc3be1b3717", + "version": 536870912, + "prevHash": "0000016e1c01f52517a6796f2e5d77060e886f87771facb2e6a4ac82fdea8f63", + "merkleRoot": "38a75a5395065be0b7df03bc8877ec9f2913ea08ef99533c1320ffdc296ccf5e", + "time": 1629207543, + "bits": 504296820, + "nonce": 26261 + }, + "000000444b3f2f02085f8befe72da5442c865c290658766cf935e1a71a4f4ba7": { + "hash": "000000444b3f2f02085f8befe72da5442c865c290658766cf935e1a71a4f4ba7", + "version": 536870912, + "prevHash": "0000020c7bb8ea7672145e3c43dfe551c576a2e200422612a214089c6a98b331", + "merkleRoot": "f75e7f378cd1ff680f61bce4796dd1dfd10d2d2214924bd90ddb04633959f890", + "time": 1629236158, + "bits": 503456083, + "nonce": 9245 + }, + "000001f9c5de4d2b258a975bfbf7b9a3346890af6389512bea3cb6926b9be330": { + "hash": "000001f9c5de4d2b258a975bfbf7b9a3346890af6389512bea3cb6926b9be330", + "version": 536870912, + "prevHash": "000001d68bb423abe9208be1f8b68d490b539f7888859c6e6d0a03c610378e47", + "merkleRoot": "a9b98a2637c9f5009257d77e28743b5346cffc37dd4c4a760e0818dcf242fa3f", + "time": 1629237076, + "bits": 503463552, + "nonce": 55547 + }, + "000000dffb05c071a8c05082a475b7ce9c1e403f3b89895a6c448fe08535a5f5": { + "hash": "000000dffb05c071a8c05082a475b7ce9c1e403f3b89895a6c448fe08535a5f5", + "version": 536870912, + "prevHash": "000001953ea0bbb8ad04a9a1a2a707fef207ad22a712d7d3c619f0f9b63fa98c", + "merkleRoot": "3c77db7725a750f501edcbf3d3b05960a4b338535272db1db90a3202181adbd1", + "time": 1629234873, + "bits": 503448141, + "nonce": 27571 + }, + "000001953ea0bbb8ad04a9a1a2a707fef207ad22a712d7d3c619f0f9b63fa98c": { + "hash": "000001953ea0bbb8ad04a9a1a2a707fef207ad22a712d7d3c619f0f9b63fa98c", + "version": 536870912, + "prevHash": "000000fec97ba0de2aad058a2b248f0d090512b32751c31bd751d1489119ca7d", + "merkleRoot": "736dbf380e632635c2e695640ceb86b7d4ce36c727120582f3ed6d4e03617fb3", + "time": 1629234474, + "bits": 503450094, + "nonce": 60270 + }, + "0000016fb685b4b1efed743d2263de34a9f8323ed75e732654b1b951c5cb4dde": { + "hash": "0000016fb685b4b1efed743d2263de34a9f8323ed75e732654b1b951c5cb4dde", + "version": 536870912, + "prevHash": "0000017565c76a51042a5ec346de2dd13876ed64ac3ffeb37a7dab7c219d9c30", + "merkleRoot": "c28ee025927c6d26ecbb839120f0571f61f98d86bb84200910e1e010a5132237", + "time": 1629235557, + "bits": 503454209, + "nonce": 27170 + } + }, + "mappedBlockHeaderHeights": { + "560133": "0000016fb685b4b1efed743d2263de34a9f8323ed75e732654b1b951c5cb4dde" + }, + "blockHeight": 560133 + } + }, + "instantLocks": {}, + "syncOptions": { "skipSynchronizationBeforeHeight": 558030 } +} diff --git a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/transactions-with-metadata.json b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/transactions-with-metadata.json new file mode 100644 index 00000000000..2e6847d4b7c --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/transactions-with-metadata.json @@ -0,0 +1,101 @@ +[ + [ + "0200000001194d876938fc6a69f418367e81425640963a2f2569e44b8652fbf98deb49746a000000006b483045022100fc497d765125566b303738841fb04c953fa286e4f531f5fca21df8c2bbdb0b0f02207a57b073f934b47bb1d1b83bb7fedf7a2d52061ea8107d0362d7c2bedfe4b7d401210200669c7e5dd728b676c2c1163ddcfa88e7cd4f01d12f01188b6b32c399c008ccfeffffff024044ca02000000001976a914802950915c17f11b1a677dd7cae5101e376fb34888ac90dc762b000000001976a914daf40881fda36848da6cc430dcbec6da3ea421b088acd3830800", + { + "blockHash": "00000c1e4556add15119392ed36ec6af2640569409abfa23a9972bc3be1b3717", + "height": 558036, + "isInstantLocked": true, + "isChainLocked": true + } + ], + [ + "02000000024f5ccb10d1762b155b25a32c1242f72e506ead7dcdffebbb9a57c450eff24306000000006a473044022006d0f91fbc789475f4bc545901b069a5baf657b36914638b7847e61c6ce508a0022061cc6488a5e8e7d142acf015d9447ea2c37b2c44f7ce9822b2b3421e6f200111012103f7626b79771dc4e1928ffbb407aa32f7089bd8deebb7b7393d28397b504f3002feffffff07f289648f6f097dd46857c15d64576658b9f2c693a1472eb8beaa21b21ec020010000006a47304402207ac2b0c2ea3c073db24d893b533d1d5753f35a85c00971e8d0a1d7fdd77c1ffb0220024b43bdd62ee96189e3da40c5725d771c6cb8df0909826538f558fb32c46cf901210308b60306848cbb551d800192ddecfc07bd6cb34eb23df4c53d840a07d1db6e0cfeffffff02f6fc1a00000000001976a9147d03641b70a1883b600f9646081c8bf626504ac288ac10891132000000001976a9140b348dfe637f57295943cfdc2b5c65c79c0da6ba88ac72810800", + { + "blockHash": "00000221952c2a60adcb929de837f659308cb5c6bb7783016479381fb550fbad", + "height": 557481, + "isInstantLocked": true, + "isChainLocked": true + } + ], + [ + "0200000007fd1ef99bc1edb4ab93ba74309da788e4ac460975733f02936a6321620d2a8011000000006b483045022100dfb220a840d597179abdf49692ad64c1c0da785041975b00aee03c9625639cf202204d06eade5cca19fab1e10b1d6e1b67c77626a0e88bb4d5f61bd57293b4b64217012102295ecb812ccf52deaf304bebfe3a59a644f05bac81241ea1e3a2f8750064cbf6feffffff694226ee65ea29ba7ec3e5448464c16f11d5b7564f6b3b5d0425a4c751389519000000006b483045022100beff3263b7c99720e99af9ec146c818701efb0130603f1570f427b74aef8521802202e660bb9f7ea156f91addd5fe47cbd2c2bf388cc6e1eff3a39adffd89d26d346012102c33942799f7cbf4a7d12f1b3e52cb80cc4de083b997d3e63915df9973d5bce2afeffffff963e9964a8394a07dde1dbb1e8b34af0f194def0db77fa69229030fbade2c82d000000006b483045022100ff67776932e7a32520aa131f76bdfd6737650ad3b11edbdf466cca83f691b0e60220633bcbedebacffd53ceb7e9cdbd47928d7c2849f49ac1f8efb9f384c1a4ee46301210371c0bc42e08de059a8829730abb16f3d40cff87e5ad85d65c4a0a949d9c4b524feffffff1fe16550125c3398c25d97308d7ab89bdd3d27a1589c78e97c4823c92723cf40000000006b483045022100cca348c7ab16fac28b3bba502be54a9e3766b7da9821a90605f370b75840569702207d082510aa493988e09da046355b018781718208f8a954e14ea33d608ae59625012103699b9402e109ed9d0c67c6a45be5cf5f1236c44bb9fc4b07a2f3392ba0b64172feffffffce251fb3d87d7df03b0fbd720ce5425ab0ea86d96a4ec658463d9507928bb34b000000006a47304402203ae564ff74b08b1f96bf857f51448434418d747a02039ec1ee109a4f5d8e8106022072f8769bd175416d22f44011f7e67aec301f08573c9937be7e4a09c394c7396601210311bae874933a4503a61d1c8c2e5b57b1a278d28d4892af4bd79ab8a731495265feffffffaf2dfef80d4a1f77c75dfc74e3769d8526c66c467c859b16b3439ab213851eb2000000006a47304402200b49b7059064efb57df453dc2d20002f09b5266bc825760ef81624771f13920802200782616b8c4fb7b5eff94fdf865e6ddc4530d3932b97fdc3a747e8c451f0314c012103a94131f28f8efd67f47f2496ff6e8d9069a3a7df97202a33e90e16f257d03729feffffff344fef05224cca98a16f87515041df61cbca948518761021a286d1a76e2bfdd6000000006a47304402202a24d1123775641269c6f748d3e4dad08a682e4e334a9b73c7df84f6c22e8e7d022022c0cc2225d3f14cb58a6fb3e4bf23c0f33252d9040a9ca9ef66eb17742a476f01210347301de4c9ba7f46b0f27cb82ae70a73749821e2951d3c87c2f0d56648635d1cfeffffff0251d21b00000000001976a91440ca54360086cc0fbd69d862db58ab2b6d22805888ace058340b000000001976a914538da44e7136cc994023d89a7b4b3d02ac0e573988acf1790800", + { + "blockHash": "000001deee9f99e8219a9abcaaea135dbaae8a9b0f1ea214e6b6a37a5c5b115d", + "height": 555506, + "isInstantLocked": true, + "isChainLocked": true + } + ], + [ + "03000000051bacf8f5091d903e647e46672bba1f3a6f2d120f573868958b9105c1ed35bb1c010000006b4830450221008f3e0a1b59bd4072242fafbe994fb264ca3751480628755585de32e7a828b13902205ed37762b260f9ad8ef641084da938b8bd6dd2df0f7d8dca91e7c0ac0d31f4610121039ac6241be1c1f328522f0cd183ccbe78c10ad74d4535bec40f5e8fe082633d3cfffffffffda257e58e513e1bb9dd0cff7795c9efae03af950d269f6638229f7f15781b7d000000006a47304402203773f77a278f4039caa7024373bd5ac8002c12f88ac20b1ac1480ed616c1b561022054811f33ba1403e1fa38839994e97bb4c6540b78b1a2a2d102cccd2f218cd99e012103d59b76bb58143cf8de0ff52007242f7af15f1e6943c40d4d5d5fc5e0aa5141aeffffffff56f47e77356d4de5b665b7c6c41603e47496b37ae46cc01bf301ad80e54538a4010000006a4730440220762743c1cb769a51f6f5e600da17c35cf6ed9d45315e96c96fb203d743118b15022062d5c03716c6a6a9a1bbe9eb139cc500778ef3bf00aa21724e83391edcfcf20b012103e98d3cc012bb72006634b098d678afac500a1b3d3a430ac972075b5fb7153d87ffffffff2d9101b84d69adf1b168403ab2bcfbf3d2eebbf87a99a9be05d649d47d6c7bd3000000006b483045022100d31090741004486f2cb473eb046d058e63f12ff9e101a2f2028d5f0e3371499d022057f7b3f0375590d3961cf8b51bde61a830bb4f29b07d7e48132de7d292306193012103f4991317c773fbb37756fb222909c8bbd44e53f3c4e4f3060edb3df7d8549b24ffffffff774fb1a549c1145d22ee655bd067961eb4f9f876113b6521303db4e3c87f1aeb010000006a473044022064f3b9869041239074dadae73c2d0e78abf100d89f88d4ba33b7b3b83eaf87010220608079322bd051ac7900a3bb96d3bc694f2b8d65850bcfc043ce51e6367c654801210332d0d14a1a28c90c1149b691a675e26f4c8b485b8dcc3624280a9fdc954ef081ffffffff02e064ec69000000001976a914fa49fe511c437a0d4ec01050184bd2d6538b3f0888ac62830100000000001976a91414dfbdcfb48babe7127fa0ee90339c33a46aeda288ac00000000", + { + "blockHash": "00000084b4d9e887a6ad3f37c576a17d79c35ec9301e55210eded519e8cdcd3a", + "height": 558102, + "isInstantLocked": true, + "isChainLocked": true + } + ], + [ + "03000000014e74eb53f4b1fa08e1fa02788005b8ef9f70929b3f0668fcb9dc4093b7385af3010000006a473044022071fcd620db9f245fe91c44e9b298ee5a718c2be728958f9e707dd1f785fb05e002207b05d45e4ab78c83f9117032a109d6afa37883ca5b479d2b3110fb0d773d23df0121033883dff35aeac917a26d2cbfb59a365c7ff83256a49c4d6aed8f1c0684605c40ffffffff0280969800000000001976a914cb579d4aa777c3583f61f28425ae3fea5b60d39088ac2296bf1f000000001976a914500ddabedf00296b40842cea428951d57331d5e088ac00000000", + { + "blockHash": "000000b6006c758eda23ec7e2a640a0bf2c6a0c44827be216faff6bf4fd388e8", + "height": 555507, + "isInstantLocked": true, + "isChainLocked": true + } + ], + [ + "03000000012d9101b84d69adf1b168403ab2bcfbf3d2eebbf87a99a9be05d649d47d6c7bd3010000006a47304402201cc3d6887d5161eba36a5e6fb1ccd8e8f9eeda7fe95b4fb0a1accb99eeba0223022040d0df81fde8f59c807e541ca5bcfc9d7450f76657aeb44c708fa7d65b7d58410121038cdae47fceb5b117cd3ef5bdf8c9f2a83679a9105d012095762067bdb2351ceaffffffff0280969800000000001976a914e00939d2ec2f885f5e7dc7b9f5b06dcf868d0c4b88acabfe261f000000001976a914f03286cbb7954ea6affa9654af6cfe1210dd0c6288ac00000000", + { + "blockHash": "0000012cf6377c6cf2b317a4deed46573c09f04f6880dca731cc9ccea6691e19", + "height": 555508, + "isInstantLocked": true, + "isChainLocked": true + } + ], + [ + "0300000001f8eea18afa702989208916a4fb8ce7ed49b5d915257f6f3dd977f54b41a930f2000000006a4730440220442af7402fad5756cb4bd1890023369c9b4f63129cc2b6020b3e610cfe0f05f502206d90f315d3467d86b71871b484ab1dfdfdc7fb91cc190f35eb84c34d40490d6b012102ba0588ffd3c838b715d7c79bcf1cff2ba69befd5ea52aa3474d66f094536cac0ffffffff0380a9b24b000000001976a914fa49fe511c437a0d4ec01050184bd2d6538b3f0888ac0084d717000000001976a914e922f6420544f1be0cb593c10535cc3469198bc888ac48366206000000001976a91404a791e67467246c3c0a003007793160387de54288ac00000000", + { + "blockHash": "000001953ea0bbb8ad04a9a1a2a707fef207ad22a712d7d3c619f0f9b63fa98c", + "height": 558229, + "isInstantLocked": true, + "isChainLocked": true + } + ], + [ + "0300000001b51d5a6f5c7a680bce489e6f5a9b176ac85c49f10db4798867c7d1eb2036fbc3000000006a4730440220283fd42353767188532db4a4f1c3d0a9e96e313196ae1310af6d3006c7aa64ff022027fa50cf065c096f146e00516cb3e28a9bb387a6cf1103aae0592d5c882d25e5012102ba0588ffd3c838b715d7c79bcf1cff2ba69befd5ea52aa3474d66f094536cac0ffffffff0200131a4b000000001976a914838112cc6c85e074aa7f373e942c9f5240c3e13a88ac89959800000000001976a914f728c15b9a5fe4e6d7b6ed74b323e23f5c6e303f88ac00000000", + { + "blockHash": "000000dffb05c071a8c05082a475b7ce9c1e403f3b89895a6c448fe08535a5f5", + "height": 558230, + "isInstantLocked": true, + "isChainLocked": true + } + ], + [ + "030000000140d85c0bec59ac6ba3892164ef9816b45f5d105769bcae339af9a7874ad4d39c000000006b483045022100eef13b38a771924b1429b119ef27494fe764cf1a7b12962462c4934ba15dd426022037097eaf74e3f3b547bf5e67451c7bf1e2e2a4b7b205850b1315bc4ab983fdf2012103f376b41c9e9ebc3131e33d4de127c57c1bf3ca88f81845595c44f9ac46122677ffffffff02809b8b44000000001976a914f3a39f8266812baa084890d02fc489f2aee8075a88ac89effa02000000001976a914e3dd87e2dd2080c854d0c90abae96d985ae8902288ac00000000", + { + "blockHash": "000000444b3f2f02085f8befe72da5442c865c290658766cf935e1a71a4f4ba7", + "height": 558242, + "isInstantLocked": true, + "isChainLocked": true + } + ], + [ + "0300000002338540c64b794f73913f39f2d42d9139ce7c9d1c0ec5317c62ab4a28d6b0376f010000006a47304402204de0c38c97e07cddaa0da91563b9ae7620c593c18fe146dde8429b662e542b4902203eeb557d6553dbac0d4f813c07d5ad6ecdd9c27e2e228aaba8c727943676f62b0121038ada8b4de6d21a29ab12401e70d7f44566dbd224a056c857f273b48adf8b0cd2ffffffff351180e1d85feb4f2ae13b224a8e630ddcaf9806f8bb3b371bcbc63880ca766f000000006b483045022100b9a1ff2866f2795fead698f7b16f26c42cc91ef4efda6203e147d8fe910b31cb02203c9c846cea5efa369f2f0e015952bd5992a884a0d79b2c237bcaa842a83f0efa0121033f532214f69c414bc1742367df5cd1195c64a5ee08455d0aad17f6de72e9eaadffffffff0200ca9a3b000000001976a9140d2a064dc57ccd2270a436a871f277bbb7b9ca2088ac7f658909000000001976a914e9c12479daba9d989cedba69adb56a5a50fe500288ac00000000", + { + "blockHash": "000001f9c5de4d2b258a975bfbf7b9a3346890af6389512bea3cb6926b9be330", + "height": 558246, + "isInstantLocked": true, + "isChainLocked": true + } + ], + [ + "0300000001338540c64b794f73913f39f2d42d9139ce7c9d1c0ec5317c62ab4a28d6b0376f000000006b483045022100b996d726d224a762acf8ab3e37c085e796b44960b8e9933571ac57750e8ed05102201c6a36d72f16140d6a152be40add102d95a4ac5b177d300b10c277859690a859012103b5614f077d750a1eaffb23ca188dbcc7e267f4b8ffdedf81cdf970643027191bffffffff02008c8647000000001976a91414b05906daab037707927bc6c83900d5dbf2849688ac09869303000000001976a914791e51fff6554c18216c83d9ca81cf30cc66aff388ac00000000", + { + "blockHash": "0000016fb685b4b1efed743d2263de34a9f8323ed75e732654b1b951c5cb4dde", + "height": 558236, + "isInstantLocked": true, + "isChainLocked": true + } + ] +] diff --git a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/wallet-store.json b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/wallet-store.json new file mode 100644 index 00000000000..6846110a1f6 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/wallet-store.json @@ -0,0 +1,6 @@ +{ + "walletId": "d6143ef4e6", + "lastKnownBlock": { + "height": 11703 + } +} diff --git a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/wallet.json b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/wallet.json new file mode 100644 index 00000000000..d5a4e8cf864 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/wallet.json @@ -0,0 +1,17 @@ +{ + "mnemonic": "apart trip dignity try point rocket damp reflect raw ten normal young", + "network": "testnet", + "store": { + "accounts": { + "m/44'/1'/0'": { + "label": null, + "path": "m/44'/1'/0'", + "network": "testnet", + "blockHeight": 558282, + "blockHash": "000001f9c5de4d2b258a975bfbf7b9a3346890af6389512bea3cb6926b9be330" + } + } + }, + "identityIds": [], + "type": "hdwallet" +} diff --git a/packages/wallet-lib/fixtures/wallets/mnemonics/during-develop-before.json b/packages/wallet-lib/fixtures/wallets/mnemonics/during-develop-before.json new file mode 100644 index 00000000000..7bbf7448a1f --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/mnemonics/during-develop-before.json @@ -0,0 +1,3 @@ +{ + "mnemonic": "during develop before curtain hazard rare job language become verb message travel" +} diff --git a/packages/wallet-lib/fixtures/wallets/mnemonics/prison-crater-purchase.json b/packages/wallet-lib/fixtures/wallets/mnemonics/prison-crater-purchase.json new file mode 100644 index 00000000000..ecd47e19610 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/mnemonics/prison-crater-purchase.json @@ -0,0 +1,3 @@ +{ + "mnemonic": "prison crater purchase explain gate pepper cash nominee enroll either gossip dune" +} diff --git a/packages/wallet-lib/karma.conf.js b/packages/wallet-lib/karma.conf.js new file mode 100644 index 00000000000..50bf01deca3 --- /dev/null +++ b/packages/wallet-lib/karma.conf.js @@ -0,0 +1,58 @@ +/* eslint-disable import/no-extraneous-dependencies */ +const webpack = require('webpack'); +const dotenvResult = require('dotenv-safe').config(); + +const karmaMocha = require('karma-mocha'); +const karmaMochaReporter = require('karma-mocha-reporter'); +const karmaChai = require('karma-chai'); +const karmaChromeLauncher = require('karma-chrome-launcher'); +const karmaSourcemapLoader = require('karma-sourcemap-loader'); +const karmaWebpack = require('karma-webpack'); + +const webpackConfig = require('./webpack.config'); + +if (dotenvResult.error) { + throw dotenvResult.error; +} + +module.exports = (config) => { + config.set({ + frameworks: ['mocha', 'chai', 'webpack'], + files: [ + 'src/test/karma/loader.js', + 'tests/functional/wallet.js', + ], + preprocessors: { + 'src/test/karma/loader.js': ['webpack', 'sourcemap'], + 'tests/functional/wallet.js': ['webpack', 'sourcemap'], + }, + webpack: { + mode: 'development', + devtool: 'inline-source-map', + plugins: [ + ...webpackConfig.plugins, + new webpack.EnvironmentPlugin( + dotenvResult.parsed, + ), + ], + resolve: webpackConfig.resolve, + }, + reporters: ['mocha'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: false, + browsers: ['ChromeHeadless'], + singleRun: false, + concurrency: Infinity, + browserNoActivityTimeout: 10 * 60 * 1000, + plugins: [ + karmaMocha, + karmaMochaReporter, + karmaChai, + karmaChromeLauncher, + karmaSourcemapLoader, + karmaWebpack, + ], + }); +}; diff --git a/packages/wallet-lib/package.json b/packages/wallet-lib/package.json new file mode 100644 index 00000000000..bc6e567ab73 --- /dev/null +++ b/packages/wallet-lib/package.json @@ -0,0 +1,92 @@ +{ + "name": "@dashevo/wallet-lib", + "version": "7.23.0-dev.4", + "description": "Light wallet library for Dash", + "main": "src/index.js", + "unpkg": "dist/wallet-lib.min.js", + "scripts": { + "build:web": "webpack --stats-error-details", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "test:unit": "nyc yarn mocha 'src/**/*.spec.js'", + "test:functional": "nyc yarn run mocha 'tests/functional/*.js'", + "test:integration": "nyc yarn run mocha 'tests/integration/**/*.spec.js'", + "test:browsers": "karma start ./karma.conf.js --single-run", + "test": "yarn run test:unit && yarn run test:integration && yarn run test:functional && yarn run test:browsers", + "prepublishOnly": "yarn run build:web" + }, + "ultra": { + "concurrent": [ + "test" + ] + }, + "files": [ + "dist", + "docs", + "examples", + "src" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/dashevo/wallet-lib.git" + }, + "keywords": [ + "cryptocurrency", + "dash", + "wallet" + ], + "author": "Dash Core Team", + "license": "MIT", + "bugs": { + "url": "https://github.com/dashevo/wallet-lib/issues" + }, + "homepage": "https://github.com/dashevo/wallet-lib#readme", + "dependencies": { + "@dashevo/dapi-client": "workspace:~", + "@dashevo/dashcore-lib": "~0.19.39", + "@dashevo/dpp": "workspace:~", + "@dashevo/grpc-common": "workspace:~", + "cbor": "^8.0.0", + "crypto-js": "^4.0.0", + "lodash": "^4.17.19", + "pbkdf2": "^3.1.1", + "setimmediate": "^1.0.5", + "winston": "^3.2.1" + }, + "devDependencies": { + "assert": "^2.0.0", + "browserify-zlib": "^0.2.0", + "buffer": "^6.0.3", + "chai": "^4.3.4", + "chai-as-promised": "^7.1.1", + "crypto-browserify": "^3.12.0", + "dotenv-safe": "^8.2.0", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.24.2", + "events": "^3.3.0", + "https-browserify": "^1.0.0", + "karma": "^6.3.4", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^3.1.0", + "karma-mocha": "^2.0.1", + "karma-mocha-reporter": "^2.2.5", + "karma-sourcemap-loader": "^0.3.7", + "karma-webpack": "^5.0.0", + "mocha": "^9.1.2", + "node-inspect-extracted": "^1.0.8", + "nyc": "^15.1.0", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "sinon": "^11.1.2", + "sinon-chai": "^3.7.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "string_decoder": "^1.3.0", + "url": "^0.11.0", + "util": "^0.12.4", + "webpack": "^5.59.1", + "webpack-cli": "^4.9.1" + } +} diff --git a/packages/wallet-lib/src/CONSTANTS.js b/packages/wallet-lib/src/CONSTANTS.js new file mode 100644 index 00000000000..e2f63b4fee4 --- /dev/null +++ b/packages/wallet-lib/src/CONSTANTS.js @@ -0,0 +1,118 @@ +const CONSTANTS = { + BIP45: 'BIP45', + BIP44: 'BIP44', + DUFFS_PER_DASH: 100000000, + BIP44_ADDRESS_GAP: 20, + // TODO : When chainlock is launched in mainnet, reduce this to 1 \0/ + SECURE_TRANSACTION_CONFIRMATIONS_NB: 6, + BIP32__ROOT_PATH: 'm', + // Livenet is 5 for Dash. + BIP44_LIVENET_ROOT_PATH: "m/44'/5'", + // All testnet coins are 1's + BIP44_TESTNET_ROOT_PATH: "m/44'/1'", + + // Livenet is 5 for Dash. + DIP9_LIVENET_ROOT_PATH: "m/9'/5'", + // All testnet coins are 1's + DIP9_TESTNET_ROOT_PATH: "m/9'/1'", + // The max amount of an UTXO to be considered too big to be used in the tx before exploring + // smaller alternatives (proportinal to tx amount). + UTXO_SELECTION_MAX_SINGLE_UTXO_FACTOR: 2, + // The minimum amount an UTXO need to contribute proportional to tx amount. + UTXO_SELECTION_MIN_TX_AMOUNT_VS_UTXO_FACTOR: 0.1, + // The maximum threshold to consider fees non-significant in relation to tx amount. + UTXO_SELECTION_MAX_FEE_VS_TX_AMOUNT_FACTOR: 0.05, + // The maximum amount to pay for using small inputs instead of one big input + // when fees are significant (proportional to how much we would pay for using that big input only) + UTXO_SELECTION_MAX_FEE_VS_SINGLE_UTXO_FEE_FACTOR: 5, + MAX_STANDARD_TX_SIZE: 100000, + MAX_P2SH_SIGOPS: 15, + COINBASE_MATURITY: 100, + // limit to how many times an unconfirmed input in a new tx can be respent + UTXO_CHAINED_SPENDING_LIMIT_FOR_TX: 25, + FEES: { + DUST_RELAY_TX_FEE: 1000, + ZERO: 0, + ECONOMIC: 500, + NORMAL: 1000, + PRIORITY: 10000, + // Fee for IS are 0.0001 * INPUTS + INSTANT_FEE_PER_INPUTS: 10000, + }, + UNCONFIRMED_TRANSACTION_STATUS_CODE: -1, + WALLET_TYPES: { + ADDRESS: 'address', + PUBLICKEY: 'publicKey', + PRIVATEKEY: 'privateKey', + // TODO: DEPRECATE. + SINGLE_ADDRESS: 'single_address', + // TODO: DEPRECATE. + HDWALLET: 'hdwallet', + HDPRIVATE: 'hdprivate', + HDPUBLIC: 'hdpublic', + }, + // List of account function and properties that can be injected in a plugin + INJECTION_LISTS: { + SAFE_FUNCTIONS: [ + 'createTransaction', + 'createTransactionFromUTXOS', + 'getUTXOS', + 'getUnusedAddress', + 'getConfirmedBalance', + 'getUnconfirmedBalance', + 'getTotalBalance', + 'broadcastTransaction', + 'importTransactions', + 'importBlockHeader', + 'getAddress', + 'fetchStatus', + 'getPlugin', + 'sign', + 'getTransactions', + 'getTransactionHistory', + 'forceRefreshAccount', + 'disconnect', + 'connect', + ], + UNSAFE_FUNCTIONS: [ + 'generateAddress', + 'getPrivateKeys', + 'injectPlugin', + ], + UNSAFE_PROPERTIES: [ + 'storage', + 'identities', + ], + SAFE_PROPERTIES: [ + 'offlineMode', + 'index', + 'BIP44PATH', + 'transport', + 'walletId', + 'walletType', + 'strategy', + 'network', + ], + }, + TRANSACTION_HISTORY_TYPES: { + RECEIVED: 'received', + SENT: 'sent', + ADDRESS_TRANSFER: 'address_transfer', + ACCOUNT_TRANSFER: 'account_transfer', + UNKNOWN: 'unknown', + }, + STORAGE: { + version: 2, + autosaveIntervalTime: 10 * 1000, + }, + TXIN_OUTPOINT_TXID_BYTES: 36, + TXIN_OUTPOINT_INDEX_BYTES: 4, + TXIN_SEQUENCE_BYTES: 4, + TXOUT_DUFFS_VALUE_BYTES: 8, + VERSION_BYTES: 4, + N_LOCKTIME_BYTES: 4, + + BLOOM_FALSE_POSITIVE_RATE: 0.0001, + NULL_HASH: '0000000000000000000000000000000000000000000000000000000000000000', +}; +module.exports = CONSTANTS; diff --git a/packages/wallet-lib/src/EVENTS.js b/packages/wallet-lib/src/EVENTS.js new file mode 100644 index 00000000000..37c73d6f3c7 --- /dev/null +++ b/packages/wallet-lib/src/EVENTS.js @@ -0,0 +1,27 @@ +module.exports = { + PREFETCHED: 'prefetched', + CREATED: 'created', + STARTED: 'started', + READY: 'ready', + CONFIRMED_BALANCE_CHANGED: 'confirmed_balance_changed', + UNCONFIRMED_BALANCE_CHANGED: 'unconfirmed_balance_changed', + BLOCKHEIGHT_CHANGED: 'blockheight_changed', + BLOCK: 'block', + TRANSACTION: 'transaction', + BLOCKHEADER: 'blockheader', + FETCHED_ADDRESS: 'FETCHED/ADDRESS', + ERROR_UPDATE_ADDRESS: 'ERROR/UPDATE_ADDRESS', + FETCHED_TRANSACTION: 'FETCHED/TRANSACTION', + FETCHED_UNCONFIRMED_TRANSACTION: 'FETCHED/UNCONFIRMED_TRANSACTION', + FETCHED_CONFIRMED_TRANSACTION: 'FETCHED/CONFIRMED_TRANSACTION', + GENERATED_ADDRESS: 'GENERATED_ADDRESS', + DISCOVERY_STARTED: 'DISCOVERY_STARTED', + CONFIGURED: 'CONFIGURED', + INITIALIZED: 'INITIALIZED', + SAVE_STATE_FAILED: 'SAVE_STATE_FAILED', + SAVE_STATE_SUCCESS: 'SAVE_STATE_SUCCESS', + REHYDRATE_STATE_FAILED: 'REHYDRATE_STATE_FAILED', + REHYDRATE_STATE_SUCCESS: 'REHYDRATE_STATE_SUCCESS', + INSTANT_LOCK: 'INSTANT_LOCK', + TX_METADATA: 'TX_METADATA', +}; diff --git a/packages/wallet-lib/src/adapters/InMem.js b/packages/wallet-lib/src/adapters/InMem.js new file mode 100644 index 00000000000..f17cb5606ae --- /dev/null +++ b/packages/wallet-lib/src/adapters/InMem.js @@ -0,0 +1,20 @@ +class InMem { + constructor() { + this.isConfig = false; + this.keys = {}; + } + + config() { + this.isConfig = true; + } + + setItem(key, item) { + this.keys[key] = item; + return item; + } + + getItem(key) { + return this.keys[key] || null; + } +} +module.exports = InMem; diff --git a/packages/wallet-lib/src/adapters/inMem.spec.js b/packages/wallet-lib/src/adapters/inMem.spec.js new file mode 100644 index 00000000000..122a74421bb --- /dev/null +++ b/packages/wallet-lib/src/adapters/inMem.spec.js @@ -0,0 +1,19 @@ +const { expect } = require('chai'); +const InMem = require('./InMem'); + +const inMem = new InMem(); + +describe('Adapter - inMem', function suite() { + this.timeout(10000); + it('should provide a config method', () => { + expect(inMem.config).to.exist; + }); + it('should set an item', () => { + const item = { item: 'item' }; + expect(inMem.setItem('toto', item)).to.deep.equal(item); + }); + it('should get an item', () => { + const item = { item: 'item' }; + expect(inMem.getItem('toto')).to.deep.equal(item); + }); +}); diff --git a/packages/wallet-lib/src/errors/BlockHeaderNotInStore.js b/packages/wallet-lib/src/errors/BlockHeaderNotInStore.js new file mode 100644 index 00000000000..182fe8e767b --- /dev/null +++ b/packages/wallet-lib/src/errors/BlockHeaderNotInStore.js @@ -0,0 +1,8 @@ +const WalletLibError = require('./WalletLibError'); + +class BlockHeaderNotInStore extends WalletLibError { + constructor(identifier) { + super(`Blockheader is not in store: ${identifier}`); + } +} +module.exports = BlockHeaderNotInStore; diff --git a/packages/wallet-lib/src/errors/CoinSelectionUnsufficientUTXOS.js b/packages/wallet-lib/src/errors/CoinSelectionUnsufficientUTXOS.js new file mode 100644 index 00000000000..df5f0c91f6a --- /dev/null +++ b/packages/wallet-lib/src/errors/CoinSelectionUnsufficientUTXOS.js @@ -0,0 +1,13 @@ +const WalletLibError = require('./WalletLibError'); + +class CoinSelectionUnsufficientUTXOS extends WalletLibError { + constructor(info) { + const getErrorMessageOf = (_info) => { + const { utxosValue, outputValue } = _info; + const diff = utxosValue - outputValue; + return `Unsufficient utxos (${utxosValue}) to cover the output : ${outputValue}. Diff : ${diff}`; + }; + super(getErrorMessageOf(info)); + } +} +module.exports = CoinSelectionUnsufficientUTXOS; diff --git a/packages/wallet-lib/src/errors/CreateTransactionError.js b/packages/wallet-lib/src/errors/CreateTransactionError.js new file mode 100644 index 00000000000..8eef2c63a7f --- /dev/null +++ b/packages/wallet-lib/src/errors/CreateTransactionError.js @@ -0,0 +1,13 @@ +const WalletLibError = require('./WalletLibError'); +const CoinSelectionUnsufficientUTXOS = require('./CoinSelectionUnsufficientUTXOS'); + +class CreateTransactionError extends WalletLibError { + constructor(e) { + if (e instanceof CoinSelectionUnsufficientUTXOS) { + super('Unsufficient funds to cover the output'); + } else { + super(e); + } + } +} +module.exports = CreateTransactionError; diff --git a/packages/wallet-lib/src/errors/IndentityIdReplaceError.js b/packages/wallet-lib/src/errors/IndentityIdReplaceError.js new file mode 100644 index 00000000000..82bfce3bb0b --- /dev/null +++ b/packages/wallet-lib/src/errors/IndentityIdReplaceError.js @@ -0,0 +1,6 @@ +const WalletLibError = require('./WalletLibError'); + +class IdentityIdReplaceError extends WalletLibError { + +} +module.exports = IdentityIdReplaceError; diff --git a/packages/wallet-lib/src/errors/InjectionErrorCannotInject.js b/packages/wallet-lib/src/errors/InjectionErrorCannotInject.js new file mode 100644 index 00000000000..26ad1077ada --- /dev/null +++ b/packages/wallet-lib/src/errors/InjectionErrorCannotInject.js @@ -0,0 +1,12 @@ +const WalletLibError = require('./WalletLibError'); + +class InjectionErrorCannotInject extends WalletLibError { + constructor(pluginName, reason) { + const getErrorMessageOf = () => `Injection of plugin : ${pluginName} impossible. + Reason : ${reason}`; + + super(getErrorMessageOf()); + } +} + +module.exports = InjectionErrorCannotInject; diff --git a/packages/wallet-lib/src/errors/InjectionErrorCannotInjectUnknownDependency.js b/packages/wallet-lib/src/errors/InjectionErrorCannotInjectUnknownDependency.js new file mode 100644 index 00000000000..203c5a43869 --- /dev/null +++ b/packages/wallet-lib/src/errors/InjectionErrorCannotInjectUnknownDependency.js @@ -0,0 +1,12 @@ +const WalletLibError = require('./WalletLibError'); + +class InjectionErrorCannotInjectUnknownDependency extends WalletLibError { + constructor(pluginName, dependencyName) { + const getErrorMessageOf = () => `Injection of plugin : ${pluginName} impossible. + Unknown Dependency ${dependencyName}`; + + super(getErrorMessageOf()); + } +} + +module.exports = InjectionErrorCannotInjectUnknownDependency; diff --git a/packages/wallet-lib/src/errors/InjectionToPluginUnallowed.js b/packages/wallet-lib/src/errors/InjectionToPluginUnallowed.js new file mode 100644 index 00000000000..6d6767f0d32 --- /dev/null +++ b/packages/wallet-lib/src/errors/InjectionToPluginUnallowed.js @@ -0,0 +1,9 @@ +const WalletLibError = require('./WalletLibError'); + +class InjectionToPluginUnallowed extends WalletLibError { + constructor(currentPluginName, injectingPluginName) { + super(`Injection of plugin : ${injectingPluginName} into ${currentPluginName} not allowed`); + } +} + +module.exports = InjectionToPluginUnallowed; diff --git a/packages/wallet-lib/src/errors/InstantLockTimeoutError.js b/packages/wallet-lib/src/errors/InstantLockTimeoutError.js new file mode 100644 index 00000000000..a2f69ee6959 --- /dev/null +++ b/packages/wallet-lib/src/errors/InstantLockTimeoutError.js @@ -0,0 +1,12 @@ +const WalletLibError = require('./WalletLibError'); + +class InstantLockTimeoutError extends WalletLibError { + /** + * @param {string} transactionHash + */ + constructor(transactionHash) { + super(`InstantLock waiting period for transaction ${transactionHash} timed out`); + } +} + +module.exports = InstantLockTimeoutError; diff --git a/packages/wallet-lib/src/errors/InvalidAddress.js b/packages/wallet-lib/src/errors/InvalidAddress.js new file mode 100644 index 00000000000..fd347b5c936 --- /dev/null +++ b/packages/wallet-lib/src/errors/InvalidAddress.js @@ -0,0 +1,8 @@ +const WalletLibError = require('./WalletLibError'); + +class InvalidAddress extends WalletLibError { + constructor(address) { + super(`Address Invalid : ${address} `); + } +} +module.exports = InvalidAddress; diff --git a/packages/wallet-lib/src/errors/InvalidAddressObject.js b/packages/wallet-lib/src/errors/InvalidAddressObject.js new file mode 100644 index 00000000000..b073e539493 --- /dev/null +++ b/packages/wallet-lib/src/errors/InvalidAddressObject.js @@ -0,0 +1,33 @@ +const is = require('../utils/is'); +const WalletLibError = require('./WalletLibError'); + +class InvalidAddressObject extends WalletLibError { + constructor(addressObject) { + const getErrorMessageOf = (addressErrors) => { + if (!is.arr(addressErrors) || addressErrors.length === 0) return false; + const err = addressErrors[0]; + return `Address should have property ${err[0]} of type ${err[1]}`; + }; + + const evaluateAddressObjectError = (addrObj) => { + const addressErrors = []; + const expectedProps = [ + ['path', 'string'], + ['address', 'addressObject'], + ]; + const handledTypeVerification = Object.keys(is); + expectedProps.forEach((prop) => { + const key = prop[0]; + const type = prop[1]; + if (handledTypeVerification.includes(type)) { + if (!is[type](addrObj[key])) { + addressErrors.push(prop); + } + } + }); + return addressErrors; + }; + super(getErrorMessageOf(evaluateAddressObjectError(addressObject))); + } +} +module.exports = InvalidAddressObject; diff --git a/packages/wallet-lib/src/errors/InvalidDashcoreTransaction.js b/packages/wallet-lib/src/errors/InvalidDashcoreTransaction.js new file mode 100644 index 00000000000..0beca37db34 --- /dev/null +++ b/packages/wallet-lib/src/errors/InvalidDashcoreTransaction.js @@ -0,0 +1,9 @@ +const WalletLibError = require('./WalletLibError'); + +class InvalidDashcoreTransaction extends WalletLibError { + constructor(tx, reason = 'A Dashcore Transaction object or valid rawTransaction is required') { + super(`${reason}: ${tx.toString()}`); + } +} + +module.exports = InvalidDashcoreTransaction; diff --git a/packages/wallet-lib/src/errors/InvalidOutput.js b/packages/wallet-lib/src/errors/InvalidOutput.js new file mode 100644 index 00000000000..f2ad02425ef --- /dev/null +++ b/packages/wallet-lib/src/errors/InvalidOutput.js @@ -0,0 +1,37 @@ +const { has } = require('lodash'); +const is = require('../utils/is'); + +const WalletLibError = require('./WalletLibError'); + +class InvalidOutput extends WalletLibError { + constructor(output) { + const getErrorMessageOf = (utxoErrors) => { + if (!is.arr(utxoErrors) || utxoErrors.length === 0) return false; + const err = utxoErrors[0]; + const txid = (has(output, 'txid')) ? output.txid : 'unknown'; + const address = (has(output, 'address')) ? output.address : 'unknown'; + return `Output txid:${txid} address: ${address} should have property ${err[0]} of type ${err[1]}`; + }; + + const evaluateUTXOObjectError = (_utxo) => { + const utxosErrors = []; + const expectedProps = [ + ['address', 'string'], + ['satoshis', 'num'], + ]; + const handledTypeVerification = Object.keys(is); + expectedProps.forEach((prop) => { + const key = prop[0]; + const type = prop[1]; + if (handledTypeVerification.includes(type)) { + if (!is[type](_utxo[key])) { + utxosErrors.push(prop); + } + } + }); + return utxosErrors; + }; + super(getErrorMessageOf(evaluateUTXOObjectError(output))); + } +} +module.exports = InvalidOutput; diff --git a/packages/wallet-lib/src/errors/InvalidRawTransaction.js b/packages/wallet-lib/src/errors/InvalidRawTransaction.js new file mode 100644 index 00000000000..1a9b72f12ba --- /dev/null +++ b/packages/wallet-lib/src/errors/InvalidRawTransaction.js @@ -0,0 +1,9 @@ +const WalletLibError = require('./WalletLibError'); + +class InvalidTransaction extends WalletLibError { + constructor() { + super('A valid transaction object or it\'s hex representation is required'); + } +} + +module.exports = InvalidTransaction; diff --git a/packages/wallet-lib/src/errors/InvalidStorageAdapter.js b/packages/wallet-lib/src/errors/InvalidStorageAdapter.js new file mode 100644 index 00000000000..4b49480ff6e --- /dev/null +++ b/packages/wallet-lib/src/errors/InvalidStorageAdapter.js @@ -0,0 +1,8 @@ +const WalletLibError = require('./WalletLibError'); + +class InvalidStorageAdapter extends WalletLibError { + constructor(reason) { + super(`Invalid Storage Adapter : ${reason}`); + } +} +module.exports = InvalidStorageAdapter; diff --git a/packages/wallet-lib/src/errors/InvalidStrategy.js b/packages/wallet-lib/src/errors/InvalidStrategy.js new file mode 100644 index 00000000000..fbffca99740 --- /dev/null +++ b/packages/wallet-lib/src/errors/InvalidStrategy.js @@ -0,0 +1,9 @@ +const WalletLibError = require('./WalletLibError'); + +class InvalidStrategy extends WalletLibError { + constructor(arg) { + const type = arg.constructor.name; + super(`Unable to import strategy. Expected 'str' or 'fn' got ${type}`); + } +} +module.exports = InvalidStrategy; diff --git a/packages/wallet-lib/src/errors/InvalidTransactionObject.js b/packages/wallet-lib/src/errors/InvalidTransactionObject.js new file mode 100644 index 00000000000..7d5ec0c552d --- /dev/null +++ b/packages/wallet-lib/src/errors/InvalidTransactionObject.js @@ -0,0 +1,37 @@ +const { has } = require('lodash'); +const is = require('../utils/is'); + +const WalletLibError = require('./WalletLibError'); + +class InvalidTransactionObject extends WalletLibError { + constructor(transactionObj) { + const getErrorMessageOf = (transactionErrors) => { + if (!is.arr(transactionErrors) || transactionErrors.length === 0) return false; + const err = transactionErrors[0]; + const txid = has(transactionObj, 'txid') ? transactionObj.txid : 'unknown'; + return `Transaction txid: ${txid} should have property ${err[0]} of type ${err[1]}`; + }; + + const evaluateTransactionObjectError = (_txObj) => { + const addressErrors = []; + const expectedProps = [ + ['txid', 'txid'], + ['vin', 'array'], + ['vout', 'array'], + ]; + const handledTypeVerification = Object.keys(is); + expectedProps.forEach((prop) => { + const key = prop[0]; + const type = prop[1]; + if (handledTypeVerification.includes(type)) { + if ((!has(_txObj, key) || !is[type](_txObj[key]))) { + addressErrors.push(prop); + } + } + }); + return addressErrors; + }; + super(getErrorMessageOf(evaluateTransactionObjectError(transactionObj))); + } +} +module.exports = InvalidTransactionObject; diff --git a/packages/wallet-lib/src/errors/InvalidUTXO.js b/packages/wallet-lib/src/errors/InvalidUTXO.js new file mode 100644 index 00000000000..690d1625fa9 --- /dev/null +++ b/packages/wallet-lib/src/errors/InvalidUTXO.js @@ -0,0 +1,9 @@ +const WalletLibError = require('./WalletLibError'); + +class InvalidUTXO extends WalletLibError { + constructor() { + const message = 'Invalid UnspentOutput provided.'; + super(message); + } +} +module.exports = InvalidUTXO; diff --git a/packages/wallet-lib/src/errors/MempoolPropagationTimeoutError.js b/packages/wallet-lib/src/errors/MempoolPropagationTimeoutError.js new file mode 100644 index 00000000000..07fb2738cb0 --- /dev/null +++ b/packages/wallet-lib/src/errors/MempoolPropagationTimeoutError.js @@ -0,0 +1,12 @@ +const WalletLibError = require('./WalletLibError'); + +class MempoolPropagationTimeoutError extends WalletLibError { + /** + * @param {string} transactionHash + */ + constructor(transactionHash) { + super(`Mempool propagation waiting period for transaction ${transactionHash} timed out`); + } +} + +module.exports = MempoolPropagationTimeoutError; diff --git a/packages/wallet-lib/src/errors/PluginFailedOnStart.js b/packages/wallet-lib/src/errors/PluginFailedOnStart.js new file mode 100644 index 00000000000..132b9ea802e --- /dev/null +++ b/packages/wallet-lib/src/errors/PluginFailedOnStart.js @@ -0,0 +1,15 @@ +const WalletLibError = require('./WalletLibError'); + +class PluginFailedOnStart extends WalletLibError { + constructor(pluginType, pluginName, error) { + super(`Plugin ${pluginName} of type ${pluginType} onStart failed: ${error.message}`); + + this.error = error; + } + + getError() { + return this.error; + } +} + +module.exports = PluginFailedOnStart; diff --git a/packages/wallet-lib/src/errors/PluginInjectionError.js b/packages/wallet-lib/src/errors/PluginInjectionError.js new file mode 100644 index 00000000000..07f764bdcdb --- /dev/null +++ b/packages/wallet-lib/src/errors/PluginInjectionError.js @@ -0,0 +1,15 @@ +const WalletLibError = require('./WalletLibError'); + +class PluginInjectionError extends WalletLibError { + constructor(error) { + super(`Failed to perform standard injections with reason: ${error.message}`); + + this.error = error; + } + + getError() { + return this.error; + } +} + +module.exports = PluginInjectionError; diff --git a/packages/wallet-lib/src/errors/StorageUnableToAddTransaction.js b/packages/wallet-lib/src/errors/StorageUnableToAddTransaction.js new file mode 100644 index 00000000000..6f1e6056ea7 --- /dev/null +++ b/packages/wallet-lib/src/errors/StorageUnableToAddTransaction.js @@ -0,0 +1,9 @@ +const WalletLibError = require('./WalletLibError'); + +class StorageUnableToAddTransaction extends WalletLibError { + constructor(tx) { + const getErrorMessageOf = (_tx) => `Unable to add transaction : ${JSON.stringify(_tx)}`; + super(getErrorMessageOf(tx)); + } +} +module.exports = StorageUnableToAddTransaction; diff --git a/packages/wallet-lib/src/errors/TransactionMetadataNotInStore.js b/packages/wallet-lib/src/errors/TransactionMetadataNotInStore.js new file mode 100644 index 00000000000..4a915d480e3 --- /dev/null +++ b/packages/wallet-lib/src/errors/TransactionMetadataNotInStore.js @@ -0,0 +1,9 @@ +const WalletLibError = require('./WalletLibError'); + +class TransactionMetadataNotInStore extends WalletLibError { + constructor(txid) { + super(`Transaction metadata is not in store: ${txid}`); + } +} + +module.exports = TransactionMetadataNotInStore; diff --git a/packages/wallet-lib/src/errors/TransactionNotInStore.js b/packages/wallet-lib/src/errors/TransactionNotInStore.js new file mode 100644 index 00000000000..399ff46672b --- /dev/null +++ b/packages/wallet-lib/src/errors/TransactionNotInStore.js @@ -0,0 +1,9 @@ +const WalletLibError = require('./WalletLibError'); + +class TransactionNotInStore extends WalletLibError { + constructor(txid) { + super(`Transaction is not in store: ${txid}`); + } +} + +module.exports = TransactionNotInStore; diff --git a/packages/wallet-lib/src/errors/TransporterGenericError.js b/packages/wallet-lib/src/errors/TransporterGenericError.js new file mode 100644 index 00000000000..0e169f2aa3b --- /dev/null +++ b/packages/wallet-lib/src/errors/TransporterGenericError.js @@ -0,0 +1,8 @@ +const WalletLibError = require('./WalletLibError'); + +class TransporterGenericError extends WalletLibError { + constructor(act, reason) { + super(`Unable to ${act}, reason: ${reason}`); + } +} +module.exports = TransporterGenericError; diff --git a/packages/wallet-lib/src/errors/TxMetadataTimeoutError.js b/packages/wallet-lib/src/errors/TxMetadataTimeoutError.js new file mode 100644 index 00000000000..5c99ed4f5fb --- /dev/null +++ b/packages/wallet-lib/src/errors/TxMetadataTimeoutError.js @@ -0,0 +1,12 @@ +const WalletLibError = require('./WalletLibError'); + +class TxMetadataTimeoutError extends WalletLibError { + /** + * @param {string} transactionHash + */ + constructor(transactionHash) { + super(`Metadata waiting period for transaction ${transactionHash} timed out`); + } +} + +module.exports = TxMetadataTimeoutError; diff --git a/packages/wallet-lib/src/errors/UnknownPlugin.js b/packages/wallet-lib/src/errors/UnknownPlugin.js new file mode 100644 index 00000000000..06e61238296 --- /dev/null +++ b/packages/wallet-lib/src/errors/UnknownPlugin.js @@ -0,0 +1,10 @@ +const WalletLibError = require('./WalletLibError'); + +class UnknownPlugin extends WalletLibError { + constructor(pluginName) { + const getErrorMessageOf = () => `Unknown Plugin : ${pluginName}.`; + super(getErrorMessageOf()); + } +} + +module.exports = UnknownPlugin; diff --git a/packages/wallet-lib/src/errors/UnknownStrategy.js b/packages/wallet-lib/src/errors/UnknownStrategy.js new file mode 100644 index 00000000000..a7f72cba9fa --- /dev/null +++ b/packages/wallet-lib/src/errors/UnknownStrategy.js @@ -0,0 +1,10 @@ +const WalletLibError = require('./WalletLibError'); + +class UnknownStrategy extends WalletLibError { + constructor(strategyName) { + const getErrorMessageOf = () => `Unknown Strategy : ${strategyName}.`; + super(getErrorMessageOf()); + } +} + +module.exports = UnknownStrategy; diff --git a/packages/wallet-lib/src/errors/UnknownWorker.js b/packages/wallet-lib/src/errors/UnknownWorker.js new file mode 100644 index 00000000000..c4812474ab5 --- /dev/null +++ b/packages/wallet-lib/src/errors/UnknownWorker.js @@ -0,0 +1,10 @@ +const WalletLibError = require('./WalletLibError'); + +class UnknownWorker extends WalletLibError { + constructor(workerName) { + const getErrorMessageOf = () => `Unknown Worker : ${workerName}.`; + super(getErrorMessageOf()); + } +} + +module.exports = UnknownWorker; diff --git a/packages/wallet-lib/src/errors/ValidTransportLayerRequired.js b/packages/wallet-lib/src/errors/ValidTransportLayerRequired.js new file mode 100644 index 00000000000..fbfd43c4d0b --- /dev/null +++ b/packages/wallet-lib/src/errors/ValidTransportLayerRequired.js @@ -0,0 +1,8 @@ +const WalletLibError = require('./WalletLibError'); + +class ValidTransportLayerRequired extends WalletLibError { + constructor(method) { + super(`A transport layer is needed to perform a ${method}`); + } +} +module.exports = ValidTransportLayerRequired; diff --git a/packages/wallet-lib/src/errors/WalletLibError.js b/packages/wallet-lib/src/errors/WalletLibError.js new file mode 100644 index 00000000000..7ccb3b32bbe --- /dev/null +++ b/packages/wallet-lib/src/errors/WalletLibError.js @@ -0,0 +1,22 @@ +class WalletLibError extends Error { + constructor(...params) { + super(...params); + + this.name = this.constructor.name; + } + + /** + * @returns {string} + */ + toString() { + let string = super.toString(); + + if (this.error) { + string += `\n\n${this.error.toString()}`; + } + + return string; + } +} + +module.exports = WalletLibError; diff --git a/packages/wallet-lib/src/errors/WorkerFailedOnExecute.js b/packages/wallet-lib/src/errors/WorkerFailedOnExecute.js new file mode 100644 index 00000000000..b7f4162129d --- /dev/null +++ b/packages/wallet-lib/src/errors/WorkerFailedOnExecute.js @@ -0,0 +1,22 @@ +const WalletLibError = require('./WalletLibError'); + +class WorkerFailedOnExecute extends WalletLibError { + /** + * @param {string} pluginName + * @param {Error} error + */ + constructor(pluginName, error) { + super(`Worker ${pluginName} failed onExecute: ${error.message}`); + + this.error = error; + } + + /** + * @returns {Error} + */ + getError() { + return this.error; + } +} + +module.exports = WorkerFailedOnExecute; diff --git a/packages/wallet-lib/src/errors/WorkerFailedOnStart.js b/packages/wallet-lib/src/errors/WorkerFailedOnStart.js new file mode 100644 index 00000000000..6585ea9d1a4 --- /dev/null +++ b/packages/wallet-lib/src/errors/WorkerFailedOnStart.js @@ -0,0 +1,22 @@ +const WalletLibError = require('./WalletLibError'); + +class WorkerFailedOnStart extends WalletLibError { + /** + * @param {string} pluginName + * @param {Error} error + */ + constructor(pluginName, error) { + super(`Worker ${pluginName} failed onStart: ${error.message}`); + + this.error = error; + } + + /** + * @returns {Error} + */ + getError() { + return this.error; + } +} + +module.exports = WorkerFailedOnStart; diff --git a/packages/wallet-lib/src/errors/index.js b/packages/wallet-lib/src/errors/index.js new file mode 100644 index 00000000000..e9f7407e248 --- /dev/null +++ b/packages/wallet-lib/src/errors/index.js @@ -0,0 +1,65 @@ +const CreateTransactionError = require('./CreateTransactionError'); +const CoinSelectionUnsufficientUTXOS = require('./CoinSelectionUnsufficientUTXOS'); +const InjectionErrorCannotInject = require('./InjectionErrorCannotInject'); +const InjectionErrorCannotInjectUnknownDependency = require('./InjectionErrorCannotInjectUnknownDependency'); +const InjectionToPluginUnallowed = require('./InjectionToPluginUnallowed'); + +const PluginFailedOnStart = require('./PluginFailedOnStart'); +const WorkerFailedOnStart = require('./WorkerFailedOnStart'); +const WorkerFailedOnExecute = require('./WorkerFailedOnExecute'); + +const InvalidAddress = require('./InvalidAddress'); +const InvalidAddressObject = require('./InvalidAddressObject'); +const InvalidOutput = require('./InvalidOutput'); +const InvalidDashcoreTransaction = require('./InvalidDashcoreTransaction'); +const InvalidRawTransaction = require('./InvalidRawTransaction'); +const InvalidStrategy = require('./InvalidStrategy'); +const InvalidStorageAdapter = require('./InvalidStorageAdapter'); + +const InvalidTransactionObject = require('./InvalidTransactionObject'); +const InvalidUTXO = require('./InvalidUTXO'); +const StorageUnableToAddTransaction = require('./StorageUnableToAddTransaction'); +const TransactionNotInStore = require('./TransactionNotInStore'); +const TransactionMetadataNotInStore = require('./TransactionMetadataNotInStore'); +const BlockHeaderNotInStore = require('./BlockHeaderNotInStore'); + +const UnknownWorker = require('./UnknownWorker'); +const UnknownPlugin = require('./UnknownPlugin'); + +const ValidTransportLayerRequired = require('./ValidTransportLayerRequired'); +const WalletLibError = require('./WalletLibError'); + +const PluginInjectionError = require('./PluginInjectionError'); +const InstantLockTimeoutError = require('./InstantLockTimeoutError'); +const TxMetadataTimeoutError = require('./TxMetadataTimeoutError'); + +module.exports = { + BlockHeaderNotInStore, + CreateTransactionError, + CoinSelectionUnsufficientUTXOS, + InjectionErrorCannotInject, + InjectionErrorCannotInjectUnknownDependency, + InjectionToPluginUnallowed, + InvalidAddress, + InvalidAddressObject, + InvalidOutput, + InvalidStrategy, + InvalidDashcoreTransaction, + InvalidRawTransaction, + InvalidStorageAdapter, + InvalidTransactionObject, + InvalidUTXO, + PluginFailedOnStart, + WorkerFailedOnStart, + WorkerFailedOnExecute, + StorageUnableToAddTransaction, + TransactionNotInStore, + TransactionMetadataNotInStore, + UnknownPlugin, + UnknownWorker, + ValidTransportLayerRequired, + WalletLibError, + PluginInjectionError, + InstantLockTimeoutError, + TxMetadataTimeoutError, +}; diff --git a/packages/wallet-lib/src/index.d.ts b/packages/wallet-lib/src/index.d.ts new file mode 100644 index 00000000000..ab1ab579cc8 --- /dev/null +++ b/packages/wallet-lib/src/index.d.ts @@ -0,0 +1,27 @@ +/// +/// +/// +import { Account } from "./types/Account/Account"; +import { Wallet } from "./types/Wallet/Wallet"; +import { Identities } from "./types/Identities/Identities"; +import { ChainStore } from "./types/ChainStore/ChainStore"; +import { DerivableKeyChain } from "./types/DerivableKeyChain/DerivableKeyChain"; +import { KeyChainStore } from "./types/KeyChainStore/KeyChainStore"; +import CONSTANTS from "./CONSTANTS"; +import EVENTS from "./EVENTS"; +import utils from "./utils"; +import plugins from "./plugins"; + +export { + Account, + Wallet, + ChainStore, + DerivableKeyChain, + KeyChainStore, + Identities, + EVENTS, + CONSTANTS, + utils, + plugins, +}; +declare module '@dashevo/wallet-lib'; diff --git a/packages/wallet-lib/src/index.js b/packages/wallet-lib/src/index.js new file mode 100644 index 00000000000..71610cdc033 --- /dev/null +++ b/packages/wallet-lib/src/index.js @@ -0,0 +1,31 @@ +// Default winston transport requires setImmediate to work, so +// polyfill included here. Making it work with webpack is rather tricky, so it is used as per +// documentation: https://github.com/YuzuJS/setImmediate#usage +require('setimmediate'); +const Account = require('./types/Account/Account'); +const ChainStore = require('./types/ChainStore/ChainStore'); +const Identities = require('./types/Identities/Identities'); +const DerivableKeyChain = require('./types/DerivableKeyChain/DerivableKeyChain'); +const KeyChainStore = require('./types/KeyChainStore/KeyChainStore'); +const Storage = require('./types/Storage/Storage'); +const Wallet = require('./types/Wallet/Wallet'); +const WalletStore = require('./types/WalletStore/WalletStore'); +const EVENTS = require('./EVENTS'); +const CONSTANTS = require('./CONSTANTS'); +const utils = require('./utils'); +const plugins = require('./plugins'); + +module.exports = { + Account, + ChainStore, + Identities, + DerivableKeyChain, + KeyChainStore, + Storage, + Wallet, + WalletStore, + EVENTS, + CONSTANTS, + utils, + plugins, +}; diff --git a/packages/wallet-lib/src/logger/index.js b/packages/wallet-lib/src/logger/index.js new file mode 100644 index 00000000000..c6e4970b78b --- /dev/null +++ b/packages/wallet-lib/src/logger/index.js @@ -0,0 +1,40 @@ +const util = require('util'); +const winston = require('winston'); + +const LOG_LEVEL = process.env.LOG_LEVEL || 'info'; + +// Log levels: +// error 0 +// warn 1 +// info 2 (default) +// verbose 3 +// debug 4 +// silly 5 + +const logger = winston.createLogger({ + level: LOG_LEVEL, + transports: [ + new winston.transports.Console({ + format: winston.format.combine( + { + transform: (info) => { + const args = info[Symbol.for('splat')]; + const result = { ...info }; + if (args) { + result.message = util.format(info.message, ...args); + } + return result; + }, + }, + winston.format.colorize(), + winston.format.printf(({ + level, message, + }) => `${level}: ${message}`), + ), + }), + ], +}); + +logger.verbose(`Logger uses "${LOG_LEVEL}" level`, { level: LOG_LEVEL }); + +module.exports = logger; diff --git a/packages/wallet-lib/src/plugins/Plugins/ChainPlugin.js b/packages/wallet-lib/src/plugins/Plugins/ChainPlugin.js new file mode 100644 index 00000000000..b7e5b19a37f --- /dev/null +++ b/packages/wallet-lib/src/plugins/Plugins/ChainPlugin.js @@ -0,0 +1,103 @@ +const logger = require('../../logger'); +const { StandardPlugin } = require('..'); +const EVENTS = require('../../EVENTS'); +const { dashToDuffs } = require('../../utils'); +const ChainSyncMediator = require('../../types/Wallet/ChainSyncMediator'); + +const defaultOpts = { + firstExecutionRequired: true, + executeOnStart: true, +}; + +class ChainPlugin extends StandardPlugin { + constructor(opts = {}) { + const params = { + name: 'ChainPlugin', + executeOnStart: defaultOpts.executeOnStart, + firstExecutionRequired: defaultOpts.firstExecutionRequired, + awaitOnInjection: true, + dependencies: [ + 'storage', + 'transport', + 'fetchStatus', + 'walletId', + 'chainSyncMediator', + ], + }; + super(Object.assign(params, opts)); + this.isSubscribedToBlocks = false; + } + + /** + * Used to subscribe to blockheaders and provide BLOCK, BLOCKHEADER and BLOCKHEIGHT_CHANGED. + * Also, maintain the blockheader storage up to date. + * @return {Promise} + */ + async execBlockListener() { + const self = this; + const { network } = this.storage.application; + const chainStore = this.storage.getChainStore(network); + const walletStore = this.storage.getWalletStore(this.walletId); + + if (!this.isSubscribedToBlocks) { + self.transport.on(EVENTS.BLOCK, async (ev) => { + const { payload: block } = ev; + this.parentEvents.emit(EVENTS.BLOCK, { type: EVENTS.BLOCK, payload: block }); + }); + self.transport.on(EVENTS.BLOCKHEIGHT_CHANGED, async (ev) => { + const { payload: blockheight } = ev; + + this.parentEvents.emit(EVENTS.BLOCKHEIGHT_CHANGED, { + type: EVENTS.BLOCKHEIGHT_CHANGED, payload: blockheight, + }); + + chainStore.state.blockHeight = blockheight; + + // Update last known block for the wallet only if we are in the state of the incoming sync. + // (During the historical sync, it is populated from transactions metadata) + if (this.chainSyncMediator.state === ChainSyncMediator.STATES.CONTINUOUS_SYNC) { + walletStore.updateLastKnownBlock(blockheight); + this.storage.scheduleStateSave(); + } + + logger.debug(`ChainPlugin - setting chain blockheight ${blockheight}`); + }); + await self.transport.subscribeToBlocks(); + } + } + + /** + * Used on ChainPlugin to be able to report on BLOCKHEIGHT_CHANGED. + * Neither Block or Blockheader contains blockheight, we need to fetch it from getStatus.blocks + * @return {Promise} + */ + async execStatusFetch() { + const res = await this.fetchStatus(); + + if (!res) { + return false; + } + + const { network } = this.storage.application; + const chainStore = this.storage.getChainStore(network); + const { chain: { blocksCount: blocks }, network: { fee: { relay } } } = res; + + logger.debug('ChainPlugin - Setting up starting blockHeight', blocks); + + chainStore.state.blockHeight = blocks; + + if (relay) { + chainStore.state.fees.minRelay = dashToDuffs(relay); + } + + return true; + } + + async onStart() { + this.chainSyncMediator.state = ChainSyncMediator.STATES.CHAIN_STATUS_SYNC; + await this.execStatusFetch(); + await this.execBlockListener(); + } +} + +module.exports = ChainPlugin; diff --git a/packages/wallet-lib/src/plugins/StandardPlugin.js b/packages/wallet-lib/src/plugins/StandardPlugin.js new file mode 100644 index 00000000000..6a5c5a22bb8 --- /dev/null +++ b/packages/wallet-lib/src/plugins/StandardPlugin.js @@ -0,0 +1,76 @@ +const _ = require('lodash'); +const EventEmitter = require('events'); +const { InjectionToPluginUnallowed } = require('../errors'); +const { SAFE_FUNCTIONS, SAFE_PROPERTIES } = require('../CONSTANTS').INJECTION_LISTS; + +const defaultOpts = { + executeOnStart: false, +}; + +class StandardPlugin extends EventEmitter { + constructor(opts = {}) { + super(); + this.pluginType = _.has(opts, 'type') ? opts.type : 'Standard'; + this.name = _.has(opts, 'name') ? opts.name : 'UnnamedPlugin'; + this.dependencies = _.has(opts, 'dependencies') ? opts.dependencies : []; + this.injectionOrder = _.has(opts, 'injectionOrder') ? opts.injectionOrder : { before: [], after: [] }; + this.awaitOnInjection = _.has(opts, 'awaitOnInjection') ? opts.awaitOnInjection : false; + this.executeOnStart = _.has(opts, 'executeOnStart') + ? opts.executeOnStart + : defaultOpts.executeOnStart; + + // Apply other props + Object.keys(opts).forEach((key) => { + if (!this[key]) { + this[key] = opts[key]; + } + }); + } + + async startPlugin() { + const self = this; + + try { + if (this.executeOnStart === true && this.onStart) { + await this.onStart(); + } + const eventType = `PLUGIN/${this.name.toUpperCase()}/STARTED`; + self.parentEvents.emit(eventType, { type: eventType, payload: null }); + } catch (e) { + this.emit('error', e, { + type: 'plugin', + pluginType: 'plugin', + pluginName: this.name, + }); + } + } + + inject(name, obj, allowSensitiveOperations = false) { + const PLUGINS_NAME_LIST = []; + if (SAFE_FUNCTIONS.includes(name) || SAFE_PROPERTIES.includes(name)) { + this[name] = obj; + } else if (PLUGINS_NAME_LIST.includes(name)) { + this.emit('error', new Error('Inter-plugin support yet to come'), { + type: 'plugin', + pluginType: 'plugin', + pluginName: this.name, + }); + } else if (allowSensitiveOperations === true) { + this[name] = obj; + } else if (name === 'parentEvents') { + // Called by injectPlugin to setup the parentEvents on/emit fn. + // console.log(obj) + // this.parentEvents = {on:obj.on, emit:obj.emit}; + this.parentEvents = obj; + } else { + this.emit('error', new InjectionToPluginUnallowed(this.name, name), { + type: 'plugin', + pluginType: 'plugin', + pluginName: this.name, + }); + } + return true; + } +} + +module.exports = StandardPlugin; diff --git a/packages/wallet-lib/src/plugins/StandardPlugin.spec.js b/packages/wallet-lib/src/plugins/StandardPlugin.spec.js new file mode 100644 index 00000000000..b6794a6b0a1 --- /dev/null +++ b/packages/wallet-lib/src/plugins/StandardPlugin.spec.js @@ -0,0 +1,30 @@ +const { expect } = require('chai'); +const { EventEmitter } = require('events'); +const StandardPluginSpec = require('./StandardPlugin'); + +describe('Plugins - StandardPlugin', function suite() { + this.timeout(60000); + let plugin; + let didSomething = 0; + it('should initiate', async () => { + plugin = new StandardPluginSpec(); + plugin.provideSomething = () => { + didSomething += 1; + return true; + }; + expect(plugin).to.not.equal(null); + expect(plugin.pluginType).to.equal('Standard'); + expect(plugin.name).to.equal('UnnamedPlugin'); + expect(plugin.dependencies).to.deep.equal([]); + }); + it('should inject an event emitter', () => { + const emitter = new EventEmitter(); + plugin.inject('parentEvents', { on: emitter.on, emit: emitter.emit }); + expect(plugin.parentEvents.on).to.deep.equal(emitter.on); + expect(plugin.parentEvents.emit).to.deep.equal(emitter.emit); + }); + it('should provide methods', () => { + expect(plugin.provideSomething()).to.equal(true); + expect(didSomething).to.deep.equal(1); + }); +}); diff --git a/packages/wallet-lib/src/plugins/Worker.js b/packages/wallet-lib/src/plugins/Worker.js new file mode 100644 index 00000000000..56d03de4894 --- /dev/null +++ b/packages/wallet-lib/src/plugins/Worker.js @@ -0,0 +1,145 @@ +const _ = require('lodash'); +const logger = require('../logger'); +const StandardPlugin = require('./StandardPlugin'); + +// eslint-disable-next-line no-underscore-dangle +const _defaultOpts = { + workerIntervalTime: 10 * 1000, + executeOnStart: false, + firstExecutionRequired: false, + workerMaxPass: null, +}; + +class Worker extends StandardPlugin { + constructor(opts = JSON.parse(JSON.stringify(_defaultOpts))) { + const defaultOpts = JSON.parse(JSON.stringify(_defaultOpts)); + super({ type: 'Worker', ...opts }); + this.worker = null; + this.workerPass = 0; + this.isWorkerRunning = false; + + this.awaitOnInjection = _.has(opts, 'awaitOnInjection') + ? opts.awaitOnInjection + : false; + + this.firstExecutionRequired = _.has(opts, 'firstExecutionRequired') + ? opts.firstExecutionRequired + : defaultOpts.firstExecutionRequired; + + this.executeOnStart = _.has(opts, 'executeOnStart') + ? opts.executeOnStart + : defaultOpts.executeOnStart; + + this.workerIntervalTime = _.has(opts, 'workerIntervalTime') + ? opts.workerIntervalTime + : defaultOpts.workerIntervalTime; + + this.workerMaxPass = (opts.workerMaxPass) + ? opts.workerMaxPass + : defaultOpts.workerMaxPass; + + this.state = { + started: false, + ready: false, + }; + } + + async startWorker() { + let payloadResult = null; + const self = this; + const eventTypeStarting = `WORKER/${this.name.toUpperCase()}/STARTING`; + logger.debug(JSON.stringify({ eventTypeStarting, result: payloadResult })); + this.parentEvents.emit(eventTypeStarting, { type: eventTypeStarting, payload: payloadResult }); + try { + if (this.worker) await this.stopWorker(); + + if (this.workerIntervalTime > 0) { + this.worker = setInterval(this.execWorker.bind(self), this.workerIntervalTime); + } + + if (this.executeOnStart === true) { + if (this.onStart) { + payloadResult = await this.onStart(); + } + } + const eventTypeStarted = `WORKER/${this.name.toUpperCase()}/STARTED`; + logger.debug(JSON.stringify({ eventTypeStarted, result: payloadResult })); + this.parentEvents.emit(eventTypeStarted, { type: eventTypeStarted, payload: payloadResult }); + this.state.started = true; + + if (this.executeOnStart) await this.execWorker(); + } catch (e) { + this.emit('error', e, { + type: 'plugin', + pluginType: 'worker', + pluginName: this.name, + }); + } + } + + /** + * @param {Object} [options] + * @param {Boolean} [options.force=false] + * @param {Boolean} [options.reason] + * @returns {Promise} + */ + async stopWorker(options = {}) { + let payloadResult = options.reason; + + clearInterval(this.worker); + + this.worker = null; + this.workerPass = 0; + this.isWorkerRunning = false; + + const eventType = `WORKER/${this.name.toUpperCase()}/STOPPED`; + + if (this.onStop) { + payloadResult = await this.onStop(options); + } + + this.state.started = false; + logger.debug(JSON.stringify({ eventType, result: payloadResult })); + this.parentEvents.emit(eventType, { type: eventType, payload: payloadResult }); + } + + async execWorker() { + let payloadResult = null; + if (this.isWorkerRunning) { + return false; + } + if (this.workerMaxPass !== null && this.workerPass >= this.workerMaxPass) { + await this.stopWorker(); + return false; + } + this.isWorkerRunning = true; + + if (this.execute) { + try { + payloadResult = await this.execute(); + } catch (e) { + await this.stopWorker({ + reason: e.message, + }); + + this.emit('error', e, { + type: 'plugin', + pluginType: 'worker', + pluginName: this.name, + }); + } + } else { + throw new Error(`Worker ${this.name}: Missing execute function`); + } + + this.isWorkerRunning = false; + this.workerPass += 1; + if (!this.state.ready) this.state.ready = true; + const eventType = `WORKER/${this.name.toUpperCase()}/EXECUTED`; + logger.debug(JSON.stringify({ eventType, result: payloadResult })); + this.parentEvents.emit(eventType, { type: eventType, payload: payloadResult }); + return true; + } +} + +module.exports = Worker; diff --git a/packages/wallet-lib/src/plugins/Worker.spec.js b/packages/wallet-lib/src/plugins/Worker.spec.js new file mode 100644 index 00000000000..feb96e3de6c --- /dev/null +++ b/packages/wallet-lib/src/plugins/Worker.spec.js @@ -0,0 +1,76 @@ +const { expect } = require('chai'); +const { EventEmitter } = require('events'); +const WorkerSpec = require('./Worker'); +const FaultyWorker = require('../../fixtures/plugins/FaultyWorker'); + +describe('Plugins - Worker', function suite() { + this.timeout(60000); + let worker; + let intervalTime = 10000; + it('should initiate', async () => { + worker = new WorkerSpec(); + + expect(worker).to.not.equal(null); + expect(worker.pluginType).to.equal('Worker'); + expect(worker.name).to.equal('UnnamedPlugin'); + expect(worker.dependencies).to.deep.equal([]); + expect(worker.workerIntervalTime).to.equal(intervalTime); + expect(worker.executeOnStart).to.equal(false); + expect(worker.firstExecutionRequired).to.equal(false); + expect(worker.workerMaxPass).to.equal(null); + expect(worker.worker).to.equal(null); + expect(worker.workerPass).to.equal(0); + expect(worker.isWorkerRunning).to.equal(false); + }); + it('should inject an event emitter', () => { + const emitter = new EventEmitter(); + worker.inject('parentEvents', emitter); + expect(worker.parentEvents).to.deep.equal(emitter); + }); + it('should start and stop', (done) => { + let didSomething = 0; + worker.workerIntervalTime = 200; + worker.execute = () => { + didSomething += 1; + }; + + worker.startWorker(); + setTimeout(async () => { + expect(worker.workerPass).to.equal(4); + expect(didSomething).to.equal(4); + await worker.stopWorker(); + setTimeout(() => { + expect(worker.workerPass).to.equal(0); + expect(didSomething).to.equal(4); + done(); + }, 400); + }, 999); + }); + it('should not execute if previous is not over', function (done) { + const events = []; + worker.workerIntervalTime = 400; + worker.execute = () => { + events.push('start'); + return new Promise((resolve => { + setTimeout(()=>{ + events.push('executed'); + resolve(); + }, 600) + })) + }; + worker.startWorker(); + const expectedTimeout = worker.workerIntervalTime * 2 + 600 * 2; + const expectedEvents = ['start', 'executed', 'start', 'executed']; + setTimeout(()=>{ + worker.stopWorker(); + // It's okay if we have an additionnal "start" + expect(events.slice(0,4)).to.deep.equal(expectedEvents); + done(); + }, expectedTimeout); + }); + it('should handle faulty worker', function () { + const faultyWorker = new FaultyWorker(); + const expectedException1 = 'Some reason.'; + expect(() => faultyWorker.execute()).to.throw(expectedException1); + }); +}); diff --git a/packages/wallet-lib/src/plugins/Workers/IdentitySyncWorker.js b/packages/wallet-lib/src/plugins/Workers/IdentitySyncWorker.js new file mode 100644 index 00000000000..9e86c18f6c2 --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/IdentitySyncWorker.js @@ -0,0 +1,129 @@ +const Identity = require('@dashevo/dpp/lib/identity/Identity'); +const decodeProtocolEntityFactory = require('@dashevo/dpp/lib/decodeProtocolEntityFactory'); + +const Worker = require('../Worker'); +const logger = require('../../logger'); + +const decodeProtocolEntity = decodeProtocolEntityFactory(); + +/** + * @property {number} gapLimit + */ +class IdentitySyncWorker extends Worker { + constructor(options) { + super({ + name: 'IdentitySyncWorker', + executeOnStart: true, + firstExecutionRequired: true, + workerIntervalTime: 60 * 1000, + awaitOnInjection: true, + gapLimit: 10, + dependencies: [ + 'storage', + 'transport', + 'walletId', + 'identities', + ], + ...options, + }); + } + + async execute() { + const walletStore = this.storage.getWalletStore(this.walletId); + const indexedIds = await walletStore.getIndexedIdentityIds(); + + // Add gaps to empty indices + const unusedIndices = []; + indexedIds.forEach((id, index) => { + if (!id) { + return; + } + + unusedIndices.push(index); + }); + + logger.silly('IdentitySyncWorker - sync start'); + + let gapCount = 0; + let unusedIndex; + let index = -1; + while (gapCount < this.gapLimit) { + unusedIndex = unusedIndices.shift(); + + // check unused indices in the middle of list first + if (unusedIndex) { + // if we go through unused indices and they are not + // sequential we need to reset gap count + if (unusedIndex !== index + 1) { + gapCount = 0; + } + + index = unusedIndex; + } else { + // if unused indices are over just increment index + // until gap limit will be reached + index += 1; + } + + const { privateKey } = this.identities.getIdentityHDKeyByIndex(index, 0); + const publicKey = privateKey.toPublicKey(); + + // eslint-disable-next-line no-await-in-loop + const identityBuffers = await this.transport.getIdentitiesByPublicKeyHashes( + [publicKey.hash], + ); + + // if identity is not preset then increment gap count + // and stop sync if gap limit is reached + if (identityBuffers.length === 0) { + gapCount += 1; + + logger.silly(`IdentitySyncWorker - gap at index ${index}`); + + if (gapCount >= this.gapLimit) { + logger.silly('IdentitySyncWorker - gap limit is reached'); + + break; + } + + // eslint-disable-next-line no-continue + continue; + } + + const [identityBuffer] = identityBuffers; + + // If it's not an undefined and not a buffer or Identifier (which inherits Buffer), + // this method will loop forever. + // This check prevents this from happening + if (!Buffer.isBuffer(identityBuffer)) { + throw new Error(`Expected identity id to be a Buffer or undefined, got ${identityBuffer}`); + } + + // reset gap counter if we got an identity + // it means gaps are not sequential + gapCount = 0; + + const [protocolVersion, rawIdentity] = decodeProtocolEntity( + identityBuffer, + ); + + rawIdentity.protocolVersion = protocolVersion; + + const identity = new Identity(rawIdentity); + + logger.silly(`IdentitySyncWorker - got ${identity.getId()} at ${index}`); + + // eslint-disable-next-line no-await-in-loop + await this.storage + .getWalletStore(this.walletId) + .insertIdentityIdAtIndex( + identity.getId().toString(), + index, + ); + } + + logger.silly('IdentitySyncWorker - sync finished'); + } +} + +module.exports = IdentitySyncWorker; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker.js new file mode 100644 index 00000000000..84f85dd9d8f --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker.js @@ -0,0 +1,279 @@ +const { + Transaction, MerkleBlock, InstantLock, +} = require('@dashevo/dashcore-lib'); +const GrpcError = require('@dashevo/grpc-common/lib/server/error/GrpcError'); +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const sleep = require('../../../utils/sleep'); + +const Worker = require('../../Worker'); +const isBrowser = require('../../../utils/isBrowser'); + +const logger = require('../../../logger'); +const ChainSyncMediator = require('../../../types/Wallet/ChainSyncMediator'); + +class TransactionSyncStreamWorker extends Worker { + constructor(options) { + super({ + name: 'TransactionSyncStreamWorker', + executeOnStart: true, + firstExecutionRequired: true, + awaitOnInjection: true, + workerIntervalTime: 0, + gapLimit: 10, + dependencies: [ + 'importTransactions', + 'importBlockHeader', + 'importInstantLock', + 'storage', + 'keyChainStore', + 'transport', + 'walletId', + 'getAddress', + 'network', + 'index', + 'BIP44PATH', + 'walletType', + 'chainSyncMediator', + ], + ...options, + }); + + this.syncIncomingTransactions = false; + this.stream = null; + this.incomingSyncPromise = null; + this.pendingRequest = {}; + this.delayedRequests = {}; + this.lastSyncedBlockHeight = -1; + } + + /** + * Filter transaction based on the address list + * @param {Transaction[]} transactions + * @param {string[]} addressList + * @param {string} network + */ + static filterAddressesTransactions(transactions, addressList, network) { + const spentOutputs = []; + const unspentOutputs = []; + const filteredTransactions = transactions.filter((tx) => { + let isWalletTransaction = false; + + tx.inputs.forEach((input) => { + if (input.script) { + const addr = input.script.toAddress(network).toString(); + if (addressList.includes(addr)) { + spentOutputs.push(input); + isWalletTransaction = true; + } + } + }); + + tx.outputs.forEach((output) => { + const addr = output.script.toAddress(network).toString(); + if (addressList.includes(addr)) { + unspentOutputs.push(output); + isWalletTransaction = true; + } + }); + + return isWalletTransaction; + }); + + return { + transactions: filteredTransactions, + spentOutputs, + unspentOutputs, + }; + } + + /** + * + * @param {TransactionsWithProofsResponse} response + * @return {[]} + */ + static getMerkleBlockFromStreamResponse(response) { + let merkleBlock = null; + const rawMerkleBlock = response.getRawMerkleBlock(); + if (rawMerkleBlock) { + merkleBlock = new MerkleBlock(Buffer.from(rawMerkleBlock)); + } + return merkleBlock; + } + + /** + * + * @param response + * @return {[]} + */ + static getTransactionListFromStreamResponse(response) { + let walletTransactions = []; + const transactions = response.getRawTransactions(); + + if (transactions) { + walletTransactions = transactions + .getTransactionsList() + .map((rawTransaction) => new Transaction(Buffer.from(rawTransaction))); + } + + return walletTransactions; + } + + static getInstantSendLocksFromResponse(response) { + let walletTransactions = []; + const instantSendLockMessages = response.getInstantSendLockMessages(); + + if (instantSendLockMessages) { + walletTransactions = instantSendLockMessages + .getMessagesList() + .map((instantSendLock) => new InstantLock(Buffer.from(instantSendLock))); + } + + return walletTransactions; + } + + async onStart() { + // Using sync options here to avoid + // situation when plugin is injected directly + // instead of usual injection process + const { + skipSynchronizationBeforeHeight, + skipSynchronization, + } = (this.storage.application.syncOptions || {}); + + if (skipSynchronization) { + logger.debug('TransactionSyncStreamWorker - Wallet created from a new mnemonic. Sync from the best block height.'); + const bestBlockHeight = this.storage.getChainStore(this.network.toString()).state.blockHeight; + this.setLastSyncedBlockHeight(bestBlockHeight, true); + return; + } + + const { lastKnownBlock } = this.storage.getWalletStore(this.walletId).state; + const skipSyncBefore = typeof skipSynchronizationBeforeHeight === 'number' + ? skipSynchronizationBeforeHeight + : parseInt(skipSynchronizationBeforeHeight, 10); + + if (skipSyncBefore > lastKnownBlock.height) { + this.setLastSyncedBlockHeight( + skipSynchronizationBeforeHeight, + ); + } else if (lastKnownBlock.height !== -1) { + this.setLastSyncedBlockHeight(lastKnownBlock.height); + } + + this.chainSyncMediator.state = ChainSyncMediator.STATES.HISTORICAL_SYNC; + // We first need to sync up initial historical transactions + await this.startHistoricalSync(this.network); + await this.storage.saveState(); + } + + /** + * This is executed only once on start up. + * So we will maintain our ongoing stream during the whole execution of the wallet + * + * @returns {Promise} + */ + async execute() { + this.syncIncomingTransactions = true; + // We shouldn't block workers execution process with transaction syncing + // it should proceed in background + + this.chainSyncMediator.state = ChainSyncMediator.STATES.CONTINUOUS_SYNC; + // noinspection ES6MissingAwait + this.incomingSyncPromise = this.startIncomingSync().catch((e) => { + logger.error('Error syncing incoming transactions', e); + this.emit('error', e); + }); + } + + /** + * @param {Object} [options] + * @param {Boolean} [options.force=false] + * @param {Boolean} [options.reason] + * + * @returns {Promise} + */ + async onStop(options = {}) { + // in case of disconnect we don't need to wait until the wallet complete the sync process + if (options.force) { + this.pendingRequest = {}; + } + + // Sync, will require transaction and their blockHeader to be fetched before resolving. + // As await onStop() is a way to wait for execution before continuing, + // this ensure onStop will properly let the plugin to warn about all + // completion of pending request. + if (Object.keys(this.pendingRequest).length !== 0) { + await sleep(200); + + return this.onStop(); + } + + this.syncIncomingTransactions = false; + + if (isBrowser()) { + // Under browser environment, grpc-web doesn't call error and end events + // so we call it by ourselves + if (this.stream) { + return new Promise((resolve) => setImmediate(() => { + if (this.stream) { + this.stream.cancel(); + + const error = new GrpcError(GrpcErrorCodes.CANCELLED, 'Cancelled on client'); + // call onError events + this.stream.f.forEach((func) => func(error)); + + // call onEnd events + this.stream.c.forEach((func) => func()); + + this.stream = null; + } + + resolve(true); + })); + } + } + + // Wrapping `cancel` in `setImmediate` due to bug with double-free + // explained here (https://github.com/grpc/grpc-node/issues/1652) + // and here (https://github.com/nodejs/node/issues/38964) + return new Promise((resolve) => setImmediate(() => { + if (this.stream) { + this.stream.cancel(); + // When calling stream.cancel(), the stream will emit 'error' event + // with the code 'CANCELLED'. + // There are two cases when this happens: when the gap limit is filled + // and syncToTheGapLimit and the stream needs to be restarted with new parameters, + // and here, when stopping the worker. + // The code in stream worker distinguishes whether it need to reconnect or not by the fact + // that the old stream object is present or not. When it is set to null, it won't try to + // reconnect to the stream. + this.stream = null; + } + resolve(true); + })); + } + + setLastSyncedBlockHash(hash) { + const applicationStore = this.storage.application; + applicationStore.blockHash = hash; + return applicationStore.blockHash; + } + + getLastSyncedBlockHash() { + const { blockHash } = this.storage.application; + + return blockHash; + } +} + +TransactionSyncStreamWorker.prototype.getAddressesToSync = require('./methods/getAddressesToSync'); +TransactionSyncStreamWorker.prototype.getBestBlockHeightFromTransport = require('./methods/getBestBlockHeight'); +TransactionSyncStreamWorker.prototype.setLastSyncedBlockHeight = require('./methods/setLastSyncedBlockHeight'); +TransactionSyncStreamWorker.prototype.getLastSyncedBlockHeight = require('./methods/getLastSyncedBlockHeight'); +TransactionSyncStreamWorker.prototype.startHistoricalSync = require('./methods/startHistoricalSync'); +TransactionSyncStreamWorker.prototype.handleTransactionFromStream = require('./methods/handleTransactionFromStream'); +TransactionSyncStreamWorker.prototype.processChunks = require('./methods/processChunks'); +TransactionSyncStreamWorker.prototype.startIncomingSync = require('./methods/startIncomingSync'); +TransactionSyncStreamWorker.prototype.syncUpToTheGapLimit = require('./methods/syncUpToTheGapLimit'); + +module.exports = TransactionSyncStreamWorker; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/handlers/onStreamData.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/handlers/onStreamData.js new file mode 100644 index 00000000000..f8e8a0fcee7 --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/handlers/onStreamData.js @@ -0,0 +1,10 @@ +/* eslint-disable no-param-reassign */ +const logger = require('../../../../logger'); +const Job = require('../../../../utils/Queue/Job'); + +function onStreamData(self, data) { + logger.silly('TransactionSyncStreamWorker - received chunks waiting for processing'); + self.chunksQueue.enqueueJob(new Job(null, () => self.processChunks(data))); +} + +module.exports = onStreamData; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/handlers/onStreamEnd.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/handlers/onStreamEnd.js new file mode 100644 index 00000000000..053309fa38e --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/handlers/onStreamEnd.js @@ -0,0 +1,22 @@ +/* eslint-disable no-param-reassign */ +const logger = require('../../../../logger'); +const sleep = require('../../../../utils/sleep'); + +function onStreamEnd(workerInstance, resolve) { + const endStream = () => { + logger.silly('TransactionSyncStreamWorker - end stream on request'); + workerInstance.stream = null; + resolve(workerInstance.hasReachedGapLimit); + }; + + const tryEndStream = async () => { + if (Object.keys(workerInstance.pendingRequest).length !== 0) { + await sleep(200); + return tryEndStream(); + } + return endStream(); + }; + + tryEndStream(); +} +module.exports = onStreamEnd; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/handlers/onStreamError.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/handlers/onStreamError.js new file mode 100644 index 00000000000..624c5821010 --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/handlers/onStreamError.js @@ -0,0 +1,8 @@ +const logger = require('../../../../logger'); + +function onStreamError(error, reject) { + logger.silly('TransactionSyncStreamWorker - end stream on error'); + logger.silly(error.message); + reject(error); +} +module.exports = onStreamError; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.js new file mode 100644 index 00000000000..ec35d12f238 --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.js @@ -0,0 +1,7 @@ +// TODO: consider obtaining addresses only for current account +// instead of the whole keychain +module.exports = function getAddressesToSync() { + return this.keyChainStore.getKeyChains() + .map((keychain) => keychain.getWatchedAddresses()) + .reduce((pre, cur) => pre.concat(cur)); +}; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js new file mode 100644 index 00000000000..b78d93b64dd --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js @@ -0,0 +1,159 @@ +const { expect } = require('chai'); +const getAddressesToSync = require('./getAddressesToSync'); +const KeyChainStore = require("../../../../types/KeyChainStore/KeyChainStore"); +const DerivableKeyChain = require("../../../../types/DerivableKeyChain/DerivableKeyChain"); +const { HDPrivateKey, HDPublicKey, PrivateKey } = require("@dashevo/dashcore-lib"); + + +const privateKey = new PrivateKey('ee56be968a42e58fda23b83da17f90e002cafbe35a702c2f5598b13fdaa238db', 'testnet') +const hdprivateKey1 = new HDPrivateKey("xprv9s21ZrQH143K39R9Ux28kCBUHcQFdBeVE2CXFVz6GnA2a6pqTsPhHR5QHtMP5ZTRpYkKqc9ifjkJ2V1h318qWsYgyxCBUurRdTNthjgwKMw", 'mainnet'); +const hdpublicKey1 = new HDPublicKey("xpub661MyMwAqRbcFhaucFQun3ivEyA5gy5NKnjr1xMUVkyqdF3VNNy3TLinwnYMSUye5FF5pDSrn2SPX3zvKRQGrpZ44VVUBeuxuzov7enWpkf",'mainnet'); + +const keychainPrivate1 = new DerivableKeyChain({privateKey}); +const keychainHDPrivate1 = new DerivableKeyChain({HDPrivateKey: hdprivateKey1}); +const keychainHDPublic1 = new DerivableKeyChain({HDPublicKey: hdpublicKey1}); + +const keychainStorePrivateKeyWallet = new KeyChainStore(); +keychainStorePrivateKeyWallet.addKeyChain(keychainPrivate1, { isMasterKeyChain: true}); +keychainPrivate1.getForPath(0, { isWatched: true }) + +const keychainStoreHDPrivateKeyWallet = new KeyChainStore(); +keychainStoreHDPrivateKeyWallet.addKeyChain(keychainHDPrivate1, { isMasterKeyChain: true}); +keychainHDPrivate1.getForPath(`m/0/0`, { isWatched: true }) +keychainHDPrivate1.getForPath(`m/0/1`, { isWatched: true }) +const mockedStore1 = { + wallets: { + 123456789: { + addresses: { + misc:{ + '0':{ + address: 'yizmJb63ygipuJaRgYtpWCV2erQodmaZt1', + balanceSat: 0, + fetchedLast: 0, + path: "0", + transactions: [], + index: 0, + unconfirmedBalanceSat: 0, + used: false, + utxos: {} + } + } + }, + }, + }, +} +const mockedStore2 = { + wallets: { + 123456789: { + addresses: { + external: { + "m/44'/1'/0'/0/0": { + address: 'yizmJb63ygipuJaRgYtpWCV2erQodmaZt8', + balanceSat: 100000000, + fetchedLast: 0, + path: "m/44'/1'/0'/0/0", + transactions: [ + 'dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc', + '1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35', + '7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6', + ], + index: 0, + unconfirmedBalanceSat: 0, + used: true, + utxos: { + "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc-0": + { + address: 'yizmJb63ygipuJaRgYtpWCV2erQodmaZt8', + txId: 'dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc', + outputIndex: 0, + script: '76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac', + satoshis: 100000000, + }, + } + }, + "m/44'/1'/1'/0/0":{ + address: 'yQ5TfKcj3NHM4V4K5VBgoFJj9Q4LKX13gn', + balanceSat: 14419880000, + fetchedLast: 0, + path: "m/44'/1'/1'/0/0", + transactions: [ + 'b8838022a663ae486192cf2499f9ae657e8c3a7e823a447b8b7e3d348d3916ba', + ], + index: 0, + unconfirmedBalanceSat: 0, + used: true, + utxos: { + "b8838022a663ae486192cf2499f9ae657e8c3a7e823a447b8b7e3d348d3916ba-0": + { + address: 'yQ5TfKcj3NHM4V4K5VBgoFJj9Q4LKX13gn', + txId: 'b8838022a663ae486192cf2499f9ae657e8c3a7e823a447b8b7e3d348d3916ba', + outputIndex: 0, + script: '76a914293b5b9a2154a0e4543027d694276cd5fdcb74cd88ac', + satoshis: 14419880000, + }, + } + } + }, + internal:{ + "m/44'/1'/0'/1/0": { + address: 'yizmJb63ygipuJaRgYtpWCV2erQodmaZt9', + balanceSat: 0, + fetchedLast: 0, + path: "m/44'/1'/0'/1/0", + transactions: [], + index: 0, + unconfirmedBalanceSat: 0, + used: false, + utxos: {} + } + }, + misc:{ + '0':{ + address: 'yizmJb63ygipuJaRgYtpWCV2erQodmaZt1', + balanceSat: 0, + fetchedLast: 0, + path: "0", + transactions: [], + index: 0, + unconfirmedBalanceSat: 0, + used: false, + utxos: {} + } + } + }, + }, + }, +}; + +const mockSelfPrivateKeyType = { + storage: { getStore:()=>mockedStore1 }, + keyChainStore: keychainStorePrivateKeyWallet, + walletId: '123456789', + walletType: 'privateKey', +} +const mockSelfIndex0 = { + storage: { getStore:()=>mockedStore2 }, + keyChainStore: keychainStoreHDPrivateKeyWallet, + walletId: '123456789', + walletType: 'hdwallet', + BIP44PATH: `m/44'/1'/0'` +} +const mockSelfIndex1 = { + ...mockSelfIndex0, + BIP44PATH: `m/44'/1'/1'` +} + + +describe('TransactionSyncStreamWorker#getAddressesToSync', function suite() { + it('should correctly fetch addresses to sync', async () => { + + const addressesIndex0 = getAddressesToSync.call(mockSelfIndex0); + expect(addressesIndex0).to.deep.equal([ + "Xpkr9M3DP8RgcWw4SHUW75PYtmU1Lh5Ss2", + "Xp1kwhXoUVHKRKmoXt3dB4i4KhryHSYjtW" + ]) + + const addressesIndex2 = getAddressesToSync.call(mockSelfPrivateKeyType ); + expect(addressesIndex2).to.deep.equal(['yZprpQkn7FYUHjqm3dY4sCs9SorMCi4oyR']) + }); +}); diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getBestBlockHeight.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getBestBlockHeight.js new file mode 100644 index 00000000000..3b19e827b9c --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getBestBlockHeight.js @@ -0,0 +1,7 @@ +/** + * Return best block height + * @return {number} + */ +module.exports = async function getBestBlockHeightFromTransport() { + return this.transport.getBestBlockHeight(); +}; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getLastSyncedBlockHeight.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getLastSyncedBlockHeight.js new file mode 100644 index 00000000000..632a517f31d --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getLastSyncedBlockHeight.js @@ -0,0 +1,7 @@ +/** + * Return last synced block height + * @return {number} + */ +module.exports = function getLastSyncedBlockHeight() { + return this.lastSyncedBlockHeight; +}; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/handleTransactionFromStream.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/handleTransactionFromStream.js new file mode 100644 index 00000000000..29104fa3450 --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/handleTransactionFromStream.js @@ -0,0 +1,94 @@ +const logger = require('../../../../logger'); +const EVENTS = require('../../../../EVENTS'); + +async function handleTransactionFromStream(transaction) { + const self = this; + // As we require height information, we fetch transaction using client. + // eslint-disable-next-line no-restricted-syntax + // eslint-disable-next-line no-underscore-dangle + const transactionHash = transaction.hash; + + this.pendingRequest[transactionHash] = { isProcessing: true, type: 'transaction' }; + // eslint-disable-next-line no-await-in-loop + const getTransactionResponse = await this.transport.getTransaction(transactionHash); + + if (!getTransactionResponse) { + // This can happen due to propagation when one node inform us about a transaction, + // but the node we ask the transaction to is not aware of it. + logger.silly(`TransactionSyncStreamWorker - Transaction ${transactionHash} was not found`); + return new Promise((resolve) => { + setTimeout(() => { + resolve(self.handleTransactionFromStream(transaction)); + }, 1000); + }); + } + + if (!getTransactionResponse.blockHash) { + // at this point, transaction is not yet mined, therefore we gonna retry on next block to + // fetch this tx and subsequently its blockhash for blockheader fetching. + logger.silly(`TransactionSyncStreamWorker - Unconfirmed transaction ${transactionHash}: delayed.`); + const existingListener = this.delayedRequests[transactionHash] + && this.delayedRequests[transactionHash].blockHeightChangeListener; + + if (existingListener) { + self.parentEvents.removeListener(EVENTS.BLOCKHEIGHT_CHANGED, existingListener); + } + + this.delayedRequests[transactionHash] = { isDelayed: true, type: 'transaction', blockHeightChangeListener: null }; + + return new Promise((resolve) => { + const blockHeightChangeListener = () => { + resolve(self.handleTransactionFromStream(transaction)); + }; + + this.delayedRequests[transactionHash].blockHeightChangeListener = blockHeightChangeListener; + + self.parentEvents.once( + EVENTS.BLOCKHEIGHT_CHANGED, + blockHeightChangeListener, + ); + }); + } + + const executor = async () => { + if (self.delayedRequests[transactionHash]) { + logger.silly(`TransactionSyncStreamWorker - Processing previously delayed transaction ${transactionHash} from stream`); + const { blockHeightChangeListener } = self.delayedRequests[transactionHash]; + if (blockHeightChangeListener) { + self.parentEvents.removeListener(EVENTS.BLOCKHEIGHT_CHANGED, blockHeightChangeListener); + } + delete self.delayedRequests[transactionHash]; + } else { + logger.silly(`TransactionSyncStreamWorker - Processing transaction ${transactionHash} from stream`); + } + + this.pendingRequest[getTransactionResponse.blockHash.toString('hex')] = { isProcessing: true, type: 'blockheader' }; + // eslint-disable-next-line no-await-in-loop + const getBlockHeaderResponse = await this + .transport + .getBlockHeaderByHash(getTransactionResponse.blockHash); + // eslint-disable-next-line no-await-in-loop + await this.importBlockHeader(getBlockHeaderResponse); + delete this.pendingRequest[getTransactionResponse.blockHash.toString('hex')]; + }; + + await executor(); + + const metadata = { + blockHash: getTransactionResponse.blockHash, + height: getTransactionResponse.height, + instantLocked: getTransactionResponse.isInstantLocked, + chainLocked: getTransactionResponse.isChainLocked, + }; + + delete this.pendingRequest[transactionHash]; + + return { + transaction, + transactionHash, + metadata, + transactionResponse: getTransactionResponse, + }; +} + +module.exports = handleTransactionFromStream; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/processChunks.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/processChunks.js new file mode 100644 index 00000000000..9a6a904b810 --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/processChunks.js @@ -0,0 +1,122 @@ +/* eslint-disable no-param-reassign */ +const GrpcError = require('@dashevo/grpc-common/lib/server/error/GrpcError'); +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); +const logger = require('../../../../logger'); +const isBrowser = require('../../../../utils/isBrowser'); + +function isAnyIntersection(arrayA, arrayB) { + const intersection = arrayA.filter((e) => arrayB.indexOf(e) > -1); + return intersection.length > 0; +} + +async function processChunks(dataChunk) { + const self = this; + const addresses = this.getAddressesToSync(); + const { network } = this; + /* First check if any instant locks appeared */ + const instantLocksReceived = this.constructor.getInstantSendLocksFromResponse(dataChunk); + instantLocksReceived.forEach((isLock) => { + this.importInstantLock(isLock); + }); + + /* Incoming transactions handling */ + const transactionsFromResponse = this.constructor + .getTransactionListFromStreamResponse(dataChunk); + + const addressesTransaction = this.constructor + .filterAddressesTransactions(transactionsFromResponse, addresses, network); + + if (addressesTransaction.transactions.length) { + // Normalizing format of transaction for account.importTransactions + const addressesTransactionsWithoutMetadata = addressesTransaction.transactions + .map((tx) => [tx]); + + // When a transaction exist, there is multiple things we need to do : + // 1) The transaction itself needs to be imported + const { addressesGenerated: addressesGeneratedCount } = await self + .importTransactions(addressesTransactionsWithoutMetadata); + // 2) Transaction metadata need to be fetched and imported as well. + // as such event might happen in the future + // As we require height information, we fetch transaction using client + + const awaitingMetadataPromises = addressesTransaction.transactions + .map((transaction) => self.handleTransactionFromStream(transaction) + .then(({ + transactionResponse, + metadata, + }) => [transactionResponse.transaction, metadata])); + + Promise + .all(awaitingMetadataPromises) + .then(async (transactionsWithMetadata) => { + // Import into account + const { mostRecentHeight } = await self.importTransactions(transactionsWithMetadata); + + if (mostRecentHeight !== -1) { + this.setLastSyncedBlockHeight(mostRecentHeight, true); + } + + // Schedule save state after all chain data has been imported + this.storage.scheduleStateSave(); + }) + .catch((err) => { + logger.error('Error while importing transactions', err); + }); + + self.hasReachedGapLimit = self.hasReachedGapLimit || addressesGeneratedCount > 0; + + if (self.hasReachedGapLimit && self.stream) { + logger.silly('TransactionSyncStreamWorker - end stream - new addresses generated'); + + if (isBrowser()) { + // Under browser environment, grpc-web doesn't call error and end events + // so we call it by ourselves + await new Promise((resolveCancel) => setImmediate(() => { + self.stream.cancel(); + const error = new GrpcError(GrpcErrorCodes.CANCELLED, 'Cancelled on client'); + + // call onError events + self.stream.f.forEach((func) => func(error)); + + // call onEnd events + self.stream.c.forEach((func) => func()); + resolveCancel(); + })); + } else { + // If there are some new addresses being imported + // to the storage, that mean that we hit the gap limit + // and we need to update the bloom filter with new addresses, + // i.e. we need to open another stream with a bloom filter + // that contains new addresses. + + // DO not setting null this.stream allow to know we + // need to reset our stream (as we pass along the error) + // Wrapping `cancel` in `setImmediate` due to bug with double-free + // explained here (https://github.com/grpc/grpc-node/issues/1652) + // and here (https://github.com/nodejs/node/issues/38964) + await new Promise((resolveCancel) => setImmediate(() => { + self.stream.cancel(); + resolveCancel(); + })); + } + } + } + + /* Incoming Merkle block handling */ + const merkleBlockFromResponse = this.constructor + .getMerkleBlockFromStreamResponse(dataChunk); + + if (merkleBlockFromResponse) { + // Reverse hashes, as they're little endian in the header + const transactionsInHeader = merkleBlockFromResponse.hashes.map((hashHex) => Buffer.from(hashHex, 'hex').reverse().toString('hex')); + const transactionsInWallet = [ + ...self.storage.getChainStore(self.network).state.transactions.keys(), + ]; + const isTruePositive = isAnyIntersection(transactionsInHeader, transactionsInWallet); + if (isTruePositive) { + self.importBlockHeader(merkleBlockFromResponse.header); + } + } +} + +module.exports = processChunks; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/setLastSyncedBlockHeight.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/setLastSyncedBlockHeight.js new file mode 100644 index 00000000000..209bfe84b0f --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/setLastSyncedBlockHeight.js @@ -0,0 +1,23 @@ +/** + * Set last synced block height + * + * @param {number} blockHeight + * @param {boolean} [updateWalletState=false] + * @return {number} + */ +module.exports = function setLastSyncedBlockHeight(blockHeight, updateWalletState = false) { + if (this.lastSyncedBlockHeight >= blockHeight) { + return this.lastSyncedBlockHeight; + } + + this.lastSyncedBlockHeight = blockHeight; + + // TODO: consider getting rid of a side effect of storage update to make this a pure function + if (updateWalletState) { + const walletStore = this.storage.getWalletStore(this.walletId); + walletStore.updateLastKnownBlock(blockHeight); + this.storage.scheduleStateSave(); + } + + return blockHeight; +}; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/startHistoricalSync.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/startHistoricalSync.js new file mode 100644 index 00000000000..1586d382361 --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/startHistoricalSync.js @@ -0,0 +1,61 @@ +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); + +const logger = require('../../../../logger'); + +const GRPC_RETRY_ERRORS = [ + GrpcErrorCodes.DEADLINE_EXCEEDED, + GrpcErrorCodes.UNAVAILABLE, + GrpcErrorCodes.INTERNAL, + GrpcErrorCodes.CANCELLED, + GrpcErrorCodes.UNKNOWN, +]; + +/** + * + * @param {string} network + * @return {Promise} + */ +module.exports = async function startHistoricalSync(network) { + const bestBlockHeight = await this.getBestBlockHeightFromTransport(); + const lastSyncedBlockHeight = await this.getLastSyncedBlockHeight(); + const fromBlockHeight = lastSyncedBlockHeight > 0 ? lastSyncedBlockHeight : 1; + const count = bestBlockHeight - fromBlockHeight || 1; + const start = +new Date(); + + try { + const options = { count, network }; + options.fromBlockHeight = lastSyncedBlockHeight > 0 ? lastSyncedBlockHeight : 1; + + logger.debug(`TransactionSyncStreamWorker - HistoricalSync - Started from ${options.fromBlockHash || options.fromBlockHeight}, count: ${count}`); + const gapLimitIsReached = await this.syncUpToTheGapLimit(options); + if (gapLimitIsReached) { + await startHistoricalSync.call(this, network); + } + } catch (e) { + if (GRPC_RETRY_ERRORS.includes(e.code)) { + if (this.stream === null && e.code === GrpcErrorCodes.CANCELLED) { + // NOOP on self canceled state (via stop worker) + logger.debug('TransactionSyncStreamWorker - HistoricalSync - The Worker is stopped'); + return; + } + + logger.debug('TransactionSyncStreamWorker - HistoricalSync - Restarting the stream'); + + this.stream = null; + await startHistoricalSync.call(this, network); + + return; + } + + this.stream = null; + this.emit('error', e, { + type: 'plugin', + pluginType: 'worker', + pluginName: this.name, + }); + } + + this.setLastSyncedBlockHeight(bestBlockHeight, true); + + logger.debug(`TransactionSyncStreamWorker - HistoricalSync - Synchronized ${count} in ${+new Date() - start}ms`); +}; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/startIncomingSync.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/startIncomingSync.js new file mode 100644 index 00000000000..66a911c063d --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/startIncomingSync.js @@ -0,0 +1,50 @@ +const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); + +const logger = require('../../../../logger'); + +const GRPC_RETRY_ERRORS = [ + GrpcErrorCodes.DEADLINE_EXCEEDED, + GrpcErrorCodes.UNAVAILABLE, + GrpcErrorCodes.INTERNAL, + GrpcErrorCodes.CANCELLED, + GrpcErrorCodes.UNKNOWN, +]; + +module.exports = async function startIncomingSync() { + const { network } = this; + const lastSyncedBlockHeight = await this.getLastSyncedBlockHeight(); + const count = 0; + + try { + const options = { count, network }; + options.fromBlockHeight = lastSyncedBlockHeight > 0 ? lastSyncedBlockHeight : 1; + + await this.syncUpToTheGapLimit(options); + // The method above resolves only in two cases: the limit is reached or the server is closed. + // In both cases, the stream needs to be restarted, unless syncIncomingTransactions is + // set to false, which is signalling the worker not to restart stream. + if (this.syncIncomingTransactions) { + logger.debug(`TransactionSyncStreamWorker - IncomingSync - Restarted from height: ${lastSyncedBlockHeight}`); + + await startIncomingSync.call(this); + } + } catch (e) { + this.stream = null; + + if (GRPC_RETRY_ERRORS.includes(e.code)) { + logger.debug(`TransactionSyncStreamWorker - IncomingSync - Restarted from height: ${lastSyncedBlockHeight}`); + + if (this.syncIncomingTransactions) { + await startIncomingSync.call(this); + } + + return; + } + + this.emit('error', e, { + type: 'plugin', + pluginType: 'worker', + pluginName: this.name, + }); + } +}; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/syncUpToTheGapLimit.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/syncUpToTheGapLimit.js new file mode 100644 index 00000000000..bfb469d8fe8 --- /dev/null +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/syncUpToTheGapLimit.js @@ -0,0 +1,59 @@ +const logger = require('../../../../logger'); +const onStreamEnd = require('../handlers/onStreamEnd'); +const onStreamError = require('../handlers/onStreamError'); +const onStreamData = require('../handlers/onStreamData'); +const Queue = require('../../../../utils/Queue/Queue'); +/** + * + * @param options + * @param {string} [options.fromBlockHash] + * @param {number} count + * @param {string} network + * @param {number} [options.fromBlockHeight] + * @return {Promise} + */ +module.exports = async function syncUpToTheGapLimit({ + fromBlockHash, count, network, fromBlockHeight, +}) { + const self = this; + const addresses = this.getAddressesToSync(); + self.addresses = addresses; + logger.debug(`syncing up to the gap limit: - from block: ${fromBlockHash || fromBlockHeight} Count: ${count}`); + + if (fromBlockHash == null && fromBlockHeight == null) { + throw new Error('fromBlockHash ot fromBlockHeight should be present'); + } + + const options = { count }; + if (fromBlockHash != null) { + options.fromBlockHash = fromBlockHash; + } else { + options.fromBlockHeight = fromBlockHeight; + } + + const stream = await this.transport + .subscribeToTransactionsWithProofs(addresses, options); + + if (self.stream) { + throw new Error('Limited to one stream at the same time.'); + } + self.stream = stream; + self.network = network; + self.hasReachedGapLimit = false; + // The order is important, however, some async calls are being performed + // in order to additionally fetch metadata for each valid tx chunks. + // We therefore need to temporarily store chunks for handling. + self.chunksQueue = new Queue(); + + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + // handler for error being thrown for job processing + self.chunksQueue.on('error', (error) => { + reject(error); + }); + stream + .on('data', (data) => onStreamData(self, data)) + .on('error', (error) => onStreamError(error, reject)) + .on('end', () => onStreamEnd(self, resolve)); + }); +}; diff --git a/packages/wallet-lib/src/plugins/index.js b/packages/wallet-lib/src/plugins/index.js new file mode 100644 index 00000000000..ebad2cfcf18 --- /dev/null +++ b/packages/wallet-lib/src/plugins/index.js @@ -0,0 +1,6 @@ +const Worker = require('./Worker'); +const StandardPlugin = require('./StandardPlugin'); + +module.exports = { + StandardPlugin, Worker, +}; diff --git a/packages/wallet-lib/src/test/.eslintrc b/packages/wallet-lib/src/test/.eslintrc new file mode 100644 index 00000000000..4c2b11fe817 --- /dev/null +++ b/packages/wallet-lib/src/test/.eslintrc @@ -0,0 +1,9 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + "import/no-extraneous-dependencies": "off" + } +} diff --git a/packages/wallet-lib/src/test/bootstrap.js b/packages/wallet-lib/src/test/bootstrap.js new file mode 100644 index 00000000000..4bf8023b43c --- /dev/null +++ b/packages/wallet-lib/src/test/bootstrap.js @@ -0,0 +1,25 @@ +const { use } = require('chai'); +const path = require('path'); +const dotenvSafe = require('dotenv-safe'); +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); +const chaiAsPromised = require('chai-as-promised'); + +use(sinonChai); +use(chaiAsPromised); + +dotenvSafe.config({ + path: path.resolve(__dirname, '..', '..', '.env'), +}); + +beforeEach(function beforeEach() { + if (!this.sinonSandbox) { + this.sinonSandbox = sinon.createSandbox(); + } else { + this.sinonSandbox.restore(); + } +}); + +afterEach(function afterEach() { + this.sinonSandbox.restore(); +}); diff --git a/packages/wallet-lib/src/test/karma/bootstrap.js b/packages/wallet-lib/src/test/karma/bootstrap.js new file mode 100644 index 00000000000..ab65943e149 --- /dev/null +++ b/packages/wallet-lib/src/test/karma/bootstrap.js @@ -0,0 +1,21 @@ +const { expect, use } = require('chai'); +const sinon = require('sinon'); +const chaiAsPromised = require('chai-as-promised'); +const sinonChai = require('sinon-chai'); + +use(chaiAsPromised); +use(sinonChai); + +beforeEach(function beforeEach() { + if (!this.sinonSandbox) { + this.sinonSandbox = sinon.createSandbox(); + } else { + this.sinonSandbox.restore(); + } +}); + +afterEach(function afterEach() { + this.sinonSandbox.restore(); +}); + +global.expect = expect; diff --git a/packages/wallet-lib/src/test/karma/loader.js b/packages/wallet-lib/src/test/karma/loader.js new file mode 100644 index 00000000000..03aaa20c9cc --- /dev/null +++ b/packages/wallet-lib/src/test/karma/loader.js @@ -0,0 +1,8 @@ +// This file is used for compiling tests with webpack into one file for using with karma +require('./bootstrap'); + +const testsContext = require.context('../../../src', true, /spec.js$/); +const integrationTestsContext = require.context('../../../tests/integration', true, /spec.js$/); + +testsContext.keys().forEach(testsContext); +integrationTestsContext.keys().forEach(integrationTestsContext); diff --git a/packages/wallet-lib/src/test/mocks/LocalForageAdapterMock.js b/packages/wallet-lib/src/test/mocks/LocalForageAdapterMock.js new file mode 100644 index 00000000000..818dcc61a00 --- /dev/null +++ b/packages/wallet-lib/src/test/mocks/LocalForageAdapterMock.js @@ -0,0 +1,20 @@ +class LocalForageAdapterMock { + constructor() { + this.isConfig = false; + this.keys = {}; + } + + config() { + this.isConfig = true; + } + + setItem(key, item) { + this.keys[key] = JSON.stringify(item); + return item; + } + + getItem(key) { + return this.keys[key] ? JSON.parse(this.keys[key]) : null; + } +} +module.exports = LocalForageAdapterMock; diff --git a/packages/wallet-lib/src/test/mocks/TransportMock.js b/packages/wallet-lib/src/test/mocks/TransportMock.js new file mode 100644 index 00000000000..2f8b06dcc72 --- /dev/null +++ b/packages/wallet-lib/src/test/mocks/TransportMock.js @@ -0,0 +1,30 @@ +const EventEmitter = require('events'); +const getStatus = require('../../transport/FixtureTransport/methods/getStatus'); + +class TransportMock extends EventEmitter { + constructor(sinonSandbox, transactionStreamMock) { + super(); + this.sinonSandbox = sinonSandbox; + + this.getBestBlockHeight = sinonSandbox.stub().returns(42); + this.subscribeToTransactionsWithProofs = sinonSandbox.stub().returns(transactionStreamMock); + this.getBlockHeaderByHeight = sinonSandbox.stub() + .returns({ + hash: '000000059885815cfc06ba74b814200d29658394dbe5d1e93948a8587947747b', + version: 536870912, + prevHash: '000000c520efd2047f0b6f0c1c75e0382f8a9b7d76bb140bde3ada10c62e8b0d', + merkleRoot: 'ef292bfb7965402e57dfeb4ee8bad0055c216c4c5a4e549a0ac17a393ae8617b', + time: 1638950949, + bits: 503385436, + nonce: 351770, + }); + this.subscribeToBlocks = sinonSandbox.stub(); + this.getIdentitiesByPublicKeyHashes = sinonSandbox.stub().returns([]); + this.sendTransaction = sinonSandbox.stub(); + this.getTransaction = sinonSandbox.stub(); + this.getBlockHeaderByHash = sinonSandbox.stub(); + this.getStatus = sinonSandbox.stub().resolves(getStatus.call(this)); + } +} + +module.exports = TransportMock; diff --git a/packages/wallet-lib/src/test/mocks/TxStreamDataResponseMock.js b/packages/wallet-lib/src/test/mocks/TxStreamDataResponseMock.js new file mode 100644 index 00000000000..cea17f96034 --- /dev/null +++ b/packages/wallet-lib/src/test/mocks/TxStreamDataResponseMock.js @@ -0,0 +1,43 @@ +class TxStreamDataResponseMock { + /** + * + * @param options + * @param {Buffer} [options.rawMerkleBlock] + * @param {Buffer[]} [options.rawTransactions] + */ + constructor({ rawMerkleBlock, rawTransactions, instantSendLockMessages }) { + this.rawMerkleBlock = rawMerkleBlock; + this.rawTransactions = rawTransactions; + this.instantSendLockMessages = instantSendLockMessages; + } + + /** + * @return {Buffer} + */ + getRawMerkleBlock() { + return this.rawMerkleBlock; + } + + /** + * @return {{getTransactionsList: (): Buffer[]}} + */ + getRawTransactions() { + const { rawTransactions } = this; + return { + getTransactionsList() { + return rawTransactions || []; + }, + }; + } + + getInstantSendLockMessages() { + const { instantSendLockMessages } = this; + return { + getMessagesList() { + return instantSendLockMessages || []; + }, + }; + } +} + +module.exports = TxStreamDataResponseMock; diff --git a/packages/wallet-lib/src/test/mocks/TxStreamMock.js b/packages/wallet-lib/src/test/mocks/TxStreamMock.js new file mode 100644 index 00000000000..bcde9fe36f9 --- /dev/null +++ b/packages/wallet-lib/src/test/mocks/TxStreamMock.js @@ -0,0 +1,43 @@ +const EventEmitter = require('events'); +const TxStreamDataResponseMock = require('./TxStreamDataResponseMock'); + +class TxStreamMock extends EventEmitter { + constructor() { + super(); + + // onError minified events list + this.f = []; + // onEnd minified events list + this.c = []; + } + + cancel() { + const err = new Error(); + err.code = 2; + this.emit(TxStreamMock.EVENTS.error, err); + } + + end() { + this.emit('end'); + this.removeAllListeners(); + } + + sendTransactions(transactions) { + this.emit(TxStreamMock.EVENTS.data, new TxStreamDataResponseMock({ + rawTransactions: transactions.map((tx) => tx.toBuffer()), + })); + } + + finish() { + this.emit(TxStreamMock.EVENTS.end); + } +} + +TxStreamMock.EVENTS = { + cancel: 'cancel', + data: 'data', + end: 'end', + error: 'error', +}; + +module.exports = TxStreamMock; diff --git a/packages/wallet-lib/src/test/mocks/createAndAttachTransportMocksToWallet.js b/packages/wallet-lib/src/test/mocks/createAndAttachTransportMocksToWallet.js new file mode 100644 index 00000000000..c97c711b197 --- /dev/null +++ b/packages/wallet-lib/src/test/mocks/createAndAttachTransportMocksToWallet.js @@ -0,0 +1,20 @@ +const TxStreamMock = require('./TxStreamMock'); +const TransportMock = require('./TransportMock'); + +module.exports = async function createAndAttachTransportMocksToWallet(wallet, sinon) { + const txStreamMock = new TxStreamMock(); + const transportMock = new TransportMock(sinon, txStreamMock); + + // eslint-disable-next-line no-param-reassign + wallet.transport = transportMock; + + const accountSyncPromise = wallet.getAccount(); + // Breaking the event loop to start wallet syncing + await new Promise((resolve) => setTimeout(resolve, 0)); + // Emitting tx stream end to make wallet sync finish + txStreamMock.emit(TxStreamMock.EVENTS.end); + // Waiting for wallet to sync + await accountSyncPromise; + + return { txStreamMock, transportMock }; +}; diff --git a/packages/wallet-lib/src/test/mocks/createTransactionInAccount.js b/packages/wallet-lib/src/test/mocks/createTransactionInAccount.js new file mode 100644 index 00000000000..3efca85848d --- /dev/null +++ b/packages/wallet-lib/src/test/mocks/createTransactionInAccount.js @@ -0,0 +1,27 @@ +const { Transaction } = require('@dashevo/dashcore-lib'); + +/** + * Creates a mocked transaction in the wallet that can be used to perform various tests + * @param {Account} account + * @return {Promise} + */ +async function createTransactionInAccount(account) { + // add fake tx to the wallet so it will be able to create transactions + const walletTransaction = new Transaction(undefined) + .from([{ + amount: 150000, + script: '76a914f9996443a7d5e2694560f8715e5e8fe602133c6088ac', + outputIndex: 0, + txid: new Transaction(undefined).hash, + }]) + .to(account.getAddress(10).address, 100000); + + await account.importTransactions([[walletTransaction.serialize(true), { + height: 100, + blockHash: '0000000000000000000000000000000000000000000000000000000000000000', + }]]); + // console.log(account.storage.wallets.get('361032c8a0').state.paths) + return walletTransaction; +} + +module.exports = createTransactionInAccount; diff --git a/packages/wallet-lib/src/test/utils.js b/packages/wallet-lib/src/test/utils.js new file mode 100644 index 00000000000..14fd883c7c3 --- /dev/null +++ b/packages/wallet-lib/src/test/utils.js @@ -0,0 +1,14 @@ +const waitOneTick = () => new Promise((resolve) => { + if (typeof setImmediate === 'undefined') { + setTimeout(resolve, 10); + } else { + setImmediate(resolve); + } +}); + +const wait = (timeout) => new Promise(((resolve) => setTimeout(resolve, timeout))); + +module.exports = { + waitOneTick, + wait, +}; diff --git a/packages/wallet-lib/src/transport/AbstractTransport.js b/packages/wallet-lib/src/transport/AbstractTransport.js new file mode 100644 index 00000000000..76633f5d534 --- /dev/null +++ b/packages/wallet-lib/src/transport/AbstractTransport.js @@ -0,0 +1,64 @@ +const EventEmitter = require('events'); + +const EVENTS = require('../EVENTS'); +const logger = require('../logger'); + +/** + * @abstract + */ +class AbstractTransport extends EventEmitter { + constructor() { + super(); + + this.state = { + block: null, + blockHeaders: null, + // Executors are Interval + executors: { + blocks: null, + blockHeaders: null, + addresses: null, + }, + addressesTransactionsMap: {}, + subscriptions: { + addresses: {}, + }, + }; + } + + announce(eventName, args) { + logger.silly(`Transporter.announce(${eventName})`); + switch (eventName) { + case EVENTS.BLOCKHEADER: + case EVENTS.BLOCKHEIGHT_CHANGED: + case EVENTS.BLOCK: + case EVENTS.TRANSACTION: + case EVENTS.FETCHED_TRANSACTION: + case EVENTS.FETCHED_ADDRESS: + this.emit(eventName, { type: eventName, payload: args }); + break; + default: + this.emit(eventName, { type: eventName, payload: args }); + logger.warn('Transporter - Not implemented, announce of ', eventName, args); + } + } + + disconnect() { + const { executors, subscriptions } = this.state; + + clearInterval(subscriptions.blocks); + clearInterval(subscriptions.blockHeaders); + + // eslint-disable-next-line guard-for-in,no-restricted-syntax + for (const addr in subscriptions.addresses) { + clearInterval(addr); + delete this.state.subscriptions.addresses[addr]; + } + + clearInterval(executors.blocks); + clearInterval(executors.blockHeaders); + clearInterval(executors.addresses); + } +} + +module.exports = AbstractTransport; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/DAPIClientTransport.js b/packages/wallet-lib/src/transport/DAPIClientTransport/DAPIClientTransport.js new file mode 100644 index 00000000000..e3a68ca7943 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/DAPIClientTransport.js @@ -0,0 +1,30 @@ +const AbstractTransport = require('../AbstractTransport'); + +/** + * @implements {Transport} + */ +class DAPIClientTransport extends AbstractTransport { + constructor(client) { + super(); + + this.client = client; + } +} + +DAPIClientTransport.prototype.getBestBlock = require('./methods/getBestBlock'); +DAPIClientTransport.prototype.getBestBlockHeader = require('./methods/getBestBlockHeader'); +DAPIClientTransport.prototype.getBestBlockHash = require('./methods/getBestBlockHash'); +DAPIClientTransport.prototype.getBestBlockHeight = require('./methods/getBestBlockHeight'); +DAPIClientTransport.prototype.getBlockByHash = require('./methods/getBlockByHash'); +DAPIClientTransport.prototype.getBlockByHeight = require('./methods/getBlockByHeight'); +DAPIClientTransport.prototype.getBlockHeaderByHash = require('./methods/getBlockHeaderByHash'); +DAPIClientTransport.prototype.getBlockHeaderByHeight = require('./methods/getBlockHeaderByHeight'); +DAPIClientTransport.prototype.getStatus = require('./methods/getStatus'); +DAPIClientTransport.prototype.getTransaction = require('./methods/getTransaction'); +DAPIClientTransport.prototype.sendTransaction = require('./methods/sendTransaction'); +DAPIClientTransport.prototype.subscribeToBlockHeaders = require('./methods/subscribeToBlockHeaders'); +DAPIClientTransport.prototype.subscribeToBlocks = require('./methods/subscribeToBlocks'); +DAPIClientTransport.prototype.getIdentitiesByPublicKeyHashes = require('./methods/getIdentitiesByPublicKeyHashes'); +DAPIClientTransport.prototype.subscribeToTransactionsWithProofs = require('./methods/subscribeToTransactionsWithProofs'); + +module.exports = DAPIClientTransport; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlock.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlock.js new file mode 100644 index 00000000000..8ea7095c52f --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlock.js @@ -0,0 +1,7 @@ +const logger = require('../../../logger'); + +module.exports = async function getBestBlock() { + logger.silly('DAPIClientTransport.getBestBlock'); + + return this.getBlockByHash(await this.getBestBlockHash()); +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlock.spec.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlock.spec.js new file mode 100644 index 00000000000..0026b712808 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlock.spec.js @@ -0,0 +1,43 @@ +const { expect } = require('chai'); +const { Block } = require('@dashevo/dashcore-lib'); + +const DAPIClientTransport = require('../DAPIClientTransport'); + +describe('transports - DAPIClientTransport - .getBestBlock', function suite() { + let bestBlockHash; + let block; + let transport; + let clientMock; + + beforeEach(() => { + bestBlockHash = '0000004bb65f29621dddcb85eb0d4aa3921e856097813b00d7784514809968ad'; + block = { + header: { + hash: '0000004bb65f29621dddcb85eb0d4aa3921e856097813b00d7784514809968ad', version: 536870912, prevHash: '000002243e872509388a6bd9c1c69c719bdcee2a780262f00c3cf75060f7adae', merkleRoot: '89724abcb2132645cffa8fdce002d9ced6d59e35231eaa0b3ddaf69f6c4e5c84', time: 1585673611, bits: 503479478, nonce: 24664, + }, + transactions: [], + }; + + clientMock = { + core: { + getBestBlockHash: () => bestBlockHash, + getBlockByHash: (hash) => { + if (hash === bestBlockHash) return block; + return null; + }, + }, + } + + transport = new DAPIClientTransport(clientMock); + }) + + afterEach(() => { + transport.disconnect(); + }) + + it('should work', async () => { + const res = await transport.getBestBlock(); + + expect(res).to.deep.equal(new Block(block)); + }); +}); diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHash.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHash.js new file mode 100644 index 00000000000..6fdd38fa737 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHash.js @@ -0,0 +1,7 @@ +const logger = require('../../../logger'); + +module.exports = async function getBestBlockHash() { + logger.silly('DAPIClientTransport.getBestBlockHash'); + + return this.client.core.getBestBlockHash(); +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHash.spec.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHash.spec.js new file mode 100644 index 00000000000..d75034a4462 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHash.spec.js @@ -0,0 +1,31 @@ +const { expect } = require('chai'); + +const DAPIClientTransport = require('../DAPIClientTransport'); + +describe('transports - DAPIClientTransport - .getBestBlockHash', function suite() { + let fixture; + let transport; + let clientMock; + + beforeEach(() => { + fixture = '0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9'; + + clientMock = { + core: { + getBestBlockHash: () => fixture, + } + } + + transport = new DAPIClientTransport(clientMock); + }) + + afterEach(() => { + transport.disconnect(); + }) + + it('should work', async () => { + const res = await transport.getBestBlockHash(); + + expect(res).to.deep.equal(fixture); + }); +}); diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHeader.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHeader.js new file mode 100644 index 00000000000..2a81ceb492c --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHeader.js @@ -0,0 +1,7 @@ +const logger = require('../../../logger'); + +module.exports = async function getBestBlockHeader() { + logger.silly('DAPIClientTransport.getBestBlockHeader'); + + return this.getBlockHeaderByHash(await this.getBestBlockHash()); +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHeader.spec.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHeader.spec.js new file mode 100644 index 00000000000..bf034065eee --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHeader.spec.js @@ -0,0 +1,54 @@ +const { expect } = require('chai'); +const { Block } = require('@dashevo/dashcore-lib'); + +const DAPIClientTransport = require('../DAPIClientTransport'); + +describe('transports - DAPIClientTransport - .getBestBlockHeader', function suite() { + let bestBlockHash; + let block; + let transport; + let clientMock; + + beforeEach(() => { + bestBlockHash = '0000004bb65f29621dddcb85eb0d4aa3921e856097813b00d7784514809968ad'; + block = { + header: { + hash: '0000004bb65f29621dddcb85eb0d4aa3921e856097813b00d7784514809968ad', version: 536870912, prevHash: '000002243e872509388a6bd9c1c69c719bdcee2a780262f00c3cf75060f7adae', merkleRoot: '89724abcb2132645cffa8fdce002d9ced6d59e35231eaa0b3ddaf69f6c4e5c84', time: 1585673611, bits: 503479478, nonce: 24664, + }, + transactions: [{ + hash: '89724abcb2132645cffa8fdce002d9ced6d59e35231eaa0b3ddaf69f6c4e5c84', + version: 3, + inputs: [{ + prevTxId: '0000000000000000000000000000000000000000000000000000000000000000', outputIndex: 4294967295, sequenceNumber: 4294967295, script: '028c300109', + }], + outputs: [{ satoshis: 6885000000, script: '76a91416b93a3b9168a20605cc3cda62f6135a3baa531a88ac' }, { satoshis: 6885000000, script: '76a91416b93a3b9168a20605cc3cda62f6135a3baa531a88ac' }], + nLockTime: 0, + type: 5, + extraPayload: '02008c300000cead425668f38cfbb8dc028ad53d163fcee7282ede84d9a577ac6851a847ebc80000000000000000000000000000000000000000000000000000000000000000', + }], + }; + + + clientMock = { + core: { + getBestBlockHash: () => bestBlockHash, + getBlockByHash: (hash) => { + if (hash === bestBlockHash) return block; + return null; + }, + } + } + + transport = new DAPIClientTransport(clientMock); + }) + + afterEach(() => { + transport.disconnect(); + }) + + it('should work', async () => { + const res = await transport.getBestBlockHeader(); + + expect(res).to.deep.equal(new Block(block).header); + }); +}); diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHeight.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHeight.js new file mode 100644 index 00000000000..ffabc94492d --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHeight.js @@ -0,0 +1,10 @@ +const logger = require('../../../logger'); + +module.exports = async function getBestBlockHeight() { + logger.silly('DAPIClientTransport.getBestBlockHeight'); + + // Previously we would have done getBlock(hash).height + const { chain: { blocksCount } } = await this.getStatus(); + + return blocksCount; +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHeight.spec.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHeight.spec.js new file mode 100644 index 00000000000..298d867090c --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBestBlockHeight.spec.js @@ -0,0 +1,33 @@ +const { expect } = require('chai'); + +const DAPIClientTransport = require('../DAPIClientTransport'); + +const getStatus = require('../../FixtureTransport/methods/getStatus'); + +describe('transports - DAPIClientTransport - .getBestBlockHeight', function suite() { + let fixture; + let transport; + let clientMock; + + beforeEach(() => { + fixture = getStatus(); + + clientMock = { + core: { + getStatus: () => fixture, + } + } + + transport = new DAPIClientTransport(clientMock); + }) + + afterEach(() => { + transport.disconnect(); + }) + + it('should work', async () => { + const res = await transport.getBestBlockHeight(); + + expect(res).to.deep.equal(fixture.blocks); + }); +}); diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockByHash.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockByHash.js new file mode 100644 index 00000000000..260fd6fe72f --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockByHash.js @@ -0,0 +1,8 @@ +const { Block } = require('@dashevo/dashcore-lib'); +const logger = require('../../../logger'); + +module.exports = async function getBlockByHash(blockHash) { + logger.silly(`DAPIClient.getBlockByHash[${blockHash}]`); + + return new Block(await this.client.core.getBlockByHash(blockHash)); +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockByHash.spec.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockByHash.spec.js new file mode 100644 index 00000000000..bd0a3db723d --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockByHash.spec.js @@ -0,0 +1,31 @@ +const { expect } = require('chai'); + +const DAPIClientTransport = require('../DAPIClientTransport'); + +describe('transports - DAPIClientTransport - .getBlockByHash', function suite() { + let fixture; + let transport; + let clientMock; + + beforeEach(() => { + fixture = '00000020e2bddfb998d7be4cc4c6b126f04d6e4bd201687523ded527987431707e0200005520320b4e263bec33e08944656f7ce17efbc2c60caab7c8ed8a73d413d02d3a169d555ecdd6021e56d000000203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff050219250102ffffffff0240c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac40c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac0000000046020019250000476416132511031b71167f4bb7658eab5c3957d79636767f83e0e18e2b9ed7f8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd4901010019250000010001d02e9ee1b14c022ad6895450f3375a8e9a87f214912d4332fa997996d2000000320000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; + + clientMock = { + core: { + getBlockByHash: () => new Buffer.from(fixture, 'hex'), + } + } + + transport = new DAPIClientTransport(clientMock); + }) + + afterEach(() => { + transport.disconnect(); + }) + + it('should work', async () => { + const res = await transport.getBlockByHash('0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9'); + + expect(res.hash).to.equal('0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9'); + }); +}); diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockByHeight.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockByHeight.js new file mode 100644 index 00000000000..67323259e2b --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockByHeight.js @@ -0,0 +1,8 @@ +const { Block } = require('@dashevo/dashcore-lib'); +const logger = require('../../../logger'); + +module.exports = async function getBlockByHeight(height) { + logger.silly(`DAPIClient.getBlockByHeight[${height}]`); + + return new Block(await this.client.core.getBlockByHeight(height)); +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockByHeight.spec.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockByHeight.spec.js new file mode 100644 index 00000000000..de384167ff3 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockByHeight.spec.js @@ -0,0 +1,31 @@ +const { expect } = require('chai'); + +const DAPIClientTransport = require('../DAPIClientTransport'); + +describe('transports - DAPIClientTransport - .getBlockByHeight', function suite() { + let fixture; + let transport; + let clientMock; + + beforeEach(() => { + fixture = '0000002008f7ac5b0e2df33ac233fef59549075ed24aa893ffc1d7b7067256da420000006670782820f19b64f011c55815c9315946573ac92bd5cce6deda684edcba1472c1904e5eae0d021e953d00000103000500010000000000000000000000000000000000000000000000000000000000000000ffffffff050238180101ffffffff0240c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac40c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac0000000046020038180000476416132511031b71167f4bb7658eab5c3957d79636767f83e0e18e2b9ed7f80000000000000000000000000000000000000000000000000000000000000000'; + + clientMock = { + core: { + getBlockByHeight: () => new Buffer.from(fixture, 'hex'), + } + } + + transport = new DAPIClientTransport(clientMock); + }) + + afterEach(() => { + transport.disconnect(); + }) + + it('should work', async () => { + const res = await transport.getBlockByHeight(6200); + + expect(res.hash).to.equal('000000c33ad38337e9bf648842f3cc08b146739d561ce468bd373ee815595436'); + }); +}); diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockHeaderByHash.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockHeaderByHash.js new file mode 100644 index 00000000000..270d79f0b86 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockHeaderByHash.js @@ -0,0 +1,7 @@ +const logger = require('../../../logger'); + +module.exports = async function getBlockHeaderByHash(blockHash) { + logger.silly(`DAPIClient.getBlockHeaderByHash[${blockHash}]`); + + return (await this.getBlockByHash(blockHash)).header; +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockHeaderByHash.spec.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockHeaderByHash.spec.js new file mode 100644 index 00000000000..1c42d4f0f68 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockHeaderByHash.spec.js @@ -0,0 +1,34 @@ +const { expect } = require('chai'); + +const DAPIClientTransport = require('../DAPIClientTransport'); + +describe('transports - DAPIClientTransport .getBlockHeaderByHash', function suite() { + let fixture; + let transport; + let clientMock; + + beforeEach(() => { + fixture = '00000020e2bddfb998d7be4cc4c6b126f04d6e4bd201687523ded527987431707e0200005520320b4e263bec33e08944656f7ce17efbc2c60caab7c8ed8a73d413d02d3a169d555ecdd6021e56d000000203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff050219250102ffffffff0240c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac40c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac0000000046020019250000476416132511031b71167f4bb7658eab5c3957d79636767f83e0e18e2b9ed7f8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd4901010019250000010001d02e9ee1b14c022ad6895450f3375a8e9a87f214912d4332fa997996d2000000320000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; + + clientMock = { + core: { + getBlockByHash: () => new Buffer.from(fixture, 'hex'), + } + } + + transport = new DAPIClientTransport(clientMock); + }) + + afterEach(() => { + transport.disconnect(); + }) + + + it('should work', async () => { + const res = await transport.getBlockHeaderByHash('0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9'); + + expect(res.hash).to.equal('0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9'); + expect(res.nonce).to.equal(53334); + expect(res.timestamp).to.equal(1582669078); + }); +}); diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockHeaderByHeight.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockHeaderByHeight.js new file mode 100644 index 00000000000..8be7557c412 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockHeaderByHeight.js @@ -0,0 +1,6 @@ +const logger = require('../../../logger'); + +module.exports = async function getBlockHeaderByHeight(blockHeight) { + logger.silly(`DAPIClient.getBlockHeaderByHeight[${blockHeight}]`); + return (await this.getBlockByHeight(blockHeight)).header; +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockHeaderByHeight.spec.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockHeaderByHeight.spec.js new file mode 100644 index 00000000000..80679fbc016 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getBlockHeaderByHeight.spec.js @@ -0,0 +1,34 @@ +const { expect } = require('chai'); + +const DAPIClientTransport = require('../DAPIClientTransport'); + + +describe('transports - DAPIClientTransport .getBlockHeaderByHash', function suite() { + let fixture; + let transport; + let clientMock; + + beforeEach(() => { + fixture = '0000002008f7ac5b0e2df33ac233fef59549075ed24aa893ffc1d7b7067256da420000006670782820f19b64f011c55815c9315946573ac92bd5cce6deda684edcba1472c1904e5eae0d021e953d00000103000500010000000000000000000000000000000000000000000000000000000000000000ffffffff050238180101ffffffff0240c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac40c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac0000000046020038180000476416132511031b71167f4bb7658eab5c3957d79636767f83e0e18e2b9ed7f80000000000000000000000000000000000000000000000000000000000000000'; + + clientMock = { + core: { + getBlockByHeight: () => new Buffer.from(fixture, 'hex'), + } + } + + transport = new DAPIClientTransport(clientMock); + }) + + afterEach(() => { + transport.disconnect(); + }) + + it('should work', async () => { + const res = await transport.getBlockHeaderByHeight(6200); + + expect(res.hash).to.equal('000000c33ad38337e9bf648842f3cc08b146739d561ce468bd373ee815595436'); + expect(res.nonce).to.equal(15765); + expect(res.timestamp).to.equal(1582207169); + }); +}); diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getIdentitiesByPublicKeyHashes.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getIdentitiesByPublicKeyHashes.js new file mode 100644 index 00000000000..9b81591779f --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getIdentitiesByPublicKeyHashes.js @@ -0,0 +1,15 @@ +const logger = require('../../../logger'); + +/** + * @param {Buffer[]} publicKeyHashes + * @return {Promise} + */ +module.exports = async function getIdentitiesByPublicKeyHashes(publicKeyHashes) { + logger.silly('DAPIClientTransport.getIdentitiesByPublicKeyHashes'); + + const response = await this.client.platform.getIdentitiesByPublicKeyHashes( + publicKeyHashes, + ); + + return response.getIdentities(); +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getStatus.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getStatus.js new file mode 100644 index 00000000000..0e1d021a2ea --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getStatus.js @@ -0,0 +1,7 @@ +const logger = require('../../../logger'); + +module.exports = async function getStatus() { + logger.silly('DAPIClientTransport.getStatus'); + + return this.client.core.getStatus(); +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getStatus.spec.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getStatus.spec.js new file mode 100644 index 00000000000..fd43867d538 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getStatus.spec.js @@ -0,0 +1,33 @@ +const { expect } = require('chai'); + +const DAPIClientTransport = require('../DAPIClientTransport'); + +describe('transports - DAPIClientTransport - .getStatus', function suite() { + let fixture; + let transport; + let clientMock; + + beforeEach(() => { + fixture = { + coreVersion: 150000, protocolVersion: 70216, blocks: 9495, timeOffset: 0, connections: 16, proxy: '', difficulty: 0.001447319555790497, testnet: false, relayFee: 0.00001, errors: '', network: 'testnet', + }; + + clientMock = { + core: { + getStatus: () => fixture, + } + } + + transport = new DAPIClientTransport(clientMock); + }) + + afterEach(() => { + transport.disconnect(); + }) + + it('should work', async () => { + const res = await transport.getStatus(); + + expect(res).to.deep.equal(fixture); + }); +}); diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getTransaction.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getTransaction.js new file mode 100644 index 00000000000..936076c91b4 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getTransaction.js @@ -0,0 +1,37 @@ +const { Transaction } = require('@dashevo/dashcore-lib'); +const NotFoundError = require('@dashevo/dapi-client/lib/transport/GrpcTransport/errors/NotFoundError'); +const { is } = require('../../../utils'); +const logger = require('../../../logger'); + +/** + * @param {string} txid + * @returns {Promise} + */ +module.exports = async function getTransaction(txid) { + logger.silly(`DAPIClient.getTransaction[${txid}]`); + if (!is.txid(txid)) { + throw new Error(`Received an invalid txid to fetch : ${txid}`); + } + try { + const response = await this.client.core.getTransaction(txid); + const { + height, + instantLocked, + chainLocked, + } = response; + + return { + transaction: new Transaction(response.getTransaction()), + blockHash: response.getBlockHash().toString('hex'), + height, + instantLocked, + chainLocked, + }; + } catch (e) { + if (e instanceof NotFoundError) { + return null; + } + + throw e; + } +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getTransaction.spec.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getTransaction.spec.js new file mode 100644 index 00000000000..bb2dea976fc --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/getTransaction.spec.js @@ -0,0 +1,52 @@ +const { expect } = require('chai'); + +const DAPIClientTransport = require('../DAPIClientTransport'); +const NotFoundError = require('@dashevo/dapi-client/lib/transport/GrpcTransport/errors/NotFoundError'); +const GetTransactionResponse = require('@dashevo/dapi-client/lib/methods/core/getTransaction/GetTransactionResponse'); + +describe('transports - DAPIClientTransport .getTransaction', function suite() { + let fixture; + let transport; + let clientMock; + + beforeEach(() => { + fixture = '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502b924010effffffff0240c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac40c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200b9240000476416132511031b71167f4bb7658eab5c3957d79636767f83e0e18e2b9ed7f80000000000000000000000000000000000000000000000000000000000000000'; + + clientMock = { + core: { + getTransaction: () => { + return new GetTransactionResponse({ + transaction: new Buffer.from(fixture, 'hex'), + blockHash: Buffer.from('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176', 'hex'), + height: 42, + confirmations: 10, + isInstantLocked: true, + isChainLocked: false, + }); + }, + } + } + + transport = new DAPIClientTransport(clientMock); + }) + + afterEach(() => { + transport.disconnect(); + }) + + it('should work', async () => { + const res = await transport.getTransaction('2c0ee853b91b23d881f96f0128bbb5ebb90c9ef7e7bdb4eda360b0e5abf97239'); + expect(res.transaction.hash).to.equal('2c0ee853b91b23d881f96f0128bbb5ebb90c9ef7e7bdb4eda360b0e5abf97239'); + expect(res.blockHash).to.equal('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176'); + expect(res.height).to.equal(42); + expect(res.instantLocked).to.equal(true); + expect(res.chainLocked).to.equal(false); + }); + + it('should return null if transaction if not found', async () => { + clientMock.core.getTransaction = () => { throw new NotFoundError(); }; + + const res = await transport.getTransaction('fb52d7ee453fa69b13ce5fca07ff9a61cd1056a2ad03d51b6eec8542b6db7d76'); + expect(res).to.equal(null); + }); +}); diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/sendTransaction.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/sendTransaction.js new file mode 100644 index 00000000000..b81da5d505f --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/sendTransaction.js @@ -0,0 +1,8 @@ +const { is } = require('../../../utils'); +const logger = require('../../../logger'); + +module.exports = async function sendTransaction(serializedTransaction) { + logger.silly('DAPIClientTransport.sendTransaction'); + if (!is.string(serializedTransaction)) throw new Error('Received an invalid rawtx'); + return this.client.core.broadcastTransaction(Buffer.from(serializedTransaction, 'hex')); +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToAddressesTransactions.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToAddressesTransactions.js new file mode 100644 index 00000000000..97d6e4a51b1 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToAddressesTransactions.js @@ -0,0 +1,82 @@ +const EVENTS = require('../../../EVENTS'); +const logger = require('../../../logger'); +// Artifact from previous optimisation made in SyncWorker plugin +// Kept for reminder when Bloomfilters + +// Thoses are addresses that were used only once, and long time ago. +// Low chance of receiving fund. We still check every ten minutes +// const slowFetchThresold = 5 * 60 * 1000; +// Those are addresses that we consider standard, InstantSend promise a one minute time, +// That is what we offer here (will be changed with streams) +// const fetchThreshold = 60 * 1000; +// Those are special cases, such as the current unusedAddress for instance, +// Higher chance of receiving tx, we listen in a quite spammy ways. +const fastFetchThreshold = 15 * 1000; + +// Loop will go through every 15 sec + +async function executor(forcedAddressList = null) { + const self = this; + const { addresses } = self.state.subscriptions; + const addressList = forcedAddressList || Object.keys(addresses); + logger.silly(`DAPIClient.subscribeToAddrTx.executor[${addressList}]`); + const fetchedUtxos = {}; + addressList.forEach((address) => { + addresses[address].last = +new Date(); + fetchedUtxos[address] = []; + }); + + const utxos = (await self.getUTXO(addressList)); + + utxos.forEach((utxo) => { + const { address, txid, outputIndex } = utxo; + fetchedUtxos[address].push(utxo); + if (self.state.addressesTransactionsMap[address][txid] === undefined) { + self.getTransaction(txid).then((tx) => { + self.state.addressesTransactionsMap[address][txid] = outputIndex; + self.announce(EVENTS.FETCHED_TRANSACTION, tx); + }); + } + }); + addressList.forEach((address) => { + self.announce(EVENTS.FETCHED_ADDRESS, { address, utxos: fetchedUtxos[address] }); + }); +} + +function startExecutor() { + const self = this; + logger.silly('DAPIClientTransport.subscribeToAddressesTransactions.startExecutor'); + this.state.executors.addresses = setInterval(() => { + try { + executor.call(self); + } catch (e) { + logger.error('DAPIClientTransport.subscribeToAddressesTransactions.executor failed', e); + throw e; + } + }, fastFetchThreshold); +} + +module.exports = async function subscribeToAddressesTransactions(addressList) { + logger.silly(`DAPIClient.subscribeToAddressesTransactions[${addressList}]`); + + if (!Array.isArray(addressList)) throw new Error('Expected array of addresses'); + const { executors, subscriptions, addressesTransactionsMap } = this.state; + + addressList.forEach((address) => { + if (!subscriptions.addresses[address]) { + if (!addressesTransactionsMap[address]) { + addressesTransactionsMap[address] = {}; + } + subscriptions.addresses[address] = { priority: 1, last: null }; + } + }); + + if (!executors.addresses) { + try { + startExecutor.call(this); + } catch (e) { + logger.error('DAPIClientTransport.subscribeToAddressesTransactions.startingExecutor failed', e); + throw e; + } + } +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToBlockHeaders.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToBlockHeaders.js new file mode 100644 index 00000000000..4fa4ffbb313 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToBlockHeaders.js @@ -0,0 +1,17 @@ +const EVENTS = require('../../../EVENTS'); + +module.exports = async function subscribeToBlockHeaders() { + const self = this; + const { executors } = this.state; + + const executor = async () => { + const chainHash = await this.getBestBlockHash(); + if (!self.state.blockHeader || self.state.blockHeader.hash !== chainHash) { + self.state.blockHeader = await self.getBlockHeaderByHash(chainHash); + self.announce(EVENTS.BLOCKHEADER, self.state.blockHeader); + } + }; + await executor(); + const refreshBlockInterval = 10 * 1000;// Every 10s + executors.blockHeaders = setInterval(() => executor(), refreshBlockInterval); +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToBlockHeaders.spec.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToBlockHeaders.spec.js new file mode 100644 index 00000000000..3263f17e135 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToBlockHeaders.spec.js @@ -0,0 +1,60 @@ +const { expect } = require('chai'); +const EVENTS = require('../../../EVENTS'); + +const DAPIClientTransport = require('../DAPIClientTransport'); + +describe('transports - DAPIClientTransport - .subscribeToBlockHeaders', function suite() { + this.timeout(15000); + + let fixtures; + let transport; + let clientMock; + let getBestBlockHashCalled; + let blockHeaderAnnounced; + + beforeEach(() => { + fixtures = [ + ['00000120f4203130375fe8684b6b85415594c7a9374074026e59dbb46da42489', '00000020a586b835e3d642fb81f6624163e50ec381c7dcd11832e8cdbd670510a200000082bd70bf47542dbed1dacf9e5dc67f44c553a0bd5a1abdd5fc7198e8361b8f7cbdb2555ecaaf011ebcfe00000203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff05024525010affffffff0240c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac40c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac0000000046020045250000476416132511031b71167f4bb7658eab5c3957d79636767f83e0e18e2b9ed7f8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd4901010045250000010001f7c884225958d983e935a77322a10a8f0acb2fde388762f0c5dabba19f000000320000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'], + ['000000b2832efed61899f1800542722bc9acb688937027824ff012ab0d3aca06', '000000208924a46db4db596e02744037a9c7945541856b4b68e85f37303120f42001000079500e16a28a2c049657174c5df6e409246abbae40d5ebe73b5e652a3f8128862bb4555ef0af011ec98200000203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff05024625010effffffff0240c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac40c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac0000000046020046250000476416132511031b71167f4bb7658eab5c3957d79636767f83e0e18e2b9ed7f8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd4901010046250000010001f7c884225958d983e935a77322a10a8f0acb2fde388762f0c5dabba19f000000320000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'], + ['000000cd464d3f00fea2c3e92a24d0edec1cc2a2579397bfcb9a7f6d994e7508', '0000002006ca3a0dab12f04f8227709388b6acc92b72420580f19918d6fe2e83b2000000ee66f18278bf8c4cc2ff47a8404e30ccd3372424fe1a63f3d236f602adb730eb92b4555e01b6011e032200000203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff05024725010affffffff0240c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac40c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac0000000046020047250000476416132511031b71167f4bb7658eab5c3957d79636767f83e0e18e2b9ed7f8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd4901010047250000010001f7c884225958d983e935a77322a10a8f0acb2fde388762f0c5dabba19f000000320000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'], + ] + + getBestBlockHashCalled = 0; + blockHeaderAnnounced = []; + + clientMock = { + core: { + getBestBlockHash: () => { + const blockHash = fixtures[getBestBlockHashCalled][0]; + + getBestBlockHashCalled += 1; + + return blockHash; + }, + getBlockByHash: (hash) => new Buffer.from(fixtures.find((el) => el[0] === hash)[1], 'hex'), + } + } + + transport = new DAPIClientTransport(clientMock); + }) + + afterEach(() => { + transport.disconnect(); + }) + + it('should work', async () => new Promise(async (resolve, reject) => { + transport.on(EVENTS.BLOCKHEADER, (ev) => { + expect(ev.type).to.equal(EVENTS.BLOCKHEADER); + blockHeaderAnnounced.push(ev.payload); + + if (getBestBlockHashCalled === 2) { + blockHeaderAnnounced.forEach((blockHeader, index) => { + expect(fixtures[index][1].startsWith(blockHeader.toString('hex'))).to.equal(true); + }); + resolve(); + } + }); + + await transport.subscribeToBlockHeaders(); + })); +}); diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToBlocks.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToBlocks.js new file mode 100644 index 00000000000..5f45f0f9905 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToBlocks.js @@ -0,0 +1,21 @@ +const EVENTS = require('../../../EVENTS'); + +module.exports = async function subscribeToBlocks() { + const self = this; + const { executors } = this.state; + + const executor = async () => { + const chainHash = await this.getBestBlockHash(); + if (!self.state.block || self.state.block.hash !== chainHash) { + self.state.block = await self.getBlockByHash(await self.getBestBlockHash()); + self.announce(EVENTS.BLOCK, self.state.block); + if (self.state.block && self.state.block.transactions[0].extraPayload.height) { + const { height } = self.state.block.transactions[0].extraPayload; + self.announce(EVENTS.BLOCKHEIGHT_CHANGED, height); + } + } + }; + await executor(); + const refreshBlockInterval = 30 * 1000;// Every 30s + executors.blocks = setInterval(() => executor(), refreshBlockInterval); +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToBlocks.spec.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToBlocks.spec.js new file mode 100644 index 00000000000..93f917fc2bf --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToBlocks.spec.js @@ -0,0 +1,59 @@ +const { expect } = require('chai'); +const EVENTS = require('../../../EVENTS'); + +const DAPIClientTransport = require('../DAPIClientTransport'); + +describe('transports - DAPIClientTransport - .subscribeToBlocks', function suite() { + let fixtures; + let transport; + let clientMock; + let getBestBlockHashCalled; + let blockAnnounced; + + beforeEach(() => { + fixtures = [ + ['00000120f4203130375fe8684b6b85415594c7a9374074026e59dbb46da42489', '00000020a586b835e3d642fb81f6624163e50ec381c7dcd11832e8cdbd670510a200000082bd70bf47542dbed1dacf9e5dc67f44c553a0bd5a1abdd5fc7198e8361b8f7cbdb2555ecaaf011ebcfe00000203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff05024525010affffffff0240c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac40c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac0000000046020045250000476416132511031b71167f4bb7658eab5c3957d79636767f83e0e18e2b9ed7f8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd4901010045250000010001f7c884225958d983e935a77322a10a8f0acb2fde388762f0c5dabba19f000000320000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'], + ['000000b2832efed61899f1800542722bc9acb688937027824ff012ab0d3aca06', '000000208924a46db4db596e02744037a9c7945541856b4b68e85f37303120f42001000079500e16a28a2c049657174c5df6e409246abbae40d5ebe73b5e652a3f8128862bb4555ef0af011ec98200000203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff05024625010effffffff0240c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac40c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac0000000046020046250000476416132511031b71167f4bb7658eab5c3957d79636767f83e0e18e2b9ed7f8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd4901010046250000010001f7c884225958d983e935a77322a10a8f0acb2fde388762f0c5dabba19f000000320000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'], + ['000000cd464d3f00fea2c3e92a24d0edec1cc2a2579397bfcb9a7f6d994e7508', '0000002006ca3a0dab12f04f8227709388b6acc92b72420580f19918d6fe2e83b2000000ee66f18278bf8c4cc2ff47a8404e30ccd3372424fe1a63f3d236f602adb730eb92b4555e01b6011e032200000203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff05024725010affffffff0240c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac40c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac0000000046020047250000476416132511031b71167f4bb7658eab5c3957d79636767f83e0e18e2b9ed7f8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd4901010047250000010001f7c884225958d983e935a77322a10a8f0acb2fde388762f0c5dabba19f000000320000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'], + ]; + + getBestBlockHashCalled = 0; + blockAnnounced = []; + + clientMock = { + core: { + getBestBlockHash: () => { + const blockHash = fixtures[getBestBlockHashCalled][0]; + + getBestBlockHashCalled += 1; + + return blockHash; + }, + + getBlockByHash: (hash) => new Buffer.from(fixtures.find((el) => el[0] === hash)[1], 'hex'), + } + } + + transport = new DAPIClientTransport(clientMock); + }) + + afterEach(() => { + transport.disconnect(); + }) + + it('should work', async () => new Promise(async (resolve, reject) => { + transport.on(EVENTS.BLOCK, (ev) => { + expect(ev.type).to.equal(EVENTS.BLOCK); + blockAnnounced.push(ev.payload); + + if (getBestBlockHashCalled === 2) { + blockAnnounced.forEach((block, index) => { + expect(fixtures[index + 1][1]).to.equal(block.toString('hex')); + }); + resolve(); + } + }); + + await transport.subscribeToBlocks(); + })); +}); diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToTransactionsWithProofs.js b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToTransactionsWithProofs.js new file mode 100644 index 00000000000..0f33a7b74f6 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/methods/subscribeToTransactionsWithProofs.js @@ -0,0 +1,48 @@ +const { + BloomFilter, Address, +} = require('@dashevo/dashcore-lib'); +const logger = require('../../../logger'); + +const { BLOOM_FALSE_POSITIVE_RATE } = require('../../../CONSTANTS'); + +/** + * From a given addressList will create and submit a bloomfilter to DAPI + * and parse response looking for relevant inputs and outputs. + * @param {string[]} addressList + * @param opts + * @param {number} [opts.fromBlockHeight] + * @param {string} [opts.fromBlockHash] + * @param {number} opts.count + * @return {Promise} + */ + +module.exports = async function subscribeToTransactionsWithProofs( + addressList, + opts = { fromBlockHeight: 1, count: 0 }, +) { + const { client } = this; + logger.silly(`DAPIClient.subscribeToTransactionWithProofs[${addressList}](len:${addressList.length})`); + + if (!addressList.length) throw new Error('Unable to subscribe to transaction without addresses'); + const bloomfilter = BloomFilter.create(addressList.length, BLOOM_FALSE_POSITIVE_RATE); + + addressList.forEach((address) => { + const addressModel = new Address(address); + bloomfilter.insert(addressModel.hashBuffer); + }); + + if (opts.fromBlockHeight == null && opts.fromBlockHash == null) { + throw new Error('fromBlockHeight or fromBlockHash needs to be specified'); + } + + if (opts.fromBlockHeight === 0) { + // Historically, in order to avoid hard fork, in Bitcoin, genesis block is non-spendable. + // Therefore we continue to have it as an hardcoded non-included UTXO + // Thus, we start to one (also provokes a Internal Error if we would try to start at zero). + // eslint-disable-next-line no-param-reassign + opts.fromBlockHeight = 1; + } + + logger.debug(`Options: ${JSON.stringify(opts)}`); + return client.core.subscribeToTransactionsWithProofs(bloomfilter, opts); +}; diff --git a/packages/wallet-lib/src/transport/DAPIClientTransport/utils/getHeightFromMerkleBlockBuffer.js b/packages/wallet-lib/src/transport/DAPIClientTransport/utils/getHeightFromMerkleBlockBuffer.js new file mode 100644 index 00000000000..e067ca82619 --- /dev/null +++ b/packages/wallet-lib/src/transport/DAPIClientTransport/utils/getHeightFromMerkleBlockBuffer.js @@ -0,0 +1,19 @@ +const { + MerkleBlock, +} = require('@dashevo/dashcore-lib'); + +const getHeightFromMerkleBlockBuffer = async (client, merkleBlockBuffer) => { + // FIXME: MerkleBlock do not accept hex. + const merkleBlock = new MerkleBlock(Buffer.from(merkleBlockBuffer)); + const prevHash = merkleBlock.header.prevHash.reverse().toString('hex'); + + const prevBlock = await client.getBlockByHash(prevHash); + try { + const prevBlockHeight = prevBlock.transactions[0].extraPayload.height; + return prevBlockHeight + 1; + } catch (e) { + const prevBlockHeight = prevBlock.transactions[1].extraPayload.height; + return prevBlockHeight + 1; + } +}; +module.exports = getHeightFromMerkleBlockBuffer; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/FixtureTransport.js b/packages/wallet-lib/src/transport/FixtureTransport/FixtureTransport.js new file mode 100644 index 00000000000..3e0e09b6c67 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/FixtureTransport.js @@ -0,0 +1,73 @@ +const blocksData = require('./data/blocks/blocks'); +const AbstractTransport = require('../AbstractTransport'); + +const bestBlockDataHeight = 21546; + +/** + * This is a saved snapshot of some selected blocks and transactions + * Meant to be used as replacement of DAPIClientTransport. + * Read more on the specificities on Readme.md and the things that are saved + * + */ +class FixtureTransport extends AbstractTransport { + constructor() { + super(); + + this.height = bestBlockDataHeight; + this.blockHash = blocksData.heights[this.height]; + + this.relayFee = 0.00001; + this.difficulty = 0.00171976818884149; + this.network = 'testnet'; + } + + setHeight(height) { + if (!height) throw new Error('Height needed'); + this.height = height; + + if (!blocksData.heights[this.height]) { + throw new Error(`Missing block ${this.height}`); + } + this.blockHash = blocksData.heights[this.height]; + } + + rewindBlock(step = 1) { + this.height -= step; + if (!blocksData.heights[this.height]) { + throw new Error(`Missing block ${this.height}`); + } + this.blockHash = blocksData.heights[this.height]; + } + + forwardBlock(step = 1) { + this.height += step; + if (!blocksData.heights[this.height]) { + throw new Error(`Missing block ${this.height}`); + } + this.blockHash = blocksData.heights[this.height]; + } + + // eslint-disable-next-line class-methods-use-this + getMnemonicList() { + return [ + 'nerve iron scrap chronic error wild glue sound range hurdle alter dwarf', + ]; + } +} + +FixtureTransport.prototype.getBestBlock = require('./methods/getBestBlock'); +FixtureTransport.prototype.getBestBlockHash = require('./methods/getBestBlockHash'); +FixtureTransport.prototype.getBestBlockHeader = require('./methods/getBestBlockHeader'); +FixtureTransport.prototype.getBestBlockHeight = require('./methods/getBestBlockHeight'); +FixtureTransport.prototype.getBlockByHash = require('./methods/getBlockByHash'); +FixtureTransport.prototype.getBlockByHeight = require('./methods/getBlockByHeight'); +FixtureTransport.prototype.getBlockHeaderByHash = require('./methods/getBlockHeaderByHash'); +FixtureTransport.prototype.getBlockHeaderByHeight = require('./methods/getBlockHeaderByHeight'); +FixtureTransport.prototype.getStatus = require('./methods/getStatus'); +FixtureTransport.prototype.getTransaction = require('./methods/getTransaction'); +FixtureTransport.prototype.sendTransaction = require('./methods/sendTransaction'); +FixtureTransport.prototype.subscribeToAddressesTransactions = require('./methods/subscribeToAddressesTransactions'); +FixtureTransport.prototype.subscribeToBlockHeaders = require('./methods/subscribeToBlockHeaders'); +FixtureTransport.prototype.subscribeToBlocks = require('./methods/subscribeToBlocks'); + +module.exports = FixtureTransport; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/README.md b/packages/wallet-lib/src/transport/FixtureTransport/README.md new file mode 100644 index 00000000000..cc6d0309270 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/README.md @@ -0,0 +1,109 @@ + + + +Mnemonic : + +Origin of initial test funds : 'cause proud slush host cover sort marine display shove genius fence amused' +From address : ySnJVXXx9FtKUBTkovPaPPqCkTMNzDLPCu +UTXO : dd356b65d35b4944c039eca91cad4da640fe78ca0259f7087840588120c80ec7-0 +SAT : 40000000000 + +Mnemonic 1 : nerve iron scrap chronic error wild glue sound range hurdle alter dwarf +Addr /0 : yfhsfW9RW2i4aNGC1hnp4e84wFi7i7ZWy3 received : 2000000000 +Addr /1 : yLrA6MPBc5kb3TpWQV66JKey6CEpuBW464 received : 3000000000 +Sends 1000000000 to itself onto addr/3 : ySUnmfEHwHzirEBvm3duqyfatsq5N4joqM +Addr /4 : ycafF3CrFGE6WefjXsxoFjBTCvLoRvoupx received : 500000000 (from whisperGrid) + + +Mnemonic 2 : merry fix human idle garment warm pear nothing indoor replace gun glory +Addr /0 : yaB9r1AWzzFSR1TJXg4UqjxX4oxK5Gadmg received : 4 UTXO of *1000000000 (1 mixed + 3 single 1-1) +Addr /1 : yT6mwYTQ94JvisJ12VpGhtVitnzcvCeSy8 - Received 500000000 from whisperGrid +Addr /2 : yMP5hfgcbVc7TbcXdvdy2daXM9oTiFC4Hd - Received 500000000 from nerve iron + +Mnemonic 3 : whisper grid soda wait all firm silver matrix save wedding gorilla present +Addr /0 : ygxysXnAKVTNBPQ4gaDr2pTx6ZUracqQLN Received 5000000000 + +Paid to yT6mwYTQ94JvisJ12VpGhtVitnzcvCeSy8 -> 500000000 +Paid to ycafF3CrFGE6WefjXsxoFjBTCvLoRvoupx -> 500000000 + + +Mnemonic 4 : business dirt egg desert arrest private rich mimic enact liberty salad umbrella +Addr /0 : yS3Ja63BpkH7qHYVQvdEuiBd9xo8ZoPjZB -> Receive all from Mnemo 1,2,3 + +Mnemonic 5 : plastron reptile siéger océan esquiver paniquer rotule tuile tenir suspect zeste chenille +Addr /0 : yMEUt9JxnybepMkTwv9Kpn5jHRbcFpPtax -> Receive from business dirt. + + +Timeflow : + + + +Block 3800 : + +ySnJVXXx9FtKUBTkovPaPPqCkTMNzDLPCu -> yfhsfW9RW2i4aNGC1hnp4e84wFi7i7ZWy3 (2000000000) (3a69947afc81ec3bf8577450ccb04efb6734ef946c530984ae1612f85be11fb3) + + +Block 3801 : +ySnJVXXx9FtKUBTkovPaPPqCkTMNzDLPCu -> ygxysXnAKVTNBPQ4gaDr2pTx6ZUracqQLN (5000000000) + -> yLrA6MPBc5kb3TpWQV66JKey6CEpuBW464 (3000000000) + -> yaB9r1AWzzFSR1TJXg4UqjxX4oxK5Gadmg (1000000000) + +-> c348a94127d746981e4790487d4a109a31fdcf2307ef796e01fc13353699490c + + +Block 3802 : +ySnJVXXx9FtKUBTkovPaPPqCkTMNzDLPCu -> yaB9r1AWzzFSR1TJXg4UqjxX4oxK5Gadmg (1000000000) + -> yaB9r1AWzzFSR1TJXg4UqjxX4oxK5Gadmg (1000000000) + (140a9973b227da769749b51d1c69e74ea99a3c0d46259f42aef7f6efe096985d) + +Block 3803 : +ySnJVXXx9FtKUBTkovPaPPqCkTMNzDLPCu -> yaB9r1AWzzFSR1TJXg4UqjxX4oxK5Gadmg (1000000000) +ySnJVXXx9FtKUBTkovPaPPqCkTMNzDLPCu -> yaB9r1AWzzFSR1TJXg4UqjxX4oxK5Gadmg (1000000000) + +txid1 67057b6d72dccd14bd74e9815d3686eb48152611fdafd890b096ec2cfab7aa9c +txid2 67057b6d72dccd14bd74e9815d3686eb48152611fdafd890b096ec2cfab7aa9c + +Block 3805 : +Nerve pays itself ySUnmfEHwHzirEBvm3duqyfatsq5N4joqM : (1000000000) +->4dc4f19ac6cc469594443957d96c9f0c1bb71d487827598a703c375374c3b6fd + +ySUnmfEHwHzirEBvm3duqyfatsq5N4joqM () + +Block 3806 : + +ygxysXnAKVTNBPQ4gaDr2pTx6ZUracqQLN -> yT6mwYTQ94JvisJ12VpGhtVitnzcvCeSy8 (500000000) +\ ycafF3CrFGE6WefjXsxoFjBTCvLoRvoupx (500000000) +-> b029d54cd887a0f6c0fba8a916dc3051ee77fb99b12d6fccfcfb85b7183b0f21 +Block 3807 : + +yfhsfW9RW2i4aNGC1hnp4e84wFi7i7ZWy3 -> yS3Ja63BpkH7qHYVQvdEuiBd9xo8ZoPjZB (2000000000) +ygxysXnAKVTNBPQ4gaDr2pTx6ZUracqQLN -> yS3Ja63BpkH7qHYVQvdEuiBd9xo8ZoPjZB (3000000000) +yLrA6MPBc5kb3TpWQV66JKey6CEpuBW464 -> yS3Ja63BpkH7qHYVQvdEuiBd9xo8ZoPjZB (3000000000) +yaB9r1AWzzFSR1TJXg4UqjxX4oxK5Gadmg -> yS3Ja63BpkH7qHYVQvdEuiBd9xo8ZoPjZB (4000000000) +yT6mwYTQ94JvisJ12VpGhtVitnzcvCeSy8 -> yS3Ja63BpkH7qHYVQvdEuiBd9xo8ZoPjZB (500000000) +ycafF3CrFGE6WefjXsxoFjBTCvLoRvoupx -> yS3Ja63BpkH7qHYVQvdEuiBd9xo8ZoPjZB (500000000) + + +whisper : b812d9345fa8ea06af1d19b935eec65824d53779db74cd325690ad1d38a82757 +merry : 370b7bbd5b6e0de42a95d59e3277041ac20e945ffb93f56bb6984ba42f28a2ac +nerve : 9f398515b6fc898ebf4e7b49bbfc4359b8c89f508c6cd677e53946bd86064b28 + +yS3Ja63BpkH7qHYVQvdEuiBd9xo8ZoPjZB -> yMEUt9JxnybepMkTwv9Kpn5jHRbcFpPtax +ea9c4066394aa09cb7ee8f3997b8dc10b999a8d709c4046f81d8bf9341ae6e5b + +Block 3808 : + + +yS3Ja63BpkH7qHYVQvdEuiBd9xo8ZoPjZB -> yMEUt9JxnybepMkTwv9Kpn5jHRbcFpPtax (all) +-> 9bf7aaf7fd46c6919a750f2c01aa672a8b64b55f5b03f71ae6adf9d5b25cf9b4 + +Block 3808 : + + +yMEUt9JxnybepMkTwv9Kpn5jHRbcFpPtax -> yMEUt9JxnybepMkTwv9Kpn5jHRbcFpPtax (all) +-> 9bf7aaf7fd46c6919a750f2c01aa672a8b64b55f5b03f71ae6adf9d5b25cf9b4 + + +Blocks : + +3785 - 00000034c303c9f4fffe7c32d077c5db7168aa161ad3415defea2dcfef94267c diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21539.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21539.json new file mode 100644 index 00000000000..88e7fc26a7d --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21539.json @@ -0,0 +1,5 @@ +{ + "hash": "000003ed50eee6aec24a272f2fd9a5ad996246ce4f05d6bb4185eff8353789df", + "height":21539, + "block": "00000020c9830c2fecfd5757be0f911da813ae134841da5b932871f5734e8b10bb0100009c5b551808f63d058508627bf82bd5382a2b5d4fb1edbd4e9cba3d76a07a7cc240be965eaf57041efc8900000303000500010000000000000000000000000000000000000000000000000000000000000000ffffffff05022354010fffffffff02b6c3609a010000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88acacc3609a010000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88ac0000000046020023540000cead425668f38cfbb8dc028ad53d163fcee7282ede84d9a577ac6851a847ebc8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd4901010023540000010001e304c95a35b54630b9aa4c1409650729b9a702a390fe793608750204bd0100003200000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000001378b9538dd0a0107f26d107a74544afc84e94ab6cd0c80ce35d398c06c622dab010000006b48304502210096a62fd352dc5827fd26f2c02c147686f1cab5ace1b9f30a78408f67bb89322a02204a8b2bbbb5027e74283a2381f268d20ac0d985e4833e0becc89a312bdee9458301210231898f2676e290d627633f7ed07df5e633d633c831cfa81352034f57dd08f752feffffff0200ca9a3b000000001976a91419ba12c566e1e21deafa60f2a6de42d15f85dbbc88acc0dc2a23010000001976a914832e0cbf5107b230cc8eed200425709f765ba17888ac22540000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21543.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21543.json new file mode 100644 index 00000000000..9a58f02fec7 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21543.json @@ -0,0 +1,5 @@ +{ + "hash": "0000012e44c8e2b6c35025df2d59e173f7fe694ee4a1526659f8ea855f62c3fa", + "height":21543, + "block": "000000209be03c40f5afb1934fd5545c359bbafa68f77764fcce17bf6f53c451ef020000ae366b245f00918e7a6ca11d2f205f71428e4d281007dc1e0abdf3439b27742f21bf965e2012041e523100000303000500010000000000000000000000000000000000000000000000000000000000000000ffffffff05022754010cffffffff02b6c3609a010000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88acacc3609a010000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88ac0000000046020027540000cead425668f38cfbb8dc028ad53d163fcee7282ede84d9a577ac6851a847ebc8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd4901010027540000010001e304c95a35b54630b9aa4c1409650729b9a702a390fe793608750204bd0100003200000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000001988984007436fcd800b801afb3b6e9c0f08686fc6428fec585b50d005a48e74f010000006b48304502210099e6385f43b3bdd747959090df57f13ff97780a038694d88cd26ab1e08808f3802203144ee095a4fe60571560121da6b7c53455a195b1f0b32f055704edeb399e6c3012103ffc5d6b9084e84c65a3762161f9bcb4f3c1c9231e061af84f140cfd1966b98abfeffffff0200ca9a3b000000001976a91419ba12c566e1e21deafa60f2a6de42d15f85dbbc88acde1190e7000000001976a914ae354e2154e0f57ea7095306d4dbad12c605463088ac26540000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21546.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21546.json new file mode 100644 index 00000000000..a93751a3479 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21546.json @@ -0,0 +1,5 @@ +{ + "hash": "00000261de92f250266fc09b501813d7a0c68a311cddc35c1799905342358fd8", + "height":21546, + "block": "00000020e7b5618043db6baa9e1c2c84480b7f77c6a8e9956bf1196830d833ccb0020000d36625c7f429a3df10ed60d756c23656b96a8cd729a2488c366563652d86228999c0965ec51a031e992800000303000500010000000000000000000000000000000000000000000000000000000000000000ffffffff05022a54010bffffffff02b6c3609a010000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88acacc3609a010000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88ac000000004602002a540000cead425668f38cfbb8dc028ad53d163fcee7282ede84d9a577ac6851a847ebc8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd490101002a540000010001e304c95a35b54630b9aa4c1409650729b9a702a390fe793608750204bd010000320000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000135d3c2fe20a78d2bb8bb6cf9519f5bc44eed1a09db125b7530e990a1531bceeb010000006b483045022100c2b0c215ca0ba788f5be85b16fcb6889703c27ef41564651058c6f6b9df196db0220141405d97886b16dc4241f2e768f0813ee4587253d16d61e07ecb40424420f91012102479dc303864711d31eefc4b30ca8646acc8f8c427e975b596a652562faf15a8afeffffff0200ca9a3b000000001976a9142883a66f510c4aa8f4abacb09e7372be54477cbe88acfc46f5ab000000001976a914383eb45e8d77b1985f43877c6aa7080618bcffec88ac29540000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21635.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21635.json new file mode 100644 index 00000000000..6298d354d68 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21635.json @@ -0,0 +1,5 @@ +{ + "hash": "00000152661e6a516ea516f68fc5b11f2368d3680d4d554ddd1705a7d7868d87", + "height":21635, + "block": "000000204f262780c65a39ea993bb9b4cfbe8b3ac67dafd6e1ab79ad27f07c7473000000d625c6fd48b33dccd37fca89a986897bf3fd8ba8199fdf04bd6891f00844eaccafed965ebaeb011ea25e00000303000500010000000000000000000000000000000000000000000000000000000000000000ffffffff050283540109ffffffff0258c4609a010000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88ac4cc4609a010000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88ac0000000046020083540000cead425668f38cfbb8dc028ad53d163fcee7282ede84d9a577ac6851a847ebc8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd490101008354000001000198eb657594b5c196a0045bcd45bbeb9500ce06d4d2afb650d6776beabd0100003200000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000001b64e23b6bd8c1016c8595ab6256e97ac5a33a95b5c68cc99410bf88867023910000000006b483045022100fc88e4585654961610e375b19f33b52d10e1c7efa5ef91531c627129538cf7ef0220108a281374a691522b5deb51ce3249723efe9541e57a4de87bdd8ba7ce43ce8e012103987110fc08c848657176385b37a77fb7f6d89bc873bb4334146ffe44ac126566ffffffff0b804a5d05000000001976a9140a6a961f1c664a9cd004c593381dd4d9f1f5463588ac804a5d05000000001976a91403ab1053a3bc741a012607893c66565c6815b9d888ac804a5d05000000001976a9146c773e3b74a16931f995288645f4f6379076048688ac804a5d05000000001976a914429dfc6b9a9d86463ea65b55d8cedb26a5e04f3388ac804a5d05000000001976a91434cb4bfb6e27ed0067e47c55da615bf7230e23f888ac804a5d05000000001976a914eb9a36fab9220e5e966fdcfe1abf2ee43308cb5d88ac804a5d05000000001976a9144c9f7ef1c5af5f0d2b219a035a46c7f54035b0a288ac804a5d05000000001976a9141c44d8966f001ddb7cea277edc33b02f151b603788ac804a5d05000000001976a914f4159f063a076038a484cf9d027808dbac118a1a88ac804a5d05000000001976a9147bc630538f5bb87d3166b6cf5f69853809235f4388acdcdef505000000001976a9140a6a961f1c664a9cd004c593381dd4d9f1f5463588ac00000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21638.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21638.json new file mode 100644 index 00000000000..b8363ea9430 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/21638.json @@ -0,0 +1,5 @@ +{ + "hash": "00000011eb4dfa5034e1dd16c46e1a14b74c403a62c6fb61abccf72edd42b9d3", + "height":21638, + "block": "00000020dd5b79d06fff977784cc9eec7c15a96586c1880e9c2b2242720f2a2298000000229ebcea11f314f9830ab7b5dc3f1afc40c140bc2ff7798622fe5505d702625efeee965e2909021e59e100000403000500010000000000000000000000000000000000000000000000000000000000000000ffffffff050286540109ffffffff02e8c4609a010000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88ace5c4609a010000001976a91416b93a3b9168a20605cc3cda62f6135a3baa531a88ac0000000046020086540000cead425668f38cfbb8dc028ad53d163fcee7282ede84d9a577ac6851a847ebc8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd490101008654000001000198eb657594b5c196a0045bcd45bbeb9500ce06d4d2afb650d6776beabd0100003200000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000001ef77c1ffb0bd6d754c96ba2431a258f5adbdb2b36abe20051fc40610affa2125000000006b48304502210089ab02a3bb881bda0104061e4f28edf3200f1e85bd847ba54ab016305947284002200d75bf126b563046756e95f2057164d49f6d9c4cf8a6ab585b10ce1b925767650121020d242c568443a69b4f11b04848f9e2b82edbc7b93c041beedd041d50c9760c17ffffffff01b3495d05000000001976a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac000000000300000003ef77c1ffb0bd6d754c96ba2431a258f5adbdb2b36abe20051fc40610affa2125010000006b48304502210099bbb32cef508df77ac744c3aafc92df61f56eb73758ca391bf06885f9ffe6a2022016b28a212e42a89f8f53a789380d1efb143ccb9a31b0be23fa69f1bba162ec9e012103c5789b847c292aaf1cd0d7e4543cbf5d3394c2ab2edd5bf2639f181378f62815ffffffffef77c1ffb0bd6d754c96ba2431a258f5adbdb2b36abe20051fc40610affa2125020000006a47304402202a7a9e7e556382e7364d6013a45ab20c0197b5e090b91cdeba9d32454c21207a0220745d00507d4a327782af366d234e278d9497464b7c756ae6369f0197551c823e0121034e2af51d2a435dc3294bd8f9ec5ca0d9e64c78e1b4372ba207dba5c311ca9138ffffffffef77c1ffb0bd6d754c96ba2431a258f5adbdb2b36abe20051fc40610affa2125030000006a4730440220709f8243d6462529734c954fe2c78e8c453a51fbeea42c5a94a19791089a807402202bb4bf4a73b7a621b065a33120826e66619d2bc9a7ecd812b964a66bf03a6811012103b73217183ee40ce56b20feb924b4ab5acba9c2e66f9025fb5b5219a492733ac1ffffffff0580f0fa02000000001976a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac80f0fa02000000001976a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac80f0fa02000000001976a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac80f0fa02000000001976a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac001b2c04000000001976a914bc41039fb091e15ebc7a3b116f5191a0cb3ac24988ac00000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3798.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3798.json new file mode 100644 index 00000000000..4e5fdb3c695 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3798.json @@ -0,0 +1,5 @@ +{ + "hash": "6736ee511514c2c644224a9c7dc380cdeb0cc1a35ad1718ffe419947a57df645", + "height":3798, + "block": "0000002013b3e35c067d65a8b64b9d8cac3dd5c0e69809e8aa00f10738097b9eef01000055e85bdc3a0d5528632b1b2c3a75217ace911e8be22405da47357e05865669fe8242675effff7f20000000000103000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502d60e0108ffffffff0200902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200d60e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e380000000000000000000000000000000000000000000000000000000000000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3799.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3799.json new file mode 100644 index 00000000000..27701c43600 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3799.json @@ -0,0 +1,5 @@ +{ + "hash": "00000014a92cb29b478ea233a21bda2f28335237bb43186bb264d093cd5df0de", + "height":3799, + "block": "0000002045f67da5479941fe8f71d15aa3c10cebcd80c37d9c4a2244c6c2141551ee36675f6db2970ce86eb59d91e61f55af019bcda0d3e1010a24ebc9f9be6c3a5897772343675e7745021e92c500000103000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502d70e010dffffffff0200902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200d70e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e380000000000000000000000000000000000000000000000000000000000000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3800.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3800.json new file mode 100644 index 00000000000..721d17b71f7 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3800.json @@ -0,0 +1,5 @@ +{ + "hash": "0000003a0ed27c5eb4721929170170f85e507531716173345d6954eb0de900fe", + "height":3800, + "block": "00000020def05dcd93d064b26b1843bb375233282fda1ba233a28e479bb22ca9140000005e8a786c215e41b3278eca204576b6466e07fe7deec2853eebc199378528ba883243675e7745021e335b00000103000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502d80e010bffffffff0200902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200d80e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e380000000000000000000000000000000000000000000000000000000000000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3801.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3801.json new file mode 100644 index 00000000000..c41d91d25d4 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3801.json @@ -0,0 +1,5 @@ +{ + "hash": "70d65c760880645a741af2bd9176998903c3dc9b2907a6bd35cd7a6171442917", + "height":3801, + "block": "00000020fe00e90deb54695d347361713175505ef8700117291972b45e7cd20e3a0000006d5eb7bacfb36754fddd5659bcaacff52efdcdd3681ab91b07461e78a835a2986044675effff7f20010000000203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502d90e0107ffffffff02c6902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac31e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200d90e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e3800000000000000000000000000000000000000000000000000000000000000000300000001c70ec8208158407808f75902ca78fe40a64dad1ca9ec39c044495bd3656b35dd000000006a47304402207386b9537dd58cc6fa4be45e3ced67d3ea39942387b4f1cf566c0726a430c0bc02206bdca29656c449665035d9a8c944482be0413c80d066af0f2ac2551eca99aa52012102e913c9871a4514410ebba8da0241bb1acdd625fc67ad1de7141e150b494331f3ffffffff0200943577000000001976a914d4a84189942002ac8410191b04da41ae0445f8cf88ac09fbf9d8080000001976a91446e502918c04a65a3830ce89cc364b0cd301793388ac00000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3802.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3802.json new file mode 100644 index 00000000000..716c2b8e82c --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3802.json @@ -0,0 +1,5 @@ +{ + "hash": "104851b5d0b49e6d3780f42b278076f292f8d2a5aa807000fc051846597244f9", + "height":3802, + "block": "0000002017294471617acd35bda607299bdcc30389997691bdf21a745a648008765cd670fb2b0fc174b799d1f1b51b0bb7e2b8d4261bce7202589b8a41408b9d8026bcf98e45675effff7f20010000000303000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502da0e0107ffffffff02fb902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac3ee40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200da0e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e38000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd49010100da0e00000100015cf0253689f140d713efca32a6e6198c2a3e5b98b3bd7d7e9b52ec5e649adf3c3200000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000001b31fe15bf81216ae8409536c94ef3467fb4eb0cc507457f83bec81fc7a94693a010000006b483045022100ec009238516742aff0b31f1c3c461ed41eb76bd9ab31cea9cfd40526846b77ac02201de5deff01c3e6e7de7668628d874cc14c1843e868f58e639911fd102c5ce764012102e913c9871a4514410ebba8da0241bb1acdd625fc67ad1de7141e150b494331f3ffffffff0400f2052a010000001976a914e27bcd896614344bdc29cdaa815a1effafee86e088ac005ed0b2000000001976a91405cee8568a5355f0f0fd828c54832b3cc514639288ac00ca9a3b000000001976a91498003d5164ed32b12a29f9952a80b3c501ee845688acd0df88c0060000001976a91446e502918c04a65a3830ce89cc364b0cd301793388ac00000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3803.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3803.json new file mode 100644 index 00000000000..90e77c328aa --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3803.json @@ -0,0 +1,5 @@ +{ + "hash": "000001e691883ccd6cdb81f1f9d2c28ee02148c4995783c2d958c198d34be845", + "height":3803, + "block": "00000020f9447259461805fc007080aaa5d2f892f27680272bf480376d9eb4d0b5514810f5e40f3bd6a52880ecd17a7ce0af1f5e310eacbdeb4bea8e6d25138cdfcff1fb6046675e7745021ee63900000303000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502db0e0110ffffffff02e0902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac38e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200db0e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e38000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd49010100db0e00000100015cf0253689f140d713efca32a6e6198c2a3e5b98b3bd7d7e9b52ec5e649adf3c32000000000000003200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000010c4999363513fc016e79ef0723cffd319a104a7d4890471e9846d72741a948c3030000006a47304402200ac8005e2c8f41073b345b5037c69fe74ad0c944c7d81c26425c4cf70c6f77220220194641970cead7ececa3da875239ae18b3a40055229905c018854bcd3fcd959a012102e913c9871a4514410ebba8da0241bb1acdd625fc67ad1de7141e150b494331f3ffffffff0300ca9a3b000000001976a91498003d5164ed32b12a29f9952a80b3c501ee845688ac00ca9a3b000000001976a91498003d5164ed32b12a29f9952a80b3c501ee845688acb84a5349060000001976a91446e502918c04a65a3830ce89cc364b0cd301793388ac00000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3804.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3804.json new file mode 100644 index 00000000000..cfcdcf79a8c --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3804.json @@ -0,0 +1,5 @@ +{ + "hash": "0000000483e69d37f3a07b40cc7e9bd40ab9c5d2119bd688f5c855942c83925f", + "height":3804, + "block": "0000002045e84bd398c158d9c2835799c44821e08ec2d2f9f181db6ccd3c8891e6010000b56453d7026e65875eb9f08128334447adac29d66915a1b2ed72d7575a80996bd046675e7745021e10af00000303000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502dc0e0106ffffffff02c6902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac31e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200dc0e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e38000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd49010100dc0e00000100015cf0253689f140d713efca32a6e6198c2a3e5b98b3bd7d7e9b52ec5e649adf3c32000000000000003200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000015d9896e0eff6f7ae429f25460d3c9aa94ee7691c1db5499776da27b273990a14020000006b483045022100a76e5b4cf601938fd7bf0317b852008e946b9f063b35be07d36230bb1b044be502201e4995e38775a62670663a7a22d15157371cce7b8a2c36048daa16ea6fb28b5b012102e913c9871a4514410ebba8da0241bb1acdd625fc67ad1de7141e150b494331f3ffffffff0200ca9a3b000000001976a91498003d5164ed32b12a29f9952a80b3c501ee845688acc17fb80d060000001976a91446e502918c04a65a3830ce89cc364b0cd301793388ac00000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3805.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3805.json new file mode 100644 index 00000000000..c42bd7caa34 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3805.json @@ -0,0 +1,5 @@ +{ + "hash": "000001d13389375c3aaf53d60c3a608d00cf37e38726adaea1047c1a1a6780b6", + "height":3805, + "block": "000000205f92832c9455c8f588d69b11d2c5b90ad49b7ecc407ba0f3379de6830400000023641e26aaaaa33a2dc6a5866e70795084274aaddd86a594725dc5b23c361561f046675e7745021e0a7200000203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502dd0e0108ffffffff0200902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200dd0e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e38000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd49010100dd0e00000100015cf0253689f140d713efca32a6e6198c2a3e5b98b3bd7d7e9b52ec5e649adf3c320000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3806.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3806.json new file mode 100644 index 00000000000..362a03c0dc2 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3806.json @@ -0,0 +1,5 @@ +{ + "hash": "000001bef9c8bf377880b383ce54397d44a23c70545de59a1ac8bd6c6929bb1c", + "height":3806, + "block": "00000020b680671a1a7c04a1aead2687e337cf008d603a0cd653af3a5c378933d101000001b6754cff01e25603d36a2027581fb792067da6cbffc08c3d3c95379c7ae7c97e47675e7745021e0f2400000303000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502de0e010dffffffff02c6902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac31e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200de0e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e38000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd49010100de0e00000100015cf0253689f140d713efca32a6e6198c2a3e5b98b3bd7d7e9b52ec5e649adf3c3200000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000001b31fe15bf81216ae8409536c94ef3467fb4eb0cc507457f83bec81fc7a94693a000000006b4830450221008080a98e4dfc89613d66b47c1ee9a359e1ffab80d91b125f59f5a895358eff890220693f02d182535e86c4f4b96ac9e2563ae2d204e4131027115400b165f88f862c012102d44ee576c92da34665fe822162f704e69e31103b13782cc3b4a601d8d01aedf1ffffffff0200ca9a3b000000001976a914439520cd3e3660b17ffb0cafd531e118a0313b0988ac09c99a3b000000001976a9147e8598547fba36dd1ac179e603d78453f1442f2f88ac00000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3807.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3807.json new file mode 100644 index 00000000000..b10adcfa88f --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3807.json @@ -0,0 +1,5 @@ +{ + "hash": "000001a67f840e035f41027feb1887673c098ca89955ce070e22bdba9ecbee6f", + "height":3807, + "block": "000000201cbb29696cbdc81a9ae55d54703ca2447d3954ce83b3807837bfc8f9be0100005e7f73bceaf15f331937651d949300e18ab539fc7a561e024a13e0c5e19922697648675e7745021ecbbe00000303000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502df0e010effffffff02e0902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac38e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200df0e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e38000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd49010100df0e00000100015cf0253689f140d713efca32a6e6198c2a3e5b98b3bd7d7e9b52ec5e649adf3c32000000000000003200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000010c4999363513fc016e79ef0723cffd319a104a7d4890471e9846d72741a948c3000000006b483045022100a547d2c889ae33788ca445531bb4e71bf720bf2d483e68473c09793e65d8bd540220214a83cf0e0716963aef1b890d135004c2689bef4a74056066e011cc43cddb960121025959620a950666151ed96e4e815474f9e08a7e09e706637fb009aae9796a00a9ffffffff030065cd1d000000001976a9144a6369058eedc7ffba20537fa477c1d81a432c6e88ac0065cd1d000000001976a914b26299046d6288a95d81ce859923bff83eff641a88ace8266bee000000001976a9148846e677d114d053bb0ed023e18abe4123c33b4788ac00000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3808.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3808.json new file mode 100644 index 00000000000..04eeb1ba0cb --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3808.json @@ -0,0 +1,5 @@ +{ + "hash": "7b6139ea0a13db47ac6810ac4242a66b591daddc8e3ddd05a5e62f329cf44e07", + "height":3808, + "block": "000000206feecb9ebabd220e07ce5599a88c093c678718eb7f02415f030e847fa60100000a5f809a05dec93cc596f504e4a89bf8fa689992aafd4b3dc9bca5a60602ad82a349675effff7f20010000000603000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502e00e0106ffffffff0230962f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac8ce50b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200e00e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e38000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd49010100e00e00000100015cf0253689f140d713efca32a6e6198c2a3e5b98b3bd7d7e9b52ec5e649adf3c3200000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000001210f3b18b785fbfccc6f2db199fb77ee5130dc16a9a8fbc0f6a087d84cd529b0020000006a4730440220212a900f35cd111aae689732bca7c64f42c45876e83cfabc7b310c51a29528720220554213062992637107bf80b175725a90e74ba8200bce09c53ba4457630c67fac012103a0052777db8209b8b06b7d920618b7e24e5458d6aa306bd0bdae1b46d7a16db0ffffffff011b266bee000000001976a9143ec33076ba72b36b66b7ec571dd7417abdeb76f888ac000000000300000004fdb6c37453373c708a592778481db71b0c9f6cd9573944949546ccc69af1c44d000000006a47304402206d743614495015da6dea6c7be531adce344056aa372c2a8d5134db4adff1e87e02202e68b9cca8ca718089b3ba4ec4bea8e3a09f7562d0673a358e710d33b438471801210299cedad4ed1004cbdfeff7288534d30440114780dae2a82016d05a8f1fd2e93affffffff0c4999363513fc016e79ef0723cffd319a104a7d4890471e9846d72741a948c3010000006b483045022100d5774caf612ae3bfdcc722a93ccfa006ac199a3cb1fe4f3dc5c4f373fff564f60220415b6c3621001af8974e60bb2af85b1083a2849d0e60809ef9fb936eeb671bcb01210351e9e072c2acea8b7162bd380e497185b15552e1815e62134c53525f525ba8ebfffffffffdb6c37453373c708a592778481db71b0c9f6cd9573944949546ccc69af1c44d010000006b483045022100fe7ceee63259caa8946d6210eb6fd3a435067a7fde49ecf8eea7329845d8718802205a695657486136ce1cac22d90f355218eaa92f764f3ec6aaf123932fc284bd4101210262ab703d9173f8208d8f671c0db3e6863535b46b26a491116d3cc58008842412ffffffff210f3b18b785fbfccc6f2db199fb77ee5130dc16a9a8fbc0f6a087d84cd529b0010000006b4830450221008bda13004f4364622e5f72b6140abc7d9baa524ddc6242b6729c89dbc2b59d5e02202a720542df63d3b9aa3fcbec131dc6bb331d43558a891699c245d8c1bb6078270121024ec94cf7077efd3e9ead6ade2664e69b0f8fa082d0a4809d910c43177c1c1d5affffffff018953d347010000001976a9143ec33076ba72b36b66b7ec571dd7417abdeb76f888ac0000000003000000049caab7fa2cec96b090d8affd11261548eb86365d81e974bd14cddc726d7b0567000000006a473044022059290b27ccb84d34e82ac9c48e5f92b0489818925e09ee04895b95c845f3db0502201fd418df6d3085936837d068e343dfaaa04bf5168b2a941458495efcdf926b500121039033dde9eae4c7be3e86f76076d80b92ac7bf04a890d108a026ed67e59e95890ffffffff210f3b18b785fbfccc6f2db199fb77ee5130dc16a9a8fbc0f6a087d84cd529b0000000006a473044022049b6ca5e98781234075c6880f12c9dc2773559c24eeeee35737e9a8189f00935022004c2b70b99c1cb91240f83358efa25ed41f7b1f3d68bee10d84e15e0ac05ec310121031bdf910ae2c665034541999252ca6fadcdb8c21982e190e97beb4453db82da11ffffffff5d9896e0eff6f7ae429f25460d3c9aa94ee7691c1db5499776da27b273990a14010000006b483045022100c7c9503a4729122615ad225bc5fca6edecedbdec5f5885ff89d4a71950a9666902206a84095ac119901dd1b995f89cfab955944e3b727d1f9551118e12b9525d3bae0121039033dde9eae4c7be3e86f76076d80b92ac7bf04a890d108a026ed67e59e95890ffffffff0c4999363513fc016e79ef0723cffd319a104a7d4890471e9846d72741a948c3020000006b483045022100a54ead4b5b87786c8df57f440285b864d127023bc668cfa06ece3f317a133df50220100e41692a283161da23e88a718c4d104dab125f66b960fc4a56bb56d49741880121039033dde9eae4c7be3e86f76076d80b92ac7bf04a890d108a026ed67e59e95890ffffffff0180c09dd0000000001976a9143ec33076ba72b36b66b7ec571dd7417abdeb76f888ac000000000300000003284b0686bd4639e577d66c8c509fc8b85943fcbb497b4ebf8e89fcb61585399f000000006a47304402205bb4f7880fb0fc13218940ba341c30e817363e5590343d28639af921b2a5f1d40220010920ae4b00bbb657f8653cb44172b8cb13447bb5105ddaf32a2845ea0666b90121025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794bffffffff5727a8381dad905632cd74db7937d52458c6ee35b9191daf06eaa85f34d912b8000000006b483045022100ea2d17ffc417e1f70c9c9ae11b7d95a07ab359c1d9d634baba145bab7b1deb0802207507296e12acc83ce038e5bbd54c46fa78b9475536f64fb313fedb978d12b73b0121025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794bffffffffaca2282fa44b98b66bf593fb5f940ec21a0477329ed5952ae40d6e5bbd7b0b37000000006a47304402207926bf9176bdc88f38dde2140b2b8b0e4f331f33bb48af12c1bcce5efbb2593c022073c188d2149d5a0bfe4adff82b63d0bc62e04f2769cdcfda50a2c5e34ab7cbf60121025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794bffffffff013538dc06030000001976a9143ec33076ba72b36b66b7ec571dd7417abdeb76f888ac00000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3809.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3809.json new file mode 100644 index 00000000000..e0267898ff5 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3809.json @@ -0,0 +1,5 @@ +{ + "hash": "00000112f2341beee4c82d46d4372329d812ed8e774dc2e3dbe37c73ec10a75e", + "height":3809, + "block": "00000020074ef49c322fe6a505dd3d8edcad1d596ba64242ac1068ac47db130aea39617bf1ad00e1a5a8f3a0152f7eae975cf19335156be839d5697154db882c90b26b3faf4a675e7745021e2d2300000303000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502e10e010cffffffff02a4902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac29e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200e10e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e38000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd49010100e10e00000100015cf0253689f140d713efca32a6e6198c2a3e5b98b3bd7d7e9b52ec5e649adf3c32000000000000003200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000015b6eae4193bfd8816f04c409d7a899b910dcb897398feeb79ca04a3966409cea000000006b483045022100d7dcae4c6ed2f165c3e68d5aae91e20bc8e8c3b8cbbea9d7bb3483abdd1db5990220770146997086e655e14839777e7161594b3e5a0fa5b97b4cd2996ab8e07a6c1a0121025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794bffffffff016837dc06030000001976a9140a07be5138149ca9a1b2cc26fc575548fe6d0ff288ac00000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3810.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3810.json new file mode 100644 index 00000000000..a63d5b227d3 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3810.json @@ -0,0 +1,5 @@ +{ + "hash": "75d76c2f9fff4e5b190c8966d1409b8461a0f3edf0dca65da80df9bbe41c5faa", + "height":3810, + "block": "000000205ea710ec737ce3dbe3c24d778eed12d8292337d4462dc8e4ee1b34f21201000012e513120ea0430ecf2bcc2dd01ed6941c3d2cfcba544f4488361fb725972b37dc4b675effff7f20050000000203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502e20e0107ffffffff0200902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200e20e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e38000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd49010100e20e00000100015cf0253689f140d713efca32a6e6198c2a3e5b98b3bd7d7e9b52ec5e649adf3c320000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3811.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3811.json new file mode 100644 index 00000000000..2ed86a887e6 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3811.json @@ -0,0 +1,5 @@ +{ + "hash": "000002191547ddd731a88c429b6f39be6da95de6c082c5a8f0e77f3c0a0e8b7c", + "height":3811, + "block": "00000020aa5f1ce4bbf90da85da6dcf0edf3a061849b40d166890c195b4eff9f2f6cd775aa91830be47be3ad6babf40097c0d3b23eca41c5b258df692bbbd4edc0d30ed20c4c675e7745021ec0a700000103000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502e30e0106ffffffff0200902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200e30e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e380000000000000000000000000000000000000000000000000000000000000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3812.json b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3812.json new file mode 100644 index 00000000000..fd815f34e98 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/3812.json @@ -0,0 +1,5 @@ +{ + "hash": "000001a1bc6bc5051ba47e41751fe146ce465b780848a5d77d453761d21ce11c", + "height":3812, + "block": "000000207c8b0e0a3c7fe7f0a8c582c0e65da96dbe396f9b428ca831d7dd471519020000e9e4db4238445490792a3983dc3c71a5407a1e1236067a025693bf94a501d5d7444c675e7745021ef7ee00000103000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0502e40e010affffffff0200902f50090000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200e40e0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e380000000000000000000000000000000000000000000000000000000000000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/blocks.js b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/blocks.js new file mode 100644 index 00000000000..44b57db171c --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/blocks/blocks.js @@ -0,0 +1,32 @@ +const heights = { + 3798: '6736ee511514c2c644224a9c7dc380cdeb0cc1a35ad1718ffe419947a57df645', + 3799: '00000014a92cb29b478ea233a21bda2f28335237bb43186bb264d093cd5df0de', + 3800: '0000003a0ed27c5eb4721929170170f85e507531716173345d6954eb0de900fe', + 3801: '70d65c760880645a741af2bd9176998903c3dc9b2907a6bd35cd7a6171442917', + 3802: '104851b5d0b49e6d3780f42b278076f292f8d2a5aa807000fc051846597244f9', + 3803: '000001e691883ccd6cdb81f1f9d2c28ee02148c4995783c2d958c198d34be845', + 3804: '0000000483e69d37f3a07b40cc7e9bd40ab9c5d2119bd688f5c855942c83925f', + 3805: '000001d13389375c3aaf53d60c3a608d00cf37e38726adaea1047c1a1a6780b6', + 3806: '000001bef9c8bf377880b383ce54397d44a23c70545de59a1ac8bd6c6929bb1c', + 3807: '000001a67f840e035f41027feb1887673c098ca89955ce070e22bdba9ecbee6f', + 3808: '7b6139ea0a13db47ac6810ac4242a66b591daddc8e3ddd05a5e62f329cf44e07', + 3809: '00000112f2341beee4c82d46d4372329d812ed8e774dc2e3dbe37c73ec10a75e', + 3810: '75d76c2f9fff4e5b190c8966d1409b8461a0f3edf0dca65da80df9bbe41c5faa', + 3811: '000002191547ddd731a88c429b6f39be6da95de6c082c5a8f0e77f3c0a0e8b7c', + 3812: '000001a1bc6bc5051ba47e41751fe146ce465b780848a5d77d453761d21ce11c', + 21539: '000003ed50eee6aec24a272f2fd9a5ad996246ce4f05d6bb4185eff8353789df', + 21543: '0000012e44c8e2b6c35025df2d59e173f7fe694ee4a1526659f8ea855f62c3fa', + 21546: '00000261de92f250266fc09b501813d7a0c68a311cddc35c1799905342358fd8', + 21635: '00000152661e6a516ea516f68fc5b11f2368d3680d4d554ddd1705a7d7868d87', + 21638: '00000011eb4dfa5034e1dd16c46e1a14b74c403a62c6fb61abccf72edd42b9d3', +}; + +const hashes = Object.entries(heights).reduce((obj, [height, hash]) => { + // eslint-disable-next-line no-param-reassign + obj[hash] = height; + return obj; +}, {}); + +module.exports = { + heights, hashes, +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/05e8107d56c8c2635232f29967da1bcac709c34358a6f4e7bfb6840bc69da6b5.json b/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/05e8107d56c8c2635232f29967da1bcac709c34358a6f4e7bfb6840bc69da6b5.json new file mode 100644 index 00000000000..25ed0e18a27 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/05e8107d56c8c2635232f29967da1bcac709c34358a6f4e7bfb6840bc69da6b5.json @@ -0,0 +1,6 @@ +{ + "hash": "05e8107d56c8c2635232f29967da1bcac709c34358a6f4e7bfb6840bc69da6b5", + "blockHash": "00000011eb4dfa5034e1dd16c46e1a14b74c403a62c6fb61abccf72edd42b9d3", + "blockHeight": 21638, + "transaction": "0300000003ef77c1ffb0bd6d754c96ba2431a258f5adbdb2b36abe20051fc40610affa2125010000006b48304502210099bbb32cef508df77ac744c3aafc92df61f56eb73758ca391bf06885f9ffe6a2022016b28a212e42a89f8f53a789380d1efb143ccb9a31b0be23fa69f1bba162ec9e012103c5789b847c292aaf1cd0d7e4543cbf5d3394c2ab2edd5bf2639f181378f62815ffffffffef77c1ffb0bd6d754c96ba2431a258f5adbdb2b36abe20051fc40610affa2125020000006a47304402202a7a9e7e556382e7364d6013a45ab20c0197b5e090b91cdeba9d32454c21207a0220745d00507d4a327782af366d234e278d9497464b7c756ae6369f0197551c823e0121034e2af51d2a435dc3294bd8f9ec5ca0d9e64c78e1b4372ba207dba5c311ca9138ffffffffef77c1ffb0bd6d754c96ba2431a258f5adbdb2b36abe20051fc40610affa2125030000006a4730440220709f8243d6462529734c954fe2c78e8c453a51fbeea42c5a94a19791089a807402202bb4bf4a73b7a621b065a33120826e66619d2bc9a7ecd812b964a66bf03a6811012103b73217183ee40ce56b20feb924b4ab5acba9c2e66f9025fb5b5219a492733ac1ffffffff0580f0fa02000000001976a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac80f0fa02000000001976a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac80f0fa02000000001976a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac80f0fa02000000001976a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac001b2c04000000001976a914bc41039fb091e15ebc7a3b116f5191a0cb3ac24988ac00000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/1039026788f80b4199cc685c5ba9335aac976e25b65a59c816108cbdb6234eb6.json b/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/1039026788f80b4199cc685c5ba9335aac976e25b65a59c816108cbdb6234eb6.json new file mode 100644 index 00000000000..c64c490c33a --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/1039026788f80b4199cc685c5ba9335aac976e25b65a59c816108cbdb6234eb6.json @@ -0,0 +1,6 @@ +{ + "hash": "1039026788f80b4199cc685c5ba9335aac976e25b65a59c816108cbdb6234eb6", + "blockHash": "00000261de92f250266fc09b501813d7a0c68a311cddc35c1799905342358fd8", + "blockHeight": 21546, + "transaction": "020000000135d3c2fe20a78d2bb8bb6cf9519f5bc44eed1a09db125b7530e990a1531bceeb010000006b483045022100c2b0c215ca0ba788f5be85b16fcb6889703c27ef41564651058c6f6b9df196db0220141405d97886b16dc4241f2e768f0813ee4587253d16d61e07ecb40424420f91012102479dc303864711d31eefc4b30ca8646acc8f8c427e975b596a652562faf15a8afeffffff0200ca9a3b000000001976a9142883a66f510c4aa8f4abacb09e7372be54477cbe88acfc46f5ab000000001976a914383eb45e8d77b1985f43877c6aa7080618bcffec88ac29540000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/2521faaf1006c41f0520be6ab3b2bdadf558a23124ba964c756dbdb0ffc177ef.json b/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/2521faaf1006c41f0520be6ab3b2bdadf558a23124ba964c756dbdb0ffc177ef.json new file mode 100644 index 00000000000..9998cede38d --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/2521faaf1006c41f0520be6ab3b2bdadf558a23124ba964c756dbdb0ffc177ef.json @@ -0,0 +1,6 @@ +{ + "hash": "2521faaf1006c41f0520be6ab3b2bdadf558a23124ba964c756dbdb0ffc177ef", + "blockHash": "00000152661e6a516ea516f68fc5b11f2368d3680d4d554ddd1705a7d7868d87", + "blockHeight": 21635, + "transaction": "0200000001378b9538dd0a0107f26d107a74544afc84e94ab6cd0c80ce35d398c06c622dab010000006b48304502210096a62fd352dc5827fd26f2c02c147686f1cab5ace1b9f30a78408f67bb89322a02204a8b2bbbb5027e74283a2381f268d20ac0d985e4833e0becc89a312bdee9458301210231898f2676e290d627633f7ed07df5e633d633c831cfa81352034f57dd08f752feffffff0200ca9a3b000000001976a91419ba12c566e1e21deafa60f2a6de42d15f85dbbc88acc0dc2a23010000001976a914832e0cbf5107b230cc8eed200425709f765ba17888ac22540000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/410d43dde088a5ea41956b96471a7ec317b7a8f7a9c69b07eed6326f42ff446a.json b/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/410d43dde088a5ea41956b96471a7ec317b7a8f7a9c69b07eed6326f42ff446a.json new file mode 100644 index 00000000000..456eee1fe83 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/410d43dde088a5ea41956b96471a7ec317b7a8f7a9c69b07eed6326f42ff446a.json @@ -0,0 +1,6 @@ +{ + "hash": "410d43dde088a5ea41956b96471a7ec317b7a8f7a9c69b07eed6326f42ff446a", + "blockHash": "00000011eb4dfa5034e1dd16c46e1a14b74c403a62c6fb61abccf72edd42b9d3", + "blockHeight": 21638, + "transaction": "0300000001ef77c1ffb0bd6d754c96ba2431a258f5adbdb2b36abe20051fc40610affa2125000000006b48304502210089ab02a3bb881bda0104061e4f28edf3200f1e85bd847ba54ab016305947284002200d75bf126b563046756e95f2057164d49f6d9c4cf8a6ab585b10ce1b925767650121020d242c568443a69b4f11b04848f9e2b82edbc7b93c041beedd041d50c9760c17ffffffff01b3495d05000000001976a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac00000000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/4fe7485a000db585c5fe2864fc8686f0c0e9b6b3af01b800d8fc367400848998.json b/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/4fe7485a000db585c5fe2864fc8686f0c0e9b6b3af01b800d8fc367400848998.json new file mode 100644 index 00000000000..91008383d2f --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/4fe7485a000db585c5fe2864fc8686f0c0e9b6b3af01b800d8fc367400848998.json @@ -0,0 +1,6 @@ +{ + "hash": "4fe7485a000db585c5fe2864fc8686f0c0e9b6b3af01b800d8fc367400848998", + "blockHash": "000003ed50eee6aec24a272f2fd9a5ad996246ce4f05d6bb4185eff8353789df", + "blockHeight": 21539, + "transaction": "0200000001378b9538dd0a0107f26d107a74544afc84e94ab6cd0c80ce35d398c06c622dab010000006b48304502210096a62fd352dc5827fd26f2c02c147686f1cab5ace1b9f30a78408f67bb89322a02204a8b2bbbb5027e74283a2381f268d20ac0d985e4833e0becc89a312bdee9458301210231898f2676e290d627633f7ed07df5e633d633c831cfa81352034f57dd08f752feffffff0200ca9a3b000000001976a91419ba12c566e1e21deafa60f2a6de42d15f85dbbc88acc0dc2a23010000001976a914832e0cbf5107b230cc8eed200425709f765ba17888ac22540000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/ebce1b53a190e930755b12db091aed4ec45b9f51f96cbbb82b8da720fec2d335.json b/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/ebce1b53a190e930755b12db091aed4ec45b9f51f96cbbb82b8da720fec2d335.json new file mode 100644 index 00000000000..714cc6abe71 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/transactions/ebce1b53a190e930755b12db091aed4ec45b9f51f96cbbb82b8da720fec2d335.json @@ -0,0 +1,6 @@ +{ + "hash": "ebce1b53a190e930755b12db091aed4ec45b9f51f96cbbb82b8da720fec2d335", + "blockHash": "0000012e44c8e2b6c35025df2d59e173f7fe694ee4a1526659f8ea855f62c3fa", + "blockHeight": 21543, + "transaction": "0200000001988984007436fcd800b801afb3b6e9c0f08686fc6428fec585b50d005a48e74f010000006b48304502210099e6385f43b3bdd747959090df57f13ff97780a038694d88cd26ab1e08808f3802203144ee095a4fe60571560121da6b7c53455a195b1f0b32f055704edeb399e6c3012103ffc5d6b9084e84c65a3762161f9bcb4f3c1c9231e061af84f140cfd1966b98abfeffffff0200ca9a3b000000001976a91419ba12c566e1e21deafa60f2a6de42d15f85dbbc88acde1190e7000000001976a914ae354e2154e0f57ea7095306d4dbad12c605463088ac26540000" +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/data/utxos/yQ1fb64aeLfgqFKyeV9Hg9KTaTq5ehHm22.json b/packages/wallet-lib/src/transport/FixtureTransport/data/utxos/yQ1fb64aeLfgqFKyeV9Hg9KTaTq5ehHm22.json new file mode 100644 index 00000000000..c40e1ab3782 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/data/utxos/yQ1fb64aeLfgqFKyeV9Hg9KTaTq5ehHm22.json @@ -0,0 +1,11 @@ +{ + "21546": [ + { + "address" : "yQ1fb64aeLfgqFKyeV9Hg9KTaTq5ehHm22", + "txId": "1039026788f80b4199cc685c5ba9335aac976e25b65a59c816108cbdb6234eb6", + "outputIndex": 0, + "satoshis": 1000000000, + "script": "76a9142883a66f510c4aa8f4abacb09e7372be54477cbe88ac" + } + ] +} diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/getBestBlock.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBestBlock.js new file mode 100644 index 00000000000..f9525010f63 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBestBlock.js @@ -0,0 +1,6 @@ +const logger = require('../../../logger'); + +module.exports = async function getBestBlock() { + logger.silly('FakeNet.getBestBlock'); + return this.getBlockByHash(await this.getBestBlockHash()); +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/getBestBlockHash.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBestBlockHash.js new file mode 100644 index 00000000000..3036e32b8fe --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBestBlockHash.js @@ -0,0 +1,3 @@ +module.exports = async function getBestBlockHash() { + return this.blockHash; +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/getBestBlockHeader.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBestBlockHeader.js new file mode 100644 index 00000000000..5b66ae8f700 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBestBlockHeader.js @@ -0,0 +1,6 @@ +const logger = require('../../../logger'); + +module.exports = async function getBestBlockHeader() { + logger.silly('FakeNet.getBestBlockHeader'); + return this.getBlockHeaderByHash(await this.getBestBlockHash()); +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/getBestBlockHeight.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBestBlockHeight.js new file mode 100644 index 00000000000..496671395fb --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBestBlockHeight.js @@ -0,0 +1,3 @@ +module.exports = async function getBestBlockHeight() { + return this.height; +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/getBlockByHash.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBlockByHash.js new file mode 100644 index 00000000000..1865926fd8e --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBlockByHash.js @@ -0,0 +1,9 @@ +const { Block } = require('@dashevo/dashcore-lib'); +const fs = require('fs'); +const blocks = require('../data/blocks/blocks'); + +module.exports = async function getBlockByHash(hash) { + const height = blocks.hashes[hash]; + const blockfile = JSON.parse(fs.readFileSync(`${__dirname}/../data/blocks/${height}.json`)); + return new Block(Buffer.from(blockfile.block, 'hex')); +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/getBlockByHeight.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBlockByHeight.js new file mode 100644 index 00000000000..37b4adf26e2 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBlockByHeight.js @@ -0,0 +1,4 @@ +module.exports = async function getBlockByHeight(height) { + const hash = this.blocks.heights[height]; + return this.getBlockByHash(hash); +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/getBlockHeaderByHash.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBlockHeaderByHash.js new file mode 100644 index 00000000000..24574389fc2 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBlockHeaderByHash.js @@ -0,0 +1,3 @@ +module.exports = async function getBlockHeaderByHash(blockHash) { + return (await this.getBlockByHash(blockHash)).header; +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/getBlockHeaderByHeight.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBlockHeaderByHeight.js new file mode 100644 index 00000000000..c7d7c80dde3 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/getBlockHeaderByHeight.js @@ -0,0 +1,3 @@ +module.exports = async function getBlockHeaderByHeight(blockHeight) { + return (await this.getBlockByHeight(blockHeight)).header; +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/getStatus.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/getStatus.js new file mode 100644 index 00000000000..32b4997dad6 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/getStatus.js @@ -0,0 +1,42 @@ +module.exports = async function getStatus() { + const { height, relayFee, network } = this; + + return { + version: { + protocol: 70218, + software: 170000, + agent: '/Dash Core:0.17.0/', + }, + time: { + now: 1616495891, + offset: 0, + median: 1615546573, + }, + status: 'READY', + syncProgress: 0.9999993798366165, + chain: { + name: network, + headersCount: height, + blocksCount: height, + bestBlockHash: '0000007464fd8cae97830d794bf03efbeaa4b8c3258a3def67a89cdbd060f827', + difficulty: 0.002261509525429119, + chainWork: '000000000000000000000000000000000000000000000000022f149b98e063dc', + isSynced: true, + syncProgress: 0.9999993798366165, + }, + masternode: { + status: 'READY', + proTxHash: '04d06d16b3eca2f104ef9749d0c1c17d183eb1b4fe3a16808fd70464f03bcd63', + posePenalty: 0, + isSynced: true, + syncProgress: 1, + }, + network: { + peersCount: 8, + fee: { + relay: relayFee, + incremental: 0.00001, + }, + }, + }; +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/getTransaction.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/getTransaction.js new file mode 100644 index 00000000000..531ff9115bb --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/getTransaction.js @@ -0,0 +1,7 @@ +const { Transaction } = require('@dashevo/dashcore-lib'); +const fs = require('fs'); + +module.exports = async function getTransaction(transactionHash) { + const txFile = JSON.parse(fs.readFileSync(`${__dirname}/../data/transactions/${transactionHash}.json`)); + return new Transaction(Buffer.from(txFile.transaction, 'hex')); +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/sendTransaction.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/sendTransaction.js new file mode 100644 index 00000000000..c481f0dac07 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/sendTransaction.js @@ -0,0 +1,3 @@ +module.exports = async function sendTransaction() { + throw new Error('Not yet handled'); +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/subscribeToAddressesTransactions.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/subscribeToAddressesTransactions.js new file mode 100644 index 00000000000..f6630eb351a --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/subscribeToAddressesTransactions.js @@ -0,0 +1,74 @@ +const EVENTS = require('../../../EVENTS'); +const logger = require('../../../logger'); +// Artifact from previous optimisation made in SyncWorker plugin +// Kept for reminder when Bloomfilters + +// Thoses are addresses that were used only once, and long time ago. +// Low chance of receiving fund. We still check every ten minutes +// const slowFetchThresold = 5 * 60 * 1000; +// Those are addresses that we consider standard, InstantSend promise a one minute time, +// That is what we offer here (will be changed with streams) +// const fetchThreshold = 60 * 1000; +// Those are special cases, such as the current unusedAddress for instance, +// Higher chance of receiving tx, we listen in a quite spammy ways. +const fastFetchThreshold = 15 * 1000; + +// Loop will go through every 15 sec + +async function executor(forcedAddressList = null) { + const self = this; + const { addresses } = self.state.subscriptions; + const addressList = forcedAddressList || Object.keys(addresses); + logger.silly(`FakeNet.subscribeToAddrTx.executor[${addressList}]`); + const fetchedUtxos = {}; + addressList.forEach((address) => { + addresses[address].last = +new Date(); + fetchedUtxos[address] = []; + }); + + const utxos = (await self.getUTXO(addressList)); + + utxos.forEach((utxo) => { + const { address, txid, outputIndex } = utxo; + fetchedUtxos[address].push(utxo); + if (self.state.addressesTransactionsMap[address][txid] === undefined) { + self.getTransaction(txid).then((tx) => { + self.state.addressesTransactionsMap[address][txid] = outputIndex; + self.announce(EVENTS.FETCHED_TRANSACTION, tx); + }); + } + }); + addressList.forEach((address) => { + self.announce(EVENTS.FETCHED_ADDRESS, { address, utxos: fetchedUtxos[address] }); + }); +} + +function startExecutor() { + const self = this; + logger.silly('FakeNet.subscribeToAddressesTransactions.startExecutor'); + this.state.executors.addresses = setInterval(() => executor.call(self), fastFetchThreshold); +} + +module.exports = async function subscribeToAddressesTransactions(addressList) { + logger.silly(`FakeNet.subscribeToAddressesTransactions[${addressList}]`); + if (!Array.isArray(addressList)) throw new Error('Expected array of addresses'); + const { executors, subscriptions, addressesTransactionsMap } = this.state; + + const immediatelyExecutedAddresses = []; + addressList.forEach((address) => { + if (!subscriptions.addresses[address]) { + if (!addressesTransactionsMap[address]) { + addressesTransactionsMap[address] = {}; + } + immediatelyExecutedAddresses.push(address); + subscriptions.addresses[address] = { priority: 1, last: null }; + } + }); + + if (!executors.addresses) { + startExecutor.call(this); + } + if (immediatelyExecutedAddresses.length) { + await Promise.resolve(executor.call(this, immediatelyExecutedAddresses)); + } +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/subscribeToBlockHeaders.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/subscribeToBlockHeaders.js new file mode 100644 index 00000000000..4fa4ffbb313 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/subscribeToBlockHeaders.js @@ -0,0 +1,17 @@ +const EVENTS = require('../../../EVENTS'); + +module.exports = async function subscribeToBlockHeaders() { + const self = this; + const { executors } = this.state; + + const executor = async () => { + const chainHash = await this.getBestBlockHash(); + if (!self.state.blockHeader || self.state.blockHeader.hash !== chainHash) { + self.state.blockHeader = await self.getBlockHeaderByHash(chainHash); + self.announce(EVENTS.BLOCKHEADER, self.state.blockHeader); + } + }; + await executor(); + const refreshBlockInterval = 10 * 1000;// Every 10s + executors.blockHeaders = setInterval(() => executor(), refreshBlockInterval); +}; diff --git a/packages/wallet-lib/src/transport/FixtureTransport/methods/subscribeToBlocks.js b/packages/wallet-lib/src/transport/FixtureTransport/methods/subscribeToBlocks.js new file mode 100644 index 00000000000..a906f4575d1 --- /dev/null +++ b/packages/wallet-lib/src/transport/FixtureTransport/methods/subscribeToBlocks.js @@ -0,0 +1,17 @@ +const EVENTS = require('../../../EVENTS'); + +module.exports = async function subscribeToBlocks() { + const self = this; + const { executors } = this.state; + + const executor = async () => { + const chainHash = await this.getBestBlockHash(); + if (!self.state.block || self.state.block.hash !== chainHash) { + self.state.block = await self.getBlockByHash(await self.getBestBlockHash()); + self.announce(EVENTS.BLOCK, self.state.block); + } + }; + await executor(); + const refreshBlockInterval = 10 * 1000;// Every 10s + executors.blocks = setInterval(() => executor(), refreshBlockInterval); +}; diff --git a/packages/wallet-lib/src/transport/Transport.d.ts b/packages/wallet-lib/src/transport/Transport.d.ts new file mode 100644 index 00000000000..309df85abc2 --- /dev/null +++ b/packages/wallet-lib/src/transport/Transport.d.ts @@ -0,0 +1,37 @@ +import {Block, BlockHeader, Transaction} from "@dashevo/dashcore-lib"; + +export declare interface Transport { + announce(eventName, args) + + disconnect() + + getBestBlock(): Promise + + getBestBlockHash(): Promise + + getBestBlockHeader(): Promise + + getBestBlockHeight(): Promise + + getBlockByHash(hash): Promise + + getBlockByHeight(height): Promise + + getBlockHeaderByHash(hash): Promise + + getBlockHeaderByHeight(height): Promise + + getIdentitiesByPublicKeyHashes(publicKeyHashes: Buffer[]): Promise + + getStatus(): Promise + + getTransaction(txid): Promise + + sendTransaction(serializedTransaction): Promise + + subscribeToAddressesTransactions() + + subscribeToBlockHeaders() + + subscribeToBlocks() +} diff --git a/packages/wallet-lib/src/transport/createTransportFromOptions.js b/packages/wallet-lib/src/transport/createTransportFromOptions.js new file mode 100644 index 00000000000..52ffbde993a --- /dev/null +++ b/packages/wallet-lib/src/transport/createTransportFromOptions.js @@ -0,0 +1,23 @@ +const DAPIClient = require('@dashevo/dapi-client'); + +const _ = require('lodash'); + +const DAPIClientTransport = require('./DAPIClientTransport/DAPIClientTransport'); + +/** + * + * @param {DAPIClientOptions|Transport|DAPIClientTransport} options + * @returns {Transport|DAPIClientTransport} + */ +function createTransportFromOptions(options) { + if (!_.isPlainObject(options)) { + // Return transport instance + return options; + } + + const client = new DAPIClient(options); + + return new DAPIClientTransport(client); +} + +module.exports = createTransportFromOptions; diff --git a/packages/wallet-lib/src/types/Account/.eslintrc b/packages/wallet-lib/src/types/Account/.eslintrc new file mode 100644 index 00000000000..a8022ef2b8a --- /dev/null +++ b/packages/wallet-lib/src/types/Account/.eslintrc @@ -0,0 +1,5 @@ +{ + "rules": { + "import/newline-after-import": "off" + } +} diff --git a/packages/wallet-lib/src/types/Account/Account.d.ts b/packages/wallet-lib/src/types/Account/Account.d.ts new file mode 100644 index 00000000000..eea478bab5f --- /dev/null +++ b/packages/wallet-lib/src/types/Account/Account.d.ts @@ -0,0 +1,122 @@ +import { + Transaction, + TransactionHistory, + AddressObj, + AddressInfo, + AddressType, + transactionId, + PublicAddress, + PrivateKey, + Strategy, + Network, + broadcastTransactionOpts, + Plugins, RawTransaction, TransactionsMap, WalletObj, StatusInfo +} from "../types"; +import { DerivableKeyChain } from "../DerivableKeyChain/DerivableKeyChain"; +import { InstantLock } from "@dashevo/dashcore-lib"; +import { Identities, Wallet} from "../../index"; +import { Transport } from "../../transport/Transport"; +import { BlockHeader } from "@dashevo/dashcore-lib/typings/block/BlockHeader"; +import { UnspentOutput } from "@dashevo/dashcore-lib/typings/transaction/UnspentOutput"; +import { Storage } from "../Storage/Storage"; + +export declare class Account { + constructor(wallet: Wallet, options?: Account.Options); + + index: number; + injectDefaultPlugins?: boolean; + allowSensitiveOperations?: boolean; + debug?: boolean; + cacheTx?: boolean; + cacheBlockHeaders?: boolean; + label?: string | null; + strategy?: Strategy; + keyChainSore: KeyChainStore; + state: any; + storage: Storage; + store: Storage.store; + walletId: string; + transport: Transport; + identities: Identities; + + isReady(): Promise; + isInitialized(): Promise; + getBIP44Path(network?: Network, index?: number): string; + getNetwork(): Network; + + broadcastTransaction(rawtx: Transaction|RawTransaction, options?: broadcastTransactionOpts): Promise; + connect(): boolean; + createTransaction(opts: Account.createTransactionOptions): Transaction; + decode(method: string, data: any): any; + decrypt(method: string, data: any, secret: string, encoding?: "hex"|string): string; + encrypt(method: string, data: any, secret: string): string; + disconnect(): Promise; + fetchAddressInfo(addressObj: AddressObj, fetchUtxo: boolean): Promise; + fetchStatus(): Promise; + forceRefreshAccount(): boolean; + generateAddress(path: string): AddressObj; + getAddress(index: number, _type: AddressType): AddressObj; + getAddresses(_type: AddressType): [AddressObj]; + getBlockHeader(identifier: string|number):Promise + getConfirmedBalance(displayDuffs?: boolean): number; + getPlugin(name: string): Object; + getPrivateKeys(addressList: [PublicAddress]): [PrivateKey]; + getTotalBalance(displayDuffs?: boolean): number; + getTransaction(txid: transactionId): Transaction; + getTransactionHistory(): TransactionHistory; + getTransactions(): [Transaction]; + getUTXOS(): [UnspentOutput]; + getUnconfirmedBalance(displayDuffs?: boolean): number; + getUnusedAddress(type?: AddressType, skip?: number): AddressObj; + getUnusedIdentityIndex(): Promise; + getWorker(workerName: string): Object; + hasPlugins([Plugin]): {found:Boolean, results:[{name: string}]}; + injectPlugin(unsafePlugin: Plugins, allowSensitiveOperation?: boolean, awaitOnInjection?: boolean): Promise; + sign(object: Transaction, privateKeys: [PrivateKey], sigType?: number): Transaction; + waitForInstantLock(transactionHash: string): { promise: Promise, cancel: function }; + waitForTxMetadata(transactionHash: string): { promise: Promise, cancel: function }; +} + +export declare interface RecipientOptions { + satoshis?: number; + amount?: number; + address: PublicAddress, +} +export declare interface getUTXOSOptions { + coinbaseMaturity?: number; +} + +export declare namespace Account { + interface Options { + index?: number, + network?: Network; + debug?: boolean; + label?: string; + plugins?: [Plugins]; + cacheBlockHeaders?: boolean; + cacheTx?: boolean; + allowSensitiveOperations?: boolean; + injectDefaultPlugins?: boolean; + strategy?: Strategy; + cache?:{ + transactions?:TransactionsMap, + addresses?:WalletObj["addresses"] + } + } + + interface createTransactionOptions { + recipient?: PublicAddress, + satoshis?: number, + amount?: number, + + recipients?: [RecipientOptions] + + change?: string; + utxos?: [object]; + isInstantSend?: boolean; + deductFee?: boolean + privateKeys?: [PrivateKey], + strategy?: Strategy + } + +} diff --git a/packages/wallet-lib/src/types/Account/Account.js b/packages/wallet-lib/src/types/Account/Account.js new file mode 100644 index 00000000000..0e93636c2b2 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/Account.js @@ -0,0 +1,410 @@ +const _ = require('lodash'); +const EventEmitter = require('events'); +const logger = require('../../logger'); +const { WALLET_TYPES, BIP44_ADDRESS_GAP } = require('../../CONSTANTS'); +const { is } = require('../../utils'); +const EVENTS = require('../../EVENTS'); +const Wallet = require('../Wallet/Wallet'); +const { simpleDescendingAccumulator } = require('../../utils/coinSelections/strategies'); +const { + TxMetadataTimeoutError, + InstantLockTimeoutError, +} = require('../../errors'); + +function getNextUnusedAccountIndexForWallet(wallet) { + if (wallet && wallet.accounts) { + if (!wallet.accounts.length) return 0; + + const indexes = wallet.accounts.reduce((acc, curr) => { + acc.push(curr.index); + return acc; + }, []).sort(); + let index; + for (let i = 0; i <= indexes[indexes.length - 1] + 1; i += 1) { + if (!indexes.includes(i)) { + index = i; + break; + } + } + return index; + } + throw new Error('An account is attached to a wallet that has not been provided to the account constructor.'); +} + +const defaultOptions = { + network: 'testnet', + cacheTx: true, + cacheBlockHeaders: true, + allowSensitiveOperations: false, + plugins: [], + injectDefaultPlugins: true, + debug: false, + strategy: simpleDescendingAccumulator, +}; + +/* eslint-disable no-underscore-dangle */ +const _initializeAccount = require('./_initializeAccount'); +const _addAccountToWallet = require('./_addAccountToWallet'); +const _loadStrategy = require('./_loadStrategy'); + +const getNetwork = require('./_getNetwork'); +const getBIP44Path = require('./_getBIP44Path'); + +class Account extends EventEmitter { + constructor(wallet, opts = defaultOptions) { + super(); + if (!wallet || wallet.constructor.name !== Wallet.name) throw new Error('Expected wallet to be passed as param'); + if (!_.has(wallet, 'walletId')) throw new Error('Missing walletID to create an account'); + this.walletId = wallet.walletId; + + logger.debug(`Loading up wallet ${this.walletId}`); + + this.identities = wallet.identities; + this.chainSyncMediator = wallet.chainSyncMediator; + + this.state = { + isInitialized: false, + isReady: false, + isDisconnecting: false, + }; + this.injectDefaultPlugins = _.has(opts, 'injectDefaultPlugins') ? opts.injectDefaultPlugins : defaultOptions.injectDefaultPlugins; + this.allowSensitiveOperations = _.has(opts, 'allowSensitiveOperations') ? opts.allowSensitiveOperations : defaultOptions.allowSensitiveOperations; + this.debug = _.has(opts, 'debug') ? opts.debug : defaultOptions.debug; + // if (this.debug) process.env.LOG_LEVEL = 'debug'; + + this.waitForInstantLockTimeout = wallet.waitForInstantLockTimeout; + this.waitForTxMetadataTimeout = wallet.waitForTxMetadataTimeout; + + this.walletType = wallet.walletType; + this.offlineMode = wallet.offlineMode; + + this.index = _.has(opts, 'index') ? opts.index : getNextUnusedAccountIndexForWallet(wallet); + this.strategy = _loadStrategy(_.has(opts, 'strategy') ? opts.strategy : defaultOptions.strategy); + this.network = getNetwork(wallet.network).toString(); + this.BIP44PATH = getBIP44Path(this.network, this.index); + + this.transactions = {}; + + this.label = (opts && opts.label && is.string(opts.label)) ? opts.label : null; + + // Forward async error events to wallet allowing catching during initial sync + this.on('error', (error, errorContext) => wallet.emit('error', error, { + ...errorContext, + accountIndex: this.index, + network: this.network, + label: this.label, + })); + + // If transport is null or invalid, we won't try to fetch anything + this.transport = wallet.transport; + + this.storage = wallet.storage; + + // Forward all storage event + this.storage.on(EVENTS.CONFIGURED, (ev) => this.emit(ev.type, ev)); + this.storage.on(EVENTS.REHYDRATE_STATE_FAILED, (ev) => this.emit(ev.type, ev)); + this.storage.on(EVENTS.REHYDRATE_STATE_SUCCESS, (ev) => this.emit(ev.type, ev)); + this.storage.on(EVENTS.FETCHED_CONFIRMED_TRANSACTION, (ev) => this.emit(ev.type, ev)); + this.storage.on(EVENTS.UNCONFIRMED_BALANCE_CHANGED, (ev) => this.emit(ev.type, ev)); + this.storage.on(EVENTS.CONFIRMED_BALANCE_CHANGED, (ev) => this.emit(ev.type, ev)); + this.storage.on(EVENTS.TX_METADATA, (ev) => { + this.emit(`${ev.type}:${ev.payload.hash}`, ev.payload.metadata); + }); + this.storage.on(EVENTS.BLOCKHEADER, (ev) => this.emit(ev.type, ev)); + this.storage.on(EVENTS.BLOCKHEIGHT_CHANGED, (ev) => this.emit(ev.type, ev)); + this.storage.on(EVENTS.BLOCK, (ev) => this.emit(ev.type, ev)); + + if (this.debug) { + this.emit = (...args) => { + const { type } = args[1]; + const payload = JSON.stringify(args[1].payload); + logger.debug(`${this.walletId}:${this.index} - Emitted event ${type} - ${payload} `); + super.emit(...args); + }; + } + switch (this.walletType) { + case WALLET_TYPES.HDWALLET: + this.accountPath = getBIP44Path(this.network, this.index); + break; + case WALLET_TYPES.HDPUBLIC: + case WALLET_TYPES.PRIVATEKEY: + case WALLET_TYPES.PUBLICKEY: + case WALLET_TYPES.ADDRESS: + case WALLET_TYPES.SINGLE_ADDRESS: + this.accountPath = 'm/0'; + break; + default: + throw new Error(`Invalid wallet type ${this.walletType}`); + } + + this.storage + .getWalletStore(this.walletId) + .createPathState(this.accountPath); + + let keyChainStorePath = this.index; + const keyChainStoreOpts = {}; + + switch (this.walletType) { + case WALLET_TYPES.HDPUBLIC: + keyChainStorePath = this.accountPath; + keyChainStoreOpts.lookAheadOpts = { + paths: { + 'm/0': BIP44_ADDRESS_GAP, + }, + }; + break; + case WALLET_TYPES.HDWALLET: + case WALLET_TYPES.HDPRIVATE: + keyChainStorePath = this.BIP44PATH; + keyChainStoreOpts.lookAheadOpts = { + paths: { + 'm/0': BIP44_ADDRESS_GAP, + 'm/1': BIP44_ADDRESS_GAP, + }, + }; + break; + default: + break; + } + + this.keyChainStore = wallet + .keyChainStore + .makeChildKeyChainStore(keyChainStorePath, keyChainStoreOpts); + + // This forces keychainStore to set to issued key what is already its masterkey + if ([WALLET_TYPES.PUBLICKEY, WALLET_TYPES.PRIVATEKEY].includes(this.walletType)) { + this.keyChainStore + .getMasterKeyChain() + .getForPath('0', { isWatched: true }); + } + + this.cacheTx = (opts.cacheTx) ? opts.cacheTx : defaultOptions.cacheTx; + this.cacheBlockHeaders = (opts.cacheBlockHeaders) + ? opts.cacheBlockHeaders + : defaultOptions.cacheBlockHeaders; + + this.plugins = { + workers: {}, + standard: {}, + watchers: {}, + }; + + this.emit(EVENTS.CREATED, { type: EVENTS.CREATED, payload: null }); + + /** + * Stores promise that waits for the transaction FETCH event + * @type {Promise} + */ + this.txFetchListener = null; + + // Increases a limit of max listeners for transactions related events + // 25 - mempool limit + this.setMaxListeners(25); + } + + static getInstantLockTopicName(transactionHash) { + return `${EVENTS.INSTANT_LOCK}:${transactionHash}`; + } + + // It's actually Account that mutates wallet.accounts to add itself. + // We might want to get rid of that as it can be really confusing. + // It would gives that responsability to createAccount to create + // (and therefore push to accounts). + async init(wallet) { + await _addAccountToWallet(this, wallet); + await _initializeAccount(this, wallet.plugins); + } + + async isInitialized() { + // eslint-disable-next-line consistent-return + return new Promise(((resolve) => { + if (this.state.isInitialized) return resolve(true); + this.on(EVENTS.INITIALIZED, () => resolve(true)); + })); + } + + async isReady() { + // eslint-disable-next-line consistent-return + return new Promise(((resolve) => { + if (this.state.isReady) return resolve(true); + this.on(EVENTS.READY, () => resolve(true)); + })); + } + + /** + * Imports instant lock to an account and emits message + * @param {InstantLock} instantLock + */ + importInstantLock(instantLock) { + const chainStore = this.storage.getChainStore(this.network); + chainStore.importInstantLock(instantLock); + this.emit(Account.getInstantLockTopicName(instantLock.txid), instantLock); + } + + /** + * @param {string} transactionHash + * @param {function} callback + */ + subscribeToTransactionInstantLock(transactionHash, callback) { + const eventName = Account.getInstantLockTopicName(transactionHash); + + this.once(eventName, callback); + + return () => { + this.removeListener(eventName, callback); + }; + } + + /** + * @param {string} transactionHash + * @param {function} callback + * @returns {function} - cancel subscription + */ + subscribeToTxMetadata(transactionHash, callback) { + const eventName = `${EVENTS.TX_METADATA}:${transactionHash}`; + + this.once(eventName, callback); + + return () => { + this.removeListener(eventName, callback); + }; + } + + /** + * Waits for instant lock for a transaction or throws after a timeout + * @param {string} transactionHash - instant lock to wait for + * @param {number} timeout - in milliseconds before throwing an error if the lock didn't arrive + * @return {{promise: Promise, cancel: Function}} + */ + waitForInstantLock(transactionHash, timeout = this.waitForInstantLockTimeout) { + // Return instant lock immediately if already exists + const chainStore = this.storage.getChainStore(this.network); + const instantLock = chainStore.getInstantLock(transactionHash); + if (instantLock != null) { + return { + promise: Promise.resolve(instantLock), + cancel: () => {}, + }; + } + + let rejectTimeout; + let cancelSubscription; + + function cancel() { + cancelSubscription(); + clearTimeout(rejectTimeout); + } + + // Wait for upcoming instant lock + + const promise = Promise.race([ + new Promise((resolve) => { + cancelSubscription = this.subscribeToTransactionInstantLock( + transactionHash, + (instantLockData) => { + clearTimeout(rejectTimeout); + resolve(instantLockData); + }, + ); + }), + new Promise((resolve, reject) => { + rejectTimeout = setTimeout(() => { + cancelSubscription(); + reject(new InstantLockTimeoutError(transactionHash)); + }, timeout); + }), + ]); + + return { + promise, + cancel, + }; + } + + /** + * Waits for metadata of a transaction or throws an error after a timeout + * @param {string} transactionHash - metadata of tx to wait for + * @param {number} timeout - in ms before throwing an error if the metadata didn't arrive + * @return {{promise: Promise, cancel: Function}} + */ + waitForTxMetadata(transactionHash, timeout = this.waitForTxMetadataTimeout) { + // Return tx metadata immediately if already exists + const chainStore = this.storage.getChainStore(this.network); + const txWithMetadata = chainStore.getTransaction(transactionHash); + + if (txWithMetadata && txWithMetadata.metadata && txWithMetadata.metadata.height) { + return { + promise: Promise.resolve(txWithMetadata.metadata), + cancel: () => {}, + }; + } + + // Wait for upcoming metadata + + let rejectTimeout; + let cancelSubscription; + + function cancel() { + cancelSubscription(); + clearTimeout(rejectTimeout); + } + + const promise = Promise.race([ + new Promise((resolve) => { + cancelSubscription = this.subscribeToTxMetadata(transactionHash, (metadata) => { + clearTimeout(rejectTimeout); + resolve(metadata); + }); + }), + new Promise((resolve, reject) => { + rejectTimeout = setTimeout(() => { + cancelSubscription(); + reject(new TxMetadataTimeoutError(transactionHash)); + }, timeout); + }), + ]); + + return { + promise, + cancel, + }; + } +} + +Account.prototype.broadcastTransaction = require('./methods/broadcastTransaction'); +Account.prototype.connect = require('./methods/connect'); +Account.prototype.createTransaction = require('./methods/createTransaction'); +Account.prototype.decode = require('./methods/decode'); +Account.prototype.decrypt = require('./methods/decrypt'); +Account.prototype.disconnect = require('./methods/disconnect'); +Account.prototype.encode = require('./methods/encode'); +Account.prototype.encrypt = require('./methods/encrypt'); +Account.prototype.fetchStatus = require('./methods/fetchStatus'); +Account.prototype.forceRefreshAccount = require('./methods/forceRefreshAccount'); +Account.prototype.generateAddress = require('./methods/generateAddress'); +Account.prototype.getAddress = require('./methods/getAddress'); +Account.prototype.getAddresses = require('./methods/getAddresses'); +Account.prototype.getBlockHeader = require('./methods/getBlockHeader'); +Account.prototype.getConfirmedBalance = require('./methods/getConfirmedBalance'); +Account.prototype.getPlugin = require('./methods/getPlugin'); +Account.prototype.getPrivateKeys = require('./methods/getPrivateKeys'); +Account.prototype.getTotalBalance = require('./methods/getTotalBalance'); +Account.prototype.getTransaction = require('./methods/getTransaction'); +Account.prototype.getTransactionHistory = require('./methods/getTransactionHistory'); +Account.prototype.getTransactions = require('./methods/getTransactions'); +Account.prototype.getUnconfirmedBalance = require('./methods/getUnconfirmedBalance'); +Account.prototype.getUnusedAddress = require('./methods/getUnusedAddress'); +Account.prototype.getUnusedIdentityIndex = require('./methods/getUnusedIdentityIndex'); +Account.prototype.getUTXOS = require('./methods/getUTXOS'); +Account.prototype.getWorker = require('./methods/getWorker'); +Account.prototype.hasPlugins = require('./methods/hasPlugins'); +Account.prototype.injectPlugin = require('./methods/injectPlugin'); +Account.prototype.importTransactions = require('./methods/importTransactions'); +Account.prototype.importBlockHeader = require('./methods/importBlockHeader'); +Account.prototype.createPathsForTransactions = require('./methods/createPathsForTransactions'); +Account.prototype.generateNewPaths = require('./methods/generateNewPaths'); +Account.prototype.addPathsToStore = require('./methods/addPathsToStore'); +Account.prototype.addDefaultPaths = require('./methods/addDefaultPaths'); +Account.prototype.sign = require('./methods/sign'); + +module.exports = Account; diff --git a/packages/wallet-lib/src/types/Account/Account.spec.js b/packages/wallet-lib/src/types/Account/Account.spec.js new file mode 100644 index 00000000000..b7c2462b8e7 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/Account.spec.js @@ -0,0 +1,112 @@ +const { expect } = require('chai'); +const Dashcore = require('@dashevo/dashcore-lib'); +const knifeMnemonic = require('../../../fixtures/knifeeasily'); +const fluidMnemonic = require('../../../fixtures/fluidDepth'); +const cR4t6ePrivateKey = require('../../../fixtures/cR4t6e_pk'); +const { WALLET_TYPES } = require('../../CONSTANTS'); +const { Account, EVENTS } = require('../../index'); +const EventEmitter = require('events'); +const inMem = require('../../adapters/InMem'); +const Storage = require('../Storage/Storage'); +const {mock} = require("sinon"); +const KeyChainStore = require("../KeyChainStore/KeyChainStore"); +const DerivableKeyChain = require("../DerivableKeyChain/DerivableKeyChain"); +const blockHeader = new Dashcore.BlockHeader.fromObject({ + hash: '00000ac3a0c9df709260e41290d6902e5a4a073099f11fe8c1ce80aadc4bb331', + version: 2, + prevHash: '00000ce430de949c85a145b02e33ebbaed3772dc8f3d668f66edc6852c24d002', + merkleRoot: '663360403b5fba9cd8744c3706f9660c7d3fee4e5a9ee98ce0ad5e5ad7824c1d', + time: 1398712821, + bits: 504365040, + nonce: 312363 +}); +const mocks = { + adapter: inMem, + offlineMode: true, +}; + + +describe('Account - class', function suite() { + this.timeout(10000); + before(() => { + const emitter = new EventEmitter(); + const mockStorage = { + on: emitter.on, + emit: emitter.emit, + storage: new Storage(), + getStore: () => {}, + saveState: () => {}, + createAccount: () => {}, + importBlockHeader: (blockheader)=>{ + mockStorage.emit(EVENTS.BLOCKHEADER, {type: EVENTS.BLOCKHEADER, payload:blockheader}); + } + }; + mocks.wallet = (new (function Wallet() { + this.walletId = '1234567891'; + this.walletType = WALLET_TYPES.HDWALLET; + this.accounts = []; + this.network = Dashcore.Networks.testnet; + this.storage = new Storage(); + })()); + mocks.wallet.storage.application.network = mocks.wallet.network; + mocks.wallet.storage.createWalletStore(mocks.wallet.walletId); + mocks.wallet.storage.createChainStore(mocks.wallet.network); + mocks.wallet.keyChainStore = new KeyChainStore() + mocks.wallet.keyChainStore.addKeyChain(new DerivableKeyChain({mnemonic: fluidMnemonic.mnemonic}), { isMasterKeyChain: true }) + }); + it('should be specify on missing params', () => { + const expectedException1 = 'Expected wallet to be passed as param'; + expect(() => new Account()).to.throw(expectedException1); + }); + it('should create an account', () => { + const mockWallet = mocks.wallet; + const account = new Account(mockWallet, { injectDefaultPlugins: false }); + account.init(mockWallet).then(()=>{ + expect(account).to.be.deep.equal(mockWallet.accounts[0]); + expect(account.index).to.be.deep.equal(0); + expect(account.injectDefaultPlugins).to.be.deep.equal(false); + expect(account.allowSensitiveOperations).to.be.deep.equal(false); + expect(account.state.isReady).to.be.deep.equal(true); + expect(account.type).to.be.deep.equal(undefined); + expect(account.transactions).to.be.deep.equal({}); + expect(account.label).to.be.deep.equal(null); + expect(account.transport).to.be.deep.equal(undefined); + expect(account.cacheTx).to.be.deep.equal(true); + expect(account.plugins).to.be.deep.equal({ + workers: {}, standard: {}, watchers: {}, + }); + + account.disconnect(); + }) + }); + it('should correctly create the right expected index', async () => { + const mockWallet = mocks.wallet; + const account = new Account(mockWallet, { injectDefaultPlugins: false }); + await account.init(mockWallet); + + const account2 = new Account(mockWallet, { index: 10, injectDefaultPlugins: false }); + await account2.init(mockWallet); + + const account3 = new Account(mockWallet, { injectDefaultPlugins: false }); + await account3.init(mockWallet); + + expect(account.index).to.be.deep.equal(1); + expect(account2.index).to.be.deep.equal(10); + expect(account3.index).to.be.deep.equal(2); + account.disconnect(); + account2.disconnect(); + account3.disconnect(); + }); + it('should forward events', function (done) { + const mockWallet = mocks.wallet; + const account = new Account(mockWallet, { injectDefaultPlugins: false }); + account.init(mockWallet) + .then(async ()=>{ + account.on(EVENTS.BLOCKHEADER, ()=>{ + done(); + }); + account.importBlockHeader(blockHeader); + }) + + }); +}); diff --git a/packages/wallet-lib/src/types/Account/_addAccountToWallet.js b/packages/wallet-lib/src/types/Account/_addAccountToWallet.js new file mode 100644 index 00000000000..935b0c90b48 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/_addAccountToWallet.js @@ -0,0 +1,15 @@ +/** + * Add when not existing a element account in a parent wallet + * @param account + * @param wallet + */ +// eslint-disable-next-line no-underscore-dangle +const _addAccountToWallet = function addAccountToWallet(account, wallet) { + const { accounts } = wallet; + + const existAlready = accounts.filter((el) => el.index === wallet.index).length > 0; + if (!existAlready) { + wallet.accounts.push(account); + } +}; +module.exports = _addAccountToWallet; diff --git a/packages/wallet-lib/src/types/Account/_addAccountToWallet.spec.js b/packages/wallet-lib/src/types/Account/_addAccountToWallet.spec.js new file mode 100644 index 00000000000..e3e7d6181d7 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/_addAccountToWallet.spec.js @@ -0,0 +1,17 @@ +const { expect } = require('chai'); +const addAccountToWallet = require('./_addAccountToWallet'); + +describe('Account - addAccountToWallet', function suite() { + this.timeout(10000); + it('should add an account to a wallet', () => { + const wallet = { + accounts: [], + }; + const mockAcc = { + label: 'mockedAccount', + index: 0, + }; + addAccountToWallet(mockAcc, wallet); + expect(wallet.accounts).to.deep.equal([{ label: 'mockedAccount', index: 0 }]); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/_getBIP44Path.js b/packages/wallet-lib/src/types/Account/_getBIP44Path.js new file mode 100644 index 00000000000..b26d039ce84 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/_getBIP44Path.js @@ -0,0 +1,15 @@ +const Dashcore = require('@dashevo/dashcore-lib'); +const { + BIP44_LIVENET_ROOT_PATH, BIP44_TESTNET_ROOT_PATH, +} = require('../../CONSTANTS'); +/** + * Will return a root account path + * @param network - default : 'testnet' + * @param accountIndex - default : 0 + * @return {string} - BIP44 Path to account + */ +module.exports = function getBIP44Path(network, accountIndex = 0) { + return (network === Dashcore.Networks.livenet.toString()) + ? `${BIP44_LIVENET_ROOT_PATH}/${accountIndex}'` + : `${BIP44_TESTNET_ROOT_PATH}/${accountIndex}'`; +}; diff --git a/packages/wallet-lib/src/types/Account/_getBIP44Path.spec.js b/packages/wallet-lib/src/types/Account/_getBIP44Path.spec.js new file mode 100644 index 00000000000..c744d5a677c --- /dev/null +++ b/packages/wallet-lib/src/types/Account/_getBIP44Path.spec.js @@ -0,0 +1,6 @@ +const { expect } = require('chai'); +const Dashcore = require('@dashevo/dashcore-lib'); + + +describe('Account - getBIP44Path', () => { +}); diff --git a/packages/wallet-lib/src/types/Account/_getNetwork.js b/packages/wallet-lib/src/types/Account/_getNetwork.js new file mode 100644 index 00000000000..b3d0ce6a0b5 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/_getNetwork.js @@ -0,0 +1,5 @@ +const Dashcore = require('@dashevo/dashcore-lib'); + +module.exports = function getNetwork(network) { + return Dashcore.Networks[network].toString() || Dashcore.Networks.testnet.toString(); +}; diff --git a/packages/wallet-lib/src/types/Account/_getNetwork.spec.js b/packages/wallet-lib/src/types/Account/_getNetwork.spec.js new file mode 100644 index 00000000000..237c2517800 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/_getNetwork.spec.js @@ -0,0 +1,6 @@ +const { expect } = require('chai'); +const Dashcore = require('@dashevo/dashcore-lib'); + + +describe('Account - getNetwork', () => { +}); diff --git a/packages/wallet-lib/src/types/Account/_initializeAccount.js b/packages/wallet-lib/src/types/Account/_initializeAccount.js new file mode 100644 index 00000000000..ee9daa9d7c4 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/_initializeAccount.js @@ -0,0 +1,76 @@ +const logger = require('../../logger'); +const EVENTS = require('../../EVENTS'); +const preparePlugins = require('./_preparePlugins'); + +// eslint-disable-next-line no-underscore-dangle +async function _initializeAccount(account, userUnsafePlugins) { + const self = account; + + account.addDefaultPaths(); + + // Issue additional derivation paths in case we have transactions in the store + // at the moment of initialization (from persistent storage) + account.createPathsForTransactions(); + + // We run faster in offlineMode to speed up the process when less happens. + const readinessIntervalTime = (account.offlineMode) ? 50 : 200; + // TODO: perform rejection with a timeout + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + try { + // Will sort and inject plugins. + await preparePlugins(account, userUnsafePlugins); + + self.emit(EVENTS.STARTED, { type: EVENTS.STARTED, payload: null }); + + const sendReady = () => { + if (!self.state.isReady) { + self.emit(EVENTS.READY, { type: EVENTS.READY, payload: null }); + self.state.isReady = true; + } + }; + const sendInitialized = () => { + if (!self.state.isInitialized) { + self.emit(EVENTS.INITIALIZED, { type: EVENTS.INITIALIZED, payload: null }); + logger.debug(`Initialized with ${Object.keys(account.plugins.watchers).length} plugins`); + self.state.isInitialized = true; + } + }; + + let readyPlugins = 0; + // eslint-disable-next-line no-param-reassign,consistent-return + account.readinessInterval = setInterval(() => { + const watchedPlugins = Object.keys(account.plugins.watchers); + watchedPlugins.forEach((pluginName) => { + const watchedPlugin = account.plugins.watchers[pluginName]; + if (watchedPlugin.ready === true && !watchedPlugin.announced) { + readyPlugins += 1; + watchedPlugin.announced = true; + logger.debug(`Initialized ${pluginName} - ${readyPlugins}/${watchedPlugins.length} plugins`); + } + }); + if (readyPlugins === watchedPlugins.length) { + // At this stage, our worker are initialized + sendInitialized(); + + // If both of the plugins are present + // We need to tweak it a little bit to have BIP44 ensuring address + // while SyncWorker fetch'em on network + clearInterval(self.readinessInterval); + + if (!account.injectDefaultPlugins) { + sendReady(); + return resolve(true); + } + + sendReady(); + return resolve(true); + } + }, readinessIntervalTime); + } catch (e) { + reject(e); + } + }); +} + +module.exports = _initializeAccount; diff --git a/packages/wallet-lib/src/types/Account/_loadStrategy.js b/packages/wallet-lib/src/types/Account/_loadStrategy.js new file mode 100644 index 00000000000..8675ec1c60f --- /dev/null +++ b/packages/wallet-lib/src/types/Account/_loadStrategy.js @@ -0,0 +1,21 @@ +const _ = require('lodash'); +const { is } = require('../../utils'); +const { InvalidStrategy, UnknownStrategy } = require('../../errors'); +const buildInStrategies = require('../../utils/coinSelections/strategies'); + +const fromString = function fromString(strategyName) { + if (!_.has(buildInStrategies, strategyName)) return new UnknownStrategy(`Unknown strategy ${strategyName}`); + return buildInStrategies[strategyName]; +}; +const fromFunction = function fromFunction(arg) { + return arg; +}; + +/* eslint-disable no-underscore-dangle */ +const _loadStrategy = function _loadStrategy(arg) { + if (is.string(arg)) return fromString(arg); + if (is.fn(arg)) return fromFunction(arg); + throw new InvalidStrategy(arg); +}; + +module.exports = _loadStrategy; diff --git a/packages/wallet-lib/src/types/Account/_preparePlugins.js b/packages/wallet-lib/src/types/Account/_preparePlugins.js new file mode 100644 index 00000000000..470f9c8255a --- /dev/null +++ b/packages/wallet-lib/src/types/Account/_preparePlugins.js @@ -0,0 +1,38 @@ +const sortPlugins = require('./_sortPlugins'); +const logger = require('../../logger'); + +const preparePlugins = function preparePlugins(account, userUnsafePlugins) { + function reducer(accumulatorPromise, [plugin, allowSensitiveOperation, awaitOnInjection]) { + return accumulatorPromise + .then(async () => { + try { + await account.injectPlugin( + plugin, + allowSensitiveOperation, + awaitOnInjection, + ); + } catch (e) { + logger.error('Error injecting plugin', e); + this.emit('error', e, { + type: 'plugin', + pluginType: 'plugin', + pluginName: plugin.name, + }); + } + }); + } + + return new Promise((resolve, reject) => { + try { + const sortedPlugins = sortPlugins(account, userUnsafePlugins); + // It is important that all plugin got successfully injected in a sequential maneer + sortedPlugins.reduce(reducer, Promise.resolve()).then(() => resolve(sortedPlugins)); + + resolve(sortedPlugins); + } catch (e) { + reject(e); + } + }); +}; + +module.exports = preparePlugins; diff --git a/packages/wallet-lib/src/types/Account/_sortPlugins.js b/packages/wallet-lib/src/types/Account/_sortPlugins.js new file mode 100644 index 00000000000..ab7115a40df --- /dev/null +++ b/packages/wallet-lib/src/types/Account/_sortPlugins.js @@ -0,0 +1,140 @@ +const { each, findIndex } = require('lodash'); +const TransactionSyncStreamWorker = require('../../plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker'); +const ChainPlugin = require('../../plugins/Plugins/ChainPlugin'); +const IdentitySyncWorker = require('../../plugins/Workers/IdentitySyncWorker'); +const { WALLET_TYPES } = require('../../CONSTANTS'); + +const initPlugin = (UnsafePlugin) => { + const isInit = !(typeof UnsafePlugin === 'function'); + return (isInit) ? UnsafePlugin : new UnsafePlugin(); +}; + +/** + * Sort user defined plugins using the injectionOrder properties before or after when specified. + * + * Except if specified using before property, all system plugins (TxSyncStream, IdentitySync...) + * will be sorted on top. + * + * @param defaultSortedPlugins + * @param userUnsafePlugins + * @returns {*[]} + */ +const sortUserPlugins = (defaultSortedPlugins, userUnsafePlugins, allowSensitiveOperations) => { + const sortedPlugins = []; + const initializedSortedPlugins = []; + + // We start by ensuring all default plugins get loaded and initialized on top + defaultSortedPlugins.forEach((defaultPluginParams) => { + sortedPlugins.push(defaultPluginParams); + + // We also need to initialize them so we actually as we gonna need to read some properties. + const plugin = initPlugin(defaultPluginParams[0]); + initializedSortedPlugins.push(plugin); + }); + + // Iterate accross all user defined plugins + each(userUnsafePlugins, (UnsafePlugin) => { + const plugin = initPlugin(UnsafePlugin); + + const { + awaitOnInjection, + injectionOrder: { + before: injectBefore, + after: injectAfter, + }, + } = plugin; + + const hasAfterDependencies = !!(injectAfter && injectAfter.length); + const hasBeforeDependencies = !!(injectBefore && injectBefore.length); + const hasPluginDependencies = (hasAfterDependencies || hasBeforeDependencies); + + let injectionIndex = initializedSortedPlugins.length; + + if (hasPluginDependencies) { + let injectionBeforeIndex = -1; + let injectionAfterIndex = -1; + + if (hasBeforeDependencies) { + each(injectBefore, (pluginDependencyName) => { + const beforePluginIndex = findIndex(initializedSortedPlugins, ['name', pluginDependencyName]); + // TODO: we could have an handling that would postpone trying to insert the dependencies + // ensuring the case where we try to rely and sort based on user specified dependencies + // For now, require user to sort them when specifying the plugins. + if (beforePluginIndex === -1) throw new Error(`Dependency ${pluginDependencyName} not found`); + if (injectionBeforeIndex === -1 || injectionIndex > beforePluginIndex) { + injectionBeforeIndex = (injectionBeforeIndex === -1 || injectBefore > beforePluginIndex) + ? beforePluginIndex + : injectionBeforeIndex; + } + }); + } + + if (hasAfterDependencies) { + each(injectAfter, (pluginDependencyName) => { + const afterPluginIndex = findIndex(initializedSortedPlugins, ['name', pluginDependencyName]); + if (afterPluginIndex === -1) throw new Error(`Dependency ${pluginDependencyName} not found`); + if (injectionAfterIndex === -1 || injectionAfterIndex < afterPluginIndex + 1) { + injectionAfterIndex = afterPluginIndex + 1; + } + }); + } + + if ( + injectionBeforeIndex !== -1 + && injectionAfterIndex !== -1 + && injectionAfterIndex > injectionBeforeIndex + ) { + throw new Error(`Conflicting dependency order for ${plugin.name}`); + } + + if ( + injectionBeforeIndex !== -1 + || injectionAfterIndex !== -1 + ) { + injectionIndex = (injectionBeforeIndex !== -1) + ? injectionBeforeIndex + : injectionAfterIndex; + } + } + + // We insert both initialized and uninitialized plugins as we gonna need to read property. + initializedSortedPlugins.splice( + injectionIndex, + 0, + plugin, + ); + sortedPlugins.splice( + injectionIndex, + 0, + [UnsafePlugin, allowSensitiveOperations, awaitOnInjection], + ); + }); + initializedSortedPlugins.forEach((initializedSortedPlugin, i) => { + delete initializedSortedPlugins[i]; + }); + return sortedPlugins; +}; + +/** + * Sort plugins defined by users based on the before and after properties + * @param account + * @param userUnsafePlugins + * @returns {*[]} + */ +const sortPlugins = (account, userUnsafePlugins) => { + const plugins = []; + + // eslint-disable-next-line no-async-promise-executor + if (account.injectDefaultPlugins) { + if (!account.offlineMode) { + plugins.push([ChainPlugin, true, true]); + plugins.push([TransactionSyncStreamWorker, true, true]); + + if (account.walletType === WALLET_TYPES.HDWALLET) { + plugins.push([IdentitySyncWorker, true, true]); + } + } + } + return sortUserPlugins(plugins, userUnsafePlugins, account.allowSensitiveOperations); +}; +module.exports = sortPlugins; diff --git a/packages/wallet-lib/src/types/Account/_sortPlugins.spec.js b/packages/wallet-lib/src/types/Account/_sortPlugins.spec.js new file mode 100644 index 00000000000..bd75e148714 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/_sortPlugins.spec.js @@ -0,0 +1,310 @@ +const {expect} = require('chai'); +const {WALLET_TYPES} = require('../../CONSTANTS'); +const sortPlugins = require('./_sortPlugins'); + +const TransactionSyncStreamWorker = require('../../plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker'); +const ChainPlugin = require('../../plugins/Plugins/ChainPlugin'); +const IdentitySyncWorker = require('../../plugins/Workers/IdentitySyncWorker'); + +const Worker = require('./../../plugins/Worker'); + +class dummyWorker extends Worker { + constructor() { + super({ + name: 'dummyWorker', + }); + } +} + +class withoutPluginDependenciesWorker extends Worker { + constructor() { + super({ + name: 'withoutPluginDependenciesWorker', + }); + } +} + +const userDefinedWithoutPluginDependenciesPlugins = { + "dummyWorker": dummyWorker, + "withoutPluginDependenciesWorker": withoutPluginDependenciesWorker +}; + +class withSinglePluginDependenciesWorker extends Worker { + constructor() { + super({ + name: 'withSinglePluginDependenciesWorker', + injectionOrder: { + after: [ + 'IdentitySyncWorker' + ] + } + }); + } +} + +const userDefinedWithSinglePluginDependenciesPlugins1 = { + "dummyWorker": dummyWorker, + "withSinglePluginDependenciesWorker": withSinglePluginDependenciesWorker +}; + +class withSingleInjectBeforePluginDependenciesWorker extends Worker { + constructor() { + super({ + name: 'withSingleInjectBeforePluginDependenciesWorker', + injectionOrder: { + before: [ + 'IdentitySyncWorker' + ] + } + }); + } +} + +const userDefinedWithSingleInjectBeforePluginDependenciesPlugins1 = { + "dummyWorker": dummyWorker, + "withSingleInjectBeforePluginDependenciesWorker": withSingleInjectBeforePluginDependenciesWorker +}; + +class withSinglePluginAndSingleInjectBeforeDependenciesWorker extends Worker { + constructor() { + super({ + name: 'withSinglePluginAndSingleInjectBeforeDependenciesWorker', + injectionOrder: { + after: [ + 'ChainPlugin' + ], + before: [ + 'TransactionSyncStreamWorker' + ] + } + }); + } +} + +const userDefinedWithSinglePluginAndSingleInjectBeforeDependenciesWorker = { + "dummyWorker": dummyWorker, + "withSinglePluginAndSingleInjectBeforeDependenciesWorker": withSinglePluginAndSingleInjectBeforeDependenciesWorker +}; + +class withSinglePluginDependenciesWorker2 extends Worker { + constructor() { + super({ + name: 'withSinglePluginDependenciesWorker2', + injectionOrder: { + before: [ + 'TransactionSyncStreamWorker' + ] + } + }); + } +} + +const userDefinedWithSinglePluginDependenciesPlugins2 = { + "dummyWorker": dummyWorker, + "withSinglePluginDependenciesWorker2": withSinglePluginDependenciesWorker2 +}; + +const userDefinedWithMultiplePluginDependenciesPlugins = { + "dummyWorker": dummyWorker, + "withSinglePluginDependenciesWorker": withSinglePluginDependenciesWorker, + "withSinglePluginDependenciesWorker2": withSinglePluginDependenciesWorker2 +}; + + +class userDefinedConflictingDependenciesWorker extends Worker { + constructor() { + super({ + name: 'userDefinedConflictingDependenciesWorker', + injectionOrder: { + before: [ + 'ChainPlugin' + ], + after: [ + 'TransactionSyncStreamWorker', + ] + } + }); + } +} + + +const userDefinedConflictingDependencies = { + "dummyWorker": dummyWorker, + "userDefinedConflictingDependenciesWorker": userDefinedConflictingDependenciesWorker, + +} + +class pluginWithMultiplePluginDependencies extends Worker { + constructor() { + super({ + name: 'pluginWithMultiplePluginDependencies', + injectionOrder: { + before: [ + 'TransactionSyncStreamWorker', + 'withSinglePluginDependenciesWorker' + ] + } + }); + } +} + +const userDefinedSimpleDependencyPluginDependenciesPlugins = { + "dummyWorker": dummyWorker, + "withSinglePluginDependenciesWorker": withSinglePluginDependenciesWorker, + "pluginWithMultiplePluginDependencies": pluginWithMultiplePluginDependencies, +} +// Order is wrong here, which we also need to test +const userDefinedComplexPluginDependenciesPlugins = { + "dummyWorker": dummyWorker, + "pluginWithMultiplePluginDependencies": pluginWithMultiplePluginDependencies, + "withSinglePluginDependenciesWorker": withSinglePluginDependenciesWorker, + +} + + +const baseAccount = { + walletType: WALLET_TYPES.HDWALLET, + allowSensitiveOperations: false +} +const accountOnlineWithDefaultPlugins = { + ...baseAccount, + injectDefaultPlugins: true, +}; +const accountOnlineWithoutDefaultPlugins = { + ...baseAccount, + injectDefaultPlugins: false, +}; +const accountOfflineWithDefaultPlugins = { + ...baseAccount, + offlineMode: true, + injectDefaultPlugins: true, +}; +const accountOfflineWithoutDefaultPlugins = { + ...baseAccount, + offlineMode: true, + injectDefaultPlugins: false, +}; + + +describe('Account - _sortPlugins', () => { + describe('system plugins sorting', async function () { + it('should be able to correctly sort default plugins', async function () { + const sortedPluginsOnlineWithDefault = sortPlugins(accountOnlineWithDefaultPlugins); + + expect(sortedPluginsOnlineWithDefault).to.deep.equal([ + [ChainPlugin, true, true], + [TransactionSyncStreamWorker, true, true], + [IdentitySyncWorker, true, true], + ]) + + const sortedPluginsOnlineWithoutDefault = sortPlugins(accountOnlineWithoutDefaultPlugins); + expect(sortedPluginsOnlineWithoutDefault).to.deep.equal([]); + + const sortedPluginsOfflineWithDefault = sortPlugins(accountOfflineWithDefaultPlugins); + expect(sortedPluginsOfflineWithDefault).to.deep.equal([]) + + const sortedPluginsOfflineWithoutDefault = sortPlugins(accountOfflineWithoutDefaultPlugins); + expect(sortedPluginsOfflineWithoutDefault).to.deep.equal([]) + }); + }); + describe('user plugins sorting', async function () { + it('should handle userDefinedWithoutPluginDependenciesPlugins', async function () { + const sortedPlugins = sortPlugins(accountOnlineWithDefaultPlugins, userDefinedWithoutPluginDependenciesPlugins); + expect(sortedPlugins).to.deep.equal([ + [ChainPlugin, true, true], + [TransactionSyncStreamWorker, true,true], + [IdentitySyncWorker, true,true], + [dummyWorker, false, false], + [withoutPluginDependenciesWorker, false,false], + ]); + }); + it('should handle userDefinedWithSinglePluginDependenciesPlugins1', async function () { + const sortedPlugins = sortPlugins(accountOnlineWithDefaultPlugins, userDefinedWithSinglePluginDependenciesPlugins1); + expect(sortedPlugins).to.deep.equal([ + [ChainPlugin, true, true], + [TransactionSyncStreamWorker, true, true], + [IdentitySyncWorker, true, true], + [withSinglePluginDependenciesWorker, false, false], + [dummyWorker, false, false], + ]) + }); + it('should handle userDefinedWithSinglePluginDependenciesPlugins1', async function () { + const sortedPlugins = sortPlugins(accountOnlineWithDefaultPlugins, userDefinedWithSingleInjectBeforePluginDependenciesPlugins1); + expect(sortedPlugins).to.deep.equal([ + [ChainPlugin, true, true], + [TransactionSyncStreamWorker, true, true], + [withSingleInjectBeforePluginDependenciesWorker, false, false], + [IdentitySyncWorker, true, true], + [dummyWorker, false, false], + ]) + }); + + it('should handle userDefinedWithSinglePluginDependenciesPlugins2', async function () { + const sortedPlugins = sortPlugins(accountOnlineWithDefaultPlugins, userDefinedWithSinglePluginDependenciesPlugins2); + expect(sortedPlugins).to.deep.equal([ + [ChainPlugin, true, true], + [withSinglePluginDependenciesWorker2, false, false], + [TransactionSyncStreamWorker, true, true], + [IdentitySyncWorker, true, true], + [dummyWorker, false, false], + ]) + }); + + it('should handle withSinglePluginAndSingleInjectBeforeDependenciesWorker', function () { + const sortedPlugins = sortPlugins(accountOnlineWithDefaultPlugins, userDefinedWithSinglePluginAndSingleInjectBeforeDependenciesWorker); + expect(sortedPlugins).to.deep.equal([ + [ChainPlugin, true, true], + [withSinglePluginAndSingleInjectBeforeDependenciesWorker, false, false], + [TransactionSyncStreamWorker, true, true], + [IdentitySyncWorker, true, true], + [dummyWorker, false, false], + ]) + }); + + it('should handle userDefinedWithMultiplePluginDependenciesPlugins', async function () { + const sortedPlugins = sortPlugins(accountOnlineWithDefaultPlugins, userDefinedWithMultiplePluginDependenciesPlugins); + expect(sortedPlugins).to.deep.equal([ + [ChainPlugin, true, true], + [withSinglePluginDependenciesWorker2, false, false], + [TransactionSyncStreamWorker, true, true], + [IdentitySyncWorker, true, true], + [withSinglePluginDependenciesWorker, false, false], + [dummyWorker, false, false], + ]); + }); + it('should handle userDefinedConflictingDependencies', function () { + expect(() => sortPlugins(accountOnlineWithDefaultPlugins, userDefinedConflictingDependencies)) + .to + .throw('Conflicting dependency order for userDefinedConflictingDependenciesWorker'); + }); + it('should handle userDefinedSimpleDependencyPluginDependenciesPlugins', async function () { + const sortedPlugins = sortPlugins(accountOnlineWithDefaultPlugins, userDefinedSimpleDependencyPluginDependenciesPlugins); + + expect(sortedPlugins).to.deep.equal([ + [ChainPlugin, true, true], + [pluginWithMultiplePluginDependencies, false, false], + [TransactionSyncStreamWorker, true, true], + [IdentitySyncWorker, true, true], + [withSinglePluginDependenciesWorker, false, false], + [dummyWorker, false, false], + ]) + }); + + it('should handle userDefinedComplexPluginDependenciesPlugins', async function () { + // TODO: User specified wrongly sorted plugins with deps is not yet handled. + // rejecting with error for now. + expect(() => sortPlugins(accountOnlineWithDefaultPlugins, userDefinedComplexPluginDependenciesPlugins)) + .to + .throw('Dependency withSinglePluginDependenciesWorker not found'); + // const sortedPlugins = await sortPlugins(accountOnlineWithDefaultPlugins, userDefinedComplexPluginDependenciesPlugins); + // expect(sortedPlugins).to.deep.equal([ + // [ChainPlugin, true], + // [TransactionSyncStreamWorker, true], + // [IdentitySyncWorker, true], + // [dummyWorker, true], + // [withSinglePluginDependenciesWorker, true], + // [pluginWithMultiplePluginDependencies, true], + // ]) + }); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/addDefaultPaths.js b/packages/wallet-lib/src/types/Account/methods/addDefaultPaths.js new file mode 100644 index 00000000000..f9428ce0158 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/addDefaultPaths.js @@ -0,0 +1,13 @@ +/** + * Adds info about default derivation paths to the wallet and chain stores + */ +function addDefaultPaths() { + const defaultPaths = this.keyChainStore + .getMasterKeyChain() + .getIssuedPaths(); + + // Add default keychain paths to the account and chain store + this.addPathsToStore(defaultPaths, true); +} + +module.exports = addDefaultPaths; diff --git a/packages/wallet-lib/src/types/Account/methods/addPathsToStore.js b/packages/wallet-lib/src/types/Account/methods/addPathsToStore.js new file mode 100644 index 00000000000..d8bd5e5146c --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/addPathsToStore.js @@ -0,0 +1,20 @@ +/** + * Adds info about derivation paths to the wallet and chain stores + * @param paths - list of new derivation paths + * @param refreshUTXOState - a flag to trigger side effect in importAddress function + */ +function addPathsToStore(paths, refreshUTXOState = true) { + const accountStore = this.storage + .getWalletStore(this.walletId) + .getPathState(this.accountPath); + + const chainStore = this.storage.getChainStore(this.network); + + paths.forEach((path, i, self) => { + accountStore.addresses[path.path] = path.address.toString(); + const reconsiderTransactions = refreshUTXOState && i === self.length - 1; + chainStore.importAddress(path.address.toString(), reconsiderTransactions); + }); +} + +module.exports = addPathsToStore; diff --git a/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.js b/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.js new file mode 100644 index 00000000000..36ec401fcfd --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.js @@ -0,0 +1,175 @@ +const Dashcore = require('@dashevo/dashcore-lib'); +const { is } = require('../../../utils'); +const { + ValidTransportLayerRequired, + InvalidRawTransaction, + InvalidDashcoreTransaction, +} = require('../../../errors'); +const EVENTS = require('../../../EVENTS'); +const MempoolPropagationTimeoutError = require('../../../errors/MempoolPropagationTimeoutError'); +const logger = require('../../../logger'); + +const MEMPOOL_PROPAGATION_TIMEOUT = 360000; + +function impactAffectedInputs({ transaction }) { + const { + storage, network, + } = this; + + const { inputs, changeIndex } = transaction.toObject(); + const txid = transaction.hash; + + const addresses = storage.getChainStore(network).getAddresses(); + // We iterate out input to substract their balance. + inputs.forEach((input) => { + const potentiallySelectedAddresses = [...addresses] + .reduce((acc, [address, { transactions }]) => { + if (transactions.includes(input.prevTxId)) acc.push(address); + return acc; + }, []); + + potentiallySelectedAddresses.forEach((potentiallySelectedAddress) => { + // console.log(addresses.get(pot)); + const addressData = addresses.get(potentiallySelectedAddress); + if (addressData.utxos[`${input.prevTxId}-${input.outputIndex}`]) { + const inputUTXO = addressData.utxos[`${input.prevTxId}-${input.outputIndex}`]; + // const address = storage.store.wallets[walletId].addresses[type][path]; + // Todo: This modify the balance of an address, we need a std method to do that instead. + addressData.balanceSat -= inputUTXO.satoshis; + delete addressData.utxos[`${input.prevTxId}-${input.outputIndex}`]; + } + }); + }); + + const changeOutput = transaction.getChangeOutput(); + if (changeOutput) { + const addressString = changeOutput.script.toAddress(network).toString(); + + const address = addresses.get(addressString); + const utxoKey = `${txid}-${changeIndex}`; + + /** + * In some cases, `Storage#importTransaction` function gets called before the + * `impactAffectedInputs`and this utxo being written as a confirmed one. + * Skip creation of the unconfirmed UTXOs for such cases. + */ + if (!address.utxos[utxoKey]) { + address.utxos[utxoKey] = new Dashcore.Transaction.UnspentOutput( + { + txId: txid, + vout: changeIndex, + script: changeOutput.script, + satoshis: changeOutput.satoshis, + address: addressString, + }, + ); + address.unconfirmedBalanceSat = changeOutput.satoshis; + address.used = true; + } + } + + return true; +} + +// eslint-disable-next-line no-underscore-dangle +async function _broadcastTransaction(transaction, options = {}) { + const { network, storage } = this; + if (!this.transport) throw new ValidTransportLayerRequired('broadcast'); + + // We still support having in rawtransaction, if this is the case + // we first need to reform our object + if (is.string(transaction)) { + const rawtx = transaction.toString(); + if (!is.rawtx(rawtx)) throw new InvalidRawTransaction(rawtx); + return _broadcastTransaction.call(this, new Dashcore.Transaction(rawtx)); + } + + if (!is.dashcoreTransaction(transaction)) { + throw new InvalidDashcoreTransaction(transaction); + } + + if (!transaction.isFullySigned()) { + throw new Error('Transaction not signed.'); + } + + const { minRelay: minRelayFeeRate } = storage.getChainStore(network).state.fees; + + // eslint-disable-next-line no-underscore-dangle + const estimateKbSize = transaction._estimateSize() / 1000; + const minRelayFee = Math.ceil(estimateKbSize * minRelayFeeRate); + + if (minRelayFee > transaction.getFee() && !options.skipFeeValidation) { + throw new Error(`Expected minimum fee for transaction ${minRelayFee}. Current: ${transaction.getFee()}`); + } + const serializedTransaction = transaction.toString(); + + const txid = await this.transport.sendTransaction(serializedTransaction); + + // We now need to impact/update our affected inputs + // so we clear them out from UTXOset. + impactAffectedInputs.call(this, { + transaction, + }); + return txid; +} + +/** + * Broadcast a Transaction to the transport layer + * @param {Transaction|RawTransaction} transaction - A txobject or it's hexadecimal representation + * @param {Object} [options] + * @param {Boolean} [options.skipFeeValidation=false] - Allow to skip fee validation + * @param {Number} [options.mempoolPropagationTimeout=60000] - Time to wait for mempool propagation + * @return {Promise} + */ +async function broadcastTransaction(transaction, options = { + mempoolPropagationTimeout: MEMPOOL_PROPAGATION_TIMEOUT, +}) { + let rejectTimeout; + let cancelMempoolSubscription; + + const mempoolPropagationPromise = new Promise((resolve) => { + const listener = ({ payload }) => { + // TODO: consider reworking to use inputs/outputs comparison + // to ensure that TX malleability is not a problem + // https://dashcore.readme.io/v18.0.0/docs/core-guide-transactions-transaction-malleability + if (payload.transaction.hash === transaction.hash) { + logger.debug(`broadcastTransaction - received from mempool TX "${transaction.hash}"`); + clearTimeout(rejectTimeout); + resolve(); + } + }; + // TODO: change to FETCHED_UNCONFIRMED_TRANSACTION once this event is restored + this.once(EVENTS.FETCHED_CONFIRMED_TRANSACTION, listener); + cancelMempoolSubscription = () => { + logger.debug(`broadcastTransaction - canceled mempool subscription for TX "${transaction.hash}"`); + this.removeListener(EVENTS.FETCHED_CONFIRMED_TRANSACTION, listener); + }; + }); + + const rejectPromise = new Promise((_, reject) => { + rejectTimeout = setTimeout(() => { + reject(new MempoolPropagationTimeoutError(transaction.hash)); + }, options.mempoolPropagationTimeout); + }); + + logger.debug(`broadcastTransaction - subscribe to mempool for TX "${transaction.hash}"`); + const mempoolPropagationRace = Promise.race([ + mempoolPropagationPromise, rejectPromise, + ]); + + try { + await Promise.all([ + mempoolPropagationRace, + _broadcastTransaction.call(this, transaction, options).then((hash) => { + logger.debug(`broadcastTransaction - broadcasted TX "${hash}"`); + }), + ]); + } catch (error) { + cancelMempoolSubscription(); + throw error; + } + + return transaction.hash; +} + +module.exports = broadcastTransaction; diff --git a/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.spec.js b/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.spec.js new file mode 100644 index 00000000000..f3f6a2add0a --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.spec.js @@ -0,0 +1,136 @@ +const { expect } = require('chai'); +const EventEmitter = require('events'); +const Dashcore = require('@dashevo/dashcore-lib'); + +const broadcastTransaction = require('./broadcastTransaction'); +const EVENTS = require('../../../EVENTS'); +const validRawTxs = require('../../../../fixtures/rawtx').valid; +const invalidRawTxs = require('../../../../fixtures/rawtx').invalid; +const MempoolPropagationTimeoutError = require('../../../errors/MempoolPropagationTimeoutError'); +const ChainStore = require('../../ChainStore/ChainStore'); +const { PrivateKey } = Dashcore; + +describe('Account - broadcastTransaction', function suite() { + this.timeout(10000); + let utxos; + let address; + let keysToSign; + let oneToOneTx; + let fee; + + const chainStore = new ChainStore('testnet'); + chainStore.state.fees.minRelay = 888; + chainStore.importAddress('yTBXsrcGw74yMUsK34fBKAWJx3RNCq97Aq'); + const storage = { + getChainStore:()=> chainStore + } + + let self; + let sendCalled = 0; + + beforeEach(function () { + utxos = [ + { + address: 'yj8sq7ogzz6JtaxpBQm5Hg9YaB5cKExn5T', + txid: 'bfec828ed8ed562f53921e9580e847670044e870dda0e67b8f8d0c8d77962f7f', + vout: 1, + scriptPubKey: '76a914fa4b2bb85ad9b4075addb6d0eb50fa8b60c746c588ac', + amount: 138.7944 + } + ]; + fee = 680; + address = 'yTBXsrcGw74yMUsK34fBKAWJx3RNCq97Aq'; + keysToSign = [ + new PrivateKey('26d6b24119d1a71de6372ea2d3dc22a014d37e4828b43db6936cb41ea461cce8') + ]; + oneToOneTx = new Dashcore.Transaction() + .from(utxos) + .to(address, 138) + .fee(fee); + oneToOneTx.sign(keysToSign); + + sendCalled = 0; + self = new EventEmitter(); + self.removeListener = this.sinonSandbox.spy(); + self.transport = { + sendTransaction: (txHex) => { + const transaction = new Dashcore.Transaction(txHex) + self.emit(EVENTS.FETCHED_CONFIRMED_TRANSACTION, { + payload: { + transaction + } + }); + + sendCalled += 1; + return transaction.hash + }, + }; + self.network = 'testnet'; + self.storage = storage; + }); + + + it('should throw error on missing transport', async function () { + const expectedException1 = 'A transport layer is needed to perform a broadcast'; + self.transport = null; + + await expect(broadcastTransaction.call(self, validRawTxs.tx2to2Testnet)) + .to.be.rejectedWith(expectedException1); + + expect(self.removeListener).to.have.been.calledOnceWith(EVENTS.FETCHED_CONFIRMED_TRANSACTION); + }); + + it('should throw error on invalid rawtx (string)', async () => { + const expectedException1 = 'A valid transaction object or it\'s hex representation is required'; + + await expect(broadcastTransaction.call(self, invalidRawTxs.notRelatedString)) + .to.be.rejectedWith(expectedException1); + expect(self.removeListener).to.have.been.calledOnceWith(EVENTS.FETCHED_CONFIRMED_TRANSACTION); + }); + + it('should throw error on invalid rawtx (hex)', async () => { + const expectedException1 = 'A valid transaction object or it\'s hex representation is required'; + + await expect(broadcastTransaction.call(self, invalidRawTxs.truncatedRawTx)) + .to.be.rejectedWith(expectedException1); + expect(self.removeListener).to.have.been.calledOnceWith(EVENTS.FETCHED_CONFIRMED_TRANSACTION); + }); + + it('should work on valid Transaction object', async () => { + return broadcastTransaction + .call(self, oneToOneTx) + .then( + () => expect(sendCalled).to.equal(1) + ); + }); + + it('should throw error on fee not met', async function () { + const expectedException1 = 'Expected minimum fee for transaction 149. Current: 0'; + + oneToOneTx.fee(0); + + await expect(broadcastTransaction.call(self, oneToOneTx)) + .to.be.rejectedWith(expectedException1); + expect(self.removeListener).to.have.been.calledOnceWith(EVENTS.FETCHED_CONFIRMED_TRANSACTION); + }); + + it('should broadcast when force and fee not met', function () { + oneToOneTx.fee(0); + + return broadcastTransaction + .call(self, oneToOneTx, { skipFeeValidation: true }) + .then( + () => expect(sendCalled).to.equal(1) + ); + }); + + it('should throw mempool propagation timeout error', async function () { + self.transport.sendTransaction = () => new Promise(() => {}) + + await expect(broadcastTransaction.call(self, oneToOneTx, { + mempoolPropagationTimeout: 1 + })).to.be.rejectedWith(MempoolPropagationTimeoutError); + + expect(self.removeListener).to.have.been.calledOnceWith(EVENTS.FETCHED_CONFIRMED_TRANSACTION); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/connect.js b/packages/wallet-lib/src/types/Account/methods/connect.js new file mode 100644 index 00000000000..725a98d5a2a --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/connect.js @@ -0,0 +1,20 @@ +/** + * This method will connect to all streams and workers available + * @return {Boolean} + */ +module.exports = function connect() { + if (this.transport && this.transport.connect) { + this.transport.connect(); + } + + if (this.plugins.workers) { + const workersKey = Object.keys(this.plugins.workers); + workersKey.forEach((key) => { + this.plugins.workers[key].startWorker(); + }); + } + if (this.storage) { + this.storage.startWorker(); + } + return true; +}; diff --git a/packages/wallet-lib/src/types/Account/methods/connect.spec.js b/packages/wallet-lib/src/types/Account/methods/connect.spec.js new file mode 100644 index 00000000000..20200747c59 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/connect.spec.js @@ -0,0 +1,36 @@ +const { expect } = require('chai'); +const connect = require('./connect'); +const DummyWorker = require('../../../../fixtures/DummyWorker'); + +let transportConnected = false; +const emitted = []; + +describe('Account - connect', function suite() { + this.timeout(10000); + it('should connect to transport and worker', () => { + const self = { + emit: (eventName) => emitted.push(eventName), + transport: { + connect: () => { transportConnected = true; }, + }, + plugins: { + workers: { + dummyWorker: new DummyWorker(), + }, + }, + }; + + // We simulate what injectPlugin does regarding events + self.plugins.workers.dummyWorker.parentEvents = { on: self.on, emit: self.emit }; + + expect(connect.call(self)).to.equal(true); + expect(emitted).to.deep.equal([ + 'WORKER/DUMMYWORKER/STARTING', + 'WORKER/DUMMYWORKER/STARTED', + ]); + expect(transportConnected).to.deep.equal(true); + + // We need to stop the worker + self.plugins.workers.dummyWorker.stopWorker(); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/createPathsForTransactions.js b/packages/wallet-lib/src/types/Account/methods/createPathsForTransactions.js new file mode 100644 index 00000000000..22fb045aae8 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/createPathsForTransactions.js @@ -0,0 +1,38 @@ +const sortTransactions = require('../../../utils/sortTransactions'); + +/** + * Function goes through all transactions, and ensures address gap + * having in mind addresses already used by the account. + */ +function createPathsForTransactions() { + const chainStore = this.storage.getChainStore(this.network); + const transactions = [...chainStore.getTransactions().values()]; + + const sortedTransactions = sortTransactions(transactions); + + sortedTransactions.forEach((transaction, i, self) => { + // Update the state of UTXO for a given transaction + const { inputs, outputs } = transaction; + + const affectedAddresses = []; + [...inputs, ...outputs].forEach((element) => { + if (element.script) { + const address = element.script.toAddress(this.network).toString(); + if (chainStore.getAddress(address)) { + affectedAddresses.push(address); + } + } + }); + + // Generate new addresses in case the current set reached it's limit + // and add them to store + const paths = this.generateNewPaths(affectedAddresses); + + if (paths && paths.length) { + const refreshUTXOState = i === self.length - 1; + this.addPathsToStore(paths, refreshUTXOState); + } + }); +} + +module.exports = createPathsForTransactions; diff --git a/packages/wallet-lib/src/types/Account/methods/createTransaction.js b/packages/wallet-lib/src/types/Account/methods/createTransaction.js new file mode 100644 index 00000000000..71334ed4be5 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/createTransaction.js @@ -0,0 +1,131 @@ +/* eslint-disable no-underscore-dangle */ +const _ = require('lodash'); +const { + Transaction, PrivateKey, HDPrivateKey, crypto, Script, +} = require('@dashevo/dashcore-lib'); +const { CreateTransactionError } = require('../../../errors'); +const { dashToDuffs, coinSelection, is } = require('../../../utils'); +const _loadStrategy = require('../_loadStrategy'); + +const parseUtxos = (utxos) => { + // We do not allow mixmatch types (output, object together) utxo list + if (utxos[0] && utxos[0].constructor !== Transaction.UnspentOutput) { + return utxos.map((utxo) => new Transaction.UnspentOutput(utxo)); + } + return utxos; +}; +/** + * Create a transaction based around on the provided information + * @param {createTransactionOptions} opts - Options object + * @param opts.amount - Amount in dash that you want to send + * @param opts.satoshis - Amount in satoshis + * @param opts.recipient - Address of the recipient + * @param opts.recipients - Optional - replace individual satoshis/amount/recipient args + * @param opts.change - String - A valid Dash address - optional + * @param opts.utxos - Array - A utxo set - optional + * @param opts.isInstantSend - If you want to use IS or stdTx. + * @param opts.deductFee - Deduct fee + * @param opts.privateKeys - Overwrite default behavior : auto-searching local matching keys. + * @param opts.strategy - Overwrite default strategy + * @return {Transaction} - Transaction object + */ +function createTransaction(opts = {}) { + const tx = new Transaction(); + + let outputs = []; + + if (_.has(opts, 'recipients')) { + if (!is.arr(opts.recipients)) throw new Error('Expected recipients to be an array of recipient'); + _.each(opts.recipients, (recipient) => { + if (_.has(recipient, 'recipient') && _.has(recipient, 'satoshis')) { + outputs.push({ address: recipient.recipient, satoshis: recipient.satoshis }); + } else { + throw new Error(`Invalid recipient provided ${recipient}`); + } + }); + } else { + // FIXME : Remove amount support in next release. + if (!opts || (!opts.amount && !opts.satoshis)) { + throw new Error('An amount in dash or in satoshis is expected to create a transaction'); + } + const satoshis = (opts.amount && !opts.satoshis) ? dashToDuffs(opts.amount) : opts.satoshis; + if (!opts || !opts.recipient) { + throw new Error('A recipient is expected to create a transaction'); + } + outputs = [{ address: opts.recipient, satoshis }]; + } + + const deductFee = _.has(opts, 'deductFee') + ? opts.deductFee + : true; + + const strategy = _.has(opts, 'strategy') + ? _loadStrategy(opts.strategy) + : this.strategy; + + const utxosList = _.has(opts, 'utxos') ? parseUtxos(opts.utxos) : this.getUTXOS(); + + const feeCategory = (opts.isInstantSend) ? 'instant' : 'normal'; + let selection; + try { + selection = coinSelection(utxosList, outputs, deductFee, feeCategory, strategy); + } catch (e) { + throw new CreateTransactionError(e); + } + + const selectedUTXOs = selection.utxos; + + const selectedOutputs = selection.outputs; + const { + // feeCategory, + estimatedFee, + } = selection; + + tx.to(selectedOutputs); + tx.from(selectedUTXOs); + + // In case or excessive fund, we will get that to an address in our possession + // and determine the finalFees + // eslint-disable-next-line no-underscore-dangle + const preChangeSize = tx._estimateSize(); + const changeAddress = _.has(opts, 'change') ? opts.change : this.getUnusedAddress('internal').address; + tx.change(changeAddress); + // eslint-disable-next-line no-underscore-dangle + const deltaChangeSize = tx._estimateSize() - preChangeSize; + const finalFees = Math.ceil(estimatedFee + ((deltaChangeSize * estimatedFee) / preChangeSize)); + + tx.fee(finalFees); + const addressList = selectedUTXOs.map((el) => { + if (el.address) return el.address.toString(); + return Script + .fromHex(el.script) + .toAddress(this.getNetwork()) + .toString(); + }); + + const privateKeys = _.has(opts, 'privateKeys') + ? opts.privateKeys + : this.getPrivateKeys(addressList); + const transformedPrivateKeys = []; + privateKeys.forEach((pk) => { + if (pk.constructor.name === PrivateKey.name) { + transformedPrivateKeys.push(pk); + } else if (pk.constructor.name === HDPrivateKey.name) { + transformedPrivateKeys.push(pk.privateKey); + } else { + throw new Error(`Unexpected pk of type ${pk.constructor.name}`); + } + }); + try { + const signedTx = this.keyChainStore.getMasterKeyChain().sign( + tx, + transformedPrivateKeys, + crypto.Signature.SIGHASH_ALL, + ); + return signedTx; + } catch (e) { + throw new Error(`CreateTransaction failed with error ${e.message}`); + } +} + +module.exports = createTransaction; diff --git a/packages/wallet-lib/src/types/Account/methods/createTransaction.spec.js b/packages/wallet-lib/src/types/Account/methods/createTransaction.spec.js new file mode 100644 index 00000000000..4f2c796b65b --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/createTransaction.spec.js @@ -0,0 +1,157 @@ +const _ = require('lodash'); +const { expect } = require('chai'); +const { HDPrivateKey, Transaction } = require('@dashevo/dashcore-lib'); + +const createTransaction = require('./createTransaction'); +const FixtureTransport = require('../../../transport/FixtureTransport/FixtureTransport'); + +const getUTXOS = require('./getUTXOS'); +const getPrivateKeys = require('./getPrivateKeys'); +const getUnusedAddress = require('./getUnusedAddress'); + +const getFixtureHDAccountWithStorage = require('../../../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage'); + +const addressesFixtures = require('../../../../fixtures/addresses.json'); +const fixtureUTXOS = require('../../../transport/FixtureTransport/data/utxos/yQ1fb64aeLfgqFKyeV9Hg9KTaTq5ehHm22.json'); + +const craftedGenerousMinerStrategy = require('../../../../fixtures/strategies/craftedGenerousMinerStrategy'); + +describe('Account - createTransaction', function suite() { + this.timeout(10000); + let mockWallet; + + it('sould warn on missing inputs', function () { + const self = getFixtureHDAccountWithStorage() + self.getUTXOS = getUTXOS; + self.getUnusedAddress = getUnusedAddress; + self.getPrivateKeys = getPrivateKeys; + + const selfWithNoUTXOS = { ...self }; + selfWithNoUTXOS.getUTXOS = () => { return []}; + const mockOpts1 = {}; + const mockOpts2 = { + satoshis: 1000, + }; + const mockOpts3 = { + satoshis: 1000, + recipient: addressesFixtures.testnet.valid.yereyozxENB9jbhqpbg1coE5c39ExqLSaG.addr, + }; + const expectedException1 = 'An amount in dash or in satoshis is expected to create a transaction'; + const expectedException2 = 'A recipient is expected to create a transaction'; + const expectedException3 = 'Error: utxosList must contain at least 1 utxo'; + expect(() => createTransaction.call(self, mockOpts1)).to.throw(expectedException1); + expect(() => createTransaction.call(self, mockOpts2)).to.throw(expectedException2); + expect(() => createTransaction.call(selfWithNoUTXOS, mockOpts3)).to.throw(expectedException3); + }); + it('should create valid and deterministic transactions', async function () { + if(process.browser){ + // FixtureTransport relies heavily on fs.existSync and fs.readFile which are not available on browser + this.skip('FixtureTransport do not support browser environment due to FS intensive usage'); + return; + } + const transport = new FixtureTransport(); + transport.setHeight(21546); + + mockWallet = { + getUTXOS: () => fixtureUTXOS["21546"].map(utxo => Transaction.UnspentOutput(utxo)), + getUnusedAddress: () => { + return {"address": 'yMGXHsi8gstbd5wqfqkqcfsbwJjGBt5sWu'} + }, + getPrivateKeys: (addrList) => { + if (addrList.length === 1 && addrList[0] === 'yQ1fb64aeLfgqFKyeV9Hg9KTaTq5ehHm22') { + return [new HDPrivateKey('tprv8jG3ctd1DEVADnLP3hwS1Gfzjxf5E4WL2UutfJkhAQs7rVu2b3Ryv4WQ46mddZyMbGaSUYnY9wFeuFRAejapjoB1LGzTfM55mxMhZ1X4eGX')] + } + }, + keyChainStore: { + getMasterKeyChain:() => { + return { + sign: (tx, privateKeys) => tx.sign(privateKeys), + } + } + }, + storage: { + searchTransaction: (txId) => { + const tx = transport.getTransaction(txId); + if (tx) { + return {found: true, result: tx, hash: txId} + } else { + return {found: false, hash: txId} + } + } + } + }; + const expectedTx1 = '0300000001b64e23b6bd8c1016c8595ab6256e97ac5a33a95b5c68cc99410bf88867023910000000006a47304402200f8851bfcba02f1375c9d14cc1e4a1f442a6ba04dade5060124b6d245738eb1502206f2655f5e3714e9a1aa46de58124ec44d4da36884db0f1a39e6cad912ce009fc012103987110fc08c848657176385b37a77fb7f6d89bc873bb4334146ffe44ac126566ffffffff0250c30000000000001976a9140a6a961f1c664a9cd004c593381dd4d9f1f5463588acb9059a3b000000001976a9140a6a961f1c664a9cd004c593381dd4d9f1f5463588ac00000000'; + const expectedTx2 = '0300000001b64e23b6bd8c1016c8595ab6256e97ac5a33a95b5c68cc99410bf88867023910000000006b483045022100fc88e4585654961610e375b19f33b52d10e1c7efa5ef91531c627129538cf7ef0220108a281374a691522b5deb51ce3249723efe9541e57a4de87bdd8ba7ce43ce8e012103987110fc08c848657176385b37a77fb7f6d89bc873bb4334146ffe44ac126566ffffffff0b804a5d05000000001976a9140a6a961f1c664a9cd004c593381dd4d9f1f5463588ac804a5d05000000001976a91403ab1053a3bc741a012607893c66565c6815b9d888ac804a5d05000000001976a9146c773e3b74a16931f995288645f4f6379076048688ac804a5d05000000001976a914429dfc6b9a9d86463ea65b55d8cedb26a5e04f3388ac804a5d05000000001976a91434cb4bfb6e27ed0067e47c55da615bf7230e23f888ac804a5d05000000001976a914eb9a36fab9220e5e966fdcfe1abf2ee43308cb5d88ac804a5d05000000001976a9144c9f7ef1c5af5f0d2b219a035a46c7f54035b0a288ac804a5d05000000001976a9141c44d8966f001ddb7cea277edc33b02f151b603788ac804a5d05000000001976a914f4159f063a076038a484cf9d027808dbac118a1a88ac804a5d05000000001976a9147bc630538f5bb87d3166b6cf5f69853809235f4388acdcdef505000000001976a9140a6a961f1c664a9cd004c593381dd4d9f1f5463588ac00000000'; + + const tx1 = await createTransaction.call(mockWallet, { + recipient: 'yMGXHsi8gstbd5wqfqkqcfsbwJjGBt5sWu', + satoshis: 50000, + }); + expect(tx1.toString('hex')).to.deep.equal(expectedTx1); + + const tx2 = await createTransaction.call(mockWallet, { + recipients: [ + { + recipient: 'yMGXHsi8gstbd5wqfqkqcfsbwJjGBt5sWu', + satoshis: 90000000 + }, { + recipient: 'yLeqoVqqGf4hFDwsiJwKiLPpeJbZHJpwo7', + satoshis: 90000000 + }, { + recipient: 'yWCxg5NdRXDagFokjwdLMYNDqfEKmLPtua', + satoshis: 90000000 + }, { + recipient: 'ySPghvb9M1PqjhRYKv7iivQEuebM2aXs9f', + satoshis: 90000000 + }, { + recipient: 'yR8bXVFZAM1ysc8s4GfVTirNhTEzKizY19', + satoshis: 90000000 + }, { + recipient: 'yhoCPK6WyqtB5GmZjVqxy3faR5JMUKbt8x', + satoshis: 90000000 + }, { + recipient: 'yTJbGkT7TYVY4MYbTgdSDdq19A3VmjyEUo', + satoshis: 90000000 + }, { + recipient: 'yNtvF5g6qnbRsUJ8ggap3pd53HEmkngEJu', + satoshis: 90000000 + }, { + recipient: 'yia3dGyRdh7xZLDtum1rdCLRqabyBQbcWL', + satoshis: 90000000 + }, { + recipient: 'yXbuPCJagq4XH85hgxqsNv92kSUFroTWUA', + satoshis: 90000000 + }, + ] + }); + expect(tx2.toString('hex')).to.equal(expectedTx2); + }); + it('should be able to create transaction with specific strategy', async function () { + if(process.browser){ + // FixtureTransport relies heavily on fs.existSync and fs.readFile which are not available on browser + this.skip('FixtureTransport do not support browser environment due to FS intensive usage'); + return; + } + const expectedTxStd = '0300000001b64e23b6bd8c1016c8595ab6256e97ac5a33a95b5c68cc99410bf88867023910000000006a47304402200f8851bfcba02f1375c9d14cc1e4a1f442a6ba04dade5060124b6d245738eb1502206f2655f5e3714e9a1aa46de58124ec44d4da36884db0f1a39e6cad912ce009fc012103987110fc08c848657176385b37a77fb7f6d89bc873bb4334146ffe44ac126566ffffffff0250c30000000000001976a9140a6a961f1c664a9cd004c593381dd4d9f1f5463588acb9059a3b000000001976a9140a6a961f1c664a9cd004c593381dd4d9f1f5463588ac00000000'; + const expectedTxStrat = '0300000001b64e23b6bd8c1016c8595ab6256e97ac5a33a95b5c68cc99410bf88867023910000000006a4730440220171da851d2915f7faa20a7d7aa66383c93cca6b623d12cdb1919d913abe558aa0220154f7edac296e3e2cd393e46f18baf9f4463aaa0a6d2ce5259055280ed05d878012103987110fc08c848657176385b37a77fb7f6d89bc873bb4334146ffe44ac126566ffffffff0250c30000000000001976a9140a6a961f1c664a9cd004c593381dd4d9f1f5463588acad059a3b000000001976a9140a6a961f1c664a9cd004c593381dd4d9f1f5463588ac00000000'; + + const txStdStrategy = await createTransaction.call(mockWallet, { + recipient: 'yMGXHsi8gstbd5wqfqkqcfsbwJjGBt5sWu', + satoshis: 50000, + }); + expect(txStdStrategy.toString('hex')).to.deep.equal(expectedTxStd); + + mockWallet.strategy = craftedGenerousMinerStrategy + const txStrat1 = await createTransaction.call(mockWallet, { + recipient: 'yMGXHsi8gstbd5wqfqkqcfsbwJjGBt5sWu', + satoshis: 50000, + }); + expect(txStrat1.toString('hex')).to.deep.equal(expectedTxStrat); + const txStrat2 = await createTransaction.call(mockWallet, { + recipient: 'yMGXHsi8gstbd5wqfqkqcfsbwJjGBt5sWu', + satoshis: 50000, + strategy: craftedGenerousMinerStrategy + }); + expect(txStrat2.toString('hex')).to.deep.equal(expectedTxStrat); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/decode.js b/packages/wallet-lib/src/types/Account/methods/decode.js new file mode 100644 index 00000000000..bc0b692a54c --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/decode.js @@ -0,0 +1,16 @@ +const cbor = require('cbor'); + +/** + * Allow to decode an input + * Useful for encryption. + * @param {string} method + * @param {any} data + * @return {any} + */ +const decode = function decode(method, data) { + switch (method) { + default: + return cbor.decodeFirstSync(data); + } +}; +module.exports = decode; diff --git a/packages/wallet-lib/src/types/Account/methods/decode.spec.js b/packages/wallet-lib/src/types/Account/methods/decode.spec.js new file mode 100644 index 00000000000..50b23e10594 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/decode.spec.js @@ -0,0 +1,22 @@ +const { expect } = require('chai'); +const cbor = require('cbor'); +const decode = require('./decode'); + +describe('Account - decode', function suite() { + this.timeout(10000); + const jsonObject = { + string: 'string', + list: ['a', 'b', 'c', 'd'], + obj: { + int: 1, + boolean: true, + theNull: null, + }, + }; + const encodedJSON = cbor.encodeCanonical(jsonObject); + + it('should decode JSON with cbor', () => { + const decoded = decode('cbor', encodedJSON); + expect(decoded).to.deep.equal(jsonObject); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/decrypt.js b/packages/wallet-lib/src/types/Account/methods/decrypt.js new file mode 100644 index 00000000000..108ab58c589 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/decrypt.js @@ -0,0 +1,19 @@ +const CryptoJS = require('crypto-js'); +const { AES } = CryptoJS; + +/** + * @param {string} method + * @param {string} data + * @param {string} secret + * @param {'hex'|string} [encoding=CryptoJS.enc.Utf8] + * @return {string} + */ +const decrypt = function decrypt(method, data, secret, encoding = '') { + let decrypted; + switch (method) { + default: + decrypted = AES.decrypt(data, secret); + return (encoding === 'hex') ? decrypted.toString(CryptoJS.enc.Hex) : decrypted.toString(CryptoJS.enc.Utf8); + } +}; +module.exports = decrypt; diff --git a/packages/wallet-lib/src/types/Account/methods/decrypt.spec.js b/packages/wallet-lib/src/types/Account/methods/decrypt.spec.js new file mode 100644 index 00000000000..5f42bf099f3 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/decrypt.spec.js @@ -0,0 +1,31 @@ +const { expect } = require('chai'); +const cbor = require('cbor'); +const decrypt = require('./decrypt'); + +const jsonObject = { + string: 'string', + list: ['a', 'b', 'c', 'd'], + obj: { + int: 1, + boolean: true, + theNull: null, + }, +}; +const secret = 'secret'; + +const extPubKey = 'tpubDFFUrLDh4ihrmngy3Mb1fv6LS76tZPeYEvXBrqrMGoog7o1VAdYj8Nu8J8VZaMcRE3ypL59N51namngy8ek1kun2ZFPLKEmS5uBBfyvGcpN'; +const encryptedExtPubKey = 'U2FsdGVkX19YzY39phhQiw/mqBwAQKEYmXzw9PN6tQ8LtpQCIiGCfhYTUJEoXKQuaCug+ACuRe0C6iiFbe/7BfKNpvULK6lFFdaKjrfvNfWCCZKvBDXMVBX4u0uLWNcgcWEke/rMMAKex6Gt5UkdZd4BTv3pEiOay3YCDbtu9bY='; +const encryptedJSON = 'U2FsdGVkX19jvOuQ0Y7yJGiKDx/t1zoz3IDdlIS7uMyN7V5IUFvHMuD8D3QfoUcPOKBqTZcd8J2q3DRMC0h/5xa86ntm8gypxRCGd1IEAOFSZe9fWoW3qOW+JNOOekJGcEErFz28mffp/g0rThB14NwWDUivBNboCOZgABKJ0bS6OA/Lbcokl4+iDDCoRhkC'; + +describe('Account - decrypt', function suite() { + this.timeout(10000); + it('should decrypt extPubKey with aes', () => { + const decryptedExtPubKey = decrypt('aes', encryptedExtPubKey, secret); + expect(decryptedExtPubKey).to.equal(extPubKey); + }); + it('should decrypt encoded json', () => { + const decryptedJSON = decrypt('aes', encryptedJSON, secret); + const decodedJSON = cbor.decodeFirstSync(decryptedJSON); + expect(decodedJSON).to.deep.equal(jsonObject); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/disconnect.js b/packages/wallet-lib/src/types/Account/methods/disconnect.js new file mode 100644 index 00000000000..c60edef6af6 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/disconnect.js @@ -0,0 +1,33 @@ +/** + * This method will disconnect from all the opened streams, will stop all running workers + * and force a saving of the state. + * You want to use this method at the end of your life cycle of this lib. + * @return {Promise} + */ +module.exports = async function disconnect() { + this.isDisconnecting = true; + if (this.transport && this.transport.disconnect) { + await this.transport.disconnect(); + } + + if (this.plugins.workers) { + const workersKey = Object.keys(this.plugins.workers); + // eslint-disable-next-line no-restricted-syntax + for (const key of workersKey) { + // eslint-disable-next-line no-await-in-loop + await this.plugins.workers[key].stopWorker({ force: true }); + } + } + if (this.storage) { + await this.storage.saveState(); + await this.storage.stopWorker(); + } + if (this.removeAllListeners) this.removeAllListeners(); + if (this.storage.removeAllListeners) this.storage.removeAllListeners(); + if (this.readinessInterval) { + await clearInterval(this.readinessInterval); + await clearTimeout(this.readinessInterval); + delete this.readinessInterval; + } + return true; +}; diff --git a/packages/wallet-lib/src/types/Account/methods/disconnect.spec.js b/packages/wallet-lib/src/types/Account/methods/disconnect.spec.js new file mode 100644 index 00000000000..0e2036fb340 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/disconnect.spec.js @@ -0,0 +1,45 @@ +const { expect } = require('chai'); +const connect = require('./connect'); +const disconnect = require('./disconnect'); +const DummyWorker = require('../../../../fixtures/DummyWorker'); + +let transportConnected = false; +const emitted = []; + +describe('Account - disconnect', function suite() { + this.timeout(10000); + const self = { + emit: (eventName) => emitted.push(eventName), + removeAllListeners: () => null, + storage: { + removeAllListeners: () => null, + startWorker: () => null, + saveState: () => null, + stopWorker: () => null, + }, + transport: { + connect: () => { transportConnected = true; }, + disconnect: () => { transportConnected = false; }, + }, + plugins: { + workers: { + dummyWorker: new DummyWorker(), + }, + }, + }; + // We simulate what injectPlugin does regarding events + self.plugins.workers.dummyWorker.parentEvents = { on: self.on, emit: self.emit }; + connect.call(self); + it('should disconnect to stream and worker', async () => { + expect(transportConnected).to.equal(true); + await disconnect.call(self); + // console.log(self, transportConnected, emitted); + expect(transportConnected).to.equal(false); + expect(emitted).to.deep.equal([ + 'WORKER/DUMMYWORKER/STARTING', + 'WORKER/DUMMYWORKER/STARTED', + 'WORKER/DUMMYWORKER/EXECUTED', + 'WORKER/DUMMYWORKER/STOPPED', + ]); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/encode.js b/packages/wallet-lib/src/types/Account/methods/encode.js new file mode 100644 index 00000000000..e1991f84bcc --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/encode.js @@ -0,0 +1,15 @@ +const cbor = require('cbor'); + +/** + * Allow to canonical encode an input + * Useful for encryption. + * @param method + * @param data + */ +const encode = function encode(method, data) { + switch (method) { + default: + return cbor.encodeCanonical(data); + } +}; +module.exports = encode; diff --git a/packages/wallet-lib/src/types/Account/methods/encode.spec.js b/packages/wallet-lib/src/types/Account/methods/encode.spec.js new file mode 100644 index 00000000000..e8f2c2fc660 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/encode.spec.js @@ -0,0 +1,22 @@ +const { expect } = require('chai'); +const cbor = require('cbor'); +const encode = require('./encode'); + +describe('Account - encode', function suite() { + this.timeout(10000); + const jsonObject = { + string: 'string', + list: ['a', 'b', 'c', 'd'], + obj: { + int: 1, + boolean: true, + theNull: null, + }, + }; + + it('should encode JSON with cbor', () => { + const encodedJSON = encode('cbor', jsonObject); + const decoded = cbor.decodeFirstSync(encodedJSON); + expect(decoded).to.deep.equal(jsonObject); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/encrypt.js b/packages/wallet-lib/src/types/Account/methods/encrypt.js new file mode 100644 index 00000000000..b66e66c2874 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/encrypt.js @@ -0,0 +1,10 @@ +const AES = require('crypto-js/aes'); + +const encrypt = function encrypt(method, data, secret) { + const str = typeof data === 'string' ? data : data.toString(); + switch (method) { + default: + return AES.encrypt(str, secret).toString(); + } +}; +module.exports = encrypt; diff --git a/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js b/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js new file mode 100644 index 00000000000..facc317ea96 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js @@ -0,0 +1,51 @@ +const { expect } = require('chai'); +const CryptoJS = require('crypto-js'); +const { Wallet } = require('../../../index'); + +const derivationPath = "m/44'/1'/0'/0"; + +describe('Account - encrypt', function suite() { + this.timeout(10000); + let wallet; + let account; + beforeEach(async () => { + wallet = new Wallet({ offlineMode: true }); + account = await wallet.getAccount({ index: 0 }); + }); + + afterEach(() => { + wallet.disconnect(); + }); + const jsonObject = { + string: 'string', + list: ['a', 'b', 'c', 'd'], + obj: { + int: 1, + boolean: true, + theNull: null, + }, + }; + + const secret = 'secret'; + + it('should encrypt extPubKey with aes', () => { + const extPubKey = account.keyChainStore.getMasterKeyChain().getForPath(derivationPath).key.toString(); + const encryptedExtPubKey = account.encrypt('aes', extPubKey, secret).toString(); + const bytes = CryptoJS.AES.decrypt(encryptedExtPubKey, secret); + const decrypted = bytes.toString(CryptoJS.enc.Utf8); + expect(decrypted).to.equal(extPubKey); + }); + it('should encrypt a encoded json', () => { + const encodedJSON = account.encode('cbor', jsonObject).toString('hex'); + const encryptedJSON = account.encrypt('aes', encodedJSON, secret); + + const decryptedEncodedJSON = CryptoJS + .AES + .decrypt(encryptedJSON, secret) + .toString(CryptoJS.enc.Utf8); + + const decodedJSON = account.decode('cbor', decryptedEncodedJSON); + expect(encodedJSON).to.equal(decryptedEncodedJSON); + expect(decodedJSON).to.deep.equal(jsonObject); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/fetchStatus.js b/packages/wallet-lib/src/types/Account/methods/fetchStatus.js new file mode 100644 index 00000000000..c7727b9f795 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/fetchStatus.js @@ -0,0 +1,14 @@ +const { ValidTransportLayerRequired } = require('../../../errors'); + +/** + * @return {Promise} status + */ +async function fetchStatus() { + if (!this.transport) { + throw new ValidTransportLayerRequired('fetchStatus'); + } + + return this.transport.getStatus(); +} + +module.exports = fetchStatus; diff --git a/packages/wallet-lib/src/types/Account/methods/fetchStatus.spec.js b/packages/wallet-lib/src/types/Account/methods/fetchStatus.spec.js new file mode 100644 index 00000000000..6c9d2de1b5a --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/fetchStatus.spec.js @@ -0,0 +1,6 @@ +const { expect } = require('chai'); +const Dashcore = require('@dashevo/dashcore-lib'); + + +describe('Account - fetchStatus', () => { +}); diff --git a/packages/wallet-lib/src/types/Account/methods/forceRefreshAccount.js b/packages/wallet-lib/src/types/Account/methods/forceRefreshAccount.js new file mode 100644 index 00000000000..d7b231fd5cf --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/forceRefreshAccount.js @@ -0,0 +1,16 @@ +/** + * Force a refresh of all the addresses informations (utxo, balance, txs...) + * todo : Use a taskQueue where this would just emit the ask for a refresh. + * @return {Boolean} + */ +function forceRefreshAccount() { + const store = this.storage.getStore(); + const addressStore = store.wallets[this.walletId].addresses; + ['internal', 'external', 'misc'].forEach((type) => { + Object.keys(addressStore[type]).forEach((path) => { + addressStore[type][path].fetchedLast = 0; + }); + }); + return true; +} +module.exports = forceRefreshAccount; diff --git a/packages/wallet-lib/src/types/Account/methods/forceRefreshAccount.spec.js b/packages/wallet-lib/src/types/Account/methods/forceRefreshAccount.spec.js new file mode 100644 index 00000000000..4c942fe9d36 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/forceRefreshAccount.spec.js @@ -0,0 +1,6 @@ +const { expect } = require('chai'); +const Dashcore = require('@dashevo/dashcore-lib'); + + +describe('Account - forceRefreshAccount', () => { +}); diff --git a/packages/wallet-lib/src/types/Account/methods/generateAddress.js b/packages/wallet-lib/src/types/Account/methods/generateAddress.js new file mode 100644 index 00000000000..6d19109c108 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/generateAddress.js @@ -0,0 +1,94 @@ +const EVENTS = require('../../../EVENTS'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); +const { is } = require('../../../utils'); + +/** + * Generate an address from a path and import it to the store + * @param {string} path + * @param {boolean} [isWatchedAddress=true] - if the address will be watched + * @return {AddressInfo} Address information + * */ +function generateAddress(path, isWatchedAddress = true) { + if (is.undefOrNull(path)) throw new Error('Expected path to generate an address'); + let index = 0; + let address; + let keyPathData; + const { network } = this; + + switch (this.walletType) { + case WALLET_TYPES.ADDRESS: + address = this.keyChainStore.getMasterKeyChain().rootKey; + if (isWatchedAddress) { + this.keyChainStore.issuedPaths.set(0, { + path: 0, + address, + isUsed: false, + isWatched: true, + }); + } + break; + case WALLET_TYPES.PUBLICKEY: + // eslint-disable-next-line no-case-declarations + const { rootKey } = this.keyChainStore.getMasterKeyChain(); + address = rootKey.toAddress(network).toString(); + if (isWatchedAddress) { + this.keyChainStore.issuedPaths.set(0, { + key: rootKey, + path: 0, + address, + isUsed: false, + isWatched: true, + }); + } + break; + case WALLET_TYPES.HDPRIVATE: + case WALLET_TYPES.HDWALLET: + // eslint-disable-next-line prefer-destructuring + index = parseInt(path.toString().split('/')[2], 10); + keyPathData = this.keyChainStore + .getMasterKeyChain() + .getForPath(path, { isWatched: isWatchedAddress }); + address = keyPathData.address.toString(); + break; + case WALLET_TYPES.HDPUBLIC: + index = parseInt(path.toString().split('/')[5], 10); + // eslint-disable-next-line no-case-declarations + keyPathData = this.keyChainStore + .getMasterKeyChain() + .getForPath(path, { isWatched: isWatchedAddress }); + address = keyPathData.address.toString(); + break; + // TODO: DEPRECATE USAGE OF SINGLE_ADDRESS in favor or PRIVATEKEY + case WALLET_TYPES.PRIVATEKEY: + case WALLET_TYPES.SINGLE_ADDRESS: + default: + keyPathData = this.keyChainStore + .getMasterKeyChain() + .getForPath(path, { isWatched: isWatchedAddress }); + address = keyPathData.address.toString(); + break; + } + + const addressData = { + path: path.toString(), + index, + address, + transactions: [], + utxos: {}, + balanceSat: 0, + unconfirmedBalanceSat: 0, + }; + + const accountStore = this.storage + .getWalletStore(this.walletId) + .getPathState(this.accountPath); + + const chainStore = this.storage.getChainStore(this.network); + + accountStore.addresses[addressData.path] = addressData.address.toString(); + chainStore.importAddress(addressData.address.toString()); + this.emit(EVENTS.GENERATED_ADDRESS, { type: EVENTS.GENERATED_ADDRESS, payload: addressData }); + return addressData; +} + +module.exports = generateAddress; diff --git a/packages/wallet-lib/src/types/Account/methods/generateAddress.spec.js b/packages/wallet-lib/src/types/Account/methods/generateAddress.spec.js new file mode 100644 index 00000000000..79c73c2598e --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/generateAddress.spec.js @@ -0,0 +1,6 @@ +const { expect } = require('chai'); +const Dashcore = require('@dashevo/dashcore-lib'); + + +describe('Account - generateAddress', () => { +}); diff --git a/packages/wallet-lib/src/types/Account/methods/generateNewPaths.js b/packages/wallet-lib/src/types/Account/methods/generateNewPaths.js new file mode 100644 index 00000000000..b43ad7362f6 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/generateNewPaths.js @@ -0,0 +1,21 @@ +/** + * Marks addresses as used and generates new ones if needed + * @param {string[]} addresses + */ +function generateNewPaths(addresses) { + let issuedPaths = []; + const keyChains = this.keyChainStore.getKeyChains(); + + addresses.forEach((address) => { + keyChains.forEach((keyChain) => { + const keyChainIssuedPaths = keyChain.markAddressAsUsed(address); + if (keyChainIssuedPaths.length > 0) { + issuedPaths = issuedPaths.concat(keyChainIssuedPaths); + } + }); + }); + + return issuedPaths; +} + +module.exports = generateNewPaths; diff --git a/packages/wallet-lib/src/types/Account/methods/getAddress.js b/packages/wallet-lib/src/types/Account/methods/getAddress.js new file mode 100644 index 00000000000..86efad07386 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getAddress.js @@ -0,0 +1,26 @@ +const { WALLET_TYPES } = require('../../../CONSTANTS'); + +/** + * Get a specific addresss based on the index and type of address. + * @param {number} index - The index on the type + * @param {AddressType} [addressType="external"] - Type of the address (external, internal, misc) + * @return + */ +function getAddress(addressIndex = 0, addressType = 'external') { + const addressTypeIndex = (addressType === 'external') ? 0 : 1; + + const { addresses } = this.storage.getWalletStore(this.walletId).getPathState(this.accountPath); + const addressPath = ([WALLET_TYPES.HDPUBLIC, WALLET_TYPES.HDWALLET].includes(this.walletType)) + ? `m/${addressTypeIndex}/${addressIndex}` : '0'; + + const address = addresses[addressPath]; + if (!address) return this.generateAddress(addressPath); + + const chainStore = this.storage.getChainStore(this.network); + return { + index: addressIndex, + path: addressPath, + ...chainStore.getAddress(address), + }; +} +module.exports = getAddress; diff --git a/packages/wallet-lib/src/types/Account/methods/getAddress.spec.js b/packages/wallet-lib/src/types/Account/methods/getAddress.spec.js new file mode 100644 index 00000000000..e002c622f26 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getAddress.spec.js @@ -0,0 +1,6 @@ +const { expect } = require('chai'); +const Dashcore = require('@dashevo/dashcore-lib'); + + +describe('Account - getAddress', () => { +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getAddresses.js b/packages/wallet-lib/src/types/Account/methods/getAddresses.js new file mode 100644 index 00000000000..6d957f63d61 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getAddresses.js @@ -0,0 +1,37 @@ +const { WALLET_TYPES } = require('../../../CONSTANTS'); + +/** + * Get all the addresses from the store from a given type + * @param {AddressType} [addressType="external"] - Type of the address (external, internal, misc) + * @return {[AddressObj]} address - All address matching the type + */ +function getAddresses(addressType = 'external') { + const addressTypeIndex = (addressType === 'external') ? 0 : 1; + + const { addresses } = this.storage + .getWalletStore(this.walletId) + .getPathState(this.accountPath); + + const chainStore = this.storage.getChainStore(this.network); + + const baseAddressPath = ([WALLET_TYPES.HDPUBLIC, WALLET_TYPES.HDWALLET].includes(this.walletType)) + ? `m/${addressTypeIndex}` : '0'; + + const typedAddresses = {}; + + Object + .entries(addresses) + .forEach(([path, address]) => { + if (path.startsWith(baseAddressPath)) { + const index = parseInt(path.split('/').slice(-1)[0], 10); + typedAddresses[path] = { + index, + path, + ...chainStore.getAddress(address), + }; + } + }); + + return typedAddresses; +} +module.exports = getAddresses; diff --git a/packages/wallet-lib/src/types/Account/methods/getAddresses.spec.js b/packages/wallet-lib/src/types/Account/methods/getAddresses.spec.js new file mode 100644 index 00000000000..a3512b5ca7c --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getAddresses.spec.js @@ -0,0 +1,6 @@ +const { expect } = require('chai'); +const Dashcore = require('@dashevo/dashcore-lib'); + + +describe('Account - getAddresses', () => { +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getBlockHeader.js b/packages/wallet-lib/src/types/Account/methods/getBlockHeader.js new file mode 100644 index 00000000000..6d3cfc11db0 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getBlockHeader.js @@ -0,0 +1,23 @@ +const { is } = require('../../../utils'); + +/** + * Get a getBlockHeader from a provided block hash or block height + * @param {string|number} identifier - Block Hash or blockHeight + * @return {Promise} + */ +async function getBlockHeader(identifier) { + const search = await this.storage.searchBlockHeader(identifier); + if (search.found) { + return search.result; + } + const blockHeight = (is.num(identifier)) ? identifier : null; + const blockHeader = (is.num(identifier)) + ? await this.transport.getBlockByHeight(blockHeight) + : await this.transport.getBlockHeaderByHash(identifier); + + if (this.cacheBlockHeaders) { + await this.storage.importBlockHeader(blockHeader, blockHeight); + } + return blockHeader; +} +module.exports = getBlockHeader; diff --git a/packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.js b/packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.js new file mode 100644 index 00000000000..158ca50f02e --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.js @@ -0,0 +1,20 @@ +const { duffsToDash, calculateDuffBalance } = require('../../../utils'); + +/** + * Return the confirmed balance of an account. + * @param {boolean} [displayDuffs=true] - Set the returned format : Duff/dash. + * @return {number} Balance in dash + */ +function getConfirmedBalance(displayDuffs = true) { + const { + walletId, storage, accountPath, network, + } = this; + + const { addresses } = storage.getWalletStore(walletId).getPathState(accountPath); + + const chainStore = storage.getChainStore(network); + const totalSat = (calculateDuffBalance(Object.values(addresses), chainStore, 'confirmed')); + return (displayDuffs) ? totalSat : duffsToDash(totalSat); +} + +module.exports = getConfirmedBalance; diff --git a/packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.spec.js b/packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.spec.js new file mode 100644 index 00000000000..0c0c0e0d294 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.spec.js @@ -0,0 +1,36 @@ +const { expect } = require('chai'); +const getTotalBalance = require('./getTotalBalance'); +const getConfirmedBalance = require('./getConfirmedBalance'); +const getUnconfirmedBalance = require('./getUnconfirmedBalance'); +const getFixtureHDAccountWithStorage = require("../../../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage"); + + +let mockedAccount; +describe('Account - getTotalBalance', function suite() { + this.timeout(10000); + before(() => { + mockedAccount = getFixtureHDAccountWithStorage(); + }); + + it('should correctly get the balance', () => { + const balance = getTotalBalance.call(mockedAccount); + expect(balance).to.equal(667198249); + }); + + it('should correctly get the balance confirmed only', () => { + const balance = getConfirmedBalance.call(mockedAccount); + expect(balance).to.equal(667198249); + }); + + // TODO: file looks like a complete duplicate of the getTotalBalance.spec.js + // Should we actually mock and test confirmed balance? + it('should correctly get the balance dash value instead of duff', () => { + const balanceTotalDash = getTotalBalance.call(mockedAccount, false); + const balanceUnconfDash = getUnconfirmedBalance.call(mockedAccount, false); + const balanceConfDash = getConfirmedBalance.call(mockedAccount, false); + + expect(balanceTotalDash).to.equal(6.67198249); + expect(balanceUnconfDash).to.equal(0); + expect(balanceConfDash).to.equal(6.67198249); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getPlugin.js b/packages/wallet-lib/src/types/Account/methods/getPlugin.js new file mode 100644 index 00000000000..001e3467fff --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getPlugin.js @@ -0,0 +1,17 @@ +const { + UnknownPlugin, +} = require('../../../errors'); +/** + * Get a plugin by name + * @param {string} pluginName + * @return {*} + */ +function getPlugin(pluginName) { + const loweredPluginName = pluginName.toLowerCase(); + const stdPluginsList = Object.keys(this.plugins.standard).map((key) => key.toLowerCase()); + if (stdPluginsList.includes(loweredPluginName)) { + return this.plugins.standard[loweredPluginName]; + } + throw new UnknownPlugin(loweredPluginName); +} +module.exports = getPlugin; diff --git a/packages/wallet-lib/src/types/Account/methods/getPlugin.spec.js b/packages/wallet-lib/src/types/Account/methods/getPlugin.spec.js new file mode 100644 index 00000000000..1b77525575e --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getPlugin.spec.js @@ -0,0 +1,6 @@ +const { expect } = require('chai'); +const Dashcore = require('@dashevo/dashcore-lib'); + + +describe('Account - getPlugin', () => { +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getPrivateKeys.js b/packages/wallet-lib/src/types/Account/methods/getPrivateKeys.js new file mode 100644 index 00000000000..f7a068f8e17 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getPrivateKeys.js @@ -0,0 +1,26 @@ +/** + * Return all the private keys matching the PubKey Addr List + * @param {[string]} addressList + * @return {Array} + */ +function getPrivateKeys(addressList) { + let addresses = []; + const privKeys = []; + if (addressList.constructor.name === Object.name) { + addresses = [addressList]; + } else { addresses = addressList; } + + const { keyChainStore } = this; + + const keyChain = keyChainStore.getMasterKeyChain(); + + addresses.forEach((address) => { + const addressData = keyChain.getForAddress(address); + if (addressData) { + privKeys.push(addressData.key); + } + }); + + return privKeys; +} +module.exports = getPrivateKeys; diff --git a/packages/wallet-lib/src/types/Account/methods/getPrivateKeys.spec.js b/packages/wallet-lib/src/types/Account/methods/getPrivateKeys.spec.js new file mode 100644 index 00000000000..2143aab6f90 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getPrivateKeys.spec.js @@ -0,0 +1,6 @@ +const { expect } = require('chai'); +const Dashcore = require('@dashevo/dashcore-lib'); + + +describe('Account - getPrivateKeys', () => { +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getTotalBalance.js b/packages/wallet-lib/src/types/Account/methods/getTotalBalance.js new file mode 100644 index 00000000000..9c0161549d6 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getTotalBalance.js @@ -0,0 +1,21 @@ +const { duffsToDash, calculateDuffBalance } = require('../../../utils'); + +/** + * Return the total balance of an account (confirmed + unconfirmed). + * @param displayDuffs {boolean} True by default. Set the returned format : Duff/dash. + * @return {number} Balance in dash + */ +function getTotalBalance(displayDuffs = true) { + const { + walletId, storage, accountPath, network, + } = this; + + const { addresses } = storage.getWalletStore(walletId).getPathState(accountPath); + + const chainStore = storage.getChainStore(network); + + const totalSat = (calculateDuffBalance(Object.values(addresses), chainStore, 'total')); + return (displayDuffs) ? totalSat : duffsToDash(totalSat); +} + +module.exports = getTotalBalance; diff --git a/packages/wallet-lib/src/types/Account/methods/getTotalBalance.spec.js b/packages/wallet-lib/src/types/Account/methods/getTotalBalance.spec.js new file mode 100644 index 00000000000..443e93a2fa9 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getTotalBalance.spec.js @@ -0,0 +1,30 @@ +const { expect } = require('chai'); +const getTotalBalance = require('./getTotalBalance'); +const getConfirmedBalance = require('./getConfirmedBalance'); +const getUnconfirmedBalance = require('./getUnconfirmedBalance'); +const getFixtureHDAccountWithStorage = require("../../../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage"); + +let mockedAccount; +describe('Account - getTotalBalance', function suite() { + this.timeout(10000); + before(() => { + mockedAccount = getFixtureHDAccountWithStorage(); + }); + it('should correctly get the balance',() => { + const balance = getTotalBalance.call(mockedAccount); + expect(balance).to.equal(667198249); + }); + it('should correctly get the balance confirmed only', () => { + const balance = getConfirmedBalance.call(mockedAccount); + expect(balance).to.equal(667198249); + }); + it('should correctly get the balance dash value instead of duff', () => { + const balanceTotalDash = getTotalBalance.call(mockedAccount, false); + const balanceUnconfDash = getUnconfirmedBalance.call(mockedAccount, false); + const balanceConfDash = getConfirmedBalance.call(mockedAccount, false); + + expect(balanceTotalDash).to.equal(6.67198249); + expect(balanceUnconfDash).to.equal(0); + expect(balanceConfDash).to.equal(6.67198249); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getTransaction.js b/packages/wallet-lib/src/types/Account/methods/getTransaction.js new file mode 100644 index 00000000000..da7a097052d --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getTransaction.js @@ -0,0 +1,59 @@ +const EVENTS = require('../../../EVENTS'); + +/** + * Get a transaction from a provided txid + * @param {transactionId} txid - Transaction Hash + * @return {Promise<{metadata: TransactionMetaData|null, transaction: Transaction}>} + */ +async function getTransaction(txid = null) { + const { storage, network } = this; + const chainStore = storage.getChainStore(network); + const searchedTransaction = chainStore.getTransaction(txid); + + if (searchedTransaction) { + return searchedTransaction; + } + + const getTransactionResponse = await this.transport.getTransaction(txid); + if (!getTransactionResponse) return null; + const { + transaction, + blockHash, + height, + instantLocked, + chainLocked, + } = getTransactionResponse; + + const metadata = { + blockHash, + height, + instantLocked, + chainLocked, + }; + if (this.cacheTx) { + // We cache even if transaction / metadata are not final (case of unconfirmed tx) + await this.importTransactions([[transaction, metadata]]); + + if (height) { + if (this.cacheBlockHeaders) { + const searchBlockHeader = this.storage.searchBlockHeader(height); + if (!searchBlockHeader.found) { + // Trigger caching of blockheader + await this.getBlockHeader(height); + } + } + } else { + const self = this; + // If not yet confirmed, recall at next block. + this.once( + EVENTS.BLOCKHEIGHT_CHANGED, + () => { + self.getTransaction(txid); + }, + ); + } + } + return { transaction, metadata }; +} + +module.exports = getTransaction; diff --git a/packages/wallet-lib/src/types/Account/methods/getTransaction.spec.js b/packages/wallet-lib/src/types/Account/methods/getTransaction.spec.js new file mode 100644 index 00000000000..06f1c1d0d92 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getTransaction.spec.js @@ -0,0 +1,106 @@ +const { expect } = require('chai'); +const getTransaction = require('./getTransaction'); +const getFixtureHDAccountWithStorage = require("../../../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage"); + +let mockedAccount; +let fetchTransactionInfoCalledNb = 0; +describe('Account - getTransaction', function suite() { + this.timeout(10000); + before(() => { + mockedAccount = getFixtureHDAccountWithStorage(); + + mockedAccount.transport = { + getTransaction: () => { + fetchTransactionInfoCalledNb += 1; + return null + }, + } + }); + it('should correctly get a existing transaction', async () => { + const tx = await getTransaction.call(mockedAccount, 'a43845e580ad01f31bc06ce47ab39674e40316c4c6b765b6e54d6d35777ef456'); + + expect(tx.transaction.toObject()).to.deep.equal(expectedTx); + + expect(tx.metadata).to.deep.equal({ + "blockHash": "000001deee9f99e8219a9abcaaea135dbaae8a9b0f1ea214e6b6a37a5c5b115d", + "height": 555506, + "isInstantLocked": true, + "isChainLocked": true + }); + }); + + it('should correctly try to fetch un unexisting transaction', async () => { + expect(fetchTransactionInfoCalledNb).to.equal(0); + const tx = await getTransaction.call(mockedAccount, '92151f239013c961db15bc91d904404d2ae0520929969b59b69b17493569d0d5'); + expect(fetchTransactionInfoCalledNb).to.equal(1); + expect(tx).to.equal(null); + }); +}); + +const expectedTx = { + "hash": "a43845e580ad01f31bc06ce47ab39674e40316c4c6b765b6e54d6d35777ef456", + "version": 2, + "inputs": [ + { + "prevTxId": "11802a0d6221636a93023f73750946ace488a79d3074ba93abb4edc19bf91efd", + "outputIndex": 0, + "sequenceNumber": 4294967294, + "script": "483045022100dfb220a840d597179abdf49692ad64c1c0da785041975b00aee03c9625639cf202204d06eade5cca19fab1e10b1d6e1b67c77626a0e88bb4d5f61bd57293b4b64217012102295ecb812ccf52deaf304bebfe3a59a644f05bac81241ea1e3a2f8750064cbf6", + "scriptString": "72 0x3045022100dfb220a840d597179abdf49692ad64c1c0da785041975b00aee03c9625639cf202204d06eade5cca19fab1e10b1d6e1b67c77626a0e88bb4d5f61bd57293b4b6421701 33 0x02295ecb812ccf52deaf304bebfe3a59a644f05bac81241ea1e3a2f8750064cbf6" + }, + { + "prevTxId": "19953851c7a425045d3b6b4f56b7d5116fc1648444e5c37eba29ea65ee264269", + "outputIndex": 0, + "sequenceNumber": 4294967294, + "script": "483045022100beff3263b7c99720e99af9ec146c818701efb0130603f1570f427b74aef8521802202e660bb9f7ea156f91addd5fe47cbd2c2bf388cc6e1eff3a39adffd89d26d346012102c33942799f7cbf4a7d12f1b3e52cb80cc4de083b997d3e63915df9973d5bce2a", + "scriptString": "72 0x3045022100beff3263b7c99720e99af9ec146c818701efb0130603f1570f427b74aef8521802202e660bb9f7ea156f91addd5fe47cbd2c2bf388cc6e1eff3a39adffd89d26d34601 33 0x02c33942799f7cbf4a7d12f1b3e52cb80cc4de083b997d3e63915df9973d5bce2a" + }, + { + "prevTxId": "2dc8e2adfb30902269fa77dbf0de94f1f04ab3e8b1dbe1dd074a39a864993e96", + "outputIndex": 0, + "sequenceNumber": 4294967294, + "script": "483045022100ff67776932e7a32520aa131f76bdfd6737650ad3b11edbdf466cca83f691b0e60220633bcbedebacffd53ceb7e9cdbd47928d7c2849f49ac1f8efb9f384c1a4ee46301210371c0bc42e08de059a8829730abb16f3d40cff87e5ad85d65c4a0a949d9c4b524", + "scriptString": "72 0x3045022100ff67776932e7a32520aa131f76bdfd6737650ad3b11edbdf466cca83f691b0e60220633bcbedebacffd53ceb7e9cdbd47928d7c2849f49ac1f8efb9f384c1a4ee46301 33 0x0371c0bc42e08de059a8829730abb16f3d40cff87e5ad85d65c4a0a949d9c4b524" + }, + { + "prevTxId": "40cf2327c923487ce9789c58a1273ddd9bb87a8d30975dc298335c125065e11f", + "outputIndex": 0, + "sequenceNumber": 4294967294, + "script": "483045022100cca348c7ab16fac28b3bba502be54a9e3766b7da9821a90605f370b75840569702207d082510aa493988e09da046355b018781718208f8a954e14ea33d608ae59625012103699b9402e109ed9d0c67c6a45be5cf5f1236c44bb9fc4b07a2f3392ba0b64172", + "scriptString": "72 0x3045022100cca348c7ab16fac28b3bba502be54a9e3766b7da9821a90605f370b75840569702207d082510aa493988e09da046355b018781718208f8a954e14ea33d608ae5962501 33 0x03699b9402e109ed9d0c67c6a45be5cf5f1236c44bb9fc4b07a2f3392ba0b64172" + }, + { + "prevTxId": "4bb38b9207953d4658c64e6ad986eab05a42e50c72bd0f3bf07d7dd8b31f25ce", + "outputIndex": 0, + "sequenceNumber": 4294967294, + "script": "47304402203ae564ff74b08b1f96bf857f51448434418d747a02039ec1ee109a4f5d8e8106022072f8769bd175416d22f44011f7e67aec301f08573c9937be7e4a09c394c7396601210311bae874933a4503a61d1c8c2e5b57b1a278d28d4892af4bd79ab8a731495265", + "scriptString": "71 0x304402203ae564ff74b08b1f96bf857f51448434418d747a02039ec1ee109a4f5d8e8106022072f8769bd175416d22f44011f7e67aec301f08573c9937be7e4a09c394c7396601 33 0x0311bae874933a4503a61d1c8c2e5b57b1a278d28d4892af4bd79ab8a731495265" + }, + { + "prevTxId": "b21e8513b29a43b3169b857c466cc626859d76e374fc5dc7771f4a0df8fe2daf", + "outputIndex": 0, + "sequenceNumber": 4294967294, + "script": "47304402200b49b7059064efb57df453dc2d20002f09b5266bc825760ef81624771f13920802200782616b8c4fb7b5eff94fdf865e6ddc4530d3932b97fdc3a747e8c451f0314c012103a94131f28f8efd67f47f2496ff6e8d9069a3a7df97202a33e90e16f257d03729", + "scriptString": "71 0x304402200b49b7059064efb57df453dc2d20002f09b5266bc825760ef81624771f13920802200782616b8c4fb7b5eff94fdf865e6ddc4530d3932b97fdc3a747e8c451f0314c01 33 0x03a94131f28f8efd67f47f2496ff6e8d9069a3a7df97202a33e90e16f257d03729" + }, + { + "prevTxId": "d6fd2b6ea7d186a2211076188594cacb61df415051876fa198ca4c2205ef4f34", + "outputIndex": 0, + "sequenceNumber": 4294967294, + "script": "47304402202a24d1123775641269c6f748d3e4dad08a682e4e334a9b73c7df84f6c22e8e7d022022c0cc2225d3f14cb58a6fb3e4bf23c0f33252d9040a9ca9ef66eb17742a476f01210347301de4c9ba7f46b0f27cb82ae70a73749821e2951d3c87c2f0d56648635d1c", + "scriptString": "71 0x304402202a24d1123775641269c6f748d3e4dad08a682e4e334a9b73c7df84f6c22e8e7d022022c0cc2225d3f14cb58a6fb3e4bf23c0f33252d9040a9ca9ef66eb17742a476f01 33 0x0347301de4c9ba7f46b0f27cb82ae70a73749821e2951d3c87c2f0d56648635d1c" + } + ], + "outputs": [ + { + "satoshis": 1823313, + "script": "76a91440ca54360086cc0fbd69d862db58ab2b6d22805888ac" + }, + { + "satoshis": 187980000, + "script": "76a914538da44e7136cc994023d89a7b4b3d02ac0e573988ac" + } + ], + "nLockTime": 555505 +} + diff --git a/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.js b/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.js new file mode 100644 index 00000000000..71044afa597 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.js @@ -0,0 +1,89 @@ +const { each } = require('lodash'); +const { + categorizeTransactions, + // calculateTransactionFees, +} = require('../../../utils'); + +const sortbyTimeDescending = (a, b) => (b.time - a.time); +const sortByHeightDescending = (a, b) => (b.height - a.height); + +/** + * Get all the transaction history already formated + * @return {TransactionsHistory} + */ +function getTransactionHistory() { + const transactionHistory = []; + + const { + walletId, + walletType, + index: accountIndex, + storage, + network, + } = this; + + const transactions = this.getTransactions(); + + const walletStore = storage.getWalletStore(walletId); + const chainStore = storage.getChainStore(network); + + const transactionsWithMetadata = Object.keys(transactions).reduce((acc, hash) => { + const { metadata } = chainStore.getTransaction(hash); + acc.push([transactions[hash], metadata]); + return acc; + }, []); + + const { blockHeaders } = chainStore.state; + + const categorizedTransactions = categorizeTransactions( + transactionsWithMetadata, + walletStore, + accountIndex, + walletType, + network, + ); + const sortedCategorizedTransactions = categorizedTransactions.sort(sortByHeightDescending); + + each(sortedCategorizedTransactions, (categorizedTransaction) => { + const { + transaction, + from, + to, + type, + isChainLocked, + isInstantLocked, + satoshisBalanceImpact, + feeImpact, + } = categorizedTransaction; + const blockHash = categorizedTransaction.blockHash !== '' + ? categorizedTransaction.blockHash + : null; + // To get time of block, let's find the blockheader. + const blockHeader = blockHeaders.get(blockHash); + // If it's unconfirmed, we won't have a blockHeader nor it's time. + const time = blockHeader ? new Date(blockHeader.time * 1e3) : new Date(9999999999 * 1e3); + + const normalizedTransactionHistory = { + // Would require knowing the vout of this vin to determinate inputAmount. + // This information could be fetched, but the necessity vs the cost is questionable. + // fees: calculateTransactionFees(categorizedTransaction.transaction), + from, + to, + type, + time, + txId: transaction.hash, + blockHash, + isChainLocked, + isInstantLocked, + satoshisBalanceImpact, + feeImpact, + }; + + transactionHistory.push(normalizedTransactionHistory); + }); + + // Sort by decreasing time. + return transactionHistory.sort(sortbyTimeDescending); +} + +module.exports = getTransactionHistory; diff --git a/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.spec.js b/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.spec.js new file mode 100644 index 00000000000..7caae444622 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.spec.js @@ -0,0 +1,580 @@ +const {expect} = require('chai'); +const getTransactions = require('./getTransactions'); +const getTransactionHistory = require('./getTransactionHistory'); +const getTotalBalance = require('./getTotalBalance'); + +const getFixtureHDAccountWithStorage = require('../../../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage'); +const getFixturePrivateAccountWithStorage = require('../../../../fixtures/wallets/2a331817b9d6bf85100ef0/getFixtureAccountWithStorage'); + + +const mockedHDAccount = getFixtureHDAccountWithStorage(); +mockedHDAccount.getTransactions = getTransactions; + +const mockedPKAccount = getFixturePrivateAccountWithStorage(); +mockedPKAccount.getTransactions = getTransactions; + +describe('Account - getTransactionHistory', () => { + it('should return empty array on no transaction history', async function () { + const mockedHDSelf = { + ...getFixtureHDAccountWithStorage() + } + mockedHDSelf.getTransactions = getTransactions; + const chainStore = mockedHDSelf.storage.getChainStore('testnet') + chainStore.state.blockHeaders = new Map(); + chainStore.state.transactions = new Map(); + chainStore.state.addresses.forEach((address)=>{ + address.transactions = []; + address.utxos = {}; + address.balanceSat = 0; + }) + const transactionHistoryHD = await getTransactionHistory.call(mockedHDSelf); + const balanceImpact = transactionHistoryHD.reduce((acc, item) => { + return acc + item.satoshisBalanceImpact - item.feeImpact + },0); + + const balance = getTotalBalance.call(mockedHDSelf); + expect(balance).to.equal(balanceImpact) + + const expectedTransactionHistoryHD = []; + expect(transactionHistoryHD).to.deep.equal(expectedTransactionHistoryHD); + }); + + it('should return valid transaction for HDWallet', async function () { + const mockedHDSelf = { + ...mockedHDAccount + } + const timestartTs = +new Date(); + const transactionHistoryHD = await getTransactionHistory.call(mockedHDSelf); + const timeendTs = +new Date(); + const calculationTime = timeendTs - timestartTs; + + const balance = getTotalBalance.call(mockedHDSelf); + + const balanceImpact = transactionHistoryHD.reduce((acc, item) => { + return acc + item.satoshisBalanceImpact - item.feeImpact + },0); + + expect(balance).to.equal(balanceImpact) + + expect(calculationTime).to.be.below(60 * 1000); + + const expectedTransactionHistoryHD = [ + { + from: [ + { + address: 'yirJaK8KCE5YAmwvLadizqFw3TCXqBuZXL', + addressType: 'internal' + }, + { + address: 'yiXh4Yo5djG6QH8WzXkKm5EFzqLRJWakXz', + addressType: 'external' + } + ], + to: [ + { + address: 'yMX3ycrLVF2k6YxWQbMoYgs39aeTfY4wrB', + satoshis: 1000000000, + addressType: 'unknown' + }, + { + address: 'yhdRfg5gNr587dtEC4YYMcSHmLVEGqqtHc', + satoshis: 159999359, + addressType: 'internal' + } + ], + type: 'sent', + time: new Date(1629237076*1e3), + txId: 'e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841', + blockHash: '000001f9c5de4d2b258a975bfbf7b9a3346890af6389512bea3cb6926b9be330', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact:-1000000000, + feeImpact: 394 + }, + { + from: [{address: 'yNCqctyQaq51WU1hN5aNwsgMsZ5fRiB7GY', addressType: 'otherAccount'}], + to: [ + { + address: 'yiXh4Yo5djG6QH8WzXkKm5EFzqLRJWakXz', + satoshis: 1150000000, + addressType: 'external' + }, + { + address: 'yh6Hcyipdvp6WJpQxjNbaXP4kzPQUJpY3n', + satoshis: 49999753, + addressType: 'otherAccount' + } + ], + type: 'account_transfer', + time: new Date(1629236158*1e3), + txId: '6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135', + blockHash: '000000444b3f2f02085f8befe72da5442c865c290658766cf935e1a71a4f4ba7', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: 1150000000, + feeImpact: 0 + }, + { + from: [{ + address: 'yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv', + addressType: 'external' + }], + to: [ + { + address: 'yYJmzWey5kNecAThet5BFxAga1F4b4DKQ2', + satoshis: 1260000000, + addressType: 'otherAccount' + }, + { + address: 'yirJaK8KCE5YAmwvLadizqFw3TCXqBuZXL', + satoshis: 9999753, + addressType: 'internal' + } + ], + type: 'account_transfer', + time: new Date(1629234873*1e3), + txId: '6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533', + blockHash: '000000dffb05c071a8c05082a475b7ce9c1e403f3b89895a6c448fe08535a5f5', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: -1260000000, + feeImpact: 247 + }, + { + from: [{ + address: 'yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv', + addressType: 'external' + }], + to: [ + { + address: 'yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv', + satoshis: 1270000000, + addressType: 'external' + }, + { + address: 'yhaAB6e8m3F8zmGX7WAVYa6eEfmSrrnY8x', + satoshis: 400000000, + addressType: 'external' + }, + { + address: 'yLk4Hw3w4zDudrDVP6W8J9TggkY57zQUki', + satoshis: 107099720, + addressType: 'internal' + } + ], + type: 'address_transfer', + time: new Date(1629234474*1e3), + txId: 'c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5', + blockHash: '000001953ea0bbb8ad04a9a1a2a707fef207ad22a712d7d3c619f0f9b63fa98c', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: 0, + feeImpact: 280 + }, + + { + from: [ + { + address: 'ygHAVkMtYSqoTWHebDv7qkhMV6dHyuRsp2', + addressType: 'external' + }, + { + address: 'ygk3GCSba2J3L9G665Snozhj9HSkh5ByVE', + addressType: 'external' + }, + { + address: 'yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7', + addressType: 'external' + }, + { + address: 'yercyhdN9oEkZcB9BsW5ktFaDxFEuK6qXN', + addressType: 'external' + }, + { + address: 'yMLhEsiP2ajSh8STmXnNmkWXtoHsmawZxd', + addressType: 'external' + } + ], + to: [ + { + address: 'yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv', + satoshis: 1777100000, + addressType: 'external' + }, + { + address: 'yNDpPsJqXKM36zHSNEW7c1zSvNnrZ699FY', + satoshis: 99170, + addressType: 'internal' + } + ], + type: 'address_transfer', + time: new Date(1629216608*1e3), + txId: 'f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8', + blockHash: '00000084b4d9e887a6ad3f37c576a17d79c35ec9301e55210eded519e8cdcd3a', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: 0, + feeImpact: 830 + }, + { + from: [{ + address: 'yP8A3cbdxRtLRduy5mXDsBnJtMzHWs6ZXr', + addressType: "unknown" + }], + to: [ + { + address: 'yY16qMW4TSiYGWUyANYWMSwgwGe36KUQsR', + satoshis: 46810176, + addressType: "unknown" + }, + { + address: 'ygHAVkMtYSqoTWHebDv7qkhMV6dHyuRsp2', + satoshis: 729210000, + addressType: "external" + } + ], + type: 'received', + time: new Date(1629207543*1e3), + txId: '1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b', + blockHash: '00000c1e4556add15119392ed36ec6af2640569409abfa23a9972bc3be1b3717', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: 729210000, + feeImpact: 0 + }, + { + from: [{ + address: 'yXxUiAnB31voBDPqnwxkffcPnUvwJz6a2k', + addressType: "unknown" + }, + { + address: 'yNh6Xzw4rs1kenAo8VWCswdyUnkdYXDZsg', + addressType: "unknown" + }], + to: [ + { + "address": "yXiTNo71QQAqiw2u1i6vkEEj3m6y4sEGae", + "satoshis": 1768694, + addressType: "unknown" + }, + { + "address": "yMLhEsiP2ajSh8STmXnNmkWXtoHsmawZxd", + "satoshis": 840010000, + addressType: "external" + } + ], + time: new Date(1629126597*1e3), + txId: "eb1a7fc8e3b43d3021653b1176f8f9b41e9667d05b65ee225d14c149a5b14f77", + blockHash: "00000221952c2a60adcb929de837f659308cb5c6bb7783016479381fb550fbad", + type: "received", + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: 840010000, + feeImpact: 0 + }, + { + from: [{ + address: 'yTcjWB7v7opDzpfYKpFdFEtEvSKFsh3bW3', + addressType: "unknown" + }], + to: [ + { + "address": "ygk3GCSba2J3L9G665Snozhj9HSkh5ByVE", + "satoshis": 10000000, + addressType: "external" + }, + { + "address": "yiDVYtUZ2mKV4teSJzKBArqY4BRsZoFLYs", + "satoshis": 522649259, + addressType: "unknown" + } + ], + time: new Date(1628846998*1e3), + txId: "7d1b78157f9f2238669f260d95af03aeefc99577ff0cddb91b3e518ee557a2fd", + blockHash: "0000012cf6377c6cf2b317a4deed46573c09f04f6880dca731cc9ccea6691e19", + type: "received", + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: 10000000, + feeImpact: 0 + }, + { + from: [{ + address: 'yaLhoAZ4iex2zKmfvS9rvEmxXmRiPrjHdD', + addressType: "unknown" + }], + to: [ + { + "address": "yercyhdN9oEkZcB9BsW5ktFaDxFEuK6qXN", + "satoshis": 10000000, + addressType: "external" + }, + { + "address": "yTcjWB7v7opDzpfYKpFdFEtEvSKFsh3bW3", + "satoshis": 532649506, + addressType: "unknown" + } + ], + type: 'received', + time: new Date(1628846768*1e3), + txId: 'd37b6c7dd449d605bea9997af8bbeed2f3fbbcb23a4068b1f1ad694db801912d', + blockHash: '000000b6006c758eda23ec7e2a640a0bf2c6a0c44827be216faff6bf4fd388e8', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: 10000000, + feeImpact: 0 + }, + { + from: [ + { + address: 'ygrRyPRf9vSHnP1ieoRRvY9THtFbTMc66e', + addressType: "unknown" + }, + { + address: 'yhDaDMNRUAB93S2ZcprNLuEGHPG4VT8kYL', + addressType: "unknown" + }, + { + address: 'ygZ5fgrtGQDtwsN8K7sftSNPXN4Srhz99s', + addressType: "unknown" + }, + { + address: 'yb39TanhfUKeqaBtzqDvAE3ad9UsDuj3Fd', + addressType: "unknown" + }, + { + address: 'yToX9gDE6tn2Sv1zhq88WNfJSomeHee3rR', + addressType: "unknown" + }, + { + address: 'yViAv63brJ5kB7Gyc7yX2c7rJ9NuykCzRh', + addressType: "unknown" + }, + { + address: 'yfnJMvdE32izNQP68PhMPiHAeJKYo2PBdH', + addressType: "unknown" + }, + ], + to: [ + { + "address": "ySE2UYPf7PWMJ5oYikSscVifzQEoGiGRmd", + "satoshis": 1823313, + addressType: "unknown" + }, + { + "address": "yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7", + "satoshis": 187980000, + addressType: "external" + } + ], + type: 'received', + time: new Date(1628846677*1e3), + txId: 'a43845e580ad01f31bc06ce47ab39674e40316c4c6b765b6e54d6d35777ef456', + blockHash: '000001deee9f99e8219a9abcaaea135dbaae8a9b0f1ea214e6b6a37a5c5b115d', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: 187980000, + feeImpact: 0 + } + ] + expect(transactionHistoryHD).to.deep.equal(expectedTransactionHistoryHD); + }); + it('should correctly deal with multiple HDWallet accounts', async function () { + const mockedHDSelf = { + ...mockedHDAccount + } + mockedHDSelf.index = 1; + mockedHDSelf.accountPath = `m/44'/1'/1'`; + const transactionHistoryHD = await getTransactionHistory.call(mockedHDSelf); + + const balance = getTotalBalance.call(mockedHDSelf); + + const balanceImpact = transactionHistoryHD.reduce((acc, item) => { + return acc + item.satoshisBalanceImpact - item.feeImpact + },0); + + expect(balance).to.equal(balanceImpact) + + const expectedTransactionHistoryHD = [ + { + from: [ + { + address: 'yYJmzWey5kNecAThet5BFxAga1F4b4DKQ2', + addressType: 'external', + }, + ], + to: [ + { + address: 'yNCqctyQaq51WU1hN5aNwsgMsZ5fRiB7GY', + satoshis: 1200000000, + addressType: 'external', + }, + { + address: 'yXMrw79LPgu78EJsfGGYpm6fXKc1EMnQ49', + satoshis: 59999753, + addressType: 'internal', + }, + ], + type: 'address_transfer', + time: new Date(9999999999*1e3), + txId: '9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840', + blockHash: '0000016fb685b4b1efed743d2263de34a9f8323ed75e732654b1b951c5cb4dde', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: 0, + feeImpact: 247 + }, + { + from: [ { address: 'yNCqctyQaq51WU1hN5aNwsgMsZ5fRiB7GY', addressType: 'external' } ], + to: [ + { + address: 'yiXh4Yo5djG6QH8WzXkKm5EFzqLRJWakXz', + satoshis: 1150000000, + addressType: 'otherAccount' + }, + { + address: 'yh6Hcyipdvp6WJpQxjNbaXP4kzPQUJpY3n', + satoshis: 49999753, + addressType: 'internal' + } + ], + type: 'account_transfer', + time: new Date(1629236158*1e3), + txId: '6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135', + blockHash: '000000444b3f2f02085f8befe72da5442c865c290658766cf935e1a71a4f4ba7', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: -1150000000, + feeImpact: 247 + }, + { + from: [ { address: 'yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv', addressType: 'otherAccount' } ], + to: [ + { + address: 'yYJmzWey5kNecAThet5BFxAga1F4b4DKQ2', + satoshis: 1260000000, + addressType: 'external' + }, + { + address: 'yirJaK8KCE5YAmwvLadizqFw3TCXqBuZXL', + satoshis: 9999753, + addressType: 'otherAccount' + } + ], + type: 'account_transfer', + time: new Date(1629234873*1e3), + txId: '6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533', + blockHash: '000000dffb05c071a8c05082a475b7ce9c1e403f3b89895a6c448fe08535a5f5', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: 1260000000, + feeImpact: 0 + } + ] + expect(transactionHistoryHD).to.deep.equal(expectedTransactionHistoryHD); + }); + it('should correctly compute transaction history for single address based wallet', async function (){ + const mockedPKSelf = { + ...mockedPKAccount + } + + const transactionHistoryPK = await getTransactionHistory.call(mockedPKSelf); + + const balanceImpact = transactionHistoryPK.reduce((acc, item) => { + return acc + item.satoshisBalanceImpact - item.feeImpact + },0); + + const balance = getTotalBalance.call(mockedPKSelf); + expect(balance).to.equal(balanceImpact) + + const expectedTransactionHistoryPK = [ + { + from: [ { + address: 'ycDeuTfs4U77bTb5cq17dame28zdWHVYfk', + addressType: 'external' + } ], + to: [ + { + address: 'yP8A3cbdxRtLRduy5mXDsBnJtMzHWs6ZXr', + satoshis: 450000, + addressType: 'unknown' + }, + { + address: 'ycDeuTfs4U77bTb5cq17dame28zdWHVYfk', + satoshis: 8999753, + addressType: 'external' + } + ], + type: 'sent', + time: new Date(1629510092*1e3), + txId: '47d13f7f713f4258953292c2298c1d91e2d6dee309d689f3c8b44ccf457bab52', + blockHash: '0000007b7356e715b43ed7d5b7135fb9a2bf403e079bbcf7faec0f0da5c40117', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: -450000, + feeImpact: 247 + }, + { + from: [ { + address: 'ycDeuTfs4U77bTb5cq17dame28zdWHVYfk', + addressType: 'external' + } ], + to: [ + { + address: 'ycDeuTfs4U77bTb5cq17dame28zdWHVYfk', + addressType: 'external', + satoshis: 9450000 + }, + { + address: 'ycDeuTfs4U77bTb5cq17dame28zdWHVYfk', + addressType: 'external', + satoshis: 699999753 + } + ], + type: 'address_transfer', + time: new Date(1629509216*1e3), + txId: 'd48f415f08fb795d43b216cf56e9ef10e059d4009cfc8fc90edfc0d3850813af', + blockHash: '0000018b88fe43d07c3d63050aa82271698dc406dd08388529205dd837bf92dc', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: 0, + feeImpact: 247 + }, + { + from: [ + { + address: 'yXpVMRLKnH9e9Bdcd68e8iA3rxAerzwKop', + addressType: 'unknown' + }, + { + address: 'yeryenDBwJbe7rqdL5uv7iLiJAWSU1iTe2', + addressType: 'unknown' + } + ], + to: [ + { + address: 'yanVwuG1csehvH7PoWHxmYmjtojXBLnoYP', + addressType: 'unknown', + satoshis: 4840346 + }, + { + address: 'ycDeuTfs4U77bTb5cq17dame28zdWHVYfk', + satoshis: 709450000, + addressType: 'external' + } + ], + type: 'received', + time: new Date(1629503698*1e3), + txId: '0dcdaa9bf5b3596be1bcf22113e39026fd49d24b47190e2c7423be936cb116a7', + blockHash: '000000299efeefa87dc15474fd0423c136798975b779a2bb8aa5bb2f50509afb', + isChainLocked: true, + isInstantLocked: true, + satoshisBalanceImpact: 709450000, + feeImpact: 0 + } + ] + + expect(transactionHistoryPK).to.deep.equal(expectedTransactionHistoryPK); + + }) +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getTransactions.js b/packages/wallet-lib/src/types/Account/methods/getTransactions.js new file mode 100644 index 00000000000..8ef9ec7ee9f --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getTransactions.js @@ -0,0 +1,26 @@ +/** + * Get transaction from the store + * @return {[Transaction]} transactions - All transaction in the store + */ +module.exports = function getTransactions() { + const chainStore = this.storage.getChainStore(this.network); + const walletStore = this.storage.getWalletStore(this.walletId); + const transactions = []; + + const { addresses } = walletStore.getPathState(this.accountPath); + + Object + .values(addresses) + .forEach((address) => { + const addressData = chainStore.getAddress(address); + if (addressData) { + const transactionIds = addressData.transactions; + transactionIds.forEach((transactionId) => { + const tx = chainStore.getTransaction(transactionId); + transactions[tx.transaction.hash] = tx.transaction; + }); + } + }); + + return transactions; +}; diff --git a/packages/wallet-lib/src/types/Account/methods/getTransactions.spec.js b/packages/wallet-lib/src/types/Account/methods/getTransactions.spec.js new file mode 100644 index 00000000000..defa1d59220 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getTransactions.spec.js @@ -0,0 +1,30 @@ +const { expect } = require('chai'); +const getTransactions = require('./getTransactions'); + +const getFixtureHDAccountWithStorage = require('../../../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage'); + +const mockedHDSelf = { + ...getFixtureHDAccountWithStorage(), +} +mockedHDSelf.getTransactions = getTransactions; + +describe('Account - getTransactions', function suite() { + this.timeout(10000); + it('should get the transactions', () => { + const transactions = getTransactions.call(mockedHDSelf); + const transactionsHash = Object.keys(transactions); + + expect(transactionsHash).to.deep.equal([ + 'a43845e580ad01f31bc06ce47ab39674e40316c4c6b765b6e54d6d35777ef456', + 'f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8', + 'd37b6c7dd449d605bea9997af8bbeed2f3fbbcb23a4068b1f1ad694db801912d', + '7d1b78157f9f2238669f260d95af03aeefc99577ff0cddb91b3e518ee557a2fd', + '1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b', + 'eb1a7fc8e3b43d3021653b1176f8f9b41e9667d05b65ee225d14c149a5b14f77', + 'c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5', + '6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533', + '6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135', + 'e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841' + ]); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getUTXOS.js b/packages/wallet-lib/src/types/Account/methods/getUTXOS.js new file mode 100644 index 00000000000..0e46d619842 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getUTXOS.js @@ -0,0 +1,71 @@ +/* eslint-disable no-continue, no-restricted-syntax */ +const { Address, Transaction } = require('@dashevo/dashcore-lib'); +const { COINBASE_MATURITY } = require('../../../CONSTANTS'); + +/** + * Return all the utxos + * @param {getUTXOSOptions} options - Options object + * @param {Number} [options.coinbaseMaturity] - Allow to override coinbase maturity + * @return {UnspentOutput[]} + */ +function getUTXOS(options = { + coinbaseMaturity: COINBASE_MATURITY, +}) { + const { + walletId, + network, + } = this; + + const utxos = []; + + const chainStore = this.storage.getChainStore(network); + const accountState = this.storage.getWalletStore(walletId).getPathState(this.accountPath); + const currentBlockHeight = chainStore.state.blockHeight; + + Object.values(accountState.addresses).forEach((address) => { + const addressData = chainStore.getAddress(address); + const utxosKeys = Object.keys(addressData.utxos); + + utxosKeys.forEach((utxoIdentifier) => { + let skipUtxo = false; + const [txid, outputIndex] = utxoIdentifier.split('-'); + + const txInStore = chainStore.getTransaction(txid); + + if (txInStore && txInStore.transaction.isCoinbase()) { + const { transaction, metadata } = txInStore; + // If the transaction is not a special transaction, we can't check its + // maturity at the moment of writing this comment. + // The wallet library doesn't maintain the header chain and thus we can + // figure out the height only from the payload, but old coinbase transactions + // doesn't have a payload. + if (transaction.isSpecialTransaction()) { + const transactionHeight = metadata + ? metadata.height + : transaction.extraPayload.height; + + // We check maturity is at least 100 blocks. + // another way is to just read _scriptBuffer height value. + if (transactionHeight + options.coinbaseMaturity > currentBlockHeight) { + skipUtxo = true; + } + } + } + + if (!skipUtxo) { + utxos.push(new Transaction.UnspentOutput( + { + txId: txid, + vout: parseInt(outputIndex, 10), + script: addressData.utxos[utxoIdentifier].script, + satoshis: addressData.utxos[utxoIdentifier].satoshis, + address: new Address(addressData.address, network), + }, + )); + } + }); + }); + return utxos.sort((a, b) => b.satoshis - a.satoshis); +} + +module.exports = getUTXOS; diff --git a/packages/wallet-lib/src/types/Account/methods/getUTXOS.spec.js b/packages/wallet-lib/src/types/Account/methods/getUTXOS.spec.js new file mode 100644 index 00000000000..906c907ac18 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getUTXOS.spec.js @@ -0,0 +1,65 @@ +const { expect } = require('chai'); +const Dashcore = require('@dashevo/dashcore-lib'); +const getUTXOS = require('./getUTXOS'); +const getFixtureHDAccountWithStorage = require("../../../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage"); + +describe('Account - getUTXOS', function suite() { + this.timeout(10000); + + it('should return empty UTXOs list for new account', () => { + const mockedAccount = getFixtureHDAccountWithStorage(); + const { walletId, accountPath, network } = mockedAccount; + + // Wipe transactions and addresses from the storage to simulate empty UTXOs + mockedAccount.storage.getWalletStore(walletId).state.paths.get(accountPath).addresses = {} + const chainStore = mockedAccount.storage.getChainStore(network); + chainStore.state.blockHeaders = {}; + chainStore.state.transactions = {}; + chainStore.state.addresses = {}; + + const utxos = getUTXOS.call(mockedAccount); + + expect(utxos).to.be.deep.equal([]); + }) + + it('should get the proper UTXOS list', () => { + const mockedAccount = getFixtureHDAccountWithStorage(); + const utxos = getUTXOS.call(mockedAccount); + + const expectedUtxos = [ + { + "address": "yhaAB6e8m3F8zmGX7WAVYa6eEfmSrrnY8x", + "txid": "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5", + "vout": 1, + "scriptPubKey": "76a914e922f6420544f1be0cb593c10535cc3469198bc888ac", + "amount": 4 + }, + { + "address": "yhdRfg5gNr587dtEC4YYMcSHmLVEGqqtHc", + "txid": "e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841", + "vout": 1, + "scriptPubKey": "76a914e9c12479daba9d989cedba69adb56a5a50fe500288ac", + "amount": 1.59999359 + }, + { + "address": "yLk4Hw3w4zDudrDVP6W8J9TggkY57zQUki", + "txid": "c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5", + "vout": 2, + "scriptPubKey": "76a91404a791e67467246c3c0a003007793160387de54288ac", + "amount": 1.0709972 + }, + { + "address": "yNDpPsJqXKM36zHSNEW7c1zSvNnrZ699FY", + "txid": "f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8", + "vout": 1, + "scriptPubKey": "76a91414dfbdcfb48babe7127fa0ee90339c33a46aeda288ac", + "amount": 0.0009917 + } + ]; + + utxos.forEach((utxo, i) => { + expect(utxo).to.be.instanceOf(Dashcore.Transaction.UnspentOutput); + expect(utxo.toObject()).to.be.deep.equal(expectedUtxos[i]); + }) + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.js b/packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.js new file mode 100644 index 00000000000..9833525b4b1 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.js @@ -0,0 +1,20 @@ +const { duffsToDash, calculateDuffBalance } = require('../../../utils'); + +/** + * Return the total balance of unconfirmed utxo + * @param displayDuffs {boolean} True by default. Set the returned format : Duff/dash. + * @return {number} Balance in dash + */ +function getUnconfirmedBalance(displayDuffs = true) { + const { + walletId, storage, accountPath, network, + } = this; + + const { addresses } = storage.getWalletStore(walletId).getPathState(accountPath); + + const chainStore = storage.getChainStore(network); + const totalSat = (calculateDuffBalance(Object.values(addresses), chainStore, 'unconfirmed')); + return (displayDuffs) ? totalSat : duffsToDash(totalSat); +} + +module.exports = getUnconfirmedBalance; diff --git a/packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.spec.js b/packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.spec.js new file mode 100644 index 00000000000..1494975803b --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.spec.js @@ -0,0 +1,35 @@ +const { expect } = require('chai'); +const getTotalBalance = require('./getTotalBalance'); +const getConfirmedBalance = require('./getConfirmedBalance'); +const getUnconfirmedBalance = require('./getUnconfirmedBalance'); +const getFixtureHDAccountWithStorage = require("../../../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage"); + +let mockedAccount; +describe('Account - getUnconfirmedBalance', function suite() { + this.timeout(10000); + before(() => { + mockedAccount = getFixtureHDAccountWithStorage(); + }); + + it('should correctly get the balance', () => { + const balance = getTotalBalance.call(mockedAccount); + expect(balance).to.equal(667198249); + }); + + it('should correctly get the balance confirmed only', () => { + const balance = getConfirmedBalance.call(mockedAccount); + expect(balance).to.equal(667198249); + }); + + // TODO: file looks like a complete duplicate of the getTotalBalance.spec.js + // Should we actually mock and test unconfirmed balance? + it('should correctly get the balance dash value instead of duff', () => { + const balanceTotalDash = getTotalBalance.call(mockedAccount, false); + const balanceUnconfDash = getUnconfirmedBalance.call(mockedAccount, false); + const balanceConfDash = getConfirmedBalance.call(mockedAccount, false); + + expect(balanceTotalDash).to.equal(6.67198249); + expect(balanceUnconfDash).to.equal(0); + expect(balanceConfDash).to.equal(6.67198249); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.js b/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.js new file mode 100644 index 00000000000..f07c81d4b4b --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.js @@ -0,0 +1,61 @@ +/** + * Get an unused address from the store + * @param {AddressType} [type="external"] - Type of the requested usused address + * @param {number} [skip=0] + * @return {AddressObj} + */ +function getUnusedAddress(type = 'external', skip = 0) { + let unused = { + address: '', + }; + const skipped = 0; + const { walletId } = this; + const accountIndex = this.index; + + const { addresses } = this.storage.getWalletStore(walletId).getPathState(this.accountPath); + + const chainStore = this.storage.getChainStore(this.network); + + // We sort by type + const sortedAddresses = { + external: {}, + internal: {}, + }; + Object + .keys(addresses) + .forEach((path) => { + const splittedPath = path.split('/'); + let pathType = 'external'; + if (splittedPath.length > 1) { + pathType = (splittedPath[splittedPath.length - 2] === '0') ? 'external' : 'internal'; + } + sortedAddresses[pathType][path] = addresses[path]; + }); + + const keys = Object.keys(sortedAddresses[type]); + + for (let i = 0; i < keys.length; i += 1) { + const key = keys[i]; + const address = (sortedAddresses[type][key]); + const addressState = chainStore.getAddress(address); + if (!addressState || addressState.transactions.length === 0) { + const keychainData = this.keyChainStore.getMasterKeyChain().getForPath(key); + unused = { + address: keychainData.address.toString(), + path: key, + index: parseInt(key.split('/').splice(-1)[0], 10), + }; + break; + } + } + + if (skipped < skip) { + unused = this.getAddress(skipped); + } + if (unused.address === '') { + return this.getAddress(accountIndex, type); + } + return unused; +} + +module.exports = getUnusedAddress; diff --git a/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.spec.js b/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.spec.js new file mode 100644 index 00000000000..fb1be0ba5b1 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.spec.js @@ -0,0 +1,28 @@ +const { expect } = require('chai'); +const getUnusedAddress = require('./getUnusedAddress'); +const getFixtureHDAccountWithStorage = require('../../../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage'); + +const mockedHDSelf = { + ...getFixtureHDAccountWithStorage(), +} + +describe('Account - getUnusedAddress', function suite() { + this.timeout(10000); + + it('should get the proper unused address', () => { + const unusedAddressExternal = getUnusedAddress.call(mockedHDSelf); + const unusedAddressInternal = getUnusedAddress.call(mockedHDSelf, 'internal'); + + expect(unusedAddressExternal).to.be.deep.equal({ + address: 'ybuL6rM6dgrKzCg8s99f3jxGuv5oz5JcDA', + index: 3, + path: 'm/0/3' + }); + + expect(unusedAddressInternal).to.be.deep.equal({ + address: 'yYwKP1FQae5kbjXkmuirGx6Xzf8NzHpLqW', + path: 'm/1/4', + index: 4 + }); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getUnusedIdentityIndex.js b/packages/wallet-lib/src/types/Account/methods/getUnusedIdentityIndex.js new file mode 100644 index 00000000000..0e32b274e18 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getUnusedIdentityIndex.js @@ -0,0 +1,16 @@ +/** + * + * @return {Promise} + */ +async function getUnusedIdentityIndex() { + // Force identities sync before return unused index + await this.getWorker('IdentitySyncWorker').execWorker(); + + const identityIds = this.storage.getWalletStore(this.walletId).getIndexedIdentityIds(); + + const firstMissingIndex = identityIds.findIndex((identityId) => !identityId); + + return firstMissingIndex > -1 ? firstMissingIndex : identityIds.length; +} + +module.exports = getUnusedIdentityIndex; diff --git a/packages/wallet-lib/src/types/Account/methods/getWorker.js b/packages/wallet-lib/src/types/Account/methods/getWorker.js new file mode 100644 index 00000000000..3b856a29732 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getWorker.js @@ -0,0 +1,18 @@ +const { + UnknownWorker, +} = require('../../../errors'); +/** + * Get a worker by it's name + * @param {string} workerName + * @return {*} + */ +function getWorker(workerName) { + const loweredWorkerName = workerName.toLowerCase(); + const workersList = Object.keys(this.plugins.workers).map((key) => key.toLowerCase()); + if (workersList.includes(loweredWorkerName)) { + return this.plugins.workers[loweredWorkerName]; + } + throw new UnknownWorker(loweredWorkerName); +} + +module.exports = getWorker; diff --git a/packages/wallet-lib/src/types/Account/methods/hasPlugins.js b/packages/wallet-lib/src/types/Account/methods/hasPlugins.js new file mode 100644 index 00000000000..1f2bac0acca --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/hasPlugins.js @@ -0,0 +1,35 @@ +const _ = require('lodash'); +/** + * To any Plugins (StandardPlugins, Worker,...) will lookup in account for it's presence. + * + * @param {[Plugin]} searchedPlugins - Array of constructor or single plugin constructor + * @return {{found:Boolean, results:[{name: string}]}} search - results with presents plugin + */ +module.exports = function hasPlugins(searchedPlugins = []) { + const search = { + found: false, + results: [], + }; + if (!Array.isArray(searchedPlugins)) { + return hasPlugins.call(this, [searchedPlugins]); + } + const { plugins } = this; + _.each(searchedPlugins, (searchedPlugin) => { + const result = {}; + _.each(['workers', 'standard'], (pluginTypeName) => { + const pluginType = plugins[pluginTypeName]; + _.each(pluginType, (plugin) => { + if (searchedPlugin.name === plugin.constructor.name) { + result.name = plugin.name; + } + }); + }); + if (result.name) { + search.results.push(result); + } + }); + if (searchedPlugins.length === search.results.length) { + search.found = true; + } + return search; +}; diff --git a/packages/wallet-lib/src/types/Account/methods/importBlockHeader.js b/packages/wallet-lib/src/types/Account/methods/importBlockHeader.js new file mode 100644 index 00000000000..9200695a187 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/importBlockHeader.js @@ -0,0 +1,28 @@ +const logger = require('../../../logger'); +const EVENTS = require('../../../EVENTS'); +/** + * Import transactions and always keep a number of unused addresses up to gap + * + * @param blockHeader + * @returns {Promise} + */ +module.exports = async function importBlockHeader(blockHeader) { + // At this point, the hash of a blockHeader obtained by doing blockHeader.hash, + // do not seems to be a valid hash. + // So we will just assume continuous incremental (by one) importing process. + + // We do however have the knowledge of previous block hash by + // knowing the following blockHeight blockheader's prevHash value + // const previousHash = blockHeader.prevHash.reverse().toString('hex'); + const { + storage, network, + } = this; + + const applicationStore = storage.application; + const chainStore = storage.getChainStore(network); + applicationStore.blockHash = blockHeader.id; + + chainStore.importBlockHeader(blockHeader); + this.emit(EVENTS.BLOCKHEADER, { type: EVENTS.BLOCKHEADER, payload: blockHeader }); + logger.silly(`Account.importBlockHeader(${blockHeader.id})`); +}; diff --git a/packages/wallet-lib/src/types/Account/methods/importTransactions.js b/packages/wallet-lib/src/types/Account/methods/importTransactions.js new file mode 100644 index 00000000000..05ae5ff70ae --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/importTransactions.js @@ -0,0 +1,42 @@ +const logger = require('../../../logger'); + +/** + * Import transactions and always keep a number of unused addresses up to gap + * + * @param transactionsWithMayBeMetadata + * @returns {Promise<{ addressesGenerated: number, mostRecentHeight: number}>} + */ +module.exports = async function importTransactions(transactionsWithMayBeMetadata) { + const { + storage, + network, + } = this; + + let addressesGenerated = 0; + + const chainStore = storage.getChainStore(network); + + let mostRecentHeight = -1; + transactionsWithMayBeMetadata.forEach((transactionWithMetadata) => { + if (!Array.isArray(transactionWithMetadata)) { + throw new Error('Expecting transactions to be an array of transaction and metadata elements'); + } + const [transaction, metadata] = transactionWithMetadata; + if (metadata && metadata.height > mostRecentHeight) { + mostRecentHeight = metadata.height; + } + + const normalizedTransaction = chainStore.importTransaction(transaction, metadata); + // Affected addresses might not be from our master keychain (account) + const affectedAddressesData = chainStore.considerTransaction(normalizedTransaction.hash); + const affectedAddresses = Object.keys(affectedAddressesData); + logger.silly(`Account.importTransactions - Import ${transaction.hash} to chainStore. ${affectedAddresses.length} addresses affected.`); + + const newPaths = this.generateNewPaths(affectedAddresses); + addressesGenerated += newPaths.length; + this.addPathsToStore(newPaths); + }); + + logger.silly(`Account.importTransactions(len: ${transactionsWithMayBeMetadata.length})`); + return { addressesGenerated, mostRecentHeight }; +}; diff --git a/packages/wallet-lib/src/types/Account/methods/injectPlugin.js b/packages/wallet-lib/src/types/Account/methods/injectPlugin.js new file mode 100644 index 00000000000..4405e29b49e --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/injectPlugin.js @@ -0,0 +1,137 @@ +const _ = require('lodash'); +const { + InjectionErrorCannotInject, + InjectionErrorCannotInjectUnknownDependency: InjectionErrorCannotInjectUnknownDep, +} = require('../../../errors'); +const { is } = require('../../../utils'); +const logger = require('../../../logger'); +/** + * Will try to inject a given plugin. If needed, it will construct the object first (new). + * @param {Plugin} UnsafePlugin - Either a child object, or it's parent class to inject + * @param {Boolean} [allowSensitiveOperations=false] - forcing injection discarding unsafeOp checks. + * @param {Boolean} [awaitOnInjection=true] - When true, wait for onInjected resolve first + * @return {Promise<*>} plugin - instance of the plugin + */ +module.exports = async function injectPlugin( + UnsafePlugin, + allowSensitiveOperations = false, + awaitOnInjection = true, +) { + // TODO : Only called internally, it might be worth to remove public access to it. + // For now, it helps us on debugging + const self = this; + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + try { + const isInit = !(typeof UnsafePlugin === 'function'); + const plugin = (isInit) ? UnsafePlugin : new UnsafePlugin(); + plugin.on('error', (e, errContext) => self.emit('error', e, errContext)); + + const pluginName = plugin.name.toLowerCase(); + logger.debug(`Account.injectPlugin(${pluginName}) - starting injection`); + if (_.isEmpty(plugin)) reject(new InjectionErrorCannotInject(pluginName, 'Empty plugin')); + + // All plugins will require the event object + const { pluginType } = plugin; + + const { + on, emit, once, removeListener, + _conf, _maxListeners, _on, _events, _all, _newListener, _removeListener, listenerTree, + } = self; + + plugin.inject('parentEvents', { + on, + once, + emit, + _conf, + _maxListeners, + wildcard: true, + _on, + _events, + _all, + _newListener, + _removeListener, + removeListener, + listenerTree, + }); + + // Check for dependencies + const deps = plugin.dependencies || []; + + const injectedPlugins = Object.keys(this.plugins.standard).map((key) => key.toLowerCase()); + deps.forEach((dependencyName) => { + if (_.has(self, dependencyName)) { + plugin.inject(dependencyName, self[dependencyName], allowSensitiveOperations); + } else if (typeof self[dependencyName] === 'function') { + plugin.inject(dependencyName, self[dependencyName].bind(self), allowSensitiveOperations); + } else { + const loweredDependencyName = dependencyName.toLowerCase(); + if (injectedPlugins.includes(loweredDependencyName)) { + plugin.inject(dependencyName, this.plugins.standard[loweredDependencyName], true); + } else reject(new InjectionErrorCannotInjectUnknownDep(pluginName, dependencyName)); + } + }); + + switch (pluginType) { + case 'Worker': + self.plugins.workers[pluginName] = plugin; + if (plugin.executeOnStart === true) { + if (plugin.firstExecutionRequired === true) { + const watcher = { + ready: false, + started: false, + announced: false, + }; + self.plugins.watchers[pluginName] = watcher; + + // eslint-disable-next-line no-return-assign,no-param-reassign + const startWatcher = (_watcher) => _watcher.started = true; + // eslint-disable-next-line no-return-assign,no-param-reassign + const setReadyWatch = (_watcher) => _watcher.ready = true; + + const onStartedEvent = () => startWatcher(watcher) + && logger.silly(`WORKER/${pluginName.toUpperCase()}/STARTED`); + const onExecuteEvent = () => setReadyWatch(watcher) + && logger.silly(`WORKER/${pluginName.toUpperCase()}/EXECUTED`); + + self.on(`WORKER/${pluginName.toUpperCase()}/STARTED`, onStartedEvent); + self.on(`WORKER/${pluginName.toUpperCase()}/EXECUTED`, onExecuteEvent); + } + await plugin.startWorker(); + } + break; + case 'Standard': + if (plugin.executeOnStart === true) { + if (plugin.firstExecutionRequired === true) { + const watcher = { + ready: false, + started: false, + announced: false, + }; + self.plugins.watchers[pluginName] = watcher; + // eslint-disable-next-line no-return-assign,no-param-reassign,max-len + const startWatcher = (_watcher) => { _watcher.started = true; _watcher.ready = true; }; + + const onStartedEvent = () => startWatcher(watcher); + self.on(`PLUGIN/${pluginName.toUpperCase()}/STARTED`, onStartedEvent); + } + } + self.plugins.standard[pluginName] = plugin; + await plugin.startPlugin(); + break; + default: + throw new Error(`Unable to inject plugin: ${pluginType}`); + } + + if (is.fn(plugin.onInjected)) { + if (awaitOnInjection) await plugin.onInjected(); + else plugin.onInjected(); + } + + logger.debug(`Account.injectPlugin(${pluginName}) - successfully injected`); + return resolve(plugin); + } catch (e) { + return reject(e); + } + }); +}; diff --git a/packages/wallet-lib/src/types/Account/methods/injectPlugin.spec.js b/packages/wallet-lib/src/types/Account/methods/injectPlugin.spec.js new file mode 100644 index 00000000000..161c1f76cc1 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/injectPlugin.spec.js @@ -0,0 +1,50 @@ +const { expect } = require('chai'); +const EventEmitter = require('events'); +const FaultyWorker = require('../../../../fixtures/plugins/FaultyWorker'); +const WorkingWorker = require('../../../../fixtures/plugins/WorkingWorker'); +const injectPlugin = require('./injectPlugin'); +const expectThrowsAsync = require('../../../utils/expectThrowsAsync'); + +describe('Account - injectPlugin', function suite() { + this.timeout(12000); + const parentEvents = new EventEmitter(); + const emitter = new EventEmitter(); + const mockedSelf = { + plugins: { + standard:{}, + workers: {}, + watchers:{} + }, + storage:{}, + walletId: '123abc', + parentEvents, + on: emitter.on, + emit: emitter.emit, + } + it('should prevent sensible access', async function () { + const expectedException1 = 'Injection of plugin : storage into WorkingWorker not allowed'; + await expectThrowsAsync(async () => await injectPlugin.call(mockedSelf, WorkingWorker), expectedException1); + }); + it('should work', function (done) { + // Time of exec is 10000 ms + injectPlugin.call(mockedSelf, WorkingWorker, true).then(() => { + expect(mockedSelf.plugins.workers['workingworker']).to.exist; + expect(mockedSelf.storage.workingWorkerPass).to.equal(1); + }); + + setTimeout(() => { + expect(mockedSelf.storage.workingWorkerPass).to.equal(2); + mockedSelf.plugins.workers['workingworker'].stopWorker(); + + done(); + }, 10000); + }); + it('should handle faulty worker', async function () { + const expectedException1 = 'Some reason.'; + await expectThrowsAsync(async () => await injectPlugin.call(mockedSelf, FaultyWorker, true), expectedException1); + expect(mockedSelf.plugins.workers['faultyworker']).to.exist; + expect(mockedSelf.plugins.workers['faultyworker'].worker).to.equal(null); + expect(mockedSelf.plugins.workers['faultyworker'].isWorkerRunning).to.equal(false); + expect(mockedSelf.plugins.workers['faultyworker'].state.started).to.equal(false); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/sign.js b/packages/wallet-lib/src/types/Account/methods/sign.js new file mode 100644 index 00000000000..33756ea29a0 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/sign.js @@ -0,0 +1,34 @@ +const { PrivateKey, HDPrivateKey } = require('@dashevo/dashcore-lib'); +/** + * To any object passed (Transaction, ST,..), will try to sign the message given passed keys. + * @param {Transaction} object - The object to sign + * @param {[PrivateKey]} privateKeys - A set of private keys to sign the inputs with + * @param {number} [sigType] - a valid signature value (Dashcore.Signature) + * @return {Transaction} transaction - the signed transaction + */ +module.exports = function sign(object, privateKeys = [], sigType) { + const { network } = this; + + if (object.inputs && (!privateKeys || !privateKeys.length)) { + const addressList = []; + // We seek private key based on inputs + object.inputs.forEach((input) => { + if (input.script) { + // eslint-disable-next-line no-underscore-dangle + const addr = input.script.toAddress(network) || input.output._script.toAddress(network); + addressList.push(addr.toString()); + } + }); + this.getPrivateKeys(addressList).forEach((pk) => { + if (pk.constructor.name === PrivateKey.name) { + privateKeys.push(pk); + } else if (pk.constructor.name === HDPrivateKey.name) { + privateKeys.push(pk.privateKey); + } else { + throw new Error(`Unexpected pk of type ${pk.constructor.name}`); + } + }); + } + + return this.keyChainStore.getMasterKeyChain().sign(object, privateKeys, sigType); +}; diff --git a/packages/wallet-lib/src/types/Account/methods/sign.spec.js b/packages/wallet-lib/src/types/Account/methods/sign.spec.js new file mode 100644 index 00000000000..e2de5251eb0 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/sign.spec.js @@ -0,0 +1,41 @@ +const { expect } = require('chai'); +const Dashcore = require('@dashevo/dashcore-lib'); +const { Wallet } = require('../../../index'); + +const transactions = {"4e2a8b05a805fcee959b8ecfd5557e196a9b8490dd280d6f599b391d650407c8":{"hash":"4e2a8b05a805fcee959b8ecfd5557e196a9b8490dd280d6f599b391d650407c8","version":2,"inputs":[{"prevTxId":"1a38feca081f5c03a4fedbb62eda8706b2b43d5fc13c60b0c84e0d2c67877a4c","outputIndex":1,"sequenceNumber":4294967294,"script":"4730440220297261672847b242b46f860a827d4ef6d392471739937fd19c9e5337dd8abcfc02206f2d3fc3485e8163ac470f67dcad52c650356dc797cae8832e50afd881eba257012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1","scriptString":"71 0x30440220297261672847b242b46f860a827d4ef6d392471739937fd19c9e5337dd8abcfc02206f2d3fc3485e8163ac470f67dcad52c650356dc797cae8832e50afd881eba25701 33 0x03a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1"}],"outputs":[{"satoshis":187719774,"script":"76a91489e98f97403bb0b5674f27177452c4e8980ac42488ac"},{"satoshis":12312280000,"script":"76a91427da1075bef6c1e932f5caab1ded021f2acb65e188ac"}],"nLockTime":13621}}; +const mnemonic = 'wrist ladder salute build walk other scrap stumble true hotel layer treat'; + +describe('Account - sign', function suite() { + this.timeout(10000); + let wallet; + let account; + beforeEach(async () => { + wallet = new Wallet({ mnemonic, offlineMode: true }); + account = await wallet.getAccount({ index: 0 }); + }); + + afterEach(() => { + wallet.disconnect(); + }); + it('should sign a transaction with a message', function () { + account.importTransactions([[new Dashcore.Transaction(transactions["4e2a8b05a805fcee959b8ecfd5557e196a9b8490dd280d6f599b391d650407c8"])]]); + const transaction = account.createTransaction({ + recipient: 'yNPbcFfabtNmmxKdGwhHomdYfVs6gikbPf', // Evonet faucet + satoshis: 1000000, // 1 Dash + }); + const data = 'correct horse battery staple'; + transaction.addData(data); + const signedTransaction = account.sign(transaction); + expect(signedTransaction.isFullySigned()).to.equal(true); + expect(Buffer.from(transaction.toJSON().outputs[1].script, 'hex').slice(2).toString('utf-8')).to.equal(data); + }); + it('should sign and verify a message', () => { + const idKey = account.identities.getIdentityHDKeyByIndex(0, 0); + const idPrivateKey = idKey.privateKey; + const idAddress = idPrivateKey.toAddress().toString(); + const message = new Dashcore.Message('hello, world'); + const signed = account.sign(message, idPrivateKey); + const verify = message.verify(idAddress, signed.toString()); + expect(verify).to.equal(true); + }); +}); diff --git a/packages/wallet-lib/src/types/ChainStore/ChainStore.d.ts b/packages/wallet-lib/src/types/ChainStore/ChainStore.d.ts new file mode 100644 index 00000000000..1d63afc46d9 --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/ChainStore.d.ts @@ -0,0 +1,38 @@ +export declare interface feeState { + minRelay: number +} + +export declare interface ChainStoreState { + fees: feeState; + blockHeight: number; + blockHeaders: Map + transactions: Map + instantLocks: Map + addresses: Map +} + +type networkIdentifier = string; +type exportedState = any; + +export declare class ChainStore { + constructor(networkIdentifier: networkIdentifier); + network: networkIdentifier; + + state: ChainStoreState; + + considerTransaction(transactionHash: string): any; + exportState(): exportedState; + importState(exportedState): void; + + getAddress(address: string): any; + getAddresses(address: string): Map + + getBlockHeader(blockHeaderHash: string): any; + getInstantLock(transactionHash: string): any; + getTransaction(transactionHash: string): any; + + importAddress(address: any): void; + importAddress(blockHeader: any): void; + importInstantLock(instantLock: any): void; + importTransaction(transaction: any, metadata: any): any; +} diff --git a/packages/wallet-lib/src/types/ChainStore/ChainStore.js b/packages/wallet-lib/src/types/ChainStore/ChainStore.js new file mode 100644 index 00000000000..0835e2b8614 --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/ChainStore.js @@ -0,0 +1,73 @@ +const EventEmitter = require('events'); + +const { + Transaction, BlockHeader, +} = require('@dashevo/dashcore-lib'); + +const SCHEMA = { + blockHeaders: { + '*': (hex) => new BlockHeader(Buffer.from(hex, 'hex')), + }, + transactions: { + '*': Transaction, + }, + txMetadata: { + '*': { + blockHash: 'string', + height: 'number', + isChainLocked: 'boolean', + isInstantLocked: 'boolean', + }, + }, + fees: { + minRelay: 'number', + }, +}; + +/** + * ChainStore holds any information that is relatives to a specific network. + * Information such as blockHeaders, transactions, instantLocks. + * Also holds the state of addresses based on the transactions imported (e.g: balances and utxos). + */ +class ChainStore extends EventEmitter { + constructor(networkIdentifier = 'testnet') { + super(); + this.network = networkIdentifier; + + this.state = { + fees: { + minRelay: -1, + }, + blockHeight: 0, + blockHeaders: new Map(), + transactions: new Map(), + instantLocks: new Map(), + addresses: new Map(), + }; + } + + getTransactions() { + return this.state.transactions; + } +} + +ChainStore.prototype.SCHEMA = SCHEMA; + +ChainStore.prototype.considerTransaction = require('./methods/considerTransaction'); + +ChainStore.prototype.exportState = require('./methods/exportState'); +ChainStore.prototype.importState = require('./methods/importState'); + +ChainStore.prototype.getAddress = require('./methods/getAddress'); +ChainStore.prototype.getAddresses = require('./methods/getAddresses'); + +ChainStore.prototype.getBlockHeader = require('./methods/getBlockHeader'); +ChainStore.prototype.getInstantLock = require('./methods/getInstantLock'); +ChainStore.prototype.getTransaction = require('./methods/getTransaction'); + +ChainStore.prototype.importAddress = require('./methods/importAddress'); +ChainStore.prototype.importBlockHeader = require('./methods/importBlockHeader'); +ChainStore.prototype.importInstantLock = require('./methods/importInstantLock'); +ChainStore.prototype.importTransaction = require('./methods/importTransaction'); + +module.exports = ChainStore; diff --git a/packages/wallet-lib/src/types/ChainStore/ChainStore.spec.js b/packages/wallet-lib/src/types/ChainStore/ChainStore.spec.js new file mode 100644 index 00000000000..484ea85a414 --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/ChainStore.spec.js @@ -0,0 +1,97 @@ +const { Transaction, BlockHeader } = require('@dashevo/dashcore-lib'); +const { expect } = require('chai'); +const ChainStore = require('./ChainStore'); +const fixtures1 = require('../../../fixtures/wallets/2a331817b9d6bf85100ef0/chain-store.json'); + +describe('ChainStore - class', () => { + let testnetChainStore; + + it('should create a new chain store', () => { + testnetChainStore = new ChainStore('testnet'); + + expect(new ChainStore()).to.deep.equal(testnetChainStore); + expect(testnetChainStore.state).to.exist; + expect(testnetChainStore.state.blockHeight).to.equal(0); + expect(testnetChainStore.state.fees).to.deep.equal({ minRelay: -1 }); + expect(testnetChainStore.state.blockHeaders).to.deep.equal(new Map()); + expect(testnetChainStore.state.transactions).to.deep.equal(new Map()); + expect(testnetChainStore.state.instantLocks).to.deep.equal(new Map()); + expect(testnetChainStore.state.addresses).to.deep.equal(new Map()); + }); + it('should be able to import transactions with metadata', () => { + const { transactions, txMetadata } = fixtures1; + + const tx1 = new Transaction( + transactions.d48f415f08fb795d43b216cf56e9ef10e059d4009cfc8fc90edfc0d3850813af, + ); + const meta1 = txMetadata.d48f415f08fb795d43b216cf56e9ef10e059d4009cfc8fc90edfc0d3850813af; + testnetChainStore.importTransaction(tx1, meta1); + testnetChainStore.considerTransaction(tx1.hash); + + const storedTransactionData = testnetChainStore.getTransaction('d48f415f08fb795d43b216cf56e9ef10e059d4009cfc8fc90edfc0d3850813af'); + expect(storedTransactionData.transaction.toString()).to.equal(tx1.toString()); + expect(storedTransactionData.metadata).to.deep.equal(meta1); + }); + it('should be able to import transaction without metadata', () => { + const { transactions } = fixtures1; + + const tx1 = new Transaction(transactions['0dcdaa9bf5b3596be1bcf22113e39026fd49d24b47190e2c7423be936cb116a7']); + testnetChainStore.importTransaction(tx1); + testnetChainStore.considerTransaction(tx1.hash); + + const storedTransactionData = testnetChainStore.getTransaction('0dcdaa9bf5b3596be1bcf22113e39026fd49d24b47190e2c7423be936cb116a7'); + expect(storedTransactionData.transaction.toString()).to.equal(tx1.toString()); + expect(storedTransactionData.metadata).to.deep.equal({ + blockHash: null, + height: null, + isInstantLocked: false, + isChainLocked: false, + }); + }); + it('should update metadata', () => { + const { transactions, txMetadata } = fixtures1; + + const tx1 = new Transaction(transactions['0dcdaa9bf5b3596be1bcf22113e39026fd49d24b47190e2c7423be936cb116a7']); + const meta1 = txMetadata['0dcdaa9bf5b3596be1bcf22113e39026fd49d24b47190e2c7423be936cb116a7']; + testnetChainStore.importTransaction(tx1, meta1); + testnetChainStore.considerTransaction(tx1.hash); + const storedTransactionData = testnetChainStore.getTransaction('0dcdaa9bf5b3596be1bcf22113e39026fd49d24b47190e2c7423be936cb116a7'); + expect(storedTransactionData.metadata).to.deep.equal(meta1); + }); + it('should be able to import and get a blockheader', () => { + const { blockHeaders } = fixtures1; + + const blockheaders1 = BlockHeader.fromString( + blockHeaders['0000012464fba1e3c66e678de79e4003bf17c36d5caa689e80fd4711fe620ec1'], + ); + testnetChainStore.importBlockHeader(blockheaders1); + + const storedTransactionData = testnetChainStore.getBlockHeader('0000012464fba1e3c66e678de79e4003bf17c36d5caa689e80fd4711fe620ec1'); + expect(storedTransactionData.toString()).to.equal(blockheaders1.toString()); + }); + + it('should export and import state', () => { + const exportedState = testnetChainStore.exportState(); + const importedChainStore = new ChainStore(); + importedChainStore.importState(exportedState); + + expect(importedChainStore.state.blockHeaders) + .to.deep.equal(testnetChainStore.state.blockHeaders); + expect(importedChainStore.state.instantLocks) + .to.deep.equal(testnetChainStore.state.instantLocks); + + const expectedTransactions = testnetChainStore.state.transactions; + const importedTransactions = importedChainStore.state.transactions; + + expect(importedTransactions.size).to.equal(expectedTransactions.size); + + Array.from(expectedTransactions.keys()).forEach((txHash) => { + expect(importedTransactions.has(txHash)).to.equal(true); + expect(importedTransactions.get(txHash).transaction.toString()) + .to.equal(expectedTransactions.get(txHash).transaction.toString()); + + expect(importedTransactions.get(txHash).metadata) + .to.deep.equal(expectedTransactions.get(txHash).metadata); + }); + }); +}); diff --git a/packages/wallet-lib/src/types/ChainStore/methods/considerTransaction.js b/packages/wallet-lib/src/types/ChainStore/methods/considerTransaction.js new file mode 100644 index 00000000000..0a897c8acc8 --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/methods/considerTransaction.js @@ -0,0 +1,98 @@ +const { Transaction } = require('@dashevo/dashcore-lib'); +const logger = require('../../../logger'); +const EVENTS = require('../../../EVENTS'); + +const { Output } = Transaction; + +function considerTransaction(transactionHash) { + logger.silly(`ChainStore - Considering transaction ${transactionHash}`); + const { transaction, metadata } = this.getTransaction(transactionHash); + + const { inputs, outputs } = transaction; + let outputIndex = -1; + + const processedAddressesForTx = {}; + + let broadcastTxEvent = false; + + [...inputs, ...outputs].forEach((element) => { + const isOutput = (element instanceof Output); + if (isOutput) outputIndex += 1; + + if (element.script) { + const address = element.script.toAddress(this.network).toString(); + const watchedAddress = this.getAddress(address); + if (watchedAddress) { + // If the transactions has already been processed in a previous insertion, + // we can skip the processing now, it's important to do so as we might consider + // the same transaction multiple times (e.g: on address import) + if (watchedAddress.transactions.includes(transactionHash)) { + return; + } + + // We mark our address as affected so we update the tx later on + if (!processedAddressesForTx[watchedAddress.address]) { + processedAddressesForTx[watchedAddress.address] = watchedAddress; + } + + if (!isOutput) { + const vin = element; + const utxoKey = `${vin.prevTxId.toString('hex')}-${vin.outputIndex}`; + if (watchedAddress.utxos[utxoKey]) { + const previousOutput = watchedAddress.utxos[utxoKey]; + watchedAddress.balanceSat -= previousOutput.satoshis; + delete watchedAddress.utxos[utxoKey]; + broadcastTxEvent = true; + } + } else { + const vout = element; + + const utxoKey = `${transaction.hash}-${outputIndex}`; + if (!watchedAddress.utxos[utxoKey]) { + watchedAddress.utxos[utxoKey] = vout.toJSON(); + watchedAddress.balanceSat += vout.satoshis; + broadcastTxEvent = true; + } else if (watchedAddress.unconfirmedBalanceSat >= vout.satoshis) { + watchedAddress.unconfirmedBalanceSat -= vout.satoshis; + watchedAddress.balanceSat += vout.satoshis; + broadcastTxEvent = true; + } + } + } + } + }); + + // As the same address can have one or more inputs and one or more outputs in the same tx + // we update it's transactions array as last step of importing + Object.values(processedAddressesForTx).forEach((addressObject) => { + addressObject.transactions.push(transaction.hash); + }); + + // If any of the previous transactions added had a height that is subsequent + // of the one we just add + // We should remove and re-add address to trigger reconsidering in proper order + if (metadata && metadata.height > 0) { + Object + .keys(processedAddressesForTx) + .forEach((address) => { + const addressTransactions = processedAddressesForTx[address].transactions; + addressTransactions.forEach((tx) => { + if (metadata.height < this.getTransaction(tx).metadata.height) { + this.state.addresses.delete(address); + this.importAddress(address); + processedAddressesForTx[address] = this.getAddress(address); + } + }); + }); + this.emit(EVENTS.TX_METADATA, { hash: transaction.hash, metadata }); + } + + // TODO: restore EVENTS.FETCHED_UNCONFIRMED_TRANSACTION + if (broadcastTxEvent) { + this.emit(EVENTS.FETCHED_CONFIRMED_TRANSACTION, { transaction }); + } + + return processedAddressesForTx; +} + +module.exports = considerTransaction; diff --git a/packages/wallet-lib/src/types/ChainStore/methods/exportState.js b/packages/wallet-lib/src/types/ChainStore/methods/exportState.js new file mode 100644 index 00000000000..9d70d9811d2 --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/methods/exportState.js @@ -0,0 +1,39 @@ +function exportState() { + const { state } = this; + const { + blockHeaders, + transactions, + blockHeight, + fees, + } = state; + + const serializedState = { + blockHeaders: {}, + transactions: {}, + txMetadata: {}, + fees: {}, + }; + + let reorgSafeHeight = Infinity; + + if (blockHeight) { + reorgSafeHeight = blockHeight - 6; + } + + [...blockHeaders.entries()].forEach(([blockHeaderHash, blockHeader]) => { + serializedState.blockHeaders[blockHeaderHash] = blockHeader.toString(); + }); + + [...transactions.entries()].forEach(([transactionHash, { transaction, metadata }]) => { + if (metadata && metadata.height && metadata.height <= reorgSafeHeight) { + serializedState.transactions[transactionHash] = transaction.toString(); + serializedState.txMetadata[transactionHash] = metadata; + } + }); + + serializedState.fees.minRelay = fees.minRelay; + + return serializedState; +} + +module.exports = exportState; diff --git a/packages/wallet-lib/src/types/ChainStore/methods/getAddress.js b/packages/wallet-lib/src/types/ChainStore/methods/getAddress.js new file mode 100644 index 00000000000..1076c9ed22d --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/methods/getAddress.js @@ -0,0 +1,5 @@ +function getAddress(address) { + return this.state.addresses.get(address.toString()); +} + +module.exports = getAddress; diff --git a/packages/wallet-lib/src/types/ChainStore/methods/getAddresses.js b/packages/wallet-lib/src/types/ChainStore/methods/getAddresses.js new file mode 100644 index 00000000000..6d6b94bcf74 --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/methods/getAddresses.js @@ -0,0 +1,5 @@ +function getAddresses() { + return this.state.addresses; +} + +module.exports = getAddresses; diff --git a/packages/wallet-lib/src/types/ChainStore/methods/getBlockHeader.js b/packages/wallet-lib/src/types/ChainStore/methods/getBlockHeader.js new file mode 100644 index 00000000000..be7d9c57925 --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/methods/getBlockHeader.js @@ -0,0 +1,5 @@ +function getBlockHeader(blockHeaderHash) { + return this.state.blockHeaders.get(blockHeaderHash); +} + +module.exports = getBlockHeader; diff --git a/packages/wallet-lib/src/types/ChainStore/methods/getInstantLock.js b/packages/wallet-lib/src/types/ChainStore/methods/getInstantLock.js new file mode 100644 index 00000000000..523380658f6 --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/methods/getInstantLock.js @@ -0,0 +1,5 @@ +function getInstantLock(transactionHash) { + return this.state.instantLocks.get(transactionHash); +} + +module.exports = getInstantLock; diff --git a/packages/wallet-lib/src/types/ChainStore/methods/getTransaction.js b/packages/wallet-lib/src/types/ChainStore/methods/getTransaction.js new file mode 100644 index 00000000000..7f97e871321 --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/methods/getTransaction.js @@ -0,0 +1,5 @@ +function getTransaction(transactionHash) { + return this.state.transactions.get(transactionHash); +} + +module.exports = getTransaction; diff --git a/packages/wallet-lib/src/types/ChainStore/methods/importAddress.js b/packages/wallet-lib/src/types/ChainStore/methods/importAddress.js new file mode 100644 index 00000000000..0b5d6ca27d4 --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/methods/importAddress.js @@ -0,0 +1,35 @@ +const logger = require('../../../logger'); +const sortTransactions = require('../../../utils/sortTransactions'); + +function importAddress(address, reconsiderTransactions = true) { + logger.silly(`ChainStore - import address ${address}`); + + if (this.state.addresses.has(address.toString())) { + return; + } + + this.state.addresses.set(address.toString(), { + address: address.toString(), + transactions: [], + utxos: {}, + balanceSat: 0, + unconfirmedBalanceSat: 0, + }); + + // TODO: Consider refactoring + // this code might engage into a cyclic recursive chain of side effects + // of uncertain complexity + // (importAddress -> considerTransaction -> importAddress -> ...) + if (reconsiderTransactions) { + // We need to consider all previous transactions + const transactions = [...this.state.transactions.values()]; + + const sortedTransactions = sortTransactions(transactions); + + sortedTransactions.forEach((transaction) => { + this.considerTransaction(transaction.hash); + }); + } +} + +module.exports = importAddress; diff --git a/packages/wallet-lib/src/types/ChainStore/methods/importBlockHeader.js b/packages/wallet-lib/src/types/ChainStore/methods/importBlockHeader.js new file mode 100644 index 00000000000..9d13c064297 --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/methods/importBlockHeader.js @@ -0,0 +1,5 @@ +function importBlockHeader(blockHeader) { + this.state.blockHeaders.set(blockHeader.hash, blockHeader); +} + +module.exports = importBlockHeader; diff --git a/packages/wallet-lib/src/types/ChainStore/methods/importInstantLock.js b/packages/wallet-lib/src/types/ChainStore/methods/importInstantLock.js new file mode 100644 index 00000000000..c39d5d493ce --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/methods/importInstantLock.js @@ -0,0 +1,5 @@ +function importInstantLock(instantLock) { + this.state.instantLocks.set(instantLock.txid, instantLock); +} + +module.exports = importInstantLock; diff --git a/packages/wallet-lib/src/types/ChainStore/methods/importInstantLock.spec.js b/packages/wallet-lib/src/types/ChainStore/methods/importInstantLock.spec.js new file mode 100644 index 00000000000..86cfd3443ab --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/methods/importInstantLock.spec.js @@ -0,0 +1 @@ +const is = '01424b1c86a93794b4cc8bf9391494faf6994fe6794747ba2bc415e7bcd353204301000000c518facee06d0dc8d5b71028ce949dc8ba99d95c023af2752421a3647f6ce668827fbd5503f9a48a73ab9e30e564cd8b95f6c603caa93deaf4c3836c89d19cb71edc6edab0eccafe42d415e5598fa92e15ff71d5e8a81a34cac04c2fea031cc2f9e653a2d2a7fa2ef1994dee8df60e031c29ab7481ccb1bd3a89e7576cb79880'; \ No newline at end of file diff --git a/packages/wallet-lib/src/types/ChainStore/methods/importState.js b/packages/wallet-lib/src/types/ChainStore/methods/importState.js new file mode 100644 index 00000000000..f9f2a84c271 --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/methods/importState.js @@ -0,0 +1,23 @@ +const castStorageItemsTypes = require('../../../utils/castStorageItemsTypes'); + +function importState(rawState) { + const state = castStorageItemsTypes(rawState, this.SCHEMA); + + const { + blockHeaders, + transactions, + txMetadata, + } = state; + + Object.values(blockHeaders).forEach((blockHeader) => { + this.importBlockHeader(blockHeader); + }); + + Object.keys(transactions).forEach((hash) => { + const tx = transactions[hash]; + const metadata = txMetadata[hash]; + this.importTransaction(tx, metadata); + }); +} + +module.exports = importState; diff --git a/packages/wallet-lib/src/types/ChainStore/methods/importTransaction.js b/packages/wallet-lib/src/types/ChainStore/methods/importTransaction.js new file mode 100644 index 00000000000..2c29f7acc2b --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/methods/importTransaction.js @@ -0,0 +1,24 @@ +const { Transaction } = require('@dashevo/dashcore-lib'); +const is = require('../../../utils/is'); + +function importTransaction(transaction, metadata = {}) { + // Even if transaction is a transaction object, if manglized, + // it might end up not being a correct instanceof internally. + if (Array.isArray(transaction)) { + throw new Error('Will not import an array of transaction'); + } + const normalizedTransaction = is.string(transaction) ? new Transaction(transaction) : transaction; + this.state.transactions.set(normalizedTransaction.hash, { + transaction: normalizedTransaction, + metadata: { + blockHash: metadata.blockHash || null, + height: metadata.height || null, + isInstantLocked: metadata.isInstantLocked || false, + isChainLocked: metadata.isChainLocked || false, + }, + }); + + return normalizedTransaction; +} + +module.exports = importTransaction; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.d.ts b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.d.ts new file mode 100644 index 00000000000..1989fd6784c --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.d.ts @@ -0,0 +1,60 @@ +import {PrivateKey, Network,} from "../types"; +import {HDPrivateKey, HDPublicKey} from "@dashevo/dashcore-lib"; +import {Transaction} from "@dashevo/dashcore-lib/typings/transaction/Transaction"; + +export declare namespace DerivableKeyChain { + interface IDerivableKeyChainOptions { + network?: Network; + keys?: [Keys] + } +} + +type keyChainId = string; +type rootKey = any; +type firstUnusedAddress = { + path: string; + address: string +} + + +export declare class DerivableKeyChain { + constructor(options?: DerivableKeyChain.IDerivableKeyChainOptions); + network: Network; + keys: [Keys]; + + type: HDKeyTypesParam|PrivateKeyTypeParam; + HDPrivateKey?: HDPrivateKey; + privateKey?: PrivateKey; + + getForPath(path: string, opts: any): any; + getForAddress(address): any; + + getDIP15ExtendedKey(userUniqueId: string, contactUniqueId: string, index?: number, accountIndex?: number, type?: HDKeyTypesParam): HDKeyTypes; + getFirstUnusedAddress(): firstUnusedAddress; + getHardenedBIP44HDKey(type?: HDKeyTypesParam): HDKeyTypes; + getHardenedDIP9FeatureHDKey(type?: HDKeyTypesParam): HDKeyTypes; + getHardenedDIP15AccountKey(index?: number, type?: HDKeyTypesParam): HDKeyTypes; + getRootKey(): rootKey; + getWatchedAddresses(): Array; + getIssuedPaths(): Array; + maybeLookAhead(): any; + markAddressAsUsed(address: string): any; + sign(object: Transaction|any, privateKeys:[PrivateKey], sigType: number): any; +} + +type HDKeyTypes = HDPublicKey | HDPrivateKey; + +export declare enum HDKeyTypesParam { + HDPrivateKey="HDPrivateKey", + HDPublicKey="HDPrivateKey", +} +export declare enum PrivateKeyTypeParam { + privateKey='privateKey' +} +export declare interface Keys { + [path: string]: { + path: string + }; +} + + diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.js b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.js new file mode 100644 index 00000000000..9191f40d7e2 --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.js @@ -0,0 +1,107 @@ +const { Networks, HDPrivateKey, HDPublicKey } = require('@dashevo/dashcore-lib'); +const { PrivateKey, PublicKey } = require('@dashevo/dashcore-lib'); +const { doubleSha256 } = require('../../utils/crypto'); +const { mnemonicToHDPrivateKey } = require('../../utils/mnemonic'); + +function generateKeyChainId(key) { + const keyChainIdSuffix = doubleSha256(key.toString()).toString('hex').slice(0, 10); + return `kc${keyChainIdSuffix}`; +} + +function fromOptions(opts) { + let rootKey; + let rootKeyType; + let network = Networks.testnet.toString(); + let passphrase = ''; + + if (opts) { + if (opts.passphrase) { + passphrase = opts.passphrase; + } + if (opts.mnemonic) { + rootKeyType = 'HDPrivateKey'; + rootKey = (typeof opts.mnemonic === 'string') ? HDPrivateKey(opts.HDPrivateKey) : opts.HDPrivateKey; + } + if (opts.network) { + network = opts.network; + } + if (opts.HDPrivateKey) { + rootKeyType = 'HDPrivateKey'; + rootKey = (typeof opts.HDPrivateKey === 'string') ? HDPrivateKey(opts.HDPrivateKey) : opts.HDPrivateKey; + network = rootKey.network.toString(); + } else if (opts.HDPublicKey) { + rootKeyType = 'HDPublicKey'; + rootKey = (typeof opts.HDPublicKey === 'string') ? HDPublicKey(opts.HDPublicKey) : opts.HDPublicKey; + network = rootKey.network.toString(); + } else if (opts.privateKey) { + rootKeyType = 'privateKey'; + rootKey = (typeof opts.privateKey === 'string') ? new PrivateKey(opts.privateKey, opts.network) : opts.privateKey; + network = rootKey.network.toString(); + } else if (opts.publicKey) { + rootKeyType = 'publicKey'; + rootKey = (typeof opts.publicKey === 'string') ? new PublicKey(opts.publicKey, opts.network) : opts.publicKey; + network = rootKey.network.toString(); + } else if (opts.address) { + rootKeyType = 'address'; + rootKey = opts.address.toString(); + } else if (opts.mnemonic) { + return fromOptions({ + ...opts, + HDPrivateKey: mnemonicToHDPrivateKey(opts.mnemonic, network, passphrase), + }); + } + } + + const lookAheadOpts = { + isWatched: true, + paths: {}, + ...opts.lookAheadOpts, + }; + + return { + rootKeyType, + rootKey, + network, + passphrase, + lookAheadOpts, + }; +} + +class DerivableKeyChain { + constructor(opts = {}) { + const { + rootKey, + rootKeyType, + network, + lookAheadOpts, + } = fromOptions(opts); + if (!rootKeyType || !rootKey) { + throw new Error('Expect one of [mnemonic, HDPrivateKey, HDPublicKey, privateKey, publicKey, address] to be provided.'); + } + this.keyChainId = generateKeyChainId(rootKey); + + this.rootKey = rootKey; + this.network = network; + this.rootKeyType = rootKeyType; + this.lookAheadOpts = { isWatched: true, ...lookAheadOpts }; + + this.issuedPaths = new Map(); + + this.maybeLookAhead(); + } +} +DerivableKeyChain.prototype.getForPath = require('./methods/getForPath'); +DerivableKeyChain.prototype.getForAddress = require('./methods/getForAddress'); +DerivableKeyChain.prototype.getDIP15ExtendedKey = require('./methods/getDIP15ExtendedKey'); +DerivableKeyChain.prototype.getFirstUnusedAddress = require('./methods/getFirstUnusedAddress'); +DerivableKeyChain.prototype.getHardenedBIP44HDKey = require('./methods/getHardenedBIP44HDKey'); +DerivableKeyChain.prototype.getHardenedDIP9FeatureHDKey = require('./methods/getHardenedDIP9FeatureHDKey'); +DerivableKeyChain.prototype.getHardenedDIP15AccountKey = require('./methods/getHardenedDIP15AccountKey'); +DerivableKeyChain.prototype.getRootKey = require('./methods/getRootKey'); +DerivableKeyChain.prototype.getWatchedAddresses = require('./methods/getWatchedAddresses'); +DerivableKeyChain.prototype.getIssuedPaths = require('./methods/getIssuedPaths'); +DerivableKeyChain.prototype.maybeLookAhead = require('./methods/maybeLookAhead'); +DerivableKeyChain.prototype.markAddressAsUsed = require('./methods/markAddressAsUsed'); +DerivableKeyChain.prototype.sign = require('./methods/sign'); + +module.exports = DerivableKeyChain; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.spec.js b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.spec.js new file mode 100644 index 00000000000..98dfc69bb5b --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.spec.js @@ -0,0 +1,178 @@ +const Dashcore = require('@dashevo/dashcore-lib'); +const { expect } = require('chai'); +const DerivableKeyChain = require('./DerivableKeyChain'); +const { mnemonicToHDPrivateKey } = require('../../utils/mnemonic'); + +let derivableKeyChain; +let derivableKeyChain2; +const mnemonic = 'during develop before curtain hazard rare job language become verb message travel'; +const mnemonic2 = 'birth kingdom trash renew flavor utility donkey gasp regular alert pave layer'; +const pk = '4226d5e2fe8cbfe6f5beb7adf5a5b08b310f6c4a67fc27826779073be6f5699e'; +const hdPublicKey = 'xpub661MyMwAqRbcFGB6XSWBsD725rJDUbFUpy4zWe2u22nJ2BxpoHFxtVDfKnTnvVQHohnY7AsVpRTHDv6PyPQTYu1KxFPKw29MAVXPEpz1G7V'; +const expectedRootDIP15AccountKey_0 = 'tprv8hRzmheQujhJN5XP2dj955nAFCKeEoSifJRWuutdbwWRtusdDQ426jbp75EqErUSuTxmPyxYmP1TpcF5qdxGhXLNXRLMGsRLG6NFCv1WnaQ'; +const expectedRootDIP15AccountKey_1 = 'tprv8hRzmheQujhJQyCtFTuUFHxB3Ag5VLB994zhH4CfxbA41cq73HT2mpYq5M33V54oJyn6g514saxxVJB886G55eYX56J6D6x87UNNT6iQHkR'; +const expectedKeyForChild_0 = 'tprv8d4podc2Tg459CH2bwLHXj3vdJFBT2rdsk5Nr1djH7hzHdt5LRdvN6QyFwMiDy7ffRdik7fEVRKKgsHB4F18sh8xF6jFXpKq4sUgGBoSbKw'; +describe('DerivableKeyChain', function suite() { + this.timeout(1000); + it('should create a DerivableKeyChain', () => { + const expectedException1 = 'Expect one of [mnemonic, HDPrivateKey, HDPublicKey, privateKey, publicKey, address] to be provided.'; + expect(() => new DerivableKeyChain()).to.throw(expectedException1); + + derivableKeyChain = new DerivableKeyChain({ mnemonic: mnemonic, network: 'testnet' }); + expect(derivableKeyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(derivableKeyChain.network.toString()).to.equal('testnet'); + expect(derivableKeyChain.rootKey.network.toString()).to.equal('testnet'); + + derivableKeyChain2 = new DerivableKeyChain({ mnemonic: mnemonic2, network: 'livenet' }); + }); + it('should generate key for full path', () => { + const path = 'm/44\'/1\'/0\'/0/0'; + const pk2 = derivableKeyChain.getForPath(path).key; + const address = new Dashcore.Address(pk2.publicKey.toAddress()).toString(); + expect(address).to.equal('yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT'); + }); + it('should get hardened feature path', () => { + const hardenedPk = derivableKeyChain.getHardenedBIP44HDKey(); + const pk2 = derivableKeyChain.getForPath('m/44\'/1\'').key; + expect(pk2.toString()).to.equal(hardenedPk.toString()); + }); + it('should get DIP15 account key', function () { + const rootDIP15AccountKey_0 = derivableKeyChain.getHardenedDIP15AccountKey(0); + expect(rootDIP15AccountKey_0.toString()).to.deep.equal(expectedRootDIP15AccountKey_0); + const rootDIP15AccountKey_1 = derivableKeyChain.getHardenedDIP15AccountKey(1); + expect(rootDIP15AccountKey_1.toString()).to.deep.equal(expectedRootDIP15AccountKey_1); + }); + + it('should get DIP15 extended key', function () { + const userUniqueId = '0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'; + const contactUniqueId = '0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'; + + // m/9'/5'/15'/0'/0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'/0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'/0 + const DIP15ExtPubKey_0 = derivableKeyChain2.getDIP15ExtendedKey(userUniqueId, contactUniqueId, 0, 0, type='HDPublicKey'); + expect(DIP15ExtPubKey_0.toString()).to.equal('xpub6LTkTQFSb8KMgMSz4B6sMZLpkQAY6wSTDprDkHDmLwWLpnjxazuxZn13FrSLKUafitsxuaaffM5a49P6aswhpppWUuYW6eFnwBXshR2W2eY'); + expect(DIP15ExtPubKey_0.publicKey.toString()).to.equal('038030c88ab0106e1f4af3b939db2bafc56f892554106f08da1ce1f9ef10f807bd') + + const DIP15ExtPrivKey_0 = derivableKeyChain2.getDIP15ExtendedKey(userUniqueId, contactUniqueId, 0, 0); + expect(DIP15ExtPrivKey_0.toString()).to.equal('xprvA7UQ3tiYkkm4TsNWx9ZrzRQ6CNL3hUibrbvcwtp9nbyMwzQp3Tbi1ygZQaPoigDhCf8XUjMmGK2NbnB2kLXPYg99Lp6e3iki318sdWcFN3q'); + expect(DIP15ExtPrivKey_0.privateKey.toString()).to.equal('fac40790776d171ee1db90899b5eb2df2f7d2aaf35ad56f07ffb8ed2c57f8e60') + expect(DIP15ExtPrivKey_0.publicKey.toString()).to.equal('038030c88ab0106e1f4af3b939db2bafc56f892554106f08da1ce1f9ef10f807bd') + + const userAhash = "0xa11ce14f698b32e9bb306dba7bbbee831263dcf658abeebb39930460ead117e5"; + const userBhash = "0xb0b052ff075c5ca3c16c3e20e9ac8223834475cc1324ab07889cb24ce6a62793"; + const DIP15ExtKey_1 = derivableKeyChain.getDIP15ExtendedKey(userAhash, userBhash, 0, 0); + expect(DIP15ExtKey_1.privateKey.toString()).to.equal('60581b6dca8244d3fb3cfe619b5a22277e5423b01e5285f356981f247e0f4a60') + expect(DIP15ExtKey_1.publicKey.toString()).to.equal('03deaac00f721151307fbc7bf80d7b8afab98c1f026d67e5f56b21e2013f551ce6') + }); + it('should derive from hardened feature path', () => { + const hardenedHDKey = derivableKeyChain.getHardenedBIP44HDKey(); + const pk2 = derivableKeyChain.getForPath(`m/44'/1'`).key; + expect(pk2.toString()).to.equal(hardenedHDKey.toString()); + expect(hardenedHDKey.toString()).to.deep.equal('tprv8dtrJNytYHRiZY585hmHGbguS6VjGpK49puSB7oXZjLHcQfrAzQkF4ZCxM2DkEbyY85J4EYcZ8EjT5ZCU8ozB727TDdodbfXet5GkGau2RQ'); + const derivedPk = hardenedHDKey.deriveChild(0, true).deriveChild(0).deriveChild(0); + const address = new Dashcore.Address(derivedPk.publicKey.toAddress()).toString(); + expect(address).to.equal('yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT'); + }); + it('should get hardened DIP9FeatureHDKey', function () { + const hardenedHDKey = derivableKeyChain.getHardenedDIP9FeatureHDKey(); + const pk2 = derivableKeyChain.getForPath(`m/9'/1'`).key; + expect(pk2.toString()).to.equal(hardenedHDKey.toString()); + expect(hardenedHDKey.toString()).to.deep.equal('tprv8fBJjWoGgCpGRCbyzE9RUA59rmoN1RUijhLnXGL4VHnLxvSe523yVg4GrGzbR6TyXtdynAEh5z8UX55EXt2Cb3xjvrsx2PgTY9BHxzFVkWn'); + }); + it('should get key for path', () => { + const derivableKeyChain2 = new DerivableKeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic, 'testnet') }); + const keyForChild = derivableKeyChain2.getForPath('m/0').key; + expect(keyForChild.toString()).to.equal(expectedKeyForChild_0); + }); + + it('should mark address watched and get watched addresses', function () { + const key0 = derivableKeyChain.getForPath('m/0'); + derivableKeyChain.getForPath('m/0').isWatched = true + key0.isWatched = true; + const key1 = derivableKeyChain.getForPath('m/1', { isWatched: true }); + const key2 = derivableKeyChain.getForPath('m/2', { isWatched: true }); + + const watchedAddresses = derivableKeyChain.getWatchedAddresses(); + let expectedWatchedAddresses = [ + derivableKeyChain.getForPath('m/0').address.toString(), + derivableKeyChain.getForPath('m/1').address.toString(), + derivableKeyChain.getForPath('m/2').address.toString() + ]; + expect(watchedAddresses).to.deep.equal(expectedWatchedAddresses); + }); + it('should get watched addresses', function () { + const watchedAddresses = derivableKeyChain.getWatchedAddresses(); + const expectedWatchedAddresses = [ + 'ybQDfNwiDjk8ZH5UUmHQzAMEmjbrbK5dAj', + 'yhFX5rseJPitV45HUCaa9haeGHtLuooBaq', + 'yhqxsmYk6jfoGWf1hJKq7d4U2cGHCgzpFU' + ] + expect(watchedAddresses).to.deep.equal(expectedWatchedAddresses); + }); + // it('should get watched public keys', function () { + // const watchedPubKeys = derivableKeyChain.getWatchedPublicKeys(); + // const expectedWatchedPubKeys = [ + // '03e6ab8177a7ca2699da4f83ca3c27768fb88b70ae9d6bde1cba8de88355ccf199', + // '0246a870b65153b98e453ff08f7198d06bce2a790286f44d90929aceafafa0673f', + // '025ad16f78f67801e52abe5b512d66e3896d4c2fa3ca3150349437a5dd13519967' + // ] + // expect(watchedPubKeys).to.deep.equal(expectedWatchedPubKeys) + // }); + it('should remove an address from watched addresses', function () { + const data0 = derivableKeyChain.getForPath('m/0', { isWatched: false }); + const data1 = derivableKeyChain.getForPath('m/1'); + const data2 = derivableKeyChain.getForPath('m/2'); + data2.isWatched = false; + + expect(derivableKeyChain.getWatchedAddresses().length).to.equal(1); + }); + it('should get address for path', function (){ + const address0_1 = derivableKeyChain.getForPath('m/1').address; + expect(address0_1.toString()).to.equal('yhFX5rseJPitV45HUCaa9haeGHtLuooBaq') + }) + it('should mark address as used', function () { + const address0_0 = derivableKeyChain.getForPath('m/0').address; + derivableKeyChain.markAddressAsUsed(address0_0); + expect(derivableKeyChain.issuedPaths.get('m/0').isUsed).to.equal(true) + }); +}); +describe('DerivableKeyChain - HDPublicKey', function suite(){ + let hdpubDerivableKeyChain; + it('should initiate from a HDPublicKey', function () { + hdpubDerivableKeyChain = new DerivableKeyChain({ + HDPublicKey: new Dashcore.HDPublicKey(hdPublicKey), + network: 'testnet' + }); + // As the HDPublicKey starts with xpub, it's livenet and should take priority over our network being set. + expect(hdpubDerivableKeyChain.network.toString()).to.equal('livenet'); + expect(hdpubDerivableKeyChain.keyChainId).to.equal('kc5059442d66'); + expect(hdpubDerivableKeyChain.getRootKey().toString()).to.equal(hdPublicKey); + }); + it('should derivate', function () { + const key0_1 = hdpubDerivableKeyChain.getForPath('m/1').key; + expect(key0_1.publicKey.toAddress(hdpubDerivableKeyChain.network).toString()).to.equal('XoL5LcBiDWcj6L7fFwytsFoX5Vz7BVXw9w') + }); + it('should get address for path', function (){ + const address0_1 = hdpubDerivableKeyChain.getForPath('m/2').address; + expect(address0_1.toString()).to.equal('XwAzpxQKbgebaLiadq1c6rDeFJ4FKPUufy') + }) +}) +describe('DerivableKeyChain - single privateKey', function suite() { + this.timeout(10000); + it('should correctly errors out when not a HDPublicKey (privateKey)', () => { + const privateKey = Dashcore.PrivateKey().toString(); + const network = 'livenet'; + const pkDerivableKeyChain = new DerivableKeyChain({ privateKey, network }); + expect(pkDerivableKeyChain.network).to.equal(network); + expect(pkDerivableKeyChain.rootKeyType).to.equal('privateKey'); + expect(pkDerivableKeyChain.rootKey.toString()).to.equal(privateKey); + + const expectedException1 = 'Wallet is not loaded from a mnemonic or a HDPrivateKey, impossible to derivate keys for path m/0'; + expect(() => pkDerivableKeyChain.getForPath('m/0')).to.throw(expectedException1); + }); + it('should get private key', () => { + const privateKey = Dashcore.PrivateKey().toString(); + const pkDerivableKeyChain = new DerivableKeyChain({ privateKey, network: 'livenet' }); + expect(pkDerivableKeyChain.getRootKey().toString()).to.equal(privateKey); + expect(pkDerivableKeyChain.rootKey.toString()).to.equal(privateKey); + }); +}); diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getDIP15ExtendedKey.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getDIP15ExtendedKey.js new file mode 100644 index 00000000000..b65dc10d7da --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getDIP15ExtendedKey.js @@ -0,0 +1,26 @@ +/** + * Return the extended key of the relationship between two dashpay contacts. + * @param userUniqueId - Current userID + * @param contactUniqueId - Contact userID + * @param index - the key index. + * @param accountIndex[=0] - the internal wallet account from which derivation is done + * @param type {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys + * @return {HDPrivateKey|HDPublicKey} + */ +function getDIP15ExtendedKey(userUniqueId, contactUniqueId, index = 0, accountIndex = 0, type = 'HDPrivateKey') { + if (!['HDPrivateKey', 'HDPublicKey'].includes(this.rootKeyType)) { + throw new Error('Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate keys'); + } + if (!userUniqueId || !contactUniqueId) throw new Error('Required userUniqueId and contactUniqueId to be defined'); + + // Require a HDPrivateKey for hardened derivation + const extendedPrivateKey = this + .getHardenedDIP15AccountKey(accountIndex, 'HDPrivateKey') + .deriveChild((userUniqueId), true) + .deriveChild((contactUniqueId), true) + .deriveChild(index, false); + + return (type === 'HDPublicKey' ? extendedPrivateKey.hdPublicKey : extendedPrivateKey); +} + +module.exports = getDIP15ExtendedKey; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getFirstUnusedAddress.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getFirstUnusedAddress.js new file mode 100644 index 00000000000..ab7f3fefeeb --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getFirstUnusedAddress.js @@ -0,0 +1,12 @@ +function getFirstUnusedAddress() { + const allUnused = this.getIssuedPaths() + .filter((path) => path.isUsed === false); + + const firstUnused = allUnused.slice(0, 1)[0]; + + return { + path: firstUnused.path, + address: firstUnused.address.toString(), + }; +} +module.exports = getFirstUnusedAddress; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getForAddress.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getForAddress.js new file mode 100644 index 00000000000..4b8a01ddf04 --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getForAddress.js @@ -0,0 +1,12 @@ +function getForAddress(address) { + const searchResult = [...this.issuedPaths.entries()] + .find(([, el]) => el.address.toString() === address.toString()); + + if (!searchResult) { + return null; + } + const [path] = searchResult; + return this.getForPath(path); +} + +module.exports = getForAddress; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getForPath.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getForPath.js new file mode 100644 index 00000000000..571a307004e --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getForPath.js @@ -0,0 +1,42 @@ +const logger = require('../../../logger'); + +function getForPath(path, opts = {}) { + if (path === undefined) throw new Error('Expect a valid path to derivate'); + const stringifiedPath = path.toString(); + logger.silly(`KeyChain.getForPath(${stringifiedPath})`); + const isUsed = (opts && opts.isUsed !== undefined) ? opts.isUsed : false; + const isWatched = (opts && opts.isWatched !== undefined) ? opts.isWatched : false; + const isDerivable = ['HDPrivateKey', 'HDPublicKey'].includes(this.rootKeyType); + if (!isDerivable && stringifiedPath !== '0') { + throw new Error(`Wallet is not loaded from a mnemonic or a HDPrivateKey, impossible to derivate keys for path ${stringifiedPath}`); + } + + let data; + if (this.issuedPaths.has(stringifiedPath)) { + data = this.issuedPaths.get(stringifiedPath); + if (opts && opts.isWatched !== undefined && data.isWatched !== opts.isWatched) { + data.isWatched = opts.isWatched; + } + if (opts && opts.isUsed !== undefined && data.isUsed !== opts.isUsed) { + data.isUsed = opts.isUsed; + } + return data; + } + + const key = (isDerivable) ? this.rootKey.derive(stringifiedPath) : this.getRootKey(); + + data = { + path: stringifiedPath, + key, + isUsed, + isWatched, + address: this.rootKeyType === 'address' ? key : key.publicKey.toAddress(this.network), + issuedTime: +new Date(), + }; + + this.issuedPaths.set(stringifiedPath, data); + + return data; +} + +module.exports = getForPath; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedBIP44HDKey.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedBIP44HDKey.js new file mode 100644 index 00000000000..48da61c1616 --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedBIP44HDKey.js @@ -0,0 +1,11 @@ +const { BIP44_TESTNET_ROOT_PATH, BIP44_LIVENET_ROOT_PATH } = require('../../../CONSTANTS'); + +/** + * Return a safier root keys to derivate from + * @return {HDPrivateKey|HDPublicKey} + */ +function getHardenedBIP44HDKey() { + const pathRoot = (this.network.toString() === 'testnet') ? BIP44_TESTNET_ROOT_PATH : BIP44_LIVENET_ROOT_PATH; + return this.getForPath(pathRoot).key; +} +module.exports = getHardenedBIP44HDKey; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP15AccountKey.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP15AccountKey.js new file mode 100644 index 00000000000..c01f240fc85 --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP15AccountKey.js @@ -0,0 +1,14 @@ +/** + * Return a safier root path to derivate from + * @param {number} [accountIndex=0] - set the account index + * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys + * @return {HDPrivateKey|HDPublicKey} + */ +function getHardenedDIP15AccountKey(accountIndex = 0, type = 'HDPrivateKey') { + const hardenedFeatureRootKey = this.getHardenedDIP9FeatureHDKey(type); + + // Feature is set to 15' for all DashPay Incoming Funds derivation paths (see DIP15). + const featureKey = hardenedFeatureRootKey.deriveChild(15, true); + return featureKey.deriveChild(accountIndex, true); +} +module.exports = getHardenedDIP15AccountKey; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP9FeatureHDKey.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP9FeatureHDKey.js new file mode 100644 index 00000000000..422c9fab667 --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP9FeatureHDKey.js @@ -0,0 +1,11 @@ +const { DIP9_LIVENET_ROOT_PATH, DIP9_TESTNET_ROOT_PATH } = require('../../../CONSTANTS'); + +/** + * Return a safier root path to derivate from + * @return {HDPrivateKey|HDPublicKey} + */ +function getHardenedDIP9FeatureHDKey() { + const pathRoot = (this.network.toString() === 'testnet') ? DIP9_TESTNET_ROOT_PATH : DIP9_LIVENET_ROOT_PATH; + return this.getForPath(pathRoot).key; +} +module.exports = getHardenedDIP9FeatureHDKey; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getIssuedPaths.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getIssuedPaths.js new file mode 100644 index 00000000000..324750853ba --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getIssuedPaths.js @@ -0,0 +1,5 @@ +function getWatchedAddresses() { + return [...this.issuedPaths.values()]; +} + +module.exports = getWatchedAddresses; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getRootKey.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getRootKey.js new file mode 100644 index 00000000000..f2b54723a48 --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getRootKey.js @@ -0,0 +1,5 @@ +function getRootKey() { + return this.rootKey; +} + +module.exports = getRootKey; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getWatchedAddresses.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getWatchedAddresses.js new file mode 100644 index 00000000000..8154329119a --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getWatchedAddresses.js @@ -0,0 +1,7 @@ +function getWatchedAddresses() { + return [...this.issuedPaths.entries()] + .filter(([, el]) => el.isWatched === true) + .map(([, el]) => el.address.toString()); +} + +module.exports = getWatchedAddresses; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/markAddressAsUsed.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/markAddressAsUsed.js new file mode 100644 index 00000000000..cecf2922e1f --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/markAddressAsUsed.js @@ -0,0 +1,17 @@ +const logger = require('../../../logger'); + +function markAddressAsUsed(address) { + const searchResult = [...this.issuedPaths.entries()] + .find(([, el]) => el.address.toString() === address.toString()); + + if (searchResult) { + const [, addressData] = searchResult; + logger.silly(`KeyChain - Marking ${address} ${addressData.path} as used`); + addressData.isUsed = true; + + return this.maybeLookAhead(); + } + + return false; +} +module.exports = markAddressAsUsed; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/maybeLookAhead.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/maybeLookAhead.js new file mode 100644 index 00000000000..fd3841f40bd --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/maybeLookAhead.js @@ -0,0 +1,82 @@ +function maybeLookAhead() { + const { lookAheadOpts } = this; + const generatedPaths = []; + + if (Object.keys(lookAheadOpts.paths).length === 0) { + return generatedPaths; + } + + const usedPaths = [...this.issuedPaths.entries()] + .filter(([, el]) => el.isUsed === true) + .map(([path]) => path); + + const sortedUsedPathByBase = {}; + + usedPaths + .forEach((usedPath) => { + const splitted = usedPath.split('/'); + // Removes the index to sort which and how many base path has been generated + const basePath = splitted.splice(0, splitted.length - 1).join('/'); + if (!sortedUsedPathByBase[basePath]) sortedUsedPathByBase[basePath] = []; + sortedUsedPathByBase[basePath].push(usedPath); + }); + + const lastUsedIndexes = {}; + const lastGeneratedIndexes = {}; + + Object + .entries(lookAheadOpts.paths) + .forEach(([basePath]) => { + lastUsedIndexes[basePath] = -1; + lastGeneratedIndexes[basePath] = -1; + }); + + Object + .entries(sortedUsedPathByBase) + .forEach(([basePath, basePaths]) => { + // Sorting by index is also needed as the user might have manually issue a key + // and set it up to watched or used outside of lookAhead bounds + const sortedBasePaths = basePaths.sort((a, b) => a.split('/').splice(-1) - b.split('/').splice(-1)); + + sortedBasePaths.forEach((path) => { + const addressData = this.issuedPaths.get(path); + + const currentIndex = parseInt(path.split('/').splice(-1), 10); + + if (addressData.isUsed) { + lastUsedIndexes[basePath] = currentIndex; + } + + lastGeneratedIndexes[basePath] = currentIndex; + }); + }); + + const isWatched = lookAheadOpts.isWatched || false; + + Object + .entries(lastGeneratedIndexes) + .forEach(([basePath]) => { + const lastUsedAndLastGenGap = lastGeneratedIndexes[basePath] - lastUsedIndexes[basePath]; + const pathAmountToGenerate = lookAheadOpts.paths[basePath] - lastUsedAndLastGenGap; + + if (pathAmountToGenerate > 0) { + const lastIndex = lastGeneratedIndexes[basePath]; + const lastIndexToGenerate = lastIndex + pathAmountToGenerate; + + if (lastIndexToGenerate > lastIndex) { + for ( + let index = lastIndex + 1; + index <= lastIndexToGenerate; + index += 1) { + const timeNow = +new Date(); + const pathData = this.getForPath(`${basePath}/${index}`, { isWatched }); + if (pathData.issuedTime >= timeNow) { + generatedPaths.push(pathData); + } + } + } + } + }); + return generatedPaths; +} +module.exports = maybeLookAhead; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/sign.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/sign.js new file mode 100644 index 00000000000..b10b561f2c2 --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/sign.js @@ -0,0 +1,29 @@ +const { + crypto, Transaction, Message, +} = require('@dashevo/dashcore-lib'); + +/** + * Allow to sign any transaction or a transition object from a valid privateKeys list + * @param {Transaction|any} object + * @param {[PrivateKey]} privateKeys + * @param {number} [sigType=crypto.Signature.SIGHASH_ALL] + */ +function sign(object, privateKeys, sigType = crypto.Signature.SIGHASH_ALL) { + const handledTypes = [Transaction.name, Transaction.Payload.SubTxRegisterPayload, Message.name]; + if (!privateKeys) throw new Error('Require one or multiple privateKeys to sign'); + if (!object) throw new Error('Nothing to sign'); + if (!handledTypes.includes(object.constructor.name)) { + throw new Error(`Keychain sign : Unhandled object of type ${object.constructor.name}`); + } + const obj = object.sign(privateKeys, sigType); + + if (obj.isFullySigned && !obj.isFullySigned()) { + throw new Error('Not fully signed transaction'); + } + if (object.constructor.name === 'Message') { + // When signed, message are in string form. + return Message(obj); + } + return obj; +} +module.exports = sign; diff --git a/packages/wallet-lib/src/types/Identities/Identities.d.ts b/packages/wallet-lib/src/types/Identities/Identities.d.ts new file mode 100644 index 00000000000..9bd7a02bce9 --- /dev/null +++ b/packages/wallet-lib/src/types/Identities/Identities.d.ts @@ -0,0 +1,13 @@ +import { Wallet } from "../Wallet/Wallet"; +import { HDPrivateKey } from "@dashevo/dashcore-lib"; + +export declare class Identities { + constructor(wallet: Wallet); + + getIdentityHDKeyById(identityId: string, keyIndex: number): HDPrivateKey; + getIdentityHDKeyByIndex(identityIndex: number, keyIndex: number): HDPrivateKey; + getIdentityIds(): string[]; +} + +export declare namespace Identities { +} diff --git a/packages/wallet-lib/src/types/Identities/Identities.js b/packages/wallet-lib/src/types/Identities/Identities.js new file mode 100644 index 00000000000..f457fe9fc99 --- /dev/null +++ b/packages/wallet-lib/src/types/Identities/Identities.js @@ -0,0 +1,21 @@ +const _ = require('lodash'); +const Wallet = require('../Wallet/Wallet'); + +class Identities { + constructor(wallet) { + if (!wallet || wallet.constructor.name !== Wallet.name) throw new Error('Expected wallet to be passed as param'); + if (!_.has(wallet, 'walletId')) throw new Error('Missing walletID to create an account'); + + this.walletId = wallet.walletId; + + this.storage = wallet.storage; + + this.keyChain = wallet.keyChainStore.getMasterKeyChain(); + } +} + +Identities.prototype.getIdentityHDKeyById = require('./methods/getIdentityHDKeyById'); +Identities.prototype.getIdentityHDKeyByIndex = require('./methods/getIdentityHDKeyByIndex'); +Identities.prototype.getIdentityIds = require('./methods/getIdentityIds'); + +module.exports = Identities; diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js new file mode 100644 index 00000000000..79cd6eedc77 --- /dev/null +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js @@ -0,0 +1,20 @@ +/** + * + * @param {string} identityId + * @param {number} keyIndex + * @return {HDPrivateKey} + */ +function getIdentityHDKeyById(identityId, keyIndex) { + const identityIndex = this.storage + .getWalletStore(this.walletId) + .getIndexedIdentityIds() + .indexOf(identityId); + + if (identityIndex === -1) { + throw new Error(`Identity with ID ${identityId} is not associated with wallet, or it's not synced`); + } + + return this.getIdentityHDKeyByIndex(identityIndex, keyIndex); +} + +module.exports = getIdentityHDKeyById; diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js new file mode 100644 index 00000000000..87e60e4d58d --- /dev/null +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js @@ -0,0 +1,47 @@ +const { expect } = require('chai'); +const mockedStore = require('../../../../fixtures/sirentonight-fullstore-snapshot-1562711703'); +const getIdentityHDKeyById = require('./getIdentityHDKeyById'); + +let walletMock; +let fetchTransactionInfoCalledNb = 0; +let expectedKeyMock; +describe('Wallet#getIdentityHDKeyById', function suite() { + this.timeout(10000); + before(() => { + expectedKeyMock = "123"; + const walletStoreMock = { + getIndexedIdentityIds: () => mockedStore.wallets[Object.keys(mockedStore.wallets)].identityIds + } + + const storageMock = { + store: mockedStore, + getStore: () => mockedStore, + mappedAddress: {}, + getWalletStore: () => walletStoreMock, + }; + const walletId = Object.keys(mockedStore.wallets)[0]; + walletMock = { + walletId, + storage: storageMock, + transport: { + getTransaction: () => fetchTransactionInfoCalledNb += 1, + }, + getIdentityHDKeyByIndex: (identityIndex) => { + if (identityIndex === 0) { + return expectedKeyMock; + } + } + }; + }); + it('should filter empty indexes', async () => { + const key = await getIdentityHDKeyById.call(walletMock, "9Gk9T5mJY9j3dDX1D1tG5WYaV8g6zQTS2ocFFXe6NCrq"); + expect(key).to.deep.equal(expectedKeyMock); + }); + it('should throw an error if identity id was not found', async () => { + try { + await getIdentityHDKeyById.call(walletMock, 'randomstring'); + } catch (e) { + expect(e.message).to.be.equal('Identity with ID randomstring is not associated with wallet, or it\'s not synced') + } + }); +}); diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyByIndex.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyByIndex.js new file mode 100644 index 00000000000..aee9b0919ab --- /dev/null +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyByIndex.js @@ -0,0 +1,24 @@ +const ECDSA_KEY_TYPE = 0; +// const BLS_KEY_TYPE = 1; +/** + * Returns a private key for managing an identity + * @param {number} identityIndex - Identity index + * @param {number} keyIndex - keyIndex + * @return {HDPrivateKey} + */ +function getIdentityHDKeyByIndex(identityIndex, keyIndex) { + const { keyChain } = this; + const hardenedFeatureRootKey = keyChain.getHardenedDIP9FeatureHDKey('HDPrivateKey'); + + const identityFeatureKey = hardenedFeatureRootKey.deriveChild(5, true); + + // as defined in https://github.com/dashpay/dips/blob/master/dip-0013.md#identity-authentication-keys + const identitySubFeatureKey = identityFeatureKey.deriveChild(0, true); + + return identitySubFeatureKey + .deriveChild(ECDSA_KEY_TYPE, true) + .deriveChild(identityIndex, true) + .deriveChild(keyIndex, true); +} + +module.exports = getIdentityHDKeyByIndex; diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyByIndex.spec.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyByIndex.spec.js new file mode 100644 index 00000000000..e84db4788a3 --- /dev/null +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyByIndex.spec.js @@ -0,0 +1,51 @@ +const { expect } = require('chai'); + +const { Wallet, Identities } = require('../../../index'); + +let mnemonic; +let expectedIdentityHDKey0_0; +let expectedIdentityHDKey0_1; +let expectedIdentityHDKey1_0; +let expectedIdentityPrivateKey0_0; +let expectedIdentityPrivateKey0_1; +let expectedIdentityPrivateKey1_0; +let wallet; +let identities; +describe('Identities#getIdentityHDKeyByIndex', function suite() { + this.timeout(10000); + beforeEach(() => { + mnemonic = 'during develop before curtain hazard rare job language become verb message travel'; + + + expectedIdentityHDKey0_0 = 'tprv8nwXBDgtqkF6xZjxESRmMcmyo8LeJ7YnhEZNYrGBUVDtDbxdtjiQQ5pyVigvrep81EJWenD3BEdCV5Yrhah2tbnzjM5Dq9bnmDvX7yyRHRr'; + expectedIdentityHDKey0_1 = 'tprv8nwXBDgtqkF6z6Da9eSrw29t3qVcHqWTLzw5oFVzXnuxwhRF5RtMmc3LqGMD6NmShVUd4dkbs86PB4pZVQ7xWgg2BLK4Kqm7TDTct4YDifH'; + expectedIdentityHDKey1_0 = 'tprv8oNTEowGNFSSD6Ne3aR9hQXFT2hmvf4F9kgjbbrKmCyeBWbuH9an16tPtKrHtkbAyHofhfGa1Go6a4bZQukJ8qS657PJQEMg3Sq3Z22UnH6'; + + expectedIdentityPrivateKey0_0 = '6fcf62a14d7c452a77dee426a534b7c92cbb13a41c3b7f75700519e339ef09dc'; + expectedIdentityPrivateKey0_1 = '5e07be03de51b0c5f7af8d60074819e2cf4bdce8eb47e59c18295e151528390f'; + expectedIdentityPrivateKey1_0 = '276d1d2aa6df3c3b7d9da967641769eddd7e81055833b90a79cdb1b433dd18e5'; + wallet = new Wallet({ + offlineMode: true, + mnemonic, + }); + identities = new Identities(wallet); + }); + + afterEach(() => { + wallet.disconnect(); + }); + + it('Should derive a key for identity for a given index', () => { + const actualIdentityHDKey0_0 = identities.getIdentityHDKeyByIndex(0, 0); + const actualIdentityHDKey0_1 = identities.getIdentityHDKeyByIndex(0, 1); + const actualIdentityHDKey1_0 = identities.getIdentityHDKeyByIndex(1, 0); + + expect(actualIdentityHDKey0_0.toString()).to.be.equal(expectedIdentityHDKey0_0); + expect(actualIdentityHDKey0_1.toString()).to.be.equal(expectedIdentityHDKey0_1); + expect(actualIdentityHDKey1_0.toString()).to.be.equal(expectedIdentityHDKey1_0); + + expect(actualIdentityHDKey0_0.privateKey.toString()).to.be.equal(expectedIdentityPrivateKey0_0); + expect(actualIdentityHDKey0_1.privateKey.toString()).to.be.equal(expectedIdentityPrivateKey0_1); + expect(actualIdentityHDKey1_0.privateKey.toString()).to.be.equal(expectedIdentityPrivateKey1_0); + }); +}); diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.js new file mode 100644 index 00000000000..e72711adb42 --- /dev/null +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.js @@ -0,0 +1,12 @@ +/** + * + * @return {string[]} + */ +function getIdentityIds() { + return this.storage + .getWalletStore(this.walletId) + .getIndexedIdentityIds() + .filter(Boolean); +} + +module.exports = getIdentityIds; diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.spec.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.spec.js new file mode 100644 index 00000000000..6d0ee52d568 --- /dev/null +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.spec.js @@ -0,0 +1,38 @@ +const { expect } = require('chai'); +const mockedStore = require('../../../../fixtures/sirentonight-fullstore-snapshot-1562711703'); +const getIdentityIds = require('./getIdentityIds'); +const WalletStore = require("../../WalletStore/WalletStore"); + +let mockedWallet; +let fetchTransactionInfoCalledNb = 0; +describe('Wallet#getIdentityIds', function suite() { + this.timeout(10000); + before(() => { + const walletId = Object.keys(mockedStore.wallets)[0]; + const walletStore = new WalletStore(walletId) + const identityIds = mockedStore.wallets[walletId].identityIds; + identityIds.forEach((id, i) => { + walletStore.insertIdentityIdAtIndex(id, i) + }) + + const storageHDW = { + getWalletStore: () => walletStore + }; + + mockedWallet = { + walletId, + index: 0, + storage: storageHDW, + transport: { + getTransaction: () => fetchTransactionInfoCalledNb += 1, + }, + }; + }); + it('should filter empty indexes', async () => { + const identityIds = await getIdentityIds.call(mockedWallet); + expect(identityIds).to.deep.equal([ + "9Gk9T5mJY9j3dDX1D1tG5WYaV8g6zQTS2ocFFXe6NCrq", + "HZJywfYZ87fdJFLkp7wtnTfS29zpvR63f21gqaajLYx6" + ]); + }); +}); diff --git a/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.d.ts b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.d.ts new file mode 100644 index 00000000000..7d65d7ba31d --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.d.ts @@ -0,0 +1,20 @@ +import {keyChainId, DerivableKeyChain} from "../DerivableKeyChain/DerivableKeyChain"; + +export declare class KeyChainStore { + constructor(); + + keyChains: Map + masterKeyChainId: keyChainId | null; + + addKeyChain(keychain: DerivableKeyChain, opts?: addKeyChainParam): void; + getKeyChain(keychainId: keyChainId): DerivableKeyChain; + getKeyChains(): Array; + makeChildKeyChainStore(path: string, opts: DerivableKeyChain.IDerivableKeyChainOptions): KeyChainStore; + getMasterKeyChain(): DerivableKeyChain; +} + +export declare interface addKeyChainParam { + isMasterKeyChain?: boolean; +} + + diff --git a/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.js b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.js new file mode 100644 index 00000000000..c81c2c0ac2a --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.js @@ -0,0 +1,14 @@ +class KeyChainStore { + constructor() { + this.keyChains = new Map(); + this.masterKeyChainId = null; + } +} + +KeyChainStore.prototype.addKeyChain = require('./methods/addKeyChain'); +KeyChainStore.prototype.getKeyChain = require('./methods/getKeyChain'); +KeyChainStore.prototype.getKeyChains = require('./methods/getKeyChains'); +KeyChainStore.prototype.makeChildKeyChainStore = require('./methods/makeChildKeyChainStore'); +KeyChainStore.prototype.getMasterKeyChain = require('./methods/getMasterKeyChain'); + +module.exports = KeyChainStore; diff --git a/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.spec.js b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.spec.js new file mode 100644 index 00000000000..bfb74a6cd81 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.spec.js @@ -0,0 +1,47 @@ +const {HDPrivateKey} = require("@dashevo/dashcore-lib"); +const KeyChainsStore = require('./KeyChainStore'); +const DerivableKeyChain = require("../DerivableKeyChain/DerivableKeyChain"); +const { expect } = require('chai'); + +describe('KeyChainStore', function suite() { + let keyChainsStore; + let hdPrivateKey = new HDPrivateKey() + let hdPublicKey = new HDPrivateKey().hdPublicKey + let keyChain = new DerivableKeyChain({HDPrivateKey: hdPrivateKey}) + let keyChainPublic = new DerivableKeyChain({HDPublicKey: hdPublicKey}) + let walletKeyChain = new DerivableKeyChain({HDPrivateKey:new HDPrivateKey()}); + it('should create a KeyChainStore', () => { + keyChainsStore = new KeyChainsStore(); + expect(keyChainsStore).to.exist; + expect(keyChainsStore.keyChains).to.be.a('Map') + }); + it('should be able to add a keyChain', function () { + keyChainsStore.addKeyChain(keyChain) + expect(keyChainsStore.keyChains.has(keyChain.keyChainId)).to.equal(true); + keyChainsStore.addKeyChain(keyChainPublic) + expect(keyChainsStore.keyChains.has(keyChainPublic.keyChainId)).to.equal(true); + }); + it('should allow to specify a specific master keychain', function () { + keyChainsStore.addKeyChain(walletKeyChain, { isMasterKeyChain: true }); + expect(keyChainsStore.keyChains.has(walletKeyChain.keyChainId)).to.equal(true); + }); + it('should get all keyChains', function () { + const keyChains = keyChainsStore.getKeyChains() + expect(keyChains).to.deep.equal([keyChain, keyChainPublic, walletKeyChain]); + }); + it('should get a keychain by its ID', () => { + const requestedKeychain = keyChainsStore.getKeyChain(keyChainPublic.keyChainId); + expect(requestedKeychain).to.equal(keyChainPublic); + }) + it('should get a master keychain', function () { + const requestedWalletKeyChain = keyChainsStore.getMasterKeyChain(); + expect(requestedWalletKeyChain).to.equal(walletKeyChain); + }); + it('should make a child key chain store', function () { + const childKeyChainStore = keyChainsStore.makeChildKeyChainStore('m/0') + expect(childKeyChainStore).to.exist; + expect(childKeyChainStore.keyChains).to.be.a('Map') + expect(childKeyChainStore.getMasterKeyChain().rootKeyType).to.be.equal(HDPrivateKey.name) + }); +}); + diff --git a/packages/wallet-lib/src/types/KeyChainStore/methods/addKeyChain.js b/packages/wallet-lib/src/types/KeyChainStore/methods/addKeyChain.js new file mode 100644 index 00000000000..04e60a50739 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/methods/addKeyChain.js @@ -0,0 +1,15 @@ +function addKeyChain(keychain, opts = {}) { + if (this.keyChains.has(keychain.keyChainId)) { + throw new Error(`Trying to add already existing keyChain ${keychain.keyChainId}`); + } + + this.keyChains.set(keychain.keyChainId, keychain); + + if (opts) { + if (opts.isMasterKeyChain && !this.masterKeyChainId) { + this.masterKeyChainId = keychain.keyChainId; + } + } +} + +module.exports = addKeyChain; diff --git a/packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChain.js b/packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChain.js new file mode 100644 index 00000000000..d964ba395ee --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChain.js @@ -0,0 +1,5 @@ +function getKeyChain(keyChainId) { + return this.keyChains.get(keyChainId); +} + +module.exports = getKeyChain; diff --git a/packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChains.js b/packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChains.js new file mode 100644 index 00000000000..4e0e16160cd --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChains.js @@ -0,0 +1,5 @@ +function getKeyChains() { + return Array.from(this.keyChains.values()); +} + +module.exports = getKeyChains; diff --git a/packages/wallet-lib/src/types/KeyChainStore/methods/getMasterKeyChain.js b/packages/wallet-lib/src/types/KeyChainStore/methods/getMasterKeyChain.js new file mode 100644 index 00000000000..512d6a28133 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/methods/getMasterKeyChain.js @@ -0,0 +1,6 @@ +function getMasterKeyChain() { + const keyChainId = this.masterKeyChainId; + return this.keyChains.get(keyChainId); +} + +module.exports = getMasterKeyChain; diff --git a/packages/wallet-lib/src/types/KeyChainStore/methods/makeChildKeyChainStore.js b/packages/wallet-lib/src/types/KeyChainStore/methods/makeChildKeyChainStore.js new file mode 100644 index 00000000000..383baee3ce0 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/methods/makeChildKeyChainStore.js @@ -0,0 +1,19 @@ +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); +const logger = require('../../../logger'); + +function makeChildKeyChainStore(path, opts) { + logger.debug(`KeyChainStore - make a child keychainstore for ${path}`); + const masterKeyChain = this.getMasterKeyChain(); + if (!masterKeyChain) throw new Error('Requires a master keychain to be added first.'); + + const childKeyChainStore = new this.constructor(); + const keyChainOpts = { network: masterKeyChain.network, ...opts }; + + // Accessing the type from getKeyForPath would behave on browser differently due to mangling. + keyChainOpts[masterKeyChain.rootKeyType] = masterKeyChain.getForPath(path).key; + const childKeyChain = new DerivableKeyChain(keyChainOpts); + childKeyChainStore.addKeyChain(childKeyChain, { isMasterKeyChain: true }); + return childKeyChainStore; +} + +module.exports = makeChildKeyChainStore; diff --git a/packages/wallet-lib/src/types/Storage/Storage.js b/packages/wallet-lib/src/types/Storage/Storage.js new file mode 100644 index 00000000000..6736969ef9b --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/Storage.js @@ -0,0 +1,55 @@ +const EventEmitter = require('events'); +const { has } = require('lodash'); +const CONSTANTS = require('../../CONSTANTS'); + +const defaultOpts = { + rehydrate: true, + autosave: true, + autosaveIntervalTime: CONSTANTS.STORAGE.autosaveIntervalTime, + network: 'testnet', +}; + +/** +* Handle all the storage logic, it's a wrapper around the adapters +* So all the needed methods should be provided by the Storage class and the access to the adapter +* should be limited. +* */ +class Storage extends EventEmitter { + constructor(opts = {}) { + super(); + this.currentWalletId = ''; + this.currentNetwork = ''; + this.wallets = new Map(); + this.chains = new Map(); + this.application = { + blockHeight: 0, + }; + + this.rehydrate = has(opts, 'rehydrate') ? opts.rehydrate : defaultOpts.rehydrate; + this.autosave = has(opts, 'autosave') ? opts.autosave : defaultOpts.autosave; + this.autosaveIntervalTime = has(opts, 'autosaveIntervalTime') + ? opts.autosaveIntervalTime + : defaultOpts.autosaveIntervalTime; + + this.lastRehydrate = null; + this.lastSave = null; + this.lastModified = null; + this.configured = false; + } + + scheduleStateSave() { + this.lastModified = Date.now(); + } +} + +Storage.prototype.configure = require('./methods/configure'); +Storage.prototype.createChainStore = require('./methods/createChainStore'); +Storage.prototype.createWalletStore = require('./methods/createWalletStore'); +Storage.prototype.getChainStore = require('./methods/getChainStore'); +Storage.prototype.getWalletStore = require('./methods/getWalletStore'); +Storage.prototype.rehydrateState = require('./methods/rehydrateState'); +Storage.prototype.saveState = require('./methods/saveState'); +Storage.prototype.startWorker = require('./methods/startWorker'); +Storage.prototype.stopWorker = require('./methods/stopWorker'); + +module.exports = Storage; diff --git a/packages/wallet-lib/src/types/Storage/_configureAdapter.js b/packages/wallet-lib/src/types/Storage/_configureAdapter.js new file mode 100644 index 00000000000..98c5f639b5d --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/_configureAdapter.js @@ -0,0 +1,36 @@ +const { InvalidStorageAdapter } = require('../../errors'); + +module.exports = async function configureAdapter(argAdapter) { + let adapter; + if (!argAdapter) throw new Error('Expected an adapter to configure'); + const argAdapterContructorName = argAdapter.constructor.name; + + // In case of an adapter being a function, we assume it being a class non instanciated + if (argAdapterContructorName === 'Function') { + // eslint-disable-next-line new-cap + adapter = new argAdapter(); + if (adapter.config) { + try { + await adapter.config({ name: 'dashevo-wallet-lib' }); + } catch (e) { + throw new Error(`Tried to config the adapter. Failed with reason ${e.message}`); + } + } else if (adapter.createInstance) await adapter.createInstance({ name: 'dashevo-wallet-lib' }); + } else if (argAdapterContructorName === 'Object') { + if (argAdapter.createInstance) throw new Error('Adapter instance not created'); + adapter = argAdapter; + } else { + // Instance of specific class + adapter = argAdapter; + } + // Testing the storage + if (!adapter.getItem || !adapter.setItem) { + throw new InvalidStorageAdapter('expected getItem/setItem methods'); + } + try { + await adapter.getItem('dummy'); + } catch (e) { + throw new InvalidStorageAdapter(e.message); + } + return adapter; +}; diff --git a/packages/wallet-lib/src/types/Storage/_getDefaultAdapter.js b/packages/wallet-lib/src/types/Storage/_getDefaultAdapter.js new file mode 100644 index 00000000000..bfc4bfe7dbf --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/_getDefaultAdapter.js @@ -0,0 +1,21 @@ +const logger = require('../../logger'); +const InMem = require('../../adapters/InMem'); + +module.exports = async function getDefaultAdapter() { + const isBrowser = (typeof document !== 'undefined'); + // eslint-disable-next-line no-undef + const isReactNative = (typeof navigator !== 'undefined' && navigator.product === 'ReactNative'); + const isNode = !isBrowser && !isReactNative; + + if (isNode) { + logger.warn('Running on a NodeJS env without any specified adapter. Data will not persist.'); + return InMem; + } + if (isReactNative) { + logger.warn('Running on a React Native env without any specified adapter. Data will not persist.'); + return InMem; + } if (isBrowser) { + return InMem; + } + throw new Error('Undetected platform - No default adapter to persist data to.'); +}; diff --git a/packages/wallet-lib/src/types/Storage/methods/configure.js b/packages/wallet-lib/src/types/Storage/methods/configure.js new file mode 100644 index 00000000000..3dd4555491d --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/configure.js @@ -0,0 +1,46 @@ +const { has } = require('lodash'); +const InMem = require('../../../adapters/InMem'); +const configureAdapter = require('../_configureAdapter'); +const getDefaultAdapter = require('../_getDefaultAdapter'); +const { CONFIGURED } = require('../../../EVENTS'); +const logger = require('../../../logger'); +const CONSTANTS = require('../../../CONSTANTS'); + +/** + * To be called after instantialization as it contains all the async logic / test of adapters + * @param opts + * @return {Promise} + */ +module.exports = async function configure(opts = {}) { + this.rehydrate = has(opts, 'rehydrate') ? opts.rehydrate : this.rehydrate; + this.autosave = has(opts, 'autosave') ? opts.autosave : this.autosave; + this.adapter = await configureAdapter((opts.adapter) ? opts.adapter : await getDefaultAdapter()); + + const storage = await this.adapter.getItem(`wallet_${opts.walletId}`); + const storageVersion = storage && storage.version; + + if (!(this.adapter instanceof InMem) && storageVersion !== CONSTANTS.STORAGE.version) { + if (typeof version === 'number') { + logger.warn('Storage version mismatch, resyncing from start'); + } + + await this.adapter.setItem(`wallet_${opts.walletId}`, null); + } + + this.createWalletStore(opts.walletId); + this.createChainStore(opts.network); + + this.currentWalletId = opts.walletId; + this.currentNetwork = opts.network; + + if (this.rehydrate) { + await this.rehydrateState(); + } + + if (this.autosave) { + this.startWorker(); + } + + this.configured = true; + this.emit(CONFIGURED, { type: CONFIGURED, payload: null }); +}; diff --git a/packages/wallet-lib/src/types/Storage/methods/createChainStore.js b/packages/wallet-lib/src/types/Storage/methods/createChainStore.js new file mode 100644 index 00000000000..890ac45c070 --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/createChainStore.js @@ -0,0 +1,29 @@ +const ChainStore = require('../../ChainStore/ChainStore'); +const EVENTS = require('../../../EVENTS'); + +const EVENTS_TO_FORWARD = [ + EVENTS.FETCHED_CONFIRMED_TRANSACTION, + EVENTS.TX_METADATA, +]; + +/** + * Create when does not yet exist a chainStore + * @param network + * @return {boolean} + */ +const createChainStore = function createChain(network) { + if (!this.chains.has(network.toString())) { + const chainStore = new ChainStore(network.toString()); + this.chains.set(network.toString(), chainStore); + + EVENTS_TO_FORWARD.forEach((event) => { + chainStore.on(event, (data) => { + this.emit(event, { type: event, payload: data }); + }); + }); + + return true; + } + return false; +}; +module.exports = createChainStore; diff --git a/packages/wallet-lib/src/types/Storage/methods/createWalletStore.js b/packages/wallet-lib/src/types/Storage/methods/createWalletStore.js new file mode 100644 index 00000000000..b398746ac47 --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/createWalletStore.js @@ -0,0 +1,10 @@ +const WalletStore = require('../../WalletStore/WalletStore'); + +const createWalletStore = function createWallet(walletId = 'squawk7700') { + if (!this.wallets.has(walletId)) { + this.wallets.set(walletId, new WalletStore(walletId)); + return true; + } + return false; +}; +module.exports = createWalletStore; diff --git a/packages/wallet-lib/src/types/Storage/methods/getChainStore.js b/packages/wallet-lib/src/types/Storage/methods/getChainStore.js new file mode 100644 index 00000000000..5255eb71033 --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/getChainStore.js @@ -0,0 +1,4 @@ +function getChainStore(network) { + return this.chains.get(network); +} +module.exports = getChainStore; diff --git a/packages/wallet-lib/src/types/Storage/methods/getWalletStore.js b/packages/wallet-lib/src/types/Storage/methods/getWalletStore.js new file mode 100644 index 00000000000..3f43fc2b25e --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/getWalletStore.js @@ -0,0 +1,5 @@ +function getWalletStore(walletId) { + if (!this.wallets.has(walletId)) return null; + return this.wallets.get(walletId); +} +module.exports = getWalletStore; diff --git a/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js b/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js new file mode 100644 index 00000000000..3d8f4e12b8c --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js @@ -0,0 +1,54 @@ +const { hasMethod } = require('../../../utils'); + +const { REHYDRATE_STATE_FAILED, REHYDRATE_STATE_SUCCESS } = require('../../../EVENTS'); + +const logger = require('../../../logger'); + +/** + * Fetch the state from the persistence adapter + * @return {Promise} + */ +const rehydrateState = async function rehydrateState() { + if (this.rehydrate && this.lastRehydrate === null) { + try { + if (this.adapter && hasMethod(this.adapter, 'getItem')) { + const walletId = this.currentWalletId; + const storage = await this.adapter.getItem(`wallet_${walletId}`); + + if (storage) { + try { + const { chains } = storage; + + Object.keys(chains).forEach((chainNetwork) => { + const { chain, wallet } = storage.chains[chainNetwork]; + + const chainStore = this.getChainStore(chainNetwork); + + if (chainStore) { + chainStore.importState(chain); + } + + const walletStore = this.getWalletStore(walletId); + + if (walletStore) { + walletStore.importState(wallet); + } + }); + } catch (e) { + logger.error('Error importing persistent storage, resyncing from start', e); + + this.adapter.setItem(`wallet_${walletId}`, null); + } + } + } + + this.lastRehydrate = +new Date(); + this.emit(REHYDRATE_STATE_SUCCESS, { type: REHYDRATE_STATE_SUCCESS, payload: null }); + } catch (e) { + logger.error('Error rehydrating storage state', e); + this.emit(REHYDRATE_STATE_FAILED, { type: REHYDRATE_STATE_FAILED, payload: e }); + throw e; + } + } +}; +module.exports = rehydrateState; diff --git a/packages/wallet-lib/src/types/Storage/methods/saveState.js b/packages/wallet-lib/src/types/Storage/methods/saveState.js new file mode 100644 index 00000000000..b23131cfd55 --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/saveState.js @@ -0,0 +1,56 @@ +const { SAVE_STATE_SUCCESS, SAVE_STATE_FAILED } = require('../../../EVENTS'); +const CONSTANTS = require('../../../CONSTANTS'); + +/** + * Force persistence of the state to the adapter + * @return {Promise} + */ +const saveState = async function saveState() { + if (this.autosave && this.adapter && this.adapter.setItem) { + const self = this; + try { + const currentChainHeight = this.getChainStore(this.currentNetwork).state.blockHeight; + + const serializedWallets = [...self.wallets].reduce((acc, [walletId, walletStore]) => { + let walletStoreState; + if (walletId === this.currentWalletId) { + // For current wallet we need to take into account the current chain height + walletStoreState = walletStore.exportState(currentChainHeight); + } else { + // Others stay unaffected + walletStoreState = walletStore.exportState(); + } + + acc[walletId] = walletStoreState; + return acc; + }, {}); + + const serializedChains = [...self.chains].reduce((acc, [chainId, chainStore]) => { + acc[chainId] = chainStore.exportState(); + return acc; + }, {}); + + Object.keys(serializedWallets).forEach((walletId) => { + const storage = { version: CONSTANTS.STORAGE.version, chains: {} }; + const wallet = serializedWallets[walletId]; + + Object.keys(serializedChains).forEach((chainNetwork) => { + const chain = serializedChains[chainNetwork]; + + storage.chains[chainNetwork] = { chain, wallet }; + }); + + this.adapter.setItem(`wallet_${walletId}`, storage); + }); + + this.lastSave = +new Date(); + this.emit(SAVE_STATE_SUCCESS, { type: SAVE_STATE_SUCCESS, payload: this.lastSave }); + return true; + } catch (err) { + this.emit(SAVE_STATE_FAILED, { type: SAVE_STATE_FAILED, payload: err }); + throw err; + } + } + return false; +}; +module.exports = saveState; diff --git a/packages/wallet-lib/src/types/Storage/methods/startWorker.js b/packages/wallet-lib/src/types/Storage/methods/startWorker.js new file mode 100644 index 00000000000..4976c0ebc53 --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/startWorker.js @@ -0,0 +1,11 @@ +/** + * Allow to start the working interval (worker for saving state). + * @return {void} + */ +module.exports = function startWorker() { + this.interval = setInterval(() => { + if (this.lastModified > this.lastSave) { + this.saveState(); + } + }, this.autosaveIntervalTime); +}; diff --git a/packages/wallet-lib/src/types/Storage/methods/stopWorker.js b/packages/wallet-lib/src/types/Storage/methods/stopWorker.js new file mode 100644 index 00000000000..f0aa6690313 --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/stopWorker.js @@ -0,0 +1,9 @@ +/** + * Allow to clear the working interval (worker). + * @return {boolean} + */ +module.exports = function stopWorker() { + clearInterval(this.interval); + this.interval = null; + return true; +}; diff --git a/packages/wallet-lib/src/types/Wallet/.eslintrc b/packages/wallet-lib/src/types/Wallet/.eslintrc new file mode 100644 index 00000000000..a8022ef2b8a --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/.eslintrc @@ -0,0 +1,5 @@ +{ + "rules": { + "import/newline-after-import": "off" + } +} diff --git a/packages/wallet-lib/src/types/Wallet/ChainSyncMediator.js b/packages/wallet-lib/src/types/Wallet/ChainSyncMediator.js new file mode 100644 index 00000000000..b2f778b4507 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/ChainSyncMediator.js @@ -0,0 +1,43 @@ +/* eslint-disable no-underscore-dangle */ + +const STATES = { + OFFLINE: 'OFFLINE', + CHAIN_STATUS_SYNC: 'CHAIN_STATUS_SYNC', + HISTORICAL_SYNC: 'HISTORICAL_SYNC', + CONTINUOUS_SYNC: 'CONTINUOUS_SYNC', +}; + +/** + * The class responsible for communication of + * the chain sync state between plugins + * @class ChainSyncMediator + */ +class ChainSyncMediator { + constructor() { + this._state = STATES.OFFLINE; + } + + /** + * Changes the state of the chain sync + * @param {string} state + */ + set state(state) { + if (!STATES[state]) { + throw new Error('Invalid state'); + } + + this._state = state; + } + + /** + * Returns the current state of the chain sync + * @returns {string} + */ + get state() { + return this._state; + } +} + +ChainSyncMediator.STATES = STATES; + +module.exports = ChainSyncMediator; diff --git a/packages/wallet-lib/src/types/Wallet/Wallet.d.ts b/packages/wallet-lib/src/types/Wallet/Wallet.d.ts new file mode 100644 index 00000000000..600b63633e3 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/Wallet.d.ts @@ -0,0 +1,81 @@ +import { Mnemonic, PrivateKey, PublicKey, PublicAddress, Address, HDPublicKey, Network, Plugins } from "../types"; +import { Account } from "../Account/Account"; +import { Storage } from "../Storage/Storage"; +import { HDPrivateKey } from "@dashevo/dashcore-lib"; +import { Transport } from "../../transport/Transport"; + +export declare class Wallet { + offlineMode: boolean; + allowSensitiveOperations: boolean; + injectDefaultPlugins: boolean; + plugins: [Plugins]; + passphrase?: string; + transport: Transport; + network: Network; + walletId: string; + accounts: [undefined]; + storage: Storage; + store: Storage.store; + + constructor(opts:Wallet.IWalletOptions) + + createAccount(accOptions: Account.Options): Promise; + disconnect(): void; + exportWallet():Mnemonic["toString"]; + fromHDPrivateKey(privateKey: HDPrivateKey):void; + fromHDPublicKey(HDPublicKey:HDPublicKey):void; + fromMnemonic(mnemonic: Mnemonic):void; + fromPrivateKey(privateKey: PrivateKey):void; + fromSeed(seed:string):void; + generateNewWalletId():string; + getAccount(accOptions?: Account.Options): Promise; + sweepWallet(): Promise + + /** + * Warning: Storage dump may contain sensitive data. + * Please, do not share the output of this function for mainnet wallets. + * @param options + */ + dumpStorage(options?: { + log: boolean + }): string; +} + +declare interface DAPIClientOptions { + dapiAddressProvider?: any; + dapiAddresses?: Array; + seeds?: Array; + network?: string; + networkType?: string; + timeout?: number; + retries?: number; + baseBanTime?: number; +} + + +export declare namespace Wallet { + interface IWalletOptions { + offlineMode?: boolean; + debug?: boolean; + transport?: DAPIClientOptions | Transport; + network?: Network | string; + plugins?: undefined[]|[Plugins]; + passphrase?: string|null; + injectDefaultPlugins?: boolean; + allowSensitiveOperations?: boolean; + mnemonic?: Mnemonic | string | null; + seed?: Mnemonic | string; + privateKey?: PrivateKey | string; + HDPrivateKey?: HDPrivateKey | string; + HDPublicKey?: HDPublicKey | string; + publicKey?: PublicKey | string; + address?: Address | PublicAddress | string; + unsafeOptions?: IWalletUnsafeOptions; + waitForInstantLockTimeout?: number; + waitForTxMetadataTimeout?: number; + } + + interface IWalletUnsafeOptions { + skipSynchronizationBeforeHeight?: number; + } +} diff --git a/packages/wallet-lib/src/types/Wallet/Wallet.js b/packages/wallet-lib/src/types/Wallet/Wallet.js new file mode 100644 index 00000000000..8378baf87a6 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/Wallet.js @@ -0,0 +1,197 @@ +const { PrivateKey, Networks } = require('@dashevo/dashcore-lib'); + +const EventEmitter = require('events'); +const _ = require('lodash'); +const Storage = require('../Storage/Storage'); +const { + generateNewMnemonic, +} = require('../../utils'); + +const defaultOptions = { + debug: false, + offlineMode: false, + network: 'testnet', + plugins: [], + passphrase: null, + injectDefaultPlugins: true, + allowSensitiveOperations: false, + unsafeOptions: {}, + waitForInstantLockTimeout: 60000, + waitForTxMetadataTimeout: 540000, +}; + +const fromMnemonic = require('./methods/fromMnemonic'); +const fromPrivateKey = require('./methods/fromPrivateKey'); +const fromPublicKey = require('./methods/fromPublicKey'); +const fromAddress = require('./methods/fromAddress'); +const fromSeed = require('./methods/fromSeed'); +const fromHDPublicKey = require('./methods/fromHDPublicKey'); +const fromHDPrivateKey = require('./methods/fromHDPrivateKey'); +const generateNewWalletId = require('./methods/generateNewWalletId'); + +const createTransportFromOptions = require('../../transport/createTransportFromOptions'); +const ChainSyncMediator = require('./ChainSyncMediator'); + +/** + * Instantiate a basic Wallet object, + * A wallet is able to spawn up all preliminary steps toward the creation of a Account with + * it's own transactions + * + * A wallet can be of multiple types, which some method. + * Type are attributed in function of opts (mnemonic, seed,...) + * + * WALLET_TYPES : + * - address : opts.privateKey is provided. Allow to handle a single address object. + * - hdwallet : opts.mnemonic or opts.seed is provided. Handle a HD Wallet with it's account. + */ +class Wallet extends EventEmitter { + /** + * + * @param opts + */ + constructor(opts = defaultOptions) { + super(); + // Immediate prototype method-composition are used in order to give access in constructor. + Object.assign(Wallet.prototype, { + fromMnemonic, + fromSeed, + fromHDPrivateKey, + fromPrivateKey, + fromPublicKey, + fromAddress, + fromHDPublicKey, + generateNewWalletId, + }); + + this.passphrase = _.has(opts, 'passphrase') ? opts.passphrase : defaultOptions.passphrase; + this.offlineMode = _.has(opts, 'offlineMode') ? opts.offlineMode : defaultOptions.offlineMode; + this.debug = _.has(opts, 'debug') ? opts.debug : defaultOptions.debug; + this.allowSensitiveOperations = _.has(opts, 'allowSensitiveOperations') ? opts.allowSensitiveOperations : defaultOptions.allowSensitiveOperations; + this.injectDefaultPlugins = _.has(opts, 'injectDefaultPlugins') ? opts.injectDefaultPlugins : defaultOptions.injectDefaultPlugins; + this.unsafeOptions = _.has(opts, 'unsafeOptions') ? opts.unsafeOptions : defaultOptions.unsafeOptions; + this.waitForInstantLockTimeout = _.has(opts, 'waitForInstantLockTimeout') ? opts.waitForInstantLockTimeout : defaultOptions.waitForInstantLockTimeout; + this.waitForTxMetadataTimeout = _.has(opts, 'waitForTxMetadataTimeout') ? opts.waitForTxMetadataTimeout : defaultOptions.waitForTxMetadataTimeout; + + // Validate network + const networkName = _.has(opts, 'network') ? opts.network.toString() : defaultOptions.network; + const network = Networks.get(networkName); + + if (!network) { + throw new Error(`Invalid network: ${network}`); + } + + this.network = network.toString(); + + let createdFromNewMnemonic = false; + if ('mnemonic' in opts) { + let { mnemonic } = opts; + if (mnemonic === null) { + mnemonic = generateNewMnemonic(); + createdFromNewMnemonic = true; + } + this.fromMnemonic(mnemonic, this.network, this.passphrase); + } else if ('seed' in opts) { + this.fromSeed(opts.seed, this.network); + } else if ('HDPrivateKey' in opts) { + this.fromHDPrivateKey(opts.HDPrivateKey); + } else if ('privateKey' in opts) { + this.fromPrivateKey((opts.privateKey === null) + ? new PrivateKey(network).toString() + : opts.privateKey, this.network); + } else if ('publicKey' in opts) { + this.fromPublicKey(opts.publicKey, this.network); + } else if ('HDPublicKey' in opts) { + this.fromHDPublicKey(opts.HDPublicKey); + } else if ('address' in opts) { + this.fromAddress(opts.address, this.network); + } else { + this.fromMnemonic(generateNewMnemonic()); + createdFromNewMnemonic = true; + } + + // Notice : Most of the time, wallet id is deterministic + this.generateNewWalletId(); + + this.storage = new Storage({ + rehydrate: true, + autosave: true, + }); + + this.storage.application.network = this.network; + this.storage.configure({ + adapter: opts.adapter, + walletId: this.walletId, + network: this.network, + }); + + if (createdFromNewMnemonic) { + // As it is pretty complicated to pass any of wallet options + // to a specific plugin, using `store` as an options mediator + // is easier. + + this.storage.application.syncOptions = { + skipSynchronization: true, + }; + + if (this.unsafeOptions.skipSynchronizationBeforeHeight) { + throw new Error('"unsafeOptions.skipSynchronizationBeforeHeight" will have no effect because wallet has been' + + ' created from the new mnemonic'); + } + } else if (this.unsafeOptions.skipSynchronizationBeforeHeight) { + this.storage.application.syncOptions = { + skipSynchronizationBeforeHeight: this.unsafeOptions.skipSynchronizationBeforeHeight, + }; + } + + const plugins = opts.plugins || defaultOptions.plugins; + this.plugins = {}; + // eslint-disable-next-line no-return-assign + plugins.map((item) => this.plugins[item.name] = item); + + // Handle import of cache + if (opts.cache) { + if (opts.cache.transactions) { + this.storage.importTransactions(opts.cache.transactions); + } + if (opts.cache.addresses) { + this.storage.importAddresses(opts.cache.addresses, this.walletId); + } + } + + if (!this.offlineMode) { + if (opts.transport && opts.transport.network) { + throw new Error('Please use Wallet\'s "network" option'); + } + + if (!opts.transport) { + // eslint-disable-next-line no-param-reassign + opts.transport = {}; + } + + // eslint-disable-next-line no-param-reassign + opts.transport.network = this.network; + + this.transport = createTransportFromOptions(opts.transport); + } + + this.accounts = []; + this.interface = opts.interface; + // Suppressed global require to avoid cyclic dependencies + // eslint-disable-next-line global-require + const Identities = require('../Identities/Identities'); + this.identities = new Identities(this); + this.savedBackup = false; // TODO: When true, we delete mnemonic from internals + + this.chainSyncMediator = new ChainSyncMediator(); + } +} + +Wallet.prototype.createAccount = require('./methods/createAccount'); +Wallet.prototype.disconnect = require('./methods/disconnect'); +Wallet.prototype.getAccount = require('./methods/getAccount'); +Wallet.prototype.generateNewWalletId = generateNewWalletId; +Wallet.prototype.exportWallet = require('./methods/exportWallet'); +Wallet.prototype.sweepWallet = require('./methods/sweepWallet'); +Wallet.prototype.dumpStorage = require('./methods/dumpStorage'); + +module.exports = Wallet; diff --git a/packages/wallet-lib/src/types/Wallet/Wallet.spec.js b/packages/wallet-lib/src/types/Wallet/Wallet.spec.js new file mode 100644 index 00000000000..87f509728c5 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/Wallet.spec.js @@ -0,0 +1,250 @@ +const { expect } = require('chai'); +const Dashcore = require('@dashevo/dashcore-lib'); +const knifeMnemonic = require('../../../fixtures/knifeeasily'); +const gatherSailMnemonic = require('../../../fixtures/gathersail'); +const fluidMnemonic = require('../../../fixtures/fluidDepth'); +const cR4t6ePrivateKey = require('../../../fixtures/cR4t6e_pk'); +const { WALLET_TYPES } = require('../../CONSTANTS'); +const { Wallet } = require('../../index'); +const inMem = require('../../adapters/InMem'); +const fromHDPublicKey = require('./methods/fromHDPublicKey'); +const gatherSail = require('../../../fixtures/gathersail'); + +const mocks = { + adapter: inMem, + offlineMode: true, +}; +describe('Wallet - class', function suite() { + this.timeout(10000); + it('should create a wallet without parameters', () => { + const wallet1 = new Wallet(mocks); + expect(wallet1.walletType).to.be.equal(WALLET_TYPES.HDWALLET); + expect(Dashcore.Mnemonic(wallet1.mnemonic).toString()).to.be.equal(wallet1.mnemonic); + + expect(wallet1.plugins).to.be.deep.equal({}); + expect(wallet1.accounts).to.be.deep.equal([]); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPrivateKey'); + expect(wallet1.passphrase).to.be.deep.equal(null); + expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); + expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); + expect(wallet1.walletId).to.length(10); + expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); + + const wallet2 = new Wallet(mocks); + expect(wallet2.walletType).to.be.equal(WALLET_TYPES.HDWALLET); + expect(Dashcore.Mnemonic(wallet2.mnemonic).toString()).to.be.equal(wallet2.mnemonic); + expect(wallet2.mnemonic).to.be.not.equal(wallet1.mnemonic); + expect(wallet2.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); + wallet1.storage.on('CONFIGURED', () => { + wallet1.disconnect(); + }); + wallet2.storage.on('CONFIGURED', () => { + wallet2.disconnect(); + }); + }); + it('should create a wallet with mnemonic', () => { + const wallet1 = new Wallet({ mnemonic: knifeMnemonic.mnemonic, ...mocks }); + expect(wallet1.walletType).to.be.equal(WALLET_TYPES.HDWALLET); + expect(Dashcore.Mnemonic(wallet1.mnemonic).toString()).to.be.equal(wallet1.mnemonic); + + expect(wallet1.plugins).to.be.deep.equal({}); + expect(wallet1.accounts).to.be.deep.equal([]); + expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPrivateKey'); + expect(wallet1.passphrase).to.be.deep.equal(null); + expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); + expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); + expect(wallet1.walletId).to.be.equal(knifeMnemonic.walletIdTestnet); + + const opts2 = { mnemonic: knifeMnemonic.mnemonic, network: 'livenet', ...mocks }; + const wallet2 = new Wallet(opts2); + expect(wallet2.walletType).to.be.equal(WALLET_TYPES.HDWALLET); + expect(wallet2.network).to.be.deep.equal(Dashcore.Networks.mainnet.toString()); + expect(Dashcore.Mnemonic(wallet2.mnemonic).toString()).to.be.equal(wallet2.mnemonic); + expect(wallet2.walletId).to.be.equal(knifeMnemonic.walletIdMainnet); + wallet1.storage.on('CONFIGURED', () => { + wallet1.disconnect(); + }); + wallet2.storage.on('CONFIGURED', () => { + wallet2.disconnect(); + }); + }); + it('should create a wallet with HDPrivateKey', () => { + const wallet1 = new Wallet({ HDPrivateKey: knifeMnemonic.HDRootPrivateKeyTestnet, network: 'testnet', ...mocks }); + expect(wallet1.walletType).to.be.equal(WALLET_TYPES.HDWALLET); + expect(wallet1.mnemonic).to.be.equal(null); + + expect(wallet1.plugins).to.be.deep.equal({}); + expect(wallet1.accounts).to.be.deep.equal([]); + expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPrivateKey'); + expect(wallet1.passphrase).to.be.deep.equal(null); + expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); + expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); + expect(wallet1.walletId).to.be.equal(knifeMnemonic.walletIdTestnet); + wallet1.storage.on('CONFIGURED', () => { + wallet1.disconnect(); + }); + }); + + it('should create a wallet with HDPublicKey', () => { + const wallet1 = new Wallet({ HDPublicKey: gatherSailMnemonic.testnet.external.hdpubkey, network: 'testnet', ...mocks }); + expect(wallet1.walletType).to.be.equal(WALLET_TYPES.HDPUBLIC); + expect(wallet1.mnemonic).to.be.equal(null); + + expect(wallet1.plugins).to.be.deep.equal({}); + expect(wallet1.accounts).to.be.deep.equal([]); + expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPublicKey'); + expect(wallet1.passphrase).to.be.deep.equal(null); + expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); + expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); + expect(wallet1.walletId).to.be.equal(gatherSailMnemonic.testnet.external.walletId); + wallet1.storage.on('CONFIGURED', () => { + wallet1.disconnect(); + }); + }); + it('should create a wallet with PrivateKey', () => { + const wallet1 = new Wallet({ privateKey: cR4t6ePrivateKey.privateKey, network: 'testnet', ...mocks }); + expect(wallet1.walletType).to.be.equal(WALLET_TYPES.PRIVATEKEY); + expect(wallet1.mnemonic).to.be.equal(null); + + expect(wallet1.plugins).to.be.deep.equal({}); + expect(wallet1.accounts).to.be.deep.equal([]); + expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('privateKey'); + expect(wallet1.passphrase).to.be.deep.equal(null); + expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); + expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); + expect(wallet1.walletId).to.be.equal(cR4t6ePrivateKey.walletIdTestnet); + + wallet1.storage.on('CONFIGURED', () => { + wallet1.disconnect(); + }); + }); + it('should create a wallet with PublicKey', () => { + const publicKey = new Dashcore.PrivateKey(cR4t6ePrivateKey.privateKey).toPublicKey(); + expect(publicKey.toString()).to.equal('03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844'); + const wallet1 = new Wallet({ publicKey: publicKey.toString(), network: 'testnet', ...mocks }); + expect(wallet1.walletType).to.be.equal(WALLET_TYPES.PUBLICKEY); + expect(wallet1.mnemonic).to.be.equal(null); + + expect(wallet1.plugins).to.be.deep.equal({}); + expect(wallet1.accounts).to.be.deep.equal([]); + expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('publicKey'); + expect(wallet1.passphrase).to.be.deep.equal(null); + expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); + expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); + expect(wallet1.walletId).to.be.equal('9f1f6f37f7'); + + wallet1.storage.on('CONFIGURED', () => { + wallet1.disconnect(); + }); + + const wallet2 = new Wallet({ publicKey, network: 'testnet', ...mocks }); + expect(wallet2.walletType).to.be.equal(WALLET_TYPES.PUBLICKEY); + expect(wallet2.mnemonic).to.be.equal(null); + + expect(wallet2.plugins).to.be.deep.equal({}); + expect(wallet2.accounts).to.be.deep.equal([]); + expect(wallet2.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); + expect(wallet2.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('publicKey'); + expect(wallet2.passphrase).to.be.deep.equal(null); + expect(wallet2.allowSensitiveOperations).to.be.deep.equal(false); + expect(wallet2.injectDefaultPlugins).to.be.deep.equal(true); + expect(wallet2.walletId).to.be.equal('9f1f6f37f7'); + + wallet2.storage.on('CONFIGURED', () => { + wallet2.disconnect(); + }); + }); + it('should have an offline Mode', () => { + const wallet = new Wallet({ + offlineMode: true, privateKey: cR4t6ePrivateKey.privateKey, network: 'testnet', ...mocks, + }); + expect(wallet.offlineMode).to.equal(true); + wallet.storage.on('CONFIGURED', () => { + wallet.disconnect(); + }); + }); +}); +describe('Wallet - Get/Create Account', function suite() { + this.timeout(10000); + const wallet1 = new Wallet({ mnemonic: fluidMnemonic.mnemonic, ...mocks }); + + it('should be able to create/get a wallet', async () => { + const acc1 = await wallet1.createAccount({ injectDefaultPlugins: false }); + const acc2 = await wallet1.createAccount({ injectDefaultPlugins: false }); + + [acc1, acc2].forEach((el, i) => { + // eslint-disable-next-line no-unused-expressions + expect(el).to.exist; + expect(el).to.be.a('object'); + expect(el.constructor.name).to.equal('Account'); + expect(el.BIP44PATH).to.equal(`m/44'/1'/${i}'`); + }); + acc1.disconnect(); + acc2.disconnect(); + }); + it('should get an account in a wallet', async () => { + const acc1 = await wallet1.getAccount({ index: 0 }); + const acc2 = await wallet1.getAccount({ index: 1 }); + + expect(acc1).to.be.deep.equal(await wallet1.getAccount()); + + [acc1, acc2].forEach((el, i) => { + // eslint-disable-next-line no-unused-expressions + expect(el).to.exist; + expect(el).to.be.a('object'); + expect(el.constructor.name).to.equal('Account'); + expect(el.BIP44PATH).to.equal(`m/44'/1'/${i}'`); + }); + wallet1.storage.on('CONFIGURED', () => { + wallet1.disconnect(); + }); + }); + it('should encrypt wallet with a passphrase', () => { + const network = Dashcore.Networks.testnet.toString(); + const passphrase = 'Evolution'; + const config = { + mnemonic: fluidMnemonic.mnemonic, + passphrase, + network, + }; + const walletTestnet = new Wallet(Object.assign(config, mocks)); + const encryptedHDPriv = walletTestnet.exportWallet('HDPrivateKey'); + const expectedHDPriv = 'tprv8ZgxMBicQKsPcuZMDBeTL2qaBF7gyUPt2wbqbJG2yp8s7yzRE1cRcjRnG3Xmdv3sELwtLGz186VX3EeHQ5we1xr1qH95QN6FRopP6FZqBUJ'; + expect(encryptedHDPriv.toString()).to.equal(expectedHDPriv); + walletTestnet.storage.on('CONFIGURED', () => { + walletTestnet.disconnect(); + }); + }); + it('should be able to create an account at a specific index', async () => { + const network = Dashcore.Networks.testnet.toString(); + const passphrase = 'Evolution'; + const config = { + mnemonic: fluidMnemonic.mnemonic, + passphrase, + network, + }; + const walletTestnet = new Wallet(Object.assign(config, mocks)); + + const account = await walletTestnet.createAccount(); + + // eslint-disable-next-line no-unused-expressions + expect(account).to.exist; + expect(account.BIP44PATH.split('/')[3]).to.equal('0\''); + expect(account.index).to.equal(0); + + const accountSpecificIndex = await walletTestnet.createAccount({ index: 42 }); + + expect(accountSpecificIndex.BIP44PATH.split('/')[3]).to.equal('42\''); + expect(accountSpecificIndex.index).to.equal(42); + walletTestnet.disconnect(); + }); + it('should not leak', () => { + const mockOpts1 = { }; + fromHDPublicKey.call(mockOpts1, gatherSail.testnet.external.hdpubkey); + }); +}); diff --git a/packages/wallet-lib/src/types/Wallet/methods/createAccount.js b/packages/wallet-lib/src/types/Wallet/methods/createAccount.js new file mode 100644 index 00000000000..8710c43fda8 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/createAccount.js @@ -0,0 +1,39 @@ +const { WALLET_TYPES } = require('../../../CONSTANTS'); +const EVENTS = require('../../../EVENTS'); +/** + * Will derivate to a new account. + * @param {object} accountOpts - options to pass, will autopopulate some + * @return {Account} - account object + */ +async function createAccount(accountOpts) { + if (!this.storage.configured) { + await new Promise((resolve) => this.storage.once(EVENTS.CONFIGURED, resolve)); + } + + /** + * Wallet.createAccount calls Account that depends on Wallet. + * In order to avoid a cyclic dependency issue we put this require here and + * disable eslint global require for next line + */ + // eslint-disable-next-line global-require + const Account = require('../../Account/Account'); + + const { + injectDefaultPlugins, debug, plugins, allowSensitiveOperations, + } = this; + const baseOpts = { + injectDefaultPlugins, debug, allowSensitiveOperations, plugins, + }; + if (this.walletType === WALLET_TYPES.SINGLE_ADDRESS) { baseOpts.privateKey = this.privateKey; } + const opts = Object.assign(baseOpts, accountOpts); + + const account = new Account(this, opts); + try { + await account.init(this); + return account; + } catch (e) { + await account.disconnect(); + throw e; + } +} +module.exports = createAccount; diff --git a/packages/wallet-lib/src/types/Wallet/methods/createAccount.spec.js b/packages/wallet-lib/src/types/Wallet/methods/createAccount.spec.js new file mode 100644 index 00000000000..7c41ff641ef --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/createAccount.spec.js @@ -0,0 +1,66 @@ +const { expect } = require('chai'); +const createAccount = require('./createAccount'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); + +const exceptedException1 = 'getAccount expected index integer to be a property of accountOptions'; + + +// describe('Wallet - getAccount', () => { +// it('should warn on trying to pass arg as number', () => { +// let timesCreateAccountCalled = 0; +// let timesAttachEventsCalled = 0; +// const mockOpts = { +// accounts: [], +// storage: {}, +// walletType: WALLET_TYPES.HDWALLET, +// createAccount: (opts = { index: 0 }) => { +// timesCreateAccountCalled += 1; +// return { +// index: opts.index, +// storage: { +// attachEvents: () => timesAttachEventsCalled += 1, +// }, +// }; +// }, +// }; +// expect(() => getAccount.call(mockOpts, 0)).to.throw(exceptedException1); +// expect(timesCreateAccountCalled).to.equal(0); +// expect(timesAttachEventsCalled).to.equal(0); +// }); +// it('should create an account when not existing and get it back', () => { +// let timesCreateAccountCalled = 0; +// let timesAttachEventsCalled = 0; +// const mockOpts1 = { +// accounts: [], +// storage: {}, +// walletType: WALLET_TYPES.HDWALLET, +// createAccount: (opts = { index: 0 }) => { +// timesCreateAccountCalled += 1; +// const acc = { +// index: opts.index, +// storage: { +// attachEvents: () => timesAttachEventsCalled += 1, +// }, +// }; +// // This is actually done by Account class +// mockOpts1.accounts.push(acc); +// return acc; +// }, +// }; +// +// const acc = getAccount.call(mockOpts1); +// expect(acc.index).to.equal(0); +// expect(timesCreateAccountCalled).to.equal(1); +// expect(timesAttachEventsCalled).to.equal(1); +// const acc2 = getAccount.call(mockOpts1, { index: 0 }); +// expect(acc2.index).to.equal(0); +// expect(timesCreateAccountCalled).to.equal(1); +// expect(timesAttachEventsCalled).to.equal(2); +// expect(acc2).to.deep.equal(acc); +// +// const acc3 = getAccount.call(mockOpts1, { index: 1 }); +// expect(acc3.index).to.equal(1); +// expect(timesCreateAccountCalled).to.equal(2); +// expect(timesAttachEventsCalled).to.equal(3); +// }); +// }); diff --git a/packages/wallet-lib/src/types/Wallet/methods/disconnect.js b/packages/wallet-lib/src/types/Wallet/methods/disconnect.js new file mode 100644 index 00000000000..c0a042b4b2a --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/disconnect.js @@ -0,0 +1,17 @@ +/** + * Disconnect all the storage worker and process all account to disconnect their endpoint too. + */ +async function disconnect() { + if (this.storage) { + await this.storage.stopWorker(); + } + if (this.accounts) { + const accountPath = Object.keys(this.accounts); + // eslint-disable-next-line guard-for-in,no-restricted-syntax + for (const path in accountPath) { + // eslint-disable-next-line no-await-in-loop + await this.accounts[path].disconnect(); + } + } +} +module.exports = disconnect; diff --git a/packages/wallet-lib/src/types/Wallet/methods/dumpStorage.js b/packages/wallet-lib/src/types/Wallet/methods/dumpStorage.js new file mode 100644 index 00000000000..ac141828187 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/dumpStorage.js @@ -0,0 +1,53 @@ +const logger = require('../../../logger'); + +const defaultOptions = { + log: false, +}; + +/** + * Dumps storage on user's demand + * + * @param options - dumping options + * @return {string} - Returns JSON string of the wallet store + */ +function dumpStorage(options) { + const dumpOptions = options !== null && typeof options === 'object' + ? Object.assign(defaultOptions, options) + : defaultOptions; + + const storage = { chains: {}, wallets: {} }; + + this.storage.wallets.forEach((wallet) => { + storage.wallets[wallet.walletId] = wallet.state; + }); + + this.storage.chains.forEach((chain) => { + storage.chains[chain.network] = chain.state; + }); + + const storageDump = JSON.stringify(storage, (jsonKey, jsonValue) => { + if (jsonValue instanceof Map) { + const object = {}; + + // eslint-disable-next-line no-restricted-syntax + for (const [key, value] of jsonValue.entries()) { + object[key] = value; + } + + return object; + } + + return jsonValue; + }); + + if (dumpOptions.log) { + // Add a linebreak to the log message for the ease of copying of the + // truncated log from the browser consoles + // (the text from the buffer then can be directly pasted to the JSON parser) + logger.info('Dumping wallet storage\n', storageDump); + } + + return storageDump; +} + +module.exports = dumpStorage; diff --git a/packages/wallet-lib/src/types/Wallet/methods/exportWallet.js b/packages/wallet-lib/src/types/Wallet/methods/exportWallet.js new file mode 100644 index 00000000000..39cc12bc41f --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/exportWallet.js @@ -0,0 +1,96 @@ +const { WALLET_TYPES } = require('../../../CONSTANTS'); + +function exportMnemonic(mnemonic) { + if (!mnemonic) throw new Error('Wallet was not initiated with a mnemonic, can\'t export it'); + return mnemonic.toString(); +} + +function exportPublicKeyWallet(outputType = 'publicKey') { + switch (outputType) { + case 'publicKey': + if (!this.publicKey) throw new Error('No PublicKey to export'); + return this.publicKey.toString(); + default: + throw new Error(`Tried to export to invalid output : ${outputType}`); + } +} + +function exportAddressWallet(outputType = 'address') { + switch (outputType) { + case 'address': + if (!this.address) throw new Error('No Address to export'); + return this.address.toString(); + default: + throw new Error(`Tried to export to invalid output : ${outputType}`); + } +} + +function exportPrivateKeyWallet(outputType = 'privateKey') { + switch (outputType) { + case 'privateKey': + if (!this.privateKey) throw new Error('No PrivateKey to export'); + return this.privateKey.toString(); + default: + throw new Error(`Tried to export to invalid output : ${outputType}`); + } +} + +function exportHDWallet(outputType) { + switch (outputType) { + case undefined: + // We did not define any output, so we try first mnemonic, or HDPrivateKey + try { + return exportHDWallet.call(this, 'mnemonic'); + } catch (e) { + return exportHDWallet.call(this, 'HDPrivateKey'); + } + case 'mnemonic': + if (!this.mnemonic) throw new Error('Wallet was not initiated with a mnemonic, can\'t export it.'); + return exportMnemonic(this.mnemonic); + case 'HDPrivateKey': + if (!this.HDPrivateKey) throw new Error('No PrivateKey to export'); + return this.HDPrivateKey.toString(); + default: + throw new Error(`Tried to export to invalid output : ${outputType}`); + } +} + +function exportHDPublicWallet(outputType = 'HDPublicKey') { + switch (outputType) { + case 'HDPublicKey': + if (!this.HDPublicKey) throw new Error('No publicKey to export'); + return this.HDPublicKey.toString(); + default: + throw new Error(`Tried to export to invalid output : ${outputType}`); + } +} + +/** + * Allow to export the wallet (mnemonic). + * The default output differs from the wallet type. + * For an HDWallet, it will be it's mnemonic. + * For an HDPublic wallet (watch), it's will be that HDPubKey. + * If initiated from a private key, we output that key, similarly + * if initiated from a public key. + * On the case it's initiated from an address, we output it. + * + * @param outputType - Allow to overwrite the default output type + * @return {Mnemonic|HDPrivateKey} + */ +module.exports = function exportWallet(outputType) { + switch (this.walletType) { + case WALLET_TYPES.PRIVATEKEY: + case WALLET_TYPES.SINGLE_ADDRESS: + return exportPrivateKeyWallet.call(this, outputType); + case WALLET_TYPES.ADDRESS: + return exportAddressWallet.call(this, outputType); + case WALLET_TYPES.PUBLICKEY: + return exportPublicKeyWallet.call(this, outputType); + case WALLET_TYPES.HDPUBLIC: + return exportHDPublicWallet.call(this, outputType); + case WALLET_TYPES.HDWALLET: + return exportHDWallet.call(this, outputType); + default: + throw new Error('Trying to export from an unknown wallet type'); + } +}; diff --git a/packages/wallet-lib/src/types/Wallet/methods/exportWallet.spec.js b/packages/wallet-lib/src/types/Wallet/methods/exportWallet.spec.js new file mode 100644 index 00000000000..b0af3f6c3a5 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/exportWallet.spec.js @@ -0,0 +1,142 @@ +const { expect } = require('chai'); +const Wallet = require('../Wallet'); +const { PrivateKey, Networks } = require('@dashevo/dashcore-lib'); +const exportWallet = require('./exportWallet'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); +const cR4t6ePrivateKey = require('../../../../fixtures/cR4t6e_pk'); +const knifeMnemonic = require('../../../../fixtures/knifeeasily'); +const cR4t6eFixture = require("../../../../fixtures/cR4t6e_pk"); +const cR4t6ePublicKey = new PrivateKey(cR4t6eFixture.privateKey).toPublicKey(); + +describe('Wallet - export Wallet', function suite() { + this.timeout(10000); + it('should indicate on missing data', () => { + const mockOpts1 = { }; + const mockOpts2 = { walletType: WALLET_TYPES.PRIVATEKEY }; + const mockOpts3 = { walletType: WALLET_TYPES.HDWALLET }; + + const exceptedException1 = 'Trying to export from an unknown wallet type'; + const exceptedException2 = 'No PrivateKey to export'; + const exceptedException3 = 'Wallet was not initiated with a mnemonic, can\'t export it.'; + expect(() => exportWallet.call(mockOpts1)).to.throw(exceptedException1); + expect(() => exportWallet.call(mockOpts2)).to.throw(exceptedException2); + expect(() => exportWallet.call(mockOpts3)).to.throw(exceptedException2); + expect(() => exportWallet.call(mockOpts3, 'mnemonic')).to.throw(exceptedException3); + }); + it('should export a privateKey', () => { + const mockOpts1 = { + walletType: WALLET_TYPES.SINGLE_ADDRESS, + privateKey: cR4t6ePrivateKey.privateKey, + }; + const mockOpts2 = { + walletType: WALLET_TYPES.HDWALLET, + mnemonic: knifeMnemonic.mnemonic, + }; + const mockOpts3 = { + walletType: WALLET_TYPES.HDWALLET, + HDPrivateKey: knifeMnemonic.HDRootPrivateKeyMainnet, + }; + expect(exportWallet.call(mockOpts1)).to.equal(cR4t6ePrivateKey.privateKey); + expect(exportWallet.call(mockOpts2)).to.equal(knifeMnemonic.mnemonic); + expect(exportWallet.call(mockOpts3)).to.equal(knifeMnemonic.HDRootPrivateKeyMainnet); + }); +}); +describe('Wallet - exportWallet - integration', function suite() { + this.timeout(10000); + describe('fromMnemonic', () => { + const wallet = new Wallet({ + offlineMode: true, + mnemonic: knifeMnemonic.mnemonic, + }); + it('should work as expected', () => { + const exceptedException = 'Tried to export to invalid output : seed'; + expect(wallet.exportWallet()).to.equal(knifeMnemonic.mnemonic); + expect(wallet.exportWallet('mnemonic')).to.equal(knifeMnemonic.mnemonic); + expect(wallet.exportWallet('HDPrivateKey')).to.equal(knifeMnemonic.HDRootPrivateKeyTestnet); + expect(() => wallet.exportWallet('seed')).to.throw(exceptedException); + }); + after(() => { + wallet.disconnect(); + }); + }); + describe('fromSeed', () => { + const wallet = new Wallet({ + offlineMode: true, + seed: knifeMnemonic.seed, + }); + it('should work as expected', () => { + const exceptedException = "Wallet was not initiated with a mnemonic, can't export it."; + const exceptedException2 = 'Tried to export to invalid output : seed'; + + expect(wallet.exportWallet()).to.equal(knifeMnemonic.HDRootPrivateKeyTestnet); + expect(() => wallet.exportWallet('mnemonic')).to.throw(exceptedException); + expect(() => wallet.exportWallet('seed')).to.throw(exceptedException2); + expect(wallet.exportWallet('HDPrivateKey')).to.equal(knifeMnemonic.HDRootPrivateKeyTestnet); + }); + after(() => { + wallet.disconnect(); + }); + }); + describe('fromHDPrivateKey', () => { + const wallet = new Wallet({ + offlineMode: true, + HDPrivateKey: knifeMnemonic.HDRootPrivateKeyTestnet, + }); + it('should work as expected', () => { + const exceptedException = "Wallet was not initiated with a mnemonic, can't export it."; + const exceptedException2 = 'Tried to export to invalid output : seed'; + + expect(wallet.exportWallet()).to.equal(knifeMnemonic.HDRootPrivateKeyTestnet); + expect(() => wallet.exportWallet('mnemonic')).to.throw(exceptedException); + expect(() => wallet.exportWallet('seed')).to.throw(exceptedException2); + expect(wallet.exportWallet('HDPrivateKey')).to.equal(knifeMnemonic.HDRootPrivateKeyTestnet); + }); + after(() => { + wallet.disconnect(); + }); + }); + describe('fromHDPublicKey', () => { + const wallet = new Wallet({ + offlineMode: true, + HDPublicKey: knifeMnemonic.HDRootPublicKeyMainnet, + }); + it('should work as expected', () => { + const exceptedException = 'Tried to export to invalid output : mnemonic'; + const exceptedException2 = 'Tried to export to invalid output : seed'; + const exceptedException3 = 'Tried to export to invalid output : HDPrivateKey'; + + expect(wallet.exportWallet()).to.equal(knifeMnemonic.HDRootPublicKeyMainnet); + expect(() => wallet.exportWallet('mnemonic')).to.throw(exceptedException); + expect(() => wallet.exportWallet('seed')).to.throw(exceptedException2); + expect(() => wallet.exportWallet('HDPrivateKey')).to.throw(exceptedException3); + expect(wallet.exportWallet('HDPublicKey')).to.equal(knifeMnemonic.HDRootPublicKeyMainnet); + }); + after(() => { + wallet.disconnect(); + }); + }); + describe('fromPublicKey', () => { + const wallet = new Wallet({ + offlineMode: true, + publicKey: cR4t6ePublicKey, + }); + it('should work as expected', () => { + expect(wallet.exportWallet()).to.equal(cR4t6ePublicKey.toString()); + }); + after(() => { + wallet.disconnect(); + }); + }); + describe('fromAddress', () => { + const wallet = new Wallet({ + offlineMode: true, + address: cR4t6ePublicKey.toAddress(Networks.testnet), + }); + it('should work as expected', () => { + expect(wallet.exportWallet()).to.equal(cR4t6ePublicKey.toAddress(Networks.testnet).toString()); + }); + after(() => { + wallet.disconnect(); + }); + }); +}); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js new file mode 100644 index 00000000000..ebf0aba2925 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js @@ -0,0 +1,18 @@ +const { is } = require('../../../utils'); +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); + +/** + * @param address + */ +module.exports = function fromAddress(address, network) { + if (!is.address(address)) throw new Error('Expected a valid address (typeof Address or String)'); + this.walletType = WALLET_TYPES.ADDRESS; + this.mnemonic = null; + this.address = address.toString(); + + const keyChain = new DerivableKeyChain({ address, network }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); +}; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js new file mode 100644 index 00000000000..f0839308fc9 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js @@ -0,0 +1,48 @@ +const { expect } = require('chai'); +const { PrivateKey } = require('@dashevo/dashcore-lib'); +const fromAddress = require('./fromAddress'); +const cR4t6eFixture = require('../../../../fixtures/cR4t6e_pk'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); +const cR4t6ePublicKey = new PrivateKey(cR4t6eFixture.privateKey).toPublicKey(); + +describe('Wallet - fromAddress', function suite() { + this.timeout(10000); + it('should indicate missing data', () => { + const mockOpts1 = { }; + const exceptedException1 = 'Expected a valid address (typeof Address or String)'; + expect(() => fromAddress.call(mockOpts1)).to.throw(exceptedException1); + }); + it('should set wallet from address', () => { + const self1 = {}; + fromAddress.call(self1, cR4t6ePublicKey.toAddress()); + expect(self1.walletType).to.equal(WALLET_TYPES.ADDRESS); + expect(self1.mnemonic).to.equal(null); + expect(self1.address).to.equal(cR4t6ePublicKey.toAddress().toString()); + + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('address'); + expect(keyChain.rootKey).to.equal(cR4t6ePublicKey.toAddress().toString()); + + const self2 = {}; + fromAddress.call(self2, cR4t6ePublicKey.toAddress().toString()); + expect(self2.walletType).to.equal(WALLET_TYPES.ADDRESS); + expect(self2.mnemonic).to.equal(null); + expect(self2.address).to.equal(cR4t6ePublicKey.toAddress().toString()); + + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('address'); + expect(keyChain.rootKey).to.equal(cR4t6ePublicKey.toAddress().toString()); + }); + it('should reject invalid mnemonic', () => { + const invalidInputs = [ + { privateKey: 0 }, + { privateKey: true }, + { privateKey: false }, + ]; + + return invalidInputs.forEach((invalidInput) => { + const self = {}; + expect(() => fromAddress.call(self, invalidInput)).to.throw('Expected a valid address (typeof Address or String)'); + }); + }); +}); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js new file mode 100644 index 00000000000..a90dd2ca516 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js @@ -0,0 +1,22 @@ +const { HDPrivateKey } = require('@dashevo/dashcore-lib'); +const { + is, +} = require('../../../utils'); +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); + +/** + * Will set a wallet to work with a seed (HDPrivateKey) + * @param hdPrivateKey + */ +module.exports = function fromHDPrivateKey(hdPrivateKey) { + if (!is.HDPrivateKey(hdPrivateKey)) throw new Error('Expected a valid HDPrivateKey (typeof HDPrivateKey or String)'); + this.walletType = WALLET_TYPES.HDWALLET; + this.mnemonic = null; + this.HDPrivateKey = HDPrivateKey(hdPrivateKey); + + const keyChain = new DerivableKeyChain({ HDPrivateKey: this.HDPrivateKey }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); +}; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js new file mode 100644 index 00000000000..c947a076b4c --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js @@ -0,0 +1,36 @@ +const { expect } = require('chai'); +const fromHDPrivateKey = require('./fromHDPrivateKey'); +const knifeFixture = require('../../../../fixtures/knifeeasily'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); + +describe('Wallet - fromHDPrivateKey', function suite() { + this.timeout(10000); + it('should indicate missing data', () => { + const mockOpts1 = { }; + const exceptedException1 = 'Expected a valid HDPrivateKey (typeof HDPrivateKey or String)'; + expect(() => fromHDPrivateKey.call(mockOpts1)).to.throw(exceptedException1); + }); + it('should set wallet from a HDPrivateKey', () => { + const self1 = {}; + fromHDPrivateKey.call(self1, knifeFixture.HDRootPrivateKeyMainnet); + expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); + expect(self1.mnemonic).to.equal(null); + expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); + + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); + }); + it('should reject invalid mnemonic', () => { + const invalidInputs = [ + { seed: true }, + { seed: false }, + { seed: 0 }, + ]; + + return invalidInputs.forEach((invalidInput) => { + const self = {}; + expect(() => fromHDPrivateKey.call(self, invalidInput)).to.throw('Expected a valid HDPrivateKey (typeof HDPrivateKey or String)'); + }); + }); +}); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js new file mode 100644 index 00000000000..1a6b5aacdfc --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js @@ -0,0 +1,21 @@ +const Dashcore = require('@dashevo/dashcore-lib'); +const { is } = require('../../../utils'); +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); + +const normalizeHDPubKey = (key) => (is.string(key) ? Dashcore.HDPublicKey(key) : key); +/** + * Will set a wallet to work with a on readonly mode from a HDPublicKey + * @param HDPublicKey + */ +module.exports = function fromHDPublicKey(_hdPublicKey) { + if (!is.HDPublicKey(_hdPublicKey)) throw new Error('Expected a valid HDPublicKey (typeof HDPublicKey or String)'); + this.walletType = WALLET_TYPES.HDPUBLIC; + this.mnemonic = null; + this.HDPublicKey = normalizeHDPubKey(_hdPublicKey); + + const keyChain = new DerivableKeyChain({ HDPublicKey: this.HDPublicKey }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); +}; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js new file mode 100644 index 00000000000..8a8086b7cd9 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js @@ -0,0 +1,84 @@ +const Dashcore = require('@dashevo/dashcore-lib'); +const { expect } = require('chai'); +const Wallet = require('../Wallet'); +const fromHDPublicKey = require('./fromHDPublicKey'); +const gatherSail = require('../../../../fixtures/gathersail'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); +/** + * Theses first set of data labeled gatherSail correspond to the following mnemonic: + * gather sail face invite together focus waste barely excuse slide harbor hint + * + * + * @type {string} + */ +describe('Wallet - HDPublicKey', function suite() { + this.timeout(10000); + const gatherTestnet = gatherSail.testnet; + it('should detect wrong parameters', () => { + const mockOpts1 = {}; + const exceptedException1 = 'Expected a valid HDPublicKey (typeof HDPublicKey or String)'; + expect(() => fromHDPublicKey.call(mockOpts1)).to.throw(exceptedException1); + expect(() => fromHDPublicKey.call(mockOpts1, gatherTestnet.external.hdprivkey)).to.throw(exceptedException1); + expect(() => fromHDPublicKey.call(mockOpts1, gatherTestnet.mnemonic)).to.throw(exceptedException1); + expect(() => fromHDPublicKey.call(mockOpts1, 'cR4t6evwVZoCp1JsLk4wURK4UmBCZzZotNzn9T1mhBT19SH9JtNt')).to.throw(exceptedException1); + }); + it('should work from a valid HDPubKey', () => { + const mockOpts1 = {}; + fromHDPublicKey.call(mockOpts1, gatherTestnet.external.hdpubkey); + + expect(mockOpts1.walletType).to.equal(WALLET_TYPES.HDPUBLIC); + expect(mockOpts1.mnemonic).to.equal(null); + expect(mockOpts1.HDPublicKey.toString()).to.equal(gatherTestnet.external.hdpubkey); + expect(new Dashcore.HDPublicKey(mockOpts1.HDPublicKey)).to.equal(mockOpts1.HDPublicKey); + + const keyChain = mockOpts1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPublicKey'); + expect(keyChain.rootKey).to.deep.equal(Dashcore.HDPublicKey(gatherTestnet.external.hdpubkey)); + }); + it('should work from a HDPubKey', () => { + const wallet1 = new Wallet( + { HDPublicKey: gatherTestnet.external.hdpubkey, offlineMode: true }, + ); + + expect(wallet1.walletType).to.be.equal(WALLET_TYPES.HDPUBLIC); + expect(wallet1.mnemonic).to.be.equal(null); + + expect(wallet1.plugins).to.be.deep.equal({}); + expect(wallet1.accounts).to.be.deep.equal([]); + expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); + + const keyChain = wallet1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.be.deep.equal('HDPublicKey'); + expect(wallet1.passphrase).to.be.deep.equal(null); + expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); + expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); + expect(wallet1.walletId).to.be.equal(gatherTestnet.external.walletId); + + expect(wallet1.exportWallet()).to.be.equal(gatherTestnet.external.hdpubkey); + + + // FIXME: it appears we had introduced a bug here, + // as it is not possible to have a HDPublicKey derivation with hardened + // Either our path is m/44/1/0/0/0 or it is m/0/0. + // We should clarify this before merging TODO + wallet1 + .getAccount() + .then((account)=>{ + const unusedAddress = account.getUnusedAddress(); + const expectedUnused = { + path: "m/0/0", + index: 0, + address: 'yNJ3xxTXXBBf39VfMBbBuLH2k57uAwxBxj', + transactions: [], + balanceSat: 0, + unconfirmedBalanceSat: 0, + utxos: {}, + fetchedLast: 0, + used: false, + }; + expect(unusedAddress).to.deep.equal(expectedUnused); + + wallet1.disconnect(); + }) + }); +}); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js new file mode 100644 index 00000000000..b0b436fbbf4 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js @@ -0,0 +1,27 @@ +const { + mnemonicToHDPrivateKey, + is, +} = require('../../../utils'); +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); + +/** + * Will set a wallet to work with a mnemonic (keychain, walletType & HDPrivateKey) + * @param mnemonic + */ +module.exports = function fromMnemonic(mnemonic, network, passphrase = '') { + if (!is.mnemonic(mnemonic)) { + throw new Error('Expected a valid mnemonic (typeof String or Mnemonic)'); + } + const trimmedMnemonic = mnemonic.toString().trim(); + this.walletType = WALLET_TYPES.HDWALLET; + // As we do not require the mnemonic except in this.exportWallet + // users of wallet-lib are free to clear this prop at anytime. + this.mnemonic = trimmedMnemonic; + this.HDPrivateKey = mnemonicToHDPrivateKey(trimmedMnemonic, network, passphrase); + + this.keyChainStore = new KeyChainStore(); + const keyChain = new DerivableKeyChain({ HDPrivateKey: this.HDPrivateKey }); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); +}; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js new file mode 100644 index 00000000000..85f498c6172 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js @@ -0,0 +1,91 @@ +const Dashcore = require('@dashevo/dashcore-lib'); +const { expect } = require('chai'); +const fromMnemonic = require('./fromMnemonic'); +const knifeFixture = require('../../../../fixtures/knifeeasily'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); + +describe('Wallet - fromMnemonic', function suite() { + this.timeout(10000); + it('should indicate missing data', () => { + const mockOpts1 = {}; + const exceptedException1 = 'Expected a valid mnemonic (typeof String or Mnemonic)'; + expect(() => fromMnemonic.call(mockOpts1)).to.throw(exceptedException1); + }); + it('should set wallet from mnemonic', () => { + const self1 = { + network: 'livenet', + }; + fromMnemonic.call(self1, knifeFixture.mnemonic, 'livenet'); + expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); + expect(self1.mnemonic).to.equal(knifeFixture.mnemonic); + expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); + expect(new Dashcore.HDPrivateKey(self1.HDPrivateKey)).to.equal(self1.HDPrivateKey); + + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain.network).to.equal('livenet'); + expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); + + + const self2 = {}; + fromMnemonic.call(self2, knifeFixture.mnemonic); + expect(self2.walletType).to.equal(WALLET_TYPES.HDWALLET); + expect(self2.mnemonic).to.equal(knifeFixture.mnemonic); + + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.network).to.equal('testnet'); + expect(self2.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); + expect(new Dashcore.HDPrivateKey(self2.HDPrivateKey)).to.equal(self2.HDPrivateKey); + expect(keyChain2.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain2.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); + }); + it('should reject invalid mnemonic', () => { + const invalidInputs = [ + { mnemonic: 'knife easily prosper input concert merge prepare autumn pen blood glance chair' }, + { mnemonic: false }, + { mnemonic: true }, + { mnemonic: 0 }, + ]; + + return invalidInputs.forEach((invalidInput) => { + const self = {}; + expect(() => fromMnemonic.call(self, invalidInput)).to.throw('Expected a valid mnemonic (typeof String or Mnemonic)'); + }); + }); +}); +describe('Wallet - fromMnemonic - with passphrase', function suite() { + this.timeout(10000); + it('should correctly works with passphrase', () => { + const self1 = { + }; + fromMnemonic.call(self1, knifeFixture.mnemonic, 'livenet', knifeFixture.passphrase); + expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); + expect(self1.mnemonic).to.equal(knifeFixture.mnemonic); + expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyMainnet); + expect(new Dashcore.HDPrivateKey(self1.HDPrivateKey)).to.equal(self1.HDPrivateKey); + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain.network).to.equal('livenet'); + expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyMainnet); + + const path1 = 'm/44\'/5\'/0\'/0/0'; + const pubKey1 = keyChain.getForPath(path1).key.publicKey.toAddress(); + expect(new Dashcore.Address(pubKey1).toString()).to.equal('Xq3zjky18WjwAHpLgGLasvX5g8TeLRKaxt'); + + const self2 = { + }; + fromMnemonic.call(self2, knifeFixture.mnemonic, 'testnet', knifeFixture.passphrase); + expect(self2.walletType).to.equal(WALLET_TYPES.HDWALLET); + expect(self2.mnemonic).to.equal(knifeFixture.mnemonic); + expect(self2.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyTestnet); + expect(new Dashcore.HDPrivateKey(self2.HDPrivateKey)).to.equal(self2.HDPrivateKey); + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain2.network).to.equal('testnet'); + expect(keyChain2.rootKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyTestnet); + + const path2 = 'm/44\'/1\'/0\'/0/0'; + const pubKey2 = keyChain2.getForPath(path2).key.publicKey.toAddress(); + expect(new Dashcore.Address(pubKey2, 'testnet').toString()).to.equal('yWYCH9XDRnpdNxh67jQJFkovToBVwWr8Ck'); + }); +}); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js new file mode 100644 index 00000000000..60912dcac17 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js @@ -0,0 +1,19 @@ +const { is } = require('../../../utils'); +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); + +/** + * Will set a wallet to work with a mnemonic (keychain, walletType & HDPrivateKey) + * @param privateKey + */ +module.exports = function fromPrivateKey(privateKey, network) { + if (!is.privateKey(privateKey)) throw new Error('Expected a valid private key (typeof PrivateKey or String)'); + this.walletType = WALLET_TYPES.PRIVATEKEY; + this.mnemonic = null; + this.privateKey = privateKey; + + const keyChain = new DerivableKeyChain({ privateKey, network }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); +}; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js new file mode 100644 index 00000000000..4b6ca5e89ab --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js @@ -0,0 +1,44 @@ +const { expect } = require('chai'); +const fromPrivateKey = require('./fromPrivateKey'); +const cR4t6eFixture = require('../../../../fixtures/cR4t6e_pk'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); + +describe('Wallet - fromPrivateKey', function suite() { + this.timeout(10000); + it('should indicate missing data', () => { + const mockOpts1 = { }; + const exceptedException1 = 'Expected a valid private key (typeof PrivateKey or String)'; + expect(() => fromPrivateKey.call(mockOpts1)).to.throw(exceptedException1); + }); + it('should set wallet from private Key', () => { + const self1 = {}; + fromPrivateKey.call(self1, cR4t6eFixture.privateKey); + expect(self1.walletType).to.equal(WALLET_TYPES.PRIVATEKEY); + expect(self1.mnemonic).to.equal(null); + expect(self1.privateKey).to.equal(cR4t6eFixture.privateKey); + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('privateKey'); + expect(keyChain.rootKey.toWIF()).to.equal(cR4t6eFixture.privateKey); + + const self2 = {}; + fromPrivateKey.call(self2, cR4t6eFixture.privateKey); + expect(self2.walletType).to.equal(WALLET_TYPES.PRIVATEKEY); + expect(self2.mnemonic).to.equal(null); + expect(self2.privateKey).to.equal(cR4t6eFixture.privateKey); + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.rootKeyType).to.equal('privateKey'); + expect(keyChain2.rootKey.toWIF()).to.equal(cR4t6eFixture.privateKey); + }); + it('should reject invalid mnemonic', () => { + const invalidInputs = [ + { privateKey: 0 }, + { privateKey: true }, + { privateKey: false }, + ]; + + return invalidInputs.forEach((invalidInput) => { + const self = {}; + expect(() => fromPrivateKey.call(self, invalidInput)).to.throw('Expected a valid private key (typeof PrivateKey or String)'); + }); + }); +}); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js new file mode 100644 index 00000000000..c86dc6d6e37 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js @@ -0,0 +1,19 @@ +const { is } = require('../../../utils'); +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); + +/** + * Will set a wallet to work with a mnemonic (keychain, walletType & HDPrivateKey) + * @param privateKey + */ +module.exports = function fromPublicKey(publicKey, network) { + if (!is.publicKey(publicKey)) throw new Error('Expected a valid public key (typeof PublicKey or String)'); + this.walletType = WALLET_TYPES.PUBLICKEY; + this.mnemonic = null; + this.publicKey = publicKey; + + const keyChain = new DerivableKeyChain({ publicKey, network }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); +}; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js new file mode 100644 index 00000000000..07890f2d45d --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js @@ -0,0 +1,46 @@ +const { expect } = require('chai'); +const { PrivateKey } = require('@dashevo/dashcore-lib'); +const fromPublicKey = require('./fromPublicKey'); +const cR4t6eFixture = require('../../../../fixtures/cR4t6e_pk'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); +const cR4t6ePublicKey = new PrivateKey(cR4t6eFixture.privateKey).toPublicKey(); + +describe('Wallet - fromPublicKey', function suite() { + this.timeout(10000); + it('should indicate missing data', () => { + const mockOpts1 = { }; + const exceptedException1 = 'Expected a valid public key (typeof PublicKey or String)'; + expect(() => fromPublicKey.call(mockOpts1)).to.throw(exceptedException1); + }); + it('should set wallet from public Key', () => { + const self1 = {}; + fromPublicKey.call(self1, cR4t6ePublicKey); + expect(self1.walletType).to.equal(WALLET_TYPES.PUBLICKEY); + expect(self1.mnemonic).to.equal(null); + expect(self1.publicKey).to.equal(cR4t6ePublicKey); + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('publicKey'); + expect(keyChain.rootKey.toString()).to.equal(cR4t6ePublicKey.toString()); + + const self2 = {}; + fromPublicKey.call(self2, cR4t6ePublicKey.toString()); + expect(self2.walletType).to.equal(WALLET_TYPES.PUBLICKEY); + expect(self2.mnemonic).to.equal(null); + expect(self2.publicKey).to.equal(cR4t6ePublicKey.toString()); + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.rootKeyType).to.equal('publicKey'); + expect(keyChain2.rootKey.toString()).to.equal(cR4t6ePublicKey.toString()); + }); + it('should reject invalid mnemonic', () => { + const invalidInputs = [ + { privateKey: 0 }, + { privateKey: true }, + { privateKey: false }, + ]; + + return invalidInputs.forEach((invalidInput) => { + const self = {}; + expect(() => fromPublicKey.call(self, invalidInput)).to.throw('Expected a valid public key (typeof PublicKey or String)'); + }); + }); +}); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js new file mode 100644 index 00000000000..038fc138e6b --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js @@ -0,0 +1,14 @@ +const { + is, + seedToHDPrivateKey, +} = require('../../../utils'); + +/** + * Will set a wallet to work with a seed (HDPrivateKey) + * fixme: Term seed is often use, but we might want to rename to fromHDPrivateKey + * @param seed + */ +module.exports = function fromSeed(seed, network) { + if (!is.seed(seed)) throw new Error('Expected a valid seed (typeof string)'); + return this.fromHDPrivateKey(seedToHDPrivateKey(seed, network)); +}; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js new file mode 100644 index 00000000000..a0a9ec827ca --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js @@ -0,0 +1,51 @@ +const { expect } = require('chai'); +const fromSeed = require('./fromSeed'); +const fromHDPrivateKey = require('./fromHDPrivateKey'); +const knifeFixture = require('../../../../fixtures/knifeeasily'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); + +describe('Wallet - fromSeed', function suite() { + this.timeout(10000); + it('should indicate missing data', () => { + const mockOpts1 = { }; + const exceptedException1 = 'Expected a valid seed (typeof string)'; + expect(() => fromSeed.call(mockOpts1)).to.throw(exceptedException1); + }); + it('should set wallet from a HDPrivateKey', () => { + const self1 = { + fromHDPrivateKey, + }; + fromSeed.call(self1, knifeFixture.seed); + expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); + expect(self1.mnemonic).to.equal(null); + expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); + + const self2 = { + fromHDPrivateKey, + network: 'mainnet', + + }; + fromSeed.call(self2, knifeFixture.seed, self2.network); + expect(self2.walletType).to.equal(WALLET_TYPES.HDWALLET); + expect(self2.mnemonic).to.equal(null); + expect(self2.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain2.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); + }); + it('should reject invalid mnemonic', () => { + const invalidInputs = [ + { seed: true }, + { seed: false }, + { seed: 0 }, + ]; + + return invalidInputs.forEach((invalidInput) => { + const self = {}; + expect(() => fromSeed.call(self, invalidInput)).to.throw('Expected a valid seed (typeof string)'); + }); + }); +}); diff --git a/packages/wallet-lib/src/types/Wallet/methods/generateNewWalletId.js b/packages/wallet-lib/src/types/Wallet/methods/generateNewWalletId.js new file mode 100644 index 00000000000..66d4f87c24e --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/generateNewWalletId.js @@ -0,0 +1,37 @@ +const { mnemonicToWalletId } = require('../../../utils'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); + +/** + * Generate a wallet id for a specific wallet based on it's (HD)privateKey + * @return walletId + */ +module.exports = function generateNewWalletId() { + const { walletType } = this; + const errorMessageBase = 'Cannot generate a walletId'; + switch (walletType) { + case WALLET_TYPES.ADDRESS: + if (!this.address) throw new Error(`${errorMessageBase} : No address found`); + this.walletId = mnemonicToWalletId(this.address); + break; + case WALLET_TYPES.PUBLICKEY: + if (!this.publicKey) throw new Error(`${errorMessageBase} : No publicKey found`); + this.walletId = mnemonicToWalletId(this.publicKey); + break; + // TODO: DEPRECATE USAGE OF SINGLE_ADDRESS in favor or PRIVATEKEY + case WALLET_TYPES.PRIVATEKEY: + case WALLET_TYPES.SINGLE_ADDRESS: + if (!this.privateKey) throw new Error(`${errorMessageBase} : No privateKey found`); + this.walletId = mnemonicToWalletId(this.privateKey); + break; + case WALLET_TYPES.HDPUBLIC: + if (!this.HDPublicKey) throw new Error(`${errorMessageBase} : No HDPublicKey found`); + this.walletId = mnemonicToWalletId(this.HDPublicKey); + break; + case WALLET_TYPES.HDWALLET: + default: + if (!this.HDPrivateKey) throw new Error(`${errorMessageBase} : No HDPrivateKey found`); + this.walletId = mnemonicToWalletId(this.HDPrivateKey); + break; + } + return this.walletId; +}; diff --git a/packages/wallet-lib/src/types/Wallet/methods/generateNewWalletId.spec.js b/packages/wallet-lib/src/types/Wallet/methods/generateNewWalletId.spec.js new file mode 100644 index 00000000000..409a630f449 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/generateNewWalletId.spec.js @@ -0,0 +1,53 @@ +const { expect } = require('chai'); +const generateNewWalletId = require('./generateNewWalletId'); +const knifeMnemonic = require('../../../../fixtures/knifeeasily'); +const gatherSail = require('../../../../fixtures/gathersail'); +const cR4t6ePrivateKey = require('../../../../fixtures/cR4t6e_pk'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); + +describe('Wallet - generateNewWalletId', function suite() { + this.timeout(10000); + it('should indicate on missing data', () => { + const mockOpts1 = { }; + const mockOpts2 = { walletType: WALLET_TYPES.HDWALLET }; + const mockOpts3 = { walletType: WALLET_TYPES.PRIVATEKEY }; + + const exceptedException1 = 'Cannot generate a walletId : No HDPrivateKey found'; + const exceptedException3 = 'Cannot generate a walletId : No privateKey found'; + expect(() => generateNewWalletId.call(mockOpts1)).to.throw(exceptedException1); + expect(() => generateNewWalletId.call(mockOpts2)).to.throw(exceptedException1); + expect(() => generateNewWalletId.call(mockOpts3)).to.throw(exceptedException3); + }); + it('should generate a wallet id from HDWallet', () => { + const mockOptsMainnet = { HDPrivateKey: knifeMnemonic.HDRootPrivateKeyMainnet }; + const mockOptsTestnet = { HDPrivateKey: knifeMnemonic.HDRootPrivateKeyTestnet }; + + const walletId1 = generateNewWalletId.call(mockOptsMainnet); + expect(walletId1).to.length(10); + expect(walletId1).to.equal(knifeMnemonic.HDPrivateKeyMainnetWalletId); + + const walletId2 = generateNewWalletId.call(mockOptsTestnet); + expect(walletId2).to.length(10); + expect(walletId2).to.equal(knifeMnemonic.HDPrivateKeyTestnetWalletId); + }); + it('should generate a wallet id from HDPubKey', () => { + const mockOptsTestnet = { + walletType: WALLET_TYPES.HDPUBLIC, + HDPublicKey: gatherSail.testnet.external.hdpubkey, + }; + + const walletId1 = generateNewWalletId.call(mockOptsTestnet); + expect(walletId1).to.length(10); + expect(walletId1).to.equal(gatherSail.testnet.external.walletId); + }); + it('should generate a wallet id from single pk', () => { + const mockOpts = { + walletType: WALLET_TYPES.PRIVATEKEY, + privateKey: cR4t6ePrivateKey.privateKey, + }; + + const walletId = generateNewWalletId.call(mockOpts); + expect(walletId).to.length(10); + expect(walletId).to.equal(cR4t6ePrivateKey.walletIdTestnet); + }); +}); diff --git a/packages/wallet-lib/src/types/Wallet/methods/getAccount.js b/packages/wallet-lib/src/types/Wallet/methods/getAccount.js new file mode 100644 index 00000000000..241dbe964a0 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/getAccount.js @@ -0,0 +1,33 @@ +const _ = require('lodash'); +const { is } = require('../../../utils'); +const EVENTS = require('../../../EVENTS'); + +/** + * Get a specific account per accounts index + * @param accountOpts - If the account doesn't exist yet, we create it passing these options + * @param accountOpts.index - Default: 0, set a specific index to get + * @return {Account} + */ + +async function getAccount(accountOpts = {}) { + if (!this.storage.configured) { + await new Promise((resolve) => this.storage.once(EVENTS.CONFIGURED, resolve)); + } + + if (is.num(accountOpts)) { + throw new Error('getAccount expected index integer to be a property of accountOptions'); + } + const defaultIndex = 0; + + const accountIndex = (_.has(accountOpts, 'index') && is.num(accountOpts.index)) + ? accountOpts.index + : defaultIndex; + + const acc = this.accounts.filter((el) => el.index === accountIndex); + const baseOpts = { index: accountIndex }; + + const opts = Object.assign(baseOpts, accountOpts); + return (acc[0]) || this.createAccount(opts); +} + +module.exports = getAccount; diff --git a/packages/wallet-lib/src/types/Wallet/methods/getAccount.spec.js b/packages/wallet-lib/src/types/Wallet/methods/getAccount.spec.js new file mode 100644 index 00000000000..597d690f930 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/getAccount.spec.js @@ -0,0 +1,65 @@ +const { expect } = require('chai'); +const getAccount = require('./getAccount'); +const { WALLET_TYPES } = require('../../../CONSTANTS'); +const expectThrowsAsync = require('../../../utils/expectThrowsAsync'); + +const exceptedException1 = 'getAccount expected index integer to be a property of accountOptions'; + + +describe('Wallet - getAccount', function suite() { + this.timeout(10000); + it('should warn on trying to pass arg as number', () => { + let timesCreateAccountCalled = 0; + let timesAttachEventsCalled = 0; + const mockOpts = { + accounts: [], + storage: { + configured: true + }, + walletType: WALLET_TYPES.HDWALLET, + createAccount: (opts = { index: 0 }) => { + timesCreateAccountCalled += 1; + return { + index: opts.index, + storage: { + attachEvents: () => timesAttachEventsCalled += 1, + }, + }; + }, + }; + expectThrowsAsync(async () => await getAccount.call(mockOpts, 0),exceptedException1); + expect(timesCreateAccountCalled).to.equal(0); + expect(timesAttachEventsCalled).to.equal(0); + }); + it('should create an account when not existing and get it back', async () => { + let timesCreateAccountCalled = 0; + const mockOpts1 = { + accounts: [], + storage: { + configured: true + }, + walletType: WALLET_TYPES.HDWALLET, + createAccount: (opts = { index: 0 }) => { + timesCreateAccountCalled += 1; + const acc = { + index: opts.index, + }; + // This is actually done by Account class + mockOpts1.accounts.push(acc); + return acc; + }, + }; + + const acc = await getAccount.call(mockOpts1); + expect(acc.index).to.equal(0); + expect(timesCreateAccountCalled).to.equal(1); + const acc2 = await getAccount.call(mockOpts1, { index: 0 }); + expect(acc2.index).to.equal(0); + expect(timesCreateAccountCalled).to.equal(1); + expect(acc2).to.deep.equal(acc); + + const acc3 = await getAccount.call(mockOpts1, { index: 1 }); + expect(acc3.index).to.equal(1); + expect(timesCreateAccountCalled).to.equal(2); + }); +}); diff --git a/packages/wallet-lib/src/types/Wallet/methods/sweepWallet.js b/packages/wallet-lib/src/types/Wallet/methods/sweepWallet.js new file mode 100644 index 00000000000..b00ae2f646f --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/sweepWallet.js @@ -0,0 +1,61 @@ +const { WALLET_TYPES } = require('../../../CONSTANTS'); +const logger = require('../../../logger'); +/** + * This will sweep any paper wallet with remaining UTXOS to another Wallet created + * via a random new mnemonic or via passed one. + * Will resolves automatically network and transport. + * + * By default, the Wallet return is in offlineMode. And therefore sweep will be done + * on the first address path. You can pass offlineMode:false to overwrite. + * + * @param {Wallet.Options} opts - Options to be passed to the wallet swept. + * @return {Wallet} - Return a new random mnemonic created Wallet. + */ +async function sweepWallet(opts = {}) { + const self = this; + // eslint-disable-next-line no-async-promise-executor,consistent-return + return new Promise(async (resolve, reject) => { + if (self.walletType !== WALLET_TYPES.PRIVATEKEY) { + return reject(new Error('Can only sweep wallet initialized from privateKey')); + } + + const account = await self.getAccount({ index: 0 }); + await account.isReady(); + + const balance = await account.getTotalBalance(); + if (balance <= 0) { + return reject(new Error(`Cannot sweep an empty private key (current balance: ${balance})`)); + } + + let newWallet; + try { + const walletOpts = { + network: self.network, + transport: self.transport, + ...opts, + }; + + newWallet = new self.constructor(walletOpts); + + const recipient = newWallet.getAccount({ index: 0 }).getUnusedAddress().address; + + const tx = account.createTransaction({ + satoshis: balance, + recipient, + }); + + const txid = await account.broadcastTransaction(tx); + + logger.info(`SweepWallet: ${balance} of ${account.getAddress().address} to ${recipient} transfered. Txid :${txid}`); + + return resolve(newWallet); + } catch (err) { + if (newWallet) { + await newWallet.disconnect(); + } + + return reject(err); + } + }); +} +module.exports = sweepWallet; diff --git a/packages/wallet-lib/src/types/Wallet/methods/sweepWallet.spec.js b/packages/wallet-lib/src/types/Wallet/methods/sweepWallet.spec.js new file mode 100644 index 00000000000..21af353bc23 --- /dev/null +++ b/packages/wallet-lib/src/types/Wallet/methods/sweepWallet.spec.js @@ -0,0 +1,50 @@ +const {Wallet} = require('../../../index'); +const expectThrowsAsync = require('../../../utils/expectThrowsAsync'); +const sweepWallet = require('./sweepWallet'); + +const paperWallet = { + publicKey: 'ybvbBPisVjiemj4qSg1mzZAzTSAPk64Ppf', + privateKey: '53d0f7df9103127f159f939438254011f6fa11df18a843d3962313e38938f020', +}; + +describe('Wallet - sweepWallet', function suite() { + this.timeout(60000); + let emptyWallet; + let emptyAccount; + const transportOpts = (process.env.DAPI_SEED) + ? { + seeds: process.env.DAPI_SEED + .split(',') + } + : {} + before(async () => { + emptyWallet = new Wallet({ + privateKey: paperWallet.privateKey, + transport: transportOpts, + network: process.env.NETWORK + }); + + emptyAccount = await emptyWallet.getAccount(); + }); + + after(async () => { + if (emptyWallet) { + await emptyWallet.disconnect(); + } + }); + + it('should warn on empty balance', async () => { + await emptyAccount.isReady(); + const exceptedException = 'Cannot sweep an empty private key (current balance: 0)'; + await expectThrowsAsync(async () => await emptyWallet.sweepWallet(), exceptedException); + await emptyWallet.disconnect(); + }); + it('should warn on sweep from mnemonic', async () => { + const exceptedException = 'Can only sweep wallet initialized from privateKey'; + const mockWallet = { + walletType: 'HDWALLET', + getAccount: () => ({getAddress: () => ({address: null}), isReady: () => true}), + }; + expectThrowsAsync(async () => await sweepWallet.call(mockWallet), exceptedException); + }); +}); diff --git a/packages/wallet-lib/src/types/WalletStore/WalletStore.d.ts b/packages/wallet-lib/src/types/WalletStore/WalletStore.d.ts new file mode 100644 index 00000000000..91697ef6a8f --- /dev/null +++ b/packages/wallet-lib/src/types/WalletStore/WalletStore.d.ts @@ -0,0 +1,26 @@ +export declare interface WalletStoreState { + mnemonic: string; + paths: Map + identities: Map +} + +type walletId = string; +type exportedState = any; + +export declare class WalletStore { + constructor(walletId: walletId); + + walletId: walletId; + state: WalletStoreState; + + createPathState(path: string): void; + exportState(): exportedState; + getIdentityIdByIndex(identityIndex: number): string; + getIndexedIdentityIds(identityIndex: number): Array; + getPathState(path: string): any; + + importState(exportedState): void; + insertIdentityIdAtIndex(identityId: string, identityIndex: number): void; +} + + diff --git a/packages/wallet-lib/src/types/WalletStore/WalletStore.js b/packages/wallet-lib/src/types/WalletStore/WalletStore.js new file mode 100644 index 00000000000..e963c1dbc80 --- /dev/null +++ b/packages/wallet-lib/src/types/WalletStore/WalletStore.js @@ -0,0 +1,44 @@ +const SCHEMA = { + lastKnownBlock: { + height: 'number', + }, +}; + +class WalletStore { + constructor(walletId) { + this.walletId = walletId; + + this.state = { + mnemonic: null, + paths: new Map(), + identities: new Map(), + lastKnownBlock: { + height: -1, + }, + }; + } + + /** + * Updates last known block value + * @param height - height of a last known block + */ + updateLastKnownBlock(height) { + if (this.state.lastKnownBlock.height >= height) { + return; + } + + this.state.lastKnownBlock.height = height; + } +} + +WalletStore.prototype.SCHEMA = SCHEMA; + +WalletStore.prototype.createPathState = require('./methods/createPathState'); +WalletStore.prototype.exportState = require('./methods/exportState'); +WalletStore.prototype.getIdentityIdByIndex = require('./methods/getIdentityIdByIndex'); +WalletStore.prototype.getIndexedIdentityIds = require('./methods/getIndexedIdentityIds'); +WalletStore.prototype.getPathState = require('./methods/getPathState'); +WalletStore.prototype.importState = require('./methods/importState'); +WalletStore.prototype.insertIdentityIdAtIndex = require('./methods/insertIdentityIdAtIndex'); + +module.exports = WalletStore; diff --git a/packages/wallet-lib/src/types/WalletStore/WalletStore.spec.js b/packages/wallet-lib/src/types/WalletStore/WalletStore.spec.js new file mode 100644 index 00000000000..27b21230c2f --- /dev/null +++ b/packages/wallet-lib/src/types/WalletStore/WalletStore.spec.js @@ -0,0 +1,50 @@ +const { expect } = require('chai'); +const WalletStore = require('./WalletStore'); + +let walletStore; +describe('WalletStore - Class', ()=> { + describe('simple usage', () => { + it('should create a walletStore', function () { + walletStore = new WalletStore('squawk7700'); + walletStore.state.lastKnownBlock.height = 100; + + expect(walletStore.walletId).to.equal('squawk7700'); + }); + it('should create path state', function () { + walletStore.createPathState('m/0') + expect(walletStore.state.paths.get('m/0')).to.deep.equal({ + path: 'm/0', + addresses: {} + }); + // TODO: Can be done later to have a better way to update path state + walletStore.state.paths.get('m/0').addresses['m/0'] = 'yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7' + }); + + it('should get path state', function () { + const pathState = walletStore.getPathState('m/0'); + expect(pathState).to.deep.equal({ + path: 'm/0', + addresses: { + 'm/0': 'yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7' + } + }); + }); + it('should insert identity', function () { + const identityId = 'abcde1234'; + const identityIndex = 0; + walletStore.insertIdentityIdAtIndex(identityId, identityIndex); + }); + it('should get indexed identity ids', function () { + expect(walletStore.getIndexedIdentityIds()).to.deep.equal(['abcde1234']) + }); + it('should get identity id by index', function () { + expect(walletStore.getIdentityIdByIndex(0)).to.deep.equal('abcde1234') + }); + it('should export and import state', function () { + const exportedState = walletStore.exportState(); + const importedWalletStore = new WalletStore(); + importedWalletStore.importState(exportedState); + expect(exportedState).to.deep.equal(importedWalletStore.exportState()) + }); + }) +}) diff --git a/packages/wallet-lib/src/types/WalletStore/methods/createPathState.js b/packages/wallet-lib/src/types/WalletStore/methods/createPathState.js new file mode 100644 index 00000000000..2dd215942ce --- /dev/null +++ b/packages/wallet-lib/src/types/WalletStore/methods/createPathState.js @@ -0,0 +1,12 @@ +const logger = require('../../../logger'); + +function createPathState(path) { + logger.debug(`WalletStore - Creating path state ${path}`); + if (!this.state.paths.has(path)) { + this.state.paths.set(path, { + path, + addresses: {}, + }); + } +} +module.exports = createPathState; diff --git a/packages/wallet-lib/src/types/WalletStore/methods/exportState.js b/packages/wallet-lib/src/types/WalletStore/methods/exportState.js new file mode 100644 index 00000000000..7819cd3bdb0 --- /dev/null +++ b/packages/wallet-lib/src/types/WalletStore/methods/exportState.js @@ -0,0 +1,18 @@ +function exportState(chainHeight) { + let { lastKnownBlock: { height } } = this.state; + + /* + * If we have chain height provided, we must set last known block to + * chainHeight - 6 to avoid reorgs + */ + if (chainHeight && height > chainHeight - 6) { + height = chainHeight - 6; + } + + return { + lastKnownBlock: { + height, + }, + }; +} +module.exports = exportState; diff --git a/packages/wallet-lib/src/types/WalletStore/methods/getIdentityIdByIndex.js b/packages/wallet-lib/src/types/WalletStore/methods/getIdentityIdByIndex.js new file mode 100644 index 00000000000..f90d1e5782a --- /dev/null +++ b/packages/wallet-lib/src/types/WalletStore/methods/getIdentityIdByIndex.js @@ -0,0 +1,4 @@ +function getIdentityIdByIndex(identityIndex) { + return this.state.identities.get(identityIndex); +} +module.exports = getIdentityIdByIndex; diff --git a/packages/wallet-lib/src/types/WalletStore/methods/getIndexedIdentityIds.js b/packages/wallet-lib/src/types/WalletStore/methods/getIndexedIdentityIds.js new file mode 100644 index 00000000000..e251ba4806b --- /dev/null +++ b/packages/wallet-lib/src/types/WalletStore/methods/getIndexedIdentityIds.js @@ -0,0 +1,4 @@ +function getIndexedIdentityIds() { + return [...this.state.identities.values()]; +} +module.exports = getIndexedIdentityIds; diff --git a/packages/wallet-lib/src/types/WalletStore/methods/getPathState.js b/packages/wallet-lib/src/types/WalletStore/methods/getPathState.js new file mode 100644 index 00000000000..d2143cd4d95 --- /dev/null +++ b/packages/wallet-lib/src/types/WalletStore/methods/getPathState.js @@ -0,0 +1,4 @@ +function getPathState(path) { + return this.state.paths.get(path); +} +module.exports = getPathState; diff --git a/packages/wallet-lib/src/types/WalletStore/methods/importState.js b/packages/wallet-lib/src/types/WalletStore/methods/importState.js new file mode 100644 index 00000000000..92af05dea14 --- /dev/null +++ b/packages/wallet-lib/src/types/WalletStore/methods/importState.js @@ -0,0 +1,9 @@ +const castStorageItemsTypes = require('../../../utils/castStorageItemsTypes'); + +function importState(rawState) { + const state = castStorageItemsTypes(rawState, this.SCHEMA); + + this.state.lastKnownBlock = state.lastKnownBlock; +} + +module.exports = importState; diff --git a/packages/wallet-lib/src/types/WalletStore/methods/insertIdentityIdAtIndex.js b/packages/wallet-lib/src/types/WalletStore/methods/insertIdentityIdAtIndex.js new file mode 100644 index 00000000000..ee8a9325190 --- /dev/null +++ b/packages/wallet-lib/src/types/WalletStore/methods/insertIdentityIdAtIndex.js @@ -0,0 +1,12 @@ +const IdentityReplaceError = require('../../../errors/IndentityIdReplaceError'); + +function insertIdentityIdAtIndex(identityId, identityIndex) { + const existingId = this.getIdentityIdByIndex(identityIndex); + + if (Boolean(existingId) && existingId !== identityId) { + throw new IdentityReplaceError(`Trying to replace identity at index ${identityIndex}`); + } + + this.state.identities.set(identityIndex, identityId); +} +module.exports = insertIdentityIdAtIndex; diff --git a/packages/wallet-lib/src/types/types.d.ts b/packages/wallet-lib/src/types/types.d.ts new file mode 100644 index 00000000000..950c5491b3e --- /dev/null +++ b/packages/wallet-lib/src/types/types.d.ts @@ -0,0 +1,172 @@ +import {Account} from "./Account/Account"; + +export declare type TransactionMetaData = T & { + blockHash: string, + height: number, + instantLocked: boolean, + chainLocked: boolean +} +export declare type transactionId = T; +export declare type Mnemonic = T & { + toString(): string; +}; +export declare type PrivateKey = T & { + toString(): string; +}; +export declare type HDPublicKey = T & { + toString(): string; +}; +export declare type PublicKey = T & { + toString(): string; +}; +export declare type Seed = T & { + toString(): string; +}; +export declare type Transaction = T & { + toString(): string; +}; +export declare type TransactionWithMetaData = T & { + transaction: Transaction, + metadata: TransactionMetaData +} + +export declare type TransactionHistoryType = "received" + | "sent" + | "address_transfer" + | "account_transfer" + | "unknown" + +export declare type TransactionHistory = T & { + // fees: number, + from: [{ + address: string, + satoshis: number, + }], + to: [{ + address: string, + satoshis: number + }], + type: TransactionHistoryType + time: Date, + txId: string, + blockHash: string, + isChainLocked: boolean, + isInstantLocked: boolean +} + +export declare type TransactionsHistory = [TransactionHistory]|[]; + +export declare type TransactionsWithMetaData = [TransactionWithMetaData]; + +export declare type RawTransaction = string; +export declare type TransactionInfo = T & { + txid:string; + blockhash:string; + blockHeight:number + blocktime: string + fees: number; + size:number; + vout:[object]; + vin:[object]; + txlock:boolean; +}; +export declare type Plugins = T & { + toString(): string; +}; +export declare type PublicAddress = T; +export declare type Address = T & { + toString(): string; +}; +export declare type AddressObj = T & { + address: string; + path: string; +} + +export declare type AddressInfoMap = T & { + [pathName: string]: AddressInfo +} +export declare type broadcastTransactionOpts = T & { + skipFeeValidation?: boolean +} +export declare type AddressInfo = T & { + path: string; + address: string; + balanceSat: number; + index: number; + fetchedLast:number; + unconfirmedBalanceSat: number; + transaction: object; + used:boolean; + utxos:[object] +} + +export declare type Network = "livenet" | "testnet" | "evonet" | "regtest" | "local" | "devnet" | "mainnet"; +export declare type Strategy = "simpleDescendingAccumulator" + | "simpleAscendingAccumulator" + | 'simpleTransactionOptimizedAccumulator' + | Function; +export declare type AddressType = "external" | "internal" | "misc"; +// todo: actually, I would vote to move hdextpublic to hdextpubkey +export declare type WalletType = "single_address" | "hdwallet" | "hdextpublic"; +export declare type WalletObj = { + network?: Network; + mnemonic?: Mnemonic|string; + type: WalletType, + accounts: AccountMap, + blockHeight: number, + addresses:{ + external: AddressInfoMap, + internal: AddressInfoMap, + misc: AddressInfoMap + } +} + +export declare type StatusInfo = T & { + version: { + protocol: number, + software: number, + agent: string, + }, + time: { + now: number, + offset: number, + median: number, + }, + status: string, + syncProgress: number, + chain: { + name: string, + headersCount: number, + blocksCount: number, + bestBlockHash: string, + difficulty: number, + chainWork: string, + isSynced: boolean, + syncProgress: number, + }, + masternode: { + status: string, + proTxHash: string, + posePenalty: string + isSynced: true, + syncProgress: number, + }, + network: { + peersCount: number, + fee: { + relay: number, + incremental: number, + }, + }, +} + +export declare type TransactionsMap = { + [txid: string]: Transaction +}; + +export declare type AccountMap = { + [pathName: string]: Account +} + + +export declare type SerializedUTXO = string; diff --git a/packages/wallet-lib/src/utils/Queue/Job.js b/packages/wallet-lib/src/utils/Queue/Job.js new file mode 100644 index 00000000000..866528313b4 --- /dev/null +++ b/packages/wallet-lib/src/utils/Queue/Job.js @@ -0,0 +1,8 @@ +class Job { + constructor(id, fn) { + this.id = id; + this.fn = fn; + this.timestamp = Date.now(); + } +} +module.exports = Job; diff --git a/packages/wallet-lib/src/utils/Queue/Queue.js b/packages/wallet-lib/src/utils/Queue/Queue.js new file mode 100644 index 00000000000..9d9f862af76 --- /dev/null +++ b/packages/wallet-lib/src/utils/Queue/Queue.js @@ -0,0 +1,73 @@ +const Emitter = require('events').EventEmitter; + +class Queue extends Emitter { + constructor(options = { autoProcess: true }) { + super(); + this.jobs = []; + this.state = { + isProcessing: false, + }; + this.autoProcess = options.autoProcess || true; + + if (this.autoProcess) { + this.startAutoProcessing(); + } + } + + getSize() { + return this.jobs.length; + } + + enqueueJob(job) { + this.jobs.push(job); + this.emit('enqueued', job.id); + } + + dequeueJob() { + const job = this.jobs.shift(); + this.emit('dequeued', job); + return job; + } + + async processNext() { + const job = this.dequeueJob(); + if (job) { + await this.processJob(job); + } + } + + async processJob(job) { + this.state.isProcessing = true; + const result = await job.fn(); + this.state.isProcessing = false; + this.emit('processed', { result, job }); + return result; + } + + startAutoProcessing() { + const self = this; + + const processOnEnqueuedEvent = async () => { + try { + await self.processNext(); + } catch (e) { + self.emit('error', e); + } + }; + const processOnProcessedEvent = async () => { + try { + await self.processNext(); + } catch (e) { + self.emit('error', e); + } + if (!self.getSize()) { + this.once('enqueued', processOnEnqueuedEvent); + } + }; + + this.on('processed', processOnProcessedEvent); + this.once('enqueued', processOnEnqueuedEvent); + } +} + +module.exports = Queue; diff --git a/packages/wallet-lib/src/utils/Queue/Queue.spec.js b/packages/wallet-lib/src/utils/Queue/Queue.spec.js new file mode 100644 index 00000000000..90bf3e9a351 --- /dev/null +++ b/packages/wallet-lib/src/utils/Queue/Queue.spec.js @@ -0,0 +1,67 @@ +const { expect } = require('chai'); +const Queue = require('./Queue'); +const Job = require('./Job'); + +async function storeDataAtSomePoint(storage, data) { + return new Promise((resolve) => { + const timeoutValue = Math.ceil(Math.random() * 1000); + setTimeout(() => { + storage.push(data); + resolve(data); + }, timeoutValue); + }) +} + +let store; +let queue; +describe('Utils - Queue', function suite() { + this.timeout(5000); + const processedResults = []; + it('should instantiate a Queue', function () { + queue = new Queue({}); + expect(queue).to.exist; + store = []; + }) + it('should enqueue and process job', function (done) { + queue.on('processed', ({result}) => { + processedResults.push(result); + if (processedResults.length === 3) { + expect(processedResults).to.deep.equal([{id: 1}, {id: 2}, {id: 3}]); + done(); + } + }); + const job1 = new Job(1, storeDataAtSomePoint.bind(null, store, {id: 1})); + queue.enqueueJob(job1); + const job2 = new Job(2, storeDataAtSomePoint.bind(null, store, {id: 2})); + queue.enqueueJob(job2); + const job3 = new Job(3, storeDataAtSomePoint.bind(null, store, {id: 3})); + queue.enqueueJob(job3); + }); + it('should process next enqueued', function (done) { + const job5 = new Job(5, storeDataAtSomePoint.bind(null, store, {id: 5})) + const job6 = new Job(6, storeDataAtSomePoint.bind(null, store, {id: 6})) + const job4 = new Job(4, storeDataAtSomePoint.bind(null, store, {id: 4})); + queue.enqueueJob(job4); + queue.enqueueJob(job5); + queue.enqueueJob(job6); + queue.on('processed', () => { + if (processedResults.length === 6) done(); + }) + }); + it('should have correctly dealt with order', function () { + expect(processedResults.length).to.equal(6); + expect(processedResults).to.deep.equal([{id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}, {id: 6}]); + }); + it('should catch and emit the async error from jobs to queue', function (done) { + const asyncFnThrowing = async () => { + throw new Error('An error from job'); + } + queue.on('error', (e)=>{ + expect(e.message).to.equal('An error from job'); + done() + }) + const job7 = new Job(7, asyncFnThrowing); + queue.enqueueJob(job7); + + }); +}); diff --git a/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.js b/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.js new file mode 100644 index 00000000000..318d7959f8f --- /dev/null +++ b/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.js @@ -0,0 +1,111 @@ +const logger = require('../../logger'); +const { BIP44_ADDRESS_GAP } = require('../../CONSTANTS'); +const is = require('../is'); + +const getMissingIndexes = require('./getMissingIndexes'); +const isContiguousPath = require('./isContiguousPath'); + +const sortByIndex = (a, b) => parseInt(a.split('/')[5], 10) - parseInt(b.split('/')[5], 10); + +/** + * This method ensures there will always be enough local addresses up to gap limit as per BIP44 + * @param {Storage} walletStore + * @param walletType + * @param accountIndex + * @param getAddress + * @return {number} + */ +function ensureAccountAddressesToGapLimit(walletStore, walletType, accountIndex, getAddress) { + let generated = 0; + + const { addresses } = walletStore; + + const addressesPaths = { + external: Object.keys(addresses.external), + internal: Object.keys(addresses.internal), + }; + + // We need to ensure that all our paths are contiguous, so we first fetch the + // missing indexes + const missingIndexes = { + external: getMissingIndexes(addressesPaths.external), + internal: getMissingIndexes(addressesPaths.internal), + }; + // Gets missing addresses and adds them to the storage + // Please note that getAddress adds new addresses to storage, which it probably shouldn't + Object.entries(missingIndexes) + .forEach(([addressesType, indexes]) => { + indexes.forEach((index) => { + getAddress(index, addressesType); + }); + }); + + Object.entries(addressesPaths) + .forEach(([type, paths]) => { + addressesPaths[type] = paths + .filter((el) => parseInt(el.split('/')[3], 10) === accountIndex) + .sort(sortByIndex); + }); + + const lastUsedIndexes = { + external: -1, + internal: -1, + }; + const lastGeneratedIndexes = { + external: -1, + internal: -1, + }; + + // Scan already generated addresses and count how many are unused + Object.entries(addressesPaths) + .forEach(([type, paths]) => { + let prevPath; + paths.forEach((path) => { + const address = addresses[type][path]; + if (!isContiguousPath(path, prevPath)) { + throw new Error('Addresses are expected to be contiguous'); + } + + if (address.used) { + lastUsedIndexes[type] = address.index; + } + + lastGeneratedIndexes[type] = address.index; + prevPath = path; + }); + }); + + const gapBetweenLastUsedAndLastGenerated = { + external: lastGeneratedIndexes.external - lastUsedIndexes.external, + internal: lastGeneratedIndexes.internal - lastUsedIndexes.internal, + }; + const addressesToGenerate = { + external: BIP44_ADDRESS_GAP - gapBetweenLastUsedAndLastGenerated.external, + internal: BIP44_ADDRESS_GAP - gapBetweenLastUsedAndLastGenerated.internal, + }; + + Object.entries(addressesToGenerate) + .forEach(([typeToGenerate, numberToGenerate]) => { + if (numberToGenerate > 0) { + const pathLength = addressesPaths[typeToGenerate].length; + const lastElemPath = addressesPaths[typeToGenerate][pathLength - 1]; + const lastElem = addresses[typeToGenerate][lastElemPath]; + const lastExistingIndex = (is.def(lastElem)) ? lastElem.index : -1; + const lastIndexToGenerate = lastExistingIndex + numberToGenerate; + if (lastIndexToGenerate > lastExistingIndex) { + for ( + let index = lastExistingIndex + 1; + index <= lastIndexToGenerate; + index += 1) { + getAddress(index, typeToGenerate); + generated += 1; + } + } + } + }); + + logger.silly(`BIP44 - ensured addresses to gap limit - generated: ${generated}`); + return generated; +} + +module.exports = ensureAccountAddressesToGapLimit; diff --git a/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.spec.js b/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.spec.js new file mode 100644 index 00000000000..a702a57263e --- /dev/null +++ b/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.spec.js @@ -0,0 +1,57 @@ +const {expect} = require('chai'); +const ensureAddressesToGapLimit = require('./ensureAddressesToGapLimit'); +const {CONSTANTS} = require("../../index"); + +const walletStore = { + addresses: { + external: {}, + internal: {} + } +} + +const walletType = CONSTANTS.WALLET_TYPES.HDWALLET; +const accountIndex = 0; +const getAddress = (i, type) => { + const rootPath = `m/44'/1'/0'`; + const path = `${rootPath}/${(type === 'external') ? `0` : '1'}/${i}`; + if (!walletStore.addresses[type][path]) { + walletStore.addresses[type][path] = { + index: i, + path, + used: false + } + } + return walletStore.addresses[type][path]; +} +describe('Utils - BIP44 - ensureAddressesToGapLimit', function suite() { + + it('should set first set of 20 unused address in a row', function () { + const generated = ensureAddressesToGapLimit(walletStore, walletType, accountIndex, getAddress) + expect(generated).to.equal(40); + expect(Object.keys(walletStore.addresses.external)) + .to.deep.equal(["m/44'/1'/0'/0/0", "m/44'/1'/0'/0/1", "m/44'/1'/0'/0/2", "m/44'/1'/0'/0/3", "m/44'/1'/0'/0/4", "m/44'/1'/0'/0/5", "m/44'/1'/0'/0/6", "m/44'/1'/0'/0/7", "m/44'/1'/0'/0/8", "m/44'/1'/0'/0/9", "m/44'/1'/0'/0/10", "m/44'/1'/0'/0/11", "m/44'/1'/0'/0/12", "m/44'/1'/0'/0/13", "m/44'/1'/0'/0/14", "m/44'/1'/0'/0/15", "m/44'/1'/0'/0/16", "m/44'/1'/0'/0/17", "m/44'/1'/0'/0/18", "m/44'/1'/0'/0/19"]); + expect(Object.keys(walletStore.addresses.internal)) + .to.deep.equal(["m/44'/1'/0'/1/0", "m/44'/1'/0'/1/1", "m/44'/1'/0'/1/2", "m/44'/1'/0'/1/3", "m/44'/1'/0'/1/4", "m/44'/1'/0'/1/5", "m/44'/1'/0'/1/6", "m/44'/1'/0'/1/7", "m/44'/1'/0'/1/8", "m/44'/1'/0'/1/9", "m/44'/1'/0'/1/10", "m/44'/1'/0'/1/11", "m/44'/1'/0'/1/12", "m/44'/1'/0'/1/13", "m/44'/1'/0'/1/14", "m/44'/1'/0'/1/15", "m/44'/1'/0'/1/16", "m/44'/1'/0'/1/17", "m/44'/1'/0'/1/18", "m/44'/1'/0'/1/19"]) + }); + it('should always have a gap of 20 unused address in a row', function () { + for (let i = 0; i < 10; i++) { + walletStore.addresses.external[`m/44'/1'/0'/0/${i}`].used = true + walletStore.addresses.internal[`m/44'/1'/0'/1/${i}`].used = true + } + + const generated = ensureAddressesToGapLimit(walletStore, walletType, accountIndex, getAddress) + expect(generated).to.equal(20); + expect(Object.keys(walletStore.addresses.external)) + .to.deep.equal(["m/44'/1'/0'/0/0", "m/44'/1'/0'/0/1", "m/44'/1'/0'/0/2", "m/44'/1'/0'/0/3", "m/44'/1'/0'/0/4", "m/44'/1'/0'/0/5", "m/44'/1'/0'/0/6", "m/44'/1'/0'/0/7", "m/44'/1'/0'/0/8", "m/44'/1'/0'/0/9", "m/44'/1'/0'/0/10", "m/44'/1'/0'/0/11", "m/44'/1'/0'/0/12", "m/44'/1'/0'/0/13", "m/44'/1'/0'/0/14", "m/44'/1'/0'/0/15", "m/44'/1'/0'/0/16", "m/44'/1'/0'/0/17", "m/44'/1'/0'/0/18", "m/44'/1'/0'/0/19", "m/44'/1'/0'/0/20", "m/44'/1'/0'/0/21", "m/44'/1'/0'/0/22", "m/44'/1'/0'/0/23", "m/44'/1'/0'/0/24", "m/44'/1'/0'/0/25", "m/44'/1'/0'/0/26", "m/44'/1'/0'/0/27", "m/44'/1'/0'/0/28", "m/44'/1'/0'/0/29"]); + expect(Object.keys(walletStore.addresses.internal)) + .to.deep.equal(["m/44'/1'/0'/1/0", "m/44'/1'/0'/1/1", "m/44'/1'/0'/1/2", "m/44'/1'/0'/1/3", "m/44'/1'/0'/1/4", "m/44'/1'/0'/1/5", "m/44'/1'/0'/1/6", "m/44'/1'/0'/1/7", "m/44'/1'/0'/1/8", "m/44'/1'/0'/1/9", "m/44'/1'/0'/1/10", "m/44'/1'/0'/1/11", "m/44'/1'/0'/1/12", "m/44'/1'/0'/1/13", "m/44'/1'/0'/1/14", "m/44'/1'/0'/1/15", "m/44'/1'/0'/1/16", "m/44'/1'/0'/1/17", "m/44'/1'/0'/1/18", "m/44'/1'/0'/1/19", "m/44'/1'/0'/1/20", "m/44'/1'/0'/1/21", "m/44'/1'/0'/1/22", "m/44'/1'/0'/1/23", "m/44'/1'/0'/1/24", "m/44'/1'/0'/1/25", "m/44'/1'/0'/1/26", "m/44'/1'/0'/1/27", "m/44'/1'/0'/1/28", "m/44'/1'/0'/1/29"]); + }); + it('should keep gap for each type ', function () { + for (let i = 0; i < 15; i++) { + walletStore.addresses.internal[`m/44'/1'/0'/1/${i}`].used = true + } + const generated = ensureAddressesToGapLimit(walletStore, walletType, accountIndex, getAddress) + expect(generated).to.equal(5); + }); + +}); diff --git a/packages/wallet-lib/src/utils/bip44/getMissingIndexes.js b/packages/wallet-lib/src/utils/bip44/getMissingIndexes.js new file mode 100644 index 00000000000..3600ca23d7d --- /dev/null +++ b/packages/wallet-lib/src/utils/bip44/getMissingIndexes.js @@ -0,0 +1,39 @@ +const is = require('../is'); + +module.exports = function getMissingIndexes(paths, fromOrigin = true) { + if (!is.arr(paths)) return false; + + let sortedIndexes = []; + + paths.forEach((path) => { + const splitedPath = path.split('/'); + const index = parseInt(splitedPath[5], 10); + sortedIndexes.push(index); + }); + + sortedIndexes = sortedIndexes.sort((a, b) => a - b); + + let missingIndex = sortedIndexes.reduce((acc, cur, ind, arr) => { + const diff = cur - arr[ind - 1]; + if (diff > 1) { + let i = 1; + while (i < diff) { + acc.push(arr[ind - 1] + i); + i += 1; + } + } + return acc; + }, []); + + // Will fix missing index before our first known indexes + if (fromOrigin) { + if (sortedIndexes[0] > 0) { + for (let i = sortedIndexes[0] - 1; i >= 0; i -= 1) { + missingIndex.push(i); + } + } + } + + missingIndex = missingIndex.sort((a, b) => a - b); + return missingIndex; +}; diff --git a/packages/wallet-lib/src/utils/bip44/isContiguousPath.js b/packages/wallet-lib/src/utils/bip44/isContiguousPath.js new file mode 100644 index 00000000000..919d87c8f58 --- /dev/null +++ b/packages/wallet-lib/src/utils/bip44/isContiguousPath.js @@ -0,0 +1,15 @@ +const is = require('../is'); + +module.exports = function isContiguousPath(currPath, prevPath) { + if (is.undef(currPath)) return false; + + const splitedCurrPath = currPath.split('/'); + const currIndex = parseInt(splitedCurrPath[5], 10); + + if (is.undef(prevPath)) { + return currIndex === 0; + } + const splitedPrevPath = prevPath.split('/'); + const prevIndex = parseInt(splitedPrevPath[5], 10); + return prevIndex === currIndex - 1; +}; diff --git a/packages/wallet-lib/src/utils/calculateDuffBalance.js b/packages/wallet-lib/src/utils/calculateDuffBalance.js new file mode 100644 index 00000000000..f32da7d67ea --- /dev/null +++ b/packages/wallet-lib/src/utils/calculateDuffBalance.js @@ -0,0 +1,32 @@ +/** + * + * @param walletId - The wallet Id where to perform the calculation + * @param accountIndex - The account Index where to perform the calculation + * @param type {{'confirmed','unconfirmed','total'}} Default: total. Calculate balance by utxo type. + * @return {number} Balance in duff + */ +module.exports = function calculateDuffBalance(addresses, chainStore, type = 'total') { + let totalSat = 0; + + addresses.forEach((address) => { + const addressData = chainStore.getAddress(address); + if (!addressData) { + return; + } + + switch (type) { + case 'total': + totalSat += addressData.balanceSat + addressData.unconfirmedBalanceSat; + break; + case 'confirmed': + totalSat += addressData.balanceSat; + break; + case 'unconfirmed': + totalSat += addressData.unconfirmedBalanceSat; + break; + default: + throw new Error(`Unexpected balance type. Got ${type}`); + } + }); + return totalSat; +}; diff --git a/packages/wallet-lib/src/utils/calculateTransactionFees.js b/packages/wallet-lib/src/utils/calculateTransactionFees.js new file mode 100644 index 00000000000..2d9a592bd2b --- /dev/null +++ b/packages/wallet-lib/src/utils/calculateTransactionFees.js @@ -0,0 +1,13 @@ +const is = require('./is'); + +function calculateTransactionFees(transaction) { + if (!is.dashcoreTransaction(transaction)) throw new Error('Expected a valid transaction'); + const { inputs, outputs } = transaction; + const inputAmount = inputs.reduce((acc, input) => { + if (!input.output) throw new Error('Expected transaction input to have the output specified'); + return acc + input.output.satoshis; + }, 0); + const outputAmount = outputs.reduce((acc, output) => (acc + output.satoshis), 0); + return transaction.isCoinbase() ? 0 : inputAmount - outputAmount; +} +module.exports = calculateTransactionFees; diff --git a/packages/wallet-lib/src/utils/calculateTransactionFees.spec.js b/packages/wallet-lib/src/utils/calculateTransactionFees.spec.js new file mode 100644 index 00000000000..ecf44b0701e --- /dev/null +++ b/packages/wallet-lib/src/utils/calculateTransactionFees.spec.js @@ -0,0 +1,25 @@ +const { expect } = require('chai'); +const { Transaction } = require('@dashevo/dashcore-lib'); +const calculateTransactionFees = require('./calculateTransactionFees'); + +const tx1 = '0300000001b51d5a6f5c7a680bce489e6f5a9b176ac85c49f10db4798867c7d1eb2036fbc3000000006a4730440220283fd42353767188532db4a4f1c3d0a9e96e313196ae1310af6d3006c7aa64ff022027fa50cf065c096f146e00516cb3e28a9bb387a6cf1103aae0592d5c882d25e5012102ba0588ffd3c838b715d7c79bcf1cff2ba69befd5ea52aa3474d66f094536cac0ffffffff0200131a4b000000001976a914838112cc6c85e074aa7f373e942c9f5240c3e13a88ac89959800000000001976a914f728c15b9a5fe4e6d7b6ed74b323e23f5c6e303f88ac00000000'; +const tx2 = '0300000001338540c64b794f73913f39f2d42d9139ce7c9d1c0ec5317c62ab4a28d6b0376f000000006b483045022100b996d726d224a762acf8ab3e37c085e796b44960b8e9933571ac57750e8ed05102201c6a36d72f16140d6a152be40add102d95a4ac5b177d300b10c277859690a859012103b5614f077d750a1eaffb23ca188dbcc7e267f4b8ffdedf81cdf970643027191bffffffff02008c8647000000001976a91414b05906daab037707927bc6c83900d5dbf2849688ac09869303000000001976a914791e51fff6554c18216c83d9ca81cf30cc66aff388ac00000000'; +describe('Utils - calculateTransactionFees', function suite() { + it('should ensure a valid transaction', function (){ + const transaction = null + expect(()=>calculateTransactionFees(transaction)).to.throw('Expected a valid transaction'); + }); + it('should ensure inputs and outputs are provided', function () { + const transaction = new Transaction(tx2); + expect(()=>calculateTransactionFees(transaction)).to.throw('Expected transaction input to have the output specified'); + }); + it('should correctly calculate transaction fees', function () { + const transaction1 = new Transaction(tx1); + const transaction2 = new Transaction(tx2); + // We specify the output for the tx2 input. + transaction2.inputs[0].output = transaction1.outputs[0]; + const fees = calculateTransactionFees(transaction2); + expect(fees).to.equal(247); + }); + +}); diff --git a/packages/wallet-lib/src/utils/castStorageItemsTypes.js b/packages/wallet-lib/src/utils/castStorageItemsTypes.js new file mode 100644 index 00000000000..44232a62dbf --- /dev/null +++ b/packages/wallet-lib/src/utils/castStorageItemsTypes.js @@ -0,0 +1,54 @@ +const castStorageItemsTypes = (originalItem, schema) => { + if (!schema) { + throw new Error('Schema is undefined'); + } + + return Object.entries(schema).reduce((acc, next) => { + const [schemaKey, schemaValue] = next; + const result = {}; + + if (schemaKey !== '*' && originalItem[schemaKey] === undefined) { + throw new Error(`No item found for schema key "${schemaKey}" in item ${JSON.stringify(originalItem)}`); + } + + if (schemaValue.constructor.name !== 'Object') { + let castItem; + + if (typeof schemaValue === 'string') { + castItem = (itemToCast) => { + // eslint-disable-next-line valid-typeof + if (typeof itemToCast !== schemaValue) { + throw new Error(`Value "${itemToCast}" is not of type "${schemaValue}"`); + } + return itemToCast; + }; + } else if (typeof schemaValue === 'function') { + castItem = schemaValue; + } else { + castItem = (itemToCast) => { + const Clazz = schemaValue; + return new Clazz(itemToCast); + }; + } + + if (schemaKey === '*') { + Object.keys(originalItem).forEach((itemKey) => { + result[itemKey] = castItem(originalItem[itemKey]); + }); + } else { + result[schemaKey] = castItem(originalItem[schemaKey]); + } + } else if (schemaKey === '*') { + Object + .entries(originalItem) + .forEach(([key, value]) => { + result[key] = castStorageItemsTypes(value, schemaValue); + }, {}); + } else { + result[schemaKey] = castStorageItemsTypes(originalItem[schemaKey], schemaValue); + } + + return { ...acc, ...result }; + }, {}); +}; +module.exports = castStorageItemsTypes; diff --git a/packages/wallet-lib/src/utils/castStorageItemsTypes.spec.js b/packages/wallet-lib/src/utils/castStorageItemsTypes.spec.js new file mode 100644 index 00000000000..a5e96e6b29b --- /dev/null +++ b/packages/wallet-lib/src/utils/castStorageItemsTypes.spec.js @@ -0,0 +1,90 @@ +const _ = require('lodash'); +const {expect} = require('chai'); +const ChainStore = require('../../src/types/ChainStore/ChainStore'); +const castItemTypes = require('./castStorageItemsTypes'); +const {BlockHeader, Transaction} = require('@dashevo/dashcore-lib') +const WalletStore = require("../types/WalletStore/WalletStore"); + +const mockChainStorage = { + "blockHeaders": { + "fakeBlockHash": "000000206ff6709b4816a98a7601bc9626a597191fe4f788228a037de3bd839e811f4913c49de57ac9553f0cd2529c94eaa9781273f315a792c4148eb7e3756c9cd7e1ce40285562ffff7f2000000000" + }, + "transactions": { + "fakeTxHash": "0300000001e569f827418be2e49f5ae4a34d30ff3bc5abc723f72b0e10712598dff1e70689010000006b483045022100f9aebe9bcfaa8208f1486ad4d15f65730b5adc0cb02c1d2bd8836a4582e2ea7f02200250bbfe524f70324417237549f421c2c3584d6b532194dbac8043e46900753701210387f3d1ff9e6a06db60bd61d0757002836407e8a3b1094b446690d13392c3fb9affffffff02e8030000000000001976a9148d0ba6247ad4988ba70fdcb56bb5f45b6e49423d88aca351c253040000001976a91471a97f71915e7c1d4460ad520511761b06534d8088ac00000000" + }, + "instantLocks": {}, + "txMetadata": { + "fakeTxHash": { + "blockHash": "0eca27f921836079a85f41b134679cd557fab1f66b2e60013b873eb56e7b3f2d", + "height": 5409, + "isInstantLocked": true, + "isChainLocked": true + } + }, + "fees": { + "minRelay": -1 + } +} + +const mockWalletStorage = { + "lastKnownBlock": { + "height": 11703 + } +} + +describe('Utils - castStorageItemsTypes', function suite() { + it('should proceed with valid schema', function () { + const chainStore = castItemTypes(mockChainStorage, ChainStore.prototype.SCHEMA) + + expect(chainStore.blockHeaders.fakeBlockHash instanceof BlockHeader).to.be.true + expect(chainStore.transactions.fakeTxHash instanceof Transaction).to.be.true + expect(chainStore.txMetadata.fakeTxHash.isInstantLocked).to.be.true + expect(chainStore.txMetadata.fakeTxHash.isChainLocked).to.be.true + expect(typeof chainStore.txMetadata.fakeTxHash.height).to.be.equal('number') + + const walletStore = castItemTypes(mockWalletStorage, WalletStore.prototype.SCHEMA) + + expect(walletStore.lastKnownBlock.height).to.be.equal(11703) + }); + + it('should throw if no schema passed', function () { + expect(() => castItemTypes(mockChainStorage, null)) + .to.throw(Error, 'Schema is undefined') + }); + + it('should throw if invalid primitive value passed', function () { + const mockWalletStorageWithWrongType = _.cloneDeep(mockWalletStorage) + mockWalletStorageWithWrongType.lastKnownBlock.height = '11703' + expect(() => castItemTypes(mockWalletStorageWithWrongType, WalletStore.prototype.SCHEMA)) + .to.throw(Error, 'Value "11703" is not of type "number"'); + }); + + it('should throw if invalid object value passed', function () { + const mockChainStorageWithUnknownKeys = _.cloneDeep(mockChainStorage) + mockChainStorageWithUnknownKeys.txMetadata.unknownKey = true + + expect(() => castItemTypes(mockChainStorageWithUnknownKeys, ChainStore.prototype.SCHEMA)) + .to.throw('No item found for schema key "blockHash" in item true') + }); + + it('should throw if invalid uniform object with primitives passed', function () { + const schema = { + '*': 'boolean' + } + const items = { + '1': true, + '2': 'false' + } + + expect(() => castItemTypes(items, schema)) + .to.throw(Error, 'Value "false" is not of type "boolean"'); + }); + + it('should throw if some of the keys are missing from the storage', function () { + const mockWalletChainStorageWithMissingKeys = _.cloneDeep(mockChainStorage) + mockWalletChainStorageWithMissingKeys.txMetadata = undefined + + expect(() => castItemTypes(mockWalletChainStorageWithMissingKeys, ChainStore.prototype.SCHEMA)) + .to.throw(Error, 'No item found for schema key "txMetadata" in item'); + }); +}); diff --git a/packages/wallet-lib/src/utils/categorizeTransactions.js b/packages/wallet-lib/src/utils/categorizeTransactions.js new file mode 100644 index 00000000000..77f162043a3 --- /dev/null +++ b/packages/wallet-lib/src/utils/categorizeTransactions.js @@ -0,0 +1,218 @@ +const { each } = require('lodash'); +const classifyAddresses = require('./classifyAddresses'); +const { TRANSACTION_HISTORY_TYPES } = require('../CONSTANTS'); + +// TODO: On a private key based wallet, as change and external is similar, +// we actually cannot differentiate correctly from an address_transfer +// and a sent transaction where our own address is a change... +const determineType = (inputsDetection, outputsDetection) => { + let type = TRANSACTION_HISTORY_TYPES.UNKNOWN; + + // We first discriminate with account transfer from or to another account + if (inputsDetection.hasOtherAccountAddress + && !inputsDetection.hasOwnAddress) { + type = TRANSACTION_HISTORY_TYPES.ACCOUNT_TRANSFER; + } else if (inputsDetection.hasOwnAddress + && outputsDetection.hasOtherAccountAddress + ) { + type = TRANSACTION_HISTORY_TYPES.ACCOUNT_TRANSFER; + } else if (inputsDetection.hasOwnAddress + && !outputsDetection.hasUnknownAddress + && !outputsDetection.hasOtherAccountAddress) { + // Detecting an address transfer is the second element we need to discriminate + type = TRANSACTION_HISTORY_TYPES.ADDRESS_TRANSFER; + } else { + if (inputsDetection.hasExternalAddress) { + type = TRANSACTION_HISTORY_TYPES.RECEIVED; + } + if (outputsDetection.hasExternalAddress && !inputsDetection.hasExternalAddress) { + type = TRANSACTION_HISTORY_TYPES.RECEIVED; + } + if ( + outputsDetection.hasUnknownAddress + && (inputsDetection.hasOwnAddress) + ) { + type = TRANSACTION_HISTORY_TYPES.SENT; + } + } + + return type; +}; + +function categorizeTransactions( + transactionsWithMetadata, + walletStore, + accountIndex, + walletType, + network = 'testnet', +) { + const categorizedTransactions = []; + + const { + externalAddressesList, + internalAddressesList, + otherAccountAddressesList, + } = classifyAddresses(walletStore, accountIndex, walletType, network); + + each(transactionsWithMetadata, (transactionWithMetadata) => { + const [transaction, metadata] = transactionWithMetadata; + const from = []; + const to = []; + + let outputsHasChangeAddress = false; + let outputsHasExternalAddress = false; + let outputsHasOtherAccountAddress = false; + let outputsHasOwnAddress = false; + let outputsHasUnknownAddress = false; + + let inputsHasChangeAddress = false; + let inputsHasExternalAddress = false; + let inputsHasOtherAccountAddress = false; + let inputsHasOwnAddress = false; + let inputsHasUnknownAddress = false; + + /** + * Total duffs amount sent with current account (if any) + * @type {number} + */ + let totalAccountInput = 0; + + /** + * Total duffs amount within the tx outputs + * @type {number} + */ + let totalTxOutput = 0; + + /** + * Output balance impact + * @type {number} + */ + let satoshisBalanceImpact = 0; + + /** + * Fee balance impact (in case TX sent with the current account) + * @type {number} + */ + let feeImpact = 0; + + // For each vin, we will look at matching known addresses + // In order to know the value in, we would require fetching tx for output of vin info + transaction.inputs.forEach((vin) => { + const { script } = vin; + + // Ignore coinbase inputs + if (!script) { + return; + } + + const address = script.toAddress(network).toString(); + let addressType = 'unknown'; + if (address) { + if (internalAddressesList.includes(address)) { + addressType = 'internal'; + inputsHasChangeAddress = true; + inputsHasOwnAddress = true; + } else if (externalAddressesList.includes(address)) { + addressType = 'external'; + inputsHasExternalAddress = true; + inputsHasOwnAddress = true; + } else if (otherAccountAddressesList.includes(address)) { + addressType = 'otherAccount'; + inputsHasOtherAccountAddress = true; + } else inputsHasUnknownAddress = true; + + from.push({ + address, + addressType, + }); + + // Calculates total input amount coming from address belonging to the wallet account + const isSendTx = addressType === 'internal' || addressType === 'external'; + if (isSendTx) { + const { prevTxId, outputIndex } = vin; + const prevTxHash = prevTxId.toString('hex'); + const prevTx = transactionsWithMetadata.find(([tx]) => tx.hash === prevTxHash); + + // Previous tx might not be in the app state because of + // `skipSynchronizationBeforeHeight` option + if (prevTx) { + totalAccountInput += prevTx[0].outputs[outputIndex].satoshis; + } + } + } + }); + + // For each vout, we will look at matching known addresses + transaction.outputs.forEach((vout) => { + const { satoshis, script } = vout; + totalTxOutput += satoshis; + const address = script.toAddress(network).toString(); + let addressType = 'unknown'; + if (address) { + if (internalAddressesList.includes(address)) { + addressType = 'internal'; + outputsHasChangeAddress = true; + outputsHasOwnAddress = true; + } else if (externalAddressesList.includes(address)) { + addressType = 'external'; + outputsHasExternalAddress = true; + outputsHasOwnAddress = true; + } else if (otherAccountAddressesList.includes(address)) { + addressType = 'otherAccount'; + outputsHasOtherAccountAddress = true; + } else outputsHasUnknownAddress = true; + to.push({ + address, + satoshis, + addressType, + }); + + const accountOutput = addressType === 'internal' || addressType === 'external'; + const receivedFromUnknown = accountOutput && totalAccountInput === 0; + const sentToUnknown = !accountOutput && totalAccountInput > 0; + + if (receivedFromUnknown) { + satoshisBalanceImpact += satoshis; + } else if (sentToUnknown) { + satoshisBalanceImpact -= satoshis; + } + } + }); + + const type = determineType({ + hasChangeAddress: inputsHasChangeAddress, + hasExternalAddress: inputsHasExternalAddress, + hasOtherAccountAddress: inputsHasOtherAccountAddress, + hasOwnAddress: inputsHasOwnAddress, + hasUnknownAddress: inputsHasUnknownAddress, + }, { + hasChangeAddress: outputsHasChangeAddress, + hasExternalAddress: outputsHasExternalAddress, + hasOtherAccountAddress: outputsHasOtherAccountAddress, + hasOwnAddress: outputsHasOwnAddress, + hasUnknownAddress: outputsHasUnknownAddress, + }); + + if (totalAccountInput > 0) { + feeImpact = totalAccountInput - totalTxOutput; + } + + const categorizedTransaction = { + from, + to, + transaction, + type, + blockHash: metadata.blockHash, + height: metadata.height, + isInstantLocked: metadata.isInstantLocked, + isChainLocked: metadata.isChainLocked, + satoshisBalanceImpact, + feeImpact, + }; + categorizedTransactions.push(categorizedTransaction); + }); + + return categorizedTransactions; +} + +module.exports = categorizeTransactions; diff --git a/packages/wallet-lib/src/utils/categorizeTransactions.spec.js b/packages/wallet-lib/src/utils/categorizeTransactions.spec.js new file mode 100644 index 00000000000..4d9e71c22a4 --- /dev/null +++ b/packages/wallet-lib/src/utils/categorizeTransactions.spec.js @@ -0,0 +1,115 @@ +const {expect} = require('chai'); +const {Transaction} = require('@dashevo/dashcore-lib'); +const {each} = require('lodash'); +const {WALLET_TYPES} = require('../CONSTANTS'); + +const categorizeTransactions = require('./categorizeTransactions'); +const transactionsWithMetadataFixtures = require('../../fixtures/wallets/apart-trip-dignity/transactions-with-metadata.json'); +const expectedResults = require('../../fixtures/wallets/apart-trip-dignity/categorizeTransactions.expectedResults'); +const getFixtureHDAccountWithStorage = require("../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage"); + +const mockedHDAccount = getFixtureHDAccountWithStorage(); + +const prepareTransactionsWithMetadata = () => { + const transactionsWithMetadata = []; + each(transactionsWithMetadataFixtures, (transactionWithMetadataFixture) => { + transactionsWithMetadata.push([new Transaction(transactionWithMetadataFixture[0]), transactionWithMetadataFixture[1]]) + }); + return transactionsWithMetadata; +}; +const normalizeResults = (results) =>{ + return [...results].map((result)=>{ + result.transaction = result.transaction.toString() + return result; + }) +} +/** + * Fixtures data from real transactions on testnet. + * Tx perform as follow (where [account, address] : + * + * TX 1 (1.8798) : Faucet -> [0,0](yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7-a43845e580ad01f31bc06ce47ab39674e40316c4c6b765b6e54d6d35777ef456) + * TX 2 (0.1) : ExternalUser -> [0,1](yercyhdN9oEkZcB9BsW5ktFaDxFEuK6qXN-d37b6c7dd449d605bea9997af8bbeed2f3fbbcb23a4068b1f1ad694db801912d) + * TX 3 (0.1) : ExternalUser -> [0,2](ygk3GCSba2J3L9G665Snozhj9HSkh5ByVE-7d1b78157f9f2238669f260d95af03aeefc99577ff0cddb91b3e518ee557a2fd) + * TX 4 (8.4001) : Faucet -> [0,5](yMLhEsiP2ajSh8STmXnNmkWXtoHsmawZxd-eb1a7fc8e3b43d3021653b1176f8f9b41e9667d05b65ee225d14c149a5b14f77) + * TX 5 (7.2921) : Faucet -> [0,4](ygHAVkMtYSqoTWHebDv7qkhMV6dHyuRsp2-1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b) + * TX 6 (17.771) : [0,0][0,1][0,2][0,4][0,5] -> [0,6](yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv-f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8) + * TX 7 (16.7) : [0,6] -> [0,6][0,7](yhaAB6e8m3F8zmGX7WAVYa6eEfmSrrnY8x-c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5) + * TX 8 (12.6) : [0,6] -> [1,0](yYJmzWey5kNecAThet5BFxAga1F4b4DKQ2-6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533) + * TX 9 (12) [1,0] -> [1,1](yNCqctyQaq51WU1hN5aNwsgMsZ5fRiB7GY-9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840) + * TX 10 (11.5) [1,1] -> [0,8](yiXh4Yo5djG6QH8WzXkKm5EFzqLRJWakXz-6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135) + * TX 11 (10) [0,8] -> ExternalUser (yMX3ycrLVF2k6YxWQbMoYgs39aeTfY4wrB-e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841) + * + * Therefore, we expect TransactionHistory to get us this exact order. + * Fixture data are un-ordered as such [TXNo, FixtureElementNo]. + * [1,2][2,4][3,5][4,1][5,0][6,3][7,6][8,7][9,10][10,8][11,9] + * + */ +describe('Utils - categorizeTransactions', function suite() { + const transactionsWithMetadata = prepareTransactionsWithMetadata(); + + const accountStore = mockedHDAccount.storage.getWalletStore(mockedHDAccount.walletId); + const accountIndex = 0; + const walletType = WALLET_TYPES.HDWALLET; + + const set1 = [transactionsWithMetadata[2]]; + const expectedSetResult1 = [expectedResults[0]]; + const set2 = [transactionsWithMetadata[2], transactionsWithMetadata[4]]; + const expectedSetResult2 = [expectedResults[0], expectedResults[1]]; + const set3 = [transactionsWithMetadata[2], transactionsWithMetadata[4], transactionsWithMetadata[5]]; + const expectedSetResult3 = [expectedResults[0], expectedResults[1], expectedResults[2]]; + const set4 = [transactionsWithMetadata[2], transactionsWithMetadata[4], transactionsWithMetadata[5], transactionsWithMetadata[1]]; + const expectedSetResult4 = [expectedResults[0], expectedResults[1], expectedResults[2], expectedResults[3]]; + const set5 = [transactionsWithMetadata[2], transactionsWithMetadata[4], transactionsWithMetadata[5], transactionsWithMetadata[1], transactionsWithMetadata[0]]; + const expectedSetResult5 = [expectedResults[0], expectedResults[1], expectedResults[2], expectedResults[3], expectedResults[4]]; + const set6 = [transactionsWithMetadata[2], transactionsWithMetadata[4], transactionsWithMetadata[5], transactionsWithMetadata[1], transactionsWithMetadata[0], transactionsWithMetadata[3]]; + const expectedSetResult6 = [expectedResults[0], expectedResults[1], expectedResults[2], expectedResults[3], expectedResults[4], expectedResults[5]]; + const set7 = [transactionsWithMetadata[2], transactionsWithMetadata[4], transactionsWithMetadata[5], transactionsWithMetadata[1], transactionsWithMetadata[0], transactionsWithMetadata[3], transactionsWithMetadata[6]]; + const expectedSetResult7 = [expectedResults[0], expectedResults[1], expectedResults[2], expectedResults[3], expectedResults[4], expectedResults[5], expectedResults[6]]; + + const set8 = [transactionsWithMetadata[2], transactionsWithMetadata[4], transactionsWithMetadata[5], transactionsWithMetadata[1], transactionsWithMetadata[0], transactionsWithMetadata[3], transactionsWithMetadata[6], transactionsWithMetadata[7]]; + const expectedSetResult8 = [expectedResults[0], expectedResults[1], expectedResults[2], expectedResults[3], expectedResults[4], expectedResults[5], expectedResults[6], expectedResults[7]]; + + const set9 = [transactionsWithMetadata[2], transactionsWithMetadata[4], transactionsWithMetadata[5], transactionsWithMetadata[1], transactionsWithMetadata[0], transactionsWithMetadata[3], transactionsWithMetadata[6], transactionsWithMetadata[7], transactionsWithMetadata[10]]; + const expectedSetResult9 = [expectedResults[0], expectedResults[1], expectedResults[2], expectedResults[3], expectedResults[4], expectedResults[5], expectedResults[6], expectedResults[7], expectedResults[8]]; + const set10 = [transactionsWithMetadata[2], transactionsWithMetadata[4], transactionsWithMetadata[5], transactionsWithMetadata[1], transactionsWithMetadata[0], transactionsWithMetadata[3], transactionsWithMetadata[6], transactionsWithMetadata[7], transactionsWithMetadata[10], transactionsWithMetadata[8]]; + const expectedSetResult10 = [expectedResults[0], expectedResults[1], expectedResults[2], expectedResults[3], expectedResults[4], expectedResults[5], expectedResults[6], expectedResults[7], expectedResults[8], expectedResults[9]]; + const set11 = [transactionsWithMetadata[2], transactionsWithMetadata[4], transactionsWithMetadata[5], transactionsWithMetadata[1], transactionsWithMetadata[0], transactionsWithMetadata[3], transactionsWithMetadata[6], transactionsWithMetadata[7],transactionsWithMetadata[10], transactionsWithMetadata[8], transactionsWithMetadata[9]]; + const expectedSetResult11 = [expectedResults[0], expectedResults[1], expectedResults[2], expectedResults[3], expectedResults[4], expectedResults[5], expectedResults[6], expectedResults[7], expectedResults[8], expectedResults[9],expectedResults[10]]; + + + + it('should correctly categorize transaction', function () { + const result1 = categorizeTransactions(set1, accountStore, accountIndex, walletType); + expect(normalizeResults(result1)).to.deep.equal(expectedSetResult1) + + const result2 = categorizeTransactions(set2, accountStore, accountIndex, walletType); + expect(normalizeResults(result2)).to.deep.equal(expectedSetResult2) + + const result3 = categorizeTransactions(set3, accountStore, accountIndex, walletType); + expect(normalizeResults(result3)).to.deep.equal(expectedSetResult3) + + const result4 = categorizeTransactions(set4, accountStore, accountIndex, walletType); + expect(normalizeResults(result4)).to.deep.equal(expectedSetResult4) + + const result5 = categorizeTransactions(set5, accountStore, accountIndex, walletType); + expect(normalizeResults(result5)).to.deep.equal(expectedSetResult5) + + const result6 = categorizeTransactions(set6, accountStore, accountIndex, walletType); + expect(normalizeResults(result6)).to.deep.equal(expectedSetResult6) + + const result7 = categorizeTransactions(set7, accountStore, accountIndex, walletType); + expect(normalizeResults(result7)).to.deep.equal(expectedSetResult7) + + const result8 = categorizeTransactions(set8, accountStore, accountIndex, walletType); + expect(normalizeResults(result8)).to.deep.equal(expectedSetResult8) + + const result9 = categorizeTransactions(set9, accountStore, accountIndex, walletType); + expect(normalizeResults(result9)).to.deep.equal(expectedSetResult9) + + const result10 = categorizeTransactions(set10, accountStore, accountIndex, walletType); + expect(normalizeResults(result10)).to.deep.equal(expectedSetResult10) + + const result11 = categorizeTransactions(set11, accountStore, accountIndex, walletType); + expect(normalizeResults(result11)).to.deep.equal(expectedSetResult11) + }); +}); diff --git a/packages/wallet-lib/src/utils/classifyAddresses.js b/packages/wallet-lib/src/utils/classifyAddresses.js new file mode 100644 index 00000000000..c6f6f55d297 --- /dev/null +++ b/packages/wallet-lib/src/utils/classifyAddresses.js @@ -0,0 +1,49 @@ +const { WALLET_TYPES, BIP44_LIVENET_ROOT_PATH, BIP44_TESTNET_ROOT_PATH } = require('../CONSTANTS'); + +function classifyAddresses(walletStore, accountIndex, walletType, network = 'testnet') { + const externalAddressesList = []; + const internalAddressesList = []; + const otherAccountAddressesList = []; + const miscAddressesList = []; + + const rootPath = (network.toString() === 'testnet') + ? BIP44_TESTNET_ROOT_PATH + : BIP44_LIVENET_ROOT_PATH; + + const accountsPaths = [...walletStore.state.paths.keys()]; + + const isHDWallet = [ + WALLET_TYPES.HDWALLET, + WALLET_TYPES.HDPRIVATE, + WALLET_TYPES.HDPUBLIC].includes(walletType); + + const currentAccountPath = (isHDWallet) ? `${rootPath}/${accountIndex}'` : `m/${accountIndex}`; + + accountsPaths.forEach((accountPath) => { + const isCurrentAccountPath = accountPath === currentAccountPath; + const accountPaths = walletStore.getPathState(accountPath); + + Object.entries(accountPaths.addresses) + .forEach(([path, address]) => { + if (isCurrentAccountPath) { + if (isHDWallet) { + if (path.startsWith('m/0')) externalAddressesList.push(address); + else if (path.startsWith('m/1')) internalAddressesList.push(address); + else miscAddressesList.push(address); + } else { + externalAddressesList.push(address); + } + } else { + otherAccountAddressesList.push(address); + } + }); + }); + + return { + externalAddressesList, + internalAddressesList, + otherAccountAddressesList, + miscAddressesList, + }; +} +module.exports = classifyAddresses; diff --git a/packages/wallet-lib/src/utils/classifyAddresses.spec.js b/packages/wallet-lib/src/utils/classifyAddresses.spec.js new file mode 100644 index 00000000000..0e7c9e83b84 --- /dev/null +++ b/packages/wallet-lib/src/utils/classifyAddresses.spec.js @@ -0,0 +1,25 @@ +const {expect} = require('chai'); +const {WALLET_TYPES} = require('../CONSTANTS'); +const classifyAddresses = require('./classifyAddresses'); +const getFixtureHDAccountWithStorage = require("../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage"); + +const mockedHDAccount = getFixtureHDAccountWithStorage(); + +describe('Utils - classifyAddresses', function suite() { + it('should correctly classify address for HDWallet', function () { + const walletType = WALLET_TYPES.HDWALLET; + const accountIndex = 0; + const result = classifyAddresses(mockedHDAccount.storage.getWalletStore(mockedHDAccount.walletId), accountIndex, walletType); + const expectedResult = { + "externalAddressesList": ["yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7", "yercyhdN9oEkZcB9BsW5ktFaDxFEuK6qXN", "ygk3GCSba2J3L9G665Snozhj9HSkh5ByVE", "ybuL6rM6dgrKzCg8s99f3jxGuv5oz5JcDA", "ygHAVkMtYSqoTWHebDv7qkhMV6dHyuRsp2", "yMLhEsiP2ajSh8STmXnNmkWXtoHsmawZxd", "yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv", "yhaAB6e8m3F8zmGX7WAVYa6eEfmSrrnY8x", "yiXh4Yo5djG6QH8WzXkKm5EFzqLRJWakXz", "yQYv3Um6DsdtANo1ZPTUte75wAGMstLRex", "yiYPJmu7eEm1cXUNumQRdjv1fvPhsfgMS4", "yii4aUZhNfL6EWN9KAgAFrJzGJmqHnF4wx", "yLpTquSct2SGz2Ka45uTPDd81Kzro2Jt2k", "yMiJtpzb1Qthy9TGnavsf5NZ6EZZa4j9q3", "yacgSfW7RkwWakEZPg8USAVdzCypiG3vxS", "yVvrmoRPFLy6nUpCQBT8ZExxF5wF3DhiGU", "yaJf2aG6cFUtfv4o6TuEKsh5kr4xq5iAY4", "yfardJQ4ucgWLKQPaRHGMRMbSGm5H4ExJR", "yLSCqx7dcM5JKR2fG7vHbF2axMvuYqomaw", "yVij8XpJ78LM5hepSV1KF7T8vRpUEXCpK5", "ydJpjuJGossAZR7S5oS7cWvjygEwoj8Xwp", "yW3TmWnmhvpxRbgFcQ8oXqDRkn3RhRH6jj", "yRegVX85DThKRkH8C61TtRacfzrkiBfNy5", "yPtDCqDFRe1JuDp8pvdiEMQMz2erGwS3VG", "yM9pSw3L4oBfG7uQL5o522Hu3WTvy9awgZ", "yNC6qYJYungzuk5XUynDFKCn54Dy8ngox4", "yR5KcLr1bceLT4teTk2qoJx6pFLik1zyzL", "yRrKLGJa9JmdjBWvrHtedKjHTao6CRDTKf", "yP5dShZBydpbEzgGoXL6kcjv2KzervRrYB"], + "internalAddressesList": ["yNDpPsJqXKM36zHSNEW7c1zSvNnrZ699FY", "yLk4Hw3w4zDudrDVP6W8J9TggkY57zQUki", "yirJaK8KCE5YAmwvLadizqFw3TCXqBuZXL", "yhdRfg5gNr587dtEC4YYMcSHmLVEGqqtHc", "yYwKP1FQae5kbjXkmuirGx6Xzf8NzHpLqW", "yX9gmsm8aSxZZjYhq4w35aidT7qbhcpNjU", "ybgXCTGMHEBbQeUib8c3xAjtGAc12XtWiU", "yS31WpdMT2b34uL9C37fbUoACHhiupHCyP", "yTSpFqRoX3vyN286AUtKKhgmX5Xb41YKQe", "yQU5YsqN7psTTASuYbcMi7N5nNZGaxXb2X", "yVGGFj9BLgEab5rucSGLC6UGVLQKB4U1wJ", "yQCh5yYCHEbJzgSJE9rdHiqXHidKm3kwr5", "yX7T3Ac3yaLk5CTC5UaR93Fc7SjYkeT5hn", "yXx3WXq8kYNPbYEg5U6bL8Xfih4g5LCYVo", "yYnLMTz3jCi2KKKNuo3TVkEAGyUFg8tgkJ", "yiKa1dA6B4tSTNJqJP9Y5pQfQEffnQQDTL", "yf7vcuDnE9DVhXdMfBMQQTEi43otYQzkWE", "yTmSmocwERCeRHqNNG5SbpYKUra1HTmj8m", "yivUe5NeJsGsREwPQZUGYaTSwWB3E1oLcz", "ygfsZojdfW9UjCRU4ra95Aq6YgCC7UqZFx", "yU9fdXaUVtefwDZvxjJAr9xj1z2MtYi34A", "yXgMN6FgrgZCnTN1vhoZMh8afKMBmi3JC4", "yiqaCbXscvR8y3VFYMzdaKCaAGuDuZxMzt", "ydcgWDxheSxrLAqDBP4JXBndMCzUNf77gq"], + "otherAccountAddressesList": ["yYJmzWey5kNecAThet5BFxAga1F4b4DKQ2", "yNCqctyQaq51WU1hN5aNwsgMsZ5fRiB7GY", "yNPbYz5cZKw2EwxtkL3VSVzPi2FYp9VKjQ", "ybsGWzsnSCAZufgSeUjScVxqEdved99UM2", "yfNHuPojk8XKWP5nuueDptX4nM7qToudgx", "yXxLnDkk6s8h1PSnYaFM6MAyRarc1Kc1rY", "yipResSzN2zUvL7UYkmptKKmQTv7sNssRn", "yZPtNwimHdRiKYbNQW49qezw1Kc1YwUJeT", "yPMjYYfQbga2nBiuqqfUyX41U1vwRZ8fG8", "yLueLWWcLQsaXQ8D5o9tcyo8tfTxMWXvG4", "yN8gzgsc1RVjXThMQT5qZH2jjpnMymz6zP", "yPQLWBNwMdLxUW2oUwHGwQtfyYxD41BARJ", "yg5g2AfWFdwWexWGfbSXYbUHf1y5WWrFPs", "yWyABu4naV1Jzw7w9sn1gqhebPRSkCndsS", "ycuUPzUBjhKyUjezQR1LNot79a6C4aRLaR", "yQ7YjvAXgDAUCekveHVjr6NBveXrUemVno", "yi8bghcw627cMGpuH4bJqH6bqR5ywv1NLH", "yizHu8i2rfwzwBgnJ62s2WUe6wLoDjne6N", "yW1u3tySeUKAKJsz7sjZFyjUiTyKLB6xBv", "yNaSkdy1Q8JNubUdbLMGsGf7sTRofEJYZq", "yXMrw79LPgu78EJsfGGYpm6fXKc1EMnQ49", "yh6Hcyipdvp6WJpQxjNbaXP4kzPQUJpY3n", "yNphpXuaTZRpU9FBh2W7NkUYcr3kBDE8me", "yXFppDT59xYD41mT2pmAdnvr7aZEFdgdrN", "yeKGAiiEHBGRujvLoYewA77jDDpeDamxvF", "yaxTG66CVzKgHhHZXojRHC9ztLTvz3fwdT", "yYw6qU7dwGoELZkSTj3oSKRpM4U8qTMc1U", "yQE2MksEnSfbeNre19oja9Jj8tvpj64C5a", "yaRnvHo8oLvVmv46vMj5XPbDJouQSnmcLT", "yj5ofWf2uYQQkSavYm2WXgu1QkaZCyP3Cm", "yUCjGmEwrHJwNDrE1o2rMre6MkSbiE6yz7", "yfJzd1nE2rEqz5XEurD6vs4ykizwmw9xTv", "yUk8U3jRZMHKVTa1eFDEtZpa1G4E13FP4d", "yMr59YWQFCADq4FbWrtxDUtMwwshSrmAyK", "yetSehBupzGS9yps5ogqARUGmTMAs2xVcQ", "yNcESKLwriNrhM6EyoSpZEXrzdY3uht92T", "yN2FihGU7KdaEspp39bKrhsHypeyeYzoM2", "yirpWLxHuhwFzA6LfUPKUh1Ke9RB9BUjit", "yVDN66vvdshWdNzhUaQNB6xExAHkzs1zj8", "yPzofnEhRVfDisL2nCUJtAoSHkuyMirHZS"], + "miscAddressesList": [] + } + + expect(result.externalAddressesList).to.deep.equal(expectedResult.externalAddressesList); + expect(result.internalAddressesList).to.deep.equal(expectedResult.internalAddressesList); + expect(result.otherAccountAddressesList).to.deep.equal(expectedResult.otherAccountAddressesList); + expect(result.miscAddressesList).to.deep.equal(expectedResult.miscAddressesList); + }); +}); diff --git a/packages/wallet-lib/src/utils/coinSelection.js b/packages/wallet-lib/src/utils/coinSelection.js new file mode 100644 index 00000000000..7c3b0926600 --- /dev/null +++ b/packages/wallet-lib/src/utils/coinSelection.js @@ -0,0 +1 @@ +module.exports = require('./coinSelections/index'); diff --git a/packages/wallet-lib/src/utils/coinSelection.spec.js b/packages/wallet-lib/src/utils/coinSelection.spec.js new file mode 100644 index 00000000000..11b6ed9c865 --- /dev/null +++ b/packages/wallet-lib/src/utils/coinSelection.spec.js @@ -0,0 +1,374 @@ +const {expect} = require('chai'); +const {Transaction, Address, Script} = require('@dashevo/dashcore-lib'); +const coinSelection = require('./coinSelection'); +const {utxosList} = require('../../fixtures/crackspice'); +const STRATEGIES = require('./coinSelections/strategies'); +const TransactionEstimator = require('./coinSelections/TransactionEstimator') + +const utxosListAsUnspentOutput = utxosList.map((utxo)=> Transaction.UnspentOutput(utxo)); +const outputs = { + ONE_DASH: { + satoshis: 100000000, + address: new Address('ybefxSHaEbDATvq5gVCxjV375NWus3ttV7'), + }, + HUNDRED_DASH: { + satoshis: 10000000000, + address: new Address('ybefxSHaEbDATvq5gVCxjV375NWus3ttV7'), + }, + TWENTY_FIVE_DASH: { + satoshis: 2500000000, + address: new Address('ybefxSHaEbDATvq5gVCxjV375NWus3ttV7'), + }, + FOURTY_FIVE_DASH: { + satoshis: 4500000000, + address: new Address('ybefxSHaEbDATvq5gVCxjV375NWus3ttV7'), + }, + MILLION_DASH: { + satoshis: 100000000000000, + address: new Address('ybefxSHaEbDATvq5gVCxjV375NWus3ttV7'), + }, +}; +describe('Utils - coinSelection', function suite() { + this.timeout(10000); + it('should require a utxosList', () => { + expect(() => coinSelection()).to.throw('A utxosList is required'); + }); + it('should require a utxosList as an array', () => { + expect(() => coinSelection({})).to.throw('UtxosList is expected to be an array of utxos'); + }); + it('should require a utxosList with at least one utxo', () => { + expect(() => coinSelection([])).to.throw('utxosList must contain at least 1 utxo'); + }); + + it('should require a utxosList with valid utxo', () => { + expect(() => coinSelection([{ + toto: true, + }])).to.throw('An outputsList is required in order to perform a selection'); + }); + + + it('should require a outputsList', () => { + expect(() => coinSelection(utxosList)).to.throw('An outputsList is required in order to perform a selection'); + }); + // return; + it('should require a outputsList as an array', () => { + expect(() => coinSelection(utxosListAsUnspentOutput, {})).to.throw('outputsList must be an array of outputs'); + }); + it('should require a outputsList with at least one output', () => { + expect(() => coinSelection(utxosList, [])).to.throw('outputsList must contains at least 1 output'); + }); + it('should require a outputsList with valid outputs', () => { + expect(() => coinSelection(utxosListAsUnspentOutput, [{toto: true}])).to.throw('data parameter supplied is not a string.'); + }); + it('should alert if the total satoshis is not enough', () => { + expect(() => coinSelection(utxosListAsUnspentOutput, [outputs.HUNDRED_DASH])).to.throw('Unsufficient utxos (7099960000) to cover the output : 10000000000. Diff : -2900040000'); + }); + it('should work with normal utxo format', () => { + const output = new Transaction.UnspentOutput({ + address: 'yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42', + txId: 'd928aedc4ecc6c251cabee0672c19308573e5b4898c32779f3fd211dd8a1fbd8', + outputIndex: 1, + script: '76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac', + satoshis: 12999997288, + }); + + const result = coinSelection([output], [{ + satoshis: 2999997288, + address: 'ybefxSHaEbDATvq5gVCxjV375NWus3ttV7', + }]); + const expectedResult = { + utxos: [output], + outputs: [{satoshis: 2999997288, address: 'ybefxSHaEbDATvq5gVCxjV375NWus3ttV7', scriptType: 'P2PKH'}], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 12999997288, + }; + + expect(result).to.deep.equal(expectedResult); + }); + it('should get a coinSelection for 1 dash', () => { + const result = coinSelection(utxosListAsUnspentOutput, [outputs.ONE_DASH], false, 'normal', STRATEGIES.simpleDescendingAccumulator); + const expectedResult = { + utxos: [new Transaction.UnspentOutput({ + address: new Address('yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX'), + satoshis: 1*1e8, + txId: '071502a8b211e08f575641f3345b687a86c922108b5fd608822bffe0151aaf09', + outputIndex: 1, + script: new Script('76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac'), + })], + outputs: [{satoshis: 100000000, address: new Address('ybefxSHaEbDATvq5gVCxjV375NWus3ttV7'), scriptType: 'P2PKH'}], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 100000000, + }; + expect(result).to.deep.equal(expectedResult); + }); + it('should handle a case when using more than 25 utxos', () => { + const result = coinSelection(utxosListAsUnspentOutput, [outputs.TWENTY_FIVE_DASH], false, 'normal', STRATEGIES.simpleDescendingAccumulator); + const expectedResult = { + utxos: [ + new Transaction.UnspentOutput({ + address: new Address('yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX','testnet'), + txId: '071502a8b211e08f575641f3345b687a86c922108b5fd608822bffe0151aaf09', + outputIndex: 1, + height: 203268, + amount:1, + script: new Script('76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac'), + satoshis: 1 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX'), + txId: '0c98713b9895cf6c48f15aa717561f78339b9701f927c057758cb617f671cbfd', + outputIndex: 0, + height: 203265, + amount:1, + script: '76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac', + satoshis: 1 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX'), + txId: '1240c9e3bba3f143ec354bd37e4b860609b944dee2e426e9868e5c3244e47f04', + outputIndex: 1, + height: 203207, + amount: 0.8, + script: new Script('76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac'), + satoshis: 0.8 * 1e8, + }), new Transaction.UnspentOutput( { + address: new Address('yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42'), + txId: '157a4869ac5de33f40812f1e50e50395b472f991a72e59170037671914e72b0d', + outputIndex: 1, + height:203277, + amount: 1, + script: new Script('76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac'), + satoshis: 1 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42'), + txId: '1d90ba700b8fa18c8d9a6d3eaa505dde99a4a459c0d1e73bf40ba4b2cc2461cc', + outputIndex: 0, + script: new Script('76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac'), + satoshis: 1 * 1e8, + }), new Transaction.UnspentOutput( { + address: new Address('yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42'), + txId: '1fe685297c8c188a440affdda538ef5c757399051965352157c7e1495e6038f0', + outputIndex: 1, + script: new Script('76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac'), + satoshis: 1 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yMfDnWF6piqNA7mbSeEeAP4LiiqgxkJvNL'), + txId: '22c368e09ad8b36553b383c6a4ae989f91d1f66622b2b685262580c8a45175a4', + outputIndex: 1, + script: new Script('76a9140eb58a39a96968c19411568752ecdecf55dabb8588ac'), + satoshis: 0.5 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yZruigeCbPHVRnJG9JcSyG9AhX7PSF9oi7'), + txId: '2911362650f08df1ea16e03973bb41e1ee33680cce2ec6ce864e2daf35431e08', + outputIndex: 0, + script: new Script('76a914948cf5d360500a04d0a9080eac8514b79c1297b288ac'), + satoshis: 1.5 * 1e8, + }), new Transaction.UnspentOutput( { + address: new Address('yPWVEG3mW8pFdPCXcE53gN1fSTM8dkV7kF'), + txId: '2911362650f08df1ea16e03973bb41e1ee33680cce2ec6ce864e2daf35431e08', + outputIndex: 1, + script: new Script('76a91422fef09d745700a159553dd42227895053d33e6888ac'), + satoshis: 8.4999 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42'), + txId: '2bf25390be738308827348711da2700918b73096bfaff99de6c9c60121fa5d8e', + outputIndex: 0, + script: new Script('76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac'), + satoshis: 2 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yNgqjoW69ouSivtBMNFRCG5zSG85nyxW3d'), + txId: '36820d7268090d6f315eef03b28b7b2b2097c8b067608f652612a2c4612a6697', + outputIndex: 1, + script: new Script('76a91419fc1815a04c42a849a7a6dda826c67478514fed88ac'), + satoshis: 9.9999 * 1e8, + })], + outputs: [{satoshis: 2500000000, address: new Address('ybefxSHaEbDATvq5gVCxjV375NWus3ttV7'), scriptType: 'P2PKH'}], + feeCategory: 'normal', + estimatedFee: 1655, + utxosValue: 2829980000, + }; + expect(result).to.deep.equal(expectedResult); + }); + it('should handle a case when using more than 45 utxos', () => { + const result = coinSelection(utxosListAsUnspentOutput, [outputs.FOURTY_FIVE_DASH], false, 'normal', STRATEGIES.simpleDescendingAccumulator); + const expectedResult = { + utxos: [ + new Transaction.UnspentOutput({ + address: new Address('yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX'), + txId: '071502a8b211e08f575641f3345b687a86c922108b5fd608822bffe0151aaf09', + outputIndex: 1, + script: new Script('76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac'), + satoshis: 1 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX'), + txId: '0c98713b9895cf6c48f15aa717561f78339b9701f927c057758cb617f671cbfd', + outputIndex: 0, + script: new Script('76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac'), + satoshis: 1 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX'), + txId: '1240c9e3bba3f143ec354bd37e4b860609b944dee2e426e9868e5c3244e47f04', + outputIndex: 1, + script: new Script('76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac'), + satoshis: 0.8 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42'), + txId: '157a4869ac5de33f40812f1e50e50395b472f991a72e59170037671914e72b0d', + outputIndex: 1, + script: new Script('76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac'), + satoshis: 1 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42'), + txId: '1d90ba700b8fa18c8d9a6d3eaa505dde99a4a459c0d1e73bf40ba4b2cc2461cc', + outputIndex: 0, + script: new Script('76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac'), + satoshis: 1 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42'), + txId: '1fe685297c8c188a440affdda538ef5c757399051965352157c7e1495e6038f0', + outputIndex: 1, + script: new Script('76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac'), + satoshis: 1 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yMfDnWF6piqNA7mbSeEeAP4LiiqgxkJvNL'), + txId: '22c368e09ad8b36553b383c6a4ae989f91d1f66622b2b685262580c8a45175a4', + outputIndex: 1, + script: new Script('76a9140eb58a39a96968c19411568752ecdecf55dabb8588ac'), + satoshis: 0.5 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yZruigeCbPHVRnJG9JcSyG9AhX7PSF9oi7'), + txId: '2911362650f08df1ea16e03973bb41e1ee33680cce2ec6ce864e2daf35431e08', + outputIndex: 0, + script: new Script('76a914948cf5d360500a04d0a9080eac8514b79c1297b288ac'), + satoshis: 1.5 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yPWVEG3mW8pFdPCXcE53gN1fSTM8dkV7kF'), + txId: '2911362650f08df1ea16e03973bb41e1ee33680cce2ec6ce864e2daf35431e08', + outputIndex: 1, + script: new Script('76a91422fef09d745700a159553dd42227895053d33e6888ac'), + satoshis: 8.4999 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42'), + txId: '2bf25390be738308827348711da2700918b73096bfaff99de6c9c60121fa5d8e', + outputIndex: 0, + script: new Script('76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac'), + satoshis: 2 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yNgqjoW69ouSivtBMNFRCG5zSG85nyxW3d'), + txId: '36820d7268090d6f315eef03b28b7b2b2097c8b067608f652612a2c4612a6697', + outputIndex: 1, + script: new Script('76a91419fc1815a04c42a849a7a6dda826c67478514fed88ac'), + satoshis: 9.9999 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42'), + txId: '5b14eea2e1e07f94fbce22b50b6cda6b748a66c1119524a623c6820b75bbc7ca', + outputIndex: 0, + script: new Script('76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac'), + satoshis: 5 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX'), + txId: '5b6efaffbcf24b613ce29e18263203e05406f3fc130377eac02d579964672d67', + outputIndex: 1, + script: new Script('76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac'), + satoshis: 1 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX'), + txId: '5c462466bea61ff28e7805d20b482d83a139ea300a76052921038a22705e6937', + outputIndex: 0, + script: new Script('76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac'), + satoshis: 2 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42'), + txId: '6c42619dd84a02577458ba4f880fe8cfaced9ed518ee7c360c5b107d6ff5b62d', + outputIndex: 0, + script: new Script('76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac'), + satoshis: 1 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yQeCpWLJNGP4Aiojmz5ZC5gbYXREsnLnaX'), + txId: '7a6578995dd6eb11f0ec08e61135363fab55c0732ac05f563088b864d62f8cd4', + outputIndex: 1, + script: new Script('76a9142f6cb2047c14f0068a561fa2df704e64467ce9c588ac'), + satoshis: 1 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yW5qRPWdp1NzvxPbE4v95FDCxjxNqDEi42'), + txId: '8053a30d671b62e56a4a61d1fe2f899917cd20278e474a433e8d88d140757e0e', + outputIndex: 1, + script: new Script('76a9146b1e46d3f3d559dda4468cc30a7b612705eb810f88ac'), + satoshis: 2 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yMfDnWF6piqNA7mbSeEeAP4LiiqgxkJvNL'), + txId: '96eb6c951d69a3b8703673ca0d588cf6cee528f866fc598e84205ddcc34ea100', + outputIndex: 0, + script: new Script('76a9140eb58a39a96968c19411568752ecdecf55dabb8588ac'), + satoshis: 2 * 1e8, + }), new Transaction.UnspentOutput({ + address: new Address('yPn5VvPk7ioN9emDv3MkCKovpjNqSLwW1p'), + txId: '96eb6c951d69a3b8703673ca0d588cf6cee528f866fc598e84205ddcc34ea100', + outputIndex: 1, + script: new Script('76a91425f1c9581cd2a9976e6ace867f8e895663e6825a88ac'), + satoshis: 6.9998 * 1e8, + })], + outputs: [{satoshis: 4500000000, address: new Address('ybefxSHaEbDATvq5gVCxjV375NWus3ttV7'), scriptType: 'P2PKH'}], + feeCategory: 'normal', + estimatedFee: 2815, + utxosValue: 4929960000, + }; + // result.utxos = result.utxos.map((el) => el.toObject()); + + expect(result).to.deep.equal(expectedResult); + }); + it('should handle externally crafted strategy', function () { + // A dummy strategy that takes a random selection of utxo + const externalStrategy = (utxosList, outputsList, deductFee = false, feeCategory = 'normal') => { + const copiedUtxos = [...utxosList]; + const txEstimator = new TransactionEstimator(feeCategory); + + txEstimator.addOutputs(outputsList); + + let inputValue = 0; + let outputValue = txEstimator.getOutValue(); + const randomlySelectedUtxos = []; + + while(inputValue { + // const utxo = utxosList[15]; + // const utxos = []; + // for (let i = 0; i <= 45; i++) { + // utxos.push(utxosList[15]); + // } + // expect(() => coinSelection(utxos, [outputs.FOURTY_FIVE_DASH])).to.throw('Did not found any utxo, missing implementation of this case'); + // }); +}); diff --git a/packages/wallet-lib/src/utils/coinSelections/TransactionEstimator.js b/packages/wallet-lib/src/utils/coinSelections/TransactionEstimator.js new file mode 100644 index 00000000000..04fc7c6d969 --- /dev/null +++ b/packages/wallet-lib/src/utils/coinSelections/TransactionEstimator.js @@ -0,0 +1,171 @@ +const _ = require('lodash'); +const { + Address, Script, Transaction, +} = require('@dashevo/dashcore-lib'); +const logger = require('../../logger'); +const { + FEES, + VERSION_BYTES, + TXOUT_DUFFS_VALUE_BYTES, + N_LOCKTIME_BYTES, + TXIN_OUTPOINT_TXID_BYTES, + TXIN_OUTPOINT_INDEX_BYTES, + TXIN_SEQUENCE_BYTES, +} = require('../../CONSTANTS'); +const is = require('../is'); +const { varIntSizeBytesFromLength } = require('../varInt'); + +const { Output } = Transaction; +const calculateInputsSize = (inputs) => { + let inputsSize = 0; + inputs.forEach(() => { + // eslint-disable-next-line new-cap + const scriptPubKeyBytes = 100;// On average it's ~80 + const scriptPubKeyLengthBytes = varIntSizeBytesFromLength(scriptPubKeyBytes); + + const inputBytes = TXIN_OUTPOINT_TXID_BYTES + + TXIN_OUTPOINT_INDEX_BYTES + + scriptPubKeyLengthBytes + + scriptPubKeyBytes + + TXIN_SEQUENCE_BYTES; + + inputsSize += inputBytes; + }); + return varIntSizeBytesFromLength(inputs.length) + inputsSize; +}; +const calculateOutputsSize = (outputs, tx) => { + let outputsBytes = 0; + outputs.forEach((output) => { + const address = (output.address instanceof Address) + ? output.address + : Address.fromString(output.address); + const pkScript = Script.buildPublicKeyHashOut(address).toBuffer(); + const pkScriptSigBytes = pkScript.length; + const pkScriptLengthBytes = varIntSizeBytesFromLength(pkScriptSigBytes); + // eslint-disable-next-line new-cap + tx.addOutput(new Output.fromObject({ + satoshis: output.satoshis, + script: pkScript, + })); + + outputsBytes += (TXOUT_DUFFS_VALUE_BYTES + + pkScriptLengthBytes + + pkScriptSigBytes); + }); + + return varIntSizeBytesFromLength(outputs.length) + outputsBytes; +}; + +const defaultOpts = { + scriptType: 'P2PKH', // We only support that for now; +}; +class TransactionEstimator { + constructor(feeCategory) { + this.state = { + outputs: [], + inputs: [], + }; + this.feeCategory = feeCategory; + } + + reduceFeeFromOutput(amoutToReduce) { + const output = this.state.outputs[0]; + output.satoshis -= amoutToReduce; + } + + getOutputs() { + return this.state.outputs; + } + + getInputs() { + return this.state.inputs; + } + + getInValue() { + return this.state.inputs.reduce((prev, curr) => prev + curr.satoshis, 0); + } + + getOutValue() { + return this.state.outputs.reduce((prev, curr) => prev + curr.satoshis, 0); + } + + addInputs(_inputs = []) { + const self = this; + const inputs = (is.arr(_inputs)) ? _inputs : [_inputs]; + if (inputs.length < 1) return false; + + const addInput = (input) => { + if (!(input instanceof Transaction.UnspentOutput)) { + throw new Error('Expected valid UnspentOutput to import'); + } + self.state.inputs.push(input); + }; + + inputs.forEach(addInput); + return inputs; + } + + addOutputs(_outputs = []) { + const self = this; + const outputs = (is.arr(_outputs)) ? _outputs : [_outputs]; + if (outputs.length < 1) return false; + + const addOutput = (output) => { + if (!_.has(output, 'scriptType')) { + // eslint-disable-next-line no-param-reassign + output.scriptType = defaultOpts.scriptType; + } + self.state.outputs.push(output); + }; + + outputs.forEach(addOutput); + return outputs; + } + + getSize() { + const tx = new Transaction(); + + let size = 0; + size += VERSION_BYTES; + // DIP3 + // size += VERSION_BYTES_DIP3 + // size += TYPE_BYTES_DIP3 + size += calculateInputsSize(this.state.inputs); + size += calculateOutputsSize(this.state.outputs, tx); + size += 16; + // size += calculateExtraPayload(this.state.extraPayload); + size += N_LOCKTIME_BYTES; + + return size; + } + + getTotalOutputValue() { + let totalValue = 0; + this.state.outputs.forEach((output) => { + totalValue += output.satoshis; + }); + return totalValue; + } + + getFeeEstimate() { + return this.estimateFees(); + } + + estimateFees() { + const bytesSize = this.getSize(); + if (this.feeCategory === 'instant') { + const inputNb = this.getInputs().length; + return (inputNb * FEES.INSTANT_FEE_PER_INPUTS); + } + return ((bytesSize / 1000) * FEES[this.feeCategory.toUpperCase()]); + } + + debug() { + logger.info('=== Transaction Estimator'); + logger.info('State:', this.state); + logger.info('Size', this.getSize()); + logger.info('Fees', this.estimateFees()); + logger.info('========================='); + } +} +module.exports = TransactionEstimator; diff --git a/packages/wallet-lib/src/utils/coinSelections/helpers/index.js b/packages/wallet-lib/src/utils/coinSelections/helpers/index.js new file mode 100644 index 00000000000..c0bddccd6a9 --- /dev/null +++ b/packages/wallet-lib/src/utils/coinSelections/helpers/index.js @@ -0,0 +1,35 @@ +const sortAndVerifyUTXOS = require('./sortAndVerifyUTXOS'); + +module.exports = { + sortAndVerifyUTXOS, +}; + +// const is = require('../utils/is'); +// const { getBytesOf } = require('../utils/utils'); +// const { FEES } = require('../Constants'); +// const STRATEGIES = require('./coinSelections/strategies'); +// +// /** +// * Calculate size and value of a provided output +// * @param outputsList +// * @return {{outputBytes: number, outputValue: number}} +// */ +// const getOutputsInfo = (outputsList) => { +// let outputBytes = 0; +// let outputValue = 0; +// outputsList.forEach((output) => { +// outputBytes += getBytesOf(output, 'output'); +// outputValue += output.satoshis; +// }); +// return { +// outputBytes, +// outputValue +// } +// }; +// const estimateFee = (type, txSizeInBytes) => { +// const satPerKb = FEES[type.toUpperCase()]; +// const txSizeInKB = txSizeInBytes / 1000; +// const feeInSatoshis = satPerKb * txSizeInBytes; +// +// return parseInt(feeInSatoshis, 10); +// }; diff --git a/packages/wallet-lib/src/utils/coinSelections/helpers/sortAndVerifyUTXOS.js b/packages/wallet-lib/src/utils/coinSelections/helpers/sortAndVerifyUTXOS.js new file mode 100644 index 00000000000..058683eb4e4 --- /dev/null +++ b/packages/wallet-lib/src/utils/coinSelections/helpers/sortAndVerifyUTXOS.js @@ -0,0 +1,28 @@ +/* eslint-disable no-param-reassign */ +const sort = { + by(el, params) { + if (!params) return el; + + el.sort((a, b) => { + let result; + params.reverse().forEach((param) => { + const key = param.property; + const { direction } = (param.direction === 'ascending') ? 1 : -1; + + if ((a[key] < b[key])) { + result = -1; + } else { + result = (a[key] > b[key]) ? 1 : 0; + } + return result * direction; + }); + return 0; + }); + + return el; + }, +}; + +const sortAndVerifyUTXOS = (utxosList, opts) => sort.by(utxosList, opts); + +module.exports = sortAndVerifyUTXOS; diff --git a/packages/wallet-lib/src/utils/coinSelections/index.js b/packages/wallet-lib/src/utils/coinSelections/index.js new file mode 100644 index 00000000000..9e3cebbe356 --- /dev/null +++ b/packages/wallet-lib/src/utils/coinSelections/index.js @@ -0,0 +1,36 @@ +const { Transaction } = require('@dashevo/dashcore-lib'); +const STRATEGIES = require('./strategies'); +const InvalidUTXO = require('../../errors/InvalidUTXO'); +const InvalidOutput = require('../../errors/InvalidOutput'); +const CoinSelectionUnsufficientUTXOS = require('../../errors/CoinSelectionUnsufficientUTXOS'); + +module.exports = function coinSelection(utxosList, outputsList, deductFee = false, feeCategory = 'normal', strategy = STRATEGIES.simpleDescendingAccumulator) { + if (!utxosList) { throw new Error('A utxosList is required'); } + if (utxosList.constructor.name !== Array.name) { throw new Error('UtxosList is expected to be an array of utxos'); } + if (utxosList.length < 1) { throw new Error('utxosList must contain at least 1 utxo'); } + if (!outputsList) { throw new Error('An outputsList is required in order to perform a selection'); } + if (outputsList.constructor.name !== Array.name) { throw new Error('outputsList must be an array of outputs'); } + if (outputsList.length < 1) { throw new Error('outputsList must contains at least 1 output'); } + + let utxosValue = 0; + + for (let i = 0; i < utxosList.length; i += 1) { + const utxo = utxosList[i]; + if (!(utxo instanceof Transaction.UnspentOutput)) { + throw new InvalidUTXO(utxo); + } + utxosValue += utxo.satoshis; + } + let outputValue = 0; + + outputsList.forEach((output) => { + if (output instanceof Transaction.Output) { + throw new InvalidOutput(output); + } + outputValue += output.satoshis; + }); + if (utxosValue < outputValue) { + throw new CoinSelectionUnsufficientUTXOS({ utxosValue, outputValue }); + } + return strategy(utxosList, outputsList, deductFee, feeCategory); +}; diff --git a/packages/wallet-lib/src/utils/coinSelections/strategies/index.js b/packages/wallet-lib/src/utils/coinSelections/strategies/index.js new file mode 100644 index 00000000000..d38fd2a7e0f --- /dev/null +++ b/packages/wallet-lib/src/utils/coinSelections/strategies/index.js @@ -0,0 +1,8 @@ +const simpleAscendingAccumulator = require('./simpleAscendingAccumulator'); +const simpleDescendingAccumulator = require('./simpleDescendingAccumulator'); + +const STRATEGIES = { + simpleDescendingAccumulator, + simpleAscendingAccumulator, +}; +module.exports = STRATEGIES; diff --git a/packages/wallet-lib/src/utils/coinSelections/strategies/simpleAscendingAccumulator.js b/packages/wallet-lib/src/utils/coinSelections/strategies/simpleAscendingAccumulator.js new file mode 100644 index 00000000000..b0564813cac --- /dev/null +++ b/packages/wallet-lib/src/utils/coinSelections/strategies/simpleAscendingAccumulator.js @@ -0,0 +1,72 @@ +const { sortBy } = require('lodash'); +const TransactionEstimator = require('../TransactionEstimator'); +/** + * Given a utxos list and a threesholdSatoshis, will add them + * without any further logic up to met with requested params. + * @param utxos + * @param thresholdSatoshis + * @return {*} + */ +const simplyAccumulateUtxos = (utxos, thresholdSatoshis) => { + let pendingSatoshis = 0; + const accumulatedUtxos = utxos.filter((utxo) => { + if (pendingSatoshis < thresholdSatoshis) { + pendingSatoshis += utxo.satoshis; + return utxo; + } + return false; + }); + if (pendingSatoshis < thresholdSatoshis) { + throw new Error('Unsufficient utxo amount'); + } + return accumulatedUtxos; +}; +/** + * Simple accumulator strategy : Will try to spend using as few utxos as possible. + * Sorted by ascending value amount. + * @param {*} utxosList - A utxos list + * @param {*} outputsList - The output list + * @param {*} deductFee - default: false - Deduct fee from outputs + * @param {*} feeCategory - default: normal + + */ + +// FIXME : If we have a dust, it might cost us more to spend it (fee) than we earn. +// Might want to not spend it +const simpleAscendingAccumulator = (utxosList, outputsList, deductFee = false, feeCategory = 'normal') => { + const txEstimator = new TransactionEstimator(feeCategory); + + // We add our outputs, theses will change only in case deductfee being true + txEstimator.addOutputs(outputsList); + + const sortedUtxosList = sortBy(utxosList, ['-satoshis', 'txid', 'outputIndex']); + + const totalOutputValue = txEstimator.getTotalOutputValue(); + const simplyAccumulatedUtxos = simplyAccumulateUtxos(sortedUtxosList, totalOutputValue); + + // We add the expected inputs, which should match the requested amount + // TODO : handle case when we do not match it. + txEstimator.addInputs(simplyAccumulatedUtxos); + + const estimatedFee = txEstimator.getFeeEstimate(); + if (deductFee === true) { + // Then we check that we will be able to do it + const inValue = txEstimator.getInValue(); + const outValue = txEstimator.getOutValue(); + if (inValue < outValue + estimatedFee) { + // We don't have enough change for fee, so we remove from outValue + txEstimator.reduceFeeFromOutput((outValue + estimatedFee) - inValue); + } else { + // TODO : Here we can add some process to check up that we clearly have enough to deduct fee + } + } + // console.log('estimatedFee are', estimatedFee, 'satoshis'); + return { + utxos: txEstimator.getInputs(), + outputs: txEstimator.getOutputs(), + feeCategory, + estimatedFee, + utxosValue: txEstimator.getInValue(), + }; +}; +module.exports = simpleAscendingAccumulator; diff --git a/packages/wallet-lib/src/utils/coinSelections/strategies/simpleAscendingAccumulator.spec.js b/packages/wallet-lib/src/utils/coinSelections/strategies/simpleAscendingAccumulator.spec.js new file mode 100644 index 00000000000..c9c791d302e --- /dev/null +++ b/packages/wallet-lib/src/utils/coinSelections/strategies/simpleAscendingAccumulator.spec.js @@ -0,0 +1,234 @@ +const { expect } = require('chai'); +const { simpleAscendingAccumulator } = require('./index'); +const getUTXOS = require('../../../types/Account/methods/getUTXOS'); +const duringDevelopStore = require('../../../../fixtures/duringdevelop-fullstore-snapshot-1549310417'); + +describe('CoinSelection - Strategy - simpleAscendingAccumulator', () => { + describe.skip('it should pass - requires FakeNet', ()=>{ + it('should work as expected', () => { + const self = {}; + + const utxosList = getUTXOS.call({ + store: duringDevelopStore, + getStore: () => this.store, + walletId: '5061b8276c', + }); + + const outputsList1e4 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e4 }]; + const outputsList1e5 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e5 }]; + const outputsList1e6 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e6 }]; + const outputsList1e7 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e7 }]; + const outputsList1e8 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e8 }]; + const outputsList1e9 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e9 }]; + const outputsList1e10 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e10 }]; + const outputsList2e10 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 2e10 }]; + const outputsList6e10 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 6e10 }]; + const outputsList999e8 = [{ + address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', + satoshis: 999.99998628e8, + }]; + const outputsList1e11 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e11 }]; + + const expectedRes1e4 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 10000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes1e5 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 100000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes1e6 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes1e7 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 10000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes1e8 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 100000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes1e9 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1000000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes1e10 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 10000000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes2e10 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 20000000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 350, + utxosValue: 20000000000, + }; + const expectedRes6e10 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 60000000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 930, + utxosValue: 60000000000, + }; + const expectedRes999e8 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 5000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 1000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 1000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 1000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 100000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 100000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 100000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 100000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 100000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 100000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 100000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 100000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 100000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 1000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 100000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 1000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 100, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 1088887528, script: '76a9144f8aa6c3e302911b8c6b0ecb0538d209c144f84988ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 99999998628, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 4265, + utxosValue: 99999998628, + }; + + const res1e4 = simpleAscendingAccumulator.call(self, utxosList, outputsList1e4); + const res1e5 = simpleAscendingAccumulator.call(self, utxosList, outputsList1e5); + const res1e6 = simpleAscendingAccumulator.call(self, utxosList, outputsList1e6); + const res1e7 = simpleAscendingAccumulator.call(self, utxosList, outputsList1e7); + const res1e8 = simpleAscendingAccumulator.call(self, utxosList, outputsList1e8); + const res1e9 = simpleAscendingAccumulator.call(self, utxosList, outputsList1e9); + const res1e10 = simpleAscendingAccumulator.call(self, utxosList, outputsList1e10); + const res2e10 = simpleAscendingAccumulator.call(self, utxosList, outputsList2e10); + const res6e10 = simpleAscendingAccumulator.call(self, utxosList, outputsList6e10); + const res999e8 = simpleAscendingAccumulator.call(self, utxosList, outputsList999e8); + + res1e4.utxos[0] = res1e4.utxos[0].toJSON(); + expect(res1e4).to.deep.equal(expectedRes1e4); + res1e5.utxos[0] = res1e5.utxos[0].toJSON(); + expect(res1e5).to.deep.equal(expectedRes1e5); + + res1e6.utxos[0] = res1e6.utxos[0].toJSON(); + expect(res1e6).to.deep.equal(expectedRes1e6); + res1e7.utxos[0] = res1e7.utxos[0].toJSON(); + expect(res1e7).to.deep.equal(expectedRes1e7); + res1e8.utxos[0] = res1e8.utxos[0].toJSON(); + expect(res1e8).to.deep.equal(expectedRes1e8); + res1e9.utxos[0] = res1e9.utxos[0].toJSON(); + expect(res1e9).to.deep.equal(expectedRes1e9); + res1e10.utxos[0] = res1e10.utxos[0].toJSON(); + expect(res1e10).to.deep.equal(expectedRes1e10); + res2e10.utxos[0] = res2e10.utxos[0].toJSON(); + res2e10.utxos[1] = res2e10.utxos[1].toJSON(); + expect(res2e10).to.deep.equal(expectedRes2e10); + res6e10.utxos[0] = res6e10.utxos[0].toJSON(); + res6e10.utxos[1] = res6e10.utxos[1].toJSON(); + res6e10.utxos[2] = res6e10.utxos[2].toJSON(); + res6e10.utxos[3] = res6e10.utxos[3].toJSON(); + res6e10.utxos[4] = res6e10.utxos[4].toJSON(); + res6e10.utxos[5] = res6e10.utxos[5].toJSON(); + expect(res6e10).to.deep.equal(expectedRes6e10); + res999e8.utxos = res999e8.utxos.map((utxo) => utxo.toJSON()); + // res999e8.utxos[0] = res999e8.utxos[0].toJSON(); + expect(res999e8).to.deep.equal(expectedRes999e8); + expect(() => simpleAscendingAccumulator.call(self, utxosList, outputsList1e11)).to.throw(('Unsufficient utxo amount')); + }); + + }) +}); diff --git a/packages/wallet-lib/src/utils/coinSelections/strategies/simpleDescendingAccumulator.js b/packages/wallet-lib/src/utils/coinSelections/strategies/simpleDescendingAccumulator.js new file mode 100644 index 00000000000..d7a851ffc5b --- /dev/null +++ b/packages/wallet-lib/src/utils/coinSelections/strategies/simpleDescendingAccumulator.js @@ -0,0 +1,70 @@ +const { sortBy } = require('lodash'); +const TransactionEstimator = require('../TransactionEstimator'); + +/** + * Given a utxos list and a threesholdSatoshis, will add them + * without any further logic up to met with requested params. + * @param utxos + * @param thresholdSatoshis + * @return {*} + */ +const simplyAccumulateUtxos = (utxos, thresholdSatoshis) => { + let pendingSatoshis = 0; + const accumulatedUtxos = utxos.filter((utxo) => { + if (pendingSatoshis < thresholdSatoshis) { + pendingSatoshis += utxo.satoshis; + return utxo; + } + return false; + }); + if (pendingSatoshis < thresholdSatoshis) { + throw new Error('Unsufficient utxo amount'); + } + return accumulatedUtxos; +}; +/** + * Simple accumulator strategy : Will try to spend using as few utxos as possible. + * Sorted by descending value amount. + * @param {*} utxosList - A utxos list + * @param {*} outputsList - The output list + * @param {*} deductFee - default: false - Deduct fee from outputs + * @param {*} feeCategory - default: normal + + */ +const simpleDescendingAccumulator = (utxosList, outputsList, deductFee = false, feeCategory = 'normal') => { + const txEstimator = new TransactionEstimator(feeCategory); + + // We add our outputs, theses will change only in case deductfee being true + txEstimator.addOutputs(outputsList); + + const sortedUtxosList = sortBy(utxosList, ['-satoshis', 'txId', 'outputIndex']); + + const totalOutputValue = txEstimator.getTotalOutputValue(); + const simplyAccumulatedUtxos = simplyAccumulateUtxos(sortedUtxosList, totalOutputValue); + + // We add the expected inputs, which should match the requested amount + // TODO : handle case when we do not match it. + txEstimator.addInputs(simplyAccumulatedUtxos); + + const estimatedFee = txEstimator.getFeeEstimate(); + if (deductFee === true) { + // Then we check that we will be able to do it + const inValue = txEstimator.getInValue(); + const outValue = txEstimator.getOutValue(); + if (inValue < outValue + estimatedFee) { + // We don't have enough change for fee, so we remove from outValue + txEstimator.reduceFeeFromOutput((outValue + estimatedFee) - inValue); + } else { + // TODO : Here we can add some process to check up that we clearly have enough to deduct fee + } + } + // console.log('estimatedFee are', estimatedFee, 'satoshis'); + return { + utxos: txEstimator.getInputs(), + outputs: txEstimator.getOutputs(), + feeCategory, + estimatedFee, + utxosValue: txEstimator.getInValue(), + }; +}; +module.exports = simpleDescendingAccumulator; diff --git a/packages/wallet-lib/src/utils/coinSelections/strategies/simpleDescendingAccumulator.spec.js b/packages/wallet-lib/src/utils/coinSelections/strategies/simpleDescendingAccumulator.spec.js new file mode 100644 index 00000000000..d35002671bb --- /dev/null +++ b/packages/wallet-lib/src/utils/coinSelections/strategies/simpleDescendingAccumulator.spec.js @@ -0,0 +1,171 @@ +const { expect } = require('chai'); +const { simpleDescendingAccumulator } = require('./index'); +const getUTXOS = require('../../../types/Account/methods/getUTXOS'); +const duringDevelopStore = require('../../../../fixtures/duringdevelop-fullstore-snapshot-1549310417'); + +console.error('coinSelection.strategies.simpleDescendingAccumulator needs a rebuilt store'); +describe.skip('CoinSelection - Strategy - simpleDescendingAccumulator', () => { + it('should work as expected', () => { + const self = { + }; + + const utxosList = getUTXOS.call({ + store: duringDevelopStore, + getStore: () => this.store, + walletId: '5061b8276c', + }); + + const outputsList1e4 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e4 }]; + const outputsList1e5 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e5 }]; + const outputsList1e6 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e6 }]; + const outputsList1e7 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e7 }]; + const outputsList1e8 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e8 }]; + const outputsList1e9 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e9 }]; + const outputsList1e10 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e10 }]; + const outputsList2e10 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 2e10 }]; + const outputsList6e10 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 6e10 }]; + const outputsList1e11 = [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1e11 }]; + + const expectedRes1e4 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 10000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes1e5 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 100000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes1e6 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes1e7 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 10000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes1e8 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 100000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes1e9 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 1000000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes1e10 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 10000000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 205, + utxosValue: 10000000000, + }; + const expectedRes2e10 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 20000000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 350, + utxosValue: 20000000000, + }; + const expectedRes6e10 = { + utxos: [{ + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }, { + satoshis: 10000000000, script: '76a9143a9202121ee9ef906e567101326f2ecf8ad4ecbc88ac', + }], + outputs: [{ address: 'yU7sNM4j6fzKtbah24gCXdN636piQN8F2f', satoshis: 60000000000, scriptType: 'P2PKH' }], + feeCategory: 'normal', + estimatedFee: 930, + utxosValue: 60000000000, + }; + + + const res1e4 = simpleDescendingAccumulator.call(self, utxosList, outputsList1e4); + const res1e5 = simpleDescendingAccumulator.call(self, utxosList, outputsList1e5); + const res1e6 = simpleDescendingAccumulator.call(self, utxosList, outputsList1e6); + const res1e7 = simpleDescendingAccumulator.call(self, utxosList, outputsList1e7); + const res1e8 = simpleDescendingAccumulator.call(self, utxosList, outputsList1e8); + const res1e9 = simpleDescendingAccumulator.call(self, utxosList, outputsList1e9); + const res1e10 = simpleDescendingAccumulator.call(self, utxosList, outputsList1e10); + const res2e10 = simpleDescendingAccumulator.call(self, utxosList, outputsList2e10); + const res6e10 = simpleDescendingAccumulator.call(self, utxosList, outputsList6e10); + + res1e4.utxos[0] = res1e4.utxos[0].toJSON(); + expect(res1e4).to.deep.equal(expectedRes1e4); + + res1e5.utxos[0] = res1e5.utxos[0].toJSON(); + expect(res1e5).to.deep.equal(expectedRes1e5); + + res1e6.utxos[0] = res1e6.utxos[0].toJSON(); + expect(res1e6).to.deep.equal(expectedRes1e6); + + res1e7.utxos[0] = res1e7.utxos[0].toJSON(); + expect(res1e7).to.deep.equal(expectedRes1e7); + + res1e8.utxos[0] = res1e8.utxos[0].toJSON(); + expect(res1e8).to.deep.equal(expectedRes1e8); + + res1e9.utxos[0] = res1e9.utxos[0].toJSON(); + expect(res1e9).to.deep.equal(expectedRes1e9); + + res1e10.utxos[0] = res1e10.utxos[0].toJSON(); + expect(res1e10).to.deep.equal(expectedRes1e10); + + res2e10.utxos[0] = res2e10.utxos[0].toJSON(); + res2e10.utxos[1] = res2e10.utxos[1].toJSON(); + expect(res2e10).to.deep.equal(expectedRes2e10); + + res6e10.utxos[0] = res6e10.utxos[0].toJSON(); + res6e10.utxos[1] = res6e10.utxos[1].toJSON(); + res6e10.utxos[2] = res6e10.utxos[2].toJSON(); + res6e10.utxos[3] = res6e10.utxos[3].toJSON(); + res6e10.utxos[4] = res6e10.utxos[4].toJSON(); + res6e10.utxos[5] = res6e10.utxos[5].toJSON(); + expect(res6e10).to.deep.equal(expectedRes6e10); + expect(() => simpleDescendingAccumulator.call(self, utxosList, outputsList1e11)).to.throw(('Unsufficient utxo amount')); + + + // expect(res1).to.deep.equal(expectedRes1); + }); +}); diff --git a/packages/wallet-lib/src/utils/crypto.js b/packages/wallet-lib/src/utils/crypto.js new file mode 100644 index 00000000000..2859e74dc73 --- /dev/null +++ b/packages/wallet-lib/src/utils/crypto.js @@ -0,0 +1,19 @@ +const crypto = require('crypto'); + +function hash(alg, data) { + return crypto.createHash(alg).update(data).digest(); +} + +function sha256(data) { + return hash('sha256', data); +} + +function doubleSha256(data) { + return sha256(sha256(data)); +} + +module.exports = { + hash, + doubleSha256, + sha256, +}; diff --git a/packages/wallet-lib/src/utils/dashToDuffs.js b/packages/wallet-lib/src/utils/dashToDuffs.js new file mode 100644 index 00000000000..c540377b2f6 --- /dev/null +++ b/packages/wallet-lib/src/utils/dashToDuffs.js @@ -0,0 +1,9 @@ +const { DUFFS_PER_DASH } = require('../CONSTANTS'); + +function dashToDuffs(dash) { + if (dash === undefined || dash.constructor.name !== Number.name) { + throw new Error('Can only convert a number'); + } + return parseInt((dash * DUFFS_PER_DASH).toFixed(0), 10); +} +module.exports = dashToDuffs; diff --git a/packages/wallet-lib/src/utils/dashToDuffs.spec.js b/packages/wallet-lib/src/utils/dashToDuffs.spec.js new file mode 100644 index 00000000000..598eaf9da50 --- /dev/null +++ b/packages/wallet-lib/src/utils/dashToDuffs.spec.js @@ -0,0 +1,32 @@ +const {expect} = require('chai'); +const dashToDuffs = require('./dashToDuffs'); +const {duffsToDash} = require("./index"); + +describe('Utils - dashToDuffs', function suite() { + it('should correctly convert dash to duffs', () => { + const results = [ + dashToDuffs(1), + dashToDuffs(-1), + dashToDuffs(0.1), + dashToDuffs(0.01), + dashToDuffs(0.00000001), + dashToDuffs(0.000000001), + dashToDuffs(-0.000000001), + dashToDuffs(-12345678.9876543210), + ] + const expectedResults = [ + 100000000, + -100000000, + 10000000, + 1000000, + 1, + 0, + -0, + -1234567898765432 + ] + results.forEach((result, resultIndex) => { + expect(results[resultIndex]).to.equal(expectedResults[resultIndex]); + }) + expect(() => dashToDuffs('deuxmille')).to.throw('Can only convert a number'); + }); +}); diff --git a/packages/wallet-lib/src/utils/duffsToDash.js b/packages/wallet-lib/src/utils/duffsToDash.js new file mode 100644 index 00000000000..b3d3226da86 --- /dev/null +++ b/packages/wallet-lib/src/utils/duffsToDash.js @@ -0,0 +1,9 @@ +const { DUFFS_PER_DASH } = require('../CONSTANTS'); + +function duffsToDash(duffs) { + if (duffs === undefined || duffs.constructor.name !== Number.name) { + throw new Error('Can only convert a number'); + } + return duffs / DUFFS_PER_DASH; +} +module.exports = duffsToDash; diff --git a/packages/wallet-lib/src/utils/duffsToDash.spec.js b/packages/wallet-lib/src/utils/duffsToDash.spec.js new file mode 100644 index 00000000000..e15f79a2699 --- /dev/null +++ b/packages/wallet-lib/src/utils/duffsToDash.spec.js @@ -0,0 +1,13 @@ +const {expect} = require('chai'); +const dashToDuffs = require('./dashToDuffs'); +const {duffsToDash} = require("./index"); + +describe('Utils - duffsToDash', function suite() { + it('should correctly convert duffs to dash', () => { + it('should handle duff2Dash', () => { + expect(duffsToDash(200000000000)).to.equal(2000); + expect(duffsToDash(-200000000000)).to.equal(-2000); + expect(() => duffsToDash('deuxmille')).to.throw('Can only convert a number'); + }); + }); +}); diff --git a/packages/wallet-lib/src/utils/expectThrowsAsync.js b/packages/wallet-lib/src/utils/expectThrowsAsync.js new file mode 100644 index 00000000000..40c8b40068b --- /dev/null +++ b/packages/wallet-lib/src/utils/expectThrowsAsync.js @@ -0,0 +1,22 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +const { expect } = require('chai'); + +const expectThrowsAsync = async (method, errorMessage) => { + let error = null; + try { + const res = await method(); + expect(res).to.be.an('Error'); + if (errorMessage) { + if (res.message) { + error = res; + } + } + } catch (err) { + error = err; + } + expect(error).to.be.an('Error'); + if (errorMessage) { + expect(error.message).to.equal(errorMessage); + } +}; +module.exports = expectThrowsAsync; diff --git a/packages/wallet-lib/src/utils/extendTransactionsWithMetadata.js b/packages/wallet-lib/src/utils/extendTransactionsWithMetadata.js new file mode 100644 index 00000000000..82dea97c717 --- /dev/null +++ b/packages/wallet-lib/src/utils/extendTransactionsWithMetadata.js @@ -0,0 +1,23 @@ +const { each } = require('lodash'); +const logger = require('../logger'); + +const extendTransactionsWithMetadata = (transactions, transactionsMetadata) => { + const transactionsWithMetadata = []; + each(transactions, (transaction) => { + const { hash } = transaction; + if (transactionsMetadata[hash]) { + const transactionMetadata = transactionsMetadata[hash]; + transactionsWithMetadata.push([transaction, transactionMetadata]); + } else { + logger.silly(`Unable to find metadata for ${hash}`); + transactionsWithMetadata.push([transaction, { + blockHash: null, + height: -1, + instantLocked: null, + chainLocked: false, + }]); + } + }); + return transactionsWithMetadata; +}; +module.exports = extendTransactionsWithMetadata; diff --git a/packages/wallet-lib/src/utils/extendTransactionsWithMetadata.spec.js b/packages/wallet-lib/src/utils/extendTransactionsWithMetadata.spec.js new file mode 100644 index 00000000000..d936311f1a9 --- /dev/null +++ b/packages/wallet-lib/src/utils/extendTransactionsWithMetadata.spec.js @@ -0,0 +1,30 @@ +const { expect } = require('chai'); +const { Transaction } = require('@dashevo/dashcore-lib'); +const extendTransactionsWithMetadata = require('./extendTransactionsWithMetadata'); + +const rawtx = '03000000012d9101b84d69adf1b168403ab2bcfbf3d2eebbf87a99a9be05d649d47d6c7bd3010000006a47304402201cc3d6887d5161eba36a5e6fb1ccd8e8f9eeda7fe95b4fb0a1accb99eeba0223022040d0df81fde8f59c807e541ca5bcfc9d7450f76657aeb44c708fa7d65b7d58410121038cdae47fceb5b117cd3ef5bdf8c9f2a83679a9105d012095762067bdb2351ceaffffffff0280969800000000001976a914e00939d2ec2f885f5e7dc7b9f5b06dcf868d0c4b88acabfe261f000000001976a914f03286cbb7954ea6affa9654af6cfe1210dd0c6288ac00000000'; +const tx = new Transaction(rawtx); +const txid = '7d1b78157f9f2238669f260d95af03aeefc99577ff0cddb91b3e518ee557a2fd'; +const transactions = {}; +transactions[txid] = tx; + +const transactionsMetadata = {}; +transactionsMetadata[txid] = { + blockHash: '0000012cf6377c6cf2b317a4deed46573c09f04f6880dca731cc9ccea6691e19', + height: 555508, + instantLocked: true, + chainLocked: true +}; + +describe('Utils - extendTransactionWithMetadata', function suite() { + it('should correctly extend metadata from transaction', function () { + const result = extendTransactionsWithMetadata(transactions, transactionsMetadata); + const expectedResults = [[tx,{ + blockHash: '0000012cf6377c6cf2b317a4deed46573c09f04f6880dca731cc9ccea6691e19', + height: 555508, + instantLocked: true, + chainLocked: true + }]]; + expect(result).to.deep.equal(expectedResults); + }); +}); diff --git a/packages/wallet-lib/src/utils/feeCalculation.js b/packages/wallet-lib/src/utils/feeCalculation.js new file mode 100644 index 00000000000..482dcf35a30 --- /dev/null +++ b/packages/wallet-lib/src/utils/feeCalculation.js @@ -0,0 +1,18 @@ +module.exports = function feeCalculation(type = 'standard') { + const feeRate = { + type: null, + value: null, + }; + + switch (type) { + case 'instantSend': + feeRate.type = 'perInputs'; + feeRate.value = 10000; + return feeRate; + case 'standard': + default: + feeRate.type = 'perBytes'; + feeRate.value = 1000; + return feeRate; + } +}; diff --git a/packages/wallet-lib/src/utils/feeCalculation.spec.js b/packages/wallet-lib/src/utils/feeCalculation.spec.js new file mode 100644 index 00000000000..84aa8d0b478 --- /dev/null +++ b/packages/wallet-lib/src/utils/feeCalculation.spec.js @@ -0,0 +1,25 @@ +const { expect } = require('chai'); +const feeCalculation = require('./feeCalculation'); + +describe('Utils - feeCalculation', function suite() { + this.timeout(10000); + it('should get feeRate for an instantSend transaction', () => { + const result = feeCalculation('instantSend'); + const expectedResult = { + type: 'perInputs', + value: 10000, + }; + expect(result).to.deep.equal(expectedResult); + }); + it('should get feeRate for an classic transaction', () => { + const result1 = feeCalculation('standard'); + const result2 = feeCalculation(); + + const expectedResult = { + type: 'perBytes', + value: 1000, + }; + expect(result1).to.deep.equal(expectedResult); + expect(result2).to.deep.equal(expectedResult); + }); +}); diff --git a/packages/wallet-lib/src/utils/filterTransactions.js b/packages/wallet-lib/src/utils/filterTransactions.js new file mode 100644 index 00000000000..3badb90daef --- /dev/null +++ b/packages/wallet-lib/src/utils/filterTransactions.js @@ -0,0 +1,58 @@ +const { uniq, each } = require('lodash'); +const { WALLET_TYPES } = require('../CONSTANTS'); + +const sortByNLockTime = (a, b) => (b.nLockTime - a.nLockTime); +// Will filter out transaction that are not concerning us +// (which can happen in the case of multiple account in store) +function filterTransactions(accountStore, walletType, accountIndex, transactions) { + /** + * From transaction's hash, we would need to be able to find the time of such execution. + * Previously we used 'confirmations' value to estimate the height block where it would + * be included. + * This has been removed, and there is no way for us to easily get the block height + * or hash from a tx. + * In order to support this feature, it would require us to have the whole raw block set + * in order to find a tx in a block. + */ + if (!walletType) throw new Error('Expecting walletType to be provided.'); + if (!accountIndex && accountIndex !== 0) throw new Error('Expecting account index to be provided.'); + if (!transactions) throw new Error('Expecting transactions to be provided'); + if (!accountStore) throw new Error('Expecting accountStore to be provided'); + const filteredTransactions = []; + const filteredTransactionsId = []; + + const isHDWallet = [WALLET_TYPES.HDWALLET, WALLET_TYPES.HDPUBLIC].includes(walletType); + const { addresses } = accountStore; + const { external, internal, misc } = addresses; + + each({ ...external, ...internal }, (hdAddress) => { + if ( + hdAddress.path + && isHDWallet + && parseInt(hdAddress.path.split('/')[3], 10) === accountIndex + ) { + hdAddress.transactions.forEach((txid) => { + if (!filteredTransactionsId.includes(txid)) { + filteredTransactionsId.push(txid); + } + }); + } + }); + + // Misc addresses can be publicKey/privateKey main addresses if !isHDWallet + each(misc, (miscAddress) => { + miscAddress.transactions.forEach((txid) => { + if (!filteredTransactionsId.includes(txid)) { + filteredTransactionsId.push(txid); + } + }); + }); + + uniq(filteredTransactionsId).forEach((transactionId) => { + const tx = transactions[transactionId]; + filteredTransactions.push(tx); + }); + return filteredTransactions.sort(sortByNLockTime); +} + +module.exports = filterTransactions; diff --git a/packages/wallet-lib/src/utils/filterTransactions.spec.js b/packages/wallet-lib/src/utils/filterTransactions.spec.js new file mode 100644 index 00000000000..a5a54495a0b --- /dev/null +++ b/packages/wallet-lib/src/utils/filterTransactions.spec.js @@ -0,0 +1,151 @@ +const { Transaction } = require('@dashevo/dashcore-lib'); +const { expect } = require('chai'); +const { WALLET_TYPES } = require('../CONSTANTS'); +const filterTransactions = require('./filterTransactions'); + +const fixtureAddressesStore = { + external: {}, + internal: {}, + misc: {} +}; +const externalAddresses = [ + 'yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7', + 'yercyhdN9oEkZcB9BsW5ktFaDxFEuK6qXN', + 'ygk3GCSba2J3L9G665Snozhj9HSkh5ByVE', + 'ybuL6rM6dgrKzCg8s99f3jxGuv5oz5JcDA', + 'ygHAVkMtYSqoTWHebDv7qkhMV6dHyuRsp2', + 'yMLhEsiP2ajSh8STmXnNmkWXtoHsmawZxd', + 'yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv', + 'yhaAB6e8m3F8zmGX7WAVYa6eEfmSrrnY8x', + 'yiXh4Yo5djG6QH8WzXkKm5EFzqLRJWakXz', + 'yQYv3Um6DsdtANo1ZPTUte75wAGMstLRex', + 'yiYPJmu7eEm1cXUNumQRdjv1fvPhsfgMS4', + 'yii4aUZhNfL6EWN9KAgAFrJzGJmqHnF4wx', + 'yLpTquSct2SGz2Ka45uTPDd81Kzro2Jt2k', + 'yMiJtpzb1Qthy9TGnavsf5NZ6EZZa4j9q3', + 'yacgSfW7RkwWakEZPg8USAVdzCypiG3vxS', + 'yVvrmoRPFLy6nUpCQBT8ZExxF5wF3DhiGU', + 'yaJf2aG6cFUtfv4o6TuEKsh5kr4xq5iAY4', + 'yfardJQ4ucgWLKQPaRHGMRMbSGm5H4ExJR', + 'yLSCqx7dcM5JKR2fG7vHbF2axMvuYqomaw', + 'yVij8XpJ78LM5hepSV1KF7T8vRpUEXCpK5', + 'ydJpjuJGossAZR7S5oS7cWvjygEwoj8Xwp', + 'yW3TmWnmhvpxRbgFcQ8oXqDRkn3RhRH6jj', + 'yRegVX85DThKRkH8C61TtRacfzrkiBfNy5', + 'yPtDCqDFRe1JuDp8pvdiEMQMz2erGwS3VG', + 'yM9pSw3L4oBfG7uQL5o522Hu3WTvy9awgZ', + 'yNC6qYJYungzuk5XUynDFKCn54Dy8ngox4', +]; +const internalAddresses = [ + 'yNDpPsJqXKM36zHSNEW7c1zSvNnrZ699FY', + 'yLk4Hw3w4zDudrDVP6W8J9TggkY57zQUki', + 'yirJaK8KCE5YAmwvLadizqFw3TCXqBuZXL', + 'yhdRfg5gNr587dtEC4YYMcSHmLVEGqqtHc', + 'yYwKP1FQae5kbjXkmuirGx6Xzf8NzHpLqW', + 'yX9gmsm8aSxZZjYhq4w35aidT7qbhcpNjU', + 'ybgXCTGMHEBbQeUib8c3xAjtGAc12XtWiU', + 'yS31WpdMT2b34uL9C37fbUoACHhiupHCyP', + 'yTSpFqRoX3vyN286AUtKKhgmX5Xb41YKQe', + 'yQU5YsqN7psTTASuYbcMi7N5nNZGaxXb2X', + 'yVGGFj9BLgEab5rucSGLC6UGVLQKB4U1wJ', + 'yQCh5yYCHEbJzgSJE9rdHiqXHidKm3kwr5', + 'yX7T3Ac3yaLk5CTC5UaR93Fc7SjYkeT5hn', + 'yXx3WXq8kYNPbYEg5U6bL8Xfih4g5LCYVo', + 'yYnLMTz3jCi2KKKNuo3TVkEAGyUFg8tgkJ', + 'yiKa1dA6B4tSTNJqJP9Y5pQfQEffnQQDTL', + 'yf7vcuDnE9DVhXdMfBMQQTEi43otYQzkWE', + 'yTmSmocwERCeRHqNNG5SbpYKUra1HTmj8m', + 'yivUe5NeJsGsREwPQZUGYaTSwWB3E1oLcz', + 'ygfsZojdfW9UjCRU4ra95Aq6YgCC7UqZFx', + 'yU9fdXaUVtefwDZvxjJAr9xj1z2MtYi34A', + 'yXgMN6FgrgZCnTN1vhoZMh8afKMBmi3JC4', + 'yiqaCbXscvR8y3VFYMzdaKCaAGuDuZxMzt', + 'ydcgWDxheSxrLAqDBP4JXBndMCzUNf77gq', + 'yYccLAwvYUDkjSp8VXvEyZ1t2i799pGrde', + 'yMRfbbqFZvojgYZCshdJNWJHruQb3DuCSC' +]; + +const fixtureTransactions = {}; + +const mockTransactions = (amount) => { + return Array.from({ length: amount }).map((_, index) => { + const tx = new Transaction(); + + // Produce random lock date + const date = new Date() + date.setMinutes(Math.floor(Math.random() * 60) + index) + + tx.lockUntilDate(date) + + return tx; + }) +} + +for(let i = 0; i<=externalAddresses.length; i++){ + let path = `m/44'/1'/0'/0/${i}`; + + let externalTransactions = []; + + // Leave some addresses without any tx + if (i < externalAddresses.length / 2) { + externalTransactions = mockTransactions(3); + } + + fixtureAddressesStore.external[path] = { + path, + index: i, + transactions: externalTransactions.map(tx => tx.hash), + balanceSat: 0, + unconfirmedBalanceSat: 0, + utxos: {}, + address: externalAddresses[i] + } + + path = `m/44'/1'/0'/1/${i}`; + + let internalTransactions = []; + // Leave some addresses without any tx + if (i < internalAddresses.length / 2) { + internalTransactions = mockTransactions(3); + + if (externalTransactions.length) { + // Simulate TX change return from the external TX + internalTransactions.push(externalTransactions[0]) + } + } + + fixtureAddressesStore.internal[path] = { + path, + index: i, + transactions: internalTransactions.map(tx => tx.hash), + balanceSat: 0, + unconfirmedBalanceSat: 0, + utxos: {}, + address: internalAddresses[i] + }; + + [...externalTransactions, ...internalTransactions].forEach(tx => { + Object.assign(fixtureTransactions, { [tx.hash]: tx}) + }) +} + +describe('Utils - filterTransactions', function suite() { + it('should correctly filter a transaction', () => { + const accountStore = { + addresses: fixtureAddressesStore, + }; + const walletType = WALLET_TYPES.HDWALLET; + const accountIndex = 0; + const result = filterTransactions(accountStore, walletType, accountIndex, fixtureTransactions); + result.sort((a,b) => a.nLockTime - b.nLockTime); + const expectedResult = Object.values(fixtureTransactions) + .sort((a, b) => a.nLockTime - b.nLockTime) + + expect(result).to.deep.equal(expectedResult); + + const accountIndex1 = 1; + const result2 = filterTransactions(accountStore, walletType, accountIndex1, fixtureTransactions); + const expectedEmptyResult = []; + expect(result2).to.deep.equal(expectedEmptyResult); + }); +}); diff --git a/packages/wallet-lib/src/utils/fundWallet.js b/packages/wallet-lib/src/utils/fundWallet.js new file mode 100644 index 00000000000..9f872ac5166 --- /dev/null +++ b/packages/wallet-lib/src/utils/fundWallet.js @@ -0,0 +1,48 @@ +const EVENTS = require('../EVENTS'); + +/** + * + * @param {Account} walletAccount + * @param {string} id - transaction id + * @return {Promise} + */ +function waitForTransaction(walletAccount, id) { + return new Promise(((resolve) => { + const listener = (event) => { + const { payload: { transaction } } = event; + + if (transaction.id === id) { + walletAccount.removeListener(EVENTS.FETCHED_CONFIRMED_TRANSACTION, listener); + + resolve(transaction.id); + } + }; + + walletAccount.on(EVENTS.FETCHED_CONFIRMED_TRANSACTION, listener); + })); +} + +/** + * + * @param {Wallet} faucetWallet + * @param {Wallet} recipientWallet + * @param {number} amount + * @return {Promise} + */ +async function fundWallet(faucetWallet, recipientWallet, amount) { + const faucetAccount = await faucetWallet.getAccount(); + const recipientAccount = await recipientWallet.getAccount(); + const transaction = await faucetAccount.createTransaction({ + satoshis: amount, + recipient: recipientAccount.getAddress().address, + }); + + await Promise.all([ + faucetAccount.broadcastTransaction(transaction), + waitForTransaction(recipientAccount, transaction.id), + ]); + + return transaction.id; +} + +module.exports = fundWallet; diff --git a/packages/wallet-lib/src/utils/getBytesOf.js b/packages/wallet-lib/src/utils/getBytesOf.js new file mode 100644 index 00000000000..247af4615c0 --- /dev/null +++ b/packages/wallet-lib/src/utils/getBytesOf.js @@ -0,0 +1,20 @@ +const { Script, Address } = require('@dashevo/dashcore-lib'); + +function getBytesOf(elem, type) { + let BASE_BYTES = 0; + let SCRIPT_BYTES = 0; + + switch (type) { + case 'utxo': + BASE_BYTES = 32 + 4 + 1 + 4; + SCRIPT_BYTES = Buffer.from(elem.script, 'hex').length; + return BASE_BYTES + SCRIPT_BYTES; + case 'output': + BASE_BYTES = 8 + 1; + SCRIPT_BYTES = Script(new Address(elem.address)).toBuffer().length; + return BASE_BYTES + SCRIPT_BYTES; + default: + return false; + } +} +module.exports = getBytesOf; diff --git a/packages/wallet-lib/src/utils/getBytesOf.spec.js b/packages/wallet-lib/src/utils/getBytesOf.spec.js new file mode 100644 index 00000000000..9ef1a6f9d58 --- /dev/null +++ b/packages/wallet-lib/src/utils/getBytesOf.spec.js @@ -0,0 +1,8 @@ +const { expect } = require('chai'); +const { getBytesOf } = require("./index"); + +describe('Utils - getBytesOf', function suite() { + it('should have getBytesOf return false on unknown type', () => { + expect(getBytesOf(null, 'foo')).to.be.equal(false); + }); +}); diff --git a/packages/wallet-lib/src/utils/hasMethod.js b/packages/wallet-lib/src/utils/hasMethod.js new file mode 100644 index 00000000000..6f00af1ac2c --- /dev/null +++ b/packages/wallet-lib/src/utils/hasMethod.js @@ -0,0 +1,10 @@ +/** + * + * @param {object} obj + * @param {string} methodName + * @return {boolean} + */ +function hasMethod(obj, methodName) { + return !!obj && typeof obj[methodName] === 'function'; +} +module.exports = hasMethod; diff --git a/packages/wallet-lib/src/utils/hasMethod.spec.js b/packages/wallet-lib/src/utils/hasMethod.spec.js new file mode 100644 index 00000000000..e635241ddc6 --- /dev/null +++ b/packages/wallet-lib/src/utils/hasMethod.spec.js @@ -0,0 +1,10 @@ +const { expect } = require('chai'); +const { hasMethod} = require("./index"); + +describe('Utils - hasMethod', function suite() { + it('should correctly handle method detection', function () { + expect(hasMethod({ method1: ()=>null }, 'method1')).to.equal(true); + expect(hasMethod({ method1: ()=>null }, 'method2')).to.equal(false); + expect(hasMethod(null, 'method1')).to.equal(false); + }); +}); diff --git a/packages/wallet-lib/src/utils/hasProp.js b/packages/wallet-lib/src/utils/hasProp.js new file mode 100644 index 00000000000..a050ff046e3 --- /dev/null +++ b/packages/wallet-lib/src/utils/hasProp.js @@ -0,0 +1,9 @@ +function hasProp(obj, prop) { + if (!obj) return false; + if (Array.isArray(obj)) { + return obj.includes(prop); + } + return {}.hasOwnProperty.call(obj, prop); +} + +module.exports = hasProp; diff --git a/packages/wallet-lib/src/utils/hasProp.spec.js b/packages/wallet-lib/src/utils/hasProp.spec.js new file mode 100644 index 00000000000..43a7d9ef3b9 --- /dev/null +++ b/packages/wallet-lib/src/utils/hasProp.spec.js @@ -0,0 +1,12 @@ +const { expect } = require('chai'); +const { hasProp } = require("./index"); + +describe('Utils - hasProp', function suite() { + it('should correctly handle property detection', function () { + expect(hasProp({ key1: true }, 'key1')).to.equal(true); + expect(hasProp({ key1: true }, 'key2')).to.equal(false); + expect(hasProp(['key1'], 'key1')).to.equal(true); + expect(hasProp(['key1'], 'key2')).to.equal(false); + expect(hasProp(null, 'key2')).to.equal(false); + }); +}); diff --git a/packages/wallet-lib/src/utils/index.js b/packages/wallet-lib/src/utils/index.js new file mode 100644 index 00000000000..c23bb6a6e58 --- /dev/null +++ b/packages/wallet-lib/src/utils/index.js @@ -0,0 +1,52 @@ +const extendTransactionsWithMetadata = require('./extendTransactionsWithMetadata'); +const calculateTransactionFees = require('./calculateTransactionFees'); +const categorizeTransactions = require('./categorizeTransactions'); +const calculateDuffBalance = require('./calculateDuffBalance'); +const filterTransactions = require('./filterTransactions'); +const { hash, doubleSha256, sha256 } = require('./crypto'); +const { varIntSizeBytesFromLength } = require('./varInt'); +const classifyAddresses = require('./classifyAddresses'); +const feeCalculation = require('./feeCalculation'); +const coinSelection = require('./coinSelection'); +const fundWallet = require('./fundWallet'); +const dashToDuffs = require('./dashToDuffs'); +const duffsToDash = require('./duffsToDash'); +const getBytesOf = require('./getBytesOf'); +const hasMethod = require('./hasMethod'); +const hasProp = require('./hasProp'); +const is = require('./is'); + +const { + generateNewMnemonic, + mnemonicToHDPrivateKey, + mnemonicToWalletId, + seedToHDPrivateKey, + mnemonicToSeed, +} = require('./mnemonic'); + +module.exports = { + extendTransactionsWithMetadata, + varIntSizeBytesFromLength, + calculateTransactionFees, + categorizeTransactions, + mnemonicToHDPrivateKey, + calculateDuffBalance, + generateNewMnemonic, + seedToHDPrivateKey, + mnemonicToWalletId, + filterTransactions, + classifyAddresses, + mnemonicToSeed, + feeCalculation, + coinSelection, + doubleSha256, + dashToDuffs, + duffsToDash, + fundWallet, + getBytesOf, + hasMethod, + hasProp, + sha256, + hash, + is, +}; diff --git a/packages/wallet-lib/src/utils/is.js b/packages/wallet-lib/src/utils/is.js new file mode 100644 index 00000000000..d8b25fa1120 --- /dev/null +++ b/packages/wallet-lib/src/utils/is.js @@ -0,0 +1,53 @@ +/* eslint-disable max-len */ +// Todo : Some validators here are really proto type of methods, urgent impr is needed here. +const { + PrivateKey, + PublicKey, + HDPrivateKey, + HDPublicKey, + Transaction, + Mnemonic, + Address, +} = require('@dashevo/dashcore-lib'); + +const is = { + // Primitives + arr: (arr) => is.def(arr) && (Array.isArray(arr) || arr.constructor.name === Array.name), + num: (num) => !Number.isNaN(num) && typeof num === 'number', + float: ((float) => is.num(float) && Math.floor(float) !== float), + int: (int) => Number.isInteger(int) || (is.num(int) && Math.floor(int) === int), + hex: (h) => is.string(h) && (h.match(/([0-9]|[a-f])/gim) || []).length === h.length, + string: (str) => typeof str === 'string', + bool: (b) => b === true || b === false, + obj: (obj) => obj === Object(obj), + fn: (fn) => typeof fn === 'function', + type(val, type) { return val && val.constructor.name === type; }, + def: (val) => val !== undefined, + undef: (val) => val === undefined, + null: (val) => val === null, + exist: (val) => !is.undefOrNull(val), + undefOrNull: (val) => is.undef(val) || is.null(val), + promise: (fn) => fn && is.fn(fn.then) && is.fn(fn.catch), + JSON(val) { try { JSON.stringify(val); return true; } catch (e) { return false; } }, + stringified(val) { try { JSON.parse(val); return true; } catch (e) { return false; } }, + mnemonic: (mnemonic) => !is.undefOrNull(mnemonic) && (is.string(mnemonic) || mnemonic.constructor.name === Mnemonic.name), + network: (network) => !is.undefOrNull(network) && (is.string(network)), + publicKey: (pKey) => !is.undefOrNull(pKey) && (pKey.constructor.name === PublicKey.name || (is.string(pKey) && PublicKey.isValid(pKey))), + privateKey: (pKey) => !is.undefOrNull(pKey) && (pKey.constructor.name === PrivateKey.name || (is.string(pKey) && PrivateKey.isValid(pKey))), + HDPrivateKey: (hdKey) => !is.undefOrNull(hdKey) && (hdKey.constructor.name === HDPrivateKey.name || (is.string(hdKey) && HDPrivateKey.isValidSerialized(hdKey))), + HDPublicKey: (hdKey) => !is.undefOrNull(hdKey) && (hdKey.constructor.name === HDPublicKey.name || (is.string(hdKey) && HDPublicKey.isValidSerialized(hdKey))), + seed: (seed) => !is.undefOrNull(seed) && (is.string(seed)), + address: (addr) => !is.undefOrNull(addr) && (is.string(addr) || addr.constructor.name === Address.name), + addressObj: (addrObj) => !is.undefOrNull(addrObj) && ((!is.undefOrNull(addrObj.address) && addrObj.address.constructor.name === Address.name) || (is.string(addrObj.address) && (is.string(addrObj.path)))), + transactionObj: (tx) => is.obj(tx) && is.txid(tx.txid) && tx.vin && is.arr(tx.vin) && tx.vout && is.arr(tx.vout), + dashcoreTransaction: (tx) => is.type(tx, Transaction.name), + feeRate: (feeRate) => is.obj(feeRate) && is.string(feeRate.type) && is.int(feeRate.value), + txid: (txid) => is.string(txid) && txid.length === 64, + utxo: (utxo) => is.type(utxo, Transaction.UnspentOutput.name), + output: (output) => is.obj(output) && is.num(output.satoshis) && is.address(output.address), + rawtx: (rawtx) => is.def(rawtx) && is.hex(rawtx) && (() => { try { Transaction(rawtx); return true; } catch (e) { return false; } })(), +}; +// aliases +is.array = is.arr; + +module.exports = is; diff --git a/packages/wallet-lib/src/utils/is.spec.js b/packages/wallet-lib/src/utils/is.spec.js new file mode 100644 index 00000000000..4948857d37a --- /dev/null +++ b/packages/wallet-lib/src/utils/is.spec.js @@ -0,0 +1,219 @@ +const { Mnemonic, Networks, Address } = require('@dashevo/dashcore-lib'); +const { expect } = require('chai'); +const { is, generateNewMnemonic} = require("./index"); +const figureBridgeFixture = require("../../fixtures/figurebridge"); + +describe('Utils - is', function suite() { + it('should have is.num handle numbers', () => { + expect(is.num(100)).to.be.equals(true); + }); + it('should have is.num handle not numbers', () => { + expect(is.num('100')).to.be.equals(false); + }); + it('should have is.arr handle empty arr', () => { + expect(is.arr([])).to.be.equals(true); + }); + it('should have is.arr handle arr', () => { + expect(is.arr([1, 'b'])).to.be.equals(true); + }); + it('should have is.arr handle not array(dict)', () => { + expect(is.arr({ 100: 'b' })).to.be.equals(false); + }); + it('should have is.arr handle not array(str)', () => { + expect(is.arr('str')).to.be.equals(false); + }); + it('should have is.float handle int', () => { + expect(is.float(100)).to.be.equals(false); + }); + it('should have is.float handle float with .0(not float)', () => { + expect(is.float(100.0)).to.be.equals(false); + }); + it('should have is.float handle float', () => { + expect(is.float(100.2)).to.be.equals(true); + }); + it('should have is.float handle not float(str)', () => { + expect(is.num('100')).to.be.equals(false); + }); + + it('should have is.int handle int', () => { + expect(is.int(100)).to.be.equals(true); + }); + it('should have is.int handle zero', () => { + expect(is.int(0)).to.be.equals(true); + }); + it('should have is.int handle negative int', () => { + expect(is.int(-1)).to.be.equals(true); + }); + + it('should have is.int handle float with .0', () => { + expect(is.int(100.0)).to.be.equals(true); + }); + it('should have is.int handle float', () => { + expect(is.int(100.2)).to.be.equals(false); + }); + it('should have is.int handle not float(str)', () => { + expect(is.int('100')).to.be.equals(false); + }); + + it('should have is.bool handle true', () => { + expect(is.bool(true)).to.be.equals(true); + }); + it('should have is.bool handle false', () => { + expect(is.bool(false)).to.be.equals(true); + }); + it('should have is.bool handle int', () => { + expect(is.bool('true')).to.be.equals(false); + }); + it('should have is.hex handle hex', () => { + expect(is.hex('1234567890ABCD')).to.be.equals(true); + expect(is.hex('0b757a848f')).to.equal(true); + expect(is.hex('')).to.be.equals(true); + }); + it('should have is.hex handle not hex', () => { + expect(is.hex('12648430T')).to.be.equals(false); + }); + + it('should have is.obj handle obj', () => { + expect(is.obj(generateNewMnemonic())).to.be.equals(true); + }); + it('should have is.obj handle primitive value', () => { + expect(is.obj(false)).to.be.equals(false); + }); + it('should have is.obj handle array', () => { + expect(is.obj(['false'])).to.be.equals(true); + }); + + it('should have is.fn handle obj', () => { + expect(is.fn(generateNewMnemonic)).to.be.equals(true); + }); + it('should have is.fn handle primitive value', () => { + expect(is.fn(false)).to.be.equals(false); + }); + it('should have is.fn handle arrow function', () => { + expect(is.fn(() => generateNewMnemonic)).to.be.equals(true); + }); + + it('should have is.def handle any value', () => { + expect(is.def(1)).to.be.equals(true); + }); + it('should have is.def handle undefined', () => { + expect(is.def(undefined)).to.be.equals(false); + }); + + it('should have is.undef handle undefined', () => { + expect(is.undef(undefined)).to.be.equals(true); + }); + it('should have is.undef handle any value', () => { + expect(is.undef('undefined')).to.be.equals(false); + }); + + it('should have is.null handle null', () => { + expect(is.null(null)).to.be.equals(true); + }); + it('should have is.null handle any value', () => { + expect(is.null('null')).to.be.equals(false); + }); + + it('should have is.promise handle promise', () => { + const promise = new Promise((() => { + })); + expect(is.promise(promise)).to.be.equals(true); + }); + it('should have is.promise handle non promise', () => { + expect(is.promise(() => generateNewMnemonic)).to.be.equals(false); + }); + + it('should have is.JSON handle empty json', () => { + expect(is.JSON()).to.be.equals(true); + }); + it('should have is.JSON handle array', () => { + expect(is.JSON([1, 2])).to.be.equals(true); + }); + it('should have is.JSON handle str as json', () => { + expect(is.JSON('str')).to.be.equals(true); + }); + it('should have is.JSON not allow circular references', () => { + const circularReference = {}; + circularReference.myself = circularReference; + expect(is.JSON(circularReference)).to.be.equals(false); + }); + + it('should have is.stringified handle empty JSON', () => { + expect(is.stringified('{}')).to.be.equals(true); + }); + it('should have is.stringified handle JSON', () => { + expect(is.stringified('{"result":true, "count":42}')).to.be.equals(true); + }); + it('should have is.stringified handle str', () => { + expect(is.stringified('true')).to.be.equals(true); + }); + it('should have is.stringified not allow circular references', () => { + const circularReference = {}; + circularReference.myself = circularReference; + expect(is.stringified(circularReference)).to.be.equals(false); + }); + it('should have is.type handle type', () => { + const arr = []; + expect(is.type(arr, 'Array')).to.be.equal(true); + }); + it('should have is.mnemonic work', () => { + const mnemonic = new Mnemonic(); + const mnemonic2 = 'crack spice venue ticket vacant steak next stomach amateur review okay curtain'; + expect(is.mnemonic(mnemonic)).to.be.equal(true); + expect(is.mnemonic(mnemonic2)).to.be.equal(true); + }); + it('should have is.network work', () => { + const notANetwork = []; + const network = Networks.livenet.toString(); + const networktestnet = Networks.testnet.toString(); + const network2 = 'livenet'; + const network2testnet = 'testnet'; + const notanetwork = notANetwork; + expect(is.network(network)).to.be.equal(true); + expect(is.network(networktestnet)).to.be.equal(true); + expect(is.network(network2)).to.be.equal(true); + expect(is.network(network2testnet)).to.be.equal(true); + expect(is.network(notanetwork)).to.be.equal(false); + }); + it('should have is.seed work', () => { + const seed = new Mnemonic().toSeed(); + const seed2 = new Mnemonic().toHDPrivateKey(); + expect(is.seed(seed.toString('hex'))).to.be.equal(true); + expect(is.seed(seed2)).to.be.equal(false); + }); + it('should have is.HDPrivateKey work', () => { + const seed = new Mnemonic().toSeed(); + const seed2 = new Mnemonic().toHDPrivateKey(); + expect(is.seed(seed.toString('hex'))).to.be.equal(true); + expect(is.seed(seed2)).to.be.equal(false); + }); + it('should have is.address work', () => { + const addr = new Address('yinidcHwrfzb4bEJDSq3wtQyxRAgQxsQia'); + expect(is.address(addr)).to.be.equal(true); + }); + it('should have is.txid work', () => { + const validtxid = '56150e17895255d178eb4d3da0ccd580fdf50233a3767e1f562e05f00b48cf79'; + expect(is.txid(validtxid)).to.be.equal(true); + + const invalidtxid = '00000'; + expect(is.txid(invalidtxid)).to.be.equal(false); + }); + it('should have is.transactionObj work', () => { + const validTransaction = figureBridgeFixture.transactions['3428f0c29370d1293b4706ffd0f8b0c84a5b7c1c217d319e5ef4722354000c6e']; + expect(is.transactionObj(validTransaction)).to.be.equal(true); + + const invalidTransaction = { + vin: [], + vout: [], + }; + expect(is.transactionObj(invalidTransaction)).to.be.equal(false); + }); + it('should have is.feeRate work', () => { + const feeRate = { + type: 'perBytes', + value: 10, + }; + expect(is.feeRate(feeRate)).to.be.equal(true); + }); + +}); diff --git a/packages/wallet-lib/src/utils/isBrowser.js b/packages/wallet-lib/src/utils/isBrowser.js new file mode 100644 index 00000000000..c11a2644b49 --- /dev/null +++ b/packages/wallet-lib/src/utils/isBrowser.js @@ -0,0 +1,4 @@ +// eslint-disable-next-line no-new-func +const isBrowser = new Function('try {return this===window;}catch(e){ return false;}'); + +module.exports = isBrowser; diff --git a/packages/wallet-lib/src/utils/mnemonic.js b/packages/wallet-lib/src/utils/mnemonic.js new file mode 100644 index 00000000000..ff30fdabc8f --- /dev/null +++ b/packages/wallet-lib/src/utils/mnemonic.js @@ -0,0 +1,47 @@ +const { pbkdf2Sync } = require('pbkdf2'); +const { Mnemonic, HDPrivateKey } = require('@dashevo/dashcore-lib'); +const { doubleSha256 } = require('./crypto'); + +function generateNewMnemonic() { + return Mnemonic(); +} + +/** + * Will return the HDPrivateKey from a Mnemonic + * @param {Mnemonic|String} mnemonic + * @param {Networks | String} network + * @param {String} passphrase + * @return {HDPrivateKey} + */ +function mnemonicToHDPrivateKey(mnemonic, network = 'testnet', passphrase = '') { + if (!mnemonic) throw new Error('Expect mnemonic to be provided'); + + return (mnemonic.constructor.name === Mnemonic.name) + ? mnemonic.toHDPrivateKey(passphrase, network) + : new Mnemonic(mnemonic).toHDPrivateKey(passphrase, network); +} + +function mnemonicToWalletId(mnemonic) { + if (!mnemonic) throw new Error('Expect mnemonic to be provided'); + + const buffMnemonic = Buffer.from(mnemonic.toString()); + const buff = doubleSha256(buffMnemonic); + return buff.toString('hex').slice(0, 10); +} +const mnemonicToSeed = function mnemonicToSeed(mnemonic, password = '') { + const mnemonicBuff = Buffer.from(mnemonic.normalize('NFKD'), 'utf8'); + const saltBuff = Buffer.from(`mnemonic${password}`, 'utf8'); + return pbkdf2Sync(mnemonicBuff, saltBuff, 2048, 64, 'sha512') + .toString('hex'); +}; +// See https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki +const seedToHDPrivateKey = function seedToHDPrivateKey(seed, network = 'testnet') { + return HDPrivateKey.fromSeed(seed, network); +}; +module.exports = { + generateNewMnemonic, + mnemonicToHDPrivateKey, + mnemonicToWalletId, + mnemonicToSeed, + seedToHDPrivateKey, +}; diff --git a/packages/wallet-lib/src/utils/mnemonic.spec.js b/packages/wallet-lib/src/utils/mnemonic.spec.js new file mode 100644 index 00000000000..310465394ed --- /dev/null +++ b/packages/wallet-lib/src/utils/mnemonic.spec.js @@ -0,0 +1,141 @@ +const { expect } = require('chai'); +const { Networks } = require('@dashevo/dashcore-lib') +const { + generateNewMnemonic, + seedToHDPrivateKey, + mnemonicToHDPrivateKey, + mnemonicToWalletId, + mnemonicToSeed, +} = require('./mnemonic'); +const is = require('./is'); +const knifeEasilyFixture = require("../../fixtures/knifeeasily"); + +const mnemonic1 = 'hole lesson insane entire dolphin scissors game dwarf polar ethics drip math'; +const mnemonic2 = 'woman forest output essay bleak satisfy era ordinary exotic source portion wire'; +const mnemonic3 = 'divorce radar castle wire sun timber master income exchange wash fluid loud'; +const mnemonic4 = 'increase table banana fiscal innocent wool sport mercy motion stable prize promote'; + +const mnemonic24En = 'ability trim just nerve eternal sting jar sponsor nose fix explain acid thought cake evidence kite clog stable surge actress cushion awake latin trim'; +const expectedSeed24En = '8b991cf7bbbcda09c6542f4497f3e32b6b7e199d69f7bcd65bde001c28a3283e330c5a20f620ac6169e198b65c7a4b5ec46dbdee8f86e082421af9f6a81933e8'; +const expectedEnRootKey = 'xprv9s21ZrQH143K2EfPZFVjrocWU5451xCPASWvczv9bs7TFdxVKSgb6d4yb6rQWB8Qey321JQN3hDsfUScfiwzpzZALKXcwNnTzf5opbsuqbT'; + +const mnemonic24Jp = 'あいさつ むじゅん せんさい ちたい ごうまん ふひょう せのび ふうふ ちゃんこなべ ざいえき こそだて あそぶ ますく おおどおり こうりつ せんむ がっこう ふすま へらす あっしゅく きまる いらい そうなん むじゅん'; +const expectedSeed24Jp = '61b05c77ca18cd7d60254310886c1615a1d5397d58b032ee34a8886385a8a1106bd2cf1b8678eb2671cd744359f1b8c8bca183edb431f3e08d3c774189af1a3b'; +const expectedJpRootKey = 'xprv9s21ZrQH143K3WoLUCke5VK7Kkrjvp3K8vfFvbuKBXQMvBZRrPGJY3kCFAcXzw4DU8vxLe9kt2ShQjNB1pbes7oMzdafrrmppBtNuFYhRAc'; + +const mnemonic24Es = 'abdomen tirón lado mozo enorme sequía jugo sanidad músculo fiar esquí aceite tapa bolsa ensayo largo carne sección soltar acoger collar aprobar lento tirón'; +const expectedSeed24Es = '59681653fde7db94e926ab6e8da8ab1ced4b2e04a7a6b5fbec1eb718dba91206fe8bde5bef9b5234c11ebc223a35772cef4c2114a3b7bed6b3749812d122e292'; +const expectedEsRootKey = 'xprv9s21ZrQH143K3xmLC7RGEgYJAmPwZ699jsPVYkswkgKBztxvUusGGM45vDyS6z8runs8s31fqygFmcQ6wTbp7jybyWQNF7xwvr9NseKqiyx'; + +const mnemonic24Cn = '一 扑 苗 坦 财 辽 赵 筋 麻 析 投 国 涌 处 富 饭 信 棒 驶 用 称 间 裁 扑'; +const expectedSeed24Cn = '4e7d64cfb9d5f28f1ba2ece2e16a2263d928c84ce8657d8612b018ca16d8d9a8218596f819b4c5677d426eb7104c376c49f21ba4795888cc4239eedd3b7e6fa0'; +const expectedCnRootKey = 'xprv9s21ZrQH143K2CtwuutnGUo2SXtcTCZYcZ8Tg4KFm6C6WTMKGaEm3ga63dMsNarig7hfX8XzNfXdtBjBrs2Uaans5MmwdFKr5uDPqjcsTKb'; + +const mnemonic24CnTrad = '一 撲 苗 坦 財 遼 趙 筋 麻 析 投 國 湧 處 富 飯 信 棒 駛 用 稱 間 裁 撲'; +const expectedSeed24CnTrad = 'b26ac05cbba14b5ba6d8a42d725605fb32be201119ec3428a292f200a250d5a890509c473b79ac1c463704d1ca4a8d9b5cd5e82697f7194cf814a53b300e5f0c'; +const expectedCnTradRootKey = 'xprv9s21ZrQH143K2P4mYzdrX9ZCgM3xks4vTu2eGMrsd92CbTphBmDhwd4xPUneLq4WF66ETDiwHVzMS7T9BecoYCEw79JmxfGc3DfUrQLmpkL'; + +const mnemonic24Fr = 'abandon taupe herbe majorer écraser sanglier guimauve rouge maritime épine élitisme absurde soulever bonbon écrivain honneur cavalier ruisseau sénateur accepter compact aquarium hygiène taupe'; +const expectedSeed24fr = 'd9c46cf130dfa19e56e87a46e04be9bdec52c81b3f1db24f3011197deba2cfd4e9f62de3663165577d1d870e1f78e602eb3d0f1659a33bd2646fd58fbbbb7d7a'; +const expected24frRootKeyMain = 'xprv9s21ZrQH143K26ssNac4eHjBoeKdcZ1jZC8q1eBfrDcumyQwmnELDVFrb6Xd2E5GNtHYU1ayKv4EjfJoq5httWyaW1Fq6Zt5zvJ658m85FC'; + +const mnemonic24It = 'abbaglio topazio mantide osmosi famiglia soppeso magnete smeraldo ovocito fucilata fermento acre svista bretella fanfara maturo chirurgo sociale spronato adeguato custode arbitro mese topazio'; +const expectedSeed24It = '33100dc60a1661f5bef03282c61b7423d561bad7bfeef34b90efbb5fd4508033ae0e1f364abb8078cdeff20a3cb24824bb0f00f9166841b8a99893f3c4639ad3'; +const expectedItRootKey = 'xprv9s21ZrQH143K4btNDndrRbCDjxvmUBd17kwaapRTrivby5UkpzKapYq9v4oVMRhy7jq32gYCAKmNcviLgfZbNuC95zSKTB2UStYBNYRXTT7'; + +const mnemonic24Ko = '가끔 평양 시아버지 예상 버튼 초순 승리 책임 오른발 분석 별명 가정 태권도 기본 번역 시험 다이어트 철학 충격 간섭 마음 공개 신비 평양'; +const expectedSeed24Ko = '611498a9b705459973ae188db9659aec7b433af197b4f37e5e7832b4d13367f50e4ac6e9597f0decc1ec1287d8ff8fbdb7b5b35771cc7b349b2abbf9a9b42e50'; +const expectedKoRootKey = 'xprv9s21ZrQH143K2KzJuwYrhiBVik3Zi1Q1BjMJVvaCh3v7AVYpCDwpcUvhMEdeducZgGYoVT6vMfwNU63UMTiLAoxHk4KEAH8XbMrzAUnu8aP'; + +const mnemonic24Cz = 'abeceda vracet nezvykle peklo kolaps trhlina nehoda terapie pikle kultura koprovka archa varovat dominant kolize nosnost finance tlupa uhradit atlas honitba buvol obejmout vracet'; +const expectedSeed24Cz = '2735f49ddab96518fce00fc27b192c12bf05f884f83d55ef4464c820485569eea63be4a5b60378f3c85082774d76910c8bfd4eebe9705b77c75d8c9844a6cea5'; +const expectedCzRootKey = 'xprv9s21ZrQH143K4WmszXEM3kb6vPtvL67zWyzcZZiaqtijPhW5r1zmjvN4V66HKJYUEfoEDq199gHCE5AUUo4kMo3nReUT4EVofnakjaFLRQy'; + +const passSeed1 = 'superpassphrase'; +const expectedSeed1 = '436905e6756c24551bffaebe97d0ebd51b2fa027e838c18d45767bd833b02a80a1dd55728635b54f2b1dbed5963f4155e160ee1e96e2d67f7e8ac28557d87d96'; +const expectedPassSeed1 = 'f637c95a551647f3f49c707c2f40ea0ee38a70995ab108004529af55ea43bcf02c6bcb156f8750e6b4188ac1f0955505173336a1a1b579fe970071b0014be44c'; +const expectedPrivate1Mainnet = 'xprv9s21ZrQH143K3hVMJ7XzM4uiV1PndeSqGVzowkGjRpnSesDkmb3p5iGp8scGgAPjLw8Z3WZZr2BcbN2kfzqSYRG3VKSQgSszEdijEoWSDAC'; +const expectedPrivate1Testnet = 'tprv8ZgxMBicQKsPeWisxgPVWiXho8ozsAUqc3uvpAhBuoGvSTxqkxPZbTeG43mvgXn3iNfL3cBL1NmR4DaVoDBPMUXe1xeiLoc39jU9gRTVBd2'; + +describe('Utils - mnemonic', function suite() { + this.timeout(20000); + it('should generate new mnemonic', () => { + const result = generateNewMnemonic(); + expect(result.constructor.name).to.be.equal('Mnemonic'); + }); + it('should do mnemonicToHDPrivateKey', () => { + const mnem1 = generateNewMnemonic(); + const mnem2 = generateNewMnemonic().toString(); + const result = mnemonicToHDPrivateKey(mnem1); + const result2 = mnemonicToHDPrivateKey(mnem2); + expect(result.constructor.name).to.be.equal('HDPrivateKey'); + expect(result2.constructor.name).to.be.equal('HDPrivateKey'); + }); + it('should do mnemonicToWalletId', () => { + const mnem1 = generateNewMnemonic(); + const result = mnemonicToWalletId(mnem1); + expect(result.constructor.name).to.be.equal('String'); + expect(result.length).to.be.equal(10); + expect(is.hex(result)).to.be.equal(true); + + expect(mnemonicToWalletId(mnemonic1)).to.equal('f566600d81'); + expect(mnemonicToWalletId(mnemonic2)).to.equal('74bbe91a47'); + expect(mnemonicToWalletId(mnemonic3)).to.equal('f351a836e6'); + expect(mnemonicToWalletId(mnemonic4)).to.equal('fad183cbf7'); + + expect(() => mnemonicToWalletId()).to.throw('Expect mnemonic to be provided'); + expect(() => mnemonicToHDPrivateKey()).to.throw('Expect mnemonic to be provided'); + }); + it('should do mnemonicToSeed', () => { + expect(mnemonicToSeed(mnemonic1)).to.equal(expectedSeed1); + expect(mnemonicToSeed(mnemonic1, passSeed1)).to.equal(expectedPassSeed1); + }); + it('should do seedToHDPrivateKey', () => { + expect(seedToHDPrivateKey(expectedSeed1).toString()).to.equal(expectedPrivate1Testnet); + expect(seedToHDPrivateKey(expectedSeed1, 'mainnet').toString()).to.equal(expectedPrivate1Mainnet); + }); + it('should work with 24 words', () => { + expect(mnemonicToSeed(mnemonic24En).toString()).to.equal(expectedSeed24En); + expect(seedToHDPrivateKey(expectedSeed24En, 'mainnet').toString()).to.equal(expectedEnRootKey); + }); + it('should work with all languages', () => { + expect(mnemonicToSeed(mnemonic24Es).toString()).to.equal(expectedSeed24Es); + expect(seedToHDPrivateKey(expectedSeed24Es, 'mainnet').toString()).to.equal(expectedEsRootKey); + + expect(mnemonicToSeed(mnemonic24Jp).toString()).to.equal(expectedSeed24Jp); + expect(seedToHDPrivateKey(expectedSeed24Jp, 'mainnet').toString()).to.equal(expectedJpRootKey); + + expect(mnemonicToSeed(mnemonic24Cn).toString()).to.equal(expectedSeed24Cn); + expect(seedToHDPrivateKey(expectedSeed24Cn, 'mainnet').toString()).to.equal(expectedCnRootKey); + + expect(mnemonicToSeed(mnemonic24CnTrad).toString()).to.equal(expectedSeed24CnTrad); + expect(seedToHDPrivateKey(expectedSeed24CnTrad, 'mainnet').toString()).to.equal(expectedCnTradRootKey); + + expect(mnemonicToSeed(mnemonic24Fr).toString()).to.equal(expectedSeed24fr); + expect(seedToHDPrivateKey(expectedSeed24fr, 'mainnet').toString()).to.equal(expected24frRootKeyMain); + + expect(mnemonicToSeed(mnemonic24It).toString()).to.equal(expectedSeed24It); + expect(seedToHDPrivateKey(expectedSeed24It, 'mainnet').toString()).to.equal(expectedItRootKey); + + expect(mnemonicToSeed(mnemonic24Ko).toString()).to.equal(expectedSeed24Ko); + expect(seedToHDPrivateKey(expectedSeed24Ko, 'mainnet').toString()).to.equal(expectedKoRootKey); + + expect(mnemonicToSeed(mnemonic24Cz).toString()).to.equal(expectedSeed24Cz); + expect(seedToHDPrivateKey(expectedSeed24Cz, 'mainnet').toString()).to.equal(expectedCzRootKey); + }); + it('should generate a mnemonic', () => { + const mnemonic = generateNewMnemonic(); + expect(mnemonic).to.be.a('object'); + expect(mnemonic.toString()).to.be.a('string'); + }); + it('should convert mnemonic to seed', () => { + const network = Networks.testnet; + const seed = mnemonicToHDPrivateKey(knifeEasilyFixture.mnemonic, network); + expect(seed).to.be.a('object'); + expect(seed.toString()).to.equal(knifeEasilyFixture.HDRootPrivateKeyTestnet); + }); + it('should throw error when mnemonic is not provided in mnemonicToHDPrivateKey', () => { + const network = Networks.testnet; + expect(() => mnemonicToHDPrivateKey('', network)).to.throw('Expect mnemonic to be provide'); + }); +}); diff --git a/packages/wallet-lib/src/utils/outputHandler.js b/packages/wallet-lib/src/utils/outputHandler.js new file mode 100644 index 00000000000..568b571726d --- /dev/null +++ b/packages/wallet-lib/src/utils/outputHandler.js @@ -0,0 +1,10 @@ +module.exports = function outputHandler(outputs) { + return outputs.map((output) => { + const result = {}; + if (output.type === 'P2PKH') { + result.value = output.amount; + result.script = output.scriptPubKey; + } + return result; + }); +}; diff --git a/packages/wallet-lib/src/utils/outputHandler.spec.js b/packages/wallet-lib/src/utils/outputHandler.spec.js new file mode 100644 index 00000000000..a7e715cc1e0 --- /dev/null +++ b/packages/wallet-lib/src/utils/outputHandler.spec.js @@ -0,0 +1,25 @@ +const { expect } = require('chai'); +const outputHandler = require('./outputHandler'); + +describe('Utils - outputHandler', function suite() { + this.timeout(10000); + it('should work', () => { + const outputs = [{ + amount: 9.9999, + address: 'yeuLv2E9FGF4D9o8vphsaC2Vxoa8ZA7Efp', + scriptPubKey: '76a914cbdb740680e713c141e9fb32e92c7d90a3f3297588ac', + }, + { + amount: 9.9999, + address: 'yeuLv2E9FGF4D9o8vphsaC2Vxoa8ZA7Efp', + scriptPubKey: '76a914cbdb740680e713c141e9fb32e92c7d90a3f3297588ac', + type: 'P2PKH', + }]; + const expected = [{}, { + value: 9.9999, + script: '76a914cbdb740680e713c141e9fb32e92c7d90a3f3297588ac', + }]; + const result = outputHandler(outputs); + expect(result).to.deep.equal(expected); + }); +}); diff --git a/packages/wallet-lib/src/utils/sleep.js b/packages/wallet-lib/src/utils/sleep.js new file mode 100644 index 00000000000..2691f5be9ad --- /dev/null +++ b/packages/wallet-lib/src/utils/sleep.js @@ -0,0 +1,4 @@ +const sleep = (time) => new Promise((resolve) => { + setTimeout(resolve, time); +}); +module.exports = sleep; diff --git a/packages/wallet-lib/src/utils/sortTransactions.js b/packages/wallet-lib/src/utils/sortTransactions.js new file mode 100644 index 00000000000..d64e19c8ce8 --- /dev/null +++ b/packages/wallet-lib/src/utils/sortTransactions.js @@ -0,0 +1,49 @@ +/** + * @typedef TxMetadata + * @property {number} height + * @property {string} blockHash + * @property {boolean} isChainLocked + * @property {boolean} isInstantLocked + */ + +/** + * Sorts transactions by height taking into account prevTx linkage within the same height + * @typedef sortTransactions + * @param {{ transaction: Transaction, metadata: TxMetadata }} txsWithMetadata + * @returns {Transaction[]} + */ +const sortTransactions = (txsWithMetadata) => { + const transactionsByHeight = txsWithMetadata.reduce((acc, { transaction, metadata }) => { + const { height } = metadata; + + if (!acc[height]) { + acc[height] = []; + } + + acc[height].push(transaction); + + return acc; + }, {}); + + return Object.keys(transactionsByHeight) + .sort((a, b) => parseInt(a, 10) - parseInt(b, 10)) + .reduce((acc, height) => { + transactionsByHeight[height].sort((a, b) => { + // const prevTxHashBuffer = Buffer.alloc(32); + const prevTxHashes = new Set(); + b.inputs.forEach((input) => { + if (input.prevTxId.readUInt32BE() !== 0) { + prevTxHashes.add(input.prevTxId.toString('hex')); + } + }); + + if (prevTxHashes.has(a.hash)) { + return -1; + } + return 0; + }); + return acc.concat(transactionsByHeight[height]); + }, []); +}; + +module.exports = sortTransactions; diff --git a/packages/wallet-lib/src/utils/varInt.js b/packages/wallet-lib/src/utils/varInt.js new file mode 100644 index 00000000000..8fd8fffb97e --- /dev/null +++ b/packages/wallet-lib/src/utils/varInt.js @@ -0,0 +1,15 @@ +module.exports = { + varIntSizeBytesFromLength: (length) => { + let bytes = 1; + if (length >= 0xfd) { + bytes += 2; + if (length >= 0xffff) { + bytes += 2; + if (length >= 0xffffffff) { + bytes += 4; + } + } + } + return bytes; + }, +}; diff --git a/packages/wallet-lib/src/utils/varInt.spec.js b/packages/wallet-lib/src/utils/varInt.spec.js new file mode 100644 index 00000000000..7f51571cdc3 --- /dev/null +++ b/packages/wallet-lib/src/utils/varInt.spec.js @@ -0,0 +1,13 @@ +const { expect } = require('chai'); +const varInt = require('./varInt'); + +describe('Utils - varInt', function suite() { + this.timeout(10000); + it('should get varint of size from length', () => { + expect(varInt.varIntSizeBytesFromLength()).to.equal(1); + expect(varInt.varIntSizeBytesFromLength(1)).to.equal(1); + expect(varInt.varIntSizeBytesFromLength(42)).to.equal(1); + expect(varInt.varIntSizeBytesFromLength(42000)).to.equal(3); + expect(varInt.varIntSizeBytesFromLength(4200000000)).to.equal(5); + }); +}); diff --git a/packages/wallet-lib/tests/functional/wallet.js b/packages/wallet-lib/tests/functional/wallet.js new file mode 100644 index 00000000000..e78856ab4f5 --- /dev/null +++ b/packages/wallet-lib/tests/functional/wallet.js @@ -0,0 +1,186 @@ +const { expect } = require('chai'); + +const { Wallet } = require('../../src/index'); + +const { fundWallet } = require('../../src/utils'); +const { EVENTS } = require('../../src'); + +const seeds = process.env.DAPI_SEED + .split(','); + +let newWallet; +let wallet; +let account; +let faucetWallet; + +describe('Wallet-lib - functional', function suite() { + this.timeout(700000); + + before(() => { + faucetWallet = new Wallet({ + transport: { + seeds, + }, + network: process.env.NETWORK, + privateKey: process.env.FAUCET_PRIVATE_KEY + }); + }); + + after('Disconnection', () => { + account.disconnect(); + wallet.disconnect(); + newWallet.disconnect(); + faucetWallet.disconnect(); + }); + + describe('Wallet', () => { + describe('Create a new Wallet', () => { + it('should create a new wallet with default params', () => { + newWallet = new Wallet({ + transport: { + seeds, + }, + network: process.env.NETWORK, + }); + + expect(newWallet.walletType).to.be.equal('hdwallet'); + expect(newWallet.plugins).to.be.deep.equal({}); + expect(newWallet.accounts).to.be.deep.equal([]); + expect(newWallet.keyChainStore.getMasterKeyChain().rootKeyType) + .to.be.deep.equal('HDPrivateKey'); + expect(newWallet.passphrase).to.be.deep.equal(null); + expect(newWallet.allowSensitiveOperations).to.be.deep.equal(false); + expect(newWallet.injectDefaultPlugins).to.be.deep.equal(true); + expect(newWallet.walletId).to.length(10); + expect(newWallet.network).to.be.deep.equal('testnet'); + + const exported = newWallet.exportWallet(); + expect(exported.split(' ').length).to.equal(12); + }); + }); + + describe('Load a wallet', () => { + it('should load a wallet from mnemonic', () => { + wallet = new Wallet({ + mnemonic: newWallet.mnemonic, + transport: { + seeds, + }, + network: process.env.NETWORK, + }); + + expect(wallet.walletType).to.be.equal('hdwallet'); + expect(wallet.plugins).to.be.deep.equal({}); + expect(wallet.accounts).to.be.deep.equal([]); + expect(newWallet.keyChainStore.getMasterKeyChain().rootKeyType) + .to.be.deep.equal('HDPrivateKey'); + expect(wallet.passphrase).to.be.deep.equal(null); + expect(wallet.allowSensitiveOperations).to.be.deep.equal(false); + expect(wallet.injectDefaultPlugins).to.be.deep.equal(true); + expect(wallet.walletId).to.length(10); + expect(wallet.network).to.be.deep.equal('testnet'); + + const exported = wallet.exportWallet(); + expect(exported).to.equal(newWallet.mnemonic); + }); + }); + }); + + describe('Account', () => { + it('should await readiness', async () => { + account = await wallet.getAccount(); + await account.isReady(); + expect(account.state.isReady).to.be.deep.equal(true); + }); + + it('populate balance with dash', async () => { + const balanceBeforeTopUp = account.getTotalBalance(); + const amountToTopUp = 20000; + + await fundWallet( + faucetWallet, + wallet, + amountToTopUp + ); + + const balanceAfterTopUp = account.getTotalBalance(); + const transactions = account.getTransactions(); + + expect(Object.keys(transactions).length).to.be.equal(1); + expect(balanceBeforeTopUp).to.be.equal(0); + expect(balanceAfterTopUp).to.be.equal(amountToTopUp); + }); + + it('should has unusedAddress with index 1', () => { + const unusedAddress = account.getUnusedAddress(); + expect(unusedAddress.index).to.equal(1); + }); + + it('should not have empty balance', () => { + expect(account.getTotalBalance()).to.not.equal(0); + }); + + it('should returns some available UTXO', () => { + const UTXOs = account.getUTXOS(); + expect(UTXOs.length).to.not.equal(0); + }); + + it('should create a transaction', () => { + const newTx = account.createTransaction({ + recipient: 'ydvgJ2eVSmdKt78ZSVBJ7zarVVtdHGj3yR', + satoshis: Math.floor(account.getTotalBalance() / 2) + }); + + expect(newTx.constructor.name).to.equal('Transaction'); + expect(newTx.outputs.length).to.not.equal(0); + expect(newTx.inputs.length).to.not.equal(0); + }); + + it('should be able to restore wallet to the same state with a mnemonic', async () => { + const restoredWallet = new Wallet({ + mnemonic: wallet.mnemonic, + transport: { + seeds, + }, + network: wallet.network, + }); + const restoredAccount = await restoredWallet.getAccount(); + + let transactions = restoredAccount.getTransactions(); + + if (Object.keys(transactions).length === 0) { + // Due to the limitations of DAPI, we need to wait for a block to be mined if we connected in the + // moment when transaction already entered the mempool, but haven't been mined yet + await new Promise(resolve => restoredAccount.once(EVENTS.BLOCKHEADER, resolve)); + transactions = restoredAccount.getTransactions(); + } + + const expectedAddresses = account.getAddresses(); + const expectedTransactions = account.getTransactions(); + + const addresses = restoredAccount.getAddresses(); + + expect(Object.keys(transactions).length).to.be.equal(1); + expect(addresses).to.be.deep.equal(expectedAddresses); + expect(Object.keys(transactions)).to.be.deep.equal(Object.keys(expectedTransactions)); + }); + + it('should broadcast a chain of transactions from a single UTXO', async () => { + const txAmount = 5; + const satoshis = 1000; + let balance = account.getTotalBalance(); + + for (let i = 0; i < txAmount; i++) { + const tx = account + .createTransaction({ satoshis, recipient: "ydvgJ2eVSmdKt78ZSVBJ7zarVVtdHGj3yR" }); + + await account.broadcastTransaction(tx); + const newBalance = account.getTotalBalance(); + + const fee = tx.getFee(); + expect(newBalance).to.equal(balance - satoshis - fee); + balance = newBalance + } + }) + }); +}); diff --git a/packages/wallet-lib/tests/integration/plugins/Workers/TransactionSyncStreamWorker.spec.js b/packages/wallet-lib/tests/integration/plugins/Workers/TransactionSyncStreamWorker.spec.js new file mode 100644 index 00000000000..7774f30600d --- /dev/null +++ b/packages/wallet-lib/tests/integration/plugins/Workers/TransactionSyncStreamWorker.spec.js @@ -0,0 +1,765 @@ +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); + +const { + HDPrivateKey, + Transaction, + BlockHeader, + MerkleBlock, + InstantLock +} = require('@dashevo/dashcore-lib'); + +const TransactionSyncStreamWorker = require('../../../../src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker'); + +const TxStreamDataResponseMock = require('../../../../src/test/mocks/TxStreamDataResponseMock'); +const TxStreamMock = require('../../../../src/test/mocks/TxStreamMock'); + +const createAndAttachTransportMocksToWallet = require('../../../../src/test/mocks/createAndAttachTransportMocksToWallet') + +const { Wallet } = require('../../../../src'); + +const blockHeaderFixture = '00000020e2bddfb998d7be4cc4c6b126f04d6e4bd201687523ded527987431707e0200005520320b4e263bec33e08944656f7ce17efbc2c60caab7c8ed8a73d413d02d3a169d555ecdd6021e56d000000203000500010000000000000000000000000000000000000000000000000000000000000000ffffffff050219250102ffffffff0240c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac40c3609a010000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac0000000046020019250000476416132511031b71167f4bb7658eab5c3957d79636767f83e0e18e2b9ed7f8000000000000000000000000000000000000000000000000000000000000000003000600000000000000fd4901010019250000010001d02e9ee1b14c022ad6895450f3375a8e9a87f214912d4332fa997996d2000000320000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; +chai.use(chaiAsPromised); +const { expect } = chai; + +function wait(ms) { + return new Promise((res) => setTimeout(res, ms)); +} + +describe('TransactionSyncStreamWorker', function suite() { + this.timeout(60000); + let worker; + let storage; + let walletId; + let wallet; + let account; + let txStreamMock; + let address; + let addressAtIndex19; + let testHDKey; + let merkleBlockMock; + let transportMock; + + beforeEach(async function beforeEach() { + testHDKey = "xprv9s21ZrQH143K4PgfRZPuYjYUWRZkGfEPuWTEUESMoEZLC274ntC4G49qxgZJEPgmujsmY52eVggtwZgJPrWTMXmbYgqDVySWg46XzbGXrSZ"; + merkleBlockMock = new MerkleBlock(Buffer.from([0,0,0,32,61,11,102,108,38,155,164,49,91,246,141,178,126,155,13,118,248,83,250,15,206,21,102,65,104,183,243,167,235,167,60,113,140,110,120,87,208,191,240,19,212,100,228,121,192,125,143,44,226,9,95,98,51,25,139,172,175,27,205,201,158,85,37,8,72,52,36,95,255,255,127,32,2,0,0,0,1,0,0,0,1,140,110,120,87,208,191,240,19,212,100,228,121,192,125,143,44,226,9,95,98,51,25,139,172,175,27,205,201,158,85,37,8,1,1])); + + testHDKey = new HDPrivateKey(testHDKey).toString(); + + // Override default value of executeOnStart to prevent worker from starting + worker = new TransactionSyncStreamWorker({ executeOnStart: false }); + + // This is a full instance of wallet with a mocked transport + wallet = new Wallet({ + offlineMode: true, + plugins: [worker], + allowSensitiveOperations: true, + HDPrivateKey: new HDPrivateKey(testHDKey), + network: 'mainnet' + }); + + ({ txStreamMock, transportMock } = await createAndAttachTransportMocksToWallet(wallet, this.sinonSandbox)); + + transportMock.getBlockHeaderByHash + .returns(BlockHeader.fromString(blockHeaderFixture)); + + account = await wallet.getAccount(); + + storage = account.storage; + walletId = account.walletId; + + address = account.getAddress(0).address; + addressAtIndex19 = account.getAddress(19).address; + }); + afterEach(() => { + worker.stopWorker(); + }) + + describe("#onStart", () => { + it('should sync historical data from the last saved block', async function () { + const lastSavedBlockHeight = 40; + const bestBlockHeight = 42; + + worker.setLastSyncedBlockHeight(lastSavedBlockHeight); + const transactionsSent = []; + + account.transport.getBestBlockHeight + .returns(bestBlockHeight); + + setTimeout(async () => { + try { + expect(worker.stream).is.not.null; + + for (let i = lastSavedBlockHeight; i <= bestBlockHeight; i++) { + const transaction = new Transaction().to(address, i); + account.transport.getTransaction + .returns({ + transaction:transaction, + blockHash: Buffer.from('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176', 'hex'), + height: 42, + confirmations: 10, + isInstantLocked: true, + isChainLocked: false, + }); + transactionsSent.push(transaction); + txStreamMock.emit(TxStreamMock.EVENTS.data, new TxStreamDataResponseMock({ + rawTransactions: [transaction.toBuffer()] + })); + await wait(10); + } + + txStreamMock.emit(TxStreamMock.EVENTS.end); + } catch (e) { + console.error(e); + txStreamMock.emit(TxStreamMock.EVENTS.error, e); + } + }, 10); + + await worker.onStart(); + + const transactionsInStorage = Array.from(storage.getChainStore('livenet').state.transactions) + .map(([,t]) => t.transaction.toJSON()); + + const expectedTransactions = transactionsSent + .map((t) => t.toJSON()); + + expect(worker.stream).to.be.null; + expect(transactionsInStorage.length).to.be.equal(3); + expect(transactionsInStorage).to.have.deep.members(expectedTransactions); + }); + it('should reconnect to the historical stream when gap limit is filled', async function () { + const lastSavedBlockHeight = 40; + const bestBlockHeight = 42; + + worker.setLastSyncedBlockHeight(lastSavedBlockHeight); + const transactionsSent = []; + + account.transport.getBestBlockHeight + .returns(bestBlockHeight); + + setTimeout(async () => { + try { + expect(worker.stream).is.not.null; + + let transaction = new Transaction().to(addressAtIndex19, 10000); + account.transport.getTransaction + .returns({ + transaction:transaction, + blockHash: Buffer.from('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176', 'hex'), + height: 42, + confirmations: 10, + isInstantLocked: true, + isChainLocked: false, + }); + transactionsSent.push(transaction); + txStreamMock.emit(TxStreamMock.EVENTS.data, new TxStreamDataResponseMock({ + rawTransactions: [transaction.toBuffer()] + })); + + await wait(10); + + merkleBlockMock.hashes[0] = Buffer.from(transaction.hash, 'hex').reverse().toString('hex'); + txStreamMock.emit(TxStreamMock.EVENTS.data, new TxStreamDataResponseMock({ + rawMerkleBlock: merkleBlockMock.toBuffer() + })); + + await wait(10); + + transaction = new Transaction().to(account.getAddress(10).address, 10000); + account.transport.getTransaction + .returns({ + transaction:transaction, + blockHash: Buffer.from('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176', 'hex'), + height: 42, + confirmations: 10, + isInstantLocked: true, + isChainLocked: false, + }); + transactionsSent.push(transaction); + txStreamMock.emit(TxStreamMock.EVENTS.data, new TxStreamDataResponseMock({ + rawTransactions: [transaction.toBuffer()] + })); + + await wait(10); + + txStreamMock.emit(TxStreamMock.EVENTS.end); + } catch (e) { + console.error(e); + txStreamMock.emit(TxStreamMock.EVENTS.error, e); + } + }, 10); + + await worker.onStart(); + + const transactionsInStorage = Array.from(storage.getChainStore('livenet').state.transactions) + .map(([,t]) => t.transaction.toJSON()); + + const expectedTransactions = transactionsSent + .map((t) => t.toJSON()); + + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); + // We send transaction to index 19, so wallet should generate additional 20 addresses to keep the gap between + // the last used address + expect(Object.keys(addressesInStorage).length).to.be.equal(40); + // It should reconnect after the gap limit is reached + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(2); + // 20 external and 20 internal + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 2}); + // 20 more of external, since the last address is used. + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHeight: 42, count: 1}); + + expect(worker.stream).to.be.null; + expect(transactionsInStorage.length).to.be.equal(2); + expect(transactionsInStorage).to.have.deep.members(expectedTransactions); + }); + + it('should reconnect to the historical stream if stream is closed due to operational GRPC error', async function () { + const lastSavedBlockHeight = 40; + const bestBlockHeight = 42; + + worker.setLastSyncedBlockHeight(lastSavedBlockHeight); + const transactionsSent = []; + + account.transport.getBestBlockHeight + .returns(bestBlockHeight); + + setTimeout(async () => { + expect(worker.stream).is.not.null; + + const err = new Error('Some error'); + err.code = 4; + txStreamMock.emit(TxStreamMock.EVENTS.error, err); + + await wait(10); + + txStreamMock.emit(TxStreamMock.EVENTS.end); + }, 10); + + await worker.onStart(); + + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); + + expect(Object.keys(addressesInStorage).length).to.be.equal(20); + // It should reconnect after because of the operational error + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(2); + // 20 external and 20 internal + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 2}); + + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(40); + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 2}); + + expect(worker.stream).to.be.null; + }); + + it('should not reconnect to the historical stream if stream in case of any other error', async function () { + const lastSavedBlockHeight = 40; + const bestBlockHeight = 42; + + worker.setLastSyncedBlockHeight(lastSavedBlockHeight); + const transactionsSent = []; + + account.transport.getBestBlockHeight + .returns(bestBlockHeight); + + setTimeout(async () => { + expect(worker.stream).is.not.null; + + txStreamMock.emit(TxStreamMock.EVENTS.error, new Error('Some random error')); + }, 10); + + await expect(worker.onStart()).to.be.rejectedWith('Some random error'); + + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); + + expect(Object.keys(addressesInStorage).length).to.be.equal(20); + // Shouldn't try to reconnect + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(1); + // 20 external and 20 internal + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 2}); + + expect(worker.stream).to.be.null; + }); + }); + describe("#execute", () => { + it('should sync incoming transactions and save it to the storage', async function () { + const lastSavedBlockHeight = 40; + const bestBlockHeight = 42; + + worker.setLastSyncedBlockHeight(lastSavedBlockHeight); + const transactionsSent = []; + + account.transport.getBestBlockHeight + .returns(bestBlockHeight); + + worker.execute(); + + await wait(10); + + try { + for (let i = lastSavedBlockHeight; i <= bestBlockHeight; i++) { + const transaction = new Transaction().to(address, i); + account.transport.getTransaction + .returns({ + transaction:transaction, + blockHash: Buffer.from('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176', 'hex'), + height: 42, + confirmations: 10, + isInstantLocked: true, + isChainLocked: false, + }); + transactionsSent.push(transaction); + txStreamMock.emit(TxStreamMock.EVENTS.data, new TxStreamDataResponseMock({ + rawTransactions: [transaction.toBuffer()] + })); + await wait(10); + } + + txStreamMock.emit(TxStreamMock.EVENTS.end); + } catch (e) { + console.error(e); + txStreamMock.emit(TxStreamMock.EVENTS.error, e); + } + + await worker.onStop(); + + const transactionsInStorage = Array.from(storage.getChainStore('livenet').state.transactions) + .map(([,t]) => t.transaction.toJSON()); + + const expectedTransactions = transactionsSent + .map((t) => t.toJSON()); + + expect(worker.stream).to.be.null; + expect(transactionsInStorage.length).to.be.equal(3); + expect(transactionsInStorage).to.have.deep.members(expectedTransactions); + }) + it('should reconnect to the incoming stream when gap limit is filled', async function () { + const lastSavedBlockHeight = 40; + const bestBlockHeight = 42; + + worker.setLastSyncedBlockHeight(lastSavedBlockHeight); + const transactionsSent = []; + + account.transport.getBestBlockHeight + .returns(bestBlockHeight); + + worker.execute(); + + await wait(10); + + try { + let transaction = new Transaction().to(addressAtIndex19, 10000); + account.transport.getTransaction + .returns({ + transaction:transaction, + blockHash: Buffer.from('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176', 'hex'), + height: 42, + confirmations: 10, + isInstantLocked: true, + isChainLocked: false, + }); + transactionsSent.push(transaction); + txStreamMock.emit(TxStreamMock.EVENTS.data, new TxStreamDataResponseMock({ + rawTransactions: [transaction.toBuffer()] + })); + + await wait(10); + + merkleBlockMock.hashes[0] = Buffer.from(transaction.hash, 'hex').reverse().toString('hex'); + txStreamMock.emit(TxStreamMock.EVENTS.data, new TxStreamDataResponseMock({ + rawMerkleBlock: merkleBlockMock.toBuffer() + })); + + await wait(10); + + transaction = transaction = new Transaction().to(account.getAddress(10).address, 10000); + account.transport.getTransaction + .returns({ + transaction:transaction, + blockHash: Buffer.from('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176', 'hex'), + height: 42, + confirmations: 10, + isInstantLocked: true, + isChainLocked: false, + }); + transactionsSent.push(transaction); + txStreamMock.emit(TxStreamMock.EVENTS.data, new TxStreamDataResponseMock({ + rawTransactions: [transaction.toBuffer()] + })); + + await wait(10); + + txStreamMock.emit(TxStreamMock.EVENTS.end); + } catch (e) { + console.error(e); + txStreamMock.emit(TxStreamMock.EVENTS.error, e); + } + + await worker.onStop(); + + const transactionsInStorage = Array.from(storage.getChainStore('livenet').state.transactions) + .map(([,t]) => t.transaction.toJSON()); + + const expectedTransactions = transactionsSent + .map((t) => t.toJSON()); + + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); + // We send transaction to index 19, so wallet should generate additional 20 addresses to keep the gap between + // the last used address + expect(Object.keys(addressesInStorage).length).to.be.equal(40); + // It should reconnect after the gap limit is reached + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(2); + // 20 external and 20 internal + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 0}); + // 20 more of external, since the last address is used. + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHeight: 42, count: 0}); + + expect(worker.stream).to.be.null; + expect(transactionsInStorage.length).to.be.equal(2); + expect(transactionsInStorage).to.have.deep.members(expectedTransactions); + }); + + it('should reconnect to the incoming stream if stream is closed due to operational GRPC error', async function () { + const lastSavedBlockHeight = 40; + const bestBlockHeight = 42; + + worker.setLastSyncedBlockHeight(lastSavedBlockHeight); + + account.transport.getBestBlockHeight + .returns(bestBlockHeight); + + worker.execute(); + + await wait(10); + + const err = new Error('Some error'); + err.code = 4; + txStreamMock.emit(TxStreamMock.EVENTS.error, err); + + await wait(10); + + txStreamMock.emit(TxStreamMock.EVENTS.end); + + await worker.onStop(); + + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); + + expect(Object.keys(addressesInStorage).length).to.be.equal(20); + // It should reconnect after the gap limit is reached + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(2); + // 20 external and 20 internal + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 0}); + + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(40); + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 0}); + + expect(worker.stream).to.be.null; + }); + it('should reconnect to the server closes the stream without any errors', async function () { + const lastSavedBlockHeight = 40; + const bestBlockHeight = 42; + + worker.setLastSyncedBlockHeight(lastSavedBlockHeight); + + account.transport.getBestBlockHeight + .returns(bestBlockHeight); + + worker.execute(); + + await wait(10); + + txStreamMock.emit(TxStreamMock.EVENTS.end); + + await wait(10); + + await worker.onStop(); + + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); + + expect(Object.keys(addressesInStorage).length).to.be.equal(20); + // It should reconnect if the server closes the stream + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(2); + // 20 external and 20 internal + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 0}); + + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(40); + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 0}); + + expect(worker.stream).to.be.null; + }); + + it('should not reconnect to the incoming stream if stream in case of any other error', async function () { + const lastSavedBlockHeight = 40; + const bestBlockHeight = 42; + + worker.setLastSyncedBlockHeight(lastSavedBlockHeight); + + account.transport.getBestBlockHeight + .returns(bestBlockHeight); + + worker.execute(); + + await wait(10); + + txStreamMock.emit(TxStreamMock.EVENTS.error, new Error('Some random error')); + + await worker.onStop(); + + await expect(worker.incomingSyncPromise).to.be.rejectedWith('Some random error'); + + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); + expect(Object.keys(addressesInStorage).length).to.be.equal(20); + + // Shouldn't try to reconnect + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(1); + // 20 external and 20 internal + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 0}); + + expect(worker.stream).to.be.null; + }); + }); + + it('should propagate instant locks', async () => { + const transactions = [ + new Transaction().to(addressAtIndex19, 10000), + new Transaction().to(account.getAddress(10).address, 10000), + new Transaction().to(account.getAddress(11).address, 10000) + ]; + + const receivedInstantLocks = []; + + transactions.forEach(tx => { + account.subscribeToTransactionInstantLock(tx.hash, (isLock) => { + receivedInstantLocks.push(isLock); + }); + }); + + const instantLock1 = InstantLock.fromObject({ + version: 1, + inputs: [ + { + outpointHash: '6e200d059fb567ba19e92f5c2dcd3dde522fd4e0a50af223752db16158dabb1d', + outpointIndex: 0, + }, + ], + txid: transactions[0].hash, + cyclehash: '0dc8d0df62b076a7757ab5ca07dde0f1e2bfaf83f94299fd9a77577e6cc7022e', + signature: '8967c46529a967b3822e1ba8a173066296d02593f0f59b3a78a30a7eef9c8a120847729e62e4a32954339286b79fe7590221331cd28d576887a263f45b595d499272f656c3f5176987c976239cac16f972d796ad82931d532102a4f95eec7d80', + }); + const instantLock2 = InstantLock.fromObject({ + version: 1, + inputs: [ + { + outpointHash: '6e200d059fb567ba19e92f5c2dcd3dde522fd4e0a50af223752db16158dabb1d', + outpointIndex: 0, + }, + ], + txid: transactions[1].hash, + cyclehash: '7c30826123d0f29fe4c4a8895d7ba4eb469b1fafa6ad7b23896a1a591766a536', + signature: '8967c46529a967b3822e1ba8a173066296d02593f0f59b3a78a30a7eef9c8a120847729e62e4a32954339286b79fe7590221331cd28d576887a263f45b595d499272f656c3f5176987c976239cac16f972d796ad82931d532102a4f95eec7d80', + }); + const lastSavedBlockHeight = 40; + const bestBlockHeight = 42; + + worker.setLastSyncedBlockHeight(lastSavedBlockHeight); + const transactionsSent = []; + + account.transport.getBestBlockHeight + .returns(bestBlockHeight); + + worker.execute(); + + await wait(10); + + try { + let transaction = transactions[0]; + account.transport.getTransaction + .returns({ + transaction:transactions[0], + blockHash: Buffer.from('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176', 'hex'), + height: 42, + confirmations: 10, + isInstantLocked: true, + isChainLocked: false, + }); + + transactionsSent.push(transaction); + txStreamMock.emit(TxStreamMock.EVENTS.data, new TxStreamDataResponseMock({ + rawTransactions: [transaction.toBuffer()] + })); + + txStreamMock.emit( + TxStreamMock.EVENTS.data, + new TxStreamDataResponseMock( + { instantSendLockMessages: [instantLock1.toBuffer()] } + ) + ); + + await wait(10); + + merkleBlockMock.hashes[0] = Buffer.from(transaction.hash, 'hex').reverse().toString('hex'); + txStreamMock.emit(TxStreamMock.EVENTS.data, new TxStreamDataResponseMock({ + rawMerkleBlock: merkleBlockMock.toBuffer() + })); + + await wait(10); + + transaction = transactions[1]; + account.transport.getTransaction + .returns({ + transaction:transactions[1], + blockHash: Buffer.from('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176', 'hex'), + height: 42, + confirmations: 10, + isInstantLocked: true, + isChainLocked: false, + }); + + account.transport.getBlockHeaderByHash + .returns(new Buffer.from(blockHeaderFixture, 'hex')); + + + transactionsSent.push(transaction); + txStreamMock.emit(TxStreamMock.EVENTS.data, new TxStreamDataResponseMock({ + rawTransactions: [transaction.toBuffer()] + })); + + await wait(10); + + txStreamMock.emit(TxStreamMock.EVENTS.end); + } catch (e) { + console.error(e); + txStreamMock.emit(TxStreamMock.EVENTS.error, e); + } + + await worker.onStop(); + + const transactionsInStorage = Array.from(storage.getChainStore('livenet').state.transactions) + .map(([,t]) => t.transaction.toJSON()); + + const expectedTransactions = transactionsSent + .map((t) => t.toJSON()); + + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const externalAddressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); + + const internalAddressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/1')) + .map(([path, address])=> address); + + // We send transaction to index 19, so wallet should generate additional 20 addresses to keep the gap between + // the last used address + expect(Object.keys(externalAddressesInStorage).length).to.be.equal(40); + expect(Object.keys(internalAddressesInStorage).length).to.be.equal(20); + // It should reconnect after the gap limit is reached + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(2); + // 20 external and 20 internal + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 0}); + expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); + // 20 more of external, since the last address is used, Merkle Block received + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHeight: 42, count: 0}); + expect(worker.stream).to.be.null; + expect(transactionsInStorage.length).to.be.equal(2); + expect(transactionsInStorage).to.have.deep.members(expectedTransactions); + + const { promise } = account.waitForInstantLock(transactions[1].hash, 10000); + + const [ actualLock ] = await Promise.all([ + promise, + new Promise((resolve => { + setImmediate(() => { + txStreamMock.emit( + TxStreamMock.EVENTS.data, + new TxStreamDataResponseMock( + { instantSendLockMessages: [instantLock2.toBuffer()] } + ) + ); + resolve(); + }) + })), + ]); + + expect(actualLock).to.be.deep.equal(instantLock2); + expect(receivedInstantLocks.length).to.be.equal(2); + expect(receivedInstantLocks[0]).to.be.deep.equal(instantLock1); + expect(receivedInstantLocks[1]).to.be.deep.equal(instantLock2); + + // Test that if instant lock was already imported previously wait method will return it + const { promise: firstISFromWaitPromise } = account.waitForInstantLock(transactions[0].hash); + const firstISFromWait = await firstISFromWaitPromise; + expect(firstISFromWait).to.be.deep.equal(instantLock1); + + // Check that wait method throws if timeout has passed + + const { promise: transaction2Promise } = account.waitForInstantLock(transactions[2].hash, 1000); + + await expect(transaction2Promise).to.eventually + .be.rejectedWith('InstantLock waiting period for transaction 823c272fc1694b571805d2bc2f8936597ee52de638a0ca5323233c239fd3e8c4 timed out'); + }); + it('should start from the height specified in `skipSynchronizationBeforeHeight` options', async function () { + const bestBlockHeight = 42; + + wallet = new Wallet({ + HDPrivateKey: new HDPrivateKey(testHDKey), + unsafeOptions: { + skipSynchronizationBeforeHeight: 20, + }, + }); + + await createAndAttachTransportMocksToWallet(wallet, this.sinonSandbox); + + account = await wallet.getAccount(); + + account.transport.getBestBlockHeight.resolves(bestBlockHeight); + account.transport.getTransaction.returns({ + transaction:new Transaction().to(account.getAddress(10).address, 10000), + blockHash: Buffer.from('4f46066bd50cc2684484407696b7949e82bd906ea92c040f59a97cba47ed8176', 'hex'), + height: 42, + confirmations: 10, + isInstantLocked: true, + isChainLocked: false, + }) + + expect(account.transport.subscribeToTransactionsWithProofs.getCall(0).args[1]).to.be.deep.equal({ fromBlockHeight: 20, count: bestBlockHeight - 20 }); + }); +}); diff --git a/packages/wallet-lib/tests/integration/types/Account.spec.js b/packages/wallet-lib/tests/integration/types/Account.spec.js new file mode 100644 index 00000000000..6aface918c3 --- /dev/null +++ b/packages/wallet-lib/tests/integration/types/Account.spec.js @@ -0,0 +1,93 @@ +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); + +const { + HDPrivateKey, + MerkleBlock, +} = require('@dashevo/dashcore-lib'); + +const TransactionSyncStreamWorker = require('../../../src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker'); + +const LocalForageAdapterMock = require('../../../src/test/mocks/LocalForageAdapterMock'); +const createTransactionInAccount = require('../../../src/test/mocks/createTransactionInAccount'); + +const createAndAttachTransportMocksToWallet = require('../../../src/test/mocks/createAndAttachTransportMocksToWallet') + +const { Wallet } = require('../../../src'); + +chai.use(chaiAsPromised); +const { expect } = chai; + +describe('Account', function suite() { + this.timeout(60000); + let worker; + let storage; + let walletId; + let wallet; + let account; + let txStreamMock; + let address; + let addressAtIndex19; + let testHDKey; + let merkleBlockMock; + let transportMock; + let storageAdapterMock; + + beforeEach(async function beforeEach() { + testHDKey = "xprv9s21ZrQH143K4PgfRZPuYjYUWRZkGfEPuWTEUESMoEZLC274ntC4G49qxgZJEPgmujsmY52eVggtwZgJPrWTMXmbYgqDVySWg46XzbGXrSZ"; + merkleBlockMock = new MerkleBlock(Buffer.from([0, 0, 0, 32, 61, 11, 102, 108, 38, 155, 164, 49, 91, 246, 141, 178, 126, 155, 13, 118, 248, 83, 250, 15, 206, 21, 102, 65, 104, 183, 243, 167, 235, 167, 60, 113, 140, 110, 120, 87, 208, 191, 240, 19, 212, 100, 228, 121, 192, 125, 143, 44, 226, 9, 95, 98, 51, 25, 139, 172, 175, 27, 205, 201, 158, 85, 37, 8, 72, 52, 36, 95, 255, 255, 127, 32, 2, 0, 0, 0, 1, 0, 0, 0, 1, 140, 110, 120, 87, 208, 191, 240, 19, 212, 100, 228, 121, 192, 125, 143, 44, 226, 9, 95, 98, 51, 25, 139, 172, 175, 27, 205, 201, 158, 85, 37, 8, 1, 1])); + + testHDKey = new HDPrivateKey(testHDKey).toString(); + + // Override default value of executeOnStart to prevent worker from starting + worker = new TransactionSyncStreamWorker({executeOnStart: false}); + + storageAdapterMock = new LocalForageAdapterMock(); + + // This is a full instance of wallet with a mocked transport + wallet = new Wallet({ + offlineMode: true, + plugins: [worker], + allowSensitiveOperations: true, + HDPrivateKey: new HDPrivateKey(testHDKey), + adapter: storageAdapterMock, + network: 'livenet' + }); + + ({txStreamMock, transportMock} = await createAndAttachTransportMocksToWallet(wallet, this.sinonSandbox)); + + account = await wallet.getAccount(); + + storage = account.storage; + walletId = account.walletId; + + address = account.getAddress(0).address; + addressAtIndex19 = account.getAddress(19).address; + }); + + afterEach(() => { + worker.stopWorker(); + }) + + describe('getUTXO', () => { + it('should work if storage adapter behaves like a local forage', async () => { + await createTransactionInAccount(account); + + // Saving state to restore it later + await account.storage.saveState(); + // Restoring wallet from the saved state + const restoredWallet = new Wallet({ + offlineMode: true, + plugins: [worker], + allowSensitiveOperations: true, + HDPrivateKey: new HDPrivateKey(testHDKey), + adapter: storageAdapterMock, + network: 'livenet' + }); + const restoredAccount = await restoredWallet.getAccount(); + const utxos = await restoredAccount.getUTXOS(); + + expect(utxos.length).to.be.equal(1); + }); + }); +}); diff --git a/packages/wallet-lib/tests/integration/types/Wallet.spec.js b/packages/wallet-lib/tests/integration/types/Wallet.spec.js new file mode 100644 index 00000000000..58ae11d6bae --- /dev/null +++ b/packages/wallet-lib/tests/integration/types/Wallet.spec.js @@ -0,0 +1,339 @@ +const { + HDPrivateKey, + Transaction, + BlockHeader, + PrivateKey +} = require('@dashevo/dashcore-lib'); + +const { expect } = require('chai'); + +const { Wallet, EVENTS } = require("../../../src"); +const TransactionSyncStreamWorker = require("../../../src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker"); +const ChainPlugin = require("../../../src/plugins/Plugins/ChainPlugin"); +const LocalForageAdapterMock = require("../../../src/test/mocks/LocalForageAdapterMock"); +const createAndAttachTransportMocksToWallet = require("../../../src/test/mocks/createAndAttachTransportMocksToWallet"); +const {waitOneTick} = require("../../../src/test/utils"); + +describe('Wallet', () => { + describe('Storage', () => { + let wallet; + let txStreamMock; + let txStreamWorker; + let chainPlugin; + let transportMock; + let bestBlockHeight = 42; + let storageAdapterMock = new LocalForageAdapterMock(); + + beforeEach(async function() { + const testHDKey = "xprv9s21ZrQH143K4PgfRZPuYjYUWRZkGfEPuWTEUESMoEZLC274ntC4G49qxgZJEPgmujsmY52eVggtwZgJPrWTMXmbYgqDVySWg46XzbGXrSZ"; + txStreamWorker = new TransactionSyncStreamWorker({ executeOnStart: false }); + chainPlugin = new ChainPlugin({ executeOnStart: false }); + + wallet = new Wallet({ + offlineMode: true, + plugins: [chainPlugin, txStreamWorker], + allowSensitiveOperations: true, + HDPrivateKey: new HDPrivateKey(testHDKey), + adapter: storageAdapterMock, + network: 'livenet' + }); + + ({ txStreamMock, transportMock } = await createAndAttachTransportMocksToWallet(wallet, this.sinonSandbox)); + + transportMock.getStatus.returns({ + chain: { blocksCount: bestBlockHeight }, + network: { fee: 237 } + }) + + transportMock.sendTransaction.callsFake((tx) => { + txStreamMock.sendTransactions([new Transaction(tx)]) + }) + + await chainPlugin.onStart() + }) + + /** + * In this scenario we have a fresh wallet that receives a funding transaction + * and sends a transaction on his own. + * Points to check: + * - subscr + */ + it('should fill the storage for a fresh wallet', async function() { + const account = await wallet.getAccount(); + const { address: addressToFund } = account.getUnusedAddress(); + + /** Define a scenario */ + const scenario = { + transactions: { + fundingTx: new Transaction().to(addressToFund, 10000), + }, + blockHeaders: [ + new BlockHeader({ + version: 1, + prevHash: '0000000000000000000000000000000000000000000000000000000000000000', + merkleRoot: '0000000000000000000000000000000000000000000000000000000000000000', + time: Date.now() / 1000, + bits: 0, + nonce: 0, + }), + new BlockHeader({ + version: 1, + prevHash: '0000000000000000000000000000000000000000000000000000000000000001', + merkleRoot: '0000000000000000000000000000000000000000000000000000000000000000', + time: Date.now() / 1000, + bits: 1, + nonce: 1, + }) + ], + metadata: {} + } + + transportMock.getBestBlockHeight.returns(bestBlockHeight); + transportMock.getTransaction.callsFake(async (hash) => scenario.metadata[hash]) + transportMock.getBlockHeaderByHash.callsFake(async hash => scenario.blockHeaders.find(header => header.hash === hash)) + + Object.assign(scenario.metadata, { + [scenario.transactions.fundingTx.hash]: { + transaction: scenario.transactions.fundingTx, + height: 10, + blockHash: scenario.blockHeaders[0].hash + } + }) + + /** Start transactions sync plugin */ + txStreamWorker.onStart(); + await waitOneTick(); + + /** Ensure proper transport arguments */ + expect(transportMock.subscribeToTransactionsWithProofs.firstCall.args[1]) + .to.deep.equal({ fromBlockHeight: 1, count: 41 }); + + /** Send first funding transaction to the wallet */ + const { fundingTx } = scenario.transactions; + txStreamMock.sendTransactions([fundingTx]); + await wallet.storage.saveState(); + + /** Ensure that storage has no items for transactions without the metadata */ + let storage = storageAdapterMock.getItem(`wallet_${wallet.walletId}`) + let chainStoreState = storage.chains[wallet.network].chain; + let walletStoreState = storage.chains[wallet.network].wallet; + expect(chainStoreState.transactions).to.be.empty; + expect(chainStoreState.txMetadata).to.be.empty; + expect(chainStoreState.blockHeaders).to.be.empty; + expect(walletStoreState.lastKnownBlock.height).to.equal(-1) + + /** Wait for transactions metadata */ + await waitOneTick(); + + /** + * Simulate block height change to ensure that this value is not + * affecting WalletStore.state.lastKnownBlock, because we still in the phase of historical sync + */ + transportMock.emit(EVENTS.BLOCKHEIGHT_CHANGED, { payload: (bestBlockHeight = 43) }) + await waitOneTick(); + await wallet.storage.saveState(); + + /** + * Ensure that chain items for fundingTx have been propagated + * alongside with the lastKnownBlock + */ + storage = storageAdapterMock.getItem(`wallet_${wallet.walletId}`) + chainStoreState = storage.chains[wallet.network].chain; + walletStoreState = storage.chains[wallet.network].wallet; + expect(chainStoreState.transactions[fundingTx.hash]).to.exist; + expect(chainStoreState.txMetadata[fundingTx.hash]).to.exist + expect(chainStoreState.blockHeaders[scenario.blockHeaders[0].hash]).to.exist; + expect(walletStoreState.lastKnownBlock.height).to.equal(10) + + /** End historical sync */ + txStreamMock.finish(); + await waitOneTick(); + + /** + * Ensure that reorg safe height (chain height - 6) is set as last known block + * after historical sync is finished + */ + await wallet.storage.saveState(); + storage = storageAdapterMock.getItem(`wallet_${wallet.walletId}`) + walletStoreState = storage.chains[wallet.network].wallet; + expect(walletStoreState.lastKnownBlock.height).to.equal(37) + + /** Start continuous sync */ + txStreamWorker.execute() + await waitOneTick(); + + /** Ensure proper transport arguments */ + expect(transportMock.subscribeToTransactionsWithProofs.lastCall.args[1]) + .to.deep.equal({ fromBlockHeight: 42, count: 0 }); + + /** Broadcast transaction from the wallet */ + const sendTx = account.createTransaction({ + recipient: new PrivateKey().toAddress(), + satoshis: 1000 + }); + await account.broadcastTransaction(sendTx) + + Object.assign(scenario.metadata, { + [sendTx.hash]: { + transaction: sendTx, + height: 44, + blockHash: scenario.blockHeaders[1].hash + } + }) + + transportMock.emit(EVENTS.BLOCKHEIGHT_CHANGED, { payload: (bestBlockHeight = 44) }) + await waitOneTick(); + + /** + * Ensure that reorg safe height (chain height - 6) is set as last known block height + * and sent transaction hasn't been saved because it's still not reorg-safe + * */ + await wallet.storage.saveState(); + storage = storageAdapterMock.getItem(`wallet_${wallet.walletId}`) + walletStoreState = storage.chains[wallet.network].wallet; + expect(Object.keys(chainStoreState.transactions)).to.have.lengthOf(1) + expect(Object.keys(chainStoreState.txMetadata)).to.have.lengthOf(1) + expect(Object.keys(chainStoreState.blockHeaders)).to.have.lengthOf(1) + expect(walletStoreState.lastKnownBlock.height).to.equal(38) + + /** + * Emit one more BLOCKHEIGHT_CHANGE event to ensure that previously considered + * reorg unsafe items were saved + */ + transportMock.emit(EVENTS.BLOCKHEIGHT_CHANGED, { payload: (bestBlockHeight = 50) }) + await waitOneTick(); + + await wallet.storage.saveState(); + storage = storageAdapterMock.getItem(`wallet_${wallet.walletId}`) + chainStoreState = storage.chains[wallet.network].chain; + walletStoreState = storage.chains[wallet.network].wallet; + + /** + * Ensure that storage have been updated with the latest + * transactions and relevant chain data which now considered reorg safe + */ + expect(Object.keys(chainStoreState.transactions)).to.have.lengthOf(2) + expect(Object.keys(chainStoreState.txMetadata)).to.have.lengthOf(2) + expect(Object.keys(chainStoreState.blockHeaders)).to.have.lengthOf(2) + expect(walletStoreState.lastKnownBlock.height).to.equal(44) + + /** Update chain height */ + bestBlockHeight = 52; + }) + + /** + * In this scenario we have a wallet that picks part of the data from the storage + * and then sends a new transaction to the network + */ + it('should ensure synchronization from last known block for wallet with storage', async () => { + const scenario = { + blockHeaders: [ + new BlockHeader({ + version: 1, + prevHash: '0000000000000000000000000000000000000000000000000000000000000002', + merkleRoot: '0000000000000000000000000000000000000000000000000000000000000000', + time: Date.now() / 1000, + bits: 0, + nonce: 0, + }), + ], + metadata: {} + } + + transportMock.getTransaction.callsFake(async (hash) => scenario.metadata[hash]) + transportMock.getBestBlockHeight.returns(bestBlockHeight); + transportMock.getBlockHeaderByHash + .callsFake(async hash => scenario.blockHeaders.find(header => header.hash === hash)) + + /** Initialize account */ + const account = await wallet.getAccount(); + + const walletStore = account.storage.getWalletStore(wallet.walletId); + const chainStore = account.storage.getChainStore(wallet.network); + + /** Ensure that storage contains transaction and relevant chain data */ + expect(chainStore.state.transactions.size).to.equal(2); + expect(chainStore.state.blockHeaders.size).to.equal(2) + expect(walletStore.state.lastKnownBlock.height).to.equal(44) + + /** Start transactions sync plugin */ + txStreamWorker.onStart(); + await waitOneTick(); + + /** Ensure that historical synchronization starts from last known block */ + expect(transportMock.subscribeToTransactionsWithProofs.lastCall.args[1]) + .to.deep.equal({ fromBlockHeight: 44, count: 8 }); + + /** End historical sync */ + txStreamMock.finish(); + await waitOneTick(); + + /** Ensure that reorg-safe block set as last known block */ + await wallet.storage.saveState(); + let storage = storageAdapterMock.getItem(`wallet_${wallet.walletId}`) + let walletStoreState = storage.chains[wallet.network].wallet + expect(walletStoreState.lastKnownBlock.height).to.equal(46) + + /** Start continuous sync */ + txStreamWorker.execute() + await waitOneTick(); + + /** Ensure proper transport arguments */ + expect(transportMock.subscribeToTransactionsWithProofs.lastCall.args[1]) + .to.deep.equal({ fromBlockHeight: 52, count: 0 }); + + /** Broadcast transaction from the wallet */ + const sendTx = account.createTransaction({ + recipient: new PrivateKey().toAddress(), + satoshis: 1000 + }); + await account.broadcastTransaction(sendTx) + + Object.assign(scenario.metadata, { + [sendTx.hash]: { + transaction: sendTx, + height: 53, + blockHash: scenario.blockHeaders[0].hash + } + }) + + /** Wait for sendTx metadata arrives to the storage */ + await waitOneTick(); + + /** + * Ensure that storage still in reorg-safe state + */ + await wallet.storage.saveState(); + storage = storageAdapterMock.getItem(`wallet_${wallet.walletId}`) + let chainStoreState = storage.chains[wallet.network].chain + walletStoreState = storage.chains[wallet.network].wallet + + expect(Object.keys(chainStoreState.transactions)).to.have.lengthOf(2) + expect(Object.keys(chainStoreState.txMetadata)).to.have.lengthOf(2) + expect(Object.keys(chainStoreState.blockHeaders)).to.have.lengthOf(3) + expect(walletStoreState.lastKnownBlock.height).to.equal(46) + + + /** + * Emit one more BLOCKHEIGHT_CHANGE event to ensure that previously considered + * reorg unsafe items were saved + */ + transportMock.emit(EVENTS.BLOCKHEIGHT_CHANGED, { payload: (bestBlockHeight = 59) }) + await waitOneTick(); + + await wallet.storage.saveState(); + storage = storageAdapterMock.getItem(`wallet_${wallet.walletId}`) + chainStoreState = storage.chains[wallet.network].chain + walletStoreState = storage.chains[wallet.network].wallet + + /** + * Ensure that storage have been updated with the latest + * transactions and relevant chain data which now considered reorg safe + */ + expect(Object.keys(chainStoreState.transactions)).to.have.lengthOf(3) + expect(Object.keys(chainStoreState.txMetadata)).to.have.lengthOf(3) + expect(Object.keys(chainStoreState.blockHeaders)).to.have.lengthOf(3) + expect(walletStoreState.lastKnownBlock.height).to.equal(53) + }) + }) +}) diff --git a/packages/wallet-lib/webpack.config.js b/packages/wallet-lib/webpack.config.js new file mode 100644 index 00000000000..217234b50d5 --- /dev/null +++ b/packages/wallet-lib/webpack.config.js @@ -0,0 +1,41 @@ +const path = require('path'); +const webpack = require('webpack'); + +const webConfig = { + entry: './src/index.js', + mode: 'production', + target: 'web', + output: { + path: path.resolve(__dirname, 'dist'), + libraryTarget: 'umd', + filename: 'wallet-lib.min.js', + // fixes ReferenceError: window is not defined + globalObject: "(typeof self !== 'undefined' ? self : this)", + }, + resolve: { + fallback: { + fs: false, + crypto: require.resolve('crypto-browserify'), + buffer: require.resolve('buffer/'), + assert: require.resolve('assert/'), + url: require.resolve('url/'), + path: require.resolve('path-browserify'), + http: require.resolve('stream-http'), + https: require.resolve('https-browserify'), + stream: require.resolve('stream-browserify'), + util: require.resolve('util/'), + os: require.resolve('os-browserify/browser'), + zlib: require.resolve('browserify-zlib'), + events: require.resolve('events/'), + string_decoder: require.resolve('string_decoder/'), + }, + extensions: ['.ts', '.js', '.json'], + }, + plugins: [ + new webpack.ProvidePlugin({ + Buffer: [require.resolve('buffer/'), 'Buffer'], + process: require.resolve('process/browser'), + }), + ], +}; +module.exports = webConfig; diff --git a/scripts/configure_dashmate.sh b/scripts/configure_dashmate.sh new file mode 100755 index 00000000000..83edf07e4f5 --- /dev/null +++ b/scripts/configure_dashmate.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -e + +CONFIG_NAME="local" + +FULL_PATH=$(realpath "$0") +DIR_PATH=$(dirname "$FULL_PATH") +ROOT_PATH=$(dirname "$DIR_PATH") +PACKAGES_PATH="$ROOT_PATH/packages" + +DAPI_REPO_PATH="${PACKAGES_PATH}/dapi" +DRIVE_REPO_PATH="${PACKAGES_PATH}/js-drive" + +# build Drive and DAPI from sources +yarn dashmate config set --config=${CONFIG_NAME} platform.sourcePath "$ROOT_PATH" + +# create tenderdash blocks every 10s to speed up test suite +yarn dashmate config set --config=${CONFIG_NAME} platform.drive.tenderdash.consensus.createEmptyBlocksInterval "10s" + +# collect drive logs for bench suite +yarn dashmate config set --config=${CONFIG_NAME} platform.drive.abci.log.jsonFile.level "trace" +yarn dashmate config set --config=${CONFIG_NAME} platform.drive.abci.log.jsonFile.path "${ROOT_PATH}/logs/drive.json" diff --git a/scripts/configure_dotenv.sh b/scripts/configure_dotenv.sh new file mode 100755 index 00000000000..58de3c15e80 --- /dev/null +++ b/scripts/configure_dotenv.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +set -e + +SCRIPT_PATH=$(realpath "$0") +SCRIPT_DIRECTORY_PATH=$(dirname "$SCRIPT_PATH") +PROJECT_ROOT_PATH=$(dirname "$SCRIPT_DIRECTORY_PATH") +PACKAGES_PATH="$PROJECT_ROOT_PATH/packages" +LOGS_PATH="$PROJECT_ROOT_PATH/logs" + +CONFIG=local +DAPI_PATH="${PACKAGES_PATH}"/dapi +DRIVE_PATH="${PACKAGES_PATH}"/js-drive +SDK_PATH="${PACKAGES_PATH}"/js-dash-sdk +WALLET_LIB_PATH="${PACKAGES_PATH}"/wallet-lib + +touch "${LOGS_PATH}"/mint.log + +# DAPI: +cp "${DAPI_PATH}"/.env.example "${DAPI_PATH}"/.env + +# JS-SDK: +FAUCET_ADDRESS=$(grep -m 1 "Address:" "${LOGS_PATH}"/mint.log | awk '{printf $3}') +FAUCET_PRIVATE_KEY=$(grep -m 1 "Private key:" "${LOGS_PATH}"/mint.log | awk '{printf $4}') +DPNS_CONTRACT_ID=$(yarn dashmate config get --config="${CONFIG}_1" platform.dpns.contract.id) + +SDK_ENV_FILE_PATH=${SDK_PATH}/.env +rm -f "${SDK_ENV_FILE_PATH}" +touch "${SDK_ENV_FILE_PATH}" + +#cat << 'EOF' >> ${SDK_ENV_FILE_PATH} +echo "DAPI_SEED=127.0.0.1 +FAUCET_ADDRESS=${FAUCET_ADDRESS} +FAUCET_PRIVATE_KEY=${FAUCET_PRIVATE_KEY} +DPNS_CONTRACT_ID=${DPNS_CONTRACT_ID} +NETWORK=regtest" >> "${SDK_ENV_FILE_PATH}" +#EOF + +# DRIVE: +cp "${DRIVE_PATH}"/.env.example "${DRIVE_PATH}"/.env + +# WALLET-LIB: +WALLET_LIB_ENV_FILE_PATH=${WALLET_LIB_PATH}/.env +rm -f "${WALLET_LIB_ENV_FILE_PATH}" +touch "${WALLET_LIB_ENV_FILE_PATH}" + +#cat << 'EOF' >> ${SDK_ENV_FILE_PATH} +echo "DAPI_SEED=127.0.0.1 +FAUCET_PRIVATE_KEY=${FAUCET_PRIVATE_KEY} +NETWORK=regtest" >> "${WALLET_LIB_ENV_FILE_PATH}" +#EOF diff --git a/scripts/configure_test_suite.sh b/scripts/configure_test_suite.sh new file mode 100755 index 00000000000..fe7c9ef7fbc --- /dev/null +++ b/scripts/configure_test_suite.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash + +set -e + +PATH_TO_SCRIPT=$(realpath $0) +PATH_TO_SCRIPTS_DIRECTORY=$(dirname $PATH_TO_SCRIPT) +PATH_TO_PROJECT_ROOT=$(dirname $PATH_TO_SCRIPTS_DIRECTORY) +PATH_TO_PACKAGES="${PATH_TO_PROJECT_ROOT}/packages" + +TEST_SUITE_PATH="${PATH_TO_PACKAGES}/platform-test-suite" +BENCH_SUITE_PATH="${PATH_TO_PACKAGES}/bench-suite" + +CONFIG="local" + +SETUP_FILE_PATH=${PATH_TO_PROJECT_ROOT}/logs/setup.log + +DPNS_OWNER_PRIVATE_KEY=$(grep -m 1 "DPNS Private Key:" ${SETUP_FILE_PATH} | awk '{$1="";printf $5}') +FEATURE_FLAGS_OWNER_PRIVATE_KEY=$(grep -m 1 "Feature Flags Private Key:" ${SETUP_FILE_PATH} | awk '{$1="";printf $6}') +DASHPAY_OWNER_PRIVATE_KEY=$(grep -m 1 "Dashpay Private Key:" ${SETUP_FILE_PATH} | awk '{$1="";printf $5}') +MASTERNODE_REWARD_SHARES_OWNER_PRIVATE_KEY=$(grep -m 1 "Masternode Reward Shares Private Key:" "${SETUP_FILE_PATH}" | awk '{$1="";printf $7}') +MASTERNODE_REWARD_SHARES_OWNER_PRO_REG_TX_HASH=$(grep -m 1 "ProRegTx transaction ID:" "${SETUP_FILE_PATH}" | awk '{printf $5}') +MASTERNODE_REWARD_SHARES_MN_OWNER_PRIVATE_KEY=$(grep -m 1 "Owner Private Key:" "${SETUP_FILE_PATH}" | awk '{printf $5}') + +echo "Mint 100 Dash to faucet address" + +MINT_FILE_PATH=${PATH_TO_PROJECT_ROOT}/logs/mint.log + +yarn dashmate wallet mint --verbose --config=local_seed 100 | tee "${MINT_FILE_PATH}" +FAUCET_ADDRESS=$(grep -m 1 "Address:" "${MINT_FILE_PATH}" | awk '{printf $3}') +FAUCET_PRIVATE_KEY=$(grep -m 1 "Private key:" "${MINT_FILE_PATH}" | awk '{printf $4}') +FAUCET_WALLET_USE_STORAGE=true + +# check variables are not empty +if [ -z "$FAUCET_ADDRESS" ] || \ + [ -z "$FAUCET_PRIVATE_KEY" ] || \ + [ -z "$DPNS_OWNER_PRIVATE_KEY" ] || \ + [ -z "$FEATURE_FLAGS_OWNER_PRIVATE_KEY" ] || \ + [ -z "$DASHPAY_OWNER_PRIVATE_KEY" ] || \ + [ -z "$MASTERNODE_REWARD_SHARES_OWNER_PRO_REG_TX_HASH" ] || \ + [ -z "$MASTERNODE_REWARD_SHARES_OWNER_PRIVATE_KEY" ] || \ + [ -z "$MASTERNODE_REWARD_SHARES_MN_OWNER_PRIVATE_KEY" ] +then + echo "Internal error. Some of the env variables are empty. Please check logs above." + exit 1 +fi + +TEST_SUITE_ENV_FILE_PATH=${TEST_SUITE_PATH}/.env +rm -f ${TEST_SUITE_ENV_FILE_PATH} +touch ${TEST_SUITE_ENV_FILE_PATH} + +#cat << 'EOF' >> ${TEST_SUITE_ENV_FILE_PATH} +echo "DAPI_SEED=127.0.0.1 +FAUCET_ADDRESS=${FAUCET_ADDRESS} +FAUCET_PRIVATE_KEY=${FAUCET_PRIVATE_KEY} +FAUCET_WALLET_USE_STORAGE=${FAUCET_WALLET_USE_STORAGE} +FAUCET_WALLET_STORAGE_DIR="${PATH_TO_PROJECT_ROOT}/db" +DPNS_OWNER_PRIVATE_KEY=${DPNS_OWNER_PRIVATE_KEY} +FEATURE_FLAGS_OWNER_PRIVATE_KEY=${FEATURE_FLAGS_OWNER_PRIVATE_KEY} +DASHPAY_OWNER_PRIVATE_KEY=${DASHPAY_OWNER_PRIVATE_KEY} +MASTERNODE_REWARD_SHARES_OWNER_PRO_REG_TX_HASH=${MASTERNODE_REWARD_SHARES_OWNER_PRO_REG_TX_HASH} +MASTERNODE_REWARD_SHARES_OWNER_PRIVATE_KEY=${MASTERNODE_REWARD_SHARES_OWNER_PRIVATE_KEY} +MASTERNODE_REWARD_SHARES_MN_OWNER_PRIVATE_KEY=${MASTERNODE_REWARD_SHARES_MN_OWNER_PRIVATE_KEY} +NETWORK=regtest" >> ${TEST_SUITE_ENV_FILE_PATH} +#EOF + +BENCH_SUITE_ENV_FILE_PATH=${BENCH_SUITE_PATH}/.env +rm -f ${BENCH_SUITE_ENV_FILE_PATH} +touch ${BENCH_SUITE_ENV_FILE_PATH} + +#cat << 'EOF' >> ${BENCH_SUITE_ENV_FILE_PATH} +echo "DAPI_SEED=127.0.0.1 +FAUCET_ADDRESS=${FAUCET_ADDRESS} +FAUCET_PRIVATE_KEY=${FAUCET_PRIVATE_KEY} +DRIVE_LOG_PATH=${PATH_TO_PROJECT_ROOT}/logs/drive.json +NETWORK=regtest" >> ${BENCH_SUITE_ENV_FILE_PATH} +#EOF diff --git a/scripts/prepare_docs.sh b/scripts/prepare_docs.sh new file mode 100755 index 00000000000..ed983e5ce8b --- /dev/null +++ b/scripts/prepare_docs.sh @@ -0,0 +1,11 @@ +# Consolidate package docs into one folder for building documentation +cp -r ./packages/js-dapi-client/docs/ ./docs/DAPI-Client +mv ./docs/DAPI-Client/_sidebar.md ./docs/DAPI-Client/Overview.md +cp -r ./packages/js-dpp/docs/ ./docs/Dash-Platform-Protocol +mv ./docs/Dash-Platform-Protocol/_sidebar.md ./docs/Dash-Platform-Protocol/Overview.md +cp -r ./packages/js-dash-sdk/docs/ ./docs/SDK +mv ./docs/SDK/_sidebar.md ./docs/SDK/Overview.md +# Exclude folder with empty documents +rm -r ./docs/SDK/walkthroughs +cp -r ./packages/wallet-lib/docs/ ./docs/Wallet-library +mv ./docs/Wallet-library/_sidebar.md ./docs/Wallet-library/Overview.md diff --git a/scripts/release/bump_version.js b/scripts/release/bump_version.js new file mode 100755 index 00000000000..d0033652c57 --- /dev/null +++ b/scripts/release/bump_version.js @@ -0,0 +1,75 @@ +const fs = require('fs'); +const path = require('path'); +const semver = require('semver'); +const packagesIterator = require('../utils/packagesIterator'); +const rootPackageJson = require('../../package.json'); + +const convertReleaseToPrerelease = (version) => { + const bumpedVersion = semver.inc(version, 'minor'); + + return `${semver.major(bumpedVersion)}.${semver.minor(bumpedVersion)}.0-dev.1`; +}; + +(async () => { + let [ releaseType ] = process.argv.slice(2); + + const packagesDir = path.join(__dirname, '..', '..', 'packages'); + const { version: rootVersion } = rootPackageJson; + const rootVersionType = semver.prerelease(rootVersion) !== null ? 'prerelease' : 'release'; + + // Figure out release type using current version if not set + if (releaseType === undefined) { + // get releaseType from root package.json + releaseType = rootVersionType; + } + + if (rootVersionType === releaseType && releaseType === 'release') { + // release to release + for (const { filename, json } of packagesIterator(packagesDir)) { + const { version } = json; + json.version = semver.inc(version, 'patch'); + + fs.writeFileSync(filename, `${JSON.stringify(json, null, 2)}\n`); + } + + // root version + rootPackageJson.version = semver.inc(rootPackageJson.version, 'patch'); + fs.writeFileSync(path.join(__dirname, '..', '..', 'package.json'), `${JSON.stringify(rootPackageJson, null, 2)}\n`); + } else if (rootVersionType === 'release' && releaseType === 'prerelease') { + // release to prerelease + for (const { filename, json } of packagesIterator(packagesDir)) { + const { version } = json; + json.version = convertReleaseToPrerelease(version); + + fs.writeFileSync(filename, `${JSON.stringify(json, null, 2)}\n`); + } + + // root version + rootPackageJson.version = convertReleaseToPrerelease(rootPackageJson.version); + fs.writeFileSync(path.join(__dirname, '..', '..', 'package.json'), `${JSON.stringify(rootPackageJson, null, 2)}\n`); + } else if (rootVersionType === 'prerelease' && releaseType === 'release') { + // prerelease to release + for (const { filename, json } of packagesIterator(packagesDir)) { + const { version } = json; + json.version = semver.inc(version, 'minor'); + + fs.writeFileSync(filename, `${JSON.stringify(json, null, 2)}\n`); + } + + // root version + rootPackageJson.version = semver.inc(rootPackageJson.version, 'minor'); + fs.writeFileSync(path.join(__dirname, '..', '..', 'package.json'), `${JSON.stringify(rootPackageJson, null, 2)}\n`); + } else { + // prerelease to prerelease + for (const { filename, json } of packagesIterator(packagesDir)) { + const { version } = json; + json.version = semver.inc(version, 'prerelease'); + + fs.writeFileSync(filename, `${JSON.stringify(json, null, 2)}\n`); + } + + // root version + rootPackageJson.version = semver.inc(rootPackageJson.version, 'prerelease'); + fs.writeFileSync(path.join(__dirname, '..', '..', 'package.json'), `${JSON.stringify(rootPackageJson, null, 2)}\n`); + } +})(); diff --git a/scripts/release/find_latest_tag.js b/scripts/release/find_latest_tag.js new file mode 100644 index 00000000000..4193c327dc6 --- /dev/null +++ b/scripts/release/find_latest_tag.js @@ -0,0 +1,47 @@ +const semver = require('semver'); +const execute = require('../utils/execute'); + +const [ version ] = process.argv.slice(2); + +if (!version) { + console.log('usage example: yarn node find_latest_tag.js v0.21.0'); + process.exit(1); +} + +(async () => { + const tags = (await execute('git tag -l --sort=-v:refname')); + + const isPrerelease = semver.prerelease(version) !== null; + const parsedVersion = semver.parse(version); + + let result; + + if (!isPrerelease) { + // stable + + // try to find the latest stable version with same minor part + result = tags.match(new RegExp(`^v${parsedVersion.major}\.${parsedVersion.minor}\.([0-9]+)$`, 'mgi')); + + // try to find the latest stable version with previous minor part + if (!result) { + result = tags.match(new RegExp(`^v${parsedVersion.major}\.${parsedVersion.minor - 1}\.([0-9]+)$`, 'mgi')); + } + } else { + // prerelease + + // try to find previous prerelease + result = tags.match(new RegExp(`^v${parsedVersion.major}\.${parsedVersion.minor}\.0-dev.([0-9]+)$`, 'mgi')); + + if (!result) { + // try to find the latest stable version with previous minor part + result = tags.match(new RegExp(`^v${parsedVersion.major}\.${parsedVersion.minor - 1}\.([0-9]+)$`, 'mgi')); + } + } + + if (!result) { + console.log(`Can't find latest tag for the version ${version}`); + process.exit(1); + } + + console.log(result[0]); +})(); diff --git a/scripts/release/generate_changelog.js b/scripts/release/generate_changelog.js new file mode 100755 index 00000000000..544b2cc3348 --- /dev/null +++ b/scripts/release/generate_changelog.js @@ -0,0 +1,35 @@ +const fs = require('fs'); +const tempfile = require('tempfile') +const addStream = require('add-stream'); +const conventionalChangelog = require('conventional-changelog'); + +const [ from ] = process.argv.slice(2); + +if (!from) { + console.error('usage: generate_changelog.js v0.22.0'); + process.exit(1); +} + +const options = { + preset: 'dash', +}; + +const gitRawCommitsOpts = { + from, +}; + +const outfile = 'CHANGELOG.md'; +const tmp = tempfile(); + +const readStream = fs.createReadStream(outfile) + .on('error', function () { + console.warn('infile does not exist.') + }); + +conventionalChangelog(options, undefined, gitRawCommitsOpts) +.pipe(addStream(readStream)) + .pipe(fs.createWriteStream(tmp)) + .on('finish', function () { + fs.createReadStream(tmp) + .pipe(fs.createWriteStream(outfile)) + }); diff --git a/scripts/release/pr_description.md b/scripts/release/pr_description.md new file mode 100644 index 00000000000..e1504e6a9e1 --- /dev/null +++ b/scripts/release/pr_description.md @@ -0,0 +1,33 @@ + + + +## Issue being fixed or feature implemented + + +Release new Dash Platform version + +## What was done? + +- Updated changelog +- Bumped packages version + +## How Has This Been Tested? + + + +None + +## Breaking Changes + + +None + +## Checklist: + +- [x] I have performed a self-review of my own code +- [x] I have commented my code, particularly in hard-to-understand areas +- [x] I have added or updated relevant unit/integration/functional/e2e tests +- [x] I have made corresponding changes to the documentation + +**For repository code-owners and collaborators only** +- [x] I have assigned this pull request to a milestone diff --git a/scripts/release/release.sh b/scripts/release/release.sh new file mode 100755 index 00000000000..005433159ef --- /dev/null +++ b/scripts/release/release.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash + +set -e + +# get current dir +DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +# get current version +PACKAGE_VERSION=$(cat $DIR/../../package.json|grep version|head -1|awk -F: '{ print $2 }'|sed 's/[", ]//g') + +RELEASE_TYPE="$1" + +# if parameter is empty, get release type from current version +if [ -z "$RELEASE_TYPE" ] +then + if [[ $PACKAGE_VERSION == *-* ]] + then + RELEASE_TYPE="prerelease" + else + RELEASE_TYPE="release" + fi +fi + +if [[ $RELEASE_TYPE != "release" ]] && [[ $RELEASE_TYPE != "prerelease" ]] +then + echo "release or prerelease are the only acceptable options" + exit 1 +fi + +UNCOMMITTED_FILES="$(git status -su)" +if [ -n "$UNCOMMITTED_FILES" ] +then + echo "commit or stash your changes before running this script" + exit 1 +fi + +# ensure github authentication +if ! gh auth status&> /dev/null; then + gh auth login +fi + +# bump version +yarn node $DIR/bump_version.js "$RELEASE_TYPE" + +NEW_PACKAGE_VERSION=$(cat $DIR/../../package.json|grep version|head -1|awk -F: '{ print $2 }'|sed 's/[", ]//g') + +# get last tag for changelog +LATEST_TAG=$(yarn node $DIR/find_latest_tag.js $NEW_PACKAGE_VERSION) + +# generate changelog +yarn node $DIR/generate_changelog.js $LATEST_TAG + +echo "New version is $NEW_PACKAGE_VERSION" + +VERSION_WITHOUT_PRERELEASE=${NEW_PACKAGE_VERSION%-*} +CURRENT_BRANCH=$(git branch --show-current) + +if [[ $RELEASE_TYPE == "release" ]] +then + BRANCH="master" +else + BRANCH="v${VERSION_WITHOUT_PRERELEASE%.*}-dev" +fi + +if [[ "$CURRENT_BRANCH" != "$BRANCH" ]] +then + echo "you must run this script either from the master of from the dev branch" + git checkout . + exit 1 +fi + +# create branch +git checkout -b release_"$NEW_PACKAGE_VERSION" + +# commit changes +git commit -am "chore(release): update changelog and version to $NEW_PACKAGE_VERSION" + +# push changes +git push -u origin release_"$NEW_PACKAGE_VERSION" + +# create PR +if [[ $RELEASE_TYPE == "release" ]] +then + MILESTONE="v${VERSION_WITHOUT_PRERELEASE%.*}.x" +else + MILESTONE="v${VERSION_WITHOUT_PRERELEASE%.*}.0" +fi + +gh pr create --base $BRANCH \ + --fill \ + --title "chore(release): update changelog and bump version to $NEW_PACKAGE_VERSION" \ + --body-file $DIR/pr_description.md \ + --milestone $MILESTONE + +# switch back to base branch +git checkout - diff --git a/scripts/setup_local_network.sh b/scripts/setup_local_network.sh new file mode 100755 index 00000000000..69c081aab46 --- /dev/null +++ b/scripts/setup_local_network.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +set -e + +MINING_INTERVAL_IN_SECONDS=30 +MASTERNODES_COUNT=3 + +FULL_PATH=$(realpath $0) +DIR_PATH=$(dirname $FULL_PATH) +ROOT_PATH=$(dirname $DIR_PATH) +PACKAGES_PATH="$ROOT_PATH/packages" + +DASHMATE_BIN="${PACKAGES_PATH}/dashmate/bin/dashmate" + +yarn dashmate update -v + +yarn dashmate setup local --verbose \ + --debug-logs \ + --miner-interval="${MINING_INTERVAL_IN_SECONDS}s" \ + --node-count=${MASTERNODES_COUNT} | tee "${ROOT_PATH}"/logs/setup.log diff --git a/scripts/utils/execute.js b/scripts/utils/execute.js new file mode 100644 index 00000000000..7574b845d26 --- /dev/null +++ b/scripts/utils/execute.js @@ -0,0 +1,27 @@ +const { exec } = require('child_process'); + +/** + * + * @param {string} command + * @param [options] + * @param {string} [options.cwd] - working directory to run command from + * @param {boolean} [options.forwardStdout] - forwarding stdout of the command to console.log + * @returns {Promise} + */ +module.exports = function execute(command, options) { + return new Promise((resolve, reject) => { + const childProcess = exec(command, options, (err, result) => { + if (err) { + return reject(err); + } + + return resolve(result); + }); + + if (options && options.forwardStdout && childProcess.stdout) { + childProcess.stdout.on('data', (data) => { + process.stdout.write(data); + }); + } + }); +} diff --git a/scripts/utils/packagesIterator.js b/scripts/utils/packagesIterator.js new file mode 100644 index 00000000000..8fb6859d8a5 --- /dev/null +++ b/scripts/utils/packagesIterator.js @@ -0,0 +1,22 @@ +const fs = require('fs'); +const path = require('path'); + +/** + * + * @param {string} packagesDir + * @return {Generator<{filename: string, json: Object}, void, *>} + */ +module.exports = function *packagesIterator (packagesDir) { + const items = fs.readdirSync(packagesDir); + + for (const item of items) { + const fullPath = path.join(packagesDir, item); + + if (fs.lstatSync(fullPath).isDirectory()) { + const packageFile = path.join(fullPath, 'package.json'); + + yield { filename: packageFile, json: require(packageFile) }; + } + } +}; + diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000000..b3b2b66daa1 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,15856 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 5 + cacheKey: 8 + +"@apidevtools/json-schema-ref-parser@npm:^8.0.0": + version: 8.0.0 + resolution: "@apidevtools/json-schema-ref-parser@npm:8.0.0" + dependencies: + "@jsdevtools/ono": ^7.1.0 + call-me-maybe: ^1.0.1 + js-yaml: ^3.13.1 + checksum: 3875f3c2fcde9330fa2da5fa8bf4150fb50dc8b5f43b02c5315b79c471b1e1603c3b0320c31f2be80fbc81376efd19e87c8d13da21daadd92544354b6d43d2e7 + languageName: node + linkType: hard + +"@babel/code-frame@npm:7.12.11": + version: 7.12.11 + resolution: "@babel/code-frame@npm:7.12.11" + dependencies: + "@babel/highlight": ^7.10.4 + checksum: 3963eff3ebfb0e091c7e6f99596ef4b258683e4ba8a134e4e95f77afe85be5c931e184fff6435fb4885d12eba04a5e25532f7fbc292ca13b48e7da943474e2f3 + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/code-frame@npm:7.16.7" + dependencies: + "@babel/highlight": ^7.16.7 + checksum: db2f7faa31bc2c9cf63197b481b30ea57147a5fc1a6fab60e5d6c02cdfbf6de8e17b5121f99917b3dabb5eeb572da078312e70697415940383efc140d4e0808b + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.13.11, @babel/compat-data@npm:^7.16.0, @babel/compat-data@npm:^7.16.4": + version: 7.16.4 + resolution: "@babel/compat-data@npm:7.16.4" + checksum: 4949ce54eafc4b38d5623696a872acaaced1a523605708d81c2c483253941917d90dae0de40fc01e152ae56075dadd89c23014da5a632b09c001a716fa689cae + languageName: node + linkType: hard + +"@babel/core@npm:^7.15.5, @babel/core@npm:^7.7.5": + version: 7.16.0 + resolution: "@babel/core@npm:7.16.0" + dependencies: + "@babel/code-frame": ^7.16.0 + "@babel/generator": ^7.16.0 + "@babel/helper-compilation-targets": ^7.16.0 + "@babel/helper-module-transforms": ^7.16.0 + "@babel/helpers": ^7.16.0 + "@babel/parser": ^7.16.0 + "@babel/template": ^7.16.0 + "@babel/traverse": ^7.16.0 + "@babel/types": ^7.16.0 + convert-source-map: ^1.7.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.1.2 + semver: ^6.3.0 + source-map: ^0.5.0 + checksum: a140f669daa90c774016a76b1f85641975333c1c219ae0a8e65d8b4c316836e918276e0dfd55613b14f8e578406a92393d4368a63bdd5d0708122976ee2ee8e3 + languageName: node + linkType: hard + +"@babel/generator@npm:^7.16.0, @babel/generator@npm:^7.17.3": + version: 7.17.3 + resolution: "@babel/generator@npm:7.17.3" + dependencies: + "@babel/types": ^7.17.0 + jsesc: ^2.5.1 + source-map: ^0.5.0 + checksum: ddf70e3489976018dfc2da8b9f43ec8c582cac2da681ed4a6227c53b26a9626223e4dca90098b3d3afe43bc67f20160856240e826c56b48e577f34a5a7e22b9f + languageName: node + linkType: hard + +"@babel/helper-annotate-as-pure@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-annotate-as-pure@npm:7.16.0" + dependencies: + "@babel/types": ^7.16.0 + checksum: 0db76106983e10ffc482c5f01e89c3b4687d2474bea69c44470b2acb6bd37f362f9057d6e69c617255390b5d0063d9932a931e83c3e130445b688ca1fcdb5bcd + languageName: node + linkType: hard + +"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.16.0" + dependencies: + "@babel/helper-explode-assignable-expression": ^7.16.0 + "@babel/types": ^7.16.0 + checksum: 01beb9f3f2285b7b170cc167ec79b2fd657202cb25be9cb111951f94a04c97c5b446dd1498ede32f0052d67fc9f2f2ac2b7862351b364fe94f9b4de98488d863 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.16.0, @babel/helper-compilation-targets@npm:^7.16.3": + version: 7.16.3 + resolution: "@babel/helper-compilation-targets@npm:7.16.3" + dependencies: + "@babel/compat-data": ^7.16.0 + "@babel/helper-validator-option": ^7.14.5 + browserslist: ^4.17.5 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 038bcd43ac914371c51bf6e72b5cedcae432f0d359285d74a9133c6a839bd625a7d5412d7471d50aa78a3e1c79b0a692b50a8d6a1299ebf69733b512ff199323 + languageName: node + linkType: hard + +"@babel/helper-create-class-features-plugin@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-create-class-features-plugin@npm:7.16.0" + dependencies: + "@babel/helper-annotate-as-pure": ^7.16.0 + "@babel/helper-function-name": ^7.16.0 + "@babel/helper-member-expression-to-functions": ^7.16.0 + "@babel/helper-optimise-call-expression": ^7.16.0 + "@babel/helper-replace-supers": ^7.16.0 + "@babel/helper-split-export-declaration": ^7.16.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 0f7d1b8d413e5fbd719c95e22e3b59749b4c6c652f20e0fa1fa954112145a134c22709f1325574632d7262aeeeaaf4fc7c2eb8117e0d521e42b36d05c3e5a885 + languageName: node + linkType: hard + +"@babel/helper-create-regexp-features-plugin@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.16.0" + dependencies: + "@babel/helper-annotate-as-pure": ^7.16.0 + regexpu-core: ^4.7.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: d6230477e1997ed1fa0aee9ab34d3ce96400e0df25101879fdaf90ea613adec68ec06a609d8c78787c02a6275ef5a7403a38aa8fd42fef1a4d27bcfe577c81d6 + languageName: node + linkType: hard + +"@babel/helper-define-polyfill-provider@npm:^0.3.0": + version: 0.3.0 + resolution: "@babel/helper-define-polyfill-provider@npm:0.3.0" + dependencies: + "@babel/helper-compilation-targets": ^7.13.0 + "@babel/helper-module-imports": ^7.12.13 + "@babel/helper-plugin-utils": ^7.13.0 + "@babel/traverse": ^7.13.0 + debug: ^4.1.1 + lodash.debounce: ^4.0.8 + resolve: ^1.14.2 + semver: ^6.1.2 + peerDependencies: + "@babel/core": ^7.4.0-0 + checksum: 372378ac4235c4fe135f1cd6d0f63697e7cb3ef63a884eb14f4b439984846bcaec0b7a32cf8df6756a21557ae3ebb3c2ee18d9a191260705a583333e5e60df7c + languageName: node + linkType: hard + +"@babel/helper-environment-visitor@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-environment-visitor@npm:7.16.7" + dependencies: + "@babel/types": ^7.16.7 + checksum: c03a10105d9ebd1fe632a77356b2e6e2f3c44edba9a93b0dc3591b6a66bd7a2e323dd9502f9ce96fc6401234abff1907aa877b6674f7826b61c953f7c8204bbe + languageName: node + linkType: hard + +"@babel/helper-explode-assignable-expression@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-explode-assignable-expression@npm:7.16.0" + dependencies: + "@babel/types": ^7.16.0 + checksum: 563352b5e9b0b9584187176723ea65ea6ac9348d612c2bdc76701634eae445fd05d18f7b7555f5c6bbe4ec4d9d30172633a56bf4cfbb1333b798f58444057652 + languageName: node + linkType: hard + +"@babel/helper-function-name@npm:^7.16.0, @babel/helper-function-name@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-function-name@npm:7.16.7" + dependencies: + "@babel/helper-get-function-arity": ^7.16.7 + "@babel/template": ^7.16.7 + "@babel/types": ^7.16.7 + checksum: fc77cbe7b10cfa2a262d7a37dca575c037f20419dfe0c5d9317f589599ca24beb5f5c1057748011159149eaec47fe32338c6c6412376fcded68200df470161e1 + languageName: node + linkType: hard + +"@babel/helper-get-function-arity@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-get-function-arity@npm:7.16.7" + dependencies: + "@babel/types": ^7.16.7 + checksum: 25d969fb207ff2ad5f57a90d118f6c42d56a0171022e200aaa919ba7dc95ae7f92ec71cdea6c63ef3629a0dc962ab4c78e09ca2b437185ab44539193f796e0c3 + languageName: node + linkType: hard + +"@babel/helper-hoist-variables@npm:^7.16.0, @babel/helper-hoist-variables@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-hoist-variables@npm:7.16.7" + dependencies: + "@babel/types": ^7.16.7 + checksum: 6ae1641f4a751cd9045346e3f61c3d9ec1312fd779ab6d6fecfe2a96e59a481ad5d7e40d2a840894c13b3fd6114345b157f9e3062fc5f1580f284636e722de60 + languageName: node + linkType: hard + +"@babel/helper-member-expression-to-functions@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-member-expression-to-functions@npm:7.16.0" + dependencies: + "@babel/types": ^7.16.0 + checksum: 58ef8e3a4af0c1dc43a2011f43f25502877ac1c5aa9a4a6586f0265ab857b65831f60560044bc9380df43c91ac21cad39a84095b91764b433d1acf18d27e38d6 + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-module-imports@npm:7.16.0" + dependencies: + "@babel/types": ^7.16.0 + checksum: 8e1eb9ac39440e52080b87c78d8d318e7c93658bdd0f3ce0019c908de88cbddafdc241f392898c0b0ba81fc52c8c6d2f9cc1b163ac5ed2a474d49b11646b7516 + languageName: node + linkType: hard + +"@babel/helper-module-transforms@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-module-transforms@npm:7.16.0" + dependencies: + "@babel/helper-module-imports": ^7.16.0 + "@babel/helper-replace-supers": ^7.16.0 + "@babel/helper-simple-access": ^7.16.0 + "@babel/helper-split-export-declaration": ^7.16.0 + "@babel/helper-validator-identifier": ^7.15.7 + "@babel/template": ^7.16.0 + "@babel/traverse": ^7.16.0 + "@babel/types": ^7.16.0 + checksum: a3d0e5556f26ebdf2ae422af3b9a1ba1848fead891f46bcd1c6a4be88ad8e9f348140f81d1843a3481574be1643a9c79b01469231f5b5801f5d5e691efdd11f3 + languageName: node + linkType: hard + +"@babel/helper-optimise-call-expression@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-optimise-call-expression@npm:7.16.0" + dependencies: + "@babel/types": ^7.16.0 + checksum: 121ae6054fcec76ed2c4dd83f0281b901c1e3cfac1bbff79adc3667983903ad1030a0ad9a8bea58e52b225e13881cf316f371c65276976e7a6762758a98be8f6 + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.13.0, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": + version: 7.14.5 + resolution: "@babel/helper-plugin-utils@npm:7.14.5" + checksum: fe20e90a24d02770a60ebe80ab9f0dfd7258503cea8006c71709ac9af1aa3e47b0de569499673f11ea6c99597f8c0e4880ae1d505986e61101b69716820972fe + languageName: node + linkType: hard + +"@babel/helper-remap-async-to-generator@npm:^7.16.0, @babel/helper-remap-async-to-generator@npm:^7.16.4": + version: 7.16.4 + resolution: "@babel/helper-remap-async-to-generator@npm:7.16.4" + dependencies: + "@babel/helper-annotate-as-pure": ^7.16.0 + "@babel/helper-wrap-function": ^7.16.0 + "@babel/types": ^7.16.0 + checksum: debe997695fe2c11813e88b2fa4afc89d4543f72457dda00c7296a728cd5eeb81d4ef8607a5fef7823da410a8579407c631a430e5bfc78290172ff6fc430355c + languageName: node + linkType: hard + +"@babel/helper-replace-supers@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-replace-supers@npm:7.16.0" + dependencies: + "@babel/helper-member-expression-to-functions": ^7.16.0 + "@babel/helper-optimise-call-expression": ^7.16.0 + "@babel/traverse": ^7.16.0 + "@babel/types": ^7.16.0 + checksum: 61f04bbe05ff0987d5a8d5253cb101d47004a27951d6c5cd95457e30fcb3adaca85f0bcaa7f31f4d934f22386b935ac7281398c68982d4a4768769d95c028460 + languageName: node + linkType: hard + +"@babel/helper-simple-access@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-simple-access@npm:7.16.0" + dependencies: + "@babel/types": ^7.16.0 + checksum: 2d7155f318411788b42d2f4a3d406de12952ad620d0bd411a0f3b5803389692ad61d9e7fab5f93b23ad3d8a09db4a75ca9722b9873a606470f468bc301944af6 + languageName: node + linkType: hard + +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.16.0" + dependencies: + "@babel/types": ^7.16.0 + checksum: b9ed2896eb253e6a85f472b0d4098ed80403758ad1a4e34b02b11e8276e3083297526758b1a3e6886e292987266f10622d7dbced3508cc22b296a74903b41cfb + languageName: node + linkType: hard + +"@babel/helper-split-export-declaration@npm:^7.16.0, @babel/helper-split-export-declaration@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-split-export-declaration@npm:7.16.7" + dependencies: + "@babel/types": ^7.16.7 + checksum: e10aaf135465c55114627951b79115f24bc7af72ecbb58d541d66daf1edaee5dde7cae3ec8c3639afaf74526c03ae3ce723444e3b5b3dc77140c456cd84bcaa1 + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.15.7, @babel/helper-validator-identifier@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-validator-identifier@npm:7.16.7" + checksum: dbb3db9d184343152520a209b5684f5e0ed416109cde82b428ca9c759c29b10c7450657785a8b5c5256aa74acc6da491c1f0cf6b784939f7931ef82982051b69 + languageName: node + linkType: hard + +"@babel/helper-validator-option@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/helper-validator-option@npm:7.14.5" + checksum: 1b25c34a5cb3d8602280f33b9ab687d2a77895e3616458d0f70ddc450ada9b05e342c44f322bc741d51b252e84cff6ec44ae93d622a3354828579a643556b523 + languageName: node + linkType: hard + +"@babel/helper-wrap-function@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-wrap-function@npm:7.16.0" + dependencies: + "@babel/helper-function-name": ^7.16.0 + "@babel/template": ^7.16.0 + "@babel/traverse": ^7.16.0 + "@babel/types": ^7.16.0 + checksum: 2bb4e05f49cf217cc5890581284a051245ba0ddaccbe3ddd662010d7a6969f52d2027e310d26db2e030273c5fe9341448c7845fcb4795ad8eb56bdeabec148b8 + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.16.0": + version: 7.16.3 + resolution: "@babel/helpers@npm:7.16.3" + dependencies: + "@babel/template": ^7.16.0 + "@babel/traverse": ^7.16.3 + "@babel/types": ^7.16.0 + checksum: b725b1aab734e9e1407247ee499880583855843fa2855377a2c26277bd9fbd7080219109189bc69b18d71cc30759666bfe66d534729b41452097866d1f5a66ef + languageName: node + linkType: hard + +"@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.16.7": + version: 7.16.10 + resolution: "@babel/highlight@npm:7.16.10" + dependencies: + "@babel/helper-validator-identifier": ^7.16.7 + chalk: ^2.0.0 + js-tokens: ^4.0.0 + checksum: 1f1bdd752a90844f4efc22166a46303fb651ba0fd75a06daba3ebae2575ab3edc1da9827c279872a3aaf305f50a18473c5fa1966752726a2b253065fd4c0745e + languageName: node + linkType: hard + +"@babel/parser@npm:^7.16.0, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.17.3, @babel/parser@npm:^7.7.0": + version: 7.17.3 + resolution: "@babel/parser@npm:7.17.3" + bin: + parser: ./bin/babel-parser.js + checksum: 311869baef97c7630ac3b3c4600da18229b95aa2785b2daab2044384745fe0653070916ade28749fb003f7369a081111ada53e37284ba48d6b5858cbb9e411d1 + languageName: node + linkType: hard + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.16.2": + version: 7.16.2 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.16.2" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 6ed9dbbf18b24f6edd2286554f718ea3a1eb3fdae4faece6fabfb68d1e249377d8392ae1931f52ce67fdfcfec26caf8d141bbcce9d6321851b5a08f52070a91e + languageName: node + linkType: hard + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 + "@babel/plugin-proposal-optional-chaining": ^7.16.0 + peerDependencies: + "@babel/core": ^7.13.0 + checksum: bb115479292e2c66671a62c46a64d8dae1fc8bbf604c83f82a421216e3d40632dbe86e8ba34e66318c215eddfc4f25e6e7fe19123517f1cf5b6003b1efbd911a + languageName: node + linkType: hard + +"@babel/plugin-proposal-async-generator-functions@npm:^7.16.4": + version: 7.16.4 + resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.16.4" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-remap-async-to-generator": ^7.16.4 + "@babel/plugin-syntax-async-generators": ^7.8.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: dcd5a76ee12eacee93440e021a7e4a8e53b5d13d26c8fd7d412fc83341a1633a949bef1ef94301ae753164d39d303cb01b59234e6b48205377ca1d041f670ba5 + languageName: node + linkType: hard + +"@babel/plugin-proposal-class-properties@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-class-properties@npm:7.16.0" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b1665ced553e5cdb95eec2fda321cb226c5f255edd1a94b226b9d81e97e026472184b6898af26f2bb9ee64101fad1afe215b6fc469d3103dec78c55e732e49aa + languageName: node + linkType: hard + +"@babel/plugin-proposal-class-static-block@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-class-static-block@npm:7.16.0" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/plugin-syntax-class-static-block": ^7.14.5 + peerDependencies: + "@babel/core": ^7.12.0 + checksum: 59c4bb3d6ad4828e7773fe1c63730c68bf646c3a8d042b9ed4062fd98a26c1656b7ee108c5f144fd8b24ff567baf3b2efa644be29c6c8bcfe60e09e485e22116 + languageName: node + linkType: hard + +"@babel/plugin-proposal-dynamic-import@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-dynamic-import@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/plugin-syntax-dynamic-import": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 4027da640443d8fd4a20637d1dd67cce1c13207b8c19fa77796a08b9eec9881b95322c1a5c489128adf3a12e9bbc02b31de9ddd536c909d072577a74a2a70b67 + languageName: node + linkType: hard + +"@babel/plugin-proposal-export-namespace-from@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/plugin-syntax-export-namespace-from": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0bdc166ac44d9a0579e6d14d07ed1364932b4b7852626f4ba0c0011464097ed23bec43a3e93793d888c2854918ce9937ac251a945abbe0d283eaa1df206e0b05 + languageName: node + linkType: hard + +"@babel/plugin-proposal-json-strings@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-json-strings@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/plugin-syntax-json-strings": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fa93be8eff22ced96a68c9db8c0e930414a4ffb44cf68b473717309c06a4feee2bac6e41415a699c829f29928653d67b4b7d29a45861784d235264d829055a1e + languageName: node + linkType: hard + +"@babel/plugin-proposal-logical-assignment-operators@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7e6cd10248803f0c5801805ef1a357314940c3204c3d2f00994711f272c21276f181d0e83ada5bce6185ae2c97c4417e778331505ffc2e71a2b9c4425a5dcc6d + languageName: node + linkType: hard + +"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e50f94929970cdc5c6ee22ec4c95c46ae25cdd8c391baf601f7f3d3a3cec417efc663a3fafa9ae5bca82a6815d49687b07cab9857f5a10e9ea862438ecb81e4a + languageName: node + linkType: hard + +"@babel/plugin-proposal-numeric-separator@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-numeric-separator@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/plugin-syntax-numeric-separator": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: eb7895a4f38263df644a0ded7042991190f23bdec4b53f3e2c8b40b82d2dbc537a6ca9afbfd490d1aa5dd33244e7a51bf1ae0c4c6890d9978bc1adc325b7e795 + languageName: node + linkType: hard + +"@babel/plugin-proposal-object-rest-spread@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.16.0" + dependencies: + "@babel/compat-data": ^7.16.0 + "@babel/helper-compilation-targets": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/plugin-syntax-object-rest-spread": ^7.8.3 + "@babel/plugin-transform-parameters": ^7.16.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c7716ba50e65aae613e553dd568d3f4b4c42fa8d9f1c3aca6cc227670fc792b600cd5a5c710451490f3d7d5916e77607cba45033e199534ca71feed451f63820 + languageName: node + linkType: hard + +"@babel/plugin-proposal-optional-catch-binding@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-optional-catch-binding@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 5003a1d48fb6bac1661b481681baf7941de518f1f773d9745e65a650e750b715cb69181a4b723e28f4e43b94143b7b0fe5d12ff1ceceda9731f073cd6bf4e195 + languageName: node + linkType: hard + +"@babel/plugin-proposal-optional-chaining@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-optional-chaining@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 + "@babel/plugin-syntax-optional-chaining": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8301e0829220327c8b969b711c5c4ee5aef88b391e5fb7838381bd18c0fd0cf360d3a307ad5c6113414470ae920504dc2c41983af0ddf3762f5c88957e0c3a94 + languageName: node + linkType: hard + +"@babel/plugin-proposal-private-methods@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-private-methods@npm:7.16.0" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 6f648f54ea1219262b7a05f86f94de7cb466dc81ffd86e4f37ba536037762457ef13408083eb4325d44d2a5aae27c097756efe1067f5c1fbddb8078b923580f5 + languageName: node + linkType: hard + +"@babel/plugin-proposal-private-property-in-object@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.16.0" + dependencies: + "@babel/helper-annotate-as-pure": ^7.16.0 + "@babel/helper-create-class-features-plugin": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/plugin-syntax-private-property-in-object": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 9098fb34f4abac376ec5823bf6aaedacd46e6925a6fc62559a8086a110bf39310ee308bfbbed052f047ad803b7148b87e43b6d83a759be0aeab1149efd4b8eeb + languageName: node + linkType: hard + +"@babel/plugin-proposal-unicode-property-regex@npm:^7.16.0, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": + version: 7.16.0 + resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.16.0" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f26b76c9aa680820fe693f768a36e3a2c4d969e72d7a362059fffad7c874eed8a89bde2be5bde650283a685bd879415f8937fb37a9a1397b287a81df0c6f7c23 + languageName: node + linkType: hard + +"@babel/plugin-syntax-async-generators@npm:^7.8.4": + version: 7.8.4 + resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7ed1c1d9b9e5b64ef028ea5e755c0be2d4e5e4e3d6cf7df757b9a8c4cfa4193d268176d0f1f7fbecdda6fe722885c7fda681f480f3741d8a2d26854736f05367 + languageName: node + linkType: hard + +"@babel/plugin-syntax-class-properties@npm:^7.12.13": + version: 7.12.13 + resolution: "@babel/plugin-syntax-class-properties@npm:7.12.13" + dependencies: + "@babel/helper-plugin-utils": ^7.12.13 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 24f34b196d6342f28d4bad303612d7ff566ab0a013ce89e775d98d6f832969462e7235f3e7eaf17678a533d4be0ba45d3ae34ab4e5a9dcbda5d98d49e5efa2fc + languageName: node + linkType: hard + +"@babel/plugin-syntax-class-static-block@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/plugin-syntax-class-static-block@npm:7.14.5" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3e80814b5b6d4fe17826093918680a351c2d34398a914ce6e55d8083d72a9bdde4fbaf6a2dcea0e23a03de26dc2917ae3efd603d27099e2b98380345703bf948 + languageName: node + linkType: hard + +"@babel/plugin-syntax-dynamic-import@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-dynamic-import@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ce307af83cf433d4ec42932329fad25fa73138ab39c7436882ea28742e1c0066626d224e0ad2988724c82644e41601cef607b36194f695cb78a1fcdc959637bd + languageName: node + linkType: hard + +"@babel/plugin-syntax-export-namespace-from@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-export-namespace-from@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 85740478be5b0de185228e7814451d74ab8ce0a26fcca7613955262a26e99e8e15e9da58f60c754b84515d4c679b590dbd3f2148f0f58025f4ae706f1c5a5d4a + languageName: node + linkType: hard + +"@babel/plugin-syntax-json-strings@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-json-strings@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bf5aea1f3188c9a507e16efe030efb996853ca3cadd6512c51db7233cc58f3ac89ff8c6bdfb01d30843b161cfe7d321e1bf28da82f7ab8d7e6bc5464666f354a + languageName: node + linkType: hard + +"@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4": + version: 7.10.4 + resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: aff33577037e34e515911255cdbb1fd39efee33658aa00b8a5fd3a4b903585112d037cce1cc9e4632f0487dc554486106b79ccd5ea63a2e00df4363f6d4ff886 + languageName: node + linkType: hard + +"@babel/plugin-syntax-nullish-coalescing-operator@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-nullish-coalescing-operator@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 87aca4918916020d1fedba54c0e232de408df2644a425d153be368313fdde40d96088feed6c4e5ab72aac89be5d07fef2ddf329a15109c5eb65df006bf2580d1 + languageName: node + linkType: hard + +"@babel/plugin-syntax-numeric-separator@npm:^7.10.4": + version: 7.10.4 + resolution: "@babel/plugin-syntax-numeric-separator@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 01ec5547bd0497f76cc903ff4d6b02abc8c05f301c88d2622b6d834e33a5651aa7c7a3d80d8d57656a4588f7276eba357f6b7e006482f5b564b7a6488de493a1 + languageName: node + linkType: hard + +"@babel/plugin-syntax-object-rest-spread@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-object-rest-spread@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fddcf581a57f77e80eb6b981b10658421bc321ba5f0a5b754118c6a92a5448f12a0c336f77b8abf734841e102e5126d69110a306eadb03ca3e1547cab31f5cbf + languageName: node + linkType: hard + +"@babel/plugin-syntax-optional-catch-binding@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-optional-catch-binding@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 910d90e72bc90ea1ce698e89c1027fed8845212d5ab588e35ef91f13b93143845f94e2539d831dc8d8ededc14ec02f04f7bd6a8179edd43a326c784e7ed7f0b9 + languageName: node + linkType: hard + +"@babel/plugin-syntax-optional-chaining@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-optional-chaining@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: eef94d53a1453361553c1f98b68d17782861a04a392840341bc91780838dd4e695209c783631cf0de14c635758beafb6a3a65399846ffa4386bff90639347f30 + languageName: node + linkType: hard + +"@babel/plugin-syntax-private-property-in-object@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/plugin-syntax-private-property-in-object@npm:7.14.5" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b317174783e6e96029b743ccff2a67d63d38756876e7e5d0ba53a322e38d9ca452c13354a57de1ad476b4c066dbae699e0ca157441da611117a47af88985ecda + languageName: node + linkType: hard + +"@babel/plugin-syntax-top-level-await@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/plugin-syntax-top-level-await@npm:7.14.5" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bbd1a56b095be7820029b209677b194db9b1d26691fe999856462e66b25b281f031f3dfd91b1619e9dcf95bebe336211833b854d0fb8780d618e35667c2d0d7e + languageName: node + linkType: hard + +"@babel/plugin-transform-arrow-functions@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ff647300424968d1cd6c6b015fd72d332042a94c7b08f3e785f32d22364bfad49258a41c53675de08573af98da1a623efa03da13a653f06988f79a9d571f7030 + languageName: node + linkType: hard + +"@babel/plugin-transform-async-to-generator@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.16.0" + dependencies: + "@babel/helper-module-imports": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-remap-async-to-generator": ^7.16.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 2ebf505f43350d246007d754577477ddb0132c4ab39c9fd420d36ebb6e489b2b3eb48f27fe58f7ad0c742946a1e81e3b150666507abab03fe6bd649ff585ed45 + languageName: node + linkType: hard + +"@babel/plugin-transform-block-scoped-functions@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f7efc5d8ce9242e11c94c82d9c940d4c534a751ff3679839d2f7d7a300c29ac4c4a3c26c238b5f2828201cac8a848bfb6342c285460f6ce5bc267cbdc1bb070b + languageName: node + linkType: hard + +"@babel/plugin-transform-block-scoping@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-block-scoping@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e5bcb9eeed7974ee6dd14c360c21ad2465f81342001e5468bbec5db483fffc78bb0e7f84155be6c32588bc0b43a6ca0050c7962400b33d134f6298c31c8073d4 + languageName: node + linkType: hard + +"@babel/plugin-transform-classes@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-classes@npm:7.16.0" + dependencies: + "@babel/helper-annotate-as-pure": ^7.16.0 + "@babel/helper-function-name": ^7.16.0 + "@babel/helper-optimise-call-expression": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-replace-supers": ^7.16.0 + "@babel/helper-split-export-declaration": ^7.16.0 + globals: ^11.1.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7db47296045761b3f35a9075b4bcce99ad5aa93714cca235961fa596983ba6cfd4d84b29fa6745e4752bd2a60ac299b0dee3231ce20061b6798ae16a147e4992 + languageName: node + linkType: hard + +"@babel/plugin-transform-computed-properties@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-computed-properties@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0f86de419cf5daf28b01c5b2feafa426e5b0ec776290e731de3d7a6ec4ec742400e13436d67292e500ecd50e21ddab9ae34da79357a85a443d30dc94f2a4f6a3 + languageName: node + linkType: hard + +"@babel/plugin-transform-destructuring@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-destructuring@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0a499c9abd6b50d4da6a3c8416e3cdf305f8002fddb3bd9ddd0774ba17ab1b10134f79fe8edc495c94344e5ab387626fb0ee124d31810758968a92d573ff9034 + languageName: node + linkType: hard + +"@babel/plugin-transform-dotall-regex@npm:^7.16.0, @babel/plugin-transform-dotall-regex@npm:^7.4.4": + version: 7.16.0 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.16.0" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c1f381f0d44a1b33714a68ffd60f2b9efac1be95caf3c21192cc8233afde2fae1da268e26b3cb40764736f090793b66946574c3310cfdd4906a7e72310239ff9 + languageName: node + linkType: hard + +"@babel/plugin-transform-duplicate-keys@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 66f09487fdf737aa280c780a609bafc9a771b34b5f9a8dccf69752c22110893763f6c105062776f084ed872a55d1656b3f14e2a9c2031f3dbdf31da20d9c827b + languageName: node + linkType: hard + +"@babel/plugin-transform-exponentiation-operator@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.16.0" + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 22e1d4804a5fc522744a1cc13e2c35c5d81c2e303a634822fee59829477b3748dcf897a020c3083084350ab1d3b76752157b216971157763394021e2f2184094 + languageName: node + linkType: hard + +"@babel/plugin-transform-for-of@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-for-of@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 504d967b30b00d3e1a2784f6a215963fc0036871f8fd6ca61e41e67cdb3319511e9148164428144469416b35b0e02c896c144402ace7cd7a6c45b0d1e8746ae6 + languageName: node + linkType: hard + +"@babel/plugin-transform-function-name@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-function-name@npm:7.16.0" + dependencies: + "@babel/helper-function-name": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 289f4fce26e8b3a81fcae752cecdb78b363eb29e400aa4dc8318484156d908ddc6dd5b274b8fbcdb80ea59a362834554c4a5d3454e974957dbd2b30c3d00ad3f + languageName: node + linkType: hard + +"@babel/plugin-transform-literals@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-literals@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7291771c7626a27684053ceefc4e2e3e480a6ceab9f3c8abbdd9c90fcea63f035ace397e53bfc4b7311b835f7c79449be03226affa69e2e2a96c14b6da4d5db9 + languageName: node + linkType: hard + +"@babel/plugin-transform-member-expression-literals@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d5ed6cf840b9fd8b88f719dea46dc26a1778f10aeab6878b3eabf2350cfa813bfeff09d91c6afc93dd3536a48bc892a0afcf9f99f3bad6b54b41638f3ae80fa9 + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-amd@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-modules-amd@npm:7.16.0" + dependencies: + "@babel/helper-module-transforms": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + babel-plugin-dynamic-import-node: ^2.3.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c37ccb8cd7a301123fb5590712d957bf9f82bb0d89a83441b570a9f9793af76b99449c93f1079ad187fb598a5eeb5571561ff4d71af9192c7d6e407a464d6aff + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-commonjs@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.16.0" + dependencies: + "@babel/helper-module-transforms": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-simple-access": ^7.16.0 + babel-plugin-dynamic-import-node: ^2.3.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a7e43670f503b31d6ad42977ddefb7bffc23f700a24252859652aa03efd666698567b0817060dd6f84a6cd23e7aac7464bc0dc7f7f929cad212263abcac9d470 + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-systemjs@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.16.0" + dependencies: + "@babel/helper-hoist-variables": ^7.16.0 + "@babel/helper-module-transforms": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-validator-identifier": ^7.15.7 + babel-plugin-dynamic-import-node: ^2.3.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 4aa9bd45a4c1f79a4abd92482b4f9ac6492b5e727ee34316c80a30b6524281d39959a2d556b231eae4b1031f35e0133e60270f9e4bfa5f25a8cb68ef145dfcd2 + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-umd@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-modules-umd@npm:7.16.0" + dependencies: + "@babel/helper-module-transforms": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b07d41eae3a1163fdb2dca4bffb0de880981e6581163948a88b7665709e860612932f5a73e54d70057e834d3968e3b5f86222f1d302c9e1d34d95a764584af54 + languageName: node + linkType: hard + +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.16.0" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.16.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 758a87aca66ea7944c5f94ed7a798220c3b2986da4c38dc3f63221065ec96534bf39b3b043dd9759dbdff4026d340bbe51082d5ad4505c19b08893663130675b + languageName: node + linkType: hard + +"@babel/plugin-transform-new-target@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-new-target@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c741ba3e84c182f1af3174cb7f00c4e434080ff882e72c7b2743d1d636eebcf12c865772be051a323c823bd4ebdfbae19cb78e95218d6b14c338f27a64608e31 + languageName: node + linkType: hard + +"@babel/plugin-transform-object-super@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-object-super@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-replace-supers": ^7.16.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b6ed0a8f5a1231b4dadb5edb2cef8fba7957cbad943c0018002719d066fda93b805da961e42b38d625e43e7c79f5c07d5719d6d63f9cf178501882a4aa5d30da + languageName: node + linkType: hard + +"@babel/plugin-transform-parameters@npm:^7.16.0, @babel/plugin-transform-parameters@npm:^7.16.3": + version: 7.16.3 + resolution: "@babel/plugin-transform-parameters@npm:7.16.3" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7c0154fa66f03f69f6767adc01e72ef00d50cae8eb87c65506adccccc1cf776730ecbb96a5de0127910554cc0e86e375bc437fa085f619783d368936736a4f58 + languageName: node + linkType: hard + +"@babel/plugin-transform-property-literals@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-property-literals@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e9eb9355db4cf18dc82879174fc2de6590521afea04f1c80c5805d3f759bfa25946bcac1095b5fe0e4ad3f5eb330cd7e308467626a0212f07b9f41b9f00affa8 + languageName: node + linkType: hard + +"@babel/plugin-transform-regenerator@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-regenerator@npm:7.16.0" + dependencies: + regenerator-transform: ^0.14.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 32b1b43f8d55d9e78e87bbc6a19b0bb0ff968220e215e9a3984c0de140048c54c62cf46889bee16f987221eab112909318de391426df33cdbe3fd710480068f7 + languageName: node + linkType: hard + +"@babel/plugin-transform-reserved-words@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-reserved-words@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7a8288cfe2375e43579d3786d5f6654b36d8344b1be3df4fbafe81ae49bf634f85f68fe5a1a280f56aa7d626deaaa6ba89e586422b3d8b13f7d4b0e0617362d6 + languageName: node + linkType: hard + +"@babel/plugin-transform-shorthand-properties@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7ae0f218aaccd2f7e8b0027c558fbbc291f7df7c83749826075776de780d1ac421f9056c760c5eb2e486b7b1983a41cd8dc00589504904b833c810fdb80b3868 + languageName: node + linkType: hard + +"@babel/plugin-transform-spread@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-spread@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c295ef5e329fc31bd78e0aac3d6d848475a26e40cffff207dfd450416a25478bedb03402a0cc569bc5b7d3e92c22bff8a7cf76f1a9d896070e3cdeae1aee0316 + languageName: node + linkType: hard + +"@babel/plugin-transform-sticky-regex@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 80c7ccb797e4d31f112ace4614e8259ad0707eab3ed1c5a900ac0799dc23fded8bad57142ceb29222d6f0645f7b0d6a74fa133c945b8611d5db137b13ee68882 + languageName: node + linkType: hard + +"@babel/plugin-transform-template-literals@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-template-literals@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 230638ee56bbe8c4237d2c3366d700eca1f66f93c37935f6d775f699c5d2593e3f176e81010cfb2d46f89e340c6c042649263c3b913ce269182fadfb4db01369 + languageName: node + linkType: hard + +"@babel/plugin-transform-typeof-symbol@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 60e91d57b3e5a5ca02cebbf9f6dacd06e8a3b7c92c54fd60616f01ac1c79b3ec5fd2e8c5fa5c86ffcd9da6fa811e6de8dc7602cf1e05da17def0ea06f1e8548e + languageName: node + linkType: hard + +"@babel/plugin-transform-unicode-escapes@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 63ac80d6b7592a7a038cde0b7b8fd7fc8f478de107543fb20c0ee47e00c5cd4c12be936501f55e2fd9370056603d9c4e4c57cdf335674837475865f80b4ae734 + languageName: node + linkType: hard + +"@babel/plugin-transform-unicode-regex@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.16.0" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 61e498425fb44951067e1d17cd66e97777a340118c06943cee9d1032a8bfec661f262738a9b2a00a498b0ad5ba56551ea81e76f0d6afe46c0301abc3a86bee22 + languageName: node + linkType: hard + +"@babel/preset-env@npm:^7.15.4": + version: 7.16.4 + resolution: "@babel/preset-env@npm:7.16.4" + dependencies: + "@babel/compat-data": ^7.16.4 + "@babel/helper-compilation-targets": ^7.16.3 + "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-validator-option": ^7.14.5 + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.16.2 + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.16.0 + "@babel/plugin-proposal-async-generator-functions": ^7.16.4 + "@babel/plugin-proposal-class-properties": ^7.16.0 + "@babel/plugin-proposal-class-static-block": ^7.16.0 + "@babel/plugin-proposal-dynamic-import": ^7.16.0 + "@babel/plugin-proposal-export-namespace-from": ^7.16.0 + "@babel/plugin-proposal-json-strings": ^7.16.0 + "@babel/plugin-proposal-logical-assignment-operators": ^7.16.0 + "@babel/plugin-proposal-nullish-coalescing-operator": ^7.16.0 + "@babel/plugin-proposal-numeric-separator": ^7.16.0 + "@babel/plugin-proposal-object-rest-spread": ^7.16.0 + "@babel/plugin-proposal-optional-catch-binding": ^7.16.0 + "@babel/plugin-proposal-optional-chaining": ^7.16.0 + "@babel/plugin-proposal-private-methods": ^7.16.0 + "@babel/plugin-proposal-private-property-in-object": ^7.16.0 + "@babel/plugin-proposal-unicode-property-regex": ^7.16.0 + "@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-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-transform-arrow-functions": ^7.16.0 + "@babel/plugin-transform-async-to-generator": ^7.16.0 + "@babel/plugin-transform-block-scoped-functions": ^7.16.0 + "@babel/plugin-transform-block-scoping": ^7.16.0 + "@babel/plugin-transform-classes": ^7.16.0 + "@babel/plugin-transform-computed-properties": ^7.16.0 + "@babel/plugin-transform-destructuring": ^7.16.0 + "@babel/plugin-transform-dotall-regex": ^7.16.0 + "@babel/plugin-transform-duplicate-keys": ^7.16.0 + "@babel/plugin-transform-exponentiation-operator": ^7.16.0 + "@babel/plugin-transform-for-of": ^7.16.0 + "@babel/plugin-transform-function-name": ^7.16.0 + "@babel/plugin-transform-literals": ^7.16.0 + "@babel/plugin-transform-member-expression-literals": ^7.16.0 + "@babel/plugin-transform-modules-amd": ^7.16.0 + "@babel/plugin-transform-modules-commonjs": ^7.16.0 + "@babel/plugin-transform-modules-systemjs": ^7.16.0 + "@babel/plugin-transform-modules-umd": ^7.16.0 + "@babel/plugin-transform-named-capturing-groups-regex": ^7.16.0 + "@babel/plugin-transform-new-target": ^7.16.0 + "@babel/plugin-transform-object-super": ^7.16.0 + "@babel/plugin-transform-parameters": ^7.16.3 + "@babel/plugin-transform-property-literals": ^7.16.0 + "@babel/plugin-transform-regenerator": ^7.16.0 + "@babel/plugin-transform-reserved-words": ^7.16.0 + "@babel/plugin-transform-shorthand-properties": ^7.16.0 + "@babel/plugin-transform-spread": ^7.16.0 + "@babel/plugin-transform-sticky-regex": ^7.16.0 + "@babel/plugin-transform-template-literals": ^7.16.0 + "@babel/plugin-transform-typeof-symbol": ^7.16.0 + "@babel/plugin-transform-unicode-escapes": ^7.16.0 + "@babel/plugin-transform-unicode-regex": ^7.16.0 + "@babel/preset-modules": ^0.1.5 + "@babel/types": ^7.16.0 + babel-plugin-polyfill-corejs2: ^0.3.0 + babel-plugin-polyfill-corejs3: ^0.4.0 + babel-plugin-polyfill-regenerator: ^0.3.0 + core-js-compat: ^3.19.1 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 72a5d7e460fbaa2a90d6e341d8c33dcc2d742421fb983b61695ce46637e479808d09bec58a123a5e11732734a477cea8cb957aeefb101bb5723fca460566f034 + languageName: node + linkType: hard + +"@babel/preset-modules@npm:^0.1.5": + version: 0.1.5 + resolution: "@babel/preset-modules@npm:0.1.5" + dependencies: + "@babel/helper-plugin-utils": ^7.0.0 + "@babel/plugin-proposal-unicode-property-regex": ^7.4.4 + "@babel/plugin-transform-dotall-regex": ^7.4.4 + "@babel/types": ^7.4.4 + esutils: ^2.0.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8430e0e9e9d520b53e22e8c4c6a5a080a12b63af6eabe559c2310b187bd62ae113f3da82ba33e9d1d0f3230930ca702843aae9dd226dec51f7d7114dc1f51c10 + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.8.4": + version: 7.17.9 + resolution: "@babel/runtime@npm:7.17.9" + dependencies: + regenerator-runtime: ^0.13.4 + checksum: 4d56bdb82890f386d5a57c40ef985a0ed7f0a78f789377a2d0c3e8826819e0f7f16ba0fe906d9b2241c5f7ca56630ef0653f5bb99f03771f7b87ff8af4bf5fe3 + languageName: node + linkType: hard + +"@babel/template@npm:^7.16.0, @babel/template@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/template@npm:7.16.7" + dependencies: + "@babel/code-frame": ^7.16.7 + "@babel/parser": ^7.16.7 + "@babel/types": ^7.16.7 + checksum: 10cd112e89276e00f8b11b55a51c8b2f1262c318283a980f4d6cdb0286dc05734b9aaeeb9f3ad3311900b09bc913e02343fcaa9d4a4f413964aaab04eb84ac4a + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.16.0, @babel/traverse@npm:^7.16.3, @babel/traverse@npm:^7.7.0": + version: 7.17.3 + resolution: "@babel/traverse@npm:7.17.3" + dependencies: + "@babel/code-frame": ^7.16.7 + "@babel/generator": ^7.17.3 + "@babel/helper-environment-visitor": ^7.16.7 + "@babel/helper-function-name": ^7.16.7 + "@babel/helper-hoist-variables": ^7.16.7 + "@babel/helper-split-export-declaration": ^7.16.7 + "@babel/parser": ^7.17.3 + "@babel/types": ^7.17.0 + debug: ^4.1.0 + globals: ^11.1.0 + checksum: 780d7ecf711758174989794891af08d378f81febdb8932056c0d9979524bf0298e28f8e7708a872d7781151506c28f56c85c63ea3f1f654662c2fcb8a3eb9fdc + languageName: node + linkType: hard + +"@babel/types@npm:^7.16.0, @babel/types@npm:^7.16.7, @babel/types@npm:^7.17.0, @babel/types@npm:^7.4.4, @babel/types@npm:^7.7.0, @babel/types@npm:^7.8.3": + version: 7.17.0 + resolution: "@babel/types@npm:7.17.0" + dependencies: + "@babel/helper-validator-identifier": ^7.16.7 + to-fast-properties: ^2.0.0 + checksum: 12e5a287986fe557188e87b2c5202223f1dc83d9239a196ab936fdb9f8c1eb0be717ff19f934b5fad4e29a75586d5798f74bed209bccea1c20376b9952056f0e + languageName: node + linkType: hard + +"@cspotcode/source-map-consumer@npm:0.8.0": + version: 0.8.0 + resolution: "@cspotcode/source-map-consumer@npm:0.8.0" + checksum: c0c16ca3d2f58898f1bd74c4f41a189dbcc202e642e60e489cbcc2e52419c4e89bdead02c886a12fb13ea37798ede9e562b2321df997ebc210ae9bd881561b4e + languageName: node + linkType: hard + +"@cspotcode/source-map-support@npm:0.7.0": + version: 0.7.0 + resolution: "@cspotcode/source-map-support@npm:0.7.0" + dependencies: + "@cspotcode/source-map-consumer": 0.8.0 + checksum: 9faddda7757cd778b5fd6812137b2cc265810043680d6399acc20441668fafcdc874053be9dccd0d9110087287bfad27eb3bf342f72bceca9aa9059f5d0c4be8 + languageName: node + linkType: hard + +"@dabh/diagnostics@npm:^2.0.2": + version: 2.0.2 + resolution: "@dabh/diagnostics@npm:2.0.2" + dependencies: + colorspace: 1.1.x + enabled: 2.0.x + kuler: ^2.0.0 + checksum: 4d95cc31249a840b6cc3dba3dc4345a9295265413456068a0d07b69fa0ec6a5a5bc2c39e56ec04c6509ac1f4d9c17fc80baaaddd5caa1abcdd3aaeffe2b63cec + languageName: node + linkType: hard + +"@dashevo/abci@npm:~0.23.0-dev.1": + version: 0.23.0-dev.1 + resolution: "@dashevo/abci@npm:0.23.0-dev.1" + dependencies: + "@dashevo/protobufjs": 6.10.5 + bl: ^1.2.3 + protocol-buffers-encodings: ^1.1.0 + checksum: 6a7cf16ed9b460c027a4122980d37783e12ab34886b34a3b4a77d001824ebe15e87e5f2267898820e0b9e5d8caeb236f10460e9c771fce6117b6b14b6f7e9e4a + languageName: node + linkType: hard + +"@dashevo/bench-suite@workspace:packages/bench-suite": + version: 0.0.0-use.local + resolution: "@dashevo/bench-suite@workspace:packages/bench-suite" + dependencies: + "@dashevo/dashcore-lib": ~0.19.39 + "@dashevo/dpns-contract": "workspace:~" + "@dashevo/dpp": "workspace:~" + "@dashevo/wallet-lib": "workspace:~" + babel-eslint: ^10.1.0 + console-table-printer: ^2.11.0 + dash: "workspace:~" + dotenv-safe: ^8.2.0 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + lodash.clone: ~4.5.0 + lodash.matches: ^4.6.0 + mathjs: ^10.4.3 + mocha: ^9.1.2 + languageName: unknown + linkType: soft + +"@dashevo/dapi-client@workspace:packages/js-dapi-client, @dashevo/dapi-client@workspace:~": + version: 0.0.0-use.local + resolution: "@dashevo/dapi-client@workspace:packages/js-dapi-client" + dependencies: + "@babel/core": ^7.15.5 + "@dashevo/dapi-grpc": "workspace:~" + "@dashevo/dash-spv": "workspace:~" + "@dashevo/dashcore-lib": ~0.19.39 + "@dashevo/dpp": "workspace:~" + "@dashevo/grpc-common": "workspace:~" + "@grpc/grpc-js": ^1.3.7 + assert-browserify: ^2.0.0 + axios: ^0.21.1 + babel-loader: ^8.2.2 + bs58: ^4.0.1 + buffer: ^6.0.3 + cbor: ^8.0.0 + chai: ^4.3.4 + chai-as-promised: ^7.1.1 + comment-parser: ^0.7.6 + core-js: ^3.17.2 + crypto-browserify: ^3.12.0 + dirty-chai: ^2.0.1 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + eslint-plugin-jsdoc: ^27.0.0 + events: ^3.3.0 + karma: ^6.3.4 + karma-chai: ^0.1.0 + karma-chrome-launcher: ^3.1.0 + karma-firefox-launcher: ^2.1.1 + karma-mocha: ^2.0.1 + karma-mocha-reporter: ^2.2.5 + karma-webpack: ^5.0.0 + lodash.sample: ^4.2.1 + mocha: ^9.1.2 + node-inspect-extracted: ^1.0.8 + nyc: ^15.1.0 + path-browserify: ^1.0.1 + process: ^0.11.10 + sinon: ^11.1.2 + sinon-chai: ^3.7.0 + stream-browserify: ^3.0.0 + string_decoder: ^1.3.0 + url: ^0.11.0 + util: ^0.12.4 + webpack: ^5.59.1 + webpack-cli: ^4.9.1 + languageName: unknown + linkType: soft + +"@dashevo/dapi-grpc@workspace:packages/dapi-grpc, @dashevo/dapi-grpc@workspace:~": + version: 0.0.0-use.local + resolution: "@dashevo/dapi-grpc@workspace:packages/dapi-grpc" + dependencies: + "@dashevo/grpc-common": "workspace:~" + "@dashevo/protobufjs": 6.10.5 + "@grpc/grpc-js": ^1.3.7 + chai: ^4.3.4 + chai-as-promised: ^7.1.1 + dirty-chai: ^2.0.1 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + google-protobuf: ^3.12.2 + grpc-web: 1.2.1 + long: ^5.2.0 + mocha: ^9.1.2 + mocha-sinon: ^2.1.2 + sinon: ^11.1.2 + sinon-chai: ^3.7.0 + languageName: unknown + linkType: soft + +"@dashevo/dapi@workspace:packages/dapi": + version: 0.0.0-use.local + resolution: "@dashevo/dapi@workspace:packages/dapi" + dependencies: + "@dashevo/dapi-client": "workspace:~" + "@dashevo/dapi-grpc": "workspace:~" + "@dashevo/dashcore-lib": ~0.19.39 + "@dashevo/dashd-rpc": ^2.3.2 + "@dashevo/dp-services-ctl": "github:dashevo/js-dp-services-ctl#v0.19-dev" + "@dashevo/dpp": "workspace:~" + "@dashevo/grpc-common": "workspace:~" + "@grpc/grpc-js": ^1.3.7 + ajv: ^8.6.0 + bs58: ^4.0.1 + cbor: ^8.0.0 + chai: ^4.3.4 + chai-as-promised: ^7.1.1 + dirty-chai: ^2.0.1 + dotenv: ^8.6.0 + dotenv-expand: ^5.1.0 + dotenv-safe: ^8.2.0 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + jayson: ^3.3.4 + lodash: ^4.17.19 + lru-cache: ^5.1.1 + mocha: ^9.1.2 + mocha-sinon: ^2.1.2 + nyc: ^15.1.0 + request: ^2.87.0 + request-promise-native: ^1.0.5 + semver: ^7.3.2 + sinon: ^11.1.2 + sinon-chai: ^3.7.0 + swagger-jsdoc: ^3.5.0 + ws: ^7.5.3 + zeromq: ^5.2.8 + languageName: unknown + linkType: soft + +"@dashevo/dark-gravity-wave@npm:^1.1.1": + version: 1.1.1 + resolution: "@dashevo/dark-gravity-wave@npm:1.1.1" + checksum: 4f2f0bddfa339bafe38248970b8747e6e002499fbaffb69953d19565de84c9c054a442bd6f950240927023122fd2e74fc7663269c97bba138f83d77053dec6c4 + languageName: node + linkType: hard + +"@dashevo/dash-spv@workspace:packages/dash-spv, @dashevo/dash-spv@workspace:~": + version: 0.0.0-use.local + resolution: "@dashevo/dash-spv@workspace:packages/dash-spv" + dependencies: + "@dashevo/dark-gravity-wave": ^1.1.1 + "@dashevo/dash-util": ^2.0.3 + "@dashevo/dashcore-lib": ~0.19.39 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + levelup: ^4.4.0 + memdown: ^5.1.0 + mocha: ^9.1.2 + should: ^13.2.3 + languageName: unknown + linkType: soft + +"@dashevo/dash-util@npm:^2.0.3": + version: 2.0.3 + resolution: "@dashevo/dash-util@npm:2.0.3" + dependencies: + bn.js: ^4.6.4 + buffer-reverse: ^1.0.1 + checksum: ef93e629e98dfe4203cd5f20a2cca6a80706e3a19ddbcf93e6c5eb9c740ac3aab6e2d5427484a75e2ab3cd0161871ef1697b305ff9f6c68c0ca27762e72ba65a + languageName: node + linkType: hard + +"@dashevo/dashcore-lib@npm:~0.19.30, @dashevo/dashcore-lib@npm:~0.19.39": + version: 0.19.39 + resolution: "@dashevo/dashcore-lib@npm:0.19.39" + dependencies: + "@dashevo/x11-hash-js": ^1.0.2 + "@types/node": ^12.12.47 + bloom-filter: ^0.2.0 + bls-signatures: ^0.2.5 + bn.js: =4.11.8 + bs58: =4.0.1 + elliptic: 6.5.3 + eslint-config-prettier: ^8.3.0 + inherits: =2.0.1 + lodash: ^4.17.20 + unorm: ^1.6.0 + checksum: e441cf46a95bc777d9b5d966d05cbf423bea1d8a6721493f94973f7d500c870fc2f55f20cc5feecfb6784bf8617b4342e4cade784ece3cad2263510dfc7a440d + languageName: node + linkType: hard + +"@dashevo/dashd-rpc@npm:^2.3.0, @dashevo/dashd-rpc@npm:^2.3.2": + version: 2.3.2 + resolution: "@dashevo/dashd-rpc@npm:2.3.2" + dependencies: + async: ^3.2.0 + bluebird: ^3.7.2 + checksum: 56ff41d695b4ce44922228ee74cdeea83de212d0b75ba6b6f400ad363a12f004528b18d1377b231ffb020f79a7fa6c9f1a911f9ae1eb4f79b5ad16dade4672ee + languageName: node + linkType: hard + +"@dashevo/dashpay-contract@npm:~0.22.1": + version: 0.22.1 + resolution: "@dashevo/dashpay-contract@npm:0.22.1" + checksum: cff4700aaf362d72e4888ae8e2f794068451a6f8dd55890a453e4e503f92d93d089bb19860ddad455408bcbf3cca72282c6dc4dfc45de8a6380637cdf613f6b1 + languageName: node + linkType: hard + +"@dashevo/dashpay-contract@workspace:packages/dashpay-contract, @dashevo/dashpay-contract@workspace:~": + version: 0.0.0-use.local + resolution: "@dashevo/dashpay-contract@workspace:packages/dashpay-contract" + dependencies: + "@dashevo/dpp": "workspace:~" + chai: ^4.3.4 + dirty-chai: ^2.0.1 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + mocha: ^9.1.2 + sinon: ^11.1.2 + sinon-chai: ^3.7.0 + languageName: unknown + linkType: soft + +"@dashevo/docker-compose@npm:^0.24.1": + version: 0.24.1 + resolution: "@dashevo/docker-compose@npm:0.24.1" + dependencies: + yaml: ^1.10.2 + checksum: 7792e09b5d2d216eb883d0fe2d54e422a4cf277e17af99dd108078ca41aab9b66eccf9eddb2716e752b6cfe183a6c45d2292b42cce881c658afdcb6cd0d5ca3a + languageName: node + linkType: hard + +"@dashevo/dp-services-ctl@github:dashevo/js-dp-services-ctl#v0.19-dev": + version: 0.19.0-dev.1 + resolution: "@dashevo/dp-services-ctl@https://github.com/dashevo/js-dp-services-ctl.git#commit=3976076b0018c5b4632ceda4c752fc597f27a640" + dependencies: + "@dashevo/dashd-rpc": ^2.3.0 + dockerode: ^3.2.1 + jayson: ^2.1.0 + lodash: ^4.17.19 + mongodb: ^3.3.4 + checksum: 0325823966c10163bf2d4ca5b2899294740f9657e958d032e1c513a87d75b1e1963cb6fe1a29edc886ca06dbcd2db25838b5ed561dba1f8eadb40cd33f3fe99c + languageName: node + linkType: hard + +"@dashevo/dpns-contract@npm:~0.22.1": + version: 0.22.1 + resolution: "@dashevo/dpns-contract@npm:0.22.1" + checksum: d96f91f8ee156d0effff71329a8d081435c39730eb7800915f5ec13a79ad4667de95c48cbb14fb12fa73f0b1b377c6225eacfbe48cb5aa5d9a1cc433fc87a6b9 + languageName: node + linkType: hard + +"@dashevo/dpns-contract@workspace:packages/dpns-contract, @dashevo/dpns-contract@workspace:~": + version: 0.0.0-use.local + resolution: "@dashevo/dpns-contract@workspace:packages/dpns-contract" + dependencies: + "@dashevo/dpp": "workspace:~" + chai: ^4.3.4 + dirty-chai: ^2.0.1 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + mocha: ^9.1.2 + sinon: ^11.1.2 + sinon-chai: ^3.7.0 + languageName: unknown + linkType: soft + +"@dashevo/dpp@npm:~0.22.0-dev.7": + version: 0.22.1 + resolution: "@dashevo/dpp@npm:0.22.1" + dependencies: + "@apidevtools/json-schema-ref-parser": ^8.0.0 + "@dashevo/dashcore-lib": ~0.19.30 + "@dashevo/dashpay-contract": ~0.22.1 + "@dashevo/dpns-contract": ~0.22.1 + "@dashevo/feature-flags-contract": ~0.22.1 + "@dashevo/masternode-reward-shares-contract": ~0.22.1 + "@dashevo/wasm-re2": ~1.0.2 + ajv: ^8.6.0 + ajv-formats: ^2.1.1 + bignumber.js: ^9.0.1 + bls-signatures: ^0.2.5 + bs58: ^4.0.1 + cbor: ^8.0.0 + fast-json-patch: ^3.1.0 + json-schema-diff-validator: ^0.4.1 + json-schema-traverse: ^1.0.0 + lodash.clonedeep: ^4.5.0 + lodash.clonedeepwith: ^4.5.0 + lodash.get: ^4.4.2 + lodash.set: ^4.3.2 + long: ^5.2.0 + checksum: ae3d2e1c9fda59699a2a9e67ffccba967f8ea804fc5775853b1e20510b7c9af86a8964a81a830c92dbc68d2dabb5c06bbea673df2df7c6883bdcb2211bba20ce + languageName: node + linkType: hard + +"@dashevo/dpp@workspace:packages/js-dpp, @dashevo/dpp@workspace:~": + version: 0.0.0-use.local + resolution: "@dashevo/dpp@workspace:packages/js-dpp" + dependencies: + "@apidevtools/json-schema-ref-parser": ^8.0.0 + "@babel/core": ^7.15.5 + "@babel/preset-env": ^7.15.4 + "@dashevo/dashcore-lib": ~0.19.39 + "@dashevo/dashpay-contract": "workspace:~" + "@dashevo/dpns-contract": "workspace:~" + "@dashevo/feature-flags-contract": "workspace:~" + "@dashevo/masternode-reward-shares-contract": "workspace:~" + "@dashevo/wasm-re2": ~1.0.2 + acorn: ^8.5.0 + ajv: ^8.6.0 + ajv-formats: ^2.1.1 + assert: ^2.0.0 + babel-loader: ^8.2.2 + bignumber.js: ^9.0.1 + bls-signatures: ^0.2.5 + bs58: ^4.0.1 + buffer: ^6.0.3 + cbor: ^8.0.0 + chai: ^4.3.4 + chai-as-promised: ^7.1.1 + chai-exclude: ^2.1.0 + chai-string: ^1.5.0 + core-js: ^3.17.2 + crypto-browserify: ^3.12.0 + dirty-chai: ^2.0.1 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + events: ^3.3.0 + fast-json-patch: ^3.1.0 + https-browserify: ^1.0.0 + json-schema-diff-validator: ^0.4.1 + json-schema-traverse: ^1.0.0 + karma: ^6.3.4 + karma-chai: ^0.1.0 + karma-chrome-launcher: ^3.1.0 + karma-firefox-launcher: ^2.1.1 + karma-mocha: ^2.0.1 + karma-mocha-reporter: ^2.2.5 + karma-webpack: ^5.0.0 + lodash.clonedeep: ^4.5.0 + lodash.clonedeepwith: ^4.5.0 + lodash.get: ^4.4.2 + lodash.set: ^4.3.2 + long: ^5.2.0 + mocha: ^9.1.2 + node-inspect-extracted: ^1.0.8 + nyc: ^15.1.0 + path-browserify: ^1.0.1 + process: ^0.11.10 + sinon: ^11.1.2 + sinon-chai: ^3.7.0 + stream-browserify: ^3.0.0 + stream-http: ^3.2.0 + string_decoder: ^1.3.0 + url: ^0.11.0 + util: ^0.12.4 + webpack: ^5.59.1 + webpack-cli: ^4.9.1 + languageName: unknown + linkType: soft + +"@dashevo/drive@workspace:packages/js-drive": + version: 0.0.0-use.local + resolution: "@dashevo/drive@workspace:packages/js-drive" + dependencies: + "@dashevo/abci": ~0.23.0-dev.1 + "@dashevo/dapi-grpc": "workspace:~" + "@dashevo/dashcore-lib": ~0.19.39 + "@dashevo/dashd-rpc": ^2.3.2 + "@dashevo/dashpay-contract": "workspace:~" + "@dashevo/dp-services-ctl": "github:dashevo/js-dp-services-ctl#v0.19-dev" + "@dashevo/dpns-contract": "workspace:~" + "@dashevo/dpp": "workspace:~" + "@dashevo/feature-flags-contract": "workspace:~" + "@dashevo/grpc-common": "workspace:~" + "@dashevo/masternode-reward-shares-contract": "workspace:~" + "@dashevo/rs-drive": 0.23.0-dev.5.pr.114.5 + "@types/pino": ^6.3.0 + ajv: ^8.6.0 + ajv-keywords: ^5.0.0 + awilix: ^4.2.6 + babel-eslint: ^10.1.0 + blake3: ^2.1.4 + browserify: ^16.5.1 + bs58: ^4.0.1 + cbor: ^8.0.0 + chai: ^4.3.4 + chai-as-promised: ^7.1.1 + chai-string: ^1.5.0 + chalk: ^4.1.0 + dirty-chai: ^2.0.1 + dotenv-expand: ^5.1.0 + dotenv-safe: ^8.2.0 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + find-my-way: ^2.2.2 + js-merkle: ^0.1.5 + levelup: ^4.4.0 + lodash.clonedeep: ^4.5.0 + lodash.get: ^4.4.2 + lodash.set: ^4.3.2 + long: ^5.2.0 + lru-cache: ^5.1.1 + memdown: ^5.1.0 + mocha: ^9.1.2 + node-graceful: ^3.0.1 + nyc: ^15.1.0 + pino: ^6.4.0 + pino-multi-stream: ^5.2.0 + pino-pretty: ^4.0.3 + rimraf: ^3.0.2 + setimmediate: ^1.0.5 + sinon: ^11.1.2 + sinon-chai: ^3.7.0 + through2: ^3.0.1 + zeromq: ^5.2.8 + languageName: unknown + linkType: soft + +"@dashevo/feature-flags-contract@npm:~0.22.1": + version: 0.22.1 + resolution: "@dashevo/feature-flags-contract@npm:0.22.1" + checksum: 6590ea68bb00ddb648d0b712ab8d573ce1c0abf974dd5b0355e464ad5c98fbe976355c6510bb812809078e24857d102225e073e1ae555d7bacbe18d9d388d41f + languageName: node + linkType: hard + +"@dashevo/feature-flags-contract@workspace:packages/feature-flags-contract, @dashevo/feature-flags-contract@workspace:~": + version: 0.0.0-use.local + resolution: "@dashevo/feature-flags-contract@workspace:packages/feature-flags-contract" + dependencies: + "@dashevo/dpp": "workspace:~" + chai: ^4.3.4 + dirty-chai: ^2.0.1 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + mocha: ^9.1.2 + sinon: ^11.1.2 + sinon-chai: ^3.7.0 + languageName: unknown + linkType: soft + +"@dashevo/grpc-common@workspace:packages/js-grpc-common, @dashevo/grpc-common@workspace:~": + version: 0.0.0-use.local + resolution: "@dashevo/grpc-common@workspace:packages/js-grpc-common" + dependencies: + "@dashevo/protobufjs": 6.10.5 + "@grpc/grpc-js": ^1.3.7 + "@grpc/proto-loader": ^0.5.2 + cbor: ^8.0.0 + chai: ^4.3.4 + chai-as-promised: ^7.1.1 + dirty-chai: ^2.0.1 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + lodash.get: ^4.4.2 + long: ^5.2.0 + mocha: ^9.1.2 + mocha-sinon: ^2.1.2 + nyc: ^15.1.0 + semver: ^7.3.2 + sinon: ^11.1.2 + sinon-chai: ^3.7.0 + languageName: unknown + linkType: soft + +"@dashevo/masternode-reward-shares-contract@npm:~0.22.1": + version: 0.22.1 + resolution: "@dashevo/masternode-reward-shares-contract@npm:0.22.1" + checksum: 577a40876be092cd27564a794e06676ca188cf9fa555a6daaa6f0ab18713ad55f2f4fc6b759e3958e514cade4ec50747bac3d47d3a1075ddb1fad4355317f389 + languageName: node + linkType: hard + +"@dashevo/masternode-reward-shares-contract@workspace:packages/masternode-reward-shares-contract, @dashevo/masternode-reward-shares-contract@workspace:~": + version: 0.0.0-use.local + resolution: "@dashevo/masternode-reward-shares-contract@workspace:packages/masternode-reward-shares-contract" + dependencies: + "@dashevo/dpp": "workspace:~" + chai: ^4.3.4 + dirty-chai: ^2.0.1 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + mocha: ^9.1.2 + sinon: ^11.1.2 + sinon-chai: ^3.7.0 + languageName: unknown + linkType: soft + +"@dashevo/merk@github:dashevo/node-merk#eb37003300d22c6c04604463bcd7e861dd07000f": + version: 2.1.3 + resolution: "@dashevo/merk@https://github.com/dashevo/node-merk.git#commit=eb37003300d22c6c04604463bcd7e861dd07000f" + dependencies: + neon-load-or-build: ^2.2.2 + checksum: 1e056bbd02fc3fc459efd85169bb35b4e33435137c0f4eb2e1fa993a1e913d83d662f133884889cd1cd2ab410009c9359429df769f4f2f3caa4e5f22fb8c1a04 + languageName: node + linkType: hard + +"@dashevo/platform-test-suite@workspace:packages/platform-test-suite": + version: 0.0.0-use.local + resolution: "@dashevo/platform-test-suite@workspace:packages/platform-test-suite" + dependencies: + "@dashevo/dapi-client": "workspace:~" + "@dashevo/dashcore-lib": ~0.19.39 + "@dashevo/dpns-contract": "workspace:~" + "@dashevo/dpp": "workspace:~" + "@dashevo/feature-flags-contract": "workspace:~" + "@dashevo/grpc-common": "workspace:~" + "@dashevo/masternode-reward-shares-contract": "workspace:~" + "@dashevo/merk": "github:dashevo/node-merk#eb37003300d22c6c04604463bcd7e861dd07000f" + "@dashevo/wallet-lib": "workspace:~" + assert: ^2.0.0 + assert-browserify: ^2.0.0 + blake3: ^2.1.4 + browserify-zlib: ^0.2.0 + buffer: ^6.0.3 + bufferutil: ^4.0.6 + chai: ^4.3.4 + chai-as-promised: ^7.1.1 + crypto-browserify: ^3.12.0 + dash: "workspace:~" + dirty-chai: ^2.0.1 + dotenv-safe: ^8.2.0 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + events: ^3.3.0 + github-api: ^3.3.0 + https-browserify: ^1.0.0 + js-merkle: ^0.1.5 + karma: ^6.3.4 + karma-chai: ^0.1.0 + karma-chrome-launcher: ^3.1.0 + karma-firefox-launcher: ^2.1.1 + karma-mocha: ^2.0.1 + karma-mocha-reporter: ^2.2.5 + karma-sourcemap-loader: ^0.3.7 + karma-webpack: ^5.0.0 + localforage: ^1.10.0 + mocha: ^9.1.2 + net: ^1.0.2 + nodeforage: ^1.1.2 + os-browserify: ^0.3.0 + path-browserify: ^1.0.1 + process: ^0.11.10 + semver: ^7.3.2 + sinon: ^11.1.2 + sinon-chai: ^3.7.0 + stream-browserify: ^3.0.0 + stream-http: ^3.2.0 + string_decoder: ^1.3.0 + tls: ^0.0.1 + url: ^0.11.0 + utf-8-validate: ^5.0.9 + util: ^0.12.4 + webpack: ^5.59.1 + ws: ^7.5.3 + languageName: unknown + linkType: soft + +"@dashevo/platform@workspace:.": + version: 0.0.0-use.local + resolution: "@dashevo/platform@workspace:." + dependencies: + add-stream: ^1.0.0 + conventional-changelog: ^3.1.24 + conventional-changelog-dash: "github:dashevo/conventional-changelog-dash" + semver: ^7.3.2 + tempfile: ^3.0.0 + ultra-runner: ^3.10.5 + languageName: unknown + linkType: soft + +"@dashevo/protobufjs@npm:6.10.5": + version: 6.10.5 + resolution: "@dashevo/protobufjs@npm:6.10.5" + dependencies: + "@protobufjs/aspromise": ^1.1.2 + "@protobufjs/base64": ^1.1.2 + "@protobufjs/codegen": ^2.0.4 + "@protobufjs/eventemitter": ^1.1.0 + "@protobufjs/fetch": ^1.1.0 + "@protobufjs/float": ^1.0.2 + "@protobufjs/inquire": ^1.1.0 + "@protobufjs/path": ^1.1.2 + "@protobufjs/pool": ^1.1.0 + "@protobufjs/utf8": ^1.1.0 + "@types/long": ^4.0.1 + "@types/node": ^13.7.0 + long: ^4.0.0 + bin: + pbjs: bin/pbjs + pbts: bin/pbts + checksum: 212838566371568ecdabb71e4f5395bf7dcf10703335e827d8bc30d9b2d1c901f5b2963b126550fe83ec70377b9f2065c1e1b943cda32ef19442978eebb50805 + languageName: node + linkType: hard + +"@dashevo/rs-drive@npm:0.23.0-dev.5.pr.114.5": + version: 0.23.0-dev.5.pr.114.5 + resolution: "@dashevo/rs-drive@npm:0.23.0-dev.5.pr.114.5" + dependencies: + "@dashevo/dpp": ~0.22.0-dev.7 + cargo-cp-artifact: ^0.1.6 + cbor: ^8.1.0 + neon-load-or-build: ^2.2.2 + neon-tag-prebuild: "github:shumkov/neon-tag-prebuild#patch-1" + checksum: 000ba38e338ff743f4ff459810a2397c226dc56267786e95401ed6c3f28d00e79090afe8132ea92603d76774d3aba295f4d3fc96c2bc2a25ec9180e3246767e6 + languageName: node + linkType: hard + +"@dashevo/wallet-lib@workspace:packages/wallet-lib, @dashevo/wallet-lib@workspace:~": + version: 0.0.0-use.local + resolution: "@dashevo/wallet-lib@workspace:packages/wallet-lib" + dependencies: + "@dashevo/dapi-client": "workspace:~" + "@dashevo/dashcore-lib": ~0.19.39 + "@dashevo/dpp": "workspace:~" + "@dashevo/grpc-common": "workspace:~" + assert: ^2.0.0 + browserify-zlib: ^0.2.0 + buffer: ^6.0.3 + cbor: ^8.0.0 + chai: ^4.3.4 + chai-as-promised: ^7.1.1 + crypto-browserify: ^3.12.0 + crypto-js: ^4.0.0 + dotenv-safe: ^8.2.0 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + events: ^3.3.0 + https-browserify: ^1.0.0 + karma: ^6.3.4 + karma-chai: ^0.1.0 + karma-chrome-launcher: ^3.1.0 + karma-mocha: ^2.0.1 + karma-mocha-reporter: ^2.2.5 + karma-sourcemap-loader: ^0.3.7 + karma-webpack: ^5.0.0 + lodash: ^4.17.19 + mocha: ^9.1.2 + node-inspect-extracted: ^1.0.8 + nyc: ^15.1.0 + os-browserify: ^0.3.0 + path-browserify: ^1.0.1 + pbkdf2: ^3.1.1 + process: ^0.11.10 + setimmediate: ^1.0.5 + sinon: ^11.1.2 + sinon-chai: ^3.7.0 + stream-browserify: ^3.0.0 + stream-http: ^3.2.0 + string_decoder: ^1.3.0 + url: ^0.11.0 + util: ^0.12.4 + webpack: ^5.59.1 + webpack-cli: ^4.9.1 + winston: ^3.2.1 + languageName: unknown + linkType: soft + +"@dashevo/wasm-re2@npm:~1.0.2": + version: 1.0.2 + resolution: "@dashevo/wasm-re2@npm:1.0.2" + checksum: 3d54788e4e133e0677e60c125550a1f81cc5d9ed742e7dfec936e4da7653bb17a43054db3fc1287074efdb05ebbef7625d19f1f3286662f135218fadc27e6818 + languageName: node + linkType: hard + +"@dashevo/x11-hash-js@npm:^1.0.2": + version: 1.0.2 + resolution: "@dashevo/x11-hash-js@npm:1.0.2" + checksum: a4856fb50f3d171d65492e1e72ef8cde2ea4e8a966fe8fc90d452db2e1a75e09a34e64918432ef9b207790fe51195b05b472c166ab809cf1bf8d3f69e0f57298 + languageName: node + linkType: hard + +"@discoveryjs/json-ext@npm:^0.5.0": + version: 0.5.5 + resolution: "@discoveryjs/json-ext@npm:0.5.5" + checksum: 40844548d87689d742a098c3bfe342cc7f0d0500a814fce4592886de68f7e027937938324578311998d49a1f1e5d0394c578bb814fab04375b521637cb7a0dea + languageName: node + linkType: hard + +"@eslint/eslintrc@npm:^0.4.3": + version: 0.4.3 + resolution: "@eslint/eslintrc@npm:0.4.3" + dependencies: + ajv: ^6.12.4 + debug: ^4.1.1 + espree: ^7.3.0 + globals: ^13.9.0 + ignore: ^4.0.6 + import-fresh: ^3.2.1 + js-yaml: ^3.13.1 + minimatch: ^3.0.4 + strip-json-comments: ^3.1.1 + checksum: 03a7704150b868c318aab6a94d87a33d30dc2ec579d27374575014f06237ba1370ae11178db772f985ef680d469dc237e7b16a1c5d8edaaeb8c3733e7a95a6d3 + languageName: node + linkType: hard + +"@gar/promisify@npm:^1.0.1": + version: 1.1.2 + resolution: "@gar/promisify@npm:1.1.2" + checksum: d05081e0887a49c178b75ee3067bd6ee086f73c154d121b854fb2e044e8a89cb1cbb6de3a0dd93a519b80f0531fda68b099dd7256205f7fbb3490324342f2217 + languageName: node + linkType: hard + +"@grpc/grpc-js@npm:^1.3.7": + version: 1.4.4 + resolution: "@grpc/grpc-js@npm:1.4.4" + dependencies: + "@grpc/proto-loader": ^0.6.4 + "@types/node": ">=12.12.47" + checksum: f9be710ceff8e14865718dbead97c85f3457889ae01c4d94f1338bb19dfe19c92549046b3ea2f0df31360363c39322031dfff0189df5798e7b8452016afcce6e + languageName: node + linkType: hard + +"@grpc/proto-loader@npm:^0.5.2": + version: 0.5.6 + resolution: "@grpc/proto-loader@npm:0.5.6" + dependencies: + lodash.camelcase: ^4.3.0 + protobufjs: ^6.8.6 + checksum: 13fe76d84ab1a516f3dc47d06df4dd682f6f1515a7a4aa3f8cddcc8f8256f33cbf529bd0b6729946f548f7459acfcd9b5b026c10572e21d40213a358115658b5 + languageName: node + linkType: hard + +"@grpc/proto-loader@npm:^0.6.4": + version: 0.6.7 + resolution: "@grpc/proto-loader@npm:0.6.7" + dependencies: + "@types/long": ^4.0.1 + lodash.camelcase: ^4.3.0 + long: ^4.0.0 + protobufjs: ^6.10.0 + yargs: ^16.1.1 + bin: + proto-loader-gen-types: build/bin/proto-loader-gen-types.js + checksum: af1909ec3697cb3b6ba4eac47c5ecd3dcbfd28e3d95b2a8b0e8cc84edb33e9ec7ed75efbb9f5ec479712ed3c1f23638f2dc5130a28e7da388a59f54ef158ba9c + languageName: node + linkType: hard + +"@hapi/bourne@npm:^2.0.0": + version: 2.0.0 + resolution: "@hapi/bourne@npm:2.0.0" + checksum: 2ea0922101d3fecec43428194c72c5dbe0be908dd7ad07347879dc720820ac410ead79a4c349a2e1726e8af062464160c6d32b6566bbc4c60865923f9d7dd006 + languageName: node + linkType: hard + +"@humanwhocodes/config-array@npm:^0.5.0": + version: 0.5.0 + resolution: "@humanwhocodes/config-array@npm:0.5.0" + dependencies: + "@humanwhocodes/object-schema": ^1.2.0 + debug: ^4.1.1 + minimatch: ^3.0.4 + checksum: 44ee6a9f05d93dd9d5935a006b17572328ba9caff8002442f601736cbda79c580cc0f5a49ce9eb88fbacc5c3a6b62098357c2e95326cd17bb9f1a6c61d6e95e7 + languageName: node + linkType: hard + +"@humanwhocodes/object-schema@npm:^1.2.0": + version: 1.2.1 + resolution: "@humanwhocodes/object-schema@npm:1.2.1" + checksum: a824a1ec31591231e4bad5787641f59e9633827d0a2eaae131a288d33c9ef0290bd16fda8da6f7c0fcb014147865d12118df10db57f27f41e20da92369fcb3f1 + languageName: node + linkType: hard + +"@hutson/parse-repository-url@npm:^3.0.0": + version: 3.0.2 + resolution: "@hutson/parse-repository-url@npm:3.0.2" + checksum: 39992c5f183c5ca3d761d6ed9dfabcb79b5f3750bf1b7f3532e1dc439ca370138bbd426ee250fdaba460bc948e6761fbefd484b8f4f36885d71ded96138340d1 + languageName: node + linkType: hard + +"@isaacs/string-locale-compare@npm:^1.1.0": + version: 1.1.0 + resolution: "@isaacs/string-locale-compare@npm:1.1.0" + checksum: 7287da5d11497b82c542d3c2abe534808015be4f4883e71c26853277b5456f6bbe4108535db847a29f385ad6dc9318ffb0f55ee79bb5f39993233d7dccf8751d + languageName: node + linkType: hard + +"@istanbuljs/load-nyc-config@npm:^1.0.0": + version: 1.1.0 + resolution: "@istanbuljs/load-nyc-config@npm:1.1.0" + 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 + checksum: d578da5e2e804d5c93228450a1380e1a3c691de4953acc162f387b717258512a3e07b83510a936d9fab03eac90817473917e24f5d16297af3867f59328d58568 + languageName: node + linkType: hard + +"@istanbuljs/schema@npm:^0.1.2": + version: 0.1.3 + resolution: "@istanbuljs/schema@npm:0.1.3" + checksum: 5282759d961d61350f33d9118d16bcaed914ebf8061a52f4fa474b2cb08720c9c81d165e13b82f2e5a8a212cc5af482f0c6fc1ac27b9e067e5394c9a6ed186c9 + languageName: node + linkType: hard + +"@jest/types@npm:^27.2.5": + version: 27.2.5 + resolution: "@jest/types@npm:27.2.5" + dependencies: + "@types/istanbul-lib-coverage": ^2.0.0 + "@types/istanbul-reports": ^3.0.0 + "@types/node": "*" + "@types/yargs": ^16.0.0 + chalk: ^4.0.0 + checksum: 322603c24354a5333b5b7a670464422a46e0244a5a96a35552a7018eb4ac2e84c3b7657336b0ea6aa114963f9b6d0da8b8f6f963cb044fea9e7bc04d464b0ab1 + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.0.3": + version: 3.0.8 + resolution: "@jridgewell/resolve-uri@npm:3.0.8" + checksum: 28d739f49b4a52a95843b15669dcb2daaab48f0eaef8f457b9aacd0bdebeb60468d0684f73244f613b786e9d871c25abdbe6f55991bba36814cdadc399dbb3a8 + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.10": + version: 1.4.14 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" + checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.7": + version: 0.3.14 + resolution: "@jridgewell/trace-mapping@npm:0.3.14" + dependencies: + "@jridgewell/resolve-uri": ^3.0.3 + "@jridgewell/sourcemap-codec": ^1.4.10 + checksum: b9537b9630ffb631aef9651a085fe361881cde1772cd482c257fe3c78c8fd5388d681f504a9c9fe1081b1c05e8f75edf55ee10fdb58d92bbaa8dbf6a7bd6b18c + languageName: node + linkType: hard + +"@jsdevtools/ono@npm:^7.1.0": + version: 7.1.3 + resolution: "@jsdevtools/ono@npm:7.1.3" + checksum: 2297fcd472ba810bffe8519d2249171132844c7174f3a16634f9260761c8c78bc0428a4190b5b6d72d45673c13918ab9844d706c3ed4ef8f62ab11a2627a08ad + languageName: node + linkType: hard + +"@leichtgewicht/ip-codec@npm:^2.0.1": + version: 2.0.3 + resolution: "@leichtgewicht/ip-codec@npm:2.0.3" + checksum: 5b6bee0481c82ac05c748322e34ac68aa01757451b4f49f1ab9cc91e420a1ea4cd0fc4678251e6fa41d566a3e3683cca3e179fb767c87845286863ac98b54f15 + languageName: node + linkType: hard + +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" + dependencies: + "@nodelib/fs.stat": 2.0.5 + run-parallel: ^1.1.9 + checksum: a970d595bd23c66c880e0ef1817791432dbb7acbb8d44b7e7d0e7a22f4521260d4a83f7f9fd61d44fda4610105577f8f58a60718105fb38352baed612fd79e59 + languageName: node + linkType: hard + +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 012480b5ca9d97bff9261571dbbec7bbc6033f69cc92908bc1ecfad0792361a5a1994bc48674b9ef76419d056a03efadfce5a6cf6dbc0a36559571a7a483f6f0 + languageName: node + linkType: hard + +"@nodelib/fs.walk@npm:^1.2.3": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": 2.1.5 + fastq: ^1.6.0 + checksum: 190c643f156d8f8f277bf2a6078af1ffde1fd43f498f187c2db24d35b4b4b5785c02c7dc52e356497b9a1b65b13edc996de08de0b961c32844364da02986dc53 + languageName: node + linkType: hard + +"@npmcli/arborist@npm:^4.0.4": + version: 4.3.1 + resolution: "@npmcli/arborist@npm:4.3.1" + dependencies: + "@isaacs/string-locale-compare": ^1.1.0 + "@npmcli/installed-package-contents": ^1.0.7 + "@npmcli/map-workspaces": ^2.0.0 + "@npmcli/metavuln-calculator": ^2.0.0 + "@npmcli/move-file": ^1.1.0 + "@npmcli/name-from-folder": ^1.0.1 + "@npmcli/node-gyp": ^1.0.3 + "@npmcli/package-json": ^1.0.1 + "@npmcli/run-script": ^2.0.0 + bin-links: ^3.0.0 + cacache: ^15.0.3 + common-ancestor-path: ^1.0.1 + json-parse-even-better-errors: ^2.3.1 + json-stringify-nice: ^1.1.4 + mkdirp: ^1.0.4 + mkdirp-infer-owner: ^2.0.0 + npm-install-checks: ^4.0.0 + npm-package-arg: ^8.1.5 + npm-pick-manifest: ^6.1.0 + npm-registry-fetch: ^12.0.1 + pacote: ^12.0.2 + parse-conflict-json: ^2.0.1 + proc-log: ^1.0.0 + promise-all-reject-late: ^1.0.0 + promise-call-limit: ^1.0.1 + read-package-json-fast: ^2.0.2 + readdir-scoped-modules: ^1.1.0 + rimraf: ^3.0.2 + semver: ^7.3.5 + ssri: ^8.0.1 + treeverse: ^1.0.4 + walk-up-path: ^1.0.0 + bin: + arborist: bin/index.js + checksum: 51470ebb9a47c414822d1c05eda7dfef672848ddacf5734cc0575cc1a9f92cca32f17aac209d9e520424620a9ff4685db103f8b16953e2ef1823dfa2871b50e7 + languageName: node + linkType: hard + +"@npmcli/fs@npm:^1.0.0": + version: 1.0.0 + resolution: "@npmcli/fs@npm:1.0.0" + dependencies: + "@gar/promisify": ^1.0.1 + semver: ^7.3.5 + checksum: f2b4990107dd2a5b18794c89aaff6f62f3a67883d49a20602fdfc353cbc7f8c5fd50edeffdc769e454900e01b8b8e43d0b9eb524d00963d69f3c829be1a2e8ac + languageName: node + linkType: hard + +"@npmcli/git@npm:^2.1.0": + version: 2.1.0 + resolution: "@npmcli/git@npm:2.1.0" + dependencies: + "@npmcli/promise-spawn": ^1.3.2 + lru-cache: ^6.0.0 + mkdirp: ^1.0.4 + npm-pick-manifest: ^6.1.1 + promise-inflight: ^1.0.1 + promise-retry: ^2.0.1 + semver: ^7.3.5 + which: ^2.0.2 + checksum: 1f89752df7b836f378b8828423c6ae344fe59399915b9460acded19686e2d0626246251a3cd4cc411ed21c1be6fe7f0c2195c17f392e88748581262ee806dc33 + languageName: node + linkType: hard + +"@npmcli/installed-package-contents@npm:^1.0.6, @npmcli/installed-package-contents@npm:^1.0.7": + version: 1.0.7 + resolution: "@npmcli/installed-package-contents@npm:1.0.7" + dependencies: + npm-bundled: ^1.1.1 + npm-normalize-package-bin: ^1.0.1 + bin: + installed-package-contents: index.js + checksum: a4a29b99d439827ce2e7817c1f61b56be160e640696e31dc513a2c8a37c792f75cdb6258ec15a1e22904f20df0a8a3019dd3766de5e6619f259834cf64233538 + languageName: node + linkType: hard + +"@npmcli/map-workspaces@npm:^2.0.0": + version: 2.0.1 + resolution: "@npmcli/map-workspaces@npm:2.0.1" + dependencies: + "@npmcli/name-from-folder": ^1.0.1 + glob: ^7.2.0 + minimatch: ^5.0.0 + read-package-json-fast: ^2.0.3 + checksum: 16c6738e154536580bbca00ededc9e0ed04e008c980319e06b2cdcee055f40f3e9e55c1d51d23a8226aa998d3b4e845e52b658409538880f0f9af403c44c03e8 + languageName: node + linkType: hard + +"@npmcli/metavuln-calculator@npm:^2.0.0": + version: 2.0.0 + resolution: "@npmcli/metavuln-calculator@npm:2.0.0" + dependencies: + cacache: ^15.0.5 + json-parse-even-better-errors: ^2.3.1 + pacote: ^12.0.0 + semver: ^7.3.2 + checksum: bf88115e7c52a5fcf9d3f06d47eeb18acb6077327ee035661b6e4c26102b5e963aa3461679a50fb54427ff4526284a8fdebc743689dd7d71d8ee3814e8f341ee + languageName: node + linkType: hard + +"@npmcli/move-file@npm:^1.0.1, @npmcli/move-file@npm:^1.1.0": + version: 1.1.2 + resolution: "@npmcli/move-file@npm:1.1.2" + dependencies: + mkdirp: ^1.0.4 + rimraf: ^3.0.2 + checksum: c96381d4a37448ea280951e46233f7e541058cf57a57d4094dd4bdcaae43fa5872b5f2eb6bfb004591a68e29c5877abe3cdc210cb3588cbf20ab2877f31a7de7 + languageName: node + linkType: hard + +"@npmcli/name-from-folder@npm:^1.0.1": + version: 1.0.1 + resolution: "@npmcli/name-from-folder@npm:1.0.1" + checksum: 67339f4096e32b712d2df0250cc95c087569f09e657d7f81a1760fa2cc5123e29c3c3e1524388832310ba2d96ec4679985b643b44627f6a51f4a00c3b0075de9 + languageName: node + linkType: hard + +"@npmcli/node-gyp@npm:^1.0.2, @npmcli/node-gyp@npm:^1.0.3": + version: 1.0.3 + resolution: "@npmcli/node-gyp@npm:1.0.3" + checksum: 496d5eef2e90e34bb07e96adbcbbce3dba5370ae87e8c46ff5b28570848f35470c8e008b8f69e50863632783e0a9190e6f55b2e4b049c537142821153942d26a + languageName: node + linkType: hard + +"@npmcli/package-json@npm:^1.0.1": + version: 1.0.1 + resolution: "@npmcli/package-json@npm:1.0.1" + dependencies: + json-parse-even-better-errors: ^2.3.1 + checksum: 08b66c8ddb1d6b678975a83006d2fe5070b3013bcb68ea9d54c0142538a614596ddfd1143183fbb8f82c5cecf477d98f3c4e473ef34df3bbf3814e97e37e18d3 + languageName: node + linkType: hard + +"@npmcli/promise-spawn@npm:^1.2.0, @npmcli/promise-spawn@npm:^1.3.2": + version: 1.3.2 + resolution: "@npmcli/promise-spawn@npm:1.3.2" + dependencies: + infer-owner: ^1.0.4 + checksum: 543b7c1e26230499b4100b10d45efa35b1077e8f25595050f34930ca3310abe9524f7387279fe4330139e0f28a0207595245503439276fd4b686cca2b6503080 + languageName: node + linkType: hard + +"@npmcli/run-script@npm:^2.0.0": + version: 2.0.0 + resolution: "@npmcli/run-script@npm:2.0.0" + dependencies: + "@npmcli/node-gyp": ^1.0.2 + "@npmcli/promise-spawn": ^1.3.2 + node-gyp: ^8.2.0 + read-package-json-fast: ^2.0.1 + checksum: c016ea9411e434d84e9bb9c30814c2868eee3ff32625f3e1af4671c3abfe0768739ffb2dba5520da926ae44315fc5f507b744f0626a80bc9461f2f19760e5fa0 + languageName: node + linkType: hard + +"@oclif/color@npm:^1.0.0": + version: 1.0.0 + resolution: "@oclif/color@npm:1.0.0" + dependencies: + ansi-styles: ^4.2.1 + chalk: ^4.1.0 + strip-ansi: ^6.0.0 + supports-color: ^8.1.1 + tslib: ^2 + checksum: 60521f90ebaa87401332aba48c09ab09d8d6f67e1e1d2951eec6852efbfb62750674a32a1278af7176892f689268842ee4a5a8dbc6862dce8d3057d7c8cfffd1 + languageName: node + linkType: hard + +"@oclif/core@npm:^1.0.8, @oclif/core@npm:^1.2.0, @oclif/core@npm:^1.2.1, @oclif/core@npm:^1.3.0, @oclif/core@npm:^1.3.4": + version: 1.3.4 + resolution: "@oclif/core@npm:1.3.4" + dependencies: + "@oclif/linewrap": ^1.0.0 + "@oclif/screen": ^3.0.2 + ansi-escapes: ^4.3.0 + ansi-styles: ^4.2.0 + cardinal: ^2.1.1 + chalk: ^4.1.2 + clean-stack: ^3.0.1 + cli-progress: ^3.10.0 + debug: ^4.3.3 + ejs: ^3.1.6 + fs-extra: ^9.1.0 + get-package-type: ^0.1.0 + globby: ^11.0.4 + hyperlinker: ^1.0.0 + indent-string: ^4.0.0 + is-wsl: ^2.2.0 + js-yaml: ^3.13.1 + lodash: ^4.17.21 + natural-orderby: ^2.0.3 + object-treeify: ^1.1.4 + password-prompt: ^1.1.2 + semver: ^7.3.5 + string-width: ^4.2.3 + strip-ansi: ^6.0.1 + supports-color: ^8.1.1 + supports-hyperlinks: ^2.2.0 + tslib: ^2.3.1 + widest-line: ^3.1.0 + wrap-ansi: ^7.0.0 + checksum: c7f29f71ce5c399442f7a13e28b8bf6f4f0f13bddedfa70ccb12aa128a828d087f3d425f5e295b01e636d720e30ec084fc8ba091ac760202f745bbbfec0a8559 + languageName: node + linkType: hard + +"@oclif/linewrap@npm:^1.0.0": + version: 1.0.0 + resolution: "@oclif/linewrap@npm:1.0.0" + checksum: a072016a58b5e1331bbc21303ad5100fcda846ac4b181e344aec88bb24c5da09c416651e51313ffcc846a83514b74b8b987dd965982900f3edbb42b4e87cc246 + languageName: node + linkType: hard + +"@oclif/plugin-help@npm:^5.1.11": + version: 5.1.11 + resolution: "@oclif/plugin-help@npm:5.1.11" + dependencies: + "@oclif/core": ^1.2.0 + checksum: ab2d1377cbfb935ccd71f4b83f1419b434c53b49fffe5358c446973b793e0bf30d79db2383ee37d91a08cc5a2762c59ec23ad9f47481c2219e9f7d028b2869cc + languageName: node + linkType: hard + +"@oclif/plugin-not-found@npm:^2.3.1": + version: 2.3.1 + resolution: "@oclif/plugin-not-found@npm:2.3.1" + dependencies: + "@oclif/color": ^1.0.0 + "@oclif/core": ^1.2.1 + fast-levenshtein: ^3.0.0 + lodash: ^4.17.21 + checksum: b6aeddb7335f2963b5835f0a7f9c0fe0952e112a0360373974059d927cbb21cb9f3c3cb0dcf36268d8db5e524877268c7bc3def51a4f907fe7fdc5471fb42819 + languageName: node + linkType: hard + +"@oclif/plugin-warn-if-update-available@npm:^2.0.4": + version: 2.0.4 + resolution: "@oclif/plugin-warn-if-update-available@npm:2.0.4" + dependencies: + "@oclif/core": ^1.0.8 + chalk: ^4.1.0 + debug: ^4.1.0 + fs-extra: ^9.0.1 + http-call: ^5.2.2 + lodash: ^4.17.21 + semver: ^7.3.2 + checksum: 9a127aaaa3e37a390f17070f9c96fbee5c9b0c5ad0c1a01da84f28717ed80048ea909d4fe691b4021ee98af861d5ae34412e2b35180cbed36765eb0a078e5fdb + languageName: node + linkType: hard + +"@oclif/screen@npm:^3.0.2": + version: 3.0.2 + resolution: "@oclif/screen@npm:3.0.2" + checksum: 962678c65f1cf5b06864295a212020e3ddda36ab37190ca317e938943325a5acdbf3cb2761c371612daf1565e397fa5ff7bd0563887d746ccfde7c4ab312f005 + languageName: node + linkType: hard + +"@octokit/auth-token@npm:^2.4.4": + version: 2.5.0 + resolution: "@octokit/auth-token@npm:2.5.0" + dependencies: + "@octokit/types": ^6.0.3 + checksum: 45949296c09abcd6beb4c3f69d45b0c1f265f9581d2a9683cf4d1800c4cf8259c2f58d58e44c16c20bffb85a0282a176c0d51f4af300e428b863f27b910e6297 + languageName: node + linkType: hard + +"@octokit/core@npm:^3.5.1": + version: 3.5.1 + resolution: "@octokit/core@npm:3.5.1" + dependencies: + "@octokit/auth-token": ^2.4.4 + "@octokit/graphql": ^4.5.8 + "@octokit/request": ^5.6.0 + "@octokit/request-error": ^2.0.5 + "@octokit/types": ^6.0.3 + before-after-hook: ^2.2.0 + universal-user-agent: ^6.0.0 + checksum: 67179739fc9712b201f2400f132287a2c56a18506e00900bc9d2a3f742b74f1ba69ad998e42f28f3964c0bd1d5478232c1ec7b485c97702b821fbe22b76afa90 + languageName: node + linkType: hard + +"@octokit/endpoint@npm:^6.0.1": + version: 6.0.12 + resolution: "@octokit/endpoint@npm:6.0.12" + dependencies: + "@octokit/types": ^6.0.3 + is-plain-object: ^5.0.0 + universal-user-agent: ^6.0.0 + checksum: b48b29940af11c4b9bca41cf56809754bb8385d4e3a6122671799d27f0238ba575b3fde86d2d30a84f4dbbc14430940de821e56ecc6a9a92d47fc2b29a31479d + languageName: node + linkType: hard + +"@octokit/graphql@npm:^4.5.8": + version: 4.8.0 + resolution: "@octokit/graphql@npm:4.8.0" + dependencies: + "@octokit/request": ^5.6.0 + "@octokit/types": ^6.0.3 + universal-user-agent: ^6.0.0 + checksum: f68afe53f63900d4a16a0a733f2f500df2695b731f8ed32edb728d50edead7f5011437f71d069c2d2f6d656227703d0c832a3c8af58ecf82bd5dcc051f2d2d74 + languageName: node + linkType: hard + +"@octokit/openapi-types@npm:^11.2.0": + version: 11.2.0 + resolution: "@octokit/openapi-types@npm:11.2.0" + checksum: eb373ea496bc96bf0233505a0916eb38cb193d1829cab935e1cf1fd21839c402a1d835d3c0326290c756c0ed980a64d0ae73ad3c5d5decde9000f0828aa7ff52 + languageName: node + linkType: hard + +"@octokit/plugin-paginate-rest@npm:^2.16.8": + version: 2.17.0 + resolution: "@octokit/plugin-paginate-rest@npm:2.17.0" + dependencies: + "@octokit/types": ^6.34.0 + peerDependencies: + "@octokit/core": ">=2" + checksum: c8753cda6f7ede79d0e9df43a54e56020aa1c9c6887684e0e0d45cb6ee0dcabf460c3e4b8a18edabef711bb269fd826616e99e78dc29fb30d47c210c562603a0 + languageName: node + linkType: hard + +"@octokit/plugin-request-log@npm:^1.0.4": + version: 1.0.4 + resolution: "@octokit/plugin-request-log@npm:1.0.4" + peerDependencies: + "@octokit/core": ">=3" + checksum: 2086db00056aee0f8ebd79797b5b57149ae1014e757ea08985b71eec8c3d85dbb54533f4fd34b6b9ecaa760904ae6a7536be27d71e50a3782ab47809094bfc0c + languageName: node + linkType: hard + +"@octokit/plugin-rest-endpoint-methods@npm:^5.12.0": + version: 5.13.0 + resolution: "@octokit/plugin-rest-endpoint-methods@npm:5.13.0" + dependencies: + "@octokit/types": ^6.34.0 + deprecation: ^2.3.1 + peerDependencies: + "@octokit/core": ">=3" + checksum: f331457e4317130adb456b27df2a99609fb54a4dc2da6f87009e567c7325680c901abf18ad08483535bab4ec1c892e4236f4135a2804603aebb12c0698c678c8 + languageName: node + linkType: hard + +"@octokit/request-error@npm:^2.0.5, @octokit/request-error@npm:^2.1.0": + version: 2.1.0 + resolution: "@octokit/request-error@npm:2.1.0" + dependencies: + "@octokit/types": ^6.0.3 + deprecation: ^2.0.0 + once: ^1.4.0 + checksum: baec2b5700498be01b4d958f9472cb776b3f3b0ea52924323a07e7a88572e24cac2cdf7eb04a0614031ba346043558b47bea2d346e98f0e8385b4261f138ef18 + languageName: node + linkType: hard + +"@octokit/request@npm:^5.6.0": + version: 5.6.3 + resolution: "@octokit/request@npm:5.6.3" + dependencies: + "@octokit/endpoint": ^6.0.1 + "@octokit/request-error": ^2.1.0 + "@octokit/types": ^6.16.1 + is-plain-object: ^5.0.0 + node-fetch: ^2.6.7 + universal-user-agent: ^6.0.0 + checksum: c0b4542eb4baaf880d673c758d3e0b5c4a625a4ae30abf40df5548b35f1ff540edaac74625192b1aff42a79ac661e774da4ab7d5505f1cb4ef81239b1e8510c5 + languageName: node + linkType: hard + +"@octokit/rest@npm:^18.0.6": + version: 18.12.0 + resolution: "@octokit/rest@npm:18.12.0" + dependencies: + "@octokit/core": ^3.5.1 + "@octokit/plugin-paginate-rest": ^2.16.8 + "@octokit/plugin-request-log": ^1.0.4 + "@octokit/plugin-rest-endpoint-methods": ^5.12.0 + checksum: c18bd6676a60b66819b016b0f969fcd04d8dfa04d01b7af9af9a7410ff028c621c995185e29454c23c47906da506c1e01620711259989a964ebbfd9106f5b715 + languageName: node + linkType: hard + +"@octokit/types@npm:^6.0.3, @octokit/types@npm:^6.16.1, @octokit/types@npm:^6.34.0": + version: 6.34.0 + resolution: "@octokit/types@npm:6.34.0" + dependencies: + "@octokit/openapi-types": ^11.2.0 + checksum: f122b9aee8f6baddd515e34a0913e73b21d4bc82d6ee59d77a8aaf01b4a02c10867dd013003d087a83dc96db23511893669015af6d30c27cece185e21cf1df89 + languageName: node + linkType: hard + +"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/aspromise@npm:1.1.2" + checksum: 011fe7ef0826b0fd1a95935a033a3c0fd08483903e1aa8f8b4e0704e3233406abb9ee25350ec0c20bbecb2aad8da0dcea58b392bbd77d6690736f02c143865d2 + languageName: node + linkType: hard + +"@protobufjs/base64@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/base64@npm:1.1.2" + checksum: 67173ac34de1e242c55da52c2f5bdc65505d82453893f9b51dc74af9fe4c065cf4a657a4538e91b0d4a1a1e0a0642215e31894c31650ff6e3831471061e1ee9e + languageName: node + linkType: hard + +"@protobufjs/codegen@npm:^2.0.4": + version: 2.0.4 + resolution: "@protobufjs/codegen@npm:2.0.4" + checksum: 59240c850b1d3d0b56d8f8098dd04787dcaec5c5bd8de186fa548de86b86076e1c50e80144b90335e705a044edf5bc8b0998548474c2a10a98c7e004a1547e4b + languageName: node + linkType: hard + +"@protobufjs/eventemitter@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/eventemitter@npm:1.1.0" + checksum: 0369163a3d226851682f855f81413cbf166cd98f131edb94a0f67f79e75342d86e89df9d7a1df08ac28be2bc77e0a7f0200526bb6c2a407abbfee1f0262d5fd7 + languageName: node + linkType: hard + +"@protobufjs/fetch@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/fetch@npm:1.1.0" + dependencies: + "@protobufjs/aspromise": ^1.1.1 + "@protobufjs/inquire": ^1.1.0 + checksum: 3fce7e09eb3f1171dd55a192066450f65324fd5f7cc01a431df01bb00d0a895e6bfb5b0c5561ce157ee1d886349c90703d10a4e11a1a256418ff591b969b3477 + languageName: node + linkType: hard + +"@protobufjs/float@npm:^1.0.2": + version: 1.0.2 + resolution: "@protobufjs/float@npm:1.0.2" + checksum: 5781e1241270b8bd1591d324ca9e3a3128d2f768077a446187a049e36505e91bc4156ed5ac3159c3ce3d2ba3743dbc757b051b2d723eea9cd367bfd54ab29b2f + languageName: node + linkType: hard + +"@protobufjs/inquire@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/inquire@npm:1.1.0" + checksum: ca06f02eaf65ca36fb7498fc3492b7fc087bfcc85c702bac5b86fad34b692bdce4990e0ef444c1e2aea8c034227bd1f0484be02810d5d7e931c55445555646f4 + languageName: node + linkType: hard + +"@protobufjs/path@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/path@npm:1.1.2" + checksum: 856eeb532b16a7aac071cacde5c5620df800db4c80cee6dbc56380524736205aae21e5ae47739114bf669ab5e8ba0e767a282ad894f3b5e124197cb9224445ee + languageName: node + linkType: hard + +"@protobufjs/pool@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/pool@npm:1.1.0" + checksum: d6a34fbbd24f729e2a10ee915b74e1d77d52214de626b921b2d77288bd8f2386808da2315080f2905761527cceffe7ec34c7647bd21a5ae41a25e8212ff79451 + languageName: node + linkType: hard + +"@protobufjs/utf8@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/utf8@npm:1.1.0" + checksum: f9bf3163d13aaa3b6f5e6fbf37a116e094ea021c0e1f2a7ccd0e12a29e2ce08dafba4e8b36e13f8ed7397e1591610ce880ed1289af4d66cf4ace8a36a9557278 + languageName: node + linkType: hard + +"@sindresorhus/is@npm:^0.14.0": + version: 0.14.0 + resolution: "@sindresorhus/is@npm:0.14.0" + checksum: 971e0441dd44ba3909b467219a5e242da0fc584048db5324cfb8048148fa8dcc9d44d71e3948972c4f6121d24e5da402ef191420d1266a95f713bb6d6e59c98a + languageName: node + linkType: hard + +"@sinonjs/commons@npm:^1.6.0, @sinonjs/commons@npm:^1.7.0, @sinonjs/commons@npm:^1.8.3": + version: 1.8.3 + resolution: "@sinonjs/commons@npm:1.8.3" + dependencies: + type-detect: 4.0.8 + checksum: 6159726db5ce6bf9f2297f8427f7ca5b3dff45b31e5cee23496f1fa6ef0bb4eab878b23fb2c5e6446381f6a66aba4968ef2fc255c1180d753d4b8c271636a2e5 + languageName: node + linkType: hard + +"@sinonjs/fake-timers@npm:^7.0.4, @sinonjs/fake-timers@npm:^7.1.0, @sinonjs/fake-timers@npm:^7.1.2": + version: 7.1.2 + resolution: "@sinonjs/fake-timers@npm:7.1.2" + dependencies: + "@sinonjs/commons": ^1.7.0 + checksum: c84773d7973edad5511a31d2cc75023447b5cf714a84de9bb50eda45dda88a0d3bd2c30bf6e6e936da50a048d5352e2151c694e13e59b97d187ba1f329e9a00c + languageName: node + linkType: hard + +"@sinonjs/samsam@npm:^6.0.2": + version: 6.0.2 + resolution: "@sinonjs/samsam@npm:6.0.2" + dependencies: + "@sinonjs/commons": ^1.6.0 + lodash.get: ^4.4.2 + type-detect: ^4.0.8 + checksum: bc1514edf15f4fa42a1bf27024b15f87654deb2999045c0e427659ff3c734eba44661fceae3624be23cc15ee9c6ddafe5209af2192845c6b267350b54eed1495 + languageName: node + linkType: hard + +"@sinonjs/text-encoding@npm:^0.7.1": + version: 0.7.1 + resolution: "@sinonjs/text-encoding@npm:0.7.1" + checksum: 130de0bb568c5f8a611ec21d1a4e3f80ab0c5ec333010f49cfc1adc5cba6d8808699c8a587a46b0f0b016a1f4c1389bc96141e773e8460fcbb441875b2e91ba7 + languageName: node + linkType: hard + +"@szmarczak/http-timer@npm:^1.1.2": + version: 1.1.2 + resolution: "@szmarczak/http-timer@npm:1.1.2" + dependencies: + defer-to-connect: ^1.0.1 + checksum: 4d9158061c5f397c57b4988cde33a163244e4f02df16364f103971957a32886beb104d6180902cbe8b38cb940e234d9f98a4e486200deca621923f62f50a06fe + languageName: node + linkType: hard + +"@tootallnate/once@npm:1": + version: 1.1.2 + resolution: "@tootallnate/once@npm:1.1.2" + checksum: e1fb1bbbc12089a0cb9433dc290f97bddd062deadb6178ce9bcb93bb7c1aecde5e60184bc7065aec42fe1663622a213493c48bbd4972d931aae48315f18e1be9 + languageName: node + linkType: hard + +"@tootallnate/once@npm:2": + version: 2.0.0 + resolution: "@tootallnate/once@npm:2.0.0" + checksum: ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 + languageName: node + linkType: hard + +"@tsconfig/node10@npm:^1.0.7": + version: 1.0.8 + resolution: "@tsconfig/node10@npm:1.0.8" + checksum: b8d5fffbc6b17ef64ef74f7fdbccee02a809a063ade785c3648dae59406bc207f70ea2c4296f92749b33019fa36a5ae716e42e49cc7f1bbf0fd147be0d6b970a + languageName: node + linkType: hard + +"@tsconfig/node12@npm:^1.0.7": + version: 1.0.9 + resolution: "@tsconfig/node12@npm:1.0.9" + checksum: a01b2400ab3582b86b589c6d31dcd0c0656f333adecde85d6d7d4086adb059808b82692380bb169546d189bf771ae21d02544a75b57bd6da4a5dd95f8567bec9 + languageName: node + linkType: hard + +"@tsconfig/node14@npm:^1.0.0": + version: 1.0.1 + resolution: "@tsconfig/node14@npm:1.0.1" + checksum: 976345e896c0f059867f94f8d0f6ddb8b1844fb62bf36b727de8a9a68f024857e5db97ed51d3325e23e0616a5e48c034ff51a8d595b3fe7e955f3587540489be + languageName: node + linkType: hard + +"@tsconfig/node16@npm:^1.0.2": + version: 1.0.2 + resolution: "@tsconfig/node16@npm:1.0.2" + checksum: ca94d3639714672bbfd55f03521d3f56bb6a25479bd425da81faf21f13e1e9d15f40f97377dedbbf477a5841c5b0c8f4cd1b391f33553d750b9202c54c2c07aa + languageName: node + linkType: hard + +"@types/chai-as-promised@npm:*": + version: 7.1.4 + resolution: "@types/chai-as-promised@npm:7.1.4" + dependencies: + "@types/chai": "*" + checksum: bb974e77e0357fcc9a01f4b46eb1d3d6a40621a479654fa17539890cd59635faf9b860b8c3851f638d1e239404b1dc8e7ab1305f26dec43e19cce6796e01fe48 + languageName: node + linkType: hard + +"@types/chai@npm:*, @types/chai@npm:^4.2.12": + version: 4.2.22 + resolution: "@types/chai@npm:4.2.22" + checksum: dca66a263b25c26112c0a8c6df20316412fa54b557443a108836c07cee961aa56cc5b1763273f69eb450c83ca9f28069ff78b617bffc01806cdd83afc1c20c2a + languageName: node + linkType: hard + +"@types/component-emitter@npm:^1.2.10": + version: 1.2.11 + resolution: "@types/component-emitter@npm:1.2.11" + checksum: 0e081c5f7a4b113af3732f67ad9ebb487d5c239d440d96938ff9a679d18bb9337a513638e12b5b02a7a921494eef18c5a4d78f1188bc43a12290edd74c42a9c7 + languageName: node + linkType: hard + +"@types/connect@npm:^3.4.33": + version: 3.4.35 + resolution: "@types/connect@npm:3.4.35" + dependencies: + "@types/node": "*" + checksum: fe81351470f2d3165e8b12ce33542eef89ea893e36dd62e8f7d72566dfb7e448376ae962f9f3ea888547ce8b55a40020ca0e01d637fab5d99567673084542641 + languageName: node + linkType: hard + +"@types/cookie@npm:^0.4.1": + version: 0.4.1 + resolution: "@types/cookie@npm:0.4.1" + checksum: 3275534ed69a76c68eb1a77d547d75f99fedc80befb75a3d1d03662fb08d697e6f8b1274e12af1a74c6896071b11510631ba891f64d30c78528d0ec45a9c1a18 + languageName: node + linkType: hard + +"@types/cors@npm:^2.8.12": + version: 2.8.12 + resolution: "@types/cors@npm:2.8.12" + checksum: 8c45f112c7d1d2d831b4b266f2e6ed33a1887a35dcbfe2a18b28370751fababb7cd045e745ef84a523c33a25932678097bf79afaa367c6cb3fa0daa7a6438257 + languageName: node + linkType: hard + +"@types/dirty-chai@npm:^2.0.2": + version: 2.0.2 + resolution: "@types/dirty-chai@npm:2.0.2" + dependencies: + "@types/chai": "*" + "@types/chai-as-promised": "*" + checksum: 6015689ef7d64d4012b7327f87c39e95397500a50eb47d456ff2a8f59824d78d8a0e805d9629970a1e767fa06a03ccc8c0c65b3605e6e1e2368d1bfeeccc831b + languageName: node + linkType: hard + +"@types/eslint-scope@npm:^3.7.0": + version: 3.7.1 + resolution: "@types/eslint-scope@npm:3.7.1" + dependencies: + "@types/eslint": "*" + "@types/estree": "*" + checksum: 4271c9adad19ad8a1d23062d9020468a51c7f81594b12b8e68f7d460c09e14d57cae3e82b077c402766369c0c17e2de72da72c405fa465d18a46c0b14ce92530 + languageName: node + linkType: hard + +"@types/eslint@npm:*": + version: 8.2.0 + resolution: "@types/eslint@npm:8.2.0" + dependencies: + "@types/estree": "*" + "@types/json-schema": "*" + checksum: 18f37790afc57412c74c9a0ef9a8cc44c1237a3f3d70e3e4e3daad38ed501f1a70395ff3955d3e4b481a5d04e6819ad2c377cd287c7315b3b633f0f1bda7b4a2 + languageName: node + linkType: hard + +"@types/estree@npm:*, @types/estree@npm:^0.0.50": + version: 0.0.50 + resolution: "@types/estree@npm:0.0.50" + checksum: 9a2b6a4a8c117f34d08fbda5e8f69b1dfb109f7d149b60b00fd7a9fb6ac545c078bc590aa4ec2f0a256d680cf72c88b3b28b60c326ee38a7bc8ee1ee95624922 + languageName: node + linkType: hard + +"@types/expect@npm:^1.20.4": + version: 1.20.4 + resolution: "@types/expect@npm:1.20.4" + checksum: c09a9abec2c1776dd8948920dc3bad87b1206c843509d3d3002040983b1769b2e3914202a6c20b72e5c3fb5738a1ab87cb7be9d3fe9efabf2a324173b222a224 + languageName: node + linkType: hard + +"@types/expect@npm:^24.3.0": + version: 24.3.0 + resolution: "@types/expect@npm:24.3.0" + dependencies: + expect: "*" + checksum: 8d017b49b1b11fcf1c9bd2807bf7e1d49e34a4cdd6c40a40fbeb21a9d5873eb0d159043c647dce9ebabb589fef7833796ae17fbf7736233a32769797a3db4db5 + languageName: node + linkType: hard + +"@types/express-serve-static-core@npm:^4.17.9": + version: 4.17.25 + resolution: "@types/express-serve-static-core@npm:4.17.25" + dependencies: + "@types/node": "*" + "@types/qs": "*" + "@types/range-parser": "*" + checksum: a60d44676db470afd413130ca8b464d864eb2c1a882b1037a52c5b612eebb61bcc4289d927cb09456be56c78bebe3cb24ffeaf0fa11bd7f5237a3ed5360abf3a + languageName: node + linkType: hard + +"@types/glob@npm:^7.1.1": + version: 7.2.0 + resolution: "@types/glob@npm:7.2.0" + dependencies: + "@types/minimatch": "*" + "@types/node": "*" + checksum: 6ae717fedfdfdad25f3d5a568323926c64f52ef35897bcac8aca8e19bc50c0bd84630bbd063e5d52078b2137d8e7d3c26eabebd1a2f03ff350fff8a91e79fc19 + languageName: node + linkType: hard + +"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0": + version: 2.0.3 + resolution: "@types/istanbul-lib-coverage@npm:2.0.3" + checksum: 0650cba4be8f464bee89b9de0b71a5ea3b5cc676ce24e1196b5d6a51542ce9e613ae4549bf19756bb33dbbbb32b47931040266100062bfb197c597d73e341eb0 + languageName: node + linkType: hard + +"@types/istanbul-lib-report@npm:*": + version: 3.0.0 + resolution: "@types/istanbul-lib-report@npm:3.0.0" + dependencies: + "@types/istanbul-lib-coverage": "*" + checksum: 656398b62dc288e1b5226f8880af98087233cdb90100655c989a09f3052b5775bf98ba58a16c5ae642fb66c61aba402e07a9f2bff1d1569e3b306026c59f3f36 + languageName: node + linkType: hard + +"@types/istanbul-reports@npm:^3.0.0": + version: 3.0.1 + resolution: "@types/istanbul-reports@npm:3.0.1" + dependencies: + "@types/istanbul-lib-report": "*" + checksum: f1ad54bc68f37f60b30c7915886b92f86b847033e597f9b34f2415acdbe5ed742fa559a0a40050d74cdba3b6a63c342cac1f3a64dba5b68b66a6941f4abd7903 + languageName: node + linkType: hard + +"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.8": + version: 7.0.9 + resolution: "@types/json-schema@npm:7.0.9" + checksum: 259d0e25f11a21ba5c708f7ea47196bd396e379fddb79c76f9f4f62c945879dc21657904914313ec2754e443c5018ea8372362f323f30e0792897fdb2098a705 + languageName: node + linkType: hard + +"@types/json5@npm:^0.0.29": + version: 0.0.29 + resolution: "@types/json5@npm:0.0.29" + checksum: e60b153664572116dfea673c5bda7778dbff150498f44f998e34b5886d8afc47f16799280e4b6e241c0472aef1bc36add771c569c68fc5125fc2ae519a3eb9ac + languageName: node + linkType: hard + +"@types/keyv@npm:^3.1.1": + version: 3.1.3 + resolution: "@types/keyv@npm:3.1.3" + dependencies: + "@types/node": "*" + checksum: b5f8aa592cc21c16d99e69aec0976f12b893b055e4456d90148a610a6b6088e297b2ba5f38f8c8280cef006cfd8f9ec99e069905020882619dc5fc8aa46f5f27 + languageName: node + linkType: hard + +"@types/lodash@npm:^4.14.159": + version: 4.14.177 + resolution: "@types/lodash@npm:4.14.177" + checksum: 00f9eb300ed5219cfbabb3448d4a71744895edad60dcfda9f028c0808a50eb6ad1ca9f673b56a85f85668a4ea54b9870643625468a747039c435643e52253a75 + languageName: node + linkType: hard + +"@types/long@npm:^4.0.1": + version: 4.0.1 + resolution: "@types/long@npm:4.0.1" + checksum: ff9653c33f5000d0f131fd98a950a0343e2e33107dd067a97ac4a3b9678e1a2e39ea44772ad920f54ef6e8f107f76bc92c2584ba905a0dc4253282a4101166d0 + languageName: node + linkType: hard + +"@types/minimatch@npm:*, @types/minimatch@npm:^3.0.3": + version: 3.0.5 + resolution: "@types/minimatch@npm:3.0.5" + checksum: c41d136f67231c3131cf1d4ca0b06687f4a322918a3a5adddc87ce90ed9dbd175a3610adee36b106ae68c0b92c637c35e02b58c8a56c424f71d30993ea220b92 + languageName: node + linkType: hard + +"@types/minimist@npm:^1.2.0": + version: 1.2.2 + resolution: "@types/minimist@npm:1.2.2" + checksum: b8da83c66eb4aac0440e64674b19564d9d86c80ae273144db9681e5eeff66f238ade9515f5006ffbfa955ceff8b89ad2bd8ec577d7caee74ba101431fb07045d + languageName: node + linkType: hard + +"@types/mocha@npm:^8.0.3": + version: 8.2.3 + resolution: "@types/mocha@npm:8.2.3" + checksum: b43ed1b642a2ee62bf10792a07d5d21d66ab8b4d2cf5d822c8a7643e77b90009aecc000eefab5f6ddc9eb69004192f84119a6f97a8499e1a13ea082e7a5e71bf + languageName: node + linkType: hard + +"@types/node@npm:*, @types/node@npm:>=10.0.0, @types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0": + version: 17.0.21 + resolution: "@types/node@npm:17.0.21" + checksum: 89dcd2fe82f21d3634266f8384e9c865cf8af49685639fbdbd799bdd1040480fb1e8eeda2d3b9fce41edbe704d2a4be9f427118c4ae872e8d9bb7cbeb3c41a94 + languageName: node + linkType: hard + +"@types/node@npm:^10.3.5": + version: 10.17.60 + resolution: "@types/node@npm:10.17.60" + checksum: 2cdb3a77d071ba8513e5e8306fa64bf50e3c3302390feeaeff1fd325dd25c8441369715dfc8e3701011a72fed5958c7dfa94eb9239a81b3c286caa4d97db6eef + languageName: node + linkType: hard + +"@types/node@npm:^12.12.47, @types/node@npm:^12.12.54": + version: 12.20.37 + resolution: "@types/node@npm:12.20.37" + checksum: 8c8b12f802678b3b87c5344b6c84082be08561dda81dc161d42be8cd327330d1a5227cef039c45a5e63a6d4a01ef5ef215dccc42d06100f59f6a8814b4f91cdd + languageName: node + linkType: hard + +"@types/node@npm:^13.7.0": + version: 13.13.52 + resolution: "@types/node@npm:13.13.52" + checksum: 8f1afff497ebeba209e2dc340d823284e087a47632afe99a7daa30eaff80893e520f222ad400cd1f2d3b8288e93cf3eaded52a8e64eaefb8aacfe6c35de98f42 + languageName: node + linkType: hard + +"@types/node@npm:^14.6.0": + version: 14.17.34 + resolution: "@types/node@npm:14.17.34" + checksum: 803a7532b6998aca6aa1b3e6239f280e82612d4f641d38fbf08962c905ea9d1b278b2f09d396beac5120a4e68f33d7467298b197f2f00150ec1dcd546de02a89 + languageName: node + linkType: hard + +"@types/node@npm:^15.6.1": + version: 15.14.9 + resolution: "@types/node@npm:15.14.9" + checksum: 49f7f0522a3af4b8389aee660e88426490cd54b86356672a1fedb49919a8797c00d090ec2dcc4a5df34edc2099d57fc2203d796c4e7fbd382f2022ccd789eee7 + languageName: node + linkType: hard + +"@types/normalize-package-data@npm:^2.4.0": + version: 2.4.1 + resolution: "@types/normalize-package-data@npm:2.4.1" + checksum: e87bccbf11f95035c89a132b52b79ce69a1e3652fe55962363063c9c0dae0fe2477ebc585e03a9652adc6f381d24ba5589cc5e51849df4ced3d3e004a7d40ed5 + languageName: node + linkType: hard + +"@types/pino-pretty@npm:*": + version: 4.7.3 + resolution: "@types/pino-pretty@npm:4.7.3" + dependencies: + "@types/node": "*" + "@types/pino": 6.3 + checksum: 40fe67e73d26242d1f741ba62bd57aa4ce34842376878137f16befde3df0dd5ee39c30bc89da3d5e99848f69b00b73010863a740fd469585e0852ea656cb5b7b + languageName: node + linkType: hard + +"@types/pino-std-serializers@npm:*": + version: 2.4.1 + resolution: "@types/pino-std-serializers@npm:2.4.1" + dependencies: + "@types/node": "*" + checksum: a156e25882db9aade2576dbe6414379efcdd4fad24211d3f22f20e0cd4bee569215799ee5cd9b2b15282f18461a8a54573ff42bf6bee5d35b72513be2f78bdec + languageName: node + linkType: hard + +"@types/pino@npm:6.3, @types/pino@npm:^6.3.0": + version: 6.3.12 + resolution: "@types/pino@npm:6.3.12" + dependencies: + "@types/node": "*" + "@types/pino-pretty": "*" + "@types/pino-std-serializers": "*" + sonic-boom: ^2.1.0 + checksum: 801735146669312d02459781e5180220630eaef643da36dc5a9a97520e7ecc3da7270f31a86fcdcb1dc835073c9143fc628024ba5e3a0ea7cbb86aada4897709 + languageName: node + linkType: hard + +"@types/qs@npm:*": + version: 6.9.7 + resolution: "@types/qs@npm:6.9.7" + checksum: 7fd6f9c25053e9b5bb6bc9f9f76c1d89e6c04f7707a7ba0e44cc01f17ef5284adb82f230f542c2d5557d69407c9a40f0f3515e8319afd14e1e16b5543ac6cdba + languageName: node + linkType: hard + +"@types/range-parser@npm:*": + version: 1.2.4 + resolution: "@types/range-parser@npm:1.2.4" + checksum: b7c0dfd5080a989d6c8bb0b6750fc0933d9acabeb476da6fe71d8bdf1ab65e37c136169d84148034802f48378ab94e3c37bb4ef7656b2bec2cb9c0f8d4146a95 + languageName: node + linkType: hard + +"@types/responselike@npm:^1.0.0": + version: 1.0.0 + resolution: "@types/responselike@npm:1.0.0" + dependencies: + "@types/node": "*" + checksum: e99fc7cc6265407987b30deda54c1c24bb1478803faf6037557a774b2f034c5b097ffd65847daa87e82a61a250d919f35c3588654b0fdaa816906650f596d1b0 + languageName: node + linkType: hard + +"@types/sinon-chai@npm:^3.2.4": + version: 3.2.5 + resolution: "@types/sinon-chai@npm:3.2.5" + dependencies: + "@types/chai": "*" + "@types/sinon": "*" + checksum: ac332b8f2c9e13f081773a1c01fa12225768879ed310b36ba954982fccdf464fca4c3b852a60b2ca8e232026dd0a386b04f638bc903761c0d33375d9b3e9240f + languageName: node + linkType: hard + +"@types/sinon@npm:*": + version: 10.0.6 + resolution: "@types/sinon@npm:10.0.6" + dependencies: + "@sinonjs/fake-timers": ^7.1.0 + checksum: 1c2ae7daa822014a558d513c1ae341aed676bfe678b9e48cf13a0ccc0eabc429f211371e8f10495d5eb156c0aedfeb3ad5253ebfe026fc14a5b77c461a6cea2a + languageName: node + linkType: hard + +"@types/sinon@npm:^9.0.4": + version: 9.0.11 + resolution: "@types/sinon@npm:9.0.11" + dependencies: + "@types/sinonjs__fake-timers": "*" + checksum: 2074490973012283ec9ccb9f607fa12f36c78d8801f63ec437d3e8351dae161a018836cc02e8b039118ec9fb7680331594716ed0858075a11d381edd27faa75c + languageName: node + linkType: hard + +"@types/sinonjs__fake-timers@npm:*": + version: 8.1.0 + resolution: "@types/sinonjs__fake-timers@npm:8.1.0" + checksum: 02d8f5a2c821b42d2efb74d9733d66cb85140cc53a12c97175d1e12c5320953a111ae903da1147c49701c1ad1cbc45bb98d9f653547b78321d8032c7fee22e87 + languageName: node + linkType: hard + +"@types/stack-utils@npm:^2.0.0": + version: 2.0.1 + resolution: "@types/stack-utils@npm:2.0.1" + checksum: 205fdbe3326b7046d7eaf5e494d8084f2659086a266f3f9cf00bccc549c8e36e407f88168ad4383c8b07099957ad669f75f2532ed4bc70be2b037330f7bae019 + languageName: node + linkType: hard + +"@types/vinyl@npm:^2.0.4": + version: 2.0.6 + resolution: "@types/vinyl@npm:2.0.6" + dependencies: + "@types/expect": ^1.20.4 + "@types/node": "*" + checksum: 5012fb61e3a29e7deaac7e66b6d8cb73d87d15965c8a38cb69277c2beb851a9a8ec09d4a1b07a3151e143afc2e3a102ca368b9a0e08f2f29de9183c97f9c7d85 + languageName: node + linkType: hard + +"@types/ws@npm:^7.4.4": + version: 7.4.7 + resolution: "@types/ws@npm:7.4.7" + dependencies: + "@types/node": "*" + checksum: b4c9b8ad209620c9b21e78314ce4ff07515c0cadab9af101c1651e7bfb992d7fd933bd8b9c99d110738fd6db523ed15f82f29f50b45510288da72e964dedb1a3 + languageName: node + linkType: hard + +"@types/yargs-parser@npm:*": + version: 20.2.1 + resolution: "@types/yargs-parser@npm:20.2.1" + checksum: 1d039e64494a7a61ddd278349a3dc60b19f99ff0517425696e796f794e4252452b9d62178e69755ad03f439f9dc0c8c3d7b3a1201b3a24e134bac1a09fa11eaa + languageName: node + linkType: hard + +"@types/yargs@npm:^16.0.0": + version: 16.0.4 + resolution: "@types/yargs@npm:16.0.4" + dependencies: + "@types/yargs-parser": "*" + checksum: caa21d2c957592fe2184a8368c8cbe5a82a6c2e2f2893722e489f842dc5963293d2f3120bc06fe3933d60a3a0d1e2eb269649fd6b1947fe1820f8841ba611dd9 + languageName: node + linkType: hard + +"@ungap/promise-all-settled@npm:1.1.2": + version: 1.1.2 + resolution: "@ungap/promise-all-settled@npm:1.1.2" + checksum: 08d37fdfa23a6fe8139f1305313562ebad973f3fac01bcce2773b2bda5bcb0146dfdcf3cb6a722cf0a5f2ca0bc56a827eac8f1e7b3beddc548f654addf1fc34c + languageName: node + linkType: hard + +"@webassemblyjs/ast@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/ast@npm:1.11.1" + dependencies: + "@webassemblyjs/helper-numbers": 1.11.1 + "@webassemblyjs/helper-wasm-bytecode": 1.11.1 + checksum: 1eee1534adebeece635362f8e834ae03e389281972611408d64be7895fc49f48f98fddbbb5339bf8a72cb101bcb066e8bca3ca1bf1ef47dadf89def0395a8d87 + languageName: node + linkType: hard + +"@webassemblyjs/floating-point-hex-parser@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.11.1" + checksum: b8efc6fa08e4787b7f8e682182d84dfdf8da9d9c77cae5d293818bc4a55c1f419a87fa265ab85252b3e6c1fd323d799efea68d825d341a7c365c64bc14750e97 + languageName: node + linkType: hard + +"@webassemblyjs/helper-api-error@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/helper-api-error@npm:1.11.1" + checksum: 0792813f0ed4a0e5ee0750e8b5d0c631f08e927f4bdfdd9fe9105dc410c786850b8c61bff7f9f515fdfb149903bec3c976a1310573a4c6866a94d49bc7271959 + languageName: node + linkType: hard + +"@webassemblyjs/helper-buffer@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/helper-buffer@npm:1.11.1" + checksum: a337ee44b45590c3a30db5a8b7b68a717526cf967ada9f10253995294dbd70a58b2da2165222e0b9830cd4fc6e4c833bf441a721128d1fe2e9a7ab26b36003ce + languageName: node + linkType: hard + +"@webassemblyjs/helper-numbers@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/helper-numbers@npm:1.11.1" + dependencies: + "@webassemblyjs/floating-point-hex-parser": 1.11.1 + "@webassemblyjs/helper-api-error": 1.11.1 + "@xtuc/long": 4.2.2 + checksum: 44d2905dac2f14d1e9b5765cf1063a0fa3d57295c6d8930f6c59a36462afecc6e763e8a110b97b342a0f13376166c5d41aa928e6ced92e2f06b071fd0db59d3a + languageName: node + linkType: hard + +"@webassemblyjs/helper-wasm-bytecode@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.11.1" + checksum: eac400113127832c88f5826bcc3ad1c0db9b3dbd4c51a723cfdb16af6bfcbceb608170fdaac0ab7731a7e18b291be7af68a47fcdb41cfe0260c10857e7413d97 + languageName: node + linkType: hard + +"@webassemblyjs/helper-wasm-section@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/helper-wasm-section@npm:1.11.1" + dependencies: + "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/helper-buffer": 1.11.1 + "@webassemblyjs/helper-wasm-bytecode": 1.11.1 + "@webassemblyjs/wasm-gen": 1.11.1 + checksum: 617696cfe8ecaf0532763162aaf748eb69096fb27950219bb87686c6b2e66e11cd0614d95d319d0ab1904bc14ebe4e29068b12c3e7c5e020281379741fe4bedf + languageName: node + linkType: hard + +"@webassemblyjs/ieee754@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/ieee754@npm:1.11.1" + dependencies: + "@xtuc/ieee754": ^1.2.0 + checksum: 23a0ac02a50f244471631802798a816524df17e56b1ef929f0c73e3cde70eaf105a24130105c60aff9d64a24ce3b640dad443d6f86e5967f922943a7115022ec + languageName: node + linkType: hard + +"@webassemblyjs/leb128@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/leb128@npm:1.11.1" + dependencies: + "@xtuc/long": 4.2.2 + checksum: 33ccc4ade2f24de07bf31690844d0b1ad224304ee2062b0e464a610b0209c79e0b3009ac190efe0e6bd568b0d1578d7c3047fc1f9d0197c92fc061f56224ff4a + languageName: node + linkType: hard + +"@webassemblyjs/utf8@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/utf8@npm:1.11.1" + checksum: 972c5cfc769d7af79313a6bfb96517253a270a4bf0c33ba486aa43cac43917184fb35e51dfc9e6b5601548cd5931479a42e42c89a13bb591ffabebf30c8a6a0b + languageName: node + linkType: hard + +"@webassemblyjs/wasm-edit@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/wasm-edit@npm:1.11.1" + dependencies: + "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/helper-buffer": 1.11.1 + "@webassemblyjs/helper-wasm-bytecode": 1.11.1 + "@webassemblyjs/helper-wasm-section": 1.11.1 + "@webassemblyjs/wasm-gen": 1.11.1 + "@webassemblyjs/wasm-opt": 1.11.1 + "@webassemblyjs/wasm-parser": 1.11.1 + "@webassemblyjs/wast-printer": 1.11.1 + checksum: 6d7d9efaec1227e7ef7585a5d7ff0be5f329f7c1c6b6c0e906b18ed2e9a28792a5635e450aca2d136770d0207225f204eff70a4b8fd879d3ac79e1dcc26dbeb9 + languageName: node + linkType: hard + +"@webassemblyjs/wasm-gen@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/wasm-gen@npm:1.11.1" + dependencies: + "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/helper-wasm-bytecode": 1.11.1 + "@webassemblyjs/ieee754": 1.11.1 + "@webassemblyjs/leb128": 1.11.1 + "@webassemblyjs/utf8": 1.11.1 + checksum: 1f6921e640293bf99fb16b21e09acb59b340a79f986c8f979853a0ae9f0b58557534b81e02ea2b4ef11e929d946708533fd0693c7f3712924128fdafd6465f5b + languageName: node + linkType: hard + +"@webassemblyjs/wasm-opt@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/wasm-opt@npm:1.11.1" + dependencies: + "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/helper-buffer": 1.11.1 + "@webassemblyjs/wasm-gen": 1.11.1 + "@webassemblyjs/wasm-parser": 1.11.1 + checksum: 21586883a20009e2b20feb67bdc451bbc6942252e038aae4c3a08e6f67b6bae0f5f88f20bfc7bd0452db5000bacaf5ab42b98cf9aa034a6c70e9fc616142e1db + languageName: node + linkType: hard + +"@webassemblyjs/wasm-parser@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/wasm-parser@npm:1.11.1" + dependencies: + "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/helper-api-error": 1.11.1 + "@webassemblyjs/helper-wasm-bytecode": 1.11.1 + "@webassemblyjs/ieee754": 1.11.1 + "@webassemblyjs/leb128": 1.11.1 + "@webassemblyjs/utf8": 1.11.1 + checksum: 1521644065c360e7b27fad9f4bb2df1802d134dd62937fa1f601a1975cde56bc31a57b6e26408b9ee0228626ff3ba1131ae6f74ffb7d718415b6528c5a6dbfc2 + languageName: node + linkType: hard + +"@webassemblyjs/wast-printer@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/wast-printer@npm:1.11.1" + dependencies: + "@webassemblyjs/ast": 1.11.1 + "@xtuc/long": 4.2.2 + checksum: f15ae4c2441b979a3b4fce78f3d83472fb22350c6dc3fd34bfe7c3da108e0b2360718734d961bba20e7716cb8578e964b870da55b035e209e50ec9db0378a3f7 + languageName: node + linkType: hard + +"@webpack-cli/configtest@npm:^1.1.0": + version: 1.1.0 + resolution: "@webpack-cli/configtest@npm:1.1.0" + peerDependencies: + webpack: 4.x.x || 5.x.x + webpack-cli: 4.x.x + checksum: 69e7816b5b5d8589d5fc14af591d63831ff6ea2ca2d498c2d8bc354faaef9aeb282f70ad13df2fc5c3726be0f88c3dbc7facc37f3ab5a8cad44f562081792b28 + languageName: node + linkType: hard + +"@webpack-cli/info@npm:^1.4.0": + version: 1.4.0 + resolution: "@webpack-cli/info@npm:1.4.0" + dependencies: + envinfo: ^7.7.3 + peerDependencies: + webpack-cli: 4.x.x + checksum: 6385b1e2c511d0136fa53fcff5ecdc00ce7590d01648b437089e6d9c7b1866da8c6e850c41a7c52d3eb3ae23a31f3f40e1cead77ea2046ee6eb6b23a4124f4a9 + languageName: node + linkType: hard + +"@webpack-cli/serve@npm:^1.6.0": + version: 1.6.0 + resolution: "@webpack-cli/serve@npm:1.6.0" + peerDependencies: + webpack-cli: 4.x.x + peerDependenciesMeta: + webpack-dev-server: + optional: true + checksum: 050a930b63653ae0002e135cc9b0810483dd0857acd8e7ae2f41011f48f8856a150dd60c787105597ef8814541031779be1dc015ef637d70a7524d373cbbf346 + languageName: node + linkType: hard + +"@xtuc/ieee754@npm:^1.2.0": + version: 1.2.0 + resolution: "@xtuc/ieee754@npm:1.2.0" + checksum: ac56d4ca6e17790f1b1677f978c0c6808b1900a5b138885d3da21732f62e30e8f0d9120fcf8f6edfff5100ca902b46f8dd7c1e3f903728634523981e80e2885a + languageName: node + linkType: hard + +"@xtuc/long@npm:4.2.2": + version: 4.2.2 + resolution: "@xtuc/long@npm:4.2.2" + checksum: 8ed0d477ce3bc9c6fe2bf6a6a2cc316bb9c4127c5a7827bae947fa8ec34c7092395c5a283cc300c05b5fa01cbbfa1f938f410a7bf75db7c7846fea41949989ec + languageName: node + linkType: hard + +"JSONStream@npm:^1.0.3, JSONStream@npm:^1.0.4, JSONStream@npm:^1.3.1, JSONStream@npm:^1.3.5": + version: 1.3.5 + resolution: "JSONStream@npm:1.3.5" + dependencies: + jsonparse: ^1.2.0 + through: ">=2.2.7 <3" + bin: + JSONStream: ./bin.js + checksum: 2605fa124260c61bad38bb65eba30d2f72216a78e94d0ab19b11b4e0327d572b8d530c0c9cc3b0764f727ad26d39e00bf7ebad57781ca6368394d73169c59e46 + languageName: node + linkType: hard + +"abbrev@npm:1": + version: 1.1.1 + resolution: "abbrev@npm:1.1.1" + checksum: a4a97ec07d7ea112c517036882b2ac22f3109b7b19077dc656316d07d308438aac28e4d9746dc4d84bf6b1e75b4a7b0a5f3cb30592419f128ca9a8cee3bcfa17 + languageName: node + linkType: hard + +"abstract-leveldown@npm:~6.2.1": + version: 6.2.3 + resolution: "abstract-leveldown@npm:6.2.3" + dependencies: + buffer: ^5.5.0 + immediate: ^3.2.3 + level-concat-iterator: ~2.0.0 + level-supports: ~1.0.0 + xtend: ~4.0.0 + checksum: 00202b2eb7955dd7bc04f3e44d225e60160cedb8f96fe6ae0e6dca9c356d57071f001ece8ae1d53f48095c4c036d92b3440f2bc7666730610ddea030f9fbde4a + languageName: node + linkType: hard + +"accepts@npm:~1.3.4": + version: 1.3.7 + resolution: "accepts@npm:1.3.7" + dependencies: + mime-types: ~2.1.24 + negotiator: 0.6.2 + checksum: 27fc8060ffc69481ff6719cd3ee06387d2b88381cb0ce626f087781bbd02201a645a9febc8e7e7333558354b33b1d2f922ad13560be4ec1b7ba9e76fc1c1241d + languageName: node + linkType: hard + +"acorn-import-assertions@npm:^1.7.6": + version: 1.8.0 + resolution: "acorn-import-assertions@npm:1.8.0" + peerDependencies: + acorn: ^8 + checksum: 5c4cf7c850102ba7ae0eeae0deb40fb3158c8ca5ff15c0bca43b5c47e307a1de3d8ef761788f881343680ea374631ae9e9615ba8876fee5268dbe068c98bcba6 + languageName: node + linkType: hard + +"acorn-jsx@npm:^5.3.1": + version: 5.3.2 + resolution: "acorn-jsx@npm:5.3.2" + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: c3d3b2a89c9a056b205b69530a37b972b404ee46ec8e5b341666f9513d3163e2a4f214a71f4dfc7370f5a9c07472d2fd1c11c91c3f03d093e37637d95da98950 + languageName: node + linkType: hard + +"acorn-node@npm:^1.2.0, acorn-node@npm:^1.3.0, acorn-node@npm:^1.5.2, acorn-node@npm:^1.6.1": + version: 1.8.2 + resolution: "acorn-node@npm:1.8.2" + dependencies: + acorn: ^7.0.0 + acorn-walk: ^7.0.0 + xtend: ^4.0.2 + checksum: 02e1564a1ccf8bd1fcefcd01235398af4a9effaf032c5397994ddd275590a72894cb3e26e4b82579ccdda1e48ade7486aef61e771ddae3563ca452b927f443d8 + languageName: node + linkType: hard + +"acorn-walk@npm:^7.0.0": + version: 7.2.0 + resolution: "acorn-walk@npm:7.2.0" + checksum: 9252158a79b9d92f1bc0dd6acc0fcfb87a67339e84bcc301bb33d6078936d27e35d606b4d35626d2962cd43c256d6f27717e70cbe15c04fff999ab0b2260b21f + languageName: node + linkType: hard + +"acorn-walk@npm:^8.1.1": + version: 8.2.0 + resolution: "acorn-walk@npm:8.2.0" + checksum: 1715e76c01dd7b2d4ca472f9c58968516a4899378a63ad5b6c2d668bba8da21a71976c14ec5f5b75f887b6317c4ae0b897ab141c831d741dc76024d8745f1ad1 + languageName: node + linkType: hard + +"acorn@npm:^7.0.0, acorn@npm:^7.4.0": + version: 7.4.1 + resolution: "acorn@npm:7.4.1" + bin: + acorn: bin/acorn + checksum: 1860f23c2107c910c6177b7b7be71be350db9e1080d814493fae143ae37605189504152d1ba8743ba3178d0b37269ce1ffc42b101547fdc1827078f82671e407 + languageName: node + linkType: hard + +"acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.6.0": + version: 8.6.0 + resolution: "acorn@npm:8.6.0" + bin: + acorn: bin/acorn + checksum: 9d0de73b73cb6ea8ccd8263a8144d9e2c4b6af90ea0c429997538af0ebbe83c5addecee814b2a7f91f7f615d0bd1547cc7137b3fa236ce058adc64feccee850b + languageName: node + linkType: hard + +"add-stream@npm:^1.0.0": + version: 1.0.0 + resolution: "add-stream@npm:1.0.0" + checksum: 3e9e8b0b8f0170406d7c3a9a39bfbdf419ccccb0fd2a396338c0fda0a339af73bf738ad414fc520741de74517acf0dd92b4a36fd3298a47fd5371eee8f2c5a06 + languageName: node + linkType: hard + +"agent-base@npm:6, agent-base@npm:^6.0.2": + version: 6.0.2 + resolution: "agent-base@npm:6.0.2" + dependencies: + debug: 4 + checksum: f52b6872cc96fd5f622071b71ef200e01c7c4c454ee68bc9accca90c98cfb39f2810e3e9aa330435835eedc8c23f4f8a15267f67c6e245d2b33757575bdac49d + languageName: node + linkType: hard + +"agentkeepalive@npm:^4.1.3, agentkeepalive@npm:^4.2.0": + version: 4.2.0 + resolution: "agentkeepalive@npm:4.2.0" + dependencies: + debug: ^4.1.0 + depd: ^1.1.2 + humanize-ms: ^1.2.1 + checksum: 89806f83ceebbcaabf6bd581a8dce4870910fd2a11f66df8f505b4cd4ce4ca5ab9e6eec8d11ce8531a6b60f6748b75b0775e0e2fa33871503ef00d535418a19a + languageName: node + linkType: hard + +"aggregate-error@npm:^3.0.0": + version: 3.1.0 + resolution: "aggregate-error@npm:3.1.0" + dependencies: + clean-stack: ^2.0.0 + indent-string: ^4.0.0 + checksum: 1101a33f21baa27a2fa8e04b698271e64616b886795fd43c31068c07533c7b3facfcaf4e9e0cab3624bd88f729a592f1c901a1a229c9e490eafce411a8644b79 + languageName: node + linkType: hard + +"ajv-formats@npm:^2.1.1": + version: 2.1.1 + resolution: "ajv-formats@npm:2.1.1" + dependencies: + ajv: ^8.0.0 + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + checksum: 4a287d937f1ebaad4683249a4c40c0fa3beed30d9ddc0adba04859026a622da0d317851316ea64b3680dc60f5c3c708105ddd5d5db8fe595d9d0207fd19f90b7 + languageName: node + linkType: hard + +"ajv-keywords@npm:^3.5.2": + version: 3.5.2 + resolution: "ajv-keywords@npm:3.5.2" + peerDependencies: + ajv: ^6.9.1 + checksum: 7dc5e5931677a680589050f79dcbe1fefbb8fea38a955af03724229139175b433c63c68f7ae5f86cf8f65d55eb7c25f75a046723e2e58296707617ca690feae9 + languageName: node + linkType: hard + +"ajv-keywords@npm:^5.0.0": + version: 5.0.0 + resolution: "ajv-keywords@npm:5.0.0" + dependencies: + fast-deep-equal: ^3.1.3 + peerDependencies: + ajv: ^8.0.0 + checksum: 239dd46383a861f9e1dda1f463542ddfa07b4aed886eccb2a4328672c886030b5fdbb7869e0e293ba5549c9b86b23b40fa0e3c0785047e081302f00e41b1e4c1 + languageName: node + linkType: hard + +"ajv@npm:^6.10.0, ajv@npm:^6.10.2, ajv@npm:^6.12.3, ajv@npm:^6.12.4, ajv@npm:^6.12.5": + version: 6.12.6 + resolution: "ajv@npm:6.12.6" + 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 + checksum: 874972efe5c4202ab0a68379481fbd3d1b5d0a7bd6d3cc21d40d3536ebff3352a2a1fabb632d4fd2cc7fe4cbdcd5ed6782084c9bbf7f32a1536d18f9da5007d4 + languageName: node + linkType: hard + +"ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.6.0": + version: 8.8.1 + resolution: "ajv@npm:8.8.1" + dependencies: + fast-deep-equal: ^3.1.1 + json-schema-traverse: ^1.0.0 + require-from-string: ^2.0.2 + uri-js: ^4.2.2 + checksum: 1d586cea81b266f5f984c3a9f392a70f59181eb895ecb3463c4fc5c6acd5a4aefbe28f6d361dec4b04078fa6ec8343113cc8abdf577c8b99790d30ef71eea6b2 + languageName: node + linkType: hard + +"ansi-align@npm:^3.0.0": + version: 3.0.1 + resolution: "ansi-align@npm:3.0.1" + dependencies: + string-width: ^4.1.0 + checksum: 6abfa08f2141d231c257162b15292467081fa49a208593e055c866aa0455b57f3a86b5a678c190c618faa79b4c59e254493099cb700dd9cf2293c6be2c8f5d8d + languageName: node + linkType: hard + +"ansi-colors@npm:4.1.1, ansi-colors@npm:^4.1.1": + version: 4.1.1 + resolution: "ansi-colors@npm:4.1.1" + checksum: 138d04a51076cb085da0a7e2d000c5c0bb09f6e772ed5c65c53cb118d37f6c5f1637506d7155fb5f330f0abcf6f12fa2e489ac3f8cdab9da393bf1bb4f9a32b0 + languageName: node + linkType: hard + +"ansi-escapes@npm:^3.1.0": + version: 3.2.0 + resolution: "ansi-escapes@npm:3.2.0" + checksum: 0f94695b677ea742f7f1eed961f7fd8d05670f744c6ad1f8f635362f6681dcfbc1575cb05b43abc7bb6d67e25a75fb8c7ea8f2a57330eb2c76b33f18cb2cef0a + languageName: node + linkType: hard + +"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": + version: 4.3.2 + resolution: "ansi-escapes@npm:4.3.2" + dependencies: + type-fest: ^0.21.3 + checksum: 93111c42189c0a6bed9cdb4d7f2829548e943827ee8479c74d6e0b22ee127b2a21d3f8b5ca57723b8ef78ce011fbfc2784350eb2bde3ccfccf2f575fa8489815 + languageName: node + linkType: hard + +"ansi-regex@npm:^2.0.0": + version: 2.1.1 + resolution: "ansi-regex@npm:2.1.1" + checksum: 190abd03e4ff86794f338a31795d262c1dfe8c91f7e01d04f13f646f1dcb16c5800818f886047876f1272f065570ab86b24b99089f8b68a0e11ff19aed4ca8f1 + languageName: node + linkType: hard + +"ansi-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "ansi-regex@npm:3.0.0" + checksum: 2ad11c416f81c39f5c65eafc88cf1d71aa91d76a2f766e75e457c2a3c43e8a003aadbf2966b61c497aa6a6940a36412486c975b3270cdfc3f413b69826189ec3 + languageName: node + linkType: hard + +"ansi-regex@npm:^4.1.0": + version: 4.1.0 + resolution: "ansi-regex@npm:4.1.0" + checksum: 97aa4659538d53e5e441f5ef2949a3cffcb838e57aeaad42c4194e9d7ddb37246a6526c4ca85d3940a9d1e19b11cc2e114530b54c9d700c8baf163c31779baf8 + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b + languageName: node + linkType: hard + +"ansi-split@npm:^1.0.1": + version: 1.0.1 + resolution: "ansi-split@npm:1.0.1" + dependencies: + ansi-regex: ^3.0.0 + checksum: 301b98e935222273f668e8b7587fc1fbbc8a48be14e08899ad3be92327a78f584945fc745201bff90819cb687132945114bb7f9bd44d055197dfc983850ce23c + languageName: node + linkType: hard + +"ansi-styles@npm:^2.2.1": + version: 2.2.1 + resolution: "ansi-styles@npm:2.2.1" + checksum: ebc0e00381f2a29000d1dac8466a640ce11943cef3bda3cd0020dc042e31e1058ab59bf6169cd794a54c3a7338a61ebc404b7c91e004092dd20e028c432c9c2c + languageName: node + linkType: hard + +"ansi-styles@npm:^3.0.0, ansi-styles@npm:^3.2.0, ansi-styles@npm:^3.2.1": + version: 3.2.1 + resolution: "ansi-styles@npm:3.2.1" + dependencies: + color-convert: ^1.9.0 + checksum: d85ade01c10e5dd77b6c89f34ed7531da5830d2cb5882c645f330079975b716438cd7ebb81d0d6e6b4f9c577f19ae41ab55f07f19786b02f9dfd9e0377395665 + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0, ansi-styles@npm:^4.2.0, ansi-styles@npm:^4.2.1": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: ^2.0.1 + checksum: 513b44c3b2105dd14cc42a19271e80f386466c4be574bccf60b627432f9198571ebf4ab1e4c3ba17347658f4ee1711c163d574248c0c1cdc2d5917a0ad582ec4 + languageName: node + linkType: hard + +"ansi-styles@npm:^5.0.0": + version: 5.2.0 + resolution: "ansi-styles@npm:5.2.0" + checksum: d7f4e97ce0623aea6bc0d90dcd28881ee04cba06c570b97fd3391bd7a268eedfd9d5e2dd4fdcbdd82b8105df5faf6f24aaedc08eaf3da898e702db5948f63469 + languageName: node + linkType: hard + +"ansicolors@npm:~0.3.2": + version: 0.3.2 + resolution: "ansicolors@npm:0.3.2" + checksum: e84fae7ebc27ac96d9dbb57f35f078cd6dde1b7046b0f03f73dcefc9fbb1f2e82e3685d083466aded8faf038f9fa9ebb408d215282bcd7aaa301d5ac3c486815 + languageName: node + linkType: hard + +"anymatch@npm:~3.1.2": + version: 3.1.2 + resolution: "anymatch@npm:3.1.2" + dependencies: + normalize-path: ^3.0.0 + picomatch: ^2.0.4 + checksum: 985163db2292fac9e5a1e072bf99f1b5baccf196e4de25a0b0b81865ebddeb3b3eb4480734ef0a2ac8c002845396b91aa89121f5b84f93981a4658164a9ec6e9 + languageName: node + linkType: hard + +"append-transform@npm:^2.0.0": + version: 2.0.0 + resolution: "append-transform@npm:2.0.0" + dependencies: + default-require-extensions: ^3.0.0 + checksum: f26f393bf7a428fd1bb18f2758a819830a582243310c5170edb3f98fdc5a535333d02b952f7c2d9b14522bd8ead5b132a0b15000eca18fa9f49172963ebbc231 + languageName: node + linkType: hard + +"aproba@npm:^1.0.3": + version: 1.2.0 + resolution: "aproba@npm:1.2.0" + checksum: 0fca141966559d195072ed047658b6e6c4fe92428c385dd38e288eacfc55807e7b4989322f030faff32c0f46bb0bc10f1e0ac32ec22d25315a1e5bbc0ebb76dc + languageName: node + linkType: hard + +"aproba@npm:^1.0.3 || ^2.0.0": + version: 2.0.0 + resolution: "aproba@npm:2.0.0" + checksum: 5615cadcfb45289eea63f8afd064ab656006361020e1735112e346593856f87435e02d8dcc7ff0d11928bc7d425f27bc7c2a84f6c0b35ab0ff659c814c138a24 + languageName: node + linkType: hard + +"archy@npm:^1.0.0": + version: 1.0.0 + resolution: "archy@npm:1.0.0" + checksum: 504ae7af655130bab9f471343cfdb054feaec7d8e300e13348bc9fe9e660f83d422e473069584f73233c701ae37d1c8452ff2522f2a20c38849e0f406f1732ac + languageName: node + linkType: hard + +"are-we-there-yet@npm:^2.0.0": + version: 2.0.0 + resolution: "are-we-there-yet@npm:2.0.0" + dependencies: + delegates: ^1.0.0 + readable-stream: ^3.6.0 + checksum: 6c80b4fd04ecee6ba6e737e0b72a4b41bdc64b7d279edfc998678567ff583c8df27e27523bc789f2c99be603ffa9eaa612803da1d886962d2086e7ff6fa90c7c + languageName: node + linkType: hard + +"are-we-there-yet@npm:^3.0.0": + version: 3.0.0 + resolution: "are-we-there-yet@npm:3.0.0" + dependencies: + delegates: ^1.0.0 + readable-stream: ^3.6.0 + checksum: 348edfdd931b0b50868b55402c01c3f64df1d4c229ab6f063539a5025fd6c5f5bb8a0cab409bbed8d75d34762d22aa91b7c20b4204eb8177063158d9ba792981 + languageName: node + linkType: hard + +"are-we-there-yet@npm:~1.1.2": + version: 1.1.7 + resolution: "are-we-there-yet@npm:1.1.7" + dependencies: + delegates: ^1.0.0 + readable-stream: ^2.0.6 + checksum: 70d251719c969b2745bfe5ddf3ebaefa846a636e90a6d5212573676af5d6670e15457761d4725731e19cbebdce42c4ab0cbedf23ab047f2a08274985aa10a3c7 + languageName: node + linkType: hard + +"arg@npm:^4.1.0": + version: 4.1.3 + resolution: "arg@npm:4.1.3" + checksum: 544af8dd3f60546d3e4aff084d451b96961d2267d668670199692f8d054f0415d86fc5497d0e641e91546f0aa920e7c29e5250e99fc89f5552a34b5d93b77f43 + languageName: node + linkType: hard + +"argparse@npm:^1.0.7": + version: 1.0.10 + resolution: "argparse@npm:1.0.10" + dependencies: + sprintf-js: ~1.0.2 + checksum: 7ca6e45583a28de7258e39e13d81e925cfa25d7d4aacbf806a382d3c02fcb13403a07fb8aeef949f10a7cfe4a62da0e2e807b348a5980554cc28ee573ef95945 + languageName: node + linkType: hard + +"argparse@npm:^2.0.1": + version: 2.0.1 + resolution: "argparse@npm:2.0.1" + checksum: 83644b56493e89a254bae05702abf3a1101b4fa4d0ca31df1c9985275a5a5bd47b3c27b7fa0b71098d41114d8ca000e6ed90cad764b306f8a503665e4d517ced + languageName: node + linkType: hard + +"args@npm:^5.0.1": + version: 5.0.1 + resolution: "args@npm:5.0.1" + dependencies: + camelcase: 5.0.0 + chalk: 2.4.2 + leven: 2.1.0 + mri: 1.1.4 + checksum: 51e2a05f32d15b8e292f000e6b232118df61b8f4fd446b17bb4e99df9ab47fe2c4a01924d7f967a6f08e82f9c19be277b08ed22bceff058aca849144ef8efed3 + languageName: node + linkType: hard + +"array-differ@npm:^3.0.0": + version: 3.0.0 + resolution: "array-differ@npm:3.0.0" + checksum: 117edd9df5c1530bd116c6e8eea891d4bd02850fd89b1b36e532b6540e47ca620a373b81feca1c62d1395d9ae601516ba538abe5e8172d41091da2c546b05fb7 + languageName: node + linkType: hard + +"array-ify@npm:^1.0.0": + version: 1.0.0 + resolution: "array-ify@npm:1.0.0" + checksum: c0502015b319c93dd4484f18036bcc4b654eb76a4aa1f04afbcef11ac918859bb1f5d71ba1f0f1141770db9eef1a4f40f1761753650873068010bbf7bcdae4a4 + languageName: node + linkType: hard + +"array-includes@npm:^3.1.4": + version: 3.1.4 + resolution: "array-includes@npm:3.1.4" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.3 + es-abstract: ^1.19.1 + get-intrinsic: ^1.1.1 + is-string: ^1.0.7 + checksum: 69967c38c52698f84b50a7aed5554aadc89c6ac6399b6d92ad061a5952f8423b4bba054c51d40963f791dfa294d7247cdd7988b6b1f2c5861477031c6386e1c0 + languageName: node + linkType: hard + +"array-union@npm:^2.1.0": + version: 2.1.0 + resolution: "array-union@npm:2.1.0" + checksum: 5bee12395cba82da674931df6d0fea23c4aa4660cb3b338ced9f828782a65caa232573e6bf3968f23e0c5eb301764a382cef2f128b170a9dc59de0e36c39f98d + languageName: node + linkType: hard + +"array.prototype.flat@npm:^1.2.5": + version: 1.2.5 + resolution: "array.prototype.flat@npm:1.2.5" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.3 + es-abstract: ^1.19.0 + checksum: 9cc6414b111abfc7717e39546e4887b1e5ec74df8f1618d83425deaa95752bf05d475d1d241253b4d88d4a01f8e1bc84845ad5b7cc2047f8db2f614512acd40e + languageName: node + linkType: hard + +"arrify@npm:^1.0.0, arrify@npm:^1.0.1": + version: 1.0.1 + resolution: "arrify@npm:1.0.1" + checksum: 745075dd4a4624ff0225c331dacb99be501a515d39bcb7c84d24660314a6ec28e68131b137e6f7e16318170842ce97538cd298fc4cd6b2cc798e0b957f2747e7 + languageName: node + linkType: hard + +"arrify@npm:^2.0.1": + version: 2.0.1 + resolution: "arrify@npm:2.0.1" + checksum: 067c4c1afd182806a82e4c1cb8acee16ab8b5284fbca1ce29408e6e91281c36bb5b612f6ddfbd40a0f7a7e0c75bf2696eb94c027f6e328d6e9c52465c98e4209 + languageName: node + linkType: hard + +"asap@npm:^2.0.0": + version: 2.0.6 + resolution: "asap@npm:2.0.6" + checksum: b296c92c4b969e973260e47523207cd5769abd27c245a68c26dc7a0fe8053c55bb04360237cb51cab1df52be939da77150ace99ad331fb7fb13b3423ed73ff3d + languageName: node + linkType: hard + +"asn1.js@npm:^5.2.0": + version: 5.4.1 + resolution: "asn1.js@npm:5.4.1" + dependencies: + bn.js: ^4.0.0 + inherits: ^2.0.1 + minimalistic-assert: ^1.0.0 + safer-buffer: ^2.1.0 + checksum: 3786a101ac6f304bd4e9a7df79549a7561950a13d4bcaec0c7790d44c80d147c1a94ba3d4e663673406064642a40b23fcd6c82a9952468e386c1a1376d747f9a + languageName: node + linkType: hard + +"asn1@npm:^0.2.4, asn1@npm:~0.2.3": + version: 0.2.6 + resolution: "asn1@npm:0.2.6" + dependencies: + safer-buffer: ~2.1.0 + checksum: 39f2ae343b03c15ad4f238ba561e626602a3de8d94ae536c46a4a93e69578826305366dc09fbb9b56aec39b4982a463682f259c38e59f6fa380cd72cd61e493d + languageName: node + linkType: hard + +"assert-browserify@npm:^2.0.0": + version: 2.0.0 + resolution: "assert-browserify@npm:2.0.0" + dependencies: + es6-object-assign: ^1.1.0 + is-nan: ^1.2.1 + object-is: ^1.0.1 + util: ^0.12.0 + checksum: 93c167293e54ad445db737b9a3d5b3ba75c20281b63ab9bfc41d4f80efac8860bbcee68046c9e96e17fa1cee60db296008855db59255245650fe61c6fce757c0 + languageName: node + linkType: hard + +"assert-plus@npm:1.0.0, assert-plus@npm:^1.0.0": + version: 1.0.0 + resolution: "assert-plus@npm:1.0.0" + checksum: 19b4340cb8f0e6a981c07225eacac0e9d52c2644c080198765d63398f0075f83bbc0c8e95474d54224e297555ad0d631c1dcd058adb1ddc2437b41a6b424ac64 + languageName: node + linkType: hard + +"assert@npm:^1.4.0": + version: 1.5.0 + resolution: "assert@npm:1.5.0" + dependencies: + object-assign: ^4.1.1 + util: 0.10.3 + checksum: 9be48435f726029ae7020c5888a3566bf4d617687aab280827f2e4029644b6515a9519ea10d018b342147c02faf73d9e9419e780e8937b3786ee4945a0ca71e5 + languageName: node + linkType: hard + +"assert@npm:^2.0.0": + version: 2.0.0 + resolution: "assert@npm:2.0.0" + dependencies: + es6-object-assign: ^1.1.0 + is-nan: ^1.2.1 + object-is: ^1.0.1 + util: ^0.12.0 + checksum: bb91f181a86d10588ee16c5e09c280f9811373974c29974cbe401987ea34e966699d7989a812b0e19377b511ea0bc627f5905647ce569311824848ede382cae8 + languageName: node + linkType: hard + +"assertion-error@npm:^1.1.0": + version: 1.1.0 + resolution: "assertion-error@npm:1.1.0" + checksum: fd9429d3a3d4fd61782eb3962ae76b6d08aa7383123fca0596020013b3ebd6647891a85b05ce821c47d1471ed1271f00b0545cf6a4326cf2fc91efcc3b0fbecf + languageName: node + linkType: hard + +"astral-regex@npm:^1.0.0": + version: 1.0.0 + resolution: "astral-regex@npm:1.0.0" + checksum: 93417fc0879531cd95ace2560a54df865c9461a3ac0714c60cbbaa5f1f85d2bee85489e78d82f70b911b71ac25c5f05fc5a36017f44c9bb33c701bee229ff848 + languageName: node + linkType: hard + +"astral-regex@npm:^2.0.0": + version: 2.0.0 + resolution: "astral-regex@npm:2.0.0" + checksum: 876231688c66400473ba505731df37ea436e574dd524520294cc3bbc54ea40334865e01fa0d074d74d036ee874ee7e62f486ea38bc421ee8e6a871c06f011766 + languageName: node + linkType: hard + +"async@npm:0.9.x": + version: 0.9.2 + resolution: "async@npm:0.9.2" + checksum: 87dbf129292b8a6c32a4e07f43f462498162aa86f404a7e11f978dbfdf75cfb163c26833684bb07b9d436083cd604cbbf730a57bfcbe436c6ae1ed266cdc56bb + languageName: node + linkType: hard + +"async@npm:^3.1.0, async@npm:^3.2.0": + version: 3.2.2 + resolution: "async@npm:3.2.2" + checksum: 90712c98df0c6d0ef0190f8bee9797bf6c7035a1317c9a036b80306a8d2246396b3ee356b4540ff349e29e625fafa25d4f04e11b6ac1c5f6b4c74c803e641137 + languageName: node + linkType: hard + +"async@npm:~1.5": + version: 1.5.2 + resolution: "async@npm:1.5.2" + checksum: fe5d6214d8f15bd51eee5ae8ec5079b228b86d2d595f47b16369dec2e11b3ff75a567bb5f70d12d79006665fbbb7ee0a7ec0e388524eefd454ecbe651c124ebd + languageName: node + linkType: hard + +"asynckit@npm:^0.4.0": + version: 0.4.0 + resolution: "asynckit@npm:0.4.0" + checksum: 7b78c451df768adba04e2d02e63e2d0bf3b07adcd6e42b4cf665cb7ce899bedd344c69a1dcbce355b5f972d597b25aaa1c1742b52cffd9caccb22f348114f6be + languageName: node + linkType: hard + +"at-least-node@npm:^1.0.0": + version: 1.0.0 + resolution: "at-least-node@npm:1.0.0" + checksum: 463e2f8e43384f1afb54bc68485c436d7622acec08b6fad269b421cb1d29cebb5af751426793d0961ed243146fe4dc983402f6d5a51b720b277818dbf6f2e49e + languageName: node + linkType: hard + +"atomic-sleep@npm:^1.0.0": + version: 1.0.0 + resolution: "atomic-sleep@npm:1.0.0" + checksum: b95275afb2f80732f22f43a60178430c468906a415a7ff18bcd0feeebc8eec3930b51250aeda91a476062a90e07132b43a1794e8d8ffcf9b650e8139be75fa36 + languageName: node + linkType: hard + +"available-typed-arrays@npm:^1.0.5": + version: 1.0.5 + resolution: "available-typed-arrays@npm:1.0.5" + checksum: 20eb47b3cefd7db027b9bbb993c658abd36d4edd3fe1060e83699a03ee275b0c9b216cc076ff3f2db29073225fb70e7613987af14269ac1fe2a19803ccc97f1a + languageName: node + linkType: hard + +"awilix@npm:^4.2.6": + version: 4.3.4 + resolution: "awilix@npm:4.3.4" + dependencies: + camel-case: ^4.1.2 + glob: ^7.1.6 + checksum: d8cd0afd03a7920f667805d79b0fe52e69ffb453a3e127d6d6b7fa82cdc17e21e88fa620556b14db66398d9ed3fe2da90e2780fd7029f04b3d4b78fa66f3efd8 + languageName: node + linkType: hard + +"aws-sdk@npm:^2.1069.0": + version: 2.1076.0 + resolution: "aws-sdk@npm:2.1076.0" + dependencies: + buffer: 4.9.2 + events: 1.1.1 + ieee754: 1.1.13 + jmespath: 0.16.0 + querystring: 0.2.0 + sax: 1.2.1 + url: 0.10.3 + uuid: 3.3.2 + xml2js: 0.4.19 + checksum: b618ff816886c4a43e9a684c05526fc3cfcf4da850b813d7defd1424705520f46b9427aac8eab8e6005f102fc26eed60445133e675e4503f11a2d55f8ed5f138 + languageName: node + linkType: hard + +"aws-sign2@npm:~0.7.0": + version: 0.7.0 + resolution: "aws-sign2@npm:0.7.0" + checksum: b148b0bb0778098ad8cf7e5fc619768bcb51236707ca1d3e5b49e41b171166d8be9fdc2ea2ae43d7decf02989d0aaa3a9c4caa6f320af95d684de9b548a71525 + languageName: node + linkType: hard + +"aws4@npm:^1.8.0": + version: 1.11.0 + resolution: "aws4@npm:1.11.0" + checksum: 5a00d045fd0385926d20ebebcfba5ec79d4482fe706f63c27b324d489a04c68edb0db99ed991e19eda09cb8c97dc2452059a34d97545cebf591d7a2b5a10999f + languageName: node + linkType: hard + +"axios@npm:^0.21.1": + version: 0.21.4 + resolution: "axios@npm:0.21.4" + dependencies: + follow-redirects: ^1.14.0 + checksum: 44245f24ac971e7458f3120c92f9d66d1fc695e8b97019139de5b0cc65d9b8104647db01e5f46917728edfc0cfd88eb30fc4c55e6053eef4ace76768ce95ff3c + languageName: node + linkType: hard + +"babel-eslint@npm:^10.1.0": + version: 10.1.0 + resolution: "babel-eslint@npm:10.1.0" + dependencies: + "@babel/code-frame": ^7.0.0 + "@babel/parser": ^7.7.0 + "@babel/traverse": ^7.7.0 + "@babel/types": ^7.7.0 + eslint-visitor-keys: ^1.0.0 + resolve: ^1.12.0 + peerDependencies: + eslint: ">= 4.12.1" + checksum: bdc1f62b6b0f9c4d5108c96d835dad0c0066bc45b7c020fcb2d6a08107cf69c9217a99d3438dbd701b2816896190c4283ba04270ed9a8349ee07bd8dafcdc050 + languageName: node + linkType: hard + +"babel-loader@npm:^8.2.2": + version: 8.2.3 + resolution: "babel-loader@npm:8.2.3" + dependencies: + find-cache-dir: ^3.3.1 + loader-utils: ^1.4.0 + make-dir: ^3.1.0 + schema-utils: ^2.6.5 + peerDependencies: + "@babel/core": ^7.0.0 + webpack: ">=2" + checksum: 78e1e1a91954d644b6ce66366834d4d245febbc0fde33e4e2831725e83d6e760d12b3a78e9534ce92af69067bef1d9d9674df36d8c1f20ee127bc2354b2203ba + languageName: node + linkType: hard + +"babel-plugin-dynamic-import-node@npm:^2.3.3": + version: 2.3.3 + resolution: "babel-plugin-dynamic-import-node@npm:2.3.3" + dependencies: + object.assign: ^4.1.0 + checksum: c9d24415bcc608d0db7d4c8540d8002ac2f94e2573d2eadced137a29d9eab7e25d2cbb4bc6b9db65cf6ee7430f7dd011d19c911a9a778f0533b4a05ce8292c9b + languageName: node + linkType: hard + +"babel-plugin-polyfill-corejs2@npm:^0.3.0": + version: 0.3.0 + resolution: "babel-plugin-polyfill-corejs2@npm:0.3.0" + dependencies: + "@babel/compat-data": ^7.13.11 + "@babel/helper-define-polyfill-provider": ^0.3.0 + semver: ^6.1.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ffede597982066221291fe7c48ec1f1dda2b4ed3ee3e715436320697f35368223e1275bf095769d0b0c1115b90031dc525dd81b8ee9f6c8972cf1d2e10ad2b7d + languageName: node + linkType: hard + +"babel-plugin-polyfill-corejs3@npm:^0.4.0": + version: 0.4.0 + resolution: "babel-plugin-polyfill-corejs3@npm:0.4.0" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.3.0 + core-js-compat: ^3.18.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 18dce9a09a608b4844bce468a1d7b3abfc8a2a4c0df317ad6eb5951c0c95f3d1cc99699d8e67642cdd629f5074499d481481ae5e203ce85b8ed73e8295e25da8 + languageName: node + linkType: hard + +"babel-plugin-polyfill-regenerator@npm:^0.3.0": + version: 0.3.0 + resolution: "babel-plugin-polyfill-regenerator@npm:0.3.0" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.3.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ecca4389fd557554efc6de834f84f7c85e83c348d5283de2032d35429bc7121ed6f336553d3d704021f9bef22fca339fbee560d3b0fb8bb1d4eca2fecaaeebcb + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 + languageName: node + linkType: hard + +"base-x@npm:^3.0.2": + version: 3.0.9 + resolution: "base-x@npm:3.0.9" + dependencies: + safe-buffer: ^5.0.1 + checksum: 957101d6fd09e1903e846fd8f69fd7e5e3e50254383e61ab667c725866bec54e5ece5ba49ce385128ae48f9ec93a26567d1d5ebb91f4d56ef4a9cc0d5a5481e8 + languageName: node + linkType: hard + +"base64-arraybuffer@npm:~1.0.1": + version: 1.0.1 + resolution: "base64-arraybuffer@npm:1.0.1" + checksum: 04b6fe6818b1c79774fa8aea169063521ad177f2ba04d2a4a0f00fca297d516319b551a3cda76050263da751b4ffb07d939fc1b5eb155f0e429659733e60afb0 + languageName: node + linkType: hard + +"base64-js@npm:^1.0.2, base64-js@npm:^1.3.1": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 + languageName: node + linkType: hard + +"base64id@npm:2.0.0, base64id@npm:~2.0.0": + version: 2.0.0 + resolution: "base64id@npm:2.0.0" + checksum: 581b1d37e6cf3738b7ccdd4d14fe2bfc5c238e696e2720ee6c44c183b838655842e22034e53ffd783f872a539915c51b0d4728a49c7cc678ac5a758e00d62168 + languageName: node + linkType: hard + +"bcrypt-pbkdf@npm:^1.0.0, bcrypt-pbkdf@npm:^1.0.2": + version: 1.0.2 + resolution: "bcrypt-pbkdf@npm:1.0.2" + dependencies: + tweetnacl: ^0.14.3 + checksum: 4edfc9fe7d07019609ccf797a2af28351736e9d012c8402a07120c4453a3b789a15f2ee1530dc49eee8f7eb9379331a8dd4b3766042b9e502f74a68e7f662291 + languageName: node + linkType: hard + +"before-after-hook@npm:^2.2.0": + version: 2.2.2 + resolution: "before-after-hook@npm:2.2.2" + checksum: dc2e1ffe389e5afbef2a46790b1b5a50247ed57aba67649cfa9ec2552d248cc9278f222e72fb5a8ff59bbb39d78fbaa97e7234ead0c6b5e8418b67a8644ce207 + languageName: node + linkType: hard + +"big.js@npm:^5.2.2": + version: 5.2.2 + resolution: "big.js@npm:5.2.2" + checksum: b89b6e8419b097a8fb4ed2399a1931a68c612bce3cfd5ca8c214b2d017531191070f990598de2fc6f3f993d91c0f08aa82697717f6b3b8732c9731866d233c9e + languageName: node + linkType: hard + +"bignumber.js@npm:^9.0.1": + version: 9.0.1 + resolution: "bignumber.js@npm:9.0.1" + checksum: 6e72f6069d9db32fc8d27561164de9f811b15f9144be61f323d8b36150a239eea50c92e20ba38af2ba5e717af10b8ef12db8f9948fe2ff02bf17ede5239d15d3 + languageName: node + linkType: hard + +"bin-links@npm:^3.0.0": + version: 3.0.0 + resolution: "bin-links@npm:3.0.0" + dependencies: + cmd-shim: ^4.0.1 + mkdirp-infer-owner: ^2.0.0 + npm-normalize-package-bin: ^1.0.0 + read-cmd-shim: ^2.0.0 + rimraf: ^3.0.0 + write-file-atomic: ^4.0.0 + checksum: 61cec54a913bf1897c29db1ac277c022cc97a7189a55b2ed7343e75955800e4ec149e76b134f9c685947e37196282d652bf1f9fa893919283827b61ca289b170 + languageName: node + linkType: hard + +"binary-extensions@npm:^2.0.0": + version: 2.2.0 + resolution: "binary-extensions@npm:2.2.0" + checksum: ccd267956c58d2315f5d3ea6757cf09863c5fc703e50fbeb13a7dc849b812ef76e3cf9ca8f35a0c48498776a7478d7b4a0418e1e2b8cb9cb9731f2922aaad7f8 + languageName: node + linkType: hard + +"binaryextensions@npm:^4.15.0, binaryextensions@npm:^4.16.0": + version: 4.18.0 + resolution: "binaryextensions@npm:4.18.0" + checksum: 6fe92a9004c5a7c08a8d49ac4087581043a0d195e76c288619c13e9232d0b80543f01da0037bb0f1b02830c174721fcad92bdfe76c84295cc8f308ee3b74d184 + languageName: node + linkType: hard + +"bl@npm:^1.2.3": + version: 1.2.3 + resolution: "bl@npm:1.2.3" + dependencies: + readable-stream: ^2.3.5 + safe-buffer: ^5.1.1 + checksum: 123f097989ce2fa9087ce761cd41176aaaec864e28f7dfe5c7dab8ae16d66d9844f849c3ad688eb357e3c5e4f49b573e3c0780bb8bc937206735a3b6f8569a5f + languageName: node + linkType: hard + +"bl@npm:^2.2.1": + version: 2.2.1 + resolution: "bl@npm:2.2.1" + dependencies: + readable-stream: ^2.3.5 + safe-buffer: ^5.1.1 + checksum: 4f5d9b258919646a8d02f1731379e53b6f6309e34596ae02afbc3aeb183910bd2d0b70681f889b7c620ca48f65dc1cd0992ee1266c90d6d7c3be60688d141233 + languageName: node + linkType: hard + +"bl@npm:^4.0.3, bl@npm:^4.1.0": + version: 4.1.0 + resolution: "bl@npm:4.1.0" + dependencies: + buffer: ^5.5.0 + inherits: ^2.0.4 + readable-stream: ^3.4.0 + checksum: 9e8521fa7e83aa9427c6f8ccdcba6e8167ef30cc9a22df26effcc5ab682ef91d2cbc23a239f945d099289e4bbcfae7a192e9c28c84c6202e710a0dfec3722662 + languageName: node + linkType: hard + +"blake3@npm:^2.1.4": + version: 2.1.7 + resolution: "blake3@npm:2.1.7" + checksum: 5960e1cb36866edb718566684e5899323d7ee22e76e9e741c9d6b21f1957f7fd151c5642f67ee4e4901f8e4973c442fe9c2cb05175bd20c13c3744a15c748e69 + languageName: node + linkType: hard + +"bloom-filter@npm:^0.2.0": + version: 0.2.0 + resolution: "bloom-filter@npm:0.2.0" + checksum: 0a19b85cbdbd44baf5511c85844b220a8f2a03591700bbffae223df1176f749aefe31d4afdd0bc647a8d17c49f36aa3577978792154e1e442a2b0e633aab2ab5 + languageName: node + linkType: hard + +"bls-signatures@npm:^0.2.5": + version: 0.2.5 + resolution: "bls-signatures@npm:0.2.5" + checksum: 472d697f09b1e7701311271150042b4923f0b1e1cd081ca2e7580242902e23ce0ecd97b3b448ea431114eee6a86081a226d80bd8a2a6aea73f9704da8e0dd668 + languageName: node + linkType: hard + +"bluebird@npm:^3.4.7, bluebird@npm:^3.7.2": + version: 3.7.2 + resolution: "bluebird@npm:3.7.2" + checksum: 869417503c722e7dc54ca46715f70e15f4d9c602a423a02c825570862d12935be59ed9c7ba34a9b31f186c017c23cac6b54e35446f8353059c101da73eac22ef + languageName: node + linkType: hard + +"bn.js@npm:4.12.0": + version: 4.12.0 + resolution: "bn.js@npm:4.12.0" + checksum: 39afb4f15f4ea537b55eaf1446c896af28ac948fdcf47171961475724d1bb65118cca49fa6e3d67706e4790955ec0e74de584e45c8f1ef89f46c812bee5b5a12 + languageName: node + linkType: hard + +"body-parser@npm:^1.19.0": + version: 1.19.0 + resolution: "body-parser@npm:1.19.0" + dependencies: + bytes: 3.1.0 + content-type: ~1.0.4 + debug: 2.6.9 + depd: ~1.1.2 + http-errors: 1.7.2 + iconv-lite: 0.4.24 + on-finished: ~2.3.0 + qs: 6.7.0 + raw-body: 2.4.0 + type-is: ~1.6.17 + checksum: 490231b4c89bbd43112762f7ba8e5342c174a6c9f64284a3b0fcabf63277e332f8316765596f1e5b15e4f3a6cf0422e005f4bb3149ed3a224bb025b7a36b9ac1 + languageName: node + linkType: hard + +"boxen@npm:^5.0.0": + version: 5.1.2 + resolution: "boxen@npm:5.1.2" + dependencies: + ansi-align: ^3.0.0 + camelcase: ^6.2.0 + chalk: ^4.1.0 + cli-boxes: ^2.2.1 + string-width: ^4.2.2 + type-fest: ^0.20.2 + widest-line: ^3.1.0 + wrap-ansi: ^7.0.0 + checksum: 82d03e42a72576ff235123f17b7c505372fe05c83f75f61e7d4fa4bcb393897ec95ce766fecb8f26b915f0f7a7227d66e5ec7cef43f5b2bd9d3aeed47ec55877 + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.11 + resolution: "brace-expansion@npm:1.1.11" + dependencies: + balanced-match: ^1.0.0 + concat-map: 0.0.1 + checksum: faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07 + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.1": + version: 2.0.1 + resolution: "brace-expansion@npm:2.0.1" + dependencies: + balanced-match: ^1.0.0 + checksum: a61e7cd2e8a8505e9f0036b3b6108ba5e926b4b55089eeb5550cd04a471fe216c96d4fe7e4c7f995c728c554ae20ddfc4244cad10aef255e72b62930afd233d1 + languageName: node + linkType: hard + +"braces@npm:^3.0.1, braces@npm:^3.0.2, braces@npm:~3.0.2": + version: 3.0.2 + resolution: "braces@npm:3.0.2" + dependencies: + fill-range: ^7.0.1 + checksum: e2a8e769a863f3d4ee887b5fe21f63193a891c68b612ddb4b68d82d1b5f3ff9073af066c343e9867a393fe4c2555dcb33e89b937195feb9c1613d259edfcd459 + languageName: node + linkType: hard + +"brorand@npm:^1.0.1": + version: 1.1.0 + resolution: "brorand@npm:1.1.0" + checksum: 8a05c9f3c4b46572dec6ef71012b1946db6cae8c7bb60ccd4b7dd5a84655db49fe043ecc6272e7ef1f69dc53d6730b9e2a3a03a8310509a3d797a618cbee52be + languageName: node + linkType: hard + +"browser-pack@npm:^6.0.1": + version: 6.1.0 + resolution: "browser-pack@npm:6.1.0" + dependencies: + JSONStream: ^1.0.3 + combine-source-map: ~0.8.0 + defined: ^1.0.0 + safe-buffer: ^5.1.1 + through2: ^2.0.0 + umd: ^3.0.0 + bin: + browser-pack: bin/cmd.js + checksum: 9e5993d3eefb7c56a68cfc8810e59a2920481f93bdcb0a53e07b322f273f697cfeb3a2302aa7fc0f725d29be0e8cc629561f463f2c8b06e2958497869d42cc53 + languageName: node + linkType: hard + +"browser-resolve@npm:^2.0.0": + version: 2.0.0 + resolution: "browser-resolve@npm:2.0.0" + dependencies: + resolve: ^1.17.0 + checksum: 69225e73b555bd6d2a08fb93c7342cfcf3b5058b975099c52649cd5c3cec84c2066c5385084d190faedfb849684d9dabe10129f0cd401d1883572f2e6650f440 + languageName: node + linkType: hard + +"browser-stdout@npm:1.3.1": + version: 1.3.1 + resolution: "browser-stdout@npm:1.3.1" + checksum: b717b19b25952dd6af483e368f9bcd6b14b87740c3d226c2977a65e84666ffd67000bddea7d911f111a9b6ddc822b234de42d52ab6507bce4119a4cc003ef7b3 + languageName: node + linkType: hard + +"browserify-aes@npm:^1.0.0, browserify-aes@npm:^1.0.4": + version: 1.2.0 + resolution: "browserify-aes@npm:1.2.0" + dependencies: + buffer-xor: ^1.0.3 + cipher-base: ^1.0.0 + create-hash: ^1.1.0 + evp_bytestokey: ^1.0.3 + inherits: ^2.0.1 + safe-buffer: ^5.0.1 + checksum: 4a17c3eb55a2aa61c934c286f34921933086bf6d67f02d4adb09fcc6f2fc93977b47d9d884c25619144fccd47b3b3a399e1ad8b3ff5a346be47270114bcf7104 + languageName: node + linkType: hard + +"browserify-cipher@npm:^1.0.0": + version: 1.0.1 + resolution: "browserify-cipher@npm:1.0.1" + dependencies: + browserify-aes: ^1.0.4 + browserify-des: ^1.0.0 + evp_bytestokey: ^1.0.0 + checksum: 2d8500acf1ee535e6bebe808f7a20e4c3a9e2ed1a6885fff1facbfd201ac013ef030422bec65ca9ece8ffe82b03ca580421463f9c45af6c8415fd629f4118c13 + languageName: node + linkType: hard + +"browserify-des@npm:^1.0.0": + version: 1.0.2 + resolution: "browserify-des@npm:1.0.2" + dependencies: + cipher-base: ^1.0.1 + des.js: ^1.0.0 + inherits: ^2.0.1 + safe-buffer: ^5.1.2 + checksum: b15a3e358a1d78a3b62ddc06c845d02afde6fc826dab23f1b9c016e643e7b1fda41de628d2110b712f6a44fb10cbc1800bc6872a03ddd363fb50768e010395b7 + languageName: node + linkType: hard + +"browserify-rsa@npm:^4.0.0, browserify-rsa@npm:^4.0.1": + version: 4.1.0 + resolution: "browserify-rsa@npm:4.1.0" + dependencies: + bn.js: ^5.0.0 + randombytes: ^2.0.1 + checksum: 155f0c135873efc85620571a33d884aa8810e40176125ad424ec9d85016ff105a07f6231650914a760cca66f29af0494087947b7be34880dd4599a0cd3c38e54 + languageName: node + linkType: hard + +"browserify-sign@npm:^4.0.0": + version: 4.2.1 + resolution: "browserify-sign@npm:4.2.1" + dependencies: + bn.js: ^5.1.1 + browserify-rsa: ^4.0.1 + create-hash: ^1.2.0 + create-hmac: ^1.1.7 + elliptic: ^6.5.3 + inherits: ^2.0.4 + parse-asn1: ^5.1.5 + readable-stream: ^3.6.0 + safe-buffer: ^5.2.0 + checksum: 0221f190e3f5b2d40183fa51621be7e838d9caa329fe1ba773406b7637855f37b30f5d83e52ff8f244ed12ffe6278dd9983638609ed88c841ce547e603855707 + languageName: node + linkType: hard + +"browserify-zlib@npm:^0.2.0, browserify-zlib@npm:~0.2.0": + version: 0.2.0 + resolution: "browserify-zlib@npm:0.2.0" + dependencies: + pako: ~1.0.5 + checksum: 5cd9d6a665190fedb4a97dfbad8dabc8698d8a507298a03f42c734e96d58ca35d3c7d4085e283440bbca1cd1938cff85031728079bedb3345310c58ab1ec92d6 + languageName: node + linkType: hard + +"browserify@npm:^16.5.1": + version: 16.5.2 + resolution: "browserify@npm:16.5.2" + dependencies: + JSONStream: ^1.0.3 + assert: ^1.4.0 + browser-pack: ^6.0.1 + browser-resolve: ^2.0.0 + browserify-zlib: ~0.2.0 + buffer: ~5.2.1 + cached-path-relative: ^1.0.0 + concat-stream: ^1.6.0 + console-browserify: ^1.1.0 + constants-browserify: ~1.0.0 + crypto-browserify: ^3.0.0 + defined: ^1.0.0 + deps-sort: ^2.0.0 + domain-browser: ^1.2.0 + duplexer2: ~0.1.2 + events: ^2.0.0 + glob: ^7.1.0 + has: ^1.0.0 + htmlescape: ^1.1.0 + https-browserify: ^1.0.0 + inherits: ~2.0.1 + insert-module-globals: ^7.0.0 + labeled-stream-splicer: ^2.0.0 + mkdirp-classic: ^0.5.2 + module-deps: ^6.2.3 + os-browserify: ~0.3.0 + parents: ^1.0.1 + path-browserify: ~0.0.0 + process: ~0.11.0 + punycode: ^1.3.2 + querystring-es3: ~0.2.0 + read-only-stream: ^2.0.0 + readable-stream: ^2.0.2 + resolve: ^1.1.4 + shasum: ^1.0.0 + shell-quote: ^1.6.1 + stream-browserify: ^2.0.0 + stream-http: ^3.0.0 + string_decoder: ^1.1.1 + subarg: ^1.0.0 + syntax-error: ^1.1.1 + through2: ^2.0.0 + timers-browserify: ^1.0.1 + tty-browserify: 0.0.1 + url: ~0.11.0 + util: ~0.10.1 + vm-browserify: ^1.0.0 + xtend: ^4.0.0 + bin: + browserify: bin/cmd.js + checksum: 75dacf5c82355146b49a2febb3bf9f7898893931973cf901849791827e44782afcb562be7bc3a893d9022ae528fd6fccdf24fc8812cb5aa1b081bb7ce34c46b5 + languageName: node + linkType: hard + +"browserslist@npm:^4.14.5, browserslist@npm:^4.17.5, browserslist@npm:^4.17.6": + version: 4.18.1 + resolution: "browserslist@npm:4.18.1" + dependencies: + caniuse-lite: ^1.0.30001280 + electron-to-chromium: ^1.3.896 + escalade: ^3.1.1 + node-releases: ^2.0.1 + picocolors: ^1.0.0 + bin: + browserslist: cli.js + checksum: ae58322deef15960fc2e601d71bc081b571cfab6705999a3d24db5325b9cfadf5f676615f4460207a93e600549c33d60d37b4502007fe9e737b3cc19e20575d5 + languageName: node + linkType: hard + +"bs58@npm:=4.0.1, bs58@npm:^4.0.1": + version: 4.0.1 + resolution: "bs58@npm:4.0.1" + dependencies: + base-x: ^3.0.2 + checksum: b3c5365bb9e0c561e1a82f1a2d809a1a692059fae016be233a6127ad2f50a6b986467c3a50669ce4c18929dcccb297c5909314dd347a25a68c21b68eb3e95ac2 + languageName: node + linkType: hard + +"bson@npm:^1.1.4": + version: 1.1.6 + resolution: "bson@npm:1.1.6" + checksum: 75762c9b7e0b3156cb0f38c7eb9ffcade53f0b04ac87dece9cba38f6dc570d9af91251de6a8988b294063cfaa21894c60ac9e85c34176accb3674acb092d66a7 + languageName: node + linkType: hard + +"buffer-from@npm:^1.0.0, buffer-from@npm:^1.1.0": + version: 1.1.2 + resolution: "buffer-from@npm:1.1.2" + checksum: 0448524a562b37d4d7ed9efd91685a5b77a50672c556ea254ac9a6d30e3403a517d8981f10e565db24e8339413b43c97ca2951f10e399c6125a0d8911f5679bb + languageName: node + linkType: hard + +"buffer-reverse@npm:^1.0.1": + version: 1.0.1 + resolution: "buffer-reverse@npm:1.0.1" + checksum: e350872a89b17af0a7e1bd7a73239a535164f3f010b0800add44f2e52bd0511548dc5b96c20309effba969868c385023d2d02a0add6155f6a76da7b3073b77bd + languageName: node + linkType: hard + +"buffer-xor@npm:^1.0.3": + version: 1.0.3 + resolution: "buffer-xor@npm:1.0.3" + checksum: 10c520df29d62fa6e785e2800e586a20fc4f6dfad84bcdbd12e1e8a83856de1cb75c7ebd7abe6d036bbfab738a6cf18a3ae9c8e5a2e2eb3167ca7399ce65373a + languageName: node + linkType: hard + +"buffer@npm:4.9.2": + version: 4.9.2 + resolution: "buffer@npm:4.9.2" + dependencies: + base64-js: ^1.0.2 + ieee754: ^1.1.4 + isarray: ^1.0.0 + checksum: 8801bc1ba08539f3be70eee307a8b9db3d40f6afbfd3cf623ab7ef41dffff1d0a31de0addbe1e66e0ca5f7193eeb667bfb1ecad3647f8f1b0750de07c13295c3 + languageName: node + linkType: hard + +"buffer@npm:^5.5.0": + version: 5.7.1 + resolution: "buffer@npm:5.7.1" + dependencies: + base64-js: ^1.3.1 + ieee754: ^1.1.13 + checksum: e2cf8429e1c4c7b8cbd30834ac09bd61da46ce35f5c22a78e6c2f04497d6d25541b16881e30a019c6fd3154150650ccee27a308eff3e26229d788bbdeb08ab84 + languageName: node + linkType: hard + +"buffer@npm:^6.0.3": + version: 6.0.3 + resolution: "buffer@npm:6.0.3" + dependencies: + base64-js: ^1.3.1 + ieee754: ^1.2.1 + checksum: 5ad23293d9a731e4318e420025800b42bf0d264004c0286c8cc010af7a270c7a0f6522e84f54b9ad65cbd6db20b8badbfd8d2ebf4f80fa03dab093b89e68c3f9 + languageName: node + linkType: hard + +"buffer@npm:~5.2.1": + version: 5.2.1 + resolution: "buffer@npm:5.2.1" + dependencies: + base64-js: ^1.0.2 + ieee754: ^1.1.4 + checksum: aa3f25bb88d313b8317b436677b46e9e32db64ae397dd5a9d1f867da132985b857c71deaa36cc37666fdb955d8d0f66abeae9460aa7d9b2dca36a9da2f50d05e + languageName: node + linkType: hard + +"bufferutil@npm:^4.0.6": + version: 4.0.6 + resolution: "bufferutil@npm:4.0.6" + dependencies: + node-gyp: latest + node-gyp-build: ^4.3.0 + checksum: dd107560947445280af7820c3d0534127b911577d85d537e1d7e0aa30fd634853cef8a994d6e8aed3d81388ab1a20257de776164afe6a6af8e78f5f17968ebd6 + languageName: node + linkType: hard + +"builtin-status-codes@npm:^3.0.0": + version: 3.0.0 + resolution: "builtin-status-codes@npm:3.0.0" + checksum: 1119429cf4b0d57bf76b248ad6f529167d343156ebbcc4d4e4ad600484f6bc63002595cbb61b67ad03ce55cd1d3c4711c03bbf198bf24653b8392420482f3773 + languageName: node + linkType: hard + +"builtins@npm:^1.0.3": + version: 1.0.3 + resolution: "builtins@npm:1.0.3" + checksum: 47ce94f7eee0e644969da1f1a28e5f29bd2e48b25b2bbb61164c345881086e29464ccb1fb88dbc155ea26e8b1f5fc8a923b26c8c1ed0935b67b644d410674513 + languageName: node + linkType: hard + +"bytes@npm:3.1.0": + version: 3.1.0 + resolution: "bytes@npm:3.1.0" + checksum: 7c3b21c5d9d44ed455460d5d36a31abc6fa2ce3807964ba60a4b03fd44454c8cf07bb0585af83bfde1c5cc2ea4bbe5897bc3d18cd15e0acf25a3615a35aba2df + languageName: node + linkType: hard + +"cacache@npm:^15.0.3, cacache@npm:^15.0.5, cacache@npm:^15.2.0, cacache@npm:^15.3.0": + version: 15.3.0 + resolution: "cacache@npm:15.3.0" + dependencies: + "@npmcli/fs": ^1.0.0 + "@npmcli/move-file": ^1.0.1 + chownr: ^2.0.0 + fs-minipass: ^2.0.0 + glob: ^7.1.4 + infer-owner: ^1.0.4 + lru-cache: ^6.0.0 + minipass: ^3.1.1 + minipass-collect: ^1.0.2 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.2 + mkdirp: ^1.0.3 + p-map: ^4.0.0 + promise-inflight: ^1.0.1 + rimraf: ^3.0.2 + ssri: ^8.0.1 + tar: ^6.0.2 + unique-filename: ^1.1.1 + checksum: a07327c27a4152c04eb0a831c63c00390d90f94d51bb80624a66f4e14a6b6360bbf02a84421267bd4d00ca73ac9773287d8d7169e8d2eafe378d2ce140579db8 + languageName: node + linkType: hard + +"cacheable-request@npm:^6.0.0": + version: 6.1.0 + resolution: "cacheable-request@npm:6.1.0" + dependencies: + clone-response: ^1.0.2 + get-stream: ^5.1.0 + http-cache-semantics: ^4.0.0 + keyv: ^3.0.0 + lowercase-keys: ^2.0.0 + normalize-url: ^4.1.0 + responselike: ^1.0.2 + checksum: b510b237b18d17e89942e9ee2d2a077cb38db03f12167fd100932dfa8fc963424bfae0bfa1598df4ae16c944a5484e43e03df8f32105b04395ee9495e9e4e9f1 + languageName: node + linkType: hard + +"cached-path-relative@npm:^1.0.0, cached-path-relative@npm:^1.0.2": + version: 1.0.2 + resolution: "cached-path-relative@npm:1.0.2" + checksum: 643fa65a6522f975505d273c2027ff7632437e9be79bb7f02fa655ccb30cfe6e6219eff70b8ad73558806f6453bc18391623967ef2d065745fc4a1efd48c2a3e + languageName: node + linkType: hard + +"caching-transform@npm:^4.0.0": + version: 4.0.0 + resolution: "caching-transform@npm:4.0.0" + dependencies: + hasha: ^5.0.0 + make-dir: ^3.0.0 + package-hash: ^4.0.0 + write-file-atomic: ^3.0.0 + checksum: c4db6939533b677866808de67c32f0aaf8bf4fd3e3b8dc957e5d630c007c06b7f11512d44c38a38287fb068e931067e8da9019c34d787259a44121c9a6b87a1f + languageName: node + linkType: hard + +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind@npm:1.0.2" + dependencies: + function-bind: ^1.1.1 + get-intrinsic: ^1.0.2 + checksum: f8e31de9d19988a4b80f3e704788c4a2d6b6f3d17cfec4f57dc29ced450c53a49270dc66bf0fbd693329ee948dd33e6c90a329519aef17474a4d961e8d6426b0 + languageName: node + linkType: hard + +"call-me-maybe@npm:^1.0.1": + version: 1.0.1 + resolution: "call-me-maybe@npm:1.0.1" + checksum: d19e9d6ac2c6a83fb1215718b64c5e233f688ebebb603bdfe4af59cde952df1f2b648530fab555bf290ea910d69d7d9665ebc916e871e0e194f47c2e48e4886b + languageName: node + linkType: hard + +"callsites@npm:^3.0.0": + version: 3.1.0 + resolution: "callsites@npm:3.1.0" + checksum: 072d17b6abb459c2ba96598918b55868af677154bec7e73d222ef95a8fdb9bbf7dae96a8421085cdad8cd190d86653b5b6dc55a4484f2e5b2e27d5e0c3fc15b3 + languageName: node + linkType: hard + +"camel-case@npm:^4.1.2": + version: 4.1.2 + resolution: "camel-case@npm:4.1.2" + dependencies: + pascal-case: ^3.1.2 + tslib: ^2.0.3 + checksum: bcbd25cd253b3cbc69be3f535750137dbf2beb70f093bdc575f73f800acc8443d34fd52ab8f0a2413c34f1e8203139ffc88428d8863e4dfe530cfb257a379ad6 + languageName: node + linkType: hard + +"camelcase-keys@npm:^6.2.2": + version: 6.2.2 + resolution: "camelcase-keys@npm:6.2.2" + dependencies: + camelcase: ^5.3.1 + map-obj: ^4.0.0 + quick-lru: ^4.0.1 + checksum: 43c9af1adf840471e54c68ab3e5fe8a62719a6b7dbf4e2e86886b7b0ff96112c945736342b837bd2529ec9d1c7d1934e5653318478d98e0cf22c475c04658e2a + languageName: node + linkType: hard + +"camelcase@npm:5.0.0": + version: 5.0.0 + resolution: "camelcase@npm:5.0.0" + checksum: 8bfe920e0472d79d34f0279da1391f155bcce7fc74c99b49dafae4f787396040a34f4023da837ab0b4372e63224b460f9524b495906863c38876faea9da53705 + languageName: node + linkType: hard + +"camelcase@npm:^5.0.0, camelcase@npm:^5.3.1": + version: 5.3.1 + resolution: "camelcase@npm:5.3.1" + checksum: e6effce26b9404e3c0f301498184f243811c30dfe6d0b9051863bd8e4034d09c8c2923794f280d6827e5aa055f6c434115ff97864a16a963366fb35fd673024b + languageName: node + linkType: hard + +"camelcase@npm:^6.0.0, camelcase@npm:^6.2.0": + version: 6.2.1 + resolution: "camelcase@npm:6.2.1" + checksum: d876272ef76391ebf8442fb7ea1d77e80ae179ce1339e021a8731b4895fd190dc19e148e045469cff5825d4c089089f3fff34d804d3f49115d55af97dd6ac0af + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001280": + version: 1.0.30001282 + resolution: "caniuse-lite@npm:1.0.30001282" + checksum: 62797fd756e88bfa01f0f983bea9de7814293b209456e8f0b20596b03d2880246f63dc90f947a1fa63f92806ebefbb86fc7811dbecb7839927886d07996938be + languageName: node + linkType: hard + +"cardinal@npm:^2.1.1": + version: 2.1.1 + resolution: "cardinal@npm:2.1.1" + dependencies: + ansicolors: ~0.3.2 + redeyed: ~2.1.0 + bin: + cdl: ./bin/cdl.js + checksum: e8d4ae46439cf8fed481c0efd267711ee91e199aa7821a9143e784ed94a6495accd01a0b36d84d377e8ee2cc9928a6c9c123b03be761c60b805f2c026b8a99ad + languageName: node + linkType: hard + +"cargo-cp-artifact@npm:^0.1.6": + version: 0.1.6 + resolution: "cargo-cp-artifact@npm:0.1.6" + bin: + cargo-cp-artifact: bin/cargo-cp-artifact.js + checksum: 2f5d2f3e73372c9d398746372e8d1f7b171f27c8d4e4e9f7de898ead93b29631d62ea26c1ae352fa2c1542e124c2c77ac8a60acc5634bb433133ee2c7c6f1e09 + languageName: node + linkType: hard + +"caseless@npm:~0.12.0": + version: 0.12.0 + resolution: "caseless@npm:0.12.0" + checksum: b43bd4c440aa1e8ee6baefee8063b4850fd0d7b378f6aabc796c9ec8cb26d27fb30b46885350777d9bd079c5256c0e1329ad0dc7c2817e0bb466810ebb353751 + languageName: node + linkType: hard + +"cbor@npm:^8.0.0, cbor@npm:^8.1.0": + version: 8.1.0 + resolution: "cbor@npm:8.1.0" + dependencies: + nofilter: ^3.1.0 + checksum: a90338435dc7b45cc01461af979e3bb6ddd4f2a08584c437586039cd5f2235014c06e49d664295debbfb3514d87b2f06728092ab6aa6175e2e85e9cd7dc0c1fd + languageName: node + linkType: hard + +"chai-as-promised@npm:^7.1.1": + version: 7.1.1 + resolution: "chai-as-promised@npm:7.1.1" + dependencies: + check-error: ^1.0.2 + peerDependencies: + chai: ">= 2.1.2 < 5" + checksum: 7262868a5b51a12af4e432838ddf97a893109266a505808e1868ba63a12de7ee1166e9d43b5c501a190c377c1b11ecb9ff8e093c89f097ad96c397e8ec0f8d6a + languageName: node + linkType: hard + +"chai-exclude@npm:^2.1.0": + version: 2.1.0 + resolution: "chai-exclude@npm:2.1.0" + dependencies: + fclone: ^1.0.11 + peerDependencies: + chai: ">= 4.0.0 < 5" + checksum: 29d964d9f667bd2c8e0e5e597c299550ff13e41b737450d6f91aa4a28064e24f034c1296c85a1d0ba5ebc2a01188f4147dc60bda0f46382f8fb851052f770bb3 + languageName: node + linkType: hard + +"chai-string@npm:^1.5.0": + version: 1.5.0 + resolution: "chai-string@npm:1.5.0" + peerDependencies: + chai: ^4.1.2 + checksum: d443bb416f6d4dd3395442f459af197aff93f2546dcb5bce6313badf505aa828615117f0eadc99e97ec518b7d2df387b538c81c647e6daf5c386d5bafa212e8f + languageName: node + linkType: hard + +"chai@npm:^4.3.4": + version: 4.3.4 + resolution: "chai@npm:4.3.4" + dependencies: + assertion-error: ^1.1.0 + check-error: ^1.0.2 + deep-eql: ^3.0.1 + get-func-name: ^2.0.0 + pathval: ^1.1.1 + type-detect: ^4.0.5 + checksum: 772c522b3bfe3fcf0e0e74edfe584cd886b0e85a73126dec750095300e023d4e1ec6d40e3c35a80d2bd8f33dca46c42767a36f5f50f32dca6fa31c88b5f49ab8 + languageName: node + linkType: hard + +"chalk@npm:2.4.2, chalk@npm:^2.0.0, chalk@npm:^2.0.1, chalk@npm:^2.1.0, chalk@npm:^2.4.1, chalk@npm:^2.4.2": + version: 2.4.2 + resolution: "chalk@npm:2.4.2" + dependencies: + ansi-styles: ^3.2.1 + escape-string-regexp: ^1.0.5 + supports-color: ^5.3.0 + checksum: ec3661d38fe77f681200f878edbd9448821924e0f93a9cefc0e26a33b145f1027a2084bf19967160d11e1f03bfe4eaffcabf5493b89098b2782c3fe0b03d80c2 + languageName: node + linkType: hard + +"chalk@npm:^1.0.0": + version: 1.1.3 + resolution: "chalk@npm:1.1.3" + dependencies: + ansi-styles: ^2.2.1 + escape-string-regexp: ^1.0.2 + has-ansi: ^2.0.0 + strip-ansi: ^3.0.0 + supports-color: ^2.0.0 + checksum: 9d2ea6b98fc2b7878829eec223abcf404622db6c48396a9b9257f6d0ead2acf18231ae368d6a664a83f272b0679158da12e97b5229f794939e555cc574478acd + languageName: node + linkType: hard + +"chalk@npm:^3.0.0": + version: 3.0.0 + resolution: "chalk@npm:3.0.0" + dependencies: + ansi-styles: ^4.1.0 + supports-color: ^7.1.0 + checksum: 8e3ddf3981c4da405ddbd7d9c8d91944ddf6e33d6837756979f7840a29272a69a5189ecae0ff84006750d6d1e92368d413335eab4db5476db6e6703a1d1e0505 + languageName: node + linkType: hard + +"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: ^4.1.0 + supports-color: ^7.1.0 + checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc + languageName: node + linkType: hard + +"chance@npm:^1.1.6": + version: 1.1.8 + resolution: "chance@npm:1.1.8" + checksum: e733f51e1094d7b0343a9e79f38599086442e6284fc2789cf9e072c71a0070874a1b340f9fffe4e66260899733f768cf113434be466ecb0016e3afcb60add3ee + languageName: node + linkType: hard + +"chardet@npm:^0.7.0": + version: 0.7.0 + resolution: "chardet@npm:0.7.0" + checksum: 6fd5da1f5d18ff5712c1e0aed41da200d7c51c28f11b36ee3c7b483f3696dabc08927fc6b227735eb8f0e1215c9a8abd8154637f3eff8cada5959df7f58b024d + languageName: node + linkType: hard + +"check-error@npm:^1.0.2": + version: 1.0.2 + resolution: "check-error@npm:1.0.2" + checksum: d9d106504404b8addd1ee3f63f8c0eaa7cd962a1a28eb9c519b1c4a1dc7098be38007fc0060f045ee00f075fbb7a2a4f42abcf61d68323677e11ab98dc16042e + languageName: node + linkType: hard + +"chokidar@npm:3.5.2, chokidar@npm:^3.5.1, chokidar@npm:^3.5.2": + version: 3.5.2 + resolution: "chokidar@npm:3.5.2" + dependencies: + anymatch: ~3.1.2 + braces: ~3.0.2 + fsevents: ~2.3.2 + glob-parent: ~5.1.2 + is-binary-path: ~2.1.0 + is-glob: ~4.0.1 + normalize-path: ~3.0.0 + readdirp: ~3.6.0 + dependenciesMeta: + fsevents: + optional: true + checksum: d1fda32fcd67d9f6170a8468ad2630a3c6194949c9db3f6a91b16478c328b2800f433fb5d2592511b6cb145a47c013ea1cce60b432b1a001ae3ee978a8bffc2d + languageName: node + linkType: hard + +"chownr@npm:^1.1.1": + version: 1.1.4 + resolution: "chownr@npm:1.1.4" + checksum: 115648f8eb38bac5e41c3857f3e663f9c39ed6480d1349977c4d96c95a47266fcacc5a5aabf3cb6c481e22d72f41992827db47301851766c4fd77ac21a4f081d + languageName: node + linkType: hard + +"chownr@npm:^2.0.0": + version: 2.0.0 + resolution: "chownr@npm:2.0.0" + checksum: c57cf9dd0791e2f18a5ee9c1a299ae6e801ff58fee96dc8bfd0dcb4738a6ce58dd252a3605b1c93c6418fe4f9d5093b28ffbf4d66648cb2a9c67eaef9679be2f + languageName: node + linkType: hard + +"chrome-trace-event@npm:^1.0.2": + version: 1.0.3 + resolution: "chrome-trace-event@npm:1.0.3" + checksum: cb8b1fc7e881aaef973bd0c4a43cd353c2ad8323fb471a041e64f7c2dd849cde4aad15f8b753331a32dda45c973f032c8a03b8177fc85d60eaa75e91e08bfb97 + languageName: node + linkType: hard + +"ci-info@npm:^2.0.0": + version: 2.0.0 + resolution: "ci-info@npm:2.0.0" + checksum: 3b374666a85ea3ca43fa49aa3a048d21c9b475c96eb13c133505d2324e7ae5efd6a454f41efe46a152269e9b6a00c9edbe63ec7fa1921957165aae16625acd67 + languageName: node + linkType: hard + +"cipher-base@npm:^1.0.0, cipher-base@npm:^1.0.1, cipher-base@npm:^1.0.3": + version: 1.0.4 + resolution: "cipher-base@npm:1.0.4" + dependencies: + inherits: ^2.0.1 + safe-buffer: ^5.0.1 + checksum: 47d3568dbc17431a339bad1fe7dff83ac0891be8206911ace3d3b818fc695f376df809bea406e759cdea07fff4b454fa25f1013e648851bec790c1d75763032e + languageName: node + linkType: hard + +"clean-stack@npm:^2.0.0": + version: 2.2.0 + resolution: "clean-stack@npm:2.2.0" + checksum: 2ac8cd2b2f5ec986a3c743935ec85b07bc174d5421a5efc8017e1f146a1cf5f781ae962618f416352103b32c9cd7e203276e8c28241bbe946160cab16149fb68 + languageName: node + linkType: hard + +"clean-stack@npm:^3.0.1": + version: 3.0.1 + resolution: "clean-stack@npm:3.0.1" + dependencies: + escape-string-regexp: 4.0.0 + checksum: dc18c842d7792dd72d463936b1b0a5b2621f0fc11588ee48b602e1a29b6c010c606d89f3de1f95d15d72de74aea93c0fbac8246593a31d95f8462cac36148e05 + languageName: node + linkType: hard + +"cli-boxes@npm:^1.0.0": + version: 1.0.0 + resolution: "cli-boxes@npm:1.0.0" + checksum: 101cfd6464a418a76523c332665eaf0641522f30ecc2492de48263ada6b0852333b2ed47b2998ddda621e7008471c51f597f813be798db237c33ba45b27e802a + languageName: node + linkType: hard + +"cli-boxes@npm:^2.2.1": + version: 2.2.1 + resolution: "cli-boxes@npm:2.2.1" + checksum: be79f8ec23a558b49e01311b39a1ea01243ecee30539c880cf14bf518a12e223ef40c57ead0cb44f509bffdffc5c129c746cd50d863ab879385370112af4f585 + languageName: node + linkType: hard + +"cli-cursor@npm:^3.1.0": + version: 3.1.0 + resolution: "cli-cursor@npm:3.1.0" + dependencies: + restore-cursor: ^3.1.0 + checksum: 2692784c6cd2fd85cfdbd11f53aea73a463a6d64a77c3e098b2b4697a20443f430c220629e1ca3b195ea5ac4a97a74c2ee411f3807abf6df2b66211fec0c0a29 + languageName: node + linkType: hard + +"cli-progress@npm:^3.10.0": + version: 3.10.0 + resolution: "cli-progress@npm:3.10.0" + dependencies: + string-width: ^4.2.0 + checksum: 8e22c6265f95598002986c6508d05004bd9f5ee17c06f239d8d59e14f5a7e5605055f5d705a3a7d69f6072ea0752b1c094f28c48a704f9fd00378d7d16f0b46d + languageName: node + linkType: hard + +"cli-spinners@npm:^2.5.0": + version: 2.6.1 + resolution: "cli-spinners@npm:2.6.1" + checksum: 423409baaa7a58e5104b46ca1745fbfc5888bbd0b0c5a626e052ae1387060839c8efd512fb127e25769b3dc9562db1dc1b5add6e0b93b7ef64f477feb6416a45 + languageName: node + linkType: hard + +"cli-table@npm:^0.3.1": + version: 0.3.11 + resolution: "cli-table@npm:0.3.11" + dependencies: + colors: 1.0.3 + checksum: 59fb61f992ac9bc8610ed98c72bf7f5d396c5afb42926b6747b46b0f8bb98a0dfa097998e77542ac334c1eb7c18dbf4f104d5783493273c5ec4c34084aa7c663 + languageName: node + linkType: hard + +"cli-truncate@npm:^2.1.0": + version: 2.1.0 + resolution: "cli-truncate@npm:2.1.0" + dependencies: + slice-ansi: ^3.0.0 + string-width: ^4.2.0 + checksum: bf1e4e6195392dc718bf9cd71f317b6300dc4a9191d052f31046b8773230ece4fa09458813bf0e3455a5e68c0690d2ea2c197d14a8b85a7b5e01c97f4b5feb5d + languageName: node + linkType: hard + +"cli-width@npm:^3.0.0": + version: 3.0.0 + resolution: "cli-width@npm:3.0.0" + checksum: 4c94af3769367a70e11ed69aa6095f1c600c0ff510f3921ab4045af961820d57c0233acfa8b6396037391f31b4c397e1f614d234294f979ff61430a6c166c3f6 + languageName: node + linkType: hard + +"cliui@npm:^6.0.0": + version: 6.0.0 + resolution: "cliui@npm:6.0.0" + dependencies: + string-width: ^4.2.0 + strip-ansi: ^6.0.0 + wrap-ansi: ^6.2.0 + checksum: 4fcfd26d292c9f00238117f39fc797608292ae36bac2168cfee4c85923817d0607fe21b3329a8621e01aedf512c99b7eaa60e363a671ffd378df6649fb48ae42 + languageName: node + linkType: hard + +"cliui@npm:^7.0.2": + version: 7.0.4 + resolution: "cliui@npm:7.0.4" + dependencies: + string-width: ^4.2.0 + strip-ansi: ^6.0.0 + wrap-ansi: ^7.0.0 + checksum: ce2e8f578a4813806788ac399b9e866297740eecd4ad1823c27fd344d78b22c5f8597d548adbcc46f0573e43e21e751f39446c5a5e804a12aace402b7a315d7f + languageName: node + linkType: hard + +"clone-buffer@npm:^1.0.0": + version: 1.0.0 + resolution: "clone-buffer@npm:1.0.0" + checksum: a39a35e7fd081e0f362ba8195bd15cbc8205df1fbe4598bb4e09c1f9a13c0320a47ab8a61a8aa83561e4ed34dc07666d73254ee952ddd3985e4286b082fe63b9 + languageName: node + linkType: hard + +"clone-deep@npm:^4.0.1": + version: 4.0.1 + resolution: "clone-deep@npm:4.0.1" + dependencies: + is-plain-object: ^2.0.4 + kind-of: ^6.0.2 + shallow-clone: ^3.0.0 + checksum: 770f912fe4e6f21873c8e8fbb1e99134db3b93da32df271d00589ea4a29dbe83a9808a322c93f3bcaf8584b8b4fa6fc269fc8032efbaa6728e0c9886c74467d2 + languageName: node + linkType: hard + +"clone-response@npm:^1.0.2": + version: 1.0.2 + resolution: "clone-response@npm:1.0.2" + dependencies: + mimic-response: ^1.0.0 + checksum: 2d0e61547fc66276e0903be9654ada422515f5a15741691352000d47e8c00c226061221074ce2c0064d12e975e84a8687cfd35d8b405750cb4e772f87b256eda + languageName: node + linkType: hard + +"clone-stats@npm:^1.0.0": + version: 1.0.0 + resolution: "clone-stats@npm:1.0.0" + checksum: 654c0425afc5c5c55a4d95b2e0c6eccdd55b5247e7a1e7cca9000b13688b96b0a157950c72c5307f9fd61f17333ad796d3cd654778f2d605438012391cc4ada5 + languageName: node + linkType: hard + +"clone@npm:^1.0.2": + version: 1.0.4 + resolution: "clone@npm:1.0.4" + checksum: d06418b7335897209e77bdd430d04f882189582e67bd1f75a04565f3f07f5b3f119a9d670c943b6697d0afb100f03b866b3b8a1f91d4d02d72c4ecf2bb64b5dd + languageName: node + linkType: hard + +"clone@npm:^2.1.1": + version: 2.1.2 + resolution: "clone@npm:2.1.2" + checksum: aaf106e9bc025b21333e2f4c12da539b568db4925c0501a1bf4070836c9e848c892fa22c35548ce0d1132b08bbbfa17a00144fe58fccdab6fa900fec4250f67d + languageName: node + linkType: hard + +"cloneable-readable@npm:^1.0.0": + version: 1.1.3 + resolution: "cloneable-readable@npm:1.1.3" + dependencies: + inherits: ^2.0.1 + process-nextick-args: ^2.0.0 + readable-stream: ^2.3.5 + checksum: 23b3741225a80c1760dff58aafb6a45383d5ee2d42de7124e4e674387cfad2404493d685b35ebfca9098f99c296e5c5719e748c9750c13838a2016ea2d2bb83a + languageName: node + linkType: hard + +"cmd-shim@npm:^4.0.1": + version: 4.1.0 + resolution: "cmd-shim@npm:4.1.0" + dependencies: + mkdirp-infer-owner: ^2.0.0 + checksum: d25bb57a8accab681bcfc632e085573b9395cdc60aed8d0ce479f988f9ced16720c89732aef81020140e43fd223b6573c22402e5a1c0cbd0149443104df88d68 + languageName: node + linkType: hard + +"code-point-at@npm:^1.0.0": + version: 1.1.0 + resolution: "code-point-at@npm:1.1.0" + checksum: 17d5666611f9b16d64fdf48176d9b7fb1c7d1c1607a189f7e600040a11a6616982876af148230336adb7d8fe728a559f743a4e29db3747e3b1a32fa7f4529681 + languageName: node + linkType: hard + +"color-convert@npm:^1.9.0, color-convert@npm:^1.9.3": + version: 1.9.3 + resolution: "color-convert@npm:1.9.3" + dependencies: + color-name: 1.1.3 + checksum: fd7a64a17cde98fb923b1dd05c5f2e6f7aefda1b60d67e8d449f9328b4e53b228a428fd38bfeaeb2db2ff6b6503a776a996150b80cdf224062af08a5c8a3a203 + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: ~1.1.4 + checksum: 79e6bdb9fd479a205c71d89574fccfb22bd9053bd98c6c4d870d65c132e5e904e6034978e55b43d69fcaa7433af2016ee203ce76eeba9cfa554b373e7f7db336 + languageName: node + linkType: hard + +"color-name@npm:1.1.3": + version: 1.1.3 + resolution: "color-name@npm:1.1.3" + checksum: 09c5d3e33d2105850153b14466501f2bfb30324a2f76568a408763a3b7433b0e50e5b4ab1947868e65cb101bb7cb75029553f2c333b6d4b8138a73fcc133d69d + languageName: node + linkType: hard + +"color-name@npm:^1.0.0, color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 + languageName: node + linkType: hard + +"color-string@npm:^1.6.0": + version: 1.6.0 + resolution: "color-string@npm:1.6.0" + dependencies: + color-name: ^1.0.0 + simple-swizzle: ^0.2.2 + checksum: 33466a65277dd3d4ce24ef1991b47069292f75d1a43b0d2e7ea43076ba793728e965d50deed2b523f35519f4995a908253fcbcc774baceae8e439bc78c02e850 + languageName: node + linkType: hard + +"color-support@npm:^1.1.2": + version: 1.1.3 + resolution: "color-support@npm:1.1.3" + bin: + color-support: bin.js + checksum: 9b7356817670b9a13a26ca5af1c21615463b500783b739b7634a0c2047c16cef4b2865d7576875c31c3cddf9dd621fa19285e628f20198b233a5cfdda6d0793b + languageName: node + linkType: hard + +"color@npm:^3.1.3": + version: 3.2.1 + resolution: "color@npm:3.2.1" + dependencies: + color-convert: ^1.9.3 + color-string: ^1.6.0 + checksum: f81220e8b774d35865c2561be921f5652117638dcda7ca4029262046e37fc2444ac7bbfdd110cf1fd9c074a4ee5eda8f85944ffbdda26186b602dd9bb05f6400 + languageName: node + linkType: hard + +"colorette@npm:^2.0.14": + version: 2.0.16 + resolution: "colorette@npm:2.0.16" + checksum: cd55596a3a2d1071c1a28eee7fd8a5387593ff1bd10a3e8d0a6221499311fe34a9f2b9272d77c391e0e003dcdc8934fb2f8d106e7ef1f7516f8060c901d41a27 + languageName: node + linkType: hard + +"colors@npm:1.0.3": + version: 1.0.3 + resolution: "colors@npm:1.0.3" + checksum: 234e8d3ab7e4003851cdd6a1f02eaa16dabc502ee5f4dc576ad7959c64b7477b15bd21177bab4055a4c0a66aa3d919753958030445f87c39a253d73b7a3637f5 + languageName: node + linkType: hard + +"colors@npm:^1.2.1, colors@npm:^1.4.0": + version: 1.4.0 + resolution: "colors@npm:1.4.0" + checksum: 98aa2c2418ad87dedf25d781be69dc5fc5908e279d9d30c34d8b702e586a0474605b3a189511482b9d5ed0d20c867515d22749537f7bc546256c6014f3ebdcec + languageName: node + linkType: hard + +"colorspace@npm:1.1.x": + version: 1.1.4 + resolution: "colorspace@npm:1.1.4" + dependencies: + color: ^3.1.3 + text-hex: 1.0.x + checksum: bb3934ef3c417e961e6d03d7ca60ea6e175947029bfadfcdb65109b01881a1c0ecf9c2b0b59abcd0ee4a0d7c1eae93beed01b0e65848936472270a0b341ebce8 + languageName: node + linkType: hard + +"combine-source-map@npm:^0.8.0, combine-source-map@npm:~0.8.0": + version: 0.8.0 + resolution: "combine-source-map@npm:0.8.0" + dependencies: + convert-source-map: ~1.1.0 + inline-source-map: ~0.6.0 + lodash.memoize: ~3.0.3 + source-map: ~0.5.3 + checksum: 26b3064a4e58400e04089acbf5c8741c47db079706bb2fcd79a7768f99d68de9baf1eb48081cdfbc568e308633105af2aeaf52c73e388619ba1f56463fb73a2e + languageName: node + linkType: hard + +"combined-stream@npm:^1.0.6, combined-stream@npm:~1.0.6": + version: 1.0.8 + resolution: "combined-stream@npm:1.0.8" + dependencies: + delayed-stream: ~1.0.0 + checksum: 49fa4aeb4916567e33ea81d088f6584749fc90c7abec76fd516bf1c5aa5c79f3584b5ba3de6b86d26ddd64bae5329c4c7479343250cfe71c75bb366eae53bb7c + languageName: node + linkType: hard + +"commander@npm:4.0.1": + version: 4.0.1 + resolution: "commander@npm:4.0.1" + checksum: a8df9873c699abe5a6396164cf8ca9e2908246469cfff7178066c0d05575622ac43cebfb387c5531f800e336a812833728474fc248d4c4fb00b1df58434d5215 + languageName: node + linkType: hard + +"commander@npm:7.1.0": + version: 7.1.0 + resolution: "commander@npm:7.1.0" + checksum: 99c120b939b610b1fb4a14424b48dc6431643ec46836f251a26434ad77b1eed22577a36378cfd66ed4b56fc69f98553f7dcb30f1539e3685874fd77cfb6e52fb + languageName: node + linkType: hard + +"commander@npm:^2.12.2, commander@npm:^2.20.0, commander@npm:^2.20.3, commander@npm:^2.7.1": + version: 2.20.3 + resolution: "commander@npm:2.20.3" + checksum: ab8c07884e42c3a8dbc5dd9592c606176c7eb5c1ca5ff274bcf907039b2c41de3626f684ea75ccf4d361ba004bbaff1f577d5384c155f3871e456bdf27becf9e + languageName: node + linkType: hard + +"commander@npm:^7.0.0": + version: 7.2.0 + resolution: "commander@npm:7.2.0" + checksum: 53501cbeee61d5157546c0bef0fedb6cdfc763a882136284bed9a07225f09a14b82d2a84e7637edfd1a679fb35ed9502fd58ef1d091e6287f60d790147f68ddc + languageName: node + linkType: hard + +"comment-parser@npm:^0.7.5, comment-parser@npm:^0.7.6": + version: 0.7.6 + resolution: "comment-parser@npm:0.7.6" + checksum: 880e4d58c0b9dc69c50479d98a838b916980c4f0f53f8eff89e8d389bf40e93b8590c10e24faaca070a344226894e8ec8da6927b74e3f1f275b8445a14e488ea + languageName: node + linkType: hard + +"common-ancestor-path@npm:^1.0.1": + version: 1.0.1 + resolution: "common-ancestor-path@npm:1.0.1" + checksum: 1d2e4186067083d8cc413f00fc2908225f04ae4e19417ded67faa6494fb313c4fcd5b28a52326d1a62b466e2b3a4325e92c31133c5fee628cdf8856b3a57c3d7 + languageName: node + linkType: hard + +"commondir@npm:^1.0.1": + version: 1.0.1 + resolution: "commondir@npm:1.0.1" + checksum: 59715f2fc456a73f68826285718503340b9f0dd89bfffc42749906c5cf3d4277ef11ef1cca0350d0e79204f00f1f6d83851ececc9095dc88512a697ac0b9bdcb + languageName: node + linkType: hard + +"compare-func@npm:^2.0.0": + version: 2.0.0 + resolution: "compare-func@npm:2.0.0" + dependencies: + array-ify: ^1.0.0 + dot-prop: ^5.1.0 + checksum: fb71d70632baa1e93283cf9d80f30ac97f003aabee026e0b4426c9716678079ef5fea7519b84d012cbed938c476493866a38a79760564a9e21ae9433e40e6f0d + languageName: node + linkType: hard + +"complex.js@npm:^2.1.0": + version: 2.1.0 + resolution: "complex.js@npm:2.1.0" + checksum: 8a31a0d8191d793e08c8bac5065b8146f2243d8f15b81736c07211f7e2b73e5d71c5d010ed38525985d83ac37027d66dbe51704e8070a59df6cf8130d55562b7 + languageName: node + linkType: hard + +"component-emitter@npm:~1.3.0": + version: 1.3.0 + resolution: "component-emitter@npm:1.3.0" + checksum: b3c46de38ffd35c57d1c02488355be9f218e582aec72d72d1b8bbec95a3ac1b38c96cd6e03ff015577e68f550fbb361a3bfdbd9bb248be9390b7b3745691be6b + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 902a9f5d8967a3e2faf138d5cb784b9979bad2e6db5357c5b21c568df4ebe62bcb15108af1b2253744844eb964fc023fbd9afbbbb6ddd0bcc204c6fb5b7bf3af + languageName: node + linkType: hard + +"concat-stream@npm:^1.6.0, concat-stream@npm:^1.6.1, concat-stream@npm:~1.6.0": + version: 1.6.2 + resolution: "concat-stream@npm:1.6.2" + dependencies: + buffer-from: ^1.0.0 + inherits: ^2.0.3 + readable-stream: ^2.2.2 + typedarray: ^0.0.6 + checksum: 1ef77032cb4459dcd5187bd710d6fc962b067b64ec6a505810de3d2b8cc0605638551b42f8ec91edf6fcd26141b32ef19ad749239b58fae3aba99187adc32285 + languageName: node + linkType: hard + +"concurrently@npm:^7.0.0": + version: 7.0.0 + resolution: "concurrently@npm:7.0.0" + dependencies: + chalk: ^4.1.0 + date-fns: ^2.16.1 + lodash: ^4.17.21 + rxjs: ^6.6.3 + spawn-command: ^0.0.2-1 + supports-color: ^8.1.0 + tree-kill: ^1.2.2 + yargs: ^16.2.0 + bin: + concurrently: dist/bin/concurrently.js + checksum: 1be78f24bf814f097adaf2e61a37563eb73f12272bbeda90e3e616a23525f793268e22a3c6aedf34e5bad44459f7c30ee053c617905416ba11ce2bee55f089a5 + languageName: node + linkType: hard + +"configstore@npm:^5.0.1": + version: 5.0.1 + resolution: "configstore@npm:5.0.1" + dependencies: + dot-prop: ^5.2.0 + graceful-fs: ^4.1.2 + make-dir: ^3.0.0 + unique-string: ^2.0.0 + write-file-atomic: ^3.0.0 + xdg-basedir: ^4.0.0 + checksum: 60ef65d493b63f96e14b11ba7ec072fdbf3d40110a94fb7199d1c287761bdea5c5244e76b2596325f30c1b652213aa75de96ea20afd4a5f82065e61ea090988e + languageName: node + linkType: hard + +"confusing-browser-globals@npm:^1.0.10": + version: 1.0.10 + resolution: "confusing-browser-globals@npm:1.0.10" + checksum: 7ccdc44c2ca419cf6576c3e4336106e18d1c5337f547e461342f51aec4a10f96fdfe45414b522be3c7d24ea0b62bf4372cd37768022e4d6161707ffb2c0987e6 + languageName: node + linkType: hard + +"connect@npm:^3.7.0": + version: 3.7.0 + resolution: "connect@npm:3.7.0" + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: ~1.3.3 + utils-merge: 1.0.1 + checksum: 96e1c4effcf219b065c7823e57351c94366d2e2a6952fa95e8212bffb35c86f1d5a3f9f6c5796d4cd3a5fdda628368b1c3cc44bf19c66cfd68fe9f9cab9177e2 + languageName: node + linkType: hard + +"console-browserify@npm:^1.1.0": + version: 1.2.0 + resolution: "console-browserify@npm:1.2.0" + checksum: 226591eeff8ed68e451dffb924c1fb750c654d54b9059b3b261d360f369d1f8f70650adecf2c7136656236a4bfeb55c39281b5d8a55d792ebbb99efd3d848d52 + languageName: node + linkType: hard + +"console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0, console-control-strings@npm:~1.1.0": + version: 1.1.0 + resolution: "console-control-strings@npm:1.1.0" + checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed + languageName: node + linkType: hard + +"console-table-printer@npm:^2.11.0": + version: 2.11.0 + resolution: "console-table-printer@npm:2.11.0" + dependencies: + simple-wcswidth: ^1.0.1 + checksum: 125797e3b936eb08182cc704eb8d6aaa6ab82c6fb1f739afad3dd06847b8a89227fe80f84ed105accfa6f4487ff12821f931727624bfda67d8a8ebdc1314f168 + languageName: node + linkType: hard + +"constants-browserify@npm:~1.0.0": + version: 1.0.0 + resolution: "constants-browserify@npm:1.0.0" + checksum: f7ac8c6d0b6e4e0c77340a1d47a3574e25abd580bfd99ad707b26ff7618596cf1a5e5ce9caf44715e9e01d4a5d12cb3b4edaf1176f34c19adb2874815a56e64f + languageName: node + linkType: hard + +"content-type@npm:^1.0.4, content-type@npm:~1.0.4": + version: 1.0.4 + resolution: "content-type@npm:1.0.4" + checksum: 3d93585fda985d1554eca5ebd251994327608d2e200978fdbfba21c0c679914d5faf266d17027de44b34a72c7b0745b18584ecccaa7e1fdfb6a68ac7114f12e0 + languageName: node + linkType: hard + +"conventional-changelog-angular@npm:^5.0.12": + version: 5.0.13 + resolution: "conventional-changelog-angular@npm:5.0.13" + dependencies: + compare-func: ^2.0.0 + q: ^1.5.1 + checksum: 6ed4972fce25a50f9f038c749cc9db501363131b0fb2efc1fccecba14e4b1c80651d0d758d4c350a609f32010c66fa343eefd49c02e79e911884be28f53f3f90 + languageName: node + linkType: hard + +"conventional-changelog-atom@npm:^2.0.8": + version: 2.0.8 + resolution: "conventional-changelog-atom@npm:2.0.8" + dependencies: + q: ^1.5.1 + checksum: 12ecbd928f8c261f9afaac067fcc0cf10ff6ac8505e4285dc3d9959ee072a8937ac942d505e850dce27c4527046009adb22b498ba0b10802916d2c7d2dc1f7bc + languageName: node + linkType: hard + +"conventional-changelog-codemirror@npm:^2.0.8": + version: 2.0.8 + resolution: "conventional-changelog-codemirror@npm:2.0.8" + dependencies: + q: ^1.5.1 + checksum: cf331db40cc54c2353b0189aba26a2b959cb08b059bf2a81245272027371519c9acc90d574295782985829c50f0c52da60c952c70ec6dbd70e9e17affeb61453 + languageName: node + linkType: hard + +"conventional-changelog-conventionalcommits@npm:^4.5.0": + version: 4.6.1 + resolution: "conventional-changelog-conventionalcommits@npm:4.6.1" + dependencies: + compare-func: ^2.0.0 + lodash: ^4.17.15 + q: ^1.5.1 + checksum: f866616c8f6f21cea005b42792451bfbd16bd4d82872867d1218f67a7993a53c5d87e26d6b483d9252e8022f2e4570e6cf9fa2a409aae5a3d73eea92ccf78b13 + languageName: node + linkType: hard + +"conventional-changelog-core@npm:^4.2.1": + version: 4.2.4 + resolution: "conventional-changelog-core@npm:4.2.4" + dependencies: + add-stream: ^1.0.0 + conventional-changelog-writer: ^5.0.0 + conventional-commits-parser: ^3.2.0 + dateformat: ^3.0.0 + get-pkg-repo: ^4.0.0 + git-raw-commits: ^2.0.8 + git-remote-origin-url: ^2.0.0 + git-semver-tags: ^4.1.1 + lodash: ^4.17.15 + normalize-package-data: ^3.0.0 + q: ^1.5.1 + read-pkg: ^3.0.0 + read-pkg-up: ^3.0.0 + through2: ^4.0.0 + checksum: 56d5194040495ea316e53fd64cb3614462c318f0fe54b1bf25aba6fba9b3d51cb9fdf7ac5b766f17e5529a3f90e317257394e00b0a9a5ce42caf3a59f82afb3a + languageName: node + linkType: hard + +"conventional-changelog-dash@github:dashevo/conventional-changelog-dash": + version: 1.0.0 + resolution: "conventional-changelog-dash@https://github.com/dashevo/conventional-changelog-dash.git#commit=3d4d77e2cea876a27b92641c28b15aedf13eb788" + dependencies: + compare-func: ^2.0.0 + lodash: ^4.17.15 + q: ^1.5.1 + checksum: 98199fb767b8ac013d3907b7dd6a05a626a03764ec91c971978486f77b0a4acab3a32957ff1f2d80d2d5e023a7d58ada3945061077e9ec41cf4c95d73ebbfefa + languageName: node + linkType: hard + +"conventional-changelog-ember@npm:^2.0.9": + version: 2.0.9 + resolution: "conventional-changelog-ember@npm:2.0.9" + dependencies: + q: ^1.5.1 + checksum: 30c7bd48ce995e39fc91bcd8c719b2bee10cb408c246a6a7de6cec44a3ca12afe5a86f57f55aa1fd2c64beb484c68013d16658047e6273f130c1c80e7dad38e9 + languageName: node + linkType: hard + +"conventional-changelog-eslint@npm:^3.0.9": + version: 3.0.9 + resolution: "conventional-changelog-eslint@npm:3.0.9" + dependencies: + q: ^1.5.1 + checksum: 402ae73a8c5390405d4f902819f630f56fa7dfa8f6bef77b3b5f2fb7c8bd17f64ad83edbacc030cfef5b84400ab722d4f166dd906296a4d286e66205c1bd8a3f + languageName: node + linkType: hard + +"conventional-changelog-express@npm:^2.0.6": + version: 2.0.6 + resolution: "conventional-changelog-express@npm:2.0.6" + dependencies: + q: ^1.5.1 + checksum: c139fa9878971455cce9904a195d92f770679d24a88ef07a016a6954e28f0f237ec59e45f2591b2fc9b8e10fd46c30150ddf0ce50a2cb03be85cae0ee64d4cdd + languageName: node + linkType: hard + +"conventional-changelog-jquery@npm:^3.0.11": + version: 3.0.11 + resolution: "conventional-changelog-jquery@npm:3.0.11" + dependencies: + q: ^1.5.1 + checksum: df1145467c75e8e61f35ed24d7539e8b7dcdc810b86267b0173420c8955590cca139eb51f89ac255d70c632433d996b0ed227cb1acdf59537f3d2f4ad9c770d3 + languageName: node + linkType: hard + +"conventional-changelog-jshint@npm:^2.0.9": + version: 2.0.9 + resolution: "conventional-changelog-jshint@npm:2.0.9" + dependencies: + compare-func: ^2.0.0 + q: ^1.5.1 + checksum: ec96144b75fdb84c4a6f7db9b671dc258d964cd7aa35f9b00539e42bbe05601a9127c17cf0dcc315ae81a0dd20fe795d9d41dd90373928d24b33f065728eb2e2 + languageName: node + linkType: hard + +"conventional-changelog-preset-loader@npm:^2.3.4": + version: 2.3.4 + resolution: "conventional-changelog-preset-loader@npm:2.3.4" + checksum: 23a889b7fcf6fe7653e61f32a048877b2f954dcc1e0daa2848c5422eb908e6f24c78372f8d0d2130b5ed941c02e7010c599dccf44b8552602c6c8db9cb227453 + languageName: node + linkType: hard + +"conventional-changelog-writer@npm:^5.0.0": + version: 5.0.0 + resolution: "conventional-changelog-writer@npm:5.0.0" + dependencies: + conventional-commits-filter: ^2.0.7 + dateformat: ^3.0.0 + handlebars: ^4.7.6 + json-stringify-safe: ^5.0.1 + lodash: ^4.17.15 + meow: ^8.0.0 + semver: ^6.0.0 + split: ^1.0.0 + through2: ^4.0.0 + bin: + conventional-changelog-writer: cli.js + checksum: c310b949d354688b971f576c92cac77f11540fee56dccb990169e94e4fc42e40245d2c381f826b7d781deb04d4f7e01701cc29bdd1c3d3cdf8817e8b7a80ea18 + languageName: node + linkType: hard + +"conventional-changelog@npm:^3.1.24": + version: 3.1.24 + resolution: "conventional-changelog@npm:3.1.24" + dependencies: + conventional-changelog-angular: ^5.0.12 + conventional-changelog-atom: ^2.0.8 + conventional-changelog-codemirror: ^2.0.8 + conventional-changelog-conventionalcommits: ^4.5.0 + conventional-changelog-core: ^4.2.1 + conventional-changelog-ember: ^2.0.9 + conventional-changelog-eslint: ^3.0.9 + conventional-changelog-express: ^2.0.6 + conventional-changelog-jquery: ^3.0.11 + conventional-changelog-jshint: ^2.0.9 + conventional-changelog-preset-loader: ^2.3.4 + checksum: 54253a3e3761369a8c68ec1ea57f3847b323a0104503dfccfd305553f77e83636132406d463dfa60ad3851dba42d84a528e8cb685943e8d6d7ae3eb37aaa19bb + languageName: node + linkType: hard + +"conventional-commits-filter@npm:^2.0.7": + version: 2.0.7 + resolution: "conventional-commits-filter@npm:2.0.7" + dependencies: + lodash.ismatch: ^4.4.0 + modify-values: ^1.0.0 + checksum: feb567f680a6da1baaa1ef3cff393b3c56a5828f77ab9df5e70626475425d109a6fee0289b4979223c62bbd63bf9c98ef532baa6fcb1b66ee8b5f49077f5d46c + languageName: node + linkType: hard + +"conventional-commits-parser@npm:^3.2.0": + version: 3.2.3 + resolution: "conventional-commits-parser@npm:3.2.3" + dependencies: + JSONStream: ^1.0.4 + is-text-path: ^1.0.1 + lodash: ^4.17.15 + meow: ^8.0.0 + split2: ^3.0.0 + through2: ^4.0.0 + bin: + conventional-commits-parser: cli.js + checksum: 0f57b5cb7cb359eb49e6807cfd82b27cbe9ac30ec580b20ad7e79575561183110532a6c2e6328ce6c4cd05c01458b9bb781f1f6653b14560f7c509b87b0e9ac7 + languageName: node + linkType: hard + +"convert-source-map@npm:^1.7.0": + version: 1.8.0 + resolution: "convert-source-map@npm:1.8.0" + dependencies: + safe-buffer: ~5.1.1 + checksum: 985d974a2d33e1a2543ada51c93e1ba2f73eaed608dc39f229afc78f71dcc4c8b7d7c684aa647e3c6a3a204027444d69e53e169ce94e8d1fa8d7dee80c9c8fed + languageName: node + linkType: hard + +"convert-source-map@npm:~1.1.0": + version: 1.1.3 + resolution: "convert-source-map@npm:1.1.3" + checksum: 0ed6bdecd330fd05941b417b63ebc9001b438f6d6681cd9a068617c3d4b649794dc35c95ba239d0a01f0b9499912b9e0d0d1b7c612e3669c57c65ce4bbc8fdd8 + languageName: node + linkType: hard + +"cookie@npm:~0.4.1": + version: 0.4.1 + resolution: "cookie@npm:0.4.1" + checksum: bd7c47f5d94ab70ccdfe8210cde7d725880d2fcda06d8e375afbdd82de0c8d3b73541996e9ce57d35f67f672c4ee6d60208adec06b3c5fc94cebb85196084cf8 + languageName: node + linkType: hard + +"core-js-compat@npm:^3.18.0, core-js-compat@npm:^3.19.1": + version: 3.19.1 + resolution: "core-js-compat@npm:3.19.1" + dependencies: + browserslist: ^4.17.6 + semver: 7.0.0 + checksum: ed302c99814bd7227b549f639fe5f1a3b9d885c0f878c1203f10be0a33c7d0b199931cb904074cc988ab48411132d4f41adf1603e4eebe5c5d42bdc62a3f5c5d + languageName: node + linkType: hard + +"core-js@npm:^3.17.2": + version: 3.19.1 + resolution: "core-js@npm:3.19.1" + checksum: 2f669061788dc6fea823f0433d871deeaaaacc7d68ef2748859509522a34df5c83e648c3c6a1993fed0ab188081b3cf32b957b2a1f46156a2b20bd775961ade4 + languageName: node + linkType: hard + +"core-util-is@npm:1.0.2": + version: 1.0.2 + resolution: "core-util-is@npm:1.0.2" + checksum: 7a4c925b497a2c91421e25bf76d6d8190f0b2359a9200dbeed136e63b2931d6294d3b1893eda378883ed363cd950f44a12a401384c609839ea616befb7927dab + languageName: node + linkType: hard + +"core-util-is@npm:~1.0.0": + version: 1.0.3 + resolution: "core-util-is@npm:1.0.3" + checksum: 9de8597363a8e9b9952491ebe18167e3b36e7707569eed0ebf14f8bba773611376466ae34575bca8cfe3c767890c859c74056084738f09d4e4a6f902b2ad7d99 + languageName: node + linkType: hard + +"cors@npm:~2.8.5": + version: 2.8.5 + resolution: "cors@npm:2.8.5" + dependencies: + object-assign: ^4 + vary: ^1 + checksum: ced838404ccd184f61ab4fdc5847035b681c90db7ac17e428f3d81d69e2989d2b680cc254da0e2554f5ed4f8a341820a1ce3d1c16b499f6e2f47a1b9b07b5006 + languageName: node + linkType: hard + +"cpu-features@npm:0.0.2": + version: 0.0.2 + resolution: "cpu-features@npm:0.0.2" + dependencies: + nan: ^2.14.1 + node-gyp: latest + checksum: 15177f9a2d465e4d84390f902c977b34f237dadb29fd8553853b13d906ffe5f15be9f091c72db4f34c71412d5ff4e0e4edf04caebc875b02d1d7ecfce2963299 + languageName: node + linkType: hard + +"create-ecdh@npm:^4.0.0": + version: 4.0.4 + resolution: "create-ecdh@npm:4.0.4" + dependencies: + bn.js: ^4.1.0 + elliptic: ^6.5.3 + checksum: 0dd7fca9711d09e152375b79acf1e3f306d1a25ba87b8ff14c2fd8e68b83aafe0a7dd6c4e540c9ffbdd227a5fa1ad9b81eca1f233c38bb47770597ba247e614b + languageName: node + linkType: hard + +"create-hash@npm:^1.1.0, create-hash@npm:^1.1.2, create-hash@npm:^1.2.0": + version: 1.2.0 + resolution: "create-hash@npm:1.2.0" + dependencies: + cipher-base: ^1.0.1 + inherits: ^2.0.1 + md5.js: ^1.3.4 + ripemd160: ^2.0.1 + sha.js: ^2.4.0 + checksum: 02a6ae3bb9cd4afee3fabd846c1d8426a0e6b495560a977ba46120c473cb283be6aa1cace76b5f927cf4e499c6146fb798253e48e83d522feba807d6b722eaa9 + languageName: node + linkType: hard + +"create-hmac@npm:^1.1.0, create-hmac@npm:^1.1.4, create-hmac@npm:^1.1.7": + version: 1.1.7 + resolution: "create-hmac@npm:1.1.7" + dependencies: + cipher-base: ^1.0.3 + create-hash: ^1.1.0 + inherits: ^2.0.1 + ripemd160: ^2.0.0 + safe-buffer: ^5.0.1 + sha.js: ^2.4.8 + checksum: ba12bb2257b585a0396108c72830e85f882ab659c3320c83584b1037f8ab72415095167ced80dc4ce8e446a8ecc4b2acf36d87befe0707d73b26cf9dc77440ed + languageName: node + linkType: hard + +"create-require@npm:^1.1.0": + version: 1.1.1 + resolution: "create-require@npm:1.1.1" + checksum: a9a1503d4390d8b59ad86f4607de7870b39cad43d929813599a23714831e81c520bddf61bcdd1f8e30f05fd3a2b71ae8538e946eb2786dc65c2bbc520f692eff + languageName: node + linkType: hard + +"cross-spawn@npm:^6.0.0, cross-spawn@npm:^6.0.5": + version: 6.0.5 + resolution: "cross-spawn@npm:6.0.5" + dependencies: + nice-try: ^1.0.4 + path-key: ^2.0.1 + semver: ^5.5.0 + shebang-command: ^1.2.0 + which: ^1.2.9 + checksum: f893bb0d96cd3d5751d04e67145bdddf25f99449531a72e82dcbbd42796bbc8268c1076c6b3ea51d4d455839902804b94bc45dfb37ecbb32ea8e54a6741c3ab9 + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": + version: 7.0.3 + resolution: "cross-spawn@npm:7.0.3" + dependencies: + path-key: ^3.1.0 + shebang-command: ^2.0.0 + which: ^2.0.1 + checksum: 671cc7c7288c3a8406f3c69a3ae2fc85555c04169e9d611def9a675635472614f1c0ed0ef80955d5b6d4e724f6ced67f0ad1bb006c2ea643488fcfef994d7f52 + languageName: node + linkType: hard + +"crypto-browserify@npm:^3.0.0, crypto-browserify@npm:^3.12.0": + version: 3.12.0 + resolution: "crypto-browserify@npm:3.12.0" + dependencies: + browserify-cipher: ^1.0.0 + browserify-sign: ^4.0.0 + create-ecdh: ^4.0.0 + create-hash: ^1.1.0 + create-hmac: ^1.1.0 + diffie-hellman: ^5.0.0 + inherits: ^2.0.1 + pbkdf2: ^3.0.3 + public-encrypt: ^4.0.0 + randombytes: ^2.0.0 + randomfill: ^1.0.3 + checksum: c1609af82605474262f3eaa07daa0b2140026bd264ab316d4bf1170272570dbe02f0c49e29407fe0d3634f96c507c27a19a6765fb856fed854a625f9d15618e2 + languageName: node + linkType: hard + +"crypto-js@npm:^4.0.0": + version: 4.1.1 + resolution: "crypto-js@npm:4.1.1" + checksum: b3747c12ee3a7632fab3b3e171ea50f78b182545f0714f6d3e7e2858385f0f4101a15f2517e033802ce9d12ba50a391575ff4638c9de3dd9b2c4bc47768d5425 + languageName: node + linkType: hard + +"crypto-random-string@npm:^2.0.0": + version: 2.0.0 + resolution: "crypto-random-string@npm:2.0.0" + checksum: 0283879f55e7c16fdceacc181f87a0a65c53bc16ffe1d58b9d19a6277adcd71900d02bb2c4843dd55e78c51e30e89b0fec618a7f170ebcc95b33182c28f05fd6 + languageName: node + linkType: hard + +"custom-event@npm:~1.0.0": + version: 1.0.1 + resolution: "custom-event@npm:1.0.1" + checksum: 334f48a6d5fb98df95c5f72cab2729417ffdcc74aebb1d51aa9220391bdee028ec36d9e19976a5a64f536e1e4aceb5bb4f0232d4761acc3e8fd74c54573959bd + languageName: node + linkType: hard + +"dargs@npm:^7.0.0": + version: 7.0.0 + resolution: "dargs@npm:7.0.0" + checksum: b8f1e3cba59c42e1f13a114ad4848c3fc1cf7470f633ee9e9f1043762429bc97d91ae31b826fb135eefde203a3fdb20deb0c0a0222ac29d937b8046085d668d1 + languageName: node + linkType: hard + +"dash-ast@npm:^1.0.0": + version: 1.0.0 + resolution: "dash-ast@npm:1.0.0" + checksum: db59e5e275d8159fb3b84bcd2936470c3fecb626f6486c179a28afad141cd95a578faaa3695ad6106153ca861da99a3d891fda37757b49afab773b3a46c638e6 + languageName: node + linkType: hard + +"dash@workspace:packages/js-dash-sdk, dash@workspace:~": + version: 0.0.0-use.local + resolution: "dash@workspace:packages/js-dash-sdk" + dependencies: + "@dashevo/dapi-client": "workspace:~" + "@dashevo/dashcore-lib": ~0.19.39 + "@dashevo/dashpay-contract": "workspace:~" + "@dashevo/dpns-contract": "workspace:~" + "@dashevo/dpp": "workspace:~" + "@dashevo/grpc-common": "workspace:~" + "@dashevo/masternode-reward-shares-contract": "workspace:~" + "@dashevo/wallet-lib": "workspace:~" + "@types/chai": ^4.2.12 + "@types/dirty-chai": ^2.0.2 + "@types/expect": ^24.3.0 + "@types/mocha": ^8.0.3 + "@types/node": ^14.6.0 + "@types/sinon": ^9.0.4 + "@types/sinon-chai": ^3.2.4 + assert: ^2.0.0 + browserify-zlib: ^0.2.0 + bs58: ^4.0.1 + buffer: ^6.0.3 + chai: ^4.3.4 + chance: ^1.1.6 + crypto-browserify: ^3.12.0 + dirty-chai: ^2.0.1 + dotenv-safe: ^8.2.0 + events: ^3.3.0 + https-browserify: ^1.0.0 + karma: ^6.3.4 + karma-chai: ^0.1.0 + karma-chrome-launcher: ^3.1.0 + karma-firefox-launcher: ^2.1.1 + karma-mocha: ^2.0.1 + karma-mocha-reporter: ^2.2.5 + karma-webpack: ^5.0.0 + mocha: ^9.1.2 + net: ^1.0.2 + node-inspect-extracted: ^1.0.8 + nodemon: ^2.0.4 + os-browserify: ^0.3.0 + path-browserify: ^1.0.1 + process: ^0.11.10 + rimraf: ^3.0.2 + sinon: ^11.1.2 + sinon-chai: ^3.7.0 + stream-browserify: ^3.0.0 + stream-http: ^3.2.0 + string_decoder: ^1.3.0 + terser-webpack-plugin: ^5.3.1 + tls: ^0.0.1 + ts-loader: ^8.0.2 + ts-mocha: ^8.0.0 + ts-mock-imports: ^1.3.0 + ts-node: ^10.4.0 + typescript: ^3.9.5 + url: ^0.11.0 + util: ^0.12.4 + webpack: ^5.59.1 + webpack-cli: ^4.9.1 + languageName: unknown + linkType: soft + +"dashdash@npm:^1.12.0": + version: 1.14.1 + resolution: "dashdash@npm:1.14.1" + dependencies: + assert-plus: ^1.0.0 + checksum: 3634c249570f7f34e3d34f866c93f866c5b417f0dd616275decae08147dcdf8fccfaa5947380ccfb0473998ea3a8057c0b4cd90c875740ee685d0624b2983598 + languageName: node + linkType: hard + +"dashmate@workspace:packages/dashmate": + version: 0.0.0-use.local + resolution: "dashmate@workspace:packages/dashmate" + dependencies: + "@dashevo/dashcore-lib": ~0.19.39 + "@dashevo/dashd-rpc": ^2.3.2 + "@dashevo/dashpay-contract": "workspace:~" + "@dashevo/docker-compose": ^0.24.1 + "@dashevo/dpns-contract": "workspace:~" + "@dashevo/dpp": "workspace:~" + "@dashevo/feature-flags-contract": "workspace:~" + "@dashevo/masternode-reward-shares-contract": "workspace:~" + "@dashevo/wallet-lib": "workspace:~" + "@oclif/core": ^1.3.4 + "@oclif/plugin-help": ^5.1.11 + ajv: ^8.6.0 + ajv-formats: ^2.1.1 + awilix: ^4.2.6 + bls-signatures: ^0.2.5 + chalk: ^4.1.0 + dash: "workspace:~" + dockerode: ^3.2.0 + dot: ^1.1.3 + dotenv: ^8.6.0 + enquirer: ^2.3.6 + eslint: ^7.32.0 + eslint-config-airbnb-base: ^14.2.1 + eslint-plugin-import: ^2.24.2 + glob: ^7.1.6 + globby: ^11 + hasbin: ^1.2.3 + jayson: ^3.3.4 + listr2: 3.5.0 + lodash.clonedeep: ^4.5.0 + lodash.get: ^4.4.2 + lodash.isequal: ^4.5.0 + lodash.merge: ^4.6.2 + lodash.set: ^4.3.2 + memory-streams: ^0.1.3 + node-fetch: ^2.6.1 + node-graceful: ^3.0.1 + oclif: ^2.4.5 + pretty-bytes: ^5.3.0 + pretty-ms: ^7.0.0 + public-ip: ^4.0.1 + rxjs: ^6.6.7 + semver: ^7.3.2 + strip-ansi: ^6.0.1 + table: ^5.4.6 + bin: + dashmate: bin/dashmate + languageName: unknown + linkType: soft + +"date-fns@npm:^2.16.1": + version: 2.28.0 + resolution: "date-fns@npm:2.28.0" + checksum: a0516b2e4f99b8bffc6cc5193349f185f195398385bdcaf07f17c2c4a24473c99d933eb0018be4142a86a6d46cb0b06be6440ad874f15e795acbedd6fd727a1f + languageName: node + linkType: hard + +"date-format@npm:^2.1.0": + version: 2.1.0 + resolution: "date-format@npm:2.1.0" + checksum: ff2c80c76021a315409b6ce2f08997f6e4a61ae68042dbf2cefda450207712a804aa30ac52e235f3de495dc915842507249c74e4668659835cc4870892042394 + languageName: node + linkType: hard + +"date-format@npm:^3.0.0": + version: 3.0.0 + resolution: "date-format@npm:3.0.0" + checksum: 9e1d224460d27f28fd0ce9ae72790bfb850a0d71ce97926633968d5ff9a4c86a537ff288edbe60cd4549a3c35bf5ad3b930d57cd5d579ce9da0a7e71605bdd74 + languageName: node + linkType: hard + +"dateformat@npm:^3.0.0": + version: 3.0.3 + resolution: "dateformat@npm:3.0.3" + checksum: ca4911148abb09887bd9bdcd632c399b06f3ecad709a18eb594d289a1031982f441e08e281db77ffebcb2cbcbfa1ac578a7cbfbf8743f41009aa5adc1846ed34 + languageName: node + linkType: hard + +"dateformat@npm:^4.5.0, dateformat@npm:^4.5.1": + version: 4.6.3 + resolution: "dateformat@npm:4.6.3" + checksum: c3aa0617c0a5b30595122bc8d1bee6276a9221e4d392087b41cbbdf175d9662ae0e50d0d6dcdf45caeac5153c4b5b0844265f8cd2b2245451e3da19e39e3b65d + languageName: node + linkType: hard + +"debug@npm:2.6.9, debug@npm:^2.2.0, debug@npm:^2.6.9": + version: 2.6.9 + resolution: "debug@npm:2.6.9" + dependencies: + ms: 2.0.0 + checksum: d2f51589ca66df60bf36e1fa6e4386b318c3f1e06772280eea5b1ae9fd3d05e9c2b7fd8a7d862457d00853c75b00451aa2d7459b924629ee385287a650f58fe6 + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.0.1, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:~4.3.1, debug@npm:~4.3.2": + version: 4.3.3 + resolution: "debug@npm:4.3.3" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 14472d56fe4a94dbcfaa6dbed2dd3849f1d72ba78104a1a328047bb564643ca49df0224c3a17fa63533fd11dd3d4c8636cd861191232a2c6735af00cc2d4de16 + languageName: node + linkType: hard + +"debug@npm:4.3.2": + version: 4.3.2 + resolution: "debug@npm:4.3.2" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 820ea160e267e23c953c9ed87e7ad93494d8cda2f7349af5e7e3bb236d23707ee3022f477d5a7d2ee86ef2bf7d60aa9ab22d1f58080d7deb9dccd073585e1e43 + languageName: node + linkType: hard + +"debug@npm:^3.2.7": + version: 3.2.7 + resolution: "debug@npm:3.2.7" + dependencies: + ms: ^2.1.1 + checksum: b3d8c5940799914d30314b7c3304a43305fd0715581a919dacb8b3176d024a782062368405b47491516d2091d6462d4d11f2f4974a405048094f8bfebfa3071c + languageName: node + linkType: hard + +"debuglog@npm:^1.0.1": + version: 1.0.1 + resolution: "debuglog@npm:1.0.1" + checksum: 970679f2eb7a73867e04d45b52583e7ec6dee1f33c058e9147702e72a665a9647f9c3d6e7c2f66f6bf18510b23eb5ded1b617e48ac1db23603809c5ddbbb9763 + languageName: node + linkType: hard + +"decamelize-keys@npm:^1.1.0": + version: 1.1.0 + resolution: "decamelize-keys@npm:1.1.0" + dependencies: + decamelize: ^1.1.0 + map-obj: ^1.0.0 + checksum: 8bc5d32e035a072f5dffc1f1f3d26ca7ab1fb44a9cade34c97ab6cd1e62c81a87e718101e96de07d78cecda20a3fdb955df958e46671ccad01bb8dcf0de2e298 + languageName: node + linkType: hard + +"decamelize@npm:^1.1.0, decamelize@npm:^1.2.0": + version: 1.2.0 + resolution: "decamelize@npm:1.2.0" + checksum: ad8c51a7e7e0720c70ec2eeb1163b66da03e7616d7b98c9ef43cce2416395e84c1e9548dd94f5f6ffecfee9f8b94251fc57121a8b021f2ff2469b2bae247b8aa + languageName: node + linkType: hard + +"decamelize@npm:^4.0.0": + version: 4.0.0 + resolution: "decamelize@npm:4.0.0" + checksum: b7d09b82652c39eead4d6678bb578e3bebd848add894b76d0f6b395bc45b2d692fb88d977e7cfb93c4ed6c119b05a1347cef261174916c2e75c0a8ca57da1809 + languageName: node + linkType: hard + +"decimal.js@npm:^10.3.1": + version: 10.3.1 + resolution: "decimal.js@npm:10.3.1" + checksum: 0351ac9f05fe050f23227aa6a4573bee2d58fa7378fcf28d969a8c789525032effb488a90320fd3fe86a66e17b4bc507d811b15eada5b7f0e7ec5d2af4c24a59 + languageName: node + linkType: hard + +"decompress-response@npm:^3.3.0": + version: 3.3.0 + resolution: "decompress-response@npm:3.3.0" + dependencies: + mimic-response: ^1.0.0 + checksum: 952552ac3bd7de2fc18015086b09468645c9638d98a551305e485230ada278c039c91116e946d07894b39ee53c0f0d5b6473f25a224029344354513b412d7380 + languageName: node + linkType: hard + +"deep-eql@npm:^3.0.1": + version: 3.0.1 + resolution: "deep-eql@npm:3.0.1" + dependencies: + type-detect: ^4.0.0 + checksum: 4f4c9fb79eb994fb6e81d4aa8b063adc40c00f831588aa65e20857d5d52f15fb23034a6576ecf886f7ff6222d5ae42e71e9b7d57113e0715b1df7ea1e812b125 + languageName: node + linkType: hard + +"deep-extend@npm:^0.6.0": + version: 0.6.0 + resolution: "deep-extend@npm:0.6.0" + checksum: 7be7e5a8d468d6b10e6a67c3de828f55001b6eb515d014f7aeb9066ce36bd5717161eb47d6a0f7bed8a9083935b465bc163ee2581c8b128d29bf61092fdf57a7 + languageName: node + linkType: hard + +"deep-is@npm:^0.1.3, deep-is@npm:~0.1.3": + version: 0.1.4 + resolution: "deep-is@npm:0.1.4" + checksum: edb65dd0d7d1b9c40b2f50219aef30e116cedd6fc79290e740972c132c09106d2e80aa0bc8826673dd5a00222d4179c84b36a790eef63a4c4bca75a37ef90804 + languageName: node + linkType: hard + +"default-require-extensions@npm:^3.0.0": + version: 3.0.0 + resolution: "default-require-extensions@npm:3.0.0" + dependencies: + strip-bom: ^4.0.0 + checksum: 0b5bdb6786ebb0ff6ef55386f37c8d221963fbbd3009588fe71032c85ca16da05eff2ad01bfe9bfc8bac5ce95a18f66b38c50d454482e3e9d2de1142424a3e7c + languageName: node + linkType: hard + +"defaults@npm:^1.0.3": + version: 1.0.3 + resolution: "defaults@npm:1.0.3" + dependencies: + clone: ^1.0.2 + checksum: 96e2112da6553d376afd5265ea7cbdb2a3b45535965d71ab8bb1da10c8126d168fdd5268799625324b368356d21ba2a7b3d4ec50961f11a47b7feb9de3d4413e + languageName: node + linkType: hard + +"defer-to-connect@npm:^1.0.1": + version: 1.1.3 + resolution: "defer-to-connect@npm:1.1.3" + checksum: 9491b301dcfa04956f989481ba7a43c2231044206269eb4ab64a52d6639ee15b1252262a789eb4239fb46ab63e44d4e408641bae8e0793d640aee55398cb3930 + languageName: node + linkType: hard + +"deferred-leveldown@npm:~5.3.0": + version: 5.3.0 + resolution: "deferred-leveldown@npm:5.3.0" + dependencies: + abstract-leveldown: ~6.2.1 + inherits: ^2.0.3 + checksum: 5631e153528bb9de1aa60d59a5065d1a519374c5e4c1d486f2190dba4008dcf5c2ee8dd7f2f81396fc4d5a6bb6e7d0055e3dfe68afe00da02adaa3bf329addf7 + languageName: node + linkType: hard + +"define-properties@npm:^1.1.3": + version: 1.1.3 + resolution: "define-properties@npm:1.1.3" + dependencies: + object-keys: ^1.0.12 + checksum: da80dba55d0cd76a5a7ab71ef6ea0ebcb7b941f803793e4e0257b384cb772038faa0c31659d244e82c4342edef841c1a1212580006a05a5068ee48223d787317 + languageName: node + linkType: hard + +"defined@npm:^1.0.0": + version: 1.0.0 + resolution: "defined@npm:1.0.0" + checksum: 77672997c5001773371c4dbcce98da0b3dc43089d6da2ad87c4b800adb727633cea8723ea3889fe0c2112a2404e2fd07e3bfd0e55f7426aa6441d8992045dbd5 + languageName: node + linkType: hard + +"delay@npm:^5.0.0": + version: 5.0.0 + resolution: "delay@npm:5.0.0" + checksum: 62f151151ecfde0d9afbb8a6be37a6d103c4cb24f35a20ef3fe56f920b0d0d0bb02bc9c0a3084d0179ef669ca332b91155f2ee4d9854622cd2cdba5fc95285f9 + languageName: node + linkType: hard + +"delayed-stream@npm:~1.0.0": + version: 1.0.0 + resolution: "delayed-stream@npm:1.0.0" + checksum: 46fe6e83e2cb1d85ba50bd52803c68be9bd953282fa7096f51fc29edd5d67ff84ff753c51966061e5ba7cb5e47ef6d36a91924eddb7f3f3483b1c560f77a0020 + languageName: node + linkType: hard + +"delegates@npm:^1.0.0": + version: 1.0.0 + resolution: "delegates@npm:1.0.0" + checksum: a51744d9b53c164ba9c0492471a1a2ffa0b6727451bdc89e31627fdf4adda9d51277cfcbfb20f0a6f08ccb3c436f341df3e92631a3440226d93a8971724771fd + languageName: node + linkType: hard + +"denque@npm:^1.4.1": + version: 1.5.1 + resolution: "denque@npm:1.5.1" + checksum: 4375ad19d5cea99f90effa82a8cecdaa10f4eb261fbcd7e47cd753ff2737f037aac8f7f4e031cc77f3966314c491c86a0d3b20c128aeee57f791b4662c45108e + languageName: node + linkType: hard + +"depd@npm:^1.1.2, depd@npm:~1.1.2": + version: 1.1.2 + resolution: "depd@npm:1.1.2" + checksum: 6b406620d269619852885ce15965272b829df6f409724415e0002c8632ab6a8c0a08ec1f0bd2add05dc7bd7507606f7e2cc034fa24224ab829580040b835ecd9 + languageName: node + linkType: hard + +"deprecation@npm:^2.0.0, deprecation@npm:^2.3.1": + version: 2.3.1 + resolution: "deprecation@npm:2.3.1" + checksum: f56a05e182c2c195071385455956b0c4106fe14e36245b00c689ceef8e8ab639235176a96977ba7c74afb173317fac2e0ec6ec7a1c6d1e6eaa401c586c714132 + languageName: node + linkType: hard + +"deps-sort@npm:^2.0.0": + version: 2.0.1 + resolution: "deps-sort@npm:2.0.1" + dependencies: + JSONStream: ^1.0.3 + shasum-object: ^1.0.0 + subarg: ^1.0.0 + through2: ^2.0.0 + bin: + deps-sort: bin/cmd.js + checksum: 1cbaad500aa1592d7497321faf39c7bb7b86ed0930b1efd0c54efdf68433fc53d8bc844bb220723c7861b397ba886495ebdab2cb0fbf13262d1342d98a88622b + languageName: node + linkType: hard + +"des.js@npm:^1.0.0": + version: 1.0.1 + resolution: "des.js@npm:1.0.1" + dependencies: + inherits: ^2.0.1 + minimalistic-assert: ^1.0.0 + checksum: 1ec2eedd7ed6bd61dd5e0519fd4c96124e93bb22de8a9d211b02d63e5dd152824853d919bb2090f965cc0e3eb9c515950a9836b332020d810f9c71feb0fd7df4 + languageName: node + linkType: hard + +"detect-indent@npm:^6.0.0": + version: 6.1.0 + resolution: "detect-indent@npm:6.1.0" + checksum: ab953a73c72dbd4e8fc68e4ed4bfd92c97eb6c43734af3900add963fd3a9316f3bc0578b018b24198d4c31a358571eff5f0656e81a1f3b9ad5c547d58b2d093d + languageName: node + linkType: hard + +"detective@npm:^5.2.0": + version: 5.2.0 + resolution: "detective@npm:5.2.0" + dependencies: + acorn-node: ^1.6.1 + defined: ^1.0.0 + minimist: ^1.1.1 + bin: + detective: bin/detective.js + checksum: 2ab266aecbd695b42e4703cfa560178ceac4308a74baece58185775426e65573d563d84f33e6a3b28ef3a544aa0c039c0730ada939c6458862e6643f66044f32 + languageName: node + linkType: hard + +"dezalgo@npm:^1.0.0": + version: 1.0.3 + resolution: "dezalgo@npm:1.0.3" + dependencies: + asap: ^2.0.0 + wrappy: 1 + checksum: 8b26238db91423b2702a7a6d9629d0019c37c415e7b6e75d4b3e8d27e9464e21cac3618dd145f4d4ee96c70cc6ff034227b5b8a0e9c09015a8bdbe6dace3cfb9 + languageName: node + linkType: hard + +"di@npm:^0.0.1": + version: 0.0.1 + resolution: "di@npm:0.0.1" + checksum: 3f09a99534d33e49264585db7f863ea8bc76c25c4d5a60df387c946018ecf1e1516b2c05a2092e5ca51fcdc08cefe609a6adc5253fa831626cb78cad4746505e + languageName: node + linkType: hard + +"diff-sequences@npm:^27.0.6": + version: 27.0.6 + resolution: "diff-sequences@npm:27.0.6" + checksum: f35ad024d426cd1026d6c98a1f604c41966a0e89712b05a38812fc11e645ff0e915ec17bc8f4b6910fed6df0b309b255aa6c7c77728be452c6dbbfa30aa2067b + languageName: node + linkType: hard + +"diff@npm:5.0.0, diff@npm:^5.0.0": + version: 5.0.0 + resolution: "diff@npm:5.0.0" + checksum: f19fe29284b633afdb2725c2a8bb7d25761ea54d321d8e67987ac851c5294be4afeab532bd84531e02583a3fe7f4014aa314a3eda84f5590e7a9e6b371ef3b46 + languageName: node + linkType: hard + +"diff@npm:^3.1.0": + version: 3.5.0 + resolution: "diff@npm:3.5.0" + checksum: 00842950a6551e26ce495bdbce11047e31667deea546527902661f25cc2e73358967ebc78cf86b1a9736ec3e14286433225f9970678155753a6291c3bca5227b + languageName: node + linkType: hard + +"diff@npm:^4.0.1": + version: 4.0.2 + resolution: "diff@npm:4.0.2" + checksum: f2c09b0ce4e6b301c221addd83bf3f454c0bc00caa3dd837cf6c127d6edf7223aa2bbe3b688feea110b7f262adbfc845b757c44c8a9f8c0c5b15d8fa9ce9d20d + languageName: node + linkType: hard + +"diffie-hellman@npm:^5.0.0": + version: 5.0.3 + resolution: "diffie-hellman@npm:5.0.3" + dependencies: + bn.js: ^4.1.0 + miller-rabin: ^4.0.0 + randombytes: ^2.0.0 + checksum: 0e620f322170c41076e70181dd1c24e23b08b47dbb92a22a644f3b89b6d3834b0f8ee19e37916164e5eb1ee26d2aa836d6129f92723995267250a0b541811065 + languageName: node + linkType: hard + +"dir-glob@npm:^3.0.1": + version: 3.0.1 + resolution: "dir-glob@npm:3.0.1" + dependencies: + path-type: ^4.0.0 + checksum: fa05e18324510d7283f55862f3161c6759a3f2f8dbce491a2fc14c8324c498286c54282c1f0e933cb930da8419b30679389499b919122952a4f8592362ef4615 + languageName: node + linkType: hard + +"dirty-chai@npm:^2.0.1": + version: 2.0.1 + resolution: "dirty-chai@npm:2.0.1" + peerDependencies: + chai: ">=2.2.1 <5" + checksum: 1e8602e78a0a47b9f701a66a4c2d7f375b9c8ae4235b79297bbc5926996bd9aa95f0759e5bc44a37d6046ebe9ba78a4151e9d628fc05bdb986599650abf9d805 + languageName: node + linkType: hard + +"dns-packet@npm:^5.2.4": + version: 5.3.0 + resolution: "dns-packet@npm:5.3.0" + dependencies: + "@leichtgewicht/ip-codec": ^2.0.1 + checksum: ac93e0f6d43ef5d31250279a173d95f7a946e4affac587b0417ecf13dc0e770a974e28391d86cd4b937bcc082520bfe90186c5c7c778597fe569d605742b8ade + languageName: node + linkType: hard + +"dns-socket@npm:^4.2.2": + version: 4.2.2 + resolution: "dns-socket@npm:4.2.2" + dependencies: + dns-packet: ^5.2.4 + checksum: d02b83ecc9b0f1d2fc459f93c6390c768a8805002637d1f74113d623fa7b2478a695ade7761a0a847622781f5e6dd008a9a1469ac75a617bdf1b775f2156943c + languageName: node + linkType: hard + +"docker-modem@npm:^3.0.0": + version: 3.0.3 + resolution: "docker-modem@npm:3.0.3" + dependencies: + debug: ^4.1.1 + readable-stream: ^3.5.0 + split-ca: ^1.0.1 + ssh2: ^1.4.0 + checksum: 4ad495d17a7bbb29f48e3bf8ab74508848a3ca62c2dffc399fc0b9b2d1caccb1be54cc53001d5e0d56069e6cb4a91da4b017240733080b6648a66b40345e1f96 + languageName: node + linkType: hard + +"dockerode@npm:^3.2.0, dockerode@npm:^3.2.1": + version: 3.3.1 + resolution: "dockerode@npm:3.3.1" + dependencies: + docker-modem: ^3.0.0 + tar-fs: ~2.0.1 + checksum: 930162ae2d8a1fe0e99d9a5885b09aa438da6274d4a30cb90e73046655dbc90764eb755361a63ba08f167e257c4d649d67bce71f650461a20b97fcde0af05ca5 + languageName: node + linkType: hard + +"doctrine@npm:3.0.0, doctrine@npm:^3.0.0": + version: 3.0.0 + resolution: "doctrine@npm:3.0.0" + dependencies: + esutils: ^2.0.2 + checksum: fd7673ca77fe26cd5cba38d816bc72d641f500f1f9b25b83e8ce28827fe2da7ad583a8da26ab6af85f834138cf8dae9f69b0cd6ab925f52ddab1754db44d99ce + languageName: node + linkType: hard + +"doctrine@npm:^2.1.0": + version: 2.1.0 + resolution: "doctrine@npm:2.1.0" + dependencies: + esutils: ^2.0.2 + checksum: a45e277f7feaed309fe658ace1ff286c6e2002ac515af0aaf37145b8baa96e49899638c7cd47dccf84c3d32abfc113246625b3ac8f552d1046072adee13b0dc8 + languageName: node + linkType: hard + +"dom-serialize@npm:^2.2.1": + version: 2.2.1 + resolution: "dom-serialize@npm:2.2.1" + dependencies: + custom-event: ~1.0.0 + ent: ~2.2.0 + extend: ^3.0.0 + void-elements: ^2.0.0 + checksum: 48262e299a694dbfa32905ecceb29b89f2ce59adfc00cb676284f85ee0c8db0225e07961cbf9b06bf309291deebf52c958f855a5b6709d556000acf46d5a46ef + languageName: node + linkType: hard + +"domain-browser@npm:^1.2.0": + version: 1.2.0 + resolution: "domain-browser@npm:1.2.0" + checksum: 8f1235c7f49326fb762f4675795246a6295e7dd566b4697abec24afdba2460daa7dfbd1a73d31efbf5606b3b7deadb06ce47cf06f0a476e706153d62a4ff2b90 + languageName: node + linkType: hard + +"dot-prop@npm:^5.1.0, dot-prop@npm:^5.2.0": + version: 5.3.0 + resolution: "dot-prop@npm:5.3.0" + dependencies: + is-obj: ^2.0.0 + checksum: d5775790093c234ef4bfd5fbe40884ff7e6c87573e5339432870616331189f7f5d86575c5b5af2dcf0f61172990f4f734d07844b1f23482fff09e3c4bead05ea + languageName: node + linkType: hard + +"dot@npm:^1.1.3": + version: 1.1.3 + resolution: "dot@npm:1.1.3" + bin: + dottojs: ./bin/dot-packer + checksum: 9a2ecf7b5ff8c5121481702e9fdc7eb802ebf5b9e318a73631f70cb9484c7bb6973322912c173f5fa52d1f9408eaef4d6468e9ef8af3899664f4d42091f5f868 + languageName: node + linkType: hard + +"dotenv-expand@npm:^5.1.0": + version: 5.1.0 + resolution: "dotenv-expand@npm:5.1.0" + checksum: 8017675b7f254384915d55f9eb6388e577cf0a1231a28d54b0ca03b782be9501b0ac90ac57338636d395fa59051e6209e9b44b8ddf169ce6076dffb5dea227d3 + languageName: node + linkType: hard + +"dotenv-safe@npm:^8.2.0": + version: 8.2.0 + resolution: "dotenv-safe@npm:8.2.0" + dependencies: + dotenv: ^8.2.0 + checksum: 8b73770330528d77630009afa44239127c44d634b0fa7d7545932a8e72686d774ceadea62d381ed7c911e0facac4ee1489132e44b746f36eb8f2e3ee6929eb78 + languageName: node + linkType: hard + +"dotenv@npm:^8.2.0, dotenv@npm:^8.6.0": + version: 8.6.0 + resolution: "dotenv@npm:8.6.0" + checksum: 38e902c80b0666ab59e9310a3d24ed237029a7ce34d976796349765ac96b8d769f6df19090f1f471b77a25ca391971efde8a1ea63bb83111bd8bec8e5cc9b2cd + languageName: node + linkType: hard + +"duplexer2@npm:^0.1.2, duplexer2@npm:~0.1.0, duplexer2@npm:~0.1.2": + version: 0.1.4 + resolution: "duplexer2@npm:0.1.4" + dependencies: + readable-stream: ^2.0.2 + checksum: 744961f03c7f54313f90555ac20284a3fb7bf22fdff6538f041a86c22499560eb6eac9d30ab5768054137cb40e6b18b40f621094e0261d7d8c35a37b7a5ad241 + languageName: node + linkType: hard + +"duplexer3@npm:^0.1.4": + version: 0.1.4 + resolution: "duplexer3@npm:0.1.4" + checksum: c2fd6969314607d23439c583699aaa43c4100d66b3e161df55dccd731acc57d5c81a64bb4f250805fbe434ddb1d2623fee2386fb890f5886ca1298690ec53415 + languageName: node + linkType: hard + +"ecc-jsbn@npm:~0.1.1": + version: 0.1.2 + resolution: "ecc-jsbn@npm:0.1.2" + dependencies: + jsbn: ~0.1.0 + safer-buffer: ^2.1.0 + checksum: 22fef4b6203e5f31d425f5b711eb389e4c6c2723402e389af394f8411b76a488fa414d309d866e2b577ce3e8462d344205545c88a8143cc21752a5172818888a + languageName: node + linkType: hard + +"ee-first@npm:1.1.1": + version: 1.1.1 + resolution: "ee-first@npm:1.1.1" + checksum: 1b4cac778d64ce3b582a7e26b218afe07e207a0f9bfe13cc7395a6d307849cfe361e65033c3251e00c27dd060cab43014c2d6b2647676135e18b77d2d05b3f4f + languageName: node + linkType: hard + +"ejs@npm:^3.1.6": + version: 3.1.6 + resolution: "ejs@npm:3.1.6" + dependencies: + jake: ^10.6.1 + bin: + ejs: ./bin/cli.js + checksum: 81a9cdea0b4ded3b5a4b212b7c17e20bb07468f08394e2d519708d367957a70aef3d282a6d5d38bf6ad313ba25802b9193d4227f29b084d2ee0f28d115141d48 + languageName: node + linkType: hard + +"electron-to-chromium@npm:^1.3.896": + version: 1.3.903 + resolution: "electron-to-chromium@npm:1.3.903" + checksum: 0f96af03efee4691c6e4cf76524baf8ea5dc38cb2e74282e5c6d2dad65bc2aa0e99afdcb386bf26eed4512a2356457fddbd6963167a1ee381fd41e5b29be90ee + languageName: node + linkType: hard + +"elliptic@npm:6.5.3": + version: 6.5.3 + resolution: "elliptic@npm:6.5.3" + dependencies: + bn.js: ^4.4.0 + brorand: ^1.0.1 + hash.js: ^1.0.0 + hmac-drbg: ^1.0.0 + inherits: ^2.0.1 + minimalistic-assert: ^1.0.0 + minimalistic-crypto-utils: ^1.0.0 + checksum: fe1e546ed35ff69622130eb56abd3df8b4e9f009922ec2f1a4437d9c752a026d570a9863751912076effa1060f559bee8d816d6e89835fe8111834694e812165 + languageName: node + linkType: hard + +"emoji-regex@npm:^7.0.1": + version: 7.0.3 + resolution: "emoji-regex@npm:7.0.3" + checksum: 9159b2228b1511f2870ac5920f394c7e041715429a68459ebe531601555f11ea782a8e1718f969df2711d38c66268174407cbca57ce36485544f695c2dfdc96e + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: d4c5c39d5a9868b5fa152f00cada8a936868fd3367f33f71be515ecee4c803132d11b31a6222b2571b1e5f7e13890156a94880345594d0ce7e3c9895f560f192 + languageName: node + linkType: hard + +"emojis-list@npm:^3.0.0": + version: 3.0.0 + resolution: "emojis-list@npm:3.0.0" + checksum: ddaaa02542e1e9436c03970eeed445f4ed29a5337dfba0fe0c38dfdd2af5da2429c2a0821304e8a8d1cadf27fdd5b22ff793571fa803ae16852a6975c65e8e70 + languageName: node + linkType: hard + +"enabled@npm:2.0.x": + version: 2.0.0 + resolution: "enabled@npm:2.0.0" + checksum: 9d256d89f4e8a46ff988c6a79b22fa814b4ffd82826c4fdacd9b42e9b9465709d3b748866d0ab4d442dfc6002d81de7f7b384146ccd1681f6a7f868d2acca063 + languageName: node + linkType: hard + +"encodeurl@npm:~1.0.2": + version: 1.0.2 + resolution: "encodeurl@npm:1.0.2" + checksum: e50e3d508cdd9c4565ba72d2012e65038e5d71bdc9198cb125beb6237b5b1ade6c0d343998da9e170fb2eae52c1bed37d4d6d98a46ea423a0cddbed5ac3f780c + languageName: node + linkType: hard + +"encoding@npm:^0.1.12": + version: 0.1.13 + resolution: "encoding@npm:0.1.13" + dependencies: + iconv-lite: ^0.6.2 + checksum: bb98632f8ffa823996e508ce6a58ffcf5856330fde839ae42c9e1f436cc3b5cc651d4aeae72222916545428e54fd0f6aa8862fd8d25bdbcc4589f1e3f3715e7f + languageName: node + linkType: hard + +"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": + version: 1.4.4 + resolution: "end-of-stream@npm:1.4.4" + dependencies: + once: ^1.4.0 + checksum: 530a5a5a1e517e962854a31693dbb5c0b2fc40b46dad2a56a2deec656ca040631124f4795823acc68238147805f8b021abbe221f4afed5ef3c8e8efc2024908b + languageName: node + linkType: hard + +"engine.io-parser@npm:~5.0.0": + version: 5.0.2 + resolution: "engine.io-parser@npm:5.0.2" + dependencies: + base64-arraybuffer: ~1.0.1 + checksum: bd65c3cdce29c31308168fa0ca4cd67b97f515d6016d55b2951de8c6fb698e4025da5e16acaa5a642463f00791121c15c37b96883d4a2f6f0ea1942962c1e1e9 + languageName: node + linkType: hard + +"engine.io@npm:~6.1.0": + version: 6.1.0 + resolution: "engine.io@npm:6.1.0" + dependencies: + "@types/cookie": ^0.4.1 + "@types/cors": ^2.8.12 + "@types/node": ">=10.0.0" + accepts: ~1.3.4 + base64id: 2.0.0 + cookie: ~0.4.1 + cors: ~2.8.5 + debug: ~4.3.1 + engine.io-parser: ~5.0.0 + ws: ~8.2.3 + checksum: 37ff47e24c471d47d01ee2afbe9e7603013e256424a554ab73794a5bd4f69f08a4ba7d51f68832dcee028894f74e19043e173a071f26fc1d31493847100fb106 + languageName: node + linkType: hard + +"enhanced-resolve@npm:^4.0.0": + version: 4.5.0 + resolution: "enhanced-resolve@npm:4.5.0" + dependencies: + graceful-fs: ^4.1.2 + memory-fs: ^0.5.0 + tapable: ^1.0.0 + checksum: 4d87488584c4d67d356ef4ba04978af4b2d4d18190cb859efac8e8475a34d5d6c069df33faa5a0a22920b0586dbf330f6a08d52bb15a8771a9ce4d70a2da74ba + languageName: node + linkType: hard + +"enhanced-resolve@npm:^5.8.3": + version: 5.8.3 + resolution: "enhanced-resolve@npm:5.8.3" + dependencies: + graceful-fs: ^4.2.4 + tapable: ^2.2.0 + checksum: d79fbe531106448b768bb0673fb623ec0202d7ee70373ab7d4f4745d5dfe0806f38c9db7e7da8c941288fe475ab3d538db3791fce522056eeea40ca398c9e287 + languageName: node + linkType: hard + +"enquirer@npm:^2.3.5, enquirer@npm:^2.3.6": + version: 2.3.6 + resolution: "enquirer@npm:2.3.6" + dependencies: + ansi-colors: ^4.1.1 + checksum: 1c0911e14a6f8d26721c91e01db06092a5f7675159f0261d69c403396a385afd13dd76825e7678f66daffa930cfaa8d45f506fb35f818a2788463d022af1b884 + languageName: node + linkType: hard + +"ent@npm:~2.2.0": + version: 2.2.0 + resolution: "ent@npm:2.2.0" + checksum: f588b5707d6fef36011ea10d530645912a69530a1eb0831f8708c498ac028363a7009f45cfadd28ceb4dafd9ac17ec15213f88d09ce239cd033cfe1328dd7d7d + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e + languageName: node + linkType: hard + +"envinfo@npm:^7.7.3": + version: 7.8.1 + resolution: "envinfo@npm:7.8.1" + bin: + envinfo: dist/cli.js + checksum: de736c98d6311c78523628ff127af138451b162e57af5293c1b984ca821d0aeb9c849537d2fde0434011bed33f6bca5310ca2aab8a51a3f28fc719e89045d648 + languageName: node + linkType: hard + +"err-code@npm:^2.0.2": + version: 2.0.3 + resolution: "err-code@npm:2.0.3" + checksum: 8b7b1be20d2de12d2255c0bc2ca638b7af5171142693299416e6a9339bd7d88fc8d7707d913d78e0993176005405a236b066b45666b27b797252c771156ace54 + languageName: node + linkType: hard + +"errno@npm:^0.1.3, errno@npm:~0.1.1": + version: 0.1.8 + resolution: "errno@npm:0.1.8" + dependencies: + prr: ~1.0.1 + bin: + errno: cli.js + checksum: 1271f7b9fbb3bcbec76ffde932485d1e3561856d21d847ec613a9722ee924cdd4e523a62dc71a44174d91e898fe21fdc8d5b50823f4b5e0ce8c35c8271e6ef4a + languageName: node + linkType: hard + +"error-ex@npm:^1.3.1": + version: 1.3.2 + resolution: "error-ex@npm:1.3.2" + dependencies: + is-arrayish: ^0.2.1 + checksum: c1c2b8b65f9c91b0f9d75f0debaa7ec5b35c266c2cac5de412c1a6de86d4cbae04ae44e510378cb14d032d0645a36925d0186f8bb7367bcc629db256b743a001 + languageName: node + linkType: hard + +"error@npm:^10.4.0": + version: 10.4.0 + resolution: "error@npm:10.4.0" + checksum: 26c9ecb7af8de775c7f8c143aa2557cf42bf6dddbef1d68db8ad3501a29af872bea53ca4e2af20ac464bf7475a2827bd37898846fea27692c3ce66500a3e3fc2 + languageName: node + linkType: hard + +"es-abstract@npm:^1.18.5, es-abstract@npm:^1.19.0, es-abstract@npm:^1.19.1": + version: 1.19.1 + resolution: "es-abstract@npm:1.19.1" + dependencies: + call-bind: ^1.0.2 + es-to-primitive: ^1.2.1 + function-bind: ^1.1.1 + get-intrinsic: ^1.1.1 + get-symbol-description: ^1.0.0 + has: ^1.0.3 + has-symbols: ^1.0.2 + internal-slot: ^1.0.3 + is-callable: ^1.2.4 + is-negative-zero: ^2.0.1 + is-regex: ^1.1.4 + is-shared-array-buffer: ^1.0.1 + is-string: ^1.0.7 + is-weakref: ^1.0.1 + object-inspect: ^1.11.0 + object-keys: ^1.1.1 + object.assign: ^4.1.2 + string.prototype.trimend: ^1.0.4 + string.prototype.trimstart: ^1.0.4 + unbox-primitive: ^1.0.1 + checksum: b6be8410672c5364db3fb01eb786e30c7b4bb32b4af63d381c08840f4382c4a168e7855cd338bf59d4f1a1a1138f4d748d1fd40ec65aaa071876f9e9fbfed949 + languageName: node + linkType: hard + +"es-module-lexer@npm:^0.9.0": + version: 0.9.3 + resolution: "es-module-lexer@npm:0.9.3" + checksum: 84bbab23c396281db2c906c766af58b1ae2a1a2599844a504df10b9e8dc77ec800b3211fdaa133ff700f5703d791198807bba25d9667392d27a5e9feda344da8 + languageName: node + linkType: hard + +"es-to-primitive@npm:^1.2.1": + version: 1.2.1 + resolution: "es-to-primitive@npm:1.2.1" + dependencies: + is-callable: ^1.1.4 + is-date-object: ^1.0.1 + is-symbol: ^1.0.2 + checksum: 4ead6671a2c1402619bdd77f3503991232ca15e17e46222b0a41a5d81aebc8740a77822f5b3c965008e631153e9ef0580540007744521e72de8e33599fca2eed + languageName: node + linkType: hard + +"es6-error@npm:^4.0.1": + version: 4.1.1 + resolution: "es6-error@npm:4.1.1" + checksum: ae41332a51ec1323da6bbc5d75b7803ccdeddfae17c41b6166ebbafc8e8beb7a7b80b884b7fab1cc80df485860ac3c59d78605e860bb4f8cd816b3d6ade0d010 + languageName: node + linkType: hard + +"es6-object-assign@npm:^1.1.0": + version: 1.1.0 + resolution: "es6-object-assign@npm:1.1.0" + checksum: 8d4fdf63484d78b5c64cacc2c2e1165bc7b6a64b739d2a9db6a4dc8641d99cc9efb433cdd4dc3d3d6b00bfa6ce959694e4665e3255190339945c5f33b692b5d8 + languageName: node + linkType: hard + +"es6-promise@npm:^4.0.3": + version: 4.2.8 + resolution: "es6-promise@npm:4.2.8" + checksum: 95614a88873611cb9165a85d36afa7268af5c03a378b35ca7bda9508e1d4f1f6f19a788d4bc755b3fd37c8ebba40782018e02034564ff24c9d6fa37e959ad57d + languageName: node + linkType: hard + +"es6-promisify@npm:^5.0.0": + version: 5.0.0 + resolution: "es6-promisify@npm:5.0.0" + dependencies: + es6-promise: ^4.0.3 + checksum: fbed9d791598831413be84a5374eca8c24800ec71a16c1c528c43a98e2dadfb99331483d83ae6094ddb9b87e6f799a15d1553cebf756047e0865c753bc346b92 + languageName: node + linkType: hard + +"escalade@npm:^3.1.1": + version: 3.1.1 + resolution: "escalade@npm:3.1.1" + checksum: a3e2a99f07acb74b3ad4989c48ca0c3140f69f923e56d0cba0526240ee470b91010f9d39001f2a4a313841d237ede70a729e92125191ba5d21e74b106800b133 + languageName: node + linkType: hard + +"escape-goat@npm:^2.0.0": + version: 2.1.1 + resolution: "escape-goat@npm:2.1.1" + checksum: ce05c70c20dd7007b60d2d644b625da5412325fdb57acf671ba06cb2ab3cd6789e2087026921a05b665b0a03fadee2955e7fc0b9a67da15a6551a980b260eba7 + languageName: node + linkType: hard + +"escape-html@npm:~1.0.3": + version: 1.0.3 + resolution: "escape-html@npm:1.0.3" + checksum: 6213ca9ae00d0ab8bccb6d8d4e0a98e76237b2410302cf7df70aaa6591d509a2a37ce8998008cbecae8fc8ffaadf3fb0229535e6a145f3ce0b211d060decbb24 + languageName: node + linkType: hard + +"escape-latex@npm:^1.2.0": + version: 1.2.0 + resolution: "escape-latex@npm:1.2.0" + checksum: 73a787319f0965ecb8244bb38bf3a3cba872f0b9a5d3da8821140e9f39fe977045dc953a62b1a2bed4d12bfccbe75a7d8ec786412bf00739eaa2f627d0a8e0d6 + languageName: node + linkType: hard + +"escape-string-regexp@npm:4.0.0, escape-string-regexp@npm:^4.0.0": + version: 4.0.0 + resolution: "escape-string-regexp@npm:4.0.0" + checksum: 98b48897d93060f2322108bf29db0feba7dd774be96cd069458d1453347b25ce8682ecc39859d4bca2203cc0ab19c237bcc71755eff49a0f8d90beadeeba5cc5 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^1.0.2, escape-string-regexp@npm:^1.0.5": + version: 1.0.5 + resolution: "escape-string-regexp@npm:1.0.5" + checksum: 6092fda75c63b110c706b6a9bfde8a612ad595b628f0bd2147eea1d3406723020810e591effc7db1da91d80a71a737a313567c5abb3813e8d9c71f4aa595b410 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^2.0.0": + version: 2.0.0 + resolution: "escape-string-regexp@npm:2.0.0" + checksum: 9f8a2d5743677c16e85c810e3024d54f0c8dea6424fad3c79ef6666e81dd0846f7437f5e729dfcdac8981bc9e5294c39b4580814d114076b8d36318f46ae4395 + languageName: node + linkType: hard + +"escodegen@npm:^2.0.0": + version: 2.0.0 + resolution: "escodegen@npm:2.0.0" + dependencies: + esprima: ^4.0.1 + estraverse: ^5.2.0 + esutils: ^2.0.2 + optionator: ^0.8.1 + source-map: ~0.6.1 + dependenciesMeta: + source-map: + optional: true + bin: + escodegen: bin/escodegen.js + esgenerate: bin/esgenerate.js + checksum: 5aa6b2966fafe0545e4e77936300cc94ad57cfe4dc4ebff9950492eaba83eef634503f12d7e3cbd644ecc1bab388ad0e92b06fd32222c9281a75d1cf02ec6cef + languageName: node + linkType: hard + +"eslint-config-airbnb-base@npm:^14.2.1": + version: 14.2.1 + resolution: "eslint-config-airbnb-base@npm:14.2.1" + dependencies: + confusing-browser-globals: ^1.0.10 + object.assign: ^4.1.2 + object.entries: ^1.1.2 + peerDependencies: + eslint: ^5.16.0 || ^6.8.0 || ^7.2.0 + eslint-plugin-import: ^2.22.1 + checksum: 858bea748a3c8685b52fcf2488e6a0b964022f8387f4ee1e69cb707d4fda2a409f09eb8eea658bcd83fae3519967d10208ba7576dd3d3202b8cf0b9d1a6e21eb + languageName: node + linkType: hard + +"eslint-config-prettier@npm:^8.3.0": + version: 8.3.0 + resolution: "eslint-config-prettier@npm:8.3.0" + peerDependencies: + eslint: ">=7.0.0" + bin: + eslint-config-prettier: bin/cli.js + checksum: df4cea3032671995bb5ab07e016169072f7fa59f44a53251664d9ca60951b66cdc872683b5c6a3729c91497c11490ca44a79654b395dd6756beb0c3903a37196 + languageName: node + linkType: hard + +"eslint-import-resolver-node@npm:^0.3.6": + version: 0.3.6 + resolution: "eslint-import-resolver-node@npm:0.3.6" + dependencies: + debug: ^3.2.7 + resolve: ^1.20.0 + checksum: 6266733af1e112970e855a5bcc2d2058fb5ae16ad2a6d400705a86b29552b36131ffc5581b744c23d550de844206fb55e9193691619ee4dbf225c4bde526b1c8 + languageName: node + linkType: hard + +"eslint-module-utils@npm:^2.7.1": + version: 2.7.1 + resolution: "eslint-module-utils@npm:2.7.1" + dependencies: + debug: ^3.2.7 + find-up: ^2.1.0 + pkg-dir: ^2.0.0 + checksum: c30dfa125aafe65e5f6a30a31c26932106fcf09934a2f47d7f8a393ed9106da7b07416f2337b55c85f9db0175c873ee0827be5429a24ec381b49940f342b9ac3 + languageName: node + linkType: hard + +"eslint-plugin-import@npm:^2.24.2": + version: 2.25.3 + resolution: "eslint-plugin-import@npm:2.25.3" + dependencies: + array-includes: ^3.1.4 + array.prototype.flat: ^1.2.5 + debug: ^2.6.9 + doctrine: ^2.1.0 + eslint-import-resolver-node: ^0.3.6 + eslint-module-utils: ^2.7.1 + has: ^1.0.3 + is-core-module: ^2.8.0 + is-glob: ^4.0.3 + minimatch: ^3.0.4 + object.values: ^1.1.5 + resolve: ^1.20.0 + tsconfig-paths: ^3.11.0 + peerDependencies: + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + checksum: 8bdf4b1fafb0e5c8f57a1673f72d84307d32c06a23942990d198c8b32a85a5ae0098872d1ef5bf80d7dfe8ec542f6a671e3c5e706731a80b493c9015f7a147f5 + languageName: node + linkType: hard + +"eslint-plugin-jsdoc@npm:^27.0.0": + version: 27.1.2 + resolution: "eslint-plugin-jsdoc@npm:27.1.2" + dependencies: + comment-parser: ^0.7.5 + debug: ^4.1.1 + jsdoctypeparser: ^6.1.0 + lodash: ^4.17.15 + regextras: ^0.7.1 + semver: ^6.3.0 + spdx-expression-parse: ^3.0.1 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 + checksum: df6550e0570178455d753aaa7ed89fdd55f5c06892f5dd4edd3c25552a70348cdd3803958cc4186ad71b7fcdf5888a948526bd4da52f7787c08185cd639380e8 + languageName: node + linkType: hard + +"eslint-scope@npm:5.1.1, eslint-scope@npm:^5.1.1": + version: 5.1.1 + resolution: "eslint-scope@npm:5.1.1" + dependencies: + esrecurse: ^4.3.0 + estraverse: ^4.1.1 + checksum: 47e4b6a3f0cc29c7feedee6c67b225a2da7e155802c6ea13bbef4ac6b9e10c66cd2dcb987867ef176292bf4e64eccc680a49e35e9e9c669f4a02bac17e86abdb + languageName: node + linkType: hard + +"eslint-utils@npm:^2.1.0": + version: 2.1.0 + resolution: "eslint-utils@npm:2.1.0" + dependencies: + eslint-visitor-keys: ^1.1.0 + checksum: 27500938f348da42100d9e6ad03ae29b3de19ba757ae1a7f4a087bdcf83ac60949bbb54286492ca61fac1f5f3ac8692dd21537ce6214240bf95ad0122f24d71d + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^1.0.0, eslint-visitor-keys@npm:^1.1.0, eslint-visitor-keys@npm:^1.3.0": + version: 1.3.0 + resolution: "eslint-visitor-keys@npm:1.3.0" + checksum: 37a19b712f42f4c9027e8ba98c2b06031c17e0c0a4c696cd429bd9ee04eb43889c446f2cd545e1ff51bef9593fcec94ecd2c2ef89129fcbbf3adadbef520376a + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^2.0.0": + version: 2.1.0 + resolution: "eslint-visitor-keys@npm:2.1.0" + checksum: e3081d7dd2611a35f0388bbdc2f5da60b3a3c5b8b6e928daffff7391146b434d691577aa95064c8b7faad0b8a680266bcda0a42439c18c717b80e6718d7e267d + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^3.1.0": + version: 3.1.0 + resolution: "eslint-visitor-keys@npm:3.1.0" + checksum: fd2d613bb315bc549068ca97771d868437fb60c8f13ef8d6d54669773ff53f814b759fa9e57966f15e4c50a5f5e11c6ba47060b8f201f9776311f6c5d5c11b70 + languageName: node + linkType: hard + +"eslint@npm:^7.32.0": + version: 7.32.0 + resolution: "eslint@npm:7.32.0" + dependencies: + "@babel/code-frame": 7.12.11 + "@eslint/eslintrc": ^0.4.3 + "@humanwhocodes/config-array": ^0.5.0 + ajv: ^6.10.0 + chalk: ^4.0.0 + cross-spawn: ^7.0.2 + debug: ^4.0.1 + doctrine: ^3.0.0 + enquirer: ^2.3.5 + escape-string-regexp: ^4.0.0 + eslint-scope: ^5.1.1 + eslint-utils: ^2.1.0 + eslint-visitor-keys: ^2.0.0 + espree: ^7.3.1 + esquery: ^1.4.0 + esutils: ^2.0.2 + fast-deep-equal: ^3.1.3 + file-entry-cache: ^6.0.1 + functional-red-black-tree: ^1.0.1 + glob-parent: ^5.1.2 + globals: ^13.6.0 + ignore: ^4.0.6 + import-fresh: ^3.0.0 + imurmurhash: ^0.1.4 + is-glob: ^4.0.0 + js-yaml: ^3.13.1 + json-stable-stringify-without-jsonify: ^1.0.1 + levn: ^0.4.1 + lodash.merge: ^4.6.2 + minimatch: ^3.0.4 + natural-compare: ^1.4.0 + optionator: ^0.9.1 + progress: ^2.0.0 + regexpp: ^3.1.0 + semver: ^7.2.1 + strip-ansi: ^6.0.0 + strip-json-comments: ^3.1.0 + table: ^6.0.9 + text-table: ^0.2.0 + v8-compile-cache: ^2.0.3 + bin: + eslint: bin/eslint.js + checksum: cc85af9985a3a11085c011f3d27abe8111006d34cc274291b3c4d7bea51a4e2ff6135780249becd919ba7f6d6d1ecc38a6b73dacb6a7be08d38453b344dc8d37 + languageName: node + linkType: hard + +"espree@npm:^7.3.0, espree@npm:^7.3.1": + version: 7.3.1 + resolution: "espree@npm:7.3.1" + dependencies: + acorn: ^7.4.0 + acorn-jsx: ^5.3.1 + eslint-visitor-keys: ^1.3.0 + checksum: aa9b50dcce883449af2e23bc2b8d9abb77118f96f4cb313935d6b220f77137eaef7724a83c3f6243b96bc0e4ab14766198e60818caad99f9519ae5a336a39b45 + languageName: node + linkType: hard + +"espree@npm:^9.1.0": + version: 9.1.0 + resolution: "espree@npm:9.1.0" + dependencies: + acorn: ^8.6.0 + acorn-jsx: ^5.3.1 + eslint-visitor-keys: ^3.1.0 + checksum: ba9b0f759c49c19a098e0bb97f3b9b05441a60dec3f868bc412ae300e00ba20cb0bd2c6a1bdd6c4f0056e6382650bf45b4982d81e67ad0210c1c16b336f73c39 + languageName: node + linkType: hard + +"esprima@npm:^4.0.0, esprima@npm:^4.0.1, esprima@npm:~4.0.0": + version: 4.0.1 + resolution: "esprima@npm:4.0.1" + bin: + esparse: ./bin/esparse.js + esvalidate: ./bin/esvalidate.js + checksum: b45bc805a613dbea2835278c306b91aff6173c8d034223fa81498c77dcbce3b2931bf6006db816f62eacd9fd4ea975dfd85a5b7f3c6402cfd050d4ca3c13a628 + languageName: node + linkType: hard + +"esquery@npm:^1.4.0": + version: 1.4.0 + resolution: "esquery@npm:1.4.0" + dependencies: + estraverse: ^5.1.0 + checksum: a0807e17abd7fbe5fbd4fab673038d6d8a50675cdae6b04fbaa520c34581be0c5fa24582990e8acd8854f671dd291c78bb2efb9e0ed5b62f33bac4f9cf820210 + languageName: node + linkType: hard + +"esrecurse@npm:^4.3.0": + version: 4.3.0 + resolution: "esrecurse@npm:4.3.0" + dependencies: + estraverse: ^5.2.0 + checksum: ebc17b1a33c51cef46fdc28b958994b1dc43cd2e86237515cbc3b4e5d2be6a811b2315d0a1a4d9d340b6d2308b15322f5c8291059521cc5f4802f65e7ec32837 + languageName: node + linkType: hard + +"estraverse@npm:^4.1.1": + version: 4.3.0 + resolution: "estraverse@npm:4.3.0" + checksum: a6299491f9940bb246124a8d44b7b7a413a8336f5436f9837aaa9330209bd9ee8af7e91a654a3545aee9c54b3308e78ee360cef1d777d37cfef77d2fa33b5827 + languageName: node + linkType: hard + +"estraverse@npm:^5.1.0, estraverse@npm:^5.2.0, estraverse@npm:^5.3.0": + version: 5.3.0 + resolution: "estraverse@npm:5.3.0" + checksum: 072780882dc8416ad144f8fe199628d2b3e7bbc9989d9ed43795d2c90309a2047e6bc5979d7e2322a341163d22cfad9e21f4110597fe487519697389497e4e2b + languageName: node + linkType: hard + +"esutils@npm:^2.0.2": + version: 2.0.3 + resolution: "esutils@npm:2.0.3" + checksum: 22b5b08f74737379a840b8ed2036a5fb35826c709ab000683b092d9054e5c2a82c27818f12604bfc2a9a76b90b6834ef081edbc1c7ae30d1627012e067c6ec87 + languageName: node + linkType: hard + +"eventemitter3@npm:^4.0.0, eventemitter3@npm:^4.0.4": + version: 4.0.7 + resolution: "eventemitter3@npm:4.0.7" + checksum: 1875311c42fcfe9c707b2712c32664a245629b42bb0a5a84439762dd0fd637fc54d078155ea83c2af9e0323c9ac13687e03cfba79b03af9f40c89b4960099374 + languageName: node + linkType: hard + +"events@npm:1.1.1": + version: 1.1.1 + resolution: "events@npm:1.1.1" + checksum: 40431eb005cc4c57861b93d44c2981a49e7feb99df84cf551baed299ceea4444edf7744733f6a6667e942af687359b1f4a87ec1ec4f21d5127dac48a782039b9 + languageName: node + linkType: hard + +"events@npm:^2.0.0": + version: 2.1.0 + resolution: "events@npm:2.1.0" + checksum: 8756c4f40a57ffdaa60f1e285beb1fcf2873a26ef713879b927ed648b2833cbbbcdbf93460a3af407af55677e89c044ac9c3c5639a7b3ce38f4dfec2fa4d039e + languageName: node + linkType: hard + +"events@npm:^3.2.0, events@npm:^3.3.0": + version: 3.3.0 + resolution: "events@npm:3.3.0" + checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780 + languageName: node + linkType: hard + +"evp_bytestokey@npm:^1.0.0, evp_bytestokey@npm:^1.0.3": + version: 1.0.3 + resolution: "evp_bytestokey@npm:1.0.3" + dependencies: + md5.js: ^1.3.4 + node-gyp: latest + safe-buffer: ^5.1.1 + checksum: ad4e1577f1a6b721c7800dcc7c733fe01f6c310732bb5bf2240245c2a5b45a38518b91d8be2c610611623160b9d1c0e91f1ce96d639f8b53e8894625cf20fa45 + languageName: node + linkType: hard + +"execa@npm:^0.10.0": + version: 0.10.0 + resolution: "execa@npm:0.10.0" + dependencies: + cross-spawn: ^6.0.0 + get-stream: ^3.0.0 + is-stream: ^1.1.0 + npm-run-path: ^2.0.0 + p-finally: ^1.0.0 + signal-exit: ^3.0.0 + strip-eof: ^1.0.0 + checksum: da132af2b209e69d79f91751ac6d15ddbb8d9414f9e5f7a53405232679a3dca00fe11eb14e0cd5c2c374a749061410a7717fcc3094f6dd779cf4d259faa58d9a + languageName: node + linkType: hard + +"execa@npm:^4.1.0": + version: 4.1.0 + resolution: "execa@npm:4.1.0" + dependencies: + cross-spawn: ^7.0.0 + get-stream: ^5.0.0 + human-signals: ^1.1.1 + is-stream: ^2.0.0 + merge-stream: ^2.0.0 + npm-run-path: ^4.0.0 + onetime: ^5.1.0 + signal-exit: ^3.0.2 + strip-final-newline: ^2.0.0 + checksum: e30d298934d9c52f90f3847704fd8224e849a081ab2b517bbc02f5f7732c24e56a21f14cb96a08256deffeb2d12b2b7cb7e2b014a12fb36f8d3357e06417ed55 + languageName: node + linkType: hard + +"execa@npm:^5.0.0": + version: 5.1.1 + resolution: "execa@npm:5.1.1" + 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 + checksum: fba9022c8c8c15ed862847e94c252b3d946036d7547af310e344a527e59021fd8b6bb0723883ea87044dc4f0201f949046993124a42ccb0855cae5bf8c786343 + languageName: node + linkType: hard + +"expect@npm:*": + version: 27.3.1 + resolution: "expect@npm:27.3.1" + dependencies: + "@jest/types": ^27.2.5 + ansi-styles: ^5.0.0 + jest-get-type: ^27.3.1 + jest-matcher-utils: ^27.3.1 + jest-message-util: ^27.3.1 + jest-regex-util: ^27.0.6 + checksum: e7681ecc7ab1006a9311c66729ba7cef598671e89f48e832f319feb9bb0c79a231d30da039c09ad437e5e18d69aced2a66c102ef63eb58a2e4f39a591bba2f60 + languageName: node + linkType: hard + +"extend@npm:^3.0.0, extend@npm:~3.0.2": + version: 3.0.2 + resolution: "extend@npm:3.0.2" + checksum: a50a8309ca65ea5d426382ff09f33586527882cf532931cb08ca786ea3146c0553310bda688710ff61d7668eba9f96b923fe1420cdf56a2c3eaf30fcab87b515 + languageName: node + linkType: hard + +"external-editor@npm:^3.0.3": + version: 3.1.0 + resolution: "external-editor@npm:3.1.0" + dependencies: + chardet: ^0.7.0 + iconv-lite: ^0.4.24 + tmp: ^0.0.33 + checksum: 1c2a616a73f1b3435ce04030261bed0e22d4737e14b090bb48e58865da92529c9f2b05b893de650738d55e692d071819b45e1669259b2b354bc3154d27a698c7 + languageName: node + linkType: hard + +"extsprintf@npm:1.3.0": + version: 1.3.0 + resolution: "extsprintf@npm:1.3.0" + checksum: cee7a4a1e34cffeeec18559109de92c27517e5641991ec6bab849aa64e3081022903dd53084f2080d0d2530803aa5ee84f1e9de642c365452f9e67be8f958ce2 + languageName: node + linkType: hard + +"extsprintf@npm:^1.2.0": + version: 1.4.1 + resolution: "extsprintf@npm:1.4.1" + checksum: a2f29b241914a8d2bad64363de684821b6b1609d06ae68d5b539e4de6b28659715b5bea94a7265201603713b7027d35399d10b0548f09071c5513e65e8323d33 + languageName: node + linkType: hard + +"eyes@npm:^0.1.8": + version: 0.1.8 + resolution: "eyes@npm:0.1.8" + checksum: c31703a92bf36ba75ee8d379ee7985c24ee6149f3a6175f44cec7a05b178c38bce9836d3ca48c9acb0329a960ac2c4b2ead4e60cdd4fe6e8c92cad7cd6913687 + languageName: node + linkType: hard + +"fast-decode-uri-component@npm:^1.0.0": + version: 1.0.1 + resolution: "fast-decode-uri-component@npm:1.0.1" + checksum: 427a48fe0907e76f0e9a2c228e253b4d8a8ab21d130ee9e4bb8339c5ba4086235cf9576831f7b20955a752eae4b525a177ff9d5825dd8d416e7726939194fbee + languageName: node + linkType: hard + +"fast-deep-equal@npm:^2.0.1": + version: 2.0.1 + resolution: "fast-deep-equal@npm:2.0.1" + checksum: b701835a87985e0ec4925bdf1f0c1e7eb56309b5d12d534d5b4b69d95a54d65bb16861c081781ead55f73f12d6c60ba668713391ee7fbf6b0567026f579b7b0b + languageName: node + linkType: hard + +"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": + version: 3.1.3 + resolution: "fast-deep-equal@npm:3.1.3" + checksum: e21a9d8d84f53493b6aa15efc9cfd53dd5b714a1f23f67fb5dc8f574af80df889b3bce25dc081887c6d25457cce704e636395333abad896ccdec03abaf1f3f9d + languageName: node + linkType: hard + +"fast-glob@npm:^3.0.3, fast-glob@npm:^3.2.5, fast-glob@npm:^3.2.9": + version: 3.2.11 + resolution: "fast-glob@npm:3.2.11" + 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 + checksum: f473105324a7780a20c06de842e15ddbb41d3cb7e71d1e4fe6e8373204f22245d54f5ab9e2061e6a1c613047345954d29b022e0e76f5c28b1df9858179a0e6d7 + languageName: node + linkType: hard + +"fast-json-patch@npm:^2.0.5": + version: 2.2.1 + resolution: "fast-json-patch@npm:2.2.1" + dependencies: + fast-deep-equal: ^2.0.1 + checksum: 955aebb3f873d1fb0452a5d8c34865ce4c3c6cdafeb7d3ad98d43b467de9a5a0d304132f8595fd2b373f8f4d200605947e865286b180f3a55e8377a634893164 + languageName: node + linkType: hard + +"fast-json-patch@npm:^3.1.0": + version: 3.1.0 + resolution: "fast-json-patch@npm:3.1.0" + checksum: bad25a6121650d5e138fba787f8e8c6c738779a9a84a978513261110632b1450d96b92a7fedd17188ae847cd5a2b0560725b400a0214aefd3c6506e1be36c66e + languageName: node + linkType: hard + +"fast-json-stable-stringify@npm:^2.0.0": + version: 2.1.0 + resolution: "fast-json-stable-stringify@npm:2.1.0" + checksum: b191531e36c607977e5b1c47811158733c34ccb3bfde92c44798929e9b4154884378536d26ad90dfecd32e1ffc09c545d23535ad91b3161a27ddbb8ebe0cbecb + languageName: node + linkType: hard + +"fast-levenshtein@npm:^2.0.6, fast-levenshtein@npm:~2.0.6": + version: 2.0.6 + resolution: "fast-levenshtein@npm:2.0.6" + checksum: 92cfec0a8dfafd9c7a15fba8f2cc29cd0b62b85f056d99ce448bbcd9f708e18ab2764bda4dd5158364f4145a7c72788538994f0d1787b956ef0d1062b0f7c24c + languageName: node + linkType: hard + +"fast-levenshtein@npm:^3.0.0": + version: 3.0.0 + resolution: "fast-levenshtein@npm:3.0.0" + dependencies: + fastest-levenshtein: ^1.0.7 + checksum: 02732ba6c656797ca7e987c25f3e53718c8fcc39a4bfab46def78eef7a8729eb629632d4a7eca4c27a33e10deabffa9984839557e18a96e91ecf7ccaeedb9890 + languageName: node + linkType: hard + +"fast-redact@npm:^3.0.0": + version: 3.0.2 + resolution: "fast-redact@npm:3.0.2" + checksum: f4ffdf48f1647dbe0411884e5dca85ebef0762d1ce1937f6779beaea5c83ef7c35416d800b2bff60f1a252b670d1707f9484c9a5d0ef721e68f3dae94b420fa8 + languageName: node + linkType: hard + +"fast-safe-stringify@npm:^2.0.7, fast-safe-stringify@npm:^2.0.8": + version: 2.1.1 + resolution: "fast-safe-stringify@npm:2.1.1" + checksum: a851cbddc451745662f8f00ddb622d6766f9bd97642dabfd9a405fb0d646d69fc0b9a1243cbf67f5f18a39f40f6fa821737651ff1bceeba06c9992ca2dc5bd3d + languageName: node + linkType: hard + +"fastest-levenshtein@npm:^1.0.12, fastest-levenshtein@npm:^1.0.7": + version: 1.0.12 + resolution: "fastest-levenshtein@npm:1.0.12" + checksum: e1a013698dd1d302c7a78150130c7d50bb678c2c2f8839842a796d66cc7cdf50ea6b3d7ca930b0c8e7e8c2cd84fea8ab831023b382f7aab6922c318c1451beab + languageName: node + linkType: hard + +"fastify-warning@npm:^0.2.0": + version: 0.2.0 + resolution: "fastify-warning@npm:0.2.0" + checksum: c19ebccf54a3122877d2248400772ca98bacbabdf97826211ede29246c640d47431a2eebed1f52f9421139ed5e52e42d3bd4aefc46e27b6f34add3507529fd97 + languageName: node + linkType: hard + +"fastq@npm:^1.6.0": + version: 1.13.0 + resolution: "fastq@npm:1.13.0" + dependencies: + reusify: ^1.0.4 + checksum: 32cf15c29afe622af187d12fc9cd93e160a0cb7c31a3bb6ace86b7dea3b28e7b72acde89c882663f307b2184e14782c6c664fa315973c03626c7d4bff070bb0b + languageName: node + linkType: hard + +"fclone@npm:^1.0.11": + version: 1.0.11 + resolution: "fclone@npm:1.0.11" + checksum: 016eb1eac443b0c896adf938f6b300bfc86365444f5160ccf3d68a598d1372d00ca96ab8e116185dc9f316cec83de5f2df157fe7fe365a57bcba1ccc17491f61 + languageName: node + linkType: hard + +"fecha@npm:^4.2.0": + version: 4.2.1 + resolution: "fecha@npm:4.2.1" + checksum: 26993474949d94cd2de5eee7dfe283d671d5cd61acdba8819df478cbc86495273363f4a7e98d15ee51563110a38328d268982a6e9048169bce8f15aeba5931f9 + languageName: node + linkType: hard + +"figures@npm:^3.0.0, figures@npm:^3.2.0": + version: 3.2.0 + resolution: "figures@npm:3.2.0" + dependencies: + escape-string-regexp: ^1.0.5 + checksum: 85a6ad29e9aca80b49b817e7c89ecc4716ff14e3779d9835af554db91bac41c0f289c418923519392a1e582b4d10482ad282021330cd045bb7b80c84152f2a2b + languageName: node + linkType: hard + +"file-entry-cache@npm:^6.0.1": + version: 6.0.1 + resolution: "file-entry-cache@npm:6.0.1" + dependencies: + flat-cache: ^3.0.4 + checksum: f49701feaa6314c8127c3c2f6173cfefff17612f5ed2daaafc6da13b5c91fd43e3b2a58fd0d63f9f94478a501b167615931e7200e31485e320f74a33885a9c74 + languageName: node + linkType: hard + +"filelist@npm:^1.0.1": + version: 1.0.2 + resolution: "filelist@npm:1.0.2" + dependencies: + minimatch: ^3.0.4 + checksum: 4d6953cb6f76c5345a52fc50222949e244946f485462ab6bae977176fff64fe5200cc1f44db175c27fc887f91cead401504c22eefcdcc064012ee44759947561 + languageName: node + linkType: hard + +"fill-range@npm:^7.0.1": + version: 7.0.1 + resolution: "fill-range@npm:7.0.1" + dependencies: + to-regex-range: ^5.0.1 + checksum: cc283f4e65b504259e64fd969bcf4def4eb08d85565e906b7d36516e87819db52029a76b6363d0f02d0d532f0033c9603b9e2d943d56ee3b0d4f7ad3328ff917 + languageName: node + linkType: hard + +"finalhandler@npm:1.1.2": + version: 1.1.2 + resolution: "finalhandler@npm:1.1.2" + 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 + checksum: 617880460c5138dd7ccfd555cb5dde4d8f170f4b31b8bd51e4b646bb2946c30f7db716428a1f2882d730d2b72afb47d1f67cc487b874cb15426f95753a88965e + languageName: node + linkType: hard + +"find-cache-dir@npm:^3.2.0, find-cache-dir@npm:^3.3.1": + version: 3.3.2 + resolution: "find-cache-dir@npm:3.3.2" + dependencies: + commondir: ^1.0.1 + make-dir: ^3.0.2 + pkg-dir: ^4.1.0 + checksum: 1e61c2e64f5c0b1c535bd85939ae73b0e5773142713273818cc0b393ee3555fb0fd44e1a5b161b8b6c3e03e98c2fcc9c227d784850a13a90a8ab576869576817 + languageName: node + linkType: hard + +"find-my-way@npm:^2.2.2": + version: 2.2.5 + resolution: "find-my-way@npm:2.2.5" + dependencies: + fast-decode-uri-component: ^1.0.0 + safe-regex2: ^2.0.0 + semver-store: ^0.3.0 + checksum: 93303495659720c55c92e6cd7c430861c722865ac93454eac8b014954db82cfbcbac20826e74d871cef8883d484b2b8dc15a435b8964696bcdffe76afce5ade1 + languageName: node + linkType: hard + +"find-up@npm:5.0.0, find-up@npm:^5.0.0": + version: 5.0.0 + resolution: "find-up@npm:5.0.0" + dependencies: + locate-path: ^6.0.0 + path-exists: ^4.0.0 + checksum: 07955e357348f34660bde7920783204ff5a26ac2cafcaa28bace494027158a97b9f56faaf2d89a6106211a8174db650dd9f503f9c0d526b1202d5554a00b9095 + languageName: node + linkType: hard + +"find-up@npm:^2.0.0, find-up@npm:^2.1.0": + version: 2.1.0 + resolution: "find-up@npm:2.1.0" + dependencies: + locate-path: ^2.0.0 + checksum: 43284fe4da09f89011f08e3c32cd38401e786b19226ea440b75386c1b12a4cb738c94969808d53a84f564ede22f732c8409e3cfc3f7fb5b5c32378ad0bbf28bd + languageName: node + linkType: hard + +"find-up@npm:^4.0.0, find-up@npm:^4.1.0": + version: 4.1.0 + resolution: "find-up@npm:4.1.0" + dependencies: + locate-path: ^5.0.0 + path-exists: ^4.0.0 + checksum: 4c172680e8f8c1f78839486e14a43ef82e9decd0e74145f40707cc42e7420506d5ec92d9a11c22bd2c48fb0c384ea05dd30e10dd152fefeec6f2f75282a8b844 + languageName: node + linkType: hard + +"find-yarn-workspace-root2@npm:1.2.16": + version: 1.2.16 + resolution: "find-yarn-workspace-root2@npm:1.2.16" + dependencies: + micromatch: ^4.0.2 + pkg-dir: ^4.2.0 + checksum: b4abdd37ab87c2172e2abab69ecbfed365d63232742cd1f0a165020fba1b200478e944ec2035c6aaf0ae142ac4c523cbf08670f45e59b242bcc295731b017825 + languageName: node + linkType: hard + +"find-yarn-workspace-root@npm:^2.0.0": + version: 2.0.0 + resolution: "find-yarn-workspace-root@npm:2.0.0" + dependencies: + micromatch: ^4.0.2 + checksum: fa5ca8f9d08fe7a54ce7c0a5931ff9b7e36f9ee7b9475fb13752bcea80ec6b5f180fa5102d60b376d5526ce924ea3fc6b19301262efa0a5d248dd710f3644242 + languageName: node + linkType: hard + +"first-chunk-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "first-chunk-stream@npm:2.0.0" + dependencies: + readable-stream: ^2.0.2 + checksum: 2fa86f93a455eac09a9dd1339464f06510183fb4f1062b936d10605bce5728ec5c564a268318efd7f2b55a1ce3ff4dc795585a99fe5dd1940caf28afeb284b47 + languageName: node + linkType: hard + +"flat-cache@npm:^3.0.4": + version: 3.0.4 + resolution: "flat-cache@npm:3.0.4" + dependencies: + flatted: ^3.1.0 + rimraf: ^3.0.2 + checksum: 4fdd10ecbcbf7d520f9040dd1340eb5dfe951e6f0ecf2252edeec03ee68d989ec8b9a20f4434270e71bcfd57800dc09b3344fca3966b2eb8f613072c7d9a2365 + languageName: node + linkType: hard + +"flat@npm:^5.0.2": + version: 5.0.2 + resolution: "flat@npm:5.0.2" + bin: + flat: cli.js + checksum: 12a1536ac746db74881316a181499a78ef953632ddd28050b7a3a43c62ef5462e3357c8c29d76072bb635f147f7a9a1f0c02efef6b4be28f8db62ceb3d5c7f5d + languageName: node + linkType: hard + +"flatstr@npm:^1.0.12": + version: 1.0.12 + resolution: "flatstr@npm:1.0.12" + checksum: e1bb562c94b119e958bf37e55738b172b5f8aaae6532b9660ecd877779f8559dbbc89613ba6b29ccc13447e14c59277d41450f785cf75c30df9fce62f459e9a8 + languageName: node + linkType: hard + +"flatted@npm:^2.0.1": + version: 2.0.2 + resolution: "flatted@npm:2.0.2" + checksum: 473c754db7a529e125a22057098f1a4c905ba17b8cc269c3acf77352f0ffa6304c851eb75f6a1845f74461f560e635129ca6b0b8a78fb253c65cea4de3d776f2 + languageName: node + linkType: hard + +"flatted@npm:^3.1.0": + version: 3.2.4 + resolution: "flatted@npm:3.2.4" + checksum: 7d33846428ab337ec81ef9b8b9103894c1c81f5f67feb32bd4ed106fbc47da60d56edb42efd36c9f1f30a010272aeccd34ec1ffacfe9dfdff19673b1d4df481b + languageName: node + linkType: hard + +"fn.name@npm:1.x.x": + version: 1.1.0 + resolution: "fn.name@npm:1.1.0" + checksum: e357144f48cfc9a7f52a82bbc6c23df7c8de639fce049cac41d41d62cabb740cdb9f14eddc6485e29c933104455bdd7a69bb14a9012cef9cd4fa252a4d0cf293 + languageName: node + linkType: hard + +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0": + version: 1.14.5 + resolution: "follow-redirects@npm:1.14.5" + peerDependenciesMeta: + debug: + optional: true + checksum: f004a76b2ee3a849772c2816e30928253bf47537b0f00184d89f4966413add96a228a4d96ca8c702bc045a683c52c2ba41545c915cc1a5e33bf8fd9d07b59aee + languageName: node + linkType: hard + +"foreach@npm:^2.0.4, foreach@npm:^2.0.5": + version: 2.0.5 + resolution: "foreach@npm:2.0.5" + checksum: dab4fbfef0b40b69ee5eab81bcb9626b8fa8b3469c8cfa26480f3e5e1ee08c40eae07048c9a967c65aeda26e774511ccc70b3f10a604c01753c6ef24361f0fc8 + languageName: node + linkType: hard + +"foreground-child@npm:^2.0.0": + version: 2.0.0 + resolution: "foreground-child@npm:2.0.0" + dependencies: + cross-spawn: ^7.0.0 + signal-exit: ^3.0.2 + checksum: f77ec9aff621abd6b754cb59e690743e7639328301fbea6ff09df27d2befaf7dd5b77cec51c32323d73a81a7d91caaf9413990d305cbe3d873eec4fe58960956 + languageName: node + linkType: hard + +"forever-agent@npm:~0.6.1": + version: 0.6.1 + resolution: "forever-agent@npm:0.6.1" + checksum: 766ae6e220f5fe23676bb4c6a99387cec5b7b62ceb99e10923376e27bfea72f3c3aeec2ba5f45f3f7ba65d6616965aa7c20b15002b6860833bb6e394dea546a8 + languageName: node + linkType: hard + +"form-data@npm:~2.3.2": + version: 2.3.3 + resolution: "form-data@npm:2.3.3" + dependencies: + asynckit: ^0.4.0 + combined-stream: ^1.0.6 + mime-types: ^2.1.12 + checksum: 10c1780fa13dbe1ff3100114c2ce1f9307f8be10b14bf16e103815356ff567b6be39d70fc4a40f8990b9660012dc24b0f5e1dde1b6426166eb23a445ba068ca3 + languageName: node + linkType: hard + +"fraction.js@npm:^4.2.0": + version: 4.2.0 + resolution: "fraction.js@npm:4.2.0" + checksum: 8c76a6e21dedea87109d6171a0ac77afa14205794a565d71cb10d2925f629a3922da61bf45ea52dbc30bce4d8636dc0a27213a88cbd600eab047d82f9a3a94c5 + languageName: node + linkType: hard + +"fromentries@npm:^1.2.0": + version: 1.3.2 + resolution: "fromentries@npm:1.3.2" + checksum: 33729c529ce19f5494f846f0dd4945078f4e37f4e8955f4ae8cc7385c218f600e9d93a7d225d17636c20d1889106fd87061f911550861b7072f53bf891e6b341 + languageName: node + linkType: hard + +"fs-constants@npm:^1.0.0": + version: 1.0.0 + resolution: "fs-constants@npm:1.0.0" + checksum: 18f5b718371816155849475ac36c7d0b24d39a11d91348cfcb308b4494824413e03572c403c86d3a260e049465518c4f0d5bd00f0371cdfcad6d4f30a85b350d + languageName: node + linkType: hard + +"fs-extra@npm:^6.0.1": + version: 6.0.1 + resolution: "fs-extra@npm:6.0.1" + dependencies: + graceful-fs: ^4.1.2 + jsonfile: ^4.0.0 + universalify: ^0.1.0 + checksum: 133dbd765e05c1cdaaf723308e00ffbe746da5ad516ad890ae2da2a538982c1175371055c778fbe68d1fca1da9ed4003ba55c4a14e070372eabf6a7c48062759 + languageName: node + linkType: hard + +"fs-extra@npm:^8.1, fs-extra@npm:^8.1.0": + version: 8.1.0 + resolution: "fs-extra@npm:8.1.0" + dependencies: + graceful-fs: ^4.2.0 + jsonfile: ^4.0.0 + universalify: ^0.1.0 + checksum: bf44f0e6cea59d5ce071bba4c43ca76d216f89e402dc6285c128abc0902e9b8525135aa808adad72c9d5d218e9f4bcc63962815529ff2f684ad532172a284880 + languageName: node + linkType: hard + +"fs-extra@npm:^9.0.1, fs-extra@npm:^9.1.0": + version: 9.1.0 + resolution: "fs-extra@npm:9.1.0" + dependencies: + at-least-node: ^1.0.0 + graceful-fs: ^4.2.0 + jsonfile: ^6.0.1 + universalify: ^2.0.0 + checksum: ba71ba32e0faa74ab931b7a0031d1523c66a73e225de7426e275e238e312d07313d2da2d33e34a52aa406c8763ade5712eb3ec9ba4d9edce652bcacdc29e6b20 + languageName: node + linkType: hard + +"fs-minipass@npm:^2.0.0, fs-minipass@npm:^2.1.0": + version: 2.1.0 + resolution: "fs-minipass@npm:2.1.0" + dependencies: + minipass: ^3.0.0 + checksum: 1b8d128dae2ac6cc94230cc5ead341ba3e0efaef82dab46a33d171c044caaa6ca001364178d42069b2809c35a1c3c35079a32107c770e9ffab3901b59af8c8b1 + languageName: node + linkType: hard + +"fs.realpath@npm:^1.0.0": + version: 1.0.0 + resolution: "fs.realpath@npm:1.0.0" + checksum: 99ddea01a7e75aa276c250a04eedeffe5662bce66c65c07164ad6264f9de18fb21be9433ead460e54cff20e31721c811f4fb5d70591799df5f85dce6d6746fd0 + languageName: node + linkType: hard + +"fsevents@patch:fsevents@~2.3.2#~builtin": + version: 2.3.2 + resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=18f3a7" + dependencies: + node-gyp: latest + conditions: os=darwin + languageName: node + linkType: hard + +fsevents@~2.3.2: + version: 2.3.2 + resolution: "fsevents@npm:2.3.2" + dependencies: + node-gyp: latest + checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f + conditions: os=darwin + languageName: node + linkType: hard + +"function-bind@npm:^1.1.1": + version: 1.1.1 + resolution: "function-bind@npm:1.1.1" + checksum: b32fbaebb3f8ec4969f033073b43f5c8befbb58f1a79e12f1d7490358150359ebd92f49e72ff0144f65f2c48ea2a605bff2d07965f548f6474fd8efd95bf361a + languageName: node + linkType: hard + +"functional-red-black-tree@npm:^1.0.1, functional-red-black-tree@npm:~1.0.1": + version: 1.0.1 + resolution: "functional-red-black-tree@npm:1.0.1" + checksum: ca6c170f37640e2d94297da8bb4bf27a1d12bea3e00e6a3e007fd7aa32e37e000f5772acf941b4e4f3cf1c95c3752033d0c509af157ad8f526e7f00723b9eb9f + languageName: node + linkType: hard + +"gauge@npm:^3.0.0": + version: 3.0.2 + resolution: "gauge@npm:3.0.2" + dependencies: + aproba: ^1.0.3 || ^2.0.0 + color-support: ^1.1.2 + console-control-strings: ^1.0.0 + has-unicode: ^2.0.1 + object-assign: ^4.1.1 + signal-exit: ^3.0.0 + string-width: ^4.2.3 + strip-ansi: ^6.0.1 + wide-align: ^1.1.2 + checksum: 81296c00c7410cdd48f997800155fbead4f32e4f82109be0719c63edc8560e6579946cc8abd04205297640691ec26d21b578837fd13a4e96288ab4b40b1dc3e9 + languageName: node + linkType: hard + +"gauge@npm:^4.0.0": + version: 4.0.1 + resolution: "gauge@npm:4.0.1" + dependencies: + ansi-regex: ^5.0.1 + aproba: ^1.0.3 || ^2.0.0 + color-support: ^1.1.2 + console-control-strings: ^1.0.0 + has-unicode: ^2.0.1 + signal-exit: ^3.0.0 + string-width: ^4.2.3 + strip-ansi: ^6.0.1 + wide-align: ^1.1.2 + checksum: 398540c761f2efbd8c35323b781a572db09a3ab2d62ea672fc440eb88e9249624ac8b64275731d9ef266cde6491880df5360d74d0829da077136ee614b08f85c + languageName: node + linkType: hard + +"gauge@npm:~2.7.3": + version: 2.7.4 + resolution: "gauge@npm:2.7.4" + dependencies: + aproba: ^1.0.3 + console-control-strings: ^1.0.0 + has-unicode: ^2.0.0 + object-assign: ^4.1.0 + signal-exit: ^3.0.0 + string-width: ^1.0.1 + strip-ansi: ^3.0.1 + wide-align: ^1.1.0 + checksum: a89b53cee65579b46832e050b5f3a79a832cc422c190de79c6b8e2e15296ab92faddde6ddf2d376875cbba2b043efa99b9e1ed8124e7365f61b04e3cee9d40ee + languageName: node + linkType: hard + +"gensync@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "gensync@npm:1.0.0-beta.2" + checksum: a7437e58c6be12aa6c90f7730eac7fa9833dc78872b4ad2963d2031b00a3367a93f98aec75f9aaac7220848e4026d67a8655e870b24f20a543d103c0d65952ec + languageName: node + linkType: hard + +"get-assigned-identifiers@npm:^1.2.0": + version: 1.2.0 + resolution: "get-assigned-identifiers@npm:1.2.0" + checksum: 5ea831c744a645ebd56fff818c80ffc583995c2ca3958236c7cfaac670242300e4f08498a9bbafd3ecbe30027d58ed50e7fa6268ecfe4b8e5c888ea7275cb56c + languageName: node + linkType: hard + +"get-caller-file@npm:^2.0.1, get-caller-file@npm:^2.0.5": + version: 2.0.5 + resolution: "get-caller-file@npm:2.0.5" + checksum: b9769a836d2a98c3ee734a88ba712e62703f1df31b94b784762c433c27a386dd6029ff55c2a920c392e33657d80191edbf18c61487e198844844516f843496b9 + languageName: node + linkType: hard + +"get-func-name@npm:^2.0.0": + version: 2.0.0 + resolution: "get-func-name@npm:2.0.0" + checksum: 8d82e69f3e7fab9e27c547945dfe5cc0c57fc0adf08ce135dddb01081d75684a03e7a0487466f478872b341d52ac763ae49e660d01ab83741f74932085f693c3 + languageName: node + linkType: hard + +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.0, get-intrinsic@npm:^1.1.1": + version: 1.1.1 + resolution: "get-intrinsic@npm:1.1.1" + dependencies: + function-bind: ^1.1.1 + has: ^1.0.3 + has-symbols: ^1.0.1 + checksum: a9fe2ca8fa3f07f9b0d30fb202bcd01f3d9b9b6b732452e79c48e79f7d6d8d003af3f9e38514250e3553fdc83c61650851cb6870832ac89deaaceb08e3721a17 + languageName: node + linkType: hard + +"get-package-type@npm:^0.1.0": + version: 0.1.0 + resolution: "get-package-type@npm:0.1.0" + checksum: bba0811116d11e56d702682ddef7c73ba3481f114590e705fc549f4d868972263896af313c57a25c076e3c0d567e11d919a64ba1b30c879be985fc9d44f96148 + languageName: node + linkType: hard + +"get-pkg-repo@npm:^4.0.0": + version: 4.2.1 + resolution: "get-pkg-repo@npm:4.2.1" + dependencies: + "@hutson/parse-repository-url": ^3.0.0 + hosted-git-info: ^4.0.0 + through2: ^2.0.0 + yargs: ^16.2.0 + bin: + get-pkg-repo: src/cli.js + checksum: 5abf169137665e45b09a857b33ad2fdcf2f4a09f0ecbd0ebdd789a7ce78c39186a21f58621127eb724d2d4a3a7ee8e6bd4ac7715efda01ad5200665afc218e0d + languageName: node + linkType: hard + +"get-stdin@npm:^4.0.1": + version: 4.0.1 + resolution: "get-stdin@npm:4.0.1" + checksum: 4f73d3fe0516bc1f3dc7764466a68ad7c2ba809397a02f56c2a598120e028430fcff137a648a01876b2adfb486b4bc164119f98f1f7d7c0abd63385bdaa0113f + languageName: node + linkType: hard + +"get-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "get-stream@npm:3.0.0" + checksum: 36142f46005ed74ce3a45c55545ec4e7da8e243554179e345a786baf144e5c4a35fb7bdc49fadfa9f18bd08000589b6fe364abdadfc4e1eb0e1b9914a6bb9c56 + languageName: node + linkType: hard + +"get-stream@npm:^4.1.0": + version: 4.1.0 + resolution: "get-stream@npm:4.1.0" + dependencies: + pump: ^3.0.0 + checksum: 443e1914170c15bd52ff8ea6eff6dfc6d712b031303e36302d2778e3de2506af9ee964d6124010f7818736dcfde05c04ba7ca6cc26883106e084357a17ae7d73 + languageName: node + linkType: hard + +"get-stream@npm:^5.0.0, get-stream@npm:^5.1.0": + version: 5.2.0 + resolution: "get-stream@npm:5.2.0" + dependencies: + pump: ^3.0.0 + checksum: 8bc1a23174a06b2b4ce600df38d6c98d2ef6d84e020c1ddad632ad75bac4e092eeb40e4c09e0761c35fc2dbc5e7fff5dab5e763a383582c4a167dd69a905bd12 + languageName: node + linkType: hard + +"get-stream@npm:^6.0.0": + version: 6.0.1 + resolution: "get-stream@npm:6.0.1" + checksum: e04ecece32c92eebf5b8c940f51468cd53554dcbb0ea725b2748be583c9523d00128137966afce410b9b051eb2ef16d657cd2b120ca8edafcf5a65e81af63cad + languageName: node + linkType: hard + +"get-symbol-description@npm:^1.0.0": + version: 1.0.0 + resolution: "get-symbol-description@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.1.1 + checksum: 9ceff8fe968f9270a37a1f73bf3f1f7bda69ca80f4f80850670e0e7b9444ff99323f7ac52f96567f8b5f5fbe7ac717a0d81d3407c7313e82810c6199446a5247 + languageName: node + linkType: hard + +"getpass@npm:^0.1.1": + version: 0.1.7 + resolution: "getpass@npm:0.1.7" + dependencies: + assert-plus: ^1.0.0 + checksum: ab18d55661db264e3eac6012c2d3daeafaab7a501c035ae0ccb193c3c23e9849c6e29b6ac762b9c2adae460266f925d55a3a2a3a3c8b94be2f222df94d70c046 + languageName: node + linkType: hard + +"git-raw-commits@npm:^2.0.8": + version: 2.0.10 + resolution: "git-raw-commits@npm:2.0.10" + dependencies: + dargs: ^7.0.0 + lodash: ^4.17.15 + meow: ^8.0.0 + split2: ^3.0.0 + through2: ^4.0.0 + bin: + git-raw-commits: cli.js + checksum: 66e2d7b4cdeff946ac639e1bba37f5dcbd9f5c9245348b31e027e4529f6b6733d23f75768d285d5f29c1f08d3485705a4932300a81a45b77b660fe3ce6089c29 + languageName: node + linkType: hard + +"git-remote-origin-url@npm:^2.0.0": + version: 2.0.0 + resolution: "git-remote-origin-url@npm:2.0.0" + dependencies: + gitconfiglocal: ^1.0.0 + pify: ^2.3.0 + checksum: 85263a09c044b5f4fe2acc45cbb3c5331ab2bd4484bb53dfe7f3dd593a4bf90a9786a2e00b9884524331f50b3da18e8c924f01c2944087fc7f342282c4437b73 + languageName: node + linkType: hard + +"git-semver-tags@npm:^4.1.1": + version: 4.1.1 + resolution: "git-semver-tags@npm:4.1.1" + dependencies: + meow: ^8.0.0 + semver: ^6.0.0 + bin: + git-semver-tags: cli.js + checksum: e16d02a515c0f88289a28b5bf59bf42c0dc053765922d3b617ae4b50546bd4f74a25bf3ad53b91cb6c1159319a2e92533b160c573b856c2629125c8b26b3b0e3 + languageName: node + linkType: hard + +"gitconfiglocal@npm:^1.0.0": + version: 1.0.0 + resolution: "gitconfiglocal@npm:1.0.0" + dependencies: + ini: ^1.3.2 + checksum: e6d2764c15bbab6d1d1000d1181bb907f6b3796bb04f63614dba571b18369e0ecb1beaf27ce8da5b24307ef607e3a5f262a67cb9575510b9446aac697d421beb + languageName: node + linkType: hard + +"github-api@npm:^3.3.0": + version: 3.4.0 + resolution: "github-api@npm:3.4.0" + dependencies: + axios: ^0.21.1 + debug: ^2.2.0 + js-base64: ^2.1.9 + utf8: ^2.1.1 + checksum: d6f2def92b518af436b0fde572c9f2b619922d2ccd1038c5e3f5772b382b8a5800b5003421b89d4690b5c1ec067a1f98dd62c90686b160567edfe7105bf7d59a + languageName: node + linkType: hard + +"github-slugger@npm:^1.4.0": + version: 1.4.0 + resolution: "github-slugger@npm:1.4.0" + checksum: 4f52e7a21f5c6a4c5328f01fe4fe13ae8881fea78bfe31f9e72c4038f97e3e70d52fb85aa7633a52c501dc2486874474d9abd22aa61cbe9b113099a495551c6b + languageName: node + linkType: hard + +"github-username@npm:^6.0.0": + version: 6.0.0 + resolution: "github-username@npm:6.0.0" + dependencies: + "@octokit/rest": ^18.0.6 + checksum: c40a6151dc293b66809c4c52c21dde2b0ea91a256e1a2eb489658947c12032aecd781c61b921e613f52290feb88c53994ee59a09450bfde2eeded34b3e07e2b7 + languageName: node + linkType: hard + +"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": + version: 5.1.2 + resolution: "glob-parent@npm:5.1.2" + dependencies: + is-glob: ^4.0.1 + checksum: f4f2bfe2425296e8a47e36864e4f42be38a996db40420fe434565e4480e3322f18eb37589617a98640c5dc8fdec1a387007ee18dbb1f3f5553409c34d17f425e + languageName: node + linkType: hard + +"glob-to-regexp@npm:^0.4.1": + version: 0.4.1 + resolution: "glob-to-regexp@npm:0.4.1" + checksum: e795f4e8f06d2a15e86f76e4d92751cf8bbfcf0157cea5c2f0f35678a8195a750b34096b1256e436f0cebc1883b5ff0888c47348443e69546a5a87f9e1eb1167 + languageName: node + linkType: hard + +"glob@npm:7.1.6": + version: 7.1.6 + resolution: "glob@npm:7.1.6" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.0.4 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 351d549dd90553b87c2d3f90ce11aed9e1093c74130440e7ae0592e11bbcd2ce7f0ebb8ba6bfe63aaf9b62166a7f4c80cb84490ae5d78408bb2572bf7d4ee0a6 + languageName: node + linkType: hard + +"glob@npm:7.1.7": + version: 7.1.7 + resolution: "glob@npm:7.1.7" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.0.4 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: b61f48973bbdcf5159997b0874a2165db572b368b931135832599875919c237fc05c12984e38fe828e69aa8a921eb0e8a4997266211c517c9cfaae8a93988bb8 + languageName: node + linkType: hard + +"glob@npm:^7.0.0, glob@npm:^7.0.5, glob@npm:^7.1.0, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7, glob@npm:^7.2.0": + version: 7.2.0 + resolution: "glob@npm:7.2.0" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.0.4 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 78a8ea942331f08ed2e055cb5b9e40fe6f46f579d7fd3d694f3412fe5db23223d29b7fee1575440202e9a7ff9a72ab106a39fee39934c7bedafe5e5f8ae20134 + languageName: node + linkType: hard + +"global-dirs@npm:^3.0.0": + version: 3.0.0 + resolution: "global-dirs@npm:3.0.0" + dependencies: + ini: 2.0.0 + checksum: 953c17cf14bf6ee0e2100ae82a0d779934eed8a3ec5c94a7a4f37c5b3b592c31ea015fb9a15cf32484de13c79f4a814f3015152f3e1d65976cfbe47c1bfe4a88 + languageName: node + linkType: hard + +"globals@npm:^11.1.0": + version: 11.12.0 + resolution: "globals@npm:11.12.0" + checksum: 67051a45eca3db904aee189dfc7cd53c20c7d881679c93f6146ddd4c9f4ab2268e68a919df740d39c71f4445d2b38ee360fc234428baea1dbdfe68bbcb46979e + languageName: node + linkType: hard + +"globals@npm:^13.6.0, globals@npm:^13.9.0": + version: 13.12.0 + resolution: "globals@npm:13.12.0" + dependencies: + type-fest: ^0.20.2 + checksum: 1f959abb11117916468a1afcba527eead152900cad652c8383c4e8976daea7ec55e1ee30c086f48d1b8655719f214e9d92eca083c3a43b5543bc4056e7e5fccf + languageName: node + linkType: hard + +"globby@npm:^10.0.1": + version: 10.0.2 + resolution: "globby@npm:10.0.2" + dependencies: + "@types/glob": ^7.1.1 + array-union: ^2.1.0 + dir-glob: ^3.0.1 + fast-glob: ^3.0.3 + glob: ^7.1.3 + ignore: ^5.1.1 + merge2: ^1.2.3 + slash: ^3.0.0 + checksum: 167cd067f2cdc030db2ec43232a1e835fa06217577d545709dbf29fd21631b30ff8258705172069c855dc4d5766c3b2690834e35b936fbff01ad0329fb95a26f + languageName: node + linkType: hard + +"globby@npm:^11, globby@npm:^11.0.1, globby@npm:^11.0.3, globby@npm:^11.0.4": + version: 11.1.0 + resolution: "globby@npm:11.1.0" + 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 + checksum: b4be8885e0cfa018fc783792942d53926c35c50b3aefd3fdcfb9d22c627639dc26bd2327a40a0b74b074100ce95bb7187bfeae2f236856aa3de183af7a02aea6 + languageName: node + linkType: hard + +"globrex@npm:^0.1.2": + version: 0.1.2 + resolution: "globrex@npm:0.1.2" + checksum: adca162494a176ce9ecf4dd232f7b802956bb1966b37f60c15e49d2e7d961b66c60826366dc2649093cad5a0d69970cfa8875bd1695b5a1a2f33dcd2aa88da3c + languageName: node + linkType: hard + +"google-protobuf@npm:^3.12.2": + version: 3.19.1 + resolution: "google-protobuf@npm:3.19.1" + checksum: 9ec57e1bdff76f9aec0f855376c1e0aa07de5c1b856b490d5c72b9e69a9e6e6ad24879a14e148a1542dbf27444575ad88b0a96c779f0d11b94211d78e1e5c924 + languageName: node + linkType: hard + +"got@npm:^9.6.0": + version: 9.6.0 + resolution: "got@npm:9.6.0" + dependencies: + "@sindresorhus/is": ^0.14.0 + "@szmarczak/http-timer": ^1.1.2 + cacheable-request: ^6.0.0 + decompress-response: ^3.3.0 + duplexer3: ^0.1.4 + get-stream: ^4.1.0 + lowercase-keys: ^1.0.1 + mimic-response: ^1.0.1 + p-cancelable: ^1.0.0 + to-readable-stream: ^1.0.0 + url-parse-lax: ^3.0.0 + checksum: 941807bd9704bacf5eb401f0cc1212ffa1f67c6642f2d028fd75900471c221b1da2b8527f4553d2558f3faeda62ea1cf31665f8b002c6137f5de8732f07370b0 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": + version: 4.2.10 + resolution: "graceful-fs@npm:4.2.10" + checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da + languageName: node + linkType: hard + +"grouped-queue@npm:^2.0.0": + version: 2.0.0 + resolution: "grouped-queue@npm:2.0.0" + checksum: be5c6cfac0db6b6f147d82d6a6629170afe84df8f8fe56bc3acfa53603c30141cf8a6a31b341c08d4acacd323385044fef2d750a979942c967c501fad5b6a633 + languageName: node + linkType: hard + +"growl@npm:1.10.5": + version: 1.10.5 + resolution: "growl@npm:1.10.5" + checksum: 4b86685de6831cebcbb19f93870bea624afee61124b0a20c49017013987cd129e73a8c4baeca295728f41d21265e1f859d25ef36731b142ca59c655fea94bb1a + languageName: node + linkType: hard + +"grpc-web@npm:1.2.1": + version: 1.2.1 + resolution: "grpc-web@npm:1.2.1" + checksum: 3860a3761793ccff10db513126957acf2794bcdb0fb82fc20fc4ffbc16e25b9742500d966acc88fbb258bf1d88ef7a4014189287bffbc79177560d7d83eadec1 + languageName: node + linkType: hard + +"handlebars@npm:^4.7.6": + version: 4.7.7 + resolution: "handlebars@npm:4.7.7" + dependencies: + minimist: ^1.2.5 + neo-async: ^2.6.0 + source-map: ^0.6.1 + uglify-js: ^3.1.4 + wordwrap: ^1.0.0 + dependenciesMeta: + uglify-js: + optional: true + bin: + handlebars: bin/handlebars + checksum: 1e79a43f5e18d15742977cb987923eab3e2a8f44f2d9d340982bcb69e1735ed049226e534d7c1074eaddaf37e4fb4f471a8adb71cddd5bc8cf3f894241df5cee + languageName: node + linkType: hard + +"har-schema@npm:^2.0.0": + version: 2.0.0 + resolution: "har-schema@npm:2.0.0" + checksum: d8946348f333fb09e2bf24cc4c67eabb47c8e1d1aa1c14184c7ffec1140a49ec8aa78aa93677ae452d71d5fc0fdeec20f0c8c1237291fc2bcb3f502a5d204f9b + languageName: node + linkType: hard + +"har-validator@npm:~5.1.3": + version: 5.1.5 + resolution: "har-validator@npm:5.1.5" + dependencies: + ajv: ^6.12.3 + har-schema: ^2.0.0 + checksum: b998a7269ca560d7f219eedc53e2c664cd87d487e428ae854a6af4573fc94f182fe9d2e3b92ab968249baec7ebaf9ead69cf975c931dc2ab282ec182ee988280 + languageName: node + linkType: hard + +"hard-rejection@npm:^2.1.0": + version: 2.1.0 + resolution: "hard-rejection@npm:2.1.0" + checksum: 7baaf80a0c7fff4ca79687b4060113f1529589852152fa935e6787a2bc96211e784ad4588fb3048136ff8ffc9dfcf3ae385314a5b24db32de20bea0d1597f9dc + languageName: node + linkType: hard + +"has-ansi@npm:^2.0.0": + version: 2.0.0 + resolution: "has-ansi@npm:2.0.0" + dependencies: + ansi-regex: ^2.0.0 + checksum: 1b51daa0214440db171ff359d0a2d17bc20061164c57e76234f614c91dbd2a79ddd68dfc8ee73629366f7be45a6df5f2ea9de83f52e1ca24433f2cc78c35d8ec + languageName: node + linkType: hard + +"has-bigints@npm:^1.0.1": + version: 1.0.1 + resolution: "has-bigints@npm:1.0.1" + checksum: 44ab55868174470065d2e0f8f6def1c990d12b82162a8803c679699fa8a39f966e336f2a33c185092fe8aea7e8bf2e85f1c26add5f29d98f2318bd270096b183 + languageName: node + linkType: hard + +"has-flag@npm:^3.0.0": + version: 3.0.0 + resolution: "has-flag@npm:3.0.0" + checksum: 4a15638b454bf086c8148979aae044dd6e39d63904cd452d970374fa6a87623423da485dfb814e7be882e05c096a7ccf1ebd48e7e7501d0208d8384ff4dea73b + languageName: node + linkType: hard + +"has-flag@npm:^4.0.0": + version: 4.0.0 + resolution: "has-flag@npm:4.0.0" + checksum: 261a1357037ead75e338156b1f9452c016a37dcd3283a972a30d9e4a87441ba372c8b81f818cd0fbcd9c0354b4ae7e18b9e1afa1971164aef6d18c2b6095a8ad + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.1, has-symbols@npm:^1.0.2": + version: 1.0.2 + resolution: "has-symbols@npm:1.0.2" + checksum: 2309c426071731be792b5be43b3da6fb4ed7cbe8a9a6bcfca1862587709f01b33d575ce8f5c264c1eaad09fca2f9a8208c0a2be156232629daa2dd0c0740976b + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.0": + version: 1.0.0 + resolution: "has-tostringtag@npm:1.0.0" + dependencies: + has-symbols: ^1.0.2 + checksum: cc12eb28cb6ae22369ebaad3a8ab0799ed61270991be88f208d508076a1e99abe4198c965935ce85ea90b60c94ddda73693b0920b58e7ead048b4a391b502c1c + languageName: node + linkType: hard + +"has-unicode@npm:^2.0.0, has-unicode@npm:^2.0.1": + version: 2.0.1 + resolution: "has-unicode@npm:2.0.1" + checksum: 1eab07a7436512db0be40a710b29b5dc21fa04880b7f63c9980b706683127e3c1b57cb80ea96d47991bdae2dfe479604f6a1ba410106ee1046a41d1bd0814400 + languageName: node + linkType: hard + +"has-yarn@npm:^2.1.0": + version: 2.1.0 + resolution: "has-yarn@npm:2.1.0" + checksum: 5eb1d0bb8518103d7da24532bdbc7124ffc6d367b5d3c10840b508116f2f1bcbcf10fd3ba843ff6e2e991bdf9969fd862d42b2ed58aade88343326c950b7e7f7 + languageName: node + linkType: hard + +"has@npm:^1.0.0, has@npm:^1.0.3": + version: 1.0.3 + resolution: "has@npm:1.0.3" + dependencies: + function-bind: ^1.1.1 + checksum: b9ad53d53be4af90ce5d1c38331e712522417d017d5ef1ebd0507e07c2fbad8686fffb8e12ddecd4c39ca9b9b47431afbb975b8abf7f3c3b82c98e9aad052792 + languageName: node + linkType: hard + +"hasbin@npm:^1.2.3": + version: 1.2.3 + resolution: "hasbin@npm:1.2.3" + dependencies: + async: ~1.5 + checksum: b30ae3dc4b8427ced20ab55d7a7500ba33f537c6795adb85b49ae8fba1113fb514c67a45cf7c5b0a0b785960fe69fd7edcaf3fba94a95ec41693e430cddb1aa8 + languageName: node + linkType: hard + +"hash-base@npm:^3.0.0": + version: 3.1.0 + resolution: "hash-base@npm:3.1.0" + dependencies: + inherits: ^2.0.4 + readable-stream: ^3.6.0 + safe-buffer: ^5.2.0 + checksum: 26b7e97ac3de13cb23fc3145e7e3450b0530274a9562144fc2bf5c1e2983afd0e09ed7cc3b20974ba66039fad316db463da80eb452e7373e780cbee9a0d2f2dc + languageName: node + linkType: hard + +"hash.js@npm:^1.0.0, hash.js@npm:^1.0.3": + version: 1.1.7 + resolution: "hash.js@npm:1.1.7" + dependencies: + inherits: ^2.0.3 + minimalistic-assert: ^1.0.1 + checksum: e350096e659c62422b85fa508e4b3669017311aa4c49b74f19f8e1bc7f3a54a584fdfd45326d4964d6011f2b2d882e38bea775a96046f2a61b7779a979629d8f + languageName: node + linkType: hard + +"hasha@npm:^5.0.0": + version: 5.2.2 + resolution: "hasha@npm:5.2.2" + dependencies: + is-stream: ^2.0.0 + type-fest: ^0.8.0 + checksum: 06cc474bed246761ff61c19d629977eb5f53fa817be4313a255a64ae0f433e831a29e83acb6555e3f4592b348497596f1d1653751008dda4f21c9c21ca60ac5a + languageName: node + linkType: hard + +"he@npm:1.2.0": + version: 1.2.0 + resolution: "he@npm:1.2.0" + bin: + he: bin/he + checksum: 3d4d6babccccd79c5c5a3f929a68af33360d6445587d628087f39a965079d84f18ce9c3d3f917ee1e3978916fc833bb8b29377c3b403f919426f91bc6965e7a7 + languageName: node + linkType: hard + +"hmac-drbg@npm:^1.0.0": + version: 1.0.1 + resolution: "hmac-drbg@npm:1.0.1" + dependencies: + hash.js: ^1.0.3 + minimalistic-assert: ^1.0.0 + minimalistic-crypto-utils: ^1.0.1 + checksum: bd30b6a68d7f22d63f10e1888aee497d7c2c5c0bb469e66bbdac99f143904d1dfe95f8131f95b3e86c86dd239963c9d972fcbe147e7cffa00e55d18585c43fe0 + languageName: node + linkType: hard + +"hosted-git-info@npm:^2.1.4": + version: 2.8.9 + resolution: "hosted-git-info@npm:2.8.9" + checksum: c955394bdab888a1e9bb10eb33029e0f7ce5a2ac7b3f158099dc8c486c99e73809dca609f5694b223920ca2174db33d32b12f9a2a47141dc59607c29da5a62dd + languageName: node + linkType: hard + +"hosted-git-info@npm:^4.0.0, hosted-git-info@npm:^4.0.1": + version: 4.0.2 + resolution: "hosted-git-info@npm:4.0.2" + dependencies: + lru-cache: ^6.0.0 + checksum: d1b2d7720398ce96a788bd38d198fbddce089a2381f63cfb01743e6c7e5aed656e5547fe74090fb9fe53b2cb785b0e8c9ebdddadff48ed26bb471dd23cd25458 + languageName: node + linkType: hard + +"html-escaper@npm:^2.0.0": + version: 2.0.2 + resolution: "html-escaper@npm:2.0.2" + checksum: d2df2da3ad40ca9ee3a39c5cc6475ef67c8f83c234475f24d8e9ce0dc80a2c82df8e1d6fa78ddd1e9022a586ea1bd247a615e80a5cd9273d90111ddda7d9e974 + languageName: node + linkType: hard + +"htmlescape@npm:^1.1.0": + version: 1.1.1 + resolution: "htmlescape@npm:1.1.1" + checksum: c59a915ae6ae076b5720243c8c594fd8c76e927d511ed5f205e4d586f47d521478d7148dc7fbe3d4a0cfc30abcc2dd215b30255903c09ed04eb38bca44367c5d + languageName: node + linkType: hard + +"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.0": + version: 4.1.0 + resolution: "http-cache-semantics@npm:4.1.0" + checksum: 974de94a81c5474be07f269f9fd8383e92ebb5a448208223bfb39e172a9dbc26feff250192ecc23b9593b3f92098e010406b0f24bd4d588d631f80214648ed42 + languageName: node + linkType: hard + +"http-call@npm:^5.1.2, http-call@npm:^5.2.2": + version: 5.3.0 + resolution: "http-call@npm:5.3.0" + dependencies: + content-type: ^1.0.4 + debug: ^4.1.1 + is-retry-allowed: ^1.1.0 + is-stream: ^2.0.0 + parse-json: ^4.0.0 + tunnel-agent: ^0.6.0 + checksum: 06e9342e1fc9d805ab666c862cac58ece953e0a72007410f4fba9aef40075f4c8bf0fdebbcfa1648433db05003ce1e00496ddb92e8dcff319a976638b2be4057 + languageName: node + linkType: hard + +"http-errors@npm:1.7.2": + version: 1.7.2 + resolution: "http-errors@npm:1.7.2" + dependencies: + depd: ~1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.1 + statuses: ">= 1.5.0 < 2" + toidentifier: 1.0.0 + checksum: 5534b0ae08e77f5a45a2380f500e781f6580c4ff75b816cb1f09f99a290b57e78a518be6d866db1b48cca6b052c09da2c75fc91fb16a2fe3da3c44d9acbb9972 + languageName: node + linkType: hard + +"http-proxy-agent@npm:^4.0.1": + version: 4.0.1 + resolution: "http-proxy-agent@npm:4.0.1" + dependencies: + "@tootallnate/once": 1 + agent-base: 6 + debug: 4 + checksum: c6a5da5a1929416b6bbdf77b1aca13888013fe7eb9d59fc292e25d18e041bb154a8dfada58e223fc7b76b9b2d155a87e92e608235201f77d34aa258707963a82 + languageName: node + linkType: hard + +"http-proxy-agent@npm:^5.0.0": + version: 5.0.0 + resolution: "http-proxy-agent@npm:5.0.0" + dependencies: + "@tootallnate/once": 2 + agent-base: 6 + debug: 4 + checksum: e2ee1ff1656a131953839b2a19cd1f3a52d97c25ba87bd2559af6ae87114abf60971e498021f9b73f9fd78aea8876d1fb0d4656aac8a03c6caa9fc175f22b786 + languageName: node + linkType: hard + +"http-proxy@npm:^1.18.1": + version: 1.18.1 + resolution: "http-proxy@npm:1.18.1" + dependencies: + eventemitter3: ^4.0.0 + follow-redirects: ^1.0.0 + requires-port: ^1.0.0 + checksum: f5bd96bf83e0b1e4226633dbb51f8b056c3e6321917df402deacec31dd7fe433914fc7a2c1831cf7ae21e69c90b3a669b8f434723e9e8b71fd68afe30737b6a5 + languageName: node + linkType: hard + +"http-signature@npm:~1.2.0": + version: 1.2.0 + resolution: "http-signature@npm:1.2.0" + dependencies: + assert-plus: ^1.0.0 + jsprim: ^1.2.2 + sshpk: ^1.7.0 + checksum: 3324598712266a9683585bb84a75dec4fd550567d5e0dd4a0fff6ff3f74348793404d3eeac4918fa0902c810eeee1a86419e4a2e92a164132dfe6b26743fb47c + languageName: node + linkType: hard + +"https-browserify@npm:^1.0.0": + version: 1.0.0 + resolution: "https-browserify@npm:1.0.0" + checksum: 09b35353e42069fde2435760d13f8a3fb7dd9105e358270e2e225b8a94f811b461edd17cb57594e5f36ec1218f121c160ddceeec6e8be2d55e01dcbbbed8cbae + languageName: node + linkType: hard + +"https-proxy-agent@npm:^5.0.0": + version: 5.0.0 + resolution: "https-proxy-agent@npm:5.0.0" + dependencies: + agent-base: 6 + debug: 4 + checksum: 165bfb090bd26d47693597661298006841ab733d0c7383a8cb2f17373387a94c903a3ac687090aa739de05e379ab6f868bae84ab4eac288ad85c328cd1ec9e53 + languageName: node + linkType: hard + +"human-signals@npm:^1.1.1": + version: 1.1.1 + resolution: "human-signals@npm:1.1.1" + checksum: d587647c9e8ec24e02821b6be7de5a0fc37f591f6c4e319b3054b43fd4c35a70a94c46fc74d8c1a43c47fde157d23acd7421f375e1c1365b09a16835b8300205 + languageName: node + linkType: hard + +"human-signals@npm:^2.1.0": + version: 2.1.0 + resolution: "human-signals@npm:2.1.0" + checksum: b87fd89fce72391625271454e70f67fe405277415b48bcc0117ca73d31fa23a4241787afdc8d67f5a116cf37258c052f59ea82daffa72364d61351423848e3b8 + languageName: node + linkType: hard + +"humanize-ms@npm:^1.2.1": + version: 1.2.1 + resolution: "humanize-ms@npm:1.2.1" + dependencies: + ms: ^2.0.0 + checksum: 9c7a74a2827f9294c009266c82031030eae811ca87b0da3dceb8d6071b9bde22c9f3daef0469c3c533cc67a97d8a167cd9fc0389350e5f415f61a79b171ded16 + languageName: node + linkType: hard + +"hyperlinker@npm:^1.0.0": + version: 1.0.0 + resolution: "hyperlinker@npm:1.0.0" + checksum: f6d020ac552e9d048668206c805a737262b4c395546c773cceea3bc45252c46b4fa6eeb67c5896499dad00d21cb2f20f89fdd480a4529cfa3d012da2957162f9 + languageName: node + linkType: hard + +"iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24": + version: 0.4.24 + resolution: "iconv-lite@npm:0.4.24" + dependencies: + safer-buffer: ">= 2.1.2 < 3" + checksum: bd9f120f5a5b306f0bc0b9ae1edeb1577161503f5f8252a20f1a9e56ef8775c9959fd01c55f2d3a39d9a8abaf3e30c1abeb1895f367dcbbe0a8fd1c9ca01c4f6 + languageName: node + linkType: hard + +"iconv-lite@npm:^0.6.2": + version: 0.6.3 + resolution: "iconv-lite@npm:0.6.3" + dependencies: + safer-buffer: ">= 2.1.2 < 3.0.0" + checksum: 3f60d47a5c8fc3313317edfd29a00a692cc87a19cac0159e2ce711d0ebc9019064108323b5e493625e25594f11c6236647d8e256fbe7a58f4a3b33b89e6d30bf + languageName: node + linkType: hard + +"ieee754@npm:1.1.13": + version: 1.1.13 + resolution: "ieee754@npm:1.1.13" + checksum: 102df1ba662e316e6160f7ce29c7c7fa3e04f2014c288336c5a9ff40bbcc2a27d209fa2a81ebfb33f28b1941021343d30e9ad8ee85a2d61f79f5936c35edc33d + languageName: node + linkType: hard + +"ieee754@npm:^1.1.13, ieee754@npm:^1.1.4, ieee754@npm:^1.2.1": + version: 1.2.1 + resolution: "ieee754@npm:1.2.1" + checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e + languageName: node + linkType: hard + +"ignore-by-default@npm:^1.0.1": + version: 1.0.1 + resolution: "ignore-by-default@npm:1.0.1" + checksum: 441509147b3615e0365e407a3c18e189f78c07af08564176c680be1fabc94b6c789cad1342ad887175d4ecd5225de86f73d376cec8e06b42fd9b429505ffcf8a + languageName: node + linkType: hard + +"ignore-walk@npm:^4.0.1": + version: 4.0.1 + resolution: "ignore-walk@npm:4.0.1" + dependencies: + minimatch: ^3.0.4 + checksum: 903cd5cb68d57b2e70fddb83d885aea55f137a44636254a29b08037797376d8d3e09d1c58935778f3a271bf6a2b41ecc54fc22260ac07190e09e1ec7253b49f3 + languageName: node + linkType: hard + +"ignore@npm:^4.0.6": + version: 4.0.6 + resolution: "ignore@npm:4.0.6" + checksum: 248f82e50a430906f9ee7f35e1158e3ec4c3971451dd9f99c9bc1548261b4db2b99709f60ac6c6cac9333494384176cc4cc9b07acbe42d52ac6a09cad734d800 + languageName: node + linkType: hard + +"ignore@npm:^5.1.1, ignore@npm:^5.1.8, ignore@npm:^5.2.0": + version: 5.2.0 + resolution: "ignore@npm:5.2.0" + checksum: 6b1f926792d614f64c6c83da3a1f9c83f6196c2839aa41e1e32dd7b8d174cef2e329d75caabb62cb61ce9dc432f75e67d07d122a037312db7caa73166a1bdb77 + languageName: node + linkType: hard + +"immediate@npm:^3.2.3": + version: 3.3.0 + resolution: "immediate@npm:3.3.0" + checksum: 634b4305101e2452eba6c07d485bf3e415995e533c94b9c3ffbc37026fa1be34def6e4f2276b0dc2162a3f91628564a4bfb26280278b89d3ee54624e854d2f5f + languageName: node + linkType: hard + +"immediate@npm:~3.0.5": + version: 3.0.6 + resolution: "immediate@npm:3.0.6" + checksum: f9b3486477555997657f70318cc8d3416159f208bec4cca3ff3442fd266bc23f50f0c9bd8547e1371a6b5e82b821ec9a7044a4f7b944798b25aa3cc6d5e63e62 + languageName: node + linkType: hard + +"immediate@npm:~3.2.3": + version: 3.2.3 + resolution: "immediate@npm:3.2.3" + checksum: 9867dc70794f3aa246a90afe8a0166607590b687e8c572839ff2342292ac2da4b1cdfd396d38f7b9e72625d817d601e73c33c2874e9c0b8e0f1d6658b3c03496 + languageName: node + linkType: hard + +"import-fresh@npm:^3.0.0, import-fresh@npm:^3.2.1": + version: 3.3.0 + resolution: "import-fresh@npm:3.3.0" + dependencies: + parent-module: ^1.0.0 + resolve-from: ^4.0.0 + checksum: 2cacfad06e652b1edc50be650f7ec3be08c5e5a6f6d12d035c440a42a8cc028e60a5b99ca08a77ab4d6b1346da7d971915828f33cdab730d3d42f08242d09baa + languageName: node + linkType: hard + +"import-lazy@npm:^2.1.0": + version: 2.1.0 + resolution: "import-lazy@npm:2.1.0" + checksum: 05294f3b9dd4971d3a996f0d2f176410fb6745d491d6e73376429189f5c1c3d290548116b2960a7cf3e89c20cdf11431739d1d2d8c54b84061980795010e803a + languageName: node + linkType: hard + +"import-local@npm:^3.0.2": + version: 3.0.3 + resolution: "import-local@npm:3.0.3" + dependencies: + pkg-dir: ^4.2.0 + resolve-cwd: ^3.0.0 + bin: + import-local-fixture: fixtures/cli.js + checksum: 38ae57d35e7fd5f63b55895050c798d4dd590e4e2337e9ffa882fb3ea7a7716f3162c7300e382e0a733ca5d07b389fadff652c00fa7b072d5cb6ea34ca06b179 + languageName: node + linkType: hard + +"imurmurhash@npm:^0.1.4": + version: 0.1.4 + resolution: "imurmurhash@npm:0.1.4" + checksum: 7cae75c8cd9a50f57dadd77482359f659eaebac0319dd9368bcd1714f55e65badd6929ca58569da2b6494ef13fdd5598cd700b1eba23f8b79c5f19d195a3ecf7 + languageName: node + linkType: hard + +"indent-string@npm:^4.0.0": + version: 4.0.0 + resolution: "indent-string@npm:4.0.0" + checksum: 824cfb9929d031dabf059bebfe08cf3137365e112019086ed3dcff6a0a7b698cb80cf67ccccde0e25b9e2d7527aa6cc1fed1ac490c752162496caba3e6699612 + languageName: node + linkType: hard + +"infer-owner@npm:^1.0.4": + version: 1.0.4 + resolution: "infer-owner@npm:1.0.4" + checksum: 181e732764e4a0611576466b4b87dac338972b839920b2a8cde43642e4ed6bd54dc1fb0b40874728f2a2df9a1b097b8ff83b56d5f8f8e3927f837fdcb47d8a89 + languageName: node + linkType: hard + +"inflight@npm:^1.0.4": + version: 1.0.6 + resolution: "inflight@npm:1.0.6" + dependencies: + once: ^1.3.0 + wrappy: 1 + checksum: f4f76aa072ce19fae87ce1ef7d221e709afb59d445e05d47fba710e85470923a75de35bfae47da6de1b18afc3ce83d70facf44cfb0aff89f0a3f45c0a0244dfd + languageName: node + linkType: hard + +"inherits@npm:2, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.1, inherits@npm:~2.0.3, inherits@npm:~2.0.4": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 + languageName: node + linkType: hard + +"inherits@npm:2.0.1, inherits@npm:=2.0.1": + version: 2.0.1 + resolution: "inherits@npm:2.0.1" + checksum: 6536b9377296d4ce8ee89c5c543cb75030934e61af42dba98a428e7d026938c5985ea4d1e3b87743a5b834f40ed1187f89c2d7479e9d59e41d2d1051aefba07b + languageName: node + linkType: hard + +"inherits@npm:2.0.3": + version: 2.0.3 + resolution: "inherits@npm:2.0.3" + checksum: 78cb8d7d850d20a5e9a7f3620db31483aa00ad5f722ce03a55b110e5a723539b3716a3b463e2b96ce3fe286f33afc7c131fa2f91407528ba80cea98a7545d4c0 + languageName: node + linkType: hard + +"ini@npm:2.0.0": + version: 2.0.0 + resolution: "ini@npm:2.0.0" + checksum: e7aadc5fb2e4aefc666d74ee2160c073995a4061556b1b5b4241ecb19ad609243b9cceafe91bae49c219519394bbd31512516cb22a3b1ca6e66d869e0447e84e + languageName: node + linkType: hard + +"ini@npm:^1.3.2, ini@npm:~1.3.0": + version: 1.3.8 + resolution: "ini@npm:1.3.8" + checksum: dfd98b0ca3a4fc1e323e38a6c8eb8936e31a97a918d3b377649ea15bdb15d481207a0dda1021efbd86b464cae29a0d33c1d7dcaf6c5672bee17fa849bc50a1b3 + languageName: node + linkType: hard + +"inline-source-map@npm:~0.6.0": + version: 0.6.2 + resolution: "inline-source-map@npm:0.6.2" + dependencies: + source-map: ~0.5.3 + checksum: 1f7fa2ad1764d03a0a525d5c47993f9e3d0445f29c2e2413d2878deecb6ecb1e6f9137a6207e3db8dc129565bde15de88c1ba2665407e753e7f3ec768ca29262 + languageName: node + linkType: hard + +"inquirer@npm:^8.0.0": + version: 8.2.0 + resolution: "inquirer@npm:8.2.0" + dependencies: + ansi-escapes: ^4.2.1 + chalk: ^4.1.1 + cli-cursor: ^3.1.0 + cli-width: ^3.0.0 + external-editor: ^3.0.3 + figures: ^3.0.0 + lodash: ^4.17.21 + mute-stream: 0.0.8 + ora: ^5.4.1 + run-async: ^2.4.0 + rxjs: ^7.2.0 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + through: ^2.3.6 + checksum: 861d1a9324ae933b49126b3541d94e4d6a2f2a25411b3f3cc00c34bf1bdab34146362d702cf289efe6d8034900dc5905bcf2ea716092a02b6fc390e5986dd236 + languageName: node + linkType: hard + +"insert-module-globals@npm:^7.0.0": + version: 7.2.1 + resolution: "insert-module-globals@npm:7.2.1" + dependencies: + JSONStream: ^1.0.3 + acorn-node: ^1.5.2 + combine-source-map: ^0.8.0 + concat-stream: ^1.6.1 + is-buffer: ^1.1.0 + path-is-absolute: ^1.0.1 + process: ~0.11.0 + through2: ^2.0.0 + undeclared-identifiers: ^1.1.2 + xtend: ^4.0.0 + bin: + insert-module-globals: bin/cmd.js + checksum: c44de7e802186e3207e24beadd71a5bb834700456a9e6f5c8fbb415b6f8356aff44df806e32bf9131143c53348d873fb050ea2b8f3c4cac762922e191b6bef15 + languageName: node + linkType: hard + +"internal-slot@npm:^1.0.3": + version: 1.0.3 + resolution: "internal-slot@npm:1.0.3" + dependencies: + get-intrinsic: ^1.1.0 + has: ^1.0.3 + side-channel: ^1.0.4 + checksum: 1944f92e981e47aebc98a88ff0db579fd90543d937806104d0b96557b10c1f170c51fb777b97740a8b6ddeec585fca8c39ae99fd08a8e058dfc8ab70937238bf + languageName: node + linkType: hard + +"interpret@npm:^1.0.0": + version: 1.4.0 + resolution: "interpret@npm:1.4.0" + checksum: 2e5f51268b5941e4a17e4ef0575bc91ed0ab5f8515e3cf77486f7c14d13f3010df9c0959f37063dcc96e78d12dc6b0bb1b9e111cdfe69771f4656d2993d36155 + languageName: node + linkType: hard + +"interpret@npm:^2.2.0": + version: 2.2.0 + resolution: "interpret@npm:2.2.0" + checksum: f51efef7cb8d02da16408ffa3504cd6053014c5aeb7bb8c223727e053e4235bf565e45d67028b0c8740d917c603807aa3c27d7bd2f21bf20b6417e2bb3e5fd6e + languageName: node + linkType: hard + +"ip-regex@npm:^4.0.0": + version: 4.3.0 + resolution: "ip-regex@npm:4.3.0" + checksum: 7ff904b891221b1847f3fdf3dbb3e6a8660dc39bc283f79eb7ed88f5338e1a3d1104b779bc83759159be266249c59c2160e779ee39446d79d4ed0890dfd06f08 + languageName: node + linkType: hard + +"ip@npm:^1.1.5": + version: 1.1.5 + resolution: "ip@npm:1.1.5" + checksum: 30133981f082a060a32644f6a7746e9ba7ac9e2bc07ecc8bbdda3ee8ca9bec1190724c390e45a1ee7695e7edfd2a8f7dda2c104ec5f7ac5068c00648504c7e5a + languageName: node + linkType: hard + +"is-arguments@npm:^1.0.4": + version: 1.1.1 + resolution: "is-arguments@npm:1.1.1" + dependencies: + call-bind: ^1.0.2 + has-tostringtag: ^1.0.0 + checksum: 7f02700ec2171b691ef3e4d0e3e6c0ba408e8434368504bb593d0d7c891c0dbfda6d19d30808b904a6cb1929bca648c061ba438c39f296c2a8ca083229c49f27 + languageName: node + linkType: hard + +"is-arrayish@npm:^0.2.1": + version: 0.2.1 + resolution: "is-arrayish@npm:0.2.1" + checksum: eef4417e3c10e60e2c810b6084942b3ead455af16c4509959a27e490e7aee87cfb3f38e01bbde92220b528a0ee1a18d52b787e1458ee86174d8c7f0e58cd488f + languageName: node + linkType: hard + +"is-arrayish@npm:^0.3.1": + version: 0.3.2 + resolution: "is-arrayish@npm:0.3.2" + checksum: 977e64f54d91c8f169b59afcd80ff19227e9f5c791fa28fa2e5bce355cbaf6c2c356711b734656e80c9dd4a854dd7efcf7894402f1031dfc5de5d620775b4d5f + languageName: node + linkType: hard + +"is-bigint@npm:^1.0.1": + version: 1.0.4 + resolution: "is-bigint@npm:1.0.4" + dependencies: + has-bigints: ^1.0.1 + checksum: c56edfe09b1154f8668e53ebe8252b6f185ee852a50f9b41e8d921cb2bed425652049fbe438723f6cb48a63ca1aa051e948e7e401e093477c99c84eba244f666 + languageName: node + linkType: hard + +"is-binary-path@npm:~2.1.0": + version: 2.1.0 + resolution: "is-binary-path@npm:2.1.0" + dependencies: + binary-extensions: ^2.0.0 + checksum: 84192eb88cff70d320426f35ecd63c3d6d495da9d805b19bc65b518984b7c0760280e57dbf119b7e9be6b161784a5a673ab2c6abe83abb5198a432232ad5b35c + languageName: node + linkType: hard + +"is-boolean-object@npm:^1.1.0": + version: 1.1.2 + resolution: "is-boolean-object@npm:1.1.2" + dependencies: + call-bind: ^1.0.2 + has-tostringtag: ^1.0.0 + checksum: c03b23dbaacadc18940defb12c1c0e3aaece7553ef58b162a0f6bba0c2a7e1551b59f365b91e00d2dbac0522392d576ef322628cb1d036a0fe51eb466db67222 + languageName: node + linkType: hard + +"is-buffer@npm:^1.1.0": + version: 1.1.6 + resolution: "is-buffer@npm:1.1.6" + checksum: 4a186d995d8bbf9153b4bd9ff9fd04ae75068fe695d29025d25e592d9488911eeece84eefbd8fa41b8ddcc0711058a71d4c466dcf6f1f6e1d83830052d8ca707 + languageName: node + linkType: hard + +"is-callable@npm:^1.1.4, is-callable@npm:^1.2.4": + version: 1.2.4 + resolution: "is-callable@npm:1.2.4" + checksum: 1a28d57dc435797dae04b173b65d6d1e77d4f16276e9eff973f994eadcfdc30a017e6a597f092752a083c1103cceb56c91e3dadc6692fedb9898dfaba701575f + languageName: node + linkType: hard + +"is-ci@npm:^2.0.0": + version: 2.0.0 + resolution: "is-ci@npm:2.0.0" + dependencies: + ci-info: ^2.0.0 + bin: + is-ci: bin.js + checksum: 77b869057510f3efa439bbb36e9be429d53b3f51abd4776eeea79ab3b221337fe1753d1e50058a9e2c650d38246108beffb15ccfd443929d77748d8c0cc90144 + languageName: node + linkType: hard + +"is-core-module@npm:^2.5.0, is-core-module@npm:^2.8.0, is-core-module@npm:^2.8.1": + version: 2.8.1 + resolution: "is-core-module@npm:2.8.1" + dependencies: + has: ^1.0.3 + checksum: 418b7bc10768a73c41c7ef497e293719604007f88934a6ffc5f7c78702791b8528102fb4c9e56d006d69361549b3d9519440214a74aefc7e0b79e5e4411d377f + languageName: node + linkType: hard + +"is-date-object@npm:^1.0.1": + version: 1.0.5 + resolution: "is-date-object@npm:1.0.5" + dependencies: + has-tostringtag: ^1.0.0 + checksum: baa9077cdf15eb7b58c79398604ca57379b2fc4cf9aa7a9b9e295278648f628c9b201400c01c5e0f7afae56507d741185730307cbe7cad3b9f90a77e5ee342fc + languageName: node + linkType: hard + +"is-docker@npm:^2.0.0": + version: 2.2.1 + resolution: "is-docker@npm:2.2.1" + bin: + is-docker: cli.js + checksum: 3fef7ddbf0be25958e8991ad941901bf5922ab2753c46980b60b05c1bf9c9c2402d35e6dc32e4380b980ef5e1970a5d9d5e5aa2e02d77727c3b6b5e918474c56 + languageName: node + linkType: hard + +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: df033653d06d0eb567461e58a7a8c9f940bd8c22274b94bf7671ab36df5719791aae15eef6d83bbb5e23283967f2f984b8914559d4449efda578c775c4be6f85 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^1.0.0": + version: 1.0.0 + resolution: "is-fullwidth-code-point@npm:1.0.0" + dependencies: + number-is-nan: ^1.0.0 + checksum: 4d46a7465a66a8aebcc5340d3b63a56602133874af576a9ca42c6f0f4bd787a743605771c5f246db77da96605fefeffb65fc1dbe862dcc7328f4b4d03edf5a57 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^2.0.0": + version: 2.0.0 + resolution: "is-fullwidth-code-point@npm:2.0.0" + checksum: eef9c6e15f68085fec19ff6a978a6f1b8f48018fd1265035552078ee945573594933b09bbd6f562553e2a241561439f1ef5339276eba68d272001343084cfab8 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 + languageName: node + linkType: hard + +"is-generator-function@npm:^1.0.7": + version: 1.0.10 + resolution: "is-generator-function@npm:1.0.10" + dependencies: + has-tostringtag: ^1.0.0 + checksum: d54644e7dbaccef15ceb1e5d91d680eb5068c9ee9f9eb0a9e04173eb5542c9b51b5ab52c5537f5703e48d5fddfd376817c1ca07a84a407b7115b769d4bdde72b + languageName: node + linkType: hard + +"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: ^2.1.1 + checksum: d381c1319fcb69d341cc6e6c7cd588e17cd94722d9a32dbd60660b993c4fb7d0f19438674e68dfec686d09b7c73139c9166b47597f846af387450224a8101ab4 + languageName: node + linkType: hard + +"is-installed-globally@npm:^0.4.0": + version: 0.4.0 + resolution: "is-installed-globally@npm:0.4.0" + dependencies: + global-dirs: ^3.0.0 + is-path-inside: ^3.0.2 + checksum: 3359840d5982d22e9b350034237b2cda2a12bac1b48a721912e1ab8e0631dd07d45a2797a120b7b87552759a65ba03e819f1bd63f2d7ab8657ec0b44ee0bf399 + languageName: node + linkType: hard + +"is-interactive@npm:^1.0.0": + version: 1.0.0 + resolution: "is-interactive@npm:1.0.0" + checksum: 824808776e2d468b2916cdd6c16acacebce060d844c35ca6d82267da692e92c3a16fdba624c50b54a63f38bdc4016055b6f443ce57d7147240de4f8cdabaf6f9 + languageName: node + linkType: hard + +"is-ip@npm:^3.1.0": + version: 3.1.0 + resolution: "is-ip@npm:3.1.0" + dependencies: + ip-regex: ^4.0.0 + checksum: da2c2b282407194adf2320bade0bad94be9c9d0bdab85ff45b1b62d8185f31c65dff3884519d57bf270277e5ea2046c7916a6e5a6db22fe4b7ddcdd3760f23eb + languageName: node + linkType: hard + +"is-lambda@npm:^1.0.1": + version: 1.0.1 + resolution: "is-lambda@npm:1.0.1" + checksum: 93a32f01940220532e5948538699ad610d5924ac86093fcee83022252b363eb0cc99ba53ab084a04e4fb62bf7b5731f55496257a4c38adf87af9c4d352c71c35 + languageName: node + linkType: hard + +"is-nan@npm:^1.2.1": + version: 1.3.2 + resolution: "is-nan@npm:1.3.2" + dependencies: + call-bind: ^1.0.0 + define-properties: ^1.1.3 + checksum: 5dfadcef6ad12d3029d43643d9800adbba21cf3ce2ec849f734b0e14ee8da4070d82b15fdb35138716d02587c6578225b9a22779cab34888a139cc43e4e3610a + languageName: node + linkType: hard + +"is-negative-zero@npm:^2.0.1": + version: 2.0.1 + resolution: "is-negative-zero@npm:2.0.1" + checksum: a46f2e0cb5e16fdb8f2011ed488979386d7e68d381966682e3f4c98fc126efe47f26827912baca2d06a02a644aee458b9cba307fb389f6b161e759125db7a3b8 + languageName: node + linkType: hard + +"is-npm@npm:^5.0.0": + version: 5.0.0 + resolution: "is-npm@npm:5.0.0" + checksum: 9baff02b0c69a3d3c79b162cb2f9e67fb40ef6d172c16601b2e2471c21e9a4fa1fc9885a308d7bc6f3a3cd2a324c27fa0bf284c133c3349bb22571ab70d041cc + languageName: node + linkType: hard + +"is-number-object@npm:^1.0.4": + version: 1.0.6 + resolution: "is-number-object@npm:1.0.6" + dependencies: + has-tostringtag: ^1.0.0 + checksum: c697704e8fc2027fc41cb81d29805de4e8b6dc9c3efee93741dbf126a8ecc8443fef85adbc581415ae7e55d325e51d0a942324ae35c829131748cce39cba55f3 + languageName: node + linkType: hard + +"is-number@npm:^7.0.0": + version: 7.0.0 + resolution: "is-number@npm:7.0.0" + checksum: 456ac6f8e0f3111ed34668a624e45315201dff921e5ac181f8ec24923b99e9f32ca1a194912dc79d539c97d33dba17dc635202ff0b2cf98326f608323276d27a + languageName: node + linkType: hard + +"is-obj@npm:^2.0.0": + version: 2.0.0 + resolution: "is-obj@npm:2.0.0" + checksum: c9916ac8f4621962a42f5e80e7ffdb1d79a3fab7456ceaeea394cd9e0858d04f985a9ace45be44433bf605673c8be8810540fe4cc7f4266fc7526ced95af5a08 + languageName: node + linkType: hard + +"is-path-inside@npm:^3.0.2": + version: 3.0.3 + resolution: "is-path-inside@npm:3.0.3" + checksum: abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9 + languageName: node + linkType: hard + +"is-plain-obj@npm:^1.1.0": + version: 1.1.0 + resolution: "is-plain-obj@npm:1.1.0" + checksum: 0ee04807797aad50859652a7467481816cbb57e5cc97d813a7dcd8915da8195dc68c436010bf39d195226cde6a2d352f4b815f16f26b7bf486a5754290629931 + languageName: node + linkType: hard + +"is-plain-obj@npm:^2.0.0, is-plain-obj@npm:^2.1.0": + version: 2.1.0 + resolution: "is-plain-obj@npm:2.1.0" + checksum: cec9100678b0a9fe0248a81743041ed990c2d4c99f893d935545cfbc42876cbe86d207f3b895700c690ad2fa520e568c44afc1605044b535a7820c1d40e38daa + languageName: node + linkType: hard + +"is-plain-object@npm:^2.0.4": + version: 2.0.4 + resolution: "is-plain-object@npm:2.0.4" + dependencies: + isobject: ^3.0.1 + checksum: 2a401140cfd86cabe25214956ae2cfee6fbd8186809555cd0e84574f88de7b17abacb2e477a6a658fa54c6083ecbda1e6ae404c7720244cd198903848fca70ca + languageName: node + linkType: hard + +"is-plain-object@npm:^5.0.0": + version: 5.0.0 + resolution: "is-plain-object@npm:5.0.0" + checksum: e32d27061eef62c0847d303125440a38660517e586f2f3db7c9d179ae5b6674ab0f469d519b2e25c147a1a3bc87156d0d5f4d8821e0ce4a9ee7fe1fcf11ce45c + languageName: node + linkType: hard + +"is-regex@npm:^1.1.4": + version: 1.1.4 + resolution: "is-regex@npm:1.1.4" + dependencies: + call-bind: ^1.0.2 + has-tostringtag: ^1.0.0 + checksum: 362399b33535bc8f386d96c45c9feb04cf7f8b41c182f54174c1a45c9abbbe5e31290bbad09a458583ff6bf3b2048672cdb1881b13289569a7c548370856a652 + languageName: node + linkType: hard + +"is-retry-allowed@npm:^1.1.0": + version: 1.2.0 + resolution: "is-retry-allowed@npm:1.2.0" + checksum: 50d700a89ae31926b1c91b3eb0104dbceeac8790d8b80d02f5c76d9a75c2056f1bb24b5268a8a018dead606bddf116b2262e5ac07401eb8b8783b266ed22558d + languageName: node + linkType: hard + +"is-scoped@npm:^2.1.0": + version: 2.1.0 + resolution: "is-scoped@npm:2.1.0" + dependencies: + scoped-regex: ^2.0.0 + checksum: bc4726ec6c71c10d095e815040e361ce9f75503b9c2b1dadd3af720222034cd35e2601e44002a9e372709abc1dba357195c64977395adac2c100789becc901fb + languageName: node + linkType: hard + +"is-shared-array-buffer@npm:^1.0.1": + version: 1.0.1 + resolution: "is-shared-array-buffer@npm:1.0.1" + checksum: 2ffb92533e64e2876e6cfe6906871d28400b6f1a53130fe652ec8007bc0e5044d05e7af8e31bdc992fbba520bd92938cfbeedd0f286be92f250c7c76191c4d90 + languageName: node + linkType: hard + +"is-stream@npm:^1.1.0": + version: 1.1.0 + resolution: "is-stream@npm:1.1.0" + checksum: 063c6bec9d5647aa6d42108d4c59723d2bd4ae42135a2d4db6eadbd49b7ea05b750fd69d279e5c7c45cf9da753ad2c00d8978be354d65aa9f6bb434969c6a2ae + languageName: node + linkType: hard + +"is-stream@npm:^2.0.0": + version: 2.0.1 + resolution: "is-stream@npm:2.0.1" + checksum: b8e05ccdf96ac330ea83c12450304d4a591f9958c11fd17bed240af8d5ffe08aedafa4c0f4cfccd4d28dc9d4d129daca1023633d5c11601a6cbc77521f6fae66 + languageName: node + linkType: hard + +"is-string@npm:^1.0.5, is-string@npm:^1.0.7": + version: 1.0.7 + resolution: "is-string@npm:1.0.7" + dependencies: + has-tostringtag: ^1.0.0 + checksum: 323b3d04622f78d45077cf89aab783b2f49d24dc641aa89b5ad1a72114cfeff2585efc8c12ef42466dff32bde93d839ad321b26884cf75e5a7892a938b089989 + languageName: node + linkType: hard + +"is-symbol@npm:^1.0.2, is-symbol@npm:^1.0.3": + version: 1.0.4 + resolution: "is-symbol@npm:1.0.4" + dependencies: + has-symbols: ^1.0.2 + checksum: 92805812ef590738d9de49d677cd17dfd486794773fb6fa0032d16452af46e9b91bb43ffe82c983570f015b37136f4b53b28b8523bfb10b0ece7a66c31a54510 + languageName: node + linkType: hard + +"is-text-path@npm:^1.0.1": + version: 1.0.1 + resolution: "is-text-path@npm:1.0.1" + dependencies: + text-extensions: ^1.0.0 + checksum: fb5d78752c22b3f73a7c9540768f765ffcfa38c9e421e2b9af869565307fa1ae5e3d3a2ba016a43549742856846566d327da406e94a5846ec838a288b1704fd2 + languageName: node + linkType: hard + +"is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.7": + version: 1.1.8 + resolution: "is-typed-array@npm:1.1.8" + dependencies: + available-typed-arrays: ^1.0.5 + call-bind: ^1.0.2 + es-abstract: ^1.18.5 + foreach: ^2.0.5 + has-tostringtag: ^1.0.0 + checksum: aa0f9f0716e19e2fb8aef69e69e4205479d25ace778e2339fc910948115cde4b0d9aff9d5d1e8b80f09a5664998278e05e54ad3dc9cb12cefcf86db71084ed00 + languageName: node + linkType: hard + +"is-typedarray@npm:^1.0.0, is-typedarray@npm:~1.0.0": + version: 1.0.0 + resolution: "is-typedarray@npm:1.0.0" + checksum: 3508c6cd0a9ee2e0df2fa2e9baabcdc89e911c7bd5cf64604586697212feec525aa21050e48affb5ffc3df20f0f5d2e2cf79b08caa64e1ccc9578e251763aef7 + languageName: node + linkType: hard + +"is-unicode-supported@npm:^0.1.0": + version: 0.1.0 + resolution: "is-unicode-supported@npm:0.1.0" + checksum: a2aab86ee7712f5c2f999180daaba5f361bdad1efadc9610ff5b8ab5495b86e4f627839d085c6530363c6d6d4ecbde340fb8e54bdb83da4ba8e0865ed5513c52 + languageName: node + linkType: hard + +"is-utf8@npm:^0.2.0, is-utf8@npm:^0.2.1": + version: 0.2.1 + resolution: "is-utf8@npm:0.2.1" + checksum: 167ccd2be869fc228cc62c1a28df4b78c6b5485d15a29027d3b5dceb09b383e86a3522008b56dcac14b592b22f0a224388718c2505027a994fd8471465de54b3 + languageName: node + linkType: hard + +"is-weakref@npm:^1.0.1": + version: 1.0.1 + resolution: "is-weakref@npm:1.0.1" + dependencies: + call-bind: ^1.0.0 + checksum: fdafb7b955671dd2f9658ff47c86e4025c0650fc68a3542a40e5a75898a763b1abd6b1e1f9f13207eed49541cdd76af67d73c44989ea358b201b70274cf8f6c1 + languageName: node + linkType: hard + +"is-windows@npm:^1.0.2": + version: 1.0.2 + resolution: "is-windows@npm:1.0.2" + checksum: 438b7e52656fe3b9b293b180defb4e448088e7023a523ec21a91a80b9ff8cdb3377ddb5b6e60f7c7de4fa8b63ab56e121b6705fe081b3cf1b828b0a380009ad7 + languageName: node + linkType: hard + +"is-wsl@npm:^2.2.0": + version: 2.2.0 + resolution: "is-wsl@npm:2.2.0" + dependencies: + is-docker: ^2.0.0 + checksum: 20849846ae414997d290b75e16868e5261e86ff5047f104027026fd61d8b5a9b0b3ade16239f35e1a067b3c7cc02f70183cb661010ed16f4b6c7c93dad1b19d8 + languageName: node + linkType: hard + +"is-yarn-global@npm:^0.3.0": + version: 0.3.0 + resolution: "is-yarn-global@npm:0.3.0" + checksum: bca013d65fee2862024c9fbb3ba13720ffca2fe750095174c1c80922fdda16402b5c233f5ac9e265bc12ecb5446e7b7f519a32d9541788f01d4d44e24d2bf481 + languageName: node + linkType: hard + +"isarray@npm:0.0.1": + version: 0.0.1 + resolution: "isarray@npm:0.0.1" + checksum: 49191f1425681df4a18c2f0f93db3adb85573bcdd6a4482539d98eac9e705d8961317b01175627e860516a2fc45f8f9302db26e5a380a97a520e272e2a40a8d4 + languageName: node + linkType: hard + +"isarray@npm:^1.0.0, isarray@npm:~1.0.0": + version: 1.0.0 + resolution: "isarray@npm:1.0.0" + checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab + languageName: node + linkType: hard + +"isbinaryfile@npm:^4.0.8": + version: 4.0.8 + resolution: "isbinaryfile@npm:4.0.8" + checksum: 606e3bb648d1a0dee23459d1d937bb2560e66a5281ec7c9ff50e585402d73321ac268d0f34cb7393125b3ebc4c7962d39e50a01cdb8904b52fce08b7ccd2bf9f + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 26bf6c5480dda5161c820c5b5c751ae1e766c587b1f951ea3fcfc973bafb7831ae5b54a31a69bd670220e42e99ec154475025a468eae58ea262f813fdc8d1c62 + languageName: node + linkType: hard + +"isobject@npm:^3.0.1": + version: 3.0.1 + resolution: "isobject@npm:3.0.1" + checksum: db85c4c970ce30693676487cca0e61da2ca34e8d4967c2e1309143ff910c207133a969f9e4ddb2dc6aba670aabce4e0e307146c310350b298e74a31f7d464703 + languageName: node + linkType: hard + +"isomorphic-ws@npm:^4.0.1": + version: 4.0.1 + resolution: "isomorphic-ws@npm:4.0.1" + peerDependencies: + ws: "*" + checksum: d7190eadefdc28bdb93d67b5f0c603385aaf87724fa2974abb382ac1ec9756ed2cfb27065cbe76122879c2d452e2982bc4314317f3d6c737ddda6c047328771a + languageName: node + linkType: hard + +"isstream@npm:~0.1.2": + version: 0.1.2 + resolution: "isstream@npm:0.1.2" + checksum: 1eb2fe63a729f7bdd8a559ab552c69055f4f48eb5c2f03724430587c6f450783c8f1cd936c1c952d0a927925180fcc892ebd5b174236cf1065d4bd5bdb37e963 + languageName: node + linkType: hard + +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.0.0-alpha.1": + version: 3.2.0 + resolution: "istanbul-lib-coverage@npm:3.2.0" + checksum: a2a545033b9d56da04a8571ed05c8120bf10e9bce01cf8633a3a2b0d1d83dff4ac4fe78d6d5673c27fc29b7f21a41d75f83a36be09f82a61c367b56aa73c1ff9 + languageName: node + linkType: hard + +"istanbul-lib-hook@npm:^3.0.0": + version: 3.0.0 + resolution: "istanbul-lib-hook@npm:3.0.0" + dependencies: + append-transform: ^2.0.0 + checksum: ac4d0a0751e959cfe4c95d817df5f1f573f9b0cf892552e60d81785654291391fac1ceb667f13bb17fcc2ef23b74c89ed8cf1c6148c833c8596a2b920b079101 + languageName: node + linkType: hard + +"istanbul-lib-instrument@npm:^4.0.0": + version: 4.0.3 + resolution: "istanbul-lib-instrument@npm:4.0.3" + dependencies: + "@babel/core": ^7.7.5 + "@istanbuljs/schema": ^0.1.2 + istanbul-lib-coverage: ^3.0.0 + semver: ^6.3.0 + checksum: fa1171d3022b1bb8f6a734042620ac5d9ee7dc80f3065a0bb12863e9f0494d0eefa3d86608fcc0254ab2765d29d7dad8bdc42e5f8df2f9a1fbe85ccc59d76cb9 + languageName: node + linkType: hard + +"istanbul-lib-processinfo@npm:^2.0.2": + version: 2.0.2 + resolution: "istanbul-lib-processinfo@npm:2.0.2" + dependencies: + archy: ^1.0.0 + cross-spawn: ^7.0.0 + istanbul-lib-coverage: ^3.0.0-alpha.1 + make-dir: ^3.0.0 + p-map: ^3.0.0 + rimraf: ^3.0.0 + uuid: ^3.3.3 + checksum: 400bd0b25b623c172e48d37e5bdda7a58b2fe5beeedfeb03099aed3385223d31e4cfa6f9932be07bbf06cfd039023301bce81d3b70b9a20a79a38b0f12cb261a + languageName: node + linkType: hard + +"istanbul-lib-report@npm:^3.0.0": + version: 3.0.0 + resolution: "istanbul-lib-report@npm:3.0.0" + dependencies: + istanbul-lib-coverage: ^3.0.0 + make-dir: ^3.0.0 + supports-color: ^7.1.0 + checksum: 3f29eb3f53c59b987386e07fe772d24c7f58c6897f34c9d7a296f4000de7ae3de9eb95c3de3df91dc65b134c84dee35c54eee572a56243e8907c48064e34ff1b + languageName: node + linkType: hard + +"istanbul-lib-source-maps@npm:^4.0.0": + version: 4.0.1 + resolution: "istanbul-lib-source-maps@npm:4.0.1" + dependencies: + debug: ^4.1.1 + istanbul-lib-coverage: ^3.0.0 + source-map: ^0.6.1 + checksum: 21ad3df45db4b81852b662b8d4161f6446cd250c1ddc70ef96a585e2e85c26ed7cd9c2a396a71533cfb981d1a645508bc9618cae431e55d01a0628e7dec62ef2 + languageName: node + linkType: hard + +"istanbul-reports@npm:^3.0.2": + version: 3.0.5 + resolution: "istanbul-reports@npm:3.0.5" + dependencies: + html-escaper: ^2.0.0 + istanbul-lib-report: ^3.0.0 + checksum: b167411c4cd551aec39c8275ef42f25e7083caa5a467c1b35f33b19f37211656ebf03f1cbe5c55d691b44398314dcc73be52dc6b7afb13b7a1a02eb65d702a75 + languageName: node + linkType: hard + +"jake@npm:^10.6.1": + version: 10.8.2 + resolution: "jake@npm:10.8.2" + dependencies: + async: 0.9.x + chalk: ^2.4.2 + filelist: ^1.0.1 + minimatch: ^3.0.4 + bin: + jake: ./bin/cli.js + checksum: b604c51863260e374ccd62cd0cfe0b659f72cb71beb7d5fb5137dd65b04cf9d5603abd01f9f6eaaac8f4182f396d6cfae01e0b0844c2215c9c1e200572307cf9 + languageName: node + linkType: hard + +"javascript-natural-sort@npm:^0.7.1": + version: 0.7.1 + resolution: "javascript-natural-sort@npm:0.7.1" + checksum: 161e2c512cc7884bc055a582c6645d9032cab88497a76123d73cb23bfb03d97a04cf7772ecdb8bd3366fc07192c2f996366f479f725c23ef073fffe03d6a586a + languageName: node + linkType: hard + +"jayson@npm:^2.1.0": + version: 2.1.2 + resolution: "jayson@npm:2.1.2" + dependencies: + "@types/node": ^10.3.5 + JSONStream: ^1.3.1 + commander: ^2.12.2 + es6-promisify: ^5.0.0 + eyes: ^0.1.8 + json-stringify-safe: ^5.0.1 + lodash: ^4.17.11 + uuid: ^3.2.1 + bin: + jayson: ./bin/jayson.js + checksum: 7d66d37ea08d585d8cdef0db889b208e682e983aecd21300e19e30dfa444882680d4ea46ccffd81a0b355852c470fc8bdbaceaba8c4262791836e40ac606854c + languageName: node + linkType: hard + +"jayson@npm:^3.3.4": + version: 3.6.5 + resolution: "jayson@npm:3.6.5" + dependencies: + "@types/connect": ^3.4.33 + "@types/express-serve-static-core": ^4.17.9 + "@types/lodash": ^4.14.159 + "@types/node": ^12.12.54 + "@types/ws": ^7.4.4 + JSONStream: ^1.3.5 + commander: ^2.20.3 + delay: ^5.0.0 + es6-promisify: ^5.0.0 + eyes: ^0.1.8 + isomorphic-ws: ^4.0.1 + json-stringify-safe: ^5.0.1 + lodash: ^4.17.20 + uuid: ^3.4.0 + ws: ^7.4.5 + bin: + jayson: bin/jayson.js + checksum: dde536e7206afdaf88be97e481f0e6977f88713614a06269ade2ffe31ab98735f69ee305d7fb8a6d76391bd15eb2cc7add95355764287f6f33aed97ea7ac6968 + languageName: node + linkType: hard + +"jest-diff@npm:^27.3.1": + version: 27.3.1 + resolution: "jest-diff@npm:27.3.1" + dependencies: + chalk: ^4.0.0 + diff-sequences: ^27.0.6 + jest-get-type: ^27.3.1 + pretty-format: ^27.3.1 + checksum: 49231a4ac4bed1cce8f5135db2a26a83673d5cbe5716bca29900a45ae0ddf237099d9091acac436b9c60ab933b0e7ca086ce8cb71f44411b572b69adbe96128d + languageName: node + linkType: hard + +"jest-get-type@npm:^27.3.1": + version: 27.3.1 + resolution: "jest-get-type@npm:27.3.1" + checksum: b0b8db1d770c6332b4189bbf4073184489acbb1095410cf53add033daf911577ee6bd1c4f8d747dd2f3d63de42f7eb15c5527fc7288a2855a046f4a8957cd902 + languageName: node + linkType: hard + +"jest-matcher-utils@npm:^27.3.1": + version: 27.3.1 + resolution: "jest-matcher-utils@npm:27.3.1" + dependencies: + chalk: ^4.0.0 + jest-diff: ^27.3.1 + jest-get-type: ^27.3.1 + pretty-format: ^27.3.1 + checksum: 118c428b5509c767596a785697f8bedf90eb06278ffb76ecd57eb8eebc7c66a17dabb5960e100e7b1a91fb2638722bfec0152a3deb1162049eeb98ebe40f6caa + languageName: node + linkType: hard + +"jest-message-util@npm:^27.3.1": + version: 27.3.1 + resolution: "jest-message-util@npm:27.3.1" + dependencies: + "@babel/code-frame": ^7.12.13 + "@jest/types": ^27.2.5 + "@types/stack-utils": ^2.0.0 + chalk: ^4.0.0 + graceful-fs: ^4.2.4 + micromatch: ^4.0.4 + pretty-format: ^27.3.1 + slash: ^3.0.0 + stack-utils: ^2.0.3 + checksum: 2d10734765e3e965f92b7cf009206a702e644228114bda3e20c40f59fe603422a55aa6632b4413e030bf352a03f362d321c0d881908c1d24b05e097da3ee3c4a + languageName: node + linkType: hard + +"jest-regex-util@npm:^27.0.6": + version: 27.0.6 + resolution: "jest-regex-util@npm:27.0.6" + checksum: 4d613b00f2076560e9d5e5674ec63a4130d7b1584dbbf25d84d3a455b0ff7a12d8f94eaa00facd7934d285330d370c270ca093667d537a5842e95457e8e1ecf4 + languageName: node + linkType: hard + +"jest-worker@npm:^27.4.5": + version: 27.5.1 + resolution: "jest-worker@npm:27.5.1" + dependencies: + "@types/node": "*" + merge-stream: ^2.0.0 + supports-color: ^8.0.0 + checksum: 98cd68b696781caed61c983a3ee30bf880b5bd021c01d98f47b143d4362b85d0737f8523761e2713d45e18b4f9a2b98af1eaee77afade4111bb65c77d6f7c980 + languageName: node + linkType: hard + +"jmespath@npm:0.16.0": + version: 0.16.0 + resolution: "jmespath@npm:0.16.0" + checksum: 2d602493a1e4addfd1350ac8c9d54b1b03ed09e305fd863bab84a4ee1f52868cf939dd1a08c5cdea29ce9ba8f86875ebb458b6ed45dab3e1c3f2694503fb2fd9 + languageName: node + linkType: hard + +"jmespath@npm:^0.15.0": + version: 0.15.0 + resolution: "jmespath@npm:0.15.0" + checksum: 353bb9e69cc4c1560be0a4df43cb4020abc246e1c60cb5b55dcc76d8c858383f1633faf22ccaf6a5e09568a2077d0f4f1e989e6fcfd496b5cef87964cc8cb9e7 + languageName: node + linkType: hard + +"joycon@npm:^2.2.5": + version: 2.2.5 + resolution: "joycon@npm:2.2.5" + checksum: 930bb748c0ade3b70cca756aa559916a3e0df36b06b0ace629d9c4a6081d235d3d7a93eb7d3094d53ab7a3658bcd5c6a54e4ed235e5f5c03a177597a669081eb + languageName: node + linkType: hard + +"js-base64@npm:^2.1.9": + version: 2.6.4 + resolution: "js-base64@npm:2.6.4" + checksum: 5f4084078d6c46f8529741d110df84b14fac3276b903760c21fa8cc8521370d607325dfe1c1a9fbbeaae1ff8e602665aaeef1362427d8fef704f9e3659472ce8 + languageName: node + linkType: hard + +"js-merkle@npm:^0.1.5": + version: 0.1.5 + resolution: "js-merkle@npm:0.1.5" + checksum: 1b19f50c06a1642a4871da94aa304f1c0cf26443629ce5ee366b29d2929de1d26ae295eb7dee0c67c3b6d37c10295609519d54d5ec657faec4c0e28674d7d951 + languageName: node + linkType: hard + +"js-tokens@npm:^4.0.0": + version: 4.0.0 + resolution: "js-tokens@npm:4.0.0" + checksum: 8a95213a5a77deb6cbe94d86340e8d9ace2b93bc367790b260101d2f36a2eaf4e4e22d9fa9cf459b38af3a32fb4190e638024cf82ec95ef708680e405ea7cc78 + languageName: node + linkType: hard + +"js-yaml@npm:3.13.1": + version: 3.13.1 + resolution: "js-yaml@npm:3.13.1" + dependencies: + argparse: ^1.0.7 + esprima: ^4.0.0 + bin: + js-yaml: bin/js-yaml.js + checksum: 7511b764abb66d8aa963379f7d2a404f078457d106552d05a7b556d204f7932384e8477513c124749fa2de52eb328961834562bd09924902c6432e40daa408bc + languageName: node + linkType: hard + +"js-yaml@npm:4.1.0": + version: 4.1.0 + resolution: "js-yaml@npm:4.1.0" + dependencies: + argparse: ^2.0.1 + bin: + js-yaml: bin/js-yaml.js + checksum: c7830dfd456c3ef2c6e355cc5a92e6700ceafa1d14bba54497b34a99f0376cecbb3e9ac14d3e5849b426d5a5140709a66237a8c991c675431271c4ce5504151a + languageName: node + linkType: hard + +"js-yaml@npm:^3.13.0, js-yaml@npm:^3.13.1": + version: 3.14.1 + resolution: "js-yaml@npm:3.14.1" + dependencies: + argparse: ^1.0.7 + esprima: ^4.0.0 + bin: + js-yaml: bin/js-yaml.js + checksum: bef146085f472d44dee30ec34e5cf36bf89164f5d585435a3d3da89e52622dff0b188a580e4ad091c3341889e14cb88cac6e4deb16dc5b1e9623bb0601fc255c + languageName: node + linkType: hard + +"jsbn@npm:~0.1.0": + version: 0.1.1 + resolution: "jsbn@npm:0.1.1" + checksum: e5ff29c1b8d965017ef3f9c219dacd6e40ad355c664e277d31246c90545a02e6047018c16c60a00f36d561b3647215c41894f5d869ada6908a2e0ce4200c88f2 + languageName: node + linkType: hard + +"jsdoctypeparser@npm:^6.1.0": + version: 6.1.0 + resolution: "jsdoctypeparser@npm:6.1.0" + bin: + jsdoctypeparser: ./bin/jsdoctypeparser + checksum: 14a0ef3671afc53909854db6edd7c417b13b86196083ccd246995df1f742a4fa8f2079526bfa46c1ab2a26dbc5caa7806bd8d43c13abfc053ea3d1f9ebb70792 + languageName: node + linkType: hard + +"jsesc@npm:^2.5.1": + version: 2.5.2 + resolution: "jsesc@npm:2.5.2" + bin: + jsesc: bin/jsesc + checksum: 4dc190771129e12023f729ce20e1e0bfceac84d73a85bc3119f7f938843fe25a4aeccb54b6494dce26fcf263d815f5f31acdefac7cc9329efb8422a4f4d9fa9d + languageName: node + linkType: hard + +"jsesc@npm:~0.5.0": + version: 0.5.0 + resolution: "jsesc@npm:0.5.0" + bin: + jsesc: bin/jsesc + checksum: b8b44cbfc92f198ad972fba706ee6a1dfa7485321ee8c0b25f5cedd538dcb20cde3197de16a7265430fce8277a12db066219369e3d51055038946039f6e20e17 + languageName: node + linkType: hard + +"json-buffer@npm:3.0.0": + version: 3.0.0 + resolution: "json-buffer@npm:3.0.0" + checksum: 0cecacb8025370686a916069a2ff81f7d55167421b6aa7270ee74e244012650dd6bce22b0852202ea7ff8624fce50ff0ec1bdf95914ccb4553426e290d5a63fa + languageName: node + linkType: hard + +"json-parse-better-errors@npm:^1.0.1, json-parse-better-errors@npm:^1.0.2": + version: 1.0.2 + resolution: "json-parse-better-errors@npm:1.0.2" + checksum: ff2b5ba2a70e88fd97a3cb28c1840144c5ce8fae9cbeeddba15afa333a5c407cf0e42300cd0a2885dbb055227fe68d405070faad941beeffbfde9cf3b2c78c5d + languageName: node + linkType: hard + +"json-parse-even-better-errors@npm:^2.3.0, json-parse-even-better-errors@npm:^2.3.1": + version: 2.3.1 + resolution: "json-parse-even-better-errors@npm:2.3.1" + checksum: 798ed4cf3354a2d9ccd78e86d2169515a0097a5c133337807cdf7f1fc32e1391d207ccfc276518cc1d7d8d4db93288b8a50ba4293d212ad1336e52a8ec0a941f + languageName: node + linkType: hard + +"json-pointer@npm:^0.6.0": + version: 0.6.1 + resolution: "json-pointer@npm:0.6.1" + dependencies: + foreach: ^2.0.4 + checksum: 882b4b24b515fd866f8c228ed1381c129a40bdbfec578b73882be86a52bbcac15aaa87579c548167d4d8d9e3fc60e35aefead473df95c463553bede2b957f79c + languageName: node + linkType: hard + +"json-schema-diff-validator@npm:^0.4.1": + version: 0.4.1 + resolution: "json-schema-diff-validator@npm:0.4.1" + dependencies: + fast-json-patch: ^2.0.5 + json-pointer: ^0.6.0 + bin: + json-schema-diff-validator: ./dist/bin/cli.js + checksum: d75b7f55409afd581dbfba5b8f8c936669fa2b0dcd857ec7b563f2f2f50ae90c88b5f8cca65d41a66c591f62c9a71a449fc8fc4d8b94fe4d7490e1bb9e59b429 + languageName: node + linkType: hard + +"json-schema-ref-parser@npm:^7.1.3": + version: 7.1.4 + resolution: "json-schema-ref-parser@npm:7.1.4" + dependencies: + call-me-maybe: ^1.0.1 + js-yaml: ^3.13.1 + ono: ^6.0.0 + checksum: 690252bb1e055be586b68de1c9af804bbba2ebdc9937e89451be87b20127881d74619c0ce48d669f8e54015e7242e71dedd1a6a80c62f37337981a249ed956f1 + languageName: node + linkType: hard + +"json-schema-traverse@npm:^0.4.1": + version: 0.4.1 + resolution: "json-schema-traverse@npm:0.4.1" + checksum: 7486074d3ba247769fda17d5181b345c9fb7d12e0da98b22d1d71a5db9698d8b4bd900a3ec1a4ffdd60846fc2556274a5c894d0c48795f14cb03aeae7b55260b + languageName: node + linkType: hard + +"json-schema-traverse@npm:^1.0.0": + version: 1.0.0 + resolution: "json-schema-traverse@npm:1.0.0" + checksum: 02f2f466cdb0362558b2f1fd5e15cce82ef55d60cd7f8fa828cf35ba74330f8d767fcae5c5c2adb7851fa811766c694b9405810879bc4e1ddd78a7c0e03658ad + languageName: node + linkType: hard + +"json-schema@npm:0.2.3": + version: 0.2.3 + resolution: "json-schema@npm:0.2.3" + checksum: bbc2070988fb5f2a2266a31b956f1b5660e03ea7eaa95b33402901274f625feb586ae0c485e1df854fde40a7f0dc679f3b3ca8e5b8d31f8ea07a0d834de785c7 + languageName: node + linkType: hard + +"json-stable-stringify-without-jsonify@npm:^1.0.1": + version: 1.0.1 + resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" + checksum: cff44156ddce9c67c44386ad5cddf91925fe06b1d217f2da9c4910d01f358c6e3989c4d5a02683c7a5667f9727ff05831f7aa8ae66c8ff691c556f0884d49215 + languageName: node + linkType: hard + +"json-stable-stringify@npm:~0.0.0": + version: 0.0.1 + resolution: "json-stable-stringify@npm:0.0.1" + dependencies: + jsonify: ~0.0.0 + checksum: 3a148d4c32bf65c61ceba1a10ffe3e91b8f106135cc203ab464cfe7792e545426294beb60711406a4ef62c001c20c916efc600e44e3ce66d1927bb7f781f8201 + languageName: node + linkType: hard + +"json-stringify-nice@npm:^1.1.4": + version: 1.1.4 + resolution: "json-stringify-nice@npm:1.1.4" + checksum: 6ddf781148b46857ab04e97f47be05f14c4304b86eb5478369edbeacd070c21c697269964b982fc977e8989d4c59091103b1d9dc291aba40096d6cbb9a392b72 + languageName: node + linkType: hard + +"json-stringify-safe@npm:^5.0.1, json-stringify-safe@npm:~5.0.1": + version: 5.0.1 + resolution: "json-stringify-safe@npm:5.0.1" + checksum: 48ec0adad5280b8a96bb93f4563aa1667fd7a36334f79149abd42446d0989f2ddc58274b479f4819f1f00617957e6344c886c55d05a4e15ebb4ab931e4a6a8ee + languageName: node + linkType: hard + +"json5@npm:^1.0.1": + version: 1.0.1 + resolution: "json5@npm:1.0.1" + dependencies: + minimist: ^1.2.0 + bin: + json5: lib/cli.js + checksum: e76ea23dbb8fc1348c143da628134a98adf4c5a4e8ea2adaa74a80c455fc2cdf0e2e13e6398ef819bfe92306b610ebb2002668ed9fc1af386d593691ef346fc3 + languageName: node + linkType: hard + +"json5@npm:^2.1.2, json5@npm:^2.2.0": + version: 2.2.0 + resolution: "json5@npm:2.2.0" + dependencies: + minimist: ^1.2.5 + bin: + json5: lib/cli.js + checksum: e88fc5274bb58fc99547baa777886b069d2dd96d9cfc4490b305fd16d711dabd5979e35a4f90873cefbeb552e216b041a304fe56702bedba76e19bc7845f208d + languageName: node + linkType: hard + +"jsonfile@npm:^4.0.0": + version: 4.0.0 + resolution: "jsonfile@npm:4.0.0" + dependencies: + graceful-fs: ^4.1.6 + dependenciesMeta: + graceful-fs: + optional: true + checksum: 6447d6224f0d31623eef9b51185af03ac328a7553efcee30fa423d98a9e276ca08db87d71e17f2310b0263fd3ffa6c2a90a6308367f661dc21580f9469897c9e + languageName: node + linkType: hard + +"jsonfile@npm:^6.0.1": + version: 6.1.0 + resolution: "jsonfile@npm:6.1.0" + dependencies: + graceful-fs: ^4.1.6 + universalify: ^2.0.0 + dependenciesMeta: + graceful-fs: + optional: true + checksum: 7af3b8e1ac8fe7f1eccc6263c6ca14e1966fcbc74b618d3c78a0a2075579487547b94f72b7a1114e844a1e15bb00d440e5d1720bfc4612d790a6f285d5ea8354 + languageName: node + linkType: hard + +"jsonify@npm:~0.0.0": + version: 0.0.0 + resolution: "jsonify@npm:0.0.0" + checksum: d8d4ed476c116e6987a460dcb82f22284686caae9f498ac87b0502c1765ac1522f4f450a4cad4cc368d202fd3b27a3860735140a82867fc6d558f5f199c38bce + languageName: node + linkType: hard + +"jsonparse@npm:^1.2.0, jsonparse@npm:^1.3.1": + version: 1.3.1 + resolution: "jsonparse@npm:1.3.1" + checksum: 6514a7be4674ebf407afca0eda3ba284b69b07f9958a8d3113ef1005f7ec610860c312be067e450c569aab8b89635e332cee3696789c750692bb60daba627f4d + languageName: node + linkType: hard + +"jsprim@npm:^1.2.2": + version: 1.4.1 + resolution: "jsprim@npm:1.4.1" + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.2.3 + verror: 1.10.0 + checksum: 6bcb20ec265ae18bb48e540a6da2c65f9c844f7522712d6dfcb01039527a49414816f4869000493363f1e1ea96cbad00e46188d5ecc78257a19f152467587373 + languageName: node + linkType: hard + +"just-diff-apply@npm:^4.0.1": + version: 4.0.1 + resolution: "just-diff-apply@npm:4.0.1" + checksum: fdb58c0c8da766943fb316158d823fe485058d6b31ec6c51f99076df76363fa1ca35d79fb23f53184bf5b7443ae470fe5f087b4a504e913a8f96474963907e2e + languageName: node + linkType: hard + +"just-diff@npm:^5.0.1": + version: 5.0.1 + resolution: "just-diff@npm:5.0.1" + checksum: efbdb652987ca109839dba385904ea152cc73ef4c165eebb4be0af261734cf91387e529fcd52aea5ba9567b4ef76c584ee6254ccf0030dc5d0ccdab3b890a085 + languageName: node + linkType: hard + +"just-extend@npm:^4.0.2": + version: 4.2.1 + resolution: "just-extend@npm:4.2.1" + checksum: ff9fdede240fad313efeeeb68a660b942e5586d99c0058064c78884894a2690dc09bba44c994ad4e077e45d913fef01a9240c14a72c657b53687ac58de53b39c + languageName: node + linkType: hard + +"karma-chai@npm:^0.1.0": + version: 0.1.0 + resolution: "karma-chai@npm:0.1.0" + peerDependencies: + chai: "*" + karma: ">=0.10.9" + checksum: 7fae0b4acea35121218c5284e49c7a0e4ad5806abca50ee1451e314e63b4e7b72aaccda90a78d0099cc3a02eb07ebea92cc8877b74cfd62db52fe7bb0907a287 + languageName: node + linkType: hard + +"karma-chrome-launcher@npm:^3.1.0": + version: 3.1.0 + resolution: "karma-chrome-launcher@npm:3.1.0" + dependencies: + which: ^1.2.1 + checksum: 63431ddec9aa40e2a0439d9e2bcfa58a6822efd08e2666bdbc3f55dfbe8fcc0b401035b71b1f6f21340339dc56c172edaed8e8c0ddc6949873318ad1666b2dd9 + languageName: node + linkType: hard + +"karma-firefox-launcher@npm:^2.1.1": + version: 2.1.2 + resolution: "karma-firefox-launcher@npm:2.1.2" + dependencies: + is-wsl: ^2.2.0 + which: ^2.0.1 + checksum: bfd5b35b35949fee50d92def57b32ab9702926ac24c90036583b76beac33fe8100ec601c9e71087b861d6ca7b3d54e8d2f9b62fa1b5115c8b99512dd763cf2ad + languageName: node + linkType: hard + +"karma-mocha-reporter@npm:^2.2.5": + version: 2.2.5 + resolution: "karma-mocha-reporter@npm:2.2.5" + dependencies: + chalk: ^2.1.0 + log-symbols: ^2.1.0 + strip-ansi: ^4.0.0 + peerDependencies: + karma: ">=0.13" + checksum: 8b9e43c64bc975d38c18958d7ba95baf9a8d23f11ee4641955fe0ae5e8bd563596c8965d6f290ce8c73c4832fb98bb7ed5fbbaa82b49273d2ca24aa7f3d0d5e5 + languageName: node + linkType: hard + +"karma-mocha@npm:^2.0.1": + version: 2.0.1 + resolution: "karma-mocha@npm:2.0.1" + dependencies: + minimist: ^1.2.3 + checksum: a09f4758758a899fb97836660624ccd1769325e05f6efca63c9132806cc8dfeb20eaf78b3bc4db7921dcb3c48384fbfd5cddfa3568ddaf00197c75852ec9b480 + languageName: node + linkType: hard + +"karma-sourcemap-loader@npm:^0.3.7": + version: 0.3.8 + resolution: "karma-sourcemap-loader@npm:0.3.8" + dependencies: + graceful-fs: ^4.1.2 + checksum: 12e21849af695f4aaa012752128b494e8b4b601c8d910212253d153b92153017364ad99f9ed86d58578ce44a7e53e9498ebd329950f1d0f0290bb90f4fea6d2c + languageName: node + linkType: hard + +"karma-webpack@npm:^5.0.0": + version: 5.0.0 + resolution: "karma-webpack@npm:5.0.0" + dependencies: + glob: ^7.1.3 + minimatch: ^3.0.4 + webpack-merge: ^4.1.5 + peerDependencies: + webpack: ^5.0.0 + checksum: 869b835f91b99036d12c1b4342126b75093f7f524e2b245d557e720a402faf8bc90050f99962b0f12af98535812f46fa6325d1a7ed8569aed6dc5ead40c63ec4 + languageName: node + linkType: hard + +"karma@npm:^6.3.4": + version: 6.3.9 + resolution: "karma@npm:6.3.9" + dependencies: + body-parser: ^1.19.0 + braces: ^3.0.2 + chokidar: ^3.5.1 + colors: ^1.4.0 + connect: ^3.7.0 + di: ^0.0.1 + dom-serialize: ^2.2.1 + glob: ^7.1.7 + graceful-fs: ^4.2.6 + http-proxy: ^1.18.1 + isbinaryfile: ^4.0.8 + lodash: ^4.17.21 + log4js: ^6.3.0 + mime: ^2.5.2 + minimatch: ^3.0.4 + qjobs: ^1.2.0 + range-parser: ^1.2.1 + rimraf: ^3.0.2 + socket.io: ^4.2.0 + source-map: ^0.6.1 + tmp: ^0.2.1 + ua-parser-js: ^0.7.30 + yargs: ^16.1.1 + bin: + karma: bin/karma + checksum: 2e652c8f4d520593bedf96d21a9ab92b02e93bdb2598c7abee79ccdbd793099c634555e247559801b1226bae3a5544be61a3570f8ed560e96064447fb19206c9 + languageName: node + linkType: hard + +"keyv@npm:^3.0.0": + version: 3.1.0 + resolution: "keyv@npm:3.1.0" + dependencies: + json-buffer: 3.0.0 + checksum: bb7e8f3acffdbafbc2dd5b63f377fe6ec4c0e2c44fc82720449ef8ab54f4a7ce3802671ed94c0f475ae0a8549703353a2124561fcf3317010c141b32ca1ce903 + languageName: node + linkType: hard + +"kind-of@npm:^6.0.2, kind-of@npm:^6.0.3": + version: 6.0.3 + resolution: "kind-of@npm:6.0.3" + checksum: 3ab01e7b1d440b22fe4c31f23d8d38b4d9b91d9f291df683476576493d5dfd2e03848a8b05813dd0c3f0e835bc63f433007ddeceb71f05cb25c45ae1b19c6d3b + languageName: node + linkType: hard + +"kuler@npm:^2.0.0": + version: 2.0.0 + resolution: "kuler@npm:2.0.0" + checksum: 9e10b5a1659f9ed8761d38df3c35effabffbd19fc6107324095238e4ef0ff044392cae9ac64a1c2dda26e532426485342226b93806bd97504b174b0dcf04ed81 + languageName: node + linkType: hard + +"labeled-stream-splicer@npm:^2.0.0": + version: 2.0.2 + resolution: "labeled-stream-splicer@npm:2.0.2" + dependencies: + inherits: ^2.0.1 + stream-splicer: ^2.0.0 + checksum: 4f7097b7666cd6d110f2a700f2905f703aa2a6d21c76fb390fcf441f436b269f5b1ad813178af4406cf6ddf01f3ac24435b3ff8fe2d9678664c147bf92f056b3 + languageName: node + linkType: hard + +"latest-version@npm:^5.1.0": + version: 5.1.0 + resolution: "latest-version@npm:5.1.0" + dependencies: + package-json: ^6.3.0 + checksum: fbc72b071eb66c40f652441fd783a9cca62f08bf42433651937f078cd9ef94bf728ec7743992777826e4e89305aef24f234b515e6030503a2cbee7fc9bdc2c0f + languageName: node + linkType: hard + +"level-concat-iterator@npm:~2.0.0": + version: 2.0.1 + resolution: "level-concat-iterator@npm:2.0.1" + checksum: 562583ef1292215f8e749c402510cb61c4d6fccf4541082b3d21dfa5ecde9fcccfe52bdcb5cfff9d2384e7ce5891f44df9439a6ddb39b0ffe31015600b4a828a + languageName: node + linkType: hard + +"level-errors@npm:~2.0.0": + version: 2.0.1 + resolution: "level-errors@npm:2.0.1" + dependencies: + errno: ~0.1.1 + checksum: aca5d7670e2a40609db8d7743fce289bb5202c0bc13e4a78f81f36a6642e9abc0110f48087d3d3c2c04f023d70d4ee6f2db0e20c63d29b3fda323a67bfff6526 + languageName: node + linkType: hard + +"level-iterator-stream@npm:~4.0.0": + version: 4.0.2 + resolution: "level-iterator-stream@npm:4.0.2" + dependencies: + inherits: ^2.0.4 + readable-stream: ^3.4.0 + xtend: ^4.0.2 + checksum: 239e2c7e62bffb485ed696bcd3b98de7a2bc455d13be4fce175ae3544fe9cda81c2ed93d3e88b61380ae6d28cce02511862d77b86fb2ba5b5cf00471f3c1eccc + languageName: node + linkType: hard + +"level-supports@npm:~1.0.0": + version: 1.0.1 + resolution: "level-supports@npm:1.0.1" + dependencies: + xtend: ^4.0.2 + checksum: 5d6bdb88cf00c3d9adcde970db06a548c72c5a94bf42c72f998b58341a105bfe2ea30d313ce1e84396b98cc9ddbc0a9bd94574955a86e929f73c986e10fc0df0 + languageName: node + linkType: hard + +"levelup@npm:^4.4.0": + version: 4.4.0 + resolution: "levelup@npm:4.4.0" + dependencies: + deferred-leveldown: ~5.3.0 + level-errors: ~2.0.0 + level-iterator-stream: ~4.0.0 + level-supports: ~1.0.0 + xtend: ~4.0.0 + checksum: 5a09e34c78cd7c23f9f6cb73563f1ebe8121ffc5f9f5f232242529d4fbdd40e8d1ffb337d2defa0b842334e0dbd4028fbfe7a072eebfe2c4d07174f0aa4aabca + languageName: node + linkType: hard + +"leven@npm:2.1.0": + version: 2.1.0 + resolution: "leven@npm:2.1.0" + checksum: f7b4a01b15c0ee2f92a04c0367ea025d10992b044df6f0d4ee1a845d4a488b343e99799e2f31212d72a2b1dea67124f57c1bb1b4561540df45190e44b5b8b394 + languageName: node + linkType: hard + +"levn@npm:^0.4.1": + version: 0.4.1 + resolution: "levn@npm:0.4.1" + dependencies: + prelude-ls: ^1.2.1 + type-check: ~0.4.0 + checksum: 12c5021c859bd0f5248561bf139121f0358285ec545ebf48bb3d346820d5c61a4309535c7f387ed7d84361cf821e124ce346c6b7cef8ee09a67c1473b46d0fc4 + languageName: node + linkType: hard + +"levn@npm:~0.3.0": + version: 0.3.0 + resolution: "levn@npm:0.3.0" + dependencies: + prelude-ls: ~1.1.2 + type-check: ~0.3.2 + checksum: 0d084a524231a8246bb10fec48cdbb35282099f6954838604f3c7fc66f2e16fa66fd9cc2f3f20a541a113c4dafdf181e822c887c8a319c9195444e6c64ac395e + languageName: node + linkType: hard + +"lie@npm:3.1.1": + version: 3.1.1 + resolution: "lie@npm:3.1.1" + dependencies: + immediate: ~3.0.5 + checksum: 6da9f2121d2dbd15f1eca44c0c7e211e66a99c7b326ec8312645f3648935bc3a658cf0e9fa7b5f10144d9e2641500b4f55bd32754607c3de945b5f443e50ddd1 + languageName: node + linkType: hard + +"lines-and-columns@npm:^1.1.6": + version: 1.1.6 + resolution: "lines-and-columns@npm:1.1.6" + checksum: 198a5436b1fa5cf703bae719c01c686b076f0ad7e1aafd95a58d626cabff302dc0414822126f2f80b58a8c3d66cda8a7b6da064f27130f87e1d3506d6dfd0d68 + languageName: node + linkType: hard + +"listr2@npm:3.5.0": + version: 3.5.0 + resolution: "listr2@npm:3.5.0" + dependencies: + chalk: ^4.1.0 + cli-truncate: ^2.1.0 + figures: ^3.2.0 + indent-string: ^4.0.0 + log-update: ^4.0.0 + p-map: ^4.0.0 + rxjs: ^6.6.7 + through: ^2.3.8 + wrap-ansi: ^7.0.0 + peerDependencies: + enquirer: ">= 2.3.0 < 3" + checksum: cf308374629674608e41ca25e9314181551a1e2a752ddba495b7366326da88e1b01df24d13db5b7bf7ab0d8735db9cf1d5b6939b552c9c96b6314d596d3ba268 + languageName: node + linkType: hard + +"load-json-file@npm:^4.0.0": + version: 4.0.0 + resolution: "load-json-file@npm:4.0.0" + dependencies: + graceful-fs: ^4.1.2 + parse-json: ^4.0.0 + pify: ^3.0.0 + strip-bom: ^3.0.0 + checksum: 8f5d6d93ba64a9620445ee9bde4d98b1eac32cf6c8c2d20d44abfa41a6945e7969456ab5f1ca2fb06ee32e206c9769a20eec7002fe290de462e8c884b6b8b356 + languageName: node + linkType: hard + +"load-json-file@npm:^6.2.0": + version: 6.2.0 + resolution: "load-json-file@npm:6.2.0" + dependencies: + graceful-fs: ^4.1.15 + parse-json: ^5.0.0 + strip-bom: ^4.0.0 + type-fest: ^0.6.0 + checksum: 4429e430ebb99375fc7cd936348e4f7ba729486080ced4272091c1e386a7f5f738ea3337d8ffd4b01c2f5bc3ddde92f2c780045b66838fe98bdb79f901884643 + languageName: node + linkType: hard + +"load-yaml-file@npm:^0.2.0": + version: 0.2.0 + resolution: "load-yaml-file@npm:0.2.0" + dependencies: + graceful-fs: ^4.1.5 + js-yaml: ^3.13.0 + pify: ^4.0.1 + strip-bom: ^3.0.0 + checksum: d86d7ec7b15a1c35b40fb0d8abe710a7de83e0c1186c1d35a7eaaf8581611828089a3e706f64560c2939762bc73f18a7b85aed9335058c640e033933cf317f11 + languageName: node + linkType: hard + +"loader-runner@npm:^4.2.0": + version: 4.2.0 + resolution: "loader-runner@npm:4.2.0" + checksum: e61aea8b6904b8af53d9de6f0484da86c462c0001f4511bedc837cec63deb9475cea813db62f702cd7930420ccb0e75c78112270ca5c8b61b374294f53c0cb3a + languageName: node + linkType: hard + +"loader-utils@npm:^1.4.0": + version: 1.4.0 + resolution: "loader-utils@npm:1.4.0" + dependencies: + big.js: ^5.2.2 + emojis-list: ^3.0.0 + json5: ^1.0.1 + checksum: d150b15e7a42ac47d935c8b484b79e44ff6ab4c75df7cc4cb9093350cf014ec0b17bdb60c5d6f91a37b8b218bd63b973e263c65944f58ca2573e402b9a27e717 + languageName: node + linkType: hard + +"loader-utils@npm:^2.0.0": + version: 2.0.2 + resolution: "loader-utils@npm:2.0.2" + dependencies: + big.js: ^5.2.2 + emojis-list: ^3.0.0 + json5: ^2.1.2 + checksum: 9078d1ed47cadc57f4c6ddbdb2add324ee7da544cea41de3b7f1128e8108fcd41cd3443a85b7ee8d7d8ac439148aa221922774efe4cf87506d4fb054d5889303 + languageName: node + linkType: hard + +"localforage@npm:^1.10.0": + version: 1.10.0 + resolution: "localforage@npm:1.10.0" + dependencies: + lie: 3.1.1 + checksum: f2978b434dafff9bcb0d9498de57d97eba165402419939c944412e179cab1854782830b5ec196212560b22712d1dd03918939f59cf1d4fc1d756fca7950086cf + languageName: node + linkType: hard + +"locate-path@npm:^2.0.0": + version: 2.0.0 + resolution: "locate-path@npm:2.0.0" + dependencies: + p-locate: ^2.0.0 + path-exists: ^3.0.0 + checksum: 02d581edbbbb0fa292e28d96b7de36b5b62c2fa8b5a7e82638ebb33afa74284acf022d3b1e9ae10e3ffb7658fbc49163fcd5e76e7d1baaa7801c3e05a81da755 + languageName: node + linkType: hard + +"locate-path@npm:^5.0.0": + version: 5.0.0 + resolution: "locate-path@npm:5.0.0" + dependencies: + p-locate: ^4.1.0 + checksum: 83e51725e67517287d73e1ded92b28602e3ae5580b301fe54bfb76c0c723e3f285b19252e375712316774cf52006cb236aed5704692c32db0d5d089b69696e30 + languageName: node + linkType: hard + +"locate-path@npm:^6.0.0": + version: 6.0.0 + resolution: "locate-path@npm:6.0.0" + dependencies: + p-locate: ^5.0.0 + checksum: 72eb661788a0368c099a184c59d2fee760b3831c9c1c33955e8a19ae4a21b4116e53fa736dc086cdeb9fce9f7cc508f2f92d2d3aae516f133e16a2bb59a39f5a + languageName: node + linkType: hard + +"lodash.camelcase@npm:^4.3.0": + version: 4.3.0 + resolution: "lodash.camelcase@npm:4.3.0" + checksum: cb9227612f71b83e42de93eccf1232feeb25e705bdb19ba26c04f91e885bfd3dd5c517c4a97137658190581d3493ea3973072ca010aab7e301046d90740393d1 + languageName: node + linkType: hard + +"lodash.clone@npm:~4.5.0": + version: 4.5.0 + resolution: "lodash.clone@npm:4.5.0" + checksum: 5839f22acf3a43c026ac4325f7bcd378f34967415cd0b9fd7efa9bbbf38dc665900d36e040944c5afab94a51ff8a24f6cfc3781fe439705cbad5c722e9506b16 + languageName: node + linkType: hard + +"lodash.clonedeep@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.clonedeep@npm:4.5.0" + checksum: 92c46f094b064e876a23c97f57f81fbffd5d760bf2d8a1c61d85db6d1e488c66b0384c943abee4f6af7debf5ad4e4282e74ff83177c9e63d8ff081a4837c3489 + languageName: node + linkType: hard + +"lodash.clonedeepwith@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.clonedeepwith@npm:4.5.0" + checksum: 9fbf4ebfa04b381df226a2298eba680327bea3d0d5d19c5118de7ae218fd219186e30e9fd0d33b13729f34ffbc83c1cf09cb27aff265ba94cb602b8a2b1e71c9 + languageName: node + linkType: hard + +"lodash.debounce@npm:^4.0.8": + version: 4.0.8 + resolution: "lodash.debounce@npm:4.0.8" + checksum: a3f527d22c548f43ae31c861ada88b2637eb48ac6aa3eb56e82d44917971b8aa96fbb37aa60efea674dc4ee8c42074f90f7b1f772e9db375435f6c83a19b3bc6 + languageName: node + linkType: hard + +"lodash.find@npm:^4.6.0": + version: 4.6.0 + resolution: "lodash.find@npm:4.6.0" + checksum: b737f849a4fe36f5c3664ea636780dda2fde18335021faf80cdfdcb300ed75441da6f55cfd6de119092d8bb2ddbc4433f4a8de4b99c0b9c8640465b0901c717c + languageName: node + linkType: hard + +"lodash.flattendeep@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.flattendeep@npm:4.4.0" + checksum: 8521c919acac3d4bcf0aaf040c1ca9cb35d6c617e2d72e9b4d51c9a58b4366622cd6077441a18be626c3f7b28227502b3bf042903d447b056ee7e0b11d45c722 + languageName: node + linkType: hard + +"lodash.get@npm:^4.4.2": + version: 4.4.2 + resolution: "lodash.get@npm:4.4.2" + checksum: e403047ddb03181c9d0e92df9556570e2b67e0f0a930fcbbbd779370972368f5568e914f913e93f3b08f6d492abc71e14d4e9b7a18916c31fa04bd2306efe545 + languageName: node + linkType: hard + +"lodash.isequal@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.isequal@npm:4.5.0" + checksum: da27515dc5230eb1140ba65ff8de3613649620e8656b19a6270afe4866b7bd461d9ba2ac8a48dcc57f7adac4ee80e1de9f965d89d4d81a0ad52bb3eec2609644 + languageName: node + linkType: hard + +"lodash.ismatch@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.ismatch@npm:4.4.0" + checksum: a393917578842705c7fc1a30fb80613d1ac42d20b67eb26a2a6004d6d61ee90b419f9eb320508ddcd608e328d91eeaa2651411727eaa9a12534ed6ccb02fc705 + languageName: node + linkType: hard + +"lodash.matches@npm:^4.6.0": + version: 4.6.0 + resolution: "lodash.matches@npm:4.6.0" + checksum: 002617abb63d3735d8600e5e74d499a02edbef090c33acf1198f46b3ec924752729bbac2eb4f26ec00b0ce3b382fd03fdf25d19cfff08ec735180fcb36cd3fe4 + languageName: node + linkType: hard + +"lodash.memoize@npm:~3.0.3": + version: 3.0.4 + resolution: "lodash.memoize@npm:3.0.4" + checksum: fc52e0916b896fa79d6b85fbeaa0e44a381b70f1fcab7acab10188aaeeb2107e21b9b992bff560f405696e0a6e3bb5c08af18955d628a1e8ab6b11df14ff6172 + languageName: node + linkType: hard + +"lodash.merge@npm:^4.6.1, lodash.merge@npm:^4.6.2": + version: 4.6.2 + resolution: "lodash.merge@npm:4.6.2" + checksum: ad580b4bdbb7ca1f7abf7e1bce63a9a0b98e370cf40194b03380a46b4ed799c9573029599caebc1b14e3f24b111aef72b96674a56cfa105e0f5ac70546cdc005 + languageName: node + linkType: hard + +"lodash.sample@npm:^4.2.1": + version: 4.2.1 + resolution: "lodash.sample@npm:4.2.1" + checksum: 8d93c1db133b7c7c5d6f87eb6e512c83dc97baf1de56a2013eff17fd8ddffa5a0901541e320933d9fdb6a4d90e8a597d44bb7aa1e4fa6eca4da534802941e4ab + languageName: node + linkType: hard + +"lodash.set@npm:^4.3.2": + version: 4.3.2 + resolution: "lodash.set@npm:4.3.2" + checksum: a9122f49eef9f2d0fc9061a33d87f8e5b8c6b23d46e8b9e9ce1529d3588d79741bd1145a3abdfa3b13082703e65af27ff18d8a07bfc22b9be32f3fc36f763f70 + languageName: node + linkType: hard + +"lodash.truncate@npm:^4.4.2": + version: 4.4.2 + resolution: "lodash.truncate@npm:4.4.2" + checksum: b463d8a382cfb5f0e71c504dcb6f807a7bd379ff1ea216669aa42c52fc28c54e404bfbd96791aa09e6df0de2c1d7b8f1b7f4b1a61f324d38fe98bc535aeee4f5 + languageName: node + linkType: hard + +"lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21": + version: 4.17.21 + resolution: "lodash@npm:4.17.21" + checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 + languageName: node + linkType: hard + +"log-symbols@npm:4.1.0, log-symbols@npm:^4.0.0, log-symbols@npm:^4.1.0": + version: 4.1.0 + resolution: "log-symbols@npm:4.1.0" + dependencies: + chalk: ^4.1.0 + is-unicode-supported: ^0.1.0 + checksum: fce1497b3135a0198803f9f07464165e9eb83ed02ceb2273930a6f8a508951178d8cf4f0378e9d28300a2ed2bc49050995d2bd5f53ab716bb15ac84d58c6ef74 + languageName: node + linkType: hard + +"log-symbols@npm:^2.1.0": + version: 2.2.0 + resolution: "log-symbols@npm:2.2.0" + dependencies: + chalk: ^2.0.1 + checksum: 4c95e3b65f0352dbe91dc4989c10baf7a44e2ef5b0db7e6721e1476268e2b6f7090c3aa880d4f833a05c5c3ff18f4ec5215a09bd0099986d64a8186cfeb48ac8 + languageName: node + linkType: hard + +"log-update@npm:^4.0.0": + version: 4.0.0 + resolution: "log-update@npm:4.0.0" + dependencies: + ansi-escapes: ^4.3.0 + cli-cursor: ^3.1.0 + slice-ansi: ^4.0.0 + wrap-ansi: ^6.2.0 + checksum: ae2f85bbabc1906034154fb7d4c4477c79b3e703d22d78adee8b3862fa913942772e7fa11713e3d96fb46de4e3cabefbf5d0a544344f03b58d3c4bff52aa9eb2 + languageName: node + linkType: hard + +"log4js@npm:^6.3.0": + version: 6.3.0 + resolution: "log4js@npm:6.3.0" + dependencies: + date-format: ^3.0.0 + debug: ^4.1.1 + flatted: ^2.0.1 + rfdc: ^1.1.4 + streamroller: ^2.2.4 + checksum: da2812bbe477d0594154562d63c8b23030d4a31964bbf9d5b708f528eb57adb9e4c2eec4caf087400776b5758e4f5e5a5ef90c1453bec471aba2a8c42ee9176c + languageName: node + linkType: hard + +"logform@npm:^2.2.0": + version: 2.3.0 + resolution: "logform@npm:2.3.0" + dependencies: + colors: ^1.2.1 + fecha: ^4.2.0 + ms: ^2.1.1 + safe-stable-stringify: ^1.1.0 + triple-beam: ^1.3.0 + checksum: a82d36823d487dffeb9c7f468bb60a20a643bb5299860b17050e68866f2b8c18f1a6eeb158ad5ae8aa90ada2923a5f2a04809f1e041dd2167f18308116432970 + languageName: node + linkType: hard + +"long@npm:^4.0.0": + version: 4.0.0 + resolution: "long@npm:4.0.0" + checksum: 16afbe8f749c7c849db1f4de4e2e6a31ac6e617cead3bdc4f9605cb703cd20e1e9fc1a7baba674ffcca57d660a6e5b53a9e236d7b25a295d3855cca79cc06744 + languageName: node + linkType: hard + +"long@npm:^5.2.0": + version: 5.2.0 + resolution: "long@npm:5.2.0" + checksum: 37aa4e67b9c3eebc6d9d675adcc9d06f06059ca268922a71273de389746bf07f0ff282f9e604d17fdf84c4149099b44e936ea2b621a6c4759a216621afa97efd + languageName: node + linkType: hard + +"lower-case@npm:^2.0.2": + version: 2.0.2 + resolution: "lower-case@npm:2.0.2" + dependencies: + tslib: ^2.0.3 + checksum: 83a0a5f159ad7614bee8bf976b96275f3954335a84fad2696927f609ddae902802c4f3312d86668722e668bef41400254807e1d3a7f2e8c3eede79691aa1f010 + languageName: node + linkType: hard + +"lowercase-keys@npm:^1.0.0, lowercase-keys@npm:^1.0.1": + version: 1.0.1 + resolution: "lowercase-keys@npm:1.0.1" + checksum: 4d045026595936e09953e3867722e309415ff2c80d7701d067546d75ef698dac218a4f53c6d1d0e7368b47e45fd7529df47e6cb56fbb90523ba599f898b3d147 + languageName: node + linkType: hard + +"lowercase-keys@npm:^2.0.0": + version: 2.0.0 + resolution: "lowercase-keys@npm:2.0.0" + checksum: 24d7ebd56ccdf15ff529ca9e08863f3c54b0b9d1edb97a3ae1af34940ae666c01a1e6d200707bce730a8ef76cb57cc10e65f245ecaaf7e6bc8639f2fb460ac23 + languageName: node + linkType: hard + +"lru-cache@npm:^5.1.1": + version: 5.1.1 + resolution: "lru-cache@npm:5.1.1" + dependencies: + yallist: ^3.0.2 + checksum: c154ae1cbb0c2206d1501a0e94df349653c92c8cbb25236d7e85190bcaf4567a03ac6eb43166fabfa36fd35623694da7233e88d9601fbf411a9a481d85dbd2cb + languageName: node + linkType: hard + +"lru-cache@npm:^6.0.0": + version: 6.0.0 + resolution: "lru-cache@npm:6.0.0" + dependencies: + yallist: ^4.0.0 + checksum: f97f499f898f23e4585742138a22f22526254fdba6d75d41a1c2526b3b6cc5747ef59c5612ba7375f42aca4f8461950e925ba08c991ead0651b4918b7c978297 + languageName: node + linkType: hard + +"lru-cache@npm:^7.3.1": + version: 7.3.1 + resolution: "lru-cache@npm:7.3.1" + checksum: 34bb50c015ffc29fd83545e912f28cea6e03fbf41c497fa220c4f131b990f9ddf95babac98745b416cbc6c0d835254d61668d09b8a4ecb476934546afc9e51bd + languageName: node + linkType: hard + +"ltgt@npm:~2.2.0": + version: 2.2.1 + resolution: "ltgt@npm:2.2.1" + checksum: 7e3874296f7538bc8087b428ac4208008d7b76916354b34a08818ca7c83958c1df10ec427eeeaad895f6b81e41e24745b18d30f89abcc21d228b94f6961d50a2 + languageName: node + linkType: hard + +"make-dir@npm:^3.0.0, make-dir@npm:^3.0.2, make-dir@npm:^3.1.0": + version: 3.1.0 + resolution: "make-dir@npm:3.1.0" + dependencies: + semver: ^6.0.0 + checksum: 484200020ab5a1fdf12f393fe5f385fc8e4378824c940fba1729dcd198ae4ff24867bc7a5646331e50cead8abff5d9270c456314386e629acec6dff4b8016b78 + languageName: node + linkType: hard + +"make-error@npm:^1.1.1": + version: 1.3.6 + resolution: "make-error@npm:1.3.6" + checksum: b86e5e0e25f7f777b77fabd8e2cbf15737972869d852a22b7e73c17623928fccb826d8e46b9951501d3f20e51ad74ba8c59ed584f610526a48f8ccf88aaec402 + languageName: node + linkType: hard + +"make-fetch-happen@npm:^10.0.1": + version: 10.0.3 + resolution: "make-fetch-happen@npm:10.0.3" + dependencies: + agentkeepalive: ^4.2.0 + cacache: ^15.3.0 + http-cache-semantics: ^4.1.0 + http-proxy-agent: ^5.0.0 + https-proxy-agent: ^5.0.0 + is-lambda: ^1.0.1 + lru-cache: ^7.3.1 + minipass: ^3.1.6 + minipass-collect: ^1.0.2 + minipass-fetch: ^1.4.1 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.4 + negotiator: ^0.6.3 + promise-retry: ^2.0.1 + socks-proxy-agent: ^6.1.1 + ssri: ^8.0.1 + checksum: edf3ba5119c7cf528485dbeafd14dc84c01c97038c13696d20ff5edb5274d9fddc418be3dffe7c542f7dc4691dc221e9faf913b207f6ccb7c81b43f479525c0c + languageName: node + linkType: hard + +"make-fetch-happen@npm:^9.1.0": + version: 9.1.0 + resolution: "make-fetch-happen@npm:9.1.0" + dependencies: + agentkeepalive: ^4.1.3 + cacache: ^15.2.0 + http-cache-semantics: ^4.1.0 + http-proxy-agent: ^4.0.1 + https-proxy-agent: ^5.0.0 + is-lambda: ^1.0.1 + lru-cache: ^6.0.0 + minipass: ^3.1.3 + minipass-collect: ^1.0.2 + minipass-fetch: ^1.3.2 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.4 + negotiator: ^0.6.2 + promise-retry: ^2.0.1 + socks-proxy-agent: ^6.0.0 + ssri: ^8.0.0 + checksum: 0eb371c85fdd0b1584fcfdf3dc3c62395761b3c14658be02620c310305a9a7ecf1617a5e6fb30c1d081c5c8aaf177fa133ee225024313afabb7aa6a10f1e3d04 + languageName: node + linkType: hard + +"map-obj@npm:^1.0.0": + version: 1.0.1 + resolution: "map-obj@npm:1.0.1" + checksum: 9949e7baec2a336e63b8d4dc71018c117c3ce6e39d2451ccbfd3b8350c547c4f6af331a4cbe1c83193d7c6b786082b6256bde843db90cb7da2a21e8fcc28afed + languageName: node + linkType: hard + +"map-obj@npm:^4.0.0": + version: 4.3.0 + resolution: "map-obj@npm:4.3.0" + checksum: fbc554934d1a27a1910e842bc87b177b1a556609dd803747c85ece420692380827c6ae94a95cce4407c054fa0964be3bf8226f7f2cb2e9eeee432c7c1985684e + languageName: node + linkType: hard + +"mathjs@npm:^10.4.3": + version: 10.4.3 + resolution: "mathjs@npm:10.4.3" + dependencies: + "@babel/runtime": ^7.17.8 + complex.js: ^2.1.0 + decimal.js: ^10.3.1 + escape-latex: ^1.2.0 + fraction.js: ^4.2.0 + javascript-natural-sort: ^0.7.1 + seedrandom: ^3.0.5 + tiny-emitter: ^2.1.0 + typed-function: ^2.1.0 + bin: + mathjs: bin/cli.js + checksum: ed2343b2ab41c5638d54b04cecfb815069ea6508629b18bd1cc519711560ee43412cae82d17cfb5ed8e55ba2a8f3a90999bdd23457be6703157113de74c285fd + languageName: node + linkType: hard + +"md5.js@npm:^1.3.4": + version: 1.3.5 + resolution: "md5.js@npm:1.3.5" + dependencies: + hash-base: ^3.0.0 + inherits: ^2.0.1 + safe-buffer: ^5.1.2 + checksum: 098494d885684bcc4f92294b18ba61b7bd353c23147fbc4688c75b45cb8590f5a95fd4584d742415dcc52487f7a1ef6ea611cfa1543b0dc4492fe026357f3f0c + languageName: node + linkType: hard + +"media-typer@npm:0.3.0": + version: 0.3.0 + resolution: "media-typer@npm:0.3.0" + checksum: af1b38516c28ec95d6b0826f6c8f276c58aec391f76be42aa07646b4e39d317723e869700933ca6995b056db4b09a78c92d5440dc23657e6764be5d28874bba1 + languageName: node + linkType: hard + +"mem-fs-editor@npm:^8.1.2 || ^9.0.0": + version: 9.4.0 + resolution: "mem-fs-editor@npm:9.4.0" + dependencies: + binaryextensions: ^4.16.0 + commondir: ^1.0.1 + deep-extend: ^0.6.0 + ejs: ^3.1.6 + globby: ^11.0.3 + isbinaryfile: ^4.0.8 + minimatch: ^3.0.4 + multimatch: ^5.0.0 + normalize-path: ^3.0.0 + textextensions: ^5.13.0 + peerDependencies: + mem-fs: ^2.1.0 + peerDependenciesMeta: + mem-fs: + optional: true + checksum: 427b71d59a4bd7032b12ef22e8f69fdf3165ec7cc4350f1a1fca190c4211ba8a01e14a04d9a7db8cb44f74034de2bc6e42f0d5ffb5b20590e71646baac5a8a0c + languageName: node + linkType: hard + +"mem-fs@npm:^1.2.0 || ^2.0.0": + version: 2.2.1 + resolution: "mem-fs@npm:2.2.1" + dependencies: + "@types/node": ^15.6.1 + "@types/vinyl": ^2.0.4 + vinyl: ^2.0.1 + vinyl-file: ^3.0.0 + checksum: e44fb4acf8391a847b9e9494115b27300eda77aa7c6caea533786f43d385253515b0c0ff4f00906744b2bd31010df923cc448bb6efb9593f41df5d40b0e69046 + languageName: node + linkType: hard + +"memdown@npm:^5.1.0": + version: 5.1.0 + resolution: "memdown@npm:5.1.0" + dependencies: + abstract-leveldown: ~6.2.1 + functional-red-black-tree: ~1.0.1 + immediate: ~3.2.3 + inherits: ~2.0.1 + ltgt: ~2.2.0 + safe-buffer: ~5.2.0 + checksum: 23e4414034e975eae1edd6864874bbe77501d41814fc27e8ead946c3379cb1cbea303d724083d08a6a269af9bf5d55073f1f767dfa7ad6e70465769f87e29794 + languageName: node + linkType: hard + +"memory-fs@npm:^0.5.0": + version: 0.5.0 + resolution: "memory-fs@npm:0.5.0" + dependencies: + errno: ^0.1.3 + readable-stream: ^2.0.1 + checksum: a9f25b0a8ecfb7324277393f19ef68e6ba53b9e6e4b526bbf2ba23055c5440fbf61acc7bf66bfd980e9eb4951a4790f6f777a9a3abd36603f22c87e8a64d3d6b + languageName: node + linkType: hard + +"memory-pager@npm:^1.0.2": + version: 1.5.0 + resolution: "memory-pager@npm:1.5.0" + checksum: d1a2e684583ef55c61cd3a49101da645b11ad57014dfc565e0b43baa9004b743f7e4ab81493d8fff2ab24e9950987cc3209c94bcc4fc8d7e30a475489a1f15e9 + languageName: node + linkType: hard + +"memory-streams@npm:^0.1.3": + version: 0.1.3 + resolution: "memory-streams@npm:0.1.3" + dependencies: + readable-stream: ~1.0.2 + checksum: aebb6dc54c35ff8e7fcbbffc736ae95938d9bb7ed66735b693ed18743fcc6268f64eaa80b2580a49c3c73ddc00cd880d5847f318997affe6d2a46b75365715f8 + languageName: node + linkType: hard + +"meow@npm:^8.0.0": + version: 8.1.2 + resolution: "meow@npm:8.1.2" + dependencies: + "@types/minimist": ^1.2.0 + camelcase-keys: ^6.2.2 + decamelize-keys: ^1.1.0 + hard-rejection: ^2.1.0 + minimist-options: 4.1.0 + normalize-package-data: ^3.0.0 + read-pkg-up: ^7.0.1 + redent: ^3.0.0 + trim-newlines: ^3.0.0 + type-fest: ^0.18.0 + yargs-parser: ^20.2.3 + checksum: bc23bf1b4423ef6a821dff9734406bce4b91ea257e7f10a8b7f896f45b59649f07adc0926e2917eacd8cf1df9e4cd89c77623cf63dfd0f8bf54de07a32ee5a85 + languageName: node + linkType: hard + +"merge-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "merge-stream@npm:2.0.0" + checksum: 6fa4dcc8d86629705cea944a4b88ef4cb0e07656ebf223fa287443256414283dd25d91c1cd84c77987f2aec5927af1a9db6085757cb43d90eb170ebf4b47f4f4 + languageName: node + linkType: hard + +"merge2@npm:^1.2.3, merge2@npm:^1.3.0, merge2@npm:^1.4.1": + version: 1.4.1 + resolution: "merge2@npm:1.4.1" + checksum: 7268db63ed5169466540b6fb947aec313200bcf6d40c5ab722c22e242f651994619bcd85601602972d3c85bd2cc45a358a4c61937e9f11a061919a1da569b0c2 + languageName: node + linkType: hard + +"micro-memoize@npm:^4.0.9": + version: 4.0.9 + resolution: "micro-memoize@npm:4.0.9" + checksum: c755539864caaa232b948e1b351f371df91ceaa78e16b18abe64dd488de93dde253456c3a6c4f93b48e05aa3e2e012891f7c21e5ba876d7850f4e4ea33a0f944 + languageName: node + linkType: hard + +"micromatch@npm:^4.0.0, micromatch@npm:^4.0.2, micromatch@npm:^4.0.4": + version: 4.0.4 + resolution: "micromatch@npm:4.0.4" + dependencies: + braces: ^3.0.1 + picomatch: ^2.2.3 + checksum: ef3d1c88e79e0a68b0e94a03137676f3324ac18a908c245a9e5936f838079fcc108ac7170a5fadc265a9c2596963462e402841406bda1a4bb7b68805601d631c + languageName: node + linkType: hard + +"miller-rabin@npm:^4.0.0": + version: 4.0.1 + resolution: "miller-rabin@npm:4.0.1" + dependencies: + bn.js: ^4.0.0 + brorand: ^1.0.1 + bin: + miller-rabin: bin/miller-rabin + checksum: 00cd1ab838ac49b03f236cc32a14d29d7d28637a53096bf5c6246a032a37749c9bd9ce7360cbf55b41b89b7d649824949ff12bc8eee29ac77c6b38eada619ece + languageName: node + linkType: hard + +"mime-db@npm:1.51.0": + version: 1.51.0 + resolution: "mime-db@npm:1.51.0" + checksum: 613b1ac9d6e725cc24444600b124a7f1ce6c60b1baa654f39a3e260d0995a6dffc5693190217e271af7e2a5612dae19f2a73f3e316707d797a7391165f7ef423 + languageName: node + linkType: hard + +"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24": + version: 2.1.34 + resolution: "mime-types@npm:2.1.34" + dependencies: + mime-db: 1.51.0 + checksum: 67013de9e9d6799bde6d669d18785b7e18bcd212e710d3e04a4727f92f67a8ad4e74aee24be28b685adb794944814bde649119b58ee3282ffdbee58f9278d9f3 + languageName: node + linkType: hard + +"mime@npm:^2.5.2": + version: 2.6.0 + resolution: "mime@npm:2.6.0" + bin: + mime: cli.js + checksum: 1497ba7b9f6960694268a557eae24b743fd2923da46ec392b042469f4b901721ba0adcf8b0d3c2677839d0e243b209d76e5edcbd09cfdeffa2dfb6bb4df4b862 + languageName: node + linkType: hard + +"mimic-fn@npm:^2.1.0": + version: 2.1.0 + resolution: "mimic-fn@npm:2.1.0" + checksum: d2421a3444848ce7f84bd49115ddacff29c15745db73f54041edc906c14b131a38d05298dae3081667627a59b2eb1ca4b436ff2e1b80f69679522410418b478a + languageName: node + linkType: hard + +"mimic-response@npm:^1.0.0, mimic-response@npm:^1.0.1": + version: 1.0.1 + resolution: "mimic-response@npm:1.0.1" + checksum: 034c78753b0e622bc03c983663b1cdf66d03861050e0c8606563d149bc2b02d63f62ce4d32be4ab50d0553ae0ffe647fc34d1f5281184c6e1e8cf4d85e8d9823 + languageName: node + linkType: hard + +"min-indent@npm:^1.0.0": + version: 1.0.1 + resolution: "min-indent@npm:1.0.1" + checksum: bfc6dd03c5eaf623a4963ebd94d087f6f4bbbfd8c41329a7f09706b0cb66969c4ddd336abeb587bc44bc6f08e13bf90f0b374f9d71f9f01e04adc2cd6f083ef1 + languageName: node + linkType: hard + +"minimalistic-assert@npm:^1.0.0, minimalistic-assert@npm:^1.0.1": + version: 1.0.1 + resolution: "minimalistic-assert@npm:1.0.1" + checksum: cc7974a9268fbf130fb055aff76700d7e2d8be5f761fb5c60318d0ed010d839ab3661a533ad29a5d37653133385204c503bfac995aaa4236f4e847461ea32ba7 + languageName: node + linkType: hard + +"minimalistic-crypto-utils@npm:^1.0.0, minimalistic-crypto-utils@npm:^1.0.1": + version: 1.0.1 + resolution: "minimalistic-crypto-utils@npm:1.0.1" + checksum: 6e8a0422b30039406efd4c440829ea8f988845db02a3299f372fceba56ffa94994a9c0f2fd70c17f9969eedfbd72f34b5070ead9656a34d3f71c0bd72583a0ed + languageName: node + linkType: hard + +"minimatch@npm:3.0.4, minimatch@npm:^3.0.4": + version: 3.0.4 + resolution: "minimatch@npm:3.0.4" + dependencies: + brace-expansion: ^1.1.7 + checksum: 66ac295f8a7b59788000ea3749938b0970344c841750abd96694f80269b926ebcafad3deeb3f1da2522978b119e6ae3a5869b63b13a7859a456b3408bd18a078 + languageName: node + linkType: hard + +"minimatch@npm:^5.0.0": + version: 5.0.0 + resolution: "minimatch@npm:5.0.0" + dependencies: + brace-expansion: ^2.0.1 + checksum: 810d4165fa2b16d0ffe8eb7586b8b7b7122ab77efa8f351686ffd1e8cbe63997ae9d5e7404fe611a676e59e1a47f7f5e8d9a004e0fe5bb6d5ac35457469116a4 + languageName: node + linkType: hard + +"minimist-options@npm:4.1.0": + version: 4.1.0 + resolution: "minimist-options@npm:4.1.0" + dependencies: + arrify: ^1.0.1 + is-plain-obj: ^1.1.0 + kind-of: ^6.0.3 + checksum: 8c040b3068811e79de1140ca2b708d3e203c8003eb9a414c1ab3cd467fc5f17c9ca02a5aef23bedc51a7f8bfbe77f87e9a7e31ec81fba304cda675b019496f4e + languageName: node + linkType: hard + +"minimist@npm:^1.1.0, minimist@npm:^1.1.1, minimist@npm:^1.2.0, minimist@npm:^1.2.3, minimist@npm:^1.2.5": + version: 1.2.5 + resolution: "minimist@npm:1.2.5" + checksum: 86706ce5b36c16bfc35c5fe3dbb01d5acdc9a22f2b6cc810b6680656a1d2c0e44a0159c9a3ba51fb072bb5c203e49e10b51dcd0eec39c481f4c42086719bae52 + languageName: node + linkType: hard + +"minipass-collect@npm:^1.0.2": + version: 1.0.2 + resolution: "minipass-collect@npm:1.0.2" + dependencies: + minipass: ^3.0.0 + checksum: 14df761028f3e47293aee72888f2657695ec66bd7d09cae7ad558da30415fdc4752bbfee66287dcc6fd5e6a2fa3466d6c484dc1cbd986525d9393b9523d97f10 + languageName: node + linkType: hard + +"minipass-fetch@npm:^1.3.2, minipass-fetch@npm:^1.4.1": + version: 1.4.1 + resolution: "minipass-fetch@npm:1.4.1" + dependencies: + encoding: ^0.1.12 + minipass: ^3.1.0 + minipass-sized: ^1.0.3 + minizlib: ^2.0.0 + dependenciesMeta: + encoding: + optional: true + checksum: ec93697bdb62129c4e6c0104138e681e30efef8c15d9429dd172f776f83898471bc76521b539ff913248cc2aa6d2b37b652c993504a51cc53282563640f29216 + languageName: node + linkType: hard + +"minipass-flush@npm:^1.0.5": + version: 1.0.5 + resolution: "minipass-flush@npm:1.0.5" + dependencies: + minipass: ^3.0.0 + checksum: 56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf + languageName: node + linkType: hard + +"minipass-json-stream@npm:^1.0.1": + version: 1.0.1 + resolution: "minipass-json-stream@npm:1.0.1" + dependencies: + jsonparse: ^1.3.1 + minipass: ^3.0.0 + checksum: 791b696a27d1074c4c08dab1bf5a9f3201145c2933e428f45d880467bce12c60de4703203d2928de4b162d0ae77b0bb4b55f96cb846645800aa0eb4919b3e796 + languageName: node + linkType: hard + +"minipass-pipeline@npm:^1.2.2, minipass-pipeline@npm:^1.2.4": + version: 1.2.4 + resolution: "minipass-pipeline@npm:1.2.4" + dependencies: + minipass: ^3.0.0 + checksum: b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b + languageName: node + linkType: hard + +"minipass-sized@npm:^1.0.3": + version: 1.0.3 + resolution: "minipass-sized@npm:1.0.3" + dependencies: + minipass: ^3.0.0 + checksum: 79076749fcacf21b5d16dd596d32c3b6bf4d6e62abb43868fac21674078505c8b15eaca4e47ed844985a4514854f917d78f588fcd029693709417d8f98b2bd60 + languageName: node + linkType: hard + +"minipass@npm:^3.0.0, minipass@npm:^3.1.0, minipass@npm:^3.1.1, minipass@npm:^3.1.3, minipass@npm:^3.1.6": + version: 3.1.6 + resolution: "minipass@npm:3.1.6" + dependencies: + yallist: ^4.0.0 + checksum: 57a04041413a3531a65062452cb5175f93383ef245d6f4a2961d34386eb9aa8ac11ac7f16f791f5e8bbaf1dfb1ef01596870c88e8822215db57aa591a5bb0a77 + languageName: node + linkType: hard + +"minizlib@npm:^2.0.0, minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": + version: 2.1.2 + resolution: "minizlib@npm:2.1.2" + dependencies: + minipass: ^3.0.0 + yallist: ^4.0.0 + checksum: f1fdeac0b07cf8f30fcf12f4b586795b97be856edea22b5e9072707be51fc95d41487faec3f265b42973a304fe3a64acd91a44a3826a963e37b37bafde0212c3 + languageName: node + linkType: hard + +"mkdirp-classic@npm:^0.5.2": + version: 0.5.3 + resolution: "mkdirp-classic@npm:0.5.3" + checksum: 3f4e088208270bbcc148d53b73e9a5bd9eef05ad2cbf3b3d0ff8795278d50dd1d11a8ef1875ff5aea3fa888931f95bfcb2ad5b7c1061cfefd6284d199e6776ac + languageName: node + linkType: hard + +"mkdirp-infer-owner@npm:^2.0.0": + version: 2.0.0 + resolution: "mkdirp-infer-owner@npm:2.0.0" + dependencies: + chownr: ^2.0.0 + infer-owner: ^1.0.4 + mkdirp: ^1.0.3 + checksum: d8f4ecd32f6762459d6b5714eae6487c67ae9734ab14e26d14377ddd9b2a1bf868d8baa18c0f3e73d3d513f53ec7a698e0f81a9367102c870a55bef7833880f7 + languageName: node + linkType: hard + +"mkdirp@npm:^0.5.1": + version: 0.5.5 + resolution: "mkdirp@npm:0.5.5" + dependencies: + minimist: ^1.2.5 + bin: + mkdirp: bin/cmd.js + checksum: 3bce20ea525f9477befe458ab85284b0b66c8dc3812f94155af07c827175948cdd8114852ac6c6d82009b13c1048c37f6d98743eb019651ee25c39acc8aabe7d + languageName: node + linkType: hard + +"mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": + version: 1.0.4 + resolution: "mkdirp@npm:1.0.4" + bin: + mkdirp: bin/cmd.js + checksum: a96865108c6c3b1b8e1d5e9f11843de1e077e57737602de1b82030815f311be11f96f09cce59bd5b903d0b29834733e5313f9301e3ed6d6f6fba2eae0df4298f + languageName: node + linkType: hard + +"mocha-sinon@npm:^2.1.2": + version: 2.1.2 + resolution: "mocha-sinon@npm:2.1.2" + peerDependencies: + mocha: "*" + sinon: "*" + checksum: 605cfdd9af15979187ce75c2b38608289166c7cbb3fcc1b03d3813301a0d1e9a81909f7decb7cdb82c010d9f27213f1b842679848bb6c1f42fa9f23600ca7a1a + languageName: node + linkType: hard + +"mocha@npm:^9.1.2": + version: 9.1.3 + resolution: "mocha@npm:9.1.3" + dependencies: + "@ungap/promise-all-settled": 1.1.2 + ansi-colors: 4.1.1 + browser-stdout: 1.3.1 + chokidar: 3.5.2 + debug: 4.3.2 + diff: 5.0.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 7.1.7 + growl: 1.10.5 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 3.0.4 + ms: 2.1.3 + nanoid: 3.1.25 + serialize-javascript: 6.0.0 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + which: 2.0.2 + workerpool: 6.1.5 + yargs: 16.2.0 + yargs-parser: 20.2.4 + yargs-unparser: 2.0.0 + bin: + _mocha: bin/_mocha + mocha: bin/mocha + checksum: 4185038f1d49c7c5ab2f8d77e42c182a77ed78f08f0ce713cc34919bc89b618ed9d6d4f24cbb92049f0d2ed03fb1a2a5b20e0fb07f8cf4a86ba83eb99dacde99 + languageName: node + linkType: hard + +"modify-values@npm:^1.0.0": + version: 1.0.1 + resolution: "modify-values@npm:1.0.1" + checksum: 8296610c608bc97b03c2cf889c6cdf4517e32fa2d836440096374c2209f6b7b3e256c209493a0b32584b9cb32d528e99d0dd19dcd9a14d2d915a312d391cc7e9 + languageName: node + linkType: hard + +"module-deps@npm:^6.2.3": + version: 6.2.3 + resolution: "module-deps@npm:6.2.3" + dependencies: + JSONStream: ^1.0.3 + browser-resolve: ^2.0.0 + cached-path-relative: ^1.0.2 + concat-stream: ~1.6.0 + defined: ^1.0.0 + detective: ^5.2.0 + duplexer2: ^0.1.2 + inherits: ^2.0.1 + parents: ^1.0.0 + readable-stream: ^2.0.2 + resolve: ^1.4.0 + stream-combiner2: ^1.1.1 + subarg: ^1.0.0 + through2: ^2.0.0 + xtend: ^4.0.0 + bin: + module-deps: bin/cmd.js + checksum: cccead8f81b77ec621c29c4407978ce50de6f15c7152b54e81b65ff043d4254fd40071e53a3989a36066ff0d3ce9ae9e65f81aed79b3b5397024dbc8be5d68c7 + languageName: node + linkType: hard + +"mongodb@npm:^3.3.4": + version: 3.7.3 + resolution: "mongodb@npm:3.7.3" + dependencies: + bl: ^2.2.1 + bson: ^1.1.4 + denque: ^1.4.1 + optional-require: ^1.1.8 + safe-buffer: ^5.1.2 + saslprep: ^1.0.0 + dependenciesMeta: + saslprep: + optional: true + peerDependenciesMeta: + aws4: + optional: true + bson-ext: + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + mongodb-extjson: + optional: true + snappy: + optional: true + checksum: ef7690fe6ee7d1752f121b14e59b3fabfddc60ff0536babce6c945703ad0010de9e6fa7de4c91b99275c256876a72a06899ce27893aba0838c2b542088bd1044 + languageName: node + linkType: hard + +"mri@npm:1.1.4": + version: 1.1.4 + resolution: "mri@npm:1.1.4" + checksum: e65b9aed3b9e423ad4c11f529ab1b9280f65dce8fb476d0da236b5c570ad3322fbbcd2393180855f1474f8b0f982d76ad398766fbd47b8a5ab4069e325d0268e + languageName: node + linkType: hard + +"ms@npm:2.0.0": + version: 2.0.0 + resolution: "ms@npm:2.0.0" + checksum: 0e6a22b8b746d2e0b65a430519934fefd41b6db0682e3477c10f60c76e947c4c0ad06f63ffdf1d78d335f83edee8c0aa928aa66a36c7cd95b69b26f468d527f4 + languageName: node + linkType: hard + +"ms@npm:2.1.2": + version: 2.1.2 + resolution: "ms@npm:2.1.2" + checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f + languageName: node + linkType: hard + +"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d + languageName: node + linkType: hard + +"multimatch@npm:^5.0.0": + version: 5.0.0 + resolution: "multimatch@npm:5.0.0" + dependencies: + "@types/minimatch": ^3.0.3 + array-differ: ^3.0.0 + array-union: ^2.1.0 + arrify: ^2.0.1 + minimatch: ^3.0.4 + checksum: 82c8030a53af965cab48da22f1b0f894ef99e16ee680dabdfbd38d2dfacc3c8208c475203d747afd9e26db44118ed0221d5a0d65268c864f06d6efc7ac6df812 + languageName: node + linkType: hard + +"mute-stream@npm:0.0.8": + version: 0.0.8 + resolution: "mute-stream@npm:0.0.8" + checksum: ff48d251fc3f827e5b1206cda0ffdaec885e56057ee86a3155e1951bc940fd5f33531774b1cc8414d7668c10a8907f863f6561875ee6e8768931a62121a531a1 + languageName: node + linkType: hard + +"nan@npm:2.14.2": + version: 2.14.2 + resolution: "nan@npm:2.14.2" + dependencies: + node-gyp: latest + checksum: 7a269139b66a7d37470effb7fb36a8de8cc3b5ffba6e40bb8e0545307911fe5ebf94797ec62f655ecde79c237d169899f8bd28256c66a32cbc8284faaf94c3f4 + languageName: node + linkType: hard + +"nan@npm:^2.14.1, nan@npm:^2.15.0": + version: 2.15.0 + resolution: "nan@npm:2.15.0" + dependencies: + node-gyp: latest + checksum: 33e1bb4dfca447fe37d4bb5889be55de154828632c8d38646db67293a21afd61ed9909cdf1b886214a64707d935926c4e60e2b09de9edfc2ad58de31d6ce8f39 + languageName: node + linkType: hard + +"nanoid@npm:3.1.25": + version: 3.1.25 + resolution: "nanoid@npm:3.1.25" + bin: + nanoid: bin/nanoid.cjs + checksum: e2353828c7d8fde65265e9c981380102e2021f292038a93fd27288bad390339833286e8cbc7531abe1cb2c6b317e55f38b895dcb775151637bb487388558e0ff + languageName: node + linkType: hard + +"natural-compare@npm:^1.4.0": + version: 1.4.0 + resolution: "natural-compare@npm:1.4.0" + checksum: 23ad088b08f898fc9b53011d7bb78ec48e79de7627e01ab5518e806033861bef68d5b0cd0e2205c2f36690ac9571ff6bcb05eb777ced2eeda8d4ac5b44592c3d + languageName: node + linkType: hard + +"natural-orderby@npm:^2.0.3": + version: 2.0.3 + resolution: "natural-orderby@npm:2.0.3" + checksum: 039be7f0b6cf81e63d2ae5299553f8e6c8f6ae4f571c7c002eab9c6d36a2e33101704e0ec64c3cecef956fa3b1a68bb0ddfc03208e89f31c0b0bb806f3198646 + languageName: node + linkType: hard + +"negotiator@npm:0.6.2": + version: 0.6.2 + resolution: "negotiator@npm:0.6.2" + checksum: dfddaff6c06792f1c4c3809e29a427b8daef8cd437c83b08dd51d7ee11bbd1c29d9512d66b801144d6c98e910ffd8723f2432e0cbf8b18d41d2a09599c975ab3 + languageName: node + linkType: hard + +"negotiator@npm:^0.6.2, negotiator@npm:^0.6.3": + version: 0.6.3 + resolution: "negotiator@npm:0.6.3" + checksum: b8ffeb1e262eff7968fc90a2b6767b04cfd9842582a9d0ece0af7049537266e7b2506dfb1d107a32f06dd849ab2aea834d5830f7f4d0e5cb7d36e1ae55d021d9 + languageName: node + linkType: hard + +"neo-async@npm:^2.6.0, neo-async@npm:^2.6.2": + version: 2.6.2 + resolution: "neo-async@npm:2.6.2" + checksum: deac9f8d00eda7b2e5cd1b2549e26e10a0faa70adaa6fdadca701cc55f49ee9018e427f424bac0c790b7c7e2d3068db97f3093f1093975f2acb8f8818b936ed9 + languageName: node + linkType: hard + +"neon-load-or-build@npm:^2.2.2": + version: 2.2.2 + resolution: "neon-load-or-build@npm:2.2.2" + bin: + neon-load-or-build: ./bin.js + neon-load-or-build-optional: ./optional.js + neon-load-or-build-test: ./build-test.js + checksum: 3cafba0e26ad2d343c9c0bcbaaeef34fd646941da401a02bb7307a99095774c4bbbe4d2e168641e50f7228cb933ac9c5e68db390ec7e52dc9d01be99b5a90258 + languageName: node + linkType: hard + +"neon-tag-prebuild@github:shumkov/neon-tag-prebuild#patch-1": + version: 1.1.0 + resolution: "neon-tag-prebuild@https://github.com/shumkov/neon-tag-prebuild.git#commit=a429834da27432b129eceb737e4d2b3f03fa5496" + dependencies: + mkdirp: ^1.0.4 + node-abi: ^2.19.1 + bin: + neon-tag-prebuild: ./bin.js + checksum: 00a16bf27c06e0fa021be8019942490b4dc4f9cfd09b13eda042fdbc1f3873b7d74438f3b0d327651f77b93d087dd6ca5b9497bfc73457d12870f15cff0f0066 + languageName: node + linkType: hard + +"net@npm:^1.0.2": + version: 1.0.2 + resolution: "net@npm:1.0.2" + checksum: d97e215d922e87e9aa86e87daae73ce7f4e291f2d71365a115f2f51831b1add86e73584e944285daafcad4058c09bb85e33250da84834a26a83020740d45b4a1 + languageName: node + linkType: hard + +"nice-try@npm:^1.0.4": + version: 1.0.5 + resolution: "nice-try@npm:1.0.5" + checksum: 0b4af3b5bb5d86c289f7a026303d192a7eb4417231fe47245c460baeabae7277bcd8fd9c728fb6bd62c30b3e15cd6620373e2cf33353b095d8b403d3e8a15aff + languageName: node + linkType: hard + +"nise@npm:^5.1.0": + version: 5.1.0 + resolution: "nise@npm:5.1.0" + dependencies: + "@sinonjs/commons": ^1.7.0 + "@sinonjs/fake-timers": ^7.0.4 + "@sinonjs/text-encoding": ^0.7.1 + just-extend: ^4.0.2 + path-to-regexp: ^1.7.0 + checksum: e3843cc125163ce99b7fb0328edf427b981be32c6c719684582cf0a46fb5206173835a9a14dedac3c4833e415ab0e0493f9f4d4163572a3a0c95db39b093166d + languageName: node + linkType: hard + +"no-case@npm:^3.0.4": + version: 3.0.4 + resolution: "no-case@npm:3.0.4" + dependencies: + lower-case: ^2.0.2 + tslib: ^2.0.3 + checksum: 0b2ebc113dfcf737d48dde49cfebf3ad2d82a8c3188e7100c6f375e30eafbef9e9124aadc3becef237b042fd5eb0aad2fd78669c20972d045bbe7fea8ba0be5c + languageName: node + linkType: hard + +"node-abi@npm:^2.19.1": + version: 2.30.1 + resolution: "node-abi@npm:2.30.1" + dependencies: + semver: ^5.4.1 + checksum: 3f4b0c912ce4befcd7ceab4493ba90b51d60dfcc90f567c93f731d897ef8691add601cb64c181683b800f21d479d68f9a6e15d8ab8acd16a5706333b9e30a881 + languageName: node + linkType: hard + +"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7": + version: 2.6.7 + resolution: "node-fetch@npm:2.6.7" + dependencies: + whatwg-url: ^5.0.0 + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: 8d816ffd1ee22cab8301c7756ef04f3437f18dace86a1dae22cf81db8ef29c0bf6655f3215cb0cdb22b420b6fe141e64b26905e7f33f9377a7fa59135ea3e10b + languageName: node + linkType: hard + +"node-graceful@npm:^3.0.1": + version: 3.1.0 + resolution: "node-graceful@npm:3.1.0" + checksum: 159d06ca29e09b3d97fc3ce0976a53a6a259b965c23ca90be0ff4fcc92b922ad2d5e62909d1f575c5480923471561060952dcd3ae94809f1b29812e5c2a33ecc + languageName: node + linkType: hard + +"node-gyp-build@npm:^4.2.3, node-gyp-build@npm:^4.3.0": + version: 4.3.0 + resolution: "node-gyp-build@npm:4.3.0" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 1ecab16d9f275174d516e223f60f65ebe07540347d5c04a6a7d6921060b7f2e3af4f19463d9d1dcedc452e275c2ae71354a99405e55ebd5b655bb2f38025c728 + languageName: node + linkType: hard + +"node-gyp@npm:^8.2.0": + version: 8.4.1 + resolution: "node-gyp@npm:8.4.1" + dependencies: + env-paths: ^2.2.0 + glob: ^7.1.4 + graceful-fs: ^4.2.6 + make-fetch-happen: ^9.1.0 + nopt: ^5.0.0 + npmlog: ^6.0.0 + rimraf: ^3.0.2 + semver: ^7.3.5 + tar: ^6.1.2 + which: ^2.0.2 + bin: + node-gyp: bin/node-gyp.js + checksum: 341710b5da39d3660e6a886b37e210d33f8282047405c2e62c277bcc744c7552c5b8b972ebc3a7d5c2813794e60cc48c3ebd142c46d6e0321db4db6c92dd0355 + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 8.4.0 + resolution: "node-gyp@npm:8.4.0" + dependencies: + env-paths: ^2.2.0 + glob: ^7.1.4 + graceful-fs: ^4.2.6 + make-fetch-happen: ^9.1.0 + nopt: ^5.0.0 + npmlog: ^4.1.2 + rimraf: ^3.0.2 + semver: ^7.3.5 + tar: ^6.1.2 + which: ^2.0.2 + bin: + node-gyp: bin/node-gyp.js + checksum: a5a0045f6a1708a7760cfee2b5e2cd9072dd6a0d5d3376bb96e0bae1f1e43d14a0bd54970e1fbd2632cceb9c23d36a3efabe88c26256693e969566cf977501c2 + languageName: node + linkType: hard + +"node-inspect-extracted@npm:^1.0.8": + version: 1.0.8 + resolution: "node-inspect-extracted@npm:1.0.8" + checksum: 41ecca97d3d1931066c31c97679a89a7c67d0eb3246eb8895f8861fd49c7607dfd382c1dc14ea774951d32abe281d68b027d0ca200adf8761049f3ee7a40c854 + languageName: node + linkType: hard + +"node-preload@npm:^0.2.1": + version: 0.2.1 + resolution: "node-preload@npm:0.2.1" + dependencies: + process-on-spawn: ^1.0.0 + checksum: 4586f91ac7417b33accce0ac629fb60f642d0c8d212b3c536dc3dda37fe54f8a3b858273380e1036e41a65d85470332c358315d2288e6584260d620fb4b00fb3 + languageName: node + linkType: hard + +"node-releases@npm:^2.0.1": + version: 2.0.1 + resolution: "node-releases@npm:2.0.1" + checksum: b20dd8d4bced11f75060f0387e05e76b9dc4a0451f7bb3516eade6f50499ea7768ba95d8a60d520c193402df1e58cb3fe301510cc1c1ad68949c3d57b5149866 + languageName: node + linkType: hard + +"nodeforage@npm:^1.1.2": + version: 1.1.2 + resolution: "nodeforage@npm:1.1.2" + dependencies: + lodash.find: ^4.6.0 + lodash.ismatch: ^4.4.0 + lodash.merge: ^4.6.1 + proper-lockfile: ^3.2.0 + slocket: ^1.0.5 + checksum: a670ece8b5e486514bb859d3723d8998806a071f200315fc7457755617d37515ceba8c13b46d9aa644b3517271b561dc50b57a9325a5537cbb34bccdd418ded5 + languageName: node + linkType: hard + +"nodemon@npm:^2.0.4": + version: 2.0.15 + resolution: "nodemon@npm:2.0.15" + dependencies: + chokidar: ^3.5.2 + debug: ^3.2.7 + ignore-by-default: ^1.0.1 + minimatch: ^3.0.4 + pstree.remy: ^1.1.8 + semver: ^5.7.1 + supports-color: ^5.5.0 + touch: ^3.1.0 + undefsafe: ^2.0.5 + update-notifier: ^5.1.0 + bin: + nodemon: bin/nodemon.js + checksum: 0569b09b713fdcc76f06734d7cc106950e69e02069cbf44bda3fae8d266926bdfa003aeddd22f8fcdf46ea6ff51ca64f5528f8006536e79820a26e648ef346cf + languageName: node + linkType: hard + +"nofilter@npm:^3.1.0": + version: 3.1.0 + resolution: "nofilter@npm:3.1.0" + checksum: 58aa85a5b4b35cbb6e42de8a8591c5e338061edc9f3e7286f2c335e9e9b9b8fa7c335ae45daa8a1f3433164dc0b9a3d187fa96f9516e04a17a1f9ce722becc4f + languageName: node + linkType: hard + +"nopt@npm:^5.0.0": + version: 5.0.0 + resolution: "nopt@npm:5.0.0" + dependencies: + abbrev: 1 + bin: + nopt: bin/nopt.js + checksum: d35fdec187269503843924e0114c0c6533fb54bbf1620d0f28b4b60ba01712d6687f62565c55cc20a504eff0fbe5c63e22340c3fad549ad40469ffb611b04f2f + languageName: node + linkType: hard + +"nopt@npm:~1.0.10": + version: 1.0.10 + resolution: "nopt@npm:1.0.10" + dependencies: + abbrev: 1 + bin: + nopt: ./bin/nopt.js + checksum: f62575aceaa3be43f365bf37a596b89bbac2e796b001b6d2e2a85c2140a4e378ff919e2753ccba959c4fd344776fc88c29b393bc167fa939fb1513f126f4cd45 + languageName: node + linkType: hard + +"normalize-package-data@npm:^2.3.2, normalize-package-data@npm:^2.5.0": + version: 2.5.0 + resolution: "normalize-package-data@npm:2.5.0" + dependencies: + hosted-git-info: ^2.1.4 + resolve: ^1.10.0 + semver: 2 || 3 || 4 || 5 + validate-npm-package-license: ^3.0.1 + checksum: 7999112efc35a6259bc22db460540cae06564aa65d0271e3bdfa86876d08b0e578b7b5b0028ee61b23f1cae9fc0e7847e4edc0948d3068a39a2a82853efc8499 + languageName: node + linkType: hard + +"normalize-package-data@npm:^3.0.0, normalize-package-data@npm:^3.0.3": + version: 3.0.3 + resolution: "normalize-package-data@npm:3.0.3" + dependencies: + hosted-git-info: ^4.0.1 + is-core-module: ^2.5.0 + semver: ^7.3.4 + validate-npm-package-license: ^3.0.1 + checksum: bbcee00339e7c26fdbc760f9b66d429258e2ceca41a5df41f5df06cc7652de8d82e8679ff188ca095cad8eff2b6118d7d866af2b68400f74602fbcbce39c160a + languageName: node + linkType: hard + +"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": + version: 3.0.0 + resolution: "normalize-path@npm:3.0.0" + checksum: 88eeb4da891e10b1318c4b2476b6e2ecbeb5ff97d946815ffea7794c31a89017c70d7f34b3c2ebf23ef4e9fc9fb99f7dffe36da22011b5b5c6ffa34f4873ec20 + languageName: node + linkType: hard + +"normalize-url@npm:^4.1.0": + version: 4.5.1 + resolution: "normalize-url@npm:4.5.1" + checksum: 9a9dee01df02ad23e171171893e56e22d752f7cff86fb96aafeae074819b572ea655b60f8302e2d85dbb834dc885c972cc1c573892fea24df46b2765065dd05a + languageName: node + linkType: hard + +"npm-bundled@npm:^1.1.1": + version: 1.1.2 + resolution: "npm-bundled@npm:1.1.2" + dependencies: + npm-normalize-package-bin: ^1.0.1 + checksum: 6e599155ef28d0b498622f47f1ba189dfbae05095a1ed17cb3a5babf961e965dd5eab621f0ec6f0a98de774e5836b8f5a5ee639010d64f42850a74acec3d4d09 + languageName: node + linkType: hard + +"npm-install-checks@npm:^4.0.0": + version: 4.0.0 + resolution: "npm-install-checks@npm:4.0.0" + dependencies: + semver: ^7.1.1 + checksum: 8308ff48e61e0863d7f148f62543e1f6c832525a7d8002ea742d5e478efa8b29bf65a87f9fb82786e15232e4b3d0362b126c45afdceed4c051c0d3c227dd0ace + languageName: node + linkType: hard + +"npm-normalize-package-bin@npm:^1.0.0, npm-normalize-package-bin@npm:^1.0.1": + version: 1.0.1 + resolution: "npm-normalize-package-bin@npm:1.0.1" + checksum: ae7f15155a1e3ace2653f12ddd1ee8eaa3c84452fdfbf2f1943e1de264e4b079c86645e2c55931a51a0a498cba31f70022a5219d5665fbcb221e99e58bc70122 + languageName: node + linkType: hard + +"npm-package-arg@npm:^8.0.1, npm-package-arg@npm:^8.1.2, npm-package-arg@npm:^8.1.5": + version: 8.1.5 + resolution: "npm-package-arg@npm:8.1.5" + dependencies: + hosted-git-info: ^4.0.1 + semver: ^7.3.4 + validate-npm-package-name: ^3.0.0 + checksum: ae76afbcebb4ea8d0b849b8b18ed1b0491030fb04a0af5d75f1b8390cc50bec186ced9fbe60f47d939eab630c7c0db0919d879ac56a87d3782267dfe8eec60d3 + languageName: node + linkType: hard + +"npm-packlist@npm:^3.0.0": + version: 3.0.0 + resolution: "npm-packlist@npm:3.0.0" + dependencies: + glob: ^7.1.6 + ignore-walk: ^4.0.1 + npm-bundled: ^1.1.1 + npm-normalize-package-bin: ^1.0.1 + bin: + npm-packlist: bin/index.js + checksum: 8550ecdec5feb2708aa8289e71c3e9ed72dd792642dd3d2c871955504c0e460bc1c2106483a164eb405b3cdfcfddf311315d4a647fca1a511f710654c015a91e + languageName: node + linkType: hard + +"npm-pick-manifest@npm:^6.0.0, npm-pick-manifest@npm:^6.1.0, npm-pick-manifest@npm:^6.1.1": + version: 6.1.1 + resolution: "npm-pick-manifest@npm:6.1.1" + dependencies: + npm-install-checks: ^4.0.0 + npm-normalize-package-bin: ^1.0.1 + npm-package-arg: ^8.1.2 + semver: ^7.3.4 + checksum: 7a7b9475ae95cf903d37471229efbd12a829a9a7a1020ba36e75768aaa35da4c3a087fde3f06070baf81ec6b2ea2b660f022a1172644e6e7188199d7c1d2954b + languageName: node + linkType: hard + +"npm-registry-fetch@npm:^12.0.0, npm-registry-fetch@npm:^12.0.1": + version: 12.0.2 + resolution: "npm-registry-fetch@npm:12.0.2" + dependencies: + make-fetch-happen: ^10.0.1 + minipass: ^3.1.6 + minipass-fetch: ^1.4.1 + minipass-json-stream: ^1.0.1 + minizlib: ^2.1.2 + npm-package-arg: ^8.1.5 + checksum: 88ef49b6fad104165f183ec804a65471a23cead40fa035ac57f2cbe084feffe9c10bed8c4234af3fa549d947108450d5359b41ae5dec9a1ffca4d8fa7c7f78b8 + languageName: node + linkType: hard + +"npm-run-path@npm:4.0.1, npm-run-path@npm:^4.0.0, npm-run-path@npm:^4.0.1": + version: 4.0.1 + resolution: "npm-run-path@npm:4.0.1" + dependencies: + path-key: ^3.0.0 + checksum: 5374c0cea4b0bbfdfae62da7bbdf1e1558d338335f4cacf2515c282ff358ff27b2ecb91ffa5330a8b14390ac66a1e146e10700440c1ab868208430f56b5f4d23 + languageName: node + linkType: hard + +"npm-run-path@npm:^2.0.0": + version: 2.0.2 + resolution: "npm-run-path@npm:2.0.2" + dependencies: + path-key: ^2.0.0 + checksum: acd5ad81648ba4588ba5a8effb1d98d2b339d31be16826a118d50f182a134ac523172101b82eab1d01cb4c2ba358e857d54cfafd8163a1ffe7bd52100b741125 + languageName: node + linkType: hard + +"npmlog@npm:^4.1.2": + version: 4.1.2 + resolution: "npmlog@npm:4.1.2" + dependencies: + are-we-there-yet: ~1.1.2 + console-control-strings: ~1.1.0 + gauge: ~2.7.3 + set-blocking: ~2.0.0 + checksum: edbda9f95ec20957a892de1839afc6fb735054c3accf6fbefe767bac9a639fd5cea2baeac6bd2bcd50a85cb54924d57d9886c81c7fbc2332c2ddd19227504192 + languageName: node + linkType: hard + +"npmlog@npm:^5.0.1": + version: 5.0.1 + resolution: "npmlog@npm:5.0.1" + dependencies: + are-we-there-yet: ^2.0.0 + console-control-strings: ^1.1.0 + gauge: ^3.0.0 + set-blocking: ^2.0.0 + checksum: 516b2663028761f062d13e8beb3f00069c5664925871a9b57989642ebe09f23ab02145bf3ab88da7866c4e112cafff72401f61a672c7c8a20edc585a7016ef5f + languageName: node + linkType: hard + +"npmlog@npm:^6.0.0": + version: 6.0.1 + resolution: "npmlog@npm:6.0.1" + dependencies: + are-we-there-yet: ^3.0.0 + console-control-strings: ^1.1.0 + gauge: ^4.0.0 + set-blocking: ^2.0.0 + checksum: f1a4078a73ebc89896a832bbf869f491c32ecb12e0434b9a7499878ce8f29f22e72befe3c53cd8cdc9dbf4b4057297e783ab0b6746a8b067734de6205af4d538 + languageName: node + linkType: hard + +"number-is-nan@npm:^1.0.0": + version: 1.0.1 + resolution: "number-is-nan@npm:1.0.1" + checksum: 13656bc9aa771b96cef209ffca31c31a03b507ca6862ba7c3f638a283560620d723d52e626d57892c7fff475f4c36ac07f0600f14544692ff595abff214b9ffb + languageName: node + linkType: hard + +"nyc@npm:^15.1.0": + version: 15.1.0 + resolution: "nyc@npm:15.1.0" + dependencies: + "@istanbuljs/load-nyc-config": ^1.0.0 + "@istanbuljs/schema": ^0.1.2 + caching-transform: ^4.0.0 + convert-source-map: ^1.7.0 + decamelize: ^1.2.0 + find-cache-dir: ^3.2.0 + find-up: ^4.1.0 + foreground-child: ^2.0.0 + get-package-type: ^0.1.0 + glob: ^7.1.6 + istanbul-lib-coverage: ^3.0.0 + istanbul-lib-hook: ^3.0.0 + istanbul-lib-instrument: ^4.0.0 + istanbul-lib-processinfo: ^2.0.2 + istanbul-lib-report: ^3.0.0 + istanbul-lib-source-maps: ^4.0.0 + istanbul-reports: ^3.0.2 + make-dir: ^3.0.0 + node-preload: ^0.2.1 + p-map: ^3.0.0 + process-on-spawn: ^1.0.0 + resolve-from: ^5.0.0 + rimraf: ^3.0.0 + signal-exit: ^3.0.2 + spawn-wrap: ^2.0.0 + test-exclude: ^6.0.0 + yargs: ^15.0.2 + bin: + nyc: bin/nyc.js + checksum: 82a7031982df2fd6ab185c9f1b5d032b6221846268007b45b5773c6582e776ab33e96cd22b4231520345942fcef69b4339bd967675b8483f3fa255b56326faef + languageName: node + linkType: hard + +"oauth-sign@npm:~0.9.0": + version: 0.9.0 + resolution: "oauth-sign@npm:0.9.0" + checksum: 8f5497a127967866a3c67094c21efd295e46013a94e6e828573c62220e9af568cc1d2d04b16865ba583e430510fa168baf821ea78f355146d8ed7e350fc44c64 + languageName: node + linkType: hard + +"object-assign@npm:^4, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f + languageName: node + linkType: hard + +"object-inspect@npm:^1.11.0, object-inspect@npm:^1.9.0": + version: 1.11.0 + resolution: "object-inspect@npm:1.11.0" + checksum: 8c64f89ce3a7b96b6925879ad5f6af71d498abc217e136660efecd97452991216f375a7eb47cb1cb50643df939bf0c7cc391567b7abc6a924d04679705e58e27 + languageName: node + linkType: hard + +"object-is@npm:^1.0.1": + version: 1.1.5 + resolution: "object-is@npm:1.1.5" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.3 + checksum: 989b18c4cba258a6b74dc1d74a41805c1a1425bce29f6cabb50dcb1a6a651ea9104a1b07046739a49a5bb1bc49727bcb00efd5c55f932f6ea04ec8927a7901fe + languageName: node + linkType: hard + +"object-keys@npm:^1.0.12, object-keys@npm:^1.1.1": + version: 1.1.1 + resolution: "object-keys@npm:1.1.1" + checksum: b363c5e7644b1e1b04aa507e88dcb8e3a2f52b6ffd0ea801e4c7a62d5aa559affe21c55a07fd4b1fd55fc03a33c610d73426664b20032405d7b92a1414c34d6a + languageName: node + linkType: hard + +"object-treeify@npm:^1.1.4": + version: 1.1.33 + resolution: "object-treeify@npm:1.1.33" + checksum: 3af7f889349571ee73f5bdfb5ac478270c85eda8bcba950b454eb598ce41759a1ed6b0b43fbd624cb449080a4eb2df906b602e5138b6186b9563b692231f1694 + languageName: node + linkType: hard + +"object.assign@npm:^4.1.0, object.assign@npm:^4.1.2": + version: 4.1.2 + resolution: "object.assign@npm:4.1.2" + dependencies: + call-bind: ^1.0.0 + define-properties: ^1.1.3 + has-symbols: ^1.0.1 + object-keys: ^1.1.1 + checksum: d621d832ed7b16ac74027adb87196804a500d80d9aca536fccb7ba48d33a7e9306a75f94c1d29cbfa324bc091bfc530bc24789568efdaee6a47fcfa298993814 + languageName: node + linkType: hard + +"object.entries@npm:^1.1.2": + version: 1.1.5 + resolution: "object.entries@npm:1.1.5" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.3 + es-abstract: ^1.19.1 + checksum: d658696f74fd222060d8428d2a9fda2ce736b700cb06f6bdf4a16a1892d145afb746f453502b2fa55d1dca8ead6f14ddbcf66c545df45adadea757a6c4cd86c7 + languageName: node + linkType: hard + +"object.values@npm:^1.1.5": + version: 1.1.5 + resolution: "object.values@npm:1.1.5" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.3 + es-abstract: ^1.19.1 + checksum: 0f17e99741ebfbd0fa55ce942f6184743d3070c61bd39221afc929c8422c4907618c8da694c6915bc04a83ab3224260c779ba37fc07bb668bdc5f33b66a902a4 + languageName: node + linkType: hard + +"oclif@npm:^2.4.5": + version: 2.4.5 + resolution: "oclif@npm:2.4.5" + dependencies: + "@oclif/core": ^1.3.0 + "@oclif/plugin-help": ^5.1.11 + "@oclif/plugin-not-found": ^2.3.1 + "@oclif/plugin-warn-if-update-available": ^2.0.4 + aws-sdk: ^2.1069.0 + concurrently: ^7.0.0 + debug: ^4.3.3 + find-yarn-workspace-root: ^2.0.0 + fs-extra: ^8.1 + github-slugger: ^1.4.0 + lodash: ^4.17.21 + normalize-package-data: ^3.0.3 + qqjs: ^0.3.11 + semver: ^7.3.5 + tslib: ^2.3.1 + yeoman-environment: ^3.9.1 + yeoman-generator: ^5.6.1 + yosay: ^2.0.2 + bin: + oclif: bin/run + checksum: 8c8901c5f3b7adaac901af503d93c5b2cb06e9ee34f0e351f346e683cde08a4f48e2b6aee5eaff1c40081326fbb2926b410e09f66479214db49155395ebaa58b + languageName: node + linkType: hard + +"on-finished@npm:~2.3.0": + version: 2.3.0 + resolution: "on-finished@npm:2.3.0" + dependencies: + ee-first: 1.1.1 + checksum: 1db595bd963b0124d6fa261d18320422407b8f01dc65863840f3ddaaf7bcad5b28ff6847286703ca53f4ec19595bd67a2f1253db79fc4094911ec6aa8df1671b + languageName: node + linkType: hard + +"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: 1 + checksum: cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 + languageName: node + linkType: hard + +"one-time@npm:^1.0.0": + version: 1.0.0 + resolution: "one-time@npm:1.0.0" + dependencies: + fn.name: 1.x.x + checksum: fd008d7e992bdec1c67f53a2f9b46381ee12a9b8c309f88b21f0223546003fb47e8ad7c1fd5843751920a8d276c63bd4b45670ef80c61fb3e07dbccc962b5c7d + languageName: node + linkType: hard + +"onetime@npm:^5.1.0, onetime@npm:^5.1.2": + version: 5.1.2 + resolution: "onetime@npm:5.1.2" + dependencies: + mimic-fn: ^2.1.0 + checksum: 2478859ef817fc5d4e9c2f9e5728512ddd1dbc9fb7829ad263765bb6d3b91ce699d6e2332eef6b7dff183c2f490bd3349f1666427eaba4469fba0ac38dfd0d34 + languageName: node + linkType: hard + +"ono@npm:^6.0.0": + version: 6.0.1 + resolution: "ono@npm:6.0.1" + checksum: 182db954b7e8906de06cd0b1783c78d52df142c65346c9beaf1ce1be3d34d48e408bd003f2b5e0a9aa90d5d505d9f0ecd06d1804de7300a681c08a7962d770f8 + languageName: node + linkType: hard + +"openapi-schemas@npm:^1.0.2": + version: 1.0.3 + resolution: "openapi-schemas@npm:1.0.3" + checksum: 170dbf4d103880ed7e0082d97f9eed007630c1d153795d5c82f868fd6790980dd182fa6ceddec65bb701f1ac294b15ff66fe6cab2906c784bff1e1fbca95f1f9 + languageName: node + linkType: hard + +"openapi-types@npm:^1.3.5": + version: 1.3.5 + resolution: "openapi-types@npm:1.3.5" + checksum: c2d20ea228977b301ccacf9aebadd94ab852ef1fe7343b92e0b20b66c0ca30ef2097beaf918870a65744b2758f4f5089637d140f765a9fa3adabe24ebcc2a1a2 + languageName: node + linkType: hard + +"optional-require@npm:^1.1.8": + version: 1.1.8 + resolution: "optional-require@npm:1.1.8" + dependencies: + require-at: ^1.0.6 + checksum: 437db76f713052925185ae80837b593877f75101154e8937f50d33b0b07bd500c214efc9016748642109b6e3e1197eb0513a2963eb06bcf3890f88a2724b1c87 + languageName: node + linkType: hard + +"optionator@npm:^0.8.1": + version: 0.8.3 + resolution: "optionator@npm:0.8.3" + dependencies: + deep-is: ~0.1.3 + fast-levenshtein: ~2.0.6 + levn: ~0.3.0 + prelude-ls: ~1.1.2 + type-check: ~0.3.2 + word-wrap: ~1.2.3 + checksum: b8695ddf3d593203e25ab0900e265d860038486c943ff8b774f596a310f8ceebdb30c6832407a8198ba3ec9debe1abe1f51d4aad94843612db3b76d690c61d34 + languageName: node + linkType: hard + +"optionator@npm:^0.9.1": + version: 0.9.1 + resolution: "optionator@npm:0.9.1" + dependencies: + deep-is: ^0.1.3 + fast-levenshtein: ^2.0.6 + levn: ^0.4.1 + prelude-ls: ^1.2.1 + type-check: ^0.4.0 + word-wrap: ^1.2.3 + checksum: dbc6fa065604b24ea57d734261914e697bd73b69eff7f18e967e8912aa2a40a19a9f599a507fa805be6c13c24c4eae8c71306c239d517d42d4c041c942f508a0 + languageName: node + linkType: hard + +"ora@npm:^5.4.1": + version: 5.4.1 + resolution: "ora@npm:5.4.1" + 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 + checksum: 28d476ee6c1049d68368c0dc922e7225e3b5600c3ede88fade8052837f9ed342625fdaa84a6209302587c8ddd9b664f71f0759833cbdb3a4cf81344057e63c63 + languageName: node + linkType: hard + +"os-browserify@npm:^0.3.0, os-browserify@npm:~0.3.0": + version: 0.3.0 + resolution: "os-browserify@npm:0.3.0" + checksum: 16e37ba3c0e6a4c63443c7b55799ce4066d59104143cb637ecb9fce586d5da319cdca786ba1c867abbe3890d2cbf37953f2d51eea85e20dd6c4570d6c54bfebf + languageName: node + linkType: hard + +"os-tmpdir@npm:~1.0.2": + version: 1.0.2 + resolution: "os-tmpdir@npm:1.0.2" + checksum: 5666560f7b9f10182548bf7013883265be33620b1c1b4a4d405c25be2636f970c5488ff3e6c48de75b55d02bde037249fe5dbfbb4c0fb7714953d56aed062e6d + languageName: node + linkType: hard + +"p-cancelable@npm:^1.0.0": + version: 1.1.0 + resolution: "p-cancelable@npm:1.1.0" + checksum: 2db3814fef6d9025787f30afaee4496a8857a28be3c5706432cbad76c688a6db1874308f48e364a42f5317f5e41e8e7b4f2ff5c8ff2256dbb6264bc361704ece + languageName: node + linkType: hard + +"p-finally@npm:^1.0.0": + version: 1.0.0 + resolution: "p-finally@npm:1.0.0" + checksum: 93a654c53dc805dd5b5891bab16eb0ea46db8f66c4bfd99336ae929323b1af2b70a8b0654f8f1eae924b2b73d037031366d645f1fd18b3d30cbd15950cc4b1d4 + languageName: node + linkType: hard + +"p-limit@npm:^1.1.0": + version: 1.3.0 + resolution: "p-limit@npm:1.3.0" + dependencies: + p-try: ^1.0.0 + checksum: 281c1c0b8c82e1ac9f81acd72a2e35d402bf572e09721ce5520164e9de07d8274451378a3470707179ad13240535558f4b277f02405ad752e08c7d5b0d54fbfd + languageName: node + linkType: hard + +"p-limit@npm:^2.2.0": + version: 2.3.0 + resolution: "p-limit@npm:2.3.0" + dependencies: + p-try: ^2.0.0 + checksum: 84ff17f1a38126c3314e91ecfe56aecbf36430940e2873dadaa773ffe072dc23b7af8e46d4b6485d302a11673fe94c6b67ca2cfbb60c989848b02100d0594ac1 + languageName: node + linkType: hard + +"p-limit@npm:^3.0.2": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: ^0.1.0 + checksum: 7c3690c4dbf62ef625671e20b7bdf1cbc9534e83352a2780f165b0d3ceba21907e77ad63401708145ca4e25bfc51636588d89a8c0aeb715e6c37d1c066430360 + languageName: node + linkType: hard + +"p-locate@npm:^2.0.0": + version: 2.0.0 + resolution: "p-locate@npm:2.0.0" + dependencies: + p-limit: ^1.1.0 + checksum: e2dceb9b49b96d5513d90f715780f6f4972f46987dc32a0e18bc6c3fc74a1a5d73ec5f81b1398af5e58b99ea1ad03fd41e9181c01fa81b4af2833958696e3081 + languageName: node + linkType: hard + +"p-locate@npm:^4.1.0": + version: 4.1.0 + resolution: "p-locate@npm:4.1.0" + dependencies: + p-limit: ^2.2.0 + checksum: 513bd14a455f5da4ebfcb819ef706c54adb09097703de6aeaa5d26fe5ea16df92b48d1ac45e01e3944ce1e6aa2a66f7f8894742b8c9d6e276e16cd2049a2b870 + languageName: node + linkType: hard + +"p-locate@npm:^5.0.0": + version: 5.0.0 + resolution: "p-locate@npm:5.0.0" + dependencies: + p-limit: ^3.0.2 + checksum: 1623088f36cf1cbca58e9b61c4e62bf0c60a07af5ae1ca99a720837356b5b6c5ba3eb1b2127e47a06865fee59dd0453cad7cc844cda9d5a62ac1a5a51b7c86d3 + languageName: node + linkType: hard + +"p-map@npm:^3.0.0": + version: 3.0.0 + resolution: "p-map@npm:3.0.0" + dependencies: + aggregate-error: ^3.0.0 + checksum: 49b0fcbc66b1ef9cd379de1b4da07fa7a9f84b41509ea3f461c31903623aaba8a529d22f835e0d77c7cb9fcc16e4fae71e308fd40179aea514ba68f27032b5d5 + languageName: node + linkType: hard + +"p-map@npm:^4.0.0": + version: 4.0.0 + resolution: "p-map@npm:4.0.0" + dependencies: + aggregate-error: ^3.0.0 + checksum: cb0ab21ec0f32ddffd31dfc250e3afa61e103ef43d957cc45497afe37513634589316de4eb88abdfd969fe6410c22c0b93ab24328833b8eb1ccc087fc0442a1c + languageName: node + linkType: hard + +"p-queue@npm:^6.6.2": + version: 6.6.2 + resolution: "p-queue@npm:6.6.2" + dependencies: + eventemitter3: ^4.0.4 + p-timeout: ^3.2.0 + checksum: 832642fcc4ab6477b43e6d7c30209ab10952969ed211c6d6f2931be8a4f9935e3578c72e8cce053dc34f2eb6941a408a2c516a54904e989851a1a209cf19761c + languageName: node + linkType: hard + +"p-timeout@npm:^3.2.0": + version: 3.2.0 + resolution: "p-timeout@npm:3.2.0" + dependencies: + p-finally: ^1.0.0 + checksum: 3dd0eaa048780a6f23e5855df3dd45c7beacff1f820476c1d0d1bcd6648e3298752ba2c877aa1c92f6453c7dd23faaf13d9f5149fc14c0598a142e2c5e8d649c + languageName: node + linkType: hard + +"p-transform@npm:^1.3.0": + version: 1.3.0 + resolution: "p-transform@npm:1.3.0" + dependencies: + debug: ^4.3.2 + p-queue: ^6.6.2 + checksum: d1e2d6ad75241878c302531c262e3c13ea50f5e8c9fbfbf119faf415b719158858ae97dda44b8ec91ad9a7efbbcdb731e452b75df860f9515569d57ea66f9cec + languageName: node + linkType: hard + +"p-try@npm:^1.0.0": + version: 1.0.0 + resolution: "p-try@npm:1.0.0" + checksum: 3b5303f77eb7722144154288bfd96f799f8ff3e2b2b39330efe38db5dd359e4fb27012464cd85cb0a76e9b7edd1b443568cb3192c22e7cffc34989df0bafd605 + languageName: node + linkType: hard + +"p-try@npm:^2.0.0": + version: 2.2.0 + resolution: "p-try@npm:2.2.0" + checksum: f8a8e9a7693659383f06aec604ad5ead237c7a261c18048a6e1b5b85a5f8a067e469aa24f5bc009b991ea3b058a87f5065ef4176793a200d4917349881216cae + languageName: node + linkType: hard + +"package-hash@npm:^4.0.0": + version: 4.0.0 + resolution: "package-hash@npm:4.0.0" + dependencies: + graceful-fs: ^4.1.15 + hasha: ^5.0.0 + lodash.flattendeep: ^4.4.0 + release-zalgo: ^1.0.0 + checksum: 32c49e3a0e1c4a33b086a04cdd6d6e570aee019cb8402ec16476d9b3564a40e38f91ce1a1f9bc88b08f8ef2917a11e0b786c08140373bdf609ea90749031e6fc + languageName: node + linkType: hard + +"package-json@npm:^6.3.0": + version: 6.5.0 + resolution: "package-json@npm:6.5.0" + dependencies: + got: ^9.6.0 + registry-auth-token: ^4.0.0 + registry-url: ^5.0.0 + semver: ^6.2.0 + checksum: cc9f890d3667d7610e6184decf543278b87f657d1ace0deb4a9c9155feca738ef88f660c82200763d3348010f4e42e9c7adc91e96ab0f86a770955995b5351e2 + languageName: node + linkType: hard + +"pacote@npm:^12.0.0, pacote@npm:^12.0.2": + version: 12.0.3 + resolution: "pacote@npm:12.0.3" + dependencies: + "@npmcli/git": ^2.1.0 + "@npmcli/installed-package-contents": ^1.0.6 + "@npmcli/promise-spawn": ^1.2.0 + "@npmcli/run-script": ^2.0.0 + cacache: ^15.0.5 + chownr: ^2.0.0 + fs-minipass: ^2.1.0 + infer-owner: ^1.0.4 + minipass: ^3.1.3 + mkdirp: ^1.0.3 + npm-package-arg: ^8.0.1 + npm-packlist: ^3.0.0 + npm-pick-manifest: ^6.0.0 + npm-registry-fetch: ^12.0.0 + promise-retry: ^2.0.1 + read-package-json-fast: ^2.0.1 + rimraf: ^3.0.2 + ssri: ^8.0.1 + tar: ^6.1.0 + bin: + pacote: lib/bin.js + checksum: 730e2b344619daff078b1f7c085c2da3b1417f1667204384cba981409098af2375b130a6470f75ea22f09b83c00fe227143b68e50d0dd7ff972e28a697b9c1d5 + languageName: node + linkType: hard + +"pad-component@npm:0.0.1": + version: 0.0.1 + resolution: "pad-component@npm:0.0.1" + checksum: 2d92ad68b6c86ce2afcc75c9536401ef8b25a03f9b1330fbe5a9a9862a5cbb0e4088848d427919f4cb7526c333b7eada7cb590328e69775257e20363023bb424 + languageName: node + linkType: hard + +"pako@npm:~1.0.5": + version: 1.0.11 + resolution: "pako@npm:1.0.11" + checksum: 1be2bfa1f807608c7538afa15d6f25baa523c30ec870a3228a89579e474a4d992f4293859524e46d5d87fd30fa17c5edf34dbef0671251d9749820b488660b16 + languageName: node + linkType: hard + +"parent-module@npm:^1.0.0": + version: 1.0.1 + resolution: "parent-module@npm:1.0.1" + dependencies: + callsites: ^3.0.0 + checksum: 6ba8b255145cae9470cf5551eb74be2d22281587af787a2626683a6c20fbb464978784661478dd2a3f1dad74d1e802d403e1b03c1a31fab310259eec8ac560ff + languageName: node + linkType: hard + +"parents@npm:^1.0.0, parents@npm:^1.0.1": + version: 1.0.1 + resolution: "parents@npm:1.0.1" + dependencies: + path-platform: ~0.11.15 + checksum: 094fc817d5e8d94e9f9d38c2618a2822f2960b7a268183a36326c5d1cf6ff32f97b1158b0f9b32ab126573996dfe6db104feda6d26e8531d762d178ef4488fc8 + languageName: node + linkType: hard + +"parse-asn1@npm:^5.0.0, parse-asn1@npm:^5.1.5": + version: 5.1.6 + resolution: "parse-asn1@npm:5.1.6" + dependencies: + asn1.js: ^5.2.0 + browserify-aes: ^1.0.0 + evp_bytestokey: ^1.0.0 + pbkdf2: ^3.0.3 + safe-buffer: ^5.1.1 + checksum: 9243311d1f88089bc9f2158972aa38d1abd5452f7b7cabf84954ed766048fe574d434d82c6f5a39b988683e96fb84cd933071dda38927e03469dc8c8d14463c7 + languageName: node + linkType: hard + +"parse-conflict-json@npm:^2.0.1": + version: 2.0.1 + resolution: "parse-conflict-json@npm:2.0.1" + dependencies: + json-parse-even-better-errors: ^2.3.1 + just-diff: ^5.0.1 + just-diff-apply: ^4.0.1 + checksum: 398728731f3b7330d2885075f1dad0abd6fb943fca6aaa5f0edf46ccf06fe72b3ae09327f19447e98052fdfbf8bcfeee3aa14d7eb843846ec158b871a7fc1bba + languageName: node + linkType: hard + +"parse-json@npm:^4.0.0": + version: 4.0.0 + resolution: "parse-json@npm:4.0.0" + dependencies: + error-ex: ^1.3.1 + json-parse-better-errors: ^1.0.1 + checksum: 0fe227d410a61090c247e34fa210552b834613c006c2c64d9a05cfe9e89cf8b4246d1246b1a99524b53b313e9ac024438d0680f67e33eaed7e6f38db64cfe7b5 + languageName: node + linkType: hard + +"parse-json@npm:^5.0.0": + version: 5.2.0 + resolution: "parse-json@npm:5.2.0" + 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 + checksum: 62085b17d64da57f40f6afc2ac1f4d95def18c4323577e1eced571db75d9ab59b297d1d10582920f84b15985cbfc6b6d450ccbf317644cfa176f3ed982ad87e2 + languageName: node + linkType: hard + +"parse-ms@npm:^2.1.0": + version: 2.1.0 + resolution: "parse-ms@npm:2.1.0" + checksum: d5c66c76cca8df5bd0574e2d11b9c3752893b59b466e74308d4a2f09760dc5436a1633f549cad300fc8c3c19154d14959a3b8333d3b2f7bd75898fe18149d564 + languageName: node + linkType: hard + +"parseurl@npm:~1.3.3": + version: 1.3.3 + resolution: "parseurl@npm:1.3.3" + checksum: 407cee8e0a3a4c5cd472559bca8b6a45b82c124e9a4703302326e9ab60fc1081442ada4e02628efef1eb16197ddc7f8822f5a91fd7d7c86b51f530aedb17dfa2 + languageName: node + linkType: hard + +"pascal-case@npm:^3.1.2": + version: 3.1.2 + resolution: "pascal-case@npm:3.1.2" + dependencies: + no-case: ^3.0.4 + tslib: ^2.0.3 + checksum: ba98bfd595fc91ef3d30f4243b1aee2f6ec41c53b4546bfa3039487c367abaa182471dcfc830a1f9e1a0df00c14a370514fa2b3a1aacc68b15a460c31116873e + languageName: node + linkType: hard + +"password-prompt@npm:^1.1.2": + version: 1.1.2 + resolution: "password-prompt@npm:1.1.2" + dependencies: + ansi-escapes: ^3.1.0 + cross-spawn: ^6.0.5 + checksum: 4763ec1b48cb311d60df37186e31f1b85ec3249a21cc17bbf8407d66c5b55cffe34b4eb529ebd044ed4ced7f3ea3fad744fe15e30a5de31645433e94cd444266 + languageName: node + linkType: hard + +"path-browserify@npm:^1.0.1": + version: 1.0.1 + resolution: "path-browserify@npm:1.0.1" + checksum: c6d7fa376423fe35b95b2d67990060c3ee304fc815ff0a2dc1c6c3cfaff2bd0d572ee67e18f19d0ea3bbe32e8add2a05021132ac40509416459fffee35200699 + languageName: node + linkType: hard + +"path-browserify@npm:~0.0.0": + version: 0.0.1 + resolution: "path-browserify@npm:0.0.1" + checksum: ae8dcd45d0d3cfbaf595af4f206bf3ed82d77f72b4877ae7e77328079e1468c84f9386754bb417d994d5a19bf47882fd253565c18441cd5c5c90ae5187599e35 + languageName: node + linkType: hard + +"path-exists@npm:^3.0.0": + version: 3.0.0 + resolution: "path-exists@npm:3.0.0" + checksum: 96e92643aa34b4b28d0de1cd2eba52a1c5313a90c6542d03f62750d82480e20bfa62bc865d5cfc6165f5fcd5aeb0851043c40a39be5989646f223300021bae0a + languageName: node + linkType: hard + +"path-exists@npm:^4.0.0": + version: 4.0.0 + resolution: "path-exists@npm:4.0.0" + checksum: 505807199dfb7c50737b057dd8d351b82c033029ab94cb10a657609e00c1bc53b951cfdbccab8de04c5584d5eff31128ce6afd3db79281874a5ef2adbba55ed1 + languageName: node + linkType: hard + +"path-is-absolute@npm:^1.0.0, path-is-absolute@npm:^1.0.1": + version: 1.0.1 + resolution: "path-is-absolute@npm:1.0.1" + checksum: 060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8 + languageName: node + linkType: hard + +"path-key@npm:^2.0.0, path-key@npm:^2.0.1": + version: 2.0.1 + resolution: "path-key@npm:2.0.1" + checksum: f7ab0ad42fe3fb8c7f11d0c4f849871e28fbd8e1add65c370e422512fc5887097b9cf34d09c1747d45c942a8c1e26468d6356e2df3f740bf177ab8ca7301ebfd + languageName: node + linkType: hard + +"path-key@npm:^3.0.0, path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 55cd7a9dd4b343412a8386a743f9c746ef196e57c823d90ca3ab917f90ab9f13dd0ded27252ba49dbdfcab2b091d998bc446f6220cd3cea65db407502a740020 + languageName: node + linkType: hard + +"path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 49abf3d81115642938a8700ec580da6e830dde670be21893c62f4e10bd7dd4c3742ddc603fe24f898cba7eb0c6bc1777f8d9ac14185d34540c6d4d80cd9cae8a + languageName: node + linkType: hard + +"path-platform@npm:~0.11.15": + version: 0.11.15 + resolution: "path-platform@npm:0.11.15" + checksum: 239f2eae720531ff5a48837de68f94ebd7cf6cd2bf295b39beb97c5bafc34a34a683b62f9f5ad5ca5e78d71d7d44c29e7c56373c1c8473ab128a4e648bb898f0 + languageName: node + linkType: hard + +"path-to-regexp@npm:^1.7.0": + version: 1.8.0 + resolution: "path-to-regexp@npm:1.8.0" + dependencies: + isarray: 0.0.1 + checksum: 709f6f083c0552514ef4780cb2e7e4cf49b0cc89a97439f2b7cc69a608982b7690fb5d1720a7473a59806508fc2dae0be751ba49f495ecf89fd8fbc62abccbcd + languageName: node + linkType: hard + +"path-type@npm:^3.0.0": + version: 3.0.0 + resolution: "path-type@npm:3.0.0" + dependencies: + pify: ^3.0.0 + checksum: 735b35e256bad181f38fa021033b1c33cfbe62ead42bb2222b56c210e42938eecb272ae1949f3b6db4ac39597a61b44edd8384623ec4d79bfdc9a9c0f12537a6 + languageName: node + linkType: hard + +"path-type@npm:^4.0.0": + version: 4.0.0 + resolution: "path-type@npm:4.0.0" + checksum: 5b1e2daa247062061325b8fdbfd1fb56dde0a448fb1455453276ea18c60685bdad23a445dc148cf87bc216be1573357509b7d4060494a6fd768c7efad833ee45 + languageName: node + linkType: hard + +"pathval@npm:^1.1.1": + version: 1.1.1 + resolution: "pathval@npm:1.1.1" + checksum: 090e3147716647fb7fb5b4b8c8e5b55e5d0a6086d085b6cd23f3d3c01fcf0ff56fd3cc22f2f4a033bd2e46ed55d61ed8379e123b42afe7d531a2a5fc8bb556d6 + languageName: node + linkType: hard + +"pbkdf2@npm:^3.0.3, pbkdf2@npm:^3.1.1": + version: 3.1.2 + resolution: "pbkdf2@npm:3.1.2" + dependencies: + create-hash: ^1.1.2 + create-hmac: ^1.1.4 + ripemd160: ^2.0.1 + safe-buffer: ^5.0.1 + sha.js: ^2.4.8 + checksum: 2c950a100b1da72123449208e231afc188d980177d021d7121e96a2de7f2abbc96ead2b87d03d8fe5c318face097f203270d7e27908af9f471c165a4e8e69c92 + languageName: node + linkType: hard + +"performance-now@npm:^2.1.0": + version: 2.1.0 + resolution: "performance-now@npm:2.1.0" + checksum: 534e641aa8f7cba160f0afec0599b6cecefbb516a2e837b512be0adbe6c1da5550e89c78059c7fabc5c9ffdf6627edabe23eb7c518c4500067a898fa65c2b550 + languageName: node + linkType: hard + +"picocolors@npm:^1.0.0": + version: 1.0.0 + resolution: "picocolors@npm:1.0.0" + checksum: a2e8092dd86c8396bdba9f2b5481032848525b3dc295ce9b57896f931e63fc16f79805144321f72976383fc249584672a75cc18d6777c6b757603f372f745981 + languageName: node + linkType: hard + +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3": + version: 2.3.0 + resolution: "picomatch@npm:2.3.0" + checksum: 16818720ea7c5872b6af110760dee856c8e4cd79aed1c7a006d076b1cc09eff3ae41ca5019966694c33fbd2e1cc6ea617ab10e4adac6df06556168f13be3fca2 + languageName: node + linkType: hard + +"pid-cwd@npm:^1.2.0": + version: 1.2.0 + resolution: "pid-cwd@npm:1.2.0" + checksum: 5a7872f39bce9c2885215e013b66e775e01060fd14f9054e14f35f4c77dcc59bbf51d3a1da055f1b14854b16cafef84c9c63a249a7ab1286e37a0de02941fb86 + languageName: node + linkType: hard + +"pify@npm:^2.3.0": + version: 2.3.0 + resolution: "pify@npm:2.3.0" + checksum: 9503aaeaf4577acc58642ad1d25c45c6d90288596238fb68f82811c08104c800e5a7870398e9f015d82b44ecbcbef3dc3d4251a1cbb582f6e5959fe09884b2ba + languageName: node + linkType: hard + +"pify@npm:^3.0.0": + version: 3.0.0 + resolution: "pify@npm:3.0.0" + checksum: 6cdcbc3567d5c412450c53261a3f10991665d660961e06605decf4544a61a97a54fefe70a68d5c37080ff9d6f4cf51444c90198d1ba9f9309a6c0d6e9f5c4fde + languageName: node + linkType: hard + +"pify@npm:^4.0.1": + version: 4.0.1 + resolution: "pify@npm:4.0.1" + checksum: 9c4e34278cb09987685fa5ef81499c82546c033713518f6441778fbec623fc708777fe8ac633097c72d88470d5963094076c7305cafc7ad340aae27cfacd856b + languageName: node + linkType: hard + +"pino-multi-stream@npm:^5.2.0": + version: 5.3.0 + resolution: "pino-multi-stream@npm:5.3.0" + dependencies: + pino: ^6.0.0 + checksum: 10ddb859836a1d8e4894a408c4bc0c00ca67fe81c2c1aa8ff1534720a5e1ad0261a976239408ec5cdfa5fd7f0444c83d6caa00189efe5cf95b230ed9a32ee6d5 + languageName: node + linkType: hard + +"pino-pretty@npm:^4.0.3": + version: 4.8.0 + resolution: "pino-pretty@npm:4.8.0" + dependencies: + "@hapi/bourne": ^2.0.0 + args: ^5.0.1 + chalk: ^4.0.0 + dateformat: ^4.5.1 + fast-safe-stringify: ^2.0.7 + jmespath: ^0.15.0 + joycon: ^2.2.5 + pump: ^3.0.0 + readable-stream: ^3.6.0 + rfdc: ^1.3.0 + split2: ^3.1.1 + strip-json-comments: ^3.1.1 + bin: + pino-pretty: bin.js + checksum: 8e2e4cdb80c7f8b4df318f30415c98a09f952174a7dd9b0910041f995b8476fc177568e950ea3ce5967303c46356df37d13f822cff99c848e4177c957d3b1dad + languageName: node + linkType: hard + +"pino-std-serializers@npm:^3.1.0": + version: 3.2.0 + resolution: "pino-std-serializers@npm:3.2.0" + checksum: 77e29675b116e42ae9fe6d4ef52ef3a082ffc54922b122d85935f93ddcc20277f0b0c873c5c6c5274a67b0409c672aaae3de6bcea10a2d84699718dda55ba95b + languageName: node + linkType: hard + +"pino@npm:^6.0.0, pino@npm:^6.4.0": + version: 6.13.3 + resolution: "pino@npm:6.13.3" + dependencies: + fast-redact: ^3.0.0 + fast-safe-stringify: ^2.0.8 + fastify-warning: ^0.2.0 + flatstr: ^1.0.12 + pino-std-serializers: ^3.1.0 + quick-format-unescaped: ^4.0.3 + sonic-boom: ^1.0.2 + bin: + pino: bin.js + checksum: a580decd47a1c8b32a846ba1cb478087b523636d697bd4c57833d10b3f2b35c7d06739715ad9a291b41caf002b8d1bbf98674bfb3e99989fd41b7d934cca861c + languageName: node + linkType: hard + +"pkg-dir@npm:^2.0.0": + version: 2.0.0 + resolution: "pkg-dir@npm:2.0.0" + dependencies: + find-up: ^2.1.0 + checksum: 8c72b712305b51e1108f0ffda5ec1525a8307e54a5855db8fb1dcf77561a5ae98e2ba3b4814c9806a679f76b2f7e5dd98bde18d07e594ddd9fdd25e9cf242ea1 + languageName: node + linkType: hard + +"pkg-dir@npm:^4.1.0, pkg-dir@npm:^4.2.0": + version: 4.2.0 + resolution: "pkg-dir@npm:4.2.0" + dependencies: + find-up: ^4.0.0 + checksum: 9863e3f35132bf99ae1636d31ff1e1e3501251d480336edb1c211133c8d58906bed80f154a1d723652df1fda91e01c7442c2eeaf9dc83157c7ae89087e43c8d6 + languageName: node + linkType: hard + +"preferred-pm@npm:^3.0.3": + version: 3.0.3 + resolution: "preferred-pm@npm:3.0.3" + dependencies: + find-up: ^5.0.0 + find-yarn-workspace-root2: 1.2.16 + path-exists: ^4.0.0 + which-pm: 2.0.0 + checksum: 0de0948cb6ae22213f2ad7868032d89f1e1443d9caabc22ceeb9d284f19d359d65b67fab178f4db5c8c6ca6ae34642bdc72730b70ab1899ea158e2677a88a6d0 + languageName: node + linkType: hard + +"prelude-ls@npm:^1.2.1": + version: 1.2.1 + resolution: "prelude-ls@npm:1.2.1" + checksum: cd192ec0d0a8e4c6da3bb80e4f62afe336df3f76271ac6deb0e6a36187133b6073a19e9727a1ff108cd8b9982e4768850d413baa71214dd80c7979617dca827a + languageName: node + linkType: hard + +"prelude-ls@npm:~1.1.2": + version: 1.1.2 + resolution: "prelude-ls@npm:1.1.2" + checksum: c4867c87488e4a0c233e158e4d0d5565b609b105d75e4c05dc760840475f06b731332eb93cc8c9cecb840aa8ec323ca3c9a56ad7820ad2e63f0261dadcb154e4 + languageName: node + linkType: hard + +"prepend-http@npm:^2.0.0": + version: 2.0.0 + resolution: "prepend-http@npm:2.0.0" + checksum: 7694a9525405447662c1ffd352fcb41b6410c705b739b6f4e3a3e21cf5fdede8377890088e8934436b8b17ba55365a615f153960f30877bf0d0392f9e93503ea + languageName: node + linkType: hard + +"pretty-bytes@npm:^5.3.0": + version: 5.6.0 + resolution: "pretty-bytes@npm:5.6.0" + checksum: 9c082500d1e93434b5b291bd651662936b8bd6204ec9fa17d563116a192d6d86b98f6d328526b4e8d783c07d5499e2614a807520249692da9ec81564b2f439cd + languageName: node + linkType: hard + +"pretty-format@npm:^27.3.1": + version: 27.3.1 + resolution: "pretty-format@npm:27.3.1" + dependencies: + "@jest/types": ^27.2.5 + ansi-regex: ^5.0.1 + ansi-styles: ^5.0.0 + react-is: ^17.0.1 + checksum: 2979eae85a4f7ba1c3946faa8f5c6497cc80dc64ba499ccd5fdada267f82dc664f315a4c1cdd4c0b4b97edbae399a7bf0a957cc1b87feb91cd95f1e436834fed + languageName: node + linkType: hard + +"pretty-ms@npm:^7.0.0": + version: 7.0.1 + resolution: "pretty-ms@npm:7.0.1" + dependencies: + parse-ms: ^2.1.0 + checksum: d76c4920283b48be91f1d3797a2ce4bd51187d58d2a609ae993c028f73c92d16439449d857af57ccad91ae3a38b30c87307f5589749a056102ebb494c686957e + languageName: node + linkType: hard + +"proc-log@npm:^1.0.0": + version: 1.0.0 + resolution: "proc-log@npm:1.0.0" + checksum: 249605d5b28bfa0499d70da24ab056ad1e082a301f0a46d0ace6e8049cf16aaa0e71d9ea5cab29b620ffb327c18af97f0e012d1db090673447e7c1d33239dd96 + languageName: node + linkType: hard + +"process-nextick-args@npm:^2.0.0, process-nextick-args@npm:~2.0.0": + version: 2.0.1 + resolution: "process-nextick-args@npm:2.0.1" + checksum: 1d38588e520dab7cea67cbbe2efdd86a10cc7a074c09657635e34f035277b59fbb57d09d8638346bf7090f8e8ebc070c96fa5fd183b777fff4f5edff5e9466cf + languageName: node + linkType: hard + +"process-on-spawn@npm:^1.0.0": + version: 1.0.0 + resolution: "process-on-spawn@npm:1.0.0" + dependencies: + fromentries: ^1.2.0 + checksum: 597769e3db6a8e2cb1cd64a952bbc150220588debac31c7cf1a9f620ce981e25583d8d70848d8a14953577608512984a8808c3be77e09af8ebdcdc14ec23a295 + languageName: node + linkType: hard + +"process@npm:^0.11.10, process@npm:~0.11.0": + version: 0.11.10 + resolution: "process@npm:0.11.10" + checksum: bfcce49814f7d172a6e6a14d5fa3ac92cc3d0c3b9feb1279774708a719e19acd673995226351a082a9ae99978254e320ccda4240ddc474ba31a76c79491ca7c3 + languageName: node + linkType: hard + +"progress@npm:^2.0.0": + version: 2.0.3 + resolution: "progress@npm:2.0.3" + checksum: f67403fe7b34912148d9252cb7481266a354bd99ce82c835f79070643bb3c6583d10dbcfda4d41e04bbc1d8437e9af0fb1e1f2135727878f5308682a579429b7 + languageName: node + linkType: hard + +"promise-all-reject-late@npm:^1.0.0": + version: 1.0.1 + resolution: "promise-all-reject-late@npm:1.0.1" + checksum: d7d61ac412352e2c8c3463caa5b1c3ca0f0cc3db15a09f180a3da1446e33d544c4261fc716f772b95e4c27d559cfd2388540f44104feb356584f9c73cfb9ffcb + languageName: node + linkType: hard + +"promise-call-limit@npm:^1.0.1": + version: 1.0.1 + resolution: "promise-call-limit@npm:1.0.1" + checksum: e69aed17f5f34bbd7aecff28faedb456e3500a08af31ee759ef75f2d8c2219d7c0e59f153f4d8c339056de8c304e0dd4acc500c339e7ea1e9c0e7bb1444367c8 + languageName: node + linkType: hard + +"promise-inflight@npm:^1.0.1": + version: 1.0.1 + resolution: "promise-inflight@npm:1.0.1" + checksum: 22749483091d2c594261517f4f80e05226d4d5ecc1fc917e1886929da56e22b5718b7f2a75f3807e7a7d471bc3be2907fe92e6e8f373ddf5c64bae35b5af3981 + languageName: node + linkType: hard + +"promise-retry@npm:^2.0.1": + version: 2.0.1 + resolution: "promise-retry@npm:2.0.1" + dependencies: + err-code: ^2.0.2 + retry: ^0.12.0 + checksum: f96a3f6d90b92b568a26f71e966cbbc0f63ab85ea6ff6c81284dc869b41510e6cdef99b6b65f9030f0db422bf7c96652a3fff9f2e8fb4a0f069d8f4430359429 + languageName: node + linkType: hard + +"proper-lockfile@npm:^3.2.0": + version: 3.2.0 + resolution: "proper-lockfile@npm:3.2.0" + dependencies: + graceful-fs: ^4.1.11 + retry: ^0.12.0 + signal-exit: ^3.0.2 + checksum: 1be1bb702b9d47bdf18d75f22578f51370781feba7d2617f70ff8c66a86bcfa6e55b4f69c57fc326380110f2d1ffdb6e54a4900814bf156c04ee4eb2d3c065aa + languageName: node + linkType: hard + +"protobufjs@npm:^6.10.0, protobufjs@npm:^6.8.6": + version: 6.11.2 + resolution: "protobufjs@npm:6.11.2" + dependencies: + "@protobufjs/aspromise": ^1.1.2 + "@protobufjs/base64": ^1.1.2 + "@protobufjs/codegen": ^2.0.4 + "@protobufjs/eventemitter": ^1.1.0 + "@protobufjs/fetch": ^1.1.0 + "@protobufjs/float": ^1.0.2 + "@protobufjs/inquire": ^1.1.0 + "@protobufjs/path": ^1.1.2 + "@protobufjs/pool": ^1.1.0 + "@protobufjs/utf8": ^1.1.0 + "@types/long": ^4.0.1 + "@types/node": ">=13.7.0" + long: ^4.0.0 + bin: + pbjs: bin/pbjs + pbts: bin/pbts + checksum: 80e9d9610c3eb66f9eae4e44a1ae30381cedb721b7d5f635d781fe4c507e2c77bb7c879addcd1dda79733d3ae589d9e66fd18d42baf99b35df7382a0f9920795 + languageName: node + linkType: hard + +"protocol-buffers-encodings@npm:^1.1.0": + version: 1.1.1 + resolution: "protocol-buffers-encodings@npm:1.1.1" + dependencies: + signed-varint: ^2.0.1 + varint: 5.0.0 + checksum: 1b22d6d05bbd6249cbfe9a792003945a3a4cb49c2d0e19a4b10ea2a9b543d610fcf244108133f7a5df83479a8e8cded51a33845477c1490361bfbe66a49c64ae + languageName: node + linkType: hard + +"prr@npm:~1.0.1": + version: 1.0.1 + resolution: "prr@npm:1.0.1" + checksum: 3bca2db0479fd38f8c4c9439139b0c42dcaadcc2fbb7bb8e0e6afaa1383457f1d19aea9e5f961d5b080f1cfc05bfa1fe9e45c97a1d3fd6d421950a73d3108381 + languageName: node + linkType: hard + +"ps-list@npm:^7.2.0": + version: 7.2.0 + resolution: "ps-list@npm:7.2.0" + checksum: 38969f4fb86e2b88bac4b033ff63dcf98e9490d4fa40d94feb369d79f9aa6a04efccbc3338c49eeeb2f0890cada80d74daee28584f0902c4cba0928f6e394c37 + languageName: node + linkType: hard + +"psl@npm:^1.1.28": + version: 1.8.0 + resolution: "psl@npm:1.8.0" + checksum: 6150048ed2da3f919478bee8a82f3828303bc0fc730fb015a48f83c9977682c7b28c60ab01425a72d82a2891a1681627aa530a991d50c086b48a3be27744bde7 + languageName: node + linkType: hard + +"pstree.remy@npm:^1.1.8": + version: 1.1.8 + resolution: "pstree.remy@npm:1.1.8" + checksum: 5cb53698d6bb34dfb278c8a26957964aecfff3e161af5fbf7cee00bbe9d8547c7aced4bd9cb193bce15fb56e9e4220fc02a5bf9c14345ffb13a36b858701ec2d + languageName: node + linkType: hard + +"public-encrypt@npm:^4.0.0": + version: 4.0.3 + resolution: "public-encrypt@npm:4.0.3" + dependencies: + bn.js: ^4.1.0 + browserify-rsa: ^4.0.0 + create-hash: ^1.1.0 + parse-asn1: ^5.0.0 + randombytes: ^2.0.1 + safe-buffer: ^5.1.2 + checksum: 215d446e43cef021a20b67c1df455e5eea134af0b1f9b8a35f9e850abf32991b0c307327bc5b9bc07162c288d5cdb3d4a783ea6c6640979ed7b5017e3e0c9935 + languageName: node + linkType: hard + +"public-ip@npm:^4.0.1": + version: 4.0.4 + resolution: "public-ip@npm:4.0.4" + dependencies: + dns-socket: ^4.2.2 + got: ^9.6.0 + is-ip: ^3.1.0 + checksum: 9a0c3194b219d14e8996080f90857ca025c37ebe233cb89b7ba629714ebcdc6dfd563084931d497c597b56599468ec064b7a9196bbdc92feefa908d1e1585ea7 + languageName: node + linkType: hard + +"pump@npm:^3.0.0": + version: 3.0.0 + resolution: "pump@npm:3.0.0" + dependencies: + end-of-stream: ^1.1.0 + once: ^1.3.1 + checksum: e42e9229fba14732593a718b04cb5e1cfef8254544870997e0ecd9732b189a48e1256e4e5478148ecb47c8511dca2b09eae56b4d0aad8009e6fac8072923cfc9 + languageName: node + linkType: hard + +"punycode@npm:1.3.2": + version: 1.3.2 + resolution: "punycode@npm:1.3.2" + checksum: b8807fd594b1db33335692d1f03e8beeddde6fda7fbb4a2e32925d88d20a3aa4cd8dcc0c109ccaccbd2ba761c208dfaaada83007087ea8bfb0129c9ef1b99ed6 + languageName: node + linkType: hard + +"punycode@npm:^1.3.2": + version: 1.4.1 + resolution: "punycode@npm:1.4.1" + checksum: fa6e698cb53db45e4628559e557ddaf554103d2a96a1d62892c8f4032cd3bc8871796cae9eabc1bc700e2b6677611521ce5bb1d9a27700086039965d0cf34518 + languageName: node + linkType: hard + +"punycode@npm:^2.1.0, punycode@npm:^2.1.1": + version: 2.1.1 + resolution: "punycode@npm:2.1.1" + checksum: 823bf443c6dd14f669984dea25757b37993f67e8d94698996064035edd43bed8a5a17a9f12e439c2b35df1078c6bec05a6c86e336209eb1061e8025c481168e8 + languageName: node + linkType: hard + +"pupa@npm:^2.1.1": + version: 2.1.1 + resolution: "pupa@npm:2.1.1" + dependencies: + escape-goat: ^2.0.0 + checksum: 49529e50372ffdb0cccf0efa0f3b3cb0a2c77805d0d9cc2725bd2a0f6bb414631e61c93a38561b26be1259550b7bb6c2cb92315aa09c8bf93f3bdcb49f2b2fb7 + languageName: node + linkType: hard + +"q@npm:^1.5.1": + version: 1.5.1 + resolution: "q@npm:1.5.1" + checksum: 147baa93c805bc1200ed698bdf9c72e9e42c05f96d007e33a558b5fdfd63e5ea130e99313f28efc1783e90e6bdb4e48b67a36fcc026b7b09202437ae88a1fb12 + languageName: node + linkType: hard + +"qjobs@npm:^1.2.0": + version: 1.2.0 + resolution: "qjobs@npm:1.2.0" + checksum: eb64c00724d2fecaf9246383b4eebc3a4c34845b25d41921dd57f41b30a4310cef661543facac27ceb6911aab64a1acdf45b5d8f1d5e2838554d0c010ee56852 + languageName: node + linkType: hard + +"qqjs@npm:^0.3.11": + version: 0.3.11 + resolution: "qqjs@npm:0.3.11" + dependencies: + chalk: ^2.4.1 + debug: ^4.1.1 + execa: ^0.10.0 + fs-extra: ^6.0.1 + get-stream: ^5.1.0 + glob: ^7.1.2 + globby: ^10.0.1 + http-call: ^5.1.2 + load-json-file: ^6.2.0 + pkg-dir: ^4.2.0 + tar-fs: ^2.0.0 + tmp: ^0.1.0 + write-json-file: ^4.1.1 + checksum: 7962df855b7a0405550ae39beb5c133574a11db475635d4fb6311c469d83cf9cae03efa4c8a0e02b82a4cae9d7bec072383354249f12ab136dc8590e57a40dbd + languageName: node + linkType: hard + +"qs@npm:6.7.0": + version: 6.7.0 + resolution: "qs@npm:6.7.0" + checksum: dfd5f6adef50e36e908cfa70a6233871b5afe66fbaca37ecc1da352ba29eb2151a3797991948f158bb37fccde51bd57845cb619a8035287bfc24e4591172c347 + languageName: node + linkType: hard + +"qs@npm:~6.5.2": + version: 6.5.2 + resolution: "qs@npm:6.5.2" + checksum: 24af7b9928ba2141233fba2912876ff100403dba1b08b20c3b490da9ea6c636760445ea2211a079e7dfa882a5cf8f738337b3748c8bdd0f93358fa8881d2db8f + languageName: node + linkType: hard + +"querystring-es3@npm:~0.2.0": + version: 0.2.1 + resolution: "querystring-es3@npm:0.2.1" + checksum: 691e8d6b8b157e7cd49ae8e83fcf86de39ab3ba948c25abaa94fba84c0986c641aa2f597770848c64abce290ed17a39c9df6df737dfa7e87c3b63acc7d225d61 + languageName: node + linkType: hard + +"querystring@npm:0.2.0": + version: 0.2.0 + resolution: "querystring@npm:0.2.0" + checksum: 8258d6734f19be27e93f601758858c299bdebe71147909e367101ba459b95446fbe5b975bf9beb76390156a592b6f4ac3a68b6087cea165c259705b8b4e56a69 + languageName: node + linkType: hard + +"queue-microtask@npm:^1.2.2": + version: 1.2.3 + resolution: "queue-microtask@npm:1.2.3" + checksum: b676f8c040cdc5b12723ad2f91414d267605b26419d5c821ff03befa817ddd10e238d22b25d604920340fd73efd8ba795465a0377c4adf45a4a41e4234e42dc4 + languageName: node + linkType: hard + +"quick-format-unescaped@npm:^4.0.3": + version: 4.0.4 + resolution: "quick-format-unescaped@npm:4.0.4" + checksum: 7bc32b99354a1aa46c089d2a82b63489961002bb1d654cee3e6d2d8778197b68c2d854fd23d8422436ee1fdfd0abaddc4d4da120afe700ade68bd357815b26fd + languageName: node + linkType: hard + +"quick-lru@npm:^4.0.1": + version: 4.0.1 + resolution: "quick-lru@npm:4.0.1" + checksum: bea46e1abfaa07023e047d3cf1716a06172c4947886c053ede5c50321893711577cb6119360f810cc3ffcd70c4d7db4069c3cee876b358ceff8596e062bd1154 + languageName: node + linkType: hard + +"randombytes@npm:^2.0.0, randombytes@npm:^2.0.1, randombytes@npm:^2.0.5, randombytes@npm:^2.1.0": + version: 2.1.0 + resolution: "randombytes@npm:2.1.0" + dependencies: + safe-buffer: ^5.1.0 + checksum: d779499376bd4cbb435ef3ab9a957006c8682f343f14089ed5f27764e4645114196e75b7f6abf1cbd84fd247c0cb0651698444df8c9bf30e62120fbbc52269d6 + languageName: node + linkType: hard + +"randomfill@npm:^1.0.3": + version: 1.0.4 + resolution: "randomfill@npm:1.0.4" + dependencies: + randombytes: ^2.0.5 + safe-buffer: ^5.1.0 + checksum: 33734bb578a868d29ee1b8555e21a36711db084065d94e019a6d03caa67debef8d6a1bfd06a2b597e32901ddc761ab483a85393f0d9a75838f1912461d4dbfc7 + languageName: node + linkType: hard + +"range-parser@npm:^1.2.1": + version: 1.2.1 + resolution: "range-parser@npm:1.2.1" + checksum: 0a268d4fea508661cf5743dfe3d5f47ce214fd6b7dec1de0da4d669dd4ef3d2144468ebe4179049eff253d9d27e719c88dae55be64f954e80135a0cada804ec9 + languageName: node + linkType: hard + +"raw-body@npm:2.4.0": + version: 2.4.0 + resolution: "raw-body@npm:2.4.0" + dependencies: + bytes: 3.1.0 + http-errors: 1.7.2 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + checksum: 6343906939e018c6e633a34a938a5d6d1e93ffcfa48646e00207d53b418e941953b521473950c079347220944dc75ba10e7b3c08bf97e3ac72c7624882db09bb + languageName: node + linkType: hard + +"rc@npm:^1.2.8": + version: 1.2.8 + resolution: "rc@npm:1.2.8" + dependencies: + deep-extend: ^0.6.0 + ini: ~1.3.0 + minimist: ^1.2.0 + strip-json-comments: ~2.0.1 + bin: + rc: ./cli.js + checksum: 2e26e052f8be2abd64e6d1dabfbd7be03f80ec18ccbc49562d31f617d0015fbdbcf0f9eed30346ea6ab789e0fdfe4337f033f8016efdbee0df5354751842080e + languageName: node + linkType: hard + +"react-is@npm:^17.0.1": + version: 17.0.2 + resolution: "react-is@npm:17.0.2" + checksum: 9d6d111d8990dc98bc5402c1266a808b0459b5d54830bbea24c12d908b536df7883f268a7868cfaedde3dd9d4e0d574db456f84d2e6df9c4526f99bb4b5344d8 + languageName: node + linkType: hard + +"read-cmd-shim@npm:^2.0.0": + version: 2.0.0 + resolution: "read-cmd-shim@npm:2.0.0" + checksum: 024f0a092d3630ad344af63eb0539bce90978883dd06a93e7bfbb26913168ab034473eae4a85685ea76a982eb31b0e8e16dee9c1138dabb3a925e7c4757952bc + languageName: node + linkType: hard + +"read-only-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "read-only-stream@npm:2.0.0" + dependencies: + readable-stream: ^2.0.2 + checksum: aa48979d1f0e8a83522e60698cf3375dca7b284dd066758ded7c3539613ac08275f94dfe0503d2bdfe964ef3cb65facb87a4b3a8250e5a7e89d07af4451019d8 + languageName: node + linkType: hard + +"read-package-json-fast@npm:^2.0.1, read-package-json-fast@npm:^2.0.2, read-package-json-fast@npm:^2.0.3": + version: 2.0.3 + resolution: "read-package-json-fast@npm:2.0.3" + dependencies: + json-parse-even-better-errors: ^2.3.0 + npm-normalize-package-bin: ^1.0.1 + checksum: fca37b3b2160b9dda7c5588b767f6a2b8ce68d03a044000e568208e20bea0cf6dd2de17b90740ce8da8b42ea79c0b3859649dadf29510bbe77224ea65326a903 + languageName: node + linkType: hard + +"read-pkg-up@npm:^3.0.0": + version: 3.0.0 + resolution: "read-pkg-up@npm:3.0.0" + dependencies: + find-up: ^2.0.0 + read-pkg: ^3.0.0 + checksum: 16175573f2914ab9788897bcbe2a62b5728d0075e62285b3680cebe97059e2911e0134a062cf6e51ebe3e3775312bc788ac2039ed6af38ec68d2c10c6f2b30fb + languageName: node + linkType: hard + +"read-pkg-up@npm:^7.0.1": + version: 7.0.1 + resolution: "read-pkg-up@npm:7.0.1" + dependencies: + find-up: ^4.1.0 + read-pkg: ^5.2.0 + type-fest: ^0.8.1 + checksum: e4e93ce70e5905b490ca8f883eb9e48b5d3cebc6cd4527c25a0d8f3ae2903bd4121c5ab9c5a3e217ada0141098eeb661313c86fa008524b089b8ed0b7f165e44 + languageName: node + linkType: hard + +"read-pkg@npm:^3.0.0": + version: 3.0.0 + resolution: "read-pkg@npm:3.0.0" + dependencies: + load-json-file: ^4.0.0 + normalize-package-data: ^2.3.2 + path-type: ^3.0.0 + checksum: 398903ebae6c7e9965419a1062924436cc0b6f516c42c4679a90290d2f87448ed8f977e7aa2dbba4aa1ac09248628c43e493ac25b2bc76640e946035200e34c6 + languageName: node + linkType: hard + +"read-pkg@npm:^5.2.0": + version: 5.2.0 + resolution: "read-pkg@npm:5.2.0" + dependencies: + "@types/normalize-package-data": ^2.4.0 + normalize-package-data: ^2.5.0 + parse-json: ^5.0.0 + type-fest: ^0.6.0 + checksum: eb696e60528b29aebe10e499ba93f44991908c57d70f2d26f369e46b8b9afc208ef11b4ba64f67630f31df8b6872129e0a8933c8c53b7b4daf0eace536901222 + languageName: node + linkType: hard + +"readable-stream@npm:2 || 3, readable-stream@npm:3, readable-stream@npm:^3.0.0, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0": + version: 3.6.0 + resolution: "readable-stream@npm:3.6.0" + dependencies: + inherits: ^2.0.3 + string_decoder: ^1.1.1 + util-deprecate: ^1.0.1 + checksum: d4ea81502d3799439bb955a3a5d1d808592cf3133350ed352aeaa499647858b27b1c4013984900238b0873ec8d0d8defce72469fb7a83e61d53f5ad61cb80dc8 + languageName: node + linkType: hard + +"readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.2, readable-stream@npm:^2.0.6, readable-stream@npm:^2.2.2, readable-stream@npm:^2.3.5, readable-stream@npm:^2.3.7, readable-stream@npm:~2.3.6": + version: 2.3.7 + resolution: "readable-stream@npm:2.3.7" + 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 + checksum: e4920cf7549a60f8aaf694d483a0e61b2a878b969d224f89b3bc788b8d920075132c4b55a7494ee944c7b6a9a0eada28a7f6220d80b0312ece70bbf08eeca755 + languageName: node + linkType: hard + +"readable-stream@npm:~1.0.2": + version: 1.0.34 + resolution: "readable-stream@npm:1.0.34" + dependencies: + core-util-is: ~1.0.0 + inherits: ~2.0.1 + isarray: 0.0.1 + string_decoder: ~0.10.x + checksum: 85042c537e4f067daa1448a7e257a201070bfec3dd2706abdbd8ebc7f3418eb4d3ed4b8e5af63e2544d69f88ab09c28d5da3c0b77dc76185fddd189a59863b60 + languageName: node + linkType: hard + +"readdir-scoped-modules@npm:^1.1.0": + version: 1.1.0 + resolution: "readdir-scoped-modules@npm:1.1.0" + dependencies: + debuglog: ^1.0.1 + dezalgo: ^1.0.0 + graceful-fs: ^4.1.2 + once: ^1.3.0 + checksum: 6d9f334e40dfd0f5e4a8aab5e67eb460c95c85083c690431f87ab2c9135191170e70c2db6d71afcafb78e073d23eb95dcb3fc33ef91308f6ebfe3197be35e608 + languageName: node + linkType: hard + +"readdirp@npm:~3.6.0": + version: 3.6.0 + resolution: "readdirp@npm:3.6.0" + dependencies: + picomatch: ^2.2.1 + checksum: 1ced032e6e45670b6d7352d71d21ce7edf7b9b928494dcaba6f11fba63180d9da6cd7061ebc34175ffda6ff529f481818c962952004d273178acd70f7059b320 + languageName: node + linkType: hard + +"rechoir@npm:^0.6.2": + version: 0.6.2 + resolution: "rechoir@npm:0.6.2" + dependencies: + resolve: ^1.1.6 + checksum: fe76bf9c21875ac16e235defedd7cbd34f333c02a92546142b7911a0f7c7059d2e16f441fe6fb9ae203f459c05a31b2bcf26202896d89e390eda7514d5d2702b + languageName: node + linkType: hard + +"rechoir@npm:^0.7.0": + version: 0.7.1 + resolution: "rechoir@npm:0.7.1" + dependencies: + resolve: ^1.9.0 + checksum: 2a04aab4e28c05fcd6ee6768446bc8b859d8f108e71fc7f5bcbc5ef25e53330ce2c11d10f82a24591a2df4c49c4f61feabe1fd11f844c66feedd4cd7bb61146a + languageName: node + linkType: hard + +"redent@npm:^3.0.0": + version: 3.0.0 + resolution: "redent@npm:3.0.0" + dependencies: + indent-string: ^4.0.0 + strip-indent: ^3.0.0 + checksum: fa1ef20404a2d399235e83cc80bd55a956642e37dd197b4b612ba7327bf87fa32745aeb4a1634b2bab25467164ab4ed9c15be2c307923dd08b0fe7c52431ae6b + languageName: node + linkType: hard + +"redeyed@npm:~2.1.0": + version: 2.1.1 + resolution: "redeyed@npm:2.1.1" + dependencies: + esprima: ~4.0.0 + checksum: 39a1426e377727cfb47a0e24e95c1cf78d969fbc388dc1e0fa1e2ef8a8756450cefb8b0c2598f63b85f1a331986fca7604c0db798427a5775a1dbdb9c1291979 + languageName: node + linkType: hard + +"regenerate-unicode-properties@npm:^9.0.0": + version: 9.0.0 + resolution: "regenerate-unicode-properties@npm:9.0.0" + dependencies: + regenerate: ^1.4.2 + checksum: 62df21c274259a68c6fa1373e5ddb4d6f6374ad72c08dd488b7802880bc1c3b6de716303ec56c9f793a73d01815e9d81f03a8fbe3f32bc0f7fdf8d70d4841b64 + languageName: node + linkType: hard + +"regenerate@npm:^1.4.2": + version: 1.4.2 + resolution: "regenerate@npm:1.4.2" + checksum: 3317a09b2f802da8db09aa276e469b57a6c0dd818347e05b8862959c6193408242f150db5de83c12c3fa99091ad95fb42a6db2c3329bfaa12a0ea4cbbeb30cb0 + languageName: node + linkType: hard + +"regenerator-runtime@npm:^0.13.4": + version: 0.13.9 + resolution: "regenerator-runtime@npm:0.13.9" + checksum: 65ed455fe5afd799e2897baf691ca21c2772e1a969d19bb0c4695757c2d96249eb74ee3553ea34a91062b2a676beedf630b4c1551cc6299afb937be1426ec55e + languageName: node + linkType: hard + +"regenerator-transform@npm:^0.14.2": + version: 0.14.5 + resolution: "regenerator-transform@npm:0.14.5" + dependencies: + "@babel/runtime": ^7.8.4 + checksum: a467a3b652b4ec26ff964e9c5f1817523a73fc44cb928b8d21ff11aebeac5d10a84d297fe02cea9f282bcec81a0b0d562237da69ef0f40a0160b30a4fa98bc94 + languageName: node + linkType: hard + +"regexpp@npm:^3.1.0": + version: 3.2.0 + resolution: "regexpp@npm:3.2.0" + checksum: a78dc5c7158ad9ddcfe01aa9144f46e192ddbfa7b263895a70a5c6c73edd9ce85faf7c0430e59ac38839e1734e275b9c3de5c57ee3ab6edc0e0b1bdebefccef8 + languageName: node + linkType: hard + +"regexpu-core@npm:^4.7.1": + version: 4.8.0 + resolution: "regexpu-core@npm:4.8.0" + dependencies: + regenerate: ^1.4.2 + regenerate-unicode-properties: ^9.0.0 + regjsgen: ^0.5.2 + regjsparser: ^0.7.0 + unicode-match-property-ecmascript: ^2.0.0 + unicode-match-property-value-ecmascript: ^2.0.0 + checksum: df92e3e6482409f0a0de162ca1b4e17897e9b0b0687caead6804f04e9b89847e47abbfd0bfc62f52a0b833acf764ea5bdb7b707bb088034824a675ee95d31dec + languageName: node + linkType: hard + +"regextras@npm:^0.7.1": + version: 0.7.1 + resolution: "regextras@npm:0.7.1" + checksum: ffcd5bfd5842564ed4db94af58ee3280cfc00bd19c235afc4b681be0f84b60ac2d3da345af5e92099b2b05d4d8b7063ab720c92bfee1162b2b7ae063d879baec + languageName: node + linkType: hard + +"registry-auth-token@npm:^4.0.0": + version: 4.2.1 + resolution: "registry-auth-token@npm:4.2.1" + dependencies: + rc: ^1.2.8 + checksum: aa72060b573a50607cfd2dee16d0e51e13ca58b6a80442e74545325dc24d2c38896e6bad229bdcc1fc9759fa81b4066be8693d4d6f45927318e7c793a93e9cd0 + languageName: node + linkType: hard + +"registry-url@npm:^5.0.0": + version: 5.1.0 + resolution: "registry-url@npm:5.1.0" + dependencies: + rc: ^1.2.8 + checksum: bcea86c84a0dbb66467b53187fadebfea79017cddfb4a45cf27530d7275e49082fe9f44301976eb0164c438e395684bcf3dae4819b36ff9d1640d8cc60c73df9 + languageName: node + linkType: hard + +"regjsgen@npm:^0.5.2": + version: 0.5.2 + resolution: "regjsgen@npm:0.5.2" + checksum: 87c83d8488affae2493a823904de1a29a1867a07433c5e1142ad749b5606c5589b305fe35bfcc0972cf5a3b0d66b1f7999009e541be39a5d42c6041c59e2fb52 + languageName: node + linkType: hard + +"regjsparser@npm:^0.7.0": + version: 0.7.0 + resolution: "regjsparser@npm:0.7.0" + dependencies: + jsesc: ~0.5.0 + bin: + regjsparser: bin/parser + checksum: fefff9adcab47650817d2c492aac774f11a44b824a4a814e466ebc76313e03e79c50d2babde7e04888296f6ec0fd094e3eeeafa8122c60184de92cdb30636a57 + languageName: node + linkType: hard + +"release-zalgo@npm:^1.0.0": + version: 1.0.0 + resolution: "release-zalgo@npm:1.0.0" + dependencies: + es6-error: ^4.0.1 + checksum: b59849dc310f6c426f34e308c48ba83df3d034ddef75189951723bb2aac99d29d15f5e127edad951c4095fc9025aa582053907154d68fe0c5380cd6a75365e53 + languageName: node + linkType: hard + +"remove-trailing-separator@npm:^1.0.1": + version: 1.1.0 + resolution: "remove-trailing-separator@npm:1.1.0" + checksum: d3c20b5a2d987db13e1cca9385d56ecfa1641bae143b620835ac02a6b70ab88f68f117a0021838db826c57b31373d609d52e4f31aca75fc490c862732d595419 + languageName: node + linkType: hard + +"replace-ext@npm:^1.0.0": + version: 1.0.1 + resolution: "replace-ext@npm:1.0.1" + checksum: 4994ea1aaa3d32d152a8d98ff638988812c4fa35ba55485630008fe6f49e3384a8a710878e6fd7304b42b38d1b64c1cd070e78ece411f327735581a79dd88571 + languageName: node + linkType: hard + +"request-promise-core@npm:1.1.4": + version: 1.1.4 + resolution: "request-promise-core@npm:1.1.4" + dependencies: + lodash: ^4.17.19 + peerDependencies: + request: ^2.34 + checksum: c798bafd552961e36fbf5023b1d081e81c3995ab390f1bc8ef38a711ba3fe4312eb94dbd61887073d7356c3499b9380947d7f62faa805797c0dc50f039425699 + languageName: node + linkType: hard + +"request-promise-native@npm:^1.0.5": + version: 1.0.9 + resolution: "request-promise-native@npm:1.0.9" + dependencies: + request-promise-core: 1.1.4 + stealthy-require: ^1.1.1 + tough-cookie: ^2.3.3 + peerDependencies: + request: ^2.34 + checksum: 3e2c694eefac88cb20beef8911ad57a275ab3ccbae0c4ca6c679fffb09d5fd502458aab08791f0814ca914b157adab2d4e472597c97a73be702918e41725ed69 + languageName: node + linkType: hard + +"request@npm:^2.87.0": + version: 2.88.2 + resolution: "request@npm:2.88.2" + dependencies: + aws-sign2: ~0.7.0 + aws4: ^1.8.0 + caseless: ~0.12.0 + combined-stream: ~1.0.6 + extend: ~3.0.2 + forever-agent: ~0.6.1 + form-data: ~2.3.2 + har-validator: ~5.1.3 + http-signature: ~1.2.0 + is-typedarray: ~1.0.0 + isstream: ~0.1.2 + json-stringify-safe: ~5.0.1 + mime-types: ~2.1.19 + oauth-sign: ~0.9.0 + performance-now: ^2.1.0 + qs: ~6.5.2 + safe-buffer: ^5.1.2 + tough-cookie: ~2.5.0 + tunnel-agent: ^0.6.0 + uuid: ^3.3.2 + checksum: 4e112c087f6eabe7327869da2417e9d28fcd0910419edd2eb17b6acfc4bfa1dad61954525949c228705805882d8a98a86a0ea12d7f739c01ee92af7062996983 + languageName: node + linkType: hard + +"require-at@npm:^1.0.6": + version: 1.0.6 + resolution: "require-at@npm:1.0.6" + checksum: 7753a6ebad99855ef015d5533a787c65e883c94c23371368eebf6f1c7e2a078811013b204823152cbab206a00e825e8e5ca09416fd835a489fa30bf064fbe6d9 + languageName: node + linkType: hard + +"require-directory@npm:^2.1.1": + version: 2.1.1 + resolution: "require-directory@npm:2.1.1" + checksum: fb47e70bf0001fdeabdc0429d431863e9475e7e43ea5f94ad86503d918423c1543361cc5166d713eaa7029dd7a3d34775af04764bebff99ef413111a5af18c80 + languageName: node + linkType: hard + +"require-from-string@npm:^2.0.2": + version: 2.0.2 + resolution: "require-from-string@npm:2.0.2" + checksum: a03ef6895445f33a4015300c426699bc66b2b044ba7b670aa238610381b56d3f07c686251740d575e22f4c87531ba662d06937508f0f3c0f1ddc04db3130560b + languageName: node + linkType: hard + +"require-main-filename@npm:^2.0.0": + version: 2.0.0 + resolution: "require-main-filename@npm:2.0.0" + checksum: e9e294695fea08b076457e9ddff854e81bffbe248ed34c1eec348b7abbd22a0d02e8d75506559e2265e96978f3c4720bd77a6dad84755de8162b357eb6c778c7 + languageName: node + linkType: hard + +"requires-port@npm:^1.0.0": + version: 1.0.0 + resolution: "requires-port@npm:1.0.0" + checksum: eee0e303adffb69be55d1a214e415cf42b7441ae858c76dfc5353148644f6fd6e698926fc4643f510d5c126d12a705e7c8ed7e38061113bdf37547ab356797ff + languageName: node + linkType: hard + +"resolve-cwd@npm:^3.0.0": + version: 3.0.0 + resolution: "resolve-cwd@npm:3.0.0" + dependencies: + resolve-from: ^5.0.0 + checksum: 546e0816012d65778e580ad62b29e975a642989108d9a3c5beabfb2304192fa3c9f9146fbdfe213563c6ff51975ae41bac1d3c6e047dd9572c94863a057b4d81 + languageName: node + linkType: hard + +"resolve-from@npm:^4.0.0": + version: 4.0.0 + resolution: "resolve-from@npm:4.0.0" + checksum: f4ba0b8494846a5066328ad33ef8ac173801a51739eb4d63408c847da9a2e1c1de1e6cbbf72699211f3d13f8fc1325648b169bd15eb7da35688e30a5fb0e4a7f + languageName: node + linkType: hard + +"resolve-from@npm:^5.0.0": + version: 5.0.0 + resolution: "resolve-from@npm:5.0.0" + checksum: 4ceeb9113e1b1372d0cd969f3468fa042daa1dd9527b1b6bb88acb6ab55d8b9cd65dbf18819f9f9ddf0db804990901dcdaade80a215e7b2c23daae38e64f5bdf + languageName: node + linkType: hard + +"resolve@^1.1.4, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.4.0, resolve@^1.9.0, resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.12.0": + version: 1.22.0 + resolution: "resolve@npm:1.22.0" + dependencies: + is-core-module: ^2.8.1 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: a2d14cc437b3a23996f8c7367eee5c7cf8149c586b07ca2ae00e96581ce59455555a1190be9aa92154785cf9f2042646c200d0e00e0bbd2b8a995a93a0ed3e4e + languageName: node + linkType: hard + +"resolve@patch:resolve@^1.1.4#~builtin, resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.12.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.17.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.4.0#~builtin, resolve@patch:resolve@^1.9.0#~builtin": + version: 1.22.0 + resolution: "resolve@patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b" + dependencies: + is-core-module: ^2.8.1 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: c79ecaea36c872ee4a79e3db0d3d4160b593f2ca16e031d8283735acd01715a203607e9ded3f91f68899c2937fa0d49390cddbe0fb2852629212f3cda283f4a7 + languageName: node + linkType: hard + +"responselike@npm:^1.0.2": + version: 1.0.2 + resolution: "responselike@npm:1.0.2" + dependencies: + lowercase-keys: ^1.0.0 + checksum: 2e9e70f1dcca3da621a80ce71f2f9a9cad12c047145c6ece20df22f0743f051cf7c73505e109814915f23f9e34fb0d358e22827723ee3d56b623533cab8eafcd + languageName: node + linkType: hard + +"restore-cursor@npm:^3.1.0": + version: 3.1.0 + resolution: "restore-cursor@npm:3.1.0" + dependencies: + onetime: ^5.1.0 + signal-exit: ^3.0.2 + checksum: f877dd8741796b909f2a82454ec111afb84eb45890eb49ac947d87991379406b3b83ff9673a46012fca0d7844bb989f45cc5b788254cf1a39b6b5a9659de0630 + languageName: node + linkType: hard + +"ret@npm:~0.2.0": + version: 0.2.2 + resolution: "ret@npm:0.2.2" + checksum: 774964bb413a3525e687bca92d81c1cd75555ec33147c32ecca22f3d06409e35df87952cfe3d57afff7650a0f7e42139cf60cb44e94c29dde390243bc1941f16 + languageName: node + linkType: hard + +"retry@npm:^0.12.0": + version: 0.12.0 + resolution: "retry@npm:0.12.0" + checksum: 623bd7d2e5119467ba66202d733ec3c2e2e26568074923bc0585b6b99db14f357e79bdedb63cab56cec47491c4a0da7e6021a7465ca6dc4f481d3898fdd3158c + languageName: node + linkType: hard + +"reusify@npm:^1.0.4": + version: 1.0.4 + resolution: "reusify@npm:1.0.4" + checksum: c3076ebcc22a6bc252cb0b9c77561795256c22b757f40c0d8110b1300723f15ec0fc8685e8d4ea6d7666f36c79ccc793b1939c748bf36f18f542744a4e379fcc + languageName: node + linkType: hard + +"rfdc@npm:^1.1.4, rfdc@npm:^1.3.0": + version: 1.3.0 + resolution: "rfdc@npm:1.3.0" + checksum: fb2ba8512e43519983b4c61bd3fa77c0f410eff6bae68b08614437bc3f35f91362215f7b4a73cbda6f67330b5746ce07db5dd9850ad3edc91271ad6deea0df32 + languageName: node + linkType: hard + +"rimraf@npm:^2.5.4, rimraf@npm:^2.6.3": + version: 2.7.1 + resolution: "rimraf@npm:2.7.1" + dependencies: + glob: ^7.1.3 + bin: + rimraf: ./bin.js + checksum: cdc7f6eacb17927f2a075117a823e1c5951792c6498ebcce81ca8203454a811d4cf8900314154d3259bb8f0b42ab17f67396a8694a54cae3283326e57ad250cd + languageName: node + linkType: hard + +"rimraf@npm:^3.0.0, rimraf@npm:^3.0.2": + version: 3.0.2 + resolution: "rimraf@npm:3.0.2" + dependencies: + glob: ^7.1.3 + bin: + rimraf: bin.js + checksum: 87f4164e396f0171b0a3386cc1877a817f572148ee13a7e113b238e48e8a9f2f31d009a92ec38a591ff1567d9662c6b67fd8818a2dbbaed74bc26a87a2a4a9a0 + languageName: node + linkType: hard + +"ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1": + version: 2.0.2 + resolution: "ripemd160@npm:2.0.2" + dependencies: + hash-base: ^3.0.0 + inherits: ^2.0.1 + checksum: 006accc40578ee2beae382757c4ce2908a826b27e2b079efdcd2959ee544ddf210b7b5d7d5e80467807604244e7388427330f5c6d4cd61e6edaddc5773ccc393 + languageName: node + linkType: hard + +"run-async@npm:^2.0.0, run-async@npm:^2.4.0": + version: 2.4.1 + resolution: "run-async@npm:2.4.1" + checksum: a2c88aa15df176f091a2878eb840e68d0bdee319d8d97bbb89112223259cebecb94bc0defd735662b83c2f7a30bed8cddb7d1674eb48ae7322dc602b22d03797 + languageName: node + linkType: hard + +"run-parallel@npm:^1.1.9": + version: 1.2.0 + resolution: "run-parallel@npm:1.2.0" + dependencies: + queue-microtask: ^1.2.2 + checksum: cb4f97ad25a75ebc11a8ef4e33bb962f8af8516bb2001082ceabd8902e15b98f4b84b4f8a9b222e5d57fc3bd1379c483886ed4619367a7680dad65316993021d + languageName: node + linkType: hard + +"rxjs@npm:^6.6.3, rxjs@npm:^6.6.7": + version: 6.6.7 + resolution: "rxjs@npm:6.6.7" + dependencies: + tslib: ^1.9.0 + checksum: bc334edef1bb8bbf56590b0b25734ba0deaf8825b703256a93714308ea36dff8a11d25533671adf8e104e5e8f256aa6fdfe39b2e248cdbd7a5f90c260acbbd1b + languageName: node + linkType: hard + +"rxjs@npm:^7.2.0": + version: 7.5.4 + resolution: "rxjs@npm:7.5.4" + dependencies: + tslib: ^2.1.0 + checksum: 6f55f835f2543bc8214900f9e28b6320e6adc95875011fbca63e80a66eb18c9ff7cfdccb23b2180cbb6412762b98ed158c89fd51cb020799d127c66ea38c3c0e + languageName: node + linkType: hard + +"safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:~5.2.0": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 + languageName: node + linkType: hard + +"safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": + version: 5.1.2 + resolution: "safe-buffer@npm:5.1.2" + checksum: f2f1f7943ca44a594893a852894055cf619c1fbcb611237fc39e461ae751187e7baf4dc391a72125e0ac4fb2d8c5c0b3c71529622e6a58f46b960211e704903c + languageName: node + linkType: hard + +"safe-regex2@npm:^2.0.0": + version: 2.0.0 + resolution: "safe-regex2@npm:2.0.0" + dependencies: + ret: ~0.2.0 + checksum: f5e182fca040dedd50ae052ea0eb035d9903b2db71243d5d8b43299735857288ef2ab52546a368d9c6fd1333b2a0d039297925e78ffc14845354f3f6158af7c2 + languageName: node + linkType: hard + +"safe-stable-stringify@npm:^1.1.0": + version: 1.1.1 + resolution: "safe-stable-stringify@npm:1.1.1" + checksum: e32a30720e8a2e3043b8b96733f015c1aa7a21a5a328074ce917b8afe4d26b4308c186c74fa92131e5f794b1efc63caa32defafceaa2981accaaedbc8b2c861c + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: cab8f25ae6f1434abee8d80023d7e72b598cf1327164ddab31003c51215526801e40b66c5e65d658a0af1e9d6478cadcb4c745f4bd6751f97d8644786c0978b0 + languageName: node + linkType: hard + +"saslprep@npm:^1.0.0": + version: 1.0.3 + resolution: "saslprep@npm:1.0.3" + dependencies: + sparse-bitfield: ^3.0.3 + checksum: 4fdc0b70fb5e523f977de405e12cca111f1f10dd68a0cfae0ca52c1a7919a94d1556598ba2d35f447655c3b32879846c77f9274c90806f6673248ae3cea6ee43 + languageName: node + linkType: hard + +"sax@npm:1.2.1": + version: 1.2.1 + resolution: "sax@npm:1.2.1" + checksum: 8dca7d5e1cd7d612f98ac50bdf0b9f63fbc964b85f0c4e2eb271f8b9b47fd3bf344c4d6a592e69ecf726d1485ca62cd8a52e603bbc332d18a66af25a9a1045ad + languageName: node + linkType: hard + +"sax@npm:>=0.6.0": + version: 1.2.4 + resolution: "sax@npm:1.2.4" + checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe + languageName: node + linkType: hard + +"schema-utils@npm:^2.6.5": + version: 2.7.1 + resolution: "schema-utils@npm:2.7.1" + dependencies: + "@types/json-schema": ^7.0.5 + ajv: ^6.12.4 + ajv-keywords: ^3.5.2 + checksum: 32c62fc9e28edd101e1bd83453a4216eb9bd875cc4d3775e4452b541908fa8f61a7bbac8ffde57484f01d7096279d3ba0337078e85a918ecbeb72872fb09fb2b + languageName: node + linkType: hard + +"schema-utils@npm:^3.1.0, schema-utils@npm:^3.1.1": + version: 3.1.1 + resolution: "schema-utils@npm:3.1.1" + dependencies: + "@types/json-schema": ^7.0.8 + ajv: ^6.12.5 + ajv-keywords: ^3.5.2 + checksum: fb73f3d759d43ba033c877628fe9751620a26879f6301d3dbeeb48cf2a65baec5cdf99da65d1bf3b4ff5444b2e59cbe4f81c2456b5e0d2ba7d7fd4aed5da29ce + languageName: node + linkType: hard + +"scoped-regex@npm:^2.0.0": + version: 2.1.0 + resolution: "scoped-regex@npm:2.1.0" + checksum: 4e820444cb79727bb302d94dafe07999cce18b6026e4866583466821b3d246403034bc46085e1f4b63ec99491b637540a7c74fb2a66c5c4287700ec357d8af86 + languageName: node + linkType: hard + +"seedrandom@npm:^3.0.5": + version: 3.0.5 + resolution: "seedrandom@npm:3.0.5" + checksum: 728b56bc3bc1b9ddeabd381e449b51cb31bdc0aa86e27fcd0190cea8c44613d5bcb2f6bb63ed79f78180cbe791c20b8ec31a9627f7b7fc7f476fd2bdb7e2da9f + languageName: node + linkType: hard + +"semver-diff@npm:^3.1.1": + version: 3.1.1 + resolution: "semver-diff@npm:3.1.1" + dependencies: + semver: ^6.3.0 + checksum: 8bbe5a5d7add2d5e51b72314a9215cd294d71f41cdc2bf6bd59ee76411f3610b576172896f1d191d0d7294cb9f2f847438d2ee158adacc0c224dca79052812fe + languageName: node + linkType: hard + +"semver-store@npm:^0.3.0": + version: 0.3.0 + resolution: "semver-store@npm:0.3.0" + checksum: b38f747123e850191526a912657c653c7e5963d164a8daf99e52aa30bc8c5bdac176dc6dab714e17a1a8489ac138c18ff7161b1961f1882888bce637990442dd + languageName: node + linkType: hard + +"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.4.1, semver@npm:^5.5.0, semver@npm:^5.7.1": + version: 5.7.1 + resolution: "semver@npm:5.7.1" + bin: + semver: ./bin/semver + checksum: 57fd0acfd0bac382ee87cd52cd0aaa5af086a7dc8d60379dfe65fea491fb2489b6016400813930ecd61fd0952dae75c115287a1b16c234b1550887117744dfaf + languageName: node + linkType: hard + +"semver@npm:7.0.0": + version: 7.0.0 + resolution: "semver@npm:7.0.0" + bin: + semver: bin/semver.js + checksum: 272c11bf8d083274ef79fe40a81c55c184dff84dd58e3c325299d0927ba48cece1f020793d138382b85f89bab5002a35a5ba59a3a68a7eebbb597eb733838778 + languageName: node + linkType: hard + +"semver@npm:^6.0.0, semver@npm:^6.1.1, semver@npm:^6.1.2, semver@npm:^6.2.0, semver@npm:^6.3.0": + version: 6.3.0 + resolution: "semver@npm:6.3.0" + bin: + semver: ./bin/semver.js + checksum: 1b26ecf6db9e8292dd90df4e781d91875c0dcc1b1909e70f5d12959a23c7eebb8f01ea581c00783bbee72ceeaad9505797c381756326073850dc36ed284b21b9 + languageName: node + linkType: hard + +"semver@npm:^7.1.1, semver@npm:^7.1.3, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5": + version: 7.3.5 + resolution: "semver@npm:7.3.5" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: 5eafe6102bea2a7439897c1856362e31cc348ccf96efd455c8b5bc2c61e6f7e7b8250dc26b8828c1d76a56f818a7ee907a36ae9fb37a599d3d24609207001d60 + languageName: node + linkType: hard + +"serialize-javascript@npm:6.0.0, serialize-javascript@npm:^6.0.0": + version: 6.0.0 + resolution: "serialize-javascript@npm:6.0.0" + dependencies: + randombytes: ^2.1.0 + checksum: 56f90b562a1bdc92e55afb3e657c6397c01a902c588c0fe3d4c490efdcc97dcd2a3074ba12df9e94630f33a5ce5b76a74784a7041294628a6f4306e0ec84bf93 + languageName: node + linkType: hard + +"set-blocking@npm:^2.0.0, set-blocking@npm:~2.0.0": + version: 2.0.0 + resolution: "set-blocking@npm:2.0.0" + checksum: 6e65a05f7cf7ebdf8b7c75b101e18c0b7e3dff4940d480efed8aad3a36a4005140b660fa1d804cb8bce911cac290441dc728084a30504d3516ac2ff7ad607b02 + languageName: node + linkType: hard + +"setimmediate@npm:^1.0.5": + version: 1.0.5 + resolution: "setimmediate@npm:1.0.5" + checksum: c9a6f2c5b51a2dabdc0247db9c46460152ffc62ee139f3157440bd48e7c59425093f42719ac1d7931f054f153e2d26cf37dfeb8da17a794a58198a2705e527fd + languageName: node + linkType: hard + +"setprototypeof@npm:1.1.1": + version: 1.1.1 + resolution: "setprototypeof@npm:1.1.1" + checksum: a8bee29c1c64c245d460ce53f7460af8cbd0aceac68d66e5215153992cc8b3a7a123416353e0c642060e85cc5fd4241c92d1190eec97eda0dcb97436e8fcca3b + languageName: node + linkType: hard + +"sha.js@npm:^2.4.0, sha.js@npm:^2.4.8, sha.js@npm:~2.4.4": + version: 2.4.11 + resolution: "sha.js@npm:2.4.11" + dependencies: + inherits: ^2.0.1 + safe-buffer: ^5.0.1 + bin: + sha.js: ./bin.js + checksum: ebd3f59d4b799000699097dadb831c8e3da3eb579144fd7eb7a19484cbcbb7aca3c68ba2bb362242eb09e33217de3b4ea56e4678184c334323eca24a58e3ad07 + languageName: node + linkType: hard + +"shallow-clone@npm:^3.0.0": + version: 3.0.1 + resolution: "shallow-clone@npm:3.0.1" + dependencies: + kind-of: ^6.0.2 + checksum: 39b3dd9630a774aba288a680e7d2901f5c0eae7b8387fc5c8ea559918b29b3da144b7bdb990d7ccd9e11be05508ac9e459ce51d01fd65e583282f6ffafcba2e7 + languageName: node + linkType: hard + +"shasum-object@npm:^1.0.0": + version: 1.0.0 + resolution: "shasum-object@npm:1.0.0" + dependencies: + fast-safe-stringify: ^2.0.7 + checksum: fc3531b7ae6ca1cc76138bec54896ee61ff4e7cc62e37ebd47963c8c92f867c6232332e21437dbca60c9109e077b38ece631b59b045e10e0502949363e337895 + languageName: node + linkType: hard + +"shasum@npm:^1.0.0": + version: 1.0.2 + resolution: "shasum@npm:1.0.2" + dependencies: + json-stable-stringify: ~0.0.0 + sha.js: ~2.4.4 + checksum: 61d908825cb4c7a40aa098a5b1a6f8baa782dee38f996fbb0b86358b92a424a6467c5f6e1cadf42567f4283ff640dbf2dbc321e5ab293ca3d4d50657c3908bec + languageName: node + linkType: hard + +"shebang-command@npm:^1.2.0": + version: 1.2.0 + resolution: "shebang-command@npm:1.2.0" + dependencies: + shebang-regex: ^1.0.0 + checksum: 9eed1750301e622961ba5d588af2212505e96770ec376a37ab678f965795e995ade7ed44910f5d3d3cb5e10165a1847f52d3348c64e146b8be922f7707958908 + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: ^3.0.0 + checksum: 6b52fe87271c12968f6a054e60f6bde5f0f3d2db483a1e5c3e12d657c488a15474121a1d55cd958f6df026a54374ec38a4a963988c213b7570e1d51575cea7fa + languageName: node + linkType: hard + +"shebang-regex@npm:^1.0.0": + version: 1.0.0 + resolution: "shebang-regex@npm:1.0.0" + checksum: 404c5a752cd40f94591dfd9346da40a735a05139dac890ffc229afba610854d8799aaa52f87f7e0c94c5007f2c6af55bdcaeb584b56691926c5eaf41dc8f1372 + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 1a2bcae50de99034fcd92ad4212d8e01eedf52c7ec7830eedcf886622804fe36884278f2be8be0ea5fde3fd1c23911643a4e0f726c8685b61871c8908af01222 + languageName: node + linkType: hard + +"shell-quote@npm:^1.6.1": + version: 1.7.3 + resolution: "shell-quote@npm:1.7.3" + checksum: aca58e73a3a5d933d02e0bdddedc53ee14f7c2ec264f97ac915b9d4482d077a38e422aa664631d60a672cd3cdb4054eb2e6c0303f54882453dacb6483e482d34 + languageName: node + linkType: hard + +"shelljs@npm:^0.8.5": + version: 0.8.5 + resolution: "shelljs@npm:0.8.5" + dependencies: + glob: ^7.0.0 + interpret: ^1.0.0 + rechoir: ^0.6.2 + bin: + shjs: bin/shjs + checksum: 7babc46f732a98f4c054ec1f048b55b9149b98aa2da32f6cf9844c434b43c6251efebd6eec120937bd0999e13811ebd45efe17410edb3ca938f82f9381302748 + languageName: node + linkType: hard + +"shellwords-ts@npm:^3.0.0": + version: 3.0.0 + resolution: "shellwords-ts@npm:3.0.0" + checksum: 32faa081b1996c250d41b60a41764078d1e2b46706fe656d01d02aec6937fbf4645aedb68d6f1784c69845d66f0557656583ef872b7367c8d9f8c84543c5c343 + languageName: node + linkType: hard + +"should-equal@npm:^2.0.0": + version: 2.0.0 + resolution: "should-equal@npm:2.0.0" + dependencies: + should-type: ^1.4.0 + checksum: 3f3580a223bf76f9309a4d957d2dcbd6059bda816f2e6656e822b7518218ef653c25e9271b2f5765ca6f5a72a217105ad343a8ceea831d15aff44dd691cc1dcd + languageName: node + linkType: hard + +"should-format@npm:^3.0.3": + version: 3.0.3 + resolution: "should-format@npm:3.0.3" + dependencies: + should-type: ^1.3.0 + should-type-adaptors: ^1.0.1 + checksum: 5304e89b4d4c42078c7f66232d13cca1d6a1c00c173f500f64160f57d4ecd7522a25106b313fe8f8694547e8a1ce4d975f1f09a3d1618f1dc054db48c0683d87 + languageName: node + linkType: hard + +"should-type-adaptors@npm:^1.0.1": + version: 1.1.0 + resolution: "should-type-adaptors@npm:1.1.0" + dependencies: + should-type: ^1.3.0 + should-util: ^1.0.0 + checksum: 94dd1d225c8f2590278f46689258a1df684ca1f26262459c4e2d64a09d06935ec1410a24fe7b5f98b9429093e48afef2ed1b370634e0444b930547df4943f70d + languageName: node + linkType: hard + +"should-type@npm:^1.3.0, should-type@npm:^1.4.0": + version: 1.4.0 + resolution: "should-type@npm:1.4.0" + checksum: 88d9324c6c0c2f94e71d2f8b11c84e44de81f16eeb6fafcba47f4af430c65e46bad18eb472827526cad22b4fe693aba8b022739d1c453672faf28860df223491 + languageName: node + linkType: hard + +"should-util@npm:^1.0.0": + version: 1.0.1 + resolution: "should-util@npm:1.0.1" + checksum: c3be15e0fdc851f8338676b3f8b590d330bbea94ec41c1343cc9983dea295915073f69a215795454b6adda6579ec8927c7c0ab178b83f9f11a0247ccdba53381 + languageName: node + linkType: hard + +"should@npm:^13.2.3": + version: 13.2.3 + resolution: "should@npm:13.2.3" + dependencies: + should-equal: ^2.0.0 + should-format: ^3.0.3 + should-type: ^1.4.0 + should-type-adaptors: ^1.0.1 + should-util: ^1.0.0 + checksum: 74bcc0eb85e0a63a88e501ff9ca3b53dbc6d1ee47823c029a18a4b14b3ef4e2561733e161033df720599d2153283470e9647fdcb1bbc78903960ffb0363239c4 + languageName: node + linkType: hard + +"side-channel@npm:^1.0.4": + version: 1.0.4 + resolution: "side-channel@npm:1.0.4" + dependencies: + call-bind: ^1.0.0 + get-intrinsic: ^1.0.2 + object-inspect: ^1.9.0 + checksum: 351e41b947079c10bd0858364f32bb3a7379514c399edb64ab3dce683933483fc63fb5e4efe0a15a2e8a7e3c436b6a91736ddb8d8c6591b0460a24bb4a1ee245 + languageName: node + linkType: hard + +"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": + version: 3.0.7 + resolution: "signal-exit@npm:3.0.7" + checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 + languageName: node + linkType: hard + +"signed-varint@npm:^2.0.1": + version: 2.0.1 + resolution: "signed-varint@npm:2.0.1" + dependencies: + varint: ~5.0.0 + checksum: a9fd2d954d62149d5dcbf7292c028d5665046763bd3e2b68f5603fca9248c808ca727f0b70e8e785d292c40f6a43b7406d56a37c7b06becd3c6ad0972c5d0e94 + languageName: node + linkType: hard + +"simple-concat@npm:^1.0.0": + version: 1.0.1 + resolution: "simple-concat@npm:1.0.1" + checksum: 4d211042cc3d73a718c21ac6c4e7d7a0363e184be6a5ad25c8a1502e49df6d0a0253979e3d50dbdd3f60ef6c6c58d756b5d66ac1e05cda9cacd2e9fc59e3876a + languageName: node + linkType: hard + +"simple-swizzle@npm:^0.2.2": + version: 0.2.2 + resolution: "simple-swizzle@npm:0.2.2" + dependencies: + is-arrayish: ^0.3.1 + checksum: a7f3f2ab5c76c4472d5c578df892e857323e452d9f392e1b5cf74b74db66e6294a1e1b8b390b519fa1b96b5b613f2a37db6cffef52c3f1f8f3c5ea64eb2d54c0 + languageName: node + linkType: hard + +"simple-wcswidth@npm:^1.0.1": + version: 1.0.1 + resolution: "simple-wcswidth@npm:1.0.1" + checksum: dc5bf4cb131d9c386825d1355add2b1ecc408b37dc2c2334edd7a1a4c9f527e6b594dedcdbf6d949bce2740c3a332e39af1183072a2d068e40d9e9146067a37f + languageName: node + linkType: hard + +"sinon-chai@npm:^3.7.0": + version: 3.7.0 + resolution: "sinon-chai@npm:3.7.0" + peerDependencies: + chai: ^4.0.0 + sinon: ">=4.0.0" + checksum: 49a353d8eb66cc6db35ac452f6965c72778aa090d1f036dd1e54ba88594b1c3f314b1a403eaff22a4e314f94dc92d9c7d03cbb88c21d89e814293bf5b299964d + languageName: node + linkType: hard + +"sinon@npm:^11.1.2": + version: 11.1.2 + resolution: "sinon@npm:11.1.2" + dependencies: + "@sinonjs/commons": ^1.8.3 + "@sinonjs/fake-timers": ^7.1.2 + "@sinonjs/samsam": ^6.0.2 + diff: ^5.0.0 + nise: ^5.1.0 + supports-color: ^7.2.0 + checksum: 1d01377e230c9ba976bf33f28b588bae7901b0b5a503d2f6b2a7914b0dbaa9f09823481926c6f2abed820123c7fa865519695af3ae2e9ba18d8b025616163501 + languageName: node + linkType: hard + +"slash@npm:^3.0.0": + version: 3.0.0 + resolution: "slash@npm:3.0.0" + checksum: 94a93fff615f25a999ad4b83c9d5e257a7280c90a32a7cb8b4a87996e4babf322e469c42b7f649fd5796edd8687652f3fb452a86dc97a816f01113183393f11c + languageName: node + linkType: hard + +"slice-ansi@npm:^2.1.0": + version: 2.1.0 + resolution: "slice-ansi@npm:2.1.0" + dependencies: + ansi-styles: ^3.2.0 + astral-regex: ^1.0.0 + is-fullwidth-code-point: ^2.0.0 + checksum: 4e82995aa59cef7eb03ef232d73c2239a15efa0ace87a01f3012ebb942e963fbb05d448ce7391efcd52ab9c32724164aba2086f5143e0445c969221dde3b6b1e + languageName: node + linkType: hard + +"slice-ansi@npm:^3.0.0": + version: 3.0.0 + resolution: "slice-ansi@npm:3.0.0" + dependencies: + ansi-styles: ^4.0.0 + astral-regex: ^2.0.0 + is-fullwidth-code-point: ^3.0.0 + checksum: 5ec6d022d12e016347e9e3e98a7eb2a592213a43a65f1b61b74d2c78288da0aded781f665807a9f3876b9daa9ad94f64f77d7633a0458876c3a4fdc4eb223f24 + languageName: node + linkType: hard + +"slice-ansi@npm:^4.0.0": + version: 4.0.0 + resolution: "slice-ansi@npm:4.0.0" + dependencies: + ansi-styles: ^4.0.0 + astral-regex: ^2.0.0 + is-fullwidth-code-point: ^3.0.0 + checksum: 4a82d7f085b0e1b070e004941ada3c40d3818563ac44766cca4ceadd2080427d337554f9f99a13aaeb3b4a94d9964d9466c807b3d7b7541d1ec37ee32d308756 + languageName: node + linkType: hard + +"slocket@npm:^1.0.5": + version: 1.0.5 + resolution: "slocket@npm:1.0.5" + dependencies: + bluebird: ^3.4.7 + rimraf: ^2.5.4 + signal-exit: ^3.0.2 + checksum: 4ea3cba56c38325ce190ffa96ef0122ede78846ce12b82c810b247fd52549cfb962fe10bcc629ee14be315db085ff5ebb1059c3ccd1e52251a5b0be66f0cf0d7 + languageName: node + linkType: hard + +"smart-buffer@npm:^4.1.0": + version: 4.2.0 + resolution: "smart-buffer@npm:4.2.0" + checksum: b5167a7142c1da704c0e3af85c402002b597081dd9575031a90b4f229ca5678e9a36e8a374f1814c8156a725d17008ae3bde63b92f9cfd132526379e580bec8b + languageName: node + linkType: hard + +"socket.io-adapter@npm:~2.3.3": + version: 2.3.3 + resolution: "socket.io-adapter@npm:2.3.3" + checksum: 73890e0a33e48a9e4be83e5fa2b8ea9728d2a35ae2fed373cad4d6744c6512c0e1c735e7820df9821e58c4738dc355bdaec5aae30bc56f4d6a41d999596d0c82 + languageName: node + linkType: hard + +"socket.io-parser@npm:~4.0.4": + version: 4.0.4 + resolution: "socket.io-parser@npm:4.0.4" + dependencies: + "@types/component-emitter": ^1.2.10 + component-emitter: ~1.3.0 + debug: ~4.3.1 + checksum: c173b4f3747c51e2af802eca35212f4dcfa8fe55d7fdc07b9a01da1ecc956791c1bf6591e307952548eab69e6500bcfe27cea8aff1386b860d9bb51f98e4fafb + languageName: node + linkType: hard + +"socket.io@npm:^4.2.0": + version: 4.4.0 + resolution: "socket.io@npm:4.4.0" + dependencies: + accepts: ~1.3.4 + base64id: ~2.0.0 + debug: ~4.3.2 + engine.io: ~6.1.0 + socket.io-adapter: ~2.3.3 + socket.io-parser: ~4.0.4 + checksum: 3e680f6969501d31200bfd9a420f23f923146343f329ba803d339715e4ef673a27a3250fe598d321b86ed1880f2873e56a2567fab070c2622238aedb84abd536 + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^6.0.0, socks-proxy-agent@npm:^6.1.1": + version: 6.1.1 + resolution: "socks-proxy-agent@npm:6.1.1" + dependencies: + agent-base: ^6.0.2 + debug: ^4.3.1 + socks: ^2.6.1 + checksum: 9a8a4f791bba0060315cf7291ca6f9db37d6fc280fd0860d73d8887d3efe4c22e823aa25a8d5375f6079279f8dc91b50c075345179bf832bfe3c7c26d3582e3c + languageName: node + linkType: hard + +"socks@npm:^2.6.1": + version: 2.6.1 + resolution: "socks@npm:2.6.1" + dependencies: + ip: ^1.1.5 + smart-buffer: ^4.1.0 + checksum: 2ca9d616e424f645838ebaabb04f85d94ea999e0f8393dc07f86c435af22ed88cb83958feeabd1bb7bc537c635ed47454255635502c6808a6df61af1f41af750 + languageName: node + linkType: hard + +"sonic-boom@npm:^1.0.2": + version: 1.4.1 + resolution: "sonic-boom@npm:1.4.1" + dependencies: + atomic-sleep: ^1.0.0 + flatstr: ^1.0.12 + checksum: 189fa8fe5c2dc05d3513fc1a4926a2f16f132fa6fa0b511745a436010cdcd9c1d3b3cb6a9d7c05bd32a965dc77673a5ac0eb0992e920bdedd16330d95323124f + languageName: node + linkType: hard + +"sonic-boom@npm:^2.1.0": + version: 2.3.1 + resolution: "sonic-boom@npm:2.3.1" + dependencies: + atomic-sleep: ^1.0.0 + checksum: 4f5022de97483bb6f889415e342f9a451dbdebbe732df454f7d6e417cc80e813c19e2d646ad5ae20a92510a34c311fb9fbc440864bb234b78b3a2100467d851b + languageName: node + linkType: hard + +"sort-keys@npm:^4.0.0, sort-keys@npm:^4.2.0": + version: 4.2.0 + resolution: "sort-keys@npm:4.2.0" + dependencies: + is-plain-obj: ^2.0.0 + checksum: 1535ffd5a789259fc55107d5c3cec09b3e47803a9407fcaae37e1b9e0b813762c47dfee35b6e71e20ca7a69798d0a4791b2058a07f6cab5ef17b2dae83cedbda + languageName: node + linkType: hard + +"source-map-support@npm:^0.5.6, source-map-support@npm:~0.5.20": + version: 0.5.21 + resolution: "source-map-support@npm:0.5.21" + dependencies: + buffer-from: ^1.0.0 + source-map: ^0.6.0 + checksum: 43e98d700d79af1d36f859bdb7318e601dfc918c7ba2e98456118ebc4c4872b327773e5a1df09b0524e9e5063bb18f0934538eace60cca2710d1fa687645d137 + languageName: node + linkType: hard + +"source-map@npm:^0.5.0, source-map@npm:~0.5.3": + version: 0.5.7 + resolution: "source-map@npm:0.5.7" + checksum: 5dc2043b93d2f194142c7f38f74a24670cd7a0063acdaf4bf01d2964b402257ae843c2a8fa822ad5b71013b5fcafa55af7421383da919752f22ff488bc553f4d + languageName: node + linkType: hard + +"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.1": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2 + languageName: node + linkType: hard + +"source-map@npm:~0.7.2": + version: 0.7.3 + resolution: "source-map@npm:0.7.3" + checksum: cd24efb3b8fa69b64bf28e3c1b1a500de77e84260c5b7f2b873f88284df17974157cc88d386ee9b6d081f08fdd8242f3fc05c953685a6ad81aad94c7393dedea + languageName: node + linkType: hard + +"sparse-bitfield@npm:^3.0.3": + version: 3.0.3 + resolution: "sparse-bitfield@npm:3.0.3" + dependencies: + memory-pager: ^1.0.2 + checksum: 174da88dbbcc783d5dbd26921931cc83830280b8055fb05333786ebe6fc015b9601b24972b3d55920dd2d9f5fb120576fbfa2469b08e5222c9cadf3f05210aab + languageName: node + linkType: hard + +"spawn-command@npm:^0.0.2-1": + version: 0.0.2 + resolution: "spawn-command@npm:0.0.2" + checksum: e35c5d28177b4d461d33c88cc11f6f3a5079e2b132c11e1746453bbb7a0c0b8a634f07541a2a234fa4758239d88203b758def509161b651e81958894c0b4b64b + languageName: node + linkType: hard + +"spawn-wrap@npm:^2.0.0": + version: 2.0.0 + resolution: "spawn-wrap@npm:2.0.0" + dependencies: + foreground-child: ^2.0.0 + is-windows: ^1.0.2 + make-dir: ^3.0.0 + rimraf: ^3.0.0 + signal-exit: ^3.0.2 + which: ^2.0.1 + checksum: 5a518e37620def6d516b86207482a4f76bcf3c37c57d8d886d9fa399b04e5668d11fd12817b178029b02002a5ebbd09010374307effa821ba39594042f0a2d96 + languageName: node + linkType: hard + +"spdx-correct@npm:^3.0.0": + version: 3.1.1 + resolution: "spdx-correct@npm:3.1.1" + dependencies: + spdx-expression-parse: ^3.0.0 + spdx-license-ids: ^3.0.0 + checksum: 77ce438344a34f9930feffa61be0eddcda5b55fc592906ef75621d4b52c07400a97084d8701557b13f7d2aae0cb64f808431f469e566ef3fe0a3a131dcb775a6 + languageName: node + linkType: hard + +"spdx-exceptions@npm:^2.1.0": + version: 2.3.0 + resolution: "spdx-exceptions@npm:2.3.0" + checksum: cb69a26fa3b46305637123cd37c85f75610e8c477b6476fa7354eb67c08128d159f1d36715f19be6f9daf4b680337deb8c65acdcae7f2608ba51931540687ac0 + languageName: node + linkType: hard + +"spdx-expression-parse@npm:^3.0.0, spdx-expression-parse@npm:^3.0.1": + version: 3.0.1 + resolution: "spdx-expression-parse@npm:3.0.1" + dependencies: + spdx-exceptions: ^2.1.0 + spdx-license-ids: ^3.0.0 + checksum: a1c6e104a2cbada7a593eaa9f430bd5e148ef5290d4c0409899855ce8b1c39652bcc88a725259491a82601159d6dc790bedefc9016c7472f7de8de7361f8ccde + languageName: node + linkType: hard + +"spdx-license-ids@npm:^3.0.0": + version: 3.0.11 + resolution: "spdx-license-ids@npm:3.0.11" + checksum: 1da1acb090257773e60b022094050e810ae9fec874dc1461f65dc0400cd42dd830ab2df6e64fb49c2db3dce386dd0362110780e1b154db7c0bb413488836aaeb + languageName: node + linkType: hard + +"split-ca@npm:^1.0.1": + version: 1.0.1 + resolution: "split-ca@npm:1.0.1" + checksum: 1e7409938a95ee843fe2593156a5735e6ee63772748ee448ea8477a5a3e3abde193c3325b3696e56a5aff07c7dcf6b1f6a2f2a036895b4f3afe96abb366d893f + languageName: node + linkType: hard + +"split2@npm:^3.0.0, split2@npm:^3.1.1": + version: 3.2.2 + resolution: "split2@npm:3.2.2" + dependencies: + readable-stream: ^3.0.0 + checksum: 8127ddbedd0faf31f232c0e9192fede469913aa8982aa380752e0463b2e31c2359ef6962eb2d24c125bac59eeec76873678d723b1c7ff696216a1cd071e3994a + languageName: node + linkType: hard + +"split@npm:^1.0.0": + version: 1.0.1 + resolution: "split@npm:1.0.1" + dependencies: + through: 2 + checksum: 12f4554a5792c7e98bb3e22b53c63bfa5ef89aa704353e1db608a55b51f5b12afaad6e4a8ecf7843c15f273f43cdadd67b3705cc43d48a75c2cf4641d51f7e7a + languageName: node + linkType: hard + +"sprintf-js@npm:~1.0.2": + version: 1.0.3 + resolution: "sprintf-js@npm:1.0.3" + checksum: 19d79aec211f09b99ec3099b5b2ae2f6e9cdefe50bc91ac4c69144b6d3928a640bb6ae5b3def70c2e85a2c3d9f5ec2719921e3a59d3ca3ef4b2fd1a4656a0df3 + languageName: node + linkType: hard + +"ssh2@npm:^1.4.0": + version: 1.5.0 + resolution: "ssh2@npm:1.5.0" + dependencies: + asn1: ^0.2.4 + bcrypt-pbkdf: ^1.0.2 + cpu-features: 0.0.2 + nan: ^2.15.0 + dependenciesMeta: + cpu-features: + optional: true + nan: + optional: true + checksum: 6a2252c12d9587eeb31c499d1d13bb14a255d08243762e1a92b2db76add8912b2c8c25103c79b92e156d598c1e2781c5523b58aa9e85aee06fea365e4cc188bb + languageName: node + linkType: hard + +"sshpk@npm:^1.7.0": + version: 1.16.1 + resolution: "sshpk@npm:1.16.1" + dependencies: + asn1: ~0.2.3 + assert-plus: ^1.0.0 + bcrypt-pbkdf: ^1.0.0 + dashdash: ^1.12.0 + ecc-jsbn: ~0.1.1 + getpass: ^0.1.1 + jsbn: ~0.1.0 + safer-buffer: ^2.0.2 + tweetnacl: ~0.14.0 + bin: + sshpk-conv: bin/sshpk-conv + sshpk-sign: bin/sshpk-sign + sshpk-verify: bin/sshpk-verify + checksum: 5e76afd1cedc780256f688b7c09327a8a650902d18e284dfeac97489a735299b03c3e72c6e8d22af03dbbe4d6f123fdfd5f3c4ed6bedbec72b9529a55051b857 + languageName: node + linkType: hard + +"ssri@npm:^8.0.0, ssri@npm:^8.0.1": + version: 8.0.1 + resolution: "ssri@npm:8.0.1" + dependencies: + minipass: ^3.1.1 + checksum: bc447f5af814fa9713aa201ec2522208ae0f4d8f3bda7a1f445a797c7b929a02720436ff7c478fb5edc4045adb02b1b88d2341b436a80798734e2494f1067b36 + languageName: node + linkType: hard + +"stack-trace@npm:0.0.x": + version: 0.0.10 + resolution: "stack-trace@npm:0.0.10" + checksum: 473036ad32f8c00e889613153d6454f9be0536d430eb2358ca51cad6b95cea08a3cc33cc0e34de66b0dad221582b08ed2e61ef8e13f4087ab690f388362d6610 + languageName: node + linkType: hard + +"stack-utils@npm:^2.0.3": + version: 2.0.5 + resolution: "stack-utils@npm:2.0.5" + dependencies: + escape-string-regexp: ^2.0.0 + checksum: 76b69da0f5b48a34a0f93c98ee2a96544d2c4ca2557f7eef5ddb961d3bdc33870b46f498a84a7c4f4ffb781df639840e7ebf6639164ed4da5e1aeb659615b9c7 + languageName: node + linkType: hard + +"statuses@npm:>= 1.5.0 < 2, statuses@npm:~1.5.0": + version: 1.5.0 + resolution: "statuses@npm:1.5.0" + checksum: c469b9519de16a4bb19600205cffb39ee471a5f17b82589757ca7bd40a8d92ebb6ed9f98b5a540c5d302ccbc78f15dc03cc0280dd6e00df1335568a5d5758a5c + languageName: node + linkType: hard + +"stealthy-require@npm:^1.1.1": + version: 1.1.1 + resolution: "stealthy-require@npm:1.1.1" + checksum: 6805b857a9f3a6a1079fc6652278038b81011f2a5b22cbd559f71a6c02087e6f1df941eb10163e3fdc5391ab5807aa46758d4258547c1f5ede31e6d9bfda8dd3 + languageName: node + linkType: hard + +"stream-browserify@npm:^2.0.0": + version: 2.0.2 + resolution: "stream-browserify@npm:2.0.2" + dependencies: + inherits: ~2.0.1 + readable-stream: ^2.0.2 + checksum: 8de7bcab5582e9a931ae1a4768be7efe8fa4b0b95fd368d16d8cf3e494b897d6b0a7238626de5d71686e53bddf417fd59d106cfa3af0ec055f61a8d1f8fc77b3 + languageName: node + linkType: hard + +"stream-browserify@npm:^3.0.0": + version: 3.0.0 + resolution: "stream-browserify@npm:3.0.0" + dependencies: + inherits: ~2.0.4 + readable-stream: ^3.5.0 + checksum: 4c47ef64d6f03815a9ca3874e2319805e8e8a85f3550776c47ce523b6f4c6cd57f40e46ec6a9ab8ad260fde61863c2718f250d3bedb3fe9052444eb9abfd9921 + languageName: node + linkType: hard + +"stream-combiner2@npm:^1.1.1": + version: 1.1.1 + resolution: "stream-combiner2@npm:1.1.1" + dependencies: + duplexer2: ~0.1.0 + readable-stream: ^2.0.2 + checksum: dd32d179fa8926619c65471a7396fc638ec8866616c0b8747c4e05563ccdb0b694dd4e83cd799f1c52789c965a40a88195942b82b8cea2ee7a5536f1954060f9 + languageName: node + linkType: hard + +"stream-http@npm:^3.0.0, stream-http@npm:^3.2.0": + version: 3.2.0 + resolution: "stream-http@npm:3.2.0" + dependencies: + builtin-status-codes: ^3.0.0 + inherits: ^2.0.4 + readable-stream: ^3.6.0 + xtend: ^4.0.2 + checksum: c9b78453aeb0c84fcc59555518ac62bacab9fa98e323e7b7666e5f9f58af8f3155e34481078509b02929bd1268427f664d186604cdccee95abc446099b339f83 + languageName: node + linkType: hard + +"stream-splicer@npm:^2.0.0": + version: 2.0.1 + resolution: "stream-splicer@npm:2.0.1" + dependencies: + inherits: ^2.0.1 + readable-stream: ^2.0.2 + checksum: 7bb3563961450e69183baa04272e042bdd7df44f6d75bf1cce0d6a628efd2d4b0a0d2a290bed0674ea7719c87e6cf6bf7406ca1d17413abf1484430d36d65580 + languageName: node + linkType: hard + +"streamroller@npm:^2.2.4": + version: 2.2.4 + resolution: "streamroller@npm:2.2.4" + dependencies: + date-format: ^2.1.0 + debug: ^4.1.1 + fs-extra: ^8.1.0 + checksum: 83060ded804747d2a9f202f142d24680a01f3bc5e36e9bd746b3e530252bbbf29a8030659f3c66e2dcd3d1ce403144bd302d9b4e51be0f9ed7d2f371a13d166b + languageName: node + linkType: hard + +"string-width@npm:^1.0.1": + version: 1.0.2 + resolution: "string-width@npm:1.0.2" + dependencies: + code-point-at: ^1.0.0 + is-fullwidth-code-point: ^1.0.0 + strip-ansi: ^3.0.0 + checksum: 5c79439e95bc3bd7233a332c5f5926ab2ee90b23816ed4faa380ce3b2576d7800b0a5bb15ae88ed28737acc7ea06a518c2eef39142dd727adad0e45c776cd37e + languageName: node + linkType: hard + +"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: ^8.0.0 + is-fullwidth-code-point: ^3.0.0 + strip-ansi: ^6.0.1 + checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb + languageName: node + linkType: hard + +"string-width@npm:^2.0.0": + version: 2.1.1 + resolution: "string-width@npm:2.1.1" + dependencies: + is-fullwidth-code-point: ^2.0.0 + strip-ansi: ^4.0.0 + checksum: d6173abe088c615c8dffaf3861dc5d5906ed3dc2d6fd67ff2bd2e2b5dce7fd683c5240699cf0b1b8aa679a3b3bd6b28b5053c824cb89b813d7f6541d8f89064a + languageName: node + linkType: hard + +"string-width@npm:^3.0.0": + version: 3.1.0 + resolution: "string-width@npm:3.1.0" + dependencies: + emoji-regex: ^7.0.1 + is-fullwidth-code-point: ^2.0.0 + strip-ansi: ^5.1.0 + checksum: 57f7ca73d201682816d573dc68bd4bb8e1dff8dc9fcf10470fdfc3474135c97175fec12ea6a159e67339b41e86963112355b64529489af6e7e70f94a7caf08b2 + languageName: node + linkType: hard + +"string.prototype.trimend@npm:^1.0.4": + version: 1.0.4 + resolution: "string.prototype.trimend@npm:1.0.4" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.3 + checksum: 17e5aa45c3983f582693161f972c1c1fa4bbbdf22e70e582b00c91b6575f01680dc34e83005b98e31abe4d5d29e0b21fcc24690239c106c7b2315aade6a898ac + languageName: node + linkType: hard + +"string.prototype.trimstart@npm:^1.0.4": + version: 1.0.4 + resolution: "string.prototype.trimstart@npm:1.0.4" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.3 + checksum: 3fb06818d3cccac5fa3f5f9873d984794ca0e9f6616fae6fcc745885d9efed4e17fe15f832515d9af5e16c279857fdbffdfc489ca4ed577811b017721b30302f + languageName: node + linkType: hard + +"string_decoder@npm:^1.1.1, string_decoder@npm:^1.3.0": + version: 1.3.0 + resolution: "string_decoder@npm:1.3.0" + dependencies: + safe-buffer: ~5.2.0 + checksum: 8417646695a66e73aefc4420eb3b84cc9ffd89572861fe004e6aeb13c7bc00e2f616247505d2dbbef24247c372f70268f594af7126f43548565c68c117bdeb56 + languageName: node + linkType: hard + +"string_decoder@npm:~0.10.x": + version: 0.10.31 + resolution: "string_decoder@npm:0.10.31" + checksum: fe00f8e303647e5db919948ccb5ce0da7dea209ab54702894dd0c664edd98e5d4df4b80d6fabf7b9e92b237359d21136c95bf068b2f7760b772ca974ba970202 + languageName: node + linkType: hard + +"string_decoder@npm:~1.1.1": + version: 1.1.1 + resolution: "string_decoder@npm:1.1.1" + dependencies: + safe-buffer: ~5.1.0 + checksum: 9ab7e56f9d60a28f2be697419917c50cac19f3e8e6c28ef26ed5f4852289fe0de5d6997d29becf59028556f2c62983790c1d9ba1e2a3cc401768ca12d5183a5b + languageName: node + linkType: hard + +"strip-ansi@npm:^3.0.0, strip-ansi@npm:^3.0.1": + version: 3.0.1 + resolution: "strip-ansi@npm:3.0.1" + dependencies: + ansi-regex: ^2.0.0 + checksum: 9b974de611ce5075c70629c00fa98c46144043db92ae17748fb780f706f7a789e9989fd10597b7c2053ae8d1513fd707816a91f1879b2f71e6ac0b6a863db465 + languageName: node + linkType: hard + +"strip-ansi@npm:^4.0.0": + version: 4.0.0 + resolution: "strip-ansi@npm:4.0.0" + dependencies: + ansi-regex: ^3.0.0 + checksum: d9186e6c0cf78f25274f6750ee5e4a5725fb91b70fdd79aa5fe648eab092a0ec5b9621b22d69d4534a56319f75d8944efbd84e3afa8d4ad1b9a9491f12c84eca + languageName: node + linkType: hard + +"strip-ansi@npm:^5.1.0": + version: 5.2.0 + resolution: "strip-ansi@npm:5.2.0" + dependencies: + ansi-regex: ^4.1.0 + checksum: bdb5f76ade97062bd88e7723aa019adbfacdcba42223b19ccb528ffb9fb0b89a5be442c663c4a3fb25268eaa3f6ea19c7c3fbae830bd1562d55adccae1fcec46 + languageName: node + linkType: hard + +"strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: ^5.0.1 + checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c + languageName: node + linkType: hard + +"strip-bom-buf@npm:^1.0.0": + version: 1.0.0 + resolution: "strip-bom-buf@npm:1.0.0" + dependencies: + is-utf8: ^0.2.1 + checksum: 246665fa1c50eb0852ed174fdbd7da34edb444165e7dda2cd58e66b49a2900707d9f8d3f94bcc8542fe1f46ae7b4274a3411b8ab9e43cd1dcf1b77416e324cfb + languageName: node + linkType: hard + +"strip-bom-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "strip-bom-stream@npm:2.0.0" + dependencies: + first-chunk-stream: ^2.0.0 + strip-bom: ^2.0.0 + checksum: 3e2ff494d9181537ceee35c7bb662fee9dfcebba0779f004e7c1455278f2616c0915b66d8d5432a6cb7e23c792c199717b4661a12e8fa2728a18961dd0203a2e + languageName: node + linkType: hard + +"strip-bom@npm:^2.0.0": + version: 2.0.0 + resolution: "strip-bom@npm:2.0.0" + dependencies: + is-utf8: ^0.2.0 + checksum: 08efb746bc67b10814cd03d79eb31bac633393a782e3f35efbc1b61b5165d3806d03332a97f362822cf0d4dd14ba2e12707fcff44fe1c870c48a063a0c9e4944 + languageName: node + linkType: hard + +"strip-bom@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-bom@npm:3.0.0" + checksum: 8d50ff27b7ebe5ecc78f1fe1e00fcdff7af014e73cf724b46fb81ef889eeb1015fc5184b64e81a2efe002180f3ba431bdd77e300da5c6685d702780fbf0c8d5b + languageName: node + linkType: hard + +"strip-bom@npm:^4.0.0": + version: 4.0.0 + resolution: "strip-bom@npm:4.0.0" + checksum: 9dbcfbaf503c57c06af15fe2c8176fb1bf3af5ff65003851a102749f875a6dbe0ab3b30115eccf6e805e9d756830d3e40ec508b62b3f1ddf3761a20ebe29d3f3 + languageName: node + linkType: hard + +"strip-eof@npm:^1.0.0": + version: 1.0.0 + resolution: "strip-eof@npm:1.0.0" + checksum: 40bc8ddd7e072f8ba0c2d6d05267b4e0a4800898c3435b5fb5f5a21e6e47dfaff18467e7aa0d1844bb5d6274c3097246595841fbfeb317e541974ee992cac506 + languageName: node + linkType: hard + +"strip-final-newline@npm:^2.0.0": + version: 2.0.0 + resolution: "strip-final-newline@npm:2.0.0" + checksum: 69412b5e25731e1938184b5d489c32e340605bb611d6140344abc3421b7f3c6f9984b21dff296dfcf056681b82caa3bb4cc996a965ce37bcfad663e92eae9c64 + languageName: node + linkType: hard + +"strip-indent@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-indent@npm:3.0.0" + dependencies: + min-indent: ^1.0.0 + checksum: 18f045d57d9d0d90cd16f72b2313d6364fd2cb4bf85b9f593523ad431c8720011a4d5f08b6591c9d580f446e78855c5334a30fb91aa1560f5d9f95ed1b4a0530 + languageName: node + linkType: hard + +"strip-json-comments@npm:3.1.1, strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1": + version: 3.1.1 + resolution: "strip-json-comments@npm:3.1.1" + checksum: 492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443 + languageName: node + linkType: hard + +"strip-json-comments@npm:~2.0.1": + version: 2.0.1 + resolution: "strip-json-comments@npm:2.0.1" + checksum: 1074ccb63270d32ca28edfb0a281c96b94dc679077828135141f27d52a5a398ef5e78bcf22809d23cadc2b81dfbe345eb5fd8699b385c8b1128907dec4a7d1e1 + languageName: node + linkType: hard + +"subarg@npm:^1.0.0": + version: 1.0.0 + resolution: "subarg@npm:1.0.0" + dependencies: + minimist: ^1.1.0 + checksum: 8359df72e9a2d03c35702ba58e49cac04daae8f27dff26837e12687c7d10cb800a036fd33fdc5eb0e8c24fb25d804f657fe8bde18dd3dd6ec7dab8eff7aac27e + languageName: node + linkType: hard + +"supports-color@npm:8.1.1, supports-color@npm:^8.0.0, supports-color@npm:^8.1.0, supports-color@npm:^8.1.1": + version: 8.1.1 + resolution: "supports-color@npm:8.1.1" + dependencies: + has-flag: ^4.0.0 + checksum: c052193a7e43c6cdc741eb7f378df605636e01ad434badf7324f17fb60c69a880d8d8fcdcb562cf94c2350e57b937d7425ab5b8326c67c2adc48f7c87c1db406 + languageName: node + linkType: hard + +"supports-color@npm:^2.0.0": + version: 2.0.0 + resolution: "supports-color@npm:2.0.0" + checksum: 602538c5812b9006404370b5a4b885d3e2a1f6567d314f8b4a41974ffe7d08e525bf92ae0f9c7030e3b4c78e4e34ace55d6a67a74f1571bc205959f5972f88f0 + languageName: node + linkType: hard + +"supports-color@npm:^5.3.0, supports-color@npm:^5.5.0": + version: 5.5.0 + resolution: "supports-color@npm:5.5.0" + dependencies: + has-flag: ^3.0.0 + checksum: 95f6f4ba5afdf92f495b5a912d4abee8dcba766ae719b975c56c084f5004845f6f5a5f7769f52d53f40e21952a6d87411bafe34af4a01e65f9926002e38e1dac + languageName: node + linkType: hard + +"supports-color@npm:^7.0.0, supports-color@npm:^7.1.0, supports-color@npm:^7.2.0": + version: 7.2.0 + resolution: "supports-color@npm:7.2.0" + dependencies: + has-flag: ^4.0.0 + checksum: 3dda818de06ebbe5b9653e07842d9479f3555ebc77e9a0280caf5a14fb877ffee9ed57007c3b78f5a6324b8dbeec648d9e97a24e2ed9fdb81ddc69ea07100f4a + languageName: node + linkType: hard + +"supports-hyperlinks@npm:^2.2.0": + version: 2.2.0 + resolution: "supports-hyperlinks@npm:2.2.0" + dependencies: + has-flag: ^4.0.0 + supports-color: ^7.0.0 + checksum: aef04fb41f4a67f1bc128f7c3e88a81b6cf2794c800fccf137006efe5bafde281da3e42e72bf9206c2fcf42e6438f37e3a820a389214d0a88613ca1f2d36076a + languageName: node + linkType: hard + +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 53b1e247e68e05db7b3808b99b892bd36fb096e6fba213a06da7fab22045e97597db425c724f2bbd6c99a3c295e1e73f3e4de78592289f38431049e1277ca0ae + languageName: node + linkType: hard + +"swagger-jsdoc@npm:^3.5.0": + version: 3.7.0 + resolution: "swagger-jsdoc@npm:3.7.0" + dependencies: + commander: 4.0.1 + doctrine: 3.0.0 + glob: 7.1.6 + js-yaml: 3.13.1 + swagger-parser: 8.0.4 + bin: + swagger-jsdoc: bin/swagger-jsdoc.js + checksum: 436e6321f314bbb44a17934c17c26614af764a8a02c15b851a580b9ff4a6b59fe15f3d6c7e77ebde733f5e3df740cca80e7f74dc4cb2b6ea11d050ceb6736229 + languageName: node + linkType: hard + +"swagger-methods@npm:^2.0.1": + version: 2.0.2 + resolution: "swagger-methods@npm:2.0.2" + checksum: 1321362be766b2ddf6ef791d04975db19bbc6b41e485ce4beb34baef8103b90b1b650c8600ed37c5d52c44f7b4dd959d30d457db7c38553038b687104365891e + languageName: node + linkType: hard + +"swagger-parser@npm:8.0.4": + version: 8.0.4 + resolution: "swagger-parser@npm:8.0.4" + dependencies: + call-me-maybe: ^1.0.1 + json-schema-ref-parser: ^7.1.3 + ono: ^6.0.0 + openapi-schemas: ^1.0.2 + openapi-types: ^1.3.5 + swagger-methods: ^2.0.1 + z-schema: ^4.2.2 + checksum: cd07ac3dbe69f397e6a9cd5906fd33427eece8a2ec6296354d0a5a227699bd41deaccb7c4bcf271c51a9420792c9b587fdfbb492f926e6b39f62721309a00190 + languageName: node + linkType: hard + +"syntax-error@npm:^1.1.1": + version: 1.4.0 + resolution: "syntax-error@npm:1.4.0" + dependencies: + acorn-node: ^1.2.0 + checksum: c1c3f048fed1948865fda5e79e11b02addb32da323c9c9fb214d3a933f9fda668e55c848f7c4082514ea4f1cf3dcfab0c7b9c762bfad1306271753c0fcc4b14f + languageName: node + linkType: hard + +"table@npm:^5.4.6": + version: 5.4.6 + resolution: "table@npm:5.4.6" + dependencies: + ajv: ^6.10.2 + lodash: ^4.17.14 + slice-ansi: ^2.1.0 + string-width: ^3.0.0 + checksum: 9e35d3efa788edc17237eef8852f8e4b9178efd65a7d115141777b2ee77df4b7796c05f4ed3712d858f98894ac5935a481ceeb6dcb9895e2f67a61cce0e63b6c + languageName: node + linkType: hard + +"table@npm:^6.0.9": + version: 6.7.3 + resolution: "table@npm:6.7.3" + dependencies: + ajv: ^8.0.1 + lodash.truncate: ^4.4.2 + slice-ansi: ^4.0.0 + string-width: ^4.2.3 + strip-ansi: ^6.0.1 + checksum: 61d732f51108222d158eca2a91bfaae41c14e0cba6eb04c702ec5a1b136219d4925940d5c4d9aff5720bc4e2385dcbe2ed52dcf37bbbd8b2be48c01c1cf2ed1d + languageName: node + linkType: hard + +"taketalk@npm:^1.0.0": + version: 1.0.0 + resolution: "taketalk@npm:1.0.0" + dependencies: + get-stdin: ^4.0.1 + minimist: ^1.1.0 + checksum: b9a6ae2d6e41573a18958b7ac76ab89a6f703a3abfb24fc1613171bece8b6bb76e68fa39da6077ed6cac22d0d39530e40e9078aea93f3cf9285f48b05e046a2b + languageName: node + linkType: hard + +"tapable@npm:^1.0.0": + version: 1.1.3 + resolution: "tapable@npm:1.1.3" + checksum: 53ff4e7c3900051c38cc4faab428ebfd7e6ad0841af5a7ac6d5f3045c5b50e88497bfa8295b4b3fbcadd94993c9e358868b78b9fb249a76cb8b018ac8dccafd7 + languageName: node + linkType: hard + +"tapable@npm:^2.1.1, tapable@npm:^2.2.0": + version: 2.2.1 + resolution: "tapable@npm:2.2.1" + checksum: 3b7a1b4d86fa940aad46d9e73d1e8739335efd4c48322cb37d073eb6f80f5281889bf0320c6d8ffcfa1a0dd5bfdbd0f9d037e252ef972aca595330538aac4d51 + languageName: node + linkType: hard + +"tar-fs@npm:^2.0.0": + version: 2.1.1 + resolution: "tar-fs@npm:2.1.1" + dependencies: + chownr: ^1.1.1 + mkdirp-classic: ^0.5.2 + pump: ^3.0.0 + tar-stream: ^2.1.4 + checksum: f5b9a70059f5b2969e65f037b4e4da2daf0fa762d3d232ffd96e819e3f94665dbbbe62f76f084f1acb4dbdcce16c6e4dac08d12ffc6d24b8d76720f4d9cf032d + languageName: node + linkType: hard + +"tar-fs@npm:~2.0.1": + version: 2.0.1 + resolution: "tar-fs@npm:2.0.1" + dependencies: + chownr: ^1.1.1 + mkdirp-classic: ^0.5.2 + pump: ^3.0.0 + tar-stream: ^2.0.0 + checksum: 26cd297ed2421bc8038ce1a4ca442296b53739f409847d495d46086e5713d8db27f2c03ba2f461d0f5ddbc790045628188a8544f8ae32cbb6238b279b68d0247 + languageName: node + linkType: hard + +"tar-stream@npm:^2.0.0, tar-stream@npm:^2.1.4": + version: 2.2.0 + resolution: "tar-stream@npm:2.2.0" + dependencies: + bl: ^4.0.3 + end-of-stream: ^1.4.1 + fs-constants: ^1.0.0 + inherits: ^2.0.3 + readable-stream: ^3.1.1 + checksum: 699831a8b97666ef50021c767f84924cfee21c142c2eb0e79c63254e140e6408d6d55a065a2992548e72b06de39237ef2b802b99e3ece93ca3904a37622a66f3 + languageName: node + linkType: hard + +"tar@npm:^6.0.2, tar@npm:^6.1.0, tar@npm:^6.1.2": + version: 6.1.11 + resolution: "tar@npm:6.1.11" + dependencies: + chownr: ^2.0.0 + fs-minipass: ^2.0.0 + minipass: ^3.0.0 + minizlib: ^2.1.1 + mkdirp: ^1.0.3 + yallist: ^4.0.0 + checksum: a04c07bb9e2d8f46776517d4618f2406fb977a74d914ad98b264fc3db0fe8224da5bec11e5f8902c5b9bcb8ace22d95fbe3c7b36b8593b7dfc8391a25898f32f + languageName: node + linkType: hard + +"temp-dir@npm:^2.0.0": + version: 2.0.0 + resolution: "temp-dir@npm:2.0.0" + checksum: cc4f0404bf8d6ae1a166e0e64f3f409b423f4d1274d8c02814a59a5529f07db6cd070a749664141b992b2c1af337fa9bb451a460a43bb9bcddc49f235d3115aa + languageName: node + linkType: hard + +"tempfile@npm:^3.0.0": + version: 3.0.0 + resolution: "tempfile@npm:3.0.0" + dependencies: + temp-dir: ^2.0.0 + uuid: ^3.3.2 + checksum: ebf07b7e580ab0a0673093d84007783f3d3bef9f82108e463c353974382296ba2d3309c8ac9574efba0dec8c99c27c5392f11b3de85d3f3a88a22850e9f782ef + languageName: node + linkType: hard + +"terser-webpack-plugin@npm:^5.1.3, terser-webpack-plugin@npm:^5.3.1": + version: 5.3.3 + resolution: "terser-webpack-plugin@npm:5.3.3" + dependencies: + "@jridgewell/trace-mapping": ^0.3.7 + jest-worker: ^27.4.5 + schema-utils: ^3.1.1 + serialize-javascript: ^6.0.0 + terser: ^5.7.2 + peerDependencies: + webpack: ^5.1.0 + peerDependenciesMeta: + "@swc/core": + optional: true + esbuild: + optional: true + uglify-js: + optional: true + checksum: 4b8d508d8a0f6e604addb286975f1fa670f8c3964a67abc03a7cfcfd4cdeca4b07dda6655e1c4425427fb62e4d2b0ca59d84f1b2cd83262ff73616d5d3ccdeb5 + languageName: node + linkType: hard + +"terser@npm:^5.7.2": + version: 5.10.0 + resolution: "terser@npm:5.10.0" + dependencies: + commander: ^2.20.0 + source-map: ~0.7.2 + source-map-support: ~0.5.20 + peerDependencies: + acorn: ^8.5.0 + peerDependenciesMeta: + acorn: + optional: true + bin: + terser: bin/terser + checksum: 1080faeb6d5cd155bb39d9cc41d20a590eafc9869560d5285f255f6858604dcd135311e344188a106f87fedb12d096ad3799cfc2e65acd470b85d468b1c7bd4c + languageName: node + linkType: hard + +"test-exclude@npm:^6.0.0": + version: 6.0.0 + resolution: "test-exclude@npm:6.0.0" + dependencies: + "@istanbuljs/schema": ^0.1.2 + glob: ^7.1.4 + minimatch: ^3.0.4 + checksum: 3b34a3d77165a2cb82b34014b3aba93b1c4637a5011807557dc2f3da826c59975a5ccad765721c4648b39817e3472789f9b0fa98fc854c5c1c7a1e632aacdc28 + languageName: node + linkType: hard + +"text-extensions@npm:^1.0.0": + version: 1.9.0 + resolution: "text-extensions@npm:1.9.0" + checksum: 56a9962c1b62d39b2bcb369b7558ca85c1b55e554b38dfd725edcc0a1babe5815782a60c17ff6b839093b163dfebb92b804208aaaea616ec7571c8059ae0cf44 + languageName: node + linkType: hard + +"text-hex@npm:1.0.x": + version: 1.0.0 + resolution: "text-hex@npm:1.0.0" + checksum: 1138f68adc97bf4381a302a24e2352f04992b7b1316c5003767e9b0d3367ffd0dc73d65001ea02b07cd0ecc2a9d186de0cf02f3c2d880b8a522d4ccb9342244a + languageName: node + linkType: hard + +"text-table@npm:^0.2.0": + version: 0.2.0 + resolution: "text-table@npm:0.2.0" + checksum: b6937a38c80c7f84d9c11dd75e49d5c44f71d95e810a3250bd1f1797fc7117c57698204adf676b71497acc205d769d65c16ae8fa10afad832ae1322630aef10a + languageName: node + linkType: hard + +"textextensions@npm:^5.12.0, textextensions@npm:^5.13.0": + version: 5.14.0 + resolution: "textextensions@npm:5.14.0" + checksum: 1f610ccf2a2c1445fb7156c23b5c8defd608c74e8047df18abd4b9ee44c74d29b453ba104d14c53c91f9026670f7c923114cd12200ee5ec458cc518fd0798c74 + languageName: node + linkType: hard + +"through2@npm:^2.0.0": + version: 2.0.5 + resolution: "through2@npm:2.0.5" + dependencies: + readable-stream: ~2.3.6 + xtend: ~4.0.1 + checksum: beb0f338aa2931e5660ec7bf3ad949e6d2e068c31f4737b9525e5201b824ac40cac6a337224856b56bd1ddd866334bbfb92a9f57cd6f66bc3f18d3d86fc0fe50 + languageName: node + linkType: hard + +"through2@npm:^3.0.1": + version: 3.0.2 + resolution: "through2@npm:3.0.2" + dependencies: + inherits: ^2.0.4 + readable-stream: 2 || 3 + checksum: 47c9586c735e7d9cbbc1029f3ff422108212f7cc42e06d5cc9fff7901e659c948143c790e0d0d41b1b5f89f1d1200bdd200c7b72ad34f42f9edbeb32ea49e8b7 + languageName: node + linkType: hard + +"through2@npm:^4.0.0": + version: 4.0.2 + resolution: "through2@npm:4.0.2" + dependencies: + readable-stream: 3 + checksum: ac7430bd54ccb7920fd094b1c7ff3e1ad6edd94202e5528331253e5fde0cc56ceaa690e8df9895de2e073148c52dfbe6c4db74cacae812477a35660090960cc0 + languageName: node + linkType: hard + +"through@npm:2, through@npm:>=2.2.7 <3, through@npm:^2.3.6, through@npm:^2.3.8": + version: 2.3.8 + resolution: "through@npm:2.3.8" + checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd + languageName: node + linkType: hard + +"timers-browserify@npm:^1.0.1": + version: 1.4.2 + resolution: "timers-browserify@npm:1.4.2" + dependencies: + process: ~0.11.0 + checksum: b7437e228684d8e6e193580d363ffdcd752396c0d1013503f50e412aa86e920248a8627450ad40557443e07ef6b9b602ffc940b3ba06db23774a7ab507e1911d + languageName: node + linkType: hard + +"tiny-emitter@npm:^2.1.0": + version: 2.1.0 + resolution: "tiny-emitter@npm:2.1.0" + checksum: fbcfb5145751a0e3b109507a828eb6d6d4501352ab7bb33eccef46e22e9d9ad3953158870a6966a59e57ab7c3f9cfac7cab8521db4de6a5e757012f4677df2dd + languageName: node + linkType: hard + +"tls@npm:^0.0.1": + version: 0.0.1 + resolution: "tls@npm:0.0.1" + checksum: b0205b0efb5f4537d3031cc3797f93c52fb568f41878795cd5361f604b49bc26ad830c1de340fc71f070967d55f73a6fac8527506fc519efe9c5eadcd510a1f8 + languageName: node + linkType: hard + +"tmp@npm:^0.0.33": + version: 0.0.33 + resolution: "tmp@npm:0.0.33" + dependencies: + os-tmpdir: ~1.0.2 + checksum: 902d7aceb74453ea02abbf58c203f4a8fc1cead89b60b31e354f74ed5b3fb09ea817f94fb310f884a5d16987dd9fa5a735412a7c2dd088dd3d415aa819ae3a28 + languageName: node + linkType: hard + +"tmp@npm:^0.1.0": + version: 0.1.0 + resolution: "tmp@npm:0.1.0" + dependencies: + rimraf: ^2.6.3 + checksum: 6bab8431de9d245d4264bd8cd6bb216f9d22f179f935dada92a11d1315572c8eb7c3334201e00594b4708608bd536fad3a63bfb037e7804d827d66aa53a1afcd + languageName: node + linkType: hard + +"tmp@npm:^0.2.1": + version: 0.2.1 + resolution: "tmp@npm:0.2.1" + dependencies: + rimraf: ^3.0.0 + checksum: 8b1214654182575124498c87ca986ac53dc76ff36e8f0e0b67139a8d221eaecfdec108c0e6ec54d76f49f1f72ab9325500b246f562b926f85bcdfca8bf35df9e + languageName: node + linkType: hard + +"to-fast-properties@npm:^2.0.0": + version: 2.0.0 + resolution: "to-fast-properties@npm:2.0.0" + checksum: be2de62fe58ead94e3e592680052683b1ec986c72d589e7b21e5697f8744cdbf48c266fa72f6c15932894c10187b5f54573a3bcf7da0bfd964d5caf23d436168 + languageName: node + linkType: hard + +"to-readable-stream@npm:^1.0.0": + version: 1.0.0 + resolution: "to-readable-stream@npm:1.0.0" + checksum: 2bd7778490b6214a2c40276065dd88949f4cf7037ce3964c76838b8cb212893aeb9cceaaf4352a4c486e3336214c350270f3263e1ce7a0c38863a715a4d9aeb5 + languageName: node + linkType: hard + +"to-regex-range@npm:^5.0.1": + version: 5.0.1 + resolution: "to-regex-range@npm:5.0.1" + dependencies: + is-number: ^7.0.0 + checksum: f76fa01b3d5be85db6a2a143e24df9f60dd047d151062d0ba3df62953f2f697b16fe5dad9b0ac6191c7efc7b1d9dcaa4b768174b7b29da89d4428e64bc0a20ed + languageName: node + linkType: hard + +"toidentifier@npm:1.0.0": + version: 1.0.0 + resolution: "toidentifier@npm:1.0.0" + checksum: 199e6bfca1531d49b3506cff02353d53ec987c9ee10ee272ca6484ed97f1fc10fb77c6c009079ca16d5c5be4a10378178c3cacdb41ce9ec954c3297c74c6053e + languageName: node + linkType: hard + +"touch@npm:^3.1.0": + version: 3.1.0 + resolution: "touch@npm:3.1.0" + dependencies: + nopt: ~1.0.10 + bin: + nodetouch: ./bin/nodetouch.js + checksum: e0be589cb5b0e6dbfce6e7e077d4a0d5f0aba558ef769c6d9c33f635e00d73d5be49da6f8631db302ee073919d82b5b7f56da2987feb28765c95a7673af68647 + languageName: node + linkType: hard + +"tough-cookie@npm:^2.3.3, tough-cookie@npm:~2.5.0": + version: 2.5.0 + resolution: "tough-cookie@npm:2.5.0" + dependencies: + psl: ^1.1.28 + punycode: ^2.1.1 + checksum: 16a8cd090224dd176eee23837cbe7573ca0fa297d7e468ab5e1c02d49a4e9a97bb05fef11320605eac516f91d54c57838a25864e8680e27b069a5231d8264977 + languageName: node + linkType: hard + +"tr46@npm:~0.0.3": + version: 0.0.3 + resolution: "tr46@npm:0.0.3" + checksum: 726321c5eaf41b5002e17ffbd1fb7245999a073e8979085dacd47c4b4e8068ff5777142fc6726d6ca1fd2ff16921b48788b87225cbc57c72636f6efa8efbffe3 + languageName: node + linkType: hard + +"tree-kill@npm:^1.2.2": + version: 1.2.2 + resolution: "tree-kill@npm:1.2.2" + bin: + tree-kill: cli.js + checksum: 49117f5f410d19c84b0464d29afb9642c863bc5ba40fcb9a245d474c6d5cc64d1b177a6e6713129eb346b40aebb9d4631d967517f9fbe8251c35b21b13cd96c7 + languageName: node + linkType: hard + +"treeverse@npm:^1.0.4": + version: 1.0.4 + resolution: "treeverse@npm:1.0.4" + checksum: 712640acd811060ff552a3c761f700d18d22a4da544d31b4e290817ac4bbbfcfe33b58f85e7a5787e6ff7351d3a9100670721a289ca14eb87b36ad8a0c20ebd8 + languageName: node + linkType: hard + +"trim-newlines@npm:^3.0.0": + version: 3.0.1 + resolution: "trim-newlines@npm:3.0.1" + checksum: b530f3fadf78e570cf3c761fb74fef655beff6b0f84b29209bac6c9622db75ad1417f4a7b5d54c96605dcd72734ad44526fef9f396807b90839449eb543c6206 + languageName: node + linkType: hard + +"triple-beam@npm:^1.2.0, triple-beam@npm:^1.3.0": + version: 1.3.0 + resolution: "triple-beam@npm:1.3.0" + checksum: 7d7b77d8625fb252c126c24984a68de462b538a8fcd1de2abd0a26421629cf3527d48e23b3c2264f08f4a6c3bc40a478a722176f4d7b6a1acc154cb70c359f2b + languageName: node + linkType: hard + +"ts-loader@npm:^8.0.2": + version: 8.3.0 + resolution: "ts-loader@npm:8.3.0" + dependencies: + chalk: ^4.1.0 + enhanced-resolve: ^4.0.0 + loader-utils: ^2.0.0 + micromatch: ^4.0.0 + semver: ^7.3.4 + peerDependencies: + typescript: "*" + webpack: "*" + checksum: 93dd15b553a2621f969c4c834e7eb085b9b079adb702cba68a7ee516bcd2c67620e62cc5a8c57345e90c644ff6b689ee5a09f0702e440284fdfe872e9aaeefd8 + languageName: node + linkType: hard + +"ts-mocha@npm:^8.0.0": + version: 8.0.0 + resolution: "ts-mocha@npm:8.0.0" + dependencies: + ts-node: 7.0.1 + tsconfig-paths: ^3.5.0 + peerDependencies: + mocha: ^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X + dependenciesMeta: + tsconfig-paths: + optional: true + bin: + ts-mocha: bin/ts-mocha + checksum: 66062e82f9be469cdcf51b120ff83e4a69c664fafb3ee020708cb5d65e87ee6bd8c98703a1012f2fb583d540dcbe14e0be77a36ea69a6c08ba3c89345859d8d9 + languageName: node + linkType: hard + +"ts-mock-imports@npm:^1.3.0": + version: 1.3.8 + resolution: "ts-mock-imports@npm:1.3.8" + peerDependencies: + sinon: ">= 4.1.2" + typescript: ">=2.6.1" + checksum: 16009464849249dc807d6e41c4a08a8ea5145bef44ae83701f86c5be43e3582868891035bc6aed18c082f52b3c4db4d81908634162e210cf8d90bf8469a548cd + languageName: node + linkType: hard + +"ts-node@npm:7.0.1": + version: 7.0.1 + resolution: "ts-node@npm:7.0.1" + dependencies: + arrify: ^1.0.0 + buffer-from: ^1.1.0 + diff: ^3.1.0 + make-error: ^1.1.1 + minimist: ^1.2.0 + mkdirp: ^0.5.1 + source-map-support: ^0.5.6 + yn: ^2.0.0 + bin: + ts-node: dist/bin.js + checksum: 07ed6ea1805361828737a767cfd6c57ea6e267ee8679282afb933610af02405e1a87c1f2aea1d38ed8e66b34fcbf6272b6021ab95d78849105d2e57fc283870b + languageName: node + linkType: hard + +"ts-node@npm:^10.4.0": + version: 10.4.0 + resolution: "ts-node@npm:10.4.0" + dependencies: + "@cspotcode/source-map-support": 0.7.0 + "@tsconfig/node10": ^1.0.7 + "@tsconfig/node12": ^1.0.7 + "@tsconfig/node14": ^1.0.0 + "@tsconfig/node16": ^1.0.2 + acorn: ^8.4.1 + acorn-walk: ^8.1.1 + arg: ^4.1.0 + create-require: ^1.1.0 + diff: ^4.0.1 + make-error: ^1.1.1 + yn: 3.1.1 + peerDependencies: + "@swc/core": ">=1.2.50" + "@swc/wasm": ">=1.2.50" + "@types/node": "*" + typescript: ">=2.7" + peerDependenciesMeta: + "@swc/core": + optional: true + "@swc/wasm": + optional: true + bin: + ts-node: dist/bin.js + ts-node-cwd: dist/bin-cwd.js + ts-node-script: dist/bin-script.js + ts-node-transpile-only: dist/bin-transpile.js + ts-script: dist/bin-script-deprecated.js + checksum: 3933ac0a937d33c45e04a6750fcdd3e765eb2897d1da1307cd97ac52af093bcfb632ec0453a75000a65c8b5b7bdb32b1077050a186dcc556e62657cb592e6d49 + languageName: node + linkType: hard + +"tsconfig-paths@npm:^3.11.0, tsconfig-paths@npm:^3.5.0": + version: 3.12.0 + resolution: "tsconfig-paths@npm:3.12.0" + dependencies: + "@types/json5": ^0.0.29 + json5: ^1.0.1 + minimist: ^1.2.0 + strip-bom: ^3.0.0 + checksum: 4999ec6cd1c7cc06750a460dbc0d39fe3595a4308cb5f1d0d0a8283009cf9c0a30d5a156508c28fe3a47760508af5263ab288fc23d71e9762779674257a95d3b + languageName: node + linkType: hard + +"tslib@npm:2.1.0": + version: 2.1.0 + resolution: "tslib@npm:2.1.0" + checksum: aa189c8179de0427b0906da30926fd53c59d96ec239dff87d6e6bc831f608df0cbd6f77c61dabc074408bd0aa0b9ae4ec35cb2c15f729e32f37274db5730cb78 + languageName: node + linkType: hard + +"tslib@npm:^1.9.0": + version: 1.14.1 + resolution: "tslib@npm:1.14.1" + checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd + languageName: node + linkType: hard + +"tslib@npm:^2, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.1": + version: 2.3.1 + resolution: "tslib@npm:2.3.1" + checksum: de17a98d4614481f7fcb5cd53ffc1aaf8654313be0291e1bfaee4b4bb31a20494b7d218ff2e15017883e8ea9626599b3b0e0229c18383ba9dce89da2adf15cb9 + languageName: node + linkType: hard + +"tty-browserify@npm:0.0.1": + version: 0.0.1 + resolution: "tty-browserify@npm:0.0.1" + checksum: 93b745d43fa5a7d2b948fa23be8d313576d1d884b48acd957c07710bac1c0d8ac34c0556ad4c57c73d36e11741763ef66b3fb4fb97b06b7e4d525315a3cd45f5 + languageName: node + linkType: hard + +"tunnel-agent@npm:^0.6.0": + version: 0.6.0 + resolution: "tunnel-agent@npm:0.6.0" + dependencies: + safe-buffer: ^5.0.1 + checksum: 05f6510358f8afc62a057b8b692f05d70c1782b70db86d6a1e0d5e28a32389e52fa6e7707b6c5ecccacc031462e4bc35af85ecfe4bbc341767917b7cf6965711 + languageName: node + linkType: hard + +"tweetnacl@npm:^0.14.3, tweetnacl@npm:~0.14.0": + version: 0.14.5 + resolution: "tweetnacl@npm:0.14.5" + checksum: 6061daba1724f59473d99a7bb82e13f211cdf6e31315510ae9656fefd4779851cb927adad90f3b488c8ed77c106adc0421ea8055f6f976ff21b27c5c4e918487 + languageName: node + linkType: hard + +"type-check@npm:^0.4.0, type-check@npm:~0.4.0": + version: 0.4.0 + resolution: "type-check@npm:0.4.0" + dependencies: + prelude-ls: ^1.2.1 + checksum: ec688ebfc9c45d0c30412e41ca9c0cdbd704580eb3a9ccf07b9b576094d7b86a012baebc95681999dd38f4f444afd28504cb3a89f2ef16b31d4ab61a0739025a + languageName: node + linkType: hard + +"type-check@npm:~0.3.2": + version: 0.3.2 + resolution: "type-check@npm:0.3.2" + dependencies: + prelude-ls: ~1.1.2 + checksum: dd3b1495642731bc0e1fc40abe5e977e0263005551ac83342ecb6f4f89551d106b368ec32ad3fb2da19b3bd7b2d1f64330da2ea9176d8ddbfe389fb286eb5124 + languageName: node + linkType: hard + +"type-detect@npm:4.0.8, type-detect@npm:^4.0.0, type-detect@npm:^4.0.5, type-detect@npm:^4.0.8": + version: 4.0.8 + resolution: "type-detect@npm:4.0.8" + checksum: 62b5628bff67c0eb0b66afa371bd73e230399a8d2ad30d852716efcc4656a7516904570cd8631a49a3ce57c10225adf5d0cbdcb47f6b0255fe6557c453925a15 + languageName: node + linkType: hard + +"type-fest@npm:^0.18.0": + version: 0.18.1 + resolution: "type-fest@npm:0.18.1" + checksum: e96dcee18abe50ec82dab6cbc4751b3a82046da54c52e3b2d035b3c519732c0b3dd7a2fa9df24efd1a38d953d8d4813c50985f215f1957ee5e4f26b0fe0da395 + languageName: node + linkType: hard + +"type-fest@npm:^0.20.2": + version: 0.20.2 + resolution: "type-fest@npm:0.20.2" + checksum: 4fb3272df21ad1c552486f8a2f8e115c09a521ad7a8db3d56d53718d0c907b62c6e9141ba5f584af3f6830d0872c521357e512381f24f7c44acae583ad517d73 + languageName: node + linkType: hard + +"type-fest@npm:^0.21.2, type-fest@npm:^0.21.3": + version: 0.21.3 + resolution: "type-fest@npm:0.21.3" + checksum: e6b32a3b3877f04339bae01c193b273c62ba7bfc9e325b8703c4ee1b32dc8fe4ef5dfa54bf78265e069f7667d058e360ae0f37be5af9f153b22382cd55a9afe0 + languageName: node + linkType: hard + +"type-fest@npm:^0.6.0": + version: 0.6.0 + resolution: "type-fest@npm:0.6.0" + checksum: b2188e6e4b21557f6e92960ec496d28a51d68658018cba8b597bd3ef757721d1db309f120ae987abeeda874511d14b776157ff809f23c6d1ce8f83b9b2b7d60f + languageName: node + linkType: hard + +"type-fest@npm:^0.8.0, type-fest@npm:^0.8.1": + version: 0.8.1 + resolution: "type-fest@npm:0.8.1" + checksum: d61c4b2eba24009033ae4500d7d818a94fd6d1b481a8111612ee141400d5f1db46f199c014766b9fa9b31a6a7374d96fc748c6d688a78a3ce5a33123839becb7 + languageName: node + linkType: hard + +"type-is@npm:~1.6.17": + version: 1.6.18 + resolution: "type-is@npm:1.6.18" + dependencies: + media-typer: 0.3.0 + mime-types: ~2.1.24 + checksum: 2c8e47675d55f8b4e404bcf529abdf5036c537a04c2b20177bcf78c9e3c1da69da3942b1346e6edb09e823228c0ee656ef0e033765ec39a70d496ef601a0c657 + languageName: node + linkType: hard + +"typed-function@npm:^2.1.0": + version: 2.1.0 + resolution: "typed-function@npm:2.1.0" + checksum: 168c2c8f765fbecc842521a5fb62a5800958f9fcb0ce78d69c38a5e96c81fe133f853256ec8ee245ee2fc42b4c9342b5ba754c732189550c64c12e758892dc43 + languageName: node + linkType: hard + +"typedarray-to-buffer@npm:^3.1.5": + version: 3.1.5 + resolution: "typedarray-to-buffer@npm:3.1.5" + dependencies: + is-typedarray: ^1.0.0 + checksum: 99c11aaa8f45189fcfba6b8a4825fd684a321caa9bd7a76a27cf0c7732c174d198b99f449c52c3818107430b5f41c0ccbbfb75cb2ee3ca4a9451710986d61a60 + languageName: node + linkType: hard + +"typedarray@npm:^0.0.6": + version: 0.0.6 + resolution: "typedarray@npm:0.0.6" + checksum: 33b39f3d0e8463985eeaeeacc3cb2e28bc3dfaf2a5ed219628c0b629d5d7b810b0eb2165f9f607c34871d5daa92ba1dc69f49051cf7d578b4cbd26c340b9d1b1 + languageName: node + linkType: hard + +typescript@^3.9.5: + version: 3.9.10 + resolution: "typescript@npm:3.9.10" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 46c842e2cd4797b88b66ef06c9c41dd21da48b95787072ccf39d5f2aa3124361bc4c966aa1c7f709fae0509614d76751455b5231b12dbb72eb97a31369e1ff92 + languageName: node + linkType: hard + +"typescript@patch:typescript@^3.9.5#~builtin": + version: 3.9.10 + resolution: "typescript@patch:typescript@npm%3A3.9.10#~builtin::version=3.9.10&hash=ddd1e8" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: dc7141ab555b23a8650a6787f98845fc11692063d02b75ff49433091b3af2fe3d773650dea18389d7c21f47d620fb3b110ea363dab4ab039417a6ccbbaf96fc2 + languageName: node + linkType: hard + +"ua-parser-js@npm:^0.7.30": + version: 0.7.31 + resolution: "ua-parser-js@npm:0.7.31" + checksum: e2f8324a83d1715601576af85b2b6c03890699aaa7272950fc77ea925c70c5e4f75060ae147dc92124e49f7f0e3d6dd2b0a91e7f40d267e92df8894be967ba8b + languageName: node + linkType: hard + +"uglify-js@npm:^3.1.4, uglify-js@npm:^3.14.4": + version: 3.14.4 + resolution: "uglify-js@npm:3.14.4" + bin: + uglifyjs: bin/uglifyjs + checksum: 13217db5212a201de2ad89873a4e31b26a140c21c0239cefea4ee1c2861c71a5c133538312ce08c92bf97e5b00c8e170d0ef90213026c17ffa689d2e2cdbce73 + languageName: node + linkType: hard + +"ultra-runner@npm:^3.10.5": + version: 3.10.5 + resolution: "ultra-runner@npm:3.10.5" + dependencies: + ansi-split: ^1.0.1 + chalk: ^4.1.0 + cross-spawn: ^7.0.3 + fast-glob: ^3.2.5 + globrex: ^0.1.2 + ignore: ^5.1.8 + json5: ^2.2.0 + micro-memoize: ^4.0.9 + npm-run-path: 4.0.1 + pid-cwd: ^1.2.0 + ps-list: ^7.2.0 + shellwords-ts: ^3.0.0 + string-width: ^4.2.0 + tslib: 2.1.0 + type-fest: ^0.21.2 + wrap-ansi: ^7.0.0 + yamljs: ^0.3.0 + yargs: ^16.2.0 + bin: + ultra: bin/ultra.js + checksum: 4aed834863a8c199b093131fd0c66cf0699fdba715815d6b0672a7c69148ad3a920dd758aba9d9e76fae01472fb7d1df1feaeae9292b21fec89412f1ef1b916f + languageName: node + linkType: hard + +"umd@npm:^3.0.0": + version: 3.0.3 + resolution: "umd@npm:3.0.3" + bin: + umd: ./bin/cli.js + checksum: 264302acabbc71ef279cfb832d6bb53096a12618e9ef8465b274c5a3fffa5f4da6cf7b8d024fec53a7114742c132bba9f6a6d4d4b5eca2bb55d556d0c57a9f15 + languageName: node + linkType: hard + +"unbox-primitive@npm:^1.0.1": + version: 1.0.1 + resolution: "unbox-primitive@npm:1.0.1" + dependencies: + function-bind: ^1.1.1 + has-bigints: ^1.0.1 + has-symbols: ^1.0.2 + which-boxed-primitive: ^1.0.2 + checksum: 89d950e18fb45672bc6b3c961f1e72c07beb9640c7ceed847b571ba6f7d2af570ae1a2584cfee268b9d9ea1e3293f7e33e0bc29eaeb9f8e8a0bab057ff9e6bba + languageName: node + linkType: hard + +"undeclared-identifiers@npm:^1.1.2": + version: 1.1.3 + resolution: "undeclared-identifiers@npm:1.1.3" + dependencies: + acorn-node: ^1.3.0 + dash-ast: ^1.0.0 + get-assigned-identifiers: ^1.2.0 + simple-concat: ^1.0.0 + xtend: ^4.0.1 + bin: + undeclared-identifiers: bin.js + checksum: e1f2a18d7bf735ec2b9ee464a621d8db72768e75e59334d34d1f7085e21558c621cc105dfd4cc7a0a219b91c43b71fbdea0508cdbe3b3396ed96902c6d5d590e + languageName: node + linkType: hard + +"undefsafe@npm:^2.0.5": + version: 2.0.5 + resolution: "undefsafe@npm:2.0.5" + checksum: f42ab3b5770fedd4ada175fc1b2eb775b78f609156f7c389106aafd231bfc210813ee49f54483d7191d7b76e483bc7f537b5d92d19ded27156baf57592eb02cc + languageName: node + linkType: hard + +"unicode-canonical-property-names-ecmascript@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" + checksum: 39be078afd014c14dcd957a7a46a60061bc37c4508ba146517f85f60361acf4c7539552645ece25de840e17e293baa5556268d091ca6762747fdd0c705001a45 + languageName: node + linkType: hard + +"unicode-match-property-ecmascript@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-match-property-ecmascript@npm:2.0.0" + dependencies: + unicode-canonical-property-names-ecmascript: ^2.0.0 + unicode-property-aliases-ecmascript: ^2.0.0 + checksum: 1f34a7434a23df4885b5890ac36c5b2161a809887000be560f56ad4b11126d433c0c1c39baf1016bdabed4ec54829a6190ee37aa24919aa116dc1a5a8a62965a + languageName: node + linkType: hard + +"unicode-match-property-value-ecmascript@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-match-property-value-ecmascript@npm:2.0.0" + checksum: 8fe6a09d9085a625cabcead5d95bdbc1a2d5d481712856092ce0347231e81a60b93a68f1b69e82b3076a07e415a72c708044efa2aa40ae23e2e7b5c99ed4a9ea + languageName: node + linkType: hard + +"unicode-property-aliases-ecmascript@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-property-aliases-ecmascript@npm:2.0.0" + checksum: dda4d39128cbbede2ac60fbb85493d979ec65913b8a486bf7cb7a375a2346fa48cbf9dc6f1ae23376e7e8e684c2b411434891e151e865a661b40a85407db51d0 + languageName: node + linkType: hard + +"unique-filename@npm:^1.1.1": + version: 1.1.1 + resolution: "unique-filename@npm:1.1.1" + dependencies: + unique-slug: ^2.0.0 + checksum: cf4998c9228cc7647ba7814e255dec51be43673903897b1786eff2ac2d670f54d4d733357eb08dea969aa5e6875d0e1bd391d668fbdb5a179744e7c7551a6f80 + languageName: node + linkType: hard + +"unique-slug@npm:^2.0.0": + version: 2.0.2 + resolution: "unique-slug@npm:2.0.2" + dependencies: + imurmurhash: ^0.1.4 + checksum: 5b6876a645da08d505dedb970d1571f6cebdf87044cb6b740c8dbb24f0d6e1dc8bdbf46825fd09f994d7cf50760e6f6e063cfa197d51c5902c00a861702eb75a + languageName: node + linkType: hard + +"unique-string@npm:^2.0.0": + version: 2.0.0 + resolution: "unique-string@npm:2.0.0" + dependencies: + crypto-random-string: ^2.0.0 + checksum: ef68f639136bcfe040cf7e3cd7a8dff076a665288122855148a6f7134092e6ed33bf83a7f3a9185e46c98dddc445a0da6ac25612afa1a7c38b8b654d6c02498e + languageName: node + linkType: hard + +"universal-user-agent@npm:^6.0.0": + version: 6.0.0 + resolution: "universal-user-agent@npm:6.0.0" + checksum: 5092bbc80dd0d583cef0b62c17df0043193b74f425112ea6c1f69bc5eda21eeec7a08d8c4f793a277eb2202ffe9b44bec852fa3faff971234cd209874d1b79ef + languageName: node + linkType: hard + +"universalify@npm:^0.1.0": + version: 0.1.2 + resolution: "universalify@npm:0.1.2" + checksum: 40cdc60f6e61070fe658ca36016a8f4ec216b29bf04a55dce14e3710cc84c7448538ef4dad3728d0bfe29975ccd7bfb5f414c45e7b78883567fb31b246f02dff + languageName: node + linkType: hard + +"universalify@npm:^2.0.0": + version: 2.0.0 + resolution: "universalify@npm:2.0.0" + checksum: 2406a4edf4a8830aa6813278bab1f953a8e40f2f63a37873ffa9a3bc8f9745d06cc8e88f3572cb899b7e509013f7f6fcc3e37e8a6d914167a5381d8440518c44 + languageName: node + linkType: hard + +"unorm@npm:^1.6.0": + version: 1.6.0 + resolution: "unorm@npm:1.6.0" + checksum: 9a86546256a45f855b6cfe719086785d6aada94f63778cecdecece8d814ac26af76cb6da70130da0a08b8803bbf0986e56c7ec4249038198f3de02607fffd811 + languageName: node + linkType: hard + +"unpipe@npm:1.0.0, unpipe@npm:~1.0.0": + version: 1.0.0 + resolution: "unpipe@npm:1.0.0" + checksum: 4fa18d8d8d977c55cb09715385c203197105e10a6d220087ec819f50cb68870f02942244f1017565484237f1f8c5d3cd413631b1ae104d3096f24fdfde1b4aa2 + languageName: node + linkType: hard + +"untildify@npm:^4.0.0": + version: 4.0.0 + resolution: "untildify@npm:4.0.0" + checksum: 39ced9c418a74f73f0a56e1ba4634b4d959422dff61f4c72a8e39f60b99380c1b45ed776fbaa0a4101b157e4310d873ad7d114e8534ca02609b4916bb4187fb9 + languageName: node + linkType: hard + +"update-notifier@npm:^5.1.0": + version: 5.1.0 + resolution: "update-notifier@npm:5.1.0" + dependencies: + boxen: ^5.0.0 + chalk: ^4.1.0 + configstore: ^5.0.1 + has-yarn: ^2.1.0 + import-lazy: ^2.1.0 + is-ci: ^2.0.0 + is-installed-globally: ^0.4.0 + is-npm: ^5.0.0 + is-yarn-global: ^0.3.0 + latest-version: ^5.1.0 + pupa: ^2.1.1 + semver: ^7.3.4 + semver-diff: ^3.1.1 + xdg-basedir: ^4.0.0 + checksum: 461e5e5b002419296d3868ee2abe0f9ab3e1846d9db642936d0c46f838872ec56069eddfe662c45ce1af0a8d6d5026353728de2e0a95ab2e3546a22ea077caf1 + languageName: node + linkType: hard + +"uri-js@npm:^4.2.2": + version: 4.4.1 + resolution: "uri-js@npm:4.4.1" + dependencies: + punycode: ^2.1.0 + checksum: 7167432de6817fe8e9e0c9684f1d2de2bb688c94388f7569f7dbdb1587c9f4ca2a77962f134ec90be0cc4d004c939ff0d05acc9f34a0db39a3c797dada262633 + languageName: node + linkType: hard + +"url-parse-lax@npm:^3.0.0": + version: 3.0.0 + resolution: "url-parse-lax@npm:3.0.0" + dependencies: + prepend-http: ^2.0.0 + checksum: 1040e357750451173132228036aff1fd04abbd43eac1fb3e4fca7495a078bcb8d33cb765fe71ad7e473d9c94d98fd67adca63bd2716c815a2da066198dd37217 + languageName: node + linkType: hard + +"url@npm:0.10.3": + version: 0.10.3 + resolution: "url@npm:0.10.3" + dependencies: + punycode: 1.3.2 + querystring: 0.2.0 + checksum: 7b83ddb106c27bf9bde8629ccbe8d26e9db789c8cda5aa7db72ca2c6f9b8a88a5adf206f3e10db78e6e2d042b327c45db34c7010c1bf0d9908936a17a2b57d05 + languageName: node + linkType: hard + +"url@npm:^0.11.0, url@npm:~0.11.0": + version: 0.11.0 + resolution: "url@npm:0.11.0" + dependencies: + punycode: 1.3.2 + querystring: 0.2.0 + checksum: 50d100d3dd2d98b9fe3ada48cadb0b08aa6be6d3ac64112b867b56b19be4bfcba03c2a9a0d7922bfd7ac17d4834e88537749fe182430dfd9b68e520175900d90 + languageName: node + linkType: hard + +"utf-8-validate@npm:^5.0.9": + version: 5.0.9 + resolution: "utf-8-validate@npm:5.0.9" + dependencies: + node-gyp: latest + node-gyp-build: ^4.3.0 + checksum: 90117f1b65e0a1256c83dfad529983617263b622f2379745311d0438c7ea31db0d134ebd0dca84c3f5847a3560a3d249644e478a9109c616f63c7ea19cac53dc + languageName: node + linkType: hard + +"utf8@npm:^2.1.1": + version: 2.1.2 + resolution: "utf8@npm:2.1.2" + checksum: de5d18adb219cae7871e1c105249e2fc7e6cae0e01c2b4c2eb6b099851b3bf62d1db6be6d83b5e4dea09036f8d16dd7222ad46eb326b38940a988e86743c1a61 + languageName: node + linkType: hard + +"util-deprecate@npm:^1.0.1, util-deprecate@npm:~1.0.1": + version: 1.0.2 + resolution: "util-deprecate@npm:1.0.2" + checksum: 474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2 + languageName: node + linkType: hard + +"util@npm:0.10.3": + version: 0.10.3 + resolution: "util@npm:0.10.3" + dependencies: + inherits: 2.0.1 + checksum: bd800f5d237a82caddb61723a6cbe45297d25dd258651a31335a4d5d981fd033cb4771f82db3d5d59b582b187cb69cfe727dc6f4d8d7826f686ee6c07ce611e0 + languageName: node + linkType: hard + +"util@npm:^0.12.0, util@npm:^0.12.4": + version: 0.12.4 + resolution: "util@npm:0.12.4" + dependencies: + inherits: ^2.0.3 + is-arguments: ^1.0.4 + is-generator-function: ^1.0.7 + is-typed-array: ^1.1.3 + safe-buffer: ^5.1.2 + which-typed-array: ^1.1.2 + checksum: 8eac7a6e6b341c0f1b3eb73bbe5dfcae31a7e9699c8fc3266789f3e95f7637946a7700dcf1904dbd3749a58a36760ebf7acf4bb5b717f7468532a8a79f44eff0 + languageName: node + linkType: hard + +"util@npm:~0.10.1": + version: 0.10.4 + resolution: "util@npm:0.10.4" + dependencies: + inherits: 2.0.3 + checksum: 913f9a90d05a60e91f91af01b8bd37e06bca4cc02d7b49e01089f9d5b78be2fffd61fb1a41b517de7238c5fc7337fa939c62d1fb4eb82e014894c7bee6637aaf + languageName: node + linkType: hard + +"utils-merge@npm:1.0.1": + version: 1.0.1 + resolution: "utils-merge@npm:1.0.1" + checksum: c81095493225ecfc28add49c106ca4f09cdf56bc66731aa8dabc2edbbccb1e1bfe2de6a115e5c6a380d3ea166d1636410b62ef216bb07b3feb1cfde1d95d5080 + languageName: node + linkType: hard + +"uuid@npm:3.3.2": + version: 3.3.2 + resolution: "uuid@npm:3.3.2" + bin: + uuid: ./bin/uuid + checksum: 8793629d2799f500aeea9fcd0aec6c4e9fbcc4d62ed42159ad96be345c3fffac1bbf61a23e18e2782600884fee05e6d4012ce4b70d0037c8e987533ae6a77870 + languageName: node + linkType: hard + +"uuid@npm:^3.2.1, uuid@npm:^3.3.2, uuid@npm:^3.3.3, uuid@npm:^3.4.0": + version: 3.4.0 + resolution: "uuid@npm:3.4.0" + bin: + uuid: ./bin/uuid + checksum: 58de2feed61c59060b40f8203c0e4ed7fd6f99d42534a499f1741218a1dd0c129f4aa1de797bcf822c8ea5da7e4137aa3673431a96dae729047f7aca7b27866f + languageName: node + linkType: hard + +"v8-compile-cache@npm:^2.0.3": + version: 2.3.0 + resolution: "v8-compile-cache@npm:2.3.0" + checksum: adb0a271eaa2297f2f4c536acbfee872d0dd26ec2d76f66921aa7fc437319132773483344207bdbeee169225f4739016d8d2dbf0553913a52bb34da6d0334f8e + languageName: node + linkType: hard + +"validate-npm-package-license@npm:^3.0.1": + version: 3.0.4 + resolution: "validate-npm-package-license@npm:3.0.4" + dependencies: + spdx-correct: ^3.0.0 + spdx-expression-parse: ^3.0.0 + checksum: 35703ac889d419cf2aceef63daeadbe4e77227c39ab6287eeb6c1b36a746b364f50ba22e88591f5d017bc54685d8137bc2d328d0a896e4d3fd22093c0f32a9ad + languageName: node + linkType: hard + +"validate-npm-package-name@npm:^3.0.0": + version: 3.0.0 + resolution: "validate-npm-package-name@npm:3.0.0" + dependencies: + builtins: ^1.0.3 + checksum: ce4c68207abfb22c05eedb09ff97adbcedc80304a235a0844f5344f1fd5086aa80e4dbec5684d6094e26e35065277b765c1caef68bcea66b9056761eddb22967 + languageName: node + linkType: hard + +"validator@npm:^13.6.0": + version: 13.7.0 + resolution: "validator@npm:13.7.0" + checksum: 2b83283de1222ca549a7ef57f46e8d49c6669213348db78b7045bce36a3b5843ff1e9f709ebf74574e06223461ee1f264f8cc9a26a0060a79a27de079d8286ef + languageName: node + linkType: hard + +"varint@npm:5.0.0": + version: 5.0.0 + resolution: "varint@npm:5.0.0" + checksum: 527c65ad87f1d140c03cf734d5c193430ef75fc21b7ec9d2b72f06ee19dbf686be70e0bee27674db3807cedb73ba13ce36a589427ebe52ac620de11686a74c1c + languageName: node + linkType: hard + +"varint@npm:~5.0.0": + version: 5.0.2 + resolution: "varint@npm:5.0.2" + checksum: e1a66bf9a6cea96d1f13259170d4d41b845833acf3a9df990ea1e760d279bd70d5b1f4c002a50197efd2168a2fd43eb0b808444600fd4d23651e8d42fe90eb05 + languageName: node + linkType: hard + +"vary@npm:^1": + version: 1.1.2 + resolution: "vary@npm:1.1.2" + checksum: ae0123222c6df65b437669d63dfa8c36cee20a504101b2fcd97b8bf76f91259c17f9f2b4d70a1e3c6bbcee7f51b28392833adb6b2770b23b01abec84e369660b + languageName: node + linkType: hard + +"verror@npm:1.10.0": + version: 1.10.0 + resolution: "verror@npm:1.10.0" + dependencies: + assert-plus: ^1.0.0 + core-util-is: 1.0.2 + extsprintf: ^1.2.0 + checksum: c431df0bedf2088b227a4e051e0ff4ca54df2c114096b0c01e1cbaadb021c30a04d7dd5b41ab277bcd51246ca135bf931d4c4c796ecae7a4fef6d744ecef36ea + languageName: node + linkType: hard + +"vinyl-file@npm:^3.0.0": + version: 3.0.0 + resolution: "vinyl-file@npm:3.0.0" + dependencies: + graceful-fs: ^4.1.2 + pify: ^2.3.0 + strip-bom-buf: ^1.0.0 + strip-bom-stream: ^2.0.0 + vinyl: ^2.0.1 + checksum: e187a74d41f45d22e8faa17b5552a795aca7c4084034dd9683086ace3752651f164c42aee1961081005f8299388b0a62ad1e3eea991a8d3747db58502f900ff4 + languageName: node + linkType: hard + +"vinyl@npm:^2.0.1": + version: 2.2.1 + resolution: "vinyl@npm:2.2.1" + dependencies: + clone: ^2.1.1 + clone-buffer: ^1.0.0 + clone-stats: ^1.0.0 + cloneable-readable: ^1.0.0 + remove-trailing-separator: ^1.0.1 + replace-ext: ^1.0.0 + checksum: 1f663973f1362f2d074b554f79ff7673187667082373b3d3e628beb1fc2a7ff33024f10b492fbd8db421a09ea3b7b22c3d3de4a0f0e73ead7b4685af570b906f + languageName: node + linkType: hard + +"vm-browserify@npm:^1.0.0": + version: 1.1.2 + resolution: "vm-browserify@npm:1.1.2" + checksum: 10a1c50aab54ff8b4c9042c15fc64aefccce8d2fb90c0640403242db0ee7fb269f9b102bdb69cfb435d7ef3180d61fd4fb004a043a12709abaf9056cfd7e039d + languageName: node + linkType: hard + +"void-elements@npm:^2.0.0": + version: 2.0.1 + resolution: "void-elements@npm:2.0.1" + checksum: 700c07ba9cfa2dff88bb23974b3173118f9ad8107143db9e5d753552be15cf93380954d4e7f7d7bc80e7306c35c3a7fb83ab0ce4d4dcc18abf90ca8b31452126 + languageName: node + linkType: hard + +"walk-up-path@npm:^1.0.0": + version: 1.0.0 + resolution: "walk-up-path@npm:1.0.0" + checksum: b8019ac4fb9ba1576839ec66d2217f62ab773c1cc4c704bfd1c79b1359fef5366f1382d3ab230a66a14c3adb1bf0fe102d1fdaa3437881e69154dfd1432abd32 + languageName: node + linkType: hard + +"watchpack@npm:^2.2.0": + version: 2.2.0 + resolution: "watchpack@npm:2.2.0" + dependencies: + glob-to-regexp: ^0.4.1 + graceful-fs: ^4.1.2 + checksum: e275f48fae29edee3195c51a8312b609581b9be5ce323d3102ffd082cb124f48d7a393ce05e4110239e4354379e04d78a97ceb26ae367746e7e218bf258135c8 + languageName: node + linkType: hard + +"wcwidth@npm:^1.0.1": + version: 1.0.1 + resolution: "wcwidth@npm:1.0.1" + dependencies: + defaults: ^1.0.3 + checksum: 814e9d1ddcc9798f7377ffa448a5a3892232b9275ebb30a41b529607691c0491de47cba426e917a4d08ded3ee7e9ba2f3fe32e62ee3cd9c7d3bafb7754bd553c + languageName: node + linkType: hard + +"webidl-conversions@npm:^3.0.0": + version: 3.0.1 + resolution: "webidl-conversions@npm:3.0.1" + checksum: c92a0a6ab95314bde9c32e1d0a6dfac83b578f8fa5f21e675bc2706ed6981bc26b7eb7e6a1fab158e5ce4adf9caa4a0aee49a52505d4d13c7be545f15021b17c + languageName: node + linkType: hard + +"webpack-cli@npm:^4.9.1": + version: 4.9.1 + resolution: "webpack-cli@npm:4.9.1" + dependencies: + "@discoveryjs/json-ext": ^0.5.0 + "@webpack-cli/configtest": ^1.1.0 + "@webpack-cli/info": ^1.4.0 + "@webpack-cli/serve": ^1.6.0 + colorette: ^2.0.14 + commander: ^7.0.0 + execa: ^5.0.0 + fastest-levenshtein: ^1.0.12 + import-local: ^3.0.2 + interpret: ^2.2.0 + rechoir: ^0.7.0 + webpack-merge: ^5.7.3 + peerDependencies: + webpack: 4.x.x || 5.x.x + peerDependenciesMeta: + "@webpack-cli/generators": + optional: true + "@webpack-cli/migrate": + optional: true + webpack-bundle-analyzer: + optional: true + webpack-dev-server: + optional: true + bin: + webpack-cli: bin/cli.js + checksum: 2aff0349c15e54d616e1fd6dc1f59be16ec1a630f652f948c0b4b108776d1889446e3498e83d9d514bf1b28c5125a8b87c4aeb5dceb41b593ba90765af673c4f + languageName: node + linkType: hard + +"webpack-merge@npm:^4.1.5": + version: 4.2.2 + resolution: "webpack-merge@npm:4.2.2" + dependencies: + lodash: ^4.17.15 + checksum: ce58bc8ab53a3dd5d9a0df65684571349eef53372bf8f224521072110485391335b26ab097c5f07829b88d0c146056944149566e5a953f05997b0fe2cbaf8dd6 + languageName: node + linkType: hard + +"webpack-merge@npm:^5.7.3": + version: 5.8.0 + resolution: "webpack-merge@npm:5.8.0" + dependencies: + clone-deep: ^4.0.1 + wildcard: ^2.0.0 + checksum: 88786ab91013f1bd2a683834ff381be81c245a4b0f63304a5103e90f6653f44dab496a0768287f8531761f8ad957d1f9f3ccb2cb55df0de1bd9ee343e079da26 + languageName: node + linkType: hard + +"webpack-sources@npm:^3.2.2": + version: 3.2.2 + resolution: "webpack-sources@npm:3.2.2" + checksum: cc81f1f1bfd1c25c7a565598850294b515bcccf7974d0249b4a0c8c607307866ce3f9e8cdef1c74d5facfb0d993944c499cfd4b7c8f52d01359b6671cc5823d4 + languageName: node + linkType: hard + +"webpack@npm:^5.59.1": + version: 5.64.1 + resolution: "webpack@npm:5.64.1" + dependencies: + "@types/eslint-scope": ^3.7.0 + "@types/estree": ^0.0.50 + "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/wasm-edit": 1.11.1 + "@webassemblyjs/wasm-parser": 1.11.1 + acorn: ^8.4.1 + acorn-import-assertions: ^1.7.6 + browserslist: ^4.14.5 + chrome-trace-event: ^1.0.2 + enhanced-resolve: ^5.8.3 + es-module-lexer: ^0.9.0 + eslint-scope: 5.1.1 + events: ^3.2.0 + glob-to-regexp: ^0.4.1 + graceful-fs: ^4.2.4 + json-parse-better-errors: ^1.0.2 + loader-runner: ^4.2.0 + mime-types: ^2.1.27 + neo-async: ^2.6.2 + schema-utils: ^3.1.0 + tapable: ^2.1.1 + terser-webpack-plugin: ^5.1.3 + watchpack: ^2.2.0 + webpack-sources: ^3.2.2 + peerDependenciesMeta: + webpack-cli: + optional: true + bin: + webpack: bin/webpack.js + checksum: d2a1baddaed03f2ce70c13501b89935d4bb4af30b4accb0d2dfbd00ec6f491eaf46272346cedcf013b477e131c8cc202ee0b2d2c51ece1cfb713fd2d98b80a52 + languageName: node + linkType: hard + +"whatwg-url@npm:^5.0.0": + version: 5.0.0 + resolution: "whatwg-url@npm:5.0.0" + dependencies: + tr46: ~0.0.3 + webidl-conversions: ^3.0.0 + checksum: b8daed4ad3356cc4899048a15b2c143a9aed0dfae1f611ebd55073310c7b910f522ad75d727346ad64203d7e6c79ef25eafd465f4d12775ca44b90fa82ed9e2c + languageName: node + linkType: hard + +"which-boxed-primitive@npm:^1.0.2": + version: 1.0.2 + resolution: "which-boxed-primitive@npm:1.0.2" + 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 + checksum: 53ce774c7379071729533922adcca47220228405e1895f26673bbd71bdf7fb09bee38c1d6399395927c6289476b5ae0629863427fd151491b71c4b6cb04f3a5e + languageName: node + linkType: hard + +"which-module@npm:^2.0.0": + version: 2.0.0 + resolution: "which-module@npm:2.0.0" + checksum: 809f7fd3dfcb2cdbe0180b60d68100c88785084f8f9492b0998c051d7a8efe56784492609d3f09ac161635b78ea29219eb1418a98c15ce87d085bce905705c9c + languageName: node + linkType: hard + +"which-pm@npm:2.0.0": + version: 2.0.0 + resolution: "which-pm@npm:2.0.0" + dependencies: + load-yaml-file: ^0.2.0 + path-exists: ^4.0.0 + checksum: e556635eaf237b3a101043a21c2890af045db40eac4df3575161d4fb834c2aa65456f81c60d8ea4db2d51fe5ac549d989eeabd17278767c2e4179361338ac5ce + languageName: node + linkType: hard + +"which-typed-array@npm:^1.1.2": + version: 1.1.7 + resolution: "which-typed-array@npm:1.1.7" + dependencies: + available-typed-arrays: ^1.0.5 + call-bind: ^1.0.2 + es-abstract: ^1.18.5 + foreach: ^2.0.5 + has-tostringtag: ^1.0.0 + is-typed-array: ^1.1.7 + checksum: 147837cf5866e36b6b2e427731709e02f79f1578477cbde68ed773a5307520a6cb6836c73c79c30690a473266ee59010b83b6d9b25d8d677a40ff77fb37a8a84 + languageName: node + linkType: hard + +"which@npm:2.0.2, which@npm:^2.0.1, which@npm:^2.0.2": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: ^2.0.0 + bin: + node-which: ./bin/node-which + checksum: 1a5c563d3c1b52d5f893c8b61afe11abc3bab4afac492e8da5bde69d550de701cf9806235f20a47b5c8fa8a1d6a9135841de2596535e998027a54589000e66d1 + languageName: node + linkType: hard + +"which@npm:^1.2.1, which@npm:^1.2.9": + version: 1.3.1 + resolution: "which@npm:1.3.1" + dependencies: + isexe: ^2.0.0 + bin: + which: ./bin/which + checksum: f2e185c6242244b8426c9df1510e86629192d93c1a986a7d2a591f2c24869e7ffd03d6dac07ca863b2e4c06f59a4cc9916c585b72ee9fa1aa609d0124df15e04 + languageName: node + linkType: hard + +"wide-align@npm:^1.1.0, wide-align@npm:^1.1.2": + version: 1.1.5 + resolution: "wide-align@npm:1.1.5" + dependencies: + string-width: ^1.0.2 || 2 || 3 || 4 + checksum: d5fc37cd561f9daee3c80e03b92ed3e84d80dde3365a8767263d03dacfc8fa06b065ffe1df00d8c2a09f731482fcacae745abfbb478d4af36d0a891fad4834d3 + languageName: node + linkType: hard + +"widest-line@npm:^3.1.0": + version: 3.1.0 + resolution: "widest-line@npm:3.1.0" + dependencies: + string-width: ^4.0.0 + checksum: 03db6c9d0af9329c37d74378ff1d91972b12553c7d72a6f4e8525fe61563fa7adb0b9d6e8d546b7e059688712ea874edd5ded475999abdeedf708de9849310e0 + languageName: node + linkType: hard + +"wildcard@npm:^2.0.0": + version: 2.0.0 + resolution: "wildcard@npm:2.0.0" + checksum: 1f4fe4c03dfc492777c60f795bbba597ac78794f1b650d68f398fbee9adb765367c516ebd4220889b6a81e9626e7228bbe0d66237abb311573c2ee1f4902a5ad + languageName: node + linkType: hard + +"winston-transport@npm:^4.4.0": + version: 4.4.0 + resolution: "winston-transport@npm:4.4.0" + dependencies: + readable-stream: ^2.3.7 + triple-beam: ^1.2.0 + checksum: 953d78d152b355962d97697c3ccdc26fda6be017a0e1e555729e218d1269aa32a60e9ff16eb7a72c6403f733e88bab664b259feae3857667b54ff8e2f149fa52 + languageName: node + linkType: hard + +"winston@npm:^3.2.1": + version: 3.3.3 + resolution: "winston@npm:3.3.3" + dependencies: + "@dabh/diagnostics": ^2.0.2 + async: ^3.1.0 + is-stream: ^2.0.0 + logform: ^2.2.0 + one-time: ^1.0.0 + readable-stream: ^3.4.0 + stack-trace: 0.0.x + triple-beam: ^1.3.0 + winston-transport: ^4.4.0 + checksum: 89a0a8db4e577d0df2bee8af67a751663fb80aaa782750b5a0a151a6bf97074dd0eb7c81780e196197735b851c12ea9c176952128fc51fae07a8a5ddba82913a + languageName: node + linkType: hard + +"word-wrap@npm:^1.2.3, word-wrap@npm:~1.2.3": + version: 1.2.3 + resolution: "word-wrap@npm:1.2.3" + checksum: 30b48f91fcf12106ed3186ae4fa86a6a1842416df425be7b60485de14bec665a54a68e4b5156647dec3a70f25e84d270ca8bc8cd23182ed095f5c7206a938c1f + languageName: node + linkType: hard + +"wordwrap@npm:^1.0.0": + version: 1.0.0 + resolution: "wordwrap@npm:1.0.0" + checksum: 2a44b2788165d0a3de71fd517d4880a8e20ea3a82c080ce46e294f0b68b69a2e49cff5f99c600e275c698a90d12c5ea32aff06c311f0db2eb3f1201f3e7b2a04 + languageName: node + linkType: hard + +"workerpool@npm:6.1.5": + version: 6.1.5 + resolution: "workerpool@npm:6.1.5" + checksum: 5defea1fd3e36b4f83c2bb184cade4a71e27030d46ee5efe704e90e19baf3a5c7146fddef010cbd0b7df3edbfca1e9e851bd35d8da8c99ec6d8bbfe121d8c0b0 + languageName: node + linkType: hard + +"wrap-ansi@npm:^2.0.0": + version: 2.1.0 + resolution: "wrap-ansi@npm:2.1.0" + dependencies: + string-width: ^1.0.1 + strip-ansi: ^3.0.1 + checksum: 2dacd4b3636f7a53ee13d4d0fe7fa2ed9ad81e9967e17231924ea88a286ec4619a78288de8d41881ee483f4449ab2c0287cde8154ba1bd0126c10271101b2ee3 + languageName: node + linkType: hard + +"wrap-ansi@npm:^6.2.0": + version: 6.2.0 + resolution: "wrap-ansi@npm:6.2.0" + dependencies: + ansi-styles: ^4.0.0 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + checksum: 6cd96a410161ff617b63581a08376f0cb9162375adeb7956e10c8cd397821f7eb2a6de24eb22a0b28401300bf228c86e50617cd568209b5f6775b93c97d2fe3a + languageName: node + linkType: hard + +"wrap-ansi@npm:^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: ^4.0.0 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + checksum: a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b + languageName: node + linkType: hard + +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 + languageName: node + linkType: hard + +"write-file-atomic@npm:^3.0.0": + version: 3.0.3 + resolution: "write-file-atomic@npm:3.0.3" + dependencies: + imurmurhash: ^0.1.4 + is-typedarray: ^1.0.0 + signal-exit: ^3.0.2 + typedarray-to-buffer: ^3.1.5 + checksum: c55b24617cc61c3a4379f425fc62a386cc51916a9b9d993f39734d005a09d5a4bb748bc251f1304e7abd71d0a26d339996c275955f527a131b1dcded67878280 + languageName: node + linkType: hard + +"write-file-atomic@npm:^4.0.0": + version: 4.0.1 + resolution: "write-file-atomic@npm:4.0.1" + dependencies: + imurmurhash: ^0.1.4 + signal-exit: ^3.0.7 + checksum: 8f780232533ca6223c63c9b9c01c4386ca8c625ebe5017a9ed17d037aec19462ae17109e0aa155bff5966ee4ae7a27b67a99f55caf3f32ffd84155e9da3929fc + languageName: node + linkType: hard + +"write-json-file@npm:^4.1.1": + version: 4.3.0 + resolution: "write-json-file@npm:4.3.0" + dependencies: + detect-indent: ^6.0.0 + graceful-fs: ^4.1.15 + is-plain-obj: ^2.0.0 + make-dir: ^3.0.0 + sort-keys: ^4.0.0 + write-file-atomic: ^3.0.0 + checksum: 33908c591923dc273e6574e7c0e2df157acfcf498e3a87c5615ced006a465c4058877df6abce6fc1acd2844fa3cf4518ace4a34d5d82ab28bcf896317ba1db6f + languageName: node + linkType: hard + +"ws@npm:^7.4.5, ws@npm:^7.5.3": + version: 7.5.5 + resolution: "ws@npm:7.5.5" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: bd2b437256012af526c69c03d6670a132e7ab0fe5853f3b7092826acea4203fad4ee2a8d0d9bd44834b2b968e747bf34f753ab535f4a3edf40d262da4b1d0805 + languageName: node + linkType: hard + +"ws@npm:~8.2.3": + version: 8.2.3 + resolution: "ws@npm:8.2.3" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: c869296ccb45f218ac6d32f8f614cd85b50a21fd434caf11646008eef92173be53490810c5c23aea31bc527902261fbfd7b062197eea341b26128d4be56a85e4 + languageName: node + linkType: hard + +"xdg-basedir@npm:^4.0.0": + version: 4.0.0 + resolution: "xdg-basedir@npm:4.0.0" + checksum: 0073d5b59a37224ed3a5ac0dd2ec1d36f09c49f0afd769008a6e9cd3cd666bd6317bd1c7ce2eab47e1de285a286bad11a9b038196413cd753b79770361855f3c + languageName: node + linkType: hard + +"xml2js@npm:0.4.19": + version: 0.4.19 + resolution: "xml2js@npm:0.4.19" + dependencies: + sax: ">=0.6.0" + xmlbuilder: ~9.0.1 + checksum: ca8b2fee430d450a18947786bfd7cd1a353ee00fc6fd550acbc8a8e65f1b4df5e9786fcb2990c1a5514ecd554d445fb74e1d716b3a4fcfffc10554aeb5db482b + languageName: node + linkType: hard + +"xmlbuilder@npm:~9.0.1": + version: 9.0.7 + resolution: "xmlbuilder@npm:9.0.7" + checksum: 8193bb323806a002764f013bea0c6e9ff2dc26fd29109408761b16b59a8ad2214c2abe8e691755fd8b525586e3a0e1efeb92335947d7b0899032b779f1705a53 + languageName: node + linkType: hard + +"xtend@npm:^4.0.0, xtend@npm:^4.0.1, xtend@npm:^4.0.2, xtend@npm:~4.0.0, xtend@npm:~4.0.1": + version: 4.0.2 + resolution: "xtend@npm:4.0.2" + checksum: ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a + languageName: node + linkType: hard + +"y18n@npm:^4.0.0": + version: 4.0.3 + resolution: "y18n@npm:4.0.3" + checksum: 014dfcd9b5f4105c3bb397c1c8c6429a9df004aa560964fb36732bfb999bfe83d45ae40aeda5b55d21b1ee53d8291580a32a756a443e064317953f08025b1aa4 + languageName: node + linkType: hard + +"y18n@npm:^5.0.5": + version: 5.0.8 + resolution: "y18n@npm:5.0.8" + checksum: 54f0fb95621ee60898a38c572c515659e51cc9d9f787fb109cef6fde4befbe1c4602dc999d30110feee37456ad0f1660fa2edcfde6a9a740f86a290999550d30 + languageName: node + linkType: hard + +"yallist@npm:^3.0.2": + version: 3.1.1 + resolution: "yallist@npm:3.1.1" + checksum: 48f7bb00dc19fc635a13a39fe547f527b10c9290e7b3e836b9a8f1ca04d4d342e85714416b3c2ab74949c9c66f9cebb0473e6bc353b79035356103b47641285d + languageName: node + linkType: hard + +"yallist@npm:^4.0.0": + version: 4.0.0 + resolution: "yallist@npm:4.0.0" + checksum: 343617202af32df2a15a3be36a5a8c0c8545208f3d3dfbc6bb7c3e3b7e8c6f8e7485432e4f3b88da3031a6e20afa7c711eded32ddfb122896ac5d914e75848d5 + languageName: node + linkType: hard + +"yaml@npm:^1.10.2": + version: 1.10.2 + resolution: "yaml@npm:1.10.2" + checksum: ce4ada136e8a78a0b08dc10b4b900936912d15de59905b2bf415b4d33c63df1d555d23acb2a41b23cf9fb5da41c256441afca3d6509de7247daa062fd2c5ea5f + languageName: node + linkType: hard + +"yamljs@npm:^0.3.0": + version: 0.3.0 + resolution: "yamljs@npm:0.3.0" + dependencies: + argparse: ^1.0.7 + glob: ^7.0.5 + bin: + json2yaml: ./bin/json2yaml + yaml2json: ./bin/yaml2json + checksum: 76b770d34c7b9babdc4508e4c7c0cbdf371e17129cc027095d9eac0ae5b841c1b16fc2d625ebb542cc299ed4593478abdfcca172b3f0169e0939c6f2ed2e81a4 + languageName: node + linkType: hard + +"yargs-parser@npm:20.2.4": + version: 20.2.4 + resolution: "yargs-parser@npm:20.2.4" + checksum: d251998a374b2743a20271c2fd752b9fbef24eb881d53a3b99a7caa5e8227fcafd9abf1f345ac5de46435821be25ec12189a11030c12ee6481fef6863ed8b924 + languageName: node + linkType: hard + +"yargs-parser@npm:^18.1.2": + version: 18.1.3 + resolution: "yargs-parser@npm:18.1.3" + dependencies: + camelcase: ^5.0.0 + decamelize: ^1.2.0 + checksum: 60e8c7d1b85814594d3719300ecad4e6ae3796748b0926137bfec1f3042581b8646d67e83c6fc80a692ef08b8390f21ddcacb9464476c39bbdf52e34961dd4d9 + languageName: node + linkType: hard + +"yargs-parser@npm:^20.2.2, yargs-parser@npm:^20.2.3": + version: 20.2.9 + resolution: "yargs-parser@npm:20.2.9" + checksum: 8bb69015f2b0ff9e17b2c8e6bfe224ab463dd00ca211eece72a4cd8a906224d2703fb8a326d36fdd0e68701e201b2a60ed7cf81ce0fd9b3799f9fe7745977ae3 + languageName: node + linkType: hard + +"yargs-unparser@npm:2.0.0": + version: 2.0.0 + resolution: "yargs-unparser@npm:2.0.0" + dependencies: + camelcase: ^6.0.0 + decamelize: ^4.0.0 + flat: ^5.0.2 + is-plain-obj: ^2.1.0 + checksum: 68f9a542c6927c3768c2f16c28f71b19008710abd6b8f8efbac6dcce26bbb68ab6503bed1d5994bdbc2df9a5c87c161110c1dfe04c6a3fe5c6ad1b0e15d9a8a3 + languageName: node + linkType: hard + +"yargs@npm:16.2.0, yargs@npm:^16.1.1, yargs@npm:^16.2.0": + version: 16.2.0 + resolution: "yargs@npm:16.2.0" + dependencies: + cliui: ^7.0.2 + escalade: ^3.1.1 + get-caller-file: ^2.0.5 + require-directory: ^2.1.1 + string-width: ^4.2.0 + y18n: ^5.0.5 + yargs-parser: ^20.2.2 + checksum: b14afbb51e3251a204d81937c86a7e9d4bdbf9a2bcee38226c900d00f522969ab675703bee2a6f99f8e20103f608382936034e64d921b74df82b63c07c5e8f59 + languageName: node + linkType: hard + +"yargs@npm:^15.0.2": + version: 15.4.1 + resolution: "yargs@npm:15.4.1" + 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 + checksum: 40b974f508d8aed28598087720e086ecd32a5fd3e945e95ea4457da04ee9bdb8bdd17fd91acff36dc5b7f0595a735929c514c40c402416bbb87c03f6fb782373 + languageName: node + linkType: hard + +"yeoman-environment@npm:^3.9.1": + version: 3.9.1 + resolution: "yeoman-environment@npm:3.9.1" + dependencies: + "@npmcli/arborist": ^4.0.4 + are-we-there-yet: ^2.0.0 + arrify: ^2.0.1 + binaryextensions: ^4.15.0 + chalk: ^4.1.0 + cli-table: ^0.3.1 + commander: 7.1.0 + dateformat: ^4.5.0 + debug: ^4.1.1 + diff: ^5.0.0 + error: ^10.4.0 + escape-string-regexp: ^4.0.0 + execa: ^5.0.0 + find-up: ^5.0.0 + globby: ^11.0.1 + grouped-queue: ^2.0.0 + inquirer: ^8.0.0 + is-scoped: ^2.1.0 + lodash: ^4.17.10 + log-symbols: ^4.0.0 + mem-fs: ^1.2.0 || ^2.0.0 + mem-fs-editor: ^8.1.2 || ^9.0.0 + minimatch: ^3.0.4 + npmlog: ^5.0.1 + p-queue: ^6.6.2 + p-transform: ^1.3.0 + pacote: ^12.0.2 + preferred-pm: ^3.0.3 + pretty-bytes: ^5.3.0 + semver: ^7.1.3 + slash: ^3.0.0 + strip-ansi: ^6.0.0 + text-table: ^0.2.0 + textextensions: ^5.12.0 + untildify: ^4.0.0 + peerDependencies: + mem-fs: ^1.2.0 || ^2.0.0 + mem-fs-editor: ^8.1.2 || ^9.0.0 + bin: + yoe: cli/index.js + checksum: 60a19b9962184857c52003004ff600a1c99ebb19821f8d3e3a9be8dd87e72083118daa36f44f8171c0ad32a865ef975592fd26cf6edc5294c09d645a6bc7f680 + languageName: node + linkType: hard + +"yeoman-generator@npm:^5.6.1": + version: 5.6.1 + resolution: "yeoman-generator@npm:5.6.1" + dependencies: + chalk: ^4.1.0 + dargs: ^7.0.0 + debug: ^4.1.1 + execa: ^4.1.0 + github-username: ^6.0.0 + lodash: ^4.17.11 + minimist: ^1.2.5 + read-pkg-up: ^7.0.1 + run-async: ^2.0.0 + semver: ^7.2.1 + shelljs: ^0.8.5 + sort-keys: ^4.2.0 + text-table: ^0.2.0 + peerDependencies: + yeoman-environment: ^3.2.0 + peerDependenciesMeta: + yeoman-environment: + optional: true + checksum: ef036210b6fb16f32d2615cd7c5d60a3ed17d1be5402ad997eee91ac709880802ad4026c1486bfff1ef891d284c1bbb15ab45caf8e287e175204d204f8d03474 + languageName: node + linkType: hard + +"yn@npm:3.1.1": + version: 3.1.1 + resolution: "yn@npm:3.1.1" + checksum: 2c487b0e149e746ef48cda9f8bad10fc83693cd69d7f9dcd8be4214e985de33a29c9e24f3c0d6bcf2288427040a8947406ab27f7af67ee9456e6b84854f02dd6 + languageName: node + linkType: hard + +"yn@npm:^2.0.0": + version: 2.0.0 + resolution: "yn@npm:2.0.0" + checksum: 9d49527cb3e9a0948cc057223810bf30607bf04b9ff7666cc1681a6501d660b60d90000c16f9e29311b0f28d8a06222ada565ccdca5f1049cdfefb1908217572 + languageName: node + linkType: hard + +"yocto-queue@npm:^0.1.0": + version: 0.1.0 + resolution: "yocto-queue@npm:0.1.0" + checksum: f77b3d8d00310def622123df93d4ee654fc6a0096182af8bd60679ddcdfb3474c56c6c7190817c84a2785648cdee9d721c0154eb45698c62176c322fb46fc700 + languageName: node + linkType: hard + +"yosay@npm:^2.0.2": + version: 2.0.2 + resolution: "yosay@npm:2.0.2" + dependencies: + ansi-regex: ^2.0.0 + ansi-styles: ^3.0.0 + chalk: ^1.0.0 + cli-boxes: ^1.0.0 + pad-component: 0.0.1 + string-width: ^2.0.0 + strip-ansi: ^3.0.0 + taketalk: ^1.0.0 + wrap-ansi: ^2.0.0 + bin: + yosay: cli.js + checksum: 7e0220ef1321a9f0db4632fb564ff0bad66523bd22bb5cd6435886145bba284a4c1f651f51d629f4a904c79b0bbf13940fee1e127746f9f20b3e5eae8336c6cb + languageName: node + linkType: hard + +"z-schema@npm:^4.2.2": + version: 4.2.4 + resolution: "z-schema@npm:4.2.4" + dependencies: + commander: ^2.7.1 + lodash.get: ^4.4.2 + lodash.isequal: ^4.5.0 + validator: ^13.6.0 + dependenciesMeta: + commander: + optional: true + bin: + z-schema: bin/z-schema + checksum: 9afc0b8d4f75122fbbac8835b0398fc6ab3cfa3f68792e4e86edcd9be1e9ae9d982368a8ae25a4eeea6aad3ab35a24379b76e1525b105c23169c4f93b3185004 + languageName: node + linkType: hard + +"zeromq@npm:^5.2.8": + version: 5.2.8 + resolution: "zeromq@npm:5.2.8" + dependencies: + nan: 2.14.2 + node-gyp: latest + node-gyp-build: ^4.2.3 + checksum: 0fada0fe60a4227d4ced3db6fe5fec308db8b973c63f4b5cb9e4354e8d467cbea949b626df512f77a73771161d018d83cb8f6bafbb75ca146cca565d433c72d4 + languageName: node + linkType: hard